3
\$\begingroup\$

In response to my own question on Stack Overflow, I'm attempting a solution to decouple JavaScript and html. I'd appreciate any comments, proposed refactoring, or criticism.

I have a fiddle of it in use here

Here's the code:

(function (window, $, undefined) {
    "use strict";
    var ab = window.ab = window.autoBinder = {},
        elements;

    ab.exportSymbol = function (publicPath, object) {
        var tokens = publicPath.split("."),
            target = window,
            i;

        for (i = 0; i < tokens.length - 1; i++) {
            target = target[tokens[i]];
        }
        target[tokens[tokens.length - 1]] = object;
    };

    ab.exportProperty = function (owner, publicName, object) {
        owner[publicName] = object;
    };

    ab.extensions = (function () {
        return {
            datePicker: function (element) {
                var params = {
                    minDate: 0,
                    showButtonPanel: false
                },
            e = $(element);

                if (e.data("mindate") !== undefined) {
                    params.minDate = e.data("mindate");
                }

                if (e.data("showpanel") !== undefined) {
                    params.showButtonPanel = e.data("showpanel");
                }

                e.datepicker(params);
            }
        };
    })();

    ab.hookups = (function () {
        return {
            make: function (value, element) {
                if (ab.extensions[value]) {
                    ab.extensions[value](element);
                }
            },
            publish: function (value, $element) {
                if (!value) {
                    return;
                }

                $element.change(function () {
                    $element.trigger(value, $element);
                });
            },
            subscribe: function (value, $element) {
                var trigger = $element.data("trigger");
                if (trigger === undefined) {
                    throw "Found Subscribe without Trigger.";
                }

                if (!ab.extensions[trigger]) {
                    throw "Found Subscribe without Trigger.";
                }

                $(document).bind(value, function (event, htmlElement) {
                    ab.extensions[trigger](event, htmlElement, $element);
                });
            }
        };
    })();

    ab.exportSymbol("ab.extensions.datePicker", ab.extensions.datePicker);
    ab.exportSymbol("ab.hookups.make", ab.hookups.make);
    ab.exportSymbol("ab.hookups.publish", ab.hookups.publish);
    ab.exportSymbol("ab.hookups.subscribe", ab.hookups.subscribe);


    $(window).load(function () {
        elements = $("*").filter(function () {
            var data = $(this).data(),
                p;
            if (data === undefined) {
                return false;
            }
            for (p in data) {
                if (data.hasOwnProperty(p)) {
                    return true;
                }
            }
            return false;
        });

        elements.each(function () {
            var obj = $(this),
                data = obj.data(),
                p;

            for (p in data) {
                if (data.hasOwnProperty(p)) {
                    if (ab.hookups[p]) {
                        ab.hookups[p](data[p], obj);
                    }
                }
            }
        });
    });
} (window, jQuery));

// #region Example of Extending extensions
    window.ab.extensions.effectiveDateChanged = function (event, triggerElement, subscribedElement) {
        var $element = jQuery(triggerElement);
        subscribedElement.val("Value set to: " + $element.val());
    };

window.ab.extensions.dropDownChanged = function(event, triggerElement, subscribedElement) {
    var $element = jQuery(triggerElement),
        value = $element.val();

    if (value === "") {
        subscribedElement.attr("disabled", "disabled");
        subscribedElement.html("");
        return;
    }

    subscribedElement.removeAttr("disabled");
    subscribedElement.html("<option>" + value + "</option>");
};
// #endregion
\$\endgroup\$
3
\$\begingroup\$

Update here: http://jsfiddle.net/5ej8G/3/ (code is also pasted below)

Main suggestions:

  • your exportSymbol function can be refactored to handle an array of publicPaths and Objects.
  • The subscribe conditions can be consolidated since they throw the same message.
  • check your parameters before using. typeOf checks don't work well with all objects, but there are techniques to have them work with all objects. One from Angus Croll(http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/) is included in the link above.
  • Explicitly state which elements you want your code to act on, maybe by passing in a parameter object. doing a $("*"), on medium to large pages (with lots of dom elements) can be a performance hit.
