use
5.006;
our
$VERSION
=
"1.6"
;
our
$TEMPLATE_SRC
;
sub
new {
my
$pkg
=
shift
;
my
$self
=
bless
({
button_label
=>
'Choose'
,
height
=> 0,
width
=> 300,
scrollbars
=> 0,
hide_selects
=> 1,
hide_textareas
=> 0,
indent_width
=> 25,
include_css
=> 1,
resizable
=> 0,
image_path
=>
"."
,
parent_var
=> 0,
@_
,
},
$pkg
);
$self
->{image_path} .=
"/"
unless
$self
->{image_path} =~ m!/$!;
foreach
my
$req
(
qw(name data title)
) {
croak(
"Missing required parameter '$req'"
)
unless
exists
$self
->{
$req
};
}
return
$self
;
}
sub
output {
my
(
$self
,
$template
) =
@_
;
$template
||= HTML::Template->new(
scalarref
=> \
$TEMPLATE_SRC
,
die_on_bad_params
=> 0,
global_vars
=> 1,
);
my
@loop
;
$self
->_output_node(
node
=>
$self
->{data},
loop
=> \
@loop
,
);
my
%param
= (
loop
=> \
@loop
,
map
{ (
$_
,
$self
->{
$_
}) }
qw(name height width
indent_width onselect
form_field form_field_form
button_label
button_image title
include_css resizable
image_path scrollbars
hide_selects hide_textareas
)
);
my
$output
;
if
(
$self
->can(
'_output_generate'
)) {
$output
=
$self
->_output_generate(
$template
, \
%param
);
}
else
{
$template
->param(
%param
);
$output
=
$template
->output;
}
return
$output
;
}
sub
_output_node {
my
(
$self
,
%arg
) =
@_
;
my
@nodes
;
if
(
ref
$arg
{node} eq
'ARRAY'
) {
@nodes
= @{
$arg
{node}};
}
else
{
@nodes
= (
$arg
{node});
}
for
my
$node
(
@nodes
) {
my
$id
= next_id();
push
@{
$arg
{loop}}, {
label
=>
$node
->{label},
value
=>
$node
->{value},
id
=>
$id
,
open
=>
$node
->{
open
} ? 1 : 0,
inactive
=>
$node
->{inactive} ? 1 : 0,
(
$self
->{parent_var} ?
(
parent
=> [
$arg
{parent} || () ]) :
()),
};
if
(
$node
->{children} and @{
$node
->{children}}) {
$arg
{loop}[-1]{has_children} = 1;
for
my
$child
(@{
$node
->{children}}) {
$self
->_output_node(
node
=>
$child
,
parent
=>
$node
,
loop
=>
$arg
{loop},
);
}
push
@{
$arg
{loop}}, {
end_block
=> 1 };
}
}
}
{
my
$id
= 1;
sub
next_id {
$id
++ }
}
$TEMPLATE_SRC
=
<<END;
<tmpl_if include_css><style type="text/css"><!--
/* style for the box around the widget */
.hpts-outer {
visibility: hidden;
position: absolute;
top: 0px;
left: 0px;
border: 2px outset #333333;
background-color: #ffffff;
filter: progid:DXImageTransform.Microsoft.dropShadow( Color=bababa,offx=3,offy=3,positive=true);
}
/* style for the box that contains the tree */
.hpts-inner {
<tmpl_if scrollbars>
overflow: scroll;
</tmpl_if>
width: <tmpl_var width>px;
<tmpl_if height>
height: <tmpl_var height>px;
</tmpl_if>
}
/* title bar style. The width here will define a minimum width for
the widget. */
.hpts-title {
padding: 2px;
margin-bottom: 4px;
font-size: large;
color: #ffffff;
background-color: #666666;
width: <tmpl_var width>px;
}
/* style of a block of child nodes - indents them under their parent
and starts them hidden */
.hpts-block {
margin-left: 24px;
display: none;
}
/* style for the button bar at the bottom of the widget */
.hpts-bbar {
padding: 3px;
text-align: right;
margin-top: 10px;
background-color: #666666;
width: <tmpl_var width>px;
}
/* style for the buttons at the bottom of the widget */
.hpts-button {
margin-left: 15px;
background-color: #ffffff;
color: #000000;
}
/* style for selected labels */
.hpts-label-selected {
background: #98ccfe;
}
/* style for labels after being unselected */
.hpts-label-unselected {
background: #ffffff;
}
/* style for bottom bar used for resizing */
.hpts-botbar {
background-color: #666666;
width: <tmpl_var width>px;
font-size: 7px;
padding: 3px;
}
--></style></tmpl_if>
<script type="text/javascript">
<!--
/* record location of mouse on each click */
var hpts_mouseX;
var hpts_mouseY;
var hpts_offsetX;
var hpts_offsetY;
var hpts_locked_titlebar; /* for moving */
var hpts_locked_botbar; /* for resizing */
var hpts_curr_width = <tmpl_if width><tmpl_var width><tmpl_else>225</tmpl_if>;
var hpts_curr_height = <tmpl_if height><tmpl_var height><tmpl_else>200</tmpl_if>;
document.onmousedown = hpts_lock;
document.onmousemove = hpts_drag;
document.onmouseup = hpts_release;
function hpts_lock(evt) {
evt = (evt) ? evt : event;
hpts_set_locked(evt);
hpts_update_mouse(evt);
if (hpts_locked_titlebar) {
if (evt.pageX) {
hpts_offsetX = evt.pageX - ((hpts_locked_titlebar.offsetLeft) ?
hpts_locked_titlebar.offsetLeft : hpts_locked_titlebar.left);
hpts_offsetY = evt.pageY - ((hpts_locked_titlebar.offsetTop) ?
hpts_locked_titlebar.offsetTop : hpts_locked_titlebar.top);
} else if (evt.offsetX || evt.offsetY) {
hpts_offsetX = evt.offsetX - ((evt.offsetX < -2) ?
0 : document.body.scrollLeft);
hpts_offsetY = evt.offsetY - ((evt.offsetY < -2) ?
0 : document.body.scrollTop);
} else if (evt.clientX) {
hpts_offsetX = evt.clientX - ((hpts_locked_titlebar.offsetLeft) ?
hpts_locked_titlebar.offsetLeft : 0);
hpts_offsetY = evt.clientY - ((hpts_locked_titlebar.offsetTop) ?
hpts_locked_titlebar.offsetTop : 0);
}
return false;
}
if (hpts_locked_botbar) {
if (evt.pageX) {
hpts_offsetX = evt.pageX;
hpts_offsetY = evt.pageY;
} else if (evt.clientX) {
hpts_offsetX = evt.clientX;
hpts_offsetY = evt.clientY;
} else if (evt.offsetX || evt.offsetY) {
hpts_offsetX = evt.offsetX - ((evt.offsetX < -2) ?
0 : document.body.scrollLeft);
hpts_offsetY = evt.offsetY - ((evt.offsetY < -2) ?
0 : document.body.scrollTop);
}
return false;
}
return true;
}
function hpts_update_mouse(evt) {
if (evt.pageX) {
hpts_mouseX = evt.pageX;
hpts_mouseY = evt.pageY;
} else {
hpts_mouseX = evt.clientX + document.documentElement.scrollLeft + document.body.scrollLeft;
hpts_mouseY = evt.clientY + document.documentElement.scrollTop + document.body.scrollTop;
}
}
function hpts_set_locked(evt) {
var target = (evt.target) ? evt.target : evt.srcElement;
if (target && target.className == "hpts-title") {
hpts_locked_titlebar = target.parentNode;
return;
} else if (target && target.className == "hpts-botbar") {
hpts_locked_botbar = target.parentNode;
return;
}
hpts_locked_titlebar = null;
hpts_locked_botbar = null;
return;
}
function hpts_drag(evt) {
evt = (evt) ? evt : event;
hpts_update_mouse(evt);
var titleobj = document.getElementById("<tmpl_var name>-title");
var innerobj = document.getElementById("<tmpl_var name>-inner");
var bbarobj = document.getElementById("<tmpl_var name>-bbar");
var botbarobj = document.getElementById("<tmpl_var name>-botbar");
if (hpts_locked_titlebar) {
hpts_locked_titlebar.style.left = (hpts_mouseX - hpts_offsetX) + "px";
hpts_locked_titlebar.style.top = (hpts_mouseY - hpts_offsetY) + "px";
evt.cancelBubble = true;
return false;
}
if (hpts_locked_botbar) {
titleobj.style.width = (hpts_curr_width + hpts_mouseX - hpts_offsetX) + "px";
innerobj.style.width = (hpts_curr_width + hpts_mouseX - hpts_offsetX) + "px";
bbarobj.style.width = (hpts_curr_width + hpts_mouseX - hpts_offsetX) + "px";
botbarobj.style.width = (hpts_curr_width + hpts_mouseX - hpts_offsetX) + "px";
innerobj.style.height = (hpts_curr_height + hpts_mouseY - hpts_offsetY) + "px";
evt.cancelBubble = true;
return false;
}
}
function hpts_release(evt) {
hpts_locked_titlebar = null;
if (hpts_locked_botbar){
var widthstr = document.getElementById("<tmpl_var name>-inner").style.width;
var heightstr = document.getElementById("<tmpl_var name>-inner").style.height;
hpts_curr_width = parseFloat(widthstr.substr(0,widthstr.indexOf("px")));
hpts_curr_height = parseFloat(heightstr.substr(0,heightstr.indexOf("px")));
}
hpts_locked_botbar = null;
}
var <tmpl_var name>_selected_id = -1;
var <tmpl_var name>_selected_val;
var <tmpl_var name>_selected_elem;
/* expand or collapse a sub-tree */
function <tmpl_var name>_toggle_expand(id) {
var obj = document.getElementById("<tmpl_var name>-desc-" + id);
var plus = document.getElementById("<tmpl_var name>-plus-" + id);
var node = document.getElementById("<tmpl_var name>-node-" + id);
if (obj.style.display != 'block') {
obj.style.display = 'block';
plus.src = "<tmpl_var image_path>minus.png";
node.src = "<tmpl_var image_path>open_node.png";
} else {
obj.style.display = 'none';
plus.src = "<tmpl_var image_path>plus.png";
node.src = "<tmpl_var image_path>closed_node.png";
}
}
/* select or unselect a node */
function <tmpl_var name>_toggle_select(id, val) {
if (<tmpl_var name>_selected_id != -1) {
/* turn off old selected value */
var old = document.getElementById("<tmpl_var name>-line-" + <tmpl_var name>_selected_id);
old.className = "hpts-label-unselected";
}
if (id == <tmpl_var name>_selected_id) {
/* clicked twice, turn it off and go back to nothing selected */
<tmpl_var name>_selected_id = -1;
} else {
/* turn on selected item */
var new_obj = document.getElementById("<tmpl_var name>-line-" + id);
new_obj.className = "hpts-label-selected";
<tmpl_var name>_selected_id = id;
<tmpl_var name>_selected_val = val;
}
}
/* it's showtime! */
function <tmpl_var name>_show() {
var obj = document.getElementById("<tmpl_var name>-outer");
var x = Math.floor(hpts_mouseX - (hpts_curr_width/2));
x = (x > 2 ? x : 2);
var y = Math.floor(hpts_mouseY - (hpts_curr_height/5 * 4));
y = (y > 2 ? y : 2);
document.getElementById('<tmpl_var name>-inner').style.overflow = 'auto'; /*hack FF(OS X)*/
obj.style.left = x + "px";
obj.style.top = y + "px";
obj.style.visibility = "visible";
<tmpl_if hide_selects>
for(var f = 0; f < document.forms.length; f++) {
for(var x = 0; x < document.forms[f].elements.length; x++) {
var e = document.forms[f].elements[x];
if (e.options) {
e.style.visibility = "hidden";
}
}
}
</tmpl_if>
<tmpl_if hide_textareas>
for(var f = 0; f < document.forms.length; f++) {
for(var x = 0; x < document.forms[f].elements.length; x++) {
var e = document.forms[f].elements[x];
if (e.rows) {
e.style.visibility = "hidden";
}
}
}
</tmpl_if>
}
/* user clicks the ok button */
function <tmpl_var name>_ok() {
if (<tmpl_var name>_selected_id == -1) {
/* ahomosezwha? */
alert("Please select an item or click Cancel to cancel selection.");
return;
}
/* fill in a form field if they spec'd one */
<tmpl_if form_field><tmpl_if form_field_form>document.forms["<tmpl_var form_field_form>"]<tmpl_else>document.forms[0]</tmpl_if>.elements["<tmpl_var form_field>"].value = <tmpl_var name>_selected_val;</tmpl_if>
/* trigger onselect */
<tmpl_if onselect><tmpl_var onselect>(<tmpl_var name>_selected_val)</tmpl_if>
<tmpl_var name>_close();
}
function <tmpl_var name>_cancel() {
<tmpl_var name>_close();
}
function <tmpl_var name>_close () {
document.getElementById('<tmpl_var name>-inner').style.overflow = 'hidden'; /*hack FF(OS X)*/
/* hide window */
var obj = document.getElementById("<tmpl_var name>-outer");
obj.style.visibility = "hidden";
/* clear selection */
if (<tmpl_var name>_selected_id != -1) {
<tmpl_var name>_toggle_select(<tmpl_var name>_selected_id);
}
<tmpl_if hide_selects>
for(var f = 0; f < document.forms.length; f++) {
for(var x = 0; x < document.forms[f].elements.length; x++) {
var e = document.forms[f].elements[x];
if (e.options) {
e.style.visibility = "visible";
}
}
}
</tmpl_if>
<tmpl_if hide_textareas>
for(var f = 0; f < document.forms.length; f++) {
for(var x = 0; x < document.forms[f].elements.length; x++) {
var e = document.forms[f].elements[x];
if (e.rows) {
e.style.visibility = "visible";
}
}
}
</tmpl_if>
}
//-->
</script>
<div id="<tmpl_var name>-outer" class="hpts-outer">
<div class="hpts-title" id="<tmpl_var name>-title"><tmpl_var title></div>
<div class="hpts-inner" id="<tmpl_var name>-inner">
<tmpl_loop loop>
<tmpl_unless end_block>
<div style="white-space:nowrap">
<tmpl_if has_children>
<img alt="" id="<tmpl_var name>-plus-<tmpl_var id>" width=16 height=16 src="<tmpl_var image_path><tmpl_if open>minus<tmpl_else>plus</tmpl_if>.png" onclick="<tmpl_var name>_toggle_expand(<tmpl_var id>)"><span id="<tmpl_var name>-line-<tmpl_var id>" <tmpl_unless inactive>ondblclick="<tmpl_var name>_toggle_expand(<tmpl_var id>)" onclick="<tmpl_var name>_toggle_select(<tmpl_var id>, '<tmpl_var escape=html value>')"</tmpl_unless>>
<tmpl_else>
<img alt="" width=16 height=16 src="<tmpl_var image_path>L.png"><span id="<tmpl_var name>-line-<tmpl_var id>" <tmpl_unless inactive>onclick="<tmpl_var name>_toggle_select(<tmpl_var id>, '<tmpl_var escape=html value>')"</tmpl_unless>>
</tmpl_if>
<img id="<tmpl_var name>-node-<tmpl_var id>" width=16 height=16 src="<tmpl_var image_path>closed_node.png" alt="">
<tmpl_unless inactive><a href="javascript:void(0);"></tmpl_unless><tmpl_var label><tmpl_unless inactive></a></tmpl_unless>
</span>
</div>
<tmpl_if has_children>
<div id="<tmpl_var name>-desc-<tmpl_var id>" class="hpts-block" style="white-space: nowrap; display: <tmpl_if open>block<tmpl_else>none</tmpl_if>">
</tmpl_if>
<tmpl_else>
</div>
</tmpl_unless>
</tmpl_loop>
</div>
<div class="hpts-bbar" id="<tmpl_var name>-bbar" style="white-space:nowrap">
<input class="hpts-button" type="button" value=" Ok " onclick="<tmpl_var name>_ok()">
<input class="hpts-button" type="button" value="Cancel" onclick="<tmpl_var name>_cancel()">
</div>
<tmpl_if resizable> <div id="<tmpl_var name>-botbar" class="hpts-botbar"> </div></tmpl_if>
</div>
<input class="hpts-button" type="button" value="<tmpl_var button_label>" onmouseup="<tmpl_var name>_show()">
END
1;