Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-habitat

Package Overview
Dependencies
Maintainers
3
Versions
30
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-habitat - npm Package Compare versions

Comparing version 0.4.2 to 0.5.0-beta

README.md

9

CHANGELOG.md
## React Habitat Change log
### [0.5.0]
- Updated packages in examples
- Updated readme with JSON encoding information [#11](https://github.com/DeloitteDigitalAPAC/react-habitat/issues/11). Thanks @joshuakelly
- Deprecated elements property from Bootstrapper
- Added dynamic html wire up support [#12](https://github.com/DeloitteDigitalAPAC/react-habitat/issues/12)
- Small optimisation wins for production builds
- Removed all deprecated methods
### [0.4.2]

@@ -4,0 +13,0 @@

547

dist/react-habitat.js

@@ -55,3 +55,3 @@ (function webpackUniversalModuleDefinition(root, factory) {

/* 0 */
/***/ function(module, exports, __webpack_require__) {
/***/ (function(module, exports, __webpack_require__) {

@@ -83,5 +83,5 @@ 'use strict';

/***/ },
/***/ }),
/* 1 */
/***/ function(module, exports, __webpack_require__) {
/***/ (function(module, exports, __webpack_require__) {

@@ -91,3 +91,3 @@ 'use strict';

Object.defineProperty(exports, "__esModule", {
value: true
value: true
});

@@ -115,26 +115,30 @@

/**
* Safely log to the console
*/
log = function log(type, args) {
/**
* Safely log to the console
*/
log = function log(type, args) {
if (typeof console !== 'undefined' && console[type]) {
if (console[type].apply) {
console[type].apply(undefined, args);
} else {
// IE9 Fallback
console[type](args);
}
}
};
if (typeof console !== 'undefined' && console[type]) {
console[type].apply(undefined, args);
}
};
/**
* Concats the message and arguments into a single array
*/
concatArgs = function concatArgs(msg, args) {
var throwArgs = [msg];
/**
* Concats the message and arguments into a single array
*/
concatArgs = function concatArgs(msg, args) {
var throwArgs = [msg];
if (args && args.length) {
for (var i = 0; i < args.length; i++) {
throwArgs.push(args[i]);
}
}
if (args && args.length > 2) {
for (var i = 2; i < args.length; i++) {
throwArgs.push(args[i]);
}
}
return throwArgs;
};
return throwArgs;
};
}

@@ -147,35 +151,45 @@

var Logger = function () {
function Logger() {
_classCallCheck(this, Logger);
}
function Logger() {
_classCallCheck(this, Logger);
}
_createClass(Logger, null, [{
key: 'warn',
_createClass(Logger, null, [{
key: 'warn',
/**
* Log a warning
* @param {string} code - The warning code
* @param {string} msg - The warning message
*/
value: function warn(code, msg) {
var args = concatArgs('WARNING: ' + code + ' ' + msg + ' ' + WARN_DEFINITIONS_URL + '#' + code.toLowerCase(), arguments);
log('warn', args);
}
/**
* Log a warning
* @param {string} code - The warning code
* @param {string} msg - The warning message
* @param {Array} debugs - Any debugging arguments
*/
value: function warn(code, msg) {
for (var _len = arguments.length, debugs = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
debugs[_key - 2] = arguments[_key];
}
/**
* Log an error
* @param {string} code - The warning code
* @param {string} msg - The error message
*/
var args = concatArgs('WARNING: ' + code + ' ' + msg + ' ' + WARN_DEFINITIONS_URL + '#' + code.toLowerCase(), debugs);
log('warn', args);
}
}, {
key: 'error',
value: function error(code, msg) {
var args = concatArgs('ERROR: ' + code + ' ' + msg + ' ' + WARN_DEFINITIONS_URL + '#' + code.toLowerCase(), arguments);
log('error', args);
}
}]);
/**
* Log an error
* @param {string} code - The warning code
* @param {string} msg - The error message
* @param {Array} debugs - Any debugging arguments
*/
return Logger;
}, {
key: 'error',
value: function error(code, msg) {
for (var _len2 = arguments.length, debugs = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
debugs[_key2 - 2] = arguments[_key2];
}
var args = concatArgs('ERROR: ' + code + ' ' + msg + ' ' + WARN_DEFINITIONS_URL + '#' + code.toLowerCase(), debugs);
log('error', args);
}
}]);
return Logger;
}();

@@ -186,5 +200,5 @@

/***/ },
/***/ }),
/* 2 */
/***/ function(module, exports, __webpack_require__) {
/***/ (function(module, exports, __webpack_require__) {

@@ -220,9 +234,25 @@ 'use strict';

/**
* Parses a container and populate components
* Safe callback wrapper
* @param {null|function} cb - The callback
* @private
*/
function _callback(cb, context) {
if (typeof cb === 'function') {
for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
args[_key - 2] = arguments[_key];
}
cb.call.apply(cb, [context].concat(args));
}
}
/**
* Apply a container to nodes and populate components
* @param {array} container The container
* @param {array} elements The elements to parse
* @param {array} nodes The elements to parse
* @param {string} componentSelector The component selector
* @param cb
* @param {function} [cb=null] - Optional callback
* @private
*/
function parseContainer(container, elements, componentSelector) {
function _applyContainer(container, nodes, componentSelector) {
var cb = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;

@@ -233,22 +263,48 @@

var id = container.id();
var resolveQueue = [];
// Iterate over component elements in the dom
for (var i = 0; i < elements.length; ++i) {
var ele = elements[i];
var _loop = function _loop(i) {
var ele = nodes[i];
// Ignore elements that have already been connected
if (_Habitat2.default.hasHabitat(ele)) {
return 'continue';
}
var componentName = ele.getAttribute(componentSelector);
var component = container.resolve(componentName);
resolveQueue.push(container.resolve(componentName).then(function (component) {
// This is an expensive operation so only do on non prod builds
if (true) {
if (ele.querySelector('[' + componentSelector + ']')) {
_Logger2.default.warn('RHW08', 'Component should not contain any nested components.', ele);
}
}
if (component) {
if (ele.querySelector('[' + componentSelector + ']')) {
_Logger2.default.warn('RHW08', 'Component should not contain any nested components.', ele);
}
// Inject the component
factory.inject(component, _Habitat2.default.parseProps(ele), _Habitat2.default.create(ele, id));
} else {
_Logger2.default.error('RHW01', 'Cannot resolve component "' + componentName + '" for element.', ele);
}
}).catch(function (err) {
_Logger2.default.error('RHW01', 'Cannot resolve component "' + componentName + '" for element.', err, ele);
}));
};
for (var i = 0; i < nodes.length; ++i) {
var _ret = _loop(i);
if (_ret === 'continue') continue;
}
if (typeof cb === 'function') {
cb.call();
}
// Trigger callback when all promises are finished
// regardless if some fail
Promise.all(resolveQueue.map(function (p) {
return p.catch(function (e) {
return e;
});
})).then(function () {
_callback(cb);
}).catch(function (err) {
// We should never get here.. if we do this is a bug
throw err;
});
}

@@ -273,10 +329,28 @@

// Set dom component selector
/**
* The DOM component selector
* @type {string}
*/
this.componentSelector = DEFAULT_HABITAT_SELECTOR;
// The target elements
this._elements = null;
/**
* The container
* @type {Container|null}
* @private
*/
this._container = null;
// The container
this._container = null;
/**
* The watcher's observer instance or null
* @type {MutationObserver|null}
* @private
*/
this._observer = null;
/**
* Observing persistence status flag
* @type {boolean}
* @private
*/
this._isWatching = false;
}

@@ -294,2 +368,4 @@

value: function setContainer(container) {
var _this = this;
var cb = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;

@@ -306,10 +382,135 @@

// Find all the elements in the dom with the component selector attribute
this._elements = window.document.body.querySelectorAll('[' + this.componentSelector + ']');
// Wire up the components from the container
parseContainer(this._container, this._elements, this.componentSelector, cb);
this.update(null, function () {
_callback(cb, _this);
});
}
/**
* Apply the container to an updated dom structure
* @param {node} node - Target node to parse or null for entire document body
* @param {function} [cb=null] - Optional callback
*/
}, {
key: 'update',
value: function update(node) {
var _this2 = this;
var cb = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
// Check if we have a container before attempting an update
if (!this._container) {
_callback(cb);
return;
}
var target = node || window.document.body.querySelectorAll('[' + this.componentSelector + ']');
// Lifecycle event
// Hook to allow developers to cancel operation
if (typeof this.shouldUpdate === 'function') {
if (this.shouldUpdate(target) === false) {
_callback(cb, this);
return;
}
}
// Temporarily stop the watcher from triggering from our own Habitat injections
// This is better for performance however, this could possibly miss any
// mutations during the parsing time.. possible bug maybe? dont know yet.
var shouldWatcherPersist = this._isWatching;
if (shouldWatcherPersist) {
this.stopWatcher();
}
// Lifecycle event
if (typeof this.willUpdate === 'function') {
this.willUpdate(target);
}
_applyContainer(this._container, target, this.componentSelector, function () {
// Restart the dom watcher if persisting
if (shouldWatcherPersist) {
_this2.startWatcher();
}
// Lifecycle event
if (typeof _this2.didUpdate === 'function') {
_this2.didUpdate(target);
}
_callback(cb, _this2);
});
}
/**
* Start DOM watcher for auto wire ups
*/
}, {
key: 'startWatcher',
value: function startWatcher() {
// Feature available?
if (typeof MutationObserver === 'undefined') {
_Logger2.default.error('RHE09', 'MutationObserver not available.');
return;
}
// Create observer if not assigned already
if (!this._observer) {
this._observer = new MutationObserver(this._handleDomMutation.bind(this));
}
// Start observing for dom changes filtered by our component selector
this._observer.observe(window.document.body, {
childList: true,
attributes: true,
subtree: true,
attributeOldValue: false,
characterData: false,
characterDataOldValue: false,
attributeFilter: [this.componentSelector]
});
// Set flag for persistence during update's
this._isWatching = true;
}
/**
* Stop the DOM watcher if running
*/
}, {
key: 'stopWatcher',
value: function stopWatcher() {
if (this._observer && this._observer.disconnect) {
this._observer.disconnect();
}
this._isWatching = false;
}
/**
* Handle dom mutation event
* @param {MutationRecord} mutationRecord - The mutation record
*/
}, {
key: '_handleDomMutation',
value: function _handleDomMutation(mutationRecord) {
if (typeof mutationRecord !== 'undefined') {
// Only run update if nodes have been added
var diff = mutationRecord.filter(function (r) {
return r.addedNodes.length;
});
if (diff.length) {
this.update();
}
} else {
// Fallback
this.update();
}
}
/**
* Dispose the container and destroy habitat instances

@@ -325,2 +526,5 @@ * @param {function} [cb=null] - Optional callback

// Stop dom watcher if any
this.stopWatcher();
// get the container's factory

@@ -340,8 +544,7 @@ var factory = this._container.domFactory();

this._container = null;
this._elements = null;
this._observer = null;
this._isWatching = false;
// Handle callback
if (typeof cb === 'function') {
cb.call();
}
_callback(cb, this);
}

@@ -356,5 +559,5 @@ }]);

/***/ },
/***/ }),
/* 3 */
/***/ function(module, exports, __webpack_require__) {
/***/ (function(module, exports, __webpack_require__) {

@@ -367,11 +570,11 @@ 'use strict';

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /**
* Copyright 2016-present, Deloitte Digital.
* All rights reserved.
*
* This source code is licensed under the BSD-3-Clause license found in the
* LICENSE file in the root directory of this source tree.
*/
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /**
* Copyright 2016-present, Deloitte Digital.
* All rights reserved.
*
* This source code is licensed under the BSD-3-Clause license found in the
* LICENSE file in the root directory of this source tree.
*/

@@ -382,6 +585,2 @@ var _ReactDomFactory = __webpack_require__(6);

var _Logger = __webpack_require__(1);
var _Logger2 = _interopRequireDefault(_Logger);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -396,8 +595,7 @@

*/
var assignId = function idFactory() {
var _nextId = 0;
var _assignId = function idFactory() {
var nextId = 0;
return function _assignId() {
_nextId++;
return 'C' + _nextId;
nextId = nextId + 1;
return 'C' + nextId;
};

@@ -407,2 +605,12 @@ }();

/**
* Determine if the object is a Promise
* @param {*} obj - The test object
* @returns {boolean} - True if deemed to be a promise
* @private
*/
var _isPromise = function _isPromise(obj) {
return !!obj && ((typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
};
/**
* The Container class

@@ -420,3 +628,3 @@ */

this._components = {};
this._id = assignId();
this._id = _assignId();
}

@@ -438,4 +646,4 @@

* Register a component in the container
* @param {string} name - A unique component key
* @param {object} comp - The component
* @param {string} name - A unique component key
* @param {object|Promise} comp - The component or Promise
*/

@@ -469,4 +677,4 @@

* Resolve a component from the container
* @param {string} name - The unique component key
* @returns {object}
* @param {string} name - The unique component key
* @returns {Promise}
*/

@@ -477,3 +685,13 @@

value: function resolve(name) {
return this._components[name];
var registration = this._components[name];
if (!registration) {
return Promise.reject(new Error('Not Registered'));
}
if (_isPromise(registration)) {
return registration;
}
return Promise.resolve(registration);
}

@@ -491,42 +709,2 @@

}
/**
* Register a component in the container
* @param {string} name - A unique component key
* @param {object} comp - The component
* @deprecated
*/
}, {
key: 'registerComponent',
value: function registerComponent(name, comp) {
_Logger2.default.warn('RHW03', 'registerComponent is being deprecated. Please use "register" instead.');
this.register(name, comp);
}
/**
* Register multiple components to the container
* @param {object} comps - The components
*/
}, {
key: 'registerComponents',
value: function registerComponents(comps) {
_Logger2.default.warn('RHW03', 'registerComponents is being deprecated. Please use "registerAll" instead.');
this.registerAll(comps);
}
/**
* Gets a component for key
* @param name
* @returns {Object}
* @deprecated
*/
}, {
key: 'getComponent',
value: function getComponent(name) {
_Logger2.default.warn('RHW03', 'getComponent is being deprecated. Please use "resolve" instead.');
return this.resolve(name);
}
}]);

@@ -540,5 +718,5 @@

/***/ },
/***/ }),
/* 4 */
/***/ function(module, exports, __webpack_require__) {
/***/ (function(module, exports, __webpack_require__) {

@@ -628,3 +806,4 @@ 'use strict';

var props = {
proxy: ele };
proxy: ele // Pass in a reference to the original node
};

@@ -749,3 +928,4 @@ // Populate custom props from reading any ele attributes that start with 'data-prop-'

try {
// It might be better if we keep references in a weak map, need to look at this in the future
// It might be better if we keep references in a weak map, need to look
// at this in the future
habitat[HABITAT_HOST_KEY] = host;

@@ -814,5 +994,5 @@ } catch (e) {

/***/ },
/***/ }),
/* 5 */
/***/ function(module, exports, __webpack_require__) {
/***/ (function(module, exports, __webpack_require__) {

@@ -825,2 +1005,5 @@ 'use strict';

exports._Mixin = undefined;
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
exports.createBootstrapper = createBootstrapper;

@@ -860,3 +1043,3 @@

*/
function _Mixin(spec) {
function _Mixin(spec, callback) {
_classCallCheck(this, _Mixin);

@@ -877,2 +1060,7 @@

// Set the watcher value if defined
if (typeof spec.enableWatcher === 'boolean') {
_this.enableWatcher = spec.enableWatcher;
}
// Create a new container

@@ -886,7 +1074,34 @@ var container = new _Container2.default();

_this._shouldUpdateProxy = spec.shouldUpdate || null;
_this._willUpdateProxy = spec.willUpdate || null;
_this._didUpdateProxy = spec.didUpdate || null;
// Finally, set the container
_this.setContainer(container);
_this.setContainer(container, callback);
return _this;
}
_createClass(_Mixin, [{
key: 'shouldUpdate',
value: function shouldUpdate(node) {
if (this._shouldUpdateProxy) {
this._shouldUpdateProxy(node);
}
}
}, {
key: 'willUpdate',
value: function willUpdate() {
if (this._willUpdateProxy) {
this._willUpdateProxy();
}
}
}, {
key: 'didUpdate',
value: function didUpdate() {
if (this._didUpdateProxy) {
this._didUpdateProxy();
}
}
}]);
return _Mixin;

@@ -901,8 +1116,10 @@ }(_Bootstrapper3.default);

function createBootstrapper(spec) {
return new _Mixin(spec);
var cb = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
return new _Mixin(spec, cb);
}
/***/ },
/***/ }),
/* 6 */
/***/ function(module, exports, __webpack_require__) {
/***/ (function(module, exports, __webpack_require__) {

@@ -986,17 +1203,17 @@ 'use strict';

/***/ },
/***/ }),
/* 7 */
/***/ function(module, exports) {
/***/ (function(module, exports) {
module.exports = __WEBPACK_EXTERNAL_MODULE_7__;
/***/ },
/***/ }),
/* 8 */
/***/ function(module, exports) {
/***/ (function(module, exports) {
module.exports = __WEBPACK_EXTERNAL_MODULE_8__;
/***/ }
/***/ })
/******/ ])
});
;