(function (window, $, undefined) {
    "use strict";
    var ab = window.ab = window.autoBinder = {},
        elements,

        toType = function(obj) {
      return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
    };
    //can handle an array of publicPaths and objects
    ab.exportSymbols = function (paramArray) {
        var tokens,
            target = window,
            i,
            index,
            len,
            limit,
            exportSymbol;
        if(toType(paramArray) !== 'array') { 
            throw "You must pass an array into this function.";            
        }

        //Export Symbol now becomes a inner helper function
        exportSymbol = function(pubPath, obj) {
            tokens = pubPath.split(".");
            lastIndex = tokens.Length;

            for (i = 0; i < lastIndex ; i++) {
                target = target[tokens[i]];
            }
            target[tokens[lastIndex]] = obj;
        };

        //validate and run helper function on each object in array
        for(index= 0, len=paramArray.length; i < len; i++) {
            if(toType(paramArray.publicPath) !== 'string' 
               || toType(paramArray.publicPath) !== 'object') {
                    continue;
            }
            exportSymbol(paramArray[index].publicPath, paramArray[index].obj);            
        }
    };

    ab.exportProperty = function (owner, publicName, object) {
        owner[publicName] = object;
    };

    ab.extensions = function () {
        return {
            datePicker: function (element) {
                var params = {
                    minDate: 0,
                    showButtonPanel: false
                },
            e = $(element);

                if (e.data("mindate") !== undefined) {
                    params.minDate = e.data("mindate");
                }

                if (e.data("showpanel") !== undefined) {
                    params.showButtonPanel = e.data("showpanel");
                }

                e.datepicker(params);
            }
        };
    }();

    ab.hookups = (function () {
        return {
            make: function (value, element) {

                if (ab.extensions[value]) {
                    ab.extensions[value](element);
                }
            },
            publish: function (value, $element) {

                if (!value) {
                    return;
                }

                $element.change(function () {
                    $element.trigger(value, $element);
                });
            },
            subscribe: function (value, $element) {
                var trigger = $element.data("trigger");

               //Conditions can be consolidate since they trow the same message
                if ((trigger === undefined) || !ab.extensions[trigger]) {
                    throw "Found Subscribe without Trigger.";
                }                

                $(document).bind(value, function (event, htmlElement) {
                    ab.extensions[trigger](event, htmlElement, $element);
                });
            }
        };
    })();

    //can pass in a an array of object instead of mutiple calls
    ab.exportSymbols([
                     {pubPath : "ab.extensions.datePicker", obj : ab.extensions.datePicker},
                     {pubPath : "ab.hookups.make"         , obj : ab.hookups.make},
                     {pubPath : "ab.hookups.publish"      , obj : ab.hookups.publish},
                     {pubPath : "ab.hookups.subscribe"    , obj : ab.hookups.subscribe}
    ]);


    $(window).load(function () {
        elements = $("*").filter(function () {
            var data = $(this).data(),
                p;
            if (data === undefined) {
                return false;
            }
            for (p in data) {
                if (data.hasOwnProperty(p)) {
                    return true;
                }
            }
            return false;
        });

        elements.each(function () {
            var obj = $(this),
                data = obj.data(),
                p;

            for (p in data) {
                if (data.hasOwnProperty(p)) {
                    if (ab.hookups[p]) {
                        ab.hookups[p](data[p], obj);
                    }
                }
            }
        });
    });
} (window, jQuery));

//  Example Extending extensions
    window.ab.extensions.effectiveDateChanged = function (event, triggerElement, subscribedElement) {
        var $element = jQuery(triggerElement);
        subscribedElement.val("Value set to: " + $element.val());
    };

window.ab.extensions.dropDownChanged = function(event, triggerElement, subscribedElement) {
    var $element = jQuery(triggerElement),
        value = $element.val();

    if (value === "") {
        subscribedElement.attr("disabled", "disabled");
        subscribedElement.html("");
        return;
    }

    subscribedElement.removeAttr("disabled");
    subscribedElement.html("<option>" + value + "</option>");
};
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.