diff --git a/classes/display/DisplayHandler.class.php b/classes/display/DisplayHandler.class.php index 1edb959f2..4c3b3b5e5 100644 --- a/classes/display/DisplayHandler.class.php +++ b/classes/display/DisplayHandler.class.php @@ -176,16 +176,34 @@ class DisplayHandler extends Handler { case 'panel': $data = Rhymix\Framework\Debug::getDebugData(); + if ($data->entries) + { + foreach ($data->entries as &$entry) + { + if (is_scalar($entry->message)) + { + $entry->message = var_export($entry->message, true); + } + else + { + $entry->message = trim(print_r($entry->message, true)); + } + } + } switch (Context::getResponseMethod()) { case 'HTML': $json_options = defined('JSON_PRETTY_PRINT') ? (JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) : 0; - $panel_script = ""; + $panel_script = sprintf('', RX_BASEURL, 'common/js/debug.js', filemtime(RX_BASEDIR . 'common/js/debug.js')); + $panel_script .= "\n"; $body_end_position = strrpos($output, '') ?: strlen($output); $output = substr($output, 0, $body_end_position) . "\n$panel_script\n" . substr($output, $body_end_position); return; case 'JSON': - $output = preg_replace('/\}$/', ',"_rx_debug":' . json_encode($data) . '}', $output); + if (preg_match('/^(.+)\}$/', $output, $matches)) + { + $output = $matches[1] . ',"_rx_debug":' . json_encode($data) . '}'; + } return; default: return; diff --git a/common/css/xe.css b/common/css/xe.css index 2bdbb9740..1347d659c 100644 --- a/common/css/xe.css +++ b/common/css/xe.css @@ -290,3 +290,122 @@ button.btn { .btn-group>.btn.active { z-index: 2; } + +/* Debug */ +#rhymix_debug_button { + position: fixed; + left: 0; bottom: 40px; + background: #eeeeee; + border: 1px solid #ccc; border-left: 0; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + box-shadow: 0px 0px 3px 0px rgba(0, 0, 0, 0.18), 0px 0px 6px 0px rgba(0, 0, 0, 0.12); + z-index: 1073741824; +} +#rhymix_debug_button:hover { + background: #444444; +} +#rhymix_debug_button a { + display: block; + font: bold 12px/14px Arial, sans-serif; + color: #444; + text-decoration: none; + padding: 4px 8px; +} +#rhymix_debug_button a:hover { + color: #eeeeee; +} +#rhymix_debug_panel { + display: none; + position: absolute; + left: 0; top: 0; + max-width: 96%; + min-height: 100%; + background: #fcfcfc; + border-right: 1px solid #ccc; + box-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.18), 0px 0px 8px 0px rgba(0, 0, 0, 0.12); + z-index: 1073741824; +} +#rhymix_debug_panel .debug_header { + clear: both; + width: 100%; + height: 36px; + background: #444444; + background: linear-gradient(to right, #222222 0%, #444444 40%, #eeeeee 100%); + position: relative; +} +#rhymix_debug_panel .debug_header h2 { + font: bold 16px/20px Arial, sans-serif; + color: #fcfcfc; + position: absolute; + left: 10px; top: 10px; +} +#rhymix_debug_panel .debug_header .debug_maximize { + font: normal 20px/24px Arial, sans-serif; + color: #444444; + position: absolute; + right: 32px; top: 7px; +} +#rhymix_debug_panel .debug_header .debug_close { + font: normal 28px/28px Arial, sans-serif; + color: #444444; + position: absolute; + right: 10px; top: 4px; +} +#rhymix_debug_panel .debug_header .debug_close:hover { + color: #f44336; +} +#rhymix_debug_panel .debug_page { + clear: both; + margin: 12px 10px; + font: normal 12px/16px Arial, NanumBarunGothic, NanumGothic, "Malgun Gothic", sans-serif; +} +#rhymix_debug_panel .debug_page .debug_page_header { + padding-bottom: 8px; + border-bottom: 1px solid #ddd; + position: relative; +} +#rhymix_debug_panel .debug_page .debug_page_header h3 { + color: #444; + font: inherit; + font-size: 14px; + font-weight: bold; + margin: 0; + padding: 0; +} +#rhymix_debug_panel .debug_page .debug_page_collapse { + display: block; + position: absolute; + right: 0; top: 0; + color: #999; + font-size: 10px; + line-height: 12px; + text-decoration: none; + padding: 2px 2px; +} +#rhymix_debug_panel .debug_page .debug_page_body { + margin: 8px 4px 8px 10px; +} +#rhymix_debug_panel .debug_page .debug_page_body h4 { + color: #444; + font: inherit; + font-size: 13px; + font-weight: bold; + margin: 0 0 8px 0; + padding: 0; +} +#rhymix_debug_panel .debug_page .debug_entry { + font-family: Consolas, "Courier New", monospace; + color: #666; + margin-left: 38px; + margin-bottom: 8px; + text-indent: -28px; + white-space: pre-wrap; +} +#rhymix_debug_panel .debug_page .debug_entry.no_indentation { + margin-left: 10px; + text-indent: 0; +} +#rhymix_debug_panel .debug_page .debug_entry.collapse_spaces { + white-space: pre-line; +} diff --git a/common/framework/debug.php b/common/framework/debug.php index f71670c07..071377ff7 100644 --- a/common/framework/debug.php +++ b/common/framework/debug.php @@ -379,6 +379,10 @@ class Debug // Clean up the backtrace. foreach (array('entries', 'errors', 'queries') as $key) { + if (!$data->$key) + { + continue; + } foreach ($data->$key as &$entry) { if (isset($entry->file)) diff --git a/common/js/common.js b/common/js/common.js index 29164bc60..ab14e21c7 100644 --- a/common/js/common.js +++ b/common/js/common.js @@ -28,6 +28,9 @@ } }); + /* Array for pending debug data */ + window.rhymix_debug_pending_data = []; + /** * @brief XE 공용 유틸리티 함수 * @namespace XE diff --git a/common/js/debug.js b/common/js/debug.js new file mode 100644 index 000000000..ce192eaf5 --- /dev/null +++ b/common/js/debug.js @@ -0,0 +1,119 @@ + +/** + * Client-side script for manipulating the debug panel on Rhymix. + * + * @file debug.js + * @author Kijin Sung + */ +jQuery(function() { + + // Find debug panel elements. + var panel = $("#rhymix_debug_panel"); + var button = $("#rhymix_debug_button"); + + // Initialize the debug button. + $('').text("DEBUG").appendTo(button).click(function(event) { + event.preventDefault(); + panel.css({ width: 0 }).show().animate({ width: 640 }, 200); + button.hide(); + }); + + // Initialize the debug panel. + var header = $('
').appendTo(panel); + header.append('

RHYMIX DEBUG

'); + header.append($('+').click(function(event) { + panel.animate({ width: "95%" }, 300); + })); + header.append($('×').click(function(event) { + event.preventDefault(); + panel.animate({ width: 0 }, 200, function() { + panel.hide(); + button.show(); + }); + })); + + // Define a function for adding debug data to the panel. + window.rhymix_debug_add_data = function(data) { + + // Create the page. + var page = $('
').appendTo(panel); + var page_body = $('
').appendTo(page); + + // Create the page header. + var page_header = $('
').prependTo(page); + page_header.append($('

').text(data.page_title).attr("title", data.url)); + page_header.append($('').text("▲").click(function(event) { + event.preventDefault(); + if (page_body.is(":visible")) { + page_body.slideUp(200); + $(this).text("▼"); + } else { + page_body.slideDown(200); + $(this).text("▲"); + } + })); + + // Add general information. + page_body.append($('

').text('General Information')); + page_body.append($('
').text( + 'Request: ' + data.request.method + ' (' + data.request.size + ' bytes)' + "\n" + + 'Response: ' + data.response.method + ' (' + data.response.size + ' bytes)' + "\n" + + 'Time: ' + data.timing.total)); + + // Add debug entries. + if (data.entries && data.entries.length) { + page_body.append($('

').text('Debug Entries (' + data.entries.length + ')')); + for (var i in data.entries) { + var entry = $('
').appendTo(page_body); + var num = parseInt(i) + 1; if (num < 10) num = "0" + num; + var backtrace = ""; + for (var j in data.entries[i].backtrace) { + backtrace += "\n- " + data.entries[i].backtrace[j].file + ":" + data.entries[i].backtrace[j].line; + } + entry.text(num + ". " + data.entries[i].message + backtrace); + } + } + + // Add errors. + if (data.errors && data.errors.length) { + page_body.append($('

').text('Errors (' + data.errors.length + ')')); + for (var i in data.errors) { + var entry = $('
').appendTo(page_body); + var num = parseInt(i) + 1; if (num < 10) num = "0" + num; + var backtrace = ""; + for (var j in data.errors[i].backtrace) { + backtrace += "\n- " + data.errors[i].backtrace[j].file + ":" + data.errors[i].backtrace[j].line; + } + entry.text(num + ". " + data.errors[i].type + ": " + data.errors[i].message + backtrace); + } + } + + // Add queries. + if (data.queries && data.queries.length) { + page_body.append($('

').text('Queries (' + data.queries.length + ')')); + for (var i in data.queries) { + var entry = $('
').appendTo(page_body); + var num = parseInt(i) + 1; if (num < 10) num = "0" + num; + var description = ""; + description += "\nCaller: " + data.queries[i].file + ":" + data.queries[i].line + " (" + data.queries[i].method + ")"; + description += "\nConnection: " + data.queries[i].query_connection; + description += "\nQuery Time: " + data.queries[i].query_time.toFixed(4) + " sec"; + description += "\nResult: " + ((data.queries[i].message === "success") ? "success" : ("error " + data.queries[i].error_code + " " + data.queries[i].message)); + entry.text(num + ". " + data.queries[i].query_string + description); + } + } + }; + + // Add debug data from the current page. + if (rhymix_debug_content) { + rhymix_debug_content.page_title = 'MAIN PAGE'; + rhymix_debug_add_data(rhymix_debug_content); + } + + // Add debug data from pending AJAX requests. + if (rhymix_debug_pending_data) { + while (rhymix_debug_pending_data.length) { + rhymix_debug_add_data(rhymix_debug_pending_data.shift()); + } + } +}); diff --git a/common/js/xml_handler.js b/common/js/xml_handler.js index 128916ed2..5e2ee7f8e 100644 --- a/common/js/xml_handler.js +++ b/common/js/xml_handler.js @@ -77,6 +77,16 @@ } }); + // Add debug information. + if (data._rx_debug) { + data._rx_debug.page_title = "AJAX : " + params.module + "." + params.act; + if (window.rhymix_debug_add_data) { + window.rhymix_debug_add_data(data._rx_debug); + } else { + window.rhymix_debug_pending_data.push(data._rx_debug); + } + } + // If the response contains an error, display the error message. if (data.error != "0") { // This way of calling an error handler is deprecated. Do not use it. @@ -176,6 +186,16 @@ clearTimeout(wfsr_timeout); waiting_obj.hide().trigger("cancel_confirm"); + // Add debug information. + if (data._rx_debug) { + data._rx_debug.page_title = "AJAX : " + params.module + "." + params.act; + if (window.rhymix_debug_add_data) { + window.rhymix_debug_add_data(data._rx_debug); + } else { + window.rhymix_debug_pending_data.push(data._rx_debug); + } + } + // If the response contains an error, display the error message. if(data.error != "0" && data.error > -1000) { if(data.error == -1 && data.message == "msg_is_not_administrator") { diff --git a/common/tpl/common_layout.html b/common/tpl/common_layout.html index 344d14d55..c68945e8f 100644 --- a/common/tpl/common_layout.html +++ b/common/tpl/common_layout.html @@ -73,6 +73,7 @@ xe.msg_select_menu = "{$lang->msg_select_menu}";
+
{@ $js_body_files = Context::getJsFile('body') } diff --git a/common/tpl/mobile_layout.html b/common/tpl/mobile_layout.html index a23d378ef..f957cc7ef 100644 --- a/common/tpl/mobile_layout.html +++ b/common/tpl/mobile_layout.html @@ -61,6 +61,7 @@ var default_url = "{Context::getDefaultUrl()}"; {$content} {Context::getHtmlFooter()}
+
{@ $js_body_files = Context::getJsFile('body') }