summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJo Shields <directhex@apebox.org>2012-11-14 16:30:10 (GMT)
committerJo Shields <directhex@apebox.org>2012-11-14 16:30:10 (GMT)
commit0f65dfaea27abbadee9c0b5581320c04428de5db (patch)
treeb5de4a3a0f7822b6e595f271b6e3d996fd4d2fca
downloadthunderbird-sieve-0f65dfaea27abbadee9c0b5581320c04428de5db.tar.gz
thunderbird-sieve-0f65dfaea27abbadee9c0b5581320c04428de5db.tar.xz
import thunderbird-sieve 0.2.2+20121028+dfsgupstream
-rw-r--r--.project11
-rw-r--r--.settings/org.eclipse.core.resources.prefs11
-rw-r--r--CHANGELOG.md127
-rw-r--r--CONTRIBUTORS.md15
-rw-r--r--LICENSE.md26
-rw-r--r--README.md63
-rw-r--r--src/sieve@mozdev.org/agpl-3.0.txt661
-rw-r--r--src/sieve@mozdev.org/bootstrap.js83
-rw-r--r--src/sieve@mozdev.org/chrome.manifest9
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/am-sieve-account.js95
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/am-sieve-account.xul71
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/components/SieveAccountManager.js115
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/components/SieveProtocolHandler.js125
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/editor/Sieve.css328
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFilterEditor.html94
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFilterEditor.js1277
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFilterEditor.xul212
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFilterExplorer.js594
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFilterExplorer.xul134
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFiltersTreeView.js94
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveStatus.js271
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveStatus.xul209
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/editor/print.css25
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/filterList/SieveFilterList.js219
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/filterList/SieveFilterList.xul50
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/active.pngbin0 -> 983 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/active2.gifbin0 -> 723 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/btn_donate_LG.gifbin0 -> 1714 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/contributors.pngbin0 -> 71434 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/info.pngbin0 -> 648 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/load.gifbin0 -> 3208 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/passive.pngbin0 -> 805 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/sidebarTitle.pngbin0 -> 240 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/splitter.pngbin0 -> 85 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/sslWarning.pngbin0 -> 3860 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/syntax-error.pngbin0 -> 1645 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/syntax-ok.pngbin0 -> 1910 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/syntax-warning.pngbin0 -> 1411 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.back.pngbin0 -> 1591 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.compile.pngbin0 -> 3483 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.content.pngbin0 -> 1128 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.copy.pngbin0 -> 784 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.cut.pngbin0 -> 1445 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.exit.pngbin0 -> 1564 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.forward.pngbin0 -> 1589 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.home.pngbin0 -> 3726 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.paste.pngbin0 -> 1073 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.print.pngbin0 -> 1296 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.redo.pngbin0 -> 3744 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.reference.pngbin0 -> 3881 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.save.pngbin0 -> 1276 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.search.pngbin0 -> 1264 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.source.pngbin0 -> 1422 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.tools.pngbin0 -> 3934 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.undo.pngbin0 -> 4284 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/LICENSE23
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/README.md8
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/lib/codemirror.css231
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/lib/codemirror.js4124
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/lib/util/matchbrackets.js61
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/lib/util/searchcursor.js119
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/mode/sieve/LICENSE23
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/mode/sieve/sieve.js187
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/package.json21
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/theme/eclipse.css25
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libManageSieve/SieveAbstractClient.js485
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5173/rfc5173.txt563
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveActions.js298
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveBlocks.js163
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveConditions.js278
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveImports.js149
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveNumbers.js84
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveOperators.js211
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveStrings.js789
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveTests.js767
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveWhiteSpaces.js294
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/rfc5228.txt2355
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/widgets/SieveActionsUI.js149
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/widgets/SieveBlocksUI.js79
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/widgets/SieveConditionsUI.js104
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/widgets/SieveNumbersUI.js1
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/widgets/SieveOperatorsUI.js116
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/widgets/SieveStringsUI.js349
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/widgets/SieveTestsUI.js321
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/widgets/simplicity/SieveActionUI.js29
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/widgets/simplicity/SieveBlocksUI.js435
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/widgets/simplicity/SieveTestsUI.js86
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5230/logic/SieveVacation.js124
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5230/rfc5230.txt899
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5230/widgets/SieveVacationUI.js0
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5232/logic/SieveImapFlags.js329
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5232/rfc5232.txt675
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5232/widgets/SieveImapFlagsUI.js83
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5232/work.txt64
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5293/rfc5293.txt507
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5429/logic/SieveReject.js99
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5429/rfc5429.txt787
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5429/widgets/SieveRejectUI.js59
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/SieveGui.html277
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/SieveSimpleGui.html184
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/jquery.js4
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/SieveDesigner.js68
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/SieveLexer.js171
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/SieveParser.js170
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/SieveScriptDOM.js201
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/events/DragHandler.js117
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/events/DropHandler.js856
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/logic/Elements.js311
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/style/_style.css182
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/style/background.pngbin0 -> 3127 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/style/edit.pngbin0 -> 238 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/style/hline.pngbin0 -> 213 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/style/layout.css88
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/style/listitem.add.pngbin0 -> 505 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/style/listitem.delete.pngbin0 -> 599 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/style/listitem.drop.pngbin0 -> 331 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/style/simplicity/listitem.add.pngbin0 -> 505 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/style/simplicity/listitem.delete.pngbin0 -> 599 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/style/simplicity/listitem.drop.pngbin0 -> 331 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/style/simplicity/style.css282
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/style/style.css283
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/style/trash-full.pngbin0 -> 3991 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/style/trash.pngbin0 -> 2900 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/style/vline.pngbin0 -> 161 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/style/vline2.pngbin0 -> 163 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/toolkit/widgets/Boxes.js384
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/modules/overlays/SieveOverlay.jsm465
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/modules/overlays/SieveOverlayManager.jsm506
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/modules/sieve/Sieve.js745
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/modules/sieve/SieveAccounts.js1258
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/modules/sieve/SieveAutoConfig.js152
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/modules/sieve/SieveConnectionManager.js233
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/modules/sieve/SieveRequest.js1466
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/modules/sieve/SieveResponse.js830
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/modules/sieve/SieveResponseCodes.js113
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/modules/sieve/SieveResponseParser.js362
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/modules/sieve/SieveSession.js685
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/modules/utils/SieveTabType.jsm185
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/modules/utils/SieveWindowHelper.jsm202
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/options/SieveAccountOptions.js626
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/content/options/SieveAccountOptions.xul266
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/locale/de-DE/am-sieve-account.properties11
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/locale/de-DE/locale.dtd291
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/locale/de-DE/locale.properties68
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/locale/en-US/am-sieve-account.properties11
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/locale/en-US/locale.dtd285
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/locale/en-US/locale.properties68
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/locale/es-ES/am-sieve-account.properties15
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/locale/es-ES/locale.dtd286
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/locale/es-ES/locale.properties72
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/locale/fr-FR/am-sieve-account.properties12
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/locale/fr-FR/locale.dtd288
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/locale/fr-FR/locale.properties71
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/locale/ru-RU/am-sieve-account.properties14
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/locale/ru-RU/locale.dtd286
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/locale/ru-RU/locale.properties71
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/skin/ToolBarButton.css12
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/skin/images/filter16x16.pngbin0 -> 677 bytes
-rw-r--r--src/sieve@mozdev.org/chrome/chromeFiles/skin/images/filter24x24.pngbin0 -> 996 bytes
-rw-r--r--src/sieve@mozdev.org/install.rdf37
-rw-r--r--tools/GSSAPI Proxy/gssapi-sieve-proxy.pl439
-rw-r--r--tools/Replay Server/org/mozdev/sieve/ReplayServer.java452
-rw-r--r--tools/Replay Server/org/mozdev/sieve/SieveServerSocket.java65
-rw-r--r--tools/Replay Server/org/mozdev/sieve/SieveSocket.java78
-rw-r--r--tools/Tests/Proxy.js37
-rw-r--r--tools/Tests/SASL Login.js64
-rw-r--r--tools/Tests/SieveRequest.js1546
167 files changed, 37723 insertions, 0 deletions
diff --git a/.project b/.project
new file mode 100644
index 0000000..04b7340
--- /dev/null
+++ b/.project
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>sf.net</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ </buildSpec>
+ <natures>
+ </natures>
+</projectDescription>
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..9fa8ee0
--- /dev/null
+++ b/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,11 @@
+eclipse.preferences.version=1
+encoding//src/sieve@mozdev.org/chrome/chromeFiles/locale/de-DE/am-sieve-account.properties=UTF-8
+encoding//src/sieve@mozdev.org/chrome/chromeFiles/locale/de-DE/locale.properties=UTF-8
+encoding//src/sieve@mozdev.org/chrome/chromeFiles/locale/en-US/am-sieve-account.properties=UTF-8
+encoding//src/sieve@mozdev.org/chrome/chromeFiles/locale/en-US/locale.properties=UTF-8
+encoding//src/sieve@mozdev.org/chrome/chromeFiles/locale/es-ES/am-sieve-account.properties=UTF-8
+encoding//src/sieve@mozdev.org/chrome/chromeFiles/locale/es-ES/locale.properties=UTF-8
+encoding//src/sieve@mozdev.org/chrome/chromeFiles/locale/fr-FR/am-sieve-account.properties=UTF-8
+encoding//src/sieve@mozdev.org/chrome/chromeFiles/locale/fr-FR/locale.properties=UTF-8
+encoding//src/sieve@mozdev.org/chrome/chromeFiles/locale/ru-RU/am-sieve-account.properties=UTF-8
+encoding//src/sieve@mozdev.org/chrome/chromeFiles/locale/ru-RU/locale.properties=UTF-8
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..7ae3a8b
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,127 @@
+# Changelogs
+
+## Sieve 0.2.2 - (22.09.2012)
+* [NEW] Syntax highlight in text editor. It is based on [CodeMirror](http://www.codemirror.net)
+* [NEW] Merge dialog if local script is newer than the remote.
+* [NEW] Support for thunderbird's upcoming AppMenu
+* [NEW] Changed indication in tab title
+* [FIXED] Closing Tab in offline mode could fail
+
+## Sieve 0.2.1 - (15.08.2012)
+* [NEW] Extension is [restartless / Bootstrapped](https://developer.mozilla.org/en-US/docs/Extensions/Bootstrapped_extensions)
+* [NEW] Spanish locale
+* [UPDATED] All strings should now be localized
+* [UPDATED] French and German translations
+* [UPDATED] Improved look and feel
+* [UPDATED] Improved error handling and error messages
+* [FIXED] Improved close and shutdown behavior
+* [FIXED] Referrals broken, because session was lost after connect
+* [FIXED] Toolbar in graphical editor now scrolls in x-position but maintains it's y-position
+* [FIXED] Side and side and error bar dimensions should persist
+* [FIXED] "Source View" button partly broken
+
+## Sieve 0.2.0 - (02.04.2012)
+* [NEW] Graphical Interface for editing scripts
+* [UPDATE] French locale
+
+## Sieve 0.1.14 - (16.12.2011)
+* [NEW] [SCRAM-SHA1](https://tools.ietf.org/html/rfc5802) support
+* [NEW] Tabbed UI
+* [FIXED] CRAM-MD5 compatibility issues
+* [FIXED] Packet fragmentation caused starttls workaround to randomly fail
+
+## Sieve 0.1.13 - (19.07.2011)
+* [UPDATED] Change default port to 4190 (see [RFC5804](https://wiki.tools.ietf.org/html/rfc5804#section-1.8"))
+* [UPDATED] Hostname and Port configuration separated
+* [UPDATED] Improved workaround for cyrus STARTTLS bug
+* [FIXED] SASL CRAM-MD5 failed on Dovecot
+* [NEW] Connection pipelining
+* [NEW] Support Tabulator Key in the Editor
+* [NEW] Option to Force TLS
+* [NEW] Account Wizard
+
+## Sieve 0.1.12 - (04.04.2011)
+* [FIXED] Referrals
+* [FIXED] Detect timeouts more reliable
+
+## Sieve 0.1.11 - (19.02.2011)
+* [NEW] Non modal windows instead of modal dialogs
+* [NEW] Concurrent sieve session
+* [NEW] [Postbox 2.x](http://www.postbox-inc.com/) support
+* [NEW] [Seamonkey](http://www.seamonkey-project.org/) support
+* [UDATED] Improved support for Thunderbird's offline mode
+
+## Sieve 0.1.10 - (01.08.2010)
+* [NEW] Toolbar button
+* [NEW] Support and Detect Thunderbird's offline mode
+* [NEW] [Gecko 2.0]("https://developer.mozilla.org/en/XPCOM/XPCOM_changes_in_Gecko_2.0") support
+* [UPDATED] Improve / reduce code footprint
+* [FIXED] Overriding expired certificates
+* [NEW] Tolerate non RFC conform response codes from broken cyrus servers
+
+## Sieve 0.1.9 - (08.02.2010)
+* [FIXED] Unusable Account Manager due to bad character in French locale
+* [UPDATED] French and German translations
+* [NEW] Russian locale
+* [NEW] Socks Proxy support
+* [NEW] [Postbox 1.x]("http://www.postbox-inc.com/") support
+
+## Sieve 0.1.8 (04.01.2010)
+* [FIXED] SASL CRAM-MD5
+* [FIXED] Use deprecated SASL LOGIN only as last resort
+* [NEW] Line numbers in editor
+
+## Sieve 0.1.7 (11.12.2009)
+* [NEW] CHECKSCRIPT, NOOP and RENAME command
+* [NEW] VERSION, MAXREDIRECTS, NOTIFY, LANGUAGE and OWNER capabilities
+* [NEW] Support Thunderbird 3's new Password Manager
+* [NEW] Settings Dialog integrated into Thunderbird's account manager
+* [NEW] Search and replace within a Sieve script
+* [FIXED] Exporting scripts on Linux
+* [NEW] Sort scripts by name
+* [FIXED] Renaming an active script on dovecot servers
+* [NEW] Implement Extension core as components
+* [NEW] SASL CRAM-MD5
+* [FIXED] Override bad certificates in Thunderbird 3
+* [NEW] French locale
+
+## Sieve 0.1.6 (12.10.2008)
+* [FIXED] Empty windows on Linux
+* [NEW] German locale
+
+## Sieve 0.1.5 (03.10.2008)
+* [NEW] Extended debug output
+* [NEW] SASL proxy authorization
+* [NEW] SASL authentication mechanism can be forced
+* [UPDATED] Improved error handling
+* [NEW] Enable secure extension updates
+* [FIXED] TLS handshake failed with non cyrus server
+* [NEW] Capability dialog integrated into SieveFilters window
+* [UPDATED] Option &amp; Filter Editor UI
+* [NEW] Display Cursor position in statusbar
+* [NEW] Sidebar with a Sieve Language reference
+* [UPDATED] custom authentication
+* [FIXED] UTF-8 compatibility issues
+
+## Sieve 0.1.4 (22.04.2007)
+* [FIXED] "SASL Login" mechanism
+* [FIXED] Large sieve scripts caused the extension to die silently due to packet fragmentation
+* [FIXED] UTF-8 compatibility issues
+
+## Sieve 0.1.3 (30.09.2006)
+* [NEW] SASL Login mechanism
+* [FIXED] Line break related issues
+
+## Sieve 0.1.2 (03.09.2006)
+* [NEW] Automatic Extension updates
+* [FIXED] Line break issue "line 1: syntax error,..."
+* [NEW] Rename button
+* [NEW] Debug mode
+
+## Sieve 0.1.1 (10.05.2006)
+* [NEW] Referrals
+* [UPDATED] Error handling improved
+* [FIXED] Settings dialog
+
+## Sieve 0.1.0 (01.05.2006)
+* [NEW] Initial release \ No newline at end of file
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
new file mode 100644
index 0000000..3ca4557
--- /dev/null
+++ b/CONTRIBUTORS.md
@@ -0,0 +1,15 @@
+# Contributors
+Everyone who contributed either code, translations, money or resources is credited
+here in no specific order.
+
+## Code and translations
+Marion Desnault, Max Dittrich, Sébastien, Виктор Букреев, Christian Rößner,
+Michael Fladischer, Александр Моисеев, Cyril Kluska, Dominique Couot, Carlos Gomez Agun
+
+## Donors
+John Fawcett, Simon Clarkstone, Janusz Wilk, Ralph Angenendt, Ralf Loeser,
+Lars Bahner, Alexander Brüning, Sebastian Bremicker, Christophe Sokol, Trent Payne,
+Chris J Kottaridis, Robert Fantini, Antonino Balsamo, Christopher Davies
+
+## Resources
+jQuery, CodeMirror, Mozdev.org, sf.net, github \ No newline at end of file
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..89f9300
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,26 @@
+# Licensing information
+
+Keep in mind, Sieve is a powerful mail filter, loss of data is likely when you
+misconfigure a script. Furthermore is likely that this extension contains bugs.
+As the source code is freely available, every one can check the correctness and
+the quality of the software before using it. This means you use the extension at
+your own risk. There is absolutely no liability for loss of data or damage or
+similar stuff caused by this extension.
+
+The extension is free and open source software, it is made available to you
+under the terms of the [GNU Affero General Public License (AGPLv3)](http://www.fsf.org/licensing/licenses/agpl-3.0.html).
+This means you may use, copy and distribute it to others. You are also welcome to modify
+the source code as you want to meet your needs.
+
+All of the source code is available under licenses which are both free and
+open source. Most of it is available under the AGPLv3. The remainder of the software
+which is not under the AGPLv3 is available under one of a variety of other licenses.
+Those are given below:
+
+* [CodeMirror](http://www.codemirror.net) is released under a [MIT Style license](http://codemirror.net/LICENSE)
+* [JQuery](http://www.jquery.com) is released under the [MIT License](http://jquery.org/license/)
+
+If you don't agree with any of these licenses, don't use the extension! As every
+country has it's own jurisdiction, it could be that parts of the license or the whole
+license itself is illegal, invalid or violates a law of your country. If so you
+are not allowed to use the extension! \ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8710af1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,63 @@
+# Thunderbird Sieve Extension
+
+[Sieve](http://en.wikipedia.org/wiki/Sieve_%28mail_filtering_language%29) is a powerful script language for server-side mail filtering. It is
+intended to be used with [IMAP](http://tools.ietf.org/html/rfc3501) and thus
+it is widely spread. Many IMAP Server are capable of running sieve filters.
+Sieve stores and runs all script on the server-side.
+
+Now there is the dilemma - you have access to a server supporting sieve but,
+how do you manage your scripts on this server?
+
+You can use telnet for this purpose, but that is fair to uncomfortable, not
+applicable for a normal user and almost impossible with secure connections.
+Wouldn't it be great to activate, edit, delete and add sieve scripts with a
+convenient interface? That is exactly what the Sieve Extension offers...
+
+## Status
+
+The extension is an implementation of the [sieve management protocol (RFC 5804)](https://wiki.tools.ietf.org/html/rfc5804).
+Currently only "SASL Plain", "SASL Login", "SASL CRAM MD5" and "[SASL SCRAM SHA1](https://tools.ietf.org/html/rfc5802)"
+Authentication mechanisms are supported, others may be implemented on request.
+The project exists since 2006 and can be considered as stable.
+
+## Bugs
+
+Please report bugs via the [issue tracker](https://github.com/thsmi/sieve/issues)
+or send an email to schmid-thomas at gmx.net
+
+Give me 1-2 weeks time for a reply. If you did not receive a reply at all, it
+might be a good idea to check your spam filter.
+
+## License
+
+The extension is free and open source software, it is made available to you
+under the terms of the [GNU Affero General Public License (AGPLv3)](http://www.fsf.org/licensing/licenses/agpl-3.0.html).
+
+Refer to [Licensing information](https://github.com/thsmi/sieve/blob/master/LICENSE.md) for details.
+
+## Releases
+
+You are looking for the most recent release? Just go to [addons.mozilla.org](https://addons.mozilla.org/en-US/thunderbird/addon/sieve/).
+
+See the [Changelog](https://github.com/thsmi/sieve/blob/master/CHANGELOG.md)
+for what's new in the most recent and upcoming releases.
+
+## Developments Builds
+
+You are looking for the latest "bleeding edge" features and willing to risk more instability?
+Or you might even want to test out newly added code to help identify and debug problems?
+
+Just go to the download section:
+
+https://github.com/thsmi/sieve/downloads
+
+1. Right-Click on the nightly build and choose "Save Link As..." to Download and
+ save the file to your hard disk.
+2. In Mozilla Thunderbird, open the addon manager (Tools Menu/Addons)
+3. Click the Install button, and locate/select the file you downloaded and click "OK"
+
+
+But keep in mind: You use these development builds at your own risk and please
+report bugs! Don't be confused these builds don't use special version numbers,
+but anyhow they should automatically upgrade to stable builds upon release of
+the next version. \ No newline at end of file
diff --git a/src/sieve@mozdev.org/agpl-3.0.txt b/src/sieve@mozdev.org/agpl-3.0.txt
new file mode 100644
index 0000000..dba13ed
--- /dev/null
+++ b/src/sieve@mozdev.org/agpl-3.0.txt
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
diff --git a/src/sieve@mozdev.org/bootstrap.js b/src/sieve@mozdev.org/bootstrap.js
new file mode 100644
index 0000000..d86f09b
--- /dev/null
+++ b/src/sieve@mozdev.org/bootstrap.js
@@ -0,0 +1,83 @@
+/*
+ * The content of this file is licenced. You may obtain a copy of the license
+ * at http://sieve.mozdev.org or request it via email from the author.
+ *
+ * Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ *
+ */
+
+// Enable Strict Mode
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+function install(data, reason)
+{
+}
+
+function startup(data, reason)
+{
+ if (Services.vc.compare(Services.appinfo.platformVersion, "10.0") < 0)
+ Components.manager.addBootstrappedManifestLocation(data.installPath);
+
+ // Step 1: Register XPCOM Components
+ Cu.import("chrome://sieve/content/components/SieveAccountManager.js");
+ SieveAccountManagerComponent.load();
+
+ Cu.import("chrome://sieve/content/components/SieveProtocolHandler.js");
+ SieveProtocolHandlerComponent.load();
+
+ Cu.import("chrome://sieve/content/modules/overlays/SieveOverlayManager.jsm");
+ Cu.import("chrome://sieve/content/modules/overlays/SieveOverlay.jsm");
+
+ SieveOverlayManager.addOverlay(
+ SieveMailWindowOverlay,"chrome://messenger/content/messenger.xul");
+ SieveOverlayManager.addOverlay(
+ SieveFilterListOverlay,"chrome://messenger/content/FilterListDialog.xul");
+
+ SieveOverlayManager.addOverlay(
+ SieveToolbarOverlay, "chrome://global/content/customizeToolbar.xul");
+
+ SieveOverlayManager.load();
+
+ // TODO if reason ADDON_UPGRADE restore previously open tabs...
+}
+
+function shutdown(data, reason)
+{
+ // Speedup shutdown, we don't need to cleanup if thunderbird closes
+ if (reason == APP_SHUTDOWN)
+ return;
+
+ // TODO if reason ADDON_UPGRADE persist all open tabs...
+
+ // Step 1: Unload XPCOM Componenets
+ SieveAccountManagerComponent.unload();
+ //delete SieveAccountManagerComponent;
+ Cu.unload("chrome://sieve/content/components/SieveAccountManager.js")
+
+ SieveProtocolHandlerComponent.unload();
+ //delete SieveProtocolHandlerComponent;
+ Cu.unload("chrome://sieve/content/components/SieveProtocolHandler.js");
+
+
+ // Step 2: remove Code Injections
+ SieveOverlayManager.unload();
+
+ Cu.unload("chrome://sieve/content/modules/overlays/SieveOverlay.jsm");
+ Cu.unload("chrome://sieve/content/modules/overlays/SieveOverlayManager.jsm");
+
+ Cu.unload("chrome://sieve/content/modules/utils/SieveWindowHelper.jsm");
+
+ // Remove Chrome Manifest
+ Components.manager.removeBootstrappedManifestLocation(data.installPath);
+};
+
diff --git a/src/sieve@mozdev.org/chrome.manifest b/src/sieve@mozdev.org/chrome.manifest
new file mode 100644
index 0000000..9777093
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome.manifest
@@ -0,0 +1,9 @@
+content sieve chrome/chromeFiles/content/
+
+locale sieve en-US chrome/chromeFiles/locale/en-US/
+locale sieve de-DE chrome/chromeFiles/locale/de-DE/
+locale sieve fr-FR chrome/chromeFiles/locale/fr-FR/
+locale sieve ru-RU chrome/chromeFiles/locale/ru-RU/
+locale sieve es-ES chrome/chromeFiles/locale/es-ES/
+
+skin sieve classic chrome/chromeFiles/skin/
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/am-sieve-account.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/am-sieve-account.js
new file mode 100644
index 0000000..6538bda
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/am-sieve-account.js
@@ -0,0 +1,95 @@
+/*
+ * The contents of this file is licenced. You may obtain a copy of
+ * the license at http://sieve.mozdev.org or request it via email
+ * from the author. Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ */
+
+// Enable Strict Mode
+"use strict";
+
+Components.utils.import("chrome://sieve/content/modules/overlays/SieveOverlayManager.jsm");
+SieveOverlayManager.require("/sieve/SieveAccounts.js",this,window);
+
+var gSieveAccount = null;
+var gSivIncomingServer = null;
+
+function onLoad() {
+ parent.onPanelLoaded('am-sieve-account.xul');
+}
+
+function onUnload() {
+ gSieveAccount = null;
+ gSivIncomingServer =null;
+}
+
+/*
+function onAcceptEditor() { }
+
+function onSave() { }
+*/
+
+function onPreInit(account, accountvalues)
+{
+ gSivIncomingServer = account.incomingServer;
+}
+
+function onInit(pageId, serverId)
+{
+ gSieveAccount = SieveAccountManager.getAccountByServer(gSivIncomingServer);
+ UpdatePage();
+}
+
+function UpdatePage()
+{
+ if (gSieveAccount.isEnabled())
+ document.getElementById('rgAccount').selectedIndex = 1;
+ else
+ document.getElementById('rgAccount').selectedIndex = 0;
+
+ document.getElementById('txtHostname').value
+ = gSieveAccount.getHost().getHostname();
+ document.getElementById('txtPort').value
+ = gSieveAccount.getHost().getPort();
+ document.getElementById('txtTLS').value
+ = gSieveAccount.getHost().isTLSForced();
+
+ document.getElementById('txtAuth').value
+ = gSieveAccount.getLogin().getDescription();
+
+ document.getElementById('txtUserName').value
+ = gSieveAccount.getLogin().getUsername();
+}
+
+function onAccountStatusChange()
+{
+ var rgAccount = document.getElementById('rgAccount');
+
+ if (rgAccount.selectedIndex > 0)
+ gSieveAccount.setEnabled(true);
+ else if (gSieveAccount)
+ gSieveAccount.setEnabled(false);
+}
+
+function onFiltersClick()
+{
+ Components.utils.import("chrome://sieve/content/modules/utils/SieveWindowHelper.jsm");
+ SieveUtils.OpenFilter(window,gSivIncomingServer);
+
+ parent.close();
+}
+
+function onSettingsClick()
+{
+ // We don't need a mediator right here as long as we open a modal window ...
+ // ... Because Thunderbird ensures, that the parent account settings can ...
+ // ... be opened exactly one time!
+
+ window.openDialog("chrome://sieve/content/options/SieveAccountOptions.xul",
+ "FilterAccountOptions", "chrome,modal,titlebar,centerscreen",
+ { SieveAccount: gSieveAccount});
+
+ UpdatePage();
+} \ No newline at end of file
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/am-sieve-account.xul b/src/sieve@mozdev.org/chrome/chromeFiles/content/am-sieve-account.xul
new file mode 100644
index 0000000..e571dc4
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/am-sieve-account.xul
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+ <!--
+ The content of this file is licenced. You may obtain a copy of
+ the license at http://sieve.mozdev.org or request it via email
+ from the author. Do not remove or change this comment.
+
+ The initial author of the code is:
+ Thomas Schmid <schmid-thomas@gmx.net>
+
+ Contributor(s):
+ Marion Desnault
+-->
+
+<?xml-stylesheet href="chrome://messenger/skin/accountManage.css" type="text/css"?>
+<!DOCTYPE page SYSTEM "chrome://sieve/locale/locale.dtd">
+
+<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="onLoad()" onunload="onUnload()">
+
+
+ <script type="application/javascript" src="chrome://sieve/content/am-sieve-account.js"/>
+ <dialogheader title="&options.title;"/>
+
+ <description>&options.account.sieve;</description>
+ <description>&options.account.activate;</description>
+
+ <radiogroup id="rgAccount" onselect="onAccountStatusChange(this);">
+ <radio label="&options.account.activate.no;"/>
+ <radio label="&options.account.activate.yes;"/>
+ </radiogroup>
+ <hbox id="smtpServerInfoBox" class="indent">
+ <stack flex="1" class="inset">
+ <spacer id="backgroundBox"/>
+ <grid>
+ <columns>
+ <column/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row align="center">
+ <hbox pack="end"><label id="lblHost" value="&options.info.servername;"/></hbox>
+ <textbox id="txtHostname" class="plain"/>
+ </row>
+ <row align="center">
+ <hbox pack="end"><label id="lblPort" value="&options.info.port;"/></hbox>
+ <textbox id="txtPort" class="plain"/>
+ </row>
+ <row align="center">
+ <hbox pack="end"><label id="lblAuth" value="&options.info.auth;"/></hbox>
+ <textbox id="txtAuth" class="plain"/>
+ </row>
+ <row align="center">
+ <hbox pack="end"><label id="lblUserName" value="&options.info.username;"/></hbox>
+ <textbox id="txtUserName" class="plain"/>
+ </row>
+ <row align="center">
+ <hbox pack="end"><label id="lblTLS" value="&options.info.secure;"/></hbox>
+ <textbox id="txtTLS" class="plain"/>
+ </row>
+ </rows>
+ </grid>
+ </stack>
+ </hbox>
+ <hbox align="right">
+ <button label ="&options.account.filters;" oncommand="onFiltersClick();" />
+ <button label ="&options.account.settings;" oncommand="onSettingsClick();"/>
+ </hbox>
+
+ <vbox flex = "1"/>
+</page>
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/components/SieveAccountManager.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/components/SieveAccountManager.js
new file mode 100644
index 0000000..2f1c5c2
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/components/SieveAccountManager.js
@@ -0,0 +1,115 @@
+/*
+ * The content of this file is licenced. You may obtain a copy of the license
+ * at http://sieve.mozdev.org or request it via email from the author.
+ *
+ * Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ *
+ */
+
+// Enable Strict Mode
+"use strict"
+
+const EXPORTED_SYMBOLS = [ "SieveAccountManagerComponent"];
+
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+
+
+ //class constructor
+ function SieveAccountManagerExtension() {};
+
+ // class definition
+ SieveAccountManagerExtension.prototype =
+ {
+ classID : Components.ID("{87f5b0a0-14eb-11df-a769-0002a5d5c51b}"),
+ contactID : "@mozilla.org/accountmanager/extension;1?name=sieve.mozdev.org",
+ classDescription: "Sieve Account Manager Extension",
+
+ name : "sieve-account",
+ chromePackageName : "sieve",
+ showPanel: function(server)
+ {
+ if (server.type == "imap")
+ return true;
+
+ if (server.type == "pop3")
+ return true;
+
+ return false;
+ },
+
+ QueryInterface: function(aIID)
+ {
+ if (!aIID.equals(Components.interfaces.nsIMsgAccountManagerExtension)
+ && !aIID.equals(Components.interfaces.nsISupports))
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+
+ return this;
+ }
+ };
+
+ const SieveAccountManagerFactory =
+ {
+ _singleton: null,
+ createInstance: function (outer, iid)
+ {
+ if (outer != null)
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+
+ if (this._singleton == null)
+ this._singleton = new SieveAccountManagerExtension();
+
+ return this._singleton.QueryInterface(iid);
+ },
+
+ QueryInterface: function (iid)
+ {
+ if (iid.equals(Ci.nsIFactory) || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+ };
+
+
+const SieveAccountManagerComponent = {
+ load : function()
+ {
+ var compMgr = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+ compMgr.registerFactory(
+ SieveAccountManagerExtension.prototype.classID,
+ SieveAccountManagerExtension.prototype.classDescription,
+ SieveAccountManagerExtension.prototype.contactID,
+ SieveAccountManagerFactory);
+
+
+ var catMgr = Components.classes["@mozilla.org/categorymanager;1"]
+ .getService(Ci.nsICategoryManager);
+
+ catMgr.addCategoryEntry(
+ "mailnews-accountmanager-extensions",
+ SieveAccountManagerExtension.prototype.classDescription,
+ SieveAccountManagerExtension.prototype.contactID,
+ false, true);
+ },
+
+ unload : function()
+ {
+ var compMgr = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+ compMgr.unregisterFactory(
+ SieveAccountManagerExtension.prototype.classID,
+ SieveAccountManagerFactory);
+
+ var catMgr = Components.classes["@mozilla.org/categorymanager;1"]
+ .getService(Ci.nsICategoryManager);
+ catMgr.deleteCategoryEntry(
+ "mailnews-accountmanager-extensions",
+ SieveAccountManagerExtension.prototype.classDescription,
+ false);
+ }
+} \ No newline at end of file
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/components/SieveProtocolHandler.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/components/SieveProtocolHandler.js
new file mode 100644
index 0000000..3607fca
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/components/SieveProtocolHandler.js
@@ -0,0 +1,125 @@
+/*
+ * The contents of this file is licenced. You may obtain a copy of
+ * the license at http://sieve.mozdev.org or request it via email
+ * from the author. Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ *
+ * Inspired by ChatZilla code...
+ */
+
+// Enable Strict Mode
+"use strict";
+
+var EXPORTED_SYMBOLS = [ "SieveProtocolHandlerComponent"];
+
+if (typeof(Cc) == 'undefined')
+ { var Cc = Components.classes; }
+
+if (typeof(Ci) == 'undefined')
+ { var Ci = Components.interfaces; }
+
+if (typeof(Cr) == 'undefined')
+ { var Cr = Components.results; }
+
+/**
+ * Implements an Protocol handler component for sieve. This is needed inorder
+ * to obtain proxy information. As it has basically a stub, without any function,
+ * it uses "x-sieve" as scheme instead of "sieve".
+ */
+function SieveProtocolHandler() {};
+
+SieveProtocolHandler.prototype =
+{
+ scheme : "x-sieve",
+ defaultPort : 4190,
+
+ classID : Components.ID("{65f30660-14eb-11df-8351-0002a5d5c51b}"),
+ contactID : "@mozilla.org/network/protocol;1?name="+SieveProtocolHandler.prototype.scheme,
+ classDescription: SieveProtocolHandler.prototype.scheme+" protocol handler",
+
+ protocolFlags :
+ Ci.nsIProtocolHandler.URI_NORELATIVE |
+ Ci.nsIProtocolHandler.URI_NOAUTH |
+ Ci.nsIProtocolHandler.ALLOWS_PROXY |
+ (("URI_DANGEROUS_TO_LOAD" in Ci.nsIProtocolHandler) ? Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE : 0) |
+ (("URI_NON_PERSISTABLE" in Ci.nsIProtocolHandler) ? Ci.nsIProtocolHandler.URI_NON_PERSISTABLE : 0) |
+ (("URI_DOES_NOT_RETURN_DATA" in Ci.nsIProtocolHandler) ? Ci.nsIProtocolHandler.URI_DOES_NOT_RETURN_DATA : 0),
+
+ allowPort : function(port, scheme)
+ {
+ if (scheme==this.scheme)
+ return true;
+ else
+ return false;
+ },
+
+ newURI : function (spec, charset, baseURI)
+ {
+ var url = Cc["@mozilla.org/network/standard-url;1"].createInstance(Ci.nsIStandardURL);
+
+ // Normalize URL to an standard URL
+ url.init(Ci.nsIStandardURL.URLTYPE_AUTHORITY/*URLTYPE_STANDARD*/, this.defaultPort, spec, charset, baseURI);
+
+ return url.QueryInterface(Ci.nsIURI);
+ },
+
+ newChannel : function (URI)
+ {
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ QueryInterface : function(aIID)
+ {
+ if (aIID.equals(Ci.nsISupports))
+ return this;
+
+ // nsIProtocolHandler defines newURI(), newChannel(), allowPort, ...
+ // ... protocolFlags, defaultPort and scheme
+ if (aIID.equals(Ci.nsIProtocolHandler))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+
+ var SieveProtocolHandlerFactory =
+ {
+ createInstance: function (outer, iid)
+ {
+ if (outer != null)
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+
+ return new SieveProtocolHandler().QueryInterface(iid);
+ },
+
+ QueryInterface: function (iid)
+ {
+ if (iid.equals(Ci.nsIFactory) || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+ };
+
+var SieveProtocolHandlerComponent = {};
+
+SieveProtocolHandlerComponent.load = function()
+{
+ var compMgr = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+ compMgr.registerFactory(
+ SieveProtocolHandler.prototype.classID,
+ SieveProtocolHandler.prototype.classDescription,
+ SieveProtocolHandler.prototype.contactID,
+ SieveProtocolHandlerFactory);
+}
+
+SieveProtocolHandlerComponent.unload = function()
+{
+ var compMgr = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+ compMgr.unregisterFactory(
+ SieveProtocolHandler.prototype.classID,
+ SieveProtocolHandlerFactory);
+}
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/Sieve.css b/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/Sieve.css
new file mode 100644
index 0000000..a1898aa
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/Sieve.css
@@ -0,0 +1,328 @@
+@import url("chrome://messenger/skin/");
+@import url("chrome://global/skin/toolbar.css");
+
+
+ /* the surrounding box */
+ .sivEditor
+ {
+ background-color: -moz-Field;
+ /* -moz-appearance: textfield*/
+ }
+
+ .sivEditor textbox
+ {
+ -moz-appearance: none !important;
+ margin: 0px !important;
+ border: none !important;
+ padding: 2px !important;
+
+ font-family: monospace;
+ color: -moz-fieldtext;
+ font-size: 12px;
+ }
+
+ /* the editor */
+ .sivEditor #sivContentEditor
+ {
+ background: url(chrome://sieve/content/images/splitter.png) repeat-y 650px 0px;
+
+ overflow:hidden;
+ }
+
+ .sivEditor #sivContentEditor textarea
+ {
+ overflow-y:scroll;
+ overflow-x:scroll;
+ }
+
+ /* style the line numbers column */
+ .sivEditor #sivLineNumbers
+ {
+ margin:0px;
+ padding:0px;
+
+ border: none;
+ /*border-right: 1px groove;*/
+
+ background: #f8f8f8;
+ }
+
+ .sivEditor #sivLineNumbers textbox
+ {
+ width:50px;
+ background: #f8f8f8 !important;
+ overflow:hidden;
+ }
+
+/* the previous textbox contains an anonymous textarea... */
+.sivEditor #sivLineNumbers textarea
+{
+ overflow:hidden;
+ text-align:right !important;
+}
+
+.sivEditor #sivLineNumbers scrollbar
+{
+ visibility:hidden;
+ width:50px;
+}
+
+
+#sideBarTitle2
+{
+ height: 20px;
+ background: url(chrome://sieve/content/images/sidebarTitle.png) repeat;
+ -moz-appearance: none;
+}
+
+
+#StatusWait
+{
+ background: white;
+ font-weight: bold;
+ color: darkgray;
+}
+
+#StatusError
+{
+ background-color: white
+}
+
+.sivError
+{
+ background-color: #ffe6e6;
+ border: 1px solid black;
+ padding: 5px;
+ margin-top : 5px;
+ -moz-border-radius: 5px;
+}
+
+/*#sivExplorerError description
+{
+ white-space: pre;
+}*/
+
+
+#sivAutoConfig
+{
+ max-width: 430px;
+ background-color: white;
+ border: 1px solid ThreeDShadow;
+ padding: 20px;
+
+ -moz-border-radius: 5px;
+}
+
+#sivAutoConfigInfo
+{
+ padding-left: 40px;
+ background: url(chrome://sieve/content/images/syntax-ok.png) no-repeat 0px 0px;
+}
+
+#sivAutoConfigWait
+{
+ padding-left: 40px;
+ background: url(chrome://sieve/content/images/load.gif) no-repeat 0px 0px;
+}
+
+#sivAutoConfigError
+{
+ padding-left: 40px;
+ background: url(chrome://sieve/content/images/syntax-warning.png) no-repeat 0px 0px;
+}
+
+.sivExplorerWarning
+{
+ background-color: white
+}
+
+.sivWarningBar
+{
+ background-image: -moz-linear-gradient(bottom, #ffdd66, #ffeb7d);
+ border-bottom: 1px solid black;
+
+ padding :5px;
+ margin: 0px;
+}
+
+.sivWarning
+{
+ background-image: -moz-linear-gradient(bottom, #ffdd66, #ffeb7d);
+ /*background-color: #FFFFCC;*/
+ border: 1px solid black;
+ padding: 5px;
+ margin-top : 5px;
+ -moz-border-radius: 5px;
+}
+
+#StatusWarning description
+{
+ white-space: pre;
+}
+
+.sieveSideBar stack image
+{
+ list-style-image: url(chrome://sieve/content/images/sidebarTitle.png);
+}
+
+.sieveSideBar deck image
+{
+ background: url(chrome://sieve/content/images/load.gif) no-repeat center;
+ background-color: white;
+}
+
+.sieveSideBar stack
+{
+ margin: 0px;
+ padding: 0px;
+ height: 20px;
+}
+
+#imgErrorBar
+{
+ padding: 5px;
+}
+#vbServerDetails spacer {
+ background-color: #FFFFFF;
+ opacity: 0.5;
+}
+
+#vbServerDetails textbox {
+ background-color: transparent;
+}
+
+#btnServerDetails
+{
+ border: 0px !important;
+ background-color: transparent;
+ -moz-appearance: none;
+}
+
+#btnServerDetails description
+{
+ border:1px solid #0000ff;
+}
+
+.toolbarbutton-1[type="menu"] {
+ -moz-binding: url("chrome://global/content/bindings/toolbarbutton.xml#menu-vertical") !important;
+ }
+
+.toolbarbutton-1
+{
+ -moz-box-orient: vertical;
+ min-width: 0px;
+}
+
+#btnSave
+{
+ list-style-image: url(chrome://sieve/content/images/toolbar.save.png);
+}
+
+#btnViewSource
+{
+ list-style-image: url(chrome://sieve/content/images/toolbar.source.png);
+}
+
+#btnUndo
+{
+ list-style-image: url(chrome://sieve/content/images/toolbar.undo.png);
+}
+
+#btnRedo
+{
+ list-style-image: url(chrome://sieve/content/images/toolbar.redo.png);
+}
+
+#btnCopy
+{
+ list-style-image: url(chrome://sieve/content/images/toolbar.copy.png);
+}
+
+#btnCut
+{
+ list-style-image: url(chrome://sieve/content/images/toolbar.cut.png);
+}
+
+#btnPaste
+{
+ list-style-image: url(chrome://sieve/content/images/toolbar.paste.png);
+}
+
+#btnCompile
+{
+ list-style-image: url(chrome://sieve/content/images/toolbar.compile.png);
+}
+
+#btnSearchBar
+{
+ list-style-image: url(chrome://sieve/content/images/toolbar.search.png);
+}
+
+#btnTools
+{
+ list-style-image: url(chrome://sieve/content/images/toolbar.tools.png);
+}
+
+#btnDonate
+{
+ cursor:pointer;
+ background: url(chrome://sieve/content/images/btn_donate_LG.gif) no-repeat center center;
+ width: 96px;
+ height: 26px;
+}
+
+#btnPrint
+{
+ list-style-image: url(chrome://sieve/content/images/toolbar.print.png);
+}
+
+#btnReference
+{
+ list-style-image: url(chrome://sieve/content/images/toolbar.reference.png);
+}
+
+#btnSideBarBack
+{
+ list-style-image: url(chrome://sieve/content/images/toolbar.back.png);
+}
+
+#btnSideBarForward
+{
+ list-style-image: url(chrome://sieve/content/images/toolbar.forward.png);
+}
+
+#btnSideBarHome
+{
+ list-style-image: url(chrome://sieve/content/images/toolbar.home.png);
+}
+
+.spVertical{
+ border: none;
+ border-right: 1px solid #A9B7C9;
+ min-width: 0;
+ width: 5px;
+ background-color: transparent;
+ margin-left: -5px;
+ position: relative;
+ z-index: 10;
+ -moz-transition: border-width .3s ease-in;
+}
+
+#spErrorBar {
+ border: none;
+ border-bottom: 1px solid #A9B7C9;
+ min-height: 0;
+ height: 5px;
+ background-color: transparent;
+ margin-top: -5px;
+ position: relative;
+ z-index: 10;
+ -moz-transition: border-width .3s ease-in;
+}
+
+#vbErrorBar {
+ background: #f8f8f8;
+ border-bottom: none;
+ min-height: 50px;
+}
+
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFilterEditor.html b/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFilterEditor.html
new file mode 100644
index 0000000..15215fe
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFilterEditor.html
@@ -0,0 +1,94 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Sieve Editor</title>
+
+ <meta charset="utf-8">
+
+ <link rel="stylesheet" href="./../libs/CodeMirror/lib/codemirror.css">
+ <link rel="stylesheet" href="./../libs/CodeMirror/theme/eclipse.css">
+
+ <script src="./../libs/CodeMirror/lib/codemirror.js"></script>
+ <script src="./../libs/CodeMirror/lib/util/searchcursor.js"></script>
+ <script src="./../libs/CodeMirror/lib/util/matchbrackets.js"></script>
+
+ <script src="./../libs/CodeMirror/mode/sieve/sieve.js"></script>
+
+ <style>
+ .CodeMirror-fullscreen {
+ display: block;
+ position: absolute;
+ top: 0; left: 0;
+ width: 100%;
+ z-index: 9999;
+
+ background-image: url(chrome://sieve/content/images/splitter.png) ;
+ background-repeat: repeat-y;
+ background-color:white;
+ background-position: 650px top;
+ background-attachment: fixed;
+
+ font-family: -moz-fixed;
+ font-size: 12px;
+ line-height: normal;
+ }
+
+ .CodeMirror-gutter {
+ border: none;
+ border-right: 1px solid #A9B7C9;
+ -moz-transition: border-width .3s ease-in;
+ background: #f8f8f8;
+ min-width:40px;
+ }
+
+ body { margin:0px;}
+
+ .activeline {background: #e8f2ff !important;}
+ </style>
+ </head>
+
+ <body>
+ <form>
+ <textarea id="code" name="code"></textarea>
+ </form>
+
+ <script>
+
+ function winHeight() {
+ return window.innerHeight || (document.documentElement || document.body).clientHeight;
+ }
+
+ function setFullScreen(cm) {
+ var wrap = cm.getWrapperElement();
+ wrap.className += " CodeMirror-fullscreen";
+ wrap.style.height = winHeight() + "px";
+ document.documentElement.style.overflow = "hidden";
+ cm.refresh();
+ }
+
+ CodeMirror.on(window, "resize", function() {
+ document.body.getElementsByClassName("CodeMirror-fullscreen")[0]
+ .CodeMirror.getWrapperElement().style.height = winHeight() + "px";
+ });
+
+ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
+ lineNumbers: true,
+ theme: "eclipse",
+ matchBrackets: true
+ });
+
+ setFullScreen(editor,true);
+
+
+ function onActiveLineChange()
+ {
+ editor.setLineClass(hlLine, null, null);
+ hlLine = editor.setLineClass(editor.getCursor().line, null, "activeline");
+ }
+
+ var hlLine = editor.setLineClass(0, null, "activeline");
+ editor.on("cursorActivity", onActiveLineChange);
+
+ </script>
+ </body>
+</html>
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFilterEditor.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFilterEditor.js
new file mode 100644
index 0000000..ab49ec9
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFilterEditor.js
@@ -0,0 +1,1277 @@
+/*
+ * The content of this file is licenced. You may obtain a copy of the license
+ * at http://sieve.mozdev.org or request it via email from the author.
+ *
+ * Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ *
+ * Hints for Spekt IDE autocomplete, they have to be in the first comment...
+ * @include "/sieve/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libManageSieve/SieveResponse.js"
+ * @include "/sieve/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libManageSieve/SieveRequest.js"
+ */
+
+// Enable Strict Mode
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+Cu.import("chrome://sieve/content/modules/overlays/SieveOverlayManager.jsm");
+Cu.import("chrome://sieve/content/modules/utils/SieveWindowHelper.jsm");
+
+SieveOverlayManager.require("/sieve/SieveConnectionManager.js",this,window);
+SieveOverlayManager.require("/sieve/SieveAccounts.js",this,window);
+
+var gBackHistory = new Array();
+var gForwardHistory = new Array();
+
+var gPrintSettings = null;
+
+
+
+var gEditorStatus =
+{
+ checkScriptDelay : 200,
+ checkScriptTimer : null,
+
+ persistedScript : null,
+
+ closeListener : null,
+
+ checksum : {
+ // used to detect if the script was changed via a gui...
+ gui : null,
+ // used to detect if the script changed upon a reconnect
+ server : null //the script's serverside checksum
+ }
+}
+
+var gSFE = new SieveFilterEditor();
+
+function SieveFilterEditor()
+{
+ SieveAbstractClient.call(this);
+}
+
+SieveFilterEditor.prototype.__proto__ = SieveAbstractClient.prototype;
+
+SieveFilterEditor.prototype.onChannelReady
+ = function(cid)
+{
+ // We observe only our channel...
+ if (cid != this._cid)
+ return;
+
+ var that = this;
+ var event = {
+ onError : function (reponse)
+ {
+ // Script does not exists, or was deleted
+
+ // if we have a server checksum, we are reconnecting and our
+ // editor contains valid data. A typical szenario is having
+ // an unsaved script, losing the connection and then clicking
+ // on reconnect.
+ if (gEditorStatus.checksum.server)
+ {
+ that.onScriptLoaded(that.getScript())
+ return;
+ }
+
+ if (gEditorStatus.persistedScript)
+ {
+ that.onScriptLoaded(gEditorStatus.persistedScript);
+ return;
+ }
+
+ // if not use the default script
+ var date = new Date();
+ var script = "#\r\n# "+date.getFullYear()+"-"+(date.getMonth()+1)+"-"+date.getDate()+"\r\n#\r\n";
+ that.onScriptLoaded(script);
+ }
+ }
+
+ var request = new SieveGetScriptRequest(this.getScriptName());
+ request.addGetScriptListener(this);
+ request.addErrorListener(event);
+
+ this.sendRequest(request)
+}
+
+SieveFilterEditor.prototype.onChannelClosed
+ = function(cid)
+{
+ // some other channel died we don't care about that...
+}
+
+SieveFilterEditor.prototype._calcChecksum
+ = function(str)
+{
+ var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+ .createInstance(Ci.nsIScriptableUnicodeConverter);
+
+ // we use UTF-8 here
+ converter.charset = "UTF-8";
+
+ var result = {};
+ // data is an array of bytes
+ var data = converter.convertToByteArray(str, result);
+
+ var ch = Cc["@mozilla.org/security/hash;1"]
+ .createInstance(Ci.nsICryptoHash);
+
+ ch.init(ch.SHA512);
+ ch.update(data, data.length);
+
+ var hash = ch.finish(false);
+
+ // return the two-digit hexadecimal code for a byte
+ function toHexString(charCode)
+ {
+ return ("0" + charCode.toString(16)).slice(-2);
+ }
+
+ // convert the binary hash data to a hex string.
+ return [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
+}
+
+SieveFilterEditor.prototype.onGetScriptResponse
+ = function(response)
+{
+
+ // The sercer checksum is empty, so we have nothing to compare, which means...
+ // ...the script was never loaded
+ if (!gEditorStatus.checksum.server)
+ {
+ this.onScriptLoaded(response.getScriptBody());
+ return;
+ }
+
+ var remoteScript = this._calcChecksum(response.getScriptBody());
+
+ // The server side script is equal to out current script. We are perfectly
+ // in sync. And do not need to do anything...
+ if (remoteScript == this._calcChecksum(this.getScript()))
+ {
+ this.onScriptLoaded(this.getScript());
+ return;
+ }
+
+ // The server side script is equal to our last save. So no third party changed
+ // anything. We can be sure the local script is newer.
+ if (remoteScript == gEditorStatus.checksum.server)
+ {
+ this.onScriptLoaded(this.getScript());
+ return;
+ }
+
+
+ // not so good. We got out of sync, we can't descide which one is newer...
+ this.onStatusChange(10,{local:this.getScript(),remote:response.getScriptBody()});
+}
+
+SieveFilterEditor.prototype.onPutScriptResponse
+ = function(response)
+{
+ // update the last save shadow copy
+ var script = this.getScript();
+
+ // update the gui checksum & the editor script only when needed
+ if (!document.getElementById('btnViewSource').checked)
+ {
+ document.getElementById("sivEditor2").contentWindow.editor.setValue(script);
+ gEditorStatus.checksum.gui = this._calcChecksum(script);
+ }
+
+ gEditorStatus.checksum.server = this._calcChecksum(script);
+
+ this.setScriptName();
+
+ // check if tab is in the progress of closing
+ if (!gEditorStatus.closeListener)
+ return;
+
+ if (closeTab())
+ return;
+
+ // just calling close is for some reason broken, so we use our helper...
+ //window.arguments[0].wrappedJSObject["close"]();
+}
+
+SieveFilterEditor.prototype.onCheckScriptResponse
+ = function(response)
+{
+ var strings = Services.strings.createBundle("chrome://sieve/locale/locale.properties");
+
+ if (!response.hasError())
+ {
+ // TODO: The response might contain warnings, parse them
+ document.getElementById("lblErrorBar").firstChild.nodeValue
+ = strings.GetStringFromName("syntax.ok");
+
+ document.getElementById("imgErrorBar").src
+ = "chrome://sieve/content/images/syntax-ok.png";
+
+ return;
+ }
+
+ // CHECKSCRIPT or PUTSCRIPT failed and the server rejected the script...
+ // ... most likely because of syntax errors.
+ //
+ // In case we used the PUTSCRIPT hack, we don't need to delete the...
+ // ... temporary script because it was never stored on the server, due...
+ // ... to this error...
+
+ // we got an overquota warning, this means syntaxcheck can't be performed
+ if (response.getResponseCode().equalsCode("QUOTA"))
+ {
+
+ document.getElementById("lblErrorBar").firstChild.nodeValue
+ = strings.GetStringFromName("syntax.quota");
+
+ document.getElementById("imgErrorBar").src
+ = "chrome://sieve/content/images/syntax-warning.png";
+
+ return;
+ }
+
+
+ document.getElementById("lblErrorBar").firstChild.nodeValue
+ = response.getMessage();
+
+ document.getElementById("imgErrorBar").src
+ = "chrome://sieve/content/images/syntax-error.png";
+
+ return;
+}
+
+//*********************
+// Custom Methods
+
+SieveFilterEditor.prototype.onScriptLoaded
+ = function(script)
+{
+ this.onStatusChange(0);
+ gEditorStatus.persistedScript = null;
+
+ var editor = document.getElementById("sivEditor2").contentWindow.editor;
+
+ editor.setCursor({line:0,ch:0});
+ editor.setValue(script);
+ editor.clearHistory();
+
+
+ // Ugly workaround...
+ document.getElementById("sivEditor2").contentWindow.onActiveLineChange();
+
+
+ if (gEditorStatus.checksum.server == null) {
+ gEditorStatus.checksum.server = this._calcChecksum(this.getScript());
+ this.setScriptName();
+ }
+
+ document.getElementById("sivEditor2").focus();
+ editor.focus();
+}
+
+SieveFilterEditor.prototype.observe
+ = function(aSubject, aTopic, aData)
+{
+ if (aTopic == "quit-application-requested")
+ {
+ // we are asychnonous, so need to trigger the evet if we are done...
+ var callback = function () {
+ var cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
+ .createInstance(Ci.nsISupportsPRBool);
+ Services.obs.notifyObservers(cancelQuit, "quit-application-requested", aData);
+
+ // Something aborted the quit process.
+ if (cancelQuit.data)
+ return;
+
+ // TODO if aData == restart add flag Ci.nsIAppStartup.eRestart
+ Cc["@mozilla.org/toolkit/app-startup;1"]
+ .getService(Ci.nsIAppStartup)
+ .quit(Ci.nsIAppStartup.eAttemptQuit);
+ }
+
+ if (asyncCloseTab(callback) == false)
+ aSubject.QueryInterface(Ci.nsISupportsPRBool).data = true
+
+ return;
+ }
+
+ SieveAbstractClient.prototype.observe.call(this,aSubject,aTopic,aData);
+}
+
+
+SieveFilterEditor.prototype.onStatusChange
+ = function (state, message)
+{
+
+ if (state == 0)
+ {
+ document.getElementById("sivEditorStatus").setAttribute('hidden','true');
+ document.getElementById('dkView').removeAttribute('collapsed');
+ return;
+ }
+
+ // The rest has to be redirected to the status window...
+ document.getElementById('dkView').setAttribute('collapsed','true');
+ document.getElementById("sivEditorStatus").contentWindow.onStatus(state,message)
+ document.getElementById("sivEditorStatus").removeAttribute('hidden');
+}
+
+
+SieveFilterEditor.prototype.getScript
+ = function ()
+{
+ if (gEditorStatus.persistedScript)
+ return gEditorStatus.persistedScript;
+
+ // Thunderbird scrambles linebreaks to single \n so we have to fix that
+ var editor = document.getElementById("sivEditor2").contentWindow.editor.getValue()
+ .replace(/\r\n|\r|\n|\u0085|\u000C|\u2028|\u2029/g,"\r\n");
+
+ if (document.getElementById('btnViewSource').checked)
+ return editor;
+
+ var widget = document.getElementById("sivWidgetEditor")
+ .contentWindow.getSieveScript();
+
+ if (this._calcChecksum(widget) == gEditorStatus.checksum.gui)
+ return editor;
+
+ return widget;
+}
+
+SieveFilterEditor.prototype.hasChanged
+ = function()
+{
+
+ if (gEditorStatus.checksum.server == null)
+ return true;
+
+ if (this._calcChecksum(this.getScript()) == gEditorStatus.checksum.server)
+ return false;
+
+ return true;
+}
+
+
+SieveFilterEditor.prototype.putScript
+ = function (scriptName, content)
+{
+
+ if (typeof(scriptName) == "undefined")
+ scriptName = this.getScriptName();
+
+ if (typeof(content) == "undefined")
+ content = this.getScript();
+
+ var event = {
+ onError : function(response)
+ {
+ Cc["@mozilla.org/embedcomp/prompt-service;1"]
+ .getService(Ci.nsIPromptService)
+ .alert(window,"Error","The script could not be saved:\n\n"+response.getMessage())
+
+ // If save failes during shutdown we have to abort it...
+ gEditorStatus.closeListener = null;
+ }
+ }
+
+ var request = new SievePutScriptRequest(scriptName,content);
+ request.addPutScriptListener(this);
+ request.addErrorListener(event);
+
+ this.sendRequest(request);
+}
+
+SieveFilterEditor.prototype.setScriptName
+ = function (scriptName)
+{
+ if (typeof(scriptName) != "undefined")
+ this._scriptName = scriptName;
+
+ var title = this.getScriptName() +" - Sieve Filters";
+
+
+ if (this.hasChanged())
+ title = "*"+title;
+
+ if (document.title != title)
+ document.title = title;
+}
+
+SieveFilterEditor.prototype.getScriptName
+ = function()
+{
+ return this._scriptName;
+}
+
+function onCompile()
+{
+ gSFE.checkScript(gSFE.getScript());
+}
+
+function onInput()
+{
+ // TODO update change status more lazilys
+ gSFE.setScriptName();
+
+ // on every keypress we reset the timeout
+ if (gEditorStatus.checkScriptTimer != null)
+ {
+ clearTimeout(gEditorStatus.checkScriptTimer);
+ gEditorStatus.checkScriptTimer = null;
+ }
+
+ if (document.getElementById("btnCompile").checked)
+ gEditorStatus.checkScriptTimer = setTimeout(function() {onCompile();}, gEditorStatus.checkScriptDelay);
+}
+
+function onWindowPersist()
+{
+ var args = {};
+
+ args["compile"] = document.getElementById('btnCompile').checked;
+ args["account"] = gEditorStatus.account;
+
+ // the script cannot be canged in the editor
+ args["scriptName"] = window.arguments[0].wrappedJSObject["scriptName"];
+
+ // we do not persist upon shutdown, the user already descided wether we
+ // wants to keep the script or not. We need this only in case of a crash
+ if (gSFE && !gEditorStatus.closeListener)
+ {
+ args["scriptBody"] = gSFE.getScript();
+ args["checksumServer"] = gEditorStatus.checksum.server;
+ }
+
+ return args;
+}
+
+function onWindowLoad()
+{
+ document.getElementById("sivEditor2")
+ .contentWindow.editor.on("change",onInput);
+
+ // hack to prevent links to be opened in the default browser window...
+ document.getElementById("ifSideBar").
+ addEventListener(
+ "click",
+ function(event) {onSideBarBrowserClick(event);},
+ false);
+
+ document.getElementById("sivWidgetEditor")
+ .setAttribute("src","chrome://sieve/content/libs/libSieveDOM/SieveGui.html")
+
+ var args = window.arguments[0].wrappedJSObject;
+ gEditorStatus.account = args["account"];
+
+ var account = SieveAccountManager.getAccountByName(gEditorStatus.account);
+
+ document.getElementById("sivEditorStatus").contentWindow
+ .onAttach(account,
+ function() { gEditorStatus.closeListener = null; gSFE.connect(account) },
+ { onUseRemote : function (script) { onUseRemoteScript(script); },
+ onKeepLocal : function (script) { onKeepLocalScript();} });
+
+ // There might be a default or persisted script...
+ if (args["scriptBody"])
+ gEditorStatus.persistedScript = args["scriptBody"];
+
+ if (args["checksumServer"])
+ gEditorStatus.checksum.server = args["checksumServer"];
+
+
+ gEditorStatus.checkScriptDelay = account.getSettings().getCompileDelay();
+
+ gSFE.setScriptName(args["scriptName"]);
+
+ document.getElementById("lblErrorBar").firstChild.nodeValue
+ = Services.strings
+ .createBundle("chrome://sieve/locale/locale.properties")
+ .GetStringFromName("syntax.ok");
+
+ gSFE.onStatusChange(3,"status.loading");
+
+ gSFE.connect(account);
+
+ //preload sidebar...
+ onSideBarHome();
+
+ if (!args["compile"])
+ args["compile"] = account.getSettings().hasCompileDelay();
+
+ onErrorBar(args["compile"]);
+ onSideBar(true);
+ onSearchBar(false);
+ /*onViewSource(true);*/
+
+ Cc["@mozilla.org/observer-service;1"]
+ .getService (Ci.nsIObserverService)
+ .addObserver(gSFE ,"quit-application-requested", false);
+
+/* window.addEventListener("unload", function() {
+ Cc["@mozilla.org/observer-service;1"]
+ .getService (Ci.nsIObserverService)
+ .removeObserver(gSFE, "quit-application-requested");
+ }, false);*/
+}
+
+
+function onDonate()
+{
+ var url = Cc["@mozilla.org/network/io-service;1"]
+ .getService(Ci.nsIIOService)
+ .newURI("https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EAS576XCWHKTC", null, null)
+
+ Cc["@mozilla.org/uriloader/external-protocol-service;1"]
+ .getService(Ci.nsIExternalProtocolService)
+ .loadUrl(url);
+}
+
+function onViewSource(visible,aNoUpdate)
+{
+ if (typeof(visible) == "undefined")
+ visible = document.getElementById('btnViewSource').checked
+
+ var deck = document.getElementById('dkView');
+
+ if (visible)
+ {
+ // show Source
+ deck.selectedIndex = 0;
+ onErrorBar(document.getElementById('btnCompile').hasAttribute("checked"));
+
+ document.getElementById("btnViewSource").setAttribute('checked', 'true')
+
+ document.getElementById("btnUndo").removeAttribute('disabled');
+ document.getElementById("btnRedo").removeAttribute('disabled');
+ document.getElementById("btnCut").removeAttribute('disabled');
+ document.getElementById("btnCopy").removeAttribute('disabled');
+ document.getElementById("btnPaste").removeAttribute('disabled');
+ document.getElementById("btnCompile").removeAttribute('disabled');
+ document.getElementById("btnSearchBar").removeAttribute('disabled');
+
+ document.getElementById("sivEditor2").focus();
+
+ var script = document.getElementById("sivWidgetEditor")
+ .contentWindow.getSieveScript();
+
+ // GUI did not change so se can skip...
+ if (aNoUpdate || (gEditorStatus.checksum.gui == gSFE._calcChecksum(script)))
+ return;
+
+ document.getElementById("sivEditor2").contentWindow.editor.setValue(script);
+
+ onInput();
+ return;
+ }
+
+ document.getElementById("btnViewSource").removeAttribute('checked')
+
+ document.getElementById("btnUndo").setAttribute('disabled',"true");
+ document.getElementById("btnRedo").setAttribute('disabled',"true");
+ document.getElementById("btnCut").setAttribute('disabled',"true");
+ document.getElementById("btnCopy").setAttribute('disabled',"true");
+ document.getElementById("btnPaste").setAttribute('disabled',"true");
+ document.getElementById("btnCompile").setAttribute('disabled',"true");
+ document.getElementById("btnSearchBar").setAttribute('disabled',"true");
+
+ onSearchBar(false);
+ onErrorBar(false,true);
+
+ deck.selectedIndex = 1;
+
+ // Make GUI seem to be more agile...
+ window.setTimeout( function() {updateWidgets()} ,0);
+}
+
+function updateWidgets()
+{
+
+ try {
+ var capabilities = SieveConnections
+ .getChannel(gSFE._sid,gSFE._cid).extensions;
+
+ var script = document.getElementById("sivEditor2").contentWindow.editor.getValue();
+
+ // set script content...
+ document.getElementById("sivWidgetEditor")
+ .contentWindow.setSieveScript(script,capabilities)
+
+ // ... and create a shadow copy
+ gEditorStatus.checksum.gui = gSFE._calcChecksum(
+ document.getElementById("sivWidgetEditor").contentWindow.getSieveScript());
+ }
+ catch (ex){
+ alert("Error while parsing script.\n\n"+ex);
+ // switching to souce view failed
+ onViewSource(true,true);
+ }
+}
+
+function onSideBarBrowserClick(event)
+{
+ var href = null;
+
+ if (event.target.nodeName == "A")
+ href = event.target.href;
+ else if (event.target.parentNode.nodeName == "A")
+ href = event.target.parentNode.href;
+ else
+ return;
+
+ event.preventDefault();
+
+ if (gForwardHistory.length != 0)
+ gForwardHistory = new Array();
+
+ onSideBarGo(href);
+}
+
+function onSideBarBack()
+{
+ // store the current location in the history...
+ gForwardHistory.push(gBackHistory.pop());
+ // ... and go back to the last page
+ onSideBarGo(gBackHistory.pop());
+}
+
+function onSideBarForward()
+{
+ onSideBarGo(gForwardHistory.pop());
+}
+
+function onSideBarHome()
+{
+ if (gForwardHistory.length != 0)
+ gForwardHistory = new Array();
+
+ //document.getElementById("ifSideBar").setAttribute('src',uri);
+ onSideBarGo("http://sieve.mozdev.org/reference/en/index.html");
+}
+
+function onSideBarLoading(loading)
+{
+ if (loading)
+ document.getElementById("dkSideBarBrowser").selectedIndex = 1;
+ else
+ document.getElementById("dkSideBarBrowser").selectedIndex = 0;
+}
+
+function onSideBarGo(uri)
+{
+ onSideBarLoading(true);
+
+ gBackHistory.push(uri);
+
+ if (gBackHistory.length > 20)
+ gBackHistory.shift();
+
+ if (gBackHistory.length == 1)
+ document.getElementById("btnSideBarBack").setAttribute('disabled',"true");
+ else
+ document.getElementById("btnSideBarBack").removeAttribute('disabled');
+
+ if (gForwardHistory.length == 0)
+ document.getElementById("btnSideBarForward").setAttribute('disabled',"true");
+ else
+ document.getElementById("btnSideBarForward").removeAttribute('disabled');
+
+ /*if (document.getElementById("ifSideBar").addEventListener)
+ document.addEventListener(
+ "DOMContentLoaded", function(event) { onSideBarLoading(false); }, false);*/
+ if (document.getElementById("ifSideBar").addEventListener)
+ document.getElementById("ifSideBar").addEventListener(
+ "DOMContentLoaded", function(event) { onSideBarLoading(false); }, false);
+
+ document.getElementById("ifSideBar").setAttribute('src', uri);
+}
+
+function onSave()
+{
+ gSFE.putScript();
+}
+
+/**
+ *
+ * @param {} callback
+ * @return {Boolean}
+ * true if a synchonous shutdown was successfull
+ * false if shutdown was canceled or asynchnous shutdown started and
+ * the callback will be invoked
+ */
+function asyncCloseTab(callback)
+{
+ // We are already closed...
+ if (!gSFE)
+ return true;
+
+ if (!gSFE.hasChanged())
+ return closeTab();
+
+ if (!gSFE.isActive())
+ gEditorStatus.closeListener = null;
+
+ if (gEditorStatus.closeListener)
+ return false;
+
+ // we need to wait for user feedback...
+ var prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"]
+ .getService(Ci.nsIPromptService);
+
+ var strings = Services.strings.createBundle("chrome://sieve/locale/locale.properties")
+
+ // The flags 393733 equals [Save] [Don't Save] [Cancel]
+ var result =
+ prompts.confirmEx(
+ window, strings.GetStringFromName("edit.save.title") ,
+ strings.GetStringFromName("edit.save.description"), 393733,
+ "", "", "", null, { value : false });
+
+ // cancel clicked...
+ if (result == 1)
+ return false;
+
+ // don't save clicked...
+ if (result != 0)
+ return closeTab();
+
+ gEditorStatus.closeListener = callback;
+ onSave();
+
+ return false;
+}
+
+/**
+ * Closes the tab, does not prompt if it should be saved or not...
+ * @return {Boolean}
+ */
+function closeTab()
+{
+ try
+ {
+ Cc["@mozilla.org/observer-service;1"]
+ .getService (Ci.nsIObserverService)
+ .removeObserver(gSFE,"quit-application-requested");
+ }
+ catch (ex) {}
+
+ clearTimeout(gEditorStatus.checkScriptTimer);
+
+ try {
+ gSFE.disconnect();
+ }
+ catch (ex) {}
+
+ gSFE = null;
+
+ // we need to unlock the close function, otherwise we might endup in a
+ // deadlock
+ if (gEditorStatus.closeListener)
+ gEditorStatus.closeListener();
+ // we need to null the listener before calling it otherwise we could
+ // enup in an endless loop...
+ /* var callback = gEditorStatus.closeListener;
+ gEditorStatus.closeListener = null;
+
+ if (callback)
+ callback();*/
+
+ gEditorStatus.closeListener = null;
+ return true;
+}
+
+function onImport()
+{
+ var filePicker = Cc["@mozilla.org/filepicker;1"]
+ .createInstance(Ci.nsIFilePicker);
+
+ filePicker.appendFilter("Sieve Scripts (*.siv)", "*.siv");
+ filePicker.appendFilter("All Files (*.*)", "*.*");
+ filePicker.init(window, "Import Sieve Script", filePicker.modeOpen);
+
+ if (filePicker.show() != filePicker.returnOK)
+ return;
+
+ var inputStream = Cc["@mozilla.org/network/file-input-stream;1"]
+ .createInstance(Ci.nsIFileInputStream);
+ var scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
+ .createInstance(Ci.nsIScriptableInputStream);
+
+ inputStream.init(filePicker.file, 0x01, parseInt("0444", 8), null);
+ scriptableStream.init(inputStream);
+
+ // todo insert imported snipplet instead of replacing the whole script
+ var script = scriptableStream.read(scriptableStream.available());
+
+ scriptableStream.close();
+ inputStream.close();
+
+ document.getElementById("sivEditor2").contentWindow.editor.replaceSelection(script);
+
+ onInput();
+}
+
+function onExport()
+{
+ var filePicker = Cc["@mozilla.org/filepicker;1"]
+ .createInstance(Ci.nsIFilePicker);
+
+ filePicker.defaultExtension = ".siv";
+ filePicker.defaultString = gSFE.getScriptName() + ".siv";
+
+ filePicker.appendFilter("Sieve Scripts (*.siv)", "*.siv");
+ filePicker.appendFilter("Text Files (*.txt)", "*.txt");
+ filePicker.appendFilter("All Files (*.*)", "*.*");
+ filePicker.init(window, "Export Sieve Script", filePicker.modeSave);
+
+ var result = filePicker.show();
+
+ if ((result != filePicker.returnOK) && (result != filePicker.returnReplace))
+ return;
+
+ var file = filePicker.file;
+
+ if (file.exists() == false)
+ file.create(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("0644", 8));
+
+ var outputStream = Cc["@mozilla.org/network/file-output-stream;1"]
+ .createInstance(Ci.nsIFileOutputStream);
+
+ outputStream.init(file, 0x04 | 0x08 | 0x20, parseInt("0644", 8), null);
+
+ var data = gSFE.getScript();
+ outputStream.write(data, data.length);
+ outputStream.close();
+}
+
+function onErrorBar(visible,aSilent)
+{
+ if (visible == null)
+ visible = document.getElementById('btnCompile').checked
+
+ if (visible)
+ {
+ if (!aSilent)
+ document.getElementById("btnCompile").setAttribute('checked', 'true')
+
+ document.getElementById('spErrorBar').removeAttribute('hidden');
+ document.getElementById('vbErrorBar').removeAttribute('hidden');
+
+ onCompile();
+
+ return;
+ }
+
+ clearTimeout(gEditorStatus.checkScriptTimer);
+ gEditorStatus.checkScriptTimer = null;
+
+ if (!aSilent)
+ document.getElementById("btnCompile").removeAttribute('checked');
+
+ document.getElementById("vbErrorBar").setAttribute('hidden', 'true');
+ document.getElementById('spErrorBar').setAttribute('hidden', 'true');
+ return;
+}
+
+/**
+ * Shows the Sidebar containing the Sieve Reference
+ */
+function onSideBarShow()
+{
+ document.getElementById('btnReference').setAttribute('checked', 'true')
+ document.getElementById('spSideBar').removeAttribute('hidden');
+ document.getElementById('vbSidebar').removeAttribute('hidden');
+
+ return;
+}
+
+/**
+ * Shows the Sidebar containing the Sieve Reference
+ */
+function onSideBarHide()
+{
+ document.getElementById('btnReference').removeAttribute('checked');
+ document.getElementById('spSideBar').setAttribute('hidden', 'true');
+ document.getElementById('vbSidebar').setAttribute('hidden', 'true')
+
+ onSearchBar(false);
+
+ return;
+}
+
+function onSideBar(visible)
+{
+ if (visible == null)
+ visible = document.getElementById('btnReference').checked
+
+ if (visible)
+ onSideBarShow();
+ else
+ onSideBarHide();
+
+ return;
+}
+
+function onSearchBar(visible)
+{
+ if (visible == null)
+ visible = document.getElementById('btnSearchBar').checked
+
+ if (visible)
+ {
+ onSideBar(true);
+
+ document.getElementById('btnSearchBar').setAttribute('checked', 'true')
+ document.getElementById('vbSearchBar').removeAttribute('hidden');
+
+ return;
+ }
+
+ document.getElementById('vbSearchBar').setAttribute('hidden', 'true')
+ document.getElementById('btnSearchBar').removeAttribute('checked');
+ return;
+}
+
+function onFindString()
+{
+ document.getElementById("boxSearchError").removeAttribute('hidden');
+
+ // ... convert to lowercase, if the search is not case sensitive...
+ var editor = document.getElementById("sivEditor2").contentWindow.editor;
+ var reverse = !!document.getElementById('cbxBackward').checked;
+
+
+ function maxCursor(start,end)
+ {
+ if (start.line > end.line)
+ return start
+
+ if (start.line < end.line)
+ return end;
+
+ // start.line == end.line
+ if (start.ch > end.ch)
+ return start;
+
+ return end;
+ }
+
+ function minCursor(start,end)
+ {
+ if (start.line <end.line)
+ return start
+
+ if (start.line > end.line)
+ return end;
+
+ // start.line == end.line
+ if (start.ch > end.ch)
+ return end;
+
+ return start;
+ }
+
+ var start = editor.getCursor(true);
+ var end = editor.getCursor(false);
+
+ var cursor = editor.getSearchCursor(
+ document.getElementById("txtToken").value,
+ reverse ? minCursor(start,end) : maxCursor(start,end),
+ !document.getElementById('cbxCaseSensitive').checked);
+
+ if (!cursor.find(reverse))
+ {
+ // warp search at top or bottom
+ cursor = editor.getSearchCursor(
+ document.getElementById("txtToken").value,
+ reverse ? {line: editor.lineCount() - 1} : {line: 0, ch: 0},
+ !document.getElementById('cbxCaseSensitive').checked);
+
+ if (!cursor.find(reverse))
+ return;
+ }
+
+ if (reverse)
+ editor.setSelection(cursor.from(), cursor.to());
+ else
+ editor.setSelection(cursor.to(), cursor.from());
+
+ document.getElementById("boxSearchError").setAttribute('hidden','true');
+ return;
+}
+
+function onReplaceString()
+{
+ var token = document.getElementById("txtToken").value;
+ var editor = document.getElementById("sivEditor2").contentWindow.editor;
+ var caseSensitive = document.getElementById('cbxCaseSensitive').checked;
+
+ if (caseSensitive)
+ {
+ if (editor.getSelection() != token)
+ onFindString();
+
+ if (editor.getSelection() != token)
+ return;
+ }
+
+ if (!caseSensitive)
+ {
+ if (editor.getSelection().toLowerCase() != token.toLowerCase())
+ onFindString();
+
+ if (editor.getSelection().toLowerCase() != token.toLowerCase())
+ return;
+ }
+
+ editor.replaceSelection(document.getElementById("txtReplace").value);
+
+ onInput();
+
+ return;
+}
+
+
+
+function getPrintSettings()
+{
+ var pref = Components.classes["@mozilla.org/preferences-service;1"]
+ .getService(Components.interfaces.nsIPrefBranch);
+ if (pref)
+ {
+ var gPrintSettingsAreGlobal = pref.getBoolPref("print.use_global_printsettings", false);
+ var gSavePrintSettings = pref.getBoolPref("print.save_print_settings", false);
+ }
+
+ var printSettings;
+ try
+ {
+ var PSSVC = Components.classes["@mozilla.org/gfx/printsettings-service;1"]
+ .getService(Components.interfaces.nsIPrintSettingsService);
+ if (gPrintSettingsAreGlobal)
+ {
+ printSettings = PSSVC.globalPrintSettings;
+ this.setPrinterDefaultsForSelectedPrinter(PSSVC, printSettings);
+ }
+ else
+ {
+ printSettings = PSSVC.newPrintSettings;
+ }
+ }
+ catch (e)
+ {
+ alert("getPrintSettings: "+e+"\n");
+ }
+ return printSettings;
+}
+
+
+function onPrint()
+{
+ // we print in xml this means any specail charaters have to be html entities...
+ // ... so we need a dirty hack to convert all entities...
+ alert("Print");
+ var script = document.getElementById("sivContentEditor").value;
+ script = (new XMLSerializer()).serializeToString(document.createTextNode(script));
+
+ script = script.replace(/\r\n/g,"\r");
+ script = script.replace(/\n/g,"\r");
+ script = script.replace(/\r/g,"\r\n");
+
+ var data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
+ + "<?xml-stylesheet type=\"text/css\" href=\"chrome://sieve/content/editor/print.css\"?>\r\n"
+ + "<SieveScript>\r\n"
+ + "<title xmlns=\"http://www.w3.org/1999/xhtml\">\r\n"
+ + gSFE.getScriptName()
+ + "</title>\r\n"
+ + "<SieveScriptName>\r\n"
+ + gSFE.getScriptName()
+ + "</SieveScriptName>\r\n"
+ + "<SieveScriptLine>\r\n"
+ + script
+ + "</SieveScriptLine>\r\n"
+ + "</SieveScript>\r\n";
+
+ data = "data:application/xml;base64,"+btoa(data);
+
+ /*// get URI and add to list for printing
+ var messageList = new Array(1);
+ messageList[0] = data;
+
+ var prevPS = gPrintSettings;
+
+ var printSettingsService =
+ Components.classes["@mozilla.org/gfx/printsettings-service;1"]
+ .getService(Components.interfaces.nsIPrintSettingsService);
+
+ var printSettings = printSettingsService.CreatePrintSettings();
+ // var printSettings = printSettingsService.globalPrintSettings;
+
+ printEngineWindow = window.openDialog("chrome://messenger/content/msgPrintEngine.xul",
+ "",
+ "chrome,dialog=no,all,centerscreen",
+ messageList.length, messageList, statusFeedback,
+ printSettings, false,
+ Components.interfaces.nsIMsgPrintEngine.MNAB_PRINT_MSG,
+ window)*/
+
+
+ var printSettings;// = getPrintSettings();
+ /* get the print engine instance */
+ var printEngine = Components.classes["@mozilla.org/messenger/msgPrintEngine;1"].createInstance();
+ printEngine.QueryInterface(Components.interfaces.nsIMsgPrintEngine);
+
+ var printSettingsService =
+ Components.classes["@mozilla.org/gfx/printsettings-service;1"]
+ .getService(Components.interfaces.nsIPrintSettingsService);
+ var printSettings = printSettingsService.newPrintSettings;
+
+ printEngine.setWindow(window);
+ printEngine.doPrintPreview = false;
+ printEngine.showWindow(false);
+ printEngine.setMsgType(Components.interfaces.nsIMsgPrintEngine.MNAB_PRINT_MSG);
+ printEngine.setParentWindow(null);
+ //printEngine.setParentWindow(window);
+
+ var messageList = new Array(1);
+ messageList[0] = data;
+
+ printEngine.setPrintURICount(messageList.length);
+ printEngine.addPrintURI(messageList);
+
+ printEngine.startPrintOperation(printSettings);
+
+// printEngine.setStatusFeedback(statusFeedback);
+// printEngine.setStartupPPObserver(gStartupPPObserver);
+
+ alert("End Print");
+}
+/*function onPrint()
+{
+ var statusFeedback;
+ statusFeedback = Components.classes["@mozilla.org/messenger/statusfeedback;1"].createInstance();
+ statusFeedback = statusFeedback.QueryInterface(Components.interfaces.nsIMsgStatusFeedback);
+
+ // we print in xml this means any specail charaters have to be html entities...
+ // ... so we need a dirty hack to convert all entities...
+
+ var script = document.getElementById("sivContentEditor").value;
+ script = (new XMLSerializer()).serializeToString(document.createTextNode(script));
+
+ script = script.replace(/\r\n/g,"\r");
+ script = script.replace(/\n/g,"\r");
+ script = script.replace(/\r/g,"\r\n");
+
+ var data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
+ + "<?xml-stylesheet type=\"text/css\" href=\"chrome://sieve/content/editor/print.css\"?>\r\n"
+ + "<SieveScript>\r\n"
+ + "<title xmlns=\"http://www.w3.org/1999/xhtml\">\r\n"
+ + document.getElementById("txtName").value
+ + "</title>\r\n"
+ + "<SieveScriptName>\r\n"
+ + document.getElementById("txtName").value
+ + "</SieveScriptName>\r\n"
+ + "<SieveScriptLine>\r\n"
+ + script
+ + "</SieveScriptLine>\r\n"
+ + "</SieveScript>\r\n";
+
+ data = "data:application/xml;base64,"+btoa(data);
+
+
+ if (gPrintSettings == null)
+ gPrintSettings = PrintUtils.getPrintSettings();
+
+ printEngineWindow = window.openDialog("chrome://messenger/content/msgPrintEngine.xul",
+ "",
+ "chrome,dialog=no,all,centerscreen",
+ 1, [data], statusFeedback,
+ gPrintSettings,false,
+ Components.interfaces.nsIMsgPrintEngine.MNAB_PRINT_MSG,
+ window);
+
+ return;
+}*/
+
+function onKeepLocalScript()
+{
+ gSFE.onScriptLoaded(gSFE.getScript());
+}
+
+function onUseRemoteScript(script)
+{
+ gSFE.onScriptLoaded(script);
+}
+
+function onUndo()
+{
+ document.getElementById("sivEditor2").contentWindow.editor.undo();
+}
+
+function onRedo()
+{
+ document.getElementById("sivEditor2").contentWindow.editor.redo();
+}
+
+
+function onEditorShowMenu()
+{
+ // enxure editor is focused...
+ /*if (document.commandDispatcher.focusedElement != this.parentNode.firstChild)
+ this.parentNode.firstChild.focus();*/
+
+ // we can use the built in function to determin if cut, copy and paste is possible
+ var controller = document.commandDispatcher.getControllerForCommand("cmd_cut");
+ if (controller.isCommandEnabled("cmd_cut"))
+ document.getElementById("ctxCut").removeAttribute("disabled");
+ else
+ document.getElementById("ctxCut").setAttribute("disabled", "true");
+
+ var controller = document.commandDispatcher.getControllerForCommand("cmd_copy");
+ if (controller.isCommandEnabled("cmd_copy"))
+ document.getElementById("ctxCopy").removeAttribute("disabled");
+ else
+ document.getElementById("ctxCopy").setAttribute("disabled", "true");
+
+ var controller = document.commandDispatcher.getControllerForCommand("cmd_paste");
+ if (controller.isCommandEnabled("cmd_paste"))
+ document.getElementById("ctxPaste").removeAttribute("disabled");
+ else
+ document.getElementById("ctxPaste").setAttribute("disabled", "true");
+ // Undo : editor.history
+
+ var editor = document.getElementById("sivEditor2").contentWindow.editor;
+
+ if (editor.somethingSelected())
+ document.getElementById("ctxDelete").removeAttribute("disabled");
+ else
+ document.getElementById("ctxDelete").setAttribute("disabled", "true");
+
+ if (editor.historySize().undo > 0)
+ document.getElementById("ctxUndo").removeAttribute("disabled");
+ else
+ document.getElementById("ctxUndo").setAttribute("disabled", "true");
+ // select all -> immer möglich
+}
+
+function onDelete()
+{
+ document.getElementById("sivEditor2").contentWindow.editor.replaceSelection("");
+}
+
+function onSelectAll()
+{
+ var editor = document.getElementById("sivEditor2").contentWindow.editor;
+ editor.setSelection({line:0,ch:0},{line: editor.lineCount() - 1});
+}
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFilterEditor.xul b/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFilterEditor.xul
new file mode 100644
index 0000000..786a239
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFilterEditor.xul
@@ -0,0 +1,212 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+ <!--
+ The content of this file is licenced. You may obtain a copy of
+ the license at http://sieve.mozdev.org or request it via email
+ from the author. Do not remove or change this comment.
+
+ The initial author of the code is:
+ Thomas Schmid <schmid-thomas@gmx.net>
+
+ Contributor(s):
+ Marion Desnault
+-->
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
+<!DOCTYPE window [
+ <!ENTITY % sieveDTD SYSTEM "chrome://sieve/locale/locale.dtd">
+ %sieveDTD;
+ <!ENTITY % textcontextDTD SYSTEM "chrome://global/locale/textcontext.dtd">
+ %textcontextDTD;
+]>
+
+<?xml-stylesheet href="chrome://sieve/content/editor/Sieve.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/mailWindow1.css"?>
+
+<window
+ id="SieveFilterEditor"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ style="width: 45em; height: 31em;"
+ persist="width height screenX screenY"
+ onload="onWindowLoad();"
+ windowtype="Sieve:FilterEditor" >
+
+ <!-- needed for copy and paste -->
+ <script type="application/javascript"
+ src="chrome://global/content/globalOverlay.js" />
+ <script type="application/javascript"
+ src="chrome://sieve/content/libs/libManageSieve/SieveAbstractClient.js"/>
+ <script type="application/javascript"
+ src="chrome://sieve/content/editor/SieveFilterEditor.js" />
+ <!-- <script type="application/javascript"
+ src="chrome://global/content/printUtils.js"/>
+ <script type="application/javascript"
+ src="chrome://messenger/content/msgPrintEngine.js"/> -->
+
+ <keyset>
+ <key key="&shortcut.save.key;"
+ oncommand="onSave();"
+ modifiers="accel"/>
+ </keyset>
+
+ <menupopup id="ctxEditor" onpopupshowing="onEditorShowMenu();">
+ <menuitem id="ctxUndo"
+ label="&undoCmd.label;" accesskey="&undoCmd.accesskey;"
+ oncommand="onUndo(); event.stopPropagation();" />
+ <menuseparator/>
+ <menuitem id="ctxCut"
+ label="&cutCmd.label;" accesskey="&cutCmd.accesskey;"
+ oncommand="goDoCommand('cmd_cut'); event.stopPropagation();" />
+ <menuitem id="ctxCopy"
+ label="&copyCmd.label;" accesskey="&copyCmd.accesskey;"
+ oncommand="goDoCommand('cmd_copy'); event.stopPropagation();" />
+ <menuitem id="ctxPaste"
+ label="&pasteCmd.label;" accesskey="&pasteCmd.accesskey;"
+ oncommand=" goDoCommand('cmd_paste'); event.stopPropagation();" />
+ <menuitem id="ctxDelete"
+ label="&deleteCmd.label;" accesskey="&deleteCmd.accesskey;"
+ oncommand="onDelete(); event.stopPropagation();" />
+ <menuseparator/>
+ <menuitem label="&selectAllCmd.label;" accesskey="&selectAllCmd.accesskey;"
+ oncommand="onSelectAll(); event.stopPropagation();" />
+ </menupopup>
+
+<toolbox class="mail-toolbox">
+ <toolbar class="inline-toolbar chromeclass-toolbar">
+ <toolbarbutton id="btnSave"
+ label="&edit.toolbar.save;" class="toolbarbutton-1"
+ oncommand="onSave();" />
+ <toolbarspacer />
+ <toolbarbutton id="btnViewSource" type="checkbox"
+ label="Source" class="toolbarbutton-1"
+ oncommand="onViewSource();" checked="true"/>
+ <toolbarspacer />
+ <toolbarbutton id="btnUndo"
+ label="&edit.toolbar.undo;" class="toolbarbutton-1"
+ oncommand="onUndo()/*goDoCommand('cmd_undo')*/;" />
+ <toolbarbutton id="btnRedo"
+ label="&edit.toolbar.redo;" class="toolbarbutton-1"
+ oncommand="onRedo()/*goDoCommand('cmd_redo');*/" />
+ <toolbarspacer />
+ <toolbarbutton id="btnCut"
+ label="&edit.toolbar.cut;" class="toolbarbutton-1"
+ oncommand="goDoCommand('cmd_cut');" />
+ <toolbarbutton id="btnCopy"
+ label="&edit.toolbar.copy;" class="toolbarbutton-1"
+ oncommand="goDoCommand('cmd_copy');" />
+ <toolbarbutton id="btnPaste"
+ label="&edit.toolbar.paste;" class="toolbarbutton-1"
+ oncommand=" goDoCommand('cmd_paste');" />
+ <toolbarspacer />
+ <toolbarbutton id="btnCompile" type="checkbox"
+ label="&edit.toolbar.compile;" class="toolbarbutton-1"
+ oncommand="onErrorBar();" />
+ <toolbarbutton id="btnReference" type="checkbox"
+ label="&edit.toolbar.reference;" class="toolbarbutton-1"
+ oncommand="onSideBar();" />
+ <toolbarbutton id="btnSearchBar" type="checkbox"
+ label="&edit.toolbar.search;" class="toolbarbutton-1"
+ oncommand="onSearchBar();" />
+ <toolbarspacer />
+ <!--<toolbarbutton id="btnPrint"
+ label="&edit.toolbar.print;" class="toolbarbutton-1"
+ oncommand="onPrint();" />-->
+ <toolbarbutton id="btnTools"
+ label="&edit.toolbar.tools;"
+ type="menu" class="toolbarbutton-1" >
+ <menupopup id="puTools">
+ <menuitem label="&edit.toolbar.import;" oncommand="onImport();" />
+ <menuitem label="&edit.toolbar.export;" oncommand="onExport();" />
+ </menupopup>
+ </toolbarbutton>
+ <toolbarsplitter flex="1"/>
+ <box id="btnDonate" onclick="onDonate();"/>
+ </toolbar>
+</toolbox>
+
+ <hbox flex="1" >
+ <vbox flex="1">
+ <!-- Status messages -->
+ <iframe id="sivEditorStatus" flex="1" type="content"
+ src="chrome://sieve/content/editor/SieveStatus.xul" />
+
+ <deck id="dkView" flex="1" selectedIndex="0">
+ <!-- Sieve Script editor -->
+ <iframe id="sivEditor2" flex="1"
+ src="chrome://sieve/content/editor/SieveFilterEditor.html"
+ contextmenu="ctxEditor" />
+
+ <iframe id="sivWidgetEditor" flex="1" type="content"
+ src="about:blank" />
+ </deck>
+
+ <!-- Error Bar (Showing Script errors) -->
+ <splitter id="spErrorBar" resizeafter="farthest" />
+
+ <vbox id="vbErrorBar" persist="height">
+
+ <hbox align="center" min-height="50px" flex="1" style="overflow:auto;" >
+ <image id="imgErrorBar" pack="center" src="chrome://sieve/content/images/syntax-ok.png"/>
+ <description flex="1" id="lblErrorBar" style="white-space: pre;" >
+ </description>
+ </hbox>
+ </vbox>
+
+ </vbox>
+
+ <splitter id="spSideBar" class="spVertical" resizeafter="farthest" />
+
+ <vbox id="vbSidebar" persist="width" class="sieveSideBar">
+ <vbox id="vbSearchBar">
+ <stack class="outset">
+ <image flex="1"/>
+ <label flex="1" value="&edit.searchreplace.title;" />
+ </stack>
+
+ <hbox align="center">
+ <label value="&edit.searchreplace.searchfor;"/>
+ <textbox flex="1" id="txtToken"/>
+ </hbox>
+ <hbox align="center">
+ <label value="&edit.searchreplace.replacewith;"/>
+ <textbox flex="1" id="txtReplace" />
+ </hbox>
+ <groupbox orient="vertical" >
+ <caption label="&edit.searchreplace.options;"/>
+ <checkbox id="cbxCaseSensitive" label="&edit.searchreplace.case;" />
+ <checkbox id="cbxBackward" label="&edit.searchreplace.backward;"/>
+ </groupbox>
+ <hbox align="center" id="boxSearchError" hidden="true">
+ <image src="chrome://sieve/content/images/info.png" />
+ <label value="&edit.searchreplace.error;" />
+ </hbox>
+ <hbox pack="end">
+ <button id="btnFind" label="&edit.searchreplace.find;" oncommand="onFindString();"/>
+ <button id="btnReplace" label="&edit.searchreplace.replace;" oncommand="onReplaceString();"/>
+ </hbox>
+ <splitter resizeafter="farthest" disabled="true" />
+ </vbox>
+ <vbox flex="1">
+ <stack class="outset">
+ <image flex="1"/>
+ <label flex="1" value="&edit.sidebar.title;" />
+ </stack>
+ <hbox >
+ <toolbarbutton id="btnSideBarBack"
+ label="&edit.toolbar.back;" class="toolbarbutton-1"
+ oncommand="onSideBarBack();" />
+ <toolbarbutton id="btnSideBarForward"
+ label="&edit.toolbar.forward;" class="toolbarbutton-1"
+ oncommand="onSideBarForward();" />
+ <toolbarbutton id="btnSideBarHome"
+ label="&edit.toolbar.home;" class="toolbarbutton-1"
+ oncommand="onSideBarHome();" />
+ </hbox>
+ <deck id="dkSideBarBrowser" flex="1" selectedIndex="1">
+ <iframe id="ifSideBar" width="250px" flex="1" src="about:blank" type="content" />
+ <image flex="1" />
+ </deck>
+ </vbox>
+ </vbox>
+</hbox>
+</window>
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFilterExplorer.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFilterExplorer.js
new file mode 100644
index 0000000..c3cd28a
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFilterExplorer.js
@@ -0,0 +1,594 @@
+/*
+ *
+ * The contents of this file is licenced. You may obtain a copy of
+ * the license at http://sieve.mozdev.org or request it via email
+ * from the author. Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ *
+ * Hints for Spekt IDE autocomplete, they have to be in the first comment...
+ * @include "/sieve/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libManageSieve/SieveAccounts.js"
+ * @include "/sieve/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libManageSieve/Sieve.js"
+ * @include "/sieve/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libManageSieve/SieveRequest.js"
+ * @include "/sieve/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libManageSieve/SieveResponse.js"
+ * @include "/sieve/src/sieve@mozdev.org/chrome/chromeFiles/content/SieveOverlay.js
+ * @include "/sieve/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFilterTreeView.js"
+ */
+
+// Enable Strict Mode
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+Cu.import("chrome://sieve/content/modules/overlays/SieveOverlayManager.jsm");
+Cu.import("chrome://sieve/content/modules/utils/SieveWindowHelper.jsm");
+
+SieveOverlayManager.require("/sieve/SieveConnectionManager.js",this,window);
+SieveOverlayManager.require("/sieve/SieveAccounts.js",this,window);
+
+/** @type {{Components.interfaces.nsIConsoleService}}*/
+var gLogger = null;
+
+// We do it java style...
+function SieveFilterExplorer()
+{
+ SieveAbstractClient.call(this);
+
+ this._view = null;
+}
+
+SieveFilterExplorer.prototype.__proto__ = SieveAbstractClient.prototype;
+
+// TODO muss der error listener wirklich jedes mal gesetzet werden...
+// eigentlich müssete der default doch beim Objekt rauskommen...
+
+//-- Sieve Related Events
+SieveFilterExplorer.prototype.onListScriptResponse
+ = function(response)
+{
+ // Show List View...
+ var tree = document.getElementById('treeImapRules');
+
+ this._view.update(response.getScripts());
+ tree.view = this._view;
+
+ // always select something
+ if ((tree.currentIndex < 0) && (tree.view.rowCount > 0))
+ tree.view.selection.select(0);
+
+ this.onStatusChange(0);
+
+ // force repainting treeview to speedup the ui...
+ tree.treeBoxObject.invalidate();
+}
+
+SieveFilterExplorer.prototype.onSetActiveResponse
+ = function(response)
+{
+ // Always refresh the table ...
+ this.listScript();
+}
+
+SieveFilterExplorer.prototype.onDeleteScriptResponse
+ = function(response)
+{
+ // Always refresh the table ...
+ this.listScript();
+}
+
+SieveFilterExplorer.prototype.onChannelClosed
+ = function()
+{
+ // a channel is usually closed when a child window is closed. Therefore
+ // it is a good idea to refresh the list...
+ this.listScript();
+}
+
+SieveFilterExplorer.prototype.onChannelReady
+ = function(cid)
+{
+ // We observe only our channel...
+ if (cid != this._cid)
+ return;
+
+ // List all scripts as soon as we are connected
+ this.listScript();
+}
+
+SieveFilterExplorer.prototype._renameScript
+ = function (oldName, newName,isActive)
+{
+ // As we are emulating rename, the server does not check for scripts with...
+ // ... conflicting names. Instead it will overwrite such a script silently...
+ // ... So we try hard and double check our cached scriptnames for possible...
+ // ... conflicts inoder to prevent possible dataloss.
+
+ var tree = document.getElementById('treeImapRules');
+ for(var i = 0; i < this._view.rules.length; i++)
+ if (this._view.rules[i].script == newName)
+ return alert("Script already exists");
+
+ if (typeof(isActive) == "undefined")
+ isActive == tree.view.getCellValue(tree.currentIndex, tree.columns.getColumnAt(1));
+
+ SieveAbstractClient.prototype._renameScript.call(this,oldName, newName, isActive);
+}
+
+SieveFilterExplorer.prototype.connect
+ = function (account)
+{
+ if (!account)
+ account = getSelectedAccount();
+
+ SieveAbstractClient.prototype.connect.call(this,account);
+}
+
+
+SieveFilterExplorer.prototype.disconnect
+ = function (state,message)
+{
+ disableControls(true);
+ SieveAbstractClient.prototype.disconnect.call(this,state,message);
+}
+
+SieveFilterExplorer.prototype.onStatusChange
+ = function (state, message)
+{
+ // Script ready
+ if (state == 0)
+ {
+ disableControls(false);
+ document.getElementById("sivExplorerStatus").setAttribute('hidden','true');
+ document.getElementById('sivExplorerTree').removeAttribute('collapsed');
+ return;
+ }
+
+ // Capabilities...
+ if (state == 7)
+ {
+ document.getElementById('txtSASL').value = message.getSasl();
+ document.getElementById('txtExtensions').value = message.getExtensions(true);
+ document.getElementById('txtImplementation').value = message.getImplementation();
+ document.getElementById('txtVersion').value = "v"+message.getVersion().toFixed(2);
+ }
+
+ // The rest has to be redirected to the status window...
+ document.getElementById('sivExplorerTree').setAttribute('collapsed','true');
+ document.getElementById("sivExplorerStatus").contentWindow.onStatus(state,message)
+ document.getElementById("sivExplorerStatus").removeAttribute('hidden');
+}
+
+
+var gSFE = new SieveFilterExplorer();
+
+function onWindowLoad()
+{
+
+ // now create a logger session...
+ if (gLogger == null)
+ gLogger = Cc["@mozilla.org/consoleservice;1"]
+ .getService(Ci.nsIConsoleService);
+
+ var menuImapAccounts = document.getElementById("menuImapAccounts");
+
+ var accounts = SieveAccountManager.getAccounts();
+
+
+ for (var i = 0; i < accounts.length; i++)
+ {
+ menuImapAccounts.appendItem(
+ accounts[i].getDescription(),
+ accounts[i].getKey(),"").disabled = false;
+
+ if (window.arguments.length == 0)
+ continue;
+
+ if (window.arguments[0].wrappedJSObject.server != accounts[i].getUri())
+ continue;
+
+ menuImapAccounts.selectedIndex = i;
+ }
+
+ gSFE._view = new SieveTreeView(new Array(), onCycleCell);
+ document.getElementById('treeImapRules').view = gSFE._view;
+
+ if (menuImapAccounts.selectedIndex == -1)
+ menuImapAccounts.selectedIndex = 0;
+
+ onSelectAccount();
+
+}
+
+function closeTab()
+{
+ // Don't forget to close this channel...
+ if (gSFE)
+ gSFE.disconnect();
+
+ document.getElementById("sivExplorerStatus").contentWindow.onDetach();
+
+ return true;
+}
+
+/**
+ * @return {SieveAccount}
+ */
+function getSelectedAccount()
+{
+ var selectedItem = document.getElementById("menuImapAccounts").selectedItem;
+
+ if (!selectedItem)
+ return null;
+
+ return SieveAccountManager.getAccountByName(selectedItem.value);
+}
+
+function onSelectAccount(server)
+{
+ document.getElementById("sivExplorerStatus").contentWindow.onDetach();
+
+ gSFE.disconnect();
+
+ // update the TreeView...
+ var tree = document.getElementById('treeImapRules');
+
+ tree.view.selection.clearSelection();
+
+ gSFE._view.update(new Array());
+ tree.view = gSFE._view;
+
+ var account = null;
+
+ if (server)
+ account = SieveAccountManager.getAccountByServer(server);
+ else
+ account = getSelectedAccount();
+
+ document.getElementById("sivExplorerStatus").contentWindow
+ .onAttach(account,function() { gSFE.connect() });
+
+ if (account == null)
+ return gSFE.onStatusChange(2,"error.noaccount");
+
+ // Disable and cancel if account is not enabled
+ if ((!account.isEnabled()) || account.isFirstRun())
+ {
+ account.setFirstRun();
+ return gSFE.onStatusChange(8,0);
+ }
+
+ // TODO wait for timeout or session close before calling connect again
+ // otherwise we might endup using a closing channel. An isClosing function
+ // and the follwoing code migh help to detect this...
+
+/* if (isClosing)
+ setTimeout(function(){sivDisconnect(force=true); sivConnect(account)}, 1000);*/
+
+ gSFE.connect(account);
+}
+
+function onDeleteClick()
+{
+ var tree = document.getElementById('treeImapRules');
+ if (tree.currentIndex < 0)
+ return;
+
+ var prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"]
+ .getService(Ci.nsIPromptService);
+
+ var check = {value: false}; // default the checkbox to false
+
+ var flags = prompts.BUTTON_POS_0 * prompts.BUTTON_TITLE_YES +
+ prompts.BUTTON_POS_1 * prompts.BUTTON_TITLE_NO;
+
+ var strings = Services.strings.createBundle("chrome://sieve/locale/locale.properties")
+ // The checkbox will be hidden, and button will contain the index of the button pressed,
+ // 0, 1, or 2.
+
+ var button = prompts.confirmEx(null,
+ strings.GetStringFromName("list.delete.title"),
+ strings.GetStringFromName("list.delete.description"),
+ flags, "", "", "", null, check);
+
+ if (button != 0)
+ return;
+
+ var scriptName = new String(tree.view.getCellText(tree.currentIndex, tree.columns.getColumnAt(0)));
+
+ gSFE.deleteScript(scriptName)
+}
+/**
+ * @param {String} scriptName
+ * @param {String} scriptBody
+ */
+function sivOpenEditor(scriptName,scriptBody)
+{
+ /*var wm = Cc["@mozilla.org/appshell/window-mediator;1"]
+ .getService(Ci.nsIWindowMediator);
+
+ var enumerator = wm.getEnumerator("Sieve:FilterEditor");
+ while(enumerator.hasMoreElements())
+ {
+ var win = enumerator.getNext();
+
+ if (win.name != "x-sieve:"+sid+"/"+scriptName)
+ continue
+
+ if (win.closed)
+ continue;
+
+ win.focus();
+ return;
+ } */
+
+ var args = new Array();
+
+ args["scriptName"] = scriptName;
+ /*if (scriptBody)
+ args["scriptBody"] = scriptBody;*/
+
+ args["sieve"] = gSFE._sid;
+ args["uri"] = "x-sieve:"+gSFE._sid+"/"+scriptName;
+ args["account"] = getSelectedAccount().imapKey;
+
+ // This is a hack from DEVMO
+ args.wrappedJSObject = args;
+
+
+ /*Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci.nsIWindowWatcher)
+ .openWindow(null,"chrome://sieve/content/editor/SieveFilterEditor.xul",
+ "x-sieve:"+sid+"/"+scriptName,
+ "chrome,titlebar,resizable,centerscreen,all", args);*/
+
+ // Every script needs to be open in a unique tab...
+ // ... so we have to crawl through all windows through ...
+ // ... looking for a tabmail component and test there if ...
+ // ... the script is already open.
+ var mediator = Components
+ .classes["@mozilla.org/appshell/window-mediator;1"]
+ .getService(Components.interfaces.nsIWindowMediator);
+
+ var w = mediator.getXULWindowEnumerator(null);
+
+ while(w.hasMoreElements())
+ {
+ var win = w.getNext();
+ var docShells = win
+ .QueryInterface(Ci.nsIXULWindow).docShell
+ .getDocShellEnumerator(Ci.nsIDocShellTreeItem.typeChrome,Ci.nsIDocShell.ENUMERATE_FORWARDS);
+
+ while (docShells.hasMoreElements())
+ {
+ var childDoc = docShells.getNext()
+ .QueryInterface(Ci.nsIDocShell)
+ .contentViewer.DOMDocument;
+
+ //if (childDoc.location.href == "chrome://sieve/content/editor/SieveFilterExplorer.xul")
+
+ if (childDoc.location.href != "chrome://messenger/content/messenger.xul")
+ continue;
+
+ var tabmail = childDoc.getElementById("tabmail");
+
+ if (!tabmail)
+ continue;
+
+ if (!tabmail.tabModes.SieveEditorTab)
+ continue;
+
+ if (!tabmail.tabModes.SieveEditorTab.tabs.length)
+ continue;
+
+ for (var i = 0; i < tabmail.tabModes.SieveEditorTab.tabs.length ; i++)
+ {
+ if (tabmail.tabModes.SieveEditorTab.tabs[i].uri != args["uri"])
+ continue;
+
+ tabmail.switchToTab(tabmail.tabModes.SieveEditorTab.tabs[i]);
+ childDoc.defaultView.QueryInterface(Ci.nsIDOMWindow).focus();
+
+ return;
+ }
+ }
+ }
+
+ var mail3PaneWindow = Cc["@mozilla.org/appshell/window-mediator;1"]
+ .getService(Ci.nsIWindowMediator)
+ .getMostRecentWindow("mail:3pane");
+
+ tabmail = mail3PaneWindow.document.getElementById("tabmail");
+ tabmail.openTab("SieveEditorTab", args);
+ return;
+}
+
+
+function onNewClick()
+{
+ // Instead of prompting for the scriptname, setting the scriptname to an
+ // unused scriptname (eg. unnamed+000]) would offer a better workflow...
+ // Also put a template script would be good...
+
+ var prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"]
+ .getService(Ci.nsIPromptService);
+
+ var input = {value:"unnamed"};
+ var check = {value:false};
+
+ var strings = Services.strings.createBundle("chrome://sieve/locale/locale.properties")
+ // The checkbox will be hidden, and button will contain the index of the button pressed,
+ // 0, 1, or 2.
+
+ var result
+ = prompts.prompt(
+ window,
+ strings.GetStringFromName("list.new.title"),
+ strings.GetStringFromName("list.new.description"),
+ input, null, check);
+
+ // Did the User cancel the dialog?
+ if (result != true)
+ return;
+
+ sivOpenEditor(input.value);
+}
+
+function onEditClick()
+{
+ var tree = document.getElementById('treeImapRules');
+ if (tree.currentIndex < 0)
+ return;
+
+ var scriptName = new String(tree.view.getCellText(tree.currentIndex, tree.columns.getColumnAt(0)));
+
+ sivOpenEditor(scriptName);
+
+ return;
+}
+
+
+
+
+function disableControls(disabled)
+{
+ if (disabled)
+ {
+ document.getElementById('newButton').setAttribute('disabled','true');
+ document.getElementById('editButton').setAttribute('disabled','true');
+ document.getElementById('deleteButton').setAttribute('disabled','true');
+ document.getElementById('renameButton').setAttribute('disabled','true');
+ document.getElementById('btnActivateScript').setAttribute('disabled','true');
+ document.getElementById('treeImapRules').setAttribute('disabled','true');
+ document.getElementById('btnServerDetails').setAttribute('disabled','true');
+ document.getElementById('vbServerDetails').setAttribute('hidden','true');
+ }
+ else
+ {
+ document.getElementById('newButton').removeAttribute('disabled');
+ document.getElementById('editButton').removeAttribute('disabled');
+ document.getElementById('deleteButton').removeAttribute('disabled');
+ document.getElementById('btnActivateScript').removeAttribute('disabled');
+ document.getElementById('renameButton').removeAttribute('disabled');
+ document.getElementById('treeImapRules').removeAttribute('disabled');
+ document.getElementById('btnServerDetails').removeAttribute('disabled');
+ }
+}
+
+
+function onRenameClick()
+{
+
+ var tree = document.getElementById('treeImapRules');
+
+ if (tree.currentIndex == -1)
+ return;
+
+ var oldScriptName = new String(tree.view.getCellText(tree.currentIndex, tree.columns.getColumnAt(0)));
+
+ var prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"]
+ .getService(Ci.nsIPromptService);
+
+ var input = {value:oldScriptName};
+ var check = {value:false};
+
+ var strings = Services.strings.createBundle("chrome://sieve/locale/locale.properties")
+ // The checkbox will be hidden, and button will contain the index of the button pressed,
+ // 0, 1, or 2.
+
+ var result
+ = prompts.prompt(
+ window,
+ strings.GetStringFromName("list.rename.title"),
+ strings.GetStringFromName("list.rename.description"),
+ input, null, check);
+
+ // Did the User cancel the dialog?
+ if (result != true)
+ return;
+
+ // it the old name equals the new name, ignore the request.
+ if (input.value.toLowerCase() == oldScriptName.toLowerCase())
+ return;
+
+ gSFE.renameScript(oldScriptName, input.value);
+}
+
+function onServerDetails()
+{
+ var el = document.getElementById("vbServerDetails");
+ var img = document.getElementById("imgServerDetails");
+
+ if (el.hasAttribute('hidden'))
+ {
+ img.setAttribute('src','chrome://global/skin/tree/twisty-open.png');
+ el.removeAttribute('hidden');
+ }
+ else
+ {
+ el.setAttribute('hidden','true');
+ img.setAttribute('src','chrome://global/skin/tree/twisty-clsd.png');
+ }
+}
+
+function onSettingsClick()
+{
+ var server = Cc['@mozilla.org/messenger/account-manager;1']
+ .getService(Ci.nsIMsgAccountManager)
+ .getIncomingServer(getSelectedAccount().imapKey);
+
+ SieveUtils.OpenSettings(window,server);
+}
+
+function onActivateClick()
+{
+ var tree = document.getElementById('treeImapRules');
+ if (tree.currentIndex < 0)
+ return;
+
+ // imitate click in the treeview
+ tree.view.cycleCell(tree.currentIndex,tree.columns.getColumnAt(1));
+
+ return;
+}
+
+function onTreeDblClick(ev)
+{
+ var tree = document.getElementById('treeImapRules');
+ // TODO test if tree is visible
+ var row = {}, column = {}, part = {};
+
+ tree.treeBoxObject.getCellAt(ev.clientX, ev.clientY, row, column, part);
+
+ if ((row.value == -1) || (column.value == -1))
+ return;
+
+ // ignore cycler cells, e.g. the one to (de)active scripts
+ if (column.value.cycler)
+ return;
+
+ var scriptName = tree.view.getCellText(row.value, tree.columns.getColumnAt(0));
+
+ sivOpenEditor(scriptName);
+
+ return;
+}
+
+function onCycleCell(row,col,script,active)
+{
+ gSFE.setActiveScript((active?null:script))
+}
+
+function onDonate()
+{
+ var url = Cc["@mozilla.org/network/io-service;1"]
+ .getService(Ci.nsIIOService)
+ .newURI("https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EAS576XCWHKTC", null, null)
+
+ Cc["@mozilla.org/uriloader/external-protocol-service;1"]
+ .getService(Ci.nsIExternalProtocolService)
+ .loadUrl(url);
+} \ No newline at end of file
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFilterExplorer.xul b/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFilterExplorer.xul
new file mode 100644
index 0000000..a5d9417
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFilterExplorer.xul
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ The content of this file is licenced. You may obtain a copy of
+ the license at http://sieve.mozdev.org or request it via email
+ from the author. Do not remove or change this comment.
+
+ The initial author of the code is:
+ Thomas Schmid <schmid-thomas@gmx.net>
+
+ Contributor(s):
+ Marion Desnault
+-->
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
+<!DOCTYPE window SYSTEM "chrome://sieve/locale/locale.dtd">
+
+<?xml-stylesheet href="chrome://sieve/content/editor/Sieve.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/accountManage.css" type="text/css"?>
+<?xml-stylesheet href="chrome://global/skin/inContentUI.css" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="SieveFilterExplorer"
+ onload="onWindowLoad();"
+ persist="width height screenX screenY"
+ style="width: 45em; height: 31em;"
+ title="&list.title;"
+ windowtype="Sieve:FilterExplorer">
+
+
+ <script type="application/javascript"
+ src="chrome://sieve/content/libs/libManageSieve/SieveAbstractClient.js"/>
+ <script type="application/javascript"
+ src="chrome://sieve/content/editor/SieveFiltersTreeView.js"/>
+ <script type="application/javascript"
+ src="chrome://sieve/content/editor/SieveFilterExplorer.js"/>
+
+ <hbox align="center">
+ <label value="&list.accounts;" control="serverMenu"/>
+ <menulist id="menuImapAccounts" oncommand="onSelectAccount();">
+ </menulist>
+ <spacer flex="1"/>
+ <box id="btnDonate" onclick="onDonate();"/>
+ </hbox>
+
+ <grid flex="1" id="gridList" >
+ <columns>
+ <column flex="1"/>
+ <column/>
+ </columns>
+ <rows>
+ <row>
+ <separator class="thin"/>
+ </row>
+ <row>
+ <description>&list.tree;</description>
+ </row>
+
+ <row flex="1">
+ <vbox>
+ <tree id="treeImapRules" flex="1"
+ hidecolumnpicker="true" seltype="single"
+ ondblclick="onTreeDblClick(event);">
+ <treecols>
+ <treecol id="namecol" label="&list.tree.name;" flex="1"/>
+ <treecol id="active" label="&list.tree.active;" cycler="true"/>
+ </treecols>
+
+ <treechildren id="sivExplorerTree" collapsed="true" />
+ <iframe id="sivExplorerStatus" flex="1" src="chrome://sieve/content/editor/SieveStatus.xul" />
+
+ </tree>
+ <vbox>
+ <button id="btnServerDetails" oncommand="onServerDetails();" flex="1">
+ <hbox flex="1" align="center" >
+ <image pack="center" id="imgServerDetails" height="9px" width="9px"
+ src="chrome://global/skin/tree/twisty-clsd.png" />
+ <label style="text-align:left" value="&list.details.capability;" flex="1" />
+ </hbox>
+ </button>
+
+ <vbox id="vbServerDetails" >
+ <stack flex="1" class="inset">
+ <spacer/>
+ <grid>
+ <columns>
+ <column />
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row align="baseline">
+ <hbox pack="end"><label value="&list.details.capability.implementation;"/></hbox>
+ <textbox id="txtImplementation" class="plain"/>
+ </row>
+ <row align="baseline">
+ <hbox pack="end"><label value="&list.details.capability.extensions;"/></hbox>
+ <textbox id="txtExtensions" class="plain"/>
+ </row>
+ <row align="baseline">
+ <hbox pack="end"><label value="&list.details.capability.sasl;"/></hbox>
+ <textbox id="txtSASL" class="plain"/>
+ </row>
+ <row align="baseline">
+ <hbox pack="end"><label value="&list.details.capability.protocol;"/></hbox>
+ <textbox id="txtVersion" class="plain"/>
+ </row>
+ </rows>
+ </grid>
+ </stack>
+ </vbox>
+ </vbox>
+ </vbox>
+ <vbox>
+ <button id="newButton" label="&list.new;"
+ oncommand="onNewClick();"/>
+ <button id="editButton" label="&list.edit;"
+ oncommand="onEditClick();"/>
+ <button id="deleteButton" label="&list.delete;"
+ oncommand="onDeleteClick();"/>
+ <button id="renameButton" label="&list.rename;"
+ oncommand="onRenameClick();"/>
+ <button id="btnActivateScript" label="&list.activate;"
+ oncommand="onActivateClick();"/>
+ <spacer flex="1"/>
+ <button id="settings" label="&list.settings;"
+ oncommand="onSettingsClick();"/>
+ <spacer flex="1"/>
+ </vbox>
+ </row>
+ </rows>
+ </grid>
+</window>
+
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFiltersTreeView.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFiltersTreeView.js
new file mode 100644
index 0000000..2a20eb6
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveFiltersTreeView.js
@@ -0,0 +1,94 @@
+/*
+ * The contents of this file is licenced. You may obtain a copy of
+ * the license at http://sieve.mozdev.org or request it via email
+ * from the author. Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ */
+
+// This is our custom view, based on the treeview interface
+
+function SieveTreeView(rules, listener)
+{
+ this.listener = listener;
+ this.rules = rules;
+ this.rowCount = rules.length;
+}
+
+SieveTreeView.prototype.update
+ = function(rules)
+{
+ this.rules = rules;
+ this.rowCount = this.rules.length;
+
+ this.rules.sort( function(a,b){
+ return a.script.toLocaleLowerCase().localeCompare(b.script.toLocaleLowerCase());
+ /*if (a.script.toLowerCase() > b.script.toLowerCase()) return 1;
+ if (a.script.toLowerCase() < b.script.toLowerCase()) return -1;
+ return 0;*/});
+}
+
+SieveTreeView.prototype.getCellValue
+ = function(row,column)
+{
+ if (column.id == "namecol")
+ return this.rules[row].script;
+ else
+ return this.rules[row].active;
+}
+
+SieveTreeView.prototype.getCellText
+ = function(row,column)
+{
+ if (column.id == "namecol")
+ return this.rules[row].script;
+ else
+ return "";
+}
+
+SieveTreeView.prototype.setTree
+ = function(treebox){ this.treebox = treebox; }
+
+SieveTreeView.prototype.isContainer
+ = function(row){ return false; }
+
+SieveTreeView.prototype.isSeparator
+ = function(row){ return false; }
+
+SieveTreeView.prototype.isSorted
+ = function(row){ return false; }
+
+SieveTreeView.prototype.getLevel
+ = function(row){ return 0; }
+
+SieveTreeView.prototype.getImageSrc
+ = function(row,column)
+{
+ if (column.id == "namecol")
+ return null;
+
+ if (this.rules[row].active)
+ return "chrome://sieve/content/images/active.png"
+
+ return "chrome://sieve/content/images/passive.png"
+}
+
+SieveTreeView.prototype.getRowProperties
+ = function(row,props){}
+
+SieveTreeView.prototype.getCellProperties
+ = function(row,col,props){}
+
+SieveTreeView.prototype.getColumnProperties
+ = function(colid,col,props){}
+
+SieveTreeView.prototype.cycleHeader
+ = function(col){}
+
+SieveTreeView.prototype.cycleCell
+ = function(row, col)
+{
+ this.listener(row,col,this.rules[row].script,this.rules[row].active);
+ this.selection.select(row);
+}
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveStatus.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveStatus.js
new file mode 100644
index 0000000..de58a6b
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveStatus.js
@@ -0,0 +1,271 @@
+/*
+ * The content of this file is licenced. You may obtain a copy of the license
+ * at http://sieve.mozdev.org or request it via email from the author.
+ *
+ * Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ *
+ */
+
+// Enable Strict Mode
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+Cu.import("chrome://sieve/content/modules/overlays/SieveOverlayManager.jsm");
+Cu.import("chrome://sieve/content/modules/utils/SieveWindowHelper.jsm");
+SieveOverlayManager.require("/sieve/SieveAutoConfig.js",this,window);
+
+/*
+ * Auto Config
+ */
+
+var gAutoConfig = null;
+var gAccount = null;
+var gCallback = null;
+var gCallbacks = null;
+
+
+var gAutoConfigEvent =
+{
+ onSuccess : function(host,port,proxy)
+ {
+ onStatus(8,2);
+
+ gAccount.setActiveHost(0);
+ gAccount.getHost().setPort(port);
+ gAccount.setEnabled(true);
+
+ gAutoConfig = null;
+ },
+
+ onError : function()
+ {
+ onStatus(8,3);
+ gAutoConfig = null;
+ }
+}
+
+function onAutoConfigRunClick()
+{
+ if (gAutoConfig)
+ gAutoConfig.cancel();
+
+ gAutoConfig = new SieveAutoConfig();
+
+ gAutoConfig.addHost(
+ gAccount.getHost(0).getHostname(),
+ 4190,
+ gAccount.getProxy().getProxyInfo());
+
+ gAutoConfig.addHost(
+ gAccount.getHost(0).getHostname(),
+ 2000,
+ gAccount.getProxy().getProxyInfo());
+
+ gAutoConfig.run(gAutoConfigEvent);
+
+ onStatus(8,1);
+}
+
+function onAutoConfigCancelClick()
+{
+ gAutoConfig.cancel();
+ gAutoConfig = null;
+
+ onStatus(8,3);
+}
+
+function onAutoConfigFinishedClick()
+{
+ gCallback();
+}
+
+function onReconnectClick()
+{
+ gCallback();
+}
+
+function onBadCertOverride(targetSite,permanent)
+{
+ try
+ {
+ var overrideService = Cc["@mozilla.org/security/certoverride;1"]
+ .getService(Ci.nsICertOverrideService);
+
+ var recentCertsSvc = Cc["@mozilla.org/security/recentbadcerts;1"]
+ .getService(Ci.nsIRecentBadCertsService);
+
+ var status = recentCertsSvc.getRecentBadCert(targetSite);
+ if (!status)
+ throw "No certificate stored for taget Site..."
+
+ var flags = ((status.isUntrusted)? overrideService.ERROR_UNTRUSTED : 0)
+ | ((status.isDomainMismatch)? overrideService.ERROR_MISMATCH : 0)
+ | ((status.isNotValidAtThisTime)? overrideService.ERROR_TIME : 0);
+
+ var cert = status.QueryInterface(Ci.nsISSLStatus).serverCert;
+ if (!cert)
+ throw "Status does not contain a certificate..."
+
+ overrideService.rememberValidityOverride(
+ targetSite.split(":")[0], // Host Name with port (host:port)
+ targetSite.split(":")[1],
+ cert,
+ flags,
+ !permanent);
+
+ gCallback();
+ }
+ catch (ex)
+ {
+ onStatus(2,"error.brokencert");
+ gLogger.logStringMessage("onBadCertOverride:"+ex);
+ }
+
+}
+
+function onDetach()
+{
+ if (gAutoConfig)
+ {
+ gAutoConfig.cancel();
+ gAutoConfig = null;
+ }
+
+ gCallback = null;
+ gCallbacks = null;
+}
+
+/**
+ * The callback is invoced inf the user wants to reconnect
+ *
+ * @param {} account
+ * @param {} callback
+ *
+ */
+function onAttach(account, callback,callbacks)
+{
+ if (gAutoConfig)
+ {
+ gAutoConfig.cancel();
+ gAutoConfig = null;
+ }
+
+ gAccount = account;
+ gCallback = callback;
+ if (callbacks)
+ gCallbacks = callbacks;
+}
+
+function onStatus(state, message)
+{
+ // we need this array to corelate status ids and the deck's selectedIndex
+ // 0:StatusWait, 1:StatusBadCert, 2:StatusDisabled, 3:StatusConnectionLost,
+ // 4:StatusOffline, 5:StatusWarning, 6:StatusOutOfSync, 7:StatusError
+ var mapping = { 0:null,1:5,2:7,3:0,4:7,5:1,6:4,7:null,8:2,9:3,10:6};
+
+ try {
+ var strings = Services.strings.createBundle("chrome://sieve/locale/locale.properties");
+
+
+ switch (state)
+ {
+ case 1: document.getElementById('StatusWarningMsg')
+ .firstChild.nodeValue = strings.GetStringFromName(message);
+ break;
+ // client error
+ case 2: document.getElementById('StatusErrorMsg')
+ .firstChild.nodeValue = strings.GetStringFromName(message);
+ break;
+ case 3: document.getElementById('StatusWaitMsg')
+ .firstChild.nodeValue = strings.GetStringFromName(message);
+ break;
+ // server error
+ case 4: document.getElementById('StatusErrorMsg').textContent = message;
+ break;
+ case 5: document.getElementById("btnIgnoreBadCert").setAttribute("message", message);
+ document.getElementById("btnIgnoreBadCert").setAttribute("oncommand",
+ "onBadCertOverride(this.getAttribute('message'),document.getElementById('cbBadCertRemember').checked);");
+
+ document.getElementById("btnAbortBadCert").setAttribute("oncommand",
+ "onStatus(1,'warning.brokencert');");
+
+ break;
+ // Offline Mode
+ case 6:
+ break;
+ // Capabilities set...
+ case 7:
+ return;
+ // account disabled
+ case 8:
+ document.getElementById('sivAutoConfig').setAttribute("selectedIndex",message);
+ break;
+
+ case 9:
+ break;
+
+ case 10:
+ document.getElementById("sivClientSide").value = message.local;
+ document.getElementById("sivServerSide").value = message.remote;
+ break;
+
+ // Show the tobber as default...
+ default:
+ document.getElementById('StatusWaitMsg').firstChild.nodeValue = "";
+ state = 0;
+ }
+
+ document.getElementById('StatusDeck').setAttribute("selectedIndex",""+mapping[state]);
+
+ } catch (ex)
+ {
+ Cu.reportError(state+" || "+message +"||"+ex.toSource());
+ }
+}
+
+function onSettingsClick()
+{
+ var server = Cc['@mozilla.org/messenger/account-manager;1']
+ .getService(Ci.nsIMsgAccountManager)
+ .getIncomingServer(gAccount.imapKey);
+
+ SieveUtils.OpenSettings(window,server);
+}
+
+
+function onGoOnlineClick()
+{
+ var ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+ ioService.offline = false;
+
+ gCallback();
+}
+
+
+function onKeepLocal()
+{
+ gCallbacks.onKeepLocal();
+}
+
+function onUseRemote()
+{
+ gCallbacks.onUseRemote(document.getElementById("sivServerSide").value);
+}
+
+
+// ChannelCreated
+// ChannelClosed
+// ChannelReady
+// ChannelStatus
+
+// Events Account Switch
+// Status Change
+
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveStatus.xul b/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveStatus.xul
new file mode 100644
index 0000000..7d3e697
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/SieveStatus.xul
@@ -0,0 +1,209 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+ <!--
+ The content of this file is licenced. You may obtain a copy of
+ the license at http://sieve.mozdev.org or request it via email
+ from the author. Do not remove or change this comment.
+
+ The initial author of the code is:
+ Thomas Schmid <schmid-thomas@gmx.net>
+-->
+
+<!DOCTYPE window SYSTEM "chrome://sieve/locale/locale.dtd">
+
+<?xml-stylesheet href="chrome://sieve/content/editor/Sieve.css" type="text/css"?>
+
+<!--
+ Contiains commonly used dialogs during login etc...
+-->
+
+<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" >
+
+ <script type="application/javascript"
+ src="chrome://sieve/content/libs/libManageSieve/SieveRequest.js"/>
+ <script type="application/javascript"
+ src="chrome://sieve/content/libs/libManageSieve/SieveResponseCodes.js"/>
+ <script type="application/javascript"
+ src="chrome://sieve/content/libs/libManageSieve/SieveResponse.js"/>
+
+
+ <script type="application/javascript"
+ src="chrome://sieve/content/libs/libManageSieve/SieveAutoConfig.js" />
+
+ <script type="application/javascript"
+ src="chrome://sieve/content/editor/SieveStatus.js"/>
+
+ <deck id="StatusDeck" flex="1">
+
+ <!-- 0. Wait tobber -->
+ <vbox id="StatusWait" align="center" pack="center" flex="1" >
+ <image pack="center" src="chrome://sieve/content/images/load.gif"/>
+ <spacer/>
+ <description id="StatusWaitMsg" pack="center">
+ </description>
+ </vbox>
+
+ <!-- 1. Bad Cert Warning Message -->
+ <box id="StatusBadCert" align="center" pack="center" flex="1">
+ <hbox align="center" class="sivWarning" >
+ <image pack="center" src="chrome://sieve/content/images/sslWarning.png"/>
+ <vbox>
+ <description style="font-weight:bold">&list.badcert;</description>
+ <description pack="center" style="white-space: pre-wrap;">&list.badcert.error;</description>
+ <checkbox id="cbBadCertRemember" label="&list.badcert.remember;" checked="true"/>
+ <hbox align="right">
+ <button id="btnIgnoreBadCert" label="&list.badcert.ignore;" />
+ <button id="btnAbortBadCert" label="&list.badcert.abort;" />
+ </hbox>
+ </vbox>
+ </hbox>
+ </box>
+
+ <!-- 2. Error Message: Account not configured -->
+ <vbox id="StatusDisabled" align="center" pack="center" flex="1">
+ <box>
+ <deck id="sivAutoConfig" selectedIndex="0">
+
+ <!-- Welcome -->
+ <vbox id="sivAutoConfigInfo">
+ <label style="font-weight:bold">&list.autoconfig.welcome.caption;</label>
+ <description>&list.autoconfig.welcome.description1;</description>
+ <description>&list.autoconfig.welcome.description2;</description>
+ <separator flex="1"/>
+ <hbox align="right">
+ <button label="&list.autoconfig.advanced;"
+ oncommand="onSettingsClick();" />
+ <button label="&list.autoconfig.continue;"
+ oncommand="onAutoConfigRunClick();" />
+ </hbox>
+ </vbox>
+
+ <!-- Detecting -->
+ <vbox id="sivAutoConfigWait">
+ <label style="font-weight:bold">&list.autoconfig.detecting.caption;</label>
+ <label>&list.autoconfig.detecting.description;</label>
+ <separator flex="1"/>
+ <hbox align="right">
+ <button label="&list.autoconfig.cancel;"
+ oncommand="onAutoConfigCancelClick();" />
+ </hbox>
+ </vbox>
+
+ <!-- Success -->
+ <vbox id="sivAutoConfigInfo">
+ <label style="font-weight:bold">&list.autoconfig.success.caption;</label>
+ <description>&list.autoconfig.success.description;</description>
+ <separator flex="1"/>
+ <hbox align="right" >
+ <button label="&list.autoconfig.advanced;"
+ oncommand="onSettingsClick();" />
+ <button label="&list.autoconfig.continue;"
+ oncommand="onAutoConfigFinishedClick();" />
+ </hbox>
+ </vbox>
+
+ <!-- Error -->
+ <vbox id="sivAutoConfigError">
+ <label style="font-weight:bold">&list.autoconfig.error.caption;</label>
+ <description>&list.autoconfig.error.description;</description>
+ <separator/>
+ <hbox align="right">
+ <button label="&list.autoconfig.retest;"
+ oncommand="onAutoConfigRunClick();" />
+ <button label="&list.autoconfig.config;"
+ oncommand="onSettingsClick();" />
+ </hbox>
+ </vbox>
+ </deck>
+ </box>
+ </vbox>
+
+
+ <!-- 3. Error Message: Connecton to server lost... -->
+ <box id="StatusConnectionLost" align="center" pack="center" flex="1">
+ <hbox align="center" class="sivError" >
+ <image pack="center" src="chrome://sieve/content/images/syntax-error.png"/>
+ <vbox>
+ <description pack="center" flex="1">
+ &status.connectivity.loss.description;
+ </description>
+ <hbox align="right">
+ <button label="&status.connectivity.loss.reconnect;" oncommand="onReconnectClick();" />
+ </hbox>
+ </vbox>
+ </hbox>
+ </box>
+
+ <!-- 4. Error Message: Thunderbird in offline mode-->
+ <box id="StatusOffline" align="center" pack="center" flex="1">
+ <hbox align="center" class="sivWarning" >
+ <image pack="center" src="chrome://sieve/content/images/syntax-ok.png"/>
+ <vbox>
+ <description pack="center" flex="1">
+ &status.offline.description;
+ </description>
+ <hbox align="right">
+ <button label="&status.offline.go;" oncommand="onGoOnlineClick();" />
+ </hbox>
+ </vbox>
+ </hbox>
+ </box>
+
+ <box id="StatusWarning" align="center" pack="center" flex="1" >
+ <hbox align="center" class="sivWarning">
+ <image pack="center" src="chrome://sieve/content/images/syntax-ok.png"/>
+ <vbox>
+ <description id="StatusWarningMsg" pack="center">&status.warning;</description>
+ <hbox align="right">
+ <button id="btnWarningContinue" label="&status.warning.ignore;"
+ oncommand="onIgnoreOffline();" />
+ <button label="&status.warning.reconnect;" oncommand="onReconnectClick();" />
+ </hbox>
+ </vbox>
+ </hbox>
+ </box>
+
+ <vbox id="StatusOutOfSync" flex="1" >
+ <vbox class="sivWarningBar" align="center" pack="center" >
+ <description id="StatusWarningMsg">&status.sync.description;</description>
+ <hbox align="right" pack="right">
+ <button label="&status.sync.keep;"
+ oncommand="onKeepLocal()" />
+ <button label="&status.sync.replace;"
+ oncommand="onUseRemote()" />
+ </hbox>
+ </vbox>
+
+ <hbox flex="1" style="margin:0px; padding:0px;" class="sivEditor">
+
+ <textbox id="sivClientSide" flex="1"
+ readonly="true"
+ multiline="true" wrap="off"
+ autocomplete="off" />
+
+ <splitter class="spVertical" resizeafter="farthest" />
+
+ <textbox id="sivServerSide" flex="1"
+ readonly="true"
+ multiline="true" wrap="off"
+ autocomplete="off"/>
+ </hbox>
+ </vbox>
+
+ <!-- Error Message -->
+ <box id="StatusError" align="center" pack="center" flex="1">
+ <hbox align="center" class="sivError">
+ <image pack="center" src="chrome://sieve/content/images/syntax-error.png"/>
+ <vbox>
+ <description style="font-weight:bold">&status.error;</description>
+ <description id="StatusErrorMsg" pack="center" ></description>
+ <hbox align="right">
+ <button label="&status.error.reconnect;" oncommand="onReconnectClick();" />
+ </hbox>
+ </vbox>
+ </hbox>
+ </box>
+
+ </deck>
+</page>
+ \ No newline at end of file
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/print.css b/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/print.css
new file mode 100644
index 0000000..24a15cc
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/editor/print.css
@@ -0,0 +1,25 @@
+
+SieveScript
+{
+ font-family: Georgia, Bitstream Vera, Serif;
+ display: block;
+}
+
+SieveScriptName
+{
+ color: #666;
+ display:block;
+ font-weight:bold;
+
+ font-size: 18pt;
+ border-bottom: 1pt dotted #bbb;
+ margin-bottom: 10pt;
+}
+
+SieveScriptLine
+{
+ display:block;
+ font-size: 10pt;
+ white-space: pre-wrap;
+}
+
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/filterList/SieveFilterList.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/filterList/SieveFilterList.js
new file mode 100644
index 0000000..44999ec
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/filterList/SieveFilterList.js
@@ -0,0 +1,219 @@
+/*
+ * The content of this file is licenced. You may obtain a copy of the license
+ * at http://sieve.mozdev.org or request it via email from the author.
+ *
+ * Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ *
+ */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("chrome://sieve/content/modules/overlays/SieveOverlayManager.jsm");
+
+SieveOverlayManager.require("/sieve/SieveConnectionManager.js",this,window);
+SieveOverlayManager.require("/sieve/SieveAccounts.js",this,window);
+
+
+function errorhandler(msg, url, line)
+ {
+ alert(msg);
+ Components.utils.reportError(msg);
+ }
+
+window.onerror = errorhandler;
+
+
+// TODO möglichkeit bauen einen FilterList Dialog an die GUI zu binden bzw.
+// davon zu befreien. Dadurch wird garantiert dass immer nur die aktuelle
+// Session angezeigt wird.
+
+
+function SieveFilterListDialog()
+{
+ SieveAbstractClient.call(this);
+ this._script = "Thunderbird Mailfilters"
+}
+
+SieveFilterListDialog.prototype.__proto__ = SieveAbstractClient.prototype;
+
+SieveFilterListDialog.prototype.onListScriptResponse
+ = function(response)
+{
+ var scripts = response.getScripts();
+
+ for (var i=0; i<scripts.length; i++)
+ {
+ if (!scripts[i].active)
+ continue;
+
+ this._script = scripts[i].script;
+
+ this.getScript(this._script);
+ return;
+ }
+
+ try {
+
+ var capabilities = SieveConnections
+ .getChannel(this._sid,this._cid).extensions;
+
+ document.getElementById("sivContent")
+ .contentWindow.setSieveScript("",capabilities);
+
+ this.onStatusChange(0);
+ }
+ catch (ex) {
+ alert(ex);
+ }
+
+
+}
+
+SieveFilterListDialog.prototype.onGetScriptResponse
+ = function(response)
+{
+ try {
+
+ var capabilities = SieveConnections
+ .getChannel(this._sid,this._cid).extensions;
+
+ document.getElementById("sivContent")
+ .contentWindow.setSieveScript(response.getScriptBody(),capabilities);
+ }
+ catch (ex) {
+ alert(ex);
+ }
+
+ this.onStatusChange(0);
+}
+
+
+
+SieveFilterListDialog.prototype.onChannelClosed
+ = function()
+{
+ // a channel is usually closed when a child window is closed.
+ // it might be a good idea to check if the script was changed.
+}
+
+
+SieveFilterListDialog.prototype.onChannelReady
+ = function(cid)
+{
+ // We observe only our channel...
+ if (cid != this._cid)
+ return;
+
+ this.listScript();
+
+ // Step 1: List script
+
+ // get active if any
+}
+
+SieveFilterListDialog.prototype.onStatusChange
+ = function(state,message)
+{
+ // Script ready
+ if (state == 0)
+ {
+ document.getElementById("sivStatus").setAttribute('hidden','true');
+ document.getElementById('sivContent').removeAttribute('hidden');
+ return;
+ }
+
+ // The rest has to be redirected to the status window...
+ //document.getElementById('sivExplorerTree').setAttribute('collapsed','true');
+ document.getElementById("sivStatus").contentWindow.onStatus(state,message)
+ document.getElementById("sivStatus").removeAttribute('hidden');
+ document.getElementById('sivContent').setAttribute('hidden','true');
+}
+
+
+var gSFLD = new SieveFilterListDialog();
+
+
+/*function onCanChangeAccount(key)
+{
+ if (!gSFLD)
+ return true;
+
+ if (gSFLD._key != key)
+ return true;
+
+ return false;
+}*/
+
+function onLoad()
+{
+ var key = window.frameElement.getAttribute("key");
+
+ var account = SieveAccountManager.getAccountByName(key);
+
+ document.getElementById("sivStatus").contentWindow
+ .onAttach(account,function() { onLoad() });
+
+ // the content is heavy weight javascript. So load it lazily
+ var iframe = document.getElementById("sivContent")
+
+ if (iframe.hasAttribute("src"))
+ iframe.contentWindow.location.reload();
+ else
+ iframe.setAttribute("src","chrome://sieve/content/libs/libSieveDOM/SieveSimpleGui.html");
+
+ if ((!account.isEnabled()) || account.isFirstRun())
+ {
+ account.setFirstRun();
+ gSFLD.onStatusChange(8);
+
+ return;
+ }
+
+ gSFLD.onStatusChange(3,"progress.connecting");
+
+ gSFLD.connect(account);
+
+}
+
+window.onunload = function ()
+{
+ if (gSFLD)
+ gSFLD.disconnect();
+}
+
+
+
+/*
+ * StartUp...
+ *
+ * Step 1
+ * 0. Connect
+ * 1. Get Scripts
+ *
+ * 2. Script Active?
+ * 2a. No create a new Filter named "Thunderbird"
+ * 2b. Make Filter Active
+ *
+ * 3. Get Active Script
+ *
+ * 4. pass script to iframe
+ *
+ *
+ * On Change:
+ * 1. is script empty?
+ * 1a delete
+ * 1b stop;
+ *
+ * 2. put script
+ *
+ *
+ * On Close
+ * Disconnect...
+ *
+ */
+
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/filterList/SieveFilterList.xul b/src/sieve@mozdev.org/chrome/chromeFiles/content/filterList/SieveFilterList.xul
new file mode 100644
index 0000000..f93751e
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/filterList/SieveFilterList.xul
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+ <!--
+ The content of this file is licenced. You may obtain a copy of
+ the license at http://sieve.mozdev.org or request it via email
+ from the author. Do not remove or change this comment.
+
+ The initial author of the code is:
+ Thomas Schmid <schmid-thomas@gmx.net>
+-->
+
+<!DOCTYPE window SYSTEM "chrome://sieve/locale/locale.dtd">
+
+<?xml-stylesheet href="chrome://sieve/content/editor/Sieve.css" type="text/css"?>
+
+<!--
+ As other extension may override thunderbird's filter dialog, we we use this
+ warpper to isolate all sieve related code. As side effect we do not need to care
+ about namespace pollution etc...
+
+ An extra plus is the onload and onclose event,
+
+
+ all code into a separate compartment.
+ It's just a wrapper to isolate all sieve related function from thunderbird's
+ default filter dialog.
+-->
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onclose="return onClose();"
+ onload="return onLoad();">
+
+ <!-- <script type="application/javascript"
+ src="chrome://sieve/content/libs/libManageSieve/SieveAccounts.js"/>
+ <script type="application/javascript"
+ src="chrome://sieve/content/libs/libManageSieve/SieveRequest.js"/>
+ <script type="application/javascript"
+ src="chrome://sieve/content/libs/libManageSieve/SieveResponseCodes.js"/>-->
+
+ <script type="application/javascript"
+ src="chrome://sieve/content/libs/libManageSieve/SieveAbstractClient.js"/>
+
+ <script type="application/javascript"
+ src="chrome://sieve/content/filterList/SieveFilterList.js"/>
+
+ <iframe src="chrome://sieve/content/editor/SieveStatus.xul"
+ id="sivStatus" flex="1" />
+ <iframe id="sivContent" flex="1" hidden="true" />
+
+</window> \ No newline at end of file
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/active.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/active.png
new file mode 100644
index 0000000..ee507d9
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/active.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/active2.gif b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/active2.gif
new file mode 100644
index 0000000..6bdc3b5
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/active2.gif
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/btn_donate_LG.gif b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/btn_donate_LG.gif
new file mode 100644
index 0000000..43cef69
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/btn_donate_LG.gif
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/contributors.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/contributors.png
new file mode 100644
index 0000000..379e252
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/contributors.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/info.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/info.png
new file mode 100644
index 0000000..4393795
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/info.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/load.gif b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/load.gif
new file mode 100644
index 0000000..3c2f7c0
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/load.gif
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/passive.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/passive.png
new file mode 100644
index 0000000..82774ed
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/passive.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/sidebarTitle.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/sidebarTitle.png
new file mode 100644
index 0000000..d35073e
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/sidebarTitle.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/splitter.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/splitter.png
new file mode 100644
index 0000000..3b0cb01
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/splitter.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/sslWarning.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/sslWarning.png
new file mode 100644
index 0000000..0994698
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/sslWarning.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/syntax-error.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/syntax-error.png
new file mode 100644
index 0000000..cdd95ba
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/syntax-error.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/syntax-ok.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/syntax-ok.png
new file mode 100644
index 0000000..2ac5747
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/syntax-ok.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/syntax-warning.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/syntax-warning.png
new file mode 100644
index 0000000..91aea93
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/syntax-warning.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.back.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.back.png
new file mode 100644
index 0000000..bc24272
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.back.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.compile.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.compile.png
new file mode 100644
index 0000000..a05d00e
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.compile.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.content.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.content.png
new file mode 100644
index 0000000..cb6a2a8
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.content.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.copy.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.copy.png
new file mode 100644
index 0000000..51b4319
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.copy.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.cut.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.cut.png
new file mode 100644
index 0000000..e1132a1
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.cut.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.exit.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.exit.png
new file mode 100644
index 0000000..000eaf1
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.exit.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.forward.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.forward.png
new file mode 100644
index 0000000..ecc3fe5
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.forward.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.home.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.home.png
new file mode 100644
index 0000000..5949963
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.home.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.paste.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.paste.png
new file mode 100644
index 0000000..b569a56
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.paste.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.print.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.print.png
new file mode 100644
index 0000000..ab55542
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.print.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.redo.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.redo.png
new file mode 100644
index 0000000..5662caf
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.redo.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.reference.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.reference.png
new file mode 100644
index 0000000..490205b
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.reference.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.save.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.save.png
new file mode 100644
index 0000000..a942775
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.save.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.search.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.search.png
new file mode 100644
index 0000000..07de16f
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.search.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.source.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.source.png
new file mode 100644
index 0000000..d69c54c
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.source.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.tools.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.tools.png
new file mode 100644
index 0000000..16396fe
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.tools.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.undo.png b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.undo.png
new file mode 100644
index 0000000..1c73c46
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/images/toolbar.undo.png
Binary files differ
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/LICENSE b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/LICENSE
new file mode 100644
index 0000000..3916e96
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/LICENSE
@@ -0,0 +1,23 @@
+Copyright (C) 2012 by Marijn Haverbeke <marijnh@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+Please note that some subdirectories of the CodeMirror distribution
+include their own LICENSE files, and are released under different
+licences.
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/README.md b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/README.md
new file mode 100644
index 0000000..8ed9871
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/README.md
@@ -0,0 +1,8 @@
+# CodeMirror [![Build Status](https://secure.travis-ci.org/marijnh/CodeMirror.png?branch=master)](http://travis-ci.org/marijnh/CodeMirror)
+
+CodeMirror is a JavaScript component that provides a code editor in
+the browser. When a mode is available for the language you are coding
+in, it will color your code, and optionally help with indentation.
+
+The project page is http://codemirror.net
+The manual is at http://codemirror.net/doc/manual.html
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/lib/codemirror.css b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/lib/codemirror.css
new file mode 100644
index 0000000..9be4b00
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/lib/codemirror.css
@@ -0,0 +1,231 @@
+/* BASICS */
+
+.CodeMirror {
+ /* Set height, width, borders, and global font properties here */
+ font-family: monospace;
+ height: 300px;
+}
+.CodeMirror-scroll {
+ /* Set scrolling behaviour here */
+ overflow: auto;
+}
+
+/* PADDING */
+
+.CodeMirror-lines {
+ padding: 4px 0; /* Vertical padding around content */
+}
+.CodeMirror pre {
+ padding: 0 4px; /* Horizontal padding of content */
+}
+
+.CodeMirror-scrollbar-filler {
+ background-color: white; /* The little square between H and V scrollbars */
+}
+
+/* GUTTER */
+
+.CodeMirror-gutters {
+ border-right: 1px solid #ddd;
+ background-color: #f7f7f7;
+}
+.CodeMirror-linenumbers {}
+.CodeMirror-linenumber {
+ padding: 0 3px 0 5px;
+ min-width: 20px;
+ text-align: right;
+ color: #999;
+}
+
+/* CURSOR */
+
+.CodeMirror pre.CodeMirror-cursor {
+ border-left: 1px solid black;
+}
+/* Shown when moving in bi-directional text */
+.CodeMirror pre.CodeMirror-secondarycursor {
+ border-left: 1px solid silver;
+}
+.cm-keymap-fat-cursor pre.CodeMirror-cursor {
+ width: auto;
+ border: 0;
+ background: transparent;
+ background: rgba(0, 200, 0, .4);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800);
+}
+/* Kludge to turn off filter in ie9+, which also accepts rgba */
+.cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id) {
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+}
+/* Can style cursor different in overwrite (non-insert) mode */
+.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {}
+
+/* DEFAULT THEME */
+
+.cm-s-default .cm-keyword {color: #708;}
+.cm-s-default .cm-atom {color: #219;}
+.cm-s-default .cm-number {color: #164;}
+.cm-s-default .cm-def {color: #00f;}
+.cm-s-default .cm-variable {color: black;}
+.cm-s-default .cm-variable-2 {color: #05a;}
+.cm-s-default .cm-variable-3 {color: #085;}
+.cm-s-default .cm-property {color: black;}
+.cm-s-default .cm-operator {color: black;}
+.cm-s-default .cm-comment {color: #a50;}
+.cm-s-default .cm-string {color: #a11;}
+.cm-s-default .cm-string-2 {color: #f50;}
+.cm-s-default .cm-meta {color: #555;}
+.cm-s-default .cm-error {color: #f00;}
+.cm-s-default .cm-qualifier {color: #555;}
+.cm-s-default .cm-builtin {color: #30a;}
+.cm-s-default .cm-bracket {color: #997;}
+.cm-s-default .cm-tag {color: #170;}
+.cm-s-default .cm-attribute {color: #00c;}
+.cm-s-default .cm-header {color: blue;}
+.cm-s-default .cm-quote {color: #090;}
+.cm-s-default .cm-hr {color: #999;}
+.cm-s-default .cm-link {color: #00c;}
+
+.cm-header, .cm-strong {font-weight: bold;}
+.cm-em {font-style: italic;}
+.cm-emstrong {font-style: italic; font-weight: bold;}
+.cm-link {text-decoration: underline;}
+
+.cm-invalidchar {color: #f00;}
+
+div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
+div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
+
+/* STOP */
+
+/* The rest of this file contains styles related to the mechanics of
+ the editor. You probably shouldn't touch them. */
+
+.CodeMirror {
+ line-height: 1;
+ position: relative;
+ overflow: hidden;
+}
+
+.CodeMirror-scroll {
+ /* 30px is the magic margin used to hide the element's real scrollbars */
+ /* See overflow: hidden in .CodeMirror, and the paddings in .CodeMirror-sizer */
+ margin-bottom: -30px; margin-right: -30px;
+ padding-bottom: 30px; padding-right: 30px;
+ height: 100%;
+ outline: none; /* Prevent dragging from highlighting the element */
+ position: relative;
+}
+.CodeMirror-sizer {
+ position: relative;
+}
+
+/* The fake, visible scrollbars. Used to force redraw during scrolling
+ before actuall scrolling happens, thus preventing shaking and
+ flickering artifacts. */
+.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler {
+ position: absolute;
+ z-index: 6;
+ display: none;
+}
+.CodeMirror-vscrollbar {
+ right: 0; top: 0;
+ overflow-x: hidden;
+ overflow-y: scroll;
+}
+.CodeMirror-hscrollbar {
+ bottom: 0; left: 0;
+ overflow-y: hidden;
+ overflow-x: scroll;
+}
+.CodeMirror-scrollbar-filler {
+ right: 0; bottom: 0;
+ z-index: 6;
+}
+
+.CodeMirror-gutters {
+ position: absolute; left: 0; top: 0;
+ height: 100%;
+ z-index: 3;
+}
+.CodeMirror-gutter {
+ height: 100%;
+ float: left;
+}
+.CodeMirror-gutter-elt {
+ position: absolute;
+ cursor: default;
+ z-index: 4;
+}
+
+.CodeMirror-lines {
+ cursor: text;
+}
+.CodeMirror pre {
+ /* Reset some styles that the rest of the page might have set */
+ -moz-border-radius: 0; -webkit-border-radius: 0; -o-border-radius: 0; border-radius: 0;
+ border-width: 0;
+ background: transparent;
+ font-family: inherit;
+ font-size: inherit;
+ margin: 0;
+ white-space: pre;
+ word-wrap: normal;
+ line-height: inherit;
+ color: inherit;
+ z-index: 2;
+ position: relative;
+ overflow: visible;
+}
+.CodeMirror-wrap pre {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ word-break: normal;
+}
+.CodeMirror-linebackground {
+ position: absolute;
+ left: 0; right: 0; top: 0; bottom: 0;
+ z-index: 0;
+}
+
+.CodeMirror-linewidget {
+ position: relative;
+ z-index: 2;
+}
+
+.CodeMirror-wrap .CodeMirror-scroll {
+ overflow-x: hidden;
+}
+
+.CodeMirror-measure {
+ position: absolute;
+ width: 100%; height: 0px;
+ overflow: hidden;
+ visibility: hidden;
+}
+.CodeMirror-measure pre { position: static; }
+
+.CodeMirror pre.CodeMirror-cursor {
+ position: absolute;
+ visibility: hidden;
+ border-right: none;
+ width: 0;
+}
+.CodeMirror-focused pre.CodeMirror-cursor {
+ visibility: visible;
+}
+
+.CodeMirror-selected { background: #d9d9d9; }
+.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
+
+.CodeMirror-searching {
+ background: #ffa;
+ background: rgba(255, 255, 0, .4);
+}
+
+@media print {
+ /* Hide the cursor when printing */
+ .CodeMirror pre.CodeMirror-cursor {
+ visibility: hidden;
+ }
+}
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/lib/codemirror.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/lib/codemirror.js
new file mode 100644
index 0000000..a9c7412
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/lib/codemirror.js
@@ -0,0 +1,4124 @@
+// CodeMirror version 3.0beta2
+//
+// CodeMirror is the only global var we claim
+window.CodeMirror = (function() {
+ "use strict";
+
+ // BROWSER SNIFFING
+
+ // Crude, but necessary to handle a number of hard-to-feature-detect
+ // bugs and behavior differences.
+ var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
+ var ie = /MSIE \d/.test(navigator.userAgent);
+ var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent);
+ var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent);
+ var ie_lt10 = /MSIE [1-9]\b/.test(navigator.userAgent);
+ var webkit = /WebKit\//.test(navigator.userAgent);
+ var chrome = /Chrome\//.test(navigator.userAgent);
+ var opera = /Opera\//.test(navigator.userAgent);
+ var safari = /Apple Computer/.test(navigator.vendor);
+ var khtml = /KHTML\//.test(navigator.userAgent);
+ var mac_geLion = /Mac OS X 1\d\D([7-9]|\d\d)\D/.test(navigator.userAgent);
+ var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent);
+
+ var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
+ var mac = ios || /Mac/.test(navigator.platform);
+ var win = /Win/.test(navigator.platform);
+
+ // CONSTRUCTOR
+
+ function CodeMirror(place, options) {
+ if (!(this instanceof CodeMirror)) return new CodeMirror(place, options, true);
+
+ this.options = options = options || {};
+ // Determine effective options based on given values and defaults.
+ for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
+ options[opt] = defaults[opt];
+ setGuttersForLineNumbers(options);
+
+ var display = this.display = makeDisplay(place);
+ display.wrapper.CodeMirror = this;
+ updateGutters(this);
+ themeChanged(this);
+ keyMapChanged(this);
+ if (options.tabindex != null) display.input.tabIndex = options.tabindex;
+ if (options.autofocus) focusInput(this);
+ if (options.lineWrapping) display.wrapper.className += " CodeMirror-wrap";
+
+ var doc = new BranchChunk([new LeafChunk([makeLine("", null, textHeight(display))])]);
+ // The selection. These are always maintained to point at valid
+ // positions. Inverted is used to remember that the user is
+ // selecting bottom-to-top.
+ this.view = {
+ doc: doc,
+ // frontier is the point up to which the content has been parsed,
+ frontier: 0, highlight: new Delayed(),
+ sel: {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false, shift: false},
+ scrollTop: 0, scrollLeft: 0,
+ overwrite: false, focused: false,
+ // Tracks the maximum line length so that
+ // the horizontal scrollbar can be kept
+ // static when scrolling.
+ maxLine: getLine(doc, 0),
+ maxLineChanged: false,
+ suppressEdits: false,
+ goalColumn: null
+ };
+ loadMode(this);
+
+ // Initialize the content.
+ this.setValue(options.value || "");
+ // Override magic textarea content restore that IE sometimes does
+ // on our hidden textarea on reload
+ if (ie) setTimeout(bind(resetInput, this, true), 20);
+ this.view.history = makeHistory();
+
+ registerEventHandlers(this);
+ // IE throws unspecified error in certain cases, when
+ // trying to access activeElement before onload
+ var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { }
+ if (hasFocus || options.autofocus) setTimeout(bind(onFocus, this), 20);
+ else onBlur(this);
+
+ for (var opt in optionHandlers)
+ if (optionHandlers.propertyIsEnumerable(opt))
+ optionHandlers[opt](this, options[opt]);
+ for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
+ }
+
+ // DISPLAY CONSTRUCTOR
+
+ function makeDisplay(place) {
+ var d = {};
+ var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;");
+ input.setAttribute("wrap", "off"); input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
+ // Wraps and hides input textarea
+ d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
+ // The actual fake scrollbars.
+ d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
+ d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
+ d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
+ // DIVs containing the selection and the actual code
+ d.lineDiv = elt("div");
+ d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
+ // Blinky cursor, and element used to ensure cursor fits at the end of a line
+ d.cursor = elt("pre", "\u00a0", "CodeMirror-cursor");
+ // Secondary cursor, shown when on a 'jump' in bi-directional text
+ d.otherCursor = elt("pre", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
+ // Used to measure text size
+ d.measure = elt("div", null, "CodeMirror-measure");
+ // Wraps everything that needs to exist inside the vertically-padded coordinate system
+ d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor],
+ null, "position: relative; outline: none");
+ // Moved around its parent to cover visible view
+ d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
+ d.gutters = elt("div", null, "CodeMirror-gutters");
+ d.lineGutter = null;
+ // Set to the height of the text, causes scrolling
+ d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
+ // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
+ d.heightForcer = elt("div", "\u00a0", null, "position: absolute; height: " + scrollerCutOff + "px");
+ // Provides scrolling
+ d.scroller = elt("div", [d.sizer, d.heightForcer], "CodeMirror-scroll");
+ d.scroller.setAttribute("tabIndex", "-1");
+ // The element in which the editor lives.
+ d.wrapper = elt("div", [d.gutters, d.inputDiv, d.scrollbarH, d.scrollbarV,
+ d.scrollbarFiller, d.scroller], "CodeMirror");
+ // Work around IE7 z-index bug
+ if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
+ if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
+
+ // Needed to hide big blue blinking cursor on Mobile Safari
+ if (ios) input.style.width = "0px";
+ if (!webkit) d.scroller.draggable = true;
+ // Needed to handle Tab key in KHTML
+ if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; }
+ // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
+ else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px";
+
+ // Current visible range (may be bigger than the view window).
+ d.viewOffset = d.showingFrom = d.showingTo = d.lastSizeC = 0;
+
+ // Used to only resize the line number gutter when necessary (when
+ // the amount of lines crosses a boundary that makes its width change)
+ d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
+ // See readInput and resetInput
+ d.prevInput = "";
+ // Set to true when a non-horizontal-scrolling widget is added. As
+ // an optimization, widget aligning is skipped when d is false.
+ d.alignWidgets = false;
+ // Flag that indicates whether we currently expect input to appear
+ // (after some event like 'keypress' or 'input') and are polling
+ // intensively.
+ d.pollingFast = false;
+ // Self-resetting timeout for the poller
+ d.poll = new Delayed();
+ // True when a drag from the editor is active
+ d.draggingText = false;
+
+ d.cachedCharWidth = d.cachedTextHeight = null;
+ d.measureLineCache = [];
+ d.measureLineCache.pos = 0;
+
+ // Tracks when resetInput has punted to just putting a short
+ // string instead of the (large) selection.
+ d.inaccurateSelection = false;
+
+ // Used to adjust overwrite behaviour when a paste has been
+ // detected
+ d.pasteIncoming = false;
+
+ return d;
+ }
+
+ // STATE UPDATES
+
+ // Used to get the editor into a consistent state again when options change.
+
+ function loadMode(cm) {
+ var doc = cm.view.doc;
+ cm.view.mode = CodeMirror.getMode(cm.options, cm.options.mode);
+ doc.iter(0, doc.size, function(line) { line.stateAfter = null; });
+ cm.view.frontier = 0;
+ startWorker(cm, 100);
+ }
+
+ function wrappingChanged(cm) {
+ var doc = cm.view.doc;
+ if (cm.options.lineWrapping) {
+ cm.display.wrapper.className += " CodeMirror-wrap";
+ var perLine = cm.display.wrapper.clientWidth / charWidth(cm.display) - 3;
+ doc.iter(0, doc.size, function(line) {
+ if (line.hidden) return;
+ var guess = Math.ceil(line.text.length / perLine) || 1;
+ if (guess != 1) updateLineHeight(line, guess);
+ });
+ cm.display.sizer.style.minWidth = "";
+ } else {
+ cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
+ computeMaxLength(cm.view);
+ doc.iter(0, doc.size, function(line) {
+ if (line.height != 1 && !line.hidden) updateLineHeight(line, 1);
+ });
+ }
+ regChange(cm, 0, doc.size);
+ }
+
+ function keyMapChanged(cm) {
+ var style = keyMap[cm.options.keyMap].style;
+ cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
+ (style ? " cm-keymap-" + style : "");
+ }
+
+ function themeChanged(cm) {
+ cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
+ cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
+ }
+
+ function guttersChanged(cm) {
+ updateGutters(cm);
+ updateDisplay(cm, true);
+ }
+
+ function updateGutters(cm) {
+ var gutters = cm.display.gutters, specs = cm.options.gutters;
+ removeChildren(gutters);
+ for (var i = 0; i < specs.length; ++i) {
+ var gutterClass = specs[i];
+ var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
+ if (gutterClass == "CodeMirror-linenumbers") {
+ cm.display.lineGutter = gElt;
+ gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
+ }
+ }
+ gutters.style.display = i ? "" : "none";
+ }
+
+ function computeMaxLength(view) {
+ view.maxLine = getLine(view.doc, 0); view.maxLineChanged = true;
+ var maxLineLength = view.maxLine.text.length;
+ view.doc.iter(1, view.doc.size, function(line) {
+ var l = line.text;
+ if (!line.hidden && l.length > maxLineLength) {
+ maxLineLength = l.length; view.maxLine = line;
+ }
+ });
+ }
+
+ // Make sure the gutters options contains the element
+ // "CodeMirror-linenumbers" when the lineNumbers option is true.
+ function setGuttersForLineNumbers(options) {
+ var found = false;
+ for (var i = 0; i < options.gutters.length; ++i) {
+ if (options.gutters[i] == "CodeMirror-linenumbers") {
+ if (options.lineNumbers) found = true;
+ else options.gutters.splice(i--, 1);
+ }
+ }
+ if (!found && options.lineNumbers)
+ options.gutters.push("CodeMirror-linenumbers");
+ }
+
+ // SCROLLBARS
+
+ // Re-synchronize the fake scrollbars with the actual size of the
+ // content. Optionally force a scrollTop.
+ function updateScrollbars(d /* display */, docHeight, scrollTop) {
+ d.sizer.style.minHeight = d.heightForcer.style.top = (docHeight + 2 * paddingTop(d)) + "px";
+ var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
+ var needsV = d.scroller.scrollHeight > d.scroller.clientHeight;
+ if (needsV) {
+ d.scrollbarV.style.display = "block";
+ d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
+ d.scrollbarV.firstChild.style.height =
+ (d.scroller.scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
+ if (scrollTop != null) {
+ d.scrollbarV.scrollTop = d.scroller.scrollTop = scrollTop;
+ // 'Nudge' the scrollbar to work around a Webkit bug where,
+ // in some situations, we'd end up with a scrollbar that
+ // reported its scrollTop (and looked) as expected, but
+ // *behaved* as if it was still in a previous state (i.e.
+ // couldn't scroll up, even though it appeared to be at the
+ // bottom).
+ if (webkit) setTimeout(function() {
+ if (d.scrollbarV.scrollTop != scrollTop) return;
+ d.scrollbarV.scrollTop = scrollTop + (scrollTop ? -1 : 1);
+ d.scrollbarV.scrollTop = scrollTop;
+ }, 0);
+ }
+ } else d.scrollbarV.style.display = "";
+ if (needsH) {
+ d.scrollbarH.style.display = "block";
+ d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0";
+ d.scrollbarH.firstChild.style.width =
+ (d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px";
+ } else d.scrollbarH.style.display = "";
+ if (needsH && needsV) {
+ d.scrollbarFiller.style.display = "block";
+ d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
+ } else d.scrollbarFiller.style.display = "";
+
+ if (mac_geLion && scrollbarWidth(d.measure) === 0)
+ d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
+ }
+
+ function visibleLines(display, doc, scrollTop) {
+ var top = (scrollTop != null ? scrollTop : display.scroller.scrollTop) - paddingTop(display);
+ var fromHeight = Math.max(0, Math.floor(top));
+ var toHeight = Math.ceil(top + display.wrapper.clientHeight);
+ return {from: lineAtHeight(doc, fromHeight),
+ to: lineAtHeight(doc, toHeight)};
+ }
+
+ // LINE NUMBERS
+
+ function alignVertically(display) {
+ if (!display.alignWidgets && !display.gutters.firstChild) return;
+ var l = compensateForHScroll(display) + "px";
+ for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
+ for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
+ }
+ }
+
+ function maybeUpdateLineNumberWidth(cm) {
+ if (!cm.options.lineNumbers) return false;
+ var doc = cm.view.doc, last = lineNumberFor(cm.options, doc.size - 1), display = cm.display;
+ if (last.length != display.lineNumChars) {
+ var test = display.measure.appendChild(elt("div", [elt("div", last)],
+ "CodeMirror-linenumber CodeMirror-gutter-elt"));
+ var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
+ display.lineGutter.style.width = "";
+ display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding);
+ display.lineNumWidth = display.lineNumInnerWidth + padding;
+ display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
+ display.lineGutter.style.width = display.lineNumWidth + "px";
+ return true;
+ }
+ return false;
+ }
+
+ function lineNumberFor(options, i) {
+ return String(options.lineNumberFormatter(i + options.firstLineNumber));
+ }
+ function compensateForHScroll(display) {
+ return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
+ }
+
+ // DISPLAY DRAWING
+
+ function updateDisplay(cm, changes, scrollTop) {
+ var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo;
+ var updated = updateDisplayInner(cm, changes, scrollTop);
+ if (updated) {
+ signalLater(cm, cm, "update", cm);
+ if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
+ signalLater(cm, cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
+ }
+ updateSelection(cm);
+ updateScrollbars(cm.display, cm.view.doc.height, scrollTop);
+ return updated;
+ }
+
+ // Uses a set of changes plus the current scroll position to
+ // determine which DOM updates have to be made, and makes the
+ // updates.
+ function updateDisplayInner(cm, changes, scrollTop) {
+ var display = cm.display, doc = cm.view.doc;
+ if (!display.wrapper.clientWidth) {
+ display.showingFrom = display.showingTo = display.viewOffset = 0;
+ return;
+ }
+
+ // Compute the new visible window
+ // If scrollTop is specified, use that to determine which lines
+ // to render instead of the current scrollbar position.
+ var visible = visibleLines(display, doc, scrollTop);
+ // Bail out if the visible area is already rendered and nothing changed.
+ if (changes !== true && changes.length == 0 &&
+ visible.from > display.showingFrom && visible.to < display.showingTo)
+ return;
+
+ if (changes && maybeUpdateLineNumberWidth(cm))
+ changes = true;
+ display.sizer.style.marginLeft = display.scrollbarH.style.left = display.gutters.offsetWidth + "px";
+ // Used to determine which lines need their line numbers updated
+ var positionsChangedFrom = changes === true ? 0 : Infinity;
+ if (cm.options.lineNumbers && changes && changes !== true)
+ for (var i = 0; i < changes.length; ++i)
+ if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; }
+
+ var from = Math.max(visible.from - cm.options.viewportMargin, 0);
+ var to = Math.min(doc.size, visible.to + cm.options.viewportMargin);
+ if (display.showingFrom < from && from - display.showingFrom < 20) from = display.showingFrom;
+ if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(doc.size, display.showingTo);
+
+ // Create a range of theoretically intact lines, and punch holes
+ // in that using the change info.
+ var intact = changes === true ? [] :
+ computeIntact([{from: display.showingFrom, to: display.showingTo, domStart: 0}], changes);
+ // Clip off the parts that won't be visible
+ var intactLines = 0;
+ for (var i = 0; i < intact.length; ++i) {
+ var range = intact[i];
+ if (range.from < from) {range.domStart += (from - range.from); range.from = from;}
+ if (range.to > to) range.to = to;
+ if (range.from >= range.to) intact.splice(i--, 1);
+ else intactLines += range.to - range.from;
+ }
+ if (intactLines == to - from && from == display.showingFrom && to == display.showingTo)
+ return;
+ intact.sort(function(a, b) {return a.domStart - b.domStart;});
+
+ if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
+ patchDisplay(cm, from, to, intact, positionsChangedFrom);
+ display.lineDiv.style.display = "";
+
+ var different = from != display.showingFrom || to != display.showingTo ||
+ display.lastSizeC != display.wrapper.clientHeight;
+ // This is just a bogus formula that detects when the editor is
+ // resized or the font size changes.
+ if (different) display.lastSizeC = display.wrapper.clientHeight;
+ display.showingFrom = from; display.showingTo = to;
+ display.viewOffset = heightAtLine(doc, from);
+ startWorker(cm, 100);
+
+ // Since this is all rather error prone, it is honoured with the
+ // only assertion in the whole file.
+ if (display.lineDiv.childNodes.length != display.showingTo - display.showingFrom)
+ throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (display.showingTo - display.showingFrom) +
+ " nodes=" + display.lineDiv.childNodes.length);
+
+ // Update line heights for visible lines based on actual DOM
+ // sizes
+ var curNode = display.lineDiv.firstChild, relativeTo = curNode.offsetTop;
+ doc.iter(display.showingFrom, display.showingTo, function(line) {
+ // Work around bizarro IE7 bug where, sometimes, our curNode
+ // is magically replaced with a new node in the DOM, leaving
+ // us with a reference to an orphan (nextSibling-less) node.
+ if (!curNode) return;
+ if (!line.hidden || (line.hidden.handle.showWidgets && line.widgets)) {
+ var end = curNode.offsetHeight + curNode.offsetTop;
+ var height = end - relativeTo, diff = line.height - height;
+ if (height < 2) height = textHeight(display);
+ relativeTo = end;
+ if (diff > .001 || diff < -.001)
+ updateLineHeight(line, height);
+ }
+ curNode = curNode.nextSibling;
+ });
+
+ // Position the mover div to align with the current virtual scroll position
+ display.mover.style.top = display.viewOffset + "px";
+ return true;
+ }
+
+ function computeIntact(intact, changes) {
+ for (var i = 0, l = changes.length || 0; i < l; ++i) {
+ var change = changes[i], intact2 = [], diff = change.diff || 0;
+ for (var j = 0, l2 = intact.length; j < l2; ++j) {
+ var range = intact[j];
+ if (change.to <= range.from && change.diff)
+ intact2.push({from: range.from + diff, to: range.to + diff,
+ domStart: range.domStart});
+ else if (change.to <= range.from || change.from >= range.to)
+ intact2.push(range);
+ else {
+ if (change.from > range.from)
+ intact2.push({from: range.from, to: change.from, domStart: range.domStart});
+ if (change.to < range.to)
+ intact2.push({from: change.to + diff, to: range.to + diff,
+ domStart: range.domStart + (change.to - range.from)});
+ }
+ }
+ intact = intact2;
+ }
+ return intact;
+ }
+
+ function getDimensions(cm) {
+ var d = cm.display, left = {}, width = {};
+ for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
+ left[cm.options.gutters[i]] = n.offsetLeft;
+ width[cm.options.gutters[i]] = n.offsetWidth;
+ }
+ return {fixedPos: compensateForHScroll(d),
+ gutterTotalWidth: d.gutters.offsetWidth,
+ gutterLeft: left,
+ gutterWidth: width,
+ wrapperWidth: d.wrapper.clientWidth};
+ }
+
+ function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
+ function killNode(node) {
+ var tmp = node.nextSibling;
+ node.parentNode.removeChild(node);
+ return tmp;
+ }
+ var dims = getDimensions(cm);
+ var display = cm.display, lineNumbers = cm.options.lineNumbers;
+ // The first pass removes the DOM nodes that aren't intact.
+ if (!intact.length) {
+ // old IE does bad things to nodes when .innerHTML = "" is used on a parent
+ // we still need widgets and markers intact to add back to the new content later
+ if (ie_lt10) for (var ld = display.lineDiv, tmp = ld.firstChild; tmp; tmp = ld.firstChild) ld.removeChild(tmp);
+ else removeChildren(display.lineDiv);
+ } else {
+ var domPos = 0, curNode = display.lineDiv.firstChild, n;
+ for (var i = 0; i < intact.length; ++i) {
+ var cur = intact[i];
+ while (cur.domStart > domPos) {curNode = killNode(curNode); domPos++;}
+ for (var j = cur.from, e = cur.to; j < e; ++j) {
+ if (lineNumbers && updateNumbersFrom <= j && curNode.firstChild)
+ setTextContent(curNode.firstChild.firstChild, lineNumberFor(cm.options, j));
+ curNode = curNode.nextSibling; domPos++;
+ }
+ }
+ while (curNode) curNode = killNode(curNode);
+ }
+ // This pass fills in the lines that actually changed.
+ var nextIntact = intact.shift(), curNode = display.lineDiv.firstChild;
+ var j = from;
+
+ cm.view.doc.iter(from, to, function(line) {
+ if (nextIntact && nextIntact.to == j) nextIntact = intact.shift();
+ if (!nextIntact || nextIntact.from > j)
+ display.lineDiv.insertBefore(buildLineElement(cm, line, j, dims), curNode);
+ else
+ curNode = curNode.nextSibling;
+ ++j;
+ });
+ }
+
+ function buildLineElement(cm, line, lineNo, dims) {
+ if (line.hidden && (!line.hidden.handle.showWidgets || !line.widgets))
+ return elt("div");
+
+ var lineElement = line.hidden ? elt("div") : lineContent(cm, line);
+ var markers = line.gutterMarkers, display = cm.display;
+
+ if (!cm.options.lineNumbers && !markers && !line.bgClassName &&
+ (!line.widgets || !line.widgets.length)) return lineElement;
+
+ // Lines with gutter elements or a background class need
+ // to be wrapped again, and have the extra elements added
+ // to the wrapper div
+
+ var wrap = elt("div", null, null, "position: relative");
+ if (cm.options.lineNumbers || markers) {
+ var gutterWrap = wrap.appendChild(elt("div", null, null, "position: absolute; left: " +
+ dims.fixedPos + "px"));
+ wrap.alignable = [gutterWrap];
+ if (cm.options.lineNumbers)
+ gutterWrap.appendChild(elt("div", lineNumberFor(cm.options, lineNo),
+ "CodeMirror-linenumber CodeMirror-gutter-elt",
+ "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
+ + display.lineNumInnerWidth + "px"));
+ if (markers)
+ for (var k = 0; k < cm.options.gutters.length; ++k) {
+ var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
+ if (found) {
+ gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
+ dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
+ }
+ }
+ }
+ // Kludge to make sure the styled element lies behind the selection (by z-index)
+ if (line.bgClassName)
+ wrap.appendChild(elt("div", "\u00a0", line.bgClassName + " CodeMirror-linebackground"));
+ wrap.appendChild(lineElement);
+ if (line.widgets)
+ for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
+ var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
+ node.widget = widget;
+ if (widget.noHScroll) {
+ (wrap.alignable || (wrap.alignable = [])).push(node);
+ var width = dims.wrapperWidth;
+ node.style.left = dims.fixedPos + "px";
+ if (!widget.coverGutter) {
+ width -= dims.gutterTotalWidth;
+ node.style.paddingLeft = dims.gutterTotalWidth + "px";
+ }
+ node.style.width = width + "px";
+ }
+ if (widget.coverGutter) {
+ node.style.zIndex = 5;
+ node.style.position = "relative";
+ if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
+ }
+ if (widget.above)
+ wrap.insertBefore(node, cm.options.lineNumbers && !line.hidden ? gutterWrap : lineElement);
+ else
+ wrap.appendChild(node);
+ }
+
+ if (ie_lt8) wrap.style.zIndex = 2;
+ return wrap;
+ }
+
+ // SELECTION / CURSOR
+
+ function selHead(view) {
+ return view.sel.inverted ? view.sel.from : view.sel.to;
+ }
+
+ function updateSelection(cm) {
+ var headPos, display = cm.display, doc = cm.view.doc, sel = cm.view.sel;
+ if (posEq(sel.from, sel.to)) { // No selection, single cursor
+ var pos = headPos = cursorCoords(cm, sel.from, "div");
+ display.cursor.style.left = pos.left + "px";
+ display.cursor.style.top = pos.top + "px";
+ display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
+ display.cursor.style.display = "";
+ display.selectionDiv.style.display = "none";
+
+ if (pos.other) {
+ display.otherCursor.style.display = "";
+ display.otherCursor.style.left = pos.other.left + "px";
+ display.otherCursor.style.top = pos.other.top + "px";
+ display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
+ } else { display.otherCursor.style.display = "none"; }
+ } else {
+ headPos = cursorCoords(cm, selHead(cm.view), "div");
+ var fragment = document.createDocumentFragment();
+ var clientWidth = display.lineSpace.offsetWidth;
+ var add = function(left, top, width, bottom) {
+ if (top < 0) top = 0;
+ fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
+ "px; top: " + top + "px; width: " + (width == null ? clientWidth : width) +
+ "px; height: " + (bottom - top) + "px"));
+ };
+
+ var middleFrom = sel.from.line + 1, middleTo = sel.to.line - 1, sameLine = sel.from.line == sel.to.line;
+ var drawForLine = function(line, from, toArg, retTop) {
+ var lineObj = getLine(doc, line), lineLen = lineObj.text.length;
+ var coords = function(ch) { return charCoords(cm, {line: line, ch: ch}, "div", lineObj); };
+ var rVal = retTop ? Infinity : -Infinity;
+ iterateBidiSections(getOrder(lineObj), from, toArg == null ? lineLen : toArg, function(from, to, dir) {
+ var leftPos = coords(dir == "rtl" ? to - 1 : from);
+ var rightPos = coords(dir == "rtl" ? from : to - 1);
+ var left = leftPos.left, right = rightPos.right;
+ if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
+ add(left, leftPos.top, null, leftPos.bottom);
+ left = paddingLeft(display);
+ if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
+ }
+ if (toArg == null && to == lineLen) right = clientWidth;
+ rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal);
+ add(left, rightPos.top, right - left, rightPos.bottom);
+ });
+ return rVal;
+ };
+
+ var middleTop = Infinity, middleBot = -Infinity;
+ if (sel.from.ch || sameLine)
+ // Draw the first line of selection.
+ middleTop = drawForLine(sel.from.line, sel.from.ch, sameLine ? sel.to.ch : null);
+ else
+ // Simply include it in the middle block.
+ middleFrom = sel.from.line;
+
+ if (!sameLine && sel.to.ch)
+ middleBot = drawForLine(sel.to.line, 0, sel.to.ch, true);
+
+ if (middleFrom <= middleTo) {
+ // Draw the middle
+ var botLine = getLine(doc, middleTo),
+ bottom = charCoords(cm, {line: middleTo, ch: botLine.text.length}, "div", botLine);
+ // Kludge to try and prevent fetching coordinates twice if
+ // start end end are on same line.
+ var top = (middleFrom != middleTo || botLine.height > bottom.bottom - bottom.top) ?
+ charCoords(cm, {line: middleFrom, ch: 0}, "div") : bottom;
+ middleTop = Math.min(middleTop, top.top);
+ middleBot = Math.max(middleBot, bottom.bottom);
+ }
+ if (middleTop < middleBot) add(paddingLeft(display), middleTop, null, middleBot);
+
+ removeChildrenAndAdd(display.selectionDiv, fragment);
+ display.cursor.style.display = display.otherCursor.style.display = "none";
+ display.selectionDiv.style.display = "";
+ }
+
+ // Move the hidden textarea near the cursor to prevent scrolling artifacts
+ var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
+ display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
+ headPos.top + lineOff.top - wrapOff.top)) + "px";
+ display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
+ headPos.left + lineOff.left - wrapOff.left)) + "px";
+ }
+
+ // Cursor-blinking
+ function restartBlink(cm) {
+ var display = cm.display;
+ clearInterval(display.blinker);
+ var on = true;
+ display.cursor.style.visibility = display.otherCursor.style.visibility = "";
+ display.blinker = setInterval(function() {
+ display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
+ }, cm.options.cursorBlinkRate);
+ }
+
+ // HIGHLIGHT WORKER
+
+ function startWorker(cm, time) {
+ if (cm.view.frontier < cm.display.showingTo)
+ cm.view.highlight.set(time, bind(highlightWorker, cm));
+ }
+
+ function highlightWorker(cm) {
+ var view = cm.view, doc = view.doc;
+ if (view.frontier >= cm.display.showingTo) return;
+ var end = +new Date + cm.options.workTime;
+ var state = copyState(view.mode, getStateBefore(cm, view.frontier));
+ var changed = [], prevChange;
+ doc.iter(view.frontier, Math.min(doc.size, cm.display.showingTo + 500), function(line) {
+ if (view.frontier >= cm.display.showingFrom) { // Visible
+ if (highlightLine(cm, line, state) && view.frontier >= cm.display.showingFrom) {
+ if (prevChange && prevChange.end == view.frontier) prevChange.end++;
+ else changed.push(prevChange = {start: view.frontier, end: view.frontier + 1});
+ }
+ line.stateAfter = copyState(view.mode, state);
+ } else {
+ processLine(cm, line, state);
+ line.stateAfter = view.frontier % 5 == 0 ? copyState(view.mode, state) : null;
+ }
+ ++view.frontier;
+ if (+new Date > end) {
+ startWorker(cm, cm.options.workDelay);
+ return true;
+ }
+ });
+ if (changed.length)
+ operation(cm, function() {
+ for (var i = 0; i < changed.length; ++i)
+ regChange(this, changed[i].start, changed[i].end);
+ })();
+ }
+
+ // Finds the line to start with when starting a parse. Tries to
+ // find a line with a stateAfter, so that it can start with a
+ // valid state. If that fails, it returns the line with the
+ // smallest indentation, which tends to need the least context to
+ // parse correctly.
+ function findStartLine(cm, n) {
+ var minindent, minline, doc = cm.view.doc;
+ for (var search = n, lim = n - 100; search > lim; --search) {
+ if (search == 0) return 0;
+ var line = getLine(doc, search-1);
+ if (line.stateAfter) return search;
+ var indented = countColumn(line.text, null, cm.options.tabSize);
+ if (minline == null || minindent > indented) {
+ minline = search - 1;
+ minindent = indented;
+ }
+ }
+ return minline;
+ }
+
+ function getStateBefore(cm, n) {
+ var view = cm.view;
+ var pos = findStartLine(cm, n), state = pos && getLine(view.doc, pos-1).stateAfter;
+ if (!state) state = startState(view.mode);
+ else state = copyState(view.mode, state);
+ view.doc.iter(pos, n, function(line) {
+ processLine(cm, line, state);
+ line.stateAfter = (pos == n - 1 || pos % 5 == 0) ? copyState(view.mode, state) : null;
+ });
+ return state;
+ }
+
+ // POSITION MEASUREMENT
+
+ function paddingTop(display) {return display.lineSpace.offsetTop;}
+ function paddingLeft(display) {
+ var e = removeChildrenAndAdd(display.measure, elt("pre")).appendChild(elt("span", "x"));
+ return e.offsetLeft;
+ }
+
+ function measureLine(cm, line, ch) {
+ var wrapping = cm.options.lineWrapping, display = cm.display;
+ // First look in the cache
+ var cache = cm.display.measureLineCache;
+ for (var i = 0; i < cache.length; ++i) {
+ var memo = cache[i];
+ if (memo.ch == ch && memo.text == line.text &&
+ (!wrapping || display.scroller.clientWidth == memo.width))
+ return {top: memo.top, bottom: memo.bottom, left: memo.left, right: memo.right};
+ }
+
+ var atEnd = ch && ch == line.text.length;
+ var pre = lineContent(cm, line, atEnd ? ch - 1 : ch);
+ removeChildrenAndAdd(display.measure, pre);
+ var anchor = pre.anchor;
+ // We'll sample once at the top, once at the bottom of the line,
+ // to get the real line height (in case there tokens on the line
+ // with bigger fonts)
+ anchor.style.verticalAlign = "top";
+ var outer = display.lineDiv.getBoundingClientRect(), box1 = anchor.getBoundingClientRect();
+ var left = box1.left - outer.left, right = box1.right - outer.left;
+ if (ie) {
+ var left1 = anchor.offsetLeft;
+ // In IE, verticalAlign does not influence offsetTop, unless
+ // the element is an inline-block. Unfortunately, inline
+ // blocks have different wrapping behaviour, so we have to do
+ // some icky thing with inserting "Zero-Width No-Break Spaces"
+ // to compensate for wrapping artifacts.
+ anchor.style.display = "inline-block";
+ if (wrapping && anchor.offsetLeft != left1) {
+ anchor.parentNode.insertBefore(document.createTextNode("\ufeff"), anchor);
+ if (anchor.offsetLeft != left1)
+ anchor.parentNode.insertBefore(document.createTextNode("\ufeff"), anchor.nextSibling);
+ if (anchor.offsetLeft != left1)
+ anchor.parentNode.removeChild(anchor.previousSibling);
+ }
+ }
+ var top = Math.max(0, box1.top - outer.top);
+ anchor.style.verticalAlign = "bottom";
+ var bottom = Math.min(anchor.getBoundingClientRect().bottom - outer.top, pre.offsetHeight);
+ if (atEnd) left = right;
+
+ // Store result in the cache
+ var memo = {ch: ch, text: line.text, width: display.wrapper.clientWidth,
+ top: top, bottom: bottom, left: left, right: right};
+ if (cache.length == 8) cache[++cache.pos % 8] = memo;
+ else cache.push(memo);
+
+ return {top: top, bottom: bottom, left: left, right: right};
+ }
+
+ // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
+ function intoCoordSystem(cm, lineObj, pos, rect, context) {
+ if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
+ var size = lineObj.widgets[i].node.offsetHeight;
+ rect.top += size; rect.bottom += size;
+ }
+ if (context == "line") return rect;
+ if (!context) context = "local";
+ var yOff = heightAtLine(cm.view.doc, pos.line);
+ if (context != "local") yOff -= cm.display.viewOffset;
+ if (context == "page") {
+ var lOff = cm.display.lineSpace.getBoundingClientRect();
+ yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
+ var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
+ rect.left += xOff; rect.right += xOff;
+ }
+ rect.top += yOff; rect.bottom += yOff;
+ return rect;
+ }
+
+ function charCoords(cm, pos, context, lineObj) {
+ if (!lineObj) lineObj = getLine(cm.view.doc, pos.line);
+ return intoCoordSystem(cm, lineObj, pos, measureLine(cm, lineObj, pos.ch), context);
+ }
+
+ function cursorCoords(cm, pos, context, lineObj) {
+ lineObj = lineObj || getLine(cm.view.doc, pos.line);
+ function get(ch, right) {
+ var m = measureLine(cm, lineObj, ch);
+ if (right) m.left = m.right; else m.right = m.left;
+ return intoCoordSystem(cm, lineObj, pos, m, context);
+ }
+ var order = getOrder(lineObj), ch = pos.ch;
+ if (!order) return get(ch);
+ var main, other, linedir = order[0].level;
+ for (var i = 0; i < order.length; ++i) {
+ var part = order[i], rtl = part.level % 2, nb, here;
+ if (part.from < ch && part.to > ch) return get(ch, rtl);
+ var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to;
+ if (left == ch) {
+ // Opera and IE return bogus offsets and widths for edges
+ // where the direction flips, but only for the side with the
+ // lower level. So we try to use the side with the higher
+ // level.
+ if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true);
+ else here = get(rtl && part.from != part.to ? ch - 1 : ch);
+ if (rtl == linedir) main = here; else other = here;
+ } else if (right == ch) {
+ var nb = i < order.length - 1 && order[i+1];
+ if (!rtl && nb && nb.from == nb.to) continue;
+ if (nb && part.level < nb.level) here = get(nb.level % 2 ? nb.to - 1 : nb.from);
+ else here = get(rtl ? ch : ch - 1, true);
+ if (rtl == linedir) main = here; else other = here;
+ }
+ }
+ if (linedir && !ch) other = get(order[0].to - 1);
+ if (!main) return other;
+ if (other) main.other = other;
+ return main;
+ }
+
+ // Coords must be lineSpace-local
+ function coordsChar(cm, x, y) {
+ var display = cm.display, doc = cm.view.doc;
+ var cw = charWidth(display), heightPos = display.viewOffset + y;
+ if (heightPos < 0) return {line: 0, ch: 0, outside: true};
+ var lineNo = lineAtHeight(doc, heightPos);
+ if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc, doc.size - 1).text.length};
+ var lineObj = getLine(doc, lineNo);
+ if (!lineObj.text.length) return {line: lineNo, ch: 0};
+ var innerOff = heightPos - heightAtLine(doc, lineNo);
+ if (x < 0) x = 0;
+ var wrongLine = false, cWidth = display.wrapper.clientWidth;
+ function getX(ch) {
+ var sp = cursorCoords(cm, {line: lineNo, ch: ch}, "line", lineObj);
+ wrongLine = true;
+ if (innerOff > sp.bottom) return Math.max(0, sp.left - cWidth);
+ else if (innerOff < sp.top) return sp.left + cWidth;
+ else wrongLine = false;
+ return sp.left;
+ }
+ var bidi = getOrder(lineObj), dist = lineObj.text.length;
+ var from = lineLeft(lineObj), fromX = 0, to = lineRight(lineObj), toX;
+ if (!bidi) {
+ // Guess a suitable upper bound for our search.
+ var estimated = Math.min(to, Math.ceil((x + Math.floor(innerOff / textHeight(display)) *
+ display.wrapper.clientWidth * .9) / cw));
+ for (;;) {
+ var estX = getX(estimated);
+ if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
+ else {toX = estX; to = estimated; break;}
+ }
+ // Try to guess a suitable lower bound as well.
+ estimated = Math.floor(to * 0.8); estX = getX(estimated);
+ if (estX < x) {from = estimated; fromX = estX;}
+ dist = to - from;
+ } else toX = getX(to);
+ if (x > toX) return {line: lineNo, ch: to, outside: wrongLine};
+ // Do a binary search between these bounds.
+ for (;;) {
+ if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
+ var after = x - fromX < toX - x, ch = after ? from : to;
+ while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
+ return {line: lineNo, ch: ch, after: after, outside: wrongLine};
+ }
+ var step = Math.ceil(dist / 2), middle = from + step;
+ if (bidi) {
+ middle = from;
+ for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
+ }
+ var middleX = getX(middle);
+ if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; dist -= step;}
+ else {from = middle; fromX = middleX; dist = step;}
+ }
+ }
+
+ var measureText;
+ function textHeight(display) {
+ if (display.cachedTextHeight != null) return display.cachedTextHeight;
+ if (measureText == null) {
+ measureText = elt("pre");
+ // Measure a bunch of lines, for browsers that compute
+ // fractional heights.
+ for (var i = 0; i < 49; ++i) {
+ measureText.appendChild(document.createTextNode("x"));
+ measureText.appendChild(elt("br"));
+ }
+ measureText.appendChild(document.createTextNode("x"));
+ }
+ removeChildrenAndAdd(display.measure, measureText);
+ var height = measureText.offsetHeight / 50;
+ if (height > 3) display.cachedTextHeight = height;
+ removeChildren(display.measure);
+ return height || 1;
+ }
+
+ function charWidth(display) {
+ if (display.cachedCharWidth != null) return display.cachedCharWidth;
+ var anchor = elt("span", "x");
+ var pre = elt("pre", [anchor]);
+ removeChildrenAndAdd(display.measure, pre);
+ var width = anchor.offsetWidth;
+ if (width > 2) display.cachedCharWidth = width;
+ return width || 10;
+ }
+
+ // OPERATIONS
+
+ // Operations are used to wrap changes in such a way that each
+ // change won't have to update the cursor and display (which would
+ // be awkward, slow, and error-prone), but instead updates are
+ // batched and then all combined and executed at once.
+
+ function startOperation(cm) {
+ if (cm.curOp) ++cm.curOp.depth;
+ else cm.curOp = {
+ // Nested operations delay update until the outermost one
+ // finishes.
+ depth: 1,
+ // An array of ranges of lines that have to be updated. See
+ // updateDisplay.
+ changes: [],
+ delayedCallbacks: [],
+ updateInput: null,
+ userSelChange: null,
+ textChanged: null,
+ selectionChanged: false,
+ updateMaxLine: false
+ };
+ }
+
+ function endOperation(cm) {
+ var op = cm.curOp;
+ if (--op.depth) return;
+ cm.curOp = null;
+ var view = cm.view, display = cm.display;
+ if (op.updateMaxLine) computeMaxLength(view);
+ if (view.maxLineChanged && !cm.options.lineWrapping) {
+ var width = measureLine(cm, view.maxLine, view.maxLine.text.length).left;
+ display.sizer.style.minWidth = (width + 3 + scrollerCutOff) + "px";
+ view.maxLineChanged = false;
+ }
+ var newScrollPos, updated;
+ if (op.selectionChanged) {
+ var coords = cursorCoords(cm, selHead(view));
+ newScrollPos = calculateScrollPos(display, coords.left, coords.top, coords.left, coords.bottom);
+ }
+ if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null)
+ updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop);
+ if (!updated && op.selectionChanged) updateSelection(cm);
+ if (newScrollPos) scrollCursorIntoView(cm);
+ if (op.selectionChanged) restartBlink(cm);
+
+ if (view.focused && op.updateInput)
+ resetInput(cm, op.userSelChange);
+
+ if (op.textChanged)
+ signal(cm, "change", cm, op.textChanged);
+ if (op.selectionChanged) signal(cm, "cursorActivity", cm);
+ for (var i = 0; i < op.delayedCallbacks.length; ++i) op.delayedCallbacks[i](cm);
+ }
+
+ // Wraps a function in an operation. Returns the wrapped function.
+ function operation(cm1, f) {
+ return function() {
+ var cm = cm1 || this;
+ startOperation(cm);
+ try {var result = f.apply(cm, arguments);}
+ finally {endOperation(cm);}
+ return result;
+ };
+ }
+
+ function regChange(cm, from, to, lendiff) {
+ cm.curOp.changes.push({from: from, to: to, diff: lendiff});
+ }
+
+ // INPUT HANDLING
+
+ function slowPoll(cm) {
+ if (cm.view.pollingFast) return;
+ cm.display.poll.set(cm.options.pollInterval, function() {
+ readInput(cm);
+ if (cm.view.focused) slowPoll(cm);
+ });
+ }
+
+ function fastPoll(cm) {
+ var missed = false;
+ cm.display.pollingFast = true;
+ function p() {
+ var changed = readInput(cm);
+ if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);}
+ else {cm.display.pollingFast = false; slowPoll(cm);}
+ }
+ cm.display.poll.set(20, p);
+ }
+
+ // prevInput is a hack to work with IME. If we reset the textarea
+ // on every change, that breaks IME. So we look for changes
+ // compared to the previous content instead. (Modern browsers have
+ // events that indicate IME taking place, but these are not widely
+ // supported or compatible enough yet to rely on.)
+ function readInput(cm) {
+ var input = cm.display.input, prevInput = cm.display.prevInput, view = cm.view, sel = view.sel;
+ if (!view.focused || hasSelection(input) || cm.options.readOnly) return false;
+ var text = input.value;
+ if (text == prevInput && posEq(sel.from, sel.to)) return false;
+ startOperation(cm);
+ view.sel.shift = null;
+ var same = 0, l = Math.min(prevInput.length, text.length);
+ while (same < l && prevInput[same] == text[same]) ++same;
+ if (same < prevInput.length)
+ sel.from = {line: sel.from.line, ch: sel.from.ch - (prevInput.length - same)};
+ else if (view.overwrite && posEq(sel.from, sel.to) && !cm.display.pasteIncoming)
+ sel.to = {line: sel.to.line, ch: Math.min(getLine(cm.view.doc, sel.to.line).text.length, sel.to.ch + (text.length - same))};
+ var updateInput = cm.curOp.updateInput;
+ cm.replaceSelection(text.slice(same), "end");
+ cm.curOp.updateInput = updateInput;
+ if (text.length > 1000) { input.value = cm.display.prevInput = ""; }
+ else cm.display.prevInput = text;
+ endOperation(cm);
+ cm.display.pasteIncoming = false;
+ return true;
+ }
+
+ function resetInput(cm, user) {
+ var view = cm.view, minimal, selected;
+ if (!posEq(view.sel.from, view.sel.to)) {
+ cm.display.prevInput = "";
+ minimal = hasCopyEvent &&
+ (view.sel.to.line - view.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
+ if (minimal) cm.display.input.value = "-";
+ else cm.display.input.value = selected || cm.getSelection();
+ if (view.focused) selectInput(cm.display.input);
+ } else if (user) cm.display.prevInput = cm.display.input.value = "";
+ cm.display.inaccurateSelection = minimal;
+ }
+
+ function focusInput(cm) {
+ if (cm.options.readOnly != "nocursor") cm.display.input.focus();
+ }
+
+ // EVENT HANDLERS
+
+ function registerEventHandlers(cm) {
+ var d = cm.display;
+ on(d.scroller, "mousedown", operation(cm, onMouseDown));
+ on(d.gutters, "mousedown", operation(cm, clickInGutter));
+ on(d.scroller, "dblclick", operation(cm, e_preventDefault));
+ on(d.lineSpace, "selectstart", function(e) {
+ if (!mouseEventInWidget(d, e)) e_preventDefault(e);
+ });
+ // Gecko browsers fire contextmenu *after* opening the menu, at
+ // which point we can't mess with it anymore. Context menu is
+ // handled in onMouseDown for Gecko.
+ if (!gecko) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
+
+ on(d.scroller, "scroll", function() {
+ setScrollTop(cm, d.scroller.scrollTop);
+ setScrollLeft(cm, d.scroller.scrollLeft);
+ signal(cm, "scroll", cm);
+ });
+ on(d.scrollbarV, "scroll", function() {
+ setScrollTop(cm, d.scrollbarV.scrollTop);
+ });
+ on(d.scrollbarH, "scroll", function() {
+ setScrollLeft(cm, d.scrollbarH.scrollLeft);
+ });
+
+ on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
+ on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
+
+ function reFocus() { if (cm.view.focused) setTimeout(bind(focusInput, cm), 0); }
+ on(d.scrollbarH, "mousedown", reFocus);
+ on(d.scrollbarV, "mousedown", reFocus);
+ // Prevent wrapper from ever scrolling
+ on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
+ on(window, "resize", function resizeHandler() {
+ // Might be a text scaling operation, clear size caches.
+ d.cachedCharWidth = d.cachedTextHeight = null;
+ d.measureLineCache.length = d.measureLineCache.pos = 0;
+ if (d.wrapper.parentNode) updateDisplay(cm, true);
+ else off(window, "resize", resizeHandler);
+ });
+
+ on(d.input, "keyup", operation(cm, function(e) {
+ if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
+ if (e_prop(e, "keyCode") == 16) cm.view.sel.shift = null;
+ }));
+ on(d.input, "input", bind(fastPoll, cm));
+ on(d.input, "keydown", operation(cm, onKeyDown));
+ on(d.input, "keypress", operation(cm, onKeyPress));
+ on(d.input, "focus", bind(onFocus, cm));
+ on(d.input, "blur", bind(onBlur, cm));
+
+ function drag_(e) {
+ if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
+ e_stop(e);
+ }
+ if (cm.options.dragDrop) {
+ on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
+ on(d.scroller, "dragenter", drag_);
+ on(d.scroller, "dragover", drag_);
+ on(d.scroller, "drop", operation(cm, onDrop));
+ }
+ on(d.scroller, "paste", function(){focusInput(cm); fastPoll(cm);});
+ on(d.input, "paste", function() {
+ d.pasteIncoming = true;
+ fastPoll(cm);
+ });
+
+ function prepareCopy() {
+ if (d.inaccurateSelection) {
+ d.prevInput = "";
+ d.inaccurateSelection = false;
+ d.input.value = cm.getSelection();
+ selectInput(d.input);
+ }
+ }
+ on(d.input, "cut", prepareCopy);
+ on(d.input, "copy", prepareCopy);
+
+ // Needed to handle Tab key in KHTML
+ if (khtml) on(d.sizer, "mouseup", function() {
+ if (document.activeElement == d.input) d.input.blur();
+ focusInput(cm);
+ });
+ }
+
+ function mouseEventInWidget(display, e) {
+ for (var n = e_target(e); n != display.wrapper; n = n.parentNode)
+ if (/\bCodeMirror-linewidget\b/.test(n.className) ||
+ n.parentNode == display.sizer && n != display.mover) return true;
+ }
+
+ function posFromMouse(cm, e, liberal) {
+ var display = cm.display;
+ if (!liberal) {
+ var target = e_target(e);
+ if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
+ target == display.scrollbarV || target == display.scrollbarV.firstChild ||
+ target == display.scrollbarFiller) return null;
+ }
+ var x, y, space = display.lineSpace.getBoundingClientRect();
+ // Fails unpredictably on IE[67] when mouse is dragged around quickly.
+ try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
+ return coordsChar(cm, x - space.left, y - space.top);
+ }
+
+ var lastClick, lastDoubleClick;
+ function onMouseDown(e) {
+ var cm = this, display = cm.display, view = cm.view, sel = view.sel, doc = view.doc;
+ setShift(cm.view, e_prop(e, "shiftKey"));
+
+ if (mouseEventInWidget(display, e)) {
+ if (!webkit) {
+ display.scroller.draggable = false;
+ setTimeout(function(){display.scroller.draggable = true;}, 100);
+ }
+ return;
+ }
+ if (clickInGutter.call(cm, e)) return;
+ var start = posFromMouse(cm, e);
+
+ switch (e_button(e)) {
+ case 3:
+ if (gecko) onContextMenu.call(cm, cm, e);
+ return;
+ case 2:
+ if (start) setSelectionUser(cm, start, start);
+ setTimeout(bind(focusInput, cm), 20);
+ e_preventDefault(e);
+ return;
+ }
+ // For button 1, if it was clicked inside the editor
+ // (posFromMouse returning non-null), we have to adjust the
+ // selection.
+ if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
+
+ if (!view.focused) onFocus(cm);
+
+ var now = +new Date, type = "single";
+ if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
+ type = "triple";
+ e_preventDefault(e);
+ setTimeout(bind(focusInput, cm), 20);
+ selectLine(cm, start.line);
+ } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
+ type = "double";
+ lastDoubleClick = {time: now, pos: start};
+ e_preventDefault(e);
+ var word = findWordAt(getLine(doc, start.line).text, start);
+ setSelectionUser(cm, word.from, word.to);
+ } else { lastClick = {time: now, pos: start}; }
+
+ var last = start;
+ if (cm.options.dragDrop && dragAndDrop && !cm.options.readOnly && !posEq(sel.from, sel.to) &&
+ !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
+ var dragEnd = operation(cm, function(e2) {
+ if (webkit) display.scroller.draggable = false;
+ view.draggingText = false;
+ off(document, "mouseup", dragEnd);
+ off(display.scroller, "drop", dragEnd);
+ if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
+ e_preventDefault(e2);
+ setSelectionUser(cm, start, start);
+ focusInput(cm);
+ }
+ });
+ // Let the drag handler handle this.
+ if (webkit) display.scroller.draggable = true;
+ view.draggingText = dragEnd;
+ // IE's approach to draggable
+ if (display.scroller.dragDrop) display.scroller.dragDrop();
+ on(document, "mouseup", dragEnd);
+ on(display.scroller, "drop", dragEnd);
+ return;
+ }
+ e_preventDefault(e);
+ if (type == "single") setSelectionUser(cm, start, start);
+
+ var startstart = sel.from, startend = sel.to;
+
+ function doSelect(cur) {
+ if (type == "single") {
+ setSelectionUser(cm, start, cur);
+ } else if (type == "double") {
+ var word = findWordAt(getLine(doc, cur.line).text, cur);
+ if (posLess(cur, startstart)) setSelectionUser(cm, word.from, startend);
+ else setSelectionUser(cm, startstart, word.to);
+ } else if (type == "triple") {
+ if (posLess(cur, startstart)) setSelectionUser(cm, startend, clipPos(doc, {line: cur.line, ch: 0}));
+ else setSelectionUser(cm, startstart, clipPos(doc, {line: cur.line + 1, ch: 0}));
+ }
+ }
+
+ var editorSize = display.wrapper.getBoundingClientRect();
+ // Used to ensure timeout re-tries don't fire when another extend
+ // happened in the meantime (clearTimeout isn't reliable -- at
+ // least on Chrome, the timeouts still happen even when cleared,
+ // if the clear happens after their scheduled firing time).
+ var counter = 0;
+
+ function extend(e) {
+ var curCount = ++counter;
+ var cur = posFromMouse(cm, e, true);
+ if (!cur) return;
+ if (!posEq(cur, last)) {
+ if (!view.focused) onFocus(cm);
+ last = cur;
+ doSelect(cur);
+ var visible = visibleLines(display, doc);
+ if (cur.line >= visible.to || cur.line < visible.from)
+ setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
+ } else {
+ var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
+ if (outside) setTimeout(operation(cm, function() {
+ if (counter != curCount) return;
+ display.scroller.scrollTop += outside;
+ extend(e);
+ }), 50);
+ }
+ }
+
+ function done(e) {
+ counter = Infinity;
+ var cur = posFromMouse(cm, e);
+ if (cur) doSelect(cur);
+ e_preventDefault(e);
+ focusInput(cm);
+ off(document, "mousemove", move);
+ off(document, "mouseup", up);
+ }
+
+ var move = operation(cm, function(e) {
+ e_preventDefault(e);
+ if (!ie && !e_button(e)) done(e);
+ else extend(e);
+ });
+ var up = operation(cm, done);
+ on(document, "mousemove", move);
+ on(document, "mouseup", up);
+ }
+
+ function onDrop(e) {
+ var cm = this;
+ if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
+ e_preventDefault(e);
+ var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
+ if (!pos || cm.options.readOnly) return;
+ if (files && files.length && window.FileReader && window.File) {
+ var n = files.length, text = Array(n), read = 0;
+ var loadFile = function(file, i) {
+ var reader = new FileReader;
+ reader.onload = function() {
+ text[i] = reader.result;
+ if (++read == n) {
+ pos = clipPos(cm.view.doc, pos);
+ operation(cm, function() {
+ var end = replaceRange(cm, text.join(""), pos, pos);
+ setSelectionUser(cm, pos, end);
+ })();
+ }
+ };
+ reader.readAsText(file);
+ };
+ for (var i = 0; i < n; ++i) loadFile(files[i], i);
+ } else {
+ // Don't do a replace if the drop happened inside of the selected text.
+ if (cm.view.draggingText && !(posLess(pos, cm.view.sel.from) || posLess(cm.view.sel.to, pos))) {
+ cm.view.draggingText(e);
+ if (ie) setTimeout(bind(focusInput, cm), 50);
+ return;
+ }
+ try {
+ var text = e.dataTransfer.getData("Text");
+ if (text) {
+ compoundChange(cm, function() {
+ var curFrom = cm.view.sel.from, curTo = cm.view.sel.to;
+ setSelectionUser(cm, pos, pos);
+ if (cm.view.draggingText) replaceRange(cm, "", curFrom, curTo);
+ cm.replaceSelection(text);
+ focusInput(cm);
+ onFocus(cm);
+ });
+ }
+ }
+ catch(e){}
+ }
+ }
+
+ function clickInGutter(e) {
+ var cm = this, display = cm.display;
+ try { var mX = e.clientX, mY = e.clientY; }
+ catch(e) { return false; }
+
+ if (mX >= Math.floor(display.gutters.getBoundingClientRect().right)) return false;
+ e_preventDefault(e);
+ if (!hasHandler(cm, "gutterClick")) return true;
+
+ var lineBox = display.lineDiv.getBoundingClientRect();
+ if (mY > lineBox.bottom) return true;
+ mY -= lineBox.top - display.viewOffset;
+
+ for (var i = 0; i < cm.options.gutters.length; ++i) {
+ var g = display.gutters.childNodes[i];
+ if (g && g.getBoundingClientRect().right >= mX) {
+ var line = lineAtHeight(cm.view.doc, mY);
+ var gutter = cm.options.gutters[i];
+ signalLater(cm, cm, "gutterClick", cm, line, gutter, e);
+ break;
+ }
+ }
+ return true;
+ }
+
+ function onDragStart(cm, e) {
+ var txt = cm.getSelection();
+ e.dataTransfer.setData("Text", txt);
+
+ // Use dummy image instead of default browsers image.
+ if (e.dataTransfer.setDragImage)
+ e.dataTransfer.setDragImage(elt('img'), 0, 0);
+ }
+
+ function setScrollTop(cm, val) {
+ if (cm.view.scrollTop == val) return;
+ cm.view.scrollTop = val;
+ if (!gecko) updateDisplay(cm, [], val);
+ if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
+ if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
+ if (gecko) updateDisplay(cm, []);
+ }
+ function setScrollLeft(cm, val) {
+ if (cm.view.scrollLeft == val) return;
+ cm.view.scrollLeft = val;
+ if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
+ if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
+ alignVertically(cm.display);
+ }
+
+ // Since the delta values reported on mouse wheel events are
+ // unstandardized between browsers and even browser versions, and
+ // generally horribly unpredictable, this code starts by measuring
+ // the scroll effect that the first few mouse wheel events have,
+ // and, from that, detects the way it can convert deltas to pixel
+ // offsets afterwards.
+ //
+ // The reason we want to directly handle the wheel event is that it
+ // gives us a chance to update the display before the actual
+ // scrolling happens, reducing flickering.
+
+ var wheelSamples = [], wheelDX, wheelDY, wheelStartX, wheelStartY, wheelPixelsPerUnit;
+ function onScrollWheel(cm, e) {
+ var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
+ if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
+ if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
+ else if (dy == null) dy = e.wheelDelta;
+
+ var scroll = cm.display.scroller;
+ if (wheelPixelsPerUnit != null) {
+ if (dx) setScrollLeft(cm, Math.max(0, Math.round(scroll.scrollLeft += dx * wheelPixelsPerUnit)));
+ if (dy) setScrollTop(cm, Math.max(0, Math.round(scroll.scrollTop + dy * wheelPixelsPerUnit)));
+ e_stop(e);
+ } else {
+ if (wheelStartX == null) {
+ wheelStartX = scroll.scrollLeft; wheelStartY = scroll.scrollTop;
+ wheelDX = dx; wheelDY = dy;
+ setTimeout(function() {
+ var movedX = scroll.scrollLeft - wheelStartX;
+ var movedY = scroll.scrollTop - wheelStartY;
+ var sample = (movedY && wheelDY && movedY / wheelDY) ||
+ (movedX && wheelDX && movedX / wheelDX);
+ wheelStartX = wheelStartY = null;
+ if (!sample) return;
+ wheelSamples.push(sample);
+ if (wheelSamples.length >= 15) {
+ for (var total = 0, i = 0; i < wheelSamples.length; ++i) total += wheelSamples[i];
+ wheelPixelsPerUnit = total / wheelSamples.length;
+ }
+ }, 200);
+ } else {
+ wheelDX += dx; wheelDY += dy;
+ }
+ }
+ }
+
+ function doHandleBinding(cm, bound, dropShift) {
+ if (typeof bound == "string") {
+ bound = commands[bound];
+ if (!bound) return false;
+ }
+ var view = cm.view, prevShift = view.sel.shift;
+ try {
+ if (cm.options.readOnly) view.suppressEdits = true;
+ if (dropShift) view.sel.shift = null;
+ bound(cm);
+ } catch(e) {
+ if (e != Pass) throw e;
+ return false;
+ } finally {
+ view.sel.shift = prevShift;
+ view.suppressEdits = false;
+ }
+ return true;
+ }
+
+ var maybeTransition;
+ function handleKeyBinding(cm, e) {
+ // Handle auto keymap transitions
+ var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
+ clearTimeout(maybeTransition);
+ if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
+ if (getKeyMap(cm.options.keyMap) == startMap)
+ cm.options.keyMap = (next.call ? next.call(null, cm) : next);
+ }, 50);
+
+ var name = keyNames[e_prop(e, "keyCode")], handled = false;
+ var flipCtrlCmd = opera && mac;
+ if (name == null || e.altGraphKey) return false;
+ if (e_prop(e, "altKey")) name = "Alt-" + name;
+ if (e_prop(e, flipCtrlCmd ? "metaKey" : "ctrlKey")) name = "Ctrl-" + name;
+ if (e_prop(e, flipCtrlCmd ? "ctrlKey" : "metaKey")) name = "Cmd-" + name;
+
+ var stopped = false;
+ function stop() { stopped = true; }
+
+ if (e_prop(e, "shiftKey")) {
+ handled = lookupKey("Shift-" + name, cm.options.extraKeys, cm.options.keyMap,
+ function(b) {return doHandleBinding(cm, b, true);}, stop)
+ || lookupKey(name, cm.options.extraKeys, cm.options.keyMap, function(b) {
+ if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b);
+ }, stop);
+ } else {
+ handled = lookupKey(name, cm.options.extraKeys, cm.options.keyMap,
+ function(b) { return doHandleBinding(cm, b); }, stop);
+ }
+ if (stopped) handled = false;
+ if (handled) {
+ e_preventDefault(e);
+ restartBlink(cm);
+ if (ie) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
+ }
+ return handled;
+ }
+
+ function handleCharBinding(cm, e, ch) {
+ var handled = lookupKey("'" + ch + "'", cm.options.extraKeys, cm.options.keyMap,
+ function(b) { return doHandleBinding(cm, b, true); });
+ if (handled) {
+ e_preventDefault(e);
+ restartBlink(cm);
+ }
+ return handled;
+ }
+
+ var lastStoppedKey = null;
+ function onKeyDown(e) {
+ var cm = this;
+ if (!cm.view.focused) onFocus(cm);
+ if (ie && e.keyCode == 27) { e.returnValue = false; }
+ if (cm.display.pollingFast) { if (readInput(cm)) cm.display.pollingFast = false; }
+ if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
+ var code = e_prop(e, "keyCode");
+ // IE does strange things with escape.
+ setShift(cm.view, code == 16 || e_prop(e, "shiftKey"));
+ // First give onKeyEvent option a chance to handle this.
+ var handled = handleKeyBinding(cm, e);
+ if (opera) {
+ lastStoppedKey = handled ? code : null;
+ // Opera has no cut event... we try to at least catch the key combo
+ if (!handled && code == 88 && e_prop(e, mac ? "metaKey" : "ctrlKey"))
+ cm.replaceSelection("");
+ }
+ }
+
+ function onKeyPress(e) {
+ var cm = this;
+ if (cm.display.pollingFast) readInput(cm);
+ if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
+ var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode");
+ if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
+ if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
+ var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
+ if (this.options.electricChars && this.view.mode.electricChars &&
+ this.options.smartIndent && !this.options.readOnly &&
+ this.view.mode.electricChars.indexOf(ch) > -1)
+ setTimeout(operation(cm, function() {indentLine(cm, cm.view.sel.to.line, "smart");}), 75);
+ if (handleCharBinding(cm, e, ch)) return;
+ fastPoll(cm);
+ }
+
+ function onFocus(cm) {
+ if (cm.options.readOnly == "nocursor") return;
+ if (!cm.view.focused) {
+ signal(cm, "focus", cm);
+ cm.view.focused = true;
+ if (cm.display.scroller.className.search(/\bCodeMirror-focused\b/) == -1)
+ cm.display.scroller.className += " CodeMirror-focused";
+ }
+ slowPoll(cm);
+ restartBlink(cm);
+ }
+ function onBlur(cm) {
+ if (cm.view.focused) {
+ signal(cm, "blur", cm);
+ cm.view.focused = false;
+ cm.display.scroller.className = cm.display.scroller.className.replace(" CodeMirror-focused", "");
+ }
+ clearInterval(cm.display.blinker);
+ setTimeout(function() {if (!cm.view.focused) cm.view.sel.shift = null;}, 150);
+ }
+
+ var detectingSelectAll;
+ function onContextMenu(cm, e) {
+ var display = cm.display, sel = cm.view.sel;
+ var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
+ if (!pos || opera) return; // Opera is difficult.
+ if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
+ operation(cm, setSelection)(cm, pos, pos);
+
+ var oldCSS = display.input.style.cssText;
+ display.inputDiv.style.position = "absolute";
+ display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
+ "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
+ "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
+ focusInput(cm);
+ resetInput(cm, true);
+ // Adds "Select all" to context menu in FF
+ if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
+
+ function rehide() {
+ display.inputDiv.style.position = "relative";
+ display.input.style.cssText = oldCSS;
+ if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
+ slowPoll(cm);
+
+ // Try to detect the user choosing select-all
+ if (display.input.selectionStart != null) {
+ clearTimeout(detectingSelectAll);
+ var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0;
+ display.prevInput = " ";
+ display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
+ detectingSelectAll = setTimeout(function poll(){
+ if (display.prevInput == " " && display.input.selectionStart == 0)
+ operation(cm, commands.selectAll)(cm);
+ else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
+ else resetInput(cm);
+ }, 200);
+ }
+ }
+
+ if (gecko) {
+ e_stop(e);
+ on(window, "mouseup", function mouseup() {
+ off(window, "mouseup", mouseup);
+ setTimeout(rehide, 20);
+ });
+ } else {
+ setTimeout(rehide, 50);
+ }
+ }
+
+ // UPDATING
+
+ // Replace the range from from to to by the strings in newText.
+ // Afterwards, set the selection to selFrom, selTo.
+ function updateDoc(cm, from, to, newText, selFrom, selTo) {
+ var view = cm.view, doc = view.doc;
+ if (view.suppressEdits) return;
+ var old = [];
+ doc.iter(from.line, to.line + 1, function(line) {
+ old.push(newHL(line.text, line.markedSpans));
+ });
+ if (view.history) {
+ addChange(view.history, from.line, newText.length, old);
+ while (view.history.done.length > cm.options.undoDepth) view.history.done.shift();
+ }
+ var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText);
+ updateDocNoUndo(cm, from, to, lines, selFrom, selTo);
+ }
+ function unredoHelper(cm, from, to) {
+ var doc = cm.view.doc;
+ if (!from.length) return;
+ var set = from.pop(), out = [];
+ for (var i = set.length - 1; i >= 0; i -= 1) {
+ var change = set[i];
+ var replaced = [], end = change.start + change.added;
+ doc.iter(change.start, end, function(line) { replaced.push(newHL(line.text, line.markedSpans)); });
+ out.push({start: change.start, added: change.old.length, old: replaced});
+ var pos = {line: change.start + change.old.length - 1,
+ ch: editEnd(hlText(lst(replaced)), hlText(lst(change.old)))};
+ updateDocNoUndo(cm, {line: change.start, ch: 0}, {line: end - 1, ch: getLine(doc, end-1).text.length},
+ change.old, pos, pos);
+ }
+ to.push(out);
+ }
+ function undo(cm) {
+ var hist = cm.view.history;
+ unredoHelper(cm, hist.done, hist.undone);
+ }
+ function redo(cm) {
+ var hist = cm.view.history;
+ unredoHelper(cm, hist.undone, hist.done);
+ }
+
+ function updateDocNoUndo(cm, from, to, lines, selFrom, selTo) {
+ var view = cm.view, doc = view.doc, display = cm.display;
+ if (view.suppressEdits) return;
+ var recomputeMaxLength = false, maxLineLength = view.maxLine.text.length;
+ if (!cm.options.lineWrapping)
+ doc.iter(from.line, to.line + 1, function(line) {
+ if (!line.hidden && line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
+ });
+
+ var nlines = to.line - from.line, firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
+ var lastHL = lst(lines), th = textHeight(display);
+
+ // First adjust the line structure
+ if (from.ch == 0 && to.ch == 0 && hlText(lastHL) == "") {
+ // This is a whole-line replace. Treated specially to make
+ // sure line objects move the way they are supposed to.
+ var added = [], prevLine = null;
+ for (var i = 0, e = lines.length - 1; i < e; ++i)
+ added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
+ updateLine(cm, lastLine, lastLine.text, hlSpans(lastHL));
+ if (nlines) doc.remove(from.line, nlines, cm);
+ if (added.length) doc.insert(from.line, added);
+ } else if (firstLine == lastLine) {
+ if (lines.length == 1) {
+ updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) +
+ firstLine.text.slice(to.ch), hlSpans(lines[0]));
+ } else {
+ for (var added = [], i = 1, e = lines.length - 1; i < e; ++i)
+ added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
+ added.push(makeLine(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL), th));
+ updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
+ doc.insert(from.line + 1, added);
+ }
+ } else if (lines.length == 1) {
+ updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) +
+ lastLine.text.slice(to.ch), hlSpans(lines[0]));
+ doc.remove(from.line + 1, nlines, cm);
+ } else {
+ var added = [];
+ updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
+ updateLine(cm, lastLine, hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL));
+ for (var i = 1, e = lines.length - 1; i < e; ++i)
+ added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
+ if (nlines > 1) doc.remove(from.line + 1, nlines - 1, cm);
+ doc.insert(from.line + 1, added);
+ }
+
+ if (cm.options.lineWrapping) {
+ var perLine = Math.max(5, display.scroller.clientWidth / charWidth(display) - 3);
+ doc.iter(from.line, from.line + lines.length, function(line) {
+ if (line.hidden) return;
+ var guess = (Math.ceil(line.text.length / perLine) || 1) * th;
+ if (guess != line.height) updateLineHeight(line, guess);
+ });
+ } else {
+ doc.iter(from.line, from.line + lines.length, function(line) {
+ var l = line.text;
+ if (!line.hidden && l.length > maxLineLength) {
+ view.maxLine = line; maxLineLength = l.length; view.maxLineChanged = true;
+ recomputeMaxLength = false;
+ }
+ });
+ if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
+ }
+
+ // Adjust frontier, schedule worker
+ view.frontier = Math.min(view.frontier, from.line);
+ startWorker(cm, 400);
+
+ var lendiff = lines.length - nlines - 1;
+ // Remember that these lines changed, for updating the display
+ regChange(cm, from.line, to.line + 1, lendiff);
+ if (hasHandler(cm, "change")) {
+ // Normalize lines to contain only strings, since that's what
+ // the change event handler expects
+ for (var i = 0; i < lines.length; ++i)
+ if (typeof lines[i] != "string") lines[i] = lines[i].text;
+ var changeObj = {from: from, to: to, text: lines};
+ if (cm.curOp.textChanged) {
+ for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
+ cur.next = changeObj;
+ } else cm.curOp.textChanged = changeObj;
+ }
+
+ // Update the selection
+ setSelection(cm, clipPos(doc, selFrom), clipPos(doc, selTo), true);
+ }
+
+ function replaceRange(cm, code, from, to) {
+ if (!to) to = from;
+ if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
+ code = splitLines(code);
+ function adjustPos(pos) {
+ if (posLess(pos, from)) return pos;
+ if (!posLess(to, pos)) return end;
+ var line = pos.line + code.length - (to.line - from.line) - 1;
+ var ch = pos.ch;
+ if (pos.line == to.line)
+ ch += lst(code).length - (to.ch - (to.line == from.line ? from.ch : 0));
+ return {line: line, ch: ch};
+ }
+ var end;
+ replaceRange1(cm, code, from, to, function(end1) {
+ end = end1;
+ return {from: adjustPos(cm.view.sel.from), to: adjustPos(cm.view.sel.to)};
+ });
+ return end;
+ }
+ function replaceRange1(cm, code, from, to, computeSel) {
+ var endch = code.length == 1 ? code[0].length + from.ch : lst(code).length;
+ var newSel = computeSel({line: from.line + code.length - 1, ch: endch});
+ updateDoc(cm, from, to, code, newSel.from, newSel.to);
+ }
+
+ // SELECTION
+
+ function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
+ function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
+ function copyPos(x) {return {line: x.line, ch: x.ch};}
+
+ function clipLine(doc, n) {return Math.max(0, Math.min(n, doc.size-1));}
+ function clipPos(doc, pos) {
+ if (pos.line < 0) return {line: 0, ch: 0};
+ if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc, doc.size-1).text.length};
+ var ch = pos.ch, linelen = getLine(doc, pos.line).text.length;
+ if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
+ else if (ch < 0) return {line: pos.line, ch: 0};
+ else return pos;
+ }
+ function isLine(doc, l) {return l >= 0 && l < doc.size;}
+
+ function setShift(view, val) {
+ if (val) view.sel.shift = view.sel.shift || selHead(view);
+ else view.sel.shift = null;
+ }
+ function setSelectionUser(cm, from, to) {
+ var view = cm.view, sh = view.sel.shift;
+ if (sh) {
+ sh = clipPos(view.doc, sh);
+ if (posLess(sh, from)) from = sh;
+ else if (posLess(to, sh)) to = sh;
+ }
+ setSelection(cm, from, to);
+ cm.curOp.userSelChange = true;
+ }
+
+ // Update the selection. Last two args are only used by
+ // updateDoc, since they have to be expressed in the line
+ // numbers before the update.
+ function setSelection(cm, from, to, isChange) {
+ cm.curOp.updateInput = true;
+ var sel = cm.view.sel, doc = cm.view.doc;
+ cm.view.goalColumn = null;
+ if (posEq(sel.from, from) && posEq(sel.to, to)) return;
+ if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
+
+ // Skip over hidden lines.
+ if (isChange || from.line != sel.from.line) {
+ var from1 = skipHidden(doc, from, sel.from.line, sel.from.ch, cm);
+ // If there is no non-hidden line left, force visibility on current line
+ if (!from1) cm.unfoldLines(getLine(doc, from.line).hidden.id);
+ else from = from1;
+ }
+ if (isChange || to.line != sel.to.line) to = skipHidden(doc, to, sel.to.line, sel.to.ch, cm);
+
+ if (posEq(from, to)) sel.inverted = false;
+ else if (posEq(from, sel.to)) sel.inverted = false;
+ else if (posEq(to, sel.from)) sel.inverted = true;
+
+ if (cm.options.autoClearEmptyLines && posEq(sel.from, sel.to)) {
+ var head = selHead(cm.view);
+ if (head.line != sel.from.line && sel.from.line < doc.size) {
+ var oldLine = getLine(doc, sel.from.line);
+ if (/^\s+$/.test(oldLine.text))
+ setTimeout(operation(cm, function() {
+ if (oldLine.parent && /^\s+$/.test(oldLine.text)) {
+ var no = lineNo(oldLine);
+ replaceRange(cm, "", {line: no, ch: 0}, {line: no, ch: oldLine.text.length});
+ }
+ }, 10));
+ }
+ }
+
+ sel.from = from; sel.to = to;
+ cm.curOp.selectionChanged = true;
+ }
+
+ function skipHidden(doc, pos, oldLine, oldCh, allowUnfold) {
+ function getNonHidden(dir) {
+ var lNo = pos.line + dir, end = dir == 1 ? doc.size : -1;
+ while (lNo != end) {
+ var line = getLine(doc, lNo);
+ if (!line.hidden) {
+ var ch = pos.ch;
+ if (toEnd || ch > oldCh || ch > line.text.length) ch = line.text.length;
+ return {line: lNo, ch: ch};
+ }
+ lNo += dir;
+ }
+ }
+ var line = getLine(doc, pos.line);
+ while (allowUnfold && line.hidden && line.hidden.handle.unfoldOnEnter)
+ allowUnfold.unfoldLines(line.hidden.handle);
+ if (!line.hidden) return pos;
+ var toEnd = pos.ch == line.text.length && pos.ch != oldCh;
+ if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1);
+ else return getNonHidden(-1) || getNonHidden(1);
+ }
+
+ // SCROLLING
+
+ function scrollCursorIntoView(cm) {
+ var view = cm.view, coords = cursorCoords(cm, selHead(view));
+ scrollIntoView(cm.display, coords.left, coords.top, coords.left, coords.bottom);
+ if (!view.focused) return;
+ var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
+ if (coords.top + box.top < 0) doScroll = true;
+ else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
+ if (doScroll != null) {
+ var hidden = display.cursor.style.display == "none";
+ if (hidden) {
+ display.cursor.style.display = "";
+ display.cursor.style.left = coords.left + "px";
+ display.cursor.style.top = (coords.top - display.viewOffset) + "px";
+ }
+ display.cursor.scrollIntoView(doScroll);
+ if (hidden) display.cursor.style.display = "none";
+ }
+ }
+
+ function scrollIntoView(display, x1, y1, x2, y2) {
+ var scrollPos = calculateScrollPos(display, x1, y1, x2, y2);
+ if (scrollPos.scrollLeft != null) {display.scrollbarH.scrollLeft = display.scroller.scrollLeft = scrollPos.scrollLeft;}
+ if (scrollPos.scrollTop != null) {display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos.scrollTop;}
+ }
+
+ function calculateScrollPos(display, x1, y1, x2, y2) {
+ var pt = paddingTop(display);
+ y1 += pt; y2 += pt;
+ var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
+ var docBottom = display.scroller.scrollHeight - scrollerCutOff;
+ var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
+ if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
+ else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2 - screen);
+
+ var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
+ x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
+ var gutterw = display.gutters.offsetWidth;
+ var atLeft = x1 < gutterw + 10;
+ if (x1 < screenleft + gutterw || atLeft) {
+ if (atLeft) x1 = 0;
+ result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
+ } else if (x2 > screenw + screenleft - 3) {
+ result.scrollLeft = x2 + 10 - screenw;
+ }
+ return result;
+ }
+
+ // API UTILITIES
+
+ function indentLine(cm, n, how) {
+ var doc = cm.view.doc;
+ if (!how) how = "add";
+ if (how == "smart") {
+ if (!cm.view.mode.indent) how = "prev";
+ else var state = getStateBefore(cm, n);
+ }
+
+ var tabSize = cm.options.tabSize;
+ var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
+ var curSpaceString = line.text.match(/^\s*/)[0], indentation;
+ if (how == "smart") {
+ indentation = cm.view.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
+ if (indentation == Pass) how = "prev";
+ }
+ if (how == "prev") {
+ if (n) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
+ else indentation = 0;
+ }
+ else if (how == "add") indentation = curSpace + cm.options.indentUnit;
+ else if (how == "subtract") indentation = curSpace - cm.options.indentUnit;
+ indentation = Math.max(0, indentation);
+ var diff = indentation - curSpace;
+
+ var indentString = "", pos = 0;
+ if (cm.options.indentWithTabs)
+ for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
+ if (pos < indentation) indentString += spaceStr(indentation - pos);
+
+ if (indentString != curSpaceString)
+ replaceRange(cm, indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length});
+ line.stateAfter = null;
+ }
+
+ function changeLine(cm, handle, op) {
+ var no = handle, line = handle, doc = cm.view.doc;
+ if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
+ else no = lineNo(handle);
+ if (no == null) return null;
+ if (op(line, no)) regChange(cm, no, no + 1);
+ else return null;
+ return line;
+ }
+
+ function findPosH(cm, dir, unit, visually) {
+ var doc = cm.view.doc, end = selHead(cm.view), line = end.line, ch = end.ch;
+ var lineObj = getLine(doc, line);
+ function findNextLine() {
+ for (var l = line + dir, e = dir < 0 ? -1 : doc.size; l != e; l += dir) {
+ var lo = getLine(doc, l);
+ if (!lo.hidden || lo.hidden.handle.unfoldOnEnter) { line = l; lineObj = lo; return true; }
+ }
+ }
+ function moveOnce(boundToLine) {
+ var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
+ if (next == null) {
+ if (!boundToLine && findNextLine()) {
+ if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
+ else ch = dir < 0 ? lineObj.text.length : 0;
+ } else return false;
+ } else ch = next;
+ return true;
+ }
+ if (unit == "char") moveOnce();
+ else if (unit == "column") moveOnce(true);
+ else if (unit == "word") {
+ var sawWord = false;
+ for (;;) {
+ if (dir < 0) if (!moveOnce()) break;
+ if (isWordChar(lineObj.text.charAt(ch))) sawWord = true;
+ else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;}
+ if (dir > 0) if (!moveOnce()) break;
+ }
+ }
+ return {line: line, ch: ch};
+ }
+
+ function findWordAt(line, pos) {
+ var start = pos.ch, end = pos.ch;
+ if (line) {
+ if (pos.after === false || end == line.length) --start; else ++end;
+ var startChar = line.charAt(start);
+ var check = isWordChar(startChar) ? isWordChar :
+ /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} :
+ function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
+ while (start > 0 && check(line.charAt(start - 1))) --start;
+ while (end < line.length && check(line.charAt(end))) ++end;
+ }
+ return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}};
+ }
+
+ function selectLine(cm, line) {
+ setSelectionUser(cm, {line: line, ch: 0}, clipPos(cm.view.doc, {line: line + 1, ch: 0}));
+ }
+
+ // PROTOTYPE
+
+ // The publicly visible API. Note that operation(null, f) means
+ // 'wrap f in an operation, performed on its `this` parameter'
+
+ CodeMirror.prototype = {
+ getValue: function(lineSep) {
+ var text = [], doc = this.view.doc;
+ doc.iter(0, doc.size, function(line) { text.push(line.text); });
+ return text.join(lineSep || "\n");
+ },
+
+ setValue: operation(null, function(code) {
+ var doc = this.view.doc, top = {line: 0, ch: 0}, lastLen = getLine(doc, doc.size-1).text.length;
+ updateDoc(this, top, {line: doc.size - 1, ch: lastLen}, splitLines(code), top, top);
+ }),
+
+ getSelection: function(lineSep) { return this.getRange(this.view.sel.from, this.view.sel.to, lineSep); },
+
+ replaceSelection: operation(null, function(code, collapse) {
+ var sel = this.view.sel;
+ replaceRange1(this, splitLines(code), sel.from, sel.to, function(end) {
+ if (collapse == "end") return {from: end, to: end};
+ else if (collapse == "start") return {from: sel.from, to: sel.from};
+ else return {from: sel.from, to: end};
+ });
+ }),
+
+ focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
+
+ setOption: function(option, value) {
+ var options = this.options;
+ if (options[option] == value && option != "mode") return;
+ options[option] = value;
+ if (option == "mode" || option == "indentUnit") loadMode(this);
+ else if (option == "readOnly" && value == "nocursor") {onBlur(this); this.display.input.blur();}
+ else if (option == "readOnly" && !value) {resetInput(this, true);}
+ else if (option == "theme") themeChanged(this);
+ else if (option == "lineWrapping") operation(this, wrappingChanged)(this);
+ else if (option == "tabSize") updateDisplay(this, true);
+ else if (option == "keyMap") keyMapChanged(this);
+ else if (option == "gutters" || option == "lineNumbers") setGuttersForLineNumbers(this.options);
+ else if (option == "tabindex") this.display.input.tabIndex = value;
+ else if (option == "viewportMargin") this.refresh();
+ if (option == "lineNumbers" || option == "gutters" || option == "firstLineNumber" ||
+ option == "theme" || option == "lineNumberFormatter")
+ guttersChanged(this);
+ if (optionHandlers.hasOwnProperty(option))
+ optionHandlers[option](this, value);
+ },
+
+ getOption: function(option) {return this.options[option];},
+
+ getMode: function() {return this.view.mode;},
+
+ undo: operation(null, function() {
+ var hist = this.view.history;
+ unredoHelper(this, hist.done, hist.undone);
+ }),
+ redo: operation(null, function() {
+ var hist = this.view.history;
+ unredoHelper(this, hist.undone, hist.done);
+ }),
+
+ indentLine: operation(null, function(n, dir) {
+ if (typeof dir != "string") {
+ if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
+ else dir = dir ? "add" : "subtract";
+ }
+ if (isLine(this.view.doc, n)) indentLine(this, n, dir);
+ }),
+
+ indentSelection: operation(null, function(how) {
+ var sel = this.view.sel;
+ if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
+ var e = sel.to.line - (sel.to.ch ? 0 : 1);
+ for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
+ }),
+
+ historySize: function() {
+ var hist = this.view.history;
+ return {undo: hist.done.length, redo: hist.undone.length};
+ },
+
+ clearHistory: function() {this.view.history = makeHistory();},
+
+ getHistory: function() {
+ var hist = this.view.history;
+ function cp(arr) {
+ for (var i = 0, nw = [], nwelt; i < arr.length; ++i) {
+ nw.push(nwelt = []);
+ for (var j = 0, elt = arr[i]; j < elt.length; ++j) {
+ var old = [], cur = elt[j];
+ nwelt.push({start: cur.start, added: cur.added, old: old});
+ for (var k = 0; k < cur.old.length; ++k) old.push(hlText(cur.old[k]));
+ }
+ }
+ return nw;
+ }
+ return {done: cp(hist.done), undone: cp(hist.undone)};
+ },
+
+ setHistory: function(histData) {
+ var hist = this.view.history = makeHistory();
+ hist.done = histData.done;
+ hist.undone = histData.undone;
+ },
+
+ getTokenAt: function(pos) {
+ var doc = this.view.doc;
+ pos = clipPos(doc, pos);
+ return getTokenAt(this, getLine(doc, pos.line), getStateBefore(this, pos.line), pos.ch);
+ },
+
+ getStateAfter: function(line) {
+ var doc = this.view.doc;
+ line = clipLine(doc, line == null ? doc.size - 1: line);
+ return getStateBefore(this, line + 1);
+ },
+
+ cursorCoords: function(start, mode) {
+ var pos, sel = this.view.sel;
+ if (start == null) start = sel.inverted;
+ if (typeof start == "object") pos = clipPos(this.view.doc, start);
+ else pos = start ? sel.from : sel.to;
+ return cursorCoords(this, pos, mode || "page");
+ },
+
+ charCoords: function(pos, mode) {
+ return charCoords(this, clipPos(this.view.doc, pos), mode || "page");
+ },
+
+ coordsChar: function(coords) {
+ var off = this.display.lineSpace.getBoundingClientRect();
+ return coordsChar(this, coords.left - off.left, coords.top - off.top);
+ },
+
+ markText: operation(null, function(from, to, className, options) {
+ var doc = this.view.doc;
+ from = clipPos(doc, from); to = clipPos(doc, to);
+ var marker = new TextMarker(this, "range", className);
+ if (options) for (var opt in options) if (options.hasOwnProperty(opt))
+ marker[opt] = options[opt];
+ var curLine = from.line;
+ doc.iter(curLine, to.line + 1, function(line) {
+ var span = {from: curLine == from.line ? from.ch : null,
+ to: curLine == to.line ? to.ch : null,
+ marker: marker};
+ line.markedSpans = (line.markedSpans || []).concat([span]);
+ marker.lines.push(line);
+ ++curLine;
+ });
+ regChange(this, from.line, to.line + 1);
+ return marker;
+ }),
+
+ setBookmark: function(pos) {
+ var doc = this.view.doc;
+ pos = clipPos(doc, pos);
+ var marker = new TextMarker(this, "bookmark"), line = getLine(doc, pos.line);
+ var span = {from: pos.ch, to: pos.ch, marker: marker};
+ line.markedSpans = (line.markedSpans || []).concat([span]);
+ marker.lines.push(line);
+ return marker;
+ },
+
+ findMarksAt: function(pos) {
+ var doc = this.view.doc;
+ pos = clipPos(doc, pos);
+ var markers = [], spans = getLine(doc, pos.line).markedSpans;
+ if (spans) for (var i = 0; i < spans.length; ++i) {
+ var span = spans[i];
+ if ((span.from == null || span.from <= pos.ch) &&
+ (span.to == null || span.to >= pos.ch))
+ markers.push(span.marker);
+ }
+ return markers;
+ },
+
+ setGutterMarker: operation(null, function(line, gutterID, value) {
+ return changeLine(this, line, function(line) {
+ var markers = line.gutterMarkers || (line.gutterMarkers = {});
+ markers[gutterID] = value;
+ if (!value && isEmpty(markers)) line.gutterMarkers = null;
+ return true;
+ });
+ }),
+
+ clearGutter: operation(null, function(gutterID) {
+ var i = 0, cm = this, doc = cm.view.doc;
+ doc.iter(0, doc.size, function(line) {
+ if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
+ line.gutterMarkers[gutterID] = null;
+ regChange(cm, i, i + 1);
+ if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
+ }
+ ++i;
+ });
+ }),
+
+ setLineClass: operation(null, function(handle, className, bgClassName) {
+ return changeLine(this, handle, function(line) {
+ if (line.className != className || line.bgClassName != bgClassName) {
+ line.className = className;
+ line.bgClassName = bgClassName;
+ return true;
+ }
+ });
+ }),
+
+ addLineWidget: operation(null, function addLineWidget(handle, node, options) {
+ var widget = options || {};
+ widget.node = node;
+ if (widget.noHScroll) this.display.alignWidgets = true;
+ changeLine(this, handle, function(line) {
+ (line.widgets || (line.widgets = [])).push(widget);
+ widget.line = line;
+ return true;
+ });
+ return widget;
+ }),
+
+ removeLineWidget: operation(null, function(widget) {
+ var ws = widget.line.widgets, no = lineNo(widget.line);
+ if (no == null) return;
+ for (var i = 0; i < ws.length; ++i) if (ws[i] == widget) ws.splice(i--, 1);
+ regChange(this, no, no + 1);
+ }),
+
+ foldLines: operation(null, function(from, to, options) {
+ if (typeof from != "number") from = lineNo(from);
+ if (typeof to != "number") to = lineNo(to);
+ if (from > to) return;
+ var handle = options || {}, lines = handle.lines = [];
+ var cm = this, view = cm.view, doc = view.doc;
+ doc.iter(from, to, function(line) {
+ lines.push(line);
+ if (!line.hidden && line.text.length == cm.view.maxLine.text.length)
+ cm.curOp.updateMaxLine = true;
+ line.hidden = {handle: handle, prev: line.hidden};
+ updateLineHeight(line, 0);
+ });
+ var sel = view.sel, selFrom = sel.from, selTo = sel.to;
+ if (selFrom.line >= from && selFrom.line < to)
+ selFrom = skipHidden(doc, {line: selFrom.line, ch: 0}, selFrom.line, 0);
+ if (selTo.line >= from && selTo.line < to)
+ selTo = skipHidden(doc, {line: selTo.line, ch: 0}, selTo.line, 0);
+ if (selFrom != sel.from || selTo != sel.to) setSelection(this, selFrom, selTo);
+ regChange(cm, from, to);
+ return handle;
+ }),
+
+ unfoldLines: operation(null, function(handle) {
+ var from, to;
+ for (var i = 0; i < handle.lines.length; ++i) {
+ var line = handle.lines[i], hidden = line.hidden;
+ if (!line.parent) continue;
+ if (hidden && hidden.handle == handle) line.hidden = line.hidden.prev;
+ else for (var h = hidden; h; h = h.prev) if (h.prev && h.prev.handle == handle) h.prev = h.prev.prev;
+ if (hidden && !line.hidden) {
+ var no = lineNo(handle);
+ from = Math.min(from, no); to = Math.max(to, no);
+ updateLineHeight(line, textHeight(this.display));
+ if (line.text.length > this.view.maxLine.text.length) {
+ this.view.maxLine = line;
+ this.view.maxLineChanged = true;
+ }
+ }
+ }
+ if (from != null) {
+ regChange(this, from, to + 1);
+ signalLater(this, handle, "unfold");
+ }
+ }),
+
+ lineInfo: function(line) {
+ if (typeof line == "number") {
+ if (!isLine(this.view.doc, line)) return null;
+ var n = line;
+ line = getLine(this.view.doc, line);
+ if (!line) return null;
+ } else {
+ var n = lineNo(line);
+ if (n == null) return null;
+ }
+ return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
+ lineClass: line.className, bgClass: line.bgClassName, widgets: line.widgets};
+ },
+
+ getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};},
+
+ addWidget: function(pos, node, scroll, vert, horiz) {
+ var display = this.display;
+ pos = cursorCoords(this, clipPos(this.view.doc, pos));
+ var top = pos.top, left = pos.left;
+ node.style.position = "absolute";
+ display.sizer.appendChild(node);
+ if (vert == "over") top = pos.top;
+ else if (vert == "near") {
+ var vspace = Math.max(display.wrapper.clientHeight, this.view.doc.height),
+ hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
+ if (pos.bottom + node.offsetHeight > vspace && pos.top > node.offsetHeight)
+ top = pos.top - node.offsetHeight;
+ if (left + node.offsetWidth > hspace)
+ left = hspace - node.offsetWidth;
+ }
+ node.style.top = (top + paddingTop(display)) + "px";
+ node.style.left = node.style.right = "";
+ if (horiz == "right") {
+ left = display.sizer.clientWidth - node.offsetWidth;
+ node.style.right = "0px";
+ } else {
+ if (horiz == "left") left = 0;
+ else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
+ node.style.left = left + "px";
+ }
+ if (scroll)
+ scrollIntoView(display, left, top, left + node.offsetWidth, top + node.offsetHeight);
+ },
+
+ lineCount: function() {return this.view.doc.size;},
+
+ clipPos: function(pos) {return clipPos(this.view.doc, pos);},
+
+ getCursor: function(start) {
+ var sel = this.view.sel;
+ if (start == null) start = sel.inverted;
+ return copyPos(start ? sel.from : sel.to);
+ },
+
+ somethingSelected: function() {return !posEq(this.view.sel.from, this.view.sel.to);},
+
+ setCursor: operation(null, function(line, ch, user) {
+ var pos = typeof line == "number" ? {line: line, ch: ch || 0} : line;
+ (user ? setSelectionUser : setSelection)(this, pos, pos);
+ }),
+
+ setSelection: operation(null, function(from, to, user) {
+ var doc = this.view.doc;
+ (user ? setSelectionUser : setSelection)(this, clipPos(doc, from), clipPos(doc, to || from));
+ }),
+
+ getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
+
+ getLineHandle: function(line) {
+ var doc = this.view.doc;
+ if (isLine(doc, line)) return getLine(doc, line);
+ },
+
+ getLineNumber: function(line) {return lineNo(line);},
+
+ setLine: operation(null, function(line, text) {
+ if (isLine(this.view.doc, line))
+ replaceRange(this, text, {line: line, ch: 0}, {line: line, ch: getLine(this.view.doc, line).text.length});
+ }),
+
+ removeLine: operation(null, function(line) {
+ if (isLine(this.view.doc, line))
+ replaceRange(this, "", {line: line, ch: 0}, clipPos(this.view.doc, {line: line+1, ch: 0}));
+ }),
+
+ replaceRange: operation(null, function(code, from, to) {
+ var doc = this.view.doc;
+ from = clipPos(doc, from);
+ to = to ? clipPos(doc, to) : from;
+ return replaceRange(this, code, from, to);
+ }),
+
+ getRange: function(from, to, lineSep) {
+ var doc = this.view.doc;
+ from = clipPos(doc, from); to = clipPos(doc, to);
+ var l1 = from.line, l2 = to.line;
+ if (l1 == l2) return getLine(doc, l1).text.slice(from.ch, to.ch);
+ var code = [getLine(doc, l1).text.slice(from.ch)];
+ doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
+ code.push(getLine(doc, l2).text.slice(0, to.ch));
+ return code.join(lineSep || "\n");
+ },
+
+ triggerOnKeyDown: operation(null, onKeyDown),
+
+ execCommand: function(cmd) {return commands[cmd](this);},
+
+ // Stuff used by commands, probably not much use to outside code.
+ moveH: operation(null, function(dir, unit) {
+ var sel = this.view.sel, pos = dir < 0 ? sel.from : sel.to;
+ if (sel.shift || posEq(sel.from, sel.to)) pos = findPosH(this, dir, unit, true);
+ setSelectionUser(this, pos, pos);
+ }),
+
+ deleteH: operation(null, function(dir, unit) {
+ var sel = this.view.sel;
+ if (!posEq(sel.from, sel.to)) replaceRange(this, "", sel.from, sel.to);
+ else replaceRange(this, "", sel.from, findPosH(this, dir, unit, false));
+ this.curOp.userSelChange = true;
+ }),
+
+ moveV: operation(null, function(dir, unit) {
+ var view = this.view, doc = view.doc, display = this.display;
+ var dist = 0, cur = selHead(view), pos = cursorCoords(this, cur, "div");
+ var x = pos.left, y;
+ if (view.goalColumn != null) x = view.goalColumn;
+ if (unit == "page") {
+ var pageSize = Math.min(display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
+ y = pos.top + dir * pageSize;
+ } else if (unit == "line") {
+ y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
+ }
+ do {
+ var target = coordsChar(this, x, y);
+ y += dir * 5;
+ } while (target.outside && (dir < 0 ? y > 0 : y < doc.height));
+
+ if (unit == "page") display.scrollbarV.scrollTop += charCoords(this, target, "div").top - pos.top;
+ setSelectionUser(this, target, target);
+ view.goalColumn = x;
+ }),
+
+ toggleOverwrite: function() {
+ if (this.view.overwrite = !this.view.overwrite)
+ this.display.cursor.className += " CodeMirror-overwrite";
+ else
+ this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
+ },
+
+ posFromIndex: function(off) {
+ var lineNo = 0, ch, doc = this.view.doc;
+ doc.iter(0, doc.size, function(line) {
+ var sz = line.text.length + 1;
+ if (sz > off) { ch = off; return true; }
+ off -= sz;
+ ++lineNo;
+ });
+ return clipPos(doc, {line: lineNo, ch: ch});
+ },
+ indexFromPos: function (coords) {
+ if (coords.line < 0 || coords.ch < 0) return 0;
+ var index = coords.ch;
+ this.view.doc.iter(0, coords.line, function (line) {
+ index += line.text.length + 1;
+ });
+ return index;
+ },
+
+ scrollTo: function(x, y) {
+ if (x != null) this.display.scrollbarH.scrollLeft = this.display.scroller.scrollLeft = x;
+ if (y != null) this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = y;
+ updateDisplay(this, []);
+ },
+ getScrollInfo: function() {
+ var scroller = this.display.scroller, co = scrollerCutOff;
+ return {left: scroller.scrollLeft, top: scroller.scrollTop,
+ height: scroller.scrollHeight - co, width: scroller.scrollWidth - co,
+ clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
+ },
+
+ setSize: function(width, height) {
+ function interpret(val) {
+ val = String(val);
+ return /^\d+$/.test(val) ? val + "px" : val;
+ }
+ if (width != null) this.display.wrapper.style.width = interpret(width);
+ if (height != null) this.display.wrapper.style.height = interpret(height);
+ this.refresh();
+ },
+
+ on: function(type, f) {on(this, type, f);},
+ off: function(type, f) {off(this, type, f);},
+
+ operation: function(f){return operation(this, f)();},
+ compoundChange: function(f){return compoundChange(this, f);},
+
+ refresh: function(){
+ updateDisplay(this, true, this.view.scrollTop);
+ if (this.display.scrollbarV.scrollHeight > this.view.scrollTop)
+ this.display.scrollbarV.scrollTop = this.view.scrollTop;
+ },
+
+ getInputField: function(){return this.display.input;},
+ getWrapperElement: function(){return this.display.wrapper;},
+ getScrollerElement: function(){return this.display.scroller;},
+ getGutterElement: function(){return this.display.gutters;}
+ };
+
+ // OPTION DEFAULTS
+
+ // The default configuration options.
+ var defaults = CodeMirror.defaults = {
+ value: "",
+ mode: null,
+ theme: "default",
+ indentUnit: 2,
+ indentWithTabs: false,
+ smartIndent: true,
+ tabSize: 4,
+ keyMap: "default",
+ extraKeys: null,
+ electricChars: true,
+ autoClearEmptyLines: false,
+ onKeyEvent: null,
+ onDragEvent: null,
+ lineWrapping: false,
+ lineNumbers: false,
+ gutters: [],
+ fixedGutter: false,
+ firstLineNumber: 1,
+ readOnly: false,
+ dragDrop: true,
+ cursorBlinkRate: 530,
+ cursorHeight: 1,
+ workTime: 100,
+ workDelay: 200,
+ flattenSpans: true,
+ pollInterval: 100,
+ undoDepth: 40,
+ viewportMargin: 10,
+ tabindex: null,
+ autofocus: null,
+ lineNumberFormatter: function(integer) { return integer; }
+ };
+
+ // MODE DEFINITION AND QUERYING
+
+ // Known modes, by name and by MIME
+ var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
+
+ CodeMirror.defineMode = function(name, mode) {
+ if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
+ if (arguments.length > 2) {
+ mode.dependencies = [];
+ for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
+ }
+ modes[name] = mode;
+ };
+
+ CodeMirror.defineMIME = function(mime, spec) {
+ mimeModes[mime] = spec;
+ };
+
+ CodeMirror.resolveMode = function(spec) {
+ if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
+ spec = mimeModes[spec];
+ else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec))
+ return CodeMirror.resolveMode("application/xml");
+ if (typeof spec == "string") return {name: spec};
+ else return spec || {name: "null"};
+ };
+
+ CodeMirror.getMode = function(options, spec) {
+ var spec = CodeMirror.resolveMode(spec);
+ var mfactory = modes[spec.name];
+ if (!mfactory) return CodeMirror.getMode(options, "text/plain");
+ var modeObj = mfactory(options, spec);
+ if (modeExtensions.hasOwnProperty(spec.name)) {
+ var exts = modeExtensions[spec.name];
+ for (var prop in exts) if (exts.hasOwnProperty(prop)) modeObj[prop] = exts[prop];
+ }
+ modeObj.name = spec.name;
+ return modeObj;
+ };
+
+ CodeMirror.defineMode("null", function() {
+ return {token: function(stream) {stream.skipToEnd();}};
+ });
+ CodeMirror.defineMIME("text/plain", "null");
+
+ var modeExtensions = CodeMirror.modeExtensions = {};
+ CodeMirror.extendMode = function(mode, properties) {
+ var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
+ for (var prop in properties) if (properties.hasOwnProperty(prop))
+ exts[prop] = properties[prop];
+ };
+
+ // EXTENSIONS
+
+ CodeMirror.defineExtension = function(name, func) {
+ CodeMirror.prototype[name] = func;
+ };
+ var optionHandlers = CodeMirror.optionHandlers = {};
+ CodeMirror.defineOption = function(name, deflt, handler) {
+ CodeMirror.defaults[name] = deflt;
+ optionHandlers[name] = handler;
+ };
+
+ var initHooks = [];
+ CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
+
+ // MODE STATE HANDLING
+
+ // Utility functions for working with state. Exported because modes
+ // sometimes need to do this.
+ function copyState(mode, state) {
+ if (state === true) return state;
+ if (mode.copyState) return mode.copyState(state);
+ var nstate = {};
+ for (var n in state) {
+ var val = state[n];
+ if (val instanceof Array) val = val.concat([]);
+ nstate[n] = val;
+ }
+ return nstate;
+ }
+ CodeMirror.copyState = copyState;
+
+ function startState(mode, a1, a2) {
+ return mode.startState ? mode.startState(a1, a2) : true;
+ }
+ CodeMirror.startState = startState;
+
+ CodeMirror.innerMode = function(mode, state) {
+ while (mode.innerMode) {
+ var info = mode.innerMode(state);
+ state = info.state;
+ mode = info.mode;
+ }
+ return info || {mode: mode, state: state};
+ };
+
+ // STANDARD COMMANDS
+
+ var commands = CodeMirror.commands = {
+ selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});},
+ killLine: function(cm) {
+ var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
+ if (!sel && cm.getLine(from.line).length == from.ch)
+ cm.replaceRange("", from, {line: from.line + 1, ch: 0});
+ else cm.replaceRange("", from, sel ? to : {line: from.line});
+ },
+ deleteLine: function(cm) {var l = cm.getCursor().line; cm.replaceRange("", {line: l, ch: 0}, {line: l});},
+ undo: function(cm) {cm.undo();},
+ redo: function(cm) {cm.redo();},
+ goDocStart: function(cm) {cm.setCursor(0, 0, true);},
+ goDocEnd: function(cm) {cm.setSelection({line: cm.lineCount() - 1}, null, true);},
+ goLineStart: function(cm) {
+ var line = cm.getCursor().line;
+ cm.setCursor(line, lineStart(cm.getLineHandle(line)), true);
+ },
+ goLineStartSmart: function(cm) {
+ var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), order = getOrder(line);
+ if (!order || order[0].level == 0) {
+ var firstNonWS = Math.max(0, line.text.search(/\S/));
+ cm.setCursor(cur.line, cur.ch <= firstNonWS && cur.ch ? 0 : firstNonWS, true);
+ } else cm.setCursor(cur.line, lineStart(line), true);
+ },
+ goLineEnd: function(cm) {
+ var line = cm.getCursor().line;
+ cm.setCursor(line, lineEnd(cm.getLineHandle(line)), true);
+ },
+ goLineUp: function(cm) {cm.moveV(-1, "line");},
+ goLineDown: function(cm) {cm.moveV(1, "line");},
+ goPageUp: function(cm) {cm.moveV(-1, "page");},
+ goPageDown: function(cm) {cm.moveV(1, "page");},
+ goCharLeft: function(cm) {cm.moveH(-1, "char");},
+ goCharRight: function(cm) {cm.moveH(1, "char");},
+ goColumnLeft: function(cm) {cm.moveH(-1, "column");},
+ goColumnRight: function(cm) {cm.moveH(1, "column");},
+ goWordLeft: function(cm) {cm.moveH(-1, "word");},
+ goWordRight: function(cm) {cm.moveH(1, "word");},
+ delCharBefore: function(cm) {cm.deleteH(-1, "char");},
+ delCharAfter: function(cm) {cm.deleteH(1, "char");},
+ delWordBefore: function(cm) {cm.deleteH(-1, "word");},
+ delWordAfter: function(cm) {cm.deleteH(1, "word");},
+ indentAuto: function(cm) {cm.indentSelection("smart");},
+ indentMore: function(cm) {cm.indentSelection("add");},
+ indentLess: function(cm) {cm.indentSelection("subtract");},
+ insertTab: function(cm) {cm.replaceSelection("\t", "end");},
+ defaultTab: function(cm) {
+ if (cm.somethingSelected()) cm.indentSelection("add");
+ else cm.replaceSelection("\t", "end");
+ },
+ transposeChars: function(cm) {
+ var cur = cm.getCursor(), line = cm.getLine(cur.line);
+ if (cur.ch > 0 && cur.ch < line.length - 1)
+ cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
+ {line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1});
+ },
+ newlineAndIndent: function(cm) {
+ cm.replaceSelection("\n", "end");
+ cm.indentLine(cm.getCursor().line);
+ },
+ toggleOverwrite: function(cm) {cm.toggleOverwrite();}
+ };
+
+ // STANDARD KEYMAPS
+
+ var keyMap = CodeMirror.keyMap = {};
+ keyMap.basic = {
+ "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
+ "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
+ "Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
+ "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
+ };
+ // Note that the save and find-related commands aren't defined by
+ // default. Unknown commands are simply ignored.
+ keyMap.pcDefault = {
+ "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
+ "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
+ "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
+ "Ctrl-Backspace": "delWordBefore", "Ctrl-Delete": "delWordAfter", "Ctrl-S": "save", "Ctrl-F": "find",
+ "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
+ "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
+ fallthrough: "basic"
+ };
+ keyMap.macDefault = {
+ "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
+ "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft",
+ "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordBefore",
+ "Ctrl-Alt-Backspace": "delWordAfter", "Alt-Delete": "delWordAfter", "Cmd-S": "save", "Cmd-F": "find",
+ "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
+ "Cmd-[": "indentLess", "Cmd-]": "indentMore",
+ fallthrough: ["basic", "emacsy"]
+ };
+ keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
+ keyMap.emacsy = {
+ "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
+ "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
+ "Ctrl-V": "goPageUp", "Shift-Ctrl-V": "goPageDown", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
+ "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
+ };
+
+ // KEYMAP DISPATCH
+
+ function getKeyMap(val) {
+ if (typeof val == "string") return keyMap[val];
+ else return val;
+ }
+
+ function lookupKey(name, extraMap, map, handle, stop) {
+ function lookup(map) {
+ map = getKeyMap(map);
+ var found = map[name];
+ if (found === false) {
+ if (stop) stop();
+ return true;
+ }
+ if (found != null && handle(found)) return true;
+ if (map.nofallthrough) {
+ if (stop) stop();
+ return true;
+ }
+ var fallthrough = map.fallthrough;
+ if (fallthrough == null) return false;
+ if (Object.prototype.toString.call(fallthrough) != "[object Array]")
+ return lookup(fallthrough);
+ for (var i = 0, e = fallthrough.length; i < e; ++i) {
+ if (lookup(fallthrough[i])) return true;
+ }
+ return false;
+ }
+ if (extraMap && lookup(extraMap)) return true;
+ return lookup(map);
+ }
+ function isModifierKey(event) {
+ var name = keyNames[e_prop(event, "keyCode")];
+ return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
+ }
+ CodeMirror.isModifierKey = isModifierKey;
+
+ // FROMTEXTAREA
+
+ CodeMirror.fromTextArea = function(textarea, options) {
+ if (!options) options = {};
+ options.value = textarea.value;
+ if (!options.tabindex && textarea.tabindex)
+ options.tabindex = textarea.tabindex;
+ // Set autofocus to true if this textarea is focused, or if it has
+ // autofocus and no other element is focused.
+ if (options.autofocus == null) {
+ var hasFocus = document.body;
+ // doc.activeElement occasionally throws on IE
+ try { hasFocus = document.activeElement; } catch(e) {}
+ options.autofocus = hasFocus == textarea ||
+ textarea.getAttribute("autofocus") != null && hasFocus == document.body;
+ }
+
+ function save() {textarea.value = cm.getValue();}
+ if (textarea.form) {
+ // Deplorable hack to make the submit method do the right thing.
+ on(textarea.form, "submit", save);
+ if (typeof textarea.form.submit == "function") {
+ var realSubmit = textarea.form.submit;
+ textarea.form.submit = function wrappedSubmit() {
+ save();
+ textarea.form.submit = realSubmit;
+ textarea.form.submit();
+ textarea.form.submit = wrappedSubmit;
+ };
+ }
+ }
+
+ textarea.style.display = "none";
+ var cm = CodeMirror(function(node) {
+ textarea.parentNode.insertBefore(node, textarea.nextSibling);
+ }, options);
+ cm.save = save;
+ cm.getTextArea = function() { return textarea; };
+ cm.toTextArea = function() {
+ save();
+ textarea.parentNode.removeChild(cm.getWrapperElement());
+ textarea.style.display = "";
+ if (textarea.form) {
+ off(textarea.form, "submit", save);
+ if (typeof textarea.form.submit == "function")
+ textarea.form.submit = realSubmit;
+ }
+ };
+ return cm;
+ };
+
+ // STRING STREAM
+
+ // Fed to the mode parsers, provides helper functions to make
+ // parsers more succinct.
+
+ // The character stream used by a mode's parser.
+ function StringStream(string, tabSize) {
+ this.pos = this.start = 0;
+ this.string = string;
+ this.tabSize = tabSize || 8;
+ }
+
+ StringStream.prototype = {
+ eol: function() {return this.pos >= this.string.length;},
+ sol: function() {return this.pos == 0;},
+ peek: function() {return this.string.charAt(this.pos) || undefined;},
+ next: function() {
+ if (this.pos < this.string.length)
+ return this.string.charAt(this.pos++);
+ },
+ eat: function(match) {
+ var ch = this.string.charAt(this.pos);
+ if (typeof match == "string") var ok = ch == match;
+ else var ok = ch && (match.test ? match.test(ch) : match(ch));
+ if (ok) {++this.pos; return ch;}
+ },
+ eatWhile: function(match) {
+ var start = this.pos;
+ while (this.eat(match)){}
+ return this.pos > start;
+ },
+ eatSpace: function() {
+ var start = this.pos;
+ while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
+ return this.pos > start;
+ },
+ skipToEnd: function() {this.pos = this.string.length;},
+ skipTo: function(ch) {
+ var found = this.string.indexOf(ch, this.pos);
+ if (found > -1) {this.pos = found; return true;}
+ },
+ backUp: function(n) {this.pos -= n;},
+ column: function() {return countColumn(this.string, this.start, this.tabSize);},
+ indentation: function() {return countColumn(this.string, null, this.tabSize);},
+ match: function(pattern, consume, caseInsensitive) {
+ if (typeof pattern == "string") {
+ var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
+ if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
+ if (consume !== false) this.pos += pattern.length;
+ return true;
+ }
+ } else {
+ var match = this.string.slice(this.pos).match(pattern);
+ if (match && match.index > 0) return null;
+ if (match && consume !== false) this.pos += match[0].length;
+ return match;
+ }
+ },
+ current: function(){return this.string.slice(this.start, this.pos);}
+ };
+ CodeMirror.StringStream = StringStream;
+
+ // TEXTMARKERS
+
+ function TextMarker(cm, type, style) {
+ this.lines = [];
+ this.type = type;
+ this.cm = cm;
+ if (style) this.style = style;
+ }
+
+ TextMarker.prototype.clear = function() {
+ startOperation(this.cm);
+ var min = null, max = null;
+ for (var i = 0; i < this.lines.length; ++i) {
+ var line = this.lines[i];
+ var span = getMarkedSpanFor(line.markedSpans, this);
+ if (span.from != null) min = lineNo(line);
+ if (span.to != null) max = lineNo(line);
+ line.markedSpans = removeMarkedSpan(line.markedSpans, span);
+ }
+ if (min != null) regChange(this.cm, min, max + 1);
+ this.lines.length = 0;
+ this.explicitlyCleared = true;
+ endOperation(this.cm);
+ };
+
+ TextMarker.prototype.find = function() {
+ var from, to;
+ for (var i = 0; i < this.lines.length; ++i) {
+ var line = this.lines[i];
+ var span = getMarkedSpanFor(line.markedSpans, this);
+ if (span.from != null || span.to != null) {
+ var found = lineNo(line);
+ if (span.from != null) from = {line: found, ch: span.from};
+ if (span.to != null) to = {line: found, ch: span.to};
+ }
+ }
+ if (this.type == "bookmark") return from;
+ return from && {from: from, to: to};
+ };
+
+ // TEXTMARKER SPANS
+
+ function getMarkedSpanFor(spans, marker) {
+ if (spans) for (var i = 0; i < spans.length; ++i) {
+ var span = spans[i];
+ if (span.marker == marker) return span;
+ }
+ }
+ function removeMarkedSpan(spans, span) {
+ for (var r, i = 0; i < spans.length; ++i)
+ if (spans[i] != span) (r || (r = [])).push(spans[i]);
+ return r;
+ }
+
+ function markedSpansBefore(old, startCh, endCh) {
+ if (old) for (var i = 0, nw; i < old.length; ++i) {
+ var span = old[i], marker = span.marker;
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
+ if (startsBefore || marker.type == "bookmark" && span.from == startCh && span.from != endCh) {
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
+ (nw || (nw = [])).push({from: span.from,
+ to: endsAfter ? null : span.to,
+ marker: marker});
+ }
+ }
+ return nw;
+ }
+
+ function markedSpansAfter(old, endCh) {
+ if (old) for (var i = 0, nw; i < old.length; ++i) {
+ var span = old[i], marker = span.marker;
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
+ if (endsAfter || marker.type == "bookmark" && span.from == endCh) {
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
+ (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
+ to: span.to == null ? null : span.to - endCh,
+ marker: marker});
+ }
+ }
+ return nw;
+ }
+
+ function updateMarkedSpans(oldFirst, oldLast, startCh, endCh, newText) {
+ if (!oldFirst && !oldLast) return newText;
+ // Get the spans that 'stick out' on both sides
+ var first = markedSpansBefore(oldFirst, startCh);
+ var last = markedSpansAfter(oldLast, endCh);
+
+ // Next, merge those two ends
+ var sameLine = newText.length == 1, offset = lst(newText).length + (sameLine ? startCh : 0);
+ if (first) {
+ // Fix up .to properties of first
+ for (var i = 0; i < first.length; ++i) {
+ var span = first[i];
+ if (span.to == null) {
+ var found = getMarkedSpanFor(last, span.marker);
+ if (!found) span.to = startCh;
+ else if (sameLine) span.to = found.to == null ? null : found.to + offset;
+ }
+ }
+ }
+ if (last) {
+ // Fix up .from in last (or move them into first in case of sameLine)
+ for (var i = 0; i < last.length; ++i) {
+ var span = last[i];
+ if (span.to != null) span.to += offset;
+ if (span.from == null) {
+ var found = getMarkedSpanFor(first, span.marker);
+ if (!found) {
+ span.from = offset;
+ if (sameLine) (first || (first = [])).push(span);
+ }
+ } else {
+ span.from += offset;
+ if (sameLine) (first || (first = [])).push(span);
+ }
+ }
+ }
+
+ var newMarkers = [newHL(newText[0], first)];
+ if (!sameLine) {
+ // Fill gap with whole-line-spans
+ var gap = newText.length - 2, gapMarkers;
+ if (gap > 0 && first)
+ for (var i = 0; i < first.length; ++i)
+ if (first[i].to == null)
+ (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
+ for (var i = 0; i < gap; ++i)
+ newMarkers.push(newHL(newText[i+1], gapMarkers));
+ newMarkers.push(newHL(lst(newText), last));
+ }
+ return newMarkers;
+ }
+
+ // hl stands for history-line, a data structure that can be either a
+ // string (line without markers) or a {text, markedSpans} object.
+ function hlText(val) { return typeof val == "string" ? val : val.text; }
+ function hlSpans(val) {
+ if (typeof val == "string") return null;
+ var spans = val.markedSpans, out = null;
+ for (var i = 0; i < spans.length; ++i) {
+ if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
+ else if (out) out.push(spans[i]);
+ }
+ return !out ? spans : out.length ? out : null;
+ }
+ function newHL(text, spans) { return spans ? {text: text, markedSpans: spans} : text; }
+
+ function detachMarkedSpans(line) {
+ var spans = line.markedSpans;
+ if (!spans) return;
+ for (var i = 0; i < spans.length; ++i) {
+ var lines = spans[i].marker.lines;
+ var ix = indexOf(lines, line);
+ lines.splice(ix, 1);
+ }
+ line.markedSpans = null;
+ }
+
+ function attachMarkedSpans(line, spans) {
+ if (!spans) return;
+ for (var i = 0; i < spans.length; ++i)
+ var marker = spans[i].marker.lines.push(line);
+ line.markedSpans = spans;
+ }
+
+ // LINE DATA STRUCTURE
+
+ // Line objects. These hold state related to a line, including
+ // highlighting info (the styles array).
+ function makeLine(text, markedSpans, height) {
+ var line = {text: text, height: height};
+ attachMarkedSpans(line, markedSpans);
+ return line;
+ }
+
+ function updateLine(cm, line, text, markedSpans) {
+ line.text = text;
+ line.stateAfter = line.styles = null;
+ if (line.order != null) line.order = null;
+ detachMarkedSpans(line);
+ attachMarkedSpans(line, markedSpans);
+ signalLater(cm, line, "change");
+ }
+
+ function cleanUpLine(line) {
+ line.parent = null;
+ detachMarkedSpans(line);
+ }
+
+ // Run the given mode's parser over a line, update the styles
+ // array, which contains alternating fragments of text and CSS
+ // classes.
+ function highlightLine(cm, line, state) {
+ var mode = cm.view.mode, flattenSpans = cm.options.flattenSpans;
+ var changed = !line.styles, pos = 0, curText = "", curStyle = null;
+ var stream = new StringStream(line.text, cm.options.tabSize), st = line.styles || (line.styles = []);
+ if (line.text == "" && mode.blankLine) mode.blankLine(state);
+ while (!stream.eol()) {
+ var style = mode.token(stream, state), substr = stream.current();
+ stream.start = stream.pos;
+ if (!flattenSpans || curStyle != style) {
+ if (curText) {
+ changed = changed || pos >= st.length || curText != st[pos] || curStyle != st[pos+1];
+ st[pos++] = curText; st[pos++] = curStyle;
+ }
+ curText = substr; curStyle = style;
+ } else curText = curText + substr;
+ // Give up when line is ridiculously long
+ if (stream.pos > 5000) break;
+ }
+ if (curText) {
+ changed = changed || pos >= st.length || curText != st[pos] || curStyle != st[pos+1];
+ st[pos++] = curText; st[pos++] = curStyle;
+ }
+ if (stream.pos > 5000) st[pos++] = line.text.slice(stream.pos); st[pos++] = null;
+ if (pos != st.length) { st.length = pos; changed = true; }
+ return changed;
+ }
+
+ // Lightweight form of highlight -- proceed over this line and
+ // update state, but don't save a style array.
+ function processLine(cm, line, state) {
+ var mode = cm.view.mode;
+ var stream = new StringStream(line.text, cm.options.tabSize);
+ if (line.text == "" && mode.blankLine) mode.blankLine(state);
+ while (!stream.eol() && stream.pos <= 5000) {
+ mode.token(stream, state);
+ stream.start = stream.pos;
+ }
+ }
+
+ // Fetch the parser token for a given character. Useful for hacks
+ // that want to inspect the mode state (say, for completion).
+ function getTokenAt(cm, line, state, ch) {
+ var mode = cm.view.mode;
+ var txt = line.text, stream = new StringStream(txt, cm.options.tabSize);
+ while (stream.pos < ch && !stream.eol()) {
+ stream.start = stream.pos;
+ var style = mode.token(stream, state);
+ }
+ return {start: stream.start,
+ end: stream.pos,
+ string: stream.current(),
+ className: style || null,
+ state: state};
+ }
+
+ var styleToClassCache = {};
+ function styleToClass(style) {
+ if (!style) return null;
+ return styleToClassCache[style] ||
+ (styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-"));
+ }
+
+ // Produces an HTML fragment for the line, taking selection,
+ // marking, and highlighting into account.
+ function buildLineContent(line, tabSize, wrapAt, compensateForWrapping) {
+ var first = true, col = 0, specials = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g;
+ var pre = elt("pre");
+ if (line.className) pre.className = line.className;
+ function span_(text, style) {
+ if (!text) return;
+ // Work around a bug where, in some compat modes, IE ignores leading spaces
+ if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1);
+ first = false;
+ if (!specials.test(text)) {
+ col += text.length;
+ var content = document.createTextNode(text);
+ } else {
+ var content = document.createDocumentFragment(), pos = 0;
+ while (true) {
+ specials.lastIndex = pos;
+ var m = specials.exec(text);
+ var skipped = m ? m.index - pos : text.length - pos;
+ if (skipped) {
+ content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
+ col += skipped;
+ }
+ if (!m) break;
+ pos += skipped + 1;
+ if (m[0] == "\t") {
+ var tabWidth = tabSize - col % tabSize;
+ content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
+ col += tabWidth;
+ } else {
+ var token = elt("span", "\u2022", "cm-invalidchar");
+ token.title = "\\u" + m[0].charCodeAt(0).toString(16);
+ content.appendChild(token);
+ col += 1;
+ }
+ }
+ }
+ if (style != null) return pre.appendChild(elt("span", [content], style));
+ else return pre.appendChild(content);
+ }
+ var span = span_;
+ if (wrapAt != null) {
+ var outPos = 0;
+ span = function(text, style) {
+ var l = text.length;
+ if (wrapAt >= outPos && wrapAt < outPos + l) {
+ var cut = wrapAt - outPos;
+ if (wrapAt > outPos) {
+ span_(text.slice(0, cut), style);
+ // See comment at the definition of spanAffectsWrapping
+ if (compensateForWrapping && spanAffectsWrapping.test(text.slice(cut - 1, cut + 1)))
+ pre.appendChild(elt("wbr"));
+ }
+ if (cut + 1 == l) {
+ pre.anchor = span_(text.slice(cut), style || "");
+ wrapAt--;
+ } else {
+ var end = cut + 1;
+ while (isExtendingChar.test(text.charAt(end))) ++end;
+ pre.anchor = span_(text.slice(cut, end), style || "");
+ if (compensateForWrapping && spanAffectsWrapping.test(text.slice(cut, end + 1)))
+ pre.appendChild(elt("wbr"));
+ span_(text.slice(end), style);
+ }
+ outPos += l;
+ } else {
+ outPos += l;
+ span_(text, style);
+ }
+ };
+ }
+
+ var st = line.styles, allText = line.text, marked = line.markedSpans;
+ var len = allText.length;
+ if (!allText) {
+ span("\u00a0");
+ } else if (!marked || !marked.length) {
+ for (var i = 0, ch = 0; ch < len; i+=2) {
+ var str = st[i], style = st[i+1], l = str.length;
+ if (ch + l > len) str = str.slice(0, len - ch);
+ ch += l;
+ span(str, styleToClass(style));
+ }
+ } else {
+ marked.sort(function(a, b) { return a.from - b.from; });
+ var pos = 0, i = 0, text = "", style, sg = 0;
+ var nextChange = marked[0].from || 0, marks = [], markpos = 0;
+ var advanceMarks = function() {
+ var m;
+ while (markpos < marked.length &&
+ ((m = marked[markpos]).from == pos || m.from == null)) {
+ if (m.marker.type == "range") marks.push(m);
+ ++markpos;
+ }
+ nextChange = markpos < marked.length ? marked[markpos].from : Infinity;
+ for (var i = 0; i < marks.length; ++i) {
+ var to = marks[i].to;
+ if (to == null) to = Infinity;
+ if (to == pos) marks.splice(i--, 1);
+ else nextChange = Math.min(to, nextChange);
+ }
+ };
+ var m = 0;
+ while (pos < len) {
+ if (nextChange == pos) advanceMarks();
+ var upto = Math.min(len, nextChange);
+ while (true) {
+ if (text) {
+ var end = pos + text.length;
+ var appliedStyle = style;
+ for (var j = 0; j < marks.length; ++j) {
+ var mark = marks[j];
+ appliedStyle = (appliedStyle ? appliedStyle + " " : "") + mark.marker.style;
+ if (mark.marker.endStyle && mark.to === Math.min(end, upto)) appliedStyle += " " + mark.marker.endStyle;
+ if (mark.marker.startStyle && mark.from === pos) appliedStyle += " " + mark.marker.startStyle;
+ }
+ span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
+ if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
+ pos = end;
+ }
+ text = st[i++]; style = styleToClass(st[i++]);
+ }
+ }
+ }
+ return pre;
+ }
+
+ // DOCUMENT DATA STRUCTURE
+
+ function LeafChunk(lines) {
+ this.lines = lines;
+ this.parent = null;
+ for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
+ lines[i].parent = this;
+ height += lines[i].height;
+ }
+ this.height = height;
+ }
+
+ LeafChunk.prototype = {
+ chunkSize: function() { return this.lines.length; },
+ remove: function(at, n, cm) {
+ for (var i = at, e = at + n; i < e; ++i) {
+ var line = this.lines[i];
+ this.height -= line.height;
+ cleanUpLine(line);
+ signalLater(cm, line, "delete");
+ }
+ this.lines.splice(at, n);
+ },
+ collapse: function(lines) {
+ lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
+ },
+ insertHeight: function(at, lines, height) {
+ this.height += height;
+ this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
+ for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
+ },
+ iterN: function(at, n, op) {
+ for (var e = at + n; at < e; ++at)
+ if (op(this.lines[at])) return true;
+ }
+ };
+
+ function BranchChunk(children) {
+ this.children = children;
+ var size = 0, height = 0;
+ for (var i = 0, e = children.length; i < e; ++i) {
+ var ch = children[i];
+ size += ch.chunkSize(); height += ch.height;
+ ch.parent = this;
+ }
+ this.size = size;
+ this.height = height;
+ this.parent = null;
+ }
+
+ BranchChunk.prototype = {
+ chunkSize: function() { return this.size; },
+ remove: function(at, n, callbacks) {
+ this.size -= n;
+ for (var i = 0; i < this.children.length; ++i) {
+ var child = this.children[i], sz = child.chunkSize();
+ if (at < sz) {
+ var rm = Math.min(n, sz - at), oldHeight = child.height;
+ child.remove(at, rm, callbacks);
+ this.height -= oldHeight - child.height;
+ if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
+ if ((n -= rm) == 0) break;
+ at = 0;
+ } else at -= sz;
+ }
+ if (this.size - n < 25) {
+ var lines = [];
+ this.collapse(lines);
+ this.children = [new LeafChunk(lines)];
+ this.children[0].parent = this;
+ }
+ },
+ collapse: function(lines) {
+ for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
+ },
+ insert: function(at, lines) {
+ var height = 0;
+ for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
+ this.insertHeight(at, lines, height);
+ },
+ insertHeight: function(at, lines, height) {
+ this.size += lines.length;
+ this.height += height;
+ for (var i = 0, e = this.children.length; i < e; ++i) {
+ var child = this.children[i], sz = child.chunkSize();
+ if (at <= sz) {
+ child.insertHeight(at, lines, height);
+ if (child.lines && child.lines.length > 50) {
+ while (child.lines.length > 50) {
+ var spilled = child.lines.splice(child.lines.length - 25, 25);
+ var newleaf = new LeafChunk(spilled);
+ child.height -= newleaf.height;
+ this.children.splice(i + 1, 0, newleaf);
+ newleaf.parent = this;
+ }
+ this.maybeSpill();
+ }
+ break;
+ }
+ at -= sz;
+ }
+ },
+ maybeSpill: function() {
+ if (this.children.length <= 10) return;
+ var me = this;
+ do {
+ var spilled = me.children.splice(me.children.length - 5, 5);
+ var sibling = new BranchChunk(spilled);
+ if (!me.parent) { // Become the parent node
+ var copy = new BranchChunk(me.children);
+ copy.parent = me;
+ me.children = [copy, sibling];
+ me = copy;
+ } else {
+ me.size -= sibling.size;
+ me.height -= sibling.height;
+ var myIndex = indexOf(me.parent.children, me);
+ me.parent.children.splice(myIndex + 1, 0, sibling);
+ }
+ sibling.parent = me.parent;
+ } while (me.children.length > 10);
+ me.parent.maybeSpill();
+ },
+ iter: function(from, to, op) { this.iterN(from, to - from, op); },
+ iterN: function(at, n, op) {
+ for (var i = 0, e = this.children.length; i < e; ++i) {
+ var child = this.children[i], sz = child.chunkSize();
+ if (at < sz) {
+ var used = Math.min(n, sz - at);
+ if (child.iterN(at, used, op)) return true;
+ if ((n -= used) == 0) break;
+ at = 0;
+ } else at -= sz;
+ }
+ }
+ };
+
+ // LINE UTILITIES
+
+ function lineDoc(line) {
+ for (var d = line.parent; d && d.parent; d = d.parent) {}
+ return d;
+ }
+
+ function getLine(chunk, n) {
+ while (!chunk.lines) {
+ for (var i = 0;; ++i) {
+ var child = chunk.children[i], sz = child.chunkSize();
+ if (n < sz) { chunk = child; break; }
+ n -= sz;
+ }
+ }
+ return chunk.lines[n];
+ }
+
+ function updateLineHeight(line, height) {
+ var diff = height - line.height;
+ for (var n = line; n; n = n.parent) n.height += diff;
+ }
+
+ function lineNo(line) {
+ if (line.parent == null) return null;
+ var cur = line.parent, no = indexOf(cur.lines, line);
+ for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
+ for (var i = 0, e = chunk.children.length; ; ++i) {
+ if (chunk.children[i] == cur) break;
+ no += chunk.children[i].chunkSize();
+ }
+ }
+ return no;
+ }
+
+ function lineAtHeight(chunk, h) {
+ var n = 0;
+ outer: do {
+ for (var i = 0, e = chunk.children.length; i < e; ++i) {
+ var child = chunk.children[i], ch = child.height;
+ if (h < ch) { chunk = child; continue outer; }
+ h -= ch;
+ n += child.chunkSize();
+ }
+ return n;
+ } while (!chunk.lines);
+ for (var i = 0, e = chunk.lines.length; i < e; ++i) {
+ var line = chunk.lines[i], lh = line.height;
+ if (h < lh) break;
+ h -= lh;
+ }
+ return n + i;
+ }
+
+ function heightAtLine(chunk, n) {
+ var h = 0;
+ outer: do {
+ for (var i = 0, e = chunk.children.length; i < e; ++i) {
+ var child = chunk.children[i], sz = child.chunkSize();
+ if (n < sz) { chunk = child; continue outer; }
+ n -= sz;
+ h += child.height;
+ }
+ return h;
+ } while (!chunk.lines);
+ for (var i = 0; i < n; ++i) h += chunk.lines[i].height;
+ return h;
+ }
+
+ function getOrder(line) {
+ var order = line.order;
+ if (order == null) order = line.order = bidiOrdering(line.text);
+ return order;
+ }
+
+ function lineContent(cm, line, anchorAt) {
+ if (!line.styles) {
+ highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
+ }
+ return buildLineContent(line, cm.options.tabSize, anchorAt, cm.options.lineWrapping);
+ }
+
+ // HISTORY
+
+ // The history object 'chunks' changes that are made close together
+ // and at almost the same time into bigger undoable units.
+ function makeHistory() {
+ return {time: 0, done: [], undone: [],
+ compound: 0, closed: false};
+ }
+
+ function addChange(history, start, added, old) {
+ history.undone.length = 0;
+ var time = +new Date, cur = lst(history.done), last = cur && lst(cur);
+ var dtime = time - history.time;
+
+ if (cur && !history.closed && history.compound) {
+ cur.push({start: start, added: added, old: old});
+ } else if (dtime > 400 || !last || history.closed ||
+ last.start > start + old.length || last.start + last.added < start) {
+ history.done.push([{start: start, added: added, old: old}]);
+ history.closed = false;
+ } else {
+ var startBefore = Math.max(0, last.start - start),
+ endAfter = Math.max(0, (start + old.length) - (last.start + last.added));
+ for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]);
+ for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]);
+ if (startBefore) last.start = start;
+ last.added += added - (old.length - startBefore - endAfter);
+ }
+ history.time = time;
+ }
+
+ function compoundChange(cm, f) {
+ var hist = cm.view.history;
+ if (!hist.compound++) hist.closed = true;
+ try { return f(); }
+ finally { if (!--hist.compound) hist.closed = true; }
+ }
+
+ // EVENT OPERATORS
+
+ function stopMethod() {e_stop(this);}
+ // Ensure an event has a stop method.
+ function addStop(event) {
+ if (!event.stop) event.stop = stopMethod;
+ return event;
+ }
+
+ function e_preventDefault(e) {
+ if (e.preventDefault) e.preventDefault();
+ else e.returnValue = false;
+ }
+ function e_stopPropagation(e) {
+ if (e.stopPropagation) e.stopPropagation();
+ else e.cancelBubble = true;
+ }
+ function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
+ CodeMirror.e_stop = e_stop;
+ CodeMirror.e_preventDefault = e_preventDefault;
+ CodeMirror.e_stopPropagation = e_stopPropagation;
+
+ function e_target(e) {return e.target || e.srcElement;}
+ function e_button(e) {
+ var b = e.which;
+ if (b == null) {
+ if (e.button & 1) b = 1;
+ else if (e.button & 2) b = 3;
+ else if (e.button & 4) b = 2;
+ }
+ if (mac && e.ctrlKey && b == 1) b = 3;
+ return b;
+ }
+
+ // Allow 3rd-party code to override event properties by adding an override
+ // object to an event object.
+ function e_prop(e, prop) {
+ var overridden = e.override && e.override.hasOwnProperty(prop);
+ return overridden ? e.override[prop] : e[prop];
+ }
+
+ // EVENT HANDLING
+
+ function on(emitter, type, f) {
+ if (emitter.addEventListener)
+ emitter.addEventListener(type, f, false);
+ else if (emitter.attachEvent)
+ emitter.attachEvent("on" + type, f);
+ else {
+ var map = emitter._handlers || (emitter._handlers = {});
+ var arr = map[type] || (map[type] = []);
+ arr.push(f);
+ }
+ }
+
+ function off(emitter, type, f) {
+ if (emitter.removeEventListener)
+ emitter.removeEventListener(type, f, false);
+ else if (emitter.detachEvent)
+ emitter.detachEvent("on" + type, f);
+ else {
+ var arr = emitter._handlers && emitter._handlers[type];
+ if (!arr) return;
+ for (var i = 0; i < arr.length; ++i)
+ if (arr[i] == f) { arr.splice(i, 1); break; }
+ }
+ }
+
+ function signal(emitter, type /*, values...*/) {
+ var arr = emitter._handlers && emitter._handlers[type];
+ if (!arr) return;
+ var args = Array.prototype.slice.call(arguments, 2);
+ for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
+ }
+
+ function signalLater(cm, emitter, type /*, values...*/) {
+ var arr = emitter._handlers && emitter._handlers[type];
+ if (!arr) return;
+ var args = Array.prototype.slice.call(arguments, 3), flist = cm.curOp && cm.curOp.delayedCallbacks;
+ function bnd(f) {return function(){f.apply(null, args);};};
+ for (var i = 0; i < arr.length; ++i)
+ if (flist) flist.push(bnd(arr[i]));
+ else arr[i].apply(null, args);
+ }
+
+ function hasHandler(emitter, type) {
+ var arr = emitter._handlers && emitter._handlers[type];
+ return arr && arr.length > 0;
+ }
+
+ CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
+
+ // MISC UTILITIES
+
+ // Number of pixels added to scroller and sizer to hide scrollbar
+ var scrollerCutOff = 30;
+
+ // Returned or thrown by various protocols to signal 'I'm not
+ // handling this'.
+ var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
+
+ function Delayed() {this.id = null;}
+ Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
+
+ // Counts the column offset in a string, taking tabs into account.
+ // Used mostly to find indentation.
+ function countColumn(string, end, tabSize) {
+ if (end == null) {
+ end = string.search(/[^\s\u00a0]/);
+ if (end == -1) end = string.length;
+ }
+ for (var i = 0, n = 0; i < end; ++i) {
+ if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
+ else ++n;
+ }
+ return n;
+ }
+
+ var spaceStrs = [""];
+ function spaceStr(n) {
+ while (spaceStrs.length <= n)
+ spaceStrs.push(lst(spaceStrs) + " ");
+ return spaceStrs[n];
+ }
+
+ function lst(arr) { return arr[arr.length-1]; }
+
+ function selectInput(node) {
+ if (ios) { // Mobile Safari apparently has a bug where select() is broken.
+ node.selectionStart = 0;
+ node.selectionEnd = node.value.length;
+ } else node.select();
+ }
+
+ // Used to position the cursor after an undo/redo by finding the
+ // last edited character.
+ function editEnd(from, to) {
+ if (!to) return 0;
+ if (!from) return to.length;
+ for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j)
+ if (from.charAt(i) != to.charAt(j)) break;
+ return j + 1;
+ }
+
+ function indexOf(collection, elt) {
+ if (collection.indexOf) return collection.indexOf(elt);
+ for (var i = 0, e = collection.length; i < e; ++i)
+ if (collection[i] == elt) return i;
+ return -1;
+ }
+
+ function bind(f) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ return function(){return f.apply(null, args);};
+ }
+
+ function isWordChar(ch) {
+ return /\w/.test(ch) || ch.toUpperCase() != ch.toLowerCase();
+ }
+
+ function isEmpty(obj) {
+ var c = 0;
+ for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) ++c;
+ return !c;
+ }
+
+ var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F]/;
+
+ // DOM UTILITIES
+
+ function elt(tag, content, className, style) {
+ var e = document.createElement(tag);
+ if (className) e.className = className;
+ if (style) e.style.cssText = style;
+ if (typeof content == "string") setTextContent(e, content);
+ else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
+ return e;
+ }
+
+ function removeChildren(e) {
+ e.innerHTML = "";
+ return e;
+ }
+
+ function removeChildrenAndAdd(parent, e) {
+ return removeChildren(parent).appendChild(e);
+ }
+
+ function setTextContent(e, str) {
+ if (ie_lt9) {
+ e.innerHTML = "";
+ e.appendChild(document.createTextNode(str));
+ } else e.textContent = str;
+ }
+
+ // FEATURE DETECTION
+
+ // Detect drag-and-drop
+ var dragAndDrop = function() {
+ // There is *some* kind of drag-and-drop support in IE6-8, but I
+ // couldn't get it to work yet.
+ if (ie_lt9) return false;
+ var div = elt('div');
+ return "draggable" in div || "dragDrop" in div;
+ }();
+
+ // Feature-detect whether newlines in textareas are converted to \r\n
+ var lineSep = function () {
+ var te = elt("textarea");
+ te.value = "foo\nbar";
+ if (te.value.indexOf("\r") > -1) return "\r\n";
+ return "\n";
+ }();
+
+ // For a reason I have yet to figure out, some browsers disallow
+ // word wrapping between certain characters *only* if a new inline
+ // element is started between them. This makes it hard to reliably
+ // measure the position of things, since that requires inserting an
+ // extra span. This terribly fragile set of regexps matches the
+ // character combinations that suffer from this phenomenon on the
+ // various browsers.
+ var spanAffectsWrapping = /^$/; // Won't match any two-character string
+ if (gecko) spanAffectsWrapping = /$'/;
+ else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
+ else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/;
+
+ var knownScrollbarWidth;
+ function scrollbarWidth(measure) {
+ if (knownScrollbarWidth != null) return knownScrollbarWidth;
+ var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: scroll");
+ removeChildrenAndAdd(measure, test);
+ if (test.offsetWidth)
+ knownScrollbarWidth = test.offsetHeight - test.clientHeight;
+ return knownScrollbarWidth || 0;
+ }
+
+ // See if "".split is the broken IE version, if so, provide an
+ // alternative way to split lines.
+ var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
+ var pos = 0, result = [], l = string.length;
+ while (pos <= l) {
+ var nl = string.indexOf("\n", pos);
+ if (nl == -1) nl = string.length;
+ var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
+ var rt = line.indexOf("\r");
+ if (rt != -1) {
+ result.push(line.slice(0, rt));
+ pos += rt + 1;
+ } else {
+ result.push(line);
+ pos = nl + 1;
+ }
+ }
+ return result;
+ } : function(string){return string.split(/\r\n?|\n/);};
+ CodeMirror.splitLines = splitLines;
+
+ var hasSelection = window.getSelection ? function(te) {
+ try { return te.selectionStart != te.selectionEnd; }
+ catch(e) { return false; }
+ } : function(te) {
+ try {var range = te.ownerDocument.selection.createRange();}
+ catch(e) {}
+ if (!range || range.parentElement() != te) return false;
+ return range.compareEndPoints("StartToEnd", range) != 0;
+ };
+
+ var hasCopyEvent = (function() {
+ var e = elt("div");
+ if ("oncopy" in e) return true;
+ e.setAttribute("oncopy", "return;");
+ return typeof e.oncopy == 'function';
+ })();
+
+ // KEY NAMING
+
+ var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
+ 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
+ 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
+ 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
+ 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
+ 221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
+ 63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
+ CodeMirror.keyNames = keyNames;
+ (function() {
+ // Number keys
+ for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
+ // Alphabetic keys
+ for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
+ // Function keys
+ for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
+ })();
+
+ // BIDI HELPERS
+
+ function iterateBidiSections(order, from, to, f) {
+ if (!order) return f(from, to, "ltr");
+ for (var i = 0; i < order.length; ++i) {
+ var part = order[i];
+ if (part.from < to && part.to > from)
+ f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
+ }
+ }
+
+ function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
+ function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
+
+ function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
+ function lineRight(line) {
+ var order = getOrder(line);
+ if (!order) return line.text.length;
+ return bidiRight(lst(order));
+ }
+ function lineStart(line) {
+ var order = getOrder(line);
+ if (!order) return 0;
+ return order[0].level % 2 ? lineRight(line) : lineLeft(line);
+ }
+ function lineEnd(line) {
+ var order = getOrder(line);
+ if (!order) return line.text.length;
+ return order[0].level % 2 ? lineLeft(line) : lineRight(line);
+ }
+
+ // This is somewhat involved. It is needed in order to move
+ // 'visually' through bi-directional text -- i.e., pressing left
+ // should make the cursor go left, even when in RTL text. The
+ // tricky part is the 'jumps', where RTL and LTR text touch each
+ // other. This often requires the cursor offset to move more than
+ // one unit, in order to visually move one unit.
+ function moveVisually(line, start, dir, byUnit) {
+ var bidi = getOrder(line);
+ if (!bidi) return moveLogically(line, start, dir, byUnit);
+ var moveOneUnit = byUnit ? function(pos, dir) {
+ do pos += dir;
+ while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
+ return pos;
+ } : function(pos, dir) { return pos + dir; };
+ var linedir = bidi[0].level;
+ for (var i = 0; i < bidi.length; ++i) {
+ var part = bidi[i], sticky = part.level % 2 == linedir;
+ if ((part.from < start && part.to > start) ||
+ (sticky && (part.from == start || part.to == start))) break;
+ }
+ var target = moveOneUnit(start, part.level % 2 ? -dir : dir);
+
+ while (target != null) {
+ if (part.level % 2 == linedir) {
+ if (target < part.from || target > part.to) {
+ part = bidi[i += dir];
+ target = part && (dir > 0 == part.level % 2 ? moveOneUnit(part.to, -1) : moveOneUnit(part.from, 1));
+ } else break;
+ } else {
+ if (target == bidiLeft(part)) {
+ part = bidi[--i];
+ target = part && bidiRight(part);
+ } else if (target == bidiRight(part)) {
+ part = bidi[++i];
+ target = part && bidiLeft(part);
+ } else break;
+ }
+ }
+
+ return target < 0 || target > line.text.length ? null : target;
+ }
+
+ function moveLogically(line, start, dir, byUnit) {
+ var target = start + dir;
+ if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(target))) target += dir;
+ return target < 0 || target > line.text.length ? null : target;
+ }
+
+ // Bidirectional ordering algorithm
+ // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
+ // that this (partially) implements.
+
+ // One-char codes used for character types:
+ // L (L): Left-to-Right
+ // R (R): Right-to-Left
+ // r (AL): Right-to-Left Arabic
+ // 1 (EN): European Number
+ // + (ES): European Number Separator
+ // % (ET): European Number Terminator
+ // n (AN): Arabic Number
+ // , (CS): Common Number Separator
+ // m (NSM): Non-Spacing Mark
+ // b (BN): Boundary Neutral
+ // s (B): Paragraph Separator
+ // t (S): Segment Separator
+ // w (WS): Whitespace
+ // N (ON): Other Neutrals
+
+ // Returns null if characters are ordered as they appear
+ // (left-to-right), or an array of sections ({from, to, level}
+ // objects) in the order in which they occur visually.
+ var bidiOrdering = (function() {
+ // Character types for codepoints 0 to 0xff
+ var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL";
+ // Character types for codepoints 0x600 to 0x6ff
+ var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr";
+ function charType(code) {
+ var type = "L";
+ if (code <= 0xff) return lowTypes.charAt(code);
+ else if (0x590 <= code && code <= 0x5f4) return "R";
+ else if (0x600 <= code && code <= 0x6ff) return arabicTypes.charAt(code - 0x600);
+ else if (0x700 <= code && code <= 0x8ac) return "r";
+ else return "L";
+ }
+
+ var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
+ var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
+
+ return function charOrdering(str) {
+ if (!bidiRE.test(str)) return false;
+ var len = str.length, types = new Array(len), startType = null;
+ for (var i = 0; i < len; ++i) {
+ var type = types[i] = charType(str.charCodeAt(i));
+ if (startType == null) {
+ if (type == "L") startType = "L";
+ else if (type == "R" || type == "r") startType = "R";
+ }
+ }
+ if (startType == null) startType = "L";
+
+ // W1. Examine each non-spacing mark (NSM) in the level run, and
+ // change the type of the NSM to the type of the previous
+ // character. If the NSM is at the start of the level run, it will
+ // get the type of sor.
+ for (var i = 0, prev = startType; i < len; ++i) {
+ var type = types[i];
+ if (type == "m") types[i] = prev;
+ else prev = type;
+ }
+
+ // W2. Search backwards from each instance of a European number
+ // until the first strong type (R, L, AL, or sor) is found. If an
+ // AL is found, change the type of the European number to Arabic
+ // number.
+ // W3. Change all ALs to R.
+ for (var i = 0, cur = startType; i < len; ++i) {
+ var type = types[i];
+ if (type == "1" && cur == "r") types[i] = "n";
+ else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
+ }
+
+ // W4. A single European separator between two European numbers
+ // changes to a European number. A single common separator between
+ // two numbers of the same type changes to that type.
+ for (var i = 1, prev = types[0]; i < len - 1; ++i) {
+ var type = types[i];
+ if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
+ else if (type == "," && prev == types[i+1] &&
+ (prev == "1" || prev == "n")) types[i] = prev;
+ prev = type;
+ }
+
+ // W5. A sequence of European terminators adjacent to European
+ // numbers changes to all European numbers.
+ // W6. Otherwise, separators and terminators change to Other
+ // Neutral.
+ for (var i = 0; i < len; ++i) {
+ var type = types[i];
+ if (type == ",") types[i] = "N";
+ else if (type == "%") {
+ for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
+ var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N";
+ for (var j = i; j < end; ++j) types[j] = replace;
+ i = end - 1;
+ }
+ }
+
+ // W7. Search backwards from each instance of a European number
+ // until the first strong type (R, L, or sor) is found. If an L is
+ // found, then change the type of the European number to L.
+ for (var i = 0, cur = startType; i < len; ++i) {
+ var type = types[i];
+ if (cur == "L" && type == "1") types[i] = "L";
+ else if (isStrong.test(type)) cur = type;
+ }
+
+ // N1. A sequence of neutrals takes the direction of the
+ // surrounding strong text if the text on both sides has the same
+ // direction. European and Arabic numbers act as if they were R in
+ // terms of their influence on neutrals. Start-of-level-run (sor)
+ // and end-of-level-run (eor) are used at level run boundaries.
+ // N2. Any remaining neutrals take the embedding direction.
+ for (var i = 0; i < len; ++i) {
+ if (isNeutral.test(types[i])) {
+ for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
+ var before = (i ? types[i-1] : startType) == "L";
+ var after = (end < len - 1 ? types[end] : startType) == "L";
+ var replace = before || after ? "L" : "R";
+ for (var j = i; j < end; ++j) types[j] = replace;
+ i = end - 1;
+ }
+ }
+
+ // Here we depart from the documented algorithm, in order to avoid
+ // building up an actual levels array. Since there are only three
+ // levels (0, 1, 2) in an implementation that doesn't take
+ // explicit embedding into account, we can build up the order on
+ // the fly, without following the level-based algorithm.
+ var order = [], m;
+ for (var i = 0; i < len;) {
+ if (countsAsLeft.test(types[i])) {
+ var start = i;
+ for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
+ order.push({from: start, to: i, level: 0});
+ } else {
+ var pos = i, at = order.length;
+ for (++i; i < len && types[i] != "L"; ++i) {}
+ for (var j = pos; j < i;) {
+ if (countsAsNum.test(types[j])) {
+ if (pos < j) order.splice(at, 0, {from: pos, to: j, level: 1});
+ var nstart = j;
+ for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
+ order.splice(at, 0, {from: nstart, to: j, level: 2});
+ pos = j;
+ } else ++j;
+ }
+ if (pos < i) order.splice(at, 0, {from: pos, to: i, level: 1});
+ }
+ }
+ if (order[0].level == 1 && (m = str.match(/^\s+/))) {
+ order[0].from = m[0].length;
+ order.unshift({from: 0, to: m[0].length, level: 0});
+ }
+ if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
+ lst(order).to -= m[0].length;
+ order.push({from: len - m[0].length, to: len, level: 0});
+ }
+ if (order[0].level != lst(order).level)
+ order.push({from: len, to: len, level: order[0].level});
+
+ return order;
+ };
+ })();
+
+ // THE END
+
+ CodeMirror.version = "3.0 B2";
+
+ return CodeMirror;
+})();
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/lib/util/matchbrackets.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/lib/util/matchbrackets.js
new file mode 100644
index 0000000..04d892d
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/lib/util/matchbrackets.js
@@ -0,0 +1,61 @@
+(function() {
+ var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
+ function findMatchingBracket(cm) {
+ var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
+ var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
+ if (!match) return null;
+ var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1;
+ var style = cm.getTokenAt({line: cur.line, ch: pos + 1}).className;
+
+ var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
+ function scan(line, lineNo, start) {
+ if (!line.text) return;
+ var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1;
+ if (start != null) pos = start + d;
+ for (; pos != end; pos += d) {
+ var ch = line.text.charAt(pos);
+ if (re.test(ch) && cm.getTokenAt({line: lineNo, ch: pos + 1}).className == style) {
+ var match = matching[ch];
+ if (match.charAt(1) == ">" == forward) stack.push(ch);
+ else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
+ else if (!stack.length) return {pos: pos, match: true};
+ }
+ }
+ }
+ for (var i = cur.line, found, e = forward ? Math.min(i + 100, cm.lineCount()) : Math.max(-1, i - 100); i != e; i+=d) {
+ if (i == cur.line) found = scan(line, i, pos);
+ else found = scan(cm.getLineHandle(i), i);
+ if (found) break;
+ }
+ return {from: {line: cur.line, ch: pos}, to: found && {line: i, ch: found.pos}, match: found && found.match};
+ }
+
+ function matchBrackets(cm, autoclear) {
+ var found = findMatchingBracket(cm);
+ if (!found) return;
+ var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
+ var one = cm.markText(found.from, {line: found.from.line, ch: found.from.ch + 1}, style);
+ var two = found.to && cm.markText(found.to, {line: found.to.line, ch: found.to.ch + 1}, style);
+ var clear = function() {
+ cm.operation(function() { one.clear(); two && two.clear(); });
+ };
+ if (autoclear) setTimeout(clear, 800);
+ else return clear;
+ }
+
+ var currentlyHighlighted = null;
+ function doMatchBrackets(cm) {
+ cm.operation(function() {
+ if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
+ if (!cm.somethingSelected()) currentlyHighlighted = matchBrackets(cm, false);
+ });
+ }
+
+ CodeMirror.defineOption("matchBrackets", false, function(cm, val) {
+ if (val) cm.on("cursorActivity", doMatchBrackets);
+ else cm.off("cursorActivity", doMatchBrackets);
+ });
+
+ CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
+ CodeMirror.defineExtension("findMatchingBracket", function(){return findMatchingBracket(this);});
+})();
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/lib/util/searchcursor.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/lib/util/searchcursor.js
new file mode 100644
index 0000000..1750aad
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/lib/util/searchcursor.js
@@ -0,0 +1,119 @@
+(function(){
+ function SearchCursor(cm, query, pos, caseFold) {
+ this.atOccurrence = false; this.cm = cm;
+ if (caseFold == null && typeof query == "string") caseFold = false;
+
+ pos = pos ? cm.clipPos(pos) : {line: 0, ch: 0};
+ this.pos = {from: pos, to: pos};
+
+ // The matches method is filled in based on the type of query.
+ // It takes a position and a direction, and returns an object
+ // describing the next occurrence of the query, or null if no
+ // more matches were found.
+ if (typeof query != "string") { // Regexp match
+ if (!query.global) query = new RegExp(query.source, query.ignoreCase ? "ig" : "g");
+ this.matches = function(reverse, pos) {
+ if (reverse) {
+ query.lastIndex = 0;
+ var line = cm.getLine(pos.line).slice(0, pos.ch), match = query.exec(line), start = 0;
+ while (match) {
+ start += match.index + 1;
+ line = line.slice(start);
+ query.lastIndex = 0;
+ var newmatch = query.exec(line);
+ if (newmatch) match = newmatch;
+ else break;
+ }
+ start--;
+ } else {
+ query.lastIndex = pos.ch;
+ var line = cm.getLine(pos.line), match = query.exec(line),
+ start = match && match.index;
+ }
+ if (match)
+ return {from: {line: pos.line, ch: start},
+ to: {line: pos.line, ch: start + match[0].length},
+ match: match};
+ };
+ } else { // String query
+ if (caseFold) query = query.toLowerCase();
+ var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
+ var target = query.split("\n");
+ // Different methods for single-line and multi-line queries
+ if (target.length == 1)
+ this.matches = function(reverse, pos) {
+ var line = fold(cm.getLine(pos.line)), len = query.length, match;
+ if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
+ : (match = line.indexOf(query, pos.ch)) != -1)
+ return {from: {line: pos.line, ch: match},
+ to: {line: pos.line, ch: match + len}};
+ };
+ else
+ this.matches = function(reverse, pos) {
+ var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(cm.getLine(ln));
+ var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
+ if (reverse ? offsetA >= pos.ch || offsetA != match.length
+ : offsetA <= pos.ch || offsetA != line.length - match.length)
+ return;
+ for (;;) {
+ if (reverse ? !ln : ln == cm.lineCount() - 1) return;
+ line = fold(cm.getLine(ln += reverse ? -1 : 1));
+ match = target[reverse ? --idx : ++idx];
+ if (idx > 0 && idx < target.length - 1) {
+ if (line != match) return;
+ else continue;
+ }
+ var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
+ if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
+ return;
+ var start = {line: pos.line, ch: offsetA}, end = {line: ln, ch: offsetB};
+ return {from: reverse ? end : start, to: reverse ? start : end};
+ }
+ };
+ }
+ }
+
+ SearchCursor.prototype = {
+ findNext: function() {return this.find(false);},
+ findPrevious: function() {return this.find(true);},
+
+ find: function(reverse) {
+ var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to);
+ function savePosAndFail(line) {
+ var pos = {line: line, ch: 0};
+ self.pos = {from: pos, to: pos};
+ self.atOccurrence = false;
+ return false;
+ }
+
+ for (;;) {
+ if (this.pos = this.matches(reverse, pos)) {
+ this.atOccurrence = true;
+ return this.pos.match || true;
+ }
+ if (reverse) {
+ if (!pos.line) return savePosAndFail(0);
+ pos = {line: pos.line-1, ch: this.cm.getLine(pos.line-1).length};
+ }
+ else {
+ var maxLine = this.cm.lineCount();
+ if (pos.line == maxLine - 1) return savePosAndFail(maxLine);
+ pos = {line: pos.line+1, ch: 0};
+ }
+ }
+ },
+
+ from: function() {if (this.atOccurrence) return this.pos.from;},
+ to: function() {if (this.atOccurrence) return this.pos.to;},
+
+ replace: function(newText) {
+ var self = this;
+ if (this.atOccurrence)
+ self.pos.to = this.cm.replaceRange(newText, self.pos.from, self.pos.to);
+ }
+ };
+
+ CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
+ return new SearchCursor(this, query, pos, caseFold);
+ });
+})();
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/mode/sieve/LICENSE b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/mode/sieve/LICENSE
new file mode 100644
index 0000000..24e4c94
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/mode/sieve/LICENSE
@@ -0,0 +1,23 @@
+Copyright (C) 2012 Thomas Schmid <schmid-thomas@gmx.net>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+Please note that some subdirectories of the CodeMirror distribution
+include their own LICENSE files, and are released under different
+licences.
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/mode/sieve/sieve.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/mode/sieve/sieve.js
new file mode 100644
index 0000000..34ad0d5
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/mode/sieve/sieve.js
@@ -0,0 +1,187 @@
+/*
+ * The content of this file is licenced. You may obtain a copy of the
+ * license at http://sieve.mozdev.org or request it via email from the author.
+ *
+ * Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ *
+ * Contributor(s):
+ *
+ */
+
+CodeMirror.defineMode("sieve", function(config) {
+
+ function words(aWords) {
+ var obj = {};
+ for (var i = 0; i < aWords.length; ++i)
+ obj[aWords[i]] = true;
+ return obj;
+ }
+
+ var keywords = words(["if","elsif","else","stop","require"]);
+ var atoms = words(["true","false","not"]);
+ var indentUnit = config.indentUnit;
+
+ function tokenBase(stream, state) {
+
+ var ch = stream.next();
+
+ switch (ch) {
+ case "/" :
+ if (stream.eat("*") == false)
+ break;
+
+ state.tokenize = tokenCComment;
+ return tokenCComment(stream, state);
+
+ case "#" :
+ stream.skipToEnd();
+ return "comment";
+
+ case "\"" :
+ state.tokenize = tokenString(ch);
+ return state.tokenize(stream, state);
+
+ case "(":
+ state._indent.push("(");
+ case "{" :
+ state._indent.push("{");
+ return null;
+
+ case ")" :
+ state._indent.pop();
+ case "}" :
+ state._indent.pop();
+ return null;
+
+ case "," :
+ case ";" :
+ return null;
+
+ // ":" (ALPHA / "_") *(ALPHA / DIGIT / "_")
+ case ":" :
+ stream.eatWhile(/[a-zA-Z_]/);
+ stream.eatWhile(/[a-zA-Z0-9_]/);
+
+ return "operator";
+
+ }
+
+ // 1*DIGIT "K" / "M" / "G"
+ if (/\d/.test(ch)) {
+ stream.eatWhile(/[\d]/);
+ stream.eat(/[KkMmGg]/)
+ return "number";
+ }
+
+ stream.eatWhile(/\w/);
+ //stream.eatWhile(/[\w\$_]/);
+
+ var cur = stream.current();
+
+ // "text:" *(SP / HTAB) (hash-comment / CRLF)
+ // *(multiline-literal / multiline-dotstart)
+ // "." CRLF
+ if ((cur == "text") && stream.eat(":"))
+ {
+ state.tokenize = tokenMultiLineString;
+ return "string";
+ }
+
+ if (keywords.propertyIsEnumerable(cur))
+ return "keyword";
+
+ if (atoms.propertyIsEnumerable(cur))
+ return "atom";
+
+ return null;
+ }
+
+ function tokenMultiLineString(stream, state)
+ {
+ state._multiLineString = true;
+ // the first line is special it may contain a comment
+ if (!stream.sol()) {
+ stream.eatSpace();
+
+ if (stream.peek() == "#") {
+ stream.skipToEnd();
+ return "comment";
+ }
+
+ stream.skipToEnd();
+ return "string";
+ }
+
+ if ((stream.next() == ".") && (stream.eol()))
+ {
+ state._multiLineString = false;
+ state.tokenize = tokenBase;
+ }
+
+ return "string";
+ }
+
+ function tokenCComment(stream, state) {
+ var maybeEnd = false, ch;
+ while ((ch = stream.next()) != null) {
+ if (maybeEnd && ch == "/") {
+ state.tokenize = tokenBase;
+ break;
+ }
+ maybeEnd = (ch == "*");
+ }
+ return "comment";
+ }
+
+ function tokenString(quote) {
+ return function(stream, state) {
+ var escaped = false, ch;
+ while ((ch = stream.next()) != null) {
+ if (ch == quote && !escaped)
+ break;
+ escaped = !escaped && ch == "\\";
+ }
+ if (!escaped) state.tokenize = tokenBase;
+ return "string";
+ };
+ }
+
+
+ return {
+ startState: function(base) {
+
+ return {tokenize: tokenBase,
+ baseIndent: base || 0,
+ _indent: []
+ };
+ },
+
+ token: function(stream, state) {
+
+ if (stream.eatSpace())
+ return null;
+
+ return (state.tokenize || tokenBase)(stream, state)
+
+ },
+
+ indent: function(state, textAfter)
+ {
+ var length = state._indent.length;
+ if (textAfter && (textAfter[0] == "}"))
+ length--;
+
+ if (length <0)
+ length = 0;
+
+ return length * indentUnit;
+ },
+
+ electricChars: "}"
+ };
+});
+
+CodeMirror.defineMIME("application/sieve", "sieve");
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/package.json b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/package.json
new file mode 100644
index 0000000..ea18e6b
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "codemirror",
+ "version":"3.0.2",
+ "main": "codemirror.js",
+ "description": "In-browser code editing made bearable",
+ "licenses": [{"type": "MIT",
+ "url": "http://codemirror.net/LICENSE"}],
+ "directories": {"lib": "./lib"},
+ "scripts": {"test": "node ./test/run.js"},
+ "devDependencies": {"node-static": "0.6.0"},
+ "bugs": "http://github.com/marijnh/CodeMirror/issues",
+ "keywords": ["JavaScript", "CodeMirror", "Editor"],
+ "homepage": "http://codemirror.net",
+ "maintainers":[{"name": "Marijn Haverbeke",
+ "email": "marijnh@gmail.com",
+ "web": "http://marijnhaverbeke.nl"}],
+ "repositories": [{"type": "git",
+ "url": "http://marijnhaverbeke.nl/git/codemirror"},
+ {"type": "git",
+ "url": "https://github.com/marijnh/CodeMirror.git"}]
+}
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/theme/eclipse.css b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/theme/eclipse.css
new file mode 100644
index 0000000..47d66a0
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/CodeMirror/theme/eclipse.css
@@ -0,0 +1,25 @@
+.cm-s-eclipse span.cm-meta {color: #FF1717;}
+.cm-s-eclipse span.cm-keyword { line-height: 1em; font-weight: bold; color: #7F0055; }
+.cm-s-eclipse span.cm-atom {color: #219;}
+.cm-s-eclipse span.cm-number {color: #164;}
+.cm-s-eclipse span.cm-def {color: #00f;}
+.cm-s-eclipse span.cm-variable {color: black;}
+.cm-s-eclipse span.cm-variable-2 {color: #0000C0;}
+.cm-s-eclipse span.cm-variable-3 {color: #0000C0;}
+.cm-s-eclipse span.cm-property {color: black;}
+.cm-s-eclipse span.cm-operator {color: black;}
+.cm-s-eclipse span.cm-comment {color: #3F7F5F;}
+.cm-s-eclipse span.cm-string {color: #2A00FF;}
+.cm-s-eclipse span.cm-string-2 {color: #f50;}
+.cm-s-eclipse span.cm-error {color: #f00;}
+.cm-s-eclipse span.cm-qualifier {color: #555;}
+.cm-s-eclipse span.cm-builtin {color: #30a;}
+.cm-s-eclipse span.cm-bracket {color: #cc7;}
+.cm-s-eclipse span.cm-tag {color: #170;}
+.cm-s-eclipse span.cm-attribute {color: #00c;}
+.cm-s-eclipse span.cm-link {color: #219;}
+
+.cm-s-eclipse .CodeMirror-matchingbracket {
+ border:1px solid grey;
+ color:black !important;;
+}
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libManageSieve/SieveAbstractClient.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libManageSieve/SieveAbstractClient.js
new file mode 100644
index 0000000..61be620
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libManageSieve/SieveAbstractClient.js
@@ -0,0 +1,485 @@
+/*
+ *
+ * The contents of this file is licenced. You may obtain a copy of
+ * the license at http://sieve.mozdev.org or request it via email
+ * from the author. Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ *
+ */
+
+
+// Enable Strict Mode
+"use strict";
+
+Components.utils.import("chrome://sieve/content/modules/sieve/SieveConnectionManager.js");
+Components.utils.import("chrome://sieve/content/modules/sieve/SieveRequest.js");
+
+
+//TODO merge and or rename into SieveChannel
+
+function SieveAbstractClient()
+{
+ this._sid = null;
+ this._cid = null;
+}
+
+// TODO muss der error listener wirklich jedes mal gesetzet werden...
+// eigentlich müssete der default doch beim Objekt rauskommen...
+
+//-- Sieve Related Events
+SieveAbstractClient.prototype.onListScriptResponse
+ = function(response)
+{
+ throw "implement onListScriptResponse";
+}
+
+SieveAbstractClient.prototype.onSetActiveResponse
+ = function(response)
+{
+ throw "implement onSetActiveResponse";
+}
+
+SieveAbstractClient.prototype.onDeleteScriptResponse
+ = function(response)
+{
+ throw "implement onDeleteScriptResponse";
+}
+
+SieveAbstractClient.prototype.onGetScriptResponse
+ = function(response)
+{
+ throw "implement onGetScriptResponse";
+}
+
+SieveAbstractClient.prototype.onCheckScriptResponse
+ = function(response)
+{
+ throw "implement onCheckScriptResponse";
+}
+
+SieveAbstractClient.prototype.onOffline
+ = function()
+{
+ this.disconnect(6);
+}
+
+SieveAbstractClient.prototype.onTimeout
+ = function()
+{
+ // TODO implement a loggin facility
+ //gLogger.logStringMessage("SieveAbstractClient.js\nOnTimeout");
+ this.disconnect(1,"warning.timeout");
+}
+
+SieveAbstractClient.prototype.onError
+ = function(response)
+{
+ // TODO implement a loggin facility
+ //gLogger.logStringMessage("SivFilerExplorer.OnError: "+response.getMessage());
+ this.disconnect(4,response.getMessage());
+}
+
+SieveAbstractClient.prototype.onDisconnect
+ = function()
+{
+ var ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+
+ if (ioService.offline)
+ this.onOffline()
+
+ this.disconnect(9);
+}
+
+SieveAbstractClient.prototype.onChannelClosed
+ = function()
+{
+ throw "implement onChannelClosed";
+}
+
+SieveAbstractClient.prototype.onChannelCreated
+ = function(sieve)
+{
+ this.onChannelReady(this._cid);
+}
+
+SieveAbstractClient.prototype.onChannelReady
+ = function(cid)
+{
+ // We observe only our channel...
+ if (cid != this._cid)
+ return;
+
+ throw "implement onChannelReady";
+}
+
+SieveAbstractClient.prototype.onChannelStatus
+ = function(id,text)
+{
+ this.onStatusChange(id,text);
+}
+
+SieveAbstractClient.prototype.onStatusChange
+ = function (state, message)
+{
+ throw "implement onStatusChange"
+}
+
+SieveAbstractClient.prototype.onBadCert
+ = function(targetSite)
+{
+ this.disconnect(5,targetSite);
+}
+
+SieveAbstractClient.prototype.observe
+ = function(aSubject, aTopic, aData)
+ {
+ if (aTopic != "network:offline-status-changed")
+ return;
+
+ if (aData == "offline")
+ this.onOffline();
+
+ if (aData == "online")
+ this.connect();
+ }
+
+
+/******************************************************************************/
+
+ // TODO it should accept an strings instead of an account object
+SieveAbstractClient.prototype.connect
+ = function (account)
+{
+ var ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+
+ if (ioService.offline)
+ return this.onStatusChange(6);
+
+ this.onStatusChange(3,"progress.connecting");
+
+ // Ensure that Sieve Object is null...
+ var sivManager = SieveConnections;
+
+ this._sid = sivManager.createSession(account.getKey());
+ sivManager.addSessionListener(this._sid,this);
+
+ this._cid = sivManager.createChannel(this._sid);
+
+ sivManager.openChannel(this._sid,this._cid);
+
+ Cc["@mozilla.org/observer-service;1"]
+ .getService (Ci.nsIObserverService)
+ .addObserver(this,"network:offline-status-changed", false);
+}
+
+SieveAbstractClient.prototype.disconnect
+ = function (state,message)
+{
+ if (state)
+ this.onStatusChange(state,message);
+
+ if ((!this._sid) || (!this._cid))
+ return;
+
+ try {
+ var sivManager = SieveConnections;
+ sivManager.removeSessionListener(this._sid, this);
+ sivManager.closeChannel(this._sid,this._cid);
+ }
+ catch (ex)
+ {
+ Components.utils.reportError(ex);
+ }
+
+ try
+ {
+ Cc["@mozilla.org/observer-service;1"]
+ .getService (Ci.nsIObserverService)
+ .removeObserver(this,"network:offline-status-changed");
+ }
+ catch (ex) { }
+}
+
+
+SieveAbstractClient.prototype.deleteScript
+ = function (script)
+{
+ // delete the script...
+ var request = new SieveDeleteScriptRequest(script);
+ request.addDeleteScriptListener(this);
+ request.addErrorListener(this);
+
+ this.sendRequest(request);
+}
+
+SieveAbstractClient.prototype.setActiveScript
+ = function (script)
+{
+ var request = new SieveSetActiveRequest(script);
+ request.addSetActiveListener(this);
+ request.addErrorListener(this);
+
+ this.sendRequest(request);
+}
+
+SieveAbstractClient.prototype.checkScript
+ = function (script)
+{
+ var that = this;
+
+ var lEvent =
+ {
+ onPutScriptResponse: function(response)
+ {
+ // the script is syntactically correct. This means the server accepted...
+ // ... our temporary script. So we need to do some cleanup and remove...
+ // ... the script again.
+
+ // Call delete, without response handlers, we don't care if the ...
+ // ... command succeeds or fails.
+ that.sendRequest(new SieveDeleteScriptRequest("TMP_FILE_DELETE_ME"));
+
+ // Call CHECKSCRIPT's response handler to complete the hack...
+ that.onCheckScriptResponse(response);
+ },
+
+ onError: function(response)
+ {
+ that.onCheckScriptResponse(response);
+ }
+ }
+
+
+
+ if (script.length == 0)
+ return;
+
+ // Use the CHECKSCRIPT command when possible, otherwise we need to ...
+ // ... fallback to the PUTSCRIPT/DELETESCRIPT Hack...
+
+ var request = null;
+
+
+ var canCheck = SieveConnections.getChannel(this._sid,this._cid)
+ .getCompatibility().checkscript;
+
+ if (canCheck)
+ {
+ // ... we use can the CHECKSCRIPT command
+ request = new SieveCheckScriptRequest(script);
+ request.addCheckScriptListener(this);
+ }
+ else
+ {
+ // ... we have to use the PUTSCRIPT/DELETESCRIPT Hack...
+
+ // First we use PUTSCRIPT to store a temporary script on the server...
+ // ... incase the command fails, it is most likely due to an syntax error...
+ // ... if it sucseeds the script is syntactically correct!
+ request = new SievePutScriptRequest("TMP_FILE_DELETE_ME",script);
+ request.addPutScriptListener(lEvent);
+ }
+
+ request.addErrorListener(lEvent);
+
+ this.sendRequest(request);
+}
+
+SieveAbstractClient.prototype._renameScript2
+ = function (oldName, newName)
+{
+ var that = this;
+
+ var lEvent =
+ {
+ onRenameScriptResponse: function(response)
+ {
+ that.listScript();
+ },
+ onTimeout: function()
+ {
+ that.onTimeout();
+ },
+ onError: function(response)
+ {
+ //TODO Display notification instead of an popup box.
+ alert(response.getMessage());
+ }
+ }
+
+ var request = new SieveRenameScriptRequest(oldName, newName);
+ request.addRenameScriptListener(lEvent)
+ request.addErrorListener(lEvent);
+
+ this.sendRequest(request);
+}
+
+SieveAbstractClient.prototype._renameScript
+ = function (oldName, newName, isActive)
+{
+ var that = this;
+
+ var lEvent =
+ {
+ oldScriptName : null,
+ newScriptName : null,
+ isActive : null,
+
+ onGetScriptResponse: function(response)
+ {
+ var request = new SievePutScriptRequest(
+ new String(lEvent.newScriptName),
+ new String(response.getScriptBody()));
+
+ request.addPutScriptListener(lEvent)
+ request.addErrorListener(lEvent)
+
+ that.sendRequest(request);
+ },
+ onPutScriptResponse: function(response)
+ {
+
+ if (lEvent.isActive == true)
+ {
+ var request = new SieveSetActiveRequest(lEvent.newScriptName)
+
+ request.addSetActiveListener(lEvent);
+ request.addErrorListener(that);
+
+ that.sendRequest(request);
+ }
+ else
+ lEvent.onSetActiveResponse(null);
+ },
+ onSetActiveResponse: function(response)
+ {
+ // we redirect this request to event not lEvent!
+ // because event.onDeleteScript is doing exactly what we want!
+ var request = new SieveDeleteScriptRequest(lEvent.oldScriptName);
+ request.addDeleteScriptListener(that);
+ request.addErrorListener(that);
+
+ that.sendRequest(request);
+ },
+ onTimeout: function()
+ {
+ that.onTimeout();
+ },
+ onError: function(response)
+ {
+ //TODO Display notification instead of an popup box.
+ alert("Renaming\r\n"+response.getMessage());
+ }
+ }
+
+ lEvent.oldScriptName = oldName;
+ lEvent.newScriptName = newName;
+
+ lEvent.isActive = ((isActive && (isActive=="true"))?true:false);
+
+ // first get the script and redirect the event to a local event...
+ // ... in order to put it up under its new name an then finally delete it
+ var request = new SieveGetScriptRequest(lEvent.oldScriptName);
+
+ request.addGetScriptListener(lEvent);
+ request.addErrorListener(this);
+
+ this.sendRequest(request);
+
+}
+
+
+SieveAbstractClient.prototype.renameScript
+ = function (oldScriptName,newScriptName)
+{
+
+ var canRename = SieveConnections
+ .getChannel(this._sid,this._cid)
+ .getCompatibility().renamescript;
+
+ if (canRename)
+ {
+ this._renameScript2(oldScriptName, newScriptName);
+ return;
+ }
+
+ this._renameScript(oldScriptName, newScriptName);
+}
+
+SieveAbstractClient.prototype.listScript
+ = function ()
+{
+ var request = new SieveListScriptRequest();
+ request.addListScriptListener(this);
+ request.addErrorListener(this);
+
+ this.sendRequest(request);
+}
+
+SieveAbstractClient.prototype.getScript
+ = function (script)
+{
+ var request = new SieveGetScriptRequest(script);
+ request.addGetScriptListener(this);
+ request.addErrorListener(this);
+
+ this.sendRequest(request)
+}
+
+SieveAbstractClient.prototype.putScript
+ = function (script,content)
+{
+
+ var request = new SievePutScriptRequest(script,content);
+ request.addPutScriptListener(this);
+ request.addErrorListener(this);
+
+ this.sendRequest(request);
+}
+
+SieveAbstractClient.prototype.sendRequest
+ = function (request)
+{
+ // we do not send requests while in offline mode...
+ var ioService = Cc["@mozilla.org/network/io-service;1"]
+ .getService(Ci.nsIIOService);
+
+ if (ioService.offline)
+ {
+ this.disconnect(6);
+ return;
+ }
+
+ // ... we are not so let's try. If the channel was closed...
+ // ... getChannel will throw an exception.
+ try
+ {
+ SieveConnections
+ .getChannel(this._sid,this._cid)
+ .addRequest(request);
+ }
+ catch (e)
+ {
+ // most likely getChannel caused this exception, but anyway we should ...
+ // ... display error message. If we do not catch the exception a timeout ...
+ // ... would accure, so let's display the timeout message directly.
+ this.disconnect(1,"warning.timeout");
+ }
+}
+
+SieveAbstractClient.prototype.isActive
+ = function ()
+{
+ try {
+ SieveConnections
+ .getChannel(this._sid,this._cid)
+ }
+ catch (ex)
+ {
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5173/rfc5173.txt b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5173/rfc5173.txt
new file mode 100644
index 0000000..915724c
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5173/rfc5173.txt
@@ -0,0 +1,563 @@
+
+
+
+
+
+
+Network Working Group J. Degener
+Request for Comments: 5173 P. Guenther
+Updates: 5229 Sendmail, Inc.
+Category: Standards Track April 2008
+
+
+
+ Sieve Email Filtering: Body Extension
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Abstract
+
+ This document defines a new command for the "Sieve" email filtering
+ language that tests for the occurrence of one or more strings in the
+ body of an email message.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Degener & Guenther Standards Track [Page 1]
+
+RFC 5173 Sieve Email Filtering: Body Extension April 2008
+
+
+1. Introduction
+
+ The "body" test checks for the occurrence of one or more strings in
+ the body of an email message. Such a test was initially discussed
+ for the [SIEVE] base document, but was subsequently removed because
+ it was thought to be too costly to implement.
+
+ Nevertheless, several server vendors have implemented some form of
+ the "body" test.
+
+ This document reintroduces the "body" test as an extension, and
+ specifies its syntax and semantics.
+
+2. Conventions Used in This Document
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [KEYWORDS].
+
+ Conventions for notations are as in [SIEVE] Section 1.1, including
+ the use of the "Usage:" label for the definition of text and tagged
+ argument syntax.
+
+ The rules for interpreting the grammar are defined in [SIEVE] and
+ inherited by this specification. In particular, readers of this
+ document are reminded that according to [SIEVE] Sections 2.6.2 and
+ 2.6.3, optional arguments such as COMPARATOR and MATCH-TYPE can
+ appear in any order.
+
+3. Capability Identifier
+
+ The capability string associated with the extension defined in this
+ document is "body".
+
+4. Test body
+
+ Usage: "body" [COMPARATOR] [MATCH-TYPE] [BODY-TRANSFORM]
+ <key-list: string-list>
+
+ The body test matches content in the body of an email message, that
+ is, anything following the first empty line after the header. (The
+ empty line itself, if present, is not considered to be part of the
+ body.)
+
+ The COMPARATOR and MATCH-TYPE keyword parameters are defined in
+ [SIEVE]. As specified in Sections 2.7.1 and 2.7.3 of [SIEVE], the
+ default COMPARATOR is "i;ascii-casemap" and the default MATCH-TYPE is
+ ":is".
+
+
+
+Degener & Guenther Standards Track [Page 2]
+
+RFC 5173 Sieve Email Filtering: Body Extension April 2008
+
+
+ The BODY-TRANSFORM is a keyword parameter that governs how a set of
+ strings to be matched against are extracted from the body of the
+ message. If a message consists of a header only, not followed by an
+ empty line, then that set is empty and all "body" tests return false,
+ including those that test for an empty string. (This is similar to
+ how the "header" test always fails when the named header fields
+ aren't present.) Otherwise, the transform must be followed as
+ defined below in Section 5.
+
+ Note that the transformations defined here do *not* match against
+ each line of the message independently, so the strings will usually
+ contain CRLFs. How these can be matched is governed by the
+ comparator and match-type. For example, with the default comparator
+ of "i;ascii-casemap", they can be included literally in the key
+ strings, or be matched with the "*" or "?" wildcards of the :matches
+ match-type, or be skipped with :contains.
+
+5. Body Transform
+
+ Prior to matching content in a message body, "transformations" can be
+ applied that filter and decode certain parts of the body. These
+ transformations are selected by a "BODY-TRANSFORM" keyword parameter.
+
+ Usage: ":raw"
+ / ":content" <content-types: string-list>
+ / ":text"
+
+ The default transformation is :text.
+
+5.1. Body Transform ":raw"
+
+ The ":raw" transform matches against the entire undecoded body of a
+ message as a single item.
+
+ If the specified body-transform is ":raw", the [MIME] structure of
+ the body is irrelevant. The implementation MUST NOT remove any
+ transfer encoding from the message, MUST NOT refuse to filter
+ messages with syntactic errors (unless the environment it is part of
+ rejects them outright), and MUST treat multipart boundaries or the
+ MIME headers of enclosed body parts as part of the content being
+ matched against, instead of MIME structures to interpret.
+
+
+
+
+
+
+
+
+
+
+Degener & Guenther Standards Track [Page 3]
+
+RFC 5173 Sieve Email Filtering: Body Extension April 2008
+
+
+ Example:
+
+ require "body";
+
+ # This will match a message containing the literal text
+ # "MAKE MONEY FAST" in body parts (ignoring any
+ # content-transfer-encodings) or MIME headers other than
+ # the outermost RFC 2822 header.
+
+ if body :raw :contains "MAKE MONEY FAST" {
+ discard;
+ }
+
+5.2. Body Transform ":content"
+
+ If the body transform is ":content", the MIME parts that have the
+ specified content types are matched against independently.
+
+ If an individual content type begins or ends with a '/' (slash) or
+ contains multiple slashes, then it matches no content types.
+ Otherwise, if it contains a slash, then it specifies a full
+ <type>/<subtype> pair, and matches only that specific content type.
+ If it is the empty string, all MIME content types are matched.
+ Otherwise, it specifies a <type> only, and any subtype of that type
+ matches it.
+
+ The search for MIME parts matching the :content specification is
+ recursive and automatically descends into multipart and
+ message/rfc822 MIME parts. All MIME parts with matching types are
+ searched for the key strings. The test returns true if any
+ combination of a searched MIME part and key-list argument match.
+
+ If the :content specification matches a multipart MIME part, only the
+ prologue and epilogue sections of the part will be searched for the
+ key strings, treating the entire prologue and the entire epilogue as
+ separate strings; the contents of nested parts are only searched if
+ their respective types match the :content specification.
+
+ If the :content specification matches a message/rfc822 MIME part,
+ only the header of the nested message will be searched for the key
+ strings, treating the header as a single string; the contents of the
+ nested message body parts are only searched if their content type
+ matches the :content specification.
+
+ For other MIME types, the entire part will be searched as a single
+ string.
+
+
+
+
+
+Degener & Guenther Standards Track [Page 4]
+
+RFC 5173 Sieve Email Filtering: Body Extension April 2008
+
+
+ (Matches against container types with an empty match string can be
+ useful as tests for the existence of such parts.)
+
+ Example:
+
+ From: Whomever
+ To: Someone
+ Date: Whenever
+ Subject: whatever
+ Content-Type: multipart/mixed; boundary=outer
+
+ & This is a multi-part message in MIME format.
+ &
+ --outer
+ Content-Type: multipart/alternative; boundary=inner
+
+ & This is a nested multi-part message in MIME format.
+ &
+ --inner
+ Content-Type: text/plain; charset="us-ascii"
+
+ $ Hello
+ $
+ --inner
+ Content-Type: text/html; charset="us-ascii"
+
+ % <html><body>Hello</body></html>
+ %
+ --inner--
+ &
+ & This is the end of the inner MIME multipart.
+ &
+ --outer
+ Content-Type: message/rfc822
+
+ ! From: Someone Else
+ ! Subject: hello request
+
+ $ Please say Hello
+ $
+ --outer--
+ &
+ & This is the end of the outer MIME multipart.
+
+
+
+
+
+
+
+
+Degener & Guenther Standards Track [Page 5]
+
+RFC 5173 Sieve Email Filtering: Body Extension April 2008
+
+
+ In the above example, the '&', '$', '%', and '!' characters at the
+ start of a line are used to illustrate what portions of the example
+ message are used in tests:
+
+ - the lines starting with '&' are the ones that are tested when a
+ 'body :content "multipart" :contains "MIME"' test is executed.
+
+ - the lines starting with '$' are the ones that are tested when a
+ 'body :content "text/plain" :contains "Hello"' test is executed.
+
+ - the lines starting with '%' are the ones that are tested when a
+ 'body :content "text/html" :contains "Hello"' test is executed.
+
+ - the lines starting with '$' or '%' are the ones that are tested
+ when a 'body :content "text" :contains "Hello"' test is executed.
+
+ - the lines starting with '!' are the ones that are tested when a
+ 'body :content "message/rfc822" :contains "Hello"' test is
+ executed.
+
+ Comparisons are performed on octets. Implementations decode the
+ content-transfer-encoding and convert text to [UTF-8] as input to the
+ comparator. MIME parts that cannot be decoded and converted MAY be
+ treated as plain US-ASCII, omitted, or processed according to local
+ conventions. A NUL octet (character zero) SHOULD NOT cause early
+ termination of the content being compared against. Implementations
+ MUST support the "quoted-printable", "base64", "7bit", "8bit", and
+ "binary" content transfer encodings. Implementations MUST be capable
+ of converting to UTF-8 the US-ASCII, ISO-8859-1, and the US-ASCII
+ subset of ISO-8859-* character sets.
+
+ Each matched part is matched against independently: search
+ expressions MUST NOT match across MIME part boundaries. MIME headers
+ of the containing part MUST NOT be included in the data.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Degener & Guenther Standards Track [Page 6]
+
+RFC 5173 Sieve Email Filtering: Body Extension April 2008
+
+
+ Example:
+
+ require ["body", "fileinto"];
+
+ # Save any message with any text MIME part that contains the
+ # words "missile" or "coordinates" in the "secrets" folder.
+
+ if body :content "text" :contains ["missile", "coordinates"] {
+ fileinto "secrets";
+ }
+
+ # Save any message with an audio/mp3 MIME part in
+ # the "jukebox" folder.
+
+ if body :content "audio/mp3" :contains "" {
+ fileinto "jukebox";
+ }
+
+5.3. Body Transform ":text"
+
+ The ":text" body transform matches against the results of an
+ implementation's best effort at extracting UTF-8 encoded text from a
+ message.
+
+ It is unspecified whether this transformation results in a single
+ string or multiple strings being matched against. All the text
+ extracted from a given non-container MIME part MUST be in the same
+ string.
+
+ In simple implementations, :text MAY be treated the same as :content
+ "text".
+
+ Sophisticated implementations MAY strip mark-up from the text prior
+ to matching, and MAY convert media types other than text to text
+ prior to matching.
+
+ (For example, they may be able to convert proprietary text editor
+ formats to text or apply optical character recognition algorithms to
+ image data.)
+
+ Example:
+ require ["body", "fileinto"];
+
+ # Save messages mentioning the project schedule in the
+ # project/schedule folder.
+ if body :text :contains "project schedule" {
+ fileinto "project/schedule";
+ }
+
+
+
+Degener & Guenther Standards Track [Page 7]
+
+RFC 5173 Sieve Email Filtering: Body Extension April 2008
+
+
+6. Interaction with Other Sieve Extensions
+
+ Any extension that extends the grammar for the COMPARATOR or MATCH-
+ TYPE nonterminals will also affect the implementation of "body".
+
+ Wildcard expressions used with "body" are exempt from the side
+ effects described in [VARIABLES]. That is, they MUST NOT set match
+ variables (${1}, ${2}...) to the input values corresponding to
+ wildcard sequences in the matched pattern. However, if the extension
+ is present, variable references in the key strings or content type
+ strings are evaluated as described in this document.
+
+7. IANA Considerations
+
+ The following template specifies the IANA registration of the Sieve
+ extension specified in this document:
+
+ To: iana@iana.org
+ Subject: Registration of new Sieve extension
+
+ Capability name: body
+ Description: Provides a test for matching against the
+ body of the message being processed
+ RFC number: RFC 5173
+ Contact Address: The Sieve discussion list
+ <ietf-mta-filters@imc.org>
+
+8. Security Considerations
+
+ The system MUST be sized and restricted in such a manner that even
+ malicious use of body matching does not deny service to other users
+ of the host system.
+
+ Filters relying on string matches in the raw body of an email message
+ may be more general than intended. Text matches are no replacement
+ for a spam, virus, or other security related filtering system.
+
+9. Acknowledgments
+
+ This document has been revised in part based on comments and
+ discussions that took place on and off the SIEVE mailing list.
+ Thanks to Cyrus Daboo, Ned Freed, Bob Johannessen, Simon Josefsson,
+ Mark E. Mallett, Chris Markle, Alexey Melnikov, Ken Murchison, Greg
+ Shapiro, Tim Showalter, Nigel Swinson, Dowson Tong, and Christian
+ Vogt for reviews and suggestions.
+
+
+
+
+
+
+Degener & Guenther Standards Track [Page 8]
+
+RFC 5173 Sieve Email Filtering: Body Extension April 2008
+
+
+10. References
+
+10.1. Normative References
+
+ [KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [MIME] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
+ Extensions (MIME) Part One: Format of Internet Message
+ Bodies", RFC 2045, November 1996.
+
+ [SIEVE] Guenther, P., Ed., and T. Showalter, Ed., "Sieve: An
+ Email Filtering Language", RFC 5228, January 2008.
+
+ [UTF-8] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", STD 63, RFC 3629, November 2003.
+
+10.2. Informative References
+
+ [VARIABLES] Homme, K., "Sieve Email Filtering: Variables Extension",
+ RFC 5229, January 2008.
+
+Authors' Addresses
+
+ Jutta Degener
+ 5245 College Ave, Suite #127
+ Oakland, CA 94618
+
+ EMail: jutta@pobox.com
+
+
+ Philip Guenther
+ Sendmail, Inc.
+ 6425 Christie Ave, 4th Floor
+ Emeryville, CA 94608
+
+ EMail: guenther@sendmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Degener & Guenther Standards Track [Page 9]
+
+RFC 5173 Sieve Email Filtering: Body Extension April 2008
+
+
+Full Copyright Statement
+
+ Copyright (C) The IETF Trust (2008).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND
+ THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
+ THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+
+
+
+
+
+
+
+
+
+
+
+Degener & Guenther Standards Track [Page 10]
+
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveActions.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveActions.js
new file mode 100644
index 0000000..8f3b6d8
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveActions.js
@@ -0,0 +1,298 @@
+/*
+ * The contents of this file is licenced. You may obtain a copy of
+ * the license at http://sieve.mozdev.org or request it via email
+ * from the author. Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ */
+
+"use strict";
+
+function SieveDiscard(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+ this.semicolon = this._createByName("atom/semicolon");
+}
+
+SieveDiscard.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveDiscard.isElement = function (parser) {
+ return parser.startsWith("discard");
+}
+
+SieveDiscard.nodeName = function () {
+ return "action/discard";
+}
+
+SieveDiscard.nodeType = function () {
+ return "action";
+}
+
+SieveDiscard.prototype.init
+ = function (parser)
+{
+ // Syntax :
+ // <"discard"> <";">
+ parser.extract("discard");
+ this.semicolon.init(parser);
+
+ return this;
+}
+
+SieveDiscard.prototype.toScript
+ = function ()
+{
+ return "discard"
+ + this.semicolon.toScript();
+}
+
+//***************************************
+
+function SieveRedirect(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+
+ this.whiteSpace = this._createByName("whitespace"," ");
+
+ this.address = this._createByName("string","\"username@example.com\"");
+
+ this.semicolon = this._createByName("atom/semicolon");
+}
+
+SieveRedirect.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveRedirect.isElement
+ = function (parser)
+{
+ return parser.startsWith("redirect");
+}
+
+SieveRedirect.nodeName
+ = function ()
+{
+ return "action/redirect";
+}
+
+SieveRedirect.nodeType
+ = function ()
+{
+ return "action";
+}
+
+SieveRedirect.prototype.init
+ = function (parser)
+{
+ // Syntax :
+ // <"redirect"> <address: string> <";">
+
+ // remove the "redirect" identifier ...
+ parser.extract("redirect");
+
+ // ... eat the deadcode before the stringlist...
+ this.whiteSpace.init(parser);
+
+ // ... extract the redirect address...
+ this.address.init(parser);
+
+ // ... drop the semicolon
+ this.semicolon.init(parser);
+
+ return this;
+}
+
+SieveRedirect.prototype.setAddress
+ = function (address)
+{
+ this.address.setValue(address);
+}
+
+SieveRedirect.prototype.getAddress
+ = function()
+{
+ return this.address.getValue();
+}
+
+SieveRedirect.prototype.toScript
+ = function ()
+{
+ return "redirect"
+ + this.whiteSpace.toScript()
+ + this.address.toScript()
+ + this.semicolon.toScript();
+}
+
+/******************************************************************************/
+
+function SieveStop(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+ this.semicolon = this._createByName("atom/semicolon");
+}
+
+SieveStop.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveStop.isElement = function(parser) {
+ return parser.startsWith("stop");
+}
+
+SieveStop.nodeName = function () {
+ return "action/stop";
+}
+
+SieveStop.nodeType = function () {
+ return "action";
+}
+
+SieveStop.prototype.init
+ = function (parser)
+{
+ parser.extract("stop");
+
+ this.semicolon.init(parser);
+
+ return this;
+}
+
+SieveStop.prototype.toScript
+ = function ()
+{
+ return "stop"
+ + this.semicolon.toScript();
+}
+
+
+/******************************************************************************/
+
+function SieveKeep(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+ this.semicolon = this._createByName("atom/semicolon");
+}
+
+SieveKeep.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveKeep.isElement = function(parser) {
+ return parser.startsWith("keep");
+}
+
+SieveKeep.nodeName = function () {
+ return "action/keep";
+}
+
+SieveKeep.nodeType = function () {
+ return "action";
+}
+
+SieveKeep.prototype.init
+ = function (parser)
+{
+ parser.extract("keep");
+
+ this.semicolon.init(parser);
+
+ return parser;
+}
+
+SieveKeep.prototype.toScript
+ = function ()
+{
+ return "keep"
+ + this.semicolon.toScript();
+}
+
+
+/******************************************************************************/
+
+function SieveFileInto(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+
+ this.whiteSpace = this._createByName("whitespace", " ");
+
+ this.semicolon = this._createByName("atom/semicolon");
+
+ this.string = this._createByName("string");
+ this.string.init("\"INBOX\"");
+}
+
+SieveFileInto.prototype.__proto__ = SieveAbstractElement.prototype;
+
+// Static methods needed for registration
+SieveFileInto.isElement = function (parser) {
+ return parser.startsWith("fileinto");
+}
+
+SieveFileInto.isCapable = function (capabilities) {
+ return (capabilities["fileinto"] == true);
+}
+
+SieveFileInto.nodeName = function () {
+ return "action/fileinto";
+}
+
+SieveFileInto.nodeType = function () {
+ return "action";
+}
+
+// Dynamic methods...
+SieveFileInto.prototype.init
+ = function (parser)
+{
+ // Syntax :
+ // <"fileinto"> <string> <";">
+
+ parser.extract("fileinto");
+
+ // ... eat the deadcode before the string...
+ this.whiteSpace.init(parser);
+
+ // read the string
+ this.string.init(parser);
+
+ // ... and finally remove the semicolon;
+ this.semicolon.init(parser);
+
+ return this;
+}
+
+SieveFileInto.prototype.require
+ = function (requires)
+{
+ requires["fileinto"] = true;
+}
+
+SieveFileInto.prototype.setPath
+ = function (path)
+{
+ this.string.setValue(path)
+}
+
+SieveFileInto.prototype.getPath
+ = function ()
+{
+ return this.string.getValue();
+}
+
+SieveFileInto.prototype.toScript
+ = function ()
+{
+ return "fileinto"
+ + this.whiteSpace.toScript()
+ + this.string.toScript()
+ + this.semicolon.toScript();
+}
+
+
+/******************************************************************************/
+
+if (!SieveLexer)
+ throw "Could not register Actions";
+
+SieveLexer.register(SieveDiscard);
+SieveLexer.register(SieveKeep);
+SieveLexer.register(SieveStop);
+SieveLexer.register(SieveFileInto);
+SieveLexer.register(SieveRedirect);
+
+
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveBlocks.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveBlocks.js
new file mode 100644
index 0000000..c17a778
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveBlocks.js
@@ -0,0 +1,163 @@
+/*
+ * The contents of this file is licenced. You may obtain a copy of
+ * the license at http://sieve.mozdev.org or request it via email
+ * from the author. Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ */
+
+"use strict";
+
+function SieveBlock(docshell,id)
+{
+ SieveBlockBody.call(this,docshell,id);
+}
+
+SieveBlock.prototype.__proto__ = SieveBlockBody.prototype;
+
+SieveBlock.isElement
+ = function (parser)
+{
+ return parser.isChar("{");
+}
+
+SieveBlock.nodeName = function () {
+ return "block/block";
+}
+
+SieveBlock.nodeType = function () {
+ return "block/";
+}
+
+SieveBlock.prototype.init
+ = function (parser)
+{
+ parser.extractChar("{");
+
+ SieveBlockBody.prototype.init.call(this,parser);
+
+ parser.extractChar("}");
+
+ return this;
+}
+
+SieveBlock.prototype.toScript
+ = function ()
+{
+ return "{" +SieveBlockBody.prototype.toScript.call(this)+"}";
+}
+//****************************************************************************//
+
+function SieveBlockBody(docshell,id)
+{
+ SieveAbstractBlock.call(this,docshell,id);
+
+ // Initialize Block Elements
+ this.elms = [];
+}
+
+SieveBlockBody.prototype.__proto__ = SieveAbstractBlock.prototype;
+
+SieveBlockBody.isElement
+ = function (parser)
+{
+ return SieveLexer.probeByClass(["action","condition","whitespace"],parser);
+}
+
+SieveBlockBody.nodeName = function () {
+ return "block/body";
+}
+
+SieveBlockBody.nodeType = function () {
+ return "block/";
+}
+
+SieveBlockBody.prototype.init
+ = function (parser)
+{
+ while (this._probeByClass(["action","condition","whitespace"],parser))
+ this.elms.push(
+ this._createByClass(["action","condition","whitespace"],parser));
+
+ return this;
+}
+
+SieveBlockBody.prototype.toScript
+ = function ()
+{
+ var str ="";
+
+ for (var key in this.elms)
+ str += this.elms[key].toScript();
+
+ return str;
+}
+
+
+
+function SieveRootNode(docshell)
+{
+ SieveBlockBody.call(this,docshell,-1);
+
+ this.elms[0] = this._createByName("import");
+ this.elms[1] = this._createByName("block/body");
+}
+
+SieveRootNode.prototype.__proto__ = SieveBlockBody.prototype;
+
+SieveRootNode.isElement
+ = function (token)
+{
+ return false;
+}
+
+SieveRootNode.nodeName = function () {
+ return "block/rootnode";
+}
+
+SieveRootNode.nodeType = function () {
+ return "block/";
+}
+
+
+SieveRootNode.prototype.init
+ = function (parser)
+{
+ // requires are only valid if they are
+ // before any other sieve command!
+ if (this._probeByName("import",parser))
+ this.elms[0].init(parser);
+
+ // After the import section only deadcode and actions are valid
+ if (this._probeByName("block/body",parser))
+ this.elms[1].init(parser);
+
+ return this;
+}
+
+SieveRootNode.prototype.toScript
+ = function ()
+{
+ var requires = [];
+
+ // Step 1: collect requires
+ this.elms[1].require(requires);
+
+ // Step 2: Add require...
+ for (var item in requires)
+ this.elms[0].capability(item);
+
+ // TODO Remove unused requires...
+
+ // return the script
+ return SieveBlockBody.prototype.toScript.call(this);
+}
+
+
+if (!SieveLexer)
+ throw "Could not register Block Elements";
+
+SieveLexer.register(SieveBlockBody);
+SieveLexer.register(SieveBlock);
+SieveLexer.register(SieveRootNode); \ No newline at end of file
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveConditions.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveConditions.js
new file mode 100644
index 0000000..747aeb3
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveConditions.js
@@ -0,0 +1,278 @@
+/*
+ * The contents of this file is licenced. You may obtain a copy of
+ * the license at http://sieve.mozdev.org or request it via email
+ * from the author. Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ */
+
+"use strict";
+
+function SieveIf(docshell,id)
+{
+ SieveElse.call(this,docshell,id);
+ this._test = null;
+
+ this.ws[2] = this._createByName("whitespace");
+}
+
+SieveIf.prototype.__proto__ = SieveElse.prototype;
+
+SieveIf.isElement
+ = function (parser)
+{
+ return parser.startsWith("if");
+}
+
+SieveIf.nodeName = function () {
+ return "condition/if";
+}
+
+SieveIf.nodeType = function () {
+ return "condition/";
+}
+
+SieveIf.prototype.init
+ = function (parser)
+{
+ parser.extract("if");
+
+ this.ws[2].init(parser);
+
+ this._test = this._createByClass(["test","operator"],parser);
+
+ this.ws[0].init(parser);
+
+ SieveBlock.prototype.init.call(this,parser);
+
+ this.ws[1].init(parser);
+
+ return this;
+}
+
+SieveIf.prototype.removeChild
+ = function (childId,cascade,stop)
+{
+ var elm = SieveBlock.prototype.removeChild.call(this,childId);
+ if (cascade && elm)
+ return this;
+
+ if (elm)
+ return elm;
+
+ if (this.test().id() != childId)
+ throw "Unknown ChildId";
+
+ if (!cascade)
+ throw "Use cascade to delete conditions";
+
+ this.test().parent(null);
+ this._test = null;
+
+ if ((!stop) || (stop.id() != this.id()))
+ return this.remove(cascade,stop);
+
+ return this;
+}
+
+SieveIf.prototype.test
+ = function (item)
+{
+ if (typeof(item) === "undefined")
+ return this._test;
+
+ if (item.parent())
+ throw "test already bound to "+item.parent().id();
+
+ // Release old test...
+ if(this._test)
+ this._test.parent(null);
+
+ // ... and bind new test to this node
+ this._test = item.parent(this);
+
+ return this;
+}
+
+SieveIf.prototype.empty
+ = function ()
+{
+ return (!this._test) ? true : false;
+}
+
+
+SieveIf.prototype.require
+ = function (imports)
+{
+ SieveElse.prototype.require.call(this,imports);
+ this._test.require(imports);
+}
+
+SieveIf.prototype.toScript
+ = function()
+{
+ return "if"
+ + this.ws[2].toScript()
+ + this._test.toScript()
+ + this.ws[0].toScript()
+ + SieveBlock.prototype.toScript.call(this)
+ + this.ws[1].toScript();
+}
+
+
+//****************************************************************************//
+
+function SieveElse(docshell,id)
+{
+ SieveBlock.call(this,docshell,id);
+ this.ws = [];
+ this.ws[0] = this._createByName("whitespace","\r\n");
+ this.ws[1] = this._createByName("whitespace","\r\n");
+}
+
+SieveElse.prototype.__proto__ = SieveBlock.prototype;
+
+SieveElse.isElement
+ = function (parser)
+{
+ return parser.startsWith("else");
+}
+
+SieveElse.nodeName = function () {
+ return "condition/else";
+}
+
+SieveElse.nodeType = function () {
+ return "condition/";
+}
+
+SieveElse.prototype.init
+ = function (parser)
+{
+ parser.extract("else");
+
+ this.ws[0].init(parser);
+
+ SieveBlock.prototype.init.call(this,parser);
+
+ this.ws[1].init(parser);
+
+ return this;
+}
+
+SieveElse.prototype.toScript
+ = function()
+{
+ return "else"
+ + this.ws[0].toScript()
+ + SieveBlock.prototype.toScript.call(this)
+ + this.ws[1].toScript();
+}
+
+//****************************************************************************//
+
+function SieveCondition(docshell,id)
+{
+ SieveBlockBody.call(this,docshell,id);
+
+ this.elms[0] = this._createByName("condition/if","if false {\r\n}\r\n");
+}
+
+SieveCondition.prototype.__proto__ = SieveBlockBody.prototype;
+
+SieveCondition.isElement
+ = function (parser)
+{
+ return SieveIf.isElement(parser);
+}
+
+SieveCondition.nodeName = function () {
+ return "condition";
+}
+
+SieveCondition.nodeType = function () {
+ return "condition";
+}
+
+SieveCondition.prototype.init
+ = function (parser)
+{
+ this.elms[0] = this._createByName("condition/if",parser);
+
+ while (parser.startsWith("elsif"))
+ {
+ parser.extract("els");
+
+ this.elms.push(
+ this._createByName("condition/if",parser));
+
+ }
+
+ if (this._probeByName("condition/else",parser))
+ this.elms.push(this._createByName("condition/else",parser))
+
+ return this;
+}
+
+SieveCondition.prototype.removeChild
+ = function (childId,cascade,stop)
+{
+ // should we remove the whole node
+ if (typeof (childId) === "undefined")
+ throw "Child ID Missing";
+
+ if (stop && (stop.id() == this.id()))
+ cascade = false;
+
+ var elm = SieveBlockBody.prototype.removeChild.call(this,childId,cascade,stop);
+
+ // ... if we endup after delete with just an else, merge it into parent...
+ if ((this.children().length) && (!this.children(0).test))
+ {
+ // we copy all of our else statements into our parent...
+ while (this.children(0).children().length)
+ this.parent().append(this.children(0).children(0), this);
+
+ return this.children(0).remove(cascade,stop);
+ }
+
+
+ // If SieveBlockBody cascaded through our parent, it should be null...
+ // ... and we are done
+
+ // 4. the condition might now be empty
+ if (this.parent() && (!this.children().length))
+ return this.remove(cascade,stop);
+
+ if (this.parent() && cascade)
+ return this;
+
+
+ return elm;
+}
+
+
+SieveCondition.prototype.toScript
+ = function ()
+{
+ var str ="";
+
+ for (var i=0; i<this.elms.length; i++)
+ {
+ if ((i > 0) && (this.elms[i].test))
+ str += "els"
+
+ str += this.elms[i].toScript();
+ }
+
+ return str;
+}
+
+
+if (!SieveLexer)
+ throw "Could not register Conditional Elements";
+
+SieveLexer.register(SieveIf);
+SieveLexer.register(SieveElse);
+SieveLexer.register(SieveCondition); \ No newline at end of file
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveImports.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveImports.js
new file mode 100644
index 0000000..0230a53
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveImports.js
@@ -0,0 +1,149 @@
+/*
+ * The contents of this file is licenced. You may obtain a copy of
+ * the license at http://sieve.mozdev.org or request it via email
+ * from the author. Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ */
+
+"use strict";
+
+function SieveRequire(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+
+ this.whiteSpace = this._createByName("whitespace");
+ this.semicolon = this._createByName("atom/semicolon");
+
+ this.strings = this._createByName("stringlist");
+}
+
+SieveRequire.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveRequire.isElement
+ = function (parser)
+{
+ return parser.startsWith("require");
+}
+
+SieveRequire.nodeName = function () {
+ return "import/require";
+}
+
+SieveRequire.nodeType = function () {
+ return "import/";
+}
+
+SieveRequire.prototype.init
+ = function (parser)
+{
+ // Syntax :
+ // <"require"> <stringlist> <";">
+
+ // remove the "require" identifier ...
+ parser.extract("require");
+
+ // ... eat the deadcode before the stringlist...
+ this.whiteSpace.init(parser);
+
+ // ... extract the stringlist...
+ this.strings.init(parser);
+
+ this.semicolon.init(parser);
+
+ return this;
+}
+
+SieveRequire.prototype.capability
+ = function (require)
+{
+ if (typeof(require) === "undefined")
+ return this.strings;
+
+ if (!this.strings.contains(require))
+ this.strings.append(require);
+
+ return this;
+}
+
+SieveRequire.prototype.toScript
+ = function ()
+{
+ return "require"
+ + this.whiteSpace.toScript()
+ + this.strings.toScript()
+ + this.semicolon.toScript();
+}
+
+
+// CONSTRUCTOR:
+function SieveBlockImport(docshell,id)
+{
+ SieveBlockBody.call(this,docshell,id);
+}
+
+SieveBlockImport.prototype.__proto__ = SieveBlockBody.prototype;
+
+// PUBLIC STATIC:
+SieveBlockImport.isElement
+ = function (parser)
+{
+ return SieveLexer.probeByClass(["import/","whitespace"],parser);
+}
+
+SieveBlockImport.nodeName = function () {
+ return "import";
+}
+
+SieveBlockImport.nodeType = function () {
+ return "import";
+}
+
+// PUBLIC:
+SieveBlockImport.prototype.init
+ = function (parser)
+{
+ // The import section consists of require and deadcode statments...
+ while (this._probeByClass(["import/","whitespace"],parser))
+ this.elms.push(
+ this._createByClass(["import/","whitespace"],parser));
+
+ return this;
+}
+
+
+SieveBlockImport.prototype.capability
+ = function (require)
+{
+
+ // We should try to insert new requires directly aftr the previous one...
+ // ... otherwise it looks strange.
+ var item = null;
+
+ for (var i=0; i<this.elms.length; i++)
+ {
+ if (!this.elms[i].capability)
+ continue;
+
+ item = this.elms[i];
+
+ if (this.elms[i].capability().contains(require))
+ return this;
+ }
+
+
+ this.append(
+ this.document().createByName("import/require").capability(require), item);
+
+ return this;
+}
+
+
+if (!SieveLexer)
+ throw "Could not register Import Elements";
+
+
+SieveLexer.register(SieveBlockImport);
+SieveLexer.register(SieveRequire);
+ \ No newline at end of file
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveNumbers.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveNumbers.js
new file mode 100644
index 0000000..9e1cd03
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveNumbers.js
@@ -0,0 +1,84 @@
+ /*
+ * The contents of this file is licenced. You may obtain a copy of
+ * the license at http://sieve.mozdev.org or request it via email
+ * from the author. Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ */
+
+"use strict";
+
+ function SieveNumber(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+
+ this._number = "1";
+ this._unit = "";
+}
+
+SieveNumber.isElement
+ = function (parser)
+{
+ return parser.isNumber(parser);
+}
+
+SieveNumber.nodeName = function () {
+ return "number";
+}
+
+SieveNumber.nodeType = function () {
+ return "number/";
+}
+
+SieveNumber.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveNumber.prototype.init
+ = function(parser)
+{
+ this._number = parser.extractNumber();
+
+ if (parser.isChar(['K','M','G']))
+ this._unit = parser.extractChar();
+
+ return this;
+}
+
+SieveNumber.prototype.value
+ = function (number)
+{
+ if (typeof(number) === "undefined")
+ return this._number;
+
+ number = parseInt(number,10);
+
+ if (isNaN(number))
+ throw "Invalid Number";
+
+ this._number = number;
+ return this;
+}
+
+SieveNumber.prototype.unit
+ = function (unit)
+{
+ if (typeof(unit) === "undefined")
+ return this._unit;
+
+ if ((unit != "") && (unit != "K") && (unit != "M") && (unit != "G"))
+ throw "Invalid unit mut be either K, M or G";
+
+ this._unit = unit;
+ return this;
+}
+
+SieveNumber.prototype.toScript
+ = function ()
+{
+ return ""+this._number+""+this._unit;
+}
+
+if (!SieveLexer)
+ throw "Could not register Atoms";
+
+SieveLexer.register(SieveNumber); \ No newline at end of file
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveOperators.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveOperators.js
new file mode 100644
index 0000000..0b97345
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveOperators.js
@@ -0,0 +1,211 @@
+/*
+ * The contents of this file is licenced. You may obtain a copy of
+ * the license at http://sieve.mozdev.org or request it via email
+ * from the author. Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ */
+
+ "use strict";
+
+/*
+ * Currenlty we have only Unary Operators like not and Nary/Multary like anyof allof
+ * Sieve does not implement binary (2) or tenary operators (3)
+ */
+
+// Unary operators
+
+function SieveNotOperator(docshell,id)
+{
+ // first line with deadcode
+ SieveAbstractElement.call(this,docshell,id);
+
+ this.whiteSpace = []
+ this.whiteSpace[0] = this._createByName("whitespace", " ");
+ this.whiteSpace[1] = this._createByName("whitespace");
+ // this.test = this._createByName("operator");
+}
+
+SieveNotOperator.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveNotOperator.isElement = function(parser) {
+ return parser.startsWith("not");
+}
+
+SieveNotOperator.nodeName = function () {
+ return "operator/not";
+}
+
+SieveNotOperator.nodeType = function () {
+ return "operator";
+}
+
+SieveNotOperator.prototype.init
+ = function (parser)
+{
+ // Syntax :
+ // <"not"> <test>
+ parser.extract("not");
+
+ this.whiteSpace[0].init(parser);
+
+ if ( ! this._probeByClass(["test","operator"],parser))
+ throw "Test command expected but found:\n'"+parser.bytes(50)+"'...";
+
+ this._test = this._createByClass(["test","operator"],parser)
+
+ if (this._probeByName("whitespace",parser))
+ this.whiteSpace[1].init(parser);
+
+ return this;
+
+}
+
+SieveNotOperator.prototype.removeChild
+ = function (childId,cascade,stop)
+{
+ if (!cascade)
+ throw "only cascade possible";
+
+ if (this.test().id() != childId)
+ throw "Invalid Child id";
+
+ // We cannot survive without a test ...
+ this.test().parent(null);
+ this._test = null;
+
+ if (!stop || (stop.id() != this.id()))
+ return this.remove(cascade,stop);
+
+ return this
+}
+
+SieveNotOperator.prototype.test
+ = function (item)
+{
+ if (typeof(item) === "undefined")
+ return this._test;
+
+ if (item.parent())
+ throw "test already bound to "+item.parent().id();
+
+ // Release old test...
+ if (this._test)
+ this._test.parent(null);
+
+ // ... and bind new test to this node
+ this._test = item.parent(this);
+
+ return this;
+}
+
+SieveNotOperator.prototype.toScript
+ = function ()
+{
+ return "not"
+ + this.whiteSpace[0].toScript()
+ + this._test.toScript()
+ + this.whiteSpace[1].toScript();
+}
+
+//****************************************************************************//
+
+//N-Ary Operator
+//****************************************************************************/
+function SieveAnyOfAllOfTest(docshell,id)
+{
+ SieveTestList.call(this,docshell,id);
+ this.whiteSpace = this._createByName("whitespace");
+ this.isAllOf = true;
+}
+
+// Inherrit TestList
+SieveAnyOfAllOfTest.prototype.__proto__ = SieveTestList.prototype;
+
+SieveAnyOfAllOfTest.isElement
+ = function (parser)
+{
+ if (parser.startsWith("allof"))
+ return true;
+
+ if (parser.startsWith("anyof"))
+ return true;
+
+ return false;
+}
+
+SieveAnyOfAllOfTest.nodeName = function () {
+ return "operator/anyof";
+}
+
+SieveAnyOfAllOfTest.nodeType = function () {
+ return "operator";
+}
+
+SieveAnyOfAllOfTest.prototype.init
+ = function (parser)
+{
+ if (parser.startsWith("allof"))
+ this.isAllOf = true;
+ else if (parser.startsWith("anyof"))
+ this.isAllOf = false;
+ else
+ throw "allof or anyof expected but found: \n"+parser.bytes(50)+"...";
+
+ // remove the allof or anyof
+ parser.extract(5);
+
+ this.whiteSpace.init(parser);
+
+ SieveTestList.prototype.init.call(this,parser);
+
+ return this;
+}
+
+SieveAnyOfAllOfTest.prototype.test
+ = function (item, old)
+{
+ if (typeof(item) === "undefined")
+ {
+ if (this.tests.length == 1)
+ return this.tests[0][1];
+
+ throw ".test() has more than one element";
+ }
+
+ if (item.parent())
+ throw "test already bound to "+item.parent().id();
+
+
+ // Release old test...
+ this.append(item,old);
+
+ if (typeof(old) !== "undefined")
+ this.removeChild(old.id());
+ /*if (this._test)
+ this._test.parent(null);
+
+ // ... and bind new test to this node
+ this._test = ;*/
+
+ return this;
+}
+
+SieveAnyOfAllOfTest.prototype.toScript
+ = function()
+{
+ return (this.isAllOf?"allof":"anyof")
+ + this.whiteSpace.toScript()
+ + SieveTestList.prototype.toScript.call(this);
+}
+
+
+
+if (!SieveLexer)
+ throw "Could not register Operators";
+
+
+SieveLexer.register(SieveNotOperator);
+SieveLexer.register(SieveAnyOfAllOfTest);
+
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveStrings.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveStrings.js
new file mode 100644
index 0000000..df847be
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveStrings.js
@@ -0,0 +1,789 @@
+/*
+ * The contents of this file is licenced. You may obtain a copy of
+ * the license at http://sieve.mozdev.org or request it via email
+ * from the author. Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ */
+
+"use strict";
+
+// TODO create an abstract class for get and set string...
+// TODO descide on update message weather it is a Multiline oder Quoted...
+
+
+
+// CONSTRUCTOR:
+function SieveMultiLineString(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+
+ this.text = "";
+
+ this.whiteSpace = this._createByName("whitespace");
+ this.hashComment = null;
+}
+
+SieveMultiLineString.prototype.__proto__ = SieveAbstractElement.prototype;
+
+// PUBLIC STATIC:
+SieveMultiLineString.isElement
+ = function (parser)
+{
+ return parser.startsWith("text:");
+}
+
+SieveMultiLineString.nodeName = function () {
+ return "string/multiline";
+}
+
+SieveMultiLineString.nodeType = function () {
+ return "string/";
+}
+
+// PUBLIC:
+SieveMultiLineString.prototype.init
+ = function (parser)
+{
+ //<"text:"> *(SP / HTAB) (hash-comment / CRLF)
+/* if (this._probeByName("string/multiline",parser) == false)
+ throw "Multi-line String expected but found: \n"+parser.substr(0,50)+"...";*/
+
+ // remove the "text:"
+ parser.extract("text:");
+
+ this.whiteSpace.init(parser,true);
+
+ if (this._probeByName("whitespace/hashcomment",parser))
+ this.hashComment = this._createByName("whitespace/hashcomment",parser);
+
+ // we include the previously extracted linebreak. this makes life way easier...
+ // and allows us to match agains the unique "\r\n.\r\n" Pattern instead of
+ // ... just ".\r\n"
+ parser.rewind(2);
+
+ this.text = parser.extractUntil("\r\n.\r\n");
+
+ // dump the first linebreak and remove dot stuffing
+ this.text = this.text.substr(2).replace(/^\.\./mg,".")
+
+ return this;
+}
+
+SieveMultiLineString.prototype.getValue
+ = function ()
+{
+ return this.text;
+}
+
+SieveMultiLineString.prototype.setValue
+ = function (value)
+{
+ this.text = value;
+}
+
+SieveMultiLineString.prototype.toScript
+ = function ()
+{
+ var text = this.text;
+
+ if (text != "")
+ text += "\r\n";
+
+ // Dot stuffing...
+ text = text.replace(/^\./mg,"..")
+
+ return "text:"
+ +this.whiteSpace.toScript()
+ +((this.hashComment == null)?"":this.hashComment.toScript())
+ +text
+ +".\r\n";
+}
+
+/*******************************************************************************
+ CLASSNAME:
+ SieveQuotedString implements SieveObject
+
+ CONSTUCTOR:
+ public SieveQuotedString()
+
+ PUBLIC FUNCTIONS:
+ public static boolean isQuotedString(String data)
+ public boolean parse(String data) throws Exception
+ public String getValue()
+ public String toScript()
+ public String toXUL()
+
+ MEMBER VARIABLES:
+ private String text;
+
+ DESCRIPTION:
+ Defines the atomar String which in encapsulated in Quotes (")
+
+*******************************************************************************/
+
+
+// CONSTRUCTOR:
+function SieveQuotedString(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+ this.text = "";
+}
+
+SieveQuotedString.prototype.__proto__ = SieveAbstractElement.prototype;
+
+// PUBLIC STATIC:
+SieveQuotedString.isElement
+ = function (parser)
+{
+ return parser.isChar("\"");
+}
+
+SieveQuotedString.nodeName = function () {
+ return "string/quoted";
+}
+
+SieveQuotedString.nodeType = function () {
+ return "string/";
+}
+
+// PUBLIC:
+SieveQuotedString.prototype.init
+ = function (parser)
+{
+ this.text = "";
+
+ parser.extractChar("\"");
+
+ if (parser.skipChar("\""))
+ {
+ this.text = "";
+ return this;
+ }
+
+ // we should not be tricked by escaped quotes
+
+ /*
+ * " blubber "
+ * " blub \" er" -> ignore
+ * " blubber \\" -> blubber \ -> skip
+ * " blubber \\\"" -> blubber \" ->ignore
+ * " blubber \\\\"
+ *
+ * "\\"
+ */
+
+ while(true)
+ {
+ this.text += parser.extractUntil("\"");
+
+ // Skip if the quote is not escaped
+ if (this.text.charAt(this.text.length-1) != "\\")
+ break;
+
+ // well it is obviously escaped, so we have to check if the escape
+ // character is escaped
+ if (this.text.length >= 2)
+ if (this.text.charAt(this.text.length-2) == "\\")
+ break;
+
+ // add the quote, it was escaped...
+ this.text += "\"";
+ }
+
+ // Only double quotes and backslashes are escaped...
+ // ... so we convert \" into "
+ this.text = this.text.replace('\\"','"',"g")
+ // ... and convert \\ to \
+ this.text = this.text.replace("\\\\","\\","g");
+
+ // ... We should finally ignore an other backslash patterns...
+ // ... but as they are illegal anyway, we assume a perfect world.
+
+ return this;
+}
+
+SieveQuotedString.prototype.getValue
+ = function ()
+{
+ return this.text;
+}
+
+SieveQuotedString.prototype.setValue
+ = function (value)
+{
+ if (value.search(/(\r\n|\n|\r)/gm) != -1)
+ throw "Quoted string support only single line strings";
+
+ this.text = value;
+}
+
+SieveQuotedString.prototype.toScript
+ = function ()
+{
+ return "\""+this.text.replace("\\","\\\\","g").replace('"','\\"',"g")+"\"";
+}
+
+/*******************************************************************************
+ CLASSNAME:
+ SieveStringList implements SieveObject
+
+ CONSTUCTOR:
+ public SieveStringList()
+
+ PUBLIC FUNCTIONS:
+ public static boolean isStringList(String data)
+ public boolean parse(String data) throws Exception
+ public String toScript()
+ public String toXUL()
+
+ MEMBER VARIABLES:
+ private Array[] elements;
+ private boolean compact;
+
+ DESCRIPTION:
+ A Stringlist is an Array of Quotedstring
+
+*******************************************************************************/
+
+
+// CONSTRUCTOR:
+function SieveStringList(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+
+ this.elements = [];
+
+ // if the list contains only one entry...
+ // ... use the comact syntac, this means ...
+ // ... don't use the "[...]" to encapsulate the string
+ this.compact = true;
+}
+
+SieveStringList.prototype.__proto__ = SieveAbstractElement.prototype;
+
+// PUBLIC STATIC:
+SieveStringList.isElement
+ = function (parser)
+{
+ // the [ is not necessary if the list contains only one enty!
+ if (parser.isChar("["))
+ return true;
+
+ if (SieveLexer.probeByName("string/quoted",parser))
+ return true;
+
+ return false;
+}
+
+SieveStringList.nodeName = function () {
+ return "stringlist";
+}
+
+SieveStringList.nodeType = function () {
+ return "stringlist";
+}
+
+// PUBLIC:
+SieveStringList.prototype.init
+ = function (parser)
+{
+ this.elements = [];
+
+ if (this._probeByName("string/quoted",parser))
+ {
+ this.compact = true;
+ var item = [];
+ item[1] = this._createByName("string/quoted",parser);
+ this.elements[0] = item;
+
+ return this;
+ }
+
+ this.compact = false;
+
+ parser.extractChar("[");
+
+ while ( ! parser.isChar("]"))
+ {
+ if (this.elements.length)
+ parser.extractChar(",");
+
+ var element = [];
+
+ if (this._probeByName("whitespace",parser))
+ element[0] = this._createByName("whitespace",parser);
+
+ if (this._probeByName("string/quoted",parser) == false)
+ throw "Quoted String expected but found: \n"+parser.bytes(50)+"...";
+
+ element[1] = this._createByName("string/quoted",parser);
+
+ if (this._probeByName("whitespace",parser))
+ element[2] = this._createByName("whitespace",parser);
+
+ this.elements.push(element);
+ }
+
+ parser.extractChar("]");
+ return this;
+}
+
+SieveStringList.prototype.contains
+ = function (str,matchCase)
+{
+ var item = "";
+
+ if (typeof(matchCase) === "undefined")
+ str = str.toLowerCase();
+
+ for (var i=0; i<this.elements.length; i++)
+ {
+ if (typeof(matchCase) === "undefined")
+ item = this.elements[i][1].getValue().toLowerCase();
+ else
+ item = this.elements[i][1].getValue();
+
+ if (item == str)
+ return true
+ }
+
+ return false;
+}
+
+SieveStringList.prototype.item
+ = function (idx,value)
+{
+ if (typeof(value) !== "undefined")
+ this.elements[idx][1].setValue(value);
+
+ return this.elements[idx][1].getValue();
+}
+
+SieveStringList.prototype.size
+ = function ()
+{
+ return this.elements.length;
+}
+
+SieveStringList.prototype.append
+ = function(str)
+{
+ var elm = [null,"",null]
+ elm[1] = this._createByName("string/quoted",'""');
+ elm[1].setValue(str);
+
+ this.elements.push(elm);
+}
+
+SieveStringList.prototype.clear
+ = function()
+{
+ this.elements = [];
+}
+
+SieveStringList.prototype.remove
+ = function(str)
+{
+ for (var i =0; i<this.elements.length; i++)
+ {
+ if (this.elements[i][1].getValue() != str)
+ continue;
+
+ this.elements.splice(i,1);
+ }
+}
+
+
+SieveStringList.prototype.toScript
+ = function ()
+{
+ if (this.elements == 0)
+ return '""';
+
+ if (this.compact && this.elements.length <= 1)
+ return this.elements[0][1].toScript();
+
+ var result = "[";
+ var separator = "";
+
+ for (var i = 0;i<this.elements.length; i++)
+ {
+ result = result + separator
+ + ((this.elements[i][0] != null)? this.elements[i][0].toScript() : "")
+ + this.elements[i][1].toScript()
+ + ((this.elements[i][2] != null)? this.elements[i][2].toScript() : "");
+
+ separator = ",";
+ }
+ result += "]";
+
+ return result;
+}
+
+/*******************************************************************************
+ CLASSNAME:
+ SieveString implements SieveObject
+
+ CONSTUCTOR:
+ public SieveString()
+
+ PUBLIC FUNCTIONS:
+ public static boolean isString(String data)
+ public boolean parse(String data) throws Exception
+ public String getValue()
+ public String toScript()
+ public String toXUL()
+
+ MEMBER VARIABLES:
+ private String string;
+
+ DESCRIPTION:
+ Defines the SieveString primitive by combinig the two atomar Stringtypes
+ SieveQuotedString and SieveMultiLineString.
+
+*******************************************************************************/
+
+
+// CONSTRUCTOR:
+function SieveString(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+ this.string = this._createByName("string/quoted");
+}
+
+SieveString.prototype.__proto__ = SieveAbstractElement.prototype;
+
+// PUBLIC STATIC:
+SieveString.isElement
+ = function(parser)
+{
+ return SieveLexer.probeByClass(["string/"],parser);
+}
+
+SieveString.nodeName = function () {
+ return "string";
+}
+
+SieveString.nodeType = function () {
+ return "string";
+}
+
+// PUBLIC:
+SieveString.prototype.init
+ = function (parser)
+{
+ this.string = this._createByClass(["string/"],parser);
+
+ return this;
+}
+
+SieveString.prototype.getValue
+ = function ()
+{
+ return this.string.getValue();
+}
+
+SieveString.prototype.setValue
+ = function (value)
+{
+ //TODO: convert from/to multiline and singleline
+ return this.string.setValue(value);
+}
+
+SieveString.prototype.toScript
+ = function ()
+{
+ return this.string.toScript();
+}
+
+//***************************************************************************//
+
+//Matchtypes
+
+/**
+ * Matchtypes are used to compare Strings
+ * @param {} id
+ */
+function SieveMatchType(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+ this.type = "is";
+ this.optional = true;
+}
+
+SieveMatchType.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveMatchType.isElement
+ = function (parser)
+{
+ if (parser.startsWith(":is"))
+ return true;
+ if (parser.startsWith(":matches"))
+ return true;
+ if (parser.startsWith(":contains"))
+ return true;
+
+ return false;
+}
+
+SieveMatchType.nodeName = function () {
+ return "match-type";
+}
+
+SieveMatchType.nodeType = function () {
+ return "comparison";
+}
+
+SieveMatchType.prototype.init
+ = function (parser)
+{
+ if (parser.startsWith(":is"))
+ this.type = "is";
+ else if (parser.startsWith(":matches"))
+ this.type = "matches";
+ else if (parser.startsWith(":contains"))
+ this.type = "contains"
+ else
+ throw "Syntaxerror, unknown match type";
+
+ parser.extract(this.type.length+1);
+
+ if (this.type == "is")
+ this.optional = false;
+
+ return this;
+}
+
+SieveMatchType.prototype.isOptional
+ = function (value)
+{
+ if (typeof(value) === "undefined")
+ return ((this.optional) && (this.type == "is"))
+
+ this.optional = value;
+
+ return this;
+}
+
+SieveMatchType.prototype.matchType
+ = function (value)
+{
+ if(typeof(value) === "undefined")
+ return this.type
+
+ value = value.toLowerCase();
+
+ if ((value == "is") || (value == "matches") || (value == "contains"))
+ this.type = value;
+ else
+ throw "Unkonwn Match type >>"+value+"<<";
+
+ return this;
+}
+
+SieveMatchType.prototype.toScript
+ = function ()
+{
+ if (this.isOptional())
+ return "";
+
+ return ":"+this.type;
+}
+
+
+
+/**
+ * Addresses are one of the most frequent things represented as strings.
+ * These are structured, and allows comparison against the local-
+ * part or the domain of an address
+ *
+ * email@example.com
+ * [local Part]@[Domain Part]
+ *
+ * ist example.com der domain-part, email der local-part.
+ */
+//":localpart" / ":domain" / ":all"
+
+
+function SieveAddressPart(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+ this.type = "all";
+ this.optional = true;
+}
+
+SieveAddressPart.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveAddressPart.isElement
+ = function (parser)
+{
+ if (parser.startsWith(":localpart"))
+ return true;
+ if (parser.startsWith(":domain"))
+ return true;
+ if (parser.startsWith(":all"))
+ return true;
+
+ return false;
+}
+
+SieveAddressPart.nodeName = function () {
+ return "address-part";
+}
+
+SieveAddressPart.nodeType = function () {
+ return "comparison";
+}
+
+
+SieveAddressPart.prototype.init
+ = function (parser)
+{
+ if (parser.startsWith(":localpart"))
+ this.type = "localpart";
+ else if (parser.startsWith(":domain"))
+ this.type = "domain";
+ else if (parser.startsWith(":all"))
+ this.type = "all"
+ else
+ throw "Syntaxerror, unknown address part";
+
+ parser.extract(this.type.length+1);
+
+ if (this.type == "all")
+ this.optional = false;
+
+ return this;
+}
+
+SieveAddressPart.prototype.isOptional
+ = function (value)
+{
+ if (typeof(value) === "undefined")
+ return ((this.optional) && (this.type == "all"))
+
+ this.optional = value;
+}
+
+SieveAddressPart.prototype.addressPart
+ = function (value)
+{
+ if(typeof(value) === "undefined")
+ return this.type
+
+ value = value.toLowerCase();
+
+ if ((value == "all") || (value == "domain") || (value == "localpart"))
+ this.type = value;
+ else
+ throw "Unkonwn Match type >>"+value+"<<";
+
+ return this;
+}
+
+SieveAddressPart.prototype.toScript
+ = function ()
+{
+ if (this.isOptional())
+ return "";
+
+ return ":"+this.type;
+}
+
+
+ // Comparators define the charset. All Sieve implementation have to support
+ // which" is case sensitive and "i;ascii-codemap" which is case
+ // insensitive.
+
+/**
+ * Comparators sepcify the charset which should be used for string comparison
+ * By default two matchtypes are supported.
+ *
+ * "i;octet" compares strings based on UTF-8 octetts
+ *
+ * "i;ascii-codemap" converts strings before comparison to ASCII
+ */
+
+function SieveComparator(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+ this.whiteSpace = this._createByName("whitespace"," ");
+ this._comparator = this._createByName("string/quoted","\"i;ascii-casemap\"");
+ this.optional = true;
+}
+
+SieveComparator.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveComparator.isElement
+ = function(parser)
+{
+ return (parser.startsWith(":comparator"))
+}
+
+SieveComparator.nodeName = function () {
+ return "comparator";
+}
+
+SieveComparator.nodeType = function () {
+ return "comparison";
+}
+
+SieveComparator.prototype.init
+ = function (parser)
+{
+ // Syntax :
+ // <":comparator"> <comparator-name: string>
+ parser.extract(":comparator");
+
+ this.whiteSpace.init(parser);
+
+ this._comparator.init(parser);
+
+ this.optional = false;
+
+ return this;
+}
+
+SieveComparator.prototype.isOptional
+ = function (value)
+{
+ if (typeof(value) === "undefined")
+ return ((this.optional) && (this._comparator.getValue() == "i;ascii-casemap"))
+
+ this.optional = value;
+}
+
+SieveComparator.prototype.comparator
+ = function (value)
+{
+ if(typeof(value) === "undefined")
+ return this._comparator.getValue();
+
+ this._comparator.setValue(value);
+
+ return this;
+}
+
+SieveComparator.prototype.toScript
+ = function ()
+{
+ if (this.isOptional())
+ return "";
+
+ return ":comparator"
+ +this.whiteSpace.toScript()
+ +this._comparator.toScript();
+}
+
+if (!SieveLexer)
+ throw "Could not register Strings Elements";
+
+SieveLexer.register(SieveStringList);
+SieveLexer.register(SieveString);
+SieveLexer.register(SieveQuotedString);
+SieveLexer.register(SieveMultiLineString);
+SieveLexer.register(SieveMatchType);
+SieveLexer.register(SieveComparator);
+SieveLexer.register(SieveAddressPart); \ No newline at end of file
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveTests.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveTests.js
new file mode 100644
index 0000000..7e29085
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveTests.js
@@ -0,0 +1,767 @@
+/*
+ * The contents of this file is licenced. You may obtain a copy of
+ * the license at http://sieve.mozdev.org or request it via email
+ * from the author. Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ */
+
+ "use strict";
+
+//****************************************************************************//
+
+//<envelope> [COMPARATOR] [ADDRESS-PART] [MATCH-TYPE]
+// <envelope-part: string-list> <key-list: string-list>
+
+function SieveEnvelope(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+
+ this.whiteSpace = [];
+ this.whiteSpace[0] = this._createByName("whitespace"," ");
+ this.whiteSpace[1] = this._createByName("whitespace"," ");
+ this.whiteSpace[2] = this._createByName("whitespace"," ");
+ this.whiteSpace[3] = this._createByName("whitespace"," ");
+ this.whiteSpace[4] = this._createByName("whitespace"," ");
+ this.whiteSpace[5] = this._createByName("whitespace"," ");
+
+ this.addressPart = this._createByName("address-part");
+ this.matchType = this._createByName("match-type");
+ this.comparator = this._createByName("comparator")
+
+ this.envelopeList = this._createByName("stringlist");
+ this.keyList = this._createByName("stringlist");
+}
+
+SieveEnvelope.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveEnvelope.isElement
+ = function(parser)
+{
+ return parser.startsWith("envelope");
+}
+
+SieveEnvelope.isCapable
+ = function (capabilities)
+{
+ return (capabilities["envelope"] == true);
+}
+
+SieveEnvelope.nodeName = function () {
+ return "test/envelope";
+}
+
+SieveEnvelope.nodeType = function () {
+ return "test";
+}
+
+SieveEnvelope.prototype.init
+ = function (parser)
+{
+ parser.extract("envelope");
+ this.whiteSpace[0].init(parser);
+
+ while (true)
+ {
+ if (this.addressPart.isOptional() && this._probeByName("address-part",parser))
+ {
+ this.addressPart.init(parser)
+ this.whiteSpace[1].init(parser);
+
+ continue;
+ }
+
+ if (this.comparator.isOptional() && this._probeByName("comparator",parser))
+ {
+ this.comparator.init(parser);
+ this.whiteSpace[2].init(parser);
+
+ continue;
+ }
+
+ if (this.matchType.isOptional() && this._probeByName("match-type",parser))
+ {
+ this.matchType.init(parser);
+ this.whiteSpace[3].init(parser);
+
+ continue;
+ }
+
+ break;
+ }
+
+ this.envelopeList.init(parser);
+
+ this.whiteSpace[4].init(parser);
+
+ this.keyList.init(parser);
+
+ this.whiteSpace[5].init(parser);
+
+ return this;
+}
+
+SieveEnvelope.prototype.require
+ = function (requires)
+{
+ requires["envelope"] = true;
+}
+
+SieveEnvelope.prototype.toScript
+ = function ()
+{
+ return "envelope"
+ + this.whiteSpace[0].toScript()
+ + this.addressPart.toScript()
+ + (!this.addressPart.isOptional() ? this.whiteSpace[1].toScript() : "" )
+ + this.comparator.toScript()
+ + (!this.comparator.isOptional() ? this.whiteSpace[2].toScript() : "" )
+ + this.matchType.toScript()
+ + (!this.matchType.isOptional() ? this.whiteSpace[3].toScript(): "" )
+ + this.envelopeList.toScript()
+ + this.whiteSpace[4].toScript()
+ + this.keyList.toScript()
+ + this.whiteSpace[5].toScript();
+}
+
+
+
+/******************************************************************************/
+
+
+//address [ADDRESS-PART] [COMPARATOR] [MATCH-TYPE]
+// <header-list: string-list> <key-list: string-list>
+
+
+/*
+ * The contents of this file is licenced. You may obtain a copy of
+ * the license at http://sieve.mozdev.org or request it via email
+ * from the author. Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ */
+
+function SieveAddress(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+
+ this.options = new Array(null,null,null);
+
+ this.whiteSpace = []
+ this.whiteSpace[0] = this._createByName("whitespace"," ");
+ this.whiteSpace[1] = this._createByName("whitespace"," ");
+ this.whiteSpace[2] = this._createByName("whitespace"," ");
+ this.whiteSpace[3] = this._createByName("whitespace"," ");
+ this.whiteSpace[4] = this._createByName("whitespace"," ");
+ this.whiteSpace[5] = this._createByName("whitespace"," ");
+
+ this.addressPart = this._createByName("address-part");
+ this.matchType = this._createByName("match-type");
+ this.comparator = this._createByName("comparator")
+
+ this.headerList = this._createByName("stringlist","\"To\"");
+ this.keyList = this._createByName("stringlist","\"me@example.com\"");
+}
+
+SieveAddress.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveAddress.isElement
+ = function (parser)
+{
+ return parser.startsWith("address");
+}
+
+SieveAddress.nodeName = function () {
+ return "test/address";
+}
+
+SieveAddress.nodeType = function () {
+ return "test";
+}
+
+SieveAddress.prototype.init
+ = function (parser)
+{
+ parser.extract("address");
+
+ this.whiteSpace[0].init(parser);
+
+ while (true)
+ {
+ if (this.addressPart.isOptional() && this._probeByName("address-part",parser))
+ {
+ this.addressPart.init(parser)
+ this.whiteSpace[1].init(parser);
+
+ continue;
+ }
+
+ if (this.comparator.isOptional() && this._probeByName("comparator",parser))
+ {
+ this.comparator.init(parser);
+ this.whiteSpace[2].init(parser);
+
+ continue;
+ }
+
+ if (this.matchType.isOptional() && this._probeByName("match-type",parser))
+ {
+ this.matchType.init(parser);
+ this.whiteSpace[3].init(parser);
+
+ continue;
+ }
+
+ break;
+ }
+
+ this.headerList.init(parser);
+
+ this.whiteSpace[4].init(parser);
+
+ this.keyList.init(parser);
+
+ this.whiteSpace[5].init(parser);
+
+ return this;
+}
+
+SieveAddress.prototype.toScript
+ = function ()
+{
+
+ return "address"
+ + this.whiteSpace[0].toScript()
+ + this.addressPart.toScript()
+ + ((!this.addressPart.isOptional()) ? this.whiteSpace[1].toScript() : "" )
+ + this.comparator.toScript()
+ + ((!this.comparator.isOptional()) ? this.whiteSpace[2].toScript() : "" )
+ + this.matchType.toScript()
+ + ((!this.matchType.isOptional()) ? this.whiteSpace[3].toScript(): "" )
+ + this.headerList.toScript()
+ + this.whiteSpace[4].toScript()
+ + this.keyList.toScript()
+ + this.whiteSpace[5].toScript();
+}
+
+
+
+/******************************************************************************/
+
+function SieveBoolean(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+
+ // first line with deadcode
+ this.whiteSpace = this._createByName("whitespace");
+
+ this.value = false;
+}
+
+SieveBoolean.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveBoolean.isElement
+ = function(parser)
+{
+ if (parser.startsWith("true"))
+ return true;
+ if (parser.startsWith("false"))
+ return true;
+
+ return false;
+}
+
+SieveBoolean.nodeName = function () {
+ return "test/boolean";
+}
+
+SieveBoolean.nodeType = function () {
+ return "test";
+}
+
+SieveBoolean.prototype.init
+ = function (parser)
+{
+
+ if (parser.startsWith("true"))
+ {
+ parser.extract("true");
+ this.value = true
+ }
+
+ if (parser.startsWith("false"))
+ {
+ parser.extract("false");
+ this.value = false;
+ }
+
+ this.whiteSpace.init(parser);
+
+ return this;
+}
+
+
+SieveBoolean.prototype.toScript
+ = function ()
+{
+ if (this.value)
+ return "true"+this.whiteSpace.toScript();
+
+ return "false"+this.whiteSpace.toScript();
+}
+
+
+
+/******************************************************************************/
+function SieveSize(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+
+ this.whiteSpace = [];
+ this.whiteSpace[0] = this._createByName("whitespace", " ");
+ this.whiteSpace[1] = this._createByName("whitespace", " ");
+ this.whiteSpace[2] = this._createByName("whitespace", " ");
+
+ this.over = false;
+ this.size = this._createByName("number");
+}
+
+SieveSize.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveSize.isElement
+ = function(parser)
+{
+ return parser.startsWith("size");
+}
+
+SieveSize.nodeName = function () {
+ return "test/size";
+}
+
+SieveSize.nodeType = function () {
+ return "test";
+}
+
+SieveSize.prototype.init
+ = function (parser)
+{
+ // Syntax :
+ // <"size"> <":over" / ":under"> <limit: number>
+
+ parser.extract("size");
+
+ this.whiteSpace[0].init(parser);
+
+ if (parser.startsWith(":over"))
+ {
+ parser.extract(":over")
+ this.isOver(true);
+ }
+ else if (parser.startsWith(":under"))
+ {
+ parser.extract(":under")
+ this.isOver(false);
+ }
+ else
+ throw "Syntaxerror, :under or :over expected";
+
+ this.whiteSpace[1].init(parser);
+ this.size.init(parser);
+ this.whiteSpace[2].init(parser);
+
+ return this;
+}
+
+/**
+ * Gets and Sets the over operator
+ * @param @optional {BOOL} value
+ * @return {}
+ */
+SieveSize.prototype.isOver
+ = function (value)
+{
+ if (typeof(value) === "undefined")
+ return this.over;
+
+ if (typeof(value) === "string")
+ value = ((""+value).toLowerCase() == "true")? true: false;
+
+ this.over = value;
+
+ return this;
+}
+
+SieveSize.prototype.getSize
+ = function ()
+{
+ return this.size;
+}
+
+SieveSize.prototype.toScript
+ = function ()
+{
+ return "size"
+ + this.whiteSpace[0].toScript()
+ + ((this.isOver())?":over":":under")
+ + this.whiteSpace[1].toScript()
+ + this.getSize().toScript()
+ + this.whiteSpace[2].toScript();
+}
+
+
+
+/******************************************************************************/
+
+function SieveExists(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+
+ this.whiteSpace = [];
+ this.whiteSpace[0] = this._createByName("whitespace",' ' );
+ this.whiteSpace[1] = this._createByName("whitespace",' ');
+
+ this.headerNames = this._createByName("stringlist",'"From"');
+}
+
+SieveExists.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveExists.isElement
+ = function(parser)
+{
+ return parser.startsWith("exists");
+}
+
+SieveExists.nodeName = function () {
+ return "test/exists";
+}
+
+SieveExists.nodeType = function () {
+ return "test";
+}
+
+SieveExists.prototype.init
+ = function (parser)
+{
+ // Syntax :
+ // <"exists"> <header-names: string-list>
+ parser.extract("exists");
+
+ this.whiteSpace[0].init(parser);
+
+ this.headerNames.init(parser);
+
+ this.whiteSpace[1].init(parser);
+
+ return this;
+
+}
+
+SieveExists.prototype.toScript
+ = function ()
+{
+ return "exists"
+ + this.whiteSpace[0].toScript()
+ + this.headerNames.toScript()
+ + this.whiteSpace[1].toScript();
+}
+
+/******************************************************************************/
+
+function SieveHeader(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+
+ this.whiteSpace = [];
+ this.whiteSpace[0] = this._createByName("whitespace"," ");
+ this.whiteSpace[1] = this._createByName("whitespace"," ");
+ this.whiteSpace[2] = this._createByName("whitespace"," ");
+ this.whiteSpace[3] = this._createByName("whitespace"," ");
+ this.whiteSpace[4] = this._createByName("whitespace"," ");
+
+ this.headerNames = this._createByName("stringlist",'"Subject"');
+ this.keyList = this._createByName("stringlist",'"Example"');
+
+ this.matchType = this._createByName("match-type");
+ this.comparator = this._createByName("comparator");
+
+}
+
+SieveHeader.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveHeader.isElement
+ = function (parser)
+{
+ return parser.startsWith("header");
+}
+
+SieveHeader.nodeName = function () {
+ return "test/header";
+}
+
+SieveHeader.nodeType = function () {
+ return "test";
+}
+
+SieveHeader.prototype.init
+ = function (parser)
+{
+ // Syntax :
+ // <"header"> [COMPARATOR] [MATCH-TYPE] <header-names: string-list> <key-list: string-list>
+ parser.extract("header");
+
+ this.whiteSpace[0].init(parser);
+
+ // It can be [Comparator] [MATCH-TYPE] or [MATCH-TYPE] [COMPARATOR]
+ while (true)
+ {
+ if (this.comparator.isOptional() && this._probeByName("comparator",parser))
+ {
+ this.comparator.init(parser);
+ this.whiteSpace[1].init(parser);
+
+ continue;
+ }
+
+ if (this.matchType.isOptional() && this._probeByName("match-type",parser))
+ {
+ this.matchType.init(parser);
+ this.whiteSpace[2].init(parser);
+
+ continue;
+ }
+
+ break;
+ }
+
+ this.headerNames.init(parser);
+
+ this.whiteSpace[3].init(parser);
+
+ this.keyList.init(parser);
+
+ this.whiteSpace[4].init(parser);
+
+ return this;
+}
+
+SieveHeader.prototype.keys
+ = function(idx)
+{
+ if (typeof(idx) === "undefined")
+ return this.keyList;
+
+ return this.keyList.item(idx);
+}
+
+SieveHeader.prototype.headers
+ = function(idx)
+{
+ if (typeof(idx) === "undefined")
+ return this.headerNames;
+
+ return this.headerNames.item(idx);
+}
+
+SieveHeader.prototype.toScript
+ = function ()
+{
+
+ // Yes, we normalize match types...
+ // ... sorry about that
+ return "header"
+ + this.whiteSpace[0].toScript()
+ + this.comparator.toScript()
+ + (!this.comparator.isOptional() ? this.whiteSpace[1].toScript() : "" )
+ + this.matchType.toScript()
+ + (!this.matchType.isOptional() ? this.whiteSpace[2].toScript(): "" )
+ + this.headerNames.toScript()
+ + this.whiteSpace[3].toScript()
+ + this.keyList.toScript()
+ + this.whiteSpace[4].toScript()
+}
+
+
+// TODO Stringlist and testslist are quite simmilar
+function SieveTestList(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+ this.tests = [];
+}
+
+SieveTestList.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveTestList.isElement
+ = function (parser)
+{
+ return parser.isChar("(");
+}
+
+SieveTestList.nodeName = function () {
+ return "test/testlist";
+}
+
+SieveTestList.nodeType = function () {
+ return "test/";
+}
+
+SieveTestList.prototype.init
+ = function (parser)
+{
+ this.tests = [];
+
+ parser.extractChar("(");
+
+ while ( ! parser.isChar(")"))
+ {
+ if (this.tests.length > 0)
+ parser.extractChar(",");
+
+ var element = [];
+
+ element[0] = this._createByName("whitespace");
+ if (this._probeByName("whitespace",parser))
+ element[0].init(parser);
+
+ element[1] = this._createByClass(["test","operator"],parser)
+
+ element[2] = this._createByName("whitespace");
+ if (this._probeByName("whitespace",parser))
+ element[2].init(parser);
+
+ this.tests.push(element);
+ }
+
+ parser.extractChar(")");
+
+ return this;
+}
+
+SieveTestList.prototype.append
+ = function (elm, sibling)
+{
+ var element = [];
+
+ switch ([].concat(elm).length)
+ {
+ case 1 :
+ element[0] = this._createByName("whitespace","\r\n");
+ element[1] = elm;
+ element[2] = this._createByName("whitespace");
+ break;
+
+ case 3 :
+ element = elm;
+ break;
+
+ default:
+ throw "Can not append element to list";
+ }
+
+ // we have to do this fist as there is a good chance the the index
+ // might change after deleting...
+ if(elm.parent())
+ elm.remove();
+
+ var idx = this.tests.length;
+
+ if (sibling && (sibling.id() >= 0))
+ for (var idx = 0; idx<this.tests.length; idx++)
+ if (this.tests[idx][1].id() == sibling.id())
+ break;
+
+ this.tests.splice(idx,0,element);
+ elm.parent(this);
+
+ return this;
+}
+
+SieveTestList.prototype.empty
+ = function ()
+{
+ // The direct descendants of our root node are always considered as
+ // not empty. Otherwise cascaded remove would wipe them away.
+ if (this.document().root() == this.parent())
+ return false;
+
+ for (var i=0; i<this.tests.length; i++)
+ if (this.tests[i][1].widget())
+ return false;
+
+ return true;
+}
+
+SieveTestList.prototype.removeChild
+ = function (childId,cascade,stop)
+{
+ // should we remove the whole node
+ if (typeof (childId) === "undefined")
+ throw "Child ID Missing";
+ //return SieveAbstractElement.prototype.remove.call(this);
+
+ // ... or just a child item
+ var elm = null;
+ // Is it a direct match?
+ for (var i=0; i<this.tests.length; i++)
+ {
+ if (this.tests[i][1].id() != childId)
+ continue;
+
+ elm = this.tests[i][1];
+ elm.parent(null);
+
+ this.tests.splice(i,1);
+
+ break;
+ }
+
+ if (cascade && this.empty())
+ if ((!stop) || (stop.id() != this.id()))
+ return this.remove(cascade,stop);
+
+ if (cascade)
+ return this;
+
+ return elm;
+}
+
+
+SieveTestList.prototype.toScript
+ = function()
+{
+ var result = "("
+
+ for (var i = 0;i<this.tests.length; i++)
+ {
+ result = result
+ + ((i>0)?",":"")
+ + this.tests[i][0].toScript()
+ + this.tests[i][1].toScript()
+ + this.tests[i][2].toScript();
+ }
+
+ result += ")";
+
+ return result;
+}
+
+SieveTestList.prototype.require
+ = function (imports)
+{
+ for (var i=0; i<this.tests.length; i++)
+ this.tests[i][1].require(imports)
+}
+
+
+if (!SieveLexer)
+ throw "Could not register Conditional Elements";
+
+SieveLexer.register(SieveAddress);
+SieveLexer.register(SieveBoolean);
+SieveLexer.register(SieveEnvelope);
+SieveLexer.register(SieveExists);
+SieveLexer.register(SieveHeader);
+SieveLexer.register(SieveSize);
+
+SieveLexer.register(SieveTestList);
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveWhiteSpaces.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveWhiteSpaces.js
new file mode 100644
index 0000000..b3d2c54
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/logic/SieveWhiteSpaces.js
@@ -0,0 +1,294 @@
+/*
+ * The contents of this file is licenced. You may obtain a copy of
+ * the license at http://sieve.mozdev.org or request it via email
+ * from the author. Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ */
+
+ "use strict";
+
+// TODO HashComment seperated by linebreaks are equivalent to bracket Comments...
+
+function SieveLineBreak(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+}
+
+SieveLineBreak.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveLineBreak.isElement
+ = function (parser)
+{
+ return parser.startsWith("\r\n");
+}
+
+SieveLineBreak.nodeName = function () {
+ return "whitespace/linebreak";
+}
+
+SieveLineBreak.nodeType = function () {
+ return "whitespace/";
+}
+
+SieveLineBreak.prototype.init
+ = function (parser)
+{
+ parser.extract("\r\n");
+ return this;
+}
+
+SieveLineBreak.prototype.toScript
+ = function ()
+{
+ return "\r\n";
+}
+
+/******************************************************************************/
+
+
+function SieveDeadCode(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+ this.whiteSpace = "";
+}
+
+SieveDeadCode.isElement = function (parser) {
+ return (parser.isChar([" ","\t"]))
+}
+
+SieveDeadCode.nodeName = function () {
+ return "whitespace/deadcode";
+}
+
+SieveDeadCode.nodeType = function () {
+ return "whitespace/";
+}
+
+SieveDeadCode.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveDeadCode.prototype.init
+ = function (parser)
+{
+ this.whiteSpace = parser.extractToken([" ","\t"]);
+
+ return this;
+}
+
+SieveDeadCode.prototype.toScript
+ = function ()
+{
+ return this.whiteSpace;
+}
+
+/******************************************************************************/
+
+function SieveBracketComment(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+ this.text = "";
+}
+
+SieveBracketComment.isElement = function (parser) {
+ return parser.startsWith("/*");
+}
+
+SieveBracketComment.nodeName = function () {
+ return "whitespace/bracketcomment";
+}
+
+SieveBracketComment.nodeType = function () {
+ return "whitespace/";
+}
+
+SieveBracketComment.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveBracketComment.prototype.init
+ = function (parser)
+{
+ parser.extract("/*")
+
+ this.text = parser.extractUntil("*/");
+
+ return this;
+}
+
+SieveBracketComment.prototype.toScript
+ = function ()
+{
+ return "/*"+this.text+"*/";
+}
+
+/******************************************************************************/
+
+function SieveHashComment(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+ this.text = "";
+}
+
+SieveHashComment.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveHashComment.isElement
+ = function (parser)
+{
+ return parser.isChar("#");
+}
+
+SieveHashComment.nodeName = function () {
+ return "whitespace/hashcomment";
+}
+
+SieveHashComment.nodeType = function () {
+ return "whitespace/";
+}
+
+SieveHashComment.prototype.init
+ = function (parser)
+{
+ parser.extract("#");
+
+ // ... and find the end of the comment
+ this.text = parser.extractUntil("\r\n");
+
+ return this;
+}
+
+SieveHashComment.prototype.toScript
+ = function ()
+{
+ return "#"+this.text+"\r\n";
+}
+
+/******************************************************************************/
+
+function SieveWhiteSpace(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+ this.elements = [];
+}
+
+SieveWhiteSpace.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveWhiteSpace.isElement
+ = function (parser)
+{
+ return SieveLexer.probeByClass(["whitespace/"],parser);
+}
+
+SieveWhiteSpace.nodeName = function () {
+ return "whitespace";
+}
+
+SieveWhiteSpace.nodeType = function () {
+ return "whitespace";
+}
+
+
+/**
+ * Parses a String for whitespace characters. It stops as soon as
+ * it finds the first non whitespace. This means this method extracts
+ * zero or more whitespace characters
+ *
+ *
+ * @param {} data
+ * @param {boolean} crlf
+ * if true linebreaks (\r\n) are not considerd as valid whitespace characters
+ * @return {}
+ */
+
+SieveWhiteSpace.prototype.init
+ = function (parser,crlf)
+{
+ var isCrlf = false;
+ this.elements = [];
+
+ // After the import section only deadcode and actions are valid
+ while (this._probeByClass(["whitespace/"],parser))
+ {
+ // Check for CRLF...
+ if (crlf && this._probeByName("whitespace/linebreak",parser))
+ isCrlf = true;
+
+ this.elements.push(this._createByClass(["whitespace/"],parser));
+
+ // break if we found a CRLF
+ if (isCrlf)
+ break;
+ }
+
+ return this
+}
+
+SieveWhiteSpace.prototype.toScript
+ = function ()
+{
+ var result = "";
+ for (var key in this.elements)
+ result += this.elements[key].toScript();
+
+ return result;
+}
+
+/******************************************************************************/
+
+function SieveSemicolon(docshell,id)
+{
+ SieveAbstractElement.call(this,docshell,id);
+
+ this.whiteSpace = [];
+ this.whiteSpace[0] = this._createByName("whitespace");
+ this.whiteSpace[1] = this._createByName("whitespace","\r\n");
+}
+
+SieveSemicolon.prototype.__proto__ = SieveAbstractElement.prototype;
+
+SieveSemicolon.isElement
+ = function (parser)
+{
+ return true;
+}
+
+SieveSemicolon.nodeName = function () {
+ return "atom/semicolon";
+}
+
+SieveSemicolon.nodeType = function () {
+ return "atom/";
+}
+
+SieveSemicolon.prototype.init
+ = function (parser)
+{
+ // Syntax :
+ // [whitespace] <";"> [whitespace]
+ if (this._probeByName("whitespace",parser))
+ this.whiteSpace[0].init(parser,true);
+
+ parser.extractChar(";");
+
+ this.whiteSpace[1].init(parser,true);
+
+ return this;
+}
+
+SieveSemicolon.prototype.toScript
+ = function ()
+{
+ return this.whiteSpace[0].toScript()+ ";" + this.whiteSpace[1].toScript();
+}
+
+/******************************************************************************/
+
+if (!SieveLexer)
+ throw "Could not register DeadCode Elements";
+
+SieveLexer.register(SieveLineBreak);
+SieveLexer.register(SieveDeadCode);
+SieveLexer.register(SieveBracketComment);
+SieveLexer.register(SieveHashComment);
+
+SieveLexer.register(SieveWhiteSpace);
+
+SieveLexer.register(SieveSemicolon); \ No newline at end of file
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/rfc5228.txt b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/rfc5228.txt
new file mode 100644
index 0000000..e5a02c6
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/rfc5228.txt
@@ -0,0 +1,2355 @@
+
+
+
+
+
+
+Network Working Group P. Guenther, Ed.
+Request for Comments: 5228 Sendmail, Inc.
+Obsoletes: 3028 T. Showalter, Ed.
+Category: Standards Track January 2008
+
+
+ Sieve: An Email Filtering Language
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Abstract
+
+ This document describes a language for filtering email messages at
+ time of final delivery. It is designed to be implementable on either
+ a mail client or mail server. It is meant to be extensible, simple,
+ and independent of access protocol, mail architecture, and operating
+ system. It is suitable for running on a mail server where users may
+ not be allowed to execute arbitrary programs, such as on black box
+ Internet Message Access Protocol (IMAP) servers, as the base language
+ has no variables, loops, or ability to shell out to external
+ programs.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Guenther & Showalter Standards Track [Page 1]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+Table of Contents
+
+ 1. Introduction ....................................................4
+ 1.1. Conventions Used in This Document ..........................4
+ 1.2. Example Mail Messages ......................................5
+ 2. Design ..........................................................6
+ 2.1. Form of the Language .......................................6
+ 2.2. Whitespace .................................................7
+ 2.3. Comments ...................................................7
+ 2.4. Literal Data ...............................................7
+ 2.4.1. Numbers .............................................7
+ 2.4.2. Strings .............................................8
+ 2.4.2.1. String Lists ...............................9
+ 2.4.2.2. Headers ....................................9
+ 2.4.2.3. Addresses .................................10
+ 2.4.2.4. Encoding Characters Using
+ "encoded-character" .......................10
+ 2.5. Tests .....................................................11
+ 2.5.1. Test Lists .........................................12
+ 2.6. Arguments .................................................12
+ 2.6.1. Positional Arguments ...............................12
+ 2.6.2. Tagged Arguments ...................................12
+ 2.6.3. Optional Arguments .................................13
+ 2.6.4. Types of Arguments .................................13
+ 2.7. String Comparison .........................................13
+ 2.7.1. Match Type .........................................14
+ 2.7.2. Comparisons across Character Sets ..................15
+ 2.7.3. Comparators ........................................15
+ 2.7.4. Comparisons against Addresses ......................16
+ 2.8. Blocks ....................................................17
+ 2.9. Commands ..................................................17
+ 2.10. Evaluation ...............................................18
+ 2.10.1. Action Interaction ................................18
+ 2.10.2. Implicit Keep .....................................18
+ 2.10.3. Message Uniqueness in a Mailbox ...................19
+ 2.10.4. Limits on Numbers of Actions ......................19
+ 2.10.5. Extensions and Optional Features ..................19
+ 2.10.6. Errors ............................................20
+ 2.10.7. Limits on Execution ...............................20
+ 3. Control Commands ...............................................21
+ 3.1. Control if ................................................21
+ 3.2. Control require ...........................................22
+ 3.3. Control stop ..............................................22
+ 4. Action Commands ................................................23
+ 4.1. Action fileinto ...........................................23
+ 4.2. Action redirect ...........................................23
+ 4.3. Action keep ...............................................24
+ 4.4. Action discard ............................................25
+
+
+
+Guenther & Showalter Standards Track [Page 2]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ 5. Test Commands ..................................................26
+ 5.1. Test address ..............................................26
+ 5.2. Test allof ................................................27
+ 5.3. Test anyof ................................................27
+ 5.4. Test envelope .............................................27
+ 5.5. Test exists ...............................................28
+ 5.6. Test false ................................................28
+ 5.7. Test header ...............................................29
+ 5.8. Test not ..................................................29
+ 5.9. Test size .................................................29
+ 5.10. Test true ................................................30
+ 6. Extensibility ..................................................30
+ 6.1. Capability String .........................................31
+ 6.2. IANA Considerations .......................................31
+ 6.2.1. Template for Capability Registrations ..............32
+ 6.2.2. Handling of Existing Capability Registrations ......32
+ 6.2.3. Initial Capability Registrations ...................32
+ 6.3. Capability Transport ......................................33
+ 7. Transmission ...................................................33
+ 8. Parsing ........................................................34
+ 8.1. Lexical Tokens ............................................34
+ 8.2. Grammar ...................................................36
+ 8.3. Statement Elements ........................................36
+ 9. Extended Example ...............................................37
+ 10. Security Considerations .......................................38
+ 11. Acknowledgments ...............................................39
+ 12. Normative References ..........................................39
+ 13. Informative References ........................................40
+ 14. Changes from RFC 3028 .........................................41
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Guenther & Showalter Standards Track [Page 3]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+1. Introduction
+
+ This memo documents a language that can be used to create filters for
+ electronic mail. It is not tied to any particular operating system
+ or mail architecture. It requires the use of [IMAIL]-compliant
+ messages, but should otherwise generalize to many systems.
+
+ The language is powerful enough to be useful but limited in order to
+ allow for a safe server-side filtering system. The intention is to
+ make it impossible for users to do anything more complex (and
+ dangerous) than write simple mail filters, along with facilitating
+ the use of graphical user interfaces (GUIs) for filter creation and
+ manipulation. The base language was not designed to be Turing-
+ complete: it does not have a loop control structure or functions.
+
+ Scripts written in Sieve are executed during final delivery, when the
+ message is moved to the user-accessible mailbox. In systems where
+ the Mail Transfer Agent (MTA) does final delivery, such as
+ traditional Unix mail, it is reasonable to filter when the MTA
+ deposits mail into the user's mailbox.
+
+ There are a number of reasons to use a filtering system. Mail
+ traffic for most users has been increasing due to increased usage of
+ email, the emergence of unsolicited email as a form of advertising,
+ and increased usage of mailing lists.
+
+ Experience at Carnegie Mellon has shown that if a filtering system is
+ made available to users, many will make use of it in order to file
+ messages from specific users or mailing lists. However, many others
+ did not make use of the Andrew system's FLAMES filtering language
+ [FLAMES] due to difficulty in setting it up.
+
+ Because of the expectation that users will make use of filtering if
+ it is offered and easy to use, this language has been made simple
+ enough to allow many users to make use of it, but rich enough that it
+ can be used productively. However, it is expected that GUI-based
+ editors will be the preferred way of editing filters for a large
+ number of users.
+
+1.1. Conventions Used in This Document
+
+ In the sections of this document that discuss the requirements of
+ various keywords and operators, the following conventions have been
+ adopted.
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [KEYWORDS].
+
+
+
+Guenther & Showalter Standards Track [Page 4]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ Each section on a command (test, action, or control) has a line
+ labeled "Usage:". This line describes the usage of the command,
+ including its name and its arguments. Required arguments are listed
+ inside angle brackets ("<" and ">"). Optional arguments are listed
+ inside square brackets ("[" and "]"). Each argument is followed by
+ its type, so "<key: string>" represents an argument called "key" that
+ is a string. Literal strings are represented with double-quoted
+ strings. Alternatives are separated with slashes, and parentheses
+ are used for grouping, similar to [ABNF].
+
+ In the "Usage:" line, there are three special pieces of syntax that
+ are frequently repeated, MATCH-TYPE, COMPARATOR, and ADDRESS-PART.
+ These are discussed in sections 2.7.1, 2.7.3, and 2.7.4,
+ respectively.
+
+ The formal grammar for these commands is defined in section 8 and is
+ the authoritative reference on how to construct commands, but the
+ formal grammar does not specify the order, semantics, number or types
+ of arguments to commands, or the legal command names. The intent is
+ to allow for extension without changing the grammar.
+
+1.2. Example Mail Messages
+
+ The following mail messages will be used throughout this document in
+ examples.
+
+ Message A
+ -----------------------------------------------------------
+ Date: Tue, 1 Apr 1997 09:06:31 -0800 (PST)
+ From: coyote@desert.example.org
+ To: roadrunner@acme.example.com
+ Subject: I have a present for you
+
+ Look, I'm sorry about the whole anvil thing, and I really
+ didn't mean to try and drop it on you from the top of the
+ cliff. I want to try to make it up to you. I've got some
+ great birdseed over here at my place--top of the line
+ stuff--and if you come by, I'll have it all wrapped up
+ for you. I'm really sorry for all the problems I've caused
+ for you over the years, but I know we can work this out.
+ --
+ Wile E. Coyote "Super Genius" coyote@desert.example.org
+ -----------------------------------------------------------
+
+
+
+
+
+
+
+
+Guenther & Showalter Standards Track [Page 5]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ Message B
+ -----------------------------------------------------------
+ From: youcouldberich!@reply-by-postal-mail.invalid
+ Sender: b1ff@de.res.example.com
+ To: rube@landru.example.com
+ Date: Mon, 31 Mar 1997 18:26:10 -0800
+ Subject: $$$ YOU, TOO, CAN BE A MILLIONAIRE! $$$
+
+ YOU MAY HAVE ALREADY WON TEN MILLION DOLLARS, BUT I DOUBT
+ IT! SO JUST POST THIS TO SIX HUNDRED NEWSGROUPS! IT WILL
+ GUARANTEE THAT YOU GET AT LEAST FIVE RESPONSES WITH MONEY!
+ MONEY! MONEY! COLD HARD CASH! YOU WILL RECEIVE OVER
+ $20,000 IN LESS THAN TWO MONTHS! AND IT'S LEGAL!!!!!!!!!
+ !!!!!!!!!!!!!!!!!!111111111!!!!!!!11111111111!!1 JUST
+ SEND $5 IN SMALL, UNMARKED BILLS TO THE ADDRESSES BELOW!
+ -----------------------------------------------------------
+
+2. Design
+
+2.1. Form of the Language
+
+ The language consists of a set of commands. Each command consists of
+ a set of tokens delimited by whitespace. The command identifier is
+ the first token and it is followed by zero or more argument tokens.
+ Arguments may be literal data, tags, blocks of commands, or test
+ commands.
+
+ With the exceptions of strings and comments, the language is limited
+ to US-ASCII characters. Strings and comments may contain octets
+ outside the US-ASCII range. Specifically, they will normally be in
+ UTF-8, as specified in [UTF-8]. NUL (US-ASCII 0) is never permitted
+ in scripts, while CR and LF can only appear as the CRLF line ending.
+
+ Note: While this specification permits arbitrary octets to appear
+ in Sieve scripts inside strings and comments, this has made it
+ difficult to robustly handle Sieve scripts in programs that are
+ sensitive to the encodings used. The "encoded-character"
+ capability (section 2.4.2.4) provides an alternative means of
+ representing such octets in strings using just US-ASCII
+ characters. As such, the use of non-UTF-8 text in scripts should
+ be considered a deprecated feature that may be abandoned.
+
+ Tokens other than strings are considered case-insensitive.
+
+
+
+
+
+
+
+
+Guenther & Showalter Standards Track [Page 6]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+2.2. Whitespace
+
+ Whitespace is used to separate tokens. Whitespace is made up of
+ tabs, newlines (CRLF, never just CR or LF), and the space character.
+ The amount of whitespace used is not significant.
+
+2.3. Comments
+
+ Two types of comments are offered. Comments are semantically
+ equivalent to whitespace and can be used anyplace that whitespace is
+ (with one exception in multi-line strings, as described in the
+ grammar).
+
+ Hash comments begin with a "#" character that is not contained within
+ a string and continue until the next CRLF.
+
+ Example: if size :over 100k { # this is a comment
+ discard;
+ }
+
+ Bracketed comments begin with the token "/*" and end with "*/"
+ outside of a string. Bracketed comments may span multiple lines.
+ Bracketed comments do not nest.
+
+ Example: if size :over 100K { /* this is a comment
+ this is still a comment */ discard /* this is a comment
+ */ ;
+ }
+
+2.4. Literal Data
+
+ Literal data means data that is not executed, merely evaluated "as
+ is", to be used as arguments to commands. Literal data is limited to
+ numbers, strings, and string lists.
+
+2.4.1. Numbers
+
+ Numbers are given as ordinary decimal numbers. As a shorthand for
+ expressing larger values, such as message sizes, a suffix of "K",
+ "M", or "G" MAY be appended to indicate a multiple of a power of two.
+ To be comparable with the power-of-two-based versions of SI units
+ that computers frequently use, "K" specifies kibi-, or 1,024 (2^10)
+ times the value of the number; "M" specifies mebi-, or 1,048,576
+ (2^20) times the value of the number; and "G" specifies gibi-, or
+ 1,073,741,824 (2^30) times the value of the number [BINARY-SI].
+
+
+
+
+
+
+Guenther & Showalter Standards Track [Page 7]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ Implementations MUST support integer values in the inclusive range
+ zero to 2,147,483,647 (2^31 - 1), but MAY support larger values.
+
+ Only non-negative integers are permitted by this specification.
+
+2.4.2. Strings
+
+ Scripts involve large numbers of string values as they are used for
+ pattern matching, addresses, textual bodies, etc. Typically, short
+ quoted strings suffice for most uses, but a more convenient form is
+ provided for longer strings such as bodies of messages.
+
+ A quoted string starts and ends with a single double quote (the <">
+ character, US-ASCII 34). A backslash ("\", US-ASCII 92) inside of a
+ quoted string is followed by either another backslash or a double
+ quote. These two-character sequences represent a single backslash or
+ double quote within the value, respectively.
+
+ Scripts SHOULD NOT escape other characters with a backslash.
+
+ An undefined escape sequence (such as "\a" in a context where "a" has
+ no special meaning) is interpreted as if there were no backslash (in
+ this case, "\a" is just "a"), though that may be changed by
+ extensions.
+
+ Non-printing characters such as tabs, CRLF, and control characters
+ are permitted in quoted strings. Quoted strings MAY span multiple
+ lines. An unencoded NUL (US-ASCII 0) is not allowed in strings; see
+ section 2.4.2.4 for how it can be encoded.
+
+ As message header data is converted to [UTF-8] for comparison (see
+ section 2.7.2), most string values will use the UTF-8 encoding.
+ However, implementations MUST accept all strings that match the
+ grammar in section 8. The ability to use non-UTF-8 encoded strings
+ matches existing practice and has proven to be useful both in tests
+ for invalid data and in arguments containing raw MIME parts for
+ extension actions that generate outgoing messages.
+
+ For entering larger amounts of text, such as an email message, a
+ multi-line form is allowed. It starts with the keyword "text:",
+ followed by a CRLF, and ends with the sequence of a CRLF, a single
+ period, and another CRLF. The CRLF before the final period is
+ considered part of the value. In order to allow the message to
+ contain lines with a single dot, lines are dot-stuffed. That is,
+ when composing a message body, an extra '.' is added before each line
+ that begins with a '.'. When the server interprets the script, these
+ extra dots are removed. Note that a line that begins with a dot
+ followed by a non-dot character is not interpreted as dot-stuffed;
+
+
+
+Guenther & Showalter Standards Track [Page 8]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ that is, ".foo" is interpreted as ".foo". However, because this is
+ potentially ambiguous, scripts SHOULD be properly dot-stuffed so such
+ lines do not appear.
+
+ Note that a hashed comment or whitespace may occur in between the
+ "text:" and the CRLF, but not within the string itself. Bracketed
+ comments are not allowed here.
+
+2.4.2.1. String Lists
+
+ When matching patterns, it is frequently convenient to match against
+ groups of strings instead of single strings. For this reason, a list
+ of strings is allowed in many tests, implying that if the test is
+ true using any one of the strings, then the test is true.
+
+ For instance, the test 'header :contains ["To", "Cc"]
+ ["me@example.com", "me00@landru.example.com"]' is true if either a To
+ header or Cc header of the input message contains either of the email
+ addresses "me@example.com" or "me00@landru.example.com".
+
+ Conversely, in any case where a list of strings is appropriate, a
+ single string is allowed without being a member of a list: it is
+ equivalent to a list with a single member. This means that the test
+ 'exists "To"' is equivalent to the test 'exists ["To"]'.
+
+2.4.2.2. Headers
+
+ Headers are a subset of strings. In the Internet Message
+ Specification [IMAIL], each header line is allowed to have whitespace
+ nearly anywhere in the line, including after the field name and
+ before the subsequent colon. Extra spaces between the header name
+ and the ":" in a header field are ignored.
+
+ A header name never contains a colon. The "From" header refers to a
+ line beginning "From:" (or "From :", etc.). No header will match
+ the string "From:" due to the trailing colon.
+
+ Similarly, no header will match a syntactically invalid header name.
+ An implementation MUST NOT cause an error for syntactically invalid
+ header names in tests.
+
+ Header lines are unfolded as described in [IMAIL] section 2.2.3.
+ Interpretation of header data SHOULD be done according to [MIME3]
+ section 6.2 (see section 2.7.2 below for details).
+
+
+
+
+
+
+
+Guenther & Showalter Standards Track [Page 9]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+2.4.2.3. Addresses
+
+ A number of commands call for email addresses, which are also a
+ subset of strings. When these addresses are used in outbound
+ contexts, addresses must be compliant with [IMAIL], but are further
+ constrained within this document. Using the symbols defined in
+ [IMAIL], section 3, the syntax of an address is:
+
+ sieve-address = addr-spec ; simple address
+ / phrase "<" addr-spec ">" ; name & addr-spec
+
+ That is, routes and group syntax are not permitted. If multiple
+ addresses are required, use a string list. Named groups are not
+ permitted.
+
+ It is an error for a script to execute an action with a value for use
+ as an outbound address that doesn't match the "sieve-address" syntax.
+
+2.4.2.4. Encoding Characters Using "encoded-character"
+
+ When the "encoded-character" extension is in effect, certain
+ character sequences in strings are replaced by their decoded value.
+ This happens after escape sequences are interpreted and dot-
+ unstuffing has been done. Implementations SHOULD support "encoded-
+ character".
+
+ Arbitrary octets can be embedded in strings by using the syntax
+ encoded-arb-octets. The sequence is replaced by the octets with the
+ hexadecimal values given by each hex-pair.
+
+ blank = WSP / CRLF
+ encoded-arb-octets = "${hex:" hex-pair-seq "}"
+ hex-pair-seq = *blank hex-pair *(1*blank hex-pair) *blank
+ hex-pair = 1*2HEXDIG
+
+ Where WSP and HEXDIG non-terminals are defined in Appendix B.1 of
+ [ABNF].
+
+ It may be inconvenient or undesirable to enter Unicode characters
+ verbatim, and for these cases the syntax encoded-unicode-char can be
+ used. The sequence is replaced by the UTF-8 encoding of the
+ specified Unicode characters, which are identified by the hexadecimal
+ value of unicode-hex.
+
+ encoded-unicode-char = "${unicode:" unicode-hex-seq "}"
+ unicode-hex-seq = *blank unicode-hex
+ *(1*blank unicode-hex) *blank
+ unicode-hex = 1*HEXDIG
+
+
+
+Guenther & Showalter Standards Track [Page 10]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ It is an error for a script to use a hexadecimal value that isn't in
+ either the range 0 to D7FF or the range E000 to 10FFFF. (The range
+ D800 to DFFF is excluded as those character numbers are only used as
+ part of the UTF-16 encoding form and are not applicable to the UTF-8
+ encoding that the syntax here represents.)
+
+ Note: Implementations MUST NOT raise an error for an out-of-range
+ Unicode value unless the sequence containing it is well-formed
+ according to the grammar.
+
+ The capability string for use with the require command is "encoded-
+ character".
+
+ In the following script, message B is discarded, since the specified
+ test string is equivalent to "$$$".
+
+ Example: require "encoded-character";
+ if header :contains "Subject" "$${hex:24 24}" {
+ discard;
+ }
+ The following examples demonstrate valid and invalid encodings and
+ how they are handled:
+
+ "$${hex:40}" -> "$@"
+ "${hex: 40 }" -> "@"
+ "${HEX: 40}" -> "@"
+ "${hex:40" -> "${hex:40"
+ "${hex:400}" -> "${hex:400}"
+ "${hex:4${hex:30}}" -> "${hex:40}"
+ "${unicode:40}" -> "@"
+ "${ unicode:40}" -> "${ unicode:40}"
+ "${UNICODE:40}" -> "@"
+ "${UnICoDE:0000040}" -> "@"
+ "${Unicode:40}" -> "@"
+ "${Unicode:Cool}" -> "${Unicode:Cool}"
+ "${unicode:200000}" -> error
+ "${Unicode:DF01} -> error
+
+2.5. Tests
+
+ Tests are given as arguments to commands in order to control their
+ actions. In this document, tests are given to if/elsif to decide
+ which block of code is run.
+
+
+
+
+
+
+
+
+Guenther & Showalter Standards Track [Page 11]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+2.5.1. Test Lists
+
+ Some tests ("allof" and "anyof", which implement logical "and" and
+ logical "or", respectively) may require more than a single test as an
+ argument. The test-list syntax element provides a way of grouping
+ tests as a comma-separated list in parentheses.
+
+ Example: if anyof (not exists ["From", "Date"],
+ header :contains "from" "fool@example.com") {
+ discard;
+ }
+
+2.6. Arguments
+
+ In order to specify what to do, most commands take arguments. There
+ are three types of arguments: positional, tagged, and optional.
+
+ It is an error for a script, on a single command, to use conflicting
+ arguments or to use a tagged or optional argument more than once.
+
+2.6.1. Positional Arguments
+
+ Positional arguments are given to a command that discerns their
+ meaning based on their order. When a command takes positional
+ arguments, all positional arguments must be supplied and must be in
+ the order prescribed.
+
+2.6.2. Tagged Arguments
+
+ This document provides for tagged arguments in the style of
+ CommonLISP. These are also similar to flags given to commands in
+ most command-line systems.
+
+ A tagged argument is an argument for a command that begins with ":"
+ followed by a tag naming the argument, such as ":contains". This
+ argument means that zero or more of the next tokens have some
+ particular meaning depending on the argument. These next tokens may
+ be literal data, but they are never blocks.
+
+ Tagged arguments are similar to positional arguments, except that
+ instead of the meaning being derived from the command, it is derived
+ from the tag.
+
+ Tagged arguments must appear before positional arguments, but they
+ may appear in any order with other tagged arguments. For simplicity
+ of the specification, this is not expressed in the syntax definitions
+
+
+
+
+
+Guenther & Showalter Standards Track [Page 12]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ with commands, but they still may be reordered arbitrarily provided
+ they appear before positional arguments. Tagged arguments may be
+ mixed with optional arguments.
+
+ Tagged arguments SHOULD NOT take tagged arguments as arguments.
+
+2.6.3. Optional Arguments
+
+ Optional arguments are exactly like tagged arguments except that they
+ may be left out, in which case a default value is implied. Because
+ optional arguments tend to result in shorter scripts, they have been
+ used far more than tagged arguments.
+
+ One particularly noteworthy case is the ":comparator" argument, which
+ allows the user to specify which comparator [COLLATION] will be used
+ to compare two strings, since different languages may impose
+ different orderings on UTF-8 [UTF-8] strings.
+
+2.6.4. Types of Arguments
+
+ Abstractly, arguments may be literal data, tests, or blocks of
+ commands. In this way, an "if" control structure is merely a command
+ that happens to take a test and a block as arguments and may execute
+ the block of code.
+
+ However, this abstraction is ambiguous from a parsing standpoint.
+
+ The grammar in section 8.2 presents a parsable version of this:
+ Arguments are string lists (string-lists), numbers, and tags, which
+ may be followed by a test or a test list (test-list), which may be
+ followed by a block of commands. No more than one test or test list,
+ or more than one block of commands, may be used, and commands that
+ end with a block of commands do not end with semicolons.
+
+2.7. String Comparison
+
+ When matching one string against another, there are a number of ways
+ of performing the match operation. These are accomplished with three
+ types of matches: an exact match, a substring match, and a wildcard
+ glob-style match. These are described below.
+
+ In order to provide for matches between character sets and case
+ insensitivity, Sieve uses the comparators defined in the Internet
+ Application Protocol Collation Registry [COLLATION].
+
+
+
+
+
+
+
+Guenther & Showalter Standards Track [Page 13]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ However, when a string represents the name of a header, the
+ comparator is never user-specified. Header comparisons are always
+ done with the "i;ascii-casemap" operator, i.e., case-insensitive
+ comparisons, because this is the way things are defined in the
+ message specification [IMAIL].
+
+2.7.1. Match Type
+
+ Commands that perform string comparisons may have an optional match
+ type argument. The three match types in this specification are
+ ":contains", ":is", and ":matches".
+
+ The ":contains" match type describes a substring match. If the value
+ argument contains the key argument as a substring, the match is true.
+ For instance, the string "frobnitzm" contains "frob" and "nit", but
+ not "fbm". The empty key ("") is contained in all values.
+
+ The ":is" match type describes an absolute match; if the contents of
+ the first string are absolutely the same as the contents of the
+ second string, they match. Only the string "frobnitzm" is the string
+ "frobnitzm". The empty key ("") only ":is" matches with the empty
+ value.
+
+ The ":matches" match type specifies a wildcard match using the
+ characters "*" and "?"; the entire value must be matched. "*"
+ matches zero or more characters in the value and "?" matches a single
+ character in the value, where the comparator that is used (see
+ section 2.7.3) defines what a character is. For example, the
+ comparators "i;octet" and "i;ascii-casemap" define a character to be
+ a single octet, so "?" will always match exactly one octet when one
+ of those comparators is in use. In contrast, a Unicode-based
+ comparator would define a character to be any UTF-8 octet sequence
+ encoding one Unicode character and thus "?" may match more than one
+ octet. "?" and "*" may be escaped as "\\?" and "\\*" in strings to
+ match against themselves. The first backslash escapes the second
+ backslash; together, they escape the "*". This is awkward, but it is
+ commonplace in several programming languages that use globs and
+ regular expressions.
+
+ In order to specify what type of match is supposed to happen,
+ commands that support matching take optional arguments ":matches",
+ ":is", and ":contains". Commands default to using ":is" matching if
+ no match type argument is supplied. Note that these modifiers
+ interact with comparators; in particular, only comparators that
+ support the "substring match" operation are suitable for matching
+ with ":contains" or ":matches". It is an error to use a comparator
+ with ":contains" or ":matches" that is not compatible with it.
+
+
+
+
+Guenther & Showalter Standards Track [Page 14]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ It is an error to give more than one of these arguments to a given
+ command.
+
+ For convenience, the "MATCH-TYPE" syntax element is defined here as
+ follows:
+
+ Syntax: ":is" / ":contains" / ":matches"
+
+2.7.2. Comparisons across Character Sets
+
+ Messages may involve a number of character sets. In order for
+ comparisons to work across character sets, implementations SHOULD
+ implement the following behavior:
+
+ Comparisons are performed on octets. Implementations convert text
+ from header fields in all charsets [MIME3] to Unicode, encoded as
+ UTF-8, as input to the comparator (see section 2.7.3).
+ Implementations MUST be capable of converting US-ASCII, ISO-8859-
+ 1, the US-ASCII subset of ISO-8859-* character sets, and UTF-8.
+ Text that the implementation cannot convert to Unicode for any
+ reason MAY be treated as plain US-ASCII (including any [MIME3]
+ syntax) or processed according to local conventions. An encoded
+ NUL octet (character zero) SHOULD NOT cause early termination of
+ the header content being compared against.
+
+ If implementations fail to support the above behavior, they MUST
+ conform to the following:
+
+ No two strings can be considered equal if one contains octets
+ greater than 127.
+
+2.7.3. Comparators
+
+ In order to allow for language-independent, case-independent matches,
+ the match type may be coupled with a comparator name. The Internet
+ Application Protocol Collation Registry [COLLATION] provides the
+ framework for describing and naming comparators.
+
+ All implementations MUST support the "i;octet" comparator (simply
+ compares octets) and the "i;ascii-casemap" comparator (which treats
+ uppercase and lowercase characters in the US-ASCII subset of UTF-8 as
+ the same). If left unspecified, the default is "i;ascii-casemap".
+
+ Some comparators may not be usable with substring matches; that is,
+ they may only work with ":is". It is an error to try to use a
+ comparator with ":matches" or ":contains" that is not compatible with
+ it.
+
+
+
+
+Guenther & Showalter Standards Track [Page 15]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ Sieve treats a comparator result of "undefined" the same as a result
+ of "no-match". That is, this base specification does not provide any
+ means to directly detect invalid comparator input.
+
+ A comparator is specified by the ":comparator" option with commands
+ that support matching. This option is followed by a string providing
+ the name of the comparator to be used. For convenience, the syntax
+ of a comparator is abbreviated to "COMPARATOR", and (repeated in
+ several tests) is as follows:
+
+ Syntax: ":comparator" <comparator-name: string>
+
+ So in this example,
+
+ Example: if header :contains :comparator "i;octet" "Subject"
+ "MAKE MONEY FAST" {
+ discard;
+ }
+
+ would discard any message with subjects like "You can MAKE MONEY
+ FAST", but not "You can Make Money Fast", since the comparator used
+ is case-sensitive.
+
+ Comparators other than "i;octet" and "i;ascii-casemap" must be
+ declared with require, as they are extensions. If a comparator
+ declared with require is not known, it is an error, and execution
+ fails. If the comparator is not declared with require, it is also an
+ error, even if the comparator is supported. (See section 2.10.5.)
+
+ Both ":matches" and ":contains" match types are compatible with the
+ "i;octet" and "i;ascii-casemap" comparators and may be used with
+ them.
+
+ It is an error to give more than one of these arguments to a given
+ command.
+
+2.7.4. Comparisons against Addresses
+
+ Addresses are one of the most frequent things represented as strings.
+ These are structured, and being able to compare against the local-
+ part or the domain of an address is useful, so some tests that act
+ exclusively on addresses take an additional optional argument that
+ specifies what the test acts on.
+
+ These optional arguments are ":localpart", ":domain", and ":all",
+ which act on the local-part (left side), the domain-part (right
+ side), and the whole address.
+
+
+
+
+Guenther & Showalter Standards Track [Page 16]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ If an address is not syntactically valid, then it will not be matched
+ by tests specifying ":localpart" or ":domain".
+
+ The kind of comparison done, such as whether or not the test done is
+ case-insensitive, is specified as a comparator argument to the test.
+
+ If an optional address-part is omitted, the default is ":all".
+
+ It is an error to give more than one of these arguments to a given
+ command.
+
+ For convenience, the "ADDRESS-PART" syntax element is defined here as
+ follows:
+
+ Syntax: ":localpart" / ":domain" / ":all"
+
+2.8. Blocks
+
+ Blocks are sets of commands enclosed within curly braces and supplied
+ as the final argument to a command. Such a command is a control
+ structure: when executed it has control over the number of times the
+ commands in the block are executed.
+
+ With the commands supplied in this memo, there are no loops. The
+ control structures supplied--if, elsif, and else--run a block either
+ once or not at all.
+
+2.9. Commands
+
+ Sieve scripts are sequences of commands. Commands can take any of
+ the tokens above as arguments, and arguments may be either tagged or
+ positional arguments. Not all commands take all arguments.
+
+ There are three kinds of commands: test commands, action commands,
+ and control commands.
+
+ The simplest is an action command. An action command is an
+ identifier followed by zero or more arguments, terminated by a
+ semicolon. Action commands do not take tests or blocks as arguments.
+ The actions referenced in this document are:
+
+ - keep, to save the message in the default location
+ - fileinto, to save the message in a specific mailbox
+ - redirect, to forward the message to another address
+ - discard, to silently throw away the message
+
+
+
+
+
+
+Guenther & Showalter Standards Track [Page 17]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ A control command is a command that affects the parsing or the flow
+ of execution of the Sieve script in some way. A control structure is
+ a control command that ends with a block instead of a semicolon.
+
+ A test command is used as part of a control command. It is used to
+ specify whether or not the block of code given to the control command
+ is executed.
+
+2.10. Evaluation
+
+2.10.1. Action Interaction
+
+ Some actions cannot be used with other actions because the result
+ would be absurd. These restrictions are noted throughout this memo.
+
+ Extension actions MUST state how they interact with actions defined
+ in this specification.
+
+2.10.2. Implicit Keep
+
+ Previous experience with filtering systems suggests that cases tend
+ to be missed in scripts. To prevent errors, Sieve has an "implicit
+ keep".
+
+ An implicit keep is a keep action (see section 4.3) performed in
+ absence of any action that cancels the implicit keep.
+
+ An implicit keep is performed if a message is not written to a
+ mailbox, redirected to a new address, or explicitly thrown out. That
+ is, if a fileinto, a keep, a redirect, or a discard is performed, an
+ implicit keep is not.
+
+ Some actions may be defined to not cancel the implicit keep. These
+ actions may not directly affect the delivery of a message, and are
+ used for their side effects. None of the actions specified in this
+ document meet that criteria, but extension actions may.
+
+ For instance, with any of the short messages offered above, the
+ following script produces no actions.
+
+ Example: if size :over 500K { discard; }
+
+ As a result, the implicit keep is taken.
+
+
+
+
+
+
+
+
+Guenther & Showalter Standards Track [Page 18]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+2.10.3. Message Uniqueness in a Mailbox
+
+ Implementations SHOULD NOT deliver a message to the same mailbox more
+ than once, even if a script explicitly asks for a message to be
+ written to a mailbox twice.
+
+ The test for equality of two messages is implementation-defined.
+
+ If a script asks for a message to be written to a mailbox twice, it
+ MUST NOT be treated as an error.
+
+2.10.4. Limits on Numbers of Actions
+
+ Site policy MAY limit the number of actions taken and MAY impose
+ restrictions on which actions can be used together. In the event
+ that a script hits a policy limit on the number of actions taken for
+ a particular message, an error occurs.
+
+ Implementations MUST allow at least one keep or one fileinto. If
+ fileinto is not implemented, implementations MUST allow at least one
+ keep.
+
+2.10.5. Extensions and Optional Features
+
+ Because of the differing capabilities of many mail systems, several
+ features of this specification are optional. Before any of these
+ extensions can be executed, they must be declared with the "require"
+ action.
+
+ If an extension is not enabled with "require", implementations MUST
+ treat it as if they did not support it at all. This protects scripts
+ from having their behavior altered by extensions that the script
+ author might not have even been aware of.
+
+ Implementations MUST NOT execute any Sieve script test or command
+ subsequent to "require" if one of the required extensions is
+ unavailable.
+
+ Note: The reason for this restriction is that prior experiences
+ with languages such as LISP and Tcl suggest that this is a
+ workable way of noting that a given script uses an extension.
+
+ Extensions that define actions MUST state how they interact with
+ actions discussed in the base specification.
+
+
+
+
+
+
+
+Guenther & Showalter Standards Track [Page 19]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+2.10.6. Errors
+
+ In any programming language, there are compile-time and run-time
+ errors.
+
+ Compile-time errors are ones in syntax that are detectable if a
+ syntax check is done.
+
+ Run-time errors are not detectable until the script is run. This
+ includes transient failures like disk full conditions, but also
+ includes issues like invalid combinations of actions.
+
+ When an error occurs in a Sieve script, all processing stops.
+
+ Implementations MAY choose to do a full parse, then evaluate the
+ script, then do all actions. Implementations might even go so far as
+ to ensure that execution is atomic (either all actions are executed
+ or none are executed).
+
+ Other implementations may choose to parse and run at the same time.
+ Such implementations are simpler, but have issues with partial
+ failure (some actions happen, others don't).
+
+ Implementations MUST perform syntactic, semantic, and run-time checks
+ on code that is actually executed. Implementations MAY perform those
+ checks or any part of them on code that is not reached during
+ execution.
+
+ When an error happens, implementations MUST notify the user that an
+ error occurred and which actions (if any) were taken, and do an
+ implicit keep.
+
+2.10.7. Limits on Execution
+
+ Implementations may limit certain constructs. However, this
+ specification places a lower bound on some of these limits.
+
+ Implementations MUST support fifteen levels of nested blocks.
+
+ Implementations MUST support fifteen levels of nested test lists.
+
+
+
+
+
+
+
+
+
+
+
+Guenther & Showalter Standards Track [Page 20]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+3. Control Commands
+
+ Control structures are needed to allow for multiple and conditional
+ actions.
+
+3.1. Control if
+
+ There are three pieces to if: "if", "elsif", and "else". Each is
+ actually a separate command in terms of the grammar. However, an
+ elsif or else MUST only follow an if or elsif. An error occurs if
+ these conditions are not met.
+
+ Usage: if <test1: test> <block1: block>
+
+ Usage: elsif <test2: test> <block2: block>
+
+ Usage: else <block3: block>
+
+ The semantics are similar to those of any of the many other
+ programming languages these control structures appear in. When the
+ interpreter sees an "if", it evaluates the test associated with it.
+ If the test is true, it executes the block associated with it.
+
+ If the test of the "if" is false, it evaluates the test of the first
+ "elsif" (if any). If the test of "elsif" is true, it runs the
+ elsif's block. An elsif may be followed by an elsif, in which case,
+ the interpreter repeats this process until it runs out of elsifs.
+
+ When the interpreter runs out of elsifs, there may be an "else" case.
+ If there is, and none of the if or elsif tests were true, the
+ interpreter runs the else's block.
+
+ This provides a way of performing exactly one of the blocks in the
+ chain.
+
+ In the following example, both messages A and B are dropped.
+
+ Example: require "fileinto";
+ if header :contains "from" "coyote" {
+ discard;
+ } elsif header :contains ["subject"] ["$$$"] {
+ discard;
+ } else {
+ fileinto "INBOX";
+ }
+
+
+
+
+
+
+Guenther & Showalter Standards Track [Page 21]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ When the script below is run over message A, it redirects the message
+ to acm@example.com; message B, to postmaster@example.com; any other
+ message is redirected to field@example.com.
+
+ Example: if header :contains ["From"] ["coyote"] {
+ redirect "acm@example.com";
+ } elsif header :contains "Subject" "$$$" {
+ redirect "postmaster@example.com";
+ } else {
+ redirect "field@example.com";
+ }
+
+ Note that this definition prohibits the "... else if ..." sequence
+ used by C. This is intentional, because this construct produces a
+ shift-reduce conflict.
+
+3.2. Control require
+
+ Usage: require <capabilities: string-list>
+
+ The require action notes that a script makes use of a certain
+ extension. Such a declaration is required to use the extension, as
+ discussed in section 2.10.5. Multiple capabilities can be declared
+ with a single require.
+
+ The require command, if present, MUST be used before anything other
+ than a require can be used. An error occurs if a require appears
+ after a command other than require.
+
+ Example: require ["fileinto", "reject"];
+
+ Example: require "fileinto";
+ require "vacation";
+
+3.3. Control stop
+
+ Usage: stop
+
+ The "stop" action ends all processing. If the implicit keep has not
+ been cancelled, then it is taken.
+
+
+
+
+
+
+
+
+
+
+
+Guenther & Showalter Standards Track [Page 22]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+4. Action Commands
+
+ This document supplies four actions that may be taken on a message:
+ keep, fileinto, redirect, and discard.
+
+ Implementations MUST support the "keep", "discard", and "redirect"
+ actions.
+
+ Implementations SHOULD support "fileinto".
+
+ Implementations MAY limit the number of certain actions taken (see
+ section 2.10.4).
+
+4.1. Action fileinto
+
+ Usage: fileinto <mailbox: string>
+
+ The "fileinto" action delivers the message into the specified
+ mailbox. Implementations SHOULD support fileinto, but in some
+ environments this may be impossible. Implementations MAY place
+ restrictions on mailbox names; use of an invalid mailbox name MAY be
+ treated as an error or result in delivery to an implementation-
+ defined mailbox. If the specified mailbox doesn't exist, the
+ implementation MAY treat it as an error, create the mailbox, or
+ deliver the message to an implementation-defined mailbox. If the
+ implementation uses a different encoding scheme than UTF-8 for
+ mailbox names, it SHOULD reencode the mailbox name from UTF-8 to its
+ encoding scheme. For example, the Internet Message Access Protocol
+ [IMAP] uses modified UTF-7, such that a mailbox argument of "odds &
+ ends" would appear in IMAP as "odds &- ends".
+
+ The capability string for use with the require command is "fileinto".
+
+ In the following script, message A is filed into mailbox
+ "INBOX.harassment".
+
+ Example: require "fileinto";
+ if header :contains ["from"] "coyote" {
+ fileinto "INBOX.harassment";
+ }
+
+4.2. Action redirect
+
+ Usage: redirect <address: string>
+
+ The "redirect" action is used to send the message to another user at
+ a supplied address, as a mail forwarding feature does. The
+ "redirect" action makes no changes to the message body or existing
+
+
+
+Guenther & Showalter Standards Track [Page 23]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ headers, but it may add new headers. In particular, existing
+ Received headers MUST be preserved and the count of Received headers
+ in the outgoing message MUST be larger than the same count on the
+ message as received by the implementation. (An implementation that
+ adds a Received header before processing the message does not need to
+ add another when redirecting.)
+
+ The message is sent back out with the address from the redirect
+ command as an envelope recipient. Implementations MAY combine
+ separate redirects for a given message into a single submission with
+ multiple envelope recipients. (This is not a Mail User Agent (MUA)-
+ style forward, which creates a new message with a different sender
+ and message ID, wrapping the old message in a new one.)
+
+ The envelope sender address on the outgoing message is chosen by the
+ sieve implementation. It MAY be copied from the message being
+ processed. However, if the message being processed has an empty
+ envelope sender address the outgoing message MUST also have an empty
+ envelope sender address. This last requirement is imposed to prevent
+ loops in the case where a message is redirected to an invalid address
+ when then returns a delivery status notification that also ends up
+ being redirected to the same invalid address.
+
+ A simple script can be used for redirecting all mail:
+
+ Example: redirect "bart@example.com";
+
+ Implementations MUST take measures to implement loop control,
+ possibly including adding headers to the message or counting Received
+ headers as specified in section 6.2 of [SMTP]. If an implementation
+ detects a loop, it causes an error.
+
+ Implementations MUST provide means of limiting the number of
+ redirects a Sieve script can perform. See section 10 for more
+ details.
+
+ Implementations MAY ignore a redirect action silently due to policy
+ reasons. For example, an implementation MAY choose not to redirect
+ to an address that is known to be undeliverable. Any ignored
+ redirect MUST NOT cancel the implicit keep.
+
+4.3. Action keep
+
+ Usage: keep
+
+ The "keep" action is whatever action is taken in lieu of all other
+ actions, if no filtering happens at all; generally, this simply means
+ to file the message into the user's main mailbox. This command
+
+
+
+Guenther & Showalter Standards Track [Page 24]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ provides a way to execute this action without needing to know the
+ name of the user's main mailbox, providing a way to call it without
+ needing to understand the user's setup or the underlying mail system.
+
+ For instance, in an implementation where the IMAP server is running
+ scripts on behalf of the user at time of delivery, a keep command is
+ equivalent to a fileinto "INBOX".
+
+ Example: if size :under 1M { keep; } else { discard; }
+
+ Note that the above script is identical to the one below.
+
+ Example: if not size :under 1M { discard; }
+
+4.4. Action discard
+
+ Usage: discard
+
+ Discard is used to silently throw away the message. It does so by
+ simply canceling the implicit keep. If discard is used with other
+ actions, the other actions still happen. Discard is compatible with
+ all other actions. (For instance, fileinto+discard is equivalent to
+ fileinto.)
+
+ Discard MUST be silent; that is, it MUST NOT return a non-delivery
+ notification of any kind ([DSN], [MDN], or otherwise).
+
+ In the following script, any mail from "idiot@example.com" is thrown
+ out.
+
+ Example: if header :contains ["from"] ["idiot@example.com"] {
+ discard;
+ }
+
+ While an important part of this language, "discard" has the potential
+ to create serious problems for users: Students who leave themselves
+ logged in to an unattended machine in a public computer lab may find
+ their script changed to just "discard". In order to protect users in
+ this situation (along with similar situations), implementations MAY
+ keep messages destroyed by a script for an indefinite period, and MAY
+ disallow scripts that throw out all mail.
+
+
+
+
+
+
+
+
+
+
+Guenther & Showalter Standards Track [Page 25]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+5. Test Commands
+
+ Tests are used in conditionals to decide which part(s) of the
+ conditional to execute.
+
+ Implementations MUST support these tests: "address", "allof",
+ "anyof", "exists", "false", "header", "not", "size", and "true".
+
+ Implementations SHOULD support the "envelope" test.
+
+5.1. Test address
+
+ Usage: address [COMPARATOR] [ADDRESS-PART] [MATCH-TYPE]
+ <header-list: string-list> <key-list: string-list>
+
+ The "address" test matches Internet addresses in structured headers
+ that contain addresses. It returns true if any header contains any
+ key in the specified part of the address, as modified by the
+ comparator and the match keyword. Whether there are other addresses
+ present in the header doesn't affect this test; this test does not
+ provide any way to determine whether an address is the only address
+ in a header.
+
+ Like envelope and header, this test returns true if any combination
+ of the header-list and key-list arguments match and returns false
+ otherwise.
+
+ Internet email addresses [IMAIL] have the somewhat awkward
+ characteristic that the local-part to the left of the at-sign is
+ considered case sensitive, and the domain-part to the right of the
+ at-sign is case insensitive. The "address" command does not deal
+ with this itself, but provides the ADDRESS-PART argument for allowing
+ users to deal with it.
+
+ The address primitive never acts on the phrase part of an email
+ address or on comments within that address. It also never acts on
+ group names, although it does act on the addresses within the group
+ construct.
+
+ Implementations MUST restrict the address test to headers that
+ contain addresses, but MUST include at least From, To, Cc, Bcc,
+ Sender, Resent-From, and Resent-To, and it SHOULD include any other
+ header that utilizes an "address-list" structured header body.
+
+ Example: if address :is :all "from" "tim@example.com" {
+ discard;
+ }
+
+
+
+
+Guenther & Showalter Standards Track [Page 26]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+5.2. Test allof
+
+ Usage: allof <tests: test-list>
+
+ The "allof" test performs a logical AND on the tests supplied to it.
+
+ Example: allof (false, false) => false
+ allof (false, true) => false
+ allof (true, true) => true
+
+ The allof test takes as its argument a test-list.
+
+5.3. Test anyof
+
+ Usage: anyof <tests: test-list>
+
+ The "anyof" test performs a logical OR on the tests supplied to it.
+
+ Example: anyof (false, false) => false
+ anyof (false, true) => true
+ anyof (true, true) => true
+
+5.4. Test envelope
+
+ Usage: envelope [COMPARATOR] [ADDRESS-PART] [MATCH-TYPE]
+ <envelope-part: string-list> <key-list: string-list>
+
+ The "envelope" test is true if the specified part of the [SMTP] (or
+ equivalent) envelope matches the specified key. This specification
+ defines the interpretation of the (case insensitive) "from" and "to"
+ envelope-parts. Additional envelope-parts may be defined by other
+ extensions; implementations SHOULD consider unknown envelope parts an
+ error.
+
+ If one of the envelope-part strings is (case insensitive) "from",
+ then matching occurs against the FROM address used in the SMTP MAIL
+ command. The null reverse-path is matched against as the empty
+ string, regardless of the ADDRESS-PART argument specified.
+
+ If one of the envelope-part strings is (case insensitive) "to", then
+ matching occurs against the TO address used in the SMTP RCPT command
+ that resulted in this message getting delivered to this user. Note
+ that only the most recent TO is available, and only the one relevant
+ to this user.
+
+ The envelope-part is a string list and may contain more than one
+ parameter, in which case all of the strings specified in the key-list
+ are matched against all parts given in the envelope-part list.
+
+
+
+Guenther & Showalter Standards Track [Page 27]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ Like address and header, this test returns true if any combination of
+ the envelope-part list and key-list arguments match and returns false
+ otherwise.
+
+ All tests against envelopes MUST drop source routes.
+
+ If the SMTP transaction involved several RCPT commands, only the data
+ from the RCPT command that caused delivery to this user is available
+ in the "to" part of the envelope.
+
+ If a protocol other than SMTP is used for message transport,
+ implementations are expected to adapt this command appropriately.
+
+ The envelope command is optional. Implementations SHOULD support it,
+ but the necessary information may not be available in all cases. The
+ capability string for use with the require command is "envelope".
+
+ Example: require "envelope";
+ if envelope :all :is "from" "tim@example.com" {
+ discard;
+ }
+
+5.5. Test exists
+
+ Usage: exists <header-names: string-list>
+
+ The "exists" test is true if the headers listed in the header-names
+ argument exist within the message. All of the headers must exist or
+ the test is false.
+
+ The following example throws out mail that doesn't have a From header
+ and a Date header.
+
+ Example: if not exists ["From","Date"] {
+ discard;
+ }
+
+5.6. Test false
+
+ Usage: false
+
+ The "false" test always evaluates to false.
+
+
+
+
+
+
+
+
+
+Guenther & Showalter Standards Track [Page 28]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+5.7. Test header
+
+ Usage: header [COMPARATOR] [MATCH-TYPE]
+ <header-names: string-list> <key-list: string-list>
+
+ The "header" test evaluates to true if the value of any of the named
+ headers, ignoring leading and trailing whitespace, matches any key.
+ The type of match is specified by the optional match argument, which
+ defaults to ":is" if not specified, as specified in section 2.6.
+
+ Like address and envelope, this test returns true if any combination
+ of the header-names list and key-list arguments match and returns
+ false otherwise.
+
+ If a header listed in the header-names argument exists, it contains
+ the empty key (""). However, if the named header is not present, it
+ does not match any key, including the empty key. So if a message
+ contained the header
+
+ X-Caffeine: C8H10N4O2
+
+ these tests on that header evaluate as follows:
+
+ header :is ["X-Caffeine"] [""] => false
+ header :contains ["X-Caffeine"] [""] => true
+
+ Testing whether a given header is either absent or doesn't contain
+ any non-whitespace characters can be done using a negated "header"
+ test:
+
+ not header :matches "Cc" "?*"
+
+5.8. Test not
+
+ Usage: not <test1: test>
+
+ The "not" test takes some other test as an argument, and yields the
+ opposite result. "not false" evaluates to "true" and "not true"
+ evaluates to "false".
+
+5.9. Test size
+
+ Usage: size <":over" / ":under"> <limit: number>
+
+ The "size" test deals with the size of a message. It takes either a
+ tagged argument of ":over" or ":under", followed by a number
+ representing the size of the message.
+
+
+
+
+Guenther & Showalter Standards Track [Page 29]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ If the argument is ":over", and the size of the message is greater
+ than the number provided, the test is true; otherwise, it is false.
+
+ If the argument is ":under", and the size of the message is less than
+ the number provided, the test is true; otherwise, it is false.
+
+ Exactly one of ":over" or ":under" must be specified, and anything
+ else is an error.
+
+ The size of a message is defined to be the number of octets in the
+ [IMAIL] representation of the message.
+
+ Note that for a message that is exactly 4,000 octets, the message is
+ neither ":over" nor ":under" 4000 octets.
+
+5.10. Test true
+
+ Usage: true
+
+ The "true" test always evaluates to true.
+
+6. Extensibility
+
+ New control commands, actions, and tests can be added to the
+ language. Sites must make these features known to their users; this
+ document does not define a way to discover the list of extensions
+ supported by the server.
+
+ Any extensions to this language MUST define a capability string that
+ uniquely identifies that extension. Capability string are case-
+ sensitive; for example, "foo" and "FOO" are different capabilities.
+ If a new version of an extension changes the functionality of a
+ previously defined extension, it MUST use a different name.
+ Extensions may register a set of related capabilities by registering
+ just a unique prefix for them. The "comparator-" prefix is an
+ example of this. The prefix MUST end with a "-" and MUST NOT overlap
+ any existing registrations.
+
+ In a situation where there is a script submission protocol and an
+ extension advertisement mechanism aware of the details of this
+ language, scripts submitted can be checked against the mail server to
+ prevent use of an extension that the server does not support.
+
+ Extensions MUST state how they interact with constraints defined in
+ section 2.10, e.g., whether they cancel the implicit keep, and which
+ actions they are compatible and incompatible with. Extensions MUST
+ NOT change the behavior of the "require" control command or alter the
+ interpretation of the argument to the "require" control.
+
+
+
+Guenther & Showalter Standards Track [Page 30]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ Extensions that can submit new email messages or otherwise generate
+ new protocol requests MUST consider loop suppression, at least to
+ document any security considerations.
+
+6.1. Capability String
+
+ Capability strings are typically short strings describing what
+ capabilities are supported by the server.
+
+ Capability strings beginning with "vnd." represent vendor-defined
+ extensions. Such extensions are not defined by Internet standards or
+ RFCs, but are still registered with IANA in order to prevent
+ conflicts. Extensions starting with "vnd." SHOULD be followed by the
+ name of the vendor and product, such as "vnd.acme.rocket-sled".
+
+ The following capability strings are defined by this document:
+
+ encoded-character The string "encoded-character" indicates that the
+ implementation supports the interpretation of
+ "${hex:...}" and "${unicode:...}" in strings.
+
+ envelope The string "envelope" indicates that the implementation
+ supports the "envelope" command.
+
+ fileinto The string "fileinto" indicates that the implementation
+ supports the "fileinto" command.
+
+ comparator- The string "comparator-elbonia" is provided if the
+ implementation supports the "elbonia" comparator.
+ Therefore, all implementations have at least the
+ "comparator-i;octet" and "comparator-i;ascii-casemap"
+ capabilities. However, these comparators may be used
+ without being declared with require.
+
+6.2. IANA Considerations
+
+ In order to provide a standard set of extensions, a registry is
+ maintained by IANA. This registry contains both vendor-controlled
+ capability names (beginning with "vnd.") and IETF-controlled
+ capability names. Vendor-controlled capability names may be
+ registered on a first-come, first-served basis, by applying to IANA
+ with the form in the following section. Registration of capability
+ prefixes that do not begin with "vnd." REQUIRES a standards track or
+ IESG-approved experimental RFC.
+
+ Extensions designed for interoperable use SHOULD use IETF-controlled
+ capability names.
+
+
+
+
+Guenther & Showalter Standards Track [Page 31]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+6.2.1. Template for Capability Registrations
+
+ The following template is to be used for registering new Sieve
+ extensions with IANA.
+
+ To: iana@iana.org
+ Subject: Registration of new Sieve extension
+
+ Capability name: [the string for use in the 'require' statement]
+ Description: [a brief description of what the extension adds
+ or changes]
+ RFC number: [for extensions published as RFCs]
+ Contact address: [email and/or physical address to contact for
+ additional information]
+
+6.2.2. Handling of Existing Capability Registrations
+
+ In order to bring the existing capability registrations in line with
+ the new template, IANA has modified each as follows:
+
+ 1. The "capability name" and "capability arguments" fields have been
+ eliminated
+ 2. The "capability keyword" field have been renamed to "Capability
+ name"
+ 3. An empty "Description" field has been added
+ 4. The "Standards Track/IESG-approved experimental RFC number" field
+ has been renamed to "RFC number"
+ 5. The "Person and email address to contact for further information"
+ field should be renamed to "Contact address"
+
+6.2.3. Initial Capability Registrations
+
+ This RFC updates the following entries in the IANA registry for Sieve
+ extensions.
+
+ Capability name: encoded-character
+ Description: changes the interpretation of strings to allow
+ arbitrary octets and Unicode characters to be
+ represented using US-ASCII
+ RFC number: RFC 5228 (Sieve base spec)
+ Contact address: The Sieve discussion list <ietf-mta-filters@imc.org>
+
+ Capability name: fileinto
+ Description: adds the 'fileinto' action for delivering to a
+ mailbox other than the default
+ RFC number: RFC 5228 (Sieve base spec)
+ Contact address: The Sieve discussion list <ietf-mta-filters@imc.org>
+
+
+
+
+Guenther & Showalter Standards Track [Page 32]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ Capability name: envelope
+ Description: adds the 'envelope' test for testing the message
+ transport sender and recipient address
+ RFC number: RFC 5228 (Sieve base spec)
+ Contact address: The Sieve discussion list <ietf-mta-filters@imc.org>
+
+ Capability name: comparator-* (anything starting with "comparator-")
+ Description: adds the indicated comparator for use with the
+ :comparator argument
+ RFC number: RFC 5228 (Sieve base spec) and [COLLATION]
+ Contact address: The Sieve discussion list <ietf-mta-filters@imc.org>
+
+6.3. Capability Transport
+
+ A method of advertising which capabilities an implementation supports
+ is difficult due to the wide range of possible implementations. Such
+ a mechanism, however, should have the property that the
+ implementation can advertise the complete set of extensions that it
+ supports.
+
+7. Transmission
+
+ The [MIME] type for a Sieve script is "application/sieve".
+
+ The registration of this type for RFC 2048 requirements is updated as
+ follows:
+
+ Subject: Registration of MIME media type application/sieve
+
+ MIME media type name: application
+ MIME subtype name: sieve
+ Required parameters: none
+ Optional parameters: none
+ Encoding considerations: Most Sieve scripts will be textual,
+ written in UTF-8. When non-7bit characters are used,
+ quoted-printable is appropriate for transport systems
+ that require 7bit encoding.
+ Security considerations: Discussed in section 10 of this RFC.
+ Interoperability considerations: Discussed in section 2.10.5
+ of this RFC.
+ Published specification: this RFC.
+ Applications that use this media type: sieve-enabled mail
+ servers and clients
+ Additional information:
+ Magic number(s):
+ File extension(s): .siv .sieve
+ Macintosh File Type Code(s):
+
+
+
+
+Guenther & Showalter Standards Track [Page 33]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ Person & email address to contact for further information:
+ See the discussion list at ietf-mta-filters@imc.org.
+ Intended usage:
+ COMMON
+ Author/Change controller:
+ The SIEVE WG, delegated by the IESG.
+
+8. Parsing
+
+ The Sieve grammar is separated into tokens and a separate grammar as
+ most programming languages are. Additional rules are supplied here
+ for common arguments to various language facilities.
+
+8.1. Lexical Tokens
+
+ Sieve scripts are encoded in UTF-8. The following assumes a valid
+ UTF-8 encoding; special characters in Sieve scripts are all US-ASCII.
+
+ The following are tokens in Sieve:
+
+ - identifiers
+ - tags
+ - numbers
+ - quoted strings
+ - multi-line strings
+ - other separators
+
+ Identifiers, tags, and numbers are case-insensitive, while quoted
+ strings and multi-line strings are case-sensitive.
+
+ Blanks, horizontal tabs, CRLFs, and comments ("whitespace") are
+ ignored except as they separate tokens. Some whitespace is required
+ to separate otherwise adjacent tokens and in specific places in the
+ multi-line strings. CR and LF can only appear in CRLF pairs.
+
+ The other separators are single individual characters and are
+ mentioned explicitly in the grammar.
+
+ The lexical structure of sieve is defined in the following grammar
+ (as described in [ABNF]):
+
+ bracket-comment = "/*" *not-star 1*STAR
+ *(not-star-slash *not-star 1*STAR) "/"
+ ; No */ allowed inside a comment.
+ ; (No * is allowed unless it is the last
+ ; character, or unless it is followed by a
+ ; character that isn't a slash.)
+
+
+
+
+Guenther & Showalter Standards Track [Page 34]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ comment = bracket-comment / hash-comment
+
+ hash-comment = "#" *octet-not-crlf CRLF
+
+ identifier = (ALPHA / "_") *(ALPHA / DIGIT / "_")
+
+ multi-line = "text:" *(SP / HTAB) (hash-comment / CRLF)
+ *(multiline-literal / multiline-dotstart)
+ "." CRLF
+
+ multiline-literal = [ octet-not-period *octet-not-crlf ] CRLF
+
+ multiline-dotstart = "." 1*octet-not-crlf CRLF
+ ; A line containing only "." ends the
+ ; multi-line. Remove a leading '.' if
+ ; followed by another '.'.
+
+ not-star = CRLF / %x01-09 / %x0B-0C / %x0E-29 / %x2B-FF
+ ; either a CRLF pair, OR a single octet
+ ; other than NUL, CR, LF, or star
+
+ not-star-slash = CRLF / %x01-09 / %x0B-0C / %x0E-29 / %x2B-2E /
+ %x30-FF
+ ; either a CRLF pair, OR a single octet
+ ; other than NUL, CR, LF, star, or slash
+
+ number = 1*DIGIT [ QUANTIFIER ]
+
+ octet-not-crlf = %x01-09 / %x0B-0C / %x0E-FF
+ ; a single octet other than NUL, CR, or LF
+
+ octet-not-period = %x01-09 / %x0B-0C / %x0E-2D / %x2F-FF
+ ; a single octet other than NUL,
+ ; CR, LF, or period
+
+ octet-not-qspecial = %x01-09 / %x0B-0C / %x0E-21 / %x23-5B / %x5D-FF
+ ; a single octet other than NUL,
+ ; CR, LF, double-quote, or backslash
+
+ QUANTIFIER = "K" / "M" / "G"
+
+ quoted-other = "\" octet-not-qspecial
+ ; represents just the octet-no-qspecial
+ ; character. SHOULD NOT be used
+
+ quoted-safe = CRLF / octet-not-qspecial
+ ; either a CRLF pair, OR a single octet other
+ ; than NUL, CR, LF, double-quote, or backslash
+
+
+
+Guenther & Showalter Standards Track [Page 35]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ quoted-special = "\" (DQUOTE / "\")
+ ; represents just a double-quote or backslash
+
+ quoted-string = DQUOTE quoted-text DQUOTE
+
+ quoted-text = *(quoted-safe / quoted-special / quoted-other)
+
+ STAR = "*"
+
+ tag = ":" identifier
+
+ white-space = 1*(SP / CRLF / HTAB) / comment
+
+8.2. Grammar
+
+ The following is the grammar of Sieve after it has been lexically
+ interpreted. No whitespace or comments appear below. The start
+ symbol is "start".
+
+ argument = string-list / number / tag
+
+ arguments = *argument [ test / test-list ]
+
+ block = "{" commands "}"
+
+ command = identifier arguments (";" / block)
+
+ commands = *command
+
+ start = commands
+
+ string = quoted-string / multi-line
+
+ string-list = "[" string *("," string) "]" / string
+ ; if there is only a single string, the brackets
+ ; are optional
+
+ test = identifier arguments
+
+ test-list = "(" test *("," test) ")"
+
+8.3. Statement Elements
+
+ These elements are collected from the "Syntax" sections elsewhere in
+ this document, and are provided here in [ABNF] syntax so that they
+ can be modified by extensions.
+
+ ADDRESS-PART = ":localpart" / ":domain" / ":all"
+
+
+
+Guenther & Showalter Standards Track [Page 36]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ COMPARATOR = ":comparator" string
+
+ MATCH-TYPE = ":is" / ":contains" / ":matches"
+
+9. Extended Example
+
+ The following is an extended example of a Sieve script. Note that it
+ does not make use of the implicit keep.
+
+ #
+ # Example Sieve Filter
+ # Declare any optional features or extension used by the script
+ #
+ require ["fileinto"];
+
+ #
+ # Handle messages from known mailing lists
+ # Move messages from IETF filter discussion list to filter mailbox
+ #
+ if header :is "Sender" "owner-ietf-mta-filters@imc.org"
+ {
+ fileinto "filter"; # move to "filter" mailbox
+ }
+ #
+ # Keep all messages to or from people in my company
+ #
+ elsif address :DOMAIN :is ["From", "To"] "example.com"
+ {
+ keep; # keep in "In" mailbox
+ }
+
+ #
+ # Try and catch unsolicited email. If a message is not to me,
+ # or it contains a subject known to be spam, file it away.
+ #
+ elsif anyof (NOT address :all :contains
+ ["To", "Cc", "Bcc"] "me@example.com",
+ header :matches "subject"
+ ["*make*money*fast*", "*university*dipl*mas*"])
+ {
+ fileinto "spam"; # move to "spam" mailbox
+ }
+ else
+ {
+ # Move all other (non-company) mail to "personal"
+ # mailbox.
+ fileinto "personal";
+ }
+
+
+
+Guenther & Showalter Standards Track [Page 37]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+10. Security Considerations
+
+ Users must get their mail. It is imperative that whatever
+ implementations use to store the user-defined filtering scripts
+ protect them from unauthorized modification, to preserve the
+ integrity of the mail system. An attacker who can modify a script
+ can cause mail to be discarded, rejected, or forwarded to an
+ unauthorized recipient. In addition, it's possible that Sieve
+ scripts might expose private information, such as mailbox names, or
+ email addresses of favored (or disfavored) correspondents. Because
+ of that, scripts SHOULD also be protected from unauthorized
+ retrieval.
+
+ Several commands, such as "discard", "redirect", and "fileinto",
+ allow for actions to be taken that are potentially very dangerous.
+
+ Use of the "redirect" command to generate notifications may easily
+ overwhelm the target address, especially if it was not designed to
+ handle large messages.
+
+ Allowing a single script to redirect to multiple destinations can be
+ used as a means of amplifying the number of messages in an attack.
+ Moreover, if loop detection is not properly implemented, it may be
+ possible to set up exponentially growing message loops. Accordingly,
+ Sieve implementations:
+
+ (1) MUST implement facilities to detect and break message loops. See
+ section 6.2 of [SMTP] for additional information on basic loop
+ detection strategies.
+
+ (2) MUST provide the means for administrators to limit the ability of
+ users to abuse redirect. In particular, it MUST be possible to
+ limit the number of redirects a script can perform.
+ Additionally, if no use cases exist for using redirect to
+ multiple destinations, this limit SHOULD be set to 1. Additional
+ limits, such as the ability to restrict redirect to local users,
+ MAY also be implemented.
+
+ (3) MUST provide facilities to log use of redirect in order to
+ facilitate tracking down abuse.
+
+ (4) MAY use script analysis to determine whether or not a given
+ script can be executed safely. While the Sieve language is
+ sufficiently complex that full analysis of all possible scripts
+ is computationally infeasible, the majority of real-world scripts
+ are amenable to analysis. For example, an implementation might
+
+
+
+
+
+Guenther & Showalter Standards Track [Page 38]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ allow scripts that it has determined are safe to run unhindered,
+ block scripts that are potentially problematic, and subject
+ unclassifiable scripts to additional auditing and logging.
+
+ Allowing redirects at all may not be appropriate in situations where
+ email accounts are freely available and/or not trackable to a human
+ who can be held accountable for creating message bombs or other
+ abuse.
+
+ As with any filter on a message stream, if the Sieve implementation
+ and the mail agents 'behind' Sieve in the message stream differ in
+ their interpretation of the messages, it may be possible for an
+ attacker to subvert the filter. Of particular note are differences
+ in the interpretation of malformed messages (e.g., missing or extra
+ syntax characters) or those that exhibit corner cases (e.g., NUL
+ octets encoded via [MIME3]).
+
+11. Acknowledgments
+
+ This document has been revised in part based on comments and
+ discussions that took place on and off the SIEVE mailing list.
+ Thanks to Sharon Chisholm, Cyrus Daboo, Ned Freed, Arnt Gulbrandsen,
+ Michael Haardt, Kjetil Torgrim Homme, Barry Leiba, Mark E. Mallett,
+ Alexey Melnikov, Eric Rescorla, Rob Siemborski, and Nigel Swinson for
+ reviews and suggestions.
+
+12. Normative References
+
+ [ABNF] Crocker, D., Ed., and P. Overell, "Augmented BNF for
+ Syntax Specifications: ABNF", RFC 4234, October 2005.
+
+ [COLLATION] Newman, C., Duerst, M., and A. Gulbrandsen, "Internet
+ Application Protocol Collation Registry", RFC 4790, March
+ 2007.
+
+ [IMAIL] Resnick, P., Ed., "Internet Message Format", RFC 2822,
+ April 2001.
+
+ [KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [MIME] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
+ Extensions (MIME) Part One: Format of Internet Message
+ Bodies", RFC 2045, November 1996.
+
+ [MIME3] Moore, K., "MIME (Multipurpose Internet Mail Extensions)
+ Part Three: Message Header Extensions for Non-ASCII
+ Text", RFC 2047, November 1996.
+
+
+
+Guenther & Showalter Standards Track [Page 39]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+ [SMTP] Klensin, J., Ed., "Simple Mail Transfer Protocol", RFC
+ 2821, April 2001.
+
+ [UTF-8] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", STD 63, RFC 3629, November 2003.
+
+13. Informative References
+
+ [BINARY-SI] "Standard IEC 60027-2: Letter symbols to be used in
+ electrical technology - Part 2: Telecommunications and
+ electronics", January 1999.
+
+ [DSN] Moore, K. and G. Vaudreuil, "An Extensible Message Format
+ for Delivery Status Notifications", RFC 3464, January
+ 2003.
+
+ [FLAMES] Borenstein, N, and C. Thyberg, "Power, Ease of Use, and
+ Cooperative Work in a Practical Multimedia Message
+ System", Int. J. of Man-Machine Studies, April, 1991.
+ Reprinted in Computer-Supported Cooperative Work and
+ Groupware, Saul Greenberg, editor, Harcourt Brace
+ Jovanovich, 1991. Reprinted in Readings in Groupware and
+ Computer-Supported Cooperative Work, Ronald Baecker,
+ editor, Morgan Kaufmann, 1993.
+
+ [IMAP] Crispin, M., "Internet Message Access Protocol - version
+ 4rev1", RFC 3501, March 2003.
+
+ [MDN] Hansen, T., Ed., and G. Vaudreuil, Ed., "Message
+ Disposition Notification", RFC 3798, May 2004.
+
+ [RFC3028] Showalter, T., "Sieve: A Mail Filtering Language", RFC
+ 3028, January 2001.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Guenther & Showalter Standards Track [Page 40]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+14. Changes from RFC 3028
+
+ This following list is a summary of the changes that have been made
+ in the Sieve language base specification from [RFC3028].
+
+ 1. Removed ban on tests having side-effects
+ 2. Removed reject extension (will be specified in a separate RFC)
+ 3. Clarified description of comparators to match [COLLATION], the
+ new base specification for them
+ 4. Require stripping of leading and trailing whitespace in "header"
+ test
+ 5. Clarified or tightened handling of many minor items, including:
+ - invalid [MIME3] encoding
+ - invalid addresses in headers
+ - invalid header field names in tests
+ - 'undefined' comparator result
+ - unknown envelope parts
+ - null return-path in "envelope" test
+ 6. Capability strings are case-sensitive
+ 7. Clarified that fileinto should reencode non-ASCII mailbox
+ names to match the mailstore's conventions
+ 8. Errors in the ABNF were corrected
+ 9. The references were updated and split into normative and
+ informative
+ 10. Added encoded-character capability and deprecated (but did not
+ remove) use of arbitrary binary octets in Sieve scripts.
+ 11. Updated IANA registration template, and added IANA
+ considerations to permit capability prefix registrations.
+ 12. Added .sieve as a valid extension for Sieve scripts.
+
+Editors' Addresses
+
+ Philip Guenther
+ Sendmail, Inc.
+ 6425 Christie St. Ste 400
+ Emeryville, CA 94608
+ EMail: guenther@sendmail.com
+
+ Tim Showalter
+ EMail: tjs@psaux.com
+
+
+
+
+
+
+
+
+
+
+
+Guenther & Showalter Standards Track [Page 41]
+
+RFC 5228 Sieve: An Email Filtering Language January 2008
+
+
+Full Copyright Statement
+
+ Copyright (C) The IETF Trust (2008).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND
+ THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
+ THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+
+
+
+
+
+
+
+
+
+
+
+Guenther & Showalter Standards Track [Page 42]
+
diff --git a/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/widgets/SieveActionsUI.js b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/widgets/SieveActionsUI.js
new file mode 100644
index 0000000..518e14b
--- /dev/null
+++ b/src/sieve@mozdev.org/chrome/chromeFiles/content/libs/libSieveDOM/RFC5228/widgets/SieveActionsUI.js
@@ -0,0 +1,149 @@
+/*
+ * The content of this file is licenced. You may obtain a copy of the license
+ * at http://sieve.mozdev.org or request it via email from the author.
+ *
+ * Do not remove or change this comment.
+ *
+ * The initial author of the code is:
+ * Thomas Schmid <schmid-thomas@gmx.net>
+ *
+ */
+
+"use strict";
+
+/******************************************************************************/
+
+function SieveStopUI(elm)
+{
+ SieveActionBoxUI.call(this,elm);
+}
+
+SieveStopUI.prototype.__proto__ = SieveActionBoxUI.prototype;
+
+SieveStopUI.prototype.initSummary
+ = function ()
+{
+ return $(document.createElement("div"))
+ .text("End Script (Stop processing)");
+}
+
+
+/******************************************************************************/
+function SieveDiscardUI(elm)
+{
+ SieveActionBoxUI.call(this,elm);
+}
+
+SieveDiscardUI.prototype.__proto__ = SieveActionBoxUI.prototype;
+
+SieveDiscardUI.prototype.initSummary
+ = function ()
+{
+ return $(document.createElement("div"))
+ .text("Discard message sil