jails-js 5.0.0-beta.6 → 5.0.0-beta.9

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/package.json CHANGED
@@ -1,41 +1,43 @@
1
1
  {
2
- "name": "jails-js",
3
- "version": "5.0.0-beta.6",
4
- "description": "A Modern Javascript Library",
5
- "main": "dist/jails.js",
6
- "scripts": {
7
- "start": "webpack --watch --mode=development",
8
- "build": "webpack --mode=production",
9
- "test": "echo \"Error: no test specified\" && exit 1"
10
- },
11
- "repository": {
12
- "type": "git",
13
- "url": "https://github.com/jails-org/Jails.git"
14
- },
15
- "keywords": [
16
- "Jails",
17
- "Javascript",
18
- "Component",
19
- "Micro-Library"
20
- ],
21
- "author": "javiani",
22
- "license": "MIT",
23
- "bugs": {
24
- "url": "https://github.com/jails-org/Jails/issues"
25
- },
26
- "homepage": "https://github.com/jails-org/Jails",
27
- "devDependencies": {
28
- "@babel/core": "^7.2.2",
29
- "@babel/preset-env": "^7.2.3",
30
- "babel-loader": "^8.0.5",
31
- "babel-preset-env": "^1.7.0",
32
- "ts-loader": "^9.2.6",
33
- "typescript": "^4.5.4",
34
- "webpack": "^5.59.1",
35
- "webpack-cli": "^3.2.1"
36
- },
37
- "dependencies": {
38
- "morphdom": "^2.6.1",
39
- "sodajs": "^0.4.10"
40
- }
2
+ "name": "jails-js",
3
+ "version": "5.0.0-beta.9",
4
+ "description": "A Modern Javascript Library",
5
+ "main": "dist/jails.js",
6
+ "scripts": {
7
+ "start": "webpack --watch --mode=development",
8
+ "build": "webpack --mode=production",
9
+ "test": "echo \"Error: no test specified\" && exit 1"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/jails-org/Jails.git"
14
+ },
15
+ "keywords": [
16
+ "Jails",
17
+ "Javascript",
18
+ "Component",
19
+ "Micro-Library"
20
+ ],
21
+ "author": "javiani",
22
+ "license": "MIT",
23
+ "bugs": {
24
+ "url": "https://github.com/jails-org/Jails/issues"
25
+ },
26
+ "homepage": "https://github.com/jails-org/Jails",
27
+ "devDependencies": {
28
+ "@babel/core": "^7.2.2",
29
+ "@babel/preset-env": "^7.2.3",
30
+ "babel-loader": "^8.0.5",
31
+ "babel-plugin-transform-custom-element-classes": "^0.1.0",
32
+ "babel-preset-env": "^1.7.0",
33
+ "ts-loader": "^9.2.6",
34
+ "typescript": "^4.5.4",
35
+ "webpack": "^5.59.1",
36
+ "webpack-cli": "^3.2.1"
37
+ },
38
+ "dependencies": {
39
+ "morphdom": "^2.6.1",
40
+ "squirrelly": "^8.0.8",
41
+ "template7": "^1.4.2"
42
+ }
41
43
  }
