diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..dd84ea7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/README.md b/README.md index 821e27f..401e1c0 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,15 @@ Observe.js [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Observe.js%2C%20a%20free%20JavaScript%observe%builder&url=https://github.com/williamtroup/Observe.js&hashtags=javascript,html,observe) -[![npm](https://img.shields.io/badge/npmjs-v0.6.1-blue)](https://www.npmjs.com/package/jobserve.js) -[![nuget](https://img.shields.io/badge/nuget-v0.6.1-purple)](https://www.nuget.org/packages/jObserve.js/) +[![npm](https://img.shields.io/badge/npmjs-v0.7.0-blue)](https://www.npmjs.com/package/jobserve.js) +[![nuget](https://img.shields.io/badge/nuget-v0.7.0-purple)](https://www.nuget.org/packages/jObserve.js/) [![license](https://img.shields.io/badge/license-MIT-green)](https://github.com/williamtroup/Observe.js/blob/main/LICENSE.txt) [![discussions Welcome](https://img.shields.io/badge/discussions-Welcome-red)](https://github.com/williamtroup/Observe.js/discussions) -[![coded by William Troup](https://img.shields.io/badge/coded_by-William_Troup-yellow)](https://github.com/williamtroup) +[![coded by William Troup](https://img.shields.io/badge/coded_by-William_Troup-yellow)](https://www.william-troup.com/) >

A lightweight JavaScript library that allows developers to keep track of changes to JavaScript objects and/or DOM elements.

->

v0.6.1

+>

v0.7.0



@@ -27,18 +27,32 @@ Observe.js

+

What browsers are supported?

All modern browsers (such as Google Chrome, FireFox, and Opera) are fully supported.

+

What are the most recent changes?

To see a list of all the most recent changes, click [here](docs/CHANGE_LOG.md).

+ +

How do I install Observe.js?

+ +You can install the library with npm into your local modules directory using the following command: + +```markdown +npm install jobserve.js +``` +
+
+ +

How do I get started?

To get started using Observe.js, do the following steps: diff --git a/README_NUGET.md b/README_NUGET.md index 2079d3f..0f24bed 100644 --- a/README_NUGET.md +++ b/README_NUGET.md @@ -1,11 +1,11 @@ -# Observe.js v0.6.1 +# Observe.js v0.7.0 [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Observe.js%2C%20a%20free%20JavaScript%observe%builder&url=https://github.com/williamtroup/Observe.js&hashtags=javascript,html,observe) -[![npm](https://img.shields.io/badge/npmjs-v0.6.1-blue)](https://www.npmjs.com/package/jobserve.js) -[![nuget](https://img.shields.io/badge/nuget-v0.6.1-purple)](https://www.nuget.org/packages/jObserve.js/) +[![npm](https://img.shields.io/badge/npmjs-v0.7.0-blue)](https://www.npmjs.com/package/jobserve.js) +[![nuget](https://img.shields.io/badge/nuget-v0.7.0-purple)](https://www.nuget.org/packages/jObserve.js/) [![license](https://img.shields.io/badge/license-MIT-green)](https://github.com/williamtroup/Observe.js/blob/main/LICENSE.txt) [![discussions Welcome](https://img.shields.io/badge/discussions-Welcome-red)](https://github.com/williamtroup/Observe.js/discussions) -[![coded by William Troup](https://img.shields.io/badge/coded_by-William_Troup-yellow)](https://github.com/williamtroup) +[![coded by William Troup](https://img.shields.io/badge/coded_by-William_Troup-yellow)](https://www.william-troup.com/) > A lightweight JavaScript library that allows developers to keep track of changes to JavaScript objects and/or DOM elements. @@ -32,6 +32,15 @@ All modern browsers (such as Google Chrome, FireFox, and Opera) are fully suppor To see a list of all the most recent changes, click [here](https://github.com/williamtroup/Observe.js/blob/main/docs/CHANGE_LOG.md). +## How do I install Observe.js? + +You can install the library with npm into your local modules directory using the following command: + +```markdown +npm install jobserve.js +``` + + ## How do I get started? To get started using Observe.js, do the following steps: diff --git a/dist/observe.js b/dist/observe.js index 56a2be2..d81a3f2 100644 --- a/dist/observe.js +++ b/dist/observe.js @@ -1,296 +1,460 @@ -/*! Observe.js v0.6.1 | (c) Bunoon 2024 | MIT License */ +/*! Observe.js v0.7.0 | (c) Bunoon 2024 | MIT License */ (function() { - function I() { - for (var a = q.domElementTypes, b = a.length, c = 0; c < b; c++) { - var f = y.getElementsByTagName(a[c]); - f = [].slice.call(f); - for (var k = f.length, h = 0; h < k; h++) { - var l = f[h], e = !0; - if (r(l) && l.hasAttribute("data-observe-watch-options")) { - var g = l.getAttribute("data-observe-watch-options"); - p(g) ? (g = A(g), g.parsed && t(g.result) ? (g = J(g.result), p(l.id) || (l.id = K()), l.removeAttribute("data-observe-watch-options"), L(l, g, l.id)) : q.safeMode || (console.error("The attribute 'data-observe-watch-options' is not a valid object."), e = !1)) : q.safeMode || (console.error("The attribute 'data-observe-watch-options' has not been set correctly."), e = !1); - } - if (!e) { + function collectDOMObjects() { + var tagTypes = _configuration.domElementTypes; + var tagTypesLength = tagTypes.length; + var tagTypeIndex = 0; + for (; tagTypeIndex < tagTypesLength; tagTypeIndex++) { + var domElements = _parameter_Document.getElementsByTagName(tagTypes[tagTypeIndex]); + var elements = [].slice.call(domElements); + var elementsLength = elements.length; + var elementIndex = 0; + for (; elementIndex < elementsLength; elementIndex++) { + if (!collectDOMObject(elements[elementIndex])) { break; } } } } - function L(a, b, c) { - var f = null; - if (t(a)) { - f = K(); - var k = J(b); - b = {}; - b.options = k; - b.totalChanges = 0; - p(c) ? (a = y.getElementById(c), r(a) && (b.domElementId = c, b.cachedObject = a.outerHTML, b.originalObject = a.outerHTML)) : (b.cachedObject = JSON.stringify(a), b.originalObject = a); - b.timer = setInterval(function() { - var h = f, l = new Date(); - if (!B(k.starts) || l >= k.starts) { - if (d.hasOwnProperty(h)) { - var e = d[h], g = p(e.domElementId), m = null; - g && (m = y.getElementById(e.domElementId), r(m) ? e.originalObject = m.outerHTML : (e.originalObject = C.empty, u(e.options.onRemove, e.domElementId))); - var v = e.cachedObject, n = e.originalObject; - n = g ? n : JSON.stringify(n); - if (v !== n) { - e.options.reset ? g ? m.outerHTML = e.cachedObject : e.originalObject = A(v).result : e.cachedObject = n; - if (g) { - u(e.options.onChange, v, n); - } else { - if (g = A(v).result, m = A(n).result, w(g) || w(m)) { - u(e.options.onChange, g, m); - } else { - if (w(e.options.propertyNames)) { - for (v = e.options.propertyNames.length, n = 0; n < v; n++) { - var M = e.options.propertyNames[n]; - if (g[M] !== m[M]) { - u(e.options.onChange, g, m); - break; - } - } - } else { - u(e.options.onChange, g, m); - } - D(e.options.onPropertyChange) && N(g, m, e); - } - } - e.totalChanges++; - 0 < e.options.pauseTimeoutOnChange && E(h, e.options.pauseTimeoutOnChange); - e.options.cancelOnChange && x(h); - 0 < e.options.maximumChangesBeforeCanceling && e.totalChanges >= e.options.maximumChangesBeforeCanceling && x(h); + function collectDOMObject(element) { + var result = true; + if (isDefined(element) && element.hasAttribute(_attribute_Name_Watch_Options)) { + var bindingOptionsData = element.getAttribute(_attribute_Name_Watch_Options); + if (isDefinedString(bindingOptionsData)) { + var bindingOptions = getObjectFromString(bindingOptionsData); + if (bindingOptions.parsed && isDefinedObject(bindingOptions.result)) { + bindingOptions = getWatchOptions(bindingOptions.result); + if (!isDefinedString(element.id)) { + element.id = newGuid(); + } + element.removeAttribute(_attribute_Name_Watch_Options); + createWatch(element, bindingOptions, element.id); + } else { + if (!_configuration.safeMode) { + console.error("The attribute '" + _attribute_Name_Watch_Options + "' is not a valid object."); + result = false; + } + } + } else { + if (!_configuration.safeMode) { + console.error("The attribute '" + _attribute_Name_Watch_Options + "' has not been set correctly."); + result = false; + } + } + } + return result; + } + function createWatch(object, options, domElementId) { + var storageId = null; + if (isDefinedObject(object)) { + storageId = newGuid(); + var watchOptions = getWatchOptions(options); + var watch = {}; + var startWatchObject; + watch.options = watchOptions; + watch.totalChanges = 0; + if (isDefinedString(domElementId)) { + var domElement = _parameter_Document.getElementById(domElementId); + if (isDefined(domElement)) { + watch.domElementId = domElementId; + watch.cachedObject = domElement.outerHTML; + watch.originalObject = domElement.outerHTML; + startWatchObject = domElement.outerHTML; + } + } else { + watch.cachedObject = _parameter_Json.stringify(object); + watch.originalObject = object; + startWatchObject = object; + } + if (isDefined(watch.cachedObject)) { + fireCustomTrigger(watch.options.onStart, startWatchObject); + watch.timer = setInterval(function() { + watchTimer(watchOptions, storageId); + }, watchOptions.timeout); + _watches[storageId] = watch; + } + } + return storageId; + } + function watchTimer(watchOptions, storageId) { + var currentDateTime = new Date(); + if (!isDefinedDate(watchOptions.starts) || currentDateTime >= watchOptions.starts) { + watchObjectForChanges(storageId); + if (isDefinedDate(watchOptions.expires) && currentDateTime >= watchOptions.expires) { + cancelWatchObject(storageId); + } + } + } + function watchObjectForChanges(storageId) { + if (_watches.hasOwnProperty(storageId)) { + var watch = _watches[storageId]; + var isDomElement = isDefinedString(watch.domElementId); + var domElement = null; + if (isDomElement) { + domElement = _parameter_Document.getElementById(watch.domElementId); + if (isDefined(domElement)) { + watch.originalObject = domElement.outerHTML; + } else { + watch.originalObject = _string.empty; + fireCustomTrigger(watch.options.onRemove, watch.domElementId); + } + } + var cachedObject = watch.cachedObject; + var originalObject = watch.originalObject; + var originalObjectJson = !isDomElement ? _parameter_Json.stringify(originalObject) : originalObject; + if (cachedObject !== originalObjectJson) { + if (watch.options.reset) { + if (isDomElement) { + domElement.outerHTML = watch.cachedObject; + } else { + watch.originalObject = getObjectFromString(cachedObject).result; + } + } else { + watch.cachedObject = originalObjectJson; + } + if (isDomElement) { + fireCustomTrigger(watch.options.onChange, cachedObject, originalObjectJson); + } else { + var oldValue = getObjectFromString(cachedObject).result; + var newValue = getObjectFromString(originalObjectJson).result; + if (!isDefinedArray(oldValue) && !isDefinedArray(newValue)) { + compareWatchObject(oldValue, newValue, watch); + if (isDefinedFunction(watch.options.onPropertyChange)) { + compareWatchObjectProperties(oldValue, newValue, watch); } + } else { + fireCustomTrigger(watch.options.onChange, oldValue, newValue); } - B(k.expires) && l >= k.expires && x(h); } - }, k.timeout); - d[f] = b; + watch.totalChanges++; + if (watch.options.pauseTimeoutOnChange > 0) { + pauseWatchObject(storageId, watch.options.pauseTimeoutOnChange); + } + if (watch.options.cancelOnChange) { + cancelWatchObject(storageId); + } + if (watch.options.maximumChangesBeforeCanceling > 0 && watch.totalChanges >= watch.options.maximumChangesBeforeCanceling) { + cancelWatchObject(storageId); + } + } } - return f; } - function N(a, b, c) { - for (var f in a) { - if (a.hasOwnProperty(f)) { - var k = a[f], h = null; - b.hasOwnProperty(f) && (h = b[f]); - t(k) && t(h) ? N(k, h, c.options) : (!w(c.options.propertyNames) || -1 < c.options.propertyNames.indexOf(f)) && JSON.stringify(k) !== JSON.stringify(h) && u(c.options.onPropertyChange, f, k, h); + function compareWatchObject(oldObject, newObject, watch) { + if (isDefinedArray(watch.options.propertyNames)) { + var propertyNamesLength = watch.options.propertyNames.length; + var propertyNameIndex = 0; + for (; propertyNameIndex < propertyNamesLength; propertyNameIndex++) { + var propertyName = watch.options.propertyNames[propertyNameIndex]; + if (oldObject[propertyName] !== newObject[propertyName]) { + fireCustomTrigger(watch.options.onChange, oldObject, newObject); + break; + } } + } else { + fireCustomTrigger(watch.options.onChange, oldObject, newObject); } } - function O() { - for (var a in d) { - d.hasOwnProperty(a) && x(a); + function compareWatchObjectProperties(oldObject, newObject, watch) { + var propertyName; + for (propertyName in oldObject) { + if (oldObject.hasOwnProperty(propertyName)) { + var propertyOldValue = oldObject[propertyName]; + var propertyNewValue = null; + if (newObject.hasOwnProperty(propertyName)) { + propertyNewValue = newObject[propertyName]; + } + if (isDefinedObject(propertyOldValue) && isDefinedObject(propertyNewValue)) { + compareWatchObjectProperties(propertyOldValue, propertyNewValue, watch.options); + } else { + if (!isDefinedArray(watch.options.propertyNames) || watch.options.propertyNames.indexOf(propertyName) > -1) { + if (_parameter_Json.stringify(propertyOldValue) !== _parameter_Json.stringify(propertyNewValue)) { + fireCustomTrigger(watch.options.onPropertyChange, propertyName, propertyOldValue, propertyNewValue); + } + } + } + } } } - function x(a) { - if (d.hasOwnProperty(a)) { - var b = d[a].options; - if (b.allowCanceling || P) { - u(b.onCancel, a), clearTimeout(d[a].timer), delete d[a]; + function cancelWatchesForObjects() { + var storageId; + for (storageId in _watches) { + if (_watches.hasOwnProperty(storageId)) { + cancelWatchObject(storageId); } } } - function E(a, b) { - var c = !1; - if (d.hasOwnProperty(a)) { - var f = d[a].options; - f.allowPausing && (f.starts = new Date(), f.starts.setMilliseconds(f.starts.getMilliseconds() + b), c = !0); + function cancelWatchObject(storageId) { + if (_watches.hasOwnProperty(storageId)) { + var watchOptions = _watches[storageId].options; + if (watchOptions.allowCanceling || _watches_Cancel) { + fireCustomTrigger(watchOptions.onCancel, storageId); + clearTimeout(_watches[storageId].timer); + delete _watches[storageId]; + } } - return c; } - function J(a) { - a = t(a) ? a : {}; - a.timeout = H(a.timeout, 250); - var b = a; - var c = a.starts; - c = B(c) ? c : null; - b.starts = c; - b = a; - c = a.expires; - c = B(c) ? c : null; - b.expires = c; - a.reset = z(a.reset, !1); - a.cancelOnChange = z(a.cancelOnChange, !1); - a.maximumChangesBeforeCanceling = H(a.maximumChangesBeforeCanceling, 0); - a.pauseTimeoutOnChange = H(a.pauseTimeoutOnChange, 0); - b = a; - c = a.propertyNames; - c = w(c) ? c : null; - b.propertyNames = c; - a.allowCanceling = z(a.allowCanceling, !0); - a.allowPausing = z(a.allowPausing, !0); - a.onChange = F(a.onChange, null); - a.onPropertyChange = F(a.onPropertyChange, null); - a.onCancel = F(a.onCancel, null); - a.onRemove = F(a.onRemove, null); - return a; + function pauseWatchObject(storageId, milliseconds) { + var result = false; + if (_watches.hasOwnProperty(storageId)) { + var watchOptions = _watches[storageId].options; + if (watchOptions.allowPausing) { + watchOptions.starts = new Date(); + watchOptions.starts.setMilliseconds(watchOptions.starts.getMilliseconds() + milliseconds); + result = true; + } + } + return result; + } + function getWatchOptions(newOptions) { + var options = !isDefinedObject(newOptions) ? {} : newOptions; + options.timeout = getDefaultNumber(options.timeout, 250); + options.starts = getDefaultDate(options.starts, null); + options.expires = getDefaultDate(options.expires, null); + options.reset = getDefaultBoolean(options.reset, false); + options.cancelOnChange = getDefaultBoolean(options.cancelOnChange, false); + options.maximumChangesBeforeCanceling = getDefaultNumber(options.maximumChangesBeforeCanceling, 0); + options.pauseTimeoutOnChange = getDefaultNumber(options.pauseTimeoutOnChange, 0); + options.propertyNames = getDefaultArray(options.propertyNames, null); + options.allowCanceling = getDefaultBoolean(options.allowCanceling, true); + options.allowPausing = getDefaultBoolean(options.allowPausing, true); + options = getWatchOptionsCustomTriggers(options); + return options; + } + function getWatchOptionsCustomTriggers(options) { + options.onChange = getDefaultFunction(options.onChange, null); + options.onPropertyChange = getDefaultFunction(options.onPropertyChange, null); + options.onCancel = getDefaultFunction(options.onCancel, null); + options.onRemove = getDefaultFunction(options.onRemove, null); + options.onStart = getDefaultFunction(options.onStart, null); + return options; } - function u(a) { - D(a) && a.apply(null, [].slice.call(arguments, 1)); + function fireCustomTrigger(triggerFunction) { + if (isDefinedFunction(triggerFunction)) { + triggerFunction.apply(null, [].slice.call(arguments, 1)); + } } - function K() { - for (var a = [], b = 0; 32 > b; b++) { - 8 !== b && 12 !== b && 16 !== b && 20 !== b || a.push("-"); - var c = Math.floor(16 * Math.random()).toString(16); - a.push(c); + function newGuid() { + var result = []; + var charIndex = 0; + for (; charIndex < 32; charIndex++) { + if (charIndex === 8 || charIndex === 12 || charIndex === 16 || charIndex === 20) { + result.push("-"); + } + var character = _parameter_Math.floor(_parameter_Math.random() * 16).toString(16); + result.push(character); } - return a.join(C.empty); + return result.join(_string.empty); + } + function isDefined(value) { + return value !== null && value !== undefined && value !== _string.empty; } - function r(a) { - return null !== a && void 0 !== a && a !== C.empty; + function isDefinedObject(object) { + return isDefined(object) && typeof object === "object"; } - function t(a) { - return r(a) && "object" === typeof a; + function isDefinedBoolean(object) { + return isDefined(object) && typeof object === "boolean"; } - function p(a) { - return r(a) && "string" === typeof a; + function isDefinedString(object) { + return isDefined(object) && typeof object === "string"; } - function D(a) { - return r(a) && "function" === typeof a; + function isDefinedFunction(object) { + return isDefined(object) && typeof object === "function"; } - function w(a) { - return t(a) && a instanceof Array; + function isDefinedNumber(object) { + return isDefined(object) && typeof object === "number"; } - function B(a) { - return t(a) && a instanceof Date; + function isDefinedArray(object) { + return isDefinedObject(object) && object instanceof Array; } - function z(a, b) { - return r(a) && "boolean" === typeof a ? a : b; + function isDefinedDate(object) { + return isDefinedObject(object) && object instanceof Date; } - function F(a, b) { - return D(a) ? a : b; + function getDefaultBoolean(value, defaultValue) { + return isDefinedBoolean(value) ? value : defaultValue; } - function H(a, b) { - return r(a) && "number" === typeof a ? a : b; + function getDefaultFunction(value, defaultValue) { + return isDefinedFunction(value) ? value : defaultValue; } - function A(a) { - var b = !0, c = null; + function getDefaultNumber(value, defaultValue) { + return isDefinedNumber(value) ? value : defaultValue; + } + function getDefaultDate(value, defaultValue) { + return isDefinedDate(value) ? value : defaultValue; + } + function getDefaultArray(value, defaultValue) { + return isDefinedArray(value) ? value : defaultValue; + } + function getDefaultStringOrArray(value, defaultValue) { + if (isDefinedString(value)) { + value = value.split(_string.space); + if (value.length === 0) { + value = defaultValue; + } + } else { + value = getDefaultArray(value, defaultValue); + } + return value; + } + function getObjectFromString(objectString) { + var parsed = true; + var result = null; try { - p(a) && (c = JSON.parse(a)); - } catch (f) { + if (isDefinedString(objectString)) { + result = _parameter_Json.parse(objectString); + } + } catch (e1) { try { - c = eval("(" + a + ")"), D(c) && (c = c()); - } catch (k) { - b = R("Errors in object: " + f.message + ", " + k.message), c = null; + result = eval("(" + objectString + ")"); + if (isDefinedFunction(result)) { + result = result(); + } + } catch (e2) { + parsed = logError("Errors in object: " + e1.message + ", " + e2.message); + result = null; } } - return {parsed:b, result:c}; + return {parsed:parsed, result:result}; } - function R(a) { - var b = !0; - q.safeMode || (console.error(a), b = !1); - return b; + function logError(error) { + var result = true; + if (!_configuration.safeMode) { + console.error(error); + result = false; + } + return result; } - function Q() { - q.safeMode = z(q.safeMode, !0); - var a = q, b = q.domElementTypes, c = ["*"]; - p(b) ? (b = b.split(C.space), 0 === b.length && (b = c)) : b = w(b) ? b : c; - a.domElementTypes = b; + function buildDefaultConfiguration() { + _configuration.safeMode = getDefaultBoolean(_configuration.safeMode, true); + _configuration.domElementTypes = getDefaultStringOrArray(_configuration.domElementTypes, ["*"]); } - var y = null, G = null, C = {empty:""}, d = {}, P = !1, q = {}; - this.watch = function(a, b) { - return L(a, b); + var _parameter_Document = null; + var _parameter_Window = null; + var _parameter_Math = null; + var _parameter_Json = null; + var _string = {empty:""}; + var _watches = {}; + var _watches_Cancel = false; + var _configuration = {}; + var _attribute_Name_Watch_Options = "data-observe-watch-options"; + this.watch = function(object, options) { + return createWatch(object, options); }; - this.cancelWatch = function(a) { - var b = !1; - if (d.hasOwnProperty(a)) { - x(a), b = !0; + this.cancelWatch = function(id) { + var result = false; + if (_watches.hasOwnProperty(id)) { + cancelWatchObject(id); + result = true; } else { - for (var c in d) { - if (d.hasOwnProperty(c) && p(d[c].domElementId) && d[c].domElementId === a) { - x(c); - b = !0; + var storageId; + for (storageId in _watches) { + if (_watches.hasOwnProperty(storageId) && isDefinedString(_watches[storageId].domElementId) && _watches[storageId].domElementId === id) { + cancelWatchObject(storageId); + result = true; break; } } } - return b; + return result; }; this.cancelWatches = function() { - O(); + cancelWatchesForObjects(); return this; }; - this.getWatch = function(a) { - var b = null; - if (d.hasOwnProperty(a)) { - b = d[a]; + this.getWatch = function(id) { + var result = null; + if (_watches.hasOwnProperty(id)) { + result = _watches[id]; } else { - for (var c in d) { - if (d.hasOwnProperty(c) && p(d[c].domElementId) && d[c].domElementId === a) { - b = d[c]; + var storageId; + for (storageId in _watches) { + if (_watches.hasOwnProperty(storageId) && isDefinedString(_watches[storageId].domElementId) && _watches[storageId].domElementId === id) { + result = _watches[storageId]; break; } } } - return b; + return result; }; this.getWatches = function() { - return d; + return _watches; }; - this.pauseWatch = function(a, b) { - var c = !1; - if (d.hasOwnProperty(a)) { - c = E(a, b); + this.pauseWatch = function(id, milliseconds) { + var result = false; + if (_watches.hasOwnProperty(id)) { + result = pauseWatchObject(id, milliseconds); } else { - for (var f in d) { - if (d.hasOwnProperty(f) && p(d[f].domElementId) && d[f].domElementId === a) { - c = E(f, b); + var storageId; + for (storageId in _watches) { + if (_watches.hasOwnProperty(storageId) && isDefinedString(_watches[storageId].domElementId) && _watches[storageId].domElementId === id) { + result = pauseWatchObject(storageId, milliseconds); break; } } } - return c; + return result; }; - this.pauseWatches = function(a) { - for (var b in d) { - d.hasOwnProperty(b) && E(b, a); + this.pauseWatches = function(milliseconds) { + var storageId; + for (storageId in _watches) { + if (_watches.hasOwnProperty(storageId)) { + pauseWatchObject(storageId, milliseconds); + } } return this; }; - this.resumeWatch = function(a) { - var b = !1; - if (d.hasOwnProperty(a)) { - d[a].options.starts = null, b = !0; + this.resumeWatch = function(id) { + var result = false; + if (_watches.hasOwnProperty(id)) { + _watches[id].options.starts = null; + result = true; } else { - for (var c in d) { - if (d.hasOwnProperty(c) && p(d[c].domElementId) && d[c].domElementId === a) { - d[c].options.starts = null; - b = !0; + var storageId; + for (storageId in _watches) { + if (_watches.hasOwnProperty(storageId) && isDefinedString(_watches[storageId].domElementId) && _watches[storageId].domElementId === id) { + _watches[storageId].options.starts = null; + result = true; break; } } } - return b; + return result; }; this.resumeWatches = function() { - for (var a in d) { - d.hasOwnProperty(a) && (d[a].options.starts = null); + var storageId; + for (storageId in _watches) { + if (_watches.hasOwnProperty(storageId)) { + _watches[storageId].options.starts = null; + } } return this; }; this.searchDomForNewWatches = function() { - I(); + collectDOMObjects(); return this; }; - this.setConfiguration = function(a) { - q = t(a) ? a : {}; - Q(); + this.setConfiguration = function(newOptions) { + _configuration = !isDefinedObject(newOptions) ? {} : newOptions; + buildDefaultConfiguration(); return this; }; this.getVersion = function() { - return "0.6.1"; + return "0.7.0"; }; - (function(a, b) { - y = a; - G = b; - Q(); - y.addEventListener("DOMContentLoaded", function() { - I(); + (function(documentObject, windowObject, mathObject, jsonObject) { + _parameter_Document = documentObject; + _parameter_Window = windowObject; + _parameter_Math = mathObject; + _parameter_Json = jsonObject; + buildDefaultConfiguration(); + _parameter_Document.addEventListener("DOMContentLoaded", function() { + collectDOMObjects(); }); - G.addEventListener("unload", function() { - P = !0; - O(); + _parameter_Window.addEventListener("unload", function() { + _watches_Cancel = true; + cancelWatchesForObjects(); }); - r(G.$observe) || (G.$observe = this); - })(document, window); + if (!isDefined(_parameter_Window.$observe)) { + _parameter_Window.$observe = this; + } + })(document, window, Math, JSON); })(); \ No newline at end of file diff --git a/dist/observe.min.js b/dist/observe.min.js index 09ade40..94c883c 100644 --- a/dist/observe.min.js +++ b/dist/observe.min.js @@ -1,13 +1,13 @@ -/*! Observe.js v0.6.1 | (c) Bunoon 2024 | MIT License */ -(function(){function I(){for(var a=q.domElementTypes,b=a.length,c=0;c=k.starts){if(d.hasOwnProperty(h)){var e=d[h],g=p(e.domElementId), -m=null;g&&(m=y.getElementById(e.domElementId),r(m)?e.originalObject=m.outerHTML:(e.originalObject=C.empty,u(e.options.onRemove,e.domElementId)));var v=e.cachedObject,n=e.originalObject;n=g?n:JSON.stringify(n);if(v!==n){e.options.reset?g?m.outerHTML=e.cachedObject:e.originalObject=A(v).result:e.cachedObject=n;if(g)u(e.options.onChange,v,n);else if(g=A(v).result,m=A(n).result,w(g)||w(m))u(e.options.onChange,g,m);else{if(w(e.options.propertyNames))for(v=e.options.propertyNames.length,n=0;n=e.options.maximumChangesBeforeCanceling&&x(h)}}B(k.expires)&&l>=k.expires&&x(h)}},k.timeout);d[f]=b}return f}function N(a,b,c){for(var f in a)if(a.hasOwnProperty(f)){var k=a[f], -h=null;b.hasOwnProperty(f)&&(h=b[f]);t(k)&&t(h)?N(k,h,c.options):(!w(c.options.propertyNames)||-1b;b++){8!==b&&12!==b&&16!==b&&20!==b||a.push("-");var c=Math.floor(16*Math.random()).toString(16);a.push(c)}return a.join(C.empty)}function r(a){return null!==a&&void 0!==a&&a!==C.empty}function t(a){return r(a)&&"object"===typeof a}function p(a){return r(a)&& -"string"===typeof a}function D(a){return r(a)&&"function"===typeof a}function w(a){return t(a)&&a instanceof Array}function B(a){return t(a)&&a instanceof Date}function z(a,b){return r(a)&&"boolean"===typeof a?a:b}function F(a,b){return D(a)?a:b}function H(a,b){return r(a)&&"number"===typeof a?a:b}function A(a){var b=!0,c=null;try{p(a)&&(c=JSON.parse(a))}catch(f){try{c=eval("("+a+")"),D(c)&&(c=c())}catch(k){b=R("Errors in object: "+f.message+", "+k.message),c=null}}return{parsed:b,result:c}}function R(a){var b= -!0;q.safeMode||(console.error(a),b=!1);return b}function Q(){q.safeMode=z(q.safeMode,!0);var a=q,b=q.domElementTypes,c=["*"];p(b)?(b=b.split(C.space),0===b.length&&(b=c)):b=w(b)?b:c;a.domElementTypes=b}var y=null,G=null,C={empty:""},d={},P=!1,q={};this.watch=function(a,b){return L(a,b)};this.cancelWatch=function(a){var b=!1;if(d.hasOwnProperty(a))x(a),b=!0;else for(var c in d)if(d.hasOwnProperty(c)&&p(d[c].domElementId)&&d[c].domElementId===a){x(c);b=!0;break}return b};this.cancelWatches=function(){O(); -return this};this.getWatch=function(a){var b=null;if(d.hasOwnProperty(a))b=d[a];else for(var c in d)if(d.hasOwnProperty(c)&&p(d[c].domElementId)&&d[c].domElementId===a){b=d[c];break}return b};this.getWatches=function(){return d};this.pauseWatch=function(a,b){var c=!1;if(d.hasOwnProperty(a))c=E(a,b);else for(var f in d)if(d.hasOwnProperty(f)&&p(d[f].domElementId)&&d[f].domElementId===a){c=E(f,b);break}return c};this.pauseWatches=function(a){for(var b in d)d.hasOwnProperty(b)&&E(b,a);return this};this.resumeWatch= -function(a){var b=!1;if(d.hasOwnProperty(a))d[a].options.starts=null,b=!0;else for(var c in d)if(d.hasOwnProperty(c)&&p(d[c].domElementId)&&d[c].domElementId===a){d[c].options.starts=null;b=!0;break}return b};this.resumeWatches=function(){for(var a in d)d.hasOwnProperty(a)&&(d[a].options.starts=null);return this};this.searchDomForNewWatches=function(){I();return this};this.setConfiguration=function(a){q=t(a)?a:{};Q();return this};this.getVersion=function(){return"0.6.1"};(function(a,b){y=a;G=b;Q(); -y.addEventListener("DOMContentLoaded",function(){I()});G.addEventListener("unload",function(){P=!0;O()});r(G.$observe)||(G.$observe=this)})(document,window)})(); \ No newline at end of file +/*! Observe.js v0.7.0 | (c) Bunoon 2024 | MIT License */ +(function(){function L(){for(var a=r.domElementTypes,b=a.length,c=0;c=g.starts){if(d.hasOwnProperty(h)){var e=d[h],l=q(e.domElementId),m=null;l&&(m=A.getElementById(e.domElementId),p(m)?e.originalObject=m.outerHTML:(e.originalObject=F.empty,u(e.options.onRemove,e.domElementId)));var x=e.cachedObject,n=e.originalObject;n=l?n:w.stringify(n);if(x!==n){e.options.reset?l?m.outerHTML=e.cachedObject:e.originalObject=D(x).result:e.cachedObject=n;if(l)u(e.options.onChange,x,n);else if(l=D(x).result,m=D(n).result,y(l)||y(m))u(e.options.onChange,l,m);else{if(y(e.options.propertyNames))for(x= +e.options.propertyNames.length,n=0;n=e.options.maximumChangesBeforeCanceling&&z(h)}}E(g.expires)&&v>=g.expires&&z(h)}},g.timeout),d[f]=b)}return f}function Q(a,b,c){for(var f in a)if(a.hasOwnProperty(f)){var g= +a[f],k=null;b.hasOwnProperty(f)&&(k=b[f]);t(g)&&t(k)?Q(g,k,c.options):(!y(c.options.propertyNames)||-1b;b++){8!==b&&12!==b&&16!==b&&20!==b||a.push("-");var c=K.floor(16*K.random()).toString(16);a.push(c)}return a.join(F.empty)}function p(a){return null!==a&&void 0!==a&&a!==F.empty}function t(a){return p(a)&&"object"=== +typeof a}function q(a){return p(a)&&"string"===typeof a}function G(a){return p(a)&&"function"===typeof a}function y(a){return t(a)&&a instanceof Array}function E(a){return t(a)&&a instanceof Date}function B(a,b){return p(a)&&"boolean"===typeof a?a:b}function C(a,b){return G(a)?a:b}function J(a,b){return p(a)&&"number"===typeof a?a:b}function D(a){var b=!0,c=null;try{q(a)&&(c=w.parse(a))}catch(f){try{c=eval("("+a+")"),G(c)&&(c=c())}catch(g){b=U("Errors in object: "+f.message+", "+g.message),c=null}}return{parsed:b, +result:c}}function U(a){var b=!0;r.safeMode||(console.error(a),b=!1);return b}function T(){r.safeMode=B(r.safeMode,!0);var a=r,b=r.domElementTypes,c=["*"];q(b)?(b=b.split(F.space),0===b.length&&(b=c)):b=y(b)?b:c;a.domElementTypes=b}var A=null,I=null,K=null,w=null,F={empty:""},d={},S=!1,r={};this.watch=function(a,b){return O(a,b)};this.cancelWatch=function(a){var b=!1;if(d.hasOwnProperty(a))z(a),b=!0;else for(var c in d)if(d.hasOwnProperty(c)&&q(d[c].domElementId)&&d[c].domElementId===a){z(c);b=!0; +break}return b};this.cancelWatches=function(){R();return this};this.getWatch=function(a){var b=null;if(d.hasOwnProperty(a))b=d[a];else for(var c in d)if(d.hasOwnProperty(c)&&q(d[c].domElementId)&&d[c].domElementId===a){b=d[c];break}return b};this.getWatches=function(){return d};this.pauseWatch=function(a,b){var c=!1;if(d.hasOwnProperty(a))c=H(a,b);else for(var f in d)if(d.hasOwnProperty(f)&&q(d[f].domElementId)&&d[f].domElementId===a){c=H(f,b);break}return c};this.pauseWatches=function(a){for(var b in d)d.hasOwnProperty(b)&& +H(b,a);return this};this.resumeWatch=function(a){var b=!1;if(d.hasOwnProperty(a))d[a].options.starts=null,b=!0;else for(var c in d)if(d.hasOwnProperty(c)&&q(d[c].domElementId)&&d[c].domElementId===a){d[c].options.starts=null;b=!0;break}return b};this.resumeWatches=function(){for(var a in d)d.hasOwnProperty(a)&&(d[a].options.starts=null);return this};this.searchDomForNewWatches=function(){L();return this};this.setConfiguration=function(a){r=t(a)?a:{};T();return this};this.getVersion=function(){return"0.7.0"}; +(function(a,b,c,f){A=a;I=b;K=c;w=f;T();A.addEventListener("DOMContentLoaded",function(){L()});I.addEventListener("unload",function(){S=!0;R()});p(I.$observe)||(I.$observe=this)})(document,window,Math,JSON)})(); \ No newline at end of file diff --git a/docs/CHANGE_LOG.md b/docs/CHANGE_LOG.md index 93291c0..f1f1264 100644 --- a/docs/CHANGE_LOG.md +++ b/docs/CHANGE_LOG.md @@ -1,5 +1,28 @@ # Observe.js - Change Log: +## Version 0.7.0: + +#### **Rules:** +- Watches will now only start if an object can be found (is not null, or the DOM element exists). + +#### **Binding Options / Function Options - Custom Triggers:** +- Added a new binding/option custom trigger called "onStart", which states an event that should be triggered when a watch is started. + +#### **General Improvements:** +- Added Math injection directly into the main instance. +- Added JSON injection directly into the main instance. +- Improved keywords in the package files. + +#### **Documentation:** +- Added install instructions into the main README files. +- Added documentation that states how issues and new feature requests should be raised. + +#### **Fixes:** +- Fixed the "observe.js.nuspec" file including the ".github" folder when NuGet PACK is called. + +
+ + ## Version 0.6.1: - Updated project homepage URL. - Fixed the binding property "allowPausing" defaulting to "null" instead of "true" when not manually set. diff --git a/docs/binding/options/CUSTOM_TRIGGERS.md b/docs/binding/options/CUSTOM_TRIGGERS.md index 0448a45..15c32fb 100644 --- a/docs/binding/options/CUSTOM_TRIGGERS.md +++ b/docs/binding/options/CUSTOM_TRIGGERS.md @@ -33,6 +33,11 @@ Fires when a DOM element is no longer available in the DOM.
***Parameter:*** id: '*string*' - The ID of the DOM element. +### options.onStart( *originalValue* ): +Fires when a watch is started. +
+***Parameter:*** id: '*object*' - The object that the watch as started for. +
diff --git a/observe.js.nuspec b/observe.js.nuspec index c4f09a1..667c6e5 100644 --- a/observe.js.nuspec +++ b/observe.js.nuspec @@ -2,7 +2,7 @@ jObserve.js - 0.6.1 + 0.7.0 Observe.js A lightweight JavaScript library that allows developers to keep track of changes to JavaScript objects and/or DOM elements. William Troup @@ -10,11 +10,11 @@ README_NUGET.md MIT - javascript css observe.js observe observable watcher object changes watch + javascript css observe.js observe observable watcher object changes watch event events trigger triggers handler docs\images\icon.png William Troup - + \ No newline at end of file diff --git a/package.json b/package.json index 0357aa0..7298032 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "jobserve.js", "title": "Observe.js", "description": "A lightweight JavaScript library that allows developers to keep track of changes to JavaScript objects and/or DOM elements.", - "version": "0.6.1", + "version": "0.7.0", "main": "dist/observe.js", "homepage": "https://www.william-troup.com/observe-js/", "author": { @@ -21,7 +21,12 @@ "watcher", "object", "changes", - "watch" + "watch", + "event", + "events", + "trigger", + "triggers", + "handler" ], "bugs": { "url": "https://github.com/williamtroup/Observe.js/issues" diff --git a/src/observe.js b/src/observe.js index 9648199..34b24ca 100644 --- a/src/observe.js +++ b/src/observe.js @@ -4,7 +4,7 @@ * A lightweight JavaScript library that allows developers to keep track of changes to JavaScript objects and/or DOM elements. * * @file observe.js - * @version v0.6.1 + * @version v0.7.0 * @author Bunoon * @license MIT License * @copyright Bunoon 2024 @@ -15,6 +15,8 @@ var // Variables: Constructor Parameters _parameter_Document = null, _parameter_Window = null, + _parameter_Math = null, + _parameter_Json = null, // Variables: Strings _string = { @@ -107,7 +109,8 @@ storageId = newGuid(); var watchOptions = getWatchOptions( options ), - watch = {}; + watch = {}, + startWatchObject; watch.options = watchOptions; watch.totalChanges = 0; @@ -119,18 +122,26 @@ watch.domElementId = domElementId; watch.cachedObject = domElement.outerHTML; watch.originalObject = domElement.outerHTML; + + startWatchObject = domElement.outerHTML; } } else { - watch.cachedObject = JSON.stringify( object ); + watch.cachedObject = _parameter_Json.stringify( object ); watch.originalObject = object; + + startWatchObject = object; } - watch.timer = setInterval( function() { - watchTimer( watchOptions, storageId ); - }, watchOptions.timeout ); + if ( isDefined( watch.cachedObject ) ) { + fireCustomTrigger( watch.options.onStart, startWatchObject ); - _watches[ storageId ] = watch; + watch.timer = setInterval( function() { + watchTimer( watchOptions, storageId ); + }, watchOptions.timeout ); + + _watches[ storageId ] = watch; + } } return storageId; @@ -168,7 +179,7 @@ var cachedObject = watch.cachedObject, originalObject = watch.originalObject, - originalObjectJson = !isDomElement ? JSON.stringify( originalObject ) : originalObject; + originalObjectJson = !isDomElement ? _parameter_Json.stringify( originalObject ) : originalObject; if ( cachedObject !== originalObjectJson ) { if ( watch.options.reset ) { @@ -251,7 +262,7 @@ } else { if ( !isDefinedArray( watch.options.propertyNames ) || watch.options.propertyNames.indexOf( propertyName ) > -1 ) { - if ( JSON.stringify( propertyOldValue ) !== JSON.stringify( propertyNewValue ) ) { + if ( _parameter_Json.stringify( propertyOldValue ) !== _parameter_Json.stringify( propertyNewValue ) ) { fireCustomTrigger( watch.options.onPropertyChange, propertyName, propertyOldValue, propertyNewValue ); } } @@ -329,6 +340,7 @@ options.onPropertyChange = getDefaultFunction( options.onPropertyChange, null ); options.onCancel = getDefaultFunction( options.onCancel, null ); options.onRemove = getDefaultFunction( options.onRemove, null ); + options.onStart = getDefaultFunction( options.onStart, null ); return options; } @@ -361,7 +373,7 @@ result.push( "-" ); } - var character = Math.floor( Math.random() * 16 ).toString( 16 ); + var character = _parameter_Math.floor( _parameter_Math.random() * 16 ).toString( 16 ); result.push( character ); } @@ -455,7 +467,7 @@ try { if ( isDefinedString( objectString ) ) { - result = JSON.parse( objectString ); + result = _parameter_Json.parse( objectString ); } } catch ( e1 ) { @@ -767,7 +779,7 @@ * @returns {string} The version number. */ this.getVersion = function() { - return "0.6.1"; + return "0.7.0"; }; @@ -777,9 +789,11 @@ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */ - ( function ( documentObject, windowObject ) { + ( function ( documentObject, windowObject, mathObject, jsonObject ) { _parameter_Document = documentObject; _parameter_Window = windowObject; + _parameter_Math = mathObject; + _parameter_Json = jsonObject; buildDefaultConfiguration(); @@ -797,5 +811,5 @@ _parameter_Window.$observe = this; } - } ) ( document, window ); + } ) ( document, window, Math, JSON ); } )(); \ No newline at end of file diff --git a/test/dist/observe.js.basic.html b/test/dist/observe.js.basic.html index 7ae5658..7be6ae2 100644 --- a/test/dist/observe.js.basic.html +++ b/test/dist/observe.js.basic.html @@ -15,7 +15,7 @@

Observe.js - Basic

-
+
When changed, this element should fire a custom trigger.
@@ -103,13 +103,15 @@

Additional Data:

onChange: onObservableObjectChange, onPropertyChange: onObservableObjectPropertyChange, onCancel: onCancelWatch, - propertyNames: [ "bool", "num" ] + propertyNames: [ "bool", "num" ], + onStart: onStartWatch } ); $observe.watch( window.observeArray, { onChange: onObservableObjectChange, onPropertyChange: onObservableObjectPropertyChange, onCancel: onCancelWatch, + onStart: onStartWatch } ); } @@ -136,5 +138,10 @@

