Skip to content

Commit

Permalink
Merge 09cf38c into 4efe4f1
Browse files Browse the repository at this point in the history
  • Loading branch information
s-weigand committed Dec 6, 2019
2 parents 4efe4f1 + 09cf38c commit c63038d
Show file tree
Hide file tree
Showing 11 changed files with 453 additions and 5 deletions.
2 changes: 1 addition & 1 deletion cms/static/cms/js/dist/3.7.1/bundle.toolbar.min.js

Large diffs are not rendered by default.

100 changes: 97 additions & 3 deletions cms/static/cms/js/modules/cms.structureboard.js
Expand Up @@ -1270,6 +1270,8 @@ class StructureBoard {
}
var fixedContentMarkup = contentMarkup;
var newDoc = new DOMParser().parseFromString(fixedContentMarkup, 'text/html');
let newScripts = $(newDoc).find('script');
let oldScripts = $(document).find('script');

const structureScrollTop = $('.cms-structure-content').scrollTop();

Expand All @@ -1295,16 +1297,108 @@ class StructureBoard {
toolbar.prependTo(document.body);
CMS.API.Toolbar._refreshMarkup(newToolbar);

$('.cms-structure-content').scrollTop(structureScrollTop);
this.addJsScriptsNeededForRender(newScripts, oldScripts);

$('.cms-structure-content').scrollTop(structureScrollTop);
Plugin._refreshPlugins();

Helpers._getWindow().dispatchEvent(new Event('load'));
$(Helpers._getWindow()).trigger('cms-content-refresh');

this._loadedContent = true;
}

/**
* Checks if new scripts with the class 'cms-execute-js-to-render' exist
* and if they were present before. If they weren't present before - they will be downloaded
* and executed. If the script also has the class 'cms-trigger-load-events' the
* 'load' and 'DOMContentLoaded' events will be triggered
*
* @param {jQuery} newScripts jQuery selector of the scripts for the new body content
* @param {jQuery} oldScripts jQuery selector of the scripts for the old body content
*/
addJsScriptsNeededForRender(newScripts, oldScripts) {
const scriptSrcList = [];
let classListCollection = [];
const that = this;

newScripts.each(function() {
let scriptExists = false;
let newScript = $(this);

if (newScript.hasClass('cms-execute-js-to-render')) {
$(oldScripts).each(function() {
let oldScript = $(this);

if (newScript.prop('outerHTML') === oldScript.prop('outerHTML')) {
scriptExists = true;
return false;
}
});
if (!scriptExists) {
let classList = newScript.attr('class').split(' ');

classListCollection = classListCollection.concat(classList);
if (typeof newScript.prop('src') === 'string' && newScript.prop('src') !== '') {
scriptSrcList.push(newScript.prop('src'));
} else {
let jsFile = document.createElement('script');

jsFile.textContent = newScript.prop('textContent') || '';
jsFile.type = 'text/javascript';
document.body.appendChild(jsFile);
}
}
}
});
if (scriptSrcList.length === 0) {
that.triggerLoadEventsByClass(classListCollection);
} else {
that.getMultiScripts(scriptSrcList).done(function() {
that.triggerLoadEventsByClass(classListCollection);
});
}
}

/**
* Downloads and executes all scripts given by `scriptSrcList` and returns a
* `Promise` when all are done. This is to prevent multiple triggering of
* load events by `triggerLoadEventsByClass` in `addJsScriptsNeededForRender`.
* Original solution from https://stackoverflow.com/a/11803418
*
* @param {String[]} scriptSrcList Array of script sources
*
* @returns {Promise}
*/
getMultiScripts(scriptSrcList) {
let _arr = $.map(scriptSrcList, function(scr) {
return $.getScript(scr);
});

// eslint-disable-next-line new-cap
_arr.push($.Deferred(function(deferred) {
$(deferred.resolve);
}));

return $.when.apply($, _arr);
}

/**
* Triggers events if specific classes were in any of the scripts added by
* the method addJsScriptsNeededForRender
*
* @param {String[]} classListCollection array of all classes the script tags had
*/
triggerLoadEventsByClass(classListCollection) {
if (classListCollection.indexOf('cms-trigger-event-document-DOMContentLoaded') > -1) {
Helpers._getWindow().document.dispatchEvent(new Event('DOMContentLoaded'));
}
if (classListCollection.indexOf('cms-trigger-event-window-DOMContentLoaded') > -1) {
Helpers._getWindow().dispatchEvent(new Event('DOMContentLoaded'));
}
if (classListCollection.indexOf('cms-trigger-event-window-load') > -1) {
Helpers._getWindow().dispatchEvent(new Event('load'));
}
}

