haori 0.2.0 → 0.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.ja.md +3 -1
- package/README.md +3 -1
- package/dist/haori.cjs.js +11 -11
- package/dist/haori.cjs.js.map +1 -1
- package/dist/haori.es.js +1134 -898
- package/dist/haori.es.js.map +1 -1
- package/dist/haori.iife.js +11 -11
- package/dist/haori.iife.js.map +1 -1
- package/dist/index.d.ts +38 -6
- package/dist/package.json +1 -1
- package/dist/src/core.d.ts.map +1 -1
- package/dist/src/core.js +1 -1
- package/dist/src/core.js.map +1 -1
- package/dist/src/env.d.ts +18 -0
- package/dist/src/env.d.ts.map +1 -1
- package/dist/src/env.js +44 -0
- package/dist/src/env.js.map +1 -1
- package/dist/src/event.d.ts +16 -1
- package/dist/src/event.d.ts.map +1 -1
- package/dist/src/event.js +4 -4
- package/dist/src/event.js.map +1 -1
- package/dist/src/haori.d.ts +13 -0
- package/dist/src/haori.d.ts.map +1 -1
- package/dist/src/haori.js +18 -0
- package/dist/src/haori.js.map +1 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/intersect.d.ts +21 -0
- package/dist/src/intersect.d.ts.map +1 -0
- package/dist/src/intersect.js +188 -0
- package/dist/src/intersect.js.map +1 -0
- package/dist/src/observer.d.ts.map +1 -1
- package/dist/src/observer.js +5 -0
- package/dist/src/observer.js.map +1 -1
- package/dist/src/procedure.d.ts +18 -0
- package/dist/src/procedure.d.ts.map +1 -1
- package/dist/src/procedure.js +272 -172
- package/dist/src/procedure.js.map +1 -1
- package/dist/tests/env.test.js +28 -0
- package/dist/tests/env.test.js.map +1 -1
- package/dist/tests/event.test.js +13 -1
- package/dist/tests/event.test.js.map +1 -1
- package/dist/tests/intersect.test.d.ts +2 -0
- package/dist/tests/intersect.test.d.ts.map +1 -0
- package/dist/tests/intersect.test.js +173 -0
- package/dist/tests/intersect.test.js.map +1 -0
- package/dist/tests/procedure-bind-append.test.d.ts +2 -0
- package/dist/tests/procedure-bind-append.test.d.ts.map +1 -0
- package/dist/tests/procedure-bind-append.test.js +80 -0
- package/dist/tests/procedure-bind-append.test.js.map +1 -0
- package/dist/tests/procedure-fetch-options.test.js +58 -0
- package/dist/tests/procedure-fetch-options.test.js.map +1 -1
- package/package.json +1 -1
package/dist/src/procedure.js
CHANGED
|
@@ -31,6 +31,58 @@ function resolveProcedureHaoriApi() {
|
|
|
31
31
|
const hasRequiredMethods = PROCEDURE_HAORI_METHOD_NAMES.every(methodName => typeof candidate?.[methodName] === 'function');
|
|
32
32
|
return hasRequiredMethods ? candidate : Haori;
|
|
33
33
|
}
|
|
34
|
+
const QUERY_TRANSPORT_METHODS = new Set(['GET', 'HEAD', 'OPTIONS']);
|
|
35
|
+
/**
|
|
36
|
+
* URL クエリ化の対象メソッドかどうかを判定します。
|
|
37
|
+
*
|
|
38
|
+
* @param method 判定対象の HTTP メソッド。
|
|
39
|
+
* @return クエリ送信対象なら true。
|
|
40
|
+
*/
|
|
41
|
+
function isQueryTransportMethod(method) {
|
|
42
|
+
return QUERY_TRANSPORT_METHODS.has(method.toUpperCase());
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 送信データを URLSearchParams に追加します。
|
|
46
|
+
*
|
|
47
|
+
* @param params 追加先の URLSearchParams。
|
|
48
|
+
* @param payload 追加対象の送信データ。
|
|
49
|
+
* @return 戻り値はありません。
|
|
50
|
+
*/
|
|
51
|
+
function appendPayloadToSearchParams(params, payload) {
|
|
52
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
53
|
+
if (value === undefined) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (value === null) {
|
|
57
|
+
params.append(key, '');
|
|
58
|
+
}
|
|
59
|
+
else if (Array.isArray(value)) {
|
|
60
|
+
value.forEach(item => {
|
|
61
|
+
params.append(key, String(item));
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
else if (typeof value === 'object' || typeof value === 'function') {
|
|
65
|
+
params.append(key, JSON.stringify(value));
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
params.append(key, String(value));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 送信データをクエリ文字列へ付加した URL を返します。
|
|
74
|
+
*
|
|
75
|
+
* @param fetchUrl 元のフェッチ URL。
|
|
76
|
+
* @param payload 追加対象の送信データ。
|
|
77
|
+
* @return クエリ文字列を付加した URL。
|
|
78
|
+
*/
|
|
79
|
+
function appendPayloadToUrl(fetchUrl, payload) {
|
|
80
|
+
const url = new URL(fetchUrl, window.location.href);
|
|
81
|
+
const params = new URLSearchParams(url.search);
|
|
82
|
+
appendPayloadToSearchParams(params, payload);
|
|
83
|
+
url.search = params.toString();
|
|
84
|
+
return url.toString();
|
|
85
|
+
}
|
|
34
86
|
/**
|
|
35
87
|
* 手続き的処理管理クラスです。
|
|
36
88
|
*/
|
|
@@ -401,6 +453,16 @@ ${body}
|
|
|
401
453
|
const paramsString = fragment.getRawAttribute(bindParamsAttr);
|
|
402
454
|
options.bindParams = paramsString.split('&').map(p => p.trim());
|
|
403
455
|
}
|
|
456
|
+
const bindAppendAttr = event
|
|
457
|
+
? Procedure.attrName(event, 'bind-append')
|
|
458
|
+
: Procedure.attrName(null, 'bind-append', true);
|
|
459
|
+
if (fragment.hasAttribute(bindAppendAttr)) {
|
|
460
|
+
const paramsString = fragment.getRawAttribute(bindAppendAttr);
|
|
461
|
+
options.bindAppendParams = paramsString
|
|
462
|
+
.split('&')
|
|
463
|
+
.map(p => p.trim())
|
|
464
|
+
.filter(Boolean);
|
|
465
|
+
}
|
|
404
466
|
if (event) {
|
|
405
467
|
if (fragment.hasAttribute(Procedure.attrName(event, 'adjust'))) {
|
|
406
468
|
const adjustSelector = fragment.getRawAttribute(Procedure.attrName(event, 'adjust'));
|
|
@@ -600,180 +662,194 @@ ${body}
|
|
|
600
662
|
* @returns 実行結果のPromise
|
|
601
663
|
*/
|
|
602
664
|
run() {
|
|
665
|
+
return this.runWithResult().then(() => undefined);
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* 一連の処理を実行し、成功したかどうかを返します。
|
|
669
|
+
*
|
|
670
|
+
* @returns 成功した場合は true、途中停止や失敗時は false
|
|
671
|
+
*/
|
|
672
|
+
runWithResult() {
|
|
673
|
+
return this.execute();
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* 一連の処理を実行します。成功結果を内部で扱うための実体です。
|
|
677
|
+
*
|
|
678
|
+
* @returns 実行成功時は true、停止や失敗時は false
|
|
679
|
+
*/
|
|
680
|
+
async execute() {
|
|
603
681
|
if (Object.keys(this.options).length === 0) {
|
|
604
|
-
return
|
|
682
|
+
return false;
|
|
605
683
|
}
|
|
606
684
|
if (this.options.formFragment &&
|
|
607
685
|
this.validate(this.options.formFragment) === false) {
|
|
608
|
-
return
|
|
686
|
+
return false;
|
|
609
687
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
}
|
|
688
|
+
const confirmed = await this.confirm();
|
|
689
|
+
if (!confirmed) {
|
|
690
|
+
return false;
|
|
691
|
+
}
|
|
692
|
+
let fetchUrl = this.options.fetchUrl;
|
|
693
|
+
let fetchOptions = this.options.fetchOptions;
|
|
694
|
+
if (this.options.beforeCallback) {
|
|
695
|
+
const result = this.options.beforeCallback(fetchUrl || null, fetchOptions || null);
|
|
696
|
+
if (result !== undefined && result !== null) {
|
|
697
|
+
if (result === false || (typeof result === 'object' && result.stop)) {
|
|
698
|
+
return false;
|
|
699
|
+
}
|
|
700
|
+
if (typeof result === 'object') {
|
|
701
|
+
fetchUrl = ('fetchUrl' in result ? result.fetchUrl : fetchUrl);
|
|
702
|
+
fetchOptions = ('fetchOptions' in result ? result.fetchOptions : fetchOptions);
|
|
626
703
|
}
|
|
627
704
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
705
|
+
}
|
|
706
|
+
// フォーム値と data を統合してペイロードを作成
|
|
707
|
+
const payload = {};
|
|
708
|
+
if (this.options.formFragment) {
|
|
709
|
+
const formValues = Form.getValues(this.options.formFragment);
|
|
710
|
+
Object.assign(payload, formValues);
|
|
711
|
+
}
|
|
712
|
+
if (this.options.data && typeof this.options.data === 'object') {
|
|
713
|
+
Object.assign(payload, this.options.data);
|
|
714
|
+
}
|
|
715
|
+
const hasPayload = Object.keys(payload).length > 0;
|
|
716
|
+
if (fetchUrl) {
|
|
717
|
+
const finalOptions = { ...(fetchOptions || {}) };
|
|
718
|
+
const headers = new Headers(finalOptions.headers || undefined);
|
|
719
|
+
const requestedMethod = (finalOptions.method || 'GET').toUpperCase();
|
|
720
|
+
const isDemoQueryNormalization = Env.runtime === 'demo' && !isQueryTransportMethod(requestedMethod);
|
|
721
|
+
const method = isDemoQueryNormalization ? 'GET' : requestedMethod;
|
|
722
|
+
finalOptions.method = method;
|
|
723
|
+
if (method === 'GET' || method === 'HEAD' || method === 'OPTIONS') {
|
|
724
|
+
if (hasPayload) {
|
|
725
|
+
fetchUrl = appendPayloadToUrl(fetchUrl, payload);
|
|
726
|
+
}
|
|
636
727
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
const params = new URLSearchParams(url.search);
|
|
646
|
-
for (const [k, v] of Object.entries(payload)) {
|
|
647
|
-
if (v === undefined) {
|
|
648
|
-
continue;
|
|
649
|
-
}
|
|
650
|
-
if (v === null) {
|
|
651
|
-
params.append(k, '');
|
|
652
|
-
}
|
|
653
|
-
else if (Array.isArray(v)) {
|
|
654
|
-
v.forEach(item => {
|
|
655
|
-
params.append(k, String(item));
|
|
656
|
-
});
|
|
657
|
-
}
|
|
658
|
-
else if (typeof v === 'object' || typeof v === 'function') {
|
|
659
|
-
params.append(k, JSON.stringify(v));
|
|
660
|
-
}
|
|
661
|
-
else {
|
|
662
|
-
params.append(k, String(v));
|
|
663
|
-
}
|
|
728
|
+
else if (hasPayload) {
|
|
729
|
+
const contentType = headers.get('Content-Type') || '';
|
|
730
|
+
if (/multipart\/form-data/i.test(contentType)) {
|
|
731
|
+
headers.delete('Content-Type');
|
|
732
|
+
const formData = new FormData();
|
|
733
|
+
for (const [k, v] of Object.entries(payload)) {
|
|
734
|
+
if (v === undefined || v === null) {
|
|
735
|
+
formData.append(k, '');
|
|
664
736
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
else if (hasPayload) {
|
|
670
|
-
const contentType = headers.get('Content-Type') || '';
|
|
671
|
-
if (/multipart\/form-data/i.test(contentType)) {
|
|
672
|
-
headers.delete('Content-Type');
|
|
673
|
-
const formData = new FormData();
|
|
674
|
-
for (const [k, v] of Object.entries(payload)) {
|
|
675
|
-
if (v === undefined || v === null) {
|
|
676
|
-
formData.append(k, '');
|
|
677
|
-
}
|
|
678
|
-
else if (v instanceof Blob) {
|
|
679
|
-
formData.append(k, v);
|
|
680
|
-
}
|
|
681
|
-
else if (Array.isArray(v)) {
|
|
682
|
-
v.forEach(item => formData.append(k, String(item)));
|
|
683
|
-
}
|
|
684
|
-
else if (typeof v === 'object') {
|
|
685
|
-
formData.append(k, JSON.stringify(v));
|
|
686
|
-
}
|
|
687
|
-
else {
|
|
688
|
-
formData.append(k, String(v));
|
|
689
|
-
}
|
|
737
|
+
else if (v instanceof Blob) {
|
|
738
|
+
formData.append(k, v);
|
|
690
739
|
}
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
if (v === null) {
|
|
700
|
-
params.append(k, '');
|
|
701
|
-
}
|
|
702
|
-
else if (Array.isArray(v)) {
|
|
703
|
-
v.forEach(item => params.append(k, String(item)));
|
|
704
|
-
}
|
|
705
|
-
else if (typeof v === 'object') {
|
|
706
|
-
params.append(k, JSON.stringify(v));
|
|
707
|
-
}
|
|
708
|
-
else {
|
|
709
|
-
params.append(k, String(v));
|
|
710
|
-
}
|
|
740
|
+
else if (Array.isArray(v)) {
|
|
741
|
+
v.forEach(item => formData.append(k, String(item)));
|
|
742
|
+
}
|
|
743
|
+
else if (typeof v === 'object') {
|
|
744
|
+
formData.append(k, JSON.stringify(v));
|
|
745
|
+
}
|
|
746
|
+
else {
|
|
747
|
+
formData.append(k, String(v));
|
|
711
748
|
}
|
|
712
|
-
finalOptions.body = params;
|
|
713
|
-
}
|
|
714
|
-
else {
|
|
715
|
-
headers.set('Content-Type', 'application/json');
|
|
716
|
-
finalOptions.body = JSON.stringify(payload);
|
|
717
749
|
}
|
|
750
|
+
finalOptions.body = formData;
|
|
718
751
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
return fetch(fetchUrl, finalOptions)
|
|
725
|
-
.then(response => {
|
|
726
|
-
return this.handleFetchResult(response, fetchUrl || undefined, startedAt);
|
|
727
|
-
})
|
|
728
|
-
.catch(error => {
|
|
729
|
-
if (fetchUrl) {
|
|
730
|
-
HaoriEvent.fetchError(this.options.targetFragment.getTarget(), fetchUrl, error);
|
|
752
|
+
else if (/application\/x-www-form-urlencoded/i.test(contentType)) {
|
|
753
|
+
const params = new URLSearchParams();
|
|
754
|
+
for (const [k, v] of Object.entries(payload)) {
|
|
755
|
+
if (v === undefined) {
|
|
756
|
+
continue;
|
|
731
757
|
}
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
758
|
+
if (v === null) {
|
|
759
|
+
params.append(k, '');
|
|
760
|
+
}
|
|
761
|
+
else if (Array.isArray(v)) {
|
|
762
|
+
v.forEach(item => params.append(k, String(item)));
|
|
763
|
+
}
|
|
764
|
+
else if (typeof v === 'object') {
|
|
765
|
+
params.append(k, JSON.stringify(v));
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
params.append(k, String(v));
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
finalOptions.body = params;
|
|
739
772
|
}
|
|
740
773
|
else {
|
|
741
|
-
|
|
774
|
+
headers.set('Content-Type', 'application/json');
|
|
775
|
+
finalOptions.body = JSON.stringify(payload);
|
|
742
776
|
}
|
|
743
777
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
778
|
+
finalOptions.headers = headers;
|
|
779
|
+
let queryString;
|
|
780
|
+
if (isDemoQueryNormalization) {
|
|
781
|
+
queryString = fetchUrl
|
|
782
|
+
? new URL(fetchUrl, window.location.href).search || undefined
|
|
783
|
+
: undefined;
|
|
784
|
+
headers.delete('Content-Type');
|
|
785
|
+
Log.info('Haori demo fetch normalization', {
|
|
786
|
+
runtime: Env.runtime,
|
|
787
|
+
requestedMethod,
|
|
788
|
+
effectiveMethod: method,
|
|
789
|
+
transportMode: 'query-get',
|
|
790
|
+
url: fetchUrl,
|
|
791
|
+
payload: hasPayload ? payload : undefined,
|
|
792
|
+
queryString,
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
// fetchstartイベントを発火
|
|
796
|
+
if (this.options.targetFragment && fetchUrl) {
|
|
797
|
+
const startedAt = performance.now();
|
|
798
|
+
const fetchStartMetadata = {
|
|
799
|
+
runtime: Env.runtime,
|
|
800
|
+
requestedMethod,
|
|
801
|
+
effectiveMethod: method,
|
|
802
|
+
transportMode: isDemoQueryNormalization ? 'query-get' : 'http',
|
|
803
|
+
...(isDemoQueryNormalization ? { queryString } : {}),
|
|
804
|
+
};
|
|
805
|
+
HaoriEvent.fetchStart(this.options.targetFragment.getTarget(), fetchUrl, finalOptions, hasPayload ? payload : undefined, fetchStartMetadata);
|
|
806
|
+
return fetch(fetchUrl, finalOptions)
|
|
807
|
+
.then(response => {
|
|
808
|
+
return this.handleFetchResult(response, fetchUrl || undefined, startedAt);
|
|
809
|
+
})
|
|
810
|
+
.catch(error => {
|
|
811
|
+
if (fetchUrl) {
|
|
812
|
+
HaoriEvent.fetchError(this.options.targetFragment.getTarget(), fetchUrl, error);
|
|
813
|
+
}
|
|
814
|
+
throw error;
|
|
761
815
|
});
|
|
762
|
-
return this.handleFetchResult(response);
|
|
763
816
|
}
|
|
817
|
+
return fetch(fetchUrl, finalOptions).then(response => {
|
|
818
|
+
return this.handleFetchResult(response, fetchUrl || undefined);
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
// fetchUrlが無い場合(changeイベント等)、bindFragmentsが無ければformFragmentにバインド
|
|
822
|
+
if ((!this.options.bindFragments ||
|
|
823
|
+
this.options.bindFragments.length === 0) &&
|
|
824
|
+
this.options.formFragment &&
|
|
825
|
+
hasPayload) {
|
|
826
|
+
// 双方向バインディング: フォーム値を自動的にバインディングデータに反映
|
|
827
|
+
const formFragment = this.options.formFragment;
|
|
828
|
+
const formElement = formFragment.getTarget();
|
|
829
|
+
formElement.setAttribute(`${Env.prefix}bind`, JSON.stringify(payload));
|
|
830
|
+
const bindingData = formFragment.getBindingData();
|
|
831
|
+
Object.assign(bindingData, payload);
|
|
832
|
+
await Core.setBindingData(formElement, bindingData);
|
|
833
|
+
return true;
|
|
834
|
+
}
|
|
835
|
+
const merged = hasPayload ? payload : {};
|
|
836
|
+
const response = new Response(JSON.stringify(merged), {
|
|
837
|
+
headers: { 'Content-Type': 'application/json' },
|
|
764
838
|
});
|
|
839
|
+
return this.handleFetchResult(response);
|
|
765
840
|
}
|
|
766
841
|
/**
|
|
767
842
|
* フェッチ後の処理を実行します。
|
|
768
843
|
*/
|
|
769
|
-
handleFetchResult(response, url, startedAt) {
|
|
844
|
+
async handleFetchResult(response, url, startedAt) {
|
|
770
845
|
const activeHaori = resolveProcedureHaoriApi();
|
|
771
846
|
// エラー応答時は以後の処理を停止し、メッセージを伝播
|
|
772
847
|
if (!response.ok) {
|
|
773
848
|
if (this.options.targetFragment && url) {
|
|
774
849
|
HaoriEvent.fetchError(this.options.targetFragment.getTarget(), url, new Error(`${response.status} ${response.statusText}`), response.status, startedAt);
|
|
775
850
|
}
|
|
776
|
-
|
|
851
|
+
await this.handleFetchError(response);
|
|
852
|
+
return false;
|
|
777
853
|
}
|
|
778
854
|
// fetchendイベントを発火
|
|
779
855
|
if (this.options.targetFragment && url && startedAt) {
|
|
@@ -783,7 +859,7 @@ ${body}
|
|
|
783
859
|
const result = this.options.afterCallback(response);
|
|
784
860
|
if (result !== undefined && result !== null) {
|
|
785
861
|
if (result === false || (typeof result === 'object' && result.stop)) {
|
|
786
|
-
return
|
|
862
|
+
return false;
|
|
787
863
|
}
|
|
788
864
|
if (typeof result === 'object' && 'response' in result) {
|
|
789
865
|
response = ('response' in result ? result.response : response);
|
|
@@ -842,30 +918,19 @@ ${body}
|
|
|
842
918
|
});
|
|
843
919
|
}
|
|
844
920
|
// 仕様順序: 先に各種操作(bind/adjust/row/reset/refetch/click/open/close)を完了
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
})
|
|
859
|
-
.then(() => {
|
|
860
|
-
this.pushHistory();
|
|
861
|
-
return Promise.resolve();
|
|
862
|
-
})
|
|
863
|
-
.then(() => {
|
|
864
|
-
if (this.options.redirectUrl) {
|
|
865
|
-
window.location.href = this.options.redirectUrl;
|
|
866
|
-
}
|
|
867
|
-
return Promise.resolve();
|
|
868
|
-
});
|
|
921
|
+
await Promise.all(promises);
|
|
922
|
+
// その後にダイアログ/トーストを表示
|
|
923
|
+
if (this.options.dialogMessage) {
|
|
924
|
+
await activeHaori.dialog(this.options.dialogMessage);
|
|
925
|
+
}
|
|
926
|
+
if (this.options.toastMessage) {
|
|
927
|
+
await activeHaori.toast(this.options.toastMessage, 'info');
|
|
928
|
+
}
|
|
929
|
+
this.pushHistory();
|
|
930
|
+
if (this.options.redirectUrl) {
|
|
931
|
+
window.location.href = this.options.redirectUrl;
|
|
932
|
+
}
|
|
933
|
+
return true;
|
|
869
934
|
}
|
|
870
935
|
/**
|
|
871
936
|
* history.pushState を実行します。
|
|
@@ -989,7 +1054,7 @@ ${body}
|
|
|
989
1054
|
if (entries.length === 0) {
|
|
990
1055
|
// 汎用メッセージ
|
|
991
1056
|
await addGeneralMessage(`${response.status} ${response.statusText}`);
|
|
992
|
-
return;
|
|
1057
|
+
return false;
|
|
993
1058
|
}
|
|
994
1059
|
// メッセージを反映
|
|
995
1060
|
for (const e of entries) {
|
|
@@ -1000,7 +1065,7 @@ ${body}
|
|
|
1000
1065
|
await addGeneralMessage(e.message);
|
|
1001
1066
|
}
|
|
1002
1067
|
}
|
|
1003
|
-
return;
|
|
1068
|
+
return false;
|
|
1004
1069
|
}
|
|
1005
1070
|
catch {
|
|
1006
1071
|
// JSON 解析失敗時はテキストにフォールバック
|
|
@@ -1019,6 +1084,7 @@ ${body}
|
|
|
1019
1084
|
catch {
|
|
1020
1085
|
await addGeneralMessage(`${response.status} ${response.statusText}`);
|
|
1021
1086
|
}
|
|
1087
|
+
return false;
|
|
1022
1088
|
}
|
|
1023
1089
|
/**
|
|
1024
1090
|
* 対象のフラグメント以下の入力要素に対してバリデーションを実行します。
|
|
@@ -1106,7 +1172,21 @@ ${body}
|
|
|
1106
1172
|
if (this.options.bindArg) {
|
|
1107
1173
|
this.options.bindFragments.forEach(fragment => {
|
|
1108
1174
|
const bindingData = fragment.getBindingData();
|
|
1109
|
-
|
|
1175
|
+
const bindArg = this.options.bindArg;
|
|
1176
|
+
if (data &&
|
|
1177
|
+
typeof data === 'object' &&
|
|
1178
|
+
!Array.isArray(data)) {
|
|
1179
|
+
const currentValue = bindingData[bindArg];
|
|
1180
|
+
const currentObject = currentValue &&
|
|
1181
|
+
typeof currentValue === 'object' &&
|
|
1182
|
+
!Array.isArray(currentValue)
|
|
1183
|
+
? currentValue
|
|
1184
|
+
: {};
|
|
1185
|
+
bindingData[bindArg] = this.mergeAppendBindingData(fragment, data, currentObject);
|
|
1186
|
+
}
|
|
1187
|
+
else {
|
|
1188
|
+
bindingData[bindArg] = data;
|
|
1189
|
+
}
|
|
1110
1190
|
promises.push(Core.setBindingData(fragment.getTarget(), bindingData));
|
|
1111
1191
|
});
|
|
1112
1192
|
}
|
|
@@ -1116,12 +1196,32 @@ ${body}
|
|
|
1116
1196
|
}
|
|
1117
1197
|
else {
|
|
1118
1198
|
this.options.bindFragments.forEach(fragment => {
|
|
1119
|
-
|
|
1199
|
+
const resolvedData = this.mergeAppendBindingData(fragment, data);
|
|
1200
|
+
promises.push(Core.setBindingData(fragment.getTarget(), resolvedData));
|
|
1120
1201
|
});
|
|
1121
1202
|
}
|
|
1122
1203
|
return Promise.all(promises).then(() => undefined);
|
|
1123
1204
|
});
|
|
1124
1205
|
}
|
|
1206
|
+
/**
|
|
1207
|
+
* bind-append 指定があるキーについて、既存配列と結合したデータを返します。
|
|
1208
|
+
*/
|
|
1209
|
+
mergeAppendBindingData(fragment, data, currentData = fragment.getBindingData()) {
|
|
1210
|
+
if (!this.options.bindAppendParams ||
|
|
1211
|
+
this.options.bindAppendParams.length === 0) {
|
|
1212
|
+
return data;
|
|
1213
|
+
}
|
|
1214
|
+
const merged = { ...data };
|
|
1215
|
+
const current = currentData;
|
|
1216
|
+
for (const key of this.options.bindAppendParams) {
|
|
1217
|
+
const incoming = merged[key];
|
|
1218
|
+
const existing = current[key];
|
|
1219
|
+
if (Array.isArray(existing) && Array.isArray(incoming)) {
|
|
1220
|
+
merged[key] = existing.concat(incoming);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
return merged;
|
|
1224
|
+
}
|
|
1125
1225
|
/**
|
|
1126
1226
|
* 値の増減を行います。
|
|
1127
1227
|
*/
|