wallace 0.0.6 → 0.0.8
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/lib/component.js +76 -21
- package/lib/index.js +11 -11
- package/lib/initCalls.js +16 -23
- package/lib/{pool.js → repeaters.js} +24 -27
- package/lib/types.d.ts +2 -1
- package/lib/utils.js +17 -19
- package/package.json +3 -3
package/lib/component.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The base component.
|
|
3
3
|
*/
|
|
4
|
-
export function Component(
|
|
5
|
-
this.
|
|
4
|
+
export function Component() {
|
|
5
|
+
this.ctrl = undefined; // The controller.
|
|
6
6
|
this.props = undefined; // The props passed to the component. May be changed.
|
|
7
|
-
this.el = null; // the element - will be set during build
|
|
8
|
-
this.ref = {}; // user set references to elements or components
|
|
7
|
+
this.el = null; // the element - will be set during build.
|
|
8
|
+
this.ref = {}; // user set references to elements or components.
|
|
9
9
|
// Internal state objects
|
|
10
|
-
this.
|
|
11
|
-
this.
|
|
12
|
-
this.
|
|
10
|
+
this._e = {}; // The dynamic elements in the DOM.
|
|
11
|
+
this._p = {}; // The previous values for watches to compare against.
|
|
12
|
+
this._s = []; // A stash for misc objects.
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
var proto = Component.prototype;
|
|
@@ -21,10 +21,11 @@ Object.defineProperty(proto, "hidden", {
|
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
24
|
+
* The render function that gets called by parent components.
|
|
25
25
|
*/
|
|
26
|
-
proto.render = function (props) {
|
|
26
|
+
proto.render = function (props, ctrl) {
|
|
27
27
|
this.props = props;
|
|
28
|
+
this.ctrl = ctrl;
|
|
28
29
|
this.update();
|
|
29
30
|
};
|
|
30
31
|
|
|
@@ -36,29 +37,83 @@ proto.update = function () {
|
|
|
36
37
|
let i = 0,
|
|
37
38
|
watch,
|
|
38
39
|
element,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
parent,
|
|
41
|
+
displayToggle,
|
|
42
|
+
detacher,
|
|
43
|
+
lookupReturn,
|
|
44
|
+
lookupTrue,
|
|
45
|
+
shouldBeVisible,
|
|
46
|
+
visibilityChanged,
|
|
47
|
+
detachedElements,
|
|
48
|
+
detachedElement,
|
|
49
|
+
index,
|
|
50
|
+
adjustedIndex,
|
|
51
|
+
thisElement;
|
|
42
52
|
|
|
43
|
-
const watches = this.
|
|
53
|
+
const watches = this._w;
|
|
44
54
|
const lookup = this._l;
|
|
45
55
|
const props = this.props;
|
|
46
56
|
lookup.reset();
|
|
47
57
|
const il = watches.length;
|
|
58
|
+
/*
|
|
59
|
+
Watches is an array of objects with keys:
|
|
60
|
+
e: the element reference (string)
|
|
61
|
+
c: the callbacks (object)
|
|
62
|
+
?v: visibility toggle (object)
|
|
63
|
+
|
|
64
|
+
The display toggle has keys:
|
|
65
|
+
q: the query key in lookup
|
|
66
|
+
s: the number of watches to skip as their node is underneath
|
|
67
|
+
r: reversed
|
|
68
|
+
?d: detacher
|
|
69
|
+
|
|
70
|
+
The detacher has keys:
|
|
71
|
+
i: the initial element index
|
|
72
|
+
s: the stash key of the detacher (plain object)
|
|
73
|
+
e: the parent element key
|
|
74
|
+
*/
|
|
48
75
|
while (i < il) {
|
|
49
76
|
watch = watches[i];
|
|
50
|
-
element = this._e[watch.
|
|
51
|
-
|
|
77
|
+
element = this._e[watch.e];
|
|
78
|
+
displayToggle = watch.v;
|
|
52
79
|
i++;
|
|
53
80
|
shouldBeVisible = true;
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
81
|
+
if (displayToggle) {
|
|
82
|
+
lookupReturn = lookup.get(this, props, displayToggle.q);
|
|
83
|
+
lookupTrue = !!lookupReturn.n;
|
|
84
|
+
shouldBeVisible = displayToggle.r ? lookupTrue : !lookupTrue;
|
|
85
|
+
visibilityChanged = lookupTrue != !!lookupReturn.o;
|
|
86
|
+
detacher = displayToggle.d;
|
|
87
|
+
if (detacher) {
|
|
88
|
+
index = detacher.i;
|
|
89
|
+
parent = this._e[detacher.e];
|
|
90
|
+
detachedElements = this._s[detacher.s];
|
|
91
|
+
detachedElement = detachedElements[index];
|
|
92
|
+
if (shouldBeVisible && detachedElement) {
|
|
93
|
+
adjustedIndex =
|
|
94
|
+
index -
|
|
95
|
+
Object.keys(detachedElements).filter(function (k) {
|
|
96
|
+
return k < index && detachedElements[k];
|
|
97
|
+
}).length;
|
|
98
|
+
parent.insertBefore(
|
|
99
|
+
detachedElement,
|
|
100
|
+
parent.childNodes[adjustedIndex],
|
|
101
|
+
);
|
|
102
|
+
detachedElements[index] = null;
|
|
103
|
+
} else if (!shouldBeVisible && !detachedElement) {
|
|
104
|
+
thisElement = this._e[watch.e];
|
|
105
|
+
parent.removeChild(thisElement);
|
|
106
|
+
detachedElements[index] = thisElement;
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
element.hidden = !shouldBeVisible;
|
|
110
|
+
}
|
|
111
|
+
if (!shouldBeVisible) {
|
|
112
|
+
i += displayToggle.s;
|
|
113
|
+
}
|
|
59
114
|
}
|
|
60
115
|
if (shouldBeVisible) {
|
|
61
|
-
lookup.applyCallbacks(this, props, element, watch.
|
|
116
|
+
lookup.applyCallbacks(this, props, element, watch.c);
|
|
62
117
|
}
|
|
63
118
|
}
|
|
64
119
|
};
|
package/lib/index.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { createComponent,
|
|
1
|
+
import { createComponent, watch, mount } from "./utils";
|
|
2
2
|
import { Component } from "./component";
|
|
3
|
-
import {
|
|
3
|
+
import { KeyedRepeater, SequentialRepeater } from "./repeaters";
|
|
4
4
|
import {
|
|
5
5
|
extendComponent,
|
|
6
6
|
findElement,
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
getKeyedRepeater,
|
|
8
|
+
getSequentialRepeater,
|
|
9
9
|
onEvent,
|
|
10
10
|
nestComponent,
|
|
11
11
|
defineComponent,
|
|
12
12
|
extendPrototype,
|
|
13
|
-
|
|
13
|
+
stashMisc,
|
|
14
14
|
saveRef,
|
|
15
15
|
} from "./initCalls";
|
|
16
16
|
|
|
@@ -18,17 +18,17 @@ export {
|
|
|
18
18
|
extendComponent,
|
|
19
19
|
Component,
|
|
20
20
|
createComponent,
|
|
21
|
-
|
|
21
|
+
watch,
|
|
22
22
|
defineComponent,
|
|
23
23
|
extendPrototype,
|
|
24
24
|
findElement,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
getKeyedRepeater,
|
|
26
|
+
getSequentialRepeater,
|
|
27
|
+
KeyedRepeater,
|
|
28
28
|
mount,
|
|
29
29
|
nestComponent,
|
|
30
30
|
onEvent,
|
|
31
|
-
|
|
31
|
+
stashMisc,
|
|
32
32
|
saveRef,
|
|
33
|
-
|
|
33
|
+
SequentialRepeater,
|
|
34
34
|
};
|
package/lib/initCalls.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Component } from "./component";
|
|
2
2
|
import { Lookup } from "./lookup";
|
|
3
3
|
import { buildComponent, replaceNode } from "./utils";
|
|
4
|
-
import {
|
|
4
|
+
import { KeyedRepeater, SequentialRepeater } from "./repeaters";
|
|
5
5
|
const throwAway = document.createElement("template");
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -20,15 +20,15 @@ export const findElement = (rootElement, path) => {
|
|
|
20
20
|
return path.reduce((acc, index) => acc.childNodes[index], rootElement);
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
export const nestComponent = (rootElement, path, cls
|
|
23
|
+
export const nestComponent = (rootElement, path, cls) => {
|
|
24
24
|
const el = findElement(rootElement, path),
|
|
25
|
-
child = buildComponent(cls
|
|
25
|
+
child = buildComponent(cls);
|
|
26
26
|
replaceNode(el, child.el);
|
|
27
27
|
return child;
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
|
-
* Saves a reference to element or nested component.
|
|
31
|
+
* Saves a reference to element or nested component. Returns the element.
|
|
32
32
|
*/
|
|
33
33
|
export const saveRef = (element, component, name) => {
|
|
34
34
|
component.ref[name] = element;
|
|
@@ -36,10 +36,11 @@ export const saveRef = (element, component, name) => {
|
|
|
36
36
|
};
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
|
-
*
|
|
39
|
+
* Stash something on the component. Returns the element.
|
|
40
|
+
* The generated code is expected to keep track of the position.
|
|
40
41
|
*/
|
|
41
|
-
export const
|
|
42
|
-
component.
|
|
42
|
+
export const stashMisc = (element, component, object) => {
|
|
43
|
+
component._s.push(object);
|
|
43
44
|
return element;
|
|
44
45
|
};
|
|
45
46
|
|
|
@@ -48,15 +49,12 @@ export const onEvent = (element, eventName, callback) => {
|
|
|
48
49
|
return element;
|
|
49
50
|
};
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
*/
|
|
54
|
-
export const getKeyedPool = (cls, keyFn) => {
|
|
55
|
-
return new KeyedPool(cls, keyFn);
|
|
52
|
+
export const getKeyedRepeater = (cls, keyFn) => {
|
|
53
|
+
return new KeyedRepeater(cls, keyFn);
|
|
56
54
|
};
|
|
57
55
|
|
|
58
|
-
export const
|
|
59
|
-
return new
|
|
56
|
+
export const getSequentialRepeater = (cls) => {
|
|
57
|
+
return new SequentialRepeater(cls);
|
|
60
58
|
};
|
|
61
59
|
|
|
62
60
|
export function defineComponent(
|
|
@@ -82,21 +80,16 @@ export function extendComponent(
|
|
|
82
80
|
lookups,
|
|
83
81
|
buildFunction,
|
|
84
82
|
) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
sq: arr[1], // The shield query key
|
|
88
|
-
rv: arr[2], // whether shieldQuery should be flipped
|
|
89
|
-
sc: arr[3], // The number of items to shield
|
|
90
|
-
cb: arr[4], // The callbacks - object
|
|
91
|
-
}));
|
|
83
|
+
//Ensure these do not clash with fields on the component itself.
|
|
84
|
+
prototype._w = watches;
|
|
92
85
|
prototype._l = new Lookup(lookups);
|
|
93
86
|
prototype._b = buildFunction;
|
|
94
87
|
prototype._n = makeEl(html);
|
|
95
88
|
}
|
|
96
89
|
|
|
97
90
|
export function extendPrototype(base, prototypeExtras) {
|
|
98
|
-
const Constructor = function (
|
|
99
|
-
base.call(this
|
|
91
|
+
const Constructor = function () {
|
|
92
|
+
base.call(this);
|
|
100
93
|
};
|
|
101
94
|
Constructor.prototype = Object.create(base && base.prototype, {
|
|
102
95
|
constructor: {
|
|
@@ -3,17 +3,17 @@ import { createComponent } from "./utils";
|
|
|
3
3
|
/*
|
|
4
4
|
* Gets a component from the pool.
|
|
5
5
|
*/
|
|
6
|
-
|
|
6
|
+
function getComponent(pool, componentDefinition, ctrl, key, props) {
|
|
7
7
|
let component;
|
|
8
8
|
if (pool.hasOwnProperty(key)) {
|
|
9
9
|
component = pool[key];
|
|
10
|
-
component.render(
|
|
10
|
+
component.render(props, ctrl);
|
|
11
11
|
} else {
|
|
12
|
-
component = createComponent(
|
|
12
|
+
component = createComponent(componentDefinition, props, ctrl);
|
|
13
13
|
pool[key] = component;
|
|
14
14
|
}
|
|
15
15
|
return component;
|
|
16
|
-
}
|
|
16
|
+
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Trims the unwanted child elements from the end.
|
|
@@ -44,29 +44,27 @@ function pull(arr, item, to) {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
|
-
*
|
|
48
|
-
* Must not be shared.
|
|
47
|
+
* Repeats nested components, reusing items based on key.
|
|
49
48
|
*
|
|
50
|
-
* @param {
|
|
51
|
-
* @param {function} keyFn - A function which obtains the key
|
|
49
|
+
* @param {function} componentDefinition - The ComponentDefinition to create.
|
|
50
|
+
* @param {function} keyFn - A function which obtains the key.
|
|
52
51
|
*/
|
|
53
|
-
export function
|
|
54
|
-
this._v =
|
|
52
|
+
export function KeyedRepeater(componentDefinition, keyFn) {
|
|
53
|
+
this._v = componentDefinition;
|
|
55
54
|
this._f = keyFn;
|
|
56
55
|
this._k = []; // keys
|
|
57
56
|
this._p = {}; // pool of component instances
|
|
58
57
|
}
|
|
59
|
-
const proto =
|
|
58
|
+
const proto = KeyedRepeater.prototype;
|
|
60
59
|
|
|
61
60
|
/**
|
|
62
61
|
* Retrieves a single component. Though not used in wallace itself, it may
|
|
63
62
|
* be used elsewhere, such as in the router.
|
|
64
63
|
*
|
|
65
64
|
* @param {Object} item - The item which will be passed as props.
|
|
66
|
-
* @param {Component} parent - The parent component.
|
|
67
65
|
*/
|
|
68
|
-
proto.getOne = function (item,
|
|
69
|
-
return getComponent(this._p, this._v, this._f(item), item
|
|
66
|
+
proto.getOne = function (item, ctrl) {
|
|
67
|
+
return getComponent(this._p, this._v, ctrl, this._f(item), item);
|
|
70
68
|
};
|
|
71
69
|
|
|
72
70
|
/**
|
|
@@ -75,13 +73,12 @@ proto.getOne = function (item, parent) {
|
|
|
75
73
|
*
|
|
76
74
|
* @param {DOMElement} e - The DOM element to patch.
|
|
77
75
|
* @param {Array} items - Array of items which will be passed as props.
|
|
78
|
-
* @param {Component} parent - The parent component.
|
|
79
76
|
*/
|
|
80
|
-
proto.patch = function (e, items,
|
|
77
|
+
proto.patch = function (e, items, ctrl) {
|
|
81
78
|
// Attempt to speed up by reducing lookups. Does this even do anything?
|
|
82
79
|
// Does webpack undo this/do it for for me? Does the engine?
|
|
83
80
|
const pool = this._p;
|
|
84
|
-
const
|
|
81
|
+
const componentDefinition = this._v;
|
|
85
82
|
const keyFn = this._f;
|
|
86
83
|
const childNodes = e.childNodes;
|
|
87
84
|
const itemsLength = items.length;
|
|
@@ -94,7 +91,7 @@ proto.patch = function (e, items, parent) {
|
|
|
94
91
|
for (let i = 0; i < itemsLength; i++) {
|
|
95
92
|
item = items[i];
|
|
96
93
|
key = keyFn(item);
|
|
97
|
-
component = getComponent(pool,
|
|
94
|
+
component = getComponent(pool, componentDefinition, ctrl, key, item);
|
|
98
95
|
newKeys.push(key);
|
|
99
96
|
if (i > childElementCount) {
|
|
100
97
|
e.appendChild(component.el);
|
|
@@ -108,12 +105,12 @@ proto.patch = function (e, items, parent) {
|
|
|
108
105
|
};
|
|
109
106
|
|
|
110
107
|
/**
|
|
111
|
-
*
|
|
108
|
+
* Repeats nested components, yielding from its pool sequentially.
|
|
112
109
|
*
|
|
113
|
-
* @param {
|
|
110
|
+
* @param {componentDefinition} componentDefinition - The class ComponentDefinition to create.
|
|
114
111
|
*/
|
|
115
|
-
export function
|
|
116
|
-
this._v =
|
|
112
|
+
export function SequentialRepeater(componentDefinition) {
|
|
113
|
+
this._v = componentDefinition;
|
|
117
114
|
this._p = []; // pool of component instances
|
|
118
115
|
this._c = 0; // Child element count
|
|
119
116
|
}
|
|
@@ -124,11 +121,11 @@ export function SequentialPool(componentClass) {
|
|
|
124
121
|
*
|
|
125
122
|
* @param {DOMElement} e - The DOM element to patch.
|
|
126
123
|
* @param {Array} items - Array of items which will be passed as props.
|
|
127
|
-
* @param {
|
|
124
|
+
* @param {any} ctrl - The parent item's controller.
|
|
128
125
|
*/
|
|
129
|
-
|
|
126
|
+
SequentialRepeater.prototype.patch = function (e, items, ctrl) {
|
|
130
127
|
const pool = this._p;
|
|
131
|
-
const
|
|
128
|
+
const componentDefinition = this._v;
|
|
132
129
|
const childNodes = e.childNodes;
|
|
133
130
|
const itemsLength = items.length;
|
|
134
131
|
let item,
|
|
@@ -140,9 +137,9 @@ SequentialPool.prototype.patch = function (e, items, parent) {
|
|
|
140
137
|
item = items[i];
|
|
141
138
|
if (i < poolCount) {
|
|
142
139
|
component = pool[i];
|
|
143
|
-
component.render(item);
|
|
140
|
+
component.render(item, ctrl);
|
|
144
141
|
} else {
|
|
145
|
-
component = createComponent(
|
|
142
|
+
component = createComponent(componentDefinition, item, ctrl);
|
|
146
143
|
pool.push(component);
|
|
147
144
|
poolCount++;
|
|
148
145
|
}
|
package/lib/types.d.ts
CHANGED
|
@@ -27,9 +27,10 @@ declare module "wallace" {
|
|
|
27
27
|
element: string | HTMLElement,
|
|
28
28
|
component: Accepts<T>,
|
|
29
29
|
props?: T,
|
|
30
|
+
ctrl?: any,
|
|
30
31
|
): Component<T>;
|
|
31
32
|
|
|
32
|
-
export function
|
|
33
|
+
export function watch<T>(obj: T, callback: CallableFunction): T;
|
|
33
34
|
|
|
34
35
|
export function extendPrototype<T>(
|
|
35
36
|
base: Accepts<T>,
|
package/lib/utils.js
CHANGED
|
@@ -4,10 +4,9 @@
|
|
|
4
4
|
* @param {unsure} elementOrId Either a string representing an id, or an element.
|
|
5
5
|
* @param {class} cls The class of Component to create
|
|
6
6
|
* @param {object} props The props to pass to the component (optional)
|
|
7
|
-
* @param {object} parent The parent component (optional)
|
|
8
7
|
*/
|
|
9
|
-
export function mount(elementOrId, cls, props,
|
|
10
|
-
const component = createComponent(cls,
|
|
8
|
+
export function mount(elementOrId, cls, props, ctrl) {
|
|
9
|
+
const component = createComponent(cls, props, ctrl);
|
|
11
10
|
replaceNode(getElement(elementOrId), component.el);
|
|
12
11
|
return component;
|
|
13
12
|
}
|
|
@@ -26,35 +25,34 @@ export function getElement(elementOrId) {
|
|
|
26
25
|
* Creates a component and initialises it.
|
|
27
26
|
*
|
|
28
27
|
* @param {class} cls The class of Component to create
|
|
29
|
-
* @param {object} parent The parent component (optional)
|
|
30
28
|
* @param {object} props The props to pass to the component (optional)
|
|
31
29
|
*/
|
|
32
|
-
export function createComponent(cls,
|
|
33
|
-
const component = buildComponent(cls
|
|
34
|
-
component.render(props);
|
|
30
|
+
export function createComponent(cls, props, ctrl) {
|
|
31
|
+
const component = buildComponent(cls);
|
|
32
|
+
component.render(props, ctrl);
|
|
35
33
|
return component;
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
/**
|
|
39
|
-
* Builds a component.
|
|
37
|
+
* Builds a component's initial DOM.
|
|
40
38
|
*/
|
|
41
|
-
export function buildComponent(cls
|
|
42
|
-
const component = new cls(
|
|
43
|
-
const
|
|
44
|
-
const dom =
|
|
39
|
+
export function buildComponent(cls) {
|
|
40
|
+
const component = new cls();
|
|
41
|
+
const proto = cls.prototype;
|
|
42
|
+
const dom = proto._n.cloneNode(true);
|
|
45
43
|
component.el = dom;
|
|
46
|
-
|
|
44
|
+
proto._b(component, dom);
|
|
47
45
|
return component;
|
|
48
46
|
}
|
|
49
47
|
|
|
50
48
|
/**
|
|
51
|
-
* Wraps target in a Proxy which calls
|
|
49
|
+
* Wraps target in a Proxy which calls a function whenever it is modified.
|
|
52
50
|
*
|
|
53
51
|
* @param {*} target - Any object, including arrays.
|
|
54
|
-
* @param {*}
|
|
55
|
-
* @returns a Proxy object.
|
|
52
|
+
* @param {*} callback - A callback function.
|
|
53
|
+
* @returns a Proxy of the object.
|
|
56
54
|
*/
|
|
57
|
-
export
|
|
55
|
+
export function watch(target, callback) {
|
|
58
56
|
const handler = {
|
|
59
57
|
get(target, key) {
|
|
60
58
|
if (key == "isProxy") return true;
|
|
@@ -67,9 +65,9 @@ export const createProxy = (target, component) => {
|
|
|
67
65
|
},
|
|
68
66
|
set(target, key, value) {
|
|
69
67
|
target[key] = value;
|
|
70
|
-
|
|
68
|
+
callback();
|
|
71
69
|
return true;
|
|
72
70
|
},
|
|
73
71
|
};
|
|
74
72
|
return new Proxy(target, handler);
|
|
75
|
-
}
|
|
73
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wallace",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"author": "Andrew Buchan",
|
|
5
5
|
"description": "The framework that brings you glee.",
|
|
6
6
|
"license": "ISC",
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
"test": "jest --clearCache && jest"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"babel-plugin-wallace": "^0.0.
|
|
16
|
+
"babel-plugin-wallace": "^0.0.8",
|
|
17
17
|
"browserify": "^17.0.1"
|
|
18
18
|
},
|
|
19
|
-
"gitHead": "
|
|
19
|
+
"gitHead": "7548afec467b0e4f6e433a401cca08acb3dc7bc7"
|
|
20
20
|
}
|