@@ -0,0 +1,158 @@
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
+ const template = templates[tplid]
17
+ const state = { data: module.model ? dup(module.model) : {} }
18
+
19
+ let batchUpdates = []
20
+
21
+ const base = {
22
+ template,
23
+ elm,
24
+ dependencies,
25
+ publish,
26
+ subscribe,
27
+ unsubscribe,
28
+
29
+ main(fn: MainArgs) {
30
+ options.main = fn
31
+ },
32
+
33
+ unmount(fn) {
34
+ options.unmount = fn
35
+ },
36
+
37
+ onupdate(fn) {
38
+ options.onupdate = fn
39
+ },
40
+
41
+ on(eventName: string, selectorOrCallback: object | Function, callback: Function) {
42
+ on(elm, eventName, selectorOrCallback, callback)
43
+ },
44
+
45
+ off(eventName: string, callback: Function) {
46
+ off(elm, eventName, callback)
47
+ },
48
+
49
+ trigger(eventName: string, target: string, args: any) {
50
+ if (target.constructor === String)
51
+ trigger(elm.querySelector(target), eventName, { args: args })
52
+ else trigger(elm, eventName, { args: target })
53
+ },
54
+
55
+ emit: (...args) => {
56
+ trigger(elm, args.shift(), { args: args })
57
+ },
58
+
59
+ state: {
60
+ set(data: any) {
61
+ if (data.constructor === Function) {
62
+ const newstate = dup(state.data)
63
+ data(newstate)
64
+ base.render(newstate)
65
+ } else {
66
+ base.render(data)
67
+ }
68
+ return new Promise((resolve) => rAF(_ => rAF(resolve)))
69
+ },
70
+ get() {
71
+ return dup(state.data)
72
+ }
73
+ },
74
+
75
+ render(data: object = state.data) {
76
+
77
+ if (!document.body.contains(elm))
78
+ return
79
+
80
+ batchUpdates.push(data)
81
+
82
+ rAF(() => {
83
+ rAF(() => {
84
+ if (batchUpdates.length) {
85
+
86
+ const batchData = {}
87
+ batchUpdates.forEach(d => Object.assign(batchData, d))
88
+ batchUpdates = []
89
+
90
+ state.data = Object.assign(state.data, batchData)
91
+
92
+ const newdata = dup(state.data)
93
+ const newhtml = base.template(options.view(newdata))
94
+
95
+ morphdom(elm, newhtml, morphdomOptions(elm, options))
96
+
97
+ Array
98
+ .from(elm.querySelectorAll('[tplid]'))
99
+ .map(child => {
100
+ child.options.onupdate(newdata)
101
+ child.base.render(newdata)
102
+ return child
103
+ })
104
+ }
105
+ })
106
+ })
107
+ }
108
+ }
109
+
110
+ return { base, options }
111
+ }
112
+
113
+ const getOptions = (module) => ({
114
+ main: _ => _,
115
+ unmount: _ => _,
116
+ onupdate: _ => _,
117
+ view: module.view ? module.view : _ => _
118
+ })
119
+
120
+ const morphdomOptions = (_parent, options) => ({
121
+
122
+ onNodeAdded: onUpdates(_parent, options),
123
+ onElUpdated: onUpdates(_parent, options),
124
+ onBeforeElChildrenUpdated: checkStatic,
125
+ onBeforeElUpdated: checkStatic,
126
+
127
+ getNodeKey(node) {
128
+ if (node.nodeType === 1 && node.getAttribute('tplid'))
129
+ return node.dataset.key || node.getAttribute('tplid')
130
+ return false
131
+ }
132
+ })
133
+
134
+ const checkStatic = (node) => {
135
+ if ('static' in node.dataset) {
136
+ return false
137
+ }
138
+ }
139
+
140
+ const onUpdates = (_parent, options) => (node) => {
141
+
142
+ if (node.nodeType === 1) {
143
+
144
+ if (node.getAttribute && node.getAttribute('scope')) {
145
+
146
+ const scope = JSON.parse(node.getAttribute('scope').replace(/\'/g, '\"'))
147
+
148
+ Array.from(node.querySelectorAll('[tplid]'))
149
+ .map(el => {
150
+ const data = Object.assign({}, _parent.base.state.get(), scope)
151
+ options.onupdate(data)
152
+ el.base.render(data)
153
+ })
154
+
155
+ node.removeAttribute('scope')
156
+ }
157
+ }
158
+ }
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 CHANGED
@@ -1,62 +1,28 @@
1
- import { Element } from './Element'
2
- import { Scanner } from './Scanner'
3
- import { Component } from './Component'
4
- import { Instances } from './Instances'
5
- import { stripTemplateTag, dup } from './utils'
1
+ import { buildtemplates, stripTemplateTag } from './utils'
2
+ import Element from './element'
6
3
 
7
- const components = {}
4
+ export const templates = {}
5
+ export const components = {}
8
6
 
9
7
  export default {
10
8
 
11
- start() {
12
-
13
- const body: HTMLElement = document.body
14
-
15
- stripTemplateTag( body )
16
-
17
- Scanner.observe( body, createElement, disposeElement )
18
- Scanner.scan( body, createElement )
9
+ register(name: string, module: any, dependencies: object = {}) {
10
+ components[name] = { name, module, dependencies }
19
11
  },
20
12
 
21
- register( name, module, dependencies = {} ) {
22
- components[name] = { name, module, dependencies }
13
+ start() {
14
+ const body = document.body
15
+ stripTemplateTag(body)
16
+ buildtemplates(body, components, templates)
17
+ registerComponents()
23
18
  }
24
19
  }
25
20
 
26
- const createElement = ( element: HTMLElement ) => {
27
-
28
- const ElementInterface = Element( element )
29
- const names = element.dataset.component.split(/\s/)
30
-
31
- names.forEach( name => {
32
-
33
- const C = components[name]
34
-
35
- if( !C ) {
36
- console.warn(`Jails - Module ${name} not registered`)
37
- return
38
- }
39
-
40
- const { module, dependencies } = C
41
- ElementInterface.model = Object.assign({}, module.model? dup(module.model) : null, ElementInterface.model )
42
-
43
- const base = Component({ name, element, dependencies, ElementInterface })
44
- const promise = module.default(base)
45
-
46
- if( promise && promise.then ) {
47
- ElementInterface.promises.push(promise)
48
- }
49
-
50
- base.__initialize()
51
- ElementInterface.view = module.view || ElementInterface.view
52
- ElementInterface.instances[name] = { methods: {} }
53
- })
54
-
55
- ElementInterface.update()
56
- }
57
-
58
- const disposeElement = ( node ) => {
59
- const instance = Instances.get(node)
60
- if( Instances.get(node) )
61
- instance.dispose()
21
+ const registerComponents = () => {
22
+ Object
23
+ .values(components)
24
+ .forEach(({ name, module, dependencies }) => {
25
+ const Base = Element(module, dependencies, templates, components)
26
+ customElements.define(name, Base)
27
+ })
62
28
  }
@@ -0,0 +1,89 @@
1
+ import { compile, defaultConfig, filters } from 'squirrelly'
2
+ import { stripTemplateTag, 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 vdom = element.cloneNode(true)
13
+
14
+ stripTemplateTag(vdom)
15
+
16
+ const newvdom = directives(vdom)
17
+
18
+ const html = decodeHtmlEntities(
19
+ newvdom.outerHTML
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,58 @@
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
+ return templates[id]
38
+ }
39
+
40
+ return templates[tplid]
41
+ }
42
+
43
+ export const buildtemplates = (target, components, templates) => {
44
+
45
+ return Array
46
+ .from(target.querySelectorAll('*'))
47
+ .filter(node => node.tagName.toLocaleLowerCase() in components)
48
+ .reverse()
49
+ .map(node => {
50
+ createTemplateId(node, templates)
51
+ return node
52
+ })
53
+ }
54
+
55
+ export const decodeHtmlEntities = (str) => {
56
+ textarea.innerHTML = str
57
+ return textarea.value
58
+ }
@@ -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 CHANGED
@@ -1,6 +1,16 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "lib": [ "dom" ],
4
- "sourceMap": true
5
- }
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
+ ]
6
16
  }
package/src/Component.ts DELETED
@@ -1,123 +0,0 @@
1
- import * as Pubsub from './utils/pubsub'
2
- import { on, off, trigger } from './utils/events'
3
- import { rAF } from './utils'
4
- import { Instances } from './Instances'
5
-
6
- export const Component = ({
7
-
8
- name,
9
- element,
10
- dependencies,
11
- ElementInterface
12
-
13
- }) => {
14
-
15
- const subscriptions = []
16
-
17
- let stateSubscriptions = []
18
- let resolver
19
- let promise = new Promise(resolve => resolver = resolve)
20
-
21
- const base = {
22
-
23
- name,
24
- dependencies,
25
- elm: element,
26
- publish: Pubsub.publish,
27
- unsubscribe: Pubsub.unsubscribe,
28
-
29
- __initialize() {
30
- resolver(base)
31
- },
32
-
33
- main(fn) {
34
- promise
35
- .then( _ => fn().forEach(lambda => lambda(base)))
36
- .catch( err => console.error( err) )
37
- },
38
-
39
- expose(methods) {
40
- ElementInterface.instances[name].methods = methods
41
- },
42
-
43
- state: {
44
- set( state ) {
45
- if( state.constructor === Function ){
46
- const model = ElementInterface.model
47
- state(model)
48
- ElementInterface.update(model)
49
- } else {
50
- ElementInterface.update(state)
51
- }
52
- stateSubscriptions.forEach( fn => fn(ElementInterface.model) )
53
- return new Promise((resolve) => rAF(_ => rAF(resolve) ))
54
- },
55
- get() {
56
- return ElementInterface.model
57
- },
58
- subscribe(fn){
59
- stateSubscriptions.push(fn)
60
- },
61
- unsubscribe(fn){
62
- stateSubscriptions = stateSubscriptions.filter( item => item !== fn )
63
- }
64
- },
65
-
66
- destroy(callback) {
67
- ElementInterface.destroyers.push(callback)
68
- },
69
-
70
- on(name, selectorOrCallback, callback) {
71
- on(element, name, selectorOrCallback, callback)
72
- },
73
-
74
- off(name, callback) {
75
- off(element, name, callback)
76
- },
77
-
78
- trigger(ev, target, args) {
79
- if (target.constructor === String)
80
- trigger(element.querySelector(target), ev, { args: args })
81
- else trigger(element, ev, { args: target })
82
- },
83
-
84
- emit(n, params) {
85
- const args = Array.prototype.slice.call(arguments)
86
- trigger(element, args.shift(), { args: args })
87
- },
88
-
89
- update(fn) {
90
- ElementInterface.parentUpdate = fn
91
- },
92
-
93
- get(name, query) {
94
-
95
- return function () {
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 instance = Instances.get(el).instances[name]
104
- if (instance && (method in instance.methods))
105
- instance.methods[method].apply(null, args)
106
- })
107
-
108
- if (element.matches(query)) {
109
- const instance = Instances.get(element).instances[name]
110
- if (instance && method in instance.methods)
111
- instance.methods[method].apply(null, args)
112
- }
113
- }
114
- },
115
-
116
- subscribe(name, method) {
117
- subscriptions.push({ name, method })
118
- Pubsub.subscribe(name, method)
119
- }
120
- }
121
-
122
- return base
123
- }