@antimatter-audio/antimatter-ui 2.3.0 → 2.4.0
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/README.md +11 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.js +84 -80
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,6 +46,17 @@ NOTE: You will not see any _local_ changes you have made to this library yet. To
|
|
|
46
46
|
|
|
47
47
|
5. Run `npm run start` in **this project** to build your local changes.
|
|
48
48
|
|
|
49
|
+
6. If you don't see your changes, try running:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
rm -rf node_modules
|
|
53
|
+
npm cache clean --force
|
|
54
|
+
npm install
|
|
55
|
+
npm run link-ui
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
in **antimatter-plugin-template**.
|
|
59
|
+
|
|
49
60
|
You should now see your local changes to these components at [http://localhost:3000](http://localhost:3000).
|
|
50
61
|
|
|
51
62
|
### Adding new components
|
package/dist/index.d.ts
CHANGED
|
@@ -141,8 +141,19 @@ interface KnobProps {
|
|
|
141
141
|
knobType?: KNOB_TYPE;
|
|
142
142
|
className?: string;
|
|
143
143
|
style?: object;
|
|
144
|
+
mockInitialScaledValue?: number;
|
|
145
|
+
mockProperties?: JuceSliderProperties;
|
|
144
146
|
}
|
|
145
|
-
|
|
147
|
+
interface JuceSliderProperties {
|
|
148
|
+
name?: string;
|
|
149
|
+
label?: string;
|
|
150
|
+
start: number;
|
|
151
|
+
end: number;
|
|
152
|
+
interval: number;
|
|
153
|
+
skew?: number;
|
|
154
|
+
numSteps?: number;
|
|
155
|
+
}
|
|
156
|
+
declare function Knob({ label, labelPosition, showValue, knobType, id, className, mockInitialScaledValue, mockProperties, }: React__default.PropsWithChildren<KnobProps>): React__default.JSX.Element;
|
|
146
157
|
declare namespace Knob {
|
|
147
158
|
var LABEL_POSITION: typeof POSITION;
|
|
148
159
|
var KNOB_TYPE: typeof KNOB_TYPE;
|
package/dist/index.js
CHANGED
|
@@ -476,6 +476,22 @@ var TextLabel = function(param) {
|
|
|
476
476
|
}, text ? text : children);
|
|
477
477
|
};
|
|
478
478
|
|
|
479
|
+
var clamp = function(val) {
|
|
480
|
+
var min = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : 0, max = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : 1;
|
|
481
|
+
return Math.max(min, Math.min(max, val));
|
|
482
|
+
};
|
|
483
|
+
// mocks the `normalisedToScaledValue` internal method from JUCE
|
|
484
|
+
// This will allow us to test the JS UI without needing access to the JUCE backend
|
|
485
|
+
function normalisedToScaledValue(param) {
|
|
486
|
+
var normalisedValue = param.normalisedValue, _param_skew = param.skew, skew = _param_skew === void 0 ? 1.0 : _param_skew, end = param.end, start = param.start;
|
|
487
|
+
return Math.pow(normalisedValue, 1 / skew) * (end - start) + start;
|
|
488
|
+
}
|
|
489
|
+
// mocks the `getNormalisedValue` method from JUCE
|
|
490
|
+
// This will allow us to test the JS UI without needing access to the JUCE backend
|
|
491
|
+
function getNormalisedValue(param) {
|
|
492
|
+
var scaledValue = param.scaledValue, start = param.start, end = param.end, _param_skew = param.skew, skew = _param_skew === void 0 ? 1.0 : _param_skew;
|
|
493
|
+
return Math.pow((scaledValue - start) / (end - start), skew);
|
|
494
|
+
}
|
|
479
495
|
var decimalToPercent = function(decimal) {
|
|
480
496
|
return Math.round(100 * (typeof decimal === 'string' ? parseFloat(decimal) : decimal));
|
|
481
497
|
};
|
|
@@ -579,96 +595,85 @@ var KNOB_TYPE;
|
|
|
579
595
|
KNOB_TYPE["BIPOLAR"] = "BIPOLAR";
|
|
580
596
|
})(KNOB_TYPE || (KNOB_TYPE = {}));
|
|
581
597
|
function Knob(param) {
|
|
582
|
-
var label = param.label, _param_labelPosition = param.labelPosition, labelPosition = _param_labelPosition === void 0 ? POSITION.TOP : _param_labelPosition, _param_showValue = param.showValue, showValue = _param_showValue === void 0 ? true : _param_showValue, _param_knobType = param.knobType, knobType = _param_knobType === void 0 ? "STANDARD" : _param_knobType, id = param.id, className = param.className
|
|
598
|
+
var label = param.label, _param_labelPosition = param.labelPosition, labelPosition = _param_labelPosition === void 0 ? POSITION.TOP : _param_labelPosition, _param_showValue = param.showValue, showValue = _param_showValue === void 0 ? true : _param_showValue, _param_knobType = param.knobType, knobType = _param_knobType === void 0 ? "STANDARD" : _param_knobType, id = param.id, className = param.className, _param_mockInitialScaledValue = param.mockInitialScaledValue, mockInitialScaledValue = _param_mockInitialScaledValue === void 0 ? 0 : _param_mockInitialScaledValue, _param_mockProperties = param.// From https://github.com/juce-framework/JUCE/blob/51d11a2be6d5c97ccf12b4e5e827006e19f0555a/modules/juce_gui_extra/native/javascript/index.js#L135
|
|
599
|
+
mockProperties, mockProperties = _param_mockProperties === void 0 ? {
|
|
600
|
+
start: 0,
|
|
601
|
+
end: 100,
|
|
602
|
+
interval: 0.01,
|
|
603
|
+
skew: 1,
|
|
604
|
+
name: '',
|
|
605
|
+
label: '',
|
|
606
|
+
numSteps: 100
|
|
607
|
+
} : _param_mockProperties;
|
|
583
608
|
var knobState = Juce.getSliderState(id);
|
|
609
|
+
// if isLocalhost is true, the front end app is running outside of JUCE in a browser.
|
|
610
|
+
var isLocalhost = window.location.hostname === 'localhost';
|
|
611
|
+
// getNormalisedValue:
|
|
612
|
+
/**
|
|
613
|
+
* Returns the normalised value of the corresponding backend parameter. This value is always in the
|
|
614
|
+
* [0, 1] range (inclusive).
|
|
615
|
+
*
|
|
616
|
+
* The meaning of this range is the same as in the case of
|
|
617
|
+
* AudioProcessorParameter::getValue() (C++).
|
|
618
|
+
*/ // See https://github.com/juce-framework/JUCE/blob/51d11a2be6d5c97ccf12b4e5e827006e19f0555a/modules/juce_gui_extra/native/javascript/index.js#L230C1-L238C6
|
|
584
619
|
var _useState = _sliced_to_array$1(useState(0), 2), normalizedValue = _useState[0], setNormalizedValue = _useState[1];
|
|
585
|
-
|
|
620
|
+
// getScaledValue:
|
|
621
|
+
/**
|
|
622
|
+
* Returns the scaled value of the parameter. This corresponds to the return value of
|
|
623
|
+
* NormalisableRange::convertFrom0to1() (C++). This value will differ from a linear
|
|
624
|
+
* [0, 1] range if a non-default NormalisableRange was set for the parameter.
|
|
625
|
+
*/ var _useState1 = _sliced_to_array$1(useState(0), 2), scaledValue = _useState1[0], setScaledValue = _useState1[1];
|
|
586
626
|
var _useState2 = _sliced_to_array$1(useState(null), 2), properties = _useState2[0], setProperties = _useState2[1];
|
|
587
|
-
// const [log, setLog] = useState('');
|
|
588
627
|
var handleChange = function(_, newValue) {
|
|
589
|
-
knobState.setNormalisedValue(newValue);
|
|
590
|
-
|
|
628
|
+
knobState === null || knobState === void 0 ? void 0 : knobState.setNormalisedValue(newValue);
|
|
629
|
+
// If the front end app is running in a browser, set the new value here.
|
|
630
|
+
// Otherwise, the value will come from JUCE.
|
|
631
|
+
if (isLocalhost) {
|
|
632
|
+
var newValueClamped = clamp(newValue);
|
|
633
|
+
setNormalizedValue(newValueClamped);
|
|
634
|
+
(mockProperties === null || mockProperties === void 0 ? void 0 : mockProperties.start) && (mockProperties === null || mockProperties === void 0 ? void 0 : mockProperties.end) && setScaledValue(normalisedToScaledValue({
|
|
635
|
+
normalisedValue: newValueClamped,
|
|
636
|
+
start: mockProperties === null || mockProperties === void 0 ? void 0 : mockProperties.start,
|
|
637
|
+
end: mockProperties === null || mockProperties === void 0 ? void 0 : mockProperties.end
|
|
638
|
+
}));
|
|
639
|
+
}
|
|
591
640
|
};
|
|
592
|
-
// const mouseDown = () => {
|
|
593
|
-
// knobState.sliderDragStarted();
|
|
594
|
-
// };
|
|
595
|
-
// const changeCommitted = (event: any, newValue: any) => {
|
|
596
|
-
// console.log(event);
|
|
597
|
-
// knobState.setNormalisedValue(newValue);
|
|
598
|
-
// knobState.sliderDragEnded();
|
|
599
|
-
// };
|
|
600
641
|
useEffect(function() {
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
642
|
+
{
|
|
643
|
+
if (isLocalhost) {
|
|
644
|
+
// This sets 'mockProperties' as the value for 'properties' if the app is running in a browser.
|
|
645
|
+
setProperties(mockProperties !== null && mockProperties !== void 0 ? mockProperties : null);
|
|
646
|
+
setNormalizedValue(getNormalisedValue({
|
|
647
|
+
scaledValue: mockInitialScaledValue,
|
|
648
|
+
start: mockProperties === null || mockProperties === void 0 ? void 0 : mockProperties.start,
|
|
649
|
+
end: mockProperties === null || mockProperties === void 0 ? void 0 : mockProperties.end
|
|
650
|
+
}));
|
|
651
|
+
setScaledValue(mockInitialScaledValue);
|
|
652
|
+
knobState.setNormalisedValue(mockInitialScaledValue);
|
|
653
|
+
return;
|
|
654
|
+
} else {
|
|
655
|
+
var valueListenerId = knobState.valueChangedEvent.addListener(function() {
|
|
656
|
+
setNormalizedValue(knobState.getNormalisedValue());
|
|
657
|
+
setScaledValue(knobState.getScaledValue());
|
|
658
|
+
});
|
|
659
|
+
var propertiesListenerId = knobState.propertiesChangedEvent.addListener(function() {
|
|
660
|
+
return setProperties(knobState.properties);
|
|
661
|
+
});
|
|
662
|
+
setProperties(knobState.properties);
|
|
663
|
+
setNormalizedValue(knobState.getNormalisedValue());
|
|
664
|
+
setScaledValue(knobState.getScaledValue());
|
|
665
|
+
return function cleanup() {
|
|
666
|
+
knobState.valueChangedEvent.removeListener(valueListenerId);
|
|
667
|
+
knobState.propertiesChangedEvent.removeListener(propertiesListenerId);
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
}
|
|
615
671
|
}, []);
|
|
616
|
-
// function calculateValue() {
|
|
617
|
-
// return sliderState.getScaledValue();
|
|
618
|
-
// }
|
|
619
|
-
// const [hasStoreData, setHasStoreData] = useState<boolean>(false);
|
|
620
|
-
// function snapToLegalValue(value: any) {
|
|
621
|
-
// if (interval == 0) return value;
|
|
622
|
-
// return clamp(
|
|
623
|
-
// min + interval * Math.floor((value - min) / interval + 0.5),
|
|
624
|
-
// min,
|
|
625
|
-
// max,
|
|
626
|
-
// );
|
|
627
|
-
// }
|
|
628
|
-
// useEffect(() => {
|
|
629
|
-
// const knobState = Juce.getSliderState(id);
|
|
630
|
-
// knobState.valueChangedEvent.addListener(() => {
|
|
631
|
-
// console.log('value changed listener');
|
|
632
|
-
// setValue(knobState.getNormalisedValue());
|
|
633
|
-
// setPr
|
|
634
|
-
// setMin(knobState?.properties?.start);
|
|
635
|
-
// setMax(knobState?.properties?.end);
|
|
636
|
-
// setInterval(knobState?.properties?.interval);
|
|
637
|
-
// // setValue(knobState?.scaledValue);
|
|
638
|
-
// });
|
|
639
|
-
// knobRef?.current?.addEventListener('mousedown', (event: any) => {
|
|
640
|
-
// console.log(event, 'mousedown listener');
|
|
641
|
-
// knobState.sliderDragStarted();
|
|
642
|
-
// });
|
|
643
|
-
// knobRef?.current?.addEventListener('mouseup', (event: any) => {
|
|
644
|
-
// console.log(knobState, event, 'mouseup listener');
|
|
645
|
-
// knobState.sliderDragEnded();
|
|
646
|
-
// });
|
|
647
|
-
// knobRef?.current?.addEventListener('mousemove', (event: any) => {
|
|
648
|
-
// console.log(knobState, 'mousemove listener');
|
|
649
|
-
// knobState.setNormalisedValue(value + interval);
|
|
650
|
-
// });
|
|
651
|
-
// }, []);
|
|
652
672
|
var bindDrag = useDrag(function(param) {
|
|
653
673
|
var down = param.down, delta = param.delta;
|
|
654
674
|
if (down) {
|
|
655
|
-
// const knobState = Juce.getSliderState(id);
|
|
656
|
-
// console.log(interval, value, 'interval');
|
|
657
|
-
// setValue(() => {
|
|
658
|
-
// // console.log(value, delta, delta[1] * -1, 'value, delta[1]');
|
|
659
|
-
// const newValue = value + delta[1] * (properties?.interval || 0.01) * -1;
|
|
660
|
-
// return clamp(newValue);
|
|
661
|
-
// });
|
|
662
|
-
// console.log(value, 'VALUE');
|
|
663
675
|
var newValue = normalizedValue + delta[1] * ((properties === null || properties === void 0 ? void 0 : properties.interval) || 0.01) * -1;
|
|
664
|
-
// knobState.setNormalisedValue(Math.round(normalizedValue * 100) / 100);
|
|
665
676
|
handleChange(event, newValue);
|
|
666
|
-
// console.log(
|
|
667
|
-
// knobState,
|
|
668
|
-
// value,
|
|
669
|
-
// snapToLegalValue(value),
|
|
670
|
-
// 'KNOBSTATE_______',
|
|
671
|
-
// );
|
|
672
677
|
}
|
|
673
678
|
}, {
|
|
674
679
|
preventDefault: true
|
|
@@ -692,14 +697,13 @@ function Knob(param) {
|
|
|
692
697
|
}), knobType === "BIPOLAR" && /*#__PURE__*/ React__default.createElement("div", {
|
|
693
698
|
className: "Knob-center-marker"
|
|
694
699
|
}), /*#__PURE__*/ React__default.createElement("div", _object_spread_props$1(_object_spread$1({
|
|
695
|
-
// onPointerDown={(e) => e.currentTarget.focus()}
|
|
696
700
|
className: classnames('Knob', className)
|
|
697
701
|
}, bindDrag()), {
|
|
698
702
|
style: {
|
|
699
703
|
transform: knobType === "STANDARD" ? '' : ')',
|
|
700
704
|
touchAction: 'none'
|
|
701
705
|
}
|
|
702
|
-
}), /*#__PURE__*/ React__default.createElement("div", {
|
|
706
|
+
}), /*#__PURE__*/ React__default.createElement("ul", null, /*#__PURE__*/ React__default.createElement("li", null, properties === null || properties === void 0 ? void 0 : properties.start), /*#__PURE__*/ React__default.createElement("li", null, properties === null || properties === void 0 ? void 0 : properties.end), /*#__PURE__*/ React__default.createElement("li", null, normalizedValue)), /*#__PURE__*/ React__default.createElement("div", {
|
|
703
707
|
className: "Knob-inner",
|
|
704
708
|
style: {
|
|
705
709
|
rotate: "".concat(knobType === "STANDARD" ? normalizedValue : normalizedValue + 0.5, "turn")
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../node_modules/style-inject/dist/style-inject.es.js"],"sourcesContent":["function styleInject(css, ref) {\n if ( ref === void 0 ) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') { return; }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nexport default styleInject;\n"],"names":[],"mappings":";;;;;;;;AAAA,SAAS,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE;AAC/B,EAAE,KAAK,GAAG,KAAK,KAAK,CAAC,GAAG,GAAG,GAAG,EAAE,CAAC;AACjC,EAAE,IAAI,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;AAC9B;AACA,EAAE,IAAI,CAAC,GAAG,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,EAAE,OAAO,EAAE;AAC1D;AACA,EAAE,IAAI,IAAI,GAAG,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AACvE,EAAE,IAAI,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;AAC9C,EAAE,KAAK,CAAC,IAAI,GAAG,UAAU,CAAC;AAC1B;AACA,EAAE,IAAI,QAAQ,KAAK,KAAK,EAAE;AAC1B,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE;AACzB,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;AAChD,KAAK,MAAM;AACX,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AAC9B,KAAK;AACL,GAAG,MAAM;AACT,IAAI,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AAC5B,GAAG;AACH;AACA,EAAE,IAAI,KAAK,CAAC,UAAU,EAAE;AACxB,IAAI,KAAK,CAAC,UAAU,CAAC,OAAO,GAAG,GAAG,CAAC;AACnC,GAAG,MAAM;AACT,IAAI,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;AACpD,GAAG;AACH
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../node_modules/style-inject/dist/style-inject.es.js"],"sourcesContent":["function styleInject(css, ref) {\n if ( ref === void 0 ) ref = {};\n var insertAt = ref.insertAt;\n\n if (!css || typeof document === 'undefined') { return; }\n\n var head = document.head || document.getElementsByTagName('head')[0];\n var style = document.createElement('style');\n style.type = 'text/css';\n\n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild);\n } else {\n head.appendChild(style);\n }\n } else {\n head.appendChild(style);\n }\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n style.appendChild(document.createTextNode(css));\n }\n}\n\nexport default styleInject;\n"],"names":[],"mappings":";;;;;;;;AAAA,SAAS,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE;AAC/B,EAAE,KAAK,GAAG,KAAK,KAAK,CAAC,GAAG,GAAG,GAAG,EAAE,CAAC;AACjC,EAAE,IAAI,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;AAC9B;AACA,EAAE,IAAI,CAAC,GAAG,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,EAAE,OAAO,EAAE;AAC1D;AACA,EAAE,IAAI,IAAI,GAAG,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AACvE,EAAE,IAAI,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;AAC9C,EAAE,KAAK,CAAC,IAAI,GAAG,UAAU,CAAC;AAC1B;AACA,EAAE,IAAI,QAAQ,KAAK,KAAK,EAAE;AAC1B,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE;AACzB,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;AAChD,KAAK,MAAM;AACX,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AAC9B,KAAK;AACL,GAAG,MAAM;AACT,IAAI,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AAC5B,GAAG;AACH;AACA,EAAE,IAAI,KAAK,CAAC,UAAU,EAAE;AACxB,IAAI,KAAK,CAAC,UAAU,CAAC,OAAO,GAAG,GAAG,CAAC;AACnC,GAAG,MAAM;AACT,IAAI,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;AACpD,GAAG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","x_google_ignoreList":[0]}
|