MDL-51222 Javascript: Trigger events for filters on DOM insertion
authorDamyon Wiese <damyon@moodle.com>
Thu, 27 Aug 2015 09:15:17 +0000 (17:15 +0800)
committerDamyon Wiese <damyon@moodle.com>
Tue, 22 Sep 2015 07:40:51 +0000 (15:40 +0800)
When nodes are added to the dom, they may need to be re-processed by a JS based
filter. To do this we need to trigger the legacy YUI event filter-content-updated.

To make this easier I added some wrappers to template that will insert the node, run any
JS and trigger the event.

I also changed existing yui code to call the amd function to trigger the event. This way
all jquery and yui listeners will always be notified.

17 files changed:
.jshintrc
admin/tool/templatelibrary/amd/build/display.min.js
admin/tool/templatelibrary/amd/build/search.min.js
admin/tool/templatelibrary/amd/src/display.js
admin/tool/templatelibrary/amd/src/search.js
filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-debug.js
filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-min.js
filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker.js
filter/glossary/yui/src/autolinker/js/autolinker.js
lib/amd/build/event.min.js [new file with mode: 0644]
lib/amd/build/templates.min.js
lib/amd/src/event.js [new file with mode: 0644]
lib/amd/src/templates.js
lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js
lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js
lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js
lib/editor/atto/plugins/equation/yui/src/button/js/button.js

index 8b8a806..ee94a05 100644 (file)
--- a/.jshintrc
+++ b/.jshintrc
@@ -35,7 +35,8 @@
     "plusplus":     false,
     "predef": [
         "M",
-        "define"
+        "define",
+        "require"
     ],
     "proto":        false,
     "regexdash":    false,
