penguin/AstroJS

Javascript framework for my blog

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

raw ยท 20520 bytes

(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 {typeof System.utils.IKey} */
	var IKey                                    = __import( "System.utils.IKey" );
	/** @type {typeof System.utils.DataKey} */
	var DataKey                                 = __import( "System.utils.DataKey" );
	/** @type {typeof System.utils.EventKey} */
	var EventKey                                = __import( "System.utils.EventKey" );
	/** @type {typeof Components.MessageBox} */
	var MessageBox                              = __import( "Components.MessageBox" );
	/** @type {typeof Components.Mouse.ContextMenu} */
	var ContextMenu                             = __import( "Components.Mouse.ContextMenu" );
	/** @type {typeof Dandelion} */
	var Dand                                    = __import( "Dandelion" );
	/** @type {typeof Dandelion.IDOMObject} */
	var IDOMObject                              = __import( "Dandelion.IDOMObject" );
	/** @type {function(...?): Dandelion.IDOMElement} */
	var IDOMElement                             = __import( "Dandelion.IDOMElement" );
	/** @type {Astro.Blog.Config} */
	var Conf                                    = __import( "Astro.Blog.Config" );

	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

		/** @type {_AstConf_.SiteFile} */
		, config = Conf.get( "SiteFile" )

		, 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 IKey("data-title", data.name)
					, new IKey("data-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
						, id: data.id
						, 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.wrapna( "img", new IKey( "src", 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.wrapna( "img", new IKey( "src", 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 )
			{
				if( !e ) return;
				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 config.f_host + canvasView.idata.href; }
			, copyHash = function ()
			{
				var data = canvasView.idata;
				return data.isCollection ? data.id : data.hash;
			}

			, openAction = function ()
			{
				if(canvasView.idata.isCollection) openCollection(IDOMElement(canvasView.idata.stage).getDAttribute("id"));
				else window.open( config.f_host + 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 asl_colls" )
				;

				// 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.wrap("span", null, "c75", t.getDAttribute("title") )
								, Dand.wrap("span", null, "c25", t.getDAttribute("author") )
							]
							, 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 ];
					}

					if( !first )
					{
						debug.Info( "No files" );
						return;
					}

					// 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);

		this.stage = stage;
	};

	ns[ NS_EXPORT ]( EX_CLASS, "SiteLibrary", SiteLibrary );
})();