From ff5fe31160a08502d2d8ef55925332147eac3bd9 Mon Sep 17 00:00:00 2001 From: skodak <skodak> Date: Wed, 24 Jun 2009 22:34:29 +0000 Subject: [PATCH] MDL-19580 cleanup of require css and js filepicker code --- lib/adminlib.php | 2 + lib/ajax/ajaxlib.php | 4 +- lib/editor/tinymce/lib.php | 8 +- lib/editorlib.php | 31 +++++ lib/form/editor.php | 8 +- lib/form/filemanager.php | 8 +- lib/form/filepicker.php | 7 +- lib/form/htmleditor.php | 2 + lib/javascript.php | 2 - lib/weblib.php | 1 + mod/resource/type/file/resource.class.php | 4 +- repository/lib.php | 135 +++++----------------- repository/repository.css | 73 ++++++++++++ repository/repository.src.js | 5 +- 14 files changed, 169 insertions(+), 121 deletions(-) create mode 100644 repository/repository.css diff --git a/lib/adminlib.php b/lib/adminlib.php index 09ccc5b740..2751eb138b 100644 --- a/lib/adminlib.php +++ b/lib/adminlib.php @@ -1578,6 +1578,7 @@ class admin_setting_confightmleditor extends admin_setting_configtext { $this->rows = $rows; $this->cols = $cols; parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype); + editors_head_setup(); } /** * Returns an XHTML string for the editor @@ -2985,6 +2986,7 @@ class admin_setting_special_frontpagedesc extends admin_setting { */ public function __construct() { parent::__construct('summary', get_string('frontpagedescription'), get_string('frontpagedescriptionhelp'), NULL); + editors_head_setup(); } /** diff --git a/lib/ajax/ajaxlib.php b/lib/ajax/ajaxlib.php index 7bdbc3a457..cc33e68298 100644 --- a/lib/ajax/ajaxlib.php +++ b/lib/ajax/ajaxlib.php @@ -658,7 +658,9 @@ class required_yui_lib extends linked_requirement { $this->jss[] = $manager->js($jsurl, true); } foreach ($cssurls as $cssurl) { - //$manager->css($cssurl, true); + // this might be a bit problematic because it requires yui to be + // requested before print_header() - this was not required in 1.9.x + $manager->css($cssurl, true); } } diff --git a/lib/editor/tinymce/lib.php b/lib/editor/tinymce/lib.php index 2d66a6a5f1..0bf66896db 100644 --- a/lib/editor/tinymce/lib.php +++ b/lib/editor/tinymce/lib.php @@ -51,11 +51,15 @@ class tinymce_texteditor extends texteditor { return true; } - public function use_editor($elementid, array $options=null) { + public function head_setup() { global $CFG, $PAGE; - + require_once("$CFG->dirroot/repository/lib.php"); $PAGE->requires->js('/lib/editor/tinymce/tiny_mce_src.js'); $PAGE->requires->js('/lib/editor/tinymce/extra/tinymce_utils.js'); + } + + public function use_editor($elementid, array $options=null) { + global $PAGE; $PAGE->requires->js_function_call('mce_init_editor', array($elementid, $this->get_init_params($elementid, $options))); } diff --git a/lib/editorlib.php b/lib/editorlib.php index cb629f48c1..0696bf26e6 100644 --- a/lib/editorlib.php +++ b/lib/editorlib.php @@ -111,6 +111,30 @@ function get_available_editors() { return $editors; } +/** + * Setup all JS and CSS needed for editors. + * @return void + */ +function editors_head_setup() { + global $CFG; + + if (empty($CFG->texteditors)) { + $CFG->texteditors = 'tinymce,textarea'; + } + $active = explode(',', $CFG->texteditors); + + foreach ($active as $editorname) { + if (!$editor = get_texteditor($editorname)) { + continue; + } + if (!$editor->supported_by_browser()) { + // bad luck, this editor is not compatible + continue; + } + $editor->head_setup(); + } +} + /** * Base abstract text editor class. * @@ -150,6 +174,13 @@ abstract class texteditor { * @return void */ public abstract function use_editor($elementid, array $options=null); + + /** + * Setup all JS and CSS needed for editor. + * @return void + */ + public function head_setup() { + } } diff --git a/lib/form/editor.php b/lib/form/editor.php index 9de29d3988..053f3cd1c7 100644 --- a/lib/form/editor.php +++ b/lib/form/editor.php @@ -15,6 +15,7 @@ class MoodleQuickForm_editor extends HTML_QuickForm_element { function MoodleQuickForm_editor($elementName=null, $elementLabel=null, $attributes=null, $options=null) { global $CFG; + require_once("$CFG->dirroot/repository/lib.php"); $options = (array)$options; foreach ($options as $name=>$value) { @@ -29,6 +30,9 @@ class MoodleQuickForm_editor extends HTML_QuickForm_element { $this->_options['context'] = get_context_instance(CONTEXT_SYSTEM); } parent::HTML_QuickForm_element($elementName, $elementLabel, $attributes); + + repository_head_setup(); + editors_head_setup(); } function setName($name) { @@ -171,9 +175,9 @@ class MoodleQuickForm_editor extends HTML_QuickForm_element { require_once($CFG->dirroot.'/repository/lib.php'); $client_id = uniqid(); - $ret = repository_get_client($ctx, $client_id, array('image', 'video', 'media'), '*'); + $repojs = repository_get_client($ctx, $client_id, array('image', 'video', 'media'), '*'); - $str .= $ret['css'].$ret['js']; + $str .= $repojs; $str .= <<<EOD <script type="text/javascript"> id2clientid['$id'] = '$client_id'; diff --git a/lib/form/filemanager.php b/lib/form/filemanager.php index 630659b4b5..055a2b3846 100644 --- a/lib/form/filemanager.php +++ b/lib/form/filemanager.php @@ -9,6 +9,7 @@ class MoodleQuickForm_filemanager extends HTML_QuickForm_element { function MoodleQuickForm_filemanager($elementName=null, $elementLabel=null, $attributes=null, $options=null) { global $CFG; + require_once("$CFG->dirroot/repository/lib.php"); $options = (array)$options; foreach ($options as $name=>$value) { @@ -20,6 +21,8 @@ class MoodleQuickForm_filemanager extends HTML_QuickForm_element { $this->_options['maxbytes'] = get_max_upload_file_size($CFG->maxbytes, $options['maxbytes']); } parent::HTML_QuickForm_element($elementName, $elementLabel, $attributes); + + repository_head_setup(); } function setName($name) { @@ -162,15 +165,14 @@ class MoodleQuickForm_filemanager extends HTML_QuickForm_element { } $client_id = uniqid(); - $repo_info = repository_get_client($context, $client_id, $this->_options['filetypes'], $this->_options['returnvalue']); + $repojs = repository_get_client($context, $client_id, $this->_options['filetypes'], $this->_options['returnvalue']); $html = $this->_get_draftfiles($draftitemid, $client_id); $accessiblefp = get_string('accessiblefilepicker', 'repository'); $str = $this->_getTabs(); $str .= $html; - $str .= $repo_info['css']; - $str .= $repo_info['js']; + $str .= $repojs; $str .= <<<EOD <input value="$draftitemid" name="{$this->_attributes['name']}" type="hidden" /> <a href="###" id="btnadd-{$client_id}" style="display:none" class="btnaddfile" onclick="return callpicker('$id', '$client_id', '$draftitemid')">$straddfile</a> diff --git a/lib/form/filepicker.php b/lib/form/filepicker.php index 5739b3b783..cd667c8d83 100644 --- a/lib/form/filepicker.php +++ b/lib/form/filepicker.php @@ -18,6 +18,7 @@ class MoodleQuickForm_filepicker extends HTML_QuickForm_input { function MoodleQuickForm_filepicker($elementName=null, $elementLabel=null, $attributes=null, $options=null) { global $CFG; + require_once("$CFG->dirroot/repository/lib.php"); $options = (array)$options; foreach ($options as $name=>$value) { @@ -29,6 +30,8 @@ class MoodleQuickForm_filepicker extends HTML_QuickForm_input { $this->_options['maxbytes'] = get_max_upload_file_size($CFG->maxbytes, $options['maxbytes']); } parent::HTML_QuickForm_input($elementName, $elementLabel, $attributes); + + repository_head_setup(); } function setHelpButton($helpbuttonargs, $function='helpbutton') { @@ -84,14 +87,14 @@ class MoodleQuickForm_filepicker extends HTML_QuickForm_input { $context = get_context_instance(CONTEXT_COURSE, $COURSE->id); } $client_id = uniqid(); - $repository_info = repository_get_client($context, $client_id, $this->_options['filetypes'], $this->_options['returnvalue']); + $repojs = repository_get_client($context, $client_id, $this->_options['filetypes'], $this->_options['returnvalue']); $id = $this->_attributes['id']; $elname = $this->_attributes['name']; $str = $this->_getTabs(); $str .= '<input type="hidden" name="'.$elname.'" id="'.$id.'" '.$draftvalue.' />'; - $str .= $repository_info['css'].$repository_info['js']; + $str .= $repojs; $str .= <<<EOD <a href="#nonjsfp" class="btnaddfile" onclick="return callpicker('$client_id', '$id', '$draftvalue')">$straddfile</a> diff --git a/lib/form/htmleditor.php b/lib/form/htmleditor.php index 23dfca6ffc..853979e7e1 100644 --- a/lib/form/htmleditor.php +++ b/lib/form/htmleditor.php @@ -36,6 +36,8 @@ class MoodleQuickForm_htmleditor extends MoodleQuickForm_textarea{ $this->_type='textarea'; } $this->_canUseHtmlEditor = $this->_options['canUseHtmlEditor']; + + editors_head_setup(); } /** * set html for help button diff --git a/lib/javascript.php b/lib/javascript.php index 7816e183c2..5b09887575 100644 --- a/lib/javascript.php +++ b/lib/javascript.php @@ -45,8 +45,6 @@ setTimeout('fix_column_widths()', 20); </script> <script type="text/javascript"> //<![CDATA[ -var id2clientid = {}; -var id2itemid = {}; <?php if (!empty($focus)) { if(($pos = strpos($focus, '.')) !== false) { diff --git a/lib/weblib.php b/lib/weblib.php index 79139a9e1a..dc5bdce473 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -4991,6 +4991,7 @@ function print_textarea($usehtmleditor, $rows, $cols, $width, $height, $name, $v } if ($usehtmleditor) { + editors_head_setup(); $editor = get_preferred_texteditor(FORMAT_HTML); $editor->use_editor($id, array('legacy'=>true)); } else { diff --git a/mod/resource/type/file/resource.class.php b/mod/resource/type/file/resource.class.php index 20c5bfde08..455ff92eed 100644 --- a/mod/resource/type/file/resource.class.php +++ b/mod/resource/type/file/resource.class.php @@ -193,6 +193,8 @@ class resource_file extends resource_base { */ function display() { global $CFG, $THEME, $USER, $PAGE; + ///Yahoo javascript libaries for updating embedded object size + $PAGE->requires->yui_lib('container'); /// Set up generic stuff first, including checking for access parent::display(); @@ -386,8 +388,6 @@ class resource_file extends resource_base { if (empty($frameset) and !$embedded and !$inpopup and ($resource->options == "frame" || $resource->options == "objectframe") and empty($USER->screenreader)) { /// display the resource into a object tag if ($resource->options == "objectframe") { - ///Yahoo javascript libaries for updating embedded object size - $PAGE->requires->yui_lib('container'); ///Moodle Header and navigation bar $navigation = build_navigation($this->navlinks, $cm); diff --git a/repository/lib.php b/repository/lib.php index bcd6c3618d..c0fc1f93c8 100644 --- a/repository/lib.php +++ b/repository/lib.php @@ -1,5 +1,4 @@ <?php - // $Id$ /////////////////////////////////////////////////////////////////////////// // // @@ -1722,6 +1721,35 @@ function repository_setup_default_plugins() { return true; } +/** + * Loads + * @return void + */ +function repository_head_setup() { + global $PAGE; + + $PAGE->requires->yui_lib('yahoo'); + $PAGE->requires->yui_lib('dom'); + $PAGE->requires->yui_lib('element'); + $PAGE->requires->yui_lib('event'); + $PAGE->requires->yui_lib('json'); + $PAGE->requires->yui_lib('treeview'); + $PAGE->requires->yui_lib('dragdrop'); + $PAGE->requires->yui_lib('container'); + $PAGE->requires->yui_lib('resize'); + $PAGE->requires->yui_lib('layout'); + $PAGE->requires->yui_lib('connection'); + $PAGE->requires->yui_lib('button'); + $PAGE->requires->yui_lib('selector'); + + //TODO: remove the ->in_head() once we refactor the inline script tags in repo code + $PAGE->requires->js('repository/repository.src.js')->in_head(); + + //TODO: remove following after we moe the content of file + // proper place (==themes) + $PAGE->requires->css('repository/repository.css'); +} + /** * Return javascript to create file picker to browse repositories * @global object $CFG @@ -1740,111 +1768,8 @@ function repository_get_client($context, $id = '', $accepted_filetypes = '*', $ $video_file_ext = json_encode($ft->get_file_ext(array('video'))); $accepted_file_ext = json_encode($ft->get_file_ext($accepted_filetypes)); - $css = ''; $js = ''; if (!isset($CFG->filepickerjsloaded)) { - $css .= <<<EOD -<style type="text/css"> -@import "$CFG->httpswwwroot/lib/yui/resize/assets/skins/sam/resize.css"; -@import "$CFG->httpswwwroot/lib/yui/container/assets/skins/sam/container.css"; -@import "$CFG->httpswwwroot/lib/yui/layout/assets/skins/sam/layout.css"; -@import "$CFG->httpswwwroot/lib/yui/button/assets/skins/sam/button.css"; -@import "$CFG->httpswwwroot/lib/yui/assets/skins/sam/treeview.css"; -</style> -<style type="text/css"> -/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */ -/* copy from yui/examples/treeview/assets/css/folders/tree.css */ -/* first or middle sibling, no children */ -.ygtvtn { background: url($CFG->pixpath/y/tn.gif) 0 0 no-repeat; width:17px; height:22px; } -/* first or middle sibling, collapsable */ -.ygtvtm { background: url($CFG->pixpath/y/tm.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer } -/* first or middle sibling, collapsable, hover */ -.ygtvtmh { background: url($CFG->pixpath/y/tmh.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer } -/* first or middle sibling, expandable */ -.ygtvtp { background: url($CFG->pixpath/y/tp.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer } -/* first or middle sibling, expandable, hover */ -.ygtvtph { background: url($CFG->pixpath/y/tph.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer } -/* last sibling, no children */ -.ygtvln { background: url($CFG->pixpath/y/ln.gif) 0 0 no-repeat; width:17px; height:22px; } -/* Last sibling, collapsable */ -.ygtvlm { background: url($CFG->pixpath/y/lm.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer } -/* Last sibling, collapsable, hover */ -.ygtvlmh { background: url($CFG->pixpath/y/lmh.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer } -/* Last sibling, expandable */ -.ygtvlp { background: url($CFG->pixpath/y/lp.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer } -/* Last sibling, expandable, hover */ -.ygtvlph { background: url($CFG->pixpath/y/lph.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer } -/* Loading icon */ -.ygtvloading { background: url($CFG->pixpath/y/loading.gif) 0 0 no-repeat; width:16px; height:22px; } -/* the style for the empty cells that are used for rendering the depth - * of the node */ -.ygtvdepthcell { background: url($CFG->pixpath/y/vline.gif) 0 0 no-repeat; width:17px; height:22px; } -.ygtvblankdepthcell { width:17px; height:22px; } -/* the style of the div around each node */ -.ygtvitem { } -.ygtvitem table{ margin-bottom:0; } -.ygtvitem td { border:none;padding:0; } -/* the style of the div around each node's collection of children */ -.ygtvchildren { } -* html .ygtvchildren { height:1%; } -/* the style of the text label in ygTextNode */ -.ygtvlabel, .ygtvlabel:link, .ygtvlabel:visited, .ygtvlabel:hover { margin-left:2px; text-decoration: none; } - - -.file-picker{font-size:12px;} -.file-picker strong{background:#FFFFCC} -.file-picker a{color: #336699} -.file-picker a:hover{background:#003366;color:white} -.fp-panel{padding:0;margin:0; text-align:left;} -.fp-login-form{text-align:center} -.fp-searchbar{float:right} -.fp-viewbar{width:300px;float:left} -.fp-toolbar{padding: .8em;background: #FFFFCC;text-align:center;margin: 3px} -.fp-toolbar a{padding: 0 .5em} -.fp-list{list-style-type:none;padding:0;float:left;width:100%;margin:0;} -.fp-list li{border-bottom:1px dotted gray;margin-bottom: 1em;} -.fp-repo-name{display:block;padding: .5em;margin-bottom: .5em} -.fp-pathbar{margin: .4em;border-bottom: 1px dotted gray;} -.fp-pathbar a{padding: .4em;} -.fp-rename-form{text-align:center} -.fp-rename-form p{margin: 1em;} -.fp-upload-form{margin: 2em 0;text-align:center} -.fp-upload-btn a{cursor: default;background: white;border:1px solid gray;color:black;padding: .5em} -.fp-upload-btn a:hover {background: grey;color:white} -.fp-paging{margin:1em .5em; clear:both;text-align:center;line-height: 2.5em;} -.fp-paging a{padding: .5em;border: 1px solid #CCC} -.fp-paging a.cur_page{border: 1px solid blue} -.fp-popup{text-align:center} -.fp-grid{float:left;text-align:center;} -.fp-grid div{overflow: hidden} -.fp-grid p{margin:0;padding:0;background: #FFFFCC} -.fp-grid .label{height:48px;text-align:center} -.fp-grid span{color:gray} -</style> - -<!--[if IE 6]> -<style type="text/css"> -/* Fix for IE6 */ -.yui-skin-sam .yui-panel .hd{} -</style> -<![endif]--> -EOD; - $PAGE->requires->yui_lib('yahoo'); - $PAGE->requires->yui_lib('dom'); - $PAGE->requires->yui_lib('element'); - $PAGE->requires->yui_lib('event'); - $PAGE->requires->yui_lib('json'); - $PAGE->requires->yui_lib('treeview'); - $PAGE->requires->yui_lib('dragdrop'); - $PAGE->requires->yui_lib('container'); - $PAGE->requires->yui_lib('resize'); - $PAGE->requires->yui_lib('layout'); - $PAGE->requires->yui_lib('connection'); - $PAGE->requires->yui_lib('button'); - $PAGE->requires->yui_lib('selector'); - require_js(array( - 'repository/repository.src.js' - )); $lang = array(); $lang['title'] = get_string('title', 'repository'); $lang['preview'] = get_string('preview', 'repository'); @@ -1931,5 +1856,5 @@ EOD; $js .= "\r\n"; $js .= "</script>"; - return array('css'=>$css, 'js'=>$js); + return $js; } diff --git a/repository/repository.css b/repository/repository.css new file mode 100644 index 0000000000..71a4c20ef2 --- /dev/null +++ b/repository/repository.css @@ -0,0 +1,73 @@ +/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */ +/* copy from yui/examples/treeview/assets/css/folders/tree.css */ +/* first or middle sibling, no children */ +.ygtvtn { background: url(../pix/y/tn.gif) 0 0 no-repeat; width:17px; height:22px; } +/* first or middle sibling, collapsable */ +.ygtvtm { background: url(../pix/y/tm.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer } +/* first or middle sibling, collapsable, hover */ +.ygtvtmh { background: url(../pix/y/tmh.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer } +/* first or middle sibling, expandable */ +.ygtvtp { background: url(../pix/y/tp.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer } +/* first or middle sibling, expandable, hover */ +.ygtvtph { background: url(../pix/y/tph.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer } +/* last sibling, no children */ +.ygtvln { background: url(../pix/y/ln.gif) 0 0 no-repeat; width:17px; height:22px; } +/* Last sibling, collapsable */ +.ygtvlm { background: url(../pix/y/lm.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer } +/* Last sibling, collapsable, hover */ +.ygtvlmh { background: url(../pix/y/lmh.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer } +/* Last sibling, expandable */ +.ygtvlp { background: url(../pix/y/lp.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer } +/* Last sibling, expandable, hover */ +.ygtvlph { background: url(../pix/y/lph.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer } +/* Loading icon */ +.ygtvloading { background: url(../pix/y/loading.gif) 0 0 no-repeat; width:16px; height:22px; } +/* the style for the empty cells that are used for rendering the depth + * of the node */ +.ygtvdepthcell { background: url(../pix/y/vline.gif) 0 0 no-repeat; width:17px; height:22px; } +.ygtvblankdepthcell { width:17px; height:22px; } +/* the style of the div around each node */ +.ygtvitem { } +.ygtvitem table{ margin-bottom:0; } +.ygtvitem td { border:none;padding:0; } +/* the style of the div around each node's collection of children */ +.ygtvchildren { } +* html .ygtvchildren { height:1%; } +/* the style of the text label in ygTextNode */ +.ygtvlabel, .ygtvlabel:link, .ygtvlabel:visited, .ygtvlabel:hover { margin-left:2px; text-decoration: none; } + + +.file-picker{font-size:12px;} +.file-picker strong{background:#FFFFCC} +.file-picker a{color: #336699} +.file-picker a:hover{background:#003366;color:white} +.fp-panel{padding:0;margin:0; text-align:left;} +.fp-login-form{text-align:center} +.fp-searchbar{float:right} +.fp-viewbar{width:300px;float:left} +.fp-toolbar{padding: .8em;background: #FFFFCC;text-align:center;margin: 3px} +.fp-toolbar a{padding: 0 .5em} +.fp-list{list-style-type:none;padding:0;float:left;width:100%;margin:0;} +.fp-list li{border-bottom:1px dotted gray;margin-bottom: 1em;} +.fp-repo-name{display:block;padding: .5em;margin-bottom: .5em} +.fp-pathbar{margin: .4em;border-bottom: 1px dotted gray;} +.fp-pathbar a{padding: .4em;} +.fp-rename-form{text-align:center} +.fp-rename-form p{margin: 1em;} +.fp-upload-form{margin: 2em 0;text-align:center} +.fp-upload-btn a{cursor: default;background: white;border:1px solid gray;color:black;padding: .5em} +.fp-upload-btn a:hover {background: grey;color:white} +.fp-paging{margin:1em .5em; clear:both;text-align:center;line-height: 2.5em;} +.fp-paging a{padding: .5em;border: 1px solid #CCC} +.fp-paging a.cur_page{border: 1px solid blue} +.fp-popup{text-align:center} +.fp-grid{float:left;text-align:center;} +.fp-grid div{overflow: hidden} +.fp-grid p{margin:0;padding:0;background: #FFFFCC} +.fp-grid .label{height:48px;text-align:center} +.fp-grid span{color:gray} + + +/* TODO: Fix IE6 somehow +.yui-skin-sam .yui-panel .hd{} +*/ \ No newline at end of file diff --git a/repository/repository.src.js b/repository/repository.src.js index d4d664b58e..ea55965f6c 100644 --- a/repository/repository.src.js +++ b/repository/repository.src.js @@ -1,4 +1,3 @@ -// $Id$ /////////////////////////////////////////////////////////////////////////// // // // Don't modify this file unless you know how it works // @@ -9,9 +8,11 @@ * methods you can call it directly without creating an instance. * If you are going to create a file picker, you need create an instance * repo = new repository_client(); - */ +var id2clientid = {}; +var id2itemid = {}; + var repository_listing = {}; var cached_client_id = {}; var file_extensions = {}; -- 2.39.5