In developing this library, I borrowed heavily from both Douglas Crockford and Daniel Brockman. Without their insight into JavaScript, my library would never have seen the light of day. I owe both these men a deep debt of gratitude.
That being said, neither Douglas Crockford nor Daniel Brockman are in any way responsible for the code that follows. If you decide to use any or all of the code, you alone bear the burden of responsibility. It is provided "as is" with no warranty or guarantee.
In this article, I will discuss the core functions of "apex". I will discuss the add-in modules in other articles when time permits.
The code is shown in sections, and a discussion follows.
Note: the "</p>" tags at the beginning of each code snippet are artifacts from the article creation process provided by Experts-Exchange; they are not part of my article nor my code.
- 1
- Declaration
"apex" begins with a header block, followed by a few functions and declarations:1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48:
/* ###################################### apex.js Universal properties and methods Components ========== .core Core services .ajax AJAX methods .event Event management .json JSON "to_string" and "to_object" .string LTrim, RTrim, Trim, Pad, Chop ###################################### */ // If the "apex" namespace does not exist, create it if (typeof apex === 'undefined') { var apex = {}; } // Create true array from pseudo-array (like "arguments") function to_array(imposter) { var ra = []; for(var i = 0; i < imposter.length; i++) { ra.push(imposter[i]); } return ra; } // Create namespace within "apex" apex.namespace = function() { var a = to_array(arguments); var o = null; for (var i = 0; i < a.length; i++) { var d = a[i].split("."); o = apex; for (var j = (d[0] === 'apex') ? 1 : 0; j < d.length; j++) { o[d[j]] = o[d[j]] || {}; o = o[d[j]]; } } return o; }; // Known namespaces apex.namespace('core','ajax','event','json','string');
The function "to_array" returns a true Array object from a pseudo array such as the "arguments" collection that is available to every function. Declaring it at the top of the code makes it available to all of the functions in the library.
"apex.namespace" adds a handle for a new add-in.
- 2
- Event Handling
Next we need a way to handle events. I use Daniel Brockman's method, available from the link in the comment block.1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38:
/* ###################################### OO Event Listening through Partial Application in JavaScript, from http://www.brockman.se/writing/method-references.html.utf8 ###################################### */ (function () { var destructor = {}; Function.prototype.bind = function(object) { var method = this; var oldArguments = to_array(arguments).slice(1); return function(argument) { if (argument === destructor) { method = null; oldArguments = null; } else if (method === null) { throw 'Attempt to invoke destructed method reference'; } else { var newArguments = to_array(arguments); return method.apply(object, oldArguments.concat(newArguments)); } }; }; Function.prototype.bindEventListener = function(object) { var method = this; var oldArguments = to_array(arguments).slice(1); return function(event) { return method.apply(object, [event || window.event].concat(oldArguments)); }; }; })();
This function is added to the prototype chain for Function. It allows us to bind a method to an object. It also lets us bind an event handler to an object. It provides a way to un-bind a method by passing in a new Object ("{}"). Finally, it lets us curry a function, that is, to pass a partial list of parameters to a function.
Visit http://www.brockman.se/writing/metho d-referenc es.html.ut f8 for a much more thorough explanation of the inner workings of this code.
- 3
- Core Properties and Methods
Next we have the "core" portion of apex.1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129:
// #################################### apex.core = function() { var site = 'over-engineering'; var PURL = document.location.href; var URL_BASE = 'http://' + (PURL.indexOf('www.') > 0 ? 'www.' + site + '.com/' : 'localhost/' + site) + '/'; var URL_BROKER = URL_BASE + 'apex/'; var URL_FULL = URL_BASE; function enclude(file_name, loaded) { // File prefix and suffix var pfx = '<script type="text/javascript" src="' + URL_BROKER; var sfx = '"><\/script>'; // Include the file(s) for (var i = 0; i < file_name.length; i++) { if (!loaded) { document.write(pfx + file_name[i] + sfx); } else { var elem = document.createElement('script'); elem.src = pfx + file_name[i] + sfx; document.body.appendChild(elem); } } } function get_node_type(name) { var node_name = { 'ELEMENT' :'1', 'ATTRIBUTE' :'2', 'TEXT' :'3', 'CDATA_SECTION' :'4', 'ENTITY_REFERENCE' :'5', 'ENTITY' :'6', 'PROCESSING_INSTRUCTION':'7', 'COMMENT' :'8', 'DOCUMENT' :'9', 'DOCUMENT_TYPE' :'10', 'DOCUMENT_FRAGMENT' :'11', 'NOTATION' :'12' }; return node_name[name.toUpperCase()]; } function get_node_name(type) { var node_type = { '1' :'ELEMENT', '2' :'ATTRIBUTE', '3' :'TEXT', '4' :'CDATA_SECTION', '5' :'ENTITY_REFERENCE', '6' :'ENTITY', '7' :'PROCESSING_INSTRUCTION', '8' :'COMMENT', '9' :'DOCUMENT', '10':'DOCUMENT_TYPE', '11':'DOCUMENT_FRAGMENT', '12':'NOTATION' }; return node_type[type]; } function get_broker_control() { return function(){ return { 'async' :false, 'broker_url' :URL_BROKER, 'broker_dir' :'apex/', 'broker_name' :'xs_sys.asp', 'callback_method' :null, 'callback_parms' :null, 'proc_name' :'', 'notify_window' :null, 'alert_interval' :1000, 'content_type' :'application/x-www-form-urlencoded', 'header' :[], 'mode' :'' /* Set to 'plain_text' for non-JSON requests */ }; }(); } function get_elements_by_className(arg) { // Expects // // arg.node Location of the elements (default is "document) // arg.tag = Tagname (default is "*") // // Returns // arg.result Array of objects arg.node = arg.node || document; arg.tag = arg.tag || '*'; arg.result = []; // var el = arg.node.getElementsByTagName(arg.tag); var pattern = new RegExp('(^|\\s)' + arg.clas + '(\\s|$)'); // for (var i = 0; i < el.length; i++) { if (pattern.test(el[i].className)) { arg.result.push({'type':el[i].tagName, 'id':el[i].id}); } } return arg; } function tcelfer(obj) { // Expects // // obj JSON object obj = obj || apex.core; var ra = []; if ((obj.length) || 0 > 0) { for (var j = 0; j < obj.length; j++) { ra.push(tcelfer(obj[j])); } } else { for (var n in obj) { if (obj.hasOwnProperty(n)) { ra.push([n, obj[n]]); } } } return ra; }
By declaring "apex.core" as a function (or class, if you prefer), we can have private properties and methods and privileged (or public) methods and properties.
- 4
- Private Methods and Properties
The initial declarations set some URL locations.
"enclude" provides us with a way to include external files.
"get_node_type" returns the value of a "nodeName" attribute.
"get_node_name" returns the "nodeName" from a value.
"get_broker_control" returns a JSON object used for Ajax. The default settings are for a Synchronous Ajax call. Changing the "async" property to "true", referencing a Function in "callback_method" and setting the (optional) parameters in "callback_parms" will result in an Asynchronous Ajax call.
"get_elements_by_className" returns an array of JSON objects denoting HTML elements sharing a CSS class name.
"tcelfer" is a rudimentary reflection method. It returns a "name=value" list for any JSON object.
- 5
- Exposed Methods and Properties
These provide access to the private properties and methods.1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27:
// Exposed properties and methods return { /* * ############################ * Internal methods * ---------------------------- */ 'broker_url' :URL_BROKER, 'region' :site + '.com', 'region_from_db':false, /* get region from db? */ '$' :function(arg) { return document.getElementById(arg); }, '$$' :function(arg) { return get_elements_by_className(arg); }, '$$$' :function(tag) { return document.getElementsByTagName(tag); }, 'broker_control':function() { return get_broker_control(); }, 'enclude' :function(file_name, mode) { return enclude([file_name], mode); }, /* INCLUDE external file */ 'event_control' :function(cat, ele, eve, han) { return get_event_control(cat, ele, eve, han); }, 'reflect' :function(arg) { return tcelfer(arg); }, 'error_trap' :function(desc, url, line) { return trap_error(desc, url, line); }, 'node_type' :function(arg) { return get_node_type(arg); }, 'node_name' :function(arg) { return get_node_name(arg); }, // 'empty' :null // dummy object closer }; }();
"empty" is an indicator that terminates the exposed methods. Since each exposed method is followed by a comma, "empty" is just a convenience, but it prevents me from forgetting to add a comma if I add a new method.
- 6
- Usage
"apex" is included in a standard <script> block.1: 2:
[script type="text/javascript" src="apex/apex.js"][/script]
To set event handlers, I have a "page_load" function (note that the "event" add-in has not yet been discussed):1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
<script type="text/javascript"> var catg = 'page'; // Events are grouped for un-binding function page_exit() { apex.event.flush('all'); return true; } function begin() { // Stub } function finis() { //Stub } function page_load() { apex.event.sink({ 'category':catg, 'element':window, 'event_name':'unload', 'handler':page_exit, 'capture':false }); apex.event.sink({ 'category':catg, 'element':apex.core.$('cmdStart'), 'event_name':'click', 'handler':begin, 'capture':false }); apex.event.sink({ 'category':catg, 'element':apex.core.$('cmdStop'), 'event_name':'click', 'handler':finis, 'capture':false }); } window.onload = page_load; </script>
"catg" is used to group the handlers; a different value will allow groups of handlers to be bound and unbound while leaving the other groups attached.
"page_load" sets the handlers (and anything else that must happen).
"page_exit" performs clean up duties.
"begin" and "finis" are stub functions for buttons defined elsewhere on the page; I include them as an example for clarity.
- 7
- Conclusion
This concludes the article on the core properties and methods of the "apex" JavaScript library.
I will expand on the usage of "apex" in further articles, and as time permits.