@@ -1,1 +0,1 @@

!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("React"),require("ReactDOM")):"function"==typeof define&&define.amd?define(["React","ReactDOM"],t):"object"==typeof exports?exports.ReactHabitat=t(require("React"),require("ReactDOM")):e.ReactHabitat=t(e.React,e.ReactDOM)}(this,function(e,t){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var o=n(2),a=r(o),i=n(3),u=r(i),l=n(5);t.default={Bootstrapper:a.default,Container:u.default,createBootstrapper:l.createBootstrapper},e.exports=t.default},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0});var o=function(){function e(e,t){for(var n=0;t.length>n;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),a=function(){},i=a,u=a,l="http://tinyurl.com/jxryd3s",c=function(){function e(){r(this,e)}return o(e,null,[{key:"warn",value:function(e,t){var n=u("WARNING: "+e+" "+t+" "+l+"#"+e.toLowerCase(),arguments);i("warn",n)}},{key:"error",value:function(e,t){var n=u("ERROR: "+e+" "+t+" "+l+"#"+e.toLowerCase(),arguments);i("error",n)}}]),e}();t.default=c,e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t,n){for(var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,o=e.domFactory(),a=e.id(),i=0;t.length>i;++i){var u=t[i],c=u.getAttribute(n),f=e.resolve(c);f?(u.querySelector("["+n+"]")&&s.default.warn("RHW08","Component should not contain any nested components.",u),o.inject(f,l.default.parseProps(u),l.default.create(u,a))):s.default.error("RHW01",'Cannot resolve component "'+c+'" for element.',u)}"function"==typeof r&&r.call()}Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t){for(var n=0;t.length>n;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),u=n(4),l=r(u),c=n(1),s=r(c),f="data-component",d=function(){function e(){if(o(this,e),!window||!window&&!window.document)throw Error("React Habitat requires a window but cannot see one :(");this.componentSelector=f,this._elements=null,this._container=null}return i(e,[{key:"setContainer",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return null!==this._container?void s.default.error("RHW02","A container is already set. Please call dispose() before assigning a new one."):(this._container=e,this._elements=window.document.body.querySelectorAll("["+this.componentSelector+"]"),void a(this._container,this._elements,this.componentSelector,t))}},{key:"dispose",value:function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,t=this._container.domFactory(),n=window.document.body.querySelectorAll('[data-habitat="'+this._container.id()+'"]'),r=0;n.length>r;++r)t.dispose(n[r]),l.default.destroy(n[r]);this._container=null,this._elements=null,"function"==typeof e&&e.call()}}]),e}();t.default=d,e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0});var a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},i=function(){function e(e,t){for(var n=0;t.length>n;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),u=n(6),l=r(u),c=n(1),s=r(c),f=function(){var e=0;return function(){return e++,"C"+e}}(),d=function(){function e(){o(this,e),this._components={},this._id=f()}return i(e,[{key:"id",value:function(){return this._id}},{key:"register",value:function(e,t){if("string"!=typeof e)throw Error("Unexpected component key. Expects a string.",e);this._components[e]=t}},{key:"registerAll",value:function(e){if("object"!==(void 0===e?"undefined":a(e)))throw Error("Unexpected components type. Expects type object",e);Object.assign(this._components,e)}},{key:"resolve",value:function(e){return this._components[e]}},{key:"domFactory",value:function(){return l.default}},{key:"registerComponent",value:function(e,t){s.default.warn("RHW03",'registerComponent is being deprecated. Please use "register" instead.'),this.register(e,t)}},{key:"registerComponents",value:function(e){s.default.warn("RHW03",'registerComponents is being deprecated. Please use "registerAll" instead.'),this.registerAll(e)}},{key:"getComponent",value:function(e){return s.default.warn("RHW03",'getComponent is being deprecated. Please use "resolve" instead.'),this.resolve(e)}}]),e}();t.default=d,e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e){var t=e.currentStyle||window.getComputedStyle(e,"");return t.display}function i(e){return e[1].toUpperCase()}function u(e,t){return t.replace(e,"").replace(/-([a-z])/g,i)}Object.defineProperty(t,"__esModule",{value:!0});var l=function(){function e(e,t){for(var n=0;t.length>n;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),c=n(1),s=r(c),f="habitatHostElement",d="data-habitat",p="data-has-habitat",y="data-prop-",b="data-props",v="data-n-prop-",h="data-r-prop-",m=!1,w=function(){function e(){o(this,e)}return l(e,null,[{key:"parseProps",value:function(e){for(var t={proxy:e},n=0;e.attributes.length>n;n++){var r=e.attributes[n];if(0===r.name.indexOf(y)){var o=u(y,r.name),a=r.value||"";"string"==typeof a&&"false"===a.toLowerCase()&&(a=!1),"string"==typeof a&&"true"===a.toLowerCase()&&(a=!0),"string"==typeof a&&a.length>=2&&("{"===a[0]&&"}"===a[a.length-1]||"["===a[0]&&"]"===a[a.length-1])&&(a=JSON.parse(a)),"string"==typeof a&&"null"===a.toLowerCase()&&(a=null),t[o]=a}else if(r.name===b)Object.assign(t,JSON.parse(r.value));else if(0===r.name.indexOf("data-n-prop-")){var i=u(v,r.name);t[i]=parseFloat(r.value)}else if(window&&0===r.name.indexOf(h)){var l=u(h,r.name);t[l]=window[r.value]}}return t}},{key:"create",value:function(e,t){if(window.document.body===e||null===e||void 0===e)return s.default.warn("RHW04","Cannot open a habitat for element.",e),null;var n="span";"block"===a(e)&&(n="div");var r=window.document.createElement(n),o=e.getAttribute("data-habitat-class")||null,i=!1;if(null!==e.getAttribute("data-habitat-no-replace")&&(i="true"===e.getAttribute("data-habitat-no-replace").toLocaleLowerCase()),r.setAttribute(d,t),o&&(r.className=""+o),e.parentNode.insertBefore(r,e.nextSibling),"INPUT"!==e.tagName){if(!i){var u=e.parentNode.removeChild(e);try{r[f]=u}catch(l){m&&(s.default.warn("RHW06","Arbitrary properties are disabled. The container may not dispose correctly.",l),m=!0)}}}else e.setAttribute(p,"true"),"hidden"!==e.getAttribute("type")&&e.setAttribute("style","display: none;");return r}},{key:"hasHabitat",value:function(e){return null!==e.getAttribute(p)}},{key:"destroy",value:function(e){try{void 0!==e[f]&&e.parentNode.insertBefore(e[f],e)}finally{e.parentNode.removeChild(e)}}}]),e}();t.default=w,e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function u(e){return new d(e)}Object.defineProperty(t,"__esModule",{value:!0}),t._Mixin=void 0,t.createBootstrapper=u;var l=n(2),c=r(l),s=n(3),f=r(s),d=t._Mixin=function(e){function t(e){o(this,t);var n=a(this,(t.__proto__||Object.getPrototypeOf(t)).call(this));if(!e.container)return console.warn('"Container" property was not supplied'),a(n);e.componentSelector&&(n.componentSelector=e.componentSelector);for(var r=new f.default,i=0;e.container.length>i;i++)r.register(e.container[i].register,e.container[i].for);return n.setContainer(r),n}return i(t,e),t}(c.default)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0});var a=function(){function e(e,t){for(var n=0;t.length>n;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),i=n(7),u=r(i),l=n(8),c=r(l),s=n(1),f=r(s),d=function(){function e(){o(this,e)}return a(e,null,[{key:"inject",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments[2];n?c.default.render(u.default.createElement(e,t||{}),n):f.default.warn("RHW07","Target element is null or undefined.")}},{key:"dispose",value:function(e){e&&c.default.unmountComponentAtNode(e)}}]),e}();t.default=d,e.exports=t.default},function(t,n){t.exports=e},function(e,n){e.exports=t}])});
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("React"),require("ReactDOM")):"function"==typeof define&&define.amd?define(["React","ReactDOM"],t):"object"==typeof exports?exports.ReactHabitat=t(require("React"),require("ReactDOM")):e.ReactHabitat=t(e.React,e.ReactDOM)}(this,function(e,t){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=n(2),a=r(o),i=n(3),u=r(i),l=n(5);t.default={Bootstrapper:a.default,Container:u.default,createBootstrapper:l.createBootstrapper},e.exports=t.default},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0});var o=function(){function e(e,t){for(var n=0;t.length>n;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),a=function(){},i=a,u=a,l="http://tinyurl.com/jxryd3s",c=function(){function e(){r(this,e)}return o(e,null,[{key:"warn",value:function(e,t){for(var n=arguments.length,r=Array(n>2?n-2:0),o=2;n>o;o++)r[o-2]=arguments[o];var a=u("WARNING: "+e+" "+t+" "+l+"#"+e.toLowerCase(),r);i("warn",a)}},{key:"error",value:function(e,t){for(var n=arguments.length,r=Array(n>2?n-2:0),o=2;n>o;o++)r[o-2]=arguments[o];var a=u("ERROR: "+e+" "+t+" "+l+"#"+e.toLowerCase(),r);i("error",a)}}]),e}();t.default=c,e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if("function"==typeof e){for(var n=arguments.length,r=Array(n>2?n-2:0),o=2;n>o;o++)r[o-2]=arguments[o];e.call.apply(e,[t].concat(r))}}function i(e,t,n){for(var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,o=e.domFactory(),i=e.id(),u=[],l=function(r){var a=t[r];if(c.default.hasHabitat(a))return"continue";var l=a.getAttribute(n);u.push(e.resolve(l).then(function(e){o.inject(e,c.default.parseProps(a),c.default.create(a,i))}).catch(function(e){f.default.error("RHW01",'Cannot resolve component "'+l+'" for element.',e,a)}))},s=0;t.length>s;++s){l(s)}Promise.all(u.map(function(e){return e.catch(function(e){return e})})).then(function(){a(r)}).catch(function(e){throw e})}Object.defineProperty(t,"__esModule",{value:!0});var u=function(){function e(e,t){for(var n=0;t.length>n;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),l=n(4),c=r(l),s=n(1),f=r(s),d="data-component",p=function(){function e(){if(o(this,e),!window||!window&&!window.document)throw Error("React Habitat requires a window but cannot see one :(");this.componentSelector=d,this._container=null,this._observer=null,this._isWatching=!1}return u(e,[{key:"setContainer",value:function(e){var t=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return null!==this._container?void f.default.error("RHW02","A container is already set. Please call dispose() before assigning a new one."):(this._container=e,void this.update(null,function(){a(n,t)}))}},{key:"update",value:function(e){var t=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;if(!this._container)return void a(n);var r=e||window.document.body.querySelectorAll("["+this.componentSelector+"]");if("function"==typeof this.shouldUpdate&&this.shouldUpdate(r)===!1)return void a(n,this);var o=this._isWatching;o&&this.stopWatcher(),"function"==typeof this.willUpdate&&this.willUpdate(r),i(this._container,r,this.componentSelector,function(){o&&t.startWatcher(),"function"==typeof t.didUpdate&&t.didUpdate(r),a(n,t)})}},{key:"startWatcher",value:function(){return"undefined"==typeof MutationObserver?void f.default.error("RHE09","MutationObserver not available."):(this._observer||(this._observer=new MutationObserver(this._handleDomMutation.bind(this))),this._observer.observe(window.document.body,{childList:!0,attributes:!0,subtree:!0,attributeOldValue:!1,characterData:!1,characterDataOldValue:!1,attributeFilter:[this.componentSelector]}),void(this._isWatching=!0))}},{key:"stopWatcher",value:function(){this._observer&&this._observer.disconnect&&this._observer.disconnect(),this._isWatching=!1}},{key:"_handleDomMutation",value:function(e){if(void 0!==e){var t=e.filter(function(e){return e.addedNodes.length});t.length&&this.update()}else this.update()}},{key:"dispose",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;this.stopWatcher();for(var t=this._container.domFactory(),n=window.document.body.querySelectorAll('[data-habitat="'+this._container.id()+'"]'),r=0;n.length>r;++r)t.dispose(n[r]),c.default.destroy(n[r]);this._container=null,this._observer=null,this._isWatching=!1,a(e,this)}}]),e}();t.default=p,e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0});var a=function(){function e(e,t){for(var n=0;t.length>n;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},u=n(6),l=r(u),c=function(){var e=0;return function(){return e+=1,"C"+e}}(),s=function(e){return!!e&&("object"===(void 0===e?"undefined":i(e))||"function"==typeof e)&&"function"==typeof e.then},f=function(){function e(){o(this,e),this._components={},this._id=c()}return a(e,[{key:"id",value:function(){return this._id}},{key:"register",value:function(e,t){if("string"!=typeof e)throw Error("Unexpected component key. Expects a string.",e);this._components[e]=t}},{key:"registerAll",value:function(e){if("object"!==(void 0===e?"undefined":i(e)))throw Error("Unexpected components type. Expects type object",e);Object.assign(this._components,e)}},{key:"resolve",value:function(e){var t=this._components[e];return t?s(t)?t:Promise.resolve(t):Promise.reject(Error("Not Registered"))}},{key:"domFactory",value:function(){return l.default}}]),e}();t.default=f,e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e){var t=e.currentStyle||window.getComputedStyle(e,"");return t.display}function i(e){return e[1].toUpperCase()}function u(e,t){return t.replace(e,"").replace(/-([a-z])/g,i)}Object.defineProperty(t,"__esModule",{value:!0});var l=function(){function e(e,t){for(var n=0;t.length>n;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),c=n(1),s=r(c),f="habitatHostElement",d="data-habitat",p="data-has-habitat",h="data-prop-",y="data-props",v="data-n-prop-",b="data-r-prop-",m=!1,w=function(){function e(){o(this,e)}return l(e,null,[{key:"parseProps",value:function(e){for(var t={proxy:e},n=0;e.attributes.length>n;n++){var r=e.attributes[n];if(0===r.name.indexOf(h)){var o=u(h,r.name),a=r.value||"";"string"==typeof a&&"false"===a.toLowerCase()&&(a=!1),"string"==typeof a&&"true"===a.toLowerCase()&&(a=!0),"string"==typeof a&&a.length>=2&&("{"===a[0]&&"}"===a[a.length-1]||"["===a[0]&&"]"===a[a.length-1])&&(a=JSON.parse(a)),"string"==typeof a&&"null"===a.toLowerCase()&&(a=null),t[o]=a}else if(r.name===y)Object.assign(t,JSON.parse(r.value));else if(0===r.name.indexOf("data-n-prop-")){var i=u(v,r.name);t[i]=parseFloat(r.value)}else if(window&&0===r.name.indexOf(b)){var l=u(b,r.name);t[l]=window[r.value]}}return t}},{key:"create",value:function(e,t){if(window.document.body===e||null===e||void 0===e)return s.default.warn("RHW04","Cannot open a habitat for element.",e),null;var n="span";"block"===a(e)&&(n="div");var r=window.document.createElement(n),o=e.getAttribute("data-habitat-class")||null,i=!1;if(null!==e.getAttribute("data-habitat-no-replace")&&(i="true"===e.getAttribute("data-habitat-no-replace").toLocaleLowerCase()),r.setAttribute(d,t),o&&(r.className=""+o),e.parentNode.insertBefore(r,e.nextSibling),"INPUT"!==e.tagName){if(!i){var u=e.parentNode.removeChild(e);try{r[f]=u}catch(e){m&&(s.default.warn("RHW06","Arbitrary properties are disabled. The container may not dispose correctly.",e),m=!0)}}}else e.setAttribute(p,"true"),"hidden"!==e.getAttribute("type")&&e.setAttribute("style","display: none;");return r}},{key:"hasHabitat",value:function(e){return null!==e.getAttribute(p)}},{key:"destroy",value:function(e){try{void 0!==e[f]&&e.parentNode.insertBefore(e[f],e)}finally{e.parentNode.removeChild(e)}}}]),e}();t.default=w,e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function u(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return new p(e,t)}Object.defineProperty(t,"__esModule",{value:!0}),t._Mixin=void 0;var l=function(){function e(e,t){for(var n=0;t.length>n;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}();t.createBootstrapper=u;var c=n(2),s=r(c),f=n(3),d=r(f),p=t._Mixin=function(e){function t(e,n){o(this,t);var r=a(this,(t.__proto__||Object.getPrototypeOf(t)).call(this));if(!e.container)return console.warn('"Container" property was not supplied'),a(r);e.componentSelector&&(r.componentSelector=e.componentSelector),"boolean"==typeof e.enableWatcher&&(r.enableWatcher=e.enableWatcher);for(var i=new d.default,u=0;e.container.length>u;u++)i.register(e.container[u].register,e.container[u].for);return r._shouldUpdateProxy=e.shouldUpdate||null,r._willUpdateProxy=e.willUpdate||null,r._didUpdateProxy=e.didUpdate||null,r.setContainer(i,n),r}return i(t,e),l(t,[{key:"shouldUpdate",value:function(e){this._shouldUpdateProxy&&this._shouldUpdateProxy(e)}},{key:"willUpdate",value:function(){this._willUpdateProxy&&this._willUpdateProxy()}},{key:"didUpdate",value:function(){this._didUpdateProxy&&this._didUpdateProxy()}}]),t}(s.default)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0});var a=function(){function e(e,t){for(var n=0;t.length>n;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),i=n(7),u=r(i),l=n(8),c=r(l),s=n(1),f=r(s),d=function(){function e(){o(this,e)}return a(e,null,[{key:"inject",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments[2];n?c.default.render(u.default.createElement(e,t||{}),n):f.default.warn("RHW07","Target element is null or undefined.")}},{key:"dispose",value:function(e){e&&c.default.unmountComponentAtNode(e)}}]),e}();t.default=d,e.exports=t.default},function(t,n){t.exports=e},function(e,n){e.exports=t}])});

