diff --git a/mod/scorm/backup/moodle2/backup_scorm_stepslib.php b/mod/scorm/backup/moodle2/backup_scorm_stepslib.php index 11712d028f1..64694426181 100644 --- a/mod/scorm/backup/moodle2/backup_scorm_stepslib.php +++ b/mod/scorm/backup/moodle2/backup_scorm_stepslib.php @@ -42,7 +42,7 @@ class backup_scorm_activity_structure_step extends backup_activity_structure_ste 'whatgrade', 'maxattempt', 'forcecompleted', 'forcenewattempt', 'lastattemptlock', 'displayattemptstatus', 'displaycoursestructure', 'updatefreq', 'sha1hash', 'md5hash', 'revision', 'launch', - 'skipview', 'hidebrowse', 'hidetoc', 'hidenav', + 'skipview', 'hidebrowse', 'hidetoc', 'nav', 'navpositionleft', 'navpositiontop', 'auto', 'popup', 'options', 'width', 'height', 'timeopen', 'timeclose', 'timemodified', 'completionstatusrequired', 'completionscorerequired')); diff --git a/mod/scorm/datamodels/scorm_13lib.php b/mod/scorm/datamodels/scorm_13lib.php index 906d288f147..eca069ffcfb 100644 --- a/mod/scorm/datamodels/scorm_13lib.php +++ b/mod/scorm/datamodels/scorm_13lib.php @@ -986,7 +986,11 @@ function scorm_seq_rollup_rule_check ($sco, $userid, $action) { function scorm_seq_flow_tree_traversal($activity, $direction, $childrenflag, $prevdirection, $seq, $userid, $skip = false) { $revdirection = false; $parent = scorm_get_parent($activity); - $children = scorm_get_available_children($parent); + if (!empty($parent)) { + $children = scorm_get_available_children($parent); + } else { + $children = array(); + } $childrensize = count($children); if (($prevdirection != null && $prevdirection == 'backward') && ($children[$childrensize-1]->id == $activity->id)) { diff --git a/mod/scorm/db/install.xml b/mod/scorm/db/install.xml index d69f1ea6a00..a36bd6f36b2 100644 --- a/mod/scorm/db/install.xml +++ b/mod/scorm/db/install.xml @@ -1,5 +1,5 @@ - @@ -31,7 +31,9 @@ - + + + diff --git a/mod/scorm/db/upgrade.php b/mod/scorm/db/upgrade.php index 870a9886f12..242f89e11e8 100644 --- a/mod/scorm/db/upgrade.php +++ b/mod/scorm/db/upgrade.php @@ -129,6 +129,47 @@ function xmldb_scorm_upgrade($oldversion) { upgrade_mod_savepoint(true, 2013081303, 'scorm'); } + if ($oldversion < 2013090100) { + $table = new xmldb_table('scorm'); + + $field = new xmldb_field('nav', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, true, null, 1, 'hidetoc'); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + $field = new xmldb_field('navpositionleft', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, -100, 'nav'); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + $field = new xmldb_field('navpositiontop', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, -100, 'navpositionleft'); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + $field = new xmldb_field('hidenav'); + if ($dbman->field_exists($table, $field)) { + // Update nav setting to disable navigation buttons. + $DB->set_field('scorm', 'nav', 0, array('hidenav' => 1)); + // Update nav setting to show floating navigation buttons under TOC. + $DB->set_field('scorm', 'nav', 2, array('hidenav' => 0)); + $DB->set_field('scorm', 'navpositionleft', 215, array('hidenav' => 0)); + $DB->set_field('scorm', 'navpositiontop', 300, array('hidenav' => 0)); + // Drop hidenav field. + $dbman->drop_field($table, $field); + } + + $params = array('plugin' => 'scorm', 'name' => 'hidenav'); + if ($DB->record_exists('config_plugins', $params)) { + $DB->delete_records('config_plugins', $params); + } + $params = array('plugin' => 'scorm', 'name' => 'hidenav_adv'); + if ($DB->record_exists('config_plugins', $params)) { + $DB->delete_records('config_plugins', $params); + } + upgrade_mod_savepoint(true, 2013090100, 'scorm'); + } + return true; } diff --git a/mod/scorm/lang/en/scorm.php b/mod/scorm/lang/en/scorm.php index f39efdaa8fe..5016d551be7 100644 --- a/mod/scorm/lang/en/scorm.php +++ b/mod/scorm/lang/en/scorm.php @@ -67,6 +67,8 @@ $string['browsemode'] = 'Preview mode'; $string['browserepository'] = 'Browse repository'; $string['calculatedweight'] = 'Calculated weight'; $string['cannotfindsco'] = 'Could not find SCO'; +$string['collapsetocwinsize'] = 'Collapse TOC when window size below'; +$string['collapsetocwinsizedesc'] = 'This setting lets you specify the window size below which the TOC should automatically collapse.'; $string['compatibilitysettings'] = 'Compatibility settings'; $string['completed'] = 'Completed'; $string['completionscorerequired'] = 'Require minimum score'; @@ -118,6 +120,7 @@ $string['finishscorm'] = 'If you have finished viewing this resource, {$a}'; $string['finishscormlinkname'] = 'click here to return to the course page'; $string['firstaccess'] = 'First access'; $string['firstattempt'] = 'First attempt'; +$string['floating'] = 'Floating'; $string['forcecompleted'] = 'Force completed'; $string['forcecompleted_help'] = 'If enabled, the status of the current attempt is forced to "completed". (Only applicable to SCORM 1.2 packages.)'; $string['forcecompleteddesc'] = 'This preference sets the default value for the force completed setting'; @@ -130,6 +133,8 @@ $string['forcejavascriptmessage'] = 'JavaScript is required to view this object, $string['found'] = 'Manifest found'; $string['frameheight'] = 'The height of the stage frame or window.'; $string['framewidth'] = 'The width of the stage frame or window.'; +$string['fromleft'] = 'From left'; +$string['fromtop'] = 'From top'; $string['fullscreen'] = 'Fill the whole screen'; $string['general'] = 'General data'; $string['gradeaverage'] = 'Average grade'; @@ -155,8 +160,6 @@ $string['hidebrowse'] = 'Disable preview mode'; $string['hidebrowse_help'] = 'Preview mode allows a student to browse an activity before attempting it. If preview mode is disabled, the preview button is hidden.'; $string['hidebrowsedesc'] = 'Preview mode allows a student to browse an activity before attempting it.'; $string['hideexit'] = 'Hide exit link'; -$string['hidenav'] = 'Hide navigation buttons'; -$string['hidenavdesc'] = 'Whether to show or hide the navigation buttons.'; $string['hidereview'] = 'Hide review button'; $string['hidetoc'] = 'Display course structure in player'; $string['hidetoc_help'] = 'How the table of contents is displayed in the SCORM player'; @@ -227,6 +230,17 @@ SCORM activities may be used * As an assessment tool'; $string['modulename_link'] = 'mod/scorm/view'; $string['modulenameplural'] = 'SCORM packages'; +$string['nav'] = 'Show Navigation'; +$string['nav_help'] = 'This setting specifies wether to show or hide the navigation buttons and their position. + +There are 3 options: + +* No - Do not show the navigation buttons +* Under content - Show the navigation buttons under SCORM package content +* Float - Allows to manually specify the navigation buttons position from left and from top with respect to the window.'; +$string['navdesc'] = 'This setting specifies wether to show/hide navigation buttons and their position.'; +$string['navpositionleft'] = 'Position of navigation buttons from left in pixels.'; +$string['navpositiontop'] = 'Position of navigation buttons from top in pixels.'; $string['newattempt'] = 'Start a new attempt'; $string['next'] = 'Continue'; $string['noactivity'] = 'Nothing to report'; @@ -340,6 +354,7 @@ $string['typeaiccurl'] = 'External AICC URL'; $string['typeexternal'] = 'External SCORM manifest'; $string['typelocal'] = 'Uploaded package'; $string['typelocalsync'] = 'Downloaded package'; +$string['undercontent'] = 'Under content'; $string['unziperror'] = 'An error occurs during package unzip'; $string['updatefreq'] = 'Auto-update frequency'; $string['updatefreq_error'] = 'Auto-update frequency can only be set when the package file is hosted externally'; diff --git a/mod/scorm/lib.php b/mod/scorm/lib.php index a3d17cd7508..3d8aff75e65 100644 --- a/mod/scorm/lib.php +++ b/mod/scorm/lib.php @@ -34,6 +34,11 @@ define('SCORM_TOC_HIDDEN', 1); define('SCORM_TOC_POPUP', 2); define('SCORM_TOC_DISABLED', 3); +// Used to show/hide navigation buttons and set their position. +define('SCORM_NAV_DISABLED', 0); +define('SCORM_NAV_UNDER_CONTENT', 1); +define('SCORM_NAV_FLOATING', 2); + //used to check what SCORM version is being used. define('SCORM_12', 1); define('SCORM_13', 2); diff --git a/mod/scorm/loaddatamodel.php b/mod/scorm/loaddatamodel.php index 297375d25e8..f9d6ba1bff6 100644 --- a/mod/scorm/loaddatamodel.php +++ b/mod/scorm/loaddatamodel.php @@ -29,6 +29,7 @@ $id = optional_param('id', 0, PARAM_INT); // Course Module ID, or $a = optional_param('a', 0, PARAM_INT); // scorm ID. $scoid = required_param('scoid', PARAM_INT); // sco ID. $mode = optional_param('mode', '', PARAM_ALPHA); // navigation mode. +$currentorg = optional_param('currentorg', '', PARAM_RAW); // Selected organization. $attempt = required_param('attempt', PARAM_INT); // new attempt. if (!empty($id)) { diff --git a/mod/scorm/locallib.php b/mod/scorm/locallib.php index b1cde822434..2b770d8bae7 100644 --- a/mod/scorm/locallib.php +++ b/mod/scorm/locallib.php @@ -149,6 +149,17 @@ function scorm_get_popup_display_array() { 1 => get_string('popup', 'scorm')); } +/** + * Returns an array of the array of navigation buttons display options + * + * @return array an array of navigation buttons display options + */ +function scorm_get_navigation_display_array() { + return array(SCORM_NAV_DISABLED => get_string('no'), + SCORM_NAV_UNDER_CONTENT => get_string('undercontent', 'scorm'), + SCORM_NAV_FLOATING => get_string('floating', 'scorm')); +} + /** * Returns an array of the array of attempt options * @@ -1445,7 +1456,7 @@ function scorm_get_toc_object($user, $scorm, $currentorg='', $scoid='', $mode='n } // Get the parent scoes! - $result = scorm_get_toc_get_parent_child($result); + $result = scorm_get_toc_get_parent_child($result, $currentorg); // Be safe, prevent warnings from showing up while returning array if (!isset($scoid)) { @@ -1455,10 +1466,15 @@ function scorm_get_toc_object($user, $scorm, $currentorg='', $scoid='', $mode='n return array('scoes' => $result, 'usertracks' => $usertracks, 'scoid' => $scoid); } -function scorm_get_toc_get_parent_child(&$result) { +function scorm_get_toc_get_parent_child(&$result, $currentorg) { $final = array(); $level = 0; - $prevparent = '/'; + // Organization is always the root, prevparent. + if (!empty($currentorg)) { + $prevparent = $currentorg; + } else { + $prevparent = '/'; + } foreach ($result as $sco) { if ($sco->parent == '/') { @@ -1587,7 +1603,7 @@ function scorm_format_toc_for_treeview($user, $scorm, $scoes, $usertracks, $cmid if ($sco->scormtype == 'sco') { $result->toc .= $sco->statusicon.' '.format_string($sco->title).''.$score."\n"; } else { - $result->toc .= ' '.format_string($sco->title).''.$score."\n"; + $result->toc .= ' '.format_string($sco->title).''.$score."\n"; } } else { if ($sco->scormtype == 'sco') { @@ -1599,9 +1615,9 @@ function scorm_format_toc_for_treeview($user, $scorm, $scoes, $usertracks, $cmid } else { if (!empty($sco->launch)) { if ($sco->scormtype == 'sco') { - $result->toc .= ''.$sco->statusicon.' '.format_string($sco->title).' '.$score.''; + $result->toc .= ''.$sco->statusicon.' '.format_string($sco->title).' '.$score.''; } else { - $result->toc .= ' '.format_string($sco->title).' '.$score.''; + $result->toc .= ' '.format_string($sco->title).' '.$score.''; } } else { if ($sco->scormtype == 'sco') { @@ -1710,8 +1726,9 @@ function scorm_get_toc($user, $scorm, $cmid, $toclink=TOCJSLINK, $currentorg='', $organizationsco = null; if ($tocheader) { - $result->toc = "
\n"; - $result->toc .= "
\n"; + $result->toc = "
\n"; + $result->toc .= "
\n"; + $result->toc .= "
\n"; $result->toc .= "
\n"; } @@ -1761,8 +1778,12 @@ function scorm_get_toc($user, $scorm, $cmid, $toclink=TOCJSLINK, $currentorg='', $result->attemptleft = $treeview->attemptleft; if ($tocheader) { - $result->toc .= "
\n"; + $result->toc .= "
\n"; + $result->toc .= "
\n"; + $result->toc .= "
\n"; + $result->toc .= "
"; $result->toc .= "
\n"; + $result->toc .= "
\n"; } return $result; diff --git a/mod/scorm/mod_form.php b/mod/scorm/mod_form.php index 99d9d875f90..06fcf4c0cf4 100644 --- a/mod/scorm/mod_form.php +++ b/mod/scorm/mod_form.php @@ -162,11 +162,28 @@ class mod_scorm_mod_form extends moodleform_mod { $mform->setAdvanced('hidetoc', $cfgscorm->hidetoc_adv); $mform->disabledIf('hidetoc', 'scormtype', 'eq', SCORM_TYPE_AICCURL); - // Hide Navigation panel. - $mform->addElement('selectyesno', 'hidenav', get_string('hidenav', 'scorm')); - $mform->setDefault('hidenav', $cfgscorm->hidenav); - $mform->setAdvanced('hidenav', $cfgscorm->hidenav_adv); - $mform->disabledIf('hidenav', 'hidetoc', 'noteq', 0); + // Navigation panel display. + $mform->addElement('select', 'nav', get_string('nav', 'scorm'), scorm_get_navigation_display_array()); + $mform->addHelpButton('nav', 'nav', 'scorm'); + $mform->setDefault('nav', $cfgscorm->nav); + $mform->setAdvanced('nav', $cfgscorm->nav_adv); + $mform->disabledIf('nav', 'hidetoc', 'noteq', SCORM_TOC_SIDE); + + // Navigation panel position from left. + $mform->addElement('text', 'navpositionleft', get_string('fromleft', 'scorm'), 'maxlength="5" size="5"'); + $mform->setDefault('navpositionleft', $cfgscorm->navpositionleft); + $mform->setType('navpositionleft', PARAM_INT); + $mform->setAdvanced('navpositionleft', $cfgscorm->navpositionleft_adv); + $mform->disabledIf('navpositionleft', 'hidetoc', 'noteq', SCORM_TOC_SIDE); + $mform->disabledIf('navpositionleft', 'nav', 'noteq', SCORM_NAV_FLOATING); + + // Navigation panel position from top. + $mform->addElement('text', 'navpositiontop', get_string('fromtop', 'scorm'), 'maxlength="5" size="5"'); + $mform->setDefault('navpositiontop', $cfgscorm->navpositiontop); + $mform->setType('navpositiontop', PARAM_INT); + $mform->setAdvanced('navpositiontop', $cfgscorm->navpositiontop_adv); + $mform->disabledIf('navpositiontop', 'hidetoc', 'noteq', SCORM_TOC_SIDE); + $mform->disabledIf('navpositiontop', 'nav', 'noteq', SCORM_NAV_FLOATING); // Display attempt status. $mform->addElement('select', 'displayattemptstatus', get_string('displayattemptstatus', 'scorm'), diff --git a/mod/scorm/module.js b/mod/scorm/module.js index 817b377f642..efdaa8efd84 100644 --- a/mod/scorm/module.js +++ b/mod/scorm/module.js @@ -24,16 +24,17 @@ mod_scorm_launch_next_sco = null; mod_scorm_launch_prev_sco = null; mod_scorm_activate_item = null; +mod_scorm_parse_toc_tree = null; scorm_layout_widget = null; M.mod_scorm = {}; -M.mod_scorm.init = function(Y, hide_nav, hide_toc, toc_title, window_name, launch_sco, scoes_nav) { +M.mod_scorm.init = function(Y, nav_display, navposition_left, navposition_top, hide_toc, collapsetocwinsize, toc_title, window_name, launch_sco, scoes_nav) { var scorm_disable_toc = false; var scorm_hide_nav = true; var scorm_hide_toc = true; if (hide_toc == 0) { - if (hide_nav != 1) { + if (nav_display !== 0) { scorm_hide_nav = false; } scorm_hide_toc = false; @@ -47,32 +48,98 @@ M.mod_scorm.init = function(Y, hide_nav, hide_toc, toc_title, window_name, launc var scorm_bloody_labelclick = false; var scorm_nav_panel; - Y.use('yui2-resize', 'yui2-dragdrop', 'yui2-container', 'yui2-button', 'yui2-layout', 'yui2-treeview', 'yui2-json', 'yui2-event', function(Y) { + Y.use('button', 'dd-plugin', 'panel', 'resize', 'gallery-sm-treeview', function(Y) { - Y.YUI2.widget.TextNode.prototype.getContentHtml = function() { - var sb = []; - sb[sb.length] = this.href ? '':''; - return sb.join(""); + return node; }; + Y.TreeView.prototype.openAll = function () { + var tree = this; + Y.all('.yui3-treeview-can-have-children').each(function() { + var node = tree.getNodeById(this.get('id')); + node.open(); + }); + }; + + // TODO: Remove next(), previous() prototype functions after YUI has been updated to 3.11.0 - MDL-41208. + Y.Tree.Node.prototype.next = function () { + if (this.parent) { + return this.parent.children[this.index() + 1]; + } + }; + + Y.Tree.Node.prototype.previous = function () { + if (this.parent) { + return this.parent.children[this.index() - 1]; + } + }; + + var scorm_parse_toc_tree = function(srcNode) { + var SELECTORS = { + child: '> li', + label: '> li, > a', + textlabel : '> li, > span', + subtree: '> ul, > li' + }, + children = []; + + srcNode.all(SELECTORS.child).each(function(childNode) { + var child = {}, + labelNode = childNode.one(SELECTORS.label), + textNode = childNode.one(SELECTORS.textlabel), + subTreeNode = childNode.one(SELECTORS.subtree); + + if (labelNode) { + var title = labelNode.getAttribute('title'); + var scoid = labelNode.getData('scoid'); + child.label = labelNode.get('outerHTML'); + // Will be good to change to url instead of title. + if (title && title !== '#') { + child.title = title; + } + if (typeof scoid !== 'undefined') { + child.scoid = scoid; + } + } else if (textNode) { + // The selector did not find a label node with anchor. + child.label = textNode.get('outerHTML'); + } + + if (subTreeNode) { + child.children = scorm_parse_toc_tree(subTreeNode); + } + + children.push(child); + }); + + return children; + }; + + mod_scorm_parse_toc_tree = scorm_parse_toc_tree; + var scorm_activate_item = function(node) { if (!node) { return; } + // Check if the item is already active, avoid recursive calls. + if (Y.one('#scorm_object')) { + var scorm_active_url = Y.one('#scorm_object').getAttribute('src'); + var node_full_url = M.cfg.wwwroot + '/mod/scorm/loadSCO.php?' + node.title; + if (node_full_url === scorm_active_url) { + return; + } + } scorm_current_node = node; - scorm_current_node.highlight(); + // Avoid recursive calls. + if (!scorm_current_node.state.selected) { + scorm_current_node.select(); + } // remove any reference to the old API if (window.API) { @@ -99,7 +166,7 @@ M.mod_scorm.init = function(Y, hide_nav, hide_toc, toc_title, window_name, launc document.getElementById('external-scormapi').src = api_url; } - var content = new Y.YUI2.util.Element('scorm_content'); + var content = Y.one('#scorm_content'); var obj = document.createElement('iframe'); obj.setAttribute('id', 'scorm_object'); obj.setAttribute('type', 'text/html'); @@ -111,10 +178,10 @@ M.mod_scorm.init = function(Y, hide_nav, hide_toc, toc_title, window_name, launc if(! mine) { alert(M.str.scorm.popupsblocked); } - mine.close() + mine.close(); } - var old = Y.YUI2.util.Dom.get('scorm_object'); + var old = Y.one('#scorm_object'); if (old) { if(window_name) { var cwidth = scormplayerdata.cwidth; @@ -125,16 +192,13 @@ M.mod_scorm.init = function(Y, hide_nav, hide_toc, toc_title, window_name, launc content.replaceChild(obj, old); } } else { - content.appendChild(obj); + content.prepend(obj); } - scorm_resize_frame(); - - var left = scorm_layout_widget.getUnitByPosition('left'); - if (left.expand) { - scorm_current_node.focus(); - } if (scorm_hide_nav == false) { + if (nav_display === 1 && navposition_left > 0 && navposition_top > 0) { + Y.one('#scorm_object').addClass(cssclasses.scorm_nav_under_content); + } scorm_fixnav(); } }; @@ -146,95 +210,88 @@ M.mod_scorm.init = function(Y, hide_nav, hide_toc, toc_title, window_name, launc * @return void */ var scorm_fixnav = function() { - scorm_buttons[0].set('disabled', (scorm_skipprev(scorm_current_node) == null || scorm_skipprev(scorm_current_node).title == null || - scoes_nav[launch_sco].hideprevious == 1)); - scorm_buttons[1].set('disabled', (scorm_prev(scorm_current_node) == null || scorm_prev(scorm_current_node).title == null || - scoes_nav[launch_sco].hideprevious == 1)); - scorm_buttons[2].set('disabled', (scorm_up(scorm_current_node) == null) || scorm_up(scorm_current_node).title == null); - scorm_buttons[3].set('disabled', (((scorm_next(scorm_current_node) == null || scorm_next(scorm_current_node).title == null) && - (scoes_nav[launch_sco].flow != 1)) || (scoes_nav[launch_sco].hidecontinue == 1))); - scorm_buttons[4].set('disabled', (scorm_skipnext(scorm_current_node) == null || scorm_skipnext(scorm_current_node).title == null || - scoes_nav[launch_sco].hidecontinue == 1)); + scorm_buttons[0].set('disabled', ((scorm_skipprev(scorm_current_node) === null) || (scorm_skipprev(scorm_current_node).parent.isRoot()) || + (scorm_skipprev(scorm_current_node).title === null) || (scoes_nav[launch_sco].hideprevious === 1))); + scorm_buttons[1].set('disabled', ((scorm_prev(scorm_current_node) === null) || (scorm_skipprev(scorm_current_node).parent.isRoot()) || + (scorm_prev(scorm_current_node).title === null) || (scoes_nav[launch_sco].hideprevious === 1))); + scorm_buttons[2].set('disabled', (scorm_up(scorm_current_node) === null) || (scorm_up(scorm_current_node).parent.isRoot()) || + (scorm_up(scorm_current_node).title === null)); + scorm_buttons[3].set('disabled', ((scorm_next(scorm_current_node) === null) || ((scorm_next(scorm_current_node).title === null) && + (scoes_nav[launch_sco].flow !== 1)) || (scoes_nav[launch_sco].hidecontinue === 1))); + scorm_buttons[4].set('disabled', (scorm_skipnext(scorm_current_node) === null || scorm_skipnext(scorm_current_node).title === null || + scoes_nav[launch_sco].hidecontinue === 1)); }; - var scorm_resize_parent = function() { - // fudge IE7 to redraw the screen - parent.resizeBy(-10, -10); - parent.resizeBy(10, 10); - var ifr = Y.YUI2.util.Dom.get('scorm_object'); - if (ifr) { - ifr.detachEvent("onload", scorm_resize_parent); + var scorm_toggle_toc = function(windowresize) { + var toc = Y.one('#scorm_toc'); + var scorm_content = Y.one('#scorm_content'); + var scorm_toc_toggle_btn = Y.one('#scorm_toc_toggle_btn'); + var toc_disabled = toc.hasClass('disabled'); + var disabled_by = toc.getAttribute('disabled-by'); + // Remove width element style from resize handle. + toc.setStyle('width', null); + scorm_content.setStyle('width', null); + if (windowresize === true) { + if (disabled_by === 'user') { + return; + } + var body = Y.one('body'); + if (body.get('winWidth') < collapsetocwinsize) { + toc.addClass(cssclasses.disabled) + .setAttribute('disabled-by', 'screen-size'); + scorm_toc_toggle_btn.setHTML('>') + .set('title', M.util.get_string('show', 'moodle')); + scorm_content.removeClass(cssclasses.scorm_grid_content_toc_visible) + .addClass(cssclasses.scorm_grid_content_toc_hidden); + } else if (body.get('winWidth') > collapsetocwinsize) { + toc.removeClass(cssclasses.disabled) + .removeAttribute('disabled-by'); + scorm_toc_toggle_btn.setHTML('<') + .set('title', M.util.get_string('hide', 'moodle')); + scorm_content.removeClass(cssclasses.scorm_grid_content_toc_hidden) + .addClass(cssclasses.scorm_grid_content_toc_visible); + } + return; + } + if (toc_disabled) { + toc.removeClass(cssclasses.disabled) + .removeAttribute('disabled-by'); + scorm_toc_toggle_btn.setHTML('<') + .set('title', M.util.get_string('hide', 'moodle')); + scorm_content.removeClass(cssclasses.scorm_grid_content_toc_hidden) + .addClass(cssclasses.scorm_grid_content_toc_visible); + } else { + toc.addClass(cssclasses.disabled) + .setAttribute('disabled-by', 'user'); + scorm_toc_toggle_btn.setHTML('>') + .set('title', M.util.get_string('show', 'moodle')); + scorm_content.removeClass(cssclasses.scorm_grid_content_toc_visible) + .addClass(cssclasses.scorm_grid_content_toc_hidden); } }; - var scorm_resize_layout = function(alsowidth) { + var scorm_resize_layout = function() { if (window_name) { return; } - if (alsowidth) { - scorm_layout_widget.setStyle('width', ''); - var newwidth = scorm_get_htmlelement_size('content', 'width'); - } // make sure that the max width of the TOC doesn't go to far - var left = scorm_layout_widget.getUnitByPosition('left'); - var maxwidth = parseInt(Y.YUI2.util.Dom.getStyle('scorm_layout', 'width')); - left.set('maxWidth', (maxwidth - 50)); - var cwidth = left.get('width'); + var scorm_toc_node = Y.one('#scorm_toc'); + var maxwidth = parseInt(Y.one('#scorm_layout').getComputedStyle('width'), 10); + scorm_toc_node.setStyle('maxWidth', (maxwidth - 200)); + var cwidth = parseInt(scorm_toc_node.getComputedStyle('width'), 10); if (cwidth > (maxwidth - 1)) { - left.set('width', (maxwidth - 50)); + scorm_toc_node.setStyle('width', (maxwidth - 50)); } - scorm_layout_widget.setStyle('height', '100%'); - var center = scorm_layout_widget.getUnitByPosition('center'); - center.setStyle('height', '100%'); - - // calculate the rough new height - newheight = Y.YUI2.util.Dom.getViewportHeight() -5; + // Calculate the rough new height from the viewport height. + newheight = Y.one('body').get('winHeight') -5; if (newheight < 600) { newheight = 600; } - scorm_layout_widget.set('height', newheight); + Y.one('#scorm_layout').setStyle('height', newheight); - scorm_layout_widget.render(); - scorm_resize_frame(); - - if (scorm_nav_panel) { - scorm_nav_panel.align('bl', 'bl'); - } - }; - - var scorm_get_htmlelement_size = function(el, prop) { - var val = Y.YUI2.util.Dom.getStyle(el, prop); - if (val == 'auto') { - if (el.get) { - el = el.get('element'); // get real HTMLElement from YUI element - } - val = Y.YUI2.util.Dom.getComputedStyle(Y.YUI2.util.Dom.get(el), prop); - } - return parseInt(val); - }; - - var scorm_resize_frame = function() { - var obj = Y.YUI2.util.Dom.get('scorm_object'); - if (obj) { - var content = scorm_layout_widget.getUnitByPosition('center').get('wrap'); - // basically trap IE6 and 7 - if (Y.YUI2.env.ua.ie > 5 && Y.YUI2.env.ua.ie < 8) { - if( obj.style.setAttribute ) { - obj.style.setAttribute("cssText", 'width: ' +(content.offsetWidth - 6)+'px; height: ' + (content.offsetHeight - 10)+'px;'); - } - else { - obj.style.setAttribute('width', (content.offsetWidth - 6)+'px', 0); - obj.style.setAttribute('height', (content.offsetHeight - 10)+'px', 0); - } - } - else { - obj.style.width = (content.offsetWidth)+'px'; - obj.style.height = (content.offsetHeight - 10)+'px'; - } - } }; // Handle AJAX Request @@ -245,14 +302,20 @@ M.mod_scorm.init = function(Y, hide_nav, hide_toc, toc_title, window_name, launc }; var scorm_up = function(node, update_launch_sco) { - var node = scorm_tree_node.getHighlightedNode(); - if (node.depth > 0 && typeof scoes_nav[launch_sco].parentscoid != 'undefined') { + if (node.parent && node.parent.parent && typeof scoes_nav[launch_sco].parentscoid !== 'undefined') { var parentscoid = scoes_nav[launch_sco].parentscoid; - node.parent.title = scoes_nav[parentscoid].url; + var parent = node.parent; + if (parent.title !== scoes_nav[parentscoid].url) { + parent = scorm_tree_node.getNodeByAttribute('title', scoes_nav[parentscoid].url); + if (parent === null) { + parent = scorm_tree_node.rootNode.children[0]; + parent.title = scoes_nav[parentscoid].url; + } + } if (update_launch_sco) { launch_sco = parentscoid; } - return node.parent; + return parent; } return null; }; @@ -266,12 +329,18 @@ M.mod_scorm.init = function(Y, hide_nav, hide_toc, toc_title, window_name, launc }; var scorm_prev = function(node, update_launch_sco) { - if (node.previousSibling && node.previousSibling.children.length && - typeof scoes_nav[launch_sco].prevscoid != 'undefined') { - var node = scorm_lastchild(node.previousSibling); + if (node.previous() && node.previous().children.length && + typeof scoes_nav[launch_sco].prevscoid !== 'undefined') { + node = scorm_lastchild(node.previous()); if (node) { var prevscoid = scoes_nav[launch_sco].prevscoid; - node.title = scoes_nav[prevscoid].url; + if (node.title !== scoes_nav[prevscoid].url) { + node = scorm_tree_node.getNodeByAttribute('title', scoes_nav[prevscoid].url); + if (node === null) { + node = scorm_tree_node.rootNode.children[0]; + node.title = scoes_nav[prevscoid].url; + } + } if (update_launch_sco) { launch_sco = prevscoid; } @@ -284,32 +353,53 @@ M.mod_scorm.init = function(Y, hide_nav, hide_toc, toc_title, window_name, launc }; var scorm_skipprev = function(node, update_launch_sco) { - if (node.previousSibling && typeof scoes_nav[launch_sco].prevsibling != 'undefined') { + if (node.previous() && typeof scoes_nav[launch_sco].prevsibling !== 'undefined') { var prevsibling = scoes_nav[launch_sco].prevsibling; - node.previousSibling.title = scoes_nav[prevsibling].url; + var previous = node.previous(); + var prevscoid = scoes_nav[launch_sco].prevscoid; + if (previous.title !== scoes_nav[prevscoid].url) { + previous = scorm_tree_node.getNodeByAttribute('title', scoes_nav[prevsibling].url); + if (previous === null) { + previous = scorm_tree_node.rootNode.children[0]; + previous.title = scoes_nav[prevsibling].url; + } + } if (update_launch_sco) { launch_sco = prevsibling; } - return node.previousSibling; - } else if (node.depth > 0 && typeof scoes_nav[launch_sco].parentscoid != 'undefined') { + return previous; + } else if (node.parent && node.parent.parent && typeof scoes_nav[launch_sco].parentscoid !== 'undefined') { var parentscoid = scoes_nav[launch_sco].parentscoid; - node.parent.title = scoes_nav[parentscoid].url; + var parent = node.parent; + if (parent.title !== scoes_nav[parentscoid].url) { + parent = scorm_tree_node.getNodeByAttribute('title', scoes_nav[parentscoid].url); + if (parent === null) { + parent = scorm_tree_node.rootNode.children[0]; + parent.title = scoes_nav[parentscoid].url; + } + } if (update_launch_sco) { launch_sco = parentscoid; } - return node.parent; + return parent; } return null; }; var scorm_next = function(node, update_launch_sco) { if (node === false) { - return scorm_tree_node.getRoot().children[0]; + return scorm_tree_node.children[0]; } if (node.children.length && typeof scoes_nav[launch_sco].nextscoid != 'undefined') { - var node = node.children[0]; + node = node.children[0]; var nextscoid = scoes_nav[launch_sco].nextscoid; - node.title = scoes_nav[nextscoid].url; + if (node.title !== scoes_nav[nextscoid].url) { + node = scorm_tree_node.getNodeByAttribute('title', scoes_nav[nextscoid].url); + if (node === null) { + node = scorm_tree_node.rootNode.children[0]; + node.title = scoes_nav[nextscoid].url; + } + } if (update_launch_sco) { launch_sco = nextscoid; } @@ -319,297 +409,419 @@ M.mod_scorm.init = function(Y, hide_nav, hide_toc, toc_title, window_name, launc }; var scorm_skipnext = function(node, update_launch_sco) { - if (node.nextSibling && typeof scoes_nav[launch_sco].nextsibling != 'undefined') { + var next = node.next(); + if (next && next.title && typeof scoes_nav[launch_sco] !== 'undefined' && typeof scoes_nav[launch_sco].nextsibling !== 'undefined') { var nextsibling = scoes_nav[launch_sco].nextsibling; - node.nextSibling.title = scoes_nav[nextsibling].url; + if (next.title !== scoes_nav[nextsibling].url) { + next = scorm_tree_node.getNodeByAttribute('title', scoes_nav[nextsibling].url); + if (next === null) { + next = scorm_tree_node.rootNode.children[0]; + next.title = scoes_nav[nextsibling].url; + } + } if (update_launch_sco) { launch_sco = nextsibling; } - return node.nextSibling; - } else if (node.depth > 0 && typeof scoes_nav[launch_sco].parentscoid != 'undefined') { + return next; + } else if (node.parent && node.parent.parent && typeof scoes_nav[launch_sco].parentscoid !== 'undefined') { var parentscoid = scoes_nav[launch_sco].parentscoid; + var parent = node.parent; + if (parent.title !== scoes_nav[parentscoid].url) { + parent = scorm_tree_node.getNodeByAttribute('title', scoes_nav[parentscoid].url); + if (parent === null) { + parent = scorm_tree_node.rootNode.children[0]; + } + } if (update_launch_sco) { launch_sco = parentscoid; } - return scorm_skipnext(node.parent, update_launch_sco); + return scorm_skipnext(parent, update_launch_sco); } return null; }; // Launch prev sco var scorm_launch_prev_sco = function() { - var result = null; - if (scoes_nav[launch_sco].flow == 1) { + var result = null; + if (scoes_nav[launch_sco].flow === 1) { var datastring = scoes_nav[launch_sco].url + '&function=scorm_seq_flow&request=backward'; result = scorm_ajax_request(M.cfg.wwwroot + '/mod/scorm/datamodels/sequencinghandler.php?', datastring); mod_scorm_seq = encodeURIComponent(result); result = Y.JSON.parse (result); if (typeof result.nextactivity.id != undefined) { - var node = scorm_prev(scorm_tree_node.getHighlightedNode()) + var node = scorm_prev(scorm_tree_node.getSelectedNodes()[0]); if (node == null) { - // Avoid use of TreeView for Navigation - node = scorm_tree_node.getHighlightedNode(); + // Avoid use of TreeView for Navigation. + node = scorm_tree_node.getSelectedNodes()[0]; + } + if (node.title !== scoes_nav[result.nextactivity.id].url) { + node = scorm_tree_node.getNodeByAttribute('title', scoes_nav[result.nextactivity.id].url); + if (node === null) { + node = scorm_tree_node.rootNode.children[0]; + node.title = scoes_nav[result.nextactivity.id].url; + } } - node.title = scoes_nav[result.nextactivity.id].url; launch_sco = result.nextactivity.id; scorm_activate_item(node); scorm_fixnav(); } else { - scorm_activate_item(scorm_prev(scorm_tree_node.getHighlightedNode(), true)); + scorm_activate_item(scorm_prev(scorm_tree_node.getSelectedNodes()[0], true)); } - } else { - scorm_activate_item(scorm_prev(scorm_tree_node.getHighlightedNode(), true)); - } + } else { + scorm_activate_item(scorm_prev(scorm_tree_node.getSelectedNodes()[0], true)); + } }; // Launch next sco var scorm_launch_next_sco = function () { - var result = null; - if (scoes_nav[launch_sco].flow == 1) { + var result = null; + if (scoes_nav[launch_sco].flow === 1) { var datastring = scoes_nav[launch_sco].url + '&function=scorm_seq_flow&request=forward'; result = scorm_ajax_request(M.cfg.wwwroot + '/mod/scorm/datamodels/sequencinghandler.php?', datastring); mod_scorm_seq = encodeURIComponent(result); result = Y.JSON.parse (result); - if (typeof result.nextactivity.id != undefined) { - var node = scorm_next(scorm_tree_node.getHighlightedNode()) - if (node == null) { - // Avoid use of TreeView for Navigation - node = scorm_tree_node.getHighlightedNode(); - } + if (typeof result.nextactivity !== 'undefined' && typeof result.nextactivity.id !== 'undefined') { + var node = scorm_next(scorm_tree_node.getSelectedNodes()[0]); + if (node === null) { + // Avoid use of TreeView for Navigation. + node = scorm_tree_node.getSelectedNodes()[0]; + } + node = scorm_tree_node.getNodeByAttribute('title', scoes_nav[result.nextactivity.id].url); + if (node === null) { + node = scorm_tree_node.rootNode.children[0]; node.title = scoes_nav[result.nextactivity.id].url; - launch_sco = result.nextactivity.id; - scorm_activate_item(node); - scorm_fixnav(); + } + launch_sco = result.nextactivity.id; + scorm_activate_item(node); + scorm_fixnav(); } else { - scorm_activate_item(scorm_next(scorm_tree_node.getHighlightedNode(), true)); + scorm_activate_item(scorm_next(scorm_tree_node.getSelectedNodes()[0], true)); } - } else { - scorm_activate_item(scorm_next(scorm_tree_node.getHighlightedNode(), true)); - } + } else { + scorm_activate_item(scorm_next(scorm_tree_node.getSelectedNodes()[0], true)); + } }; mod_scorm_launch_prev_sco = scorm_launch_prev_sco; mod_scorm_launch_next_sco = scorm_launch_next_sco; + var cssclasses = { + // YUI grid class: use 100% of the available width to show only content, TOC hidden. + scorm_grid_content_toc_hidden: 'yui3-u-1', + // YUI grid class: use 1/5 of the available width to show TOC. + scorm_grid_toc: 'yui3-u-1-5', + // YUI grid class: use 1/24 of the available width to show TOC toggle button. + scorm_grid_toggle: 'yui3-u-1-24', + // YUI grid class: use 3/4 of the available width to show content, TOC visible. + scorm_grid_content_toc_visible: 'yui3-u-3-4', + // Reduce height of #scorm_object to accomodate nav buttons under content. + scorm_nav_under_content: 'scorm_nav_under_content', + disabled: 'disabled' + }; // layout - Y.YUI2.widget.LayoutUnit.prototype.STR_COLLAPSE = M.str.moodle.hide; - Y.YUI2.widget.LayoutUnit.prototype.STR_EXPAND = M.str.moodle.show; + Y.one('#scorm_toc_title').setHTML(toc_title); if (scorm_disable_toc) { - scorm_layout_widget = new Y.YUI2.widget.Layout('scorm_layout', { - minWidth: 255, - minHeight: 600, - units: [ - { position: 'left', body: 'scorm_toc', header: toc_title, width: 0, resize: true, gutter: '0px 0px 0px 0px', collapse: false}, - { position: 'center', body: '
', gutter: '0px 0px 0px 0px', scroll: true} - ] - }); + Y.one('#scorm_toc').addClass(cssclasses.disabled); + Y.one('#scorm_toc_toggle').addClass(cssclasses.disabled); + Y.one('#scorm_content').addClass(cssclasses.scorm_grid_content_toc_hidden); } else { - scorm_layout_widget = new Y.YUI2.widget.Layout('scorm_layout', { - minWidth: 255, - minHeight: 600, - units: [ - { position: 'left', body: 'scorm_toc', header: toc_title, width: 250, resize: true, gutter: '2px 5px 5px 2px', collapse: true, minWidth:250, maxWidth: 590}, - { position: 'center', body: '
', gutter: '2px 5px 5px 2px', scroll: true} - ] - }); + Y.one('#scorm_toc').addClass(cssclasses.scorm_grid_toc); + Y.one('#scorm_toc_toggle').addClass(cssclasses.scorm_grid_toggle); + Y.one('#scorm_toc_toggle_btn') + .setHTML('<') + .setAttribute('title', M.util.get_string('hide', 'moodle')); + Y.one('#scorm_content').addClass(cssclasses.scorm_grid_content_toc_visible); + scorm_toggle_toc(true); } - scorm_layout_widget.render(); - var left = scorm_layout_widget.getUnitByPosition('left'); - if (!scorm_disable_toc) { - left.on('collapse', function() { - scorm_resize_frame(); - }); - left.on('expand', function() { - scorm_resize_frame(); - }); - } - // ugly resizing hack that works around problems with resizing of iframes and objects - left._resize.on('startResize', function() { - var obj = Y.YUI2.util.Dom.get('scorm_object'); - obj.style.display = 'none'; - }); - left._resize.on('endResize', function() { - var obj = Y.YUI2.util.Dom.get('scorm_object'); - obj.style.display = 'block'; - scorm_resize_frame(); - }); - // hide the TOC if that is the default if (!scorm_disable_toc) { if (scorm_hide_toc == true) { - left.collapse(); + Y.one('#scorm_toc').addClass(cssclasses.disabled); + Y.one('#scorm_toc_toggle_btn') + .setHTML('>') + .setAttribute('title', M.util.get_string('show', 'moodle')); + Y.one('#scorm_content') + .removeClass(cssclasses.scorm_grid_content_toc_visible) + .addClass(cssclasses.scorm_grid_content_toc_hidden); } } + + // TOC Resize handle. + var layout_width = parseInt(Y.one('#scorm_layout').getComputedStyle('width'), 10); + var scorm_resize_handle = new Y.Resize({ + node: '#scorm_toc', + handles: 'r', + defMinWidth: 0.2 * layout_width + }); // TOC tree - var tree = new Y.YUI2.widget.TreeView('scorm_tree'); + var toc_source = Y.one('#scorm_tree > ul'); + var toc = scorm_parse_toc_tree(toc_source); + // Empty container after parsing toc. + var el = document.getElementById('scorm_tree'); + el.innerHTML = ''; + var tree = new Y.TreeView({ + container: '#scorm_tree', + nodes: toc, + multiSelect: false + }); scorm_tree_node = tree; - tree.singleNodeHighlight = true; - tree.subscribe('labelClick', function(node) { + // Trigger after instead of on, avoid recursive calls. + tree.after('select', function(e) { + var node = e.node; if (node.title == '' || node.title == null) { return; //this item has no navigation } + // If item is already active, return; avoid recursive calls. + if (Y.one('#scorm_data')) { + var scorm_active_url = Y.one('#scorm_object').getAttribute('src'); + var node_full_url = M.cfg.wwwroot + '/mod/scorm/loadSCO.php?' + node.title; + if (node_full_url === scorm_active_url) { + return; + } + } + // Update launch_sco. + if (typeof node.scoid !== 'undefined') { + launch_sco = node.scoid; + } scorm_activate_item(node); if (node.children.length) { scorm_bloody_labelclick = true; } }); if (!scorm_disable_toc) { - tree.subscribe('collapse', function(node) { + tree.on('close', function(e) { if (scorm_bloody_labelclick) { scorm_bloody_labelclick = false; return false; } }); - tree.subscribe('expand', function(node) { + tree.subscribe('open', function(e) { if (scorm_bloody_labelclick) { scorm_bloody_labelclick = false; return false; } }); } - tree.expandAll(); tree.render(); + tree.openAll(); // On getting the window, always set the focus on the current item - Y.YUI2.util.Event.on(window, 'focus', function (e) { - var current = scorm_tree_node.getHighlightedNode(); - var left = scorm_layout_widget.getUnitByPosition('left'); - if (current && left.expand) { - current.focus(); + Y.one(Y.config.win).on('focus', function (e) { + var current = scorm_tree_node.getSelectedNodes()[0]; + var toc_disabled = Y.one('#scorm_toc').hasClass('disabled'); + if (current.id && !toc_disabled) { + Y.one('#' + current.id).focus(); } }); // navigation if (scorm_hide_nav == false) { - var left = scorm_layout_widget.getUnitByPosition('left'); - navposition = Y.YUI2.util.Dom.getXY(left); - navposition[1] += 200; - scorm_nav_panel = new Y.YUI2.widget.Panel('scorm_navpanel', { visible:true, draggable:true, close:false, xy: navposition, - autofillheight: "body"} ); - scorm_nav_panel.setHeader(M.str.scorm.navigation); + // TODO: make some better&accessible buttons. + var navbuttonshtml = ' ' + + '  ' + + ' '; + if (nav_display === 1) { + Y.one('#scorm_navpanel').setHTML(navbuttonshtml); + } else { + // Nav panel is floating type. + var navposition = null; + if (navposition_left < 0 && navposition_top < 0) { + // Set default XY. + navposition = Y.one('#scorm_toc').getXY(); + navposition[1] += 200; + } else { + // Set user defined XY. + navposition = []; + navposition[0] = parseInt(navposition_left, 10); + navposition[1] = parseInt(navposition_top, 10); + } + scorm_nav_panel = new Y.Panel({ + fillHeight: "body", + headerContent: M.util.get_string('navigation', 'scorm'), + visible: true, + xy: navposition, + zIndex: 999 + }); + scorm_nav_panel.set('bodyContent', navbuttonshtml); + scorm_nav_panel.removeButton('close'); + scorm_nav_panel.plug(Y.Plugin.Drag, {handles: ['.yui3-widget-hd']}); + scorm_nav_panel.render(); + } - //TODO: make some better&accessible buttons - scorm_nav_panel.setBody(''); - scorm_nav_panel.render(); - scorm_buttons[0] = new Y.YUI2.widget.Button('nav_skipprev'); - scorm_buttons[1] = new Y.YUI2.widget.Button('nav_prev'); - scorm_buttons[2] = new Y.YUI2.widget.Button('nav_up'); - scorm_buttons[3] = new Y.YUI2.widget.Button('nav_next'); - scorm_buttons[4] = new Y.YUI2.widget.Button('nav_skipnext'); - scorm_buttons[0].on('click', function(ev) { - scorm_activate_item(scorm_skipprev(scorm_tree_node.getHighlightedNode(), true)); + scorm_buttons[0] = new Y.Button({ + srcNode: '#nav_skipprev', + render: true, + on: { + 'click' : function(ev) { + scorm_activate_item(scorm_skipprev(scorm_tree_node.getSelectedNodes()[0], true)); + }, + 'keydown' : function(ev) { + if (ev.domEvent.keyCode === 13 || ev.domEvent.keyCode === 32) { + scorm_activate_item(scorm_skipprev(scorm_tree_node.getSelectedNodes()[0], true)); + } + } + } }); - scorm_buttons[1].on('click', function(ev) { - scorm_launch_prev_sco(); + scorm_buttons[1] = new Y.Button({ + srcNode: '#nav_prev', + render: true, + on: { + 'click' : function(ev) { + scorm_launch_prev_sco(); + }, + 'keydown' : function(ev) { + if (ev.domEvent.keyCode === 13 || ev.domEvent.keyCode === 32) { + scorm_launch_prev_sco(); + } + } + } }); - scorm_buttons[2].on('click', function(ev) { - scorm_activate_item(scorm_up(scorm_tree_node.getHighlightedNode(), true)); + scorm_buttons[2] = new Y.Button({ + srcNode: '#nav_up', + render: true, + on: { + 'click' : function(ev) { + scorm_activate_item(scorm_up(scorm_tree_node.getSelectedNodes()[0], true)); + }, + 'keydown' : function(ev) { + if (ev.domEvent.keyCode === 13 || ev.domEvent.keyCode === 32) { + scorm_activate_item(scorm_up(scorm_tree_node.getSelectedNodes()[0], true)); + } + } + } }); - scorm_buttons[3].on('click', function(ev) { - scorm_launch_next_sco(); + scorm_buttons[3] = new Y.Button({ + srcNode: '#nav_next', + render: true, + on: { + 'click' : function(ev) { + scorm_launch_next_sco(); + }, + 'keydown' : function(ev) { + if (ev.domEvent.keyCode === 13 || ev.domEvent.keyCode === 32) { + scorm_launch_next_sco(); + } + } + } }); - scorm_buttons[4].on('click', function(ev) { - scorm_activate_item(scorm_skipnext(scorm_tree_node.getHighlightedNode(), true)); + scorm_buttons[4] = new Y.Button({ + srcNode: '#nav_skipnext', + render: true, + on: { + 'click' : function(ev) { + scorm_activate_item(scorm_skipnext(scorm_tree_node.getSelectedNodes()[0], true)); + }, + 'keydown' : function(ev) { + if (ev.domEvent.keyCode === 13 || ev.domEvent.keyCode === 32) { + scorm_activate_item(scorm_skipnext(scorm_tree_node.getSelectedNodes()[0], true)); + } + } + } }); - scorm_nav_panel.render(); } // finally activate the chosen item - var scorm_first_url = tree.getRoot().children[0]; + var scorm_first_url = null; + if (tree.rootNode.children[0].title !== scoes_nav[launch_sco].url) { + var node = tree.getNodeByAttribute('title', scoes_nav[launch_sco].url); + if (node !== null) { + scorm_first_url = node; + } + } else { + scorm_first_url = tree.rootNode.children[0]; + } + if (scorm_first_url == null) { // This is probably a single sco with no children (AICC Direct uses this). - scorm_first_url = tree.getRoot(); + scorm_first_url = tree.rootNode; } scorm_first_url.title = scoes_nav[launch_sco].url; scorm_activate_item(scorm_first_url); // resizing - scorm_resize_layout(false); + scorm_resize_layout(); + // Collapse/expand TOC. + Y.one('#scorm_toc_toggle').on('click', scorm_toggle_toc); + Y.one('#scorm_toc_toggle').on('key', scorm_toggle_toc, 'down:enter,32'); // fix layout if window resized - window.onresize = function() { - scorm_resize_layout(true); - }; + Y.on("windowresize", function() { + scorm_resize_layout(); + var toc_displayed = Y.one('#scorm_toc').getComputedStyle('display') !== 'none'; + if ((!scorm_disable_toc && !scorm_hide_toc) || toc_displayed) { + scorm_toggle_toc(true); + } + // Set 20% as minWidth constrain of TOC. + var layout_width = parseInt(Y.one('#scorm_layout').getComputedStyle('width'), 10); + scorm_resize_handle.set('defMinWidth', 0.2 * layout_width); + }); + // On resize drag, change width of scorm_content. + scorm_resize_handle.on('resize:resize', function() { + var tocwidth = parseInt(Y.one('#scorm_toc').getComputedStyle('width'), 10); + var layoutwidth = parseInt(Y.one('#scorm_layout').getStyle('width'), 10); + Y.one('#scorm_content').setStyle('width', (layoutwidth - tocwidth - 60)); + }); }); }; M.mod_scorm.connectPrereqCallback = { - success: function(o) { - YUI().use('yui2-treeview', 'yui2-layout', function(Y) { - // MDL-29159 The core version of getContentHtml doesn't escape text properly. - Y.YUI2.widget.TextNode.prototype.getContentHtml = function() { - var sb = []; - sb[sb.length] = this.href ? '':''; - return sb.join(""); - }; - - if (o.responseText !== undefined) { - var tree = new Y.YUI2.widget.TreeView('scorm_tree'); - if (scorm_tree_node && o.responseText) { - var hnode = scorm_tree_node.getHighlightedNode(); - var hidx = null; - if (hnode) { - hidx = hnode.index + scorm_tree_node.getNodeCount(); - } - // all gone - var root_node = scorm_tree_node.getRoot(); - while (root_node.children.length > 0) { - scorm_tree_node.removeNode(root_node.children[0]); - } - } - // make sure the temporary tree element is not there - var el_old_tree = document.getElementById('scormtree123'); - if (el_old_tree) { - el_old_tree.parentNode.removeChild(el_old_tree); - } - var el_new_tree = document.createElement('div'); - var pagecontent = document.getElementById("page-content"); - el_new_tree.setAttribute('id','scormtree123'); - el_new_tree.innerHTML = o.responseText; - // make sure it doesnt show - el_new_tree.style.display = 'none'; - pagecontent.appendChild(el_new_tree) - // ignore the first level element as this is the title - var startNode = el_new_tree.firstChild.firstChild; - if (startNode.tagName == 'LI') { - // go back to the beginning - startNode = el_new_tree; - } - //var sXML = new XMLSerializer().serializeToString(startNode); - scorm_tree_node.buildTreeFromMarkup('scormtree123'); - var el = document.getElementById('scormtree123'); - el.parentNode.removeChild(el); - scorm_tree_node.expandAll(); - scorm_tree_node.render(); - if (hidx != null) { - hnode = scorm_tree_node.getNodeByIndex(hidx); - if (hnode) { - hnode.highlight(); - var left = scorm_layout_widget.getUnitByPosition('left'); - if (left.expand) { - hnode.focus(); + // All gone with clear, add new root node. + scorm_tree_node.clear(scorm_tree_node.createNode()); + } + // Make sure the temporary tree element is not there. + var el_old_tree = document.getElementById('scormtree123'); + if (el_old_tree) { + el_old_tree.parentNode.removeChild(el_old_tree); + } + var el_new_tree = document.createElement('div'); + var pagecontent = document.getElementById("page-content"); + el_new_tree.setAttribute('id','scormtree123'); + el_new_tree.innerHTML = o.responseText; + // Make sure it does not show. + el_new_tree.style.display = 'none'; + pagecontent.appendChild(el_new_tree); + // Ignore the first level element as this is the title. + var startNode = el_new_tree.firstChild.firstChild; + if (startNode.tagName == 'LI') { + // Go back to the beginning. + startNode = el_new_tree; + } + var toc_source = Y.one('#scormtree123 > ul'); + var toc = mod_scorm_parse_toc_tree(toc_source); + scorm_tree_node.appendNode(scorm_tree_node.rootNode, toc); + var el = document.getElementById('scormtree123'); + el.parentNode.removeChild(el); + scorm_tree_node.render(); + scorm_tree_node.openAll(); + if (stitle !== null) { + snode = scorm_tree_node.getNodeByAttribute('title', stitle); + // Do not let destroyed node to be selected. + if (snode && !snode.state.destroyed) { + snode.select(); + var toc_disabled = Y.one('#scorm_toc').hasClass('disabled'); + if (!toc_disabled) { + if (!snode.state.selected) { + snode.select(); } } } } - }); + } }, - failure: function(o) { + failure: function(id, o) { // TODO: do some sort of error handling. } diff --git a/mod/scorm/player.php b/mod/scorm/player.php index 6a5b84f60be..d5e4b34b756 100644 --- a/mod/scorm/player.php +++ b/mod/scorm/player.php @@ -84,6 +84,13 @@ $forcejs = get_config('scorm', 'forcejavascript'); if (!empty($forcejs)) { $PAGE->add_body_class('forcejavascript'); } +$collapsetocwinsize = get_config('scorm', 'collapsetocwinsize'); +if (empty($collapsetocwinsize)) { + // Set as default window size to collapse TOC. + $collapsetocwinsize = 767; +} else { + $collapsetocwinsize = intval($collapsetocwinsize); +} require_login($course, false, $cm); @@ -246,7 +253,7 @@ if ($result->prerequisites) { ?> id, $mode, $attempt); +$scoes = scorm_get_toc_object($USER, $scorm, $currentorg, $sco->id, $mode, $attempt); $adlnav = scorm_get_adlnav_json($scoes['scoes']); if (empty($scorm->popup) || $displaymode == 'popup') { @@ -258,7 +265,9 @@ if (empty($scorm->popup) || $displaymode == 'popup') { 'fullpath' => '/mod/scorm/module.js', 'requires' => array('json'), ); - $PAGE->requires->js_init_call('M.mod_scorm.init', array($scorm->hidenav, $scorm->hidetoc, $result->toctitle, $name, $sco->id, $adlnav), false, $jsmodule); + $scorm->nav = intval($scorm->nav); + $PAGE->requires->js_init_call('M.mod_scorm.init', array($scorm->nav, $scorm->navpositionleft, $scorm->navpositiontop, $scorm->hidetoc, + $collapsetocwinsize, $result->toctitle, $name, $sco->id, $adlnav), false, $jsmodule); } if (!empty($forcejs)) { echo $OUTPUT->box(get_string("forcejavascriptmessage", "scorm"), "generalbox boxaligncenter forcejavascriptmessage"); diff --git a/mod/scorm/settings.php b/mod/scorm/settings.php index aacb8e213df..8781dd922bb 100644 --- a/mod/scorm/settings.php +++ b/mod/scorm/settings.php @@ -60,9 +60,21 @@ if ($ADMIN->fulltree) { get_string('hidetoc', 'scorm'), get_string('hidetocdesc', 'scorm'), array('value' => 0, 'adv' => true), scorm_get_hidetoc_array())); - $settings->add(new admin_setting_configselect_with_advanced('scorm/hidenav', - get_string('hidenav', 'scorm'), get_string('hidenavdesc', 'scorm'), - array('value' => 0, 'adv' => false), $yesno)); + $settings->add(new admin_setting_configselect_with_advanced('scorm/nav', + get_string('nav', 'scorm'), get_string('navdesc', 'scorm'), + array('value' => SCORM_NAV_UNDER_CONTENT, 'adv' => true), scorm_get_navigation_display_array())); + + $settings->add(new admin_setting_configtext_with_advanced('scorm/navpositionleft', + get_string('fromleft', 'scorm'), get_string('navpositionleft', 'scorm'), + array('value' => -100, 'adv' => true))); + + $settings->add(new admin_setting_configtext_with_advanced('scorm/navpositiontop', + get_string('fromtop', 'scorm'), get_string('navpositiontop', 'scorm'), + array('value' => -100, 'adv' => true))); + + $settings->add(new admin_setting_configtext_with_advanced('scorm/collapsetocwinsize', + get_string('collapsetocwinsize', 'scorm'), get_string('collapsetocwinsizedesc', 'scorm'), + array('value' => 767, 'adv' => true))); $settings->add(new admin_setting_configselect_with_advanced('scorm/displayattemptstatus', get_string('displayattemptstatus', 'scorm'), get_string('displayattemptstatusdesc', 'scorm'), diff --git a/mod/scorm/styles.css b/mod/scorm/styles.css index c36fe31b6eb..77d694be4f6 100644 --- a/mod/scorm/styles.css +++ b/mod/scorm/styles.css @@ -3,24 +3,32 @@ .path-mod-scorm .scorm-center {text-align: center;} .path-mod-scorm .scorm-right {text-align: right;} .path-mod-scorm .scoframe {position: relative;width: 100%;height: 100%;} +.ios #scormpage #scorm_content {-webkit-overflow-scrolling: touch; overflow: scroll;} -#page-mod-scorm-player #scormobject {height: 100%;} #page-mod-scorm-player #scormtop {position: relative;width: 100%;height: 30px;} #page-mod-scorm-player #scormbrowse {position: absolute;left: 5px;top: 0px;} #page-mod-scorm-player #scormnav {position: absolute;right: 5px;top: 0px;text-align: center;top: 3px;width: 100%;} #page-mod-scorm-player #scormbox {width: 74%;height: 100%;position: absolute;right: 0px;top: 0px;} #page-mod-scorm-player #scormpage {position: relative;width: 100%;height: 100%;} -#page-mod-scorm-player #scormpage #toctree {position:relative;width:100%;overflow-x: auto;overflow-y: auto;} +#page-mod-scorm-player #scormpage #toctree {position:relative;width:100%;} #page-mod-scorm-player #tocbox {position: relative;left: 0px;width: 100%;height: 100%;font-size: 0.8em;} #page-mod-scorm-player #toctree { overflow: visible; } #page-mod-scorm-player #tochead {position: relative;text-align: center;top: 3px;height: 30px;} #page-mod-scorm-player #scormpage .scoframe {frameborder: 0;} +#page-mod-scorm-player #scormpage #scorm_object {border: none; width: 98%; height: 98%;} +#page-mod-scorm-player #scormpage #scorm_object.scorm_nav_under_content {height: 95%;} +#page-mod-scorm-player #scormpage #scorm_content {height: 100%;} +#page-mod-scorm-player #scormpage #scorm_toc {position: relative;} +#page-mod-scorm-player #scormpage #scorm_toc_title {font-size: 1.2em; font-weight: bold;} +#page-mod-scorm-player #scormpage #scorm_tree {border-right: 5px solid rgb(239, 245, 255);} +#page-mod-scorm-player #scormpage #scorm_navpanel {text-align: center;} #page-mod-scorm-player .toc, #page-mod-scorm-player .no-toc {width: 100%;} #page-mod-scorm-player .structlist {list-style-type: none;white-space: nowrap;} #page-mod-scorm-player .structurelist {position: relative;list-style-type: none;width: 96%;margin:0;padding:0;} #page-mod-scorm-player .structurelist ul {padding-left: 0.5em;margin-left: 0.5em;} +#page-mod-scorm-player #scormpage #scorm_toc.disabled {display:none} #page-mod-scorm-view .structurelist {list-style-type: none;white-space: nowrap;} #page-mod-scorm-view .structurelist {list-style-type: none;white-space: nowrap;} @@ -45,3 +53,37 @@ word-break: break-all; } +#page-mod-scorm-player #scormpage span.yui3-treeview-icon {display: none;} +#page-mod-scorm-player #scormpage li.yui3-treeview-has-children > div.yui3-treeview-row > span.yui3-treeview-icon {display: block;} + +#page-mod-scorm-player #scormpage div.yui3-u-1, +#page-mod-scorm-player #scormpage div.yui3-u-3-4, +#page-mod-scorm-player #scormpage div.yui3-u-1-5, +#page-mod-scorm-player #scormpage div.yui3-u-1-24 { + display: inline-block; + *display: inline; + zoom: 1; + letter-spacing: normal; + word-spacing: normal; + vertical-align: top; + text-rendering: auto; +} + +#page-mod-scorm-player #scormpage div.yui3-u-1 {display: block;} + +#page-mod-scorm-player #scormpage div.yui3-u-3-4 {width: 75%;} + +#page-mod-scorm-player #scormpage div.yui3-u-1-5 {width: 20%;} + +#page-mod-scorm-player #scormpage div.yui3-u-1-24 {width: 4.1666%;} + +#page-mod-scorm-player #scormpage div.yui3-g-r {*letter-spacing: normal; *word-spacing: -0.43em;} + +/** +* Opera as of 12 on Windows needs word-spacing. +* The ".opera-only" selector is used to prevent actual prefocus styling +* and is not required in markup. +*/ +#page-mod-scorm-player .opera-only :-o-prefocus, + +#page-mod-scorm-player #scormpage div.yui3-g-r img {max-width: 100%;} diff --git a/mod/scorm/version.php b/mod/scorm/version.php index 56084add8c6..3b43237306f 100644 --- a/mod/scorm/version.php +++ b/mod/scorm/version.php @@ -25,7 +25,7 @@ defined('MOODLE_INTERNAL') || die(); -$module->version = 2013081303; // The current module version (Date: YYYYMMDDXX) +$module->version = 2013090100; // The current module version (Date: YYYYMMDDXX) $module->requires = 2013050100; // Requires this Moodle version $module->component = 'mod_scorm'; // Full name of the plugin (used for diagnostics) $module->cron = 300;