From 7c8c8a8f49e469471b9f27f0bdecf6787eedbb84 Mon Sep 17 00:00:00 2001 From: Patrick Steele-Idem Date: Tue, 4 Nov 2014 11:05:48 -0700 Subject: [PATCH] Added support for immediate widget initialization (versus waiting for DOM ready event) Also, better support for initializing widgets in async fragments. --- lib/WidgetsContext.js | 16 ++++++------- lib/init-widgets-browser.js | 44 ++++++++++++++++++++++++------------ lib/marko-widgets-browser.js | 6 ++++- lib/marko-widgets.js | 24 ++++++++++++-------- lib/uniqueId.js | 10 ++++---- marko-taglib.json | 6 +++-- taglib/init-widgets-tag.js | 34 ++++++++++++++++++---------- taglib/widget-tag.js | 10 ++++---- 8 files changed, 94 insertions(+), 56 deletions(-) diff --git a/lib/WidgetsContext.js b/lib/WidgetsContext.js index 03ce9e4bc..774c08ebb 100644 --- a/lib/WidgetsContext.js +++ b/lib/WidgetsContext.js @@ -6,9 +6,9 @@ var initWidgets = require('./init-widgets'); var EventEmitter = require('events').EventEmitter; var inherit = require('raptor-util/inherit'); -function WidgetsContext(context) { +function WidgetsContext(out) { EventEmitter.call(this); - this.context = context; + this.out = out; this.widgets = []; this.widgetStack = []; } @@ -54,7 +54,7 @@ WidgetsContext.prototype = { _this.widgets.push(widgetDef); } widgetStack.push(widgetDef); - + this.emit('beginWidget', widgetDef); return widgetDef; @@ -67,7 +67,7 @@ WidgetsContext.prototype = { this.widgetStack = []; }, _nextWidgetId: function () { - return 'w' + uniqueId(this.context); + return 'w' + uniqueId(this.out); }, initWidgets: function () { var widgetDefs = this.widgets; @@ -81,11 +81,11 @@ WidgetsContext.prototype = { inherit(WidgetsContext, EventEmitter); -WidgetsContext.getWidgetsContext = function (context) { - var attributes = context.attributes; +WidgetsContext.getWidgetsContext = function (out) { + var global = out.global; - return attributes[WIDGET_CONTEXT_KEY] || - (attributes[WIDGET_CONTEXT_KEY] = new WidgetsContext(context)); + return global[WIDGET_CONTEXT_KEY] || + (global[WIDGET_CONTEXT_KEY] = new WidgetsContext(out)); }; diff --git a/lib/init-widgets-browser.js b/lib/init-widgets-browser.js index 3adf7b420..3cb0d3607 100644 --- a/lib/init-widgets-browser.js +++ b/lib/init-widgets-browser.js @@ -304,21 +304,29 @@ exports.initClientRendered = function(widgetDefs) { * @param {boolean} If the */ exports.initServerRendered = function(scanDOM) { - ready(function() { - var idsEl = document.getElementById('rwidgets'); - if (!idsEl && !scanDOM) { // If there is no index and "scan DOM" is not set to true then do nothing - return; + function doInit() { + var dataIds; + + if (typeof scanDOM === 'string') { + dataIds = scanDOM; + scanDOM = false; + } else { + var idsEl = document.getElementById('rwidgets'); + if (!idsEl && !scanDOM) { // If there is no index and "scan DOM" is not set to true then do nothing + return; + } + + // Make sure widgets are only initialized once by checking a flag + if (document.rwidgetsInitialized === true) { + return; + } + + // Set flag to avoid trying to do this multiple times + document.rwidgetsInitialized = true; + + dataIds = idsEl ? idsEl.getAttribute('data-ids') : null; } - // Make sure widgets are only initialized once by checking a flag - if (document.rwidgetsInitialized === true) { - return; - } - - // Set flag to avoid trying to do this multiple times - document.rwidgetsInitialized = true; - - var dataIds = idsEl ? idsEl.getAttribute('data-ids') : null; var initContext = new InitContext(); if (dataIds == null || dataIds === '*') { // If the data-ids attribute is * then server is tell us we need to scan the DOM initAllWidgetsInDOM(initContext); @@ -336,5 +344,13 @@ exports.initServerRendered = function(scanDOM) { initWidgetFromEl(el, initContext); } } - }); + } + + if (typeof scanDOM === 'string') { + doInit(); + } else { + ready(doInit); + } + + }; \ No newline at end of file diff --git a/lib/marko-widgets-browser.js b/lib/marko-widgets-browser.js index fb9492fa7..c506afdb1 100644 --- a/lib/marko-widgets-browser.js +++ b/lib/marko-widgets-browser.js @@ -60,4 +60,8 @@ raptorPubsub if (widgetsContext) { widgetsContext.initWidgets(); } - }); \ No newline at end of file + }); + +window.$rwidgets = function(ids) { + initWidgets.initServerRendered(ids); +}; \ No newline at end of file diff --git a/lib/marko-widgets.js b/lib/marko-widgets.js index 30191620c..9ecfa716d 100644 --- a/lib/marko-widgets.js +++ b/lib/marko-widgets.js @@ -112,17 +112,19 @@ exports.attrs = function(widget) { return attrs; }; -exports.writeInitWidgetsCode = function(widgetsContext, context, options) { +exports.writeInitWidgetsCode = function(widgetsContext, out, options) { var clearWidgets = true; var scanDOM = false; + var immediate = false; if (options) { clearWidgets = options.clearWidgets !== false; scanDOM = options.scanDOM === true; + immediate = options.immediate === true; } if (scanDOM) { - context.write(TAG_START + '*' + TAG_END); + out.write(TAG_START + '*' + TAG_END); } else { var widgets = widgetsContext.getWidgets(); @@ -134,12 +136,6 @@ exports.writeInitWidgetsCode = function(widgetsContext, context, options) { var commaRequired = false; - var writeWidgets = function(widgets) { - for (var i = 0, len = widgets.length; i < len; i++) { - writeWidget(widgets[i]); - } - }; - var writeWidget = function(widget) { if (widget.children.length) { @@ -156,9 +152,19 @@ exports.writeInitWidgetsCode = function(widgetsContext, context, options) { ids += widget.id; }; + var writeWidgets = function(widgets) { + for (var i = 0, len = widgets.length; i < len; i++) { + writeWidget(widgets[i]); + } + }; + writeWidgets(widgets); - context.write(TAG_START + ids + TAG_END); + if (immediate) { + out.write(''); + } else { + out.write(TAG_START + ids + TAG_END); + } } if (clearWidgets !== false) { diff --git a/lib/uniqueId.js b/lib/uniqueId.js index c5fd647f9..57d1a472d 100644 --- a/lib/uniqueId.js +++ b/lib/uniqueId.js @@ -1,7 +1,7 @@ -module.exports = function (context) { - var attrs = context.attributes; - if (!attrs._nextId) { - attrs._nextId = 0; +module.exports = function (out) { + var global = out.global; + if (!global._nextWidgetId) { + global._nextWidgetId = 0; } - return attrs._nextId++; + return global._nextWidgetId++; }; \ No newline at end of file diff --git a/marko-taglib.json b/marko-taglib.json index 31038bb43..fa0cc54f2 100644 --- a/marko-taglib.json +++ b/marko-taglib.json @@ -38,14 +38,16 @@ "renderer": "./taglib/init-widgets-tag.js", "attributes": { "function-name": "string", - "include-script-tag": "boolean" + "include-script-tag": "boolean", + "immediate": "boolean" } }, "init-widgets": { "renderer": "./taglib/init-widgets-tag.js", "attributes": { "function-name": "string", - "include-script-tag": "boolean" + "include-script-tag": "boolean", + "immediate": "boolean" } } } diff --git a/taglib/init-widgets-tag.js b/taglib/init-widgets-tag.js index 3228be224..0b5e806d6 100644 --- a/taglib/init-widgets-tag.js +++ b/taglib/init-widgets-tag.js @@ -1,22 +1,32 @@ -var raptorWidgets = require('../'); +var markoWidgets = require('../'); -module.exports = function render(input, context) { - var widgetsContext = raptorWidgets.getWidgetsContext(context); +module.exports = function render(input, out) { + var widgetsContext = markoWidgets.getWidgetsContext(out); - if (context.featureLastFlush === false) { - // If the rendering context doesn't support the ability to know when all of the asynchronous fragmnents + var options = input.immediate ? {immediate: true} : null; + + if (input.immediate === true) { + out.on('asyncFragmentFinish', function(eventArgs) { + var asyncFragmentOut = eventArgs.out; + var widgetsContext = markoWidgets.getWidgetsContext(asyncFragmentOut); + markoWidgets.writeInitWidgetsCode(widgetsContext, asyncFragmentOut, options); + }); + } + + if (out.featureLastFlush === false) { + // If the rendering out doesn't support the ability to know when all of the asynchronous fragmnents // have completed then we won't be able to know which widgets were rendered so we will // need to scan the DOM to find the widgets - raptorWidgets.writeInitWidgetsCode(widgetsContext, context, {scanDOM: true}); + markoWidgets.writeInitWidgetsCode(widgetsContext, out, {scanDOM: true}); } else { - var asyncContext = context.beginAsync({ last: true, timeout: -1 }); - context.once('last', function() { - if (!widgetsContext.hasWidgets()) { - return asyncContext.end(); + var asyncOut = out.beginAsync({ last: true, timeout: -1 }); + out.onLast(function(next) { + if (widgetsContext.hasWidgets()) { + markoWidgets.writeInitWidgetsCode(widgetsContext, asyncOut, options); } - raptorWidgets.writeInitWidgetsCode(widgetsContext, asyncContext); - asyncContext.end(); + asyncOut.end(); + next(); }); } }; \ No newline at end of file diff --git a/taglib/widget-tag.js b/taglib/widget-tag.js index b9a078809..e2789c0bf 100644 --- a/taglib/widget-tag.js +++ b/taglib/widget-tag.js @@ -4,16 +4,16 @@ var DUMMY_WIDGET_DEF = { elId: function () { } }; -module.exports = function render(input, context) { +module.exports = function render(input, out) { var modulePath = input.module; var config = input.config || input._cfg; - var widgetArgs = context.attributes.widgetArgs; + var widgetArgs = out.global.widgetArgs; var id = input.id; - var scope = input.scope || context.getAttribute('widget'); + var scope = input.scope || out.getAttribute('widget'); var assignedId = input.assignedId; var events; if (widgetArgs) { - delete context.attributes.widgetArgs; + delete out.global.widgetArgs; scope = scope || widgetArgs.scope; assignedId = assignedId || widgetArgs.id; events = widgetArgs.events; @@ -21,7 +21,7 @@ module.exports = function render(input, context) { if (!id && input.hasOwnProperty('id')) { throw new Error('Invalid widget ID for "' + modulePath + '"'); } - var widgetsContext = widgets.getWidgetsContext(context); + var widgetsContext = widgets.getWidgetsContext(out); if (modulePath) {