added basic support for htmx (ajax)

- if there is HX-Request header present we sent only a part of the whole html
- we return only specific stream defined by [out ...] ezc statement
- the name of the stream is passed in the 'frame' parameter
  (if not present then 'content' is assumed)
- added ezc function: winix_is_htmx_request
This commit is contained in:
Tomasz Sowa 2021-05-27 19:36:04 +02:00
parent 1292a56d1b
commit ba331dea4a
8 changed files with 257 additions and 7 deletions

View File

@ -724,6 +724,25 @@ void App::SaveSessionsIfNeeded()
const std::wstring * App::CreateFrameAnswer()
{
Request & req = *cur.request;
auto i = req.out_streams.streams_map.begin();
const std::wstring * frame = cur.request->ParamValuep(L"frame");
for( ; i != req.out_streams.streams_map.end() ; ++i)
{
if( (frame && i->first == *frame) || (!frame && i->first == L"content") )
{
return &i->second->Str();
break;
}
}
return nullptr;
}
void App::CreateJSONAnswer()
{
Request & req = *cur.request;
@ -1764,11 +1783,12 @@ return header;
void App::SendTextAnswer()
{
const std::wstring * source;
const std::wstring * source = nullptr;
bool compressing = false;
int compress_encoding = 0;
size_t output_size = 0;
Header header = GetHTTPStatusCode();
if( CanSendContent() )
@ -1781,6 +1801,17 @@ size_t output_size = 0;
source = &json_out_stream.Str(); // json_out_stream was prepared by CreateJSONAnswer()
}
else
if( cur.request->headers_in.has_key(L"HX-Request") || cur.request->headers_in.has_key(L"hx_request") ) // fastcgi will change the header to hx_request
{
source = CreateFrameAnswer();
if( !source )
{
empty_response.clear();
source = &empty_response;
}
}
else
{
source = &cur.request->out_main_stream.Str();
}

View File

@ -164,6 +164,7 @@ private:
std::wstring http_header_name;
std::wstring http_header_value;
std::string http_header_8bit;
std::wstring empty_response;
morm::ModelConnector model_connector; // main thread model connector, each thread has its own connector
morm::JSONConnector json_connector;
@ -206,6 +207,7 @@ private:
void SaveSessionsIfNeeded(); // !! IMPROVE ME wywalic do menagera sesji??
void LogAccess();
void SendData(const BinaryPage & page, FCGX_Stream * out);
const std::wstring * CreateFrameAnswer();
void CreateJSONAnswer();
void ReadRequest();

View File

@ -3,13 +3,17 @@
[# blocks are not connected with languages yet, so don't use \{ but {]
if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 )
{
CKEDITOR.tools.enableHtml5Elements( document );
function winix_ckeditor_old_browser_support() {
if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 )
{
CKEDITOR.tools.enableHtml5Elements( document );
}
}
[if not winix_is_htmx_request]
winix_ckeditor_old_browser_support();
[end]
</script>
[end]
@ -22,6 +26,7 @@ if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 )
[block ckeditor]
<script>
function winix_ckeditor_initialize() {
var editorElement = CKEDITOR.document.getById( '[0]' );
editorElement.setAttribute( 'contenteditable', 'true' );
@ -83,6 +88,13 @@ extraAllowedContent : 'aside caption figure figcaption article footer header sec
});
}
[if not winix_is_htmx_request]
winix_ckeditor_initialize();
[end]
</script>

View File

