jails-js 4.2.2 → 5.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/Element.ts ADDED
@@ -0,0 +1,117 @@
1
+ import { stripTemplateTag, dup, rAF, createTemplate, uuid } from './utils'
2
+ import morphdom from 'morphdom'
3
+ import { setSodaConfig } from './soda-config'
4
+
5
+ const sodajs = setSodaConfig()
6
+ const templates = {}
7
+
8
+ export const Element = ( el:HTMLElement ) => {
9
+
10
+ stripTemplateTag( el )
11
+
12
+ let updates = []
13
+
14
+ const model = Object.assign({}, JSON.parse(el.dataset.initialState || '{}'))
15
+ const morphdomOptions = lifecycle(el)
16
+ const { template, tplid } = getTemplateData(el)
17
+
18
+ const api = {
19
+
20
+ tplid,
21
+ el,
22
+ template,
23
+ model,
24
+ parent: {},
25
+ view : _ => _,
26
+ instances:{},
27
+ destroyers:[],
28
+ promises: [],
29
+ parentUpdate: data => null,
30
+
31
+ dispose(){
32
+ if( api.promises.length ){
33
+ Promise.all(api.promises)
34
+ .then(_ => api.destroyers.forEach( destroy => destroy(api) ))
35
+ }else {
36
+ api.destroyers.forEach( destroy => destroy(api) )
37
+ }
38
+ },
39
+
40
+ update( data = {}, isParentUpdate ) {
41
+
42
+ if( !document.body.contains(el) )
43
+ return
44
+
45
+ updates.push( data )
46
+
47
+ rAF( _ => {
48
+
49
+ if( updates.length ) {
50
+
51
+ const newdata = {}
52
+ updates.forEach( d => Object.assign(newdata, d ) )
53
+ updates = []
54
+
55
+ api.model = Object.assign( api.model, newdata )
56
+
57
+ if( isParentUpdate ) {
58
+ api.parentUpdate( api.model )
59
+ }
60
+
61
+ const newhtml = sodajs(template, api.view(dup(api.model)))
62
+ morphdom( el, newhtml, morphdomOptions )
63
+
64
+ Array
65
+ .from( el.querySelectorAll('[data-component]') )
66
+ .forEach( node => {
67
+ if( !node.__instance__ ) return
68
+ const { parent, ...model } = api.model
69
+ const initialState = node.dataset.initialState? JSON.parse(node.dataset.initialState): {}
70
+ const newmodel = Object.assign(initialState, { parent:model })
71
+ node.__instance__.update( newmodel, true )
72
+ })
73
+ }
74
+ })
75
+ }
76
+ }
77
+
78
+ el.__instance__ = api
79
+
80
+ return api
81
+ }
82
+
83
+ const lifecycle = ( element: HTMLElement ) => {
84
+ return {
85
+ onBeforeElUpdated: update(element),
86
+ onBeforeElChildrenUpdated: update(element),
87
+ getNodeKey(node) {
88
+ if( node.nodeType === 1 && node.dataset.tplid )
89
+ return node.dataset.key || node.dataset.tplid
90
+ return false
91
+ }
92
+ }
93
+ }
94
+
95
+ const update = ( element: HTMLElement ) => ( node: HTMLElement, toEl: HTMLElement ) => {
96
+ if ( node.isEqualNode(toEl) )
97
+ return false
98
+ if( node.nodeType == 1 ) {
99
+ if( 'static' in node.dataset )
100
+ return false
101
+ }
102
+ return true
103
+ }
104
+
105
+ const getTemplateData = ( element: HTMLElement ) => {
106
+ if( element.getAttribute('tplid') ) {
107
+ const tplid = element.getAttribute('tplid')
108
+ const template = templates[tplid]
109
+ return { tplid, template }
110
+ }else {
111
+ const tplid = uuid()
112
+ element.setAttribute('tplid', tplid)
113
+ templates[tplid] = createTemplate(element.outerHTML, templates)
114
+ const template = templates[tplid]
115
+ return { tplid, template }
116
+ }
117
+ }
package/src/Scanner.ts ADDED
@@ -0,0 +1,26 @@
1
+
2
+ export const Scanner = {
3
+
4
+ scan( node: HTMLElement, callback ) {
5
+ if( node.nodeType === 1 ) {
6
+ const list : Array<HTMLElement> = Array.from( node.querySelectorAll('[data-component]') )
7
+ const elements : Array<HTMLElement> = node.dataset.component? [node].concat(list) : list
8
+ if( elements.length ) {
9
+ elements.reverse().forEach( callback )
10
+ }
11
+ }
12
+ },
13
+
14
+ observe( target: HTMLElement, onAdd, onRemove ) {
15
+ const observer = new MutationObserver(mutations => mutations.forEach( mutation => {
16
+ if (mutation.type === 'childList') {
17
+ if (mutation.addedNodes.length) {
18
+ Array.from( mutation.addedNodes ).forEach( node => Scanner.scan(node, onAdd) )
19
+ } else if (mutation.removedNodes.length) {
20
+ Array.from( mutation.removedNodes ).forEach( node => Scanner.scan(node, onRemove) )
21
+ }
22
+ }
23
+ }))
24
+ observer.observe(target, { childList: true, subtree: true })
25
+ }
26
+ }
package/src/index.ts ADDED
@@ -0,0 +1,60 @@
1
+ import { Element } from './Element'
2
+ import { Scanner } from './Scanner'
3
+ import { Component } from './Component'
4
+ import { stripTemplateTag } from './utils'
5
+
6
+ const components = {}
7
+
8
+ export default {
9
+
10
+ start() {
11
+
12
+ const body: HTMLElement = document.body
13
+
14
+ stripTemplateTag( body )
15
+
16
+ Scanner.scan( body, createElement )
17
+ Scanner.observe( body, createElement, disposeElement )
18
+ },
19
+
20
+ register( name, module, dependencies = {} ) {
21
+ components[name] = { name, module, dependencies }
22
+ }
23
+ }
24
+
25
+ const createElement = ( element: HTMLElement ) => {
26
+
27
+ const ElementInterface = Element( element )
28
+ const names = element.dataset.component.split(/\s/)
29
+
30
+ names.forEach( name => {
31
+
32
+ const C = components[name]
33
+
34
+ if( !C ) {
35
+ console.warn(`Jails - Module ${name} not registered`)
36
+ return
37
+ }
38
+
39
+ const { module, dependencies } = C
40
+ ElementInterface.model = Object.assign({}, module.model, ElementInterface.model )
41
+
42
+ const base = Component({ name, element, dependencies, ElementInterface })
43
+ const promise = module.default(base)
44
+
45
+ if( promise && promise.then ) {
46
+ ElementInterface.promises.push(promise)
47
+ }
48
+
49
+ base.__initialize()
50
+ ElementInterface.view = module.view || ElementInterface.view
51
+ ElementInterface.instances[name] = { methods: {} }
52
+ })
53
+
54
+ ElementInterface.update()
55
+ }
56
+
57
+ const disposeElement = ( node ) => {
58
+ if( node.__instance__)
59
+ node.__instance__.dispose()
60
+ }
@@ -1,8 +1,10 @@
1
- import { uuid } from './utils'
1
+ import sodajs from 'sodajs'
2
2
 
