From: Chuck Hagenbuch Date: Thu, 4 Dec 2008 03:35:56 +0000 (-0500) Subject: add drag-n-drop portal code from #6102 X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=fd089930a1ef4a57d68fd4e8f155e3dc8b06331e;p=horde.git add drag-n-drop portal code from #6102 --- diff --git a/drag_n_drop_portal/block.php b/drag_n_drop_portal/block.php new file mode 100644 index 000000000..d105d908d --- /dev/null +++ b/drag_n_drop_portal/block.php @@ -0,0 +1,37 @@ + + * @package Folks + */ +define('HORDE_BASE', dirname(__FILE__) . '/..'); +require_once HORDE_BASE . '/lib/base.php'; +require_once 'Horde/Loader.php'; + +// Block to load +$block_id = Util::getFormData('block'); +list($app, $name) = explode(':', $block_id); + +$block_data = array(); +$block = Horde_Block_Collection::getBlock($app, $name, Util::getFormData('defaults')); +if ($block instanceof PEAR_Error) { + $block_data['title'] = $block->getMessage(); + $block_data['content'] = $block->getDebugInfo(); +} else { + $block_data['title'] = @$block->getTitle(); + if ($block_data['title'] instanceof PEAR_Error) { + $block_data['title'] = $block_data['title']->getMessage(); + } + $block_data['content'] = @$block->getContent(); + if ($block_data['content'] instanceof PEAR_Error) { + $block_data['content'] = $block_data['content']->getDebugInfo(); + } +} + +echo Horde_Serialize::serialize($block_data, SERIALIZE_JSON, NLS::getCharset()); diff --git a/drag_n_drop_portal/index.php b/drag_n_drop_portal/index.php new file mode 100644 index 000000000..7bfb3def6 --- /dev/null +++ b/drag_n_drop_portal/index.php @@ -0,0 +1,69 @@ + + * @package Folks + */ +define('HORDE_BASE', dirname(__FILE__) . '/..'); +require_once HORDE_BASE . '/lib/base.php'; +require_once 'Horde/Loader.php'; +require_once './lib/Block/Layout/View/js.php'; + +if (!Auth::isAuthenticated()) { + Horde::authenticationFailureRedirect(); +} + +// Load layout from preferences. +$layout_pref = unserialize($prefs->getValue('portal_layout')); +if (!is_array($layout_pref)) { + $layout_pref = array(); +} +if (!count($layout_pref)) { + $layout_pref = Horde_Block_Collection::getFixedBlocks(); +} + +// Render layout. +$view = new Horde_Block_Layout_View_Js($layout_pref); +$layout_html = $view->toHtml(); + +$title = _("Edit yout profile page"); + +Horde::addScriptFile('prototype.js', 'horde', true); +Horde::addScriptFile('effects.js', 'horde', true); +Horde::addScriptFile('redbox.js', 'horde', true); +require HORDE_TEMPLATES . '/common-header.inc'; +require HORDE_TEMPLATES . '/menu/menu.inc'; +?> + + + + + + + + + + +
+ + +get('templates', 'horde') . '/common-footer.inc'; diff --git a/drag_n_drop_portal/js/portal.js b/drag_n_drop_portal/js/portal.js new file mode 100644 index 000000000..2cc5e9456 --- /dev/null +++ b/drag_n_drop_portal/js/portal.js @@ -0,0 +1 @@ +if(typeof Draggable=="undefined"){throw("widget.js requires including script.aculo.us' dragdrop.js library")}if(typeof Builder=="undefined"){throw("widget.js requires including script.aculo.us' builder.js library")}if(typeof Xilinus=="undefined"){Xilinus={}}Builder.dump();Xilinus.Widget=Class.create();Xilinus.Widget.lastId=0;Xilinus.Widget.remove=function(B,A){if(A&&A.afterFinish){A.afterFinish.call()}};Object.extend(Xilinus.Widget.prototype,{initialize:function(D,E){D=D||"widget";this._id=E||("widget_"+Xilinus.Widget.lastId++);this._titleDiv=DIV({className:"header",id:this._getId("header")},"");this._contentDiv=DIV({className:"headerbox",id:this._getId("content")},"");this._footerDiv=DIV({className:D+"_statusbar",id:this._getId("footer")},"");var C=DIV({className:"header"},this._titleDiv);var B=DIV({className:"headerbox"},this._contentDiv);var A=DIV({className:D+"_sw"},this._footerDiv);this._div=DIV({className:D+(D!="widget"?" widget":""),id:this._getId()},[C,B,A]);this._div.widget=this;return this},destroy:function(){this._div.remove()},getElement:function(){return $(this._getId())||$(this._div)},setTitle:function(A){$(this._titleDiv).update(A);return this},getTitle:function(A){return $(this._titleDiv)},setFooter:function(A){$(this._footerDiv).update(A);return this},getFooter:function(A){return $(this._footerDiv)},setContent:function(A){$(this._contentDiv).update(A);return this},getContent:function(A){return $(this._contentDiv)},updateHeight:function(){$(this._contentDiv).setStyle({height:null});var A=$(this._contentDiv).getHeight();$(this._contentDiv).setStyle({height:A+"px"})},_getId:function(A){return(A?A+"_":"")+this._id}});Xilinus.Portal=Class.create();Object.extend(Xilinus.Portal.prototype,{lastEvent:null,widgets:null,columns:null,initialize:function(B,A){this.options=Object.extend({url:null,onOverWidget:null,onOutWidget:null,onChange:null,onUpdate:null,removeEffect:Xilinus.Widget.remove},A);this._columns=(typeof B=="string")?$$(B):B;this._widgets=new Array();this._columns.each(function(C){Droppables.add(C,{onHover:this.onHover.bind(this),overlap:"vertical",accept:this.options.accept})}.bind(this));this._outTimer=null;this._columns.invoke("undoPositioned");this._currentOverWidget=null;this._widgetMouseOver=this.widgetMouseOver.bindAsEventListener(this);this._widgetMouseOut=this.widgetMouseOut.bindAsEventListener(this);Draggables.addObserver({onEnd:this.endDrag.bind(this),onStart:this.startDrag.bind(this)})},add:function(C,B,A){A=typeof A=="undefined"?true:A;this._widgets.push(C);if(this.options.accept){C.getElement().addClassName(this.options.accept)}this._columns[B].appendChild(C.getElement());C.updateHeight();if(A){C.draggable=new Draggable(C.getElement(),{handle:C._titleDiv,revert:false});C.getTitle().addClassName("widget_draggable")}this._updateColumnsHeight();if(this.options.onOverWidget){C.getElement().immediateDescendants().invoke("observe","mouseover",this._widgetMouseOver)}if(this.options.onOutWidget){C.getElement().immediateDescendants().invoke("observe","mouseout",this._widgetMouseOut)}},remove:function(A){this._widgets.reject(function(B){return B==A});if(this.options.onOverWidget){A.getElement().immediateDescendants().invoke("stopObserving","mouseover",this._widgetMouseOver)}if(this.options.onOutWidget){A.getElement().immediateDescendants().invoke("stopObserving","mouseout",this._widgetMouseOut)}if(A.draggable){A.draggable.destroy()}this.options.removeEffect(A.getElement(),{afterFinish:function(){A.destroy()}});this._updateColumnsHeight()},serialize:function(){parameters="";this._columns.each(function(A){var B=A.immediateDescendants().collect(function(C){return A.id+"[]="+C.id}).join("&");parameters+=B+"&"});return parameters},addWidgetControls:function(A){$(A).observe("mouseover",this._widgetMouseOver);$(A).observe("mouseout",this._widgetMouseOut)},widgetMouseOver:function(B){this._clearTimer();var A=Event.element(B).up(".widget");if(this._currentOverWidget==null||this._currentOverWidget!=A){if(this._currentOverWidget&&this._currentOverWidget!=A){this.options.onOutWidget(this,this._currentOverWidget.widget)}this._currentOverWidget=A;this.options.onOverWidget(this,A.widget)}},widgetMouseOut:function(B){this._clearTimer();var A=Event.element(B).up(".widget");this._outTimer=setTimeout(this._doWidgetMouseOut.bind(this,A),100)},_doWidgetMouseOut:function(A){this._currentOverWidget=null;this.options.onOutWidget(this,A.widget)},startDrag:function(B,A){var D=A.element;if(!this._widgets.find(function(F){return F==D.widget})){return}var C=D.parentNode;var E=DIV({className:"widget_ghost"},"");$(E).setStyle({height:D.getHeight()+"px"});C.insertBefore(E,D);D.setStyle({width:D.getWidth()+"px"});Position.absolutize(D);document.body.appendChild(D);A.element.ghost=E;this._savePosition=this.serialize()},endDrag:function(B,A){var D=A.element;if(!this._widgets.find(function(E){return E==D.widget})){return}var C=D.ghost.parentNode;C.insertBefore(A.element,D.ghost);D.ghost.remove();if(Prototype.Browser.Opera){D.setStyle({top:0,left:0,width:"100%",height:D._originalHeight,zIndex:null,opacity:null,position:"relative"})}else{D.setStyle({top:null,left:null,width:null,height:D._originalHeight,zIndex:null,opacity:null,position:"relative"})}D.ghost=null;D.widget.updateHeight();this._updateColumnsHeight();if(this._savePosition!=this.serialize()){if(this.options.url){new Ajax.Request(this.options.url,{parameters:this.serialize()})}if(this.options.onUpdate){this.options.onUpdate(this)}}},onHover:function(D,H,I){var A=Position.cumulativeOffset(H);var J=A[0]+10;var F=A[1]+(1-I)*H.getHeight();if(Position.within(D.ghost,J,F)){return}var M=false;var G=false;for(var C=0,B=this._widgets.length;CE&&L!=D.ghost){H.appendChild(D.ghost);G=true}}}if(G&&this.options.onChange){this.options.onChange(this)}this._updateColumnsHeight()},_updateColumnsHeight:function(){var A=0;this._columns.each(function(B){A=Math.max(A,B.immediateDescendants().inject(0,function(D,C){return D+C.getHeight()}))});this._columns.invoke("setStyle",{height:A+"px"})},_clearTimer:function(){if(this._outTimer){clearTimeout(this._outTimer);this._outTimer=null}}}); \ No newline at end of file diff --git a/drag_n_drop_portal/js/portal_edit.js b/drag_n_drop_portal/js/portal_edit.js new file mode 100644 index 000000000..03f7f3bf6 --- /dev/null +++ b/drag_n_drop_portal/js/portal_edit.js @@ -0,0 +1 @@ +var _widgets_blocks=new Array();var _layout_params=new Array();function onOverWidget(A,B){B.getElement().insertBefore($("control_buttons"),B.getElement().firstChild);$("control_buttons").show()}function onOutWidget(A,B){$("control_buttons").hide()}function minimizeWidget(A){var B=$(A).up(".widget").widget;id=B._getId().substr(7);if($("content_widget_"+id).style.display=="none"){$("content_widget_"+id).style.display="block";A.id="minimize_button"}else{$("content_widget_"+id).style.display="none";A.id="maximize_button"}}function removeWidget(A){var B=$(A).up(".widget").widget;if(confirm(confirm_remove)){document.body.appendChild($("control_buttons").hide());portal.remove(B)}}function listWidgets(){RedBox.loading();new Ajax.Request(list_url,{method:"get",onSuccess:function(A){RedBox.showHtml('
'+A.responseText+"
")},onFailure:function(A){RedBox.close()}})}function addWidget(){select=$("block_selection");title=select.options[select.selectedIndex].text;widget=new Xilinus.Widget();widget.setTitle(title);widget.setContent(title);portal.add(widget,parseInt($F("block_column")));_widgets_blocks[_widgets_blocks.length]=select.value;_layout_params[_widgets_blocks.length]=new Array();editWidget(widget);cancelRedBox()}function reloadWidget(A){if($(A).id=="reload_button"){var B=$(A).up(".widget").widget}else{var B=A}var C=B._getId().substr(7);new Ajax.Request(load_url,{parameters:getAjaxParameters(A),method:"get",onSuccess:function(D){block_data=D.responseText.evalJSON(true);B.setTitle(block_data.title);B.setContent(block_data.content);_widgets_blocks[B._getId().substr(7)]=block_used},onFailure:function(D){alert("Someting gone wrong.")}})}function editWidget(A){if($(A).id=="reload_button"){var B=$(A).up(".widget").widget}else{var B=A}new Ajax.Request(edit_url,{parameters:getAjaxParameters(A),method:"get",onSuccess:function(C){RedBox.showHtml('
'+C.responseText+"
")},onFailure:function(C){RedBox.close()}})}function getAjaxParameters(A){if($(A).id=="reload_button"||$(A).id=="edit_button"){var B=$(A).up(".widget").widget}else{var B=A}var C=B._getId().substr(7);parameters="block="+_widgets_blocks[C];parameters=parameters+"&widget="+B._getId();p=_layout_params[C];for(a in p){if(typeof(p[a])!="string"){break}parameters=parameters+"&defaults["+a+"]="+p[a]}return parameters}function setParams(){widget_name="";params=new Array();inputs=$("blockform").getElements();inputs.each(function(B){name=B.name.substr(0,6);if(name=="params"){pos=B.name.indexOf("]",7);param_name=B.name.substr(7,pos-7);if(B.type=="checkbox"){params[param_name]=B.checked}else{params[param_name]=B.value}}if(name=="widget"){widget_name=B.value}});_layout_params[widget_name.substr(7)]=params;var A=$(widget_name).widget;reloadWidget(A);cancelRedBox()}function noParams(B,C){var A=$(B).widget;reloadWidget(A);cancelRedBox()}function cancelRedBox(){RedBox.close();return false}function savePortal(){parameters=portal.serialize();for(var A=0;A<_layout_params.length;A++){parameters=parameters+"¶ms["+A+"][type]="+_widgets_blocks[A];p=_layout_params[A];for(a in p){if(typeof(p[a])!="string"){break}parameters=parameters+"¶ms["+A+"]["+a+"]="+p[a]}}new Ajax.Request(save_url,{parameters:parameters,method:"post"})}; \ No newline at end of file diff --git a/drag_n_drop_portal/js/src/portal.js b/drag_n_drop_portal/js/src/portal.js new file mode 100644 index 000000000..a22b33bfd --- /dev/null +++ b/drag_n_drop_portal/js/src/portal.js @@ -0,0 +1,366 @@ +// Copyright (c) 2006 Sébastien Gruhier (http://xilinus.com, http://itseb.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// VERSION 1.1-trunk + +if(typeof Draggable == 'undefined') + throw("widget.js requires including script.aculo.us' dragdrop.js library"); + +if(typeof Builder == 'undefined') + throw("widget.js requires including script.aculo.us' builder.js library"); + +// Xilinus namespace +if(typeof Xilinus == 'undefined') + Xilinus = {} + +Builder.dump(); + + +Xilinus.Widget = Class.create(); +Xilinus.Widget.lastId = 0; +Xilinus.Widget.remove = function(element, options) { + if (options && options.afterFinish) + options.afterFinish.call(); +} + +Object.extend(Xilinus.Widget.prototype, { + initialize: function(className, id) { + className = className || "widget"; + this._id = id || ("widget_" + Xilinus.Widget.lastId++); + + this._titleDiv = DIV({className: 'header', id: this._getId("header")}, ""); + this._contentDiv = DIV({className: 'headerbox', id: this._getId("content")}, ""); + this._footerDiv = DIV({className: className + '_statusbar', id: this._getId("footer")}, ""); + + var divHeader = DIV({className: 'header' }, this._titleDiv); + var divContent = DIV({className: 'headerbox' }, this._contentDiv); + var divFooter = DIV({className: className + '_sw' }, this._footerDiv); + + this._div = DIV({className: className + (className != "widget" ? " widget" : ""), id: this._getId()}, [divHeader, divContent, divFooter]); + this._div.widget = this; + + return this; + }, + + destroy: function() { + this._div.remove(); + }, + + getElement: function() { + return $(this._getId()) || $(this._div); + }, + + setTitle: function(title) { + $(this._titleDiv).update(title); + return this; + }, + + getTitle: function(title) { + return $(this._titleDiv) + }, + + setFooter: function(title) { + $(this._footerDiv).update(title); + return this; + }, + + getFooter: function(title) { + return $(this._footerDiv) + }, + + setContent: function(title) { + $(this._contentDiv).update(title); + return this; + }, + + getContent: function(title) { + return $(this._contentDiv) + }, + + updateHeight: function() { + $(this._contentDiv).setStyle({height: null}) + + var h = $(this._contentDiv).getHeight(); + $(this._contentDiv).setStyle({height: h + "px"}) + }, + + // PRIVATE FUNCTIONS + _getId: function(prefix) { + return (prefix ? prefix + "_" : "") + this._id; + } +}); + + +Xilinus.Portal = Class.create() +Object.extend(Xilinus.Portal.prototype, { + lastEvent: null, + widgets: null, + columns: null, + + initialize: function(columns, options) { + this.options = Object.extend({ + url: null, // Url called by Ajax.Request after a drop + onOverWidget: null, // Called when the mouse goes over a widget + onOutWidget: null, // Called when the mouse goes out of a widget + onChange: null, // Called a widget has been move during drag and drop + onUpdate: null, // Called a widget has been move after drag and drop + removeEffect: Xilinus.Widget.remove // Remove effect (by default no effect), you can set it to Effect.SwitchOff for example + }, options) + this._columns = (typeof columns == "string") ? $$(columns) : columns; + this._widgets = new Array(); + this._columns.each(function(element) {Droppables.add(element, {onHover: this.onHover.bind(this), + overlap: "vertical", + accept: this.options.accept})}.bind(this)); + this._outTimer = null; + + // Draggable calls makePositioned for IE fix (??), I had to remove it for all browsers fix :) to handle properly zIndex + this._columns.invoke("undoPositioned"); + + this._currentOverWidget = null; + this._widgetMouseOver = this.widgetMouseOver.bindAsEventListener(this); + this._widgetMouseOut = this.widgetMouseOut.bindAsEventListener(this); + + Draggables.addObserver({ onEnd: this.endDrag.bind(this), onStart: this.startDrag.bind(this) }); + }, + + add: function(widget, columnIndex, draggable) { + draggable = typeof draggable == "undefined" ? true : draggable + // Add to widgets list + this._widgets.push(widget); + if (this.options.accept) + widget.getElement().addClassName(this.options.accept) + // Add element to column + this._columns[columnIndex].appendChild(widget.getElement()); + widget.updateHeight(); + + // Make header draggable + if (draggable) { + widget.draggable = new Draggable(widget.getElement(),{ handle: widget._titleDiv, revert: false}); + widget.getTitle().addClassName("widget_draggable"); + } + + // Update columns heights + this._updateColumnsHeight(); + + // Add mouse observers + if (this.options.onOverWidget) + widget.getElement().immediateDescendants().invoke("observe", "mouseover", this._widgetMouseOver); + if (this.options.onOutWidget) + widget.getElement().immediateDescendants().invoke("observe", "mouseout", this._widgetMouseOut); + }, + + remove: function(widget) { + // Remove from the list + this._widgets.reject(function(w) { return w == widget}); + + // Remove observers + if (this.options.onOverWidget) + widget.getElement().immediateDescendants().invoke("stopObserving", "mouseover", this._widgetMouseOver); + if (this.options.onOutWidget) + widget.getElement().immediateDescendants().invoke("stopObserving", "mouseout", this._widgetMouseOut); + + // Remove draggable + if (widget.draggable) + widget.draggable.destroy(); + + // Remove from the dom + this.options.removeEffect(widget.getElement(), {afterFinish: function() {widget.destroy();}}); + + // Update columns heights + this._updateColumnsHeight(); + }, + + serialize: function() { + parameters = "" + this._columns.each(function(column) { + var p = column.immediateDescendants().collect(function(element) { + return column.id + "[]=" + element.id + }).join("&") + parameters += p + "&" + }); + + return parameters; + }, + + addWidgetControls: function(element) { + $(element).observe("mouseover", this._widgetMouseOver); + $(element).observe("mouseout", this._widgetMouseOut); + }, + + // EVENTS CALLBACKS + widgetMouseOver: function(event) { + this._clearTimer(); + + var element = Event.element(event).up(".widget"); + if (this._currentOverWidget == null || this._currentOverWidget != element) { + if (this._currentOverWidget && this._currentOverWidget != element) + this.options.onOutWidget(this, this._currentOverWidget.widget) + + this._currentOverWidget = element; + this.options.onOverWidget(this, element.widget) + } + }, + + widgetMouseOut: function(event) { + this._clearTimer(); + var element = Event.element(event).up(".widget"); + this._outTimer = setTimeout(this._doWidgetMouseOut.bind(this, element), 100); + }, + + _doWidgetMouseOut: function(element) { + this._currentOverWidget = null; + this.options.onOutWidget(this, element.widget) + }, + + // DRAGGABLE OBSERVER CALLBACKS + startDrag: function(eventName, draggable) { + var widget = draggable.element; + + if (!this._widgets.find(function(w) {return w == widget.widget})) + return; + + var column = widget.parentNode; + + // Create and insert ghost widget + var ghost = DIV({className: 'widget_ghost'}, ""); + $(ghost).setStyle({height: widget.getHeight() + 'px'}) + + column.insertBefore(ghost, widget); + + // IE Does not absolutize properly the widget, needs to set width before + widget.setStyle({width: widget.getWidth() + "px"}); + + // Absolutize and move widget on body + Position.absolutize(widget); + document.body.appendChild(widget); + + // Store ghost to drag widget for later use + draggable.element.ghost = ghost; + + // Store current position + this._savePosition = this.serialize(); + }, + + endDrag: function(eventName, draggable) { + var widget = draggable.element; + if (!this._widgets.find(function(w) {return w == widget.widget})) + return; + + var column = widget.ghost.parentNode; + + column.insertBefore(draggable.element, widget.ghost); + widget.ghost.remove(); + + if (Prototype.Browser.Opera) + widget.setStyle({top: 0, left: 0, width: "100%", height: widget._originalHeight, zIndex: null, opacity: null, position: "relative"}) + else + widget.setStyle({top: null, left: null, width: null, height: widget._originalHeight, zIndex: null, opacity: null, position: "relative"}) + + widget.ghost = null; + widget.widget.updateHeight(); + this._updateColumnsHeight(); + + // Fire events if changed + if (this._savePosition != this.serialize()) { + if (this.options.url) + new Ajax.Request(this.options.url, {parameters: this.serialize()}); + + if (this.options.onUpdate) + this.options.onUpdate(this); + } + }, + + onHover: function(dragWidget, dropon, overlap) { + var offset = Position.cumulativeOffset(dropon); + var x = offset[0] + 10; + var y = offset[1] + (1 - overlap) * dropon.getHeight(); + + // Check over ghost widget + if (Position.within(dragWidget.ghost, x, y)) + return; + + // Find if it's overlapping a widget + var found = false; + var moved = false; + for (var index = 0, len = this._widgets.length; index < len; ++index) { + var w = this._widgets[index].getElement(); + if (w == dragWidget || w.parentNode != dropon) + continue; + + if (Position.within(w, x, y)) { + var overlap = Position.overlap( 'vertical', w); + // Bottom of the widget + if (overlap < 0.5) { + // Check if the ghost widget is not already below this widget + if (w.next() != dragWidget.ghost) { + w.parentNode.insertBefore(dragWidget.ghost, w.next()); + moved = true; + } + } + // Top of the widget + else { + // Check if the ghost widget is not already above this widget + if (w.previous() != dragWidget.ghost) { + w.parentNode.insertBefore(dragWidget.ghost, w); + moved = true; + } + } + found = true; + break; + } + } + // Not found a widget + if (! found) { + // Check if dropon has ghost widget + if (dragWidget.ghost.parentNode != dropon) { + // Get last widget bottom value + var last = dropon.immediateDescendants().last(); + var yLast = last ? Position.cumulativeOffset(last)[1] + last.getHeight() : 0; + if (y > yLast && last != dragWidget.ghost) { + dropon.appendChild(dragWidget.ghost); + moved = true; + } + } + } + if (moved && this.options.onChange) + this.options.onChange(this) + + this._updateColumnsHeight(); + }, + + // PRIVATE FUNCTIONS + _updateColumnsHeight: function() { + var h = 0; + this._columns.each(function(col) { + h = Math.max(h, col.immediateDescendants().inject(0, function(sum, element) { + return sum + element.getHeight(); + })); + }) + this._columns.invoke("setStyle", {height: h + 'px'}) + }, + + _clearTimer: function() { + if (this._outTimer) { + clearTimeout(this._outTimer); + this._outTimer = null; + } + } +}); diff --git a/drag_n_drop_portal/js/src/portal_edit.js b/drag_n_drop_portal/js/src/portal_edit.js new file mode 100644 index 000000000..f664f2fe0 --- /dev/null +++ b/drag_n_drop_portal/js/src/portal_edit.js @@ -0,0 +1,204 @@ + +var _widgets_blocks = new Array(); +var _layout_params = new Array(); + +function onOverWidget(portal, widget) { + widget.getElement().insertBefore($('control_buttons'), widget.getElement().firstChild); + $('control_buttons').show(); +} + +function onOutWidget(portal, widget) { + $('control_buttons').hide(); +} + +function minimizeWidget(element) { + var widget = $(element).up(".widget").widget; + id = widget._getId().substr(7); + if ($('content_widget_' + id).style.display == 'none') { + $('content_widget_' + id).style.display = 'block'; + element.id = 'minimize_button'; + } else { + $('content_widget_' + id).style.display = 'none'; + element.id = 'maximize_button'; + } +} + +function removeWidget(element) { + var widget = $(element).up(".widget").widget; + + if (confirm(confirm_remove)) { + document.body.appendChild($('control_buttons').hide()) + portal.remove(widget); + } +} + +function listWidgets() { + + RedBox.loading(); + + // load edit options + new Ajax.Request(list_url, { + method: 'get', + onSuccess: function(transport) { + RedBox.showHtml('
' + transport.responseText + '
'); + }, + onFailure: function(transport) { + RedBox.close(); + } + }); +} + +function addWidget() { + + // Add widget + select = $('block_selection'); + title = select.options[select.selectedIndex].text; + + widget = new Xilinus.Widget(); + widget.setTitle(title); + widget.setContent(title); + portal.add(widget, parseInt($F('block_column'))); + + _widgets_blocks[_widgets_blocks.length] = select.value; + _layout_params[_widgets_blocks.length] = new Array(); + + // Edit wiget + editWidget(widget); + + cancelRedBox(); +} + +function reloadWidget(element) { + + if ($(element).id == 'reload_button') { + var widget = $(element).up(".widget").widget; + } else { + var widget = element; + } + + var id = widget._getId().substr(7); + + new Ajax.Request(load_url, { + parameters: getAjaxParameters(element), + method: 'get', + onSuccess: function(transport) { + block_data = transport.responseText.evalJSON(true); + widget.setTitle(block_data['title']); + widget.setContent(block_data['content']); + _widgets_blocks[widget._getId().substr(7)] = block_used; + }, + onFailure: function(transport) { + alert('Someting gone wrong.'); + } + }); +} + +function editWidget(element) { + + if ($(element).id == 'reload_button') { + var widget = $(element).up(".widget").widget; + } else { + var widget = element; + } + + new Ajax.Request(edit_url, { + parameters: getAjaxParameters(element), + method: 'get', + onSuccess: function(transport) { + RedBox.showHtml('
' + transport.responseText + '
'); + }, + onFailure: function(transport) { + RedBox.close(); + } + }); + +} + +function getAjaxParameters(element) { + + if ($(element).id == 'reload_button' || $(element).id == 'edit_button') { + var widget = $(element).up(".widget").widget; + } else { + var widget = element; + } + + var id = widget._getId().substr(7); + + parameters = 'block=' + _widgets_blocks[id]; + parameters = parameters + '&widget=' + widget._getId(); + + p = _layout_params[id]; + for (a in p) { + if (typeof(p[a]) != 'string') { + break; + } + parameters = parameters + '&defaults[' + a + ']=' + p[a]; + } + + return parameters; +} + +function setParams() { + + widget_name = ''; + params = new Array(); + inputs = $('blockform').getElements(); + inputs.each(function(item) { + name = item.name.substr(0, 6); + if (name == 'params') { + pos = item.name.indexOf(']', 7); + param_name = item.name.substr(7, pos - 7); + if (item.type == 'checkbox') { + params[param_name] = item.checked; + } else { + params[param_name] = item.value; + } + } + if (name == 'widget') { + widget_name = item.value; + } + }); + + _layout_params[widget_name.substr(7)] = params; + + var widget = $(widget_name).widget; + reloadWidget(widget); + + cancelRedBox(); +} + +function noParams(widget_name, msg) { + + // alert(msg); + + var widget = $(widget_name).widget; + reloadWidget(widget); + + cancelRedBox(); +} + +function cancelRedBox() { + RedBox.close(); + return false; +} + +function savePortal() { + + parameters = portal.serialize(); + + for (var i = 0; i < _layout_params.length; i++) { + parameters = parameters + '¶ms[' + i + '][type]=' + _widgets_blocks[i]; + p = _layout_params[i]; + for (a in p) { + if (typeof(p[a]) != 'string') { + break; + } + parameters = parameters + '¶ms[' + i + '][' + a + ']=' + p[a]; + } + } + + new Ajax.Request(save_url, { + parameters: parameters, + method: 'post' + }); +} diff --git a/drag_n_drop_portal/lib/Block/Layout/View/js.php b/drag_n_drop_portal/lib/Block/Layout/View/js.php new file mode 100644 index 000000000..2e5cd23e9 --- /dev/null +++ b/drag_n_drop_portal/lib/Block/Layout/View/js.php @@ -0,0 +1,113 @@ + + * @since Horde 4 + * @package Horde_Block + */ +class Horde_Block_Layout_View_Js extends Horde_Block_Layout_View { + + /** + * Render the current layout as HTML. + * + * @return string HTML layout. + */ + function toHtml() + { + $html = '
'; + $js = ''; + + $js_init .= 'portal.addWidgetControls("control_buttons");' + . '}' + . 'document.observe("dom:loaded", init); +' + . ''; + + $html .= '
' . "\n" . $js . "\n" . $js_init; + + // Strip any CSS tags out of the returned content so + // they can be handled seperately. + if (preg_match_all('//', $html, $links)) { + $html = str_replace($links[0], '', $html); + $this->_linkTags = $links[0]; + } + + return $html; + } + + function _serializeBlock($js_id, $app, $name, $params, &$js_init, $col_num) + { + $block = Horde_Block_Collection::getBlock($app, $name, $params); + if ($block instanceof PEAR_Error) { + $title = $block->getMessage(); + $content = $block->getDebugInfo(); + $params = array(); + } else { + $content = @$block->getContent(); + if ($content instanceof PEAR_Error) { + $content = $content->getDebugInfo(); + } + $title = @$block->getTitle(); + if ($title instanceof PEAR_Error) { + $title = $title->getMessage(); + } else { + $title = strip_tags($title); + } + } + + $content = Horde_Serialize::serialize($content, SERIALIZE_JSON, NLS::getCharset()); + $title = Horde_Serialize::serialize($title, SERIALIZE_JSON, NLS::getCharset()); + $params = Horde_Serialize::serialize($params, SERIALIZE_JSON, NLS::getCharset()); + + $js_init .= 'portal.add(new Xilinus.Widget().' + . 'setTitle(title_' . $js_id .').' + . ' setContent(content_' . $js_id .'), ' . $col_num . ');' + . '_widgets_blocks[' . $js_id . '] = "' . $app . ':' . $name . '";' + . '_layout_params[' . $js_id . '] = \'' . $params . '\'.evalJSON();' + . 'delete title_' . $js_id .';' + . 'delete content_' . $js_id .';' . "\n"; + + return 'var content_' . $js_id . ' = ' . $content . ';' . "\n" + . 'var title_' . $js_id . ' = ' . $title . ';' . "\n"; + } +} diff --git a/drag_n_drop_portal/params.php b/drag_n_drop_portal/params.php new file mode 100644 index 000000000..92ff91466 --- /dev/null +++ b/drag_n_drop_portal/params.php @@ -0,0 +1,42 @@ + + * @package Folks + */ +define('HORDE_BASE', dirname(__FILE__) . '/..'); +require_once HORDE_BASE . '/lib/base.php'; +require_once 'Horde/Loader.php'; + +// Block to load +$block_id = Util::getFormData('block'); +list($app, $name) = explode(':', $block_id); + +// Load collection +$blocks = new Horde_Block_Collection(null, array($app)); + +// Create block params form +$params = $blocks->getParams($app, $name); +if (empty($params) || + !$blocks->isEditable($app, $name)) { + echo ''; +} else { + $block = &$blocks->getBlock($app, $name); + + $defaults = Util::getFormData('defaults'); + if (empty($defaults)) { + foreach ($params as $key => $val) { + $defaults[$key] = $val; + } + } + if (!isset($defaults['_refresh_time'])) { + $defaults['_refresh_time'] = 0; + } + require './templates/portal/params.php'; +} diff --git a/drag_n_drop_portal/save.php b/drag_n_drop_portal/save.php new file mode 100644 index 000000000..41057cd80 --- /dev/null +++ b/drag_n_drop_portal/save.php @@ -0,0 +1,39 @@ + + * @package Folks + */ +define('HORDE_BASE', dirname(__FILE__) . '/..'); +require_once HORDE_BASE . '/lib/base.php'; +require_once 'Horde/Loader.php'; + +if (!Auth::isAuthenticated()) { + Horde::authenticationFailureRedirect(); +} + +$layout = array(); +$params = Util::getPost('params'); +foreach ($_POST as $column => $rows) { + if (substr($column, 0, 11) != 'widget_col_') { + continue; + } + $col = (int)substr($column, 11); + foreach ($rows as $row => $widget) { + $id = (int)substr($widget, 7); + list($app, $name) = explode(':', $params[$id]['type']); + $layout[$row][$col] = array('app' => $app, + 'height' => 1, + 'width' => 1, + 'params' => array('type' => $name, + 'params' => $params[$id])); + } +} + +$prefs->setValue('portal_layout', serialize($layout)); diff --git a/drag_n_drop_portal/select.php b/drag_n_drop_portal/select.php new file mode 100644 index 000000000..855f69686 --- /dev/null +++ b/drag_n_drop_portal/select.php @@ -0,0 +1,36 @@ + + * @package Folks + */ +define('HORDE_BASE', dirname(__FILE__) . '/..'); +require_once HORDE_BASE . '/lib/base.php'; +require_once 'Horde/Loader.php'; + +?> +
+ +
+ +
+" onclick="return addWidget()" /> +" onclick="return cancelRedBox()" /> +
diff --git a/drag_n_drop_portal/templates/portal/params.php b/drag_n_drop_portal/templates/portal/params.php new file mode 100644 index 000000000..a917a10ff --- /dev/null +++ b/drag_n_drop_portal/templates/portal/params.php @@ -0,0 +1,37 @@ +

getName($app, $name)) ?>

+
+ + + + + +updateable): ?> + + + + + + + + + + + + + + +
  + +
