├── .gitignore ├── .travis.yml ├── .zuul.yml ├── README.md ├── dist └── base-element.js ├── examples ├── angular.js ├── custom-element.js ├── ember.js ├── es6.js ├── list.js ├── nesting.js ├── react.js ├── server-side.js ├── standalone.js ├── virtual-dom.js └── webcomponent.js ├── index.js ├── package.json └── test ├── fixtures ├── button.js └── nested.js ├── index.js └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.12' 4 | before_install: 5 | - npm install npm -g 6 | before_script: 7 | - npm install 8 | script: npm run ci 9 | addons: 10 | sauce_connect: true 11 | env: 12 | global: 13 | - secure: hluRrXiBDo5jS+H6vp6LqFpuBOoM4E+G7knBYv8VoZ3C48sJsBpDFtKzfLBRBjwCS8+yrHlQT1I6l+1ESGwBZaSD7snn3rX+m7tv1FEoB/hlGLXCpGQAdRXVM9RmSbMf4MvaG4VQIIZzGIsu8zkv3AiD9lg5tb9/IQ+OgeKOZ04= 14 | - secure: Oyo6tyhBv5xaKYmHdVboMO4TL1Wvb5jZ3kyA7vXndyA8aZBqFqSh9PRuu9JDXNK+MPvvYyZLjSB3xiXQAAmY01W4f4dRiKmdBapr+hVAWUtAHcxJIFYpLnUcxPgin8ZvkIEBSRSEVyTfJZEp3Lcrf0DLfiGXK5uRqTdI3DNwYUM= 15 | -------------------------------------------------------------------------------- /.zuul.yml: -------------------------------------------------------------------------------- 1 | ui: tape 2 | browsers: 3 | - name: chrome 4 | version: latest 5 | - name: firefox 6 | version: latest 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # base-element 2 | An element authoring library for creating standalone and performant elements. 3 | 4 | [![build status](https://secure.travis-ci.org/shama/base-element.svg)](https://travis-ci.org/shama/base-element) 5 | [![NPM version](https://badge.fury.io/js/base-element.svg)](https://badge.fury.io/js/base-element) 6 | [![experimental](http://hughsk.github.io/stability-badges/dist/experimental.svg)](http://github.com/hughsk/stability-badges) 7 | 8 | [![Sauce Test Status](https://saucelabs.com/browser-matrix/shama.svg)](https://saucelabs.com/u/shama) 9 | 10 | View [this example List element](https://github.com/shama/base-element/blob/master/examples/list.js) in use with: 11 | * [Angular](https://github.com/shama/base-element/blob/master/examples/angular.js) 12 | * [React](https://github.com/shama/base-element/blob/master/examples/react.js) 13 | * [Ember](https://github.com/shama/base-element/blob/master/examples/ember.js) 14 | * [Web Component](https://github.com/shama/base-element/blob/master/examples/webcomponent.js) 15 | * [virtual-dom](https://github.com/shama/base-element/blob/master/examples/virtual-dom.js) 16 | * [or just standalone](https://github.com/shama/base-element/blob/master/examples/standalone.js) 17 | 18 | Or other examples: 19 | * [with ES6](https://github.com/shama/base-element/blob/master/examples/es6.js) 20 | * [nested architecture](https://github.com/shama/base-element/blob/master/examples/nesting.js) 21 | * [server side rendering](https://github.com/shama/base-element/blob/master/examples/server-side.js) 22 | 23 | ## example usage 24 | You can construct your element API however you choose. A way that I prefer is 25 | by inheriting prototypes: 26 | 27 | ```js 28 | var BaseElement = require('base-element') 29 | 30 | function Bear () { 31 | BaseElement.call(this) 32 | } 33 | Bear.prototype = Object.create(BaseElement.prototype) 34 | // Or inherits(Bear, BaseElement) 35 | // Or class Bear extends BaseElement 36 | ``` 37 | 38 | Then build your elements: 39 | 40 | ```js 41 | Bear.prototype.render = function (typeOfBear) { 42 | // Create a virtual DOM tree 43 | var vtree = this.html('div.bear', ['Im a ' + typeOfBear + '!']) 44 | // Call afterRender with your vtree when returning your vtree 45 | return this.afterRender(vtree) 46 | } 47 | ``` 48 | 49 | ### Prefer just functions? 50 | If you prefer just functions, an alternative interface is available: 51 | 52 | ```js 53 | var createElement = require('base-element') 54 | 55 | // Create an element on a parent 56 | var el = createElement(document.body) 57 | el.render(function () { 58 | // Render a button upon clicked will alert 59 | return el.html('button', { 60 | onclick: function (e) { 61 | window.alert(e.target.innerText + ' button was clicked') 62 | } 63 | }, 'click me') 64 | }) 65 | ``` 66 | 67 | ### data down, events up 68 | DOMs work best (in the opinion of myself and many) when data goes down 69 | and event (or actions) go up. 70 | 71 | A simple example is a button element that changes when clicked. **How** it 72 | changes is up to the element but **what** it changes to is up to the user. 73 | 74 | This is our Button element: 75 | 76 | ```js 77 | var BaseElement = require('base-element') 78 | 79 | function Button () { 80 | BaseElement.call(this) 81 | } 82 | Button.prototype = Object.create(BaseElement.prototype) 83 | // Or inherits(Button, BaseElement) 84 | // Or class Button extends BaseElement 85 | 86 | Button.prototype.render = function (label) { 87 | var self = this 88 | // The "label" data is coming down 89 | var vtree = this.html('button', { 90 | onclick: function (event) { 91 | // We send the "clicked" event up 92 | self.send('clicked', event.target) 93 | } 94 | }, label) 95 | return this.afterRender(vtree) 96 | } 97 | ``` 98 | 99 | and this is the user's implementation, creates a button and on every click it 100 | changes to a random number: 101 | 102 | ```js 103 | var button = require('your-button')() 104 | button.addEventListener('clicked', function (node) { 105 | button.render('button label ' + Math.random()) 106 | }) 107 | ``` 108 | 109 | ### nested architecture 110 | Elements created using `base-element` are intended on being shared and extended 111 | by others. Each element should not require an additional library/framework to 112 | run it or be injected into it in order to be ran. Elements should be standalone. 113 | 114 | For example if you create an `input-box` element and published on npm: 115 | 116 | ```js 117 | var BaseElement = require('base-element') 118 | function InputBox (el) { 119 | BaseElement.call(this, el) 120 | } 121 | InputBox.prototype = Object.create(BaseElement.prototype) 122 | module.exports = InputBox 123 | 124 | InputBox.prototype.render = function (value) { 125 | // Builds an Enter your email inside this
157 | this.html('label', data.label || 'Enter your email'), 158 | // Call the InputBox's render 159 | InputBox.prototype.render(data.value) 160 | ]) 161 | // Return the virtual DOM tree 162 | return this.afterRender(vtree) 163 | } 164 | ``` 165 | 166 | Both `input-box` and `email-input` can be ran on their own. When `input-box` 167 | updates over time, `email-input` can stay on a previous version until an upgrade 168 | can be made. 169 | 170 | ## install 171 | 172 | ### npm with browserify, webpack, etc 173 | 174 | * `npm install base-element` 175 | * `var BaseElement = require('base-element')` 176 | 177 | ### standalone 178 | 179 | * copy/download/etc [dist/base-element.js](https://github.com/shama/base-element/blob/master/dist/base-element.js) 180 | * `` 181 | * `` 182 | 183 | ## api 184 | 185 | ### `var element = new BaseElement([attachTo])` 186 | `attachTo` is a DOM element you want to append to such as `document.body` 187 | 188 | By default, the element will not attach itself to a parent node. This is useful 189 | for handling the rendering on your own. 190 | 191 | ### `element.send(name[, params...])` 192 | Sends an event up with a given `name` and `params`. 193 | 194 | ### `element.addEventListener(name, function)` 195 | Register an event listener for a given name: 196 | 197 | ```js 198 | element.addEventListener('clicked', function (params) {}) 199 | ``` 200 | 201 | ### `element.afterRender([params...])` 202 | This method needs to be called when returning a constructed virtual tree. It 203 | will detect if we are at the top of the render tree and perform the DOM diff 204 | and patching. 205 | 206 | ```js 207 | Button.prototype.render = function (data) { 208 | var vtree = this.html('button') 209 | return this.afterRender(vtree) 210 | } 211 | ``` 212 | 213 | ### `element.html(tag[, options], value)` 214 | A convenience wrapper for creating virtual-hyperscript nodes, i.e.: 215 | 216 | ```js 217 | var h = require('virtual-dom/h') 218 | var vtree = h('div', 'Testing') 219 | 220 | // is the same as 221 | var vtree = this.html('div', 'Testing') 222 | ``` 223 | 224 | ### `element.toString([data...])` 225 | For rendering your element as a string of HTML. `data` is any initial data 226 | passed to your `render` function. 227 | 228 | ### `element.element` 229 | The root DOM node the virtual tree resides on. 230 | 231 | ### `element.vtree` 232 | The current virtual DOM tree of the base element. 233 | 234 | ### default events 235 | `load` and `unload` events will be sent by default if your top level element 236 | registers `this` as it's properties: 237 | 238 | ```js 239 | var BaseElement = require('base-element') 240 | function Button(el) { 241 | BaseElement.call(this, el) 242 | this.addEventListener('load', function (node) { 243 | console.log(node + ' has loaded!') 244 | }) 245 | this.addEventListener('unload', function (node) { 246 | console.log(node + ' has unloaded!') 247 | }) 248 | } 249 | Button.prototype.render = function (data) { 250 | // The top level element is provided with `this`, events will be fired 251 | return this.afterRender(this.html('button', this, 'click me')) 252 | } 253 | ``` 254 | 255 | ## similar projects 256 | 257 | * [vel](https://github.com/yoshuawuyts/vel) 258 | create and render virtual-dom elements with ease 259 | 260 | # license 261 | (c) 2015 Kyle Robinson Young. MIT License 262 | -------------------------------------------------------------------------------- /dist/base-element.js: -------------------------------------------------------------------------------- 1 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.BaseElement = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o'; 163 | } 164 | 165 | function tagContent(node) { 166 | var innerHTML = node.properties.innerHTML; 167 | if (innerHTML != null) return innerHTML; 168 | else { 169 | var ret = ''; 170 | if (node.children && node.children.length) { 171 | for (var i = 0, l = node.children.length; i'; 183 | } 184 | },{"./create-attribute":3,"./void-elements":14,"escape-html":5,"param-case":11,"virtual-dom/virtual-hyperscript/hooks/attribute-hook":31,"virtual-dom/virtual-hyperscript/hooks/soft-set-hook":33,"virtual-dom/vnode/is-thunk":37,"virtual-dom/vnode/is-vnode":39,"virtual-dom/vnode/is-vtext":40,"virtual-dom/vnode/is-widget":41,"xtend":12}],5:[function(require,module,exports){ 185 | /** 186 | * Escape special characters in the given string of html. 187 | * 188 | * @param {String} html 189 | * @return {String} 190 | * @api private 191 | */ 192 | 193 | module.exports = function(html) { 194 | return String(html) 195 | .replace(/&/g, '&') 196 | .replace(/"/g, '"') 197 | .replace(/'/g, ''') 198 | .replace(//g, '>'); 200 | } 201 | 202 | },{}],6:[function(require,module,exports){ 203 | /** 204 | * Special language-specific overrides. 205 | * 206 | * Source: ftp://ftp.unicode.org/Public/UCD/latest/ucd/SpecialCasing.txt 207 | * 208 | * @type {Object} 209 | */ 210 | var LANGUAGES = { 211 | tr: { 212 | regexp: /\u0130|\u0049|\u0049\u0307/g, 213 | map: { 214 | '\u0130': '\u0069', 215 | '\u0049': '\u0131', 216 | '\u0049\u0307': '\u0069' 217 | } 218 | }, 219 | az: { 220 | regexp: /[\u0130]/g, 221 | map: { 222 | '\u0130': '\u0069', 223 | '\u0049': '\u0131', 224 | '\u0049\u0307': '\u0069' 225 | } 226 | }, 227 | lt: { 228 | regexp: /[\u0049\u004A\u012E\u00CC\u00CD\u0128]/g, 229 | map: { 230 | '\u0049': '\u0069\u0307', 231 | '\u004A': '\u006A\u0307', 232 | '\u012E': '\u012F\u0307', 233 | '\u00CC': '\u0069\u0307\u0300', 234 | '\u00CD': '\u0069\u0307\u0301', 235 | '\u0128': '\u0069\u0307\u0303' 236 | } 237 | } 238 | } 239 | 240 | /** 241 | * Lowercase a string. 242 | * 243 | * @param {String} str 244 | * @return {String} 245 | */ 246 | module.exports = function (str, locale) { 247 | var lang = LANGUAGES[locale] 248 | 249 | str = str == null ? '' : String(str) 250 | 251 | if (lang) { 252 | str = str.replace(lang.regexp, function (m) { return lang.map[m] }) 253 | } 254 | 255 | return str.toLowerCase() 256 | } 257 | 258 | },{}],7:[function(require,module,exports){ 259 | var lowerCase = require('lower-case') 260 | 261 | var NON_WORD_REGEXP = require('./vendor/non-word-regexp') 262 | var CAMEL_CASE_REGEXP = require('./vendor/camel-case-regexp') 263 | var TRAILING_DIGIT_REGEXP = require('./vendor/trailing-digit-regexp') 264 | 265 | /** 266 | * Sentence case a string. 267 | * 268 | * @param {String} str 269 | * @param {String} locale 270 | * @param {String} replacement 271 | * @return {String} 272 | */ 273 | module.exports = function (str, locale, replacement) { 274 | if (str == null) { 275 | return '' 276 | } 277 | 278 | replacement = replacement || ' ' 279 | 280 | function replace (match, index, string) { 281 | if (index === 0 || index === (string.length - match.length)) { 282 | return '' 283 | } 284 | 285 | return replacement 286 | } 287 | 288 | str = String(str) 289 | // Support camel case ("camelCase" -> "camel Case"). 290 | .replace(CAMEL_CASE_REGEXP, '$1 $2') 291 | // Support digit groups ("test2012" -> "test 2012"). 292 | .replace(TRAILING_DIGIT_REGEXP, '$1 $2') 293 | // Remove all non-word characters and replace with a single space. 294 | .replace(NON_WORD_REGEXP, replace) 295 | 296 | // Lower case the entire string. 297 | return lowerCase(str, locale) 298 | } 299 | 300 | },{"./vendor/camel-case-regexp":8,"./vendor/non-word-regexp":9,"./vendor/trailing-digit-regexp":10,"lower-case":6}],8:[function(require,module,exports){ 301 | module.exports = /([\u0061-\u007A\u00B5\u00DF-\u00F6\u00F8-\u00FF\u0101\u0103\u0105\u0107\u0109\u010B\u010D\u010F\u0111\u0113\u0115\u0117\u0119\u011B\u011D\u011F\u0121\u0123\u0125\u0127\u0129\u012B\u012D\u012F\u0131\u0133\u0135\u0137\u0138\u013A\u013C\u013E\u0140\u0142\u0144\u0146\u0148\u0149\u014B\u014D\u014F\u0151\u0153\u0155\u0157\u0159\u015B\u015D\u015F\u0161\u0163\u0165\u0167\u0169\u016B\u016D\u016F\u0171\u0173\u0175\u0177\u017A\u017C\u017E-\u0180\u0183\u0185\u0188\u018C\u018D\u0192\u0195\u0199-\u019B\u019E\u01A1\u01A3\u01A5\u01A8\u01AA\u01AB\u01AD\u01B0\u01B4\u01B6\u01B9\u01BA\u01BD-\u01BF\u01C6\u01C9\u01CC\u01CE\u01D0\u01D2\u01D4\u01D6\u01D8\u01DA\u01DC\u01DD\u01DF\u01E1\u01E3\u01E5\u01E7\u01E9\u01EB\u01ED\u01EF\u01F0\u01F3\u01F5\u01F9\u01FB\u01FD\u01FF\u0201\u0203\u0205\u0207\u0209\u020B\u020D\u020F\u0211\u0213\u0215\u0217\u0219\u021B\u021D\u021F\u0221\u0223\u0225\u0227\u0229\u022B\u022D\u022F\u0231\u0233-\u0239\u023C\u023F\u0240\u0242\u0247\u0249\u024B\u024D\u024F-\u0293\u0295-\u02AF\u0371\u0373\u0377\u037B-\u037D\u0390\u03AC-\u03CE\u03D0\u03D1\u03D5-\u03D7\u03D9\u03DB\u03DD\u03DF\u03E1\u03E3\u03E5\u03E7\u03E9\u03EB\u03ED\u03EF-\u03F3\u03F5\u03F8\u03FB\u03FC\u0430-\u045F\u0461\u0463\u0465\u0467\u0469\u046B\u046D\u046F\u0471\u0473\u0475\u0477\u0479\u047B\u047D\u047F\u0481\u048B\u048D\u048F\u0491\u0493\u0495\u0497\u0499\u049B\u049D\u049F\u04A1\u04A3\u04A5\u04A7\u04A9\u04AB\u04AD\u04AF\u04B1\u04B3\u04B5\u04B7\u04B9\u04BB\u04BD\u04BF\u04C2\u04C4\u04C6\u04C8\u04CA\u04CC\u04CE\u04CF\u04D1\u04D3\u04D5\u04D7\u04D9\u04DB\u04DD\u04DF\u04E1\u04E3\u04E5\u04E7\u04E9\u04EB\u04ED\u04EF\u04F1\u04F3\u04F5\u04F7\u04F9\u04FB\u04FD\u04FF\u0501\u0503\u0505\u0507\u0509\u050B\u050D\u050F\u0511\u0513\u0515\u0517\u0519\u051B\u051D\u051F\u0521\u0523\u0525\u0527\u0561-\u0587\u1D00-\u1D2B\u1D6B-\u1D77\u1D79-\u1D9A\u1E01\u1E03\u1E05\u1E07\u1E09\u1E0B\u1E0D\u1E0F\u1E11\u1E13\u1E15\u1E17\u1E19\u1E1B\u1E1D\u1E1F\u1E21\u1E23\u1E25\u1E27\u1E29\u1E2B\u1E2D\u1E2F\u1E31\u1E33\u1E35\u1E37\u1E39\u1E3B\u1E3D\u1E3F\u1E41\u1E43\u1E45\u1E47\u1E49\u1E4B\u1E4D\u1E4F\u1E51\u1E53\u1E55\u1E57\u1E59\u1E5B\u1E5D\u1E5F\u1E61\u1E63\u1E65\u1E67\u1E69\u1E6B\u1E6D\u1E6F\u1E71\u1E73\u1E75\u1E77\u1E79\u1E7B\u1E7D\u1E7F\u1E81\u1E83\u1E85\u1E87\u1E89\u1E8B\u1E8D\u1E8F\u1E91\u1E93\u1E95-\u1E9D\u1E9F\u1EA1\u1EA3\u1EA5\u1EA7\u1EA9\u1EAB\u1EAD\u1EAF\u1EB1\u1EB3\u1EB5\u1EB7\u1EB9\u1EBB\u1EBD\u1EBF\u1EC1\u1EC3\u1EC5\u1EC7\u1EC9\u1ECB\u1ECD\u1ECF\u1ED1\u1ED3\u1ED5\u1ED7\u1ED9\u1EDB\u1EDD\u1EDF\u1EE1\u1EE3\u1EE5\u1EE7\u1EE9\u1EEB\u1EED\u1EEF\u1EF1\u1EF3\u1EF5\u1EF7\u1EF9\u1EFB\u1EFD\u1EFF-\u1F07\u1F10-\u1F15\u1F20-\u1F27\u1F30-\u1F37\u1F40-\u1F45\u1F50-\u1F57\u1F60-\u1F67\u1F70-\u1F7D\u1F80-\u1F87\u1F90-\u1F97\u1FA0-\u1FA7\u1FB0-\u1FB4\u1FB6\u1FB7\u1FBE\u1FC2-\u1FC4\u1FC6\u1FC7\u1FD0-\u1FD3\u1FD6\u1FD7\u1FE0-\u1FE7\u1FF2-\u1FF4\u1FF6\u1FF7\u210A\u210E\u210F\u2113\u212F\u2134\u2139\u213C\u213D\u2146-\u2149\u214E\u2184\u2C30-\u2C5E\u2C61\u2C65\u2C66\u2C68\u2C6A\u2C6C\u2C71\u2C73\u2C74\u2C76-\u2C7B\u2C81\u2C83\u2C85\u2C87\u2C89\u2C8B\u2C8D\u2C8F\u2C91\u2C93\u2C95\u2C97\u2C99\u2C9B\u2C9D\u2C9F\u2CA1\u2CA3\u2CA5\u2CA7\u2CA9\u2CAB\u2CAD\u2CAF\u2CB1\u2CB3\u2CB5\u2CB7\u2CB9\u2CBB\u2CBD\u2CBF\u2CC1\u2CC3\u2CC5\u2CC7\u2CC9\u2CCB\u2CCD\u2CCF\u2CD1\u2CD3\u2CD5\u2CD7\u2CD9\u2CDB\u2CDD\u2CDF\u2CE1\u2CE3\u2CE4\u2CEC\u2CEE\u2CF3\u2D00-\u2D25\u2D27\u2D2D\uA641\uA643\uA645\uA647\uA649\uA64B\uA64D\uA64F\uA651\uA653\uA655\uA657\uA659\uA65B\uA65D\uA65F\uA661\uA663\uA665\uA667\uA669\uA66B\uA66D\uA681\uA683\uA685\uA687\uA689\uA68B\uA68D\uA68F\uA691\uA693\uA695\uA697\uA723\uA725\uA727\uA729\uA72B\uA72D\uA72F-\uA731\uA733\uA735\uA737\uA739\uA73B\uA73D\uA73F\uA741\uA743\uA745\uA747\uA749\uA74B\uA74D\uA74F\uA751\uA753\uA755\uA757\uA759\uA75B\uA75D\uA75F\uA761\uA763\uA765\uA767\uA769\uA76B\uA76D\uA76F\uA771-\uA778\uA77A\uA77C\uA77F\uA781\uA783\uA785\uA787\uA78C\uA78E\uA791\uA793\uA7A1\uA7A3\uA7A5\uA7A7\uA7A9\uA7FA\uFB00-\uFB06\uFB13-\uFB17\uFF41-\uFF5A])([\u0041-\u005A\u00C0-\u00D6\u00D8-\u00DE\u0100\u0102\u0104\u0106\u0108\u010A\u010C\u010E\u0110\u0112\u0114\u0116\u0118\u011A\u011C\u011E\u0120\u0122\u0124\u0126\u0128\u012A\u012C\u012E\u0130\u0132\u0134\u0136\u0139\u013B\u013D\u013F\u0141\u0143\u0145\u0147\u014A\u014C\u014E\u0150\u0152\u0154\u0156\u0158\u015A\u015C\u015E\u0160\u0162\u0164\u0166\u0168\u016A\u016C\u016E\u0170\u0172\u0174\u0176\u0178\u0179\u017B\u017D\u0181\u0182\u0184\u0186\u0187\u0189-\u018B\u018E-\u0191\u0193\u0194\u0196-\u0198\u019C\u019D\u019F\u01A0\u01A2\u01A4\u01A6\u01A7\u01A9\u01AC\u01AE\u01AF\u01B1-\u01B3\u01B5\u01B7\u01B8\u01BC\u01C4\u01C7\u01CA\u01CD\u01CF\u01D1\u01D3\u01D5\u01D7\u01D9\u01DB\u01DE\u01E0\u01E2\u01E4\u01E6\u01E8\u01EA\u01EC\u01EE\u01F1\u01F4\u01F6-\u01F8\u01FA\u01FC\u01FE\u0200\u0202\u0204\u0206\u0208\u020A\u020C\u020E\u0210\u0212\u0214\u0216\u0218\u021A\u021C\u021E\u0220\u0222\u0224\u0226\u0228\u022A\u022C\u022E\u0230\u0232\u023A\u023B\u023D\u023E\u0241\u0243-\u0246\u0248\u024A\u024C\u024E\u0370\u0372\u0376\u0386\u0388-\u038A\u038C\u038E\u038F\u0391-\u03A1\u03A3-\u03AB\u03CF\u03D2-\u03D4\u03D8\u03DA\u03DC\u03DE\u03E0\u03E2\u03E4\u03E6\u03E8\u03EA\u03EC\u03EE\u03F4\u03F7\u03F9\u03FA\u03FD-\u042F\u0460\u0462\u0464\u0466\u0468\u046A\u046C\u046E\u0470\u0472\u0474\u0476\u0478\u047A\u047C\u047E\u0480\u048A\u048C\u048E\u0490\u0492\u0494\u0496\u0498\u049A\u049C\u049E\u04A0\u04A2\u04A4\u04A6\u04A8\u04AA\u04AC\u04AE\u04B0\u04B2\u04B4\u04B6\u04B8\u04BA\u04BC\u04BE\u04C0\u04C1\u04C3\u04C5\u04C7\u04C9\u04CB\u04CD\u04D0\u04D2\u04D4\u04D6\u04D8\u04DA\u04DC\u04DE\u04E0\u04E2\u04E4\u04E6\u04E8\u04EA\u04EC\u04EE\u04F0\u04F2\u04F4\u04F6\u04F8\u04FA\u04FC\u04FE\u0500\u0502\u0504\u0506\u0508\u050A\u050C\u050E\u0510\u0512\u0514\u0516\u0518\u051A\u051C\u051E\u0520\u0522\u0524\u0526\u0531-\u0556\u10A0-\u10C5\u10C7\u10CD\u1E00\u1E02\u1E04\u1E06\u1E08\u1E0A\u1E0C\u1E0E\u1E10\u1E12\u1E14\u1E16\u1E18\u1E1A\u1E1C\u1E1E\u1E20\u1E22\u1E24\u1E26\u1E28\u1E2A\u1E2C\u1E2E\u1E30\u1E32\u1E34\u1E36\u1E38\u1E3A\u1E3C\u1E3E\u1E40\u1E42\u1E44\u1E46\u1E48\u1E4A\u1E4C\u1E4E\u1E50\u1E52\u1E54\u1E56\u1E58\u1E5A\u1E5C\u1E5E\u1E60\u1E62\u1E64\u1E66\u1E68\u1E6A\u1E6C\u1E6E\u1E70\u1E72\u1E74\u1E76\u1E78\u1E7A\u1E7C\u1E7E\u1E80\u1E82\u1E84\u1E86\u1E88\u1E8A\u1E8C\u1E8E\u1E90\u1E92\u1E94\u1E9E\u1EA0\u1EA2\u1EA4\u1EA6\u1EA8\u1EAA\u1EAC\u1EAE\u1EB0\u1EB2\u1EB4\u1EB6\u1EB8\u1EBA\u1EBC\u1EBE\u1EC0\u1EC2\u1EC4\u1EC6\u1EC8\u1ECA\u1ECC\u1ECE\u1ED0\u1ED2\u1ED4\u1ED6\u1ED8\u1EDA\u1EDC\u1EDE\u1EE0\u1EE2\u1EE4\u1EE6\u1EE8\u1EEA\u1EEC\u1EEE\u1EF0\u1EF2\u1EF4\u1EF6\u1EF8\u1EFA\u1EFC\u1EFE\u1F08-\u1F0F\u1F18-\u1F1D\u1F28-\u1F2F\u1F38-\u1F3F\u1F48-\u1F4D\u1F59\u1F5B\u1F5D\u1F5F\u1F68-\u1F6F\u1FB8-\u1FBB\u1FC8-\u1FCB\u1FD8-\u1FDB\u1FE8-\u1FEC\u1FF8-\u1FFB\u2102\u2107\u210B-\u210D\u2110-\u2112\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u2130-\u2133\u213E\u213F\u2145\u2183\u2C00-\u2C2E\u2C60\u2C62-\u2C64\u2C67\u2C69\u2C6B\u2C6D-\u2C70\u2C72\u2C75\u2C7E-\u2C80\u2C82\u2C84\u2C86\u2C88\u2C8A\u2C8C\u2C8E\u2C90\u2C92\u2C94\u2C96\u2C98\u2C9A\u2C9C\u2C9E\u2CA0\u2CA2\u2CA4\u2CA6\u2CA8\u2CAA\u2CAC\u2CAE\u2CB0\u2CB2\u2CB4\u2CB6\u2CB8\u2CBA\u2CBC\u2CBE\u2CC0\u2CC2\u2CC4\u2CC6\u2CC8\u2CCA\u2CCC\u2CCE\u2CD0\u2CD2\u2CD4\u2CD6\u2CD8\u2CDA\u2CDC\u2CDE\u2CE0\u2CE2\u2CEB\u2CED\u2CF2\uA640\uA642\uA644\uA646\uA648\uA64A\uA64C\uA64E\uA650\uA652\uA654\uA656\uA658\uA65A\uA65C\uA65E\uA660\uA662\uA664\uA666\uA668\uA66A\uA66C\uA680\uA682\uA684\uA686\uA688\uA68A\uA68C\uA68E\uA690\uA692\uA694\uA696\uA722\uA724\uA726\uA728\uA72A\uA72C\uA72E\uA732\uA734\uA736\uA738\uA73A\uA73C\uA73E\uA740\uA742\uA744\uA746\uA748\uA74A\uA74C\uA74E\uA750\uA752\uA754\uA756\uA758\uA75A\uA75C\uA75E\uA760\uA762\uA764\uA766\uA768\uA76A\uA76C\uA76E\uA779\uA77B\uA77D\uA77E\uA780\uA782\uA784\uA786\uA78B\uA78D\uA790\uA792\uA7A0\uA7A2\uA7A4\uA7A6\uA7A8\uA7AA\uFF21-\uFF3A\u0030-\u0039\u00B2\u00B3\u00B9\u00BC-\u00BE\u0660-\u0669\u06F0-\u06F9\u07C0-\u07C9\u0966-\u096F\u09E6-\u09EF\u09F4-\u09F9\u0A66-\u0A6F\u0AE6-\u0AEF\u0B66-\u0B6F\u0B72-\u0B77\u0BE6-\u0BF2\u0C66-\u0C6F\u0C78-\u0C7E\u0CE6-\u0CEF\u0D66-\u0D75\u0E50-\u0E59\u0ED0-\u0ED9\u0F20-\u0F33\u1040-\u1049\u1090-\u1099\u1369-\u137C\u16EE-\u16F0\u17E0-\u17E9\u17F0-\u17F9\u1810-\u1819\u1946-\u194F\u19D0-\u19DA\u1A80-\u1A89\u1A90-\u1A99\u1B50-\u1B59\u1BB0-\u1BB9\u1C40-\u1C49\u1C50-\u1C59\u2070\u2074-\u2079\u2080-\u2089\u2150-\u2182\u2185-\u2189\u2460-\u249B\u24EA-\u24FF\u2776-\u2793\u2CFD\u3007\u3021-\u3029\u3038-\u303A\u3192-\u3195\u3220-\u3229\u3248-\u324F\u3251-\u325F\u3280-\u3289\u32B1-\u32BF\uA620-\uA629\uA6E6-\uA6EF\uA830-\uA835\uA8D0-\uA8D9\uA900-\uA909\uA9D0-\uA9D9\uAA50-\uAA59\uABF0-\uABF9\uFF10-\uFF19])/g 302 | 303 | },{}],9:[function(require,module,exports){ 304 | module.exports = /[^\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC\u0030-\u0039\u00B2\u00B3\u00B9\u00BC-\u00BE\u0660-\u0669\u06F0-\u06F9\u07C0-\u07C9\u0966-\u096F\u09E6-\u09EF\u09F4-\u09F9\u0A66-\u0A6F\u0AE6-\u0AEF\u0B66-\u0B6F\u0B72-\u0B77\u0BE6-\u0BF2\u0C66-\u0C6F\u0C78-\u0C7E\u0CE6-\u0CEF\u0D66-\u0D75\u0E50-\u0E59\u0ED0-\u0ED9\u0F20-\u0F33\u1040-\u1049\u1090-\u1099\u1369-\u137C\u16EE-\u16F0\u17E0-\u17E9\u17F0-\u17F9\u1810-\u1819\u1946-\u194F\u19D0-\u19DA\u1A80-\u1A89\u1A90-\u1A99\u1B50-\u1B59\u1BB0-\u1BB9\u1C40-\u1C49\u1C50-\u1C59\u2070\u2074-\u2079\u2080-\u2089\u2150-\u2182\u2185-\u2189\u2460-\u249B\u24EA-\u24FF\u2776-\u2793\u2CFD\u3007\u3021-\u3029\u3038-\u303A\u3192-\u3195\u3220-\u3229\u3248-\u324F\u3251-\u325F\u3280-\u3289\u32B1-\u32BF\uA620-\uA629\uA6E6-\uA6EF\uA830-\uA835\uA8D0-\uA8D9\uA900-\uA909\uA9D0-\uA9D9\uAA50-\uAA59\uABF0-\uABF9\uFF10-\uFF19]+/g 305 | 306 | },{}],10:[function(require,module,exports){ 307 | module.exports = /([\u0030-\u0039\u00B2\u00B3\u00B9\u00BC-\u00BE\u0660-\u0669\u06F0-\u06F9\u07C0-\u07C9\u0966-\u096F\u09E6-\u09EF\u09F4-\u09F9\u0A66-\u0A6F\u0AE6-\u0AEF\u0B66-\u0B6F\u0B72-\u0B77\u0BE6-\u0BF2\u0C66-\u0C6F\u0C78-\u0C7E\u0CE6-\u0CEF\u0D66-\u0D75\u0E50-\u0E59\u0ED0-\u0ED9\u0F20-\u0F33\u1040-\u1049\u1090-\u1099\u1369-\u137C\u16EE-\u16F0\u17E0-\u17E9\u17F0-\u17F9\u1810-\u1819\u1946-\u194F\u19D0-\u19DA\u1A80-\u1A89\u1A90-\u1A99\u1B50-\u1B59\u1BB0-\u1BB9\u1C40-\u1C49\u1C50-\u1C59\u2070\u2074-\u2079\u2080-\u2089\u2150-\u2182\u2185-\u2189\u2460-\u249B\u24EA-\u24FF\u2776-\u2793\u2CFD\u3007\u3021-\u3029\u3038-\u303A\u3192-\u3195\u3220-\u3229\u3248-\u324F\u3251-\u325F\u3280-\u3289\u32B1-\u32BF\uA620-\uA629\uA6E6-\uA6EF\uA830-\uA835\uA8D0-\uA8D9\uA900-\uA909\uA9D0-\uA9D9\uAA50-\uAA59\uABF0-\uABF9\uFF10-\uFF19])([^\u0030-\u0039\u00B2\u00B3\u00B9\u00BC-\u00BE\u0660-\u0669\u06F0-\u06F9\u07C0-\u07C9\u0966-\u096F\u09E6-\u09EF\u09F4-\u09F9\u0A66-\u0A6F\u0AE6-\u0AEF\u0B66-\u0B6F\u0B72-\u0B77\u0BE6-\u0BF2\u0C66-\u0C6F\u0C78-\u0C7E\u0CE6-\u0CEF\u0D66-\u0D75\u0E50-\u0E59\u0ED0-\u0ED9\u0F20-\u0F33\u1040-\u1049\u1090-\u1099\u1369-\u137C\u16EE-\u16F0\u17E0-\u17E9\u17F0-\u17F9\u1810-\u1819\u1946-\u194F\u19D0-\u19DA\u1A80-\u1A89\u1A90-\u1A99\u1B50-\u1B59\u1BB0-\u1BB9\u1C40-\u1C49\u1C50-\u1C59\u2070\u2074-\u2079\u2080-\u2089\u2150-\u2182\u2185-\u2189\u2460-\u249B\u24EA-\u24FF\u2776-\u2793\u2CFD\u3007\u3021-\u3029\u3038-\u303A\u3192-\u3195\u3220-\u3229\u3248-\u324F\u3251-\u325F\u3280-\u3289\u32B1-\u32BF\uA620-\uA629\uA6E6-\uA6EF\uA830-\uA835\uA8D0-\uA8D9\uA900-\uA909\uA9D0-\uA9D9\uAA50-\uAA59\uABF0-\uABF9\uFF10-\uFF19])/g 308 | 309 | },{}],11:[function(require,module,exports){ 310 | var sentenceCase = require('sentence-case'); 311 | 312 | /** 313 | * Param case a string. 314 | * 315 | * @param {String} string 316 | * @param {String} [locale] 317 | * @return {String} 318 | */ 319 | module.exports = function (string, locale) { 320 | return sentenceCase(string, locale, '-'); 321 | }; 322 | 323 | },{"sentence-case":7}],12:[function(require,module,exports){ 324 | module.exports = extend 325 | 326 | function extend() { 327 | var target = {} 328 | 329 | for (var i = 0; i < arguments.length; i++) { 330 | var source = arguments[i] 331 | 332 | for (var key in source) { 333 | if (source.hasOwnProperty(key)) { 334 | target[key] = source[key] 335 | } 336 | } 337 | } 338 | 339 | return target 340 | } 341 | 342 | },{}],13:[function(require,module,exports){ 343 | /** 344 | * Attribute types. 345 | */ 346 | 347 | var types = { 348 | BOOLEAN: 1, 349 | OVERLOADED_BOOLEAN: 2 350 | }; 351 | 352 | /** 353 | * Properties. 354 | * 355 | * Taken from https://github.com/facebook/react/blob/847357e42e5267b04dd6e297219eaa125ab2f9f4/src/browser/ui/dom/HTMLDOMPropertyConfig.js 356 | * 357 | */ 358 | 359 | var properties = { 360 | /** 361 | * Standard Properties 362 | */ 363 | accept: true, 364 | acceptCharset: true, 365 | accessKey: true, 366 | action: true, 367 | allowFullScreen: types.BOOLEAN, 368 | allowTransparency: true, 369 | alt: true, 370 | async: types.BOOLEAN, 371 | autocomplete: true, 372 | autofocus: types.BOOLEAN, 373 | autoplay: types.BOOLEAN, 374 | cellPadding: true, 375 | cellSpacing: true, 376 | charset: true, 377 | checked: types.BOOLEAN, 378 | classID: true, 379 | className: true, 380 | cols: true, 381 | colSpan: true, 382 | content: true, 383 | contentEditable: true, 384 | contextMenu: true, 385 | controls: types.BOOLEAN, 386 | coords: true, 387 | crossOrigin: true, 388 | data: true, // For `` acts as `src`. 389 | dateTime: true, 390 | defer: types.BOOLEAN, 391 | dir: true, 392 | disabled: types.BOOLEAN, 393 | download: types.OVERLOADED_BOOLEAN, 394 | draggable: true, 395 | enctype: true, 396 | form: true, 397 | formAction: true, 398 | formEncType: true, 399 | formMethod: true, 400 | formNoValidate: types.BOOLEAN, 401 | formTarget: true, 402 | frameBorder: true, 403 | headers: true, 404 | height: true, 405 | hidden: types.BOOLEAN, 406 | href: true, 407 | hreflang: true, 408 | htmlFor: true, 409 | httpEquiv: true, 410 | icon: true, 411 | id: true, 412 | label: true, 413 | lang: true, 414 | list: true, 415 | loop: types.BOOLEAN, 416 | manifest: true, 417 | marginHeight: true, 418 | marginWidth: true, 419 | max: true, 420 | maxLength: true, 421 | media: true, 422 | mediaGroup: true, 423 | method: true, 424 | min: true, 425 | multiple: types.BOOLEAN, 426 | muted: types.BOOLEAN, 427 | name: true, 428 | noValidate: types.BOOLEAN, 429 | open: true, 430 | pattern: true, 431 | placeholder: true, 432 | poster: true, 433 | preload: true, 434 | radiogroup: true, 435 | readOnly: types.BOOLEAN, 436 | rel: true, 437 | required: types.BOOLEAN, 438 | role: true, 439 | rows: true, 440 | rowSpan: true, 441 | sandbox: true, 442 | scope: true, 443 | scrolling: true, 444 | seamless: types.BOOLEAN, 445 | selected: types.BOOLEAN, 446 | shape: true, 447 | size: true, 448 | sizes: true, 449 | span: true, 450 | spellcheck: true, 451 | src: true, 452 | srcdoc: true, 453 | srcset: true, 454 | start: true, 455 | step: true, 456 | style: true, 457 | tabIndex: true, 458 | target: true, 459 | title: true, 460 | type: true, 461 | useMap: true, 462 | value: true, 463 | width: true, 464 | wmode: true, 465 | 466 | /** 467 | * Non-standard Properties 468 | */ 469 | // autoCapitalize and autoCorrect are supported in Mobile Safari for 470 | // keyboard hints. 471 | autocapitalize: true, 472 | autocorrect: true, 473 | // itemProp, itemScope, itemType are for Microdata support. See 474 | // http://schema.org/docs/gs.html 475 | itemProp: true, 476 | itemScope: types.BOOLEAN, 477 | itemType: true, 478 | // property is supported for OpenGraph in meta tags. 479 | property: true 480 | }; 481 | 482 | /** 483 | * Properties to attributes mapping. 484 | * 485 | * The ones not here are simply converted to lower case. 486 | */ 487 | 488 | var attributeNames = { 489 | acceptCharset: 'accept-charset', 490 | className: 'class', 491 | htmlFor: 'for', 492 | httpEquiv: 'http-equiv' 493 | }; 494 | 495 | /** 496 | * Exports. 497 | */ 498 | 499 | module.exports = { 500 | attributeTypes: types, 501 | properties: properties, 502 | attributeNames: attributeNames 503 | }; 504 | },{}],14:[function(require,module,exports){ 505 | 506 | /** 507 | * Void elements. 508 | * 509 | * https://github.com/facebook/react/blob/v0.12.0/src/browser/ui/ReactDOMComponent.js#L99 510 | */ 511 | 512 | module.exports = { 513 | 'area': true, 514 | 'base': true, 515 | 'br': true, 516 | 'col': true, 517 | 'embed': true, 518 | 'hr': true, 519 | 'img': true, 520 | 'input': true, 521 | 'keygen': true, 522 | 'link': true, 523 | 'meta': true, 524 | 'param': true, 525 | 'source': true, 526 | 'track': true, 527 | 'wbr': true 528 | }; 529 | },{}],15:[function(require,module,exports){ 530 | var createElement = require("./vdom/create-element.js") 531 | 532 | module.exports = createElement 533 | 534 | },{"./vdom/create-element.js":26}],16:[function(require,module,exports){ 535 | var diff = require("./vtree/diff.js") 536 | 537 | module.exports = diff 538 | 539 | },{"./vtree/diff.js":47}],17:[function(require,module,exports){ 540 | var h = require("./virtual-hyperscript/index.js") 541 | 542 | module.exports = h 543 | 544 | },{"./virtual-hyperscript/index.js":34}],18:[function(require,module,exports){ 545 | /*! 546 | * Cross-Browser Split 1.1.1 547 | * Copyright 2007-2012 Steven Levithan 548 | * Available under the MIT License 549 | * ECMAScript compliant, uniform cross-browser split method 550 | */ 551 | 552 | /** 553 | * Splits a string into an array of strings using a regex or string separator. Matches of the 554 | * separator are not included in the result array. However, if `separator` is a regex that contains 555 | * capturing groups, backreferences are spliced into the result each time `separator` is matched. 556 | * Fixes browser bugs compared to the native `String.prototype.split` and can be used reliably 557 | * cross-browser. 558 | * @param {String} str String to split. 559 | * @param {RegExp|String} separator Regex or string to use for separating the string. 560 | * @param {Number} [limit] Maximum number of items to include in the result array. 561 | * @returns {Array} Array of substrings. 562 | * @example 563 | * 564 | * // Basic use 565 | * split('a b c d', ' '); 566 | * // -> ['a', 'b', 'c', 'd'] 567 | * 568 | * // With limit 569 | * split('a b c d', ' ', 2); 570 | * // -> ['a', 'b'] 571 | * 572 | * // Backreferences in result array 573 | * split('..word1 word2..', /([a-z]+)(\d+)/i); 574 | * // -> ['..', 'word', '1', ' ', 'word', '2', '..'] 575 | */ 576 | module.exports = (function split(undef) { 577 | 578 | var nativeSplit = String.prototype.split, 579 | compliantExecNpcg = /()??/.exec("")[1] === undef, 580 | // NPCG: nonparticipating capturing group 581 | self; 582 | 583 | self = function(str, separator, limit) { 584 | // If `separator` is not a regex, use `nativeSplit` 585 | if (Object.prototype.toString.call(separator) !== "[object RegExp]") { 586 | return nativeSplit.call(str, separator, limit); 587 | } 588 | var output = [], 589 | flags = (separator.ignoreCase ? "i" : "") + (separator.multiline ? "m" : "") + (separator.extended ? "x" : "") + // Proposed for ES6 590 | (separator.sticky ? "y" : ""), 591 | // Firefox 3+ 592 | lastLastIndex = 0, 593 | // Make `global` and avoid `lastIndex` issues by working with a copy 594 | separator = new RegExp(separator.source, flags + "g"), 595 | separator2, match, lastIndex, lastLength; 596 | str += ""; // Type-convert 597 | if (!compliantExecNpcg) { 598 | // Doesn't need flags gy, but they don't hurt 599 | separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags); 600 | } 601 | /* Values for `limit`, per the spec: 602 | * If undefined: 4294967295 // Math.pow(2, 32) - 1 603 | * If 0, Infinity, or NaN: 0 604 | * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296; 605 | * If negative number: 4294967296 - Math.floor(Math.abs(limit)) 606 | * If other: Type-convert, then use the above rules 607 | */ 608 | limit = limit === undef ? -1 >>> 0 : // Math.pow(2, 32) - 1 609 | limit >>> 0; // ToUint32(limit) 610 | while (match = separator.exec(str)) { 611 | // `separator.lastIndex` is not reliable cross-browser 612 | lastIndex = match.index + match[0].length; 613 | if (lastIndex > lastLastIndex) { 614 | output.push(str.slice(lastLastIndex, match.index)); 615 | // Fix browsers whose `exec` methods don't consistently return `undefined` for 616 | // nonparticipating capturing groups 617 | if (!compliantExecNpcg && match.length > 1) { 618 | match[0].replace(separator2, function() { 619 | for (var i = 1; i < arguments.length - 2; i++) { 620 | if (arguments[i] === undef) { 621 | match[i] = undef; 622 | } 623 | } 624 | }); 625 | } 626 | if (match.length > 1 && match.index < str.length) { 627 | Array.prototype.push.apply(output, match.slice(1)); 628 | } 629 | lastLength = match[0].length; 630 | lastLastIndex = lastIndex; 631 | if (output.length >= limit) { 632 | break; 633 | } 634 | } 635 | if (separator.lastIndex === match.index) { 636 | separator.lastIndex++; // Avoid an infinite loop 637 | } 638 | } 639 | if (lastLastIndex === str.length) { 640 | if (lastLength || !separator.test("")) { 641 | output.push(""); 642 | } 643 | } else { 644 | output.push(str.slice(lastLastIndex)); 645 | } 646 | return output.length > limit ? output.slice(0, limit) : output; 647 | }; 648 | 649 | return self; 650 | })(); 651 | 652 | },{}],19:[function(require,module,exports){ 653 | 'use strict'; 654 | 655 | var OneVersionConstraint = require('individual/one-version'); 656 | 657 | var MY_VERSION = '7'; 658 | OneVersionConstraint('ev-store', MY_VERSION); 659 | 660 | var hashKey = '__EV_STORE_KEY@' + MY_VERSION; 661 | 662 | module.exports = EvStore; 663 | 664 | function EvStore(elem) { 665 | var hash = elem[hashKey]; 666 | 667 | if (!hash) { 668 | hash = elem[hashKey] = {}; 669 | } 670 | 671 | return hash; 672 | } 673 | 674 | },{"individual/one-version":21}],20:[function(require,module,exports){ 675 | (function (global){ 676 | 'use strict'; 677 | 678 | /*global window, global*/ 679 | 680 | var root = typeof window !== 'undefined' ? 681 | window : typeof global !== 'undefined' ? 682 | global : {}; 683 | 684 | module.exports = Individual; 685 | 686 | function Individual(key, value) { 687 | if (key in root) { 688 | return root[key]; 689 | } 690 | 691 | root[key] = value; 692 | 693 | return value; 694 | } 695 | 696 | }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 697 | },{}],21:[function(require,module,exports){ 698 | 'use strict'; 699 | 700 | var Individual = require('./index.js'); 701 | 702 | module.exports = OneVersion; 703 | 704 | function OneVersion(moduleName, version, defaultValue) { 705 | var key = '__INDIVIDUAL_ONE_VERSION_' + moduleName; 706 | var enforceKey = key + '_ENFORCE_SINGLETON'; 707 | 708 | var versionValue = Individual(enforceKey, version); 709 | 710 | if (versionValue !== version) { 711 | throw new Error('Can only have one copy of ' + 712 | moduleName + '.\n' + 713 | 'You already have version ' + versionValue + 714 | ' installed.\n' + 715 | 'This means you cannot install version ' + version); 716 | } 717 | 718 | return Individual(key, defaultValue); 719 | } 720 | 721 | },{"./index.js":20}],22:[function(require,module,exports){ 722 | "use strict"; 723 | 724 | module.exports = function isObject(x) { 725 | return typeof x === "object" && x !== null; 726 | }; 727 | 728 | },{}],23:[function(require,module,exports){ 729 | var nativeIsArray = Array.isArray 730 | var toString = Object.prototype.toString 731 | 732 | module.exports = nativeIsArray || isArray 733 | 734 | function isArray(obj) { 735 | return toString.call(obj) === "[object Array]" 736 | } 737 | 738 | },{}],24:[function(require,module,exports){ 739 | var patch = require("./vdom/patch.js") 740 | 741 | module.exports = patch 742 | 743 | },{"./vdom/patch.js":29}],25:[function(require,module,exports){ 744 | var isObject = require("is-object") 745 | var isHook = require("../vnode/is-vhook.js") 746 | 747 | module.exports = applyProperties 748 | 749 | function applyProperties(node, props, previous) { 750 | for (var propName in props) { 751 | var propValue = props[propName] 752 | 753 | if (propValue === undefined) { 754 | removeProperty(node, propName, propValue, previous); 755 | } else if (isHook(propValue)) { 756 | removeProperty(node, propName, propValue, previous) 757 | if (propValue.hook) { 758 | propValue.hook(node, 759 | propName, 760 | previous ? previous[propName] : undefined) 761 | } 762 | } else { 763 | if (isObject(propValue)) { 764 | patchObject(node, props, previous, propName, propValue); 765 | } else { 766 | node[propName] = propValue 767 | } 768 | } 769 | } 770 | } 771 | 772 | function removeProperty(node, propName, propValue, previous) { 773 | if (previous) { 774 | var previousValue = previous[propName] 775 | 776 | if (!isHook(previousValue)) { 777 | if (propName === "attributes") { 778 | for (var attrName in previousValue) { 779 | node.removeAttribute(attrName) 780 | } 781 | } else if (propName === "style") { 782 | for (var i in previousValue) { 783 | node.style[i] = "" 784 | } 785 | } else if (typeof previousValue === "string") { 786 | node[propName] = "" 787 | } else { 788 | node[propName] = null 789 | } 790 | } else if (previousValue.unhook) { 791 | previousValue.unhook(node, propName, propValue) 792 | } 793 | } 794 | } 795 | 796 | function patchObject(node, props, previous, propName, propValue) { 797 | var previousValue = previous ? previous[propName] : undefined 798 | 799 | // Set attributes 800 | if (propName === "attributes") { 801 | for (var attrName in propValue) { 802 | var attrValue = propValue[attrName] 803 | 804 | if (attrValue === undefined) { 805 | node.removeAttribute(attrName) 806 | } else { 807 | node.setAttribute(attrName, attrValue) 808 | } 809 | } 810 | 811 | return 812 | } 813 | 814 | if(previousValue && isObject(previousValue) && 815 | getPrototype(previousValue) !== getPrototype(propValue)) { 816 | node[propName] = propValue 817 | return 818 | } 819 | 820 | if (!isObject(node[propName])) { 821 | node[propName] = {} 822 | } 823 | 824 | var replacer = propName === "style" ? "" : undefined 825 | 826 | for (var k in propValue) { 827 | var value = propValue[k] 828 | node[propName][k] = (value === undefined) ? replacer : value 829 | } 830 | } 831 | 832 | function getPrototype(value) { 833 | if (Object.getPrototypeOf) { 834 | return Object.getPrototypeOf(value) 835 | } else if (value.__proto__) { 836 | return value.__proto__ 837 | } else if (value.constructor) { 838 | return value.constructor.prototype 839 | } 840 | } 841 | 842 | },{"../vnode/is-vhook.js":38,"is-object":22}],26:[function(require,module,exports){ 843 | var document = require("global/document") 844 | 845 | var applyProperties = require("./apply-properties") 846 | 847 | var isVNode = require("../vnode/is-vnode.js") 848 | var isVText = require("../vnode/is-vtext.js") 849 | var isWidget = require("../vnode/is-widget.js") 850 | var handleThunk = require("../vnode/handle-thunk.js") 851 | 852 | module.exports = createElement 853 | 854 | function createElement(vnode, opts) { 855 | var doc = opts ? opts.document || document : document 856 | var warn = opts ? opts.warn : null 857 | 858 | vnode = handleThunk(vnode).a 859 | 860 | if (isWidget(vnode)) { 861 | return vnode.init() 862 | } else if (isVText(vnode)) { 863 | return doc.createTextNode(vnode.text) 864 | } else if (!isVNode(vnode)) { 865 | if (warn) { 866 | warn("Item is not a valid virtual dom node", vnode) 867 | } 868 | return null 869 | } 870 | 871 | var node = (vnode.namespace === null) ? 872 | doc.createElement(vnode.tagName) : 873 | doc.createElementNS(vnode.namespace, vnode.tagName) 874 | 875 | var props = vnode.properties 876 | applyProperties(node, props) 877 | 878 | var children = vnode.children 879 | 880 | for (var i = 0; i < children.length; i++) { 881 | var childNode = createElement(children[i], opts) 882 | if (childNode) { 883 | node.appendChild(childNode) 884 | } 885 | } 886 | 887 | return node 888 | } 889 | 890 | },{"../vnode/handle-thunk.js":36,"../vnode/is-vnode.js":39,"../vnode/is-vtext.js":40,"../vnode/is-widget.js":41,"./apply-properties":25,"global/document":2}],27:[function(require,module,exports){ 891 | // Maps a virtual DOM tree onto a real DOM tree in an efficient manner. 892 | // We don't want to read all of the DOM nodes in the tree so we use 893 | // the in-order tree indexing to eliminate recursion down certain branches. 894 | // We only recurse into a DOM node if we know that it contains a child of 895 | // interest. 896 | 897 | var noChild = {} 898 | 899 | module.exports = domIndex 900 | 901 | function domIndex(rootNode, tree, indices, nodes) { 902 | if (!indices || indices.length === 0) { 903 | return {} 904 | } else { 905 | indices.sort(ascending) 906 | return recurse(rootNode, tree, indices, nodes, 0) 907 | } 908 | } 909 | 910 | function recurse(rootNode, tree, indices, nodes, rootIndex) { 911 | nodes = nodes || {} 912 | 913 | 914 | if (rootNode) { 915 | if (indexInRange(indices, rootIndex, rootIndex)) { 916 | nodes[rootIndex] = rootNode 917 | } 918 | 919 | var vChildren = tree.children 920 | 921 | if (vChildren) { 922 | 923 | var childNodes = rootNode.childNodes 924 | 925 | for (var i = 0; i < tree.children.length; i++) { 926 | rootIndex += 1 927 | 928 | var vChild = vChildren[i] || noChild 929 | var nextIndex = rootIndex + (vChild.count || 0) 930 | 931 | // skip recursion down the tree if there are no nodes down here 932 | if (indexInRange(indices, rootIndex, nextIndex)) { 933 | recurse(childNodes[i], vChild, indices, nodes, rootIndex) 934 | } 935 | 936 | rootIndex = nextIndex 937 | } 938 | } 939 | } 940 | 941 | return nodes 942 | } 943 | 944 | // Binary search for an index in the interval [left, right] 945 | function indexInRange(indices, left, right) { 946 | if (indices.length === 0) { 947 | return false 948 | } 949 | 950 | var minIndex = 0 951 | var maxIndex = indices.length - 1 952 | var currentIndex 953 | var currentItem 954 | 955 | while (minIndex <= maxIndex) { 956 | currentIndex = ((maxIndex + minIndex) / 2) >> 0 957 | currentItem = indices[currentIndex] 958 | 959 | if (minIndex === maxIndex) { 960 | return currentItem >= left && currentItem <= right 961 | } else if (currentItem < left) { 962 | minIndex = currentIndex + 1 963 | } else if (currentItem > right) { 964 | maxIndex = currentIndex - 1 965 | } else { 966 | return true 967 | } 968 | } 969 | 970 | return false; 971 | } 972 | 973 | function ascending(a, b) { 974 | return a > b ? 1 : -1 975 | } 976 | 977 | },{}],28:[function(require,module,exports){ 978 | var applyProperties = require("./apply-properties") 979 | 980 | var isWidget = require("../vnode/is-widget.js") 981 | var VPatch = require("../vnode/vpatch.js") 982 | 983 | var render = require("./create-element") 984 | var updateWidget = require("./update-widget") 985 | 986 | module.exports = applyPatch 987 | 988 | function applyPatch(vpatch, domNode, renderOptions) { 989 | var type = vpatch.type 990 | var vNode = vpatch.vNode 991 | var patch = vpatch.patch 992 | 993 | switch (type) { 994 | case VPatch.REMOVE: 995 | return removeNode(domNode, vNode) 996 | case VPatch.INSERT: 997 | return insertNode(domNode, patch, renderOptions) 998 | case VPatch.VTEXT: 999 | return stringPatch(domNode, vNode, patch, renderOptions) 1000 | case VPatch.WIDGET: 1001 | return widgetPatch(domNode, vNode, patch, renderOptions) 1002 | case VPatch.VNODE: 1003 | return vNodePatch(domNode, vNode, patch, renderOptions) 1004 | case VPatch.ORDER: 1005 | reorderChildren(domNode, patch) 1006 | return domNode 1007 | case VPatch.PROPS: 1008 | applyProperties(domNode, patch, vNode.properties) 1009 | return domNode 1010 | case VPatch.THUNK: 1011 | return replaceRoot(domNode, 1012 | renderOptions.patch(domNode, patch, renderOptions)) 1013 | default: 1014 | return domNode 1015 | } 1016 | } 1017 | 1018 | function removeNode(domNode, vNode) { 1019 | var parentNode = domNode.parentNode 1020 | 1021 | if (parentNode) { 1022 | parentNode.removeChild(domNode) 1023 | } 1024 | 1025 | destroyWidget(domNode, vNode); 1026 | 1027 | return null 1028 | } 1029 | 1030 | function insertNode(parentNode, vNode, renderOptions) { 1031 | var newNode = render(vNode, renderOptions) 1032 | 1033 | if (parentNode) { 1034 | parentNode.appendChild(newNode) 1035 | } 1036 | 1037 | return parentNode 1038 | } 1039 | 1040 | function stringPatch(domNode, leftVNode, vText, renderOptions) { 1041 | var newNode 1042 | 1043 | if (domNode.nodeType === 3) { 1044 | domNode.replaceData(0, domNode.length, vText.text) 1045 | newNode = domNode 1046 | } else { 1047 | var parentNode = domNode.parentNode 1048 | newNode = render(vText, renderOptions) 1049 | 1050 | if (parentNode && newNode !== domNode) { 1051 | parentNode.replaceChild(newNode, domNode) 1052 | } 1053 | } 1054 | 1055 | return newNode 1056 | } 1057 | 1058 | function widgetPatch(domNode, leftVNode, widget, renderOptions) { 1059 | var updating = updateWidget(leftVNode, widget) 1060 | var newNode 1061 | 1062 | if (updating) { 1063 | newNode = widget.update(leftVNode, domNode) || domNode 1064 | } else { 1065 | newNode = render(widget, renderOptions) 1066 | } 1067 | 1068 | var parentNode = domNode.parentNode 1069 | 1070 | if (parentNode && newNode !== domNode) { 1071 | parentNode.replaceChild(newNode, domNode) 1072 | } 1073 | 1074 | if (!updating) { 1075 | destroyWidget(domNode, leftVNode) 1076 | } 1077 | 1078 | return newNode 1079 | } 1080 | 1081 | function vNodePatch(domNode, leftVNode, vNode, renderOptions) { 1082 | var parentNode = domNode.parentNode 1083 | var newNode = render(vNode, renderOptions) 1084 | 1085 | if (parentNode && newNode !== domNode) { 1086 | parentNode.replaceChild(newNode, domNode) 1087 | } 1088 | 1089 | return newNode 1090 | } 1091 | 1092 | function destroyWidget(domNode, w) { 1093 | if (typeof w.destroy === "function" && isWidget(w)) { 1094 | w.destroy(domNode) 1095 | } 1096 | } 1097 | 1098 | function reorderChildren(domNode, moves) { 1099 | var childNodes = domNode.childNodes 1100 | var keyMap = {} 1101 | var node 1102 | var remove 1103 | var insert 1104 | 1105 | for (var i = 0; i < moves.removes.length; i++) { 1106 | remove = moves.removes[i] 1107 | node = childNodes[remove.from] 1108 | if (remove.key) { 1109 | keyMap[remove.key] = node 1110 | } 1111 | domNode.removeChild(node) 1112 | } 1113 | 1114 | var length = childNodes.length 1115 | for (var j = 0; j < moves.inserts.length; j++) { 1116 | insert = moves.inserts[j] 1117 | node = keyMap[insert.key] 1118 | // this is the weirdest bug i've ever seen in webkit 1119 | domNode.insertBefore(node, insert.to >= length++ ? null : childNodes[insert.to]) 1120 | } 1121 | } 1122 | 1123 | function replaceRoot(oldRoot, newRoot) { 1124 | if (oldRoot && newRoot && oldRoot !== newRoot && oldRoot.parentNode) { 1125 | oldRoot.parentNode.replaceChild(newRoot, oldRoot) 1126 | } 1127 | 1128 | return newRoot; 1129 | } 1130 | 1131 | },{"../vnode/is-widget.js":41,"../vnode/vpatch.js":44,"./apply-properties":25,"./create-element":26,"./update-widget":30}],29:[function(require,module,exports){ 1132 | var document = require("global/document") 1133 | var isArray = require("x-is-array") 1134 | 1135 | var domIndex = require("./dom-index") 1136 | var patchOp = require("./patch-op") 1137 | module.exports = patch 1138 | 1139 | function patch(rootNode, patches) { 1140 | return patchRecursive(rootNode, patches) 1141 | } 1142 | 1143 | function patchRecursive(rootNode, patches, renderOptions) { 1144 | var indices = patchIndices(patches) 1145 | 1146 | if (indices.length === 0) { 1147 | return rootNode 1148 | } 1149 | 1150 | var index = domIndex(rootNode, patches.a, indices) 1151 | var ownerDocument = rootNode.ownerDocument 1152 | 1153 | if (!renderOptions) { 1154 | renderOptions = { patch: patchRecursive } 1155 | if (ownerDocument !== document) { 1156 | renderOptions.document = ownerDocument 1157 | } 1158 | } 1159 | 1160 | for (var i = 0; i < indices.length; i++) { 1161 | var nodeIndex = indices[i] 1162 | rootNode = applyPatch(rootNode, 1163 | index[nodeIndex], 1164 | patches[nodeIndex], 1165 | renderOptions) 1166 | } 1167 | 1168 | return rootNode 1169 | } 1170 | 1171 | function applyPatch(rootNode, domNode, patchList, renderOptions) { 1172 | if (!domNode) { 1173 | return rootNode 1174 | } 1175 | 1176 | var newNode 1177 | 1178 | if (isArray(patchList)) { 1179 | for (var i = 0; i < patchList.length; i++) { 1180 | newNode = patchOp(patchList[i], domNode, renderOptions) 1181 | 1182 | if (domNode === rootNode) { 1183 | rootNode = newNode 1184 | } 1185 | } 1186 | } else { 1187 | newNode = patchOp(patchList, domNode, renderOptions) 1188 | 1189 | if (domNode === rootNode) { 1190 | rootNode = newNode 1191 | } 1192 | } 1193 | 1194 | return rootNode 1195 | } 1196 | 1197 | function patchIndices(patches) { 1198 | var indices = [] 1199 | 1200 | for (var key in patches) { 1201 | if (key !== "a") { 1202 | indices.push(Number(key)) 1203 | } 1204 | } 1205 | 1206 | return indices 1207 | } 1208 | 1209 | },{"./dom-index":27,"./patch-op":28,"global/document":2,"x-is-array":23}],30:[function(require,module,exports){ 1210 | var isWidget = require("../vnode/is-widget.js") 1211 | 1212 | module.exports = updateWidget 1213 | 1214 | function updateWidget(a, b) { 1215 | if (isWidget(a) && isWidget(b)) { 1216 | if ("name" in a && "name" in b) { 1217 | return a.id === b.id 1218 | } else { 1219 | return a.init === b.init 1220 | } 1221 | } 1222 | 1223 | return false 1224 | } 1225 | 1226 | },{"../vnode/is-widget.js":41}],31:[function(require,module,exports){ 1227 | 'use strict'; 1228 | 1229 | module.exports = AttributeHook; 1230 | 1231 | function AttributeHook(namespace, value) { 1232 | if (!(this instanceof AttributeHook)) { 1233 | return new AttributeHook(namespace, value); 1234 | } 1235 | 1236 | this.namespace = namespace; 1237 | this.value = value; 1238 | } 1239 | 1240 | AttributeHook.prototype.hook = function (node, prop, prev) { 1241 | if (prev && prev.type === 'AttributeHook' && 1242 | prev.value === this.value && 1243 | prev.namespace === this.namespace) { 1244 | return; 1245 | } 1246 | 1247 | node.setAttributeNS(this.namespace, prop, this.value); 1248 | }; 1249 | 1250 | AttributeHook.prototype.unhook = function (node, prop, next) { 1251 | if (next && next.type === 'AttributeHook' && 1252 | next.namespace === this.namespace) { 1253 | return; 1254 | } 1255 | 1256 | var colonPosition = prop.indexOf(':'); 1257 | var localName = colonPosition > -1 ? prop.substr(colonPosition + 1) : prop; 1258 | node.removeAttributeNS(this.namespace, localName); 1259 | }; 1260 | 1261 | AttributeHook.prototype.type = 'AttributeHook'; 1262 | 1263 | },{}],32:[function(require,module,exports){ 1264 | 'use strict'; 1265 | 1266 | var EvStore = require('ev-store'); 1267 | 1268 | module.exports = EvHook; 1269 | 1270 | function EvHook(value) { 1271 | if (!(this instanceof EvHook)) { 1272 | return new EvHook(value); 1273 | } 1274 | 1275 | this.value = value; 1276 | } 1277 | 1278 | EvHook.prototype.hook = function (node, propertyName) { 1279 | var es = EvStore(node); 1280 | var propName = propertyName.substr(3); 1281 | 1282 | es[propName] = this.value; 1283 | }; 1284 | 1285 | EvHook.prototype.unhook = function(node, propertyName) { 1286 | var es = EvStore(node); 1287 | var propName = propertyName.substr(3); 1288 | 1289 | es[propName] = undefined; 1290 | }; 1291 | 1292 | },{"ev-store":19}],33:[function(require,module,exports){ 1293 | 'use strict'; 1294 | 1295 | module.exports = SoftSetHook; 1296 | 1297 | function SoftSetHook(value) { 1298 | if (!(this instanceof SoftSetHook)) { 1299 | return new SoftSetHook(value); 1300 | } 1301 | 1302 | this.value = value; 1303 | } 1304 | 1305 | SoftSetHook.prototype.hook = function (node, propertyName) { 1306 | if (node[propertyName] !== this.value) { 1307 | node[propertyName] = this.value; 1308 | } 1309 | }; 1310 | 1311 | },{}],34:[function(require,module,exports){ 1312 | 'use strict'; 1313 | 1314 | var isArray = require('x-is-array'); 1315 | 1316 | var VNode = require('../vnode/vnode.js'); 1317 | var VText = require('../vnode/vtext.js'); 1318 | var isVNode = require('../vnode/is-vnode'); 1319 | var isVText = require('../vnode/is-vtext'); 1320 | var isWidget = require('../vnode/is-widget'); 1321 | var isHook = require('../vnode/is-vhook'); 1322 | var isVThunk = require('../vnode/is-thunk'); 1323 | 1324 | var parseTag = require('./parse-tag.js'); 1325 | var softSetHook = require('./hooks/soft-set-hook.js'); 1326 | var evHook = require('./hooks/ev-hook.js'); 1327 | 1328 | module.exports = h; 1329 | 1330 | function h(tagName, properties, children) { 1331 | var childNodes = []; 1332 | var tag, props, key, namespace; 1333 | 1334 | if (!children && isChildren(properties)) { 1335 | children = properties; 1336 | props = {}; 1337 | } 1338 | 1339 | props = props || properties || {}; 1340 | tag = parseTag(tagName, props); 1341 | 1342 | // support keys 1343 | if (props.hasOwnProperty('key')) { 1344 | key = props.key; 1345 | props.key = undefined; 1346 | } 1347 | 1348 | // support namespace 1349 | if (props.hasOwnProperty('namespace')) { 1350 | namespace = props.namespace; 1351 | props.namespace = undefined; 1352 | } 1353 | 1354 | // fix cursor bug 1355 | if (tag === 'INPUT' && 1356 | !namespace && 1357 | props.hasOwnProperty('value') && 1358 | props.value !== undefined && 1359 | !isHook(props.value) 1360 | ) { 1361 | props.value = softSetHook(props.value); 1362 | } 1363 | 1364 | transformProperties(props); 1365 | 1366 | if (children !== undefined && children !== null) { 1367 | addChild(children, childNodes, tag, props); 1368 | } 1369 | 1370 | 1371 | return new VNode(tag, props, childNodes, key, namespace); 1372 | } 1373 | 1374 | function addChild(c, childNodes, tag, props) { 1375 | if (typeof c === 'string') { 1376 | childNodes.push(new VText(c)); 1377 | } else if (isChild(c)) { 1378 | childNodes.push(c); 1379 | } else if (isArray(c)) { 1380 | for (var i = 0; i < c.length; i++) { 1381 | addChild(c[i], childNodes, tag, props); 1382 | } 1383 | } else if (c === null || c === undefined) { 1384 | return; 1385 | } else { 1386 | throw UnexpectedVirtualElement({ 1387 | foreignObject: c, 1388 | parentVnode: { 1389 | tagName: tag, 1390 | properties: props 1391 | } 1392 | }); 1393 | } 1394 | } 1395 | 1396 | function transformProperties(props) { 1397 | for (var propName in props) { 1398 | if (props.hasOwnProperty(propName)) { 1399 | var value = props[propName]; 1400 | 1401 | if (isHook(value)) { 1402 | continue; 1403 | } 1404 | 1405 | if (propName.substr(0, 3) === 'ev-') { 1406 | // add ev-foo support 1407 | props[propName] = evHook(value); 1408 | } 1409 | } 1410 | } 1411 | } 1412 | 1413 | function isChild(x) { 1414 | return isVNode(x) || isVText(x) || isWidget(x) || isVThunk(x); 1415 | } 1416 | 1417 | function isChildren(x) { 1418 | return typeof x === 'string' || isArray(x) || isChild(x); 1419 | } 1420 | 1421 | function UnexpectedVirtualElement(data) { 1422 | var err = new Error(); 1423 | 1424 | err.type = 'virtual-hyperscript.unexpected.virtual-element'; 1425 | err.message = 'Unexpected virtual child passed to h().\n' + 1426 | 'Expected a VNode / Vthunk / VWidget / string but:\n' + 1427 | 'got:\n' + 1428 | errorString(data.foreignObject) + 1429 | '.\n' + 1430 | 'The parent vnode is:\n' + 1431 | errorString(data.parentVnode) 1432 | '\n' + 1433 | 'Suggested fix: change your `h(..., [ ... ])` callsite.'; 1434 | err.foreignObject = data.foreignObject; 1435 | err.parentVnode = data.parentVnode; 1436 | 1437 | return err; 1438 | } 1439 | 1440 | function errorString(obj) { 1441 | try { 1442 | return JSON.stringify(obj, null, ' '); 1443 | } catch (e) { 1444 | return String(obj); 1445 | } 1446 | } 1447 | 1448 | },{"../vnode/is-thunk":37,"../vnode/is-vhook":38,"../vnode/is-vnode":39,"../vnode/is-vtext":40,"../vnode/is-widget":41,"../vnode/vnode.js":43,"../vnode/vtext.js":45,"./hooks/ev-hook.js":32,"./hooks/soft-set-hook.js":33,"./parse-tag.js":35,"x-is-array":23}],35:[function(require,module,exports){ 1449 | 'use strict'; 1450 | 1451 | var split = require('browser-split'); 1452 | 1453 | var classIdSplit = /([\.#]?[a-zA-Z0-9_:-]+)/; 1454 | var notClassId = /^\.|#/; 1455 | 1456 | module.exports = parseTag; 1457 | 1458 | function parseTag(tag, props) { 1459 | if (!tag) { 1460 | return 'DIV'; 1461 | } 1462 | 1463 | var noId = !(props.hasOwnProperty('id')); 1464 | 1465 | var tagParts = split(tag, classIdSplit); 1466 | var tagName = null; 1467 | 1468 | if (notClassId.test(tagParts[1])) { 1469 | tagName = 'DIV'; 1470 | } 1471 | 1472 | var classes, part, type, i; 1473 | 1474 | for (i = 0; i < tagParts.length; i++) { 1475 | part = tagParts[i]; 1476 | 1477 | if (!part) { 1478 | continue; 1479 | } 1480 | 1481 | type = part.charAt(0); 1482 | 1483 | if (!tagName) { 1484 | tagName = part; 1485 | } else if (type === '.') { 1486 | classes = classes || []; 1487 | classes.push(part.substring(1, part.length)); 1488 | } else if (type === '#' && noId) { 1489 | props.id = part.substring(1, part.length); 1490 | } 1491 | } 1492 | 1493 | if (classes) { 1494 | if (props.className) { 1495 | classes.push(props.className); 1496 | } 1497 | 1498 | props.className = classes.join(' '); 1499 | } 1500 | 1501 | return props.namespace ? tagName : tagName.toUpperCase(); 1502 | } 1503 | 1504 | },{"browser-split":18}],36:[function(require,module,exports){ 1505 | var isVNode = require("./is-vnode") 1506 | var isVText = require("./is-vtext") 1507 | var isWidget = require("./is-widget") 1508 | var isThunk = require("./is-thunk") 1509 | 1510 | module.exports = handleThunk 1511 | 1512 | function handleThunk(a, b) { 1513 | var renderedA = a 1514 | var renderedB = b 1515 | 1516 | if (isThunk(b)) { 1517 | renderedB = renderThunk(b, a) 1518 | } 1519 | 1520 | if (isThunk(a)) { 1521 | renderedA = renderThunk(a, null) 1522 | } 1523 | 1524 | return { 1525 | a: renderedA, 1526 | b: renderedB 1527 | } 1528 | } 1529 | 1530 | function renderThunk(thunk, previous) { 1531 | var renderedThunk = thunk.vnode 1532 | 1533 | if (!renderedThunk) { 1534 | renderedThunk = thunk.vnode = thunk.render(previous) 1535 | } 1536 | 1537 | if (!(isVNode(renderedThunk) || 1538 | isVText(renderedThunk) || 1539 | isWidget(renderedThunk))) { 1540 | throw new Error("thunk did not return a valid node"); 1541 | } 1542 | 1543 | return renderedThunk 1544 | } 1545 | 1546 | },{"./is-thunk":37,"./is-vnode":39,"./is-vtext":40,"./is-widget":41}],37:[function(require,module,exports){ 1547 | module.exports = isThunk 1548 | 1549 | function isThunk(t) { 1550 | return t && t.type === "Thunk" 1551 | } 1552 | 1553 | },{}],38:[function(require,module,exports){ 1554 | module.exports = isHook 1555 | 1556 | function isHook(hook) { 1557 | return hook && 1558 | (typeof hook.hook === "function" && !hook.hasOwnProperty("hook") || 1559 | typeof hook.unhook === "function" && !hook.hasOwnProperty("unhook")) 1560 | } 1561 | 1562 | },{}],39:[function(require,module,exports){ 1563 | var version = require("./version") 1564 | 1565 | module.exports = isVirtualNode 1566 | 1567 | function isVirtualNode(x) { 1568 | return x && x.type === "VirtualNode" && x.version === version 1569 | } 1570 | 1571 | },{"./version":42}],40:[function(require,module,exports){ 1572 | var version = require("./version") 1573 | 1574 | module.exports = isVirtualText 1575 | 1576 | function isVirtualText(x) { 1577 | return x && x.type === "VirtualText" && x.version === version 1578 | } 1579 | 1580 | },{"./version":42}],41:[function(require,module,exports){ 1581 | module.exports = isWidget 1582 | 1583 | function isWidget(w) { 1584 | return w && w.type === "Widget" 1585 | } 1586 | 1587 | },{}],42:[function(require,module,exports){ 1588 | module.exports = "2" 1589 | 1590 | },{}],43:[function(require,module,exports){ 1591 | var version = require("./version") 1592 | var isVNode = require("./is-vnode") 1593 | var isWidget = require("./is-widget") 1594 | var isThunk = require("./is-thunk") 1595 | var isVHook = require("./is-vhook") 1596 | 1597 | module.exports = VirtualNode 1598 | 1599 | var noProperties = {} 1600 | var noChildren = [] 1601 | 1602 | function VirtualNode(tagName, properties, children, key, namespace) { 1603 | this.tagName = tagName 1604 | this.properties = properties || noProperties 1605 | this.children = children || noChildren 1606 | this.key = key != null ? String(key) : undefined 1607 | this.namespace = (typeof namespace === "string") ? namespace : null 1608 | 1609 | var count = (children && children.length) || 0 1610 | var descendants = 0 1611 | var hasWidgets = false 1612 | var hasThunks = false 1613 | var descendantHooks = false 1614 | var hooks 1615 | 1616 | for (var propName in properties) { 1617 | if (properties.hasOwnProperty(propName)) { 1618 | var property = properties[propName] 1619 | if (isVHook(property) && property.unhook) { 1620 | if (!hooks) { 1621 | hooks = {} 1622 | } 1623 | 1624 | hooks[propName] = property 1625 | } 1626 | } 1627 | } 1628 | 1629 | for (var i = 0; i < count; i++) { 1630 | var child = children[i] 1631 | if (isVNode(child)) { 1632 | descendants += child.count || 0 1633 | 1634 | if (!hasWidgets && child.hasWidgets) { 1635 | hasWidgets = true 1636 | } 1637 | 1638 | if (!hasThunks && child.hasThunks) { 1639 | hasThunks = true 1640 | } 1641 | 1642 | if (!descendantHooks && (child.hooks || child.descendantHooks)) { 1643 | descendantHooks = true 1644 | } 1645 | } else if (!hasWidgets && isWidget(child)) { 1646 | if (typeof child.destroy === "function") { 1647 | hasWidgets = true 1648 | } 1649 | } else if (!hasThunks && isThunk(child)) { 1650 | hasThunks = true; 1651 | } 1652 | } 1653 | 1654 | this.count = count + descendants 1655 | this.hasWidgets = hasWidgets 1656 | this.hasThunks = hasThunks 1657 | this.hooks = hooks 1658 | this.descendantHooks = descendantHooks 1659 | } 1660 | 1661 | VirtualNode.prototype.version = version 1662 | VirtualNode.prototype.type = "VirtualNode" 1663 | 1664 | },{"./is-thunk":37,"./is-vhook":38,"./is-vnode":39,"./is-widget":41,"./version":42}],44:[function(require,module,exports){ 1665 | var version = require("./version") 1666 | 1667 | VirtualPatch.NONE = 0 1668 | VirtualPatch.VTEXT = 1 1669 | VirtualPatch.VNODE = 2 1670 | VirtualPatch.WIDGET = 3 1671 | VirtualPatch.PROPS = 4 1672 | VirtualPatch.ORDER = 5 1673 | VirtualPatch.INSERT = 6 1674 | VirtualPatch.REMOVE = 7 1675 | VirtualPatch.THUNK = 8 1676 | 1677 | module.exports = VirtualPatch 1678 | 1679 | function VirtualPatch(type, vNode, patch) { 1680 | this.type = Number(type) 1681 | this.vNode = vNode 1682 | this.patch = patch 1683 | } 1684 | 1685 | VirtualPatch.prototype.version = version 1686 | VirtualPatch.prototype.type = "VirtualPatch" 1687 | 1688 | },{"./version":42}],45:[function(require,module,exports){ 1689 | var version = require("./version") 1690 | 1691 | module.exports = VirtualText 1692 | 1693 | function VirtualText(text) { 1694 | this.text = String(text) 1695 | } 1696 | 1697 | VirtualText.prototype.version = version 1698 | VirtualText.prototype.type = "VirtualText" 1699 | 1700 | },{"./version":42}],46:[function(require,module,exports){ 1701 | var isObject = require("is-object") 1702 | var isHook = require("../vnode/is-vhook") 1703 | 1704 | module.exports = diffProps 1705 | 1706 | function diffProps(a, b) { 1707 | var diff 1708 | 1709 | for (var aKey in a) { 1710 | if (!(aKey in b)) { 1711 | diff = diff || {} 1712 | diff[aKey] = undefined 1713 | } 1714 | 1715 | var aValue = a[aKey] 1716 | var bValue = b[aKey] 1717 | 1718 | if (aValue === bValue) { 1719 | continue 1720 | } else if (isObject(aValue) && isObject(bValue)) { 1721 | if (getPrototype(bValue) !== getPrototype(aValue)) { 1722 | diff = diff || {} 1723 | diff[aKey] = bValue 1724 | } else if (isHook(bValue)) { 1725 | diff = diff || {} 1726 | diff[aKey] = bValue 1727 | } else { 1728 | var objectDiff = diffProps(aValue, bValue) 1729 | if (objectDiff) { 1730 | diff = diff || {} 1731 | diff[aKey] = objectDiff 1732 | } 1733 | } 1734 | } else { 1735 | diff = diff || {} 1736 | diff[aKey] = bValue 1737 | } 1738 | } 1739 | 1740 | for (var bKey in b) { 1741 | if (!(bKey in a)) { 1742 | diff = diff || {} 1743 | diff[bKey] = b[bKey] 1744 | } 1745 | } 1746 | 1747 | return diff 1748 | } 1749 | 1750 | function getPrototype(value) { 1751 | if (Object.getPrototypeOf) { 1752 | return Object.getPrototypeOf(value) 1753 | } else if (value.__proto__) { 1754 | return value.__proto__ 1755 | } else if (value.constructor) { 1756 | return value.constructor.prototype 1757 | } 1758 | } 1759 | 1760 | },{"../vnode/is-vhook":38,"is-object":22}],47:[function(require,module,exports){ 1761 | var isArray = require("x-is-array") 1762 | 1763 | var VPatch = require("../vnode/vpatch") 1764 | var isVNode = require("../vnode/is-vnode") 1765 | var isVText = require("../vnode/is-vtext") 1766 | var isWidget = require("../vnode/is-widget") 1767 | var isThunk = require("../vnode/is-thunk") 1768 | var handleThunk = require("../vnode/handle-thunk") 1769 | 1770 | var diffProps = require("./diff-props") 1771 | 1772 | module.exports = diff 1773 | 1774 | function diff(a, b) { 1775 | var patch = { a: a } 1776 | walk(a, b, patch, 0) 1777 | return patch 1778 | } 1779 | 1780 | function walk(a, b, patch, index) { 1781 | if (a === b) { 1782 | return 1783 | } 1784 | 1785 | var apply = patch[index] 1786 | var applyClear = false 1787 | 1788 | if (isThunk(a) || isThunk(b)) { 1789 | thunks(a, b, patch, index) 1790 | } else if (b == null) { 1791 | 1792 | // If a is a widget we will add a remove patch for it 1793 | // Otherwise any child widgets/hooks must be destroyed. 1794 | // This prevents adding two remove patches for a widget. 1795 | if (!isWidget(a)) { 1796 | clearState(a, patch, index) 1797 | apply = patch[index] 1798 | } 1799 | 1800 | apply = appendPatch(apply, new VPatch(VPatch.REMOVE, a, b)) 1801 | } else if (isVNode(b)) { 1802 | if (isVNode(a)) { 1803 | if (a.tagName === b.tagName && 1804 | a.namespace === b.namespace && 1805 | a.key === b.key) { 1806 | var propsPatch = diffProps(a.properties, b.properties) 1807 | if (propsPatch) { 1808 | apply = appendPatch(apply, 1809 | new VPatch(VPatch.PROPS, a, propsPatch)) 1810 | } 1811 | apply = diffChildren(a, b, patch, apply, index) 1812 | } else { 1813 | apply = appendPatch(apply, new VPatch(VPatch.VNODE, a, b)) 1814 | applyClear = true 1815 | } 1816 | } else { 1817 | apply = appendPatch(apply, new VPatch(VPatch.VNODE, a, b)) 1818 | applyClear = true 1819 | } 1820 | } else if (isVText(b)) { 1821 | if (!isVText(a)) { 1822 | apply = appendPatch(apply, new VPatch(VPatch.VTEXT, a, b)) 1823 | applyClear = true 1824 | } else if (a.text !== b.text) { 1825 | apply = appendPatch(apply, new VPatch(VPatch.VTEXT, a, b)) 1826 | } 1827 | } else if (isWidget(b)) { 1828 | if (!isWidget(a)) { 1829 | applyClear = true 1830 | } 1831 | 1832 | apply = appendPatch(apply, new VPatch(VPatch.WIDGET, a, b)) 1833 | } 1834 | 1835 | if (apply) { 1836 | patch[index] = apply 1837 | } 1838 | 1839 | if (applyClear) { 1840 | clearState(a, patch, index) 1841 | } 1842 | } 1843 | 1844 | function diffChildren(a, b, patch, apply, index) { 1845 | var aChildren = a.children 1846 | var orderedSet = reorder(aChildren, b.children) 1847 | var bChildren = orderedSet.children 1848 | 1849 | var aLen = aChildren.length 1850 | var bLen = bChildren.length 1851 | var len = aLen > bLen ? aLen : bLen 1852 | 1853 | for (var i = 0; i < len; i++) { 1854 | var leftNode = aChildren[i] 1855 | var rightNode = bChildren[i] 1856 | index += 1 1857 | 1858 | if (!leftNode) { 1859 | if (rightNode) { 1860 | // Excess nodes in b need to be added 1861 | apply = appendPatch(apply, 1862 | new VPatch(VPatch.INSERT, null, rightNode)) 1863 | } 1864 | } else { 1865 | walk(leftNode, rightNode, patch, index) 1866 | } 1867 | 1868 | if (isVNode(leftNode) && leftNode.count) { 1869 | index += leftNode.count 1870 | } 1871 | } 1872 | 1873 | if (orderedSet.moves) { 1874 | // Reorder nodes last 1875 | apply = appendPatch(apply, new VPatch( 1876 | VPatch.ORDER, 1877 | a, 1878 | orderedSet.moves 1879 | )) 1880 | } 1881 | 1882 | return apply 1883 | } 1884 | 1885 | function clearState(vNode, patch, index) { 1886 | // TODO: Make this a single walk, not two 1887 | unhook(vNode, patch, index) 1888 | destroyWidgets(vNode, patch, index) 1889 | } 1890 | 1891 | // Patch records for all destroyed widgets must be added because we need 1892 | // a DOM node reference for the destroy function 1893 | function destroyWidgets(vNode, patch, index) { 1894 | if (isWidget(vNode)) { 1895 | if (typeof vNode.destroy === "function") { 1896 | patch[index] = appendPatch( 1897 | patch[index], 1898 | new VPatch(VPatch.REMOVE, vNode, null) 1899 | ) 1900 | } 1901 | } else if (isVNode(vNode) && (vNode.hasWidgets || vNode.hasThunks)) { 1902 | var children = vNode.children 1903 | var len = children.length 1904 | for (var i = 0; i < len; i++) { 1905 | var child = children[i] 1906 | index += 1 1907 | 1908 | destroyWidgets(child, patch, index) 1909 | 1910 | if (isVNode(child) && child.count) { 1911 | index += child.count 1912 | } 1913 | } 1914 | } else if (isThunk(vNode)) { 1915 | thunks(vNode, null, patch, index) 1916 | } 1917 | } 1918 | 1919 | // Create a sub-patch for thunks 1920 | function thunks(a, b, patch, index) { 1921 | var nodes = handleThunk(a, b) 1922 | var thunkPatch = diff(nodes.a, nodes.b) 1923 | if (hasPatches(thunkPatch)) { 1924 | patch[index] = new VPatch(VPatch.THUNK, null, thunkPatch) 1925 | } 1926 | } 1927 | 1928 | function hasPatches(patch) { 1929 | for (var index in patch) { 1930 | if (index !== "a") { 1931 | return true 1932 | } 1933 | } 1934 | 1935 | return false 1936 | } 1937 | 1938 | // Execute hooks when two nodes are identical 1939 | function unhook(vNode, patch, index) { 1940 | if (isVNode(vNode)) { 1941 | if (vNode.hooks) { 1942 | patch[index] = appendPatch( 1943 | patch[index], 1944 | new VPatch( 1945 | VPatch.PROPS, 1946 | vNode, 1947 | undefinedKeys(vNode.hooks) 1948 | ) 1949 | ) 1950 | } 1951 | 1952 | if (vNode.descendantHooks || vNode.hasThunks) { 1953 | var children = vNode.children 1954 | var len = children.length 1955 | for (var i = 0; i < len; i++) { 1956 | var child = children[i] 1957 | index += 1 1958 | 1959 | unhook(child, patch, index) 1960 | 1961 | if (isVNode(child) && child.count) { 1962 | index += child.count 1963 | } 1964 | } 1965 | } 1966 | } else if (isThunk(vNode)) { 1967 | thunks(vNode, null, patch, index) 1968 | } 1969 | } 1970 | 1971 | function undefinedKeys(obj) { 1972 | var result = {} 1973 | 1974 | for (var key in obj) { 1975 | result[key] = undefined 1976 | } 1977 | 1978 | return result 1979 | } 1980 | 1981 | // List diff, naive left to right reordering 1982 | function reorder(aChildren, bChildren) { 1983 | // O(M) time, O(M) memory 1984 | var bChildIndex = keyIndex(bChildren) 1985 | var bKeys = bChildIndex.keys 1986 | var bFree = bChildIndex.free 1987 | 1988 | if (bFree.length === bChildren.length) { 1989 | return { 1990 | children: bChildren, 1991 | moves: null 1992 | } 1993 | } 1994 | 1995 | // O(N) time, O(N) memory 1996 | var aChildIndex = keyIndex(aChildren) 1997 | var aKeys = aChildIndex.keys 1998 | var aFree = aChildIndex.free 1999 | 2000 | if (aFree.length === aChildren.length) { 2001 | return { 2002 | children: bChildren, 2003 | moves: null 2004 | } 2005 | } 2006 | 2007 | // O(MAX(N, M)) memory 2008 | var newChildren = [] 2009 | 2010 | var freeIndex = 0 2011 | var freeCount = bFree.length 2012 | var deletedItems = 0 2013 | 2014 | // Iterate through a and match a node in b 2015 | // O(N) time, 2016 | for (var i = 0 ; i < aChildren.length; i++) { 2017 | var aItem = aChildren[i] 2018 | var itemIndex 2019 | 2020 | if (aItem.key) { 2021 | if (bKeys.hasOwnProperty(aItem.key)) { 2022 | // Match up the old keys 2023 | itemIndex = bKeys[aItem.key] 2024 | newChildren.push(bChildren[itemIndex]) 2025 | 2026 | } else { 2027 | // Remove old keyed items 2028 | itemIndex = i - deletedItems++ 2029 | newChildren.push(null) 2030 | } 2031 | } else { 2032 | // Match the item in a with the next free item in b 2033 | if (freeIndex < freeCount) { 2034 | itemIndex = bFree[freeIndex++] 2035 | newChildren.push(bChildren[itemIndex]) 2036 | } else { 2037 | // There are no free items in b to match with 2038 | // the free items in a, so the extra free nodes 2039 | // are deleted. 2040 | itemIndex = i - deletedItems++ 2041 | newChildren.push(null) 2042 | } 2043 | } 2044 | } 2045 | 2046 | var lastFreeIndex = freeIndex >= bFree.length ? 2047 | bChildren.length : 2048 | bFree[freeIndex] 2049 | 2050 | // Iterate through b and append any new keys 2051 | // O(M) time 2052 | for (var j = 0; j < bChildren.length; j++) { 2053 | var newItem = bChildren[j] 2054 | 2055 | if (newItem.key) { 2056 | if (!aKeys.hasOwnProperty(newItem.key)) { 2057 | // Add any new keyed items 2058 | // We are adding new items to the end and then sorting them 2059 | // in place. In future we should insert new items in place. 2060 | newChildren.push(newItem) 2061 | } 2062 | } else if (j >= lastFreeIndex) { 2063 | // Add any leftover non-keyed items 2064 | newChildren.push(newItem) 2065 | } 2066 | } 2067 | 2068 | var simulate = newChildren.slice() 2069 | var simulateIndex = 0 2070 | var removes = [] 2071 | var inserts = [] 2072 | var simulateItem 2073 | 2074 | for (var k = 0; k < bChildren.length;) { 2075 | var wantedItem = bChildren[k] 2076 | simulateItem = simulate[simulateIndex] 2077 | 2078 | // remove items 2079 | while (simulateItem === null && simulate.length) { 2080 | removes.push(remove(simulate, simulateIndex, null)) 2081 | simulateItem = simulate[simulateIndex] 2082 | } 2083 | 2084 | if (!simulateItem || simulateItem.key !== wantedItem.key) { 2085 | // if we need a key in this position... 2086 | if (wantedItem.key) { 2087 | if (simulateItem && simulateItem.key) { 2088 | // if an insert doesn't put this key in place, it needs to move 2089 | if (bKeys[simulateItem.key] !== k + 1) { 2090 | removes.push(remove(simulate, simulateIndex, simulateItem.key)) 2091 | simulateItem = simulate[simulateIndex] 2092 | // if the remove didn't put the wanted item in place, we need to insert it 2093 | if (!simulateItem || simulateItem.key !== wantedItem.key) { 2094 | inserts.push({key: wantedItem.key, to: k}) 2095 | } 2096 | // items are matching, so skip ahead 2097 | else { 2098 | simulateIndex++ 2099 | } 2100 | } 2101 | else { 2102 | inserts.push({key: wantedItem.key, to: k}) 2103 | } 2104 | } 2105 | else { 2106 | inserts.push({key: wantedItem.key, to: k}) 2107 | } 2108 | k++ 2109 | } 2110 | // a key in simulate has no matching wanted key, remove it 2111 | else if (simulateItem && simulateItem.key) { 2112 | removes.push(remove(simulate, simulateIndex, simulateItem.key)) 2113 | } 2114 | } 2115 | else { 2116 | simulateIndex++ 2117 | k++ 2118 | } 2119 | } 2120 | 2121 | // remove all the remaining nodes from simulate 2122 | while(simulateIndex < simulate.length) { 2123 | simulateItem = simulate[simulateIndex] 2124 | removes.push(remove(simulate, simulateIndex, simulateItem && simulateItem.key)) 2125 | } 2126 | 2127 | // If the only moves we have are deletes then we can just 2128 | // let the delete patch remove these items. 2129 | if (removes.length === deletedItems && !inserts.length) { 2130 | return { 2131 | children: newChildren, 2132 | moves: null 2133 | } 2134 | } 2135 | 2136 | return { 2137 | children: newChildren, 2138 | moves: { 2139 | removes: removes, 2140 | inserts: inserts 2141 | } 2142 | } 2143 | } 2144 | 2145 | function remove(arr, index, key) { 2146 | arr.splice(index, 1) 2147 | 2148 | return { 2149 | from: index, 2150 | key: key 2151 | } 2152 | } 2153 | 2154 | function keyIndex(children) { 2155 | var keys = {} 2156 | var free = [] 2157 | var length = children.length 2158 | 2159 | for (var i = 0; i < length; i++) { 2160 | var child = children[i] 2161 | 2162 | if (child.key) { 2163 | keys[child.key] = i 2164 | } else { 2165 | free.push(i) 2166 | } 2167 | } 2168 | 2169 | return { 2170 | keys: keys, // A hash of key name to index 2171 | free: free, // An array of unkeyed item indices 2172 | } 2173 | } 2174 | 2175 | function appendPatch(apply, patch) { 2176 | if (apply) { 2177 | if (isArray(apply)) { 2178 | apply.push(patch) 2179 | } else { 2180 | apply = [apply, patch] 2181 | } 2182 | 2183 | return apply 2184 | } else { 2185 | return patch 2186 | } 2187 | } 2188 | 2189 | },{"../vnode/handle-thunk":36,"../vnode/is-thunk":37,"../vnode/is-vnode":39,"../vnode/is-vtext":40,"../vnode/is-widget":41,"../vnode/vpatch":44,"./diff-props":46,"x-is-array":23}],48:[function(require,module,exports){ 2190 | module.exports = BaseElement 2191 | 2192 | var h = require('virtual-dom/h') 2193 | var diff = require('virtual-dom/diff') 2194 | var patch = require('virtual-dom/patch') 2195 | var createElement = require('virtual-dom/create-element') 2196 | var toHTML = require('vdom-to-html') 2197 | 2198 | function BaseElement (el) { 2199 | if (!(this instanceof BaseElement)) return new BaseElement(el) 2200 | this.vtree = null 2201 | this.element = null 2202 | this.__appendTo__ = el 2203 | this.__events__ = Object.create(null) 2204 | this.__BaseElementSig__ = 'be-' + Date.now() 2205 | this.__onload__ = new Onload(this.send.bind(this)) 2206 | } 2207 | 2208 | BaseElement.prototype.html = function () { 2209 | return h.apply(this, arguments) 2210 | } 2211 | 2212 | BaseElement.prototype.afterRender = function (vtree) { 2213 | if (this.hasOwnProperty('__BaseElementSig__')) { 2214 | return BaseElement.prototype.render.call(this, vtree) 2215 | } 2216 | return vtree 2217 | } 2218 | 2219 | BaseElement.prototype.render = function (vtree) { 2220 | if (typeof vtree === 'function') { 2221 | vtree = vtree.call(this) 2222 | } 2223 | // Top level vnode must have className for CSS 2224 | // TODO: Check if were using CSS though 2225 | if (vtree && vtree.properties && !vtree.properties.className) { 2226 | vtree.properties.className = this.__BaseElementSig__ 2227 | } 2228 | if (!this.vtree) { 2229 | this.vtree = vtree 2230 | this.element = createElement(this.vtree) 2231 | if (this.__appendTo__) { 2232 | this.__appendTo__.appendChild(this.element) 2233 | } 2234 | } else { 2235 | var patches = diff(this.vtree, vtree) 2236 | this.element = patch(this.element, patches) 2237 | this.vtree = vtree 2238 | } 2239 | return this.vtree 2240 | } 2241 | 2242 | BaseElement.prototype.toString = function () { 2243 | this.render.apply(this, arguments) 2244 | return toHTML(this.vtree) 2245 | } 2246 | 2247 | BaseElement.prototype.send = function (name) { 2248 | var found = this.__events__[name] 2249 | if (!found) return this 2250 | var args = Array.prototype.slice.call(arguments, 1) 2251 | for (var i = 0; i < found.length; i++) { 2252 | var fn = found[i] 2253 | if (typeof fn === 'function') fn.apply(this, args) 2254 | } 2255 | return this 2256 | } 2257 | 2258 | BaseElement.prototype.addEventListener = function (name, cb) { 2259 | if (!Array.isArray(this.__events__[name])) this.__events__[name] = [] 2260 | this.__events__[name].push(cb) 2261 | } 2262 | 2263 | function Onload (cb) { 2264 | this.cb = cb 2265 | } 2266 | Onload.prototype.hook = function (node) { 2267 | var self = this 2268 | setTimeout(function () { 2269 | self.cb('load', node) 2270 | }, 10) 2271 | } 2272 | Onload.prototype.unhook = function (node) { 2273 | var self = this 2274 | setTimeout(function () { 2275 | self.cb('unload', node) 2276 | }, 10) 2277 | } 2278 | 2279 | },{"vdom-to-html":4,"virtual-dom/create-element":15,"virtual-dom/diff":16,"virtual-dom/h":17,"virtual-dom/patch":24}]},{},[48])(48) 2280 | }); -------------------------------------------------------------------------------- /examples/angular.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable */ 2 | 3 | // This example uses ./list.js (an element created with base-element) 4 | // in use with an AngularJS directive: https://docs.angularjs.org/guide/directive 5 | 6 | var List = require('./list.js') 7 | 8 | angular.module('myApp') 9 | .directive('list', function () { 10 | return { 11 | restrict: 'EA', 12 | scope: { 13 | data: '=' 14 | }, 15 | link: function ($scope, $element) { 16 | var list = new List($element[0]) 17 | $scope.$watch('data', function (data) { 18 | list.render(data) 19 | }) 20 | 21 | // When item clicked 22 | list.addEventListener('clicked', function (item) { 23 | window.alert('You clicked ' + item.innerHTML) 24 | }) 25 | 26 | // When item added 27 | list.addEventListener('added', function () { 28 | data.push(String(Math.random() * 9999)) 29 | list.render(data) 30 | }) 31 | } 32 | } 33 | }) 34 | -------------------------------------------------------------------------------- /examples/custom-element.js: -------------------------------------------------------------------------------- 1 | // This example uses ./list.js (an element created with base-element) 2 | // in use with the library "custom-element": https://www.npmjs.com/package/custom-element 3 | 4 | require('webcomponents.js') 5 | 6 | var List = require('./list.js') 7 | var createCustom = require('custom-element') 8 | 9 | // Mock data for the element 10 | var data = ['one', 'two', 'three'] 11 | 12 | var CustomHTMLElement = createCustom().on('created', function () { 13 | var self = this 14 | // When the element is created, consume List's API 15 | // Attaching to the element so it can be called via the element itself, totally optional though 16 | self.list = new List(self) 17 | self.list.render(data) 18 | self.list.addEventListener('clicked', function (item) { 19 | window.alert('You clicked ' + item.innerHTML) 20 | }) 21 | self.list.addEventListener('added', function () { 22 | data.push(String(Math.random() * 9999)) 23 | self.list.render(data) 24 | }) 25 | }).on('attribute', function (key) { 26 | // When an attribute is set, add the attribute value to our data 27 | var test = this.getAttribute(key) 28 | data.push(test) 29 | this.list.render(data) 30 | }) 31 | 32 | // Register our custom element 33 | var CustomElement = document.registerElement('custom-element', CustomHTMLElement) 34 | 35 | // Create and append it to document.body 36 | var el = new CustomElement() 37 | document.body.appendChild(el) 38 | 39 | // 2s later, modify the attribute of the element 40 | setTimeout(function () { 41 | el.setAttribute('data-test', 'I was added because of an attribute') 42 | }, 2000) 43 | -------------------------------------------------------------------------------- /examples/ember.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable */ 2 | 3 | // This example uses ./list.js (an element created with base-element) 4 | // in use with an Ember.js Component: http://emberjs.com/api/classes/Ember.Component.html 5 | 6 | var List = require('./list.js') 7 | 8 | var MyListComponent = Ember.Component.extend({ 9 | data: ['one', 'two', 'three'], 10 | didInsertElement: function () { 11 | this._super() 12 | 13 | var data = this.get('data') 14 | 15 | var list = new List(this.element) 16 | list.render(data) 17 | 18 | // When item clicked 19 | list.addEventListener('clicked', function (item) { 20 | window.alert('You clicked ' + item.innerHTML) 21 | }) 22 | 23 | // When item added 24 | list.addEventListener('added', function () { 25 | data.push(String(Math.random() * 9999)) 26 | list.render(data) 27 | }) 28 | 29 | } 30 | }) 31 | -------------------------------------------------------------------------------- /examples/es6.js: -------------------------------------------------------------------------------- 1 | // This example shows an example "complex" slideshow element in use with ES6 2 | // syntax care of babel: https://babeljs.io/ 3 | 4 | import BaseElement from '../index.js' 5 | 6 | class Slides extends BaseElement { 7 | constructor (el) { 8 | super(el) 9 | // Create a property for the actively shown slide 10 | this.activeIndex = 0 11 | // When a slide is selected, set that as the activeIndex then render 12 | this.addEventListener('select', idx => { 13 | this.activeIndex = idx 14 | this.render(this.data) 15 | }) 16 | // Inline styles for this element 17 | this.style = { 18 | display: 'table', 19 | margin: '0', 20 | padding: '0' 21 | } 22 | } 23 | render (slides) { 24 | // Save a local copy of data for re-rendering 25 | this.data = slides 26 | slides = slides.map((slide, idx) => { 27 | let tag = 'li' 28 | // If this is the active slide, set the content and .active class 29 | if (idx === this.activeIndex) { 30 | tag = 'li.active' 31 | this.content = slide.content 32 | } 33 | // Render each
  • 34 | return this.html(tag, { 35 | style: { 36 | listStyle: 'none', 37 | float: 'left', 38 | margin: '0 .5em' 39 | } 40 | }, [ 41 | this.html('button', { 42 | onclick: e => { 43 | // onclick send a select event up with index 44 | this.send('select', idx) 45 | } 46 | }, slide.name) 47 | ]) 48 | }) 49 | // Build our tree with
      buttons
    content
    50 | let vtree = this.html('div', [ 51 | this.html('ul.sections', this, slides), 52 | this.html('.content', { 53 | style: { 54 | padding: '1em' 55 | } 56 | }, this.content) 57 | ]) 58 | // Return the tree wrapped in afterRender 59 | return this.afterRender(vtree) 60 | } 61 | } 62 | 63 | // The end user API from here on down 64 | let slides = new Slides(document.body) 65 | slides.render([ 66 | { name: 'intro', content: 'Intro...' }, 67 | { name: 'examples', content: 'Examples...' }, 68 | { name: 'outro', content: 'Outro...' } 69 | ]) 70 | -------------------------------------------------------------------------------- /examples/list.js: -------------------------------------------------------------------------------- 1 | // An example element that can gets used by all the other examples 2 | 3 | module.exports = List 4 | 5 | var BaseElement = require('../index.js') 6 | var attachCSS = require('attach-css') 7 | 8 | function List (el) { 9 | BaseElement.call(this, el) 10 | this.className = 'list' 11 | } 12 | List.prototype = Object.create(BaseElement.prototype) 13 | 14 | List.prototype.render = function (items) { 15 | var self = this 16 | 17 | // Create li upon click just lets us know it was clicked 18 | items = items.map(function (item) { 19 | return self.html('li', { 20 | onclick: function (e) { 21 | self.send('clicked', e.target) 22 | } 23 | }, item) 24 | }) 25 | 26 | // Add button that adds new li's to list 27 | items.unshift(this.html('li', this.html('button', { 28 | onclick: function (e) { 29 | self.send('added') 30 | } 31 | }, ['add item']))) 32 | 33 | return this.afterRender(this.html('ul', this, items)) 34 | } 35 | 36 | // Localized CSS can be returned using attachCSS() 37 | List.prototype.css = function () { 38 | // Can be a string, brfs a style.css, or css preprocessor 39 | return attachCSS(` 40 | * { 41 | font-family: Helvetica, sans-serif; 42 | } 43 | ul { 44 | margin: 0; 45 | padding: 0; 46 | } 47 | ul li { 48 | list-style: none; 49 | border-bottom: 1px solid #ddd; 50 | padding: .3em 0; 51 | } 52 | ul li:hover { 53 | background-color: #eee; 54 | transition: .5s; 55 | padding-left: .5em; 56 | cursor: pointer; 57 | } 58 | li:first-child { 59 | padding: .3em; 60 | border-bottom: 1px solid #333; 61 | text-align: right; 62 | } 63 | button { 64 | border: none; 65 | padding: .3em .5em; 66 | background-color: #00bcd4; 67 | color: white; 68 | font-size: 1.2em; 69 | cursor: pointer; 70 | } 71 | button:hover { 72 | padding: .3em 1em; 73 | transition: .5s; 74 | } 75 | `, this.vtree) 76 | } 77 | -------------------------------------------------------------------------------- /examples/nesting.js: -------------------------------------------------------------------------------- 1 | var BaseElement = require('../index.js') 2 | 3 | // First Bear extends BaseElement 4 | function Bear (el) { 5 | BaseElement.call(this, el) 6 | } 7 | Bear.prototype = Object.create(BaseElement.prototype) 8 | Bear.prototype.render = function (items) { 9 | var self = this 10 | var vtree = items.map(function (item) { 11 | return self.html('li.bear', item) 12 | }) 13 | return this.afterRender(vtree) 14 | } 15 | 16 | // Second Grizzly extends Bear 17 | function Grizzly (el) { 18 | Bear.call(this, el) 19 | } 20 | Grizzly.prototype = Object.create(Bear.prototype) 21 | Grizzly.prototype.render = function (data) { 22 | var bears = Bear.prototype.render(data.bears) 23 | var vtree = this.html('ul.grizzly', [ 24 | this.html('li', 'Top'), 25 | bears, 26 | this.html('li', 'Bottom') 27 | ]) 28 | return this.afterRender(vtree) 29 | } 30 | 31 | // Third App extends Grizzly 32 | function App (el) { 33 | Grizzly.call(this, el) 34 | } 35 | App.prototype = Object.create(Grizzly.prototype) 36 | App.prototype.render = function (data) { 37 | var middle = Grizzly.prototype.render(data) 38 | var vtree = this.html('div.app', [data.heading, middle]) 39 | return this.afterRender(vtree) 40 | } 41 | 42 | // The end user only cares about App 43 | var app = new App(document.body) 44 | app.render({ 45 | heading: 'Bears', 46 | bears: ['Grizzly', 'Polar', 'Brown'] 47 | }) 48 | -------------------------------------------------------------------------------- /examples/react.js: -------------------------------------------------------------------------------- 1 | // This example uses ./list.js (an element created with base-element) 2 | // in use with a React component: https://facebook.github.io/react/ 3 | 4 | var List = require('./list.js') 5 | var React = require('react') 6 | 7 | // Create a React component that wraps the List 8 | var XList = React.createClass({ 9 | propTypes: { 10 | items: React.PropTypes.arrayOf(React.propTypes.string) 11 | }, 12 | componentDidMount: function () { 13 | var list = new List(React.findDOMNode(this)) 14 | var data = this.props.items 15 | list.render(data) 16 | 17 | // When item clicked 18 | list.addEventListener('clicked', function (item) { 19 | window.alert('You clicked ' + item.innerHTML) 20 | }) 21 | 22 | // When item added 23 | list.addEventListener('added', function () { 24 | data.push(String(Math.random() * 9999)) 25 | list.render(data) 26 | }) 27 | }, 28 | render: function () { 29 | return React.createElement('div') 30 | } 31 | }) 32 | 33 | React.render( 34 | React.createElement(XList, { items: ['one', 'two', 'three'] }), 35 | document.body 36 | ) 37 | -------------------------------------------------------------------------------- /examples/server-side.js: -------------------------------------------------------------------------------- 1 | var List = require('./list.js') 2 | var http = require('http') 3 | var browserify = require('browserify') 4 | var path = require('path') 5 | 6 | // Create our server 7 | http.createServer(function (req, res) { 8 | // Some crude routing 9 | if (req.url === '/' || req.url === '/index.html') { 10 | index(res) 11 | } else if (req.url === '/bundle.js') { 12 | script(res) 13 | } else { 14 | res.end('') 15 | } 16 | }).listen(1337) 17 | console.log('Server running at http://localhost:1337/') 18 | 19 | // Our index.html 20 | function index (res) { 21 | res.writeHead(200, {'Content-Type': 'text/html'}) 22 | 23 | // Initial data for the list 24 | var data = ['This is', 'the initial', 'content'] 25 | 26 | // Create/render the list 27 | var list = new List() 28 | 29 | // Convert the list element to static HTML and send out 30 | var html = list.toString(data) 31 | 32 | // Serve up our template 33 | var template = ` 34 | ${html} 35 | 36 | ` 37 | res.end(template) 38 | } 39 | 40 | // Our script that will take over after the initial static render 41 | function script (res) { 42 | var b = browserify() 43 | b.add(path.join(__dirname, 'standalone.js')) 44 | b.bundle(function (err, buf) { 45 | if (err) throw err 46 | res.writeHead(200, {'Content-Type': 'application/javascript'}) 47 | res.end(buf) 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /examples/standalone.js: -------------------------------------------------------------------------------- 1 | // This example uses ./list.js (an element created with base-element) 2 | // by itself :) 3 | 4 | var List = require('./list.js') 5 | 6 | // Data for the list 7 | var data = ['one', 'two', 'three'] 8 | 9 | // Clear out any staticly rendered html (if this script is being served by server-side.js) 10 | document.body.innerHTML = '' 11 | 12 | // Create a list 13 | var list = new List(document.body) 14 | list.className = 'my-list' 15 | list.render(data) 16 | 17 | // When item clicked 18 | list.addEventListener('clicked', function (item) { 19 | window.alert('You clicked ' + item.innerHTML) 20 | }) 21 | 22 | // When item added 23 | list.addEventListener('added', function () { 24 | data.push(String(Math.random() * 9999)) 25 | list.render(data) 26 | }) 27 | 28 | // WIP: CSS should be written out to a separate file or nonce'd 29 | // this part is just for testing and will be removed 30 | var style = document.createElement('style') 31 | style.innerHTML = list.css() 32 | document.head.appendChild(style) 33 | -------------------------------------------------------------------------------- /examples/virtual-dom.js: -------------------------------------------------------------------------------- 1 | // This example uses ./list.js (an element created with base-element) 2 | // in use with virtual-dom: https://github.com/Matt-Esch/virtual-dom 3 | 4 | var List = require('./list.js') 5 | 6 | var h = require('virtual-dom/h') 7 | var diff = require('virtual-dom/diff') 8 | var patch = require('virtual-dom/patch') 9 | var createElement = require('virtual-dom/create-element') 10 | 11 | // Data for the list 12 | var data = ['one', 'two', 'three'] 13 | 14 | // Create a list that does not automatically appendTo 15 | // as we'll be handling that with virtual-dom 16 | var list = new List() 17 | 18 | // When item clicked 19 | list.addEventListener('clicked', function (item) { 20 | window.alert('You clicked ' + item.innerHTML) 21 | }) 22 | 23 | // When item added 24 | list.addEventListener('added', function () { 25 | data.push(String(Math.random() * 9999)) 26 | list.render(data) 27 | }) 28 | 29 | // Main render function 30 | function render () { 31 | return h('.my-list', [ 32 | 'With ' + data.length + ' rows:', 33 | list.render(data) 34 | ]) 35 | } 36 | 37 | // Initial DOM tree render 38 | var tree = render() 39 | var rootNode = createElement(tree) 40 | document.body.appendChild(rootNode) 41 | 42 | // Main render loop 43 | setInterval(function () { 44 | var newTree = render() 45 | var patches = diff(tree, newTree) 46 | rootNode = patch(rootNode, patches) 47 | tree = newTree 48 | }, 100) 49 | -------------------------------------------------------------------------------- /examples/webcomponent.js: -------------------------------------------------------------------------------- 1 | /*global HTMLElement:false*/ 2 | 3 | // This example uses ./list.js (an element created with base-element) 4 | // in use with web components: http://webcomponents.org/ 5 | 6 | var List = require('./list.js') 7 | 8 | var data = ['one', 'two', 'three'] 9 | 10 | // Create a proto for our custom element 11 | var proto = Object.create(HTMLElement.prototype) 12 | proto.createdCallback = function () { 13 | // Create a new list and append to this custom element 14 | var list = new List(this) 15 | list.render(data) 16 | 17 | // When item clicked 18 | list.addEventListener('clicked', function (item) { 19 | window.alert('You clicked ' + item.innerHTML) 20 | }) 21 | 22 | // When item added 23 | list.addEventListener('added', function () { 24 | data.push(String(Math.random() * 9999)) 25 | list.render(data) 26 | }) 27 | } 28 | 29 | // Create our tag using the above proto 30 | var XList = document.registerElement('x-list', { 31 | prototype: proto 32 | }) 33 | document.body.appendChild(new XList()) 34 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = BaseElement 2 | 3 | var h = require('virtual-dom/h') 4 | var diff = require('virtual-dom/diff') 5 | var patch = require('virtual-dom/patch') 6 | var createElement = require('virtual-dom/create-element') 7 | var toHTML = require('vdom-to-html') 8 | var EventTarget = require('dom-event-target') 9 | var inherits = require('inherits') 10 | var nextTick = require('next-tick') 11 | 12 | function BaseElement (el) { 13 | if (!(this instanceof BaseElement)) return new BaseElement(el) 14 | EventTarget.call(this) 15 | this.vtree = null 16 | this.element = null 17 | this.__appendTo__ = el 18 | this.__BaseElementSig__ = 'be-' + Date.now() 19 | this.__onload__ = new Onload(this.send.bind(this)) 20 | } 21 | inherits(BaseElement, EventTarget) 22 | 23 | BaseElement.prototype.html = function BaseElement_html () { 24 | return h.apply(this, arguments) 25 | } 26 | 27 | BaseElement.prototype.afterRender = function BaseElement_afterRender (vtree) { 28 | if (this.hasOwnProperty('__BaseElementSig__')) { 29 | return BaseElement.prototype.render.call(this, vtree) 30 | } 31 | return vtree 32 | } 33 | 34 | BaseElement.prototype.render = function BaseElement_render (vtree) { 35 | if (typeof vtree === 'function') { 36 | vtree = vtree.call(this) 37 | } 38 | // Top level vnode must have className for CSS 39 | // TODO: Check if were using CSS though 40 | if (vtree && vtree.properties && !vtree.properties.className) { 41 | vtree.properties.className = this.__BaseElementSig__ 42 | } 43 | if (!this.vtree) { 44 | this.vtree = vtree 45 | this.element = createElement(this.vtree) 46 | if (this.__appendTo__) { 47 | this.__appendTo__.appendChild(this.element) 48 | } 49 | } else { 50 | var patches = diff(this.vtree, vtree) 51 | this.element = patch(this.element, patches) 52 | this.vtree = vtree 53 | } 54 | return this.vtree 55 | } 56 | 57 | BaseElement.prototype.toString = function BaseElement_toString () { 58 | this.render.apply(this, arguments) 59 | return toHTML(this.vtree) 60 | } 61 | 62 | function Onload (cb) { 63 | this.cb = cb 64 | } 65 | Onload.prototype.hook = function BaseElement_hook (node) { 66 | var self = this 67 | nextTick(function BaseElement_hook_nextTick () { 68 | self.cb('load', node) 69 | }) 70 | } 71 | Onload.prototype.unhook = function BaseElement_unhook (node) { 72 | var self = this 73 | nextTick(function BaseElement_unhook_nextTick () { 74 | self.cb('unload', node) 75 | }) 76 | } 77 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "base-element", 3 | "version": "3.0.1", 4 | "description": "An element authoring library for creating standalone and performant elements.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "standard && node test/server.js && zuul --local 9966 -- test/index.js", 8 | "ci": "standard && node test/server.js && zuul -- test/index.js", 9 | "start": "budo examples/standalone.js", 10 | "build": "browserify index.js --standalone BaseElement -o dist/base-element.js", 11 | "prepublish": "npm run build" 12 | }, 13 | "author": "Kyle Robinson Young (http://dontkry.com)", 14 | "license": "MIT", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/shama/base-element.git" 18 | }, 19 | "files": [ 20 | "index.js", 21 | "dist/" 22 | ], 23 | "standard": { 24 | "ignore": [ 25 | "**/dist/**" 26 | ] 27 | }, 28 | "keywords": [ 29 | "virtual", 30 | "dom", 31 | "element", 32 | "diff", 33 | "browser" 34 | ], 35 | "devDependencies": { 36 | "attach-css": "^1.1.0", 37 | "babelify": "^6.0.2", 38 | "browser-test-helpers": "^1.0.0", 39 | "browserify": "^10.0.0", 40 | "budo": "^4.0.0", 41 | "standard": "^4.0.0", 42 | "tap-spec": "^4.0.0", 43 | "tape": "^4.0.0", 44 | "zuul": "^3.0.0" 45 | }, 46 | "dependencies": { 47 | "dom-event-target": "~1.0.0", 48 | "inherits": "~2.0.1", 49 | "isarray": "0.0.1", 50 | "next-tick": "~0.2.2", 51 | "vdom-to-html": "^2.1.1", 52 | "virtual-dom": "^2.0.1" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/fixtures/button.js: -------------------------------------------------------------------------------- 1 | module.exports = Button 2 | 3 | var BaseElement = require('../../index.js') 4 | 5 | function Button (el) { 6 | BaseElement.call(this, el) 7 | var self = this 8 | this.onclick = function (e) { 9 | self.send('clicked', e.target) 10 | } 11 | } 12 | Button.prototype = Object.create(BaseElement.prototype) 13 | 14 | Button.prototype.render = function (label) { 15 | var vtree = this.html('button', this, label) 16 | return this.afterRender(vtree) 17 | } 18 | -------------------------------------------------------------------------------- /test/fixtures/nested.js: -------------------------------------------------------------------------------- 1 | module.exports = Top 2 | 3 | var BaseElement = require('../../index.js') 4 | 5 | function Bottom (el) { 6 | BaseElement.call(this, el) 7 | } 8 | Bottom.prototype = Object.create(BaseElement.prototype) 9 | 10 | Bottom.prototype.render = function (data) { 11 | var vtree = this.html('li.bottom', data) 12 | return this.afterRender(vtree) 13 | } 14 | 15 | function Middle (el) { 16 | Bottom.call(this, el) 17 | } 18 | Middle.prototype = Object.create(Bottom.prototype) 19 | 20 | Middle.prototype.render = function (data) { 21 | var bottom = Bottom.prototype.render(data) 22 | var vtree = this.html('ul.middle', bottom) 23 | return this.afterRender(vtree) 24 | } 25 | 26 | function Top (el) { 27 | Middle.call(this, el) 28 | } 29 | Top.prototype = Object.create(Middle.prototype) 30 | 31 | Top.prototype.render = function (data) { 32 | var middle = Middle.prototype.render(data) 33 | var vtree = this.html('div.top', middle) 34 | return this.afterRender(vtree) 35 | } 36 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var help = require('browser-test-helpers')() 2 | var test = require('tape') 3 | 4 | var createElement = require('../index.js') 5 | var Button = require('./fixtures/button.js') 6 | var Nested = require('./fixtures/nested.js') 7 | 8 | test('simple down and up', function (t) { 9 | setUp(function (fixture) { 10 | var expected = 'Testing ' + Math.random() 11 | var button = new Button(fixture) 12 | button.addEventListener('clicked', function (el) { 13 | t.equal(el.innerHTML, expected, 'data was sent down and event came up') 14 | tearDown(fixture, t.end) 15 | }) 16 | button.render(expected) 17 | help.click(button.element) 18 | }) 19 | }) 20 | 21 | test('renders nested elements', function (t) { 22 | t.plan(1) 23 | setUp(function (fixture) { 24 | var nested = new Nested(fixture) 25 | nested.render('test') 26 | t.equal(fixture.innerHTML, '
    • test
    ') 27 | tearDown(fixture, t.end) 28 | }) 29 | }) 30 | 31 | test('functional API', function (t) { 32 | t.plan(1) 33 | setUp(function (fixture) { 34 | var el = createElement(fixture) 35 | el.render(function () { 36 | return this.html('.test', 'testing') 37 | }) 38 | t.equal(fixture.innerHTML, '
    testing
    ') 39 | tearDown(fixture, t.end) 40 | }) 41 | }) 42 | 43 | test('load event fired', function (t) { 44 | t.plan(1) 45 | setUp(function (fixture) { 46 | var button = new Button(fixture) 47 | button.addEventListener('load', function (node) { 48 | t.equal(node.innerHTML, 'Test') 49 | tearDown(fixture, t.end) 50 | }) 51 | button.render('Test') 52 | }) 53 | }) 54 | 55 | test('unload event fired', function (t) { 56 | t.plan(1) 57 | setUp(function (fixture) { 58 | var button = new Button() 59 | button.addEventListener('unload', function (node) { 60 | t.equal(node.innerHTML, 'Test') 61 | tearDown(fixture, t.end) 62 | }) 63 | 64 | var el = createElement(fixture) 65 | el.render(function () { 66 | return this.html('div', button.render('Test')) 67 | }) 68 | 69 | setTimeout(function () { 70 | el.render(function () { 71 | return this.html('div') 72 | }) 73 | }, 100) 74 | }) 75 | }) 76 | 77 | test('toString', function (t) { 78 | t.plan(1) 79 | setUp(function (fixture) { 80 | var nested = new Nested(fixture) 81 | t.equal(nested.toString('test'), '
    • test
    ') 82 | tearDown(fixture, t.end) 83 | }) 84 | }) 85 | 86 | function setUp (cb) { 87 | var fixture = document.createElement('div') 88 | fixture.setAttribute('id', 'fixture' + Date.now()) 89 | document.body.appendChild(fixture) 90 | cb(fixture) 91 | } 92 | 93 | function tearDown (fixture, cb) { 94 | fixture.parentNode.removeChild(fixture) 95 | cb() 96 | } 97 | -------------------------------------------------------------------------------- /test/server.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | 3 | var Nested = require('./fixtures/nested.js') 4 | 5 | test('toString on server side', function (t) { 6 | t.plan(1) 7 | var nested = new Nested() 8 | t.equal(nested.toString('test'), '
    • test
    ') 9 | t.end() 10 | }) 11 | --------------------------------------------------------------------------------