@@ -69,5 +69,20 @@ /**

/**
* Dispose of the container
*/
dispose: () => void;
* Apply the container to an updated dom structure
*/
update: (node?: Element) => void;
/**
* Start DOM watcher for auto wire ups
*/
startWatcher: () => void;
/**
* Stop the DOM watcher if running
*/
stopWatcher: () => void;
/**
* Dispose of the container
*/
dispose: () => void;
}

@@ -88,7 +103,17 @@

/**
* Collection of elements matching the component selector
* Apply the container to an updated dom structure
*/
elements: NodeListOf<Element>;
update: (node?: Element) => void;
/**
* Start DOM watcher for auto wire ups
*/
startWatcher: () => void;
/**
* Stop the DOM watcher if running
*/
stopWatcher: () => void;
/**
* Disposes the container

@@ -95,0 +120,0 @@ */

@@ -30,9 +30,25 @@ 'use strict';

/**
* Parses a container and populate components
* Safe callback wrapper
* @param {null|function} cb - The callback
* @private
*/
function _callback(cb, context) {
if (typeof cb === 'function') {
for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
args[_key - 2] = arguments[_key];
}
cb.call.apply(cb, [context].concat(args));
}
}
/**
* Apply a container to nodes and populate components
* @param {array} container The container
* @param {array} elements The elements to parse
* @param {array} nodes The elements to parse
* @param {string} componentSelector The component selector
* @param cb
* @param {function} [cb=null] - Optional callback
* @private
*/
function parseContainer(container, elements, componentSelector) {
function _applyContainer(container, nodes, componentSelector) {
var cb = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;

@@ -43,22 +59,48 @@

var id = container.id();
var resolveQueue = [];
// Iterate over component elements in the dom
for (var i = 0; i < elements.length; ++i) {
var ele = elements[i];
var _loop = function _loop(i) {
var ele = nodes[i];
// Ignore elements that have already been connected
if (_Habitat2.default.hasHabitat(ele)) {
return 'continue';
}
var componentName = ele.getAttribute(componentSelector);
var component = container.resolve(componentName);
resolveQueue.push(container.resolve(componentName).then(function (component) {
// This is an expensive operation so only do on non prod builds
if (process.env.NODE_ENV !== 'production') {
if (ele.querySelector('[' + componentSelector + ']')) {
_Logger2.default.warn('RHW08', 'Component should not contain any nested components.', ele);
}
}
if (component) {
if (ele.querySelector('[' + componentSelector + ']')) {
_Logger2.default.warn('RHW08', 'Component should not contain any nested components.', ele);
}
// Inject the component
factory.inject(component, _Habitat2.default.parseProps(ele), _Habitat2.default.create(ele, id));
} else {
_Logger2.default.error('RHW01', 'Cannot resolve component "' + componentName + '" for element.', ele);
}
}).catch(function (err) {
_Logger2.default.error('RHW01', 'Cannot resolve component "' + componentName + '" for element.', err, ele);
}));
};
for (var i = 0; i < nodes.length; ++i) {
var _ret = _loop(i);
if (_ret === 'continue') continue;
}
if (typeof cb === 'function') {
cb.call();
}
// Trigger callback when all promises are finished
// regardless if some fail
Promise.all(resolveQueue.map(function (p) {
return p.catch(function (e) {
return e;
});
})).then(function () {
_callback(cb);
}).catch(function (err) {
// We should never get here.. if we do this is a bug
throw err;
});
}

@@ -83,10 +125,28 @@

// Set dom component selector
/**
* The DOM component selector
* @type {string}
*/
this.componentSelector = DEFAULT_HABITAT_SELECTOR;
// The target elements
this._elements = null;
/**
* The container
* @type {Container|null}
* @private
*/
this._container = null;
// The container
this._container = null;
/**
* The watcher's observer instance or null
* @type {MutationObserver|null}
* @private
*/
this._observer = null;
/**
* Observing persistence status flag
* @type {boolean}
* @private
*/
this._isWatching = false;
}

@@ -104,2 +164,4 @@

value: function setContainer(container) {
var _this = this;
var cb = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;

@@ -116,10 +178,135 @@

// Find all the elements in the dom with the component selector attribute
this._elements = window.document.body.querySelectorAll('[' + this.componentSelector + ']');
// Wire up the components from the container
parseContainer(this._container, this._elements, this.componentSelector, cb);
this.update(null, function () {
_callback(cb, _this);
});
}
/**
* Apply the container to an updated dom structure
* @param {node} node - Target node to parse or null for entire document body
* @param {function} [cb=null] - Optional callback
*/
}, {
key: 'update',
value: function update(node) {
var _this2 = this;
var cb = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
// Check if we have a container before attempting an update
if (!this._container) {
_callback(cb);
return;
}
var target = node || window.document.body.querySelectorAll('[' + this.componentSelector + ']');
// Lifecycle event
// Hook to allow developers to cancel operation
if (typeof this.shouldUpdate === 'function') {
if (this.shouldUpdate(target) === false) {
_callback(cb, this);
return;
}
}
// Temporarily stop the watcher from triggering from our own Habitat injections
// This is better for performance however, this could possibly miss any
// mutations during the parsing time.. possible bug maybe? dont know yet.
var shouldWatcherPersist = this._isWatching;
if (shouldWatcherPersist) {
this.stopWatcher();
}
// Lifecycle event
if (typeof this.willUpdate === 'function') {
this.willUpdate(target);
}
_applyContainer(this._container, target, this.componentSelector, function () {
// Restart the dom watcher if persisting
if (shouldWatcherPersist) {
_this2.startWatcher();
}
// Lifecycle event
if (typeof _this2.didUpdate === 'function') {
_this2.didUpdate(target);
}
_callback(cb, _this2);
});
}
/**
* Start DOM watcher for auto wire ups
*/
}, {
key: 'startWatcher',
value: function startWatcher() {
// Feature available?
if (typeof MutationObserver === 'undefined') {
_Logger2.default.error('RHE09', 'MutationObserver not available.');
return;
}
// Create observer if not assigned already
if (!this._observer) {
this._observer = new MutationObserver(this._handleDomMutation.bind(this));
}
// Start observing for dom changes filtered by our component selector
this._observer.observe(window.document.body, {
childList: true,
attributes: true,
subtree: true,
attributeOldValue: false,
characterData: false,
characterDataOldValue: false,
attributeFilter: [this.componentSelector]
});
// Set flag for persistence during update's
this._isWatching = true;
}
/**
* Stop the DOM watcher if running
*/
}, {
key: 'stopWatcher',
value: function stopWatcher() {
if (this._observer && this._observer.disconnect) {
this._observer.disconnect();
}
this._isWatching = false;
}
/**
* Handle dom mutation event
* @param {MutationRecord} mutationRecord - The mutation record
*/
}, {
key: '_handleDomMutation',
value: function _handleDomMutation(mutationRecord) {
if (typeof mutationRecord !== 'undefined') {
// Only run update if nodes have been added
var diff = mutationRecord.filter(function (r) {
return r.addedNodes.length;
});
if (diff.length) {
this.update();
}
} else {
// Fallback
this.update();
}
}
/**
* Dispose the container and destroy habitat instances

@@ -135,2 +322,5 @@ * @param {function} [cb=null] - Optional callback

// Stop dom watcher if any
this.stopWatcher();
// get the container's factory

@@ -150,8 +340,7 @@ var factory = this._container.domFactory();

this._container = null;
this._elements = null;
this._observer = null;
this._isWatching = false;
// Handle callback
if (typeof cb === 'function') {
cb.call();
}
_callback(cb, this);
}

@@ -158,0 +347,0 @@ }]);

@@ -7,2 +7,5 @@ 'use strict';

exports._Mixin = undefined;
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
exports.createBootstrapper = createBootstrapper;

@@ -42,3 +45,3 @@

*/
function _Mixin(spec) {
function _Mixin(spec, callback) {
_classCallCheck(this, _Mixin);

@@ -59,2 +62,7 @@

// Set the watcher value if defined
if (typeof spec.enableWatcher === 'boolean') {
_this.enableWatcher = spec.enableWatcher;
}
// Create a new container

@@ -68,7 +76,34 @@ var container = new _Container2.default();

_this._shouldUpdateProxy = spec.shouldUpdate || null;
_this._willUpdateProxy = spec.willUpdate || null;
_this._didUpdateProxy = spec.didUpdate || null;
// Finally, set the container
_this.setContainer(container);
_this.setContainer(container, callback);
return _this;
}
_createClass(_Mixin, [{
key: 'shouldUpdate',
value: function shouldUpdate(node) {
if (this._shouldUpdateProxy) {
this._shouldUpdateProxy(node);
}
}
}, {
key: 'willUpdate',
value: function willUpdate() {
if (this._willUpdateProxy) {
this._willUpdateProxy();
}
}
}, {
key: 'didUpdate',
value: function didUpdate() {
if (this._didUpdateProxy) {
this._didUpdateProxy();
}
}
}]);
return _Mixin;

@@ -83,3 +118,5 @@ }(_Bootstrapper3.default);

function createBootstrapper(spec) {
return new _Mixin(spec);
var cb = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
return new _Mixin(spec, cb);
}

@@ -7,11 +7,11 @@ 'use strict';

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /**
* Copyright 2016-present, Deloitte Digital.
* All rights reserved.
*
* This source code is licensed under the BSD-3-Clause license found in the
* LICENSE file in the root directory of this source tree.
*/
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /**
* Copyright 2016-present, Deloitte Digital.
* All rights reserved.
*
* This source code is licensed under the BSD-3-Clause license found in the
* LICENSE file in the root directory of this source tree.
*/

@@ -22,6 +22,2 @@ var _ReactDomFactory = require('./factories/ReactDomFactory');

var _Logger = require('./Logger');
var _Logger2 = _interopRequireDefault(_Logger);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -36,8 +32,7 @@

*/
var assignId = function idFactory() {
var _nextId = 0;
var _assignId = function idFactory() {
var nextId = 0;
return function _assignId() {
_nextId++;
return 'C' + _nextId;
nextId = nextId + 1;
return 'C' + nextId;
};

@@ -47,2 +42,12 @@ }();

/**
* Determine if the object is a Promise
* @param {*} obj - The test object
* @returns {boolean} - True if deemed to be a promise
* @private
*/
var _isPromise = function _isPromise(obj) {
return !!obj && ((typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
};
/**
* The Container class

@@ -60,3 +65,3 @@ */

this._components = {};
this._id = assignId();
this._id = _assignId();
}

@@ -78,4 +83,4 @@

* Register a component in the container
* @param {string} name - A unique component key
* @param {object} comp - The component
* @param {string} name - A unique component key
* @param {object|Promise} comp - The component or Promise
*/

@@ -109,4 +114,4 @@

* Resolve a component from the container
* @param {string} name - The unique component key
* @returns {object}
* @param {string} name - The unique component key
* @returns {Promise}
*/

@@ -117,3 +122,13 @@

value: function resolve(name) {
return this._components[name];
var registration = this._components[name];
if (!registration) {
return Promise.reject(new Error('Not Registered'));
}
if (_isPromise(registration)) {
return registration;
}
return Promise.resolve(registration);
}

@@ -131,42 +146,2 @@

}
/**
* Register a component in the container
* @param {string} name - A unique component key
* @param {object} comp - The component
* @deprecated
*/
}, {
key: 'registerComponent',
value: function registerComponent(name, comp) {
_Logger2.default.warn('RHW03', 'registerComponent is being deprecated. Please use "register" instead.');
this.register(name, comp);
}
/**
* Register multiple components to the container
* @param {object} comps - The components
*/
}, {
key: 'registerComponents',
value: function registerComponents(comps) {
_Logger2.default.warn('RHW03', 'registerComponents is being deprecated. Please use "registerAll" instead.');
this.registerAll(comps);
}
/**
* Gets a component for key
* @param name
* @returns {Object}
* @deprecated
*/
}, {
key: 'getComponent',
value: function getComponent(name) {
_Logger2.default.warn('RHW03', 'getComponent is being deprecated. Please use "resolve" instead.');
return this.resolve(name);
}
}]);

