diff --git a/guacamole/src/main/webapp/app/index/config/templateRequestDecorator.js b/guacamole/src/main/webapp/app/index/config/templateRequestDecorator.js index cb848c3c0..51e2361d1 100644 --- a/guacamole/src/main/webapp/app/index/config/templateRequestDecorator.js +++ b/guacamole/src/main/webapp/app/index/config/templateRequestDecorator.js @@ -32,17 +32,193 @@ angular.module('index').config(['$provide', function($provide) { var $q = $injector.get('$q'); /** - * Array of the root elements of all patches which should be applied to - * the HTML of retrieved templates. + * Array of the raw HTML of all patches which should be applied to the + * HTML of retrieved templates. * - * @type Element[] + * @type String[] */ var patches = [ - $('

HELLO BEFORE

')[0], - $('

HELLO AFTER

')[0], - $('
:-)
')[0] + '

HELLO BEFORE

', + '

HELLO AFTER

', + '
:-)
' ]; + /** + * Represents a single HTML patching operation which will be applied + * to the raw HTML of a template. The name of the patching operation + * MUST be one of the valid names defined within + * PatchOperation.Operations. + * + * @contructor + * @param {String} name + * The name of the patching operation that will be applied. Valid + * names are defined within PatchOperation.Operations. + * + * @param {String} selector + * The CSS selector which determines which elements within a + * template will be affected by the patch operation. + */ + var PatchOperation = function PatchOperation(name, selector) { + + /** + * Applies this patch operation to the template defined by the + * given root element, which must be a single element wrapped by + * JQuery. + * + * @param {Element[]} root + * The JQuery-wrapped root element of the template to which + * this patch operation should be applied. + * + * @param {Element[]} elements + * The elements which should be applied by the patch + * operation. For example, if the patch operation is inserting + * elements, these are the elements that will be inserted. + */ + this.apply = function apply(root, elements) { + PatchOperation.Operations[name](root, selector, elements); + }; + + }; + + /** + * Mapping of all valid patch operation names to their corresponding + * implementations. Each implementation accepts the same three + * parameters: the root element of the template being patched, the CSS + * selector determining which elements within the template are patched, + * and an array of elements which make up the body of the patch. + * + * @type Object. + */ + PatchOperation.Operations = { + + /** + * Inserts the given elements before the elements matched by the + * provided CSS selector. + * + * @param {Element[]} root + * The JQuery-wrapped root element of the template being + * patched. + * + * @param {String} selector + * The CSS selector which determines where this patch operation + * should be applied within the template defined by root. + * + * @param {Element[]} elements + * The contents of the patch which should be applied to the + * template defined by root at the locations selected by the + * given CSS selector. + */ + 'before' : function before(root, selector, elements) { + root.find(selector).before(elements); + }, + + /** + * Inserts the given elements after the elements matched by the + * provided CSS selector. + * + * @param {Element[]} root + * The JQuery-wrapped root element of the template being + * patched. + * + * @param {String} selector + * The CSS selector which determines where this patch operation + * should be applied within the template defined by root. + * + * @param {Element[]} elements + * The contents of the patch which should be applied to the + * template defined by root at the locations selected by the + * given CSS selector. + */ + 'after' : function after(root, selector, elements) { + root.find(selector).after(elements); + }, + + /** + * Replaces the elements matched by the provided CSS selector with + * the given elements. + * + * @param {Element[]} root + * The JQuery-wrapped root element of the template being + * patched. + * + * @param {String} selector + * The CSS selector which determines where this patch operation + * should be applied within the template defined by root. + * + * @param {Element[]} elements + * The contents of the patch which should be applied to the + * template defined by root at the locations selected by the + * given CSS selector. + */ + 'replace' : function replace(root, selector, elements) { + root.find(selector).replaceWith(elements); + }, + + /** + * Inserts the given elements within the elements matched by the + * provided CSS selector, before any existing children. + * + * @param {Element[]} root + * The JQuery-wrapped root element of the template being + * patched. + * + * @param {String} selector + * The CSS selector which determines where this patch operation + * should be applied within the template defined by root. + * + * @param {Element[]} elements + * The contents of the patch which should be applied to the + * template defined by root at the locations selected by the + * given CSS selector. + */ + 'before-children' : function beforeChildren(root, selector, elements) { + root.find(selector).prepend(elements); + }, + + /** + * Inserts the given elements within the elements matched by the + * provided CSS selector, after any existing children. + * + * @param {Element[]} root + * The JQuery-wrapped root element of the template being + * patched. + * + * @param {String} selector + * The CSS selector which determines where this patch operation + * should be applied within the template defined by root. + * + * @param {Element[]} elements + * The contents of the patch which should be applied to the + * template defined by root at the locations selected by the + * given CSS selector. + */ + 'after-children' : function afterChildren(root, selector, elements) { + root.find(selector).append(elements); + }, + + /** + * Inserts the given elements within the elements matched by the + * provided CSS selector, replacing any existing children. + * + * @param {Element[]} root + * The JQuery-wrapped root element of the template being + * patched. + * + * @param {String} selector + * The CSS selector which determines where this patch operation + * should be applied within the template defined by root. + * + * @param {Element[]} elements + * The contents of the patch which should be applied to the + * template defined by root at the locations selected by the + * given CSS selector. + */ + 'replace-children' : function replaceChildren(root, selector, elements) { + root.find(selector).empty().append(elements); + } + + }; + /** * Invokes $templateRequest() with all arguments exactly as provided, * applying all HTML patches from any installed Guacamole extensions @@ -65,29 +241,38 @@ angular.module('index').config(['$provide', function($provide) { // Apply all defined patches angular.forEach(patches, function applyPatch(patch) { - // Ignore any patches which are malformed - if (patch.tagName !== 'GUAC-PATCH') - return; + var elements = $(patch); - // Insert after any elements which match the "after" - // selector (if defined) - var after = patch.getAttribute('after'); - if (after) - root.find(after).after(patch.innerHTML); + // Filter out and parse all applicable meta tags + var operations = []; + elements = elements.filter(function filterMetaTags(index, element) { - // Insert before any elements which match the "before" - // selector (if defined) - var before = patch.getAttribute('before'); - if (before) - root.find(before).before(patch.innerHTML); + // Leave non-meta tags untouched + if (element.tagName !== 'META') + return true; - // Replace any elements which match the "replace" selector - // (if defined) - var replace = patch.getAttribute('replace'); - if (replace) - root.find(replace).html(patch.innerHTML); + // Only meta tags having a valid "name" attribute need + // to be filtered + var name = element.getAttribute('name'); + if (!name || !(name in PatchOperation.Operations)) + return true; - // Ignore all other attributes + // The "content" attribute must be present for any + // valid "name" meta tag + var content = element.getAttribute('content'); + if (!content) + return true; + + // Filter out and parse meta tag + operations.push(new PatchOperation(name, content)); + return false; + + }); + + // Apply each operation implied by the meta tags + angular.forEach(operations, function applyOperation(operation) { + operation.apply(root, elements); + }); });