├── .gitignore ├── .travis.yml ├── README.md ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | .nyc_output 2 | coverage 3 | package-lock.json 4 | node_modules -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: 3 | directories: 4 | - node_modules 5 | notifications: 6 | email: false 7 | node_js: 8 | - '8' 9 | before_script: 10 | - npm prune 11 | after_success: 12 | - npm run semantic-release 13 | branches: 14 | except: 15 | - /^v\d+\.\d+\.\d+$/ 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## DEPRECATED: Use [rza](https://github.com/mikeal/rza) for a class based approach or [gza](https://github.com/mikeal/gza) for a functional approach. 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* globals MutationObserver, HTMLElement */ 2 | const each = (arr, fn) => { 3 | return Array.from(arr).forEach(fn) 4 | } 5 | 6 | const observer = (element, onAttributes) => { 7 | var observer = new MutationObserver(mutations => { 8 | mutations = Array.from(mutations) 9 | let attributes = Object.assign({}, 10 | mutations 11 | .filter(m => m.type === 'attributes') 12 | .map(m => m.attributeName) 13 | .map(attr => { 14 | let o = {} 15 | o[attr] = element.getAttribute(attr) 16 | return o 17 | }) 18 | ) 19 | onAttributes(attributes) 20 | 21 | mutations.filter(m => m.type === 'childList') 22 | .forEach(m => { 23 | if (m.addedNodes && element.onAddedNode) { 24 | each(m.addedNodes, n => element.onAddedNode(n)) 25 | } 26 | if (m.removedNodes && element.onRemovedNode) { 27 | each(m.removedNodes, n => element.onRemovedNode(n)) 28 | } 29 | }) 30 | }) 31 | 32 | observer.observe(element, {attributes: true, childList: true}) 33 | return observer 34 | } 35 | 36 | class ZComponent extends HTMLElement { 37 | constructor () { 38 | super() 39 | let shadow = this.shadow 40 | if (shadow) { 41 | let shadowRoot = this.attachShadow({mode: 'open'}) 42 | shadowRoot.innerHTML = shadow 43 | } 44 | let _keys = [] 45 | let parseProto = obj => { 46 | let proto = Object.getPrototypeOf(obj) 47 | if (proto === ZComponent) return 48 | 49 | let descs = Object.getOwnPropertyDescriptors(proto) 50 | let __keys = Object.keys(descs).filter(k => descs[k].set) 51 | 52 | /* This is a hack, and I would prefer a different detection method. 53 | The problem is that the entire proto chain is just "HTMLElement" 54 | and the polyfills for WebComponents complicate things a bit. 55 | I tried other detection methods but this was the best I could do. 56 | */ 57 | if (!__keys.includes('_zcomponentSetterIdentifier')) { 58 | _keys = _keys.concat(__keys) 59 | parseProto(proto) 60 | } 61 | } 62 | parseProto(this) 63 | observer(this, attributes => { 64 | for (let key in attributes) { 65 | if (_keys.indexOf(key) !== -1) { 66 | this[key] = attributes[key] 67 | } 68 | } 69 | }) 70 | let constructedKeys = [] 71 | each(this.attributes, node => { 72 | let key = node.name 73 | if (_keys.indexOf(key) !== -1) { 74 | // Ensure subclass constructor has finished 75 | // before setting properties w/ setTimeout. 76 | setTimeout(() => { 77 | this[key] = node.value 78 | }, 0) 79 | } 80 | constructedKeys.push(key) 81 | }) 82 | // Safari Hack 83 | // For some reason mutation observer doesn't pick up 84 | // initial attributes but those are also not on the element yet. 85 | setTimeout(() => { 86 | each(this.attributes, node => { 87 | let key = node.name 88 | if (constructedKeys.includes(key)) return 89 | if (_keys.indexOf(key) !== -1) { 90 | this[key] = node.value 91 | } 92 | }) 93 | }, 0) 94 | } 95 | set shadow (shadow) { 96 | this.shadowRoot.innerHTML = shadow 97 | } 98 | set _zcomponentSetterIdentifier (value) { 99 | } 100 | } 101 | 102 | module.exports = ZComponent 103 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zcomponent", 3 | "version": "0.0.0-development", 4 | "description": "WebComponent base class for building custom HTML elements.", 5 | "main": "index.js", 6 | "scripts": { 7 | "commit": "git-cz", 8 | "test": "standard", 9 | "precommit": "npm test", 10 | "prepush": "npm test", 11 | "commitmsg": "validate-commit-msg", 12 | "semantic-release": "semantic-release pre && npm publish && semantic-release post" 13 | }, 14 | "keywords": [], 15 | "author": "Mikeal Rogers (http://www.mikealrogers.com)", 16 | "license": "Apache-2.0", 17 | "devDependencies": { 18 | "husky": "^0.14.3", 19 | "standard": "^10.0.3", 20 | "semantic-release": "^8.0.3" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/mikeal/zcomponent.git" 25 | } 26 | } 27 | --------------------------------------------------------------------------------