@@ -173,0 +148,0 @@

@@ -84,3 +84,4 @@ 'use strict';

var props = {
proxy: ele };
proxy: ele // Pass in a reference to the original node
};

@@ -205,3 +206,4 @@ // Populate custom props from reading any ele attributes that start with 'data-prop-'

try {
// It might be better if we keep references in a weak map, need to look at this in the future
// It might be better if we keep references in a weak map, need to look
// at this in the future
habitat[HABITAT_HOST_KEY] = host;

@@ -208,0 +210,0 @@ } catch (e) {

'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
value: true
});

@@ -27,26 +27,30 @@

/**
* Safely log to the console
*/
log = function log(type, args) {
/**
* Safely log to the console
*/
log = function log(type, args) {
if (typeof console !== 'undefined' && console[type]) {
if (console[type].apply) {
console[type].apply(undefined, args);
} else {
// IE9 Fallback
console[type](args);
}
}
};
if (typeof console !== 'undefined' && console[type]) {
console[type].apply(undefined, args);
}
};
/**
* Concats the message and arguments into a single array
*/
concatArgs = function concatArgs(msg, args) {
var throwArgs = [msg];
/**
* Concats the message and arguments into a single array
*/
concatArgs = function concatArgs(msg, args) {
var throwArgs = [msg];
if (args && args.length) {
for (var i = 0; i < args.length; i++) {
throwArgs.push(args[i]);
}
}
if (args && args.length > 2) {
for (var i = 2; i < args.length; i++) {
throwArgs.push(args[i]);
}
}
return throwArgs;
};
return throwArgs;
};
}