index 49c0afe..55ae25d 100644 (file)
Binary files a/admin/tool/templatelibrary/amd/build/display.min.js and b/admin/tool/templatelibrary/amd/build/display.min.js differ
index 869d27b..b8458ae 100644 (file)
Binary files a/admin/tool/templatelibrary/amd/build/search.min.js and b/admin/tool/templatelibrary/amd/build/search.min.js differ
index bb2f77a..05507ee 100644 (file)
@@ -99,9 +99,7 @@ define(['jquery', 'core/ajax', 'core/log', 'core/notification', 'core/templates'
         }
         if (context) {
             templates.render(templateName, context).done(function(html, js) {
-                $('[data-region="displaytemplateexample"]').empty();
-                $('[data-region="displaytemplateexample"]').append(html);
-                templates.runTemplateJS(js);
+                templates.replaceNodeContents($('[data-region="displaytemplateexample"]'), html, js);
             }).fail(notification.exception);
         } else {
             str.get_string('templatehasnoexample', 'tool_templatelibrary').done(function(s) {
index efefb26..2c9d828 100644 (file)
@@ -32,8 +32,8 @@ define(['jquery', 'core/ajax', 'core/log', 'core/notification', 'core/templates'
      */
     var reloadListTemplate = function(templateList) {
         templates.render('tool_templatelibrary/search_results', { templates: templateList })
-            .done(function (result) {
-                $('[data-region="searchresults"]').replaceWith(result);
+            .done(function (result, js) {
+                templates.replaceNode($('[data-region="searchresults"]'), result, js);
             }).fail(notification.exception);
     };
 
index b932ef7..ecffc3c 100644 (file)
Binary files a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-debug.js and b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-debug.js differ
index 34d316d..46b748d 100644 (file)
Binary files a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-min.js and b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-min.js differ
index b932ef7..ecffc3c 100644 (file)
Binary files a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker.js and b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker.js differ
index 564f348..3689b4a 100644 (file)
@@ -20,46 +20,53 @@ Y.extend(AUTOLINKER, Y.Base, {
     alertpanels: {},
     initializer : function() {
         var self = this;
-        Y.delegate('click', function(e){
-            e.preventDefault();
+        require(['core/event'], function(event) {
+            Y.delegate('click', function(e){
+                e.preventDefault();
 
-            //display a progress indicator
-            var title = '',
-                content = Y.Node.create('<div id="glossaryfilteroverlayprogress">' +
-                                        '<img src="' + M.cfg.loadingicon + '" class="spinner" />' +
-                                        '</div>'),
-                o = new Y.Overlay({
-                    headerContent :  title,
-                    bodyContent : content
-                }),
-                fullurl,
-                cfg;
-            self.overlay = o;
-            o.render(Y.one(document.body));
+                //display a progress indicator
+                var title = '',
+                    content = Y.Node.create('<div id="glossaryfilteroverlayprogress">' +
+                                            '<img src="' + M.cfg.loadingicon + '" class="spinner" />' +
+                                            '</div>'),
+                    o = new Y.Overlay({
+                        headerContent :  title,
+                        bodyContent : content
+                    }),
+                    fullurl,
+                    cfg;
+                self.overlay = o;
+                o.render(Y.one(document.body));
 
-            //Switch over to the ajax url and fetch the glossary item
-            fullurl = this.getAttribute('href').replace('showentry.php','showentry_ajax.php');
-            cfg = {
-                method: 'get',
-                context : self,
-                on: {
-                    success: function(id, o) {
-                        this.display_callback(o.responseText);
-                    },
-                    failure: function(id, o) {
-                        var debuginfo = o.statusText;
-                        if (M.cfg.developerdebug) {
-                            o.statusText += ' (' + fullurl + ')';
+                //Switch over to the ajax url and fetch the glossary item
+                fullurl = this.getAttribute('href').replace('showentry.php','showentry_ajax.php');
+                cfg = {
+                    method: 'get',
+                    context : self,
+                    on: {
+                        success: function(id, o) {
+                            this.display_callback(o.responseText, event);
+                        },
+                        failure: function(id, o) {
+                            var debuginfo = o.statusText;
+                            if (M.cfg.developerdebug) {
+                                o.statusText += ' (' + fullurl + ')';
+                            }
+                            new M.core.exception({ message: debuginfo });
                         }
-                        this.display_callback('bodyContent',debuginfo);
                     }
-                }
-            };
-            Y.io(fullurl, cfg);
+                };
+                Y.io(fullurl, cfg);
 
-        }, Y.one(document.body), 'a.glossary.autolink.concept');
+            }, Y.one(document.body), 'a.glossary.autolink.concept');
+        });
     },
-    display_callback : function(content) {
+    /**
+     * @method display_callback
+     * @param {String} content - Content to display
+     * @param {Object} event The amd event module used to fire events for jquery and yui.
+     */
+    display_callback : function(content, event) {
         var data,
             key,
             alertpanel,
@@ -76,7 +83,8 @@ Y.extend(AUTOLINKER, Y.Base, {
                     definition = data.entries[key].definition + data.entries[key].attachments;
                     alertpanel = new M.core.alert({title:data.entries[key].concept, draggable: true,
                         message:definition, modal:false, yesLabel: M.util.get_string('ok', 'moodle')});
-                    Y.fire(M.core.event.FILTER_CONTENT_UPDATED, {nodes: (new Y.NodeList(alertpanel.get('boundingBox')))});
+                    // Notify the filters about the modified nodes.
+                    event.notifyFilterContentUpdated(alertpanel.get('boundingBox').getDOMNode());
                     Y.Node.one('#id_yuialertconfirm-' + alertpanel.get('COUNT')).focus();
 
                     // Register alertpanel for stacking.
diff --git a/lib/amd/build/event.min.js b/lib/amd/build/event.min.js
new file mode 100644 (file)
index 0000000..86a6407
Binary files /dev/null and b/lib/amd/build/event.min.js differ
index c75432b..71f74a7 100644 (file)
Binary files a/lib/amd/build/templates.min.js and b/lib/amd/build/templates.min.js differ
diff --git a/lib/amd/src/event.js b/lib/amd/src/event.js
new file mode 100644 (file)
index 0000000..ce20865
--- /dev/null
@@ -0,0 +1,52 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Global registry of core events that can be triggered/listened for.
+ *
+ * @module     core/event
+ * @package    core
+ * @class      event
+ * @copyright  2015 Damyon Wiese <damyon@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      3.0
+ */
+define([ 'jquery', 'core/yui' ],
+       function($, Y) {
+
+    return /** @alias module:core/event */ {
+        // Public variables and functions.
+        /**
+         * Trigger an event using both JQuery and YUI
+         *
+         * @method notifyFilterContentUpdated
+         * @param {string}|{JQuery} nodes - Selector or list of elements that were inserted.
+         */
+        notifyFilterContentUpdated: function(nodes) {
+            nodes = $(nodes);
+            Y.use('event', 'moodle-core-event', function(Y) {
+                // Trigger it the JQuery way.
+                $('document').trigger(M.core.event.FILTER_CONTENT_UPDATED, nodes);
+
+                // Create a YUI NodeList from our JQuery Object.
+                var yuiNodes = new Y.NodeList(nodes.get());
+
+                // And again for YUI.
+                Y.fire(M.core.event.FILTER_CONTENT_UPDATED, { nodes: yuiNodes });
+            });
+        },
+
+    };
+});
index a152e03..6e4bc0c 100644 (file)
@@ -30,9 +30,10 @@ define([ 'core/mustache',
          'core/notification',
          'core/url',
          'core/config',
-         'core/localstorage'
+         'core/localstorage',
+         'core/event'
        ],
-       function(mustache, $, ajax, str, notification, coreurl, config, storage) {
+       function(mustache, $, ajax, str, notification, coreurl, config, storage, event) {
 
     // Private variables and functions.
 
@@ -326,6 +327,50 @@ define([ 'core/mustache',
         return deferred.promise();
     };
 
+    /**
+     * Execute a block of JS returned from a template.
+     * Call this AFTER adding the template HTML into the DOM so the nodes can be found.
+     *
+     * @method runTemplateJS
+     * @param {string} source - A block of javascript.
+     */
+    var runTemplateJS = function(source) {
+        if (source.trim() !== '') {
+            var newscript = $('<script>').attr('type','text/javascript').html(source);
+            $('head').append(newscript);
+        }
+    };
+
+    /**
+     * Do some DOM replacement and trigger correct events and fire javascript.
+     *
+     * @method domReplace
+     * @private
+     * @param {JQuery} element - Element or selector to replace.
+     * @param {String} newHTML - HTML to insert / replace.
+     * @param {String} newJS - Javascript to run after the insertion.
+     * @param {Boolean} replaceChildNodes - Replace only the childnodes, alternative is to replace the entire node.
+     */
+    var domReplace = function(element, newHTML, newJS, replaceChildNodes) {
+        var replaceNode = $(element);
+        if (replaceNode.length) {
+            // First create the dom nodes so we have a reference to them.
+            var newNodes = $(newHTML);
+            // Do the replacement in the page.
+            if (replaceChildNodes) {
+                replaceNode.empty();
+                replaceNode.append(newNodes);
+            } else {
+                replaceNode.replaceWith(newNodes);
+            }
+            // Run any javascript associated with the new HTML.
+            runTemplateJS(newJS);
+            // Notify all filters about the new content.
+            event.notifyFilterContentUpdated(newNodes);
+        }
+    };
+
+
     return /** @alias module:core/templates */ {
         // Public variables and functions.
         /**
@@ -379,12 +424,28 @@ define([ 'core/mustache',
          * Call this AFTER adding the template HTML into the DOM so the nodes can be found.
          *
          * @method runTemplateJS
-         * @private
          * @param {string} source - A block of javascript.
          */
-        runTemplateJS: function(source) {
-            var newscript = $('<script>').attr('type','text/javascript').html(source);
-            $('head').append(newscript);
+        runTemplateJS: runTemplateJS,
+
+        /**
+         * Replace a node in the page with some new HTML and run the JS.
+         *
+         * @method replaceNodeContents
+         * @param {string} source - A block of javascript.
+         */
+        replaceNodeContents: function(element, newHTML, newJS) {
+            return domReplace(element, newHTML, newJS, true);
+        },
+
+        /**
+         * Insert a node in the page with some new HTML and run the JS.
+         *
+         * @method replaceNode
+         * @param {string} source - A block of javascript.
+         */
+        replaceNode: function(element, newHTML, newJS) {
+            return domReplace(element, newHTML, newJS, false);
         }
     };
 });
index 628f2ce..270e0eb 100644 (file)
Binary files a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js and b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js differ
index 05522fd..5740db9 100644 (file)
Binary files a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js and b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js differ
index 0350ece..ade5d5f 100644 (file)
Binary files a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js and b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js differ
index 864f421..2b30130 100644 (file)
@@ -30,7 +30,6 @@
  * @class Button
  * @extends M.editor_atto.EditorPlugin
  */
-
 var COMPONENTNAME = 'atto_equation',
     LOGNAME = 'atto_equation',
     CSS = {
@@ -229,8 +228,10 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
 
         tabview.render();
         dialogue.show();
-        // Trigger any JS filters to reprocess the new nodes.
-        Y.fire(M.core.event.FILTER_CONTENT_UPDATED, {nodes: (new Y.NodeList(dialogue.get('boundingBox')))});
+        // Notify the filters about the modified nodes.
+        require(['core/event'], function(event) {
+            event.notifyFilterContentUpdated(dialogue.get('boundingBox').getDOMNode());
+        });
 
         if (equation) {
             content.one(SELECTORS.EQUATION_TEXT).set('text', equation);
@@ -494,7 +495,10 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
         if (preview.status === 200) {
             previewNode.setHTML(preview.responseText);
 
-            Y.fire(M.core.event.FILTER_CONTENT_UPDATED, {nodes: (new Y.NodeList(previewNode))});
+            // Notify the filters about the modified nodes.
+            require(['core/event'], function(event) {
+                event.notifyFilterContentUpdated(previewNode.getDOMNode());
+            });
         }
     },