Cleaning the js in the skills tree, adding a javascript/skills.js file see #1791
parent
53a7d3d460
commit
5721da93f6
@ -0,0 +1,173 @@ |
||||
var exampleDropOptions = { |
||||
tolerance:'touch', |
||||
hoverClass:'dropHover', |
||||
activeClass:'dragActive' |
||||
}; |
||||
|
||||
var connectorPaintStyle = { |
||||
lineWidth:5, |
||||
strokeStyle:"#deea18", |
||||
joinstyle:"round" |
||||
}; |
||||
// .. and this is the hover style.
|
||||
var connectorHoverStyle = { |
||||
lineWidth:7, |
||||
strokeStyle:"#2e2aF8" |
||||
}; |
||||
|
||||
|
||||
//Settings when editing stuff
|
||||
var edit_arrow_color = '#ccc';
|
||||
|
||||
var editEndpoint = {
|
||||
//connectorStyle:connectorPaintStyle,
|
||||
connector:[ "Flowchart", { stub:28 } ], |
||||
hoverPaintStyle:connectorHoverStyle, |
||||
connectorHoverStyle:connectorHoverStyle, |
||||
anchors: ['BottomCenter','TopCenter'],
|
||||
endpoint:"Rectangle", |
||||
paintStyle:{ width:10, height:10, fillStyle:edit_arrow_color }, |
||||
isSource:true, |
||||
scope:'blue rectangle', |
||||
maxConnections:10, |
||||
connectorStyle : { |
||||
gradient:{ |
||||
stops:[[0, edit_arrow_color], [0.5, edit_arrow_color], [1, edit_arrow_color]] |
||||
}, //gradient stuff
|
||||
lineWidth:2, |
||||
strokeStyle: edit_arrow_color |
||||
}, |
||||
isTarget:true, |
||||
dropOptions : exampleDropOptions |
||||
}; |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* Clean window block classes*/ |
||||
function cleanclass(obj) { |
||||
obj.removeClass('first_window'); |
||||
obj.removeClass('second_window'); |
||||
obj.removeClass('third_window'); |
||||
} |
||||
|
||||
function open_parent(parent_id, id) {
|
||||
console.log("open_parent call : id " + id + " parent_id:" + parent_id);
|
||||
var numeric_parent_id = parent_id.split('_')[1]; |
||||
var numeric_id = id.split('_')[1];
|
||||
load_parent(numeric_parent_id, numeric_id); |
||||
} |
||||
|
||||
//open block function
|
||||
function open_block(id) { |
||||
console.log("open_block id : " + id);
|
||||
var numeric_id = id.split('_')[1];
|
||||
for (var i = 0; i < skills.length; i++) { |
||||
//Remove everything except parents
|
||||
if (jQuery.inArray(skills[i].element, parents) == -1) { |
||||
//console.log('deleting this'+ skills[i].element);
|
||||
jsPlumb.deleteEndpoint(skills[i].endp); |
||||
$("#"+skills[i].element).remove(); |
||||
//skills.splice(i,1);
|
||||
//console.log('Removing '+skills[i].element);
|
||||
} |
||||
} |
||||
/*if ($('#'+id).length == 0) { |
||||
$('body').append('<div id="'+id+'" class="open_block window " >'+id+'</div>');
|
||||
}*/ |
||||
|
||||
//Modifying current block position
|
||||
pos = $('#'+id).position();
|
||||
left_value = center_x;
|
||||
|
||||
if (parents.length == 2) {
|
||||
top_value = space_between_blocks_y + offset_y; |
||||
} else { |
||||
top_value = pos.left; |
||||
} |
||||
jsPlumb.animate(id, { left: left_value, top:top_value }, { duration:duration_value });
|
||||
|
||||
//Modifying root block position
|
||||
pos_parent = $('#'+parents[0]).position(); |
||||
jsPlumb.animate(parents[0], { left: center_x, top:offset_y }, { duration:duration_value });
|
||||
|
||||
top_value = 2*space_between_blocks_y +offset_y ;
|
||||
load_children(numeric_id, top_value);
|
||||
} |
||||
|
||||
function load_parent(parent_id, id) { |
||||
console.log("load_parent call : id " + id + " parent_id:" + parent_id); |
||||
var ix= 0;
|
||||
$.ajax({ |
||||
url: url+'&a=load_direct_parents&id='+id, |
||||
async: false,
|
||||
success: function(json) {
|
||||
var json = jQuery.parseJSON(json); |
||||
|
||||
$.each(json,function(i,item) { |
||||
left_value = center_x + space_between_blocks_x * ix; |
||||
top_value = offset_y; |
||||
|
||||
$('body').append('<div id="block_'+item.id+ '" class="open_block window " >'+item.name+'</div>');
|
||||
var es = prepare("block_" + item.id, editEndpoint); |
||||
var es2 = prepare("block_" + id, editEndpoint); |
||||
|
||||
jsPlumb.connect({ |
||||
source: es, target:es2
|
||||
});
|
||||
jsPlumb.animate("block_" + item.id, { left: left_value, top : top_value }, { duration : duration_value}); |
||||
|
||||
if (item.parent_id) { |
||||
console.log('setting hidden_parent '+item.parent_id); |
||||
hidden_parent = "block_" + item.parent_id;
|
||||
} else { |
||||
console.log('setting NO--- hidden_parent '); |
||||
} |
||||
ix++;
|
||||
}); |
||||
|
||||
} |
||||
}); |
||||
} |
||||
|
||||
function load_children(my_id, top_value) { |
||||
console.log("load_children call : my_id " + my_id + " top_value:" + top_value);
|
||||
//Loading children
|
||||
var ix = 0; |
||||
$.getJSON(url+'&a=load_children&id='+my_id, {},
|
||||
function(json) {
|
||||
$.each(json,function(i,item) {
|
||||
left_value = ix*space_between_blocks_x + center_x/2 - block_size / 2; |
||||
//top_value = 300;
|
||||
//Display::url($skill['name'], '#', array('id'=>'edit_block_'.$block_id, 'class'=>'edit_block'))
|
||||
item.name = '<a href="#" class="edit_block" id="edit_block_'+item.id+'">'+item.name+'</a>';
|
||||
|
||||
$('body').append('<div id="block_'+item.id+ '" class="third_window open_block window " >'+item.name+'</div>');
|
||||
|
||||
var es = prepare("block_" + item.id, editEndpoint); |
||||
var e2 = prepare("block_" + my_id, editEndpoint); |
||||
|
||||
jsPlumb.connect({ |
||||
source: es, target:e2 |
||||
});
|
||||
jsPlumb.animate("block_" + item.id, { |
||||
left: left_value, top : top_value |
||||
}, { duration:duration_value}); |
||||
ix++;
|
||||
}); |
||||
} |
||||
); |
||||
} |
||||
|
||||
function checkLength( o, n, min, max ) { |
||||
if ( o.val().length > max || o.val().length < min ) { |
||||
o.addClass( "ui-state-error" ); |
||||
updateTips( "Length of " + n + " must be between " + |
||||
min + " and " + max + "." ); |
||||
return false; |
||||
} else { |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
@ -1 +1,46 @@ |
||||
ski |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
/** |
||||
* @package chamilo.admin |
||||
*/ |
||||
|
||||
// Language files that need to be included. |
||||
$language_file = array('admin'); |
||||
|
||||
$cidReset = true; |
||||
require_once '../inc/global.inc.php'; |
||||
require_once api_get_path(LIBRARY_PATH).'skill.lib.php'; |
||||
require_once api_get_path(LIBRARY_PATH).'skill.visualizer.lib.php'; |
||||
|
||||
$this_section = SECTION_MYPROFILE; |
||||
|
||||
//api_protect_admin_script(); |
||||
|
||||
//Adds the JS needed to use the jqgrid |
||||
$htmlHeadXtra[] = api_get_jquery_ui_js(true); |
||||
$htmlHeadXtra[] = api_get_js('jquery.jsPlumb.all.js'); |
||||
$htmlHeadXtra[] = api_get_js('skills.js'); |
||||
|
||||
$skill = new Skill(); |
||||
$skills = $skill->get_all(true); |
||||
$type = 'edit'; //edit |
||||
|
||||
$tree = $skill->get_skills_tree(null, true); |
||||
$skill_visualizer = new SkillVisualizer($tree, $type); |
||||
|
||||
$html = $skill_visualizer->return_html(); |
||||
$url = api_get_path(WEB_AJAX_PATH).'skill.ajax.php?1=1'; |
||||
|
||||
$tpl = new Template(null, false, false); |
||||
|
||||
$tpl->assign('url', $url); |
||||
$tpl->assign('html', $html); |
||||
$tpl->assign('skill_visualizer', $skill_visualizer); |
||||
$tpl->assign('js', $skill_visualizer->return_js()); |
||||
|
||||
// |
||||
$content = $tpl->fetch('default/skill/skill_tree_student.tpl'); |
||||
$tpl->assign('content', $content); |
||||
$tpl->display_no_layout_template(); |
||||
|
||||
@ -0,0 +1,416 @@ |
||||
<script type="text/javascript"> |
||||
|
||||
//js settings |
||||
var url = '{$url}'; |
||||
var skills = []; //current window divs |
||||
var parents = []; //list of parents normally there should be only 2 |
||||
var hidden_parent = ''; |
||||
var duration_value = 500; |
||||
|
||||
|
||||
//Block settings see the SkillVisualizer Class |
||||
var offset_x = {$skill_visualizer->offset_x}; |
||||
var offset_y = {$skill_visualizer->offset_y}; |
||||
var space_between_blocks_x = {$skill_visualizer->space_between_blocks_x}; |
||||
var space_between_blocks_y = {$skill_visualizer->space_between_blocks_y}; |
||||
var center_x = {$skill_visualizer->center_x}; |
||||
var block_size = {$skill_visualizer->block_size}; |
||||
|
||||
//Setting the parent by default |
||||
var parents = ['block_1']; |
||||
|
||||
/* Clean window block classes*/ |
||||
function cleanclass(obj) { |
||||
obj.removeClass('first_window'); |
||||
obj.removeClass('second_window'); |
||||
obj.removeClass('third_window'); |
||||
} |
||||
|
||||
jsPlumb.bind("ready", function() { |
||||
|
||||
//Open dialog |
||||
$("#dialog-form").dialog({ |
||||
autoOpen: false, |
||||
modal : true, |
||||
width : 550, |
||||
height : 480, |
||||
}); |
||||
|
||||
|
||||
//On box click -(we use live instead of bind because we're creating divs on the fly ) |
||||
$(".open_block").live('click', function() { |
||||
var id = $(this).attr('id'); |
||||
|
||||
//if is root |
||||
if (parents[0] == id) { |
||||
parents = [id]; |
||||
} |
||||
|
||||
if (parents[1] != id) { |
||||
if (parents.length == 2 ) { |
||||
hidden_parent = parents[0]; |
||||
//console.log('deleting: '+parents[0]); |
||||
//removing father |
||||
for (var i = 0; i < skills.length; i++) { |
||||
|
||||
if ( skills[i].element == parents[0] ) { |
||||
//console.log('deleting :'+ skills[i].element + ' here '); |
||||
jsPlumb.deleteEndpoint(skills[i].endp); |
||||
$("#"+skills[i].element).remove(); |
||||
//skills.splice(i,1) |
||||
} |
||||
} |
||||
parents.splice(0,1); |
||||
parents.push(id); |
||||
} |
||||
|
||||
if ($(this).hasClass('first_window')) { |
||||
//show the hidden_parent |
||||
//if (hidden_parent != '') { |
||||
parents = [hidden_parent, id]; |
||||
// console.log(parents); |
||||
open_parent(hidden_parent, id); |
||||
//} |
||||
} |
||||
if (jQuery.inArray(id, parents) == -1) { |
||||
parents.push(id); |
||||
} |
||||
open_block(id); |
||||
} |
||||
|
||||
//Setting class |
||||
cleanclass($(this)); |
||||
$(this).addClass('second_window'); |
||||
|
||||
parent_div = $("#"+parents[0]); |
||||
cleanclass(parent_div); |
||||
parent_div.addClass('first_window'); |
||||
|
||||
parent_div = $("#"+parents[1]); |
||||
cleanclass(parent_div); |
||||
parent_div.addClass('second_window'); |
||||
|
||||
console.log(parents); |
||||
// console.log(skills); |
||||
console.log('hidden_parent : ' + hidden_parent); |
||||
}); |
||||
|
||||
|
||||
|
||||
$(".edit_block").live('click',function() { |
||||
var my_id = $(this).attr('id'); |
||||
my_id = my_id.split('_')[2]; |
||||
|
||||
//Cleaning selected |
||||
$("#parent_id option:selected").removeAttr('selected'); |
||||
$("#gradebook_id option:selected").removeAttr('selected'); |
||||
|
||||
$.ajax({ |
||||
url: url+'&a=get_skill_info&id='+my_id, |
||||
success: function(json) { |
||||
var skill = jQuery.parseJSON(json); |
||||
$("#name").attr('value', skill.name); |
||||
$("#id").attr('value', skill.id); |
||||
$("#description").attr('value', skill.description); |
||||
//filling parent_id |
||||
$("#parent_id option[value='"+skill.extra.parent_id+"']").attr('selected', 'selected'); |
||||
//filling the gradebook_id |
||||
jQuery.each(skill.gradebooks, function(index, data) { |
||||
$("#gradebook_id option[value='"+data.id+"']").attr('selected', 'selected'); |
||||
}); |
||||
}, |
||||
}); |
||||
|
||||
|
||||
$("#gradebook_id").trigger("liszt:updated"); |
||||
$("#parent_id").trigger("liszt:updated"); |
||||
|
||||
|
||||
$("#dialog-form").dialog("open"); |
||||
return false; |
||||
}); |
||||
|
||||
//Filling select |
||||
$("#add_item_link").click(function() { |
||||
|
||||
$("#name").attr('value', ''); |
||||
$("#description").attr('value', ''); |
||||
|
||||
|
||||
$("#dialog-form").dialog("open"); |
||||
|
||||
}); |
||||
|
||||
|
||||
|
||||
$("#dialog-form").dialog({ |
||||
close: function() { |
||||
$("#name").attr('value', ''); |
||||
$("#description").attr('value', ''); |
||||
} |
||||
}); |
||||
|
||||
// chrome fix. |
||||
document.onselectstart = function () { return false; }; |
||||
|
||||
// render mode |
||||
var resetRenderMode = function(desiredMode) { |
||||
var newMode = jsPlumb.setRenderMode(desiredMode); |
||||
jsPlumbDemo.init(); |
||||
}; |
||||
resetRenderMode(jsPlumb.CANVAS); |
||||
}); |
||||
|
||||
|
||||
|
||||
;(function() { |
||||
|
||||
prepare = function(elId, endpoint) { |
||||
jsPlumbDemo.initHover(elId); |
||||
//jsPlumbDemo.initAnimation(elId); |
||||
var e = jsPlumb.addEndpoint(elId, endpoint); |
||||
jsPlumbDemo.initjulio(e); |
||||
skills.push({ |
||||
element:elId, endp:e |
||||
}); |
||||
return e; |
||||
}, |
||||
|
||||
window.jsPlumbDemo = { |
||||
initjulio :function(e) { |
||||
}, |
||||
initHover :function(elId) { |
||||
|
||||
$("#" + elId).click(function() { |
||||
var all = jsPlumb.getConnections({ |
||||
source:elId |
||||
}); |
||||
}); |
||||
|
||||
/*$("#" + elId).hover( |
||||
function() { $(this).addClass("bigdot-hover"); }, |
||||
function() { $(this).removeClass("bigdot-hover"); } |
||||
);*/ |
||||
}, |
||||
init : function() { |
||||
|
||||
jsPlumb.Defaults.DragOptions = { cursor: 'pointer', zIndex:2000 }; |
||||
jsPlumb.Defaults.PaintStyle = { strokeStyle:'#666' }; |
||||
jsPlumb.Defaults.EndpointStyle = { width:20, height:16, strokeStyle:'#666' }; |
||||
jsPlumb.Defaults.Endpoint = "Rectangle"; |
||||
jsPlumb.Defaults.Anchors = ["TopCenter", "TopCenter"]; |
||||
|
||||
var connections = []; |
||||
var updateConnections = function(conn, remove) { |
||||
if (!remove) connections.push(conn); |
||||
else { |
||||
var idx = -1; |
||||
for (var i = 0; i < connections.length; i++) { |
||||
if (connections[i] == conn) { |
||||
idx = i; break; |
||||
} |
||||
} |
||||
if (idx != -1) connections.splice(idx, 1); |
||||
} |
||||
if (connections.length > 0) { |
||||
var s = "<span>current connections</span><br/><br/><table><tr><th>scope</th><th>source</th><th>target</th></tr>"; |
||||
for (var j = 0; j < connections.length; j++) { |
||||
s = s + "<tr><td>" + connections[j].scope + "</td>" + "<td>" + connections[j].sourceId + "</td><td>" + connections[j].targetId + "</td></tr>"; |
||||
} |
||||
jsPlumbDemo.showConnectionInfo(s); |
||||
} else |
||||
jsPlumbDemo.hideConnectionInfo(); |
||||
}; |
||||
|
||||
jsPlumb.bind("jsPlumbConnection", function(e) { |
||||
updateConnections(e.connection); |
||||
}); |
||||
jsPlumb.bind("jsPlumbConnectionDetached", function(e) { |
||||
updateConnections(e.connection, true); |
||||
}); |
||||
|
||||
|
||||
|
||||
/** |
||||
first example endpoint. it's a 25x21 rectangle (the size is provided in the 'style' arg to the Endpoint), and it's both a source |
||||
and target. the 'scope' of this Endpoint is 'exampleConnection', meaning any connection starting from this Endpoint is of type |
||||
'exampleConnection' and can only be dropped on an Endpoint target that declares 'exampleEndpoint' as its drop scope, and also that |
||||
only 'exampleConnection' types can be dropped here. |
||||
|
||||
the connection style for this endpoint is a Bezier curve (we didn't provide one, so we use the default), with a lineWidth of |
||||
5 pixels, and a gradient. |
||||
|
||||
note the use of the '$.extend' function to setup generic connection types. this will save you a lot of typing, and probably |
||||
errors. |
||||
|
||||
*/ |
||||
// this is the paint style for the connecting lines.. |
||||
|
||||
|
||||
jsPlumb.Defaults.Overlays = [ |
||||
//[ "Arrow", { location:0.5 } ], if you want to add an arrow in the connection |
||||
]; |
||||
|
||||
|
||||
|
||||
jsPlumb.setMouseEventsEnabled(true); |
||||
|
||||
//Default |
||||
var default_arrow_color = '#ccc'; |
||||
var defaultEndpoint = { |
||||
anchors: ['BottomCenter','TopCenter'], |
||||
endpoint:"Rectangle", |
||||
paintStyle:{ width:1, height:1, fillStyle:default_arrow_color }, |
||||
isSource:false, |
||||
scope:'blue rectangle', |
||||
maxConnections:10, |
||||
connectorStyle : { |
||||
gradient:{ stops:[[0, default_arrow_color], [0.5, default_arrow_color], [1, default_arrow_color]] }, |
||||
lineWidth:5, |
||||
strokeStyle:default_arrow_color |
||||
}, |
||||
isTarget:false, |
||||
setDraggableByDefault : false, |
||||
}; |
||||
|
||||
// Done end point |
||||
var done_arrow_color = '#73982C'; |
||||
var doneEndpoint = { |
||||
endpoint:"Rectangle", |
||||
paintStyle:{ width:1, height:1, fillStyle:done_arrow_color}, |
||||
isSource:false, |
||||
scope:'blue rectangle', |
||||
maxConnections:10, |
||||
connectorStyle : { |
||||
gradient:{ stops:[[0, done_arrow_color], [0.5, done_arrow_color], [1, done_arrow_color]] }, |
||||
lineWidth:5, |
||||
strokeStyle:done_arrow_color |
||||
}, |
||||
isTarget:false, |
||||
setDraggableByDefault : false, |
||||
}; |
||||
|
||||
|
||||
{$js} |
||||
// three ways to do this - an id, a list of ids, or a selector (note the two different types of selectors shown here...anything that is valid jquery will work of course) |
||||
//jsPlumb.draggable("window1"); |
||||
//jsPlumb.draggable(["window1", "window2"]); |
||||
//jsPlumb.draggable($("#window1")); |
||||
var divsWithWindowClass = jsPlumbDemo.getSelector(".window"); |
||||
jsPlumb.draggable(divsWithWindowClass); |
||||
|
||||
// add the third example using the '.window' class. |
||||
//jsPlumb.addEndpoint(divsWithWindowClass, exampleEndpoint3); |
||||
|
||||
// each library uses different syntax for event stuff, so it is handed off |
||||
// to the draggableConnectorsDemo-<library>.js files. |
||||
jsPlumbDemo.attachBehaviour(); |
||||
} |
||||
}; |
||||
})(); |
||||
|
||||
$(document).ready( function() { |
||||
//When creating a connection see |
||||
//http://jsplumb.org/apidocs/files/jsPlumb-1-3-2-all-js.html#bind |
||||
jsPlumb.bind("jsPlumbConnection", function(conn) { |
||||
//alert("Connection created " + conn.sourceId + " to " + conn.targetId + " "); |
||||
//jsPlumb.detach(conn); |
||||
}); |
||||
//When double clicking a connection |
||||
jsPlumb.bind("click", function(conn) { |
||||
if (confirm("Delete connection from " + conn.sourceId + " to " + conn.targetId + "?")) |
||||
jsPlumb.detach(conn); |
||||
}); |
||||
|
||||
//When double clicking a connection |
||||
jsPlumb.bind("click", function(endpoint) { |
||||
if (confirm("Delete connection from " + conn.sourceId + " to " + conn.targetId + "?")) |
||||
jsPlumb.detach(conn); |
||||
}); |
||||
$(".chzn-select").chosen(); |
||||
}); |
||||
|
||||
;(function() { |
||||
|
||||
jsPlumbDemo.showConnectionInfo = function(s) { |
||||
$("#list").html(s); |
||||
$("#list").fadeIn({ complete:function() { jsPlumb.repaintEverything(); }}); |
||||
}; |
||||
|
||||
jsPlumbDemo.hideConnectionInfo = function() { |
||||
$("#list").fadeOut({ complete:function() { jsPlumb.repaintEverything(); }}); |
||||
}; |
||||
|
||||
jsPlumbDemo.getSelector = function(spec) { |
||||
return $(spec); |
||||
}; |
||||
|
||||
jsPlumbDemo.attachBehaviour = function() { |
||||
$(".hide").click(function() { |
||||
jsPlumb.toggle($(this).attr("rel")); |
||||
}); |
||||
|
||||
$(".drag").click(function() { |
||||
var s = jsPlumb.toggleDraggable($(this).attr("rel")); |
||||
$(this).html(s ? 'disable dragging' : 'enable dragging'); |
||||
if (!s) $("#" + $(this).attr("rel")).addClass('drag-locked'); else $("#" + $(this).attr("rel")).removeClass('drag-locked'); |
||||
$("#" + $(this).attr("rel")).css("cursor", s ? "pointer" : "default"); |
||||
}); |
||||
|
||||
$(".detach").click(function() { |
||||
jsPlumb.detachAll($(this).attr("rel")); |
||||
}); |
||||
|
||||
$("#clear").click(function() { |
||||
jsPlumb.detachEverything(); showConnections(); |
||||
}); |
||||
}; |
||||
})(); |
||||
|
||||
</script> |
||||
|
||||
{$html} |
||||
|
||||
<div id="dialog-form" style="display:none;"> |
||||
|
||||
<form id="add_item" name="form"> |
||||
<input type="hidden" name="id" id="id"/> |
||||
<div class="row"> |
||||
<div class="label"> |
||||
<label for="name">Name</label> |
||||
</div> |
||||
<div class="formw"> |
||||
<input type="text" name="name" id="name" size="40" /> |
||||
</div> |
||||
</div> |
||||
<div class="row"> |
||||
<div class="label"> |
||||
<label for="name">Parent</label> |
||||
</div> |
||||
<div class="formw"> |
||||
<select id="parent_id" name="parent_id" /> |
||||
</select> |
||||
</div> |
||||
</div> |
||||
<div class="row"> |
||||
<div class="label"> |
||||
<label for="name">Gradebook</label> |
||||
</div> |
||||
<div class="formw"> |
||||
<select id="gradebook_id" name="gradebook_id[]" multiple="multiple"/> |
||||
</select> |
||||
<span class="help-block"> |
||||
Gradebook Description |
||||
</span> |
||||
</div> |
||||
</div> |
||||
<div class="row"> |
||||
<div class="label"> |
||||
<label for="name">Description</label> |
||||
</div> |
||||
<div class="formw"> |
||||
<textarea name="description" id="description" cols="40" rows="7"></textarea> |
||||
</div> |
||||
</div> |
||||
</form> |
||||
</div> |
||||
Loading…
Reference in new issue