@@ -59,35 +63,45 @@

var Logger = function () {
function Logger() {
_classCallCheck(this, Logger);
}
function Logger() {
_classCallCheck(this, Logger);
}
_createClass(Logger, null, [{
key: 'warn',
_createClass(Logger, null, [{
key: 'warn',
/**
* Log a warning
* @param {string} code - The warning code
* @param {string} msg - The warning message
*/
value: function warn(code, msg) {
var args = concatArgs('WARNING: ' + code + ' ' + msg + ' ' + WARN_DEFINITIONS_URL + '#' + code.toLowerCase(), arguments);
log('warn', args);
}
/**
* Log a warning
* @param {string} code - The warning code
* @param {string} msg - The warning message
* @param {Array} debugs - Any debugging arguments
*/
value: function warn(code, msg) {
for (var _len = arguments.length, debugs = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
debugs[_key - 2] = arguments[_key];
}
/**
* Log an error
* @param {string} code - The warning code
* @param {string} msg - The error message
*/
var args = concatArgs('WARNING: ' + code + ' ' + msg + ' ' + WARN_DEFINITIONS_URL + '#' + code.toLowerCase(), debugs);
log('warn', args);
}
}, {
key: 'error',
value: function error(code, msg) {
var args = concatArgs('ERROR: ' + code + ' ' + msg + ' ' + WARN_DEFINITIONS_URL + '#' + code.toLowerCase(), arguments);
log('error', args);
}
}]);
/**
* Log an error
* @param {string} code - The warning code
* @param {string} msg - The error message
* @param {Array} debugs - Any debugging arguments
*/
return Logger;
}, {
key: 'error',
value: function error(code, msg) {
for (var _len2 = arguments.length, debugs = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
debugs[_key2 - 2] = arguments[_key2];
}
var args = concatArgs('ERROR: ' + code + ' ' + msg + ' ' + WARN_DEFINITIONS_URL + '#' + code.toLowerCase(), debugs);
log('error', args);
}
}]);
return Logger;
}();

