Skip to content
Snippets Groups Projects
spawner.js 12.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*!
     * Spawn UI elements using the Spawner service
     */
    
    var XNAT = getObject(XNAT);
    
    (function(factory){
        if (typeof define === 'function' && define.amd) {
            define(factory);
        }
        else if (typeof exports === 'object') {
            module.exports = factory();
        }
        else {
            return factory();
        }
    }(function(){
    
        var undefined,
    
            NAMESPACE  = 'XNAT.ui',
            $          = jQuery || null, // check and localize
    
        XNAT.ui =
            getObject(XNAT.ui || {});
        XNAT.spawner = spawner =
            getObject(XNAT.spawner || {});
    
        // keep track of items that spawned
        spawner.spawnedElements = [];
    
        // keep track of items that didn't spawn
        spawner.notSpawned = [];
    
        // ==================================================
        // MAIN FUNCTION
    
        spawner.spawn = spawner.init = function _spawn(obj){
    
    
            var frag  = document.createDocumentFragment(),
    
                var kind, element, method, spawnedElement, $spawnedElement;
    
                // 'prop' can be a new or existing DOM element
                if (prop instanceof Element) {
                    element = prop;
                    prop = {
                        kind: 'element',
                        element: element
                    };
                }
                else {
                    // save the config properties in a new object
                    prop = cloneObject(prop);
                }
    
                // add this for proper handling in 'universal' widgets
                prop.spawnerElement = true;
    
    
                prop.element = prop.element || prop.config || {};
    
    
                // use 'name' property in element or config
                // then look for 'name' at object root
    
                // lastly use the object's own name
                prop.name = prop.name || item;
    
                // auto-generate IDs if not specified
                // I really don't like doing this here.
                prop.id = prop.id || prop.element.id || toDashed(prop.name);
    
                // accept 'kind' or 'type' property name
                // but 'kind' will take priority
                // with a fallback to a generic div
    
                kind = prop.kind || prop.type || null;
    
                // start at the site root if starting with '/'
                if (prop.element.href) {
                    prop.element.href = setRoot(prop.element.href)
    
                if (prop.element.src) {
                    prop.element.src = setRoot(prop.element.src)
    
                if (prop.element.action) {
                    prop.element.action = setRoot(prop.element.action)
    
                // do a raw spawn() if 'kind' is 'element'
                // or if there's a tag property
    
                if (kind === 'element' || prop.tag || prop.element.tag) {
    
    
                    // pass 'content' (not contentS) property to add
                    // stuff directly to spawned element
    
                    prop.content = prop.content || prop.children || '';
    
                        // if setting up Spawner elements in JS, allow a
                        // DOM element to be passed in the 'element' property
                        if (prop.element instanceof Element) {
                            spawnedElement = prop.element;
                        }
                        else {
                            spawnedElement = spawn(prop.tag || prop.element.tag || 'span', prop.element, prop.content);
                        }
    
    
                        // convert relative URIs for href, src, and action attributes
                        if (spawnedElement.href) {
                            spawnedElement.href = setRoot(spawnedElement.getAttribute('href'))
                        }
                        if (spawnedElement.src) {
                            spawnedElement.src = setRoot(spawnedElement.getAttribute('src'))
                        }
                        if (spawnedElement.action) {
                            spawnedElement.action = setRoot(spawnedElement.getAttribute('action'))
                        }
    
    
                        // jQuery's .append() method is
                        // MUCH more robust and forgiving
                        // than element.appendChild()
    
                        $frag.append(spawnedElement);
                        spawner.spawnedElements.push(spawnedElement);
    
                else if (/^(text|html)$/i.test(kind)) {
                    $frag.append(prop.content||prop.html||prop.text)
                }
    
                        lookupObjectValue(XNAT, kind + '.init') ||
    
                        lookupObjectValue(XNAT, kind) ||
    
                        // XNAT.ui.kind.init()
                        lookupObjectValue(NAMESPACE + '.' + kind + '.init') ||
    
                        // XNAT.ui.kind()
                        lookupObjectValue(NAMESPACE + '.' + kind) ||
    
                        // XNAT.element.kind()
                        lookupObjectValue(XNAT, 'element.' + kind) ||
    
    
    
                    // only spawn elements with defined methods
                    if (isFunction(method)) {
    
    
    Mark M. Florida's avatar
    Mark M. Florida committed
                        // 'spawnedElement' item will be an
    
                        // object with an 'element' property 
                        // or a .get() method that will retrieve 
                        // the spawned item
    
                        // add spawnedElement to the master frag
    
                        $frag.append(spawnedElement.element);
    
                        // save a reference to spawnedElement
    
                        spawner.spawnedElements.push(spawnedElement.element);
    
                        if (hasConsole) console.log('not spawned: ' + prop);
                        spawner.notSpawned.push(prop);
    
                // give up if no spawnedElement
                if (!spawnedElement) return;
    
    
                // spawn child elements from...
                // 'contents' or 'content' or 'children' or
                // a property matching the value of either 'contains' or 'kind'
    
                if (prop.contains || prop.contents || prop[prop.kind]) {
                    prop.contents = prop[prop.contains] || prop.contents || prop[prop.kind];
    
    Mark M. Florida's avatar
    Mark M. Florida committed
                    // if there's a 'target' property, put contents in there
    
                    if (spawnedElement.target || spawnedElement.inner) {
                        $spawnedElement = $(spawnedElement.target || spawnedElement.inner);
    
                        $spawnedElement = $(spawnedElement.element);
    
    
                    // if a string, number, or boolean is passed as 'contents'
                    // just append that as-is (as a string)
                    if (stringable(prop.contents)) {
                        $spawnedElement.append(prop.contents+'');
                    }
                    else {
                        $spawnedElement.append(_spawn(prop.contents).get());
                    }
    
                // Treat 'before' and 'after' just like 'contents'
                // but insert the items 'before' or 'after' the main
                // spawned (outer) element. This may have unintended
                // consequences depending on the HTML structure of the
                // spawned widget that has things 'before' or 'after' it.
    
    
                    if (stringable(prop.after) || Array.isArray(prop.after)) {
                        $frag.append(prop.after)
                    }
                    else if (isPlainObject(prop.after)) {
                        $frag.append(_spawn(prop.after).get())
                    }
    
                    if (stringable(prop.before) || Array.isArray(prop.before)) {
                        $frag.prepend(prop.before)
                    }
                    else if (isPlainObject(prop.before)) {
                        $frag.prepend(_spawn(prop.before).get())
                    }
    
                // if there's a .load() method, fire that
    
                if (isFunction(spawnedElement.load||null)) {
    
                    spawnedElement.load.call(spawnedElement);
                }
                
                // if there's an .onRender() method, queue it
                if (isFunction(spawnedElement.onRender||null)) {
                    callbacks.push({
                        onRender: spawnedElement.onRender,
                        spawned: spawnedElement,
                        $element: $spawnedElement
                    });
    
    Mark M. Florida's avatar
    Mark M. Florida committed
            _spawn.children = frag.children;
    
    Mark M. Florida's avatar
    Mark M. Florida committed
            _spawn.getContents = function(){
                return $frag.contents();    
            };
    
            _spawn.done = function(callback){
                if (isFunction(callback)) {
    
            _spawn.render = function(container, wait, callback){
    
    
                var $container = $$(container).hide();
    
                // fire collected callbacks
                callbacks.forEach(function(obj){
                    try {
                        obj.onRender.call(obj.spawned, obj.$element, obj);
                    }
                    catch(e) {
                        console.log(e)
                    }
                });
    
                setTimeout(function(){
    
        // ==================================================
    
        // given a container and spawner object,
        // spawn the elements into the container
        spawner.render = function(container, obj){
            return spawner.spawn(obj).render($$(container))
        };
    
    
        // spawn elements with only the namespace/element path,
        // container/selector, and an optional AJAX config object
        // XNAT.spawner.resolve('siteAdmin/adminPage').render('#page-container');
        // or assign it to a variable and render later.
        // var adminPage = XNAT.spawner.resolve('siteAdmin/adminPage');
        // adminPage.render('#page-container');
        // and methods from the AJAX request will be in .get.done(), .get.fail(), etc.
        spawner.resolve = function(nsPath, opts) {
    
            // you can pass a config object as the only argument
            opts = cloneObject(firstDefined(opts, getObject(nsPath)));
    
            console.log(opts);
    
            var url = opts.url || XNAT.url.restUrl('/xapi/spawner/resolve/' + nsPath);
    
            var request = XNAT.xhr.getJSON(extend(true, {
                url: url
            }, opts));
    
    
            function spawnRender(){
                var renderArgs = arguments;
    
                    spawner.spawn(obj).render.apply(request, renderArgs);
                    return request;
    
                    XNAT.url.rootUrl('/page/admin/data/config/site-admin-sample-new.json');
            return $.getJSON({
                url: jsonUrl,
                success: function(data){
                    spawner.spawn(data);
                }
            });