handleAddPlugin(data) {
if (data.plugin_parent) {
$(`.cms-draggable-${data.plugin_parent}`).replaceWith(data.structure.html);
Expand Down
Empty file.
@@ -0,0 +1,84 @@
# -*- coding: utf-8 -*-
import itertools

from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool

from .models import DummyModel


@plugin_pool.register_plugin
class DynamicJsLoadingPlugin(CMSPluginBase):
model = DummyModel
name = "DynamicJsLoading"
render_template = "dynamic_js_loading/dynamic_js_loading.html"

def render(self, context, instance, placeholder):
"""
This generates a list of all 16 class usage permutations.
originaly from https://stackoverflow.com/a/54059999
>>> import itertools
>>> l=[False,True]
>>> list(itertools.product(l,repeat=4))
[(False, False, False, False),
(False, False, False, True),
(False, False, True, False),
(False, False, True, True),
(False, True, False, False),
(False, True, False, True),
(False, True, True, False),
(False, True, True, True),
(True, False, False, False),
(True, False, False, True),
(True, False, True, False),
(True, False, True, True),
(True, True, False, False),
(True, True, False, True),
(True, True, True, False),
(True, True, True, True)]
"""
binary_list = [False, True]
case_list = list(itertools.product(binary_list, repeat=4))
context["js_test_classes"] = self.generate_class_string(
*case_list[instance.testcase - 1]
)

return super(DynamicJsLoadingPlugin, self).render(
context, instance, placeholder
)

def generate_class_string(
self, execute_js, document_content, window_content, window_load
):
"""
Generates the class string for the different test cases based on the truth values of the parameters
Parameters
----------
execute_js : bool
Whether or not to use the class "cms-execute-js-to-render"
document_content: bool
Whether or not to use the class "cms-trigger-event-document-DOMContentLoaded"
window_content: bool
Whether or not to use the class "cms-trigger-event-window-DOMContentLoaded"
window_load: bool
Whether or not to use the class "cms-trigger-event-window-DOMContentLoaded"
"""
full_class_list = [
"cms-execute-js-to-render",
"cms-trigger-event-document-DOMContentLoaded",
"cms-trigger-event-window-DOMContentLoaded",
"cms-trigger-event-window-load",
]
include_list = [execute_js, document_content, window_content, window_load]
class_list = []
for include_bool, class_string in zip(include_list, full_class_list):
if include_bool:
class_list.append(class_string)
return " ".join(class_list)

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models
from django.utils.encoding import python_2_unicode_compatible

from cms.models import CMSPlugin


@python_2_unicode_compatible
class DummyModel(CMSPlugin):
testcase = models.IntegerField(
verbose_name='Testcase',
)

def __str__(self):
return str(self.testcase) or str(self.pk)
@@ -0,0 +1,22 @@
function append_h2_element_from_src (id) {
var h2_elem = document.createElement('h2');

h2_elem.id = id;
h2_elem.textContent = 'id=' + id;
document.body.appendChild(h2_elem);
}

append_h2_element_from_src('from_src_no_trigger');

document.addEventListener('DOMContentLoaded', function() {
append_h2_element_from_src('from_src_needs_trigger_document_DOMContentLoaded');
});

window.addEventListener('DOMContentLoaded', function() {
append_h2_element_from_src('from_src_needs_trigger_window_DOMContentLoaded');
});

window.addEventListener('load', function() {
append_h2_element_from_src('from_src_needs_trigger_window_load');
});
console.log("SCRIPT LOADED")
@@ -0,0 +1,33 @@
{% load sekizai_tags static %}
<h1 id="dynamic_js_loading_heading">Test dynamic loading of testcase {{instance.testcase}}</h1>


{% addtoblock "js" %}
<script class="{{js_test_classes}}">
function append_h2_element_inline(id) {
var h2_elem = document.createElement('h2');

h2_elem.id = id;
h2_elem.textContent = 'id=' + id;
document.body.appendChild(h2_elem);
}

append_h2_element_inline('inline_no_trigger');

document.addEventListener('DOMContentLoaded', function() {
append_h2_element_inline('inline_needs_trigger_document_DOMContentLoaded');
});

window.addEventListener('DOMContentLoaded', function() {
append_h2_element_inline('inline_needs_trigger_window_DOMContentLoaded');
});

window.addEventListener('load', function() {
append_h2_element_inline('inline_needs_trigger_window_load');
});
</script>
{% endaddtoblock %}

{% addtoblock "js" %}
<script src="{% static '/dynamic_js_loading/js/test_script.js' %}" class="{{js_test_classes}}"></script>
{% endaddtoblock %}

0 comments on commit c63038d

Please sign in to comment.