Compare commits
892 Commits
3.0.0-beta
...
3.0.5
Author | SHA1 | Date | |
---|---|---|---|
6a956472fe | |||
0c4407f43a | |||
b6c864e7a1 | |||
3d9dc6465d | |||
9a6fec094a | |||
61af224d7d | |||
9b41641e97 | |||
4e2e9f6f8f | |||
c29dc8b4c7 | |||
98a3c6b156 | |||
69c540288b | |||
651c9f5692 | |||
9ad3778cf7 | |||
c90e0e9f64 | |||
cb1730c4e2 | |||
3dd8a973fd | |||
c3ea088fca | |||
a11d6d7868 | |||
a596add838 | |||
7e7103ddab | |||
0064970ed7 | |||
9d93f3ea95 | |||
ff2a3cd19e | |||
8419d95ea1 | |||
4ad317ac7b | |||
7cccebbf2c | |||
e4f76ee9eb | |||
7f52f72c25 | |||
44afcbbeaf | |||
e816c59539 | |||
c74421a42a | |||
23eb4633c4 | |||
26241afb86 | |||
92dbf966a1 | |||
db226b54a8 | |||
3af059f5c4 | |||
8706abcdf0 | |||
2129d041ac | |||
2a4a44ebb5 | |||
a4f2d5402c | |||
9f5fc151b4 | |||
8a91eb46e6 | |||
e4ab928e82 | |||
a1b02cb862 | |||
84b942b9d2 | |||
1ca99a6209 | |||
6b61abe8c0 | |||
27028ca1ef | |||
eed88926a2 | |||
b25877c514 | |||
119e574495 | |||
7308d253b2 | |||
1c19b71697 | |||
7551fff93f | |||
b2aa919574 | |||
1102bbe483 | |||
b78dd22ba9 | |||
73110ada46 | |||
74dedd06bc | |||
20c936d13b | |||
f135b89de9 | |||
6a83930ae0 | |||
a1d0acfac2 | |||
04be06c0cb | |||
78d52d6298 | |||
5ff7c28c43 | |||
5526f315d2 | |||
d5e25fdeb1 | |||
90a7bf5179 | |||
bf1f696870 | |||
95551ad049 | |||
1ad90680f4 | |||
d69d3cb421 | |||
9adca07393 | |||
a9d129fddc | |||
b4ac09bea3 | |||
b1a403d9b5 | |||
28504fb5e3 | |||
8ebb8e3c02 | |||
c95c2cd1ae | |||
946bee2194 | |||
1f9bd04308 | |||
33572b2dc7 | |||
680446b77e | |||
bf1d76a3a7 | |||
c915fcfdff | |||
02966c3b93 | |||
84dc48daec | |||
12225004f4 | |||
320dfa2ec5 | |||
b5f3016085 | |||
cd53e369d0 | |||
6fc11af774 | |||
42e3a97616 | |||
6b16aa1692 | |||
03d0de74e4 | |||
c3b643df84 | |||
697f9ba5bc | |||
28c75c5b96 | |||
6f255854f2 | |||
91c5f9c43e | |||
62acd6404a | |||
adc1461771 | |||
66cc0964ce | |||
10d77720ad | |||
475114c6f9 | |||
a0fec7d103 | |||
4d9d92a026 | |||
e51aa8c271 | |||
d44adedade | |||
9fb3c50aa7 | |||
907053a349 | |||
f0f85cfb59 | |||
44d0341fb2 | |||
0cdae52c66 | |||
0cd9c8e416 | |||
9e3010ab52 | |||
d831b2df55 | |||
16ff630e88 | |||
d35763662e | |||
10be411b12 | |||
6ecce192f7 | |||
ee07e60fe9 | |||
a35d7a1154 | |||
ebba8dbfd6 | |||
44c637c06b | |||
d54ba734bf | |||
b45fc22306 | |||
994935d4ae | |||
ceb5ce850c | |||
97b5ed945d | |||
873b322245 | |||
12ad9e41e7 | |||
96b418e455 | |||
8ea7861f77 | |||
821976c881 | |||
6f1443e43d | |||
09fcaecdfc | |||
efd72ca9f6 | |||
550b5e9aed | |||
4b7ae5fcff | |||
fa85e12127 | |||
1cce50902b | |||
2048fa5cf9 | |||
f438c8fd31 | |||
0bfa832dad | |||
483dfbe1ec | |||
561fee491d | |||
97d157192a | |||
6b14a8a057 | |||
d27b187f5e | |||
02d49ba2ca | |||
f3571a5855 | |||
3d5f0df213 | |||
595a201fe7 | |||
c3db7d8148 | |||
4a2187ff32 | |||
279c36a30d | |||
fd65117a5d | |||
9305beace3 | |||
ba86cbfb18 | |||
2ac7d1d4ab | |||
e9c55260c8 | |||
99b6a287f8 | |||
d645f0cad4 | |||
b0343254c0 | |||
cc368f015b | |||
4e508855fc | |||
72c0a6f165 | |||
f266508654 | |||
1893d82b2d | |||
bb1dd6a2ec | |||
7097ea5d9b | |||
b4092e0641 | |||
2c068c7bb6 | |||
a1a4fcb978 | |||
f77620d649 | |||
9e932c9078 | |||
de47dbe41b | |||
6d36d67a60 | |||
607bf51b37 | |||
f38ed0272c | |||
eef969439b | |||
272f552f3c | |||
dff9c1012b | |||
52470360a1 | |||
6c62459ed4 | |||
98482cebf9 | |||
509880119e | |||
195ccb5eed | |||
122af1c943 | |||
169940058b | |||
478e60d60e | |||
9fc0ac8c4f | |||
e86b780479 | |||
42f08bdc65 | |||
c2789cdac3 | |||
8a362f49f8 | |||
784a80d1a5 | |||
922d2b4b5f | |||
91e8461cac | |||
8757598a2d | |||
524aabea1d | |||
513181ff47 | |||
6bc288ed54 | |||
77f3a875dd | |||
6584250d1a | |||
e42db162aa | |||
2762096167 | |||
b2ec3e5f7b | |||
e012bd6cbe | |||
a02e64e805 | |||
e4bb3e1133 | |||
998795e0e0 | |||
ec44b84cc9 | |||
efece061d0 | |||
e347fc74a2 | |||
027418a86c | |||
864187aa02 | |||
59ae6619c0 | |||
6aa0be8d01 | |||
657658ea2b | |||
8647128e12 | |||
c8d92b3cd2 | |||
cc8b7b45ed | |||
5b8b8c8441 | |||
7106c640ef | |||
300b84983d | |||
49c1b92838 | |||
d900827850 | |||
1688d4dbe1 | |||
856c636089 | |||
8f9e8ea185 | |||
b0b88693f1 | |||
9916eb9da8 | |||
79b5426e01 | |||
5807fd2e02 | |||
0ee39143f4 | |||
10c39bd650 | |||
20593cc5a5 | |||
f438eee842 | |||
cb4b599d97 | |||
33733219f6 | |||
737a83cdf3 | |||
9061e1b495 | |||
09199e41a1 | |||
4e91932613 | |||
227de4ecfa | |||
c1ccacf851 | |||
53f7953566 | |||
61ae2da1e3 | |||
36abd8e5e6 | |||
7e9de1fd07 | |||
7ac5e65963 | |||
cf992852b5 | |||
59482b2bfa | |||
053f9e0cdf | |||
e1cc25239b | |||
2f4452ad36 | |||
f453d685d6 | |||
2d2b4ca7f0 | |||
546e012fbf | |||
83adf089c5 | |||
2f45bb05d7 | |||
9ef8ab3e28 | |||
f4db4f05c4 | |||
e7e08dbb3a | |||
02e2f0c07a | |||
c75b6bd7eb | |||
b74be8777a | |||
7463e0d1f1 | |||
a378880cf8 | |||
33fa496913 | |||
eff996e1f8 | |||
28b894b26b | |||
c914aedc0d | |||
1745b67d64 | |||
2d25974fdc | |||
51a02784e2 | |||
b9bdc86fd9 | |||
e580e6b92b | |||
96f77498f7 | |||
62ce7c0437 | |||
c1e542cb5a | |||
544bf0ddfd | |||
609f1b5e67 | |||
4616cf67bf | |||
cc5227ca0d | |||
26bccd95d4 | |||
af58814fe7 | |||
7d9b4b31aa | |||
8a8108b41d | |||
d92b1f57bd | |||
52ef7bece4 | |||
dc11046ad1 | |||
01f41b9798 | |||
9c0d9c31f8 | |||
df499095c4 | |||
05dca3d2ce | |||
ec35b90f3e | |||
1b7e3a997f | |||
c598537025 | |||
6f149e3ec4 | |||
d181bde0e9 | |||
2985705b14 | |||
6ce925fbe8 | |||
b8aceff61f | |||
cd091c2af6 | |||
510cacf2fd | |||
eac6b1b414 | |||
316fa91a10 | |||
7c23415d26 | |||
28320cdbb6 | |||
48f3ae4ea1 | |||
e47c8bc701 | |||
78429d8f91 | |||
80c4eeed5e | |||
5985d659f9 | |||
638de3cf55 | |||
967fa09f4f | |||
a15e1200b5 | |||
a15b46cbab | |||
dcb0b45c21 | |||
14810a22b5 | |||
1b756ef0b2 | |||
5553817f9a | |||
4b7fb3ae3d | |||
efa231b08f | |||
8d8dfaa11f | |||
5ba2c4bc3a | |||
788494ec47 | |||
5e7f9e3edf | |||
2e5554a3af | |||
51fbf29031 | |||
c3c6ce989c | |||
a241d0c7bc | |||
632bce7894 | |||
1151354278 | |||
c12752403f | |||
d3ff174e9f | |||
03df7e723c | |||
6c8fe8413a | |||
89b0b51980 | |||
fa1ab733f8 | |||
127022645e | |||
b1d26b8cee | |||
f07b90adde | |||
b3884d06a8 | |||
abf1d817f4 | |||
c7b7b0abad | |||
8540e5eea9 | |||
09ed3d4fa6 | |||
b96dc8b3f7 | |||
0a4dc3eb38 | |||
a78af28943 | |||
f035d12aaf | |||
6353075f1e | |||
6c91ca9d31 | |||
6f8634570c | |||
0efcfad3d1 | |||
5d7b54ab22 | |||
ad1f6e2a8e | |||
d844b7e47f | |||
36d4e3eb15 | |||
853f686dde | |||
d17486bac4 | |||
4226684c5a | |||
364dd1b2a3 | |||
eaf10e8a96 | |||
bac494ac0d | |||
acd2b9f51e | |||
27c6fa5ff4 | |||
89b51b6215 | |||
7725391eff | |||
a37117cfa3 | |||
856331caa4 | |||
9117ae1a27 | |||
4aae8d56e5 | |||
033d527db9 | |||
b2b1f7ff71 | |||
de261d6179 | |||
a587b0a966 | |||
441aa14bcb | |||
4b4b5dd556 | |||
df9ba7e6c8 | |||
ca4f1c9387 | |||
8c151d2d11 | |||
78fb9ba46f | |||
3a0669e1a2 | |||
c466e53681 | |||
d02aed870e | |||
fad7ff0018 | |||
84a3f98725 | |||
1c3e968ec4 | |||
c090a8260b | |||
65726de7de | |||
33fe302f0d | |||
2d702dd5d3 | |||
18f208cf47 | |||
f7b1016e63 | |||
223fedba72 | |||
bf7e7e414f | |||
618d0c0c9d | |||
49318791fc | |||
a5abdd28e1 | |||
70860a676c | |||
469e9fd8e1 | |||
715b48df8d | |||
27ae0a9f16 | |||
b92329a6b5 | |||
6fe5b7e0c5 | |||
7e0c500e4f | |||
eec35c8ab6 | |||
4096c4b31b | |||
40cbefd1f4 | |||
fb5d43e975 | |||
f35b66b3cf | |||
7900e7eb8d | |||
849a24ced7 | |||
f7e73b06be | |||
52cbb9fcb2 | |||
966ec0cb7a | |||
2ff0d40d10 | |||
bb249ebe09 | |||
5a57029b38 | |||
84d427cc4c | |||
22efd2109b | |||
f044db5745 | |||
0865995d21 | |||
7c30192a03 | |||
334c3ff420 | |||
6d738ddb3b | |||
440052cf2c | |||
ccb751b44a | |||
7cf3d0960d | |||
6e45856622 | |||
9859df98b7 | |||
1071688924 | |||
4f3b0234a4 | |||
3e2bbeac11 | |||
08f81bd816 | |||
f0c59ff635 | |||
66c6f52646 | |||
4988aaf14f | |||
5773d46f1c | |||
f3a1e1b447 | |||
6de746162e | |||
60b3c066a5 | |||
63158dc2d5 | |||
dede6e56c4 | |||
dd366dde18 | |||
2fedafe483 | |||
feae98ac3f | |||
6fb18ad321 | |||
6c843e35b0 | |||
cfdb86eb6e | |||
41fb93e453 | |||
19813b5dad | |||
321393f119 | |||
ab3f41302c | |||
9dff4539e6 | |||
6520d5eca3 | |||
f5b152cdfa | |||
1d2cab1249 | |||
90b93bd76e | |||
563ca3e605 | |||
08bbfcb5e8 | |||
5572a7f1e5 | |||
9dd326e7db | |||
cf00813c7f | |||
4c898b76b2 | |||
81a2ba8e03 | |||
7e6d900b53 | |||
1a522794d6 | |||
197537d6ca | |||
b212ca801b | |||
db8f3216d7 | |||
78f9945296 | |||
0cebcd3965 | |||
4062e0662d | |||
20b7e82d3c | |||
9ab8a80567 | |||
f98d02121b | |||
e68e212ad0 | |||
cb1a1e51ba | |||
b42d8e68d9 | |||
e88d130ebb | |||
c924778d50 | |||
a9051c6d09 | |||
f6243b5d79 | |||
68c0b93586 | |||
0e8be8040c | |||
f3ea548d65 | |||
1ab6be8acd | |||
c413acd93d | |||
7614a4d8dc | |||
4a94c29b85 | |||
b5b9531ff3 | |||
e2a048a65f | |||
c28726f118 | |||
7c74885669 | |||
5f74f34cba | |||
d6ef526a9d | |||
0da0507e0a | |||
3f03c985bf | |||
77f6e13aa3 | |||
213d0e8627 | |||
1d019bc11e | |||
ca12487416 | |||
c5d42a5033 | |||
3b079440b5 | |||
419697991f | |||
d119a1e5fa | |||
1bd4264dd5 | |||
21ee60b7d7 | |||
849ca27d1f | |||
a4dad46fb7 | |||
fac4c8fb41 | |||
cdc87c23ea | |||
34d09ce0c9 | |||
fe9ae392f2 | |||
9501640f4f | |||
ad028ab55d | |||
74cb8d9735 | |||
381608df22 | |||
6aca598dc3 | |||
acbe2e383a | |||
55d3b67a2a | |||
c02394b576 | |||
6a9b8d88c2 | |||
b24c51d800 | |||
d3a3d3b277 | |||
3dcff8eb8a | |||
401339244c | |||
653ecdc4d0 | |||
88d5952684 | |||
c4d6c19c67 | |||
0554a84f77 | |||
c01b57a383 | |||
65f4f493f1 | |||
8a7ea791b0 | |||
1823985172 | |||
e735b5f3e0 | |||
cd5c57b7e9 | |||
fbf59723d8 | |||
2e166a233b | |||
1608ba5893 | |||
9d7eb8038d | |||
c9c78a7160 | |||
53df10dc2b | |||
a746c124a3 | |||
ca5a5301a8 | |||
7a2aaa86cf | |||
b626c7ea3c | |||
695152e947 | |||
a36fe400ed | |||
22bb971db5 | |||
b88452c5a2 | |||
4a2f9ad1f9 | |||
593a7de9fe | |||
e5ecf870c7 | |||
11cc97b201 | |||
e352b5bfad | |||
334448c964 | |||
7d8d535cb3 | |||
68988edd7e | |||
f164e9bb95 | |||
fe83435d14 | |||
453b7683bc | |||
25625b1ce1 | |||
048d71164a | |||
24e682e92e | |||
23d90c9f73 | |||
a9f190661f | |||
c1e56e5fa1 | |||
e2bb2679e4 | |||
d49530da0f | |||
0bc28ef3c7 | |||
563b7eccb0 | |||
94a9b63136 | |||
91ff008485 | |||
cb8fa23c3f | |||
3499de05e8 | |||
77ed4d34e8 | |||
cfc5f5a88d | |||
2514d87a00 | |||
23c6750ccc | |||
bb2af5176b | |||
95b5206e8b | |||
394118f113 | |||
b691fb5a48 | |||
1ba2492929 | |||
d08243b0ce | |||
189656fdc8 | |||
0be872eea9 | |||
22c39c0092 | |||
1b54e356b2 | |||
9079d7d4db | |||
160b28a632 | |||
03ebb30ac2 | |||
56eac1ae86 | |||
89da4c9aae | |||
b510071857 | |||
4bbec4700d | |||
29a2af2555 | |||
94d57b5287 | |||
6b7bc8a731 | |||
3dd0f00b7f | |||
b58f6cc4e0 | |||
7c942147ec | |||
6fca2061e6 | |||
0acc41c887 | |||
20d1ac81e2 | |||
cdb7b99728 | |||
54cb838d71 | |||
0b9ea23f0b | |||
3e40f768b4 | |||
6e929dcf79 | |||
7be01f0e4e | |||
9cb08734a3 | |||
55fda047f6 | |||
4706c5fc25 | |||
3ba857d03f | |||
f39cbe6b55 | |||
6dc1bf4e95 | |||
5b65a8f0ac | |||
583adf86da | |||
3b4a1b686d | |||
5390dd8421 | |||
fe8452711f | |||
f7cb53de2c | |||
1ac6dd8ccb | |||
b1425198b6 | |||
00a45f3214 | |||
8e46451337 | |||
b0d0cc09c8 | |||
af559a6fac | |||
67acceb968 | |||
703ee7ff71 | |||
29491dfd3e | |||
a49a230983 | |||
b8d285a1d9 | |||
d5227a9f2c | |||
693117eb40 | |||
f3aab6095e | |||
d70587a550 | |||
82226f2c36 | |||
2774101380 | |||
ee0e3ff95e | |||
17584dde43 | |||
3ac416de56 | |||
1e4c00169f | |||
11bbf54aad | |||
688e78560e | |||
5ca577a718 | |||
605df7dff1 | |||
fb1a3a80ff | |||
d3db755489 | |||
a4282b6a3e | |||
bc17984030 | |||
b823991867 | |||
04e238634d | |||
eba482cc67 | |||
e6663f0f3e | |||
7fa94a67c9 | |||
b4be9e1d28 | |||
a7504136a2 | |||
43fa12ec08 | |||
6723a563ed | |||
15e3e93c99 | |||
cbce789ac8 | |||
97607993fb | |||
334b119bb3 | |||
ed0b2e6231 | |||
ad7ad05ec7 | |||
bfba6d459c | |||
344eecbf11 | |||
14dc022d2d | |||
eff75cce94 | |||
51084fc57d | |||
298270f86c | |||
fe6d5d1523 | |||
3cceb32ec1 | |||
c8251a3bcd | |||
58888505b5 | |||
558c493dd7 | |||
8cdb7d77f6 | |||
539e6d3ee1 | |||
c9a0939ddd | |||
2add301b9f | |||
2d217e416a | |||
77e0ace951 | |||
933749f8f0 | |||
7b13babf3f | |||
8c673f78d7 | |||
2285c08c01 | |||
836b7179e9 | |||
f89a728c38 | |||
58f2c32362 | |||
99c4fc71c4 | |||
ce456af962 | |||
faa035a7c3 | |||
9066be6544 | |||
81c3e2facf | |||
3fafe2bd6a | |||
2f6fe8c804 | |||
ec3e88e658 | |||
b346a8846a | |||
6ff3895940 | |||
0e5401dd68 | |||
ea059ac0e4 | |||
f874ae6ca8 | |||
5bd6c6533a | |||
f539860922 | |||
536267c8f5 | |||
5b905a60e8 | |||
5e152ebaa1 | |||
2c35c7061e | |||
2515dcf4ce | |||
9458bf7418 | |||
44bf4b98b8 | |||
ff46784adf | |||
088d4f800d | |||
bb73e257db | |||
53fd3fe07c | |||
27a734d005 | |||
fb059f585e | |||
c99f990dbf | |||
5600d8087f | |||
cac3c30833 | |||
454a00c3cb | |||
156d05b4cf | |||
be63aa727d | |||
6c270ab095 | |||
bb7c9d2667 | |||
570b9dfb8f | |||
5f9baed195 | |||
45e6e0e9e6 | |||
08395d4365 | |||
167fb86927 | |||
64dbf158a4 | |||
89e57479cc | |||
fccd7f4054 | |||
1d6cdfe2cc | |||
812c6634ba | |||
419871a6bf | |||
823cd3cd07 | |||
24fa8c1d79 | |||
868f860e26 | |||
cbcd9fb22f | |||
eeb2fcb2e3 | |||
dde0b16cca | |||
01eaf1ef2d | |||
43c2908a2b | |||
6f64b9faa2 | |||
8d496e7aa3 | |||
25485a366a | |||
8aabc783e3 | |||
f72ceb2dbe | |||
af3d971154 | |||
2d3aae9dd0 | |||
11af014d9d | |||
6653327f7f | |||
3a2adde6c5 | |||
1aba3112e9 | |||
181d7b9876 | |||
ebe3f0bd00 | |||
1b69fe50ff | |||
fa3ba609f3 | |||
fe9aa03de1 | |||
cdd8e51ef9 | |||
1da06bd46a | |||
32b24a83ab | |||
5293589eea | |||
07f4c0bc1e | |||
7d9a072545 | |||
67ffcb5485 | |||
02098a3cf0 | |||
eab8065154 | |||
fb8ecef1c3 | |||
14955e5022 | |||
e83c0ff0bd | |||
09db1aac22 | |||
5fa1eb643a | |||
898913a517 | |||
3a4b364bcd | |||
d373d10f6c | |||
c07e1eff3c | |||
f5e985baa4 | |||
2c93a105cc | |||
cbf0a7684f | |||
2632feba31 | |||
c96bc755c7 | |||
bbad772d7a | |||
520a3c43be | |||
eb70df1466 | |||
5977b8b4bc | |||
2bb7d95e37 | |||
c64959dce0 | |||
ead0792b32 | |||
7bd52d456c | |||
8517896660 | |||
c78933f7c4 | |||
707d5efec1 | |||
acfb3aefba | |||
cb6f4046a2 | |||
667aa91581 | |||
b7f7dc6728 | |||
ad1e8feb23 | |||
486b382c88 | |||
472be3b071 | |||
37a9fd9e1b | |||
c984ac7a66 | |||
a1ea56f505 | |||
dd7f959731 | |||
09f4f2e78a | |||
502250a1a3 | |||
48e37f6797 | |||
420058a86d | |||
6c777ca074 | |||
a481debb77 | |||
c91f8ccc7a | |||
6a2b5e28c2 | |||
bc51b6efc8 | |||
152edda03f | |||
5029b73027 | |||
35c25d3337 | |||
ae25e95d51 | |||
5b8d0c63a1 | |||
94fb8c6096 | |||
93ef1d0197 | |||
501d00b0cc | |||
9af3dc1f9d | |||
3705ed7da0 | |||
5b99e66d8d | |||
cbedd5ff40 | |||
bfcd6f10fc | |||
79362e9955 | |||
fcf272b44a | |||
b80683a9a1 | |||
5349f3a59a | |||
95072a9ac5 | |||
8c372b0909 | |||
580dd38b3a | |||
47d4e98aae | |||
7ebb7bac17 | |||
6cbce2fc97 | |||
e8a950f32c | |||
4f722ecd8a | |||
478359f9ff | |||
a1720a5cf1 | |||
3f0ef3ded7 | |||
dcb25c1a6b | |||
c5dd575324 | |||
6eca26a4e2 | |||
e10fa065bd | |||
49673fabbd | |||
1c1a210542 | |||
30277d92cd | |||
fb940065ea | |||
afa06342a5 | |||
03d2ff5f26 | |||
ec71dff40d | |||
58faf64a5c | |||
65ff14a81d | |||
f7efe44f09 | |||
cf22e81ae1 | |||
7aa0f21d11 | |||
2e31e3d37c | |||
3e988b7a56 | |||
ce3eb06924 | |||
a37ff8d398 | |||
d0bdb1a47b | |||
855f2a55d4 | |||
fc7ec9bded | |||
028de860a2 | |||
5af91d028d | |||
a4bcf870bb | |||
e06f2f5f0b | |||
c101645d93 | |||
b8904c2d51 | |||
099db4e1c8 | |||
cdf36ccb20 | |||
79b6ab1d15 | |||
95114774da | |||
7f566fb672 | |||
d27968a215 | |||
344990d59e | |||
ea831ef160 | |||
8314b05fce | |||
fd33cc7068 | |||
92e4cc6a24 |
@ -1,8 +0,0 @@
|
|||||||
#!/usr/bin/ruby
|
|
||||||
path = "/tmp"
|
|
||||||
Dir.mkdir(path) if !File.exists?(path)
|
|
||||||
File.open("#{path}/mailpoet-#{Time.now.to_f}.txt", "w") do |f|
|
|
||||||
sleep 5
|
|
||||||
f.puts ARGV.inspect
|
|
||||||
$stdin.each_line { |line| f.puts line }
|
|
||||||
end
|
|
@ -1,3 +0,0 @@
|
|||||||
; For Unix only. You may supply arguments as well (default: "sendmail -t -i").
|
|
||||||
; http://php.net/sendmail-path
|
|
||||||
sendmail_path = /home/ubuntu/mailpoet/.circle_ci/fake-sendmail.rb
|
|
@ -3,11 +3,11 @@ Listen 8080
|
|||||||
<VirtualHost *:8080>
|
<VirtualHost *:8080>
|
||||||
UseCanonicalName Off
|
UseCanonicalName Off
|
||||||
ServerName mailpoet.loc
|
ServerName mailpoet.loc
|
||||||
DocumentRoot /home/ubuntu/mailpoet/wordpress
|
DocumentRoot /home/circleci/mailpoet/wordpress
|
||||||
DirectoryIndex index.php
|
DirectoryIndex index.php
|
||||||
LogLevel notice
|
LogLevel notice
|
||||||
|
|
||||||
<Directory /home/ubuntu/mailpoet/wordpress>
|
<Directory /home/circleci/mailpoet/wordpress>
|
||||||
Require all granted
|
Require all granted
|
||||||
</Directory>
|
</Directory>
|
||||||
</VirtualHost>
|
</VirtualHost>
|
137
.circleci/config.yml
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
version: 2
|
||||||
|
jobs:
|
||||||
|
qa_js_php5:
|
||||||
|
working_directory: /home/circleci/mailpoet
|
||||||
|
docker:
|
||||||
|
- image: circleci/php:5.6.30-apache-browsers
|
||||||
|
- image: circleci/mysql:5.7
|
||||||
|
environment:
|
||||||
|
TZ: /usr/share/zoneinfo/Etc/UTC
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: "Set up virtual host"
|
||||||
|
command: echo 127.0.0.1 mailpoet.loc | sudo tee -a /etc/hosts
|
||||||
|
- restore_cache:
|
||||||
|
key: composer-{{ checksum "composer.json" }}-{{ checksum "composer.lock" }}
|
||||||
|
- restore_cache:
|
||||||
|
key: npm-{{ checksum "package.json" }}
|
||||||
|
- run:
|
||||||
|
name: "Set up test environment"
|
||||||
|
command: source ./.circleci/setup.bash && setup php5
|
||||||
|
- save_cache:
|
||||||
|
key: composer-{{ checksum "composer.json" }}-{{ checksum "composer.lock" }}
|
||||||
|
paths:
|
||||||
|
- vendor
|
||||||
|
- save_cache:
|
||||||
|
key: npm-{{ checksum "package.json" }}
|
||||||
|
paths:
|
||||||
|
- node_modules
|
||||||
|
- run:
|
||||||
|
name: "QA Scripts"
|
||||||
|
command: ./do qa
|
||||||
|
- run:
|
||||||
|
name: "Preparing test results folder"
|
||||||
|
command: mkdir test-results
|
||||||
|
- run:
|
||||||
|
name: "JS tests"
|
||||||
|
command: |
|
||||||
|
mkdir test-results/mocha
|
||||||
|
./do t:j test-results/mocha/junit.xml
|
||||||
|
- run:
|
||||||
|
name: "PHP Unit tests"
|
||||||
|
command: |
|
||||||
|
WP_TEST_PATH="/home/circleci/mailpoet/wordpress" ./do t:u --xml
|
||||||
|
- store_test_results:
|
||||||
|
path: test-results/mocha
|
||||||
|
- store_artifacts:
|
||||||
|
path: test-results/mocha
|
||||||
|
destination: mocha
|
||||||
|
- store_test_results:
|
||||||
|
path: tests/_output
|
||||||
|
- store_artifacts:
|
||||||
|
path: tests/_output
|
||||||
|
destination: codeception
|
||||||
|
- store_artifacts:
|
||||||
|
path: /tmp/fake-mailer/
|
||||||
|
destination: fake-mailer
|
||||||
|
acceptance_tests:
|
||||||
|
working_directory: /home/circleci/mailpoet
|
||||||
|
machine: true
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: "Set up virtual host"
|
||||||
|
command: echo 127.0.0.1 mailpoet.loc | sudo tee -a /etc/hosts
|
||||||
|
- restore_cache:
|
||||||
|
key: composer-{{ checksum "composer.json" }}-{{ checksum "composer.lock" }}
|
||||||
|
- restore_cache:
|
||||||
|
key: npm-{{ checksum "package.json" }}
|
||||||
|
- run:
|
||||||
|
name: "Set up test environment"
|
||||||
|
command: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install circleci-php-5.6.23
|
||||||
|
sudo rm /usr/bin/php
|
||||||
|
sudo ln -s /opt/circleci/php/5.6.23/bin/php /usr/bin/php
|
||||||
|
# Install NodeJS+NPM
|
||||||
|
curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
|
||||||
|
sudo apt-get install nodejs build-essential
|
||||||
|
# install plugin dependencies
|
||||||
|
curl -sS https://getcomposer.org/installer | php
|
||||||
|
php composer.phar install
|
||||||
|
./do install
|
||||||
|
- save_cache:
|
||||||
|
key: composer-{{ checksum "composer.json" }}-{{ checksum "composer.lock" }}
|
||||||
|
paths:
|
||||||
|
- vendor
|
||||||
|
- save_cache:
|
||||||
|
key: npm-{{ checksum "package.json" }}
|
||||||
|
paths:
|
||||||
|
- node_modules
|
||||||
|
- run:
|
||||||
|
name: Run acceptance tests
|
||||||
|
command: |
|
||||||
|
docker-compose run codeception --steps --debug -vvv --html --xml
|
||||||
|
- store_artifacts:
|
||||||
|
path: ~/mailpoet/tests/acceptance-tests/_output
|
||||||
|
- store_test_results:
|
||||||
|
path: ~/mailpoet/tests/acceptance-tests/_output
|
||||||
|
php7:
|
||||||
|
working_directory: /home/circleci/mailpoet
|
||||||
|
docker:
|
||||||
|
- image: circleci/php:7.1-apache-browsers
|
||||||
|
- image: circleci/mysql:5.7
|
||||||
|
environment:
|
||||||
|
TZ: /usr/share/zoneinfo/Etc/UTC
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: "Set up virtual host"
|
||||||
|
command: echo 127.0.0.1 mailpoet.loc | sudo tee -a /etc/hosts
|
||||||
|
- restore_cache:
|
||||||
|
key: composer-{{ checksum "composer.json" }}-{{ checksum "composer.lock" }}
|
||||||
|
- restore_cache:
|
||||||
|
key: npm-{{ checksum "package.json" }}
|
||||||
|
- run:
|
||||||
|
name: "Set up test environment"
|
||||||
|
command: source ./.circleci/setup.bash && setup php7
|
||||||
|
- run:
|
||||||
|
name: "PHP Unit tests"
|
||||||
|
command: |
|
||||||
|
WP_TEST_PATH="/home/circleci/mailpoet/wordpress" ./do t:u --xml
|
||||||
|
- store_test_results:
|
||||||
|
path: tests/_output
|
||||||
|
- store_artifacts:
|
||||||
|
path: tests/_output
|
||||||
|
destination: codeception
|
||||||
|
- store_artifacts:
|
||||||
|
path: /tmp/fake-mailer/
|
||||||
|
destination: fake-mailer
|
||||||
|
workflows:
|
||||||
|
version: 2
|
||||||
|
build_and_test:
|
||||||
|
jobs:
|
||||||
|
- qa_js_php5
|
||||||
|
- php7
|
||||||
|
- acceptance_tests
|
17
.circleci/fake-sendmail.php
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/local/bin/php
|
||||||
|
<?php
|
||||||
|
$path = "/tmp/fake-mailer";
|
||||||
|
if(!file_exists($path)) {
|
||||||
|
mkdir($path);
|
||||||
|
}
|
||||||
|
$filename = $path . '/mailpoet-' . microtime(true) . '.txt';
|
||||||
|
$file_handle = fopen($filename, "w");
|
||||||
|
|
||||||
|
$call_arguments = print_r($argv, true) . "\n";
|
||||||
|
fwrite($file_handle, $call_arguments);
|
||||||
|
|
||||||
|
while($line = fgets(STDIN)) {
|
||||||
|
fwrite($file_handle, $line);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($file_handle);
|
9
.circleci/mailpoet_php.ini
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
; For Unix only. You may supply arguments as well (default: "sendmail -t -i").
|
||||||
|
; http://php.net/sendmail-path
|
||||||
|
sendmail_path = /usr/local/bin/fake-sendmail.php
|
||||||
|
|
||||||
|
[Date]
|
||||||
|
; Defines the default timezone used by the date functions
|
||||||
|
; http://php.net/date.timezone
|
||||||
|
date.timezone = UTC
|
||||||
|
|
48
.circleci/setup.bash
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
function setup {
|
||||||
|
local version=$1
|
||||||
|
# install PHP dependencies for WordPress
|
||||||
|
if [[ $version == "php7" ]]; then
|
||||||
|
echo "deb http://packages.dotdeb.org jessie all" | sudo tee -a /etc/apt/sources.list.d/dotdeb.list
|
||||||
|
echo "deb-src http://packages.dotdeb.org jessie all" | sudo tee -a /etc/apt/sources.list.d/dotdeb.list
|
||||||
|
wget -qO - http://www.dotdeb.org/dotdeb.gpg | sudo apt-key add -
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install mysql-client php7.0-mysql zlib1g-dev
|
||||||
|
sudo docker-php-ext-install mysqli pdo pdo_mysql zip
|
||||||
|
else
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install mysql-client php5-mysql zlib1g-dev
|
||||||
|
sudo docker-php-ext-install mysql mysqli pdo pdo_mysql zip
|
||||||
|
fi
|
||||||
|
# Add a fake sendmail mailer
|
||||||
|
sudo cp ./.circleci/fake-sendmail.php /usr/local/bin/
|
||||||
|
# configure Apache
|
||||||
|
sudo cp ./.circleci/mailpoet_php.ini /usr/local/etc/php/conf.d/
|
||||||
|
sudo cp ./.circleci/apache/mailpoet.loc.conf /etc/apache2/sites-available
|
||||||
|
sudo a2ensite mailpoet.loc
|
||||||
|
sudo a2enmod rewrite
|
||||||
|
sudo service apache2 restart
|
||||||
|
# Install NodeJS+NPM
|
||||||
|
curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
|
||||||
|
sudo apt-get install nodejs build-essential
|
||||||
|
# install plugin dependencies
|
||||||
|
curl -sS https://getcomposer.org/installer | php
|
||||||
|
./composer.phar install
|
||||||
|
./do install
|
||||||
|
# Set up Wordpress
|
||||||
|
mysql -h 127.0.0.1 -u root -e "create database wordpress"
|
||||||
|
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
|
||||||
|
chmod +x wp-cli.phar
|
||||||
|
./wp-cli.phar core download --allow-root --path=wordpress
|
||||||
|
# Generate `wp-config.php` file with debugging enabled
|
||||||
|
echo "define(\"WP_DEBUG\", true);" | ./wp-cli.phar core config --allow-root --dbname=wordpress --dbuser=root --dbhost=127.0.0.1 --path=wordpress --extra-php
|
||||||
|
# Install WordPress
|
||||||
|
./wp-cli.phar core install --allow-root --admin_name=admin --admin_password=admin --admin_email=admin@mailpoet.loc --url=http://mailpoet.loc:8080 --title=WordPress --path=wordpress
|
||||||
|
# Softlink plugin to plugin path
|
||||||
|
ln -s ../../.. wordpress/wp-content/plugins/mailpoet
|
||||||
|
./wp-cli.phar plugin activate mailpoet --path=wordpress
|
||||||
|
# Create .env file with correct path to WP installation
|
||||||
|
# TODO: Remove this line after PR gets merged and CircleCI env variables change
|
||||||
|
echo "WP_TEST_PATH=\"/home/circleci/mailpoet/wordpress\"" > .env
|
||||||
|
}
|
3
.eslintignore
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
**/vendor/**
|
||||||
|
**/testBundles/**
|
||||||
|
assets/js/src/newsletter_editor/tinymce/wplink/plugin.js
|
57
.eslintrc.es5.json
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
"extends": "airbnb/legacy",
|
||||||
|
"env": {
|
||||||
|
"amd": true,
|
||||||
|
"browser": true
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 5
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"import/no-amd": 0,
|
||||||
|
"prefer-arrow-callback": 0,
|
||||||
|
"radix": 0,
|
||||||
|
"no-alert": 0,
|
||||||
|
"guard-for-in": 0,
|
||||||
|
"no-prototype-builtins": 0,
|
||||||
|
"no-restricted-syntax": 0,
|
||||||
|
"no-useless-concat": 0,
|
||||||
|
"no-nested-ternary": 0,
|
||||||
|
"no-sequences": 0,
|
||||||
|
"no-useless-return": 0,
|
||||||
|
"array-callback-return": 0,
|
||||||
|
"new-cap": 0,
|
||||||
|
"no-continue": 0,
|
||||||
|
"no-new": 0,
|
||||||
|
"no-redeclare": 0,
|
||||||
|
"no-console": 0,
|
||||||
|
"no-empty": 0,
|
||||||
|
"no-useless-escape": 0,
|
||||||
|
"wrap-iife": 0,
|
||||||
|
"no-plusplus": 0,
|
||||||
|
"default-case": 0,
|
||||||
|
"no-lonely-if": 0,
|
||||||
|
"no-mixed-operators": 0,
|
||||||
|
"eqeqeq": 0,
|
||||||
|
"max-len": 0,
|
||||||
|
"global-require": 0,
|
||||||
|
"no-throw-literal": 0,
|
||||||
|
"no-extra-bind": 0,
|
||||||
|
"consistent-return": 0,
|
||||||
|
"no-shadow": 0,
|
||||||
|
"no-underscore-dangle": 0,
|
||||||
|
"brace-style": 0,
|
||||||
|
"no-else-return": 0,
|
||||||
|
"no-use-before-define": 0,
|
||||||
|
"camelcase": 0,
|
||||||
|
"padded-blocks": 0,
|
||||||
|
"strict": 0,
|
||||||
|
"space-infix-ops": 0,
|
||||||
|
"object-shorthand": 0,
|
||||||
|
"new-parens": 0,
|
||||||
|
"eol-last": 0,
|
||||||
|
"dot-notation": 0,
|
||||||
|
"prefer-template": 0,
|
||||||
|
"func-names": 0
|
||||||
|
}
|
||||||
|
}
|
70
.eslintrc.es6.json
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"extends": "airbnb",
|
||||||
|
"env": {
|
||||||
|
"amd": true,
|
||||||
|
"browser": true
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 6,
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"import/resolver": "webpack"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"comma-dangle": ["error", "always-multiline"],
|
||||||
|
"import/no-amd": 0,
|
||||||
|
"react/no-multi-comp": 0,
|
||||||
|
"react/sort-comp": 0,
|
||||||
|
"react/jsx-max-props-per-line": 0,
|
||||||
|
"react/prop-types": 0,
|
||||||
|
"react/jsx-first-prop-new-line": 0,
|
||||||
|
"react/no-is-mounted": 0,
|
||||||
|
"react/jsx-no-target-blank": 0,
|
||||||
|
"react/no-render-return-value": 0,
|
||||||
|
"react/jsx-boolean-value": 0,
|
||||||
|
"react/jsx-no-bind": 0,
|
||||||
|
"react/no-array-index-key": 0,
|
||||||
|
"react/self-closing-comp": 0,
|
||||||
|
"react/jsx-closing-bracket-location": 0,
|
||||||
|
"react/no-string-refs": 0,
|
||||||
|
"react/no-did-mount-set-state": 0,
|
||||||
|
"react/prefer-stateless-function": 0,
|
||||||
|
"jsx-a11y/label-has-for": 0,
|
||||||
|
"jsx-a11y/no-static-element-interactions": 0,
|
||||||
|
"jsx-a11y/alt-text": 0,
|
||||||
|
"func-names": 0,
|
||||||
|
"object-shorthand": 0,
|
||||||
|
"no-bitwise": 0,
|
||||||
|
"arrow-body-style": 0,
|
||||||
|
"prefer-template": 0,
|
||||||
|
"default-case": 0,
|
||||||
|
"array-callback-return": 0,
|
||||||
|
"consistent-return": 0,
|
||||||
|
"import/extensions": 0,
|
||||||
|
"import/no-extraneous-dependencies": 0,
|
||||||
|
"camelcase": 0,
|
||||||
|
"eqeqeq": 0,
|
||||||
|
"no-lonely-if": 0,
|
||||||
|
"space-unary-ops": 0,
|
||||||
|
"no-extra-bind": 0,
|
||||||
|
"class-methods-use-this": 0,
|
||||||
|
"no-case-declarations": 0,
|
||||||
|
"no-else-return": 0,
|
||||||
|
"max-len": 0,
|
||||||
|
"no-useless-concat": 0,
|
||||||
|
"no-sequences": 0,
|
||||||
|
"no-extra-boolean-cast": 0,
|
||||||
|
"dot-notation": 0,
|
||||||
|
"no-shadow": 0,
|
||||||
|
"no-alert": 0,
|
||||||
|
"no-script-url": 0,
|
||||||
|
"wrap-iife": 0,
|
||||||
|
"space-infix-ops": 0,
|
||||||
|
"no-irregular-whitespace": 0,
|
||||||
|
"padded-blocks": 0,
|
||||||
|
"no-underscore-dangle": 0
|
||||||
|
}
|
||||||
|
}
|
28
.eslintrc.tests.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"extends": "airbnb/legacy",
|
||||||
|
"env": {
|
||||||
|
"amd": true,
|
||||||
|
"mocha": true
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 6
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"import/no-amd": 0,
|
||||||
|
"no-whitespace-before-property": 0,
|
||||||
|
"global-require": 0,
|
||||||
|
"keyword-spacing": 0,
|
||||||
|
"no-bitwise": 0,
|
||||||
|
"no-spaced-func": 0,
|
||||||
|
"func-call-spacing": 0,
|
||||||
|
"max-len": 0,
|
||||||
|
"space-unary-ops": 0,
|
||||||
|
"no-underscore-dangle": 0,
|
||||||
|
"no-shadow": 0,
|
||||||
|
"padded-blocks": 0,
|
||||||
|
"space-before-blocks": 0,
|
||||||
|
"object-curly-spacing": 0,
|
||||||
|
"func-names": 0,
|
||||||
|
"space-before-function-paren": 0
|
||||||
|
}
|
||||||
|
}
|
44
.gitignore
vendored
@ -1,21 +1,23 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
TODO
|
TODO
|
||||||
composer.phar
|
composer.phar
|
||||||
/vendor
|
/vendor
|
||||||
tests/_output/*
|
tests/_output/*
|
||||||
tests/acceptance.suite.yml
|
tests/_support/_generated/*
|
||||||
tests/_support/_generated/*
|
node_modules
|
||||||
node_modules
|
.env
|
||||||
.env
|
npm-debug.log
|
||||||
npm-debug.log
|
!tasks/**
|
||||||
!tasks/**
|
/views/cache/**
|
||||||
/views/cache/**
|
temp
|
||||||
temp
|
.idea
|
||||||
.idea
|
mailpoet.zip
|
||||||
mailpoet.zip
|
tests/javascript/testBundles
|
||||||
tests/javascript/testBundles
|
assets/css/*.css
|
||||||
assets/css/*.css
|
assets/css/*.json
|
||||||
assets/js/*.js
|
assets/js/*.js
|
||||||
.vagrant
|
assets/js/*.json
|
||||||
lang
|
.vagrant
|
||||||
.mp_svn
|
lang
|
||||||
|
.mp_svn
|
||||||
|
/nbproject/
|
39
Dockerfile
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
FROM php:5.6-cli
|
||||||
|
|
||||||
|
ENV COMPOSER_ALLOW_SUPERUSER=1
|
||||||
|
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get -y install \
|
||||||
|
git \
|
||||||
|
zlib1g-dev \
|
||||||
|
libssl-dev \
|
||||||
|
mysql-client \
|
||||||
|
sudo less \
|
||||||
|
--no-install-recommends && \
|
||||||
|
apt-get clean && \
|
||||||
|
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
|
||||||
|
docker-php-ext-install bcmath zip mysqli pdo pdo_mysql && \
|
||||||
|
echo "date.timezone = UTC" >> /usr/local/etc/php/php.ini && \
|
||||||
|
curl -sS https://getcomposer.org/installer | php -- \
|
||||||
|
--filename=composer \
|
||||||
|
--install-dir=/usr/local/bin && \
|
||||||
|
composer global require --optimize-autoloader "hirak/prestissimo" && \
|
||||||
|
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar && \
|
||||||
|
chmod +x wp-cli.phar && \
|
||||||
|
mv wp-cli.phar /usr/local/bin/wp
|
||||||
|
|
||||||
|
# Prepare application
|
||||||
|
WORKDIR /repo
|
||||||
|
|
||||||
|
# Install vendor
|
||||||
|
COPY ./composer.json /repo/composer.json
|
||||||
|
|
||||||
|
# Add source-code
|
||||||
|
COPY . /repo
|
||||||
|
|
||||||
|
WORKDIR /wp-core/wp-content/plugins/mailpoet
|
||||||
|
ENV WP_TEST_PATH=/wp-core
|
||||||
|
|
||||||
|
ADD docker-entrypoint.sh /
|
||||||
|
|
||||||
|
RUN ["chmod", "+x", "/docker-entrypoint.sh"]
|
25
README.md
@ -61,6 +61,11 @@ $ ./do test:debug
|
|||||||
$ ./do qa
|
$ ./do qa
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- Javascript linter:
|
||||||
|
```sh
|
||||||
|
$ ./do lint:javascript
|
||||||
|
```
|
||||||
|
|
||||||
# CSS
|
# CSS
|
||||||
- [Stylus](https://learnboost.github.io/stylus/)
|
- [Stylus](https://learnboost.github.io/stylus/)
|
||||||
- [Nib extension](http://tj.github.io/nib/)
|
- [Nib extension](http://tj.github.io/nib/)
|
||||||
@ -169,7 +174,27 @@ If the job goes fine, you'll get a message like this:
|
|||||||
```
|
```
|
||||||
Go to '.mp_svn' and run 'svn ci -m "Release 3.0.0-beta.9"' to publish the
|
Go to '.mp_svn' and run 'svn ci -m "Release 3.0.0-beta.9"' to publish the
|
||||||
release
|
release
|
||||||
|
|
||||||
|
Run 'svn copy ...' to tag the release
|
||||||
```
|
```
|
||||||
It's quite literal: you can review the changes to be pushed and if you're satisfied, run the suggested command to finish the release publishing process.
|
It's quite literal: you can review the changes to be pushed and if you're satisfied, run the suggested command to finish the release publishing process.
|
||||||
|
|
||||||
If you're confident, execute `./do publish --force` and your release will be published to the remote SVN repository without manual intervention (automatically). For easier authentication you might want to set `WP_SVN_USERNAME` and `WP_SVN_PASSWORD` environment variables.
|
If you're confident, execute `./do publish --force` and your release will be published to the remote SVN repository without manual intervention (automatically). For easier authentication you might want to set `WP_SVN_USERNAME` and `WP_SVN_PASSWORD` environment variables.
|
||||||
|
|
||||||
|
# Acceptance testing
|
||||||
|
|
||||||
|
We are using Gravity Flow plugin's setup as an example for our acceptance test suite: https://www.stevenhenty.com/learn-acceptance-testing-deeply/
|
||||||
|
|
||||||
|
From the article above:
|
||||||
|
|
||||||
|
_Windows users only: enable hard drive sharing in the Docker settings._
|
||||||
|
|
||||||
|
The browser runs in a docker container. You can use a VNC client to watch the test run, follow instructions in official
|
||||||
|
repo: https://github.com/SeleniumHQ/docker-selenium
|
||||||
|
If you’re on a Mac, you can open vnc://localhost:5900 in Safari to watch the tests running in Chrome. If you’re on Windows, you’ll need a VNC client. Password: secret.
|
||||||
|
|
||||||
|
|
||||||
|
To run tests:
|
||||||
|
```sh
|
||||||
|
$ ./do test:acceptance
|
||||||
|
```
|
137
RoboFile.php
@ -1,7 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
class RoboFile extends \Robo\Tasks {
|
class RoboFile extends \Robo\Tasks {
|
||||||
|
|
||||||
function install() {
|
function install() {
|
||||||
return $this->taskExecStack()
|
return $this->taskExecStack()
|
||||||
->stopOnFail()
|
->stopOnFail()
|
||||||
@ -67,27 +66,38 @@ class RoboFile extends \Robo\Tasks {
|
|||||||
$this->_exec('./node_modules/webpack/bin/webpack.js --watch');
|
$this->_exec('./node_modules/webpack/bin/webpack.js --watch');
|
||||||
}
|
}
|
||||||
|
|
||||||
function compileAll() {
|
function compileAll($opts = ['env' => null]) {
|
||||||
$collection = $this->collectionBuilder();
|
$collection = $this->collectionBuilder();
|
||||||
$collection->addCode(array($this, 'compileJs'));
|
$collection->addCode(function() use ($opts) {
|
||||||
$collection->addCode(array($this, 'compileCss'));
|
return call_user_func(array($this, 'compileJs'), $opts);
|
||||||
|
});
|
||||||
|
$collection->addCode(function() use ($opts) {
|
||||||
|
return call_user_func(array($this, 'compileCss'), $opts);
|
||||||
|
});
|
||||||
return $collection->run();
|
return $collection->run();
|
||||||
}
|
}
|
||||||
|
|
||||||
function compileJs() {
|
function compileJs($opts = ['env' => null]) {
|
||||||
return $this->_exec('./node_modules/webpack/bin/webpack.js --bail');
|
$env = ($opts['env']) ?
|
||||||
|
sprintf('./node_modules/cross-env/dist/bin/cross-env.js NODE_ENV="%s"', $opts['env']) :
|
||||||
|
null;
|
||||||
|
return $this->_exec($env . ' ./node_modules/webpack/bin/webpack.js --bail');
|
||||||
}
|
}
|
||||||
|
|
||||||
function compileCss() {
|
function compileCss($opts = ['env' => null]) {
|
||||||
|
// Clean up folder from previous files
|
||||||
|
array_map('unlink', glob("assets/css/*.*"));
|
||||||
|
|
||||||
$css_files = array(
|
$css_files = array(
|
||||||
'assets/css/src/admin.styl',
|
'assets/css/src/admin.styl',
|
||||||
|
'assets/css/src/admin-global.styl',
|
||||||
'assets/css/src/newsletter_editor/newsletter_editor.styl',
|
'assets/css/src/newsletter_editor/newsletter_editor.styl',
|
||||||
'assets/css/src/public.styl',
|
'assets/css/src/public.styl',
|
||||||
'assets/css/src/rtl.styl',
|
'assets/css/src/rtl.styl',
|
||||||
'assets/css/src/importExport.styl'
|
'assets/css/src/importExport.styl'
|
||||||
);
|
);
|
||||||
|
|
||||||
return $this->_exec(join(' ', array(
|
$compilation_result = $this->_exec(join(' ', array(
|
||||||
'./node_modules/stylus/bin/stylus',
|
'./node_modules/stylus/bin/stylus',
|
||||||
'--include ./node_modules',
|
'--include ./node_modules',
|
||||||
'--include-css',
|
'--include-css',
|
||||||
@ -95,6 +105,25 @@ class RoboFile extends \Robo\Tasks {
|
|||||||
join(' ', $css_files),
|
join(' ', $css_files),
|
||||||
'-o assets/css/'
|
'-o assets/css/'
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
// Create manifest file
|
||||||
|
$manifest = [];
|
||||||
|
foreach(glob('assets/css/*.css') as $style) {
|
||||||
|
// Hash and rename styles if production environment
|
||||||
|
if($opts['env'] === 'production') {
|
||||||
|
$hashed_style = sprintf(
|
||||||
|
'%s.%s.css',
|
||||||
|
pathinfo($style)['filename'],
|
||||||
|
substr(md5_file($style), 0, 8)
|
||||||
|
);
|
||||||
|
$manifest[basename($style)] = $hashed_style;
|
||||||
|
rename($style, str_replace(basename($style), $hashed_style, $style));
|
||||||
|
} else {
|
||||||
|
$manifest[basename($style)] = basename($style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_put_contents('assets/css/manifest.json', json_encode($manifest, JSON_PRETTY_PRINT));
|
||||||
|
return $compilation_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makepot() {
|
function makepot() {
|
||||||
@ -126,9 +155,8 @@ class RoboFile extends \Robo\Tasks {
|
|||||||
|
|
||||||
function testUnit($opts=['file' => null, 'xml' => false]) {
|
function testUnit($opts=['file' => null, 'xml' => false]) {
|
||||||
$this->loadEnv();
|
$this->loadEnv();
|
||||||
$this->_exec('vendor/bin/codecept build');
|
|
||||||
|
|
||||||
$command = 'vendor/bin/codecept run unit -f '.(($opts['file']) ? $opts['file'] : '');
|
$command = 'vendor/bin/codecept run unit -c codeception.unit.yml -f '.(($opts['file']) ? $opts['file'] : '');
|
||||||
|
|
||||||
if($opts['xml']) {
|
if($opts['xml']) {
|
||||||
$command .= ' --xml';
|
$command .= ' --xml';
|
||||||
@ -138,9 +166,8 @@ class RoboFile extends \Robo\Tasks {
|
|||||||
|
|
||||||
function testCoverage($opts=['file' => null, 'xml' => false]) {
|
function testCoverage($opts=['file' => null, 'xml' => false]) {
|
||||||
$this->loadEnv();
|
$this->loadEnv();
|
||||||
$this->_exec('vendor/bin/codecept build');
|
|
||||||
$command = join(' ', array(
|
$command = join(' ', array(
|
||||||
'vendor/bin/codecept run',
|
'vendor/bin/codecept run unit -c codeception.unit.yml ',
|
||||||
(($opts['file']) ? $opts['file'] : ''),
|
(($opts['file']) ? $opts['file'] : ''),
|
||||||
'--coverage',
|
'--coverage',
|
||||||
($opts['xml']) ? '--coverage-xml' : '--coverage-html'
|
($opts['xml']) ? '--coverage-xml' : '--coverage-html'
|
||||||
@ -171,16 +198,26 @@ class RoboFile extends \Robo\Tasks {
|
|||||||
return $this->_exec($command);
|
return $this->_exec($command);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testDebug() {
|
function testDebug($opts=['file' => null, 'xml' => false]) {
|
||||||
$this->_exec('vendor/bin/codecept build');
|
|
||||||
$this->loadEnv();
|
$this->loadEnv();
|
||||||
return $this->_exec('vendor/bin/codecept run unit --debug');
|
$this->_exec('vendor/bin/codecept build -c codeception.unit.yml');
|
||||||
|
|
||||||
|
$command = 'vendor/bin/codecept run unit -c codeception.unit.yml --debug -f '.(($opts['file']) ? $opts['file'] : '');
|
||||||
|
|
||||||
|
if($opts['xml']) {
|
||||||
|
$command .= ' --xml';
|
||||||
|
}
|
||||||
|
return $this->_exec($command);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testAcceptance() {
|
||||||
|
return $this->_exec('COMPOSE_HTTP_TIMEOUT=200 docker-compose run codeception --steps --debug -vvv');
|
||||||
}
|
}
|
||||||
|
|
||||||
function testFailed() {
|
function testFailed() {
|
||||||
$this->loadEnv();
|
$this->loadEnv();
|
||||||
$this->_exec('vendor/bin/codecept build');
|
$this->_exec('vendor/bin/codecept build -c codeception.unit.yml');
|
||||||
return $this->_exec('vendor/bin/codecept run -g failed');
|
return $this->_exec('vendor/bin/codecept run -c codeception.unit.yml -g failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
function qa() {
|
function qa() {
|
||||||
@ -189,6 +226,7 @@ class RoboFile extends \Robo\Tasks {
|
|||||||
$collection->addCode(function() {
|
$collection->addCode(function() {
|
||||||
return $this->qaCodeSniffer('all');
|
return $this->qaCodeSniffer('all');
|
||||||
});
|
});
|
||||||
|
$collection->addCode(array($this, 'qaLintJavascript'));
|
||||||
return $collection->run();
|
return $collection->run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,24 +234,58 @@ class RoboFile extends \Robo\Tasks {
|
|||||||
return $this->_exec('./tasks/php_lint.sh lib/ tests/ mailpoet.php');
|
return $this->_exec('./tasks/php_lint.sh lib/ tests/ mailpoet.php');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function qaLintJavascript() {
|
||||||
|
return $this->_exec('npm run lint');
|
||||||
|
}
|
||||||
|
|
||||||
function qaCodeSniffer($severity='errors') {
|
function qaCodeSniffer($severity='errors') {
|
||||||
if ($severity === 'all') {
|
if ($severity === 'all') {
|
||||||
$severityFlag = '-w';
|
$severityFlag = '-w';
|
||||||
} else {
|
} else {
|
||||||
$severityFlag = '-n';
|
$severityFlag = '-n';
|
||||||
}
|
}
|
||||||
return $this->_exec(
|
return $this->collectionBuilder()
|
||||||
'./vendor/bin/phpcs '.
|
->taskExec(
|
||||||
'--standard=./tasks/code_sniffer/MailPoet '.
|
'./vendor/bin/phpcs '.
|
||||||
'--ignore=./lib/Util/Sudzy/*,./lib/Util/CSS.php,./lib/Util/XLSXWriter.php,'.
|
'--standard=./tasks/code_sniffer/MailPoet '.
|
||||||
'./lib/Util/pQuery/*,./lib/Config/PopulatorData/Templates/* '.
|
'--runtime-set testVersion 5.3-7.0 '.
|
||||||
'lib/ '.
|
'--ignore=./lib/Util/Sudzy/*,./lib/Util/CSS.php,./lib/Util/XLSXWriter.php,'.
|
||||||
$severityFlag
|
'./lib/Util/pQuery/*,./lib/Config/PopulatorData/Templates/* '.
|
||||||
);
|
'lib/ '.
|
||||||
|
$severityFlag
|
||||||
|
)
|
||||||
|
->taskExec(
|
||||||
|
'./vendor/bin/phpcs '.
|
||||||
|
'--standard=./tasks/code_sniffer/MailPoet '.
|
||||||
|
'--runtime-set testVersion 5.4-7.0 '.
|
||||||
|
'--ignore=./tests/unit/_bootstrap.php '.
|
||||||
|
'tests/unit/ '.
|
||||||
|
$severityFlag
|
||||||
|
)
|
||||||
|
->run();
|
||||||
}
|
}
|
||||||
|
|
||||||
function svnCheckout() {
|
function svnCheckout() {
|
||||||
return $this->_exec('svn co https://plugins.svn.wordpress.org/mailpoet/ .mp_svn');
|
$svn_dir = ".mp_svn";
|
||||||
|
|
||||||
|
$collection = $this->collectionBuilder();
|
||||||
|
|
||||||
|
// Clean up the SVN dir for faster shallow checkout
|
||||||
|
if(file_exists($svn_dir)) {
|
||||||
|
$collection->taskExecStack()
|
||||||
|
->exec('rm -rf ' . $svn_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
$collection->taskFileSystemStack()
|
||||||
|
->mkdir($svn_dir);
|
||||||
|
|
||||||
|
return $collection->taskExecStack()
|
||||||
|
->stopOnFail()
|
||||||
|
->dir($svn_dir)
|
||||||
|
->exec('svn co https://plugins.svn.wordpress.org/mailpoet/ -N .')
|
||||||
|
->exec('svn up trunk')
|
||||||
|
->exec('svn up assets')
|
||||||
|
->run();
|
||||||
}
|
}
|
||||||
|
|
||||||
function svnPublish($opts = ['force' => false]) {
|
function svnPublish($opts = ['force' => false]) {
|
||||||
@ -223,8 +295,9 @@ class RoboFile extends \Robo\Tasks {
|
|||||||
$plugin_data = get_plugin_data('mailpoet.php', false, false);
|
$plugin_data = get_plugin_data('mailpoet.php', false, false);
|
||||||
$plugin_version = $plugin_data['Version'];
|
$plugin_version = $plugin_data['Version'];
|
||||||
$plugin_dist_name = sanitize_title_with_dashes($plugin_data['Name']);
|
$plugin_dist_name = sanitize_title_with_dashes($plugin_data['Name']);
|
||||||
|
$plugin_dist_name = explode('-', $plugin_dist_name);
|
||||||
|
$plugin_dist_name = $plugin_dist_name[0];
|
||||||
$plugin_dist_file = $plugin_dist_name . '.zip';
|
$plugin_dist_file = $plugin_dist_name . '.zip';
|
||||||
|
|
||||||
$this->say('Publishing version: ' . $plugin_version);
|
$this->say('Publishing version: ' . $plugin_version);
|
||||||
|
|
||||||
// Sanity checks
|
// Sanity checks
|
||||||
@ -293,15 +366,15 @@ class RoboFile extends \Robo\Tasks {
|
|||||||
// Remove files from SVN repo that have already been removed locally
|
// Remove files from SVN repo that have already been removed locally
|
||||||
->exec("svn st | grep ^! | awk '$awkCmd' | xargs $xargsFlag svn rm")
|
->exec("svn st | grep ^! | awk '$awkCmd' | xargs $xargsFlag svn rm")
|
||||||
// Recursively add files to SVN that haven't been added yet
|
// Recursively add files to SVN that haven't been added yet
|
||||||
->exec("svn add --force * --auto-props --parents --depth infinity -q")
|
->exec("svn add --force * --auto-props --parents --depth infinity -q");
|
||||||
// Tag the release
|
|
||||||
->exec("svn cp trunk tags/$plugin_version");
|
|
||||||
|
|
||||||
$result = $collection->run();
|
$result = $collection->run();
|
||||||
|
|
||||||
if($result->wasSuccessful()) {
|
if($result->wasSuccessful()) {
|
||||||
// Run or suggest release command depending on a flag
|
// Run or suggest release command depending on a flag
|
||||||
|
$repo_url = "https://plugins.svn.wordpress.org/$plugin_dist_name";
|
||||||
$release_cmd = "svn ci -m \"Release $plugin_version\"";
|
$release_cmd = "svn ci -m \"Release $plugin_version\"";
|
||||||
|
$tag_cmd = "svn copy $repo_url/trunk $repo_url/tags/$plugin_version -m \"Tag $plugin_version\"";
|
||||||
if(!empty($opts['force'])) {
|
if(!empty($opts['force'])) {
|
||||||
$svn_login = getenv('WP_SVN_USERNAME');
|
$svn_login = getenv('WP_SVN_USERNAME');
|
||||||
$svn_password = getenv('WP_SVN_PASSWORD');
|
$svn_password = getenv('WP_SVN_PASSWORD');
|
||||||
@ -314,11 +387,15 @@ class RoboFile extends \Robo\Tasks {
|
|||||||
->stopOnFail()
|
->stopOnFail()
|
||||||
->dir($svn_dir)
|
->dir($svn_dir)
|
||||||
->exec($release_cmd)
|
->exec($release_cmd)
|
||||||
|
->exec($tag_cmd)
|
||||||
->run();
|
->run();
|
||||||
} else {
|
} else {
|
||||||
$this->yell(
|
$this->yell(
|
||||||
"Go to '$svn_dir' and run '$release_cmd' to publish the release"
|
"Go to '$svn_dir' and run '$release_cmd' to publish the release"
|
||||||
);
|
);
|
||||||
|
$this->yell(
|
||||||
|
"Run '$tag_cmd' to tag the release"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
15
assets/css/src/admin-global.styl
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
@import 'nib'
|
||||||
|
|
||||||
|
@require 'icons'
|
||||||
|
|
||||||
|
/*
|
||||||
|
Style for Members plugin
|
||||||
|
*/
|
||||||
|
|
||||||
|
.members-tab-title
|
||||||
|
.mailpoet-icon-logo
|
||||||
|
vertical-align: middle;
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
font-size: 20px;
|
||||||
|
margin-right: 3px;
|
@ -22,3 +22,8 @@
|
|||||||
@require 'progress_bar'
|
@require 'progress_bar'
|
||||||
|
|
||||||
@require 'subscribers'
|
@require 'subscribers'
|
||||||
|
|
||||||
|
@require 'pages'
|
||||||
|
@require 'pages_custom'
|
||||||
|
|
||||||
|
@require 'mp2migrator'
|
||||||
|
@ -17,6 +17,13 @@ a:focus
|
|||||||
.mailpoet_hidden
|
.mailpoet_hidden
|
||||||
display: none
|
display: none
|
||||||
|
|
||||||
|
// add margins to a div
|
||||||
|
.mailpoet_spaced_block
|
||||||
|
margin: 1em 0
|
||||||
|
|
||||||
|
.mailpoet_centered
|
||||||
|
text-align: center
|
||||||
|
|
||||||
// select 2
|
// select 2
|
||||||
.select2-container
|
.select2-container
|
||||||
width: 25em !important
|
width: 25em !important
|
||||||
|
24
assets/css/src/icons.styl
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
icon-font-path ?= "../fonts"
|
||||||
|
|
||||||
|
@font-face
|
||||||
|
font-family 'mailpoet'
|
||||||
|
src url(icon-font-path + '/mailpoet.ttf?mx0b6n') format('truetype'), url(icon-font-path + '/mailpoet.woff?mx0b6n') format('woff'), url(icon-font-path + '/mailpoet.svg?mx0b6n#mailpoet') format('svg')
|
||||||
|
font-weight normal
|
||||||
|
font-style normal
|
||||||
|
|
||||||
|
[class^="mailpoet-icon-"], [class*=" mailpoet-icon-"]
|
||||||
|
font-family 'mailpoet' !important
|
||||||
|
speak none
|
||||||
|
font-style normal
|
||||||
|
font-weight normal
|
||||||
|
font-variant normal
|
||||||
|
text-transform none
|
||||||
|
line-height 1
|
||||||
|
|
||||||
|
/* Better Font Rendering =========== */
|
||||||
|
-webkit-font-smoothing antialiased
|
||||||
|
-moz-osx-font-smoothing grayscale
|
||||||
|
|
||||||
|
.mailpoet-icon-logo
|
||||||
|
&:before
|
||||||
|
content "\e900"
|
@ -38,6 +38,7 @@ $green-badge-color = #55bd56
|
|||||||
font-weight: 500
|
font-weight: 500
|
||||||
border-radius: 3px
|
border-radius: 3px
|
||||||
letter-spacing: 1px
|
letter-spacing: 1px
|
||||||
|
vertical-align: middle
|
||||||
|
|
||||||
&_excellent
|
&_excellent
|
||||||
background: $excellent-badge-color
|
background: $excellent-badge-color
|
||||||
|
@ -45,7 +45,6 @@ body.mailpoet_modal_opened
|
|||||||
position: absolute
|
position: absolute
|
||||||
z-index: 25
|
z-index: 25
|
||||||
top: 48px
|
top: 48px
|
||||||
padding-bottom: 48px
|
|
||||||
margin: 0
|
margin: 0
|
||||||
|
|
||||||
.mailpoet_popup_wrapper
|
.mailpoet_popup_wrapper
|
||||||
@ -54,6 +53,7 @@ body.mailpoet_modal_opened
|
|||||||
position: relative
|
position: relative
|
||||||
width: 100%
|
width: 100%
|
||||||
z-index: 0
|
z-index: 0
|
||||||
|
height: 96%
|
||||||
|
|
||||||
.mailpoet_overlay_hidden .mailpoet_popup_wrapper
|
.mailpoet_overlay_hidden .mailpoet_popup_wrapper
|
||||||
border: 1px solid #333
|
border: 1px solid #333
|
||||||
@ -75,6 +75,7 @@ body.mailpoet_modal_opened
|
|||||||
|
|
||||||
.mailpoet_popup_body
|
.mailpoet_popup_body
|
||||||
padding: 10px 10px 10px 10px
|
padding: 10px 10px 10px 10px
|
||||||
|
height: 92%
|
||||||
|
|
||||||
// modal panel
|
// modal panel
|
||||||
#mailpoet_modal_overlay.mailpoet_panel_overlay
|
#mailpoet_modal_overlay.mailpoet_panel_overlay
|
||||||
|
33
assets/css/src/mp2migrator.styl
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#logger
|
||||||
|
width: 100%
|
||||||
|
height: 300px
|
||||||
|
background-color: transparent
|
||||||
|
border: 0
|
||||||
|
border-top: 1px #aba9a9 solid
|
||||||
|
padding: 2px
|
||||||
|
overflow: scroll
|
||||||
|
resize: both
|
||||||
|
font-size: 0.85em
|
||||||
|
margin-top: 20px
|
||||||
|
|
||||||
|
#progressbar
|
||||||
|
width: 50%
|
||||||
|
background-color: #d8d8d8
|
||||||
|
border-radius: 5px
|
||||||
|
|
||||||
|
progressbar_color = #fecf23
|
||||||
|
progressbar_gradient_to_color = #fd9215
|
||||||
|
|
||||||
|
.ui-progressbar .ui-progressbar-value
|
||||||
|
height: 100%
|
||||||
|
background-color: progressbar_color
|
||||||
|
background-image: linear-gradient(to bottom, progressbar_color, progressbar_gradient_to_color)
|
||||||
|
border-radius: 3px
|
||||||
|
box-shadow: 0 1px 0 rgba(255,255,255,0.5) inset
|
||||||
|
border 0
|
||||||
|
|
||||||
|
.mailpoet_progress_label
|
||||||
|
font-size: 15px
|
||||||
|
|
||||||
|
.error_msg
|
||||||
|
color: #f00
|
@ -179,6 +179,28 @@ select.mailpoet_font-size
|
|||||||
width: 100%
|
width: 100%
|
||||||
box-sizing: border-box
|
box-sizing: border-box
|
||||||
|
|
||||||
|
.tooltip-help-designer-subject-line div, .tooltip-help-designer-preheader div
|
||||||
|
z-index: 100001
|
||||||
|
|
||||||
|
.tooltip-help-send-preview
|
||||||
|
position: absolute
|
||||||
|
right: 15px
|
||||||
|
|
||||||
|
.tooltip-help-designer-ideal-width
|
||||||
|
color: #656565
|
||||||
|
text-transform: none
|
||||||
|
margin-left: 5px
|
||||||
|
font-weight: normal
|
||||||
|
|
||||||
|
.tooltip-help-designer-styles
|
||||||
|
position: absolute
|
||||||
|
top: 40px
|
||||||
|
|
||||||
|
.tooltip-help-designer-full-width .dashicons
|
||||||
|
line-height 34px
|
||||||
|
.tooltip-help-designer-full-width span
|
||||||
|
line-height 1.4em
|
||||||
|
|
||||||
.mailpoet_button_primary
|
.mailpoet_button_primary
|
||||||
border-color: $button-primary-border-color
|
border-color: $button-primary-border-color
|
||||||
background-color: $button-primary-background-color
|
background-color: $button-primary-background-color
|
||||||
|
@ -62,3 +62,9 @@ $draggable-widget-z-index = 2
|
|||||||
background-color: $primary-active-color
|
background-color: $primary-active-color
|
||||||
box-shadow(inset 1px 2px 1px $primary-inset-shadow-color)
|
box-shadow(inset 1px 2px 1px $primary-inset-shadow-color)
|
||||||
color: $white-color
|
color: $white-color
|
||||||
|
|
||||||
|
.mailpoet_droppable_block
|
||||||
|
cursor: move
|
||||||
|
|
||||||
|
&.mailpoet_ignore_drag
|
||||||
|
cursor: auto
|
||||||
|
@ -48,3 +48,41 @@ $resize-handle-z-index = 2
|
|||||||
|
|
||||||
.mailpoet_resize_handle
|
.mailpoet_resize_handle
|
||||||
display: inline-block
|
display: inline-block
|
||||||
|
|
||||||
|
|
||||||
|
.mailpoet_image_resize_handle_container
|
||||||
|
position: absolute
|
||||||
|
bottom: 0
|
||||||
|
right: 0
|
||||||
|
width: 20px
|
||||||
|
height: 20px
|
||||||
|
|
||||||
|
.mailpoet_image_resize_handle
|
||||||
|
position: relative
|
||||||
|
background: $resize-handle-background-color
|
||||||
|
border-radius(3px)
|
||||||
|
display: inline-block
|
||||||
|
width: 20px
|
||||||
|
height: 20px
|
||||||
|
cursor: nwse-resize
|
||||||
|
z-index: $resize-handle-z-index
|
||||||
|
|
||||||
|
.mailpoet_image_resize_handle_text,
|
||||||
|
.mailpoet_image_resize_handle_icon
|
||||||
|
pointer-events: none
|
||||||
|
|
||||||
|
.mailpoet_image_resize_handle_icon
|
||||||
|
position: absolute
|
||||||
|
top: 0
|
||||||
|
right: 0
|
||||||
|
|
||||||
|
& > svg
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
fill: $resize-handle-font-color
|
||||||
|
|
||||||
|
.mailpoet_block.mailpoet_image_resize_active > .mailpoet_block_highlight
|
||||||
|
border: 1px dashed $resize-active-color
|
||||||
|
|
||||||
|
.mailpoet_image_resize_handle
|
||||||
|
display: inline-block
|
||||||
|
@ -23,12 +23,10 @@ $block-text-line-height = $text-line-height
|
|||||||
border: 1px solid $transparent-color
|
border: 1px solid $transparent-color
|
||||||
|
|
||||||
&:hover > .mailpoet_block_highlight
|
&:hover > .mailpoet_block_highlight
|
||||||
&.mailpoet_highlight > .mailpoet_block_highlight
|
|
||||||
border: 1px dashed $block-hover-highlight-color
|
border: 1px dashed $block-hover-highlight-color
|
||||||
|
|
||||||
|
&.mailpoet_highlight > .mailpoet_block_highlight
|
||||||
.mailpoet_block:last-child
|
border: 1px dashed $block-hover-highlight-color !important
|
||||||
margin-bottom: 0
|
|
||||||
|
|
||||||
.mailpoet_content
|
.mailpoet_content
|
||||||
position: relative
|
position: relative
|
||||||
|
@ -3,14 +3,19 @@
|
|||||||
img
|
img
|
||||||
vertical-align: bottom
|
vertical-align: bottom
|
||||||
max-width: 100%
|
max-width: 100%
|
||||||
width: auto
|
|
||||||
height: auto
|
height: auto
|
||||||
|
|
||||||
&.mailpoet_full_image
|
&.mailpoet_full_image
|
||||||
padding-left: 0
|
padding-left: 0
|
||||||
padding-right: 0
|
padding-right: 0
|
||||||
|
margin: auto
|
||||||
margin-bottom: 0
|
margin-bottom: 0
|
||||||
|
|
||||||
.mailpoet_content a:hover
|
.mailpoet_content
|
||||||
cursor: all-scroll
|
margin: auto
|
||||||
|
max-width: 100%
|
||||||
|
min-width: 36px
|
||||||
|
|
||||||
|
a:hover
|
||||||
|
cursor: all-scroll
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
.mailpoet_settings_posts_display_options
|
.mailpoet_settings_posts_display_options
|
||||||
.mailpoet_settings_posts_selection
|
.mailpoet_settings_posts_selection
|
||||||
animation-slide-open-downwards()
|
animation-slide-open-downwards()
|
||||||
|
overflow-x: hidden
|
||||||
|
|
||||||
.mailpoet_settings_posts_show_display_options,
|
.mailpoet_settings_posts_show_display_options,
|
||||||
.mailpoet_settings_posts_show_post_selection
|
.mailpoet_settings_posts_show_post_selection
|
||||||
@ -26,7 +27,12 @@
|
|||||||
|
|
||||||
.mailpoet_post_selection_container
|
.mailpoet_post_selection_container
|
||||||
margin-top: 20px
|
margin-top: 20px
|
||||||
margin-bottom: 20px
|
margin-bottom: 0
|
||||||
|
|
||||||
|
.mailpoet_post_scroll_container
|
||||||
|
overflow-y: scroll
|
||||||
|
overflow-x: hidden
|
||||||
|
max-height: 400px
|
||||||
|
|
||||||
.mailpoet_settings_posts_single_post
|
.mailpoet_settings_posts_single_post
|
||||||
border-radius(1px)
|
border-radius(1px)
|
||||||
@ -45,3 +51,6 @@
|
|||||||
.mailpoet_select_post_checkbox
|
.mailpoet_select_post_checkbox
|
||||||
margin-left: 10px
|
margin-left: 10px
|
||||||
margin-right: 8px
|
margin-right: 8px
|
||||||
|
|
||||||
|
.mailpoet_post_selection_loading
|
||||||
|
color: #999
|
||||||
|
@ -127,9 +127,6 @@ body
|
|||||||
background-color: $primary-background-color
|
background-color: $primary-background-color
|
||||||
border: 1px solid $content-border-color
|
border: 1px solid $content-border-color
|
||||||
|
|
||||||
#mailpoet_modal_close
|
|
||||||
display: none
|
|
||||||
|
|
||||||
.wrap > .mailpoet_notice,
|
.wrap > .mailpoet_notice,
|
||||||
.notice
|
.notice
|
||||||
.update-nag
|
.update-nag
|
||||||
|
@ -2,7 +2,7 @@ animation-slide-open-downwards($max-height = 2000px)
|
|||||||
transition: all 250ms cubic-bezier(0.420, 0.000, 0.580, 1.000) /* ease-in-out */
|
transition: all 250ms cubic-bezier(0.420, 0.000, 0.580, 1.000) /* ease-in-out */
|
||||||
max-height: $max-height
|
max-height: $max-height
|
||||||
opacity: 1
|
opacity: 1
|
||||||
overflow-y: hidden
|
overflow-y: inherit
|
||||||
|
|
||||||
&.mailpoet_closed
|
&.mailpoet_closed
|
||||||
max-height: 0px
|
max-height: 0px
|
||||||
|
228
assets/css/src/pages.styl
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
/*
|
||||||
|
Based on /wp-admin/css/about.css of WP 4.7.
|
||||||
|
This is to make MailPoet pages independent of the WordPress
|
||||||
|
About page styles that may differ across WP versions.
|
||||||
|
Please add custom styles to pages_custom.styl
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mailpoet-about-wrap
|
||||||
|
position: relative
|
||||||
|
margin: 25px 40px 0 20px
|
||||||
|
max-width: 1050px /* readability */
|
||||||
|
font-size: 15px
|
||||||
|
|
||||||
|
div.updated, div.error, .notice
|
||||||
|
display: none !important
|
||||||
|
|
||||||
|
hr
|
||||||
|
border: 0
|
||||||
|
height: 0
|
||||||
|
margin: 0
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.1)
|
||||||
|
|
||||||
|
img
|
||||||
|
margin: 0
|
||||||
|
max-width: 100%
|
||||||
|
height: auto
|
||||||
|
vertical-align: middle
|
||||||
|
|
||||||
|
.mailpoet-logo
|
||||||
|
position: absolute
|
||||||
|
top: .2em
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
.nav-tab
|
||||||
|
padding-right: 15px
|
||||||
|
padding-left: 15px
|
||||||
|
font-size: 18px
|
||||||
|
|
||||||
|
p
|
||||||
|
line-height: 1.5
|
||||||
|
font-size: 14px
|
||||||
|
|
||||||
|
.feature-section p
|
||||||
|
max-width: 55em
|
||||||
|
margin-left: auto
|
||||||
|
margin-right: auto
|
||||||
|
|
||||||
|
h1
|
||||||
|
margin: 0.2em 200px 0 0
|
||||||
|
padding: 0
|
||||||
|
color: #32373c
|
||||||
|
line-height: 1.2em
|
||||||
|
font-size: 2.8em
|
||||||
|
font-weight: 400
|
||||||
|
|
||||||
|
h2
|
||||||
|
margin: 40px 0 .6em
|
||||||
|
font-size: 2.7em
|
||||||
|
line-height: 1.3
|
||||||
|
font-weight: 300
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
h3
|
||||||
|
margin: 1.25em 0 .6em
|
||||||
|
font-size: 1.4em
|
||||||
|
line-height: 1.5
|
||||||
|
|
||||||
|
h4
|
||||||
|
color: #23282d
|
||||||
|
|
||||||
|
.about-description
|
||||||
|
.about-text
|
||||||
|
margin-top: 1.4em
|
||||||
|
font-weight: 400
|
||||||
|
line-height: 1.6em
|
||||||
|
font-size: 19px
|
||||||
|
|
||||||
|
.about-text
|
||||||
|
margin: 1em 200px 1em 0
|
||||||
|
min-height: 60px
|
||||||
|
color: #555d66
|
||||||
|
|
||||||
|
[class$=col]
|
||||||
|
.col
|
||||||
|
float: left
|
||||||
|
position: relative
|
||||||
|
|
||||||
|
.two-col
|
||||||
|
.col
|
||||||
|
margin-right: 4.799999999%
|
||||||
|
width: 47.6%
|
||||||
|
|
||||||
|
.two-col
|
||||||
|
img
|
||||||
|
margin-bottom: 1.5em
|
||||||
|
|
||||||
|
.feature-section
|
||||||
|
&.two-col
|
||||||
|
.col
|
||||||
|
display: inline-block
|
||||||
|
float: none
|
||||||
|
margin-top: 1em
|
||||||
|
margin-right: 4.799999999%
|
||||||
|
width: calc( 47.6% - 4px )
|
||||||
|
vertical-align: top
|
||||||
|
|
||||||
|
.three-col
|
||||||
|
.col
|
||||||
|
margin-right: 4.999999999%
|
||||||
|
width: 29.95%
|
||||||
|
|
||||||
|
.two-col .col:nth-of-type(2n)
|
||||||
|
.three-col .col:nth-of-type(3n)
|
||||||
|
margin-right: 0
|
||||||
|
|
||||||
|
.feature-section
|
||||||
|
&.two-col
|
||||||
|
h3
|
||||||
|
margin-top: 0
|
||||||
|
|
||||||
|
.feature-section
|
||||||
|
h4
|
||||||
|
margin: 1.4em 0 0.6em 0
|
||||||
|
font-size: 1em
|
||||||
|
|
||||||
|
.feature-section
|
||||||
|
p
|
||||||
|
margin-top: 0.6em
|
||||||
|
|
||||||
|
.lead-description
|
||||||
|
font-size: 1.5em
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
.two-col-text
|
||||||
|
column-count: 2
|
||||||
|
column-gap: 40px
|
||||||
|
|
||||||
|
.two-col-text
|
||||||
|
p:first-of-type
|
||||||
|
margin-top: 0
|
||||||
|
|
||||||
|
.headline-feature
|
||||||
|
&.feature-video
|
||||||
|
position: relative
|
||||||
|
margin: 40px 0
|
||||||
|
padding-bottom: 56.25%
|
||||||
|
width: 100%
|
||||||
|
max-width: 100%
|
||||||
|
height: 0
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
.feature-video
|
||||||
|
embed
|
||||||
|
position: absolute
|
||||||
|
top: 0
|
||||||
|
left: 0
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
.featured-image
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
.feature-section
|
||||||
|
overflow: hidden
|
||||||
|
padding: 0 0 40px
|
||||||
|
|
||||||
|
.feature-section
|
||||||
|
&.no-heading
|
||||||
|
padding-top: 35px
|
||||||
|
|
||||||
|
.headline-feature
|
||||||
|
margin: 0 auto
|
||||||
|
max-width: 80%
|
||||||
|
|
||||||
|
.feature-section
|
||||||
|
.media-container
|
||||||
|
overflow: hidden
|
||||||
|
|
||||||
|
.feature-section
|
||||||
|
img
|
||||||
|
margin-bottom: 1em
|
||||||
|
|
||||||
|
.embed-container
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
.embed-container
|
||||||
|
iframe
|
||||||
|
max-width: 100%
|
||||||
|
|
||||||
|
.wp-embedded-content
|
||||||
|
max-width: 100%
|
||||||
|
|
||||||
|
.feature-section
|
||||||
|
.col
|
||||||
|
margin-top: 40px
|
||||||
|
|
||||||
|
.changelog
|
||||||
|
margin-bottom: 40px
|
||||||
|
|
||||||
|
.changelog
|
||||||
|
&.feature-section
|
||||||
|
.col
|
||||||
|
margin-top: 40px
|
||||||
|
|
||||||
|
@media screen and (max-width: 782px)
|
||||||
|
.two-col-text
|
||||||
|
column-count: 1
|
||||||
|
.three-col img
|
||||||
|
display: block
|
||||||
|
margin: 0 auto
|
||||||
|
|
||||||
|
@media only screen and (max-width: 500px)
|
||||||
|
margin-right: 20px
|
||||||
|
margin-left: 10px
|
||||||
|
h1
|
||||||
|
.about-text
|
||||||
|
margin-right: 0
|
||||||
|
.about-text
|
||||||
|
margin-bottom: 0.25em
|
||||||
|
.mailpoet-logo
|
||||||
|
position: relative
|
||||||
|
margin: 1em 0
|
||||||
|
width: 100%
|
||||||
|
text-align: center
|
||||||
|
.two-col .col
|
||||||
|
.three-col .col
|
||||||
|
width: 100% !important
|
||||||
|
float: none !important
|
38
assets/css/src/pages_custom.styl
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
Custom styles for MailPoet pages.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mailpoet-about-wrap
|
||||||
|
|
||||||
|
.videoWrapper
|
||||||
|
position: relative
|
||||||
|
padding-bottom: 56.25% /* 16:9 */
|
||||||
|
/*padding-top: 25px*/
|
||||||
|
height: 0
|
||||||
|
|
||||||
|
iframe
|
||||||
|
position: absolute
|
||||||
|
top: 0
|
||||||
|
left: 0
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
|
||||||
|
.mailpoet_video
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1)
|
||||||
|
|
||||||
|
#mailpoet-changelog ul
|
||||||
|
list-style: disc
|
||||||
|
padding-left: 20px
|
||||||
|
|
||||||
|
h2.mailpoet-feature-top
|
||||||
|
margin: 50px auto
|
||||||
|
|
||||||
|
a.button.go-to-plugin
|
||||||
|
margin-top: 2em
|
||||||
|
|
||||||
|
p.top-space-triple
|
||||||
|
margin-top: 3em
|
||||||
|
|
||||||
|
p.mailpoet-top-text
|
||||||
|
margin-right: 0
|
@ -9,15 +9,25 @@
|
|||||||
// sending methods
|
// sending methods
|
||||||
.mailpoet_sending_methods
|
.mailpoet_sending_methods
|
||||||
margin 25px 0 0 0
|
margin 25px 0 0 0
|
||||||
li
|
display flex
|
||||||
float left
|
flex-direction row
|
||||||
position relative
|
justify-content flex-start
|
||||||
padding 15px 15px 0 15px
|
> li
|
||||||
|
flex-grow 1
|
||||||
|
flex-shrink 1
|
||||||
|
display flex
|
||||||
|
flex-direction column
|
||||||
|
flex-basis 0
|
||||||
margin 0 25px 25px 0
|
margin 0 25px 25px 0
|
||||||
width 300px
|
|
||||||
height 300px
|
|
||||||
border 1px solid #dedede
|
border 1px solid #dedede
|
||||||
background-color #fff
|
background-color #fff
|
||||||
|
max-width 500px
|
||||||
|
.mailpoet_sending_method_description
|
||||||
|
padding: 25px
|
||||||
|
flex-grow 1
|
||||||
|
flex-shrink 0
|
||||||
|
> li:last-child
|
||||||
|
margin-right 0
|
||||||
h3
|
h3
|
||||||
text-align center
|
text-align center
|
||||||
height 54px
|
height 54px
|
||||||
@ -25,12 +35,12 @@
|
|||||||
.mailpoet_description
|
.mailpoet_description
|
||||||
font-size 14px
|
font-size 14px
|
||||||
.mailpoet_status
|
.mailpoet_status
|
||||||
|
display flex
|
||||||
|
flex-direction row
|
||||||
|
justify-content space-between
|
||||||
|
align-items center
|
||||||
background-color #2f2f2f
|
background-color #2f2f2f
|
||||||
color #fff
|
color #fff
|
||||||
position absolute
|
|
||||||
bottom 0
|
|
||||||
left 0
|
|
||||||
right 0
|
|
||||||
text-overflow ellipsis
|
text-overflow ellipsis
|
||||||
padding 15px
|
padding 15px
|
||||||
span
|
span
|
||||||
@ -38,27 +48,34 @@
|
|||||||
font-weight bold
|
font-weight bold
|
||||||
.mailpoet_active
|
.mailpoet_active
|
||||||
.mailpoet_status
|
.mailpoet_status
|
||||||
|
background-color #088b00
|
||||||
span
|
span
|
||||||
visibility visible
|
visibility visible
|
||||||
#mailpoet_mta_activate
|
#mailpoet_mta_activate
|
||||||
visibility hidden
|
visibility hidden
|
||||||
.mailpoet_actions
|
|
||||||
bottom 15px
|
|
||||||
color #fff
|
|
||||||
padding 0
|
|
||||||
position absolute
|
|
||||||
right 15px
|
|
||||||
.button-secondary
|
|
||||||
margin 0 -6px -4px 0
|
|
||||||
|
|
||||||
// premium key
|
.tooltip.dashicons.dashicons-editor-help
|
||||||
.mailpoet_premium_key
|
line-height: 1.4
|
||||||
&_valid
|
|
||||||
&::before
|
ul.sending-method-benefits
|
||||||
content '✔ '
|
list-style-type: none
|
||||||
&_invalid
|
margin-bottom: 2em
|
||||||
&::before
|
margin-top: 2em
|
||||||
content: '✗ '
|
|
||||||
|
.sending-free-plan-button
|
||||||
|
background: #FF5301
|
||||||
|
border-color: #e64c03
|
||||||
|
text-shadow: 0 -1px 1px #e64c03
|
||||||
|
box-shadow: 0 1px 0 #e64c03
|
||||||
|
margin: 10px 0
|
||||||
|
strong
|
||||||
|
text-transform: uppercase
|
||||||
|
|
||||||
|
.mailpoet_success_item::before
|
||||||
|
content '✔ '
|
||||||
|
|
||||||
|
.mailpoet_error_item::before
|
||||||
|
content '✗ '
|
||||||
|
|
||||||
// responsive
|
// responsive
|
||||||
@media screen and (max-width: 782px)
|
@media screen and (max-width: 782px)
|
||||||
@ -66,8 +83,10 @@
|
|||||||
width auto
|
width auto
|
||||||
|
|
||||||
.mailpoet_sending_methods
|
.mailpoet_sending_methods
|
||||||
li
|
flex-flow: row wrap
|
||||||
float none
|
justify-content: space-around
|
||||||
width auto
|
> li
|
||||||
margin-right 0
|
margin-right 0
|
||||||
|
flex-basis auto
|
||||||
|
|
||||||
|
|
||||||
|
11
assets/fonts/mailpoet.svg
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<metadata>Generated by IcoMoon</metadata>
|
||||||
|
<defs>
|
||||||
|
<font id="mailpoet" horiz-adv-x="1024">
|
||||||
|
<font-face units-per-em="1024" ascent="960" descent="-64" />
|
||||||
|
<missing-glyph horiz-adv-x="1024" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="0" d="" />
|
||||||
|
<glyph unicode="" glyph-name="optimised" horiz-adv-x="972" d="M230.188 949.695c-21.982-3.361-41.376-14.741-48.618-28.188l-5.948-11.637 0.517-265.588c0.776-263.779 0.776-265.847 6.206-273.088 11.12-15.258 40.601-22.24 68.79-16.551 16.551 3.621 25.861 9.827 32.584 21.723 3.879 7.499 4.397 23.791 5.689 185.161l1.293 176.628 60.514-161.111c33.102-88.702 63.875-168.612 68.013-177.404 13.707-29.481 35.687-41.376 72.151-39.049 21.206 1.293 39.308 9.827 47.584 22.499 3.361 5.172 35.947 91.806 72.41 192.662s67.237 185.42 68.53 187.49c1.551 2.587 2.587-69.307 2.587-185.937v-190.335l5.948-11.379c8.533-16.809 20.172-21.982 49.652-21.982 27.929 0.259 39.825 4.914 49.911 20.172l6.982 10.603v530.401l-5.689 9.31c-12.671 20.43-50.17 31.033-91.806 25.601-34.394-4.138-53.79-16.292-66.72-41.118-2.587-5.172-35.947-101.89-73.961-214.902s-69.824-206.367-70.6-207.403c-1.034-1.034-32.326 86.115-69.824 193.954-37.757 107.581-71.892 205.075-76.030 216.453-10.086 26.118-25.601 42.929-45.514 48.877-17.326 5.172-46.807 6.982-64.652 4.138zM54.854 243.443c-20.172-3.879-43.963-19.136-51.204-33.619-6.206-11.379-4.914-32.843 2.587-47.841 23.533-46.807 71.634-86.892 126.717-104.736 17.068-5.689 23.274-5.948 120.252-7.499 97.235-1.551 102.926-1.81 116.373-7.241 29.739-11.896 51.204-35.687 61.807-68.013 3.621-11.12 13.964-21.206 25.861-25.344 4.914-1.551 18.361-2.844 29.739-2.844 16.809 0 23.533 1.293 32.584 5.689 11.896 6.206 13.964 9.31 26.895 38.791 11.896 27.671 39.567 49.652 70.858 56.117 8.533 1.81 47.067 2.844 100.856 2.844 99.563 0 113.786 2.068 151.801 20.689 49.652 24.567 96.978 77.84 101.373 113.529 3.104 26.118-17.326 49.394-51.204 58.187-25.601 6.465-41.635-0.517-54.825-24.050-11.12-19.655-29.998-38.015-47.841-46.29l-14.741-6.982-99.563-1.551c-90.77-1.293-101.373-2.068-120.252-6.982-27.154-7.499-58.444-23.016-80.427-40.084l-17.844-13.964-16.809 13.964c-20.689 16.809-51.462 32.584-78.875 39.825-19.136 5.172-28.705 5.948-120.252 7.241l-99.563 1.551-15.775 7.241c-18.102 8.533-32.584 21.982-48.36 45.773-16.034 24.567-26.895 29.998-50.17 25.601z" />
|
||||||
|
</font></defs></svg>
|
After Width: | Height: | Size: 2.4 KiB |
BIN
assets/fonts/mailpoet.ttf
Normal file
BIN
assets/fonts/mailpoet.woff
Normal file
BIN
assets/img/mailpoet_logo_newsletter.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 143 KiB |
Before Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 124 KiB |
Before Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 262 KiB |
Before Width: | Height: | Size: 276 KiB |
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 441 KiB |
Before Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 122 KiB |
Before Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 819 B After Width: | Height: | Size: 814 B |
@ -1,8 +1,15 @@
|
|||||||
(function(e,b){if(!b.__SV){var a,f,i,g;window.mixpanel=b;b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}var c=b;"undefined"!==typeof d?c=b[d]=[]:d="mixpanel";c.people=c.people||[];c.toString=function(b){var a="mixpanel";"mixpanel"!==d&&(a+="."+d);b||(a+=" (stub)");return a};c.people.toString=function(){return c.toString(1)+".people (stub)"};i="disable time_event track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.union people.track_charge people.clear_charges people.delete_user".split(" ");
|
(function(e,a){if(!a.__SV){var b=window;try{var c,l,i,j=b.location,g=j.hash;c=function(a,b){return(l=a.match(RegExp(b+"=([^&]*)")))?l[1]:null};g&&c(g,"state")&&(i=JSON.parse(decodeURIComponent(c(g,"state"))),"mpeditor"===i.action&&(b.sessionStorage.setItem("_mpcehash",g),history.replaceState(i.desiredHash||"",e.title,j.pathname+j.search)))}catch(m){}var k,h;window.mixpanel=a;a._i=[];a.init=function(b,c,f){function e(b,a){var c=a.split(".");2==c.length&&(b=b[c[0]],a=c[1]);b[a]=function(){b.push([a].concat(Array.prototype.slice.call(arguments,
|
||||||
for(g=0;g<i.length;g++)f(c,i[g]);b._i.push([a,e,d])};b.__SV=1.2;a=e.createElement("script");a.type="text/javascript";a.async=!0;a.src="undefined"!==typeof MIXPANEL_CUSTOM_LIB_URL?MIXPANEL_CUSTOM_LIB_URL:"file:"===e.location.protocol&&"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js".match(/^\/\//)?"https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js":"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";f=e.getElementsByTagName("script")[0];f.parentNode.insertBefore(a,f)}})(document,window.mixpanel||[]);
|
0)))}}var d=a;"undefined"!==typeof f?d=a[f]=[]:f="mixpanel";d.people=d.people||[];d.toString=function(b){var a="mixpanel";"mixpanel"!==f&&(a+="."+f);b||(a+=" (stub)");return a};d.people.toString=function(){return d.toString(1)+".people (stub)"};k="disable time_event track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config reset people.set people.set_once people.increment people.append people.union people.track_charge people.clear_charges people.delete_user".split(" ");
|
||||||
|
for(h=0;h<k.length;h++)e(d,k[h]);a._i.push([b,c,f])};a.__SV=1.2;b=e.createElement("script");b.type="text/javascript";b.async=!0;b.src="undefined"!==typeof MIXPANEL_CUSTOM_LIB_URL?MIXPANEL_CUSTOM_LIB_URL:"file:"===e.location.protocol&&"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js".match(/^\/\//)?"https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js":"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";c=e.getElementsByTagName("script")[0];c.parentNode.insertBefore(b,c)}})(document,window.mixpanel||[]);
|
||||||
|
|
||||||
mixpanel.init("f683d388fb25fcf331f1b2b5c4449798");
|
window.mixpanelTrackingId = "8cce373b255e5a76fb22d57b85db0c92";
|
||||||
|
|
||||||
|
if (mailpoet_analytics_enabled) {
|
||||||
|
|
||||||
|
mixpanel.init(window.mixpanelTrackingId);
|
||||||
|
|
||||||
|
if (mailpoet_analytics_data != null) {
|
||||||
|
mixpanel.track('MailPoet 3', mailpoet_analytics_data);
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof mailpoet_analytics_data === 'object') {
|
|
||||||
mixpanel.track('Wysija Usage', mailpoet_analytics_data || {});
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
define('admin', [
|
define('admin', [
|
||||||
'jquery'
|
'jquery'
|
||||||
],
|
],
|
||||||
function(jQuery) {
|
function (jQuery) {
|
||||||
jQuery(function($) {
|
jQuery(function ($) {
|
||||||
// dom ready
|
// dom ready
|
||||||
$(function() {
|
$(function () {
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -1,74 +1,87 @@
|
|||||||
define('ajax', ['mailpoet', 'jquery', 'underscore'], function(MailPoet, jQuery, _) {
|
function requestFailed(errorMessage, xhr) {
|
||||||
'use strict';
|
if (xhr.responseJSON) {
|
||||||
MailPoet.Ajax = {
|
return xhr.responseJSON;
|
||||||
version: 0.5,
|
}
|
||||||
options: {},
|
return {
|
||||||
defaults: {
|
errors: [
|
||||||
url: null,
|
{
|
||||||
api_version: null,
|
message: errorMessage.replace('%d', xhr.status)
|
||||||
endpoint: null,
|
}
|
||||||
action: null,
|
]
|
||||||
token: null,
|
};
|
||||||
data: {}
|
}
|
||||||
},
|
|
||||||
post: function(options) {
|
define('ajax', ['mailpoet', 'jquery', 'underscore'], function (mp, jQuery, _) {
|
||||||
return this.request('post', options);
|
var MailPoet = mp;
|
||||||
},
|
|
||||||
init: function(options) {
|
MailPoet.Ajax = {
|
||||||
// merge options
|
version: 0.5,
|
||||||
this.options = jQuery.extend({}, this.defaults, options);
|
options: {},
|
||||||
|
defaults: {
|
||||||
// set default url
|
url: null,
|
||||||
if(this.options.url === null) {
|
api_version: null,
|
||||||
this.options.url = ajaxurl;
|
endpoint: null,
|
||||||
}
|
action: null,
|
||||||
|
token: null,
|
||||||
// set default token
|
data: {}
|
||||||
if(this.options.token === null) {
|
},
|
||||||
this.options.token = window.mailpoet_token;
|
post: function (options) {
|
||||||
}
|
return this.request('post', options);
|
||||||
},
|
},
|
||||||
getParams: function() {
|
init: function (options) {
|
||||||
return {
|
// merge options
|
||||||
action: 'mailpoet',
|
this.options = jQuery.extend({}, this.defaults, options);
|
||||||
api_version: this.options.api_version,
|
|
||||||
token: this.options.token,
|
// set default url
|
||||||
endpoint: this.options.endpoint,
|
if (this.options.url === null) {
|
||||||
method: this.options.action,
|
this.options.url = window.ajaxurl;
|
||||||
data: this.options.data || {}
|
}
|
||||||
}
|
|
||||||
},
|
// set default token
|
||||||
request: function(method, options) {
|
if (this.options.token === null) {
|
||||||
// set options
|
this.options.token = window.mailpoet_token;
|
||||||
this.init(options);
|
}
|
||||||
|
},
|
||||||
// set request params
|
getParams: function () {
|
||||||
var params = this.getParams();
|
return {
|
||||||
var deferred = jQuery.Deferred();
|
action: 'mailpoet',
|
||||||
|
api_version: this.options.api_version,
|
||||||
// remove null values from the data object
|
token: this.options.token,
|
||||||
if (_.isObject(params.data)) {
|
endpoint: this.options.endpoint,
|
||||||
params.data = _.pick(params.data, function(value) {
|
method: this.options.action,
|
||||||
return (value !== null)
|
data: this.options.data || {}
|
||||||
})
|
};
|
||||||
}
|
},
|
||||||
|
request: function (method, options) {
|
||||||
// ajax request
|
var params;
|
||||||
deferred = jQuery.post(
|
var deferred;
|
||||||
this.options.url,
|
// set options
|
||||||
params,
|
this.init(options);
|
||||||
null,
|
|
||||||
'json'
|
// set request params
|
||||||
).then(function(data) {
|
params = this.getParams();
|
||||||
return data;
|
|
||||||
}, function(xhr) {
|
// remove null values from the data object
|
||||||
return xhr.responseJSON;
|
if (_.isObject(params.data)) {
|
||||||
});
|
params.data = _.pick(params.data, function (value) {
|
||||||
|
return (value !== null);
|
||||||
// clear options
|
});
|
||||||
this.options = {};
|
}
|
||||||
|
|
||||||
return deferred;
|
// ajax request
|
||||||
}
|
deferred = jQuery.post(
|
||||||
};
|
this.options.url,
|
||||||
});
|
params,
|
||||||
|
null,
|
||||||
|
'json'
|
||||||
|
).then(function (data) {
|
||||||
|
return data;
|
||||||
|
}, _.partial(requestFailed, MailPoet.I18n.t('ajaxFailedErrorMessage')));
|
||||||
|
|
||||||
|
// clear options
|
||||||
|
this.options = {};
|
||||||
|
|
||||||
|
return deferred;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
73
assets/js/src/analytics_event.js
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* This creates two functions and adds them to MailPoet object
|
||||||
|
* - `trackEvent` which should be used in normal circumstances.
|
||||||
|
* This function tracks an event and sends it to mixpanel.
|
||||||
|
* This function does nothing if analytics is disabled.
|
||||||
|
* - `forceTrackEvent` which sends given event to analytics
|
||||||
|
* even if it has been disabled.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is to cache events which are triggered before the mixpanel
|
||||||
|
* library is loaded. This might happen if an event is tracked
|
||||||
|
* on page load and the mixpanel library takes a long time to load.
|
||||||
|
* After it is loaded all events are posted.
|
||||||
|
* @type {Array.Object}
|
||||||
|
*/
|
||||||
|
var eventsCache = [];
|
||||||
|
|
||||||
|
function track(name, data) {
|
||||||
|
if (typeof window.mixpanel.track !== 'function') {
|
||||||
|
window.mixpanel.init(window.mixpanelTrackingId);
|
||||||
|
}
|
||||||
|
window.mixpanel.track(name, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exportMixpanel(mp) {
|
||||||
|
var MailPoet = mp;
|
||||||
|
MailPoet.forceTrackEvent = track;
|
||||||
|
|
||||||
|
if (window.mailpoet_analytics_enabled) {
|
||||||
|
MailPoet.trackEvent = track;
|
||||||
|
} else {
|
||||||
|
MailPoet.trackEvent = function () {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function trackCachedEvents() {
|
||||||
|
eventsCache.map(function (event) {
|
||||||
|
if (window.mailpoet_analytics_enabled || event.forced) {
|
||||||
|
window.mixpanel.track(event.name, event.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function cacheEvent(forced, name, data) {
|
||||||
|
eventsCache.push({
|
||||||
|
name: name,
|
||||||
|
data: data,
|
||||||
|
forced: forced
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
define(
|
||||||
|
['mailpoet', 'underscore'],
|
||||||
|
function (mp, _) {
|
||||||
|
var MailPoet = mp;
|
||||||
|
|
||||||
|
function initializeMixpanelWhenLoaded() {
|
||||||
|
if (typeof window.mixpanel === 'object') {
|
||||||
|
exportMixpanel(MailPoet);
|
||||||
|
trackCachedEvents();
|
||||||
|
} else {
|
||||||
|
setTimeout(initializeMixpanelWhenLoaded, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MailPoet.trackEvent = _.partial(cacheEvent, false);
|
||||||
|
MailPoet.forceTrackEvent = _.partial(cacheEvent, true);
|
||||||
|
|
||||||
|
initializeMixpanelWhenLoaded();
|
||||||
|
}
|
||||||
|
);
|
@ -3,164 +3,172 @@ define('date',
|
|||||||
'mailpoet',
|
'mailpoet',
|
||||||
'jquery',
|
'jquery',
|
||||||
'moment'
|
'moment'
|
||||||
], function(
|
], function (
|
||||||
MailPoet,
|
mp,
|
||||||
jQuery,
|
jQuery,
|
||||||
Moment
|
Moment
|
||||||
) {
|
) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
MailPoet.Date = {
|
var MailPoet = mp;
|
||||||
version: 0.1,
|
|
||||||
options: {},
|
MailPoet.Date = {
|
||||||
defaults: {
|
version: 0.1,
|
||||||
offset: 0,
|
options: {},
|
||||||
format: 'F, d Y H:i:s'
|
defaults: {
|
||||||
},
|
offset: 0,
|
||||||
init: function(options) {
|
format: 'F, d Y H:i:s'
|
||||||
options = options || {};
|
},
|
||||||
|
init: function (opts) {
|
||||||
|
var options = opts || {};
|
||||||
|
|
||||||
// set UTC offset
|
// set UTC offset
|
||||||
if (
|
if (
|
||||||
options.offset === undefined
|
options.offset === undefined
|
||||||
&& window.mailpoet_date_offset !== undefined
|
&& window.mailpoet_date_offset !== undefined
|
||||||
) {
|
) {
|
||||||
options.offset = window.mailpoet_date_offset;
|
options.offset = window.mailpoet_date_offset;
|
||||||
}
|
}
|
||||||
// set date format
|
// set date format
|
||||||
if (
|
if (
|
||||||
options.format === undefined
|
options.format === undefined
|
||||||
&& window.mailpoet_date_format !== undefined
|
&& window.mailpoet_date_format !== undefined
|
||||||
) {
|
) {
|
||||||
options.format = window.mailpoet_date_format;
|
options.format = window.mailpoet_date_format;
|
||||||
}
|
|
||||||
// merge options
|
|
||||||
this.options = jQuery.extend({}, this.defaults, options);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
format: function(date, options) {
|
|
||||||
options = options || {};
|
|
||||||
this.init(options);
|
|
||||||
|
|
||||||
var date = Moment(date, this.convertFormat(options.parseFormat));
|
|
||||||
if (options.offset === 0) date = date.utc();
|
|
||||||
return date.format(this.convertFormat(this.options.format));
|
|
||||||
},
|
|
||||||
toDate: function(date, options) {
|
|
||||||
options = options || {};
|
|
||||||
this.init(options);
|
|
||||||
|
|
||||||
return Moment(date, this.convertFormat(options.parseFormat)).toDate();
|
|
||||||
},
|
|
||||||
short: function(date) {
|
|
||||||
return this.format(date, {
|
|
||||||
format: 'F, j Y'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
full: function(date) {
|
|
||||||
return this.format(date, {
|
|
||||||
format: 'F, j Y H:i:s'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
time: function(date) {
|
|
||||||
return this.format(date, {
|
|
||||||
format: 'H:i:s'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
convertFormat: function(format) {
|
|
||||||
var format_mappings = {
|
|
||||||
date: {
|
|
||||||
d: 'DD',
|
|
||||||
D: 'ddd',
|
|
||||||
j: 'D',
|
|
||||||
l: 'dddd',
|
|
||||||
N: 'E',
|
|
||||||
S: 'o',
|
|
||||||
w: 'e',
|
|
||||||
z: 'DDD',
|
|
||||||
W: 'W',
|
|
||||||
F: 'MMMM',
|
|
||||||
m: 'MM',
|
|
||||||
M: 'MMM',
|
|
||||||
n: 'M',
|
|
||||||
t: '', // no equivalent
|
|
||||||
L: '', // no equivalent
|
|
||||||
o: 'YYYY',
|
|
||||||
Y: 'YYYY',
|
|
||||||
y: 'YY',
|
|
||||||
a: 'a',
|
|
||||||
A: 'A',
|
|
||||||
B: '', // no equivalent
|
|
||||||
g: 'h',
|
|
||||||
G: 'H',
|
|
||||||
h: 'hh',
|
|
||||||
H: 'HH',
|
|
||||||
i: 'mm',
|
|
||||||
s: 'ss',
|
|
||||||
u: 'SSS',
|
|
||||||
e: 'zz', // deprecated since version 1.6.0 of moment.js
|
|
||||||
I: '', // no equivalent
|
|
||||||
O: '', // no equivalent
|
|
||||||
P: '', // no equivalent
|
|
||||||
T: '', // no equivalent
|
|
||||||
Z: '', // no equivalent
|
|
||||||
c: '', // no equivalent
|
|
||||||
r: '', // no equivalent
|
|
||||||
U: 'X'
|
|
||||||
},
|
|
||||||
strftime: {
|
|
||||||
a: 'ddd',
|
|
||||||
A: 'dddd',
|
|
||||||
b: 'MMM',
|
|
||||||
B: 'MMMM',
|
|
||||||
d: 'DD',
|
|
||||||
e: 'D',
|
|
||||||
F: 'YYYY-MM-DD',
|
|
||||||
H: 'HH',
|
|
||||||
I: 'hh',
|
|
||||||
j: 'DDDD',
|
|
||||||
k: 'H',
|
|
||||||
l: 'h',
|
|
||||||
m: 'MM',
|
|
||||||
M: 'mm',
|
|
||||||
p: 'A',
|
|
||||||
S: 'ss',
|
|
||||||
u: 'E',
|
|
||||||
w: 'd',
|
|
||||||
W: 'WW',
|
|
||||||
y: 'YY',
|
|
||||||
Y: 'YYYY',
|
|
||||||
z: 'ZZ',
|
|
||||||
Z: 'z'
|
|
||||||
}
|
}
|
||||||
};
|
// merge options
|
||||||
|
this.options = jQuery.extend({}, this.defaults, options);
|
||||||
|
|
||||||
if (!format || format.length <= 0) return format;
|
return this;
|
||||||
|
},
|
||||||
|
format: function (date, opts) {
|
||||||
|
var options = opts || {};
|
||||||
|
var momentDate;
|
||||||
|
this.init(options);
|
||||||
|
|
||||||
var replacements = format_mappings['date'];
|
momentDate = Moment(date, this.convertFormat(options.parseFormat));
|
||||||
|
if (options.offset === 0) momentDate = momentDate.utc();
|
||||||
|
return momentDate.format(this.convertFormat(this.options.format));
|
||||||
|
},
|
||||||
|
toDate: function (date, opts) {
|
||||||
|
var options = opts || {};
|
||||||
|
this.init(options);
|
||||||
|
|
||||||
var convertedFormat = [];
|
return Moment(date, this.convertFormat(options.parseFormat)).toDate();
|
||||||
var escapeToken = false;
|
},
|
||||||
|
short: function (date) {
|
||||||
|
return this.format(date, {
|
||||||
|
format: 'F, j Y'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
full: function (date) {
|
||||||
|
return this.format(date, {
|
||||||
|
format: 'F, j Y H:i:s'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
time: function (date) {
|
||||||
|
return this.format(date, {
|
||||||
|
format: 'H:i:s'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
convertFormat: function (format) {
|
||||||
|
var replacements;
|
||||||
|
var convertedFormat;
|
||||||
|
var escapeToken;
|
||||||
|
var index;
|
||||||
|
var token;
|
||||||
|
var format_mappings = {
|
||||||
|
date: {
|
||||||
|
d: 'DD',
|
||||||
|
D: 'ddd',
|
||||||
|
j: 'D',
|
||||||
|
l: 'dddd',
|
||||||
|
N: 'E',
|
||||||
|
S: 'o',
|
||||||
|
w: 'e',
|
||||||
|
z: 'DDD',
|
||||||
|
W: 'W',
|
||||||
|
F: 'MMMM',
|
||||||
|
m: 'MM',
|
||||||
|
M: 'MMM',
|
||||||
|
n: 'M',
|
||||||
|
t: '', // no equivalent
|
||||||
|
L: '', // no equivalent
|
||||||
|
o: 'YYYY',
|
||||||
|
Y: 'YYYY',
|
||||||
|
y: 'YY',
|
||||||
|
a: 'a',
|
||||||
|
A: 'A',
|
||||||
|
B: '', // no equivalent
|
||||||
|
g: 'h',
|
||||||
|
G: 'H',
|
||||||
|
h: 'hh',
|
||||||
|
H: 'HH',
|
||||||
|
i: 'mm',
|
||||||
|
s: 'ss',
|
||||||
|
u: 'SSS',
|
||||||
|
e: 'zz', // deprecated since version 1.6.0 of moment.js
|
||||||
|
I: '', // no equivalent
|
||||||
|
O: '', // no equivalent
|
||||||
|
P: '', // no equivalent
|
||||||
|
T: '', // no equivalent
|
||||||
|
Z: '', // no equivalent
|
||||||
|
c: '', // no equivalent
|
||||||
|
r: '', // no equivalent
|
||||||
|
U: 'X'
|
||||||
|
},
|
||||||
|
strftime: {
|
||||||
|
a: 'ddd',
|
||||||
|
A: 'dddd',
|
||||||
|
b: 'MMM',
|
||||||
|
B: 'MMMM',
|
||||||
|
d: 'DD',
|
||||||
|
e: 'D',
|
||||||
|
F: 'YYYY-MM-DD',
|
||||||
|
H: 'HH',
|
||||||
|
I: 'hh',
|
||||||
|
j: 'DDDD',
|
||||||
|
k: 'H',
|
||||||
|
l: 'h',
|
||||||
|
m: 'MM',
|
||||||
|
M: 'mm',
|
||||||
|
p: 'A',
|
||||||
|
S: 'ss',
|
||||||
|
u: 'E',
|
||||||
|
w: 'd',
|
||||||
|
W: 'WW',
|
||||||
|
y: 'YY',
|
||||||
|
Y: 'YYYY',
|
||||||
|
z: 'ZZ',
|
||||||
|
Z: 'z'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
for(var index = 0, token = ''; token = format.charAt(index); index++){
|
if (!format || format.length <= 0) return format;
|
||||||
if (escapeToken === true) {
|
|
||||||
convertedFormat.push('['+token+']');
|
replacements = format_mappings['date'];
|
||||||
escapeToken = false;
|
convertedFormat = [];
|
||||||
} else {
|
escapeToken = false;
|
||||||
if (token === '\\') {
|
|
||||||
// Slash escapes the next symbol to be treated as literal
|
for (index = 0, token = ''; format.charAt(index); index += 1) {
|
||||||
escapeToken = true;
|
token = format.charAt(index);
|
||||||
continue;
|
if (escapeToken === true) {
|
||||||
} else if (replacements[token] !== undefined) {
|
convertedFormat.push('[' + token + ']');
|
||||||
convertedFormat.push(replacements[token]);
|
escapeToken = false;
|
||||||
} else {
|
} else {
|
||||||
convertedFormat.push('['+token+']');
|
if (token === '\\') {
|
||||||
|
// Slash escapes the next symbol to be treated as literal
|
||||||
|
escapeToken = true;
|
||||||
|
continue;
|
||||||
|
} else if (replacements[token] !== undefined) {
|
||||||
|
convertedFormat.push(replacements[token]);
|
||||||
|
} else {
|
||||||
|
convertedFormat.push('[' + token + ']');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return convertedFormat.join('');
|
return convertedFormat.join('');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
define([
|
define([
|
||||||
'react'
|
'react',
|
||||||
],
|
],
|
||||||
function(
|
(
|
||||||
React
|
React
|
||||||
) {
|
) => {
|
||||||
const FormFieldCheckbox = React.createClass({
|
const FormFieldCheckbox = React.createClass({
|
||||||
onValueChange: function(e) {
|
onValueChange: function (e) {
|
||||||
e.target.value = this.refs.checkbox.checked ? '1' : '0';
|
e.target.value = this.refs.checkbox.checked ? '1' : '0';
|
||||||
return this.props.onValueChange(e);
|
return this.props.onValueChange(e);
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function () {
|
||||||
if (this.props.field.values === undefined) {
|
if (this.props.field.values === undefined) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -20,15 +20,15 @@ function(
|
|||||||
const options = Object.keys(this.props.field.values).map(
|
const options = Object.keys(this.props.field.values).map(
|
||||||
(value, index) => {
|
(value, index) => {
|
||||||
return (
|
return (
|
||||||
<p key={ 'checkbox-' + index }>
|
<p key={'checkbox-' + index}>
|
||||||
<label>
|
<label>
|
||||||
<input
|
<input
|
||||||
ref="checkbox"
|
ref="checkbox"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
value="1"
|
value="1"
|
||||||
checked={ isChecked }
|
checked={isChecked}
|
||||||
onChange={ this.onValueChange }
|
onChange={this.onValueChange}
|
||||||
name={ this.props.field.name }
|
name={this.props.field.name}
|
||||||
/>
|
/>
|
||||||
{ this.props.field.values[value] }
|
{ this.props.field.values[value] }
|
||||||
</label>
|
</label>
|
||||||
@ -42,8 +42,8 @@ function(
|
|||||||
{ options }
|
{ options }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return FormFieldCheckbox;
|
return FormFieldCheckbox;
|
||||||
});
|
});
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
define([
|
define([
|
||||||
'react',
|
'react',
|
||||||
'moment',
|
'moment',
|
||||||
], function(
|
], (
|
||||||
React,
|
React,
|
||||||
Moment
|
Moment
|
||||||
) {
|
) => {
|
||||||
class FormFieldDateYear extends React.Component {
|
class FormFieldDateYear extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const yearsRange = 100;
|
const yearsRange = 100;
|
||||||
@ -12,24 +12,24 @@ define([
|
|||||||
|
|
||||||
if (this.props.placeholder !== undefined) {
|
if (this.props.placeholder !== undefined) {
|
||||||
years.push((
|
years.push((
|
||||||
<option value="" key={ 0 }>{ this.props.placeholder }</option>
|
<option value="" key={0}>{ this.props.placeholder }</option>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentYear = Moment().year();
|
const currentYear = Moment().year();
|
||||||
for (let i = currentYear; i >= currentYear - yearsRange; i--) {
|
for (let i = currentYear; i >= currentYear - yearsRange; i -= 1) {
|
||||||
years.push((
|
years.push((
|
||||||
<option
|
<option
|
||||||
key={ i }
|
key={i}
|
||||||
value={ i }
|
value={i}
|
||||||
>{ i }</option>
|
>{ i }</option>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<select
|
<select
|
||||||
name={ `${this.props.name}[year]` }
|
name={`${this.props.name}[year]`}
|
||||||
value={ this.props.year }
|
value={this.props.year}
|
||||||
onChange={ this.props.onValueChange }
|
onChange={this.props.onValueChange}
|
||||||
>
|
>
|
||||||
{ years }
|
{ years }
|
||||||
</select>
|
</select>
|
||||||
@ -43,23 +43,23 @@ define([
|
|||||||
|
|
||||||
if (this.props.placeholder !== undefined) {
|
if (this.props.placeholder !== undefined) {
|
||||||
months.push((
|
months.push((
|
||||||
<option value="" key={ 0 }>{ this.props.placeholder }</option>
|
<option value="" key={0}>{ this.props.placeholder }</option>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 1; i <= 12; i++) {
|
for (let i = 1; i <= 12; i += 1) {
|
||||||
months.push((
|
months.push((
|
||||||
<option
|
<option
|
||||||
key={ i }
|
key={i}
|
||||||
value={ i }
|
value={i}
|
||||||
>{ this.props.monthNames[i - 1] }</option>
|
>{ this.props.monthNames[i - 1] }</option>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<select
|
<select
|
||||||
name={ `${this.props.name}[month]` }
|
name={`${this.props.name}[month]`}
|
||||||
value={ this.props.month }
|
value={this.props.month}
|
||||||
onChange={ this.props.onValueChange }
|
onChange={this.props.onValueChange}
|
||||||
>
|
>
|
||||||
{ months }
|
{ months }
|
||||||
</select>
|
</select>
|
||||||
@ -73,24 +73,24 @@ define([
|
|||||||
|
|
||||||
if (this.props.placeholder !== undefined) {
|
if (this.props.placeholder !== undefined) {
|
||||||
days.push((
|
days.push((
|
||||||
<option value="" key={ 0 }>{ this.props.placeholder }</option>
|
<option value="" key={0}>{ this.props.placeholder }</option>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 1; i <= 31; i++) {
|
for (let i = 1; i <= 31; i += 1) {
|
||||||
days.push((
|
days.push((
|
||||||
<option
|
<option
|
||||||
key={ i }
|
key={i}
|
||||||
value={ i }
|
value={i}
|
||||||
>{ i }</option>
|
>{ i }</option>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<select
|
<select
|
||||||
name={ `${this.props.name}[day]` }
|
name={`${this.props.name}[day]`}
|
||||||
value={ this.props.day }
|
value={this.props.day}
|
||||||
onChange={ this.props.onValueChange }
|
onChange={this.props.onValueChange}
|
||||||
>
|
>
|
||||||
{ days }
|
{ days }
|
||||||
</select>
|
</select>
|
||||||
@ -104,13 +104,13 @@ define([
|
|||||||
this.state = {
|
this.state = {
|
||||||
year: '',
|
year: '',
|
||||||
month: '',
|
month: '',
|
||||||
day: ''
|
day: '',
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.extractDateParts();
|
this.extractDateParts();
|
||||||
}
|
}
|
||||||
componentDidUpdate(prevProps, prevState) {
|
componentDidUpdate(prevProps) {
|
||||||
if (
|
if (
|
||||||
(this.props.item !== undefined && prevProps.item !== undefined)
|
(this.props.item !== undefined && prevProps.item !== undefined)
|
||||||
&& (this.props.item.id !== prevProps.item.id)
|
&& (this.props.item.id !== prevProps.item.id)
|
||||||
@ -123,7 +123,7 @@ define([
|
|||||||
? this.props.item[this.props.field.name].trim()
|
? this.props.item[this.props.field.name].trim()
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
if(value === '') {
|
if (value === '') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +132,7 @@ define([
|
|||||||
this.setState({
|
this.setState({
|
||||||
year: dateTime.format('YYYY'),
|
year: dateTime.format('YYYY'),
|
||||||
month: dateTime.format('M'),
|
month: dateTime.format('M'),
|
||||||
day: dateTime.format('D')
|
day: dateTime.format('D'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
formatValue() {
|
formatValue() {
|
||||||
@ -140,33 +140,33 @@ define([
|
|||||||
|
|
||||||
let value;
|
let value;
|
||||||
|
|
||||||
switch(dateType) {
|
switch (dateType) {
|
||||||
case 'year_month_day':
|
case 'year_month_day':
|
||||||
value = {
|
value = {
|
||||||
'year': this.state.year,
|
year: this.state.year,
|
||||||
'month': this.state.month,
|
month: this.state.month,
|
||||||
'day': this.state.day
|
day: this.state.day,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'year_month':
|
case 'year_month':
|
||||||
value = {
|
value = {
|
||||||
'year': this.state.year,
|
year: this.state.year,
|
||||||
'month': this.state.month
|
month: this.state.month,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'month':
|
case 'month':
|
||||||
value = {
|
value = {
|
||||||
'month': this.state.month
|
month: this.state.month,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'year':
|
case 'year':
|
||||||
value = {
|
value = {
|
||||||
'year': this.state.year
|
year: this.state.year,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
@ -181,16 +181,16 @@ define([
|
|||||||
field = matches[1];
|
field = matches[1];
|
||||||
property = matches[2];
|
property = matches[2];
|
||||||
|
|
||||||
let value = ~~(e.target.value);
|
const value = ~~(e.target.value);
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
[`${property}`]: value
|
[`${property}`]: value,
|
||||||
}, () => {
|
}, () => {
|
||||||
this.props.onValueChange({
|
this.props.onValueChange({
|
||||||
target: {
|
target: {
|
||||||
name: field,
|
name: field,
|
||||||
value: this.formatValue()
|
value: this.formatValue(),
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -201,43 +201,40 @@ define([
|
|||||||
const dateType = this.props.field.params.date_type;
|
const dateType = this.props.field.params.date_type;
|
||||||
const dateSelects = dateFormats[dateType][0].split('/');
|
const dateSelects = dateFormats[dateType][0].split('/');
|
||||||
|
|
||||||
const fields = dateSelects.map(type => {
|
const fields = dateSelects.map((type) => {
|
||||||
switch(type) {
|
switch (type) {
|
||||||
case 'YYYY':
|
case 'YYYY':
|
||||||
return (<FormFieldDateYear
|
return (<FormFieldDateYear
|
||||||
onValueChange={ this.onValueChange.bind(this) }
|
onValueChange={this.onValueChange.bind(this)}
|
||||||
ref={ 'year' }
|
ref={'year'}
|
||||||
key={ 'year' }
|
key={'year'}
|
||||||
name={ this.props.field.name }
|
name={this.props.field.name}
|
||||||
year={ this.state.year }
|
year={this.state.year}
|
||||||
placeholder={ this.props.field.year_placeholder }
|
placeholder={this.props.field.year_placeholder}
|
||||||
/>);
|
/>);
|
||||||
break;
|
|
||||||
|
|
||||||
case 'MM':
|
case 'MM':
|
||||||
return (<FormFieldDateMonth
|
return (<FormFieldDateMonth
|
||||||
onValueChange={ this.onValueChange.bind(this) }
|
onValueChange={this.onValueChange.bind(this)}
|
||||||
ref={ 'month' }
|
ref={'month'}
|
||||||
key={ 'month' }
|
key={'month'}
|
||||||
name={ this.props.field.name }
|
name={this.props.field.name}
|
||||||
month={ this.state.month }
|
month={this.state.month}
|
||||||
monthNames={ monthNames }
|
monthNames={monthNames}
|
||||||
placeholder={ this.props.field.month_placeholder }
|
placeholder={this.props.field.month_placeholder}
|
||||||
/>);
|
/>);
|
||||||
break;
|
|
||||||
|
|
||||||
case 'DD':
|
case 'DD':
|
||||||
return (<FormFieldDateDay
|
return (<FormFieldDateDay
|
||||||
onValueChange={ this.onValueChange.bind(this) }
|
onValueChange={this.onValueChange.bind(this)}
|
||||||
ref={ 'day' }
|
ref={'day'}
|
||||||
key={ 'day' }
|
key={'day'}
|
||||||
name={ this.props.field.name }
|
name={this.props.field.name}
|
||||||
day={ this.state.day }
|
day={this.state.day}
|
||||||
placeholder={ this.props.field.day_placeholder }
|
placeholder={this.props.field.day_placeholder}
|
||||||
/>);
|
/>);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -245,7 +242,7 @@ define([
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return FormFieldDate;
|
return FormFieldDate;
|
||||||
});
|
});
|
||||||
|
@ -7,8 +7,9 @@ define([
|
|||||||
'form/fields/checkbox.jsx',
|
'form/fields/checkbox.jsx',
|
||||||
'form/fields/selection.jsx',
|
'form/fields/selection.jsx',
|
||||||
'form/fields/date.jsx',
|
'form/fields/date.jsx',
|
||||||
|
'jquery',
|
||||||
],
|
],
|
||||||
function(
|
(
|
||||||
React,
|
React,
|
||||||
FormFieldText,
|
FormFieldText,
|
||||||
FormFieldTextarea,
|
FormFieldTextarea,
|
||||||
@ -16,91 +17,93 @@ function(
|
|||||||
FormFieldRadio,
|
FormFieldRadio,
|
||||||
FormFieldCheckbox,
|
FormFieldCheckbox,
|
||||||
FormFieldSelection,
|
FormFieldSelection,
|
||||||
FormFieldDate
|
FormFieldDate,
|
||||||
) {
|
jQuery
|
||||||
var FormField = React.createClass({
|
) => {
|
||||||
renderField: function(data, inline = false) {
|
const FormField = React.createClass({
|
||||||
var description = false;
|
renderField: function (data, inline = false) {
|
||||||
if(data.field.description) {
|
let description = false;
|
||||||
|
if (data.field.description) {
|
||||||
description = (
|
description = (
|
||||||
<p className="description">{ data.field.description }</p>
|
<p className="description">{ data.field.description }</p>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
var field = false;
|
let field = false;
|
||||||
|
let dataField = data.field;
|
||||||
|
|
||||||
if(data.field['field'] !== undefined) {
|
if (data.field['field'] !== undefined) {
|
||||||
data.field = jQuery.merge(data.field, data.field.field);
|
dataField = jQuery.merge(dataField, data.field.field);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(data.field.type) {
|
switch (dataField.type) {
|
||||||
case 'text':
|
case 'text':
|
||||||
field = (<FormFieldText {...data} />);
|
field = (<FormFieldText {...data} />);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'textarea':
|
case 'textarea':
|
||||||
field = (<FormFieldTextarea {...data} />);
|
field = (<FormFieldTextarea {...data} />);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'select':
|
case 'select':
|
||||||
field = (<FormFieldSelect {...data} />);
|
field = (<FormFieldSelect {...data} />);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'radio':
|
case 'radio':
|
||||||
field = (<FormFieldRadio {...data} />);
|
field = (<FormFieldRadio {...data} />);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'checkbox':
|
case 'checkbox':
|
||||||
field = (<FormFieldCheckbox {...data} />);
|
field = (<FormFieldCheckbox {...data} />);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'selection':
|
case 'selection':
|
||||||
field = (<FormFieldSelection {...data} />);
|
field = (<FormFieldSelection {...data} />);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'date':
|
case 'date':
|
||||||
field = (<FormFieldDate {...data} />);
|
field = (<FormFieldDate {...data} />);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'reactComponent':
|
case 'reactComponent':
|
||||||
field = (<data.field.component {...data} />);
|
field = (<data.field.component {...data} />);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(inline === true) {
|
if (inline === true) {
|
||||||
return (
|
return (
|
||||||
<span key={ 'field-' + (data.index || 0) }>
|
<span key={'field-' + (data.index || 0)}>
|
||||||
{ field }
|
{ field }
|
||||||
{ description }
|
{ description }
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<div key={ 'field-' + (data.index || 0) }>
|
<div key={'field-' + (data.index || 0)}>
|
||||||
{ field }
|
{ field }
|
||||||
{ description }
|
{ description }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function () {
|
||||||
var field = false;
|
let field = false;
|
||||||
|
|
||||||
if(this.props.field['fields'] !== undefined) {
|
if (this.props.field['fields'] !== undefined) {
|
||||||
field = this.props.field.fields.map(function(subfield, index) {
|
field = this.props.field.fields.map((subfield, index) => {
|
||||||
return this.renderField({
|
return this.renderField({
|
||||||
index: index,
|
index: index,
|
||||||
field: subfield,
|
field: subfield,
|
||||||
item: this.props.item,
|
item: this.props.item,
|
||||||
onValueChange: this.props.onValueChange || false
|
onValueChange: this.props.onValueChange || false,
|
||||||
});
|
});
|
||||||
}.bind(this));
|
});
|
||||||
} else {
|
} else {
|
||||||
field = this.renderField(this.props);
|
field = this.renderField(this.props);
|
||||||
}
|
}
|
||||||
|
|
||||||
var tip = false;
|
let tip = false;
|
||||||
if(this.props.field.tip) {
|
if (this.props.field.tip) {
|
||||||
tip = (
|
tip = (
|
||||||
<p className="description">{ this.props.field.tip }</p>
|
<p className="description">{ this.props.field.tip }</p>
|
||||||
);
|
);
|
||||||
@ -110,7 +113,7 @@ function(
|
|||||||
<tr>
|
<tr>
|
||||||
<th scope="row">
|
<th scope="row">
|
||||||
<label
|
<label
|
||||||
htmlFor={ 'field_'+this.props.field.name }
|
htmlFor={'field_' + this.props.field.name}
|
||||||
>
|
>
|
||||||
{ this.props.field.label }
|
{ this.props.field.label }
|
||||||
{ tip }
|
{ tip }
|
||||||
@ -121,7 +124,7 @@ function(
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return FormField;
|
return FormField;
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
define([
|
define([
|
||||||
'react'
|
'react',
|
||||||
],
|
],
|
||||||
function(
|
(
|
||||||
React
|
React
|
||||||
) {
|
) => {
|
||||||
const FormFieldRadio = React.createClass({
|
const FormFieldRadio = React.createClass({
|
||||||
render: function() {
|
render: function () {
|
||||||
if (this.props.field.values === undefined) {
|
if (this.props.field.values === undefined) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -14,14 +14,14 @@ function(
|
|||||||
const options = Object.keys(this.props.field.values).map(
|
const options = Object.keys(this.props.field.values).map(
|
||||||
(value, index) => {
|
(value, index) => {
|
||||||
return (
|
return (
|
||||||
<p key={ 'radio-' + index }>
|
<p key={'radio-' + index}>
|
||||||
<label>
|
<label>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
checked={ selected_value === value }
|
checked={selected_value === value}
|
||||||
value={ value }
|
value={value}
|
||||||
onChange={ this.props.onValueChange }
|
onChange={this.props.onValueChange}
|
||||||
name={ this.props.field.name } />
|
name={this.props.field.name} />
|
||||||
{ this.props.field.values[value] }
|
{ this.props.field.values[value] }
|
||||||
</label>
|
</label>
|
||||||
</p>
|
</p>
|
||||||
@ -34,8 +34,8 @@ function(
|
|||||||
{ options }
|
{ options }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return FormFieldRadio;
|
return FormFieldRadio;
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react';
|
||||||
import _ from 'underscore'
|
import _ from 'underscore';
|
||||||
|
|
||||||
const FormFieldSelect = React.createClass({
|
const FormFieldSelect = React.createClass({
|
||||||
render() {
|
render() {
|
||||||
@ -33,12 +33,12 @@ const FormFieldSelect = React.createClass({
|
|||||||
_.map(
|
_.map(
|
||||||
_.sortBy(
|
_.sortBy(
|
||||||
_.pairs(this.props.field.values),
|
_.pairs(this.props.field.values),
|
||||||
(item) => sortBy(item[0], item[1])
|
item => sortBy(item[0], item[1])
|
||||||
),
|
),
|
||||||
(item) => item[0]
|
item => item[0]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
keys = Object.keys(this.props.field.values)
|
keys = Object.keys(this.props.field.values);
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = keys.map(
|
const options = keys.map(
|
||||||
@ -50,8 +50,8 @@ const FormFieldSelect = React.createClass({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<option
|
<option
|
||||||
key={ 'option-' + index }
|
key={'option-' + index}
|
||||||
value={ value }>
|
value={value}>
|
||||||
{ this.props.field.values[value] }
|
{ this.props.field.values[value] }
|
||||||
</option>
|
</option>
|
||||||
);
|
);
|
||||||
@ -60,17 +60,17 @@ const FormFieldSelect = React.createClass({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<select
|
<select
|
||||||
name={ this.props.field.name }
|
name={this.props.field.name}
|
||||||
id={ 'field_'+this.props.field.name }
|
id={'field_' + this.props.field.name}
|
||||||
value={ this.props.item[this.props.field.name] }
|
value={this.props.item[this.props.field.name]}
|
||||||
onChange={ this.props.onValueChange }
|
onChange={this.props.onValueChange}
|
||||||
{...this.props.field.validation}
|
{...this.props.field.validation}
|
||||||
>
|
>
|
||||||
{placeholder}
|
{placeholder}
|
||||||
{options}
|
{options}
|
||||||
</select>
|
</select>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = FormFieldSelect;
|
module.exports = FormFieldSelect;
|
||||||
|
@ -2,80 +2,80 @@ define([
|
|||||||
'react',
|
'react',
|
||||||
'react-dom',
|
'react-dom',
|
||||||
'jquery',
|
'jquery',
|
||||||
'select2'
|
'select2',
|
||||||
],
|
],
|
||||||
function(
|
(
|
||||||
React,
|
React,
|
||||||
ReactDOM,
|
ReactDOM,
|
||||||
jQuery
|
jQuery
|
||||||
) {
|
) => {
|
||||||
var Selection = React.createClass({
|
const Selection = React.createClass({
|
||||||
getInitialState: function() {
|
getInitialState: function () {
|
||||||
return {
|
return {
|
||||||
items: [],
|
items: [],
|
||||||
select2: false
|
select2: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
componentWillMount: function() {
|
componentWillMount: function () {
|
||||||
this.loadCachedItems();
|
this.loadCachedItems();
|
||||||
},
|
},
|
||||||
allowMultipleValues: function() {
|
allowMultipleValues: function () {
|
||||||
return (this.props.field.multiple === true);
|
return (this.props.field.multiple === true);
|
||||||
},
|
},
|
||||||
isSelect2Initialized: function() {
|
isSelect2Initialized: function () {
|
||||||
return (this.state.select2 === true);
|
return (this.state.select2 === true);
|
||||||
},
|
},
|
||||||
componentDidMount: function() {
|
componentDidMount: function () {
|
||||||
if(this.allowMultipleValues()) {
|
if (this.allowMultipleValues()) {
|
||||||
this.setupSelect2();
|
this.setupSelect2();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
componentDidUpdate: function(prevProps, prevState) {
|
componentDidUpdate: function (prevProps) {
|
||||||
if(
|
if (
|
||||||
(this.props.item !== undefined && prevProps.item !== undefined)
|
(this.props.item !== undefined && prevProps.item !== undefined)
|
||||||
&& (this.props.item.id !== prevProps.item.id)
|
&& (this.props.item.id !== prevProps.item.id)
|
||||||
) {
|
) {
|
||||||
jQuery('#'+this.refs.select.id)
|
jQuery('#' + this.refs.select.id)
|
||||||
.val(this.getSelectedValues())
|
.val(this.getSelectedValues())
|
||||||
.trigger('change');
|
.trigger('change');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount: function () {
|
||||||
if(this.allowMultipleValues()) {
|
if (this.allowMultipleValues()) {
|
||||||
this.destroySelect2();
|
this.destroySelect2();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
destroySelect2: function() {
|
destroySelect2: function () {
|
||||||
if(this.isSelect2Initialized()) {
|
if (this.isSelect2Initialized()) {
|
||||||
jQuery('#'+this.refs.select.id).select2('destroy');
|
jQuery('#' + this.refs.select.id).select2('destroy');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setupSelect2: function() {
|
setupSelect2: function () {
|
||||||
if(this.isSelect2Initialized()) {
|
if (this.isSelect2Initialized()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var select2 = jQuery('#'+this.refs.select.id).select2({
|
const select2 = jQuery('#' + this.refs.select.id).select2({
|
||||||
width: (this.props.width || ''),
|
width: (this.props.width || ''),
|
||||||
templateResult: function(item) {
|
templateResult: function (item) {
|
||||||
if(item.element && item.element.selected) {
|
if (item.element && item.element.selected) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
if(item.title) {
|
if (item.title) {
|
||||||
return item.title;
|
return item.title;
|
||||||
} else {
|
} else {
|
||||||
return item.text;
|
return item.text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var hasRemoved = false;
|
let hasRemoved = false;
|
||||||
select2.on('select2:unselecting', function(e) {
|
select2.on('select2:unselecting', () => {
|
||||||
hasRemoved = true;
|
hasRemoved = true;
|
||||||
});
|
});
|
||||||
select2.on('select2:opening', function(e) {
|
select2.on('select2:opening', (e) => {
|
||||||
if(hasRemoved === true) {
|
if (hasRemoved === true) {
|
||||||
hasRemoved = false;
|
hasRemoved = false;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
@ -85,13 +85,13 @@ function(
|
|||||||
|
|
||||||
this.setState({ select2: true });
|
this.setState({ select2: true });
|
||||||
},
|
},
|
||||||
getSelectedValues: function() {
|
getSelectedValues: function () {
|
||||||
if(this.props.field['selected'] !== undefined) {
|
if (this.props.field['selected'] !== undefined) {
|
||||||
return this.props.field['selected'](this.props.item);
|
return this.props.field['selected'](this.props.item);
|
||||||
} else if(this.props.item !== undefined && this.props.field.name !== undefined) {
|
} else if (this.props.item !== undefined && this.props.field.name !== undefined) {
|
||||||
if (this.allowMultipleValues()) {
|
if (this.allowMultipleValues()) {
|
||||||
if (Array.isArray(this.props.item[this.props.field.name])) {
|
if (Array.isArray(this.props.item[this.props.field.name])) {
|
||||||
return this.props.item[this.props.field.name].map(function(item) {
|
return this.props.item[this.props.field.name].map((item) => {
|
||||||
return item.id;
|
return item.id;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -101,50 +101,51 @@ function(
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
loadCachedItems: function() {
|
loadCachedItems: function () {
|
||||||
if(typeof(window['mailpoet_'+this.props.field.endpoint]) !== 'undefined') {
|
if (typeof (window['mailpoet_' + this.props.field.endpoint]) !== 'undefined') {
|
||||||
var items = window['mailpoet_'+this.props.field.endpoint];
|
let items = window['mailpoet_' + this.props.field.endpoint];
|
||||||
|
|
||||||
|
|
||||||
if(this.props.field['filter'] !== undefined) {
|
if (this.props.field['filter'] !== undefined) {
|
||||||
items = items.filter(this.props.field.filter);
|
items = items.filter(this.props.field.filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
items: items
|
items: items,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleChange: function(e) {
|
handleChange: function (e) {
|
||||||
if(this.props.onValueChange !== undefined) {
|
let value;
|
||||||
if(this.props.field.multiple) {
|
if (this.props.onValueChange !== undefined) {
|
||||||
value = jQuery('#'+this.refs.select.id).val();
|
if (this.props.field.multiple) {
|
||||||
|
value = jQuery('#' + this.refs.select.id).val();
|
||||||
} else {
|
} else {
|
||||||
value = e.target.value;
|
value = e.target.value;
|
||||||
}
|
}
|
||||||
var transformedValue = this.transformChangedValue(value);
|
const transformedValue = this.transformChangedValue(value);
|
||||||
this.props.onValueChange({
|
this.props.onValueChange({
|
||||||
target: {
|
target: {
|
||||||
value: transformedValue,
|
value: transformedValue,
|
||||||
name: this.props.field.name
|
name: this.props.field.name,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getLabel: function(item) {
|
getLabel: function (item) {
|
||||||
if(this.props.field['getLabel'] !== undefined) {
|
if (this.props.field['getLabel'] !== undefined) {
|
||||||
return this.props.field.getLabel(item, this.props.item);
|
return this.props.field.getLabel(item, this.props.item);
|
||||||
}
|
}
|
||||||
return item.name;
|
return item.name;
|
||||||
},
|
},
|
||||||
getSearchLabel: function(item) {
|
getSearchLabel: function (item) {
|
||||||
if(this.props.field['getSearchLabel'] !== undefined) {
|
if (this.props.field['getSearchLabel'] !== undefined) {
|
||||||
return this.props.field.getSearchLabel(item, this.props.item);
|
return this.props.field.getSearchLabel(item, this.props.item);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
getValue: function(item) {
|
getValue: function (item) {
|
||||||
if(this.props.field['getValue'] !== undefined) {
|
if (this.props.field['getValue'] !== undefined) {
|
||||||
return this.props.field.getValue(item, this.props.item);
|
return this.props.field.getValue(item, this.props.item);
|
||||||
}
|
}
|
||||||
return item.id;
|
return item.id;
|
||||||
@ -152,24 +153,24 @@ function(
|
|||||||
// When it's impossible to represent the desired value in DOM,
|
// When it's impossible to represent the desired value in DOM,
|
||||||
// this function may be used to transform the placeholder value into
|
// this function may be used to transform the placeholder value into
|
||||||
// desired value.
|
// desired value.
|
||||||
transformChangedValue: function(value) {
|
transformChangedValue: function (value) {
|
||||||
if(typeof this.props.field['transformChangedValue'] === 'function') {
|
if (typeof this.props.field['transformChangedValue'] === 'function') {
|
||||||
return this.props.field.transformChangedValue.call(this, value);
|
return this.props.field.transformChangedValue.call(this, value);
|
||||||
} else {
|
} else {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function () {
|
||||||
const options = this.state.items.map((item, index) => {
|
const options = this.state.items.map((item, index) => {
|
||||||
let label = this.getLabel(item);
|
const label = this.getLabel(item);
|
||||||
let searchLabel = this.getSearchLabel(item);
|
const searchLabel = this.getSearchLabel(item);
|
||||||
let value = this.getValue(item);
|
const value = this.getValue(item);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<option
|
<option
|
||||||
key={ 'option-'+index }
|
key={'option-' + index}
|
||||||
value={ value }
|
value={value}
|
||||||
title={ searchLabel }
|
title={searchLabel}
|
||||||
>
|
>
|
||||||
{ label }
|
{ label }
|
||||||
</option>
|
</option>
|
||||||
@ -178,15 +179,16 @@ function(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<select
|
<select
|
||||||
id={ this.props.field.id || this.props.field.name }
|
id={this.props.field.id || this.props.field.name}
|
||||||
ref="select"
|
ref="select"
|
||||||
data-placeholder={ this.props.field.placeholder }
|
disabled={this.props.field.disabled}
|
||||||
multiple={ this.props.field.multiple }
|
data-placeholder={this.props.field.placeholder}
|
||||||
defaultValue={ this.getSelectedValues() }
|
multiple={this.props.field.multiple}
|
||||||
|
defaultValue={this.getSelectedValues()}
|
||||||
{...this.props.field.validation}
|
{...this.props.field.validation}
|
||||||
>{ options }</select>
|
>{ options }</select>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return Selection;
|
return Selection;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React from 'react'
|
import React from 'react';
|
||||||
|
|
||||||
const FormFieldText = React.createClass({
|
const FormFieldText = React.createClass({
|
||||||
render() {
|
render() {
|
||||||
@ -15,21 +15,21 @@ const FormFieldText = React.createClass({
|
|||||||
? this.props.field.disabled(this.props.item)
|
? this.props.field.disabled(this.props.item)
|
||||||
: false
|
: false
|
||||||
}
|
}
|
||||||
className={ (this.props.field.size) ? '' : 'regular-text' }
|
className={(this.props.field.size) ? '' : 'regular-text'}
|
||||||
size={
|
size={
|
||||||
(this.props.field.size !== 'auto' && this.props.field.size > 0)
|
(this.props.field.size !== 'auto' && this.props.field.size > 0)
|
||||||
? this.props.field.size
|
? this.props.field.size
|
||||||
: false
|
: false
|
||||||
}
|
}
|
||||||
name={ this.props.field.name }
|
name={this.props.field.name}
|
||||||
id={ 'field_'+this.props.field.name }
|
id={'field_' + this.props.field.name}
|
||||||
value={ value }
|
value={value}
|
||||||
placeholder={ this.props.field.placeholder }
|
placeholder={this.props.field.placeholder}
|
||||||
onChange={ this.props.onValueChange }
|
onChange={this.props.onValueChange}
|
||||||
{...this.props.field.validation}
|
{...this.props.field.validation}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = FormFieldText;
|
module.exports = FormFieldText;
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
define([
|
define([
|
||||||
'react'
|
'react',
|
||||||
],
|
],
|
||||||
function(
|
(
|
||||||
React
|
React
|
||||||
) {
|
) => {
|
||||||
var FormFieldTextarea = React.createClass({
|
const FormFieldTextarea = React.createClass({
|
||||||
render: function() {
|
render: function () {
|
||||||
return (
|
return (
|
||||||
<textarea
|
<textarea
|
||||||
type="text"
|
type="text"
|
||||||
className="regular-text"
|
className="regular-text"
|
||||||
name={ this.props.field.name }
|
name={this.props.field.name}
|
||||||
id={ 'field_'+this.props.field.name }
|
id={'field_' + this.props.field.name}
|
||||||
value={ this.props.item[this.props.field.name] }
|
value={this.props.item[this.props.field.name]}
|
||||||
placeholder={ this.props.field.placeholder }
|
placeholder={this.props.field.placeholder}
|
||||||
defaultValue={ this.props.field.defaultValue }
|
defaultValue={this.props.field.defaultValue}
|
||||||
onChange={ this.props.onValueChange }
|
onChange={this.props.onValueChange}
|
||||||
{...this.props.field.validation}
|
{...this.props.field.validation}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return FormFieldTextarea;
|
return FormFieldTextarea;
|
||||||
});
|
});
|
||||||
|
@ -4,54 +4,56 @@ define(
|
|||||||
'mailpoet',
|
'mailpoet',
|
||||||
'classnames',
|
'classnames',
|
||||||
'react-router',
|
'react-router',
|
||||||
'form/fields/field.jsx'
|
'form/fields/field.jsx',
|
||||||
|
'jquery',
|
||||||
],
|
],
|
||||||
function(
|
(
|
||||||
React,
|
React,
|
||||||
MailPoet,
|
MailPoet,
|
||||||
classNames,
|
classNames,
|
||||||
Router,
|
Router,
|
||||||
FormField
|
FormField,
|
||||||
) {
|
jQuery
|
||||||
|
) => {
|
||||||
|
|
||||||
var Form = React.createClass({
|
const Form = React.createClass({
|
||||||
contextTypes: {
|
contextTypes: {
|
||||||
router: React.PropTypes.object.isRequired
|
router: React.PropTypes.object.isRequired,
|
||||||
},
|
},
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function () {
|
||||||
return {
|
return {
|
||||||
params: {},
|
params: {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
getInitialState: function() {
|
getInitialState: function () {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
errors: [],
|
errors: [],
|
||||||
item: {}
|
item: {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
getValues: function() {
|
getValues: function () {
|
||||||
return this.props.item ? this.props.item : this.state.item;
|
return this.props.item ? this.props.item : this.state.item;
|
||||||
},
|
},
|
||||||
getErrors: function() {
|
getErrors: function () {
|
||||||
return this.props.errors ? this.props.errors : this.state.errors;
|
return this.props.errors ? this.props.errors : this.state.errors;
|
||||||
},
|
},
|
||||||
componentDidMount: function() {
|
componentDidMount: function () {
|
||||||
if(this.isMounted()) {
|
if (this.isMounted()) {
|
||||||
if(this.props.params.id !== undefined) {
|
if (this.props.params.id !== undefined) {
|
||||||
this.loadItem(this.props.params.id);
|
this.loadItem(this.props.params.id);
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
item: jQuery('.mailpoet_form').serializeObject()
|
item: jQuery('.mailpoet_form').serializeObject(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
componentWillReceiveProps: function(props) {
|
componentWillReceiveProps: function (props) {
|
||||||
if(props.params.id === undefined) {
|
if (props.params.id === undefined) {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
loading: false,
|
||||||
item: {}
|
item: {},
|
||||||
});
|
});
|
||||||
if (props.item === undefined) {
|
if (props.item === undefined) {
|
||||||
this.refs.form.reset();
|
this.refs.form.reset();
|
||||||
@ -60,7 +62,7 @@ define(
|
|||||||
this.loadItem(props.params.id);
|
this.loadItem(props.params.id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
loadItem: function(id) {
|
loadItem: function (id) {
|
||||||
this.setState({ loading: true });
|
this.setState({ loading: true });
|
||||||
|
|
||||||
MailPoet.Ajax.post({
|
MailPoet.Ajax.post({
|
||||||
@ -68,28 +70,28 @@ define(
|
|||||||
endpoint: this.props.endpoint,
|
endpoint: this.props.endpoint,
|
||||||
action: 'get',
|
action: 'get',
|
||||||
data: {
|
data: {
|
||||||
id: id
|
id: id,
|
||||||
}
|
},
|
||||||
}).done((response) => {
|
}).done((response) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
loading: false,
|
||||||
item: response.data
|
item: response.data,
|
||||||
});
|
});
|
||||||
}).fail((response) => {
|
}).fail(() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
loading: false,
|
||||||
item: {}
|
item: {},
|
||||||
}, function() {
|
}, function () {
|
||||||
this.context.router.push('/new');
|
this.context.router.push('/new');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleSubmit: function(e) {
|
handleSubmit: function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
// handle validation
|
// handle validation
|
||||||
if(this.props.isValid !== undefined) {
|
if (this.props.isValid !== undefined) {
|
||||||
if(this.props.isValid() === false) {
|
if (this.props.isValid() === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -97,18 +99,18 @@ define(
|
|||||||
this.setState({ loading: true });
|
this.setState({ loading: true });
|
||||||
|
|
||||||
// only get values from displayed fields
|
// only get values from displayed fields
|
||||||
var item = {};
|
const item = {};
|
||||||
this.props.fields.map(function(field) {
|
this.props.fields.map((field) => {
|
||||||
if(field['fields'] !== undefined) {
|
if (field['fields'] !== undefined) {
|
||||||
field.fields.map(function(subfield) {
|
field.fields.map((subfield) => {
|
||||||
item[subfield.name] = this.state.item[subfield.name];
|
item[subfield.name] = this.state.item[subfield.name];
|
||||||
}.bind(this));
|
});
|
||||||
} else {
|
} else {
|
||||||
item[field.name] = this.state.item[field.name];
|
item[field.name] = this.state.item[field.name];
|
||||||
}
|
}
|
||||||
}.bind(this));
|
});
|
||||||
// set id if specified
|
// set id if specified
|
||||||
if(this.props.params.id !== undefined) {
|
if (this.props.params.id !== undefined) {
|
||||||
item.id = this.props.params.id;
|
item.id = this.props.params.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,60 +118,61 @@ define(
|
|||||||
api_version: window.mailpoet_api_version,
|
api_version: window.mailpoet_api_version,
|
||||||
endpoint: this.props.endpoint,
|
endpoint: this.props.endpoint,
|
||||||
action: 'save',
|
action: 'save',
|
||||||
data: item
|
data: item,
|
||||||
}).always(() => {
|
}).always(() => {
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
}).done((response) => {
|
}).done(() => {
|
||||||
if(this.props.onSuccess !== undefined) {
|
if (this.props.onSuccess !== undefined) {
|
||||||
this.props.onSuccess();
|
this.props.onSuccess();
|
||||||
} else {
|
} else {
|
||||||
this.context.router.push('/');
|
this.context.router.push('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.props.params.id !== undefined) {
|
if (this.props.params.id !== undefined) {
|
||||||
this.props.messages.onUpdate();
|
this.props.messages.onUpdate();
|
||||||
} else {
|
} else {
|
||||||
this.props.messages.onCreate();
|
this.props.messages.onCreate();
|
||||||
}
|
}
|
||||||
}).fail((response) => {
|
}).fail((response) => {
|
||||||
if(response.errors.length > 0) {
|
if (response.errors.length > 0) {
|
||||||
this.setState({ errors: response.errors });
|
this.setState({ errors: response.errors });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleValueChange: function(e) {
|
handleValueChange: function (e) {
|
||||||
if (this.props.onChange) {
|
if (this.props.onChange) {
|
||||||
return this.props.onChange(e);
|
return this.props.onChange(e);
|
||||||
} else {
|
} else {
|
||||||
var item = this.state.item,
|
const item = this.state.item;
|
||||||
field = e.target.name;
|
const field = e.target.name;
|
||||||
|
|
||||||
item[field] = e.target.value;
|
item[field] = e.target.value;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
item: item
|
item: item,
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function () {
|
||||||
if(this.getErrors() !== undefined) {
|
let errors;
|
||||||
var errors = this.getErrors().map(function(error, index) {
|
if (this.getErrors() !== undefined) {
|
||||||
|
errors = this.getErrors().map((error, index) => {
|
||||||
return (
|
return (
|
||||||
<p key={ 'error-'+index } className="mailpoet_error">
|
<p key={'error-' + index} className="mailpoet_error">
|
||||||
{ error.message }
|
{ error.message }
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var formClasses = classNames(
|
const formClasses = classNames(
|
||||||
'mailpoet_form',
|
'mailpoet_form',
|
||||||
{ 'mailpoet_form_loading': this.state.loading || this.props.loading }
|
{ mailpoet_form_loading: this.state.loading || this.props.loading }
|
||||||
);
|
);
|
||||||
|
|
||||||
var beforeFormContent = false;
|
let beforeFormContent = false;
|
||||||
var afterFormContent = false;
|
let afterFormContent = false;
|
||||||
|
|
||||||
if (this.props.beforeFormContent !== undefined) {
|
if (this.props.beforeFormContent !== undefined) {
|
||||||
beforeFormContent = this.props.beforeFormContent(this.getValues());
|
beforeFormContent = this.props.beforeFormContent(this.getValues());
|
||||||
@ -179,18 +182,27 @@ define(
|
|||||||
afterFormContent = this.props.afterFormContent(this.getValues());
|
afterFormContent = this.props.afterFormContent(this.getValues());
|
||||||
}
|
}
|
||||||
|
|
||||||
var fields = this.props.fields.map(function(field, i) {
|
const fields = this.props.fields.map((field, i) => {
|
||||||
|
// Compose an onChange handler from the default and custom one
|
||||||
|
let onValueChange = this.handleValueChange;
|
||||||
|
if (field.onBeforeChange) {
|
||||||
|
onValueChange = (e) => {
|
||||||
|
field.onBeforeChange(e);
|
||||||
|
return this.handleValueChange(e);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormField
|
<FormField
|
||||||
field={ field }
|
field={field}
|
||||||
item={ this.getValues() }
|
item={this.getValues()}
|
||||||
onValueChange={ this.handleValueChange }
|
onValueChange={onValueChange}
|
||||||
key={ 'field-'+i } />
|
key={'field-' + i} />
|
||||||
);
|
);
|
||||||
}.bind(this));
|
});
|
||||||
|
|
||||||
var actions = false;
|
let actions = false;
|
||||||
if(this.props.children) {
|
if (this.props.children) {
|
||||||
actions = this.props.children;
|
actions = this.props.children;
|
||||||
} else {
|
} else {
|
||||||
actions = (
|
actions = (
|
||||||
@ -206,9 +218,9 @@ define(
|
|||||||
<div>
|
<div>
|
||||||
{ beforeFormContent }
|
{ beforeFormContent }
|
||||||
<form
|
<form
|
||||||
id={ this.props.id }
|
id={this.props.id}
|
||||||
ref="form"
|
ref="form"
|
||||||
className={ formClasses }
|
className={formClasses}
|
||||||
onSubmit={
|
onSubmit={
|
||||||
(this.props.onSubmit !== undefined)
|
(this.props.onSubmit !== undefined)
|
||||||
? this.props.onSubmit
|
? this.props.onSubmit
|
||||||
@ -228,7 +240,7 @@ define(
|
|||||||
{ afterFormContent }
|
{ afterFormContent }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return Form;
|
return Form;
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
import React from 'react'
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom';
|
||||||
import { Router, Route, IndexRoute, Link, useRouterHistory } from 'react-router'
|
import { Router, Route, IndexRoute, useRouterHistory } from 'react-router';
|
||||||
import { createHashHistory } from 'history'
|
import { createHashHistory } from 'history';
|
||||||
import FormList from 'forms/list.jsx'
|
import FormList from './list.jsx';
|
||||||
|
|
||||||
const history = useRouterHistory(createHashHistory)({ queryKey: false });
|
const history = useRouterHistory(createHashHistory)({ queryKey: false });
|
||||||
|
|
||||||
const App = React.createClass({
|
const App = React.createClass({
|
||||||
render() {
|
render() {
|
||||||
return this.props.children
|
return this.props.children;
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const container = document.getElementById('forms_container');
|
const container = document.getElementById('forms_container');
|
||||||
|
|
||||||
if(container) {
|
if (container) {
|
||||||
ReactDOM.render((
|
ReactDOM.render((
|
||||||
<Router history={ history }>
|
<Router history={history}>
|
||||||
<Route path="/" component={ App }>
|
<Route path="/" component={App}>
|
||||||
<IndexRoute component={ FormList } />
|
<IndexRoute component={FormList} />
|
||||||
<Route path="*" component={ FormList } />
|
<Route path="*" component={FormList} />
|
||||||
</Route>
|
</Route>
|
||||||
</Router>
|
</Router>
|
||||||
), container);
|
), container);
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,28 @@
|
|||||||
import React from 'react'
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom'
|
import classNames from 'classnames';
|
||||||
import { Router, Link } from 'react-router'
|
import MailPoet from 'mailpoet';
|
||||||
import Listing from 'listing/listing.jsx'
|
import jQuery from 'jquery';
|
||||||
import classNames from 'classnames'
|
import Listing from '../listing/listing.jsx';
|
||||||
import MailPoet from 'mailpoet'
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
name: 'name',
|
name: 'name',
|
||||||
label: MailPoet.I18n.t('formName'),
|
label: MailPoet.I18n.t('formName'),
|
||||||
sortable: true
|
sortable: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'segments',
|
name: 'segments',
|
||||||
label: MailPoet.I18n.t('segments')
|
label: MailPoet.I18n.t('segments'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'signups',
|
name: 'signups',
|
||||||
label: MailPoet.I18n.t('signups')
|
label: MailPoet.I18n.t('signups'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'created_at',
|
name: 'created_at',
|
||||||
label: MailPoet.I18n.t('createdOn'),
|
label: MailPoet.I18n.t('createdOn'),
|
||||||
sortable: true
|
sortable: true,
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const messages = {
|
const messages = {
|
||||||
@ -71,38 +70,38 @@ const messages = {
|
|||||||
).replace('%$1d', count.toLocaleString());
|
).replace('%$1d', count.toLocaleString());
|
||||||
}
|
}
|
||||||
MailPoet.Notice.success(message);
|
MailPoet.Notice.success(message);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const bulk_actions = [
|
const bulk_actions = [
|
||||||
{
|
{
|
||||||
name: 'trash',
|
name: 'trash',
|
||||||
label: MailPoet.I18n.t('moveToTrash'),
|
label: MailPoet.I18n.t('moveToTrash'),
|
||||||
onSuccess: messages.onTrash
|
onSuccess: messages.onTrash,
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const item_actions = [
|
const item_actions = [
|
||||||
{
|
{
|
||||||
name: 'edit',
|
name: 'edit',
|
||||||
label: MailPoet.I18n.t('edit'),
|
label: MailPoet.I18n.t('edit'),
|
||||||
link: function(item) {
|
link: function (item) {
|
||||||
return (
|
return (
|
||||||
<a href={ `admin.php?page=mailpoet-form-editor&id=${item.id}` }>{MailPoet.I18n.t('edit')}</a>
|
<a href={`admin.php?page=mailpoet-form-editor&id=${item.id}`}>{MailPoet.I18n.t('edit')}</a>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'duplicate',
|
name: 'duplicate',
|
||||||
label: MailPoet.I18n.t('duplicate'),
|
label: MailPoet.I18n.t('duplicate'),
|
||||||
onClick: function(item, refresh) {
|
onClick: function (item, refresh) {
|
||||||
return MailPoet.Ajax.post({
|
return MailPoet.Ajax.post({
|
||||||
api_version: window.mailpoet_api_version,
|
api_version: window.mailpoet_api_version,
|
||||||
endpoint: 'forms',
|
endpoint: 'forms',
|
||||||
action: 'duplicate',
|
action: 'duplicate',
|
||||||
data: {
|
data: {
|
||||||
id: item.id
|
id: item.id,
|
||||||
}
|
},
|
||||||
}).done((response) => {
|
}).done((response) => {
|
||||||
MailPoet.Notice.success(
|
MailPoet.Notice.success(
|
||||||
(MailPoet.I18n.t('formDuplicated')).replace('%$1s', response.data.name)
|
(MailPoet.I18n.t('formDuplicated')).replace('%$1s', response.data.name)
|
||||||
@ -111,16 +110,16 @@ const item_actions = [
|
|||||||
}).fail((response) => {
|
}).fail((response) => {
|
||||||
if (response.errors.length > 0) {
|
if (response.errors.length > 0) {
|
||||||
MailPoet.Notice.error(
|
MailPoet.Notice.error(
|
||||||
response.errors.map(function(error) { return error.message; }),
|
response.errors.map((error) => { return error.message; }),
|
||||||
{ scroll: true }
|
{ scroll: true }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'trash'
|
name: 'trash',
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const FormList = React.createClass({
|
const FormList = React.createClass({
|
||||||
@ -128,28 +127,28 @@ const FormList = React.createClass({
|
|||||||
MailPoet.Ajax.post({
|
MailPoet.Ajax.post({
|
||||||
api_version: window.mailpoet_api_version,
|
api_version: window.mailpoet_api_version,
|
||||||
endpoint: 'forms',
|
endpoint: 'forms',
|
||||||
action: 'create'
|
action: 'create',
|
||||||
}).done((response) => {
|
}).done((response) => {
|
||||||
window.location = mailpoet_form_edit_url + response.data.id;
|
window.location = window.mailpoet_form_edit_url + response.data.id;
|
||||||
}).fail((response) => {
|
}).fail((response) => {
|
||||||
if (response.errors.length > 0) {
|
if (response.errors.length > 0) {
|
||||||
MailPoet.Notice.error(
|
MailPoet.Notice.error(
|
||||||
response.errors.map(function(error) { return error.message; }),
|
response.errors.map((error) => { return error.message; }),
|
||||||
{ scroll: true }
|
{ scroll: true }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
renderItem(form, actions) {
|
renderItem(form, actions) {
|
||||||
let row_classes = classNames(
|
const row_classes = classNames(
|
||||||
'manage-column',
|
'manage-column',
|
||||||
'column-primary',
|
'column-primary',
|
||||||
'has-row-actions'
|
'has-row-actions'
|
||||||
);
|
);
|
||||||
|
|
||||||
let segments = mailpoet_segments.filter(function(segment) {
|
let segments = window.mailpoet_segments.filter((segment) => {
|
||||||
return (jQuery.inArray(segment.id, form.segments) !== -1);
|
return (jQuery.inArray(segment.id, form.segments) !== -1);
|
||||||
}).map(function(segment) {
|
}).map((segment) => {
|
||||||
return segment.name;
|
return segment.name;
|
||||||
}).join(', ');
|
}).join(', ');
|
||||||
|
|
||||||
@ -159,11 +158,11 @@ const FormList = React.createClass({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<td className={ row_classes }>
|
<td className={row_classes}>
|
||||||
<strong>
|
<strong>
|
||||||
<a
|
<a
|
||||||
className="row-title"
|
className="row-title"
|
||||||
href={ `admin.php?page=mailpoet-form-editor&id=${form.id}` }
|
href={`admin.php?page=mailpoet-form-editor&id=${form.id}`}
|
||||||
>{ form.name }</a>
|
>{ form.name }</a>
|
||||||
</strong>
|
</strong>
|
||||||
{ actions }
|
{ actions }
|
||||||
@ -187,25 +186,25 @@ const FormList = React.createClass({
|
|||||||
{MailPoet.I18n.t('pageTitle')} <a
|
{MailPoet.I18n.t('pageTitle')} <a
|
||||||
className="page-title-action"
|
className="page-title-action"
|
||||||
href="javascript:;"
|
href="javascript:;"
|
||||||
onClick={ this.createForm }
|
onClick={this.createForm}
|
||||||
>{MailPoet.I18n.t('new')}</a>
|
>{MailPoet.I18n.t('new')}</a>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<Listing
|
<Listing
|
||||||
limit={ mailpoet_listing_per_page }
|
limit={window.mailpoet_listing_per_page}
|
||||||
location={ this.props.location }
|
location={this.props.location}
|
||||||
params={ this.props.params }
|
params={this.props.params}
|
||||||
messages={ messages }
|
messages={messages}
|
||||||
search={ false }
|
search={false}
|
||||||
endpoint="forms"
|
endpoint="forms"
|
||||||
onRenderItem={ this.renderItem }
|
onRenderItem={this.renderItem}
|
||||||
columns={ columns }
|
columns={columns}
|
||||||
bulk_actions={ bulk_actions }
|
bulk_actions={bulk_actions}
|
||||||
item_actions={ item_actions }
|
item_actions={item_actions}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = FormList;
|
module.exports = FormList;
|
||||||
|
@ -1,117 +1,119 @@
|
|||||||
define('handlebars_helpers', ['handlebars'], function(Handlebars) {
|
define('handlebars_helpers', ['handlebars'], function (Handlebars) {
|
||||||
// Handlebars helpers
|
// Handlebars helpers
|
||||||
Handlebars.registerHelper('concat', function() {
|
Handlebars.registerHelper('concat', function () {
|
||||||
var size = (arguments.length - 1),
|
var size = (arguments.length - 1);
|
||||||
output = '';
|
var output = '';
|
||||||
for(var i = 0; i < size; i++) {
|
var i;
|
||||||
output += arguments[i];
|
for (i = 0; i < size; i++) {
|
||||||
};
|
output += arguments[i];
|
||||||
return output;
|
}
|
||||||
|
return output;
|
||||||
});
|
});
|
||||||
|
|
||||||
Handlebars.registerHelper('number_format', function(value, block) {
|
Handlebars.registerHelper('number_format', function (value) {
|
||||||
return Number(value).toLocaleString();
|
return Number(value).toLocaleString();
|
||||||
});
|
});
|
||||||
Handlebars.registerHelper('date_format', function(timestamp, block) {
|
Handlebars.registerHelper('date_format', function (timestamp, block) {
|
||||||
if(window.moment) {
|
var f;
|
||||||
if(timestamp === undefined || isNaN(timestamp) || timestamp <= 0) {
|
if (window.moment) {
|
||||||
return;
|
if (timestamp === undefined || isNaN(timestamp) || timestamp <= 0) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// set date format
|
// set date format
|
||||||
var f = block.hash.format || "MMM Do, YYYY";
|
f = block.hash.format || 'MMM Do, YYYY';
|
||||||
// check if we passed a timestamp
|
// check if we passed a timestamp
|
||||||
if(parseInt(timestamp, 10) == timestamp) {
|
if (parseInt(timestamp, 10) == timestamp) {
|
||||||
return moment.unix(timestamp).format(f);
|
return window.moment.unix(timestamp).format(f);
|
||||||
} else {
|
} else {
|
||||||
return moment.utc(timestamp).format(f);
|
return window.moment.utc(timestamp).format(f);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return timestamp;
|
return timestamp;
|
||||||
};
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Handlebars.registerHelper('cycle', function(value, block) {
|
Handlebars.registerHelper('cycle', function (value, block) {
|
||||||
var values = value.split(' ');
|
var values = value.split(' ');
|
||||||
return values[block.data.index % (values.length + 1)];
|
return values[block.data.index % (values.length + 1)];
|
||||||
});
|
});
|
||||||
|
|
||||||
Handlebars.registerHelper('ifCond', function (v1, operator, v2, options) {
|
Handlebars.registerHelper('ifCond', function (v1, operator, v2, options) {
|
||||||
switch (operator) {
|
switch (operator) {
|
||||||
case '==':
|
case '==':
|
||||||
return (v1 == v2) ? options.fn(this) : options.inverse(this);
|
return (v1 == v2) ? options.fn(this) : options.inverse(this);
|
||||||
case '===':
|
case '===':
|
||||||
return (v1 === v2) ? options.fn(this) : options.inverse(this);
|
return (v1 === v2) ? options.fn(this) : options.inverse(this);
|
||||||
case '!=':
|
case '!=':
|
||||||
return (v1 != v2) ? options.fn(this) : options.inverse(this);
|
return (v1 != v2) ? options.fn(this) : options.inverse(this);
|
||||||
case '!==':
|
case '!==':
|
||||||
return (v1 !== v2) ? options.fn(this) : options.inverse(this);
|
return (v1 !== v2) ? options.fn(this) : options.inverse(this);
|
||||||
case '<':
|
case '<':
|
||||||
return (v1 < v2) ? options.fn(this) : options.inverse(this);
|
return (v1 < v2) ? options.fn(this) : options.inverse(this);
|
||||||
case '<=':
|
case '<=':
|
||||||
return (v1 <= v2) ? options.fn(this) : options.inverse(this);
|
return (v1 <= v2) ? options.fn(this) : options.inverse(this);
|
||||||
case '>':
|
case '>':
|
||||||
return (v1 > v2) ? options.fn(this) : options.inverse(this);
|
return (v1 > v2) ? options.fn(this) : options.inverse(this);
|
||||||
case '>=':
|
case '>=':
|
||||||
return (v1 >= v2) ? options.fn(this) : options.inverse(this);
|
return (v1 >= v2) ? options.fn(this) : options.inverse(this);
|
||||||
case '&&':
|
case '&&':
|
||||||
return (v1 && v2) ? options.fn(this) : options.inverse(this);
|
return (v1 && v2) ? options.fn(this) : options.inverse(this);
|
||||||
case '||':
|
case '||':
|
||||||
return (v1 || v2) ? options.fn(this) : options.inverse(this);
|
return (v1 || v2) ? options.fn(this) : options.inverse(this);
|
||||||
case 'in':
|
case 'in':
|
||||||
var values = v2.split(',');
|
return (v2.indexOf(v1) !== -1) ? options.fn(this) : options.inverse(this);
|
||||||
return (v2.indexOf(v1) !== -1) ? options.fn(this) : options.inverse(this);
|
default:
|
||||||
default:
|
return options.inverse(this);
|
||||||
return options.inverse(this);
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Handlebars.registerHelper('nl2br', function(value, block) {
|
Handlebars.registerHelper('nl2br', function (value) {
|
||||||
return value.gsub("\n", "<br />");
|
return value.gsub('\n', '<br />');
|
||||||
});
|
});
|
||||||
|
|
||||||
Handlebars.registerHelper('json_encode', function(value, block) {
|
Handlebars.registerHelper('json_encode', function (value) {
|
||||||
return JSON.stringify(value);
|
return JSON.stringify(value);
|
||||||
});
|
});
|
||||||
|
|
||||||
Handlebars.registerHelper('json_decode', function(value, block) {
|
Handlebars.registerHelper('json_decode', function (value) {
|
||||||
return JSON.parse(value);
|
return JSON.parse(value);
|
||||||
});
|
});
|
||||||
Handlebars.registerHelper('url', function(value, block) {
|
Handlebars.registerHelper('url', function (value) {
|
||||||
var url = window.location.protocol + "//" + window.location.host + window.location.pathname;
|
var url = window.location.protocol + '//' + window.location.host + window.location.pathname;
|
||||||
|
|
||||||
return url + value;
|
return url + value;
|
||||||
});
|
});
|
||||||
Handlebars.registerHelper('emailFromMailto', function(value) {
|
Handlebars.registerHelper('emailFromMailto', function (value) {
|
||||||
var mailtoMatchingRegex = /^mailto\:/i;
|
var mailtoMatchingRegex = /^mailto\:/i;
|
||||||
if (typeof value === 'string' && value.match(mailtoMatchingRegex)) {
|
if (typeof value === 'string' && value.match(mailtoMatchingRegex)) {
|
||||||
return value.replace(mailtoMatchingRegex, '');
|
return value.replace(mailtoMatchingRegex, '');
|
||||||
} else {
|
} else {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Handlebars.registerHelper('lookup', function(obj, field, options) {
|
Handlebars.registerHelper('lookup', function (obj, field) {
|
||||||
return obj && obj[field];
|
return obj && obj[field];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
Handlebars.registerHelper('rsa_key', function(value, block) {
|
Handlebars.registerHelper('rsa_key', function (value) {
|
||||||
// extract all lines into an array
|
var lines;
|
||||||
if(value === undefined) return '';
|
// extract all lines into an array
|
||||||
|
if (value === undefined) return '';
|
||||||
|
|
||||||
var lines = value.trim().split("\n");
|
lines = value.trim().split('\n');
|
||||||
|
|
||||||
// remove header & footer
|
// remove header & footer
|
||||||
lines.shift();
|
lines.shift();
|
||||||
lines.pop();
|
lines.pop();
|
||||||
|
|
||||||
// return concatenated lines
|
// return concatenated lines
|
||||||
return lines.join('');
|
return lines.join('');
|
||||||
});
|
});
|
||||||
|
|
||||||
Handlebars.registerHelper('trim', function(value, block) {
|
Handlebars.registerHelper('trim', function (value) {
|
||||||
if(value === null || value === undefined) return '';
|
if (value === null || value === undefined) return '';
|
||||||
return value.trim();
|
return value.trim();
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -125,32 +127,33 @@ define('handlebars_helpers', ['handlebars'], function(Handlebars) {
|
|||||||
* @return {String} The truncated string.
|
* @return {String} The truncated string.
|
||||||
*/
|
*/
|
||||||
Handlebars.registerHelper('ellipsis', function (str, limit, append) {
|
Handlebars.registerHelper('ellipsis', function (str, limit, append) {
|
||||||
if (append === undefined) {
|
var strAppend = append;
|
||||||
append = '';
|
var sanitized = str.replace(/(<([^>]+)>)/g, '');
|
||||||
}
|
if (strAppend === undefined) {
|
||||||
var sanitized = str.replace(/(<([^>]+)>)/g, '');
|
strAppend = '';
|
||||||
if (sanitized.length > limit) {
|
}
|
||||||
return sanitized.substr(0, limit - append.length) + append;
|
if (sanitized.length > limit) {
|
||||||
} else {
|
return sanitized.substr(0, limit - strAppend.length) + strAppend;
|
||||||
return sanitized;
|
} else {
|
||||||
}
|
return sanitized;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Handlebars.registerHelper('getNumber', function (string) {
|
Handlebars.registerHelper('getNumber', function (string) {
|
||||||
return parseInt(string, 10);
|
return parseInt(string, 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
Handlebars.registerHelper('fontWithFallback', function(font) {
|
Handlebars.registerHelper('fontWithFallback', function (font) {
|
||||||
switch(font) {
|
switch (font) {
|
||||||
case 'Arial': return new Handlebars.SafeString("Arial, 'Helvetica Neue', Helvetica, sans-serif");
|
case 'Arial': return new Handlebars.SafeString("Arial, 'Helvetica Neue', Helvetica, sans-serif");
|
||||||
case 'Comic Sans MS': return new Handlebars.SafeString("'Comic Sans MS', 'Marker Felt-Thin', Arial, sans-serif");
|
case 'Comic Sans MS': return new Handlebars.SafeString("'Comic Sans MS', 'Marker Felt-Thin', Arial, sans-serif");
|
||||||
case 'Courier New': return new Handlebars.SafeString("'Courier New', Courier, 'Lucida Sans Typewriter', 'Lucida Typewriter', monospace");
|
case 'Courier New': return new Handlebars.SafeString("'Courier New', Courier, 'Lucida Sans Typewriter', 'Lucida Typewriter', monospace");
|
||||||
case 'Georgia': return new Handlebars.SafeString("Georgia, Times, 'Times New Roman', serif");
|
case 'Georgia': return new Handlebars.SafeString("Georgia, Times, 'Times New Roman', serif");
|
||||||
case 'Lucida': return new Handlebars.SafeString("'Lucida Sans Unicode', 'Lucida Grande', sans-serif");
|
case 'Lucida': return new Handlebars.SafeString("'Lucida Sans Unicode', 'Lucida Grande', sans-serif");
|
||||||
case 'Tahoma': return new Handlebars.SafeString("Tahoma, Verdana, Segoe, sans-serif");
|
case 'Tahoma': return new Handlebars.SafeString('Tahoma, Verdana, Segoe, sans-serif');
|
||||||
case 'Times New Roman': return new Handlebars.SafeString("'Times New Roman', Times, Baskerville, Georgia, serif");
|
case 'Times New Roman': return new Handlebars.SafeString("'Times New Roman', Times, Baskerville, Georgia, serif");
|
||||||
case 'Trebuchet MS': return new Handlebars.SafeString("'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif");
|
case 'Trebuchet MS': return new Handlebars.SafeString("'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif");
|
||||||
case 'Verdana': return new Handlebars.SafeString("Verdana, Geneva, sans-serif");
|
case 'Verdana': return new Handlebars.SafeString('Verdana, Geneva, sans-serif');
|
||||||
default: return font;
|
default: return font;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
22
assets/js/src/help-tooltip.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
define('helpTooltip', ['mailpoet', 'react', 'react-dom', 'help-tooltip.jsx'],
|
||||||
|
function (mp, React, ReactDOM, TooltipComponent) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var MailPoet = mp;
|
||||||
|
|
||||||
|
MailPoet.helpTooltip = {
|
||||||
|
show: function (domContainerNode, opts) {
|
||||||
|
|
||||||
|
ReactDOM.render(React.createElement(
|
||||||
|
TooltipComponent, {
|
||||||
|
tooltip: opts.tooltip,
|
||||||
|
tooltipId: opts.tooltipId,
|
||||||
|
place: opts.place
|
||||||
|
}
|
||||||
|
), domContainerNode);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
63
assets/js/src/help-tooltip.jsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactTooltip from 'react-tooltip';
|
||||||
|
import ReactHtmlParser from 'react-html-parser';
|
||||||
|
|
||||||
|
function Tooltip(props) {
|
||||||
|
let tooltipId = props.tooltipId;
|
||||||
|
let tooltip = props.tooltip;
|
||||||
|
// tooltip ID must be unique, defaults to tooltip text
|
||||||
|
if (!props.tooltipId && typeof props.tooltip === 'string') {
|
||||||
|
tooltipId = props.tooltip;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof props.tooltip === 'string') {
|
||||||
|
tooltip = (<span
|
||||||
|
style={{
|
||||||
|
pointerEvents: 'all',
|
||||||
|
maxWidth: '400',
|
||||||
|
display: 'inline-block',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ReactHtmlParser(props.tooltip)}
|
||||||
|
</span>);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className={props.className}>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
cursor: 'pointer',
|
||||||
|
}}
|
||||||
|
className="tooltip dashicons dashicons-editor-help"
|
||||||
|
data-event="click"
|
||||||
|
data-tip
|
||||||
|
data-for={tooltipId}
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<ReactTooltip
|
||||||
|
globalEventOff="click"
|
||||||
|
multiline={true}
|
||||||
|
id={tooltipId}
|
||||||
|
efect="solid"
|
||||||
|
place={props.place}
|
||||||
|
>
|
||||||
|
{tooltip}
|
||||||
|
</ReactTooltip>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Tooltip.propTypes = {
|
||||||
|
tooltipId: React.PropTypes.string,
|
||||||
|
tooltip: React.PropTypes.node.isRequired,
|
||||||
|
place: React.PropTypes.string,
|
||||||
|
className: React.PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
Tooltip.defaultProps = {
|
||||||
|
tooltipId: undefined,
|
||||||
|
place: undefined,
|
||||||
|
className: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = Tooltip;
|
32
assets/js/src/help/help.jsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import { Router, Route, IndexRedirect, useRouterHistory } from 'react-router';
|
||||||
|
import { createHashHistory } from 'history';
|
||||||
|
|
||||||
|
import KnowledgeBase from 'help/knowledge_base.jsx';
|
||||||
|
import SystemInfo from 'help/system_info.jsx';
|
||||||
|
|
||||||
|
const history = useRouterHistory(createHashHistory)({ queryKey: false });
|
||||||
|
|
||||||
|
const App = React.createClass({
|
||||||
|
render() {
|
||||||
|
return this.props.children;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const container = document.getElementById('help_container');
|
||||||
|
|
||||||
|
if (container) {
|
||||||
|
|
||||||
|
ReactDOM.render((
|
||||||
|
<Router history={history}>
|
||||||
|
<Route path="/" component={App}>
|
||||||
|
<IndexRedirect to="knowledgeBase" />
|
||||||
|
{/* Pages */}
|
||||||
|
<Route path="knowledgeBase(/)**" params={{ tab: 'knowledgeBase' }} component={KnowledgeBase} />
|
||||||
|
<Route path="systemInfo(/)**" params={{ tab: 'systemInfo' }} component={SystemInfo} />
|
||||||
|
</Route>
|
||||||
|
</Router>
|
||||||
|
), container);
|
||||||
|
|
||||||
|
}
|
29
assets/js/src/help/knowledge_base.jsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import MailPoet from 'mailpoet';
|
||||||
|
|
||||||
|
import Tabs from './tabs.jsx';
|
||||||
|
|
||||||
|
function KnowledgeBase() {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<Tabs tab="knowledgeBase" />
|
||||||
|
|
||||||
|
<p>{MailPoet.I18n.t('knowledgeBaseIntro')}</p>
|
||||||
|
<ul>
|
||||||
|
<li><a target="_blank" href="http://beta.docs.mailpoet.com/category/116-common-problems">Common Problems</a></li>
|
||||||
|
<li><a target="_blank" href="http://beta.docs.mailpoet.com/category/165-newsletters">Newsletters</a></li>
|
||||||
|
<li><a target="_blank" href="http://beta.docs.mailpoet.com/category/156-migration-questions">Migration Questions</a></li>
|
||||||
|
<li><a target="_blank" href="http://beta.docs.mailpoet.com/category/149-sending-methods">Sending Methods</a></li>
|
||||||
|
<li><a target="_blank" href="http://beta.docs.mailpoet.com/category/139-subscription-forms">Subscription Forms</a></li>
|
||||||
|
<li><a target="_blank" href="http://beta.docs.mailpoet.com/category/114-getting-started">Getting Started</a></li>
|
||||||
|
<li><a target="_blank" href="http://beta.docs.mailpoet.com/category/123-newsletter-designer">Newsletter Designer</a></li>
|
||||||
|
<li><a target="_blank" href="http://beta.docs.mailpoet.com/category/121-subscribers-and-lists">Subscribers and Lists</a></li>
|
||||||
|
</ul>
|
||||||
|
<a target="_blank" href="http://beta.docs.mailpoet.com/" className="button button-primary">{MailPoet.I18n.t('knowledgeBaseButton')}</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = KnowledgeBase;
|
47
assets/js/src/help/system_info.jsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import MailPoet from 'mailpoet';
|
||||||
|
import _ from 'underscore';
|
||||||
|
|
||||||
|
import Tabs from './tabs.jsx';
|
||||||
|
|
||||||
|
function handleFocus(event) {
|
||||||
|
event.target.select();
|
||||||
|
}
|
||||||
|
|
||||||
|
function printData(data) {
|
||||||
|
if (_.isObject(data)) {
|
||||||
|
const printableData = Object.keys(data).map((key) => {
|
||||||
|
return `${key}: ${data[key]}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
return (<textarea
|
||||||
|
readOnly={true}
|
||||||
|
onFocus={handleFocus}
|
||||||
|
value={printableData.join('\n')}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
height: '400px',
|
||||||
|
}}
|
||||||
|
/>);
|
||||||
|
} else {
|
||||||
|
return (<p>{MailPoet.I18n.t('systemInfoDataError')}</p>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function KnowledgeBase() {
|
||||||
|
const data = window.help_scout_data;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<Tabs tab="systemInfo" />
|
||||||
|
|
||||||
|
<div className="mailpoet_notice notice inline notice-success" style={{ marginTop: '1em' }}>
|
||||||
|
<p>{MailPoet.I18n.t('systemInfoIntro')}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{printData(data)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = KnowledgeBase;
|
46
assets/js/src/help/tabs.jsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Link } from 'react-router';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import MailPoet from 'mailpoet';
|
||||||
|
|
||||||
|
const tabs = [
|
||||||
|
{
|
||||||
|
name: 'knowledgeBase',
|
||||||
|
label: MailPoet.I18n.t('tabKnowledgeBaseTitle'),
|
||||||
|
link: '/knowledgeBase',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'systemInfo',
|
||||||
|
label: MailPoet.I18n.t('tabSystemInfoTitle'),
|
||||||
|
link: '/systemInfo',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function Tabs(props) {
|
||||||
|
|
||||||
|
const tabLinks = tabs.map((tab, index) => {
|
||||||
|
const tabClasses = classNames(
|
||||||
|
'nav-tab',
|
||||||
|
{ 'nav-tab-active': (props.tab === tab.name) }
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={'tab-' + index}
|
||||||
|
className={tabClasses}
|
||||||
|
to={tab.link}
|
||||||
|
>{ tab.label }</Link>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<h2 className="nav-tab-wrapper">
|
||||||
|
{ tabLinks }
|
||||||
|
</h2>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Tabs.propTypes = { tab: React.PropTypes.string };
|
||||||
|
Tabs.defaultProps = { tab: 'knowledgeBase' };
|
||||||
|
|
||||||
|
module.exports = Tabs;
|
@ -1,3 +0,0 @@
|
|||||||
define([], function() {
|
|
||||||
!function(e,o,n){window.HSCW=o,window.HS=n,n.beacon=n.beacon||{};var t=n.beacon;t.userConfig={},t.readyQueue=[],t.config=function(e){this.userConfig=e},t.ready=function(e){this.readyQueue.push(e)},o.config={docs:{enabled:!0,baseUrl:"//mailpoet3.helpscoutdocs.com/"},contact:{enabled:!0,formId:"aa21ca80-a4f5-11e6-91aa-0a5fecc78a4d"}};var r=e.getElementsByTagName("script")[0],c=e.createElement("script");c.type="text/javascript",c.async=!0,c.src="https://djtflbt20bdde.cloudfront.net/",r.parentNode.insertBefore(c,r)}(document,window.HSCW||{},window.HS||{});
|
|
||||||
});
|
|
@ -1,23 +1,25 @@
|
|||||||
define('i18n',
|
define('i18n',
|
||||||
[
|
[
|
||||||
'mailpoet'
|
'mailpoet'
|
||||||
], function(
|
], function (
|
||||||
MailPoet
|
mp
|
||||||
) {
|
) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var translations = {};
|
var MailPoet = mp;
|
||||||
|
|
||||||
MailPoet.I18n = {
|
var translations = {};
|
||||||
add: function(key, value) {
|
|
||||||
translations[key] = value;
|
|
||||||
},
|
|
||||||
t: function(key) {
|
|
||||||
return translations[key] || 'TRANSLATION "%$1s" NOT FOUND'.replace("%$1s", key);
|
|
||||||
},
|
|
||||||
all: function() {
|
|
||||||
return translations;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
});
|
MailPoet.I18n = {
|
||||||
|
add: function (key, value) {
|
||||||
|
translations[key] = value;
|
||||||
|
},
|
||||||
|
t: function (key) {
|
||||||
|
return translations[key] || 'TRANSLATION "%$1s" NOT FOUND'.replace('%$1s', key);
|
||||||
|
},
|
||||||
|
all: function () {
|
||||||
|
return translations;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
@ -1,21 +1,24 @@
|
|||||||
define('iframe', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
|
define('iframe', ['mailpoet'], function (mp) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var MailPoet = mp;
|
||||||
MailPoet.Iframe = {
|
MailPoet.Iframe = {
|
||||||
marginY: 20,
|
marginY: 20,
|
||||||
autoSize: function(iframe) {
|
autoSize: function (iframe) {
|
||||||
if(!iframe) return;
|
if (!iframe) return;
|
||||||
|
|
||||||
this.setSize(
|
this.setSize(
|
||||||
iframe,
|
iframe,
|
||||||
iframe.contentWindow.document.body.scrollHeight
|
iframe.contentWindow.document.body.scrollHeight
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
setSize: function(iframe, i) {
|
setSize: function (sizeIframe, i) {
|
||||||
if(!iframe) return;
|
var iframe = sizeIframe;
|
||||||
|
if (!iframe) return;
|
||||||
|
|
||||||
iframe.style.height = (
|
iframe.style.height = (
|
||||||
parseInt(i) + this.marginY
|
parseInt(i, 10) + this.marginY
|
||||||
) + "px";
|
) + 'px';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,9 +2,10 @@ define(
|
|||||||
[
|
[
|
||||||
'jquery'
|
'jquery'
|
||||||
],
|
],
|
||||||
function(
|
function (
|
||||||
$
|
jQuery
|
||||||
) {
|
) {
|
||||||
|
var $ = jQuery;
|
||||||
// Combination of jQuery.deparam and jQuery.serializeObject by Ben Alman.
|
// Combination of jQuery.deparam and jQuery.serializeObject by Ben Alman.
|
||||||
/*!
|
/*!
|
||||||
* jQuery BBQ: Back Button & Query Library - v1.2.1 - 2/17/2010
|
* jQuery BBQ: Back Button & Query Library - v1.2.1 - 2/17/2010
|
||||||
@ -22,31 +23,31 @@ define(
|
|||||||
* Dual licensed under the MIT and GPL licenses.
|
* Dual licensed under the MIT and GPL licenses.
|
||||||
* http://benalman.com/about/license/
|
* http://benalman.com/about/license/
|
||||||
*/
|
*/
|
||||||
$.fn.serializeObject = function(coerce) {
|
$.fn.serializeObject = function (coerce) {
|
||||||
var obj = {},
|
var obj = {};
|
||||||
coerce_types = { 'true': !0, 'false': !1, 'null': null };
|
var coerce_types = { true: !0, false: !1, null: null };
|
||||||
|
|
||||||
// Iterate over all name=value pairs.
|
// Iterate over all name=value pairs.
|
||||||
$.each( this.serializeArray(), function(j,v){
|
$.each(this.serializeArray(), function (j, v) {
|
||||||
var key = v.name,
|
var key = v.name;
|
||||||
val = v.value,
|
var val = v.value;
|
||||||
cur = obj,
|
var cur = obj;
|
||||||
i = 0,
|
var i = 0;
|
||||||
|
|
||||||
// If key is more complex than 'foo', like 'a[]' or 'a[b][c]', split it
|
// If key is more complex than 'foo', like 'a[]' or 'a[b][c]', split it
|
||||||
// into its component parts.
|
// into its component parts.
|
||||||
keys = key.split( '][' ),
|
var keys = key.split('][');
|
||||||
keys_last = keys.length - 1;
|
var keys_last = keys.length - 1;
|
||||||
|
|
||||||
// If the first keys part contains [ and the last ends with ], then []
|
// If the first keys part contains [ and the last ends with ], then []
|
||||||
// are correctly balanced.
|
// are correctly balanced.
|
||||||
if ( /\[/.test( keys[0] ) && /\]$/.test( keys[ keys_last ] ) ) {
|
if (/\[/.test(keys[0]) && /\]$/.test(keys[keys_last])) {
|
||||||
// Remove the trailing ] from the last keys part.
|
// Remove the trailing ] from the last keys part.
|
||||||
keys[ keys_last ] = keys[ keys_last ].replace( /\]$/, '' );
|
keys[keys_last] = keys[keys_last].replace(/\]$/, '');
|
||||||
|
|
||||||
// Split first keys part into two parts on the [ and add them back onto
|
// Split first keys part into two parts on the [ and add them back onto
|
||||||
// the beginning of the keys array.
|
// the beginning of the keys array.
|
||||||
keys = keys.shift().split('[').concat( keys );
|
keys = keys.shift().split('[').concat(keys);
|
||||||
|
|
||||||
keys_last = keys.length - 1;
|
keys_last = keys.length - 1;
|
||||||
} else {
|
} else {
|
||||||
@ -55,14 +56,14 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Coerce values.
|
// Coerce values.
|
||||||
if ( coerce ) {
|
if (coerce) {
|
||||||
val = val && !isNaN(val) ? +val // number
|
val = val && !isNaN(val) ? +val // number
|
||||||
: val === 'undefined' ? undefined // undefined
|
: val === 'undefined' ? undefined // undefined
|
||||||
: coerce_types[val] !== undefined ? coerce_types[val] // true, false, null
|
: coerce_types[val] !== undefined ? coerce_types[val] // true, false, null
|
||||||
: val; // string
|
: val; // string
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( keys_last ) {
|
if (keys_last) {
|
||||||
// Complex key, build deep object structure based on a few rules:
|
// Complex key, build deep object structure based on a few rules:
|
||||||
// * The 'cur' pointer starts at the object top-level.
|
// * The 'cur' pointer starts at the object top-level.
|
||||||
// * [] = array push (n is set to array length), [n] = array if n is
|
// * [] = array push (n is set to array length), [n] = array if n is
|
||||||
@ -72,25 +73,26 @@ define(
|
|||||||
// object or array based on the type of the next keys part.
|
// object or array based on the type of the next keys part.
|
||||||
// * Move the 'cur' pointer to the next level.
|
// * Move the 'cur' pointer to the next level.
|
||||||
// * Rinse & repeat.
|
// * Rinse & repeat.
|
||||||
for ( ; i <= keys_last; i++ ) {
|
for (; i <= keys_last; i++) {
|
||||||
key = keys[i] === '' ? cur.length : keys[i];
|
key = keys[i] === '' ? cur.length : keys[i];
|
||||||
cur = cur[key] = i < keys_last
|
cur[key] = i < keys_last
|
||||||
? cur[key] || ( keys[i+1] && isNaN( keys[i+1] ) ? {} : [] )
|
? cur[key] || (keys[i + 1] && isNaN(keys[i + 1]) ? {} : [])
|
||||||
: val;
|
: val;
|
||||||
|
cur = cur[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Simple key, even simpler rules, since only scalars and shallow
|
// Simple key, even simpler rules, since only scalars and shallow
|
||||||
// arrays are allowed.
|
// arrays are allowed.
|
||||||
|
|
||||||
if ( $.isArray( obj[key] ) ) {
|
if ($.isArray(obj[key])) {
|
||||||
// val is already an array, so push on the next value.
|
// val is already an array, so push on the next value.
|
||||||
obj[key].push( val );
|
obj[key].push(val);
|
||||||
|
|
||||||
} else if ( obj[key] !== undefined ) {
|
} else if (obj[key] !== undefined) {
|
||||||
// val isn't an array, but since a second value has been specified,
|
// val isn't an array, but since a second value has been specified,
|
||||||
// convert val into an array.
|
// convert val into an array.
|
||||||
obj[key] = [ obj[key], val ];
|
obj[key] = [obj[key], val];
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// val is a scalar.
|
// val is a scalar.
|
||||||
@ -104,4 +106,4 @@ define(
|
|||||||
|
|
||||||
return $;
|
return $;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -1,84 +1,84 @@
|
|||||||
define([
|
define([
|
||||||
'react',
|
'react',
|
||||||
'mailpoet'
|
'mailpoet',
|
||||||
],
|
],
|
||||||
function(
|
(
|
||||||
React,
|
React,
|
||||||
MailPoet
|
MailPoet
|
||||||
) {
|
) => {
|
||||||
var ListingBulkActions = React.createClass({
|
const ListingBulkActions = React.createClass({
|
||||||
getInitialState: function() {
|
getInitialState: function () {
|
||||||
return {
|
return {
|
||||||
action: false,
|
action: false,
|
||||||
extra: false
|
extra: false,
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
handleChangeAction: function(e) {
|
handleChangeAction: function (e) {
|
||||||
this.setState({
|
this.setState({
|
||||||
action: e.target.value,
|
action: e.target.value,
|
||||||
extra: false
|
extra: false,
|
||||||
}, function() {
|
}, () => {
|
||||||
var action = this.getSelectedAction();
|
const action = this.getSelectedAction();
|
||||||
|
|
||||||
// action on select callback
|
// action on select callback
|
||||||
if(action !== null && action['onSelect'] !== undefined) {
|
if (action !== null && action['onSelect'] !== undefined) {
|
||||||
this.setState({
|
this.setState({
|
||||||
extra: action.onSelect(e)
|
extra: action.onSelect(e),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}.bind(this));
|
});
|
||||||
},
|
},
|
||||||
handleApplyAction: function(e) {
|
handleApplyAction: function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
var action = this.getSelectedAction();
|
const action = this.getSelectedAction();
|
||||||
|
|
||||||
if(action === null) {
|
if (action === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var selected_ids = (this.props.selection !== 'all')
|
const selected_ids = (this.props.selection !== 'all')
|
||||||
? this.props.selected_ids
|
? this.props.selected_ids
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
var data = (action['getData'] !== undefined)
|
const data = (action['getData'] !== undefined)
|
||||||
? action.getData()
|
? action.getData()
|
||||||
: {};
|
: {};
|
||||||
|
|
||||||
data.action = this.state.action;
|
data.action = this.state.action;
|
||||||
|
|
||||||
var onSuccess = function() {};
|
let onSuccess = function () {};
|
||||||
if(action['onSuccess'] !== undefined) {
|
if (action['onSuccess'] !== undefined) {
|
||||||
onSuccess = action.onSuccess;
|
onSuccess = action.onSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(data.action) {
|
if (data.action) {
|
||||||
const promise = this.props.onBulkAction(selected_ids, data);
|
const promise = this.props.onBulkAction(selected_ids, data);
|
||||||
if (promise !== false) {
|
if (promise !== false) {
|
||||||
promise.then(onSuccess);
|
promise.then(onSuccess);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
action: false,
|
action: false,
|
||||||
extra: false
|
extra: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getSelectedAction: function() {
|
getSelectedAction: function () {
|
||||||
var selected_action = this.refs.action.value;
|
const selected_action = this.refs.action.value;
|
||||||
if(selected_action.length > 0) {
|
if (selected_action.length > 0) {
|
||||||
var action = this.props.bulk_actions.filter(function(action) {
|
const action = this.props.bulk_actions.filter((action) => {
|
||||||
return (action.name === selected_action);
|
return (action.name === selected_action);
|
||||||
});
|
});
|
||||||
|
|
||||||
if(action.length > 0) {
|
if (action.length > 0) {
|
||||||
return action[0];
|
return action[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function () {
|
||||||
if(this.props.bulk_actions.length === 0) {
|
if (this.props.bulk_actions.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,29 +93,29 @@ function(
|
|||||||
<select
|
<select
|
||||||
name="bulk_actions"
|
name="bulk_actions"
|
||||||
ref="action"
|
ref="action"
|
||||||
value={ this.state.action }
|
value={this.state.action}
|
||||||
onChange={this.handleChangeAction}
|
onChange={this.handleChangeAction}
|
||||||
>
|
>
|
||||||
<option value="">{MailPoet.I18n.t('bulkActions')}</option>
|
<option value="">{MailPoet.I18n.t('bulkActions')}</option>
|
||||||
{ this.props.bulk_actions.map(function(action, index) {
|
{ this.props.bulk_actions.map((action, index) => {
|
||||||
return (
|
return (
|
||||||
<option
|
<option
|
||||||
value={ action.name }
|
value={action.name}
|
||||||
key={ 'action-' + index }
|
key={'action-' + index}
|
||||||
>{ action.label }</option>
|
>{ action.label }</option>
|
||||||
);
|
);
|
||||||
}.bind(this)) }
|
}) }
|
||||||
</select>
|
</select>
|
||||||
<input
|
<input
|
||||||
onClick={ this.handleApplyAction }
|
onClick={this.handleApplyAction}
|
||||||
type="submit"
|
type="submit"
|
||||||
defaultValue={MailPoet.I18n.t('apply')}
|
defaultValue={MailPoet.I18n.t('apply')}
|
||||||
className="button action" />
|
className="button action" />
|
||||||
|
|
||||||
{ this.state.extra }
|
{ this.state.extra }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return ListingBulkActions;
|
return ListingBulkActions;
|
||||||
|
@ -1,27 +1,30 @@
|
|||||||
define([
|
define([
|
||||||
'react',
|
'react',
|
||||||
'jquery',
|
'jquery',
|
||||||
'mailpoet'
|
'mailpoet',
|
||||||
],
|
],
|
||||||
function(
|
(
|
||||||
React,
|
React,
|
||||||
jQuery,
|
jQuery,
|
||||||
MailPoet
|
MailPoet
|
||||||
) {
|
) => {
|
||||||
var ListingFilters = React.createClass({
|
const ListingFilters = React.createClass({
|
||||||
handleFilterAction: function() {
|
handleFilterAction: function () {
|
||||||
let filters = {};
|
const filters = {};
|
||||||
this.getAvailableFilters().map((filter, i) => {
|
this.getAvailableFilters().map((filter, i) => {
|
||||||
filters[this.refs['filter-'+i].name] = this.refs['filter-'+i].value
|
filters[this.refs['filter-' + i].name] = this.refs['filter-' + i].value;
|
||||||
});
|
});
|
||||||
|
if (this.props.onBeforeSelectFilter) {
|
||||||
|
this.props.onBeforeSelectFilter(filters);
|
||||||
|
}
|
||||||
return this.props.onSelectFilter(filters);
|
return this.props.onSelectFilter(filters);
|
||||||
},
|
},
|
||||||
handleEmptyTrash: function() {
|
handleEmptyTrash: function () {
|
||||||
return this.props.onEmptyTrash();
|
return this.props.onEmptyTrash();
|
||||||
},
|
},
|
||||||
getAvailableFilters: function() {
|
getAvailableFilters: function () {
|
||||||
let filters = this.props.filters;
|
const filters = this.props.filters;
|
||||||
return Object.keys(filters).filter(function(filter) {
|
return Object.keys(filters).filter((filter) => {
|
||||||
return !(
|
return !(
|
||||||
filters[filter].length === 0
|
filters[filter].length === 0
|
||||||
|| (
|
|| (
|
||||||
@ -31,39 +34,39 @@ function(
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
componentDidUpdate: function() {
|
componentDidUpdate: function () {
|
||||||
const selected_filters = this.props.filter;
|
const selected_filters = this.props.filter;
|
||||||
const available_filters = this.getAvailableFilters().map(
|
this.getAvailableFilters().map(
|
||||||
function(filter, i) {
|
(filter, i) => {
|
||||||
if (selected_filters[filter] !== undefined && selected_filters[filter]) {
|
if (selected_filters[filter] !== undefined && selected_filters[filter]) {
|
||||||
jQuery(this.refs['filter-'+i])
|
jQuery(this.refs['filter-' + i])
|
||||||
.val(selected_filters[filter])
|
.val(selected_filters[filter])
|
||||||
.trigger('change');
|
.trigger('change');
|
||||||
}
|
}
|
||||||
}.bind(this)
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function () {
|
||||||
const filters = this.props.filters;
|
const filters = this.props.filters;
|
||||||
const available_filters = this.getAvailableFilters()
|
const available_filters = this.getAvailableFilters()
|
||||||
.map(function(filter, i) {
|
.map((filter, i) => {
|
||||||
return (
|
return (
|
||||||
<select
|
<select
|
||||||
ref={ `filter-${i}` }
|
ref={`filter-${i}`}
|
||||||
key={ `filter-${i}` }
|
key={`filter-${i}`}
|
||||||
name={ filter }
|
name={filter}
|
||||||
>
|
>
|
||||||
{ filters[filter].map(function(option, j) {
|
{ filters[filter].map((option, j) => {
|
||||||
return (
|
return (
|
||||||
<option
|
<option
|
||||||
value={ option.value }
|
value={option.value}
|
||||||
key={ 'filter-option-' + j }
|
key={'filter-option-' + j}
|
||||||
>{ option.label }</option>
|
>{ option.label }</option>
|
||||||
);
|
);
|
||||||
}.bind(this)) }
|
}) }
|
||||||
</select>
|
</select>
|
||||||
);
|
);
|
||||||
}.bind(this));
|
});
|
||||||
|
|
||||||
let button;
|
let button;
|
||||||
|
|
||||||
@ -71,7 +74,7 @@ function(
|
|||||||
button = (
|
button = (
|
||||||
<input
|
<input
|
||||||
id="post-query-submit"
|
id="post-query-submit"
|
||||||
onClick={ this.handleFilterAction }
|
onClick={this.handleFilterAction}
|
||||||
type="submit"
|
type="submit"
|
||||||
defaultValue={MailPoet.I18n.t('filter')}
|
defaultValue={MailPoet.I18n.t('filter')}
|
||||||
className="button" />
|
className="button" />
|
||||||
@ -82,7 +85,7 @@ function(
|
|||||||
if (this.props.group === 'trash') {
|
if (this.props.group === 'trash') {
|
||||||
empty_trash = (
|
empty_trash = (
|
||||||
<input
|
<input
|
||||||
onClick={ this.handleEmptyTrash }
|
onClick={this.handleEmptyTrash}
|
||||||
type="submit"
|
type="submit"
|
||||||
value={MailPoet.I18n.t('emptyTrash')}
|
value={MailPoet.I18n.t('emptyTrash')}
|
||||||
className="button"
|
className="button"
|
||||||
@ -97,7 +100,7 @@ function(
|
|||||||
{ empty_trash }
|
{ empty_trash }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return ListingFilters;
|
return ListingFilters;
|
||||||
|
@ -1,40 +1,40 @@
|
|||||||
define(['react', 'classnames'], function(React, classNames) {
|
define(['react', 'classnames'], (React, classNames) => {
|
||||||
|
|
||||||
var ListingGroups = React.createClass({
|
const ListingGroups = React.createClass({
|
||||||
handleSelect: function(group) {
|
handleSelect: function (group) {
|
||||||
return this.props.onSelectGroup(group);
|
return this.props.onSelectGroup(group);
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function () {
|
||||||
var groups = this.props.groups.map(function(group, index) {
|
const groups = this.props.groups.map((group, index) => {
|
||||||
if(group.name === 'trash' && group.count === 0) {
|
if (group.name === 'trash' && group.count === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var classes = classNames(
|
const classes = classNames(
|
||||||
{ 'current' : (group.name === this.props.group) }
|
{ current: (group.name === this.props.group) }
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
|
||||||
<li key={index}>
|
|
||||||
{(index > 0) ? ' |' : ''}
|
|
||||||
<a
|
|
||||||
href="javascript:;"
|
|
||||||
className={classes}
|
|
||||||
onClick={this.handleSelect.bind(this, group.name)} >
|
|
||||||
{group.label} <span className="count">({ group.count.toLocaleString() })</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}.bind(this));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul className="subsubsub">
|
<li key={index}>
|
||||||
{ groups }
|
{(index > 0) ? ' |' : ''}
|
||||||
</ul>
|
<a
|
||||||
|
href="javascript:;"
|
||||||
|
className={classes}
|
||||||
|
onClick={this.handleSelect.bind(this, group.name)} >
|
||||||
|
{group.label} <span className="count">({ group.count.toLocaleString() })</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
return ListingGroups;
|
return (
|
||||||
}
|
<ul className="subsubsub">
|
||||||
);
|
{ groups }
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return ListingGroups;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
@ -1,31 +1,32 @@
|
|||||||
import MailPoet from 'mailpoet'
|
import MailPoet from 'mailpoet';
|
||||||
import React from 'react'
|
import React from 'react';
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames';
|
||||||
|
|
||||||
const ListingHeader = React.createClass({
|
const ListingHeader = React.createClass({
|
||||||
handleSelectItems: function() {
|
handleSelectItems: function () {
|
||||||
return this.props.onSelectItems(
|
return this.props.onSelectItems(
|
||||||
this.refs.toggle.checked
|
this.refs.toggle.checked
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function () {
|
||||||
const columns = this.props.columns.map(function(column, index) {
|
const columns = this.props.columns.map((column, index) => {
|
||||||
column.is_primary = (index === 0);
|
const renderColumn = column;
|
||||||
column.sorted = (this.props.sort_by === column.name)
|
renderColumn.is_primary = (index === 0);
|
||||||
|
renderColumn.sorted = (this.props.sort_by === column.name)
|
||||||
? this.props.sort_order
|
? this.props.sort_order
|
||||||
: 'desc';
|
: 'desc';
|
||||||
return (
|
return (
|
||||||
<ListingColumn
|
<ListingColumn
|
||||||
onSort={this.props.onSort}
|
onSort={this.props.onSort}
|
||||||
sort_by={this.props.sort_by}
|
sort_by={this.props.sort_by}
|
||||||
key={ 'column-' + index }
|
key={'column-' + index}
|
||||||
column={column} />
|
column={renderColumn} />
|
||||||
);
|
);
|
||||||
}.bind(this));
|
});
|
||||||
|
|
||||||
let checkbox;
|
let checkbox;
|
||||||
|
|
||||||
if(this.props.is_selectable === true) {
|
if (this.props.is_selectable === true) {
|
||||||
checkbox = (
|
checkbox = (
|
||||||
<th
|
<th
|
||||||
className="manage-column column-cb check-column">
|
className="manage-column column-cb check-column">
|
||||||
@ -36,8 +37,8 @@ const ListingHeader = React.createClass({
|
|||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="select_all"
|
name="select_all"
|
||||||
ref="toggle"
|
ref="toggle"
|
||||||
checked={ this.props.selection }
|
checked={this.props.selection}
|
||||||
onChange={ this.handleSelectItems } />
|
onChange={this.handleSelectItems} />
|
||||||
</th>
|
</th>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -48,28 +49,28 @@ const ListingHeader = React.createClass({
|
|||||||
{columns}
|
{columns}
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const ListingColumn = React.createClass({
|
const ListingColumn = React.createClass({
|
||||||
handleSort: function() {
|
handleSort: function () {
|
||||||
const sort_by = this.props.column.name;
|
const sort_by = this.props.column.name;
|
||||||
const sort_order = (this.props.column.sorted === 'asc') ? 'desc' : 'asc';
|
const sort_order = (this.props.column.sorted === 'asc') ? 'desc' : 'asc';
|
||||||
this.props.onSort(sort_by, sort_order);
|
this.props.onSort(sort_by, sort_order);
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function () {
|
||||||
const classes = classNames(
|
const classes = classNames(
|
||||||
'manage-column',
|
'manage-column',
|
||||||
{ 'column-primary': this.props.column.is_primary },
|
{ 'column-primary': this.props.column.is_primary },
|
||||||
{ 'sortable': this.props.column.sortable },
|
{ sortable: this.props.column.sortable },
|
||||||
this.props.column.sorted,
|
this.props.column.sorted,
|
||||||
{ 'sorted': (this.props.sort_by === this.props.column.name) }
|
{ sorted: (this.props.sort_by === this.props.column.name) }
|
||||||
);
|
);
|
||||||
let label;
|
let label;
|
||||||
|
|
||||||
if(this.props.column.sortable === true) {
|
if (this.props.column.sortable === true) {
|
||||||
label = (
|
label = (
|
||||||
<a onClick={ this.handleSort }>
|
<a onClick={this.handleSort}>
|
||||||
<span>{ this.props.column.label }</span>
|
<span>{ this.props.column.label }</span>
|
||||||
<span className="sorting-indicator"></span>
|
<span className="sorting-indicator"></span>
|
||||||
</a>
|
</a>
|
||||||
@ -79,13 +80,13 @@ const ListingColumn = React.createClass({
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<th
|
<th
|
||||||
className={ classes }
|
className={classes}
|
||||||
id={this.props.column.name }
|
id={this.props.column.name}
|
||||||
scope="col"
|
scope="col"
|
||||||
width={ this.props.column.width || null }
|
width={this.props.column.width || null}
|
||||||
>{label}</th>
|
>{label}</th>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = ListingHeader;
|
module.exports = ListingHeader;
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
import MailPoet from 'mailpoet'
|
import MailPoet from 'mailpoet';
|
||||||
import jQuery from 'jquery'
|
import jQuery from 'jquery';
|
||||||
import React from 'react'
|
import React from 'react';
|
||||||
import _ from 'underscore'
|
import _ from 'underscore';
|
||||||
import { Router, Link } from 'react-router'
|
import { Link } from 'react-router';
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames';
|
||||||
import ListingBulkActions from 'listing/bulk_actions.jsx'
|
import ListingBulkActions from 'listing/bulk_actions.jsx';
|
||||||
import ListingHeader from 'listing/header.jsx'
|
import ListingHeader from 'listing/header.jsx';
|
||||||
import ListingPages from 'listing/pages.jsx'
|
import ListingPages from 'listing/pages.jsx';
|
||||||
import ListingSearch from 'listing/search.jsx'
|
import ListingSearch from 'listing/search.jsx';
|
||||||
import ListingGroups from 'listing/groups.jsx'
|
import ListingGroups from 'listing/groups.jsx';
|
||||||
import ListingFilters from 'listing/filters.jsx'
|
import ListingFilters from 'listing/filters.jsx';
|
||||||
|
|
||||||
const ListingItem = React.createClass({
|
const ListingItem = React.createClass({
|
||||||
getInitialState: function() {
|
getInitialState: function () {
|
||||||
return {
|
return {
|
||||||
expanded: false
|
expanded: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
handleSelectItem: function(e) {
|
handleSelectItem: function (e) {
|
||||||
this.props.onSelectItem(
|
this.props.onSelectItem(
|
||||||
parseInt(e.target.value, 10),
|
parseInt(e.target.value, 10),
|
||||||
e.target.checked
|
e.target.checked
|
||||||
@ -25,20 +25,20 @@ const ListingItem = React.createClass({
|
|||||||
|
|
||||||
return !e.target.checked;
|
return !e.target.checked;
|
||||||
},
|
},
|
||||||
handleRestoreItem: function(id) {
|
handleRestoreItem: function (id) {
|
||||||
this.props.onRestoreItem(id);
|
this.props.onRestoreItem(id);
|
||||||
},
|
},
|
||||||
handleTrashItem: function(id) {
|
handleTrashItem: function (id) {
|
||||||
this.props.onTrashItem(id);
|
this.props.onTrashItem(id);
|
||||||
},
|
},
|
||||||
handleDeleteItem: function(id) {
|
handleDeleteItem: function (id) {
|
||||||
this.props.onDeleteItem(id);
|
this.props.onDeleteItem(id);
|
||||||
},
|
},
|
||||||
handleToggleItem: function(id) {
|
handleToggleItem: function () {
|
||||||
this.setState({ expanded: !this.state.expanded });
|
this.setState({ expanded: !this.state.expanded });
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function () {
|
||||||
var checkbox = false;
|
let checkbox = false;
|
||||||
|
|
||||||
if (this.props.is_selectable === true) {
|
if (this.props.is_selectable === true) {
|
||||||
checkbox = (
|
checkbox = (
|
||||||
@ -48,12 +48,12 @@ const ListingItem = React.createClass({
|
|||||||
}</label>
|
}</label>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
value={ this.props.item.id }
|
value={this.props.item.id}
|
||||||
checked={
|
checked={
|
||||||
this.props.item.selected || this.props.selection === 'all'
|
this.props.item.selected || this.props.selection === 'all'
|
||||||
}
|
}
|
||||||
onChange={ this.handleSelectItem }
|
onChange={this.handleSelectItem}
|
||||||
disabled={ this.props.selection === 'all' } />
|
disabled={this.props.selection === 'all'} />
|
||||||
</th>
|
</th>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -63,7 +63,7 @@ const ListingItem = React.createClass({
|
|||||||
|
|
||||||
if (custom_actions.length > 0) {
|
if (custom_actions.length > 0) {
|
||||||
let is_first = true;
|
let is_first = true;
|
||||||
item_actions = custom_actions.map(function(action, index) {
|
item_actions = custom_actions.map((action, index) => {
|
||||||
if (action.display !== undefined) {
|
if (action.display !== undefined) {
|
||||||
if (action.display(this.props.item) === false) {
|
if (action.display(this.props.item) === false) {
|
||||||
return;
|
return;
|
||||||
@ -74,14 +74,14 @@ const ListingItem = React.createClass({
|
|||||||
|
|
||||||
if (action.name === 'trash') {
|
if (action.name === 'trash') {
|
||||||
custom_action = (
|
custom_action = (
|
||||||
<span key={ 'action-'+index } className="trash">
|
<span key={'action-' + index} className="trash">
|
||||||
{(!is_first) ? ' | ' : ''}
|
{(!is_first) ? ' | ' : ''}
|
||||||
<a
|
<a
|
||||||
href="javascript:;"
|
href="javascript:;"
|
||||||
onClick={ this.handleTrashItem.bind(
|
onClick={this.handleTrashItem.bind(
|
||||||
null,
|
null,
|
||||||
this.props.item.id
|
this.props.item.id
|
||||||
) }>
|
)}>
|
||||||
{MailPoet.I18n.t('moveToTrash')}
|
{MailPoet.I18n.t('moveToTrash')}
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
@ -89,8 +89,8 @@ const ListingItem = React.createClass({
|
|||||||
} else if (action.refresh) {
|
} else if (action.refresh) {
|
||||||
custom_action = (
|
custom_action = (
|
||||||
<span
|
<span
|
||||||
onClick={ this.props.onRefreshItems }
|
onClick={this.props.onRefreshItems}
|
||||||
key={ 'action-'+index } className={ action.name }>
|
key={'action-' + index} className={action.name}>
|
||||||
{(!is_first) ? ' | ' : ''}
|
{(!is_first) ? ' | ' : ''}
|
||||||
{ action.link(this.props.item) }
|
{ action.link(this.props.item) }
|
||||||
</span>
|
</span>
|
||||||
@ -98,7 +98,7 @@ const ListingItem = React.createClass({
|
|||||||
} else if (action.link) {
|
} else if (action.link) {
|
||||||
custom_action = (
|
custom_action = (
|
||||||
<span
|
<span
|
||||||
key={ 'action-'+index } className={ action.name }>
|
key={'action-' + index} className={action.name}>
|
||||||
{(!is_first) ? ' | ' : ''}
|
{(!is_first) ? ' | ' : ''}
|
||||||
{ action.link(this.props.item) }
|
{ action.link(this.props.item) }
|
||||||
</span>
|
</span>
|
||||||
@ -106,7 +106,7 @@ const ListingItem = React.createClass({
|
|||||||
} else {
|
} else {
|
||||||
custom_action = (
|
custom_action = (
|
||||||
<span
|
<span
|
||||||
key={ 'action-'+index } className={ action.name }>
|
key={'action-' + index} className={action.name}>
|
||||||
{(!is_first) ? ' | ' : ''}
|
{(!is_first) ? ' | ' : ''}
|
||||||
<a href="javascript:;" onClick={
|
<a href="javascript:;" onClick={
|
||||||
(action.onClick !== undefined)
|
(action.onClick !== undefined)
|
||||||
@ -125,11 +125,11 @@ const ListingItem = React.createClass({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return custom_action;
|
return custom_action;
|
||||||
}.bind(this));
|
});
|
||||||
} else {
|
} else {
|
||||||
item_actions = (
|
item_actions = (
|
||||||
<span className="edit">
|
<span className="edit">
|
||||||
<Link to={ `/edit/${ this.props.item.id }` }>{MailPoet.I18n.t('edit')}</Link>
|
<Link to={`/edit/${this.props.item.id}`}>{MailPoet.I18n.t('edit')}</Link>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -137,13 +137,13 @@ const ListingItem = React.createClass({
|
|||||||
let actions;
|
let actions;
|
||||||
|
|
||||||
if (this.props.group === 'trash') {
|
if (this.props.group === 'trash') {
|
||||||
actions = (
|
actions = (
|
||||||
<div>
|
<div>
|
||||||
<div className="row-actions">
|
<div className="row-actions">
|
||||||
<span>
|
<span>
|
||||||
<a
|
<a
|
||||||
href="javascript:;"
|
href="javascript:;"
|
||||||
onClick={ this.handleRestoreItem.bind(
|
onClick={this.handleRestoreItem.bind(
|
||||||
null,
|
null,
|
||||||
this.props.item.id
|
this.props.item.id
|
||||||
)}
|
)}
|
||||||
@ -154,7 +154,7 @@ const ListingItem = React.createClass({
|
|||||||
<a
|
<a
|
||||||
className="submitdelete"
|
className="submitdelete"
|
||||||
href="javascript:;"
|
href="javascript:;"
|
||||||
onClick={ this.handleDeleteItem.bind(
|
onClick={this.handleDeleteItem.bind(
|
||||||
null,
|
null,
|
||||||
this.props.item.id
|
this.props.item.id
|
||||||
)}
|
)}
|
||||||
@ -162,7 +162,7 @@ const ListingItem = React.createClass({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={ this.handleToggleItem.bind(null, this.props.item.id) }
|
onClick={this.handleToggleItem.bind(null, this.props.item.id)}
|
||||||
className="toggle-row" type="button">
|
className="toggle-row" type="button">
|
||||||
<span className="screen-reader-text">{MailPoet.I18n.t('showMoreDetails')}</span>
|
<span className="screen-reader-text">{MailPoet.I18n.t('showMoreDetails')}</span>
|
||||||
</button>
|
</button>
|
||||||
@ -175,7 +175,7 @@ const ListingItem = React.createClass({
|
|||||||
{ item_actions }
|
{ item_actions }
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={ this.handleToggleItem.bind(null, this.props.item.id) }
|
onClick={this.handleToggleItem.bind(null, this.props.item.id)}
|
||||||
className="toggle-row" type="button">
|
className="toggle-row" type="button">
|
||||||
<span className="screen-reader-text">{MailPoet.I18n.t('showMoreDetails')}</span>
|
<span className="screen-reader-text">{MailPoet.I18n.t('showMoreDetails')}</span>
|
||||||
</button>
|
</button>
|
||||||
@ -186,18 +186,29 @@ const ListingItem = React.createClass({
|
|||||||
const row_classes = classNames({ 'is-expanded': this.state.expanded });
|
const row_classes = classNames({ 'is-expanded': this.state.expanded });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr className={ row_classes }>
|
<tr className={row_classes}>
|
||||||
{ checkbox }
|
{ checkbox }
|
||||||
{ this.props.onRenderItem(this.props.item, actions) }
|
{ this.props.onRenderItem(this.props.item, actions) }
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const ListingItems = React.createClass({
|
const ListingItems = React.createClass({
|
||||||
render: function() {
|
render: function () {
|
||||||
if (this.props.items.length === 0) {
|
if (this.props.items.length === 0) {
|
||||||
|
let message;
|
||||||
|
if (this.props.loading === true) {
|
||||||
|
message = (this.props.messages.onLoadingItems
|
||||||
|
&& this.props.messages.onLoadingItems(this.props.group))
|
||||||
|
|| MailPoet.I18n.t('loadingItems');
|
||||||
|
} else {
|
||||||
|
message = (this.props.messages.onNoItemsFound
|
||||||
|
&& this.props.messages.onNoItemsFound(this.props.group))
|
||||||
|
|| MailPoet.I18n.t('noItemsFound');
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr className="no-items">
|
<tr className="no-items">
|
||||||
@ -207,11 +218,7 @@ const ListingItems = React.createClass({
|
|||||||
+ (this.props.is_selectable ? 1 : 0)
|
+ (this.props.is_selectable ? 1 : 0)
|
||||||
}
|
}
|
||||||
className="colspanchange">
|
className="colspanchange">
|
||||||
{
|
{message}
|
||||||
(this.props.loading === true)
|
|
||||||
? (this.props.messages.onLoadingItems || MailPoet.I18n.t('loadingItems'))
|
|
||||||
: (this.props.messages.onNoItemsFound || MailPoet.I18n.t('noItemsFound'))
|
|
||||||
}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -219,16 +226,16 @@ const ListingItems = React.createClass({
|
|||||||
} else {
|
} else {
|
||||||
const select_all_classes = classNames(
|
const select_all_classes = classNames(
|
||||||
'mailpoet_select_all',
|
'mailpoet_select_all',
|
||||||
{ 'mailpoet_hidden': (
|
{ mailpoet_hidden: (
|
||||||
this.props.selection === false
|
this.props.selection === false
|
||||||
|| (this.props.count <= this.props.limit)
|
|| (this.props.count <= this.props.limit)
|
||||||
)
|
),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr className={ select_all_classes }>
|
<tr className={select_all_classes}>
|
||||||
<td colSpan={
|
<td colSpan={
|
||||||
this.props.columns.length
|
this.props.columns.length
|
||||||
+ (this.props.is_selectable ? 1 : 0)
|
+ (this.props.is_selectable ? 1 : 0)
|
||||||
@ -243,7 +250,7 @@ const ListingItems = React.createClass({
|
|||||||
}
|
}
|
||||||
|
|
||||||
<a
|
<a
|
||||||
onClick={ this.props.onSelectAll }
|
onClick={this.props.onSelectAll}
|
||||||
href="javascript:;">{
|
href="javascript:;">{
|
||||||
(this.props.selection !== 'all')
|
(this.props.selection !== 'all')
|
||||||
? MailPoet.I18n.t('selectAllLink')
|
? MailPoet.I18n.t('selectAllLink')
|
||||||
@ -252,38 +259,39 @@ const ListingItems = React.createClass({
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
{this.props.items.map(function(item, index) {
|
{this.props.items.map((item, index) => {
|
||||||
item.id = parseInt(item.id, 10);
|
const renderItem = item;
|
||||||
item.selected = (this.props.selected_ids.indexOf(item.id) !== -1);
|
renderItem.id = parseInt(item.id, 10);
|
||||||
|
renderItem.selected = (this.props.selected_ids.indexOf(renderItem.id) !== -1);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListingItem
|
<ListingItem
|
||||||
columns={ this.props.columns }
|
columns={this.props.columns}
|
||||||
onSelectItem={ this.props.onSelectItem }
|
onSelectItem={this.props.onSelectItem}
|
||||||
onRenderItem={ this.props.onRenderItem }
|
onRenderItem={this.props.onRenderItem}
|
||||||
onDeleteItem={ this.props.onDeleteItem }
|
onDeleteItem={this.props.onDeleteItem}
|
||||||
onRestoreItem={ this.props.onRestoreItem }
|
onRestoreItem={this.props.onRestoreItem}
|
||||||
onTrashItem={ this.props.onTrashItem }
|
onTrashItem={this.props.onTrashItem}
|
||||||
onRefreshItems={ this.props.onRefreshItems }
|
onRefreshItems={this.props.onRefreshItems}
|
||||||
selection={ this.props.selection }
|
selection={this.props.selection}
|
||||||
is_selectable={ this.props.is_selectable }
|
is_selectable={this.props.is_selectable}
|
||||||
item_actions={ this.props.item_actions }
|
item_actions={this.props.item_actions}
|
||||||
group={ this.props.group }
|
group={this.props.group}
|
||||||
key={ `item-${item.id}-${index}` }
|
key={`item-${renderItem.id}-${index}`}
|
||||||
item={ item } />
|
item={renderItem} />
|
||||||
);
|
);
|
||||||
}.bind(this))}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const Listing = React.createClass({
|
const Listing = React.createClass({
|
||||||
contextTypes: {
|
contextTypes: {
|
||||||
router: React.PropTypes.object.isRequired
|
router: React.PropTypes.object.isRequired,
|
||||||
},
|
},
|
||||||
getInitialState: function() {
|
getInitialState: function () {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
search: '',
|
search: '',
|
||||||
@ -299,31 +307,31 @@ const Listing = React.createClass({
|
|||||||
filter: {},
|
filter: {},
|
||||||
selected_ids: [],
|
selected_ids: [],
|
||||||
selection: false,
|
selection: false,
|
||||||
meta: {}
|
meta: {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
getParam: function(param) {
|
getParam: function (param) {
|
||||||
const regex = /(.*)\[(.*)\]/;
|
const regex = /(.*)\[(.*)\]/;
|
||||||
const matches = regex.exec(param);
|
const matches = regex.exec(param);
|
||||||
return [matches[1], matches[2]];
|
return [matches[1], matches[2]];
|
||||||
},
|
},
|
||||||
initWithParams: function(params) {
|
initWithParams: function (params) {
|
||||||
let state = this.getInitialState();
|
const state = this.getInitialState();
|
||||||
// check for url params
|
// check for url params
|
||||||
if (params.splat) {
|
if (params.splat) {
|
||||||
params.splat.split('/').map(param => {
|
params.splat.split('/').map((param) => {
|
||||||
let [key, value] = this.getParam(param);
|
const [key, value] = this.getParam(param);
|
||||||
switch(key) {
|
switch (key) {
|
||||||
case 'filter':
|
case 'filter':
|
||||||
let filters = {};
|
const filters = {};
|
||||||
value.split('&').map(function(pair) {
|
value.split('&').map((pair) => {
|
||||||
let [k, v] = pair.split('=')
|
const [k, v] = pair.split('=');
|
||||||
filters[k] = v
|
filters[k] = v;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
state.filter = filters;
|
state.filter = filters;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
state[key] = value;
|
state[key] = value;
|
||||||
}
|
}
|
||||||
@ -345,13 +353,13 @@ const Listing = React.createClass({
|
|||||||
state.sort_order = this.props.sort_order;
|
state.sort_order = this.props.sort_order;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState(state, function() {
|
this.setState(state, () => {
|
||||||
this.getItems();
|
this.getItems();
|
||||||
}.bind(this));
|
});
|
||||||
},
|
},
|
||||||
getParams: function() {
|
getParams: function () {
|
||||||
// get all route parameters (without the "splat")
|
// get all route parameters (without the "splat")
|
||||||
let params = _.omit(this.props.params, 'splat');
|
const params = _.omit(this.props.params, 'splat');
|
||||||
// TODO:
|
// TODO:
|
||||||
// find a way to set the "type" in the routes definition
|
// find a way to set the "type" in the routes definition
|
||||||
// so that it appears in `this.props.params`
|
// so that it appears in `this.props.params`
|
||||||
@ -360,10 +368,10 @@ const Listing = React.createClass({
|
|||||||
}
|
}
|
||||||
return params;
|
return params;
|
||||||
},
|
},
|
||||||
setParams: function() {
|
setParams: function () {
|
||||||
if (this.props.location) {
|
if (this.props.location) {
|
||||||
let params = Object.keys(this.state)
|
const params = Object.keys(this.state)
|
||||||
.filter(key => {
|
.filter((key) => {
|
||||||
return (
|
return (
|
||||||
[
|
[
|
||||||
'group',
|
'group',
|
||||||
@ -371,74 +379,75 @@ const Listing = React.createClass({
|
|||||||
'search',
|
'search',
|
||||||
'page',
|
'page',
|
||||||
'sort_by',
|
'sort_by',
|
||||||
'sort_order'
|
'sort_order',
|
||||||
].indexOf(key) !== -1
|
].indexOf(key) !== -1
|
||||||
)
|
);
|
||||||
})
|
})
|
||||||
.map(key => {
|
.map((key) => {
|
||||||
let value = this.state[key];
|
let value = this.state[key];
|
||||||
if (value === Object(value)) {
|
if (value === Object(value)) {
|
||||||
value = jQuery.param(value)
|
value = jQuery.param(value);
|
||||||
} else if (value === Boolean(value)) {
|
} else if (value === Boolean(value)) {
|
||||||
value = value.toString()
|
value = value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value !== '' && value !== null) {
|
if (value !== '' && value !== null) {
|
||||||
return `${key}[${value}]`
|
return `${key}[${value}]`;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter(key => { return (key !== undefined) })
|
.filter((key) => { return (key !== undefined); })
|
||||||
.join('/');
|
.join('/');
|
||||||
|
|
||||||
// set url
|
// set url
|
||||||
let url = this.getUrlWithParams(params);
|
const url = this.getUrlWithParams(params);
|
||||||
|
|
||||||
if (this.props.location.pathname !== url) {
|
if (this.props.location.pathname !== url) {
|
||||||
this.context.router.push(`${url}`);
|
this.context.router.push(`${url}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getUrlWithParams: function(params) {
|
getUrlWithParams: function (params) {
|
||||||
let base_url = (this.props.base_url !== undefined)
|
let base_url = (this.props.base_url !== undefined)
|
||||||
? this.props.base_url
|
? this.props.base_url
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
if (base_url !== null) {
|
if (base_url !== null) {
|
||||||
base_url = this.setBaseUrlParams(base_url);
|
base_url = this.setBaseUrlParams(base_url);
|
||||||
return `/${ base_url }/${ params }`;
|
return `/${base_url}/${params}`;
|
||||||
} else {
|
} else {
|
||||||
return `/${ params }`;
|
return `/${params}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setBaseUrlParams: function(base_url) {
|
setBaseUrlParams: function (base_url) {
|
||||||
if (base_url.indexOf(':') !== -1) {
|
let ret = base_url;
|
||||||
|
if (ret.indexOf(':') !== -1) {
|
||||||
const params = this.getParams();
|
const params = this.getParams();
|
||||||
Object.keys(params).map((key) => {
|
Object.keys(params).map((key) => {
|
||||||
if (base_url.indexOf(':'+key) !== -1) {
|
if (ret.indexOf(':' + key) !== -1) {
|
||||||
base_url = base_url.replace(':'+key, params[key]);
|
ret = ret.replace(':' + key, params[key]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return base_url;
|
return ret;
|
||||||
},
|
},
|
||||||
componentDidMount: function() {
|
componentDidMount: function () {
|
||||||
if (this.isMounted()) {
|
if (this.isMounted()) {
|
||||||
const params = this.props.params || {};
|
const params = this.props.params || {};
|
||||||
this.initWithParams(params);
|
this.initWithParams(params);
|
||||||
|
|
||||||
if (this.props.auto_refresh) {
|
if (this.props.auto_refresh) {
|
||||||
jQuery(document).on('heartbeat-tick.mailpoet', function(e, data) {
|
jQuery(document).on('heartbeat-tick.mailpoet', () => {
|
||||||
this.getItems();
|
this.getItems();
|
||||||
}.bind(this));
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
componentWillReceiveProps: function(nextProps) {
|
componentWillReceiveProps: function (nextProps) {
|
||||||
const params = nextProps.params || {};
|
const params = nextProps.params || {};
|
||||||
this.initWithParams(params);
|
this.initWithParams(params);
|
||||||
},
|
},
|
||||||
getItems: function() {
|
getItems: function () {
|
||||||
if (this.isMounted()) {
|
if (this.isMounted()) {
|
||||||
this.setState({ loading: true });
|
this.setState({ loading: true });
|
||||||
|
|
||||||
@ -456,8 +465,8 @@ const Listing = React.createClass({
|
|||||||
filter: this.state.filter,
|
filter: this.state.filter,
|
||||||
search: this.state.search,
|
search: this.state.search,
|
||||||
sort_by: this.state.sort_by,
|
sort_by: this.state.sort_by,
|
||||||
sort_order: this.state.sort_order
|
sort_order: this.state.sort_order,
|
||||||
}
|
},
|
||||||
}).always(() => {
|
}).always(() => {
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
}).done((response) => {
|
}).done((response) => {
|
||||||
@ -466,7 +475,7 @@ const Listing = React.createClass({
|
|||||||
filters: response.meta.filters || {},
|
filters: response.meta.filters || {},
|
||||||
groups: response.meta.groups || [],
|
groups: response.meta.groups || [],
|
||||||
count: response.meta.count || 0,
|
count: response.meta.count || 0,
|
||||||
meta: _.omit(response.meta, ['filters', 'groups', 'count'])
|
meta: _.omit(response.meta, ['filters', 'groups', 'count']),
|
||||||
}, () => {
|
}, () => {
|
||||||
// if viewing an empty trash
|
// if viewing an empty trash
|
||||||
if (this.state.group === 'trash' && response.meta.count === 0) {
|
if (this.state.group === 'trash' && response.meta.count === 0) {
|
||||||
@ -482,17 +491,17 @@ const Listing = React.createClass({
|
|||||||
}).fail((response) => {
|
}).fail((response) => {
|
||||||
if (response.errors.length > 0) {
|
if (response.errors.length > 0) {
|
||||||
MailPoet.Notice.error(
|
MailPoet.Notice.error(
|
||||||
response.errors.map(function(error) { return error.message; }),
|
response.errors.map((error) => { return error.message; }),
|
||||||
{ scroll: true }
|
{ scroll: true }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleRestoreItem: function(id) {
|
handleRestoreItem: function (id) {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: true,
|
loading: true,
|
||||||
page: 1
|
page: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
MailPoet.Ajax.post({
|
MailPoet.Ajax.post({
|
||||||
@ -500,8 +509,8 @@ const Listing = React.createClass({
|
|||||||
endpoint: this.props.endpoint,
|
endpoint: this.props.endpoint,
|
||||||
action: 'restore',
|
action: 'restore',
|
||||||
data: {
|
data: {
|
||||||
id: id
|
id: id,
|
||||||
}
|
},
|
||||||
}).done((response) => {
|
}).done((response) => {
|
||||||
if (
|
if (
|
||||||
this.props.messages !== undefined
|
this.props.messages !== undefined
|
||||||
@ -512,15 +521,15 @@ const Listing = React.createClass({
|
|||||||
this.getItems();
|
this.getItems();
|
||||||
}).fail((response) => {
|
}).fail((response) => {
|
||||||
MailPoet.Notice.error(
|
MailPoet.Notice.error(
|
||||||
response.errors.map(function(error) { return error.message; }),
|
response.errors.map((error) => { return error.message; }),
|
||||||
{ scroll: true }
|
{ scroll: true }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleTrashItem: function(id) {
|
handleTrashItem: function (id) {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: true,
|
loading: true,
|
||||||
page: 1
|
page: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
MailPoet.Ajax.post({
|
MailPoet.Ajax.post({
|
||||||
@ -528,8 +537,8 @@ const Listing = React.createClass({
|
|||||||
endpoint: this.props.endpoint,
|
endpoint: this.props.endpoint,
|
||||||
action: 'trash',
|
action: 'trash',
|
||||||
data: {
|
data: {
|
||||||
id: id
|
id: id,
|
||||||
}
|
},
|
||||||
}).done((response) => {
|
}).done((response) => {
|
||||||
if (
|
if (
|
||||||
this.props.messages !== undefined
|
this.props.messages !== undefined
|
||||||
@ -540,15 +549,15 @@ const Listing = React.createClass({
|
|||||||
this.getItems();
|
this.getItems();
|
||||||
}).fail((response) => {
|
}).fail((response) => {
|
||||||
MailPoet.Notice.error(
|
MailPoet.Notice.error(
|
||||||
response.errors.map(function(error) { return error.message; }),
|
response.errors.map((error) => { return error.message; }),
|
||||||
{ scroll: true }
|
{ scroll: true }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleDeleteItem: function(id) {
|
handleDeleteItem: function (id) {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: true,
|
loading: true,
|
||||||
page: 1
|
page: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
MailPoet.Ajax.post({
|
MailPoet.Ajax.post({
|
||||||
@ -556,8 +565,8 @@ const Listing = React.createClass({
|
|||||||
endpoint: this.props.endpoint,
|
endpoint: this.props.endpoint,
|
||||||
action: 'delete',
|
action: 'delete',
|
||||||
data: {
|
data: {
|
||||||
id: id
|
id: id,
|
||||||
}
|
},
|
||||||
}).done((response) => {
|
}).done((response) => {
|
||||||
if (
|
if (
|
||||||
this.props.messages !== undefined
|
this.props.messages !== undefined
|
||||||
@ -568,15 +577,15 @@ const Listing = React.createClass({
|
|||||||
this.getItems();
|
this.getItems();
|
||||||
}).fail((response) => {
|
}).fail((response) => {
|
||||||
MailPoet.Notice.error(
|
MailPoet.Notice.error(
|
||||||
response.errors.map(function(error) { return error.message; }),
|
response.errors.map((error) => { return error.message; }),
|
||||||
{ scroll: true }
|
{ scroll: true }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleEmptyTrash: function() {
|
handleEmptyTrash: function () {
|
||||||
return this.handleBulkAction('all', {
|
return this.handleBulkAction('all', {
|
||||||
action: 'delete',
|
action: 'delete',
|
||||||
group: 'trash'
|
group: 'trash',
|
||||||
}).done((response) => {
|
}).done((response) => {
|
||||||
MailPoet.Notice.success(
|
MailPoet.Notice.success(
|
||||||
MailPoet.I18n.t('permanentlyDeleted').replace('%d', response.meta.count)
|
MailPoet.I18n.t('permanentlyDeleted').replace('%d', response.meta.count)
|
||||||
@ -585,12 +594,12 @@ const Listing = React.createClass({
|
|||||||
this.handleGroup('all');
|
this.handleGroup('all');
|
||||||
}).fail((response) => {
|
}).fail((response) => {
|
||||||
MailPoet.Notice.error(
|
MailPoet.Notice.error(
|
||||||
response.errors.map(function(error) { return error.message; }),
|
response.errors.map((error) => { return error.message; }),
|
||||||
{ scroll: true }
|
{ scroll: true }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleBulkAction: function(selected_ids, params) {
|
handleBulkAction: function (selected_ids, params) {
|
||||||
if (
|
if (
|
||||||
this.state.selection === false
|
this.state.selection === false
|
||||||
&& this.state.selected_ids.length === 0
|
&& this.state.selected_ids.length === 0
|
||||||
@ -601,15 +610,15 @@ const Listing = React.createClass({
|
|||||||
|
|
||||||
this.setState({ loading: true });
|
this.setState({ loading: true });
|
||||||
|
|
||||||
var data = params || {};
|
const data = params || {};
|
||||||
data.listing = {
|
data.listing = {
|
||||||
params: this.getParams(),
|
params: this.getParams(),
|
||||||
offset: 0,
|
offset: 0,
|
||||||
limit: 0,
|
limit: 0,
|
||||||
filter: this.state.filter,
|
filter: this.state.filter,
|
||||||
group: this.state.group,
|
group: this.state.group,
|
||||||
search: this.state.search
|
search: this.state.search,
|
||||||
}
|
};
|
||||||
if (selected_ids !== 'all') {
|
if (selected_ids !== 'all') {
|
||||||
data.listing.selection = selected_ids;
|
data.listing.selection = selected_ids;
|
||||||
}
|
}
|
||||||
@ -618,35 +627,42 @@ const Listing = React.createClass({
|
|||||||
api_version: window.mailpoet_api_version,
|
api_version: window.mailpoet_api_version,
|
||||||
endpoint: this.props.endpoint,
|
endpoint: this.props.endpoint,
|
||||||
action: 'bulkAction',
|
action: 'bulkAction',
|
||||||
data: data
|
data: data,
|
||||||
}).done(() => {
|
}).done(() => {
|
||||||
this.getItems();
|
this.getItems();
|
||||||
|
}).fail((response) => {
|
||||||
|
if (response.errors.length > 0) {
|
||||||
|
MailPoet.Notice.error(
|
||||||
|
response.errors.map((error) => { return error.message; }),
|
||||||
|
{ scroll: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleSearch: function(search) {
|
handleSearch: function (search) {
|
||||||
this.setState({
|
this.setState({
|
||||||
search: search,
|
search: search,
|
||||||
page: 1,
|
page: 1,
|
||||||
selection: false,
|
selection: false,
|
||||||
selected_ids: []
|
selected_ids: [],
|
||||||
}, function() {
|
}, () => {
|
||||||
this.setParams();
|
this.setParams();
|
||||||
}.bind(this));
|
});
|
||||||
},
|
},
|
||||||
handleSort: function(sort_by, sort_order = 'asc') {
|
handleSort: function (sort_by, sort_order = 'asc') {
|
||||||
this.setState({
|
this.setState({
|
||||||
sort_by: sort_by,
|
sort_by: sort_by,
|
||||||
sort_order: (sort_order === 'asc') ? 'asc' : 'desc',
|
sort_order: (sort_order === 'asc') ? 'asc' : 'desc',
|
||||||
}, function() {
|
}, () => {
|
||||||
this.setParams();
|
this.setParams();
|
||||||
}.bind(this));
|
});
|
||||||
},
|
},
|
||||||
handleSelectItem: function(id, is_checked) {
|
handleSelectItem: function (id, is_checked) {
|
||||||
var selected_ids = this.state.selected_ids,
|
let selected_ids = this.state.selected_ids;
|
||||||
selection = false;
|
let selection = false;
|
||||||
|
|
||||||
if (is_checked) {
|
if (is_checked) {
|
||||||
selected_ids = jQuery.merge(selected_ids, [ id ]);
|
selected_ids = jQuery.merge(selected_ids, [id]);
|
||||||
// check whether all items on the page are selected
|
// check whether all items on the page are selected
|
||||||
if (
|
if (
|
||||||
jQuery('tbody .check-column :checkbox:not(:checked)').length === 0
|
jQuery('tbody .check-column :checkbox:not(:checked)').length === 0
|
||||||
@ -659,48 +675,48 @@ const Listing = React.createClass({
|
|||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
selection: selection,
|
selection: selection,
|
||||||
selected_ids: selected_ids
|
selected_ids: selected_ids,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleSelectItems: function(is_checked) {
|
handleSelectItems: function (is_checked) {
|
||||||
if (is_checked === false) {
|
if (is_checked === false) {
|
||||||
this.clearSelection();
|
this.clearSelection();
|
||||||
} else {
|
} else {
|
||||||
var selected_ids = this.state.items.map(function(item) {
|
const selected_ids = this.state.items.map((item) => {
|
||||||
return ~~item.id;
|
return ~~item.id;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
selected_ids: selected_ids,
|
selected_ids: selected_ids,
|
||||||
selection: 'page'
|
selection: 'page',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleSelectAll: function() {
|
handleSelectAll: function () {
|
||||||
if (this.state.selection === 'all') {
|
if (this.state.selection === 'all') {
|
||||||
this.clearSelection();
|
this.clearSelection();
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
selection: 'all',
|
selection: 'all',
|
||||||
selected_ids: []
|
selected_ids: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
clearSelection: function() {
|
clearSelection: function () {
|
||||||
this.setState({
|
this.setState({
|
||||||
selection: false,
|
selection: false,
|
||||||
selected_ids: []
|
selected_ids: [],
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleFilter: function(filters) {
|
handleFilter: function (filters) {
|
||||||
this.setState({
|
this.setState({
|
||||||
filter: filters,
|
filter: filters,
|
||||||
page: 1
|
page: 1,
|
||||||
}, function() {
|
}, () => {
|
||||||
this.setParams();
|
this.setParams();
|
||||||
}.bind(this));
|
});
|
||||||
},
|
},
|
||||||
handleGroup: function(group) {
|
handleGroup: function (group) {
|
||||||
// reset search
|
// reset search
|
||||||
jQuery('#search_input').val('');
|
jQuery('#search_input').val('');
|
||||||
|
|
||||||
@ -708,35 +724,35 @@ const Listing = React.createClass({
|
|||||||
group: group,
|
group: group,
|
||||||
filter: {},
|
filter: {},
|
||||||
search: '',
|
search: '',
|
||||||
page: 1
|
page: 1,
|
||||||
}, function() {
|
}, () => {
|
||||||
this.setParams();
|
this.setParams();
|
||||||
}.bind(this));
|
});
|
||||||
},
|
},
|
||||||
handleSetPage: function(page) {
|
handleSetPage: function (page) {
|
||||||
this.setState({
|
this.setState({
|
||||||
page: page,
|
page: page,
|
||||||
selection: false,
|
selection: false,
|
||||||
selected_ids: []
|
selected_ids: [],
|
||||||
}, function() {
|
}, () => {
|
||||||
this.setParams();
|
this.setParams();
|
||||||
}.bind(this));
|
});
|
||||||
},
|
},
|
||||||
handleRenderItem: function(item, actions) {
|
handleRenderItem: function (item, actions) {
|
||||||
const render = this.props.onRenderItem(item, actions, this.state.meta);
|
const render = this.props.onRenderItem(item, actions, this.state.meta);
|
||||||
return render.props.children;
|
return render.props.children;
|
||||||
},
|
},
|
||||||
handleRefreshItems: function() {
|
handleRefreshItems: function () {
|
||||||
this.getItems();
|
this.getItems();
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function () {
|
||||||
const items = this.state.items;
|
const items = this.state.items;
|
||||||
const sort_by = this.state.sort_by;
|
const sort_by = this.state.sort_by;
|
||||||
const sort_order = this.state.sort_order;
|
const sort_order = this.state.sort_order;
|
||||||
|
|
||||||
// columns
|
// columns
|
||||||
let columns = this.props.columns || [];
|
let columns = this.props.columns || [];
|
||||||
columns = columns.filter(function(column) {
|
columns = columns.filter((column) => {
|
||||||
return (column.display === undefined || !!(column.display) === true);
|
return (column.display === undefined || !!(column.display) === true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -748,13 +764,13 @@ const Listing = React.createClass({
|
|||||||
{
|
{
|
||||||
name: 'restore',
|
name: 'restore',
|
||||||
label: MailPoet.I18n.t('restore'),
|
label: MailPoet.I18n.t('restore'),
|
||||||
onSuccess: this.props.messages.onRestore
|
onSuccess: this.props.messages.onRestore,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'delete',
|
name: 'delete',
|
||||||
label: MailPoet.I18n.t('deletePermanently'),
|
label: MailPoet.I18n.t('deletePermanently'),
|
||||||
onSuccess: this.props.messages.onDelete
|
onSuccess: this.props.messages.onDelete,
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -767,14 +783,14 @@ const Listing = React.createClass({
|
|||||||
'widefat',
|
'widefat',
|
||||||
'fixed',
|
'fixed',
|
||||||
'striped',
|
'striped',
|
||||||
{ 'mailpoet_listing_loading': this.state.loading }
|
{ mailpoet_listing_loading: this.state.loading }
|
||||||
);
|
);
|
||||||
|
|
||||||
// search
|
// search
|
||||||
let search = (
|
let search = (
|
||||||
<ListingSearch
|
<ListingSearch
|
||||||
onSearch={ this.handleSearch }
|
onSearch={this.handleSearch}
|
||||||
search={ this.state.search }
|
search={this.state.search}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
if (this.props.search === false) {
|
if (this.props.search === false) {
|
||||||
@ -784,9 +800,9 @@ const Listing = React.createClass({
|
|||||||
// groups
|
// groups
|
||||||
let groups = (
|
let groups = (
|
||||||
<ListingGroups
|
<ListingGroups
|
||||||
groups={ this.state.groups }
|
groups={this.state.groups}
|
||||||
group={ this.state.group }
|
group={this.state.group}
|
||||||
onSelectGroup={ this.handleGroup }
|
onSelectGroup={this.handleGroup}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
if (this.props.groups === false) {
|
if (this.props.groups === false) {
|
||||||
@ -805,84 +821,85 @@ const Listing = React.createClass({
|
|||||||
{ search }
|
{ search }
|
||||||
<div className="tablenav top clearfix">
|
<div className="tablenav top clearfix">
|
||||||
<ListingBulkActions
|
<ListingBulkActions
|
||||||
count={ this.state.count }
|
count={this.state.count}
|
||||||
bulk_actions={ bulk_actions }
|
bulk_actions={bulk_actions}
|
||||||
selection={ this.state.selection }
|
selection={this.state.selection}
|
||||||
selected_ids={ this.state.selected_ids }
|
selected_ids={this.state.selected_ids}
|
||||||
onBulkAction={ this.handleBulkAction } />
|
onBulkAction={this.handleBulkAction} />
|
||||||
<ListingFilters
|
<ListingFilters
|
||||||
filters={ this.state.filters }
|
filters={this.state.filters}
|
||||||
filter={ this.state.filter }
|
filter={this.state.filter}
|
||||||
group={ this.state.group }
|
group={this.state.group}
|
||||||
onSelectFilter={ this.handleFilter }
|
onBeforeSelectFilter={this.props.onBeforeSelectFilter || null}
|
||||||
onEmptyTrash={ this.handleEmptyTrash }
|
onSelectFilter={this.handleFilter}
|
||||||
|
onEmptyTrash={this.handleEmptyTrash}
|
||||||
/>
|
/>
|
||||||
<ListingPages
|
<ListingPages
|
||||||
count={ this.state.count }
|
count={this.state.count}
|
||||||
page={ this.state.page }
|
page={this.state.page}
|
||||||
limit={ this.state.limit }
|
limit={this.state.limit}
|
||||||
onSetPage={ this.handleSetPage } />
|
onSetPage={this.handleSetPage} />
|
||||||
</div>
|
</div>
|
||||||
<table className={ table_classes }>
|
<table className={table_classes}>
|
||||||
<thead>
|
<thead>
|
||||||
<ListingHeader
|
<ListingHeader
|
||||||
onSort={ this.handleSort }
|
onSort={this.handleSort}
|
||||||
onSelectItems={ this.handleSelectItems }
|
onSelectItems={this.handleSelectItems}
|
||||||
selection={ this.state.selection }
|
selection={this.state.selection}
|
||||||
sort_by={ sort_by }
|
sort_by={sort_by}
|
||||||
sort_order={ sort_order }
|
sort_order={sort_order}
|
||||||
columns={ columns }
|
columns={columns}
|
||||||
is_selectable={ bulk_actions.length > 0 } />
|
is_selectable={bulk_actions.length > 0} />
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<ListingItems
|
<ListingItems
|
||||||
onRenderItem={ this.handleRenderItem }
|
onRenderItem={this.handleRenderItem}
|
||||||
onDeleteItem={ this.handleDeleteItem }
|
onDeleteItem={this.handleDeleteItem}
|
||||||
onRestoreItem={ this.handleRestoreItem }
|
onRestoreItem={this.handleRestoreItem}
|
||||||
onTrashItem={ this.handleTrashItem }
|
onTrashItem={this.handleTrashItem}
|
||||||
onRefreshItems={ this.handleRefreshItems }
|
onRefreshItems={this.handleRefreshItems}
|
||||||
columns={ columns }
|
columns={columns}
|
||||||
is_selectable={ bulk_actions.length > 0 }
|
is_selectable={bulk_actions.length > 0}
|
||||||
onSelectItem={ this.handleSelectItem }
|
onSelectItem={this.handleSelectItem}
|
||||||
onSelectAll={ this.handleSelectAll }
|
onSelectAll={this.handleSelectAll}
|
||||||
selection={ this.state.selection }
|
selection={this.state.selection}
|
||||||
selected_ids={ this.state.selected_ids }
|
selected_ids={this.state.selected_ids}
|
||||||
loading={ this.state.loading }
|
loading={this.state.loading}
|
||||||
group={ this.state.group }
|
group={this.state.group}
|
||||||
count={ this.state.count }
|
count={this.state.count}
|
||||||
limit={ this.state.limit }
|
limit={this.state.limit}
|
||||||
item_actions={ item_actions }
|
item_actions={item_actions}
|
||||||
messages={ messages }
|
messages={messages}
|
||||||
items={ items } />
|
items={items} />
|
||||||
|
|
||||||
<tfoot>
|
<tfoot>
|
||||||
<ListingHeader
|
<ListingHeader
|
||||||
onSort={ this.handleSort }
|
onSort={this.handleSort}
|
||||||
onSelectItems={ this.handleSelectItems }
|
onSelectItems={this.handleSelectItems}
|
||||||
selection={ this.state.selection }
|
selection={this.state.selection}
|
||||||
sort_by={ sort_by }
|
sort_by={sort_by}
|
||||||
sort_order={ sort_order }
|
sort_order={sort_order}
|
||||||
columns={ columns }
|
columns={columns}
|
||||||
is_selectable={ bulk_actions.length > 0 } />
|
is_selectable={bulk_actions.length > 0} />
|
||||||
</tfoot>
|
</tfoot>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
<div className="tablenav bottom">
|
<div className="tablenav bottom">
|
||||||
<ListingBulkActions
|
<ListingBulkActions
|
||||||
count={ this.state.count }
|
count={this.state.count}
|
||||||
bulk_actions={ bulk_actions }
|
bulk_actions={bulk_actions}
|
||||||
selection={ this.state.selection }
|
selection={this.state.selection}
|
||||||
selected_ids={ this.state.selected_ids }
|
selected_ids={this.state.selected_ids}
|
||||||
onBulkAction={ this.handleBulkAction } />
|
onBulkAction={this.handleBulkAction} />
|
||||||
<ListingPages
|
<ListingPages
|
||||||
count={ this.state.count }
|
count={this.state.count}
|
||||||
page={ this.state.page }
|
page={this.state.page}
|
||||||
limit={ this.state.limit }
|
limit={this.state.limit}
|
||||||
onSetPage={ this.handleSetPage } />
|
onSetPage={this.handleSetPage} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = Listing;
|
module.exports = Listing;
|
||||||
|
@ -1,84 +1,84 @@
|
|||||||
define([
|
define([
|
||||||
'react',
|
'react',
|
||||||
'classnames',
|
'classnames',
|
||||||
'mailpoet'
|
'mailpoet',
|
||||||
], function(
|
], (
|
||||||
React,
|
React,
|
||||||
classNames,
|
classNames,
|
||||||
MailPoet
|
MailPoet
|
||||||
) {
|
) => {
|
||||||
|
|
||||||
var ListingPages = React.createClass({
|
const ListingPages = React.createClass({
|
||||||
getInitialState: function() {
|
getInitialState: function () {
|
||||||
return {
|
return {
|
||||||
page: null
|
page: null,
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
setPage: function(page) {
|
setPage: function (page) {
|
||||||
this.setState({
|
this.setState({
|
||||||
page: null
|
page: null,
|
||||||
}, function () {
|
}, () => {
|
||||||
this.props.onSetPage(this.constrainPage(page));
|
this.props.onSetPage(this.constrainPage(page));
|
||||||
}.bind(this));
|
});
|
||||||
},
|
},
|
||||||
setFirstPage: function() {
|
setFirstPage: function () {
|
||||||
this.setPage(1);
|
this.setPage(1);
|
||||||
},
|
},
|
||||||
setLastPage: function() {
|
setLastPage: function () {
|
||||||
this.setPage(this.getLastPage());
|
this.setPage(this.getLastPage());
|
||||||
},
|
},
|
||||||
setPreviousPage: function() {
|
setPreviousPage: function () {
|
||||||
this.setPage(this.constrainPage(
|
this.setPage(this.constrainPage(
|
||||||
parseInt(this.props.page, 10) - 1)
|
parseInt(this.props.page, 10) - 1)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
setNextPage: function() {
|
setNextPage: function () {
|
||||||
this.setPage(this.constrainPage(
|
this.setPage(this.constrainPage(
|
||||||
parseInt(this.props.page, 10) + 1)
|
parseInt(this.props.page, 10) + 1)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
constrainPage: function(page) {
|
constrainPage: function (page) {
|
||||||
return Math.min(Math.max(1, Math.abs(~~page)), this.getLastPage());
|
return Math.min(Math.max(1, Math.abs(~~page)), this.getLastPage());
|
||||||
},
|
},
|
||||||
handleSetManualPage: function(e) {
|
handleSetManualPage: function (e) {
|
||||||
if(e.which === 13) {
|
if (e.which === 13) {
|
||||||
this.setPage(this.state.page);
|
this.setPage(this.state.page);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleChangeManualPage: function(e) {
|
handleChangeManualPage: function (e) {
|
||||||
this.setState({
|
this.setState({
|
||||||
page: e.target.value
|
page: e.target.value,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleBlurManualPage: function(e) {
|
handleBlurManualPage: function (e) {
|
||||||
this.setPage(e.target.value);
|
this.setPage(e.target.value);
|
||||||
},
|
},
|
||||||
getLastPage: function() {
|
getLastPage: function () {
|
||||||
return Math.ceil(this.props.count / this.props.limit);
|
return Math.ceil(this.props.count / this.props.limit);
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function () {
|
||||||
if(this.props.count === 0) {
|
if (this.props.count === 0) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
var pagination = false;
|
let pagination = false;
|
||||||
var firstPage = (
|
let firstPage = (
|
||||||
<span aria-hidden="true" className="tablenav-pages-navspan">«</span>
|
<span aria-hidden="true" className="tablenav-pages-navspan">«</span>
|
||||||
);
|
);
|
||||||
var previousPage = (
|
let previousPage = (
|
||||||
<span aria-hidden="true" className="tablenav-pages-navspan">‹</span>
|
<span aria-hidden="true" className="tablenav-pages-navspan">‹</span>
|
||||||
);
|
);
|
||||||
var nextPage = (
|
let nextPage = (
|
||||||
<span aria-hidden="true" className="tablenav-pages-navspan">›</span>
|
<span aria-hidden="true" className="tablenav-pages-navspan">›</span>
|
||||||
);
|
);
|
||||||
var lastPage = (
|
let lastPage = (
|
||||||
<span aria-hidden="true" className="tablenav-pages-navspan">»</span>
|
<span aria-hidden="true" className="tablenav-pages-navspan">»</span>
|
||||||
);
|
);
|
||||||
|
|
||||||
if(this.props.limit > 0 && this.props.count > this.props.limit) {
|
if (this.props.limit > 0 && this.props.count > this.props.limit) {
|
||||||
if(this.props.page > 1) {
|
if (this.props.page > 1) {
|
||||||
previousPage = (
|
previousPage = (
|
||||||
<a href="javascript:;"
|
<a href="javascript:;"
|
||||||
onClick={ this.setPreviousPage }
|
onClick={this.setPreviousPage}
|
||||||
className="prev-page">
|
className="prev-page">
|
||||||
<span className="screen-reader-text">{MailPoet.I18n.t('previousPage')}</span>
|
<span className="screen-reader-text">{MailPoet.I18n.t('previousPage')}</span>
|
||||||
<span aria-hidden="true">‹</span>
|
<span aria-hidden="true">‹</span>
|
||||||
@ -86,10 +86,10 @@ define([
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.props.page > 2) {
|
if (this.props.page > 2) {
|
||||||
firstPage = (
|
firstPage = (
|
||||||
<a href="javascript:;"
|
<a href="javascript:;"
|
||||||
onClick={ this.setFirstPage }
|
onClick={this.setFirstPage}
|
||||||
className="first-page">
|
className="first-page">
|
||||||
<span className="screen-reader-text">{MailPoet.I18n.t('firstPage')}</span>
|
<span className="screen-reader-text">{MailPoet.I18n.t('firstPage')}</span>
|
||||||
<span aria-hidden="true">«</span>
|
<span aria-hidden="true">«</span>
|
||||||
@ -97,10 +97,10 @@ define([
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.props.page < this.getLastPage()) {
|
if (this.props.page < this.getLastPage()) {
|
||||||
nextPage = (
|
nextPage = (
|
||||||
<a href="javascript:;"
|
<a href="javascript:;"
|
||||||
onClick={ this.setNextPage }
|
onClick={this.setNextPage}
|
||||||
className="next-page">
|
className="next-page">
|
||||||
<span className="screen-reader-text">{MailPoet.I18n.t('nextPage')}</span>
|
<span className="screen-reader-text">{MailPoet.I18n.t('nextPage')}</span>
|
||||||
<span aria-hidden="true">›</span>
|
<span aria-hidden="true">›</span>
|
||||||
@ -108,10 +108,10 @@ define([
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.props.page < this.getLastPage() - 1) {
|
if (this.props.page < this.getLastPage() - 1) {
|
||||||
lastPage = (
|
lastPage = (
|
||||||
<a href="javascript:;"
|
<a href="javascript:;"
|
||||||
onClick={ this.setLastPage }
|
onClick={this.setLastPage}
|
||||||
className="last-page">
|
className="last-page">
|
||||||
<span className="screen-reader-text">{MailPoet.I18n.t('lastPage')}</span>
|
<span className="screen-reader-text">{MailPoet.I18n.t('lastPage')}</span>
|
||||||
<span aria-hidden="true">»</span>
|
<span aria-hidden="true">»</span>
|
||||||
@ -119,8 +119,8 @@ define([
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let pageValue = this.props.page;
|
let pageValue = this.props.page;
|
||||||
if(this.state.page !== null) {
|
if (this.state.page !== null) {
|
||||||
pageValue = this.state.page;
|
pageValue = this.state.page;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,13 +136,13 @@ define([
|
|||||||
htmlFor="current-page-selector">{MailPoet.I18n.t('currentPage')}</label>
|
htmlFor="current-page-selector">{MailPoet.I18n.t('currentPage')}</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
onChange={ this.handleChangeManualPage }
|
onChange={this.handleChangeManualPage}
|
||||||
onKeyUp={ this.handleSetManualPage }
|
onKeyUp={this.handleSetManualPage}
|
||||||
onBlur={ this.handleBlurManualPage }
|
onBlur={this.handleBlurManualPage}
|
||||||
aria-describedby="table-paging"
|
aria-describedby="table-paging"
|
||||||
size="2"
|
size="2"
|
||||||
ref="page"
|
ref="page"
|
||||||
value={ pageValue }
|
value={pageValue}
|
||||||
name="paged"
|
name="paged"
|
||||||
id="current-page-selector"
|
id="current-page-selector"
|
||||||
className="current-page" />
|
className="current-page" />
|
||||||
@ -159,12 +159,12 @@ define([
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
var classes = classNames(
|
const classes = classNames(
|
||||||
'tablenav-pages',
|
'tablenav-pages',
|
||||||
{ 'one-page': (this.props.count <= this.props.limit) }
|
{ 'one-page': (this.props.count <= this.props.limit) }
|
||||||
);
|
);
|
||||||
|
|
||||||
var numberOfItemsLabel;
|
let numberOfItemsLabel;
|
||||||
if (this.props.count == 1) {
|
if (this.props.count == 1) {
|
||||||
numberOfItemsLabel = MailPoet.I18n.t('numberOfItemsSingular');
|
numberOfItemsLabel = MailPoet.I18n.t('numberOfItemsSingular');
|
||||||
} else {
|
} else {
|
||||||
@ -172,13 +172,13 @@ define([
|
|||||||
.replace('%$1d', this.props.count.toLocaleString());
|
.replace('%$1d', this.props.count.toLocaleString());
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className={ classes }>
|
<div className={classes}>
|
||||||
<span className="displaying-num">{ numberOfItemsLabel }</span>
|
<span className="displaying-num">{ numberOfItemsLabel }</span>
|
||||||
{ pagination }
|
{ pagination }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return ListingPages;
|
return ListingPages;
|
||||||
|
@ -1,30 +1,30 @@
|
|||||||
define([
|
define([
|
||||||
'mailpoet',
|
'mailpoet',
|
||||||
'react'
|
'react',
|
||||||
], function(
|
], (
|
||||||
MailPoet,
|
MailPoet,
|
||||||
React
|
React
|
||||||
) {
|
) => {
|
||||||
|
|
||||||
var ListingSearch = React.createClass({
|
const ListingSearch = React.createClass({
|
||||||
handleSearch: function(e) {
|
handleSearch: function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.props.onSearch(
|
this.props.onSearch(
|
||||||
this.refs.search.value
|
this.refs.search.value
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
componentWillReceiveProps: function(nextProps) {
|
componentWillReceiveProps: function (nextProps) {
|
||||||
this.refs.search.value = nextProps.search
|
this.refs.search.value = nextProps.search;
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function () {
|
||||||
if(this.props.search === false) {
|
if (this.props.search === false) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<form name="search" onSubmit={this.handleSearch}>
|
<form name="search" onSubmit={this.handleSearch}>
|
||||||
<p className="search-box">
|
<p className="search-box">
|
||||||
<label htmlFor="search_input" className="screen-reader-text">
|
<label htmlFor="search_input" className="screen-reader-text">
|
||||||
{MailPoet.I18n.t('searchLabel')}
|
{MailPoet.I18n.t('searchLabel')}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="search"
|
type="search"
|
||||||
@ -40,7 +40,7 @@ define([
|
|||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return ListingSearch;
|
return ListingSearch;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
define('mailpoet', [], function() {
|
define('mailpoet', [], function () {
|
||||||
// A placeholder for MailPoet object
|
// A placeholder for MailPoet object
|
||||||
var MailPoet = {};
|
var MailPoet = {};
|
||||||
|
|
||||||
|
210
assets/js/src/mp2migrator.js
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
define('mp2migrator', ['mailpoet', 'jquery'], function (mp, jQuery) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var MailPoet = mp;
|
||||||
|
MailPoet.MP2Migrator = {
|
||||||
|
|
||||||
|
fatal_error: '',
|
||||||
|
is_logging: false,
|
||||||
|
|
||||||
|
startLogger: function () {
|
||||||
|
MailPoet.MP2Migrator.is_logging = true;
|
||||||
|
clearTimeout(MailPoet.MP2Migrator.displayLogs_timeout);
|
||||||
|
clearTimeout(MailPoet.MP2Migrator.updateProgressbar_timeout);
|
||||||
|
clearTimeout(MailPoet.MP2Migrator.update_wordpress_info_timeout);
|
||||||
|
setTimeout(MailPoet.MP2Migrator.updateDisplay, 1000);
|
||||||
|
},
|
||||||
|
|
||||||
|
stopLogger: function () {
|
||||||
|
MailPoet.MP2Migrator.is_logging = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateDisplay: function () {
|
||||||
|
MailPoet.MP2Migrator.displayLogs();
|
||||||
|
MailPoet.MP2Migrator.updateProgressbar();
|
||||||
|
},
|
||||||
|
|
||||||
|
displayLogs: function () {
|
||||||
|
jQuery.ajax({
|
||||||
|
url: window.mailpoet_mp2_migrator.log_file_url,
|
||||||
|
cache: false
|
||||||
|
}).done(function (result) {
|
||||||
|
jQuery('#logger').html('');
|
||||||
|
result.split('\n').forEach(function (resultRow) {
|
||||||
|
var row = resultRow;
|
||||||
|
if (row.substr(0, 7) === '[ERROR]' || row.substr(0, 9) === '[WARNING]' || row === MailPoet.I18n.t('import_stopped_by_user')) {
|
||||||
|
row = '<span class="error_msg">' + row + '</span>'; // Mark the errors in red
|
||||||
|
}
|
||||||
|
// Test if the import is complete
|
||||||
|
else if (row === MailPoet.I18n.t('import_complete')) {
|
||||||
|
jQuery('#import-actions').hide();
|
||||||
|
jQuery('#upgrade-completed').show();
|
||||||
|
}
|
||||||
|
jQuery('#logger').append(row + '<br />\n');
|
||||||
|
|
||||||
|
});
|
||||||
|
jQuery('#logger').append('<span class="error_msg">' + MailPoet.MP2Migrator.fatal_error + '</span>' + '<br />\n');
|
||||||
|
}).always(function () {
|
||||||
|
if (MailPoet.MP2Migrator.is_logging) {
|
||||||
|
MailPoet.MP2Migrator.displayLogs_timeout = setTimeout(MailPoet.MP2Migrator.displayLogs, 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateProgressbar: function () {
|
||||||
|
jQuery.ajax({
|
||||||
|
url: window.mailpoet_mp2_migrator.progress_url,
|
||||||
|
cache: false,
|
||||||
|
dataType: 'json'
|
||||||
|
}).always(function (result) {
|
||||||
|
// Move the progress bar
|
||||||
|
var progress = 0;
|
||||||
|
if ((result.total !== undefined) && (Number(result.total) !== 0)) {
|
||||||
|
progress = Math.round(Number(result.current) / Number(result.total) * 100);
|
||||||
|
}
|
||||||
|
jQuery('#progressbar').progressbar('option', 'value', progress);
|
||||||
|
jQuery('#progresslabel').html(progress + '%');
|
||||||
|
if (Number(result.current) !== 0) {
|
||||||
|
jQuery('#skip-import').hide();
|
||||||
|
jQuery('#progressbar').show();
|
||||||
|
jQuery('#logger-container').show();
|
||||||
|
}
|
||||||
|
if (MailPoet.MP2Migrator.is_logging) {
|
||||||
|
MailPoet.MP2Migrator.updateProgressbar_timeout = setTimeout(MailPoet.MP2Migrator.updateProgressbar, 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
startImport: function () {
|
||||||
|
MailPoet.MP2Migrator.fatal_error = '';
|
||||||
|
// Start displaying the logs
|
||||||
|
MailPoet.MP2Migrator.startLogger();
|
||||||
|
|
||||||
|
// Disable the import button
|
||||||
|
MailPoet.MP2Migrator.import_button_label = jQuery('#import').val();
|
||||||
|
jQuery('#import').val(MailPoet.I18n.t('importing')).attr('disabled', 'disabled');
|
||||||
|
// Hide the Skip button
|
||||||
|
jQuery('#skip-import').hide();
|
||||||
|
// Show the stop button
|
||||||
|
jQuery('#stop-import').show();
|
||||||
|
|
||||||
|
// Run the import
|
||||||
|
MailPoet.Ajax.post({
|
||||||
|
api_version: window.mailpoet_api_version,
|
||||||
|
endpoint: 'MP2Migrator',
|
||||||
|
action: 'import',
|
||||||
|
data: {
|
||||||
|
}
|
||||||
|
}).always(function () {
|
||||||
|
MailPoet.MP2Migrator.stopLogger();
|
||||||
|
MailPoet.MP2Migrator.updateDisplay(); // Get the latest information after the import was stopped
|
||||||
|
MailPoet.MP2Migrator.reactivateImportButton();
|
||||||
|
}).done(function (response) {
|
||||||
|
if (response) {
|
||||||
|
MailPoet.MP2Migrator.fatal_error = response.data;
|
||||||
|
}
|
||||||
|
}).fail(function (response) {
|
||||||
|
if (response.errors.length > 0) {
|
||||||
|
MailPoet.Notice.error(
|
||||||
|
response.errors.map(function (error) {
|
||||||
|
return error.message;
|
||||||
|
}),
|
||||||
|
{ scroll: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
reactivateImportButton: function () {
|
||||||
|
jQuery('#import').val(MailPoet.MP2Migrator.import_button_label).removeAttr('disabled');
|
||||||
|
jQuery('#stop-import').hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
stopImport: function () {
|
||||||
|
jQuery('#stop-import').attr('disabled', 'disabled');
|
||||||
|
// Stop the import
|
||||||
|
MailPoet.Ajax.post({
|
||||||
|
api_version: window.mailpoet_api_version,
|
||||||
|
endpoint: 'MP2Migrator',
|
||||||
|
action: 'stopImport',
|
||||||
|
data: {
|
||||||
|
}
|
||||||
|
}).always(function () {
|
||||||
|
jQuery('#stop-import').removeAttr('disabled'); // Enable the button
|
||||||
|
MailPoet.MP2Migrator.reactivateImportButton();
|
||||||
|
MailPoet.MP2Migrator.updateDisplay(); // Get the latest information after the import was stopped
|
||||||
|
}).fail(function (response) {
|
||||||
|
if (response.errors.length > 0) {
|
||||||
|
MailPoet.Notice.error(
|
||||||
|
response.errors.map(function (error) {
|
||||||
|
return error.message;
|
||||||
|
}),
|
||||||
|
{ scroll: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
MailPoet.MP2Migrator.stopLogger();
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
skipImport: function () {
|
||||||
|
MailPoet.Ajax.post({
|
||||||
|
api_version: window.mailpoet_api_version,
|
||||||
|
endpoint: 'MP2Migrator',
|
||||||
|
action: 'skipImport',
|
||||||
|
data: {
|
||||||
|
}
|
||||||
|
}).done(function () {
|
||||||
|
MailPoet.MP2Migrator.gotoWelcomePage();
|
||||||
|
}).fail(function (response) {
|
||||||
|
if (response.errors.length > 0) {
|
||||||
|
MailPoet.Notice.error(
|
||||||
|
response.errors.map(function (error) {
|
||||||
|
return error.message;
|
||||||
|
}),
|
||||||
|
{ scroll: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
gotoWelcomePage: function () {
|
||||||
|
window.location.href = 'admin.php?page=mailpoet-welcome';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actions to run when the DOM is ready
|
||||||
|
*/
|
||||||
|
jQuery(function () {
|
||||||
|
jQuery('#progressbar').progressbar({ value: 0 });
|
||||||
|
|
||||||
|
// Import button
|
||||||
|
jQuery('#import').click(function () {
|
||||||
|
MailPoet.MP2Migrator.startImport();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stop import button
|
||||||
|
jQuery('#stop-import').click(function () {
|
||||||
|
MailPoet.MP2Migrator.stopImport();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Skip import link
|
||||||
|
jQuery('#skip-import').click(function () {
|
||||||
|
MailPoet.MP2Migrator.skipImport();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Go to welcome page
|
||||||
|
jQuery('#goto-welcome').click(function () {
|
||||||
|
MailPoet.MP2Migrator.gotoWelcomePage();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update the display
|
||||||
|
MailPoet.MP2Migrator.updateDisplay();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@ -1,12 +1,9 @@
|
|||||||
define([
|
define([
|
||||||
'backbone',
|
'backbone',
|
||||||
'backbone.marionette',
|
'backbone.marionette',
|
||||||
'backbone.radio',
|
'backbone.radio'
|
||||||
'jquery',
|
], function (Backbone, Marionette, BackboneRadio) {
|
||||||
'underscore',
|
var Radio = BackboneRadio;
|
||||||
'handlebars',
|
|
||||||
'handlebars_helpers'
|
|
||||||
], function(Backbone, Marionette, Radio, jQuery, _, Handlebars) {
|
|
||||||
|
|
||||||
var AppView = Marionette.View.extend({
|
var AppView = Marionette.View.extend({
|
||||||
el: '#mailpoet_editor',
|
el: '#mailpoet_editor',
|
||||||
@ -15,20 +12,22 @@ define([
|
|||||||
contentRegion: '#mailpoet_editor_content',
|
contentRegion: '#mailpoet_editor_content',
|
||||||
sidebarRegion: '#mailpoet_editor_sidebar',
|
sidebarRegion: '#mailpoet_editor_sidebar',
|
||||||
bottomRegion: '#mailpoet_editor_bottom',
|
bottomRegion: '#mailpoet_editor_bottom',
|
||||||
headingRegion: '#mailpoet_editor_heading',
|
headingRegion: '#mailpoet_editor_heading'
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var EditorApplication = Marionette.Application.extend({
|
var EditorApplication = Marionette.Application.extend({
|
||||||
region: '#mailpoet_editor',
|
region: '#mailpoet_editor',
|
||||||
|
|
||||||
onStart: function() {
|
onStart: function () {
|
||||||
this._appView = new AppView();
|
this._appView = new AppView();
|
||||||
this.showView(this._appView);
|
this.showView(this._appView);
|
||||||
},
|
},
|
||||||
|
|
||||||
getChannel: function(channel) {
|
getChannel: function (channel) {
|
||||||
if (channel === undefined) channel = 'global';
|
if (channel === undefined) {
|
||||||
|
return Radio.channel('global');
|
||||||
|
}
|
||||||
return Radio.channel(channel);
|
return Radio.channel(channel);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
* For more check: http://marionettejs.com/docs/marionette.behaviors.html#behaviorslookup
|
* For more check: http://marionettejs.com/docs/marionette.behaviors.html#behaviorslookup
|
||||||
*/
|
*/
|
||||||
define([
|
define([
|
||||||
'backbone.marionette'
|
'backbone.marionette'
|
||||||
], function(Marionette) {
|
], function (BackboneMarionette) {
|
||||||
|
var Marionette = BackboneMarionette;
|
||||||
var BehaviorsLookup = {};
|
var BehaviorsLookup = {};
|
||||||
Marionette.Behaviors.behaviorsLookup = function() {
|
Marionette.Behaviors.behaviorsLookup = function () {
|
||||||
return BehaviorsLookup;
|
return BehaviorsLookup;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,23 +4,44 @@
|
|||||||
* Adds a color picker integration with the view
|
* Adds a color picker integration with the view
|
||||||
*/
|
*/
|
||||||
define([
|
define([
|
||||||
'backbone.marionette',
|
'backbone.marionette',
|
||||||
'newsletter_editor/behaviors/BehaviorsLookup',
|
'newsletter_editor/behaviors/BehaviorsLookup',
|
||||||
'mailpoet',
|
'mailpoet',
|
||||||
'spectrum'
|
'spectrum'
|
||||||
], function(Marionette, BehaviorsLookup, MailPoet, Spectrum) {
|
], function (Marionette, BehaviorsLookup, MailPoet) {
|
||||||
|
var BL = BehaviorsLookup;
|
||||||
|
|
||||||
BehaviorsLookup.ColorPickerBehavior = Marionette.Behavior.extend({
|
BL.ColorPickerBehavior = Marionette.Behavior.extend({
|
||||||
onRender: function() {
|
onRender: function () {
|
||||||
this.view.$('.mailpoet_color').spectrum({
|
var that = this;
|
||||||
clickoutFiresChange: true,
|
var preferredFormat = 'hex6';
|
||||||
showInput: true,
|
this.view.$('.mailpoet_color').each(function () {
|
||||||
showInitial: true,
|
var $input = that.view.$(this);
|
||||||
preferredFormat: "hex6",
|
var updateColorInput = function (color) {
|
||||||
allowEmpty: true,
|
if (color && color.getAlpha() > 0) {
|
||||||
chooseText: MailPoet.I18n.t('selectColor'),
|
$input.val(color.toString(preferredFormat));
|
||||||
cancelText: MailPoet.I18n.t('cancelColorSelection')
|
} else {
|
||||||
|
$input.val('');
|
||||||
|
}
|
||||||
|
$input.trigger('change');
|
||||||
|
};
|
||||||
|
$input.spectrum({
|
||||||
|
clickoutFiresChange: true,
|
||||||
|
showInput: true,
|
||||||
|
showInitial: true,
|
||||||
|
showPalette: true,
|
||||||
|
showSelectionPalette: true,
|
||||||
|
palette: [],
|
||||||
|
localStorageKey: 'newsletter_editor.spectrum.palette',
|
||||||
|
preferredFormat: preferredFormat,
|
||||||
|
allowEmpty: true,
|
||||||
|
chooseText: MailPoet.I18n.t('selectColor'),
|
||||||
|
cancelText: MailPoet.I18n.t('cancelColorSelection'),
|
||||||
|
change: updateColorInput,
|
||||||
|
move: updateColorInput,
|
||||||
|
hide: updateColorInput
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* ContainerDropZoneBehavior
|
* ContainerDropZoneBehavior
|
||||||
*
|
*
|
||||||
@ -6,28 +7,29 @@
|
|||||||
* accept droppables
|
* accept droppables
|
||||||
*/
|
*/
|
||||||
define([
|
define([
|
||||||
'backbone.marionette',
|
'backbone.marionette',
|
||||||
'underscore',
|
'underscore',
|
||||||
'jquery',
|
'jquery',
|
||||||
'newsletter_editor/behaviors/BehaviorsLookup',
|
'newsletter_editor/behaviors/BehaviorsLookup',
|
||||||
'interact'
|
'interact'
|
||||||
], function(Marionette, _, jQuery, BehaviorsLookup, interact) {
|
], function (Marionette, _, jQuery, BL, interact) {
|
||||||
|
var BehaviorsLookup = BL;
|
||||||
|
|
||||||
BehaviorsLookup.ContainerDropZoneBehavior = Marionette.Behavior.extend({
|
BehaviorsLookup.ContainerDropZoneBehavior = Marionette.Behavior.extend({
|
||||||
defaults: {
|
defaults: {
|
||||||
columnLimit: 3,
|
columnLimit: 3
|
||||||
},
|
},
|
||||||
onRender: function() {
|
onRender: function () {
|
||||||
var dragAndDropDisabled = _.isObject(this.view.options.renderOptions) && this.view.options.renderOptions.disableDragAndDrop === true;
|
var dragAndDropDisabled = _.isObject(this.view.options.renderOptions) && this.view.options.renderOptions.disableDragAndDrop === true;
|
||||||
if (!dragAndDropDisabled) {
|
if (!dragAndDropDisabled) {
|
||||||
this.addDropZone();
|
this.addDropZone();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
addDropZone: function(_event) {
|
addDropZone: function () {
|
||||||
var that = this,
|
var that = this;
|
||||||
view = this.view,
|
var view = this.view;
|
||||||
domElement = that.$el.get(0),
|
var domElement = that.$el.get(0);
|
||||||
acceptableElementSelector;
|
var acceptableElementSelector;
|
||||||
|
|
||||||
// TODO: Extract this limitation code to be controlled from containers
|
// TODO: Extract this limitation code to be controlled from containers
|
||||||
if (this.view.renderOptions.depth === 0) {
|
if (this.view.renderOptions.depth === 0) {
|
||||||
@ -45,36 +47,42 @@ define([
|
|||||||
interact(domElement).dropzone({
|
interact(domElement).dropzone({
|
||||||
accept: acceptableElementSelector,
|
accept: acceptableElementSelector,
|
||||||
overlap: 'pointer', // Mouse pointer denotes location of a droppable
|
overlap: 'pointer', // Mouse pointer denotes location of a droppable
|
||||||
ondragenter: function(event) {
|
ondragenter: function () {
|
||||||
// 1. Visually mark block as active for dropping
|
// 1. Visually mark block as active for dropping
|
||||||
view.$el.addClass('mailpoet_drop_active');
|
view.$el.addClass('mailpoet_drop_active');
|
||||||
},
|
},
|
||||||
ondragleave: function(event) {
|
ondragleave: function () {
|
||||||
// 1. Remove visual markings of active dropping container
|
// 1. Remove visual markings of active dropping container
|
||||||
// 2. Remove visual markings of drop position visualization
|
// 2. Remove visual markings of drop position visualization
|
||||||
that.cleanup();
|
that.cleanup();
|
||||||
},
|
},
|
||||||
ondropmove: function(event) {
|
ondropmove: function (event) {
|
||||||
// 1. Compute actual location of the mouse within the container
|
// 1. Compute actual location of the mouse within the container
|
||||||
// 2. Check if insertion is regular (between blocks) or special (with container insertion)
|
// 2. Check if insertion is regular (between blocks) or special (with container insertion)
|
||||||
// 3a. If insertion is regular, compute position where insertion should happen
|
// 3a. If insertion is regular, compute position where insertion should happen
|
||||||
// 3b. If insertion is special, compute position (which side) and which cell the insertion belongs to
|
// 3b. If insertion is special, compute position (which side) and which cell the insertion belongs to
|
||||||
// 4. If insertion at that position is not visualized, display position visualization there, remove other visualizations from this container
|
// 4. If insertion at that position is not visualized, display position visualization there, remove other visualizations from this container
|
||||||
var dropPosition = that.getDropPosition(
|
var dropPosition = that.getDropPosition(
|
||||||
event.dragmove.pageX,
|
event.dragmove.pageX,
|
||||||
event.dragmove.pageY,
|
event.dragmove.pageY,
|
||||||
view.$el,
|
view.$el,
|
||||||
view.model.get('orientation'),
|
view.model.get('orientation'),
|
||||||
view.model.get('blocks').length
|
view.model.get('blocks').length
|
||||||
),
|
);
|
||||||
element = view.$el,
|
var element = view.$el;
|
||||||
markerWidth = '',
|
var markerWidth = '';
|
||||||
markerHeight = '',
|
var markerHeight = '';
|
||||||
containerOffset = element.offset(),
|
var containerOffset = element.offset();
|
||||||
viewCollection = that.getCollection(),
|
var viewCollection = that.getCollection();
|
||||||
marker, targetModel, targetView, targetElement,
|
var marker;
|
||||||
topOffset, leftOffset, isLastBlockInsertion,
|
var targetModel;
|
||||||
$targetBlock, margin;
|
var targetView;
|
||||||
|
var targetElement;
|
||||||
|
var topOffset;
|
||||||
|
var leftOffset;
|
||||||
|
var isLastBlockInsertion;
|
||||||
|
var $targetBlock;
|
||||||
|
var margin;
|
||||||
|
|
||||||
if (dropPosition === undefined) return;
|
if (dropPosition === undefined) return;
|
||||||
|
|
||||||
@ -148,7 +156,7 @@ define([
|
|||||||
// compensated for to position marker right in the middle of two
|
// compensated for to position marker right in the middle of two
|
||||||
// blocks
|
// blocks
|
||||||
if (dropPosition.position === 'before') {
|
if (dropPosition.position === 'before') {
|
||||||
$targetBlock = that.getChildren().findByModel(viewCollection.at(dropPosition.index-1)).$el;
|
$targetBlock = that.getChildren().findByModel(viewCollection.at(dropPosition.index - 1)).$el;
|
||||||
} else {
|
} else {
|
||||||
$targetBlock = that.getChildren().findByModel(viewCollection.at(dropPosition.index)).$el;
|
$targetBlock = that.getChildren().findByModel(viewCollection.at(dropPosition.index)).$el;
|
||||||
}
|
}
|
||||||
@ -161,7 +169,7 @@ define([
|
|||||||
|
|
||||||
element.append(marker);
|
element.append(marker);
|
||||||
},
|
},
|
||||||
ondrop: function(event) {
|
ondrop: function (event) {
|
||||||
// 1. Compute actual location of the mouse
|
// 1. Compute actual location of the mouse
|
||||||
// 2. Check if insertion is regular (between blocks) or special (with container insertion)
|
// 2. Check if insertion is regular (between blocks) or special (with container insertion)
|
||||||
// 3a. If insertion is regular
|
// 3a. If insertion is regular
|
||||||
@ -176,15 +184,19 @@ define([
|
|||||||
// 4. Perform cleanup actions
|
// 4. Perform cleanup actions
|
||||||
|
|
||||||
var dropPosition = that.getDropPosition(
|
var dropPosition = that.getDropPosition(
|
||||||
event.dragEvent.pageX,
|
event.dragEvent.pageX,
|
||||||
event.dragEvent.pageY,
|
event.dragEvent.pageY,
|
||||||
view.$el,
|
view.$el,
|
||||||
view.model.get('orientation'),
|
view.model.get('orientation'),
|
||||||
view.model.get('blocks').length
|
view.model.get('blocks').length
|
||||||
),
|
);
|
||||||
droppableModel = event.draggable.getDropModel(),
|
var droppableModel = event.draggable.getDropModel();
|
||||||
viewCollection = that.getCollection(),
|
var viewCollection = that.getCollection();
|
||||||
droppedView, droppedModel, index, tempCollection, tempCollection2;
|
var droppedView;
|
||||||
|
var index;
|
||||||
|
var tempCollection;
|
||||||
|
var tempCollection2;
|
||||||
|
var tempModel;
|
||||||
|
|
||||||
if (dropPosition === undefined) return;
|
if (dropPosition === undefined) return;
|
||||||
|
|
||||||
@ -194,43 +206,44 @@ define([
|
|||||||
|
|
||||||
if (view.model.get('orientation') === 'horizontal' && droppableModel.get('type') !== 'container') {
|
if (view.model.get('orientation') === 'horizontal' && droppableModel.get('type') !== 'container') {
|
||||||
// Regular blocks always need to be inserted into columns - vertical containers
|
// Regular blocks always need to be inserted into columns - vertical containers
|
||||||
tempCollection = new (EditorApplication.getBlockTypeModel('container'))({
|
|
||||||
orientation: 'vertical',
|
tempCollection = new (window.EditorApplication.getBlockTypeModel('container'))({
|
||||||
|
orientation: 'vertical'
|
||||||
});
|
});
|
||||||
tempCollection.get('blocks').add(droppableModel);
|
tempCollection.get('blocks').add(droppableModel);
|
||||||
viewCollection.add(tempCollection, {at: index});
|
viewCollection.add(tempCollection, { at: index });
|
||||||
} else {
|
} else {
|
||||||
viewCollection.add(droppableModel, {at: index});
|
viewCollection.add(droppableModel, { at: index });
|
||||||
}
|
}
|
||||||
|
|
||||||
droppedView = that.getChildren().findByModel(droppableModel);
|
droppedView = that.getChildren().findByModel(droppableModel);
|
||||||
} else {
|
} else {
|
||||||
// Special insertion by replacing target block with collection
|
// Special insertion by replacing target block with collection
|
||||||
// and inserting dropModel into that
|
// and inserting dropModel into that
|
||||||
var tempModel = viewCollection.at(dropPosition.index);
|
tempModel = viewCollection.at(dropPosition.index);
|
||||||
|
|
||||||
tempCollection = new (EditorApplication.getBlockTypeModel('container'))({
|
tempCollection = new (window.EditorApplication.getBlockTypeModel('container'))({
|
||||||
orientation: (view.model.get('orientation') === 'vertical') ? 'horizontal' : 'vertical',
|
orientation: (view.model.get('orientation') === 'vertical') ? 'horizontal' : 'vertical'
|
||||||
});
|
});
|
||||||
|
|
||||||
viewCollection.remove(tempModel);
|
viewCollection.remove(tempModel);
|
||||||
|
|
||||||
if (tempCollection.get('orientation') === 'horizontal') {
|
if (tempCollection.get('orientation') === 'horizontal') {
|
||||||
if (dropPosition.position === 'before') {
|
if (dropPosition.position === 'before') {
|
||||||
tempCollection2 = new (EditorApplication.getBlockTypeModel('container'))({
|
tempCollection2 = new (window.EditorApplication.getBlockTypeModel('container'))({
|
||||||
orientation: 'vertical',
|
orientation: 'vertical'
|
||||||
});
|
});
|
||||||
tempCollection2.get('blocks').add(droppableModel);
|
tempCollection2.get('blocks').add(droppableModel);
|
||||||
tempCollection.get('blocks').add(tempCollection2);
|
tempCollection.get('blocks').add(tempCollection2);
|
||||||
}
|
}
|
||||||
tempCollection2 = new (EditorApplication.getBlockTypeModel('container'))({
|
tempCollection2 = new (window.EditorApplication.getBlockTypeModel('container'))({
|
||||||
orientation: 'vertical',
|
orientation: 'vertical'
|
||||||
});
|
});
|
||||||
tempCollection2.get('blocks').add(tempModel);
|
tempCollection2.get('blocks').add(tempModel);
|
||||||
tempCollection.get('blocks').add(tempCollection2);
|
tempCollection.get('blocks').add(tempCollection2);
|
||||||
if (dropPosition.position === 'after') {
|
if (dropPosition.position === 'after') {
|
||||||
tempCollection2 = new (EditorApplication.getBlockTypeModel('container'))({
|
tempCollection2 = new (window.EditorApplication.getBlockTypeModel('container'))({
|
||||||
orientation: 'vertical',
|
orientation: 'vertical'
|
||||||
});
|
});
|
||||||
tempCollection2.get('blocks').add(droppableModel);
|
tempCollection2.get('blocks').add(droppableModel);
|
||||||
tempCollection.get('blocks').add(tempCollection2);
|
tempCollection.get('blocks').add(tempCollection2);
|
||||||
@ -244,7 +257,7 @@ define([
|
|||||||
tempCollection.get('blocks').add(droppableModel);
|
tempCollection.get('blocks').add(droppableModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
viewCollection.add(tempCollection, {at: dropPosition.index});
|
viewCollection.add(tempCollection, { at: dropPosition.index });
|
||||||
|
|
||||||
// Call post add actions
|
// Call post add actions
|
||||||
droppedView = that.getChildren().findByModel(tempCollection).children.findByModel(droppableModel);
|
droppedView = that.getChildren().findByModel(tempCollection).children.findByModel(droppableModel);
|
||||||
@ -254,49 +267,53 @@ define([
|
|||||||
event.draggable.onDrop({
|
event.draggable.onDrop({
|
||||||
dropBehavior: that,
|
dropBehavior: that,
|
||||||
droppedModel: droppableModel,
|
droppedModel: droppableModel,
|
||||||
droppedView: droppedView,
|
droppedView: droppedView
|
||||||
});
|
});
|
||||||
|
|
||||||
that.cleanup();
|
that.cleanup();
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
cleanup: function() {
|
cleanup: function () {
|
||||||
// 1. Remove visual markings of active dropping container
|
// 1. Remove visual markings of active dropping container
|
||||||
this.view.$el.removeClass('mailpoet_drop_active');
|
this.view.$el.removeClass('mailpoet_drop_active');
|
||||||
|
|
||||||
// 2. Remove visual markings of drop position visualization
|
// 2. Remove visual markings of drop position visualization
|
||||||
this.view.$('.mailpoet_drop_marker').remove();
|
this.view.$('.mailpoet_drop_marker').remove();
|
||||||
},
|
},
|
||||||
getDropPosition: function(eventX, eventY, unsafe) {
|
getDropPosition: function (eventX, eventY, is_unsafe) {
|
||||||
var SPECIAL_AREA_INSERTION_WIDTH = 0.00, // Disable special insertion. Default: 0.3
|
var SPECIAL_AREA_INSERTION_WIDTH = 0.00; // Disable special insertion. Default: 0.3
|
||||||
|
|
||||||
element = this.view.$el,
|
var element = this.view.$el;
|
||||||
orientation = this.view.model.get('orientation'),
|
var orientation = this.view.model.get('orientation');
|
||||||
|
|
||||||
elementOffset = element.offset(),
|
var elementOffset = element.offset();
|
||||||
elementPageX = elementOffset.left,
|
var elementPageX = elementOffset.left;
|
||||||
elementPageY = elementOffset.top,
|
var elementPageY = elementOffset.top;
|
||||||
elementWidth = element.outerWidth(true),
|
var elementWidth = element.outerWidth(true);
|
||||||
elementHeight = element.outerHeight(true),
|
var elementHeight = element.outerHeight(true);
|
||||||
|
|
||||||
relativeX = eventX - elementPageX,
|
var relativeX = eventX - elementPageX;
|
||||||
relativeY = eventY - elementPageY,
|
var relativeY = eventY - elementPageY;
|
||||||
|
|
||||||
relativeOffset, elementLength,
|
var relativeOffset;
|
||||||
|
var elementLength;
|
||||||
|
|
||||||
canAcceptNormalInsertion = this._canAcceptNormalInsertion(),
|
var canAcceptNormalInsertion = this._canAcceptNormalInsertion();
|
||||||
canAcceptSpecialInsertion = this._canAcceptSpecialInsertion(),
|
var canAcceptSpecialInsertion = this._canAcceptSpecialInsertion();
|
||||||
|
|
||||||
insertionType, index, position, indexAndPosition;
|
var insertionType;
|
||||||
|
var index;
|
||||||
|
var position;
|
||||||
|
var indexAndPosition;
|
||||||
|
|
||||||
unsafe = !!unsafe;
|
var unsafe = !!is_unsafe;
|
||||||
|
|
||||||
if (this.getCollection().length === 0) {
|
if (this.getCollection().length === 0) {
|
||||||
return {
|
return {
|
||||||
insertionType: 'normal',
|
insertionType: 'normal',
|
||||||
index: 0,
|
index: 0,
|
||||||
position: 'inside',
|
position: 'inside'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,21 +364,23 @@ define([
|
|||||||
return {
|
return {
|
||||||
insertionType: insertionType, // 'normal'|'special'
|
insertionType: insertionType, // 'normal'|'special'
|
||||||
index: index,
|
index: index,
|
||||||
position: position, // 'inside'|'before'|'after'
|
position: position // 'inside'|'before'|'after'
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
_computeNormalIndex: function(eventX, eventY) {
|
_computeNormalIndex: function (eventX, eventY) {
|
||||||
// Normal insertion inserts dropModel before target element if
|
// Normal insertion inserts dropModel before target element if
|
||||||
// event happens on the first half of the element and after the
|
// event happens on the first half of the element and after the
|
||||||
// target element if event happens on the second half of the element.
|
// target element if event happens on the second half of the element.
|
||||||
// Halves depend on orientation.
|
// Halves depend on orientation.
|
||||||
|
|
||||||
var index = this._computeCellIndex(eventX, eventY),
|
var index = this._computeCellIndex(eventX, eventY);
|
||||||
// TODO: Handle case when there are no children, container is empty
|
// TODO: Handle case when there are no children, container is empty
|
||||||
targetView = this.getChildren().findByModel(this.getCollection().at(index)),
|
var targetView = this.getChildren().findByModel(this.getCollection().at(index));
|
||||||
orientation = this.view.model.get('orientation'),
|
var orientation = this.view.model.get('orientation');
|
||||||
element = targetView.$el,
|
var element = targetView.$el;
|
||||||
eventOffset, closeOffset, elementDimension;
|
var eventOffset;
|
||||||
|
var closeOffset;
|
||||||
|
var elementDimension;
|
||||||
|
|
||||||
if (orientation === 'vertical') {
|
if (orientation === 'vertical') {
|
||||||
eventOffset = eventY;
|
eventOffset = eventY;
|
||||||
@ -376,63 +395,64 @@ define([
|
|||||||
if (eventOffset <= closeOffset + elementDimension / 2) {
|
if (eventOffset <= closeOffset + elementDimension / 2) {
|
||||||
// First half of the element
|
// First half of the element
|
||||||
return {
|
return {
|
||||||
index: index,
|
index: index,
|
||||||
position: 'before',
|
position: 'before'
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// Second half of the element
|
// Second half of the element
|
||||||
return {
|
return {
|
||||||
index: index,
|
index: index,
|
||||||
position: 'after',
|
position: 'after'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_computeSpecialIndex: function(eventX, eventY) {
|
_computeSpecialIndex: function (eventX, eventY) {
|
||||||
return this._computeCellIndex(eventX, eventY);
|
return this._computeCellIndex(eventX, eventY);
|
||||||
},
|
},
|
||||||
_computeCellIndex: function(eventX, eventY) {
|
_computeCellIndex: function (eventX, eventY) {
|
||||||
var orientation = this.view.model.get('orientation'),
|
var orientation = this.view.model.get('orientation');
|
||||||
eventOffset = (orientation === 'vertical') ? eventY : eventX,
|
var eventOffset = (orientation === 'vertical') ? eventY : eventX;
|
||||||
resultView = this.getChildren().find(function(view) {
|
var resultView = this.getChildren().find(function (view) {
|
||||||
var element = view.$el,
|
var element = view.$el;
|
||||||
closeOffset, farOffset;
|
var closeOffset;
|
||||||
|
var farOffset;
|
||||||
|
|
||||||
if (orientation === 'vertical') {
|
if (orientation === 'vertical') {
|
||||||
closeOffset = element.offset().top;
|
closeOffset = element.offset().top;
|
||||||
farOffset = element.outerHeight(true);
|
farOffset = element.outerHeight(true);
|
||||||
} else {
|
} else {
|
||||||
closeOffset = element.offset().left;
|
closeOffset = element.offset().left;
|
||||||
farOffset = element.outerWidth(true);
|
farOffset = element.outerWidth(true);
|
||||||
}
|
}
|
||||||
farOffset += closeOffset;
|
farOffset += closeOffset;
|
||||||
|
|
||||||
return closeOffset <= eventOffset && eventOffset <= farOffset;
|
return closeOffset <= eventOffset && eventOffset <= farOffset;
|
||||||
});
|
});
|
||||||
|
|
||||||
var index = (typeof resultView === 'object') ? resultView._index : 0;
|
var index = (typeof resultView === 'object') ? resultView._index : 0;
|
||||||
|
|
||||||
return index;
|
return index;
|
||||||
},
|
},
|
||||||
_canAcceptNormalInsertion: function() {
|
_canAcceptNormalInsertion: function () {
|
||||||
var orientation = this.view.model.get('orientation'),
|
var orientation = this.view.model.get('orientation');
|
||||||
depth = this.view.renderOptions.depth,
|
var depth = this.view.renderOptions.depth;
|
||||||
childCount = this.getChildren().length;
|
var childCount = this.getChildren().length;
|
||||||
// Note that depth is zero indexed. Root container has depth=0
|
// Note that depth is zero indexed. Root container has depth=0
|
||||||
return orientation === 'vertical' || (orientation === 'horizontal' && depth === 1 && childCount < this.options.columnLimit);
|
return orientation === 'vertical' || (orientation === 'horizontal' && depth === 1 && childCount < this.options.columnLimit);
|
||||||
},
|
},
|
||||||
_canAcceptSpecialInsertion: function() {
|
_canAcceptSpecialInsertion: function () {
|
||||||
var orientation = this.view.model.get('orientation'),
|
var orientation = this.view.model.get('orientation');
|
||||||
depth = this.view.renderOptions.depth,
|
var depth = this.view.renderOptions.depth;
|
||||||
childCount = this.getChildren().length;
|
var childCount = this.getChildren().length;
|
||||||
return depth === 0 || (depth === 1 && orientation === 'horizontal' && childCount <= this.options.columnLimit);
|
return depth === 0 || (depth === 1 && orientation === 'horizontal' && childCount <= this.options.columnLimit);
|
||||||
},
|
},
|
||||||
getCollectionView: function() {
|
getCollectionView: function () {
|
||||||
return this.view.getChildView('blocks');
|
return this.view.getChildView('blocks');
|
||||||
},
|
},
|
||||||
getChildren: function() {
|
getChildren: function () {
|
||||||
return this.getCollectionView().children;
|
return this.getCollectionView().children;
|
||||||
},
|
},
|
||||||
getCollection: function() {
|
getCollection: function () {
|
||||||
return this.getCollectionView().collection;
|
return this.getCollectionView().collection;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|