@@ -94,0 +108,0 @@

{
"name": "react-habitat",
"version": "0.4.2",
"version": "0.5.0-beta",
"description": "A React DOM Bootstrapper designed to harmonise a hybrid application",

@@ -46,4 +46,4 @@ "main": "./lib/index.js",

"eslint": "^3.2.2",
"eslint-config-airbnb": "^10.0.0",
"eslint-plugin-import": "^1.12.0",
"eslint-config-deloitte": "^1.0.1",
"eslint-plugin-import": "^1.16.0",
"eslint-plugin-jasmine": "^1.8.1",

@@ -53,8 +53,9 @@ "eslint-plugin-jsx-a11y": "^2.0.1",

"jasmine-core": "^2.4.1",
"karma": "^1.1.2",
"karma": "^1.7.0",
"karma-chrome-launcher": "^1.0.1",
"karma-jasmine": "^1.0.2",
"karma-phantomjs-launcher": "^1.0.1",
"karma-firefox-launcher": "^1.0.1",
"karma-jasmine": "^1.1.0",
"karma-polyfill": "^1.0.0",
"karma-webpack": "^1.8.0",
"phantomjs-polyfill-object-assign": "0.0.2",
"prop-types": "^15.5.10",
"webpack": "^1.13.0"

@@ -76,3 +77,4 @@ },

