jails-js 5.0.0-beta.1 → 5.0.0-beta.10

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.
@@ -0,0 +1,149 @@
1
+ import morphdom from 'morphdom'
2
+
3
+ import { rAF, dup, buildtemplates } from './utils'
4
+ import { on, off, trigger } from './utils/events'
5
+ import { publish, subscribe, unsubscribe } from './utils/pubsub'
6
+
7
+ type MainArgs = () => Array<Function>
8
+
9
+ export default function Component(elm, { module, dependencies, templates, components }) {
10
+
11
+ const options = getOptions(module)
12
+
13
+ buildtemplates(elm, components, templates)
14
+
15
+ const tplid = elm.getAttribute('tplid')
16
+
17
+ const template = templates[tplid]
18
+ const state = { data: module.model ? dup(module.model) : {} }
19
+
20
+ const base = {
21
+ template,
22
+ elm,
23
+ dependencies,
24
+ publish,
25
+ subscribe,
26
+ unsubscribe,
27
+
28
+ main(fn: MainArgs) {
29
+ options.main = fn
30
+ },
31
+
32
+ unmount(fn) {
33
+ options.unmount = fn
34
+ },
35
+
36
+ onupdate(fn) {
37
+ options.onupdate = fn
38
+ },
39
+
40
+ on(eventName: string, selectorOrCallback: object | Function, callback: Function) {
41
+ on(elm, eventName, selectorOrCallback, callback)
42
+ },
43
+
44
+ off(eventName: string, callback: Function) {
45
+ off(elm, eventName, callback)
46
+ },
47
+
48
+ trigger(eventName: string, target: string, args: any) {
49
+ if (target.constructor === String)
50
+ trigger(elm.querySelector(target), eventName, { args: args })
51
+ else trigger(elm, eventName, { args: target })
52
+ },
53
+
54
+ emit: (...args) => {
55
+ trigger(elm, args.shift(), { args: args })
56
+ },
57
+
58
+ state: {
59
+ set(data: any) {
60
+ if (data.constructor === Function) {
61
+ const newstate = dup(state.data)
62
+ data(newstate)
63
+ base.render(newstate)
64
+ } else {
65
+ base.render(data)
66
+ }
67
+ return new Promise((resolve) => rAF(_ => rAF(resolve)))
68
+ },
69
+ get() {
70
+ return dup(state.data)
71
+ }
72
+ },
73
+
74
+ render(data: object = state.data) {
75
+
76
+ if (!document.body.contains(elm))
77
+ return
78
+
79
+ state.data = Object.assign(state.data, data)
80
+
81
+ const newdata = dup(state.data)
82
+ const newhtml = base.template(options.view(newdata))
83
+
84
+ morphdom(elm, newhtml, morphdomOptions(elm, options))
85
+
86
+ rAF(_ => {
87
+ rAF(_ => {
88
+ Array
89
+ .from(elm.querySelectorAll('[tplid]'))
90
+ .forEach(child => {
91
+ child.options.onupdate(newdata)
92
+ child.base.render(newdata)
93
+ })
94
+ })
95
+
96
+ })
97
+
98
+ }
99
+ }
100
+
101
+ return { base, options }
102
+ }
103
+
104
+ const getOptions = (module) => ({
105
+ main: _ => _,
106
+ unmount: _ => _,
107
+ onupdate: _ => _,
108
+ view: module.view ? module.view : _ => _
109
+ })
110
+
111
+ const morphdomOptions = (_parent, options) => ({
112
+
113
+ onNodeAdded: onUpdates(_parent, options),
114
+ onElUpdated: onUpdates(_parent, options),
115
+ onBeforeElChildrenUpdated: checkStatic,
116
+ onBeforeElUpdated: checkStatic,
117
+
118
+ getNodeKey(node) {
119
+ if (node.nodeType === 1 && node.getAttribute('tplid'))
120
+ return node.dataset.key || node.getAttribute('tplid')
121
+ return false
122
+ }
123
+ })
124
+
125
+ const checkStatic = (node) => {
126
+ if ('static' in node.dataset) {
127
+ return false
128
+ }
129
+ }
130
+
131
+ const onUpdates = (_parent, options) => (node) => {
132
+
133
+ if (node.nodeType === 1) {
134
+
135
+ if (node.getAttribute && node.getAttribute('scope')) {
136
+
137
+ const scope = JSON.parse(node.getAttribute('scope').replace(/\'/g, '\"'))
138
+
139
+ Array.from(node.querySelectorAll('[tplid]'))
140
+ .map(el => {
141
+ const data = Object.assign({}, _parent.base.state.get(), scope)
142
+ options.onupdate(data)
143
+ el.base.render(data)
144
+ })
145
+
146
+ node.removeAttribute('scope')
147
+ }
148
+ }
149
+ }
package/src/element.ts ADDED
@@ -0,0 +1,35 @@
1
+ import Component from './component'
2
+
3
+ export default function Element(module, dependencies, templates, components) {
4
+
5
+ return class extends HTMLElement {
6
+
7
+ constructor() {
8
+
9
+ super()
10
+
11
+ const { base, options } = Component(this, { module, dependencies, templates, components })
12
+
13
+ this.base = base
14
+ this.options = options
15
+
16
+ module.default(base)
17
+ }
18
+
19
+ connectedCallback() {
20
+ this.base.render()
21
+ this.options.main().forEach(f => f(this.base))
22
+ }
23
+
24
+ disconnectedCallback() {
25
+ this.options.unmount(this.base)
26
+ delete this.options
27
+ delete this.base
28
+ delete this.__events
29
+ }
30
+
31
+ attributeChangedCallback() {
32
+ //TODO
33
+ }
34
+ }
35
+ }
package/src/index.ts ADDED
@@ -0,0 +1,27 @@
1
+ import { buildtemplates } from './utils'
2
+ import Element from './element'
3
+
4
+ export const templates = {}
5
+ export const components = {}
6
+
7
+ export default {
8
+
9
+ register(name: string, module: any, dependencies: object = {}) {
10
+ components[name] = { name, module, dependencies }
11
+ },
12
+
13
+ start() {
14
+ const body = document.body
15
+ buildtemplates(body, components, templates)
16
+ registerComponents()
17
+ }
18
+ }
19
+
20
+ const registerComponents = () => {
21
+ Object
22
+ .values(components)
23
+ .forEach(({ name, module, dependencies }) => {
24
+ const Base = Element(module, dependencies, templates, components)
25
+ customElements.define(name, Base)
26
+ })
27
+ }
@@ -0,0 +1,89 @@
1
+ import { compile, defaultConfig, filters } from 'squirrelly'
2
+ import { decodeHtmlEntities } from './utils'
3
+
4
+ const defaultOptions = {
5
+ ...defaultConfig,
6
+ tags: ['{', '}'],
7
+ useWith: true
8
+ }
9
+
10
+ export default function templateSystem(element) {
11
+
12
+ const tree = document.createElement('template')
13
+
14
+ tree.innerHTML = element.outerHTML.replace(/<template.*>|<\/template>/g, '')
15
+
16
+ directives(tree.content)
17
+
18
+ const html = decodeHtmlEntities(
19
+ tree.innerHTML
20
+ .replace(/html-(selected|checked|readonly|disabled|autoplay)=\"(.*)\"/g, `{@if ($2) }$1{/if}`)
21
+ .replace(/html-/g, '')
22
+ )
23
+
24
+ const template = compile(html, defaultOptions)
25
+
26
+ return (data) => {
27
+ return template(data, defaultOptions)
28
+ }
29
+ }
30
+
31
+ /**@Directives */
32
+
33
+ const directives = (vdom) => {
34
+
35
+ const nodes = Array
36
+ .from(vdom.querySelectorAll('[html-for],[html-if],[html-foreach]'))
37
+ .reverse()
38
+
39
+ if (nodes.length) {
40
+
41
+ nodes.forEach(node => {
42
+ if (node.getAttribute('html-foreach')) {
43
+ const instruction = node.getAttribute('html-foreach')
44
+ const split = instruction.match(/(.*)\sin\s(.*)/)
45
+ const varname = split[1]
46
+ const object = split[2]
47
+ node.removeAttribute('html-foreach')
48
+ node.setAttribute('scope', `{${varname} | JSON($key, '${varname}')}`)
49
+ const open = document.createTextNode(`{@foreach(${object}) => $key, ${varname}}`)
50
+ const close = document.createTextNode('{/foreach}')
51
+ wrap(open, node, close)
52
+ } else if (node.getAttribute('html-for')) {
53
+ const instruction = node.getAttribute('html-for')
54
+ const split = instruction.match(/(.*)\sin\s(.*)/)
55
+ const varname = split[1]
56
+ const object = split[2]
57
+ node.removeAttribute('html-for')
58
+ node.setAttribute('scope', `{${varname} | JSON($index, '${varname}')}`)
59
+ const open = document.createTextNode(`{@each(${object}) => ${varname}, $index}`)
60
+ const close = document.createTextNode('{/each}')
61
+ wrap(open, node, close)
62
+ } else if (node.getAttribute('html-if')) {
63
+ const instruction = node.getAttribute('html-if')
64
+ node.removeAttribute('html-if')
65
+ const open = document.createTextNode(`{@if (${instruction}) }`)
66
+ const close = document.createTextNode('{/if}')
67
+ wrap(open, node, close)
68
+ }
69
+ })
70
+ }
71
+
72
+ return vdom
73
+ }
74
+
75
+ filters.define('JSON', (scope, index, varname) => {
76
+
77
+ const key = index.constructor == String ? '$key' : '$index'
78
+ const newobject = { $index: index }
79
+
80
+ newobject[varname] = scope
81
+ newobject[key] = index
82
+
83
+ return JSON.stringify(newobject)
84
+ })
85
+
86
+ const wrap = (open, node, close) => {
87
+ node.parentNode.insertBefore(open, node)
88
+ node.parentNode.insertBefore(close, node.nextSibling)
89
+ }
File without changes
@@ -0,0 +1,57 @@
1
+ import templateSystem from '../template-system'
2
+
3
+ const textarea = document.createElement('textarea')
4
+
5
+ export const rAF = (fn) => {
6
+ (requestAnimationFrame || setTimeout)(fn, 1000 / 60)
7
+ }
8
+
9
+ export const uuid = () => {
10
+ return 'xxxxxxxx'.replace(/[xy]/g, (c) => {
11
+ const r = Math.random() * 8 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8)
12
+ return v.toString(8)
13
+ })
14
+ }
15
+
16
+ export const stripTemplateTag = (element) => {
17
+ const templates = Array.from(element.querySelectorAll('template'))
18
+ // https://gist.github.com/harmenjanssen/07e425248779c65bc5d11b02fb913274
19
+ templates.forEach(template => {
20
+ template.parentNode.replaceChild(template.content, template)
21
+ stripTemplateTag(template.content)
22
+ })
23
+ }
24
+
25
+ export const dup = (o) => {
26
+ return JSON.parse(JSON.stringify(o))
27
+ }
28
+
29
+ export const createTemplateId = (element, templates) => {
30
+
31
+ const tplid = element.getAttribute('tplid')
32
+
33
+ if (!tplid) {
34
+ const id = uuid()
35
+ element.setAttribute('tplid', id)
36
+ templates[id] = templateSystem(element)
37
+ }
38
+ }
39
+
40
+ export const buildtemplates = (target, components, templates) => {
41
+
42
+ return Array
43
+ .from(target.querySelectorAll('*'))
44
+ .filter(node => node.tagName.toLowerCase() in components)
45
+ .reverse()
46
+ .map(node => {
47
+ Array.from(node.querySelectorAll('template'))
48
+ .map(template => buildtemplates(template.content, components, templates))
49
+ createTemplateId(node, templates)
50
+ return node
51
+ })
52
+ }
53
+
54
+ export const decodeHtmlEntities = (str) => {
55
+ textarea.innerHTML = str
56
+ return textarea.value
57
+ }
@@ -3,14 +3,14 @@ const _async = {}
3
3
 
4
4
  export const publish = (name, params) => {
5
5
  _async[name] = Object.assign({}, _async[name], params)
6
- if( topics[name] )
6
+ if (topics[name])
7
7
  topics[name].forEach(topic => topic(params))
8
8
  }
9
9
 
10
10
  export const subscribe = (name, method) => {
11
11
  topics[name] = topics[name] || []
12
12
  topics[name].push(method)
13
- if ( name in _async ) {
13
+ if (name in _async) {
14
14
  method(_async[name])
15
15
  }
16
16
  }
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "baseUrl": "./src",
4
+ "outDir": "./dist/",
5
+ "target": "es6",
6
+ "allowJs": true,
7
+ "moduleResolution": "node",
8
+ "allowSyntheticDefaultImports": true,
9
+ "lib": [
10
+ "dom"
11
+ ]
12
+ },
13
+ "exclude": [
14
+ "./node_modules"
15
+ ]
16
+ }
@@ -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/.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/component.js DELETED
@@ -1,128 +0,0 @@
1
- import { on, off, trigger } from './utils/events'
2
- import { rAF } from './utils'
3
-
4
- export default function Component ({
5
- name,
6
- element,
7
- dependencies,
8
- Pubsub,
9
- ElementInterface,
10
- AST
11
- }) {
12
-
13
- const subscriptions = []
14
-
15
- let stateSubscriptions = []
16
- let resolver
17
- let promise = new Promise(resolve => resolver = resolve)
18
-
19
- const base = {
20
-
21
- name,
22
- dependencies,
23
- elm: element,
24
- publish: Pubsub.publish,
25
- unsubscribe: Pubsub.unsubscribe,
26
-
27
- __initialize() {
28
- resolver(base)
29
- },
30
-
31
- main(fn) {
32
- promise
33
- .then( _ => fn().forEach(lambda => lambda(base)))
34
- .catch( err => console.error( err) )
35
- },
36
-
37
- expose(methods) {
38
- ElementInterface.instances[name].methods = methods
39
- },
40
-
41
- state: {
42
- set( state ) {
43
- if( state.constructor === Function ){
44
- const model = ElementInterface.model
45
- state(model)
46
- ElementInterface.update(model)
47
- stateSubscriptions.forEach( fn => fn(model) )
48
- } else {
49
- ElementInterface.update(state)
50
- stateSubscriptions.forEach( fn => fn(state) )
51
- }
52
- return new Promise((resolve) => rAF(resolve))
53
- },
54
- get() {
55
- return ElementInterface.model
56
- },
57
- subscribe(fn){
58
- stateSubscriptions.push(fn)
59
- },
60
- unsubscribe(fn){
61
- stateSubscriptions = stateSubscriptions.filter( item => item !== fn )
62
- }
63
- },
64
-
65
- destroy(callback) {
66
- ElementInterface.destroyers.push(callback)
67
- },
68
-
69
- on(name, selectorOrCallback, callback) {
70
- on(element, name, selectorOrCallback, callback)
71
- },
72
-
73
- off(name, callback) {
74
- off(element, name, callback)
75
- },
76
-
77
- trigger(ev, target, args) {
78
- if (target.constructor === String)
79
- trigger(element.querySelector(target), ev, { args: args })
80
- else trigger(element, ev, { args: target })
81
- },
82
-
83
- emit(n, params) {
84
- const args = Array.prototype.slice.call(arguments)
85
- trigger(element, args.shift(), { args: args })
86
- },
87
-
88
- update(fn) {
89
- ElementInterface.parentUpdate = fn
90
- },
91
-
92
- get(name, query) {
93
-
94
- return function () {
95
- rAF(_ => {
96
- const args = Array.prototype.slice.call(arguments),
97
- method = args.shift(),
98
- selector = `[data-component*=${name}]`
99
- query = query ? selector + query : selector
100
-
101
- Array.from(element.querySelectorAll(query))
102
- .forEach(el => {
103
- const item = AST.find( item => item.element == el )
104
- if( item ) {
105
- const instance = item.instances[name]
106
- if (instance && (method in instance.methods))
107
- instance.methods[method].apply(null, args)
108
- }
109
- })
110
-
111
- if (element.matches(query)) {
112
- const item = AST.find( item => item.element == element )
113
- const instance = item.instances[name]
114
- if (instance && method in instance.methods)
115
- instance.methods[method].apply(null, args)
116
- }
117
- })
118
- }
119
- },
120
-
121
- subscribe(name, method) {
122
- subscriptions.push({ name, method })
123
- Pubsub.subscribe(name, method)
124
- }
125
- }
126
-
127
- return base
128
- }