|
| gliba 28.01.2007 01:53 Динамическое оглавление используется в проигрывателе курсов в системе дистанционного обучения "Карат".
Переход к курсу.
Код динамической загрузки оглавления
<style>
/* menu item */
A.menuel:link { COLOR: #0000ff; }
A.menuel:visited { COLOR: #0000cc; }
A.menuel:hover { COLOR: #00ffff; }
A.menuel:active { COLOR: #00ffff; }
/* current menu item */
A.menuact:link { color: #ffff00; }
A.menuact:visited { COLOR: #eeee00; }
A.menuact:hover { COLOR: #00ffff; }
A.menuact:active { COLOR: #00ffff; }
/* hint for menu item */
.hint
{
BACKGROUND-COLOR: khaki
}
</style>
<script>
//navigation block
var navdata = ""; //navigation data
var oldClass = ""; //class of not selected menu
var curPage = -1; //current page displayed
var pages = new Array(); //array of course page objects
var lastSel = -1; //selected page
var iForward = 2; //1 - step forward, -1 - step back, 2 - initial open menu, 0 - show/hide
var maxwidth = 1; //menu width
var pageTotal = 0; //number of real pages
var yOffMenu = -1; //y-offset of menu table
var skin = "";
var bEnglish = 0;
function coursePage( pid, title, url, level, num )
{
this.pid = pid;
this.title = title;
this.url = url;
this.level = level; //= 0 - main menu, < 0 - submenu item
this.type = 0; //0 - content, 1 - test
this.stat = "i"; //incomplete
this.iMenu = 0; //submenu: 0 - none, 1 - opened, -1 - closed
this.num = num; //
}
//parse course navigation data
function parsenavdata()
{
pageTotal = 0;
maxwidth = 1;
var lastlevel = 0;
var arr = navdata.split( "\n" );
for( var i = 0; i < arr.length; i++ )
{
var s = trimSpaces( arr[ i ] );
if( s == "" ) continue;
var arr2 = s.split( "|" ); //pid, title, url, level <= 0
var cur = pages.length;
var level = Math.abs( 1 * arr2[ 3 ] );
if( arr2[ 2 ] != "" ) pageTotal ++;
pages[ cur ] = new coursePage( arr2[ 0 ], arr2[ 1 ], arr2[ 2 ], level, pageTotal );
if( level > lastlevel && cur > 0 )
{
pages[ cur - 1 ].iMenu = -1; //prev.item has submenu, close it
}
lastlevel = level;
var titlen = arr2[ 1 ].length; //title length
if( titlen > maxwidth ) maxwidth = titlen;
}
maxwidth *= 11;
}
function createmenu()
{
if( document.getElementById == null ) return;
var objDiv = document.getElementById( "andr" );
if( objDiv == null ) return;
//start table for menu
var s = ( '<table cellspacing="0" cellpadding="2" border="0" width="' + maxwidth + '"><tr><td>' );
var iSkipLevel = 100;
for( var i = 0; i < pages.length; i++ )
{ //check every page
if( pages[ i ].level > iSkipLevel )
{ //hide
}
else
{ //display with indent
var iconspace = '<img border="0" height="16" width="' + ( pages[ i ].level * 16 ) + '" src="' + skin + 'm_space.gif">';
var icon = '<img border="0" ';
if( pages[ i ].iMenu == 0 )
{ //$ - non-free resourse
if( pages[ i ].url.indexOf( "$" ) >= 0 ) icon += 'src="' + skin + 'm_pagebook$.gif">';
else icon += 'src="' + skin + 'm_pagebook.gif">';
}
else if( pages[ i ].iMenu > 0 ) icon += 'src="' + skin + 'm_openbook.gif">';
else icon += 'src="' + skin + 'm_closebook.gif">';
s += ( iconspace + '<a class="menuel" href="#" id="page' + i + '" onclick="return loadpage(' + i + ')" onmouseover="displaystatus(event,this,' + i + ')" onmouseout="resetstatus(' + i + ')">' + icon + pages[ i ].title + '</a><br>' );
if( pages[ i ].iMenu < 0 ) iSkipLevel = pages[ i ].level;
else iSkipLevel = 100;
}
}
s += '</td></tr></table>'; //end menu table
//hint to display the status of menu item
var hint = '<div class="hint" id="hint" style="visibility:hidden;position:absolute;left:10;top:200;width:100;">bla-bla</div>';
s += hint;
objDiv.innerHTML = s;
lastSel = -1;
yOffMenu = -1;
}
//display a stus of menu item
function displaystatus( e, elem, n )
{
if( yOffMenu == -1 )
{ //calculate the y-offset of table with menu items
var p = elem;
while( p && p.nodeName != "BODY" )
{
if( p.nodeName == "DIV" || p.nodeName == "TABLE" )
{
yOffMenu += p.offsetTop;
}
p = p.parentNode;
}
}
var stat = ( bEnglish == 1 ) ? "completed" : "завершен";
var mylevel = pages[ n ].level;
for( var i = n; i < pages.length; i++ )
{
if( i > n && pages[ i ].level >= 0 &&
pages[ i ].level <= mylevel ) break; //upper menu item reached
if( ( pages[ i ].stat == "i" || pages[ i ].stat == "f" ) &&
pages[ i ].url != "" )
{
stat = ( bEnglish == 1 ) ? "incomplete" : "незавершен";
break;
}
}
if( document.getElementById )
{
var obj = document.getElementById( "hint" );
if( obj != null )
{
obj.style.visibility='visible';
obj.style.top = yOffMenu + elem.offsetTop + elem.offsetHeight + 3;
obj.innerHTML = stat;
}
}
}
function resetstatus( n )
{
if( document.getElementById )
{
var obj = document.getElementById( "hint" );
if( obj != null ) obj.style.visibility='hidden';
}
}
function findOpenedItem( n )
{ //find index of opened item
var mylevel = pages[ n ].level;
if( mylevel == 0 ) return n;
var i, iVis = n; //assume it's visible
for( i = n; i >= 0; i-- )
{
if( pages[ i ].level == mylevel - 1 )
{ //parent
mylevel--;
if( pages[ i ].iMenu < 0 ) iVis = i; //submenu is invisible
if( pages[ i ].level == 0 ) break;
}
}
return iVis;
}
//open all upper submenus of n
function extendmenuitem( n )
{
var nextlevel = pages[ n ].level - 1, i;
for( i = n; i >= 0; i-- )
{
if( pages[ i ].level == nextlevel )
{ //parent
nextlevel--;
if( pages[ i ].iMenu < 0 ) pages[ i ].iMenu = 1; //submenu will be visible
if( pages[ i ].level == 0 ) break;
}
}
return;
}
//select item - change color
function selectmenuitem( n )
{
var i = n, obj;
if( i == lastSel ) return; //already selected
if( document.getElementById )
{
if( lastSel >= 0 )
{ //deselect
obj = document.getElementById( "page" + lastSel );
if( obj != null ) obj.className = oldClass;
}
//select
obj = document.getElementById( "page" + i );
if( obj != null )
{
oldClass = obj.className;
obj.className = "menuact";
}
}
lastSel = i;
}
//iForward: 1 - step forward, -1 - step back, 2 - open all external menus, 0 - show/hide
function loadpage( m )
{
var bLoad = true, bUpdateMenu = false;
var n = 1 * m;
if( n < 0 || n >= pages.length )
{ //invalid index
if( curPage >= 0 ) return false;
n = 0;
m = 0;
}
if( iForward == 1 || iForward == 2 )
{ //go down
for( ; n < pages.length; n++ )
{ //url can be "" for menu item
if( pages[ n ].url != "" ) break;
}
}
if( iForward == -1 )
{ //go up
for( ; n >= 0; n-- )
{ //url can be "" for menu item
if( pages[ n ].url != "" ) break;
}
}
if( iForward == 0 && pages[ n ].iMenu != 0 )
{ //show/hide submenu
{ //don't navigate
pages[ n ].iMenu = - pages[ n ].iMenu; //show/hide submenu
bUpdateMenu = true;
if( pages[ n ].url == "" )
{
n = curPage;
bLoad = false;
}
}
}
if( n < 0 || n >= pages.length )
{
iForward = 0;
return false; //invalid index
}
curPage = n;
//open url
var url = pages[ curPage ].url;
if( bLoad )
{
openit( url );
}
if( pages[ curPage ].stat == "i" &&
url.indexOf( "test" ) < 0 )
{ //if resource is not a test and available then mark a page as completed
pages[ curPage ].stat = "c";
}
if( !bUpdateMenu && n != findOpenedItem( n ) )
{ //new menu item is hidden
extendmenuitem( n );
bUpdateMenu = true;
}
if( bUpdateMenu )
{
createmenu();
}
selectmenuitem( n );
iForward = 0;
return false; //false to supress <a href> behavior
}
//remove leading and trailing spaces, \r, \n
function trimSpaces( buf )
{
//remove leading spaces
var len = buf.length;
while( len > 0 )
{
var s = buf.charAt( 0 );
if( s == " " || s == "\n" || s == "\r" )
{
len --;
buf = buf.substr( 1, len );
}
else break;
}
//remove trailing spaces
while( len > 0 )
{
var s = buf.charAt( len - 1 );
if( s == " " || s == "\n" || s == "\r" )
{
len --;
buf = buf.substr( 0, len );
}
else break;
}
return buf;
}
//open url in new window
function openit( url, w, h, scroll, resize, left, top )
{
if( url == "" ) return;
var thisloc = "" + window.location;
if( "" + w == "undefined" ) w = 600;
if( "" + h == "undefined" ) h = 350;
if( "" + scroll == "undefined" ) scroll = 'yes';
if( "" + resize == "undefined" ) resize = 'yes';
if( "" + left == "undefined" ) {
left = ( screen.width - w ) / 2;
if( left < 0 ) left = 0;
}
if( "" + top == "undefined" ) {
top = ( screen.height - h - 30 ) / 2;
if( top < 0 ) top = 0;
}
var pos2 = url.toLowerCase().indexOf("http:"), loc = url;
if( pos2 < 0 && url.charAt( 0 ) != "/" )
{ //relative path
var pos = thisloc.lastIndexOf("/");
if( pos >= 0 )
{
loc = thisloc.substring(0, pos+1) + url;
}
}
if( loc == "" ) return;
window.open( loc, '_blank', 'width=' + w + ',height=' + h + ',left=' + left + ',top=' + top + ',scrollbars=' + scroll + ',resizable=' + resize );
return;
}
//load sample
function loadsample()
{
if( pages.length > 0 ) return false;
navdata = "1|JavaScript||0\n2|алгоритм SHA-1|sub002.htm|-1\n3|Построение гистограмм|sub003.htm|-1\n4|Как скопировать файл с CD-ROM'а в приложении MFC|sub001.htm|0";
parsenavdata();
createmenu();
return false;
}
</script>
<div id="andr">Оглавление не загружено</div><a href="#" onclick="return loadsample()">Загрузить</a>
Переход к примеру.
Комментарии.
Cтруктура оглавления хранится в переменной navdata как список строк в формате
"Идентификатор страницы | название страницы | url страницы | уровень вложенности".
Пример данных для оглавления из 4-х элементов.
navdata = "1|JavaScript||0\n2|алгоритм SHA-1|sub002.htm|-1\n3|Построение гистограмм|sub003.htm|-1\n4|Как скопировать файл с CD-ROM'а в приложении MFC|sub001.htm|0";
Анализ данных о структуре оглавления происходит в функции parsenavdata().
Переменная navdata расщепляется в массив arr. Каждый элемент массива есть одна строка
описания структуры курса. Каждая непустая строка в свою очередь расщепляется в массив arr2,
который используется для генерации объекта coursePage, который добавляется в конец массива pages.
Функция createmenu() создает код таблицы с оглавлением, а также элемент "hint".
Каждый элемент оглавления реализуется как тег <A> c методами
onclick="return loadpage()" onmouseover="displaystatus()" onmouseout="resetstatus()"
Затем этот код "вставляется" в элемент документа с идентификатором "andr".
Если уровень элемента меньше нуля, то вначале он не показывается (меню свернуто).
Меню можно раскрыть с помощью клика. Следующий клик приведет к сворачиванию меню.
Если url элемента оглавления непуст, то он запускается в новом окне с помощью функции openit().
При подведении курсора к элементу оглавления появляется подсказка (hint) о статусе элемента.
|