--- /dev/null
+<?php
+/**
+ * $Id: block.php 219 2008-01-11 09:45:33Z duck $
+ *
+ * Copyright Obala d.o.o. (www.obala.si)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ * @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());
--- /dev/null
+<?php
+/**
+ * $Id: index.php 220 2008-01-11 11:46:13Z duck $
+ *
+ * Copyright Obala d.o.o. (www.obala.si)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ * @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';
+?>
+
+<script type="text/javascript" src="js/builder.js"></script>
+<script type="text/javascript" src="js/dragdrop.js"></script>
+<script type="text/javascript" src="js/src/portal.js"></script>
+<script type="text/javascript" src="js/src/portal_edit.js"></script>
+
+<link href="themes/screen.css" rel="stylesheet" type="text/css" />
+</head>
+
+<div id="menuBottom">
+ <a href="#" onclick="listWidgets()"><?php echo _("Add content") ?></a> |
+ <a href="#" onclick="alert('TODO')"><?php echo _("Reset to default") ?></a> |
+ <a href="#" onclick="savePortal()"><?php echo _("Save") ?></a> |
+</div>
+<br class="clear" />
+ <div id="control_buttons" style="display: none">
+ <a href="#" onclick="minimizeWidget(this); return false;" id="minimize_button" title="<?php echo _("Minimize") ?>"></a>
+ <a href="#" onclick="editWidget(this); return false;" id="edit_button" title="<?php echo _("Edit") ?>"></a>
+ <a href="#" onclick="reloadWidget(this); return false;" id="reload_button" title="<?php echo _("Reload") ?>"></a>
+ <a href="#" onclick="removeWidget(this); return false;" id="delete_button" title="<?php echo _("Delete") ?>"></a>
+ </div>
+
+<?php
+
+echo $layout_html;
+
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+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;C<B;++C){var K=this._widgets[C].getElement();if(K==D||K.parentNode!=H){continue}if(Position.within(K,J,F)){var I=Position.overlap("vertical",K);if(I<0.5){if(K.next()!=D.ghost){K.parentNode.insertBefore(D.ghost,K.next());G=true}}else{if(K.previous()!=D.ghost){K.parentNode.insertBefore(D.ghost,K);G=true}}M=true;break}}if(!M){if(D.ghost.parentNode!=H){var L=H.immediateDescendants().last();var E=L?Position.cumulativeOffset(L)[1]+L.getHeight():0;if(F>E&&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
--- /dev/null
+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('<div id="RB_info">'+A.responseText+"</div>")},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('<div id="RB_info">'+C.responseText+"</div>")},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
--- /dev/null
+// 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;
+ }
+ }
+});
--- /dev/null
+
+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('<div id="RB_info">' + transport.responseText + '</div>');
+ },
+ 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('<div id="RB_info">' + transport.responseText + '</div>');
+ },
+ 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'
+ });
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Block_Layout_View class represents the user defined portal layout.
+ *
+ * $Horde: framework/Block/Block/Layout/View.php,v 1.17 2007/11/22 00:28:06 jan Exp $
+ *
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ * @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 = '<div id="page">';
+ $js = '<script type="text/javascript">' . "\n";
+ $js .= 'var confirm_remove = "' . _("Are sure to remove this block?") . '";' . "\n";
+ $js .= 'var edit_url = "' . Horde::applicationUrl('dragdrop/params.php') . '";' . "\n";
+ $js .= 'var load_url = "' . Horde::applicationUrl('dragdrop/block.php') . '";' . "\n";
+ $js .= 'var list_url = "' . Horde::applicationUrl('dragdrop/select.php') . '";' . "\n";
+ $js .= 'var save_url = "' . Horde::applicationUrl('dragdrop/save.php') . '";' . "\n";
+
+ $js_init = '<script type="text/javascript">'
+ . ' function init() {'
+ . ' portal = new Xilinus.Portal("#page div", {onOverWidget: onOverWidget, onOutWidget: onOutWidget});';
+
+ $js_id = 0;
+ $widget_col = 0;
+ $columns = 0;
+ foreach ($this->_layout as $row_num => $row) {
+ foreach ($row as $col_num => $item) {
+ if ($col_num > $columns) {
+ $columns = $col_num;
+ }
+ if (is_array($item)) {
+ $js .= $this->_serializeBlock($js_id, $item['app'], $item['params']['type'],
+ $item['params']['params'], $js_init, $col_num);
+ $js_id++;
+ }
+ }
+ }
+
+ $columns = max($columns, 2); // FOR TESTING ADD AT KEAST 3 COLUMNS
+ for ($col_num = 0; $col_num <= $columns; $col_num++) {
+ $html .= '<div id="widget_col_' . $col_num . '"></div>';
+ }
+
+ $js .= '</script>';
+
+ $js_init .= 'portal.addWidgetControls("control_buttons");'
+ . '}'
+ . 'document.observe("dom:loaded", init);
+'
+ . '</script>';
+
+ $html .= '</div>' . "\n" . $js . "\n" . $js_init;
+
+ // Strip any CSS <link> tags out of the returned content so
+ // they can be handled seperately.
+ if (preg_match_all('/<link .*?rel="stylesheet".*?\/>/', $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";
+ }
+}
--- /dev/null
+<?php
+/**
+ * $Id: params.php 216 2008-01-10 19:47:31Z duck $
+ *
+ * Copyright Obala d.o.o. (www.obala.si)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ * @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 '<script type="text/javascript">noParams("' . Util::getFormData('widget') . '", "' . _("This block has no special parameters.") . '");</script>';
+} 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';
+}
--- /dev/null
+<?php
+/**
+ * $Id: block.php 219 2008-01-11 09:45:33Z duck $
+ *
+ * Copyright Obala d.o.o. (www.obala.si)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ * @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));
--- /dev/null
+<?php
+/**
+ * $Id: select.php 215 2008-01-10 19:32:18Z duck $
+ *
+ * Copyright Obala d.o.o. (www.obala.si)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Duck <duck@obala.net>
+ * @package Folks
+ */
+define('HORDE_BASE', dirname(__FILE__) . '/..');
+require_once HORDE_BASE . '/lib/base.php';
+require_once 'Horde/Loader.php';
+
+?>
+<form>
+<select id="block_column">
+<option value="0"><?php echo _("First column") ?></option>
+<option value="1"><?php echo _("Second column") ?></option>
+<option value="2"><?php echo _("Third column") ?></option>
+</select>
+<br />
+<select id="block_selection" size="7">
+<?php
+$collection = Horde_Block_Collection::singleton();
+foreach ($collection->getBlocksList() as $id => $name) {
+ echo '<option value="' . $id .'">' . $name . '</option>';
+}
+?>
+</select>
+<br />
+<input type="button" class="button" value="<?php echo _("Add") ?>" onclick="return addWidget()" />
+<input type="button" class="button" value="<?php echo _("Cancel") ?>" onclick="return cancelRedBox()" />
+</form>
--- /dev/null
+<h1 class="header"><?php echo htmlspecialchars($blocks->getName($app, $name)) ?></h1>
+<form action="<?php echo Horde::selfUrl() ?>#block" method="post" id="blockform" name="blockform">
+<?php Util::pformInput() ?>
+<input type="hidden" name="action" value="save" />
+<input type="hidden" name="block" value="<?php echo Util::getFormData('block') ?>" />
+<input type="hidden" name="widget" value="<?php echo Util::getFormData('widget') ?>" />
+<table style="width: 100%; border-collapse: collapse;">
+<?php if ($block->updateable): ?>
+<tr>
+ <td class="text rightAlign" valign="top"><?php echo Horde::label('params_refresh_time', _("Refresh rate:")) ?> </td>
+ <td class="text" valign="top">
+ <select id="params_refresh_time" name="params[_refresh_time]">
+ <option<?php if ($defaults['_refresh_time'] == 0) echo ' selected="selected"' ?> value="0"><?php echo _("Never") ?></option>
+ <option<?php if ($defaults['_refresh_time'] == 30) echo ' selected="selected"' ?> value="30"><?php echo _("Every 30 seconds") ?></option>
+ <option<?php if ($defaults['_refresh_time'] == 60) echo ' selected="selected"' ?> value="60"><?php echo _("Every minute") ?></option>
+ <option<?php if ($defaults['_refresh_time'] == 300) echo ' selected="selected"' ?> value="300"><?php echo _("Every 5 minutes") ?></option>
+ <option<?php if ($defaults['_refresh_time'] == 900) echo ' selected="selected"' ?> value="900"><?php echo _("Every 15 minutes") ?></option>
+ <option<?php if ($defaults['_refresh_time'] == 1800) echo ' selected="selected"' ?> value="1800"><?php echo _("Every half hour") ?></option>
+ <option<?php if ($defaults['_refresh_time'] == 3600) echo ' selected="selected"' ?> value="3600"><?php echo _("Every hour") ?></option>
+ </select>
+ </td>
+</tr>
+<?php endif; ?>
+<?php $i = 0; foreach ($params as $id): $i++; ?>
+ <tr>
+ <td class="<?php echo ($i % 2) ? 'text' : 'item0' ?> rightAlign" valign="top"><?php echo $blocks->getParamName($app, $name, $id) ?>: </td>
+ <td class="<?php echo ($i % 2) ? 'text' : 'item0' ?>" valign="top"><?php echo $blocks->getOptionsWidget($app, $name, $id, $defaults) ?></td>
+ </tr>
+<?php endforeach; ?>
+<tr>
+<td class="control" colspan="2" style="text-align: center;">
+<input type="button" class="button" value="<?php echo _("Save") ?>" onclick="return setParams()" />
+<input type="button" class="button" value="<?php echo _("Cancel") ?>" onclick="return cancelRedBox()" />
+</td>
+</tr>
+</table>
+</form>
--- /dev/null
+
+/* 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;
+}