Listing 1: Ajax_MEMORY_TREE Package Body

create or replace package body ajax_memory_tree
as
-------------------------------------------------------------
-- Ajax Memory Tree Example                                --
-- Scott Spendolini, Sumner Technologies, LLC              --
-- http://sumnertech.com                                   --
-- info@sumnertech.com                                     --
-- Thanks to Carl Backstrom for the original Ajax Tree     --
-------------------------------------------------------------
-------------------- I N I T
procedure init
is
  l_count number;
  l_id number;
begin
-- This procedure should only be called one time per session
-- immediately after the user logs in
-- Remove all old session info from tree_temp
delete from tree_temp where app_user = v('APP_USER') and app_session != v('APP_SESSION');
-- Ensure that there is only one top level node
select count(*) into l_count from tree_view where pid is null;
if l_count = 1 then
  -- Get the Top Level of the tree
  select id into l_id from tree_view where pid is null;
  -- Seed the tree_temp table with the top level node
  for x in (select * from tree_view where id = l_id)
  loop
    insert into tree_temp values (null, v('APP_USER'), v('APP_SESSION'), x.pid, x.id, x.name, 'Y', 'plus');
  end loop;
  -- Set P1_TREE_ROOT to the value of the top node
  htmldb_util.set_session_state('P1_TREE_ROOT', l_id);
  -- Set the initially selected node to 1 as well
  htmldb_util.set_session_state('P1_SELECTED_NODE', l_id);
else
  -- The data in the table will not work with this tree
  htp.p('<b>ERROR: You have more than 1 row in your table with a NULL Parent ID.</b>');
end if;
end init;
-------------------- P L U S _ M I N U S
procedure plus_minus
is
  l_icon varchar2(100);
  l_app_user varchar2(255) := v('APP_USER');
  l_app_session number := v('APP_SESSION');
  l_branch_id number := v('P1_BRANCH_ID');
begin
-- Get and change the icon from + to - or from - to plus, based on what the previous value was
select icon into l_icon from tree_temp
  where id = l_branch_id and app_user = l_app_user and app_session = l_app_session;
if l_icon = 'plus' then
  update tree_temp set icon = 'minus'
    where id = l_branch_id and app_user = l_app_user and app_session = l_app_session;
else
  update tree_temp set icon = 'plus'
    where id = l_branch_id and app_user = l_app_user and app_session = l_app_session;
end if;
end plus_minus;
-------------------- R E N D E R _ B R A N C H
procedure render_branch
  (p_type in varchar2, p_icon in varchar2, p_class in varchar2, p_id in number, p_pid in number, p_name in varchar2)
is
begin
-- If the branch has sub nodes, render it with a +/- sign
if p_type = 'exp' then
  htp.prn('<li style="vertical-align:middle;"><img style="cursor:pointer;margin-right:5px;"
         src="/i/' || p_icon || '.gif" onClick="getTreeNode(this,''' || p_id || ''')" />
		 <a class="' || p_class || '" href="f?p=' ||
		 v('APP_ID') || ':1:' || v('APP_SESSION') || '::::P1_SELECTED_NODE:' || p_id || '">' || p_name ||
		 '</a></span></li>');
-- Otherwise, just render the item
else
  htp.prn('<li style="vertical-align:middle;margin-left:5px;"><img src="/i/wwv_bullet.gif" style="padding-right:5px;"/><a 
  class="' || p_class || '" href="f?p=' || v('APP_ID') || ':1:' || v('APP_SESSION') ||
	 '::::P1_SELECTED_NODE:' || p_id || '">' || p_name || '</a></span></li>');
end if;
end render_branch;
-------------------- R E N D E R
procedure render
is
  l_level number := 1;
  l_class varchar2(2000);
begin
-- Render the Tree
htp.prn('<ul id="treeTrunk" style="list-style:none;">');
for x in
  (select pid, id, name, exp, icon, level from 
    (select * from tree_temp where app_user = v('APP_USER'))
   connect by prior id = pid
   start with id = v('P1_TREE_ROOT'))
loop
  -- Determine if the node being rendered is the Selected Node
  if v('P1_SELECTED_NODE') = x.id then
    l_class := 'selected_node';
  else
    l_class := 'unselected_node';
  end if;
  -- If this node is a new level, then start a new UL
  if x.level > l_level then
    htp.prn('<ul id="branch' || x.pid || '" style="list-style:none;">');
  elsif x.level < l_level then
    -- Otherwise, close the existing UL, accounting for multiple skipped levels
    for y in 1..(l_level - x.level)
	loop
	  htp.prn('</ul>');
	end loop;
  end if;
  -- If this node has children, then print out the + or - control
  if x.exp = 'Y' or x.pid is null then
	render_branch('exp', x.icon, l_class, x.id, x.pid, x.name);
  else
    -- Otherwise, just print the node name
	render_branch('no', x.icon, l_class, x.id, x.pid, x.name);
  end if;
  l_level := x.level;