Additional Data:

console.log( "Watch " + id + " canceled." ); console.log( "" ); } + + function onStartWatch( object ) { + console.log( "Watch started for: " + JSON.stringify( object ) ); + console.log( "" ); + } \ No newline at end of file diff --git a/test/dist/observe.js.min.html b/test/dist/observe.js.min.html index be82e1e..9bdab75 100644 --- a/test/dist/observe.js.min.html +++ b/test/dist/observe.js.min.html @@ -15,7 +15,7 @@

Observe.js - Basic

-
+
When changed, this element should fire a custom trigger.
@@ -99,7 +99,8 @@

Additional Data:

onChange: onObservableObjectChange, onPropertyChange: onObservableObjectPropertyChange, onCancel: onCancelWatch, - propertyNames: [ "bool", "num" ] + propertyNames: [ "bool", "num" ], + onStart: onStartWatch } ); } @@ -126,5 +127,10 @@

Additional Data:

console.log( "Watch " + id + " canceled." ); console.log( "" ); } + + function onStartWatch( object ) { + console.log( "Watch started for: " + JSON.stringify( object ) ); + console.log( "" ); + } \ No newline at end of file diff --git a/test/src/observe.js.basic.html b/test/src/observe.js.basic.html index 71bdee8..0335d7a 100644 --- a/test/src/observe.js.basic.html +++ b/test/src/observe.js.basic.html @@ -15,7 +15,7 @@

Observe.js - Basic

-
+
When changed, this element should fire a custom trigger.
@@ -103,13 +103,15 @@

Additional Data:

onChange: onObservableObjectChange, onPropertyChange: onObservableObjectPropertyChange, onCancel: onCancelWatch, - propertyNames: [ "bool", "num" ] + propertyNames: [ "bool", "num" ], + onStart: onStartWatch } ); $observe.watch( window.observeArray, { onChange: onObservableObjectChange, onPropertyChange: onObservableObjectPropertyChange, onCancel: onCancelWatch, + onStart: onStartWatch } ); } @@ -136,5 +138,10 @@

Additional Data:

console.log( "Watch " + id + " canceled." ); console.log( "" ); } + + function onStartWatch( object ) { + console.log( "Watch started for: " + JSON.stringify( object ) ); + console.log( "" ); + } \ No newline at end of file