forked from Botanical/BotanJS
542 lines
13 KiB
JavaScript
542 lines
13 KiB
JavaScript
(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 opostData = __import( "System.Net.postData" );
|
|
var prettyDate = __import( "Astro.utils.Date.pretty" );
|
|
|
|
var Visualizer = ns[ NS_INVOKE ]( "Visualizer" );
|
|
var Flag = ns[ NS_INVOKE ]( "Flag" );
|
|
|
|
|
|
// Editor Override
|
|
var postData = function( processor, data, success, failed )
|
|
{
|
|
data[ "editor" ] = 1;
|
|
opostData( processor, data, success, failed );
|
|
};
|
|
|
|
// Wrappers for every plugins
|
|
var PluginBundles = function( article, plugins )
|
|
{
|
|
this.plugins = plugins;
|
|
for( var i in this.plugins )
|
|
{
|
|
/* @type {Astro.Blog.AstroEdit.IPlugins} */
|
|
var g = this.plugins[i];
|
|
g.bindArticle = article;
|
|
}
|
|
};
|
|
|
|
PluginBundles.prototype.setForView = function( pluginId, data )
|
|
{
|
|
for( var i in this.plugins )
|
|
{
|
|
var p = this.plugins[i];
|
|
if( p.id == pluginId )
|
|
{
|
|
p.setForView( data );
|
|
}
|
|
}
|
|
};
|
|
|
|
PluginBundles.prototype.getData = function( pluginId, data )
|
|
{
|
|
for( var i in this.plugins )
|
|
{
|
|
var p = this.plugins[i];
|
|
if( p.id == pluginId )
|
|
{
|
|
return p.getData( data );
|
|
}
|
|
}
|
|
};
|
|
|
|
var Article = function( processorSet, plugins )
|
|
{
|
|
var pBubble = new Bubble();
|
|
|
|
var Draft = ns[ NS_INVOKE ]( "Draft" );
|
|
|
|
var _title = "";
|
|
var _content = "";
|
|
|
|
//// Classes & obj
|
|
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
|
|
, section: 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" )
|
|
, section: set_field_name( Dand.wrap( "input" ), "section" )
|
|
};
|
|
|
|
// 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 }}}*/
|
|
|
|
/*{{{ Button sections */
|
|
var ae_stitles = Dand.glass( "ae_stitle", true );
|
|
var ae_panel_section = Dand.glass( "ae_panel_section", true );
|
|
|
|
var l = ae_stitles.length;
|
|
for( var i = 0; i < l; i ++ )
|
|
{
|
|
var t = ae_stitles[i];
|
|
var p = ae_panel_section[i];
|
|
t.addEventListener( "Click", function( e )
|
|
{
|
|
var p = this.p;
|
|
if( this.t.expanded = !this.t.expanded )
|
|
{
|
|
p.style.height = "auto";
|
|
Cycle.next(
|
|
function(){
|
|
p.style.height = this.h + "px";
|
|
}.bind({ h: p.element.clientHeight })
|
|
);
|
|
p.style.height = 0;
|
|
}
|
|
else
|
|
{
|
|
p.style.height = 0;
|
|
}
|
|
}.bind({ t: t, p: p }) );
|
|
}
|
|
|
|
ae_stitles[0].element.click();
|
|
/* End Button sections }}}*/
|
|
|
|
// Install plugins
|
|
plugins = new PluginBundles( this, plugins );
|
|
|
|
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;
|
|
|
|
// If this is a published article
|
|
// we need to set the ref_id for ArticleModel
|
|
// then set current id to draft's id
|
|
if( ArticleModel.article_id != obj.article_id )
|
|
{
|
|
ArticleModel.draft = true;
|
|
ArticleModel.ref_id = __article_id;
|
|
ArticleModel.article_id = obj.article_id;
|
|
}
|
|
|
|
// 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.entry.ref_id )
|
|
{
|
|
// This is a backup item
|
|
__article_id = obj.article_id;
|
|
|
|
pBubble.setColor( "royalblue" );
|
|
pBubble.pop( "Using backup entry" );
|
|
Cycle.delay( function () { pBubble.blurp() }, 3000 );
|
|
}
|
|
|
|
setArticle( obj.entry );
|
|
__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)";
|
|
|
|
plugins.setForView( "tags", ArticleModel.tags );
|
|
plugins.setForView( "section", ArticleModel.section );
|
|
}
|
|
|
|
, 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 ( obj )
|
|
{
|
|
var loc = window.location;
|
|
loc.replace(
|
|
loc.href.replace(
|
|
"/" + ArticleModel.article_id
|
|
, ArticleModel.ref_id ? ( "/" + ArticleModel.ref_id ) : ""
|
|
)
|
|
);
|
|
}
|
|
|
|
, serverFailed = function ( obj )
|
|
{
|
|
pBubble.setColor( "red" );
|
|
pBubble.pop( obj ? obj["mesg"] : "Server Error" );
|
|
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
|
|
, draft: 1
|
|
, tags: plugins.getData( "tags" )
|
|
, section: plugins.getData( "section" )
|
|
};
|
|
|
|
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 = plugins.getData( "tags" );
|
|
ae_p_fields.section.value = plugins.getData( "section" );
|
|
|
|
ae_preview.submit();
|
|
};
|
|
|
|
this.invoke = function (_class)
|
|
{
|
|
if ( _class instanceof Draft ) _ae_draft = _class;
|
|
if ( _class instanceof Visualizer )
|
|
{
|
|
_visualizer = _class;
|
|
_visualizer.setContentDiv( ae_content );
|
|
}
|
|
};
|
|
|
|
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
|
|
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 = plugins.getData( "tags" );
|
|
_data.section = plugins.getData( "section" );
|
|
|
|
postData( processorSet, _data, publishSuccess.bind({ contentUpdate: true }), serverFailed );
|
|
}
|
|
else
|
|
{
|
|
// Do not submit uneccessary data!
|
|
postData( processorSet, _data, publishSuccess, serverFailed );
|
|
}
|
|
};
|
|
|
|
this.drop = function ()
|
|
{
|
|
if( ArticleModel.draft )
|
|
{
|
|
new MessageBox(
|
|
"Delete draft"
|
|
, "Are you sure you want to delete this draft? (Published article will only remove backup draft.)"
|
|
, "Delete", "No"
|
|
, deleteDraft
|
|
).show();
|
|
}
|
|
else
|
|
{
|
|
new MessageBox(
|
|
"Delete Draft"
|
|
, "There is no draft to delete"
|
|
).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();
|
|
}
|
|
}.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 );
|
|
})();
|