@ -1,2 +1,180 @@
[if winix_has_htmx]
<div id="winix-oob" hx-swap-oob="true">
[# content added by htmx library when using hx-swap-oob]
[if winix_is_htmx_request]
[out "content"]
<script>
function winix_css_download_by_index(css_files, index, callback)
\{
if( index < css_files.length )
\{
var link = document.createElement('link');
link.classList.add("winix-auto-delete");
link.rel = 'stylesheet';
link.type = 'text/css';
link.onload = function() \{
console.log("loading external css: " + css_files\[index\]);
winix_css_download_by_index(css_files, index+1, callback);
\}
link.href = css_files\[index\];
document.head.appendChild(link);
\}
else
\{
console.log("all css files downloaded");
callback();
\}
\}
function winix_css_download(css_files, callback)
\{
// add test if css_files is a table
winix_css_download_by_index(css_files, 0, callback);
\}
function winix_js_download_by_index(js_files, index, callback)
\{
if( index < js_files.length )
\{
var script = document.createElement('script');
script.classList.add("winix-auto-delete");
script.onload = function() \{
console.log("loading external js: " + js_files\[index\]);
winix_js_download_by_index(js_files, index+1, callback);
\}
script.src = js_files\[index\];
document.head.appendChild(script);
\}
else
\{
console.log("all js files downloaded");
callback();
\}
\}
function winix_js_download(js_files, callback)
\{
// add test if js_files is a table
winix_js_download_by_index(js_files, 0, callback);
\}
</script>
[end] [# out "content"]
[if winix_function_is "emacs"]
[out "content"]
<style>
.CodeMirror-matchingtag \{
font-weight: bold;
background: none;
\}
.CodeMirror-activeline-background \{
background: #f3f3f3;
\}
.CodeMirror \{
border: 1px solid #dedede;
\}
</style>
<script>
var winix_code_mirror_editor;
js_files = \[
"[doc_base_url_common]/codemirror/5.59.2/lib/codemirror.js",
"[doc_base_url_common]/codemirror/5.59.2/mode/css/css.js",
"[doc_base_url_common]/codemirror/5.59.2/mode/javascript/javascript.js",
"[doc_base_url_common]/codemirror/5.59.2/mode/xml/xml.js",
"[doc_base_url_common]/codemirror/5.59.2/mode/htmlmixed/htmlmixed.js",
"[doc_base_url_common]/codemirror/5.59.2/addon/display/fullscreen.js",
"[doc_base_url_common]/codemirror/5.59.2/addon/fold/xml-fold.js",
"[doc_base_url_common]/codemirror/5.59.2/addon/edit/matchtags.js",
"[doc_base_url_common]/codemirror/5.59.2/addon/selection/active-line.js",
\];
css_files = \[
"[doc_base_url_common]/codemirror/5.59.2/lib/codemirror.css",
"[doc_base_url_common]/codemirror/5.59.2/addon/display/fullscreen.css",
\];
winix_js_download(js_files, winix_initialize_editor);
winix_css_download(css_files, function()\{\});
function winix_initialize_editor()
\{
var text_area = document.getElementById("winix_content_id");
if( text_area )
\{
winix_code_mirror_editor = CodeMirror.fromTextArea(text_area, \{
mode: "htmlmixed",
theme: "default",
indentUnit: 4,
smartIndent: true,
tabSize: 4,
indentWithTabs: true,
extraKeys: \{
"F11": function(cm) \{ cm.setOption("fullScreen", !cm.getOption("fullScreen")); \},
"Esc": function(cm) \{ if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false); \}
\},
lineNumbers: true,
lineWiseCopyCut: false,
dragDrop: false,
matchTags: true,
styleActiveLine: true
\});
\}
\}
</script>
[end] [# out "content"]
[end] [# if winix_function_is "emacs"]
[if winix_function_is "ckeditor"]
[out "content"]
[# what about winix_has_jquery?]
<script>
js_files = \[
"[doc_base_url_common]/ckeditor_4.9.2/ckeditor.js",
"[doc_base_url_common]/jquery/1.12.4/jquery.min.js",
"[doc_base_url_common]/winix/update_button.js",
\];
winix_js_download(js_files, winix_ckeditor_initialize);
</script>
[end]
[end] [# out "content"]
[end] [# if winix_function_is "emacs"]
[end] [# if winix_is_htmx_request]
</div>
[end] [# if winix_has_htmx]

View File

@ -37,10 +37,27 @@
[if winix_load_htmx]
[def winix_has_htmx true]
<script src="[doc_base_url_common]/htmx.org/1.4.0/dist/htmx.min.js"></script>
[end]
[if winix_has_htmx]
<script>htmx.on("htmx:afterOnLoad", function(evt) \{
[# we can optimize it by searching first the <head> tag]
var winix_elements = htmx.findAll(".winix-auto-delete");
for(var i=0 ; i<winix_elements.length ; ++i)
\{
console.log("removing:");
console.log(winix_elements\[i\]);
htmx.remove(winix_elements\[i\]);
\}
\});
</script>
[end]
[if winix_function_is "emacs"]

View File

@ -766,6 +766,7 @@ void Templates::CreateFunctions()
ezc_functions.Insert("winix_locale_tab", winix_locale_tab);
ezc_functions.Insert("winix_locale_tab_id", winix_locale_tab_id);
ezc_functions.Insert("winix_locale_tab_name", winix_locale_tab_name);
ezc_functions.Insert("winix_is_htmx_request", winix_is_htmx_request);
/*

View File

@ -661,7 +661,7 @@ namespace TemplatesFunctions
void winix_locale_tab(Info & i);
void winix_locale_tab_id(Info & i);
void winix_locale_tab_name(Info & i);
void winix_is_htmx_request(Info & i);
/*
who

View File

@ -375,6 +375,15 @@ void winix_locale_tab_name(Info & i)
}
void winix_is_htmx_request(Info & i)
{
if( cur->request->headers_in.has_key(L"HX-Request") || cur->request->headers_in.has_key(L"hx_request") )
{
// fastcgi will change the header to hx_request
i.res = true;
}
}