3
- export default ({ sodajs: soda, models }) => {
3
+ export const setSodaConfig = () => {
4
4
 
5
- soda.directive('repeat', {
5
+ sodajs.prefix('v-')
6
+
7
+ sodajs.directive('repeat', {
6
8
 
7
9
  priority: 10,
8
10
 
@@ -52,17 +54,13 @@ export default ({ sodajs: soda, models }) => {
52
54
  itemScope[trackName] = i
53
55
  itemScope[itemName] = repeatObj[i]
54
56
 
55
- const components = findComponents(itemNode)
56
-
57
- components.forEach(node => {
58
- const ID = uuid()
59
- node.setAttribute('data-model-id', ID)
60
- models[ID] = itemScope
61
- })
62
-
63
57
  itemNode.removeAttribute(`${this._prefix}repeat`)
58
+
64
59
  el.parentNode.insertBefore(itemNode, el)
65
60
 
61
+ Array.from(itemNode.querySelectorAll('[data-component]'))
62
+ .forEach( node => node.setAttribute('data-initial-state', JSON.stringify(itemScope)) )
63
+
66
64
  compileNode(itemNode, itemScope)
67
65
  }
68
66
 
@@ -84,11 +82,6 @@ export default ({ sodajs: soda, models }) => {
84
82
  el.innerHTML = ''
85
83
  }
86
84
  })
87
- }
88
85
 
89
- function findComponents(el) {
90
- const isComponent = el.getAttribute('data-component')
91
- const component = isComponent ? [el] : []
92
- const childComponents = Array.prototype.slice.call(el.querySelectorAll('[data-component]'))
93
- return component.concat(childComponents)
86
+ return sodajs
94
87
  }
@@ -3,38 +3,6 @@ export const rAF = (fn) => {
3
3
  (requestAnimationFrame || setTimeout)(fn, 1000 / 60)
4
4
  }
5
5
 
6
- export const nextFrame = (fn) => {
7
- rAF(() => rAF(fn))
8
- }
9
-
10
- export const addClass = (element) => (string) => {
11
- string.split(/\s/).map(item => element.classList.add(item))
12
- }
13
-
14
- export const removeClass = (element) => (string) => {
15
- string.split(/\s/).map(item => element.classList.remove(item))
16
- }
17
-
18
- export const getPrefix = (object) => {
19
- for (let key in object)
20
- if (key in document.body.style)
21
- return object[key]
22
- }
23
-
24
- export const animationEnd = getPrefix({
25
- animation: 'animationend',
26
- OAnimation: 'oAnimationEnd',
27
- MozAnimation: 'animationend',
28
- WebkitAnimation: 'webkitAnimationEnd'
29
- })
30
-
31
- export const transitionEnd = getPrefix({
32
- transition: 'transitionend',
33
- OTransition: 'oTransitionEnd',
34
- MozTransition: 'transitionend',
35
- WebkitTransition: 'webkitTransitionEnd'
36
- })
37
-
38
6
  export const uuid = () => {
39
7
  return 'xxxxxxxx'.replace(/[xy]/g, (c) => {
40
8
  const r = Math.random() * 8 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8)
@@ -42,36 +10,32 @@ export const uuid = () => {
42
10
  })
43
11
  }
44
12
 
45
- export const setIds = (acc, element) => {
46
- const id = uuid()
47
- element.setAttribute('data-reactor-id', id)
48
- acc[id] = element.outerHTML.replace(/<(x-)?template*.>|<\/(x-)?template>/g, '')
49
- return acc
13
+ export const stripTemplateTag = ( element ) => {
14
+ const templates = Array.from(element.querySelectorAll('template'))
15
+ // https://gist.github.com/harmenjanssen/07e425248779c65bc5d11b02fb913274
16
+ templates.forEach( template => {
17
+ template.parentNode.replaceChild(template.content, template )
18
+ })
50
19
  }
51
20
 
52
- export const createTemplates = ( html, type = 'div' ) => {
53
-
54
- const SELECTOR = '[data-component]:not([data-reactor-id])'
55
- const virtual = document.createElement(type)
56
- virtual.innerHTML = html.replace(/<template*.>/g, '<x-template>').replace(/<\/template>/g, '</x-template>')
21
+ export const dup = (o) => {
22
+ return JSON.parse( JSON.stringify(o) )
23
+ }
57
24
 
58
- const elements = Array.from(virtual.querySelectorAll(SELECTOR))
59
- const templates = elements.reverse().reduce(setIds, {})
25
+ export const createTemplate = ( html, templates ) => {
60
26
 
61
- return {
62
- templates,
63
- html: virtual.innerHTML.replace(/<x-template*.>/g, '<template>').replace(/<\/x-template>/g, '</template>')
64
- }
65
- }
27
+ const vroot = document.createElement('div')
28
+ vroot.innerHTML = html
29
+ stripTemplateTag( vroot )
66
30
 
67
- export const dup = (object) => {
68
- return JSON.parse(JSON.stringify(object))
69
- }
31
+ Array
32
+ .from(vroot.querySelectorAll('[data-component]'))
33
+ .forEach( c => {
34
+ const tplid = c.getAttribute('tplid')
35
+ const cache = templates[tplid]
36
+ if( cache )
37
+ c.outerHTML = cache
38
+ })
70
39
 
71
- export const getParent = (el, selector) => {
72
- let elem = el.parentNode
73
- for ( ; elem && elem !== document; elem = elem.parentNode ) {
74
- if ( elem.matches( selector ) ) return elem
75
- }
76
- return null
40
+ return vroot.innerHTML
77
41
  }
package/tsconfig.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": [ "dom" ],
4
+ "sourceMap": true
5
+ }
6
+ }
@@ -0,0 +1,35 @@
1
+ const path = require('path')
2
+
3
+ module.exports = {
4
+
5
+ devtool: 'source-map',
6
+
7
+ resolve: {
8
+ extensions: ['.ts', '.js', '.json']
9
+ },
10
+
11
+ entry: {
12
+ jails: './src/index.ts'
13
+ },
14
+
15
+ module: {
16
+ rules: [
17
+ {
18
+ test: /\.ts$/,
19
+ exclude: [/node_modules/],
20
+ loader: 'ts-loader',
21
+ options: {
22
+ transpileOnly: true
23
+ }
24
+ }
25
+ ]
26
+ },
27
+
28
+ output: {
29
+ path : path.resolve(__dirname, './dist'),
30
+ filename : '[name].js',
31
+ libraryTarget : 'umd',
32
+ library : 'jails',
33
+ umdNamedDefine: true
34
+ }
35
+ }
package/.babelrc DELETED
@@ -1,16 +0,0 @@
1
- {
2
- "comments": true,
3
- "presets": [
4
- [
5
- "env",
6
- {
7
- "targets": {
8
- "browsers": [
9
- "ie 11"
10
- ]
11
- },
12
- "useBuiltIns": true
13
- }
14
- ]
15
- ]
16
- }
package/.eslintignore DELETED
@@ -1,2 +0,0 @@
1
- node_modules/*
2
- build/*
package/.eslintrc DELETED
@@ -1,20 +0,0 @@
1
- {
2
- "rules": {
3
- "indent": [2,"tab", {"SwitchCase":1}],
4
- "quotes": [2,"single"],
5
- "semi": [2,"never"],
6
- "no-unused-vars": ["warn", { "vars": "all", "args": "none" }],
7
- "no-console": 0
8
- },
9
- "env": {
10
- "es6": true,
11
- "browser": true,
12
- "node" : true,
13
- "jquery": true,
14
- },
15
- "extends": "eslint:recommended",
16
- "parserOptions": {
17
- "sourceType": "module"
18
- },
19
- "globals":{}
20
- }
package/.eslintrc.js DELETED
@@ -1,28 +0,0 @@
1
- module.exports = {
2
- "parser":"babel-eslint",
3
- "rules": {
4
- "indent": [2,"tab", {"SwitchCase":1}],
5
- "quotes": [2,"single"],
6
- "semi": [2,"never"],
7
- "no-unused-vars": ["warn", { "vars": "all", "args": "none" }],
8
- "no-console": 0
9
- },
10
- "env": {
11
- "es6": true,
12
- "browser": true,
13
- "node" : true,
14
- "jquery": true,
15
- },
16
- "extends": "eslint:recommended",
17
- "parserOptions": {
18
- "sourceType": "module",
19
- "ecmaVersion": 2017,
20
- "ecmaFeatures": {
21
- "experimentalObjectRestSpread": true
22
- }
23
- },
24
- "globals":{
25
- "APPCONFIG":true,
26
- "webcomponents":true
27
- }
28
- }
package/src/animation.js DELETED
@@ -1,94 +0,0 @@
1
- import {
2
- addClass,
3
- removeClass,
4
- animationEnd,
5
- transitionEnd,
6
- nextFrame
7
- } from './utils'
8
-
9
- export const onBeforeAdd = (node, animation) => {
10
-
11
- const enter = `${animation}-enter`
12
- const enterActive = `${animation}-enter-active`
13
- const addClassNames = addClass(node)
14
-
15
- addClassNames(`${enter} ${enterActive}`)
16
- }
17
-
18
- export const onAdd = (node, animation) => {
19
-
20
- const enter = `${animation}-enter`
21
- const enterActive = `${animation}-enter-active`
22
- const enterTo = `${animation}-enter-to`
23
- const removeClassNames = removeClass(node)
24
- const addClassNames = addClass(node)
25
-
26
- const remove = () => {
27
- removeClassNames(`${enter} ${enterActive} ${enterTo}`)
28
- node.removeEventListener(transitionEnd, remove)
29
- node.removeEventListener(animationEnd, remove)
30
- }
31
-
32
- node.addEventListener(transitionEnd, remove)
33
- node.addEventListener(animationEnd, remove)
34
-
35
- nextFrame(() => {
36
- addClassNames(enterTo)
37
- removeClassNames(enter)
38
- })
39
- }
40
-
41
- export const onRemove = (node, animation) => {
42
-
43
- const leave = `${animation}-leave`
44
- const leaveActive = `${animation}-leave-active`
45
- const leaveTo = `${animation}-leave-to`
46
- const removeClassNames = removeClass(node)
47
- const addClassNames = addClass(node)
48
- const style = window.getComputedStyle(node)
49
-
50
- let transitionsLength = style.transitionProperty.split(',').length
51
-
52
- const remove = (e) => {
53
- if (e.type == 'transitionend') {
54
- if (e.target == node) {
55
- transitionsLength -= 1
56
- if (transitionsLength <= 1) {
57
- removeClassNames(`${leaveActive} ${leaveTo}`)
58
- node.removeEventListener(transitionEnd, remove)
59
- node.parentNode ? node.parentNode.removeChild(node) : null
60
- }
61
- }
62
- }
63
- else {
64
- removeClassNames(`${leaveActive} ${leaveTo}`)
65
- node.removeEventListener(animationEnd, remove)
66
- node.parentNode ? node.parentNode.removeChild(node) : null
67
- }
68
- }
69
-
70
- node.addEventListener(transitionEnd, remove)
71
- node.addEventListener(animationEnd, remove)
72
-
73
- addClassNames(`${leave} ${leaveActive}`)
74
-
75
- nextFrame(() => {
76
- removeClassNames(leave)
77
- addClassNames(leaveTo)
78
- })
79
- }
80
-
81
- export const animateNodes = (node, callback) => {
82
-
83
- const childnodes = node.nodeType == 1
84
- ? Array.prototype.slice.call(node.querySelectorAll('[data-animation]'))
85
- : []
86
-
87
- const list = node.dataset && node.dataset.animation
88
- ? [node].concat(childnodes)
89
- : childnodes
90
-
91
- list.forEach(n => callback(n, n.dataset.animation))
92
-
93
- return list.length > 0
94
- }
package/src/component.js DELETED
@@ -1,143 +0,0 @@
1
- import { pandora, log } from 'jails.packages/pandora'
2
- import { on, off, trigger } from './utils/events'
3
- import * as Pubsub from './utils/pubsub'
4
- import { getParent } from './utils'
5
-
6
- export default function Component ({ name, element, view, component }) {
7
-
8
- const module = component.module
9
- const store = Store({ name, element, module, view })
10
- const subscriptions = []
11
- const destroyers = []
12
-
13
- let resolver
14
- let promise = new Promise(resolve => resolver = resolve)
15
- let updater = () => null
16
-
17
- const base = {
18
-
19
- name,
20
- injection: component.dependencies,
21
- elm: element,
22
- msg: store,
23
- publish: Pubsub.publish,
24
- unsubscribe: Pubsub.unsubscribe,
25
-
26
- __initialize(base) {
27
- resolver(base)
28
- base.destroy( _ => {
29
- subscriptions.forEach(topic => Pubsub.unsubscribe(topic))
30
- destroyers.forEach(fn => element.removeEventListener(':destroy', fn))
31
- })
32
- },
33
-
34
- main(fn) {
35
- promise.then(() => fn().forEach(lambda => lambda(base)))
36
- },
37
-
38
- render(data) {
39
- view.update(element, data)
40
- },
41
-
42
- expose(methods) {
43
- element.__instances__[name].methods = methods
44
- },
45
-
46
- update(data) {
47
- if( data.apply ){
48
- const _parent = getParent(element, '[data-component]')
49
- updater = data
50
- updater( _parent.__model__ )
51
- }else {
52
- updater( data )
53
- }
54
- },
55
-
56
- destroy(callback) {
57
- destroyers.push(callback)
58
- element.addEventListener(':destroy', callback)
59
- },
60
-
61
- on(name, selectorOrCallback, callback) {
62
- on(element, name, selectorOrCallback, callback)
63
- },
64
-
65
- off(name, callback) {
66
- off(element, name, callback)
67
- },
68
-
69
- trigger(ev, target, args) {
70
- if (target.constructor === String)
71
- trigger(element.querySelector(target), ev, { args: args })
72
- else trigger(element, ev, { args: target })
73
- },
74
-
75
- emit(n, params) {
76
- const args = Array.prototype.slice.call(arguments)
77
- trigger(element, args.shift(), { args: args })
78
- },
79
-
80
- get(name, query) {
81
-
82
- return function () {
83
-
84
- const args = Array.prototype.slice.call(arguments),
85
- method = args.shift(),
86
- selector = `[data-component*=${name}]`
87
-
88
- query = query ? selector + query : selector
89
-
90
- Array.from(element.querySelectorAll(query))
91
- .forEach(el => {
92
- const instance = el.__instances__[name]
93
- if (instance && (method in instance.methods))
94
- instance.methods[method].apply(null, args)
95
- })
96
-
97
- if (element.matches(query)) {
98
- const instance = element.__instances__[name]
99
- if (instance && method in instance.methods)
100
- instance.methods[method].apply(null, args)
101
- }
102
- }
103
- },
104
-
105
- subscribe(name, method) {
106
- subscriptions.push({ name, method })
107
- Pubsub.subscribe(name, method)
108
- }
109
- }
110
-
111
- return base
112
-
113
- }
114
-
115
- const Store = ({ element, name, module, view:View }) => {
116
-
117
- const view = module.view ? module.view : state => state
118
- const initialState = View.models[element.dataset.modelId]
119
- const model = Object.assign({}, module.model, initialState)
120
- const title = name.charAt(0).toUpperCase() + name.substring(1)
121
-
122
- const middlewares = View.mode === 'development'
123
- ? [log(`Component ${title}`)]
124
- : []
125
-
126
- const actions = module.actions || {}
127
-
128
- const store = pandora({
129
- model,
130
- actions,
131
- middlewares,
132
- autostart: false,
133
- callback(state) {
134
- View.update(element, view(state))
135
- }
136
- })
137
-
138
- if (module.model && Object.keys(module.model).length) {
139
- View.update(element, view(model))
140
- }
141
-
142
- return store
143
- }