commit 0a25f87965845b99d30a486cdb9e789cd0861a8f
| author | 斟酌 鵬兄 <tgckpg@gmail.com> |
| date | 2015-08-14T13:06:23Z |
| subject | Astro Classes |
commit 0a25f87965845b99d30a486cdb9e789cd0861a8f
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date: 2015-08-14T13:06:23Z
Astro Classes
---
.../templates/test/Astro.Blog.AstroEdit.html | 201 ++++++
.../test/Astro.Blog.Components.Calendar.html | 18 +
.../test/Astro.Blog.Components.Comment.html | 101 +++
.../test/Astro.Blog.Components.Comment2.html | 150 +++++
.../test/Astro.Blog.Components.Notification.html | 38 ++
.../test/Astro.Blog.Components.SiteFile.html | 49 ++
.../test/Astro.Blog.Components.Spoiler.html | 24 +
.../test/Astro.Blog.Components.Video.html | 19 +
.../test/Astro.Blog.Layout.Article.Control.html | 28 +
.../test/Astro.Blog.Layout.Article.Latest.html | 482 +++++++++++++
.../test/Astro.Blog.Layout.ErrorPages.html | 28 +
.../templates/test/Astro.Blog.Layout.Login.html | 18 +
botanjs/src/Astro/Blog/AstroEdit/Article.js | 453 +++++++++++++
botanjs/src/Astro/Blog/AstroEdit/Draft.js | 158 +++++
botanjs/src/Astro/Blog/AstroEdit/SiteLibrary.css | 367 ++++++++++
botanjs/src/Astro/Blog/AstroEdit/SiteLibrary.js | 745 +++++++++++++++++++++
botanjs/src/Astro/Blog/AstroEdit/Tag.css | 27 +
botanjs/src/Astro/Blog/AstroEdit/Tag.js | 197 ++++++
botanjs/src/Astro/Blog/AstroEdit/Uploader.js | 241 +++++++
.../AstroEdit/Visualizer/Snippet/AcquireLib.js | 110 +++
.../Blog/AstroEdit/Visualizer/Snippet/Code.js | 191 ++++++
.../Blog/AstroEdit/Visualizer/Snippet/Html.js | 107 +++
.../Blog/AstroEdit/Visualizer/Snippet/Image.js | 157 +++++
.../Blog/AstroEdit/Visualizer/Snippet/Link.js | 131 ++++
.../Blog/AstroEdit/Visualizer/Snippet/SiteFile.js | 174 +++++
.../Blog/AstroEdit/Visualizer/Snippet/Sound.js | 146 ++++
.../Blog/AstroEdit/Visualizer/Snippet/Spoiler.js | 154 +++++
.../Astro/Blog/AstroEdit/Visualizer/Snippet/Swf.js | 214 ++++++
.../Blog/AstroEdit/Visualizer/Snippet/Video.js | 137 ++++
.../Blog/AstroEdit/Visualizer/Snippet/_this.js | 25 +
.../src/Astro/Blog/AstroEdit/Visualizer/_this.css | 101 +++
.../src/Astro/Blog/AstroEdit/Visualizer/_this.js | 496 ++++++++++++++
botanjs/src/Astro/Blog/AstroEdit/_this.css | 244 +++++++
botanjs/src/Astro/Blog/AstroEdit/_this.js | 99 +++
botanjs/src/Astro/Blog/Components/Bubble.css | 35 +
botanjs/src/Astro/Blog/Components/Bubble.js | 133 ++++
botanjs/src/Astro/Blog/Components/Calendar.css | 67 ++
botanjs/src/Astro/Blog/Components/Calendar.js | 244 +++++++
botanjs/src/Astro/Blog/Components/Comment.css | 288 ++++++++
botanjs/src/Astro/Blog/Components/Comment.js | 709 ++++++++++++++++++++
botanjs/src/Astro/Blog/Components/ControlPanel.css | 50 ++
botanjs/src/Astro/Blog/Components/ControlPanel.js | 1 +
botanjs/src/Astro/Blog/Components/CrowdTag.css | 6 +
botanjs/src/Astro/Blog/Components/CrowdTag.js | 1 +
botanjs/src/Astro/Blog/Components/Entry/Block.css | 149 +++++
botanjs/src/Astro/Blog/Components/Entry/Block.js | 1 +
botanjs/src/Astro/Blog/Components/Entry/Blog.css | 104 +++
botanjs/src/Astro/Blog/Components/Entry/Blog.js | 1 +
botanjs/src/Astro/Blog/Components/Entry/Home.css | 129 ++++
botanjs/src/Astro/Blog/Components/Entry/Home.js | 1 +
botanjs/src/Astro/Blog/Components/Entry/List.css | 67 ++
botanjs/src/Astro/Blog/Components/Entry/List.js | 1 +
botanjs/src/Astro/Blog/Components/Entry/Mega.css | 139 ++++
botanjs/src/Astro/Blog/Components/Entry/Mega.js | 1 +
botanjs/src/Astro/Blog/Components/Entry/Tag.css | 63 ++
botanjs/src/Astro/Blog/Components/Entry/Tag.js | 1 +
botanjs/src/Astro/Blog/Components/Notification.css | 155 +++++
botanjs/src/Astro/Blog/Components/Notification.js | 235 +++++++
botanjs/src/Astro/Blog/Components/SiteFile.css | 84 +++
botanjs/src/Astro/Blog/Components/SiteFile.js | 147 ++++
.../src/Astro/Blog/Components/SocialButtons.css | 8 +
botanjs/src/Astro/Blog/Components/SocialButtons.js | 40 ++
botanjs/src/Astro/Blog/Components/Spoiler.css | 25 +
botanjs/src/Astro/Blog/Components/Spoiler.js | 49 ++
botanjs/src/Astro/Blog/Components/TagControl.css | 24 +
botanjs/src/Astro/Blog/Components/TagControl.js | 1 +
.../Blog/Components/ToggleButton/CommentToggle.css | 42 ++
.../Blog/Components/ToggleButton/CommentToggle.js | 27 +
.../Astro/Blog/Components/ToggleButton/_this.css | 84 +++
.../Astro/Blog/Components/ToggleButton/_this.js | 61 ++
botanjs/src/Astro/Blog/Components/VerticalNote.css | 80 +++
botanjs/src/Astro/Blog/Components/VerticalNote.js | 79 +++
botanjs/src/Astro/Blog/Components/Video.css | 32 +
botanjs/src/Astro/Blog/Components/Video.js | 103 +++
botanjs/src/Astro/Blog/Config.js | 12 +
botanjs/src/Astro/Blog/Element/Footer.css | 12 +
botanjs/src/Astro/Blog/Element/Footer.js | 2 +
botanjs/src/Astro/Blog/Element/Header.css | 89 +++
botanjs/src/Astro/Blog/Element/Header.js | 1 +
botanjs/src/Astro/Blog/Element/Layer.css | 2 +
botanjs/src/Astro/Blog/Element/Layer.js | 1 +
botanjs/src/Astro/Blog/Layout/Article/Control.js | 53 ++
botanjs/src/Astro/Blog/Layout/Article/Latest.js | 6 +
botanjs/src/Astro/Blog/Layout/Article/_this.js | 3 +
botanjs/src/Astro/Blog/Layout/ErrorPages.css | 99 +++
botanjs/src/Astro/Blog/Layout/ErrorPages.js | 75 +++
botanjs/src/Astro/Blog/Layout/Login.css | 63 ++
botanjs/src/Astro/Blog/Layout/Login.js | 75 +++
botanjs/src/Astro/Blog/Layout/MainFrame.css | 108 +++
botanjs/src/Astro/Blog/Layout/MainFrame.js | 256 +++++++
botanjs/src/Astro/Blog/Layout/TwoColumn.css | 102 +++
botanjs/src/Astro/Blog/Layout/TwoColumn.js | 28 +
botanjs/src/Astro/Blog/Layout/_this.js | 3 +
botanjs/src/Astro/Bootstrap.css | 85 +++
botanjs/src/Astro/Bootstrap.js | 71 ++
botanjs/src/Astro/Build/Components/BuildList.css | 24 +
botanjs/src/Astro/Build/Components/BuildList.js | 1 +
botanjs/src/Astro/Build/Components/BuildReport.css | 67 ++
botanjs/src/Astro/Build/Components/BuildReport.js | 55 ++
botanjs/src/Astro/Build/Components/ItemList.css | 59 ++
botanjs/src/Astro/Build/Components/ItemList.js | 1 +
botanjs/src/Astro/Build/Element/Footer.css | 12 +
botanjs/src/Astro/Build/Element/Footer.js | 2 +
botanjs/src/Astro/Build/Element/Header.css | 87 +++
botanjs/src/Astro/Build/Element/Header.js | 1 +
botanjs/src/Astro/Build/Layout/MainFrame.css | 86 +++
botanjs/src/Astro/Build/Layout/MainFrame.js | 88 +++
botanjs/src/Astro/Build/Page/About.css | 11 +
botanjs/src/Astro/Build/Page/About.js | 1 +
botanjs/src/Astro/Build/Page/Index.css | 24 +
botanjs/src/Astro/Build/Page/Index.js | 1 +
botanjs/src/Astro/Build/Page/_this.css | 5 +
botanjs/src/Astro/Common/Element/Footer.css | 36 +
botanjs/src/Astro/Common/Element/Footer.js | 1 +
botanjs/src/Astro/Mechanism/CharacterCloud.css | 7 +
botanjs/src/Astro/Mechanism/CharacterCloud.js | 77 +++
botanjs/src/Astro/Mechanism/Parallax.css | 6 +
botanjs/src/Astro/Mechanism/Parallax.js | 89 +++
botanjs/src/Astro/utils/Date.js | 140 ++++
botanjs/src/Astro/utils/_this.js | 3 +
.../src/externs/Astro.Blog.AstroEdit.Article.js | 18 +
botanjs/src/externs/Astro.Blog.AstroEdit.Tag.js | 6 +
...stro.Blog.AstroEdit.Visualizer.Snippet.Model.js | 6 +
.../Astro.Blog.AstroEdit.Visualizer.Snippet.js | 6 +
.../src/externs/Astro.Blog.AstroEdit.Visualizer.js | 12 +
botanjs/src/externs/Astro.Blog.AstroEdit.js | 2 +
.../src/externs/Astro.Blog.Components.Bubble.js | 18 +
.../src/externs/Astro.Blog.Components.Calendar.js | 13 +
botanjs/src/externs/Astro.Blog.Components.js | 2 +
botanjs/src/externs/Astro.Blog.Config.js | 5 +
.../src/externs/Astro.Blog.Events.Responsive.js | 8 +
botanjs/src/externs/Astro.Blog.Events.js | 2 +
botanjs/src/externs/Astro.Blog.js | 2 +
botanjs/src/externs/Astro.Bootstrap.js | 8 +
.../src/externs/Astro.Mechanism.CharacterCloud.js | 5 +
botanjs/src/externs/Astro.Mechanism.Parallax.js | 9 +
botanjs/src/externs/Astro.Mechanism.js | 2 +
botanjs/src/externs/Astro.js | 2 +
botanjs/src/externs/Astro.utils.Date.js | 24 +
botanjs/src/externs/Astro.utils.js | 2 +
botanjs/src/externs/_3rdParty_.Recaptcha.js | 12 +
botanjs/src/externs/_3rdParty_.js | 2 +
botanjs/src/externs/_AstConf_.Article.js | 4 +
botanjs/src/externs/_AstConf_.AstroEdit.js | 20 +
botanjs/src/externs/_AstConf_.Comment.js | 6 +
botanjs/src/externs/_AstConf_.Control.js | 6 +
botanjs/src/externs/_AstConf_.Login.js | 6 +
botanjs/src/externs/_AstConf_.SiteFile.js | 21 +
botanjs/src/externs/_AstConf_.UserInfo.js | 9 +
botanjs/src/externs/_AstConf_.js | 2 +
botanjs/src/externs/_AstJson_.AJaxGetArticle.js | 29 +
botanjs/src/externs/_AstJson_.AJaxGetDrafts.js | 18 +
.../src/externs/_AstJson_.AJaxGetFiles.Request.js | 13 +
botanjs/src/externs/_AstJson_.AJaxGetFiles.js | 24 +
botanjs/src/externs/_AstJson_.SiteFile.js | 21 +
botanjs/src/externs/_AstJson_.js | 2 +
settings.ini | 4 +-
157 files changed, 11676 insertions(+), 2 deletions(-)
diff --git a/botanjs/service/templates/test/Astro.Blog.AstroEdit.html b/botanjs/service/templates/test/Astro.Blog.AstroEdit.html
new file mode 100644
index 0000000..c5a1317
--- /dev/null
+++ b/botanjs/service/templates/test/Astro.Blog.AstroEdit.html
@@ -0,0 +1,201 @@
+<html>
+<head>
+<title>Botan JS - Test</title>
+<script>
+var debugEnv = true;
+var _AstConf_ = {
+ "ServiceUri": "/"
+ , "BasePath": "http://blog.astropenguin.net/"
+ , "AstroEdit": {
+ "article_id": 1798
+ , "paths": {
+ "get_article" : "http://blog.astropenguin.net/user/ajax-get_article"
+ , "get_drafts" : "http://blog.astropenguin.net/user/ajax-get_drafts"
+ , "get_files" : "http://blog.astropenguin.net/user/ajax-get_files"
+ , "set_article" : "http://blog.astropenguin.net/user/ajax-set_article"
+ , "set_file" : "http://blog.astropenguin.net/user/ajax-set_file"
+ , "tag_count" : "http://blog.astropenguin.net/user/ajax-get_tag_count"
+ }
+ }
+};
+</script>
+
+<link href="/ocss/Components.Console" rel="stylesheet" type="text/css" />
+<script src="/ojs/Components.Console"></script>
+
+<link href="/ocss/Astro.Blog.AstroEdit/-Components.Console" rel="stylesheet" type="text/css" />
+<script src="/ojs/Astro.Blog.AstroEdit/-Components.Console"></script>
+<body onload="BotanJS.import('Astro.Bootstrap').init();">
+
+<div id="ae_body" class="ae_body no_transition">
+ <div class="ae_topbar">
+ <span class="ae_title flsf">AstroEdit</span>
+ <span class="ae_title flsf"><sup>Open</sup><a href="../../../article/latest" target="_blank">Blog</a></span>
+ </div>
+
+ <div class="ae_content">
+ <div class="ae_background">
+ <div id="ae_bgdimmer">
+ <input type="text" id="ae_title" class="a_title fls" placeholder="Untitled" />
+ <div id="ae_content" contentEditable="true" class="fls"></div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div id="ae_panel" class="ae_panel">
+ <div id="ae_minor_panel" class="compx ae_user_info">
+ <div class="ae_draft_section">
+ <div class="ae_user_ctonten">
+ <div class="fsf">You have 1 article(s) in draft. </div>
+ </div>
+ <div id="ae_user_drafts"></div>
+ <div class="ae_draft_logo flsf">Drafts</div>
+ </div>
+ <div class="ae_name_card">
+ <div class="ae_name">penguin</div>
+ </div>
+ <div id="ae_expand" class="ae_expand_btn"></div>
+ </div>
+
+ <div class="ae_article_action">
+
+ <!-- action -->
+ <div class="ae_stitle flsf">actions</div>
+ <div class="compx ae_panel_section">
+ <div class="ae_section_prop">
+ <div id="ae_publish_btn" class="ae_iValue ae_purple flsf">Publish</div>
+ </div>
+ <div class="ae_section_prop">
+ <form id="ae_preview" target="_blank" method="POST" action="http://blog.astropenguin.net/article/preview/">
+ <div id="ae_preview_btn" class="ae_iValue ae_coral flsf">Preview</div>
+ <input name="nickname" value="penguin" type="hidden" />
+ </form>
+ </div>
+ <div class="ae_section_prop">
+ <div id="ae_backup_btn" class="ae_iValue ae_disabled flsf">Save(Ctrl + s)</div>
+ </div>
+ <div class="ae_section_prop">
+ <div id="ae_drop_btn" class="ae_iValue ae_caution flsf">Delete</div>
+ </div>
+ </div>
+
+ <!-- iComponents -->
+ <div class="ae_stitle flsf">snippets</div>
+ <div id="ae_visual_snippets" class="compx ae_panel_section ae_blockswitch">
+ </div>
+
+ <!-- Section properties -->
+ <div class="ae_stitle flsf">article properties</div>
+ <div class="ae_panel_section">
+ <div class="ae_section_prop">
+ <div class="ae_prop_name fls">Date created</div>
+ <div id="ae_cdate" class="ae_prop_value fsf">9<sup>th</sup> December, 2014</div>
+ </div>
+ <div class="ae_section_prop">
+ <div class="ae_prop_name fls">Date modified</div>
+ <div id="ae_mdate" class="ae_prop_value fsf">9<sup>th</sup> December, 2014</div>
+ </div>
+ <div class="ae_section_prop">
+ <div class="ae_prop_name fls">Date published</div>
+ <div id="ae_pdate" class="ae_prop_value fsf">9<sup>th</sup> December, 2014</div>
+ </div>
+ <div class="ae_section_prop">
+ <div class="ae_prop_name fls">Status</div>
+ <div id="ae_status" class="ae_prop_value fsf">In draft</div>
+ </div>
+ </div>
+
+ <!-- Tags -->
+ <div class="ae_stitle flsf">tags <sup class="ae_tag_add" id="ae_addTags">+</sup></div>
+ <div id="ae_tags" class="compx ae_panel_section ae_blockswitch">
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>diary</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>music</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>discover</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>audio</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>as3</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>development</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>illustration</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>blogAPI</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>windows phone 8</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>wenku8</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>logging</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>android</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>faq</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>project</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>bash</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>linux</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>pymo</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>3ds Max</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>drawings</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>python</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>perl</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>ruby</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>analysis</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>gaming</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>fez</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>cryptography</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>php</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>ContextMenu</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>JavaScript</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>HTML</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>c++</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>modeling</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>node.js</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>mongodb</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>peer to peer</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>content distribution network</span>
+ <span class="ae_disabled"><span class="ae_tag_delete">×</span>rho</span>
+ </div>
+ </div>
+
+ <div style="height: 2em; width:100%;"></div>
+</div>
+
+<div id="siteLibrary" class="cubic500">
+ <div class="asl_header">
+ <div class="asl_title"><span>Site Library</span></div>
+ <div id="asl_smask" class="comp asl_smask"></div>
+ </div>
+ <div class="asl_viewer">
+ <div class="asl_intitle">
+ <span class="asl_home_btn" id="asl_homeBtn">
+ <span class="asl_go_up"></span>
+ <span class="asl_go_up_text">Site Library</span>
+ </span>
+ </div>
+ <div class="asl_contentPanel">
+ <div id="asl_viewer" class="compx"></div>
+ <div id="asl_contentMenu" class="asl_contentMenu"></div>
+ <div id="asl_uploader">
+
+ <div id="asl_u_head">
+ <div class="upldr_title">Upload Manager</div>
+ <div class="asl_split">
+ <div id="asl_instruction" class="asl_instruction cubic500" >Upload files: drag files to the browser window. (Folders are not supported.)</div>
+ <div class="asl_left">
+ <div id="asl_preview" class="asl_preview"></div>
+ </div>
+ <div class="asl_right">
+ <div id="asl_fileName" class="asl_fileName">Dummy</div>
+ <div class="asl_progress">
+ <div id="asl_ptext" class="asl_ptext right">0%</div>
+ <div class="asl_pbar">
+ <div id="asl_pbar_fill" class="asl_pbar_fill"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="asl_fwrapper">
+ <div id="asl_fileList" class="asl_fileList"></div>
+ </div>
+
+ </div>
+ </div>
+ </div>
+</div>
+
+</body>
+</html>
diff --git a/botanjs/service/templates/test/Astro.Blog.Components.Calendar.html b/botanjs/service/templates/test/Astro.Blog.Components.Calendar.html
new file mode 100644
index 0000000..42f738b
--- /dev/null
+++ b/botanjs/service/templates/test/Astro.Blog.Components.Calendar.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+<title>Botan JS - Test</title>
+<script>
+var debugEnv = true;
+</script>
+
+<link href="/ocss/Astro.Blog.Components.Calendar" rel="stylesheet" type="text/css" />
+<script src="/ojs/Astro.Blog.Components.Calendar"></script>
+<script>
+var a = new (BotanJS.import( "Astro.Blog.Components.Calendar" ))();
+var doThing = function() { document.body.appendChild( a.init() ); };
+</script>
+<body onload="doThing();" >
+ <button onclick="a.pop( new Date() );">Test</button>
+</body>
+
+</html>
diff --git a/botanjs/service/templates/test/Astro.Blog.Components.Comment.html b/botanjs/service/templates/test/Astro.Blog.Components.Comment.html
new file mode 100644
index 0000000..c078b2a
--- /dev/null
+++ b/botanjs/service/templates/test/Astro.Blog.Components.Comment.html
@@ -0,0 +1,101 @@
+<html>
+<head>
+<title>Botan JS - Test</title>
+<script>
+var debugEnv = true;
+var _AstConf_ = {
+};
+</script>
+
+<link href="/ocss/Astro.Blog.Components.ToggleButton.CommentToggle/Astro.Blog.Components.Comment" rel="stylesheet" type="text/css" />
+<script src="/ojs/Astro.Blog.Components.ToggleButton.CommentToggle/Astro.Blog.Components.Comment"></script>
+<script type="text/javascript" src="http://www.google.com/recaptcha/api/js/recaptcha_ajax.js"></script>
+<body onload="BotanJS.import('Astro.Bootstrap').init();">
+<div style="width: 800px">
+ <div class="c_title">
+ <span class="c_bsign">Comments</span>
+ <!---->
+ </div>
+
+
+ <div class="astx c_body" id="comment_body">
+ <!---->
+ <div class="c_comm" data-value="117">
+ <!---->
+ <div class="c_cont" id="comment_117">
+ <div class="cpole"></div>
+ <div class="c_text">Tthuhhuhetuh</div>
+ <!---->
+ <div class="c_info">
+ <div class="fls">lynn</div>
+ <div class="flsf">3:02 AM 27 Apr, 2014</div>
+ </div>
+ <!---->
+ <!---->
+ <div id="c_reply_117" value="117" class="c_reply c_tag"><span class="flsf">Reply</span></div>
+ <!---->
+
+ <!---->
+ </div>
+ <div class="c_indicator"></div>
+ <!---->
+ <div class="reply">
+ <!---->
+ <div class="c_cont" id="comment_118">
+ <div class="c_text">iiueauhhuhh</div>
+ <!---->
+ <div class="cu_info">
+ <div class="cu_stack">
+ <div class="c_info">
+ <div><a class="fls" href="http://penguin.astropenguin.net/" target="_blank">iiuuehhuh</a></div>
+ <div class="flsf">3:02 AM 27 Apr, 2014</div>
+ </div>
+ <div class="cu_avatar"><img src="http://file.astropenguin.net/blog/avatars/test.png" alt="avatar" /></div>
+ </div>
+ </div>
+ <!---->
+ <div class="r_indicator"></div>
+ </div>
+ <!---->
+ </div>
+ <!---->
+ </div>
+ <!---->
+ <!---->
+ <div id="c_input_panel" class="commentblock">
+ <div class="r_indicator" id="ri_switch" style="top: 0"></div>
+ <div class="c_relative_wrap">
+ <textarea id="content_field" class="commentInput" placeholder="Write a comment"></textarea>
+ <div id="false_content" class="c_false_input">Do you even comment?</div>
+ <!---->
+ <div class="info_field" style="text-align: right;">
+ <div>
+ <span style="width: 20%;">website: </span>
+ <input id="website_field" style="width: 80%; font-family: l-sans-serif;" placeholder="optional" type="text" />
+ </div>
+ <div id="false_website" class="c_false_input">Not a valid website</div>
+ <div>
+ Email<span style="font-family: l-serif; font-style: italic;">(won't be disclosed)</span>:
+ <input name="email" id="email_field" style="font-family: l-sans-serif;" placeholder="optional" type="text" />
+ </div>
+ <div id="false_email" class="c_false_input">Invalid email format</div>
+ <div>*Name: <input name="name" id="name_field" style="font-family: l-sans-serif;" placeholder="required" type="text" /></div>
+ <div id="false_name" class="c_false_input">Please enter a name</div>
+ </div>
+ <!---->
+ <div id="capcha_wrapper" value="1792">
+ <div style="display:none;" id="recaptcha_field"></div>
+ </div>
+ <div id="false_capcha" class="c_false_input"></div>
+ <div id="c_submit" class="c_tag"><span class="flsf">Submit</span></div>
+ </div>
+ <div class="c_so_sorry">We could be immortals</div>
+ </div>
+ <!---->
+
+ </div>
+
+</div>
+</body>
+
+</html>
diff --git a/botanjs/service/templates/test/Astro.Blog.Components.Comment2.html b/botanjs/service/templates/test/Astro.Blog.Components.Comment2.html
new file mode 100644
index 0000000..a10f006
--- /dev/null
+++ b/botanjs/service/templates/test/Astro.Blog.Components.Comment2.html
@@ -0,0 +1,150 @@
+<html>
+<head>
+<title>Botan JS - Test</title>
+<script>
+var debugEnv = true;
+var _AstConf_ = {
+ "UserInfo": {
+ "name": "Penguin"
+ , "website": "http://test.astropenguin.net"
+ , "avatar": "http://a.b/c.jpg"
+ }
+};
+</script>
+
+<link href="/ocss/Astro.Blog.Components.ToggleButton.CommentToggle/Astro.Blog.Components.Comment" rel="stylesheet" type="text/css" />
+<script src="/ojs/Astro.Blog.Components.ToggleButton.CommentToggle/Astro.Blog.Components.Comment"></script>
+<script type="text/javascript" src="http://www.google.com/recaptcha/api/js/recaptcha_ajax.js"></script>
+<body onload="BotanJS.import('Astro.Bootstrap').init();">
+<div style="width: 800px">
+ <div class="c_title">
+ <span class="c_bsign">Comments</span>
+ <!---->
+ <!---->
+ <a id="uc_ntoggle" active="1" class="btn_toggle uc_ntoggle" title="Toggle notifications">
+ <!---->
+ <span class="btn_space">..</span>
+ </a>
+ <!---->
+ </div>
+
+
+ <div class="astx c_body" id="comment_body">
+ <!---->
+ <div class="c_comm" data-value="114">
+ <!---->
+ <div class="c_cont" id="comment_114">
+ <div class="cpole"></div>
+ <div class="c_text">iiii</div>
+ <!---->
+ <div class="c_info">
+ <div class="fls">inori</div>
+ <div class="flsf">9:48 AM 24 Apr, 2014</div>
+ </div>
+ <!---->
+ <!---->
+ <div id="c_reply_114" value="114" class="c_reply c_tag"><span class="flsf">Reply</span></div>
+ <!---->
+
+ <!---->
+ <a id="cr_ntoggle_114" active="1" class="btn_toggle cr_ntoggle"></a>
+ <!---->
+ </div>
+ <div class="c_indicator"></div>
+ <!---->
+ <div class="reply">
+ <!---->
+ <div class="c_cont" id="comment_115">
+ <div class="c_text">TestComment</div>
+ <!---->
+ <div class="c_info">
+ <div class="fls">inori</div>
+ <div class="flsf">9:48 AM 24 Apr, 2014</div>
+ </div>
+ <!---->
+ <div class="r_indicator"></div>
+ </div>
+ <!---->
+ <div class="c_cont" id="comment_116">
+ <div class="c_text">Commenttttttt</div>
+ <!---->
+ <div class="cu_info">
+ <div class="cu_stack">
+ <div class="c_info">
+ <div><a class="fls" href="http://penguin.astropenguin.net/" target="_blank">ii</a></div>
+ <div class="flsf">9:48 AM 24 Apr, 2014</div>
+ </div>
+ <div class="cu_avatar"><img src="http://file.astropenguin.net/blog/avatars/penguin.png" alt="avatar" /></div>
+ </div>
+ </div>
+ <!---->
+ <div class="r_indicator"></div>
+ </div>
+ <!---->
+ </div>
+ <!---->
+ </div>
+ <!---->
+ <div class="c_comm" data-value="120">
+ <!---->
+ <div class="c_cont" id="comment_120">
+ <div class="cpole"></div>
+ <div class="c_text">iegggeg</div>
+ <!---->
+ <div class="c_info">
+ <div class="fls">losisli</div>
+ <div class="flsf">11:38 AM 23 Jul, 2014</div>
+ </div>
+ <!---->
+ <!---->
+ <div id="c_reply_120" value="120" class="c_reply c_tag"><span class="flsf">Reply</span></div>
+ <!---->
+
+ <!---->
+ <a id="cr_ntoggle_120" active="1" class="btn_toggle cr_ntoggle"></a>
+ <!---->
+ </div>
+ <div class="c_indicator"></div>
+ <!---->
+ <div class="reply">
+ <!---->
+ <div class="c_cont" id="comment_121">
+ <div class="c_text">iiiii</div>
+ <!---->
+ <div class="cu_info">
+ <div class="cu_stack">
+ <div class="c_info">
+ <div><a class="fls" href="http://penguin.astropenguin.net/" target="_blank">aaa</a></div>
+ <div class="flsf">11:38 AM 23 Jul, 2014</div>
+ </div>
+ <div class="cu_avatar"><img src="http://file.astropenguin.net/blog/avatars/penguin.png" alt="avatar" /></div>
+ </div>
+ </div>
+ <!---->
+ <div class="r_indicator"></div>
+ </div>
+ <!---->
+ </div>
+ <!---->
+ </div>
+ <!---->
+ <!---->
+ <div id="c_input_panel" class="commentblock">
+ <div class="r_indicator" id="ri_switch" style="top: 0"></div>
+ <div class="c_relative_wrap">
+ <textarea id="content_field" class="commentInput" placeholder="Write a comment"></textarea>
+ <div id="false_content" class="c_false_input">Do you even comment?</div>
+ <!---->
+ <div id="capcha_wrapper" value="1645">
+ <div style="display:none;" id="recaptcha_field"></div>
+ </div>
+ <div id="false_capcha" class="c_false_input"></div>
+ <div id="c_submit" class="c_tag"><span class="flsf">Submit</span></div>
+ </div>
+ <div class="c_so_sorry">no</div>
+ </div>
+ </div>
+</div>
+</body>
+
+</html>
diff --git a/botanjs/service/templates/test/Astro.Blog.Components.Notification.html b/botanjs/service/templates/test/Astro.Blog.Components.Notification.html
new file mode 100644
index 0000000..5404324
--- /dev/null
+++ b/botanjs/service/templates/test/Astro.Blog.Components.Notification.html
@@ -0,0 +1,38 @@
+<html>
+<head>
+<title>Botan JS - Test</title>
+<script>
+var debugEnv = true;
+var _AstConf_ = {
+ "ServiceUri": "/"
+ , "BasePath": "http://blog.astropenguin.net"
+ , "Notification": {
+ "paths": {
+ "get_mesg": "/article/ajax-notifications"
+ }
+ }
+
+};
+</script>
+
+<link href="/ocss/Components.Console" rel="stylesheet" type="text/css" />
+<script src="/ojs/Components.Console"></script>
+
+<link href="/ocss/Astro.Blog.Components.Notification/-Components.Console" rel="stylesheet" type="text/css" />
+<script src="/rjs/Astro.Blog.Components.Notification/-Components.Console"></script>
+
+<body onload="BotanJS.import('Astro.Bootstrap').init();">
+<div style="position: absolute; width: 200px; height: 600px; right: 0">
+ <div id="notifications" class="notifications">
+ <div class="nt_button" data-count="0">
+ <span>Notifications</span>
+ </div>
+ <div id="nt_body" class="nt_body cubic200">
+ <div class="nt_arrow"></div>
+ </div>
+
+ </div>
+</div>
+
+</body>
+</html>
diff --git a/botanjs/service/templates/test/Astro.Blog.Components.SiteFile.html b/botanjs/service/templates/test/Astro.Blog.Components.SiteFile.html
new file mode 100644
index 0000000..1209709
--- /dev/null
+++ b/botanjs/service/templates/test/Astro.Blog.Components.SiteFile.html
@@ -0,0 +1,49 @@
+<html>
+<head>
+<title>Botan JS - Test</title>
+<script>
+var debugEnv = true;
+var _AstConf_ = {
+ "SiteFile": {
+ "f_host": "http://file.astropenguin.net/blog/site-library/"
+ , "path": {
+ "download": "http://blog.astropenguin.net/blog/download/"
+ , "info": "http://blog.astropenguin.net/article/ajax-getinfo?file="
+ , "image": {
+ "small": "http://blog.astropenguin.net/image/s100/"
+ , "medium": "http://blog.astropenguin.net/image/s200/"
+ , "large": "http://blog.astropenguin.net/image/s600/"
+ }
+ }
+ , "files": [
+ [ "5481375831d40", "681e07dcd2d4309a0eb40330c0c4a791" ]
+ , [ "54814d0dcd9f4", "02ee86256c0ac9a1d74508a54d466458" ]
+ ]
+ }
+};
+</script>
+
+<link href="/ocss/Astro.Blog.Components.SiteFile" rel="stylesheet" type="text/css" />
+<script src="/ojs/Astro.Blog.Components.SiteFile"></script>
+<body onload="BotanJS.import('Astro.Bootstrap').init();">
+ <div id="sf_5481375831d40" data-size="large" class="site_file">
+ <span class="loading_bubble">
+ <span></span>
+ <span></span>
+ <span></span>
+ <span></span>
+ </span>
+ <span style="vertical-align: middle;">Getting information ...</span>
+ </div>
+ <div id="sf_54814d0dcd9f4" data-size="large" class="site_file">
+ <span class="loading_bubble">
+ <span></span>
+ <span></span>
+ <span></span>
+ <span></span>
+ </span>
+ <span style="vertical-align: middle;">Getting information ...</span>
+ </div>
+</body>
+
+</html>
diff --git a/botanjs/service/templates/test/Astro.Blog.Components.Spoiler.html b/botanjs/service/templates/test/Astro.Blog.Components.Spoiler.html
new file mode 100644
index 0000000..fb19dc5
--- /dev/null
+++ b/botanjs/service/templates/test/Astro.Blog.Components.Spoiler.html
@@ -0,0 +1,24 @@
+<html>
+<head>
+<title>Botan JS - Test</title>
+<script>
+var debugEnv = true;
+var _AstConf_ = {
+ "Spoiler": [ 1, 2 ]
+};
+</script>
+
+<link href="/ocss/Astro.Blog.Components.Spoiler" rel="stylesheet" type="text/css" />
+<script src="/ojs/Astro.Blog.Components.Spoiler"></script>
+<body onload="BotanJS.import('Astro.Bootstrap').init();">
+ <div id="spoiler_1" class="spoiler">
+ <div class="s_title flsf">Test title</div>
+ <div class="s_content" style="display: none;">test_content</div>
+ </div>
+ <div id="spoiler_2" class="spoiler">
+ <div class="s_title flsf">Test title 2</div>
+ <div class="s_content" style="display: none;">test_content 2</div>
+ </div>
+</body>
+
+</html>
diff --git a/botanjs/service/templates/test/Astro.Blog.Components.Video.html b/botanjs/service/templates/test/Astro.Blog.Components.Video.html
new file mode 100644
index 0000000..e415196
--- /dev/null
+++ b/botanjs/service/templates/test/Astro.Blog.Components.Video.html
@@ -0,0 +1,19 @@
+<html>
+<head>
+<title>Botan JS - Test</title>
+<script>
+var debugEnv = true;
+var _AstConf_ = {
+ "Video": [ "vimeo_1", "youtube_1" ]
+};
+</script>
+
+<link href="/ocss/Astro.Blog.Components.Video" rel="stylesheet" type="text/css" />
+<script src="/ojs/Astro.Blog.Components.Video"></script>
+
+<body onload="BotanJS.import('Astro.Bootstrap').init();">
+ <div id="vimeo_1" value="75668670" class="v_wrapper" ><div><div></div></div></div>
+ <div id="youtube_1" value="Q9aM9Ch97U8" class="v_wrapper" style="background: black url(http://img.youtube.com/vi/Q9aM9Ch97U8/hqdefault.jpg) no-repeat center center;" ><div><div></div></div></div>
+</body>
+
+</html>
diff --git a/botanjs/service/templates/test/Astro.Blog.Layout.Article.Control.html b/botanjs/service/templates/test/Astro.Blog.Layout.Article.Control.html
new file mode 100644
index 0000000..c540cc0
--- /dev/null
+++ b/botanjs/service/templates/test/Astro.Blog.Layout.Article.Control.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>blog - astropenguin</title>
+ <meta name="description" content="Astropenguin blogAPI Beta">
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+
+ <script>
+ var debugEnv = true;
+ var _AstConf_ = {
+ "ArticleID": 11
+ , "Control" : {
+ "action": {
+ "del": "/delete"
+ }
+ }
+ };
+ </script>
+
+ <!-- BotanJS.Astro.Blog -->
+ <link href="/ocss/Astro.Blog.Layout.Article.Control" rel="stylesheet" type="text/css" />
+ <script src="/ojs/Astro.Blog.Layout.Article.Control"></script>
+
+</head>
+<body onload="BotanJS.import('Astro.Bootstrap').init();">
+ <a id="control_delete" class="b_button b_delete flsf" title="Delete this article">✕</a>
+</body>
+</html>
diff --git a/botanjs/service/templates/test/Astro.Blog.Layout.Article.Latest.html b/botanjs/service/templates/test/Astro.Blog.Layout.Article.Latest.html
new file mode 100644
index 0000000..470db35
--- /dev/null
+++ b/botanjs/service/templates/test/Astro.Blog.Layout.Article.Latest.html
@@ -0,0 +1,482 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>blog - astropenguin</title>
+ <meta name="description" content="Astropenguin blogAPI Beta">
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+
+ <script>
+ var debugEnv = true;
+ var _AstConf_ = {
+ "ServiceUri": "/"
+ , "BasePath": "http://blog.astropenguin.net/"
+ , "MainFrame": {
+ "isHome": true
+ }
+ };
+ </script>
+
+ <!-- BotanJS.Astro.Blog -->
+ <link href="/ocss/Components.Console" rel="stylesheet" type="text/css" />
+ <script src="/ojs/Components.Console"></script>
+
+ <link href="/ocss/Astro.Blog.Layout.MainFrame/Astro.Blog.Layout.Article.Latest/-Components.Console" rel="stylesheet" type="text/css" />
+ <script src="/ojs/Astro.Blog.Layout.MainFrame/Astro.Blog.Layout.Article.Latest/-Components.Console"></script>
+
+<!-- Google Analystic
+<script>
+ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+ })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+
+ ga('create', 'UA-44315187-1', 'astropenguin.net');
+ ga('send', 'pageview');
+
+</script>
+-->
+</head>
+
+<body onload="BotanJS.import('Astro.Bootstrap').init();">
+
+ <!--header-->
+ <div class="home">
+ <div id="header" class="wrapPage">
+ <div class="home_banner"></div>
+ <div id="menu">
+ <span id="menu_home">Home</span>
+ <span id="menu_blog">Blog</span>
+ <span id="menu_cont">Contact</span>
+ </div>
+ </div>
+ </div>
+ <!-- end header -->
+
+ <div id="horizon" class="horizon"></div><!-- horizon line -->
+
+ <!-- contact -->
+ <div id="contact" class="contact">
+ <div style="height: 100%; text-align: center;">Pending Illustrations here.</div>
+ <div id="collapse_panel" class="comp collapse_panel"><div></div></div>
+ </div>
+
+ <div id="mbody" class="mbody">
+ <div id="main_background" class="no_transition_recursive flsf"></div>
+ <div class="block_deco"></div>
+ <div class="wrapPage" id="expanded_content">
+
+ <!-- contentWrap -->
+ <div class="contentWrap">
+
+ <!-- dock right -->
+ <div id="dockright" class="dockright">
+
+ <!-- main -->
+ <div class="main">
+ <!-- vpole -->
+ <div class="vpole">
+ <!-- vertical pole -->
+
+
+ <!-- Paging, do not format! -->
+ <div id="top_control" class="page_control"><div class="p_navigation">
+
+ <a href="/article/latest/">Latest</a>/<a href="/article/featured/">Featured</a>/<a href="/article/archived/">Archived</a>
+ <span>•</span>
+ <a href="/article/user/%E6%96%9F%E9%85%8C+%E9%B5%AC%E5%85%84/">斟酌 鵬兄</a>/<a href="/article/user/Aaron/">Aaron</a> </div><div class="p_controls">
+ <a class="flsf" href="/article/latest/before-2014-01-16T23:40:34+0800">older ></a> </div></div>
+ <!-- End Paging -->
+
+ <!-- posts wrapper -->
+ <div class="posts"><div class="h_title fls">Latest</div>
+<div class="home_entry">
+
+ <div class="h_info">
+ <!-- date stamp -->
+ <div class="h_infoWrapper">
+ <a class="h_avatarWrapper cubic500" target="_blank" href="http://penguin.astropenguin.net/">
+ <div class="h_avatar cubic500" style="background-image: url(http://file.astropenguin.net/blog/avatars/penguin_s.png);"></div>
+ </a>
+
+ <div class="h_auth_com">斟酌 鵬兄</div>
+ <div class="h_datetime flsf">16<sup>th</sup> June 2014</div>
+ </div>
+ </div>
+
+ <div class="h_body">
+ <div class="h_bodyWrapper">
+ <div class="h_title fls">什麼都要錢啊</div>
+ <div class="h_content">
+ <div class="h_img"><img src="/image/s600/7c3f8e2c65555d8dbb7b6f6d5005cd81.jpg" /></div> <div class="h_desc fls">最近在架設,途中於到太多阻礙了。<br />
+事情是這樣的:<br />
+在我發現ISP擋掉了我的smtp之後,醒起以前Google 好像會提供免費個人域名的電郵服務,於是便投靠Google。</div>
+
+ <!-- Click will navigate to full article view -->
+ <div class="h_read_full">[<a class="h_read_full_a flsf" href="/article/view/1796/">read full article</a>]</div>
+ </div>
+ </div>
+ </div>
+
+</div><!-- end article -->
+<div class="home_entry">
+
+ <div class="h_info">
+ <!-- date stamp -->
+ <div class="h_infoWrapper">
+ <a class="h_avatarWrapper cubic500" target="_blank" href="http://penguin.astropenguin.net/">
+ <div class="h_avatar cubic500" style="background-image: url(http://file.astropenguin.net/blog/avatars/penguin_s.png);"></div>
+ </a>
+
+ <div class="h_auth_com">斟酌 鵬兄</div>
+ <div class="h_datetime flsf">12<sup>th</sup> May 2014</div>
+ </div>
+ </div>
+
+ <div class="h_body">
+ <div class="h_bodyWrapper">
+ <div class="h_title fls">codename "rho"—實作p2p</div>
+ <div class="h_content">
+ <div class="h_img"><img src="/image/s600/5859c1979262e75b4bc2ea9810431049.jpg" /></div> <div class="h_desc fls">終於查出來了,這是由於文庫架設的CDN再及防火牆問題。<br />
+下圖表示當前(青藍)文庫的網絡架構:<br />
+很可惜基於現時的協定(洋紅),我沒辦法想出有效的補救方案。這次的項目規模比較大,沒辦法就這麼「做」出來。</div>
+
+ <!-- Click will navigate to full article view -->
+ <div class="h_read_full">[<a class="h_read_full_a flsf" href="/article/view/1795/">read full article</a>]</div>
+ </div>
+ </div>
+ </div>
+
+</div><!-- end article -->
+<div class="home_entry">
+
+ <div class="h_info">
+ <!-- date stamp -->
+ <div class="h_infoWrapper">
+ <a class="h_avatarWrapper cubic500" target="_blank" href="http://penguin.astropenguin.net/">
+ <div class="h_avatar cubic500" style="background-image: url(http://file.astropenguin.net/blog/avatars/penguin_s.png);"></div>
+ </a>
+
+ <div class="h_auth_com">斟酌 鵬兄</div>
+ <div class="h_datetime flsf">27<sup>th</sup> April 2014</div>
+ </div>
+ </div>
+
+ <div class="h_body">
+ <div class="h_bodyWrapper">
+ <div class="h_title fls">node.js</div>
+ <div class="h_content">
+ <div class="h_img"><img src="/image/s600/6961f7c8533e4aff762130c8dff8a007.jpg" /></div> <div class="h_desc fls">最近收到好多來信說文庫app連不上啊,於是我想是不是要替文庫做分流好呢?<br />
+最近工作來來去去都是php + codeigniter,快要悶死了。記得當初是想做設計的,結果找的工作卻是跟前端開發相關。</div>
+
+ <!-- Click will navigate to full article view -->
+ <div class="h_read_full">[<a class="h_read_full_a flsf" href="/article/view/1794/">read full article</a>]</div>
+ </div>
+ </div>
+ </div>
+
+</div><!-- end article -->
+<div class="home_entry">
+
+ <div class="h_info">
+ <!-- date stamp -->
+ <div class="h_infoWrapper">
+ <a class="h_avatarWrapper cubic500" target="_blank" href="http://penguin.astropenguin.net/">
+ <div class="h_avatar cubic500" style="background-image: url(http://file.astropenguin.net/blog/avatars/penguin_s.png);"></div>
+ </a>
+
+ <div class="h_auth_com">斟酌 鵬兄</div>
+ <div class="h_cCount">Comment(s) +2</div>
+ <div class="h_datetime flsf">7<sup>th</sup> February 2014</div>
+ </div>
+ </div>
+
+ <div class="h_body">
+ <div class="h_bodyWrapper">
+ <div class="h_title fls">重新開始維護wenku8</div>
+ <div class="h_content">
+ <div class="h_img"><img src="/image/s600/2f2981a17c2c2e43ccce23e0b0f8ce57.jpg" /></div> <div class="h_desc fls">之前因為工作上沒空考慮其他事,可是近來工作實在太清閒了。所以我想重新把持起維護的工作來,加上近來發現Win7的電腦其實也可以使用VS2012。於是我花了點時間架好工作台起來。</div>
+
+ <!-- Click will navigate to full article view -->
+ <div class="h_read_full">[<a class="h_read_full_a flsf" href="/article/view/1792/">read full article</a>]</div>
+ </div>
+ </div>
+ </div>
+
+</div><!-- end article -->
+<div class="home_entry">
+
+ <div class="h_info">
+ <!-- date stamp -->
+ <div class="h_infoWrapper">
+ <a class="h_avatarWrapper cubic500" target="_blank" href="http://penguin.astropenguin.net/">
+ <div class="h_avatar cubic500" style="background-image: url(http://file.astropenguin.net/blog/avatars/penguin_s.png);"></div>
+ </a>
+
+ <div class="h_auth_com">斟酌 鵬兄</div>
+ <div class="h_datetime flsf">7<sup>th</sup> February 2014</div>
+ </div>
+ </div>
+
+ <div class="h_body">
+ <div class="h_bodyWrapper">
+ <div class="h_title fls">讓程序自動編寫程序</div>
+ <div class="h_content">
+ <div class="h_img"><img src="/image/s600/63c1b30a05be44e3e00d6fa5ff27755d.jpg" /></div> <div class="h_desc fls">最近利用gcin的倉頡對照表寫了一個小程序。(簡單來說就是一個CLI的倉頡轉換器)<br />
+為什麼要做這個東西呢?當然這是為了可以在公司寫blog而不被人發現啊!</div>
+
+ <!-- Click will navigate to full article view -->
+ <div class="h_read_full">[<a class="h_read_full_a flsf" href="/article/view/1791/">read full article</a>]</div>
+ </div>
+ </div>
+ </div>
+
+</div><!-- end article -->
+<div class="home_entry">
+
+ <div class="h_info">
+ <!-- date stamp -->
+ <div class="h_infoWrapper">
+ <a class="h_avatarWrapper cubic500" target="_blank" href="http://mbi.astropenguin.net/">
+ <div class="h_avatar cubic500" style="background-image: url(http://file.astropenguin.net/blog/avatars/mr.m_s.png);"></div>
+ </a>
+
+ <div class="h_auth_com">Aaron</div>
+ <div class="h_cCount">Comment(s) +1</div>
+ <div class="h_datetime flsf">22<sup>nd</sup> January 2014</div>
+ </div>
+ </div>
+
+ <div class="h_body">
+ <div class="h_bodyWrapper">
+ <div class="h_title fls">呀! 甚麼都寫不出來!</div>
+ <div class="h_content">
+ <div class="h_desc fls">最近消化幾套動漫, 進擊的巨人, Steins; Gate 是比較有印象<br />
+巨人不錯, 不過還是不喜歡一堆手手腳腳在畫面上<br />
+S; G 便和前作 Chaos; Head 差不多, 還好這次我沒有一頭霧水了...</div>
+
+ <!-- Click will navigate to full article view -->
+ <div class="h_read_full">[<a class="h_read_full_a flsf" href="/article/view/1790/">read full article</a>]</div>
+ </div>
+ </div>
+ </div>
+
+</div><!-- end article -->
+<div class="home_entry">
+
+ <div class="h_info">
+ <!-- date stamp -->
+ <div class="h_infoWrapper">
+ <a class="h_avatarWrapper cubic500" target="_blank" href="http://penguin.astropenguin.net/">
+ <div class="h_avatar cubic500" style="background-image: url(http://file.astropenguin.net/blog/avatars/penguin_s.png);"></div>
+ </a>
+
+ <div class="h_auth_com">斟酌 鵬兄</div>
+ <div class="h_datetime flsf">18<sup>th</sup> January 2014</div>
+ </div>
+ </div>
+
+ <div class="h_body">
+ <div class="h_bodyWrapper">
+ <div class="h_title fls">給力的右鍵選單</div>
+ <div class="h_content">
+ <div class="h_img"><img src="/image/s600/681e07dcd2d4309a0eb40330c0c4a791.jpg" /></div> <div class="h_desc fls">好吧,雖然這個blog還沒做好。但製作途中還是有很多技術可以拿出來討論的。<br />
+其中有幾點是我這近半年才發現的。<br />
+Scope就是一般編程都必讀的一環,亦是經常讓人頭痛的東西。</div>
+
+ <!-- Click will navigate to full article view -->
+ <div class="h_read_full">[<a class="h_read_full_a flsf" href="/article/view/1783/">read full article</a>]</div>
+ </div>
+ </div>
+ </div>
+
+</div><!-- end article -->
+<div class="home_entry">
+
+ <div class="h_info">
+ <!-- date stamp -->
+ <div class="h_infoWrapper">
+ <a class="h_avatarWrapper cubic500" target="_blank" href="http://penguin.astropenguin.net/">
+ <div class="h_avatar cubic500" style="background-image: url(http://file.astropenguin.net/blog/avatars/penguin_s.png);"></div>
+ </a>
+
+ <div class="h_auth_com">斟酌 鵬兄</div>
+ <div class="h_datetime flsf">16<sup>th</sup> January 2014</div>
+ </div>
+ </div>
+
+ <div class="h_body">
+ <div class="h_bodyWrapper">
+ <div class="h_title fls">動態組裝JS組態</div>
+ <div class="h_content">
+ <div class="h_img"><img src="/image/s600/eb528c0d8d0e9d6b52aa78b061cca5ef.jpg" /></div> <div class="h_desc fls">有夠繞舌的,今天的主題就是動能地將不同的JS合併成一個檔案,以增加頁面載入速度及減低對伺服器的請求數。</div>
+
+ <!-- Click will navigate to full article view -->
+ <div class="h_read_full">[<a class="h_read_full_a flsf" href="/article/view/1788/">read full article</a>]</div>
+ </div>
+ </div>
+ </div>
+
+</div><!-- end article -->
+</div>
+ <!-- end posts -->
+
+ <div id="bottom_control" class="page_control"></div>
+
+ <!---->
+ <!-- posts wrapper -->
+ <div class="posts">
+ <div>
+ <div class="m_title fls">Featured <sup><a class="flsf m_more" href="/article/featured/">more</a></sup></div>
+<!---->
+ <a class="mega_entry" href="/article/view/1791/" data-content="最近利用gcin的倉頡對照表寫了一個小程序。(簡單來說就是一個CLI的倉頡轉換器)
+為什麼要做這個東西呢?當然這是為了可以在公司寫blog而不被人發現啊!">
+
+ <!---->
+ <div class="cCont flsf" style="background-image: url(/image/s600/63c1b30a05be44e3e00d6fa5ff27755d.jpg); !important">讓程序自動編寫程序</div>
+ <!---->
+
+ <div class="noteTag Fri">
+ <div class="cDate flsf">7<sup>th</sup> February, 2014 Fri</div>
+ </div>
+ </a>
+<!---->
+ <a class="mega_entry" href="/article/view/1783/" data-content="好吧,雖然這個blog還沒做好。但製作途中還是有很多技術可以拿出來討論的。
+其中有幾點是我這近半年才發現的。
+Scope就是一般編程都必讀的一環,亦是經常讓人頭痛的東西。">
+
+ <!---->
+ <div class="cCont flsf" style="background-image: url(/image/s600/681e07dcd2d4309a0eb40330c0c4a791.jpg); !important">給力的右鍵選單</div>
+ <!---->
+
+ <div class="noteTag Sat">
+ <div class="cDate flsf">18<sup>th</sup> January, 2014 Sat</div>
+ </div>
+ </a>
+<!---->
+</div>
+ </div>
+ <!---->
+ </div>
+ <!-- end vpole -->
+
+ <!---->
+
+ </div>
+ <!-- end main -->
+
+
+
+ <!-- Navigation panel -->
+ <div class="left_nav_panel">
+
+ <!-- Control Panel -->
+ <div id="control_panel" class="control_panel">
+ <!---->
+ <div><a href="/user/login/">Login >></a></div>
+ <!---->
+ </div>
+
+ <!-- Section Tags -->
+ <div class="tags">
+ <a style="font-size: 2em;" href="/article/tag/diary/">diary(12)</a>
+ <a style="font-size: 1.3333333333333em;" href="/article/tag/development/">development(8)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/illustration/">illustration(1)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/blogAPI/">blogAPI(4)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/windows phone 8/">windows phone 8(3)</a>
+ <a style="font-size: 1.5em;" href="/article/tag/wenku8/">wenku8(9)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/logging/">logging(2)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/android/">android(1)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/faq/">faq(1)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/project/">project(4)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/bash/">bash(2)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/linux/">linux(1)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/pymo/">pymo(2)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/3ds Max/">3ds Max(1)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/drawings/">drawings(1)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/python/">python(1)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/perl/">perl(1)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/analysis/">analysis(1)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/gaming/">gaming(1)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/fez/">fez(1)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/cryptography/">cryptography(1)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/php/">php(2)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/ContextMenu/">ContextMenu(1)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/JavaScript/">JavaScript(1)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/HTML/">HTML(1)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/c++/">c++(1)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/modeling/">modeling(1)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/node.js/">node.js(1)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/mongodb/">mongodb(1)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/peer to peer/">peer to peer(1)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/content distribution network/">content distribution network(1)</a>
+ <a style="font-size: 0.8em;" href="/article/tag/rho/">rho(1)</a>
+ </div> </div>
+
+
+ </div><!-- end dock right -->
+ </div>
+
+ <!-- Extended Sections -->
+ <div id="extend_sec">
+ </div>
+ <!-- end Extended Sections -->
+ </div><!-- end wrap page -->
+
+ </div>
+
+ <!-- footer -->
+ <div id="footer">
+ <div id="fhorizon" class="horizon"></div>
+ <div class="footer_bg">
+ <div class="f_links">
+ <!-- projects -->
+ <div class="flink_section">
+ <span>projects</span>
+
+ <a target="_blank" href="#" title="In construction">blogAPI</a>
+ <a target="_blank" href="http://wenku8.astropenguin.net/">wenku8</a>
+ </div>
+
+ <!-- guides -->
+ <div class="flink_section">
+ <span>guides</span>
+ <a target="_blank" href="#" title="In construction">site map</a>
+ <a target="_blank" href="#" title="In construction">FAQ</a>
+ </div>
+
+ <!-- Powered by -->
+ <div class="flink_section">
+ <span>powered by</span>
+ <a target="_blank" href="http://www.debian.org/">Linux (debian)</a>
+ <a target="_blank" href="http://httpd.apache.org/">Apache httpd</a>
+ <a target="_blank" href="http://www.mysql.com/">MySQL</a>
+ <a target="_blank" href="http://php.net/">php</a>
+ </div>
+
+ <!-- links -->
+ <div class="flink_section">
+ <span>links</span>
+ <a target="_blank" href="http://www.godaddy.com/">Go Daddy</a>
+ <a target="_blank" href="http://freedns.afraid.org/">Free DNS</a>
+ </div>
+
+ <div class="flink_section">
+ <span>test lab</span>
+ <a target="_blank" href="http://blog.astropenguin.net/testsuite/flex/index">Flex</a>
+ </div>
+
+
+ <!-- About -->
+ <div class="flink_section">
+ <span>about</span>
+ <a target="_blank" href="http://astropenguin.net/">Astorpenguin</a>
+ </div>
+ </div>
+ <div class="copysign flsf">
+ <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/deed.en_US"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by-sa/4.0/80x15.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">blogAPI</span> by <a xmlns:cc="http://creativecommons.org/ns#" href="http://astropenguin.net" property="cc:attributionName" rel="cc:attributionURL">Astropenguin</a> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/deed.en_US">Creative Commons Attribution-ShareAlike 4.0 International License</a>.
+ </div>
+ </div>
+ </div>
+ <!-- end footer -->
+</body>
+</html>
diff --git a/botanjs/service/templates/test/Astro.Blog.Layout.ErrorPages.html b/botanjs/service/templates/test/Astro.Blog.Layout.ErrorPages.html
new file mode 100644
index 0000000..a031860
--- /dev/null
+++ b/botanjs/service/templates/test/Astro.Blog.Layout.ErrorPages.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>404 Page not found</title>
+
+<script type="text/javascript">
+var debugEnv = true;
+var _AstConf_ = {
+ "ErrorCode": 403
+};
+</script>
+
+<link href="/ocss/Astro.Blog.Layout.ErrorPages" rel="stylesheet" type="text/css" />
+<script src="/ojs/Astro.Blog.Layout.ErrorPages"></script>
+
+</head>
+<body onload="BotanJS.import('Astro.Bootstrap').init()">
+ <div class="rooms">
+ <div id="g">
+ <div id="p_prepage" class="p_abs100"></div>
+ <div id="p403">
+ <div id="p403" class="p_abs100"></div>
+ <div class="bc b403 c_center"><div class="ringbg c_center"><div class="code c403 c_center"></div></div></div>
+ </div>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/botanjs/service/templates/test/Astro.Blog.Layout.Login.html b/botanjs/service/templates/test/Astro.Blog.Layout.Login.html
new file mode 100644
index 0000000..29392ba
--- /dev/null
+++ b/botanjs/service/templates/test/Astro.Blog.Layout.Login.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+<title>Botan JS - Test</title>
+<script>
+var debugEnv = true;
+var _AstConf_ = {
+ "Login": {
+ "formAct": "testset"
+ , "sHeat": "GOOD"
+ }
+};
+</script>
+
+<link href="/ocss/Astro.Blog.Layout.Login" rel="stylesheet" type="text/css" />
+<script src="/ojs/Astro.Blog.Layout.Login"></script>
+<body onload="BotanJS.import('Astro.Bootstrap').init();">
+</body>
+</html>
diff --git a/botanjs/src/Astro/Blog/AstroEdit/Article.js b/botanjs/src/Astro/Blog/AstroEdit/Article.js
new file mode 100644
index 0000000..2af3458
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/Article.js
@@ -0,0 +1,453 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.AstroEdit" );
+
+ /** @type {System.Cycle} */
+ var Cycle = __import( "System.Cycle" );
+ /** @type {System.utils.EventKey} */
+ var EventKey = __import( "System.utils.EventKey" );
+ /** @type {System.Debug} */
+ var debug = __import( "System.Debug" );
+ /** @type {Components.MessageBox} */
+ var MessageBox = __import( "Components.MessageBox" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Dandelion.IDOMObject} */
+ var IDOMObject = __import( "Dandelion.IDOMObject" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Astro.Blog.Config} */
+ var Config = __import( "Astro.Blog.Config" );
+ /** @type {Astro.Blog.Components.Bubble} */
+ var Bubble = __import( "Astro.Blog.Components.Bubble" );
+
+ var postData = __import( "System.Net.postData" );
+ var prettyDate = __import( "Astro.utils.Date.pretty" );
+
+ var Visualizer = ns[ NS_INVOKE ]( "Visualizer" );
+ var Tag = ns[ NS_INVOKE ]( "Tag" );
+
+ var Article = function( processorSet )
+ {
+ var pBubble = new Bubble();
+
+ var Draft = ns[ NS_INVOKE ]( "Draft" );
+
+ var _title = "";
+ var _content = "";
+
+ //// Classes & obj
+ var _ae_tag = null;
+ var _visualizer = null;
+ var timestamp = new Date();
+
+ /** @type {_AstJson_.AJaxGetArticle.entry} */
+ var ArticleModel = {
+ title: ""
+ , slug: ""
+ , content: ""
+ , date_modified: timestamp
+ , date_published: timestamp
+ , date_created: timestamp
+ , archived: false
+ , tags: null
+ , draft: true
+ , article_id: false
+ };
+
+ var ae_content = Dand.id( "ae_content" );
+ var ae_title = Dand.id( "ae_title" );
+
+ // Article info
+ var ae_cdate = Dand.id( "ae_cdate" );
+ var ae_mdate = Dand.id( "ae_mdate" );
+ var ae_pdate = Dand.id( "ae_pdate" );
+ var ae_status = Dand.id( "ae_status" );
+ var ae_backup = Dand.id( "ae_backup_btn" );
+ var ae_publish = Dand.id( "ae_publish_btn" );
+ var ae_preview = Dand.id( "ae_preview" );
+
+ /** @type {_AstConf_.AstroEdit} */
+ var a_conf = Config.get( "AstroEdit" );
+ var base_path = Config.get( "BasePath" );
+
+ ///// preview fields
+ var ae_p_fields = {
+ mtime: set_field_name( Dand.wrap( "input" ), "date_modified" )
+ , ptime: set_field_name( Dand.wrap( "input" ), "date_published" )
+ , title: set_field_name( Dand.wrap( "input" ), "title" )
+ , content: set_field_name( Dand.wrap( "input" ), "content" )
+ , tags: set_field_name( Dand.wrap( "input" ), "tags" )
+ };
+
+ // Initialize id
+ var __article_id = false;
+ var canSave = false;
+
+ var temp;
+
+ for ( var i in ae_p_fields ) ae_preview.appendChild( ae_p_fields[i] );
+ //// End preview fields
+
+
+ var enableSaveFunction = function ()
+ {
+ ae_backup.className = "ae_iValue ae_dodgerblue flsf";
+ canSave = true;
+ }
+
+ , disableSaveFunction = function ()
+ {
+ ae_backup.className = "ae_iValue ae_disabled flsf";
+ canSave = false;
+ }
+
+ , contentUpdate = function ()
+ {
+ if( !canSave ) enableSaveFunction();
+ // (_visualizer.saveRaw() != _content) ? enableSaveFunction() : disableSaveFunction();
+ }
+
+ //// private methods
+ ////// Handlers ///////
+
+ , saveSuccess = function (obj)
+ {
+ disableSaveFunction();
+ ae_mdate.innerHTML = prettyDate( new Date( obj.date_modified ) );
+
+ // Replace state of the content and title
+ ArticleModel.title = _title;
+ ArticleModel.content = _content;
+ ArticleModel.date_modified = obj.date_modified;
+
+ // Set article id if this is a new document
+ if ( obj.article_id )
+ ArticleModel.article_id = __article_id = obj.article_id;
+
+ window.history.replaceState( ArticleModel, "", base_path + "astroedit/" + __article_id + "/" );
+ }
+
+ , publishSuccess = function (obj)
+ {
+ ae_publish.innerHTML = "Done edit";
+ ae_backup.innerHTML = "Backup(Ctrl + s)";
+
+ ArticleModel.date_published = obj.date_published;
+ ae_pdate.innerHTML = prettyDate( new Date( obj.date_published ) );
+
+ this.contentUpdate && saveSuccess(obj);
+
+ new MessageBox("AstroEdit", "You have successfully published your article!", "Stay here", "Exit and goto article", publishAction).show();
+ }
+
+ , publishAction = function (stay)
+ {
+ if (!stay)
+ {
+ window.open( base_path + "article/view/" + ArticleModel.slug + "/") && window.close();
+ }
+ }
+
+ , restoreArticle = function(obj)
+ {
+ debug.Info("[Document] Looking for stored data");
+ if (obj)
+ {
+ debug.Info("[Document] .. data found");
+ // Set stored article id
+ __article_id = obj.article_id;
+ setArticle( obj );
+ }
+ else
+ {
+ if (!( ArticleModel && 0 < ArticleModel.article_id ))
+ {
+ debug.Info("[Document] .. data not found");
+ // This is a state of new article
+ var d = Math.floor(new Date().getTime()/1000);
+ __article_id = false;
+ setArticle({
+ title:""
+ , content:""
+ , date_modified: d
+ , date_published: 0
+ , date_created: d
+ , archived: false
+ , tags: null
+ , draft: true
+ , article_id: false
+ });
+ window.history.replaceState( ArticleModel, "", base_path + "astroedit/new/" );
+ }
+ }
+ }
+ /** @param {_AstJson_.AJaxGetArticle} */
+ , showArticle = function( obj )
+ {
+ if ( obj && obj.entry )
+ {
+ debug.Info( "[Document] Article Loaded" );
+ if ( obj.backup )
+ {
+ pBubble.setColor( "royalblue" );
+ pBubble.pop( "Using backup entry" );
+ Cycle.delay( function () { pBubble.blurp() }, 3000 );
+ }
+ setArticle( obj.entry );
+ if ( !obj.entry.article_id )
+ {
+ // sever will not return article_id
+ // since it is an UPDATE
+ // Store the article_id manually
+ obj.entry.article_id = __article_id;
+ }
+ __statePusher( obj.entry, __article_id );
+ }
+ else
+ {
+ debug.Info( "[Document] Article: " + String( __article_id ) + " not found on server." );
+ __article_id = false;
+ }
+ }
+
+ , setArticle = function ( entry )
+ {
+ ArticleModel = entry;
+ ae_title.value = _title = ArticleModel.title;
+
+ _visualizer.visualizeData( _content = ArticleModel.content );
+
+ ae_mdate.innerHTML = prettyDate( new Date( ArticleModel.date_modified ) );
+ ae_cdate.innerHTML = prettyDate( new Date( ArticleModel.date_created ) );
+
+ ae_pdate.innerHTML = ArticleModel.date_published
+ ? prettyDate( new Date( ArticleModel.date_published ) )
+ : "Not published."
+ ;
+
+ ae_status.innerHTML =
+ ( ArticleModel.archived ? "Archived. " : "" )
+ + ( ArticleModel.draft ? "In draft" : "Published" )
+ ;
+
+ ae_publish.innerHTML = ArticleModel.draft ? "Publish" : "Done edit";
+ ae_backup.innerHTML = ( ArticleModel.draft ? "Save" : "Backup" ) + "(Ctrl + s)";
+
+
+ _ae_tag && _ae_tag.setTags( ArticleModel.tags );
+ }
+
+ , deleteDraft = function (confirmed)
+ {
+ if (confirmed)
+ {
+ var _data = {
+ "draft": ArticleModel.draft ? 1 : 0
+ , "article_id": __article_id
+ , "del": 1
+ };
+
+ postData( processorSet, _data, deleteSuccess, serverFailed );
+ }
+ }
+
+ , deleteSuccess = function ()
+ {
+ location.reload(true);
+ }
+
+ , serverFailed = function ( obj )
+ {
+ pBubble.setColor( "red" );
+ pBubble.pop( obj["mesg"] );
+ Cycle.delay( function () { pBubble.blurp() }, 3000 );
+ }
+
+ , contentEmpty = function ()
+ {
+ return !Boolean( ae_content.hasChildNodes() );
+ }
+ ;
+
+ // Bind properties
+
+ this.saveOrBackup = function ()
+ {
+ if (canSave)
+ {
+ // Store current content and title
+ _title = ae_title.value.trim();
+ _content = _visualizer.saveRaw();
+
+ /** @type {_AstJson_.AJaxGetArticle.entry} */
+ var _data =
+ {
+ article_id: __article_id ? __article_id : ""
+ , title: _title
+ , content: _content
+ , tags: _ae_tag.getTags()
+ , draft: ArticleModel.draft ? 1 : 0
+ };
+
+ if (ArticleModel.draft == 0)
+ {
+ // This a published article
+ // action is Backup
+ _data.backup = 1;
+ }
+
+ postData( processorSet, _data, saveSuccess, serverFailed);
+ }
+ };
+
+ this.load = function ( aid, statePusher )
+ {
+ if( aid && __article_id != aid )
+ {
+ debug.Info( "[Document] Loading article: " + aid );
+ __statePusher = statePusher;
+ postData(
+ a_conf.paths.get_article
+ , { article_id: __article_id = aid }
+ , showArticle
+ , serverFailed
+ );
+ }
+ };
+
+ this.getArticleId = function () { return __article_id; }
+
+ this.preview = function ()
+ {
+ ae_p_fields.title.value = ae_title.value;
+ ae_p_fields.content.value = _visualizer.saveRaw();
+ ae_p_fields.mtime.value = ArticleModel.date_modified;
+ ae_p_fields.ptime.value = Number( ArticleModel.date_published )
+ ? ArticleModel.date_published
+ : Math.floor( 0.001*( new Date().getTime() ) )
+ ;
+ ae_p_fields.tags.value = _ae_tag.getTags();
+
+ ae_preview.submit();
+ };
+
+ this.invoke = function (_class)
+ {
+ if ( _class instanceof Draft ) _ae_draft = _class;
+ if ( _class instanceof Visualizer )
+ {
+ _visualizer = _class;
+ _visualizer.setContentDiv( ae_content );
+ }
+ if ( _class instanceof Tag )
+ {
+ _ae_tag = _class;
+ ArticleModel && ArticleModel.tags && ( _ae_tag ).setTags( ArticleModel.tags );
+ }
+ };
+
+ this.saveAndPublish = function ()
+ {
+ if ( contentEmpty() ) return;
+
+ /** @type {_AstJson_.AJaxGetArticle.entry} */
+ var _data = {
+ article_id: __article_id ? __article_id : ""
+ , draft: 0
+ };
+
+ if ( !ArticleModel.draft )
+ {
+ // This is a published article
+ // Do not backup
+ _data.backup = 0;
+ canSave = true;
+ }
+
+ if ( canSave )
+ {
+ // Store current content and title
+ _title = ae_title.value.trim();
+ _content = _visualizer.saveRaw();
+
+ _data.title = _title;
+ _data.content = _content;
+ _data.tags = _ae_tag.getTags();
+
+ postData( processorSet, _data, publishSuccess.bind({ contentUpdate: true }), serverFailed );
+ }
+ else
+ {
+ // Do not submit uneccessary data!
+ postData( processorSet, _data, publishSuccess, serverFailed );
+ }
+ };
+
+ this.drop = function ()
+ {
+ new MessageBox(
+ "Delete draft"
+ , "Are you sure you want to delete this draft? (Published article will only remove backup draft.)"
+ , "Delete", "No"
+ , deleteDraft
+ ).show();
+ };
+
+ this.updateContent = contentUpdate;
+
+ // Setting Ctrl+s functions
+ new IDOMObject(document).addEventListener(
+ new EventKey("KeyDown", function(e)
+ {
+ // key Ctrl(17)
+ if(e.ctrlKey == true && e.which == 83)
+ {
+ // key s (83)
+ this.saveOrBackup();
+ e.preventDefault();
+ }
+ /*
+ else if(e.which == 66)
+ {
+ // key b(66)
+ this.backup.bind(this)();
+ return false;
+ }
+ */
+ }.bind(this))
+ );
+
+ IDOMElement( ae_backup ).addEventListener( "Click", this.saveOrBackup );
+
+ window.addEventListener(
+ "popstate"
+ , function( event )
+ {
+ debug.Info( "[Document] Pop State fired" );
+ restoreArticle( event.state );
+ }
+ );
+
+ ae_title.oninput = function ()
+ {
+ ( ae_title.value != _title ) ? enableSaveFunction() : disableSaveFunction();
+ }
+
+ temp = IDOMElement(ae_content);
+
+ temp.addEventListener( "Input", function() { enableSaveFunction(); } );
+
+ document.body.appendChild( pBubble.init() );
+
+ return this;
+ };
+
+ var set_field_name = function ( elem, value )
+ {
+ elem.name = value;
+ elem.type = "hidden";
+ return elem;
+ };
+
+ ns[ NS_EXPORT ]( EX_CLASS, "Article", Article );
+})();
diff --git a/botanjs/src/Astro/Blog/AstroEdit/Draft.js b/botanjs/src/Astro/Blog/AstroEdit/Draft.js
new file mode 100644
index 0000000..695d0aa
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/Draft.js
@@ -0,0 +1,158 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.AstroEdit" );
+
+ /** @type {System.Cycle} */
+ var Cycle = __import( "System.Cycle" );
+ /** @type {System.utils.IKey} */
+ var IKey = __import( "System.utils.IKey" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+
+ var postData = __import( "System.Net.postData" );
+
+ /** @param {Astro.Blog.AstroEdit.Article} article */
+ var Draft = function ( article, draftsUri )
+ {
+ /** @type {Astro.Blog.AstroEdit.Article} */
+ var Article = ns[ NS_INVOKE ]( "Article" );
+
+ if ( !( article instanceof Article ) ) return;
+
+ // Drafts Container
+ var ae_expand = Dand.id("ae_expand")
+ , ae_drafts = Dand.id("ae_user_drafts")
+ , ae_drafts_h = null
+
+ ////// Draft Section
+ , showDrafts = function ()
+ {
+ showDrafts = function() { };
+ // One-time trigger only.
+ getDraftList({offset: 0});
+ }
+
+ , getDraftList = function ( p )
+ {
+ postData( draftsUri, p, listDraft.bind(p), draftFailed );
+ }
+
+ /** @param {_AstJson_.AJaxGetDrafts} obj */
+ , listDraft = function ( obj )
+ {
+ var entries = obj.entries, entry;
+
+ for ( var i in entries )
+ {
+ /** @param {_AstJson_.AJaxGetDrafts.entry} */
+ entry = entries[i];
+ // Insert entries
+ ae_drafts.appendChild(
+ entry = Dand.wrapc(
+ "ae_dEntry"
+ , [
+ Dand.wrapc(
+ "ae_dEntry_title fls"
+ , entry.active
+ ?
+ [
+ Dand.textNode( entry.title )
+ , Dand.wrap( "span", null, "ae_dActive_bubble", "\u25CF" )
+ ]
+ : entry.title
+ )
+ , Dand.wrapc( "ae_dEntry_content flsf", entry.content )
+ , Dand.wrapc( "ae_dEntry_date fsf", entry.date )
+ ]
+ , entry.active
+ ? [ new IKey ( "value", entry._id ), new IKey( "active" ) ]
+ : new IKey( "value", entry._id ) )
+ );
+
+ // Register on click function
+ entry.addEventListener( "click", function() {
+ article.load( entry.getAttribute( "value" ), __pushState );
+ } );
+ }
+
+ ae_drafts_h = ae_drafts.clientHeight;
+ ae_drafts.style.height = 0;
+
+ Cycle.next(function(){
+ // This doesn't make sense
+ // but we need to actually access the clientHeight
+ // to get the height animation working
+ ae_drafts.clientHeight;
+ ae_drafts.style.height = String( ae_drafts_h ) + "px";
+ });
+ }
+
+ , draftFailed = function (obj)
+ {
+
+ }
+
+
+ // Handlers
+ , setupExpand = function ()
+ {
+ ae_expand.className = "ae_expand_btn";
+ var panel = Dand.id("ae_minor_panel");
+ panel.onmouseover = function()
+ {
+ ae_expand.style.height = "20px";
+ }
+ panel.onmouseout = function()
+ {
+ ae_expand.style.height = "";
+ }
+ panel.onclick = function()
+ {
+ ae_expand.style.height = "";
+ ae_drafts.style.height = String(ae_drafts_h) + "px";
+ Cycle.delay(setupCollapse , 250);
+
+ // Disable mouse events
+ panel.onmouseover = panel.onmouseout = panel.onclick = null;
+
+ showDrafts();
+ }
+ }
+
+ , setupCollapse = function ()
+ {
+ ae_expand.className = "ae_callapse_btn";
+ Cycle.delay(function()
+ {
+ ae_expand.onclick = function()
+ {
+ ae_expand.style.backgroundColor = "";
+ ae_drafts.style.height = "0";
+ Cycle.delay(setupExpand, 250);
+
+ // Disable mouse events
+ ae_expand.onmouseover = ae_expand.onmouseout = ae_expand.onclick = null;
+ }
+ ae_expand.onmouseover = function()
+ {
+ ae_expand.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
+ }
+ ae_expand.onmouseout = function()
+ {
+ ae_expand.style.backgroundColor = "";
+ }
+ }, 250);
+ }
+ // End Handlers
+
+
+ , __pushState = function ( obj, article_id )
+ {
+ window.history.pushState( obj, "", "../" + article_id + "/" );
+ }
+
+ setupExpand();
+ article.invoke( this );
+ };
+
+ ns[ NS_EXPORT ]( EX_CLASS, "Draft", Draft );
+})();
diff --git a/botanjs/src/Astro/Blog/AstroEdit/SiteLibrary.css b/botanjs/src/Astro/Blog/AstroEdit/SiteLibrary.css
new file mode 100644
index 0000000..e5ac4f8
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/SiteLibrary.css
@@ -0,0 +1,367 @@
+#siteLibrary {
+ position: fixed;
+ width: 100%;
+ height: 100%;
+
+ top: calc(-100% - 5px);
+}
+
+#siteLibrary:hover {
+ top: calc(-100% + 1.5em);
+}
+
+#asl_viewer {
+ width: calc(100% - 300px);
+ height: 100%;
+
+ overflow-x: hidden;
+ overflow-y: scroll;
+
+ float: left;
+
+ position: relative;
+}
+
+.asl_header {
+ width: 100%;
+ height: 20px;
+
+ top: 100%;
+ margin-top: -20px;
+
+ position: absolute;
+ z-index: 1;
+}
+
+#asl_viewer > #canvasView {
+ position: absolute;
+ min-height: 100%;
+ width: 100%;
+}
+
+.asl_viewer {
+ width: 100%;
+ height: 100%;
+
+ background: #EEE;
+
+ -moz-box-shadow: 0 7px 5px black;
+ -webkit-box-shadow: 0 7px 5px black;
+ box-shadow: 0 7px 5px black;
+}
+
+.asl_contentMenu {
+ bottom: 0;
+ position: absolute;
+ padding: 1em;
+}
+
+.asl_contentMenu > span {
+ margin: 0 0.2em;
+ padding: 0.5em 0.5em;
+
+ cursor: default;
+
+ color: white;
+ background-color: rgba(0, 0, 0, 0.6);
+ font-family: custom-serif;
+}
+
+.asl_contentMenu > span:hover {
+ background-color: rgba(0, 0, 0, 0.8);
+}
+
+.asl_contentMenu > span[current] {
+ background-color: rgba(0, 0, 0, 0.8);
+}
+
+.asl_smask {
+ height: 10px;
+ background-color: royalblue;
+
+ position: absolute;
+ bottom: -5px;
+ width: 100%;
+}
+
+.asl_smask:before {content: '';
+ width: 100%;
+ height: 20px;
+
+ top: -10px;
+ position: absolute;
+
+ z-index: -1;
+ background-color: transparent;
+}
+
+.asl_smask[expand] {
+ background-image: url(http://file.astropenguin.net/blog/layout-images/collapse.png);
+ background-position: center 2.5px;
+ background-repeat: no-repeat;
+}
+
+.asl_smask[expand]:hover {
+ height: 20px;
+}
+
+#siteLibrary .asl_title, .asl_intitle {
+ font-size: 1.5em;
+
+ font-family: custom-sans;
+
+ overflow: hidden;
+
+ background-color: royalblue;
+ color: white;
+}
+
+#siteLibrary .asl_title {
+ padding: 0.3em 0.5em;
+}
+
+.asl_contentPanel {
+ overflow: hidden;
+ position: absolute;
+
+ height: calc(100% - 2.5em);
+ width: 100%;
+}
+
+#siteLibrary .asl_title {
+ cursor: default;
+
+ display: inline-block;
+ position: absolute;;
+
+ right: 350px;
+ bottom: -1.7em;
+
+ -moz-box-shadow: 2px 2px 5px black;
+ -webkit-box-shadow: 2px 2px 5px black;
+ box-shadow: 2px 2px 5px black;
+}
+
+/*** ASL Canvases ***/
+.canvasImage > span {
+ width: 100px;
+ height: 100px;
+ float: left;
+ margin: 0.2em;
+
+ background-repeat: no-repeat;
+ background-position: center;
+
+ border: 1px solid transparent;
+
+}
+
+.canvasImage > span[selected] {
+ background-color: lightblue;
+ border-color: royalblue;
+}
+
+.canvasImage > span > span {
+ width: 100%;
+ height: 100%;
+ display: block;
+}
+
+.canvasImage > span > span:hover {
+ background-color: rgba(0, 0, 0, 0.2) !important;
+ border-color: #444 !important;
+}
+
+.ci_album {
+ -moz-box-shadow: 2px 3px 0 0 black;
+ -webkit-box-shadow: 2px 3px 0 0 black;
+ box-shadow: 2px 3px 0 0 #666;
+ border: 1px solid #666 !important;
+}
+
+
+.canvasGeneral {
+ padding: 0.2em;
+ cursor: default;
+
+ color: #444;
+}
+
+.canvasGeneral > div {
+ padding: 0.2em;
+}
+
+.canvasGeneral > div:hover {
+ background-color: rgba(0, 0 ,0, 0.2);
+}
+
+.canvasGeneral > div > span {
+ display: inline-block;
+
+ width: 30%;
+ padding: 0.2em 0;
+
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.canvasGeneral > div > span:last-child {
+ width: 10%;
+}
+
+.canvasGeneral > div[selected] {
+ background-color: orangered;
+ color: white;
+}
+
+/*** End ASL Canvases ***/
+
+/*** Uploader ***/
+#asl_uploader {
+ width: 300px;
+ height: 100%;
+
+ float: right;
+
+ font-family: custom-sans;
+}
+
+#asl_u_head {
+ position: absolute;
+ background: #EEE;
+ z-index: 1;
+}
+
+.upldr_title {
+ font-size: 1.5em;
+}
+
+.asl_fwrapper {
+ overflow-y: scroll;
+
+ position: absolute;
+ top: 0;
+
+ width: 100%;
+ height: 100%;
+
+ /*
+ -moz-box-shadow: inset 0 0 20px black;
+ -webkit-box-shadow: inset 0 0 20px black;
+ box-shadow: inset 0 0 20px black;
+ */
+}
+
+.asl_fileList {
+ font-family: sans-serif;
+ color: #444;;
+
+ cursor: default;
+}
+
+.asl_fileList > div {
+ /* 0.5em for fwrapper padding */
+ margin-right: 50%;
+ padding: 0.5em;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.asl_uItem:hover {
+ background-color: rgba(0, 0, 0, 0.2);
+}
+
+.asl_uItemStat {
+ padding: 0 0.5em 0 0;
+ color: orange;
+}
+
+.asl_split {
+ position: relative;
+ height: 80px;
+ overflow: hidden;
+}
+
+.asl_split > div {
+ height: 100%;
+ padding: 0.5em;
+
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.asl_left {
+ float: left;
+ width: 135px;
+}
+
+.asl_right {
+ float: right;
+ width: calc(100% - 135px);
+}
+
+.asl_fileName {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.asl_preview {
+ padding: 0.2em;
+ background-color: white;
+ height: 100%;
+}
+
+/* Progress indicator */
+.asl_progress {
+ padding: 0.5em 0;
+}
+
+.asl_ptext {
+ font-size: 0.8em;
+}
+
+.asl_pbar {
+ background-color: white;
+ position: relative;
+ height: 1em;
+ margin-top: 0.2em;
+}
+
+.asl_pbar_fill {
+ background: greenyellow;
+ position: absolute;
+ width: 0%;
+ height: 100%;
+}
+
+.asl_go_up {
+ width: 0;
+ height: 0;
+
+ border-top: 0 solid transparent;
+ border-bottom: 0.5em solid transparent;
+ border-left: 0.5em solid #FFF;
+
+ position: absolute;
+
+ left: 0;
+}
+
+.asl_btn {
+ padding: 0.3em;
+ text-align: center;
+ cursor: default;
+}
+
+.asl_btn:hover {
+ background-color: rgba( 0, 0, 0, 0.2);
+}
+
+.asl_instruction {
+ width: 100%;
+ height: 100%;
+
+ font-size: 0.8em;
+}
diff --git a/botanjs/src/Astro/Blog/AstroEdit/SiteLibrary.js b/botanjs/src/Astro/Blog/AstroEdit/SiteLibrary.js
new file mode 100644
index 0000000..22db0f3
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/SiteLibrary.js
@@ -0,0 +1,745 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.AstroEdit" );
+
+ /** @type {System.Cycle} */
+ var Cycle = __import( "System.Cycle" );
+ /** @type {System.Debug} */
+ var debug = __import( "System.Debug" );
+ /** @type {System.utils.Perf} */
+ var Perf = __import( "System.utils.Perf" );
+ /** @type {System.utils.IKey} */
+ var IKey = __import( "System.utils.IKey" );
+ /** @type {System.utils.DataKey} */
+ var DataKey = __import( "System.utils.DataKey" );
+ /** @type {System.utils.EventKey} */
+ var EventKey = __import( "System.utils.EventKey" );
+ /** @type {Components.MessageBox} */
+ var MessageBox = __import( "Components.MessageBox" );
+ /** @type {Components.Mouse.ContextMenu} */
+ var ContextMenu = __import( "Components.Mouse.ContextMenu" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Dandelion.IDOMObject} */
+ var IDOMObject = __import( "Dandelion.IDOMObject" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+
+ var postData = __import( "System.Net.postData" );
+ var prettyDate = __import( "Astro.utils.Date.pretty" );
+
+ var SiteLibrary = function ( basePath, processorGet, processorSet ) {
+ var
+
+ view_index = []
+ , currentView
+ , inCollection = 0
+ , indexingItem
+ , itemSelected = false
+ , holdingCtrl = false
+ , holdingShift = false
+
+ , contentPanel = Dand.id("asl_viewer")
+ , canvasView
+ , contextMenu
+ , stage = Dand.id( "siteLibrary", true )
+ , asl_smask = Dand.id( "asl_smask", true )
+
+
+ , getItemKeys = function (data)
+ {
+ return [
+ new DataKey("id", data.id)
+ , new DataKey("title", data.name)
+ , new DataKey("author", data.author)
+ , new DataKey("hash", data.hash)
+ ];
+ }
+
+ , bindData = function(target, source, data)
+ {
+ IDOMElement(source).addEventListener(new EventKey("MouseDown", function (e)
+ {
+ // pass neccessary informations for contextMenu to read
+ target.idata = this;
+
+ if(e.which == 1 || (e.which == 3 && !this.stage.hasAttribute("selected")))
+ {
+ // Mousedown event will bubble up to parent node, set itemselected to true
+ itemSelected = true;
+
+ if( !holdingCtrl ) deselectAllItem.bind( target )();
+
+ if(holdingShift) rangedSelect();
+ else indexingItem = this.stage;
+
+
+ selectItem.bind(target)();
+ }
+ }.bind(
+ {
+ stage: source
+ , href: data.src_location
+ , hash: data.hash
+ , author: data.author
+ , name: data.name
+ , date: data.date_created
+ , isCollection: 0 < data.cCount
+ }
+ )));
+ }
+
+
+ ////// Canvas builders
+ , resetCanvas = function (name)
+ {
+ if(canvasView)
+ {
+ contentPanel.removeChild(canvasView);
+ contentPanel.appendChild(canvasView = Dand.wrap(null, "canvasView", name || canvasView.getAttribute("class"), null, new DataKey("name", currentView)));
+ }
+ else
+ {
+ contentPanel.appendChild(canvasView = Dand.wrap(null, "canvasView", name, null, new DataKey("name", currentView)));
+ }
+
+ IDOMElement(canvasView).addEventListener(
+ new EventKey("Click", function (e)
+ {
+ if(e.which == 1)
+ {
+ if(!itemSelected)
+ {
+ deselectAllItem.bind(canvasView)();
+ canvasView.idata = null;
+ }
+ itemSelected = false;
+ }
+ }
+ )
+ );
+ }
+
+ , buildAudioCanvas = function (e)
+ {
+ // TODO
+ resetCanvas("canvasAudio");
+ }
+
+ /** @param {_AstJson_.AJaxGetFiles} */
+ , buildImageCanvas = function ( e )
+ {
+ var f = e.files;
+ resetCanvas( "canvasImage" );
+
+ for( var i in f )
+ {
+ /** @param {_AstJson_.AJaxGetFiles.file} */
+ var file = f[i];
+ // id: i, name: file.name, hash: file.hash, sorce: file.src_location, date: file.date_created
+ var img = Number( file.cCount )
+
+ ////// Album item
+ ? Dand.wrap(
+ "span", "img_" + Perf.uuid, "ci_album", Dand.wrap("span")
+ , [
+ new IKey("style", "background-image: url(" + basePath + "image/s100/" + file.hash + ".jpg);" )
+ , new IKey("title"
+ , "Album: " + file.name
+ + "\n" + file.cCount + " file(s)"
+ + "\nDate: " + prettyDate( new Date( file.date_created ), true )
+ + "\nCreated by: " + file.author
+ )
+ , new IKey("collection", 1)
+ ].concat( getItemKeys( file ) )
+ )
+ //////// Normal filele
+ : Dand.wrap(
+ "span", "img_" + Perf.uuid, null, Dand.wrap( "span" )
+ , [
+ new IKey("style", "background-image: url(" + basePath + "image/s100/" + file.hash + ".jpg);" )
+ , new IKey("title"
+ , "File: " + file.name
+ + "\nDate: " + prettyDate( new Date( file.date_created ), true )
+ + "\nCreated by: " + file.author
+ )
+ ].concat( getItemKeys( file ) )
+ );
+
+ canvasView.appendChild( img );
+ bindData( canvasView, img, file );
+ }
+
+ contextMenu = new ContextMenu(canvasView, [
+ new EventKey("Open", openAction)
+ , {
+ name: "Copy"
+ , items: [
+ new EventKey("Copy source link", copyItemLink)
+ , new EventKey("Copy hash", copyHash)
+ ]
+ }
+ , {
+ name: "Selection"
+ , items: [
+ {
+ name: "Select"
+ , items: [
+ new EventKey("all", selectAllItem)
+ , new EventKey("none", deselectAllItem)
+ , new EventKey("Invert selection", reverseSelection)
+ ]
+ }
+ ,{
+ name: "By type"
+ , items:
+ [
+ new EventKey("Only albums", selectAllCollections)
+ , new EventKey("Only files", selectAllFiles)
+ ]
+ }
+ ]
+ }
+ , {
+ name: "Action"
+ , items: [
+ new EventKey("Group selected file(s)", setCollection)
+ , new EventKey("Add file(s) to collection", insertCollection)
+ // TODO: in-group file action changes
+ , new EventKey("Move file(s) to collection", function(){})
+ , new EventKey("Remove files(s) from collection", excludeCollection)
+ ]
+ }
+ ,new EventKey("Refresh", refreshCanvas)
+ , new EventKey("Rename", popupRename)
+ , new EventKey("Delete", confirmDelete)
+ ], "RMB");
+ }
+
+ // General file, list view
+ /** @param {_AstJson_.AJaxGetFiles} */
+ , buildGeneralCanvas = function (e)
+ {
+ var f = e.files, dateStamp;
+ resetCanvas("canvasGeneral");
+
+ for(var i in f)
+ {
+ /** @param {_AstJson_.AJaxGetFiles.file} */
+ var file = f[i];
+ dateStamp = Dand.wrap( "span" );
+ dateStamp.innerHTML = prettyDate( new Date( file.date_created ) ) ;
+
+ var li = Dand.wrap(
+ null, "g_item" + Perf.uuid, null
+ , [
+ Dand.wrapne( "span", file.name )
+ , dateStamp
+ , Dand.wrapne( "span", file.author )
+ ]
+ , getItemKeys( file )
+ );
+
+ canvasView.appendChild( li );
+ bindData( canvasView, li, file );
+
+ }
+
+ contextMenu = new ContextMenu(canvasView, [
+ {
+ name: "Copy"
+ , items: [
+ new EventKey("Copy source link", copyItemLink)
+ , new EventKey("Copy hash", copyHash)
+ ]
+ }
+ , new EventKey("Delete file", confirmDelete)
+ ], "RMB");
+
+ }
+
+ , selHasCollection = false
+
+ // Return the selected files
+ , selections = function(return_id) {
+ var s = [];
+ selHasCollection = false;
+
+ IDOMElement(canvasView).foreach(1, function(elem) {
+ // Save the hashes
+ if(elem.hasAttribute("selected"))
+ {
+ s[s.length] = return_id ? IDOMElement(elem).getDAttribute("id") : elem;
+ if(elem.hasAttribute("collection")) selHasCollection = true;
+ }
+ });
+
+ return s;
+ }
+
+ /// Menu item handlers
+ , confirmDelete = function ()
+ {
+
+ new MessageBox("Confirm delete"
+ , [
+ Dand.wrape("Are you sure you want to delete the selected item(s)?")
+ , Dand.wrape("Warning: This action cannot be undone! Make sure you have de-referenced them from your articles.", new IKey("style", "color: yellow;"))
+ ]
+ , "Delete it", "No"
+ , requestDelete.bind({files: selections()})).show();
+ }
+
+ , popupRename = function ()
+ {
+ var name_field = Dand.wrap("input", null, "v_snippet_input_single", null, new IKey("type", "text"));
+ new MessageBox("Rename \"" + canvasView.idata.name + "\"", name_field, "OK", "Cancel", requestRename.bind(
+ {
+ name: name_field
+ , stage: canvasView.idata.stage
+ , isCollection: canvasView.idata.isCollection
+ }
+ )).show();
+ }
+
+ , copyItemLink = function () { return "http://file.astropenguin.net/blog/site-library/" + canvasView.idata.href; }
+ , copyHash = function () { return canvasView.idata.hash; }
+
+ , openAction = function ()
+ {
+ if(canvasView.idata.isCollection) openCollection(IDOMElement(canvasView.idata.stage).getDAttribute("id"));
+ else window.open("http://file.astropenguin.net/blog/site-library/" + canvasView.idata.href);
+ }
+
+ //// ContextMenu Handlers: Selections
+ , selectItem = function()
+ {
+ canvasView.idata.stage.hasAttribute("selected") ? canvasView.idata.stage.removeAttribute("selected") : canvasView.idata.stage.setAttribute("selected", 1);
+ }
+
+ , selectAllItem = function ()
+ {
+ IDOMElement(canvasView).foreach(1, function(target)
+ {
+ target.setAttribute("selected", 1);
+ });
+ }
+
+ , selectAllCollections = function ()
+ {
+ IDOMElement(canvasView).foreach(1, function(target)
+ {
+ target.hasAttribute("collection") ? target.setAttribute("selected", 1) : target.removeAttribute("selected");
+ });
+ }
+
+ , selectAllFiles = function ()
+ {
+ IDOMElement(canvasView).foreach(1, function(target)
+ {
+ target.hasAttribute("collection") ? target.removeAttribute("selected") : target.setAttribute("selected", 1);
+ });
+ }
+
+ , rangedSelect = function ()
+ {
+ var items = canvasView.childNodes;
+ var k = indexingItem;
+ if(Array.prototype.indexOf.call(items, k) < Array.prototype.indexOf.call(items, canvasView.idata.stage))
+ {
+ while(k != canvasView.idata.stage)
+ {
+ k.setAttribute("selected", 1);
+ k = k.nextSibling;
+ }
+ }
+ else
+ {
+ while(k != canvasView.idata.stage)
+ {
+ k.setAttribute("selected", 1);
+ k = k.previousSibling;
+ }
+ }
+ }
+
+ , deselectAllItem = function ()
+ {
+ IDOMElement(canvasView).foreach(1, function(target)
+ {
+ target.removeAttribute("selected");
+ });
+ }
+
+ , reverseSelection = function ()
+ {
+ IDOMElement(canvasView).foreach(1, function(target)
+ {
+ target.hasAttribute("selected") ? target.removeAttribute("selected") : target.setAttribute("selected", 1);
+ });
+ }
+
+ //// ContextMenu Handlers: Collections
+ , setCollection = function ()
+ {
+ var s = selections( true )
+ var name_field = Dand.wrap(
+ "input", null
+ , "v_snippet_input_single", null
+ , new IKey( "type", "text" )
+ );
+
+ new MessageBox(
+ "Collection name"
+ , name_field
+ , "OK", "Cancel"
+ , requestCollection.bind({ name: name_field, files: s.join(",") })
+ ).show();
+ }
+
+ , openCollection = function ( id ) {
+ refreshCanvas( id, true );
+ }
+
+ , excludeCollection = function() {
+
+ }
+
+ , insertCollection = function() {
+ var collections = []
+ , ul = Dand.wrapc( "canvasGeneral", null, new IKey( "style", "color: #BBB;" ) )
+ ;
+
+ // Get current albums to list
+ IDOMElement(canvasView).foreach(1, function(target)
+ {
+ if(target.hasAttribute("collection"))
+ {
+
+ var t = IDOMElement(target)
+ // item structure
+ , li = Dand.wrap(
+ null, "g_item" + Perf.uuid, null
+ , [
+ Dand.wrapne("span", t.getDAttribute("title"), new IKey("style", "width: 75%;"))
+ , Dand.wrapne("span", t.getDAttribute("author"), new IKey("style", "width: 25%;"))
+ ]
+ , new DataKey("id", t.getDAttribute("id"))
+ );
+
+ // Set selection behaviour
+ IDOMElement(li).addEventListener("Click", function(e) {
+ IDOMElement(ul).foreach(1, function(elem) { elem.hasAttribute("selected") && elem.removeAttribute("selected"); });
+ this.setAttribute("selected", 1);
+ }.bind(li));
+
+ ul.appendChild(li);
+ }
+ });
+
+
+
+ new MessageBox("Select a collection", ul, "OK", "Cancel", requestInsert.bind({ albumList: ul, collection: selections(true) })).show();
+ }
+
+ //// ContextMenu Handlers: General actions
+ , requestRename = function( confirmed )
+ {
+ if( confirmed )
+ {
+ var pData = {
+ "name": this.name.value
+ , "id": IDOMElement( this.stage ).getDAttribute( "id" )
+ };
+
+ if(this.isCollection) pData.group = "rename";
+ else pData.file = "rename";
+
+ postData( processorSet, pData, refreshCanvas, loadFailed );
+ }
+ }
+
+ , requestDelete = function (confirmed)
+ {
+ if(confirmed || this.reConfirmed)
+ {
+ if(this.files.length < 2)
+ {
+ var file = this.files[0];
+ if(selHasCollection)
+ {
+ new MessageBox("Also delete files?"
+ , "Do you also want to delete the files within the collections?"
+ , "Delete all files", "Only delete collection"
+ , delCollection.bind(file)).show();
+ }
+ else
+ {
+ postData(
+ processorSet
+ , { "file": "remove", "hash": IDOMElement(file).getDAttribute("hash") }
+ , fileDeleted.bind(file), loadFailed
+ );
+ }
+ }
+ else
+ {
+ if(!this.reConfirmed && selHasCollection)
+ {
+ // Set the reconfirmation attr
+ this.reConfirmed = true;
+
+ new MessageBox("Also delete files?"
+ , "You have also selected collection(s), do you want to also delete files inside?"
+ , "Delete all files", "Only delete collection"
+ , requestDelete.bind(this)).show();
+ return;
+ }
+
+ var files = this.files;
+
+ for(var i in files)
+ {
+ if(files[i].hasAttribute("collection"))
+ {
+ // redirect confirmation to delCollection handler
+ // delete all things when true
+ delCollection.bind(files[i])(confirmed);
+ }
+ else
+ {
+ // redirect to self
+ requestDelete.bind({files: [files[i]]})(true);
+ }
+ }
+ }
+ }
+ }
+
+ , requestInsert = function(confirmed)
+ {
+ if(confirmed)
+ {
+ var selectedAlbum = IDOMElement(IDOMElement(this.albumList).first(1, function(elem) { return elem.hasAttribute("selected"); })).getDAttribute("id");
+ postData(
+ processorSet
+ , { "group": "insert", "id": selectedAlbum , "files": this.collection.join(",") }
+ , refreshCanvas, loadFailed
+ );
+ }
+ }
+
+ , delCollection = function (confirmed)
+ {
+ var album_id = IDOMElement(this).getDAttribute("id");
+ if(confirmed)
+ {
+ postData(processorGet, { group: "get", aid: album_id }, getFilestoDelete.bind(this), loadFailed);
+ }
+ else
+ {
+ postData(processorSet, { group: "remove", id: album_id }, refreshCanvas, loadFailed);
+ }
+ }
+
+ , getFilestoDelete = function (e)
+ {
+ var l = Object.keys(e.files).length
+ , fc = 0
+ , _self = this
+ , echoDeleted = function()
+ {
+ debug.Info("[ASL] File deleted: " + this.name);
+ fc ++;
+ if(l == fc) delCollection.bind(_self)(false);
+ }
+ , deleteFailed = function ()
+ {
+ debug.Error( new Error( "[ASL] Failed to delete: " + this.name ) );
+ fc ++;
+ if(l == fc) delCollection.bind(_self)(false);
+ }
+ ;
+
+ for(var i in e.files)
+ {
+ postData(
+ processorSet
+ , { "file": "remove", "hash": e.files[i]}, echoDeleted.bind({ "name": i })
+ , deleteFailed.bind({ "name": i })
+ );
+ }
+ }
+
+ , fileDeleted = function () { this.parentNode.removeChild(this); }
+
+ , requestCollection = function ()
+ {
+ if(this.name.value)
+ {
+ postData(
+ processorSet
+ , { "group": "create", "name": this.name.value, "files": this.files }
+ , refreshCanvas, loadFailed
+ );
+ }
+ }
+ ////// End canvas builders
+
+ , refreshCanvas = function ( view, openCollection )
+ {
+ if( typeof view != "string" && !openCollection ) view = null;
+
+ // prepare postData
+ /** @type {_AstJson_.AJaxGetFiles.Request} */
+ var pdata = {};
+
+ if( openCollection )
+ {
+ // Set and remove to prevent invalid content set to currentView
+ inCollection = view;
+ view = false;
+ }
+
+ currentView = view || ( currentView || "application" );
+
+ //// postData parameters
+ // if we are in collection
+ if( inCollection )
+ {
+ pdata.group = "list";
+ pdata.aid = inCollection;
+ }
+ // else we are in root
+ else pdata.listFiles = currentView;
+
+
+ switch( currentView )
+ {
+ case "image":
+ postData( processorGet, pdata, buildImageCanvas, loadFailed );
+ break;
+ case "audio":
+ postData( processorGet, pdata, buildAudioCanvas, loadFailed );
+ break;
+ default:
+ postData( processorGet, pdata, buildGeneralCanvas, loadFailed );
+ break;
+ }
+ }
+
+ /** @param {_AstJson_.AJaxGetFiles} e */
+ , panelSwitch = function ( e )
+ {
+ if( view_index.length < 1 )
+ {
+ var menuBar = Dand.id( "asl_contentMenu" )
+ , f = e.files, first;
+
+ // button mime/type ( counts )
+ for(var i in f)
+ {
+ var elem = Dand.wrap("span", null, "asl_menuItem", (i + "(" + f[i] + ")"), new DataKey("name", i));
+
+ view_index[view_index.length] = [i, elem];
+ menuBar.appendChild(elem);
+
+ IDOMElement(elem).addEventListener(new EventKey("Click", switchView.bind(IDOMElement(elem))));
+
+ if(!first) first = [i, elem];
+ }
+
+ // begin load index
+ refreshCanvas(first[0]);
+ // Set style
+ first[1].setAttribute("current", 1);
+ }
+ else
+ {
+ // Should update the menu buttons
+ debug.Info( "Should update the menu buttons" );
+ }
+
+ }
+
+ , switchView = function ()
+ {
+ // Do nothing if current
+ if(this.getDAttribute("name") == currentView) return;
+
+
+ for(var i in view_index)
+ {
+ if(view_index[i][0] == currentView)
+ {
+ view_index[i][1].removeAttribute("current");
+ }
+ }
+
+ // switching view, reset collection
+ inCollection = 0;
+
+ this.setAttribute("current", 1);
+ refreshCanvas(this.getDAttribute("name"));
+
+ }
+
+ , showLibrary = new EventKey("Click"
+ , function ()
+ {
+ stage.style.top = "0";
+ asl_smask.setAttribute( "expand", 1 );
+
+ // Switch behaviour
+ stage.removeEventListener( showLibrary );
+
+ Cycle.delay(function () {
+ Dand.id( "ae_body" ).style.display = Dand.id( "ae_panel" ).style.display = "none";
+ asl_smask.addEventListener( hideLibrary );
+
+ // load library statistics
+ loadLibStats( panelSwitch, loadFailed );
+ }, 500);
+ }
+ )
+
+ , hideLibrary = new EventKey(
+ "Click"
+ , function ()
+ {
+ stage.style.top = "";
+ asl_smask.removeAttribute( "expand" );
+
+ // Switch behaviour
+ asl_smask.removeEventListener( hideLibrary );
+
+ Dand.id("ae_body").style.display = Dand.id( "ae_panel" ).style.display = "";
+
+ Cycle.delay(function () {
+ stage.addEventListener( showLibrary );
+ }, 500);
+ }
+ )
+
+ , loadFailed = function (e)
+ {
+ // TODO: make some notifications here
+ }
+
+ , loadLibStats = function ( handler, failedHandler )
+ {
+ postData( processorGet, { "countTypes": 1 }, handler, failedHandler );
+ }
+
+ , keyHold = function(e) { holdingCtrl = e.ctrlKey; holdingShift = e.shiftKey; }
+ , dKeyUp = new EventKey( "KeyUp", keyHold )
+ , dKeyDown = new EventKey( "KeyDown", keyHold )
+ ;
+
+ Dand.id( "asl_homeBtn", true ).addEventListener( "click", function( e ) { refreshCanvas( 0, true ); } );
+ Dand.id( "asl_refresh", true ).addEventListener( "click", function( e ) { refreshCanvas( 0 ); } );
+ new IDOMObject( document ).addEventListeners([ dKeyUp, dKeyDown ]);
+ stage.addEventListener(showLibrary);
+ };
+
+ ns[ NS_EXPORT ]( EX_CLASS, "SiteLibrary", SiteLibrary );
+})();
diff --git a/botanjs/src/Astro/Blog/AstroEdit/Tag.css b/botanjs/src/Astro/Blog/AstroEdit/Tag.css
new file mode 100644
index 0000000..10b6b7b
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/Tag.css
@@ -0,0 +1,27 @@
+.tag_active {
+ color: white;
+ opacity: 1 !important;
+}
+
+.ae_tag_delete {
+ color: white;
+ padding: 0 0.5em 0 0;
+}
+
+.ae_tag_delete:hover {
+ color: red;
+}
+
+.ae_tag_add {
+ color: white;
+ cursor: default;
+ font-weight: 200;
+}
+
+.ae_tag_add:hover {
+ color: dodgerblue;
+}
+
+#ae_tags > span {
+ float: left;
+}
diff --git a/botanjs/src/Astro/Blog/AstroEdit/Tag.js b/botanjs/src/Astro/Blog/AstroEdit/Tag.js
new file mode 100644
index 0000000..028da5b
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/Tag.js
@@ -0,0 +1,197 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.AstroEdit" );
+
+ /** @type {System.Debug} */
+ var debug = __import( "System.Debug" );
+ /** @type {Components.MessageBox} */
+ var MessageBox = __import( "Components.MessageBox" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Astro.Blog.Config} */
+ var Config = __import( "Astro.Blog.Config" );
+
+ var postData = __import( "System.Net.postData" );
+
+ /** @param {Astro.Blog.AstroEdit.Article} */
+ var Tag = function ( ae_article, tagCountUri )
+ {
+ var Article = ns[ NS_INVOKE ]( "Article" );
+
+ if ( !( ae_article instanceof Article ) )
+ return false;
+
+ /** @type {_AstConf_.AstroEdit} */
+ var a_conf = Config.get( "AstroEdit" );
+
+ var ae_document = ae_article;
+
+ var ae_addTag = Dand.id( "ae_addTags" );
+ var ae_tags = Dand.id( "ae_tags" );
+
+ // Store tags for future use
+ var tags = {};
+
+ ////// Handlers
+ ae_addTag.onclick = function ()
+ {
+
+ // Input fields
+ var ae_tagInput;
+
+ // Popup MessageBox
+ new MessageBox(
+ "Add new tag(s)"
+ , Dand.wrape([
+ Dand.wrapc(
+ "v_instruction flsf"
+ , "Tags are separated by \\n. (existing tags will be ignored.)"
+ )
+ , ae_tagInput = Dand.wrap( "textarea", null, "v_snippet_input" )
+ ])
+ , "OK", "Cancel"
+ , parseTags.bind( ae_tagInput )
+ ).show();
+ };
+
+ var deleteTag = function (e)
+ {
+ var p = { tag: this.nodeValue, stage: this.parentNode };
+ postData( tagCountUri, p, confirmDelete.bind( p ), serverFailed );
+ e.stopPropagation();
+ };
+
+ var confirmDelete = function (obj)
+ {
+ new MessageBox(
+ "Delete Tag"
+ , [
+ Dand.wrapc("ae_blockswitch",
+ [
+ Dand.textNode( "Are you sure you want to delete " )
+ , Dand.wrap( "span", null, "tag_active", Dand.textNode( this.tag ) )
+ , Dand.textNode( " ?." )
+ ]
+ )
+ , obj.count > 0
+ ? Dand.wrape([
+ Dand.textNode( "This will affect " )
+ , Dand.wrap(
+ "span", null, "ae_affected_count", Dand.textNode( obj.count )
+ )
+ , Dand.textNode(" article(s).")
+ ])
+ : null
+ ]
+ , "Delete", "No"
+ , doDelete.bind(this)
+ ).show();
+ };
+
+ var doDelete = function( confirmed )
+ {
+ if ( confirmed )
+ {
+ postData(
+ a_conf.paths.set_tag
+ , { tag: this.tag, del: 1 }
+ , deleteSuccess.bind( this )
+ , serverFailed
+ );
+ }
+ };
+
+ var deleteSuccess = function(obj)
+ {
+ // Remove element
+ this.stage.parentNode.removeChild(this.stage);
+ // Delete reference
+ delete tags[this.tag];
+ };
+
+ var parseTags = function ()
+ {
+ // Filter invalid tags
+ var ntags = this.value.trim().match(/[^\n]+/g), d, e;
+ for (var i in ntags)
+ {
+ if(!tags[(i = ntags[i]).toLowerCase()])
+ {
+ tags[i] = Dand.wrap("span", null, "tag_active",
+ [d = Dand.wrap("span", null, "ae_tag_delete", "\u00D7"), e = Dand.textNode(i)]
+ );
+ // Seperator
+ ae_tags.appendChild(Dand.textNode(" "));
+ ae_tags.appendChild(tags[i]);
+
+ tags[i].onclick = function() { toggleTag(this) }.bind(tags[i]);
+ d.onclick = deleteTag.bind(e);
+
+ }
+ else
+ {
+ debug.Info("Tag exist: " + i);
+ // Hightlight these tags
+ tags[i.toLowerCase()].className = "tag_active";
+ }
+ }
+
+ };
+
+ var toggleTag = function (tag)
+ {
+ var isEnabled = tag.className == "tag_active" ? true : false;
+ tag.className = isEnabled ? "ae_disabled" : "tag_active";
+ };
+
+ var serverFailed = function (obj) { };
+
+
+
+ var ctag, ae_children = ae_tags.childNodes;
+ for (var i in ae_children)
+ {
+ if((ctag = ae_children[i]).nodeType == 1)
+ {
+ tags[ctag.lastChild.nodeValue.toLowerCase()] = ctag;
+ IDOMElement( ctag ).addEventListener( "Click", function() { toggleTag( this ); }.bind( ctag ) );
+ // Bind the last child (x button) to firstChild (textNode: tagname)
+ IDOMElement( ctag.firstChild ).addEventListener( "Click", deleteTag.bind( ctag.lastChild ) );
+ }
+ }
+
+ this.getTags = function ()
+ {
+ // Compile tag list
+ var tlist = [];
+
+ // Return names
+ for (var i in tags)
+ {
+ if(tags[i].className == "tag_active")
+ tlist[tlist.length] = tags[i].lastChild.nodeValue;
+ }
+
+ return tlist.join("\n");
+ }
+
+ this.setTags = function (tagList)
+ {
+ // Disable all tags first
+ for (var i in tags) tags[i].className = "ae_disabled";
+
+ // Set tags from tagList
+ for (i in tagList)
+ if (tagList[i]) tags[tagList[i].toLowerCase()].className = "tag_active";
+ }
+
+ ae_document.invoke(this);
+ }
+
+ Tag.prototype.getTags = function () { };
+ Tag.prototype.setTags = function (tagList) { };
+
+ ns[ NS_EXPORT ]( EX_CLASS, "Tag", Tag );
+
+})();
diff --git a/botanjs/src/Astro/Blog/AstroEdit/Uploader.js b/botanjs/src/Astro/Blog/AstroEdit/Uploader.js
new file mode 100644
index 0000000..face003
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/Uploader.js
@@ -0,0 +1,241 @@
+(function( ){
+ var ns = __namespace( "Astro.Blog.AstroEdit" );
+
+ /** @type {System.Cycle} */
+ var Cycle = __import( "System.Cycle" );
+ /** @type {System.Debug} */
+ var debug = __import( "System.Debug" );
+ /** @type {System.utils.EventKey} */
+ var EventKey = __import( "System.utils.EventKey" );
+ /** @type {Components.Mouse.ContextMenu} */
+ var ContextMenu = __import( "Components.Mouse.ContextMenu" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Dandelion.IDOMObject} */
+ var IDOMObject = __import( "Dandelion.IDOMObject" );
+ /** @type {Astro.Blog.Config} */
+ var Conf = __import( "Astro.Blog.Config" );
+ /** @type {System.utils.Perf} */
+ var Perf = __import( "System.utils.Perf" );
+
+ var postFile = __import( "System.Net.postFile" );
+
+
+ var Uploader = function ( set_file_uri )
+ {
+ /** @type {_AstConf_.SiteFile} */
+ var config = Conf.get( "SiteFile" );
+
+ // linkage
+ var busy = false
+ , placedFiles = []
+ , currentFile
+
+
+ // Prepare structure for dockpanel
+ , ptext = Dand.id( "asl_ptext" )
+ , pbar = Dand.id( "asl_pbar_fill" )
+ , ppreview = Dand.id( "asl_preview" )
+ , pfileName = Dand.id( "asl_fileName" )
+ , instruction = Dand.id( "asl_instruction" )
+ , p_status = Dand.id( "asl_u_head" )
+
+ , fileList = Dand.id( "asl_fileList" )
+ , stage = Dand.id( "asl_uploader" )
+
+
+ , uploadProgress = function(e)
+ {
+ var done = e.position || e.loaded
+ , total = e.totalSize || e.total
+ , percentage = String( Math.floor( done / total * 1000 )/10 ) + "%";
+
+ // Set progress
+ ptext.innetText
+ = ptext.textContent
+ = pbar.style.width
+ = percentage;
+ }
+
+ , uploadComplete = function ( response )
+ {
+ try
+ {
+ var obj = JSON.parse( response );
+ if(obj.status)
+ {
+ currentFile.setStatus( "ok", config.f_host + obj.destination );
+ }
+ else
+ {
+ currentFile.setStatus( "error", obj.message );
+ }
+ new ContextMenu( currentFile.stage, currentFile.menuKeys, "LMB", stage );
+ }
+ catch ( ex )
+ {
+ currentFile.setStatus( "error", ex.message );
+ debug.Error( ex );
+ }
+ beginUpload();
+ }
+
+ , beginUpload = function()
+ {
+ busy = true;
+ if( placedFiles.length )
+ {
+ currentFile = placedFiles.pop();
+ if( currentFile )
+ {
+ var file = currentFile.file;
+
+ pfileName.textContent = pfileName.innetText = file.name;
+
+ var fd = new FormData();
+ fd.append( "uploadFile", file );
+ postFile( set_file_uri, fd, { progress: uploadProgress, complete: uploadComplete } );
+
+ currentFile.setStatus("active");
+ instruction.style.marginTop = "-80px";
+ }
+ else
+ {
+ // deleted file, skip it
+ beginUpload();
+ }
+ }
+ else
+ {
+ busy = false;
+ instruction.style.marginTop = "";
+ }
+ }
+
+ , clearFileList = function ()
+ {
+ while( fileList.hasChildNodes() )
+ {
+ fileList.removeChild( fileList.firstChild );
+ }
+ }
+
+ , retryUpload = function ()
+ {
+ this.setStatus("pending");
+ placedFiles[placedFiles.length] = this;
+ new ContextMenu( this.stage, this.menuKeys, "RMB", stage );
+ if( !busy ) beginUpload();
+ }
+
+ , removeItem = function ()
+ {
+ this.stage.style.marginTop = "-2em";
+ this.stage.style.opacity = "0";
+
+ if( placedFiles.indexOf(this) != -1 )
+ {
+ delete placedFiles[placedFiles.indexOf(this)];
+ }
+
+ Cycle.delay(function () {
+ fileList.removeChild(this);
+ }.bind(this.stage), 250);
+ }
+ ;
+
+ fileList.style.paddingTop = String(p_status.clientHeight) + "px";
+
+
+ var docDom = IDOMObject( document );
+ // Drag/drop handlers
+ docDom.addEventListener( "DragOver", function ( e )
+ {
+ e.preventDefault();
+ this.className = "dragged_over";
+ } );
+ docDom.addEventListener( "DragEnd", function () { this.className = ""; } );
+ docDom.addEventListener( "Drop", function ( e )
+ {
+ e.preventDefault();
+ this.className = "";
+
+ // Push files
+ var list = e.dataTransfer.files;
+ for(var i in list)
+ {
+ if(list[i].type != undefined)
+ {
+ var fileItem = new ASLFileItem(list[i], {clear: clearFileList, remove: removeItem, retry: retryUpload});
+ placedFiles[placedFiles.length] = fileItem;
+
+ fileList.appendChild(fileItem.stage);
+ new ContextMenu(fileItem.stage, fileItem.menuKeys, "LMB", stage);
+ }
+ }
+
+ if(!busy) beginUpload();
+
+ return false;
+ } );
+ };
+
+ var ASLFileItem = function (file, callbacks)
+ {
+ var statusblob = Dand.wrap("span", null, "asl_uItemStat", "\u25CF")
+ , msg
+ , _items = [
+ new EventKey("Remove this item", callbacks.remove.bind(this)) // Reserved for remove item
+ , null // Reserved for retry/ Open link
+ , null // Reserved for get Error message
+ , new EventKey("Clear all", callbacks.clear)
+ ]
+
+ , popMessage = function ()
+ {
+ new MessageBox("Upload error", msg).show();
+ }
+
+ , getFileLink = function ()
+ {
+ return msg;
+ }
+ ;
+
+ this.stage = Dand.wrap( null, "asl_item" + Perf.uuid, "asl_uItem", [ statusblob, Dand.textNode(file.name) ] );
+ this.file = file;
+ this.menuKeys = _items;
+
+ this.setStatus = function (st, message)
+ {
+ msg = message;
+ switch(st)
+ {
+ case "active":
+ statusblob.style.color = "cornflowerblue";
+ break;
+ case "error":
+ statusblob.style.color = "red";
+
+ _items[1] = new EventKey( "Retry", callbacks.retry.bind(this) );
+ _items[2] = new EventKey( "Whats wrong ?", popMessage );
+
+ break;
+ case "ok":
+ statusblob.style.color = "greenyellow";
+
+ _items[1] = new EventKey( "Copy file link", getFileLink );
+
+ break;
+ default:// pending
+ delete _items[1];
+ delete _items[2];
+ statusblob.style.color = "";
+ }
+
+ }
+ };
+
+ ns[ NS_EXPORT ]( EX_CLASS, "Uploader", Uploader );
+
+})();
diff --git a/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/AcquireLib.js b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/AcquireLib.js
new file mode 100644
index 0000000..f4d29f3
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/AcquireLib.js
@@ -0,0 +1,110 @@
+(function ()
+{
+ var ns = __namespace( "Astro.Blog.AstroEdit.Visualizer.Snippet" );
+
+ /** @type {System.utils.IKey} */
+ var IKey = __import( "System.utils.IKey" );
+ /** @type {System.utils.DataKey} */
+ var DataKey = __import( "System.utils.DataKey" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Components.MessageBox} */
+ var MessageBox = __import( "Components.MessageBox" );
+
+
+ var acquirelib = function(insertSnippet, snippetWrap, createContext, override)
+ {
+ var temp, i, j
+
+ , handler = function ()
+ {
+ // Input fields
+ var input_text = Dand.wrap('input', null, "v_snippet_input_single", null, new IKey("type", "text"));
+
+ if (this._stage)
+ {
+ input_text.value = this._text;
+ }
+
+ // Popup MessageBox
+ new MessageBox(
+ "Acquire library" + ( this._stage ? " (Edit)" : "" )
+ , Dand.wrape([ Dand.wrapc( "v_instruction flsf", "Module" ) , input_text ])
+ , "OK", "Cancel"
+ , visualizer.bind({ text:input_text, stage: this._stage })
+ ).show();
+ }
+
+ , visualizer = function (submitted, override)
+ {
+ var src = override ? override.value : this.text.value
+ , stage = this.stage;
+
+ if (submitted && src)
+ {
+ // Shared clause
+
+ if (!stage)
+ {
+ if (!src) return;
+
+
+ // Visualize component
+ temp = Dand.wrap(
+ 'span'
+ , null
+ , "flsf"
+ , "AcquireLib: " + src
+ , [
+ new DataKey( "value", src )
+ , new IKey(
+ "style", "background-color: #444; color: white; padding: 0.2em 0.5em;"
+ )
+ ]
+ );
+ insertSnippet(j = snippetWrap("AcquireLib", temp, false, 'span'), Boolean(override));
+ }
+ else
+ {
+ IDOMElement(stage).setAttribute( new DataKey("value", src) );
+
+ stage.removeChild(stage.firstChild);
+ stage.appendChild(Dand.textNode("AcquireLib: " + src));
+
+ // set temp back to stage
+ temp = stage;
+ }
+
+ i = {_text: src, _stage: temp};
+
+ // Set context menu
+ createContext(i, j, handler);
+ }
+ }
+ ;
+
+ if (override)
+ {
+ visualizer(true, override);
+ override = false;
+ }
+ else
+ {
+ return handler;
+ }
+ return true;
+ };
+
+ var compile = function (stage)
+ {
+ var element = IDOMElement(stage);
+
+ return "[acquirelib]" + element.getDAttribute("value") + "[/acquirelib]";
+ };
+
+ __static_method( acquirelib, "compile", compile );
+
+ ns[ NS_EXPORT ]( EX_CLASS, "AcquireLib", acquirelib );
+})();
diff --git a/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Code.js b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Code.js
new file mode 100644
index 0000000..db78df1
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Code.js
@@ -0,0 +1,191 @@
+(function ()
+{
+ var ns = __namespace( "Astro.Blog.AstroEdit.Visualizer.Snippet" );
+
+ /** @type {System.utils.IKey} */
+ var IKey = __import( "System.utils.IKey" );
+ /** @type {System.utils.DataKey} */
+ var DataKey = __import( "System.utils.DataKey" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Components.MessageBox} */
+ var MessageBox = __import( "Components.MessageBox" );
+
+ var escapeStr = ns[ NS_INVOKE ]( "escapeStr" );
+
+ var code = function ( insertSnippet, snippetWrap, createContext, override )
+ {
+ var temp, i, j
+ , codeLangs = IKey.quickDef(
+ "Plain text" , "plain"
+ , "AS3" , "as3"
+ , "bash" , "bash"
+ , "C#" , "c#"
+ , "C/C++" , "c"
+ , "CSS" , "css"
+ , "php" , "php"
+ , "Python" , "python"
+ , "Perl" , "perl"
+ , "Ruby" , "ruby"
+ , "Html/Xml" , "html"
+ , "Java" , "java"
+ , "JavaScript" , "js"
+ , "SQL" , "sql"
+ )
+
+ // Private methods
+ , compileListItems = function ()
+ {
+ var arr = [];
+ for ( i in codeLangs )
+ {
+ arr[ arr.length ] = Dand.wrapne(
+ "option"
+ , codeLangs[i].keyName
+ , new IKey( "value", codeLangs[i].keyValue )
+ );
+ }
+ return arr;
+ }
+
+ // Snippet Class structure: handler & visualizer
+ , handler = function ()
+ {
+ // Input fields
+ var v_snippetInput = Dand.wrap( "textarea", null, "v_snippet_input" )
+ , v_codelang = Dand.wrap( "select", null, "v_select flsf", compileListItems() );
+
+ if ( this._stage )
+ {
+ if ( this._lang )
+ {
+ for ( i = 0; i < codeLangs.length; i ++ )
+ {
+ if ( codeLangs[i].keyValue == this._lang )
+ {
+ v_codelang.selectedIndex = i;
+ }
+ }
+ }
+
+ v_snippetInput.value = this._content || "";
+ }
+ else
+ {
+ // Remember the last choice
+ if ( typeof( this.pSnippeCodeChoice ) == "number" )
+ v_codelang.selectedIndex = this.pSnippeCodeChoice;
+ }
+
+ // Popup MessageBox
+ new MessageBox(
+ ( this._stage ? "Edit" : "Insert" ) + " code snippet"
+ , Dand.wrapc(
+ "v_trimmer"
+ , [
+ Dand.wrapc( "v_instruction", v_codelang )
+ , v_snippetInput
+ ]
+ )
+ , "OK", "Cancel"
+ // Switcher
+ , visualizer.bind({ code:v_snippetInput, lang: v_codelang, stage: this._stage })
+ ).show();
+ }
+
+
+ , visualizer = function ( submitted, override )
+ {
+ var lang, code
+ , stage = this.stage;
+
+ if ( override )
+ {
+ lang = override.lang;
+ code = override.value;
+ }
+ else
+ {
+ lang = this.lang[this.pSnippeCodeChoice = this.lang.selectedIndex].value;
+ code = this.code.value;
+ }
+
+
+ if ( submitted && code )
+ {
+ if (!stage)
+ {
+ // Visualize component
+ temp = Dand.wrapc(
+ "v_box"
+ , [
+ Dand.wrapne( "pre", code )
+ , Dand.wrapc( "v_description", "Script language: " + lang )
+ ]
+ , [
+ new DataKey( "value", code )
+ , new DataKey( "lang", lang )
+ ]
+ );
+ insertSnippet( j = snippetWrap( "Code", temp ), Boolean( override ) );
+ }
+ else
+ {
+ IDOMElement( stage ).setAttribute(
+ [
+ new DataKey( "value", code )
+ , new DataKey( "lang", lang )
+ ]
+ );
+
+ temp = stage.firstChild;
+ temp.removeChild( temp.firstChild );
+ stage.firstChild.appendChild( Dand.textNode( code ) );
+
+ temp = stage.lastChild;
+ temp.removeChild( temp.firstChild );
+ temp.appendChild( Dand.textNode( "Script language: " + lang ) );
+
+ temp = stage;
+
+ }
+
+ i = { _lang: lang, _content: code, _stage: temp };
+
+ // Set context menu
+ createContext( i, j, handler );
+ }
+ }
+ ;
+
+ if ( override )
+ {
+ visualizer( true, override );
+ override = false;
+ }
+ else
+ {
+ return handler;
+ }
+ return true;
+ };
+
+ var compile = function ( stage )
+ {
+ // [code lang=\"" + lang + "\"]" + code + "[/code]"
+ var element = IDOMElement( stage )
+ , lang = element.getDAttribute( "lang" );
+
+ return "[code"
+ + (lang ? (" lang=\"" + lang + "\"") : "") + "]"
+ + escapeStr( element.getDAttribute( "value" ) )
+ + "[/code]"
+ ;
+ };
+
+ __static_method( code, "compile", compile );
+
+ ns[ NS_EXPORT ]( EX_CLASS, "Code", code );
+})();
diff --git a/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Html.js b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Html.js
new file mode 100644
index 0000000..f7b1fbd
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Html.js
@@ -0,0 +1,107 @@
+(function ()
+{
+ var ns = __namespace( "Astro.Blog.AstroEdit.Visualizer.Snippet" );
+
+ /** @type {System.utils.IKey} */
+ var IKey = __import( "System.utils.IKey" );
+ /** @type {System.utils.DataKey} */
+ var DataKey = __import( "System.utils.DataKey" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Components.MessageBox} */
+ var MessageBox = __import( "Components.MessageBox" );
+
+ var escapeStr = ns[ NS_INVOKE ]( "escapeStr" );
+
+ var html = function (insertSnippet, snippetWrap, createContext, override)
+ {
+ var temp, i, j
+
+ , handler = function ()
+ {
+ // Input fields
+ var v_snippetInput = Dand.wrap( "textarea", null, "v_snippet_input" );
+
+ if ( this._stage )
+ {
+ v_snippetInput.value = this._code;
+ }
+
+ // Popup MessageBox
+ new MessageBox(
+ "Insert Html snippet"
+ , v_snippetInput
+ , "OK", "Cancel"
+ , visualizer.bind({ code: v_snippetInput, stage: this._stage })
+ ).show();
+ }
+
+ , visualizer = function (submitted, override)
+ {
+
+ var code, stage = this.stage;
+
+ code = override ? override.value : this.code.value;
+
+ if (submitted && code)
+ {
+ if (!stage)
+ {
+ // Visualize component
+ temp = Dand.wrapc(
+ "v_box"
+ , [
+ Dand.wrapne( "pre", code )
+ , Dand.wrapc( "v_description", "Raw Html Codes" )
+ ]
+ , [
+ new DataKey( "value", code )
+ , new IKey( "style", "max-height: 150px;" )
+ ]
+ );
+ insertSnippet(j = snippetWrap("Html", temp), Boolean(override));
+ }
+ else
+ {
+ IDOMElement( stage ).setAttribute(new DataKey( "value", code ));
+
+ temp = stage.firstChild;
+ temp.removeChild(temp.firstChild);
+ stage.firstChild.appendChild(Dand.textNode( code ));
+
+ temp = stage;
+
+ }
+
+ i = { _code: code, _stage: temp };
+
+ // Set context menu
+ createContext( i, j, handler );
+ }
+ };
+
+ if ( override )
+ {
+ visualizer( true, override );
+ override = false;
+ }
+ else
+ {
+ return handler;
+ }
+ return true;
+
+ };
+
+ var compile = function (stage)
+ {
+ // [html][/html]
+ return "[html]" + escapeStr( IDOMElement(stage).getDAttribute("value") ) + "[/html]";
+ };
+
+ __static_method( html, "compile", compile );
+
+ ns[ NS_EXPORT ]( EX_CLASS, "Html", html );
+})();
diff --git a/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Image.js b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Image.js
new file mode 100644
index 0000000..d0ca455
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Image.js
@@ -0,0 +1,157 @@
+(function ()
+{
+ var ns = __namespace( "Astro.Blog.AstroEdit.Visualizer.Snippet" );
+
+ /** @type {System.utils.IKey} */
+ var IKey = __import( "System.utils.IKey" );
+ /** @type {System.utils.DataKey} */
+ var DataKey = __import( "System.utils.DataKey" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Components.MessageBox} */
+ var MessageBox = __import( "Components.MessageBox" );
+
+ var image = function( insertSnippet, snippetWrap, createContext, override )
+ {
+ var temp, i, j
+
+ , handler = function ()
+ {
+ // Input fields
+ var input_url = Dand.wrap(
+ "input", null, "v_snippet_input_single", null, new IKey( "type", "text" )
+ )
+ , input_a = Dand.wrap(
+ "input", null, "v_snippet_input_single", null, new IKey( "type", "text" )
+ );
+
+ if (this._stage)
+ {
+ input_url.value = this._url;
+ input_a.value = this._href;
+ }
+
+ // Popup MessageBox
+ new MessageBox(
+ ( this._stage ? "Edit" : "Insert" ) + " image snippet",
+ Dand.wrape([
+ Dand.wrapc( "v_instruction flsf", "Link to the image:" )
+ , input_url
+ , Dand.wrapc( "v_instruction flsf", "Ancohr link(optional):" )
+ , input_a
+ ]
+ )
+ , "OK", "Cancel"
+ , visualizer.bind({ url:input_url, href:input_a, stage: this._stage })
+ ).show();
+ }
+
+ , visualizer = function ( submitted, override )
+ {
+ var src, href
+ , stage = this.stage;
+
+ if ( override )
+ {
+ src = override.value;
+ href = override.href;
+ }
+ else
+ {
+ src = this.url.value;
+ href = this.href.value;
+ }
+
+ if ( submitted && src )
+ {
+
+ // Shared clause
+ i = Dand.textNode(href ? ("link to: " + href): "No link");
+
+ if ( !stage )
+ {
+ if ( !src ) return;
+
+
+ // Visualize component
+ temp = Dand.wrapc(
+ "v_box"
+ , [
+ Dand.wrapna(
+ "img"
+ , [
+ new IKey("src", src)
+ , new IKey("style", "max-width: 100%")
+ ]
+ )
+ // caption
+ , Dand.wrapc( "v_description", i )
+ ]
+ , [
+ new DataKey("value", src)
+ , new DataKey("href", href)
+ ]
+ );
+ insertSnippet( j = snippetWrap( "Image", temp ), Boolean( override ) );
+ }
+ else
+ {
+ IDOMElement( stage ).setAttribute
+ ([
+ new DataKey( "value", src )
+ , new DataKey( "href", href )
+ ]);
+
+ stage.firstChild.setAttribute("src", src);
+
+ temp = stage.lastChild;
+ temp.removeChild(temp.firstChild);
+ temp.appendChild(i);
+
+ // set temp back to stage
+ temp = stage;
+ }
+
+ i = { _url: src, _href: href, _stage: temp };
+
+ // Set context menu
+ createContext( i, j, handler );
+ }
+ }
+ ;
+
+ if ( override )
+ {
+ visualizer( true, override );
+ override = false;
+ }
+ else
+ {
+ return handler;
+ }
+ return true;
+ };
+
+ var compile = function ( stage )
+ {
+ // [img href=\"" + this.href.value + "\"]" + this.url.value + "[/img]
+ var element = IDOMElement( stage )
+ , href = element.getDAttribute( "href" );
+
+ return "[img"
+ + ( href ? (" href=\"" + href + "\"") : "" )
+ + "]"
+ + element.getDAttribute( "value" ) + "[/img]"
+ ;
+ };
+
+ // Alias
+ var alias = "img";
+
+ __static_method( image, "compile", compile );
+ __const( image, "alias", alias );
+
+ ns[ NS_EXPORT ]( EX_CLASS, "Image", image );
+})();
diff --git a/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Link.js b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Link.js
new file mode 100644
index 0000000..67039c3
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Link.js
@@ -0,0 +1,131 @@
+(function ()
+{
+ var ns = __namespace( "Astro.Blog.AstroEdit.Visualizer.Snippet" );
+
+ /** @type {System.utils.IKey} */
+ var IKey = __import( "System.utils.IKey" );
+ /** @type {System.utils.DataKey} */
+ var DataKey = __import( "System.utils.DataKey" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Components.MessageBox} */
+ var MessageBox = __import( "Components.MessageBox" );
+
+ var link = function(insertSnippet, snippetWrap, createContext, override)
+ {
+ var temp, i, j
+
+ , handler = function ()
+ {
+ // Input fields
+ var input_text = Dand.wrap('input', null, "v_snippet_input_single", null, new IKey("type", "text"))
+ , input_a = Dand.wrap('input', null, "v_snippet_input_single", null, new IKey("type", "text"));
+
+ if (this._stage)
+ {
+ input_text.value = this._text;
+ input_a.value = this._href;
+ }
+
+ // Popup MessageBox
+ new MessageBox(
+ (this._stage ? "Edit" : "Insert") + " link",
+ Dand.wrape([
+ Dand.wrapc("v_instruction flsf", "Text")
+ , input_text
+ , Dand.wrapc("v_instruction flsf", "Link to:")
+ , input_a
+ ]
+ )
+ , "OK", "Cancel", visualizer.bind({text:input_text, href:input_a, stage: this._stage})).show();
+ }
+
+ , visualizer = function (submitted, override)
+ {
+ var src, href
+ , stage = this.stage;
+
+ if (override)
+ {
+ src = override.value;
+ href = override.href;
+ }
+ else
+ {
+ src = this.text.value;
+ href = this.href.value;
+ }
+
+ if (submitted && src && href)
+ {
+
+ // Shared clause
+
+ if (!stage)
+ {
+ if (!src) return;
+
+
+ // Visualize component
+ temp =
+ Dand.wrapne('span', src
+ , [
+ new DataKey("value", src)
+ , new DataKey("href", href)
+ ]
+ );
+ insertSnippet(j = snippetWrap("Link", temp, false, 'span'), Boolean(override));
+ }
+ else
+ {
+ IDOMElement(stage).setAttribute
+ (
+ [
+ new DataKey("value", src)
+ , new DataKey("href", href)
+ ]
+ );
+
+ stage.removeChild(stage.firstChild);
+ stage.appendChild(Dand.textNode(src));
+
+ // set temp back to stage
+ temp = stage;
+ }
+
+ i = {_text: src, _href: href, _stage: temp};
+
+ // Set context menu
+ createContext(i, j, handler);
+ }
+ }
+ ;
+
+ if (override)
+ {
+ visualizer(true, override);
+ override = false;
+ }
+ else
+ {
+ return handler;
+ }
+ return true;
+ };
+
+ var compile = function (stage)
+ {
+ // [link href=\"" + this.href.value + "\"]" + text + "[/link]
+ var element = IDOMElement(stage)
+ , href = element.getDAttribute("href");
+
+ return "[link" + (href ? (" href=\"" + href + "\"") : "") + "]" + element.getDAttribute("value") + "[/link]";
+ };
+
+
+ __static_method( link, "compile", compile );
+
+ ns[ NS_EXPORT ]( EX_CLASS, "Link", link );
+})();
diff --git a/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/SiteFile.js b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/SiteFile.js
new file mode 100644
index 0000000..00cb176
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/SiteFile.js
@@ -0,0 +1,174 @@
+(function ()
+{
+ var ns = __namespace( "Astro.Blog.AstroEdit.Visualizer.Snippet" );
+
+ /** @type {System.utils.IKey} */
+ var IKey = __import( "System.utils.IKey" );
+ /** @type {System.utils.DataKey} */
+ var DataKey = __import( "System.utils.DataKey" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Components.MessageBox} */
+ var MessageBox = __import( "Components.MessageBox" );
+ /** @type {Astro.Blog.Config} */
+ var Config = __import( "Astro.Blog.Config" );
+ /** @type {System.utils.Perf} */
+ var Perf = __import( "System.utils.Perf" );
+ /** @type {Astro.utils.Date} */
+ var XDate = __import( "Astro.utils.Date" );
+
+
+ var escapeStr = ns[ NS_INVOKE ]( "escapeStr" );
+
+ var getData = __import( "System.Net.getData" );
+
+ /** @type {_AstConf_.SiteFile} */
+ var config = null;
+
+ var sitefile = function ( insertSnippet, snippetWrap, createContext, override )
+ {
+ config = Config.get( "SiteFile" );
+ if( !config ) throw new Error( "config is not defined" );
+
+ var temp, i, j
+
+ , handler = function ()
+ {
+ // Input fields
+ var v_snippetInput = Dand.wrap("input", null, "v_snippet_input_single", null, new IKey("type", "text"));
+
+ // Popup MessageBox
+ new MessageBox("Insert site file", v_snippetInput, "OK", "Cancel", visualizer.bind({code: v_snippetInput})).show();
+ }
+
+ , __applyData = function (e)
+ {
+ var finfo = JSON.parse(e).file, s, m, l
+ , content = this.stage.firstChild
+ , desc = this.stage.lastChild
+ , _hash = this.hash;
+
+ switch ( finfo.type )
+ {
+ case "image":
+
+ this.stage.removeChild(content);
+ // Default size is large
+ var _image = Dand.wrapna("img", new IKey("src", config.path.image.large + _hash + ".jpg"))
+ , _stage = IDOMElement(this.stage)
+ , keys = [ new IKey( "type", "radio" ), new IKey( "name", "size_grp" + Perf.uuid ) ]
+ , sid
+ , selectionChanged = function ()
+ {
+ _stage.setAttribute(new DataKey("size", this.size));
+ //// Handles the size selection
+ switch(this.size)
+ {
+ case "small":
+ _image.setAttribute("src", config.path.image.small + _hash + ".jpg");
+ break;
+ case "medium":
+ _image.setAttribute("src", config.path.image.medium + _hash + ".jpg");
+ break;
+ case "large":
+ _image.setAttribute("src", config.path.image.large + _hash + ".jpg");
+ break;
+ }
+ };
+
+ this.stage.insertBefore(_image, desc);
+
+ desc.removeChild(desc.firstChild);
+ desc.appendChild(
+ Dand.wrape([
+ Dand.textNode("Size: ")
+ , s = Dand.wrapna("input", keys.concat( new IKey("id", sid = "size_" + Perf.uuid) ))
+ , Dand.wrapne("label", "small", new IKey("for", sid))
+ , m = Dand.wrapna("input", keys.concat( new IKey("id", sid = "size_" + Perf.uuid) ))
+ , Dand.wrapne("label", "medium", new IKey("for", sid))
+ , l = Dand.wrapna("input", keys.concat( new IKey("checked", "1"), new IKey("id", sid = "size_" + Perf.uuid) ))
+ , Dand.wrapne("label", "large (default)", new IKey("for", sid))
+ ])
+ );
+
+ // ad handlers to handles size change event
+ IDOMElement(s).addEventListener("Change", selectionChanged.bind({size: "small"}));
+ IDOMElement(m).addEventListener("Change", selectionChanged.bind({size: "medium"}));
+ IDOMElement(l).addEventListener("Change", selectionChanged.bind({size: "large"}));
+ break;
+ case "audio":
+ // TODO
+ break;
+ default:
+ content.firstChild.nodeValue = "Regular file: " + finfo.name;
+ content.style.paddingBottom = "2em";
+ content.appendChild( Dand.wrap( "br" ) );
+ content.appendChild( Dand.textNode( "Date: " + XDate.pretty( new Date( finfo.date_created ), true ) ) );
+ }
+
+ }
+
+ , loadFailed = function (e) { }
+
+ , visualizer = function (submitted, override)
+ {
+
+ var hash = override ? override.value : this.code.value
+ , _obj = {file: hash};
+
+ if ( submitted && hash )
+ {
+ // Visualize component
+
+ temp = Dand.wrapc("v_box"
+ , [
+ Dand.wrape("Getting information from Server ...")
+ , Dand.wrapc("v_description", "Site file (hash): " + hash)
+ ]
+ , [
+ new DataKey("value", hash)
+ , new DataKey("size", "large")
+ , new IKey("style", "max-height: 150px;")
+ ]
+ );
+
+ // Get data from site library
+ getData( config.path.info + hash, __applyData.bind({stage: temp, hash: hash}), loadFailed );
+
+ insertSnippet(j = snippetWrap("SiteFile", temp), Boolean(override));
+
+ // Set context menu
+ createContext(null, j);
+ }
+ };
+
+ if (override)
+ {
+ visualizer(true, override);
+ override = false;
+ }
+ else
+ {
+ return handler;
+ }
+ return true;
+
+ };
+
+ var compile = function ( stage )
+ {
+ stage = IDOMElement( stage );
+ // [html][/html]
+ return "[sitefile"
+ + " size=\"" + stage.getDAttribute( "size" ) + "\"" + "]"
+ + escapeStr( stage.getDAttribute( "value" ) )
+ + "[/sitefile]";
+ };
+
+ __static_method( sitefile, "compile", compile );
+
+ ns[ NS_EXPORT ]( EX_CLASS, "SiteFile", sitefile );
+
+})();
diff --git a/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Sound.js b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Sound.js
new file mode 100644
index 0000000..a654bc2
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Sound.js
@@ -0,0 +1,146 @@
+(function ()
+{
+ var ns = __namespace( "Astro.Blog.AstroEdit.Visualizer.Snippet" );
+
+ /** @type {System.utils.IKey} */
+ var IKey = __import( "System.utils.IKey" );
+ /** @type {System.utils.DataKey} */
+ var DataKey = __import( "System.utils.DataKey" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Components.MessageBox} */
+ var MessageBox = __import( "Components.MessageBox" );
+
+ var compileProp = ns[ NS_INVOKE ]( "compileProp" );
+
+ var sound = function (insertSnippet, snippetWrap, createContext, override)
+ {
+ var temp, i, j
+ , defaultArt = "http://file.astropenguin.net/blog/layout-images/disc_s.png"
+ , handler = function ()
+ {
+ // Input fields
+ var input_url = Dand.wrap('input', null, "v_snippet_input_single", null, new IKey("type", "text"))
+ , input_albumArt = Dand.wrap('input', null, "v_snippet_input_single", null, new IKey("type", "text"))
+ , input_lrc = Dand.wrap('input', null, "v_snippet_input_single", null, new IKey("type", "text"))
+ ;
+
+ if (this._stage)
+ {
+ input_url.value = this._url;
+ input_albumArt.value = this._albumArt;
+ input_lrc.value = this._lrc;
+ }
+
+ new MessageBox(
+ (this._stage ? "Edit" : "Insert") + " sound snippet"
+ , Dand.wrape([
+ Dand.wrapc("v_instruction flsf", "Link to sound:")
+ , input_url
+
+ , Dand.wrapc("v_instruction flsf", "Link to album art(optional):")
+ , input_albumArt
+
+ , Dand.wrapc("v_instruction flsf", "Link to lrc(optional):")
+ , input_lrc
+ ]
+ )
+ , "OK", "Cancel", visualizer.bind({url:input_url, albumArt:input_albumArt, lrc:input_lrc, stage: this._stage})).show();
+ }
+
+ , visualizer = function (submitted)
+ {
+
+ var src, albumArt , lrc
+ , stage = this.stage;
+
+ if (override)
+ {
+ src = override.url;
+ albumArt = override.albumArt;
+ lrc = override.lrc;
+ }
+ else
+ {
+ src = this.url.value;
+ albumArt = this.albumArt.value;
+ lrc = this.lrc.value;
+ }
+
+ if (submitted && src)
+ {
+
+ if (!stage)
+ {
+ // Visualize component
+ temp = Dand.wrapc('v_box'
+ , [
+ Dand.wrape(null, new IKey
+ (
+ "style", "height: 75px; background-size: auto 75px; background-repeat: no-repeat;"
+ + "background-image: url(" + (albumArt ? albumArt : defaultArt) + ")"
+ )
+ )
+ , Dand.wrapc('v_description', "Sound snippet")
+ ]
+ , [
+ new DataKey("value", "")
+ , new DataKey("url", src)
+ , new DataKey("albumArt", albumArt)
+ , new DataKey("lrc", lrc)
+ ]
+ );
+ insertSnippet(j = snippetWrap("Sound", temp), Boolean(override));
+ }
+ else
+ {
+ // Edit properties
+ stage.firstChild.style.backgroundImage = "url(" + (albumArt ? albumArt : defaultArt) + ")" ;
+
+ IDOMElement(stage).setAttribute
+ (
+ [
+ new DataKey("url", src)
+ , new DataKey("albumArt", albumArt)
+ , new DataKey("lrc", lrc)
+ ]
+ );
+
+ }
+
+ i = {_url: src, _albumArt: albumArt, _lrc: lrc, _stage: temp};
+
+ // Set context menu
+ createContext(i, j, handler);
+ }
+ }
+ ;
+
+ if (override)
+ {
+ visualizer(true, override);
+ override = false;
+ }
+ else
+ {
+ return handler;
+ }
+ return true;
+ };
+
+ var compile = function (stage)
+ {
+ // [sound url="" albumArt="" lrc="" ][/sound]");
+ var element = IDOMElement(stage)
+ , props = ["url", "albumArt", "lrc"];
+
+ return "[sound" + compileProp( element, props ) + "][/sound]";
+ };
+
+
+ __static_method( sound, "compile", compile );
+
+ ns[ NS_EXPORT ]( EX_CLASS, "Sound", sound );
+})();
diff --git a/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Spoiler.js b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Spoiler.js
new file mode 100644
index 0000000..98a3098
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Spoiler.js
@@ -0,0 +1,154 @@
+(function ()
+{
+ var ns = __namespace( "Astro.Blog.AstroEdit.Visualizer.Snippet" );
+
+ /** @type {System.utils.IKey} */
+ var IKey = __import( "System.utils.IKey" );
+ /** @type {System.utils.DataKey} */
+ var DataKey = __import( "System.utils.DataKey" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Components.MessageBox} */
+ var MessageBox = __import( "Components.MessageBox" );
+
+ var compileProp = ns[ NS_INVOKE ]( "compileProp" );
+
+ var spoiler = function(insertSnippet, snippetWrap, createContext, override)
+ {
+ var temp, i, j
+
+ , handler = function ()
+ {
+ // Input fields
+ var v_snippetInput = Dand.wrap('textarea', null, "v_snippet_input")
+ , input_title = Dand.wrap('input', null, "v_snippet_input_single", null, new IKey("type", "text"))
+ , input_expanded = Dand.wrapna('input', new IKey("type", "checkbox"))
+
+ if (this._stage)
+ {
+ v_snippetInput.value = this._content;
+ input_title.value = this._title;
+ input_expanded.checked = (this._expanded == "on");
+ }
+
+ // Popup MessageBox
+ new MessageBox("Insert spoiler content"
+ , Dand.wrape([
+ Dand.wrapc("v_instruction flsf", "Title")
+ , input_title
+
+ , Dand.wrapc("v_instruction flsf", "Content")
+ , v_snippetInput
+
+ , Dand.wrape([ input_expanded, Dand.textNode( "Expanded" ) ])
+ ]
+ )
+ , "OK", "Cancel", visualizer.bind({title: input_title, content:v_snippetInput, expanded: input_expanded, stage: this._stage})).show();
+ }
+
+ , visualizer = function (submitted, override)
+ {
+ var content, title, expanded
+ , stage = this.stage;
+
+ if (override)
+ {
+ content = override.value;
+ title = override.title;
+ expanded = override.expanded ? "on" : "";
+ }
+ else
+ {
+ content = this.content.value;
+ title = this.title.value;
+ expanded = this.expanded.checked ? "on" : "";
+ }
+
+ if (submitted && content)
+ {
+ // Shared Clause
+ i = Dand.textNode(title || "Spoiler");
+ if (!stage)
+ {
+ if (!content) return;
+
+ // Visualize component
+ temp = Dand.wrapc('v_box',
+ [
+ // caption
+ Dand.wrapc('v_caption', i)
+ , Dand.textNode(content)
+ ],
+ [
+ new DataKey("value", content)
+ , new DataKey("title", title)
+ , new DataKey("expanded", expanded)
+ ]
+ );
+ insertSnippet(j = snippetWrap("Spoiler", temp), Boolean(override));
+ }
+ else
+ {
+ IDOMElement(stage).setAttribute
+ (
+ [
+ new DataKey("value", content)
+ , new DataKey("title", title)
+ , new DataKey("expanded", expanded)
+ ]
+ );
+
+ temp = stage.firstChild;
+ temp.removeChild(temp.firstChild);
+ temp.appendChild(i);
+
+ stage.removeChild(stage.lastChild);
+ stage.appendChild(Dand.textNode(content));
+
+ // set temp back to stage
+ temp = stage;
+ }
+
+ i = {
+ _title: title
+ , _content: content
+ , _expanded: expanded || "off"
+ , _stage: temp
+ };
+
+ // Set context menu
+ createContext(i, j, handler);
+ }
+ }
+ ;
+
+ if (override)
+ {
+ visualizer(true, override);
+ override = false;
+ }
+ else
+ {
+ return handler;
+ }
+ return true;
+ };
+
+ var compile = function ( stage )
+ {
+ var element = IDOMElement( stage )
+ , props = ["title", "expanded"];
+
+ return "[spoiler"
+ + compileProp( element, props )
+ + "]"
+ + element.getDAttribute( "value" )
+ + "[/spoiler]";
+ };
+
+ __static_method( spoiler, "compile", compile );
+
+ ns[ NS_EXPORT ]( EX_CLASS, "Spoiler", spoiler );
+})();
diff --git a/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Swf.js b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Swf.js
new file mode 100644
index 0000000..4222f66
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Swf.js
@@ -0,0 +1,214 @@
+(function ()
+{
+ var ns = __namespace( "Astro.Blog.AstroEdit.Visualizer.Snippet" );
+
+ /** @type {System.utils.IKey} */
+ var IKey = __import( "System.utils.IKey" );
+ /** @type {System.utils.DataKey} */
+ var DataKey = __import( "System.utils.DataKey" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Components.MessageBox} */
+ var MessageBox = __import( "Components.MessageBox" );
+
+ var compileProp = ns[ NS_INVOKE ]( "compileProp" );
+
+ var swf = function (insertSnippet, snippetWrap, createContext, override)
+ {
+ var
+ temp, i, j
+
+ // Pending
+ , defaultArt = ""
+
+ , handler = function ()
+ {
+ // Input fields
+ var input_w = Dand.wrapna('input', new IKey("type", "number"))
+ , input_h = Dand.wrapna('input', new IKey("type", "number"))
+ , input_title = Dand.wrap('input', null, "v_snippet_input_single", null, [new IKey("type", "text"), new IKey("placeHolder", "optional")] )
+ , input_desc = Dand.wrap('input', null, "v_snippet_input_single", null, [new IKey("type", "text"), new IKey("placeHolder", "optional")] )
+ , input_api = Dand.wrapna('input', new IKey("type", "checkbox"))
+ , input_preview = Dand.wrap('input', null, "v_snippet_input_single", null, [new IKey("type", "text"), new IKey("placeHolder", "optional")] )
+ , input_src = Dand.wrap('input', null, "v_snippet_input_single", null, new IKey("type", "text"))
+ , stage = this._stage;
+
+
+ if (stage)
+ {
+ input_src.value = this._src;
+ input_w.value = this._width;
+ input_h.value = this._height;
+ input_title.value = this._title;
+ input_desc.value = this._desc;
+ input_preview.value = this._preview;
+ input_api.checked = (this._extAPI == "on");
+ }
+
+ new MessageBox(
+ "Insert swf object",
+ Dand.wrape([
+ Dand.wrape([Dand.textNode("Dimensions: "), input_w, Dand.textNode("\u00D7"), input_h, Dand.textNode("px")])
+
+ , Dand.wrapc("v_instruction flsf", "Title:")
+ , input_title
+
+ , Dand.wrapc("v_instruction flsf", "Description:")
+ , input_desc
+
+ , Dand.wrapc("v_instruction flsf", "Preview image:")
+ , input_preview
+
+ , Dand.wrapc("v_instruction flsf", "Link to swf:")
+ , input_src
+
+ , Dand.wrape([ input_api, Dand.textNode("Use external API") ] )
+ ]
+ )
+ , "OK", "Cancel", visualizer.bind({src:input_src, width:input_w, height:input_h, title: input_title, desc: input_desc, extAPI: input_api, preview: input_preview, stage: this._stage})).show();
+ }
+
+ , visualizer = function (submitted, override)
+ {
+ var src, width, height, title, desc, preview, extAPI
+ , stage = this.stage;
+
+ if (override)
+ {
+ src = override.value;
+ width = override.width;
+ height = override.height;
+ title = override.title;
+ desc = override.desc;
+ preview = override.preview;
+ extAPI = override.useExtAPI;
+ }
+ else
+ {
+ src = this.src.value;
+ width = this.width.value;
+ height = this.height.value;
+ title = this.title.value;
+ desc = this.desc.value;
+ preview = this.preview.value;
+ extAPI = this.extAPI.checked ? "on" : "";
+ }
+
+ if (submitted && width && height && src)
+ {
+
+ if (!stage)
+ {
+ // Visualize component
+
+ temp = Dand.wrapc('v_box'
+ , Dand.wrapc('v_description'
+ , [
+ Dand.wrap('span', null, 'fls', title)
+ , Dand.textNode(" ")
+ , Dand.wrap('span', null, 'v_caption_desc flsf', desc)
+ ]
+ , new IKey('style', 'font-size: 2em')
+ )
+ , [
+ new DataKey("value", src)
+ , new DataKey("width", width)
+ , new DataKey("height", height)
+ , new DataKey("title", title)
+ , new DataKey("desc", desc)
+ , new DataKey("preview", preview)
+ , new DataKey("useExtAPI", extAPI)
+
+ , new IKey
+ (
+ "style"
+ , "background-position: center; background-repeat: no-repeat; "
+ + "width: " + width +"px; height: " + height + "px; "
+ + "background-image: url(" + (preview ? preview : defaultArt) + ")"
+ )
+ ]
+ );
+ insertSnippet(j = snippetWrap("Swf", temp), Boolean(override));
+ }
+ else
+ {
+ IDOMElement(stage).setAttribute
+ (
+ [
+ new DataKey("value", src)
+ , new DataKey("width", width)
+ , new DataKey("height", height)
+ , new DataKey("title", title)
+ , new DataKey("desc", desc)
+ , new DataKey("preview", preview)
+ , new DataKey("useExtAPI", extAPI)
+ ]
+ );
+
+ stage.style.width = width + "px";
+ stage.style.height = height + "px";
+ stage.style.backgroundImage = "url(" + (preview ? preview : defaultArt) + ")";
+
+ // Title
+ temp = stage.firstChild.firstChild;
+ temp.removeChild(temp.firstChild);
+ temp.appendChild(Dand.textNode(title));
+
+ // Desc
+ temp = stage.firstChild.lastChild;
+ temp.removeChild(temp.firstChild);
+ temp.appendChild(Dand.textNode(desc));
+
+ temp = stage;
+ }
+
+
+ i = {
+ _src: src
+ , _width: width
+ , _height: height
+ , _title: title
+ , _desc: desc
+ , _preview: preview
+ , _extAPI: extAPI || "off"
+ , _stage: temp
+ };
+
+ // Set context menu
+ createContext(i, j, handler);
+ }
+ }
+ ;
+
+ if (override)
+ {
+ visualizer(true, override);
+ override = false;
+ }
+ else
+ {
+ return handler;
+ }
+ return true;
+ };
+
+ var compile = function (stage)
+ {
+ // [swf width="" height="" title="" desc="" preview="" useExtAPI="" ]src[/swf];
+ var element = IDOMElement(stage)
+ , props = ["width", "height", "title", "desc", "preview", "useExtAPI"];
+
+ return "[swf"
+ + compileProp( element, props )
+ + "]"
+ + element.getDAttribute("value")
+ + "[/swf]"
+ ;
+ };
+
+ __static_method( swf, "compile", compile );
+
+ ns[ NS_EXPORT ]( EX_CLASS, "Swf", swf );
+})();
diff --git a/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Video.js b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Video.js
new file mode 100644
index 0000000..ea7437c
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Video.js
@@ -0,0 +1,137 @@
+(function ()
+{
+ var ns = __namespace( "Astro.Blog.AstroEdit.Visualizer.Snippet" );
+
+ /** @type {System.utils.IKey} */
+ var IKey = __import( "System.utils.IKey" );
+ /** @type {System.utils.DataKey} */
+ var DataKey = __import( "System.utils.DataKey" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Components.MessageBox} */
+ var MessageBox = __import( "Components.MessageBox" );
+
+ var video = function (insertSnippet, snippetWrap, createContext, override)
+ {
+ var temp, i, j
+
+ , getVimeoThumbnail = function (vtag)
+ {
+ getData("http://vimeo.com/api/oembed.json?url=http%3A//vimeo.com/" + vtag.getAttribute("data-value"), setThumbnail.bind(vtag), noThumb.bind(vtag));
+ }
+
+ , noThumb = function ()
+ {
+
+ }
+
+ , setThumbnail = function (str)
+ {
+ this.style.background = "black url(" + JSON.parse(str)["thumbnail_url"] + ") center center no-repeat";
+
+ }
+
+ , handler = function ()
+ {
+ // Input fields
+ var input_url = Dand.wrap('input', null, "v_snippet_input_single", null, new IKey("type", "text"));
+
+ // Popup MessageBox
+ new MessageBox("Insert video snippet"
+ , Dand.wrape([ Dand.wrapc("v_instruction flsf", "Paste a vimeo/Youtube link below:"), input_url ])
+ , "OK", "Cancel", visualizer.bind(input_url)
+ ).show();
+ }
+
+ , visualizer = function (submitted, override)
+ {
+ var t, v;
+
+ if (override)
+ {
+ v = override.value;
+ i = override.type;
+ }
+ else
+ {
+ t = this.value;
+ // Match youtube links
+ v = t.match(/\/\/(www\.)?youtube\.com\/watch\?.*?v=([^\&\?\/\#]+)/) || t.match(/\/\/(www\.)?youtube\.com\/embed\/([^\&\?\/]+)/)
+ || t.match(/\/\/(www\.)?youtube\.com\/v\/([^\&\?\/]+)/) || t.match(/\/\/(www\.)?youtu\.be\/([^\&\?\/]+)/);
+ if (v)
+ {
+ i = "youtube";
+ v = v[2];
+ }
+ else
+ {
+ // match vimeo links
+ v = t.match(/\/\/(www\.)?vimeo.com\/(\d+)($|\/)/);
+ if (v)
+ {
+ i = "vimeo";
+ v = v[2];
+ }
+ else
+ {
+ // None matched, do nothing
+ return;
+ }
+ }
+ }
+
+ if (submitted)
+ {
+
+ // Visualize component
+ temp = Dand.wrapc('v_box', Dand.wrapc('v_description', "Video(url): " + t)
+ , [
+ new DataKey("value", v)
+ , new DataKey("type", i)
+ , new IKey("style"
+ , "width: 640px; height: 390px;"
+ + ( (i[0] == "v") ? "" : ("background: black url(http://img.youtube.com/vi/" + v + "/hqdefault.jpg) no-repeat center center;") )
+ )
+ ]
+ );
+
+ if (i[0] == "v")
+ {
+ getVimeoThumbnail(temp);
+ }
+
+ insertSnippet(j = snippetWrap("Video", temp), Boolean(override));
+
+ // Set context menu
+ createContext(null, j);
+ }
+ }
+ ;
+
+ if (override)
+ {
+ visualizer(true, override);
+ override = false;
+ }
+ else
+ {
+ return handler;
+ }
+ return true;
+ };
+
+ var compile = function (stage)
+ {
+ // [video type=\"youtube\"]" + v[2] + "[/video]
+ var element = IDOMElement(stage)
+ , type = element.getDAttribute("type");
+
+ return "[video type=\"" + type + "\"]" + element.getDAttribute("value") + "[/video]";
+ };
+
+ __static_method( video, "compile", compile );
+
+ ns[ NS_EXPORT ]( EX_CLASS, "Video", video );
+})();
diff --git a/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/_this.js b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/_this.js
new file mode 100644
index 0000000..744b20b
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/_this.js
@@ -0,0 +1,25 @@
+(function()
+{
+ var ns = __namespace( "Astro.Blog.AstroEdit.Visualizer.Snippet" );
+
+ var escapeStr = function ( str )
+ {
+ return str.replace( /\[/g, "[" ).replace( /\]/g, "]" );
+ };
+
+ var compileProp = function ( _iDOM, keys )
+ {
+ var i, j = "", k;
+ for (i in keys)
+ {
+ if( ( k = _iDOM.getDAttribute(keys[i]) ) )
+ {
+ j += " " + keys[i] + "=\"" + k + "\"";
+ }
+ }
+ return j;
+ };
+
+ ns[ NS_EXPORT ]( EX_FUNC, "escapeStr", escapeStr );
+ ns[ NS_EXPORT ]( EX_FUNC, "compileProp", compileProp );
+})();
diff --git a/botanjs/src/Astro/Blog/AstroEdit/Visualizer/_this.css b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/_this.css
new file mode 100644
index 0000000..0dfa89c
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/_this.css
@@ -0,0 +1,101 @@
+.v_snippet_input {
+ width: 600px;
+ height: 350px;
+}
+
+.v_boundary {
+ cursor: default;
+ padding: 0.2em;
+}
+
+.v_box {
+ min-height: 3em;
+ overflow: hidden;
+ position: relative;
+}
+
+.v_caption, .v_description {
+ background: rgba(0, 0, 0, 0.8);
+ padding: 0.2em;
+ color: white;
+
+ width: 100%;
+
+ font-family: sans-serif;
+ font-size: 1em;
+}
+
+.v_caption {
+ position: relative;
+}
+
+.v_description {
+ position: absolute;
+ left: 0;
+ bottom: 0;
+}
+
+.v_caption_desc {
+ font-size: 60%;
+}
+
+.v_boundary:hover {
+ background-color: rgba(0, 0, 0, 0.2);
+}
+
+.v_snippet_input_single {
+ width: 600px;
+ margin-bottom: 0.5em;
+}
+
+.v_instruction {
+ padding: 0.2em 0;
+}
+
+.v_trimmer {
+ overflow: hidden;
+}
+
+.v_select {
+ width: 100%;
+ background-color: #222;
+ font-size: 1em;
+ color: white;
+
+ background-image: none;
+ border: none;
+ box-shadow: none;
+ -webkit-appearance: none;
+}
+
+div[data-type="token"] {
+ background-color: #111;
+ color: #EEE;
+
+ padding: 0.25em 0.5em;
+ margin-right: 100%;
+
+ min-width: 8em;
+
+ display: inline-block;
+
+ position: relative;
+}
+
+div[data-type="token"]:hover {
+ background-color: #444;
+}
+
+div[data-type="token"]:after {
+ content: "Loading";
+ position: absolute;
+
+ color: rgba( 255, 255, 255, 0.2 );
+
+ right: 0;
+ bottom: 0;
+}
+
+div[data-type="token"]:hover:after {
+ color: rgba( 255, 255, 255, 0.5 );
+}
diff --git a/botanjs/src/Astro/Blog/AstroEdit/Visualizer/_this.js b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/_this.js
new file mode 100644
index 0000000..b4fcced
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/_this.js
@@ -0,0 +1,496 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.AstroEdit" );
+
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {System.Debug} */
+ var debug = __import( "System.Debug" );
+ /** @type {System.utils} */
+ var utils = __import( "System.utils" );
+ /** @type {System.utils.Perf} */
+ var Perf = __import( "System.utils.Perf" );
+ /** @type {System.utils.DataKey} */
+ var DataKey = __import( "System.utils.DataKey" );
+ /** @type {System.utils.EventKey} */
+ var EventKey = __import( "System.utils.EventKey" );
+ /** @type {System.utils.IKey} */
+ var IKey = __import( "System.utils.IKey" );
+ /** @type {System.Net.ClassLoader} */
+ var Loader = __import( "System.Net.ClassLoader" );
+ /** @type {Components.MessageBox} */
+ var MessageBox = __import( "Components.MessageBox" );
+ /** @type {Components.Mouse.ContextMenu} */
+ var ContextMenu = __import( "Components.Mouse.ContextMenu" );
+ /** @type {Astro.Blog.Config} */
+ var Config = __import( "Astro.Blog.Config" );
+
+ var snippetList = IKey.quickDef(
+ "Code" , "background: white; color: cornflowerblue;"
+ , "Image" , "background: #ff0084;"
+ , "Sound" , "background: YellowGreen;"
+ , "Video" , "background: Crimson;"
+ , "Spoiler" , "background: cornflowerblue;"
+ , "Swf" , "background: #333;"
+ , "Link" , "background: blue;"
+ , "AcquireLib" , "background: black;"
+ , "Html" , "background: coral;"
+ , "SiteFile" , "background: royalblue;"
+ );
+
+ var snippetNs = "Astro.Blog.AstroEdit.Visualizer.Snippet.";
+ //// Document Visualizer visualize snippets, used in AstroEdit and comments
+ var Visualizer = function ( e_document, snippetControls, service_uri )
+ {
+ var Article = ns[ NS_INVOKE ]( "Article" );
+ var loadedModule = {};
+
+ //// Constants
+ var article;
+ // "[^]" does not work in IE
+ var stackMatch = /\[([a-z][0-9a-z]*?)([\s\S]*?)\]([\s\S]*?)\[\/\1\]/ig;
+ var typeMatch = /([a-z][0-9a-z]*)\=\"([^"]+)\"/ig;
+
+ var snippetTokenQueue = {};
+
+ if ( e_document instanceof Article )
+ {
+ article = e_document;
+ // Allow Html snippet
+ }
+ else if ( e_document instanceof CeDocument )
+ {
+ article = e_document;
+ }
+ else return;
+
+ ////// Variables
+ var contentDiv = Dand.wrap()
+ var selRange;
+ var snippetExists = false;
+ var lastOffset = 0;
+ var raw;
+
+ ///// private methods
+ var snippetWrap = function( snippetType, element, editable, _with )
+ {
+ var snippet = Dand.wrap(
+ _with || "div"
+ , Perf.uuid
+ , "comp v_boundary"
+ , element
+ , [
+ new IKey( "contentEditable", editable ? "true" : "false" )
+ , new DataKey( "type", snippetType )
+ ]
+ );
+
+ return snippet;
+ };
+
+ var appendLinebreak = function ()
+ {
+ this.parentNode.insertBefore(Dand.wrap("br"), this.nextSibling);
+ }
+
+ var insertLinebreak = function ()
+ {
+ this.parentNode.insertBefore(Dand.wrap("br"), this);
+ };
+
+ var snippetDelete = function ()
+ {
+ this.parentNode.removeChild(this);
+ };
+
+ var snippetBBCode = function()
+ {
+ var snippet = IDOMElement( this );
+ var type = snippet.getDAttribute( "type" ).toLowerCase();
+ /** @type {Astro.Blog.AstroEdit.Visualizer.Snippet.Model} */
+ var module = loadedModule[ type ];
+ var title = "An error occurred";
+ var message = "No such module";
+
+ if( module )
+ {
+ title = "BBCode for Snippet: " + type;
+ message = module.compile( this.firstChild );
+ }
+
+ new MessageBox( title, Dand.wrapne( "pre", message ) ).show();
+ };
+
+ var createSnippetMenu = function ( snippetProp, snippet, editHandler, snippetActions )
+ {
+ var contextItems = [
+ editHandler && new EventKey( "Edit", editHandler.bind( snippetProp ) )
+ , new EventKey( "Insert a linebreak", insertLinebreak.bind( snippet ) )
+ , new EventKey( "Append a linebreak", appendLinebreak.bind( snippet ) )
+ , new EventKey( "Delete", snippetDelete.bind( snippet ) )
+ , new EventKey( "View BBCode", snippetBBCode.bind( snippet ) )
+ ];
+
+ e_document.updateContent();
+
+ if ( snippetActions instanceof Array )
+ {
+ contextItems = snippetActions.concat( contextItems );
+ }
+
+ return new ContextMenu( snippet, contextItems, "RMB" );
+ };
+
+ var _savSelection = function ()
+ {
+ contentDiv.focus();
+ if ( window.getSelection )
+ {
+ var sel = window.getSelection();
+ if ( sel.getRangeAt && sel.rangeCount )
+ {
+ selRange = sel.getRangeAt(0);
+ }
+ }
+ else if ( document.selection && document.selection.createRange )
+ {
+ selRange = document.selection.createRange();
+ }
+ else
+ {
+ selRange = null;
+ }
+ };
+
+ var _resSelection = function ()
+ {
+ if ( selRange )
+ {
+ if ( window.getSelection )
+ {
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+ sel.addRange( selRange );
+ }
+ else if ( document.selection && selRange.select )
+ {
+ selRange.select();
+ }
+ }
+ };
+
+ var lastLine = Dand.wrap( "br", "v_linebreak" );
+
+ var ensureLastLinebreak = function ()
+ {
+ if( !Dand.id( "v_linebreak" ) )
+ {
+ insertSnippet( lastLine, true );
+ }
+ else
+ {
+ insertSnippet( contentDiv.removeChild( lastLine ), true );
+ }
+ };
+
+ var insertAtCaret = function( element )
+ {
+ var sel, range;
+
+ sel = window.getSelection();
+ if ( sel.getRangeAt && sel.rangeCount )
+ {
+ range = sel.getRangeAt(0);
+ range.deleteContents();
+ range.insertNode( element );
+ range.setStartAfter( element );
+ range.setEndAfter( element );
+ range.collapse( false );
+ sel.removeAllRanges();
+ sel.addRange( range );
+ }
+
+ e_document.updateContent();
+ ensureLastLinebreak();
+ };
+
+ var insertSnippet = function( element, override )
+ {
+ if ( override )
+ {
+ contentDiv.appendChild( element );
+ return;
+ }
+
+ contentDiv.focus();
+ _resSelection();
+ insertAtCaret( element );
+ };
+
+ var replaceToken = function( element )
+ {
+ contentDiv.insertBefore( element, this.token );
+ contentDiv.removeChild( this.token );
+ };
+
+ var parseKeys = function ( match, name, value )
+ {
+ this[ name ] = value;
+ };
+
+ var snippetQueue = function( type, alias )
+ {
+ var q = snippetTokenQueue[ type ] || [];
+
+ if( alias )
+ {
+ if( !( alias instanceof Array ) ) alias = [ alias ];
+
+ for( var i in alias )
+ {
+ q = snippetTokenQueue[ alias[i] ] || [];
+ if( q.length ) return q;
+ }
+ }
+
+ return ( snippetTokenQueue[ type ] = q );
+ };
+
+ var parseSnippet = function( match, type, properties, _value, offset )
+ {
+ var temp, i, j;
+ // Texts goes first
+ if ( lastOffset != offset )
+ {
+ // innerText does not work in firefox:(
+ temp = Dand.wrape( raw.substr( lastOffset, offset - lastOffset ) );
+ // innerHTML will escape html entities, now replace linebreaks to br
+ temp.innerHTML = temp.innerHTML.replace(/[\r\n]/g, "<br>");
+ IDOMElement( contentDiv ).lootChildren( temp );
+ }
+
+ lastOffset = offset + match.length;
+
+ // Snippet Args
+ temp = { value: _value };
+ // to ekeys
+ properties.replace( typeMatch, parseKeys.bind( temp ) );
+
+ // Now deal with snippet
+ /** @type {Astro.Blog.AstroEdit.Visualizer.Snippet.Model} */
+ var _module = loadedModule[ type ];
+ if ( _module )
+ {
+ // Visualize snippet
+ new ( _module )( insertSnippet, snippetWrap, createSnippetMenu, temp );
+ }
+ else
+ {
+ // no such module, or module is not loaded yet
+ debug.Info( "[Visualizer] Queueing: " + type );
+ var token = snippetWrap( "token", type, false );
+ token.setAttribute( "class", token.getAttribute( "class" ) + " flsf" );
+ snippetQueue( type ).push([ token, temp ]);
+
+ insertSnippet( token, true );
+ }
+ };
+
+ var linebreakNodes = ["P", "DIV"];
+ var linebreakNode = function( name )
+ {
+ if( -1 < linebreakNodes.indexOf( name.toUpperCase() ) ) return "\n";
+ return "";
+ };
+
+ var ensureGoodness = function ()
+ {
+ _savSelection();
+ ensureLastLinebreak();
+ };
+
+ var stepContent = function ( element, level )
+ {
+ var j , snippetType
+ , elements = element.childNodes
+ , output = "";
+
+ for (var i = 0, l = elements.length; i < l; i ++)
+ {
+ j = elements[i];
+
+ // do nothing on lastline
+ if (j == lastLine) continue;
+
+ if (j.nodeType == 1)
+ {
+ snippetType = IDOMElement( j ).getDAttribute( "type" );
+ if( snippetType && loadedModule[ snippetType = snippetType.toLowerCase() ] )
+ {
+ output += loadedModule[ snippetType ].compile( j.firstChild );
+ }
+ else if ( j.firstChild )
+ {
+ // Text
+ output += linebreakNode( j.nodeName ) + stepContent(j, ++level);
+ }
+ else
+ {
+ // Handle special <div><br></div> => one line break
+ if(l == 1 && j.nodeName.toUpperCase() == "BR") continue;
+
+ // Line breaks
+ output += "\n";
+ }
+ }
+ // Text
+ else if (j.nodeType == 3)
+ {
+ output += j.nodeValue;
+ }
+ }
+
+ return output;
+ };
+
+ ///// Initializations
+ var snippetReady = function ( e )
+ {
+ // Initialize modules
+ var mod_name = e.substr( e.lastIndexOf(".") + 1, e.length ).toLowerCase()
+ /** @type {Astro.Blog.AstroEdit.Visualizer.Snippet.Model} */
+ var _module = __import( e );
+ var alias = _module.alias;
+
+ // Implement module if valid and available
+ if ( _module && _module.compile )
+ {
+ loadedModule[ mod_name ] = _module;
+ debug.Info( "[Visualizer] Module loaded: " + mod_name );
+ // Set alias
+ if ( alias )
+ {
+ alias = ( alias instanceof Array ) ? alias : [ alias ];
+
+ for ( var k in alias )
+ {
+ loadedModule[ alias[k] ] = _module;
+ debug.Info( "[Visualizer] Alias: " + mod_name + " => " + alias );
+ }
+ }
+
+ // Append module"s control
+ temp = Dand.wrapne(
+ "span"
+ , mod_name
+ , new IKey(
+ "style"
+ , utils.objSearch(
+ snippetList
+ /** @param {System.utils.IKey} a */
+ , function( a ) { return a.keyName.toLowerCase() == mod_name }
+ , "keyValue"
+ )
+ )
+ );
+
+ snippetControls.appendChild( temp );
+ snippetControls.appendChild( Dand.textNode( "\t" ) );
+ temp.onclick = new ( _module )( insertSnippet, snippetWrap, createSnippetMenu );
+
+ var queue = snippetQueue( mod_name, _module.alias );
+ for( var i in queue )
+ {
+ var token = queue[i];
+ new ( _module )( replaceToken.bind({ token: token[0] }), snippetWrap, createSnippetMenu, token[1] );
+ delete queue[i];
+ }
+ }
+ else
+ {
+ if ( _module )
+ {
+ delete loadedModule[ mod_name ];
+ debug.Info( "[Visualizer] Module discarded: invalid module \"" + mod_name + "\"" );
+ }
+ else
+ {
+ debug.Info( "[Visualizer] Module \"" + mod_name + "\" does not exists" );
+ }
+ }
+ };
+
+ this.setContentDiv = function( element )
+ {
+ contentDiv = element;
+ // listeners that return values cannot use addEventListener
+ contentDiv.onkeypress = function(e)
+ {
+ if (e.which == 13)
+ {
+ insertAtCaret( Dand.wrap( "br" ) );
+ _savSelection();
+ return false;
+ }
+ return true;
+ };
+
+ temp = IDOMElement( contentDiv );
+ temp.addEventListener( "Input", ensureGoodness );
+ temp.addEventListener( "KeyUp", ensureGoodness );
+ temp.addEventListener( "Click", ensureGoodness );
+
+ contentDiv.focus();
+ ensureGoodness();
+
+ try {
+ document.execCommand( "insertBrOnReturn", false, "true" );
+ } catch(e) {
+ debug.Info( "Not firefox" );
+ }
+ };
+
+ this.saveRaw = function ()
+ {
+ return stepContent( contentDiv, 0 );
+ };
+
+ this.visualizeData = function ( data )
+ {
+ if ( !data ) return;
+
+ lastOffset = 0;
+ // clear content
+ contentDiv.innerHTML = "";
+
+ ( raw = data ).replace( stackMatch, parseSnippet );
+ if ( lastOffset != raw.length )
+ {
+ // innerText does not work in firefox:(
+ temp = Dand.wrape( raw.substr( lastOffset, raw.length - lastOffset ) );
+ // innerHTML will escape html entities, now replace linebreaks to br
+ temp.innerHTML = temp.innerHTML.replace( /[\r\n]/g, "<br>" );
+ IDOMElement( contentDiv ).lootChildren( temp );
+ }
+ temp = null;
+ raw = null;
+ };
+
+ this.saveSelection = _savSelection;
+ this.restoreSelection = _resSelection;
+
+ article.invoke(this);
+
+ var modules = [];
+ for( var i in snippetList )
+ {
+ modules[ modules.length ] = snippetNs + snippetList[i].keyName;
+ }
+
+ var ldr = new Loader( service_uri, "r" );
+ ldr.load( modules, snippetReady );
+ };
+
+ ns[ NS_EXPORT ]( EX_CLASS, "Visualizer", Visualizer );
+})();
diff --git a/botanjs/src/Astro/Blog/AstroEdit/_this.css b/botanjs/src/Astro/Blog/AstroEdit/_this.css
new file mode 100644
index 0000000..e312862
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/_this.css
@@ -0,0 +1,244 @@
+.ae_panel_section {
+ padding: 0 1em;
+ color: white;
+}
+
+.ae_section_prop {
+ margin: 0.5em 0;
+ text-align: right;
+}
+
+.ae_prop_value {
+ font-size: 1.2em;
+ text-align: right;
+}
+
+/* Interactive value */
+.ae_iValue {
+ text-align: center;
+ padding: 0.5em;
+ cursor: default;
+}
+
+.ae_background {
+ background-color: #fff;
+ background-image:
+ linear-gradient(90deg, transparent 1em, #666 1em, #666 1.2em, transparent 1.2em),
+ linear-gradient(#EEE .1em, transparent .1em);
+ background-size: 100% 1.5em;
+}
+
+
+/* Buttons */
+.ae_purple { background: purple; }
+.ae_purple:hover { background: darkmagenta; }
+.ae_coral { background: orangered; }
+.ae_coral:hover { background: darkorange; }
+.ae_dodgerblue { background: dodgerblue; }
+.ae_caution { color: red; }
+.ae_caution:hover { background: #333; }
+.ae_dodgerblue:hover { background: cornflowerblue; }
+/* End buttons */
+
+
+.ae_disabled {
+ color: grey;
+ background: none;
+}
+
+.ae_blockswitch {
+ line-height: 2em;
+}
+
+.ae_blockswitch > span {
+ white-space: nowrap;
+ /* text-transform:uppercase; */
+ font-family: custom-sans;
+ padding: 0.2em 0.5em;
+ cursor: default;
+ opacity: 0.5;
+}
+
+.ae_blockswitch > span:hover {
+ background-color: black;
+ opacity: 1;
+}
+
+.ae_body {
+ max-height: 100%;
+
+ overflow-y: scroll;
+
+ position: fixed;
+}
+
+.ae_content {
+ width: auto;
+ background: SkyBlue;
+}
+
+.ae_panel {
+ width: 300px;
+ height: auto;
+ min-height: 100%;
+ background: #222;
+ color: white;
+
+ position: absolute;
+
+ top: 0;
+ right: 0;
+
+ /* will handel mouse scroll later*/
+ overflow: hidden;
+
+ -moz-box-shadow: -2px 0 5px black;
+ -webkit-box-shadow: -2px 0 5px black;
+ box-shadow: -2px 0 5px black;
+}
+
+.ae_topbar {
+ width: 100%;
+ color: #222;
+ padding: 0.35em 0;
+ overflow: hidden;
+ border-bottom: 7px solid #222;
+}
+
+
+.ae_title, .ae_stitle {
+ padding: 0.35em;
+}
+
+.ae_title {
+ position: relative;
+ top: 0.25em;
+ font-size: 2em;
+}
+
+.ae_stitle {
+ font-size: 1.5em;
+}
+
+#ae_title {
+ font-size: 2.5em;
+ color: #444;
+}
+
+#ae_content {
+ font-size: 100%;
+}
+
+#ae_title, #ae_content {
+ background: none;
+ width: 100%;
+
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+#ae_title:focus {
+ background: rgba(255, 255, 255, 0);
+}
+
+#ae_content:focus {
+ background: rgba(255, 255, 255, 0);
+}
+
+#ae_bgdimmer {
+ background: rgba(255, 255, 255, 0);
+}
+
+#ae_content, #ae_bgdimmer {
+ color: #555;
+ padding: 1em 1em 2em 1em;
+}
+
+.ae_preview {
+ display: block;
+ color: white;
+ text-decoration: none;
+ padding: 0.4em;
+ font-size: 100%;
+ width: 100%;
+}
+
+.ae_user_info {
+ background: #111;
+ overflow: hidden;
+ position: relative;
+}
+
+.ae_draft_section {
+ padding: 0.5em;
+}
+
+.ae_draft_logo {
+ font-size: 3em;
+ position: absolute;
+ opacity: 0.2;
+ cursor: default;
+}
+
+.ae_name {
+ text-align: right;
+ padding: 0.2em;
+}
+
+.ae_user_content {
+ padding: 0.2em
+}
+
+#ae_user_drafts {
+ overflow: hidden;
+}
+
+.ae_dEntry {
+ cursor: default;
+ padding: 0.2em;
+ background: rgba(255, 255, 255, 0);
+}
+
+.ae_dEntry:hover {
+ background: rgba(255, 255, 255, 0.2);
+}
+
+.ae_dEntry_date {
+ text-align: right;
+}
+
+.ae_expand_btn, .ae_callapse_btn {
+ background: url(http://file.astropenguin.net/blog/layout-images/collapse.png) center 2.5px no-repeat;
+ width: 100%;
+ height: 10px;
+}
+
+.ae_expand_btn {
+ -webkit-transform: rotate(180deg);
+ -moz-transform: rotate(180deg);
+ transform: rotate(180deg);
+}
+
+.ae_callapse_btn {
+ -webkit-transform: rotate(0deg);
+ -moz-transform: rotate(0deg);
+ transform: rotate(0deg);
+}
+
+.ae_affected_count {
+ color: red;
+}
+
+/*Active draft notation*/
+.ae_dActive_bubble {
+ float: right;
+ color: yellowgreen;
+ text-shadow: 0 0 5px yellowgreen;
+ font-size: 80%;
+}
+
+body {
+ margin: 0 auto;
+ min-width: 1200px;
+}
diff --git a/botanjs/src/Astro/Blog/AstroEdit/_this.js b/botanjs/src/Astro/Blog/AstroEdit/_this.js
new file mode 100644
index 0000000..9e173e9
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/_this.js
@@ -0,0 +1,99 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.AstroEdit" );
+
+ /** @type {System.Cycle} */
+ var Cycle = __import( "System.Cycle" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Astro.Bootstrap} */
+ var Bootstrap = __import( "Astro.Bootstrap" );
+ /** @type {Astro.Blog.Config} */
+ var Config = __import( "Astro.Blog.Config" );
+ /** @type {Astro.Blog.AstroEdit.Article} */
+ var Article = __import( "Astro.Blog.AstroEdit.Article" );
+ /** @type {Astro.Blog.AstroEdit.Draft} */
+ var Draft = __import( "Astro.Blog.AstroEdit.Draft" );
+ /** @type {Astro.Blog.AstroEdit.Tag} */
+ var Tag = __import( "Astro.Blog.AstroEdit.Tag" );
+ /** @type {Astro.Blog.AstroEdit.Visualizer} */
+ var Visualizer = __import( "Astro.Blog.AstroEdit.Visualizer" );
+ /** @type {Astro.Blog.AstroEdit.Uploader} */
+ var Uploader = __import( "Astro.Blog.AstroEdit.Uploader" );
+ /** @type {Astro.Blog.AstroEdit.SiteLibrary} */
+ var SiteLibrary = __import( "Astro.Blog.AstroEdit.SiteLibrary" );
+
+ var wh, ww, cw, html, article;
+
+ var init = function ()
+ {
+ //// Component initializations
+ /** @type {_AstConf_.AstroEdit} */
+ var a_conf = Config.get( "AstroEdit" );
+ var base_path = Config.get( "BasePath" );
+
+ Cycle.next(
+ function()
+ {
+ article.load(
+ a_conf.article_id
+ , function( obj, id )
+ {
+ window.history.replaceState( obj, "", base_path + "astroedit/" + id + "/" );
+ }
+ );
+ }
+ );
+
+ article = new Article( a_conf.paths.set_article );
+
+ // Article modules
+ new Draft( article, a_conf.paths.get_drafts );
+ new Tag( article, a_conf.paths.tag_count );
+ new Visualizer(
+ article
+ , Dand.id( "ae_visual_snippets" )
+ , Config.get( "ServiceUri" )
+ );
+
+ // Independent modules
+ new Uploader( a_conf.paths.set_file );
+ new SiteLibrary(
+ Config.get( "BasePath" )
+ , a_conf.paths.get_files
+ , a_conf.paths.set_file
+ );
+
+ var title = Dand.id("ae_title");
+ var content = Dand.id("ae_content");
+
+ // content height
+ content.style.minHeight = String(screen.availHeight) + "px";
+
+ html = document.body.parentNode;
+
+ window.onresize = adjustLayout;
+
+ // Setting event handlers
+ title.onblur = content.onblur = function () { Dand.id("ae_bgdimmer").style.background = ""; };
+ content.onfocus = content.onfocus = function ()
+ {
+ Dand.id("ae_bgdimmer").style.background = "rgba(255, 255, 255, 0.2)";
+ };
+
+ Dand.id( "ae_publish_btn" ).onclick = function () { article.saveAndPublish(); };
+ Dand.id( "ae_preview_btn" ).onclick = function () { article.preview(); };
+ Dand.id( "ae_drop_btn" ).onclick = function () { article.drop(); };;
+
+ adjustLayout();
+ };
+
+ var adjustLayout = function ()
+ {
+ ww = html.clientWidth;
+ wh = html.clientHeight;
+ cw = ww - 300;
+ Dand.id("ae_body").style.width = String(cw) + "px";
+ };
+
+ Bootstrap.regInit( init );
+})();
diff --git a/botanjs/src/Astro/Blog/Components/Bubble.css b/botanjs/src/Astro/Blog/Components/Bubble.css
new file mode 100644
index 0000000..331d0e7
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Bubble.css
@@ -0,0 +1,35 @@
+.bubble {
+ position: fixed;
+ background: white;
+ border-radius: 300px;
+
+ -moz-box-shadow: 7px 7px 10px;
+ -webkit-box-shadow: 7px 7px 10px;
+ box-shadow: 7px 7px 10px;
+
+ z-index: 98;
+}
+
+.innerBubble {
+ border-radius: 300px;
+ position: relative;
+ overflow: hidden;
+}
+
+.innerBubble:before {
+ content: '';
+ display: inline-block;
+ height: 100%;
+ vertical-align: middle;
+}
+
+.bInner{
+ display: inline-block;
+ width: 100%;
+
+ vertical-align: middle;
+ text-align: center;
+
+ font-size: 1.35em;
+ color: white;
+}
diff --git a/botanjs/src/Astro/Blog/Components/Bubble.js b/botanjs/src/Astro/Blog/Components/Bubble.js
new file mode 100644
index 0000000..342b1da
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Bubble.js
@@ -0,0 +1,133 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.Components" );
+
+ /** @type {System.Cycle} */
+ var Cycle = __import( "System.Cycle" );
+ /** @type {System.utils.EventKey} */
+ var EventKey = __import( "System.utils.EventKey" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+
+ var Bubble = function () {
+ this.stage = null;
+ this.bubble = null;
+
+ //// default is somewhat purple
+ this.bgColor = "#662D91";
+
+ this.blowInner = function ()
+ {
+ var b_style = this.bubble.style
+ , s_style = this.stage.style;
+
+ b_style.margin = "10px 10px";
+ b_style.width = "150px";
+ b_style.height = "150px";
+
+ s_style.width = "170px";
+ s_style.height = "170px";
+ s_style.bottom = "20px";
+ s_style.right = "20px";
+ };
+
+ this.blowOuter = function ()
+ {
+ var b_style = this.bubble.style
+ , s_style = this.stage.style;
+
+ b_style.margin = "100px 100px";
+
+ s_style.width = "200px";
+ s_style.height = "200px";
+ s_style.bottom = "10px";
+ s_style.right = "10px";
+
+ Cycle.delay( this.blowInner.bind(this), 250 );
+ };
+
+ this.Void = function ()
+ {
+ this.stage.style.display = "none";
+ };
+
+ // Set Bindings
+ this.init = this.init.bind(this);
+ this.blurp = this.blurp.bind(this);
+ this.pop = this.pop.bind(this);
+ this.setColor = this.setColor.bind(this);
+ };
+
+ var wrapc = function( aClass, elem )
+ {
+ return Dand.wrapc( "comp " + aClass, elem );
+ };
+
+ Bubble.prototype.init = function ()
+ {
+ this.stage = wrapc( "bubble" );
+ this.bubble = wrapc( "innerBubble" );
+
+ var b_style = this.bubble.style
+ , s_style = this.stage.style;
+
+
+ b_style.margin = "85px 85px";
+ b_style.width = "0px";
+ b_style.height = "0px";
+ b_style.background = this.bgColor;
+ b_style.overflow = "hidden";
+
+ this.bubble.appendChild(
+ this.message = wrapc( "bInner flsf" )
+ );
+
+ this.stage.appendChild(this.bubble);
+
+ s_style.display = "none";
+ s_style.width = "10px";
+ s_style.height = "10px";
+ s_style.bottom = "85px";
+ s_style.right = "85px";
+
+
+ // onClick handle to prevent persistent bubble
+ IDOMElement( this.stage ).addEventListener( new EventKey( "Click", this.blurp ) );
+ return this.stage;
+ };
+
+ Bubble.prototype.setColor = function (color)
+ {
+ this.bubble.style.background = color;
+ };
+
+ Bubble.prototype.blurp = function ()
+ {
+ var b_style = this.bubble.style
+ , s_style = this.stage.style;
+
+ b_style.margin = "0 0";
+ b_style.width = "0px";
+ b_style.height = "0px";
+ b_style.overflow = "hidden";
+
+
+ s_style.position = "fixed";
+ s_style.width = "10px";
+ s_style.height = "10px";
+ s_style.bottom = "85px";
+ s_style.right = "85px";
+
+ Cycle.delay( this.Void.bind(this), 250 );
+ };
+
+ Bubble.prototype.pop = function (message)
+ {
+ this.message.innerHTML = message;
+ this.stage.style.display = "block";
+ Cycle.delay( this.blowOuter.bind(this), 100 );
+ };
+
+ ns[ NS_EXPORT ]( EX_CLASS, "Bubble", Bubble );
+})();
diff --git a/botanjs/src/Astro/Blog/Components/Calendar.css b/botanjs/src/Astro/Blog/Components/Calendar.css
new file mode 100644
index 0000000..69e345d
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Calendar.css
@@ -0,0 +1,67 @@
+.calendar, #timeline {
+ cursor: default;
+}
+
+.calendar{
+ position: absolute;
+ display: none;
+ background: white;
+ margin: 20px auto;
+ font-size: 17px;
+ padding: 3px;
+ width: 241px;
+ box-shadow: 3px 3px 4px black;
+ border: 10px solid dodgerblue;
+ border-collapse: collapse;
+ font-family: Georgia;
+}
+
+/*Inline_Blocks*/
+.calDate, #calDays > div > span {
+ display:-moz-inline-block;
+ display:-moz-inline-box;
+ display:inline-block;
+}
+/*End_Inline_Blocks*/
+
+#calDays > div > span {
+ background: white;
+ border: 0px solid;
+ margin: 1.71428%;
+ padding: 3px 0px 3px 0px;
+ #:100/7 - (0.24% margin);
+ width: 10.857%;
+ text-align: center;
+ font-size: 0.8em;
+}
+#calDays>div {
+ height: 27px;
+ white-space: nowrap;
+}
+.calDate {
+ width: 155px;
+ text-align: center;
+}
+.navbtn {
+ opacity: .5;
+ width: 17px;
+ height: 20px;
+}
+.navbtn:hover {
+ opacity: 1;
+}
+.onhover {
+ border: 3px solid !important;
+ margin-left: -2px !important;
+ margin-top: -4px !important;
+ vertical-align: top;
+ padding: 0px 1px 2px 0px;
+ box-shadow: 3px 3px 3px black;
+}
+.toNorm {
+}
+
+#norec{color: #FF7BAC;}
+#notfin{color: #3FA9F5;}
+#notyet{color: #BDCCD4;}
+#today{color: #7AC943;}
diff --git a/botanjs/src/Astro/Blog/Components/Calendar.js b/botanjs/src/Astro/Blog/Components/Calendar.js
new file mode 100644
index 0000000..cec1de2
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Calendar.js
@@ -0,0 +1,244 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.Components" );
+
+ /** @type {System.Cycle} */
+ var Cycle = __import( "System.Cycle" );
+ /** @type {System.Debug} */
+ var debug = __import( "System.Debug" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Astro.utils.Date} */
+ var XDate = __import( "Astro.utils.Date" );
+
+ var Calendar = function ()
+ {
+ this.stage = null;
+ this.calDate = null;
+ this.show = false;
+ this.caption = null;
+ this.calDays = false;
+ this.currentHandler = function( v ) {
+ debug.Info( "[Astro.Blog.Components.Calendar] " + v );
+ };
+ };
+
+ var bodyClick = function ( e )
+ {
+ var s = e.target.getAttribute("calObj")
+ if(s)
+ {
+ this.stage.style.display = "none";
+ this.stage.style.position = "";
+ this.show = false;
+ }
+
+ if( this.show && !this.istg.contains( e.target ) )
+ {
+ this.stage.style.display = "none";
+ this.stage.style.position = "";
+ this.show = false;
+ }
+ };
+
+ var addZero = function ( num )
+ {
+ return num < 10
+ ? "0" + String(num)
+ : String(num)
+ ;
+ };
+
+ Calendar.prototype.init = function()
+ {
+ this.stage = Dand.wrapc( "astx calendar" );
+ this.istg = IDOMElement( this.stage );
+ this.calDate = new Date;
+
+ IDOMElement( document.body ).addEventListener( "Click", bodyClick.bind( this ) );
+
+ var tr = Dand.wrap(null, "navbar")
+ , td = Dand.wrap("span")
+ , btn = Dand.wrap("a", null, "navbtn")
+ ;
+ tr.setAttribute("style", "position: relative");
+
+ btn.onclick = function (e) { this.nextMon(); }.bind(this);
+
+ td.style.textAlign = "right";
+ td.style.width = "100%";
+ td.style.position = "absolute";
+ td.style.left = "0";
+
+ btn.appendChild(Dand.textNode("\u25ba"));
+
+ td.appendChild(btn);
+ tr.appendChild(td);
+
+ td = Dand.wrap("span");
+ btn = Dand.wrap("a", null, "navbtn");
+
+ btn.appendChild(Dand.textNode("\u25c4"));
+
+ btn.onclick = function (e) { this.prevMon(); }.bind(this);
+
+ td.style["float"] = "left";
+ td.style.position = "absolute";
+
+ td.appendChild(btn);
+ tr.appendChild(td);
+
+ caption = Dand.wrap("span", null, "calDate");
+ caption.style.width = "100%";
+ caption.appendChild(
+ Dand.textNode(
+ XDate.MONTH[this.calDate.getMonth()] + ", " + this.calDate.getFullYear()
+ )
+ );
+ tr.appendChild(caption);
+
+ this.stage.appendChild(tr);
+
+ this.stage.onselectstart = function() { return false;};
+
+ return this.stage;
+ };
+
+
+ Calendar.prototype.pop = function ( dateObj, handler )
+ {
+ if( typeof( dateObj ) == "string" )
+ {
+ var p = dateObj.split(",");
+ if ( p.length == 3 )
+ dateObj = new Date( p[2], p[1] - 1, p[0] );
+ }
+
+ this.draw( dateObj, handler );
+
+ this.show = false;
+
+ this.stage.style.display = "block";
+ Cycle.next( function (){ this.show = true; }.bind(this) );
+ };
+
+ Calendar.prototype.draw = function (dateObj, handler)
+ {
+ if(!dateObj)
+ dateObj = new Date;
+
+ this.calDate = dateObj;
+
+ var mon = dateObj.getMonth()
+ , yr = dateObj.getFullYear()
+ , j = dateObj.getDay()
+ , dt = dateObj.getDate()
+ , thisDate = new Date
+ , thisYear = thisDate.getFullYear()
+ , thisMonth = thisDate.getMonth()
+ , thisDate = thisDate.getDate()
+ , thisDate = Number(String(thisYear) + addZero(thisMonth) + addZero(thisDate))
+ , currHead = String(yr) + addZero(mon)
+ , currDate;
+
+ ( typeof handler == "function" ) && ( this.currentHandler = handler );
+
+ caption.innerHTML = XDate.MONTH[this.calDate.getMonth()] + ", " + this.calDate.getFullYear();
+
+ if(this.calDays)
+ this.calDays.parentElement.removeChild(this.calDays);
+ this.calDays = Dand.wrap(null, "calDays");
+
+ var days = (mon == 1) ? (yr % 4 == 0 ? 29: 28) : XDate.CAP_MONTHS[mon] ? 30 : 31;
+ for (var i = 0; i < days; i ++)
+ {
+ if(j < 0)
+ j = 6;
+ if(dt - i == 1)
+ break;
+ j --;
+ }
+
+ var tr = Dand.wrap(), td;
+ for(i = 0; i < 7; i ++)
+ {
+ td = Dand.wrap("span");
+ td.appendChild( Dand.textNode( XDate.DAY_ABBR[i] ) );
+ tr.appendChild(td);
+ }
+ this.calDays.appendChild(tr);
+
+ tr = Dand.wrap();
+ for(i = 0; i < j; i ++)
+ {
+ td = Dand.wrap("span");
+ td.style.background = "none";
+ tr.appendChild(td);
+ }
+
+ i = 0;
+ while( i ++ < days )
+ {
+ currDate = Number(currHead + addZero(i));
+ if(j > 6)
+ {
+ j = 0;
+ this.calDays.appendChild(tr);
+ tr = Dand.wrap();
+ }
+
+ td = Dand.wrap( "span" );
+ td.setAttribute( "value", i + "," + (mon + 1) + "," + yr );
+ td.setAttribute( "onmouseover", "className=\"onhover\";" );
+ td.setAttribute( "onmouseout", "className=\"toNorm\";" );
+ // Unique identifier for body click
+ // to test whether choice is chosen
+ td.setAttribute("calObj", "1");
+
+ td.onclick = function ( e )
+ {
+ this.currentHandler( e.target.getAttribute("value") );
+ }.bind(this);
+
+ if(thisDate < currDate)
+ {
+ td.setAttribute("id", "notyet");
+ }
+ else if(thisDate > currDate)
+ {
+ td.setAttribute("id", "norec");
+ /*
+ if(!( book[yr] && book[yr][mon + 1] && book[yr][mon + 1][i] )) {
+ td.setAttribute("id", "norec");
+ } else if(book[yr][mon + 1][i][1]) {
+ td.setAttribute("id", "notfin");
+ }
+ */
+ }
+ else if(currDate == thisDate)
+ {
+ td.setAttribute("id", "today");
+ currlabel = td;
+ }
+ td.appendChild(Dand.textNode(i));
+ tr.appendChild(td);
+ j ++;
+ }
+
+ if(j) this.calDays.appendChild(tr);
+
+ this.stage.appendChild(this.calDays);
+ };
+
+ Calendar.prototype.prevMon = function () {
+ this.draw( this.calDate = new Date( this.calDate.getFullYear(), this.calDate.getMonth() - 1 ) );
+ };
+
+ Calendar.prototype.nextMon = function () {
+ this.draw( this.calDate = new Date( this.calDate.getFullYear(), this.calDate.getMonth() + 1 ) );
+ };
+
+
+ ns[ NS_EXPORT ]( EX_CLASS, "Calendar", Calendar );
+})();
diff --git a/botanjs/src/Astro/Blog/Components/Comment.css b/botanjs/src/Astro/Blog/Components/Comment.css
new file mode 100644
index 0000000..a23955b
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Comment.css
@@ -0,0 +1,288 @@
+.comment-stage {
+ width: 80%;
+ float: right;
+ padding-bottom: 1em;
+ position: relative;
+}
+
+.c_body {
+ position: relative;
+ padding-left: 10px;
+}
+
+.c_title {
+ padding: 1em 0.5em;
+}
+
+.c_bsign {
+ margin-left: -0.47em;
+ padding: 0.1em 0.5em 0.1em 1em;
+ font-size: 2em;
+ color: white;
+ background: cornflowerblue;
+}
+
+.uc_ntoggle {
+ font-family: custom-sans;
+ vertical-align: top;
+ padding: 0.2em 0em;
+}
+.uc_ntoggle:before { content: '..'; }
+.uc_ntoggle:after { content: '!'; }
+
+.cr_ntoggle {
+ font-family: custom-sans;
+
+ padding: 0;
+ margin: 0;
+
+ position: absolute;
+
+ width: 1.5em;
+ height: 1.5em;
+
+ bottom: 0;
+ right: -3em;
+
+ border-radius: 2em 2em 2em 0em;
+ border: 0.5em solid white;
+
+ line-height: 1.5em !important;
+
+ background-color: white;
+ /* box-shadow: 2px 2px 10px -5px black; */
+}
+
+.cr_ntoggle:before, .cr_ntoggle:after {
+ padding: 0;
+ border-radius: 2em;
+}
+
+.cr_ntoggle:before {
+ content: '..';
+ background-color: slategrey;
+}
+
+.cr_ntoggle:after { content: '!'; }
+
+.c_comm {
+ position: relative;
+ left: 1.6em;
+ padding-right: 1.6em;
+}
+
+.c_comm > .c_cont:after {
+ content: "";
+ width: 0;
+ height: 0;
+ border-top: 1em solid transparent;
+ border-bottom: 1em solid transparent;
+ border-right: 1em solid white;
+
+ position: absolute;
+ top: 1em;
+ left: -0.9em;
+}
+
+.c_comm > .c_cont:before {
+ content: "";
+ top: 0;
+ left: -2.15em;
+ position: absolute;
+ background: cornflowerblue;
+ width: 8px;
+ height: 100%;
+}
+
+
+.c_cont {
+ position: relative;
+ background-color: white;
+ padding: 1em;
+ margin-top: 1em;
+ border: 1px solid #D7D4C9
+}
+
+.c_text {
+ padding-bottom: 0.5em;
+
+ word-wrap: break-word;
+}
+
+.c_info > div:first-child, .c_info > div > a {
+ text-align: right;
+ color: #D4145A;
+}
+
+.c_info > div:last-child {
+ text-align: right;
+ color: #AAA;
+}
+
+.cu_stack {
+ /* padding 0.5 - 0.2 border width */
+ margin: 0 0 -0.3em 0.3em;
+ position: relative;
+ text-align: right;
+
+ width: 100%;
+}
+
+.cu_stack > div {
+ display: inline-block;
+}
+
+.cu_avatar {
+ /* Equal to the border width */
+ margin: 0 0 -0.2em 0.5em;
+}
+
+.cu_avatar > img {
+ border: 0.2em white solid;
+ box-shadow: 0px 0px 5px 0 black;
+ width: 64px;
+ height: 64px;
+ float: right;
+}
+
+.reply {
+ margin-left: 20%;
+ width: 80%;
+}
+
+.reply > .c_cont:after, .r_indicator {
+ content: "";
+ width: 0;
+ height: 0;
+ border-left: 1em solid transparent;
+ border-right: 1em solid transparent;
+ border-bottom: 1em solid white;
+
+ position: absolute;
+ top: -1em;
+ right: 1em;
+}
+
+.c_relative_wrap {
+ position: relative;
+ padding: 1em;
+
+ background: radial-gradient(circle, rgba(0, 0, 0, 0) 10px, #EEE 10px) 0px 10px, #FFF;
+ background-size: 20px 20px;
+}
+
+.commentblock {
+ margin-top: 1em;
+ width: 100%;
+ position: relative;
+ #background: white;
+}
+
+#c_submit {
+ background: cornflowerblue;
+}
+
+#c_submit:hover {
+ background: cadetblue;
+}
+
+.c_reply {
+ background: Coral;
+}
+
+.c_reply:hover {
+ background: LightCoral;
+}
+
+.c_tag {
+ cursor: pointer;
+ position: absolute;
+ overflow: hidden;
+ height: 100%;
+ width: 0;
+ right: 0;
+ top: 0;
+}
+
+
+.c_tag > span {
+ -moz-transform: rotate(90deg);
+ -moz-transform-origin: 50% 50%;
+ -webkit-transform: rotate(90deg);
+ -webkit-transform-origin: 50% 50%;
+ transform: rotate(90deg);
+ transform-origin: 50% 50%;
+ font-size: 1.5em;
+ display: block;
+ color: white;
+}
+
+.commentInput {
+ width: 100%;
+ height: 80px;
+ font-size: 1.2em;
+ font-family: custom-sans;
+}
+
+.c_caption {
+ font-size: 1.2em;
+ font-family: custom-sans;
+}
+
+.c_false_input {
+ font-size: 0.8em;
+ font-size: custom-serif;
+ font-style: italic;
+ color: red;
+ text-align: right;
+ height: 0;
+ overflow: hidden;
+}
+
+.nocomment {
+ font-size: 1.2em;
+ padding: 1em;
+ background: white;
+ font-family: custom-serif !important;
+ margin: 1em;
+}
+
+.info_field > div{
+ margin: 0.2em 0;
+}
+
+#capcha_wrapper {
+ overflow: hidden;
+ height: 0;
+
+}
+
+#recaptcha_field {
+ float: right;
+ background: white;
+}
+
+iframe[src="about:blank"] {
+ /* recaptcha about:blank page */
+ display: none;
+}
+
+.commentblock, #capcha_wrapper, #c_input_panel, .c_comm {
+ -webkit-transition: all 500ms cubic-bezier(0.770, 0.000, 0.175, 1.000);
+ -moz-transition: all 500ms cubic-bezier(0.770, 0.000, 0.175, 1.000);
+ -ms-transition: all 500ms cubic-bezier(0.770, 0.000, 0.175, 1.000);
+ -o-transition: all 500ms cubic-bezier(0.770, 0.000, 0.175, 1.000);
+ transition: all 500ms cubic-bezier(0.770, 0.000, 0.175, 1.000); /* easeInOutQuart */
+
+ -webkit-transition-timing-function: cubic-bezier(0.770, 0.000, 0.175, 1.000);
+ -moz-transition-timing-function: cubic-bezier(0.770, 0.000, 0.175, 1.000);
+ -ms-transition-timing-function: cubic-bezier(0.770, 0.000, 0.175, 1.000);
+ -o-transition-timing-function: cubic-bezier(0.770, 0.000, 0.175, 1.000);
+ transition-timing-function: cubic-bezier(0.770, 0.000, 0.175, 1.000); /* easeInOutQuart */
+}
+
+.c_so_sorry {
+ padding: 0.2em;
+ font-size: 0.8em;
+ color: coral;
+}
diff --git a/botanjs/src/Astro/Blog/Components/Comment.js b/botanjs/src/Astro/Blog/Components/Comment.js
new file mode 100644
index 0000000..a40e386
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Comment.js
@@ -0,0 +1,709 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.Components.Comment" );
+
+ /** @type {System.Cycle} */
+ var Cycle = __import( "System.Cycle" );
+ /** @type {System.Debug} */
+ var debug = __import( "System.Debug" );
+ /** @type {System.Cycle.Trigger} */
+ var Trigger = __import( "System.Cycle.Trigger" );
+ /** @type {System.utils.IKey} */
+ var IKey = __import( "System.utils.IKey" );
+ /** @type {System.utils.EventKey} */
+ var EventKey = __import( "System.utils.EventKey" );
+ /** @type {System.utils.DataKey} */
+ var DataKey = __import( "System.utils.DataKey" );
+ /** @type {System.utils.Perf} */
+ var Perf = __import( "System.utils.Perf" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Astro.Bootstrap} */
+ var Bootstrap = __import( "Astro.Bootstrap" );
+ /** @type {Astro.Blog.Config} */
+ var Conf = __import( "Astro.Blog.Config" );
+ /** @type {Astro.Blog.Components.Bubble} */
+ var Bubble = __import( "Astro.Blog.Components.Bubble" );
+
+ var postData = __import( "System.Net.postData" );
+
+ /** @type {_3rdParty_.Recaptcha} */
+ var Recaptcha;
+
+ /** @type {_AstConf_.UserInfo} */
+ var currentUser = Conf.get( "UserInfo" );
+ /** @type {_AstConf_.Comment} */
+ var Config = Conf.get( "Comment" );
+ /** @type {_AstConf_.Article} */
+ var article = Conf.get( "Article" );
+
+ /*{{{ Validation Methods */
+ var v_match = function( str, regEx )
+ {
+ // Optional string is empty
+ if( !str ) return true;
+ // Not empty, validate
+ if( str.match( regEx ) != null ) return str;
+
+ return false;
+ };
+
+ var validateMail = function ( str )
+ {
+ return v_match( str, /^[\_]*([a-z0-9]+(\.|\_*)?)+@([a-z][a-z0-9\-]+(\.|\-*\.))+[a-z]{2,6}$/ );
+ };
+
+ var validateURL = function ( str )
+ {
+ return v_match( str, /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/ );
+ };
+ /* End Validation Methods}}}*/
+
+ var wrapc = Dand.wrapc;
+
+ var init = function ()
+ {
+ // TODO: should add controls to be able to remove comments
+ var contentHaveText = false
+ , nameHaveText = false
+ , fieldReady = false
+ , releaseReplyLock
+ , captcha_wrapper
+ , submit_button
+ , cBubble
+ , button
+ , cHeight = 0
+ , elmH = "1.2em"
+
+ , cp = Dand.id( "c_input_panel" )
+ , contentField = Dand.id( "content_field" )
+ , nameField = Dand.id( "name_field" )
+ , c_body = Dand.id( "comment_body" )
+
+ // If nameField exists, we assume logged in
+ , loggedIn = !nameField
+
+
+ , contentHadHaveText = false
+ , commentInput = function ()
+ {
+ // trim whitespaces
+ contentHaveText = contentField.value.trim();
+ if( contentHaveText != contentHadHaveText ) {
+ fieldReadyTrigger();
+ contentHadHaveText = contentHaveText;
+ }
+ }
+
+ , nameHadHaveText = false
+ , nameInput = function ()
+ {
+ // trim whitespaces
+ nameHaveText = nameField.value.trim();
+ if( nameHadHaveText != nameHaveText ) {
+ fieldReadyTrigger();
+ nameHadHaveText = nameHaveText;
+ }
+ }
+
+
+ , doReply = function ()
+ {
+ var c_reply = this;
+ // This will ensure capcha_wrapper is set
+ fieldReadyTrigger();
+ // Set comment id
+ submit_button.setAttribute( new DataKey( "cid", c_reply.getAttribute( "value" ) ) );
+
+ // Close reply indicator
+ Dand.id("ri_switch").style.top = "0";
+
+ // If this button is not that button
+ button && ( c_reply != button ) && releaseReplyButton();
+
+
+ // Lock reply button
+ c_reply.setAttribute("locked", "1");
+ c_reply.onclick = null;
+
+ c_reply.style.cursor = "default";
+ c_reply.style.background = "Crimson";
+ c_reply.style.width = "1.2em";
+ c_reply.style.right = "-1.2em";
+
+ // Configure release lock
+ button = c_reply;
+
+ var rf = Dand.id( "recaptcha_field" );
+ if( rf && rf.hasChildNodes() )
+ {
+ // if capcha opened
+ closeCaptcha();
+ hideSubmit();
+ Cycle.delay( function() {
+ closeInputPanel( reviveInputpanel.bind( c_reply.parentNode.parentNode ) )
+ }, 500);
+ }
+ else
+ {
+ Cycle.delay( function() {
+ closeInputPanel( reviveInputpanel.bind( c_reply.parentNode.parentNode ) )
+ }, 250);
+ }
+ }
+
+ , releaseReplyButton = function ()
+ {
+ button.removeAttribute("locked");
+ button.onclick = doReply.bind(button);
+ button.style.cursor
+ = button.style.background
+ = button.style.width
+ = button.style.right
+ = "";
+ }
+
+ , fieldReadyTrigger = loggedIn
+ ? /* Overload */ function ()
+ {
+ // Get wrapper
+ captcha_wrapper || (captcha_wrapper = Dand.id( "capcha_wrapper", true ));
+ submit_button || ( submit_button = Dand.id( "c_submit", true ) );
+ if(contentHaveText)
+ {
+ // show submit button
+ showSubmit();
+ fieldReady = true;
+ }
+ else
+ {
+ // hide submit button
+ hideSubmit();
+ fieldReady = false;
+ }
+ }
+ : /* Overload */ function ()
+ {
+ captcha_wrapper || (captcha_wrapper = Dand.id( "capcha_wrapper", true ));
+ submit_button || ( submit_button = Dand.id( "c_submit", true ) );
+ // Create capcha
+ if(!fieldReady && contentHaveText && nameHaveText)
+ {
+ createCaptcha();
+ // show submit button
+ showSubmit();
+ fieldReady = true;
+ }
+ else if(!(contentHaveText || nameHaveText))
+ {
+ // hide submit button
+ hideSubmit();
+ fieldReady = false;
+ }
+
+ }
+
+ , submitComment = loggedIn
+ ? /* Overload */ function ()
+ {
+ // Postdata
+ var p = {
+ "article_id": article.id
+ , "name": currentUser.name
+ , "website": currentUser.website
+ , "avatar": currentUser.avatar
+ }
+ // pass-through obj
+ , comment_id = submit_button.getDAttribute( "cid" );
+
+ p.article_id = article.id;
+ // Set comment id if available
+ comment_id && ( p["comment_id"] = comment_id );
+ hideSubmit();
+
+ if( getFieldsValidated(p) )
+ {
+ popBubble();
+ // Post the data, bind obj to Handler
+ postData( Config.processor, p, commentSuccess.bind(p), commentFailed );
+ }
+ else
+ {
+ Cycle.delay( showSubmit, 1500 );
+ }
+ }
+ : /* Overload */ function ()
+ {
+ var p = { "article_id": article.id }
+ , comment_id = submit_button.getDAttribute( "cid" );
+
+ // Set comment id if available
+ comment_id && ( p["comment_id"] = comment_id );
+ hideSubmit();
+
+ if( getFieldsValidated( p ) )
+ {
+ popBubble();
+ captcha_wrapper.readOnly = true;
+
+ closeCaptcha();
+ // Post the data, bind obj to Handler
+ postData( Config.processor, p, commentSuccess.bind(p), commentFailed );
+
+ }
+ else
+ {
+ Cycle.delay( showSubmit, 1500 );
+ }
+ }
+
+ , commentSuccess = function ( obj )
+ {
+ // Pass Color = Green
+ cBubble.setColor("YellowGreen");
+
+ // Popup Success message
+ cBubble.pop("Success");
+
+ // blurp after 1 sec
+ Cycle.delay( function (){ cBubble.blurp(); }, 1500 );
+
+ generateCommentStack( this, obj );
+ }
+
+ // Input panel control
+ , closeInputPanel = function (handler) {
+ cHeight = cp.clientHeight;
+
+ cp.style.transition = "all 0s";
+
+ cp.style.height = cHeight + "px";
+ cp.style.overflow = "hidden";
+
+ // Delay for 10 millisec for establishing transition properties
+ Cycle.next(function() {
+ this.style.transition = "";
+ }.bind(cp));
+
+ Trigger.transition(cp.style, "", function()
+ {
+ cp.style.padding = "0";
+ cp.style.height = "0";
+ });
+
+ // After transition finished, remove input panel
+ Trigger.height(cp, 0, function()
+ {
+ cp.parentNode.removeChild(cp);
+ // Pass panel to handler
+ if(handler) handler();
+ });
+ }
+
+ , openInputPanel = function ()
+ {
+ cp.style.padding = "";
+ cp.style.height = cHeight + "px";
+
+ // Delay for 500 millisec waiting for transition finished
+ Cycle.delay(function()
+ {
+ cp.style.transition = "all 0s";
+ cp.style.height = "";
+ cp.style.overflow = "";
+
+ Dand.id("ri_switch").style.top = "";
+
+ fieldReady = false;
+ fieldReadyTrigger();
+ }, 500);
+ }
+
+
+ , reviveInputpanel = function ()
+ {
+ this.appendChild(cp);
+ Cycle.next(openInputPanel);
+ }
+
+
+
+ // Comments submission and handling
+
+ , commentFailed = function (obj)
+ {
+ // Handle error here to prevent accidental catch by caller
+ try
+ {
+ if(obj != null)
+ {
+ if(obj.reload)
+ {
+ // reload the recaptcha
+ createCaptcha();
+ Cycle.delay(showSubmit, 1500);
+
+ // Wrong answer.
+ var f = Dand.id("false_capcha");
+ Cycle.delay( function()
+ {
+ this.innerHTML = "Wrong answer, please re-enter.";
+ this.style.height = "1.2em";
+ }.bind(f), 500 );
+
+ Cycle.delay(function (){cBubble.blurp();}, 500);
+ }
+ else
+ {
+ // Reload not needed, serious error had occurred
+ Cycle.delay(function (){cBubble.blurp();}, 500);
+ Cycle.delay(function ()
+ {
+ cBubble.setColor("red");
+ cBubble.pop("Server error");
+ }, 1000);
+
+ }
+ }
+ else
+ {
+ // reload the recaptcha
+ createCaptcha();
+ Cycle.delay(showSubmit, 1500);
+
+ Cycle.delay(function (){cBubble.blurp();}, 500);
+ Cycle.delay(function ()
+ {
+ cBubble.setColor("red");
+ cBubble.pop("Cannot connect");
+ }, 1000);
+ }
+ }
+ catch(e)
+ {
+ debug.Error(e);
+ }
+ }
+
+ , submitKey = new EventKey("Click", submitComment)
+ // Enter key
+ , captchaKey = new EventKey("KeyDown", function(e) { if(e.keyCode == 13) submitComment(); })
+
+
+ , _showSubmit = function ()
+ {
+ // prevent multiple prop assign
+ if( !submit_button.hasListener( submitKey ) )
+ {
+ submit_button.style.width = "1.2em";
+ submit_button.style.right = "-1.2em";
+ submit_button.style.cursor = "pointer";
+ }
+ submit_button.addEventListener( submitKey );
+ }
+
+ , _hideSubmit = function ()
+ {
+ // prevent multiple prop assign
+ if( submit_button.hasListener( submitKey ) )
+ {
+ submit_button.style.cursor = "default";
+ submit_button.style.width = "";
+ submit_button.style.right = "";
+ }
+ submit_button.removeEventListener( submitKey );
+ }
+
+ , showSubmit = loggedIn
+ ? /* Overload */ _showSubmit
+ : /* Overload */ function ()
+ {
+ _showSubmit();
+ captcha_wrapper.addEventListener(captchaKey);
+ }
+ , hideSubmit = loggedIn
+ ? /* Overload */ _hideSubmit
+ : /* Overload */ function ()
+ {
+ _hideSubmit();
+ captcha_wrapper.removeEventListener(captchaKey);
+ }
+
+
+ , generateCommentStack = function (rObj, obj)
+ {
+
+ var
+ // Hold for transitions
+ c_ind, c_cont, cpole
+ // The info stack
+ , _c_info = wrapc("c_info",
+ [
+ wrapc("fls", rObj.website ? Dand.wrapne( "a", rObj.name, IKey.quickDef( "target", "_blank", "href", rObj.website ) ) : rObj.name )
+ , wrapc( "flsf", obj.time )
+ ])
+ // loggedIn Overload
+ , c_info = loggedIn
+ ? wrapc("cu_stack", [ _c_info
+ , Dand.textNode(" ")
+ , wrapc("cu_avatar"
+ , Dand.wrapna( "img", IKey.quickDef( "src", rObj.avatar, "alt", "avatar" ) )
+ )]
+ )
+ : _c_info
+ // Generate comment stack
+ , c_stack = rObj.comment_id
+ // Reply Structure
+ ? wrapc("reply", wrapc("c_cont", [ c_ind = wrapc("r_indicator") , wrapc("c_text", rObj.content) , c_info ]))
+ : wrapc("c_comm",
+ [
+ c_ind = wrapc("c_indicator")
+ , c_cont = wrapc("c_cont", [ cpole = wrapc("cpole") , wrapc("c_text", rObj.content) , c_info ])
+ ]
+ )
+ ;
+
+ // Remove transition properties
+ c_stack.style.transition = "all 0s";
+ // Close Input panel
+ closeInputPanel( null );
+
+ // Remove false_fields
+ for(var i in fields) {
+ i = Dand.id("false_" + fields[i]);
+ i.parentNode.removeChild(i);
+ }
+ c_stack.style.opacity = 0;
+ cp.parentNode.insertBefore( c_stack, cp );
+
+ c_stack.style.marginTop = ( -c_stack.clientHeight ) + "px";
+
+ // if nocomment notice exist, remove it
+ if( i = Dand.id("nocomment") )
+ {
+ i.style.transition = "all 0s";
+ i.style.width = "658px";
+ i.style.height = "22px";
+ i.style.overflow = "hidden";
+ // transition
+ Cycle.next(function()
+ {
+ this.style.transition = "";
+ this.style.margin = "0";
+ this.style.width = "0";
+ this.style.padding = "0";
+ this.style.height = "0";
+ }.bind(i));
+ // Pending remove
+ Trigger.height(i, 0, function(){ this.parentNode.removeChild(this); }.bind(i));
+ }
+
+ // Delay for 10 millisec for establishing transition properties
+ Cycle.next( function() { this.style.transition = ""; }.bind( c_stack ) );
+
+ Cycle.delay(function()
+ {
+ this.style.marginTop = "";
+ this.style.opacity = 1;
+ }.bind(c_stack), 600);
+ }
+
+ //// Captcha
+ , createCaptcha = loggedIn
+ ? function() {}
+ : function ()
+ {
+ // Remove capcha
+ new IDOMElement().lootChildren( Dand.id( "recaptcha_field" ) );
+ Recaptcha.render( "recaptcha_field", {
+ "theme": "light"
+ , "sitekey": Config.siteKey
+ } );
+
+ openCaptcha();
+ }
+
+ , openCaptcha = loggedIn
+ ? function() {}
+ :function ()
+ {
+ var rf = Dand.id( "recaptcha_field" );
+
+ var k = function () {
+ return ( 0 < this.a.clientHeight );
+ }.bind({ a: rf });
+
+ Trigger.register(
+ k , function() {
+ captcha_wrapper.style.height = rf.clientHeight + "px";
+ }
+ , 50 );
+ }
+
+ , closeCaptcha = loggedIn
+ ? function() {}
+ :function ()
+ {
+ captcha_wrapper.style.height = "0";
+ }
+
+
+
+ // Validation
+ , fields = loggedIn ? ["content"] : ["content", "name", "email", "website"]
+ , _getFieldsValidated = function (obj)
+ {
+ var name, field, falsefield, f;
+
+ for(var i in fields)
+ {
+ name = fields[i];
+ field = name + "_field";
+ falsefield = "false_" + name;
+
+ f = Dand.id( falsefield );
+ f.style.height = 0;
+ switch(name)
+ {
+ case "name":
+ if(!( obj[name] = Dand.id(field).value.trim() ))
+ Cycle.delay( function() { this.style.height = elmH; }.bind(f), 500 );
+ break;
+ case "content":
+ if(!( obj[name] = Dand.id(field).value.trim() ))
+ Cycle.delay( function() { this.style.height = elmH; }.bind(f), 500 );
+ break;
+ case "website":
+ if(!( obj[name] = validateURL( Dand.id(field).value.trim() )))
+ {
+ obj[name] = false;
+ Cycle.delay( function() { this.style.height = elmH; }.bind(f), 500 );
+ }
+ else
+ {
+ if(obj[name] == true)
+ delete obj[name]
+ }
+ break;
+ case "email":
+ if(!( obj[name] = validateMail( Dand.id(field).value.trim() ) ))
+ {
+ obj[name] = false;
+ Cycle.delay( function() { this.style.height = elmH; }.bind(f), 500 );
+ }
+ else
+ {
+ if(obj[name] == true)
+ delete obj[name]
+ }
+ break;
+ }
+ }
+
+ for(var i in obj) if(!obj[i]) return false;
+
+ return true;
+ }
+
+ , getFieldsValidated = loggedIn
+ ? /* Overload */ _getFieldsValidated
+ : /* Overload */ function(obj)
+ {
+ var f = Dand.id("false_capcha");
+ f.style.height = 0;
+ // get the response field
+ if(!( obj["recaptcha_response_field"] = Recaptcha.getResponse().trim() ))
+ {
+ Cycle.delay(
+ function()
+ {
+ this.innerHTML = "You need to prove that you are a human";
+ this.style.height = elmH;
+ }.bind(f)
+ , 500 );
+ }
+ // get the challenge field
+ // else obj["recaptcha_challenge_field"] = Recaptcha.get_challenge();
+
+ return _getFieldsValidated(obj);
+ }
+
+ , showReplyButton = function ()
+ {
+ this.style.width = "1.2em";
+ this.style.right = "-1.2em";
+ }
+
+ , hideReplyButton = function ()
+ {
+ if(!this.getAttribute("locked"))
+ this.style.width = this.style.right = "";
+ }
+
+ , popBubble = function ()
+ {
+ if(cBubble == null)
+ {
+ // create a new bubble
+ cBubble = new Bubble();
+ document.body.appendChild( cBubble.init() );
+ // pop message
+ cBubble.setColor( "cornflowerblue" );
+ Cycle.next( function () { cBubble.pop("Submitting"); } );
+ }
+ }
+ ;
+
+ if( !loggedIn )
+ {
+ IDOMElement( nameField ).addEventListener( "Input", nameInput );
+ }
+
+ IDOMElement( contentField ).addEventListener( "Input", commentInput );
+
+ var l = c_body.childNodes, c_id;
+
+ for( var i in l )
+ {
+ if( l[i].nodeType == 1
+ && ( c_id = IDOMElement( l[i] ).getDAttribute("value") )
+ )
+ {
+ var reply_button = Dand.id("c_reply_" + c_id);
+ reply_button.onclick = doReply.bind(reply_button);
+
+ l[i].onmouseover = showReplyButton.bind(reply_button);
+ l[i].onmouseout = hideReplyButton.bind(reply_button);
+ }
+ }
+
+ var stage = c_body.parentNode
+ BotanJS.addEventListener( "Responsive"
+ /** e @type {Astro.Blog.Events.Responsive} */
+ , function( e ) {
+ e.data.ratio < 1
+ ? stage.style.width = "100%"
+ : stage.style.width = ""
+ ;
+ }
+ );
+
+ if( !loggedIn )
+ {
+ var reUUID = Perf.uuid;
+ Cycle.perma(
+ reUUID, function()
+ {
+ if( Recaptcha = window["grecaptcha"] )
+ {
+ Cycle.permaRemove( reUUID );
+ debug.Info( "Recaptcha instance is up" );
+ }
+ } , 500
+ );
+ }
+ };
+
+ Bootstrap.regInit( init );
+})();
diff --git a/botanjs/src/Astro/Blog/Components/ControlPanel.css b/botanjs/src/Astro/Blog/Components/ControlPanel.css
new file mode 100644
index 0000000..3a4313f
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/ControlPanel.css
@@ -0,0 +1,50 @@
+.control_panel {
+ height: 100%;
+ margin-top: 0.2em;
+}
+
+.control_panel > a {
+ display: inline-block;
+ position: relative;
+ overflow: hidden;
+
+ width: 80px;
+ height: 80px;
+}
+
+.control_panel > a:after {
+ content: attr(data-desc);
+
+ font-family: custom-sans;
+
+ background-color: rgba(0, 0, 0, 0.6);
+ color: white;
+
+ position: absolute;
+
+ width: 100%;
+
+ padding: 0 0.2em;
+
+ left: 0;
+ bottom: 0;
+
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.control_panel > a > span {
+ padding: 0.2em 0.5em;
+ background-color: orange;
+ color: white;
+}
+
+.control_panel > div {
+ font-size: 0.8em;
+ font-family: custom-sans;
+}
+
+.control_panel > div > a {
+ color: #222;
+}
diff --git a/botanjs/src/Astro/Blog/Components/ControlPanel.js b/botanjs/src/Astro/Blog/Components/ControlPanel.js
new file mode 100644
index 0000000..b82128f
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/ControlPanel.js
@@ -0,0 +1 @@
+// __namespace( "Astro.Blog.Components.ControlPanel" );
diff --git a/botanjs/src/Astro/Blog/Components/CrowdTag.css b/botanjs/src/Astro/Blog/Components/CrowdTag.css
new file mode 100644
index 0000000..619509d
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/CrowdTag.css
@@ -0,0 +1,6 @@
+.tags > a {
+ color: saddlebrown;
+ font-family: custom-sans;
+ padding: 0.2em;
+ float: left;
+}
diff --git a/botanjs/src/Astro/Blog/Components/CrowdTag.js b/botanjs/src/Astro/Blog/Components/CrowdTag.js
new file mode 100644
index 0000000..355fd5e
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/CrowdTag.js
@@ -0,0 +1 @@
+// __namespace( "Astro.Blog.Components.CrowdTag" );
diff --git a/botanjs/src/Astro/Blog/Components/Entry/Block.css b/botanjs/src/Astro/Blog/Components/Entry/Block.css
new file mode 100644
index 0000000..588da7c
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Entry/Block.css
@@ -0,0 +1,149 @@
+.bk_block {
+ float: left;
+
+ width: 225px;
+ height: 325px;
+
+ margin: 1em;
+
+ position: relative;
+ overflow: hidden;
+
+ color: #666;
+ background-color: white;
+ border: 1px solid #D4D7C9;
+
+ display: block;
+}
+
+.bk_block:hover {
+ text-decoration: none;
+}
+
+.bk_SlideWrapper {
+ position: absolute;
+ overflow: hidden;
+ top: 0.5em;
+ width: 100%;
+ height: 100%;
+}
+
+.bk_date {
+ font-size: 0.8em;
+ text-align: right;
+}
+
+.bk_title {
+ font-size: 1.2em;
+ max-height: 2.4em;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ color: #444;
+}
+
+.bk_content {
+ text-overflow: ellipsis;
+ overflow: hidden;
+}
+
+.bk_author {
+ color: crimson;
+ text-align: right;
+}
+
+.bk_cCount {
+ position: absolute;
+ bottom: 1em;
+ left: 0.5em;
+
+ font-size: 0.8em;
+ font-family: sans-serif;
+ color: orangered;
+}
+
+
+.bk_blockSlide {
+ position: absolute;
+ width: 100%;
+ height: 150%;
+
+ top: 0;
+}
+
+.bk_blockSlide:after {
+ content: attr(data-content);
+
+ position: absolute;
+
+ top: 100%;
+
+ width: 100%;
+ height: 50%;
+
+ padding: 0 0.5em;
+
+ font-size: 0.8em;
+ font-family: custom-serif;
+
+ color: #EEE;
+ background: purple;
+
+ overflow: hidden;
+ text-overflow: ellipsis;
+
+ -webkit-box-shadow: inset 0px 15px 15px -15px black;
+ -moz-box-sizing: inset 0px 15px 15px -15px black;
+ box-shadow: inset 0px 15px 15px -15px black;
+
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+
+ -webkit-transition: all 500ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -moz-transition: all 500ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -ms-transition: all 500ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -o-transition: all 500ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ transition: all 500ms cubic-bezier(0.215, 0.610, 0.355, 1.000); /* easeOutCubic */
+
+ -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -moz-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -ms-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -o-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); /* easeOutCubic */
+}
+
+.bk_blockSlide:hover:after {
+ top: 62.5%;
+ padding: 0.5em;
+}
+
+.bk_blockSlide:hover {
+ top: -50%;
+}
+
+.bk_blockFront {
+ position: absolute;
+ background: white;
+
+ width: 100%;
+ height: 100%;
+}
+
+.bk_blockWrapper {
+ position: relative;
+ padding: 0 0.5em;
+}
+
+.bk_banner {
+ width: 50%;
+ padding: 50%;
+
+ background-image: url(http://file.astropenguin.net/blog/layout-images/featured.png);
+ background-position: center;
+ background-repeat: no-repeat;
+ background-color: white;
+
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
diff --git a/botanjs/src/Astro/Blog/Components/Entry/Block.js b/botanjs/src/Astro/Blog/Components/Entry/Block.js
new file mode 100644
index 0000000..e406e74
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Entry/Block.js
@@ -0,0 +1 @@
+// __namespace( "Astro.Blog.Components.Entry.Block" );
diff --git a/botanjs/src/Astro/Blog/Components/Entry/Blog.css b/botanjs/src/Astro/Blog/Components/Entry/Blog.css
new file mode 100644
index 0000000..8fc6172
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Entry/Blog.css
@@ -0,0 +1,104 @@
+.b_entry {
+ background-color: #FAFAFA;
+ /* box-shadow: 0 0 8px -2px black; */
+ border: 1px solid #D7D4C9;
+
+ word-wrap: break-word;
+
+ position: relative;
+
+ margin-top: 0;
+ padding: 1em;
+}
+
+.b_title {
+ font-family: custom-serif;
+ font-size: 2.5em;
+ margin-bottom: 0.25em;
+ color: #444;
+}
+
+.b_avartar {
+ width: 90px;
+ height: 90px;
+
+ margin: 0 0 0.5em 0.5em;
+
+ float: right;
+
+ -moz-box-shadow: 0 0 5px 0 black;
+ -webkit-box-shadow: 0 0 5px 0 black;
+ box-shadow: 0 0 5px 0 black;
+
+ background-color: white;
+ background-position: center;
+ background-repeat: no-repeat;
+}
+
+.b_bodyWrapper {
+ font-family: custom-sans;
+ color: #555;
+ min-height: 155px;
+}
+
+.b_button {
+ position: relative;
+
+ font-family: custom-sans;
+ font-size: 1em;
+
+ padding: 0.2em 0.5em;
+ margin: 0.5em 0.2em;
+
+ background-color: grey;
+ color: white;
+
+ cursor: default;
+
+ float: right;
+}
+
+.b_delete {
+ background-color: crimson;
+}
+
+.b_publish { background-color: slategrey; }
+.b_publish:before { content: 'Publish'; background-color: purple; }
+.b_publish:after { content: 'Unpublish'; background-color: orangered; }
+
+.b_notify { background-color: slategrey; }
+.b_notify:before { content: 'Follow'; }
+.b_notify:after { content: 'Unfollow'; }
+
+.b_edit {
+ background-color: yellowgreen;
+}
+
+.b_preview {
+ background-color: orangered;
+}
+
+.b_author {
+ color: purple;
+ text-align: right;
+}
+
+.b_date, .b_mdate {
+ text-align: right;
+}
+
+.b_date {
+ font-family: custom-serif;
+}
+
+.b_mdate {
+ font-style: italic;
+ font-family: custom-sans;
+ font-size: 0.8em;
+ opacity: 0.5;
+}
+
+.b_tags > a {
+ font-size: 1em;
+ color: #76400C;
+}
diff --git a/botanjs/src/Astro/Blog/Components/Entry/Blog.js b/botanjs/src/Astro/Blog/Components/Entry/Blog.js
new file mode 100644
index 0000000..8f10fbc
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Entry/Blog.js
@@ -0,0 +1 @@
+// __namespace( "Astro.Blog.Components.Entry.Blog" );
diff --git a/botanjs/src/Astro/Blog/Components/Entry/Home.css b/botanjs/src/Astro/Blog/Components/Entry/Home.css
new file mode 100644
index 0000000..3bb22a9
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Entry/Home.css
@@ -0,0 +1,129 @@
+.h_title {
+ font-size: 2.5em;
+ color: #444;
+}
+
+.home_entry {
+ position: relative;
+ margin: 1em 0;
+}
+
+.h_info {
+ position: absolute;
+ height: 100%;
+ width: 7em;
+ text-align: right;
+}
+
+
+.h_body {
+ margin-left: 7em;
+ margin-right: 0.5em;
+}
+
+.h_bodyWrapper {
+ border: 1px solid #D4D7C9;
+ background-color: #FAFAFA;
+ color: #777;
+
+ padding: 1em 1em 2em 1em;
+
+ min-height: 155px;
+ /* Same as image size */
+ max-width: 600px;
+}
+
+.h_datetime {
+ right: 0;
+ bottom: 0.95em;
+}
+
+.h_auth_com {
+ padding-top: 0.5em;
+
+ font-family: Calibri;
+ color: #D4145A;
+}
+
+
+.h_avatarWrapper {
+ display: block;
+
+ width: 80px;
+ height: 80px;
+}
+
+.h_avatar, .a_avatarWrapper {
+ width: 80px;
+ height: 80px;
+ margin: 0 auto;
+}
+
+.h_avatar {
+
+ border-radius: 80px;
+
+ background-color: #FAFAFA;
+ background-size: 50px 50px;
+ background-repeat: no-repeat;
+ background-position: center;
+
+ -moz-box-shadow: 2px 2px 5px 0 black;
+ -webkit-box-shadow: 2px 2px 5px 0 black;
+ box-shadow: 2px 2px 5px 0 black;
+}
+
+.h_avatar:hover {
+ width: 70px;
+ height: 70px;
+ border-radius: 70px;
+
+ margin-top: 5px;
+
+ background-size: 60px 60px;
+}
+
+.h_infoWrapper {
+ padding: 0.5em;
+}
+
+.h_cCount {
+ font-family: sans-serif;
+ color: cornflowerblue;
+ font-size: 80%;
+}
+
+.h_title {
+ font-size: 2.5em;
+ color: #444;
+}
+
+.h_title, .h_desc {
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.h_read_full {
+ position: absolute;
+ bottom: 0.5em;
+ right: 1.5em;
+
+ font-size: 0.8em;
+ text-align: right;
+ padding: 0.5em 0 0.5em 0;
+ color: #F15A24;
+}
+
+.h_read_full_a {
+ color: #F15A24;
+}
+
+.h_img {
+ width: 100%;
+ max-height: 350px;
+ overflow: hidden;
+}
+
+.h_img > img {
+ max-width: 100%;
+}
diff --git a/botanjs/src/Astro/Blog/Components/Entry/Home.js b/botanjs/src/Astro/Blog/Components/Entry/Home.js
new file mode 100644
index 0000000..b171548
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Entry/Home.js
@@ -0,0 +1 @@
+// __namespace( "Astro.Blog.Components.Entry.Home" );
diff --git a/botanjs/src/Astro/Blog/Components/Entry/List.css b/botanjs/src/Astro/Blog/Components/Entry/List.css
new file mode 100644
index 0000000..24a4804
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Entry/List.css
@@ -0,0 +1,67 @@
+.list_entry {
+ position: relative;
+
+ margin: 1em 0;
+ padding: 0.5em;
+
+ background-color: #FAFAFA;
+ border: 1px solid #D4D7C9;
+}
+
+.caption {
+ font-size: 2.5em;
+ color: #444;
+}
+
+.title {
+ font-size: 1.5em;
+ color: #555;
+}
+
+.content {
+ color: #777;
+}
+
+.tags {
+ padding: 0.5em 0;
+}
+
+.flag {
+ font-family: custom-sans;
+ font-size: 0.8em;
+
+ padding: 0 0.5em;
+ margin: 0 0 0 0.2em;
+
+ float: right;
+ cursor: default;
+}
+
+.flag.archived { color: white; background: #888; }
+.flag.archived:hover { background: #666; }
+
+.flag.featured { color: white; background: coral; }
+.flag.featured:hover { background: orangered; }
+
+.tag {
+ color: #76400C;
+
+ padding: 0.2em 0.5em;
+
+ font-family: custom-sans;
+}
+
+.mdate, .author {
+ text-align: right;
+}
+
+.author {
+ color: crimson;
+}
+
+.mdate {
+ color: #AAA;
+ font-size: 0.8em;
+ font-family: custom-sans;
+}
+
diff --git a/botanjs/src/Astro/Blog/Components/Entry/List.js b/botanjs/src/Astro/Blog/Components/Entry/List.js
new file mode 100644
index 0000000..69d54bf
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Entry/List.js
@@ -0,0 +1 @@
+// __namespace( "Astro.Blog.Components.Entry.List" );
diff --git a/botanjs/src/Astro/Blog/Components/Entry/Mega.css b/botanjs/src/Astro/Blog/Components/Entry/Mega.css
new file mode 100644
index 0000000..3140bce
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Entry/Mega.css
@@ -0,0 +1,139 @@
+.m_title {
+ font-size: 2.5em;
+ color: #444;
+}
+
+.m_more {
+ color: #76400C;
+}
+
+.mega_entry span {
+ display: block;
+}
+
+.mega_entry {
+ border: 6px solid white;
+ background: white;
+ color: black;
+
+ position: relative;
+ margin: 1em;
+
+ display: block;
+
+ word-wrap: break-word;
+
+ box-shadow: 0 0 0 1px #D4D7C9;
+}
+
+.mega_entry:hover {
+ text-decoration: none;
+}
+
+.mega_entry:after {
+ content: attr(data-content);
+
+ width: 100%;
+ height: 0;
+
+ top: 100%;
+
+ position: absolute;
+ overflow: hidden;
+
+ color: white;
+ background: rgba(0, 0, 0, 0.65);
+
+ font-family: custom-serif;
+
+ padding: 0 0.5em;
+
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+
+ -webkit-transition: all 300ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -moz-transition: all 300ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -ms-transition: all 300ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -o-transition: all 300ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ transition: all 300ms cubic-bezier(0.215, 0.610, 0.355, 1.000); /* easeOutCubic */
+
+ -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -moz-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -ms-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -o-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); /* easeOutCubic */
+}
+
+.mega_entry:focus:after, .mega_entry:hover:after {
+ top: 50%;
+ height: 50%;
+ padding: 0.5em;
+}
+
+.mega_entry > #noteTag {
+ background: black;
+}
+
+.noteTag {
+ color: white;
+ padding: 0.2em;
+ overflow: hidden;
+
+ -moz-box-shadow: 0 2px 2px 0 black;
+ -webkit-box-shadow: 0 2px 2px 0 black;
+ box-shadow: 0 2px 2px 0 black;
+
+ position: absolute;
+ background: slategrey;
+ right: -6px;
+ top: 10px;
+}
+
+.mega_entry > .cCont {
+ position: relative;
+
+ height: 130px;
+ background-position: center top;
+ background-repeat: no-repeat;
+ background-color: #EEE;
+
+ background-image: url('http://file.astropenguin.net/blog/layout-images/archived.png');
+}
+
+.mega_entry > .cCont:before {
+ content: attr(data-content);
+ position: absolute;
+
+ line-height: 100%;
+ font-size: 2.5em;
+
+ padding: 0.1em;
+
+ text-shadow: 2px 2px 8px black;
+ color: white;
+
+ width: 100%;
+ height: 100%;
+
+ box-sizing: border-box;
+
+ background-color: rgba( 0, 0, 0, 0.2 );
+}
+
+.cDate {
+ padding: 0.2em;
+ font-size: 0.8em;
+}
+.cEOF {
+ height: 10px;
+}
+
+
+.Sun { background: #FF1D25; }
+.Mon { background: #662D91; }
+.Tue { background: #3FA9F5; }
+.Wed { background: #7AC943; }
+.Thu { background: #FF7BAC; }
+.Fri { background: #603813; }
+.Sat { background: #FF931E; }
diff --git a/botanjs/src/Astro/Blog/Components/Entry/Mega.js b/botanjs/src/Astro/Blog/Components/Entry/Mega.js
new file mode 100644
index 0000000..3c902f5
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Entry/Mega.js
@@ -0,0 +1 @@
+// __namespace( "Astro.Blog.Components.Entry.Mega" );
diff --git a/botanjs/src/Astro/Blog/Components/Entry/Tag.css b/botanjs/src/Astro/Blog/Components/Entry/Tag.css
new file mode 100644
index 0000000..ba8da11
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Entry/Tag.css
@@ -0,0 +1,63 @@
+.t_title {
+ font-size: 2.5em;
+ color: #444;
+}
+
+.ts_title {
+ font-size: 1.5em;
+ color: #555;
+}
+
+ul.n-archive > li {
+ position: relative;
+ padding: 0.5em 0.5em 0.1em;
+ margin: 0.5em;
+ border-right: 1px solid transparent;
+}
+
+ul.n-archive > li:hover {
+ border-right: 1px solid #555;
+}
+
+ul.n-archive a {
+ width: 100%;
+
+ display: inline-block;
+}
+
+ul.n-archive a:before {
+ content: attr(data-value);
+
+ margin-right: 0.5em;
+ padding: 0.5em;
+
+ float: left;
+ width: 1.4em;
+ text-align: center;
+
+ color: white;
+ font-family: custom-sans;
+}
+
+ul.n-archive a:hover:before {
+ text-decoration: underline;
+}
+
+ul.n-archive a:hover {
+ text-decoration: none;
+}
+
+ul.n-archive a.Sun:before { color: rgba(255, 29, 37, 1); }
+ul.n-archive a.Mon:before { color: rgba(102, 45, 145, 1); }
+ul.n-archive a.Tue:before { color: rgba(63, 169, 245, 1); }
+ul.n-archive a.Wed:before { color: rgba(122, 201, 67, 1); }
+ul.n-archive a.Thu:before { color: rgba(255, 123, 172, 1); }
+ul.n-archive a.Fri:before { color: rgba(96, 56, 19, 1); }
+ul.n-archive a.Sat:before { color: rgba(255, 147, 30, 1); }
+ul.n-archive a.Sun:hover:before { color: white; background: rgba(255, 29, 37, 1); }
+ul.n-archive a.Mon:hover:before { color: white; background: rgba(102, 45, 145, 1); }
+ul.n-archive a.Tue:hover:before { color: white; background: rgba(63, 169, 245, 1); }
+ul.n-archive a.Wed:hover:before { color: white; background: rgba(122, 201, 67, 1); }
+ul.n-archive a.Thu:hover:before { color: white; background: rgba(255, 123, 172, 1); }
+ul.n-archive a.Fri:hover:before { color: white; background: rgba(96, 56, 19, 1); }
+ul.n-archive a.Sat:hover:before { color: white; background: rgba(255, 147, 30, 1); }
diff --git a/botanjs/src/Astro/Blog/Components/Entry/Tag.js b/botanjs/src/Astro/Blog/Components/Entry/Tag.js
new file mode 100644
index 0000000..6a2b284
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Entry/Tag.js
@@ -0,0 +1 @@
+// __namespace( "Astro.Blog.Components.Entry.Tag" );
diff --git a/botanjs/src/Astro/Blog/Components/Notification.css b/botanjs/src/Astro/Blog/Components/Notification.css
new file mode 100644
index 0000000..1538844
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Notification.css
@@ -0,0 +1,155 @@
+.notifications {
+ position: relative;
+ cursor: default;
+}
+
+.nt_body {
+ position: absolute;
+
+ top: 0;
+ left: -300px;
+
+ max-width: 300px;
+ max-height: 600px;
+
+ text-align: left;
+
+ display: none;
+}
+
+.nt_body > div {
+ float: right;
+}
+
+.nt_button {
+ text-align: left;
+ display: block;
+
+ margin: 0.2em 0;
+
+ color: white;
+ background: #444;
+}
+
+.nt_button:before {
+ content: attr(data-count);
+ display: inline-block;
+
+ padding: 0 0.5em;
+ line-height: 1.5em;
+
+ background-color: royalblue;
+}
+
+.nt_button:before, .nt_switch:before {
+ -webkit-transition: all 500ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -moz-transition: all 500ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -ms-transition: all 500ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -o-transition: all 500ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ transition: all 500ms cubic-bezier(0.215, 0.610, 0.355, 1.000); /* easeOutCubic */
+
+ -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -moz-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -ms-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -o-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); /* easeOutCubic */
+}
+
+.nt_button:hover:before {
+ background: dodgerblue;
+
+}
+
+.nt_arrow {
+ border-top: 0.8em solid transparent;
+ border-left: 1em solid #444;
+ border-bottom: 0.8em solid transparent;
+}
+
+.nt_container {
+ min-width: 300px;
+
+ padding: 0.5em;
+ background: #444;
+
+ margin-right: 0.5em;
+ margin-top: -1.6em;
+}
+
+.nt_date {
+ text-align: right;
+}
+
+.nt_container > ul {
+ white-space: nowrap;
+
+ overflow: hidden;
+ text-overflow: ellipsis;
+
+ color: white;
+}
+
+.nt_container > ul > li {
+ padding: 0.25em 0.5em;
+ color: white;
+ text-decoration: none;
+}
+
+.nt_container > ul > li:hover {
+ background-color: orangered;
+}
+
+.nt_icon_settings {
+ background-image: url(http://file.astropenguin.net/blog/layout-images/settings.png);
+ padding-left: 25px !important;
+ background-repeat: no-repeat;
+}
+
+ul.nt_smenu {
+ font-family: custom-sans;
+ cursor: default;
+}
+
+ul.nt_smenu > li {
+ padding: 0.2em 0.5em;
+}
+
+ul.nt_smenu > li:hover {
+ background-color: black;
+}
+
+ul.nt_smenu > li[active] > span.nt_switch:before {
+ margin-top: -1.2em;
+}
+
+ul.nt_smenu > li > span {
+ display: inline-block;
+}
+
+.nt_switch {
+ width: 2.5em;
+ height: 1em;
+ text-align: center;
+ overflow: hidden;
+}
+
+.nt_switch:before {
+ float: left;
+ color: red;
+ content: 'OFF';
+}
+
+.nt_switch:after {
+ float: left;
+ color: yellowgreen;
+ content: 'ON';
+}
+
+.nt_tname {
+ padding-left: 0.5em;
+
+}
+
+.nt_tcount {
+ float: right;
+}
diff --git a/botanjs/src/Astro/Blog/Components/Notification.js b/botanjs/src/Astro/Blog/Components/Notification.js
new file mode 100644
index 0000000..cdd69dd
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Notification.js
@@ -0,0 +1,235 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.Components.Notification" );
+
+ /** @type {System.Debug} */
+ var debug = __import( "System.Debug" );
+ /** @type {System.Cycle} */
+ var Cycle = __import( "System.Cycle" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {System.utils.IKey} */
+ var IKey = __import( "System.utils.IKey" );
+ /** @type {System.utils.DataKey} */
+ var DataKey = __import( "System.utils.DataKey" );
+ /** @type {System.utils.EventKey} */
+ var EventKey = __import( "System.utils.EventKey" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Components.MessageBox} */
+ var MessageBox = __import( "Components.MessageBox" );
+ /** @type {Components.Mouse.ContextMenu} */
+ var ContextMenu = __import( "Components.Mouse.ContextMenu" );
+ /** @type {Astro.Bootstrap} */
+ var Bootstrap = __import( "Astro.Bootstrap" );
+ /** @type {Astro.Blog.Config} */
+ var Config = __import( "Astro.Blog.Config" );
+ /** @type {Astro.utils.Date} */
+ var XDate = __import( "Astro.utils.Date" );
+
+ var postData = __import( "System.Net.postData" );
+ var smstamp = __import( "Astro.utils.Date.smstamp" );
+
+ var init = function ()
+ {
+ var stage = Dand.id( "notifications" );
+ var bodyStyle = Dand.id( "nt_body" ).style;
+
+ var conf = Config.get( "Notification" );
+ var base_path = Config.get( "BasePath" );
+
+ var processor = base_path + conf.paths.get_mesg;
+ var contextMenu;
+
+ var itemsMenu;
+
+ var nError = function( e ) {
+ // TODO: Do something on error
+ debug.Info( e );
+ };
+
+ var followLink = function(e)
+ {
+ window.location = base_path + this.link;
+ };
+
+ var toggleFollow = function(e)
+ {
+ var _action = "enable";
+ if( this.item.getAttribute( "active" ) )
+ {
+ _action = "disable";
+ if(
+ 0 < IDOMElement( this.count ).getDAttribute( "count" )
+ && !confirm( "All followed notifications of this type will be unfollowed. Continue?" )
+ ) return;
+ }
+
+ postData(
+ processor
+ , {
+ "action": _action
+ , "tid": IDOMElement( this.item ).getDAttribute( "tid" )
+ , "cid": 0
+ }
+ , toggleSuccess.bind(this)
+ , nError
+ );
+ };
+
+ var toggleSuccess = function(e)
+ {
+ if( this.item.getAttribute( "active" ) )
+ {
+ this.item.removeAttribute( "active" );
+ this.count.innerHTML = "N/A";
+ }
+ else
+ {
+ this.item.setAttribute( "active", 1 );
+ this.count.innerHTML = "0";
+ }
+
+ };
+
+ var showSettings = function( e )
+ {
+ // Create list <ul> <li> ... </ul>
+ var ul = Dand.wrap("ul", "nt_imenu", "nt_smenu");
+
+ for( var i in e.data )
+ {
+ var li
+ , li_s = [
+ // On/off toggle
+ Dand.wrap( "span", null, "nt_switch" )
+ , Dand.wrap( "span", null, "nt_tname", e.data[i].name )
+ ]
+ , keys = [ new DataKey( "tid", i ) ]
+ ;
+
+ if( e.data[i].hasOwnProperty("count") )
+ {
+ // type count
+ li_s[2] = Dand.wrap(
+ "span"
+ , null
+ , "nt_tcount"
+ , e.data[i].count + ""
+ , new DataKey( "count", e.data[i].count )
+ );
+ // Active
+ keys[ keys.length ] = new IKey( "active", 1 );
+ }
+ else
+ {
+ li_s[2] = Dand.wrap( "span", null, "nt_tcount", "N/A" );
+ }
+
+ // Create li element
+ li = Dand.wrapne( "li", li_s, keys );
+ ul.appendChild( li );
+
+ IDOMElement( li ).addEventListener(
+ "Click"
+ , toggleFollow.bind({ item: li, count: li_s[2] })
+ );
+
+ }
+
+ var msgBox = new MessageBox( "Notification Settings", ul, "Close" ).show();
+ };
+
+ var popupSettings = function()
+ {
+ postData( processor, { action: "getSettings" }, showSettings, nError );
+ };
+
+ var readNotification = function(e)
+ {
+ postData( processor, { action: "read", id: this.id }, followLink.bind( this ), nError );
+ };
+ // functions
+ var createContextMenu = function(e)
+ {
+ var items = [], menuShow = false;
+
+ for( var n in e.data )
+ {
+ // Create items
+ items[items.length] = new IKey(
+ e.data[n].message
+ , new EventKey(
+ "m_" + e.data[n].id
+ , readNotification.bind( e.data[n] )
+ )
+ );
+ }
+
+ items[ items.length ] = new EventKey( "Settings", popupSettings );
+
+ contextMenu = new ContextMenu(
+ stage
+ , items
+ , "LMB"
+ , nt_body
+ // showMenu/hideMenu overrides style settings
+ , {
+ "class": "nt_container"
+ , "showMenu": function(stage, event)
+ {
+ if( stage.className == "nt_date" ) return;
+ if( menuShow ) return;
+ bodyStyle.marginLeft = "1em";
+ bodyStyle.marginRight = "-1em";
+ bodyStyle.opacity = 0;
+ bodyStyle.display = "block";
+ Cycle.next(function()
+ {
+ bodyStyle.marginLeft
+ = bodyStyle.marginRight = 0;
+
+ bodyStyle.opacity = 1;
+ menuShow = true;
+ });
+ }
+ , "hideMenu": function(stage)
+ {
+ if( stage.className == "nt_date" ) return;
+ if( !menuShow ) return;
+ bodyStyle.marginLeft = "1em";
+ bodyStyle.marginRight = "-1em";
+ bodyStyle.opacity = 0;
+
+ Cycle.delay(function()
+ {
+ bodyStyle.display = "none";
+ // cubic 200, we set delay to 200
+ menuShow = false;
+ }, 200);
+ }
+ // Prevent default subnode chaining
+ , "chainHide": function( stage ) { }
+ , "chainShow": function( stage ) { }
+ }
+ );
+
+ for( n in e.data )
+ {
+ var li = contextMenu.getItemByKey( "m_" + e.data[n].id ).stage;
+ li.appendChild(
+ Dand.wrapc(
+ "nt_date"
+ , smstamp( new Date( Number( e.data[n].date ) * 1000 ) )
+ )
+ );
+ }
+
+ contextMenu.getItemByKey("Settings").stage.className = "nt_icon_settings";
+ };
+
+ postData( processor, { action: "get" }, createContextMenu );
+ };
+
+ Bootstrap.regInit( init );
+
+})();
diff --git a/botanjs/src/Astro/Blog/Components/SiteFile.css b/botanjs/src/Astro/Blog/Components/SiteFile.css
new file mode 100644
index 0000000..15ba366
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/SiteFile.css
@@ -0,0 +1,84 @@
+.site_file {
+ margin: 0.5em 0;
+}
+
+.sf_regular {
+ padding: 1em;
+ background-color: #F0F0F0;
+ display: inline-block;
+}
+
+.sf_failed {
+ color: orangered;
+ opacity: 0.5;
+}
+.sf_failed:hover { opacity: 1; }
+
+.loading_bubble {
+ width: 20px;
+ height: 20px;
+
+ display: block;
+ float: left;
+
+ position: relative;
+}
+
+.loading_bubble > span {
+ width: 0.25em;
+ height: 0.25em;
+
+ position: absolute;
+
+ background-color: royalblue;
+
+ animation: p1 1s infinite;
+ animation-timing-function: linear;
+
+ /* Safari and Chrome */
+ -webkit-animation: p1 1s infinite;
+ -webkit-animation-timing-function:linear;
+}
+
+.loading_bubble > span:nth-child(1) {
+ top: 2.5px;
+ left: 2.5px;
+}
+
+.loading_bubble > span:nth-child(2) {
+ top: 2.5px;
+ left: 12.5px;
+
+ animation-delay: 250ms;
+ -webkit-animation-delay: 250ms;
+}
+
+.loading_bubble > span:nth-child(4) {
+ top: 12.5px;
+ left: 12.5px;
+
+ animation-delay: 500ms;
+ -webkit-animation-delay: 500ms;
+}
+
+.loading_bubble > span:nth-child(3) {
+ top: 12.5px;
+ left: 2.5px;
+
+ animation-delay: 750ms;
+ -webkit-animation-delay: 750ms;
+}
+
+@keyframes p1
+{
+ 0% { opacity: 0; }
+ 50% { opacity: 1; }
+ 100% { opacity: 0; }
+}
+
+@-webkit-keyframes p1 /* Safari and Chrome */
+{
+ 0% { opacity: 0; }
+ 50% { opacity: 1; }
+ 100% { opacity: 0; }
+}
diff --git a/botanjs/src/Astro/Blog/Components/SiteFile.js b/botanjs/src/Astro/Blog/Components/SiteFile.js
new file mode 100644
index 0000000..ad2a7bd
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/SiteFile.js
@@ -0,0 +1,147 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.Components" );
+
+ /** @type {System.utils.EventKey} */
+ var IKey = __import( "System.utils.IKey" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Astro.Bootstrap} */
+ var Bootstrap = __import( "Astro.Bootstrap" );
+ /** @type {Astro.Blog.Config} */
+ var Conf = __import( "Astro.Blog.Config" );
+
+ var getData = __import( "System.Net.getData" );
+ var getSMStamp = __import( "Astro.utils.Date.smstamp" );
+
+ /** @type {_AstConf_.SiteFile} */
+ var config = null;
+
+ var SiteFile = function ( id, hash )
+ {
+ if( !config ) throw new Error( "config is not defined" );
+ // TODO: Make a trigger for downloading from server
+ var stage = Dand.id( id )
+
+ , applyStructure = function( obj )
+ {
+ // remove loading bubbles
+ while( stage.hasChildNodes() ) stage.removeChild(stage.firstChild);
+
+ /** @type {_AstJson_.SiteFile} */
+ var finfo = JSON.parse( obj ).file;
+
+ switch( finfo.type ) {
+
+ case "image":
+ var node = Dand.wrap('img');
+ var k = new IKey( "src", null );
+
+ switch( IDOMElement(stage).getDAttribute('size') )
+ {
+ case "small":
+ k.keyValue = config.path.image.small + hash + '.jpg';
+ break;
+ case "medium":
+ k.keyValue = config.path.image.medium + hash + '.jpg';
+ break;
+ default: // large
+ k.keyValue = config.path.image.large + hash + '.jpg';
+ }
+ IDOMElement( node ).setAttribute( k );
+
+ stage.appendChild(Dand.wrapne(
+ 'a', node
+ , [
+ new IKey( 'href', config.f_host + finfo.src_location )
+ , new IKey( 'title', finfo.name )
+ , new IKey( 'target', '_blank' )
+ ]
+ ));
+
+ break;
+
+ case "audio":
+ // TODO
+ break;
+
+ default:
+ // create a form to post hash string to php
+ var hash_field = Dand.wrapna(
+ 'input', [ new IKey( 'type', 'hidden' ), new IKey( 'name', 'hash' ) ]
+ )
+ , name_field = Dand.wrapna(
+ 'input', [ new IKey( 'type', 'hidden' ), new IKey( 'name', 'name' ) ]
+ )
+ , link = Dand.wrapne(
+ 'a', 'download', new IKey( 'href', config.path.download + finfo.name )
+ )
+
+ , form = Dand.wrap(
+ 'form', null, 'sf_regular'
+ , [ name_field, hash_field ]
+ , [
+ new IKey('target', '_blank')
+ , new IKey('action', config.path.download + finfo.name)
+ , new IKey('method', 'POST')
+ ]
+ )
+ ;
+
+ hash_field.value = hash;
+ name_field.value = finfo.name;
+
+ IDOMElement(link).addEventListener(
+ 'Click'
+ , function(e) {
+ form.submit();
+ e.preventDefault();
+ return false;
+ }
+ );
+
+ // file name
+ form.appendChild( Dand.wrapne( 'span', 'File: ' + finfo.name) );
+
+ // download, submit button
+ form.appendChild( Dand.wrapne( 'sup', [ Dand.textNode(" ["), link, Dand.textNode("]") ] ) );
+
+ // hash
+ form.appendChild( Dand.wrapne( 'sup', Dand.wrape( 'MD5: ' + hash ) ) );
+ // date
+ form.appendChild( Dand.wrapne( 'sup', Dand.wrape( 'Date: ' + getSMStamp( new Date( finfo.date_created ) ) ) ) );
+
+
+ stage.appendChild(form);
+
+ }
+
+ }
+ , loadFailed = function( obj )
+ {
+ while( stage.hasChildNodes() ) stage.removeChild( stage.firstChild );
+ stage.appendChild( Dand.wrapc( "sf_failed", Dand.textNode( "Error: Failed to get resources info" ) ) );
+ }
+ ;
+
+ getData( config.path.info + hash, applyStructure, loadFailed );
+ };
+
+ var init = function()
+ {
+ config = Conf.get( "SiteFile" );
+ if( config )
+ {
+ for( var i in config.files )
+ {
+ var f = config.files[i];
+ new SiteFile( f[0], f[1] );
+ }
+ }
+ };
+
+ Bootstrap.regInit( init );
+
+ ns[ NS_EXPORT ]( EX_CLASS, "SiteFile", SiteFile );
+})();
diff --git a/botanjs/src/Astro/Blog/Components/SocialButtons.css b/botanjs/src/Astro/Blog/Components/SocialButtons.css
new file mode 100644
index 0000000..b53c89c
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/SocialButtons.css
@@ -0,0 +1,8 @@
+.social_panel {
+ margin: 0.5em 0;
+ text-align: right;
+}
+
+.social_panel > div {
+ vertical-align: top;
+}
diff --git a/botanjs/src/Astro/Blog/Components/SocialButtons.js b/botanjs/src/Astro/Blog/Components/SocialButtons.js
new file mode 100644
index 0000000..6939b45
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/SocialButtons.js
@@ -0,0 +1,40 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.Components.SocialButtons" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {System.utils.IKey} */
+ var IKey = __import( "System.utils.IKey" );
+ /** @type {Astro.Bootstrap} */
+ var Bootstrap = __import( "Astro.Bootstrap" );
+
+ var init = function( id )
+ {
+ var s = Dand.tag( "script" )[0];
+
+ // Facebook
+ var fb = Dand.wrapna(
+ "script"
+ , IKey.quickDef(
+ "type" , 'text/javascript'
+ , "id" , 'facebook-jssdk'
+ , "src" , "//connect.facebook.net/en_US/all.js#xfbml=1"
+ )
+ );
+
+ // Google+
+ var gplus = Dand.wrapna(
+ "script"
+ , IKey.quickDef(
+ "type" , 'text/javascript'
+ , "async" , true
+ , "src" , "https://apis.google.com/js/plusone.js"
+ )
+ );
+
+ s.parentNode.insertBefore( fb, s );
+ s.parentNode.insertBefore( gplus, s );
+ };
+
+ Bootstrap.regInit( init );
+
+})();
diff --git a/botanjs/src/Astro/Blog/Components/Spoiler.css b/botanjs/src/Astro/Blog/Components/Spoiler.css
new file mode 100644
index 0000000..463ff17
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Spoiler.css
@@ -0,0 +1,25 @@
+.spoiler {
+
+}
+
+.spoiler .title {
+ background: cornflowerblue;
+ color: white;
+ padding: 0.2em 0.5em;
+ cursor: default;
+}
+
+.spoiler .title:hover {
+ background: royalblue;
+}
+
+.spoiler .title[clicked] {
+ opacity: 0.5;
+}
+
+.spoiler .content {
+ padding: 0.5em;
+ font-size: 0.9em;
+ line-height: 1.2em;
+ display: none;
+}
diff --git a/botanjs/src/Astro/Blog/Components/Spoiler.js b/botanjs/src/Astro/Blog/Components/Spoiler.js
new file mode 100644
index 0000000..042551c
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Spoiler.js
@@ -0,0 +1,49 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.Components.Spoiler" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Astro.Bootstrap} */
+ var Bootstrap = __import( "Astro.Bootstrap" );
+ /** @type {Astro.Blog.Config} */
+ var Config = __import( "Astro.Blog.Config" );
+
+ var Spoiler = function( id )
+ {
+ var sp_stage = Dand.id( "spoiler_" + id );
+ var sp_display = ( sp_stage.nodeName == "span" ) ? "inline-block" : "block";
+
+ var ostage = IDOMElement( sp_stage );
+ var sp_title = ostage.first( "DIV" );
+ var sp_content = ostage.last( "DIV" );
+
+ sp_title.onclick = function ()
+ {
+ var s_style = sp_content.style;
+
+ if( s_style.display == sp_display )
+ {
+ s_style.display = "none";
+ sp_title.removeAttribute( "clicked" );
+ }
+ else
+ {
+ s_style.display = sp_display;
+ sp_title.setAttribute( "clicked", "" );
+ }
+ };
+
+ sp_stage.hasAttribute( "expanded" ) && sp_title.onclick();
+ };
+
+ var sps = Config.get( "Spoiler" );
+
+ Bootstrap.regInit(function() {
+ for( var i in sps )
+ {
+ new Spoiler( sps[i] );
+ }
+ });
+
+})();
diff --git a/botanjs/src/Astro/Blog/Components/TagControl.css b/botanjs/src/Astro/Blog/Components/TagControl.css
new file mode 100644
index 0000000..4448c27
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/TagControl.css
@@ -0,0 +1,24 @@
+.tag-ctrl .title {
+ font-size: 1.5em;
+ color: #444;
+ clear: both;
+}
+
+.tag-ctrl {
+ white-space-collapse: discard;
+}
+
+.tag-ctrl > div > div {
+ padding: 0.2em 0;
+ float: left;
+}
+
+.tag-ctrl > div > div > a {
+ padding: 0 0.2em;
+}
+
+.tag-ctrl > div > div > a:first-child:hover {
+ color: white;
+ background: purple;
+ text-decoration: none;
+}
diff --git a/botanjs/src/Astro/Blog/Components/TagControl.js b/botanjs/src/Astro/Blog/Components/TagControl.js
new file mode 100644
index 0000000..dd79978
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/TagControl.js
@@ -0,0 +1 @@
+// __namespace( "Astro.Blog.Components.TagControl" );
diff --git a/botanjs/src/Astro/Blog/Components/ToggleButton/CommentToggle.css b/botanjs/src/Astro/Blog/Components/ToggleButton/CommentToggle.css
new file mode 100644
index 0000000..1cfd940
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/ToggleButton/CommentToggle.css
@@ -0,0 +1,42 @@
+.uc_ntoggle {
+ font-family: custom-sans;
+ vertical-align: top;
+ padding: 0.2em 0em;
+}
+.uc_ntoggle:before { content: '..'; }
+.uc_ntoggle:after { content: '!'; }
+
+.cr_ntoggle {
+ font-family: custom-sans;
+
+ padding: 0;
+ margin: 0;
+
+ position: absolute;
+
+ width: 1.5em;
+ height: 1.5em;
+
+ bottom: 0;
+ right: -3em;
+
+ border-radius: 2em 2em 2em 0em;
+ border: 0.5em solid white;
+
+ line-height: 1.5em !important;
+
+ background-color: white;
+ /* box-shadow: 2px 2px 10px -5px black; */
+}
+
+.cr_ntoggle:before, .cr_ntoggle:after {
+ padding: 0;
+ border-radius: 2em;
+}
+
+.cr_ntoggle:before {
+ content: '..';
+ background-color: slategrey;
+}
+
+.cr_ntoggle:after { content: '!'; }
diff --git a/botanjs/src/Astro/Blog/Components/ToggleButton/CommentToggle.js b/botanjs/src/Astro/Blog/Components/ToggleButton/CommentToggle.js
new file mode 100644
index 0000000..4c549a3
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/ToggleButton/CommentToggle.js
@@ -0,0 +1,27 @@
+/*
+(function(){
+ var ns = __namespace( "Astro.Blog.Components.ToggleButton.CommentToggle" );
+
+ new ToggleButton('toggle_follow', nProcessor, {
+ enable: { action: 'enable', tid: 3, cid: 1 },
+ disable: { action: 'disable', tid: 3, cid: 1 }
+ });
+
+ new ToggleButton('uc_ntoggle', nProcessor, {
+ enable: { action: 'enable', tid: 1, cid: 1645 },
+ disable: { action: 'disable', tid: 1, cid: 1645 }
+ });
+
+ new ToggleButton('toggle_publish', '../../../user/ajax-set_article', {
+ enable: { draft: 0, article_id: 1645 },
+ disable: { draft: 1, article_id: 1645 }
+ });
+
+ for(var i in commList) {
+ new ToggleButton('cr_ntoggle_' + commList[i], nProcessor, {
+ enable: { action: 'enable', tid: 2, cid: commList[i] },
+ disable: { action: 'disable', tid: 2, cid: commList[i] }
+ });
+
+})();
+//*/
diff --git a/botanjs/src/Astro/Blog/Components/ToggleButton/_this.css b/botanjs/src/Astro/Blog/Components/ToggleButton/_this.css
new file mode 100644
index 0000000..aeb0d7f
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/ToggleButton/_this.css
@@ -0,0 +1,84 @@
+.btn_toggle {
+ position: relative;
+
+ font-family: serif;
+ font-size: 1em;
+
+ padding: 0.2em 0.5em;
+ margin: 0.5em 0.2em;
+
+ background-color: grey;
+ color: white;
+
+ cursor: default;
+
+ overflow: hidden;
+ display: inline-block;
+}
+
+.btn_toggle > .btn_space {
+ visibility: hidden;
+ padding: 0 0.5em;
+}
+
+.btn_toggle:hover {
+ text-decoration: none;
+}
+
+.btn_toggle:hover:before, .btn_toggle:hover:after {
+ text-decoration: underline;
+}
+
+.btn_toggle:before, .btn_toggle:after {
+
+ padding-top: 0.2em;
+
+ text-align: center;
+
+ position: absolute;
+ font-size: 1.2em;
+
+ width: 100%;
+ height: 100%;
+
+ top: 0;
+ left: 0;
+
+ -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -moz-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -ms-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -o-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); /* easeOutCubic */
+}
+
+.btn_toggle:before {
+ content: '';
+ top: 0%;
+
+ -webkit-transition: all 500ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -moz-transition: all 500ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -ms-transition: all 500ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -o-transition: all 500ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ transition: all 500ms cubic-bezier(0.215, 0.610, 0.355, 1.000); /* easeOutCubic */
+}
+
+.btn_toggle:after {
+ content: '';
+ background-color: royalblue;
+ top: 200%;
+
+ -webkit-transition: all 750ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -moz-transition: all 750ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -ms-transition: all 750ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -o-transition: all 750ms cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ transition: all 500ms cubic-bezier(0.215, 0.610, 0.355, 1.000); /* easeOutCubic */
+}
+
+.btn_toggle[active]:after {
+ top: 0%;
+}
+
+
+.btn_toggle[active]:before {
+ top: -120%;
+}
diff --git a/botanjs/src/Astro/Blog/Components/ToggleButton/_this.js b/botanjs/src/Astro/Blog/Components/ToggleButton/_this.js
new file mode 100644
index 0000000..8e0fe65
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/ToggleButton/_this.js
@@ -0,0 +1,61 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.Components" );
+
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Astro.Bootstrap} */
+ var Bootstrap = __import( "Astro.Bootstrap" );
+ /** @type {Astro.Blog} */
+ var config = __import( "Astro.Blog.Config" );
+
+ var postData = __import( "System.Net.postData" );
+
+ var ToggleButton = function ( elem, processor, obj )
+ {
+ var stage = Dand.id( elem );
+ if( !stage ) return;
+
+
+ var n_toggle = function (args)
+ {
+ if(stage.getAttribute('active'))
+ {
+ stage.removeAttribute('active');
+ }
+ else
+ {
+ stage.setAttribute('active', 1);
+ }
+ };
+
+ var nError = function (args)
+ {
+
+ }
+ ;
+
+ IDOMElement(stage).addEventListener(
+ 'Click'
+ , function (e) {
+ var _action = stage.getAttribute('active') ? obj.disable : obj.enable;
+ postData( processor, _action, n_toggle, nError );
+ }
+ );
+ };
+
+ var init = function()
+ {
+ var toggles = config.get( "toggle_btns" );
+ for( var i in toggles )
+ {
+ new ToggleButton( toggles[ i ][0], toggles[ i ][1], toggles[ i ][2] );
+ }
+
+ };
+
+ Bootstrap.regInit( init );
+
+ ns[ NS_EXPORT ]( EX_CLASS, "ToggleButton", ToggleButton );
+})();
diff --git a/botanjs/src/Astro/Blog/Components/VerticalNote.css b/botanjs/src/Astro/Blog/Components/VerticalNote.css
new file mode 100644
index 0000000..4cdd177
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/VerticalNote.css
@@ -0,0 +1,80 @@
+.vsign {
+ position: relative;
+ overflow-x: scroll;
+ overflow-y: hidden;
+}
+.vertical-text {
+ float: right;
+
+ color: #777;
+
+ padding: 1em 0.5em;
+ margin: 0.5em 1em;
+
+ position: relative;
+}
+
+.vertical-text.block {
+ background-color: #FAFAFA;
+}
+
+.vertical-text > a.more {
+ display: block;
+
+ top: 0;
+ left: 0;
+
+ width: 100%;
+ height: 100%;
+
+ padding: 0;
+
+ position: absolute;
+
+ border: 1px solid #D4D7C9;
+}
+.vertical-text > a.more:hover {
+ border-width: 2px;
+ border-color: purple;
+
+ cursor: pointer;
+}
+
+.vertical-text p
+, .vertical-text a
+, .vertical-text h1 , .vertical-text h2, .vertical-text h3
+, .vertical-text h4, .vertical-text h5, .vertical-text h6 {
+ padding: 0.1em;
+ margin: 0;
+ width: 1em;
+ letter-spacing: 1em;
+
+ word-break: break-all;
+ word-wrap: break-word;
+}
+
+.vertical-text > .block {
+ overflow: hidden;
+ width: 4.5em;
+}
+
+.vertical-text > * {
+ display: inline-block;
+ float: right;
+ height: 390px;
+}
+
+.vertical-text > p {
+ height: 400px;
+}
+
+.vertical-text.source > * {
+ height: auto;
+}
+
+.vertical-text.source {
+ height: 400px;
+ border: 0;
+ padding: 0;
+ visibility: hidden;
+}
diff --git a/botanjs/src/Astro/Blog/Components/VerticalNote.js b/botanjs/src/Astro/Blog/Components/VerticalNote.js
new file mode 100644
index 0000000..c3ad426
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/VerticalNote.js
@@ -0,0 +1,79 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.Components.VerticalNote" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Astro.Bootstrap} */
+ var Bootstrap = __import( "Astro.Bootstrap" );
+ /** @type {Astro.Blog.Config} */
+ var Config = __import( "Astro.Blog.Config" );
+ /** @type {Astro.utils.Date} */
+ var XDate = __import( "Astro.utils.Date" );
+
+ /** @param {Dandelion.IDOMElement} elem */
+ var VerticalNote = function( elem )
+ {
+ var relem = elem.element;
+ var maxHeight = relem.clientHeight;
+
+ // Break for elements that are too long
+ var formatted = Dand.wrapc( relem.className.replace( / *source */, " " ) );
+
+ elem.foreach( 1, function( child )
+ {
+ var ichild = IDOMElement( child );
+ switch( ichild.getDAttribute( "flag" ) )
+ {
+ case "date":
+ child.innerHTML = XDate.chinese( new Date( child.innerHTML ) );
+ break;
+ case "anchor":
+ case "image":
+ formatted.appendChild( child );
+ child.style.display = null;
+ return;
+ }
+
+ var content = child.innerHTML;
+ var len = content.length;
+ var blockh = Math.ceil( child.clientHeight / len );
+
+ var opLen = Math.floor( maxHeight / blockh );
+
+ var i = 0;
+
+ var doBreak = function( i )
+ {
+ var nLine = Dand.wrapne(
+ child.nodeName
+ , content.substr( opLen * i, opLen )
+ , ichild.aKeys()
+ );
+
+ formatted.appendChild( nLine );
+
+ formatted.addEventListener(
+ "click"
+ , function( e ) {
+ window.location = this.loc;
+ }.bind({ loc: elem.getDAttribute( "href" ) })
+ );
+
+ len -= opLen;
+ };
+
+ for( var i = 0; opLen < len; i ++ ) doBreak( i );
+ if( 0 < len ) doBreak( i );
+ } );
+
+ relem.style.display = "none";
+ relem.parentNode.insertBefore( formatted, relem.nextSibling );
+ };
+
+ Bootstrap.regInit(function() {
+ Dand.glass( "vertical-text", true )
+ .forEach( function( e ) { new VerticalNote( e ); } );
+ });
+
+})();
diff --git a/botanjs/src/Astro/Blog/Components/Video.css b/botanjs/src/Astro/Blog/Components/Video.css
new file mode 100644
index 0000000..9bd9641
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Video.css
@@ -0,0 +1,32 @@
+.v_wrapper {
+ width: 640px;
+ height: 390px;
+}
+
+.v_wrapper > div{
+ position: relative;
+ width: 100%;
+ height: 100%;
+ opacity: 0.6;
+ background: black;
+}
+
+.v_wrapper > div:hover {
+ opacity: 0.2;
+}
+
+.v_wrapper > div > div {
+ width: 0;
+ height: 0;
+ border-top: 3.5em solid transparent;
+ border-bottom: 3.5em solid transparent;
+ border-left: 5em solid white;
+
+ position: absolute;
+
+ left: 50%;
+ top: 50%;
+ margin-top: -3.5em;
+ margin-left: -1.8em;
+
+ }
\ No newline at end of file
diff --git a/botanjs/src/Astro/Blog/Components/Video.js b/botanjs/src/Astro/Blog/Components/Video.js
new file mode 100644
index 0000000..fd4530c
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Components/Video.js
@@ -0,0 +1,103 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.Components.Video" );
+
+ /** @type {System.utils.IKey} */
+ var IKey = __import( "System.utils.IKey" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Astro.Bootstrap} */
+ var Bootstrap = __import( "Astro.Bootstrap" );
+ /** @type {Astro.Blog.Config} */
+ var Config = __import( "Astro.Blog.Config" );
+
+ var getData = __import( "System.Net.getData" );
+
+ var v_current = { player: false, mask: false, stage: false, listener: false };
+
+ var init = function()
+ {
+ var conf = Config.get( "Video" );
+ if( conf )
+ {
+ for( var i in conf )
+ {
+ var type = conf[i];
+ var vid = Dand.id( type );
+ if( !vid ) continue;
+
+ type = type.split( "_" )[0];
+
+ // showVimeoPlayer / showYoutubePlayer
+ var cfunc = function( e ) { createPlayer( this.vid, "http://www.youtube.com/embed/" ); };
+
+ if( type[0] == "v" )
+ {
+ getVimeoThumbnail( vid );
+ cfunc = function( e ) { createPlayer( this.vid, "http://player.vimeo.com/video/" ); };
+ }
+
+ vid.onclick = cfunc.bind({ vid: vid });
+ }
+ }
+ };
+
+ var createPlayer = function ( vtag, url )
+ {
+ // Remove previous playing video if exists
+ if( v_current.stage )
+ {
+ v_current.stage.removeChild( v_current.player );
+ v_current.stage.appendChild( v_current.mask );
+ v_current.stage.onclick = v_current.listener;
+ }
+
+ // Remove mask
+ ( v_current.stage = vtag ).removeChild( v_current.mask = vtag.firstChild );
+
+ v_current.player = Dand.wrapna(
+ "iframe"
+ , IKey.quickDef(
+ "src" , url + vtag.getAttribute( "value" ) + "?autoplay=1"
+ , "mozallowfullscreen" , ""
+ , "allowFullScreen" , ""
+ , "webkitAllowFullScreen" , ""
+ , "frameborder" , 0
+ , "width" , vtag.clientWidth
+ , "height" , vtag.clientHeight
+ )
+ );
+ vtag.appendChild(v_current.player);
+
+ // Save event listener
+ v_current.listener = vtag.onclick;
+
+ // disable click event
+ vtag.onclick = null;
+
+ // <iframe src="http://player.vimeo.com/video/VIDEO_ID" width="WIDTH" height="HEIGHT" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
+ // <iframe id="player" type="text/html" width="WIDTH" height="HEIGHT" src="http://www.youtube.com/embed/id" frameborder="0"></iframe>
+ };
+
+ var getVimeoThumbnail = function ( vtag )
+ {
+ getData(
+ "http://vimeo.com/api/oembed.json?url=http%3A//vimeo.com/" + vtag.getAttribute( "value" )
+ , parseObj.bind( vtag )
+ , noThumb.bind( vtag )
+ );
+ };
+
+ var parseObj = function (str)
+ {
+ this.style.background = "black url(" + JSON.parse(str)["thumbnail_url"] + ") center center no-repeat";
+ };
+
+ var noThumb = function ()
+ {
+
+ };
+
+ Bootstrap.regInit( init );
+})();
diff --git a/botanjs/src/Astro/Blog/Config.js b/botanjs/src/Astro/Blog/Config.js
new file mode 100644
index 0000000..9807f22
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Config.js
@@ -0,0 +1,12 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.Config" );
+
+ var conf = window["_AstConf_"] || {};
+
+ var _get = function( name )
+ {
+ return conf[ name ] || null;
+ };
+
+ ns[ NS_EXPORT ]( EX_FUNC, "get", _get );
+})();
diff --git a/botanjs/src/Astro/Blog/Element/Footer.css b/botanjs/src/Astro/Blog/Element/Footer.css
new file mode 100644
index 0000000..1ce3144
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Element/Footer.css
@@ -0,0 +1,12 @@
+footer {
+ background-color: #222;
+ background-image: url(http://file.astropenguin.net/blog/layout-images/lines.png);
+ background-repeat: no-repeat;
+ background-position: right bottom;
+}
+
+#fhorizon {
+ position: absolute;
+ top: -7px;
+}
+
diff --git a/botanjs/src/Astro/Blog/Element/Footer.js b/botanjs/src/Astro/Blog/Element/Footer.js
new file mode 100644
index 0000000..d8f0624
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Element/Footer.js
@@ -0,0 +1,2 @@
+// __namespace( "Astro.Blog.Element.Footer" );
+// __import( "Astro.Common.Element.Footer" );
diff --git a/botanjs/src/Astro/Blog/Element/Header.css b/botanjs/src/Astro/Blog/Element/Header.css
new file mode 100644
index 0000000..fed1b55
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Element/Header.css
@@ -0,0 +1,89 @@
+.home {
+ position: relative;
+ width: 100%;
+ overflow: hidden;
+}
+
+header {
+ position: relative;
+}
+
+#menu {
+ font-size: 1.75em;
+ text-align: right;
+}
+
+#menu > * {
+ font-family: custom-sans;
+ cursor: pointer;
+ line-height: 1em !important;
+
+ position: relative;
+
+ color: rgba( 0, 0, 0, 0.5 );
+ -webkit-filter: blur( 2px );
+ filter: blur( 2px );
+}
+
+#menu > *[active] {
+ -webkit-filter: blur( 0px );
+ filter: blur( 0px );
+ color: rgba( 128, 0, 128, 1 );
+}
+
+#menu > *:hover {
+ color: rgba( 0, 0, 0, 0.75 );
+ -webkit-filter: blur( 0px );
+ filter: blur( 0px );
+ text-decoration: none;
+}
+
+.horizon {
+ position: relative;
+ width: 100%;
+ height: 7px;
+ background: #76400C;
+ z-index: 4;
+}
+
+.contact {
+ width: 100%;
+ height: 0;
+
+ -moz-box-shadow: inset 0 0 10px 0 black;
+ -webkit-box-shadow: inset 0 0 10px 0 black;
+ box-shadow: inset 0 0 10px 0 black;
+
+ color: white;
+ background: lightslategray;
+ position: absolute;
+ overflow: hidden;
+}
+
+.collapse_panel {
+ position: absolute;
+ width: 100%;
+ height: 50px;
+ bottom: -33px;
+ font-size: 3em;
+ text-align: center;
+ color: white;
+
+ -moz-box-shadow: 0 0 10px 0 black;
+ -webkit-box-shadow: 0 0 10px 0 black;
+ box-shadow: 0 0 10px 0 black;
+}
+
+.collapse_panel:after {
+ content: '';
+ position: absolute;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background:url(http://file.astropenguin.net/blog/layout-images/collapse.png) center 2.5px no-repeat;
+}
+
+#collapse_panel:hover {
+ bottom: 0;
+ cursor: pointer;
+}
diff --git a/botanjs/src/Astro/Blog/Element/Header.js b/botanjs/src/Astro/Blog/Element/Header.js
new file mode 100644
index 0000000..022a1f8
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Element/Header.js
@@ -0,0 +1 @@
+// __namespace( "Astro.Blog.Element.Header" );
diff --git a/botanjs/src/Astro/Blog/Element/Layer.css b/botanjs/src/Astro/Blog/Element/Layer.css
new file mode 100644
index 0000000..65669fa
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Element/Layer.css
@@ -0,0 +1,2 @@
+.mbody { z-index: 1; }
+header, footer { z-index: 2; }
diff --git a/botanjs/src/Astro/Blog/Element/Layer.js b/botanjs/src/Astro/Blog/Element/Layer.js
new file mode 100644
index 0000000..49ab51f
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Element/Layer.js
@@ -0,0 +1 @@
+// __namespace( "Astro.Blog.Element.Layer" );
diff --git a/botanjs/src/Astro/Blog/Layout/Article/Control.js b/botanjs/src/Astro/Blog/Layout/Article/Control.js
new file mode 100644
index 0000000..4a37e5d
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Layout/Article/Control.js
@@ -0,0 +1,53 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.Layout.Article.Control" );
+
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Components.MessageBox} */
+ var MessageBox = __import( "Components.MessageBox" );
+ /** @type {Astro.Bootstrap} */
+ var Bootstrap = __import( "Astro.Bootstrap" );
+ /** @type {Astro.Blog.Config} */
+ var config = __import( "Astro.Blog.Config" );
+
+ var postData = __import( "System.Net.postData" );
+
+ var aid = config.get( "ArticleID" );
+
+ /** @type {_AstConf_.Control} */
+ var control = config.get( "Control" );
+
+ var init = function()
+ {
+ var delete_btn = Dand.id( "control_delete", true );
+ if( delete_btn ) delete_btn.addEventListener( "Click", a_delete );
+ };
+
+ var a_delete = function ()
+ {
+ new MessageBox(
+ "Confirm"
+ , "Are you sure you want to delete this article?. Comments will also be deleted!"
+ , "Yes", "No"
+ , a_doDelete
+ ).show();
+ };
+
+ var a_doDelete = function ( confirmed )
+ {
+ var a_deleteSuccess = function ( args )
+ {
+ window.location.reload( true );
+ }
+ , a_deleteFailed = function ( args )
+ {
+
+ };
+
+ if ( confirmed ) postData( control.action.del, { "article_id": aid }, a_deleteSuccess, a_deleteFailed );
+ };
+
+ Bootstrap.regInit( init );
+})();
diff --git a/botanjs/src/Astro/Blog/Layout/Article/Latest.js b/botanjs/src/Astro/Blog/Layout/Article/Latest.js
new file mode 100644
index 0000000..0f9db83
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Layout/Article/Latest.js
@@ -0,0 +1,6 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.Layout.Article.Latest" );
+
+ // __import( "Astro.Blog.Components.Entry.Home" );
+ // __import( "Astro.Blog.Components.Entry.Mega" );
+})();
diff --git a/botanjs/src/Astro/Blog/Layout/Article/_this.js b/botanjs/src/Astro/Blog/Layout/Article/_this.js
new file mode 100644
index 0000000..0a08eea
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Layout/Article/_this.js
@@ -0,0 +1,3 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.Layout.Article" );
+})();
diff --git a/botanjs/src/Astro/Blog/Layout/ErrorPages.css b/botanjs/src/Astro/Blog/Layout/ErrorPages.css
new file mode 100644
index 0000000..376f5ad
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Layout/ErrorPages.css
@@ -0,0 +1,99 @@
+* { margin: 0; padding: 0; }
+
+/* Parallax & charCloud */
+.p_abs100 {
+ top: 0;
+ left: 0;
+ width: 100%;
+ height:100%;
+ position: absolute;
+ overflow: hidden;
+}
+
+.p_charCloud
+{
+ font-family: sans-serif;
+}
+
+/* End Parallax & charCloud */
+#g { width: 100%; height: 100%; position: absolute; top: 0; }
+
+#g, .slideTransitionOverride {
+ -webkit-transition: all 2ms cubic-bezier(0.115, 0.000, 0.355, 1.000);
+ -moz-transition: all 2s cubic-bezier(0.115, 0.000, 0.355, 1.000);
+ -ms-transition: all 2s cubic-bezier(0.115, 0.000, 0.355, 1.000);
+ -o-transition: all 2s cubic-bezier(0.115, 0.000, 0.355, 1.000);
+ transition: all 2s cubic-bezier(0.115, 0.000, 0.355, 1.000); /* custom */
+
+ -webkit-transition-timing-function: cubic-bezier(0.115, 0.000, 0.355, 1.000);
+ -moz-transition-timing-function: cubic-bezier(0.115, 0.000, 0.355, 1.000);
+ -ms-transition-timing-function: cubic-bezier(0.115, 0.000, 0.355, 1.000);
+ -o-transition-timing-function: cubic-bezier(0.115, 0.000, 0.355, 1.000);
+ transition-timing-function: cubic-bezier(0.115, 0.000, 0.355, 1.000); /* custom */
+}
+
+html, body { height: 100%; }
+
+.rooms { width: 100%; height:100%; position: absolute; overflow: hidden; z-index: -1; }
+
+#g > div{
+ width: 100%;
+ height:100%;
+ position: relative;
+}
+
+.c_center {
+ top: 50%;
+ left: 50%;
+ position: absolute;
+
+ z-index: 2;
+}
+
+.code{
+ /* Values depends on the sprite sheet*/
+ margin-left: -100px;
+ margin-top: -45px;
+ width: 200px;
+ height: 90px;
+}
+
+.c401 { background: url(http://file.astropenguin.net/blog/layout-images/errorcodes.png) 0 0; }
+.c403 { background: url(http://file.astropenguin.net/blog/layout-images/errorcodes.png) 200px 0; }
+.c404 { background: url(http://file.astropenguin.net/blog/layout-images/errorcodes.png) 0 90px; }
+
+.b401 { background: cornflowerblue; }
+.b403 { background: crimson; }
+.b404 { background: slategrey; }
+
+.t401 { color: cornflowerblue; }
+.t403 { color: crimson; }
+.t404 { color: slategrey; }
+
+
+.bc {
+ /* Actual ring diameter */
+ width: 315px;
+ height: 315px;
+ border-radius: 315px;
+
+ margin-left: -157.5px;
+ margin-top: -157.5px;
+}
+
+.ringbg {
+ /* ring canvas size */
+ width: 400px;
+ height: 400px;
+
+ margin-left: -200px;
+ margin-top: -200px;
+
+ background: url(http://file.astropenguin.net/blog/layout-images/ring.png) center center no-repeat;
+}
+
+
+h1 {
+ font-family: sans-serif;
+ font-size: 4em;
+}
\ No newline at end of file
diff --git a/botanjs/src/Astro/Blog/Layout/ErrorPages.js b/botanjs/src/Astro/Blog/Layout/ErrorPages.js
new file mode 100644
index 0000000..2f3bcc9
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Layout/ErrorPages.js
@@ -0,0 +1,75 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.Layout.ErrorPages" );
+
+ /** @type {System.Cycle} */
+ var Cycle = __import( "System.Cycle" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Astro.Bootstrap} */
+ var Bootstrap = __import( "Astro.Bootstrap" );
+ /** @type {Astro.Blog} */
+ var config = __import( "Astro.Blog.Config" );
+ /** @type {Astro.Blog.Mechanism.CharacterCloud} */
+ var CharacterCloud = __import( "Astro.Mechanism.CharacterCloud" );
+ /** @type {Astro.Blog.Mechanism.Parallax} */
+ var Parallax = __import( "Astro.Mechanism.Parallax" );
+
+ var init = function ()
+ {
+ var errorcode = config.get( "ErrorCode" );
+ var currentRoom = Dand.id( 'p' + errorcode );
+ var parent = currentRoom.parentNode;
+ var rooms = parent.childNodes;
+ var l = rooms.length;
+ var rm;
+ // far from observer;
+ var cloudRange_1 = { lowerLimit: 0, upperLimit: 250, leftLimit: -10, rightLimit: 110 };
+ var cloudRange_2 = { lowerLimit: 0, upperLimit: 150, leftLimit: -10, rightLimit: 110 };
+ var cloudRange_3 = { lowerLimit: 0, upperLimit: 200, leftLimit: -10, rightLimit: 110 };
+ // close;
+ var cloudRange_4 = { lowerLimit: 0, upperLimit: 100, leftLimit: -10, rightLimit: 110 };
+ var cCloudSymbol;
+
+ switch ( errorcode )
+ {
+ case 401:
+ cCloudSymbol = "!";
+ break;
+ case 403:
+ cCloudSymbol = "\u00D7 Forbidden";
+ break;
+ default:
+ cCloudSymbol = "Page not found";
+ }
+
+ Parallax.cssSlide( Dand.wrap(), 0, cloudRange_1 );
+ Parallax.cssSlide( Dand.wrap(), 0, cloudRange_2 );
+ Parallax.cssSlide( Dand.wrap(), 0, cloudRange_3 );
+ Parallax.cssSlide( Dand.wrap(), 0, cloudRange_4 );
+
+ var parallax_container = Dand.id( 'p' + errorcode );
+
+ parallax_container.appendChild( Parallax.cssSlide( CharacterCloud.create( cCloudSymbol, "p_charCloud t" + errorcode, 20, cloudRange_1 ), 1, cloudRange_1 ) );
+ parallax_container.appendChild( Parallax.cssSlide( CharacterCloud.create( cCloudSymbol, "p_charCloud t" + errorcode, 20, cloudRange_2 ), 1, cloudRange_2 ) );
+ parallax_container.appendChild( Parallax.cssSlide( CharacterCloud.create( cCloudSymbol, "p_charCloud t" + errorcode, 20, cloudRange_3 ), 1, cloudRange_3 ) );
+ parallax_container.appendChild( Parallax.cssSlide( CharacterCloud.create( cCloudSymbol, "p_charCloud t" + errorcode, 20, cloudRange_4 ), 1, cloudRange_4 ) );
+
+ for( var r = 0, i = 0; r < l; r ++ )
+ {
+ rm = rooms[r];
+ if( rm.nodeType == 1 )
+ {
+ if( rm == currentRoom )
+ {
+ parent.style.top = ( -i * 100 ) + "%";
+ // Parallax Scrolling!
+ Cycle.next( function() { Parallax.verticalSlideTo(i) } );
+ break;
+ }
+ i ++;
+ }
+ }
+ };
+
+ Bootstrap.regInit( init );
+})();
diff --git a/botanjs/src/Astro/Blog/Layout/Login.css b/botanjs/src/Astro/Blog/Layout/Login.css
new file mode 100644
index 0000000..aaec0e5
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Layout/Login.css
@@ -0,0 +1,63 @@
+body {
+ background-color: #222;
+ background-image: radial-gradient(
+ circle 150px at -75% -75%
+ , transparent 0%
+ , transparent 94%
+ , rgba(255, 255, 255, 0.6) 94%
+ , rgba(255, 255, 255, 0.6) 99%
+ , transparent
+ )
+ , radial-gradient(
+ circle 150px at 173% 174%
+ , transparent 0%
+ , transparent 94%
+ , rgba(255, 255, 255, 0.6) 94%
+ , rgba(255, 255, 255, 0.6) 99%
+ , transparent
+ )
+ ;
+
+ background-size: 100px 100px;
+
+ width: 100%;
+ height: 100%;
+
+ position: absolute;
+}
+
+form {
+ overflow: hidden;
+}
+
+input {
+ width: 100%;
+ margin-bottom: 0.5em;
+}
+
+.mbox_titlebar, .mbox_body {
+ background: rgba(0, 0, 0, 0.1) !important;
+}
+
+.cstatus {
+ position: relative;
+}
+
+.status {
+ position: absolute;
+
+ right: 0.25em;
+ top: 0.25em;
+
+ width: 0.5em;
+ height: 0.5em;
+
+ background: grey;
+ box-shadow: 0 0 5px 0;
+}
+
+div.msg { text-align: right; }
+sub.good { color: greenyellow; }
+sub.failed { color: #DDD; }
+sub.suspicious { color: orangered; }
+sub.banned { color: red; }
diff --git a/botanjs/src/Astro/Blog/Layout/Login.js b/botanjs/src/Astro/Blog/Layout/Login.js
new file mode 100644
index 0000000..1cf1d8b
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Layout/Login.js
@@ -0,0 +1,75 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.Layout.Login" );
+
+ /** @type {System.utils.IKey} */
+ var IKey = __import( "System.utils.IKey" );
+ /** @type {Components.MessageBox} */
+ var MessageBox = __import( "Components.MessageBox" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Astro.Bootstrap} */
+ var Bootstrap = __import( "Astro.Bootstrap" );
+ /** @type {Astro.Blog.Config} */
+ var config = __import( "Astro.Blog.Config" );
+
+ // __import( "Dandelion.CSSReset" ); CSS_RESERVATION
+ // __import( "Dandelion.CSSAnimations" ); CSS_RESERVATION
+
+ /** @type {_AstConf_.Login} */
+ var conf = config.get( "Login" );
+
+ var sHeat = conf.sHeat || "";
+ var formAction = conf.formAct || "./";
+
+ var field_username = new IKey( "name", "username" );
+ var field_password = new IKey( "name", "password" );
+
+ var init = function ()
+ {
+ var form = Dand.wrapne('form'
+ , [
+ // Basic login and password fields
+ , Dand.wrapc("flsf", "Name:")
+ , Dand.wrapna('input', [ field_username, new IKey("type", "text") ] )
+
+ , Dand.wrapc("flsf", "Password:")
+ , Dand.wrapna('input', [ field_password, new IKey("type", "password") ] )
+
+ , Dand.wrapc('msg', Dand.wrap('sub', null, sHeat.toLowerCase(), sHeat))
+ ]
+ , [
+ // Form attributes
+ new IKey('action', formAction)
+ , new IKey('method', 'POST')
+ ]
+ )
+ ;
+
+
+ var mbox = new MessageBox( "Blog.Astro", form, "Login", false, submitForm.bind( form ) ).show();
+
+ // Handle enter button
+ mbox.onkeyup = function( _e )
+ {
+ if ( submitForm.bind( form )( _e.which == 13 ) )
+ {
+ document.body.removeChild( this );
+ }
+ };
+ };
+
+ var submitForm = function (bool) {
+ if ( bool )
+ {
+ if ( this[ field_username.keyValue ].value != ""
+ && this[ field_password.keyValue ] != "" )
+ {
+ this.submit();
+ return true;
+ }
+ }
+ return false;
+ };
+
+ Bootstrap.regInit( init );
+})();
diff --git a/botanjs/src/Astro/Blog/Layout/MainFrame.css b/botanjs/src/Astro/Blog/Layout/MainFrame.css
new file mode 100644
index 0000000..755839e
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Layout/MainFrame.css
@@ -0,0 +1,108 @@
+html, body { height: 100%; }
+
+html { background-color: #222; }
+
+body {
+ background-color: #F0F0F0;
+ font-family: custom-sans;
+}
+
+pre { font-family: monospace; }
+
+::selection {
+ color: white;
+ background: purple;
+}
+::-moz-selection {
+ color: white;
+ background: purple;
+}
+
+p { padding: 0.5em; }
+
+a {
+ color: #333;
+ text-decoration: none;
+}
+
+a:hover { text-decoration: underline; }
+
+h1, h2, h3, h4, .h1, .h2, .h3, .h4 { font-family: custom-serif; }
+h1, .h1 { font-size: 2.5em; }
+h2, .h2 { font-size: 2em; }
+h3, .h3 { font-size: 1.5em; }
+h4, .h4 { font-size: 1em; }
+h5, .h5 { font-size: 0.8em; }
+h6, .h6 { font-size: 0.5em; }
+
+.clearfix {
+ clear: both;
+ padding: 0 !important;
+ margin: 0 !important;
+
+}
+
+.begin-wrapper {
+ position: relative;
+
+ min-height: 100%;
+ background-color: #EBEAEF;
+
+ overflow: hidden;
+}
+
+.mbody {
+ position: relative;
+ width: 100%;
+}
+
+.main-column {
+ width: 85%;
+ margin: 0 auto;
+}
+
+.right { text-align: right; }
+.panel { padding: 1.2em; }
+
+/* transition properties */
+div#header, div#mbody, div#contact, div#horizon {
+
+ -webkit-transition: all 500ms cubic-bezier(0.770, 0.000, 0.175, 1.000);
+ -moz-transition: all 500ms cubic-bezier(0.770, 0.000, 0.175, 1.000);
+ -ms-transition: all 500ms cubic-bezier(0.770, 0.000, 0.175, 1.000);
+ -o-transition: all 500ms cubic-bezier(0.770, 0.000, 0.175, 1.000);
+ transition: all 500ms cubic-bezier(0.770, 0.000, 0.175, 1.000); /* easeInOutQuart */
+
+ -webkit-transition-timing-function: cubic-bezier(0.770, 0.000, 0.175, 1.000);
+ -moz-transition-timing-function: cubic-bezier(0.770, 0.000, 0.175, 1.000);
+ -ms-transition-timing-function: cubic-bezier(0.770, 0.000, 0.175, 1.000);
+ -o-transition-timing-function: cubic-bezier(0.770, 0.000, 0.175, 1.000);
+ transition-timing-function: cubic-bezier(0.770, 0.000, 0.175, 1.000); /* easeInOutQuart */
+}
+
+@keyframes spin {
+ 0% { transform: rotate( 0deg ); }
+ 25% { transform: rotate( 90deg ); }
+ 50% { transform: rotate( 180deg ); }
+ 75% { transform: rotate( 270deg ); }
+ 100% { transform: rotate( 360deg ); }
+}
+
+@keyframes aspin {
+ 0% { transform: rotate( 0deg ); }
+ 25% { transform: rotate( -90deg ); }
+ 50% { transform: rotate( -180deg ); }
+ 75% { transform: rotate( -270deg ); }
+ 100% { transform: rotate( -360deg ); }
+}
+
+.spin {
+ animation-duration: 60s;
+ animation-timing-function: linear;
+ animation-iteration-count: infinite;
+ animation-name: aspin;
+}
+
+.cw { animation-name: spin; }
+
+/* end transition properties */
diff --git a/botanjs/src/Astro/Blog/Layout/MainFrame.js b/botanjs/src/Astro/Blog/Layout/MainFrame.js
new file mode 100644
index 0000000..d8604ba
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Layout/MainFrame.js
@@ -0,0 +1,256 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.Layout.MainFrame" );
+
+ /** @type {System.Cycle} */
+ var Cycle = __import( "System.Cycle" );
+ /** @type {System.utils.IKey} */
+ var IKey = __import( "System.utils.IKey" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Dandelion.IDOMObject} */
+ var IDOMObject = __import( "Dandelion.IDOMObject" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Dandelion.Window} */
+ var wsupp = __import( "Dandelion.Window" );
+ /** @type {System.Debug} */
+ var debug = __import( "System.Debug" );
+ /** @type {Astro.Bootstrap} */
+ var Bootstrap = __import( "Astro.Bootstrap" );
+ /** @type {Astro.Mechanism.CharacterCloud} */
+ var CharacterCloud = __import( "Astro.Mechanism.CharacterCloud" );
+ /** @type {Astro.Mechanism.Parallax} */
+ var Parallax = __import( "Astro.Mechanism.Parallax" );
+
+ // __import( "Dandelion.CSSReset" ); CSS_RESERVATION
+ // __import( "Dandelion.CSSAnimations" ); CSS_RESERVATION
+ // __import( "Astro.Blog.Element.Layer" ); CSS_RESERVATION
+
+ var main;
+ var header;
+ var main_h;
+
+ // menu and horizon
+ var horizon;
+ var contact;
+ var contaht_page;
+ var collapse_panel;
+ var c_expand = false;
+ var savedPath;
+
+ var init = function ()
+ {
+ // Footer at bottom
+ var padder = Dand.wrap();
+ var begin_wrapper = Dand.id( "begin-wrapper" );
+ var content_wrapper = Dand.id( "mbody" );
+ var footer = Dand.tag( "footer" );
+ var fhorizon = Dand.id( "fhorizon" );
+
+ if( footer.length )
+ {
+ footer = footer[0];
+ var fheight = footer.scrollHeight || footer.offsetHeight;
+ var hheight = fhorizon.scrollHeight || fhorizon.offsetHeight;
+
+ padder.style.height = ( fheight + hheight ) + "px";
+ begin_wrapper.style.marginBottom = "-" + fheight + "px";
+ }
+
+ content_wrapper.appendChild( padder );
+
+
+ IDOMObject( window ).addEventListener( "Resize", responsify );
+
+ initTopButton();
+ navControl();
+ responsify( null, true );
+ initBackgroundParallax();
+ };
+
+ var topButtons = [];
+ var initTopButton = function()
+ {
+ // init params
+ header = Dand.id( "header" );
+ horizon = Dand.id( "horizon" );
+ main = Dand.id( "mbody" );
+
+ contact_page = Dand.id( "contact" );
+ collapse_panel = Dand.id( "collapse_panel" );
+
+ horizon.style.backgroundColor
+ = fhorizon.style.backgroundColor
+ = "purple";
+
+ contact = Dand.id( "menu_cont", true );
+ contact.addEventListener( "Click", expandContact );
+
+ Dand.id( "menu", true ).foreach(
+ 1, function( e )
+ {
+ topButtons.push( e );
+ if( e.getAttribute( "active" ) !== null )
+ {
+ e.setAttribute( "master", true );
+ }
+ }
+ );
+ };
+
+ var navControl = function ()
+ {
+ // Page control
+ /** @type {Dandelion.IDOMElement} */
+ var page_control = Dand.id( "top_control", true );
+ var bottom_control = Dand.id( "bottom_control" );
+
+ if ( page_control && bottom_control )
+ {
+ var appendp = function( elem, self )
+ {
+ var clone = elem.cloneNode( true );
+ if( clone.className == "p_navigation" )
+ {
+ IDOMElement( clone ).reverseChild();
+ }
+ bottom_control.appendChild( clone );
+ return true;
+ };
+
+ page_control.last( document.ELEMENT_NODE, appendp );
+ page_control.first( document.ELEMENT_NODE, appendp );
+ }
+ };
+
+ /** @type {Dandelion.IDOMElement} */
+ var rspd_ratio = 0;
+ var responsify = function( e, override )
+ {
+ // Ratio changes would trigger content
+ var ratio = wsupp.clientWidth / wsupp.clientHeight;
+
+ // Swap only on ration changes from 1 < x OR x < 1
+ if( ( ratio < 1 && 1 < rspd_ratio ) || ( 1 < ratio && rspd_ratio < 1 ) || override )
+ {
+ rspd_ratio = ratio;
+ debug.Info( "Responsive Event: R = " + ratio );
+
+ BotanJS.dispatchEvent( new BotanEvent( "Responsive", { "ratio": ratio } ) );
+ }
+ };
+
+
+ /////// Menu
+ // Contact
+ var expandContact = function ()
+ {
+ // disable click event on the button
+ contact.removeEventListener( "Click", expandContact );
+ // enable click event for collapse
+ collapse_panel.onclick = doCollapse;
+
+ // Expand contact panel
+ contact.style.cursor = "default";
+ contact_page.style.height = "400px";
+ header.style.marginTop = "400px";
+
+ // Save horizon color to collapse panel
+ collapse_panel.style.backgroundColor = horizon.style.backgroundColor;
+
+ horizon.style.backgroundColor = "dimgray";
+
+ for( var i in topButtons ) topButtons[i].removeAttribute( "active" );
+ contact.setAttribute( "active", "" );
+
+ c_expand = true;
+ };
+
+ var doCollapse = function ()
+ {
+ // disable click event for collapse
+ collapse_panel.onclick = null;
+ // enable click event on the button
+ contact.addEventListener( "Click", expandContact );
+
+ // Reverting states
+ contact_page.style.height
+ = contact.style.color
+ = contact.style.cursor
+ = header.style.marginTop
+ = "";
+
+ // Apply saved color to horizon
+ horizon.style.backgroundColor = collapse_panel.style.backgroundColor;
+
+ for( var i in topButtons )
+ {
+ if( topButtons[i].getAttribute( "master" ) )
+ {
+ topButtons[i].setAttribute( "active", "" );
+ }
+ else
+ {
+ topButtons[i].removeAttribute( "active" );
+ }
+ }
+
+ c_expand = false;
+ };
+
+ var initBackgroundParallax = function ()
+ {
+ var bg = Dand.id("main_background");
+ if( !bg )
+ {
+ debug.Info( "Parallax Container is not available" );
+ return;
+ }
+ // far from observer
+ var slide_1 = { lowerLimit: 70, upperLimit: 100 }
+ // bottom cloud, concentrated
+ , slide_2a = { lowerLimit: 0, upperLimit: 200 }
+ , slide_2b = { lowerLimit: 0, upperLimit: 200 }
+ , slide_2c = { lowerLimit: 0, upperLimit: 200 }
+
+ , slide_3 = { lowerLimit: 20, upperLimit: 100 }
+ , slide_4 = { lowerLimit: 70, upperLimit: 100 }
+
+ , cloudRange_1 = { lowerLimit: 0, upperLimit: 100, leftLimit: -10, rightLimit: 110 }
+ , cloudRange_2a = { lowerLimit: 180, upperLimit: 200, leftLimit: -10, rightLimit: 110 }
+ , cloudRange_2b = { lowerLimit: 290, upperLimit: 300, leftLimit: -10, rightLimit: 110 }
+ , cloudRange_2c = { lowerLimit: 390, upperLimit: 400, leftLimit: -10, rightLimit: 110 }
+ , cloudRange_3 = { lowerLimit: 0, upperLimit: 300, leftLimit: -10, rightLimit: 110 }
+ , cloudRange_4 = { lowerLimit: 80, upperLimit: 100, leftLimit: -10, rightLimit: 110 }
+
+ , cCloudSymbol = ["\u25CF", "\u25CF", "\u25CF", "\u25CB", "\u25CB"]
+ , cCloudSymbol2 = ["\u25CC" ]
+
+ , colors = ["FF1D25", "662D91", "3FA9F5", "7AC943", "FF7BAC", "603813", "FF931E"]
+ , i = new Date().getDay()
+
+ , cCloud_1 = CharacterCloud.create( cCloudSymbol2, null, 10, cloudRange_1 )
+ , cCloud_2a = CharacterCloud.create( cCloudSymbol, null, 10, cloudRange_2a )
+ , cCloud_2b = CharacterCloud.create( cCloudSymbol, null, 20, cloudRange_2b, 10 )
+ , cCloud_2c = CharacterCloud.create( cCloudSymbol, null, 40, cloudRange_2c, 5 )
+ , cCloud_3 = CharacterCloud.create( cCloudSymbol2, "spin", 0, cloudRange_3 )
+ , cCloud_4 = CharacterCloud.create( cCloudSymbol2, "spin cw", 0, cloudRange_4 );
+
+ bg.appendChild( Parallax.cssSlide( cCloud_1, 0, slide_1 ) );
+ bg.appendChild( Parallax.cssSlide( cCloud_2a, 1, slide_2a ) );
+ bg.appendChild( Parallax.cssSlide( cCloud_2b, 1, slide_2b ) );
+ bg.appendChild( Parallax.cssSlide( cCloud_2c, 1, slide_2c ) );
+ bg.appendChild( Parallax.cssSlide( cCloud_3, 2, slide_3 ) );
+ bg.appendChild( Parallax.cssSlide( cCloud_4, 3, slide_4 ) );
+
+ IDOMElement( bg ).foreach( 1, function(e)
+ {
+ e.style.color = "#" + colors[i ++];
+ if (colors.length < i) i = 0;
+ });
+
+ Parallax.attach( window, wsupp, 0.2 );
+ };
+
+ Bootstrap.regInit( init );
+})();
diff --git a/botanjs/src/Astro/Blog/Layout/TwoColumn.css b/botanjs/src/Astro/Blog/Layout/TwoColumn.css
new file mode 100644
index 0000000..d8807c9
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Layout/TwoColumn.css
@@ -0,0 +1,102 @@
+.contentWrap {
+ position: relative;
+ width: 100%;
+}
+
+#dockleft {
+ position: absolute;
+ z-index: 0;
+}
+
+.main {
+ /* background-color: #FAFAFA; */
+ vertical-align: top;
+ min-width: 80%;
+
+ position: relative;
+}
+
+/**
+ * Simply defined as:
+ * Text / Text / Text . Text / Text / Tex
+ **/
+ul.breadcrumb > li { display: inline-block; }
+ul.breadcrumb > li:before { content: "/"; }
+ul.breadcrumb > li:first-child:before,
+ul.breadcrumb > li.sep + li:before,
+ul.breadcrumb > li.sep:before { content: " "; }
+ul.breadcrumb > li.sep:after { content: "\2022"; }
+
+.nav_panel {
+ padding-left 0.5em;
+ min-width: 20%;
+}
+
+#dockright {
+ position: relative;
+ /*border-right: #76400C solid 0.2em;*/
+}
+
+#dockright > div {
+ float: right;
+
+ margin: 0 -.3em;
+ padding: 0 0.2em 2em 0.2em;
+ display: inline-block;
+ width: 0;
+}
+
+#extend_sec {
+ min-height: 7px;
+}
+
+#extend_sec, #dockright {
+ /* Same as dockright */
+ width: 100%;
+}
+
+.vpole {
+ /* Should be the same as parent */
+ padding: 0 0.5em;
+ position: relative;
+}
+
+#bottom_control {
+ text-align: right;
+}
+
+.p_navigation {
+ clear: both;
+}
+
+.p_navigation > a, .p_controls > a {
+ color: #333;
+ padding: 0.2em 0.5em;
+}
+
+.p_navigation > a {
+ font-size: 0.8em;
+ font-family: custom-sans;
+}
+
+.p_controls > a {
+ color: #333;
+ font-size: 1.2em;
+}
+
+.ptop {
+ margin-left: -1em !important;
+}
+/*Backgrounds*/
+
+/* This style is necessary
+ * to prevent anchor shifting issue
+ */
+#main_background {
+ position: absolute;
+ overflow: hidden;
+ width: 100%;
+ height: 100%;
+}
+
+/*End_background*/
diff --git a/botanjs/src/Astro/Blog/Layout/TwoColumn.js b/botanjs/src/Astro/Blog/Layout/TwoColumn.js
new file mode 100644
index 0000000..74f64f8
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Layout/TwoColumn.js
@@ -0,0 +1,28 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.Layout.TwoColumn" );
+
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Astro.Bootstrap} */
+ var Bootstrap = __import( "Astro.Bootstrap" );
+
+ var init = function ()
+ {
+ var rspd = Dand.id( "dockright", true );
+ if( rspd )
+ {
+ BotanJS.addEventListener( "Responsive"
+ /** e @type {Astro.Blog.Events.Responsive} */
+ , function( e ) {
+ rspd.foreach(
+ document.ELEMENT_NODE
+ , e.data.ratio < 1
+ ? function( elem ) { elem.style.width = "100%"; }
+ : function( elem ) { elem.style.width = ""; }
+ );
+ });
+ }
+ };
+
+ Bootstrap.regInit( init );
+})();
diff --git a/botanjs/src/Astro/Blog/Layout/_this.js b/botanjs/src/Astro/Blog/Layout/_this.js
new file mode 100644
index 0000000..bd6fa58
--- /dev/null
+++ b/botanjs/src/Astro/Blog/Layout/_this.js
@@ -0,0 +1,3 @@
+(function(){
+ var ns = __namespace( "Astro.Blog.Layout" );
+})();
diff --git a/botanjs/src/Astro/Bootstrap.css b/botanjs/src/Astro/Bootstrap.css
new file mode 100644
index 0000000..1a3e771
--- /dev/null
+++ b/botanjs/src/Astro/Bootstrap.css
@@ -0,0 +1,85 @@
+/*{{{ Global Font Settings */
+@font-face{
+ font-family: custom-sans;
+ src: local("Quicksand Regular"),
+ local("Quicksand-Regular"),
+ url("http://file.astropenguin.net/blog/fonts/Quicksand-Regular/Quicksand-Regular.woff") format("woff"),
+ url("http://file.astropenguin.net/blog/fonts/Quicksand-Regular/Quicksand-Regular.svg") format("svg"),
+ local("Helvetica-Light"),
+ local("Avenir"),
+ local("Verdana"),
+ local("Tahoma"),
+ local("Geneva"),
+ local("sans-serif")
+ ;
+ unicode-range: U+00-FF;
+}
+
+@font-face{
+ font-family: custom-serif;
+ src: url("http://file.astropenguin.net/blog/fonts/DAYROM__/DAYROM__.woff") format("woff"),
+ url("http://file.astropenguin.net/blog/fonts/DAYROM__/DAYROM__.svg") format("svg"),
+ local("Palatino Linotype"),
+ local("Palatino"),
+ local("Georgia"),
+ local("serif");
+ unicode-range: U+00-FF;
+}
+
+.flsf {
+ font-family: custom-sans;
+}
+
+.fls {
+ font-family: custom-serif;
+}
+
+.fsf {
+ font-family: Calibri;
+}
+/* End Global Font Settings }}}*/
+
+/*{{{ Global Element Styles */
+pre {
+ font-family: monospace;
+}
+
+textarea, input, div[contentEditable="true"], select {
+ outline: none;
+}
+
+textarea, input, div[contentEditable="true"] {
+ cursor: auto;
+ border-width: 1px;
+ padding: 0.2em;
+ resize: none;
+ border-style: none;
+ background-color: #EEE;
+}
+
+textarea:focus, input:focus, div.editable:focus {
+ background-color: #FFF;
+}
+
+textarea:hover, input:hover, div.editable:hover {
+ background-color: #FFF;
+}
+
+::-webkit-input-placeholder { /* WebKit browsers */
+ color: #AAA;
+}
+
+:-moz-placeholder { /* Mozilla Firefox 4 to 18 */
+ color: #AAA;
+ opacity: 1;
+}
+
+::-moz-placeholder { /* Mozilla Firefox 19+ */
+ color: #AAA;
+ opacity: 1;
+}
+
+:-ms-input-placeholder { /* Internet Explorer 10+ */
+ color: #AAA !important;
+}
+/* End Global Element Styles }}}*/
diff --git a/botanjs/src/Astro/Bootstrap.js b/botanjs/src/Astro/Bootstrap.js
new file mode 100644
index 0000000..16f5268
--- /dev/null
+++ b/botanjs/src/Astro/Bootstrap.js
@@ -0,0 +1,71 @@
+(function(){
+ var ns = __namespace( "Astro.Bootstrap" );
+ var debug = __import( "System.Debug" );
+
+ // __import( "Dandelion.CSSReset" ); CSS_RESERVATION
+ // __import( "Dandelion.CSSAnimations" ); CSS_RESERVATION
+
+ var infs = [];
+
+ // Init Helper, body onload
+ var regInitFunc = function( f ) { infs[ infs.length ] = f; };
+
+ var inited = false;
+ var consoleExists = false;
+ var SyntaxHighlighterExists = false;
+ var CConsole = "Components.Console";
+
+ var SyntaxHighlighter = "Libraries.SyntaxHighlighter";
+
+ var init = function()
+ {
+ if( inited ) return;
+ inited = true;
+
+ if( consoleExists )
+ {
+ // The + is to prevent resolver resolving this class
+ // since this is optional
+ new ( __import( CConsole ) );
+ }
+
+ var l = infs.length;
+ debug.Info( "[Astro.Blog] init(" + l + ")" );
+ for( var i = 0; i < l; i ++ )
+ {
+ infs[i]();
+ }
+
+ if( SyntaxHighlighterExists )
+ {
+ debug.Info( "[SyntaxHighlighter] Initializing" );
+
+ /** @type {Libraries.SyntaxHighlighter} */
+ var synt = __import( SyntaxHighlighter );
+ synt.highlight();
+ }
+ };
+
+ /*{{{ Class Catching */
+ BotanJS.addEventListener( "NS_EXPORT", function( e )
+ {
+ switch( e.data.name )
+ {
+ case CConsole:
+ consoleExists = true;
+ break;
+ case SyntaxHighlighter:
+ SyntaxHighlighterExists = true;
+ break;
+ }
+ });
+
+ try {
+ __import( CConsole );
+ consoleExists = true;
+ } catch( e ) { };
+ /* End Console Catching }}}*/
+
+ ns[ NS_EXPORT ]( EX_FUNC, "init", init );
+ ns[ NS_EXPORT ]( EX_FUNC, "regInit", regInitFunc )
+})();
diff --git a/botanjs/src/Astro/Build/Components/BuildList.css b/botanjs/src/Astro/Build/Components/BuildList.css
new file mode 100644
index 0000000..8aecb18
--- /dev/null
+++ b/botanjs/src/Astro/Build/Components/BuildList.css
@@ -0,0 +1,24 @@
+.build-list > a {
+ font-family: monospace;
+
+ padding: 0.25em 0.25em;
+ margin: 0.25em 0;
+ display: block;
+}
+
+.build-list > a[data-status^="-"] > .newsha {
+ color: red;
+}
+
+.build-list > a > .newsha {
+ color: green;
+}
+
+.newsha {
+ color: white;
+}
+
+.build-list > a * {
+ padding: 0.25em 0.5em;
+ margin: 0.25em 0;
+}
diff --git a/botanjs/src/Astro/Build/Components/BuildList.js b/botanjs/src/Astro/Build/Components/BuildList.js
new file mode 100644
index 0000000..d085aae
--- /dev/null
+++ b/botanjs/src/Astro/Build/Components/BuildList.js
@@ -0,0 +1 @@
+// __namespace( "Astro.Build.Components.BuildList" );
diff --git a/botanjs/src/Astro/Build/Components/BuildReport.css b/botanjs/src/Astro/Build/Components/BuildReport.css
new file mode 100644
index 0000000..18db73b
--- /dev/null
+++ b/botanjs/src/Astro/Build/Components/BuildReport.css
@@ -0,0 +1,67 @@
+.build-report > pre {
+ padding: 2em;
+
+ border-width: 1px;
+
+ color: #C2C5C9;
+ background-color: #0C121B;
+
+ word-wrap: break-word;
+}
+
+
+.build-report > pre.ok {
+
+}
+
+.build-status {
+ color: #888;
+ padding-left: 2em;
+ margin-bottom: 0.5em;
+}
+.build-status > * .ok { color: green; }
+.build-status > * .failed { color: darkred; }
+
+.build-commit-message {
+ font-family: monospace;
+ padding: 0.2em 0;
+}
+
+#commit-sha { color: #666; }
+#commit-sha.ok { color: royalblue; }
+#commit-sha.failed { color: orangered; }
+
+.section {
+ position: relative;
+
+ margin: 1em -2em;
+ padding: 2em;
+
+ background-color: black;
+ color: #909396;
+}
+
+.section:hover {
+ color: inherit;
+}
+
+.section:before {
+ content: attr(data-name);
+ right: 0.2em;
+ bottom: 0;
+
+ font-family: custom-sans;
+ font-size: 2em;
+
+ color: #C2C5C9;
+
+ position: absolute;
+}
+
+.section:hover:before {
+ color: #909396;
+}
+
+
+.status.failed { color: orangered; }
+.status.ok { color: yellowgreen; }
diff --git a/botanjs/src/Astro/Build/Components/BuildReport.js b/botanjs/src/Astro/Build/Components/BuildReport.js
new file mode 100644
index 0000000..d84a1d9
--- /dev/null
+++ b/botanjs/src/Astro/Build/Components/BuildReport.js
@@ -0,0 +1,55 @@
+(function(){
+ var ns = __namespace( "Astro.Build.Components.BuildReport" );
+
+ /** @type {System.utils.IKey} */
+ var IKey = __import( "System.utils.IKey" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+ /** @type {Astro.Bootstrap} */
+ var Bootstrap = __import( "Astro.Bootstrap" );
+ /** @type {System.Debug} */
+ var debug = __import( "System.Debug" );
+
+ var getData = __import( "System.Net.getData" );
+
+ var init = function()
+ {
+ var commit = Dand.id( "commit-sha", true );
+ var putCommitMessage = function( e )
+ {
+ Dand.id( "commit-message" ).innerHTML = e || "Failed to load message";
+ commit.setAttribute( new IKey( "class", e ? "ok" : "failed" ) )
+ };
+
+ sanitizeReport();
+
+ getData( "/" + commit.getDAttribute( "project" ) + "/commit-message/" + commit.getDAttribute( "sha" ), putCommitMessage, putCommitMessage );
+ };
+
+ var sanitizeReport = function()
+ {
+ var buildMessage = Dand.id( "build-message" );
+ var message = buildMessage.innerHTML.replace( );
+
+ var sRegEx = /[\r\n]*{CI_SECTION_START}[\r\n\s]*(.*)[\r\n]*/g;
+ var eRegEx = /[\r\n]*{CI_SECTION_END}[\r\n]*/g;
+
+ var sCount = ( message.match( sRegEx ) || [] ).length;
+ if( !sCount ) return;
+
+ if( sCount != ( message.match( eRegEx ) || [] ).length )
+ {
+ debug.Error( new Error( "Section Flag count mismatch" ) );
+ return;
+ }
+
+ buildMessage.innerHTML = message
+ .replace( sRegEx, "<pre class=\"section\" data-name=\"$1\">" )
+ .replace( eRegEx, "</pre>" )
+ .replace( /: (failed|ok)\n/g, ": <span class=\"status $1\">$1</span>\n" )
+ ;
+
+ };
+
+ Bootstrap.regInit( init );
+})();
diff --git a/botanjs/src/Astro/Build/Components/ItemList.css b/botanjs/src/Astro/Build/Components/ItemList.css
new file mode 100644
index 0000000..18a9467
--- /dev/null
+++ b/botanjs/src/Astro/Build/Components/ItemList.css
@@ -0,0 +1,59 @@
+div.project-list > a * {
+ -webkit-transition: all .25s;
+ -moz-transition: all .25s;
+ -o-transition: all .25s;
+ transition: all .25s;
+}
+
+div.project-list > a {
+ width: 100%;
+ height: 50px;
+
+ color: #909396;
+ background-color: #48494A;
+
+ font-size: 4em;
+
+ text-align: right;
+
+ display: block;
+ overflow: hidden;
+
+ position: relative;
+
+ margin: 0.2em 0;
+
+ -webkit-transition: all .5s;
+ -moz-transition: all .5s;
+ -o-transition: all .5s;
+ transition: all .5s;
+
+ box-sizing: border-box;
+}
+
+div.project-list > a:after {
+ content: attr(data-desc);
+
+ color: #C2C5C9;
+ font-size: 0.25em;
+ padding: 0.25em 0.5em;
+
+ top: 0;
+
+ display: block;
+
+ position: absolute;
+
+ -webkit-transition: all .5s;
+ -moz-transition: all .5s;
+ -o-transition: all .5s;
+ transition: all .5s;
+}
+
+div.project-list > a:hover {
+ padding-right: 0.25em;
+}
+
+div.project-list > a:hover:after {
+ padding-left: 1em;
+}
diff --git a/botanjs/src/Astro/Build/Components/ItemList.js b/botanjs/src/Astro/Build/Components/ItemList.js
new file mode 100644
index 0000000..0e25899
--- /dev/null
+++ b/botanjs/src/Astro/Build/Components/ItemList.js
@@ -0,0 +1 @@
+// __namespace( "Astro.Build.Components.ItemList" );
diff --git a/botanjs/src/Astro/Build/Element/Footer.css b/botanjs/src/Astro/Build/Element/Footer.css
new file mode 100644
index 0000000..1bd0f35
--- /dev/null
+++ b/botanjs/src/Astro/Build/Element/Footer.css
@@ -0,0 +1,12 @@
+footer:before {
+ content: '';
+
+ width: 100%;
+ height: 0.5em;
+
+ background-color: #3A68B2;
+
+ display: block;
+}
+
+.footer_bg { width: 75%; }
diff --git a/botanjs/src/Astro/Build/Element/Footer.js b/botanjs/src/Astro/Build/Element/Footer.js
new file mode 100644
index 0000000..35a52bc
--- /dev/null
+++ b/botanjs/src/Astro/Build/Element/Footer.js
@@ -0,0 +1,2 @@
+// __namespace( "Astro.Build.Element.Footer" );
+// __import( "Astro.Common.Element.Footer" );
diff --git a/botanjs/src/Astro/Build/Element/Header.css b/botanjs/src/Astro/Build/Element/Header.css
new file mode 100644
index 0000000..37e9b3a
--- /dev/null
+++ b/botanjs/src/Astro/Build/Element/Header.css
@@ -0,0 +1,87 @@
+header {
+ position: relative;
+ border-bottom: 0.5em solid #3A68B2;
+}
+
+header:before {
+ content: '';
+
+ width: 100%;
+
+ bottom: 0;
+
+ display: block;
+ position: absolute;
+}
+
+.left, .right {
+ width: 50%;
+ display: block;
+}
+
+.right {
+ position: absolute;
+ top: 0;
+ right: 0;
+}
+
+nav {
+ width: 75%;
+ margin: 0 auto;
+}
+
+nav div ul {
+ list-style: none;
+ font-size: 1.5em;
+}
+
+nav div ul:after {
+ content: "";
+ clear: both;
+ display: block;
+}
+
+nav div ul ul {
+ top: 100%;
+ left: 0;
+
+ min-width: 5em;
+
+ display: none;
+ position: absolute;
+}
+nav div ul li {
+ padding: 0.25em 0.5em 0 0.25em;
+
+ float: right;
+
+ position: relative;
+}
+
+nav div ul li:hover {
+}
+
+nav div ul li a {
+ color: #C2C5C9;
+}
+
+nav div.right ul { float: right; }
+
+ nav div ul li:hover > ul {
+ display: block;
+ }
+
+svg.banner-deco {
+ float: right;
+ width: 68em;
+ height: 10em;
+}
+
+nav sup {
+ font-size: 50%;
+ vertical-align: top;
+}
+
+.header-padder {
+ clear: both;
+}
diff --git a/botanjs/src/Astro/Build/Element/Header.js b/botanjs/src/Astro/Build/Element/Header.js
new file mode 100644
index 0000000..ff2238d
--- /dev/null
+++ b/botanjs/src/Astro/Build/Element/Header.js
@@ -0,0 +1 @@
+// __namespace( "Astro.Build.Element.Header" );
diff --git a/botanjs/src/Astro/Build/Layout/MainFrame.css b/botanjs/src/Astro/Build/Layout/MainFrame.css
new file mode 100644
index 0000000..89843cc
--- /dev/null
+++ b/botanjs/src/Astro/Build/Layout/MainFrame.css
@@ -0,0 +1,86 @@
+html, body { height: 100%; }
+
+html { background-color: #222; }
+body {
+ font-family: custom-sans;
+}
+
+header, footer { background-color: #0C121B; }
+
+.content-wrapper {
+ position: relative;
+
+ margin: 0 auto;
+ padding: 2em;
+
+ width: 75%;
+
+ background-color: rgba( 255, 255, 255, 0.6 );
+}
+
+.begin-wrapper {
+ position: relative;
+
+ min-height: 100%;
+ color: #0C121B;
+ background-color: white; /*#C2C5C9*/
+
+ overflow: hidden;
+}
+
+.block_deco {
+ position: absolute;
+
+ width: 100%;
+ height: 400px;
+
+ bottom: 1000%;;
+ right: -27px;
+
+ background: url("http://file.astropenguin.net/blog/layout-images/build - blockdeco.png") no-repeat right bottom;
+}
+
+* html .begin-wrapper { height: 100%; }
+
+.working-in-progress {
+ width: 100%;
+ height: 2em;
+
+ color: white;
+ background-color: black;
+ background-image: repeating-linear-gradient(-35deg, transparent, transparent 35px, yellow 35px, yellow 70px);
+
+ position: relative;
+ display: block;
+}
+
+a {
+ color: #3A68B2;/*#48494A;*/
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+h1, h2, h3, h4 { font-family: custom-serif; }
+
+h1 { font-size: 2em; }
+h2 { font-size: 1.5em; }
+h3 { font-size: 1.2em; }
+
+ul.breadcrumb > li {
+ display: inline-block;
+}
+
+ul.breadcrumb > li:first-child:before {
+ content: "";
+ position: absolute;
+}
+
+ul.breadcrumb > li:before {
+ content: ">";
+ color: rgba( 0, 0, 0, 0.2 );
+}
+
+.greyed { color: #C2C5C9; }
diff --git a/botanjs/src/Astro/Build/Layout/MainFrame.js b/botanjs/src/Astro/Build/Layout/MainFrame.js
new file mode 100644
index 0000000..7723092
--- /dev/null
+++ b/botanjs/src/Astro/Build/Layout/MainFrame.js
@@ -0,0 +1,88 @@
+(function(){
+ var ns = __namespace( "Astro.Build.Layout.MainFrame" );
+
+ var Dand = __import( "Dandelion" );
+ /** @type {Astro.Bootstrap} */
+ var Bootstrap = __import( "Astro.Bootstrap" );
+ /** @type {Dandelion.IDOMElement} */
+ var IDOMElement = __import( "Dandelion.IDOMElement" );
+ /** @type {Astro.Mechanism.CharacterCloud} */
+ var CharacterCloud = __import( "Astro.Mechanism.CharacterCloud" );
+ /** @type {Astro.Mechanism.Parallax} */
+ var Parallax = __import( "Astro.Mechanism.Parallax" );
+ /** @type {Dandelion.Window} */
+ var wsupp = __import( "Dandelion.Window" );
+
+ // __import( "Dandelion.CSSReset" ); CSS_RESERVATION
+ // __import( "Dandelion.CSSAnimations" ); CSS_RESERVATION
+
+ var init = function ()
+ {
+ var padder = Dand.id( "padder" );
+ var wrapper = Dand.id( "wrapper" );
+ var footer = Dand.tag( "footer" );
+
+ var fheight = 0;
+ if( footer.length )
+ {
+ footer = footer[0];
+ fheight = ( footer.scrollHeight || footer.offsetHeight );
+ padder.style.height = fheight + "px";
+ wrapper.style.marginBottom = "-" + padder.style.height;
+ }
+
+ block_deco.style.bottom = ( fheight - 72 ) + "px";
+ initBackgroundParallax();
+ };
+
+ var initBackgroundParallax = function ()
+ {
+ var bg = Dand.id("main_background")
+ // far from observer
+ , slide_1 = { lowerLimit: 70, upperLimit: 100 }
+ // bottom cloud, concentrated
+ , slide_2a = { lowerLimit: 0, upperLimit: 200 }
+ , slide_2b = { lowerLimit: 0, upperLimit: 200 }
+ , slide_2c = { lowerLimit: 0, upperLimit: 200 }
+
+ , slide_3 = { lowerLimit: 20, upperLimit: 100 }
+ , slide_4 = { lowerLimit: 70, upperLimit: 100 }
+
+ , cloudRange_1 = { lowerLimit: 0, upperLimit: 100, leftLimit: -10, rightLimit: 110 }
+ , cloudRange_2a = { lowerLimit: 0, upperLimit: 200, leftLimit: -10, rightLimit: 110 }
+ , cloudRange_2b = { lowerLimit: 0, upperLimit: 300, leftLimit: -10, rightLimit: 110 }
+ , cloudRange_2c = { lowerLimit: 0, upperLimit: 400, leftLimit: -10, rightLimit: 110 }
+ , cloudRange_3 = { lowerLimit: 0, upperLimit: 300, leftLimit: -10, rightLimit: 110 }
+ , cloudRange_4 = { lowerLimit: 80, upperLimit: 100, leftLimit: -10, rightLimit: 110 }
+
+ , cCloudSymbol = ["\u25C6", "\u25C7", "\u25C8"]
+ , cCloudSymbol2 = ["\u25C6", "\u25C7"]
+
+ , colors = ["909396", "0C121B", "3A68B2", "48494A"]
+ , i = new Date().getDay()
+
+ , cCloud_1 = CharacterCloud.create( cCloudSymbol2, null, 20, cloudRange_1 )
+ , cCloud_2a = CharacterCloud.create( cCloudSymbol, null, 20, cloudRange_2a )
+ , cCloud_2b = CharacterCloud.create( cCloudSymbol, null, 30, cloudRange_2b, 10 )
+ , cCloud_2c = CharacterCloud.create( cCloudSymbol, null, 80, cloudRange_2c, 5 )
+ , cCloud_3 = CharacterCloud.create( cCloudSymbol2, null, 20, cloudRange_3 )
+ , cCloud_4 = CharacterCloud.create( cCloudSymbol2, null, 20, cloudRange_4 );
+
+ bg.appendChild( Parallax.cssSlide( cCloud_1, 0, slide_1 ) );
+ bg.appendChild( Parallax.cssSlide( cCloud_2a, 1, slide_2a ) );
+ bg.appendChild( Parallax.cssSlide( cCloud_2b, 1, slide_2b ) );
+ bg.appendChild( Parallax.cssSlide( cCloud_2c, 1, slide_2c ) );
+ bg.appendChild( Parallax.cssSlide( cCloud_3, 2, slide_3 ) );
+ bg.appendChild( Parallax.cssSlide( cCloud_4, 3, slide_4 ) );
+
+ IDOMElement( bg ).foreach( 1, function(e)
+ {
+ e.style.color = "#" + colors[i ++];
+ if (colors.length < i) i = 0;
+ });
+
+ Parallax.attach( window, wsupp, 0.3 );
+ };
+
+ Bootstrap.regInit( init );
+})();
diff --git a/botanjs/src/Astro/Build/Page/About.css b/botanjs/src/Astro/Build/Page/About.css
new file mode 100644
index 0000000..06e68f7
--- /dev/null
+++ b/botanjs/src/Astro/Build/Page/About.css
@@ -0,0 +1,11 @@
+ol.numlist {
+ counter-reset: item;
+}
+
+ol.numlist > li:before {
+ display: inline-block;
+ content: counter(item);
+ counter-increment: item;
+ width: 1.2em;
+ text-align: center;
+}
diff --git a/botanjs/src/Astro/Build/Page/About.js b/botanjs/src/Astro/Build/Page/About.js
new file mode 100644
index 0000000..9860667
--- /dev/null
+++ b/botanjs/src/Astro/Build/Page/About.js
@@ -0,0 +1 @@
+// var ns = __namespace( "Astro.Build.Page.About" );
diff --git a/botanjs/src/Astro/Build/Page/Index.css b/botanjs/src/Astro/Build/Page/Index.css
new file mode 100644
index 0000000..a448192
--- /dev/null
+++ b/botanjs/src/Astro/Build/Page/Index.css
@@ -0,0 +1,24 @@
+.site-news { font-family: monospace; }
+.site-news > span { display: block; }
+
+.rbuilds > a { display: block; }
+.rbuilds > a > span {
+ padding: 0 0.5em;
+}
+
+.bstatus {
+ color: red;
+ margin: 0 0.2em;
+}
+
+.bstatus[data-ok=true] {
+ color: green;
+}
+
+.bstatus:before {
+ content: "Failed";
+}
+
+.bstatus[data-ok=true]:before {
+ content: "OK";
+}
diff --git a/botanjs/src/Astro/Build/Page/Index.js b/botanjs/src/Astro/Build/Page/Index.js
new file mode 100644
index 0000000..54f67fd
--- /dev/null
+++ b/botanjs/src/Astro/Build/Page/Index.js
@@ -0,0 +1 @@
+// var ns = __namespace( "Astro.Build.Page.Index" );
diff --git a/botanjs/src/Astro/Build/Page/_this.css b/botanjs/src/Astro/Build/Page/_this.css
new file mode 100644
index 0000000..412192b
--- /dev/null
+++ b/botanjs/src/Astro/Build/Page/_this.css
@@ -0,0 +1,5 @@
+.panel {
+ padding: 0.5em;
+ display: block;
+}
+
diff --git a/botanjs/src/Astro/Common/Element/Footer.css b/botanjs/src/Astro/Common/Element/Footer.css
new file mode 100644
index 0000000..cd0b895
--- /dev/null
+++ b/botanjs/src/Astro/Common/Element/Footer.css
@@ -0,0 +1,36 @@
+footer {
+ width: 100%;
+ position: relative;
+}
+
+.footer_bg {
+ margin: 0 auto;
+ height: 100%;
+}
+
+.flink_section {
+ font-family: custom-sans;
+ float: left;
+ padding: 0.5em;
+}
+
+.flink_section > span {
+ font-size: 1.5em;
+ color: white;
+}
+
+.flink_section > a {
+ font-size: 1em;
+ color: #999;
+ display: block;
+}
+
+.copysign {
+ clear: both;
+ color: white;
+ text-align: right;
+}
+
+.copysign > a {
+ color: #BBB;
+}
diff --git a/botanjs/src/Astro/Common/Element/Footer.js b/botanjs/src/Astro/Common/Element/Footer.js
new file mode 100644
index 0000000..2cf2aaa
--- /dev/null
+++ b/botanjs/src/Astro/Common/Element/Footer.js
@@ -0,0 +1 @@
+// __namespace( "Astro.Common.Element.Footer" );
diff --git a/botanjs/src/Astro/Mechanism/CharacterCloud.css b/botanjs/src/Astro/Mechanism/CharacterCloud.css
new file mode 100644
index 0000000..4e11707
--- /dev/null
+++ b/botanjs/src/Astro/Mechanism/CharacterCloud.css
@@ -0,0 +1,7 @@
+.characterCloud {
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ position: absolute;
+}
\ No newline at end of file
diff --git a/botanjs/src/Astro/Mechanism/CharacterCloud.js b/botanjs/src/Astro/Mechanism/CharacterCloud.js
new file mode 100644
index 0000000..df2dccf
--- /dev/null
+++ b/botanjs/src/Astro/Mechanism/CharacterCloud.js
@@ -0,0 +1,77 @@
+(function(){
+ var ns = __namespace( "Astro.Mechanism.CharacterCloud" );
+
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+
+ var CharacterCloud = {};
+
+ // Character cloud creates a cloud of character with randomized properties
+ var create = function( ch, char_class, size, cloudRange, charSize )
+ {
+ var cloudMap = Dand.wrapc( "characterCloud" )
+ , charElmt
+ , rx, ry, rs
+ , cs = charSize || 15
+ , l_l = cloudRange.lowerLimit
+ // vertical range
+ , vr = cloudRange.upperLimit - l_l
+ , lf_l = cloudRange.leftLimit
+ // horizontal range
+ , hr = cloudRange.rightLimit
+
+ , charStack = ( ch instanceof Array );
+ ;
+
+
+ // The loop is CPU intensive
+ // we need to focus on reducing the CPU usage
+ if( charStack )
+ {
+ var l = ch.length;
+ for(var i = 0; i < size; i ++)
+ {
+ rx = String( lf_l + Math.random()*hr ) + "%";
+ ry = String( l_l + Math.random()*vr ) + "%";
+ rs = Math.random()*cs;
+ charElmt = Dand.wrap('span', null, char_class, ch[Math.floor(Math.random()*l)]);
+
+ var cE_style = charElmt.style;
+
+ cE_style.position = "absolute";
+ cE_style.left = rx;
+ cE_style.top = ry;
+ // cE_style.opacity = Math.random() + "";
+ cE_style.fontSize = rs + "em";
+ cE_style.marginTop = marginLeft = String( -0.5*rs ) + "em";
+
+ cloudMap.appendChild(charElmt);
+ }
+ }
+ else
+ {
+ for(var i = 0; i < size; i ++)
+ {
+ rx = String(lf_l + Math.random()*hr) + "%";
+ ry = String(l_l + Math.random()*vr) + "%";
+ rs = Math.random()*15;
+ charElmt = Dand.wrap('span', null, char_class, ch);
+
+ var cE_style = charElmt.style;
+
+ cE_style.position = "absolute";
+ cE_style.left = rx;
+ cE_style.top = ry;
+ // cE_style.opacity = String(Math.random());
+ cE_style.fontSize = String(rs) + "em";
+ cE_style.marginTop = marginLeft = String( -0.5*rs ) + "em";
+
+ cloudMap.appendChild( charElmt );
+ }
+ }
+
+ return cloudMap;
+ };
+
+ ns[ NS_EXPORT ]( EX_FUNC, "create", create );
+})();
diff --git a/botanjs/src/Astro/Mechanism/Parallax.css b/botanjs/src/Astro/Mechanism/Parallax.css
new file mode 100644
index 0000000..103e784
--- /dev/null
+++ b/botanjs/src/Astro/Mechanism/Parallax.css
@@ -0,0 +1,6 @@
+.parallaxCSSSlideLayer {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ top: 0;
+}
\ No newline at end of file
diff --git a/botanjs/src/Astro/Mechanism/Parallax.js b/botanjs/src/Astro/Mechanism/Parallax.js
new file mode 100644
index 0000000..a3c414b
--- /dev/null
+++ b/botanjs/src/Astro/Mechanism/Parallax.js
@@ -0,0 +1,89 @@
+(function(){
+ var ns = __namespace( "Astro.Mechanism.Parallax" );
+
+ /** @type {System.Cycle} */
+ var Cycle = __import( "System.Cycle" );
+ /** @type {Dandelion} */
+ var Dand = __import( "Dandelion" );
+
+ var Parallax = {
+ totalCSSSlide: 0
+ };
+
+ var cssSlide = function( element, slide_index, distance )
+ {
+ // The concept is drawn in physical book, will make a blog post for this:)
+ var name, s;
+ if( !( s = Parallax[ name = "s" + slide_index ] ) )
+ {
+ s = Parallax[name] = [];
+ Parallax.totalCSSSlide ++;
+ }
+ return (
+ s[ s.length ] = {
+ element: Dand.wrapc( "parallaxCSSSlideLayer slideTransitionOverride", element )
+ // vertical range
+ , verticalRange: distance.upperLimit - distance.lowerLimit - 100
+ // horizontal range
+ , horizontalRange: distance.rightLimit - distance.leftLimit - 100
+ }
+ ).element
+ ;
+ };
+
+ var verticalSlideTo = function( slide_index )
+ {
+ var s, l = Parallax.totalCSSSlide;
+ for( var i = 0; i < l; i ++ )
+ {
+ if(i <= slide_index)
+ {
+ s = Parallax["s" + i];
+ for( var j = 0, k; j < s.length; j ++ )
+ {
+ k = s[j];
+ k.element.style.top = String(-k.verticalRange*((slide_index + 1))) + "%";
+ }
+ }
+ else break;
+ }
+ };
+
+ var attach = function ( eDispatcher, sSupplier, opacityModifier )
+ {
+ var opacityModifier = ( opacityModifier == undefined ) ? 1 : opacityModifier + 0;
+ var s, l = Parallax.totalCSSSlide;
+
+ for( var i = 0; i < l; i ++ )
+ {
+ s = Parallax["s" + i];
+ for( var j = 0, k; j < s.length; j ++ )
+ {
+ k = s[j];
+ k.element.style.opacity = ( ( 0.35 + 0.55 * i / l ) * opacityModifier ) + "";
+ }
+ }
+
+ Cycle.perma(
+ "PARALLAXSCR"
+ , function()
+ {
+ var p = sSupplier.scrollTop/(sSupplier.scrollHeight - sSupplier.clientHeight);
+ for(var i = 0; i < l; i ++)
+ {
+ s = Parallax["s" + i];
+ for(var j = 0, k; j < s.length; j ++)
+ {
+ k = s[j];
+ k.element.style.top = String(-k.verticalRange*((j + 1)*p)) + "%";
+ }
+ }
+ }
+ , 15
+ );
+ };
+
+ ns[ NS_EXPORT ]( EX_FUNC, "cssSlide", cssSlide );
+ ns[ NS_EXPORT ]( EX_FUNC, "verticalSlideTo", verticalSlideTo );
+ ns[ NS_EXPORT ]( EX_FUNC, "attach", attach );
+})();
diff --git a/botanjs/src/Astro/utils/Date.js b/botanjs/src/Astro/utils/Date.js
new file mode 100644
index 0000000..1a82218
--- /dev/null
+++ b/botanjs/src/Astro/utils/Date.js
@@ -0,0 +1,140 @@
+(function(){
+ var ns = __namespace( "Astro.utils.Date" );
+
+ //// Static utilities
+ // Chinese char for 0-9
+ var cChar = [ "12295", "19968", "20108", "19977", "22235", "20116", "20845", "19971", "20843", "20061", "21313" ];
+ // Capital month
+ var CAP_MONTHS = [ false, false, false, true, false, true, false, false, true, false, true, false ];
+ var MONTH_ABBR = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
+ var MONTH = [
+ "January"
+ , "February"
+ , "March"
+ , "April"
+ , "May"
+ , "June"
+ , "July"
+ , "August"
+ , "September"
+ , "October"
+ , "November"
+ , "December"
+ ];
+ var DAY_ABBR = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
+ var DAY = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
+
+ // get responding chinese char from number
+ var cCountDay = function (num)
+ {
+ str = num.toString();
+ if( num != 0 && num % 10 == 0 )
+ {
+ return ( num == 10 ? "" : String.fromCharCode( cChar[ str[0] ] ) )
+ + String.fromCharCode( cChar[10] );
+ }
+ else if( num < 10 )
+ {
+ return String.fromCharCode( cChar[ str[0] ] );
+ }
+ else if( num < 20 )
+ {
+ return String.fromCharCode( cChar[10] )
+ + String.fromCharCode( cChar[ str[1] ] );
+ }
+
+ return String.fromCharCode( cChar[ str[0] ] )
+ + String.fromCharCode( cChar[10] )
+ + String.fromCharCode( cChar[ str[1] ] );
+ };
+
+
+ // Input yyyy-mm-dd small small
+ // output dd mmmm yyyy DDDDD
+ var prettyDay = function ( b, sMon, sDay )
+ {
+ var c = b.split("-");
+ var d = new Date( c[0], c[1] - 1, c[2] );
+
+ return d.getDate() + " "
+ + ( sMon ? MONTH_ABBR[ d.getMonth() ] : MONTH[ d.getMonth() ] ) + " "
+ + d.getFullYear() + " "
+ + ( sDay ? DAY_ABBR[ d.getDay() ] : DAY[ d.getDay() ] )
+ ;
+ };
+
+ // Small month stamp: output dd-mmm-yyyy
+ var smstamp = function ( dateObj )
+ {
+ return dateObj.getDate() + "-" + MONTH_ABBR[ dateObj.getMonth() ] + "-" + dateObj.getFullYear();
+ };
+
+ // Pretty format XXth MMMM, YYYY
+ var pretty = function( dateObj, plain )
+ {
+ return String( dateObj.getDate() )
+ + ( plain ? "" : "<sup>" )
+ + getOrdinalSuffix( dateObj.getDate() )
+ + ( plain ? " " : "</sup> " )
+ + MONTH[ dateObj.getMonth() ]
+ + ", "
+ + String( dateObj.getFullYear() )
+ ;
+ };
+
+ // returns a timespan
+ // Input Array [ yyyy, mm, dd ], [ yyyy, mm, dd ]
+ var diff = function( from, to )
+ {
+ return new Date(
+ new Date( to[0], to[1], to[2] ).getTime()
+ - new Date( from[0], from[1], from[2] ).getTime()
+ );
+ };
+
+ // Get a chinese date format
+ var chinese = function ( date )
+ {
+ var str = date.getFullYear().toString();
+ var datestmp = "";
+
+ for(i in str)
+ {
+ datestmp += String.fromCharCode( cChar[ str[i] ] );
+ }
+
+ datestmp += String.fromCharCode("24180");
+
+ datestmp += cCountDay(date.getMonth() + 1)
+ + String.fromCharCode("26376")
+ + cCountDay(date.getDate())
+ + String.fromCharCode("26085");
+
+ return datestmp;
+ };
+
+ // returns a suffix string
+ // st, nd, rd, th
+ var getOrdinalSuffix = function(day)
+ {
+ if (day < 4 || ( 20 < day && day < 24 )) {
+ if (day == 1 || day == 21) return "st";
+ else if (day == 2 || day == 22) return "nd";
+ else if (day == 3 || day == 23) return "rd";
+ }
+ else if (day == 31) return "st";
+ return "th";
+ };
+
+ ns[ NS_EXPORT ]( EX_FUNC, "pretty", pretty );
+ ns[ NS_EXPORT ]( EX_FUNC, "prettyDay", prettyDay );
+ ns[ NS_EXPORT ]( EX_FUNC, "diff", diff );
+ ns[ NS_EXPORT ]( EX_FUNC, "smstamp", smstamp );
+ ns[ NS_EXPORT ]( EX_FUNC, "chinese", chinese );
+
+ ns[ NS_EXPORT ]( EX_CONST, "CAP_MONTHS", CAP_MONTHS );
+ ns[ NS_EXPORT ]( EX_CONST, "MONTH", MONTH );
+ ns[ NS_EXPORT ]( EX_CONST, "MONTH_ABBR", MONTH_ABBR );
+ ns[ NS_EXPORT ]( EX_CONST, "DAY", DAY );
+ ns[ NS_EXPORT ]( EX_CONST, "DAY_ABBR", DAY_ABBR );
+})();
diff --git a/botanjs/src/Astro/utils/_this.js b/botanjs/src/Astro/utils/_this.js
new file mode 100644
index 0000000..c17dfdb
--- /dev/null
+++ b/botanjs/src/Astro/utils/_this.js
@@ -0,0 +1,3 @@
+(function(){
+ var ns = __namespace( "Astro.utils" );
+})();
diff --git a/botanjs/src/externs/Astro.Blog.AstroEdit.Article.js b/botanjs/src/externs/Astro.Blog.AstroEdit.Article.js
new file mode 100644
index 0000000..f4c3720
--- /dev/null
+++ b/botanjs/src/externs/Astro.Blog.AstroEdit.Article.js
@@ -0,0 +1,18 @@
+/** @constructor */
+Astro.Blog.AstroEdit.Article = function(){};
+/** @type {Function} */
+Astro.Blog.AstroEdit.Article.saveOrBackup;
+/** @type {Function} */
+Astro.Blog.AstroEdit.Article.load;
+/** @type {Function} */
+Astro.Blog.AstroEdit.Article.getArticleId;
+/** @type {Function} */
+Astro.Blog.AstroEdit.Article.preview;
+/** @type {Function} */
+Astro.Blog.AstroEdit.Article.invoke;
+/** @type {Function} */
+Astro.Blog.AstroEdit.Article.saveAndPublish;
+/** @type {Function} */
+Astro.Blog.AstroEdit.Article.drop;
+/** @type {Function} */
+Astro.Blog.AstroEdit.Article.updateContent;
diff --git a/botanjs/src/externs/Astro.Blog.AstroEdit.Tag.js b/botanjs/src/externs/Astro.Blog.AstroEdit.Tag.js
new file mode 100644
index 0000000..a3c6b92
--- /dev/null
+++ b/botanjs/src/externs/Astro.Blog.AstroEdit.Tag.js
@@ -0,0 +1,6 @@
+/** @constructor */
+Astro.Blog.AstroEdit.Tag = function(){};
+/** @type {Function} */
+Astro.Blog.AstroEdit.Tag.getTags;
+/** @type {Function} */
+Astro.Blog.AstroEdit.Tag.setTags;
diff --git a/botanjs/src/externs/Astro.Blog.AstroEdit.Visualizer.Snippet.Model.js b/botanjs/src/externs/Astro.Blog.AstroEdit.Visualizer.Snippet.Model.js
new file mode 100644
index 0000000..63e6b2d
--- /dev/null
+++ b/botanjs/src/externs/Astro.Blog.AstroEdit.Visualizer.Snippet.Model.js
@@ -0,0 +1,6 @@
+/** @constructor */
+Astro.Blog.AstroEdit.Visualizer.Snippet.Model = function(){};
+/** @type {Function} */
+Astro.Blog.AstroEdit.Visualizer.Snippet.Model.compile;
+/** @type {Object} */
+Astro.Blog.AstroEdit.Visualizer.Snippet.Model.alias;
diff --git a/botanjs/src/externs/Astro.Blog.AstroEdit.Visualizer.Snippet.js b/botanjs/src/externs/Astro.Blog.AstroEdit.Visualizer.Snippet.js
new file mode 100644
index 0000000..f8b9308
--- /dev/null
+++ b/botanjs/src/externs/Astro.Blog.AstroEdit.Visualizer.Snippet.js
@@ -0,0 +1,6 @@
+/** @constructor */
+Astro.Blog.AstroEdit.Visualizer.Snippet = function(){};
+/** @type {Function} */
+Astro.Blog.AstroEdit.Visualizer.Snippet.escapeStr;
+/** @type {Function} */
+Astro.Blog.AstroEdit.Visualizer.Snippet.compileProp;
diff --git a/botanjs/src/externs/Astro.Blog.AstroEdit.Visualizer.js b/botanjs/src/externs/Astro.Blog.AstroEdit.Visualizer.js
new file mode 100644
index 0000000..4cf2549
--- /dev/null
+++ b/botanjs/src/externs/Astro.Blog.AstroEdit.Visualizer.js
@@ -0,0 +1,12 @@
+/** @constructor */
+Astro.Blog.AstroEdit.Visualizer = function(){};
+/** @type {Function} */
+Astro.Blog.AstroEdit.Visualizer.setContentDiv;
+/** @type {Function} */
+Astro.Blog.AstroEdit.Visualizer.saveRaw;
+/** @type {Function} */
+Astro.Blog.AstroEdit.Visualizer.visualizeData;
+/** @type {Function} */
+Astro.Blog.AstroEdit.Visualizer.saveSelection;
+/** @type {Function} */
+Astro.Blog.AstroEdit.Visualizer.restoreSelection;
diff --git a/botanjs/src/externs/Astro.Blog.AstroEdit.js b/botanjs/src/externs/Astro.Blog.AstroEdit.js
new file mode 100644
index 0000000..9ce5259
--- /dev/null
+++ b/botanjs/src/externs/Astro.Blog.AstroEdit.js
@@ -0,0 +1,2 @@
+/** @type {Object} */
+Astro.Blog.AstroEdit = {};
diff --git a/botanjs/src/externs/Astro.Blog.Components.Bubble.js b/botanjs/src/externs/Astro.Blog.Components.Bubble.js
new file mode 100644
index 0000000..9e401d3
--- /dev/null
+++ b/botanjs/src/externs/Astro.Blog.Components.Bubble.js
@@ -0,0 +1,18 @@
+/** @constructor */
+Astro.Blog.Components.Bubble = function() {};
+
+/** @type {Function} */
+Astro.Blog.Components.Bubble.prototype.init;
+
+/** @type {Function} */
+Astro.Blog.Components.Bubble.prototype.blurp;
+
+/** @type {Function}
+ * @param {String} mesg
+ */
+Astro.Blog.Components.Bubble.prototype.pop = function( mesg ) { };
+
+/** @type {Function}
+ * @param {String} color
+ */
+Astro.Blog.Components.Bubble.prototype.setColor = function( color ) { };
diff --git a/botanjs/src/externs/Astro.Blog.Components.Calendar.js b/botanjs/src/externs/Astro.Blog.Components.Calendar.js
new file mode 100644
index 0000000..40132fa
--- /dev/null
+++ b/botanjs/src/externs/Astro.Blog.Components.Calendar.js
@@ -0,0 +1,13 @@
+/** @constructor */
+Astro.Blog.Components.Calendar = function() {};
+
+/** @type {Function}*/
+Astro.Blog.Components.Calendar.init;
+/** @type {Function}*/
+Astro.Blog.Components.Calendar.prevMon;
+/** @type {Function}*/
+Astro.Blog.Components.Calendar.nextMon;
+/** @type {Function}*/
+Astro.Blog.Components.Calendar.pop;
+/** @type {Function}*/
+Astro.Blog.Components.Calendar.draw;
diff --git a/botanjs/src/externs/Astro.Blog.Components.js b/botanjs/src/externs/Astro.Blog.Components.js
new file mode 100644
index 0000000..9103a42
--- /dev/null
+++ b/botanjs/src/externs/Astro.Blog.Components.js
@@ -0,0 +1,2 @@
+/** @constructor */
+Astro.Blog.Components = function() {};
diff --git a/botanjs/src/externs/Astro.Blog.Config.js b/botanjs/src/externs/Astro.Blog.Config.js
new file mode 100644
index 0000000..d177e9c
--- /dev/null
+++ b/botanjs/src/externs/Astro.Blog.Config.js
@@ -0,0 +1,5 @@
+/** @constructor */
+Astro.Blog.Config = function() {};
+
+/** @type {Function} */
+Astro.Blog.Config.get;
diff --git a/botanjs/src/externs/Astro.Blog.Events.Responsive.js b/botanjs/src/externs/Astro.Blog.Events.Responsive.js
new file mode 100644
index 0000000..57eb4e9
--- /dev/null
+++ b/botanjs/src/externs/Astro.Blog.Events.Responsive.js
@@ -0,0 +1,8 @@
+/** @type {Object} */
+Astro.Blog.Events.Responsive = {};
+/** @type {Object} */
+Astro.Blog.Events.Responsive.data = {};
+/** @type {Number} */
+Astro.Blog.Events.Responsive.data.ratio = {};
+
+
diff --git a/botanjs/src/externs/Astro.Blog.Events.js b/botanjs/src/externs/Astro.Blog.Events.js
new file mode 100644
index 0000000..8f6bdd7
--- /dev/null
+++ b/botanjs/src/externs/Astro.Blog.Events.js
@@ -0,0 +1,2 @@
+/** @type {Object} */
+Astro.Blog.Events = {};
diff --git a/botanjs/src/externs/Astro.Blog.js b/botanjs/src/externs/Astro.Blog.js
new file mode 100644
index 0000000..c54f099
--- /dev/null
+++ b/botanjs/src/externs/Astro.Blog.js
@@ -0,0 +1,2 @@
+/** @constructor */
+Astro.Blog = function() {};
diff --git a/botanjs/src/externs/Astro.Bootstrap.js b/botanjs/src/externs/Astro.Bootstrap.js
new file mode 100644
index 0000000..2be6d8e
--- /dev/null
+++ b/botanjs/src/externs/Astro.Bootstrap.js
@@ -0,0 +1,8 @@
+/** @constructor */
+Astro.Bootstrap = function() {};
+
+/** @type {Function} */
+Astro.Bootstrap.init;
+
+/** @type {Function} */
+Astro.Bootstrap.regInit;
diff --git a/botanjs/src/externs/Astro.Mechanism.CharacterCloud.js b/botanjs/src/externs/Astro.Mechanism.CharacterCloud.js
new file mode 100644
index 0000000..bc81c32
--- /dev/null
+++ b/botanjs/src/externs/Astro.Mechanism.CharacterCloud.js
@@ -0,0 +1,5 @@
+/** @constructor */
+Astro.Mechanism.CharacterCloud = function() {};
+
+/** @type {Function} */
+Astro.Mechanism.CharacterCloud.create;
diff --git a/botanjs/src/externs/Astro.Mechanism.Parallax.js b/botanjs/src/externs/Astro.Mechanism.Parallax.js
new file mode 100644
index 0000000..719bd2e
--- /dev/null
+++ b/botanjs/src/externs/Astro.Mechanism.Parallax.js
@@ -0,0 +1,9 @@
+/** @constructor */
+Astro.Mechanism.Parallax = function() {};
+
+/** @type {Function} */
+Astro.Mechanism.Parallax.cssSlide;
+/** @type {Function} */
+Astro.Mechanism.Parallax.verticalSlideTo;
+/** @type {Function} */
+Astro.Mechanism.Parallax.attach;
diff --git a/botanjs/src/externs/Astro.Mechanism.js b/botanjs/src/externs/Astro.Mechanism.js
new file mode 100644
index 0000000..da46090
--- /dev/null
+++ b/botanjs/src/externs/Astro.Mechanism.js
@@ -0,0 +1,2 @@
+/** @constructor */
+Astro.Mechanism = function() {};
diff --git a/botanjs/src/externs/Astro.js b/botanjs/src/externs/Astro.js
new file mode 100644
index 0000000..609cac1
--- /dev/null
+++ b/botanjs/src/externs/Astro.js
@@ -0,0 +1,2 @@
+/** @constructor */
+var Astro = function() {};
diff --git a/botanjs/src/externs/Astro.utils.Date.js b/botanjs/src/externs/Astro.utils.Date.js
new file mode 100644
index 0000000..d753b1e
--- /dev/null
+++ b/botanjs/src/externs/Astro.utils.Date.js
@@ -0,0 +1,24 @@
+/** @constructor */
+Astro.utils.Date = function() {};
+
+/** @type {Function}*/
+Astro.utils.Date.pretty;
+/** @type {Function}*/
+Astro.utils.Date.prettyDay;
+/** @type {Function}*/
+Astro.utils.Date.diff;
+/** @type {Function}*/
+Astro.utils.Date.smstamp;
+/** @type {Function}*/
+Astro.utils.Date.chinese;
+
+/** @type {constant}*/
+Astro.utils.Date.MONTH;
+/** @type {constant}*/
+Astro.utils.Date.MONTH_ABBR;
+/** @type {constant}*/
+Astro.utils.Date.DAY;
+/** @type {constant}*/
+Astro.utils.Date.DAY_ABBR;
+/** @type {constant}*/
+Astro.utils.Date.CAP_MONTHS;
diff --git a/botanjs/src/externs/Astro.utils.js b/botanjs/src/externs/Astro.utils.js
new file mode 100644
index 0000000..6bf8f73
--- /dev/null
+++ b/botanjs/src/externs/Astro.utils.js
@@ -0,0 +1,2 @@
+/** @constructor */
+Astro.utils = function() {};
diff --git a/botanjs/src/externs/_3rdParty_.Recaptcha.js b/botanjs/src/externs/_3rdParty_.Recaptcha.js
new file mode 100644
index 0000000..e4c340c
--- /dev/null
+++ b/botanjs/src/externs/_3rdParty_.Recaptcha.js
@@ -0,0 +1,12 @@
+/** @constructor */
+_3rdParty_.Recaptcha = function() {};
+
+/** @type {Number} */
+_3rdParty_.Recaptcha.timer_id;
+
+/** @type {Function} */
+_3rdParty_.render;
+/** @type {Function} */
+_3rdParty_.reset;
+/** @type {Function} */
+_3rdParty_.getResponse;
diff --git a/botanjs/src/externs/_3rdParty_.js b/botanjs/src/externs/_3rdParty_.js
new file mode 100644
index 0000000..ea23482
--- /dev/null
+++ b/botanjs/src/externs/_3rdParty_.js
@@ -0,0 +1,2 @@
+/** @type {Object} */
+var _3rdParty_ = {};
diff --git a/botanjs/src/externs/_AstConf_.Article.js b/botanjs/src/externs/_AstConf_.Article.js
new file mode 100644
index 0000000..1c442ae
--- /dev/null
+++ b/botanjs/src/externs/_AstConf_.Article.js
@@ -0,0 +1,4 @@
+/** @type {Object} */
+_AstConf_.Article = {};
+/** @type {String} */
+_AstConf_.Article.id;
diff --git a/botanjs/src/externs/_AstConf_.AstroEdit.js b/botanjs/src/externs/_AstConf_.AstroEdit.js
new file mode 100644
index 0000000..0ef0cc9
--- /dev/null
+++ b/botanjs/src/externs/_AstConf_.AstroEdit.js
@@ -0,0 +1,20 @@
+/** @type {Object} */
+_AstConf_.AstroEdit = {};
+/** @type {String} */
+_AstConf_.AstroEdit.article_id;
+/** @type {object} */
+_AstConf_.AstroEdit.paths = {};
+ /** @type {string} */
+ _AstConf_.AstroEdit.paths.set_article;
+ /** @type {string} */
+ _AstConf_.AstroEdit.paths.get_article;
+ /** @type {string} */
+ _AstConf_.AstroEdit.paths.get_drafts;
+ /** @type {string} */
+ _AstConf_.AstroEdit.paths.get_files;
+ /** @type {string} */
+ _AstConf_.AstroEdit.paths.set_file;
+ /** @type {string} */
+ _AstConf_.AstroEdit.paths.tag_count;
+ /** @type {string} */
+ _AstConf_.AstroEdit.paths.set_tag;
diff --git a/botanjs/src/externs/_AstConf_.Comment.js b/botanjs/src/externs/_AstConf_.Comment.js
new file mode 100644
index 0000000..5204e8a
--- /dev/null
+++ b/botanjs/src/externs/_AstConf_.Comment.js
@@ -0,0 +1,6 @@
+/** @type {Object} */
+_AstConf_.Comment = {};
+/** @type {String} */
+_AstConf_.Comment.processor;
+/** @type {String} */
+_AstConf_.Comment.siteKey;
diff --git a/botanjs/src/externs/_AstConf_.Control.js b/botanjs/src/externs/_AstConf_.Control.js
new file mode 100644
index 0000000..bb49c47
--- /dev/null
+++ b/botanjs/src/externs/_AstConf_.Control.js
@@ -0,0 +1,6 @@
+/** @type {Object} */
+_AstConf_.Control = {};
+/** @type {Object} */
+_AstConf_.Control.action;
+ /** @type {String} */
+ _AstConf_.Control.action.del;
diff --git a/botanjs/src/externs/_AstConf_.Login.js b/botanjs/src/externs/_AstConf_.Login.js
new file mode 100644
index 0000000..2379ac3
--- /dev/null
+++ b/botanjs/src/externs/_AstConf_.Login.js
@@ -0,0 +1,6 @@
+/** @type {Object} */
+_AstConf_.Login = {};
+/** @type {String} */
+_AstConf_.Login.formAct;
+/** @type {String} */
+_AstConf_.Login.sHeat;
diff --git a/botanjs/src/externs/_AstConf_.SiteFile.js b/botanjs/src/externs/_AstConf_.SiteFile.js
new file mode 100644
index 0000000..1b6513c
--- /dev/null
+++ b/botanjs/src/externs/_AstConf_.SiteFile.js
@@ -0,0 +1,21 @@
+/** @type {Object} */
+_AstConf_.SiteFile = {};
+ /** @type {String} */
+ _AstConf_.SiteFile.f_host;
+
+ /** @type {Object} */
+ _AstConf_.SiteFile.path;
+ /** @type {String} */
+ _AstConf_.SiteFile.path.download;
+ /** @type {String} */
+ _AstConf_.SiteFile.path.info;
+ /** @type {Object} */
+ _AstConf_.SiteFile.path.image;
+ /** @type {String} */
+ _AstConf_.SiteFile.path.image.small;
+ /** @type {String} */
+ _AstConf_.SiteFile.path.image.medium;
+ /** @type {String} */
+ _AstConf_.SiteFile.path.image.large;
+ /** @type {Array} */
+ _AstConf_.SiteFile.files;
diff --git a/botanjs/src/externs/_AstConf_.UserInfo.js b/botanjs/src/externs/_AstConf_.UserInfo.js
new file mode 100644
index 0000000..deda862
--- /dev/null
+++ b/botanjs/src/externs/_AstConf_.UserInfo.js
@@ -0,0 +1,9 @@
+/** @type {Object} */
+_AstConf_.UserInfo = {};
+
+/** @type {String} */
+_AstConf_.UserInfo.name;
+/** @type {String} */
+_AstConf_.UserInfo.website;
+/** @type {String} */
+_AstConf_.UserInfo.avatar;
diff --git a/botanjs/src/externs/_AstConf_.js b/botanjs/src/externs/_AstConf_.js
new file mode 100644
index 0000000..721280d
--- /dev/null
+++ b/botanjs/src/externs/_AstConf_.js
@@ -0,0 +1,2 @@
+/** @type {Object} */
+var _AstConf_ = {};
diff --git a/botanjs/src/externs/_AstJson_.AJaxGetArticle.js b/botanjs/src/externs/_AstJson_.AJaxGetArticle.js
new file mode 100644
index 0000000..697ca25
--- /dev/null
+++ b/botanjs/src/externs/_AstJson_.AJaxGetArticle.js
@@ -0,0 +1,29 @@
+/** @type {Object} */
+_AstJson_.AJaxGetArticle = {};
+
+/** @type {Boolean} */
+_AstJson_.AJaxGetArticle.status;
+/** @type {String} */
+_AstJson_.AJaxGetArticle.backup;
+/** @type {Object} */
+_AstJson_.AJaxGetArticle.entry;
+ /** @type {String} */
+ _AstJson_.AJaxGetArticle.entry.slug;
+ /** @type {Boolean} */
+ _AstJson_.AJaxGetArticle.entry.archived;
+ /** @type {String} */
+ _AstJson_.AJaxGetArticle.entry.date_created;
+ /** @type {Boolean} */
+ _AstJson_.AJaxGetArticle.entry.draft;
+ /** @type {Boolean} */
+ _AstJson_.AJaxGetArticle.entry.featured;
+ /** @type {String} */
+ _AstJson_.AJaxGetArticle.entry.date_modified;
+ /** @type {String} */
+ _AstJson_.AJaxGetArticle.entry.date_published;
+ /** @type {Array} */
+ _AstJson_.AJaxGetArticle.entry.tags;
+ /** @type {String} */
+ _AstJson_.AJaxGetArticle.entry.text;
+ /** @type {String} */
+ _AstJson_.AJaxGetArticle.entry.title;
diff --git a/botanjs/src/externs/_AstJson_.AJaxGetDrafts.js b/botanjs/src/externs/_AstJson_.AJaxGetDrafts.js
new file mode 100644
index 0000000..9a19293
--- /dev/null
+++ b/botanjs/src/externs/_AstJson_.AJaxGetDrafts.js
@@ -0,0 +1,18 @@
+/** @type {Object} */
+_AstJson_.AJaxGetDrafts = {};
+
+/** @type {Boolean} */
+_AstJson_.AJaxGetDrafts.status;
+/** @type {Array} */
+_AstJson_.AJaxGetDrafts.entries;
+
+/** @type {Object} */
+_AstJson_.AJaxGetDrafts.entry = {};
+ /** @type {String} */
+ _AstJson_.AJaxGetDrafts.entry.content;
+ /** @type {String} */
+ _AstJson_.AJaxGetDrafts.entry.date;
+ /** @type {String} */
+ _AstJson_.AJaxGetDrafts.entry._id;
+ /** @type {String} */
+ _AstJson_.AJaxGetDrafts.entry.title;
diff --git a/botanjs/src/externs/_AstJson_.AJaxGetFiles.Request.js b/botanjs/src/externs/_AstJson_.AJaxGetFiles.Request.js
new file mode 100644
index 0000000..4e3f0e5
--- /dev/null
+++ b/botanjs/src/externs/_AstJson_.AJaxGetFiles.Request.js
@@ -0,0 +1,13 @@
+/** @type {Object} */
+_AstJson_.AJaxGetFiles.Request = {};
+
+/** @type {String} */
+_AstJson_.AJaxGetFiles.Request.from;
+/** @type {String} */
+_AstJson_.AJaxGetFiles.Request.to;
+/** @type {String} */
+_AstJson_.AJaxGetFiles.Request.group;
+/** @type {String} */
+_AstJson_.AJaxGetFiles.Request.listFiles;
+/** @type {String} */
+_AstJson_.AJaxGetFiles.Request.aid;
diff --git a/botanjs/src/externs/_AstJson_.AJaxGetFiles.js b/botanjs/src/externs/_AstJson_.AJaxGetFiles.js
new file mode 100644
index 0000000..314ab03
--- /dev/null
+++ b/botanjs/src/externs/_AstJson_.AJaxGetFiles.js
@@ -0,0 +1,24 @@
+/** @type {Object} */
+_AstJson_.AJaxGetFiles = {};
+
+/** @type {Boolean} */
+_AstJson_.AJaxGetFiles.status;
+/** @type {Array} */
+_AstJson_.AJaxGetFiles.files;
+
+/** @type {Object} */
+_AstJson_.AJaxGetFiles.file = {};
+ /** @type {String} */
+ _AstJson_.AJaxGetFiles.file.author;
+ /** @type {String} */
+ _AstJson_.AJaxGetFiles.file.cCount;
+ /** @type {String} */
+ _AstJson_.AJaxGetFiles.file.date;
+ /** @type {String} */
+ _AstJson_.AJaxGetFiles.file.hash;
+ /** @type {String} */
+ _AstJson_.AJaxGetFiles.file.id;
+ /** @type {String} */
+ _AstJson_.AJaxGetFiles.file.name;
+ /** @type {String} */
+ _AstJson_.AJaxGetFiles.file.src_location;
diff --git a/botanjs/src/externs/_AstJson_.SiteFile.js b/botanjs/src/externs/_AstJson_.SiteFile.js
new file mode 100644
index 0000000..0cfbb04
--- /dev/null
+++ b/botanjs/src/externs/_AstJson_.SiteFile.js
@@ -0,0 +1,21 @@
+/** @type {Object} */
+_AstJson_.SiteFile = {};
+
+/** @type {Boolean} */
+_AstJson_.SiteFile.status;
+/** @type {String} */
+_AstJson_.SiteFile.message;
+/** @type {Object} */
+_AstJson_.SiteFile.file;
+ /** @type {String} */
+ _AstJson_.SiteFile.file.date_created;
+ /** @type {String} */
+ _AstJson_.SiteFile.file.name;
+ /** @type {String} */
+ _AstJson_.SiteFile.file.id;
+ /** @type {String} */
+ _AstJson_.SiteFile.file.nickname;
+ /** @type {String} */
+ _AstJson_.SiteFile.file.type;
+ /** @type {String} */
+ _AstJson_.SiteFile.file.src_location;
diff --git a/botanjs/src/externs/_AstJson_.js b/botanjs/src/externs/_AstJson_.js
new file mode 100644
index 0000000..ca6cfe7
--- /dev/null
+++ b/botanjs/src/externs/_AstJson_.js
@@ -0,0 +1,2 @@
+/** @type {Object} */
+var _AstJson_ = {};
diff --git a/settings.ini b/settings.ini
index 65c8da3..42409f5 100644
--- a/settings.ini
+++ b/settings.ini
@@ -7,9 +7,9 @@ Cache = ${SiteRoot}/cache
[BotanJS]
SrcDir = ${Paths:Runtime}/botanjs/src
-REDIS_PASS = PASSWORD_FOR_REDIS_DB
+REDIS_PASS = RJszCzFoeZaULKyY3w5W0kMVF4Ei5ZsqMVexk8wxFzQEYnRuLsdGOkHIAXYe4XNuByZHfbee7GxLsuo9xvEIKdYVpvVRYMUbcf25cuOIWV1sLgxhRv0q9KtH7cLpBroa1BIiqHtbzkd3erenel6siIqyHQxV0jPVFfm0ayCvYmdHiVL1VphBpTSnAX8JNvR2Iim5Q7aGiDpLxQD0nnnc1uQuJjwBNA3jkjddMMvHgN8iYleq4SA2xycqNNFzejlT
-CeleryBroker = redis://:${REDIS_PASS}@123.123.123.123:1234/9
+CeleryBroker = redis://:${REDIS_PASS}@192.168.100.98:6379/9
[Service]