/*jslint plusplus: true, white: true, indent: 2, maxlen: 90 */
/**
* @author Wolfgang Kowarschick
* @copyright 2013, Wolfgang Kowarschick
* <br/>
* Redistribution and use in source and binary forms, with or without
* modification, are permitted under the terms of the
* Creative Commons License Attribution-NonCommercial-ShareAlike 3.0 Unported
* (CC BY-NC-SA 3.0: http://creativecommons.org/licenses/by-nc-sa/3.0/).
*/
/**
* The sandbox pattern: When this function is evaluated,
* it first loads all modules stated in <code>p_modules</code>
* and then calls the callback function
* <pre>function(p_env_1, ... p_env_n)
*{ // code running in a sandbox
*}</pre>
* which has to be passed as last argument,
*
* @see Stoyan Stefanov, JavaScript Patterns. O'Reilly, 2010
*
* @property WAIT_UNTIL_READY - A dummy argument for <code>$wk<$code> calls.
* When it is passed the sandbox is not executed
* before the HTML document has been loaded
* completely.
* @property WAIT_UNTIL_COMPLETE - A dummy argument for <code>$wk<$code> calls.
* When it is passed the sandbox is not executed
* before the HTML document and all
* its subdocuments (images etc.) have been
* loaded completely.
* @function
* @global
* @name $wk$
*
* @param {Object[]} arguments
* A list of JSON file names (strings) and/or arbitrary objects.
* Each JSON file is loaded and its content is passed to the sandbox
* function. Every other content object is directly passed to the callback
* function.
* @param {Function} p_sandbox
* As last argument the sandbox, a callback function, has to be passed:
* <code>function(p_env_1, ... p_env_n){ ...}</code>
* The environment parameter <code>p_env_1</code>, ..., <code>p_env_n</code>
* contain the <code>objects</code> created or passed by <code>$wk$</code>.
* So the number of arguments should be equal to
* <code>arguments.length-1</code>
*/
(function(p_root, document, XMLHttpRequest)
{ "use strict";
////////////////////////////////////////////////////////////////////////////////
// $wk$ library: utilities
////////////////////////////////////////////////////////////////////////////////
var l_wk, l_key = null;
/**
* An object created by calling <code>$new$(Class, [1, 2, 3])</code>
* is identical to an object created with <code>new Class(1, 2, 3)</code>.
*
* @method
* @name $wk$.$new$
*
* @param {Function} p_constructor A function to be used as constructor.
* @param {Array} p_arguments An array of arguments to be passed
* to the constructor
* @returns {Object} The newly constructed object.
* @see http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
*/
function f_new(p_constructor, p_arguments)
{ function F_C()
{ return p_constructor.apply(this, p_arguments); }
F_C.prototype = p_constructor.prototype;
return new F_C();
}
/**
* Creates a chain of nested objects.
* <p>
* For instance: The following function call
* <pre> $wk$.pkg("ldvcs.view");</pre>
* results in the creation of two objects (if they do not already exist):
* <pre>
* ldvcs
* ldvcs.view</pre>
* </p>
*
* @method
* @name $wk$.pkg
*
* @param {String} p_name A package name, i.e. one or more name literals
* separated by dots.
* @param {String} [p_create = true]
* Indicates, whether or not to create the package,
* if it does not exists
* @returns {Object} The last package object of the package
* object chain.
*/
function f_pkg(p_name)
{ var i, n,
l_subpackages = p_name.split("."), // all literal names
l_name,
l_subpackage_object,
l_package_object = p_root; // the root object
for (i = 0, n = l_subpackages.length; i < n; i++) // for each literal name
{ l_name = l_subpackages[i]; // the current literal name
// Fetch the associated package object.
l_subpackage_object = l_package_object[l_name];
// If it does not exist: create it.
if (!l_subpackage_object)
{ l_package_object[l_name] = l_subpackage_object = {}; }
// Set the current subpackage object to be the new package object,
// for which the next subpackage object hast to be created.
l_package_object = l_subpackage_object;
}
return l_package_object;
}
/**
* Recursively mixes properties of a source object into a target object.
* Overridable properties are overridden, others not.
*
* @method
* @name $wk$.mixin
*
* @param {Object|String} p_target An arbitrary object.
* @param {Object|String} p_source
* Another arbitrary object (or a path name of an object)
* the properties of which are to be recursively mixed into
* <code>p_target</code>.
*/
function f_mixin(p_source, p_target)
{ var l_key = null,
l_target;
p_source = (p_source && p_source.constructor === String)
? f_pkg(p_source, false)
: p_source;
p_target = (p_target.constructor === String)
? f_pkg(p_target, false)
: p_target;
if (p_source === p_target)
{ return; }
for (l_key in p_source)
{ if (p_source.hasOwnProperty(l_key))
{ l_target = p_target[l_key];
if ( l_target && l_target instanceof Object
&& !(l_target instanceof Function)
)
{ f_mixin(p_source[l_key], l_target); }
else
{ Object.defineProperty
(p_target, l_key, Object.getOwnPropertyDescriptor(p_source, l_key));
}
}
}
}
l_wk =
function WK()
{ // Ensure that the function is called as a constructor.
if (!(this instanceof WK))
{ return f_new(WK, arguments); }
var i, j,
n = arguments.length-1,
f_callback = arguments[n],
l_arguments = [],
l_arg,
l_waiting_for = 1;
function f_callback_if_ready()
{ l_waiting_for--;
if (l_waiting_for === 0)
{ f_callback.apply({}, l_arguments); }
}
// Load the JSON file p_url and store the result at l_arguments[p_pos].
function f_load(p_url, p_pos)
{ var l_xhr = new XMLHttpRequest();
l_waiting_for++;
l_xhr.onload = function()
{ l_arguments[p_pos] = JSON.parse(this.response);
f_callback_if_ready();
};
l_xhr.onerror = function()
{ throw new Error(p_url + " not found"); };
l_xhr.overrideMimeType("application/json");
l_xhr.open("GET", p_url, true);
l_xhr.send();
}
for (i = 0, j = 0; i < n; i++)
{ l_arg = arguments[i];
if (l_arg === WK.WAIT_UNTIL_READY)
{ if (document.readyState === "loading")
{ l_waiting_for++;
document.addEventListener("DOMContentLoaded", f_callback_if_ready, false);
}
}
else if (l_arg === WK.WAIT_UNTIL_COMPLETE)
{ if (document.readyState !== "complete")
{ l_waiting_for++;
p_root.addEventListener("load", f_callback_if_ready);
}
}
else if (l_arg.constructor === String)
{ f_load(l_arg, j); j++; }
else
{ l_arguments[j] = l_arg; j++; }
}
f_callback_if_ready();
};
l_wk.WAIT_UNTIL_READY = {};
l_wk.WAIT_UNTIL_COMPLETE = {};
l_wk.prototype = l_wk;
l_wk.$new$ = f_new;
l_wk.pkg = f_pkg;
l_wk.mixin = f_mixin;
/*jslint forin: true*/
for (l_key in l_wk)
{ Object.defineProperty(l_wk, l_key, {writable: false, configurable: false}); }
Object.defineProperty
(p_root, "$wk$", { value: l_wk, writable: false, configurable: false });
////////////////////////////////////////////////////////////////////////////////
// End of $wk$ Library
////////////////////////////////////////////////////////////////////////////////
}(this, this.document, this.XMLHttpRequest));