diff --git a/.gitignore b/.gitignore index fd5ce69..d86b63d 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,5 @@ node_modules # msngr.js specific specRunner.html specRunner.min.html +crossWindowVerifier.html +crossWindowVerifier.min.html diff --git a/.travis.yml b/.travis.yml index 003243e..cea39f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,5 +3,4 @@ node_js: - "0.12" - "0.11" - "0.10" - - "iojs" before_install: npm install -g grunt-cli mocha-phantomjs phantomjs diff --git a/Gruntfile.js b/Gruntfile.js index a63f297..d598b59 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -17,10 +17,10 @@ module.exports = (function (grunt) { var paths = [ "src/main.js", "src/utils/*.js", - "src/builders/*.js", "src/store/*.js", + "src/objects/*.js", "src/messengers/*.js", - "src/actions/*.js", + "src/options/*.js", "src/module.exports.js", "!**/*.aspec.js", "!**/*.cspec.js", @@ -83,11 +83,11 @@ module.exports = (function (grunt) { var pkg = grunt.file.readJSON('package.json'); var main = fs.readFileSync("src/main.js", { encoding: "utf8" }); - var indexOfVersion = main.indexOf("version: "); - var indexOfNextComma = main.indexOf(",", indexOfVersion); + var indexOfVersion = main.indexOf("external.version = "); + var indexOfNextSemiColon = main.indexOf(";", indexOfVersion); var ified = main.substring(0, indexOfVersion); - ified = ified + "version: \"" + pkg.version + "\""; - ified = ified + main.substring(indexOfNextComma, main.length); + ified = ified + "external.version = \"" + pkg.version + "\""; + ified = ified + main.substring(indexOfNextSemiColon, main.length); fs.writeFileSync("src/main.js", ified, { encoding: "utf8" }); }); @@ -122,21 +122,33 @@ module.exports = (function (grunt) { }; var fs = require("fs"); var path = require("path"); - var tests = []; - var dirs = fs.readdirSync("./src/"); - for (var i = 0; i < dirs.length; ++i) { - if (fs.statSync("./src/" + dirs[i]).isDirectory()) { - var files = fs.readdirSync("./src/" + dirs[i]); - for (var j = 0; j < files.length; ++j) { - tests.push(path.join("./", "./src/", dirs[i], files[j])); + var tests = []; + var testPaths = ["./", "./src/", "./docs/"]; + + for (var k = 0; k < testPaths.length; ++k) { + var dirs = fs.readdirSync(testPaths[k]); + + for (var i = 0; i < dirs.length; ++i) { + if (fs.statSync(testPaths[k] + dirs[i]).isDirectory()) { + var files = fs.readdirSync(testPaths[k] + dirs[i]); + for (var j = 0; j < files.length; ++j) { + var p = path.join("./", testPaths[k], dirs[i], files[j]); + if (tests.indexOf(p) === -1) { + tests.push(p); + } + } + } else { + var p = path.join("./", testPaths[k], dirs[i]); + if (tests.indexOf(p) === -1) { + tests.push(p); + } } - } else { - tests.push(path.join("./", "./src/", dirs[i])); } } var scriptHtml = ""; + if (tests !== undefined && tests.length > 0) { var file = tests.shift(); while (tests.length > 0) { diff --git a/README.cspec.js b/README.cspec.js new file mode 100644 index 0000000..37dc37b --- /dev/null +++ b/README.cspec.js @@ -0,0 +1,62 @@ +if (typeof chai === "undefined" && typeof window === "undefined") { + var chai = require("chai"); +} + +if (typeof expect === "undefined") { + var expect = chai.expect; +} + +if (typeof msngr === "undefined" && typeof window === "undefined") { + var msngr = require("./msngr"); +} + +describe("./README.md", function () { + "use strict"; + + before(function () { + msngr.debug = true; + }); + + beforeEach(function () { + msngr.internal.reset(); + }); + + after(function () { + msngr.debug = false; + }); + + it("Example 1 of DOM binding", function (done) { + var userInput = document.createElement("input"); + userInput.setAttribute("name", "Username"); + userInput.value = "Kris"; + + var passwordInput = document.createElement("input"); + passwordInput.setAttribute("name", "Password"); + passwordInput.value = "hunter2"; + + var button = document.createElement("button"); + button.appendChild(document.createTextNode("Submit")); + + document.body.appendChild(userInput); + document.body.appendChild(passwordInput); + document.body.appendChild(button); + + msngr("User", "Save") + .bind("button", "click") + .option("dom", ["input"]) + .on(function (payload) { + expect(payload.Username).to.equal("Kris"); + expect(payload.Password).to.equal("hunter2"); + + document.body.removeChild(userInput); + document.body.removeChild(passwordInput); + document.body.removeChild(button); + + done(); + }); + + var me = document.createEvent("MouseEvents"); + me.initEvent("click", true, false); + button.dispatchEvent(me); + }); +}); diff --git a/README.md b/README.md index b20a12b..7d0cac0 100644 --- a/README.md +++ b/README.md @@ -1,168 +1,62 @@ # msngr.js -[![npm version](https://badge.fury.io/js/msngr.svg)](http://badge.fury.io/js/msngr) [![Bower version](https://badge.fury.io/bo/msngr.js.svg)](http://badge.fury.io/bo/msngr.js) [![Build Status](https://travis-ci.org/KrisSiegel/msngr.js.svg)](https://travis-ci.org/KrisSiegel/msngr.js/) [![Dependency Status](https://gemnasium.com/KrisSiegel/msngr.js.svg)](https://gemnasium.com/KrisSiegel/msngr.js) +[![npm version](https://badge.fury.io/js/msngr.svg)](http://badge.fury.io/js/msngr) [![Bower version](https://badge.fury.io/bo/msngr.js.svg)](http://badge.fury.io/bo/msngr.js) [![Build Status](https://travis-ci.org/KrisSiegel/msngr.js.svg)](https://travis-ci.org/KrisSiegel/msngr.js/) -msngr.js is a small library to manage messages between components with the goal of isolating business logic from a user interface or server framework. For example messages can be bound directly to DOM elements and activities can gather values allowing the handling of click events to know absolutely zero about the user interface itself. +## What is msngr.js? +msngr.js is a small library for facilitating communication between components through abstract messages within the same application be it server or client side. It also provides binding messages directly to DOM elements and even sending payloads between browser tabs / windows. -What does that mean, exactly? Read on. Want to just jump right in? Check out [this jsfiddle](http://jsfiddle.net/jnjaosfz/) page with msngr.js already included with a "Hello, World!" example. +The following example shows how to bind a message to a click event of a DOM element while gathering up the values in the related inputs for payload delivery. -### Quick note regarding upcoming 2.x release -The 2.0 branch of msngr is feature complete and includes a very different API than the current 1.x version. This was necessary to ensure consistency and an easier to use API. Additional testing, benchmarking and documentation need to be completed before 2.0 is fully ready for release at the end of May. If you'd like a preview take a look at the [2.0.0 branch](https://github.com/KrisSiegel/msngr.js/tree/2.0.0). - -## Quick Start -Installation can occur via bower or npm (alternatively just download msngr.js or msngr.min.js). - -```batch -bower install msngr.js -``` - -```batch -npm install msngr -``` - -Once installed simply include it into your project. -```html - -``` - -```javascript -var msngr = require("msngr"); -``` - -Now you're ready to start messaging! - -### Binding messages to DOM elements -One of the most handy things msngr.js can do is bind a message directly to a DOM element. Let's look at the following example. - -index.html -```html - -
- -
- +```HTML + + + ``` -userinterface.js ```javascript -msngr.bind("input[type=submit]", "click", "Profile", "Save"); +msngr("User", "Save") + .bind("button", "click") + .option("dom", ["input"]) + .on(function (payload) { + console.log(payload.Username); // Prints "Kris" + console.log(payload.Password); // Prints "hunter2" + }); ``` -business.js -```javascript -msngr.on("Profile", "Save", function () { - console.log("The profile has been saved!"); -}); -``` +## Getting msngr.js +If you want to use msngr.js on the server-side via npm simply install it via ```npm install msngr```. -As you can see ```msngr.bind()``` can accept typical selectors, an event and then a message which can be made up of a topic, category and dataType (omit the ones you do not want to use). +If you want to use it within a web browser then either install via ```bower install msngr``` or manually download msngr.js or msngr.min.js file(s) from this repository. -So this is cool, the UI and the frontend can be separated, right? Well how do you get those name and email values? Put DOM accessing code in business.js? Heck no! You can use the DOM activity to specify what values should be grabbed. +## Would you like to know more? +While msngr.js isn't very large the documentation has been split up for easy reading. -index.html -```html - -
- -
- -``` +[Full API](docs/api.md) - This is the full, exposed API that msngr makes available. This includes the methods that can be used (it does not cover internal methods or objects since those are subject to change) and examples for each. -userinterface.js -```javascript -msngr.bind("input[type=submit]", "click", { - topic: "Profile", - category: "Save", - dom: ["input[name=Name]", "input[name=Email]"]} -); -``` +[Messaging patterns](docs/messaging patterns.md) - Explains how to use the basic messaging features of msngr.js with some typical patterns. -business.js -```javascript -msngr.on("Profile", "Save", function (payload) { - console.log(payload.Name); - console.log(payload.Email); -}); -``` +[Web browser niceties](docs/web browser niceties.md) - This covers binding msngr.js to elements and events, unbinding them, how to gather up values from various types of elements and cross-window communication. -Now the payload will include an object with the values of each input specified. You can even simplify the selector even more to get the same data back like so: - -```javascript -msngr.bind("input[type=submit]", "click", { - topic: "Profile", - category: "Save", - dom: ["input"] -}); -``` - -Aggregated values are always stored with their name as the key. If the name doesn't exist then it uses an id. Should an id not exist then it defaults to tagname + count (so "input0", "input1"); - -### What about JavaScript that doesn't touch the DOM? -So msngr.js can also be used outside of situations that involve the DOM and be just as handy! A common example is abstracting away a specific library through the use of messages. An example is outlined below. - -elasticsearch.js -```javascript -msngr.on("Profile", "Save", "application/json", function (payload) { - // Save profile object into ElasticSearch -}); -``` - -business.js -```javascript -msngr.emit("Profile", "Save", "application/json", profile); -``` - -So in the example above we can save a Profile object without actually knowing who or what is going to save this. This let's us to, later on, use a configuration to allow alternative data stores such as a Mock store without changing the business.js file. So that may look like: - -mock.js -```javascript -msngr.on("Profile", "Save", "application/json", function (payload) { - // Save profile object into a mock data store -}); -``` - -### So what are activities anyway? -An activity is simply a registered method, executed synchronously, designed to be called before payload delivery should any properties within the message object match the registered method's property. - -For example the built-in DOM activity is registered with the 'dom' property. Therefore anytime someone emits a message with the 'dom' property the registered method is called before being delivered. This allows extending msngr.js in various ways without changing any method signatures. - -For instance if you want to create an activity that added two numbers together. - -```javascript -msngr.action("add", function (message, wrap) { - wrap.payload.result = wrap.payload.number1 + wrap.payload.number2; -}); - -msngr.on("Addition", function (payload) { - console.log(payload.result); // Outputs 7 -}); - -msngr.emit({ topic: "Addition", add: { } }, { number1: 5, number2: 2 }); -``` +[Extending and hacking](docs/extending and hacking.md) - Want to extend the capabilities of msngr.js? It's actually quite easy and this document covers it. Using msngr.js deep in a production system then suddenly find *something* that you need to change to avoid catastrophe? Hacking msngr.js is also covered for those times when you need *unorthodox* solutions :) -Note that what is typically supplied to the action's property in the message object is any related options the particular action may need. In this add example nothing was necessary so an empty object was passed in but for others, such as the dom action, you would pass in an array of html selectors. +[Contributing](docs/contributing.md) - Want to contributed to msngr.js? There are a couple of things you should know before you submit that pull request to better ensure it gets accepted :) -## API -Below are the methods exposed via the msngr object, their parameters and return values. +## Roadmap +The current release of msngr.js works in node.js for server-side messaging as well as the web browser. The web browser has some extra features like messaging between windows and binding to DOM elements but future features will receive more focus on the core of msngr.js with more node.js fun! -#### msngr.on(message, function) / msngr.on(topic, category, dataType, function) -This method accepts a JavaScript object with the properties "topic", "category" and "dataType" or simply fill in the first 3 parameters with those values. The function is a callback that's executed when a matching message is received and provides a payload parameter. +### What's Next? +Below is what's being worked on for future 2.x releases. -#### msngr.emit(message, payload) / msngr.emit(topic, category, dataType, payload) -This method sends a message by either providing a JavaScript object with the properties "topic", "category" and "dataType" or by simply entering each values as a parameter. The payload can be anything you want to send to a receiving callback. +* Web Socket Messaging - Easy web socket communication with messages between a server and client. -#### msngr.drop(message, handler) / msngr.drop(topic, category, dataType, handler) -This method removes a message from being executed. Specify the handler to remove or drop all handlers by specifying undefined. +* Feature detection - Support verifying what certain features can work in the environment they're in (e.g. older web browsers). When they cannot log a warning and disable the feature (warnings will be toggle-able). -#### msngr.bind(element, event, message) / msngr.bind(element, event, topic, category, dataType) -This method takes an HTML element (can be an element or selector), an event and a message then binds all 3 together. When the specified event occurs on the element the message will be emitted. Optionally add the 'dom' property to the message object to supply selectors you wish msngr would gather values from and return in the payload. +* Better browser support - Currently msngr.js should work in most current web browsers but some features may not work well in older versions of Internet Explorer and really old versions of Firefox. Additional testing and tweaking needs to be conducted for older browser support and to provide a baseline for what is or isn't supported. -#### msngr.unbind(element, event) / msngr.unbind(element, event) -This method stops an element's event from emitting a previously bound message. +* Benchmarking and optimization - Now that the majority of msngr.js's structure is written and fairly solidified we need to begin benchmarking along with profiling and optimization. Optimization had previously been ignored while the API was finalized and while msngr.js is really fast it needs to be *scientifically* fast. -#### msngr.action(property, function) -This method provides a way of extending msngr. The property is anything (except for 'topic', 'category' or 'dataType') that is supplied within a message object. If a message is emitted with a matching property the function is called with the message and an object that allows stopping the emitting entirely (via calling ```obj.preventDefault()```) or modifying the payload itself. +### What's Next Next? +At this point further planning will occur once more community feedback comes through. I have a few ideas involving integration into other messaging systems, some streaming ideas and a few optional extensions that provide message based APIs for other libraries but I'm hesitant to *only* go in one direction should other needs arise. -#### msngr.inaction(property) -This method removes the action being called on a property. +For questions, news, and whatever else that doesn't fit in GitHub issues you can follow me [@KrisSiegel](https://twitter.com/KrisSiegel) Copyright © 2014-2015 Kris Siegel diff --git a/crossWindowVerifier.html b/crossWindowVerifier.html new file mode 100644 index 0000000..416e7ef --- /dev/null +++ b/crossWindowVerifier.html @@ -0,0 +1,32 @@ + + + Cross-Window Verifier + + + + + + + + diff --git a/crossWindowVerifier.min.html b/crossWindowVerifier.min.html new file mode 100644 index 0000000..dbd9d61 --- /dev/null +++ b/crossWindowVerifier.min.html @@ -0,0 +1,32 @@ + + + Cross-Window Verifier + + + + + + + + diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..2a3881f --- /dev/null +++ b/docs/api.md @@ -0,0 +1,419 @@ +#msngr.js API +This document outlines the exposed msngr.js API. It does not cover anything internal that isn't expected to be used by typical developers. + +## ```msngr(topic, category, dataType)``` +The main msngr method takes in 3 strings to generate a message object from which all other actions are derived. + +```topic (required)``` - a string to signify the topic of the message. + +```category (optional)``` - a string to signify the category of the message. + +```dataType (optional)``` - a string to signify the dataType of the message. + +```returns``` a msngr object + +```javascript +var msg = msngr("MyTopic", "MyCategory", "MyDataType"); +``` + +## msngr object +The methods below are part of a ```msngr``` object returned from the ```msngr()``` method. + +### ```.emit(payload, callback)``` +Sends a payload to all registered handlers to the specified message. The optional callback is executed when all handlers are finished executing. + +```payload (optional)``` - the payload to send to a receiver. + +```callback (optional)``` - the callback to execute when all handlers finish executing. Provides a result object as a first parameter to the callback with an aggregation of all synchronously or asynchronously returned data. The ```payload``` parameter can be omitted while just a ```callback``` is passed into ```.emit()```. + +```javascript +var msg = msngr("MyTopic"); +msg.emit(function (result) { + console.log(result); +}); +``` + +### ```.persist(payload)``` +Registers a payload to be sent to all matching handlers regardless of when they are registered (now or in the future). + +```payload (optional)``` - the payload to send to a receiver. + +```javascript +var msg = msngr("MyTopic"); +msg.persist("My data"); +``` + +### ```.on(callback)``` +Registers a handler to handle all emissions of the specified message. The callback can take a result either synchronously or asynchronously to supply back to an emit callback. + +```callback (required)``` - registers a method to handle the previously supplied message. The value returned is then sent to the emit callback. If a return value needs to be asynchronous then the second parameter supplied to the callback can be used. + +```javascript +var msg = msngr("MyTopic"); +msg.on(function (payload, async) { + var done = async(); + done("Somevalue"); +}); +msg.emit(function (result) { + console.log(result); // Prints ["Somevalue"] +}); +``` + +### ```.once(callback)``` +The same as the ```.on(callback)``` except the handler is automatically unregistered after one execution. + +```callback (required)``` - registers a method to handle the previously supplied message. The value returned is then sent to the emit callback. If a return value needs to be asynchronous then the second parameter supplied to the callback can be used. + +```javascript +var msg = msngr("MyTopic"); +msg.once(function (payload, async) { + var done = async(); + done("Somevalue"); +}); +msg.emit(function (result) { + console.log(result); // Prints ["Somevalue"] +}); +``` + +### ```.option(key, value)``` +Options are extensions added into msngr and are executed on payloads before being sent to any registered handlers. Options are comprised of a key to enable a specific option and a value consisting of any necessary configuration needed to execute the option. + +```key (required)``` - a registered option key (e.g. "dom" is bundled into msngr). + +```value (required)``` - any configuration options that are needed for a specific option. + +```javascript +var msg = msngr("MyTopic"); +msg.option("dom", ["input[name=Name]"]); +msg.bind("button", "click"); +msg.on(function (payload) { + console.log(payload.Name); // returns the gathered input's value +}); +``` + +### ```.bind(element, event)``` +Binds an HTML element and event to the specified message. Payload consists of aggregated input values when the 'dom' option is specified with an array of selectors. + +```element (required)``` - an HTML node or selector. + +```event (required)``` - an event to bind a message to. + +```javascript +var msg = msngr("MyTopic"); +msg.option("dom", ["input[name=Name]"]); +msg.bind("button", "click"); +msg.on(function (payload) { + console.log(payload.Name); // returns the gathered input's value +}); +``` + +### ```.cease()``` +Ceases all payloads being persisted based on the specified message. + +```javascript +var msg = msngr("MyTopic"); +msg.persist("Cookies!!!"); +msg.cease(); // Gets rid of "Cookies!!!" :( +``` + +### ```.drop(handler)``` +Drops a specific handler that was registered via ```.on()```. + +```handler (required)``` - the handler to drop. + +```javascript +var msg = msngr("MyTopic"); +var myHandler = function (payload) { + console.log(payload); +}; +msg.on(myHandler); +msg.drop(myHandler); +``` + +### ```.unbind(element, event)``` +Unbinds the specified message from an element's event. + +```element (required)``` - an HTML node or selector. + +```event (required)``` - an event to unbind the message. + +```javascript +var msg = msngr("MyTopic"); +msg.bind("input[name=password]", "change"); +msg.unbind("input[name=password]", "change"); +``` + +### ```.dropAll()``` +Drops all handlers registered via ```.on()```. + +```javascript +var msg = msngr("MyTopic"); +msg.dropAll(); // Drops all handlers even outside of 'MyTopic'!!! +``` + +## Handy DOM utilities +In implementing the binding and dom option features of msngr some handy DOM utilities had to be created. Why not expose them? + +### ```isHtmlElement(obj)``` +Determines whether a passed in object is a node within the DOM or something else. + +```obj (required)``` - the object to test. + +```javascript +console.log(msngr.isHtmlElement(5)); // Outputs false +console.log(msngr.isHtmlElement(document.createElement("div"))); // Outputs true +``` + +### ```isNodeList(obj)``` +Determines whether a passed in object is a NodeList within the DOM or something else. + +```obj (required)``` - the object to test. + +```javascript +console.log(msngr.isNodeList("chickens")); // Outputs false +console.log(msngr.isNodeList(document.querySelectorAll("div"))); // Outputs true +``` + +### ```findElement(selector, root)``` +Finds a single element via selector. + +```selector (required)``` - a standard DOM selector to find an element. + +```root (optional)``` - a root position to begin querying. Defaults to document. Handy when dealing with ShadowDom. + +```javascript +var elm = msngr.findElement("div"); +``` + +### ```findElements(selector, root)``` +Finds multiple elements via selector. + +```selector (required)``` - a standard DOM selector to find an element. + +```root (optional)``` - a root position to begin querying. Defaults to document. Handy when dealing with ShadowDom. + +```javascript +var elms = msngr.findElements("div"); +``` + +### ```getDomPath(element)``` +Takes an element and determines the path to accessing it via the DOM and generates an appropriate selector. This is useful when binding against elements that have no unique characteristics. + +```element (required)``` - the element to generate a path for. + +```javascript +var elm = msngr.findElement("div"); +var path = msngr.getDomPath(elm); +var found = msngr.findElement(path); // Same object as elm +``` + +### ```querySelectorWithEq(selector, root)``` +The native querySelector does not support the :eq(n) notation that jQuery has popularized therefore it was implemented here :). + +```selector (required)``` - a standard DOM selector to find an element. + +```root (optional)``` - a root position to begin querying. Defaults to document. Handy when dealing with ShadowDom. + +```javascript +var elm = msngr.querySelectorWithEq("div:eq(1)"); +``` + +### ```querySelectorAllWithEq(selector, root)``` +The native querySelectorAll does not support the :eq(n) notation that jQuery has popularized therefore it was implemented here :). + +```selector (required)``` - a standard DOM selector to find an element. + +```root (optional)``` - a root position to begin querying. Defaults to document. Handy when dealing with ShadowDom. + +```javascript +var elms = msngr.querySelectorAllWithEq("div:eq(1) > input"); +``` + +## Miscellaneous utilities +There are multiple utility methods included in msngr. Some are used internally and some are exposed for external use by others. + +### ```msngr.extend(obj, target)``` +Extends either the msngr object or a specified target object. + +```obj (required)``` - The object used to extend a target. If it's a method it is executed and passed in the external and interface interfaces of msngr. + +```target (optional)``` - The target of the extend. If not specified it is assumed you are extending the msngr object. + +```javascript +msngr.extend({ + sayHello: function () { + return "Hello!"; + } +}); + +console.log(msngr.sayHello()); +``` + +### ```msngr.merge(input1, input2, ..., inputN)``` +Merges an n number of inputs together. Combines objects with other objects (the merging overwrites in order should conflict arrise), functions with objects, strings and strings and arrays. + +```inputn (required)``` - Specify as many parameters as necessary for merging. + +```javascript +var merged = msngr.merge({ val1: "test" }, { val2: "whatever!" }, { val2: "no!" }); +console.log(merged.val1); // Prints "test" +console.log(merged.val2); // Prints "no!" +``` + +### ```msngr.exist(obj)``` +Returns false if obj is undefined or null otherwise true. + +```javascript +console.log(msngr.exist(undefined)); // Outputs false +``` + +### ```msngr.exists(...)``` +Accepts n number of arguments. Returns false if all arguments are undefined or null otherwise true. + +```javascript +console.log(msngr.exists({ }, [], "whatever")); // Outputs true +``` + +### ```msngr.isString(obj)``` +Returns true if obj is a string otherwise false. + +```javascript +console.log(msngr.isString("something")); // Outputs true +``` + +### ```msngr.areStrings(...)``` +Accepts n number of arguments. Returns true if all arguments are strings otherwise false. + +```javascript +console.log(msngr.areStrings("something", "another", "")); // Outputs true +``` + +### ```msngr.isObject(obj)``` +Returns true if obj is an object otherwise false. + +```javascript +console.log(msngr.isObject({ })); // Outputs true +``` + +### ```msngr.areObjects(...)``` +Accepts n number of arguments. Returns true if all arguments are objects otherwise false. + +```javascript +console.log(msngr.areObjects({ }, { })); // Outputs true +``` + +### ```msngr.isNumber(obj)``` +Returns true if obj is a number otherwise false. + +```javascript +console.log(msngr.isNumber(5)); // Outputs true +``` + +### ```msngr.areNumbers(...)``` +Accepts n number of arguments. Returns true if all arguments are numbers otherwise false. + +```javascript +console.log(msngr.areNumbers(1, 5, 7, 9, 42)); // Outputs true +``` + +### ```msngr.isArray(obj)``` +Returns true if obj is an array otherwise false. + +```javascript +console.log(msngr.isArray([5, 10, 15])); // Outputs true +``` + +### ```msngr.areArrays(...)``` +Accepts n number of arguments. Returns true if all arguments are arrays otherwise false. + +```javascript +console.log(msngr.areArrays([1, 2], [5], [19, 37, 42])); // Outputs true +``` + +### ```msngr.isFunction(obj)``` +Returns true if obj is a function otherwise false. + +```javascript +console.log(msngr.isFunction(function () { })); // Outputs true +``` + +### ```msngr.areFunctions(...)``` +Accepts n number of arguments. Returns true if all arguments are functions otherwise false. + +```javascript +console.log(msngr.areFunctions(function () { }, function () { })); // Outputs true +``` + +### ```msngr.isEmptyString(obj)``` +Returns true if obj is an empty string otherwise false. + +```javascript +console.log(msngr.isEmptyString("")); // Outputs true +``` + +### ```msngr.areEmptyStrings(...)``` +Accepts n number of arguments. Returns true if all arguments are empty strings otherwise false. + +```javascript +console.log(msngr.areEmptyStrings("", " ", " ")); // Outputs true +``` + +### ```msngr.isArguments(obj)``` +Returns true if obj is an arguments object otherwise false. + +```javascript +console.log(msngr.isArguments(arguments)); // Outputs true +``` + +### ```msngr.areArguments(...)``` +Accepts n number of arguments. Returns true if all arguments are an arguments object otherwise false. + +```javascript +console.log(msngr.areArguments(arguments, [])); // Outputs false +``` + +### ```msngr.id()``` +Returns a random UUID. + +```javascript +console.log(msngr.id()); // Outputs UUID +``` + +### ```msngr.now(noDuplicates)``` +Uses feature detection to find the highest time resolution available in its running environment (performance.now(), process.hrtime() or getTime()). Once the best feature is cached the method will always return the time in milliseconds. + +```noDuplicates (optional)``` - only useful when the best available time resolution is getTime() (which kinda sucks); this flag, when set to ```true```, will force it to return a unique value even if the resolution isn't good enough to do so (hence the result will be slightly inaccurate but still useful). + +```javascript +var start = msngr.now(); +// Some computation +var end = msngr.now(); +console.log("Started at {0} and ended at {1}.".replace("{0}", start).replace("{1}", end)); +``` + +### ```msngr.removeFromArray(arr, value)``` +Removes a value from an array. Optimized for better performance in larger arrays as it will reorder the item to be removed (thus not preserving order) to increase performance. + +```arr (required)``` - the array to remove a value from. + +```value (required)``` - the value from remove from the previously specified array. + +```javascript +var myArray = [5, 17, 42, 97]; +console.log(myArray); // Outputs [5, 17, 42, 97] +msngr.removeFromArray(myArray, 42); +console.log(myArray); // Outputs [5, 17, 97]; +``` + +### ```msngr.argumentsToArray(args)``` +Converts an arguments object to an array object. + +```args (required)``` - the arguments object to convert to an array. + +```javascript +var myFunction = function () { + console.log(msngr.argumentsToArray(arguments)); +}; + +myFunction(15, 27); +``` diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 0000000..7343745 --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,24 @@ +# Contributing +To contribute to msngr.js there are a few rules I expect every developer to follow to ensure the best quality library possible. There are no "zero-tolerance" rules because, well, those are dumb in almost every single case in existence (I'm sure someone will find a reason where my rules should be ignored). + +The goal of this short document isn't to be annoying but to ensure quality. I will work with anyone who wishes to submit a pull request. + +## Checklists +The checklists should serve as a guide for submitting your pull request. + +### Bug fixing +When fixing a bug these apply: + +- A unit test *must* be included that cover the bug to ensure proper regression testing during future updates. +- If an API change is required to fix a bug the API change cannot break current compatibility. Should it break compatibility then a discussion should happen before submission of the fix. + +### Feature changes and additions +The following should be done when changing or adding features: + +- Ensure proper discussion is done within GitHub issues to ensure it will be accepted (naturally forks are not a problem; if it's not desired to put into msngr.js's main repository then feel free to keep the fork!). +- All API compatibility breaks must have a new major version number. +- All documentation [and accompanying unit tests] must be updated to comply with the new changes. If there are additions then the documentation needs to be amended along with unit tests to cover *all* documentation examples (see existing unit tests for each current document). +- All unit tests must pass when run against node, phantomjs, Internet Explorer 10+, Firefox and Chrome browsers in their expanded and minified versions. + +## Have fun! +Writing msngr.js has been a treat for me as it personally solves multiple issues for me. I'm not sure if it will be the same for anyone else or not but I hope others will find it useful and if so will help me in continuing to make it better! diff --git a/docs/extending and hacking.md b/docs/extending and hacking.md new file mode 100644 index 0000000..06cfd92 --- /dev/null +++ b/docs/extending and hacking.md @@ -0,0 +1,75 @@ +# Extending and hacking +A big part of my development background has been dealing with other people's libraries. Sometimes they're great and sometimes they're not. Regardless of how great (or not great) they are I've been in multiple situations where I need to make a critical change and it's difficult or impossible to do without modifying the original source (which is sometimes hosted on a CDN that can't change). + +With this in mind I tried to make msngr.js approachable to extend or hack. Hopefully I did a good enough job :) + +## Two ways of extending / enhancing +There are two ways of extending msngr.js depending on what you want to do and they both use the same msngr.extend() method. + +### Extending top level methods of msngr.js +The msngr.extend() method allows you to specify an object to mix with msngr. This is pretty straight forward and a basic way to get new methods into the top level of the msngr object. + +```javascript +msngr.extend({ + sayHello: function () { + console.log("Hello, msngr.js!"); + } +}); + +msngr.sayHello(); // Prints "Hello, msngr.js!" +``` + +### Adding options +You can also supply a method which is provided the external and internal interfaces of msngr as arguments then takes the result and mixes it with the msngr object. This is how you can add additional options to msngr to use on a message object. Note that this is pretty raw as it exposes you to the internal interface of msngr. + +```javascript +msngr.extend(function (external, internal) { + internal.options["my option"] = function (message, payload, options, async) { + var config = options["my option"]; + var done = async(); + // Do something here + done("My Result"); + }; + + return { }; // Optionally return an object that gets mixed with the msngr object +}); + +var msg = msngr("MyTopic"); +msg.option("my option", { configurationValue: true }); +msg.on(function (payload) { + console.log(payload); // Prints "My Result"; +}); +msg.emit(); +``` + +So first we passed in a method to msngr.extend(). This automatically gives us access to two parameters: ```external``` and ```internal```. + +```external``` provides access to all top level msngr methods without requiring the name 'msngr' should it later need to be changed due to the environment (such as multiple versions of msngr). + +```internal``` is a raw object with a set of objects, methods, properties and other items that are internally accessible to all msngr components. Here we add our new option method to an options object where, upon each execution, we receive a message, the payload, an optional configuration object (which includes configurations for *all* options to be executed for this message, not just your option) and an async method that allows a return value to be returned asynchronously. + +So what happens when your option returns a value? This value is taken and merged with the payload that is already being sent to the handler. So take caution that you're not doing anything funny with the payload. + +## Okay so what about 'hacking'? +So if you look at the ways of extending msngr.js you will notice that you can access the internal interface really easily. This lets you do *anything* to msngr.js. + +So let's say you want to change the memory indexer that msngr uses to index messages by replacing it. You could change code and submit a pull request but let's say you find an issue you need to fix NOW and you're in an environment where msngr is already approved and you can't change its code. What do you do!? + +It's actually kinda easy :) + + +```javascript +msngr.extend(function (external, internal) { + internal.objects.memory = function () { + return { + // API goes here + }; + }; +}); +``` + +So the example isn't filled out but you just replaced the memory indexer with an empty JavaScript object. Obviously this will break msngr.js pretty badly but you could now write your own indexer and drop it in here. + +You can do this with many other things in the internal interface, too. There is a message object that handles all messaging you could modify, extend or even replace. + +Enjoy! diff --git a/docs/messaging patterns.md b/docs/messaging patterns.md new file mode 100644 index 0000000..575a996 --- /dev/null +++ b/docs/messaging patterns.md @@ -0,0 +1,73 @@ +# Messaging patterns +So at this point you've hopefully come to the *right* side of thinking that messaging is the best pattern (**S**uperior **A**bstraction **L**ayer comes to mind) when developing software. No? Okay well that's fine since there is no single way of writing software that is better than all others but let's talk some messaging patterns you can use with msngr.js in your server or client code. + +## Separation of concerns +Some abstraction strategies lean to the 'black boxing' approach in which messaging can be quite handy. So let's look at an example of using messaging to save a user's preferences. + +```javascript +// BusinessLogic.js +msngr("Preferences", "Save", "application/json") + .on(function(preferences) { + // Save these + console.log(preferences); + }); +``` +```javascript +// RestLayer.js +msngr("Preferences", "Save", "application/json") + .emit(request.params.preferences, function () { + console.log("Saved"); + }); +``` + +In the example above we're saying when we receive a message with a topic of "Preferences", a category of "Save" and a dataType of "application/json" that we should act on it and "save" them (or in this case dump to console). The restful layer sends the preferences and once the on is finished executing it will execute the emit's callback letting it know it's done. + +So a simple way of separating things. One can envision creating multiple versions of 'BusinessLogic.js' where one saves to a local storage mechanism (memory perhaps?), another to MySQL, etc. This allows you to swap out backends depending on environment. + +But this is too simple; msngr is *way* cooler than this! + +## A better separation +Let's take a look at a more complex example. + +```javascript +// Server.js +// Boilerplate server code here +msngr("Server", "Ready").persist(); +``` +```javascript +// RestfulEndpoints.js +msngr("Server", "Ready").on(function () { + // Server is ready; setup restful endpoints + router.get("/Users/", function (req, res) { + msngr("Users", "List").emit(function (result) { + res.json(result); + }); + }); +}); +``` +```javascript +// MySQLBackend.js +msngr("Server", "Ready").on(function () { + // Server is ready; setup backend message handlers + msngr("Users", "List").on(function (payload, async) { + var done = async(); + // async call to MySQL for relevant data + done(results); + }); +}); +``` +```javascript +// MemoryBackend.js +msngr("Server", "Ready").on(function () { + var users = { }; + // Server is ready; setup backend message handlers + msngr("Users", "List").on(function (payload) { + return users; + }); +}); +``` +In the above example there are a several things to take note of. First the usage of ```persist()``` can persist a specific payload or just a message itself. By using persist in this way it doesn't matter what order the scripts run because they will all get the ready event from the server to let them setup themselves. + +After that, using an environment configuration, you can choose to load ```MySQLBackend.js``` or ```MemoryBackend.js```. Both use the exact same messaging handlers, one works asynchronously and the other synchronously but the receiving end (the callback specified in emit) gets the data in the same way regardless. + +Neat, right? diff --git a/docs/web browser niceties.md b/docs/web browser niceties.md new file mode 100644 index 0000000..4822157 --- /dev/null +++ b/docs/web browser niceties.md @@ -0,0 +1,70 @@ +# Web browser niceties +While msngr.js has plenty of generic capability that can run under node.js and a web browser it also includes a few nice features that only work in web browsers. + +## Cross window communication +Have you ever used a web browser where you had multiple instances of a web application open? There are many use-cases where this may happen but what do you do if a change happens in window 1 then you look at window 2? You could setup a one-off call using postMessage or make your web app revolve around the data inside of localStorage and listen to its events. But that can be annoying especially if you didn't realize your users even want this until it's too late to make such structural changes. + +With msngr.js you can use the same interfaces already available for messaging and send them across windows! Let's just jump into an example. + +```javascript +// Window 1 +var msg = msngr("Notifications", "Count"); +msg.option("cross-window"); +msg.on(function (payload) { + var notificationElm = document.querySelector("#NotificationCount"); + notificationElm.innerHTML = payload; +}); + +msg.emit(5); +``` + +```javascript +// Window 2 +var msg = msngr("Notifications", "Count"); +msg.option("cross-window"); +msg.on(function (payload) { + var notificationElm = document.querySelector("#NotificationCount"); + notificationElm.innerHTML = payload; +}); +``` + +Both windows use the same code and message to update a notification count. Window 1 decides its time to update the notification count to 5. Both windows will receive this value within these handlers. Neat, right? + +## Binding messages to DOM elements +We can take a message and bind it directly to an element's event letting you keep your code separate from even dealing with an object's eventing directly. + +```html + + +``` + +```javascript +var msg = msngr("User", "Save"); +msg.bind("button[name=save]", "click"); +msg.on(function (payload) { + // The user clicked the payload button +}); +``` + +So this lets you take a message, a message's handler and bind it directly to an element's event. This is helpful in keeping your code separate and you can even unit test by directly emitting to the same handler! + +But you still have to get that username so your code still has to hit the dom thus ruining your true code separation of presentation and business logic. Right? + +## The DOM option +Adding options to your messages allow you to take advantage of additional extensions within msngr.js. The 'dom' option allows the specifying of selectors which, upon being emitted, are used to gather and aggrevate values. Take a look at this example: + +```html + + +``` + +```javascript +var msg = msngr("User", "Save"); +msg.bind("button[name=save]", "click"); +msg.option("dom", ["input[name=username]"]); +msg.on(function (payload) { + console.log(payload.username); // The value in the username field +}); +``` + +The 'dom' option takes an array of selectors which then grab a value and puts it in the payload either by the name of the element or it generates a property itself with a tag name. Pretty handy, right? Now you can write code that handles a click event on a form and have *ZERO* DOM code in the actual handler. diff --git a/msngr.js b/msngr.js index 786203d..2ac2534 100644 --- a/msngr.js +++ b/msngr.js @@ -1,148 +1,298 @@ +/* + main.js + The main entry point for msngr.js. Covers internal and external interface generation, + versioning (for programmatic access) and the core extend method. +*/ var msngr = msngr || (function () { "use strict"; - return { - version: "1.1.0", - extend: function (obj, target) { - target = (target || msngr); - if (Object.prototype.toString.call(obj) === "[object Object]") { - for (var key in obj) { - if (obj.hasOwnProperty(key)) { - if (Object.prototype.toString.call(obj[key]) === "[object Object]") { - if (target[key] === undefined) { - target[key] = { }; - } - target[key] = msngr.extend(obj[key], target[key]); - } else if (Object.prototype.toString.call(obj[key]) === "[object Array]") { - target[key] = (target[key] || []).concat(obj[key]); - } else { - target[key] = obj[key]; + // Defaults for some internal functions + var internal = { + warnings: true + }; + + // The main method for msngr uses the message object + var external = function (topic, category, dataType) { + return internal.objects.message(topic, category, dataType); + }; + + external.version = "2.0.0"; + + // Merge two inputs into one + var twoMerge = function (input1, input2) { + if (input1 === undefined || input1 === null) { + return input2; + } + + if (input2 === undefined || input2 === null) { + return input1; + } + + var result; + var type1 = Object.prototype.toString.call(input1); + var type2 = Object.prototype.toString.call(input2); + + if (type1 === "[object Object]" && type2 === "[object Object]") { + // Object merging time! + result = { }; + // Copy input1 into result + for (var key in input1) { + if (input1.hasOwnProperty(key)) { + result[key] = input1[key]; + } + } + for (var key in input2) { + if (input2.hasOwnProperty(key)) { + if (Object.prototype.toString.call(input2[key]) === "[object Object]") { + if (result[key] === undefined) { + result[key] = { }; } + result[key] = external.merge(input1[key], input2[key]); + } else if (Object.prototype.toString.call(input1[key]) === "[object Array]" && Object.prototype.toString.call(input2[key]) === "[object Array]") { + result[key] = (input1[key] || []).concat(input2[key]); + } else { + result[key] = input2[key]; + } + } + } + return result; + } + + if (type1 === "[object String]" && type2 === "[object String]") { + result = input1 + input2; + return result; + } + + if (type1 === "[object Array]" && type2 === "[object Array]") { + result = input1.concat(input2); + return result; + } + + if (type1 === "[object Function]" && type2 === "[object Function]") { + return (function (i1, i2, args) { + return function () { + return external.merge(i1.apply(this, args), i2.apply(this, args)); + }; + }(input1, input2, arguments)); + } + + var similarObjectTypes = ["[object Function]", "[object Object]"]; + + if (similarObjectTypes.indexOf(type1) !== -1 && similarObjectTypes.indexOf(type2) !== -1) { + var method = (type1 === "[object Function]") ? input1 : input2; + var props = (type1 === "[object Object]") ? input1 : input2; + + if (method !== undefined && props !== undefined) { + for (var key in props) { + if (props.hasOwnProperty(key)) { + method[key] = props[key]; } } } - return target; + result = method; + return result; + } + + return result; + }; + + external.extend = function (obj, target) { + target = (target || external); + + if (Object.prototype.toString.call(obj) === "[object Function]") { + obj = obj.apply(this, [external, internal]); + } + + target = external.merge(obj, target); + + return target; + }; + + external.merge = function () { + var result; + if (arguments.length > 0) { + for (var i = 0; i < arguments.length; ++i) { + result = twoMerge(result, arguments[i]); + } } + + return result; }; + + // Create a debug property to allow explicit exposure to the internal object structure. + // This should only be used during unit test runs and debugging. + Object.defineProperty(external, "debug", { + set: function (value) { + if (value === true) { + external.internal = internal; + } else if (value === false) { + delete external.internal; + } + }, + get: function () { + return (external.internal !== undefined) + } + }); + + // This governs warning messages that some methods may spit into the console when warranted (du'h). + Object.defineProperty(external, "warnings", { + set: function (value) { + internal.warnings = value; + }, + get: function () { + return internal.warnings; + } + }); + + return external; }()); -msngr.extend((function () { +msngr.extend((function (external, internal) { "use strict"; return { - utils: { - argumentsToArray: function (args) { - if (msngr.utils.isArray(args)) { - return args; - } - + argumentsToArray: function (args) { + if (external.isArray(args)) { + return args; + } + if (external.isArguments(args)) { return Array.prototype.slice.call(args, 0); } + return [args]; } }; -}())); +})); -msngr.extend((function () { +msngr.extend((function (external, internal) { "use strict"; return { - utils: { - isHtmlElement: function (obj) { - var t = this.getType(obj); - return (t.indexOf("[object HTML") === 0) || (t.indexOf("[object global]") === 0); - }, - isNodeList: function (obj) { - return (this.getType(obj) === "[object NodeList]"); - }, - findElement: function (element, root) { - var elms = msngr.utils.findElements(element); - if (elms !== undefined && elms.length > 0) { - return elms[0]; - } + isHtmlElement: function (obj) { + var t = this.getType(obj); + return (t.indexOf("[object HTML") === 0) || (t.indexOf("[object global]") === 0); + }, + isNodeList: function (obj) { + return (this.getType(obj) === "[object NodeList]"); + }, + findElement: function (element, root) { + var elms = external.findElements(element, root); + if (elms !== undefined && elms.length > 0) { + return elms[0]; + } - return elms; - }, - findElements: function (selector, root) { - var elm; - if (msngr.utils.isHtmlElement(selector)) { - elm = selector; - } + return elms; + }, + findElements: function (selector, root) { + var elm; + if (external.isHtmlElement(selector)) { + elm = selector; + } - if (elm === undefined && msngr.utils.isString(selector)) { - var doc = root || document; - var result = doc.querySelectorAll(selector); - if (result !== null) { - elm = result; - } + if (elm === undefined && external.isString(selector)) { + var doc = root || document; + var result = doc.querySelectorAll(selector); + if (result !== null) { + elm = result; } + } - return elm; - }, - getDomPath: function (element) { - var node = msngr.utils.isHtmlElement(element) ? element : undefined; - if (node === undefined) { + return elm; + }, + getDomPath: function (element) { + var node = external.isHtmlElement(element) ? element : undefined; + if (node === undefined) { + return undefined; + } + + if (node.id === undefined) { + node.id = external.id(); + } + + return "#" + node.id; + }, + querySelectorAllWithEq: function (selector, root) { + if (selector === undefined) { + return null; + } + var doc = root || document; + var queue = []; + var process = function (input) { + if (input.indexOf(":eq(") === -1) { return undefined; } - if (node.id === undefined) { - node.id = msngr.utils.id(); + var eqlLoc = input.indexOf(":eq("); + var sel = input.substring(0, eqlLoc); + var ind = input.substring((eqlLoc + 4), input.indexOf(")", eqlLoc)); + selector = input.substring(input.indexOf(")", eqlLoc) + 1, input.length); + + if (sel.charAt(0) === ">") { + sel = sel.substring(1, sel.length); } - return "#" + node.id; - }, - querySelectorAllWithEq: function (selector, root) { - if (selector === undefined) { - return null; + if (selector.charAt(0) === ">") { + selector = selector.substring(1, selector.length); } - var doc = root || document; - var queue = []; - var process = function (input) { - if (input.indexOf(":eq(") === -1) { - return undefined; - } - var eqlLoc = input.indexOf(":eq("); - var sel = input.substring(0, eqlLoc); - var ind = input.substring((eqlLoc + 4), input.indexOf(")", eqlLoc)); - selector = input.substring(input.indexOf(")", eqlLoc) + 1, input.length); + queue.push({ + selector: sel, + index: parseInt(ind, 10) + }); + } + while (selector.indexOf(":eq") !== -1) { + process(selector); + } - if (sel.charAt(0) === ">") { - sel = sel.substring(1, sel.length); - } + var result; + while (queue.length > 0) { + var item = queue.shift(); + result = (result || doc).querySelectorAll(item.selector)[item.index]; + } - if (selector.charAt(0) === ">") { - selector = selector.substring(1, selector.length); - } + if (selector.trim().length > 0) { + return (result || doc).querySelectorAll(selector); + } + return [result]; + }, + querySelectorWithEq: function (selector, root) { + return external.querySelectorAllWithEq(selector, root)[0]; + } + }; +})); - queue.push({ - selector: sel, - index: parseInt(ind, 10) - }); - } - while (selector.indexOf(":eq") !== -1) { - process(selector); - } +msngr.extend((function (external, internal) { + "use strict"; - var result; - while (queue.length > 0) { - var item = queue.shift(); - result = (result || doc).querySelectorAll(item.selector)[item.index]; - } + internal.InvalidParametersException = function (str) { + return { + name: "InvalidParametersException", + severity: "unrecoverable", + message: ("Invalid parameters supplied to the {method} method".replace("{method}", str)) + }; + }; - if (selector.trim().length > 0) { - return (result || doc).querySelectorAll(selector); - } - return [result]; - }, - querySelectorWithEq: function (selector) { - return msngr.utils.querySelectorAllWithEq(selector)[0]; - } - } + internal.ReservedKeywordsException = function (keyword) { + return { + name: "ReservedKeywordsException", + severity: "unrecoverable", + message: ("Reserved keyword {keyword} supplied as action.".replace("{keyword}", keyword)) + }; }; -}())); -msngr.extend((function () { + internal.MangledException = function (variable, method) { + return { + name: "MangledException", + severity: "unrecoverable", + message: ("The {variable} was unexpectedly mangled in {method}.".replace("{variable}", variable).replace("{method}", method)) + }; + }; + + // This is an internal extension; do not export explicitly. + return { }; +})); + +msngr.extend((function (external, internal) { "use strict"; var nowPerformance = function () { @@ -162,791 +312,873 @@ msngr.extend((function () { var lastNow = undefined; return { - utils: { - id: function () { - var d = msngr.utils.now(); - var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { - var r = (d + Math.random()*16)%16 | 0; - d = Math.floor(d/16); - return (c=='x' ? r : (r&0x3|0x8)).toString(16); - }); - return uuid; - }, - now: function (noDuplicate) { - if (nowExec === undefined) { - if (typeof performance !== "undefined") { - nowExec = nowPerformance; - nowExecDebugLabel = "performance"; - } else if (typeof process !== "undefined") { - nowExec = nowNode; - nowExecDebugLabel = "node"; - } else { - nowExec = nowLegacy; - nowExecDebugLabel = "legacy"; - } - } - var now = nowExec(); - if (noDuplicate === true && lastNow === now) { - return msngr.utils.now(noDuplicate); + id: function () { + var d = external.now(); + var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = (d + Math.random()*16)%16 | 0; + d = Math.floor(d/16); + return (c=='x' ? r : (r&0x3|0x8)).toString(16); + }); + return uuid; + }, + now: function (noDuplicate) { + if (nowExec === undefined) { + if (typeof performance !== "undefined") { + nowExec = nowPerformance; + nowExecDebugLabel = "performance"; + } else if (typeof process !== "undefined") { + nowExec = nowNode; + nowExecDebugLabel = "node"; + } else { + nowExec = nowLegacy; + nowExecDebugLabel = "legacy"; } - lastNow = now; - return now; } + var now = nowExec(); + if (noDuplicate === true && lastNow === now) { + return external.now(noDuplicate); + } + lastNow = now; + return now; + }, + removeFromArray: function (arr, value) { + var inx = arr.indexOf(value); + var endIndex = arr.length - 1; + if (inx !== endIndex) { + var temp = arr[endIndex]; + arr[endIndex] = arr[inx]; + arr[inx] = temp; + } + arr.pop(); } }; -}())); +})); -msngr.extend((function () { +msngr.extend((function (external, internal) { "use strict"; - return { - utils: { - getType: function (obj) { - if (!msngr.utils.exist(obj)) { - return "" + obj; - } - return Object.prototype.toString.call(obj); - }, - isArguments: function (obj) { - return (msngr.utils.getType(obj) === "[object Arguments]"); - }, - areArguments: function () { - return msngr.utils.reiterativeValidation(msngr.utils.isArguments, msngr.utils.argumentsToArray(arguments)); - }, - isNullOrUndefined: function (obj) { - return (obj === undefined || obj === null); - }, - exist: function (obj) { - return !msngr.utils.isNullOrUndefined(obj); - }, - exists: function () { - return msngr.utils.reiterativeValidation(msngr.utils.exist, msngr.utils.argumentsToArray(arguments)); - }, - isString: function (str) { - return (msngr.utils.getType(str) === "[object String]"); - }, - areStrings: function () { - return msngr.utils.reiterativeValidation(msngr.utils.isString, msngr.utils.argumentsToArray(arguments)); - }, - isDate: function (obj) { - return (msngr.utils.getType(obj) === "[object Date]"); - }, - areDates: function () { - return msngr.utils.reiterativeValidation(msngr.utils.isDate, msngr.utils.argumentsToArray(arguments)); - }, - isArray: function (obj) { - return (msngr.utils.getType(obj) === "[object Array]"); - }, - areArrays: function () { - return msngr.utils.reiterativeValidation(msngr.utils.isArray, msngr.utils.argumentsToArray(arguments)); - }, - isNumber: function (obj) { - return (msngr.utils.getType(obj) === "[object Number]"); - }, - areNumbers: function () { - return msngr.utils.reiterativeValidation(msngr.utils.isNumber, msngr.utils.argumentsToArray(arguments)); - }, - isObject: function (obj) { - return (msngr.utils.getType(obj) === "[object Object]"); - }, - areObjects: function () { - return msngr.utils.reiterativeValidation(msngr.utils.isObject, msngr.utils.argumentsToArray(arguments)); - }, - isFunction: function (func) { - return (msngr.utils.getType(func) === "[object Function]"); - }, - areFunctions: function () { - return msngr.utils.reiterativeValidation(msngr.utils.isFunction, msngr.utils.argumentsToArray(arguments)); - }, - isEmptyString: function (str) { - var isStr = msngr.utils.isString(str); - if (str === undefined || str === null || (isStr && str.toString().trim().length === 0)) { - return true; - } - return false; - }, - areEmptyStrings: function () { - return msngr.utils.reiterativeValidation(msngr.utils.isEmptyString, msngr.utils.argumentsToArray(arguments)); - }, - hasWildCard: function (str) { - return (str.indexOf("*") !== -1); - }, - reiterativeValidation: function (validationMethod, inputs) { - var result = false; - if (msngr.utils.exist(validationMethod) && msngr.utils.exist(inputs)) { - if (!msngr.utils.isArray(inputs)) { - inputs = [inputs]; - } - for (var i = 0; i < inputs.length; ++i) { - result = validationMethod.apply(this, [inputs[i]]); - if (result === false) { - break; - } - } + internal.reiterativeValidation = function (validationMethod, inputs) { + var result = false; + if (external.exist(validationMethod) && external.exist(inputs)) { + if (!external.isArray(inputs)) { + inputs = [inputs]; + } + for (var i = 0; i < inputs.length; ++i) { + result = validationMethod.apply(this, [inputs[i]]); + if (result === false) { + break; } - return result; } - } + } + return result; }; -}())); - -/* - ./src/builders/message.js -*/ -msngr.extend((function () { - "use strict"; return { - builders: { - msg: function () { - return (function () { - var message = { }; - var props = ["topic", "category", "dataType", "payload"].concat(msngr.getAvailableActions()); - - var obj = { - build: function () { - return message; - } - }; - - for (var i = 0; i < props.length; ++i) { - (function (key) { - obj[key] = function (input) { - message[key] = input; - return obj; - }; - }(props[i])); - } - - return obj; - }()); + getType: function (obj) { + if (!external.exist(obj)) { + return "" + obj; } + return Object.prototype.toString.call(obj); + }, + isArguments: function (obj) { + return (external.getType(obj) === "[object Arguments]"); + }, + areArguments: function () { + return internal.reiterativeValidation(external.isArguments, external.argumentsToArray(arguments)); + }, + isNullOrUndefined: function (obj) { + return (obj === undefined || obj === null); + }, + exist: function (obj) { + return !external.isNullOrUndefined(obj); + }, + exists: function () { + return internal.reiterativeValidation(external.exist, external.argumentsToArray(arguments)); + }, + isString: function (str) { + return (external.getType(str) === "[object String]"); + }, + areStrings: function () { + return internal.reiterativeValidation(external.isString, external.argumentsToArray(arguments)); + }, + isDate: function (obj) { + return (external.getType(obj) === "[object Date]"); + }, + areDates: function () { + return internal.reiterativeValidation(external.isDate, external.argumentsToArray(arguments)); + }, + isArray: function (obj) { + return (external.getType(obj) === "[object Array]"); + }, + areArrays: function () { + return internal.reiterativeValidation(external.isArray, external.argumentsToArray(arguments)); + }, + isNumber: function (obj) { + return (external.getType(obj) === "[object Number]"); + }, + areNumbers: function () { + return internal.reiterativeValidation(external.isNumber, external.argumentsToArray(arguments)); + }, + isObject: function (obj) { + return (external.getType(obj) === "[object Object]"); + }, + areObjects: function () { + return internal.reiterativeValidation(external.isObject, external.argumentsToArray(arguments)); + }, + isFunction: function (func) { + return (external.getType(func) === "[object Function]"); + }, + areFunctions: function () { + return internal.reiterativeValidation(external.isFunction, external.argumentsToArray(arguments)); + }, + isEmptyString: function (str) { + var isStr = external.isString(str); + if (str === undefined || str === null || (isStr && str.toString().trim().length === 0)) { + return true; + } + return false; + }, + areEmptyStrings: function () { + return internal.reiterativeValidation(external.isEmptyString, external.argumentsToArray(arguments)); + }, + hasWildCard: function (str) { + return (str.indexOf("*") !== -1); } }; -}())); - -msngr.extend((function () { - "use strict"; - - // Index for id to message objects - var id_to_message = { }; - - // Direct index (no partials) for message - var direct_index = { - topic_to_id: { }, - topic_cat_to_id: { }, - topic_type_to_id: { }, - topic_cat_type_to_id: { } - }; - - // Message index count - var index_count = 0; - - var deleteValueFromArray = function (arr, value) { - var inx = arr.indexOf(value); - var endIndex = arr.length - 1; - if (inx !== endIndex) { - var temp = arr[endIndex]; - arr[endIndex] = arr[inx]; - arr[inx] = temp; - } - arr.pop(); - }; - - return { - store: { - index: function (message) { - if (msngr.utils.exist(message) && msngr.utils.exist(message.topic)) { - var uuid = msngr.utils.id(); - id_to_message[uuid] = message; - - if (direct_index.topic_to_id[message.topic] === undefined) { - direct_index.topic_to_id[message.topic] = []; - } - direct_index.topic_to_id[message.topic].push(uuid); - - if (msngr.utils.exist(message.category)) { - if (direct_index.topic_cat_to_id[message.topic] === undefined) { - direct_index.topic_cat_to_id[message.topic] = { }; - } - - if (direct_index.topic_cat_to_id[message.topic][message.category] === undefined) { - direct_index.topic_cat_to_id[message.topic][message.category] = []; - } - - direct_index.topic_cat_to_id[message.topic][message.category].push(uuid); - } - - if (msngr.utils.exist(message.dataType)) { - if (direct_index.topic_type_to_id[message.topic] === undefined) { - direct_index.topic_type_to_id[message.topic] = { }; - } - - if (direct_index.topic_type_to_id[message.topic][message.dataType] === undefined) { - direct_index.topic_type_to_id[message.topic][message.dataType] = []; - } - - direct_index.topic_type_to_id[message.topic][message.dataType].push(uuid); - } - - if (msngr.utils.exist(message.category) && msngr.utils.exist(message.dataType)) { - if (direct_index.topic_cat_type_to_id[message.topic] === undefined) { - direct_index.topic_cat_type_to_id[message.topic] = { }; - } - - if (direct_index.topic_cat_type_to_id[message.topic][message.category] === undefined) { - direct_index.topic_cat_type_to_id[message.topic][message.category] = { }; - } - - if (direct_index.topic_cat_type_to_id[message.topic][message.category][message.dataType] === undefined) { - direct_index.topic_cat_type_to_id[message.topic][message.category][message.dataType] = []; - } - - direct_index.topic_cat_type_to_id[message.topic][message.category][message.dataType].push(uuid); - } - - index_count++; - - return uuid; - } - return undefined; - }, - delete: function (uuid) { - if (msngr.utils.exist(uuid) && msngr.utils.exist(id_to_message[uuid])) { - var message = id_to_message[uuid]; - - if (msngr.utils.exist(message.topic)) { - deleteValueFromArray(direct_index.topic_to_id[message.topic], uuid); - - if (msngr.utils.exist(message.category)) { - deleteValueFromArray(direct_index.topic_cat_to_id[message.topic][message.category], uuid); - } - - if (msngr.utils.exist(message.dataType)) { - deleteValueFromArray(direct_index.topic_type_to_id[message.topic][message.dataType], uuid); - } - - if (msngr.utils.exist(message.category) && msngr.utils.exist(message.dataType)) { - deleteValueFromArray(direct_index.topic_cat_type_to_id[message.topic][message.category][message.dataType], uuid); - } - } - - delete id_to_message[uuid]; - index_count--; - - return true; - } - return false; - }, - query: function (message) { - if (msngr.utils.exist(message)) { - if (msngr.utils.exist(message.topic)) { - // Topic Only Results - if (!msngr.utils.exist(message.category) && !msngr.utils.exist(message.dataType)) { - return direct_index.topic_to_id[message.topic] || []; - } - - // Topic + Category Results - if (msngr.utils.exist(message.category) && !msngr.utils.exist(message.dataType)) { - return (direct_index.topic_cat_to_id[message.topic] || { })[message.category] || []; - } - - // Topic + Data Type Results - if (msngr.utils.exist(message.dataType) && !msngr.utils.exist(message.category)) { - return (direct_index.topic_type_to_id[message.topic] || { })[message.dataType] || []; - } - - // Topic + Category + Data Type Results - if (msngr.utils.exist(message.category) && msngr.utils.exist(message.dataType)) { - return ((direct_index.topic_cat_type_to_id[message.topic] || { })[message.category] || { })[message.dataType] || []; - } - } - } - - return []; - }, - clear: function () { - // Index for id to message objects - id_to_message = { }; - - // Direct index (no partials) for message - direct_index = { - topic_to_id: { }, - topic_cat_to_id: { }, - topic_type_to_id: { }, - topic_cat_type_to_id: { } - }; - - index_count = 0; - - return true; - }, - count: function () { - return index_count; - } - } - }; -}())); - -msngr.extend((function () { +})); + +msngr.extend((function (external, internal) { "use strict"; - // Throw statements - var InvalidParametersException = function (str) { - return { - severity: "unrecoverable", - message: ("Invalid parameters supplied to the {method} method".replace("{method}", str)) + internal.objects = internal.objects || { }; + internal.objects.executer = function (methods, payload, context) { + + if (external.isFunction(methods)) { + methods = [methods]; + } + + if (!external.exist(methods) || !external.isArray(methods)) { + throw internal.InvalidParametersException("executor"); + } + + var exec = function (method, pay, ctx, done) { + setTimeout(function () { + var async = false; + var async = function () { + async = true; + return function (result) { + done.apply(ctx, [result]); + }; + } + + var params = undefined; + if (external.isArray(pay)) { + params = pay; + } else { + params = [pay]; + } + params.push(async); + + var syncResult = method.apply(ctx || this, params); + if (async !== true) { + done.apply(ctx, [syncResult]); + } + }, 0); }; - }; - var UnexpectedException = function (str) { return { - severity: "unrecoverable", - message: ("An unexpected exception occured in the {method} method".replace("{method}", str)) + execute: function (done) { + if (methods.length === 0 && external.exist(done)) { + return done.apply(context, [[]]); + } + return exec(methods[0], payload, context, done); + }, + parallel: function (done) { + var results = []; + var executed = 0; + + if (methods.length === 0 && external.exist(done)) { + return done.apply(context, [[]]); + } + + for (var i = 0; i < methods.length; ++i) { + (function (m, p, c) { + exec(m, p, c, function (result) { + if (external.exist(result)) { + results.push(result); + } + + ++executed; + + if (executed === methods.length && external.exist(done)) { + done.apply(context, [results]); + } + }); + }(methods[i], payload, context)); + } + } }; }; - var registerdPaths = { }; - var registerdEvents = 0; + // This is an internal extension; do not export explicitly. + return { }; +})); - var listener = function (event) { - var node = this; - var path = msngr.utils.getDomPath(node); +msngr.extend((function (external, internal) { + "use strict"; - if (msngr.utils.exist(registerdPaths[path])) { - if (msngr.utils.exist(registerdPaths[path][event.type])) { - return msngr.emit(registerdPaths[path][event.type], event); - } - } + internal.objects = internal.objects || { }; + internal.objects.memory = function () { - // How did we get here? Must be a memory leak or something. Ugh - return msngr; - }; + // Index for id to message objects + var id_to_message = { }; - return { - bind: function (element, event, topic, category, dataType) { - if (!msngr.utils.exist(element) || !msngr.utils.exist(event) || !msngr.utils.exist(topic)) { - throw InvalidParametersException("bind"); - } - if (msngr.utils.isObject(topic) && !msngr.utils.exist(topic.topic)) { - throw InvalidParametersException("bind"); - } + // Direct index (no partials) for message + var direct_index = { + topic_to_id: { }, + topic_cat_to_id: { }, + topic_type_to_id: { }, + topic_cat_type_to_id: { } + }; - var node = msngr.utils.findElement(element); - var path = msngr.utils.getDomPath(node); + // Message index count + var index_count = 0; - if (!msngr.utils.exist(registerdPaths[path])) { - registerdPaths[path] = { }; - } + var mem = { + index: function (message) { + if (external.exist(message) && external.exist(message.topic)) { + var uuid = external.id(); + id_to_message[uuid] = message; - var message = undefined; - if (msngr.utils.isObject(topic)) { - message = topic; - } else { - message = { }; - message.topic = topic; + if (direct_index.topic_to_id[message.topic] === undefined) { + direct_index.topic_to_id[message.topic] = []; + } + direct_index.topic_to_id[message.topic].push(uuid); - if (msngr.utils.exist(category)) { - message.category = category; - } + if (external.exist(message.category)) { + if (direct_index.topic_cat_to_id[message.topic] === undefined) { + direct_index.topic_cat_to_id[message.topic] = { }; + } + + if (direct_index.topic_cat_to_id[message.topic][message.category] === undefined) { + direct_index.topic_cat_to_id[message.topic][message.category] = []; + } + + direct_index.topic_cat_to_id[message.topic][message.category].push(uuid); + } + + if (external.exist(message.dataType)) { + if (direct_index.topic_type_to_id[message.topic] === undefined) { + direct_index.topic_type_to_id[message.topic] = { }; + } + + if (direct_index.topic_type_to_id[message.topic][message.dataType] === undefined) { + direct_index.topic_type_to_id[message.topic][message.dataType] = []; + } + + direct_index.topic_type_to_id[message.topic][message.dataType].push(uuid); + } + + if (external.exist(message.category) && external.exist(message.dataType)) { + if (direct_index.topic_cat_type_to_id[message.topic] === undefined) { + direct_index.topic_cat_type_to_id[message.topic] = { }; + } + + if (direct_index.topic_cat_type_to_id[message.topic][message.category] === undefined) { + direct_index.topic_cat_type_to_id[message.topic][message.category] = { }; + } - if (msngr.utils.exist(dataType)) { - message.dataType = dataType; + if (direct_index.topic_cat_type_to_id[message.topic][message.category][message.dataType] === undefined) { + direct_index.topic_cat_type_to_id[message.topic][message.category][message.dataType] = []; + } + + direct_index.topic_cat_type_to_id[message.topic][message.category][message.dataType].push(uuid); + } + + index_count++; + + return uuid; } - } + return undefined; + }, + delete: function (uuid) { + if (external.exist(uuid) && external.exist(id_to_message[uuid])) { + var message = id_to_message[uuid]; - registerdPaths[path][event] = message; + if (external.exist(message.topic)) { + external.removeFromArray(direct_index.topic_to_id[message.topic], uuid); - node.addEventListener(event, listener); + if (external.exist(message.category)) { + external.removeFromArray(direct_index.topic_cat_to_id[message.topic][message.category], uuid); + } - registerdEvents++; + if (external.exist(message.dataType)) { + external.removeFromArray(direct_index.topic_type_to_id[message.topic][message.dataType], uuid); + } - return msngr; - }, - unbind: function (element, event) { - var node = msngr.utils.findElement(element); - var path = msngr.utils.getDomPath(node); + if (external.exist(message.category) && external.exist(message.dataType)) { + external.removeFromArray(direct_index.topic_cat_type_to_id[message.topic][message.category][message.dataType], uuid); + } + } - if (msngr.utils.exist(registerdPaths[path])) { - if (msngr.utils.exist(registerdPaths[path][event])) { - node.removeEventListener(event, listener); + delete id_to_message[uuid]; + index_count--; - delete registerdPaths[path][event]; + return true; + } + return false; + }, + query: function (message) { + if (external.exist(message)) { + if (external.exist(message.topic)) { + // Topic Only Results + if (!external.exist(message.category) && !external.exist(message.dataType)) { + return direct_index.topic_to_id[message.topic] || []; + } + + // Topic + Category Results + if (external.exist(message.category) && !external.exist(message.dataType)) { + return (direct_index.topic_cat_to_id[message.topic] || { })[message.category] || []; + } + + // Topic + Data Type Results + if (external.exist(message.dataType) && !external.exist(message.category)) { + return (direct_index.topic_type_to_id[message.topic] || { })[message.dataType] || []; + } - registerdEvents--; + // Topic + Category + Data Type Results + if (external.exist(message.category) && external.exist(message.dataType)) { + return ((direct_index.topic_cat_type_to_id[message.topic] || { })[message.category] || { })[message.dataType] || []; + } + } } + + return []; + }, + clear: function () { + // Index for id to message objects + id_to_message = { }; + + // Direct index (no partials) for message + direct_index = { + topic_to_id: { }, + topic_cat_to_id: { }, + topic_type_to_id: { }, + topic_cat_type_to_id: { } + }; + + index_count = 0; + + return true; } + }; - return msngr; - }, - getBindCount: function () { - return registerdEvents; - } + Object.defineProperty(mem, "count", { + get: function () { + return index_count; + } + }); + + return mem; }; -}())); -msngr.extend((function () { + // This is an internal extension; do not export explicitly. + return { }; +})); + +/* + ./objects/message.js + + The primary object of msngr; handles all message sending, receiving and binding. +*/ +msngr.extend((function (external, internal) { "use strict"; - // Throw statements - var InvalidParameters = function (str) { - return { - severity: "unrecoverable", - message: ("Invalid parameters supplied to the {method} method".replace("{method}", str)) - }; - }; + internal.objects = internal.objects || { }; - var delegates = { }; - var delegateCount = 0; - - var executeSync = function (method, context, params, message) { - (function (m, c, p, msg) { - var cont = true; - var wrap = { - preventDefault: function () { - cont = false; - }, - payload: p[0], - done: function () { - if (cont === true) { - m.apply(c, [wrap.payload]); - } - } - }; - msngr.act(msg, wrap); - }(method, context, params, message)); - }; + var messageIndex = internal.objects.memory(); + var payloadIndex = internal.objects.memory(); - var execute = function (method, context, params, message) { - (function (m, c, p, msg) { - setTimeout(function () { - executeSync(m, c, p, msg); - }, 0); - }(method, context, params, message)); + var handlers = { }; + var handlerCount = 0; + + var payloads = { }; + var payloadCount = 0; + + var boundDOMPaths = { }; + var boundCount = 0; + + Object.defineProperty(internal, "handlerCount", { + get: function () { + return handlerCount; + } + }); + + Object.defineProperty(internal, "boundCount", { + get: function () { + return boundCount; + } + }); + + Object.defineProperty(internal, "payloadCount", { + get: function () { + return payloadCount; + } + }); + + internal.reset = function () { + handlers = { }; + boundDOMPaths = { }; + handlerCount = 0; + boundCount = 0; + messageIndex.clear(); + payloadIndex.clear(); + payloads = { }; + payloadCount = 0; }; - var _emit = function (message, payload, callback) { - var uuids = msngr.store.query(message); - if (uuids.length > 0) { - for (var i = 0; i < uuids.length; ++i) { - var del = delegates[uuids[i]]; - var params = []; - if (msngr.utils.exist(payload || message.payload)) { - params.push(payload || message.payload); - } - execute(del.callback, del.context, params, message); + internal.processOpts = function (opts, message, payload, callback) { + var optProcessors = []; + for (var key in opts) { + if (opts.hasOwnProperty(key) && external.exist(internal.options[key])) { + optProcessors.push(internal.options[key]); } } - return msngr; + // Short circuit for no options + if (optProcessors.length === 0) { + return callback.apply(this, [payload]); + } + + // Long circuit to do stuff (du'h) + var execs = internal.objects.executer(optProcessors, [message, payload, opts], this); + + execs.parallel(function (results) { + var result = payload; + if (external.exist(results) && results.length > 0) { + for (var i = 0; i < results.length; ++i) { + if (external.exist(results[i])) { + result = external.merge(results[i], result); + } + } + } + callback.apply(this, [result]); + }); }; - var _on = function (message, callback) { - var uuid = msngr.store.index(message); - delegates[uuid] = { - callback: callback, - context: (message.context || this), - onedMessage: message - }; - delegateCount++; + internal.domListener = function (event) { + var node = this; + var path = external.getDomPath(node); - return msngr; + if (external.exist(boundDOMPaths[path])) { + if (external.exist(boundDOMPaths[path][event.type])) { + return boundDOMPaths[path][event.type].emit(); + } + } }; - var _drop = function (message, func) { - var uuids = msngr.store.query(message); - if (uuids.length > 0) { - for (var i = 0; i < uuids.length; ++i) { - var uuid = uuids[i]; - if (msngr.utils.exist(func)) { - if (delegates[uuid].callback === func) { - delete delegates[uuid]; - delegateCount--; + internal.objects.message = function (topic, category, dataType) { + var msg = undefined; + if (!external.exist(topic)) { + throw internal.InvalidParametersException("msngr"); + } - msngr.store.delete(uuid); - } - } else { - delete delegates[uuid]; - delegateCount--; + if (!external.isObject(topic) && !external.isString(topic)) { + throw internal.InvalidParametersException("msngr"); + } - msngr.store.delete(uuid); - } - } + if (external.isEmptyString(topic)) { + throw internal.InvalidParametersException("msngr"); } - return msngr; - }; + if (external.isObject(topic)) { + msg = topic; + } else { + msg = { }; + msg.topic = topic; - return { - emit: function (topic, category, dataType, payload, callback) { - if (!msngr.utils.exist(topic)) { - throw InvalidParameters("emit"); + if (!external.isEmptyString(category)) { + msg.category = category; } - var message; - if (msngr.utils.isObject(topic)) { - message = topic; - if (!msngr.utils.exist(payload) && msngr.utils.exist(category)) { - payload = category; - } - if (!msngr.utils.exist(callback) && msngr.utils.exist(dataType) && msngr.utils.isFunction(dataType)) { - callback = dataType; - } - return _emit(message, payload, callback); + if (!external.isEmptyString(dataType)) { + msg.dataType = dataType; } + } - message = { }; - var args = msngr.utils.argumentsToArray(arguments); + var options = { }; - message.topic = args.shift(); + var counts = { + emits: 0, + persists: 0, + options: 0, + ons: 0, + onces: 0, + binds: 0 + }; - if (!msngr.utils.exist(payload)) { - if (args.length > 0 && msngr.utils.isObject(args[0])) { - payload = args.shift(); + var explicitEmit = function (payload, uuids, callback) { + var uuids = uuids || messageIndex.query(msg) || []; + var methods = []; + var toDrop = []; + for (var i = 0; i < uuids.length; ++i) { + var obj = handlers[uuids[i]]; + methods.push(obj.handler); - return _emit(message, payload); + if (obj.once === true) { + toDrop.push(obj.handler); } } - message.category = args.shift(); + internal.processOpts(options, msg, payload, function (result) { + var execs = internal.objects.executer(methods, result, (msg.context || this)); + + for (var i = 0; i < toDrop.length; ++i) { + msgObj.drop(toDrop[i]); + } + + execs.parallel(callback); + + }); + }; + + var fetchPersisted = function () { + var uuids = payloadIndex.query(msg); - if (args.length > 0 && msngr.utils.isObject(args[0])) { - payload = args.shift(); + var fpay; - return _emit(message, payload); + if (uuids.length === 0) { + return undefined; } - message.dataType = args.shift(); - return _emit(message, payload); - }, - on: function (topic, category, dataType, callback) { - if (!msngr.utils.exist(topic)) { - throw InvalidParameters("on"); + if (uuids.length === 1) { + return payloads[uuids[0]]; } - var message; - if (msngr.utils.isObject(topic)) { - message = topic; - if (!msngr.utils.exist(callback) && msngr.utils.exist(category)) { - callback = category; - } - return _on(message, callback); + for (var i = 0; i < uuids.length; ++i) { + fpay = external.extend(innerPay, fpay); } - if (arguments.length > 1) { - message = { }; - var args = msngr.utils.argumentsToArray(arguments); - message.topic = args.shift(); + return fpay; + }; - message.category = args.shift(); - message.dataType = args.shift(); + var msgObj = { + option: function (key, value) { + if (!external.exist(key) || !external.isString(key)) { + throw internal.InvalidParametersException("option"); + } - callback = callback || args.pop(); + options[key] = value; + counts.options = counts.options + 1; - if (msngr.utils.isFunction(message.category) && !msngr.utils.exist(message.dataType)) { - callback = message.category; - delete message.category; - delete message.dataType; + return msgObj; + }, + emit: function (payload, callback) { + if (external.isFunction(payload)) { + callback = payload; + payload = undefined; } + explicitEmit(payload, undefined, callback); + counts.emits = counts.emits + 1; - if (msngr.utils.isFunction(message.dataType) && msngr.utils.exist(message.category)) { - callback = message.dataType; - delete message.dataType; + return msgObj; + }, + persist: function (payload) { + if (payload === undefined) { + payload = null; } - return _on(message, callback); - } + var uuids = payloadIndex.query(msg); + if (uuids.length === 0) { + var uuid = payloadIndex.index(msg); + payloads[uuid] = payload; + uuids = [uuid]; + } else { + for (var i = 0; i < uuids.length; ++i) { + payloads[uuids[i]] = external.extend(payload, payloads[uuids[i]]); + } + } - throw InvalidParameters("on"); - }, - drop: function (topic, category, dataType, callback) { - if (!msngr.utils.exist(topic)) { - throw InvalidParameters("drop"); - } + var fpay = fetchPersisted(); + + ++payloadCount; + + counts.persists = counts.persists + 1; + + return msgObj.emit(fpay); + }, + cease: function () { + var uuids = payloadIndex.query(msg); - var message; - if (msngr.utils.isObject(topic)) { - message = topic; - if (!msngr.utils.exist(callback) && msngr.utils.exist(category)) { - callback = category; + for (var i = 0; i < uuids.length; ++i) { + delete payloads[uuids[i]]; + --payloadCount; } - return _drop(message, callback); - } - if (arguments.length > 0) { - message = { }; - var args = msngr.utils.argumentsToArray(arguments); - message.topic = args.shift(); + return msgObj; + }, + on: function (handler) { + var uuid = messageIndex.index(msg); + handlers[uuid] = { + handler: handler, + context: (msg.context || this), + once: false + }; + handlerCount++; + + var payload = fetchPersisted(); + if (payload !== undefined) { + explicitEmit(payload, [uuid], undefined); + } + counts.ons = counts.ons + 1; - message.category = args.shift(); - message.dataType = args.shift(); + return msgObj; + }, + once: function (handler) { + var uuid = messageIndex.index(msg); + handlers[uuid] = { + handler: handler, + context: (msg.context || this), + once: true + }; + handlerCount++; + + var payload = fetchPersisted(); + if (payload !== undefined) { + explicitEmit(payload, [uuid], undefined); + } + counts.onces = counts.onces + 1; - callback = callback || args.pop(); + return msgObj; + }, + bind: function (element, event) { + var node = external.findElement(element); + var path = external.getDomPath(node); - if (msngr.utils.isFunction(message.category) && !msngr.utils.exist(message.dataType)) { - callback = message.category; - delete message.category; - delete message.dataType; + if (!external.exist(boundDOMPaths[path])) { + boundDOMPaths[path] = { }; } - if (msngr.utils.isFunction(message.dataType) && msngr.utils.exist(message.category)) { - callback = message.dataType; - delete message.dataType; + boundDOMPaths[path][event] = msgObj; + + node.addEventListener(event, internal.domListener); + + ++boundCount; + counts.binds = counts.binds + 1; + + return msgObj; + }, + drop: function (handler) { + var uuids = messageIndex.query(msg); + if (uuids.length > 0) { + for (var i = 0; i < uuids.length; ++i) { + var uuid = uuids[i]; + if (handlers[uuid].handler === handler) { + delete handlers[uuid]; + handlerCount--; + + messageIndex.delete(uuid); + } + } } - return _drop(message, callback); - } + return msgObj; + }, + unbind: function (element, event) { + var node = external.findElement(element); + var path = external.getDomPath(node); - throw InvalidParameters("drop"); - }, - dropAll: function () { - delegates = { }; - delegateCount = 0; - msngr.store.clear(); + if (external.exist(boundDOMPaths[path])) { + if (external.exist(boundDOMPaths[path][event])) { + node.removeEventListener(event, internal.domListener); - return msngr; - }, - getMessageCount: function () { - return delegateCount; + delete boundDOMPaths[path][event]; + + --boundCount; + } + } + + return msgObj; + }, + dropAll: function () { + var uuids = messageIndex.query(msg); + if (uuids.length > 0) { + for (var i = 0; i < uuids.length; ++i) { + var uuid = uuids[i]; + delete handlers[uuid]; + handlerCount--; + + messageIndex.delete(uuid); + } + } + + return msgObj; + } + }; + + // Expose the raw message object itself via a message property. + // Do not allow modification. + Object.defineProperty(msgObj, "message", { + get: function () { + return msg; + } + }); + + // If debug mode is enabled then let's expose the internal method hit counts. + // These counts are only good if a method is called and succeeds. + if (external.debug === true) { + Object.defineProperty(msgObj, "counts", { + get: function () { + return counts; + } + }); } + + return msgObj; }; -}())); -msngr.extend((function () { + // This is an internal extension; do not export explicitly. + return { }; +})); + +/* + ./options/cross-window.js + + The cross-window option; provides the ability to emit and receive messages across + multiple browser tabs / windows within the same web browser. +*/ +msngr.extend((function (external, internal) { "use strict"; - // Throw statements - var InvalidParameters = function (str) { - return { - name: "InvalidParameters", - severity: "unrecoverable", - message: ("Invalid parameters supplied to the {method} method".replace("{method}", str)) - }; - }; + var channelName = "__msngr_cross-window"; - var ReservedKeywords = function (keyword) { - return { - name: "ReservedKeywordsException", - severity: "unrecoverable", - message: ("Reserved keyword {keyword} supplied as action.".replace("{keyword}", keyword)) - }; - }; + internal.options = internal.options || { }; - var reservedProperties = ["topic", "category", "dataType", "payload"]; - var actions = { }; - var actionsCount = 0; + // Let's check if localstorage is even available. If it isn't we shouldn't register + if (typeof localStorage === "undefined" || typeof window === "undefined") { + return { }; + } - return { - action: function (property, handler) { - if (!msngr.utils.exist(property) || !msngr.utils.exist(handler)) { - throw InvalidParameters("action"); - } + window.addEventListener("storage", function (event) { + if (event.key === channelName) { + // New message data. Respond! + var obj; + try { + obj = JSON.parse(event.newValue); + } catch (ex) { console.log(ex); } - if (reservedProperties.indexOf(property) !== -1) { - throw ReservedKeywords(property); + if (obj !== undefined && external.isObject(obj)) { + internal.objects.message(obj.message).emit(obj.payload); } + } + }); - actions[property] = handler; - actionsCount++; - }, - inaction: function (property) { - if (!msngr.utils.exist(property)) { - throw InvalidParameters("inaction"); - } + internal.options["cross-window"] = function (message, payload, options, async) { + // Normalize all of the inputs + options = options || { }; + options = options["cross-window"] || { }; - delete actions[property]; - actionsCount--; - }, - act: function (message, superWrap) { - if (!msngr.utils.exist(message) || !msngr.utils.exist(superWrap)) { - throw InvalidParameters("act"); - } + var obj = { + message: message, + payload: payload + }; - (function (msg, sw) { - if (actionsCount > 0) { - var wrap = { - preventDefault: function () { - sw.preventDefault(); - }, - payload: sw.payload - }; - for (var key in msg) { - if (msg.hasOwnProperty(key)) { - if (reservedProperties.indexOf(key) === -1) { - if (actions[key] !== undefined) { - actions[key].apply(this, [msg, wrap]); - } - } - } - } - sw.payload = wrap.payload; - } - return sw.done(); - }(message, superWrap)); - }, - getActionCount: function () { - return actionsCount; - }, - getAvailableActions: function () { - return Object.keys(actions); - } + try { + localStorage.setItem(channelName, JSON.stringify(obj)); + } catch (ex) { console.log(ex); } + + return undefined; }; -}())); -msngr.action("dom", function (message, wrap) { + // This is an internal extension; do not export explicitly. + return { }; +})); + +/* + ./options/dom.js + + The dom option; provides value gathering from supplied selectors +*/ +msngr.extend((function (external, internal) { "use strict"; - if (msngr.utils.exist(message.dom)) { - var norm = { - gather: undefined, - doc: undefined - }; - if (!msngr.utils.isObject(message.dom)) { - if (msngr.utils.isArray(message.dom)) { - norm.gather = message.dom; - } else if (msngr.utils.isString(message.dom)) { - norm.gather = [message.dom]; - } - } else { - if (msngr.utils.exist(message.dom.gather)) { - norm.gather = (msngr.utils.isArray(message.dom.gather) ? message.dom.gather : [message.dom.gather]); - } - if (msngr.utils.exist(message.dom.root || message.dom.doc)) { - norm.doc = message.dom.root || message.dom.doc; - } + internal.options = internal.options || { }; + + internal.options.dom = function (message, payload, options, async) { + // Normalize all of the inputs + options = options || { }; + options = options.dom || { }; + var doc = options.doc || options.document || document; + + var selectors = undefined; + if (external.isObject(options) && external.exist(options.selectors) && external.isString(options.selectors)) { + selectors = [options.selectors]; + } else if (external.isString(options)) { + selectors = [options]; + } else if (external.isArray(options)) { + selectors = options; } - if (msngr.utils.exist(norm.gather) && norm.gather.length > 0) { - if (!msngr.utils.isObject(wrap.payload)) { - wrap.payload = { }; + if (!external.exist(doc) || !external.exist(selectors) || selectors.length === 0) { + return undefined; + } + + // Process all selectors and put them into a single array + var elements = []; + var selLength = selectors.length; + for (var i = 0; i < selLength; ++i) { + var found = external.findElements(selectors[i], doc); + if (found.length > 0) { + elements = elements.concat(Array.prototype.slice.call(found)); } + } - for (var i = 0; i < norm.gather.length; ++i) { - var elms = msngr.utils.findElements(norm.gather[i], message.dom.root); - if (msngr.utils.exist(elms) && elms.length > 0) { - for (var j = 0; j < elms.length; ++j) { - var elm = elms[j]; - - var prop; - if (msngr.utils.exist(elm.getAttribute("name")) && !msngr.utils.isEmptyString(elm.getAttribute("name"))) { - prop = elm.getAttribute("name"); - } else if (msngr.utils.exist(elm.id) && !msngr.utils.isEmptyString(elm.id)) { - prop = elm.getAttribute("id"); - console.log(elm.id); - } else { - prop = elm.tagName.toLowerCase() + j; - } + // Short circuit because no elements + if (elements.length === 0) { + return undefined; + } - wrap.payload[prop] = elm.value; - } - } + // Iterate through found elements and aggregate the results + var resultMap = undefined; + var elmLength = elements.length; + var unnamedTags = 0; + for (var i = 0; i < elmLength; ++i) { + var key = undefined, value = undefined; + var elm = elements[i]; + + var nameAttr = elm.getAttribute("name"); + var idAttr = elm.id; + var tagName = elm.tagName.toLowerCase(); + var val = elm.value; + + if (external.exist(nameAttr) && !external.isEmptyString(nameAttr)) { + key = nameAttr; + } else if (external.exist(idAttr) && !external.isEmptyString(idAttr)) { + key = idAttr; + } else { + key = (tagName + unnamedTags); + unnamedTags++; + } + + if (resultMap === undefined) { + resultMap = { }; } + resultMap[key] = val; } - } - return msngr; -}); + return resultMap; + }; + + // This is an internal extension; do not export explicitly. + return { }; +})); + +/* + module.exports.js + + If we're running in a node.js / io.js context then export msngr otherwise do nothing. +*/ if (typeof module !== "undefined" && typeof module.exports !== "undefined") { module.exports = msngr; } diff --git a/msngr.min.js b/msngr.min.js index 476f8f4..43ccd23 100644 --- a/msngr.min.js +++ b/msngr.min.js @@ -1 +1 @@ -var msngr=msngr||function(){"use strict";return{version:"1.1.0",extend:function(obj,target){if(target=target||msngr,"[object Object]"===Object.prototype.toString.call(obj))for(var key in obj)obj.hasOwnProperty(key)&&("[object Object]"===Object.prototype.toString.call(obj[key])?(void 0===target[key]&&(target[key]={}),target[key]=msngr.extend(obj[key],target[key])):target[key]="[object Array]"===Object.prototype.toString.call(obj[key])?(target[key]||[]).concat(obj[key]):obj[key]);return target}}}();msngr.extend(function(){"use strict";return{utils:{argumentsToArray:function(args){return msngr.utils.isArray(args)?args:Array.prototype.slice.call(args,0)}}}}()),msngr.extend(function(){"use strict";return{utils:{isHtmlElement:function(obj){var t=this.getType(obj);return 0===t.indexOf("[object HTML")||0===t.indexOf("[object global]")},isNodeList:function(obj){return"[object NodeList]"===this.getType(obj)},findElement:function(element,root){var elms=msngr.utils.findElements(element);return void 0!==elms&&elms.length>0?elms[0]:elms},findElements:function(selector,root){var elm;if(msngr.utils.isHtmlElement(selector)&&(elm=selector),void 0===elm&&msngr.utils.isString(selector)){var doc=root||document,result=doc.querySelectorAll(selector);null!==result&&(elm=result)}return elm},getDomPath:function(element){var node=msngr.utils.isHtmlElement(element)?element:void 0;return void 0===node?void 0:(void 0===node.id&&(node.id=msngr.utils.id()),"#"+node.id)},querySelectorAllWithEq:function(selector,root){if(void 0===selector)return null;for(var doc=root||document,queue=[],process=function(input){if(-1===input.indexOf(":eq("))return void 0;var eqlLoc=input.indexOf(":eq("),sel=input.substring(0,eqlLoc),ind=input.substring(eqlLoc+4,input.indexOf(")",eqlLoc));selector=input.substring(input.indexOf(")",eqlLoc)+1,input.length),">"===sel.charAt(0)&&(sel=sel.substring(1,sel.length)),">"===selector.charAt(0)&&(selector=selector.substring(1,selector.length)),queue.push({selector:sel,index:parseInt(ind,10)})};-1!==selector.indexOf(":eq");)process(selector);for(var result;queue.length>0;){var item=queue.shift();result=(result||doc).querySelectorAll(item.selector)[item.index]}return selector.trim().length>0?(result||doc).querySelectorAll(selector):[result]},querySelectorWithEq:function(selector){return msngr.utils.querySelectorAllWithEq(selector)[0]}}}}()),msngr.extend(function(){"use strict";var nowPerformance=function(){return performance.now()},nowNode=function(){return process.hrtime()[1]/1e6},nowLegacy=function(){return(new Date).getTime()},nowExec=void 0,nowExecDebugLabel="",lastNow=void 0;return{utils:{id:function(){var d=msngr.utils.now(),uuid="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(c){var r=(d+16*Math.random())%16|0;return d=Math.floor(d/16),("x"==c?r:3&r|8).toString(16)});return uuid},now:function(noDuplicate){void 0===nowExec&&("undefined"!=typeof performance?(nowExec=nowPerformance,nowExecDebugLabel="performance"):"undefined"!=typeof process?(nowExec=nowNode,nowExecDebugLabel="node"):(nowExec=nowLegacy,nowExecDebugLabel="legacy"));var now=nowExec();return noDuplicate===!0&&lastNow===now?msngr.utils.now(noDuplicate):(lastNow=now,now)}}}}()),msngr.extend(function(){"use strict";return{utils:{getType:function(obj){return msngr.utils.exist(obj)?Object.prototype.toString.call(obj):""+obj},isArguments:function(obj){return"[object Arguments]"===msngr.utils.getType(obj)},areArguments:function(){return msngr.utils.reiterativeValidation(msngr.utils.isArguments,msngr.utils.argumentsToArray(arguments))},isNullOrUndefined:function(obj){return void 0===obj||null===obj},exist:function(obj){return!msngr.utils.isNullOrUndefined(obj)},exists:function(){return msngr.utils.reiterativeValidation(msngr.utils.exist,msngr.utils.argumentsToArray(arguments))},isString:function(str){return"[object String]"===msngr.utils.getType(str)},areStrings:function(){return msngr.utils.reiterativeValidation(msngr.utils.isString,msngr.utils.argumentsToArray(arguments))},isDate:function(obj){return"[object Date]"===msngr.utils.getType(obj)},areDates:function(){return msngr.utils.reiterativeValidation(msngr.utils.isDate,msngr.utils.argumentsToArray(arguments))},isArray:function(obj){return"[object Array]"===msngr.utils.getType(obj)},areArrays:function(){return msngr.utils.reiterativeValidation(msngr.utils.isArray,msngr.utils.argumentsToArray(arguments))},isNumber:function(obj){return"[object Number]"===msngr.utils.getType(obj)},areNumbers:function(){return msngr.utils.reiterativeValidation(msngr.utils.isNumber,msngr.utils.argumentsToArray(arguments))},isObject:function(obj){return"[object Object]"===msngr.utils.getType(obj)},areObjects:function(){return msngr.utils.reiterativeValidation(msngr.utils.isObject,msngr.utils.argumentsToArray(arguments))},isFunction:function(func){return"[object Function]"===msngr.utils.getType(func)},areFunctions:function(){return msngr.utils.reiterativeValidation(msngr.utils.isFunction,msngr.utils.argumentsToArray(arguments))},isEmptyString:function(str){var isStr=msngr.utils.isString(str);return void 0===str||null===str||isStr&&0===str.toString().trim().length?!0:!1},areEmptyStrings:function(){return msngr.utils.reiterativeValidation(msngr.utils.isEmptyString,msngr.utils.argumentsToArray(arguments))},hasWildCard:function(str){return-1!==str.indexOf("*")},reiterativeValidation:function(validationMethod,inputs){var result=!1;if(msngr.utils.exist(validationMethod)&&msngr.utils.exist(inputs)){msngr.utils.isArray(inputs)||(inputs=[inputs]);for(var i=0;i0)for(var i=0;i0)for(var i=0;i0&&msngr.utils.isObject(args[0])?(payload=args.shift(),_emit(message,payload)):(message.category=args.shift(),args.length>0&&msngr.utils.isObject(args[0])?(payload=args.shift(),_emit(message,payload)):(message.dataType=args.shift(),_emit(message,payload)))},on:function(topic,category,dataType,callback){if(!msngr.utils.exist(topic))throw InvalidParameters("on");var message;if(msngr.utils.isObject(topic))return message=topic,!msngr.utils.exist(callback)&&msngr.utils.exist(category)&&(callback=category),_on(message,callback);if(arguments.length>1){message={};var args=msngr.utils.argumentsToArray(arguments);return message.topic=args.shift(),message.category=args.shift(),message.dataType=args.shift(),callback=callback||args.pop(),msngr.utils.isFunction(message.category)&&!msngr.utils.exist(message.dataType)&&(callback=message.category,delete message.category,delete message.dataType),msngr.utils.isFunction(message.dataType)&&msngr.utils.exist(message.category)&&(callback=message.dataType,delete message.dataType),_on(message,callback)}throw InvalidParameters("on")},drop:function(topic,category,dataType,callback){if(!msngr.utils.exist(topic))throw InvalidParameters("drop");var message;if(msngr.utils.isObject(topic))return message=topic,!msngr.utils.exist(callback)&&msngr.utils.exist(category)&&(callback=category),_drop(message,callback);if(arguments.length>0){message={};var args=msngr.utils.argumentsToArray(arguments);return message.topic=args.shift(),message.category=args.shift(),message.dataType=args.shift(),callback=callback||args.pop(),msngr.utils.isFunction(message.category)&&!msngr.utils.exist(message.dataType)&&(callback=message.category,delete message.category,delete message.dataType),msngr.utils.isFunction(message.dataType)&&msngr.utils.exist(message.category)&&(callback=message.dataType,delete message.dataType),_drop(message,callback)}throw InvalidParameters("drop")},dropAll:function(){return delegates={},delegateCount=0,msngr.store.clear(),msngr},getMessageCount:function(){return delegateCount}}}()),msngr.extend(function(){"use strict";var InvalidParameters=function(str){return{name:"InvalidParameters",severity:"unrecoverable",message:"Invalid parameters supplied to the {method} method".replace("{method}",str)}},ReservedKeywords=function(keyword){return{name:"ReservedKeywordsException",severity:"unrecoverable",message:"Reserved keyword {keyword} supplied as action.".replace("{keyword}",keyword)}},reservedProperties=["topic","category","dataType","payload"],actions={},actionsCount=0;return{action:function(property,handler){if(!msngr.utils.exist(property)||!msngr.utils.exist(handler))throw InvalidParameters("action");if(-1!==reservedProperties.indexOf(property))throw ReservedKeywords(property);actions[property]=handler,actionsCount++},inaction:function(property){if(!msngr.utils.exist(property))throw InvalidParameters("inaction");delete actions[property],actionsCount--},act:function(message,superWrap){if(!msngr.utils.exist(message)||!msngr.utils.exist(superWrap))throw InvalidParameters("act");!function(msg,sw){if(actionsCount>0){var wrap={preventDefault:function(){sw.preventDefault()},payload:sw.payload};for(var key in msg)msg.hasOwnProperty(key)&&-1===reservedProperties.indexOf(key)&&void 0!==actions[key]&&actions[key].apply(this,[msg,wrap]);sw.payload=wrap.payload}return sw.done()}(message,superWrap)},getActionCount:function(){return actionsCount},getAvailableActions:function(){return Object.keys(actions)}}}()),msngr.action("dom",function(message,wrap){"use strict";if(msngr.utils.exist(message.dom)){var norm={gather:void 0,doc:void 0};if(msngr.utils.isObject(message.dom)?(msngr.utils.exist(message.dom.gather)&&(norm.gather=msngr.utils.isArray(message.dom.gather)?message.dom.gather:[message.dom.gather]),msngr.utils.exist(message.dom.root||message.dom.doc)&&(norm.doc=message.dom.root||message.dom.doc)):msngr.utils.isArray(message.dom)?norm.gather=message.dom:msngr.utils.isString(message.dom)&&(norm.gather=[message.dom]),msngr.utils.exist(norm.gather)&&norm.gather.length>0){msngr.utils.isObject(wrap.payload)||(wrap.payload={});for(var i=0;i0)for(var j=0;j0)for(var i=0;i0?elms[0]:elms},findElements:function(selector,root){var elm;if(external.isHtmlElement(selector)&&(elm=selector),void 0===elm&&external.isString(selector)){var doc=root||document,result=doc.querySelectorAll(selector);null!==result&&(elm=result)}return elm},getDomPath:function(element){var node=external.isHtmlElement(element)?element:void 0;return void 0===node?void 0:(void 0===node.id&&(node.id=external.id()),"#"+node.id)},querySelectorAllWithEq:function(selector,root){if(void 0===selector)return null;for(var doc=root||document,queue=[],process=function(input){if(-1===input.indexOf(":eq("))return void 0;var eqlLoc=input.indexOf(":eq("),sel=input.substring(0,eqlLoc),ind=input.substring(eqlLoc+4,input.indexOf(")",eqlLoc));selector=input.substring(input.indexOf(")",eqlLoc)+1,input.length),">"===sel.charAt(0)&&(sel=sel.substring(1,sel.length)),">"===selector.charAt(0)&&(selector=selector.substring(1,selector.length)),queue.push({selector:sel,index:parseInt(ind,10)})};-1!==selector.indexOf(":eq");)process(selector);for(var result;queue.length>0;){var item=queue.shift();result=(result||doc).querySelectorAll(item.selector)[item.index]}return selector.trim().length>0?(result||doc).querySelectorAll(selector):[result]},querySelectorWithEq:function(selector,root){return external.querySelectorAllWithEq(selector,root)[0]}}}),msngr.extend(function(external,internal){"use strict";return internal.InvalidParametersException=function(str){return{name:"InvalidParametersException",severity:"unrecoverable",message:"Invalid parameters supplied to the {method} method".replace("{method}",str)}},internal.ReservedKeywordsException=function(keyword){return{name:"ReservedKeywordsException",severity:"unrecoverable",message:"Reserved keyword {keyword} supplied as action.".replace("{keyword}",keyword)}},internal.MangledException=function(variable,method){return{name:"MangledException",severity:"unrecoverable",message:"The {variable} was unexpectedly mangled in {method}.".replace("{variable}",variable).replace("{method}",method)}},{}}),msngr.extend(function(external,internal){"use strict";var nowPerformance=function(){return performance.now()},nowNode=function(){return process.hrtime()[1]/1e6},nowLegacy=function(){return(new Date).getTime()},nowExec=void 0,nowExecDebugLabel="",lastNow=void 0;return{id:function(){var d=external.now(),uuid="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(c){var r=(d+16*Math.random())%16|0;return d=Math.floor(d/16),("x"==c?r:3&r|8).toString(16)});return uuid},now:function(noDuplicate){void 0===nowExec&&("undefined"!=typeof performance?(nowExec=nowPerformance,nowExecDebugLabel="performance"):"undefined"!=typeof process?(nowExec=nowNode,nowExecDebugLabel="node"):(nowExec=nowLegacy,nowExecDebugLabel="legacy"));var now=nowExec();return noDuplicate===!0&&lastNow===now?external.now(noDuplicate):(lastNow=now,now)},removeFromArray:function(arr,value){var inx=arr.indexOf(value),endIndex=arr.length-1;if(inx!==endIndex){var temp=arr[endIndex];arr[endIndex]=arr[inx],arr[inx]=temp}arr.pop()}}}),msngr.extend(function(external,internal){"use strict";return internal.reiterativeValidation=function(validationMethod,inputs){var result=!1;if(external.exist(validationMethod)&&external.exist(inputs)){external.isArray(inputs)||(inputs=[inputs]);for(var i=0;i0)for(var i=0;i0)for(var i=0;i0)for(var i=0;ii;++i){var found=external.findElements(selectors[i],doc);found.length>0&&(elements=elements.concat(Array.prototype.slice.call(found)))}if(0===elements.length)return void 0;for(var resultMap=void 0,elmLength=elements.length,unnamedTags=0,i=0;elmLength>i;++i){var key=void 0,elm=elements[i],nameAttr=elm.getAttribute("name"),idAttr=elm.id,tagName=elm.tagName.toLowerCase(),val=elm.value;external.exist(nameAttr)&&!external.isEmptyString(nameAttr)?key=nameAttr:external.exist(idAttr)&&!external.isEmptyString(idAttr)?key=idAttr:(key=tagName+unnamedTags,unnamedTags++),void 0===resultMap&&(resultMap={}),resultMap[key]=val}return resultMap},{}}),"undefined"!=typeof module&&"undefined"!=typeof module.exports&&(module.exports=msngr); \ No newline at end of file diff --git a/package.json b/package.json index a92de3b..64e7bca 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "msngr", "main": "msngr.js", "description": "msngr.js is a small library used to facilitate communication through messages rather than direct binding. This loose coupling allows connecting components to each other or to UI components in an abstract way on the server or the client.", - "version": "1.1.0", + "version": "2.0.0", "keywords": ["message", "messaging", "subscription", "delegation", "eventing", "dom", "binding"], "repository": { "type": "git", @@ -20,10 +20,10 @@ "grunt-contrib-concat": "0.5.1", "grunt-contrib-clean": "0.6.0", "grunt-mocha-test": "0.12.7", - "mocha": "2.2.4", + "mocha": "2.2.5", "grunt-mocha-phantomjs": "0.6.1", "grunt-available-tasks": "0.5.7", - "chai": "2.2.0" + "chai": "2.3.0" }, "scripts": { "test": "grunt test" diff --git a/specRunner.html b/specRunner.html index 9224635..bff8a5e 100644 --- a/specRunner.html +++ b/specRunner.html @@ -8,6 +8,7 @@ diff --git a/specRunner.min.html b/specRunner.min.html index 892617a..cdd5ca1 100644 --- a/specRunner.min.html +++ b/specRunner.min.html @@ -8,9 +8,10 @@ - + diff --git a/src/actions/action.aspec.js b/src/actions/action.aspec.js deleted file mode 100644 index 1682e46..0000000 --- a/src/actions/action.aspec.js +++ /dev/null @@ -1,112 +0,0 @@ -if (typeof chai === "undefined" && typeof window === "undefined") { - var chai = require("chai"); -} - -if (typeof expect === "undefined") { - var expect = chai.expect; -} - -if (typeof msngr === "undefined" && typeof window === "undefined") { - var msngr = require("../../msngr"); -} - -describe("./actions/action.js", function () { - "use strict"; - - beforeEach(function (done) { - msngr.dropAll(); - done(); - }); - - it("msngr.action(property, function () { }) - adds an action which successfully acts on a message payload", function (done) { - msngr.on("TestTopic", function (payload) { - expect(payload).to.exist; - expect(payload).to.equal("Payload Poke!"); - - expect(msngr.getActionCount()).to.exist; - var c = msngr.getActionCount(); - msngr.inaction("poke"); - expect(msngr.getActionCount()).to.equal(c - 1); - - done(); - }); - - expect(msngr.getActionCount()).to.exist; - msngr.action("poke", function (message, wrap) { - wrap.payload = wrap.payload + " Poke!"; - }); - - msngr.emit({ topic: "TestTopic" , poke: true }, "Payload"); - }); - - it("msngr.action(property, function () { }) - adds multiple actions which all successfully act on a message payload", function (done) { - msngr.on("TestTopic", function (payload) { - expect(payload).to.exist; - expect(payload.numbers).to.exist; - expect(payload.numbers.length).to.equal(3); - expect(payload.words).to.exist; - expect(payload.words.length).to.equal(3); - expect(payload.original).to.exist; - expect(payload.original).to.equal("TestBorg"); - - msngr.inaction("numbers"); - msngr.inaction("words"); - - done(); - }); - - var start = msngr.getActionCount(); - expect(start).to.exist; - msngr.action("numbers", function (message, wrap) { - wrap.payload.numbers = [12, 15, 97]; - }); - - msngr.action("words", function (message, wrap) { - wrap.payload.words = ["float", "chicken", "springs"]; - }); - expect(msngr.getActionCount()).to.equal(start + 2); - - msngr.emit({ topic: "TestTopic" , numbers: true, words: true }, { original: "TestBorg" }); - }); - - it("msngr.action(property, function () { }) - adds multiple actions but only one acts on a message payload", function (done) { - msngr.on("TestTopic", function (payload) { - expect(payload).to.exist; - expect(payload.numbers).to.exist; - expect(payload.numbers.length).to.equal(3); - expect(payload.words).to.not.exist; - expect(payload.original).to.exist; - expect(payload.original).to.equal("TestBorg"); - - msngr.inaction("numbers"); - msngr.inaction("words"); - - done(); - }); - - var start = msngr.getActionCount(); - expect(start).to.exist; - msngr.action("numbers", function (message, wrap) { - wrap.payload.numbers = [12, 15, 97]; - }); - - msngr.action("words", function (message, wrap) { - wrap.payload.words = ["float", "chicken", "springs"]; - }); - expect(msngr.getActionCount()).to.equal(start + 2); - - msngr.emit({ topic: "TestTopic" , numbers: true }, { original: "TestBorg" }); - }); - - it("msngr.getActionCount() - Returns the correct amount of actions", function () { - var start = msngr.getActionCount(); - - msngr.action("chicken", function (message, wrap) { - // Nothing here necessary - }); - - expect(msngr.getActionCount()).to.equal(start + 1); - msngr.inaction("chicken"); - expect(msngr.getActionCount()).to.equal(start); - }); -}); diff --git a/src/actions/action.js b/src/actions/action.js deleted file mode 100644 index 14c67da..0000000 --- a/src/actions/action.js +++ /dev/null @@ -1,80 +0,0 @@ -msngr.extend((function () { - "use strict"; - - // Throw statements - var InvalidParameters = function (str) { - return { - name: "InvalidParameters", - severity: "unrecoverable", - message: ("Invalid parameters supplied to the {method} method".replace("{method}", str)) - }; - }; - - var ReservedKeywords = function (keyword) { - return { - name: "ReservedKeywordsException", - severity: "unrecoverable", - message: ("Reserved keyword {keyword} supplied as action.".replace("{keyword}", keyword)) - }; - }; - - var reservedProperties = ["topic", "category", "dataType", "payload"]; - var actions = { }; - var actionsCount = 0; - - return { - action: function (property, handler) { - if (!msngr.utils.exist(property) || !msngr.utils.exist(handler)) { - throw InvalidParameters("action"); - } - - if (reservedProperties.indexOf(property) !== -1) { - throw ReservedKeywords(property); - } - - actions[property] = handler; - actionsCount++; - }, - inaction: function (property) { - if (!msngr.utils.exist(property)) { - throw InvalidParameters("inaction"); - } - - delete actions[property]; - actionsCount--; - }, - act: function (message, superWrap) { - if (!msngr.utils.exist(message) || !msngr.utils.exist(superWrap)) { - throw InvalidParameters("act"); - } - - (function (msg, sw) { - if (actionsCount > 0) { - var wrap = { - preventDefault: function () { - sw.preventDefault(); - }, - payload: sw.payload - }; - for (var key in msg) { - if (msg.hasOwnProperty(key)) { - if (reservedProperties.indexOf(key) === -1) { - if (actions[key] !== undefined) { - actions[key].apply(this, [msg, wrap]); - } - } - } - } - sw.payload = wrap.payload; - } - return sw.done(); - }(message, superWrap)); - }, - getActionCount: function () { - return actionsCount; - }, - getAvailableActions: function () { - return Object.keys(actions); - } - }; -}())); diff --git a/src/actions/dom.js b/src/actions/dom.js deleted file mode 100644 index d9a3389..0000000 --- a/src/actions/dom.js +++ /dev/null @@ -1,53 +0,0 @@ -msngr.action("dom", function (message, wrap) { - "use strict"; - - if (msngr.utils.exist(message.dom)) { - var norm = { - gather: undefined, - doc: undefined - }; - if (!msngr.utils.isObject(message.dom)) { - if (msngr.utils.isArray(message.dom)) { - norm.gather = message.dom; - } else if (msngr.utils.isString(message.dom)) { - norm.gather = [message.dom]; - } - } else { - if (msngr.utils.exist(message.dom.gather)) { - norm.gather = (msngr.utils.isArray(message.dom.gather) ? message.dom.gather : [message.dom.gather]); - } - if (msngr.utils.exist(message.dom.root || message.dom.doc)) { - norm.doc = message.dom.root || message.dom.doc; - } - } - - if (msngr.utils.exist(norm.gather) && norm.gather.length > 0) { - if (!msngr.utils.isObject(wrap.payload)) { - wrap.payload = { }; - } - - for (var i = 0; i < norm.gather.length; ++i) { - var elms = msngr.utils.findElements(norm.gather[i], message.dom.root); - if (msngr.utils.exist(elms) && elms.length > 0) { - for (var j = 0; j < elms.length; ++j) { - var elm = elms[j]; - - var prop; - if (msngr.utils.exist(elm.getAttribute("name")) && !msngr.utils.isEmptyString(elm.getAttribute("name"))) { - prop = elm.getAttribute("name"); - } else if (msngr.utils.exist(elm.id) && !msngr.utils.isEmptyString(elm.id)) { - prop = elm.getAttribute("id"); - console.log(elm.id); - } else { - prop = elm.tagName.toLowerCase() + j; - } - - wrap.payload[prop] = elm.value; - } - } - } - } - } - - return msngr; -}); diff --git a/src/builders/message.aspec.js b/src/builders/message.aspec.js deleted file mode 100644 index 0f76c6c..0000000 --- a/src/builders/message.aspec.js +++ /dev/null @@ -1,61 +0,0 @@ -if (typeof chai === "undefined" && typeof window === "undefined") { - var chai = require("chai"); -} - -if (typeof expect === "undefined") { - var expect = chai.expect; -} - -if (typeof msngr === "undefined" && typeof window === "undefined") { - var msngr = require("../../msngr"); -} - -describe("./builders/message.js", function () { - "use strict"; - - it("msngr.builders.msg() - Builds basic message", function () { - var message = msngr.builders.msg() - .topic("TestTopic1") - .category("TestCategory1") - .dataType("TestDataType1") - .payload({ data: 1 }) - .build(); - - expect(message.topic).to.equal("TestTopic1"); - expect(message.category).to.equal("TestCategory1"); - expect(message.dataType).to.equal("TestDataType1"); - expect(message.payload.data).to.equal(1); - }); - - it("msngr.builders.msg() - Builds a message with an action", function () { - var message = msngr.builders.msg() - .topic("TestTopic1") - .dom(true) - .build(); - - expect(message.topic).to.equal("TestTopic1"); - expect(message.dom).to.equal(true); - }); - - it("msngr.builders.msg() - Builds multiple messages while maintaining separate instances", function () { - var build1 = msngr.builders.msg(); - var build2 = msngr.builders.msg(); - var build3 = msngr.builders.msg(); - - build1.topic("ChickenFeet"); - build2.category("Beer"); - build3.topic("Floating"); - build2.topic("Something"); - build1.category("Fowl"); - - build1 = build1.build(); - build2 = build2.build(); - build3 = build3.build(); - - expect(build1.topic).to.equal("ChickenFeet"); - expect(build1.category).to.equal("Fowl"); - expect(build2.topic).to.equal("Something"); - expect(build2.category).to.equal("Beer"); - expect(build3.topic).to.equal("Floating"); - }); -}); diff --git a/src/builders/message.js b/src/builders/message.js deleted file mode 100644 index 82208f8..0000000 --- a/src/builders/message.js +++ /dev/null @@ -1,34 +0,0 @@ -/* - ./src/builders/message.js -*/ -msngr.extend((function () { - "use strict"; - - return { - builders: { - msg: function () { - return (function () { - var message = { }; - var props = ["topic", "category", "dataType", "payload"].concat(msngr.getAvailableActions()); - - var obj = { - build: function () { - return message; - } - }; - - for (var i = 0; i < props.length; ++i) { - (function (key) { - obj[key] = function (input) { - message[key] = input; - return obj; - }; - }(props[i])); - } - - return obj; - }()); - } - } - }; -}())); diff --git a/src/main.aspec.js b/src/main.aspec.js index a55fe35..0fc824f 100644 --- a/src/main.aspec.js +++ b/src/main.aspec.js @@ -17,32 +17,32 @@ describe("./main.js", function () { expect(msngr).to.exist; }); - it("msngr.extend(obj, target) - expect method to exist", function () { - expect(msngr.extend).to.exist; + it("msngr.merge(input1, input2) - expect method to exist", function () { + expect(msngr.merge).to.exist; }); - it("msngr.extend(obj, target) - merges arrays from obj and target", function () { + it("msngr.merge(input1, input2) - merges arrays from input1 and input2", function () { var obj1 = { test: [1, 2] }; var obj2 = { test: [3, 4] }; - msngr.extend(obj1, obj2); + var merged = msngr.merge(obj1, obj2); - expect(obj2.test).to.exist; - expect(obj2.test.length).to.equal(4); + expect(merged.test).to.exist; + expect(merged.test.length).to.equal(4); }); - it("msngr.extend(obj, target) - expect properties to merge from obj and target", function () { + it("msngr.merge(input1, input2) - expect properties to merge from input1 and input2", function () { var obj1 = { prop: "something" }; var obj2 = { some: "thing" }; - msngr.extend(obj1, obj2); + var merged = msngr.merge(obj1, obj2); - expect(obj2.some).to.exist; - expect(obj2.prop).to.exist; - expect(obj2.prop).to.equal("something"); + expect(merged.some).to.exist; + expect(merged.prop).to.exist; + expect(merged.prop).to.equal("something"); }); - it("msngr.extend(obj, target) - expect deeply nested methods to merge from obj and target", function () { + it("msngr.merge(input1, input2) - expect deeply nested methods to merge from input1 and input2", function () { var obj1 = { this: { is: { @@ -59,16 +59,146 @@ describe("./main.js", function () { whatever: function () { return "whatever"; } }; - msngr.extend(obj1, obj2); + var merged = msngr.merge(obj1, obj2); + + expect(merged.whatever).to.exist; + expect(merged.whatever()).to.equal("whatever"); + + expect(merged.this).to.exist; + expect(merged.this.is).to.exist; + expect(merged.this.is.a).to.exist; + expect(merged.this.is.a.test).to.exist; + expect(merged.this.is.a.test.yup).to.exist; + expect(merged.this.is.a.test.yup()).to.equal("yup!"); + }); + + it("msngr.merge(input1, input2) - merges two methods together", function () { + var func1 = function () { return "test" }; + var func2 = function () { return "again" }; + + var merged = msngr.merge(func1, func2); + expect(merged).to.exist; + expect(merged()).to.equal("testagain"); + }); + + it("msngr.merge(input1, input2) - merges a method with properties", function () { + var myFunc = function () { + return 15; + }; + + var myProps = { + something: "yup", + life: 42 + }; + + var merged = msngr.merge(myFunc, myProps); + + expect(merged).to.exist; + expect(merged.something).to.exist; + expect(merged.life).to.exist; + expect(merged.something).to.equal("yup"); + expect(merged.life).to.equal(42); + expect(merged()).to.equal(15); + }); + + it("msngr.merge(input1, input2) - merging undefined value is simply ignored", function () { + var myTest = { }; + var merged = msngr.merge(undefined, myTest); + + expect(merged).to.exist; + expect(Object.keys(merged).length).to.equal(0); + }); - expect(obj2.whatever).to.exist; - expect(obj2.whatever()).to.equal("whatever"); + it("msngr.merge(input1, input2) - Property extends a string with another string", function () { + var t = "something"; + var merged = msngr.merge("whatever", t); + expect(merged).to.exist; + expect(msngr.getType(merged)).to.equal("[object String]"); + expect(merged).to.equal("whateversomething"); + }); + + it("msngr.merge(input1, input2) - Overwrites properly", function () { + var first = { val1: "stuff" }; + var second = { val1: 17 }; + + var merged = msngr.merge(first, second); + expect(merged).to.exist; + expect(merged.val1).to.exist; + expect(merged.val1).to.equal(17); + }); + + it("msngr.merge(input1, input2, input3, input4, input5, input6, input7, input8) - Overwrites properly with multiple parameters", function () { + var first = { val1: "stuff" }; + var second = { val1: 17 }; + var third = { val1: "chicken nuggets" }; + var fourth = { val1: function () { } }; + var fifth = { val1: null }; + var sixth = { val1: 1 }; + var seventh = { val1: [0, 1, 2] }; + var eighth = { val1: "hockey" }; + + var merged1 = msngr.merge(first, second, third); + var merged2 = msngr.merge(first, second, third, fourth); + var merged3 = msngr.merge(first, second, third, fourth, fifth); + var merged4 = msngr.merge(first, second, third, fourth, fifth, sixth); + var merged5 = msngr.merge(first, second, third, fourth, fifth, sixth, seventh); + var merged6 = msngr.merge(first, second, third, fourth, fifth, sixth, seventh, eighth); + + expect(merged1).to.exist; + expect(merged2).to.exist; + expect(merged3).to.exist; + expect(merged4).to.exist; + expect(merged5).to.exist; + expect(merged6).to.exist; + + expect(merged1.val1).to.exist; + expect(merged2.val1).to.exist; + expect(merged3.val1).to.not.exist; + expect(merged4.val1).to.exist; + expect(merged5.val1).to.exist; + expect(merged6.val1).to.exist; + + expect(merged1.val1).to.equal("chicken nuggets"); + expect(msngr.getType(merged2.val1)).to.equal("[object Function]"); + expect(merged3.val1).to.equal(null); + expect(merged4.val1).to.equal(1); + expect(merged5.val1.length).to.equal(3); + expect(merged6.val1).to.equal("hockey"); + }); + + it("msngr.extend(obj, target) - expect method to exist", function () { + expect(msngr.extend).to.exist; + }); + + it("msngr.extend(obj, target) - extend msngr", function () { + msngr.extend((function (external, internal) { + return { + sayHello: function () { + return "hello"; + } + }; + })); + + expect(msngr.sayHello).to.exist; + expect(msngr.sayHello()).to.equal("hello"); + + delete msngr.sayHello; + }); + + it("msngr.debug - property setting exports internal object for testing and debugging", function () { + msngr.debug = false; + expect(msngr.internal).to.not.exist; + expect(msngr.debug).to.equal(false); + msngr.debug = true; + expect(msngr.internal).to.exist; + expect(msngr.debug).to.equal(true); + msngr.debug = false; + expect(msngr.internal).to.not.exist; + }); - expect(obj2.this).to.exist; - expect(obj2.this.is).to.exist; - expect(obj2.this.is.a).to.exist; - expect(obj2.this.is.a.test).to.exist; - expect(obj2.this.is.a.test.yup).to.exist; - expect(obj2.this.is.a.test.yup()).to.equal("yup!"); + it("msngr.warnings - can set the property to true or false", function () { + expect(msngr.warnings).to.equal(true); + msngr.warnings = false; + expect(msngr.warnings).to.equal(false); }); }); diff --git a/src/main.js b/src/main.js index d0d22a5..526274d 100644 --- a/src/main.js +++ b/src/main.js @@ -1,28 +1,149 @@ +/* + main.js + The main entry point for msngr.js. Covers internal and external interface generation, + versioning (for programmatic access) and the core extend method. +*/ var msngr = msngr || (function () { "use strict"; - return { - version: "1.1.0", - extend: function (obj, target) { - target = (target || msngr); - if (Object.prototype.toString.call(obj) === "[object Object]") { - for (var key in obj) { - if (obj.hasOwnProperty(key)) { - if (Object.prototype.toString.call(obj[key]) === "[object Object]") { - if (target[key] === undefined) { - target[key] = { }; - } - target[key] = msngr.extend(obj[key], target[key]); - } else if (Object.prototype.toString.call(obj[key]) === "[object Array]") { - target[key] = (target[key] || []).concat(obj[key]); - } else { - target[key] = obj[key]; + // Defaults for some internal functions + var internal = { + warnings: true + }; + + // The main method for msngr uses the message object + var external = function (topic, category, dataType) { + return internal.objects.message(topic, category, dataType); + }; + + external.version = "2.0.0"; + + // Merge two inputs into one + var twoMerge = function (input1, input2) { + if (input1 === undefined || input1 === null) { + return input2; + } + + if (input2 === undefined || input2 === null) { + return input1; + } + + var result; + var type1 = Object.prototype.toString.call(input1); + var type2 = Object.prototype.toString.call(input2); + + if (type1 === "[object Object]" && type2 === "[object Object]") { + // Object merging time! + result = { }; + // Copy input1 into result + for (var key in input1) { + if (input1.hasOwnProperty(key)) { + result[key] = input1[key]; + } + } + for (var key in input2) { + if (input2.hasOwnProperty(key)) { + if (Object.prototype.toString.call(input2[key]) === "[object Object]") { + if (result[key] === undefined) { + result[key] = { }; } + result[key] = external.merge(input1[key], input2[key]); + } else if (Object.prototype.toString.call(input1[key]) === "[object Array]" && Object.prototype.toString.call(input2[key]) === "[object Array]") { + result[key] = (input1[key] || []).concat(input2[key]); + } else { + result[key] = input2[key]; } } } - return target; + return result; + } + + if (type1 === "[object String]" && type2 === "[object String]") { + result = input1 + input2; + return result; } + + if (type1 === "[object Array]" && type2 === "[object Array]") { + result = input1.concat(input2); + return result; + } + + if (type1 === "[object Function]" && type2 === "[object Function]") { + return (function (i1, i2, args) { + return function () { + return external.merge(i1.apply(this, args), i2.apply(this, args)); + }; + }(input1, input2, arguments)); + } + + var similarObjectTypes = ["[object Function]", "[object Object]"]; + + if (similarObjectTypes.indexOf(type1) !== -1 && similarObjectTypes.indexOf(type2) !== -1) { + var method = (type1 === "[object Function]") ? input1 : input2; + var props = (type1 === "[object Object]") ? input1 : input2; + + if (method !== undefined && props !== undefined) { + for (var key in props) { + if (props.hasOwnProperty(key)) { + method[key] = props[key]; + } + } + } + result = method; + return result; + } + + return result; }; + + external.extend = function (obj, target) { + target = (target || external); + + if (Object.prototype.toString.call(obj) === "[object Function]") { + obj = obj.apply(this, [external, internal]); + } + + target = external.merge(obj, target); + + return target; + }; + + external.merge = function () { + var result; + if (arguments.length > 0) { + for (var i = 0; i < arguments.length; ++i) { + result = twoMerge(result, arguments[i]); + } + } + + return result; + }; + + // Create a debug property to allow explicit exposure to the internal object structure. + // This should only be used during unit test runs and debugging. + Object.defineProperty(external, "debug", { + set: function (value) { + if (value === true) { + external.internal = internal; + } else if (value === false) { + delete external.internal; + } + }, + get: function () { + return (external.internal !== undefined) + } + }); + + // This governs warning messages that some methods may spit into the console when warranted (du'h). + Object.defineProperty(external, "warnings", { + set: function (value) { + internal.warnings = value; + }, + get: function () { + return internal.warnings; + } + }); + + return external; }()); diff --git a/src/messengers/bind.cspec.js b/src/messengers/bind.cspec.js deleted file mode 100644 index 94aa7a7..0000000 --- a/src/messengers/bind.cspec.js +++ /dev/null @@ -1,165 +0,0 @@ -if (typeof chai === "undefined" && typeof window === "undefined") { - var chai = require("chai"); -} - -if (typeof expect === "undefined") { - var expect = chai.expect; -} - -if (typeof msngr === "undefined" && typeof window === "undefined") { - var msngr = require("../../msngr"); -} - -describe("./messengers/bind.js", function () { - - beforeEach(function (done) { - msngr.dropAll(); - done(); - }); - - it("msngr.bind(element, event, topic) - Binds and sends with just a topic", function (done) { - var div = document.createElement("div"); - - msngr.bind(div, "testEvent", "MyTopic"); - - msngr.on("MyTopic", function (payload) { - expect(payload).to.exist; - done(); - }); - - var testEvent = document.createEvent("CustomEvent"); - testEvent.initCustomEvent("testEvent", false, false, null); - div.dispatchEvent(testEvent); - }); - - it("msngr.bind(element, event, topic, category) - Binds and sends with a topic and category", function (done) { - var div = document.createElement("div"); - - msngr.bind(div, "testEvent", "MyTopic", "MyCategory"); - - msngr.on("MyTopic", "MyCategory", function (payload) { - expect(payload).to.exist; - done(); - }); - - var testEvent = document.createEvent("CustomEvent"); - testEvent.initCustomEvent("testEvent", false, false, null); - div.dispatchEvent(testEvent); - }); - - it("msngr.bind(element, event, topic, category, dataType) - Binds and sends with a topic, category and dataType", function (done) { - var div = document.createElement("div"); - - msngr.bind(div, "testEvent", "MyTopic", "MyCategory", "MyDataType"); - - msngr.on("MyTopic", "MyCategory", "MyDataType", function (payload) { - expect(payload).to.exist; - done(); - }); - - var testEvent = document.createEvent("CustomEvent"); - testEvent.initCustomEvent("testEvent", false, false, null); - div.dispatchEvent(testEvent); - }); - - it("msngr.bind(element, event, message) - Binds and sends with a message object with a topic, category and dataType", function (done) { - var div = document.createElement("div"); - - msngr.bind(div, "testEvent", { topic: "MyTopic", category: "MyCategory", dataType: "MyDataType" }); - - msngr.on("MyTopic", "MyCategory", "MyDataType", function (payload) { - expect(payload).to.exist; - done(); - }); - - var testEvent = document.createEvent("CustomEvent"); - testEvent.initCustomEvent("testEvent", false, false, null); - div.dispatchEvent(testEvent); - }); - - it("msngr.bind(element, event, topic, category) - Binds and sends with a topic and category", function (done) { - var div = document.createElement("div"); - - msngr.bind(div, "testEvent", "MyTopic", "MyCategory"); - - msngr.on("MyTopic", "MyCategory", function (payload) { - expect(payload).to.exist; - done(); - }); - - var testEvent = document.createEvent("CustomEvent"); - testEvent.initCustomEvent("testEvent", false, false, null); - div.dispatchEvent(testEvent); - }); - - it("msngr.bind(element, event, topic, category) - Bind then remove doesn't emit message", function (done) { - var div = document.createElement("div"); - - msngr.bind(div, "testEvent", "MyTopic", "MyCategory"); - var flag = false; - msngr.on("MyTopic", "MyCategory", function (payload) { - flag = true; - expect(flag).to.equal(false); - done(); - }); - - var testEvent = document.createEvent("CustomEvent"); - testEvent.initCustomEvent("testEvent", false, false, null); - - msngr.unbind(div, "testEvent"); - - div.dispatchEvent(testEvent); - - setTimeout(function () { - expect(flag).to.equal(false); - done(); - }, 250); - }); - - it("msngr.unbind(element, event) - Unbind and ensure the message originally bound does not get sent", function (done) { - var div = document.createElement("div"); - - msngr.bind(div, "testEvent1", "MyTopic1", "MyCategory1"); - msngr.bind(div, "testEvent2", "MyTopic2", "MyCategory2"); - var flag = false; - - msngr.on("MyTopic1", "MyCategory1", function () { - flag = true; - }); - - msngr.on("MyTopic2", "MyCategory2", function () { - flag = true; - }); - - var testEvent1 = document.createEvent("CustomEvent"); - testEvent1.initCustomEvent("testEven1", false, false, null); - - var testEvent2 = document.createEvent("CustomEvent"); - testEvent2.initCustomEvent("testEven1", false, false, null); - - msngr.unbind(div, "testEvent1"); - msngr.unbind(div, "testEvent2"); - - div.dispatchEvent(testEvent1); - div.dispatchEvent(testEvent2); - - setTimeout(function () { - expect(flag).to.equal(false); - done(); - }, 250); - }); - - it("msngr.getBindCount() - Accurately tracks the bind count", function () { - var div = document.createElement("div"); - - var start = msngr.getBindCount(); - msngr.bind(div, "testEvent1", "MyTopic1", "MyCategory1"); - msngr.bind(div, "testEvent2", "MyTopic2", "MyCategory2"); - - expect(msngr.getBindCount()).to.equal(start + 2); - - msngr.unbind(div, "testEvent1"); - - expect(msngr.getBindCount()).to.equal(start + 1); - }); -}); diff --git a/src/messengers/bind.js b/src/messengers/bind.js deleted file mode 100644 index 51cc4b3..0000000 --- a/src/messengers/bind.js +++ /dev/null @@ -1,96 +0,0 @@ -msngr.extend((function () { - "use strict"; - - // Throw statements - var InvalidParametersException = function (str) { - return { - severity: "unrecoverable", - message: ("Invalid parameters supplied to the {method} method".replace("{method}", str)) - }; - }; - - var UnexpectedException = function (str) { - return { - severity: "unrecoverable", - message: ("An unexpected exception occured in the {method} method".replace("{method}", str)) - }; - }; - - var registerdPaths = { }; - var registerdEvents = 0; - - var listener = function (event) { - var node = this; - var path = msngr.utils.getDomPath(node); - - if (msngr.utils.exist(registerdPaths[path])) { - if (msngr.utils.exist(registerdPaths[path][event.type])) { - return msngr.emit(registerdPaths[path][event.type], event); - } - } - - // How did we get here? Must be a memory leak or something. Ugh - return msngr; - }; - - return { - bind: function (element, event, topic, category, dataType) { - if (!msngr.utils.exist(element) || !msngr.utils.exist(event) || !msngr.utils.exist(topic)) { - throw InvalidParametersException("bind"); - } - if (msngr.utils.isObject(topic) && !msngr.utils.exist(topic.topic)) { - throw InvalidParametersException("bind"); - } - - var node = msngr.utils.findElement(element); - var path = msngr.utils.getDomPath(node); - - if (!msngr.utils.exist(registerdPaths[path])) { - registerdPaths[path] = { }; - } - - var message = undefined; - if (msngr.utils.isObject(topic)) { - message = topic; - } else { - message = { }; - message.topic = topic; - - if (msngr.utils.exist(category)) { - message.category = category; - } - - if (msngr.utils.exist(dataType)) { - message.dataType = dataType; - } - } - - registerdPaths[path][event] = message; - - node.addEventListener(event, listener); - - registerdEvents++; - - return msngr; - }, - unbind: function (element, event) { - var node = msngr.utils.findElement(element); - var path = msngr.utils.getDomPath(node); - - if (msngr.utils.exist(registerdPaths[path])) { - if (msngr.utils.exist(registerdPaths[path][event])) { - node.removeEventListener(event, listener); - - delete registerdPaths[path][event]; - - registerdEvents--; - } - } - - return msngr; - }, - getBindCount: function () { - return registerdEvents; - } - }; -}())); diff --git a/src/messengers/mitter.aspec.js b/src/messengers/mitter.aspec.js deleted file mode 100644 index a89d107..0000000 --- a/src/messengers/mitter.aspec.js +++ /dev/null @@ -1,327 +0,0 @@ -if (typeof chai === "undefined" && typeof window === "undefined") { - var chai = require("chai"); -} - -if (typeof expect === "undefined") { - var expect = chai.expect; -} - -if (typeof msngr === "undefined" && typeof window === "undefined") { - var msngr = require("../../msngr"); -} - -describe("./messengers/mitter.js", function () { - "use strict"; - - beforeEach(function (done) { - msngr.dropAll(); - done(); - }); - - it("msngr.emit('TestTopic', { str: 'Hello, World Payload!' }) is received by msngr.on('TestTopic', function (payload))", function (done) { - expect(msngr.getMessageCount()).to.exist; - expect(msngr.getMessageCount()).to.equal(0); - - msngr.on("TestTopic", function (payload) { - expect(payload).to.exist; - expect(payload.str).to.equal("Hello, World Payload!"); - - done(); - }); - - msngr.emit("TestTopic", { str: "Hello, World Payload!" }); - }); - - it("msngr.emit('TestTopic', 'TestCategory', { str: 'MyPayload' }) is received by msngr.on('TestTopic', 'TestCategory', function (payload))", function (done) { - expect(msngr.getMessageCount()).to.exist; - expect(msngr.getMessageCount()).to.equal(0); - - msngr.on("TestTopic", "TestCategory", function (payload) { - expect(payload).to.exist; - expect(payload.str).to.equal("MyPayload"); - - done(); - }); - - msngr.emit("TestTopic", "TestCategory", { str: "MyPayload" }); - }); - - it("msngr.emit('TestTopic', 'TestCategory', 'TestDataType', { str: 'AnotherPayload' }) is received by msngr.on('TestTopic', 'TestCategory', 'TestDataType', function (payload))", function (done) { - expect(msngr.getMessageCount()).to.exist; - expect(msngr.getMessageCount()).to.equal(0); - - msngr.on("TestTopic", "TestCategory", "TestDataType", function (payload) { - expect(payload).to.exist; - expect(payload.str).to.equal("AnotherPayload"); - - done(); - }); - - msngr.emit("TestTopic", "TestCategory", "TestDataType", { str: "AnotherPayload" }); - }); - - it("msngr.emit({ topic: 'TestTopic' }, 'WeePayload') is received by msngr.on({ topic: 'TestTopic' }, function (payload))", function (done) { - expect(msngr.getMessageCount()).to.exist; - expect(msngr.getMessageCount()).to.equal(0); - - msngr.on({ topic: "TestTopic" }, function (payload) { - expect(payload).to.exist; - expect(payload).to.equal("WeePayload"); - - done(); - }); - - msngr.emit({ topic: "TestTopic" }, "WeePayload"); - }); - - it("msngr.emit({ topic: 'TestTopic', category: 'TestCategory' }, 'PayloadWUT') is received by msngr.on({ topic: 'TestTopic', category: 'TestCategory' }, function (payload))", function (done) { - expect(msngr.getMessageCount()).to.exist; - expect(msngr.getMessageCount()).to.equal(0); - - msngr.on({ topic: "TestTopic", category: "TestCategory" }, function (payload) { - expect(payload).to.exist; - expect(payload).to.equal("PayloadWUT"); - - done(); - }); - - msngr.emit({ topic: "TestTopic", category: "TestCategory" }, "PayloadWUT"); - }); - - it("msngr.emit({ topic: 'TestTopic', category: 'TestCategory', dataType: 'TestType' }, 'MY PAYLOAD') is received by msngr.on({ topic: 'TestTopic', category: 'TestCategory', dataType: 'TestType' }, function (payload))", function (done) { - expect(msngr.getMessageCount()).to.exist; - expect(msngr.getMessageCount()).to.equal(0); - - msngr.on({ topic: "TestTopic", category: "TestCategory", dataType: "TestType" }, function (payload) { - expect(payload).to.exist; - expect(payload).to.equal("MY PAYLOAD"); - - done(); - }); - - msngr.emit({ topic: "TestTopic", category: "TestCategory", dataType: "TestType" }, "MY PAYLOAD"); - }); - - it("msngr.emit('TestTopic1') is received by msngr.on('TestTopic1') and not msngr.on('TestTopic2')", function (done) { - expect(msngr.getMessageCount()).to.exist; - expect(msngr.getMessageCount()).to.equal(0); - - var counter = 0; - var cat1 = undefined; - var cat2 = undefined; - - msngr.on("TestTopic1", function () { - counter++; - cat1 = true; - }); - - msngr.on("TestTopic2", function () { - counter++; - cat2 = true; - }); - - msngr.emit("TestTopic1"); - - setTimeout(function () { - expect(cat1).to.equal(true); - expect(cat2).to.equal(undefined); - expect(counter).to.equal(1); - done(); - }, 250); - }); - - it("msngr.emit('TestTopic1', 'TestCategory1') is received by msngr.on('TestTopic1', 'TestCategory1') and not msngr.on('TestTopic1', 'TestCategory2')", function (done) { - expect(msngr.getMessageCount()).to.exist; - expect(msngr.getMessageCount()).to.equal(0); - - var counter = 0; - var cat1 = undefined; - var cat2 = undefined; - - msngr.on("TestTopic1", "TestCategory1", function () { - counter++; - cat1 = true; - }); - - msngr.on("TestTopic1", "TestCategory2", function () { - counter++; - cat2 = true; - }); - - msngr.emit("TestTopic1", "TestCategory1"); - - setTimeout(function () { - expect(cat1).to.equal(true); - expect(cat2).to.equal(undefined); - expect(counter).to.equal(1); - done(); - }, 250); - }); - - it("msngr.emit('TestTopic1', undefined, 'DataType1') is received by msngr.on('TestTopic1', undefined, 'DataType1') and not msngr.on('TestTopic1', undefined, 'DataType2')", function (done) { - expect(msngr.getMessageCount()).to.exist; - expect(msngr.getMessageCount()).to.equal(0); - - var counter = 0; - var cat1 = undefined; - var cat2 = undefined; - - msngr.on("TestTopic1", undefined, "DataType1", function () { - counter++; - cat1 = true; - }); - - msngr.on("TestTopic2", undefined, "DataType2", function () { - counter++; - cat2 = true; - }); - - msngr.emit("TestTopic1", undefined, "DataType1"); - - setTimeout(function () { - expect(cat1).to.equal(true); - expect(cat2).to.equal(undefined); - expect(counter).to.equal(1); - done(); - }, 250); - }); - - it("msngr.emit('TestTopic1', 'TestCategory1', 'TestDataType1') is received by msngr.on('TestTopic1', 'TestCategory1', 'TestDataType1') and not msngr.on('TestTopic1', 'TestCategory1', 'TestDataType2')", function (done) { - expect(msngr.getMessageCount()).to.exist; - expect(msngr.getMessageCount()).to.equal(0); - - var counter = 0; - var cat1 = undefined; - var cat2 = undefined; - - msngr.on("TestTopic1", "TestCategory1", "TestDataType1", function () { - counter++; - cat1 = true; - }); - - msngr.on("TestTopic1", "TestCategory1", "TestDataType2", function () { - counter++; - cat2 = true; - }); - - msngr.emit("TestTopic1", "TestCategory1", "TestDataType1"); - - setTimeout(function () { - expect(cat1).to.equal(true); - expect(cat2).to.equal(undefined); - expect(counter).to.equal(1); - done(); - }, 250); - }); - - it("msngr.emit('TestTopic1') is received by multiple msngr.on('TestTopic1')", function (done) { - var count = 0; - var hits = { one: undefined, two: undefined, three: undefined }; - - msngr.on("TestTopic1", function () { - count++; - hits.one = true; - }); - - msngr.on("TestTopic1", function () { - count++; - hits.two = true; - }); - - msngr.on("TestTopic1", function () { - count++; - hits.three = true; - }); - - msngr.emit("TestTopic1"); - - setTimeout(function () { - expect(count).to.equal(3); - expect(hits.one).to.exist; - expect(hits.two).to.exist; - expect(hits.three).to.exist; - done(); - }, 250); - }); - - it("msngr.drop('TestTopic') drops msngr.on('TestTopic')", function (done) { - expect(msngr.getMessageCount()).to.exist; - expect(msngr.getMessageCount()).to.equal(0); - - msngr.on("TestTopic", function () { }); - - expect(msngr.getMessageCount()).to.equal(1); - - msngr.drop("TestTopic"); - - expect(msngr.getMessageCount()).to.equal(0); - - done(); - }); - - it("msngr.drop('TestTopic', 'TestCategory') drops msngr.on('TestTopic', 'TestCategory')", function (done) { - expect(msngr.getMessageCount()).to.exist; - expect(msngr.getMessageCount()).to.equal(0); - - msngr.on("TestTopic", "TestCategory", function () { }); - - expect(msngr.getMessageCount()).to.equal(1); - - msngr.drop("TestTopic", "TestCategory"); - - expect(msngr.getMessageCount()).to.equal(0); - - done(); - }); - - it("msngr.drop('TestTopic', 'TestCategory', 'TestDataType') drops msngr.on('TestTopic', 'TestCategory', 'TestDataType')", function (done) { - expect(msngr.getMessageCount()).to.exist; - expect(msngr.getMessageCount()).to.equal(0); - - msngr.on("TestTopic", "TestCategory", "TestDataType", function () { }); - - expect(msngr.getMessageCount()).to.equal(1); - - msngr.drop("TestTopic", "TestCategory", "TestDataType"); - - expect(msngr.getMessageCount()).to.equal(0); - - done(); - }); - - it("msngr.drop('TestTopic', callback) drops specific callbacks and not the entire message", function (done) { - var func1Ran = false; - var func2Ran = false; - - var func1 = function () { - func1Ran = true; - } - - var func2 = function () { - func2Ran = true; - } - - msngr.on("TestTopic", func1); - msngr.on("TestTopic", func2); - - msngr.emit("TestTopic"); - - setTimeout(function () { - expect(func1Ran).to.equal(true); - expect(func2Ran).to.equal(true); - - func1Ran = false; - func2Ran = false; - - msngr.drop("TestTopic", func2); - - msngr.emit("TestTopic"); - - setTimeout(function () { - expect(func1Ran).to.equal(true); - expect(func2Ran).to.equal(false); - done(); - }, 250); - }, 250); - }); -}); diff --git a/src/messengers/mitter.js b/src/messengers/mitter.js deleted file mode 100644 index 9ab6772..0000000 --- a/src/messengers/mitter.js +++ /dev/null @@ -1,226 +0,0 @@ -msngr.extend((function () { - "use strict"; - - // Throw statements - var InvalidParameters = function (str) { - return { - severity: "unrecoverable", - message: ("Invalid parameters supplied to the {method} method".replace("{method}", str)) - }; - }; - - var delegates = { }; - var delegateCount = 0; - - var executeSync = function (method, context, params, message) { - (function (m, c, p, msg) { - var cont = true; - var wrap = { - preventDefault: function () { - cont = false; - }, - payload: p[0], - done: function () { - if (cont === true) { - m.apply(c, [wrap.payload]); - } - } - }; - msngr.act(msg, wrap); - }(method, context, params, message)); - }; - - var execute = function (method, context, params, message) { - (function (m, c, p, msg) { - setTimeout(function () { - executeSync(m, c, p, msg); - }, 0); - }(method, context, params, message)); - }; - - var _emit = function (message, payload, callback) { - var uuids = msngr.store.query(message); - if (uuids.length > 0) { - for (var i = 0; i < uuids.length; ++i) { - var del = delegates[uuids[i]]; - var params = []; - if (msngr.utils.exist(payload || message.payload)) { - params.push(payload || message.payload); - } - execute(del.callback, del.context, params, message); - } - } - - return msngr; - }; - - var _on = function (message, callback) { - var uuid = msngr.store.index(message); - delegates[uuid] = { - callback: callback, - context: (message.context || this), - onedMessage: message - }; - delegateCount++; - - return msngr; - }; - - var _drop = function (message, func) { - var uuids = msngr.store.query(message); - if (uuids.length > 0) { - for (var i = 0; i < uuids.length; ++i) { - var uuid = uuids[i]; - if (msngr.utils.exist(func)) { - if (delegates[uuid].callback === func) { - delete delegates[uuid]; - delegateCount--; - - msngr.store.delete(uuid); - } - } else { - delete delegates[uuid]; - delegateCount--; - - msngr.store.delete(uuid); - } - } - } - - return msngr; - }; - - return { - emit: function (topic, category, dataType, payload, callback) { - if (!msngr.utils.exist(topic)) { - throw InvalidParameters("emit"); - } - - var message; - if (msngr.utils.isObject(topic)) { - message = topic; - if (!msngr.utils.exist(payload) && msngr.utils.exist(category)) { - payload = category; - } - if (!msngr.utils.exist(callback) && msngr.utils.exist(dataType) && msngr.utils.isFunction(dataType)) { - callback = dataType; - } - return _emit(message, payload, callback); - } - - message = { }; - var args = msngr.utils.argumentsToArray(arguments); - - message.topic = args.shift(); - - if (!msngr.utils.exist(payload)) { - if (args.length > 0 && msngr.utils.isObject(args[0])) { - payload = args.shift(); - - return _emit(message, payload); - } - } - - message.category = args.shift(); - - if (args.length > 0 && msngr.utils.isObject(args[0])) { - payload = args.shift(); - - return _emit(message, payload); - } - message.dataType = args.shift(); - - return _emit(message, payload); - }, - on: function (topic, category, dataType, callback) { - if (!msngr.utils.exist(topic)) { - throw InvalidParameters("on"); - } - - var message; - if (msngr.utils.isObject(topic)) { - message = topic; - if (!msngr.utils.exist(callback) && msngr.utils.exist(category)) { - callback = category; - } - return _on(message, callback); - } - if (arguments.length > 1) { - message = { }; - var args = msngr.utils.argumentsToArray(arguments); - - message.topic = args.shift(); - - message.category = args.shift(); - message.dataType = args.shift(); - - callback = callback || args.pop(); - - if (msngr.utils.isFunction(message.category) && !msngr.utils.exist(message.dataType)) { - callback = message.category; - delete message.category; - delete message.dataType; - } - - if (msngr.utils.isFunction(message.dataType) && msngr.utils.exist(message.category)) { - callback = message.dataType; - delete message.dataType; - } - - return _on(message, callback); - } - - throw InvalidParameters("on"); - }, - drop: function (topic, category, dataType, callback) { - if (!msngr.utils.exist(topic)) { - throw InvalidParameters("drop"); - } - - var message; - if (msngr.utils.isObject(topic)) { - message = topic; - if (!msngr.utils.exist(callback) && msngr.utils.exist(category)) { - callback = category; - } - return _drop(message, callback); - } - if (arguments.length > 0) { - message = { }; - var args = msngr.utils.argumentsToArray(arguments); - - message.topic = args.shift(); - - message.category = args.shift(); - message.dataType = args.shift(); - - callback = callback || args.pop(); - - if (msngr.utils.isFunction(message.category) && !msngr.utils.exist(message.dataType)) { - callback = message.category; - delete message.category; - delete message.dataType; - } - - if (msngr.utils.isFunction(message.dataType) && msngr.utils.exist(message.category)) { - callback = message.dataType; - delete message.dataType; - } - - return _drop(message, callback); - } - - throw InvalidParameters("drop"); - }, - dropAll: function () { - delegates = { }; - delegateCount = 0; - msngr.store.clear(); - - return msngr; - }, - getMessageCount: function () { - return delegateCount; - } - }; -}())); diff --git a/src/module.exports.js b/src/module.exports.js index 83a7958..d373d94 100644 --- a/src/module.exports.js +++ b/src/module.exports.js @@ -1,3 +1,8 @@ +/* + module.exports.js + + If we're running in a node.js / io.js context then export msngr otherwise do nothing. +*/ if (typeof module !== "undefined" && typeof module.exports !== "undefined") { module.exports = msngr; } diff --git a/src/objects/executer.aspec.js b/src/objects/executer.aspec.js new file mode 100644 index 0000000..f2d288a --- /dev/null +++ b/src/objects/executer.aspec.js @@ -0,0 +1,119 @@ +if (typeof chai === "undefined" && typeof window === "undefined") { + var chai = require("chai"); +} + +if (typeof expect === "undefined") { + var expect = chai.expect; +} + +if (typeof msngr === "undefined" && typeof window === "undefined") { + var msngr = require("../../msngr"); +} + +describe("./objects/executer.js", function () { + "use strict"; + + before(function () { + msngr.debug = true; + }); + + after(function () { + msngr.debug = false; + }); + + + it("msngr.internal.objects.executer(method, payload, context).execute(done) - executes and returns a result from a sync method", function (done) { + var myFunc = function (payload, async) { + return 15; + }; + + msngr.internal.objects.executer(myFunc, undefined, this).execute(function (result) { + expect(result).to.exist; + expect(result).to.equal(15); + done(); + }); + }); + + it("msngr.internal.objects.executer(method, payload, context).execute(done) - executes and returns a result from an async method", function (done) { + var myFunc = function (payload, async) { + var d = async(); + d(42); + }; + + msngr.internal.objects.executer(myFunc, undefined, this).execute(function (result) { + expect(result).to.existl + expect(result).to.equal(42); + done(); + }); + }); + + it("msngr.internal.objects.executer(method, payload, context).execute(done) - done is executed even with no methods", function (done) { + msngr.internal.objects.executer([], undefined, this).execute(function (result) { + expect(result).to.exist; + expect(result.length).to.equal(0); + done(); + }); + }); + + it("msngr.internal.objects.executer(method, payload, context).parallel(done) - done is executed even with no methods", function (done) { + msngr.internal.objects.executer([], undefined, this).parallel(function (result) { + expect(result).to.exist; + expect(result.length).to.equal(0); + done(); + }); + }); + + it("msngr.internal.objects.executer(methods, payload, context).parallel(done) - executes multiple methods and aggregates results", function (done) { + var func1 = function (payload, async) { + expect(payload.t).to.exist; + expect(payload.t).to.equal(false); + return 1; + } + + var func2 = function (payload, async) { + expect(payload.t).to.exist; + expect(payload.t).to.equal(false); + return "test"; + } + + var func3 = function (payload, async) { + expect(payload.t).to.exist; + expect(payload.t).to.equal(false); + var d = async(); + d("whatever!"); + } + + var func4 = function (payload, async) { + expect(payload.t).to.exist; + expect(payload.t).to.equal(false); + return 97; + } + + var func5 = function (payload, async) { + expect(payload.t).to.exist; + expect(payload.t).to.equal(false); + return 100; + } + + var func6 = function (payload, async) { + expect(payload.t).to.exist; + expect(payload.t).to.equal(false); + return true; + } + + var executer = msngr.internal.objects.executer([func1, func2, func3, func4, func5, func6], { t: false }, this); + executer.parallel(function (results) { + expect(results).to.exist; + expect(results.length).to.equal(6); + expect(results.indexOf(1)).to.not.equal(-1); + expect(results.indexOf("test")).to.not.equal(-1); + expect(results.indexOf("whatever!")).to.not.equal(-1); + expect(results.indexOf(97)).to.not.equal(-1); + expect(results.indexOf(100)).to.not.equal(-1); + expect(results.indexOf(true)).to.not.equal(-1); + + done(); + }); + }); + +}); diff --git a/src/objects/executer.js b/src/objects/executer.js new file mode 100644 index 0000000..31f06d8 --- /dev/null +++ b/src/objects/executer.js @@ -0,0 +1,76 @@ +msngr.extend((function (external, internal) { + "use strict"; + + internal.objects = internal.objects || { }; + internal.objects.executer = function (methods, payload, context) { + + if (external.isFunction(methods)) { + methods = [methods]; + } + + if (!external.exist(methods) || !external.isArray(methods)) { + throw internal.InvalidParametersException("executor"); + } + + var exec = function (method, pay, ctx, done) { + setTimeout(function () { + var async = false; + var async = function () { + async = true; + return function (result) { + done.apply(ctx, [result]); + }; + } + + var params = undefined; + if (external.isArray(pay)) { + params = pay; + } else { + params = [pay]; + } + params.push(async); + + var syncResult = method.apply(ctx || this, params); + if (async !== true) { + done.apply(ctx, [syncResult]); + } + }, 0); + }; + + return { + execute: function (done) { + if (methods.length === 0 && external.exist(done)) { + return done.apply(context, [[]]); + } + return exec(methods[0], payload, context, done); + }, + parallel: function (done) { + var results = []; + var executed = 0; + + if (methods.length === 0 && external.exist(done)) { + return done.apply(context, [[]]); + } + + for (var i = 0; i < methods.length; ++i) { + (function (m, p, c) { + exec(m, p, c, function (result) { + if (external.exist(result)) { + results.push(result); + } + + ++executed; + + if (executed === methods.length && external.exist(done)) { + done.apply(context, [results]); + } + }); + }(methods[i], payload, context)); + } + } + }; + }; + + // This is an internal extension; do not export explicitly. + return { }; +})); diff --git a/src/objects/memory.aspec.js b/src/objects/memory.aspec.js new file mode 100644 index 0000000..a9289b2 --- /dev/null +++ b/src/objects/memory.aspec.js @@ -0,0 +1,283 @@ +if (typeof chai === "undefined" && typeof window === "undefined") { + var chai = require("chai"); +} + +if (typeof expect === "undefined") { + var expect = chai.expect; +} + +if (typeof msngr === "undefined" && typeof window === "undefined") { + var msngr = require("../../msngr"); +} + +describe("./objects/memory.js", function () { + "use strict"; + + before(function () { + msngr.debug = true; + }); + + after(function () { + msngr.debug = false; + }); + + it("msngr.internal.store.index(message) - indexes a message with only a topic", function () { + var memory = msngr.internal.objects.memory(); + + var message = { + topic: "TestTopic1" + }; + + expect(memory.count).to.equal(0); + var id = memory.index(message); + expect(id).to.exist; + expect(memory.count).to.equal(1); + }); + + it("msngr.internal.store.index(message) - indexes a message with a topic and category", function () { + var memory = msngr.internal.objects.memory(); + + var message = { + topic: "TestTopic1", + category: "TestCategory1" + }; + + expect(memory.count).to.equal(0); + var id = memory.index(message); + expect(id).to.exist; + expect(memory.count).to.equal(1); + }); + + it("msngr.internal.store.index(message) - indexes a message with a topic and dataType", function () { + var memory = msngr.internal.objects.memory(); + + var message = { + topic: "TestTopic1", + dataType: "TestDataType1" + }; + + expect(memory.count).to.equal(0); + var id = memory.index(message); + expect(id).to.exist; + expect(memory.count).to.equal(1); + }); + + it("msngr.internal.store.index(message) - indexes a message with a topic, category and dataType", function () { + var memory = msngr.internal.objects.memory(); + + var message = { + topic: "TestTopic1", + category: "TestCategory1", + dataType: "TestDataType1" + }; + + expect(memory.count).to.equal(0); + var id = memory.index(message); + expect(id).to.exist; + expect(memory.count).to.equal(1); + }); + + it("msngr.internal.store.index(message) - invalid message shouldn't index", function () { + var memory = msngr.internal.objects.memory(); + + var message = { + cookie: "monster" + }; + + expect(memory.count).to.equal(0); + var id = memory.index(message); + expect(id).to.not.exist; + expect(memory.count).to.equal(0); + }); + + it("msngr.internal.store.delete(uuid) - deletes a valid uuid", function () { + var memory = msngr.internal.objects.memory(); + + var message = { + topic: "TestTopic1", + category: "TestCategory1", + dataType: "TestDataType1" + }; + + var id = memory.index(message); + expect(id).to.exist; + + var result = memory.delete(id); + expect(result).to.exist; + expect(result).to.equal(true); + expect(memory.count).to.equal(0); + }); + + it("msngr.internal.store.delete(uuid) - doesn't delete an invalid uuid", function () { + var memory = msngr.internal.objects.memory(); + + var result = memory.delete("sldfjslkfjlwrjlskdfjs"); + expect(result).to.exist; + expect(result).to.equal(false); + expect(memory.count).to.equal(0); + }); + + it("msngr.internal.store.query(message) - Correctly gets one result for a query on a topic", function () { + var memory = msngr.internal.objects.memory(); + + var message = { + topic: "TestTopic1" + }; + + var id = memory.index(message); + expect(id).to.exist; + + var result = memory.query({ topic: "TestTopic1" }); + + expect(result).to.exist; + expect(result.length).to.equal(1); + }); + + it("msngr.internal.store.query(message) - Correctly gets zero results for a query on a topic that doesn't exist", function () { + var memory = msngr.internal.objects.memory(); + + var result = memory.query({ topic: "TestTopic1" }); + + expect(result).to.exist; + expect(result.length).to.equal(0); + }); + + it("msngr.internal.store.query(message) - Correctly gets one result for a query on a topic and category", function () { + var memory = msngr.internal.objects.memory(); + + var message = { + topic: "TestTopic1", + category: "TestCategory1" + }; + + var id = memory.index(message); + expect(id).to.exist; + + var result = memory.query({ topic: "TestTopic1", category: "TestCategory1" }); + + expect(result).to.exist; + expect(result.length).to.equal(1); + }); + + it("msngr.internal.store.query(message) - Correctly gets zero results for a query on a topic and category that doesn't exist", function () { + var memory = msngr.internal.objects.memory(); + + var result = memory.query({ topic: "TestTopic1", category: "TestCategory1" }); + + expect(result).to.exist; + expect(result.length).to.equal(0); + }); + + it("msngr.internal.store.query(message) - Correctly gets one result for a query on a topic, category and dataType", function () { + var memory = msngr.internal.objects.memory(); + + var message = { + topic: "TestTopic1", + category: "TestCategory1", + dataType: "TestDataType1" + }; + + var id = memory.index(message); + expect(id).to.exist; + + var result = memory.query({ topic: "TestTopic1", category: "TestCategory1", dataType: "TestDataType1" }); + + expect(result).to.exist; + expect(result.length).to.equal(1); + }); + + it("msngr.internal.store.query(message) - Correctly gets zero results for a query on a topic, category and dataType that doesn't exist", function () { + var memory = msngr.internal.objects.memory(); + + var result = memory.query({ topic: "TestTopic1", category: "TestCategory1", dataType: "TestDataType1" }); + + expect(result).to.exist; + expect(result.length).to.equal(0); + }); + + it("msngr.internal.store.query(message) - Correctly gets zero results for a query on a topic, category and dataType where category doesn't exist", function () { + var memory = msngr.internal.objects.memory(); + + var message = { + topic: "TestTopic1", + category: "TestCategory1", + dataType: "TestDataType1" + }; + + var id = memory.index(message); + expect(id).to.exist; + + var result = memory.query({ topic: "TestTopic1", category: "TestCategory2", dataType: "TestDataType1" }); + + expect(result).to.exist; + expect(result.length).to.equal(0); + }); + + it("msngr.internal.store.query(message) - Correctly gets zero results for a query on a topic, category and dataType where dataType doesn't exist", function () { + var memory = msngr.internal.objects.memory(); + + var message = { + topic: "TestTopic1", + category: "TestCategory1", + dataType: "TestDataType1" + }; + + var id = memory.index(message); + expect(id).to.exist; + + var result = memory.query({ topic: "TestTopic1", category: "TestCategory1", dataType: "TestDataType2" }); + + expect(result).to.exist; + expect(result.length).to.equal(0); + }); + + it("msngr.internal.store.query(message) - Correctly gets zero results for a query on a topic, category and dataType where topic doesn't exist", function () { + var memory = msngr.internal.objects.memory(); + + var message = { + topic: "TestTopic1", + category: "TestCategory1", + dataType: "TestDataType1" + }; + + var id = memory.index(message); + expect(id).to.exist; + + var result = memory.query({ topic: "TestTopic2", category: "TestCategory1", dataType: "TestDataType1" }); + + expect(result).to.exist; + expect(result.length).to.equal(0); + }); + + it("msngr.internal.store.clear() - Clears all indexed messages", function () { + var memory = msngr.internal.objects.memory(); + + var ids = []; + ids.push(memory.index({ topic: "chicken" })); + ids.push(memory.index({ topic: "table", category: "50" })); + ids.push(memory.index({ topic: "stairs", category: "10", dataType: "food" })); + ids.push(memory.index({ topic: "whatevea", dataType: "punk" })); + + expect(memory.count).to.equal(4); + expect(memory.clear()).to.equal(true); + expect(memory.query({ topic: "chicken" }).length).to.equal(0); + expect(memory.query({ topic: "stairs", category: "10", dataType: "food" }).length).to.equal(0); + }); + + it("msngr.internal.store.count - Returns a correct count", function () { + var memory = msngr.internal.objects.memory(); + + expect(memory.count).to.equal(0); + memory.index({ topic: "chicken" }); + expect(memory.count).to.equal(1); + memory.index({ topic: "table", category: "50" }); + expect(memory.count).to.equal(2); + memory.index({ topic: "stairs", category: "10", dataType: "food" }); + expect(memory.count).to.equal(3); + expect(memory.clear()).to.equal(true); + expect(memory.count).to.equal(0); + memory.index({ topic: "whatevea", dataType: "punk" }); + expect(memory.count).to.equal(1); + + }); +}); diff --git a/src/objects/memory.js b/src/objects/memory.js new file mode 100644 index 0000000..23c0d1a --- /dev/null +++ b/src/objects/memory.js @@ -0,0 +1,161 @@ +msngr.extend((function (external, internal) { + "use strict"; + + internal.objects = internal.objects || { }; + internal.objects.memory = function () { + + // Index for id to message objects + var id_to_message = { }; + + // Direct index (no partials) for message + var direct_index = { + topic_to_id: { }, + topic_cat_to_id: { }, + topic_type_to_id: { }, + topic_cat_type_to_id: { } + }; + + // Message index count + var index_count = 0; + + var mem = { + index: function (message) { + if (external.exist(message) && external.exist(message.topic)) { + var uuid = external.id(); + id_to_message[uuid] = message; + + if (direct_index.topic_to_id[message.topic] === undefined) { + direct_index.topic_to_id[message.topic] = []; + } + direct_index.topic_to_id[message.topic].push(uuid); + + if (external.exist(message.category)) { + if (direct_index.topic_cat_to_id[message.topic] === undefined) { + direct_index.topic_cat_to_id[message.topic] = { }; + } + + if (direct_index.topic_cat_to_id[message.topic][message.category] === undefined) { + direct_index.topic_cat_to_id[message.topic][message.category] = []; + } + + direct_index.topic_cat_to_id[message.topic][message.category].push(uuid); + } + + if (external.exist(message.dataType)) { + if (direct_index.topic_type_to_id[message.topic] === undefined) { + direct_index.topic_type_to_id[message.topic] = { }; + } + + if (direct_index.topic_type_to_id[message.topic][message.dataType] === undefined) { + direct_index.topic_type_to_id[message.topic][message.dataType] = []; + } + + direct_index.topic_type_to_id[message.topic][message.dataType].push(uuid); + } + + if (external.exist(message.category) && external.exist(message.dataType)) { + if (direct_index.topic_cat_type_to_id[message.topic] === undefined) { + direct_index.topic_cat_type_to_id[message.topic] = { }; + } + + if (direct_index.topic_cat_type_to_id[message.topic][message.category] === undefined) { + direct_index.topic_cat_type_to_id[message.topic][message.category] = { }; + } + + if (direct_index.topic_cat_type_to_id[message.topic][message.category][message.dataType] === undefined) { + direct_index.topic_cat_type_to_id[message.topic][message.category][message.dataType] = []; + } + + direct_index.topic_cat_type_to_id[message.topic][message.category][message.dataType].push(uuid); + } + + index_count++; + + return uuid; + } + return undefined; + }, + delete: function (uuid) { + if (external.exist(uuid) && external.exist(id_to_message[uuid])) { + var message = id_to_message[uuid]; + + if (external.exist(message.topic)) { + external.removeFromArray(direct_index.topic_to_id[message.topic], uuid); + + if (external.exist(message.category)) { + external.removeFromArray(direct_index.topic_cat_to_id[message.topic][message.category], uuid); + } + + if (external.exist(message.dataType)) { + external.removeFromArray(direct_index.topic_type_to_id[message.topic][message.dataType], uuid); + } + + if (external.exist(message.category) && external.exist(message.dataType)) { + external.removeFromArray(direct_index.topic_cat_type_to_id[message.topic][message.category][message.dataType], uuid); + } + } + + delete id_to_message[uuid]; + index_count--; + + return true; + } + return false; + }, + query: function (message) { + if (external.exist(message)) { + if (external.exist(message.topic)) { + // Topic Only Results + if (!external.exist(message.category) && !external.exist(message.dataType)) { + return direct_index.topic_to_id[message.topic] || []; + } + + // Topic + Category Results + if (external.exist(message.category) && !external.exist(message.dataType)) { + return (direct_index.topic_cat_to_id[message.topic] || { })[message.category] || []; + } + + // Topic + Data Type Results + if (external.exist(message.dataType) && !external.exist(message.category)) { + return (direct_index.topic_type_to_id[message.topic] || { })[message.dataType] || []; + } + + // Topic + Category + Data Type Results + if (external.exist(message.category) && external.exist(message.dataType)) { + return ((direct_index.topic_cat_type_to_id[message.topic] || { })[message.category] || { })[message.dataType] || []; + } + } + } + + return []; + }, + clear: function () { + // Index for id to message objects + id_to_message = { }; + + // Direct index (no partials) for message + direct_index = { + topic_to_id: { }, + topic_cat_to_id: { }, + topic_type_to_id: { }, + topic_cat_type_to_id: { } + }; + + index_count = 0; + + return true; + } + }; + + Object.defineProperty(mem, "count", { + get: function () { + return index_count; + } + }); + + return mem; + }; + + // This is an internal extension; do not export explicitly. + return { }; +})); diff --git a/src/objects/message.aspec.js b/src/objects/message.aspec.js new file mode 100644 index 0000000..cb97c62 --- /dev/null +++ b/src/objects/message.aspec.js @@ -0,0 +1,422 @@ +if (typeof chai === "undefined" && typeof window === "undefined") { + var chai = require("chai"); +} + +if (typeof expect === "undefined") { + var expect = chai.expect; +} + +if (typeof msngr === "undefined" && typeof window === "undefined") { + var msngr = require("../../msngr"); +} + +describe("./objects/message.js", function () { + "use strict"; + + before(function () { + msngr.debug = true; + }); + + beforeEach(function () { + msngr.internal.reset(); + }); + + after(function () { + msngr.debug = false; + }); + + it("msngr() - throws an exception for invalid input", function () { + expect(msngr.bind([])).to.throw(); + expect(msngr.bind()).to.throw(); + expect(msngr.bind("")).to.throw(); + expect(msngr.bind(123)).to.throw(); + expect(msngr.bind((new Date()))).to.throw(); + }); + + it("msngr() - handles a message object as expected", function () { + var m = msngr({ topic: "MyTopic", category: "MyCategory", dataType: "MyDataType" }); + expect(m).to.exist; + expect(m.message).to.exist; + expect(m.message.topic).to.exist; + expect(m.message.topic).to.equal("MyTopic"); + expect(m.message.category).to.exist; + expect(m.message.category).to.equal("MyCategory"); + expect(m.message.dataType).to.exist; + expect(m.message.dataType).to.equal("MyDataType"); + }); + + it("msngr() - converts single string into message object with a topic", function () { + var m = msngr("MyTopic"); + expect(m).to.exist; + expect(m.message).to.exist; + expect(m.message.topic).to.exist; + expect(m.message.topic).to.equal("MyTopic"); + }); + + it("msngr() - converts two strings into message object with a topic and category", function () { + var m = msngr("MyTopic", "MyCategory"); + expect(m).to.exist; + expect(m.message).to.exist; + expect(m.message.topic).to.exist; + expect(m.message.topic).to.equal("MyTopic"); + expect(m.message.category).to.exist; + expect(m.message.category).to.equal("MyCategory"); + }); + + it("msngr() - converts three strings into message object with a topic, category and dataType", function () { + var m = msngr("MyTopic", "MyCategory", "MyDataType"); + expect(m).to.exist; + expect(m.message).to.exist; + expect(m.message.topic).to.exist; + expect(m.message.topic).to.equal("MyTopic"); + expect(m.message.category).to.exist; + expect(m.message.category).to.equal("MyCategory"); + expect(m.message.dataType).to.exist; + expect(m.message.dataType).to.equal("MyDataType"); + }); + + it("msngr.internal.handlerCount - returns the correct count of registered messages", function () { + expect(msngr.internal.handlerCount).to.exist; + expect(msngr.internal.handlerCount).to.equal(0); + msngr("MyTopic").on(function () { }); + expect(msngr.internal.handlerCount).to.equal(1); + msngr("AnotherTopic").on(function () { }); + expect(msngr.internal.handlerCount).to.equal(2); + }); + + it("msngr() - multiple copies do not share message objects", function () { + var m1 = msngr("MyTopic1"); + var m2 = msngr("MyTopic2", "MyCategory2"); + var m3 = msngr("MyTopic3", "MyCategory3", "MyDataType3"); + var m4 = msngr("MyTopic4", "MyCategory4"); + var m5 = msngr("MyTopic5"); + + expect(m1).to.exist; + expect(m2).to.exist; + expect(m3).to.exist; + expect(m4).to.exist; + expect(m5).to.exist; + + expect(m1.message.topic).to.not.equal(m2.message.topic); + expect(m2.message.topic).to.not.equal(m3.message.topic); + expect(m3.message.topic).to.not.equal(m4.message.topic); + expect(m4.message.topic).to.not.equal(m5.message.topic); + expect(m5.message.topic).to.not.equal(m1.message.topic); + + expect(m2.message.category).to.not.equal(m3.message.category); + expect(m3.message.dataType).to.not.equal(m4.message.dataType); + }); + + it("msngr().option() - handles invalid input correctly", function () { + expect(msngr("TestTopic").option.bind({})).to.throw; + expect(msngr("TestTopic").option.bind(7)).to.throw; + expect(msngr("TestTopic").option.bind()).to.throw; + }); + + it("msngr().option() - custom option processor works as expected", function (done) { + msngr.internal.options["testsync"] = function (message, payload, options, async) { + return "synced!"; + }; + + var msg = msngr("MyTopic").option("testsync").on(function (payload) { + expect(payload).to.exist; + expect(payload).to.equal("synced!"); + + msngr.internal.options["testasync"] = function (message, payload, options, async) { + var d = async(); + d({ words: "asynced!" }); + }; + + var msg2 = msngr("AnotherTopic").option("testasync").on(function (payload2) { + expect(payload2).to.exist; + expect(payload2.words).to.equal("asynced!"); + done(); + }).emit(); + }).emit(); + }); + + it("msngr().emit() / on() - Successfully emits and handles a topic only message", function (done) { + var msg = msngr("MyTopic"); + msg.on(function (payload) { + expect(payload).to.exist; + expect(payload).to.equal("MyPayload"); + done(); + }); + + msg.emit("MyPayload"); + }); + + it("msngr().emit() / on() - Successfully emits and handles a topic and category message", function (done) { + var msg = msngr("MyTopic", "MyCategory"); + msg.on(function (payload) { + expect(payload).to.exist; + expect(payload).to.equal("AnotherPayload"); + done(); + }); + + msg.emit("AnotherPayload"); + }); + + it("msngr().emit() / on() - Successfully emits and handles a topic, category and dataType message", function (done) { + var msg = msngr("MyTopic", "MyCategory", "MyDataType"); + msg.on(function (payload) { + expect(payload).to.exist; + expect(payload).to.equal("WeePayloads!"); + done(); + }); + + msg.emit("WeePayloads!"); + }); + + it("msngr().emit() / on() - Setup three handlers, both receive emit payload", function (done) { + var handled = 0; + + var msg = msngr("MyTopic", "MyCategory", "MyDataType"); + msg.on(function (payload) { + ++handled; + expect(payload).to.exist; + expect(payload).to.equal("ThreeHandlers"); + }); + + msg.on(function (payload) { + ++handled; + expect(payload).to.exist; + expect(payload).to.equal("ThreeHandlers"); + }); + + msg.on(function (payload) { + ++handled; + expect(payload).to.exist; + expect(payload).to.equal("ThreeHandlers"); + }); + + msg.emit("ThreeHandlers"); + + setTimeout(function () { + expect(handled).to.equal(3); + done(); + }, 250); + }); + + it("msngr().emit() / on() - Setup two handlers, delete one and the other still receives", function (done) { + var handled = 0; + var answer = undefined; + var onHandler1 = function (payload) { + ++handled; + answer = 1; + expect(payload).to.exist; + expect(payload).to.equal("TwoThenOne"); + }; + + var onHandler2 = function (payload) { + ++handled; + answer = 5; + expect(payload).to.exist; + expect(payload).to.equal("TwoThenOne"); + }; + + var msg = msngr("MyTopic", "MyCategory", "MyDataType"); + + msg.on(onHandler1); + msg.on(onHandler2); + + msg.drop(onHandler1); + + msg.emit("TwoThenOne"); + + setTimeout(function () { + expect(handled).to.equal(1); + expect(answer).to.exist; + expect(answer).to.equal(5); + done(); + }, 250); + }); + + it("msngr().emit() / on() - Multiple handlers, callback on emit aggregates results", function (done) { + var handled = 0; + + var msg = msngr("MyTopic", "MyCategory", "MyDataType"); + msg.on(function (payload) { + ++handled; + expect(payload).to.exist; + expect(payload).to.equal("ThreeHandlers"); + return "testering"; + }); + + msg.on(function (payload, async) { + var finished = async(); + ++handled; + expect(payload).to.exist; + expect(payload).to.equal("ThreeHandlers"); + finished(42); + }); + + msg.on(function (payload) { + ++handled; + expect(payload).to.exist; + expect(payload).to.equal("ThreeHandlers"); + return true; + }); + + msg.emit("ThreeHandlers", function (results) { + expect(handled).to.equal(3); + expect(results).to.exist; + expect(results.length).to.equal(3); + expect(results.indexOf("testering")).to.not.equal(-1); + expect(results.indexOf(42)).to.not.equal(-1); + expect(results.indexOf(true)).to.not.equal(-1); + done(); + }); + }); + + it("msngr().emit() / once() - once is only called one time for handling the same message", function (done) { + var handledCount = 0; + + var msg = msngr("MyTopicTest"); + msg.once(function (payload) { + ++handledCount; + }); + + msg.emit(); + msg.emit(); + msg.emit(); + msg.emit(); + + setTimeout(function () { + expect(handledCount).to.equal(1); + done(); + }, 250); + }); + + it("msngr().emit(func) - if a method is passed in as first argument then that is used as callback", function (done) { + var msg = msngr("TestTopical"); + var hitOn = false; + msg.on(function () { + hitOn = true; + }); + msg.emit(function () { + expect(hitOn).to.equal(true); + done(); + }); + }); + + it("msngr().persist().on() - persist stores and re-emits data with new on registrations", function (done) { + var handledCount = 0; + + var msg = msngr("AnotherTopic"); + msg.persist("PayloadTwo"); + msg.on(function (payload) { + ++handledCount; + expect(payload).to.exist; + expect(payload).to.equal("PayloadTwo"); + expect(handledCount).to.equal(1); + done(); + }); + msg.cease(); + }); + + it("msngr().on().persist() - registers handler then receives persisted payload", function (done) { + var handledCount = 0; + + var msg = msngr("MyTestingTopic"); + msg.on(function (payload) { + ++handledCount; + expect(payload).to.exist; + expect(payload).to.equal("MyPayload"); + expect(handledCount).to.equal(1); + done(); + }); + msg.persist("MyPayload"); + msg.cease(); + + }); + + it("msngr().once().persist() - registers handler then receives persisted payload", function (done) { + var handledCount = 0; + + var msg = msngr("MyTestingTopic"); + msg.once(function (payload) { + ++handledCount; + expect(payload).to.exist; + expect(payload).to.equal("MyPayload"); + expect(handledCount).to.equal(1); + done(); + }); + msg.persist("MyPayload"); + msg.cease(); + + }); + + it("msngr().persist().cease().on() - registers a payload, unregisters a payload then registers a handler", function (done) { + var handledCount = 0; + + var msg = msngr("MyTestingTopic"); + msg.persist("MyPayload"); + msg.cease(); + msg.on(function (payload) { + ++handledCount; + }); + + setTimeout(function () { + expect(handledCount).to.equal(0); + done(); + }, 250); + + }); + + it("msngr().persist() - complex merging of persisted objects for ultimate ready status", function (done) { + var msg = msngr("MyCoolTopic", "MyCat"); + var d = false; + msg.on(function (payload) { + // Check to make sure ready state is across the board + expect(payload).to.exist; + if (payload.ready1 === true && payload.ready2 === true && payload.ready3 === true && payload.ready4 === true && d === false) { + // All statuses have come in ready! + d = true; + done(); + } + }); + + msg.persist({ ready1: true }); + msg.persist({ ready2: true }); + msg.persist({ ready3: true }); + msg.persist({ ready4: true }); + }); + + it("msngr().persist() - passing in nothing still conducts a proper persisting of message execution in later 'on's", function (done) { + msngr("Server", "Ready").persist(); + + var calls = 0; + + msngr("Server", "Ready").on(function () { + ++calls; + }); + + msngr("Server", "Ready").on(function () { + ++calls; + }); + + msngr("Server", "Ready").on(function () { + ++calls; + }); + + setTimeout(function () { + expect(calls).to.equal(3); + done(); + }, 250); + }); + + it("msngr().counts - when debug mode is enabled returns a counts object otherwise is undefined", function () { + msngr.debug = true; + var msg = msngr("test"); + expect(msg.counts).to.exist; + expect(msg.counts.emits).to.equal(0); + msg.emit(); + expect(msg.counts.emits).to.equal(1); + + msngr.debug = false; + var msg2 = msngr("another"); + expect(msg2.counts).to.not.exist; + }); + +}); diff --git a/src/objects/message.cspec.js b/src/objects/message.cspec.js new file mode 100644 index 0000000..5c6ea64 --- /dev/null +++ b/src/objects/message.cspec.js @@ -0,0 +1,128 @@ +if (typeof chai === "undefined" && typeof window === "undefined") { + var chai = require("chai"); +} + +if (typeof expect === "undefined") { + var expect = chai.expect; +} + +if (typeof msngr === "undefined" && typeof window === "undefined") { + var msngr = require("../../msngr"); +} + +describe("./objects/message.js", function () { + "use strict"; + + before(function () { + msngr.debug = true; + }); + + beforeEach(function () { + msngr.internal.reset(); + }); + + after(function () { + msngr.debug = false; + }); + + it("msngr(topic).bind(element, event) - Binds and sends with just a topic", function (done) { + var div = document.createElement("div"); + + msngr("MyTopic").bind(div, "testEvent").on(function (payload) { + done(); + }); + + var testEvent = document.createEvent("CustomEvent"); + testEvent.initCustomEvent("testEvent", false, false, null); + div.dispatchEvent(testEvent); + }); + + it("msngr(topic, category).bind(element, event) - Binds and sends with a topic and category", function (done) { + var div = document.createElement("div"); + + msngr("MyTopic", "MyCategory").bind(div, "testEvent").on(function (payload) { + done(); + }); + + var testEvent = document.createEvent("CustomEvent"); + testEvent.initCustomEvent("testEvent", false, false, null); + div.dispatchEvent(testEvent); + }); + + it("msngr(topic, category, dataType).bind(element, event) - Binds and sends with a topic, category and dataType", function (done) { + var div = document.createElement("div"); + + msngr("MyTopic", "MyCategory", "MyDataType").bind(div, "testEvent").on(function (payload) { + done(); + }); + + var testEvent = document.createEvent("CustomEvent"); + testEvent.initCustomEvent("testEvent", false, false, null); + div.dispatchEvent(testEvent); + }); + + it("msngr(topic, category).bind(element, event) - Bind then remove doesn't emit message", function (done) { + var div = document.createElement("div"); + var flag = false; + + msngr("MyTopic", "MyCategory").bind(div, "testEvent").on(function (payload) { + flag = true; + expect(flag).to.equal(false); + done(); + }); + + var testEvent = document.createEvent("CustomEvent"); + testEvent.initCustomEvent("testEvent", false, false, null); + + msngr("MyTopic", "MyCategory").unbind(div, "testEvent"); + + div.dispatchEvent(testEvent); + + setTimeout(function () { + expect(flag).to.equal(false); + done(); + }, 250); + }); + + it("msngr(topic, category).unbind(element, event) - Unbind and ensure the message originally bound does not get sent", function (done) { + var div = document.createElement("div"); + var flag = false; + + msngr("MyTopic1", "MyCategory1").bind(div, "testEvent1").on(function () { + flag = true; + }).unbind(div, "testEvent1"); + + msngr("MyTopic2", "MyCategory2").bind(div, "testEvent2").on(function () { + flag = true; + }).unbind(div, "testEvent2"); + + var testEvent1 = document.createEvent("CustomEvent"); + testEvent1.initCustomEvent("testEven1", false, false, null); + + var testEvent2 = document.createEvent("CustomEvent"); + testEvent2.initCustomEvent("testEven1", false, false, null); + + div.dispatchEvent(testEvent1); + div.dispatchEvent(testEvent2); + + setTimeout(function () { + expect(flag).to.equal(false); + done(); + }, 250); + }); + + it("msngr.internal.boundCount - Accurately tracks the bind count", function () { + var div = document.createElement("div"); + + var start = msngr.internal.boundCount; + msngr("MyTopic1", "MyCategory1").bind(div, "testEvent1"); + msngr("MyTopic2", "MyCategory2").bind(div, "testEvent2"); + + expect(msngr.internal.boundCount).to.equal(start + 2); + + msngr("MyTopic1", "MyCategory1").unbind(div, "testEvent1"); + + expect(msngr.internal.boundCount).to.equal(start + 1); + }); + +}); diff --git a/src/objects/message.js b/src/objects/message.js new file mode 100644 index 0000000..bc44269 --- /dev/null +++ b/src/objects/message.js @@ -0,0 +1,354 @@ +/* + ./objects/message.js + + The primary object of msngr; handles all message sending, receiving and binding. +*/ +msngr.extend((function (external, internal) { + "use strict"; + + internal.objects = internal.objects || { }; + + var messageIndex = internal.objects.memory(); + var payloadIndex = internal.objects.memory(); + + var handlers = { }; + var handlerCount = 0; + + var payloads = { }; + var payloadCount = 0; + + var boundDOMPaths = { }; + var boundCount = 0; + + Object.defineProperty(internal, "handlerCount", { + get: function () { + return handlerCount; + } + }); + + Object.defineProperty(internal, "boundCount", { + get: function () { + return boundCount; + } + }); + + Object.defineProperty(internal, "payloadCount", { + get: function () { + return payloadCount; + } + }); + + internal.reset = function () { + handlers = { }; + boundDOMPaths = { }; + handlerCount = 0; + boundCount = 0; + messageIndex.clear(); + payloadIndex.clear(); + payloads = { }; + payloadCount = 0; + }; + + internal.processOpts = function (opts, message, payload, callback) { + var optProcessors = []; + for (var key in opts) { + if (opts.hasOwnProperty(key) && external.exist(internal.options[key])) { + optProcessors.push(internal.options[key]); + } + } + + // Short circuit for no options + if (optProcessors.length === 0) { + return callback.apply(this, [payload]); + } + + // Long circuit to do stuff (du'h) + var execs = internal.objects.executer(optProcessors, [message, payload, opts], this); + + execs.parallel(function (results) { + var result = payload; + if (external.exist(results) && results.length > 0) { + for (var i = 0; i < results.length; ++i) { + if (external.exist(results[i])) { + result = external.merge(results[i], result); + } + } + } + callback.apply(this, [result]); + }); + }; + + internal.domListener = function (event) { + var node = this; + var path = external.getDomPath(node); + + if (external.exist(boundDOMPaths[path])) { + if (external.exist(boundDOMPaths[path][event.type])) { + return boundDOMPaths[path][event.type].emit(); + } + } + }; + + internal.objects.message = function (topic, category, dataType) { + var msg = undefined; + if (!external.exist(topic)) { + throw internal.InvalidParametersException("msngr"); + } + + if (!external.isObject(topic) && !external.isString(topic)) { + throw internal.InvalidParametersException("msngr"); + } + + if (external.isEmptyString(topic)) { + throw internal.InvalidParametersException("msngr"); + } + + if (external.isObject(topic)) { + msg = topic; + } else { + msg = { }; + msg.topic = topic; + + if (!external.isEmptyString(category)) { + msg.category = category; + } + + if (!external.isEmptyString(dataType)) { + msg.dataType = dataType; + } + } + + var options = { }; + + var counts = { + emits: 0, + persists: 0, + options: 0, + ons: 0, + onces: 0, + binds: 0 + }; + + var explicitEmit = function (payload, uuids, callback) { + var uuids = uuids || messageIndex.query(msg) || []; + var methods = []; + var toDrop = []; + for (var i = 0; i < uuids.length; ++i) { + var obj = handlers[uuids[i]]; + methods.push(obj.handler); + + if (obj.once === true) { + toDrop.push(obj.handler); + } + } + + internal.processOpts(options, msg, payload, function (result) { + var execs = internal.objects.executer(methods, result, (msg.context || this)); + + for (var i = 0; i < toDrop.length; ++i) { + msgObj.drop(toDrop[i]); + } + + execs.parallel(callback); + + }); + }; + + var fetchPersisted = function () { + var uuids = payloadIndex.query(msg); + + var fpay; + + if (uuids.length === 0) { + return undefined; + } + + if (uuids.length === 1) { + return payloads[uuids[0]]; + } + + for (var i = 0; i < uuids.length; ++i) { + fpay = external.extend(innerPay, fpay); + } + + return fpay; + }; + + var msgObj = { + option: function (key, value) { + if (!external.exist(key) || !external.isString(key)) { + throw internal.InvalidParametersException("option"); + } + + options[key] = value; + counts.options = counts.options + 1; + + return msgObj; + }, + emit: function (payload, callback) { + if (external.isFunction(payload)) { + callback = payload; + payload = undefined; + } + explicitEmit(payload, undefined, callback); + counts.emits = counts.emits + 1; + + return msgObj; + }, + persist: function (payload) { + if (payload === undefined) { + payload = null; + } + + var uuids = payloadIndex.query(msg); + if (uuids.length === 0) { + var uuid = payloadIndex.index(msg); + payloads[uuid] = payload; + uuids = [uuid]; + } else { + for (var i = 0; i < uuids.length; ++i) { + payloads[uuids[i]] = external.extend(payload, payloads[uuids[i]]); + } + } + + var fpay = fetchPersisted(); + + ++payloadCount; + + counts.persists = counts.persists + 1; + + return msgObj.emit(fpay); + }, + cease: function () { + var uuids = payloadIndex.query(msg); + + for (var i = 0; i < uuids.length; ++i) { + delete payloads[uuids[i]]; + --payloadCount; + } + + return msgObj; + }, + on: function (handler) { + var uuid = messageIndex.index(msg); + handlers[uuid] = { + handler: handler, + context: (msg.context || this), + once: false + }; + handlerCount++; + + var payload = fetchPersisted(); + if (payload !== undefined) { + explicitEmit(payload, [uuid], undefined); + } + counts.ons = counts.ons + 1; + + return msgObj; + }, + once: function (handler) { + var uuid = messageIndex.index(msg); + handlers[uuid] = { + handler: handler, + context: (msg.context || this), + once: true + }; + handlerCount++; + + var payload = fetchPersisted(); + if (payload !== undefined) { + explicitEmit(payload, [uuid], undefined); + } + counts.onces = counts.onces + 1; + + return msgObj; + }, + bind: function (element, event) { + var node = external.findElement(element); + var path = external.getDomPath(node); + + if (!external.exist(boundDOMPaths[path])) { + boundDOMPaths[path] = { }; + } + + boundDOMPaths[path][event] = msgObj; + + node.addEventListener(event, internal.domListener); + + ++boundCount; + counts.binds = counts.binds + 1; + + return msgObj; + }, + drop: function (handler) { + var uuids = messageIndex.query(msg); + if (uuids.length > 0) { + for (var i = 0; i < uuids.length; ++i) { + var uuid = uuids[i]; + if (handlers[uuid].handler === handler) { + delete handlers[uuid]; + handlerCount--; + + messageIndex.delete(uuid); + } + } + } + + return msgObj; + }, + unbind: function (element, event) { + var node = external.findElement(element); + var path = external.getDomPath(node); + + if (external.exist(boundDOMPaths[path])) { + if (external.exist(boundDOMPaths[path][event])) { + node.removeEventListener(event, internal.domListener); + + delete boundDOMPaths[path][event]; + + --boundCount; + } + } + + return msgObj; + }, + dropAll: function () { + var uuids = messageIndex.query(msg); + if (uuids.length > 0) { + for (var i = 0; i < uuids.length; ++i) { + var uuid = uuids[i]; + delete handlers[uuid]; + handlerCount--; + + messageIndex.delete(uuid); + } + } + + return msgObj; + } + }; + + // Expose the raw message object itself via a message property. + // Do not allow modification. + Object.defineProperty(msgObj, "message", { + get: function () { + return msg; + } + }); + + // If debug mode is enabled then let's expose the internal method hit counts. + // These counts are only good if a method is called and succeeds. + if (external.debug === true) { + Object.defineProperty(msgObj, "counts", { + get: function () { + return counts; + } + }); + } + + return msgObj; + }; + + // This is an internal extension; do not export explicitly. + return { }; +})); diff --git a/src/options/cross-window.cspec.js b/src/options/cross-window.cspec.js new file mode 100644 index 0000000..5bdfa40 --- /dev/null +++ b/src/options/cross-window.cspec.js @@ -0,0 +1,57 @@ +if (typeof chai === "undefined" && typeof window === "undefined") { + var chai = require("chai"); +} + +if (typeof expect === "undefined") { + var expect = chai.expect; +} + +if (typeof msngr === "undefined" && typeof window === "undefined") { + var msngr = require("../../msngr"); +} + +describe("./options/cross-window.js", function () { + "use strict"; + + this.timeout(60000); + + before(function () { + msngr.debug = true; + }); + + beforeEach(function () { + msngr.internal.reset(); + }); + + after(function () { + msngr.debug = false; + }); + + it("msngr().option('cross-window') - sends a 100 messages between different tabs or windows", function (done) { + var crossWindowVerifierPath = (window.specRunner.indexOf(".min.html") === -1) ? "crossWindowVerifier.html" : "crossWindowVerifier.min.html"; + var testCounts = 0; + var observedTests = { }; + + var iframe = document.createElement("iframe"); + var msg = msngr("CrossWindow", "Message"); + msg.on(function (payload) { + expect(payload).to.exist; + expect(payload.data.meaningOfLife).to.equal(42); + expect(payload.data.OPDelivers).to.equal(false); + expect(payload.data.text).to.equal("something"); + + if (observedTests[payload.id] === undefined) { + testCounts++; + observedTests[payload.id] = payload; + } + + if (testCounts === 100) { + document.querySelector("body").removeChild(iframe); + done(); + } + }); + + iframe.setAttribute("src", crossWindowVerifierPath); + document.querySelector("body").appendChild(iframe); + }); +}); diff --git a/src/options/cross-window.js b/src/options/cross-window.js new file mode 100644 index 0000000..0e927ff --- /dev/null +++ b/src/options/cross-window.js @@ -0,0 +1,52 @@ +/* + ./options/cross-window.js + + The cross-window option; provides the ability to emit and receive messages across + multiple browser tabs / windows within the same web browser. +*/ +msngr.extend((function (external, internal) { + "use strict"; + + var channelName = "__msngr_cross-window"; + + internal.options = internal.options || { }; + + // Let's check if localstorage is even available. If it isn't we shouldn't register + if (typeof localStorage === "undefined" || typeof window === "undefined") { + return { }; + } + + window.addEventListener("storage", function (event) { + if (event.key === channelName) { + // New message data. Respond! + var obj; + try { + obj = JSON.parse(event.newValue); + } catch (ex) { console.log(ex); } + + if (obj !== undefined && external.isObject(obj)) { + internal.objects.message(obj.message).emit(obj.payload); + } + } + }); + + internal.options["cross-window"] = function (message, payload, options, async) { + // Normalize all of the inputs + options = options || { }; + options = options["cross-window"] || { }; + + var obj = { + message: message, + payload: payload + }; + + try { + localStorage.setItem(channelName, JSON.stringify(obj)); + } catch (ex) { console.log(ex); } + + return undefined; + }; + + // This is an internal extension; do not export explicitly. + return { }; +})); diff --git a/src/actions/dom.cspec.js b/src/options/dom.cspec.js similarity index 82% rename from src/actions/dom.cspec.js rename to src/options/dom.cspec.js index ac518b5..3fb8860 100644 --- a/src/actions/dom.cspec.js +++ b/src/options/dom.cspec.js @@ -10,15 +10,22 @@ if (typeof msngr === "undefined" && typeof window === "undefined") { var msngr = require("../../msngr"); } -describe("./actions/dom.js", function () { +describe("./options/dom.js", function () { "use strict"; - beforeEach(function (done) { - msngr.dropAll(); - done(); + before(function () { + msngr.debug = true; }); - it("dom action - gathers multiple values with a selector that matches multiple elements with no IDs or names", function (done) { + beforeEach(function () { + msngr.internal.reset(); + }); + + after(function () { + msngr.debug = false; + }); + + it("dom option - gathers multiple values with a selector that matches multiple elements with no IDs or names", function (done) { var input1 = document.createElement("input"); input1.value = "Kris"; @@ -28,7 +35,7 @@ describe("./actions/dom.js", function () { document.body.appendChild(input1); document.body.appendChild(input2); - msngr.on("TestTopic", function (payload) { + msngr("TestTopic").on(function (payload) { expect(payload).to.exist; expect(payload["input0"]).to.exist; expect(payload["input0"]).to.equal("Kris"); @@ -41,7 +48,7 @@ describe("./actions/dom.js", function () { done(); }); - msngr.emit({ topic: "TestTopic", dom: ["input"] }); + msngr("TestTopic").option("dom", ["input"]).emit(); }); it("dom action - gathers multiple values with a selector that matches multiple elements", function (done) { @@ -56,7 +63,7 @@ describe("./actions/dom.js", function () { document.body.appendChild(input1); document.body.appendChild(input2); - msngr.on("TestTopic", function (payload) { + msngr("TestTopic").on(function (payload) { expect(payload).to.exist; expect(payload["Name"]).to.exist; expect(payload["Name"]).to.equal("Kris"); @@ -69,7 +76,7 @@ describe("./actions/dom.js", function () { done(); }); - msngr.emit({ topic: "TestTopic", dom: ["input"] }); + msngr("TestTopic").option("dom", ["input"]).emit(); }); it("dom action - gathers multiple values with multiple selectors that each match an element 1:1", function (done) { @@ -84,7 +91,7 @@ describe("./actions/dom.js", function () { document.body.appendChild(input1); document.body.appendChild(input2); - msngr.on("TestTopic", function (payload) { + msngr("TestTopic").on(function (payload) { expect(payload).to.exist; expect(payload["Name"]).to.exist; expect(payload["Name"]).to.equal("Kris"); @@ -97,7 +104,7 @@ describe("./actions/dom.js", function () { done(); }); - msngr.emit({ topic: "TestTopic", dom: ["input[name=Name]", "input[name=Email]"] }); + msngr("TestTopic").option("dom", ["input[name=Name]", "input[name=Email]"]).emit(); }); it("dom action - gathers multiple values with multiple IDs that each match an element 1:1", function (done) { @@ -114,7 +121,7 @@ describe("./actions/dom.js", function () { document.body.appendChild(input1); document.body.appendChild(input2); - msngr.on("TestTopic", function (payload) { + msngr("TestTopic").on(function (payload) { expect(payload).to.exist; expect(payload["Name"]).to.exist; expect(payload["Name"]).to.equal("Kris"); @@ -127,6 +134,6 @@ describe("./actions/dom.js", function () { done(); }); - msngr.emit({ topic: "TestTopic", dom: ["#Name", "#Email"] }); + msngr("TestTopic").option("dom", ["#Name", "#Email"]).emit(); }); }); diff --git a/src/options/dom.js b/src/options/dom.js new file mode 100644 index 0000000..7f794f8 --- /dev/null +++ b/src/options/dom.js @@ -0,0 +1,79 @@ +/* + ./options/dom.js + + The dom option; provides value gathering from supplied selectors +*/ +msngr.extend((function (external, internal) { + "use strict"; + + internal.options = internal.options || { }; + + internal.options.dom = function (message, payload, options, async) { + // Normalize all of the inputs + options = options || { }; + options = options.dom || { }; + var doc = options.doc || options.document || document; + + var selectors = undefined; + if (external.isObject(options) && external.exist(options.selectors) && external.isString(options.selectors)) { + selectors = [options.selectors]; + } else if (external.isString(options)) { + selectors = [options]; + } else if (external.isArray(options)) { + selectors = options; + } + + if (!external.exist(doc) || !external.exist(selectors) || selectors.length === 0) { + return undefined; + } + + // Process all selectors and put them into a single array + var elements = []; + var selLength = selectors.length; + for (var i = 0; i < selLength; ++i) { + var found = external.findElements(selectors[i], doc); + if (found.length > 0) { + elements = elements.concat(Array.prototype.slice.call(found)); + } + } + + // Short circuit because no elements + if (elements.length === 0) { + return undefined; + } + + // Iterate through found elements and aggregate the results + var resultMap = undefined; + var elmLength = elements.length; + var unnamedTags = 0; + for (var i = 0; i < elmLength; ++i) { + var key = undefined, value = undefined; + var elm = elements[i]; + + var nameAttr = elm.getAttribute("name"); + var idAttr = elm.id; + var tagName = elm.tagName.toLowerCase(); + var val = elm.value; + + if (external.exist(nameAttr) && !external.isEmptyString(nameAttr)) { + key = nameAttr; + } else if (external.exist(idAttr) && !external.isEmptyString(idAttr)) { + key = idAttr; + } else { + key = (tagName + unnamedTags); + unnamedTags++; + } + + if (resultMap === undefined) { + resultMap = { }; + } + resultMap[key] = val; + } + + return resultMap; + + }; + + // This is an internal extension; do not export explicitly. + return { }; +})); diff --git a/src/store/memory.aspec.js b/src/store/memory.aspec.js deleted file mode 100644 index b045e71..0000000 --- a/src/store/memory.aspec.js +++ /dev/null @@ -1,243 +0,0 @@ -if (typeof chai === "undefined" && typeof window === "undefined") { - var chai = require("chai"); -} - -if (typeof expect === "undefined") { - var expect = chai.expect; -} - -if (typeof msngr === "undefined" && typeof window === "undefined") { - var msngr = require("../../msngr"); -} - -describe("./stores/memory.js", function () { - "use strict"; - - beforeEach(function() { - msngr.store.clear(); - }); - - it("msngr.store.index(message) - indexes a message with only a topic", function () { - var message = { - topic: "TestTopic1" - }; - - expect(msngr.store.count()).to.equal(0); - var id = msngr.store.index(message); - expect(id).to.exist; - expect(msngr.store.count()).to.equal(1); - }); - - it("msngr.store.index(message) - indexes a message with a topic and category", function () { - var message = { - topic: "TestTopic1", - category: "TestCategory1" - }; - - expect(msngr.store.count()).to.equal(0); - var id = msngr.store.index(message); - expect(id).to.exist; - expect(msngr.store.count()).to.equal(1); - }); - - it("msngr.store.index(message) - indexes a message with a topic and dataType", function () { - var message = { - topic: "TestTopic1", - dataType: "TestDataType1" - }; - - expect(msngr.store.count()).to.equal(0); - var id = msngr.store.index(message); - expect(id).to.exist; - expect(msngr.store.count()).to.equal(1); - }); - - it("msngr.store.index(message) - indexes a message with a topic, category and dataType", function () { - var message = { - topic: "TestTopic1", - category: "TestCategory1", - dataType: "TestDataType1" - }; - - expect(msngr.store.count()).to.equal(0); - var id = msngr.store.index(message); - expect(id).to.exist; - expect(msngr.store.count()).to.equal(1); - }); - - it("msngr.store.index(message) - invalid message shouldn't index", function () { - var message = { - cookie: "monster" - }; - - expect(msngr.store.count()).to.equal(0); - var id = msngr.store.index(message); - expect(id).to.not.exist; - expect(msngr.store.count()).to.equal(0); - }); - - it("msngr.store.delete(uuid) - deletes a valid uuid", function () { - var message = { - topic: "TestTopic1", - category: "TestCategory1", - dataType: "TestDataType1" - }; - - var id = msngr.store.index(message); - expect(id).to.exist; - - var result = msngr.store.delete(id); - expect(result).to.exist; - expect(result).to.equal(true); - expect(msngr.store.count()).to.equal(0); - }); - - it("msngr.store.delete(uuid) - doesn't delete an invalid uuid", function () { - var result = msngr.store.delete("sldfjslkfjlwrjlskdfjs"); - expect(result).to.exist; - expect(result).to.equal(false); - expect(msngr.store.count()).to.equal(0); - }); - - it("msngr.store.query(message) - Correctly gets one result for a query on a topic", function () { - var message = { - topic: "TestTopic1" - }; - - var id = msngr.store.index(message); - expect(id).to.exist; - - var result = msngr.store.query({ topic: "TestTopic1" }); - - expect(result).to.exist; - expect(result.length).to.equal(1); - }); - - it("msngr.store.query(message) - Correctly gets zero results for a query on a topic that doesn't exist", function () { - var result = msngr.store.query({ topic: "TestTopic1" }); - - expect(result).to.exist; - expect(result.length).to.equal(0); - }); - - it("msngr.store.query(message) - Correctly gets one result for a query on a topic and category", function () { - var message = { - topic: "TestTopic1", - category: "TestCategory1" - }; - - var id = msngr.store.index(message); - expect(id).to.exist; - - var result = msngr.store.query({ topic: "TestTopic1", category: "TestCategory1" }); - - expect(result).to.exist; - expect(result.length).to.equal(1); - }); - - it("msngr.store.query(message) - Correctly gets zero results for a query on a topic and category that doesn't exist", function () { - var result = msngr.store.query({ topic: "TestTopic1", category: "TestCategory1" }); - - expect(result).to.exist; - expect(result.length).to.equal(0); - }); - - it("msngr.store.query(message) - Correctly gets one result for a query on a topic, category and dataType", function () { - var message = { - topic: "TestTopic1", - category: "TestCategory1", - dataType: "TestDataType1" - }; - - var id = msngr.store.index(message); - expect(id).to.exist; - - var result = msngr.store.query({ topic: "TestTopic1", category: "TestCategory1", dataType: "TestDataType1" }); - - expect(result).to.exist; - expect(result.length).to.equal(1); - }); - - it("msngr.store.query(message) - Correctly gets zero results for a query on a topic, category and dataType that doesn't exist", function () { - var result = msngr.store.query({ topic: "TestTopic1", category: "TestCategory1", dataType: "TestDataType1" }); - - expect(result).to.exist; - expect(result.length).to.equal(0); - }); - - it("msngr.store.query(message) - Correctly gets zero results for a query on a topic, category and dataType where category doesn't exist", function () { - var message = { - topic: "TestTopic1", - category: "TestCategory1", - dataType: "TestDataType1" - }; - - var id = msngr.store.index(message); - expect(id).to.exist; - - var result = msngr.store.query({ topic: "TestTopic1", category: "TestCategory2", dataType: "TestDataType1" }); - - expect(result).to.exist; - expect(result.length).to.equal(0); - }); - - it("msngr.store.query(message) - Correctly gets zero results for a query on a topic, category and dataType where dataType doesn't exist", function () { - var message = { - topic: "TestTopic1", - category: "TestCategory1", - dataType: "TestDataType1" - }; - - var id = msngr.store.index(message); - expect(id).to.exist; - - var result = msngr.store.query({ topic: "TestTopic1", category: "TestCategory1", dataType: "TestDataType2" }); - - expect(result).to.exist; - expect(result.length).to.equal(0); - }); - - it("msngr.store.query(message) - Correctly gets zero results for a query on a topic, category and dataType where topic doesn't exist", function () { - var message = { - topic: "TestTopic1", - category: "TestCategory1", - dataType: "TestDataType1" - }; - - var id = msngr.store.index(message); - expect(id).to.exist; - - var result = msngr.store.query({ topic: "TestTopic2", category: "TestCategory1", dataType: "TestDataType1" }); - - expect(result).to.exist; - expect(result.length).to.equal(0); - }); - - it("msngr.store.clear() - Clears all indexed messages", function () { - var ids = []; - ids.push(msngr.store.index({ topic: "chicken" })); - ids.push(msngr.store.index({ topic: "table", category: "50" })); - ids.push(msngr.store.index({ topic: "stairs", category: "10", dataType: "food" })); - ids.push(msngr.store.index({ topic: "whatevea", dataType: "punk" })); - - expect(msngr.store.count()).to.equal(4); - expect(msngr.store.clear()).to.equal(true); - expect(msngr.store.query({ topic: "chicken" }).length).to.equal(0); - expect(msngr.store.query({ topic: "stairs", category: "10", dataType: "food" }).length).to.equal(0); - }); - - it("msngr.store.count() - Returns a correct count", function () { - expect(msngr.store.count()).to.equal(0); - msngr.store.index({ topic: "chicken" }); - expect(msngr.store.count()).to.equal(1); - msngr.store.index({ topic: "table", category: "50" }); - expect(msngr.store.count()).to.equal(2); - msngr.store.index({ topic: "stairs", category: "10", dataType: "food" }); - expect(msngr.store.count()).to.equal(3); - expect(msngr.store.clear()).to.equal(true); - expect(msngr.store.count()).to.equal(0); - msngr.store.index({ topic: "whatevea", dataType: "punk" }); - expect(msngr.store.count()).to.equal(1); - - }); -}); diff --git a/src/store/memory.js b/src/store/memory.js deleted file mode 100644 index 79188a0..0000000 --- a/src/store/memory.js +++ /dev/null @@ -1,162 +0,0 @@ -msngr.extend((function () { - "use strict"; - - // Index for id to message objects - var id_to_message = { }; - - // Direct index (no partials) for message - var direct_index = { - topic_to_id: { }, - topic_cat_to_id: { }, - topic_type_to_id: { }, - topic_cat_type_to_id: { } - }; - - // Message index count - var index_count = 0; - - var deleteValueFromArray = function (arr, value) { - var inx = arr.indexOf(value); - var endIndex = arr.length - 1; - if (inx !== endIndex) { - var temp = arr[endIndex]; - arr[endIndex] = arr[inx]; - arr[inx] = temp; - } - arr.pop(); - }; - - return { - store: { - index: function (message) { - if (msngr.utils.exist(message) && msngr.utils.exist(message.topic)) { - var uuid = msngr.utils.id(); - id_to_message[uuid] = message; - - if (direct_index.topic_to_id[message.topic] === undefined) { - direct_index.topic_to_id[message.topic] = []; - } - direct_index.topic_to_id[message.topic].push(uuid); - - if (msngr.utils.exist(message.category)) { - if (direct_index.topic_cat_to_id[message.topic] === undefined) { - direct_index.topic_cat_to_id[message.topic] = { }; - } - - if (direct_index.topic_cat_to_id[message.topic][message.category] === undefined) { - direct_index.topic_cat_to_id[message.topic][message.category] = []; - } - - direct_index.topic_cat_to_id[message.topic][message.category].push(uuid); - } - - if (msngr.utils.exist(message.dataType)) { - if (direct_index.topic_type_to_id[message.topic] === undefined) { - direct_index.topic_type_to_id[message.topic] = { }; - } - - if (direct_index.topic_type_to_id[message.topic][message.dataType] === undefined) { - direct_index.topic_type_to_id[message.topic][message.dataType] = []; - } - - direct_index.topic_type_to_id[message.topic][message.dataType].push(uuid); - } - - if (msngr.utils.exist(message.category) && msngr.utils.exist(message.dataType)) { - if (direct_index.topic_cat_type_to_id[message.topic] === undefined) { - direct_index.topic_cat_type_to_id[message.topic] = { }; - } - - if (direct_index.topic_cat_type_to_id[message.topic][message.category] === undefined) { - direct_index.topic_cat_type_to_id[message.topic][message.category] = { }; - } - - if (direct_index.topic_cat_type_to_id[message.topic][message.category][message.dataType] === undefined) { - direct_index.topic_cat_type_to_id[message.topic][message.category][message.dataType] = []; - } - - direct_index.topic_cat_type_to_id[message.topic][message.category][message.dataType].push(uuid); - } - - index_count++; - - return uuid; - } - return undefined; - }, - delete: function (uuid) { - if (msngr.utils.exist(uuid) && msngr.utils.exist(id_to_message[uuid])) { - var message = id_to_message[uuid]; - - if (msngr.utils.exist(message.topic)) { - deleteValueFromArray(direct_index.topic_to_id[message.topic], uuid); - - if (msngr.utils.exist(message.category)) { - deleteValueFromArray(direct_index.topic_cat_to_id[message.topic][message.category], uuid); - } - - if (msngr.utils.exist(message.dataType)) { - deleteValueFromArray(direct_index.topic_type_to_id[message.topic][message.dataType], uuid); - } - - if (msngr.utils.exist(message.category) && msngr.utils.exist(message.dataType)) { - deleteValueFromArray(direct_index.topic_cat_type_to_id[message.topic][message.category][message.dataType], uuid); - } - } - - delete id_to_message[uuid]; - index_count--; - - return true; - } - return false; - }, - query: function (message) { - if (msngr.utils.exist(message)) { - if (msngr.utils.exist(message.topic)) { - // Topic Only Results - if (!msngr.utils.exist(message.category) && !msngr.utils.exist(message.dataType)) { - return direct_index.topic_to_id[message.topic] || []; - } - - // Topic + Category Results - if (msngr.utils.exist(message.category) && !msngr.utils.exist(message.dataType)) { - return (direct_index.topic_cat_to_id[message.topic] || { })[message.category] || []; - } - - // Topic + Data Type Results - if (msngr.utils.exist(message.dataType) && !msngr.utils.exist(message.category)) { - return (direct_index.topic_type_to_id[message.topic] || { })[message.dataType] || []; - } - - // Topic + Category + Data Type Results - if (msngr.utils.exist(message.category) && msngr.utils.exist(message.dataType)) { - return ((direct_index.topic_cat_type_to_id[message.topic] || { })[message.category] || { })[message.dataType] || []; - } - } - } - - return []; - }, - clear: function () { - // Index for id to message objects - id_to_message = { }; - - // Direct index (no partials) for message - direct_index = { - topic_to_id: { }, - topic_cat_to_id: { }, - topic_type_to_id: { }, - topic_cat_type_to_id: { } - }; - - index_count = 0; - - return true; - }, - count: function () { - return index_count; - } - } - }; -}())); diff --git a/src/utils/converters.aspec.js b/src/utils/converters.aspec.js index 9ca0bba..914a0a8 100644 --- a/src/utils/converters.aspec.js +++ b/src/utils/converters.aspec.js @@ -13,27 +13,27 @@ if (typeof msngr === "undefined" && typeof window === "undefined") { describe("./utils/converters.js", function () { "use strict"; - it("msngr.utils.argumentsToArray(args) - 0 arguments", function () { + it("msngr.argumentsToArray(args) - 0 arguments", function () { var func = function () { - var args = msngr.utils.argumentsToArray(arguments); + var args = msngr.argumentsToArray(arguments); expect(args.length).to.equal(0); } func(); }); - it("msngr.utils.argumentsToArray(args) - 3 arguments", function () { + it("msngr.argumentsToArray(args) - 3 arguments", function () { var func = function () { - var args = msngr.utils.argumentsToArray(arguments); + var args = msngr.argumentsToArray(arguments); expect(args.length).to.equal(3); } func(1, 2, 3); }); - it("msngr.utils.argumentsToArray(args) - 15 arguments", function () { + it("msngr.argumentsToArray(args) - 15 arguments", function () { var func = function () { - var args = msngr.utils.argumentsToArray(arguments); + var args = msngr.argumentsToArray(arguments); expect(args.length).to.equal(15); } diff --git a/src/utils/converters.js b/src/utils/converters.js index 8f4ace8..d8e122e 100644 --- a/src/utils/converters.js +++ b/src/utils/converters.js @@ -1,15 +1,15 @@ -msngr.extend((function () { +msngr.extend((function (external, internal) { "use strict"; return { - utils: { - argumentsToArray: function (args) { - if (msngr.utils.isArray(args)) { - return args; - } - + argumentsToArray: function (args) { + if (external.isArray(args)) { + return args; + } + if (external.isArguments(args)) { return Array.prototype.slice.call(args, 0); } + return [args]; } }; -}())); +})); diff --git a/src/utils/dom.cspec.js b/src/utils/dom.cspec.js index 461d4ba..40fc80d 100644 --- a/src/utils/dom.cspec.js +++ b/src/utils/dom.cspec.js @@ -13,65 +13,65 @@ if (typeof msngr === "undefined" && typeof window === "undefined") { describe("./utils/dom.js", function () { "use strict"; - it("msngr.utils.isHtmlElement(obj) - obj is a function", function () { - expect(msngr.utils.isHtmlElement(function () {})).to.equal(false); + it("msngr.isHtmlElement(obj) - obj is a function", function () { + expect(msngr.isHtmlElement(function () {})).to.equal(false); }); - it("msngr.utils.isHtmlElement(obj) - obj is a string", function () { - expect(msngr.utils.isHtmlElement("test")).to.equal(false); + it("msngr.isHtmlElement(obj) - obj is a string", function () { + expect(msngr.isHtmlElement("test")).to.equal(false); }); - it("msngr.utils.isHtmlElement(obj) - obj is an empty string", function () { - expect(msngr.utils.isHtmlElement("")).to.equal(false); + it("msngr.isHtmlElement(obj) - obj is an empty string", function () { + expect(msngr.isHtmlElement("")).to.equal(false); }); - it("msngr.utils.isHtmlElement(obj) - obj is undefined", function () { - expect(msngr.utils.isHtmlElement(undefined)).to.equal(false); + it("msngr.isHtmlElement(obj) - obj is undefined", function () { + expect(msngr.isHtmlElement(undefined)).to.equal(false); }); - it("msngr.utils.isHtmlElement(obj) - obj is null", function () { - expect(msngr.utils.isHtmlElement(null)).to.equal(false); + it("msngr.isHtmlElement(obj) - obj is null", function () { + expect(msngr.isHtmlElement(null)).to.equal(false); }); - it("msngr.utils.isHtmlElement(obj) - obj is an object", function () { - expect(msngr.utils.isHtmlElement({})).to.equal(false); + it("msngr.isHtmlElement(obj) - obj is an object", function () { + expect(msngr.isHtmlElement({})).to.equal(false); }); - it("msngr.utils.isHtmlElement(obj) - obj is a number", function () { - expect(msngr.utils.isHtmlElement(7)).to.equal(false); + it("msngr.isHtmlElement(obj) - obj is a number", function () { + expect(msngr.isHtmlElement(7)).to.equal(false); }); - it("msngr.utils.isHtmlElement(obj) - obj is an array", function () { - expect(msngr.utils.isHtmlElement([])).to.equal(false); + it("msngr.isHtmlElement(obj) - obj is an array", function () { + expect(msngr.isHtmlElement([])).to.equal(false); }); - it("msngr.utils.isHtmlElement(obj) - obj is a date", function () { - expect(msngr.utils.isHtmlElement(new Date())).to.equal(false); + it("msngr.isHtmlElement(obj) - obj is a date", function () { + expect(msngr.isHtmlElement(new Date())).to.equal(false); }); - it("msngr.utils.isHtmlElement(obj) - obj is a div element", function () { - expect(msngr.utils.isHtmlElement(document.createElement("div"))).to.equal(true); + it("msngr.isHtmlElement(obj) - obj is a div element", function () { + expect(msngr.isHtmlElement(document.createElement("div"))).to.equal(true); }); - it("msngr.utils.isHtmlElement(obj) - obj is an input element", function () { - expect(msngr.utils.isHtmlElement(document.createElement("input"))).to.equal(true); + it("msngr.isHtmlElement(obj) - obj is an input element", function () { + expect(msngr.isHtmlElement(document.createElement("input"))).to.equal(true); }); - it("msngr.utils.isHtmlElement(obj) - obj is a body element", function () { - expect(msngr.utils.isHtmlElement(document.createElement("body"))).to.equal(true); + it("msngr.isHtmlElement(obj) - obj is a body element", function () { + expect(msngr.isHtmlElement(document.createElement("body"))).to.equal(true); }); - it("msngr.utils.isHtmlElement(obj) - obj is a canvas element", function () { - expect(msngr.utils.isHtmlElement(document.createElement("canvas"))).to.equal(true); + it("msngr.isHtmlElement(obj) - obj is a canvas element", function () { + expect(msngr.isHtmlElement(document.createElement("canvas"))).to.equal(true); }); - it("msngr.utils.isNodeList(obj) - obj is a single div element", function () { + it("msngr.isNodeList(obj) - obj is a single div element", function () { var div = document.createElement("div"); - expect(msngr.utils.isNodeList(div)).to.equal(false); + expect(msngr.isNodeList(div)).to.equal(false); }); - it("msngr.utils.isNodeList(obj) - obj is a nodelist", function () { + it("msngr.isNodeList(obj) - obj is a nodelist", function () { var div1 = document.createElement("div"); var div2 = document.createElement("div"); var div3 = document.createElement("div"); @@ -79,36 +79,36 @@ describe("./utils/dom.js", function () { div1.appendChild(div2); div1.appendChild(div3); - expect(msngr.utils.isNodeList(div1.childNodes)).to.equal(true); + expect(msngr.isNodeList(div1.childNodes)).to.equal(true); }); - it("msngr.utils.findElement(obj) - obj is an HTMLElement", function () { - expect(msngr.utils.isHtmlElement(msngr.utils.findElement(document.createElement("div")))).to.equal(true); + it("msngr.findElement(obj) - obj is an HTMLElement", function () { + expect(msngr.isHtmlElement(msngr.findElement(document.createElement("div")))).to.equal(true); }); - it("msngr.utils.findElement(obj) - obj is an HTMLElement with root specified", function () { - expect(msngr.utils.isHtmlElement(msngr.utils.findElement(document.createElement("div"), document))).to.equal(true); + it("msngr.findElement(obj) - obj is an HTMLElement with root specified", function () { + expect(msngr.isHtmlElement(msngr.findElement(document.createElement("div"), document))).to.equal(true); }); - it("msngr.utils.findElement(obj) - obj is an id selector (#MyID)", function () { + it("msngr.findElement(obj) - obj is an id selector (#MyID)", function () { var div = document.createElement("div"); div.setAttribute("id", "TestID1"); document.body.appendChild(div); - expect(msngr.utils.isHtmlElement(msngr.utils.findElement("#TestID1"))).to.equal(true); + expect(msngr.isHtmlElement(msngr.findElement("#TestID1"))).to.equal(true); document.body.removeChild(div); - expect(msngr.utils.isHtmlElement(msngr.utils.findElement("#TestID1"))).to.equal(false); + expect(msngr.isHtmlElement(msngr.findElement("#TestID1"))).to.equal(false); }); - it("msngr.utils.findElement(obj) - obj is a class selector (.TestClass)", function () { + it("msngr.findElement(obj) - obj is a class selector (.TestClass)", function () { var div = document.createElement("div"); div.setAttribute("class", "TestClass"); document.body.appendChild(div); - expect(msngr.utils.isHtmlElement(msngr.utils.findElement(".TestClass"))).to.equal(true); + expect(msngr.isHtmlElement(msngr.findElement(".TestClass"))).to.equal(true); document.body.removeChild(div); - expect(msngr.utils.isHtmlElement(msngr.utils.findElement(".TestClass"))).to.equal(false); + expect(msngr.isHtmlElement(msngr.findElement(".TestClass"))).to.equal(false); }); - it("msngr.utils.findElement(obj) - obj is a html target selector", function () { + it("msngr.findElement(obj) - obj is a html target selector", function () { var div = document.createElement("div"); var div2 = document.createElement("div"); var p = document.createElement("p"); @@ -117,23 +117,23 @@ describe("./utils/dom.js", function () { div.appendChild(div2); document.body.appendChild(div); - expect(msngr.utils.isHtmlElement(msngr.utils.findElement("div div p"))).to.equal(true); + expect(msngr.isHtmlElement(msngr.findElement("div div p"))).to.equal(true); document.body.removeChild(div); - expect(msngr.utils.isHtmlElement(msngr.utils.findElement("div div p"))).to.equal(false); + expect(msngr.isHtmlElement(msngr.findElement("div div p"))).to.equal(false); }); - it("msngr.utils.findElements(obj) - obj is an id selector (#MyID)", function () { + it("msngr.findElements(obj) - obj is an id selector (#MyID)", function () { var div = document.createElement("div"); div.setAttribute("id", "TestID1"); document.body.appendChild(div); - expect(msngr.utils.isNodeList(msngr.utils.findElements("#TestID1"))).to.equal(true); - expect(msngr.utils.findElements("#TestID1").length).to.equal(1); + expect(msngr.isNodeList(msngr.findElements("#TestID1"))).to.equal(true); + expect(msngr.findElements("#TestID1").length).to.equal(1); document.body.removeChild(div); - expect(msngr.utils.isNodeList(msngr.utils.findElement("#TestID1"))).to.equal(true); - expect(msngr.utils.findElements("#TestID1").length).to.equal(0); + expect(msngr.isNodeList(msngr.findElement("#TestID1"))).to.equal(true); + expect(msngr.findElements("#TestID1").length).to.equal(0); }); - it("msngr.utils.findElements(obj) - obj is a class selector (.TestClass)", function () { + it("msngr.findElements(obj) - obj is a class selector (.TestClass)", function () { var div = document.createElement("div"); div.setAttribute("class", "TestClass"); @@ -141,15 +141,15 @@ describe("./utils/dom.js", function () { div2.setAttribute("class", "TestClass"); document.body.appendChild(div); document.body.appendChild(div2); - expect(msngr.utils.isNodeList(msngr.utils.findElements(".TestClass"))).to.equal(true); - expect(msngr.utils.findElements(".TestClass").length).to.equal(2); + expect(msngr.isNodeList(msngr.findElements(".TestClass"))).to.equal(true); + expect(msngr.findElements(".TestClass").length).to.equal(2); document.body.removeChild(div); document.body.removeChild(div2); - expect(msngr.utils.isNodeList(msngr.utils.findElements(".TestClass"))).to.equal(true); - expect(msngr.utils.findElements(".TestClass").length).to.equal(0); + expect(msngr.isNodeList(msngr.findElements(".TestClass"))).to.equal(true); + expect(msngr.findElements(".TestClass").length).to.equal(0); }); - it("msngr.utils.findElement(obj) - obj is a html target selector", function () { + it("msngr.findElement(obj) - obj is a html target selector", function () { var div = document.createElement("div"); var div2 = document.createElement("div"); var p = document.createElement("p"); @@ -158,14 +158,14 @@ describe("./utils/dom.js", function () { div.appendChild(div2); document.body.appendChild(div); - expect(msngr.utils.isNodeList(msngr.utils.findElements("div div p"))).to.equal(true); - expect(msngr.utils.findElements("div div p").length).to.equal(1); + expect(msngr.isNodeList(msngr.findElements("div div p"))).to.equal(true); + expect(msngr.findElements("div div p").length).to.equal(1); document.body.removeChild(div); - expect(msngr.utils.isNodeList(msngr.utils.findElements("div div p"))).to.equal(true); - expect(msngr.utils.findElements("div div p").length).to.equal(0); + expect(msngr.isNodeList(msngr.findElements("div div p"))).to.equal(true); + expect(msngr.findElements("div div p").length).to.equal(0); }); - it("msngr.utils.getDomPath(element) - element is a tested HTMLElement", function () { + it("msngr.getDomPath(element) - element is a tested HTMLElement", function () { var div = document.createElement("div"); div.style.display = "none"; @@ -175,12 +175,12 @@ describe("./utils/dom.js", function () { div.appendChild(p); document.body.appendChild(div); - var path = msngr.utils.getDomPath(msngr.utils.findElement("#TestID2")); - expect(msngr.utils.querySelectorAllWithEq(path)[0].id).to.equal("TestID2"); + var path = msngr.getDomPath(msngr.findElement("#TestID2")); + expect(msngr.querySelectorAllWithEq(path)[0].id).to.equal("TestID2"); document.body.removeChild(div); }); - it("msngr.utils.querySelectorAllWithEq(selector) - selector uses eq to target specific indexes", function () { + it("msngr.querySelectorAllWithEq(selector) - selector uses eq to target specific indexes", function () { var div = document.createElement("div"); div.style.display = "none"; @@ -204,13 +204,13 @@ describe("./utils/dom.js", function () { document.body.appendChild(div); - expect(msngr.utils.querySelectorAllWithEq("div#TestID3 > p:eq(0)")[0].id).to.equal("TestID3p1"); - expect(msngr.utils.querySelectorAllWithEq("div#TestID3 > p:eq(1)")[0].id).to.equal("TestID3p2"); - expect(msngr.utils.querySelectorAllWithEq("div#TestID3 > p:eq(2)")[0].id).to.equal("TestID3p3"); + expect(msngr.querySelectorAllWithEq("div#TestID3 > p:eq(0)")[0].id).to.equal("TestID3p1"); + expect(msngr.querySelectorAllWithEq("div#TestID3 > p:eq(1)")[0].id).to.equal("TestID3p2"); + expect(msngr.querySelectorAllWithEq("div#TestID3 > p:eq(2)")[0].id).to.equal("TestID3p3"); }); - it("msngr.utils.querySelectorAllWithEq(selector) - selector uses eq to target specific indexes and works with specific root", function () { + it("msngr.querySelectorAllWithEq(selector) - selector uses eq to target specific indexes and works with specific root", function () { var div = document.createElement("div"); div.style.display = "none"; @@ -234,13 +234,13 @@ describe("./utils/dom.js", function () { document.body.appendChild(div); - expect(msngr.utils.querySelectorAllWithEq("div#TestID3 > p:eq(0)", document)[0].id).to.equal("TestID3p1"); - expect(msngr.utils.querySelectorAllWithEq("div#TestID3 > p:eq(1)", document)[0].id).to.equal("TestID3p2"); - expect(msngr.utils.querySelectorAllWithEq("div#TestID3 > p:eq(2)", document)[0].id).to.equal("TestID3p3"); + expect(msngr.querySelectorAllWithEq("div#TestID3 > p:eq(0)", document)[0].id).to.equal("TestID3p1"); + expect(msngr.querySelectorAllWithEq("div#TestID3 > p:eq(1)", document)[0].id).to.equal("TestID3p2"); + expect(msngr.querySelectorAllWithEq("div#TestID3 > p:eq(2)", document)[0].id).to.equal("TestID3p3"); }); - it("msngr.utils.querySelectorWithEq(selector) - selector uses eq to target specific indexes", function () { + it("msngr.querySelectorWithEq(selector) - selector uses eq to target specific indexes", function () { var div = document.createElement("div"); div.style.display = "none"; @@ -264,9 +264,9 @@ describe("./utils/dom.js", function () { document.body.appendChild(div); - expect(msngr.utils.querySelectorWithEq("div#TestID3 > p:eq(0)").id).to.equal("TestID3p1"); - expect(msngr.utils.querySelectorWithEq("div#TestID3 > p:eq(1)").id).to.equal("TestID3p2"); - expect(msngr.utils.querySelectorWithEq("div#TestID3 > p:eq(2)").id).to.equal("TestID3p3"); + expect(msngr.querySelectorWithEq("div#TestID3 > p:eq(0)").id).to.equal("TestID3p1"); + expect(msngr.querySelectorWithEq("div#TestID3 > p:eq(1)").id).to.equal("TestID3p2"); + expect(msngr.querySelectorWithEq("div#TestID3 > p:eq(2)").id).to.equal("TestID3p3"); }); }); diff --git a/src/utils/dom.js b/src/utils/dom.js index 9e4e500..930ecff 100644 --- a/src/utils/dom.js +++ b/src/utils/dom.js @@ -1,98 +1,96 @@ -msngr.extend((function () { +msngr.extend((function (external, internal) { "use strict"; return { - utils: { - isHtmlElement: function (obj) { - var t = this.getType(obj); - return (t.indexOf("[object HTML") === 0) || (t.indexOf("[object global]") === 0); - }, - isNodeList: function (obj) { - return (this.getType(obj) === "[object NodeList]"); - }, - findElement: function (element, root) { - var elms = msngr.utils.findElements(element); - if (elms !== undefined && elms.length > 0) { - return elms[0]; - } + isHtmlElement: function (obj) { + var t = this.getType(obj); + return (t.indexOf("[object HTML") === 0) || (t.indexOf("[object global]") === 0); + }, + isNodeList: function (obj) { + return (this.getType(obj) === "[object NodeList]"); + }, + findElement: function (element, root) { + var elms = external.findElements(element, root); + if (elms !== undefined && elms.length > 0) { + return elms[0]; + } - return elms; - }, - findElements: function (selector, root) { - var elm; - if (msngr.utils.isHtmlElement(selector)) { - elm = selector; - } + return elms; + }, + findElements: function (selector, root) { + var elm; + if (external.isHtmlElement(selector)) { + elm = selector; + } - if (elm === undefined && msngr.utils.isString(selector)) { - var doc = root || document; - var result = doc.querySelectorAll(selector); - if (result !== null) { - elm = result; - } + if (elm === undefined && external.isString(selector)) { + var doc = root || document; + var result = doc.querySelectorAll(selector); + if (result !== null) { + elm = result; } + } - return elm; - }, - getDomPath: function (element) { - var node = msngr.utils.isHtmlElement(element) ? element : undefined; - if (node === undefined) { - return undefined; - } + return elm; + }, + getDomPath: function (element) { + var node = external.isHtmlElement(element) ? element : undefined; + if (node === undefined) { + return undefined; + } - if (node.id === undefined) { - node.id = msngr.utils.id(); - } + if (node.id === undefined) { + node.id = external.id(); + } - return "#" + node.id; - }, - querySelectorAllWithEq: function (selector, root) { - if (selector === undefined) { - return null; + return "#" + node.id; + }, + querySelectorAllWithEq: function (selector, root) { + if (selector === undefined) { + return null; + } + var doc = root || document; + var queue = []; + var process = function (input) { + if (input.indexOf(":eq(") === -1) { + return undefined; } - var doc = root || document; - var queue = []; - var process = function (input) { - if (input.indexOf(":eq(") === -1) { - return undefined; - } - - var eqlLoc = input.indexOf(":eq("); - var sel = input.substring(0, eqlLoc); - var ind = input.substring((eqlLoc + 4), input.indexOf(")", eqlLoc)); - selector = input.substring(input.indexOf(")", eqlLoc) + 1, input.length); - if (sel.charAt(0) === ">") { - sel = sel.substring(1, sel.length); - } + var eqlLoc = input.indexOf(":eq("); + var sel = input.substring(0, eqlLoc); + var ind = input.substring((eqlLoc + 4), input.indexOf(")", eqlLoc)); + selector = input.substring(input.indexOf(")", eqlLoc) + 1, input.length); - if (selector.charAt(0) === ">") { - selector = selector.substring(1, selector.length); - } - - queue.push({ - selector: sel, - index: parseInt(ind, 10) - }); - } - while (selector.indexOf(":eq") !== -1) { - process(selector); + if (sel.charAt(0) === ">") { + sel = sel.substring(1, sel.length); } - var result; - while (queue.length > 0) { - var item = queue.shift(); - result = (result || doc).querySelectorAll(item.selector)[item.index]; + if (selector.charAt(0) === ">") { + selector = selector.substring(1, selector.length); } - if (selector.trim().length > 0) { - return (result || doc).querySelectorAll(selector); - } - return [result]; - }, - querySelectorWithEq: function (selector) { - return msngr.utils.querySelectorAllWithEq(selector)[0]; + queue.push({ + selector: sel, + index: parseInt(ind, 10) + }); + } + while (selector.indexOf(":eq") !== -1) { + process(selector); + } + + var result; + while (queue.length > 0) { + var item = queue.shift(); + result = (result || doc).querySelectorAll(item.selector)[item.index]; + } + + if (selector.trim().length > 0) { + return (result || doc).querySelectorAll(selector); } + return [result]; + }, + querySelectorWithEq: function (selector, root) { + return external.querySelectorAllWithEq(selector, root)[0]; } }; -}())); +})); diff --git a/src/utils/exceptional.aspec.js b/src/utils/exceptional.aspec.js new file mode 100644 index 0000000..940692b --- /dev/null +++ b/src/utils/exceptional.aspec.js @@ -0,0 +1,48 @@ +if (typeof chai === "undefined" && typeof window === "undefined") { + var chai = require("chai"); +} + +if (typeof expect === "undefined") { + var expect = chai.expect; +} + +if (typeof msngr === "undefined" && typeof window === "undefined") { + var msngr = require("../../msngr"); +} + +describe("./utils/exceptional.js", function () { + "use strict"; + + before(function () { + msngr.debug = true; + }); + + after(function () { + msngr.debug = false; + }); + + it("internal.InvalidParametersException - throws an exception", function () { + var myfunc = function () { + throw internal.InvalidParametersException("MyParameter"); + }; + + expect(myfunc).to.throw(); + }); + + it("internal.ReservedKeywordsException - throws an exception", function () { + var myfunc = function () { + throw internal.ReservedKeywordsException("MyKeyword"); + }; + + expect(myfunc).to.throw(); + }); + + it("internal.MangledException - throws an exception", function () { + var myfunc = function () { + throw internal.MangledException("MyVariable", "MyFunc"); + }; + + expect(myfunc).to.throw(); + }); + +}); diff --git a/src/utils/exceptional.js b/src/utils/exceptional.js new file mode 100644 index 0000000..49204e5 --- /dev/null +++ b/src/utils/exceptional.js @@ -0,0 +1,30 @@ +msngr.extend((function (external, internal) { + "use strict"; + + internal.InvalidParametersException = function (str) { + return { + name: "InvalidParametersException", + severity: "unrecoverable", + message: ("Invalid parameters supplied to the {method} method".replace("{method}", str)) + }; + }; + + internal.ReservedKeywordsException = function (keyword) { + return { + name: "ReservedKeywordsException", + severity: "unrecoverable", + message: ("Reserved keyword {keyword} supplied as action.".replace("{keyword}", keyword)) + }; + }; + + internal.MangledException = function (variable, method) { + return { + name: "MangledException", + severity: "unrecoverable", + message: ("The {variable} was unexpectedly mangled in {method}.".replace("{variable}", variable).replace("{method}", method)) + }; + }; + + // This is an internal extension; do not export explicitly. + return { }; +})); diff --git a/src/utils/misc.aspec.js b/src/utils/misc.aspec.js index 3067d9c..15504f7 100644 --- a/src/utils/misc.aspec.js +++ b/src/utils/misc.aspec.js @@ -15,14 +15,14 @@ describe("./utils/misc.js", function () { this.timeout(60000); - it("msngr.utils.id() - generate 1 id", function () { - expect(msngr.utils.id()).to.not.equal(undefined); + it("msngr.id() - generate 1 id", function () { + expect(msngr.id()).to.not.equal(undefined); }); - it("msngr.utils.id() - generate 100 unique ids", function () { + it("msngr.id() - generate 100 unique ids", function () { var ids = []; for (var i = 0; i < 100; ++i) { - var d = msngr.utils.id(); + var d = msngr.id(); if (ids.indexOf(d) === -1) { ids.push(d); } @@ -31,10 +31,10 @@ describe("./utils/misc.js", function () { expect(ids.length).to.equal(100); }); - it("msngr.utils.id() - generate 10000 unique ids", function () { + it("msngr.id() - generate 10000 unique ids", function () { var ids = []; for (var i = 0; i < 10000; ++i) { - var d = msngr.utils.id(); + var d = msngr.id(); if (ids.indexOf(d) === -1) { ids.push(d); } @@ -43,16 +43,16 @@ describe("./utils/misc.js", function () { expect(ids.length).to.equal(10000); }); - it("msngr.utils.now() - generates a value", function () { - expect(msngr.utils.now()).to.exist; + it("msngr.now() - generates a value", function () { + expect(msngr.now()).to.exist; }); - it("msngr.utils.now(true) - 5 consecutive calls have unique values", function () { - var t1 = msngr.utils.now(true); - var t2 = msngr.utils.now(true); - var t3 = msngr.utils.now(true); - var t4 = msngr.utils.now(true); - var t5 = msngr.utils.now(true); + it("msngr.now(true) - 5 consecutive calls have unique values", function () { + var t1 = msngr.now(true); + var t2 = msngr.now(true); + var t3 = msngr.now(true); + var t4 = msngr.now(true); + var t5 = msngr.now(true); expect(t1).to.exist; expect(t2).to.exist; @@ -66,9 +66,24 @@ describe("./utils/misc.js", function () { expect(t5).to.not.equal(t4); }); - it("msngr.utils.now('sdfkjsdfl') - Correctly handles invalid input", function () { - var t = msngr.utils.now("sdfkjsdfl"); + it("msngr.now('sdfkjsdfl') - Correctly handles invalid input", function () { + var t = msngr.now("sdfkjsdfl"); expect(t).to.exist; }); + + it("msngr.removeFromArray - removes a value from an array", function () { + var arr = ["something", "another", "test", "weee"]; + + expect(arr[1]).to.equal("another"); + expect(arr.length).to.equal(4); + + msngr.removeFromArray(arr, "another"); + expect(arr[1]).to.equal("weee"); + expect(arr.length).to.equal(3); + + msngr.removeFromArray(arr, "test"); + expect(arr[1]).to.equal("weee"); + expect(arr.length).to.equal(2); + }); }); diff --git a/src/utils/misc.js b/src/utils/misc.js index 9c0f42e..b337497 100644 --- a/src/utils/misc.js +++ b/src/utils/misc.js @@ -1,4 +1,4 @@ -msngr.extend((function () { +msngr.extend((function (external, internal) { "use strict"; var nowPerformance = function () { @@ -18,36 +18,44 @@ msngr.extend((function () { var lastNow = undefined; return { - utils: { - id: function () { - var d = msngr.utils.now(); - var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { - var r = (d + Math.random()*16)%16 | 0; - d = Math.floor(d/16); - return (c=='x' ? r : (r&0x3|0x8)).toString(16); - }); - return uuid; - }, - now: function (noDuplicate) { - if (nowExec === undefined) { - if (typeof performance !== "undefined") { - nowExec = nowPerformance; - nowExecDebugLabel = "performance"; - } else if (typeof process !== "undefined") { - nowExec = nowNode; - nowExecDebugLabel = "node"; - } else { - nowExec = nowLegacy; - nowExecDebugLabel = "legacy"; - } + id: function () { + var d = external.now(); + var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = (d + Math.random()*16)%16 | 0; + d = Math.floor(d/16); + return (c=='x' ? r : (r&0x3|0x8)).toString(16); + }); + return uuid; + }, + now: function (noDuplicate) { + if (nowExec === undefined) { + if (typeof performance !== "undefined") { + nowExec = nowPerformance; + nowExecDebugLabel = "performance"; + } else if (typeof process !== "undefined") { + nowExec = nowNode; + nowExecDebugLabel = "node"; + } else { + nowExec = nowLegacy; + nowExecDebugLabel = "legacy"; } - var now = nowExec(); - if (noDuplicate === true && lastNow === now) { - return msngr.utils.now(noDuplicate); - } - lastNow = now; - return now; } + var now = nowExec(); + if (noDuplicate === true && lastNow === now) { + return external.now(noDuplicate); + } + lastNow = now; + return now; + }, + removeFromArray: function (arr, value) { + var inx = arr.indexOf(value); + var endIndex = arr.length - 1; + if (inx !== endIndex) { + var temp = arr[endIndex]; + arr[endIndex] = arr[inx]; + arr[inx] = temp; + } + arr.pop(); } }; -}())); +})); diff --git a/src/utils/validation.aspec.js b/src/utils/validation.aspec.js index f88dd21..ed2d349 100644 --- a/src/utils/validation.aspec.js +++ b/src/utils/validation.aspec.js @@ -13,549 +13,557 @@ if (typeof msngr === "undefined" && typeof window === "undefined") { describe("./utils/validation.js", function () { "use strict"; - it("msngr.utils.getType(obj) - obj is a function", function () { - expect(msngr.utils.getType(function () {})).to.equal("[object Function]"); + before(function () { + msngr.debug = true; }); - it("msngr.utils.getType(obj) - obj is a string", function () { - expect(msngr.utils.getType("test")).to.equal("[object String]"); + after(function () { + msngr.debug = false; }); - it("msngr.utils.getType(obj) - obj is a number", function () { - expect(msngr.utils.getType(42)).to.equal("[object Number]"); + it("msngr.getType(obj) - obj is a function", function () { + expect(msngr.getType(function () {})).to.equal("[object Function]"); }); - it("msngr.utils.getType(obj) - obj is an array", function () { - expect(msngr.utils.getType([])).to.equal("[object Array]"); + it("msngr.getType(obj) - obj is a string", function () { + expect(msngr.getType("test")).to.equal("[object String]"); }); - it("msngr.utils.getType(obj) - obj is an object", function () { - expect(msngr.utils.getType({ })).to.equal("[object Object]"); + it("msngr.getType(obj) - obj is a number", function () { + expect(msngr.getType(42)).to.equal("[object Number]"); }); - it("msngr.utils.getType(obj) - obj is undefined", function () { - expect(msngr.utils.getType(undefined)).to.equal("undefined"); + it("msngr.getType(obj) - obj is an array", function () { + expect(msngr.getType([])).to.equal("[object Array]"); }); - it("msngr.utils.getType(obj) - obj is null", function () { - expect(msngr.utils.getType(null)).to.equal("null"); + it("msngr.getType(obj) - obj is an object", function () { + expect(msngr.getType({ })).to.equal("[object Object]"); + }); + + it("msngr.getType(obj) - obj is undefined", function () { + expect(msngr.getType(undefined)).to.equal("undefined"); + }); + + it("msngr.getType(obj) - obj is null", function () { + expect(msngr.getType(null)).to.equal("null"); }); // isArguments - it("msngr.utils.isArguments(obj) - obj is a function", function () { - expect(msngr.utils.isArguments(function () {})).to.equal(false); + it("msngr.isArguments(obj) - obj is a function", function () { + expect(msngr.isArguments(function () {})).to.equal(false); }); - it("msngr.utils.isArguments(obj) - obj is a string with content", function () { - expect(msngr.utils.isArguments("test")).to.equal(false); + it("msngr.isArguments(obj) - obj is a string with content", function () { + expect(msngr.isArguments("test")).to.equal(false); }); - it("msngr.utils.isArguments(obj) - obj is an empty string", function () { - expect(msngr.utils.isArguments("")).to.equal(false); + it("msngr.isArguments(obj) - obj is an empty string", function () { + expect(msngr.isArguments("")).to.equal(false); }); - it("msngr.utils.isArguments(obj) - obj is a string with only whitespace", function () { - expect(msngr.utils.isArguments(" ")).to.equal(false); + it("msngr.isArguments(obj) - obj is a string with only whitespace", function () { + expect(msngr.isArguments(" ")).to.equal(false); }); - it("msngr.utils.isArguments(obj) - obj is undefined", function () { - expect(msngr.utils.isArguments(undefined)).to.equal(false); + it("msngr.isArguments(obj) - obj is undefined", function () { + expect(msngr.isArguments(undefined)).to.equal(false); }); - it("msngr.utils.isArguments(obj) - obj is null", function () { - expect(msngr.utils.isArguments(null)).to.equal(false); + it("msngr.isArguments(obj) - obj is null", function () { + expect(msngr.isArguments(null)).to.equal(false); }); - it("msngr.utils.isArguments(obj) - obj is an object", function () { - expect(msngr.utils.isArguments({})).to.equal(false); + it("msngr.isArguments(obj) - obj is an object", function () { + expect(msngr.isArguments({})).to.equal(false); }); - it("msngr.utils.isArguments(obj) - obj is a number", function () { - expect(msngr.utils.isArguments(7)).to.equal(false); + it("msngr.isArguments(obj) - obj is a number", function () { + expect(msngr.isArguments(7)).to.equal(false); }); - it("msngr.utils.isArguments(obj) - obj is an array", function () { - expect(msngr.utils.isArguments([])).to.equal(false); + it("msngr.isArguments(obj) - obj is an array", function () { + expect(msngr.isArguments([])).to.equal(false); }); - it("msngr.utils.isArguments(obj) - obj is a Date", function () { - expect(msngr.utils.isArguments(new Date())).to.equal(false); + it("msngr.isArguments(obj) - obj is a Date", function () { + expect(msngr.isArguments(new Date())).to.equal(false); }); - it("msngr.utils.isArguments(obj) - obj is an Arguments object", function () { + it("msngr.isArguments(obj) - obj is an Arguments object", function () { var tFunc = function () { - expect(msngr.utils.isArguments(arguments)).to.equal(true); + expect(msngr.isArguments(arguments)).to.equal(true); } tFunc("something", 15, "weee"); }); // isNullOrUndefined(obj) - it("msngr.utils.isNullOrUndefined(obj) - obj is a function", function () { - expect(msngr.utils.isNullOrUndefined(function () {})).to.equal(false); + it("msngr.isNullOrUndefined(obj) - obj is a function", function () { + expect(msngr.isNullOrUndefined(function () {})).to.equal(false); }); - it("msngr.utils.isNullOrUndefined(obj) - obj is a string with content", function () { - expect(msngr.utils.isNullOrUndefined("test")).to.equal(false); + it("msngr.isNullOrUndefined(obj) - obj is a string with content", function () { + expect(msngr.isNullOrUndefined("test")).to.equal(false); }); - it("msngr.utils.isNullOrUndefined(obj) - obj is an empty string", function () { - expect(msngr.utils.isNullOrUndefined("")).to.equal(false); + it("msngr.isNullOrUndefined(obj) - obj is an empty string", function () { + expect(msngr.isNullOrUndefined("")).to.equal(false); }); - it("msngr.utils.isNullOrUndefined(obj) - obj is a string with only whitespace", function () { - expect(msngr.utils.isNullOrUndefined(" ")).to.equal(false); + it("msngr.isNullOrUndefined(obj) - obj is a string with only whitespace", function () { + expect(msngr.isNullOrUndefined(" ")).to.equal(false); }); - it("msngr.utils.isNullOrUndefined(obj) - obj is undefined", function () { - expect(msngr.utils.isNullOrUndefined(undefined)).to.equal(true); + it("msngr.isNullOrUndefined(obj) - obj is undefined", function () { + expect(msngr.isNullOrUndefined(undefined)).to.equal(true); }); - it("msngr.utils.isNullOrUndefined(obj) - obj is null", function () { - expect(msngr.utils.isNullOrUndefined(null)).to.equal(true); + it("msngr.isNullOrUndefined(obj) - obj is null", function () { + expect(msngr.isNullOrUndefined(null)).to.equal(true); }); - it("msngr.utils.isNullOrUndefined(obj) - obj is an object", function () { - expect(msngr.utils.isNullOrUndefined({})).to.equal(false); + it("msngr.isNullOrUndefined(obj) - obj is an object", function () { + expect(msngr.isNullOrUndefined({})).to.equal(false); }); - it("msngr.utils.isNullOrUndefined(obj) - obj is a number", function () { - expect(msngr.utils.isNullOrUndefined(7)).to.equal(false); + it("msngr.isNullOrUndefined(obj) - obj is a number", function () { + expect(msngr.isNullOrUndefined(7)).to.equal(false); }); - it("msngr.utils.isNullOrUndefined(obj) - obj is an array", function () { - expect(msngr.utils.isNullOrUndefined([])).to.equal(false); + it("msngr.isNullOrUndefined(obj) - obj is an array", function () { + expect(msngr.isNullOrUndefined([])).to.equal(false); }); - it("msngr.utils.isNullOrUndefined(obj) - obj is a Date", function () { - expect(msngr.utils.isNullOrUndefined(new Date())).to.equal(false); + it("msngr.isNullOrUndefined(obj) - obj is a Date", function () { + expect(msngr.isNullOrUndefined(new Date())).to.equal(false); }); // exist(obj) - it("msngr.utils.exist(obj) - obj is a function", function () { - expect(msngr.utils.exist(function () {})).to.equal(true); + it("msngr.exist(obj) - obj is a function", function () { + expect(msngr.exist(function () {})).to.equal(true); }); - it("msngr.utils.exist(obj) - obj is a string with content", function () { - expect(msngr.utils.exist("test")).to.equal(true); + it("msngr.exist(obj) - obj is a string with content", function () { + expect(msngr.exist("test")).to.equal(true); }); - it("msngr.utils.exist(obj) - obj is an empty string", function () { - expect(msngr.utils.exist("")).to.equal(true); + it("msngr.exist(obj) - obj is an empty string", function () { + expect(msngr.exist("")).to.equal(true); }); - it("msngr.utils.exist(obj) - obj is a string with only whitespace", function () { - expect(msngr.utils.exist(" ")).to.equal(true); + it("msngr.exist(obj) - obj is a string with only whitespace", function () { + expect(msngr.exist(" ")).to.equal(true); }); - it("msngr.utils.exist(obj) - obj is undefined", function () { - expect(msngr.utils.exist(undefined)).to.equal(false); + it("msngr.exist(obj) - obj is undefined", function () { + expect(msngr.exist(undefined)).to.equal(false); }); - it("msngr.utils.exist(obj) - obj is null", function () { - expect(msngr.utils.exist(null)).to.equal(false); + it("msngr.exist(obj) - obj is null", function () { + expect(msngr.exist(null)).to.equal(false); }); - it("msngr.utils.exist(obj) - obj is an object", function () { - expect(msngr.utils.exist({})).to.equal(true); + it("msngr.exist(obj) - obj is an object", function () { + expect(msngr.exist({})).to.equal(true); }); - it("msngr.utils.exist(obj) - obj is a number", function () { - expect(msngr.utils.exist(7)).to.equal(true); + it("msngr.exist(obj) - obj is a number", function () { + expect(msngr.exist(7)).to.equal(true); }); - it("msngr.utils.exist(obj) - obj is an array", function () { - expect(msngr.utils.exist([])).to.equal(true); + it("msngr.exist(obj) - obj is an array", function () { + expect(msngr.exist([])).to.equal(true); }); - it("msngr.utils.exist(obj) - obj is a Date", function () { - expect(msngr.utils.exist(new Date())).to.equal(true); + it("msngr.exist(obj) - obj is a Date", function () { + expect(msngr.exist(new Date())).to.equal(true); }); // exists() - it("msngr.utils.exists(...) - arguments are of various types", function () { - expect(msngr.utils.exists("whatever", 15, true, false, { })).to.equal(true); + it("msngr.exists(...) - arguments are of various types", function () { + expect(msngr.exists("whatever", 15, true, false, { })).to.equal(true); }); - it("msngr.utils.exists(...) - arguments are of various types with an undefined item", function () { - expect(msngr.utils.exists("whatever", 15, undefined, false, { })).to.equal(false); + it("msngr.exists(...) - arguments are of various types with an undefined item", function () { + expect(msngr.exists("whatever", 15, undefined, false, { })).to.equal(false); }); - it("msngr.utils.exists(...) - arguments are of various types with a null item", function () { - expect(msngr.utils.exists(null, 15, true, false, { })).to.equal(false); + it("msngr.exists(...) - arguments are of various types with a null item", function () { + expect(msngr.exists(null, 15, true, false, { })).to.equal(false); }); // isString(str) - it("msngr.utils.isString(str) - obj is a function", function () { - expect(msngr.utils.isString(function () {})).to.equal(false); + it("msngr.isString(str) - obj is a function", function () { + expect(msngr.isString(function () {})).to.equal(false); }); - it("msngr.utils.isString(str) - obj is a string with content", function () { - expect(msngr.utils.isString("test")).to.equal(true); + it("msngr.isString(str) - obj is a string with content", function () { + expect(msngr.isString("test")).to.equal(true); }); - it("msngr.utils.isString(str) - obj is an empty string", function () { - expect(msngr.utils.isString("")).to.equal(true); + it("msngr.isString(str) - obj is an empty string", function () { + expect(msngr.isString("")).to.equal(true); }); - it("msngr.utils.isString(str) - obj is a string with only whitespace", function () { - expect(msngr.utils.isString(" ")).to.equal(true); + it("msngr.isString(str) - obj is a string with only whitespace", function () { + expect(msngr.isString(" ")).to.equal(true); }); - it("msngr.utils.isString(str) - obj is undefined", function () { - expect(msngr.utils.isString(undefined)).to.equal(false); + it("msngr.isString(str) - obj is undefined", function () { + expect(msngr.isString(undefined)).to.equal(false); }); - it("msngr.utils.isString(str) - obj is null", function () { - expect(msngr.utils.isString(null)).to.equal(false); + it("msngr.isString(str) - obj is null", function () { + expect(msngr.isString(null)).to.equal(false); }); - it("msngr.utils.isString(str) - obj is an object", function () { - expect(msngr.utils.isString({})).to.equal(false); + it("msngr.isString(str) - obj is an object", function () { + expect(msngr.isString({})).to.equal(false); }); - it("msngr.utils.isString(str) - obj is a number", function () { - expect(msngr.utils.isString(7)).to.equal(false); + it("msngr.isString(str) - obj is a number", function () { + expect(msngr.isString(7)).to.equal(false); }); - it("msngr.utils.isString(str) - obj is an array", function () { - expect(msngr.utils.isString([])).to.equal(false); + it("msngr.isString(str) - obj is an array", function () { + expect(msngr.isString([])).to.equal(false); }); - it("msngr.utils.isString(str) - obj is a Date", function () { - expect(msngr.utils.isString(new Date())).to.equal(false); + it("msngr.isString(str) - obj is a Date", function () { + expect(msngr.isString(new Date())).to.equal(false); }); // isDate(obj) - it("msngr.utils.isDate(obj) - obj is a function", function () { - expect(msngr.utils.isDate(function () {})).to.equal(false); + it("msngr.isDate(obj) - obj is a function", function () { + expect(msngr.isDate(function () {})).to.equal(false); }); - it("msngr.utils.isDate(obj) - obj is a string with content", function () { - expect(msngr.utils.isDate("test")).to.equal(false); + it("msngr.isDate(obj) - obj is a string with content", function () { + expect(msngr.isDate("test")).to.equal(false); }); - it("msngr.utils.isDate(obj) - obj is an empty string", function () { - expect(msngr.utils.isDate("")).to.equal(false); + it("msngr.isDate(obj) - obj is an empty string", function () { + expect(msngr.isDate("")).to.equal(false); }); - it("msngr.utils.isDate(obj) - obj is a string with only whitespace", function () { - expect(msngr.utils.isDate(" ")).to.equal(false); + it("msngr.isDate(obj) - obj is a string with only whitespace", function () { + expect(msngr.isDate(" ")).to.equal(false); }); - it("msngr.utils.isDate(obj) - obj is undefined", function () { - expect(msngr.utils.isDate(undefined)).to.equal(false); + it("msngr.isDate(obj) - obj is undefined", function () { + expect(msngr.isDate(undefined)).to.equal(false); }); - it("msngr.utils.isDate(obj) - obj is null", function () { - expect(msngr.utils.isDate(null)).to.equal(false); + it("msngr.isDate(obj) - obj is null", function () { + expect(msngr.isDate(null)).to.equal(false); }); - it("msngr.utils.isDate(obj) - obj is an object", function () { - expect(msngr.utils.isDate({})).to.equal(false); + it("msngr.isDate(obj) - obj is an object", function () { + expect(msngr.isDate({})).to.equal(false); }); - it("msngr.utils.isDate(obj) - obj is a number", function () { - expect(msngr.utils.isDate(7)).to.equal(false); + it("msngr.isDate(obj) - obj is a number", function () { + expect(msngr.isDate(7)).to.equal(false); }); - it("msngr.utils.isDate(obj) - obj is an array", function () { - expect(msngr.utils.isDate([])).to.equal(false); + it("msngr.isDate(obj) - obj is an array", function () { + expect(msngr.isDate([])).to.equal(false); }); - it("msngr.utils.isDate(obj) - obj is a Date", function () { - expect(msngr.utils.isDate(new Date())).to.equal(true); + it("msngr.isDate(obj) - obj is a Date", function () { + expect(msngr.isDate(new Date())).to.equal(true); }); // isArray(obj) - it("msngr.utils.isArray(obj) - obj is a function", function () { - expect(msngr.utils.isArray(function () {})).to.equal(false); + it("msngr.isArray(obj) - obj is a function", function () { + expect(msngr.isArray(function () {})).to.equal(false); }); - it("msngr.utils.isArray(obj) - obj is a string with content", function () { - expect(msngr.utils.isArray("test")).to.equal(false); + it("msngr.isArray(obj) - obj is a string with content", function () { + expect(msngr.isArray("test")).to.equal(false); }); - it("msngr.utils.isArray(obj) - obj is an empty string", function () { - expect(msngr.utils.isArray("")).to.equal(false); + it("msngr.isArray(obj) - obj is an empty string", function () { + expect(msngr.isArray("")).to.equal(false); }); - it("msngr.utils.isArray(obj) - obj is a string with only whitespace", function () { - expect(msngr.utils.isArray(" ")).to.equal(false); + it("msngr.isArray(obj) - obj is a string with only whitespace", function () { + expect(msngr.isArray(" ")).to.equal(false); }); - it("msngr.utils.isArray(obj) - obj is undefined", function () { - expect(msngr.utils.isArray(undefined)).to.equal(false); + it("msngr.isArray(obj) - obj is undefined", function () { + expect(msngr.isArray(undefined)).to.equal(false); }); - it("msngr.utils.isArray(obj) - obj is null", function () { - expect(msngr.utils.isArray(null)).to.equal(false); + it("msngr.isArray(obj) - obj is null", function () { + expect(msngr.isArray(null)).to.equal(false); }); - it("msngr.utils.isArray(obj) - obj is an object", function () { - expect(msngr.utils.isArray({})).to.equal(false); + it("msngr.isArray(obj) - obj is an object", function () { + expect(msngr.isArray({})).to.equal(false); }); - it("msngr.utils.isArray(obj) - obj is a number", function () { - expect(msngr.utils.isArray(7)).to.equal(false); + it("msngr.isArray(obj) - obj is a number", function () { + expect(msngr.isArray(7)).to.equal(false); }); - it("msngr.utils.isArray(obj) - obj is an array", function () { - expect(msngr.utils.isArray([])).to.equal(true); + it("msngr.isArray(obj) - obj is an array", function () { + expect(msngr.isArray([])).to.equal(true); }); - it("msngr.utils.isArray(obj) - obj is a Date", function () { - expect(msngr.utils.isArray(new Date())).to.equal(false); + it("msngr.isArray(obj) - obj is a Date", function () { + expect(msngr.isArray(new Date())).to.equal(false); }); // isNumber(obj) - it("msngr.utils.isNumber(obj) - obj is a function", function () { - expect(msngr.utils.isNumber(function () {})).to.equal(false); + it("msngr.isNumber(obj) - obj is a function", function () { + expect(msngr.isNumber(function () {})).to.equal(false); }); - it("msngr.utils.isNumber(obj) - obj is a string with content", function () { - expect(msngr.utils.isNumber("test")).to.equal(false); + it("msngr.isNumber(obj) - obj is a string with content", function () { + expect(msngr.isNumber("test")).to.equal(false); }); - it("msngr.utils.isNumber(obj) - obj is an empty string", function () { - expect(msngr.utils.isNumber("")).to.equal(false); + it("msngr.isNumber(obj) - obj is an empty string", function () { + expect(msngr.isNumber("")).to.equal(false); }); - it("msngr.utils.isNumber(obj) - obj is a string with only whitespace", function () { - expect(msngr.utils.isNumber(" ")).to.equal(false); + it("msngr.isNumber(obj) - obj is a string with only whitespace", function () { + expect(msngr.isNumber(" ")).to.equal(false); }); - it("msngr.utils.isNumber(obj) - obj is undefined", function () { - expect(msngr.utils.isNumber(undefined)).to.equal(false); + it("msngr.isNumber(obj) - obj is undefined", function () { + expect(msngr.isNumber(undefined)).to.equal(false); }); - it("msngr.utils.isNumber(obj) - obj is null", function () { - expect(msngr.utils.isNumber(null)).to.equal(false); + it("msngr.isNumber(obj) - obj is null", function () { + expect(msngr.isNumber(null)).to.equal(false); }); - it("msngr.utils.isNumber(obj) - obj is an object", function () { - expect(msngr.utils.isNumber({})).to.equal(false); + it("msngr.isNumber(obj) - obj is an object", function () { + expect(msngr.isNumber({})).to.equal(false); }); - it("msngr.utils.isNumber(obj) - obj is a number", function () { - expect(msngr.utils.isNumber(7)).to.equal(true); + it("msngr.isNumber(obj) - obj is a number", function () { + expect(msngr.isNumber(7)).to.equal(true); }); - it("msngr.utils.isNumber(obj) - obj is an array", function () { - expect(msngr.utils.isNumber([])).to.equal(false); + it("msngr.isNumber(obj) - obj is an array", function () { + expect(msngr.isNumber([])).to.equal(false); }); - it("msngr.utils.isNumber(obj) - obj is a Date", function () { - expect(msngr.utils.isNumber(new Date())).to.equal(false); + it("msngr.isNumber(obj) - obj is a Date", function () { + expect(msngr.isNumber(new Date())).to.equal(false); }); // isObject(obj) - it("msngr.utils.isObject(obj) - obj is a function", function () { - expect(msngr.utils.isObject(function () {})).to.equal(false); + it("msngr.isObject(obj) - obj is a function", function () { + expect(msngr.isObject(function () {})).to.equal(false); }); - it("msngr.utils.isObject(obj) - obj is a string with content", function () { - expect(msngr.utils.isObject("test")).to.equal(false); + it("msngr.isObject(obj) - obj is a string with content", function () { + expect(msngr.isObject("test")).to.equal(false); }); - it("msngr.utils.isObject(obj) - obj is an empty string", function () { - expect(msngr.utils.isObject("")).to.equal(false); + it("msngr.isObject(obj) - obj is an empty string", function () { + expect(msngr.isObject("")).to.equal(false); }); - it("msngr.utils.isObject(obj) - obj is a string with only whitespace", function () { - expect(msngr.utils.isObject(" ")).to.equal(false); + it("msngr.isObject(obj) - obj is a string with only whitespace", function () { + expect(msngr.isObject(" ")).to.equal(false); }); - it("msngr.utils.isObject(obj) - obj is undefined", function () { - expect(msngr.utils.isObject(undefined)).to.equal(false); + it("msngr.isObject(obj) - obj is undefined", function () { + expect(msngr.isObject(undefined)).to.equal(false); }); - it("msngr.utils.isObject(obj) - obj is null", function () { - expect(msngr.utils.isObject(null)).to.equal(false); + it("msngr.isObject(obj) - obj is null", function () { + expect(msngr.isObject(null)).to.equal(false); }); - it("msngr.utils.isObject(obj) - obj is an object", function () { - expect(msngr.utils.isObject({})).to.equal(true); + it("msngr.isObject(obj) - obj is an object", function () { + expect(msngr.isObject({})).to.equal(true); }); - it("msngr.utils.isObject(obj) - obj is a number", function () { - expect(msngr.utils.isObject(7)).to.equal(false); + it("msngr.isObject(obj) - obj is a number", function () { + expect(msngr.isObject(7)).to.equal(false); }); - it("msngr.utils.isObject(obj) - obj is an array", function () { - expect(msngr.utils.isObject([])).to.equal(false); + it("msngr.isObject(obj) - obj is an array", function () { + expect(msngr.isObject([])).to.equal(false); }); - it("msngr.utils.isObject(obj) - obj is a Date", function () { - expect(msngr.utils.isObject(new Date())).to.equal(false); + it("msngr.isObject(obj) - obj is a Date", function () { + expect(msngr.isObject(new Date())).to.equal(false); }); // isFunction(func) - it("msngr.utils.isFunction(func) - obj is a function", function () { - expect(msngr.utils.isFunction(function () {})).to.equal(true); + it("msngr.isFunction(func) - obj is a function", function () { + expect(msngr.isFunction(function () {})).to.equal(true); }); - it("msngr.utils.isFunction(func) - obj is a string with content", function () { - expect(msngr.utils.isFunction("test")).to.equal(false); + it("msngr.isFunction(func) - obj is a string with content", function () { + expect(msngr.isFunction("test")).to.equal(false); }); - it("msngr.utils.isFunction(func) - obj is an empty string", function () { - expect(msngr.utils.isFunction("")).to.equal(false); + it("msngr.isFunction(func) - obj is an empty string", function () { + expect(msngr.isFunction("")).to.equal(false); }); - it("msngr.utils.isFunction(func) - obj is a string with only whitespace", function () { - expect(msngr.utils.isFunction(" ")).to.equal(false); + it("msngr.isFunction(func) - obj is a string with only whitespace", function () { + expect(msngr.isFunction(" ")).to.equal(false); }); - it("msngr.utils.isFunction(func) - obj is undefined", function () { - expect(msngr.utils.isFunction(undefined)).to.equal(false); + it("msngr.isFunction(func) - obj is undefined", function () { + expect(msngr.isFunction(undefined)).to.equal(false); }); - it("msngr.utils.isFunction(func) - obj is null", function () { - expect(msngr.utils.isFunction(null)).to.equal(false); + it("msngr.isFunction(func) - obj is null", function () { + expect(msngr.isFunction(null)).to.equal(false); }); - it("msngr.utils.isFunction(func) - obj is an object", function () { - expect(msngr.utils.isFunction({})).to.equal(false); + it("msngr.isFunction(func) - obj is an object", function () { + expect(msngr.isFunction({})).to.equal(false); }); - it("msngr.utils.isFunction(func) - obj is a number", function () { - expect(msngr.utils.isFunction(7)).to.equal(false); + it("msngr.isFunction(func) - obj is a number", function () { + expect(msngr.isFunction(7)).to.equal(false); }); - it("msngr.utils.isFunction(func) - obj is an array", function () { - expect(msngr.utils.isFunction([])).to.equal(false); + it("msngr.isFunction(func) - obj is an array", function () { + expect(msngr.isFunction([])).to.equal(false); }); - it("msngr.utils.isFunction(func) - obj is a Date", function () { - expect(msngr.utils.isFunction(new Date())).to.equal(false); + it("msngr.isFunction(func) - obj is a Date", function () { + expect(msngr.isFunction(new Date())).to.equal(false); }); // isEmptyString(str) - it("msngr.utils.isEmptyString(str) - str is a function", function () { - expect(msngr.utils.isEmptyString(function () {})).to.equal(false); + it("msngr.isEmptyString(str) - str is a function", function () { + expect(msngr.isEmptyString(function () {})).to.equal(false); }); - it("msngr.utils.isEmptyString(str) - str is a string with content", function () { - expect(msngr.utils.isEmptyString("test")).to.equal(false); + it("msngr.isEmptyString(str) - str is a string with content", function () { + expect(msngr.isEmptyString("test")).to.equal(false); }); - it("msngr.utils.isEmptyString(str) - str is an empty string", function () { - expect(msngr.utils.isEmptyString("")).to.equal(true); + it("msngr.isEmptyString(str) - str is an empty string", function () { + expect(msngr.isEmptyString("")).to.equal(true); }); - it("msngr.utils.isEmptyString(str) - str is a string with only whitespace", function () { - expect(msngr.utils.isEmptyString(" ")).to.equal(true); + it("msngr.isEmptyString(str) - str is a string with only whitespace", function () { + expect(msngr.isEmptyString(" ")).to.equal(true); }); - it("msngr.utils.isEmptyString(str) - str is undefined", function () { - expect(msngr.utils.isEmptyString(undefined)).to.equal(true); + it("msngr.isEmptyString(str) - str is undefined", function () { + expect(msngr.isEmptyString(undefined)).to.equal(true); }); - it("msngr.utils.isEmptyString(str) - str is null", function () { - expect(msngr.utils.isEmptyString(null)).to.equal(true); + it("msngr.isEmptyString(str) - str is null", function () { + expect(msngr.isEmptyString(null)).to.equal(true); }); - it("msngr.utils.isEmptyString(str) - str is an object", function () { - expect(msngr.utils.isEmptyString({})).to.equal(false); + it("msngr.isEmptyString(str) - str is an object", function () { + expect(msngr.isEmptyString({})).to.equal(false); }); - it("msngr.utils.isEmptyString(str) - str is a number", function () { - expect(msngr.utils.isEmptyString(7)).to.equal(false); + it("msngr.isEmptyString(str) - str is a number", function () { + expect(msngr.isEmptyString(7)).to.equal(false); }); - it("msngr.utils.isEmptyString(str) - str is an array", function () { - expect(msngr.utils.isEmptyString([])).to.equal(false); + it("msngr.isEmptyString(str) - str is an array", function () { + expect(msngr.isEmptyString([])).to.equal(false); }); - it("msngr.utils.isEmptyString(str) - str is a Date", function () { - expect(msngr.utils.isEmptyString(new Date())).to.equal(false); + it("msngr.isEmptyString(str) - str is a Date", function () { + expect(msngr.isEmptyString(new Date())).to.equal(false); }); // hasWildCard(str) - it("msngr.utils.hasWildCard(str)", function () { - expect(msngr.utils.hasWildCard("whatever")).to.equal(false); - expect(msngr.utils.hasWildCard("")).to.equal(false); - expect(msngr.utils.hasWildCard("what*")).to.equal(true); + it("msngr.hasWildCard(str)", function () { + expect(msngr.hasWildCard("whatever")).to.equal(false); + expect(msngr.hasWildCard("")).to.equal(false); + expect(msngr.hasWildCard("what*")).to.equal(true); }); // reiterativeValidation(func, inputs) - it("msngr.utils.reiterativeValidation(func, inputs) - func is undefined", function () { - expect(msngr.utils.reiterativeValidation(undefined, [true, false, 15, "534"])).to.equal(false); + it("msngr.internal.reiterativeValidation(func, inputs) - func is undefined", function () { + expect(msngr.internal.reiterativeValidation(undefined, [true, false, 15, "534"])).to.equal(false); }); - it("msngr.utils.reiterativeValidation(func, inputs) - inputs is undefined", function () { - expect(msngr.utils.reiterativeValidation(msngr.utils.exists, undefined)).to.equal(false); + it("msngr.internal.reiterativeValidation(func, inputs) - inputs is undefined", function () { + expect(msngr.internal.reiterativeValidation(msngr.exists, undefined)).to.equal(false); }); - it("msngr.utils.reiterativeValidation(func, inputs) - func is msngr.utils.exists and inputs is a single value", function () { - expect(msngr.utils.reiterativeValidation(msngr.utils.exists, true)).to.equal(true); - expect(msngr.utils.reiterativeValidation(msngr.utils.exists, undefined)).to.equal(false); - expect(msngr.utils.reiterativeValidation(msngr.utils.exists, null)).to.equal(false); + it("msngr.internal.reiterativeValidation(func, inputs) - func is msngr.exists and inputs is a single value", function () { + expect(msngr.internal.reiterativeValidation(msngr.exists, true)).to.equal(true); + expect(msngr.internal.reiterativeValidation(msngr.exists, undefined)).to.equal(false); + expect(msngr.internal.reiterativeValidation(msngr.exists, null)).to.equal(false); }); - it("msngr.utils.reiterativeValidation(func, inputs) - func is msngr.utils.exists and inputs are various values", function () { - expect(msngr.utils.reiterativeValidation(msngr.utils.exists, [true, false, 15, "534"])).to.equal(true); - expect(msngr.utils.reiterativeValidation(msngr.utils.exists, [undefined, false, 15, "534"])).to.equal(false); - expect(msngr.utils.reiterativeValidation(msngr.utils.exists, [true, undefined, 15, "534"])).to.equal(false); + it("msngr.internal.reiterativeValidation(func, inputs) - func is msngr.exists and inputs are various values", function () { + expect(msngr.internal.reiterativeValidation(msngr.exists, [true, false, 15, "534"])).to.equal(true); + expect(msngr.internal.reiterativeValidation(msngr.exists, [undefined, false, 15, "534"])).to.equal(false); + expect(msngr.internal.reiterativeValidation(msngr.exists, [true, undefined, 15, "534"])).to.equal(false); }); // exists() - it("msngr.utils.exists()", function () { - expect(msngr.utils.exists({}, [], "something", 12)).to.equal(true); - expect(msngr.utils.exists({}, null, "something", 12)).to.equal(false); - expect(msngr.utils.exists({}, [], undefined, 12)).to.equal(false); - expect(msngr.utils.exists(null, undefined, null)).to.equal(false); + it("msngr.exists()", function () { + expect(msngr.exists({}, [], "something", 12)).to.equal(true); + expect(msngr.exists({}, null, "something", 12)).to.equal(false); + expect(msngr.exists({}, [], undefined, 12)).to.equal(false); + expect(msngr.exists(null, undefined, null)).to.equal(false); }); // areStrings() - it("msngr.utils.areStrings()", function () { - expect(msngr.utils.areStrings("Whatever", "Totes!", "Chickens")).to.equal(true); - expect(msngr.utils.areStrings("Whatever", undefined, "Chickens")).to.equal(false); - expect(msngr.utils.areStrings("Whatever", null, "Chickens")).to.equal(false); - expect(msngr.utils.areStrings("Whatever", "Totes!", 5)).to.equal(false); + it("msngr.areStrings()", function () { + expect(msngr.areStrings("Whatever", "Totes!", "Chickens")).to.equal(true); + expect(msngr.areStrings("Whatever", undefined, "Chickens")).to.equal(false); + expect(msngr.areStrings("Whatever", null, "Chickens")).to.equal(false); + expect(msngr.areStrings("Whatever", "Totes!", 5)).to.equal(false); }); // areDates() - it("msngr.utils.areDates()", function () { - expect(msngr.utils.areDates((new Date()), (new Date()))).to.equal(true); - expect(msngr.utils.areDates((new Date()), undefined, "Chickens")).to.equal(false); - expect(msngr.utils.areDates((new Date()), null, "Chickens")).to.equal(false); - expect(msngr.utils.areDates((new Date()), "Totes!", 5)).to.equal(false); + it("msngr.areDates()", function () { + expect(msngr.areDates((new Date()), (new Date()))).to.equal(true); + expect(msngr.areDates((new Date()), undefined, "Chickens")).to.equal(false); + expect(msngr.areDates((new Date()), null, "Chickens")).to.equal(false); + expect(msngr.areDates((new Date()), "Totes!", 5)).to.equal(false); }); // areNumbers() - it("msngr.utils.areNumbers()", function () { - expect(msngr.utils.areNumbers(15, 12, 90000, -1)).to.equal(true); - expect(msngr.utils.areNumbers(15, undefined, 90000, -1)).to.equal(false); - expect(msngr.utils.areNumbers(15, 12, "whatever", -1)).to.equal(false); + it("msngr.areNumbers()", function () { + expect(msngr.areNumbers(15, 12, 90000, -1)).to.equal(true); + expect(msngr.areNumbers(15, undefined, 90000, -1)).to.equal(false); + expect(msngr.areNumbers(15, 12, "whatever", -1)).to.equal(false); }); // areArrays() - it("msngr.utils.areArrays()", function () { - expect(msngr.utils.areArrays([], [15, 45], ["test"])).to.equal(true); - expect(msngr.utils.areArrays([], [15, 45], undefined)).to.equal(false); - expect(msngr.utils.areArrays([], 0, ["test"])).to.equal(false); + it("msngr.areArrays()", function () { + expect(msngr.areArrays([], [15, 45], ["test"])).to.equal(true); + expect(msngr.areArrays([], [15, 45], undefined)).to.equal(false); + expect(msngr.areArrays([], 0, ["test"])).to.equal(false); }); // areObjects() - it("msngr.utils.areObjects()", function () { - expect(msngr.utils.areObjects({}, { k: true })).to.equal(true); - expect(msngr.utils.areObjects({}, "CHICKENS FOR SALE")).to.equal(false); - expect(msngr.utils.areObjects(null, { k: true })).to.equal(false); + it("msngr.areObjects()", function () { + expect(msngr.areObjects({}, { k: true })).to.equal(true); + expect(msngr.areObjects({}, "CHICKENS FOR SALE")).to.equal(false); + expect(msngr.areObjects(null, { k: true })).to.equal(false); }); // areFunctions() - it("msngr.utils.areFunctions()", function () { - expect(msngr.utils.areFunctions(function () {}, function () {})).to.equal(true); - expect(msngr.utils.areFunctions("yup()", function () {})).to.equal(false); - expect(msngr.utils.areFunctions(function () {}, undefined)).to.equal(false); + it("msngr.areFunctions()", function () { + expect(msngr.areFunctions(function () {}, function () {})).to.equal(true); + expect(msngr.areFunctions("yup()", function () {})).to.equal(false); + expect(msngr.areFunctions(function () {}, undefined)).to.equal(false); }); // areEmptyStrings() - it("msngr.utils.areEmptyStrings()", function () { - expect(msngr.utils.areEmptyStrings("", undefined, " ")).to.equal(true); - expect(msngr.utils.areEmptyStrings("", undefined, " a ")).to.equal(false); - expect(msngr.utils.areEmptyStrings({ }, undefined, " ")).to.equal(false); + it("msngr.areEmptyStrings()", function () { + expect(msngr.areEmptyStrings("", undefined, " ")).to.equal(true); + expect(msngr.areEmptyStrings("", undefined, " a ")).to.equal(false); + expect(msngr.areEmptyStrings({ }, undefined, " ")).to.equal(false); }); }); diff --git a/src/utils/validation.cspec.js b/src/utils/validation.cspec.js index afe30cf..c27f026 100644 --- a/src/utils/validation.cspec.js +++ b/src/utils/validation.cspec.js @@ -13,12 +13,12 @@ if (typeof msngr === "undefined" && typeof window === "undefined") { describe("./utils/validation.js", function () { "use strict"; - it("msngr.utils.getType(obj) - obj is a HTMLDivElement", function () { - expect(msngr.utils.getType(document.createElement("div"))).to.equal("[object HTMLDivElement]"); + it("msngr.getType(obj) - obj is a HTMLDivElement", function () { + expect(msngr.getType(document.createElement("div"))).to.equal("[object HTMLDivElement]"); }); - it("msngr.utils.getType(obj) - obj is a HTMLInputElement", function () { - expect(msngr.utils.getType(document.createElement("input"))).to.equal("[object HTMLInputElement]"); + it("msngr.getType(obj) - obj is a HTMLInputElement", function () { + expect(msngr.getType(document.createElement("input"))).to.equal("[object HTMLInputElement]"); }); }); diff --git a/src/utils/validation.js b/src/utils/validation.js index 646fd75..981b8d8 100644 --- a/src/utils/validation.js +++ b/src/utils/validation.js @@ -1,93 +1,92 @@ -msngr.extend((function () { +msngr.extend((function (external, internal) { "use strict"; - return { - utils: { - getType: function (obj) { - if (!msngr.utils.exist(obj)) { - return "" + obj; - } - return Object.prototype.toString.call(obj); - }, - isArguments: function (obj) { - return (msngr.utils.getType(obj) === "[object Arguments]"); - }, - areArguments: function () { - return msngr.utils.reiterativeValidation(msngr.utils.isArguments, msngr.utils.argumentsToArray(arguments)); - }, - isNullOrUndefined: function (obj) { - return (obj === undefined || obj === null); - }, - exist: function (obj) { - return !msngr.utils.isNullOrUndefined(obj); - }, - exists: function () { - return msngr.utils.reiterativeValidation(msngr.utils.exist, msngr.utils.argumentsToArray(arguments)); - }, - isString: function (str) { - return (msngr.utils.getType(str) === "[object String]"); - }, - areStrings: function () { - return msngr.utils.reiterativeValidation(msngr.utils.isString, msngr.utils.argumentsToArray(arguments)); - }, - isDate: function (obj) { - return (msngr.utils.getType(obj) === "[object Date]"); - }, - areDates: function () { - return msngr.utils.reiterativeValidation(msngr.utils.isDate, msngr.utils.argumentsToArray(arguments)); - }, - isArray: function (obj) { - return (msngr.utils.getType(obj) === "[object Array]"); - }, - areArrays: function () { - return msngr.utils.reiterativeValidation(msngr.utils.isArray, msngr.utils.argumentsToArray(arguments)); - }, - isNumber: function (obj) { - return (msngr.utils.getType(obj) === "[object Number]"); - }, - areNumbers: function () { - return msngr.utils.reiterativeValidation(msngr.utils.isNumber, msngr.utils.argumentsToArray(arguments)); - }, - isObject: function (obj) { - return (msngr.utils.getType(obj) === "[object Object]"); - }, - areObjects: function () { - return msngr.utils.reiterativeValidation(msngr.utils.isObject, msngr.utils.argumentsToArray(arguments)); - }, - isFunction: function (func) { - return (msngr.utils.getType(func) === "[object Function]"); - }, - areFunctions: function () { - return msngr.utils.reiterativeValidation(msngr.utils.isFunction, msngr.utils.argumentsToArray(arguments)); - }, - isEmptyString: function (str) { - var isStr = msngr.utils.isString(str); - if (str === undefined || str === null || (isStr && str.toString().trim().length === 0)) { - return true; - } - return false; - }, - areEmptyStrings: function () { - return msngr.utils.reiterativeValidation(msngr.utils.isEmptyString, msngr.utils.argumentsToArray(arguments)); - }, - hasWildCard: function (str) { - return (str.indexOf("*") !== -1); - }, - reiterativeValidation: function (validationMethod, inputs) { - var result = false; - if (msngr.utils.exist(validationMethod) && msngr.utils.exist(inputs)) { - if (!msngr.utils.isArray(inputs)) { - inputs = [inputs]; - } - for (var i = 0; i < inputs.length; ++i) { - result = validationMethod.apply(this, [inputs[i]]); - if (result === false) { - break; - } - } + internal.reiterativeValidation = function (validationMethod, inputs) { + var result = false; + if (external.exist(validationMethod) && external.exist(inputs)) { + if (!external.isArray(inputs)) { + inputs = [inputs]; + } + for (var i = 0; i < inputs.length; ++i) { + result = validationMethod.apply(this, [inputs[i]]); + if (result === false) { + break; } - return result; } - } + } + return result; + }; + + return { + getType: function (obj) { + if (!external.exist(obj)) { + return "" + obj; + } + return Object.prototype.toString.call(obj); + }, + isArguments: function (obj) { + return (external.getType(obj) === "[object Arguments]"); + }, + areArguments: function () { + return internal.reiterativeValidation(external.isArguments, external.argumentsToArray(arguments)); + }, + isNullOrUndefined: function (obj) { + return (obj === undefined || obj === null); + }, + exist: function (obj) { + return !external.isNullOrUndefined(obj); + }, + exists: function () { + return internal.reiterativeValidation(external.exist, external.argumentsToArray(arguments)); + }, + isString: function (str) { + return (external.getType(str) === "[object String]"); + }, + areStrings: function () { + return internal.reiterativeValidation(external.isString, external.argumentsToArray(arguments)); + }, + isDate: function (obj) { + return (external.getType(obj) === "[object Date]"); + }, + areDates: function () { + return internal.reiterativeValidation(external.isDate, external.argumentsToArray(arguments)); + }, + isArray: function (obj) { + return (external.getType(obj) === "[object Array]"); + }, + areArrays: function () { + return internal.reiterativeValidation(external.isArray, external.argumentsToArray(arguments)); + }, + isNumber: function (obj) { + return (external.getType(obj) === "[object Number]"); + }, + areNumbers: function () { + return internal.reiterativeValidation(external.isNumber, external.argumentsToArray(arguments)); + }, + isObject: function (obj) { + return (external.getType(obj) === "[object Object]"); + }, + areObjects: function () { + return internal.reiterativeValidation(external.isObject, external.argumentsToArray(arguments)); + }, + isFunction: function (func) { + return (external.getType(func) === "[object Function]"); + }, + areFunctions: function () { + return internal.reiterativeValidation(external.isFunction, external.argumentsToArray(arguments)); + }, + isEmptyString: function (str) { + var isStr = external.isString(str); + if (str === undefined || str === null || (isStr && str.toString().trim().length === 0)) { + return true; + } + return false; + }, + areEmptyStrings: function () { + return internal.reiterativeValidation(external.isEmptyString, external.argumentsToArray(arguments)); + }, + hasWildCard: function (str) { + return (str.indexOf("*") !== -1); + } }; -}())); +}));