Compare commits
1052 Commits
3.0.0-rc.2
...
3.7.1
Author | SHA1 | Date | |
---|---|---|---|
a742dacf32 | |||
e62444d34e | |||
2f2a651fd2 | |||
8a8b99ad54 | |||
7c9943c463 | |||
68b091848c | |||
22d3d00841 | |||
d68dffafd3 | |||
df58322f0e | |||
ea47524cc4 | |||
56f7b4d523 | |||
1d1aa52fa8 | |||
39ae92e580 | |||
2dfb264ca6 | |||
ee2631dbf2 | |||
11067327a1 | |||
8e13eb50bf | |||
7bb2d86f93 | |||
2b351e4f34 | |||
1f30f6997e | |||
a588e95762 | |||
f8e443ed3c | |||
73f51505a2 | |||
576796db22 | |||
72aaaaa94d | |||
0470f773d5 | |||
e71355f73f | |||
aa11af3cf8 | |||
535818fec4 | |||
eba06118a4 | |||
32beb0dcd1 | |||
a70405e76d | |||
a78c9e2f99 | |||
ca8390ed43 | |||
f64cf728fe | |||
ed8c75dd06 | |||
a640e52eaf | |||
3b62367b18 | |||
32a759eca5 | |||
c105df1635 | |||
76aa5678a8 | |||
1855a3a481 | |||
2c96f1902e | |||
f3a5c6e406 | |||
26d4be24c2 | |||
1b98604c90 | |||
2b1eafc16f | |||
1635c3aac3 | |||
861d4ea9e1 | |||
8890dac207 | |||
940c1a66aa | |||
5300e338dc | |||
a73dda6690 | |||
adff7f6f74 | |||
c41fd4cb3b | |||
57de46cb29 | |||
6322932e25 | |||
93143695c4 | |||
a37cb31141 | |||
1dbd002df9 | |||
6599914263 | |||
c974e80dc5 | |||
74211658e0 | |||
72187fefdd | |||
4165ad6959 | |||
0ce72c05f3 | |||
afd801694d | |||
c20ff20f5c | |||
d638273f47 | |||
b053bb395c | |||
788362f5b8 | |||
c8782db74b | |||
3b48bc7517 | |||
bcb1f7e289 | |||
2d7555e79d | |||
bd5be51b0e | |||
ae99a06717 | |||
80c2f8970a | |||
b073f95008 | |||
b2473c9b65 | |||
e92ccdf5b1 | |||
fc5b797738 | |||
867e62d0e8 | |||
566db67e8c | |||
a535e59450 | |||
5ac75591a9 | |||
b993c47181 | |||
d195cd9798 | |||
5fb57b13e4 | |||
9fce8aa126 | |||
6892dbea91 | |||
154e88673c | |||
086ef7429b | |||
2dd15438d8 | |||
937aaae2c4 | |||
37a046a8e9 | |||
6c64ba90c7 | |||
e48a288fae | |||
abe231215b | |||
7d43a41ed9 | |||
a8443a3813 | |||
40526dd083 | |||
cb648da677 | |||
ea0676718e | |||
f5cc1cfe3a | |||
1cc28eb607 | |||
f019eaf38e | |||
ce45c8c553 | |||
9b4e49123d | |||
b2685f7591 | |||
7a3749e043 | |||
dbbb3862bc | |||
a1009eff0d | |||
3096c9a0ab | |||
e93fc41281 | |||
adbebb39f1 | |||
4955673885 | |||
972e470d28 | |||
f1effc30ba | |||
8728a69e69 | |||
5383d16c22 | |||
26f2630e1a | |||
98ac4d5508 | |||
e4f417f595 | |||
bfaec09939 | |||
585f9bcc92 | |||
e7d7674e0e | |||
ba38c55e1f | |||
3e432d7b5d | |||
afefa89466 | |||
cb80b5ca69 | |||
8f6ae91924 | |||
2bba10127d | |||
586f6943e1 | |||
10ee48076a | |||
ee04044676 | |||
63022ccad7 | |||
51d97ddda8 | |||
e8f55493d0 | |||
baab0840b7 | |||
31c3907c8f | |||
75a1c5f95b | |||
66b5aeb578 | |||
18c90fed07 | |||
06dac13327 | |||
7c47b81569 | |||
c25d4d4943 | |||
d6749a591f | |||
251198cf9c | |||
ffb6243614 | |||
0d11416f4e | |||
662b033677 | |||
0d514a8084 | |||
1756df0085 | |||
986cce4a92 | |||
a0947611fb | |||
18a6a1c439 | |||
eb4d2ab829 | |||
cdc6d8a966 | |||
139354f3e6 | |||
37f70b900d | |||
511e07455f | |||
d28b4fda58 | |||
09081631a4 | |||
618723b2bf | |||
5cb39b9bf9 | |||
467cf350e9 | |||
d317eb4fbd | |||
44b96aa9a7 | |||
3b4edd5c95 | |||
a5782a32bc | |||
a3cabad752 | |||
16b2b73727 | |||
c1f5bf9836 | |||
5e49624685 | |||
985e3a9a68 | |||
58f92045c0 | |||
82ec8d7ef9 | |||
1af242c2dd | |||
8f9299afe3 | |||
7775b5ace3 | |||
78f9fea2b0 | |||
783f33b092 | |||
d3250030ba | |||
9e685efd41 | |||
756367dd50 | |||
b5a322a8e1 | |||
3f219379d5 | |||
4037800c10 | |||
7144d4afa8 | |||
658cdd37ac | |||
05dcaeba76 | |||
fbfcfcc85b | |||
8582c19ac0 | |||
5e35a724f0 | |||
c7e83377ba | |||
f6374d0602 | |||
1df25aef76 | |||
3a8b64e11c | |||
1d9b4839f7 | |||
1c5a0bb5bd | |||
eca7de854d | |||
0adaf22eef | |||
c39aeb6283 | |||
7528aba2ef | |||
c3fff8ddbe | |||
ca9e11a231 | |||
4177701044 | |||
65a201c848 | |||
1c4b6a9ab6 | |||
9173b114a7 | |||
cd6828d19e | |||
1c021ac0a0 | |||
4469cef647 | |||
057f0376a5 | |||
7e6b29896f | |||
4c4f6be59b | |||
669d072853 | |||
1033c1c1cf | |||
330067c3f2 | |||
9a942a0bd9 | |||
b9c31698d5 | |||
d079002b09 | |||
cca4fc02c0 | |||
77248e6890 | |||
755179e0d1 | |||
46493b991c | |||
2f05eaf528 | |||
f7397d04ed | |||
3d43970fdc | |||
92864c2d4f | |||
41e028c0d4 | |||
cfbf29b60a | |||
a5137c8d62 | |||
aceb9bb031 | |||
8bc0ad48c0 | |||
449568b37b | |||
c164066522 | |||
5c10a66444 | |||
afed408297 | |||
6e7c7ae8c8 | |||
d3f9fb5f06 | |||
53b4a7ba31 | |||
39212a30e8 | |||
7c7e5bbaa6 | |||
831804d35e | |||
9af98f0afb | |||
5b96789e04 | |||
3b2355d570 | |||
fa44127efa | |||
86e01ce5de | |||
981e4a56e3 | |||
d89eb07b3c | |||
e4b79616ee | |||
21e45a39b4 | |||
b7e492e20e | |||
c2c74d7524 | |||
4d80bdb322 | |||
a58a3c9574 | |||
734d1ded1f | |||
e0f989f6a8 | |||
2265de454e | |||
381fc76d52 | |||
32fdc11c88 | |||
99edc35b08 | |||
66fa22d489 | |||
f1f69c9835 | |||
a6f8953bd3 | |||
8b25acf82c | |||
a9b40802a9 | |||
9eda024b02 | |||
003ddc3b92 | |||
b381923903 | |||
8837c9e14c | |||
8cd83575f2 | |||
03015c0bde | |||
6b36156db5 | |||
4ed39c9620 | |||
f210b15652 | |||
ef5d777fac | |||
ab2b380f2d | |||
c3f974a3c0 | |||
2c020bf283 | |||
17e13c0956 | |||
52a6c4f701 | |||
f09ad47911 | |||
0da2be21c8 | |||
8603e2a96a | |||
ddbabcfcb5 | |||
92f733d30d | |||
93a67e6882 | |||
86aff26d5f | |||
cfc425dfd7 | |||
57b2c577c6 | |||
be4224411f | |||
b2fa79fcbf | |||
1d3102e54b | |||
847916eb1f | |||
adb579a26c | |||
16607dd4be | |||
485c6008c1 | |||
8f9a0239b1 | |||
989baf2aa4 | |||
de91c90ac2 | |||
e10901d939 | |||
426a72c557 | |||
704e4e40f7 | |||
0ff9c006d8 | |||
f7fcec7953 | |||
e484705ac1 | |||
6a8337c67d | |||
f98e58eb99 | |||
476af99130 | |||
0d69b05ac0 | |||
e0052bbb93 | |||
b5b0a02ea4 | |||
3c43c04586 | |||
e87134eca4 | |||
c8f334d782 | |||
7cf63965a0 | |||
eb3cf66958 | |||
ba306ecf66 | |||
a58151fee6 | |||
9eb20f96b2 | |||
57f00bbe0c | |||
287c89749d | |||
b9546b61a8 | |||
606177baaf | |||
b7704c90e8 | |||
9d4f996a6a | |||
774e8d94b5 | |||
4ffadc990e | |||
8ab2cd75c0 | |||
d59ebbbc57 | |||
84638b2818 | |||
a8305adf8d | |||
243ed9d61b | |||
84dfa88a1a | |||
f9204f289f | |||
408b64dc0d | |||
83b24f053e | |||
9acef55352 | |||
4c0f5bb456 | |||
be6647d763 | |||
99732ac14d | |||
0b17a1099b | |||
c0c57f6b67 | |||
bf8b0c81df | |||
d82e3a25ac | |||
98ed295896 | |||
b076e03a75 | |||
a754cfaa52 | |||
3721694f4d | |||
91622f8cd9 | |||
4a3ba73406 | |||
ab3a2f064f | |||
a2270d6689 | |||
e09acbe649 | |||
b54f002635 | |||
aaaf49d071 | |||
8ff28fd974 | |||
126b37f2bb | |||
bebd835790 | |||
9526a6b470 | |||
7eb8ff756b | |||
9159e8d21d | |||
f4ba45490d | |||
9f9b44fd7e | |||
ae9f373cff | |||
80a3bbd030 | |||
aba75849e1 | |||
874a37303a | |||
f467479438 | |||
bb76a8c6aa | |||
63000c48c4 | |||
b88dec06d9 | |||
a0667adace | |||
fef8017134 | |||
87de314c18 | |||
c8a20ec4ae | |||
9739159a6f | |||
0aa4c37ad3 | |||
2c8c8b3d0e | |||
12434b412f | |||
789c2a7d4e | |||
907356bb0f | |||
8800799234 | |||
9c2aa569ea | |||
9ee6920ebb | |||
5d8d9567c7 | |||
5bddc79576 | |||
e2f23df829 | |||
43fbbca050 | |||
5109bc3922 | |||
65b4aeffc8 | |||
17a25120d4 | |||
cb055a2b06 | |||
7361613bba | |||
a8f33d9a7e | |||
c497a72aab | |||
d0f55a1d1b | |||
3b92caf677 | |||
d3c64810a8 | |||
f85a169248 | |||
2dd08c6820 | |||
d7609f48d4 | |||
1d02c688a2 | |||
37c58c6aa4 | |||
4abd034880 | |||
e8c7584b9b | |||
be3eb0a7ff | |||
eee01ee3f1 | |||
56ad9a8976 | |||
20d0ed1aac | |||
f42b61a49e | |||
5e152e920e | |||
0d26b62416 | |||
8e60b2b317 | |||
cae46d9acd | |||
bfad5509c1 | |||
3ae7b436de | |||
30b2eda29f | |||
a519eff231 | |||
fe2ab08a49 | |||
9b914a471b | |||
74f008517b | |||
ba45ee7e35 | |||
a5543d9d78 | |||
3c2ef4b8ee | |||
07417391be | |||
e00d43e506 | |||
c4f285afca | |||
c70097085e | |||
ca6bde8a64 | |||
a1ba783264 | |||
e77d00b179 | |||
b683ae0bc1 | |||
f658fd7776 | |||
1a08155f54 | |||
a3b3e1f8df | |||
87aca7c667 | |||
a7a8cd2be4 | |||
819d4dc17a | |||
6e94db24a2 | |||
5bf532a750 | |||
c63b7d9b91 | |||
a8052c118a | |||
1ad0dce425 | |||
2228f60e2a | |||
99a007fb70 | |||
5e5caab4da | |||
caa0623112 | |||
a05f9bf97b | |||
7202d9dca1 | |||
655dac458a | |||
0e487d2c54 | |||
d2d1657cb2 | |||
3bf800b51d | |||
9910072e72 | |||
8695d147e8 | |||
042d47b027 | |||
a27823ebc5 | |||
1aee4489da | |||
e4a37c3c2d | |||
ea2a91d15e | |||
72193fefae | |||
7e60155bdc | |||
7eae7dde11 | |||
d70c6ce453 | |||
3337637a8b | |||
69ca597f24 | |||
6f20a402f6 | |||
19c7efc9ef | |||
6c25fab6d6 | |||
a45a7a7616 | |||
3053e21910 | |||
699017532e | |||
7465398d17 | |||
ef410fb877 | |||
f154687c53 | |||
b773263fa4 | |||
9870f62984 | |||
1f6430c278 | |||
4018ca4a65 | |||
1342389602 | |||
368f7004c0 | |||
d33cc9d2b8 | |||
e9be62c2d3 | |||
f2733624e4 | |||
46121d74a8 | |||
cb430673f8 | |||
8852f6a3f4 | |||
56e55c072a | |||
77e60d708a | |||
27fbc25bf9 | |||
a91929682f | |||
d45c510ab3 | |||
57366e972a | |||
cbd1ae929e | |||
884f476598 | |||
55296d52ec | |||
8fb4d2e78f | |||
840473aae5 | |||
5d5b61b76f | |||
0e2a67c203 | |||
aaed5add52 | |||
f616ef4d6c | |||
083a6fd3a3 | |||
9a1ec60750 | |||
bd67f5fd36 | |||
ed2a2a6fa8 | |||
6a28fdcac2 | |||
fda1828637 | |||
ebcc094b4d | |||
51cde55217 | |||
23dff7403d | |||
de164f918c | |||
84840c5261 | |||
16b9e0d467 | |||
3c75e7b0bc | |||
c41d5894d0 | |||
1f76f0d98f | |||
e859c090c8 | |||
42528ab309 | |||
d761867a7d | |||
6996615999 | |||
3450a6fc97 | |||
554dbb8dda | |||
d13aa67a07 | |||
1f9c956637 | |||
59d09866a1 | |||
f8ade27a6c | |||
313bcddba1 | |||
fd64702eaa | |||
e2884ac625 | |||
9ce9cb929f | |||
52d33d7fe8 | |||
2ee33b2fc2 | |||
b107be3241 | |||
76148d0a85 | |||
61a7f99e42 | |||
62e3ca8a80 | |||
d431efb288 | |||
adf6485b7d | |||
170fd7f051 | |||
e7ddfc3d29 | |||
b08b53e9c0 | |||
570529a8d8 | |||
5505ffc783 | |||
6e5fc6ad6b | |||
e731b261ab | |||
955f24e71a | |||
89ebc6051a | |||
79fff7b65d | |||
7864e08900 | |||
878cdf46e6 | |||
131d341744 | |||
513062b15d | |||
4926267e36 | |||
aa4d78b1c9 | |||
8d7289e8ba | |||
eb768ff1ee | |||
8afe7f5d97 | |||
97fb5cf66f | |||
8ea4a219e2 | |||
0e08e58288 | |||
dc52ba0d93 | |||
dc569672a9 | |||
159e946093 | |||
3d2a433df1 | |||
fe0476e1c0 | |||
ea552508b4 | |||
dee2ff810c | |||
63ed835d64 | |||
fbf58f23fc | |||
b6f62bd9bc | |||
88ef454844 | |||
3572df6c0c | |||
714e6e013f | |||
91568cbeb6 | |||
393c89b21f | |||
0b491b7943 | |||
e5d7f66561 | |||
b7a7f40cde | |||
a267c7524c | |||
20b59d11a7 | |||
94c7e2a5c0 | |||
d603d99a04 | |||
e7ffe4d694 | |||
018d7bce77 | |||
64c40d5a1c | |||
7e49328d5e | |||
b50b636371 | |||
ec3bb5b95c | |||
6b2503fb36 | |||
e71d47f983 | |||
085d4f566a | |||
29b249de6e | |||
883ae5b0e4 | |||
d78990cda3 | |||
e5b5a8df37 | |||
2ea2db66ec | |||
7b76ddefd5 | |||
6c56ac8509 | |||
dc074bcb14 | |||
0193644b18 | |||
66de11ecd4 | |||
1238b1711d | |||
0061a9daf9 | |||
52a55d3bd1 | |||
7d686eb1d1 | |||
962188bdb8 | |||
72c3f763ec | |||
44c5d8490f | |||
fb85623b86 | |||
d7bf6addf1 | |||
adea1e9be1 | |||
4f77ca31a3 | |||
087f2ebcdd | |||
3d2a63f319 | |||
54dd3b621a | |||
7fea134109 | |||
fcf4bd12d9 | |||
6694555592 | |||
e6eb3d691e | |||
00f2d418cc | |||
c63f218edd | |||
fae849bbfc | |||
2a253ccb8d | |||
ff55e55ad2 | |||
d798ec446d | |||
56b4038073 | |||
076a55f1fb | |||
10eca98dc3 | |||
37aec3ee4f | |||
dc7c629e3b | |||
e4f16eda49 | |||
bf15bda84f | |||
8472a837e8 | |||
fc326131ae | |||
a19753205f | |||
ee404e3b84 | |||
f1918ac953 | |||
d399ddf6b6 | |||
3f06448f37 | |||
83d84e67d6 | |||
46c42c1bb4 | |||
944bf67190 | |||
7cac061a73 | |||
17764b708f | |||
2e0f4dfb19 | |||
7f5bc8681c | |||
bfcb85f744 | |||
cb9aaf120e | |||
97a9465db3 | |||
8dfaf9ba32 | |||
89d0da93d3 | |||
c9cbfb4317 | |||
a8ccfec5c6 | |||
d8609c9e84 | |||
5461c975d4 | |||
11298bc101 | |||
c42cf2f622 | |||
c9f1d38baa | |||
3d78d6bbe9 | |||
2b4288f301 | |||
09a2dd231a | |||
3fd4ef9985 | |||
05979965ba | |||
e625a7602a | |||
a0c41ad7ab | |||
7163747eb9 | |||
c30e2b6cf3 | |||
e9eae92ba9 | |||
0fd6fa8879 | |||
52ca6eac18 | |||
28776b8558 | |||
588ad3eab7 | |||
d791538086 | |||
62173e7996 | |||
0acdcd1ca7 | |||
f39a2c8dda | |||
b648852ef5 | |||
6e45892118 | |||
cd145c51f7 | |||
2c12d9ee2d | |||
5fe28623f1 | |||
3086b3cfc2 | |||
5fd29872ff | |||
732c8a314f | |||
23c650bfa6 | |||
f5ced785e0 | |||
4dc9004303 | |||
893c6bd72b | |||
2ac32484e1 | |||
b31e8ce5f2 | |||
4270e4c315 | |||
4b13395b0c | |||
2e67029ef5 | |||
257517b9a9 | |||
88ee64e15d | |||
c2030e9a86 | |||
16111a99fb | |||
543b3e5a91 | |||
e33b60065e | |||
3bcfadd2ab | |||
4729583d8d | |||
aa3f457595 | |||
3ad490f840 | |||
eb27ed65ae | |||
d543f62c5b | |||
4e6f7a05de | |||
5d467115ad | |||
6858b266fe | |||
852b1a4c08 | |||
b2324db7b4 | |||
e8c85e2a54 | |||
a4ac74c84a | |||
4e378484ea | |||
c55be70b2c | |||
70898790f5 | |||
bfdb535f5b | |||
996e6b16e7 | |||
0217e21753 | |||
f6f79d42e1 | |||
4bbd26b098 | |||
028179af37 | |||
87283bf838 | |||
c680badaa2 | |||
df4a936e43 | |||
e27946ebcc | |||
13e64c012f | |||
f69302be48 | |||
16edfc16ea | |||
229a9c8102 | |||
490091a7e2 | |||
5f58e5ca82 | |||
16beda530a | |||
f1b373924f | |||
6a73c463cb | |||
0271675cd0 | |||
b7555aa640 | |||
fa9ef6e5bd | |||
1c97b004ca | |||
f0ab42adf1 | |||
165d8358d4 | |||
6bd6e74bcb | |||
b23df9e0a4 | |||
7b12affb77 | |||
8c8c01aa75 | |||
0150e699a2 | |||
e6943e2638 | |||
6f80dcb1de | |||
60ed294302 | |||
e8017b58f5 | |||
c79bf7d337 | |||
ac268c1ec9 | |||
1ef131fa2d | |||
1873007550 | |||
fa2ccb51c9 | |||
8f87d654af | |||
dee6e9fbad | |||
ef90264316 | |||
70bf4be723 | |||
07ef727654 | |||
a346e5be29 | |||
5ce1eadde7 | |||
8a4d5395b1 | |||
b5feed0f46 | |||
11f9579101 | |||
c6000c959a | |||
19e67ea2b0 | |||
0f6619e25d | |||
daf747d3be | |||
7393b1f2cf | |||
efe861a9ba | |||
2c358ab179 | |||
ca157fc91d | |||
1fbe5d7bc6 | |||
71c031ccf9 | |||
6449b7ccca | |||
6a956472fe | |||
d6af88d667 | |||
b9184a202f | |||
f898746967 | |||
68165b7b78 | |||
bb8591a67b | |||
bda71ae78e | |||
abd4f6cac2 | |||
87e6cc2a4f | |||
dde598eb64 | |||
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 | |||
b9bdc86fd9 |
@ -1,6 +1,4 @@
|
||||
Listen 8080
|
||||
|
||||
<VirtualHost *:8080>
|
||||
<VirtualHost *:80>
|
||||
UseCanonicalName Off
|
||||
ServerName mailpoet.loc
|
||||
DocumentRoot /home/circleci/mailpoet/wordpress
|
||||
@ -8,6 +6,9 @@ Listen 8080
|
||||
LogLevel notice
|
||||
|
||||
<Directory /home/circleci/mailpoet/wordpress>
|
||||
Options Indexes FollowSymLinks
|
||||
AllowOverride All
|
||||
RewriteEngine On
|
||||
Require all granted
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
|
@ -1,9 +1,9 @@
|
||||
version: 2
|
||||
jobs:
|
||||
qa_js_php5:
|
||||
qa_js_security_php5:
|
||||
working_directory: /home/circleci/mailpoet
|
||||
docker:
|
||||
- image: circleci/php:5.6.30-apache-browsers
|
||||
- image: mailpoet/wordpress:5.6.30_20180417.1
|
||||
- image: circleci/mysql:5.7
|
||||
environment:
|
||||
TZ: /usr/share/zoneinfo/Etc/UTC
|
||||
@ -38,6 +38,10 @@ jobs:
|
||||
command: |
|
||||
mkdir test-results/mocha
|
||||
./do t:j test-results/mocha/junit.xml
|
||||
- run:
|
||||
name: "Composer security check"
|
||||
command: |
|
||||
./do s:composer
|
||||
- run:
|
||||
name: "PHP Unit tests"
|
||||
command: |
|
||||
@ -81,6 +85,7 @@ jobs:
|
||||
curl -sS https://getcomposer.org/installer | php
|
||||
php composer.phar install
|
||||
./do install
|
||||
./do compile:all --env production
|
||||
- save_cache:
|
||||
key: composer-{{ checksum "composer.json" }}-{{ checksum "composer.lock" }}
|
||||
paths:
|
||||
@ -94,13 +99,56 @@ jobs:
|
||||
command: |
|
||||
docker-compose run codeception --steps --debug -vvv --html --xml
|
||||
- store_artifacts:
|
||||
path: ~/mailpoet/tests/acceptance-tests/_output
|
||||
path: tests/_output
|
||||
- store_test_results:
|
||||
path: ~/mailpoet/tests/acceptance-tests/_output
|
||||
path: tests/_output
|
||||
acceptance_tests_multisite:
|
||||
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
|
||||
./do compile:all --env production
|
||||
- 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 -e MULTISITE=1 codeception --steps --debug -vvv --html --xml
|
||||
- store_artifacts:
|
||||
path: tests/_output
|
||||
- store_test_results:
|
||||
path: tests/_output
|
||||
php7:
|
||||
working_directory: /home/circleci/mailpoet
|
||||
docker:
|
||||
- image: circleci/php:7.1-apache-browsers
|
||||
- image: mailpoet/wordpress:7.1_20180417.1
|
||||
- image: circleci/mysql:5.7
|
||||
environment:
|
||||
TZ: /usr/share/zoneinfo/Etc/UTC
|
||||
@ -119,7 +167,38 @@ jobs:
|
||||
- run:
|
||||
name: "PHP Unit tests"
|
||||
command: |
|
||||
WP_TEST_PATH="/home/circleci/mailpoet/wordpress" ./do t:u --xml
|
||||
./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
|
||||
php7_multisite:
|
||||
working_directory: /home/circleci/mailpoet
|
||||
docker:
|
||||
- image: mailpoet/wordpress:7.1_20180417.1
|
||||
- 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_multisite
|
||||
- run:
|
||||
name: "PHP Unit tests"
|
||||
command: |
|
||||
./do t:multisite-unit --xml
|
||||
- store_test_results:
|
||||
path: tests/_output
|
||||
- store_artifacts:
|
||||
@ -132,6 +211,8 @@ workflows:
|
||||
version: 2
|
||||
build_and_test:
|
||||
jobs:
|
||||
- qa_js_php5
|
||||
- qa_js_security_php5
|
||||
- php7
|
||||
- php7_multisite
|
||||
- acceptance_tests
|
||||
- acceptance_tests_multisite
|
||||
|
@ -2,47 +2,61 @@
|
||||
|
||||
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
|
||||
local wp_cli_wordpress_path="--path=wordpress"
|
||||
local wp_cli_allow_root="--allow-root"
|
||||
|
||||
# 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 a2dissite 000-default.conf
|
||||
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
|
||||
|
||||
# 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
|
||||
wp core download $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
|
||||
# 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
|
||||
echo "define(\"WP_DEBUG\", true);" | wp core config --dbname=wordpress --dbuser=root --dbhost=127.0.0.1 --extra-php $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
|
||||
# Change default table prefix
|
||||
sed -i "s/\$table_prefix = 'wp_';/\$table_prefix = 'mp_';/" ./wordpress/wp-config.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
|
||||
if [[ $version == "php7_multisite" ]]; then
|
||||
# Configure multisite environment
|
||||
wp core multisite-install --admin_name=admin --admin_password=admin --admin_email=admin@mailpoet.loc --url=http://mailpoet.loc --title="WordPress MultiSite" $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
cp ./.circleci/wordpress/.htaccess ./wordpress/
|
||||
|
||||
# Add a second blog
|
||||
wp site create --slug=php7_multisite $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
echo "WP_TEST_MULTISITE_SLUG=php7_multisite" >> .env
|
||||
echo "WP_TEST_PATH_MULTISITE=/home/circleci/mailpoet/wordpress" >> .env
|
||||
echo "HTTP_HOST=mailpoet.loc" >> .env
|
||||
|
||||
# Add a third dummy blog
|
||||
wp site create --slug=dummy_multisite $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
else
|
||||
wp core install --admin_name=admin --admin_password=admin --admin_email=admin@mailpoet.loc --url=http://mailpoet.loc --title="WordPress Single" $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
echo "WP_TEST_PATH=/home/circleci/mailpoet/wordpress" >> .env
|
||||
fi
|
||||
|
||||
# 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
|
||||
|
||||
# Activate plugin
|
||||
if [[ $version == "php7_multisite" ]]; then
|
||||
wp plugin activate mailpoet --url=http://mailpoet.loc/php7_multisite/ $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
else
|
||||
wp plugin activate mailpoet $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
fi
|
||||
}
|
||||
|
12
.circleci/wordpress/.htaccess
Normal file
12
.circleci/wordpress/.htaccess
Normal file
@ -0,0 +1,12 @@
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
RewriteRule ^index\.php$ - [L]
|
||||
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} -f [OR]
|
||||
RewriteCond %{REQUEST_FILENAME} -d
|
||||
RewriteRule ^ - [L]
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
|
||||
RewriteRule . index.php [L]
|
@ -1,4 +1,6 @@
|
||||
WP_TEST_PATH="/var/www/wordpress"
|
||||
WP_TEST_PATH_MULTISITE="/var/www/wordpress"
|
||||
WP_TEST_MULTISITE_SLUG=""
|
||||
WP_TEST_ENABLE_NETWORK_TESTS="true"
|
||||
WP_TEST_IMPORT_MAILCHIMP_API=""
|
||||
WP_TEST_IMPORT_MAILCHIMP_LISTS="" // (separated with comma)
|
||||
@ -16,4 +18,5 @@ WP_TEST_MAILER_SMTP_LOGIN=""
|
||||
WP_TEST_MAILER_SMTP_PASSWORD=""
|
||||
WP_SVN_USERNAME=""
|
||||
WP_SVN_PASSWORD=""
|
||||
WP_TRANSIFEX_API_TOKEN=""
|
||||
WP_TRANSIFEX_API_TOKEN=""
|
||||
HTTP_HOST="" // URL of your site (used for multisite env and equals to DOMAIN_CURRENT_SITE from wp-config.php)
|
@ -8,80 +8,6 @@
|
||||
"ecmaVersion": 5
|
||||
},
|
||||
"rules": {
|
||||
"import/no-amd": 0,
|
||||
"space-before-function-paren": 0,
|
||||
"prefer-arrow-callback": 0,
|
||||
"no-undef": 0,
|
||||
"key-spacing": 0,
|
||||
"radix": 0,
|
||||
"no-alert": 0,
|
||||
"block-scoped-var": 0,
|
||||
"guard-for-in": 0,
|
||||
"no-prototype-builtins": 0,
|
||||
"no-restricted-syntax": 0,
|
||||
"newline-per-chained-call": 0,
|
||||
"no-useless-concat": 0,
|
||||
"no-multi-spaces": 0,
|
||||
"no-nested-ternary": 0,
|
||||
"semi-spacing": 0,
|
||||
"no-sequences": 0,
|
||||
"no-useless-return": 0,
|
||||
"array-callback-return": 0,
|
||||
"new-cap": 0,
|
||||
"no-continue": 0,
|
||||
"no-new": 0,
|
||||
"space-unary-ops": 0,
|
||||
"no-redeclare": 0,
|
||||
"no-console": 0,
|
||||
"no-empty": 0,
|
||||
"no-extra-semi": 0,
|
||||
"no-useless-escape": 0,
|
||||
"wrap-iife": 0,
|
||||
"no-unused-expressions": 0,
|
||||
"block-spacing": 0,
|
||||
"computed-property-spacing": 0,
|
||||
"no-plusplus": 0,
|
||||
"array-bracket-spacing": 0,
|
||||
"lines-around-directive": 0,
|
||||
"no-unreachable": 0,
|
||||
"default-case": 0,
|
||||
"no-lonely-if": 0,
|
||||
"space-before-blocks": 0,
|
||||
"no-unneeded-ternary": 0,
|
||||
"no-mixed-operators": 0,
|
||||
"eqeqeq": 0,
|
||||
"space-in-parens": 0,
|
||||
"semi": 0,
|
||||
"max-len": 0,
|
||||
"no-trailing-spaces": 0,
|
||||
"global-require": 0,
|
||||
"no-throw-literal": 0,
|
||||
"no-extra-bind": 0,
|
||||
"one-var-declaration-per-line": 0,
|
||||
"consistent-return": 0,
|
||||
"no-shadow": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"brace-style": 0,
|
||||
"no-else-return": 0,
|
||||
"no-use-before-define": 0,
|
||||
"one-var": 0,
|
||||
"camelcase": 0,
|
||||
"spaced-comment": 0,
|
||||
"padded-blocks": 0,
|
||||
"object-curly-spacing": 0,
|
||||
"strict": 0,
|
||||
"vars-on-top": 0,
|
||||
"no-var": 0,
|
||||
"space-infix-ops": 0,
|
||||
"no-unused-vars": 0,
|
||||
"object-shorthand": 0,
|
||||
"new-parens": 0,
|
||||
"keyword-spacing": 0,
|
||||
"eol-last": 0,
|
||||
"dot-notation": 0,
|
||||
"linebreak-style": 0,
|
||||
"indent": 0,
|
||||
"prefer-template": 0,
|
||||
"func-names": 0
|
||||
"no-underscore-dangle": 0 // Backbone uses underscores, we cannot remove them
|
||||
}
|
||||
}
|
||||
|
@ -10,79 +10,13 @@
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"import/resolver": "webpack"
|
||||
},
|
||||
"rules": {
|
||||
// Exceptions
|
||||
"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/jsx-indent-props": 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-indent": 0,
|
||||
"react/jsx-no-bind": 0,
|
||||
"react/no-array-index-key": 0,
|
||||
"react/self-closing-comp": 0,
|
||||
"react/jsx-tag-spacing": 0,
|
||||
"react/jsx-closing-bracket-location": 0,
|
||||
"react/no-string-refs": 0,
|
||||
"react/jsx-curly-spacing": 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,
|
||||
"keyword-spacing": 0,
|
||||
"default-case": 0,
|
||||
"array-callback-return": 0,
|
||||
"consistent-return": 0,
|
||||
"no-unreachable": 0,
|
||||
"no-extra-semi": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"import/extensions": 0,
|
||||
"import/no-extraneous-dependencies": 0,
|
||||
"camelcase": 0,
|
||||
"template-curly-spacing": 0,
|
||||
"eqeqeq": 0,
|
||||
"no-lonely-if": 0,
|
||||
"space-unary-ops": 0,
|
||||
"block-scoped-var": 0,
|
||||
"no-extra-bind": 0,
|
||||
"no-multi-spaces": 0,
|
||||
"class-methods-use-this": 0,
|
||||
"key-spacing": 0,
|
||||
"no-multiple-empty-lines": 0,
|
||||
"space-in-parens": 0,
|
||||
"no-case-declarations": 0,
|
||||
"array-bracket-spacing": 0,
|
||||
"newline-per-chained-call": 0,
|
||||
"no-else-return": 0,
|
||||
"max-len": 0,
|
||||
"no-useless-concat": 0,
|
||||
"no-unused-expressions": 0,
|
||||
"no-sequences": 0,
|
||||
"no-extra-boolean-cast": 0,
|
||||
"dot-notation": 0,
|
||||
"no-shadow": 0,
|
||||
"one-var": 0,
|
||||
"no-alert": 0,
|
||||
"one-var-declaration-per-line": 0,
|
||||
"no-script-url": 0,
|
||||
"wrap-iife": 0,
|
||||
"vars-on-top": 0,
|
||||
"space-infix-ops": 0,
|
||||
"no-irregular-whitespace": 0,
|
||||
"padded-blocks": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"no-undef": 0
|
||||
"import/extensions": 0 // we wouldn't be able to import jQuery without this line
|
||||
}
|
||||
}
|
||||
|
@ -8,32 +8,9 @@
|
||||
"ecmaVersion": 6
|
||||
},
|
||||
"rules": {
|
||||
"import/no-amd": 0,
|
||||
"no-undef": 0,
|
||||
"one-var": 0,
|
||||
"indent": 0,
|
||||
"linebreak-style": 0,
|
||||
"no-whitespace-before-property": 0,
|
||||
"object-property-newline": 0,
|
||||
"global-require": 0,
|
||||
"semi": 0,
|
||||
"keyword-spacing": 0,
|
||||
"no-bitwise": 0,
|
||||
"newline-per-chained-call": 0,
|
||||
"no-spaced-func": 0,
|
||||
"func-call-spacing": 0,
|
||||
"max-len": 0,
|
||||
"space-unary-ops": 0,
|
||||
"no-unused-vars": 0,
|
||||
"no-unused-expressions": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"no-shadow": 0,
|
||||
"padded-blocks": 0,
|
||||
"vars-on-top": 0,
|
||||
"space-before-blocks": 0,
|
||||
"object-curly-spacing": 0,
|
||||
"one-var-declaration-per-line": 0,
|
||||
// Exceptions
|
||||
"func-names": 0,
|
||||
"space-before-function-paren": 0
|
||||
// Temporary
|
||||
"no-underscore-dangle": 0
|
||||
}
|
||||
}
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -2,6 +2,7 @@
|
||||
TODO
|
||||
composer.phar
|
||||
/vendor
|
||||
/vendor_backup
|
||||
tests/_output/*
|
||||
tests/_support/_generated/*
|
||||
node_modules
|
||||
@ -20,4 +21,5 @@ assets/js/*.json
|
||||
.vagrant
|
||||
lang
|
||||
.mp_svn
|
||||
/nbproject/
|
||||
/nbproject/
|
||||
tests/_data/acceptanceGenerated.sql
|
24
Dockerfile
24
Dockerfile
@ -1,26 +1,8 @@
|
||||
FROM php:5.6-cli
|
||||
FROM mailpoet/wordpress:5.6-cli_20180417.1
|
||||
|
||||
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
|
||||
RUN composer global require --optimize-autoloader "hirak/prestissimo"
|
||||
|
||||
# Prepare application
|
||||
WORKDIR /repo
|
||||
@ -36,4 +18,4 @@ ENV WP_TEST_PATH=/wp-core
|
||||
|
||||
ADD docker-entrypoint.sh /
|
||||
|
||||
RUN ["chmod", "+x", "/docker-entrypoint.sh"]
|
||||
RUN ["chmod", "+x", "/docker-entrypoint.sh"]
|
||||
|
55
RoboFile.php
55
RoboFile.php
@ -90,6 +90,7 @@ class RoboFile extends \Robo\Tasks {
|
||||
|
||||
$css_files = array(
|
||||
'assets/css/src/admin.styl',
|
||||
'assets/css/src/admin-global.styl',
|
||||
'assets/css/src/newsletter_editor/newsletter_editor.styl',
|
||||
'assets/css/src/public.styl',
|
||||
'assets/css/src/rtl.styl',
|
||||
@ -152,17 +153,30 @@ class RoboFile extends \Robo\Tasks {
|
||||
return $this->_exec('./tasks/transifex_init.sh');
|
||||
}
|
||||
|
||||
function testUnit($opts=['file' => null, 'xml' => false]) {
|
||||
function testUnit($opts=['file' => null, 'xml' => false, 'multisite' => false]) {
|
||||
$this->loadEnv();
|
||||
|
||||
$command = 'vendor/bin/codecept run unit -c codeception.unit.yml -f '.(($opts['file']) ? $opts['file'] : '');
|
||||
$command = 'vendor/bin/codecept run unit -c codeception.unit.yml';
|
||||
|
||||
if($opts['multisite']) {
|
||||
$command = 'MULTISITE=true ' . $command;
|
||||
}
|
||||
|
||||
if($opts['file']) {
|
||||
$command .= ' -f ' . $opts['file'];
|
||||
}
|
||||
|
||||
if($opts['xml']) {
|
||||
$command .= ' --xml';
|
||||
}
|
||||
|
||||
return $this->_exec($command);
|
||||
}
|
||||
|
||||
function testMultisiteUnit($opts=['file' => null, 'xml' => false, 'multisite' => true]) {
|
||||
return $this->testUnit($opts);
|
||||
}
|
||||
|
||||
function testCoverage($opts=['file' => null, 'xml' => false]) {
|
||||
$this->loadEnv();
|
||||
$command = join(' ', array(
|
||||
@ -197,6 +211,10 @@ class RoboFile extends \Robo\Tasks {
|
||||
return $this->_exec($command);
|
||||
}
|
||||
|
||||
function securityComposer() {
|
||||
return $this->_exec('vendor/bin/security-checker security:check --format=simple');
|
||||
}
|
||||
|
||||
function testDebug($opts=['file' => null, 'xml' => false]) {
|
||||
$this->loadEnv();
|
||||
$this->_exec('vendor/bin/codecept build -c codeception.unit.yml');
|
||||
@ -213,6 +231,14 @@ class RoboFile extends \Robo\Tasks {
|
||||
return $this->_exec('COMPOSE_HTTP_TIMEOUT=200 docker-compose run codeception --steps --debug -vvv');
|
||||
}
|
||||
|
||||
function testAcceptanceMultisite() {
|
||||
return $this->_exec('COMPOSE_HTTP_TIMEOUT=200 docker-compose run -e MULTISITE=1 codeception --steps --debug -vvv');
|
||||
}
|
||||
|
||||
function deleteDocker() {
|
||||
return $this->_exec('docker-compose down -v --remove-orphans --rmi all');
|
||||
}
|
||||
|
||||
function testFailed() {
|
||||
$this->loadEnv();
|
||||
$this->_exec('vendor/bin/codecept build -c codeception.unit.yml');
|
||||
@ -288,15 +314,16 @@ class RoboFile extends \Robo\Tasks {
|
||||
}
|
||||
|
||||
function svnPublish($opts = ['force' => false]) {
|
||||
$this->loadWPFunctions();
|
||||
$this->loadEnv();
|
||||
|
||||
$svn_dir = ".mp_svn";
|
||||
$plugin_data = get_plugin_data('mailpoet.php', false, false);
|
||||
$plugin_version = $plugin_data['Version'];
|
||||
$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_version = $this->getPluginVersion('mailpoet.php');
|
||||
$plugin_dist_name = 'mailpoet';
|
||||
$plugin_dist_file = $plugin_dist_name . '.zip';
|
||||
|
||||
if(!$plugin_version) {
|
||||
throw new \Exception('Could not parse plugin version, check the plugin header');
|
||||
}
|
||||
$this->say('Publishing version: ' . $plugin_version);
|
||||
|
||||
// Sanity checks
|
||||
@ -415,13 +442,9 @@ class RoboFile extends \Robo\Tasks {
|
||||
$dotenv->load();
|
||||
}
|
||||
|
||||
protected function loadWPFunctions() {
|
||||
$this->loadEnv();
|
||||
define('ABSPATH', getenv('WP_TEST_PATH') . '/');
|
||||
define('WPINC', 'wp-includes');
|
||||
require_once(ABSPATH . WPINC . '/functions.php');
|
||||
require_once(ABSPATH . WPINC . '/formatting.php');
|
||||
require_once(ABSPATH . WPINC . '/plugin.php');
|
||||
require_once(ABSPATH . 'wp-admin/includes/plugin.php');
|
||||
protected function getPluginVersion($file) {
|
||||
$data = file_get_contents($file);
|
||||
preg_match('/^[ \t*]*Version:(.*)$/mi', $data, $m);
|
||||
return !empty($m[1]) ? trim($m[1]) : false;
|
||||
}
|
||||
}
|
||||
|
18
assets/css/src/admin-global.styl
Normal file
18
assets/css/src/admin-global.styl
Normal file
@ -0,0 +1,18 @@
|
||||
@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;
|
||||
|
||||
#wpbody
|
||||
padding-bottom: 20px;
|
@ -27,3 +27,7 @@
|
||||
@require 'pages_custom'
|
||||
|
||||
@require 'mp2migrator'
|
||||
|
||||
@require '../../../node_modules/react-confirm-alert/src/react-confirm-alert.css'
|
||||
|
||||
@require 'newsletter_templates'
|
@ -71,15 +71,17 @@ $box-description-font-size = $box-description-line-height
|
||||
|
||||
.mailpoet_boxes .mailpoet_description
|
||||
float:left
|
||||
width: 245px
|
||||
width: 258px
|
||||
max-height: $box-description-height
|
||||
padding-bottom: 0
|
||||
overflow: hidden
|
||||
word-wrap: break-word
|
||||
overflow-wrap: break-word
|
||||
|
||||
h3
|
||||
margin: 0 0 $box-description-space-between-heading-and-paragraph 0
|
||||
overflow: hidden
|
||||
max-width: 210px
|
||||
max-width: 223px
|
||||
line-height: $box-heading-line-height
|
||||
font-size: $box-heading-font-size
|
||||
|
||||
@ -117,6 +119,21 @@ $box-description-font-size = $box-description-line-height
|
||||
[data-type="standard"] .mailpoet_thumbnail
|
||||
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20viewBox%3D%220%200%2070%2070%22%20enable-background%3D%22new%200%200%2070%2070%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23ffffff%22%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M62.751%2C65.368L43.923%2C50.955l18.832-14.377l-5.395-4.118v-3.479l5.983%2C4.568%20v3.479l0.166%2C26.269C63.51%2C64.089%2C63.198%2C64.783%2C62.751%2C65.368z%20M31.433%2C49.5l-15.955-12v-30c0-1.657%2C1.339-3%2C2.992-3h33.905%20c1.652%2C0%2C2.992%2C1.343%2C2.992%2C3v30l-15.955%2C12H31.433z%20M18.469%2C35.5H33.5v-2H18.469V35.5z%20M18.469%2C31.5H33.5v-2H18.469V31.5z%20M18.469%2C27.5H33.5v-2H18.469V27.5z%20M38.406%2C7.5H18.469v14h19.937V7.5z%20M52.375%2C7.5H40.408v2h11.967V7.5z%20M52.375%2C11.5H40.408v2%20h11.967V11.5z%20M52.375%2C15.5H40.408v2h11.967V15.5z%20M52.375%2C19.5H40.408v2h11.967V19.5z%20M52.375%2C25.5H37.5v2h14.875V25.5z%20M52.375%2C29.5H37.5v2h14.875V29.5z%20M52.375%2C33.5H37.5v2h14.875V33.5z%20M26.14%2C17.192L28.442%2C9.5l3.277%2C6.571l1.71-2.571l2.992%2C6%20h-2.992h-3.989h-0.997H25.45h-4.986l3.989-4L26.14%2C17.192z%20M22.469%2C12.5h-1c-0.552%2C0-1-0.448-1-1v-1c0-0.552%2C0.448-1%2C1-1h1%20c0.552%2C0%2C1%2C0.448%2C1%2C1v1C23.469%2C12.052%2C23.021%2C12.5%2C22.469%2C12.5z%20M26.795%2C50.955L8.05%2C65.305c-0.419-0.574-0.55-1.41-0.55-2.174%20V37.015l-0.015%2C0.011v-3.479l5.998-4.579v3.479L8.017%2C36.62L26.795%2C50.955z%20M29.137%2C52.659v-0.157h12.462v0.144l0.047-0.036%20L59.73%2C66.5H10.989l18.084-13.889L29.137%2C52.659z%22%2F%3E%3C%2Fsvg%3E%0A")
|
||||
|
||||
[data-type*="woocommerce"] .mailpoet_thumbnail
|
||||
background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjUwMCIgaGVpZ2h0PSIxNDk0IiB2aWV3Qm94PSIwIDAgMjU2IDE1MyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTIzLjc1OSAwaDIwOC4zNzhDMjQ1LjMyNSAwIDI1NiAxMC42NzUgMjU2IDIzLjg2M3Y3OS41NDFjMCAxMy4xODgtMTAuNjc1IDIzLjg2My0yMy44NjMgMjMuODYzSDE1Ny40MWwxMC4yNTcgMjUuMTE4LTQ1LjEwOS0yNS4xMThIMjMuODYzYy0xMy4xODcgMC0yMy44NjItMTAuNjc1LTIzLjg2Mi0yMy44NjNWMjMuODYzQy0uMTA0IDEwLjc4IDEwLjU3IDAgMjMuNzU5IDB6IiBmaWxsPSIjOUI1QzhGIi8+PHBhdGggZD0iTTE0LjU3OCAyMS43NWMxLjQ1Ny0xLjk3OCAzLjY0Mi0zLjAxOCA2LjU1Ni0zLjIyNiA1LjMwOC0uNDE3IDguMzI2IDIuMDggOS4wNTQgNy40OTIgMy4yMjYgMjEuNzUgNi43NjQgNDAuMTcgMTAuNTEgNTUuMjU5bDIyLjc5LTQzLjM5NWMyLjA4Mi0zLjk1NSA0LjY4NC02LjAzNiA3LjgwNi02LjI0NCA0LjU3OS0uMzEyIDcuMzg4IDIuNjAxIDguNTMzIDguNzQxIDIuNjAyIDEzLjg0IDUuOTMyIDI1LjYgOS44ODYgMzUuNTkgMi43MDYtMjYuNDMyIDcuMjg1LTQ1LjQ3NiAxMy43MzctNTcuMjM1IDEuNTYtMi45MTQgMy44NS00LjM3MSA2Ljg2OC00LjU4IDIuMzk0LS4yMDcgNC41NzkuNTIxIDYuNTU2IDIuMDgyIDEuOTc3IDEuNTYxIDMuMDE4IDMuNTM4IDMuMjI2IDUuOTMyLjEwNCAxLjg3My0uMjA4IDMuNDM0LTEuMDQgNC45OTUtNC4wNTkgNy40OTMtNy4zOSAyMC4wODUtMTAuMDk1IDM3LjU2Ny0yLjYwMSAxNi45NjMtMy41MzggMzAuMTgtMi45MTQgMzkuNjUuMjA5IDIuNi0uMjA4IDQuODktMS4yNDggNi44NjgtMS4yNSAyLjI4OS0zLjEyMiAzLjUzOC01LjUxNiAzLjc0Ni0yLjcwNi4yMDgtNS41MTUtMS4wNC04LjIyMS0zLjg1LTkuNjc4LTkuODg3LTE3LjM3OS0yNC42NjQtMjIuOTk4LTQ0LjMzMi02Ljc2NSAxMy4zMi0xMS43NiAyMy4zMS0xNC45ODYgMjkuOTctNi4xNCAxMS43Ni0xMS4zNDMgMTcuNzk2LTE1LjcxNCAxOC4xMDgtMi44MS4yMDgtNS4yMDMtMi4xODYtNy4yODQtNy4xOC01LjMwNy0xMy42MzMtMTEuMDMxLTM5Ljk2Mi0xNy4xNy03OC45ODYtLjQxNy0yLjcwNi4yMDctNS4xIDEuNjY0LTYuOTcyem0yMjMuNjM2IDE2LjMzOGMtMy43NDYtNi41NTYtOS4yNjItMTAuNTEtMTYuNjUtMTIuMDcyLTEuOTc4LS40MTYtMy44NS0uNjI0LTUuNjItLjYyNC05Ljk5IDAtMTguMTA3IDUuMjAzLTI0LjQ1NSAxNS42MS01LjQxMiA4Ljg0NS04LjExNyAxOC42MjctOC4xMTcgMjkuMzQ2IDAgOC4wMTMgMS42NjUgMTQuODgxIDQuOTk1IDIwLjYwNSAzLjc0NiA2LjU1NiA5LjI2MiAxMC41MSAxNi42NSAxMi4wNzEgMS45NzcuNDE3IDMuODUuNjI1IDUuNjIuNjI1IDEwLjA5NCAwIDE4LjIxMS01LjIwMyAyNC40NTUtMTUuNjEgNS40MTEtOC45NSA4LjExNy0xOC43MzIgOC4xMTctMjkuNDUuMTA0LTguMTE3LTEuNjY1LTE0Ljg4Mi00Ljk5NS0yMC41MDF6bS0xMy4xMTIgMjguODI2Yy0xLjQ1NyA2Ljg2OC00LjA1OSAxMS45NjctNy45MSAxNS40MDEtMy4wMTcgMi43MDYtNS44MjcgMy44NS04LjQyOCAzLjMzLTIuNDk4LS41Mi00LjU4LTIuNzA1LTYuMTQtNi43NjQtMS4yNS0zLjIyNi0xLjg3My02LjQ1Mi0xLjg3My05LjQ3IDAtMi42MDEuMjA4LTUuMjAzLjcyOC03LjU5Ni45MzctNC4yNjcgMi43MDYtOC40MyA1LjUxNS0xMi4zODQgMy40MzUtNS4xIDcuMDc3LTcuMTggMTAuODIzLTYuNDUyIDIuNDk4LjUyIDQuNTggMi43MDYgNi4xNCA2Ljc2NCAxLjI0OSAzLjIyNiAxLjg3MyA2LjQ1MiAxLjg3MyA5LjQ3IDAgMi43MDYtLjIwOCA1LjMwNy0uNzI4IDcuN3ptLTUyLjAzMy0yOC44MjZjLTMuNzQ2LTYuNTU2LTkuMzY2LTEwLjUxLTE2LjY1LTEyLjA3Mi0xLjk3Ny0uNDE2LTMuODUtLjYyNC01LjYyLS42MjQtOS45OSAwLTE4LjEwNyA1LjIwMy0yNC40NTUgMTUuNjEtNS40MTEgOC44NDUtOC4xMTcgMTguNjI3LTguMTE3IDI5LjM0NiAwIDguMDEzIDEuNjY1IDE0Ljg4MSA0Ljk5NSAyMC42MDUgMy43NDYgNi41NTYgOS4yNjIgMTAuNTEgMTYuNjUgMTIuMDcxIDEuOTc4LjQxNyAzLjg1LjYyNSA1LjYyLjYyNSAxMC4wOTQgMCAxOC4yMTEtNS4yMDMgMjQuNDU1LTE1LjYxIDUuNDEyLTguOTUgOC4xMTctMTguNzMyIDguMTE3LTI5LjQ1IDAtOC4xMTctMS42NjUtMTQuODgyLTQuOTk1LTIwLjUwMXptLTEzLjIxNiAyOC44MjZjLTEuNDU3IDYuODY4LTQuMDU5IDExLjk2Ny03LjkwOSAxNS40MDEtMy4wMTggMi43MDYtNS44MjggMy44NS04LjQzIDMuMzMtMi40OTctLjUyLTQuNTc4LTIuNzA1LTYuMTQtNi43NjQtMS4yNDgtMy4yMjYtMS44NzItNi40NTItMS44NzItOS40NyAwLTIuNjAxLjIwOC01LjIwMy43MjgtNy41OTYuOTM3LTQuMjY3IDIuNzA2LTguNDMgNS41MTYtMTIuMzg0IDMuNDM0LTUuMSA3LjA3Ni03LjE4IDEwLjgyMi02LjQ1MiAyLjQ5OC41MiA0LjU4IDIuNzA2IDYuMTQgNi43NjQgMS4yNSAzLjIyNiAxLjg3MyA2LjQ1MiAxLjg3MyA5LjQ3LjEwNSAyLjcwNi0uMjA4IDUuMzA3LS43MjggNy43eiIgZmlsbD0iI0ZGRiIvPjwvc3ZnPg==')
|
||||
|
||||
.mailpoet_boxes_preview
|
||||
margin: -10px
|
||||
padding: 10px 20px
|
||||
|
||||
.mailpoet_boxes
|
||||
.title_and_badge
|
||||
display: flex
|
||||
flex-direction: row
|
||||
justify-content: space-between
|
||||
|
||||
.mailpoet_badge
|
||||
margin: 0 0 0 10px
|
||||
padding: 0 6px 0 6px
|
||||
max-height: 21px
|
||||
white-space: nowrap
|
||||
|
@ -28,6 +28,20 @@ a:focus
|
||||
.select2-container
|
||||
width: 25em !important
|
||||
|
||||
placeholder-color = #999 /* default Select2 placeholder color for single dropdown */
|
||||
|
||||
input.select2-search__field::-webkit-input-placeholder
|
||||
color: placeholder-color
|
||||
input.select2-search__field:-moz-placeholder
|
||||
color: placeholder-color
|
||||
input.select2-search__field::-moz-placeholder
|
||||
color: placeholder-color
|
||||
input.select2-search__field:-ms-input-placeholder
|
||||
color: placeholder-color
|
||||
|
||||
.select2-container--default.select2-container--focus .select2-selection--multiple
|
||||
border: 1px solid #aaa; /* default Select2 border for single dropdown */
|
||||
|
||||
// textareas
|
||||
textarea.regular-text
|
||||
width: 25em !important
|
||||
|
24
assets/css/src/icons.styl
Normal file
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"
|
@ -40,13 +40,13 @@ $green-badge-color = #55bd56
|
||||
letter-spacing: 1px
|
||||
vertical-align: middle
|
||||
|
||||
&_excellent
|
||||
&_excellent, &_teal
|
||||
background: $excellent-badge-color
|
||||
|
||||
&_good
|
||||
&_good, &_yellow
|
||||
background: $good-badge-color
|
||||
|
||||
&_bad
|
||||
&_bad, &_red
|
||||
background: $bad-badge-color
|
||||
|
||||
&_green
|
||||
|
@ -163,6 +163,7 @@ $master-column-tool-width = 24px
|
||||
|
||||
.mailpoet_delete_block_confirm
|
||||
color: $warning-text-color
|
||||
float: right
|
||||
|
||||
&:hover
|
||||
color: $warning-text-color
|
||||
@ -170,6 +171,7 @@ $master-column-tool-width = 24px
|
||||
|
||||
.mailpoet_delete_block_cancel
|
||||
color: $warning-alternate-text-color
|
||||
float: right
|
||||
|
||||
&:hover
|
||||
color: $warning-alternate-text-color
|
||||
|
@ -48,3 +48,41 @@ $resize-handle-z-index = 2
|
||||
|
||||
.mailpoet_resize_handle
|
||||
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
|
||||
|
@ -21,10 +21,14 @@ $block-text-line-height = $text-line-height
|
||||
left: 0
|
||||
pointer-events: none
|
||||
border: 1px solid $transparent-color
|
||||
-webkit-transition: 0.3s;
|
||||
transition: 0.3s;
|
||||
|
||||
&:hover > .mailpoet_block_highlight
|
||||
&.mailpoet_highlight > .mailpoet_block_highlight
|
||||
border: 1px dashed $block-hover-highlight-color
|
||||
|
||||
&.mailpoet_highlight > .mailpoet_block_highlight
|
||||
border: 1px dashed $block-hover-highlight-color !important
|
||||
|
||||
.mailpoet_content
|
||||
position: relative
|
||||
|
@ -3,14 +3,19 @@
|
||||
img
|
||||
vertical-align: bottom
|
||||
max-width: 100%
|
||||
width: auto
|
||||
height: auto
|
||||
|
||||
&.mailpoet_full_image
|
||||
padding-left: 0
|
||||
padding-right: 0
|
||||
margin: auto
|
||||
margin-bottom: 0
|
||||
|
||||
.mailpoet_content a:hover
|
||||
cursor: all-scroll
|
||||
.mailpoet_content
|
||||
margin: auto
|
||||
max-width: 100%
|
||||
min-width: 36px
|
||||
|
||||
a:hover
|
||||
cursor: all-scroll
|
||||
|
||||
|
@ -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 */
|
||||
max-height: $max-height
|
||||
opacity: 1
|
||||
overflow-y: hidden
|
||||
overflow-y: inherit
|
||||
|
||||
&.mailpoet_closed
|
||||
max-height: 0px
|
||||
|
9
assets/css/src/newsletter_templates.styl
Normal file
9
assets/css/src/newsletter_templates.styl
Normal file
@ -0,0 +1,9 @@
|
||||
@require 'newsletter_editor/variables'
|
||||
|
||||
.mailpoet_template_iframe
|
||||
position: absolute
|
||||
z-index: -9999
|
||||
top: 0
|
||||
left: 0
|
||||
width: $newsletter-width
|
||||
max-width: $newsletter-width
|
@ -33,3 +33,6 @@ Custom styles for MailPoet pages.
|
||||
|
||||
p.top-space-triple
|
||||
margin-top: 3em
|
||||
|
||||
p.mailpoet-top-text
|
||||
margin-right: 0
|
||||
|
@ -62,6 +62,15 @@
|
||||
margin-bottom: 2em
|
||||
margin-top: 2em
|
||||
|
||||
.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 '✔ '
|
||||
|
||||
|
11
assets/fonts/mailpoet.svg
Normal file
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.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/mailpoet.woff
Normal file
BIN
assets/fonts/mailpoet.woff
Normal file
Binary file not shown.
@ -1,12 +1,12 @@
|
||||
define('admin', [
|
||||
'jquery'
|
||||
],
|
||||
function(jQuery) {
|
||||
jQuery(function($) {
|
||||
// dom ready
|
||||
$(function() {
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
define('admin', [
|
||||
'jquery'
|
||||
],
|
||||
function admin(jQuery) {
|
||||
jQuery(function adminDomReady($) {
|
||||
// dom ready
|
||||
$(function domReady() {
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
@ -2,85 +2,86 @@ function requestFailed(errorMessage, xhr) {
|
||||
if (xhr.responseJSON) {
|
||||
return xhr.responseJSON;
|
||||
}
|
||||
var message = errorMessage.replace('%d', xhr.status);
|
||||
return {
|
||||
errors: [
|
||||
{
|
||||
message: message
|
||||
message: errorMessage.replace('%d', xhr.status)
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
define('ajax', ['mailpoet', 'jquery', 'underscore'], function(mp, jQuery, _) {
|
||||
define('ajax', ['mailpoet', 'jquery', 'underscore'], function ajax(mp, jQuery, _) {
|
||||
var MailPoet = mp;
|
||||
|
||||
MailPoet.Ajax = {
|
||||
version: 0.5,
|
||||
options: {},
|
||||
defaults: {
|
||||
url: null,
|
||||
api_version: null,
|
||||
endpoint: null,
|
||||
action: null,
|
||||
token: null,
|
||||
data: {}
|
||||
},
|
||||
post: function(options) {
|
||||
return this.request('post', options);
|
||||
},
|
||||
init: function(options) {
|
||||
// merge options
|
||||
this.options = jQuery.extend({}, this.defaults, options);
|
||||
version: 0.5,
|
||||
options: {},
|
||||
defaults: {
|
||||
url: null,
|
||||
api_version: null,
|
||||
endpoint: null,
|
||||
action: null,
|
||||
token: null,
|
||||
data: {}
|
||||
},
|
||||
post: function post(options) {
|
||||
return this.request('post', options);
|
||||
},
|
||||
init: function init(options) {
|
||||
// merge options
|
||||
this.options = jQuery.extend({}, this.defaults, options);
|
||||
|
||||
// set default url
|
||||
if(this.options.url === null) {
|
||||
this.options.url = ajaxurl;
|
||||
}
|
||||
// set default url
|
||||
if (this.options.url === null) {
|
||||
this.options.url = window.ajaxurl;
|
||||
}
|
||||
|
||||
// set default token
|
||||
if(this.options.token === null) {
|
||||
this.options.token = window.mailpoet_token;
|
||||
}
|
||||
},
|
||||
getParams: function() {
|
||||
return {
|
||||
action: 'mailpoet',
|
||||
api_version: this.options.api_version,
|
||||
token: this.options.token,
|
||||
endpoint: this.options.endpoint,
|
||||
method: this.options.action,
|
||||
data: this.options.data || {}
|
||||
};
|
||||
},
|
||||
request: function(method, options) {
|
||||
// set options
|
||||
this.init(options);
|
||||
// set default token
|
||||
if (this.options.token === null) {
|
||||
this.options.token = window.mailpoet_token;
|
||||
}
|
||||
},
|
||||
getParams: function getParams() {
|
||||
return {
|
||||
action: 'mailpoet',
|
||||
api_version: this.options.api_version,
|
||||
token: this.options.token,
|
||||
endpoint: this.options.endpoint,
|
||||
method: this.options.action,
|
||||
data: this.options.data || {}
|
||||
};
|
||||
},
|
||||
request: function request(method, options) {
|
||||
var params;
|
||||
var deferred;
|
||||
// set options
|
||||
this.init(options);
|
||||
|
||||
// set request params
|
||||
var params = this.getParams();
|
||||
// set request params
|
||||
params = this.getParams();
|
||||
|
||||
// remove null values from the data object
|
||||
if (_.isObject(params.data)) {
|
||||
params.data = _.pick(params.data, function(value) {
|
||||
return (value !== null);
|
||||
});
|
||||
}
|
||||
// remove null values from the data object
|
||||
if (_.isObject(params.data)) {
|
||||
params.data = _.pick(params.data, function isNotNull(value) {
|
||||
return (value !== null);
|
||||
});
|
||||
}
|
||||
|
||||
// ajax request
|
||||
var deferred = jQuery.post(
|
||||
// ajax request
|
||||
deferred = jQuery.post(
|
||||
this.options.url,
|
||||
params,
|
||||
null,
|
||||
'json'
|
||||
).then(function(data) {
|
||||
).then(function resultHandler(data) {
|
||||
return data;
|
||||
}, _.partial(requestFailed, MailPoet.I18n.t('ajaxFailedErrorMessage')));
|
||||
|
||||
// clear options
|
||||
this.options = {};
|
||||
this.options = {};
|
||||
|
||||
return deferred;
|
||||
}
|
||||
return deferred;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
@ -17,7 +17,7 @@
|
||||
*/
|
||||
var eventsCache = [];
|
||||
|
||||
function track(name, data){
|
||||
function track(name, data) {
|
||||
if (typeof window.mixpanel.track !== 'function') {
|
||||
window.mixpanel.init(window.mixpanelTrackingId);
|
||||
}
|
||||
@ -31,27 +31,18 @@ function exportMixpanel(mp) {
|
||||
if (window.mailpoet_analytics_enabled) {
|
||||
MailPoet.trackEvent = track;
|
||||
} else {
|
||||
MailPoet.trackEvent = function () {};
|
||||
MailPoet.trackEvent = function emptyFunction() {};
|
||||
}
|
||||
}
|
||||
|
||||
function trackCachedEvents() {
|
||||
eventsCache.map(function (event) {
|
||||
eventsCache.forEach(function trackIfEnabled(event) {
|
||||
if (window.mailpoet_analytics_enabled || event.forced) {
|
||||
window.mixpanel.track(event.name, event.data)
|
||||
window.mixpanel.track(event.name, event.data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initializeMixpanelWhenLoaded() {
|
||||
if (typeof window.mixpanel === 'object') {
|
||||
exportMixpanel(MailPoet);
|
||||
trackCachedEvents();
|
||||
} else {
|
||||
setTimeout(initializeMixpanelWhenLoaded, 100);
|
||||
}
|
||||
}
|
||||
|
||||
function cacheEvent(forced, name, data) {
|
||||
eventsCache.push({
|
||||
name: name,
|
||||
@ -62,9 +53,18 @@ function cacheEvent(forced, name, data) {
|
||||
|
||||
define(
|
||||
['mailpoet', 'underscore'],
|
||||
function(mp, _) {
|
||||
function analyticsEvent(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);
|
||||
|
||||
|
16
assets/js/src/common/loading.jsx
Normal file
16
assets/js/src/common/loading.jsx
Normal file
@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import MailPoet from 'mailpoet';
|
||||
|
||||
class Loading extends React.Component {
|
||||
componentWillMount() {
|
||||
MailPoet.Modal.loading(true);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
MailPoet.Modal.loading(false);
|
||||
}
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default Loading;
|
76
assets/js/src/common/thumbnail.jsx
Normal file
76
assets/js/src/common/thumbnail.jsx
Normal file
@ -0,0 +1,76 @@
|
||||
import _ from 'underscore';
|
||||
import MailPoet from 'mailpoet';
|
||||
import html2canvas from 'html2canvas';
|
||||
|
||||
/**
|
||||
* Generates a thumbnail from a DOM element.
|
||||
*
|
||||
* @param {DOMElement} element
|
||||
* @return {Promise<String>} DataURL of the generated image.
|
||||
*/
|
||||
export const fromDom = element =>
|
||||
html2canvas(element, {
|
||||
logging: false,
|
||||
}).then(canvas => canvas.toDataURL('image/jpeg'));
|
||||
|
||||
/**
|
||||
* Generates a thumbnail from an URL.
|
||||
*
|
||||
* @param {String} url
|
||||
* @return {Promise<String>} DataURL of the generated image.
|
||||
*/
|
||||
export const fromUrl = url =>
|
||||
new Promise((resolve, reject) => {
|
||||
const iframe = document.createElement('iframe');
|
||||
const protocol = location.href.startsWith('https://') ? 'https:' : 'http:';
|
||||
iframe.src = protocol + url.replace(/^https?:/, '');
|
||||
iframe.style.opacity = 0;
|
||||
iframe.scrolling = 'no';
|
||||
iframe.onload = () => {
|
||||
fromDom(iframe.contentDocument.documentElement)
|
||||
.then((image) => {
|
||||
document.body.removeChild(iframe);
|
||||
resolve(image);
|
||||
})
|
||||
.catch(() => {
|
||||
document.body.removeChild(iframe);
|
||||
reject(MailPoet.I18n.t('errorWhileTakingScreenshot'));
|
||||
});
|
||||
};
|
||||
const onError = () => {
|
||||
document.body.removeChild(iframe);
|
||||
reject(MailPoet.I18n.t('errorWhileTakingScreenshot'));
|
||||
};
|
||||
iframe.onerror = onError;
|
||||
iframe.onError = onError;
|
||||
iframe.className = 'mailpoet_template_iframe';
|
||||
try {
|
||||
document.body.appendChild(iframe);
|
||||
} catch (err) {
|
||||
onError();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Generates a thumbnail from a newsletter's data.
|
||||
*
|
||||
* @param {Object} data
|
||||
* @return {Promise<String>} DataURL of the generated image.
|
||||
*/
|
||||
export const fromNewsletter = data =>
|
||||
new Promise((resolve, reject) => {
|
||||
const json = data;
|
||||
if (!_.isUndefined(json.body)) {
|
||||
json.body = JSON.stringify(json.body);
|
||||
}
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'newsletters',
|
||||
action: 'showPreview',
|
||||
data: json,
|
||||
}).done(response => fromUrl(response.meta.preview_url)
|
||||
.then(resolve)
|
||||
.catch(reject)
|
||||
).fail(response => reject(response.errors));
|
||||
});
|
||||
|
@ -3,167 +3,169 @@ define('date',
|
||||
'mailpoet',
|
||||
'jquery',
|
||||
'moment'
|
||||
], function(
|
||||
], function ( // eslint-disable-line func-names
|
||||
mp,
|
||||
jQuery,
|
||||
Moment
|
||||
) {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
var MailPoet = mp;
|
||||
var MailPoet = mp;
|
||||
|
||||
MailPoet.Date = {
|
||||
version: 0.1,
|
||||
options: {},
|
||||
defaults: {
|
||||
offset: 0,
|
||||
format: 'F, d Y H:i:s'
|
||||
},
|
||||
init: function (opts) {
|
||||
var options = opts || {};
|
||||
MailPoet.Date = {
|
||||
version: 0.1,
|
||||
options: {},
|
||||
defaults: {
|
||||
offset: 0,
|
||||
format: 'F, d Y H:i:s'
|
||||
},
|
||||
init: function init(opts) {
|
||||
var options = opts || {};
|
||||
|
||||
// set UTC offset
|
||||
if (
|
||||
if (
|
||||
options.offset === undefined
|
||||
&& window.mailpoet_date_offset !== undefined
|
||||
) {
|
||||
options.offset = window.mailpoet_date_offset;
|
||||
}
|
||||
options.offset = window.mailpoet_date_offset;
|
||||
}
|
||||
// set date format
|
||||
if (
|
||||
if (
|
||||
options.format === undefined
|
||||
&& window.mailpoet_date_format !== undefined
|
||||
) {
|
||||
options.format = window.mailpoet_date_format;
|
||||
}
|
||||
// merge options
|
||||
this.options = jQuery.extend({}, this.defaults, options);
|
||||
|
||||
return this;
|
||||
},
|
||||
format: function(date, opts) {
|
||||
var options = opts || {};
|
||||
this.init(options);
|
||||
|
||||
var 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);
|
||||
|
||||
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'
|
||||
options.format = window.mailpoet_date_format;
|
||||
}
|
||||
};
|
||||
// merge options
|
||||
this.options = jQuery.extend({}, this.defaults, options);
|
||||
|
||||
if (!format || format.length <= 0) return format;
|
||||
return this;
|
||||
},
|
||||
format: function format(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 toDate(date, opts) {
|
||||
var options = opts || {};
|
||||
this.init(options);
|
||||
|
||||
var convertedFormat = [];
|
||||
var escapeToken = false;
|
||||
return Moment(date, this.convertFormat(options.parseFormat)).toDate();
|
||||
},
|
||||
short: function short(date) {
|
||||
return this.format(date, {
|
||||
format: 'F, j Y'
|
||||
});
|
||||
},
|
||||
full: function full(date) {
|
||||
return this.format(date, {
|
||||
format: 'F, j Y H:i:s'
|
||||
});
|
||||
},
|
||||
time: function time(date) {
|
||||
return this.format(date, {
|
||||
format: 'H:i:s'
|
||||
});
|
||||
},
|
||||
convertFormat: function convertFormat(format) {
|
||||
var replacements;
|
||||
var convertedFormat;
|
||||
var escapeToken;
|
||||
var index;
|
||||
var token;
|
||||
var formatMappings = {
|
||||
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 = ''; format.charAt(index); index += 1){
|
||||
token = format.charAt(index);
|
||||
if (escapeToken === true) {
|
||||
convertedFormat.push('['+token+']');
|
||||
escapeToken = false;
|
||||
} else {
|
||||
if (token === '\\') {
|
||||
if (!format || format.length <= 0) return format;
|
||||
|
||||
replacements = formatMappings.date;
|
||||
convertedFormat = [];
|
||||
escapeToken = false;
|
||||
|
||||
for (index = 0, token = ''; format.charAt(index); index += 1) {
|
||||
token = format.charAt(index);
|
||||
if (escapeToken === true) {
|
||||
convertedFormat.push('[' + token + ']');
|
||||
escapeToken = false;
|
||||
} else 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+']');
|
||||
convertedFormat.push('[' + token + ']');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return convertedFormat.join('');
|
||||
}
|
||||
};
|
||||
});
|
||||
return convertedFormat.join('');
|
||||
}
|
||||
};
|
||||
});
|
||||
|
@ -1,49 +1,43 @@
|
||||
define([
|
||||
'react',
|
||||
],
|
||||
(
|
||||
React
|
||||
) => {
|
||||
const FormFieldCheckbox = React.createClass({
|
||||
onValueChange: function (e) {
|
||||
e.target.value = this.refs.checkbox.checked ? '1' : '0';
|
||||
return this.props.onValueChange(e);
|
||||
},
|
||||
render: function () {
|
||||
if (this.props.field.values === undefined) {
|
||||
return false;
|
||||
}
|
||||
import React from 'react';
|
||||
|
||||
// isChecked will be true only if the value is "1"
|
||||
// it will be false in case value is "0" or empty
|
||||
const isChecked = !!(~~(this.props.item[this.props.field.name]));
|
||||
const options = Object.keys(this.props.field.values).map(
|
||||
(value, index) => {
|
||||
return (
|
||||
<p key={ 'checkbox-' + index }>
|
||||
<label>
|
||||
<input
|
||||
ref="checkbox"
|
||||
type="checkbox"
|
||||
value="1"
|
||||
checked={ isChecked }
|
||||
onChange={ this.onValueChange }
|
||||
name={ this.props.field.name }
|
||||
/>
|
||||
{ this.props.field.values[value] }
|
||||
</label>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
);
|
||||
const FormFieldCheckbox = React.createClass({
|
||||
onValueChange: function onValueChange(e) {
|
||||
e.target.value = this.checkbox.checked ? '1' : '0';
|
||||
return this.props.onValueChange(e);
|
||||
},
|
||||
render: function render() {
|
||||
if (this.props.field.values === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ options }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
// isChecked will be true only if the value is "1"
|
||||
// it will be false in case value is "0" or empty
|
||||
const isChecked = !!(Number(this.props.item[this.props.field.name]));
|
||||
const options = Object.keys(this.props.field.values).map(
|
||||
value => (
|
||||
<p key={`checkbox-${value}`}>
|
||||
<label htmlFor={this.props.field.name}>
|
||||
<input
|
||||
ref={(c) => { this.checkbox = c; }}
|
||||
type="checkbox"
|
||||
value="1"
|
||||
checked={isChecked}
|
||||
onChange={this.onValueChange}
|
||||
name={this.props.field.name}
|
||||
id={this.props.field.name}
|
||||
/>
|
||||
{ this.props.field.values[value] }
|
||||
</label>
|
||||
</p>
|
||||
)
|
||||
);
|
||||
|
||||
return FormFieldCheckbox;
|
||||
return (
|
||||
<div>
|
||||
{ options }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default FormFieldCheckbox;
|
||||
|
@ -1,251 +1,279 @@
|
||||
define([
|
||||
'react',
|
||||
'moment',
|
||||
], (
|
||||
React,
|
||||
Moment
|
||||
) => {
|
||||
class FormFieldDateYear extends React.Component {
|
||||
render() {
|
||||
const yearsRange = 100;
|
||||
const years = [];
|
||||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
if (this.props.placeholder !== undefined) {
|
||||
years.push((
|
||||
<option value="" key={ 0 }>{ this.props.placeholder }</option>
|
||||
));
|
||||
}
|
||||
function FormFieldDateYear(props) {
|
||||
const yearsRange = 100;
|
||||
const years = [];
|
||||
|
||||
const currentYear = Moment().year();
|
||||
for (let i = currentYear; i >= currentYear - yearsRange; i -= 1) {
|
||||
years.push((
|
||||
<option
|
||||
key={ i }
|
||||
value={ i }
|
||||
>{ i }</option>
|
||||
));
|
||||
}
|
||||
return (
|
||||
<select
|
||||
name={ `${this.props.name}[year]` }
|
||||
value={ this.props.year }
|
||||
onChange={ this.props.onValueChange }
|
||||
>
|
||||
{ years }
|
||||
</select>
|
||||
);
|
||||
}
|
||||
if (props.placeholder !== undefined) {
|
||||
years.push((
|
||||
<option value="" key={0}>{ props.placeholder }</option>
|
||||
));
|
||||
}
|
||||
|
||||
class FormFieldDateMonth extends React.Component {
|
||||
render() {
|
||||
const months = [];
|
||||
const currentYear = moment().year();
|
||||
for (let i = currentYear; i >= currentYear - yearsRange; i -= 1) {
|
||||
years.push((
|
||||
<option
|
||||
key={i}
|
||||
value={i}
|
||||
>{ i }</option>
|
||||
));
|
||||
}
|
||||
return (
|
||||
<select
|
||||
name={`${props.name}[year]`}
|
||||
value={props.year}
|
||||
onChange={props.onValueChange}
|
||||
>
|
||||
{ years }
|
||||
</select>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.placeholder !== undefined) {
|
||||
months.push((
|
||||
<option value="" key={ 0 }>{ this.props.placeholder }</option>
|
||||
));
|
||||
}
|
||||
FormFieldDateYear.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
placeholder: PropTypes.string.isRequired,
|
||||
onValueChange: PropTypes.func.isRequired,
|
||||
year: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
for (let i = 1; i <= 12; i += 1) {
|
||||
months.push((
|
||||
<option
|
||||
key={ i }
|
||||
value={ i }
|
||||
>{ this.props.monthNames[i - 1] }</option>
|
||||
));
|
||||
}
|
||||
return (
|
||||
<select
|
||||
name={ `${this.props.name}[month]` }
|
||||
value={ this.props.month }
|
||||
onChange={ this.props.onValueChange }
|
||||
>
|
||||
{ months }
|
||||
</select>
|
||||
);
|
||||
}
|
||||
function FormFieldDateMonth(props) {
|
||||
const months = [];
|
||||
|
||||
if (props.placeholder !== undefined) {
|
||||
months.push((
|
||||
<option value="" key={0}>{ props.placeholder }</option>
|
||||
));
|
||||
}
|
||||
|
||||
class FormFieldDateDay extends React.Component {
|
||||
render() {
|
||||
const days = [];
|
||||
for (let i = 1; i <= 12; i += 1) {
|
||||
months.push((
|
||||
<option
|
||||
key={i}
|
||||
value={i}
|
||||
>{ props.monthNames[i - 1] }</option>
|
||||
));
|
||||
}
|
||||
return (
|
||||
<select
|
||||
name={`${props.name}[month]`}
|
||||
value={props.month}
|
||||
onChange={props.onValueChange}
|
||||
>
|
||||
{ months }
|
||||
</select>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.placeholder !== undefined) {
|
||||
days.push((
|
||||
<option value="" key={ 0 }>{ this.props.placeholder }</option>
|
||||
));
|
||||
}
|
||||
FormFieldDateMonth.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
placeholder: PropTypes.string.isRequired,
|
||||
onValueChange: PropTypes.func.isRequired,
|
||||
month: PropTypes.string.isRequired,
|
||||
monthNames: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
};
|
||||
|
||||
for (let i = 1; i <= 31; i += 1) {
|
||||
days.push((
|
||||
<option
|
||||
key={ i }
|
||||
value={ i }
|
||||
>{ i }</option>
|
||||
));
|
||||
}
|
||||
function FormFieldDateDay(props) {
|
||||
const days = [];
|
||||
|
||||
return (
|
||||
<select
|
||||
name={ `${this.props.name}[day]` }
|
||||
value={ this.props.day }
|
||||
onChange={ this.props.onValueChange }
|
||||
>
|
||||
{ days }
|
||||
</select>
|
||||
);
|
||||
}
|
||||
if (props.placeholder !== undefined) {
|
||||
days.push((
|
||||
<option value="" key={0}>{ props.placeholder }</option>
|
||||
));
|
||||
}
|
||||
|
||||
class FormFieldDate extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
year: '',
|
||||
month: '',
|
||||
day: '',
|
||||
};
|
||||
}
|
||||
componentDidMount() {
|
||||
for (let i = 1; i <= 31; i += 1) {
|
||||
days.push((
|
||||
<option
|
||||
key={i}
|
||||
value={i}
|
||||
>{ i }</option>
|
||||
));
|
||||
}
|
||||
|
||||
return (
|
||||
<select
|
||||
name={`${props.name}[day]`}
|
||||
value={props.day}
|
||||
onChange={props.onValueChange}
|
||||
>
|
||||
{ days }
|
||||
</select>
|
||||
);
|
||||
}
|
||||
|
||||
FormFieldDateDay.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
placeholder: PropTypes.string.isRequired,
|
||||
onValueChange: PropTypes.func.isRequired,
|
||||
day: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
class FormFieldDate extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
year: '',
|
||||
month: '',
|
||||
day: '',
|
||||
};
|
||||
|
||||
this.onValueChange = this.onValueChange.bind(this);
|
||||
}
|
||||
componentDidMount() {
|
||||
this.extractDateParts();
|
||||
}
|
||||
componentDidUpdate(prevProps) {
|
||||
if (
|
||||
(this.props.item !== undefined && prevProps.item !== undefined)
|
||||
&& (this.props.item.id !== prevProps.item.id)
|
||||
) {
|
||||
this.extractDateParts();
|
||||
}
|
||||
componentDidUpdate(prevProps) {
|
||||
if (
|
||||
(this.props.item !== undefined && prevProps.item !== undefined)
|
||||
&& (this.props.item.id !== prevProps.item.id)
|
||||
) {
|
||||
this.extractDateParts();
|
||||
}
|
||||
}
|
||||
extractDateParts() {
|
||||
const value = (this.props.item[this.props.field.name] !== undefined)
|
||||
? this.props.item[this.props.field.name].trim()
|
||||
: '';
|
||||
}
|
||||
onValueChange(e) {
|
||||
// extract property from name
|
||||
const matches = e.target.name.match(/(.*?)\[(.*?)\]/);
|
||||
let field = null;
|
||||
let property = null;
|
||||
|
||||
if(value === '') {
|
||||
return;
|
||||
}
|
||||
if (matches !== null && matches.length === 3) {
|
||||
field = matches[1];
|
||||
property = matches[2];
|
||||
|
||||
const dateTime = Moment(value);
|
||||
const value = Number(e.target.value);
|
||||
|
||||
this.setState({
|
||||
year: dateTime.format('YYYY'),
|
||||
month: dateTime.format('M'),
|
||||
day: dateTime.format('D'),
|
||||
});
|
||||
}
|
||||
formatValue() {
|
||||
const dateType = this.props.field.params.date_type;
|
||||
|
||||
let value;
|
||||
|
||||
switch(dateType) {
|
||||
case 'year_month_day':
|
||||
value = {
|
||||
year: this.state.year,
|
||||
month: this.state.month,
|
||||
day: this.state.day,
|
||||
};
|
||||
break;
|
||||
|
||||
case 'year_month':
|
||||
value = {
|
||||
year: this.state.year,
|
||||
month: this.state.month,
|
||||
};
|
||||
break;
|
||||
|
||||
case 'month':
|
||||
value = {
|
||||
month: this.state.month,
|
||||
};
|
||||
break;
|
||||
|
||||
case 'year':
|
||||
value = {
|
||||
year: this.state.year,
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
onValueChange(e) {
|
||||
// extract property from name
|
||||
const matches = e.target.name.match(/(.*?)\[(.*?)\]/);
|
||||
let field = null;
|
||||
let property = null;
|
||||
|
||||
if (matches !== null && matches.length === 3) {
|
||||
field = matches[1];
|
||||
property = matches[2];
|
||||
|
||||
const value = ~~(e.target.value);
|
||||
|
||||
this.setState({
|
||||
[`${property}`]: value,
|
||||
}, () => {
|
||||
this.props.onValueChange({
|
||||
target: {
|
||||
name: field,
|
||||
value: this.formatValue(),
|
||||
},
|
||||
});
|
||||
[`${property}`]: value,
|
||||
}, () => {
|
||||
this.props.onValueChange({
|
||||
target: {
|
||||
name: field,
|
||||
value: this.formatValue(),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const monthNames = window.mailpoet_month_names || [];
|
||||
const dateFormats = window.mailpoet_date_formats || {};
|
||||
const dateType = this.props.field.params.date_type;
|
||||
const dateSelects = dateFormats[dateType][0].split('/');
|
||||
|
||||
const fields = dateSelects.map((type) => {
|
||||
switch(type) {
|
||||
case 'YYYY':
|
||||
return (<FormFieldDateYear
|
||||
onValueChange={ this.onValueChange.bind(this) }
|
||||
ref={ 'year' }
|
||||
key={ 'year' }
|
||||
name={ this.props.field.name }
|
||||
year={ this.state.year }
|
||||
placeholder={ this.props.field.year_placeholder }
|
||||
/>);
|
||||
break;
|
||||
|
||||
case 'MM':
|
||||
return (<FormFieldDateMonth
|
||||
onValueChange={ this.onValueChange.bind(this) }
|
||||
ref={ 'month' }
|
||||
key={ 'month' }
|
||||
name={ this.props.field.name }
|
||||
month={ this.state.month }
|
||||
monthNames={ monthNames }
|
||||
placeholder={ this.props.field.month_placeholder }
|
||||
/>);
|
||||
break;
|
||||
|
||||
case 'DD':
|
||||
return (<FormFieldDateDay
|
||||
onValueChange={ this.onValueChange.bind(this) }
|
||||
ref={ 'day' }
|
||||
key={ 'day' }
|
||||
name={ this.props.field.name }
|
||||
day={ this.state.day }
|
||||
placeholder={ this.props.field.day_placeholder }
|
||||
/>);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
{fields}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
formatValue() {
|
||||
const dateType = this.props.field.params.date_type;
|
||||
|
||||
return FormFieldDate;
|
||||
});
|
||||
let value;
|
||||
|
||||
switch (dateType) {
|
||||
case 'year_month_day':
|
||||
value = {
|
||||
year: this.state.year,
|
||||
month: this.state.month,
|
||||
day: this.state.day,
|
||||
};
|
||||
break;
|
||||
|
||||
case 'year_month':
|
||||
value = {
|
||||
year: this.state.year,
|
||||
month: this.state.month,
|
||||
};
|
||||
break;
|
||||
|
||||
case 'month':
|
||||
value = {
|
||||
month: this.state.month,
|
||||
};
|
||||
break;
|
||||
|
||||
case 'year':
|
||||
value = {
|
||||
year: this.state.year,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
value = {
|
||||
value: 'invalid type',
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
extractDateParts() {
|
||||
const value = (this.props.item[this.props.field.name] !== undefined)
|
||||
? this.props.item[this.props.field.name].trim()
|
||||
: '';
|
||||
|
||||
if (value === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
const dateTime = moment(value);
|
||||
|
||||
this.setState({
|
||||
year: dateTime.format('YYYY'),
|
||||
month: dateTime.format('M'),
|
||||
day: dateTime.format('D'),
|
||||
});
|
||||
}
|
||||
render() {
|
||||
const monthNames = window.mailpoet_month_names || [];
|
||||
const dateFormats = window.mailpoet_date_formats || {};
|
||||
const dateType = this.props.field.params.date_type;
|
||||
const dateSelects = dateFormats[dateType][0].split('/');
|
||||
|
||||
const fields = dateSelects.map((type) => {
|
||||
switch (type) {
|
||||
case 'YYYY':
|
||||
return (<FormFieldDateYear
|
||||
onValueChange={this.onValueChange}
|
||||
key={'year'}
|
||||
name={this.props.field.name}
|
||||
year={this.state.year}
|
||||
placeholder={this.props.field.year_placeholder}
|
||||
/>);
|
||||
|
||||
case 'MM':
|
||||
return (<FormFieldDateMonth
|
||||
onValueChange={this.onValueChange}
|
||||
key={'month'}
|
||||
name={this.props.field.name}
|
||||
month={this.state.month}
|
||||
monthNames={monthNames}
|
||||
placeholder={this.props.field.month_placeholder}
|
||||
/>);
|
||||
|
||||
case 'DD':
|
||||
return (<FormFieldDateDay
|
||||
onValueChange={this.onValueChange}
|
||||
key={'day'}
|
||||
name={this.props.field.name}
|
||||
day={this.state.day}
|
||||
placeholder={this.props.field.day_placeholder}
|
||||
/>);
|
||||
|
||||
default:
|
||||
return <div>Invalid date type</div>;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
{fields}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
FormFieldDate.propTypes = {
|
||||
item: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
field: PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
day_placeholder: PropTypes.string,
|
||||
month_placeholder: PropTypes.string,
|
||||
year_placeholder: PropTypes.string,
|
||||
params: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||
}).isRequired,
|
||||
onValueChange: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default FormFieldDate;
|
||||
|
@ -1,129 +1,119 @@
|
||||
define([
|
||||
'react',
|
||||
'form/fields/text.jsx',
|
||||
'form/fields/textarea.jsx',
|
||||
'form/fields/select.jsx',
|
||||
'form/fields/radio.jsx',
|
||||
'form/fields/checkbox.jsx',
|
||||
'form/fields/selection.jsx',
|
||||
'form/fields/date.jsx',
|
||||
],
|
||||
(
|
||||
React,
|
||||
FormFieldText,
|
||||
FormFieldTextarea,
|
||||
FormFieldSelect,
|
||||
FormFieldRadio,
|
||||
FormFieldCheckbox,
|
||||
FormFieldSelection,
|
||||
FormFieldDate
|
||||
) => {
|
||||
const FormField = React.createClass({
|
||||
renderField: function (data, inline = false) {
|
||||
let description = false;
|
||||
if(data.field.description) {
|
||||
description = (
|
||||
<p className="description">{ data.field.description }</p>
|
||||
);
|
||||
}
|
||||
import React from 'react';
|
||||
import FormFieldText from 'form/fields/text.jsx';
|
||||
import FormFieldTextarea from 'form/fields/textarea.jsx';
|
||||
import FormFieldSelect from 'form/fields/select.jsx';
|
||||
import FormFieldRadio from 'form/fields/radio.jsx';
|
||||
import FormFieldCheckbox from 'form/fields/checkbox.jsx';
|
||||
import FormFieldSelection from 'form/fields/selection.jsx';
|
||||
import FormFieldDate from 'form/fields/date.jsx';
|
||||
import jQuery from 'jquery';
|
||||
|
||||
let field = false;
|
||||
let dataField = data.field;
|
||||
|
||||
if(data.field['field'] !== undefined) {
|
||||
dataField = jQuery.merge(dataField, data.field.field);
|
||||
}
|
||||
|
||||
switch(dataField.type) {
|
||||
case 'text':
|
||||
field = (<FormFieldText {...data} />);
|
||||
break;
|
||||
|
||||
case 'textarea':
|
||||
field = (<FormFieldTextarea {...data} />);
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
field = (<FormFieldSelect {...data} />);
|
||||
break;
|
||||
|
||||
case 'radio':
|
||||
field = (<FormFieldRadio {...data} />);
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
field = (<FormFieldCheckbox {...data} />);
|
||||
break;
|
||||
|
||||
case 'selection':
|
||||
field = (<FormFieldSelection {...data} />);
|
||||
break;
|
||||
|
||||
case 'date':
|
||||
field = (<FormFieldDate {...data} />);
|
||||
break;
|
||||
|
||||
case 'reactComponent':
|
||||
field = (<data.field.component {...data} />);
|
||||
break;
|
||||
}
|
||||
|
||||
if(inline === true) {
|
||||
return (
|
||||
<span key={ 'field-' + (data.index || 0) }>
|
||||
{ field }
|
||||
{ description }
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div key={ 'field-' + (data.index || 0) }>
|
||||
{ field }
|
||||
{ description }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
render: function () {
|
||||
let field = false;
|
||||
|
||||
if(this.props.field['fields'] !== undefined) {
|
||||
field = this.props.field.fields.map((subfield, index) => {
|
||||
return this.renderField({
|
||||
index: index,
|
||||
field: subfield,
|
||||
item: this.props.item,
|
||||
onValueChange: this.props.onValueChange || false,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
field = this.renderField(this.props);
|
||||
}
|
||||
|
||||
let tip = false;
|
||||
if(this.props.field.tip) {
|
||||
tip = (
|
||||
<p className="description">{ this.props.field.tip }</p>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label
|
||||
htmlFor={ 'field_'+this.props.field.name }
|
||||
>
|
||||
{ this.props.field.label }
|
||||
{ tip }
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
{ field }
|
||||
</td>
|
||||
</tr>
|
||||
const FormField = React.createClass({
|
||||
renderField: function renderField(data, inline = false) {
|
||||
let description = false;
|
||||
if (data.field.description) {
|
||||
description = (
|
||||
<p className="description">{ data.field.description }</p>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return FormField;
|
||||
let field = false;
|
||||
let dataField = data.field;
|
||||
|
||||
if (data.field.field !== undefined) {
|
||||
dataField = jQuery.merge(dataField, data.field.field);
|
||||
}
|
||||
|
||||
switch (dataField.type) {
|
||||
case 'text':
|
||||
field = (<FormFieldText {...data} />);
|
||||
break;
|
||||
|
||||
case 'textarea':
|
||||
field = (<FormFieldTextarea {...data} />);
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
field = (<FormFieldSelect {...data} />);
|
||||
break;
|
||||
|
||||
case 'radio':
|
||||
field = (<FormFieldRadio {...data} />);
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
field = (<FormFieldCheckbox {...data} />);
|
||||
break;
|
||||
|
||||
case 'selection':
|
||||
field = (<FormFieldSelection {...data} />);
|
||||
break;
|
||||
|
||||
case 'date':
|
||||
field = (<FormFieldDate {...data} />);
|
||||
break;
|
||||
|
||||
case 'reactComponent':
|
||||
field = (<data.field.component {...data} />);
|
||||
break;
|
||||
|
||||
default:
|
||||
field = 'invalid';
|
||||
break;
|
||||
}
|
||||
|
||||
if (inline === true) {
|
||||
return (
|
||||
<span key={`field-${data.index || 0}`}>
|
||||
{ field }
|
||||
{ description }
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div key={`field-${data.index || 0}`}>
|
||||
{ field }
|
||||
{ description }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
render: function render() {
|
||||
let field = false;
|
||||
|
||||
if (this.props.field.fields !== undefined) {
|
||||
field = this.props.field.fields.map((subfield, index) => this.renderField({
|
||||
index,
|
||||
field: subfield,
|
||||
item: this.props.item,
|
||||
onValueChange: this.props.onValueChange || false,
|
||||
}));
|
||||
} else {
|
||||
field = this.renderField(this.props);
|
||||
}
|
||||
|
||||
let tip = false;
|
||||
if (this.props.field.tip) {
|
||||
tip = (
|
||||
<p className="description">{ this.props.field.tip }</p>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<tr className={`form-field-row-${this.props.field.name}`}>
|
||||
<th scope="row">
|
||||
<label
|
||||
htmlFor={`field_${this.props.field.name}`}
|
||||
>
|
||||
{ this.props.field.label }
|
||||
{ tip }
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
{ field }
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default FormField;
|
||||
|
@ -1,41 +1,36 @@
|
||||
define([
|
||||
'react',
|
||||
],
|
||||
(
|
||||
React
|
||||
) => {
|
||||
const FormFieldRadio = React.createClass({
|
||||
render: function () {
|
||||
if (this.props.field.values === undefined) {
|
||||
return false;
|
||||
}
|
||||
import React from 'react';
|
||||
|
||||
const selected_value = this.props.item[this.props.field.name];
|
||||
const options = Object.keys(this.props.field.values).map(
|
||||
(value, index) => {
|
||||
return (
|
||||
<p key={ 'radio-' + index }>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
checked={ selected_value === value }
|
||||
value={ value }
|
||||
onChange={ this.props.onValueChange }
|
||||
name={ this.props.field.name } />
|
||||
{ this.props.field.values[value] }
|
||||
</label>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
);
|
||||
const FormFieldRadio = React.createClass({
|
||||
render: function render() {
|
||||
if (this.props.field.values === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ options }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
const selectedValue = this.props.item[this.props.field.name];
|
||||
const options = Object.keys(this.props.field.values).map(
|
||||
value => (
|
||||
<p key={`radio-${value}`}>
|
||||
<label htmlFor={this.props.field.name}>
|
||||
<input
|
||||
type="radio"
|
||||
checked={selectedValue === value}
|
||||
value={value}
|
||||
onChange={this.props.onValueChange}
|
||||
name={this.props.field.name}
|
||||
id={this.props.field.name}
|
||||
/>
|
||||
{ this.props.field.values[value] }
|
||||
</label>
|
||||
</p>
|
||||
)
|
||||
);
|
||||
|
||||
return FormFieldRadio;
|
||||
return (
|
||||
<div>
|
||||
{ options }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default FormFieldRadio;
|
||||
|
@ -17,7 +17,7 @@ const FormFieldSelect = React.createClass({
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.field['filter'] !== undefined) {
|
||||
if (this.props.field.filter !== undefined) {
|
||||
filter = this.props.field.filter;
|
||||
}
|
||||
|
||||
@ -41,29 +41,28 @@ const FormFieldSelect = React.createClass({
|
||||
keys = Object.keys(this.props.field.values);
|
||||
}
|
||||
|
||||
const options = keys.map(
|
||||
(value, index) => {
|
||||
|
||||
if (filter !== false && filter(this.props.item, value) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
const options = keys
|
||||
.filter((value) => {
|
||||
if (filter === false) return true;
|
||||
return filter(this.props.item, value);
|
||||
})
|
||||
.map(
|
||||
value => (
|
||||
<option
|
||||
key={ 'option-' + index }
|
||||
value={ value }>
|
||||
key={`option-${value}`}
|
||||
value={value}
|
||||
>
|
||||
{ this.props.field.values[value] }
|
||||
</option>
|
||||
);
|
||||
}
|
||||
);
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
<select
|
||||
name={ this.props.field.name }
|
||||
id={ 'field_'+this.props.field.name }
|
||||
value={ this.props.item[this.props.field.name] }
|
||||
onChange={ this.props.onValueChange }
|
||||
name={this.props.field.name}
|
||||
id={`field_${this.props.field.name}`}
|
||||
value={this.props.item[this.props.field.name] || ''}
|
||||
onChange={this.props.onValueChange}
|
||||
{...this.props.field.validation}
|
||||
>
|
||||
{placeholder}
|
||||
|
@ -1,194 +1,261 @@
|
||||
define([
|
||||
'react',
|
||||
'react-dom',
|
||||
'jquery',
|
||||
'select2',
|
||||
],
|
||||
(
|
||||
React,
|
||||
ReactDOM,
|
||||
jQuery
|
||||
) => {
|
||||
const Selection = React.createClass({
|
||||
getInitialState: function () {
|
||||
return {
|
||||
items: [],
|
||||
select2: false,
|
||||
};
|
||||
},
|
||||
componentWillMount: function () {
|
||||
this.loadCachedItems();
|
||||
},
|
||||
allowMultipleValues: function () {
|
||||
return (this.props.field.multiple === true);
|
||||
},
|
||||
isSelect2Initialized: function () {
|
||||
return (this.state.select2 === true);
|
||||
},
|
||||
componentDidMount: function () {
|
||||
if(this.allowMultipleValues()) {
|
||||
this.setupSelect2();
|
||||
}
|
||||
},
|
||||
componentDidUpdate: function (prevProps) {
|
||||
if(
|
||||
(this.props.item !== undefined && prevProps.item !== undefined)
|
||||
&& (this.props.item.id !== prevProps.item.id)
|
||||
) {
|
||||
jQuery('#'+this.refs.select.id)
|
||||
.val(this.getSelectedValues())
|
||||
.trigger('change');
|
||||
}
|
||||
},
|
||||
componentWillUnmount: function () {
|
||||
if(this.allowMultipleValues()) {
|
||||
this.destroySelect2();
|
||||
}
|
||||
},
|
||||
destroySelect2: function () {
|
||||
if(this.isSelect2Initialized()) {
|
||||
jQuery('#'+this.refs.select.id).select2('destroy');
|
||||
}
|
||||
},
|
||||
setupSelect2: function () {
|
||||
if(this.isSelect2Initialized()) {
|
||||
return;
|
||||
}
|
||||
import React from 'react';
|
||||
import jQuery from 'jquery';
|
||||
import _ from 'underscore';
|
||||
import 'react-dom';
|
||||
import 'select2';
|
||||
|
||||
const select2 = jQuery('#'+this.refs.select.id).select2({
|
||||
width: (this.props.width || ''),
|
||||
templateResult: function (item) {
|
||||
if(item.element && item.element.selected) {
|
||||
return null;
|
||||
} else {
|
||||
if(item.title) {
|
||||
return item.title;
|
||||
} else {
|
||||
return item.text;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
const Selection = React.createClass({
|
||||
allowMultipleValues: function allowMultipleValues() {
|
||||
return (this.props.field.multiple === true);
|
||||
},
|
||||
isSelect2Initialized: function isSelect2Initialized() {
|
||||
return (jQuery(`#${this.select.id}`).hasClass('select2-hidden-accessible') === true);
|
||||
},
|
||||
isSelect2Component: function isSelect2Component() {
|
||||
return this.allowMultipleValues() || this.props.field.forceSelect2;
|
||||
},
|
||||
componentDidMount: function componentDidMount() {
|
||||
if (this.isSelect2Component()) {
|
||||
this.setupSelect2();
|
||||
}
|
||||
},
|
||||
componentDidUpdate: function componentDidUpdate(prevProps) {
|
||||
if ((this.props.item !== undefined && prevProps.item !== undefined)
|
||||
&& (this.props.item.id !== prevProps.item.id)
|
||||
) {
|
||||
jQuery(`#${this.select.id}`)
|
||||
.val(this.getSelectedValues())
|
||||
.trigger('change');
|
||||
}
|
||||
|
||||
let hasRemoved = false;
|
||||
select2.on('select2:unselecting', () => {
|
||||
hasRemoved = true;
|
||||
});
|
||||
select2.on('select2:opening', (e) => {
|
||||
if(hasRemoved === true) {
|
||||
hasRemoved = false;
|
||||
e.preventDefault();
|
||||
if (this.isSelect2Initialized() &&
|
||||
(this.getFieldId(this.props) !== this.getFieldId(prevProps)) &&
|
||||
this.props.field.resetSelect2OnUpdate !== undefined
|
||||
) {
|
||||
this.resetSelect2();
|
||||
}
|
||||
},
|
||||
componentWillUnmount: function componentWillUnmount() {
|
||||
if (this.isSelect2Component()) {
|
||||
this.destroySelect2();
|
||||
}
|
||||
},
|
||||
getFieldId: function getFieldId(data) {
|
||||
const props = data || this.props;
|
||||
return props.field.id || props.field.name;
|
||||
},
|
||||
resetSelect2: function resetSelect2() {
|
||||
this.destroySelect2();
|
||||
this.setupSelect2();
|
||||
},
|
||||
destroySelect2: function destroySelect2() {
|
||||
if (this.isSelect2Initialized()) {
|
||||
jQuery(`#${this.select.id}`).select2('destroy');
|
||||
this.cleanupAfterSelect2();
|
||||
}
|
||||
},
|
||||
cleanupAfterSelect2: function cleanupAfterSelect2() {
|
||||
// remove DOM elements created by Select2 that are not tracked by React
|
||||
jQuery(`#${this.select.id}`)
|
||||
.find('option:not(.default)')
|
||||
.remove();
|
||||
|
||||
// unbind events (https://select2.org/programmatic-control/methods#event-unbinding)
|
||||
jQuery(`#${this.select.id}`)
|
||||
.off('select2:unselecting')
|
||||
.off('select2:opening');
|
||||
},
|
||||
setupSelect2: function setupSelect2() {
|
||||
if (this.isSelect2Initialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let select2Options = {
|
||||
disabled: this.props.disabled || false,
|
||||
width: (this.props.width || ''),
|
||||
placeholder: {
|
||||
id: '', // the value of the option
|
||||
text: this.props.field.placeholder,
|
||||
},
|
||||
templateResult: function templateResult(item) {
|
||||
if (item.element && item.element.selected) {
|
||||
return null;
|
||||
} else if (item.title) {
|
||||
return item.title;
|
||||
}
|
||||
});
|
||||
return item.text;
|
||||
},
|
||||
};
|
||||
|
||||
select2.on('change', this.handleChange);
|
||||
|
||||
this.setState({ select2: true });
|
||||
},
|
||||
getSelectedValues: function () {
|
||||
if(this.props.field['selected'] !== undefined) {
|
||||
return this.props.field['selected'](this.props.item);
|
||||
} else if(this.props.item !== undefined && this.props.field.name !== undefined) {
|
||||
if (this.allowMultipleValues()) {
|
||||
if (Array.isArray(this.props.item[this.props.field.name])) {
|
||||
return this.props.item[this.props.field.name].map((item) => {
|
||||
return item.id;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return this.props.item[this.props.field.name];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
loadCachedItems: function () {
|
||||
if(typeof(window['mailpoet_'+this.props.field.endpoint]) !== 'undefined') {
|
||||
let items = window['mailpoet_'+this.props.field.endpoint];
|
||||
|
||||
|
||||
if(this.props.field['filter'] !== undefined) {
|
||||
items = items.filter(this.props.field.filter);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
items: items,
|
||||
});
|
||||
}
|
||||
},
|
||||
handleChange: function (e) {
|
||||
if(this.props.onValueChange !== undefined) {
|
||||
if(this.props.field.multiple) {
|
||||
value = jQuery('#'+this.refs.select.id).val();
|
||||
} else {
|
||||
value = e.target.value;
|
||||
}
|
||||
const transformedValue = this.transformChangedValue(value);
|
||||
this.props.onValueChange({
|
||||
target: {
|
||||
value: transformedValue,
|
||||
name: this.props.field.name,
|
||||
const remoteQuery = this.props.field.remoteQuery || null;
|
||||
if (remoteQuery) {
|
||||
select2Options = Object.assign(select2Options, {
|
||||
ajax: {
|
||||
url: window.ajaxurl,
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: function data(params) {
|
||||
return {
|
||||
action: 'mailpoet',
|
||||
api_version: window.mailpoet_api_version,
|
||||
token: window.mailpoet_token,
|
||||
endpoint: remoteQuery.endpoint,
|
||||
method: remoteQuery.method,
|
||||
data: Object.assign(
|
||||
remoteQuery.data,
|
||||
{ query: params.term }
|
||||
),
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
getLabel: function (item) {
|
||||
if(this.props.field['getLabel'] !== undefined) {
|
||||
return this.props.field.getLabel(item, this.props.item);
|
||||
}
|
||||
return item.name;
|
||||
},
|
||||
getSearchLabel: function (item) {
|
||||
if(this.props.field['getSearchLabel'] !== undefined) {
|
||||
return this.props.field.getSearchLabel(item, this.props.item);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
getValue: function (item) {
|
||||
if(this.props.field['getValue'] !== undefined) {
|
||||
return this.props.field.getValue(item, this.props.item);
|
||||
}
|
||||
return item.id;
|
||||
},
|
||||
// When it's impossible to represent the desired value in DOM,
|
||||
// this function may be used to transform the placeholder value into
|
||||
// desired value.
|
||||
transformChangedValue: function (value) {
|
||||
if(typeof this.props.field['transformChangedValue'] === 'function') {
|
||||
return this.props.field.transformChangedValue.call(this, value);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
},
|
||||
render: function () {
|
||||
const options = this.state.items.map((item, index) => {
|
||||
const label = this.getLabel(item);
|
||||
const searchLabel = this.getSearchLabel(item);
|
||||
const value = this.getValue(item);
|
||||
|
||||
return (
|
||||
<option
|
||||
key={ 'option-'+index }
|
||||
value={ value }
|
||||
title={ searchLabel }
|
||||
>
|
||||
{ label }
|
||||
</option>
|
||||
);
|
||||
processResults: function processResults(response) {
|
||||
return {
|
||||
results: response.data.map(item => (
|
||||
{ id: item.id || item.value, text: item.name || item.text }
|
||||
)),
|
||||
};
|
||||
},
|
||||
},
|
||||
minimumInputLength: remoteQuery.minimumInputLength || 2,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.props.field.extendSelect2Options !== undefined) {
|
||||
select2Options = Object.assign(select2Options, this.props.field.extendSelect2Options);
|
||||
}
|
||||
|
||||
const select2 = jQuery(`#${this.select.id}`).select2(select2Options);
|
||||
|
||||
let hasRemoved = false;
|
||||
select2.on('select2:unselecting', () => {
|
||||
hasRemoved = true;
|
||||
});
|
||||
select2.on('select2:opening', (e) => {
|
||||
if (hasRemoved === true) {
|
||||
hasRemoved = false;
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
select2.on('change', this.handleChange);
|
||||
},
|
||||
getSelectedValues: function getSelectedValues() {
|
||||
if (this.props.field.selected !== undefined) {
|
||||
return this.props.field.selected(this.props.item);
|
||||
} else if (this.props.item !== undefined && this.props.field.name !== undefined) {
|
||||
if (this.allowMultipleValues()) {
|
||||
if (_.isArray(this.props.item[this.props.field.name])) {
|
||||
return this.props.item[this.props.field.name].map(item => item.id);
|
||||
}
|
||||
} else {
|
||||
return this.props.item[this.props.field.name];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
getItems: function getItems() {
|
||||
let items;
|
||||
if (typeof (window[`mailpoet_${this.props.field.endpoint}`]) !== 'undefined') {
|
||||
items = window[`mailpoet_${this.props.field.endpoint}`];
|
||||
} else if (this.props.field.values !== undefined) {
|
||||
items = this.props.field.values;
|
||||
}
|
||||
|
||||
if (_.isArray(items)) {
|
||||
if (this.props.field.filter !== undefined) {
|
||||
items = items.filter(this.props.field.filter);
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
},
|
||||
handleChange: function handleChange(e) {
|
||||
if (this.props.onValueChange === undefined) return;
|
||||
|
||||
const valueTextPair = jQuery(`#${this.select.id}`).children(':selected').map(function element() {
|
||||
return { id: jQuery(this).val(), text: jQuery(this).text() };
|
||||
});
|
||||
const value = (this.props.field.multiple) ? _.pluck(valueTextPair, 'id') : _.pluck(valueTextPair, 'id').toString();
|
||||
const transformedValue = this.transformChangedValue(value, valueTextPair);
|
||||
|
||||
this.props.onValueChange({
|
||||
target: {
|
||||
value: transformedValue,
|
||||
name: this.props.field.name,
|
||||
id: e.target.id,
|
||||
},
|
||||
});
|
||||
},
|
||||
getLabel: function getLabel(item) {
|
||||
if (this.props.field.getLabel !== undefined) {
|
||||
return this.props.field.getLabel(item, this.props.item);
|
||||
}
|
||||
return item.name;
|
||||
},
|
||||
getSearchLabel: function getSearchLabel(item) {
|
||||
if (this.props.field.getSearchLabel !== undefined) {
|
||||
return this.props.field.getSearchLabel(item, this.props.item);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
getValue: function getValue(item) {
|
||||
if (this.props.field.getValue !== undefined) {
|
||||
return this.props.field.getValue(item, this.props.item);
|
||||
}
|
||||
return item.id;
|
||||
},
|
||||
// When it's impossible to represent the desired value in DOM,
|
||||
// this function may be used to transform the placeholder value into
|
||||
// desired value.
|
||||
transformChangedValue: function transformChangedValue(value, textValuePair) {
|
||||
if (typeof this.props.field.transformChangedValue === 'function') {
|
||||
return this.props.field.transformChangedValue.call(this, value, textValuePair);
|
||||
}
|
||||
return value;
|
||||
},
|
||||
insertEmptyOption: function insertEmptyOption() {
|
||||
// https://select2.org/placeholders
|
||||
// For single selects only, in order for the placeholder value to appear,
|
||||
// we must have a blank <option> as the first option in the <select> control.
|
||||
if (this.allowMultipleValues()) return undefined;
|
||||
if (this.props.field.placeholder) return (<option className="default" />);
|
||||
return undefined;
|
||||
},
|
||||
render: function render() {
|
||||
const items = this.getItems(this.props.field);
|
||||
const selectedValues = this.getSelectedValues();
|
||||
const options = items.map((item) => {
|
||||
const label = this.getLabel(item);
|
||||
const searchLabel = this.getSearchLabel(item);
|
||||
const value = this.getValue(item);
|
||||
|
||||
return (
|
||||
<select
|
||||
id={ this.props.field.id || this.props.field.name }
|
||||
ref="select"
|
||||
disabled={this.props.field.disabled}
|
||||
data-placeholder={ this.props.field.placeholder }
|
||||
multiple={ this.props.field.multiple }
|
||||
defaultValue={ this.getSelectedValues() }
|
||||
{...this.props.field.validation}
|
||||
>{ options }</select>
|
||||
<option
|
||||
key={`option-${item.id}`}
|
||||
className="default"
|
||||
value={value}
|
||||
title={searchLabel}
|
||||
selected={value === selectedValues}
|
||||
>
|
||||
{ label }
|
||||
</option>
|
||||
);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return Selection;
|
||||
return (
|
||||
<select
|
||||
id={this.getFieldId()}
|
||||
ref={(c) => { this.select = c; }}
|
||||
disabled={this.props.field.disabled}
|
||||
data-placeholder={this.props.field.placeholder}
|
||||
multiple={this.props.field.multiple}
|
||||
defaultValue={selectedValues}
|
||||
{...this.props.field.validation}
|
||||
>
|
||||
{ this.insertEmptyOption() }
|
||||
{ options }
|
||||
</select>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default Selection;
|
||||
|
@ -2,30 +2,52 @@ import React from 'react';
|
||||
|
||||
const FormFieldText = React.createClass({
|
||||
render() {
|
||||
let value = this.props.item[this.props.field.name];
|
||||
if (value === undefined) {
|
||||
value = this.props.field.defaultValue || '';
|
||||
const name = this.props.field.name || null;
|
||||
const item = this.props.item || {};
|
||||
let value;
|
||||
let defaultValue;
|
||||
// value should only be set when onChangeValue is configured
|
||||
if (this.props.onValueChange instanceof Function) {
|
||||
value = item[this.props.field.name];
|
||||
// set value to defaultValue if available
|
||||
value = (value === undefined && this.props.field.defaultValue) ?
|
||||
this.props.field.defaultValue : value;
|
||||
}
|
||||
// defaultValue should only be set only when value is not set
|
||||
if (!value && this.props.field.defaultValue) {
|
||||
defaultValue = this.props.field.defaultValue;
|
||||
}
|
||||
|
||||
let id = this.props.field.id || null;
|
||||
if (!id && this.props.field.name) {
|
||||
id = `field_${this.props.field.name}`;
|
||||
}
|
||||
|
||||
let className = this.props.field.class || null;
|
||||
if (!className && !this.props.field.size) {
|
||||
className = 'regular-text';
|
||||
}
|
||||
|
||||
return (
|
||||
<input
|
||||
type="text"
|
||||
disabled={
|
||||
(this.props.field['disabled'] !== undefined)
|
||||
(this.props.field.disabled !== undefined)
|
||||
? this.props.field.disabled(this.props.item)
|
||||
: false
|
||||
}
|
||||
className={ (this.props.field.size) ? '' : 'regular-text' }
|
||||
className={className}
|
||||
size={
|
||||
(this.props.field.size !== 'auto' && this.props.field.size > 0)
|
||||
? this.props.field.size
|
||||
: false
|
||||
}
|
||||
name={ this.props.field.name }
|
||||
id={ 'field_'+this.props.field.name }
|
||||
value={ value }
|
||||
placeholder={ this.props.field.placeholder }
|
||||
onChange={ this.props.onValueChange }
|
||||
name={name}
|
||||
id={id}
|
||||
value={value}
|
||||
defaultValue={defaultValue}
|
||||
placeholder={this.props.field.placeholder}
|
||||
onChange={this.props.onValueChange}
|
||||
{...this.props.field.validation}
|
||||
/>
|
||||
);
|
||||
|
@ -1,26 +1,29 @@
|
||||
define([
|
||||
'react',
|
||||
],
|
||||
(
|
||||
React
|
||||
) => {
|
||||
const FormFieldTextarea = React.createClass({
|
||||
render: function () {
|
||||
return (
|
||||
<textarea
|
||||
type="text"
|
||||
className="regular-text"
|
||||
name={ this.props.field.name }
|
||||
id={ 'field_'+this.props.field.name }
|
||||
value={ this.props.item[this.props.field.name] }
|
||||
placeholder={ this.props.field.placeholder }
|
||||
defaultValue={ this.props.field.defaultValue }
|
||||
onChange={ this.props.onValueChange }
|
||||
{...this.props.field.validation}
|
||||
/>
|
||||
);
|
||||
},
|
||||
});
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
return FormFieldTextarea;
|
||||
});
|
||||
const FormFieldTextarea = props => (
|
||||
<textarea
|
||||
type="text"
|
||||
className="regular-text"
|
||||
name={props.field.name}
|
||||
id={`field_${props.field.name}`}
|
||||
value={props.item[props.field.name]}
|
||||
placeholder={props.field.placeholder}
|
||||
defaultValue={props.field.defaultValue}
|
||||
onChange={props.onValueChange}
|
||||
{...props.field.validation}
|
||||
/>
|
||||
);
|
||||
|
||||
FormFieldTextarea.propTypes = {
|
||||
item: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
field: PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
defaultValue: PropTypes.string,
|
||||
validation: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||
}).isRequired,
|
||||
onValueChange: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default FormFieldTextarea;
|
||||
|
@ -1,246 +1,236 @@
|
||||
define(
|
||||
[
|
||||
'react',
|
||||
'mailpoet',
|
||||
'classnames',
|
||||
'react-router',
|
||||
'form/fields/field.jsx',
|
||||
],
|
||||
(
|
||||
React,
|
||||
MailPoet,
|
||||
classNames,
|
||||
Router,
|
||||
FormField
|
||||
) => {
|
||||
import React from 'react';
|
||||
import MailPoet from 'mailpoet';
|
||||
import classNames from 'classnames';
|
||||
import FormField from 'form/fields/field.jsx';
|
||||
import jQuery from 'jquery';
|
||||
|
||||
const Form = React.createClass({
|
||||
contextTypes: {
|
||||
router: React.PropTypes.object.isRequired,
|
||||
},
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
params: {},
|
||||
};
|
||||
},
|
||||
getInitialState: function () {
|
||||
return {
|
||||
const Form = React.createClass({
|
||||
contextTypes: {
|
||||
router: React.PropTypes.object.isRequired,
|
||||
},
|
||||
getDefaultProps: function getDefaultProps() {
|
||||
return {
|
||||
params: {},
|
||||
};
|
||||
},
|
||||
getInitialState: function getInitialState() {
|
||||
return {
|
||||
loading: false,
|
||||
errors: [],
|
||||
item: {},
|
||||
};
|
||||
},
|
||||
getValues: function getValues() {
|
||||
return this.props.item ? this.props.item : this.state.item;
|
||||
},
|
||||
getErrors: function getErrors() {
|
||||
return this.props.errors ? this.props.errors : this.state.errors;
|
||||
},
|
||||
componentDidMount: function componentDidMount() {
|
||||
if (this.props.params.id !== undefined) {
|
||||
this.loadItem(this.props.params.id);
|
||||
} else {
|
||||
setImmediate(() => {
|
||||
this.setState({
|
||||
item: jQuery('.mailpoet_form').mailpoetSerializeObject(),
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
componentWillReceiveProps: function componentWillReceiveProps(props) {
|
||||
if (props.params.id === undefined) {
|
||||
setImmediate(() => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
errors: [],
|
||||
item: {},
|
||||
});
|
||||
});
|
||||
if (props.item === undefined) {
|
||||
this.form.reset();
|
||||
}
|
||||
}
|
||||
},
|
||||
loadItem: function loadItem(id) {
|
||||
this.setState({ loading: true });
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: this.props.endpoint,
|
||||
action: 'get',
|
||||
data: {
|
||||
id,
|
||||
},
|
||||
}).done((response) => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
item: response.data,
|
||||
});
|
||||
if (typeof this.props.onItemLoad === 'function') {
|
||||
this.props.onItemLoad(response.data);
|
||||
}
|
||||
}).fail(() => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
item: {},
|
||||
}, function failSetStateCallback() {
|
||||
this.context.router.push('/new');
|
||||
});
|
||||
});
|
||||
},
|
||||
handleSubmit: function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// handle validation
|
||||
if (this.props.isValid !== undefined) {
|
||||
if (this.props.isValid() === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ loading: true });
|
||||
|
||||
// only get values from displayed fields
|
||||
const item = {};
|
||||
this.props.fields.forEach((field) => {
|
||||
if (field.fields !== undefined) {
|
||||
field.fields.forEach((subfield) => {
|
||||
item[subfield.name] = this.state.item[subfield.name];
|
||||
});
|
||||
} else {
|
||||
item[field.name] = this.state.item[field.name];
|
||||
}
|
||||
});
|
||||
// set id if specified
|
||||
if (this.props.params.id !== undefined) {
|
||||
item.id = this.props.params.id;
|
||||
}
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: this.props.endpoint,
|
||||
action: 'save',
|
||||
data: item,
|
||||
}).always(() => {
|
||||
this.setState({ loading: false });
|
||||
}).done(() => {
|
||||
if (this.props.onSuccess !== undefined) {
|
||||
this.props.onSuccess();
|
||||
} else {
|
||||
this.context.router.push('/');
|
||||
}
|
||||
|
||||
if (this.props.params.id !== undefined) {
|
||||
this.props.messages.onUpdate();
|
||||
} else {
|
||||
this.props.messages.onCreate();
|
||||
}
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
this.setState({ errors: response.errors });
|
||||
}
|
||||
});
|
||||
},
|
||||
handleValueChange: function handleValueChange(e) {
|
||||
if (this.props.onChange) {
|
||||
return this.props.onChange(e);
|
||||
}
|
||||
const item = this.state.item;
|
||||
const field = e.target.name;
|
||||
|
||||
item[field] = e.target.value;
|
||||
|
||||
this.setState({
|
||||
item,
|
||||
});
|
||||
return true;
|
||||
},
|
||||
render: function render() {
|
||||
let errors;
|
||||
if (this.getErrors() !== undefined) {
|
||||
errors = this.getErrors().map(error => (
|
||||
<div className="mailpoet_notice notice inline error is-dismissible" key={`error-${error.message}`}>
|
||||
<p>{ error.message }</p>
|
||||
</div>
|
||||
));
|
||||
}
|
||||
|
||||
const formClasses = classNames(
|
||||
'mailpoet_form',
|
||||
{ mailpoet_form_loading: this.state.loading || this.props.loading }
|
||||
);
|
||||
|
||||
let beforeFormContent = false;
|
||||
let afterFormContent = false;
|
||||
|
||||
if (this.props.beforeFormContent !== undefined) {
|
||||
beforeFormContent = this.props.beforeFormContent(this.getValues());
|
||||
}
|
||||
|
||||
if (this.props.afterFormContent !== undefined) {
|
||||
afterFormContent = this.props.afterFormContent(this.getValues());
|
||||
}
|
||||
|
||||
const fields = this.props.fields.map((field) => {
|
||||
// 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);
|
||||
};
|
||||
},
|
||||
getValues: function () {
|
||||
return this.props.item ? this.props.item : this.state.item;
|
||||
},
|
||||
getErrors: function () {
|
||||
return this.props.errors ? this.props.errors : this.state.errors;
|
||||
},
|
||||
componentDidMount: function () {
|
||||
if(this.isMounted()) {
|
||||
if(this.props.params.id !== undefined) {
|
||||
this.loadItem(this.props.params.id);
|
||||
} else {
|
||||
this.setState({
|
||||
item: jQuery('.mailpoet_form').serializeObject(),
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
componentWillReceiveProps: function (props) {
|
||||
if(props.params.id === undefined) {
|
||||
this.setState({
|
||||
loading: false,
|
||||
item: {},
|
||||
});
|
||||
if (props.item === undefined) {
|
||||
this.refs.form.reset();
|
||||
}
|
||||
} else {
|
||||
this.loadItem(props.params.id);
|
||||
}
|
||||
},
|
||||
loadItem: function (id) {
|
||||
this.setState({ loading: true });
|
||||
}
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: this.props.endpoint,
|
||||
action: 'get',
|
||||
data: {
|
||||
id: id,
|
||||
},
|
||||
}).done((response) => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
item: response.data,
|
||||
});
|
||||
}).fail(() => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
item: {},
|
||||
}, function () {
|
||||
this.context.router.push('/new');
|
||||
});
|
||||
});
|
||||
},
|
||||
handleSubmit: function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
// handle validation
|
||||
if(this.props.isValid !== undefined) {
|
||||
if(this.props.isValid() === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ loading: true });
|
||||
|
||||
// only get values from displayed fields
|
||||
const item = {};
|
||||
this.props.fields.map((field) => {
|
||||
if(field['fields'] !== undefined) {
|
||||
field.fields.map((subfield) => {
|
||||
item[subfield.name] = this.state.item[subfield.name];
|
||||
});
|
||||
} else {
|
||||
item[field.name] = this.state.item[field.name];
|
||||
}
|
||||
});
|
||||
// set id if specified
|
||||
if(this.props.params.id !== undefined) {
|
||||
item.id = this.props.params.id;
|
||||
}
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: this.props.endpoint,
|
||||
action: 'save',
|
||||
data: item,
|
||||
}).always(() => {
|
||||
this.setState({ loading: false });
|
||||
}).done(() => {
|
||||
if(this.props.onSuccess !== undefined) {
|
||||
this.props.onSuccess();
|
||||
} else {
|
||||
this.context.router.push('/');
|
||||
}
|
||||
|
||||
if(this.props.params.id !== undefined) {
|
||||
this.props.messages.onUpdate();
|
||||
} else {
|
||||
this.props.messages.onCreate();
|
||||
}
|
||||
}).fail((response) => {
|
||||
if(response.errors.length > 0) {
|
||||
this.setState({ errors: response.errors });
|
||||
}
|
||||
});
|
||||
},
|
||||
handleValueChange: function (e) {
|
||||
if (this.props.onChange) {
|
||||
return this.props.onChange(e);
|
||||
} else {
|
||||
const item = this.state.item;
|
||||
const field = e.target.name;
|
||||
|
||||
item[field] = e.target.value;
|
||||
|
||||
this.setState({
|
||||
item: item,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
},
|
||||
render: function () {
|
||||
let errors;
|
||||
if(this.getErrors() !== undefined) {
|
||||
errors = this.getErrors().map((error, index) => {
|
||||
return (
|
||||
<p key={ 'error-'+index } className="mailpoet_error">
|
||||
{ error.message }
|
||||
</p>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const formClasses = classNames(
|
||||
'mailpoet_form',
|
||||
{ mailpoet_form_loading: this.state.loading || this.props.loading }
|
||||
);
|
||||
|
||||
let beforeFormContent = false;
|
||||
let afterFormContent = false;
|
||||
|
||||
if (this.props.beforeFormContent !== undefined) {
|
||||
beforeFormContent = this.props.beforeFormContent(this.getValues());
|
||||
}
|
||||
|
||||
if (this.props.afterFormContent !== undefined) {
|
||||
afterFormContent = this.props.afterFormContent(this.getValues());
|
||||
}
|
||||
|
||||
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 (
|
||||
<FormField
|
||||
field={ field }
|
||||
item={ this.getValues() }
|
||||
onValueChange={ onValueChange }
|
||||
key={ 'field-'+i } />
|
||||
);
|
||||
});
|
||||
|
||||
let actions = false;
|
||||
if(this.props.children) {
|
||||
actions = this.props.children;
|
||||
} else {
|
||||
actions = (
|
||||
<input
|
||||
className="button button-primary"
|
||||
type="submit"
|
||||
value={MailPoet.I18n.t('save')}
|
||||
disabled={this.state.loading} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ beforeFormContent }
|
||||
<form
|
||||
id={ this.props.id }
|
||||
ref="form"
|
||||
className={ formClasses }
|
||||
onSubmit={
|
||||
(this.props.onSubmit !== undefined)
|
||||
? this.props.onSubmit
|
||||
: this.handleSubmit
|
||||
}
|
||||
>
|
||||
{ errors }
|
||||
|
||||
<table className="form-table">
|
||||
<tbody>
|
||||
{fields}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{ actions }
|
||||
</form>
|
||||
{ afterFormContent }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
return (
|
||||
<FormField
|
||||
field={field}
|
||||
item={this.getValues()}
|
||||
onValueChange={onValueChange}
|
||||
key={`field-${field.name}`}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
return Form;
|
||||
}
|
||||
);
|
||||
let actions = false;
|
||||
if (this.props.children) {
|
||||
actions = this.props.children;
|
||||
} else {
|
||||
actions = (
|
||||
<input
|
||||
className="button button-primary"
|
||||
type="submit"
|
||||
value={MailPoet.I18n.t('save')}
|
||||
disabled={this.state.loading}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ beforeFormContent }
|
||||
<form
|
||||
id={this.props.id}
|
||||
ref={(c) => { this.form = c; }}
|
||||
className={formClasses}
|
||||
onSubmit={
|
||||
(this.props.onSubmit !== undefined)
|
||||
? this.props.onSubmit
|
||||
: this.handleSubmit
|
||||
}
|
||||
>
|
||||
{ errors }
|
||||
|
||||
<table className="form-table">
|
||||
<tbody>
|
||||
{fields}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{ actions }
|
||||
</form>
|
||||
{ afterFormContent }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default Form;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Router, Route, IndexRoute, useRouterHistory } from 'react-router';
|
||||
import { createHashHistory } from 'history';
|
||||
import FormList from 'forms/list.jsx';
|
||||
import FormList from './list.jsx';
|
||||
|
||||
const history = useRouterHistory(createHashHistory)({ queryKey: false });
|
||||
|
||||
@ -14,12 +14,12 @@ const App = React.createClass({
|
||||
|
||||
const container = document.getElementById('forms_container');
|
||||
|
||||
if(container) {
|
||||
if (container) {
|
||||
ReactDOM.render((
|
||||
<Router history={ history }>
|
||||
<Route path="/" component={ App }>
|
||||
<IndexRoute component={ FormList } />
|
||||
<Route path="*" component={ FormList } />
|
||||
<Router history={history}>
|
||||
<Route path="/" component={App}>
|
||||
<IndexRoute component={FormList} />
|
||||
<Route path="*" component={FormList} />
|
||||
</Route>
|
||||
</Router>
|
||||
), container);
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
import Listing from 'listing/listing.jsx';
|
||||
import classNames from 'classnames';
|
||||
import MailPoet from 'mailpoet';
|
||||
import jQuery from 'jquery';
|
||||
import Listing from '../listing/listing.jsx';
|
||||
|
||||
const columns = [
|
||||
{
|
||||
@ -26,7 +27,7 @@ const columns = [
|
||||
|
||||
const messages = {
|
||||
onTrash: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -41,7 +42,7 @@ const messages = {
|
||||
MailPoet.Notice.success(message);
|
||||
},
|
||||
onDelete: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -56,7 +57,7 @@ const messages = {
|
||||
MailPoet.Notice.success(message);
|
||||
},
|
||||
onRestore: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -72,7 +73,7 @@ const messages = {
|
||||
},
|
||||
};
|
||||
|
||||
const bulk_actions = [
|
||||
const bulkActions = [
|
||||
{
|
||||
name: 'trash',
|
||||
label: MailPoet.I18n.t('moveToTrash'),
|
||||
@ -80,20 +81,20 @@ const bulk_actions = [
|
||||
},
|
||||
];
|
||||
|
||||
const item_actions = [
|
||||
const itemActions = [
|
||||
{
|
||||
name: 'edit',
|
||||
label: MailPoet.I18n.t('edit'),
|
||||
link: function (item) {
|
||||
link: function link(item) {
|
||||
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',
|
||||
label: MailPoet.I18n.t('duplicate'),
|
||||
onClick: function (item, refresh) {
|
||||
onClick: function onClick(item, refresh) {
|
||||
return MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'forms',
|
||||
@ -109,7 +110,7 @@ const item_actions = [
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
@ -128,40 +129,39 @@ const FormList = React.createClass({
|
||||
endpoint: 'forms',
|
||||
action: 'create',
|
||||
}).done((response) => {
|
||||
window.location = mailpoet_form_edit_url + response.data.id;
|
||||
window.location = window.mailpoet_form_edit_url + response.data.id;
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
renderItem(form, actions) {
|
||||
const row_classes = classNames(
|
||||
const rowClasses = classNames(
|
||||
'manage-column',
|
||||
'column-primary',
|
||||
'has-row-actions'
|
||||
);
|
||||
|
||||
let segments = mailpoet_segments.filter((segment) => {
|
||||
return (jQuery.inArray(segment.id, form.segments) !== -1);
|
||||
}).map((segment) => {
|
||||
return segment.name;
|
||||
}).join(', ');
|
||||
let segments = window.mailpoet_segments
|
||||
.filter(segment => (jQuery.inArray(segment.id, form.segments) !== -1))
|
||||
.map(segment => segment.name)
|
||||
.join(', ');
|
||||
|
||||
if (form.settings.segments_selected_by === 'user') {
|
||||
segments = MailPoet.I18n.t('userChoice') + ' ' + segments;
|
||||
segments = `${MailPoet.I18n.t('userChoice')} ${segments}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<td className={ row_classes }>
|
||||
<td className={rowClasses}>
|
||||
<strong>
|
||||
<a
|
||||
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>
|
||||
</strong>
|
||||
{ actions }
|
||||
@ -185,21 +185,21 @@ const FormList = React.createClass({
|
||||
{MailPoet.I18n.t('pageTitle')} <a
|
||||
className="page-title-action"
|
||||
href="javascript:;"
|
||||
onClick={ this.createForm }
|
||||
onClick={this.createForm}
|
||||
>{MailPoet.I18n.t('new')}</a>
|
||||
</h1>
|
||||
|
||||
<Listing
|
||||
limit={ mailpoet_listing_per_page }
|
||||
location={ this.props.location }
|
||||
params={ this.props.params }
|
||||
messages={ messages }
|
||||
search={ false }
|
||||
limit={window.mailpoet_listing_per_page}
|
||||
location={this.props.location}
|
||||
params={this.props.params}
|
||||
messages={messages}
|
||||
search={false}
|
||||
endpoint="forms"
|
||||
onRenderItem={ this.renderItem }
|
||||
columns={ columns }
|
||||
bulk_actions={ bulk_actions }
|
||||
item_actions={ item_actions }
|
||||
onRenderItem={this.renderItem}
|
||||
columns={columns}
|
||||
bulk_actions={bulkActions}
|
||||
item_actions={itemActions}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,117 +1,117 @@
|
||||
define('handlebars_helpers', ['handlebars'], function(Handlebars) {
|
||||
/* eslint-disable func-names */
|
||||
define('handlebars_helpers', ['handlebars'], function (Handlebars) {
|
||||
// Handlebars helpers
|
||||
Handlebars.registerHelper('concat', function() {
|
||||
var size = (arguments.length - 1),
|
||||
output = '';
|
||||
for(var i = 0; i < size; i++) {
|
||||
output += arguments[i];
|
||||
};
|
||||
return output;
|
||||
Handlebars.registerHelper('concat', function () {
|
||||
var size = (arguments.length - 1);
|
||||
var output = '';
|
||||
var i;
|
||||
for (i = 0; i < size; i += 1) {
|
||||
output += arguments[i];
|
||||
}
|
||||
return output;
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('number_format', function(value, block) {
|
||||
return Number(value).toLocaleString();
|
||||
Handlebars.registerHelper('number_format', function (value) {
|
||||
return Number(value).toLocaleString();
|
||||
});
|
||||
Handlebars.registerHelper('date_format', function(timestamp, block) {
|
||||
if(window.moment) {
|
||||
if(timestamp === undefined || isNaN(timestamp) || timestamp <= 0) {
|
||||
return;
|
||||
}
|
||||
Handlebars.registerHelper('date_format', function (timestamp, block) {
|
||||
var f;
|
||||
if (window.moment) {
|
||||
if (timestamp === undefined || isNaN(timestamp) || timestamp <= 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// set date format
|
||||
var f = block.hash.format || 'MMM Do, YYYY';
|
||||
// check if we passed a timestamp
|
||||
if(parseInt(timestamp, 10) == timestamp) {
|
||||
return moment.unix(timestamp).format(f);
|
||||
} else {
|
||||
return moment.utc(timestamp).format(f);
|
||||
}
|
||||
} else {
|
||||
return timestamp;
|
||||
};
|
||||
// set date format
|
||||
f = block.hash.format || 'MMM Do, YYYY';
|
||||
// check if we passed a timestamp
|
||||
if (/^\s*\d+\s*$/.test(timestamp)) {
|
||||
return window.moment.unix(timestamp).format(f);
|
||||
}
|
||||
return window.moment.utc(timestamp).format(f);
|
||||
}
|
||||
return timestamp;
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('cycle', function(value, block) {
|
||||
Handlebars.registerHelper('cycle', function (value, block) {
|
||||
var values = value.split(' ');
|
||||
return values[block.data.index % (values.length + 1)];
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('ifCond', function (v1, operator, v2, options) {
|
||||
switch (operator) {
|
||||
case '==':
|
||||
return (v1 == v2) ? options.fn(this) : options.inverse(this);
|
||||
case '===':
|
||||
return (v1 === v2) ? options.fn(this) : options.inverse(this);
|
||||
case '!=':
|
||||
return (v1 != v2) ? options.fn(this) : options.inverse(this);
|
||||
case '!==':
|
||||
return (v1 !== v2) ? options.fn(this) : options.inverse(this);
|
||||
case '<':
|
||||
return (v1 < v2) ? options.fn(this) : options.inverse(this);
|
||||
case '<=':
|
||||
return (v1 <= v2) ? options.fn(this) : options.inverse(this);
|
||||
case '>':
|
||||
return (v1 > v2) ? options.fn(this) : options.inverse(this);
|
||||
case '>=':
|
||||
return (v1 >= v2) ? options.fn(this) : options.inverse(this);
|
||||
case '&&':
|
||||
return (v1 && v2) ? options.fn(this) : options.inverse(this);
|
||||
case '||':
|
||||
return (v1 || v2) ? options.fn(this) : options.inverse(this);
|
||||
case 'in':
|
||||
var values = v2.split(',');
|
||||
return (v2.indexOf(v1) !== -1) ? options.fn(this) : options.inverse(this);
|
||||
default:
|
||||
return options.inverse(this);
|
||||
}
|
||||
switch (operator) {
|
||||
case '==':
|
||||
return (v1 == v2) ? options.fn(this) : options.inverse(this); // eslint-disable-line eqeqeq
|
||||
case '===':
|
||||
return (v1 === v2) ? options.fn(this) : options.inverse(this);
|
||||
case '!=':
|
||||
return (v1 != v2) ? options.fn(this) : options.inverse(this); // eslint-disable-line eqeqeq
|
||||
case '!==':
|
||||
return (v1 !== v2) ? options.fn(this) : options.inverse(this);
|
||||
case '<':
|
||||
return (v1 < v2) ? options.fn(this) : options.inverse(this);
|
||||
case '<=':
|
||||
return (v1 <= v2) ? options.fn(this) : options.inverse(this);
|
||||
case '>':
|
||||
return (v1 > v2) ? options.fn(this) : options.inverse(this);
|
||||
case '>=':
|
||||
return (v1 >= v2) ? options.fn(this) : options.inverse(this);
|
||||
case '&&':
|
||||
return (v1 && v2) ? options.fn(this) : options.inverse(this);
|
||||
case '||':
|
||||
return (v1 || v2) ? options.fn(this) : options.inverse(this);
|
||||
case 'in':
|
||||
return (v2.indexOf(v1) !== -1) ? options.fn(this) : options.inverse(this);
|
||||
default:
|
||||
return options.inverse(this);
|
||||
}
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('nl2br', function(value, block) {
|
||||
return value.gsub('\n', '<br />');
|
||||
Handlebars.registerHelper('nl2br', function (value) {
|
||||
return value.gsub('\n', '<br />');
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('json_encode', function(value, block) {
|
||||
return JSON.stringify(value);
|
||||
Handlebars.registerHelper('json_encode', function (value) {
|
||||
return JSON.stringify(value);
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('json_decode', function(value, block) {
|
||||
return JSON.parse(value);
|
||||
Handlebars.registerHelper('json_decode', function (value) {
|
||||
return JSON.parse(value);
|
||||
});
|
||||
Handlebars.registerHelper('url', function(value, block) {
|
||||
var url = window.location.protocol + '//' + window.location.host + window.location.pathname;
|
||||
Handlebars.registerHelper('url', function (value) {
|
||||
var url = window.location.protocol + '//' + window.location.host + window.location.pathname;
|
||||
|
||||
return url + value;
|
||||
return url + value;
|
||||
});
|
||||
Handlebars.registerHelper('emailFromMailto', function(value) {
|
||||
var mailtoMatchingRegex = /^mailto\:/i;
|
||||
if (typeof value === 'string' && value.match(mailtoMatchingRegex)) {
|
||||
return value.replace(mailtoMatchingRegex, '');
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
Handlebars.registerHelper('emailFromMailto', function (value) {
|
||||
var mailtoMatchingRegex = /^mailto:/i;
|
||||
if (typeof value === 'string' && value.match(mailtoMatchingRegex)) {
|
||||
return value.replace(mailtoMatchingRegex, '');
|
||||
}
|
||||
return value;
|
||||
});
|
||||
Handlebars.registerHelper('lookup', function(obj, field, options) {
|
||||
return obj && obj[field];
|
||||
Handlebars.registerHelper('lookup', function (obj, field) {
|
||||
return obj && obj[field];
|
||||
});
|
||||
|
||||
|
||||
Handlebars.registerHelper('rsa_key', function(value, block) {
|
||||
// extract all lines into an array
|
||||
if(value === undefined) return '';
|
||||
Handlebars.registerHelper('rsa_key', function (value) {
|
||||
var lines;
|
||||
// extract all lines into an array
|
||||
if (value === undefined) return '';
|
||||
|
||||
var lines = value.trim().split('\n');
|
||||
lines = value.trim().split('\n');
|
||||
|
||||
// remove header & footer
|
||||
lines.shift();
|
||||
lines.pop();
|
||||
lines.shift();
|
||||
lines.pop();
|
||||
|
||||
// return concatenated lines
|
||||
return lines.join('');
|
||||
return lines.join('');
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('trim', function(value, block) {
|
||||
if(value === null || value === undefined) return '';
|
||||
return value.trim();
|
||||
Handlebars.registerHelper('trim', function (value) {
|
||||
if (value === null || value === undefined) return '';
|
||||
return value.trim();
|
||||
});
|
||||
|
||||
/**
|
||||
@ -125,24 +125,23 @@ define('handlebars_helpers', ['handlebars'], function(Handlebars) {
|
||||
* @return {String} The truncated string.
|
||||
*/
|
||||
Handlebars.registerHelper('ellipsis', function (str, limit, append) {
|
||||
var strAppend = append;
|
||||
if (strAppend === undefined) {
|
||||
strAppend = '';
|
||||
}
|
||||
var sanitized = str.replace(/(<([^>]+)>)/g, '');
|
||||
if (sanitized.length > limit) {
|
||||
return sanitized.substr(0, limit - strAppend.length) + strAppend;
|
||||
} else {
|
||||
return sanitized;
|
||||
}
|
||||
var strAppend = append;
|
||||
var sanitized = str.replace(/(<([^>]+)>)/g, '');
|
||||
if (strAppend === undefined) {
|
||||
strAppend = '';
|
||||
}
|
||||
if (sanitized.length > limit) {
|
||||
return sanitized.substr(0, limit - strAppend.length) + strAppend;
|
||||
}
|
||||
return sanitized;
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('getNumber', function (string) {
|
||||
return parseInt(string, 10);
|
||||
return parseInt(string, 10);
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('fontWithFallback', function(font) {
|
||||
switch(font) {
|
||||
Handlebars.registerHelper('fontWithFallback', function (font) {
|
||||
switch (font) {
|
||||
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 'Courier New': return new Handlebars.SafeString("'Courier New', Courier, 'Lucida Sans Typewriter', 'Lucida Typewriter', monospace");
|
||||
|
@ -1,11 +1,11 @@
|
||||
define('helpTooltip', ['mailpoet', 'react', 'react-dom', 'help-tooltip.jsx'],
|
||||
function (mp, React, ReactDOM, TooltipComponent) {
|
||||
function helpTooltip(mp, React, ReactDOM, TooltipComponent) {
|
||||
'use strict';
|
||||
|
||||
var MailPoet = mp;
|
||||
|
||||
MailPoet.helpTooltip = {
|
||||
show: function (domContainerNode, opts) {
|
||||
|
||||
show: function show(domContainerNode, opts) {
|
||||
ReactDOM.render(React.createElement(
|
||||
TooltipComponent, {
|
||||
tooltip: opts.tooltip,
|
||||
@ -15,7 +15,6 @@ define('helpTooltip', ['mailpoet', 'react', 'react-dom', 'help-tooltip.jsx'],
|
||||
), domContainerNode);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -6,11 +6,11 @@ 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') {
|
||||
if (!props.tooltipId && typeof props.tooltip === 'string') {
|
||||
tooltipId = props.tooltip;
|
||||
}
|
||||
|
||||
if(typeof props.tooltip === 'string') {
|
||||
if (typeof props.tooltip === 'string') {
|
||||
tooltip = (<span
|
||||
style={{
|
||||
pointerEvents: 'all',
|
||||
@ -32,17 +32,16 @@ function Tooltip(props) {
|
||||
data-event="click"
|
||||
data-tip
|
||||
data-for={tooltipId}
|
||||
/>
|
||||
<ReactTooltip
|
||||
globalEventOff="click"
|
||||
multiline
|
||||
id={tooltipId}
|
||||
efect="solid"
|
||||
place={props.place}
|
||||
>
|
||||
</span>
|
||||
<ReactTooltip
|
||||
globalEventOff="click"
|
||||
multiline={true}
|
||||
id={tooltipId}
|
||||
efect="solid"
|
||||
place={props.place}
|
||||
>
|
||||
{tooltip}
|
||||
</ReactTooltip>
|
||||
{tooltip}
|
||||
</ReactTooltip>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
@ -3,8 +3,9 @@ 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 SystemStatus from 'help/system_status.jsx';
|
||||
import SystemInfo from 'help/system_info.jsx';
|
||||
import KnowledgeBase from 'help/knowledge_base.jsx';
|
||||
|
||||
const history = useRouterHistory(createHashHistory)({ queryKey: false });
|
||||
|
||||
@ -16,17 +17,16 @@ const App = React.createClass({
|
||||
|
||||
const container = document.getElementById('help_container');
|
||||
|
||||
if(container) {
|
||||
|
||||
if (container) {
|
||||
ReactDOM.render((
|
||||
<Router history={ history }>
|
||||
<Route path="/" component={ App }>
|
||||
<IndexRedirect to="knowledgeBase" />
|
||||
<Router history={history}>
|
||||
<Route path="/" component={App}>
|
||||
<IndexRedirect to="systemStatus" />
|
||||
{/* Pages */}
|
||||
<Route path="knowledgeBase(/)**" params={{ tab: 'knowledgeBase' }} component={ KnowledgeBase } />
|
||||
<Route path="systemInfo(/)**" params={{ tab: 'systemInfo' }} component={ SystemInfo } />
|
||||
<Route path="systemStatus(/)**" params={{ tab: 'systemStatus' }} component={SystemStatus} />
|
||||
<Route path="systemInfo(/)**" params={{ tab: 'systemInfo' }} component={SystemInfo} />
|
||||
<Route path="knowledgeBase(/)**" params={{ tab: 'knowledgeBase' }} component={KnowledgeBase} />
|
||||
</Route>
|
||||
</Router>
|
||||
), container);
|
||||
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import MailPoet from 'mailpoet';
|
||||
import Tabs from './tabs.jsx';
|
||||
|
||||
function KnowledgeBase() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
@ -12,18 +11,18 @@ function 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>
|
||||
<li><a target="_blank" rel="noreferrer noopener" href="http://beta.docs.mailpoet.com/category/116-common-problems">Common Problems</a></li>
|
||||
<li><a target="_blank" rel="noreferrer noopener" href="http://beta.docs.mailpoet.com/category/165-newsletters">Newsletters</a></li>
|
||||
<li><a target="_blank" rel="noreferrer noopener" href="http://beta.docs.mailpoet.com/category/156-migration-questions">Migration Questions</a></li>
|
||||
<li><a target="_blank" rel="noreferrer noopener" href="http://beta.docs.mailpoet.com/category/149-sending-methods">Sending Methods</a></li>
|
||||
<li><a target="_blank" rel="noreferrer noopener" href="http://beta.docs.mailpoet.com/category/139-subscription-forms">Subscription Forms</a></li>
|
||||
<li><a target="_blank" rel="noreferrer noopener" href="http://beta.docs.mailpoet.com/category/114-getting-started">Getting Started</a></li>
|
||||
<li><a target="_blank" rel="noreferrer noopener" href="http://beta.docs.mailpoet.com/category/123-newsletter-designer">Newsletter Designer</a></li>
|
||||
<li><a target="_blank" rel="noreferrer noopener" 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>
|
||||
<a target="_blank" rel="noreferrer noopener" href="http://beta.docs.mailpoet.com/" className="button button-primary">{MailPoet.I18n.t('knowledgeBaseButton')}</a>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = KnowledgeBase;
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import MailPoet from 'mailpoet';
|
||||
import _ from 'underscore';
|
||||
|
||||
import Tabs from './tabs.jsx';
|
||||
|
||||
function handleFocus(event) {
|
||||
@ -10,12 +9,10 @@ function handleFocus(event) {
|
||||
|
||||
function printData(data) {
|
||||
if (_.isObject(data)) {
|
||||
const printableData = Object.keys(data).map((key) => {
|
||||
return `${key}: ${data[key]}`;
|
||||
});
|
||||
const printableData = Object.keys(data).map(key => `${key}: ${data[key]}`);
|
||||
|
||||
return (<textarea
|
||||
readOnly={true}
|
||||
readOnly
|
||||
onFocus={handleFocus}
|
||||
value={printableData.join('\n')}
|
||||
style={{
|
||||
@ -23,25 +20,24 @@ function printData(data) {
|
||||
height: '400px',
|
||||
}}
|
||||
/>);
|
||||
} else {
|
||||
return (<p>{MailPoet.I18n.t('systemInfoDataError')}</p>);
|
||||
}
|
||||
return (<p>{MailPoet.I18n.t('systemInfoDataError')}</p>);
|
||||
}
|
||||
|
||||
function KnowledgeBase() {
|
||||
const data = window.help_scout_data;
|
||||
function SystemInfo() {
|
||||
const systemInfoData = window.systemInfoData;
|
||||
return (
|
||||
<div>
|
||||
|
||||
<Tabs tab="systemInfo" />
|
||||
|
||||
<div className="mailpoet_notice notice inline notice-success" style={{ marginTop: '1em' }}>
|
||||
<div className="mailpoet_notice notice inline" style={{ marginTop: '1em' }}>
|
||||
<p>{MailPoet.I18n.t('systemInfoIntro')}</p>
|
||||
</div>
|
||||
|
||||
{printData(data)}
|
||||
{printData(systemInfoData)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = KnowledgeBase;
|
||||
module.exports = SystemInfo;
|
||||
|
71
assets/js/src/help/system_status.jsx
Normal file
71
assets/js/src/help/system_status.jsx
Normal file
@ -0,0 +1,71 @@
|
||||
import MailPoet from 'mailpoet';
|
||||
import React from 'react';
|
||||
import ReactStringReplace from 'react-string-replace';
|
||||
import Tabs from './tabs.jsx';
|
||||
|
||||
function renderStatusMessage(status, error, link) {
|
||||
const noticeType = (status) ? 'success' : 'error';
|
||||
let noticeMessage = (status) ?
|
||||
MailPoet.I18n.t('systemStatusConnectionSuccessful') :
|
||||
`${MailPoet.I18n.t('systemStatusConnectionUnsuccessful')} ${error}`;
|
||||
|
||||
if (link) {
|
||||
noticeMessage = ReactStringReplace(
|
||||
noticeMessage,
|
||||
/\[link\](.*?)\[\/link\]/g,
|
||||
match => (
|
||||
<a href={`${link}`} key="kb-link">{ match }</a>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`mailpoet_notice notice inline notice-${noticeType}`} style={{ marginTop: '1em' }}>
|
||||
<p>{noticeMessage}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderCronSection(data) {
|
||||
const status = data.cron.isReachable;
|
||||
const url = data.cron.url;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>{MailPoet.I18n.t('systemStatusCronTitle')}</h2>
|
||||
<p>
|
||||
<a href={url} target="_blank">{url}</a>
|
||||
</p>
|
||||
{renderStatusMessage(status, MailPoet.I18n.t('systemStatusCronConnectionUnsuccessfulInfo'), '//beta.docs.mailpoet.com/article/231-sending-does-not-work')}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderMSSSection(data) {
|
||||
if (!data.mss.enabled) return undefined;
|
||||
|
||||
const status = data.mss.enabled.isReachable;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>{MailPoet.I18n.t('systemStatusMSSTitle')}</h2>
|
||||
{renderStatusMessage(status, MailPoet.I18n.t('systemStatusMSSConnectionUnsuccessfulInfo'), false)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SystemStatus() {
|
||||
const systemStatusData = window.systemStatusData;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Tabs tab="systemStatus" />
|
||||
<div className="mailpoet_notice notice inline" style={{ marginTop: '1em' }}>
|
||||
<p>{systemStatusData.mss.enabled ? MailPoet.I18n.t('systemStatusIntroCronMSS') : MailPoet.I18n.t('systemStatusIntroCron')}</p>
|
||||
</div>
|
||||
{renderCronSection(systemStatusData)}
|
||||
{renderMSSSection(systemStatusData)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
module.exports = SystemStatus;
|
@ -5,20 +5,24 @@ import MailPoet from 'mailpoet';
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
name: 'knowledgeBase',
|
||||
label: MailPoet.I18n.t('tabKnowledgeBaseTitle'),
|
||||
link: '/knowledgeBase',
|
||||
name: 'systemStatus',
|
||||
label: MailPoet.I18n.t('tabSystemStatusTitle'),
|
||||
link: '/systemStatus',
|
||||
},
|
||||
{
|
||||
name: 'systemInfo',
|
||||
label: MailPoet.I18n.t('tabSystemInfoTitle'),
|
||||
link: '/systemInfo',
|
||||
},
|
||||
{
|
||||
name: 'knowledgeBase',
|
||||
label: MailPoet.I18n.t('tabKnowledgeBaseTitle'),
|
||||
link: '/knowledgeBase',
|
||||
},
|
||||
];
|
||||
|
||||
function Tabs(props) {
|
||||
|
||||
const tabLinks = tabs.map((tab, index) => {
|
||||
const tabLinks = tabs.map((tab) => {
|
||||
const tabClasses = classNames(
|
||||
'nav-tab',
|
||||
{ 'nav-tab-active': (props.tab === tab.name) }
|
||||
@ -26,9 +30,9 @@ function Tabs(props) {
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={ 'tab-'+index }
|
||||
className={ tabClasses }
|
||||
to={ tab.link }
|
||||
key={`tab-${tab.name}`}
|
||||
className={tabClasses}
|
||||
to={tab.link}
|
||||
>{ tab.label }</Link>
|
||||
);
|
||||
});
|
||||
@ -38,9 +42,9 @@ function Tabs(props) {
|
||||
{ tabLinks }
|
||||
</h2>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
Tabs.propTypes = { tab: React.PropTypes.string };
|
||||
Tabs.defaultProps = { tab: 'knowledgeBase' };
|
||||
Tabs.defaultProps = { tab: 'systemStatus' };
|
||||
|
||||
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,24 +1,24 @@
|
||||
define('i18n',
|
||||
[
|
||||
'mailpoet'
|
||||
], function(
|
||||
], function i18n(
|
||||
mp
|
||||
) {
|
||||
'use strict';
|
||||
var MailPoet = mp;
|
||||
'use strict';
|
||||
|
||||
var translations = {};
|
||||
var MailPoet = mp;
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
var translations = {};
|
||||
|
||||
});
|
||||
MailPoet.I18n = {
|
||||
add: function add(key, value) {
|
||||
translations[key] = value;
|
||||
},
|
||||
t: function t(key) {
|
||||
return translations[key] || 'TRANSLATION "%$1s" NOT FOUND'.replace('%$1s', key);
|
||||
},
|
||||
all: function all() {
|
||||
return translations;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
@ -1,19 +1,20 @@
|
||||
define('iframe', ['mailpoet'], function(mp) {
|
||||
define('iframe', ['mailpoet'], function iframeModule(mp) {
|
||||
'use strict';
|
||||
|
||||
var MailPoet = mp;
|
||||
MailPoet.Iframe = {
|
||||
marginY: 20,
|
||||
autoSize: function(iframe) {
|
||||
if(!iframe) return;
|
||||
autoSize: function autoSize(iframe) {
|
||||
if (!iframe) return;
|
||||
|
||||
this.setSize(
|
||||
iframe,
|
||||
iframe.contentWindow.document.body.scrollHeight
|
||||
);
|
||||
},
|
||||
setSize: function(sizeIframe, i) {
|
||||
setSize: function setSize(sizeIframe, i) {
|
||||
var iframe = sizeIframe;
|
||||
if(!iframe) return;
|
||||
if (!iframe) return;
|
||||
|
||||
iframe.style.height = (
|
||||
parseInt(i, 10) + this.marginY
|
||||
|
@ -2,7 +2,7 @@ define(
|
||||
[
|
||||
'jquery'
|
||||
],
|
||||
function(
|
||||
function ( // eslint-disable-line func-names
|
||||
jQuery
|
||||
) {
|
||||
var $ = jQuery;
|
||||
@ -23,47 +23,50 @@ define(
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
* http://benalman.com/about/license/
|
||||
*/
|
||||
$.fn.serializeObject = function(coerce) {
|
||||
var obj = {},
|
||||
coerce_types = { true: !0, false: !1, null: null };
|
||||
$.fn.mailpoetSerializeObject = function (coerce) { // eslint-disable-line func-names
|
||||
var obj = {};
|
||||
var coerceTypes = { true: !0, false: !1, null: null };
|
||||
|
||||
// Iterate over all name=value pairs.
|
||||
$.each( this.serializeArray(), function(j, v){
|
||||
var key = v.name,
|
||||
val = v.value,
|
||||
cur = obj,
|
||||
i = 0,
|
||||
$.each(this.serializeArray(), function (j, v) { // eslint-disable-line func-names
|
||||
var key = v.name;
|
||||
var val = v.value;
|
||||
var cur = obj;
|
||||
var i = 0;
|
||||
|
||||
// If key is more complex than 'foo', like 'a[]' or 'a[b][c]', split it
|
||||
// into its component parts.
|
||||
keys = key.split( '][' ),
|
||||
keys_last = keys.length - 1;
|
||||
// If key is more complex than 'foo', like 'a[]' or 'a[b][c]', split it
|
||||
// into its component parts.
|
||||
var keys = key.split('][');
|
||||
var keysLast = keys.length - 1;
|
||||
|
||||
// If the first keys part contains [ and the last ends with ], then []
|
||||
// are correctly balanced.
|
||||
if ( /\[/.test( keys[0] ) && /\]$/.test( keys[ keys_last ] ) ) {
|
||||
if (/\[/.test(keys[0]) && /\]$/.test(keys[keysLast])) {
|
||||
// Remove the trailing ] from the last keys part.
|
||||
keys[ keys_last ] = keys[ keys_last ].replace( /\]$/, '' );
|
||||
keys[keysLast] = keys[keysLast].replace(/\]$/, '');
|
||||
|
||||
// Split first keys part into two parts on the [ and add them back onto
|
||||
// the beginning of the keys array.
|
||||
keys = keys.shift().split('[').concat( keys );
|
||||
keys = keys.shift().split('[').concat(keys);
|
||||
|
||||
keys_last = keys.length - 1;
|
||||
keysLast = keys.length - 1;
|
||||
} else {
|
||||
// Basic 'foo' style key.
|
||||
keys_last = 0;
|
||||
keysLast = 0;
|
||||
}
|
||||
|
||||
// Coerce values.
|
||||
if ( coerce ) {
|
||||
val = val && !isNaN(val) ? +val // number
|
||||
: val === 'undefined' ? undefined // undefined
|
||||
: coerce_types[val] !== undefined ? coerce_types[val] // true, false, null
|
||||
: val; // string
|
||||
if (coerce) {
|
||||
if (val && !isNaN(val)) { // number
|
||||
val = +val;
|
||||
} else if (val === 'undefined') { // undefined
|
||||
val = undefined;
|
||||
} else if (coerceTypes[val] !== undefined) { // true, false, null
|
||||
val = coerceTypes[val];
|
||||
}
|
||||
}
|
||||
|
||||
if ( keys_last ) {
|
||||
if (keysLast) {
|
||||
// Complex key, build deep object structure based on a few rules:
|
||||
// * The 'cur' pointer starts at the object top-level.
|
||||
// * [] = array push (n is set to array length), [n] = array if n is
|
||||
@ -73,31 +76,23 @@ define(
|
||||
// object or array based on the type of the next keys part.
|
||||
// * Move the 'cur' pointer to the next level.
|
||||
// * Rinse & repeat.
|
||||
for ( ; i <= keys_last; i++ ) {
|
||||
for (; i <= keysLast; i += 1) {
|
||||
key = keys[i] === '' ? cur.length : keys[i];
|
||||
cur[key] = i < keys_last
|
||||
? cur[key] || ( keys[i+1] && isNaN( keys[i+1] ) ? {} : [] )
|
||||
cur[key] = i < keysLast
|
||||
? cur[key] || (keys[i + 1] && isNaN(keys[i + 1]) ? {} : [])
|
||||
: val;
|
||||
cur = cur[key];
|
||||
}
|
||||
|
||||
} else if ($.isArray(obj[key])) {
|
||||
// val is already an array, so push on the next value.
|
||||
obj[key].push(val);
|
||||
} else if (obj[key] !== undefined) {
|
||||
// val isn't an array, but since a second value has been specified,
|
||||
// convert val into an array.
|
||||
obj[key] = [obj[key], val];
|
||||
} else {
|
||||
// Simple key, even simpler rules, since only scalars and shallow
|
||||
// arrays are allowed.
|
||||
|
||||
if ( $.isArray( obj[key] ) ) {
|
||||
// val is already an array, so push on the next value.
|
||||
obj[key].push( val );
|
||||
|
||||
} else if ( obj[key] !== undefined ) {
|
||||
// val isn't an array, but since a second value has been specified,
|
||||
// convert val into an array.
|
||||
obj[key] = [ obj[key], val ];
|
||||
|
||||
} else {
|
||||
// val is a scalar.
|
||||
obj[key] = val;
|
||||
}
|
||||
// val is a scalar.
|
||||
obj[key] = val;
|
||||
}
|
||||
});
|
||||
|
||||
@ -106,4 +101,4 @@ define(
|
||||
|
||||
return $;
|
||||
}
|
||||
);
|
||||
);
|
||||
|
@ -1,122 +1,114 @@
|
||||
define([
|
||||
'react',
|
||||
'mailpoet',
|
||||
],
|
||||
(
|
||||
React,
|
||||
MailPoet
|
||||
) => {
|
||||
const ListingBulkActions = React.createClass({
|
||||
getInitialState: function () {
|
||||
return {
|
||||
action: false,
|
||||
extra: false,
|
||||
};
|
||||
},
|
||||
handleChangeAction: function (e) {
|
||||
this.setState({
|
||||
action: e.target.value,
|
||||
extra: false,
|
||||
}, () => {
|
||||
const action = this.getSelectedAction();
|
||||
|
||||
// action on select callback
|
||||
if(action !== null && action['onSelect'] !== undefined) {
|
||||
this.setState({
|
||||
extra: action.onSelect(e),
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
handleApplyAction: function (e) {
|
||||
e.preventDefault();
|
||||
import React from 'react';
|
||||
import MailPoet from 'mailpoet';
|
||||
|
||||
const ListingBulkActions = React.createClass({
|
||||
getInitialState: function getInitialState() {
|
||||
return {
|
||||
action: false,
|
||||
extra: false,
|
||||
};
|
||||
},
|
||||
handleChangeAction: function handleChangeAction(e) {
|
||||
this.setState({
|
||||
action: e.target.value,
|
||||
extra: false,
|
||||
}, () => {
|
||||
const action = this.getSelectedAction();
|
||||
|
||||
if(action === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selected_ids = (this.props.selection !== 'all')
|
||||
? this.props.selected_ids
|
||||
: [];
|
||||
|
||||
const data = (action['getData'] !== undefined)
|
||||
? action.getData()
|
||||
: {};
|
||||
|
||||
data.action = this.state.action;
|
||||
|
||||
let onSuccess = function () {};
|
||||
if(action['onSuccess'] !== undefined) {
|
||||
onSuccess = action.onSuccess;
|
||||
}
|
||||
|
||||
if(data.action) {
|
||||
const promise = this.props.onBulkAction(selected_ids, data);
|
||||
if (promise !== false) {
|
||||
promise.then(onSuccess);
|
||||
};
|
||||
}
|
||||
|
||||
this.setState({
|
||||
action: false,
|
||||
extra: false,
|
||||
});
|
||||
},
|
||||
getSelectedAction: function () {
|
||||
const selected_action = this.refs.action.value;
|
||||
if(selected_action.length > 0) {
|
||||
const action = this.props.bulk_actions.filter((action) => {
|
||||
return (action.name === selected_action);
|
||||
// action on select callback
|
||||
if (action !== null && action.onSelect !== undefined) {
|
||||
this.setState({
|
||||
extra: action.onSelect(e),
|
||||
});
|
||||
|
||||
if(action.length > 0) {
|
||||
return action[0];
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
handleApplyAction: function handleApplyAction(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const action = this.getSelectedAction();
|
||||
|
||||
if (action === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedIds = (this.props.selection !== 'all')
|
||||
? this.props.selected_ids
|
||||
: [];
|
||||
|
||||
const data = (action.getData !== undefined)
|
||||
? action.getData()
|
||||
: {};
|
||||
|
||||
data.action = this.state.action;
|
||||
|
||||
let onSuccess = () => {};
|
||||
if (action.onSuccess !== undefined) {
|
||||
onSuccess = action.onSuccess;
|
||||
}
|
||||
|
||||
if (data.action) {
|
||||
const promise = this.props.onBulkAction(selectedIds, data);
|
||||
if (promise !== false) {
|
||||
promise.then(onSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
action: false,
|
||||
extra: false,
|
||||
});
|
||||
},
|
||||
getSelectedAction: function getSelectedAction() {
|
||||
const selectedAction = this.action.value;
|
||||
if (selectedAction.length > 0) {
|
||||
const action = this.props.bulk_actions.filter(act => (act.name === selectedAction));
|
||||
|
||||
if (action.length > 0) {
|
||||
return action[0];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
render: function render() {
|
||||
if (this.props.bulk_actions.length === 0) {
|
||||
return null;
|
||||
},
|
||||
render: function () {
|
||||
if(this.props.bulk_actions.length === 0) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="alignleft actions bulkactions">
|
||||
<label
|
||||
className="screen-reader-text"
|
||||
htmlFor="bulk-action-selector-top">
|
||||
{MailPoet.I18n.t('selectBulkAction')}
|
||||
</label>
|
||||
return (
|
||||
<div className="alignleft actions bulkactions">
|
||||
<label
|
||||
className="screen-reader-text"
|
||||
htmlFor="bulk-action-selector-top"
|
||||
>
|
||||
{MailPoet.I18n.t('selectBulkAction')}
|
||||
</label>
|
||||
|
||||
<select
|
||||
name="bulk_actions"
|
||||
ref="action"
|
||||
value={ this.state.action }
|
||||
onChange={this.handleChangeAction}
|
||||
>
|
||||
<option value="">{MailPoet.I18n.t('bulkActions')}</option>
|
||||
{ this.props.bulk_actions.map((action, index) => {
|
||||
return (
|
||||
<option
|
||||
value={ action.name }
|
||||
key={ 'action-' + index }
|
||||
>{ action.label }</option>
|
||||
);
|
||||
}) }
|
||||
</select>
|
||||
<input
|
||||
onClick={ this.handleApplyAction }
|
||||
type="submit"
|
||||
defaultValue={MailPoet.I18n.t('apply')}
|
||||
className="button action" />
|
||||
<select
|
||||
name="bulk_actions"
|
||||
ref={(c) => { this.action = c; }}
|
||||
value={this.state.action}
|
||||
onChange={this.handleChangeAction}
|
||||
>
|
||||
<option value="">{MailPoet.I18n.t('bulkActions')}</option>
|
||||
{ this.props.bulk_actions.map(action => (
|
||||
<option
|
||||
value={action.name}
|
||||
key={`action-${action.name}`}
|
||||
>{ action.label }</option>
|
||||
)) }
|
||||
</select>
|
||||
<input
|
||||
onClick={this.handleApplyAction}
|
||||
type="submit"
|
||||
defaultValue={MailPoet.I18n.t('apply')}
|
||||
className="button action"
|
||||
/>
|
||||
|
||||
{ this.state.extra }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
return ListingBulkActions;
|
||||
{ this.state.extra }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default ListingBulkActions;
|
||||
|
@ -1,107 +1,95 @@
|
||||
define([
|
||||
'react',
|
||||
'jquery',
|
||||
'mailpoet',
|
||||
],
|
||||
(
|
||||
React,
|
||||
jQuery,
|
||||
MailPoet
|
||||
) => {
|
||||
const ListingFilters = React.createClass({
|
||||
handleFilterAction: function () {
|
||||
const filters = {};
|
||||
this.getAvailableFilters().map((filter, i) => {
|
||||
filters[this.refs['filter-'+i].name] = this.refs['filter-'+i].value;
|
||||
});
|
||||
if (this.props.onBeforeSelectFilter) {
|
||||
this.props.onBeforeSelectFilter(filters);
|
||||
}
|
||||
return this.props.onSelectFilter(filters);
|
||||
},
|
||||
handleEmptyTrash: function () {
|
||||
return this.props.onEmptyTrash();
|
||||
},
|
||||
getAvailableFilters: function () {
|
||||
const filters = this.props.filters;
|
||||
return Object.keys(filters).filter((filter) => {
|
||||
return !(
|
||||
filters[filter].length === 0
|
||||
|| (
|
||||
filters[filter].length === 1
|
||||
&& !filters[filter][0].value
|
||||
)
|
||||
);
|
||||
});
|
||||
},
|
||||
componentDidUpdate: function () {
|
||||
const selected_filters = this.props.filter;
|
||||
this.getAvailableFilters().map(
|
||||
(filter, i) => {
|
||||
if (selected_filters[filter] !== undefined && selected_filters[filter]) {
|
||||
jQuery(this.refs['filter-'+i])
|
||||
.val(selected_filters[filter])
|
||||
.trigger('change');
|
||||
}
|
||||
import React from 'react';
|
||||
import jQuery from 'jquery';
|
||||
import MailPoet from 'mailpoet';
|
||||
|
||||
const ListingFilters = React.createClass({
|
||||
handleFilterAction: function handleFilterAction() {
|
||||
const filters = {};
|
||||
this.getAvailableFilters().forEach((filter, i) => {
|
||||
filters[this[`filter-${i}`].name] = this[`filter-${i}`].value;
|
||||
});
|
||||
if (this.props.onBeforeSelectFilter) {
|
||||
this.props.onBeforeSelectFilter(filters);
|
||||
}
|
||||
return this.props.onSelectFilter(filters);
|
||||
},
|
||||
handleEmptyTrash: function handleEmptyTrash() {
|
||||
return this.props.onEmptyTrash();
|
||||
},
|
||||
getAvailableFilters: function getAvailableFilters() {
|
||||
const filters = this.props.filters;
|
||||
return Object.keys(filters).filter(filter => !(
|
||||
filters[filter].length === 0
|
||||
|| (
|
||||
filters[filter].length === 1
|
||||
&& !filters[filter][0].value
|
||||
)
|
||||
));
|
||||
},
|
||||
componentDidUpdate: function componentDidUpdate() {
|
||||
const selectedFilters = this.props.filter;
|
||||
this.getAvailableFilters().forEach(
|
||||
(filter, i) => {
|
||||
if (selectedFilters[filter] !== undefined && selectedFilters[filter]) {
|
||||
jQuery(this[`filter-${i}`])
|
||||
.val(selectedFilters[filter])
|
||||
.trigger('change');
|
||||
}
|
||||
);
|
||||
},
|
||||
render: function () {
|
||||
const filters = this.props.filters;
|
||||
const available_filters = this.getAvailableFilters()
|
||||
.map((filter, i) => {
|
||||
return (
|
||||
<select
|
||||
ref={ `filter-${i}` }
|
||||
key={ `filter-${i}` }
|
||||
name={ filter }
|
||||
>
|
||||
{ filters[filter].map((option, j) => {
|
||||
return (
|
||||
<option
|
||||
value={ option.value }
|
||||
key={ 'filter-option-' + j }
|
||||
>{ option.label }</option>
|
||||
);
|
||||
}) }
|
||||
</select>
|
||||
);
|
||||
});
|
||||
|
||||
let button;
|
||||
|
||||
if (available_filters.length > 0) {
|
||||
button = (
|
||||
<input
|
||||
id="post-query-submit"
|
||||
onClick={ this.handleFilterAction }
|
||||
type="submit"
|
||||
defaultValue={MailPoet.I18n.t('filter')}
|
||||
className="button" />
|
||||
);
|
||||
}
|
||||
|
||||
let empty_trash;
|
||||
if (this.props.group === 'trash') {
|
||||
empty_trash = (
|
||||
<input
|
||||
onClick={ this.handleEmptyTrash }
|
||||
type="submit"
|
||||
value={MailPoet.I18n.t('emptyTrash')}
|
||||
className="button"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="alignleft actions actions">
|
||||
{ available_filters }
|
||||
{ button }
|
||||
{ empty_trash }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
},
|
||||
render: function render() {
|
||||
const filters = this.props.filters;
|
||||
const availableFilters = this.getAvailableFilters()
|
||||
.map((filter, i) => (
|
||||
<select
|
||||
ref={(c) => { this[`filter-${i}`] = c; }}
|
||||
key={`filter-${filter}`}
|
||||
name={filter}
|
||||
>
|
||||
{ filters[filter].map(option => (
|
||||
<option
|
||||
value={option.value}
|
||||
key={`filter-option-${option.value}`}
|
||||
>{ option.label }</option>
|
||||
)) }
|
||||
</select>
|
||||
));
|
||||
|
||||
return ListingFilters;
|
||||
let button;
|
||||
|
||||
if (availableFilters.length > 0) {
|
||||
button = (
|
||||
<input
|
||||
id="post-query-submit"
|
||||
onClick={this.handleFilterAction}
|
||||
type="submit"
|
||||
defaultValue={MailPoet.I18n.t('filter')}
|
||||
className="button"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
let emptyTrash;
|
||||
if (this.props.group === 'trash') {
|
||||
emptyTrash = (
|
||||
<input
|
||||
onClick={this.handleEmptyTrash}
|
||||
type="submit"
|
||||
value={MailPoet.I18n.t('emptyTrash')}
|
||||
className="button"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="alignleft actions actions">
|
||||
{ availableFilters }
|
||||
{ button }
|
||||
{ emptyTrash }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default ListingFilters;
|
||||
|
@ -1,40 +1,57 @@
|
||||
define(['react', 'classnames'], (React, classNames) => {
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const ListingGroups = React.createClass({
|
||||
handleSelect: function (group) {
|
||||
return this.props.onSelectGroup(group);
|
||||
},
|
||||
render: function () {
|
||||
const groups = this.props.groups.map((group, index) => {
|
||||
if(group.name === 'trash' && group.count === 0) {
|
||||
return false;
|
||||
}
|
||||
class ListingGroups extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleSelect = this.handleSelect.bind(this);
|
||||
}
|
||||
|
||||
const classes = classNames(
|
||||
{ current : (group.name === this.props.group) }
|
||||
);
|
||||
handleSelect(group) {
|
||||
return this.props.onSelectGroup(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>
|
||||
render() {
|
||||
const groups = this.props.groups.map((group, index) => {
|
||||
if (group.name === 'trash' && group.count === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const classes = classNames(
|
||||
{ current: (group.name === this.props.group) }
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<ul className="subsubsub">
|
||||
{ groups }
|
||||
</ul>
|
||||
<li key={group.name}>
|
||||
{(index > 0) ? ' |' : ''}
|
||||
<a
|
||||
href="javascript:;"
|
||||
className={classes}
|
||||
onClick={() => this.handleSelect(group.name)}
|
||||
>
|
||||
{group.label}
|
||||
<span className="count">({ parseInt(group.count, 10).toLocaleString() })</span>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return ListingGroups;
|
||||
return (
|
||||
<ul className="subsubsub">
|
||||
{ groups }
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
ListingGroups.propTypes = {
|
||||
onSelectGroup: PropTypes.func.isRequired,
|
||||
groups: PropTypes.arrayOf(PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
count: PropTypes.number,
|
||||
})).isRequired,
|
||||
group: PropTypes.any.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
};
|
||||
|
||||
export default ListingGroups;
|
||||
|
@ -3,12 +3,12 @@ import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
const ListingHeader = React.createClass({
|
||||
handleSelectItems: function () {
|
||||
handleSelectItems: function handleSelectItems() {
|
||||
return this.props.onSelectItems(
|
||||
this.refs.toggle.checked
|
||||
this.toggle.checked
|
||||
);
|
||||
},
|
||||
render: function () {
|
||||
render: function render() {
|
||||
const columns = this.props.columns.map((column, index) => {
|
||||
const renderColumn = column;
|
||||
renderColumn.is_primary = (index === 0);
|
||||
@ -19,26 +19,30 @@ const ListingHeader = React.createClass({
|
||||
<ListingColumn
|
||||
onSort={this.props.onSort}
|
||||
sort_by={this.props.sort_by}
|
||||
key={ 'column-' + index }
|
||||
column={renderColumn} />
|
||||
key={`column-${column.name}`}
|
||||
column={renderColumn}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
let checkbox;
|
||||
|
||||
if(this.props.is_selectable === true) {
|
||||
if (this.props.is_selectable === true) {
|
||||
checkbox = (
|
||||
<th
|
||||
className="manage-column column-cb check-column">
|
||||
<label className="screen-reader-text">
|
||||
className="manage-column column-cb check-column"
|
||||
>
|
||||
<label className="screen-reader-text" htmlFor="select_all">
|
||||
{MailPoet.I18n.t('selectAll')}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="select_all"
|
||||
ref="toggle"
|
||||
checked={ this.props.selection }
|
||||
onChange={ this.handleSelectItems } />
|
||||
id="select_all"
|
||||
ref={(c) => { this.toggle = c; }}
|
||||
checked={this.props.selection}
|
||||
onChange={this.handleSelectItems}
|
||||
/>
|
||||
</th>
|
||||
);
|
||||
}
|
||||
@ -53,12 +57,12 @@ const ListingHeader = React.createClass({
|
||||
});
|
||||
|
||||
const ListingColumn = React.createClass({
|
||||
handleSort: function () {
|
||||
const sort_by = this.props.column.name;
|
||||
const sort_order = (this.props.column.sorted === 'asc') ? 'desc' : 'asc';
|
||||
this.props.onSort(sort_by, sort_order);
|
||||
handleSort: function handleSort() {
|
||||
const sortBy = this.props.column.name;
|
||||
const sortOrder = (this.props.column.sorted === 'asc') ? 'desc' : 'asc';
|
||||
this.props.onSort(sortBy, sortOrder);
|
||||
},
|
||||
render: function () {
|
||||
render: function render() {
|
||||
const classes = classNames(
|
||||
'manage-column',
|
||||
{ 'column-primary': this.props.column.is_primary },
|
||||
@ -68,11 +72,15 @@ const ListingColumn = React.createClass({
|
||||
);
|
||||
let label;
|
||||
|
||||
if(this.props.column.sortable === true) {
|
||||
if (this.props.column.sortable === true) {
|
||||
label = (
|
||||
<a onClick={ this.handleSort }>
|
||||
<a
|
||||
onClick={this.handleSort}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<span>{ this.props.column.label }</span>
|
||||
<span className="sorting-indicator"></span>
|
||||
<span className="sorting-indicator" />
|
||||
</a>
|
||||
);
|
||||
} else {
|
||||
@ -80,10 +88,11 @@ const ListingColumn = React.createClass({
|
||||
}
|
||||
return (
|
||||
<th
|
||||
className={ classes }
|
||||
id={this.props.column.name }
|
||||
role="columnheader"
|
||||
className={classes}
|
||||
id={this.props.column.name}
|
||||
scope="col"
|
||||
width={ this.props.column.width || null }
|
||||
width={this.props.column.width || null}
|
||||
>{label}</th>
|
||||
);
|
||||
},
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,185 +1,187 @@
|
||||
define([
|
||||
'react',
|
||||
'classnames',
|
||||
'mailpoet',
|
||||
], (
|
||||
React,
|
||||
classNames,
|
||||
MailPoet
|
||||
) => {
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import MailPoet from 'mailpoet';
|
||||
|
||||
const ListingPages = React.createClass({
|
||||
getInitialState: function () {
|
||||
return {
|
||||
page: null,
|
||||
};
|
||||
},
|
||||
setPage: function (page) {
|
||||
this.setState({
|
||||
page: null,
|
||||
}, () => {
|
||||
this.props.onSetPage(this.constrainPage(page));
|
||||
});
|
||||
},
|
||||
setFirstPage: function () {
|
||||
this.setPage(1);
|
||||
},
|
||||
setLastPage: function () {
|
||||
this.setPage(this.getLastPage());
|
||||
},
|
||||
setPreviousPage: function () {
|
||||
this.setPage(this.constrainPage(
|
||||
parseInt(this.props.page, 10) - 1)
|
||||
);
|
||||
},
|
||||
setNextPage: function () {
|
||||
this.setPage(this.constrainPage(
|
||||
parseInt(this.props.page, 10) + 1)
|
||||
);
|
||||
},
|
||||
constrainPage: function (page) {
|
||||
return Math.min(Math.max(1, Math.abs(~~page)), this.getLastPage());
|
||||
},
|
||||
handleSetManualPage: function (e) {
|
||||
if(e.which === 13) {
|
||||
this.setPage(this.state.page);
|
||||
const ListingPages = React.createClass({
|
||||
getInitialState: function getInitialState() {
|
||||
return {
|
||||
page: null,
|
||||
};
|
||||
},
|
||||
setPage: function setPage(page) {
|
||||
this.setState({
|
||||
page: null,
|
||||
}, () => {
|
||||
this.props.onSetPage(this.constrainPage(page));
|
||||
});
|
||||
},
|
||||
setFirstPage: function setFirstPage() {
|
||||
this.setPage(1);
|
||||
},
|
||||
setLastPage: function setLastPage() {
|
||||
this.setPage(this.getLastPage());
|
||||
},
|
||||
setPreviousPage: function setPreviousPage() {
|
||||
this.setPage(this.constrainPage(
|
||||
parseInt(this.props.page, 10) - 1)
|
||||
);
|
||||
},
|
||||
setNextPage: function setNextPage() {
|
||||
this.setPage(this.constrainPage(
|
||||
parseInt(this.props.page, 10) + 1)
|
||||
);
|
||||
},
|
||||
constrainPage: function constrainPage(page) {
|
||||
return Math.min(Math.max(1, Math.abs(Number(page))), this.getLastPage());
|
||||
},
|
||||
handleSetManualPage: function handleSetManualPage(e) {
|
||||
if (e.which === 13) {
|
||||
this.setPage(this.state.page);
|
||||
}
|
||||
},
|
||||
handleChangeManualPage: function handleChangeManualPage(e) {
|
||||
this.setState({
|
||||
page: e.target.value,
|
||||
});
|
||||
},
|
||||
handleBlurManualPage: function handleBlurManualPage(e) {
|
||||
this.setPage(e.target.value);
|
||||
},
|
||||
getLastPage: function getLastPage() {
|
||||
return Math.ceil(this.props.count / this.props.limit);
|
||||
},
|
||||
render: function render() {
|
||||
if (this.props.count === 0) {
|
||||
return false;
|
||||
}
|
||||
let pagination = false;
|
||||
let firstPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">«</span>
|
||||
);
|
||||
let previousPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">‹</span>
|
||||
);
|
||||
let nextPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">›</span>
|
||||
);
|
||||
let lastPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">»</span>
|
||||
);
|
||||
|
||||
if (this.props.limit > 0 && this.props.count > this.props.limit) {
|
||||
if (this.props.page > 1) {
|
||||
previousPage = (
|
||||
<a
|
||||
href="javascript:;"
|
||||
onClick={this.setPreviousPage}
|
||||
className="prev-page"
|
||||
>
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('previousPage')}</span>
|
||||
<span aria-hidden="true">‹</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
},
|
||||
handleChangeManualPage: function (e) {
|
||||
this.setState({
|
||||
page: e.target.value,
|
||||
});
|
||||
},
|
||||
handleBlurManualPage: function (e) {
|
||||
this.setPage(e.target.value);
|
||||
},
|
||||
getLastPage: function () {
|
||||
return Math.ceil(this.props.count / this.props.limit);
|
||||
},
|
||||
render: function () {
|
||||
if(this.props.count === 0) {
|
||||
return false;
|
||||
} else {
|
||||
let pagination = false;
|
||||
let firstPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">«</span>
|
||||
|
||||
if (this.props.page > 2) {
|
||||
firstPage = (
|
||||
<a
|
||||
href="javascript:;"
|
||||
onClick={this.setFirstPage}
|
||||
className="first-page"
|
||||
>
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('firstPage')}</span>
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
);
|
||||
let previousPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">‹</span>
|
||||
}
|
||||
|
||||
if (this.props.page < this.getLastPage()) {
|
||||
nextPage = (
|
||||
<a
|
||||
href="javascript:;"
|
||||
onClick={this.setNextPage}
|
||||
className="next-page"
|
||||
>
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('nextPage')}</span>
|
||||
<span aria-hidden="true">›</span>
|
||||
</a>
|
||||
);
|
||||
let nextPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">›</span>
|
||||
);
|
||||
let lastPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">»</span>
|
||||
}
|
||||
|
||||
if (this.props.page < this.getLastPage() - 1) {
|
||||
lastPage = (
|
||||
<a
|
||||
href="javascript:;"
|
||||
onClick={this.setLastPage}
|
||||
className="last-page"
|
||||
>
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('lastPage')}</span>
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
if(this.props.limit > 0 && this.props.count > this.props.limit) {
|
||||
if(this.props.page > 1) {
|
||||
previousPage = (
|
||||
<a href="javascript:;"
|
||||
onClick={ this.setPreviousPage }
|
||||
className="prev-page">
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('previousPage')}</span>
|
||||
<span aria-hidden="true">‹</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
let pageValue = this.props.page;
|
||||
if (this.state.page !== null) {
|
||||
pageValue = this.state.page;
|
||||
}
|
||||
|
||||
if(this.props.page > 2) {
|
||||
firstPage = (
|
||||
<a href="javascript:;"
|
||||
onClick={ this.setFirstPage }
|
||||
className="first-page">
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('firstPage')}</span>
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
if(this.props.page < this.getLastPage()) {
|
||||
nextPage = (
|
||||
<a href="javascript:;"
|
||||
onClick={ this.setNextPage }
|
||||
className="next-page">
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('nextPage')}</span>
|
||||
<span aria-hidden="true">›</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
if(this.props.page < this.getLastPage() - 1) {
|
||||
lastPage = (
|
||||
<a href="javascript:;"
|
||||
onClick={ this.setLastPage }
|
||||
className="last-page">
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('lastPage')}</span>
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
let pageValue = this.props.page;
|
||||
if(this.state.page !== null) {
|
||||
pageValue = this.state.page;
|
||||
}
|
||||
|
||||
pagination = (
|
||||
<span className="pagination-links">
|
||||
{firstPage}
|
||||
|
||||
{previousPage}
|
||||
|
||||
<span className="paging-input">
|
||||
<label
|
||||
className="screen-reader-text"
|
||||
htmlFor="current-page-selector">{MailPoet.I18n.t('currentPage')}</label>
|
||||
<input
|
||||
type="text"
|
||||
onChange={ this.handleChangeManualPage }
|
||||
onKeyUp={ this.handleSetManualPage }
|
||||
onBlur={ this.handleBlurManualPage }
|
||||
aria-describedby="table-paging"
|
||||
size="2"
|
||||
ref="page"
|
||||
value={ pageValue }
|
||||
name="paged"
|
||||
id="current-page-selector"
|
||||
className="current-page" />
|
||||
{MailPoet.I18n.t('pageOutOf')}
|
||||
<span className="total-pages">
|
||||
{Math.ceil(this.props.count / this.props.limit).toLocaleString()}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
{nextPage}
|
||||
|
||||
{lastPage}
|
||||
pagination = (
|
||||
<span className="pagination-links">
|
||||
{firstPage}
|
||||
|
||||
{previousPage}
|
||||
|
||||
<span className="paging-input">
|
||||
<label
|
||||
className="screen-reader-text"
|
||||
htmlFor="current-page-selector"
|
||||
>{MailPoet.I18n.t('currentPage')}</label>
|
||||
<input
|
||||
type="text"
|
||||
onChange={this.handleChangeManualPage}
|
||||
onKeyUp={this.handleSetManualPage}
|
||||
onBlur={this.handleBlurManualPage}
|
||||
aria-describedby="table-paging"
|
||||
size="2"
|
||||
value={pageValue}
|
||||
name="paged"
|
||||
id="current-page-selector"
|
||||
className="current-page"
|
||||
/>
|
||||
{MailPoet.I18n.t('pageOutOf')}
|
||||
<span className="total-pages">
|
||||
{Math.ceil(this.props.count / this.props.limit).toLocaleString()}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
</span>
|
||||
|
||||
{nextPage}
|
||||
|
||||
{lastPage}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
const classes = classNames(
|
||||
'tablenav-pages',
|
||||
{ 'one-page': (this.props.count <= this.props.limit) }
|
||||
);
|
||||
const classes = classNames(
|
||||
'tablenav-pages',
|
||||
{ 'one-page': (this.props.count <= this.props.limit) }
|
||||
);
|
||||
|
||||
let numberOfItemsLabel;
|
||||
if (this.props.count == 1) {
|
||||
numberOfItemsLabel = MailPoet.I18n.t('numberOfItemsSingular');
|
||||
} else {
|
||||
numberOfItemsLabel = MailPoet.I18n.t('numberOfItemsMultiple')
|
||||
.replace('%$1d', this.props.count.toLocaleString());
|
||||
}
|
||||
return (
|
||||
<div className={ classes }>
|
||||
<span className="displaying-num">{ numberOfItemsLabel }</span>
|
||||
{ pagination }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
let numberOfItemsLabel;
|
||||
if (Number(this.props.count) === 1) {
|
||||
numberOfItemsLabel = MailPoet.I18n.t('numberOfItemsSingular');
|
||||
} else {
|
||||
numberOfItemsLabel = MailPoet.I18n.t('numberOfItemsMultiple')
|
||||
.replace('%$1d', parseInt(this.props.count, 10).toLocaleString());
|
||||
}
|
||||
|
||||
return ListingPages;
|
||||
return (
|
||||
<div className={classes}>
|
||||
<span className="displaying-num">{ numberOfItemsLabel }</span>
|
||||
{ pagination }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = ListingPages;
|
||||
|
@ -1,47 +1,51 @@
|
||||
define([
|
||||
'mailpoet',
|
||||
'react',
|
||||
], (
|
||||
MailPoet,
|
||||
React
|
||||
) => {
|
||||
import React from 'react';
|
||||
import MailPoet from 'mailpoet';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const ListingSearch = React.createClass({
|
||||
handleSearch: function (e) {
|
||||
e.preventDefault();
|
||||
this.props.onSearch(
|
||||
this.refs.search.value
|
||||
);
|
||||
},
|
||||
componentWillReceiveProps: function (nextProps) {
|
||||
this.refs.search.value = nextProps.search;
|
||||
},
|
||||
render: function () {
|
||||
if(this.props.search === false) {
|
||||
return false;
|
||||
} else {
|
||||
return (
|
||||
<form name="search" onSubmit={this.handleSearch}>
|
||||
<p className="search-box">
|
||||
<label htmlFor="search_input" className="screen-reader-text">
|
||||
{MailPoet.I18n.t('searchLabel')}
|
||||
</label>
|
||||
<input
|
||||
type="search"
|
||||
id="search_input"
|
||||
ref="search"
|
||||
name="s"
|
||||
defaultValue={this.props.search} />
|
||||
<input
|
||||
type="submit"
|
||||
defaultValue={MailPoet.I18n.t('searchLabel')}
|
||||
className="button" />
|
||||
</p>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
class ListingSearch extends React.Component {
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.search.value = nextProps.search;
|
||||
this.handleSearch = this.handleSearch.bind(this);
|
||||
}
|
||||
|
||||
return ListingSearch;
|
||||
});
|
||||
handleSearch(e) {
|
||||
e.preventDefault();
|
||||
this.props.onSearch(
|
||||
this.search.value.trim()
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.props.search === false) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
<form name="search" onSubmit={this.handleSearch}>
|
||||
<p className="search-box">
|
||||
<label htmlFor="search_input" className="screen-reader-text">
|
||||
{MailPoet.I18n.t('searchLabel')}
|
||||
</label>
|
||||
<input
|
||||
type="search"
|
||||
id="search_input"
|
||||
ref={(c) => { this.search = c; }}
|
||||
name="s"
|
||||
defaultValue={this.props.search}
|
||||
/>
|
||||
<input
|
||||
type="submit"
|
||||
defaultValue={MailPoet.I18n.t('searchLabel')}
|
||||
className="button"
|
||||
/>
|
||||
</p>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ListingSearch.propTypes = {
|
||||
search: PropTypes.string.isRequired,
|
||||
onSearch: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default ListingSearch;
|
||||
|
@ -1,4 +1,4 @@
|
||||
define('mailpoet', [], function() {
|
||||
define('mailpoet', [], function mailpoet() {
|
||||
// A placeholder for MailPoet object
|
||||
var MailPoet = {};
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
/* eslint-disable func-names */
|
||||
define('modal', ['mailpoet', 'jquery'],
|
||||
function(mp, jQuery) {
|
||||
function (mp, jQuery) {
|
||||
'use strict';
|
||||
|
||||
var MailPoet = mp;
|
||||
/***************************************************************************
|
||||
/** *************************************************************************
|
||||
MailPoet Modal:
|
||||
|
||||
version: 0.9
|
||||
@ -19,7 +21,7 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
|
||||
// loading mode
|
||||
MailPoet.Modal.loading(bool);
|
||||
***************************************************************************/
|
||||
************************************************************************** */
|
||||
|
||||
MailPoet.Modal = {
|
||||
version: 0.9,
|
||||
@ -78,45 +80,44 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
options: {},
|
||||
templates: {
|
||||
overlay: '<div id="mailpoet_modal_overlay" style="display:none;"></div>',
|
||||
popup: '<div id="mailpoet_popup" tabindex="-1">'+
|
||||
'<div class="mailpoet_popup_wrapper">'+
|
||||
'<a href="javascript:;" id="mailpoet_modal_close"></a>'+
|
||||
'<div id="mailpoet_popup_title"><h2></h2></div>'+
|
||||
'<div class="mailpoet_popup_body clearfix"></div>'+
|
||||
'</div>'+
|
||||
popup: '<div id="mailpoet_popup" tabindex="-1">' +
|
||||
'<div class="mailpoet_popup_wrapper">' +
|
||||
'<a href="javascript:;" id="mailpoet_modal_close"></a>' +
|
||||
'<div id="mailpoet_popup_title"><h2></h2></div>' +
|
||||
'<div class="mailpoet_popup_body clearfix"></div>' +
|
||||
'</div>' +
|
||||
'</div>',
|
||||
loading: '<div id="mailpoet_loading" style="display:none;">'+
|
||||
'<div id="mailpoet_modal_loading_1" class="mailpoet_modal_loading"></div>'+
|
||||
'<div id="mailpoet_modal_loading_2" class="mailpoet_modal_loading"></div>'+
|
||||
'<div id="mailpoet_modal_loading_3" class="mailpoet_modal_loading"></div>'+
|
||||
loading: '<div id="mailpoet_loading" style="display:none;">' +
|
||||
'<div id="mailpoet_modal_loading_1" class="mailpoet_modal_loading"></div>' +
|
||||
'<div id="mailpoet_modal_loading_2" class="mailpoet_modal_loading"></div>' +
|
||||
'<div id="mailpoet_modal_loading_3" class="mailpoet_modal_loading"></div>' +
|
||||
'</div>',
|
||||
panel: '<div id="mailpoet_panel">'+
|
||||
'<a href="javascript:;" id="mailpoet_modal_close"></a>'+
|
||||
'<div class="mailpoet_panel_wrapper" tabindex="-1">'+
|
||||
'<div class="mailpoet_panel_body clearfix"></div>'+
|
||||
'</div>'+
|
||||
panel: '<div id="mailpoet_panel">' +
|
||||
'<a href="javascript:;" id="mailpoet_modal_close"></a>' +
|
||||
'<div class="mailpoet_panel_wrapper" tabindex="-1">' +
|
||||
'<div class="mailpoet_panel_body clearfix"></div>' +
|
||||
'</div>' +
|
||||
'</div>',
|
||||
subpanel: '<div class="mailpoet_panel_wrapper" tabindex="-1">'+
|
||||
'<div class="mailpoet_panel_body clearfix"></div>'+
|
||||
subpanel: '<div class="mailpoet_panel_wrapper" tabindex="-1">' +
|
||||
'<div class="mailpoet_panel_body clearfix"></div>' +
|
||||
'</div>'
|
||||
},
|
||||
getContentContainer: function() {
|
||||
return jQuery('.mailpoet_'+this.options.type+'_body');
|
||||
getContentContainer: function () {
|
||||
return jQuery('.mailpoet_' + this.options.type + '_body');
|
||||
},
|
||||
setRenderer: function(renderer) {
|
||||
setRenderer: function (renderer) {
|
||||
this.renderer = renderer;
|
||||
return this;
|
||||
},
|
||||
compileTemplate: function(template) {
|
||||
if(this.renderer === 'html') {
|
||||
return function() { return template; };
|
||||
} else {
|
||||
return Handlebars.compile(template);
|
||||
compileTemplate: function (template) {
|
||||
if (this.renderer === 'html') {
|
||||
return function () { return template; };
|
||||
}
|
||||
return false;
|
||||
return window.Handlebars.compile(template);
|
||||
},
|
||||
init: function(options) {
|
||||
if(this.initialized === true) {
|
||||
init: function (options) {
|
||||
var modal;
|
||||
if (this.initialized === true) {
|
||||
this.close();
|
||||
}
|
||||
// merge options
|
||||
@ -131,10 +132,10 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
// toggle overlay
|
||||
this.toggleOverlay(this.options.overlay);
|
||||
|
||||
if(this.options.type !== null) {
|
||||
if (this.options.type !== null) {
|
||||
// insert modal depending on its type
|
||||
if(this.options.type === 'popup') {
|
||||
var modal = this.compileTemplate(
|
||||
if (this.options.type === 'popup') {
|
||||
modal = this.compileTemplate(
|
||||
this.templates[this.options.type]
|
||||
);
|
||||
// create modal
|
||||
@ -143,7 +144,7 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
// set title
|
||||
jQuery('#mailpoet_popup_title h2')
|
||||
.html(this.options.title);
|
||||
} else if(this.options.type === 'panel') {
|
||||
} else if (this.options.type === 'panel') {
|
||||
// create panel
|
||||
jQuery('#mailpoet_modal_overlay')
|
||||
.after(this.templates[this.options.type]);
|
||||
@ -152,16 +153,16 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
// add proper overlay class
|
||||
jQuery('#mailpoet_modal_overlay')
|
||||
.removeClass('mailpoet_popup_overlay mailpoet_panel_overlay')
|
||||
.addClass('mailpoet_'+this.options.type+'_overlay');
|
||||
.addClass('mailpoet_' + this.options.type + '_overlay');
|
||||
}
|
||||
|
||||
// set "success" callback if specified
|
||||
if(options.onSuccess !== undefined) {
|
||||
if (options.onSuccess !== undefined) {
|
||||
this.options.onSuccess = options.onSuccess;
|
||||
}
|
||||
|
||||
// set "cancel" callback if specified
|
||||
if(options.onCancel !== undefined) {
|
||||
if (options.onCancel !== undefined) {
|
||||
this.options.onCancel = options.onCancel;
|
||||
}
|
||||
|
||||
@ -178,111 +179,112 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
|
||||
return this;
|
||||
},
|
||||
initOverlay: function(toggle) {
|
||||
if(jQuery('#mailpoet_modal_overlay').length === 0) {
|
||||
initOverlay: function () {
|
||||
if (jQuery('#mailpoet_modal_overlay').length === 0) {
|
||||
// insert overlay into the DOM
|
||||
jQuery('body').append(this.templates.overlay);
|
||||
jQuery('body').append(this.templates.overlay);
|
||||
// insert loading indicator into overlay
|
||||
jQuery('#mailpoet_modal_overlay').append(this.templates.loading);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
toggleOverlay: function(toggle) {
|
||||
if(toggle === true) {
|
||||
jQuery('#mailpoet_modal_overlay')
|
||||
jQuery('#mailpoet_modal_overlay').append(this.templates.loading);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
toggleOverlay: function (toggle) {
|
||||
if (toggle === true) {
|
||||
jQuery('#mailpoet_modal_overlay')
|
||||
.removeClass('mailpoet_overlay_hidden');
|
||||
} else {
|
||||
jQuery('#mailpoet_modal_overlay')
|
||||
} else {
|
||||
jQuery('#mailpoet_modal_overlay')
|
||||
.addClass('mailpoet_overlay_hidden');
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
setupEvents: function() {
|
||||
return this;
|
||||
},
|
||||
setupEvents: function () {
|
||||
// close popup when user clicks on close button
|
||||
jQuery('#mailpoet_modal_close').on('click', this.cancel.bind(this));
|
||||
|
||||
// close popup when user clicks on overlay
|
||||
jQuery('#mailpoet_modal_overlay').on('click', function(e) {
|
||||
jQuery('#mailpoet_modal_overlay').on('click', function (e) {
|
||||
// we need to make sure that we are actually clicking on the overlay
|
||||
// because when clicking on the popup content, it will trigger
|
||||
// the click event on the overlay
|
||||
if(e.target.id === 'mailpoet_modal_overlay') { this.cancel(); }
|
||||
if (e.target.id === 'mailpoet_modal_overlay') { this.cancel(); }
|
||||
}.bind(this));
|
||||
|
||||
// close popup when user presses ESC key
|
||||
jQuery(document).on('keyup.mailpoet_modal', function(e) {
|
||||
if(this.opened === false) { return false; }
|
||||
if(e.keyCode === 27) { this.cancel(); }
|
||||
jQuery(document).on('keyup.mailpoet_modal', function (e) {
|
||||
if (this.opened === false) { return false; }
|
||||
if (e.keyCode === 27) { this.cancel(); }
|
||||
return true;
|
||||
}.bind(this));
|
||||
|
||||
// make sure the popup is repositioned when the window is resized
|
||||
jQuery(window).on('resize.mailpoet_modal', function() {
|
||||
jQuery(window).on('resize.mailpoet_modal', function () {
|
||||
this.setPosition();
|
||||
}.bind(this));
|
||||
|
||||
return this;
|
||||
},
|
||||
removeEvents: function() {
|
||||
removeEvents: function () {
|
||||
jQuery(document).unbind('keyup.mailpoet_modal');
|
||||
jQuery(window).unbind('resize.mailpoet_modal');
|
||||
jQuery('#mailpoet_modal_close').off('click');
|
||||
if(this.options.overlay === true) {
|
||||
if (this.options.overlay === true) {
|
||||
jQuery('#mailpoet_modal_overlay').off('click');
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
lock: function() {
|
||||
lock: function () {
|
||||
this.locked = true;
|
||||
|
||||
return this;
|
||||
},
|
||||
unlock: function() {
|
||||
unlock: function () {
|
||||
this.locked = false;
|
||||
|
||||
return this;
|
||||
},
|
||||
isLocked: function() {
|
||||
isLocked: function () {
|
||||
return this.locked;
|
||||
},
|
||||
loadTemplate: function() {
|
||||
if(this.subpanels.length > 0) {
|
||||
loadTemplate: function () {
|
||||
if (this.subpanels.length > 0) {
|
||||
// hide panel
|
||||
jQuery('.mailpoet_'+this.options.type+'_wrapper').hide();
|
||||
jQuery('.mailpoet_' + this.options.type + '_wrapper').hide();
|
||||
|
||||
// add sub panel wrapper
|
||||
jQuery('#mailpoet_'+this.options.type)
|
||||
.append(this.templates['subpanel']);
|
||||
jQuery('#mailpoet_' + this.options.type)
|
||||
.append(this.templates.subpanel);
|
||||
|
||||
// add sub panel content
|
||||
jQuery('.mailpoet_'+this.options.type+'_body').last()
|
||||
jQuery('.mailpoet_' + this.options.type + '_body').last()
|
||||
.html(this.subpanels[(this.subpanels.length - 1)].element);
|
||||
|
||||
// focus on sub panel
|
||||
if(this.options.focus) {
|
||||
this.focus();
|
||||
}
|
||||
} else if (this.options.element) {
|
||||
jQuery('.mailpoet_'+this.options.type+'_body').empty();
|
||||
jQuery('.mailpoet_'+this.options.type+'_body')
|
||||
if (this.options.focus) {
|
||||
this.focus();
|
||||
}
|
||||
} else if (this.options.element) {
|
||||
jQuery('.mailpoet_' + this.options.type + '_body').empty();
|
||||
jQuery('.mailpoet_' + this.options.type + '_body')
|
||||
.append(this.options.element);
|
||||
} else {
|
||||
jQuery('.mailpoet_'+this.options.type+'_body')
|
||||
} else {
|
||||
jQuery('.mailpoet_' + this.options.type + '_body')
|
||||
.html(
|
||||
this.options.body_template(
|
||||
this.options.data
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
loadUrl: function() {
|
||||
if(this.options.method === 'get') {
|
||||
return this;
|
||||
},
|
||||
loadUrl: function () {
|
||||
if (this.options.method === 'get') {
|
||||
// make ajax request
|
||||
jQuery.getJSON(this.options.url,
|
||||
function(data) {
|
||||
jQuery.getJSON(this.options.url,
|
||||
function (data) {
|
||||
this.options.data = jQuery.extend({}, this.options.data, data);
|
||||
// load template using fetched data
|
||||
this.loadTemplate();
|
||||
@ -290,10 +292,10 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
this.showModal();
|
||||
}.bind(this)
|
||||
);
|
||||
} else if(this.options.method === 'post') {
|
||||
} else if (this.options.method === 'post') {
|
||||
// make ajax request
|
||||
jQuery.post(this.options.url, JSON.stringify(this.options.params),
|
||||
function(data) {
|
||||
jQuery.post(this.options.url, JSON.stringify(this.options.params),
|
||||
function (data) {
|
||||
this.options.data = jQuery.extend({}, this.options.data, data);
|
||||
// load template using fetched data
|
||||
this.loadTemplate();
|
||||
@ -302,342 +304,339 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
}.bind(this),
|
||||
'json'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
setDimensions: function() {
|
||||
switch(this.options.type) {
|
||||
case 'popup':
|
||||
return this;
|
||||
},
|
||||
setDimensions: function () {
|
||||
switch (this.options.type) {
|
||||
case 'popup':
|
||||
// set popup dimensions
|
||||
jQuery('#mailpoet_popup').css({
|
||||
width: this.options.width,
|
||||
height: this.options.height
|
||||
});
|
||||
jQuery('#mailpoet_popup').css({
|
||||
width: this.options.width,
|
||||
height: this.options.height
|
||||
});
|
||||
// set popup wrapper height
|
||||
jQuery('#mailpoet_popup_wrapper').css({
|
||||
height: this.options.height
|
||||
});
|
||||
jQuery('#mailpoet_popup_wrapper').css({
|
||||
height: this.options.height
|
||||
});
|
||||
break;
|
||||
case 'panel':
|
||||
case 'panel':
|
||||
// set dimensions
|
||||
if(this.options.position === 'right') {
|
||||
if (this.options.position === 'right') {
|
||||
jQuery('#mailpoet_panel').css({
|
||||
width: this.options.width,
|
||||
right: 0,
|
||||
marginRight: '-' + this.options.width,
|
||||
left: 'auto'
|
||||
});
|
||||
} else if (this.options.position === 'left') {
|
||||
jQuery('#mailpoet_panel').css({
|
||||
width: this.options.width,
|
||||
left: 0,
|
||||
marginLeft: '-' + this.options.width,
|
||||
right: 'auto'
|
||||
});
|
||||
}
|
||||
jQuery('#mailpoet_panel').css({ minHeight: 'auto' });
|
||||
break;
|
||||
default: throw new Error('Incorrect type');
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
setPosition: function () {
|
||||
var screenWidth;
|
||||
var screenHeight;
|
||||
var modalWidth;
|
||||
var modalHeight;
|
||||
switch (this.options.type) {
|
||||
case 'popup':
|
||||
screenWidth = jQuery(window).width();
|
||||
screenHeight = jQuery(window).height();
|
||||
modalWidth = jQuery('.mailpoet_' + this.options.type + '_wrapper').width();
|
||||
modalHeight = jQuery('.mailpoet_' + this.options.type + '_wrapper').height();
|
||||
|
||||
// set position of popup depending on screen dimensions.
|
||||
jQuery('#mailpoet_popup').css({
|
||||
top: Math.max(48, parseInt((screenHeight / 2) - (modalHeight / 2), 10)),
|
||||
left: Math.max(0, parseInt((screenWidth / 2) - (modalWidth / 2), 10))
|
||||
});
|
||||
break;
|
||||
case 'panel':
|
||||
setTimeout(function () {
|
||||
// set position of popup depending on screen dimensions.
|
||||
if (this.options.position === 'right') {
|
||||
jQuery('#mailpoet_panel').css({
|
||||
width: this.options.width,
|
||||
right: 0,
|
||||
marginRight: '-' + this.options.width,
|
||||
left: 'auto'
|
||||
marginRight: 0
|
||||
});
|
||||
} else if(this.options.position === 'left') {
|
||||
} else if (this.options.position === 'left') {
|
||||
jQuery('#mailpoet_panel').css({
|
||||
width: this.options.width,
|
||||
left: 0,
|
||||
marginLeft: '-' + this.options.width,
|
||||
right: 'auto'
|
||||
marginLeft: 0
|
||||
});
|
||||
}
|
||||
jQuery('#mailpoet_panel').css({ minHeight: 'auto' });
|
||||
}.bind(this), 0);
|
||||
break;
|
||||
}
|
||||
default: throw new Error('Incorrect type');
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
setPosition: function() {
|
||||
switch(this.options.type) {
|
||||
case 'popup':
|
||||
var screenWidth = jQuery(window).width(),
|
||||
screenHeight = jQuery(window).height(),
|
||||
modalWidth = jQuery('.mailpoet_'+ this.options.type +'_wrapper').width(),
|
||||
modalHeight = jQuery('.mailpoet_'+ this.options.type +'_wrapper').height();
|
||||
|
||||
var top = Math.max(48, parseInt((screenHeight / 2) - (modalHeight / 2))),
|
||||
left = Math.max(0, parseInt((screenWidth / 2) - (modalWidth / 2)));
|
||||
|
||||
// set position of popup depending on screen dimensions.
|
||||
jQuery('#mailpoet_popup').css({
|
||||
top: top,
|
||||
left: left
|
||||
});
|
||||
break;
|
||||
case 'panel':
|
||||
setTimeout(function() {
|
||||
// set position of popup depending on screen dimensions.
|
||||
if(this.options.position === 'right') {
|
||||
jQuery('#mailpoet_panel').css({
|
||||
marginRight: 0
|
||||
});
|
||||
} else if(this.options.position === 'left') {
|
||||
jQuery('#mailpoet_panel').css({
|
||||
marginLeft: 0
|
||||
});
|
||||
}
|
||||
}.bind(this), 0);
|
||||
break;
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
showModal: function() {
|
||||
return this;
|
||||
},
|
||||
showModal: function () {
|
||||
// set modal dimensions
|
||||
this.setDimensions();
|
||||
this.setDimensions();
|
||||
|
||||
// remember the previously focused element
|
||||
this.prevFocus = jQuery(':focus');
|
||||
|
||||
// add a flag on the body so that we can prevent scrolling
|
||||
jQuery('body').addClass('mailpoet_modal_opened');
|
||||
this.prevFocus = jQuery(':focus');
|
||||
|
||||
// show popup
|
||||
jQuery('#mailpoet_'+this.options.type).show();
|
||||
jQuery('#mailpoet_' + this.options.type).show();
|
||||
|
||||
// display overlay
|
||||
this.showOverlay();
|
||||
this.showOverlay();
|
||||
|
||||
// set modal position
|
||||
this.setPosition();
|
||||
this.setPosition();
|
||||
|
||||
// add class on highlighted elements
|
||||
if(this.options.highlight !== null) {
|
||||
if(this.options.highlight.length > 0) {
|
||||
this.highlightOn(this.options.highlight);
|
||||
}
|
||||
if (this.options.highlight !== null) {
|
||||
if (this.options.highlight.length > 0) {
|
||||
this.highlightOn(this.options.highlight);
|
||||
}
|
||||
}
|
||||
|
||||
if(this.options.focus) {
|
||||
if (this.options.focus) {
|
||||
this.focus();
|
||||
}
|
||||
|
||||
// set popup as opened
|
||||
this.opened = true;
|
||||
|
||||
// trigger init event if specified
|
||||
if (this.options.onInit !== null) {
|
||||
this.options.onInit(this);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
focus: function () {
|
||||
if (this.options.type === 'popup') {
|
||||
jQuery('#mailpoet_' + this.options.type).focus();
|
||||
} else {
|
||||
// panel and subpanel
|
||||
jQuery('#mailpoet_' + this.options.type + ' .mailpoet_panel_wrapper')
|
||||
.filter(':visible').focus();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
highlightOn: function (element) {
|
||||
jQuery(element).addClass('mailpoet_modal_highlight');
|
||||
return this;
|
||||
},
|
||||
highlightOff: function () {
|
||||
jQuery('.mailpoet_modal_highlight')
|
||||
.removeClass('mailpoet_modal_highlight');
|
||||
return this;
|
||||
},
|
||||
hideModal: function () {
|
||||
// set modal as closed
|
||||
this.opened = false;
|
||||
|
||||
// hide modal
|
||||
jQuery('#mailpoet_' + this.options.type).hide();
|
||||
|
||||
// remove class on highlighted elements
|
||||
this.highlightOff();
|
||||
|
||||
return this;
|
||||
},
|
||||
showOverlay: function () {
|
||||
jQuery('#mailpoet_modal_overlay').show();
|
||||
return this;
|
||||
},
|
||||
hideOverlay: function () {
|
||||
jQuery('#mailpoet_modal_overlay').hide();
|
||||
return this;
|
||||
},
|
||||
popup: function (opts) {
|
||||
// get options
|
||||
var options = opts || {};
|
||||
// set modal type
|
||||
options.type = 'popup';
|
||||
// set overlay state
|
||||
options.overlay = options.overlay || true;
|
||||
// initialize modal
|
||||
this.init(options);
|
||||
// open modal
|
||||
this.open();
|
||||
|
||||
return this;
|
||||
},
|
||||
panel: function (opts) {
|
||||
// get options
|
||||
var options = opts || {};
|
||||
// reset subpanels
|
||||
this.subpanels = [];
|
||||
// set modal type
|
||||
options.type = 'panel';
|
||||
// set overlay state
|
||||
options.overlay = options.overlay || false;
|
||||
// set highlighted element
|
||||
options.highlight = options.highlight || null;
|
||||
// set modal dimensions
|
||||
options.width = options.width || '40%';
|
||||
options.height = options.height || 'auto';
|
||||
// initialize modal
|
||||
this.init(options);
|
||||
// open modal
|
||||
this.open();
|
||||
|
||||
return this;
|
||||
},
|
||||
subpanel: function (options) {
|
||||
if (this.opened === false) {
|
||||
// if no panel is already opened, let's create one instead
|
||||
this.panel(options);
|
||||
} else {
|
||||
// if a panel is already opened, add a sub panel to it
|
||||
this.subpanels.push(options);
|
||||
this.loadTemplate();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
loading: function (toggle) {
|
||||
// make sure the overlay is initialized and that it's visible
|
||||
this.initOverlay(true);
|
||||
|
||||
if (toggle === true) {
|
||||
this.showLoading();
|
||||
} else {
|
||||
this.hideLoading();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
showLoading: function () {
|
||||
jQuery('#mailpoet_loading').show();
|
||||
|
||||
// add loading class to overlay
|
||||
jQuery('#mailpoet_modal_overlay')
|
||||
.addClass('mailpoet_overlay_loading');
|
||||
|
||||
return this;
|
||||
},
|
||||
hideLoading: function () {
|
||||
jQuery('#mailpoet_loading').hide();
|
||||
|
||||
// remove loading class from overlay
|
||||
jQuery('#mailpoet_modal_overlay')
|
||||
.removeClass('mailpoet_overlay_loading');
|
||||
|
||||
return this;
|
||||
},
|
||||
open: function () {
|
||||
// load template if specified
|
||||
if (this.options.template !== null) {
|
||||
// check if a url was specified to get extra data
|
||||
if (this.options.url !== null) {
|
||||
this.loadUrl();
|
||||
} else {
|
||||
// load template
|
||||
this.loadTemplate();
|
||||
|
||||
// show modal window
|
||||
this.showModal();
|
||||
}
|
||||
} else {
|
||||
this.cancel();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
success: function () {
|
||||
if (this.subpanels.length > 0) {
|
||||
if (this.subpanels[(this.subpanels.length - 1)].onSuccess !== undefined) {
|
||||
this
|
||||
.subpanels[(this.subpanels.length - 1)]
|
||||
.onSuccess(this.subpanels[(this.subpanels.length - 1)].data);
|
||||
}
|
||||
} else if (this.options.onSuccess !== null) {
|
||||
this.options.onSuccess(this.options.data);
|
||||
}
|
||||
this.close();
|
||||
|
||||
return this;
|
||||
},
|
||||
cancel: function () {
|
||||
if (this.subpanels.length > 0) {
|
||||
if (this.subpanels[(this.subpanels.length - 1)].onCancel !== undefined) {
|
||||
this
|
||||
.subpanels[(this.subpanels.length - 1)]
|
||||
.onCancel(this.subpanels[(this.subpanels.length - 1)].data);
|
||||
}
|
||||
} else if (this.options.onCancel !== null) {
|
||||
this.options.onCancel(this.options.data);
|
||||
}
|
||||
this.close();
|
||||
|
||||
return this;
|
||||
},
|
||||
destroy: function () {
|
||||
this.hideOverlay();
|
||||
|
||||
// remove extra modal
|
||||
if (jQuery('#mailpoet_' + this.options.type).length > 0) {
|
||||
jQuery('#mailpoet_' + this.options.type).remove();
|
||||
}
|
||||
|
||||
this.initialized = false;
|
||||
|
||||
return this;
|
||||
},
|
||||
close: function () {
|
||||
if (this.isLocked() === true) { return this; }
|
||||
|
||||
if (this.subpanels.length > 0) {
|
||||
// close subpanel
|
||||
jQuery('.mailpoet_' + this.options.type + '_wrapper').last().remove();
|
||||
|
||||
// show previous panel
|
||||
jQuery('.mailpoet_' + this.options.type + '_wrapper').last().show();
|
||||
|
||||
// remove last subpanels
|
||||
this.subpanels.pop();
|
||||
|
||||
// focus on previous panel
|
||||
if (this.options.focus) {
|
||||
this.focus();
|
||||
}
|
||||
|
||||
// set popup as opened
|
||||
this.opened = true;
|
||||
|
||||
// trigger init event if specified
|
||||
if(this.options.onInit !== null) {
|
||||
this.options.onInit(this);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
focus: function() {
|
||||
if(this.options.type == 'popup') {
|
||||
jQuery('#mailpoet_'+this.options.type).focus();
|
||||
} else {
|
||||
// panel and subpanel
|
||||
jQuery('#mailpoet_'+this.options.type+' .mailpoet_panel_wrapper')
|
||||
.filter(':visible').focus();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
highlightOn: function(element) {
|
||||
jQuery(element).addClass('mailpoet_modal_highlight');
|
||||
return this;
|
||||
},
|
||||
highlightOff: function() {
|
||||
jQuery('.mailpoet_modal_highlight')
|
||||
.removeClass('mailpoet_modal_highlight');
|
||||
return this;
|
||||
},
|
||||
hideModal: function(callback) {
|
||||
// set modal as closed
|
||||
this.opened = false;
|
||||
|
||||
// hide modal
|
||||
jQuery('#mailpoet_'+this.options.type).hide();
|
||||
|
||||
// remove class on highlighted elements
|
||||
this.highlightOff();
|
||||
|
||||
// remove class from body to let it be scrollable
|
||||
jQuery('body').removeClass('mailpoet_modal_opened');
|
||||
|
||||
return this;
|
||||
},
|
||||
showOverlay: function(force) {
|
||||
jQuery('#mailpoet_modal_overlay').show();
|
||||
return this;
|
||||
},
|
||||
hideOverlay: function() {
|
||||
jQuery('#mailpoet_modal_overlay').hide();
|
||||
return this;
|
||||
},
|
||||
popup: function(opts) {
|
||||
// get options
|
||||
var options = opts || {};
|
||||
// set modal type
|
||||
options.type = 'popup';
|
||||
// set overlay state
|
||||
options.overlay = options.overlay || true;
|
||||
// initialize modal
|
||||
this.init(options);
|
||||
// open modal
|
||||
this.open();
|
||||
|
||||
return this;
|
||||
},
|
||||
panel: function(opts) {
|
||||
// get options
|
||||
var options = opts || {};
|
||||
// reset subpanels
|
||||
this.subpanels = [];
|
||||
// set modal type
|
||||
options.type = 'panel';
|
||||
// set overlay state
|
||||
options.overlay = options.overlay || false;
|
||||
// set highlighted element
|
||||
options.highlight = options.highlight || null;
|
||||
// set modal dimensions
|
||||
options.width = options.width || '40%';
|
||||
options.height = options.height || 'auto';
|
||||
// initialize modal
|
||||
this.init(options);
|
||||
// open modal
|
||||
this.open();
|
||||
|
||||
return this;
|
||||
},
|
||||
subpanel: function(options) {
|
||||
if(this.opened === false) {
|
||||
// if no panel is already opened, let's create one instead
|
||||
this.panel(options);
|
||||
} else {
|
||||
// if a panel is already opened, add a sub panel to it
|
||||
this.subpanels.push(options);
|
||||
this.loadTemplate();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
loading: function(toggle) {
|
||||
// make sure the overlay is initialized and that it's visible
|
||||
this.initOverlay(true);
|
||||
|
||||
if(toggle === true) {
|
||||
this.showLoading();
|
||||
} else {
|
||||
this.hideLoading();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
showLoading: function() {
|
||||
jQuery('#mailpoet_loading').show();
|
||||
|
||||
// add loading class to overlay
|
||||
jQuery('#mailpoet_modal_overlay')
|
||||
.addClass('mailpoet_overlay_loading');
|
||||
|
||||
return this;
|
||||
},
|
||||
hideLoading: function() {
|
||||
jQuery('#mailpoet_loading').hide();
|
||||
|
||||
// remove loading class from overlay
|
||||
jQuery('#mailpoet_modal_overlay')
|
||||
.removeClass('mailpoet_overlay_loading');
|
||||
|
||||
return this;
|
||||
},
|
||||
open: function() {
|
||||
// load template if specified
|
||||
if(this.options.template !== null) {
|
||||
// check if a url was specified to get extra data
|
||||
if(this.options.url !== null) {
|
||||
this.loadUrl();
|
||||
} else {
|
||||
// load template
|
||||
this.loadTemplate();
|
||||
|
||||
// show modal window
|
||||
this.showModal();
|
||||
}
|
||||
} else {
|
||||
this.cancel();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
success: function() {
|
||||
if(this.subpanels.length > 0) {
|
||||
if(this.subpanels[(this.subpanels.length - 1)].onSuccess !== undefined) {
|
||||
this.subpanels[(this.subpanels.length - 1)].onSuccess(this.subpanels[(this.subpanels.length - 1)].data);
|
||||
}
|
||||
} else {
|
||||
if(this.options.onSuccess !== null) {
|
||||
this.options.onSuccess(this.options.data);
|
||||
}
|
||||
}
|
||||
this.close();
|
||||
|
||||
return this;
|
||||
},
|
||||
cancel: function() {
|
||||
if(this.subpanels.length > 0) {
|
||||
if(this.subpanels[(this.subpanels.length - 1)].onCancel !== undefined) {
|
||||
this.subpanels[(this.subpanels.length - 1)].onCancel(this.subpanels[(this.subpanels.length - 1)].data);
|
||||
}
|
||||
} else {
|
||||
if(this.options.onCancel !== null) {
|
||||
this.options.onCancel(this.options.data);
|
||||
}
|
||||
}
|
||||
this.close();
|
||||
|
||||
return this;
|
||||
},
|
||||
destroy: function() {
|
||||
this.hideOverlay();
|
||||
|
||||
// remove extra modal
|
||||
if(jQuery('#mailpoet_'+this.options.type).length > 0) {
|
||||
jQuery('#mailpoet_'+this.options.type).remove();
|
||||
}
|
||||
|
||||
this.initialized = false;
|
||||
|
||||
return this;
|
||||
},
|
||||
close: function() {
|
||||
if(this.isLocked() === true) { return this; }
|
||||
|
||||
if(this.subpanels.length > 0) {
|
||||
// close subpanel
|
||||
jQuery('.mailpoet_'+this.options.type+'_wrapper').last().remove();
|
||||
|
||||
// show previous panel
|
||||
jQuery('.mailpoet_'+this.options.type+'_wrapper').last().show();
|
||||
|
||||
// remove last subpanels
|
||||
this.subpanels.pop();
|
||||
|
||||
// focus on previous panel
|
||||
if(this.options.focus) {
|
||||
this.focus();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// remove event handlers
|
||||
this.removeEvents();
|
||||
|
||||
// hide modal window
|
||||
this.hideModal();
|
||||
|
||||
// destroy modal element
|
||||
this.destroy();
|
||||
|
||||
// restore the previously focused element
|
||||
if(this.prevFocus !== undefined){
|
||||
this.prevFocus.focus();
|
||||
}
|
||||
|
||||
// reset options
|
||||
this.options = {
|
||||
onSuccess: null,
|
||||
onCancel: null
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// remove event handlers
|
||||
this.removeEvents();
|
||||
|
||||
// hide modal window
|
||||
this.hideModal();
|
||||
|
||||
// destroy modal element
|
||||
this.destroy();
|
||||
|
||||
// restore the previously focused element
|
||||
if (this.prevFocus !== undefined) {
|
||||
this.prevFocus.focus();
|
||||
}
|
||||
|
||||
// reset options
|
||||
this.options = {
|
||||
onSuccess: null,
|
||||
onCancel: null
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
@ -1,5 +1,7 @@
|
||||
define('mp2migrator', ['mailpoet', 'jquery'], function(mp, jQuery) {
|
||||
/* eslint-disable func-names */
|
||||
define('mp2migrator', ['mailpoet', 'jquery'], function (mp, jQuery) {
|
||||
'use strict';
|
||||
|
||||
var MailPoet = mp;
|
||||
MailPoet.MP2Migrator = {
|
||||
|
||||
@ -11,7 +13,7 @@ define('mp2migrator', ['mailpoet', 'jquery'], function(mp, jQuery) {
|
||||
clearTimeout(MailPoet.MP2Migrator.displayLogs_timeout);
|
||||
clearTimeout(MailPoet.MP2Migrator.updateProgressbar_timeout);
|
||||
clearTimeout(MailPoet.MP2Migrator.update_wordpress_info_timeout);
|
||||
setTimeout(MailPoet.MP2Migrator.updateDisplay, 1000)
|
||||
setTimeout(MailPoet.MP2Migrator.updateDisplay, 1000);
|
||||
},
|
||||
|
||||
stopLogger: function () {
|
||||
@ -25,51 +27,54 @@ define('mp2migrator', ['mailpoet', 'jquery'], function(mp, jQuery) {
|
||||
|
||||
displayLogs: function () {
|
||||
jQuery.ajax({
|
||||
url: mailpoet_mp2_migrator.log_file_url,
|
||||
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')) {
|
||||
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')) {
|
||||
} else if (row === MailPoet.I18n.t('import_complete')) { // Test if the import is 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');
|
||||
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);
|
||||
if (MailPoet.MP2Migrator.is_logging) {
|
||||
MailPoet.MP2Migrator.displayLogs_timeout = setTimeout(
|
||||
MailPoet.MP2Migrator.displayLogs,
|
||||
1000
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
updateProgressbar: function () {
|
||||
jQuery.ajax({
|
||||
url: mailpoet_mp2_migrator.progress_url,
|
||||
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);
|
||||
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) {
|
||||
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);
|
||||
if (MailPoet.MP2Migrator.is_logging) {
|
||||
MailPoet.MP2Migrator.updateProgressbar_timeout = setTimeout(
|
||||
MailPoet.MP2Migrator.updateProgressbar,
|
||||
1000
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -96,19 +101,20 @@ define('mp2migrator', ['mailpoet', 'jquery'], function(mp, jQuery) {
|
||||
}
|
||||
}).always(function () {
|
||||
MailPoet.MP2Migrator.stopLogger();
|
||||
MailPoet.MP2Migrator.updateDisplay(); // Get the latest information after the import was stopped
|
||||
// Get the latest information after the import was stopped
|
||||
MailPoet.MP2Migrator.updateDisplay();
|
||||
MailPoet.MP2Migrator.reactivateImportButton();
|
||||
}).done(function (response) {
|
||||
if(response) {
|
||||
if (response) {
|
||||
MailPoet.MP2Migrator.fatal_error = response.data;
|
||||
}
|
||||
}).fail(function (response) {
|
||||
if(response.errors.length > 0) {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map(function (error) {
|
||||
return error.message;
|
||||
}),
|
||||
{scroll: true}
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -132,14 +138,15 @@ define('mp2migrator', ['mailpoet', 'jquery'], function(mp, jQuery) {
|
||||
}).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
|
||||
// Get the latest information after the import was stopped
|
||||
MailPoet.MP2Migrator.updateDisplay();
|
||||
}).fail(function (response) {
|
||||
if(response.errors.length > 0) {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map(function (error) {
|
||||
return error.message;
|
||||
}),
|
||||
{scroll: true}
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -157,12 +164,12 @@ define('mp2migrator', ['mailpoet', 'jquery'], function(mp, jQuery) {
|
||||
}).done(function () {
|
||||
MailPoet.MP2Migrator.gotoWelcomePage();
|
||||
}).fail(function (response) {
|
||||
if(response.errors.length > 0) {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map(function (error) {
|
||||
return error.message;
|
||||
}),
|
||||
{scroll: true}
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -175,35 +182,34 @@ define('mp2migrator', ['mailpoet', 'jquery'], function(mp, jQuery) {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Actions to run when the DOM is ready
|
||||
*/
|
||||
jQuery(function () {
|
||||
jQuery('#progressbar').progressbar({value: 0});
|
||||
jQuery('#progressbar').progressbar({ value: 0 });
|
||||
|
||||
// Import button
|
||||
jQuery('#import').click(function() {
|
||||
jQuery('#import').click(function () {
|
||||
MailPoet.MP2Migrator.startImport();
|
||||
});
|
||||
|
||||
|
||||
// Stop import button
|
||||
jQuery('#stop-import').click(function() {
|
||||
jQuery('#stop-import').click(function () {
|
||||
MailPoet.MP2Migrator.stopImport();
|
||||
});
|
||||
|
||||
// Skip import link
|
||||
jQuery('#skip-import').click(function() {
|
||||
jQuery('#skip-import').click(function () {
|
||||
MailPoet.MP2Migrator.skipImport();
|
||||
});
|
||||
|
||||
// Go to welcome page
|
||||
jQuery('#goto-welcome').click(function() {
|
||||
jQuery('#goto-welcome').click(function () {
|
||||
MailPoet.MP2Migrator.gotoWelcomePage();
|
||||
});
|
||||
|
||||
|
||||
// Update the display
|
||||
MailPoet.MP2Migrator.updateDisplay();
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -1,12 +1,8 @@
|
||||
define([
|
||||
'backbone',
|
||||
'backbone.marionette',
|
||||
'backbone.radio',
|
||||
'jquery',
|
||||
'underscore',
|
||||
'handlebars',
|
||||
'handlebars_helpers'
|
||||
], function(Backbone, Marionette, BackboneRadio, jQuery, _, Handlebars) {
|
||||
'backbone',
|
||||
'backbone.marionette',
|
||||
'backbone.radio'
|
||||
], function (Backbone, Marionette, BackboneRadio) { // eslint-disable-line func-names
|
||||
var Radio = BackboneRadio;
|
||||
|
||||
var AppView = Marionette.View.extend({
|
||||
@ -23,12 +19,12 @@ define([
|
||||
var EditorApplication = Marionette.Application.extend({
|
||||
region: '#mailpoet_editor',
|
||||
|
||||
onStart: function() {
|
||||
onStart: function () { // eslint-disable-line func-names
|
||||
this._appView = new AppView();
|
||||
this.showView(this._appView);
|
||||
},
|
||||
|
||||
getChannel: function(channel) {
|
||||
getChannel: function (channel) { // eslint-disable-line func-names
|
||||
if (channel === undefined) {
|
||||
return Radio.channel('global');
|
||||
}
|
||||
@ -40,5 +36,4 @@ define([
|
||||
window.EditorApplication = app;
|
||||
|
||||
return app;
|
||||
|
||||
});
|
||||
|
@ -5,11 +5,11 @@
|
||||
* For more check: http://marionettejs.com/docs/marionette.behaviors.html#behaviorslookup
|
||||
*/
|
||||
define([
|
||||
'backbone.marionette'
|
||||
], function(BackboneMarionette) {
|
||||
'backbone.marionette'
|
||||
], function (BackboneMarionette) { // eslint-disable-line func-names
|
||||
var Marionette = BackboneMarionette;
|
||||
var BehaviorsLookup = {};
|
||||
Marionette.Behaviors.behaviorsLookup = function() {
|
||||
Marionette.Behaviors.behaviorsLookup = function () { // eslint-disable-line func-names
|
||||
return BehaviorsLookup;
|
||||
};
|
||||
|
||||
|
@ -4,23 +4,43 @@
|
||||
* Adds a color picker integration with the view
|
||||
*/
|
||||
define([
|
||||
'backbone.marionette',
|
||||
'newsletter_editor/behaviors/BehaviorsLookup',
|
||||
'mailpoet',
|
||||
'spectrum'
|
||||
], function(Marionette, BehaviorsLookup, MailPoet, Spectrum) {
|
||||
'backbone.marionette',
|
||||
'newsletter_editor/behaviors/BehaviorsLookup',
|
||||
'mailpoet',
|
||||
'spectrum'
|
||||
], function (Marionette, BehaviorsLookup, MailPoet) { // eslint-disable-line func-names
|
||||
var BL = BehaviorsLookup;
|
||||
|
||||
BL.ColorPickerBehavior = Marionette.Behavior.extend({
|
||||
onRender: function() {
|
||||
this.view.$('.mailpoet_color').spectrum({
|
||||
clickoutFiresChange: true,
|
||||
showInput: true,
|
||||
showInitial: true,
|
||||
preferredFormat: 'hex6',
|
||||
allowEmpty: true,
|
||||
chooseText: MailPoet.I18n.t('selectColor'),
|
||||
cancelText: MailPoet.I18n.t('cancelColorSelection')
|
||||
onRender: function () { // eslint-disable-line func-names
|
||||
var that = this;
|
||||
var preferredFormat = 'hex6';
|
||||
this.view.$('.mailpoet_color').each(function () { // eslint-disable-line func-names
|
||||
var $input = that.view.$(this);
|
||||
var updateColorInput = function (color) { // eslint-disable-line func-names
|
||||
if (color && color.getAlpha() > 0) {
|
||||
$input.val(color.toString(preferredFormat));
|
||||
} 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 @@
|
||||
/* eslint-disable func-names */
|
||||
/**
|
||||
* ContainerDropZoneBehavior
|
||||
*
|
||||
@ -6,29 +7,32 @@
|
||||
* accept droppables
|
||||
*/
|
||||
define([
|
||||
'backbone.marionette',
|
||||
'underscore',
|
||||
'jquery',
|
||||
'newsletter_editor/behaviors/BehaviorsLookup',
|
||||
'interact'
|
||||
], function(Marionette, _, jQuery, BL, interact) {
|
||||
'backbone.marionette',
|
||||
'underscore',
|
||||
'jquery',
|
||||
'newsletter_editor/behaviors/BehaviorsLookup',
|
||||
'interact'
|
||||
], function (Marionette, _, jQuery, BL, interact) {
|
||||
var BehaviorsLookup = BL;
|
||||
|
||||
BehaviorsLookup.ContainerDropZoneBehavior = Marionette.Behavior.extend({
|
||||
defaults: {
|
||||
columnLimit: 3
|
||||
},
|
||||
onRender: function() {
|
||||
var dragAndDropDisabled = _.isObject(this.view.options.renderOptions) && this.view.options.renderOptions.disableDragAndDrop === true;
|
||||
onRender: function () {
|
||||
var dragAndDropDisabled = (
|
||||
_.isObject(this.view.options.renderOptions)
|
||||
&& this.view.options.renderOptions.disableDragAndDrop === true
|
||||
);
|
||||
if (!dragAndDropDisabled) {
|
||||
this.addDropZone();
|
||||
this.addDropZone();
|
||||
}
|
||||
},
|
||||
addDropZone: function(_event) {
|
||||
var that = this,
|
||||
view = this.view,
|
||||
domElement = that.$el.get(0),
|
||||
acceptableElementSelector;
|
||||
addDropZone: function () {
|
||||
var that = this;
|
||||
var view = this.view;
|
||||
var domElement = that.$el.get(0);
|
||||
var acceptableElementSelector;
|
||||
|
||||
// TODO: Extract this limitation code to be controlled from containers
|
||||
if (this.view.renderOptions.depth === 0) {
|
||||
@ -46,36 +50,45 @@ define([
|
||||
interact(domElement).dropzone({
|
||||
accept: acceptableElementSelector,
|
||||
overlap: 'pointer', // Mouse pointer denotes location of a droppable
|
||||
ondragenter: function(event) {
|
||||
ondragenter: function () {
|
||||
// 1. Visually mark block as active for dropping
|
||||
view.$el.addClass('mailpoet_drop_active');
|
||||
},
|
||||
ondragleave: function(event) {
|
||||
ondragleave: function () {
|
||||
// 1. Remove visual markings of active dropping container
|
||||
// 2. Remove visual markings of drop position visualization
|
||||
that.cleanup();
|
||||
},
|
||||
ondropmove: function(event) {
|
||||
ondropmove: function (event) {
|
||||
// 1. Compute actual location of the mouse within the container
|
||||
// 2. Check if insertion is regular (between blocks) or special (with container insertion)
|
||||
// 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
|
||||
// 4. If insertion at that position is not visualized, display position visualization there, remove other visualizations from this container
|
||||
// 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
|
||||
var dropPosition = that.getDropPosition(
|
||||
event.dragmove.pageX,
|
||||
event.dragmove.pageY,
|
||||
view.$el,
|
||||
view.model.get('orientation'),
|
||||
view.model.get('blocks').length
|
||||
),
|
||||
element = view.$el,
|
||||
markerWidth = '',
|
||||
markerHeight = '',
|
||||
containerOffset = element.offset(),
|
||||
viewCollection = that.getCollection(),
|
||||
marker, targetModel, targetView, targetElement,
|
||||
topOffset, leftOffset, isLastBlockInsertion,
|
||||
$targetBlock, margin;
|
||||
event.dragmove.pageX,
|
||||
event.dragmove.pageY,
|
||||
view.$el,
|
||||
view.model.get('orientation'),
|
||||
view.model.get('blocks').length
|
||||
);
|
||||
var element = view.$el;
|
||||
var markerWidth = '';
|
||||
var markerHeight = '';
|
||||
var containerOffset = element.offset();
|
||||
var viewCollection = that.getCollection();
|
||||
var marker;
|
||||
var targetModel;
|
||||
var targetView;
|
||||
var targetElement;
|
||||
var topOffset;
|
||||
var leftOffset;
|
||||
var isLastBlockInsertion;
|
||||
var $targetBlock;
|
||||
var margin;
|
||||
|
||||
if (dropPosition === undefined) return;
|
||||
|
||||
@ -92,7 +105,11 @@ define([
|
||||
markerHeight = targetElement.height();
|
||||
} else {
|
||||
isLastBlockInsertion = that.getCollection().length === dropPosition.index;
|
||||
targetModel = isLastBlockInsertion ? viewCollection.at(dropPosition.index - 1) : viewCollection.at(dropPosition.index);
|
||||
if (isLastBlockInsertion) {
|
||||
targetModel = viewCollection.at(dropPosition.index - 1);
|
||||
} else {
|
||||
targetModel = viewCollection.at(dropPosition.index);
|
||||
}
|
||||
|
||||
targetView = that.getChildren().findByModel(targetModel);
|
||||
targetElement = targetView.$el;
|
||||
@ -149,9 +166,15 @@ define([
|
||||
// compensated for to position marker right in the middle of two
|
||||
// blocks
|
||||
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 {
|
||||
$targetBlock = that.getChildren().findByModel(viewCollection.at(dropPosition.index)).$el;
|
||||
$targetBlock = that
|
||||
.getChildren()
|
||||
.findByModel(viewCollection.at(dropPosition.index))
|
||||
.$el;
|
||||
}
|
||||
margin = $targetBlock.outerHeight(true) - $targetBlock.outerHeight();
|
||||
|
||||
@ -162,7 +185,7 @@ define([
|
||||
|
||||
element.append(marker);
|
||||
},
|
||||
ondrop: function(event) {
|
||||
ondrop: function (event) {
|
||||
// 1. Compute actual location of the mouse
|
||||
// 2. Check if insertion is regular (between blocks) or special (with container insertion)
|
||||
// 3a. If insertion is regular
|
||||
@ -172,20 +195,25 @@ define([
|
||||
// 3b1. compute position (which side) and which cell the insertion belongs to
|
||||
// 3b2. remove element at that position from the collection
|
||||
// 3b3. create a new collection, insert the removed element to it
|
||||
// 3b4. insert the droppable model at the start or end of the new collection, depending on 3b1. position
|
||||
// 3b4. insert the droppable model at the start or end of the new collection,
|
||||
// depending on 3b1. position
|
||||
// 3b5. insert the new collection into the old collection to cell from 3b1.
|
||||
// 4. Perform cleanup actions
|
||||
|
||||
var dropPosition = that.getDropPosition(
|
||||
event.dragEvent.pageX,
|
||||
event.dragEvent.pageY,
|
||||
view.$el,
|
||||
view.model.get('orientation'),
|
||||
view.model.get('blocks').length
|
||||
),
|
||||
droppableModel = event.draggable.getDropModel(),
|
||||
viewCollection = that.getCollection(),
|
||||
droppedView, droppedModel, index, tempCollection, tempCollection2;
|
||||
event.dragEvent.pageX,
|
||||
event.dragEvent.pageY,
|
||||
view.$el,
|
||||
view.model.get('orientation'),
|
||||
view.model.get('blocks').length
|
||||
);
|
||||
var droppableModel = event.draggable.getDropModel();
|
||||
var viewCollection = that.getCollection();
|
||||
var droppedView;
|
||||
var index;
|
||||
var tempCollection;
|
||||
var tempCollection2;
|
||||
var tempModel;
|
||||
|
||||
if (dropPosition === undefined) return;
|
||||
|
||||
@ -195,42 +223,43 @@ define([
|
||||
|
||||
if (view.model.get('orientation') === 'horizontal' && droppableModel.get('type') !== 'container') {
|
||||
// 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);
|
||||
viewCollection.add(tempCollection, {at: index});
|
||||
viewCollection.add(tempCollection, { at: index });
|
||||
} else {
|
||||
viewCollection.add(droppableModel, {at: index});
|
||||
viewCollection.add(droppableModel, { at: index });
|
||||
}
|
||||
|
||||
droppedView = that.getChildren().findByModel(droppableModel);
|
||||
} else {
|
||||
// Special insertion by replacing target block with collection
|
||||
// and inserting dropModel into that
|
||||
var tempModel = viewCollection.at(dropPosition.index);
|
||||
tempModel = viewCollection.at(dropPosition.index);
|
||||
|
||||
tempCollection = new (EditorApplication.getBlockTypeModel('container'))({
|
||||
orientation: (view.model.get('orientation') === 'vertical') ? 'horizontal' : 'vertical'
|
||||
tempCollection = new (window.EditorApplication.getBlockTypeModel('container'))({
|
||||
orientation: (view.model.get('orientation') === 'vertical') ? 'horizontal' : 'vertical'
|
||||
});
|
||||
|
||||
viewCollection.remove(tempModel);
|
||||
|
||||
if (tempCollection.get('orientation') === 'horizontal') {
|
||||
if (dropPosition.position === 'before') {
|
||||
tempCollection2 = new (EditorApplication.getBlockTypeModel('container'))({
|
||||
tempCollection2 = new (window.EditorApplication.getBlockTypeModel('container'))({
|
||||
orientation: 'vertical'
|
||||
});
|
||||
tempCollection2.get('blocks').add(droppableModel);
|
||||
tempCollection.get('blocks').add(tempCollection2);
|
||||
}
|
||||
tempCollection2 = new (EditorApplication.getBlockTypeModel('container'))({
|
||||
tempCollection2 = new (window.EditorApplication.getBlockTypeModel('container'))({
|
||||
orientation: 'vertical'
|
||||
});
|
||||
tempCollection2.get('blocks').add(tempModel);
|
||||
tempCollection.get('blocks').add(tempCollection2);
|
||||
if (dropPosition.position === 'after') {
|
||||
tempCollection2 = new (EditorApplication.getBlockTypeModel('container'))({
|
||||
tempCollection2 = new (window.EditorApplication.getBlockTypeModel('container'))({
|
||||
orientation: 'vertical'
|
||||
});
|
||||
tempCollection2.get('blocks').add(droppableModel);
|
||||
@ -245,10 +274,14 @@ define([
|
||||
tempCollection.get('blocks').add(droppableModel);
|
||||
}
|
||||
}
|
||||
viewCollection.add(tempCollection, {at: dropPosition.index});
|
||||
viewCollection.add(tempCollection, { at: dropPosition.index });
|
||||
|
||||
// Call post add actions
|
||||
droppedView = that.getChildren().findByModel(tempCollection).children.findByModel(droppableModel);
|
||||
droppedView = that
|
||||
.getChildren()
|
||||
.findByModel(tempCollection)
|
||||
.children
|
||||
.findByModel(droppableModel);
|
||||
}
|
||||
|
||||
// Call post add actions
|
||||
@ -262,36 +295,40 @@ define([
|
||||
}
|
||||
});
|
||||
},
|
||||
cleanup: function() {
|
||||
cleanup: function () {
|
||||
// 1. Remove visual markings of active dropping container
|
||||
this.view.$el.removeClass('mailpoet_drop_active');
|
||||
|
||||
// 2. Remove visual markings of drop position visualization
|
||||
this.view.$('.mailpoet_drop_marker').remove();
|
||||
},
|
||||
getDropPosition: function(eventX, eventY, is_unsafe) {
|
||||
var SPECIAL_AREA_INSERTION_WIDTH = 0.00, // Disable special insertion. Default: 0.3
|
||||
getDropPosition: function (eventX, eventY, isUnsafe) {
|
||||
var SPECIAL_AREA_INSERTION_WIDTH = 0.00; // Disable special insertion. Default: 0.3
|
||||
|
||||
element = this.view.$el,
|
||||
orientation = this.view.model.get('orientation'),
|
||||
var element = this.view.$el;
|
||||
var orientation = this.view.model.get('orientation');
|
||||
|
||||
elementOffset = element.offset(),
|
||||
elementPageX = elementOffset.left,
|
||||
elementPageY = elementOffset.top,
|
||||
elementWidth = element.outerWidth(true),
|
||||
elementHeight = element.outerHeight(true),
|
||||
var elementOffset = element.offset();
|
||||
var elementPageX = elementOffset.left;
|
||||
var elementPageY = elementOffset.top;
|
||||
var elementWidth = element.outerWidth(true);
|
||||
var elementHeight = element.outerHeight(true);
|
||||
|
||||
relativeX = eventX - elementPageX,
|
||||
relativeY = eventY - elementPageY,
|
||||
var relativeX = eventX - elementPageX;
|
||||
var relativeY = eventY - elementPageY;
|
||||
|
||||
relativeOffset, elementLength,
|
||||
var relativeOffset;
|
||||
var elementLength;
|
||||
|
||||
canAcceptNormalInsertion = this._canAcceptNormalInsertion(),
|
||||
canAcceptSpecialInsertion = this._canAcceptSpecialInsertion(),
|
||||
var canAcceptNormalInsertion = this._canAcceptNormalInsertion();
|
||||
var canAcceptSpecialInsertion = this._canAcceptSpecialInsertion();
|
||||
|
||||
insertionType, index, position, indexAndPosition;
|
||||
var insertionType;
|
||||
var index;
|
||||
var position;
|
||||
var indexAndPosition;
|
||||
|
||||
unsafe = !!is_unsafe;
|
||||
var unsafe = !!isUnsafe;
|
||||
|
||||
if (this.getCollection().length === 0) {
|
||||
return {
|
||||
@ -315,11 +352,17 @@ define([
|
||||
SPECIAL_AREA_INSERTION_WIDTH = 0.5;
|
||||
}
|
||||
|
||||
if (relativeOffset <= elementLength * SPECIAL_AREA_INSERTION_WIDTH && (unsafe || canAcceptSpecialInsertion)) {
|
||||
if (
|
||||
relativeOffset <= elementLength * SPECIAL_AREA_INSERTION_WIDTH
|
||||
&& (unsafe || canAcceptSpecialInsertion)
|
||||
) {
|
||||
insertionType = 'special';
|
||||
position = 'before';
|
||||
index = this._computeSpecialIndex(eventX, eventY);
|
||||
} else if (relativeOffset > elementLength * (1 - SPECIAL_AREA_INSERTION_WIDTH) && (unsafe || canAcceptSpecialInsertion)) {
|
||||
} else if (
|
||||
relativeOffset > elementLength * (1 - SPECIAL_AREA_INSERTION_WIDTH)
|
||||
&& (unsafe || canAcceptSpecialInsertion)
|
||||
) {
|
||||
insertionType = 'special';
|
||||
position = 'after';
|
||||
index = this._computeSpecialIndex(eventX, eventY);
|
||||
@ -342,7 +385,7 @@ define([
|
||||
|
||||
if (orientation === 'horizontal' && insertionType === 'special') {
|
||||
// Disable special insertion for horizontal containers
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
@ -351,18 +394,20 @@ define([
|
||||
position: position // 'inside'|'before'|'after'
|
||||
};
|
||||
},
|
||||
_computeNormalIndex: function(eventX, eventY) {
|
||||
_computeNormalIndex: function (eventX, eventY) {
|
||||
// Normal insertion inserts dropModel before target element if
|
||||
// event happens on the first half of the element and after the
|
||||
// target element if event happens on the second half of the element.
|
||||
// Halves depend on orientation.
|
||||
|
||||
var index = this._computeCellIndex(eventX, eventY),
|
||||
// TODO: Handle case when there are no children, container is empty
|
||||
targetView = this.getChildren().findByModel(this.getCollection().at(index)),
|
||||
orientation = this.view.model.get('orientation'),
|
||||
element = targetView.$el,
|
||||
eventOffset, closeOffset, elementDimension;
|
||||
var index = this._computeCellIndex(eventX, eventY);
|
||||
// TODO: Handle case when there are no children, container is empty
|
||||
var targetView = this.getChildren().findByModel(this.getCollection().at(index));
|
||||
var orientation = this.view.model.get('orientation');
|
||||
var element = targetView.$el;
|
||||
var eventOffset;
|
||||
var closeOffset;
|
||||
var elementDimension;
|
||||
|
||||
if (orientation === 'vertical') {
|
||||
eventOffset = eventY;
|
||||
@ -374,66 +419,66 @@ define([
|
||||
elementDimension = element.outerWidth(true);
|
||||
}
|
||||
|
||||
if (eventOffset <= closeOffset + elementDimension / 2) {
|
||||
if (eventOffset <= closeOffset + (elementDimension / 2)) {
|
||||
// First half of the element
|
||||
return {
|
||||
index: index,
|
||||
position: 'before'
|
||||
};
|
||||
} else {
|
||||
// Second half of the element
|
||||
return {
|
||||
index: index,
|
||||
position: 'after'
|
||||
index: index,
|
||||
position: 'before'
|
||||
};
|
||||
}
|
||||
// Second half of the element
|
||||
return {
|
||||
index: index,
|
||||
position: 'after'
|
||||
};
|
||||
},
|
||||
_computeSpecialIndex: function(eventX, eventY) {
|
||||
_computeSpecialIndex: function (eventX, eventY) {
|
||||
return this._computeCellIndex(eventX, eventY);
|
||||
},
|
||||
_computeCellIndex: function(eventX, eventY) {
|
||||
var orientation = this.view.model.get('orientation'),
|
||||
eventOffset = (orientation === 'vertical') ? eventY : eventX,
|
||||
resultView = this.getChildren().find(function(view) {
|
||||
var element = view.$el,
|
||||
closeOffset, farOffset;
|
||||
_computeCellIndex: function (eventX, eventY) {
|
||||
var orientation = this.view.model.get('orientation');
|
||||
var eventOffset = (orientation === 'vertical') ? eventY : eventX;
|
||||
var resultView = this.getChildren().find(function (view) {
|
||||
var element = view.$el;
|
||||
var closeOffset;
|
||||
var farOffset;
|
||||
|
||||
if (orientation === 'vertical') {
|
||||
closeOffset = element.offset().top;
|
||||
farOffset = element.outerHeight(true);
|
||||
} else {
|
||||
closeOffset = element.offset().left;
|
||||
farOffset = element.outerWidth(true);
|
||||
}
|
||||
farOffset += closeOffset;
|
||||
if (orientation === 'vertical') {
|
||||
closeOffset = element.offset().top;
|
||||
farOffset = element.outerHeight(true);
|
||||
} else {
|
||||
closeOffset = element.offset().left;
|
||||
farOffset = element.outerWidth(true);
|
||||
}
|
||||
farOffset += closeOffset;
|
||||
|
||||
return closeOffset <= eventOffset && eventOffset <= farOffset;
|
||||
});
|
||||
return closeOffset <= eventOffset && eventOffset <= farOffset;
|
||||
});
|
||||
|
||||
var index = (typeof resultView === 'object') ? resultView._index : 0;
|
||||
|
||||
return index;
|
||||
},
|
||||
_canAcceptNormalInsertion: function() {
|
||||
var orientation = this.view.model.get('orientation'),
|
||||
depth = this.view.renderOptions.depth,
|
||||
childCount = this.getChildren().length;
|
||||
_canAcceptNormalInsertion: function () {
|
||||
var orientation = this.view.model.get('orientation');
|
||||
var depth = this.view.renderOptions.depth;
|
||||
var childCount = this.getChildren().length;
|
||||
// Note that depth is zero indexed. Root container has depth=0
|
||||
return orientation === 'vertical' || (orientation === 'horizontal' && depth === 1 && childCount < this.options.columnLimit);
|
||||
},
|
||||
_canAcceptSpecialInsertion: function() {
|
||||
var orientation = this.view.model.get('orientation'),
|
||||
depth = this.view.renderOptions.depth,
|
||||
childCount = this.getChildren().length;
|
||||
_canAcceptSpecialInsertion: function () {
|
||||
var orientation = this.view.model.get('orientation');
|
||||
var depth = this.view.renderOptions.depth;
|
||||
var childCount = this.getChildren().length;
|
||||
return depth === 0 || (depth === 1 && orientation === 'horizontal' && childCount <= this.options.columnLimit);
|
||||
},
|
||||
getCollectionView: function() {
|
||||
getCollectionView: function () {
|
||||
return this.view.getChildView('blocks');
|
||||
},
|
||||
getChildren: function() {
|
||||
getChildren: function () {
|
||||
return this.getCollectionView().children;
|
||||
},
|
||||
getCollection: function() {
|
||||
getCollection: function () {
|
||||
return this.getCollectionView().collection;
|
||||
}
|
||||
});
|
||||
|
@ -5,12 +5,12 @@
|
||||
* Part of the drag&drop behavior.
|
||||
*/
|
||||
define([
|
||||
'backbone.marionette',
|
||||
'underscore',
|
||||
'jquery',
|
||||
'newsletter_editor/behaviors/BehaviorsLookup',
|
||||
'interact'
|
||||
], function(Marionette, _, jQuery, BehaviorsLookup, interact) {
|
||||
'backbone.marionette',
|
||||
'underscore',
|
||||
'jquery',
|
||||
'newsletter_editor/behaviors/BehaviorsLookup',
|
||||
'interact'
|
||||
], function DraggableBehavior(Marionette, _, jQuery, BehaviorsLookup, interact) {
|
||||
var BL = BehaviorsLookup;
|
||||
|
||||
BL.DraggableBehavior = Marionette.Behavior.extend({
|
||||
@ -24,22 +24,22 @@ define([
|
||||
*
|
||||
* @return Backbone.Model A model that will be passed to the receiver
|
||||
*/
|
||||
getDropModel: function() {
|
||||
throw "Missing 'drop' function for DraggableBehavior";
|
||||
getDropModel: function getDropModel() {
|
||||
throw new Error("Missing 'drop' function for DraggableBehavior");
|
||||
},
|
||||
|
||||
onDrop: function(model, view) {},
|
||||
testAttachToInstance: function(model, view) { return true; }
|
||||
onDrop: function onDrop() {},
|
||||
testAttachToInstance: function testAttachToInstance() { return true; }
|
||||
},
|
||||
onRender: function() {
|
||||
var that = this,
|
||||
interactable;
|
||||
onRender: function onRender() {
|
||||
var that = this;
|
||||
var interactable;
|
||||
|
||||
// Give instances more control over whether Draggable should be applied
|
||||
if (!this.options.testAttachToInstance(this.view.model, this.view)) return;
|
||||
|
||||
interactable = interact(this.$el.get(0), {
|
||||
ignoreFrom: this.options.ignoreSelector
|
||||
ignoreFrom: this.options.ignoreSelector
|
||||
}).draggable({
|
||||
// allow dragging of multple elements at the same time
|
||||
max: Infinity,
|
||||
@ -47,18 +47,23 @@ define([
|
||||
// Scroll when dragging near edges of a window
|
||||
autoScroll: true,
|
||||
|
||||
onstart: function(startEvent) {
|
||||
onstart: function onstart(startEvent) {
|
||||
var event = startEvent;
|
||||
var centerXOffset;
|
||||
var centerYOffset;
|
||||
var tempClone;
|
||||
var clone;
|
||||
var $clone;
|
||||
|
||||
if (that.options.cloneOriginal === true) {
|
||||
// Use substitution instead of a clone
|
||||
var tempClone = (_.isFunction(that.options.onDragSubstituteBy)) ? that.options.onDragSubstituteBy(that) : undefined,
|
||||
// Or use a clone
|
||||
clone = tempClone ? tempClone : event.target.cloneNode(true),
|
||||
|
||||
$original = jQuery(event.target),
|
||||
$clone = jQuery(clone),
|
||||
centerXOffset, centerYOffset, parentOffset;
|
||||
if (_.isFunction(that.options.onDragSubstituteBy)) {
|
||||
tempClone = that.options.onDragSubstituteBy(that);
|
||||
}
|
||||
// Or use a clone
|
||||
clone = tempClone || event.target.cloneNode(true);
|
||||
jQuery(event.target);
|
||||
$clone = jQuery(clone);
|
||||
|
||||
$clone.addClass('mailpoet_droppable_active');
|
||||
$clone.css('position', 'absolute');
|
||||
@ -71,7 +76,7 @@ define([
|
||||
// Accurate dimensions can only be taken after insertion to document
|
||||
centerXOffset = $clone.width() / 2;
|
||||
centerYOffset = $clone.height() / 2;
|
||||
$clone.css('top', event.pageY - centerYOffset);
|
||||
$clone.css('top', event.pageY - centerYOffset);
|
||||
$clone.css('left', event.pageX - centerXOffset);
|
||||
|
||||
event.interaction.element = clone;
|
||||
@ -81,14 +86,13 @@ define([
|
||||
that.view.$el.addClass('mailpoet_hidden');
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
// call this function on every dragmove event
|
||||
onmove: function (event) {
|
||||
var target = event.target,
|
||||
// keep the dragged position in the data-x/data-y attributes
|
||||
x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx,
|
||||
y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
|
||||
onmove: function onmove(event) {
|
||||
var target = event.target;
|
||||
// keep the dragged position in the data-x/data-y attributes
|
||||
var x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
|
||||
var y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
|
||||
|
||||
// translate the element
|
||||
target.style.transform = 'translate(' + x + 'px, ' + y + 'px)';
|
||||
@ -98,7 +102,7 @@ define([
|
||||
target.setAttribute('data-x', x);
|
||||
target.setAttribute('data-y', y);
|
||||
},
|
||||
onend: function (event) {
|
||||
onend: function onend(event) {
|
||||
var target = event.target;
|
||||
target.style.transform = '';
|
||||
target.style.webkitTransform = target.style.transform;
|
||||
@ -117,7 +121,7 @@ define([
|
||||
})
|
||||
.preventDefault('auto')
|
||||
.styleCursor(false)
|
||||
.actionChecker(function (pointer, event, action) {
|
||||
.actionChecker(function actionChecker(pointer, event, action) {
|
||||
// Disable dragging with right click
|
||||
if (event.button !== 0) {
|
||||
return null;
|
||||
@ -131,7 +135,7 @@ define([
|
||||
} else {
|
||||
interactable.getDropModel = this.view.getDropFunc();
|
||||
}
|
||||
interactable.onDrop = function(opts) {
|
||||
interactable.onDrop = function onDrop(opts) {
|
||||
var options = opts;
|
||||
if (_.isObject(options)) {
|
||||
// Inject Draggable behavior if possible
|
||||
|
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Highlight Container Behavior
|
||||
*
|
||||
* Highlights a container block when hovering over its tools
|
||||
*/
|
||||
define([
|
||||
'backbone.marionette',
|
||||
'newsletter_editor/behaviors/BehaviorsLookup'
|
||||
], function (Marionette, BehaviorsLookup) { // eslint-disable-line func-names
|
||||
var BL = BehaviorsLookup;
|
||||
|
||||
BL.HighlightContainerBehavior = Marionette.Behavior.extend({
|
||||
events: {
|
||||
'mouseenter @ui.tools': 'enableHighlight',
|
||||
'mouseleave @ui.tools': 'disableHighlight'
|
||||
},
|
||||
enableHighlight: function () { // eslint-disable-line func-names
|
||||
this.$el.addClass('mailpoet_highlight');
|
||||
},
|
||||
disableHighlight: function () { // eslint-disable-line func-names
|
||||
if (!this.view._isBeingEdited) {
|
||||
this.$el.removeClass('mailpoet_highlight');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
@ -4,9 +4,9 @@
|
||||
* Highlights a block that is being edited
|
||||
*/
|
||||
define([
|
||||
'backbone.marionette',
|
||||
'newsletter_editor/behaviors/BehaviorsLookup'
|
||||
], function(Marionette, BehaviorsLookup) {
|
||||
'backbone.marionette',
|
||||
'newsletter_editor/behaviors/BehaviorsLookup'
|
||||
], function (Marionette, BehaviorsLookup) { // eslint-disable-line func-names
|
||||
var BL = BehaviorsLookup;
|
||||
|
||||
BL.HighlightEditingBehavior = Marionette.Behavior.extend({
|
||||
@ -14,10 +14,12 @@ define([
|
||||
startEditing: 'enableHighlight',
|
||||
stopEditing: 'disableHighlight'
|
||||
},
|
||||
enableHighlight: function() {
|
||||
enableHighlight: function () { // eslint-disable-line func-names
|
||||
this.view._isBeingEdited = true;
|
||||
this.$el.addClass('mailpoet_highlight');
|
||||
},
|
||||
disableHighlight: function() {
|
||||
disableHighlight: function () { // eslint-disable-line func-names
|
||||
this.view._isBeingEdited = false;
|
||||
this.$el.removeClass('mailpoet_highlight');
|
||||
}
|
||||
});
|
||||
|
@ -4,63 +4,74 @@
|
||||
* Allows resizing elements within a block
|
||||
*/
|
||||
define([
|
||||
'backbone.marionette',
|
||||
'newsletter_editor/behaviors/BehaviorsLookup',
|
||||
'interact'
|
||||
], function(Marionette, BehaviorsLookup, interact) {
|
||||
'backbone.marionette',
|
||||
'newsletter_editor/behaviors/BehaviorsLookup',
|
||||
'interact'
|
||||
], function (Marionette, BehaviorsLookup, interact) { // eslint-disable-line func-names
|
||||
var BL = BehaviorsLookup;
|
||||
|
||||
BL.ResizableBehavior = Marionette.Behavior.extend({
|
||||
defaults: {
|
||||
elementSelector: null,
|
||||
resizeHandleSelector: true, // true will use edges of the element itself
|
||||
transformationFunction: function(y) { return y; },
|
||||
// for blocks that use the default onResize function
|
||||
transformationFunction: function transformationFunction(y) { return y; },
|
||||
minLength: 0,
|
||||
modelField: 'styles.block.height'
|
||||
maxLength: Infinity,
|
||||
modelField: 'styles.block.height',
|
||||
onResize: function (event) { // eslint-disable-line func-names
|
||||
var currentLength = parseFloat(this.view.model.get(this.options.modelField));
|
||||
var newLength = currentLength + this.options.transformationFunction(event.dy);
|
||||
newLength = Math.min(this.options.maxLength, Math.max(this.options.minLength, newLength));
|
||||
this.view.model.set(this.options.modelField, newLength + 'px');
|
||||
}
|
||||
},
|
||||
events: {
|
||||
mouseenter: 'showResizeHandle',
|
||||
mouseleave: 'hideResizeHandle'
|
||||
},
|
||||
onRender: function() {
|
||||
onRender: function () { // eslint-disable-line func-names
|
||||
this.attachResize();
|
||||
|
||||
if (this.isBeingResized !== true) {
|
||||
this.hideResizeHandle();
|
||||
}
|
||||
},
|
||||
attachResize: function() {
|
||||
var domElement = (this.options.elementSelector === null) ? this.view.$el.get(0) : this.view.$(this.options.elementSelector).get(0),
|
||||
that = this;
|
||||
attachResize: function () { // eslint-disable-line func-names
|
||||
var domElement;
|
||||
var that = this;
|
||||
if (this.options.elementSelector === null) {
|
||||
domElement = this.view.$el.get(0);
|
||||
} else {
|
||||
domElement = this.view.$(this.options.elementSelector).get(0);
|
||||
}
|
||||
interact(domElement).resizable({
|
||||
//axis: 'y',
|
||||
// axis: 'y',
|
||||
edges: {
|
||||
top: false,
|
||||
left: false,
|
||||
right: false,
|
||||
bottom: (typeof this.options.resizeHandleSelector === 'string') ? this.view.$(this.options.resizeHandleSelector).get(0) : this.options.resizeHandleSelector
|
||||
}
|
||||
}).on('resizestart', function(event) {
|
||||
})
|
||||
.on('resizestart', function () { // eslint-disable-line func-names
|
||||
that.isBeingResized = true;
|
||||
that.$el.addClass('mailpoet_resize_active');
|
||||
}).on('resizemove', function(event) {
|
||||
var currentLength = parseFloat(that.view.model.get(that.options.modelField)),
|
||||
newLength = currentLength + that.options.transformationFunction(event.dy);
|
||||
|
||||
if (newLength < that.options.minLength) newLength = that.options.minLength;
|
||||
|
||||
that.view.model.set(that.options.modelField, newLength + 'px');
|
||||
}).on('resizeend', function(event) {
|
||||
}).on('resizemove', function (event) { // eslint-disable-line func-names
|
||||
var onResize = that.options.onResize.bind(that);
|
||||
return onResize(event);
|
||||
})
|
||||
.on('resizeend', function () { // eslint-disable-line func-names
|
||||
that.isBeingResized = null;
|
||||
that.$el.removeClass('mailpoet_resize_active');
|
||||
});
|
||||
},
|
||||
showResizeHandle: function() {
|
||||
showResizeHandle: function () { // eslint-disable-line func-names
|
||||
if (typeof this.options.resizeHandleSelector === 'string') {
|
||||
this.view.$(this.options.resizeHandleSelector).removeClass('mailpoet_hidden');
|
||||
}
|
||||
},
|
||||
hideResizeHandle: function() {
|
||||
hideResizeHandle: function () { // eslint-disable-line func-names
|
||||
if (typeof this.options.resizeHandleSelector === 'string') {
|
||||
this.view.$(this.options.resizeHandleSelector).addClass('mailpoet_hidden');
|
||||
}
|
||||
|
@ -4,10 +4,10 @@
|
||||
* Opens up settings of a BlockView if contents are clicked upon
|
||||
*/
|
||||
define([
|
||||
'backbone.marionette',
|
||||
'jquery',
|
||||
'newsletter_editor/behaviors/BehaviorsLookup'
|
||||
], function(Marionette, jQuery, BehaviorsLookup) {
|
||||
'backbone.marionette',
|
||||
'jquery',
|
||||
'newsletter_editor/behaviors/BehaviorsLookup'
|
||||
], function (Marionette, jQuery, BehaviorsLookup) { // eslint-disable-line func-names
|
||||
var BL = BehaviorsLookup;
|
||||
|
||||
BL.ShowSettingsBehavior = Marionette.Behavior.extend({
|
||||
@ -17,12 +17,12 @@ define([
|
||||
events: {
|
||||
'click .mailpoet_content': 'showSettings'
|
||||
},
|
||||
showSettings: function(event) {
|
||||
if(!this.isIgnoredElement(event.target)) {
|
||||
showSettings: function (event) { // eslint-disable-line func-names
|
||||
if (!this.isIgnoredElement(event.target)) {
|
||||
this.view.triggerMethod('showSettings');
|
||||
}
|
||||
},
|
||||
isIgnoredElement: function(element) {
|
||||
isIgnoredElement: function (element) { // eslint-disable-line func-names
|
||||
return this.options.ignoreFrom
|
||||
&& this.options.ignoreFrom.length > 0
|
||||
&& jQuery(element).is(this.options.ignoreFrom);
|
||||
|
@ -4,29 +4,29 @@
|
||||
* Allows sorting elements within a collection
|
||||
*/
|
||||
define([
|
||||
'backbone.marionette',
|
||||
'underscore',
|
||||
'newsletter_editor/behaviors/BehaviorsLookup'
|
||||
], function(Marionette, _, BehaviorsLookup) {
|
||||
'backbone.marionette',
|
||||
'underscore',
|
||||
'newsletter_editor/behaviors/BehaviorsLookup'
|
||||
], function (Marionette, _, BehaviorsLookup) { // eslint-disable-line func-names
|
||||
var BL = BehaviorsLookup;
|
||||
|
||||
BL.SortableBehavior = Marionette.Behavior.extend({
|
||||
onRender: function() {
|
||||
onRender: function () { // eslint-disable-line func-names
|
||||
var collection = this.view.collection;
|
||||
|
||||
if (_.isFunction(this.$el.sortable)) {
|
||||
this.$el.sortable({
|
||||
cursor: 'move',
|
||||
start: function(event, ui) {
|
||||
start: function (event, ui) { // eslint-disable-line func-names
|
||||
ui.item.data('previousIndex', ui.item.index());
|
||||
},
|
||||
end: function(event, ui) {
|
||||
end: function (event, ui) { // eslint-disable-line func-names
|
||||
ui.item.removeData('previousIndex');
|
||||
},
|
||||
update: function(event, ui) {
|
||||
var previousIndex = ui.item.data('previousIndex'),
|
||||
newIndex = ui.item.index(),
|
||||
model = collection.at(previousIndex);
|
||||
update: function (event, ui) { // eslint-disable-line func-names
|
||||
var previousIndex = ui.item.data('previousIndex');
|
||||
var newIndex = ui.item.index();
|
||||
var model = collection.at(previousIndex);
|
||||
|
||||
// Replicate DOM changes. Move target model to a new position
|
||||
// within the collection
|
||||
|
@ -4,10 +4,10 @@
|
||||
* Adds TinyMCE text editing capabilities to a view
|
||||
*/
|
||||
define([
|
||||
'backbone.marionette',
|
||||
'underscore',
|
||||
'newsletter_editor/behaviors/BehaviorsLookup'
|
||||
], function(Marionette, _, BehaviorsLookup) {
|
||||
'backbone.marionette',
|
||||
'underscore',
|
||||
'newsletter_editor/behaviors/BehaviorsLookup'
|
||||
], function textEditorBehavior(Marionette, _, BehaviorsLookup) {
|
||||
var BL = BehaviorsLookup;
|
||||
|
||||
BL.TextEditorBehavior = Marionette.Behavior.extend({
|
||||
@ -19,9 +19,9 @@ define([
|
||||
invalidElements: 'script',
|
||||
blockFormats: 'Paragraph=p',
|
||||
plugins: 'link textcolor colorpicker mailpoet_shortcodes',
|
||||
configurationFilter: function(originalConfig) { return originalConfig; }
|
||||
configurationFilter: function configurationFilter(originalConfig) { return originalConfig; }
|
||||
},
|
||||
onDomRefresh: function() {
|
||||
onDomRefresh: function onDomRefresh() {
|
||||
var that = this;
|
||||
if (this.view.disableTextEditor === true) {
|
||||
return;
|
||||
@ -34,13 +34,15 @@ define([
|
||||
toolbar1: this.options.toolbar1,
|
||||
toolbar2: this.options.toolbar2,
|
||||
|
||||
browser_spellcheck: true,
|
||||
|
||||
valid_elements: this.options.validElements,
|
||||
invalid_elements: this.options.invalidElements,
|
||||
block_formats: this.options.blockFormats,
|
||||
relative_urls: false,
|
||||
remove_script_host: false,
|
||||
convert_urls: true,
|
||||
urlconverter_callback: function(url, node, on_save, name) {
|
||||
urlconverter_callback: function urlconverterCallback(url) {
|
||||
if (url.match(/\[.+\]/g)) {
|
||||
// Do not convert URLs with shortcodes
|
||||
return url;
|
||||
@ -54,27 +56,31 @@ define([
|
||||
|
||||
plugins: this.options.plugins,
|
||||
|
||||
setup: function(editor) {
|
||||
editor.on('change', function(e) {
|
||||
setup: function setup(editor) {
|
||||
editor.on('change', function onChange() {
|
||||
that.view.triggerMethod('text:editor:change', editor.getContent());
|
||||
});
|
||||
|
||||
editor.on('click', function(e) {
|
||||
editor.on('click', function onClick(e) {
|
||||
editor.focus();
|
||||
if (that._isActivationClick) {
|
||||
editor.selection.setRng(
|
||||
tinymce.dom.RangeUtils.getCaretRangeFromPoint(e.clientX, e.clientY, editor.getDoc())
|
||||
window.tinymce.dom.RangeUtils.getCaretRangeFromPoint(
|
||||
e.clientX,
|
||||
e.clientY,
|
||||
editor.getDoc()
|
||||
)
|
||||
);
|
||||
that._isActivationClick = false;
|
||||
}
|
||||
});
|
||||
|
||||
editor.on('focus', function(e) {
|
||||
editor.on('focus', function onFocus() {
|
||||
that.view.triggerMethod('text:editor:focus');
|
||||
that._isActivationClick = true;
|
||||
that._isActivationClick = true;
|
||||
});
|
||||
|
||||
editor.on('blur', function(e) {
|
||||
editor.on('blur', function onBlur() {
|
||||
that.view.triggerMethod('text:editor:blur');
|
||||
});
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* eslint-disable func-names */
|
||||
/**
|
||||
* Automated latest content block.
|
||||
* Only query parameters can be modified by the user. Posts pulled by this
|
||||
@ -7,34 +8,33 @@
|
||||
* block settings view.
|
||||
*/
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base',
|
||||
'newsletter_editor/blocks/button',
|
||||
'newsletter_editor/blocks/divider',
|
||||
'newsletter_editor/components/communication',
|
||||
'mailpoet',
|
||||
'backbone.supermodel',
|
||||
'underscore',
|
||||
'jquery'
|
||||
], function(
|
||||
App,
|
||||
BaseBlock,
|
||||
ButtonBlock,
|
||||
DividerBlock,
|
||||
CommunicationComponent,
|
||||
MailPoet,
|
||||
SuperModel,
|
||||
_,
|
||||
jQuery
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base',
|
||||
'newsletter_editor/blocks/button',
|
||||
'newsletter_editor/blocks/divider',
|
||||
'newsletter_editor/components/communication',
|
||||
'mailpoet',
|
||||
'backbone.supermodel',
|
||||
'underscore',
|
||||
'jquery'
|
||||
], function (
|
||||
App,
|
||||
BaseBlock,
|
||||
ButtonBlock,
|
||||
DividerBlock,
|
||||
CommunicationComponent,
|
||||
MailPoet,
|
||||
SuperModel,
|
||||
_,
|
||||
jQuery
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock;
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
|
||||
Module.ALCSupervisor = SuperModel.extend({
|
||||
initialize: function() {
|
||||
initialize: function () {
|
||||
var DELAY_REFRESH_FOR_MS = 500;
|
||||
this.listenTo(
|
||||
App.getChannel(),
|
||||
@ -42,13 +42,14 @@ define([
|
||||
_.debounce(this.refresh, DELAY_REFRESH_FOR_MS)
|
||||
);
|
||||
},
|
||||
refresh: function() {
|
||||
var models = App.findModels(function(model) {
|
||||
refresh: function () {
|
||||
var blocks;
|
||||
var models = App.findModels(function (model) {
|
||||
return model.get('type') === 'automatedLatestContent';
|
||||
}) || [];
|
||||
|
||||
if (models.length === 0) return;
|
||||
var blocks = _.map(models, function(model) {
|
||||
blocks = _.map(models, function (model) {
|
||||
return model.toJSON();
|
||||
});
|
||||
|
||||
@ -56,12 +57,12 @@ define([
|
||||
blocks: blocks
|
||||
}).then(_.partial(this.refreshBlocks, models));
|
||||
},
|
||||
refreshBlocks: function(models, renderedBlocks) {
|
||||
refreshBlocks: function (models, renderedBlocks) {
|
||||
_.each(
|
||||
_.zip(models, renderedBlocks),
|
||||
function(args) {
|
||||
var model = args[0],
|
||||
contents = args[1];
|
||||
function (args) {
|
||||
var model = args[0];
|
||||
var contents = args[1];
|
||||
model.trigger('refreshPosts', contents);
|
||||
}
|
||||
);
|
||||
@ -70,7 +71,7 @@ define([
|
||||
|
||||
Module.AutomatedLatestContentBlockModel = base.BlockModel.extend({
|
||||
stale: ['_container'],
|
||||
defaults: function() {
|
||||
defaults: function () {
|
||||
return this._getDefaults({
|
||||
type: 'automatedLatestContent',
|
||||
amount: '5',
|
||||
@ -83,7 +84,6 @@ define([
|
||||
titleIsLink: false, // false|true
|
||||
imageFullWidth: false, // true|false
|
||||
featuredImagePosition: 'belowTitle', // 'aboveTitle'|'belowTitle'|'none'
|
||||
//imageAlignment: 'centerPadded', // 'centerFull'|'centerPadded'|'left'|'right'|'alternate'|'none'
|
||||
showAuthor: 'no', // 'no'|'aboveText'|'belowText'
|
||||
authorPrecededBy: 'Author:',
|
||||
showCategories: 'no', // 'no'|'aboveText'|'belowText'
|
||||
@ -100,42 +100,43 @@ define([
|
||||
_container: new (App.getBlockTypeModel('container'))()
|
||||
}, App.getConfig().get('blockDefaults.automatedLatestContent'));
|
||||
},
|
||||
relations: function() {
|
||||
relations: function () {
|
||||
return {
|
||||
readMoreButton: App.getBlockTypeModel('button'),
|
||||
divider: App.getBlockTypeModel('divider'),
|
||||
_container: App.getBlockTypeModel('container')
|
||||
};
|
||||
},
|
||||
initialize: function() {
|
||||
initialize: function () {
|
||||
base.BlockView.prototype.initialize.apply(this, arguments);
|
||||
this.on('change:amount change:contentType change:terms change:inclusionType change:displayType change:titleFormat change:featuredImagePosition change:titleAlignment change:titleIsLink change:imageFullWidth change:showAuthor change:authorPrecededBy change:showCategories change:categoriesPrecededBy change:readMoreType change:readMoreText change:sortBy change:showDivider', this._scheduleFetchPosts, this);
|
||||
this.listenTo(this.get('readMoreButton'), 'change', this._scheduleFetchPosts);
|
||||
this.listenTo(this.get('divider'), 'change', this._scheduleFetchPosts);
|
||||
this.on('add remove update reset', this._scheduleFetchPosts);
|
||||
this.on('change:amount change:contentType change:terms change:inclusionType change:displayType change:titleFormat change:featuredImagePosition change:titleAlignment change:titleIsLink change:imageFullWidth change:showAuthor change:authorPrecededBy change:showCategories change:categoriesPrecededBy change:readMoreType change:readMoreText change:sortBy change:showDivider', this._handleChanges, this);
|
||||
this.listenTo(this.get('readMoreButton'), 'change', this._handleChanges);
|
||||
this.listenTo(this.get('divider'), 'change', this._handleChanges);
|
||||
this.on('add remove update reset', this._handleChanges);
|
||||
this.on('refreshPosts', this.updatePosts, this);
|
||||
},
|
||||
updatePosts: function(posts) {
|
||||
this.get('_container.blocks').reset(posts, {parse: true});
|
||||
updatePosts: function (posts) {
|
||||
this.get('_container.blocks').reset(posts, { parse: true });
|
||||
},
|
||||
/**
|
||||
* Batch more changes during a specific time, instead of fetching
|
||||
* ALC posts on each model change
|
||||
*/
|
||||
_scheduleFetchPosts: function() {
|
||||
_handleChanges: function () {
|
||||
this._updateDefaults();
|
||||
App.getChannel().trigger('automatedLatestContentRefresh');
|
||||
}
|
||||
});
|
||||
|
||||
Module.AutomatedLatestContentBlockView = base.BlockView.extend({
|
||||
className: 'mailpoet_block mailpoet_automated_latest_content_block mailpoet_droppable_block',
|
||||
initialize: function() {
|
||||
initialize: function () {
|
||||
function replaceButtonStylesHandler(data) {
|
||||
this.model.set({readMoreButton: data});
|
||||
this.model.set({ readMoreButton: data });
|
||||
}
|
||||
App.getChannel().on('replaceAllButtonStyles', replaceButtonStylesHandler.bind(this));
|
||||
},
|
||||
getTemplate: function() { return templates.automatedLatestContentBlock; },
|
||||
getTemplate: function () { return window.templates.automatedLatestContentBlock; },
|
||||
regions: {
|
||||
toolsRegion: '.mailpoet_tools',
|
||||
postsRegion: '.mailpoet_automated_latest_content_block_posts'
|
||||
@ -148,14 +149,14 @@ define([
|
||||
events: _.extend(base.BlockView.prototype.events, {
|
||||
'click .mailpoet_automated_latest_content_block_overlay': 'showSettings'
|
||||
}),
|
||||
onDragSubstituteBy: function() { return Module.AutomatedLatestContentWidgetView; },
|
||||
onRender: function() {
|
||||
var ContainerView = App.getBlockTypeView('container'),
|
||||
renderOptions = {
|
||||
disableTextEditor: true,
|
||||
disableDragAndDrop: true,
|
||||
emptyContainerMessage: MailPoet.I18n.t('noPostsToDisplay')
|
||||
};
|
||||
onDragSubstituteBy: function () { return Module.AutomatedLatestContentWidgetView; },
|
||||
onRender: function () {
|
||||
var ContainerView = App.getBlockTypeView('container');
|
||||
var renderOptions = {
|
||||
disableTextEditor: true,
|
||||
disableDragAndDrop: true,
|
||||
emptyContainerMessage: MailPoet.I18n.t('noPostsToDisplay')
|
||||
};
|
||||
this.toolsView = new Module.AutomatedLatestContentBlockToolsView({ model: this.model });
|
||||
this.showChildView('toolsRegion', this.toolsView);
|
||||
this.showChildView('postsRegion', new ContainerView({ model: this.model.get('_container'), renderOptions: renderOptions }));
|
||||
@ -163,13 +164,13 @@ define([
|
||||
});
|
||||
|
||||
Module.AutomatedLatestContentBlockToolsView = base.BlockToolsView.extend({
|
||||
getSettingsView: function() { return Module.AutomatedLatestContentBlockSettingsView; }
|
||||
getSettingsView: function () { return Module.AutomatedLatestContentBlockSettingsView; }
|
||||
});
|
||||
|
||||
// Sidebar view container
|
||||
Module.AutomatedLatestContentBlockSettingsView = base.BlockSettingsView.extend({
|
||||
getTemplate: function() { return templates.automatedLatestContentBlockSettings; },
|
||||
events: function() {
|
||||
getTemplate: function () { return window.templates.automatedLatestContentBlockSettings; },
|
||||
events: function () {
|
||||
return {
|
||||
'click .mailpoet_automated_latest_content_hide_display_options': 'toggleDisplayOptions',
|
||||
'click .mailpoet_automated_latest_content_show_display_options': 'toggleDisplayOptions',
|
||||
@ -195,7 +196,7 @@ define([
|
||||
'click .mailpoet_done_editing': 'close'
|
||||
};
|
||||
},
|
||||
onRender: function() {
|
||||
onRender: function () {
|
||||
var that = this;
|
||||
|
||||
// Dynamically update available post types
|
||||
@ -211,35 +212,36 @@ define([
|
||||
term: params.term
|
||||
};
|
||||
},
|
||||
transport: function(options, success, failure) {
|
||||
transport: function (options, success, failure) {
|
||||
var taxonomies;
|
||||
var termsPromise;
|
||||
var promise = CommunicationComponent.getTaxonomies(
|
||||
that.model.get('contentType')
|
||||
).then(function(tax) {
|
||||
).then(function (tax) {
|
||||
taxonomies = tax;
|
||||
// Fetch available terms based on the list of taxonomies already fetched
|
||||
var promise = CommunicationComponent.getTerms({
|
||||
termsPromise = CommunicationComponent.getTerms({
|
||||
search: options.data.term,
|
||||
taxonomies: _.keys(taxonomies)
|
||||
}).then(function(terms) {
|
||||
}).then(function (terms) {
|
||||
return {
|
||||
taxonomies: taxonomies,
|
||||
terms: terms
|
||||
};
|
||||
});
|
||||
return promise;
|
||||
return termsPromise;
|
||||
});
|
||||
|
||||
promise.then(success);
|
||||
promise.fail(failure);
|
||||
return promise;
|
||||
},
|
||||
processResults: function(data) {
|
||||
processResults: function (data) {
|
||||
// Transform taxonomies and terms into select2 compatible format
|
||||
return {
|
||||
results: _.map(
|
||||
data.terms,
|
||||
function(item) {
|
||||
function (item) {
|
||||
return _.defaults({
|
||||
text: data.taxonomies[item.taxonomy].labels.singular_name + ': ' + item.name,
|
||||
id: item.term_id
|
||||
@ -250,23 +252,23 @@ define([
|
||||
}
|
||||
}
|
||||
}).on({
|
||||
'select2:select': function(event) {
|
||||
'select2:select': function (event) {
|
||||
var terms = that.model.get('terms');
|
||||
terms.add(event.params.data);
|
||||
// Reset whole model in order for change events to propagate properly
|
||||
that.model.set('terms', terms.toJSON());
|
||||
},
|
||||
'select2:unselect': function(event) {
|
||||
'select2:unselect': function (event) {
|
||||
var terms = that.model.get('terms');
|
||||
terms.remove(event.params.data);
|
||||
// Reset whole model in order for change events to propagate properly
|
||||
that.model.set('terms', terms.toJSON());
|
||||
}
|
||||
}).trigger( 'change' );
|
||||
}).trigger('change');
|
||||
},
|
||||
toggleDisplayOptions: function(event) {
|
||||
var el = this.$('.mailpoet_automated_latest_content_display_options'),
|
||||
showControl = this.$('.mailpoet_automated_latest_content_show_display_options');
|
||||
toggleDisplayOptions: function () {
|
||||
var el = this.$('.mailpoet_automated_latest_content_display_options');
|
||||
var showControl = this.$('.mailpoet_automated_latest_content_show_display_options');
|
||||
if (el.hasClass('mailpoet_closed')) {
|
||||
el.removeClass('mailpoet_closed');
|
||||
showControl.addClass('mailpoet_hidden');
|
||||
@ -275,7 +277,7 @@ define([
|
||||
showControl.removeClass('mailpoet_hidden');
|
||||
}
|
||||
},
|
||||
showButtonSettings: function(event) {
|
||||
showButtonSettings: function () {
|
||||
var buttonModule = ButtonBlock;
|
||||
(new buttonModule.ButtonBlockSettingsView({
|
||||
model: this.model.get('readMoreButton'),
|
||||
@ -286,7 +288,7 @@ define([
|
||||
}
|
||||
})).render();
|
||||
},
|
||||
showDividerSettings: function(event) {
|
||||
showDividerSettings: function () {
|
||||
var dividerModule = DividerBlock;
|
||||
(new dividerModule.DividerBlockSettingsView({
|
||||
model: this.model.get('divider'),
|
||||
@ -296,21 +298,21 @@ define([
|
||||
}
|
||||
})).render();
|
||||
},
|
||||
changeReadMoreType: function(event) {
|
||||
changeReadMoreType: function (event) {
|
||||
var value = jQuery(event.target).val();
|
||||
if (value == 'link') {
|
||||
if (value === 'link') {
|
||||
this.$('.mailpoet_automated_latest_content_read_more_text').removeClass('mailpoet_hidden');
|
||||
this.$('.mailpoet_automated_latest_content_select_button').addClass('mailpoet_hidden');
|
||||
} else if (value == 'button') {
|
||||
} else if (value === 'button') {
|
||||
this.$('.mailpoet_automated_latest_content_read_more_text').addClass('mailpoet_hidden');
|
||||
this.$('.mailpoet_automated_latest_content_select_button').removeClass('mailpoet_hidden');
|
||||
}
|
||||
this.changeField('readMoreType', event);
|
||||
},
|
||||
changeDisplayType: function(event) {
|
||||
changeDisplayType: function (event) {
|
||||
var value = jQuery(event.target).val();
|
||||
|
||||
if (value == 'titleOnly') {
|
||||
if (value === 'titleOnly') {
|
||||
this.$('.mailpoet_automated_latest_content_title_as_list').removeClass('mailpoet_hidden');
|
||||
this.$('.mailpoet_automated_latest_content_image_full_width_option').addClass('mailpoet_hidden');
|
||||
this.$('.mailpoet_automated_latest_content_image_separator').addClass('mailpoet_hidden');
|
||||
@ -334,9 +336,9 @@ define([
|
||||
}
|
||||
this.changeField('displayType', event);
|
||||
},
|
||||
changeTitleFormat: function(event) {
|
||||
changeTitleFormat: function (event) {
|
||||
var value = jQuery(event.target).val();
|
||||
if (value == 'ul') {
|
||||
if (value === 'ul') {
|
||||
this.$('.mailpoet_automated_latest_content_non_title_list_options').addClass('mailpoet_hidden');
|
||||
|
||||
this.model.set('titleIsLink', true);
|
||||
@ -348,12 +350,12 @@ define([
|
||||
}
|
||||
this.changeField('titleFormat', event);
|
||||
},
|
||||
_updateContentTypes: function(postTypes) {
|
||||
var select = this.$('.mailpoet_automated_latest_content_content_type'),
|
||||
selectedValue = this.model.get('contentType');
|
||||
_updateContentTypes: function (postTypes) {
|
||||
var select = this.$('.mailpoet_automated_latest_content_content_type');
|
||||
var selectedValue = this.model.get('contentType');
|
||||
|
||||
select.find('option').remove();
|
||||
_.each(postTypes, function(type) {
|
||||
_.each(postTypes, function (type) {
|
||||
select.append(jQuery('<option>', {
|
||||
value: type.name,
|
||||
text: type.label
|
||||
@ -364,35 +366,35 @@ define([
|
||||
});
|
||||
|
||||
Module.AutomatedLatestContentWidgetView = base.WidgetView.extend({
|
||||
getTemplate: function() { return templates.automatedLatestContentInsertion; },
|
||||
getTemplate: function () { return window.templates.automatedLatestContentInsertion; },
|
||||
behaviors: {
|
||||
DraggableBehavior: {
|
||||
cloneOriginal: true,
|
||||
drop: function() {
|
||||
drop: function () {
|
||||
return new Module.AutomatedLatestContentBlockModel({}, { parse: true });
|
||||
},
|
||||
onDrop: function(options) {
|
||||
onDrop: function (options) {
|
||||
options.droppedView.triggerMethod('showSettings');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function(App, options) {
|
||||
App.registerBlockType('automatedLatestContent', {
|
||||
App.on('before:start', function (BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('automatedLatestContent', {
|
||||
blockModel: Module.AutomatedLatestContentBlockModel,
|
||||
blockView: Module.AutomatedLatestContentBlockView
|
||||
});
|
||||
|
||||
App.registerWidget({
|
||||
BeforeStartApp.registerWidget({
|
||||
name: 'automatedLatestContent',
|
||||
widgetView: Module.AutomatedLatestContentWidgetView,
|
||||
priority: 97
|
||||
});
|
||||
});
|
||||
|
||||
App.on('start', function(App, options) {
|
||||
var Application = App;
|
||||
App.on('start', function (StartApp) {
|
||||
var Application = StartApp;
|
||||
Application._ALCSupervisor = new Module.ALCSupervisor();
|
||||
Application._ALCSupervisor.refresh();
|
||||
});
|
||||
|
@ -5,30 +5,34 @@
|
||||
* BlockToolsView, BlockSettingsView and BlockWidgetView are optional.
|
||||
*/
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'backbone.marionette',
|
||||
'backbone.supermodel',
|
||||
'underscore',
|
||||
'jquery',
|
||||
'mailpoet',
|
||||
'modal'
|
||||
], function(App, Marionette, SuperModel, _, jQuery, MailPoet, Modal) {
|
||||
|
||||
'newsletter_editor/App',
|
||||
'backbone.marionette',
|
||||
'backbone.supermodel',
|
||||
'underscore',
|
||||
'jquery',
|
||||
'mailpoet',
|
||||
'modal'
|
||||
], function base(App, Marionette, SuperModel, _, jQuery, MailPoet) {
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
AugmentedView = Marionette.View.extend({});
|
||||
var Module = {};
|
||||
var AugmentedView = Marionette.View.extend({});
|
||||
|
||||
Module.BlockModel = SuperModel.extend({
|
||||
stale: [], // Attributes to be removed upon saving
|
||||
initialize: function() {
|
||||
var that = this;
|
||||
this.on('change', function() {
|
||||
initialize: function initialize() {
|
||||
this.on('change', function onChange() {
|
||||
this._updateDefaults();
|
||||
App.getChannel().trigger('autoSave');
|
||||
});
|
||||
},
|
||||
_getDefaults: function(blockDefaults, configDefaults) {
|
||||
var defaults = (_.isObject(configDefaults) && _.isFunction(configDefaults.toJSON)) ? configDefaults.toJSON() : configDefaults;
|
||||
_getDefaults: function getDefaults(blockDefaults, configDefaults) {
|
||||
var defaults;
|
||||
if (_.isObject(configDefaults) && _.isFunction(configDefaults.toJSON)) {
|
||||
defaults = configDefaults.toJSON();
|
||||
} else {
|
||||
defaults = configDefaults;
|
||||
}
|
||||
|
||||
// Patch the resulting JSON object and fix it's constructors to be Object.
|
||||
// Otherwise SuperModel interprets it not as a simpleObject
|
||||
@ -36,11 +40,15 @@ define([
|
||||
// TODO: Investigate for a better solution
|
||||
return JSON.parse(JSON.stringify(jQuery.extend(blockDefaults, defaults || {})));
|
||||
},
|
||||
toJSON: function() {
|
||||
_updateDefaults: function updateDefaults() {
|
||||
var context = this.get('context') || this.get('type');
|
||||
App.getConfig().set('blockDefaults.' + context, this.toJSON());
|
||||
},
|
||||
toJSON: function toJSON() {
|
||||
// Remove stale attributes from resulting JSON object
|
||||
return _.omit(SuperModel.prototype.toJSON.call(this), this.stale);
|
||||
},
|
||||
getChildren: function() {
|
||||
getChildren: function getChildren() {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
@ -62,13 +70,14 @@ define([
|
||||
DraggableBehavior: {
|
||||
cloneOriginal: true,
|
||||
hideOriginal: true,
|
||||
onDrop: function(options) {
|
||||
onDrop: function onDrop(options) {
|
||||
// After a clone of model has been dropped, cleanup
|
||||
// and destroy self
|
||||
options.dragBehavior.view.model.destroy();
|
||||
},
|
||||
onDragSubstituteBy: function(behavior) {
|
||||
var WidgetView, node;
|
||||
onDragSubstituteBy: function onDragSubstituteBy(behavior) {
|
||||
var WidgetView;
|
||||
var node;
|
||||
// When block is being dragged, display the widget icon instead.
|
||||
// This will create an instance of block's widget view and
|
||||
// use it's rendered DOM element instead of the content block
|
||||
@ -79,80 +88,84 @@ define([
|
||||
WidgetView.destroy();
|
||||
return node;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
HighlightEditingBehavior: {}
|
||||
},
|
||||
templateContext: function() {
|
||||
templateContext: function templateContext() {
|
||||
return {
|
||||
model: this.model.toJSON(),
|
||||
viewCid: this.cid
|
||||
};
|
||||
},
|
||||
constructor: function() {
|
||||
constructor: function constructor() {
|
||||
AugmentedView.apply(this, arguments);
|
||||
this.$el.addClass('mailpoet_editor_view_' + this.cid);
|
||||
},
|
||||
initialize: function() {
|
||||
initialize: function initialize() {
|
||||
this.on('showSettings', this.showSettings, this);
|
||||
this.on('dom:refresh', this.showBlock, this);
|
||||
this._isFirstRender = true;
|
||||
},
|
||||
showTools: function(_event) {
|
||||
showTools: function showTools() {
|
||||
if (!this.showingToolsDisabled) {
|
||||
this.$('> .mailpoet_tools').addClass('mailpoet_display_tools');
|
||||
this.toolsView.triggerMethod('showTools');
|
||||
}
|
||||
},
|
||||
hideTools: function(e) {
|
||||
hideTools: function hideTools() {
|
||||
this.$('> .mailpoet_tools').removeClass('mailpoet_display_tools');
|
||||
this.toolsView.triggerMethod('hideTools');
|
||||
},
|
||||
enableShowingTools: function() {
|
||||
enableShowingTools: function enableShowingTools() {
|
||||
this.showingToolsDisabled = false;
|
||||
},
|
||||
disableShowingTools: function() {
|
||||
disableShowingTools: function disableShowingTools() {
|
||||
this.showingToolsDisabled = true;
|
||||
this.hideTools();
|
||||
},
|
||||
showSettings: function(options) {
|
||||
showSettings: function showSettings(options) {
|
||||
this.toolsView.triggerMethod('showSettings', options);
|
||||
},
|
||||
/**
|
||||
* Defines drop behavior of BlockView instance
|
||||
*/
|
||||
getDropFunc: function() {
|
||||
return function() {
|
||||
getDropFunc: function getDropFunc() {
|
||||
return function getDropFuncClone() {
|
||||
return this.model.clone();
|
||||
}.bind(this);
|
||||
},
|
||||
disableDragging: function() {
|
||||
disableDragging: function disableDragging() {
|
||||
this.$el.addClass('mailpoet_ignore_drag');
|
||||
},
|
||||
enableDragging: function() {
|
||||
enableDragging: function enableDragging() {
|
||||
this.$el.removeClass('mailpoet_ignore_drag');
|
||||
},
|
||||
showBlock: function() {
|
||||
showBlock: function showBlock() {
|
||||
if (this._isFirstRender) {
|
||||
this.transitionIn();
|
||||
this._isFirstRender = false;
|
||||
}
|
||||
},
|
||||
deleteBlock: function() {
|
||||
this.transitionOut().then(function() {
|
||||
deleteBlock: function deleteBlock() {
|
||||
this.transitionOut().then(function deleteBlockDestroy() {
|
||||
this.model.destroy();
|
||||
}.bind(this));
|
||||
},
|
||||
duplicateBlock: function() {
|
||||
this.model.collection.add(this.model.toJSON(), {at: this.model.collection.findIndex(this.model)});
|
||||
duplicateBlock: function duplicateBlock() {
|
||||
this.model.collection.add(
|
||||
this.model.toJSON(),
|
||||
{ at: this.model.collection.findIndex(this.model) }
|
||||
);
|
||||
},
|
||||
transitionIn: function() {
|
||||
transitionIn: function transitionIn() {
|
||||
return this._transition('slideDown', 'fadeIn', 'easeOut');
|
||||
},
|
||||
transitionOut: function() {
|
||||
transitionOut: function transitionOut() {
|
||||
return this._transition('slideUp', 'fadeOut', 'easeIn');
|
||||
},
|
||||
_transition: function(slideDirection, fadeDirection, easing) {
|
||||
_transition: function transition(slideDirection, fadeDirection, easing) {
|
||||
var promise = jQuery.Deferred();
|
||||
|
||||
this.$el.velocity(
|
||||
@ -160,9 +173,9 @@ define([
|
||||
{
|
||||
duration: 250,
|
||||
easing: easing,
|
||||
complete: function() {
|
||||
complete: function complete() {
|
||||
promise.resolve();
|
||||
}.bind(this)
|
||||
}
|
||||
}
|
||||
).velocity(
|
||||
fadeDirection,
|
||||
@ -178,7 +191,7 @@ define([
|
||||
});
|
||||
|
||||
Module.BlockToolsView = AugmentedView.extend({
|
||||
getTemplate: function() { return templates.genericBlockTools; },
|
||||
getTemplate: function getTemplate() { return window.templates.genericBlockTools; },
|
||||
events: {
|
||||
'click .mailpoet_edit_block': 'changeSettings',
|
||||
'click .mailpoet_delete_block_activate': 'showDeletionConfirmation',
|
||||
@ -193,8 +206,8 @@ define([
|
||||
duplicate: true,
|
||||
move: true
|
||||
},
|
||||
getSettingsView: function() { return Module.BlockSettingsView; },
|
||||
initialize: function(opts) {
|
||||
getSettingsView: function getSettingsView() { return Module.BlockSettingsView; },
|
||||
initialize: function initialize(opts) {
|
||||
var options = opts || {};
|
||||
if (!_.isUndefined(options.tools)) {
|
||||
// Make a new block specific tool config object
|
||||
@ -205,29 +218,39 @@ define([
|
||||
this.on('hideTools', this.hideDeletionConfirmation, this);
|
||||
this.on('showSettings', this.changeSettings);
|
||||
},
|
||||
templateContext: function() {
|
||||
templateContext: function templateContext() {
|
||||
return {
|
||||
model: this.model.toJSON(),
|
||||
viewCid: this.cid,
|
||||
tools: this.tools
|
||||
};
|
||||
},
|
||||
changeSettings: function(options) {
|
||||
changeSettings: function changeSettings(options) {
|
||||
var ViewType = this.getSettingsView();
|
||||
(new ViewType(_.extend({ model: this.model }, options || {}))).render();
|
||||
},
|
||||
showDeletionConfirmation: function() {
|
||||
showDeletionConfirmation: function showDeletionConfirmation() {
|
||||
this.$('.mailpoet_delete_block')
|
||||
.closest('.mailpoet_block')
|
||||
.find('> .mailpoet_block_highlight')
|
||||
.css({ background: '#E64047', opacity: 0.5 });
|
||||
|
||||
this.$('.mailpoet_delete_block').addClass('mailpoet_delete_block_activated');
|
||||
},
|
||||
hideDeletionConfirmation: function() {
|
||||
hideDeletionConfirmation: function hideDeletionConfirmation() {
|
||||
this.$('.mailpoet_delete_block')
|
||||
.closest('.mailpoet_block')
|
||||
.find('> .mailpoet_block_highlight')
|
||||
.css({ background: 'transparent', opacity: 1 });
|
||||
|
||||
this.$('.mailpoet_delete_block').removeClass('mailpoet_delete_block_activated');
|
||||
},
|
||||
deleteBlock: function(event) {
|
||||
deleteBlock: function deleteBlock(event) {
|
||||
event.preventDefault();
|
||||
this.model.trigger('delete');
|
||||
return false;
|
||||
},
|
||||
duplicateBlock: function(event) {
|
||||
duplicateBlock: function duplicateBlock(event) {
|
||||
event.preventDefault();
|
||||
this.model.trigger('duplicate');
|
||||
return false;
|
||||
@ -239,14 +262,15 @@ define([
|
||||
behaviors: {
|
||||
ColorPickerBehavior: {}
|
||||
},
|
||||
initialize: function(params) {
|
||||
initialize: function initialize(params) {
|
||||
var panelParams;
|
||||
this.model.trigger('startEditing');
|
||||
var panelParams = {
|
||||
panelParams = {
|
||||
element: this.$el,
|
||||
template: '',
|
||||
position: 'right',
|
||||
width: App.getConfig().get('sidepanelWidth'),
|
||||
onCancel: function() {
|
||||
onCancel: function onCancel() {
|
||||
this.destroy();
|
||||
}.bind(this)
|
||||
};
|
||||
@ -257,37 +281,37 @@ define([
|
||||
MailPoet.Modal.panel(panelParams);
|
||||
}
|
||||
},
|
||||
templateContext: function() {
|
||||
templateContext: function templateContext() {
|
||||
return {
|
||||
model: this.model.toJSON()
|
||||
};
|
||||
},
|
||||
close: function(event) {
|
||||
close: function close() {
|
||||
this.destroy();
|
||||
},
|
||||
changeField: function(field, event) {
|
||||
changeField: function changeField(field, event) {
|
||||
this.model.set(field, jQuery(event.target).val());
|
||||
},
|
||||
changePixelField: function(field, event) {
|
||||
changePixelField: function changePixelField(field, event) {
|
||||
this.changeFieldWithSuffix(field, event, 'px');
|
||||
},
|
||||
changeFieldWithSuffix: function(field, event, suffix) {
|
||||
changeFieldWithSuffix: function changeFieldWithSuffix(field, event, suffix) {
|
||||
this.model.set(field, jQuery(event.target).val() + suffix);
|
||||
},
|
||||
changeBoolField: function(field, event) {
|
||||
this.model.set(field, (jQuery(event.target).val() === 'true') ? true : false);
|
||||
changeBoolField: function changeBoolField(field, event) {
|
||||
this.model.set(field, (jQuery(event.target).val() === 'true'));
|
||||
},
|
||||
changeBoolCheckboxField: function(field, event) {
|
||||
changeBoolCheckboxField: function changeBoolCheckboxField(field, event) {
|
||||
this.model.set(field, (!!jQuery(event.target).prop('checked')));
|
||||
},
|
||||
changeColorField: function(field, event) {
|
||||
changeColorField: function changeColorField(field, event) {
|
||||
var value = jQuery(event.target).val();
|
||||
if (value === '') {
|
||||
value = 'transparent';
|
||||
}
|
||||
this.model.set(field, value);
|
||||
},
|
||||
onBeforeDestroy: function() {
|
||||
onBeforeDestroy: function onBeforeDestroy() {
|
||||
MailPoet.Modal.close();
|
||||
this.model.trigger('stopEditing');
|
||||
}
|
||||
@ -297,8 +321,8 @@ define([
|
||||
className: 'mailpoet_widget mailpoet_droppable_block mailpoet_droppable_widget',
|
||||
behaviors: {
|
||||
DraggableBehavior: {
|
||||
drop: function() {
|
||||
throw 'Unsupported operation';
|
||||
drop: function drop() {
|
||||
throw new Error('Unsupported operation');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,21 @@
|
||||
/* eslint-disable func-names */
|
||||
/**
|
||||
* Button content block
|
||||
*/
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base',
|
||||
'mailpoet',
|
||||
'underscore',
|
||||
'jquery'
|
||||
], function(App, BaseBlock, MailPoet, _, jQuery) {
|
||||
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base',
|
||||
'mailpoet',
|
||||
'underscore',
|
||||
'jquery'
|
||||
], function (App, BaseBlock, MailPoet, _, jQuery) {
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock;
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
|
||||
Module.ButtonBlockModel = base.BlockModel.extend({
|
||||
defaults: function() {
|
||||
defaults: function () {
|
||||
return this._getDefaults({
|
||||
type: 'button',
|
||||
text: 'Button',
|
||||
@ -42,31 +42,31 @@ define([
|
||||
|
||||
Module.ButtonBlockView = base.BlockView.extend({
|
||||
className: 'mailpoet_block mailpoet_button_block mailpoet_droppable_block',
|
||||
getTemplate: function() { return templates.buttonBlock; },
|
||||
onDragSubstituteBy: function() { return Module.ButtonWidgetView; },
|
||||
getTemplate: function () { return window.templates.buttonBlock; },
|
||||
onDragSubstituteBy: function () { return Module.ButtonWidgetView; },
|
||||
behaviors: _.extend({}, base.BlockView.prototype.behaviors, {
|
||||
ShowSettingsBehavior: {}
|
||||
}),
|
||||
initialize: function() {
|
||||
initialize: function () {
|
||||
base.BlockView.prototype.initialize.apply(this, arguments);
|
||||
|
||||
// Listen for attempts to change all dividers in one go
|
||||
this._replaceButtonStylesHandler = function(data) { this.model.set(data); }.bind(this);
|
||||
this._replaceButtonStylesHandler = function (data) { this.model.set(data); }.bind(this);
|
||||
App.getChannel().on('replaceAllButtonStyles', this._replaceButtonStylesHandler);
|
||||
},
|
||||
onRender: function() {
|
||||
onRender: function () {
|
||||
this.toolsView = new Module.ButtonBlockToolsView({ model: this.model });
|
||||
this.showChildView('toolsRegion', this.toolsView);
|
||||
}
|
||||
});
|
||||
|
||||
Module.ButtonBlockToolsView = base.BlockToolsView.extend({
|
||||
getSettingsView: function() { return Module.ButtonBlockSettingsView; }
|
||||
getSettingsView: function () { return Module.ButtonBlockSettingsView; }
|
||||
});
|
||||
|
||||
Module.ButtonBlockSettingsView = base.BlockSettingsView.extend({
|
||||
getTemplate: function() { return templates.buttonBlockSettings; },
|
||||
events: function() {
|
||||
getTemplate: function () { return window.templates.buttonBlockSettings; },
|
||||
events: function () {
|
||||
return {
|
||||
'input .mailpoet_field_button_text': _.partial(this.changeField, 'text'),
|
||||
'input .mailpoet_field_button_url': _.partial(this.changeField, 'url'),
|
||||
@ -98,20 +98,20 @@ define([
|
||||
'click .mailpoet_done_editing': 'close'
|
||||
};
|
||||
},
|
||||
templateContext: function() {
|
||||
templateContext: function () {
|
||||
return _.extend({}, base.BlockView.prototype.templateContext.apply(this, arguments), {
|
||||
availableStyles: App.getAvailableStyles().toJSON(),
|
||||
renderOptions: this.renderOptions
|
||||
});
|
||||
},
|
||||
applyToAll: function() {
|
||||
applyToAll: function () {
|
||||
App.getChannel().trigger('replaceAllButtonStyles', _.pick(this.model.toJSON(), 'styles', 'type'));
|
||||
},
|
||||
updateValueAndCall: function(fieldToUpdate, callable, event) {
|
||||
updateValueAndCall: function (fieldToUpdate, callable, event) {
|
||||
this.$(fieldToUpdate).val(jQuery(event.target).val());
|
||||
callable(event);
|
||||
},
|
||||
changeFontWeight: function(event) {
|
||||
changeFontWeight: function (event) {
|
||||
var checked = !!jQuery(event.target).prop('checked');
|
||||
this.model.set(
|
||||
'styles.block.fontWeight',
|
||||
@ -121,24 +121,24 @@ define([
|
||||
});
|
||||
|
||||
Module.ButtonWidgetView = base.WidgetView.extend({
|
||||
getTemplate: function() { return templates.buttonInsertion; },
|
||||
getTemplate: function () { return window.templates.buttonInsertion; },
|
||||
behaviors: {
|
||||
DraggableBehavior: {
|
||||
cloneOriginal: true,
|
||||
drop: function() {
|
||||
drop: function () {
|
||||
return new Module.ButtonBlockModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function(App, options) {
|
||||
App.registerBlockType('button', {
|
||||
App.on('before:start', function (BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('button', {
|
||||
blockModel: Module.ButtonBlockModel,
|
||||
blockView: Module.ButtonBlockView
|
||||
});
|
||||
|
||||
App.registerWidget({
|
||||
BeforeStartApp.registerWidget({
|
||||
name: 'button',
|
||||
widgetView: Module.ButtonWidgetView,
|
||||
priority: 92
|
||||
|
@ -1,34 +1,33 @@
|
||||
/* eslint-disable func-names */
|
||||
/**
|
||||
* Container content block.
|
||||
* This is a special kind of block, as it can contain content blocks, as well
|
||||
* as other containers.
|
||||
*/
|
||||
define([
|
||||
'backbone',
|
||||
'backbone.marionette',
|
||||
'underscore',
|
||||
'jquery',
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base'
|
||||
], function(Backbone, Marionette, _, jQuery, App, BaseBlock) {
|
||||
|
||||
'backbone',
|
||||
'backbone.marionette',
|
||||
'underscore',
|
||||
'jquery',
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base'
|
||||
], function (Backbone, Marionette, _, jQuery, App, BaseBlock) {
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock,
|
||||
BlockCollection;
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
var BlockCollection;
|
||||
|
||||
BlockCollection = Backbone.Collection.extend({
|
||||
model: base.BlockModel,
|
||||
initialize: function() {
|
||||
this.on('add change remove', function() { App.getChannel().trigger('autoSave'); });
|
||||
initialize: function () {
|
||||
this.on('add change remove', function () { App.getChannel().trigger('autoSave'); });
|
||||
},
|
||||
parse: function(response) {
|
||||
var self = this;
|
||||
return _.map(response, function(block) {
|
||||
parse: function (response) {
|
||||
return _.map(response, function (block) {
|
||||
var Type = App.getBlockTypeModel(block.type);
|
||||
// TODO: If type has no registered model, use a backup one
|
||||
return new Type(block, {parse: true});
|
||||
return new Type(block, { parse: true });
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -37,7 +36,7 @@ define([
|
||||
relations: {
|
||||
blocks: BlockCollection
|
||||
},
|
||||
defaults: function() {
|
||||
defaults: function () {
|
||||
return this._getDefaults({
|
||||
type: 'container',
|
||||
orientation: 'vertical',
|
||||
@ -49,14 +48,16 @@ define([
|
||||
blocks: new BlockCollection()
|
||||
}, App.getConfig().get('blockDefaults.container'));
|
||||
},
|
||||
validate: function() {
|
||||
_updateDefaults: function updateDefaults() {},
|
||||
validate: function () {
|
||||
// Recursively propagate validation checks to blocks in the tree
|
||||
var invalidBlock = this.get('blocks').find(function(block) { return !block.isValid(); });
|
||||
var invalidBlock = this.get('blocks').find(function (block) { return !block.isValid(); });
|
||||
if (invalidBlock) {
|
||||
return invalidBlock.validationError;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
parse: function(response) {
|
||||
parse: function (response) {
|
||||
// If container has any blocks - add them to a collection
|
||||
if (response.type === 'container' && _.has(response, 'blocks')) {
|
||||
response.blocks = new BlockCollection(response.blocks, {
|
||||
@ -65,8 +66,8 @@ define([
|
||||
}
|
||||
return response;
|
||||
},
|
||||
getChildren: function() {
|
||||
var models = this.get('blocks').map(function(model, index, list) {
|
||||
getChildren: function () {
|
||||
var models = this.get('blocks').map(function (model) {
|
||||
return [model, model.getChildren()];
|
||||
});
|
||||
|
||||
@ -76,10 +77,10 @@ define([
|
||||
|
||||
Module.ContainerBlocksView = Marionette.CollectionView.extend({
|
||||
className: 'mailpoet_container',
|
||||
childView: function(model) {
|
||||
childView: function (model) {
|
||||
return App.getBlockTypeView(model.get('type'));
|
||||
},
|
||||
childViewOptions: function() {
|
||||
childViewOptions: function () {
|
||||
var newRenderOptions = _.clone(this.renderOptions);
|
||||
if (newRenderOptions.depth !== undefined) {
|
||||
newRenderOptions.depth += 1;
|
||||
@ -88,9 +89,9 @@ define([
|
||||
renderOptions: newRenderOptions
|
||||
};
|
||||
},
|
||||
emptyView: function() { return Module.ContainerBlockEmptyView; },
|
||||
emptyViewOptions: function() { return { renderOptions: this.renderOptions }; },
|
||||
initialize: function(options) {
|
||||
emptyView: function () { return Module.ContainerBlockEmptyView; },
|
||||
emptyViewOptions: function () { return { renderOptions: this.renderOptions }; },
|
||||
initialize: function (options) {
|
||||
this.renderOptions = options.renderOptions;
|
||||
}
|
||||
});
|
||||
@ -103,7 +104,7 @@ define([
|
||||
}
|
||||
}),
|
||||
className: 'mailpoet_block mailpoet_container_block mailpoet_droppable_block mailpoet_droppable_layout_block',
|
||||
getTemplate: function() { return templates.containerBlock; },
|
||||
getTemplate: function () { return window.templates.containerBlock; },
|
||||
events: _.extend({}, base.BlockView.prototype.events, {
|
||||
'click .mailpoet_newsletter_layer_selector': 'toggleEditingLayer'
|
||||
}),
|
||||
@ -115,13 +116,14 @@ define([
|
||||
DraggableBehavior: {
|
||||
cloneOriginal: true,
|
||||
hideOriginal: true,
|
||||
onDrop: function(options) {
|
||||
onDrop: function (options) {
|
||||
// After a clone of model has been dropped, cleanup
|
||||
// and destroy self
|
||||
options.dragBehavior.view.model.destroy();
|
||||
},
|
||||
onDragSubstituteBy: function(behavior) {
|
||||
var WidgetView, node;
|
||||
onDragSubstituteBy: function (behavior) {
|
||||
var WidgetView;
|
||||
var node;
|
||||
// When block is being dragged, display the widget icon instead.
|
||||
// This will create an instance of block's widget view and
|
||||
// use it's rendered DOM element instead of the content block
|
||||
@ -132,15 +134,17 @@ define([
|
||||
WidgetView.destroy();
|
||||
return node;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
testAttachToInstance: function(model, view) {
|
||||
testAttachToInstance: function (model, view) {
|
||||
// Attach Draggable only to layout containers and disable it
|
||||
// for root and column containers.
|
||||
return view.renderOptions.depth === 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
HighlightContainerBehavior: {}
|
||||
}),
|
||||
onDragSubstituteBy: function() {
|
||||
onDragSubstituteBy: function () {
|
||||
// For two and three column layouts display their respective widgets,
|
||||
// otherwise always default to one column layout widget
|
||||
if (this.renderOptions.depth === 1) {
|
||||
@ -148,14 +152,13 @@ define([
|
||||
if (this.model.get('blocks').length === 2) return Module.TwoColumnContainerWidgetView;
|
||||
}
|
||||
return Module.OneColumnContainerWidgetView;
|
||||
|
||||
},
|
||||
initialize: function(options) {
|
||||
initialize: function (options) {
|
||||
base.BlockView.prototype.initialize.apply(this, arguments);
|
||||
|
||||
this.renderOptions = _.defaults(options.renderOptions || {}, {});
|
||||
},
|
||||
onRender: function() {
|
||||
onRender: function () {
|
||||
this.toolsView = new Module.ContainerBlockToolsView({
|
||||
model: this.model,
|
||||
tools: {
|
||||
@ -173,40 +176,42 @@ define([
|
||||
}));
|
||||
|
||||
// TODO: Look for a better way to do this than here
|
||||
// Sets child container orientation HTML class here, as child CollectionView won't have access to model and will overwrite existing region element instead
|
||||
// Sets child container orientation HTML class here,
|
||||
// as child CollectionView won't have access to model
|
||||
// and will overwrite existing region element instead
|
||||
this.$('> .mailpoet_container').attr('class', 'mailpoet_container mailpoet_container_' + this.model.get('orientation'));
|
||||
},
|
||||
showTools: function() {
|
||||
showTools: function () {
|
||||
if (this.renderOptions.depth === 1 && !this.$el.hasClass('mailpoet_container_layer_active')) {
|
||||
this.$(this.ui.tools).addClass('mailpoet_display_tools');
|
||||
this.toolsView.triggerMethod('showTools');
|
||||
}
|
||||
},
|
||||
hideTools: function() {
|
||||
hideTools: function () {
|
||||
if (this.renderOptions.depth === 1 && !this.$el.hasClass('mailpoet_container_layer_active')) {
|
||||
this.$(this.ui.tools).removeClass('mailpoet_display_tools');
|
||||
this.toolsView.triggerMethod('hideTools');
|
||||
}
|
||||
},
|
||||
toggleEditingLayer: function(event) {
|
||||
var that = this,
|
||||
$toggleButton = this.$('> .mailpoet_tools .mailpoet_newsletter_layer_selector'),
|
||||
$overlay = jQuery('.mailpoet_layer_overlay'),
|
||||
$container = this.$('> .mailpoet_container'),
|
||||
enableContainerLayer = function() {
|
||||
that.$el.addClass('mailpoet_container_layer_active');
|
||||
$toggleButton.addClass('mailpoet_container_layer_active');
|
||||
$container.addClass('mailpoet_layer_highlight');
|
||||
$overlay.click(disableContainerLayer);
|
||||
$overlay.show();
|
||||
},
|
||||
disableContainerLayer = function() {
|
||||
that.$el.removeClass('mailpoet_container_layer_active');
|
||||
$toggleButton.removeClass('mailpoet_container_layer_active');
|
||||
$container.removeClass('mailpoet_layer_highlight');
|
||||
$overlay.hide();
|
||||
$overlay.off('click');
|
||||
};
|
||||
toggleEditingLayer: function (event) {
|
||||
var that = this;
|
||||
var $toggleButton = this.$('> .mailpoet_tools .mailpoet_newsletter_layer_selector');
|
||||
var $overlay = jQuery('.mailpoet_layer_overlay');
|
||||
var $container = this.$('> .mailpoet_container');
|
||||
var disableContainerLayer = function () {
|
||||
that.$el.removeClass('mailpoet_container_layer_active');
|
||||
$toggleButton.removeClass('mailpoet_container_layer_active');
|
||||
$container.removeClass('mailpoet_layer_highlight');
|
||||
$overlay.hide();
|
||||
$overlay.off('click');
|
||||
};
|
||||
var enableContainerLayer = function () {
|
||||
that.$el.addClass('mailpoet_container_layer_active');
|
||||
$toggleButton.addClass('mailpoet_container_layer_active');
|
||||
$container.addClass('mailpoet_layer_highlight');
|
||||
$overlay.click(disableContainerLayer);
|
||||
$overlay.show();
|
||||
};
|
||||
if ($toggleButton.hasClass('mailpoet_container_layer_active')) {
|
||||
disableContainerLayer();
|
||||
} else {
|
||||
@ -217,11 +222,11 @@ define([
|
||||
});
|
||||
|
||||
Module.ContainerBlockEmptyView = Marionette.View.extend({
|
||||
getTemplate: function() { return templates.containerEmpty; },
|
||||
initialize: function(options) {
|
||||
getTemplate: function () { return window.templates.containerEmpty; },
|
||||
initialize: function (options) {
|
||||
this.renderOptions = _.defaults(options.renderOptions || {}, {});
|
||||
},
|
||||
templateContext: function() {
|
||||
templateContext: function () {
|
||||
return {
|
||||
isRoot: this.renderOptions.depth === 0,
|
||||
emptyContainerMessage: this.renderOptions.emptyContainerMessage || ''
|
||||
@ -230,12 +235,12 @@ define([
|
||||
});
|
||||
|
||||
Module.ContainerBlockToolsView = base.BlockToolsView.extend({
|
||||
getSettingsView: function() { return Module.ContainerBlockSettingsView; }
|
||||
getSettingsView: function () { return Module.ContainerBlockSettingsView; }
|
||||
});
|
||||
|
||||
Module.ContainerBlockSettingsView = base.BlockSettingsView.extend({
|
||||
getTemplate: function() { return templates.containerBlockSettings; },
|
||||
events: function() {
|
||||
getTemplate: function () { return window.templates.containerBlockSettings; },
|
||||
events: function () {
|
||||
return {
|
||||
'change .mailpoet_field_container_background_color': _.partial(this.changeColorField, 'styles.block.backgroundColor'),
|
||||
'click .mailpoet_done_editing': 'close'
|
||||
@ -244,21 +249,21 @@ define([
|
||||
regions: {
|
||||
columnsSettingsRegion: '.mailpoet_container_columns_settings'
|
||||
},
|
||||
initialize: function() {
|
||||
initialize: function () {
|
||||
base.BlockSettingsView.prototype.initialize.apply(this, arguments);
|
||||
|
||||
this._columnsSettingsView = new (Module.ContainerBlockColumnsSettingsView)({
|
||||
collection: this.model.get('blocks')
|
||||
});
|
||||
},
|
||||
onRender: function() {
|
||||
onRender: function () {
|
||||
this.showChildView('columnsSettingsRegion', this._columnsSettingsView);
|
||||
}
|
||||
});
|
||||
|
||||
Module.ContainerBlockColumnsSettingsView = Marionette.CollectionView.extend({
|
||||
childView: function() { return Module.ContainerBlockColumnSettingsView; },
|
||||
childViewOptions: function(model, index) {
|
||||
childView: function () { return Module.ContainerBlockColumnSettingsView; },
|
||||
childViewOptions: function (model, index) {
|
||||
return {
|
||||
columnIndex: index
|
||||
};
|
||||
@ -266,11 +271,11 @@ define([
|
||||
});
|
||||
|
||||
Module.ContainerBlockColumnSettingsView = Marionette.View.extend({
|
||||
getTemplate: function() { return templates.containerBlockColumnSettings; },
|
||||
initialize: function(options) {
|
||||
getTemplate: function () { return window.templates.containerBlockColumnSettings; },
|
||||
initialize: function (options) {
|
||||
this.columnNumber = (options.columnIndex || 0) + 1;
|
||||
},
|
||||
templateContext: function() {
|
||||
templateContext: function () {
|
||||
return {
|
||||
model: this.model.toJSON(),
|
||||
columnNumber: this.columnNumber
|
||||
@ -280,11 +285,11 @@ define([
|
||||
|
||||
Module.OneColumnContainerWidgetView = base.WidgetView.extend({
|
||||
className: base.WidgetView.prototype.className + ' mailpoet_droppable_layout_block',
|
||||
getTemplate: function() { return templates.oneColumnLayoutInsertion; },
|
||||
getTemplate: function () { return window.templates.oneColumnLayoutInsertion; },
|
||||
behaviors: {
|
||||
DraggableBehavior: {
|
||||
cloneOriginal: true,
|
||||
drop: function() {
|
||||
drop: function () {
|
||||
return new Module.ContainerBlockModel({
|
||||
orientation: 'horizontal',
|
||||
blocks: [
|
||||
@ -298,11 +303,11 @@ define([
|
||||
|
||||
Module.TwoColumnContainerWidgetView = base.WidgetView.extend({
|
||||
className: base.WidgetView.prototype.className + ' mailpoet_droppable_layout_block',
|
||||
getTemplate: function() { return templates.twoColumnLayoutInsertion; },
|
||||
getTemplate: function () { return window.templates.twoColumnLayoutInsertion; },
|
||||
behaviors: {
|
||||
DraggableBehavior: {
|
||||
cloneOriginal: true,
|
||||
drop: function() {
|
||||
drop: function () {
|
||||
return new Module.ContainerBlockModel({
|
||||
orientation: 'horizontal',
|
||||
blocks: [
|
||||
@ -317,11 +322,11 @@ define([
|
||||
|
||||
Module.ThreeColumnContainerWidgetView = base.WidgetView.extend({
|
||||
className: base.WidgetView.prototype.className + ' mailpoet_droppable_layout_block',
|
||||
getTemplate: function() { return templates.threeColumnLayoutInsertion; },
|
||||
getTemplate: function () { return window.templates.threeColumnLayoutInsertion; },
|
||||
behaviors: {
|
||||
DraggableBehavior: {
|
||||
cloneOriginal: true,
|
||||
drop: function() {
|
||||
drop: function () {
|
||||
return new Module.ContainerBlockModel({
|
||||
orientation: 'horizontal',
|
||||
blocks: [
|
||||
@ -335,25 +340,25 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function(App, options) {
|
||||
App.registerBlockType('container', {
|
||||
App.on('before:start', function (BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('container', {
|
||||
blockModel: Module.ContainerBlockModel,
|
||||
blockView: Module.ContainerBlockView
|
||||
});
|
||||
|
||||
App.registerLayoutWidget({
|
||||
BeforeStartApp.registerLayoutWidget({
|
||||
name: 'oneColumnLayout',
|
||||
priority: 100,
|
||||
widgetView: Module.OneColumnContainerWidgetView
|
||||
});
|
||||
|
||||
App.registerLayoutWidget({
|
||||
BeforeStartApp.registerLayoutWidget({
|
||||
name: 'twoColumnLayout',
|
||||
priority: 100,
|
||||
widgetView: Module.TwoColumnContainerWidgetView
|
||||
});
|
||||
|
||||
App.registerLayoutWidget({
|
||||
BeforeStartApp.registerLayoutWidget({
|
||||
name: 'threeColumnLayout',
|
||||
priority: 100,
|
||||
widgetView: Module.ThreeColumnContainerWidgetView
|
||||
|
@ -1,21 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Divider content block
|
||||
*/
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base',
|
||||
'underscore',
|
||||
'jquery',
|
||||
'mailpoet'
|
||||
], function(App, BaseBlock, _, jQuery, MailPoet) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock;
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base',
|
||||
'underscore',
|
||||
'jquery'
|
||||
], function dividerBlock(App, BaseBlock, _, jQuery) {
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
|
||||
Module.DividerBlockModel = base.BlockModel.extend({
|
||||
defaults: function() {
|
||||
defaults: function defaults() {
|
||||
return this._getDefaults({
|
||||
type: 'divider',
|
||||
styles: {
|
||||
@ -33,13 +31,13 @@ define([
|
||||
|
||||
Module.DividerBlockView = base.BlockView.extend({
|
||||
className: 'mailpoet_block mailpoet_divider_block mailpoet_droppable_block',
|
||||
getTemplate: function() { return templates.dividerBlock; },
|
||||
getTemplate: function getTemplate() { return window.templates.dividerBlock; },
|
||||
modelEvents: _.omit(base.BlockView.prototype.modelEvents, 'change'),
|
||||
behaviors: _.defaults({
|
||||
ResizableBehavior: {
|
||||
elementSelector: '.mailpoet_content',
|
||||
resizeHandleSelector: '.mailpoet_resize_handle',
|
||||
transformationFunction: function(y) { return y / 2; },
|
||||
transformationFunction: function transformationFunction(y) { return y / 2; },
|
||||
minLength: 0, // TODO: Move this number to editor configuration
|
||||
modelField: 'styles.block.padding'
|
||||
},
|
||||
@ -47,45 +45,47 @@ define([
|
||||
ignoreFrom: '.mailpoet_resize_handle'
|
||||
}
|
||||
}, base.BlockView.prototype.behaviors),
|
||||
onDragSubstituteBy: function() { return Module.DividerWidgetView; },
|
||||
initialize: function() {
|
||||
base.BlockView.prototype.initialize.apply(this, arguments);
|
||||
onDragSubstituteBy: function onDragSubstituteBy() { return Module.DividerWidgetView; },
|
||||
initialize: function initialize() {
|
||||
var that = this;
|
||||
base.BlockView.prototype.initialize.apply(this, arguments);
|
||||
|
||||
// Listen for attempts to change all dividers in one go
|
||||
this._replaceDividerHandler = function(data) { that.model.set(data); that.model.trigger('applyToAll'); };
|
||||
this._replaceDividerHandler = function replaceDividerHandler(data) {
|
||||
that.model.set(data); that.model.trigger('applyToAll');
|
||||
};
|
||||
App.getChannel().on('replaceAllDividers', this._replaceDividerHandler);
|
||||
|
||||
this.listenTo(this.model, 'change:src change:styles.block.backgroundColor change:styles.block.borderStyle change:styles.block.borderWidth change:styles.block.borderColor applyToAll', this.render);
|
||||
this.listenTo(this.model, 'change:styles.block.padding', this.changePadding);
|
||||
},
|
||||
templateContext: function() {
|
||||
templateContext: function templateContext() {
|
||||
return _.extend({
|
||||
totalHeight: parseInt(this.model.get('styles.block.padding'), 10)*2 + parseInt(this.model.get('styles.block.borderWidth')) + 'px'
|
||||
totalHeight: (parseInt(this.model.get('styles.block.padding'), 10) * 2) + parseInt(this.model.get('styles.block.borderWidth'), 10) + 'px'
|
||||
}, base.BlockView.prototype.templateContext.apply(this));
|
||||
},
|
||||
onRender: function() {
|
||||
onRender: function onRender() {
|
||||
this.toolsView = new Module.DividerBlockToolsView({ model: this.model });
|
||||
this.showChildView('toolsRegion', this.toolsView);
|
||||
},
|
||||
onBeforeDestroy: function() {
|
||||
onBeforeDestroy: function onBeforeDestroy() {
|
||||
App.getChannel().off('replaceAllDividers', this._replaceDividerHandler);
|
||||
this.stopListening(this.model);
|
||||
},
|
||||
changePadding: function() {
|
||||
changePadding: function changePadding() {
|
||||
this.$('.mailpoet_content').css('padding-top', this.model.get('styles.block.padding'));
|
||||
this.$('.mailpoet_content').css('padding-bottom', this.model.get('styles.block.padding'));
|
||||
this.$('.mailpoet_resize_handle_text').text(parseInt(this.model.get('styles.block.padding'), 10)*2 + parseInt(this.model.get('styles.block.borderWidth')) + 'px');
|
||||
this.$('.mailpoet_resize_handle_text').text((parseInt(this.model.get('styles.block.padding'), 10) * 2) + parseInt(this.model.get('styles.block.borderWidth'), 10) + 'px');
|
||||
}
|
||||
});
|
||||
|
||||
Module.DividerBlockToolsView = base.BlockToolsView.extend({
|
||||
getSettingsView: function() { return Module.DividerBlockSettingsView; }
|
||||
getSettingsView: function getSettingsView() { return Module.DividerBlockSettingsView; }
|
||||
});
|
||||
|
||||
Module.DividerBlockSettingsView = base.BlockSettingsView.extend({
|
||||
getTemplate: function() { return templates.dividerBlockSettings; },
|
||||
events: function() {
|
||||
getTemplate: function getTemplate() { return window.templates.dividerBlockSettings; },
|
||||
events: function events() {
|
||||
return {
|
||||
'click .mailpoet_field_divider_style': 'changeStyle',
|
||||
|
||||
@ -99,53 +99,53 @@ define([
|
||||
'click .mailpoet_done_editing': 'close'
|
||||
};
|
||||
},
|
||||
modelEvents: function() {
|
||||
modelEvents: function modelEvents() {
|
||||
return {
|
||||
'change:styles.block.borderColor': 'repaintDividerStyleOptions'
|
||||
};
|
||||
},
|
||||
templateContext: function() {
|
||||
templateContext: function templateContext() {
|
||||
return _.extend({}, base.BlockView.prototype.templateContext.apply(this, arguments), {
|
||||
availableStyles: App.getAvailableStyles().toJSON(),
|
||||
renderOptions: this.renderOptions
|
||||
});
|
||||
},
|
||||
changeStyle: function(event) {
|
||||
changeStyle: function changeStyle(event) {
|
||||
var style = jQuery(event.currentTarget).data('style');
|
||||
this.model.set('styles.block.borderStyle', style);
|
||||
this.$('.mailpoet_field_divider_style').removeClass('mailpoet_active_divider_style');
|
||||
this.$('.mailpoet_field_divider_style[data-style="' + style + '"]').addClass('mailpoet_active_divider_style');
|
||||
},
|
||||
repaintDividerStyleOptions: function() {
|
||||
repaintDividerStyleOptions: function repaintDividerStyleOptions() {
|
||||
this.$('.mailpoet_field_divider_style > div').css('border-top-color', this.model.get('styles.block.borderColor'));
|
||||
},
|
||||
applyToAll: function(event) {
|
||||
applyToAll: function applyToAll() {
|
||||
App.getChannel().trigger('replaceAllDividers', this.model.toJSON());
|
||||
},
|
||||
updateValueAndCall: function(fieldToUpdate, callable, event) {
|
||||
updateValueAndCall: function updateValueAndCall(fieldToUpdate, callable, event) {
|
||||
this.$(fieldToUpdate).val(jQuery(event.target).val());
|
||||
callable(event);
|
||||
}
|
||||
});
|
||||
|
||||
Module.DividerWidgetView = base.WidgetView.extend({
|
||||
getTemplate: function() { return templates.dividerInsertion; },
|
||||
getTemplate: function getTemplate() { return window.templates.dividerInsertion; },
|
||||
behaviors: {
|
||||
DraggableBehavior: {
|
||||
cloneOriginal: true,
|
||||
drop: function() {
|
||||
drop: function drop() {
|
||||
return new Module.DividerBlockModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
App.on('before:start', function(App, options) {
|
||||
App.registerBlockType('divider', {
|
||||
App.on('before:start', function onBeforeStart(BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('divider', {
|
||||
blockModel: Module.DividerBlockModel,
|
||||
blockView: Module.DividerBlockView
|
||||
});
|
||||
|
||||
App.registerWidget({
|
||||
BeforeStartApp.registerWidget({
|
||||
name: 'divider',
|
||||
widgetView: Module.DividerWidgetView,
|
||||
priority: 93
|
||||
|
@ -1,19 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Footer content block
|
||||
*/
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base',
|
||||
'underscore'
|
||||
], function(App, BaseBlock, _) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock;
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base',
|
||||
'underscore',
|
||||
'mailpoet'
|
||||
], function footerBlock(App, BaseBlock, _, MailPoet) {
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
|
||||
Module.FooterBlockModel = base.BlockModel.extend({
|
||||
defaults: function() {
|
||||
defaults: function defaults() {
|
||||
return this._getDefaults({
|
||||
type: 'footer',
|
||||
text: '<a href="[link:subscription_unsubscribe_url]">Unsubscribe</a> | <a href="[link:subscription_manage_url]">Manage subscription</a><br /><b>Add your postal address here!</b>',
|
||||
@ -33,18 +33,21 @@ define([
|
||||
}
|
||||
}
|
||||
}, App.getConfig().get('blockDefaults.footer'));
|
||||
},
|
||||
_updateDefaults: function updateDefaults() {
|
||||
App.getConfig().set('blockDefaults.footer', _.omit(this.toJSON(), 'text'));
|
||||
}
|
||||
});
|
||||
|
||||
Module.FooterBlockView = base.BlockView.extend({
|
||||
className: 'mailpoet_block mailpoet_footer_block mailpoet_droppable_block',
|
||||
getTemplate: function() { return templates.footerBlock; },
|
||||
getTemplate: function getTemplate() { return window.templates.footerBlock; },
|
||||
modelEvents: _.extend({
|
||||
'change:styles.block.backgroundColor change:styles.text.fontColor change:styles.text.fontFamily change:styles.text.fontSize change:styles.text.textAlign change:styles.link.fontColor change:styles.link.textDecoration': 'render'
|
||||
}, _.omit(base.BlockView.prototype.modelEvents, 'change')),
|
||||
behaviors: _.extend({}, base.BlockView.prototype.behaviors, {
|
||||
TextEditorBehavior: {
|
||||
configurationFilter: function(originalSettings) {
|
||||
configurationFilter: function configurationFilter(originalSettings) {
|
||||
return _.extend({}, originalSettings, {
|
||||
mailpoet_shortcodes: App.getConfig().get('shortcodes').toJSON(),
|
||||
mailpoet_shortcodes_window_title: MailPoet.I18n.t('shortcodesWindowTitle')
|
||||
@ -52,45 +55,45 @@ define([
|
||||
}
|
||||
}
|
||||
}),
|
||||
onDragSubstituteBy: function() { return Module.FooterWidgetView; },
|
||||
onRender: function() {
|
||||
onDragSubstituteBy: function onDragSubstituteBy() { return Module.FooterWidgetView; },
|
||||
onRender: function onRender() {
|
||||
this.toolsView = new Module.FooterBlockToolsView({ model: this.model });
|
||||
this.showChildView('toolsRegion', this.toolsView);
|
||||
},
|
||||
onTextEditorChange: function(newContent) {
|
||||
onTextEditorChange: function onTextEditorChange(newContent) {
|
||||
this.model.set('text', newContent);
|
||||
},
|
||||
onTextEditorFocus: function() {
|
||||
onTextEditorFocus: function onTextEditorFocus() {
|
||||
this.disableDragging();
|
||||
this.disableShowingTools();
|
||||
},
|
||||
onTextEditorBlur: function() {
|
||||
onTextEditorBlur: function onTextEditorBlur() {
|
||||
this.enableDragging();
|
||||
this.enableShowingTools();
|
||||
}
|
||||
});
|
||||
|
||||
Module.FooterBlockToolsView = base.BlockToolsView.extend({
|
||||
getSettingsView: function() { return Module.FooterBlockSettingsView; }
|
||||
getSettingsView: function getSettingsView() { return Module.FooterBlockSettingsView; }
|
||||
});
|
||||
|
||||
Module.FooterBlockSettingsView = base.BlockSettingsView.extend({
|
||||
getTemplate: function() { return templates.footerBlockSettings; },
|
||||
events: function() {
|
||||
getTemplate: function getTemplate() { return window.templates.footerBlockSettings; },
|
||||
events: function events() {
|
||||
return {
|
||||
'change .mailpoet_field_footer_text_color': _.partial(this.changeColorField, 'styles.text.fontColor'),
|
||||
'change .mailpoet_field_footer_text_font_family': _.partial(this.changeField, 'styles.text.fontFamily'),
|
||||
'change .mailpoet_field_footer_text_size': _.partial(this.changeField, 'styles.text.fontSize'),
|
||||
'change #mailpoet_field_footer_link_color': _.partial(this.changeColorField, 'styles.link.fontColor'),
|
||||
'change #mailpoet_field_footer_link_underline': function(event) {
|
||||
this.model.set('styles.link.textDecoration', (event.target.checked) ? event.target.value : 'none');
|
||||
},
|
||||
'change #mailpoet_field_footer_link_underline': function linkUnderline(event) {
|
||||
this.model.set('styles.link.textDecoration', (event.target.checked) ? event.target.value : 'none');
|
||||
},
|
||||
'change .mailpoet_field_footer_background_color': _.partial(this.changeColorField, 'styles.block.backgroundColor'),
|
||||
'change .mailpoet_field_footer_alignment': _.partial(this.changeField, 'styles.text.textAlign'),
|
||||
'click .mailpoet_done_editing': 'close'
|
||||
};
|
||||
},
|
||||
templateContext: function() {
|
||||
templateContext: function templateContext() {
|
||||
return _.extend({}, base.BlockView.prototype.templateContext.apply(this, arguments), {
|
||||
availableStyles: App.getAvailableStyles().toJSON()
|
||||
});
|
||||
@ -98,24 +101,24 @@ define([
|
||||
});
|
||||
|
||||
Module.FooterWidgetView = base.WidgetView.extend({
|
||||
getTemplate: function() { return templates.footerInsertion; },
|
||||
getTemplate: function getTemplate() { return window.templates.footerInsertion; },
|
||||
behaviors: {
|
||||
DraggableBehavior: {
|
||||
cloneOriginal: true,
|
||||
drop: function() {
|
||||
drop: function drop() {
|
||||
return new Module.FooterBlockModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function(App, options) {
|
||||
App.registerBlockType('footer', {
|
||||
App.on('before:start', function beforeAppStart(BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('footer', {
|
||||
blockModel: Module.FooterBlockModel,
|
||||
blockView: Module.FooterBlockView
|
||||
});
|
||||
|
||||
App.registerWidget({
|
||||
BeforeStartApp.registerWidget({
|
||||
name: 'footer',
|
||||
widgetView: Module.FooterWidgetView,
|
||||
priority: 99
|
||||
|
@ -1,19 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Header content block
|
||||
*/
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base',
|
||||
'underscore'
|
||||
], function(App, BaseBlock, _) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock;
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base',
|
||||
'underscore',
|
||||
'mailpoet'
|
||||
], function headerBlock(App, BaseBlock, _, MailPoet) {
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
|
||||
Module.HeaderBlockModel = base.BlockModel.extend({
|
||||
defaults: function() {
|
||||
defaults: function defaults() {
|
||||
return this._getDefaults({
|
||||
type: 'header',
|
||||
text: 'Display problems? <a href="[link:newsletter_view_in_browser_url]">View it in your browser</a>',
|
||||
@ -33,18 +33,21 @@ define([
|
||||
}
|
||||
}
|
||||
}, App.getConfig().get('blockDefaults.header'));
|
||||
},
|
||||
_updateDefaults: function updateDefaults() {
|
||||
App.getConfig().set('blockDefaults.header', _.omit(this.toJSON(), 'text'));
|
||||
}
|
||||
});
|
||||
|
||||
Module.HeaderBlockView = base.BlockView.extend({
|
||||
className: 'mailpoet_block mailpoet_header_block mailpoet_droppable_block',
|
||||
getTemplate: function() { return templates.headerBlock; },
|
||||
getTemplate: function getTemplate() { return window.templates.headerBlock; },
|
||||
modelEvents: _.extend({
|
||||
'change:styles.block.backgroundColor change:styles.text.fontColor change:styles.text.fontFamily change:styles.text.fontSize change:styles.text.textAlign change:styles.link.fontColor change:styles.link.textDecoration': 'render'
|
||||
}, _.omit(base.BlockView.prototype.modelEvents, 'change')),
|
||||
behaviors: _.extend({}, base.BlockView.prototype.behaviors, {
|
||||
TextEditorBehavior: {
|
||||
configurationFilter: function(originalSettings) {
|
||||
configurationFilter: function configurationFilter(originalSettings) {
|
||||
return _.extend({}, originalSettings, {
|
||||
mailpoet_shortcodes: App.getConfig().get('shortcodes').toJSON(),
|
||||
mailpoet_shortcodes_window_title: MailPoet.I18n.t('shortcodesWindowTitle')
|
||||
@ -52,37 +55,37 @@ define([
|
||||
}
|
||||
}
|
||||
}),
|
||||
onDragSubstituteBy: function() { return Module.HeaderWidgetView; },
|
||||
onRender: function() {
|
||||
onDragSubstituteBy: function onDragSubstituteBy() { return Module.HeaderWidgetView; },
|
||||
onRender: function onRender() {
|
||||
this.toolsView = new Module.HeaderBlockToolsView({ model: this.model });
|
||||
this.showChildView('toolsRegion', this.toolsView);
|
||||
},
|
||||
onTextEditorChange: function(newContent) {
|
||||
onTextEditorChange: function onTextEditorChange(newContent) {
|
||||
this.model.set('text', newContent);
|
||||
},
|
||||
onTextEditorFocus: function() {
|
||||
onTextEditorFocus: function onTextEditorFocus() {
|
||||
this.disableDragging();
|
||||
this.disableShowingTools();
|
||||
},
|
||||
onTextEditorBlur: function() {
|
||||
onTextEditorBlur: function onTextEditorBlur() {
|
||||
this.enableDragging();
|
||||
this.enableShowingTools();
|
||||
}
|
||||
});
|
||||
|
||||
Module.HeaderBlockToolsView = base.BlockToolsView.extend({
|
||||
getSettingsView: function() { return Module.HeaderBlockSettingsView; }
|
||||
getSettingsView: function getSettingsView() { return Module.HeaderBlockSettingsView; }
|
||||
});
|
||||
|
||||
Module.HeaderBlockSettingsView = base.BlockSettingsView.extend({
|
||||
getTemplate: function() { return templates.headerBlockSettings; },
|
||||
events: function() {
|
||||
getTemplate: function getTemplate() { return window.templates.headerBlockSettings; },
|
||||
events: function events() {
|
||||
return {
|
||||
'change .mailpoet_field_header_text_color': _.partial(this.changeColorField, 'styles.text.fontColor'),
|
||||
'change .mailpoet_field_header_text_font_family': _.partial(this.changeField, 'styles.text.fontFamily'),
|
||||
'change .mailpoet_field_header_text_size': _.partial(this.changeField, 'styles.text.fontSize'),
|
||||
'change #mailpoet_field_header_link_color': _.partial(this.changeColorField, 'styles.link.fontColor'),
|
||||
'change #mailpoet_field_header_link_underline': function(event) {
|
||||
'change #mailpoet_field_header_link_underline': function linkUnderline(event) {
|
||||
this.model.set('styles.link.textDecoration', (event.target.checked) ? event.target.value : 'none');
|
||||
},
|
||||
'change .mailpoet_field_header_background_color': _.partial(this.changeColorField, 'styles.block.backgroundColor'),
|
||||
@ -90,7 +93,7 @@ define([
|
||||
'click .mailpoet_done_editing': 'close'
|
||||
};
|
||||
},
|
||||
templateContext: function() {
|
||||
templateContext: function templateContext() {
|
||||
return _.extend({}, base.BlockView.prototype.templateContext.apply(this, arguments), {
|
||||
availableStyles: App.getAvailableStyles().toJSON()
|
||||
});
|
||||
@ -98,24 +101,24 @@ define([
|
||||
});
|
||||
|
||||
Module.HeaderWidgetView = base.WidgetView.extend({
|
||||
getTemplate: function() { return templates.headerInsertion; },
|
||||
getTemplate: function getTemplate() { return window.templates.headerInsertion; },
|
||||
behaviors: {
|
||||
DraggableBehavior: {
|
||||
cloneOriginal: true,
|
||||
drop: function() {
|
||||
drop: function drop() {
|
||||
return new Module.HeaderBlockModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function(App, options) {
|
||||
App.registerBlockType('header', {
|
||||
App.on('before:start', function beforeAppStart(BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('header', {
|
||||
blockModel: Module.HeaderBlockModel,
|
||||
blockView: Module.HeaderBlockView
|
||||
});
|
||||
|
||||
App.registerWidget({
|
||||
BeforeStartApp.registerWidget({
|
||||
name: 'header',
|
||||
widgetView: Module.HeaderWidgetView,
|
||||
priority: 98
|
||||
|
@ -1,21 +1,22 @@
|
||||
/* eslint-disable func-names */
|
||||
/**
|
||||
* Image content block
|
||||
*/
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base',
|
||||
'underscore',
|
||||
'mailpoet'
|
||||
], function(App, BaseBlock, _, MailPoet) {
|
||||
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base',
|
||||
'underscore',
|
||||
'mailpoet',
|
||||
'jquery'
|
||||
], function (App, BaseBlock, _, MailPoet, jQuery) {
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock,
|
||||
ImageWidgetView;
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
var ImageWidgetView;
|
||||
|
||||
Module.ImageBlockModel = base.BlockModel.extend({
|
||||
defaults: function() {
|
||||
defaults: function () {
|
||||
return this._getDefaults({
|
||||
type: 'image',
|
||||
link: '',
|
||||
@ -30,39 +31,51 @@ define([
|
||||
}
|
||||
}
|
||||
}, App.getConfig().get('blockDefaults.image'));
|
||||
}
|
||||
},
|
||||
_updateDefaults: function () {}
|
||||
});
|
||||
|
||||
Module.ImageBlockView = base.BlockView.extend({
|
||||
className: 'mailpoet_block mailpoet_image_block mailpoet_droppable_block',
|
||||
getTemplate: function() { return templates.imageBlock; },
|
||||
onDragSubstituteBy: function() { return Module.ImageWidgetView; },
|
||||
templateContext: function() {
|
||||
getTemplate: function () { return window.templates.imageBlock; },
|
||||
onDragSubstituteBy: function () { return Module.ImageWidgetView; },
|
||||
templateContext: function () {
|
||||
return _.extend({
|
||||
imageMissingSrc: App.getConfig().get('urls.imageMissing')
|
||||
}, base.BlockView.prototype.templateContext.apply(this));
|
||||
},
|
||||
behaviors: _.extend({}, base.BlockView.prototype.behaviors, {
|
||||
ShowSettingsBehavior: {}
|
||||
ResizableBehavior: {
|
||||
elementSelector: '.mailpoet_image',
|
||||
resizeHandleSelector: '.mailpoet_image_resize_handle',
|
||||
onResize: function (event) {
|
||||
var corner = this.$('.mailpoet_image').offset();
|
||||
var width = event.pageX - corner.left;
|
||||
this.view.model.set('width', width + 'px');
|
||||
}
|
||||
},
|
||||
ShowSettingsBehavior: {
|
||||
ignoreFrom: '.mailpoet_image_resize_handle'
|
||||
}
|
||||
}),
|
||||
onRender: function() {
|
||||
onRender: function () {
|
||||
this.toolsView = new Module.ImageBlockToolsView({ model: this.model });
|
||||
this.showChildView('toolsRegion', this.toolsView);
|
||||
|
||||
if (this.model.get('fullWidth')) {
|
||||
this.$el.addClass('mailpoet_full_image');
|
||||
} else {
|
||||
this.$el.removeClass('mailpoet_full_image');
|
||||
}
|
||||
this.$('.mailpoet_content').css('width', this.model.get('width'));
|
||||
}
|
||||
});
|
||||
|
||||
Module.ImageBlockToolsView = base.BlockToolsView.extend({
|
||||
getSettingsView: function() { return Module.ImageBlockSettingsView; }
|
||||
getSettingsView: function () { return Module.ImageBlockSettingsView; }
|
||||
});
|
||||
|
||||
Module.ImageBlockSettingsView = base.BlockSettingsView.extend({
|
||||
onRender: function() {
|
||||
onRender: function () {
|
||||
MailPoet.helpTooltip.show(document.getElementById('tooltip-designer-full-width'), {
|
||||
tooltipId: 'tooltip-editor-full-width',
|
||||
tooltip: MailPoet.I18n.t('helpTooltipDesignerFullWidth')
|
||||
@ -72,8 +85,8 @@ define([
|
||||
tooltip: MailPoet.I18n.t('helpTooltipDesignerIdealWidth')
|
||||
});
|
||||
},
|
||||
getTemplate: function() { return templates.imageBlockSettings; },
|
||||
events: function() {
|
||||
getTemplate: function () { return window.templates.imageBlockSettings; },
|
||||
events: function () {
|
||||
return {
|
||||
'input .mailpoet_field_image_link': _.partial(this.changeField, 'link'),
|
||||
'input .mailpoet_field_image_address': 'changeAddress',
|
||||
@ -81,27 +94,53 @@ define([
|
||||
'change .mailpoet_field_image_full_width': _.partial(this.changeBoolCheckboxField, 'fullWidth'),
|
||||
'change .mailpoet_field_image_alignment': _.partial(this.changeField, 'styles.block.textAlign'),
|
||||
'click .mailpoet_field_image_select_another_image': 'showMediaManager',
|
||||
'click .mailpoet_done_editing': 'close'
|
||||
'click .mailpoet_done_editing': 'close',
|
||||
'input .mailpoet_field_image_width': _.partial(this.updateValueAndCall, '.mailpoet_field_image_width_input', _.partial(this.changePixelField, 'width').bind(this)),
|
||||
'change .mailpoet_field_image_width': _.partial(this.updateValueAndCall, '.mailpoet_field_image_width_input', _.partial(this.changePixelField, 'width').bind(this)),
|
||||
'input .mailpoet_field_image_width_input': _.partial(this.updateValueAndCall, '.mailpoet_field_image_width', _.partial(this.changePixelField, 'width').bind(this))
|
||||
};
|
||||
},
|
||||
initialize: function(options) {
|
||||
modelEvents: function () {
|
||||
return {
|
||||
'change:maxWidth': 'updateMaxWidth',
|
||||
'change:width': 'updateWidth'
|
||||
};
|
||||
},
|
||||
updateValueAndCall: function (fieldToUpdate, callable, event) {
|
||||
this.$(fieldToUpdate).val(jQuery(event.target).val());
|
||||
callable(event);
|
||||
},
|
||||
updateMaxWidth: function () {
|
||||
var maxWidth = parseInt(this.model.get('maxWidth'), 10);
|
||||
this.$('.mailpoet_field_image_width').attr('max', maxWidth);
|
||||
this.$('.mailpoet_field_image_width_input').attr('max', maxWidth);
|
||||
},
|
||||
updateWidth: function () {
|
||||
var width = parseInt(this.model.get('width'), 10);
|
||||
this.$('.mailpoet_field_image_width').val(width);
|
||||
this.$('.mailpoet_field_image_width_input').val(width);
|
||||
},
|
||||
initialize: function (options) {
|
||||
base.BlockSettingsView.prototype.initialize.apply(this, arguments);
|
||||
|
||||
if (options.showImageManager) {
|
||||
this.showMediaManager();
|
||||
}
|
||||
},
|
||||
showMediaManager: function() {
|
||||
showMediaManager: function () {
|
||||
var that = this;
|
||||
var MediaManager;
|
||||
var theFrame;
|
||||
if (this._mediaManager) {
|
||||
this._mediaManager.resetSelections();
|
||||
this._mediaManager.open();
|
||||
return;
|
||||
}
|
||||
|
||||
var MediaManager = wp.media.view.MediaFrame.Select.extend({
|
||||
MediaManager = window.wp.media.view.MediaFrame.Select.extend({
|
||||
|
||||
initialize: function() {
|
||||
wp.media.view.MediaFrame.prototype.initialize.apply(this, arguments);
|
||||
initialize: function () {
|
||||
window.wp.media.view.MediaFrame.prototype.initialize.apply(this, arguments);
|
||||
|
||||
_.defaults(this.options, {
|
||||
multiple: true,
|
||||
@ -118,22 +157,22 @@ define([
|
||||
this.$el.addClass('hide-title');
|
||||
},
|
||||
|
||||
resetSelections: function() {
|
||||
resetSelections: function () {
|
||||
this.state().get('selection').reset();
|
||||
},
|
||||
|
||||
createQuery: function(options) {
|
||||
var query = wp.media.query(options);
|
||||
createQuery: function (options) {
|
||||
var query = window.wp.media.query(options);
|
||||
return query;
|
||||
},
|
||||
|
||||
createStates: function() {
|
||||
createStates: function () {
|
||||
var options = this.options;
|
||||
|
||||
// Add the default states.
|
||||
this.states.add([
|
||||
// Main states.
|
||||
new wp.media.controller.Library({
|
||||
new window.wp.media.controller.Library({
|
||||
id: 'insert',
|
||||
title: 'Add images',
|
||||
priority: 20,
|
||||
@ -155,12 +194,13 @@ define([
|
||||
})
|
||||
]);
|
||||
|
||||
if(wp.media.view.settings.post.featuredImageId) {
|
||||
this.states.add(new wp.media.controller.FeaturedImage());
|
||||
if (window.wp.media.view.settings.post.featuredImageId) {
|
||||
this.states.add(new window.wp.media.controller.FeaturedImage());
|
||||
}
|
||||
},
|
||||
|
||||
bindHandlers: function() {
|
||||
bindHandlers: function () {
|
||||
var handlers;
|
||||
// from Select
|
||||
this.on('router:create:browse', this.createRouter, this);
|
||||
this.on('router:render:browse', this.browseRouter, this);
|
||||
@ -175,31 +215,31 @@ define([
|
||||
|
||||
this.on('updateExcluded', this.browseContent, this);
|
||||
|
||||
var handlers = {
|
||||
content: {
|
||||
embed: 'embedContent',
|
||||
'edit-selection': 'editSelectionContent'
|
||||
},
|
||||
toolbar: {
|
||||
'main-insert': 'mainInsertToolbar'
|
||||
}
|
||||
};
|
||||
handlers = {
|
||||
content: {
|
||||
embed: 'embedContent',
|
||||
'edit-selection': 'editSelectionContent'
|
||||
},
|
||||
toolbar: {
|
||||
'main-insert': 'mainInsertToolbar'
|
||||
}
|
||||
};
|
||||
|
||||
_.each(handlers, function(regionHandlers, region) {
|
||||
_.each(regionHandlers, function(callback, handler) {
|
||||
_.each(handlers, function (regionHandlers, region) {
|
||||
_.each(regionHandlers, function (callback, handler) {
|
||||
this.on(region + ':render:' + handler, this[callback], this);
|
||||
}, this);
|
||||
}, this);
|
||||
},
|
||||
|
||||
uploadContent: function() {
|
||||
wp.media.view.MediaFrame.Select.prototype.uploadContent.apply(this, arguments);
|
||||
uploadContent: function () {
|
||||
window.wp.media.view.MediaFrame.Select.prototype.uploadContent.apply(this, arguments);
|
||||
this.$el.addClass('hide-toolbar');
|
||||
},
|
||||
|
||||
// Content
|
||||
embedContent: function() {
|
||||
var view = new wp.media.view.Embed({
|
||||
embedContent: function () {
|
||||
var view = new window.wp.media.view.Embed({
|
||||
controller: this,
|
||||
model: this.state()
|
||||
}).render();
|
||||
@ -208,12 +248,12 @@ define([
|
||||
view.url.focus();
|
||||
},
|
||||
|
||||
editSelectionContent: function() {
|
||||
var state = this.state(),
|
||||
selection = state.get('selection'),
|
||||
view;
|
||||
editSelectionContent: function () {
|
||||
var state = this.state();
|
||||
var selection = state.get('selection');
|
||||
var view;
|
||||
|
||||
view = new wp.media.view.AttachmentsBrowser({
|
||||
view = new window.wp.media.view.AttachmentsBrowser({
|
||||
controller: this,
|
||||
collection: selection,
|
||||
selection: selection,
|
||||
@ -222,14 +262,14 @@ define([
|
||||
search: false,
|
||||
dragInfo: true,
|
||||
|
||||
AttachmentView: wp.media.view.Attachment.EditSelection
|
||||
AttachmentView: window.wp.media.view.Attachment.EditSelection
|
||||
}).render();
|
||||
|
||||
view.toolbar.set('backToLibrary', {
|
||||
text: 'Return to library',
|
||||
priority: -100,
|
||||
|
||||
click: function() {
|
||||
click: function () {
|
||||
this.controller.content.mode('browse');
|
||||
}
|
||||
});
|
||||
@ -239,23 +279,23 @@ define([
|
||||
},
|
||||
|
||||
// Toolbars
|
||||
selectionStatusToolbar: function(view) {
|
||||
selectionStatusToolbar: function (view) {
|
||||
var editable = this.state().get('editable');
|
||||
|
||||
view.set('selection', new wp.media.view.Selection({
|
||||
view.set('selection', new window.wp.media.view.Selection({
|
||||
controller: this,
|
||||
collection: this.state().get('selection'),
|
||||
priority: -40,
|
||||
|
||||
// If the selection is editable, pass the callback to
|
||||
// switch the content mode.
|
||||
editable: editable && function() {
|
||||
editable: editable && function () {
|
||||
this.controller.content.mode('edit-selection');
|
||||
}
|
||||
}).render() );
|
||||
}).render());
|
||||
},
|
||||
|
||||
mainInsertToolbar: function(view) {
|
||||
mainInsertToolbar: function (view) {
|
||||
var controller = this;
|
||||
|
||||
this.selectionStatusToolbar(view);
|
||||
@ -266,9 +306,9 @@ define([
|
||||
text: 'Select Image',
|
||||
requires: { selection: true },
|
||||
|
||||
click: function() {
|
||||
var state = controller.state(),
|
||||
selection = state.get('selection');
|
||||
click: function () {
|
||||
var state = controller.state();
|
||||
var selection = state.get('selection');
|
||||
|
||||
controller.close();
|
||||
state.trigger('insert', selection).reset();
|
||||
@ -276,9 +316,9 @@ define([
|
||||
});
|
||||
},
|
||||
|
||||
mainEmbedToolbar: function(toolbar) {
|
||||
mainEmbedToolbar: function (toolbar) {
|
||||
var tbar = toolbar;
|
||||
tbar.view = new wp.media.view.Toolbar.Embed({
|
||||
tbar.view = new window.wp.media.view.Toolbar.Embed({
|
||||
controller: this,
|
||||
text: 'Add images'
|
||||
});
|
||||
@ -286,46 +326,45 @@ define([
|
||||
|
||||
});
|
||||
|
||||
var theFrame = new MediaManager({
|
||||
id: 'mailpoet-media-manager',
|
||||
frame: 'select',
|
||||
title: 'Select image',
|
||||
editing: false,
|
||||
multiple: false,
|
||||
library: {
|
||||
type: 'image'
|
||||
},
|
||||
displaySettings: false,
|
||||
button: {
|
||||
text: 'Select'
|
||||
}
|
||||
}),
|
||||
that = this;
|
||||
this._mediaManager = theFrame;
|
||||
theFrame = new MediaManager({
|
||||
id: 'mailpoet-media-manager',
|
||||
frame: 'select',
|
||||
title: 'Select image',
|
||||
editing: false,
|
||||
multiple: false,
|
||||
library: {
|
||||
type: 'image'
|
||||
},
|
||||
displaySettings: false,
|
||||
button: {
|
||||
text: 'Select'
|
||||
}
|
||||
});
|
||||
this._mediaManager = theFrame;
|
||||
|
||||
this._mediaManager.on('insert', function() {
|
||||
this._mediaManager.on('insert', function () {
|
||||
// Append media manager image selections to Images tab
|
||||
var selection = theFrame.state().get('selection');
|
||||
selection.each(function(attachment) {
|
||||
var sizes = attachment.get('sizes'),
|
||||
// Following advice from Becs, the target width should
|
||||
// be a double of one column width to render well on
|
||||
// retina screen devices
|
||||
targetImageWidth = 1320,
|
||||
selection.each(function (attachment) {
|
||||
var sizes = attachment.get('sizes');
|
||||
// Following advice from Becs, the target width should
|
||||
// be a double of one column width to render well on
|
||||
// retina screen devices
|
||||
var targetImageWidth = 1320;
|
||||
|
||||
// For main image use the size, that's closest to being 660px in width
|
||||
sizeKeys = _.keys(sizes),
|
||||
// Pick the width that is closest to target width
|
||||
var increasingByWidthDifference = _.sortBy(
|
||||
_.keys(sizes),
|
||||
function (size) { return Math.abs(targetImageWidth - sizes[size].width); }
|
||||
);
|
||||
var bestWidth = sizes[_.first(increasingByWidthDifference)].width;
|
||||
var imagesOfBestWidth = _.filter(
|
||||
_.values(sizes),
|
||||
function (size) { return size.width === bestWidth; }
|
||||
);
|
||||
|
||||
// Pick the width that is closest to target width
|
||||
increasingByWidthDifference = _.sortBy(
|
||||
_.keys(sizes),
|
||||
function(size) { return Math.abs(targetImageWidth - sizes[size].width); }
|
||||
),
|
||||
bestWidth = sizes[_.first(increasingByWidthDifference)].width,
|
||||
imagesOfBestWidth = _.filter(_.values(sizes), function(size) { return size.width === bestWidth; }),
|
||||
|
||||
// Maximize the height if there are multiple images with same width
|
||||
mainSize = _.max(imagesOfBestWidth, function(size) { return size.height; });
|
||||
// Maximize the height if there are multiple images with same width
|
||||
var mainSize = _.max(imagesOfBestWidth, function (size) { return size.height; });
|
||||
|
||||
that.model.set({
|
||||
height: mainSize.height + 'px',
|
||||
@ -340,11 +379,11 @@ define([
|
||||
|
||||
this._mediaManager.open();
|
||||
},
|
||||
changeAddress: function(event) {
|
||||
changeAddress: function (event) {
|
||||
var src = jQuery(event.target).val();
|
||||
var image = new Image();
|
||||
|
||||
image.onload = function() {
|
||||
image.onload = function () {
|
||||
this.model.set({
|
||||
src: src,
|
||||
width: image.naturalWidth + 'px',
|
||||
@ -354,7 +393,7 @@ define([
|
||||
|
||||
image.src = src;
|
||||
},
|
||||
onBeforeDestroy: function() {
|
||||
onBeforeDestroy: function () {
|
||||
base.BlockSettingsView.prototype.onBeforeDestroy.apply(this, arguments);
|
||||
if (typeof this._mediaManager === 'object') {
|
||||
this._mediaManager.remove();
|
||||
@ -363,14 +402,14 @@ define([
|
||||
});
|
||||
|
||||
ImageWidgetView = base.WidgetView.extend({
|
||||
getTemplate: function() { return templates.imageInsertion; },
|
||||
getTemplate: function () { return window.templates.imageInsertion; },
|
||||
behaviors: {
|
||||
DraggableBehavior: {
|
||||
cloneOriginal: true,
|
||||
drop: function() {
|
||||
drop: function () {
|
||||
return new Module.ImageBlockModel();
|
||||
},
|
||||
onDrop: function(options) {
|
||||
onDrop: function (options) {
|
||||
options.droppedView.triggerMethod('showSettings', { showImageManager: true });
|
||||
}
|
||||
}
|
||||
@ -378,13 +417,13 @@ define([
|
||||
});
|
||||
Module.ImageWidgetView = ImageWidgetView;
|
||||
|
||||
App.on('before:start', function(App, options) {
|
||||
App.registerBlockType('image', {
|
||||
App.on('before:start', function (BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('image', {
|
||||
blockModel: Module.ImageBlockModel,
|
||||
blockView: Module.ImageBlockView
|
||||
});
|
||||
|
||||
App.registerWidget({
|
||||
BeforeStartApp.registerWidget({
|
||||
name: 'image',
|
||||
widgetView: Module.ImageWidgetView,
|
||||
priority: 91
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* eslint-disable func-names */
|
||||
/**
|
||||
* Posts block.
|
||||
*
|
||||
@ -11,40 +12,44 @@
|
||||
* block settings view.
|
||||
*/
|
||||
define([
|
||||
'backbone',
|
||||
'backbone.marionette',
|
||||
'backbone.radio',
|
||||
'underscore',
|
||||
'jquery',
|
||||
'mailpoet',
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/components/communication',
|
||||
'newsletter_editor/blocks/base',
|
||||
'newsletter_editor/blocks/button',
|
||||
'newsletter_editor/blocks/divider',
|
||||
'select2'
|
||||
], function(
|
||||
Backbone,
|
||||
Marionette,
|
||||
Radio,
|
||||
_,
|
||||
jQuery,
|
||||
MailPoet,
|
||||
App,
|
||||
CommunicationComponent,
|
||||
BaseBlock,
|
||||
ButtonBlock,
|
||||
DividerBlock
|
||||
'backbone',
|
||||
'backbone.marionette',
|
||||
'backbone.radio',
|
||||
'underscore',
|
||||
'jquery',
|
||||
'mailpoet',
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/components/communication',
|
||||
'newsletter_editor/blocks/base',
|
||||
'newsletter_editor/blocks/button',
|
||||
'newsletter_editor/blocks/divider',
|
||||
'select2'
|
||||
], function (
|
||||
Backbone,
|
||||
Marionette,
|
||||
Radio,
|
||||
_,
|
||||
jQuery,
|
||||
MailPoet,
|
||||
App,
|
||||
CommunicationComponent,
|
||||
BaseBlock,
|
||||
ButtonBlock,
|
||||
DividerBlock
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock;
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
var PostsDisplayOptionsSettingsView;
|
||||
var SinglePostSelectionSettingsView;
|
||||
var EmptyPostSelectionSettingsView;
|
||||
var PostSelectionSettingsView;
|
||||
var PostsSelectionCollectionView;
|
||||
|
||||
Module.PostsBlockModel = base.BlockModel.extend({
|
||||
stale: ['_selectedPosts', '_availablePosts', '_transformedPosts'],
|
||||
defaults: function() {
|
||||
defaults: function () {
|
||||
return this._getDefaults({
|
||||
type: 'posts',
|
||||
amount: '10',
|
||||
@ -60,7 +65,6 @@ define([
|
||||
titleIsLink: false, // false|true
|
||||
imageFullWidth: false, // true|false
|
||||
featuredImagePosition: 'belowTitle', // 'aboveTitle'|'belowTitle'|'none'
|
||||
//imageAlignment: 'centerPadded', // 'centerFull'|'centerPadded'|'left'|'right'|'alternate'|'none'
|
||||
showAuthor: 'no', // 'no'|'aboveText'|'belowText'
|
||||
authorPrecededBy: 'Author:',
|
||||
showCategories: 'no', // 'no'|'aboveText'|'belowText'
|
||||
@ -79,7 +83,7 @@ define([
|
||||
_transformedPosts: new (App.getBlockTypeModel('container'))()
|
||||
}, App.getConfig().get('blockDefaults.posts'));
|
||||
},
|
||||
relations: function() {
|
||||
relations: function () {
|
||||
return {
|
||||
readMoreButton: App.getBlockTypeModel('button'),
|
||||
divider: App.getBlockTypeModel('divider'),
|
||||
@ -88,16 +92,22 @@ define([
|
||||
_transformedPosts: App.getBlockTypeModel('container')
|
||||
};
|
||||
},
|
||||
initialize: function() {
|
||||
var that = this,
|
||||
POST_REFRESH_DELAY_MS = 500,
|
||||
refreshAvailablePosts = _.debounce(this.fetchAvailablePosts.bind(this), POST_REFRESH_DELAY_MS),
|
||||
refreshTransformedPosts = _.debounce(this._refreshTransformedPosts.bind(this), POST_REFRESH_DELAY_MS);
|
||||
initialize: function () {
|
||||
var POST_REFRESH_DELAY_MS = 500;
|
||||
var refreshAvailablePosts = _.debounce(
|
||||
this.fetchAvailablePosts.bind(this),
|
||||
POST_REFRESH_DELAY_MS
|
||||
);
|
||||
var refreshTransformedPosts = _.debounce(
|
||||
this._refreshTransformedPosts.bind(this),
|
||||
POST_REFRESH_DELAY_MS
|
||||
);
|
||||
|
||||
// Attach Radio.Requests API primarily for highlighting
|
||||
_.extend(this, Radio.Requests);
|
||||
|
||||
this.fetchAvailablePosts();
|
||||
this.on('change', this._updateDefaults, this);
|
||||
this.on('change:amount change:contentType change:terms change:inclusionType change:postStatus change:search change:sortBy', refreshAvailablePosts);
|
||||
this.on('loadMorePosts', this._loadMorePosts, this);
|
||||
|
||||
@ -108,41 +118,42 @@ define([
|
||||
|
||||
this.on('insertSelectedPosts', this._insertSelectedPosts, this);
|
||||
},
|
||||
fetchAvailablePosts: function() {
|
||||
fetchAvailablePosts: function () {
|
||||
var that = this;
|
||||
this.set('offset', 0);
|
||||
CommunicationComponent.getPosts(this.toJSON()).done(function(posts) {
|
||||
CommunicationComponent.getPosts(this.toJSON()).done(function (posts) {
|
||||
that.get('_availablePosts').reset(posts);
|
||||
that.get('_selectedPosts').reset(); // Empty out the collection
|
||||
that.trigger('change:_availablePosts');
|
||||
}).fail(function() {
|
||||
}).fail(function () {
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('failedToFetchAvailablePosts'));
|
||||
});
|
||||
},
|
||||
_loadMorePosts: function() {
|
||||
var that = this,
|
||||
postCount = this.get('_availablePosts').length,
|
||||
nextOffset = this.get('offset') + Number(this.get('amount'));
|
||||
_loadMorePosts: function () {
|
||||
var that = this;
|
||||
var postCount = this.get('_availablePosts').length;
|
||||
var nextOffset = this.get('offset') + Number(this.get('amount'));
|
||||
|
||||
if(postCount === 0 || postCount < nextOffset) {
|
||||
if (postCount === 0 || postCount < nextOffset) {
|
||||
// No more posts to load
|
||||
return false;
|
||||
}
|
||||
this.set('offset', nextOffset);
|
||||
this.trigger('loadingMorePosts');
|
||||
|
||||
CommunicationComponent.getPosts(this.toJSON()).done(function(posts) {
|
||||
CommunicationComponent.getPosts(this.toJSON()).done(function (posts) {
|
||||
that.get('_availablePosts').add(posts);
|
||||
that.trigger('change:_availablePosts');
|
||||
}).fail(function() {
|
||||
}).fail(function () {
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('failedToFetchAvailablePosts'));
|
||||
}).always(function() {
|
||||
}).always(function () {
|
||||
that.trigger('morePostsLoaded');
|
||||
});
|
||||
return true;
|
||||
},
|
||||
_refreshTransformedPosts: function() {
|
||||
var that = this,
|
||||
data = this.toJSON();
|
||||
_refreshTransformedPosts: function () {
|
||||
var that = this;
|
||||
var data = this.toJSON();
|
||||
|
||||
data.posts = this.get('_selectedPosts').pluck('ID');
|
||||
|
||||
@ -151,25 +162,24 @@ define([
|
||||
return;
|
||||
}
|
||||
|
||||
CommunicationComponent.getTransformedPosts(data).done(function(posts) {
|
||||
that.get('_transformedPosts').get('blocks').reset(posts, {parse: true});
|
||||
}).fail(function() {
|
||||
CommunicationComponent.getTransformedPosts(data).done(function (posts) {
|
||||
that.get('_transformedPosts').get('blocks').reset(posts, { parse: true });
|
||||
}).fail(function () {
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('failedToFetchRenderedPosts'));
|
||||
});
|
||||
},
|
||||
_insertSelectedPosts: function() {
|
||||
var that = this,
|
||||
data = this.toJSON(),
|
||||
index = this.collection.indexOf(this),
|
||||
collection = this.collection;
|
||||
_insertSelectedPosts: function () {
|
||||
var data = this.toJSON();
|
||||
var index = this.collection.indexOf(this);
|
||||
var collection = this.collection;
|
||||
|
||||
data.posts = this.get('_selectedPosts').pluck('ID');
|
||||
|
||||
if (data.posts.length === 0) return;
|
||||
|
||||
CommunicationComponent.getTransformedPosts(data).done(function(posts) {
|
||||
CommunicationComponent.getTransformedPosts(data).done(function (posts) {
|
||||
collection.add(posts, { at: index });
|
||||
}).fail(function() {
|
||||
}).fail(function () {
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('failedToFetchRenderedPosts'));
|
||||
});
|
||||
}
|
||||
@ -177,46 +187,48 @@ define([
|
||||
|
||||
Module.PostsBlockView = base.BlockView.extend({
|
||||
className: 'mailpoet_block mailpoet_posts_block mailpoet_droppable_block',
|
||||
getTemplate: function() { return templates.postsBlock; },
|
||||
getTemplate: function () { return window.templates.postsBlock; },
|
||||
modelEvents: {}, // Forcefully disable all events
|
||||
regions: _.extend({
|
||||
postsRegion: '.mailpoet_posts_block_posts'
|
||||
}, base.BlockView.prototype.regions),
|
||||
onDragSubstituteBy: function() { return Module.PostsWidgetView; },
|
||||
initialize: function() {
|
||||
onDragSubstituteBy: function () { return Module.PostsWidgetView; },
|
||||
initialize: function () {
|
||||
base.BlockView.prototype.initialize.apply(this, arguments);
|
||||
|
||||
this.toolsView = new Module.PostsBlockToolsView({ model: this.model });
|
||||
this.model.reply('blockView', this.notifyAboutSelf, this);
|
||||
},
|
||||
onRender: function() {
|
||||
onRender: function () {
|
||||
var ContainerView;
|
||||
var renderOptions;
|
||||
if (!this.getRegion('toolsRegion').hasView()) {
|
||||
this.showChildView('toolsRegion', this.toolsView);
|
||||
}
|
||||
this.trigger('showSettings');
|
||||
|
||||
var ContainerView = App.getBlockTypeView('container'),
|
||||
renderOptions = {
|
||||
disableTextEditor: true,
|
||||
disableDragAndDrop: true,
|
||||
emptyContainerMessage: MailPoet.I18n.t('noPostsToDisplay')
|
||||
};
|
||||
ContainerView = App.getBlockTypeView('container');
|
||||
renderOptions = {
|
||||
disableTextEditor: true,
|
||||
disableDragAndDrop: true,
|
||||
emptyContainerMessage: MailPoet.I18n.t('noPostsToDisplay')
|
||||
};
|
||||
this.showChildView('postsRegion', new ContainerView({ model: this.model.get('_transformedPosts'), renderOptions: renderOptions }));
|
||||
},
|
||||
notifyAboutSelf: function() {
|
||||
notifyAboutSelf: function () {
|
||||
return this;
|
||||
},
|
||||
onBeforeDestroy: function() {
|
||||
onBeforeDestroy: function () {
|
||||
this.model.stopReplying('blockView', this.notifyAboutSelf, this);
|
||||
}
|
||||
});
|
||||
|
||||
Module.PostsBlockToolsView = base.BlockToolsView.extend({
|
||||
getSettingsView: function() { return Module.PostsBlockSettingsView; }
|
||||
getSettingsView: function () { return Module.PostsBlockSettingsView; }
|
||||
});
|
||||
|
||||
Module.PostsBlockSettingsView = base.BlockSettingsView.extend({
|
||||
getTemplate: function() { return templates.postsBlockSettings; },
|
||||
getTemplate: function () { return window.templates.postsBlockSettings; },
|
||||
regions: {
|
||||
selectionRegion: '.mailpoet_settings_posts_selection',
|
||||
displayOptionsRegion: '.mailpoet_settings_posts_display_options'
|
||||
@ -226,19 +238,19 @@ define([
|
||||
'click .mailpoet_settings_posts_show_post_selection': 'switchToPostSelection',
|
||||
'click .mailpoet_settings_posts_insert_selected': 'insertPosts'
|
||||
},
|
||||
templateContext: function() {
|
||||
templateContext: function () {
|
||||
return {
|
||||
model: this.model.toJSON()
|
||||
};
|
||||
},
|
||||
initialize: function() {
|
||||
initialize: function () {
|
||||
this.model.trigger('startEditing');
|
||||
this.selectionView = new PostSelectionSettingsView({ model: this.model });
|
||||
this.displayOptionsView = new PostsDisplayOptionsSettingsView({ model: this.model });
|
||||
},
|
||||
onRender: function() {
|
||||
var that = this,
|
||||
blockView = this.model.request('blockView');
|
||||
onRender: function () {
|
||||
var that = this;
|
||||
this.model.request('blockView');
|
||||
|
||||
this.showChildView('selectionRegion', this.selectionView);
|
||||
this.showChildView('displayOptionsRegion', this.displayOptionsView);
|
||||
@ -248,7 +260,7 @@ define([
|
||||
template: '',
|
||||
position: 'right',
|
||||
width: App.getConfig().get('sidepanelWidth'),
|
||||
onCancel: function() {
|
||||
onCancel: function () {
|
||||
// Self destroy the block if the user closes settings modal
|
||||
that.model.destroy();
|
||||
}
|
||||
@ -258,7 +270,7 @@ define([
|
||||
this.selectionView.triggerMethod('attach');
|
||||
this.displayOptionsView.triggerMethod('attach');
|
||||
},
|
||||
switchToDisplayOptions: function() {
|
||||
switchToDisplayOptions: function () {
|
||||
// Switch content view
|
||||
this.$('.mailpoet_settings_posts_selection').addClass('mailpoet_closed');
|
||||
this.$('.mailpoet_settings_posts_display_options').removeClass('mailpoet_closed');
|
||||
@ -267,7 +279,7 @@ define([
|
||||
this.$('.mailpoet_settings_posts_show_display_options').addClass('mailpoet_hidden');
|
||||
this.$('.mailpoet_settings_posts_show_post_selection').removeClass('mailpoet_hidden');
|
||||
},
|
||||
switchToPostSelection: function() {
|
||||
switchToPostSelection: function () {
|
||||
// Switch content view
|
||||
this.$('.mailpoet_settings_posts_display_options').addClass('mailpoet_closed');
|
||||
this.$('.mailpoet_settings_posts_selection').removeClass('mailpoet_closed');
|
||||
@ -276,43 +288,43 @@ define([
|
||||
this.$('.mailpoet_settings_posts_show_post_selection').addClass('mailpoet_hidden');
|
||||
this.$('.mailpoet_settings_posts_show_display_options').removeClass('mailpoet_hidden');
|
||||
},
|
||||
insertPosts: function() {
|
||||
insertPosts: function () {
|
||||
this.model.trigger('insertSelectedPosts');
|
||||
this.model.destroy();
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
|
||||
var PostsSelectionCollectionView = Marionette.CollectionView.extend({
|
||||
PostsSelectionCollectionView = Marionette.CollectionView.extend({
|
||||
className: 'mailpoet_post_scroll_container',
|
||||
childView: function() { return SinglePostSelectionSettingsView; },
|
||||
emptyView: function() { return EmptyPostSelectionSettingsView; },
|
||||
childViewOptions: function() {
|
||||
childView: function () { return SinglePostSelectionSettingsView; },
|
||||
emptyView: function () { return EmptyPostSelectionSettingsView; },
|
||||
childViewOptions: function () {
|
||||
return {
|
||||
blockModel: this.blockModel
|
||||
};
|
||||
},
|
||||
initialize: function(options) {
|
||||
initialize: function (options) {
|
||||
this.blockModel = options.blockModel;
|
||||
},
|
||||
events: {
|
||||
scroll: 'onPostsScroll'
|
||||
},
|
||||
onPostsScroll: function(event) {
|
||||
onPostsScroll: function (event) {
|
||||
var $postsBox = jQuery(event.target);
|
||||
if($postsBox.scrollTop() + $postsBox.innerHeight() >= $postsBox[0].scrollHeight){
|
||||
if ($postsBox.scrollTop() + $postsBox.innerHeight() >= $postsBox[0].scrollHeight) {
|
||||
// Load more posts if scrolled to bottom
|
||||
this.blockModel.trigger('loadMorePosts');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var PostSelectionSettingsView = Marionette.View.extend({
|
||||
getTemplate: function() { return templates.postSelectionPostsBlockSettings; },
|
||||
PostSelectionSettingsView = Marionette.View.extend({
|
||||
getTemplate: function () { return window.templates.postSelectionPostsBlockSettings; },
|
||||
regions: {
|
||||
posts: '.mailpoet_post_selection_container'
|
||||
},
|
||||
events: function() {
|
||||
events: function () {
|
||||
return {
|
||||
'change .mailpoet_settings_posts_content_type': _.partial(this.changeField, 'contentType'),
|
||||
'change .mailpoet_posts_post_status': _.partial(this.changeField, 'postStatus'),
|
||||
@ -320,30 +332,36 @@ define([
|
||||
};
|
||||
},
|
||||
modelEvents: {
|
||||
'change:offset': function(model, value) {
|
||||
'change:offset': function (model, value) {
|
||||
// Scroll posts view to top if settings are changed
|
||||
if (value === 0) {
|
||||
this.$('.mailpoet_post_scroll_container').scrollTop(0);
|
||||
}
|
||||
},
|
||||
loadingMorePosts: function() {
|
||||
loadingMorePosts: function () {
|
||||
this.$('.mailpoet_post_selection_loading').css('visibility', 'visible');
|
||||
},
|
||||
morePostsLoaded: function() {
|
||||
morePostsLoaded: function () {
|
||||
this.$('.mailpoet_post_selection_loading').css('visibility', 'hidden');
|
||||
}
|
||||
},
|
||||
onRender: function() {
|
||||
templateContext: function () {
|
||||
return {
|
||||
model: this.model.toJSON()
|
||||
};
|
||||
},
|
||||
onRender: function () {
|
||||
var postsView;
|
||||
// Dynamically update available post types
|
||||
CommunicationComponent.getPostTypes().done(_.bind(this._updateContentTypes, this));
|
||||
var postsView = new PostsSelectionCollectionView({
|
||||
postsView = new PostsSelectionCollectionView({
|
||||
collection: this.model.get('_availablePosts'),
|
||||
blockModel: this.model
|
||||
});
|
||||
|
||||
this.showChildView('posts', postsView);
|
||||
},
|
||||
onAttach: function() {
|
||||
onAttach: function () {
|
||||
var that = this;
|
||||
|
||||
this.$('.mailpoet_posts_categories_and_tags').select2({
|
||||
@ -356,35 +374,36 @@ define([
|
||||
term: params.term
|
||||
};
|
||||
},
|
||||
transport: function(options, success, failure) {
|
||||
transport: function (options, success, failure) {
|
||||
var taxonomies;
|
||||
var termsPromise;
|
||||
var promise = CommunicationComponent.getTaxonomies(
|
||||
that.model.get('contentType')
|
||||
).then(function(tax) {
|
||||
).then(function (tax) {
|
||||
taxonomies = tax;
|
||||
// Fetch available terms based on the list of taxonomies already fetched
|
||||
var promise = CommunicationComponent.getTerms({
|
||||
termsPromise = CommunicationComponent.getTerms({
|
||||
search: options.data.term,
|
||||
taxonomies: _.keys(taxonomies)
|
||||
}).then(function(terms) {
|
||||
}).then(function (terms) {
|
||||
return {
|
||||
taxonomies: taxonomies,
|
||||
terms: terms
|
||||
};
|
||||
});
|
||||
return promise;
|
||||
return termsPromise;
|
||||
});
|
||||
|
||||
promise.then(success);
|
||||
promise.fail(failure);
|
||||
return promise;
|
||||
},
|
||||
processResults: function(data) {
|
||||
processResults: function (data) {
|
||||
// Transform taxonomies and terms into select2 compatible format
|
||||
return {
|
||||
results: _.map(
|
||||
data.terms,
|
||||
function(item) {
|
||||
function (item) {
|
||||
return _.defaults({
|
||||
text: data.taxonomies[item.taxonomy].labels.singular_name + ': ' + item.name,
|
||||
id: item.term_id
|
||||
@ -395,29 +414,29 @@ define([
|
||||
}
|
||||
}
|
||||
}).on({
|
||||
'select2:select': function(event) {
|
||||
'select2:select': function (event) {
|
||||
var terms = that.model.get('terms');
|
||||
terms.add(event.params.data);
|
||||
// Reset whole model in order for change events to propagate properly
|
||||
that.model.set('terms', terms.toJSON());
|
||||
},
|
||||
'select2:unselect': function(event) {
|
||||
'select2:unselect': function (event) {
|
||||
var terms = that.model.get('terms');
|
||||
terms.remove(event.params.data);
|
||||
// Reset whole model in order for change events to propagate properly
|
||||
that.model.set('terms', terms.toJSON());
|
||||
}
|
||||
}).trigger( 'change' );
|
||||
}).trigger('change');
|
||||
},
|
||||
changeField: function(field, event) {
|
||||
changeField: function (field, event) {
|
||||
this.model.set(field, jQuery(event.target).val());
|
||||
},
|
||||
_updateContentTypes: function(postTypes) {
|
||||
var select = this.$('.mailpoet_settings_posts_content_type'),
|
||||
selectedValue = this.model.get('contentType');
|
||||
_updateContentTypes: function (postTypes) {
|
||||
var select = this.$('.mailpoet_settings_posts_content_type');
|
||||
var selectedValue = this.model.get('contentType');
|
||||
|
||||
select.find('option').remove();
|
||||
_.each(postTypes, function(type) {
|
||||
_.each(postTypes, function (type) {
|
||||
select.append(jQuery('<option>', {
|
||||
value: type.name,
|
||||
text: type.label
|
||||
@ -427,29 +446,29 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
var EmptyPostSelectionSettingsView = Marionette.View.extend({
|
||||
getTemplate: function() { return templates.emptyPostPostsBlockSettings; }
|
||||
EmptyPostSelectionSettingsView = Marionette.View.extend({
|
||||
getTemplate: function () { return window.templates.emptyPostPostsBlockSettings; }
|
||||
});
|
||||
|
||||
var SinglePostSelectionSettingsView = Marionette.View.extend({
|
||||
getTemplate: function() { return templates.singlePostPostsBlockSettings; },
|
||||
events: function() {
|
||||
SinglePostSelectionSettingsView = Marionette.View.extend({
|
||||
getTemplate: function () { return window.templates.singlePostPostsBlockSettings; },
|
||||
events: function () {
|
||||
return {
|
||||
'change .mailpoet_select_post_checkbox': 'postSelectionChange'
|
||||
};
|
||||
},
|
||||
templateContext: function() {
|
||||
templateContext: function () {
|
||||
return {
|
||||
model: this.model.toJSON(),
|
||||
index: this._index
|
||||
};
|
||||
},
|
||||
initialize: function(options) {
|
||||
initialize: function (options) {
|
||||
this.blockModel = options.blockModel;
|
||||
},
|
||||
postSelectionChange: function(event) {
|
||||
var checkBox = jQuery(event.target),
|
||||
selectedPostsCollection = this.blockModel.get('_selectedPosts');
|
||||
postSelectionChange: function (event) {
|
||||
var checkBox = jQuery(event.target);
|
||||
var selectedPostsCollection = this.blockModel.get('_selectedPosts');
|
||||
if (checkBox.prop('checked')) {
|
||||
selectedPostsCollection.add(this.model);
|
||||
} else {
|
||||
@ -458,9 +477,9 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
var PostsDisplayOptionsSettingsView = base.BlockSettingsView.extend({
|
||||
getTemplate: function() { return templates.displayOptionsPostsBlockSettings; },
|
||||
events: function() {
|
||||
PostsDisplayOptionsSettingsView = base.BlockSettingsView.extend({
|
||||
getTemplate: function () { return window.templates.displayOptionsPostsBlockSettings; },
|
||||
events: function () {
|
||||
return {
|
||||
'click .mailpoet_posts_select_button': 'showButtonSettings',
|
||||
'click .mailpoet_posts_select_divider': 'showDividerSettings',
|
||||
@ -483,12 +502,12 @@ define([
|
||||
'change .mailpoet_posts_sort_by': _.partial(this.changeField, 'sortBy')
|
||||
};
|
||||
},
|
||||
templateContext: function() {
|
||||
templateContext: function () {
|
||||
return {
|
||||
model: this.model.toJSON()
|
||||
};
|
||||
},
|
||||
showButtonSettings: function(event) {
|
||||
showButtonSettings: function () {
|
||||
var buttonModule = ButtonBlock;
|
||||
(new buttonModule.ButtonBlockSettingsView({
|
||||
model: this.model.get('readMoreButton'),
|
||||
@ -499,7 +518,7 @@ define([
|
||||
}
|
||||
})).render();
|
||||
},
|
||||
showDividerSettings: function(event) {
|
||||
showDividerSettings: function () {
|
||||
var dividerModule = DividerBlock;
|
||||
(new dividerModule.DividerBlockSettingsView({
|
||||
model: this.model.get('divider'),
|
||||
@ -509,20 +528,20 @@ define([
|
||||
}
|
||||
})).render();
|
||||
},
|
||||
changeReadMoreType: function(event) {
|
||||
changeReadMoreType: function (event) {
|
||||
var value = jQuery(event.target).val();
|
||||
if (value == 'link') {
|
||||
if (value === 'link') {
|
||||
this.$('.mailpoet_posts_read_more_text').removeClass('mailpoet_hidden');
|
||||
this.$('.mailpoet_posts_select_button').addClass('mailpoet_hidden');
|
||||
} else if (value == 'button') {
|
||||
} else if (value === 'button') {
|
||||
this.$('.mailpoet_posts_read_more_text').addClass('mailpoet_hidden');
|
||||
this.$('.mailpoet_posts_select_button').removeClass('mailpoet_hidden');
|
||||
}
|
||||
this.changeField('readMoreType', event);
|
||||
},
|
||||
changeDisplayType: function(event) {
|
||||
changeDisplayType: function (event) {
|
||||
var value = jQuery(event.target).val();
|
||||
if (value == 'titleOnly') {
|
||||
if (value === 'titleOnly') {
|
||||
this.$('.mailpoet_posts_title_as_list').removeClass('mailpoet_hidden');
|
||||
this.$('.mailpoet_posts_image_full_width_option').addClass('mailpoet_hidden');
|
||||
this.$('.mailpoet_posts_image_separator').addClass('mailpoet_hidden');
|
||||
@ -547,9 +566,9 @@ define([
|
||||
|
||||
this.changeField('displayType', event);
|
||||
},
|
||||
changeTitleFormat: function(event) {
|
||||
changeTitleFormat: function (event) {
|
||||
var value = jQuery(event.target).val();
|
||||
if (value == 'ul') {
|
||||
if (value === 'ul') {
|
||||
this.$('.mailpoet_posts_non_title_list_options').addClass('mailpoet_hidden');
|
||||
|
||||
this.model.set('titleIsLink', true);
|
||||
@ -564,24 +583,24 @@ define([
|
||||
});
|
||||
|
||||
Module.PostsWidgetView = base.WidgetView.extend({
|
||||
getTemplate: function() { return templates.postsInsertion; },
|
||||
getTemplate: function () { return window.templates.postsInsertion; },
|
||||
behaviors: {
|
||||
DraggableBehavior: {
|
||||
cloneOriginal: true,
|
||||
drop: function() {
|
||||
drop: function () {
|
||||
return new Module.PostsBlockModel({}, { parse: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function(App, options) {
|
||||
App.registerBlockType('posts', {
|
||||
App.on('before:start', function (BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('posts', {
|
||||
blockModel: Module.PostsBlockModel,
|
||||
blockView: Module.PostsBlockView
|
||||
});
|
||||
|
||||
App.registerWidget({
|
||||
BeforeStartApp.registerWidget({
|
||||
name: 'posts',
|
||||
widgetView: Module.PostsWidgetView,
|
||||
priority: 96
|
||||
|
@ -1,27 +1,28 @@
|
||||
/* eslint-disable func-names */
|
||||
/**
|
||||
* Social icons content block
|
||||
*/
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base',
|
||||
'backbone',
|
||||
'backbone.marionette',
|
||||
'backbone.supermodel',
|
||||
'underscore',
|
||||
'jquery'
|
||||
], function(App, BaseBlock, Backbone, Marionette, SuperModel, _, jQuery) {
|
||||
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base',
|
||||
'backbone',
|
||||
'backbone.marionette',
|
||||
'backbone.supermodel',
|
||||
'underscore',
|
||||
'jquery'
|
||||
], function (App, BaseBlock, Backbone, Marionette, SuperModel, _, jQuery) {
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock,
|
||||
SocialBlockSettingsIconSelectorView,
|
||||
SocialBlockSettingsIconView,
|
||||
SocialBlockSettingsIconCollectionView,
|
||||
SocialBlockSettingsStylesView;
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
var SocialBlockSettingsIconSelectorView;
|
||||
var SocialBlockSettingsIconView;
|
||||
var SocialBlockSettingsIconCollectionView;
|
||||
var SocialBlockSettingsStylesView;
|
||||
var SocialIconView;
|
||||
|
||||
Module.SocialIconModel = SuperModel.extend({
|
||||
defaults: function() {
|
||||
defaults: function () {
|
||||
var defaultValues = App.getConfig().get('socialIcons.custom');
|
||||
return {
|
||||
type: 'socialIcon',
|
||||
@ -33,19 +34,19 @@ define([
|
||||
text: defaultValues.get('title')
|
||||
};
|
||||
},
|
||||
initialize: function(options) {
|
||||
initialize: function () {
|
||||
var that = this;
|
||||
// Make model swap to default values for that type when iconType changes
|
||||
this.on('change:iconType', function() {
|
||||
var defaultValues = App.getConfig().get('socialIcons').get(that.get('iconType')),
|
||||
iconSet = that.collection.iconBlockModel.getIconSet();
|
||||
this.on('change:iconType', function () {
|
||||
var defaultValues = App.getConfig().get('socialIcons').get(that.get('iconType'));
|
||||
var iconSet = that.collection.iconBlockModel.getIconSet();
|
||||
this.set({
|
||||
link: defaultValues.get('defaultLink'),
|
||||
image: iconSet.get(that.get('iconType')),
|
||||
text: defaultValues.get('title')
|
||||
});
|
||||
}, this);
|
||||
this.on('change', function() { App.getChannel().trigger('autoSave'); });
|
||||
this.on('change', function () { App.getChannel().trigger('autoSave'); });
|
||||
}
|
||||
});
|
||||
|
||||
@ -55,7 +56,7 @@ define([
|
||||
|
||||
Module.SocialBlockModel = base.BlockModel.extend({
|
||||
name: 'iconBlockModel',
|
||||
defaults: function() {
|
||||
defaults: function () {
|
||||
return this._getDefaults({
|
||||
type: 'social',
|
||||
iconSet: 'default',
|
||||
@ -65,31 +66,33 @@ define([
|
||||
relations: {
|
||||
icons: Module.SocialIconCollectionModel
|
||||
},
|
||||
initialize: function() {
|
||||
initialize: function () {
|
||||
this.get('icons').on('add remove change', this._iconsChanged, this);
|
||||
this.on('change:iconSet', this.changeIconSet, this);
|
||||
this.on('change', this._updateDefaults, this);
|
||||
},
|
||||
getIconSet: function() {
|
||||
getIconSet: function () {
|
||||
return App.getAvailableStyles().get('socialIconSets').get(this.get('iconSet'));
|
||||
},
|
||||
changeIconSet: function() {
|
||||
changeIconSet: function () {
|
||||
var iconSet = this.getIconSet();
|
||||
_.each(this.get('icons').models, function(model) {
|
||||
_.each(this.get('icons').models, function (model) {
|
||||
model.set('image', iconSet.get(model.get('iconType')));
|
||||
});
|
||||
},
|
||||
_iconsChanged: function() {
|
||||
App.getChannel().trigger('autoSave');
|
||||
_iconsChanged: function () {
|
||||
this._updateDefaults();
|
||||
App.getChannel().trigger('autoSave');
|
||||
}
|
||||
});
|
||||
|
||||
var SocialIconView = Marionette.View.extend({
|
||||
SocialIconView = Marionette.View.extend({
|
||||
tagName: 'span',
|
||||
getTemplate: function() { return templates.socialIconBlock; },
|
||||
getTemplate: function () { return window.templates.socialIconBlock; },
|
||||
modelEvents: {
|
||||
change: 'render'
|
||||
},
|
||||
templateContext: function() {
|
||||
templateContext: function () {
|
||||
var allIconSets = App.getAvailableStyles().get('socialIconSets');
|
||||
return {
|
||||
model: this.model.toJSON(),
|
||||
@ -105,7 +108,7 @@ define([
|
||||
|
||||
Module.SocialBlockView = base.BlockView.extend({
|
||||
className: 'mailpoet_block mailpoet_social_block mailpoet_droppable_block',
|
||||
getTemplate: function() { return templates.socialBlock; },
|
||||
getTemplate: function () { return window.templates.socialBlock; },
|
||||
regions: _.extend({}, base.BlockView.prototype.regions, {
|
||||
icons: '.mailpoet_social'
|
||||
}),
|
||||
@ -115,39 +118,39 @@ define([
|
||||
behaviors: _.extend({}, base.BlockView.prototype.behaviors, {
|
||||
ShowSettingsBehavior: {}
|
||||
}),
|
||||
onDragSubstituteBy: function() { return Module.SocialWidgetView; },
|
||||
onRender: function() {
|
||||
onDragSubstituteBy: function () { return Module.SocialWidgetView; },
|
||||
onRender: function () {
|
||||
this.toolsView = new Module.SocialBlockToolsView({ model: this.model });
|
||||
this.showChildView('toolsRegion', this.toolsView);
|
||||
this.showChildView('icons', new Module.SocialIconCollectionView({
|
||||
collection: this.model.get('icons')
|
||||
}))
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
Module.SocialBlockToolsView = base.BlockToolsView.extend({
|
||||
getSettingsView: function() { return Module.SocialBlockSettingsView; }
|
||||
getSettingsView: function () { return Module.SocialBlockSettingsView; }
|
||||
});
|
||||
|
||||
// Sidebar view container
|
||||
Module.SocialBlockSettingsView = base.BlockSettingsView.extend({
|
||||
getTemplate: function() { return templates.socialBlockSettings; },
|
||||
getTemplate: function () { return window.templates.socialBlockSettings; },
|
||||
regions: {
|
||||
iconRegion: '#mailpoet_social_icons_selection',
|
||||
stylesRegion: '#mailpoet_social_icons_styles'
|
||||
},
|
||||
events: function() {
|
||||
events: function () {
|
||||
return {
|
||||
'click .mailpoet_done_editing': 'close'
|
||||
};
|
||||
},
|
||||
initialize: function() {
|
||||
initialize: function () {
|
||||
base.BlockSettingsView.prototype.initialize.apply(this, arguments);
|
||||
|
||||
this._iconSelectorView = new SocialBlockSettingsIconSelectorView({ model: this.model });
|
||||
this._stylesView = new SocialBlockSettingsStylesView({ model: this.model });
|
||||
},
|
||||
onRender: function() {
|
||||
onRender: function () {
|
||||
this.showChildView('iconRegion', this._iconSelectorView);
|
||||
this.showChildView('stylesRegion', this._stylesView);
|
||||
}
|
||||
@ -155,8 +158,8 @@ define([
|
||||
|
||||
// Single icon settings view, used by the selector view
|
||||
SocialBlockSettingsIconView = Marionette.View.extend({
|
||||
getTemplate: function() { return templates.socialSettingsIcon; },
|
||||
events: function() {
|
||||
getTemplate: function () { return window.templates.socialSettingsIcon; },
|
||||
events: function () {
|
||||
return {
|
||||
'click .mailpoet_delete_block': 'deleteIcon',
|
||||
'change .mailpoet_social_icon_field_type': _.partial(this.changeField, 'iconType'),
|
||||
@ -167,35 +170,36 @@ define([
|
||||
},
|
||||
modelEvents: {
|
||||
'change:iconType': 'render',
|
||||
'change:image': function() {
|
||||
'change:image': function () {
|
||||
this.$('.mailpoet_social_icon_image').attr('src', this.model.get('image'));
|
||||
},
|
||||
'change:text': function() {
|
||||
'change:text': function () {
|
||||
this.$('.mailpoet_social_icon_image').attr('alt', this.model.get('text'));
|
||||
}
|
||||
},
|
||||
templateContext: function() {
|
||||
var icons = App.getConfig().get('socialIcons'),
|
||||
// Construct icon type list of format [{iconType: 'type', title: 'Title'}, ...]
|
||||
availableIconTypes = _.map(_.keys(icons.attributes), function(key) { return { iconType: key, title: icons.get(key).get('title') }; }),
|
||||
allIconSets = App.getAvailableStyles().get('socialIconSets');
|
||||
templateContext: function () {
|
||||
var icons = App.getConfig().get('socialIcons');
|
||||
// Construct icon type list of format [{iconType: 'type', title: 'Title'}, ...]
|
||||
var availableIconTypes = _.map(_.keys(icons.attributes), function (key) { return { iconType: key, title: icons.get(key).get('title') }; });
|
||||
var allIconSets = App.getAvailableStyles().get('socialIconSets');
|
||||
return _.extend({}, base.BlockView.prototype.templateContext.apply(this, arguments), {
|
||||
iconTypes: availableIconTypes,
|
||||
currentType: icons.get(this.model.get('iconType')).toJSON(),
|
||||
allIconSets: allIconSets.toJSON()
|
||||
});
|
||||
},
|
||||
deleteIcon: function() {
|
||||
deleteIcon: function () {
|
||||
this.model.destroy();
|
||||
},
|
||||
changeLink: function(event) {
|
||||
changeLink: function (event) {
|
||||
if (this.model.get('iconType') === 'email') {
|
||||
this.model.set('link', 'mailto:' + jQuery(event.target).val());
|
||||
} else {
|
||||
return this.changeField('link', event);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
changeField: function(field, event) {
|
||||
changeField: function (field, event) {
|
||||
this.model.set(field, jQuery(event.target).val());
|
||||
}
|
||||
});
|
||||
@ -212,7 +216,7 @@ define([
|
||||
|
||||
// Select icons section container view
|
||||
SocialBlockSettingsIconSelectorView = Marionette.View.extend({
|
||||
getTemplate: function() { return templates.socialSettingsIconSelector; },
|
||||
getTemplate: function () { return window.templates.socialSettingsIconSelector; },
|
||||
regions: {
|
||||
icons: '#mailpoet_social_icon_selector_contents'
|
||||
},
|
||||
@ -222,11 +226,11 @@ define([
|
||||
modelEvents: {
|
||||
'change:iconSet': 'render'
|
||||
},
|
||||
addSocialIcon: function() {
|
||||
addSocialIcon: function () {
|
||||
// Add a social icon with default values
|
||||
this.model.get('icons').add({});
|
||||
},
|
||||
onRender: function() {
|
||||
onRender: function () {
|
||||
this.showChildView('icons', new SocialBlockSettingsIconCollectionView({
|
||||
collection: this.model.get('icons')
|
||||
}));
|
||||
@ -235,17 +239,17 @@ define([
|
||||
});
|
||||
|
||||
SocialBlockSettingsStylesView = Marionette.View.extend({
|
||||
getTemplate: function() { return templates.socialSettingsStyles; },
|
||||
getTemplate: function () { return window.templates.socialSettingsStyles; },
|
||||
modelEvents: {
|
||||
change: 'render'
|
||||
},
|
||||
events: {
|
||||
'click .mailpoet_social_icon_set': 'changeSocialIconSet'
|
||||
},
|
||||
initialize: function() {
|
||||
initialize: function () {
|
||||
this.listenTo(this.model.get('icons'), 'add remove change', this.render);
|
||||
},
|
||||
templateContext: function() {
|
||||
templateContext: function () {
|
||||
var allIconSets = App.getAvailableStyles().get('socialIconSets');
|
||||
return {
|
||||
activeSet: this.model.get('iconSet'),
|
||||
@ -254,56 +258,33 @@ define([
|
||||
availableSocialIcons: this.model.get('icons').pluck('iconType')
|
||||
};
|
||||
},
|
||||
changeSocialIconSet: function(event) {
|
||||
changeSocialIconSet: function (event) {
|
||||
this.model.set('iconSet', jQuery(event.currentTarget).data('setname'));
|
||||
},
|
||||
onBeforeDestroy: function() {
|
||||
onBeforeDestroy: function () {
|
||||
this.model.get('icons').off('add remove', this.render, this);
|
||||
}
|
||||
});
|
||||
|
||||
Module.SocialWidgetView = base.WidgetView.extend({
|
||||
getTemplate: function() { return templates.socialInsertion; },
|
||||
getTemplate: function () { return window.templates.socialInsertion; },
|
||||
behaviors: {
|
||||
DraggableBehavior: {
|
||||
cloneOriginal: true,
|
||||
drop: function() {
|
||||
return new Module.SocialBlockModel({
|
||||
type: 'social',
|
||||
iconSet: 'default',
|
||||
icons: [
|
||||
{
|
||||
type: 'socialIcon',
|
||||
iconType: 'facebook',
|
||||
link: 'http://www.facebook.com',
|
||||
image: App.getAvailableStyles().get('socialIconSets.default.facebook'),
|
||||
height: '32px',
|
||||
width: '32px',
|
||||
text: 'Facebook'
|
||||
},
|
||||
{
|
||||
type: 'socialIcon',
|
||||
iconType: 'twitter',
|
||||
link: 'http://www.twitter.com',
|
||||
image: App.getAvailableStyles().get('socialIconSets.default.twitter'),
|
||||
height: '32px',
|
||||
width: '32px',
|
||||
text: 'Twitter'
|
||||
}
|
||||
]
|
||||
}, { parse: true });
|
||||
drop: function () {
|
||||
return new Module.SocialBlockModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function(App, options) {
|
||||
App.registerBlockType('social', {
|
||||
App.on('before:start', function (BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('social', {
|
||||
blockModel: Module.SocialBlockModel,
|
||||
blockView: Module.SocialBlockView
|
||||
});
|
||||
|
||||
App.registerWidget({
|
||||
BeforeStartApp.registerWidget({
|
||||
name: 'social',
|
||||
widgetView: Module.SocialWidgetView,
|
||||
priority: 95
|
||||
|
@ -1,19 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Spacer content block
|
||||
*/
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base',
|
||||
'underscore'
|
||||
], function(App, BaseBlock, _) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock;
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base',
|
||||
'underscore'
|
||||
], function spacerBlock(App, BaseBlock, _) {
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
|
||||
Module.SpacerBlockModel = base.BlockModel.extend({
|
||||
defaults: function() {
|
||||
defaults: function defaults() {
|
||||
return this._getDefaults({
|
||||
type: 'spacer',
|
||||
styles: {
|
||||
@ -28,7 +27,7 @@ define([
|
||||
|
||||
Module.SpacerBlockView = base.BlockView.extend({
|
||||
className: 'mailpoet_block mailpoet_spacer_block mailpoet_droppable_block',
|
||||
getTemplate: function() { return templates.spacerBlock; },
|
||||
getTemplate: function getTemplate() { return window.templates.spacerBlock; },
|
||||
behaviors: _.defaults({
|
||||
ResizableBehavior: {
|
||||
elementSelector: '.mailpoet_spacer',
|
||||
@ -41,33 +40,33 @@ define([
|
||||
}
|
||||
}, base.BlockView.prototype.behaviors),
|
||||
modelEvents: _.omit(base.BlockView.prototype.modelEvents, 'change'),
|
||||
onDragSubstituteBy: function() { return Module.SpacerWidgetView; },
|
||||
initialize: function() {
|
||||
onDragSubstituteBy: function onDragSubstituteBy() { return Module.SpacerWidgetView; },
|
||||
initialize: function initialize() {
|
||||
base.BlockView.prototype.initialize.apply(this, arguments);
|
||||
|
||||
this.listenTo(this.model, 'change:styles.block.backgroundColor', this.render);
|
||||
this.listenTo(this.model, 'change:styles.block.height', this.changeHeight);
|
||||
},
|
||||
onRender: function() {
|
||||
onRender: function onRender() {
|
||||
this.toolsView = new Module.SpacerBlockToolsView({ model: this.model });
|
||||
this.showChildView('toolsRegion', this.toolsView);
|
||||
},
|
||||
changeHeight: function() {
|
||||
changeHeight: function changeHeight() {
|
||||
this.$('.mailpoet_spacer').css('height', this.model.get('styles.block.height'));
|
||||
this.$('.mailpoet_resize_handle_text').text(this.model.get('styles.block.height'));
|
||||
},
|
||||
onBeforeDestroy: function() {
|
||||
onBeforeDestroy: function onBeforeDestroy() {
|
||||
this.stopListening(this.model);
|
||||
}
|
||||
});
|
||||
|
||||
Module.SpacerBlockToolsView = base.BlockToolsView.extend({
|
||||
getSettingsView: function() { return Module.SpacerBlockSettingsView; }
|
||||
getSettingsView: function getSettingsView() { return Module.SpacerBlockSettingsView; }
|
||||
});
|
||||
|
||||
Module.SpacerBlockSettingsView = base.BlockSettingsView.extend({
|
||||
getTemplate: function() { return templates.spacerBlockSettings; },
|
||||
events: function() {
|
||||
getTemplate: function getTemplate() { return window.templates.spacerBlockSettings; },
|
||||
events: function events() {
|
||||
return {
|
||||
'change .mailpoet_field_spacer_background_color': _.partial(this.changeColorField, 'styles.block.backgroundColor'),
|
||||
'click .mailpoet_done_editing': 'close'
|
||||
@ -76,24 +75,24 @@ define([
|
||||
});
|
||||
|
||||
Module.SpacerWidgetView = base.WidgetView.extend({
|
||||
getTemplate: function() { return templates.spacerInsertion; },
|
||||
getTemplate: function getTemplate() { return window.templates.spacerInsertion; },
|
||||
behaviors: {
|
||||
DraggableBehavior: {
|
||||
cloneOriginal: true,
|
||||
drop: function() {
|
||||
drop: function drop() {
|
||||
return new Module.SpacerBlockModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function(App, options) {
|
||||
App.registerBlockType('spacer', {
|
||||
App.on('before:start', function beforeAppStart(BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('spacer', {
|
||||
blockModel: Module.SpacerBlockModel,
|
||||
blockView: Module.SpacerBlockView
|
||||
});
|
||||
|
||||
App.registerWidget({
|
||||
BeforeStartApp.registerWidget({
|
||||
name: 'spacer',
|
||||
widgetView: Module.SpacerWidgetView,
|
||||
priority: 94
|
||||
|
@ -1,29 +1,30 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Text content block
|
||||
*/
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base',
|
||||
'underscore'
|
||||
], function(App, BaseBlock, _) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock;
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base',
|
||||
'underscore',
|
||||
'mailpoet'
|
||||
], function textBlock(App, BaseBlock, _, MailPoet) {
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
|
||||
Module.TextBlockModel = base.BlockModel.extend({
|
||||
defaults: function() {
|
||||
defaults: function defaults() {
|
||||
return this._getDefaults({
|
||||
type: 'text',
|
||||
text: 'Edit this to insert text'
|
||||
}, App.getConfig().get('blockDefaults.text'));
|
||||
}
|
||||
},
|
||||
_updateDefaults: function updateDefaults() {}
|
||||
});
|
||||
|
||||
Module.TextBlockView = base.BlockView.extend({
|
||||
className: 'mailpoet_block mailpoet_text_block mailpoet_droppable_block',
|
||||
getTemplate: function() { return templates.textBlock; },
|
||||
getTemplate: function getTemplate() { return window.templates.textBlock; },
|
||||
modelEvents: _.omit(base.BlockView.prototype.modelEvents, 'change'), // Prevent rerendering on model change due to text editor redrawing
|
||||
behaviors: _.extend({}, base.BlockView.prototype.behaviors, {
|
||||
TextEditorBehavior: {
|
||||
@ -33,7 +34,7 @@ define([
|
||||
invalidElements: 'script',
|
||||
blockFormats: 'Heading 1=h1;Heading 2=h2;Heading 3=h3;Paragraph=p',
|
||||
plugins: 'link lists code textcolor colorpicker mailpoet_shortcodes paste',
|
||||
configurationFilter: function(originalSettings) {
|
||||
configurationFilter: function configurationFilter(originalSettings) {
|
||||
return _.extend({}, originalSettings, {
|
||||
mailpoet_shortcodes: App.getConfig().get('shortcodes').toJSON(),
|
||||
mailpoet_shortcodes_window_title: MailPoet.I18n.t('shortcodesWindowTitle')
|
||||
@ -41,7 +42,7 @@ define([
|
||||
}
|
||||
}
|
||||
}),
|
||||
initialize: function(options) {
|
||||
initialize: function initialize(options) {
|
||||
base.BlockView.prototype.initialize.apply(this, arguments);
|
||||
|
||||
this.renderOptions = _.defaults(options.renderOptions || {}, {
|
||||
@ -50,8 +51,8 @@ define([
|
||||
|
||||
this.disableTextEditor = this.renderOptions.disableTextEditor;
|
||||
},
|
||||
onDragSubstituteBy: function() { return Module.TextWidgetView; },
|
||||
onRender: function() {
|
||||
onDragSubstituteBy: function onDragSubstituteBy() { return Module.TextWidgetView; },
|
||||
onRender: function onRender() {
|
||||
this.toolsView = new Module.TextBlockToolsView({
|
||||
model: this.model,
|
||||
tools: {
|
||||
@ -60,46 +61,46 @@ define([
|
||||
});
|
||||
this.showChildView('toolsRegion', this.toolsView);
|
||||
},
|
||||
onTextEditorChange: function(newContent) {
|
||||
onTextEditorChange: function onTextEditorChange(newContent) {
|
||||
this.model.set('text', newContent);
|
||||
},
|
||||
onTextEditorFocus: function() {
|
||||
onTextEditorFocus: function onTextEditorFocus() {
|
||||
this.disableDragging();
|
||||
this.disableShowingTools();
|
||||
},
|
||||
onTextEditorBlur: function() {
|
||||
onTextEditorBlur: function onTextEditorBlur() {
|
||||
this.enableDragging();
|
||||
this.enableShowingTools();
|
||||
}
|
||||
});
|
||||
|
||||
Module.TextBlockToolsView = base.BlockToolsView.extend({
|
||||
getSettingsView: function() { return Module.TextBlockSettingsView; }
|
||||
getSettingsView: function getSettingsView() { return Module.TextBlockSettingsView; }
|
||||
});
|
||||
|
||||
Module.TextBlockSettingsView = base.BlockSettingsView.extend({
|
||||
getTemplate: function() { return templates.textBlockSettings; }
|
||||
getTemplate: function getTemplate() { return window.templates.textBlockSettings; }
|
||||
});
|
||||
|
||||
Module.TextWidgetView = base.WidgetView.extend({
|
||||
getTemplate: function() { return templates.textInsertion; },
|
||||
getTemplate: function getTemplate() { return window.templates.textInsertion; },
|
||||
behaviors: {
|
||||
DraggableBehavior: {
|
||||
cloneOriginal: true,
|
||||
drop: function() {
|
||||
drop: function drop() {
|
||||
return new Module.TextBlockModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function(App, options) {
|
||||
App.registerBlockType('text', {
|
||||
App.on('before:start', function beforeAppStart(BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('text', {
|
||||
blockModel: Module.TextBlockModel,
|
||||
blockView: Module.TextBlockView
|
||||
});
|
||||
|
||||
App.registerWidget({
|
||||
BeforeStartApp.registerWidget({
|
||||
name: 'text',
|
||||
widgetView: Module.TextWidgetView,
|
||||
priority: 90
|
||||
|
@ -1,31 +0,0 @@
|
||||
/**
|
||||
* This shim replaces the default Backbone.Marionette communication library
|
||||
* Backbone.Wreqr with Backbone.Radio ahead of time,
|
||||
* since this libraries will be switched in Marionette 3.x anyway
|
||||
*
|
||||
* Courtesy of https://gist.github.com/jmeas/7992474cdb1c5672d88b
|
||||
*/
|
||||
|
||||
(function(root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(['backbone.marionette', 'backbone.radio', 'underscore'], function(Marionette, Radio, _) {
|
||||
return factory(Marionette, Radio, _);
|
||||
});
|
||||
}
|
||||
else if (typeof exports !== 'undefined') {
|
||||
var Marionette = require('backbone.marionette');
|
||||
var Radio = require('backbone.radio');
|
||||
var _ = require('underscore');
|
||||
module.exports = factory(Marionette, Radio, _);
|
||||
}
|
||||
else {
|
||||
factory(root.Backbone.Marionette, root.Backbone.Radio, root._);
|
||||
}
|
||||
}(this, function(Marionette, Radio, _) {
|
||||
'use strict';
|
||||
var MarionetteApplication = Marionette.Application;
|
||||
MarionetteApplication.prototype._initChannel = function () {
|
||||
this.channelName = _.result(this, 'channelName') || 'global';
|
||||
this.channel = _.result(this, 'channel') || Radio.channel(this.channelName);
|
||||
};
|
||||
}));
|
@ -1,13 +1,13 @@
|
||||
/* eslint-disable func-names */
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'underscore',
|
||||
'mailpoet',
|
||||
'ajax'
|
||||
], function(App, _, MailPoet) {
|
||||
|
||||
'newsletter_editor/App',
|
||||
'underscore',
|
||||
'mailpoet',
|
||||
'ajax'
|
||||
], function (App, _, MailPoet) {
|
||||
var Module = {};
|
||||
|
||||
Module._query = function(args) {
|
||||
Module._query = function (args) {
|
||||
return MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'automatedLatestContent',
|
||||
@ -17,70 +17,70 @@ define([
|
||||
};
|
||||
Module._cachedQuery = _.memoize(Module._query, JSON.stringify);
|
||||
|
||||
Module.getNewsletter = function(options) {
|
||||
Module.getNewsletter = function (options) {
|
||||
return Module._query({
|
||||
action: 'get',
|
||||
options: options
|
||||
});
|
||||
};
|
||||
|
||||
Module.getPostTypes = function() {
|
||||
Module.getPostTypes = function () {
|
||||
return Module._cachedQuery({
|
||||
action: 'getPostTypes',
|
||||
options: {}
|
||||
}).then(function(response) {
|
||||
}).then(function (response) {
|
||||
return _.values(response.data);
|
||||
});
|
||||
};
|
||||
|
||||
Module.getTaxonomies = function(postType) {
|
||||
Module.getTaxonomies = function (postType) {
|
||||
return Module._cachedQuery({
|
||||
action: 'getTaxonomies',
|
||||
options: {
|
||||
postType: postType
|
||||
}
|
||||
}).then(function(response) {
|
||||
}).then(function (response) {
|
||||
return response.data;
|
||||
});
|
||||
};
|
||||
|
||||
Module.getTerms = function(options) {
|
||||
Module.getTerms = function (options) {
|
||||
return Module._cachedQuery({
|
||||
action: 'getTerms',
|
||||
options: options
|
||||
}).then(function(response) {
|
||||
}).then(function (response) {
|
||||
return response.data;
|
||||
});
|
||||
};
|
||||
|
||||
Module.getPosts = function(options) {
|
||||
Module.getPosts = function (options) {
|
||||
return Module._cachedQuery({
|
||||
action: 'getPosts',
|
||||
options: options
|
||||
}).then(function(response) {
|
||||
}).then(function (response) {
|
||||
return response.data;
|
||||
});
|
||||
};
|
||||
|
||||
Module.getTransformedPosts = function(options) {
|
||||
Module.getTransformedPosts = function (options) {
|
||||
return Module._cachedQuery({
|
||||
action: 'getTransformedPosts',
|
||||
options: options
|
||||
}).then(function(response) {
|
||||
}).then(function (response) {
|
||||
return response.data;
|
||||
});
|
||||
};
|
||||
|
||||
Module.getBulkTransformedPosts = function(options) {
|
||||
Module.getBulkTransformedPosts = function (options) {
|
||||
return Module._query({
|
||||
action: 'getBulkTransformedPosts',
|
||||
options: options
|
||||
}).then(function(response) {
|
||||
}).then(function (response) {
|
||||
return response.data;
|
||||
});
|
||||
};
|
||||
|
||||
Module.saveNewsletter = function(options) {
|
||||
Module.saveNewsletter = function (options) {
|
||||
return MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'newsletters',
|
||||
@ -89,7 +89,7 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
Module.previewNewsletter = function(options) {
|
||||
Module.previewNewsletter = function (options) {
|
||||
return MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'newsletters',
|
||||
@ -98,7 +98,7 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
App.on('start', function(App, options) {
|
||||
App.on('start', function () {
|
||||
// Prefetch post types
|
||||
Module.getPostTypes();
|
||||
});
|
||||
|
@ -1,8 +1,8 @@
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'backbone.supermodel'
|
||||
], function(App, SuperModel) {
|
||||
|
||||
'newsletter_editor/App',
|
||||
'backbone.supermodel',
|
||||
'underscore'
|
||||
], function (App, SuperModel, _) { // eslint-disable-line func-names
|
||||
var Module = {};
|
||||
|
||||
Module.ConfigModel = SuperModel.extend({
|
||||
@ -18,19 +18,25 @@ define([
|
||||
|
||||
// Global and available styles for access in blocks and their settings
|
||||
Module._config = {};
|
||||
Module.getConfig = function() { return Module._config; };
|
||||
Module.setConfig = function(options) {
|
||||
Module.getConfig = function () { return Module._config; }; // eslint-disable-line func-names
|
||||
Module.setConfig = function (options) { // eslint-disable-line func-names
|
||||
Module._config = new Module.ConfigModel(options, { parse: true });
|
||||
return Module._config;
|
||||
};
|
||||
|
||||
App.on('before:start', function(App, options) {
|
||||
var Application = App;
|
||||
App.on('before:start', function (BeforeStartApp, options) { // eslint-disable-line func-names
|
||||
var Application = BeforeStartApp;
|
||||
var config = _.clone(options.config);
|
||||
// Expose config methods globally
|
||||
Application.getConfig = Module.getConfig;
|
||||
Application.setConfig = Module.setConfig;
|
||||
|
||||
Application.setConfig(options.config);
|
||||
config.blockDefaults = _.extend(
|
||||
config.blockDefaults,
|
||||
options.newsletter.body.blockDefaults || {}
|
||||
);
|
||||
|
||||
Application.setConfig(config);
|
||||
});
|
||||
|
||||
return Module;
|
||||
|
@ -1,24 +1,24 @@
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'backbone.supermodel',
|
||||
'underscore',
|
||||
'mailpoet'
|
||||
], function(App, SuperModel, _, MailPoet) {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'backbone.supermodel',
|
||||
'underscore',
|
||||
'mailpoet'
|
||||
], function content(App, SuperModel, _, MailPoet) {
|
||||
var Module = {};
|
||||
|
||||
// Holds newsletter entry fields, such as subject or creation datetime.
|
||||
// Does not hold newsletter content nor newsletter styles, those are
|
||||
// handled by other components.
|
||||
Module.NewsletterModel = SuperModel.extend({
|
||||
whitelisted: ['id', 'subject', 'preheader'],
|
||||
initialize: function(options) {
|
||||
this.on('change', function() {
|
||||
App.getChannel().trigger('autoSave');
|
||||
whitelisted: ['id', 'subject', 'preheader', 'type'],
|
||||
initialize: function initialize() {
|
||||
this.on('change', function onChange() {
|
||||
App.getChannel().trigger('autoSave');
|
||||
});
|
||||
},
|
||||
toJSON: function() {
|
||||
toJSON: function toJSON() {
|
||||
// Use only whitelisted properties to ensure properties editor
|
||||
// doesn't control don't change.
|
||||
return _.pick(SuperModel.prototype.toJSON.call(this), this.whitelisted);
|
||||
@ -27,64 +27,63 @@ define([
|
||||
|
||||
// Content block view and model handlers for different content types
|
||||
Module._blockTypes = {};
|
||||
Module.registerBlockType = function(type, data) {
|
||||
Module.registerBlockType = function registerBlockType(type, data) {
|
||||
Module._blockTypes[type] = data;
|
||||
};
|
||||
Module.getBlockTypeModel = function(type) {
|
||||
Module.getBlockTypeModel = function getBlockTypeModel(type) {
|
||||
if (type in Module._blockTypes) {
|
||||
return Module._blockTypes[type].blockModel;
|
||||
} else {
|
||||
throw 'Block type not supported: ' + type;
|
||||
}
|
||||
throw new Error('Block type not supported: ' + type);
|
||||
};
|
||||
Module.getBlockTypeView = function(type) {
|
||||
Module.getBlockTypeView = function getBlockTypeView(type) {
|
||||
if (type in Module._blockTypes) {
|
||||
return Module._blockTypes[type].blockView;
|
||||
} else {
|
||||
throw 'Block type not supported: ' + type;
|
||||
}
|
||||
throw new Error('Block type not supported: ' + type);
|
||||
};
|
||||
|
||||
Module.getBody = function() {
|
||||
Module.getBody = function getBody() {
|
||||
return {
|
||||
content: App._contentContainer.toJSON(),
|
||||
globalStyles: App.getGlobalStyles().toJSON()
|
||||
globalStyles: App.getGlobalStyles().toJSON(),
|
||||
blockDefaults: _.omit(App.getConfig().toJSON().blockDefaults, 'text', 'image')
|
||||
};
|
||||
};
|
||||
|
||||
Module.toJSON = function() {
|
||||
Module.toJSON = function toJSON() {
|
||||
return _.extend({
|
||||
body: Module.getBody()
|
||||
}, App.getNewsletter().toJSON());
|
||||
};
|
||||
|
||||
Module.getNewsletter = function() {
|
||||
return Module.newsletter;
|
||||
Module.getNewsletter = function getNewsletter() {
|
||||
return Module.newsletter;
|
||||
};
|
||||
|
||||
Module.findModels = function(predicate) {
|
||||
Module.findModels = function findModels(predicate) {
|
||||
var blocks = App._contentContainer.getChildren();
|
||||
return _.filter(blocks, predicate);
|
||||
};
|
||||
|
||||
App.on('before:start', function(Application, options) {
|
||||
var App = Application;
|
||||
App.on('before:start', function appBeforeStart(Application, options) {
|
||||
var BeforeStartApp = Application;
|
||||
// Expose block methods globally
|
||||
App.registerBlockType = Module.registerBlockType;
|
||||
App.getBlockTypeModel = Module.getBlockTypeModel;
|
||||
App.getBlockTypeView = Module.getBlockTypeView;
|
||||
App.toJSON = Module.toJSON;
|
||||
App.getBody = Module.getBody;
|
||||
App.getNewsletter = Module.getNewsletter;
|
||||
App.findModels = Module.findModels;
|
||||
BeforeStartApp.registerBlockType = Module.registerBlockType;
|
||||
BeforeStartApp.getBlockTypeModel = Module.getBlockTypeModel;
|
||||
BeforeStartApp.getBlockTypeView = Module.getBlockTypeView;
|
||||
BeforeStartApp.toJSON = Module.toJSON;
|
||||
BeforeStartApp.getBody = Module.getBody;
|
||||
BeforeStartApp.getNewsletter = Module.getNewsletter;
|
||||
BeforeStartApp.findModels = Module.findModels;
|
||||
|
||||
Module.newsletter = new Module.NewsletterModel(_.omit(_.clone(options.newsletter), ['body']));
|
||||
});
|
||||
|
||||
App.on('start', function(Application, options) {
|
||||
var App = Application;
|
||||
App.on('start', function appOnStart(Application, options) {
|
||||
var StartApp = Application;
|
||||
var body = options.newsletter.body;
|
||||
var content = (_.has(body, 'content')) ? body.content : {};
|
||||
var bodyContent = (_.has(body, 'content')) ? body.content : {};
|
||||
|
||||
if (!_.has(options.newsletter, 'body') || !_.isObject(options.newsletter.body)) {
|
||||
MailPoet.Notice.error(
|
||||
@ -93,13 +92,13 @@ define([
|
||||
);
|
||||
}
|
||||
|
||||
App._contentContainer = new (App.getBlockTypeModel('container'))(content, {parse: true});
|
||||
App._contentContainerView = new (App.getBlockTypeView('container'))({
|
||||
model: App._contentContainer,
|
||||
StartApp._contentContainer = new (StartApp.getBlockTypeModel('container'))(bodyContent, { parse: true });
|
||||
StartApp._contentContainerView = new (StartApp.getBlockTypeView('container'))({
|
||||
model: StartApp._contentContainer,
|
||||
renderOptions: { depth: 0 }
|
||||
});
|
||||
|
||||
App._appView.showChildView('contentRegion', App._contentContainerView);
|
||||
StartApp._appView.showChildView('contentRegion', StartApp._contentContainerView);
|
||||
});
|
||||
|
||||
|
||||
|
@ -1,35 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'backbone',
|
||||
'backbone.marionette',
|
||||
'underscore',
|
||||
'jquery'
|
||||
], function(App, Backbone, Marionette, _, jQuery) {
|
||||
|
||||
'use strict';
|
||||
|
||||
'newsletter_editor/App',
|
||||
'backbone',
|
||||
'backbone.marionette',
|
||||
'underscore',
|
||||
'jquery',
|
||||
'mailpoet'
|
||||
], function (App, Backbone, Marionette, _, jQuery, MailPoet) { // eslint-disable-line func-names
|
||||
var Module = {};
|
||||
|
||||
Module.HeadingView = Marionette.View.extend({
|
||||
getTemplate: function() { return templates.heading; },
|
||||
templateContext: function() {
|
||||
getTemplate: function () { return window.templates.heading; }, // eslint-disable-line func-names
|
||||
templateContext: function () { // eslint-disable-line func-names
|
||||
return {
|
||||
model: this.model.toJSON()
|
||||
};
|
||||
},
|
||||
events: function() {
|
||||
events: function () { // eslint-disable-line func-names
|
||||
return {
|
||||
'keyup .mailpoet_input_title': _.partial(this.changeField, 'subject'),
|
||||
'keyup .mailpoet_input_preheader': _.partial(this.changeField, 'preheader')
|
||||
};
|
||||
},
|
||||
changeField: function(field, event) {
|
||||
changeField: function (field, event) { // eslint-disable-line func-names
|
||||
this.model.set(field, jQuery(event.target).val());
|
||||
}
|
||||
});
|
||||
|
||||
App.on('start', function(App, options) {
|
||||
App._appView.showChildView('headingRegion', new Module.HeadingView({ model: App.getNewsletter() }));
|
||||
App.on('start', function (StartApp) { // eslint-disable-line func-names
|
||||
StartApp._appView.showChildView('headingRegion', new Module.HeadingView({ model: StartApp.getNewsletter() }));
|
||||
MailPoet.helpTooltip.show(document.getElementById('tooltip-designer-subject-line'), {
|
||||
tooltipId: 'tooltip-designer-subject-line-ti',
|
||||
tooltip: MailPoet.I18n.t('helpTooltipDesignerSubjectLine'),
|
||||
|
@ -1,35 +1,38 @@
|
||||
/* eslint-disable func-names */
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/components/communication',
|
||||
'mailpoet',
|
||||
'notice',
|
||||
'backbone',
|
||||
'backbone.marionette',
|
||||
'jquery',
|
||||
'blob',
|
||||
'file-saver',
|
||||
'html2canvas'
|
||||
], function(
|
||||
App,
|
||||
CommunicationComponent,
|
||||
MailPoet,
|
||||
Notice,
|
||||
Backbone,
|
||||
Marionette,
|
||||
jQuery,
|
||||
Blob,
|
||||
FileSaver,
|
||||
html2canvas
|
||||
) {
|
||||
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/components/communication',
|
||||
'mailpoet',
|
||||
'notice',
|
||||
'backbone',
|
||||
'backbone.marionette',
|
||||
'jquery',
|
||||
'blob',
|
||||
'file-saver',
|
||||
'common/thumbnail.jsx',
|
||||
'underscore',
|
||||
'jquery'
|
||||
], function (
|
||||
App,
|
||||
CommunicationComponent,
|
||||
MailPoet,
|
||||
Notice,
|
||||
Backbone,
|
||||
Marionette,
|
||||
jQuery,
|
||||
Blob,
|
||||
FileSaver,
|
||||
Thumbnail,
|
||||
_,
|
||||
$
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
saveTimeout;
|
||||
var Module = {};
|
||||
var saveTimeout;
|
||||
|
||||
// Save editor contents to server
|
||||
Module.save = function() {
|
||||
|
||||
Module.save = function () {
|
||||
var json = App.toJSON();
|
||||
|
||||
// Stringify to enable transmission of primitive non-string value types
|
||||
@ -40,12 +43,12 @@ define([
|
||||
App.getChannel().trigger('beforeEditorSave', json);
|
||||
|
||||
// save newsletter
|
||||
return CommunicationComponent.saveNewsletter(json).done(function(response) {
|
||||
if(response.success !== undefined && response.success === true) {
|
||||
return CommunicationComponent.saveNewsletter(json).done(function (response) {
|
||||
if (response.success !== undefined && response.success === true) {
|
||||
// TODO: Handle translations
|
||||
//MailPoet.Notice.success("<?php _e('Newsletter has been saved.'); ?>");
|
||||
} else if(response.error !== undefined) {
|
||||
if(response.error.length === 0) {
|
||||
// MailPoet.Notice.success("<?php _e('Newsletter has been saved.'); ?>");
|
||||
} else if (response.error !== undefined) {
|
||||
if (response.error.length === 0) {
|
||||
MailPoet.Notice.error(
|
||||
MailPoet.I18n.t('templateSaveFailed'),
|
||||
{
|
||||
@ -53,93 +56,64 @@ define([
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$(response.error).each(function(i, error) {
|
||||
$(response.error).each(function (i, error) {
|
||||
MailPoet.Notice.error(error, { scroll: true });
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!_.isUndefined(json.body)) {
|
||||
json.body = JSON.parse(json.body);
|
||||
}
|
||||
App.getChannel().trigger('afterEditorSave', json, response);
|
||||
}).fail(function(response) {
|
||||
}).fail(function (response) {
|
||||
// TODO: Handle saving errors
|
||||
App.getChannel().trigger('afterEditorSave', {}, response);
|
||||
});
|
||||
};
|
||||
|
||||
Module.getThumbnail = function(element, options) {
|
||||
var promise = html2canvas(element, options || {});
|
||||
Module.saveTemplate = function (options) {
|
||||
return Thumbnail.fromNewsletter(App.toJSON())
|
||||
.then(function (thumbnail) {
|
||||
var data = _.extend(options || {}, {
|
||||
thumbnail: thumbnail,
|
||||
body: JSON.stringify(App.getBody()),
|
||||
categories: JSON.stringify([
|
||||
'saved',
|
||||
App.getNewsletter().get('type')
|
||||
])
|
||||
});
|
||||
|
||||
return promise.then(function(oldCanvas) {
|
||||
// Temporary workaround for html2canvas-alpha2.
|
||||
// Removes 1px left transparent border from resulting canvas.
|
||||
|
||||
var oldContext = oldCanvas.getContext('2d'),
|
||||
newCanvas = document.createElement('canvas'),
|
||||
newContext = newCanvas.getContext('2d'),
|
||||
leftBorderWidth = 1;
|
||||
|
||||
newCanvas.width = oldCanvas.width;
|
||||
newCanvas.height = oldCanvas.height;
|
||||
|
||||
newContext.drawImage(
|
||||
oldCanvas,
|
||||
leftBorderWidth, 0, oldCanvas.width - leftBorderWidth, oldCanvas.height,
|
||||
0, 0, oldCanvas.width, oldCanvas.height
|
||||
);
|
||||
|
||||
return newCanvas;
|
||||
});
|
||||
return MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'newsletterTemplates',
|
||||
action: 'save',
|
||||
data: data
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Module.saveTemplate = function(options) {
|
||||
var that = this,
|
||||
promise = jQuery.Deferred();
|
||||
Module.exportTemplate = function (options) {
|
||||
return Thumbnail.fromNewsletter(App.toJSON())
|
||||
.then(function (thumbnail) {
|
||||
var data = _.extend(options || {}, {
|
||||
thumbnail: thumbnail,
|
||||
body: App.getBody(),
|
||||
categories: JSON.stringify(['saved', App.getNewsletter().get('type')])
|
||||
});
|
||||
var blob = new Blob(
|
||||
[JSON.stringify(data)],
|
||||
{ type: 'application/json;charset=utf-8' }
|
||||
);
|
||||
|
||||
promise.then(function(thumbnail) {
|
||||
var data = _.extend(options || {}, {
|
||||
thumbnail: thumbnail.toDataURL('image/jpeg'),
|
||||
body: JSON.stringify(App.getBody())
|
||||
FileSaver.saveAs(blob, 'template.json');
|
||||
MailPoet.trackEvent('Editor > Template exported', {
|
||||
'MailPoet Free version': window.mailpoet_version
|
||||
});
|
||||
});
|
||||
|
||||
return MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'newsletterTemplates',
|
||||
action: 'save',
|
||||
data: data
|
||||
});
|
||||
});
|
||||
|
||||
Module.getThumbnail(
|
||||
jQuery('#mailpoet_editor_content > .mailpoet_block').get(0)
|
||||
).then(function(thumbnail) {
|
||||
promise.resolve(thumbnail);
|
||||
});
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
Module.exportTemplate = function(options) {
|
||||
var that = this;
|
||||
return Module.getThumbnail(
|
||||
jQuery('#mailpoet_editor_content > .mailpoet_block').get(0)
|
||||
).then(function(thumbnail) {
|
||||
var data = _.extend(options || {}, {
|
||||
thumbnail: thumbnail.toDataURL('image/jpeg'),
|
||||
body: App.getBody()
|
||||
});
|
||||
var blob = new Blob(
|
||||
[JSON.stringify(data)],
|
||||
{ type: 'application/json;charset=utf-8' }
|
||||
);
|
||||
|
||||
FileSaver.saveAs(blob, 'template.json');
|
||||
MailPoet.trackEvent('Editor > Template exported', {
|
||||
'MailPoet Free version': window.mailpoet_version
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Module.SaveView = Marionette.View.extend({
|
||||
getTemplate: function() { return templates.save; },
|
||||
getTemplate: function () { return window.templates.save; },
|
||||
events: {
|
||||
'click .mailpoet_save_button': 'save',
|
||||
'click .mailpoet_save_show_options': 'toggleSaveOptions',
|
||||
@ -151,46 +125,46 @@ define([
|
||||
'click .mailpoet_save_export': 'toggleExportTemplate',
|
||||
'click .mailpoet_export_template': 'exportTemplate'
|
||||
},
|
||||
initialize: function(options) {
|
||||
initialize: function () {
|
||||
App.getChannel().on('beforeEditorSave', this.beforeSave, this);
|
||||
App.getChannel().on('afterEditorSave', this.afterSave, this);
|
||||
},
|
||||
onRender: function() {
|
||||
onRender: function () {
|
||||
this.validateNewsletter(App.toJSON());
|
||||
},
|
||||
save: function() {
|
||||
save: function () {
|
||||
this.hideOptionContents();
|
||||
App.getChannel().request('save');
|
||||
},
|
||||
beforeSave: function() {
|
||||
beforeSave: function () {
|
||||
// TODO: Add a loading animation instead
|
||||
this.$('.mailpoet_autosaved_at').text(MailPoet.I18n.t('saving'));
|
||||
},
|
||||
afterSave: function(json, response) {
|
||||
afterSave: function (json) {
|
||||
this.validateNewsletter(json);
|
||||
// Update 'Last saved timer'
|
||||
this.$('.mailpoet_editor_last_saved').removeClass('mailpoet_hidden');
|
||||
this.$('.mailpoet_autosaved_at').text('');
|
||||
},
|
||||
toggleSaveOptions: function() {
|
||||
toggleSaveOptions: function () {
|
||||
this.$('.mailpoet_save_options').toggleClass('mailpoet_hidden');
|
||||
this.$('.mailpoet_save_show_options').toggleClass('mailpoet_save_show_options_active');
|
||||
},
|
||||
toggleSaveAsTemplate: function() {
|
||||
toggleSaveAsTemplate: function () {
|
||||
this.$('.mailpoet_save_as_template_container').toggleClass('mailpoet_hidden');
|
||||
this.toggleSaveOptions();
|
||||
},
|
||||
showSaveAsTemplate: function() {
|
||||
showSaveAsTemplate: function () {
|
||||
this.$('.mailpoet_save_as_template_container').removeClass('mailpoet_hidden');
|
||||
this.toggleSaveOptions();
|
||||
},
|
||||
hideSaveAsTemplate: function() {
|
||||
hideSaveAsTemplate: function () {
|
||||
this.$('.mailpoet_save_as_template_container').addClass('mailpoet_hidden');
|
||||
},
|
||||
saveAsTemplate: function() {
|
||||
var templateName = this.$('.mailpoet_save_as_template_name').val(),
|
||||
templateDescription = this.$('.mailpoet_save_as_template_description').val(),
|
||||
that = this;
|
||||
saveAsTemplate: function () {
|
||||
var templateName = this.$('.mailpoet_save_as_template_name').val();
|
||||
var templateDescription = this.$('.mailpoet_save_as_template_description').val();
|
||||
var that = this;
|
||||
|
||||
if (templateName === '') {
|
||||
MailPoet.Notice.error(
|
||||
@ -212,7 +186,7 @@ define([
|
||||
Module.saveTemplate({
|
||||
name: templateName,
|
||||
description: templateDescription
|
||||
}).done(function() {
|
||||
}).then(function () {
|
||||
MailPoet.Notice.success(
|
||||
MailPoet.I18n.t('templateSaved'),
|
||||
{
|
||||
@ -223,7 +197,7 @@ define([
|
||||
MailPoet.trackEvent('Editor > Template saved', {
|
||||
'MailPoet Free version': window.mailpoet_version
|
||||
});
|
||||
}).fail(function() {
|
||||
}).catch(function () {
|
||||
MailPoet.Notice.error(
|
||||
MailPoet.I18n.t('templateSaveFailed'),
|
||||
{
|
||||
@ -234,19 +208,18 @@ define([
|
||||
});
|
||||
this.hideOptionContents();
|
||||
}
|
||||
|
||||
},
|
||||
toggleExportTemplate: function() {
|
||||
toggleExportTemplate: function () {
|
||||
this.$('.mailpoet_export_template_container').toggleClass('mailpoet_hidden');
|
||||
this.toggleSaveOptions();
|
||||
},
|
||||
hideExportTemplate: function() {
|
||||
hideExportTemplate: function () {
|
||||
this.$('.mailpoet_export_template_container').addClass('mailpoet_hidden');
|
||||
},
|
||||
exportTemplate: function() {
|
||||
var templateName = this.$('.mailpoet_export_template_name').val(),
|
||||
templateDescription = this.$('.mailpoet_export_template_description').val(),
|
||||
that = this;
|
||||
exportTemplate: function () {
|
||||
var templateName = this.$('.mailpoet_export_template_name').val();
|
||||
var templateDescription = this.$('.mailpoet_export_template_description').val();
|
||||
var that = this;
|
||||
|
||||
if (templateName === '') {
|
||||
MailPoet.Notice.error(
|
||||
@ -272,27 +245,28 @@ define([
|
||||
this.hideExportTemplate();
|
||||
}
|
||||
},
|
||||
hideOptionContents: function() {
|
||||
hideOptionContents: function () {
|
||||
this.hideSaveAsTemplate();
|
||||
this.hideExportTemplate();
|
||||
this.$('.mailpoet_save_options').addClass('mailpoet_hidden');
|
||||
},
|
||||
next: function() {
|
||||
next: function () {
|
||||
this.hideOptionContents();
|
||||
if(!this.$('.mailpoet_save_next').hasClass('button-disabled')) {
|
||||
if (!this.$('.mailpoet_save_next').hasClass('button-disabled')) {
|
||||
Module._cancelAutosave();
|
||||
Module.save().done(function(response) {
|
||||
Module.save().done(function () {
|
||||
window.location.href = App.getConfig().get('urls.send');
|
||||
});
|
||||
}
|
||||
},
|
||||
validateNewsletter: function(jsonObject) {
|
||||
validateNewsletter: function (jsonObject) {
|
||||
var contents;
|
||||
if (!App._contentContainer.isValid()) {
|
||||
this.showValidationError(App._contentContainer.validationError);
|
||||
return;
|
||||
}
|
||||
|
||||
var contents = JSON.stringify(jsonObject);
|
||||
contents = JSON.stringify(jsonObject);
|
||||
if (App.getConfig().get('validation.validateUnsubscribeLinkPresent') &&
|
||||
contents.indexOf('[link:subscription_unsubscribe_url]') < 0 &&
|
||||
contents.indexOf('[link:subscription_unsubscribe]') < 0) {
|
||||
@ -300,45 +274,54 @@ define([
|
||||
return;
|
||||
}
|
||||
|
||||
if ((App.getNewsletter().get('type') === 'notification') &&
|
||||
contents.indexOf('"type":"automatedLatestContent"') < 0
|
||||
) {
|
||||
this.showValidationError(MailPoet.I18n.t('automatedLatestContentMissing'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.hideValidationError();
|
||||
},
|
||||
showValidationError: function(message) {
|
||||
showValidationError: function (message) {
|
||||
var $el = this.$('.mailpoet_save_error');
|
||||
$el.text(message);
|
||||
$el.removeClass('mailpoet_hidden');
|
||||
|
||||
this.$('.mailpoet_save_next').addClass('button-disabled');
|
||||
},
|
||||
hideValidationError: function() {
|
||||
hideValidationError: function () {
|
||||
this.$('.mailpoet_save_error').addClass('mailpoet_hidden');
|
||||
this.$('.mailpoet_save_next').removeClass('button-disabled');
|
||||
}
|
||||
});
|
||||
|
||||
Module.autoSave = function() {
|
||||
Module.autoSave = function () {
|
||||
// Delay in saving editor contents, during which a new autosave
|
||||
// may be requested
|
||||
var AUTOSAVE_DELAY_DURATION = 1000;
|
||||
|
||||
Module._cancelAutosave();
|
||||
saveTimeout = setTimeout(function() {
|
||||
App.getChannel().request('save').always(function() {
|
||||
saveTimeout = setTimeout(function () {
|
||||
App.getChannel().request('save').always(function () {
|
||||
Module._cancelAutosave();
|
||||
});
|
||||
}, AUTOSAVE_DELAY_DURATION);
|
||||
};
|
||||
|
||||
Module._cancelAutosave = function() {
|
||||
Module._cancelAutosave = function () {
|
||||
if (!saveTimeout) return;
|
||||
|
||||
clearTimeout(saveTimeout);
|
||||
saveTimeout = undefined;
|
||||
};
|
||||
|
||||
Module.beforeExitWithUnsavedChanges = function(e) {
|
||||
Module.beforeExitWithUnsavedChanges = function (e) {
|
||||
var message;
|
||||
var event;
|
||||
if (saveTimeout) {
|
||||
var message = MailPoet.I18n.t('unsavedChangesWillBeLost');
|
||||
var event = e || window.event;
|
||||
message = MailPoet.I18n.t('unsavedChangesWillBeLost');
|
||||
event = e || window.event;
|
||||
|
||||
if (event) {
|
||||
event.returnValue = message;
|
||||
@ -346,10 +329,11 @@ define([
|
||||
|
||||
return message;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
App.on('before:start', function(App, options) {
|
||||
var Application = App;
|
||||
App.on('before:start', function (BeforeStartApp) {
|
||||
var Application = BeforeStartApp;
|
||||
Application.save = Module.save;
|
||||
Application.getChannel().on('autoSave', Module.autoSave);
|
||||
|
||||
@ -358,9 +342,9 @@ define([
|
||||
Application.getChannel().reply('save', Application.save);
|
||||
});
|
||||
|
||||
App.on('start', function(App, options) {
|
||||
App.on('start', function (BeforeStartApp) {
|
||||
var saveView = new Module.SaveView();
|
||||
App._appView.showChildView('bottomRegion', saveView);
|
||||
BeforeStartApp._appView.showChildView('bottomRegion', saveView);
|
||||
});
|
||||
|
||||
return Module;
|
||||
|
@ -1,29 +1,27 @@
|
||||
/* eslint-disable func-names */
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/components/communication',
|
||||
'mailpoet',
|
||||
'backbone',
|
||||
'backbone.marionette',
|
||||
'backbone.supermodel',
|
||||
'underscore',
|
||||
'jquery',
|
||||
'sticky-kit'
|
||||
], function(
|
||||
App,
|
||||
CommunicationComponent,
|
||||
MailPoet,
|
||||
Backbone,
|
||||
Marionette,
|
||||
SuperModel,
|
||||
_,
|
||||
jQuery,
|
||||
StickyKit
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/components/communication',
|
||||
'mailpoet',
|
||||
'backbone',
|
||||
'backbone.marionette',
|
||||
'backbone.supermodel',
|
||||
'underscore',
|
||||
'jquery'
|
||||
], function (
|
||||
App,
|
||||
CommunicationComponent,
|
||||
MailPoet,
|
||||
Backbone,
|
||||
Marionette,
|
||||
SuperModel,
|
||||
_,
|
||||
jQuery
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {};
|
||||
|
||||
var SidebarView;
|
||||
// Widget handlers for use to create new content blocks via drag&drop
|
||||
Module._contentWidgets = new (Backbone.Collection.extend({
|
||||
model: SuperModel.extend({
|
||||
@ -35,8 +33,8 @@ define([
|
||||
}),
|
||||
comparator: 'priority'
|
||||
}))();
|
||||
Module.registerWidget = function(widget) { return Module._contentWidgets.add(widget); };
|
||||
Module.getWidgets = function() { return Module._contentWidgets; };
|
||||
Module.registerWidget = function (widget) { return Module._contentWidgets.add(widget); };
|
||||
Module.getWidgets = function () { return Module._contentWidgets; };
|
||||
|
||||
// Layout widget handlers for use to create new layout blocks via drag&drop
|
||||
Module._layoutWidgets = new (Backbone.Collection.extend({
|
||||
@ -49,11 +47,11 @@ define([
|
||||
}),
|
||||
comparator: 'priority'
|
||||
}))();
|
||||
Module.registerLayoutWidget = function(widget) { return Module._layoutWidgets.add(widget); };
|
||||
Module.getLayoutWidgets = function() { return Module._layoutWidgets; };
|
||||
Module.registerLayoutWidget = function (widget) { return Module._layoutWidgets.add(widget); };
|
||||
Module.getLayoutWidgets = function () { return Module._layoutWidgets; };
|
||||
|
||||
var SidebarView = Marionette.View.extend({
|
||||
getTemplate: function() { return templates.sidebar; },
|
||||
SidebarView = Marionette.View.extend({
|
||||
getTemplate: function () { return window.templates.sidebar; },
|
||||
regions: {
|
||||
contentRegion: '.mailpoet_content_region',
|
||||
layoutRegion: '.mailpoet_layout_region',
|
||||
@ -61,18 +59,18 @@ define([
|
||||
previewRegion: '.mailpoet_preview_region'
|
||||
},
|
||||
events: {
|
||||
'click .mailpoet_sidebar_region h3, .mailpoet_sidebar_region .handlediv': function(event) {
|
||||
var $openRegion = this.$el.find('.mailpoet_sidebar_region:not(.closed)'),
|
||||
$targetRegion = this.$el.find(event.target).closest('.mailpoet_sidebar_region');
|
||||
'click .mailpoet_sidebar_region h3, .mailpoet_sidebar_region .handlediv': function (event) {
|
||||
var $openRegion = this.$el.find('.mailpoet_sidebar_region:not(.closed)');
|
||||
var $targetRegion = this.$el.find(event.target).closest('.mailpoet_sidebar_region');
|
||||
|
||||
$openRegion.find('.mailpoet_region_content').velocity(
|
||||
'slideUp',
|
||||
{
|
||||
duration: 250,
|
||||
easing: 'easeOut',
|
||||
complete: function() {
|
||||
complete: function () {
|
||||
$openRegion.addClass('closed');
|
||||
}.bind(this)
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@ -82,7 +80,7 @@ define([
|
||||
{
|
||||
duration: 250,
|
||||
easing: 'easeIn',
|
||||
complete: function() {
|
||||
complete: function () {
|
||||
$targetRegion.removeClass('closed');
|
||||
}
|
||||
}
|
||||
@ -90,12 +88,12 @@ define([
|
||||
}
|
||||
}
|
||||
},
|
||||
initialize: function(options) {
|
||||
initialize: function () {
|
||||
jQuery(window)
|
||||
.on('resize', this.updateHorizontalScroll.bind(this))
|
||||
.on('scroll', this.updateHorizontalScroll.bind(this));
|
||||
},
|
||||
onRender: function() {
|
||||
onRender: function () {
|
||||
this.showChildView('contentRegion', new Module.SidebarWidgetsView(
|
||||
App.getWidgets()
|
||||
));
|
||||
@ -108,24 +106,23 @@ define([
|
||||
}));
|
||||
this.showChildView('previewRegion', new Module.SidebarPreviewView());
|
||||
},
|
||||
updateHorizontalScroll: function() {
|
||||
updateHorizontalScroll: function () {
|
||||
// Fixes the sidebar so that on narrower screens the horizontal
|
||||
// position of the sidebar would be scrollable and not fixed
|
||||
// partially out of visible screen
|
||||
this.$el.parent().each(function () {
|
||||
var calculated_left, self;
|
||||
|
||||
self = jQuery(this);
|
||||
var calculatedLeft;
|
||||
var self = jQuery(this);
|
||||
|
||||
if (self.css('position') === 'fixed') {
|
||||
calculated_left = self.parent().offset().left - jQuery(window).scrollLeft();
|
||||
self.css('left', calculated_left + 'px');
|
||||
calculatedLeft = self.parent().offset().left - jQuery(window).scrollLeft();
|
||||
self.css('left', calculatedLeft + 'px');
|
||||
} else {
|
||||
self.css('left', '');
|
||||
}
|
||||
});
|
||||
},
|
||||
onDomRefresh: function() {
|
||||
onDomRefresh: function () {
|
||||
this.$el.parent().stick_in_parent({
|
||||
offset_top: 32
|
||||
});
|
||||
@ -140,23 +137,23 @@ define([
|
||||
* Draggable widget collection view
|
||||
*/
|
||||
Module.SidebarWidgetsCollectionView = Marionette.CollectionView.extend({
|
||||
childView: function(item) { return item.get('widgetView'); }
|
||||
childView: function (item) { return item.get('widgetView'); }
|
||||
});
|
||||
|
||||
/**
|
||||
* Responsible for rendering draggable content widgets
|
||||
*/
|
||||
Module.SidebarWidgetsView = Marionette.View.extend({
|
||||
getTemplate: function() { return templates.sidebarContent; },
|
||||
getTemplate: function () { return window.templates.sidebarContent; },
|
||||
regions: {
|
||||
widgets: '.mailpoet_region_content'
|
||||
},
|
||||
|
||||
initialize: function(widgets) {
|
||||
initialize: function (widgets) {
|
||||
this.widgets = widgets;
|
||||
},
|
||||
|
||||
onRender: function() {
|
||||
onRender: function () {
|
||||
this.showChildView('widgets', new Module.SidebarWidgetsCollectionView({
|
||||
collection: this.widgets
|
||||
}));
|
||||
@ -167,68 +164,68 @@ define([
|
||||
* Responsible for rendering draggable layout widgets
|
||||
*/
|
||||
Module.SidebarLayoutWidgetsView = Module.SidebarWidgetsView.extend({
|
||||
getTemplate: function() { return templates.sidebarLayout; }
|
||||
getTemplate: function () { return window.templates.sidebarLayout; }
|
||||
});
|
||||
|
||||
/**
|
||||
* Responsible for managing global styles
|
||||
*/
|
||||
Module.SidebarStylesView = Marionette.View.extend({
|
||||
getTemplate: function() { return templates.sidebarStyles; },
|
||||
getTemplate: function () { return window.templates.sidebarStyles; },
|
||||
behaviors: {
|
||||
ColorPickerBehavior: {}
|
||||
},
|
||||
events: function() {
|
||||
events: function () {
|
||||
return {
|
||||
'change #mailpoet_text_font_color': _.partial(this.changeColorField, 'text.fontColor'),
|
||||
'change #mailpoet_text_font_family': function(event) {
|
||||
'change #mailpoet_text_font_family': function (event) {
|
||||
this.model.set('text.fontFamily', event.target.value);
|
||||
},
|
||||
'change #mailpoet_text_font_size': function(event) {
|
||||
'change #mailpoet_text_font_size': function (event) {
|
||||
this.model.set('text.fontSize', event.target.value);
|
||||
},
|
||||
'change #mailpoet_h1_font_color': _.partial(this.changeColorField, 'h1.fontColor'),
|
||||
'change #mailpoet_h1_font_family': function(event) {
|
||||
'change #mailpoet_h1_font_family': function (event) {
|
||||
this.model.set('h1.fontFamily', event.target.value);
|
||||
},
|
||||
'change #mailpoet_h1_font_size': function(event) {
|
||||
'change #mailpoet_h1_font_size': function (event) {
|
||||
this.model.set('h1.fontSize', event.target.value);
|
||||
},
|
||||
'change #mailpoet_h2_font_color': _.partial(this.changeColorField, 'h2.fontColor'),
|
||||
'change #mailpoet_h2_font_family': function(event) {
|
||||
'change #mailpoet_h2_font_family': function (event) {
|
||||
this.model.set('h2.fontFamily', event.target.value);
|
||||
},
|
||||
'change #mailpoet_h2_font_size': function(event) {
|
||||
'change #mailpoet_h2_font_size': function (event) {
|
||||
this.model.set('h2.fontSize', event.target.value);
|
||||
},
|
||||
'change #mailpoet_h3_font_color': _.partial(this.changeColorField, 'h3.fontColor'),
|
||||
'change #mailpoet_h3_font_family': function(event) {
|
||||
'change #mailpoet_h3_font_family': function (event) {
|
||||
this.model.set('h3.fontFamily', event.target.value);
|
||||
},
|
||||
'change #mailpoet_h3_font_size': function(event) {
|
||||
'change #mailpoet_h3_font_size': function (event) {
|
||||
this.model.set('h3.fontSize', event.target.value);
|
||||
},
|
||||
'change #mailpoet_a_font_color': _.partial(this.changeColorField, 'link.fontColor'),
|
||||
'change #mailpoet_a_font_underline': function(event) {
|
||||
'change #mailpoet_a_font_underline': function (event) {
|
||||
this.model.set('link.textDecoration', (event.target.checked) ? event.target.value : 'none');
|
||||
},
|
||||
'change #mailpoet_newsletter_background_color': _.partial(this.changeColorField, 'wrapper.backgroundColor'),
|
||||
'change #mailpoet_background_color': _.partial(this.changeColorField, 'body.backgroundColor')
|
||||
};
|
||||
},
|
||||
templateContext: function() {
|
||||
templateContext: function () {
|
||||
return {
|
||||
model: this.model.toJSON(),
|
||||
availableStyles: this.availableStyles.toJSON()
|
||||
};
|
||||
},
|
||||
initialize: function(options) {
|
||||
initialize: function (options) {
|
||||
this.availableStyles = options.availableStyles;
|
||||
},
|
||||
changeField: function(field, event) {
|
||||
changeField: function (field, event) {
|
||||
this.model.set(field, jQuery(event.target).val());
|
||||
},
|
||||
changeColorField: function(field, event) {
|
||||
changeColorField: function (field, event) {
|
||||
var value = jQuery(event.target).val();
|
||||
if (value === '') {
|
||||
value = 'transparent';
|
||||
@ -238,18 +235,18 @@ define([
|
||||
});
|
||||
|
||||
Module.SidebarPreviewView = Marionette.View.extend({
|
||||
getTemplate: function() { return templates.sidebarPreview; },
|
||||
getTemplate: function () { return window.templates.sidebarPreview; },
|
||||
events: {
|
||||
'click .mailpoet_show_preview': 'showPreview',
|
||||
'click #mailpoet_send_preview': 'sendPreview'
|
||||
},
|
||||
onBeforeDestroy: function() {
|
||||
onBeforeDestroy: function () {
|
||||
if (this.previewView) {
|
||||
this.previewView.destroy();
|
||||
this.previewView = null;
|
||||
}
|
||||
},
|
||||
showPreview: function() {
|
||||
showPreview: function () {
|
||||
var json = App.toJSON();
|
||||
|
||||
// Stringify to enable transmission of primitive non-string value types
|
||||
@ -264,23 +261,23 @@ define([
|
||||
endpoint: 'newsletters',
|
||||
action: 'showPreview',
|
||||
data: json
|
||||
}).always(function() {
|
||||
}).always(function () {
|
||||
MailPoet.Modal.loading(false);
|
||||
}).done(function(response) {
|
||||
}).done(function (response) {
|
||||
this.previewView = new Module.NewsletterPreviewView({
|
||||
previewUrl: response.meta.preview_url
|
||||
});
|
||||
|
||||
var view = this.previewView.render();
|
||||
this.previewView.render();
|
||||
this.previewView.$el.css('height', '100%');
|
||||
|
||||
|
||||
MailPoet.Modal.popup({
|
||||
template: '',
|
||||
element: this.previewView.$el,
|
||||
width: '95%',
|
||||
height: '94%',
|
||||
title: MailPoet.I18n.t('newsletterPreview'),
|
||||
onCancel: function() {
|
||||
onCancel: function () {
|
||||
this.previewView.destroy();
|
||||
this.previewView = null;
|
||||
}.bind(this)
|
||||
@ -289,16 +286,16 @@ define([
|
||||
MailPoet.trackEvent('Editor > Browser Preview', {
|
||||
'MailPoet Free version': window.mailpoet_version
|
||||
});
|
||||
}.bind(this)).fail(function(response) {
|
||||
}.bind(this)).fail(function (response) {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map(function(error) { return error.message; }),
|
||||
response.errors.map(function (error) { return error.message; }),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
sendPreview: function() {
|
||||
sendPreview: function () {
|
||||
// get form data
|
||||
var $emailField = this.$('#mailpoet_preview_to_email');
|
||||
var data = {
|
||||
@ -321,10 +318,10 @@ define([
|
||||
MailPoet.Modal.loading(true);
|
||||
|
||||
// save before sending
|
||||
App.getChannel().request('save').always(function() {
|
||||
CommunicationComponent.previewNewsletter(data).always(function() {
|
||||
App.getChannel().request('save').always(function () {
|
||||
CommunicationComponent.previewNewsletter(data).always(function () {
|
||||
MailPoet.Modal.loading(false);
|
||||
}).done(function(response) {
|
||||
}).done(function () {
|
||||
MailPoet.Notice.success(
|
||||
MailPoet.I18n.t('newsletterPreviewSent'),
|
||||
{ scroll: true }
|
||||
@ -333,28 +330,29 @@ define([
|
||||
'MailPoet Free version': window.mailpoet_version,
|
||||
'Domain name': data.subscriber.substring(data.subscriber.indexOf('@') + 1)
|
||||
});
|
||||
}).fail(function(response) {
|
||||
}).fail(function (response) {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map(function(error) { return error.message; }),
|
||||
response.errors.map(function (error) { return error.message; }),
|
||||
{ scroll: true, static: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
|
||||
Module.NewsletterPreviewView = Marionette.View.extend({
|
||||
getTemplate: function() { return templates.newsletterPreview; },
|
||||
initialize: function(options) {
|
||||
getTemplate: function () { return window.templates.newsletterPreview; },
|
||||
initialize: function (options) {
|
||||
this.previewUrl = options.previewUrl;
|
||||
this.width = '100%';
|
||||
this.height = '100%';
|
||||
// this.width = App.getConfig().get('newsletterPreview.width');
|
||||
// this.height = App.getConfig().get('newsletterPreview.height')
|
||||
},
|
||||
templateContext: function() {
|
||||
templateContext: function () {
|
||||
return {
|
||||
previewUrl: this.previewUrl,
|
||||
width: this.width,
|
||||
@ -363,19 +361,18 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function(App, options) {
|
||||
var Application = App;
|
||||
App.on('before:start', function (BeforeStartApp) {
|
||||
var Application = BeforeStartApp;
|
||||
Application.registerWidget = Module.registerWidget;
|
||||
Application.getWidgets = Module.getWidgets;
|
||||
Application.registerLayoutWidget = Module.registerLayoutWidget;
|
||||
Application.getLayoutWidgets = Module.getLayoutWidgets;
|
||||
});
|
||||
|
||||
App.on('start', function(App, options) {
|
||||
var stylesModel = App.getGlobalStyles(),
|
||||
sidebarView = new SidebarView();
|
||||
App.on('start', function (StartApp) {
|
||||
var sidebarView = new SidebarView();
|
||||
|
||||
App._appView.showChildView('sidebarRegion', sidebarView);
|
||||
StartApp._appView.showChildView('sidebarRegion', sidebarView);
|
||||
|
||||
MailPoet.helpTooltip.show(document.getElementById('tooltip-send-preview'), {
|
||||
tooltipId: 'tooltip-editor-send-preview',
|
||||
|
@ -1,11 +1,12 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'backbone.marionette',
|
||||
'backbone.supermodel'
|
||||
], function(App, Marionette, SuperModel) {
|
||||
|
||||
'use strict';
|
||||
|
||||
'newsletter_editor/App',
|
||||
'backbone.marionette',
|
||||
'backbone.supermodel',
|
||||
'underscore'
|
||||
], function (App, Marionette, SuperModel, _) { // eslint-disable-line func-names
|
||||
var Module = {};
|
||||
|
||||
Module.StylesModel = SuperModel.extend({
|
||||
@ -41,48 +42,50 @@ define([
|
||||
backgroundColor: '#cccccc'
|
||||
}
|
||||
},
|
||||
initialize: function() {
|
||||
this.on('change', function() { App.getChannel().trigger('autoSave'); });
|
||||
initialize: function () { // eslint-disable-line func-names
|
||||
this.on('change', function () { App.getChannel().trigger('autoSave'); }); // eslint-disable-line func-names
|
||||
}
|
||||
});
|
||||
|
||||
Module.StylesView = Marionette.View.extend({
|
||||
getTemplate: function() { return templates.styles; },
|
||||
getTemplate: function () { return window.templates.styles; }, // eslint-disable-line func-names
|
||||
modelEvents: {
|
||||
change: 'render'
|
||||
},
|
||||
serializeData: function() {
|
||||
serializeData: function () { // eslint-disable-line func-names
|
||||
return this.model.toJSON();
|
||||
}
|
||||
});
|
||||
|
||||
Module._globalStyles = new SuperModel();
|
||||
Module.getGlobalStyles = function() {
|
||||
Module.getGlobalStyles = function () { // eslint-disable-line func-names
|
||||
return Module._globalStyles;
|
||||
};
|
||||
Module.setGlobalStyles = function(options) {
|
||||
Module.setGlobalStyles = function (options) { // eslint-disable-line func-names
|
||||
Module._globalStyles = new Module.StylesModel(options);
|
||||
return Module._globalStyles;
|
||||
};
|
||||
Module.getAvailableStyles = function() {
|
||||
Module.getAvailableStyles = function () { // eslint-disable-line func-names
|
||||
return App.getConfig().get('availableStyles');
|
||||
};
|
||||
|
||||
App.on('before:start', function(App, options) {
|
||||
var Application = App;
|
||||
App.on('before:start', function (BeforeStartApp, options) { // eslint-disable-line func-names
|
||||
var Application = BeforeStartApp;
|
||||
var body;
|
||||
var globalStyles;
|
||||
// Expose style methods to global application
|
||||
Application.getGlobalStyles = Module.getGlobalStyles;
|
||||
Application.setGlobalStyles = Module.setGlobalStyles;
|
||||
Application.getAvailableStyles = Module.getAvailableStyles;
|
||||
|
||||
var body = options.newsletter.body;
|
||||
var globalStyles = (_.has(body, 'globalStyles')) ? body.globalStyles : {};
|
||||
body = options.newsletter.body;
|
||||
globalStyles = (_.has(body, 'globalStyles')) ? body.globalStyles : {};
|
||||
this.setGlobalStyles(globalStyles);
|
||||
});
|
||||
|
||||
App.on('start', function(App, options) {
|
||||
var stylesView = new Module.StylesView({ model: App.getGlobalStyles() });
|
||||
App._appView.showChildView('stylesRegion', stylesView);
|
||||
App.on('start', function (StartApp) { // eslint-disable-line func-names
|
||||
var stylesView = new Module.StylesView({ model: StartApp.getGlobalStyles() });
|
||||
StartApp._appView.showChildView('stylesRegion', stylesView);
|
||||
});
|
||||
|
||||
return Module;
|
||||
|
83
assets/js/src/newsletter_editor/initializer.jsx
Normal file
83
assets/js/src/newsletter_editor/initializer.jsx
Normal file
@ -0,0 +1,83 @@
|
||||
import Hooks from 'wp-js-hooks';
|
||||
import Breadcrumb from 'newsletters/breadcrumb.jsx';
|
||||
import MailPoet from 'mailpoet';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
const renderBreadcrumb = (newsletterType) => {
|
||||
const breadcrumbContainer = document.getElementById('mailpoet_editor_breadcrumb');
|
||||
const breadcrumb = Hooks.applyFilters(
|
||||
'mailpoet_newsletters_editor_breadcrumb',
|
||||
<Breadcrumb step="editor" />,
|
||||
newsletterType,
|
||||
'editor'
|
||||
);
|
||||
|
||||
ReactDOM.render(breadcrumb, breadcrumbContainer);
|
||||
};
|
||||
|
||||
const initializeEditor = (config) => {
|
||||
const editorContainer = document.getElementById('mailpoet_editor');
|
||||
const getUrlParam = param => (location.search.split(`${param}=`)[1] || '').split('&')[0];
|
||||
|
||||
if (!editorContainer || !window.EditorApplication) return;
|
||||
|
||||
MailPoet.Modal.loading(true);
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'newsletters',
|
||||
action: 'get',
|
||||
data: {
|
||||
id: getUrlParam('id'),
|
||||
},
|
||||
})
|
||||
.always(() => MailPoet.Modal.loading(false))
|
||||
.done((response) => {
|
||||
const newsletter = response.data;
|
||||
|
||||
Promise.resolve(Hooks.applyFilters('mailpoet_newsletters_editor_extend_config', config, newsletter)).then((extendedConfig) => {
|
||||
window.EditorApplication.start({
|
||||
newsletter,
|
||||
config: extendedConfig,
|
||||
});
|
||||
}).catch(() => {
|
||||
window.EditorApplication.start({
|
||||
newsletter,
|
||||
config,
|
||||
});
|
||||
});
|
||||
|
||||
renderBreadcrumb(newsletter.type);
|
||||
|
||||
if (newsletter.status === 'sending' && newsletter.queue && newsletter.queue.status === null) {
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'sending_queue',
|
||||
action: 'pause',
|
||||
data: {
|
||||
newsletter_id: newsletter.id,
|
||||
},
|
||||
})
|
||||
.done(() => MailPoet.Notice.success(MailPoet.I18n.t('newsletterIsPaused')))
|
||||
.fail((pauseFailResponse) => {
|
||||
if (pauseFailResponse.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
pauseFailResponse.errors.map(error => error.message),
|
||||
{ scroll: true, static: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true, static: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Hooks.addAction('mailpoet_newsletters_editor_initialize', initializeEditor);
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user