end loop;
htp.prn('</ul>');
end render;
-------------------- F I N D _ N O D E
procedure find_node (p_id in varchar2)
is
  l_app_user varchar2(255) := v('APP_USER');
  l_exp char(1);
  l_icon varchar2(10);
  l_count number;
  l_fy varchar2(10) := v('P1_FY');
  cursor tree_cur is
   select pid, id, name
    from tree_view
    group by pid, id, name
    connect by prior pid = id
	start with id = p_id;
  tree_rec tree_cur%rowtype;
begin
-- Clear out the tree entries for a user and see it with the initial node
delete from tree_temp where app_user = l_app_user;
for x in (select * from tree_view where pid is null)
loop
    insert into tree_temp values (null, l_app_user, v('APP_SESSION'), x.pid, x.id, x.name, 'Y', 'minus');
end loop;
-- Loop through the cursor and build the tree for the selected node
open tree_cur;
loop
fetch tree_cur into tree_rec;
exit when tree_cur%notfound;
  for y in (select pid, id, name
    from tree_view
	where pid = tree_rec.pid)
  loop
    select count(*) into l_count from tree_view where pid = y.id;
    -- Determine whether or not a node has leaves, and if so, assign the appropriate icon
	if l_count > 0 then
        l_exp := 'Y';
      if tree_rec.id = y.id and tree_rec.id != v('P1_SELECTED_NODE') then
	    -- This node should be expanded
		l_icon := 'minus';
	  else
	    -- This node should not be expanded, yet has leaves
		l_icon := 'plus';
	  end if;
    else
      -- This node is an end-leaf
	  l_exp := 'N';
	  l_icon := null;
    end if;
	insert into tree_temp values(null, v('APP_USER'), v('APP_SESSION'), y.pid, y.id, y.name, l_exp, l_icon);
    l_icon := null;
  end loop;
end loop;
close tree_cur;
end find_node;
-------------------- C O N T R A C T
procedure contract
is
begin
-- If the user clicks the "-" icon, delete any nodes at or below that level from c_tfsms_nm_all_nodes
plus_minus();
for x in
  (select * from tree_temp
   Where app_user = v('APP_USER')
   connect by pid = prior id
   start with pid = v('P1_BRANCH_ID'))
loop
  delete from tree_temp where temp_id = x.temp_id;
end loop;
end contract;
-------------------- E X P A N D
procedure expand
is
begin
-- If the user clicks the "+" icon, add any nodes at that level to c_tfsms_nm_all_nodes
plus_minus();
for x in (select a.pid, a.id, a.name,
  (select count(*) from tree_view b where
   b.pid = a.id) branch_count
   from tree_view a
   where a.pid = v('P1_BRANCH_ID'))
loop
  if(x.branch_count > 0) then
	-- There are sub-nodes - render with a +/- icon
	insert into tree_temp values(null, v('APP_USER'), v('APP_SESSION'), x.pid, x.id, x.name, 'Y', 'plus');
  else
	-- This is an end-node - no need for a +/- icon
	insert into tree_temp values(null, v('APP_USER'), v('APP_SESSION'), x.pid, x.id, x.name, 'N', null);
  end if;
end loop;
end expand;
-------------------- P R O C E S S _ C L I C K
procedure process_click
as
  l_counter number;
  l_class varchar2(2000);
  l_icon varchar2(10);
  l_branch_id number := v('P1_BRANCH_ID');
begin
htp.prn('<ul id="branch'|| l_branch_id ||'" style="list-style:none;">');
for x in (select a.pid, a.id, a.name,
  (select count(*)  from tree_view b where
   b.pid = a.id ) branch_count
   from tree_view a
   where a.pid = l_branch_id)
loop
  if(x.branch_count > 0) then
	if v('P1_SELECTED_FD_ID') = x.id then
	  -- This node is the selected node - set the class accordingly
	  l_class := 'selected_node';
	else
	  -- This node is NOT the selected node
	  l_class := 'unselected_node';
	end if;
	render_branch('exp', 'plus', l_class, x.id, x.pid, x.name);
  else
	render_branch('no', null, l_class, x.id, x.pid, x.name);
  end if;
end loop;
htp.prn('</ul>');
end process_click;
end ajax_memory_tree;
/