getParamName($app, $name, $id) ?>: getOptionsWidget($app, $name, $id, $defaults) ?>
+" onclick="return setParams()" /> +" onclick="return cancelRedBox()" /> +
+
diff --git a/drag_n_drop_portal/themes/graphics/bottom_left.gif b/drag_n_drop_portal/themes/graphics/bottom_left.gif new file mode 100644 index 000000000..d324686e3 Binary files /dev/null and b/drag_n_drop_portal/themes/graphics/bottom_left.gif differ diff --git a/drag_n_drop_portal/themes/graphics/bottom_right.gif b/drag_n_drop_portal/themes/graphics/bottom_right.gif new file mode 100644 index 000000000..930a8cf0c Binary files /dev/null and b/drag_n_drop_portal/themes/graphics/bottom_right.gif differ diff --git a/drag_n_drop_portal/themes/graphics/delete.png b/drag_n_drop_portal/themes/graphics/delete.png new file mode 100644 index 000000000..5d466600f Binary files /dev/null and b/drag_n_drop_portal/themes/graphics/delete.png differ diff --git a/drag_n_drop_portal/themes/graphics/edit.png b/drag_n_drop_portal/themes/graphics/edit.png new file mode 100644 index 000000000..5ac227501 Binary files /dev/null and b/drag_n_drop_portal/themes/graphics/edit.png differ diff --git a/drag_n_drop_portal/themes/graphics/minus.png b/drag_n_drop_portal/themes/graphics/minus.png new file mode 100644 index 000000000..32170460c Binary files /dev/null and b/drag_n_drop_portal/themes/graphics/minus.png differ diff --git a/drag_n_drop_portal/themes/graphics/plus.png b/drag_n_drop_portal/themes/graphics/plus.png new file mode 100644 index 000000000..263e35690 Binary files /dev/null and b/drag_n_drop_portal/themes/graphics/plus.png differ diff --git a/drag_n_drop_portal/themes/graphics/redbox_spinner.gif b/drag_n_drop_portal/themes/graphics/redbox_spinner.gif new file mode 100644 index 000000000..35218b31b Binary files /dev/null and b/drag_n_drop_portal/themes/graphics/redbox_spinner.gif differ diff --git a/drag_n_drop_portal/themes/graphics/reload.png b/drag_n_drop_portal/themes/graphics/reload.png new file mode 100644 index 000000000..dcb020450 Binary files /dev/null and b/drag_n_drop_portal/themes/graphics/reload.png differ diff --git a/drag_n_drop_portal/themes/graphics/tooltip_bg.png b/drag_n_drop_portal/themes/graphics/tooltip_bg.png new file mode 100644 index 000000000..d851211bc Binary files /dev/null and b/drag_n_drop_portal/themes/graphics/tooltip_bg.png differ diff --git a/drag_n_drop_portal/themes/graphics/top_left.gif b/drag_n_drop_portal/themes/graphics/top_left.gif new file mode 100644 index 000000000..dd665b90b Binary files /dev/null and b/drag_n_drop_portal/themes/graphics/top_left.gif differ diff --git a/drag_n_drop_portal/themes/graphics/top_right.gif b/drag_n_drop_portal/themes/graphics/top_right.gif new file mode 100644 index 000000000..e46ca3930 Binary files /dev/null and b/drag_n_drop_portal/themes/graphics/top_right.gif differ diff --git a/drag_n_drop_portal/themes/screen.css b/drag_n_drop_portal/themes/screen.css new file mode 100644 index 000000000..c4f3274fb --- /dev/null +++ b/drag_n_drop_portal/themes/screen.css @@ -0,0 +1,217 @@ + +/* Redbox styles. */ +#RB_overlay { + position: absolute; + z-index: 100; + width: 100%; + height: 100%; + top: 0; + left: 0; + right: 0; + bottom: 0; + min-height: 100%; + background-color: #000; + opacity: .6; + filter: alpha(opacity=60); +} +#RB_loading { + z-index: 101; + width: 66; + margin-left: auto; + margin-right: auto; + margin-top: 200px; + padding-bottom: 66px; + text-align: center; + background: url("graphics/redbox_spinner.gif") no-repeat bottom center; +} +#RB_window { + z-index: 102; + background-color: #fff; + display: block; + text-align: left; + overflow: hidden; + margin: 20px auto 0 auto; + position: absolute; +} + +#RB_confirm { + width: 20em; + padding: 1em; + border: 1px solid #ccc; + background: #ffc; +} +#RB_confirm input { + margin: .2em; +} + +#RB_info { + width: 30em; + padding: 1em; + border: 1px solid #ccc; + background: #ccf; +} +#RB_info input { + margin: .2em; +} + +/* Portal editing */ + +#page { + margin: 10px auto; +} + +#page1 { + float: left; + width: 45%; +} + +#page2 { + float: right; + width: 45%; +} + +#widget_col_0 { + float: left; + width: 30%; + background: #E6E6E6; +} + +#widget_col_1 { + width: 50%; + float: left; + background: #CCC; +} + +#widget_col_2 { + float: left; + width: 20%; + background: #B3B3B3; +} + +#widget_col_3 { + float: left; + width: 40%; + background: #B3B3B3; +} + +#widget_col_4 { + float: left; + width: 60%; + background: #E6E6E6; +} + +#control_buttons { + position: absolute; + right: 0px; + top: 10px; + width: 90px; +} + +#reload_button { + position: relative; + float: left; + width: 16px; + height: 16px; + background: url(graphics/reload.png); + behavior: url(png.htc); + margin-right: 5px; +} + +#minimize_button { + position: relative; + float: left; + width: 16px; + height: 16px; + background: url(graphics/minus.png); + behavior: url(png.htc); + margin-right: 5px; +} + +#maximize_button { + position: relative; + float: left; + width: 16px; + height: 16px; + background: url(graphics/plus.png); + behavior: url(png.htc); + margin-right: 5px; +} + +#edit_button { + position: relative; + float: left; + width: 16px; + height: 16px; + background: url(graphics/edit.png); + behavior: url(png.htc); + margin-right: 5px; +} + +#delete_button { + position: relative; + float: left; + width: 16px; + height: 16px; + background: url(graphics/delete.png); + behavior: url(png.htc); + margin-right: 5px; +} + +/* Sliding doors technique */ +.widget_nw { + background: transparent url(graphics/top_left.gif) no-repeat; + height: 30px; +} + +.widget_w { + border-left: 1px solid #B9B9B9; + margin-left: 5px; +} + +.widget_sw { + background: transparent url(graphics/bottom_left.gif) no-repeat; + height: 15px; +} + +.widget_title { + background: url(graphics/top_right.gif) repeat-x right top; + color: #123456; + font: bold 14px/25px Tahoma, Arial, sans-serif; + height: 26px; + margin: 0 0 0 15px; + padding: 5px 0 0 0 ; + text-align: center; + margin-left: 15px; +} + +.widget_content { + background-color: #FDFDFD; + color: #71777A; + font: normal 12px/1em Tahoma, Arial, sans-serif; + overflow: hidden; + padding: 5px; + border-right: 1px solid #B9B9B9; + margin-right: 5px; +} + +.widget_statusbar { + background: transparent url(graphics/bottom_right.gif) repeat-x right top; + font-size: 8px; + height: 15px; + margin-left: 11px; +} + +.widget_draggable { + cursor: move; +} + +/* Ghost */ +.widget_ghost { + background: #FFF; + opacity: 0.5; + filter: alpha(opacity=50); + position: relative; + border: 3px dashed #F00; + margin: 0px; + padding: 0; +}