penguin/AstroJS

Javascript framework for my blog

botanjs/src/Astro/Blog/AstroEdit/Article.js

raw ยท 13925 bytes

(function(){
	var ns = __namespace( "Astro.Blog.AstroEdit" );

	/** @type {System.Cycle} */
	var Cycle                            = __import( "System.Cycle" );
	/** @type {System.utils} */
	var utils                            = __import( "System.utils" );
	/** @type {System.utils.EventKey} */
	var EventKey                         = __import( "System.utils.EventKey" );
	/** @type {System.Debug} */
	var debug                            = __import( "System.Debug" );
	/** @type {typeof Components.MessageBox} */
	var MessageBox                       = __import( "Components.MessageBox" );
	/** @type {typeof Dandelion} */
	var Dand                             = __import( "Dandelion" );
	/** @type {Dandelion.IDOMObject} */
	var IDOMObject                       = __import( "Dandelion.IDOMObject" );
	/** @type {function(...?): Dandelion.IDOMElement} */
	var IDOMElement                      = __import( "Dandelion.IDOMElement" );
	/** @type {Astro.Blog.Config} */
	var Config                           = __import( "Astro.Blog.Config" );
	/** @type {typeof 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 __statePusher;
		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 = utils.siteProto( 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];
			// If they are snippets or sections
			if( i == 2 || i == 3 )
			{
				p.style.height = "auto";
				continue;
			}
			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 )
		{
			saveSuccess( 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 )
			{
				var op = window.open( base_path + "article/id-view/" + ArticleModel.article_id + "/" );
				Cycle.next( function() {
					if( op ) 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} obj
		 * @return {void}
		 */
		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 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 );
})();