blob: b61b97bcfe2169a27ce122dfeffc0048fa20e439 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
dojo.provide("cocoon.forms.CFormsDragAndDropRepeater");
dojo.require("cocoon.forms.CFormsRepeater");
/**
* Dojo widget for repeaters, that handles drag'n drop reordering and selection
* by clicking in the rows.
* <p>
* The drop indicator can be styled with the "forms-dropIndicator" CSS class.
*
* @version $Id: CFormsRepeater.js 393543 2006-04-12 17:32:25Z cziegeler $
*/
// Extends the base CFormsRepeater class.
dojo.widget.defineWidget(
"cocoon.forms.CFormsDragAndDropRepeater",
cocoon.forms.CFormsRepeater, {
// Properties
// Widget definition
ns: "forms",
widgetType: "CFormsDragAndDropRepeater",
isContainer: true,
preventClobber: true, // don't clobber our node
/**
* Returns the action name to be called on the server for model update.
*/
getDndAction: function() {
var addAction = this.domNode.getAttribute("dnd-action");
dojo.debug("getDndAction: action=" + addAction);
return addAction;
},
/**
* Returns the drag & drop id used to share row between repeaters.
*/
getType: function() {
var type = this.domNode.getAttribute("dnd-id");
if (type == null) {
type = this.id;
}
return "cforms-" + type;
},
// widget interface
buildRendering: function(args, frag) {
// FIXME: we should destroy all drag sources and drop targets when the widget is destroyed
cocoon.forms.CFormsRepeater.superclass.buildRendering.call(this, args, frag);
this.id = this.domNode.getAttribute("id");
if (!this.orderable && this.select == "none") {
dojo.debug(this.widgetType + " '" + this.id + "' is not orderable nor selectable");
}
if (this.orderable) {
// Get the parent of the first repeater row (may be different from this.domNode)
var firstRow = dojo.byId(this.id + ".0");
if (!firstRow) return;
// Check that TR's are in TBODY otherwise it doesn't work
if (firstRow.tagName.toLowerCase() == "tr" && firstRow.parentNode.tagName.toLowerCase() != "tbody") {
throw this.widgetType + " requires TR's to be in a TBODY (check '" + this.id + "')";
}
var type = this.getType();
var dropTarget = new dojo.dnd.HtmlDropTarget(firstRow.parentNode, [type]);
dropTarget.createDropIndicator = function() {
this.dropIndicator = document.createElement("div");
this.dropIndicator.className = "forms-dropIndicator";
with (this.dropIndicator.style) {
position = "absolute";
zIndex = 1;
width = dojo.html.getBorderBox(this.domNode).width + "px";
left = dojo.html.getAbsolutePosition(this.domNode).x + "px";
}
};
dojo.event.connect("before", dropTarget, "insert", this, "beforeInsert");
dojo.event.connect(dropTarget, "insert", this, "afterInsert");
var row;
for (var idx = 0; row = dojo.byId(this.id + "." + idx); idx++) {
row.setAttribute("dndType", 'repeaterRow');
row.setAttribute("dndRepeaterId", this.id);
row.setAttribute("dndRowIndex", idx);
var dragSource = new dojo.dnd.HtmlDragSource(row, type);
row.style.cursor = "move";
}
}
if (this.select != "$no$") {
var row;
var widget = this;
for (var idx = 0; row = dojo.byId(this.id + "." + idx); idx++) {
var selectId = row.getAttribute("id") + "." + this.select + ":input";
var selectInput = dojo.byId(selectId);
if (!selectInput) {
throw "No select input found for row '" + row.getAttribute("id") + "'";
}
if (selectInput.checked) {
dojo.html.prependClass(row, "forms-row-selected");
}
(function() {
var localIdx = idx; // to use it in the closure
var localRow = row;
dojo.event.connect(row, "onclick", function(e) { widget.selectRow(e, localRow, localIdx) });
dojo.event.connect(row, "onmouseover", function(e) { dojo.html.prependClass(localRow, "forms-row-hover") });
dojo.event.connect(row, "onmouseout", function(e) { dojo.html.removeClass(localRow, "forms-row-hover") });
})()
}
}
},
beforeInsert: function(e, refNode, position) {
if (this.keepSourceInPlace(e, refNode, position)) {
e.dragObject.domNode = e.dragObject.domNode.cloneNode(true);
}
},
/**
* The dragged item should be copied instead of moved.
*
* return true - the item will be copied.
*/
keepSourceInPlace: function(e, refNode, position) {
var parts = e.dragObject.domNode.getAttribute("id").split('.');
var sourceRepeaterId = parts[0];
return sourceRepeaterId != this.id;
},
/**
* Return an object with predefined method to retrieve information
* about the source object to be inserted into this repeater
* @param e the event (has a "dragObject" property)
*/
makeDragSource: function(e) {
var repeater = this;
var result = {
_init: function(e) {
this.e = e;
this.sourceRowIndex = e.dragObject.domNode.getAttribute("dndRowIndex");
this.sourceRepeaterId = e.dragObject.domNode.getAttribute("dndRepeaterId");
},
getRowIdx: function() { return this.sourceRowIndex;},
getRepeaterId: function() { return this.sourceRepeaterId;},
isRepeater: function() { return e.dragObject.domNode.getAttribute("dndType") == 'repeaterRow';},
makeParameters: function() {
var res = {};
res[repeater.id + ".sourceRowIndex"] = this.sourceRowIndex;
res[repeater.id + ".sourceRepeaterId"] = this.sourceRepeaterId;
return res;
}
};
result._init(e);
return result;
},
/**
* Called after a dropped node has been inserted at its target position
* @param e the event (has a "dragObject" property)
* @param refNode the reference node for the insertion
* @param position the insertion position relative to refNode ("before" or "after")
*/
afterInsert: function(e, refNode, position) {
// Compute the row number before which to place the moved row
var targetRowIndex = refNode.getAttribute("dndRowIndex");
if (position == "after") {
targetRowIndex++;
}
var dragSource = this.makeDragSource(e);
if (dragSource.isRepeater(e)) {
var sourceRowIndex = dragSource.getRowIdx();
var sourceRepeaterId = dragSource.getRepeaterId();
dojo.debug("afterInsert: sourceRepeaterId=" + sourceRepeaterId);
dojo.debug("afterInsert: sourceRowIndex=" + sourceRowIndex);
}
dojo.debug("afterInsert: targetRepeaterId=" + this.id);
dojo.debug("afterInsert: targetRowIndex=" + targetRowIndex);
var params = dragSource.makeParameters();
params[this.id + ".before"] = targetRowIndex;
params["dndTarget.id"] = this.id;
// submit the form to update server-side model
if (sourceRepeaterId == this.id) {
// move
if (targetRowIndex == sourceRowIndex || targetRowIndex == sourceRowIndex + 1) {
return; // no change needed
}
var form = cocoon.forms.getForm(this.domNode);
params[this.id + ".action"] = "move";
params[this.id + ".from"] = sourceRowIndex;
dojo.widget.byId(form.getAttribute("dojoWidgetId")).submit(this.id, params);
} else if (this.getDndAction()) {
// Run dndAction
var form = cocoon.forms.getForm(this.domNode);
var dojoForm = dojo.widget.byId(form.getAttribute("dojoWidgetId"));
this.dndAction(dojoForm, dragSource, targetRowIndex, params);
}
},
/**
* Calls the server-side event to update the form model
* @param dojoForm the dojoForm
* @param dragSource the customized source reference node to be inserted into this repeater
* @param targeRowtIndex the row insertion index into this repeater
* @param params the parameter of the server-side event
*/
dndAction : function(dojoForm, dragSource, targetRowIndex, params) {
dojoForm.submit(this.getDndAction(), params);
}
});