"scripts": {
"test": "karma start --single-run --browsers PhantomJS",
"test": "npm run lint && karma start --single-run",
"debug": "NODE_ENV=development karma start",
"build": "npm run build:lib && npm run build:umd && npm run build:umd:min",

@@ -79,0 +81,0 @@ "build:lib": "babel src --out-dir lib",

@@ -23,2 +23,11 @@ ![Deloitte Digital](https://raw.githubusercontent.com/DeloitteDigital/DDBreakpoints/master/docs/deloittedigital-logo-white.png)

- [Options and Methods](#options-and-methods)
- [Setting the habitats css class](#setting-the-habitats-css-class)
- [Replace original node](#replace-original-node)
- [Dynamic Updates](#dynamic-updates)
- [Update Lifecycle](#update-lifecycle)
- [Start the dom watcher](#start-watcher)
- [Stop the dom watcher](#stop-watcher)
- [Disposing the container](#disposing-the-container)
- [Dynamic imports and code splitting](#dynamic-imports-and-code-splitting)
- [Use encoded JSON in HTML attributes](#use-encoded-json-in-html-attributes)
- [Contribute](#want-to-contribute)

@@ -42,2 +51,3 @@ - [License information](#license-bsd-3-clause)

- Adobe Experience Manager
- Hybris
- Umbraco

@@ -52,3 +62,3 @@ - Drupal

Typically if you're building a full-on one page React app that yanks data from restful API's... then this framework isn't really going to bring much benefit to you. However you are definitely invited to use it if you want to.
Typically if you're using a router... then this framework isn't really going to bring much benefit to you. However you are definitely invited to use it if you want to.

@@ -69,6 +79,11 @@ ## Features

- Supports Browsers IE9+ and all the evergreens. (IE9-11 will require an "Object.assign" [Pollyfill](https://babeljs.io/docs/usage/polyfill/))
- Supports Browsers IE9+ and all the evergreens.
- ES5, ES6/7 & TypeScript
- React v15 and up
Polyfills
- IE9-11 Will require a [Promise Polyfill](http://babeljs.io/docs/usage/polyfill/)
- IE9-10 Will require a [Object.assign Polyfill](http://babeljs.io/docs/usage/polyfill/)
We highly recommend you use something like [WebPack](https://webpack.github.io/) or [Browserify](http://browserify.org/) when using this framework.

@@ -78,9 +93,9 @@

Install with Node Package Manager (NPM)
Install with [NPM](http://npmjs.com/)
`npm install --save-dev react-habitat`
This assumes that you’re using [npm](http://npmjs.com/) package manager with a module bundler like [Webpack](http://webpack.github.io) or [Browserify](http://browserify.org/).
This assumes that you’re using a package manager with a module bundler like [Webpack](http://webpack.github.io) or [Browserify](http://browserify.org/).
If you don’t yet use [npm](http://npmjs.com/) or a modern module bundler, and would rather prefer a single-file [UMD](https://github.com/umdjs/umd) build that makes `ReactHabitat` available as a global object, you can grab a pre-built version from the dist folder.
If you don’t use a module bundler, and would prefer a single-file [UMD](https://github.com/umdjs/umd) build that makes `ReactHabitat` available as a global object, you can grab a pre-built version from the dist folder.

@@ -121,2 +136,3 @@ ## Getting Started

So for our sample application we need to register all of our components (classes) to be exposed to the DOM so things get wired up nicely.
Note in this example you can also define split points using React Habitat [dynamic imports](#dynamic-imports-and-code-splitting).

@@ -138,3 +154,6 @@ ```javascript

{register: 'SomeReactComponent', for: SomeReactComponent},
{register: 'AnotherReactComponent', for: AnotherReactComponent}
{register: 'AnotherReactComponent', for: AnotherReactComponent},
// Register a dynamic import
{register: 'AsyncReactComponent', for: import('./AsyncReactComponent')}
]

@@ -160,3 +179,3 @@ });

To *resolve* new instances of your components you need to attach a `data-component` attribute to a `div` or a `span` element in the HTML.
To *resolve* new instances of your components you need to attach a `data-component` attribute to a `div` or a `span` element in the HTML.
Any child components should be nested inside the React components themselves.

@@ -209,6 +228,7 @@

**Note** It's important that the output built javascript file is included at the end of the DOM just before the closing </body> tag.
**Note** It's important that the output built javascript file is included at the end of the DOM just before the closing body tag.
**[⬆ back to top](#table-of-contents)**
### Passing properties *(props)* to your components
## Passing properties *(props)* to your components

@@ -220,11 +240,14 @@ Resolving and registering components alone is not all that special, but passing data to it via html attributes is pretty useful. This allows the backend to

- [data-props](#data-props) Maps JSON to props.
- [data-prop-](#data-prop-) (Prefix) Maps in strings, booleans, null, array or JSON to a prop.
- [data-n-prop-](#data-n-prop-) (Prefix) Maps in numbers and floats to a prop.
- [data-r-prop-](#data-r-prop-) (Prefix) Maps in a reference to an object that exists on the global scope (window) to a prop.
|Attribute|Description|
|---|---|
|[data-props](#data-props)|Maps [encoded JSON](#use-encoded-json-in-html-attributes) to props.
|[data-prop-*](#data-prop-)|This [prefix](#prefix) maps in strings, booleans, null, array or [encoded JSON](#use-encoded-json-in-html-attributes) to a prop.
|[data-n-prop-*](#data-n-prop-)|This [prefix](#prefix) maps in numbers and floats to a prop.
|[data-r-prop-*](#data-r-prop-)|This [prefix](#prefix) in a reference to an object that exists on the global scope (window) to a prop.
**PLEASE NOTE:**
The last three options are attribute *prefixes*. This allow's you to define the property the name.
Property names will be *automatically converted* from hyphens to camel case.
### Prefix
With an attribute *prefix* the **\*** may be replaced by any name. This allow's you to define the property name.
Property names must be all lower case and hyphens will be *automatically converted* to camel case.
For example

@@ -236,7 +259,6 @@

### data-props
#### data-props
Set component props via a [encoded JSON](#use-encoded-json-in-html-attributes) string on the `data-props` attribute.
Set component props via a JSON string on the `data-props` attribute.
For example

@@ -248,3 +270,3 @@

#### data-prop-
### data-prop-*

@@ -272,9 +294,9 @@ Set an component prop via prefixing attributes with `data-prop-`.

var SomeReactComponent = React.createClass({
render: function() {
render: function() {
this.props.title === "A nice title"; //> true
this.props.showTitle === true; //> true
this.props.title === "A nice title"; //> true
this.props.showTitle === true; //> true
return <div>{ this.props.showTitle ? this.props.title : null }</div>;
}
return <div>{ this.props.showTitle ? this.props.title : null }</div>;
}
});

@@ -287,3 +309,3 @@ ```

<div data-component="SomeReactComponent"
data-prop-person="{'name': 'john', 'age': 22}">
data-prop-person='{"name": "john", "age": 22}'>
</div>

@@ -296,10 +318,10 @@ ```

var SomeReactComponent = React.createClass({
render: function() {
render: function() {
return (
<div>
Name: {this.props.person.name}
Age: {this.props.person.age}
</div>
);
return (
<div>
Name: {this.props.person.name}
Age: {this.props.person.age}
</div>
);
}

@@ -310,3 +332,3 @@ });

#### data-n-prop-
### data-n-prop-*

@@ -319,3 +341,3 @@ Set an component prop with type [number] via prefixing attributes with `data-n-prop-`.

#### data-r-prop-
### data-r-prop-*

@@ -333,5 +355,7 @@ Referenced a global variable in your component prop via prefixing attributes with `data-r-prop-`.

```
This is handy if you need to share properties between habitats or you need to set JSON onto the page.
**[⬆ back to top](#table-of-contents)**
## Passing values back again

@@ -341,3 +365,3 @@

*Every* React Habitat instance is passed in a prop named `proxy`, this is a reference the original dom element.
*Every* React Habitat instance is passed in a prop named `proxy`, this is a reference the original dom element.
Please note only `<inputs />` are left in the DOM by default. To keep a generic element in the DOM, set the `data-habitat-no-replace="true"` attribute.

@@ -359,2 +383,4 @@

**[⬆ back to top](#table-of-contents)**
## Options & Methods

@@ -374,8 +400,12 @@

**[⬆ back to top](#table-of-contents)**
### Replace original node
By default only `<inputs />` are left in the DOM when a React Habitat is created.
By default only `<inputs />` are left in the DOM when a React Habitat is created.
To keep a generic element in the DOM, set the `data-habitat-no-replace="true"` attribute.
**[⬆ back to top](#table-of-contents)**
### Changing the habitat query selector

@@ -395,6 +425,6 @@

// Create a new react habitat bootstrapper
this.domContainer = ReactHabitat.createBootstrapper({
componentSelector: 'myComponents'
});
// Create a new react habitat bootstrapper
this.domContainer = ReactHabitat.createBootstrapper({
componentSelector: 'myComponents'
});

@@ -404,2 +434,140 @@ }

**[⬆ back to top](#table-of-contents)**
### Dynamic Updates
`update()`
Will scan the DOM and for any components that require wiring up (i.e after ajaxing in some HTML).
This can be evoked automatically by using a [watcher](#start-watcher).
Example
```javascript
function MyApp() {
// Create a new react habitat bootstrapper
this.domContainer = ReactHabitat.createBootstrapper({
componentSelector: 'myComponents'
});
// This will scan the entire document body
this.domContainer.update();
// Will scan just the children of the element with id 'content'
this.domContainer.update(window.document.getElementById('content'));
}
```
By default *update()* will scan the entire body, however a parent node can optionally be passed in for better
performance if you know where the update has occurred.
Example
```javascript
function MyApp() {
// Create a new react habitat bootstrapper
this.domContainer = ReactHabitat.createBootstrapper({
componentSelector: 'myComponents'
});
// Will scan just the children of the element with id 'content'
this.domContainer.update(window.document.getElementById('content'));
}
```
### Update Lifecycle
React Habitat applications have update "lifecycle methods" that you can override to run code at particular times
in the process.
`shouldUpdate(node)`
Called when an update has been requested. Return false to cancel the update.
`willUpdate(node)`
Called when an update is about to take place.
`didUpdate(node)`
Called after an update has taken place.
Example
```javascript
function MyApp() {
// Create a new react habitat bootstrapper
this.domContainer = ReactHabitat.createBootstrapper({
componentSelector: 'myComponents'
shouldUpdate: function(node) {
// Dont allow updates on div's
if (node.tagName === 'div') {
return false;
}
},
willUpdate: function(node) {
// will update
},
didUpdate: function(node) {
// did update
}
});
}
```
**[⬆ back to top](#table-of-contents)**
### Start Watcher
**Please Note** IE 9 & 10 will require a [MutationObserver polyfill](https://github.com/megawac/MutationObserver.js/tree/master)
to use this feature. An alternative is to call [update](#update) manually.
Start watching the DOM for any changes and wire up future components automatically (eg ajaxed HTML).
Example
```javascript
function MyApp() {
// Create a new react habitat bootstrapper
this.domContainer = ReactHabitat.createBootstrapper({
componentSelector: 'myComponents'
});
// Wire up any future habitat elements automatically
this.domContainer.startWatcher();
}
```
**[⬆ back to top](#table-of-contents)**
### Stop Watcher
Will stop watching the DOM for any changes.
Example
```javascript
function MyApp() {
// Create a new react habitat bootstrapper
this.domContainer = ReactHabitat.createBootstrapper({
componentSelector: 'myComponents'
});
// Stop the watcher
this.domContainer.stopWatcher();
}
```
**[⬆ back to top](#table-of-contents)**
### Disposing the container

@@ -414,12 +582,111 @@

// Create a new react habitat bootstrapper
this.domContainer = ReactHabitat.createBootstrapper({
...
});
// Create a new react habitat bootstrapper
this.domContainer = ReactHabitat.createBootstrapper({
...
});
this.domContainer.dispose();
this.domContainer.dispose();
}
```
**[⬆ back to top](#table-of-contents)**
## Dynamic imports and code splitting
React Habitat supports resolving components asynchronously by using Promises. To define async registrations, register a Promise (that resolves to a component) instead of a component.
for example
```javascript
container.register('AsynReactComponent', new Promise(function(resolve, reject) {
// .. do async work to get 'component', then
resolve(component);
}));
```
or with registerAll
```javascript
container.registerAll({
'SomeReactComponent': new Promise(function(resolve, reject) {
// .. do async work to get 'component', then
resolve(component);
})
});
```
React Habitat has no restrictions on how you want to resolve your components however this does enable you to define code split points.
**Code splitting** is one great feature that means our visitors dont need to download the entire app before they can use it.
Think of code splitting as incrementally download your application only as its needed.
While there are other methods for code splitting we will use Webpack for these examples.
Webpack 2 treats `import()` as a [split-point](https://webpack.js.org/guides/code-splitting-async/) and puts the requested module into a separate chunk.
So for example, we could create a split point using `import()` like this:
```javascript
container.register('AsynReactComponent', new Promise(function(resolve, reject) {
import('./components/MyComponent').then(function(MyComponent) {
resolve(MyComponent);
}).catch(function(err) {
reject(err);
})
}));
```
**However**, since `import()` returns a Promise, we can actually simplify the above to:
```javascript
container.register('AsynReactComponent', import('./components/MyComponent'));
```
Here is an example using `require.ensure()` to define a [split-point in webpack 1](https://webpack.github.io/docs/code-splitting.html)
```javascript
container.register('AsynReactComponent', new Promise(function(resolve, reject) {
require.ensure(['./components/MyComponent'], function(MyComponent) {
resolve(MyComponent);
});
}));
```
**[⬆ back to top](#table-of-contents)**
### Use encoded JSON in HTML attributes
When passing JSON to an attribute, you will need to encode the value so that content can be preserved and properly rendered.
As a general rule, escape the following characters with HTML entity encoding:
`&` --> `&amp;`
`<` --> `&lt;`
`>` --> `&gt;`
`"` --> `&quot;`
`'` --> `&#x27;`
`/` --> `&#x2F;`
Example:
`<div data-props="{&quot;foo&quot;&colon; &quot;bar&quot;}"></div>`
Additionally, an encoder may replace [extended ASCII characters](https://en.wikipedia.org/wiki/Extended_ASCII) with the equivalent HTML entity encoding.
Most backend systems are capable of doing this automatically. An alternative is to use the [data-r-prop-*](#data-r-prop-) option.
**Single of Double Quotes?**
Double quotes around attributes values are the most common and our recommendation for setting properties with React Habitat.
However, there is a known hack of wrapping JSON attributes with single quotes and escaping nested single quotes.
example
`<div data-props='{"restaurant": "Bob\'s bar and grill"}'></div>`
*We will use this method in the docs to maintain readability. However, we strongly recommend you encode in production code.*
**[⬆ back to top](#table-of-contents)**
## Want to contribute?

@@ -473,1 +740,3 @@

OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**[⬆ back to top](#table-of-contents)**
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc