From 4f558235b4996dfc051251aa5b32fa59fa3845d4 Mon Sep 17 00:00:00 2001
From: Commander-lol <ljcapitanio@gmail.com>
Date: Tue, 30 Jun 2015 04:03:07 +0100
Subject: [PATCH] Refactored to make maintenence easier

---
 .gitignore                             |   1 +
 Gruntfile.js                           |  30 +++
 bower.json                             |   4 +-
 dist/tinydom.js                        | 283 +++++++++++++++++++++++++
 dist/tinydom.min.js                    |   2 +
 js/tinyDOM.min.js                      |   1 -
 js/tinyDOM.min.js.map                  |   1 -
 package.json                           |  29 +++
 src/Ajax.js                            |  49 +++++
 src/Batch.js                           | 112 ++++++++++
 src/Core.js                            |  58 +++++
 src/Json.js                            |  33 +++
 src/Util.js                            |  27 +++
 {js => src}/tinyDOM.js                 |   0
 {tests => test/files}/get.html         |   4 +-
 {tests => test/files}/jsonresponse.php |   0
 test.html => test/test.html            |   6 +-
 {js => test}/test.js                   |   2 +-
 18 files changed, 632 insertions(+), 10 deletions(-)
 create mode 100644 .gitignore
 create mode 100644 Gruntfile.js
 create mode 100644 dist/tinydom.js
 create mode 100644 dist/tinydom.min.js
 delete mode 100644 js/tinyDOM.min.js
 delete mode 100644 js/tinyDOM.min.js.map
 create mode 100644 package.json
 create mode 100644 src/Ajax.js
 create mode 100644 src/Batch.js
 create mode 100644 src/Core.js
 create mode 100644 src/Json.js
 create mode 100644 src/Util.js
 rename {js => src}/tinyDOM.js (100%)
 rename {tests => test/files}/get.html (93%)
 rename {tests => test/files}/jsonresponse.php (100%)
 rename test.html => test/test.html (92%)
 rename {js => test}/test.js (91%)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3c3629e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+node_modules
diff --git a/Gruntfile.js b/Gruntfile.js
new file mode 100644
index 0000000..c04657f
--- /dev/null
+++ b/Gruntfile.js
@@ -0,0 +1,30 @@
+module.exports = function(grunt) {
+    grunt.initConfig({
+        pkg: grunt.file.readJSON("package.json"),
+        uglify: {
+            options: {
+                banner: "/*! <%= pkg.name %> <%= grunt.template.today(\"yyyy-mm-dd\") %> */\n"
+            },
+            build: {
+                src: "dist/tinydom.js",
+                dest: "dist/tinydom.min.js"
+            }
+        },
+        concat: {
+            options: {
+                seperator: "",
+                banner: "(function () {\n'use strict';\n",
+                footer: "\n}());"
+            },
+            dist: {
+                src: ["src/Core.js", "src/Batch.js", "src/Util.js", "src/Json.js", "src/Ajax.js"],
+                dest: "dist/tinydom.js"
+            }
+        }
+    });
+
+    grunt.loadNpmTasks("grunt-contrib-uglify");
+    grunt.loadNpmTasks("grunt-contrib-concat");
+
+    grunt.registerTask("default", ["concat", "uglify"]);
+}
diff --git a/bower.json b/bower.json
index 04cfd3f..856dc41 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
 {
   "name": "tinyDOM",
-  "version": "1.0.0",
+  "version": "1.0.2",
   "homepage": "https://github.com/Commander-lol/tinyDOM",
   "moduleType": [
     "globals"
@@ -9,7 +9,7 @@
     "Louis Capitanchik <contact@louiscap.co>"
   ],
   "description": "A simple DOM batch manipulation library",
-  "main": "js/tinyDOM.min.js",
+  "main": "dist/tinyDOM.min.js",
   "keywords": [
     "DOM",
     "Batch",
diff --git a/dist/tinydom.js b/dist/tinydom.js
new file mode 100644
index 0000000..5270596
--- /dev/null
+++ b/dist/tinydom.js
@@ -0,0 +1,283 @@
+(function () {
+	'use strict';
+var proto,
+    TinyDOMFunction,
+    tinyDOM;
+
+/*
+* Polyfill from https://gist.github.com/elijahmanor/6452535
+*/
+if (Element && !Element.prototype.matches) {
+    proto = Element.prototype;
+    proto.matches = proto.matchesSelector ||
+                    proto.mozMatchesSelector || proto.msMatchesSelector ||
+                    proto.oMatchesSelector || proto.webkitMatchesSelector;
+}
+/*
+* End Polyfill
+*/
+
+TinyDOMFunction = function (selector) {
+    var elements, i, e;
+
+    if (!(this instanceof TinyDOMFunction)) {
+        return new TinyDOMFunction(selector);
+    }
+
+    if (selector === null || typeof (selector) === 'undefined') {
+        this.length = 0;
+    } else if (typeof (selector) === 'string') {
+        elements = document.querySelectorAll(selector);
+
+        this.length = elements.length;
+        for (i = 0; i < elements.length; i++) {
+            e = elements.item(i);
+            this[i] = e;
+        }
+    } else if (selector.length) {
+        for (i = 0; i < selector.length; i++) {
+            e = selector[i];
+            this[i] = e;
+        }
+    } else {
+        this[0] = selector;
+        this.length = 1;
+    }
+
+    return this;
+};
+
+tinyDOM = function (selector) {
+    return new TinyDOMFunction(selector);
+};
+
+if (!window.μ) {
+    window.μ = tinyDOM;
+}
+
+if (!window.mu) {
+    window.mu = tinyDOM;
+}
+tinyDOM.fn = TinyDOMFunction.prototype = {
+    each: function (fn) {
+        var l = this.length;
+        while (l--) {
+            fn(l, this[l], this);
+        }
+        return this;
+    },
+    on: function (ev, del, fn) {
+        if (typeof (del) === 'string') {
+            this.each(function (i, e) {
+                e.addEventListener(ev, function (firedevent) {
+                    var target = firedevent.target,
+                        matched = false;
+                    do {
+                        if (target && target.matches(del)) {
+                            fn.call(target, firedevent);
+                            matched = true;
+                        } else {
+                            target = target.parentNode;
+                            if (!target || !target.matches || target === e) {
+                                matched = true;
+                            }
+                        }
+                    } while (matched !== true);
+
+                });
+            });
+        } else {
+            fn = del;
+            this.each(function (i, e) {
+                e.addEventListener(ev, fn);
+            });
+        }
+        return this;
+    },
+    first: function () {
+        if (typeof (this[0]) !== 'undefined') {
+            return tinyDOM(this[0]);
+        } else {
+            return null;
+        }
+    },
+    parent: function (selector) {
+        var e = this[0].parentNode, stn = true;
+        if (tinyDOM.exists(selector)) {
+            while (e !== null && e !== document) {
+                if (e.matches(selector)) {
+                    stn = false;
+                    break;
+                } else {
+                    e = e.parentNode;
+                }
+            }
+            e = stn ? null : e;
+        }
+        return tinyDOM(e);
+    },
+    children: function () {
+        var n = this[0].childNodes,
+            a = [],
+            i;
+        for (i = 0; i < n.length; i++) {
+            if (tinyDOM.isElement(n[i])) {
+                a.push(n[i]);
+            }
+        }
+        return tinyDOM(a);
+    },
+    data: function (key, value) {
+        if (typeof (value) !== 'undefined') {
+            this.each(function(i, e){
+                e.setAttribute('data-' + key, value);
+            });
+            return this;
+        } else {
+            return this[0].getAttribute('data-' + key);
+        }
+    },
+    attr: function (key, value) {
+        if (typeof (value) !== 'undefined') {
+            this.each(function(i, e){
+                e.setAttribute(key, value);
+            });
+            return this;
+        } else {
+            return this[0].getAttribute(key);
+        }
+    },
+    class: function(classname, addremove){
+        if(tinyDOM.exists(addremove)){
+            this.each(function(i, e){
+                e.classList.toggle(classname, addremove);
+            });
+        } else {
+            this.each(function(i, e){
+                e.classList.toggle(classname);
+            });
+        }
+        return this;
+    },
+    trigger: function (eventName, data, bubbles, cancelable) {
+        bubbles = tinyDOM.exists(bubbles) ? bubbles : true;
+        cancelable = tinyDOM.exists(cancelable) ? cancelable : true;
+
+        var event = new CustomEvent(eventName, data, bubbles, cancelable);
+        this.each(function (i, e) {
+            e.dispatchEvent(event);
+        });
+        return this;
+    }
+};
+
+tinyDOM.ready = function(fn) {
+    document.addEventListener("DOMContentLoaded", fn);
+};
+
+tinyDOM.isElement = function (node) {
+    var is = false;
+    try {
+        is = node instanceof HTMLElement;
+    } catch (e) {
+        is = node.nodeType && node.nodeType === 1;
+    }
+    return is;
+};
+
+tinyDOM.exists = function (obj) {
+    return obj !== null && typeof (obj) !== 'undefined';
+};
+
+tinyDOM.byID = function (id) {
+    return tinyDOM(document.getElementById(id));
+};
+
+tinyDOM.triggerOn = function (target, eventName, data, bubbles, cancelable) {
+    bubbles = tinyDOM.exists(bubbles) ? bubbles : true;
+    cancelable = tinyDOM.exists(cancelable) ? cancelable : true;
+    target.dispatchEvent(new CustomEvent(eventName, data, bubbles, cancelable));
+};
+tinyDOM.json = {
+    keys: function(json) {
+        var kys = [],
+            indx;
+        for(indx in json){
+            if(json.hasOwnProperty(indx)){
+                kys.push(indx);
+            }
+        }
+        return kys;
+    },
+    is: function (obj) {
+        try {
+            JSON.parse(obj);
+            return obj !== null && typeof obj !== "undefined";
+        } catch (e) {
+            return false;
+        }
+    },
+    merge: function (json1, json2) {
+		if (!this.exists(json1) || !this.exists(json2)) {
+			return null;
+		} else {
+            var prop;
+			for (prop in json2) {
+				if (json2.hasOwnProperty(prop)) {
+					json1[prop] = json2[prop];
+				}
+			}
+			return json1;
+		}
+	}
+};
+tinyDOM.ajax = function (options) {
+    var req = new XMLHttpRequest(),
+        _this = this,
+        ev,
+        i,
+        params = {
+            method: 'GET',
+            url: '',
+            async: true,
+            user: null,
+            password: null,
+            responseType: 'text',
+            data: null,
+            headers: [],
+            callbacks: {}
+        },
+        makeListener = function (callback) {
+            return function (data) {
+                callback(data.currentTarget.response, data);
+            };
+        };
+
+    this.merge(params, options);
+
+    req.responseType = params.responseType;
+
+    if (this.exists(params.callbacks)) {
+        for (ev in params.callbacks) {
+            if (params.callbacks.hasOwnProperty(ev)) {
+                req.addEventListener(ev, makeListener(params.callbacks[ev]));
+            }
+        }
+    }
+
+    req.open(
+        params.method,
+        params.url,
+        params.async,
+        params.user,
+        params.password
+    );
+
+    for (i = 0; i < params.headers.length; i++) {
+        req.setRequestHeader(params.headers[i].header, params.headers[i].value);
+    }
+
+    req.send(params.data);
+    return req;
+};
+}());
diff --git a/dist/tinydom.min.js b/dist/tinydom.min.js
new file mode 100644
index 0000000..7803259
--- /dev/null
+++ b/dist/tinydom.min.js
@@ -0,0 +1,2 @@
+/*! tinydom 2015-06-30 */
+!function(){"use strict";var a,b,c;Element&&!Element.prototype.matches&&(a=Element.prototype,a.matches=a.matchesSelector||a.mozMatchesSelector||a.msMatchesSelector||a.oMatchesSelector||a.webkitMatchesSelector),b=function(a){var c,d,e;if(!(this instanceof b))return new b(a);if(null===a||"undefined"==typeof a)this.length=0;else if("string"==typeof a)for(c=document.querySelectorAll(a),this.length=c.length,d=0;d<c.length;d++)e=c.item(d),this[d]=e;else if(a.length)for(d=0;d<a.length;d++)e=a[d],this[d]=e;else this[0]=a,this.length=1;return this},c=function(a){return new b(a)},window.μ||(window.μ=c),window.mu||(window.mu=c),c.fn=b.prototype={each:function(a){for(var b=this.length;b--;)a(b,this[b],this);return this},on:function(a,b,c){return"string"==typeof b?this.each(function(d,e){e.addEventListener(a,function(a){var d=a.target,f=!1;do d&&d.matches(b)?(c.call(d,a),f=!0):(d=d.parentNode,d&&d.matches&&d!==e||(f=!0));while(f!==!0)})}):(c=b,this.each(function(b,d){d.addEventListener(a,c)})),this},first:function(){return"undefined"!=typeof this[0]?c(this[0]):null},parent:function(a){var b=this[0].parentNode,d=!0;if(c.exists(a)){for(;null!==b&&b!==document;){if(b.matches(a)){d=!1;break}b=b.parentNode}b=d?null:b}return c(b)},children:function(){var a,b=this[0].childNodes,d=[];for(a=0;a<b.length;a++)c.isElement(b[a])&&d.push(b[a]);return c(d)},data:function(a,b){return"undefined"!=typeof b?(this.each(function(c,d){d.setAttribute("data-"+a,b)}),this):this[0].getAttribute("data-"+a)},attr:function(a,b){return"undefined"!=typeof b?(this.each(function(c,d){d.setAttribute(a,b)}),this):this[0].getAttribute(a)},"class":function(a,b){return c.exists(b)?this.each(function(c,d){d.classList.toggle(a,b)}):this.each(function(b,c){c.classList.toggle(a)}),this},trigger:function(a,b,d,e){d=c.exists(d)?d:!0,e=c.exists(e)?e:!0;var f=new CustomEvent(a,b,d,e);return this.each(function(a,b){b.dispatchEvent(f)}),this}},c.ready=function(a){document.addEventListener("DOMContentLoaded",a)},c.isElement=function(a){var b=!1;try{b=a instanceof HTMLElement}catch(c){b=a.nodeType&&1===a.nodeType}return b},c.exists=function(a){return null!==a&&"undefined"!=typeof a},c.byID=function(a){return c(document.getElementById(a))},c.triggerOn=function(a,b,d,e,f){e=c.exists(e)?e:!0,f=c.exists(f)?f:!0,a.dispatchEvent(new CustomEvent(b,d,e,f))},c.json={keys:function(a){var b,c=[];for(b in a)a.hasOwnProperty(b)&&c.push(b);return c},is:function(a){try{return JSON.parse(a),null!==a&&"undefined"!=typeof a}catch(b){return!1}},merge:function(a,b){if(this.exists(a)&&this.exists(b)){var c;for(c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}return null}},c.ajax=function(a){var b,c,d=new XMLHttpRequest,e={method:"GET",url:"",async:!0,user:null,password:null,responseType:"text",data:null,headers:[],callbacks:{}},f=function(a){return function(b){a(b.currentTarget.response,b)}};if(this.merge(e,a),d.responseType=e.responseType,this.exists(e.callbacks))for(b in e.callbacks)e.callbacks.hasOwnProperty(b)&&d.addEventListener(b,f(e.callbacks[b]));for(d.open(e.method,e.url,e.async,e.user,e.password),c=0;c<e.headers.length;c++)d.setRequestHeader(e.headers[c].header,e.headers[c].value);return d.send(e.data),d}}();
diff --git a/js/tinyDOM.min.js b/js/tinyDOM.min.js
deleted file mode 100644
index e576755..0000000
--- a/js/tinyDOM.min.js
+++ /dev/null
@@ -1 +0,0 @@
-!function(){"use strict";var t,e,n;Element&&!Element.prototype.matches&&(t=Element.prototype,t.matches=t.matchesSelector||t.mozMatchesSelector||t.msMatchesSelector||t.oMatchesSelector||t.webkitMatchesSelector),e=function(t){var e,n,r;if(null===t||"undefined"==typeof t)this.length=0;else if("string"==typeof t)for(e=document.querySelectorAll(t),this.length=e.length,n=0;n<e.length;n++)r=e.item(n),this[n]=r;else if(t.length)for(n=0;n<t.length;n++)r=t[n],this[n]=r;else this[0]=t,this.length=1;return this},n=function(t){return new e(t)},n.fn=e.prototype={each:function(t){for(var e=this.length;e--;)t(e,this[e],this);return this},on:function(t,e,n){return"string"==typeof e?this.each(function(r,s){s.addEventListener(t,function(t){var r=t.target,i=!1;do r&&r.matches(e)?(n.call(r,t),i=!0):(r=r.parentNode,r&&r.matches&&r!==s||(i=!0));while(i!==!0)})}):(n=e,this.each(function(e,r){r.addEventListener(t,n)})),this},first:function(){return"undefined"!=typeof this[0]?n(this[0]):null},parent:function(t){var e=this[0].parentNode,r=!0;if(n.exists(t)){for(;null!==e&&e!==document;){if(e.matches(t)){r=!1;break}e=e.parentNode}e=r?null:e}return n(e)},children:function(){var t,e=this[0].childNodes,r=[];for(t=0;t<e.length;t++)n.isElement(e[t])&&r.push(e[t]);return n(r)},data:function(t,e){return"undefined"!=typeof e?(this.each(function(n,r){r.setAttribute("data-"+t,e)}),this):this[0].getAttribute("data-"+t)},attr:function(t,e){return"undefined"!=typeof e?(this.each(function(n,r){r.setAttribute(t,e)}),this):this[0].getAttribute(t)},"class":function(t,e){return this.each(n.exists(e)?function(n,r){r.classList.toggle(t,e)}:function(e,n){n.classList.toggle(t)}),this},trigger:function(t,e,r,s){r=n.exists(r)?r:!0,s=n.exists(s)?s:!0;var i=new CustomEvent(t,e,r,s);return this.each(function(t,e){e.dispatchEvent(i)}),this}},n.ready=function(t){document.addEventListener("DOMContentLoaded",t)},n.isElement=function(t){var e=!1;try{e=t instanceof HTMLElement}catch(n){e=t.nodeType&&1===t.nodeType}return e},n.isJson=function(t){try{return JSON.parse(t),null!==t&&"undefined"!=typeof t}catch(e){return!1}},n.exists=function(t){return null!==t&&"undefined"!=typeof t},n.merge=function(t,e){if(this.exists(t)&&this.exists(e)){var n;for(n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}return null},n.byID=function(t){return n(document.getElementById(t))},n.triggerOn=function(t,e,r,s,i){s=n.exists(s)?s:!0,i=n.exists(i)?i:!0,t.dispatchEvent(new CustomEvent(e,r,s,i))},n.json={keys:function(t){var e,n=[];for(e in t)t.hasOwnProperty(e)&&n.push(e);return n}},n.ajax=function(t){var e,n,r=new XMLHttpRequest,s={method:"GET",url:"",async:!0,user:null,password:null,responseType:"text",data:null,headers:[],callbacks:{}},i=function(t){return function(e){t(e.currentTarget.response,e)}};if(this.merge(s,t),r.responseType=s.responseType,this.exists(s.callbacks))for(e in s.callbacks)s.callbacks.hasOwnProperty(e)&&r.addEventListener(e,i(s.callbacks[e]));for(r.open(s.method,s.url,s.async,s.user,s.password),n=0;n<s.headers.length;n++)r.setRequestHeader(s.headers[n].header,s.headers[n].value);return r.send(s.data),r},window.μ||(window.μ=n),window.mu||(window.mu=n)}();
diff --git a/js/tinyDOM.min.js.map b/js/tinyDOM.min.js.map
deleted file mode 100644
index b390ffc..0000000
--- a/js/tinyDOM.min.js.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"sources":["tinyDOM.js"],"names":["proto","TinyDOMFunction","tinyDOM","Element","prototype","matches","matchesSelector","mozMatchesSelector","msMatchesSelector","oMatchesSelector","webkitMatchesSelector","selector","elements","i","e","this","length","document","querySelectorAll","item","fn","each","l","on","ev","del","addEventListener","firedevent","target","matched","call","parentNode","first","parent","stn","exists","children","n","childNodes","a","isElement","push","data","key","value","setAttribute","getAttribute","attr","class","classname","addremove","classList","toggle","trigger","eventName","bubbles","cancelable","event","CustomEvent","dispatchEvent","ready","node","is","HTMLElement","nodeType","isJson","obj","JSON","parse","merge","json1","json2","prop","hasOwnProperty","byID","id","getElementById","triggerOn","json","keys","indx","kys","ajax","options","req","XMLHttpRequest","params","method","url","async","user","password","responseType","headers","callbacks","makeListener","callback","currentTarget","response","open","setRequestHeader","header","send","window","μ","mu"],"mappings":"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"}
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..85967fa
--- /dev/null
+++ b/package.json
@@ -0,0 +1,29 @@
+{
+  "name": "tinydom",
+  "version": "1.0.1",
+  "description": "A simple DOM batch manipulation library",
+  "main": "dist/tinydom.js",
+  "directories": {
+    "test": "test"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/Commander-lol/tinyDOM.git"
+  },
+  "keywords": [
+    "DOM",
+    "Batch",
+    "Manipulation"
+  ],
+  "author": "Louis Capitanchik",
+  "license": "ISC",
+  "bugs": {
+    "url": "https://github.com/Commander-lol/tinyDOM/issues"
+  },
+  "homepage": "https://github.com/Commander-lol/tinyDOM#readme",
+  "devDependencies": {
+    "grunt": "^0.4.5",
+    "grunt-contrib-concat": "^0.5.1",
+    "grunt-contrib-uglify": "^0.9.1"
+  }
+}
diff --git a/src/Ajax.js b/src/Ajax.js
new file mode 100644
index 0000000..f765867
--- /dev/null
+++ b/src/Ajax.js
@@ -0,0 +1,49 @@
+tinyDOM.ajax = function (options) {
+    var req = new XMLHttpRequest(),
+        _this = this,
+        ev,
+        i,
+        params = {
+            method: 'GET',
+            url: '',
+            async: true,
+            user: null,
+            password: null,
+            responseType: 'text',
+            data: null,
+            headers: [],
+            callbacks: {}
+        },
+        makeListener = function (callback) {
+            return function (data) {
+                callback(data.currentTarget.response, data);
+            };
+        };
+
+    this.merge(params, options);
+
+    req.responseType = params.responseType;
+
+    if (this.exists(params.callbacks)) {
+        for (ev in params.callbacks) {
+            if (params.callbacks.hasOwnProperty(ev)) {
+                req.addEventListener(ev, makeListener(params.callbacks[ev]));
+            }
+        }
+    }
+
+    req.open(
+        params.method,
+        params.url,
+        params.async,
+        params.user,
+        params.password
+    );
+
+    for (i = 0; i < params.headers.length; i++) {
+        req.setRequestHeader(params.headers[i].header, params.headers[i].value);
+    }
+
+    req.send(params.data);
+    return req;
+};
diff --git a/src/Batch.js b/src/Batch.js
new file mode 100644
index 0000000..ee6a022
--- /dev/null
+++ b/src/Batch.js
@@ -0,0 +1,112 @@
+tinyDOM.fn = TinyDOMFunction.prototype = {
+    each: function (fn) {
+        var l = this.length;
+        while (l--) {
+            fn(l, this[l], this);
+        }
+        return this;
+    },
+    on: function (ev, del, fn) {
+        if (typeof (del) === 'string') {
+            this.each(function (i, e) {
+                e.addEventListener(ev, function (firedevent) {
+                    var target = firedevent.target,
+                        matched = false;
+                    do {
+                        if (target && target.matches(del)) {
+                            fn.call(target, firedevent);
+                            matched = true;
+                        } else {
+                            target = target.parentNode;
+                            if (!target || !target.matches || target === e) {
+                                matched = true;
+                            }
+                        }
+                    } while (matched !== true);
+
+                });
+            });
+        } else {
+            fn = del;
+            this.each(function (i, e) {
+                e.addEventListener(ev, fn);
+            });
+        }
+        return this;
+    },
+    first: function () {
+        if (typeof (this[0]) !== 'undefined') {
+            return tinyDOM(this[0]);
+        } else {
+            return null;
+        }
+    },
+    parent: function (selector) {
+        var e = this[0].parentNode, stn = true;
+        if (tinyDOM.exists(selector)) {
+            while (e !== null && e !== document) {
+                if (e.matches(selector)) {
+                    stn = false;
+                    break;
+                } else {
+                    e = e.parentNode;
+                }
+            }
+            e = stn ? null : e;
+        }
+        return tinyDOM(e);
+    },
+    children: function () {
+        var n = this[0].childNodes,
+            a = [],
+            i;
+        for (i = 0; i < n.length; i++) {
+            if (tinyDOM.isElement(n[i])) {
+                a.push(n[i]);
+            }
+        }
+        return tinyDOM(a);
+    },
+    data: function (key, value) {
+        if (typeof (value) !== 'undefined') {
+            this.each(function(i, e){
+                e.setAttribute('data-' + key, value);
+            });
+            return this;
+        } else {
+            return this[0].getAttribute('data-' + key);
+        }
+    },
+    attr: function (key, value) {
+        if (typeof (value) !== 'undefined') {
+            this.each(function(i, e){
+                e.setAttribute(key, value);
+            });
+            return this;
+        } else {
+            return this[0].getAttribute(key);
+        }
+    },
+    class: function(classname, addremove){
+        if(tinyDOM.exists(addremove)){
+            this.each(function(i, e){
+                e.classList.toggle(classname, addremove);
+            });
+        } else {
+            this.each(function(i, e){
+                e.classList.toggle(classname);
+            });
+        }
+        return this;
+    },
+    trigger: function (eventName, data, bubbles, cancelable) {
+        bubbles = tinyDOM.exists(bubbles) ? bubbles : true;
+        cancelable = tinyDOM.exists(cancelable) ? cancelable : true;
+
+        var event = new CustomEvent(eventName, data, bubbles, cancelable);
+        this.each(function (i, e) {
+            e.dispatchEvent(event);
+        });
+        return this;
+    }
+};
diff --git a/src/Core.js b/src/Core.js
new file mode 100644
index 0000000..4c8a3ca
--- /dev/null
+++ b/src/Core.js
@@ -0,0 +1,58 @@
+var proto,
+    TinyDOMFunction,
+    tinyDOM;
+
+/*
+* Polyfill from https://gist.github.com/elijahmanor/6452535
+*/
+if (Element && !Element.prototype.matches) {
+    proto = Element.prototype;
+    proto.matches = proto.matchesSelector ||
+                    proto.mozMatchesSelector || proto.msMatchesSelector ||
+                    proto.oMatchesSelector || proto.webkitMatchesSelector;
+}
+/*
+* End Polyfill
+*/
+
+TinyDOMFunction = function (selector) {
+    var elements, i, e;
+
+    if (!(this instanceof TinyDOMFunction)) {
+        return new TinyDOMFunction(selector);
+    }
+
+    if (selector === null || typeof (selector) === 'undefined') {
+        this.length = 0;
+    } else if (typeof (selector) === 'string') {
+        elements = document.querySelectorAll(selector);
+
+        this.length = elements.length;
+        for (i = 0; i < elements.length; i++) {
+            e = elements.item(i);
+            this[i] = e;
+        }
+    } else if (selector.length) {
+        for (i = 0; i < selector.length; i++) {
+            e = selector[i];
+            this[i] = e;
+        }
+    } else {
+        this[0] = selector;
+        this.length = 1;
+    }
+
+    return this;
+};
+
+tinyDOM = function (selector) {
+    return new TinyDOMFunction(selector);
+};
+
+if (!window.μ) {
+    window.μ = tinyDOM;
+}
+
+if (!window.mu) {
+    window.mu = tinyDOM;
+}
diff --git a/src/Json.js b/src/Json.js
new file mode 100644
index 0000000..8d9c600
--- /dev/null
+++ b/src/Json.js
@@ -0,0 +1,33 @@
+tinyDOM.json = {
+    keys: function(json) {
+        var kys = [],
+            indx;
+        for(indx in json){
+            if(json.hasOwnProperty(indx)){
+                kys.push(indx);
+            }
+        }
+        return kys;
+    },
+    is: function (obj) {
+        try {
+            JSON.parse(obj);
+            return obj !== null && typeof obj !== "undefined";
+        } catch (e) {
+            return false;
+        }
+    },
+    merge: function (json1, json2) {
+		if (!this.exists(json1) || !this.exists(json2)) {
+			return null;
+		} else {
+            var prop;
+			for (prop in json2) {
+				if (json2.hasOwnProperty(prop)) {
+					json1[prop] = json2[prop];
+				}
+			}
+			return json1;
+		}
+	}
+};
diff --git a/src/Util.js b/src/Util.js
new file mode 100644
index 0000000..6f29364
--- /dev/null
+++ b/src/Util.js
@@ -0,0 +1,27 @@
+tinyDOM.ready = function(fn) {
+    document.addEventListener("DOMContentLoaded", fn);
+};
+
+tinyDOM.isElement = function (node) {
+    var is = false;
+    try {
+        is = node instanceof HTMLElement;
+    } catch (e) {
+        is = node.nodeType && node.nodeType === 1;
+    }
+    return is;
+};
+
+tinyDOM.exists = function (obj) {
+    return obj !== null && typeof (obj) !== 'undefined';
+};
+
+tinyDOM.byID = function (id) {
+    return tinyDOM(document.getElementById(id));
+};
+
+tinyDOM.triggerOn = function (target, eventName, data, bubbles, cancelable) {
+    bubbles = tinyDOM.exists(bubbles) ? bubbles : true;
+    cancelable = tinyDOM.exists(cancelable) ? cancelable : true;
+    target.dispatchEvent(new CustomEvent(eventName, data, bubbles, cancelable));
+};
diff --git a/js/tinyDOM.js b/src/tinyDOM.js
similarity index 100%
rename from js/tinyDOM.js
rename to src/tinyDOM.js
diff --git a/tests/get.html b/test/files/get.html
similarity index 93%
rename from tests/get.html
rename to test/files/get.html
index 67f22f2..93d1d85 100644
--- a/tests/get.html
+++ b/test/files/get.html
@@ -1,7 +1,7 @@
 <div>
 	<p>
 		This is a piece of text retrieved via some sort of external request.
-		It is, after all, just a fragment of html and would be a terrible 
+		It is, after all, just a fragment of html and would be a terrible
 		page in and of itself.
 	</p>
-</div>		
\ No newline at end of file
+</div>
diff --git a/tests/jsonresponse.php b/test/files/jsonresponse.php
similarity index 100%
rename from tests/jsonresponse.php
rename to test/files/jsonresponse.php
diff --git a/test.html b/test/test.html
similarity index 92%
rename from test.html
rename to test/test.html
index 579d3d4..a91dc47 100644
--- a/test.html
+++ b/test/test.html
@@ -21,7 +21,7 @@
 		<button id="btn-show">SHOW THINGS</button>
 
 		<section class='information'>
-			<h2 data-href='tests/get.html'>About tinyDOM functions</h2>
+			<h2 data-href='files/get.html'>About tinyDOM functions</h2>
 			<p>
 				tinyDOM operates on dom elements to make it a bit easier
 				to select and manipulate them. It also provides some simple
@@ -56,9 +56,9 @@
 		</section>
 	</body>
 
-	<script type="text/javascript" src="js/tinyDOM.js"></script>
+	<script type="text/javascript" src="j../dist/tinyDOM.js"></script>
     <script>mu.ready(function(){console.log("DOM LOADED!");});</script>
-	<script type="text/javascript" src="js/test.js"></script>
+	<script type="text/javascript" src="test.js"></script>
 	<script type="text/javascript">
 	μ('#btn-show').on('click', function(){
 		μ('.important').show();
diff --git a/js/test.js b/test/test.js
similarity index 91%
rename from js/test.js
rename to test/test.js
index 48103e9..ad8684a 100644
--- a/js/test.js
+++ b/test/test.js
@@ -2,7 +2,7 @@ if(!mu.exists(window.muTest)){
   window.muTest = {
     postJsonResponse: function(){
       mu.ajax({
-        url: 'tests/jsonresponse.php',
+        url: 'files/jsonresponse.php',
         method: 'POST',
         responseType: 'json',
         success: function(data){
-- 
GitLab