commit d9969e139434961348f22c8baecad471da9ced14 Author: Alena Date: Tue Sep 16 01:40:08 2025 +0300 first commit diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6f3a291 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "liveServer.settings.port": 5501 +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/fonts.css b/fonts.css new file mode 100644 index 0000000..9a33d58 --- /dev/null +++ b/fonts.css @@ -0,0 +1,30 @@ +@font-face { + font-family: Lato; + font-weight: 900; + font-style: normal; + src: local("Lato"), + url("../fonts/Lato-Black.woff2") format('woff2'), + url("../fonts/Lato-Black.woff") format('woff'), + url("../fonts/Lato-Black.eot") format('eot'); + font-display: swap; +} +@font-face { + font-family: Lato; + font-weight: 700; + font-style: normal; + src: local("Lato"), + url("../fonts/Lato-Bold.woff2") format('woff2'), + url("../fonts/Lato-Bold.woff") format('woff'), + url("../fonts/Lato-Bold.eot") format('eot'); + font-display: swap; +} +@font-face { + font-family: Lato; + font-weight: 400; + font-style: normal; + src: local("Lato"), + url("../fonts/Lato-Regular.woff2") format('woff2'), + url("../fonts/Lato-Regular.woff") format('woff'), + url("../fonts/Lato-Regular.eot") format('eot'); + font-display: swap; +} \ No newline at end of file diff --git a/fonts/Lato-Black.eot b/fonts/Lato-Black.eot new file mode 100644 index 0000000..1a165a7 Binary files /dev/null and b/fonts/Lato-Black.eot differ diff --git a/fonts/Lato-Black.woff b/fonts/Lato-Black.woff new file mode 100644 index 0000000..7f98af6 Binary files /dev/null and b/fonts/Lato-Black.woff differ diff --git a/fonts/Lato-Black.woff2 b/fonts/Lato-Black.woff2 new file mode 100644 index 0000000..d3dc672 Binary files /dev/null and b/fonts/Lato-Black.woff2 differ diff --git a/fonts/Lato-Bold.eot b/fonts/Lato-Bold.eot new file mode 100644 index 0000000..9916538 Binary files /dev/null and b/fonts/Lato-Bold.eot differ diff --git a/fonts/Lato-Bold.woff b/fonts/Lato-Bold.woff new file mode 100644 index 0000000..0d7b3a3 Binary files /dev/null and b/fonts/Lato-Bold.woff differ diff --git a/fonts/Lato-Bold.woff2 b/fonts/Lato-Bold.woff2 new file mode 100644 index 0000000..ea5101f Binary files /dev/null and b/fonts/Lato-Bold.woff2 differ diff --git a/fonts/Lato-Regular.eot b/fonts/Lato-Regular.eot new file mode 100644 index 0000000..ddf9004 Binary files /dev/null and b/fonts/Lato-Regular.eot differ diff --git a/fonts/Lato-Regular.woff b/fonts/Lato-Regular.woff new file mode 100644 index 0000000..50c92e4 Binary files /dev/null and b/fonts/Lato-Regular.woff differ diff --git a/fonts/Lato-Regular.woff2 b/fonts/Lato-Regular.woff2 new file mode 100644 index 0000000..e29b688 Binary files /dev/null and b/fonts/Lato-Regular.woff2 differ diff --git a/images/1/Profile.svg b/images/1/Profile.svg new file mode 100644 index 0000000..9c8a2af --- /dev/null +++ b/images/1/Profile.svg @@ -0,0 +1,4 @@ + + + + diff --git a/images/1/decor/Vector.svg b/images/1/decor/Vector.svg new file mode 100644 index 0000000..2bb6935 --- /dev/null +++ b/images/1/decor/Vector.svg @@ -0,0 +1,3 @@ + + + diff --git a/images/1/decor/circles.svg b/images/1/decor/circles.svg new file mode 100644 index 0000000..73c1268 --- /dev/null +++ b/images/1/decor/circles.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/images/1/decor/dots.svg b/images/1/decor/dots.svg new file mode 100644 index 0000000..b4e46f6 --- /dev/null +++ b/images/1/decor/dots.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/1/decor/shapes (1).svg b/images/1/decor/shapes (1).svg new file mode 100644 index 0000000..1510690 --- /dev/null +++ b/images/1/decor/shapes (1).svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/images/1/decor/shapes (2).svg b/images/1/decor/shapes (2).svg new file mode 100644 index 0000000..4073d43 --- /dev/null +++ b/images/1/decor/shapes (2).svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/images/1/decor/shapes.svg b/images/1/decor/shapes.svg new file mode 100644 index 0000000..bc99b00 --- /dev/null +++ b/images/1/decor/shapes.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/images/1/illustration.svg b/images/1/illustration.svg new file mode 100644 index 0000000..a203259 --- /dev/null +++ b/images/1/illustration.svg @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/1/logo.svg b/images/1/logo.svg new file mode 100644 index 0000000..6dfab27 --- /dev/null +++ b/images/1/logo.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/images/1/shapes.svg b/images/1/shapes.svg new file mode 100644 index 0000000..5913c51 --- /dev/null +++ b/images/1/shapes.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/1/small.svg b/images/1/small.svg new file mode 100644 index 0000000..252ae23 --- /dev/null +++ b/images/1/small.svg @@ -0,0 +1,4 @@ + + + + diff --git a/images/2/Group 2.svg b/images/2/Group 2.svg new file mode 100644 index 0000000..41a7d09 --- /dev/null +++ b/images/2/Group 2.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/2/Line.svg b/images/2/Line.svg new file mode 100644 index 0000000..d74cc00 --- /dev/null +++ b/images/2/Line.svg @@ -0,0 +1,3 @@ + + + diff --git a/images/2/com.jpg b/images/2/com.jpg new file mode 100644 index 0000000..34be700 Binary files /dev/null and b/images/2/com.jpg differ diff --git a/images/2/decor/Group (1).svg b/images/2/decor/Group (1).svg new file mode 100644 index 0000000..9f3edf7 --- /dev/null +++ b/images/2/decor/Group (1).svg @@ -0,0 +1,4 @@ + + + + diff --git a/images/2/decor/Group.svg b/images/2/decor/Group.svg new file mode 100644 index 0000000..10a7ae4 --- /dev/null +++ b/images/2/decor/Group.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/2/decor/shapes (1).svg b/images/2/decor/shapes (1).svg new file mode 100644 index 0000000..c410701 --- /dev/null +++ b/images/2/decor/shapes (1).svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/images/2/decor/shapes.svg b/images/2/decor/shapes.svg new file mode 100644 index 0000000..6ac18d6 --- /dev/null +++ b/images/2/decor/shapes.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/images/2/illustration.svg b/images/2/illustration.svg new file mode 100644 index 0000000..6622743 --- /dev/null +++ b/images/2/illustration.svg @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/2/image.jpg b/images/2/image.jpg new file mode 100644 index 0000000..4479b56 Binary files /dev/null and b/images/2/image.jpg differ diff --git a/images/2/list/1.jpg b/images/2/list/1.jpg new file mode 100644 index 0000000..5fea1f0 Binary files /dev/null and b/images/2/list/1.jpg differ diff --git a/images/2/list/2.jpg b/images/2/list/2.jpg new file mode 100644 index 0000000..2f2f518 Binary files /dev/null and b/images/2/list/2.jpg differ diff --git a/images/2/list/3.jpg b/images/2/list/3.jpg new file mode 100644 index 0000000..0effb21 Binary files /dev/null and b/images/2/list/3.jpg differ diff --git a/images/2/list/4.jpg b/images/2/list/4.jpg new file mode 100644 index 0000000..f4eb48f Binary files /dev/null and b/images/2/list/4.jpg differ diff --git a/images/2/list/5.jpg b/images/2/list/5.jpg new file mode 100644 index 0000000..3ee20c0 Binary files /dev/null and b/images/2/list/5.jpg differ diff --git a/images/2/logo/logo-1.svg b/images/2/logo/logo-1.svg new file mode 100644 index 0000000..0d43cb5 --- /dev/null +++ b/images/2/logo/logo-1.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/images/2/logo/logo-2.svg b/images/2/logo/logo-2.svg new file mode 100644 index 0000000..7456244 --- /dev/null +++ b/images/2/logo/logo-2.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/images/2/logo/logo.svg b/images/2/logo/logo.svg new file mode 100644 index 0000000..c12a7e9 --- /dev/null +++ b/images/2/logo/logo.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/images/3/Line (Stroke).svg b/images/3/Line (Stroke).svg new file mode 100644 index 0000000..c02f264 --- /dev/null +++ b/images/3/Line (Stroke).svg @@ -0,0 +1,3 @@ + + + diff --git a/images/3/background.svg b/images/3/background.svg new file mode 100644 index 0000000..b8286bc --- /dev/null +++ b/images/3/background.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/3/braces.svg b/images/3/braces.svg new file mode 100644 index 0000000..5254624 --- /dev/null +++ b/images/3/braces.svg @@ -0,0 +1,4 @@ + + + + diff --git a/images/3/decor/Vector.svg b/images/3/decor/Vector.svg new file mode 100644 index 0000000..04eb20b --- /dev/null +++ b/images/3/decor/Vector.svg @@ -0,0 +1,3 @@ + + + diff --git a/images/3/decor/circles (1).svg b/images/3/decor/circles (1).svg new file mode 100644 index 0000000..5f01a8a --- /dev/null +++ b/images/3/decor/circles (1).svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/images/3/decor/circles.svg b/images/3/decor/circles.svg new file mode 100644 index 0000000..9ef68ae --- /dev/null +++ b/images/3/decor/circles.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/images/3/decor/dots (1).svg b/images/3/decor/dots (1).svg new file mode 100644 index 0000000..4a0be27 --- /dev/null +++ b/images/3/decor/dots (1).svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/3/decor/dots.svg b/images/3/decor/dots.svg new file mode 100644 index 0000000..2bf7587 --- /dev/null +++ b/images/3/decor/dots.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/3/decor/shapes (1).svg b/images/3/decor/shapes (1).svg new file mode 100644 index 0000000..732e40c --- /dev/null +++ b/images/3/decor/shapes (1).svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/images/3/decor/shapes (2).svg b/images/3/decor/shapes (2).svg new file mode 100644 index 0000000..5507442 --- /dev/null +++ b/images/3/decor/shapes (2).svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/images/3/decor/shapes.svg b/images/3/decor/shapes.svg new file mode 100644 index 0000000..443ec86 --- /dev/null +++ b/images/3/decor/shapes.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/images/3/icons/logo-1.svg b/images/3/icons/logo-1.svg new file mode 100644 index 0000000..33e6a7a --- /dev/null +++ b/images/3/icons/logo-1.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/images/3/icons/logo-10.svg b/images/3/icons/logo-10.svg new file mode 100644 index 0000000..278f82b --- /dev/null +++ b/images/3/icons/logo-10.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/images/3/icons/logo-11.svg b/images/3/icons/logo-11.svg new file mode 100644 index 0000000..aa57df9 --- /dev/null +++ b/images/3/icons/logo-11.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/images/3/icons/logo-2.svg b/images/3/icons/logo-2.svg new file mode 100644 index 0000000..6399ee0 --- /dev/null +++ b/images/3/icons/logo-2.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/images/3/icons/logo-3.svg b/images/3/icons/logo-3.svg new file mode 100644 index 0000000..d976fb5 --- /dev/null +++ b/images/3/icons/logo-3.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/images/3/icons/logo-4.svg b/images/3/icons/logo-4.svg new file mode 100644 index 0000000..69da558 --- /dev/null +++ b/images/3/icons/logo-4.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/images/3/icons/logo-5.svg b/images/3/icons/logo-5.svg new file mode 100644 index 0000000..5f28fa4 --- /dev/null +++ b/images/3/icons/logo-5.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/images/3/icons/logo-6.svg b/images/3/icons/logo-6.svg new file mode 100644 index 0000000..46aece1 --- /dev/null +++ b/images/3/icons/logo-6.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/images/3/icons/logo-7.svg b/images/3/icons/logo-7.svg new file mode 100644 index 0000000..3ada192 --- /dev/null +++ b/images/3/icons/logo-7.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/images/3/icons/logo-8.svg b/images/3/icons/logo-8.svg new file mode 100644 index 0000000..f42090c --- /dev/null +++ b/images/3/icons/logo-8.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/images/3/icons/logo-9.svg b/images/3/icons/logo-9.svg new file mode 100644 index 0000000..f4a0c93 --- /dev/null +++ b/images/3/icons/logo-9.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/images/3/icons/logo.svg b/images/3/icons/logo.svg new file mode 100644 index 0000000..c9860a7 --- /dev/null +++ b/images/3/icons/logo.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/images/3/image.jpg b/images/3/image.jpg new file mode 100644 index 0000000..08d7da2 Binary files /dev/null and b/images/3/image.jpg differ diff --git a/images/3/people/Group 2.jpg b/images/3/people/Group 2.jpg new file mode 100644 index 0000000..105379f Binary files /dev/null and b/images/3/people/Group 2.jpg differ diff --git a/images/3/people/Group 3.jpg b/images/3/people/Group 3.jpg new file mode 100644 index 0000000..389f84c Binary files /dev/null and b/images/3/people/Group 3.jpg differ diff --git a/images/3/people/Group 4.jpg b/images/3/people/Group 4.jpg new file mode 100644 index 0000000..ddeb5a5 Binary files /dev/null and b/images/3/people/Group 4.jpg differ diff --git a/images/3/people/Group 5.jpg b/images/3/people/Group 5.jpg new file mode 100644 index 0000000..c818ff7 Binary files /dev/null and b/images/3/people/Group 5.jpg differ diff --git a/images/4/icons/Calendar.svg b/images/4/icons/Calendar.svg new file mode 100644 index 0000000..7c922cd --- /dev/null +++ b/images/4/icons/Calendar.svg @@ -0,0 +1,3 @@ + + + diff --git a/images/4/icons/Clock.svg b/images/4/icons/Clock.svg new file mode 100644 index 0000000..656dcd8 --- /dev/null +++ b/images/4/icons/Clock.svg @@ -0,0 +1,4 @@ + + + + diff --git a/images/4/icons/Files.svg b/images/4/icons/Files.svg new file mode 100644 index 0000000..50bf43b --- /dev/null +++ b/images/4/icons/Files.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/images/4/icons/Play.svg b/images/4/icons/Play.svg new file mode 100644 index 0000000..bb5c776 --- /dev/null +++ b/images/4/icons/Play.svg @@ -0,0 +1,4 @@ + + + + diff --git a/images/4/icons/check.svg b/images/4/icons/check.svg new file mode 100644 index 0000000..5f227ac --- /dev/null +++ b/images/4/icons/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/images/4/icons/mic.svg b/images/4/icons/mic.svg new file mode 100644 index 0000000..75f289d --- /dev/null +++ b/images/4/icons/mic.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/images/4/illustration.svg b/images/4/illustration.svg new file mode 100644 index 0000000..d9daa05 --- /dev/null +++ b/images/4/illustration.svg @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/4/illustration2.svg b/images/4/illustration2.svg new file mode 100644 index 0000000..3a4fbaa --- /dev/null +++ b/images/4/illustration2.svg @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/4/messege/Facebook.svg b/images/4/messege/Facebook.svg new file mode 100644 index 0000000..7e35797 --- /dev/null +++ b/images/4/messege/Facebook.svg @@ -0,0 +1,3 @@ + + + diff --git a/images/4/messege/Instagram.svg b/images/4/messege/Instagram.svg new file mode 100644 index 0000000..51c5b98 --- /dev/null +++ b/images/4/messege/Instagram.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/images/4/messege/Twitter.svg b/images/4/messege/Twitter.svg new file mode 100644 index 0000000..b1d8084 --- /dev/null +++ b/images/4/messege/Twitter.svg @@ -0,0 +1,3 @@ + + + diff --git a/images/4/messege/YouTube.svg b/images/4/messege/YouTube.svg new file mode 100644 index 0000000..f1098e0 --- /dev/null +++ b/images/4/messege/YouTube.svg @@ -0,0 +1,3 @@ + + + diff --git a/images/4/messege/in.svg b/images/4/messege/in.svg new file mode 100644 index 0000000..e6799e7 --- /dev/null +++ b/images/4/messege/in.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/images/4/messege/telegram.svg b/images/4/messege/telegram.svg new file mode 100644 index 0000000..9d32463 --- /dev/null +++ b/images/4/messege/telegram.svg @@ -0,0 +1,3 @@ + + + diff --git a/images/4/outline.svg b/images/4/outline.svg new file mode 100644 index 0000000..354a30d --- /dev/null +++ b/images/4/outline.svg @@ -0,0 +1,4 @@ + + + + diff --git a/images/4/row/1.jpg b/images/4/row/1.jpg new file mode 100644 index 0000000..724cdef Binary files /dev/null and b/images/4/row/1.jpg differ diff --git a/images/4/row/2.jpg b/images/4/row/2.jpg new file mode 100644 index 0000000..5fd26c4 Binary files /dev/null and b/images/4/row/2.jpg differ diff --git a/images/4/row/3.jpg b/images/4/row/3.jpg new file mode 100644 index 0000000..55b11a7 Binary files /dev/null and b/images/4/row/3.jpg differ diff --git a/images/Chat(1).svg b/images/Chat(1).svg new file mode 100644 index 0000000..0338946 --- /dev/null +++ b/images/Chat(1).svg @@ -0,0 +1,4 @@ + + + + diff --git a/images/Line (Stroke).svg b/images/Line (Stroke).svg new file mode 100644 index 0000000..6e09492 --- /dev/null +++ b/images/Line (Stroke).svg @@ -0,0 +1,3 @@ + + + diff --git a/images/Right.svg b/images/Right.svg new file mode 100644 index 0000000..6c0e642 --- /dev/null +++ b/images/Right.svg @@ -0,0 +1,3 @@ + + + diff --git a/images/footer/Heart.svg b/images/footer/Heart.svg new file mode 100644 index 0000000..8e6fc0b --- /dev/null +++ b/images/footer/Heart.svg @@ -0,0 +1,3 @@ + + + diff --git a/images/footer/Line (Stroke).svg b/images/footer/Line (Stroke).svg new file mode 100644 index 0000000..3962049 --- /dev/null +++ b/images/footer/Line (Stroke).svg @@ -0,0 +1,3 @@ + + + diff --git a/images/footer/Mail.svg b/images/footer/Mail.svg new file mode 100644 index 0000000..b11cc77 --- /dev/null +++ b/images/footer/Mail.svg @@ -0,0 +1,3 @@ + + + diff --git a/images/footer/iPhone (1).svg b/images/footer/iPhone (1).svg new file mode 100644 index 0000000..89a37b6 --- /dev/null +++ b/images/footer/iPhone (1).svg @@ -0,0 +1,3 @@ + + + diff --git a/images/footer/logo.svg b/images/footer/logo.svg new file mode 100644 index 0000000..1b30413 --- /dev/null +++ b/images/footer/logo.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/images/iPhone(1).svg b/images/iPhone(1).svg new file mode 100644 index 0000000..0f15c9c --- /dev/null +++ b/images/iPhone(1).svg @@ -0,0 +1,3 @@ + + + diff --git a/images/outline.svg b/images/outline.svg new file mode 100644 index 0000000..354a30d --- /dev/null +++ b/images/outline.svg @@ -0,0 +1,4 @@ + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..c00e2de --- /dev/null +++ b/index.html @@ -0,0 +1,2049 @@ + + + + + + Createx + + + + + + + + +
+ +
+ +
+
+
+ + +
+ Get consultation +
+ Log in + / + Register +
+
+
+
+
+
+
+
+
+
+ +

+ Enjoy studying with Createx Online Courses +

+ +
+ +
+
    +
  • + 1200 + Students graduated +
  • + +
  • + 84 + Completed courses +
  • + +
  • + 16 + Qualified tutors +
  • + +
  • + 5 + Years of experience +
  • +
+
+
+
+
+ +
+ +
+
+
+
+
+
+

About us

+

Createx Online School

+
+
+

+ Createx Online School is a leader in online studying. + We have lots of courses and programs from the main market + experts. +

+

+ We provide relevant approaches to online learning, + internships and employment in the largest companies in the + country. Our educational programs help you get a new + specialty from scratch. During your studies, we will help + you find a job. Check the courses and online events that + we organise. +

+
+ +
+ +
+ +
+
+ +
+
    +
  • + 1200students graduated +
  • +
  • 84completed courses
  • +
  • 16qualified tutors
  • +
  • + 5years of experience +
  • +
+
+
+
+
+ +
+ +
+ +
+
+
+
+ +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+ certificate +
+
+
+
+ +
+
+
+
+

Best tutors are all here

+

Meet our team

+ +
+
+ +
+
+
+
+ +
+ +
+
+
Dianne Russell
+
Founder and CEO
+
+
+
+ +
+
+
+
+ +
+ +
+
+
Jerome Bell
+
+ Founder and Program Director +
+
+
+
+ +
+
+
+
+ +
+ +
+
+
Kristin Watson
+
+ Marketer, Curator of Marketing Course +
+
+
+
+ +
+
+
+
+ +
+ +
+
+
Marvin McKinney
+
+ PM, Curator of Management Course +
+
+
+
+ +
+
+
+
+ +
+ +
+
+
Marvin McKinney
+
+ PM, Curator of Management Course +
+
+
+
+
+
+
+
+ +
+
+
+
+

TESTIMONIALS

+

What our students say

+
+
+
+ +
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Justo, amet lectus quam viverra mus lobortis fermentum + amet, eu. Pulvinar eu sed purus facilisi. Vitae id + turpis tempus ornare turpis quis non. Congue tortor in + euismod vulputate etiam eros. Pulvinar neque pharetra + arcu diam maecenas diam integer in. +
+
+
+ +
+
+
Eleanor Pena
+
+ Position, Course +
+
+
+
+
+ +
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Justo, amet lectus quam viverra mus lobortis fermentum + amet, eu. Pulvinar eu sed purus facilisi. Vitae id + turpis tempus ornare turpis quis non. Congue tortor in + euismod vulputate etiam eros. Pulvinar neque pharetra + arcu diam maecenas diam integer in. +
+
+
+ +
+
+
Eleanor Pena
+
+ Position, Course +
+
+
+
+
+ +
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Justo, amet lectus quam viverra mus lobortis fermentum + amet, eu. Pulvinar eu sed purus facilisi. Vitae id + turpis tempus ornare turpis quis non. Congue tortor in + euismod vulputate etiam eros. Pulvinar neque pharetra + arcu diam maecenas diam integer in. +
+
+
+ +
+
+
Eleanor Pena
+
+ Position, Course +
+
+
+
+
+ +
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Justo, amet lectus quam viverra mus lobortis fermentum + amet, eu. Pulvinar eu sed purus facilisi. Vitae id + turpis tempus ornare turpis quis non. Congue tortor in + euismod vulputate etiam eros. Pulvinar neque pharetra + arcu diam maecenas diam integer in. +
+
+
+ +
+
+
Eleanor Pena
+
+ Position, Course +
+
+
+
+
+ +
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Justo, amet lectus quam viverra mus lobortis fermentum + amet, eu. Pulvinar eu sed purus facilisi. Vitae id + turpis tempus ornare turpis quis non. Congue tortor in + euismod vulputate etiam eros. Pulvinar neque pharetra + arcu diam maecenas diam integer in. +
+
+
+ +
+
+
Eleanor Pena
+
+ Position, Course +
+
+
+
+
+
+ + + + + + + + + + +
+
+
+
+
+ +
+
+
+
+

best jobs for you

+

Our students work here

+
+ +
+
+
+ +
+
+
+
+
+

Course program

+

What will you learn

+
+ +
+
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+
+

Contact info

+

Get in touch

+
+
    +
  • + + + + +
    +
    Talk to us:
    + hello@createx.com +
    +
  • +
  • + + + +
    +
    Call us:
    + (405) 555-0128 +
    +
  • +
  • + + + + +
    +
    Address:
    +
    + 2464 Royal Ln. Mesa, New Jersey 45463, USA +
    +
    +
  • +
+
+

Follow us:

+ +
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+

Any questions?

+

Drop us a line

+
+
+
+ + +
+
+ + +
+ +
+ + Send message +
+
+
+
+
+
+
+ + +
+ + + + + + + + + + diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..e69de29 diff --git a/js/mixitup.min.js b/js/mixitup.min.js new file mode 100644 index 0000000..8562101 --- /dev/null +++ b/js/mixitup.min.js @@ -0,0 +1,18 @@ +/**! + * MixItUp v3.3.1 + * A high-performance, dependency-free library for animated filtering, sorting and more + * Build 94e0fbf6-cd0b-4987-b3c0-14b59b67b8a0 + * + * @copyright Copyright 2014-2018 KunkaLabs Limited. + * @author KunkaLabs Limited. + * @link https://www.kunkalabs.com/mixitup/ + * + * @license Commercial use requires a commercial license. + * https://www.kunkalabs.com/mixitup/licenses/ + * + * Non-commercial use permitted under same terms as CC BY-NC 3.0 license. + * http://creativecommons.org/licenses/by-nc/3.0/ + */ +!function(t){"use strict";var e=null,n=null;!function(){var e=["webkit","moz","o","ms"],n=t.document.createElement("div"),a=-1;for(a=0;a-1}}(t.Element.prototype),Object.keys||(Object.keys=function(){var t=Object.prototype.hasOwnProperty,e=!1,n=[],a=-1;return e=!{toString:null}.propertyIsEnumerable("toString"),n=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],a=n.length,function(i){var o=[],r="",s=-1;if("object"!=typeof i&&("function"!=typeof i||null===i))throw new TypeError("Object.keys called on non-object");for(r in i)t.call(i,r)&&o.push(r);if(e)for(s=0;s>>0,0===i)return-1;if(e=0,arguments.length>1&&(e=Number(arguments[1]),e!==e?e=0:0!==e&&e!==1/0&&e!==-(1/0)&&(e=(e>0||-1)*Math.floor(Math.abs(e)))),e>=i)return-1;for(n=e>=0?e:Math.max(i-Math.abs(e),0);n0)||s);g++)r.id?d=r.id:(d="MixItUp"+n.randomHex(),r.id=d),e.instances[d]instanceof e.Mixer?(l=e.instances[d],(!i||i&&i.debug&&i.debug.showWarnings!==!1)&&console.warn(e.messages.warningFactoryPreexistingInstance())):(l=new e.Mixer,l.attach(r,u,d,i),e.instances[d]=l),c=new e.Facade(l),i&&i.debug&&i.debug.enable?h.push(l):h.push(c);return f=s?new e.Collection(h):h[0]},e.use=function(t){e.Base.prototype.callActions.call(e,"beforeUse",arguments),"function"==typeof t&&"mixitup-extension"===t.TYPE?"undefined"==typeof e.extensions[t.NAME]&&(t(e),e.extensions[t.NAME]=t):t.fn&&t.fn.jquery&&(e.libraries.$=t),e.Base.prototype.callActions.call(e,"afterUse",arguments)},e.instances={},e.extensions={},e.libraries={},n={hasClass:function(t,e){return!!t.className.match(new RegExp("(\\s|^)"+e+"(\\s|$)"))},addClass:function(t,e){this.hasClass(t,e)||(t.className+=t.className?" "+e:e)},removeClass:function(t,e){if(this.hasClass(t,e)){var n=new RegExp("(\\s|^)"+e+"(\\s|$)");t.className=t.className.replace(n," ").trim()}},extend:function(t,e,n,a){var i=[],o="",r=-1;n=n||!1,a=a||!1;try{if(Array.isArray(e))for(r=0;ru&&(u=f,l=c)}throw u>1&&(s=e.messages.errorConfigInvalidPropertySuggestion({probableMatch:l})),r=e.messages.errorConfigInvalidProperty({erroneous:o,suggestion:s}),new TypeError(r)}throw t},template:function(t){for(var e=/\${([\w]*)}/g,n={},a=null;a=e.exec(t);)n[a[1]]=new RegExp("\\${"+a[1]+"}","g");return function(e){var a="",i=t;e=e||{};for(a in n)i=i.replace(n[a],"undefined"!=typeof e[a]?e[a]:"");return i}},on:function(e,n,a,i){e&&(e.addEventListener?e.addEventListener(n,a,i):e.attachEvent&&(e["e"+n+a]=a,e[n+a]=function(){e["e"+n+a](t.event)},e.attachEvent("on"+n,e[n+a])))},off:function(t,e,n){t&&(t.removeEventListener?t.removeEventListener(e,n,!1):t.detachEvent&&(t.detachEvent("on"+e,t[e+n]),t[e+n]=null))},getCustomEvent:function(e,n,a){var i=null;return a=a||t.document,"function"==typeof t.CustomEvent?i=new t.CustomEvent(e,{detail:n,bubbles:!0,cancelable:!0}):"function"==typeof a.createEvent?(i=a.createEvent("CustomEvent"),i.initCustomEvent(e,!0,!0,n)):(i=a.createEventObject(),i.type=e,i.returnValue=!1,i.cancelBubble=!1,i.detail=n),i},getOriginalEvent:function(t){return t.touches&&t.touches.length?t.touches[0]:t.changedTouches&&t.changedTouches.length?t.changedTouches[0]:t},index:function(t,e){for(var n=0;null!==(t=t.previousElementSibling);)e&&!t.matches(e)||++n;return n},camelCase:function(t){return t.toLowerCase().replace(/([_-][a-z])/g,function(t){return t.toUpperCase().replace(/[_-]/,"")})},pascalCase:function(t){return(t=this.camelCase(t)).charAt(0).toUpperCase()+t.slice(1)},dashCase:function(t){return t.replace(/([A-Z])/g,"-$1").replace(/^-/,"").toLowerCase()},isElement:function(e,n){return n=n||t.document,!!(t.HTMLElement&&e instanceof t.HTMLElement)||(!!(n.defaultView&&n.defaultView.HTMLElement&&e instanceof n.defaultView.HTMLElement)||null!==e&&1===e.nodeType&&"string"==typeof e.nodeName)},createElement:function(e,n){var a=null,i=null;for(n=n||t.document,a=n.createDocumentFragment(),i=n.createElement("div"),i.innerHTML=e.trim();i.firstChild;)a.appendChild(i.firstChild);return a},removeWhitespace:function(t){for(var e;t&&"#text"===t.nodeName;)e=t,t=t.previousSibling,e.parentElement&&e.parentElement.removeChild(e)},isEqualArray:function(t,e){var n=t.length;if(n!==e.length)return!1;for(;n--;)if(t[n]!==e[n])return!1;return!0},deepEquals:function(t,e){var n;if("object"==typeof t&&t&&"object"==typeof e&&e){if(Object.keys(t).length!==Object.keys(e).length)return!1;for(n in t)if(!e.hasOwnProperty(n)||!this.deepEquals(t[n],e[n]))return!1}else if(t!==e)return!1;return!0},arrayShuffle:function(t){for(var e=t.slice(),n=e.length,a=n,i=-1,o=[];a--;)i=~~(Math.random()*n),o=e[a],e[a]=e[i],e[i]=o;return e},arrayFromList:function(t){var e,n;try{return Array.prototype.slice.call(t)}catch(a){for(e=[],n=0;n "+n),o&&e.removeAttribute("id")),i},clean:function(t){var e=[],n=-1;for(n=0;ni)return!0}return!0},Deferred:function(){this.promise=null,this.resolve=null,this.reject=null,this.id=n.randomHex()},isEmptyObject:function(t){var e="";if("function"==typeof Object.keys)return 0===Object.keys(t).length;for(e in t)if(t.hasOwnProperty(e))return!1;return!0},getClassname:function(t,e,n){var a="";return a+=t.block,a.length&&(a+=t.delineatorElement),a+=t["element"+this.pascalCase(e)],n?(a.length&&(a+=t.delineatorModifier),a+=n):a},getProperty:function(t,e){var n=e.split("."),a=null,i="",o=0;if(!e)return t;for(a=function(t){return t?t[i]:null};o-1,e.callFilters("afterIsBound",n,arguments)},addBinding:function(t){var e=this;this.callActions("beforeAddBinding",arguments),e.isBound()||e.bound.push(t),this.callActions("afterAddBinding",arguments)},removeBinding:function(t){var n=this,a=-1;this.callActions("beforeRemoveBinding",arguments),(a=n.bound.indexOf(t))>-1&&n.bound.splice(a,1),n.bound.length<1&&(n.unbindClick(),a=e.controls.indexOf(n),e.controls.splice(a,1),"active"===n.status&&n.renderStatus(n.el,"inactive")),this.callActions("afterRemoveBinding",arguments)},bindClick:function(){var t=this;this.callActions("beforeBindClick",arguments),t.handler=function(e){t.handleClick(e)},n.on(t.el,"click",t.handler),this.callActions("afterBindClick",arguments)},unbindClick:function(){var t=this;this.callActions("beforeUnbindClick",arguments),n.off(t.el,"click",t.handler),t.handler=null,this.callActions("afterUnbindClick",arguments)},handleClick:function(t){var a=this,i=null,o=null,r=!1,s=void 0,l={},c=null,u=[],f=-1;if(this.callActions("beforeHandleClick",arguments),this.pending=0,o=a.bound[0],i=a.selector?n.closestParent(t.target,o.config.selectors.control+a.selector,!0,o.dom.document):a.el,!i)return void a.callActions("afterHandleClick",arguments);switch(a.type){case"filter":l.filter=a.filter||i.getAttribute("data-filter");break;case"sort":l.sort=a.sort||i.getAttribute("data-sort");break;case"multimix":l.filter=a.filter||i.getAttribute("data-filter"),l.sort=a.sort||i.getAttribute("data-sort");break;case"toggle":l.filter=a.filter||i.getAttribute("data-toggle"),r="live"===a.status?n.hasClass(i,a.classNames.active):"active"===a.status}for(f=0;f0||("live"===a.status?a.updateLive(t,n):(i.sort=a.sort,i.filter=a.filter,a.callFilters("actionsUpdate",i,arguments),a.parseStatusChange(a.el,t,i,n)),a.callActions("afterUpdate",arguments))},updateLive:function(t,n){var a=this,i=null,o=null,r=null,s=-1;if(a.callActions("beforeUpdateLive",arguments),a.el){for(i=a.el.querySelectorAll(a.selector),s=0;r=i[s];s++){switch(o=new e.CommandMultimix,a.type){case"filter":o.filter=r.getAttribute("data-filter");break;case"sort":o.sort=r.getAttribute("data-sort");break;case"multimix":o.filter=r.getAttribute("data-filter"),o.sort=r.getAttribute("data-sort");break;case"toggle":o.filter=r.getAttribute("data-toggle")}o=a.callFilters("actionsUpdateLive",o,arguments),a.parseStatusChange(r,t,o,n)}a.callActions("afterUpdateLive",arguments)}},parseStatusChange:function(t,e,n,a){var i=this,o="",r="",s=-1;switch(i.callActions("beforeParseStatusChange",arguments),i.type){case"filter":e.filter===n.filter?i.renderStatus(t,"active"):i.renderStatus(t,"inactive");break;case"multimix":e.sort===n.sort&&e.filter===n.filter?i.renderStatus(t,"active"):i.renderStatus(t,"inactive");break;case"sort":e.sort.match(/:asc/g)&&(o=e.sort.replace(/:asc/g,"")),e.sort===n.sort||o===n.sort?i.renderStatus(t,"active"):i.renderStatus(t,"inactive");break;case"toggle":for(a.length<1&&i.renderStatus(t,"inactive"),e.filter===n.filter&&i.renderStatus(t,"active"),s=0;s-1)throw new Error(e.messages.errorInsertPreexistingElement());c.style.display="none",s.appendChild(c),s.appendChild(i.dom.document.createTextNode(" ")),n.isElement(c,i.dom.document)&&c.matches(i.config.selectors.target)&&(l=new e.Target,l.init(c,i),l.isInDom=!0,i.targets.splice(r,0,l),r++)}i.dom.parent.insertBefore(s,o)}a.startOrder=i.origOrder=i.targets,i.callActions("afterInsertTargets",arguments)},getNextSibling:function(t,e,n){var a=this,i=null;return t=Math.max(t,0),e&&"before"===n?i=e:e&&"after"===n?i=e.nextElementSibling||null:a.targets.length>0&&"undefined"!=typeof t?i=t0&&(a.config.layout.siblingAfter?i=a.config.layout.siblingAfter:a.config.layout.siblingBefore?i=a.config.layout.siblingBefore.nextElementSibling:a.dom.parent.children[0]),a.callFilters("elementGetNextSibling",i,arguments)},filterOperation:function(t){var e=this,n=!1,a=-1,i="",o=null,r=-1;for(e.callActions("beforeFilterOperation",arguments),i=t.newFilter.action,r=0;o=t.newOrder[r];r++)n=t.newFilter.collection?t.newFilter.collection.indexOf(o.dom.el)>-1:""!==t.newFilter.selector&&o.dom.el.matches(t.newFilter.selector),e.evaluateHideShow(n,o,i,t);if(t.toRemove.length)for(r=0;o=t.show[r];r++)t.toRemove.indexOf(o)>-1&&(t.show.splice(r,1),(a=t.toShow.indexOf(o))>-1&&t.toShow.splice(a,1),t.toHide.push(o),t.hide.push(o),r--);t.matching=t.show.slice(),0===t.show.length&&""!==t.newFilter.selector&&0!==e.targets.length&&(t.hasFailed=!0),e.callActions("afterFilterOperation",arguments)},evaluateHideShow:function(t,e,n,a){var i=this,o=!1,r=Array.prototype.slice.call(arguments,1);o=i.callFilters("testResultEvaluateHideShow",t,r),i.callActions("beforeEvaluateHideShow",arguments),o===!0&&"show"===n||o===!1&&"hide"===n?(a.show.push(e),!e.isShown&&a.toShow.push(e)):(a.hide.push(e),e.isShown&&a.toHide.push(e)),i.callActions("afterEvaluateHideShow",arguments)},sortOperation:function(t){var a=this,i=[],o=null,r=null,s=-1;if(a.callActions("beforeSortOperation",arguments),t.startOrder=a.targets,t.newSort.collection){for(i=[],s=0;r=t.newSort.collection[s];s++){if(a.dom.targets.indexOf(r)<0)throw new Error(e.messages.errorSortNonExistentElement());o=new e.Target,o.init(r,a),o.isInDom=!0,i.push(o)}t.newOrder=i}else"random"===t.newSort.order?t.newOrder=n.arrayShuffle(t.startOrder):""===t.newSort.attribute?(t.newOrder=a.origOrder.slice(),"desc"===t.newSort.order&&t.newOrder.reverse()):(t.newOrder=t.startOrder.slice(),t.newOrder.sort(function(e,n){return a.compare(e,n,t.newSort)}));n.isEqualArray(t.newOrder,t.startOrder)&&(t.willSort=!1),a.callActions("afterSortOperation",arguments)},compare:function(t,e,n){var a=this,i=n.order,o=a.getAttributeValue(t,n.attribute),r=a.getAttributeValue(e,n.attribute);return isNaN(1*o)||isNaN(1*r)?(o=o.toLowerCase(),r=r.toLowerCase()):(o=1*o,r=1*r),or?"asc"===i?1:-1:o===r&&n.next?a.compare(t,e,n.next):0},getAttributeValue:function(t,n){var a=this,i="";return i=t.dom.el.getAttribute("data-"+n),null===i&&a.config.debug.showWarnings&&console.warn(e.messages.warningInconsistentSortingAttributes({attribute:"data-"+n})),a.callFilters("valueGetAttributeValue",i||0,arguments)},printSort:function(e,a){var i=this,o=e?a.newOrder:a.startOrder,r=e?a.startOrder:a.newOrder,s=o.length?o[o.length-1].dom.el.nextElementSibling:null,l=t.document.createDocumentFragment(),c=null,u=null,f=null,h=-1;for(i.callActions("beforePrintSort",arguments),h=0;u=o[h];h++)f=u.dom.el,"absolute"!==f.style.position&&(n.removeWhitespace(f.previousSibling),f.parentElement.removeChild(f));for(c=s?s.previousSibling:i.dom.parent.lastChild,c&&"#text"===c.nodeName&&n.removeWhitespace(c),h=0;u=r[h];h++)f=u.dom.el,n.isElement(l.lastChild)&&l.appendChild(t.document.createTextNode(" ")),l.appendChild(f);i.dom.parent.firstChild&&i.dom.parent.firstChild!==s&&l.insertBefore(t.document.createTextNode(" "),l.childNodes[0]),s?(l.appendChild(t.document.createTextNode(" ")),i.dom.parent.insertBefore(l,s)):i.dom.parent.appendChild(l),i.callActions("afterPrintSort",arguments)},parseSortString:function(t,a){var i=this,o=t.split(" "),r=a,s=[],l=-1;for(l=0;l-1&&(c=n.substring(l),u=s.exec(c),f=u[1]),t){case"fade":a.opacity=f?parseFloat(f):0;break;case"stagger":r.staggerDuration=f?parseFloat(f):100;break;default:if(o&&r.config.animation.reverseOut&&"scale"!==t?a[t].value=(f?parseFloat(f):e.transformDefaults[t].value)*-1:a[t].value=f?parseFloat(f):e.transformDefaults[t].value,f){for(m=0;d=h[m];m++)if(f.indexOf(d)>-1){a[t].unit=d;break}}else a[t].unit=e.transformDefaults[t].unit;i.push(t+"("+a[t].value+a[t].unit+")")}r.callActions("afterParseEffect",arguments)},buildState:function(t){var n=this,a=new e.State,i=null,o=-1;for(n.callActions("beforeBuildState",arguments),o=0;i=n.targets[o];o++)(!t.toRemove.length||t.toRemove.indexOf(i)<0)&&a.targets.push(i.dom.el);for(o=0;i=t.matching[o];o++)a.matching.push(i.dom.el);for(o=0;i=t.show[o];o++)a.show.push(i.dom.el);for(o=0;i=t.hide[o];o++)(!t.toRemove.length||t.toRemove.indexOf(i)<0)&&a.hide.push(i.dom.el);return a.id=n.id,a.container=n.dom.container,a.activeFilter=t.newFilter,a.activeSort=t.newSort,a.activeDataset=t.newDataset,a.activeContainerClassName=t.newContainerClassName,a.hasFailed=t.hasFailed,a.totalTargets=n.targets.length,a.totalShow=t.show.length,a.totalHide=t.hide.length,a.totalMatching=t.matching.length,a.triggerElement=t.triggerElement,n.callFilters("stateBuildState",a,arguments)},goMix:function(a,i){var o=this,r=null;return o.callActions("beforeGoMix",arguments),o.config.animation.duration&&o.config.animation.effects&&n.isVisible(o.dom.container)||(a=!1),i.toShow.length||i.toHide.length||i.willSort||i.willChangeLayout||(a=!1),i.startState.show.length||i.show.length||(a=!1),e.events.fire("mixStart",o.dom.container,{state:i.startState,futureState:i.newState,instance:o},o.dom.document),"function"==typeof o.config.callbacks.onMixStart&&o.config.callbacks.onMixStart.call(o.dom.container,i.startState,i.newState,o),n.removeClass(o.dom.container,n.getClassname(o.config.classNames,"container",o.config.classNames.modifierFailed)),r=o.userDeferred?o.userDeferred:o.userDeferred=n.defer(e.libraries),o.isBusy=!0,a&&e.features.has.transitions?(t.pageYOffset!==i.docState.scrollTop&&t.scrollTo(i.docState.scrollLeft,i.docState.scrollTop),o.config.animation.applyPerspective&&(o.dom.parent.style[e.features.perspectiveProp]=o.config.animation.perspectiveDistance,o.dom.parent.style[e.features.perspectiveOriginProp]=o.config.animation.perspectiveOrigin),o.config.animation.animateResizeContainer&&i.startHeight!==i.newHeight&&i.viewportDeltaY!==i.startHeight-i.newHeight&&(o.dom.parent.style.height=i.startHeight+"px"),o.config.animation.animateResizeContainer&&i.startWidth!==i.newWidth&&i.viewportDeltaX!==i.startWidth-i.newWidth&&(o.dom.parent.style.width=i.startWidth+"px"),i.startHeight===i.newHeight&&(o.dom.parent.style.height=i.startHeight+"px"),i.startWidth===i.newWidth&&(o.dom.parent.style.width=i.startWidth+"px"),i.startHeight===i.newHeight&&i.startWidth===i.newWidth&&(o.dom.parent.style.overflow="hidden"),requestAnimationFrame(function(){o.moveTargets(i)}),o.callFilters("promiseGoMix",r.promise,arguments)):(o.config.debug.fauxAsync?setTimeout(function(){o.cleanUp(i)},o.config.animation.duration):o.cleanUp(i),o.callFilters("promiseGoMix",r.promise,arguments))},getStartMixData:function(n){var a=this,i=t.getComputedStyle(a.dom.parent),o=a.dom.parent.getBoundingClientRect(),r=null,s={},l=-1,c=i[e.features.boxSizingProp];for(a.incPadding="border-box"===c,a.callActions("beforeGetStartMixData",arguments),l=0;r=n.show[l];l++)s=r.getPosData(),n.showPosData[l]={startPosData:s};for(l=0;r=n.toHide[l];l++)s=r.getPosData(),n.toHidePosData[l]={startPosData:s};n.startX=o.left,n.startY=o.top,n.startHeight=a.incPadding?o.height:o.height-parseFloat(i.paddingTop)-parseFloat(i.paddingBottom)-parseFloat(i.borderTop)-parseFloat(i.borderBottom),n.startWidth=a.incPadding?o.width:o.width-parseFloat(i.paddingLeft)-parseFloat(i.paddingRight)-parseFloat(i.borderLeft)-parseFloat(i.borderRight),a.callActions("afterGetStartMixData",arguments)},setInter:function(t){var e=this,a=null,i=-1;for(e.callActions("beforeSetInter",arguments),e.config.animation.clampHeight&&(e.dom.parent.style.height=t.startHeight+"px",e.dom.parent.style.overflow="hidden"),e.config.animation.clampWidth&&(e.dom.parent.style.width=t.startWidth+"px",e.dom.parent.style.overflow="hidden"),i=0;a=t.toShow[i];i++)a.show();t.willChangeLayout&&(n.removeClass(e.dom.container,t.startContainerClassName),n.addClass(e.dom.container,t.newContainerClassName)),e.callActions("afterSetInter",arguments)},getInterMixData:function(t){var e=this,n=null,a=-1;for(e.callActions("beforeGetInterMixData",arguments),a=0;n=t.show[a];a++)t.showPosData[a].interPosData=n.getPosData();for(a=0;n=t.toHide[a];a++)t.toHidePosData[a].interPosData=n.getPosData();e.callActions("afterGetInterMixData",arguments)},setFinal:function(t){var e=this,n=null,a=-1;for(e.callActions("beforeSetFinal",arguments),t.willSort&&e.printSort(!1,t),a=0;n=t.toHide[a];a++)n.hide();e.callActions("afterSetFinal",arguments)},getFinalMixData:function(e){var a=this,i=null,o=null,r=null,s=-1;for(a.callActions("beforeGetFinalMixData",arguments),s=0;r=e.show[s];s++)e.showPosData[s].finalPosData=r.getPosData();for(s=0;r=e.toHide[s];s++)e.toHidePosData[s].finalPosData=r.getPosData();for((a.config.animation.clampHeight||a.config.animation.clampWidth)&&(a.dom.parent.style.height=a.dom.parent.style.width=a.dom.parent.style.overflow=""),a.incPadding||(i=t.getComputedStyle(a.dom.parent)),o=a.dom.parent.getBoundingClientRect(),e.newX=o.left,e.newY=o.top,e.newHeight=a.incPadding?o.height:o.height-parseFloat(i.paddingTop)-parseFloat(i.paddingBottom)-parseFloat(i.borderTop)-parseFloat(i.borderBottom),e.newWidth=a.incPadding?o.width:o.width-parseFloat(i.paddingLeft)-parseFloat(i.paddingRight)-parseFloat(i.borderLeft)-parseFloat(i.borderRight),e.viewportDeltaX=e.docState.viewportWidth-this.dom.document.documentElement.clientWidth,e.viewportDeltaY=e.docState.viewportHeight-this.dom.document.documentElement.clientHeight,e.willSort&&a.printSort(!0,e),s=0;r=e.toShow[s];s++)r.hide();for(s=0;r=e.toHide[s];s++)r.show();e.willChangeLayout&&(n.removeClass(a.dom.container,e.newContainerClassName),n.addClass(a.dom.container,a.config.layout.containerClassName)),a.callActions("afterGetFinalMixData",arguments)},getTweenData:function(t){var n=this,a=null,i=null,o=Object.getOwnPropertyNames(n.effectsIn),r="",s=null,l=-1,c=-1,u=-1,f=-1;for(n.callActions("beforeGetTweenData",arguments),u=0;a=t.show[u];u++)for(i=t.showPosData[u],i.posIn=new e.StyleData,i.posOut=new e.StyleData,i.tweenData=new e.StyleData,a.isShown?(i.posIn.x=i.startPosData.x-i.interPosData.x,i.posIn.y=i.startPosData.y-i.interPosData.y):i.posIn.x=i.posIn.y=0,i.posOut.x=i.finalPosData.x-i.interPosData.x,i.posOut.y=i.finalPosData.y-i.interPosData.y,i.posIn.opacity=a.isShown?1:n.effectsIn.opacity,i.posOut.opacity=1,i.tweenData.opacity=i.posOut.opacity-i.posIn.opacity,a.isShown||n.config.animation.nudge||(i.posIn.x=i.posOut.x,i.posIn.y=i.posOut.y),i.tweenData.x=i.posOut.x-i.posIn.x,i.tweenData.y=i.posOut.y-i.posIn.y,n.config.animation.animateResizeTargets&&(i.posIn.width=i.startPosData.width,i.posIn.height=i.startPosData.height,l=(i.startPosData.width||i.finalPosData.width)-i.interPosData.width,i.posIn.marginRight=i.startPosData.marginRight-l,c=(i.startPosData.height||i.finalPosData.height)-i.interPosData.height,i.posIn.marginBottom=i.startPosData.marginBottom-c,i.posOut.width=i.finalPosData.width,i.posOut.height=i.finalPosData.height,l=(i.finalPosData.width||i.startPosData.width)-i.interPosData.width,i.posOut.marginRight=i.finalPosData.marginRight-l,c=(i.finalPosData.height||i.startPosData.height)-i.interPosData.height,i.posOut.marginBottom=i.finalPosData.marginBottom-c,i.tweenData.width=i.posOut.width-i.posIn.width,i.tweenData.height=i.posOut.height-i.posIn.height,i.tweenData.marginRight=i.posOut.marginRight-i.posIn.marginRight,i.tweenData.marginBottom=i.posOut.marginBottom-i.posIn.marginBottom),f=0;r=o[f];f++)s=n.effectsIn[r],s instanceof e.TransformData&&s.value&&(i.posIn[r].value=s.value,i.posOut[r].value=0,i.tweenData[r].value=i.posOut[r].value-i.posIn[r].value,i.posIn[r].unit=i.posOut[r].unit=i.tweenData[r].unit=s.unit);for(u=0;a=t.toHide[u];u++)for(i=t.toHidePosData[u],i.posIn=new e.StyleData,i.posOut=new e.StyleData,i.tweenData=new e.StyleData,i.posIn.x=a.isShown?i.startPosData.x-i.interPosData.x:0,i.posIn.y=a.isShown?i.startPosData.y-i.interPosData.y:0,i.posOut.x=n.config.animation.nudge?0:i.posIn.x,i.posOut.y=n.config.animation.nudge?0:i.posIn.y,i.tweenData.x=i.posOut.x-i.posIn.x,i.tweenData.y=i.posOut.y-i.posIn.y,n.config.animation.animateResizeTargets&&(i.posIn.width=i.startPosData.width,i.posIn.height=i.startPosData.height,l=i.startPosData.width-i.interPosData.width,i.posIn.marginRight=i.startPosData.marginRight-l,c=i.startPosData.height-i.interPosData.height,i.posIn.marginBottom=i.startPosData.marginBottom-c),i.posIn.opacity=1,i.posOut.opacity=n.effectsOut.opacity,i.tweenData.opacity=i.posOut.opacity-i.posIn.opacity,f=0;r=o[f];f++)s=n.effectsOut[r],s instanceof e.TransformData&&s.value&&(i.posIn[r].value=0,i.posOut[r].value=s.value,i.tweenData[r].value=i.posOut[r].value-i.posIn[r].value,i.posIn[r].unit=i.posOut[r].unit=i.tweenData[r].unit=s.unit);n.callActions("afterGetTweenData",arguments)},moveTargets:function(t){var a=this,i=null,o=null,r=null,s="",l=!1,c=-1,u=-1,f=a.checkProgress.bind(a);for(a.callActions("beforeMoveTargets",arguments),u=0;i=t.show[u];u++)o=new e.IMoveData,r=t.showPosData[u],s=i.isShown?"none":"show",l=a.willTransition(s,t.hasEffect,r.posIn,r.posOut),l&&c++,i.show(),o.posIn=r.posIn,o.posOut=r.posOut,o.statusChange=s,o.staggerIndex=c,o.operation=t,o.callback=l?f:null,i.move(o);for(u=0;i=t.toHide[u];u++)r=t.toHidePosData[u],o=new e.IMoveData,s="hide",l=a.willTransition(s,r.posIn,r.posOut),o.posIn=r.posIn,o.posOut=r.posOut,o.statusChange=s,o.staggerIndex=u,o.operation=t,o.callback=l?f:null,i.move(o);a.config.animation.animateResizeContainer&&(a.dom.parent.style[e.features.transitionProp]="height "+a.config.animation.duration+"ms ease, width "+a.config.animation.duration+"ms ease ",requestAnimationFrame(function(){t.startHeight!==t.newHeight&&t.viewportDeltaY!==t.startHeight-t.newHeight&&(a.dom.parent.style.height=t.newHeight+"px"),t.startWidth!==t.newWidth&&t.viewportDeltaX!==t.startWidth-t.newWidth&&(a.dom.parent.style.width=t.newWidth+"px")})),t.willChangeLayout&&(n.removeClass(a.dom.container,a.config.layout.ContainerClassName),n.addClass(a.dom.container,t.newContainerClassName)),a.callActions("afterMoveTargets",arguments)},hasEffect:function(){var t=this,e=["scale","translateX","translateY","translateZ","rotateX","rotateY","rotateZ"],n="",a=null,i=!1,o=-1,r=-1;if(1!==t.effectsIn.opacity)return t.callFilters("resultHasEffect",!0,arguments);for(r=0;n=e[r];r++)if(a=t.effectsIn[n],o="undefined"!==a.value?a.value:a,0!==o){i=!0;break}return t.callFilters("resultHasEffect",i,arguments)},willTransition:function(t,e,a,i){var o=this,r=!1;return r=!!n.isVisible(o.dom.container)&&(!!("none"!==t&&e||a.x!==i.x||a.y!==i.y)||!!o.config.animation.animateResizeTargets&&(a.width!==i.width||a.height!==i.height||a.marginRight!==i.marginRight||a.marginTop!==i.marginTop)),o.callFilters("resultWillTransition",r,arguments)},checkProgress:function(t){var e=this;e.targetsDone++,e.targetsBound===e.targetsDone&&e.cleanUp(t)},cleanUp:function(t){var a=this,i=null,o=null,r=null,s=null,l=-1;for(a.callActions("beforeCleanUp",arguments),a.targetsMoved=a.targetsImmovable=a.targetsBound=a.targetsDone=0,l=0;i=t.show[l];l++)i.cleanUp(),i.show();for(l=0;i=t.toHide[l];l++)i.cleanUp(),i.hide();if(t.willSort&&a.printSort(!1,t),a.dom.parent.style[e.features.transitionProp]=a.dom.parent.style.height=a.dom.parent.style.width=a.dom.parent.style.overflow=a.dom.parent.style[e.features.perspectiveProp]=a.dom.parent.style[e.features.perspectiveOriginProp]="",t.willChangeLayout&&(n.removeClass(a.dom.container,t.startContainerClassName),n.addClass(a.dom.container,t.newContainerClassName)),t.toRemove.length){for(l=0;i=a.targets[l];l++)t.toRemove.indexOf(i)>-1&&((o=i.dom.el.previousSibling)&&"#text"===o.nodeName&&(r=i.dom.el.nextSibling)&&"#text"===r.nodeName&&n.removeWhitespace(o),t.willSort||a.dom.parent.removeChild(i.dom.el),a.targets.splice(l,1),i.isInDom=!1,l--);a.origOrder=a.targets}t.willSort&&(a.targets=t.newOrder),a.state=t.newState,a.lastOperation=t,a.dom.targets=a.state.targets,e.events.fire("mixEnd",a.dom.container,{state:a.state,instance:a},a.dom.document),"function"==typeof a.config.callbacks.onMixEnd&&a.config.callbacks.onMixEnd.call(a.dom.container,a.state,a),t.hasFailed&&(e.events.fire("mixFail",a.dom.container,{state:a.state,instance:a},a.dom.document),"function"==typeof a.config.callbacks.onMixFail&&a.config.callbacks.onMixFail.call(a.dom.container,a.state,a),n.addClass(a.dom.container,n.getClassname(a.config.classNames,"container",a.config.classNames.modifierFailed))),"function"==typeof a.userCallback&&a.userCallback.call(a.dom.container,a.state,a),"function"==typeof a.userDeferred.resolve&&a.userDeferred.resolve(a.state),a.userCallback=null,a.userDeferred=null,a.lastClicked=null,a.isToggling=!1,a.isBusy=!1,a.queue.length&&(a.callActions("beforeReadQueueCleanUp",arguments),s=a.queue.shift(),a.userDeferred=s.deferred,a.isToggling=s.isToggling,a.lastClicked=s.triggerElement,s.instruction.command instanceof e.CommandMultimix?a.multimix.apply(a,s.args):a.dataset.apply(a,s.args)),a.callActions("afterCleanUp",arguments)},parseMultimixArgs:function(t){var a=this,i=new e.UserInstruction,o=null,r=-1;for(i.animate=a.config.animation.enable,i.command=new e.CommandMultimix,r=0;r-1?i.command.position=o:"string"==typeof o?i.command.collection=n.arrayFromList(n.createElement(o).childNodes):"object"==typeof o&&n.isElement(o,a.dom.document)?i.command.collection.length?i.command.sibling=o:i.command.collection=[o]:"object"==typeof o&&o.length?i.command.collection.length?i.command.sibling=o[0]:i.command.collection=o:"object"==typeof o&&o.childNodes&&o.childNodes.length?i.command.collection.length?i.command.sibling=o.childNodes[0]:i.command.collection=n.arrayFromList(o.childNodes):"object"==typeof o?n.extend(i.command,o):"boolean"==typeof o?i.animate=o:"function"==typeof o&&(i.callback=o));if(i.command.index&&i.command.sibling)throw new Error(e.messages.errorInsertInvalidArguments());return!i.command.collection.length&&a.config.debug.showWarnings&&console.warn(e.messages.warningInsertNoElements()),i=a.callFilters("instructionParseInsertArgs",i,arguments),n.freeze(i),i},parseRemoveArgs:function(t){var a=this,i=new e.UserInstruction,o=null,r=null,s=-1;for(i.animate=a.config.animation.enable,i.command=new e.CommandRemove,s=0;s-1&&i.command.targets.push(o);return!i.command.targets.length&&a.config.debug.showWarnings&&console.warn(e.messages.warningRemoveNoElements()),n.freeze(i),i},parseDatasetArgs:function(t){var a=this,i=new e.UserInstruction,o=null,r=-1;for(i.animate=a.config.animation.enable,i.command=new e.CommandDataset,r=0;r-1&&t.toggleArray.splice(a,1),i=t.getToggleSelector(),t.multimix({filter:i},e.animate,e.callback)},sort:function(){var t=this,e=t.parseSortArgs(arguments);return t.multimix({sort:e.command},e.animate,e.callback)},changeLayout:function(){var t=this,e=t.parseChangeLayoutArgs(arguments);return t.multimix({changeLayout:e.command},e.animate,e.callback)},dataset:function(){var t=this,n=t.parseDatasetArgs(arguments),a=null,i=null,o=!1;return t.callActions("beforeDataset",arguments),t.isBusy?(i=new e.QueueItem,i.args=arguments,i.instruction=n,t.queueMix(i)):(n.callback&&(t.userCallback=n.callback),o=n.animate^t.config.animation.enable?n.animate:t.config.animation.enable,a=t.getDataOperation(n.command.dataset),t.goMix(o,a))},multimix:function(){var t=this,n=null,a=!1,i=null,o=t.parseMultimixArgs(arguments);return t.callActions("beforeMultimix",arguments),t.isBusy?(i=new e.QueueItem,i.args=arguments,i.instruction=o,i.triggerElement=t.lastClicked,i.isToggling=t.isToggling,t.queueMix(i)):(n=t.getOperation(o.command),t.config.controls.enable&&(o.command.filter&&!t.isToggling&&(t.toggleArray.length=0,t.buildToggleArray(n.command)),t.queue.length<1&&t.updateControls(n.command)),o.callback&&(t.userCallback=o.callback),a=o.animate^t.config.animation.enable?o.animate:t.config.animation.enable,t.callFilters("operationMultimix",n,arguments),t.goMix(a,n))},getOperation:function(t){var a=this,i=t.sort,o=t.filter,r=t.changeLayout,s=t.remove,l=t.insert,c=new e.Operation;return c=a.callFilters("operationUnmappedGetOperation",c,arguments),c.id=n.randomHex(),c.command=t,c.startState=a.state,c.triggerElement=a.lastClicked,a.isBusy?(a.config.debug.showWarnings&&console.warn(e.messages.warningGetOperationInstanceBusy()),null):(l&&a.insertTargets(l,c),s&&(c.toRemove=s.targets),c.startSort=c.newSort=c.startState.activeSort,c.startOrder=c.newOrder=a.targets,i&&(c.startSort=c.startState.activeSort,c.newSort=i,c.willSort=a.willSort(i,c.startState.activeSort),c.willSort&&a.sortOperation(c)),c.startFilter=c.startState.activeFilter,o?c.newFilter=o:c.newFilter=n.extend(new e.CommandFilter,c.startFilter),"all"===c.newFilter.selector?c.newFilter.selector=a.config.selectors.target:"none"===c.newFilter.selector&&(c.newFilter.selector=""),a.filterOperation(c),c.startContainerClassName=c.startState.activeContainerClassName,r?(c.newContainerClassName=r.containerClassName,c.newContainerClassName!==c.startContainerClassName&&(c.willChangeLayout=!0)):c.newContainerClassName=c.startContainerClassName,a.config.animation.enable&&(a.getStartMixData(c),a.setInter(c),c.docState=n.getDocumentState(a.dom.document),a.getInterMixData(c),a.setFinal(c),a.getFinalMixData(c),a.parseEffects(),c.hasEffect=a.hasEffect(),a.getTweenData(c)),c.willSort&&(a.targets=c.newOrder),c.newState=a.buildState(c),a.callFilters("operationMappedGetOperation",c,arguments))},tween:function(t,e){var n=null,a=null,i=-1,o=-1;for(e=Math.min(e,1),e=Math.max(e,0),o=0;n=t.show[o];o++)a=t.showPosData[o],n.applyTween(a,e);for(o=0;n=t.hide[o];o++)n.isShown&&n.hide(),(i=t.toHide.indexOf(n))>-1&&(a=t.toHidePosData[i],n.isShown||n.show(),n.applyTween(a,e))},insert:function(){var t=this,e=t.parseInsertArgs(arguments);return t.multimix({insert:e.command},e.animate,e.callback)},insertBefore:function(){var t=this,e=t.parseInsertArgs(arguments);return t.insert(e.command.collection,"before",e.command.sibling,e.animate,e.callback)},insertAfter:function(){var t=this,e=t.parseInsertArgs(arguments);return t.insert(e.command.collection,"after",e.command.sibling,e.animate,e.callback)},prepend:function(){var t=this,e=t.parseInsertArgs(arguments);return t.insert(0,e.command.collection,e.animate,e.callback)},append:function(){var t=this,e=t.parseInsertArgs(arguments);return t.insert(t.state.totalTargets,e.command.collection,e.animate,e.callback)},remove:function(){var t=this,e=t.parseRemoveArgs(arguments);return t.multimix({remove:e.command},e.animate,e.callback)},getConfig:function(t){var e=this,a=null;return a=t?n.getProperty(e.config,t):e.config,e.callFilters("valueGetConfig",a,arguments)},configure:function(t){var e=this;e.callActions("beforeConfigure",arguments),n.extend(e.config,t,!0,!0),e.callActions("afterConfigure",arguments)},getState:function(){var t=this,a=null;return a=new e.State,n.extend(a,t.state),n.freeze(a),t.callFilters("stateGetState",a,arguments)},forceRefresh:function(){var t=this;t.indexTargets()},forceRender:function(){var t=this,e=null,n=null,a="";for(a in t.cache)e=t.cache[a],n=e.render(e.data),n!==e.dom.el&&(e.isInDom&&(e.unbindEvents(),t.dom.parent.replaceChild(n,e.dom.el)),e.isShown||(n.style.display="none"),e.dom.el=n,e.isInDom&&e.bindEvents());t.state=t.buildState(t.lastOperation)},destroy:function(t){var n=this,a=null,i=null,o=0;for(n.callActions("beforeDestroy",arguments),o=0;a=n.controls[o];o++)a.removeBinding(n);for(o=0;i=n.targets[o];o++)t&&i.show(),i.unbindEvents();n.dom.container.id.match(/^MixItUp/)&&n.dom.container.removeAttribute("id"),delete e.instances[n.id],n.callActions("afterDestroy",arguments)}}),e.IMoveData=function(){e.Base.call(this),this.callActions("beforeConstruct"),this.posIn=null,this.posOut=null,this.operation=null,this.callback=null,this.statusChange="",this.duration=-1,this.staggerIndex=-1,this.callActions("afterConstruct"),n.seal(this)},e.BaseStatic.call(e.IMoveData),e.IMoveData.prototype=Object.create(e.Base.prototype),e.IMoveData.prototype.constructor=e.IMoveData,e.TargetDom=function(){e.Base.call(this),this.callActions("beforeConstruct"),this.el=null,this.callActions("afterConstruct"),n.seal(this)},e.BaseStatic.call(e.TargetDom),e.TargetDom.prototype=Object.create(e.Base.prototype),e.TargetDom.prototype.constructor=e.TargetDom,e.Target=function(){e.Base.call(this),this.callActions("beforeConstruct"),this.id="",this.sortString="",this.mixer=null,this.callback=null,this.isShown=!1,this.isBound=!1,this.isExcluded=!1,this.isInDom=!1,this.handler=null,this.operation=null,this.data=null,this.dom=new e.TargetDom,this.callActions("afterConstruct"),n.seal(this)},e.BaseStatic.call(e.Target),e.Target.prototype=Object.create(e.Base.prototype),n.extend(e.Target.prototype,{constructor:e.Target,init:function(t,n,a){var i=this,o="";if(i.callActions("beforeInit",arguments),i.mixer=n,t||(t=i.render(a)),i.cacheDom(t),i.bindEvents(),"none"!==i.dom.el.style.display&&(i.isShown=!0),a&&n.config.data.uidKey){if("undefined"==typeof(o=a[n.config.data.uidKey])||o.toString().length<1)throw new TypeError(e.messages.errorDatasetInvalidUidKey({uidKey:n.config.data.uidKey}));i.id=o,i.data=a,n.cache[o]=i}i.callActions("afterInit",arguments)},render:function(t){var a=this,i=null,o=null,r=null,s="";if(a.callActions("beforeRender",arguments),i=a.callFilters("renderRender",a.mixer.config.render.target,arguments),"function"!=typeof i)throw new TypeError(e.messages.errorDatasetRendererNotSet());return s=i(t),s&&"object"==typeof s&&n.isElement(s)?o=s:"string"==typeof s&&(r=document.createElement("div"),r.innerHTML=s,o=r.firstElementChild),a.callFilters("elRender",o,arguments)},cacheDom:function(t){var e=this;e.callActions("beforeCacheDom",arguments),e.dom.el=t,e.callActions("afterCacheDom",arguments)},getSortString:function(t){var e=this,n=e.dom.el.getAttribute("data-"+t)||"";e.callActions("beforeGetSortString",arguments),n=isNaN(1*n)?n.toLowerCase():1*n,e.sortString=n,e.callActions("afterGetSortString",arguments)},show:function(){var t=this;t.callActions("beforeShow",arguments),t.isShown||(t.dom.el.style.display="",t.isShown=!0),t.callActions("afterShow",arguments)},hide:function(){var t=this;t.callActions("beforeHide",arguments),t.isShown&&(t.dom.el.style.display="none",t.isShown=!1),t.callActions("afterHide",arguments)},move:function(t){var e=this;e.callActions("beforeMove",arguments),e.isExcluded||e.mixer.targetsMoved++,e.applyStylesIn(t),requestAnimationFrame(function(){e.applyStylesOut(t)}),e.callActions("afterMove",arguments)},applyTween:function(t,n){var a=this,i="",o=null,r=t.posIn,s=[],l=new e.StyleData,c=-1;for(a.callActions("beforeApplyTween",arguments),l.x=r.x,l.y=r.y,0===n?a.hide():a.isShown||a.show(),c=0;i=e.features.TWEENABLE[c];c++)if(o=t.tweenData[i],"x"===i){if(!o)continue;l.x=r.x+o*n}else if("y"===i){if(!o)continue;l.y=r.y+o*n}else if(o instanceof e.TransformData){if(!o.value)continue;l[i].value=r[i].value+o.value*n,l[i].unit=o.unit,s.push(i+"("+l[i].value+o.unit+")")}else{if(!o)continue;l[i]=r[i]+o*n,a.dom.el.style[i]=l[i]}(l.x||l.y)&&s.unshift("translate("+l.x+"px, "+l.y+"px)"),s.length&&(a.dom.el.style[e.features.transformProp]=s.join(" ")),a.callActions("afterApplyTween",arguments)},applyStylesIn:function(t){var n=this,a=t.posIn,i=1!==n.mixer.effectsIn.opacity,o=[];n.callActions("beforeApplyStylesIn",arguments),o.push("translate("+a.x+"px, "+a.y+"px)"),n.mixer.config.animation.animateResizeTargets&&("show"!==t.statusChange&&(n.dom.el.style.width=a.width+"px",n.dom.el.style.height=a.height+"px"),n.dom.el.style.marginRight=a.marginRight+"px",n.dom.el.style.marginBottom=a.marginBottom+"px"),i&&(n.dom.el.style.opacity=a.opacity),"show"===t.statusChange&&(o=o.concat(n.mixer.transformIn)),n.dom.el.style[e.features.transformProp]=o.join(" "),n.callActions("afterApplyStylesIn",arguments)},applyStylesOut:function(t){var n=this,a=[],i=[],o=n.mixer.config.animation.animateResizeTargets,r="undefined"!=typeof n.mixer.effectsIn.opacity;if(n.callActions("beforeApplyStylesOut",arguments),a.push(n.writeTransitionRule(e.features.transformRule,t.staggerIndex)),"none"!==t.statusChange&&a.push(n.writeTransitionRule("opacity",t.staggerIndex,t.duration)),o&&(a.push(n.writeTransitionRule("width",t.staggerIndex,t.duration)),a.push(n.writeTransitionRule("height",t.staggerIndex,t.duration)),a.push(n.writeTransitionRule("margin",t.staggerIndex,t.duration))),!t.callback)return n.mixer.targetsImmovable++,void(n.mixer.targetsMoved===n.mixer.targetsImmovable&&n.mixer.cleanUp(t.operation));switch(n.operation=t.operation,n.callback=t.callback,!n.isExcluded&&n.mixer.targetsBound++,n.isBound=!0,n.applyTransition(a),o&&t.posOut.width>0&&t.posOut.height>0&&(n.dom.el.style.width=t.posOut.width+"px",n.dom.el.style.height=t.posOut.height+"px",n.dom.el.style.marginRight=t.posOut.marginRight+"px",n.dom.el.style.marginBottom=t.posOut.marginBottom+"px"),n.mixer.config.animation.nudge||"hide"!==t.statusChange||i.push("translate("+t.posOut.x+"px, "+t.posOut.y+"px)"),t.statusChange){case"hide":r&&(n.dom.el.style.opacity=n.mixer.effectsOut.opacity),i=i.concat(n.mixer.transformOut);break;case"show":r&&(n.dom.el.style.opacity=1)}(n.mixer.config.animation.nudge||!n.mixer.config.animation.nudge&&"hide"!==t.statusChange)&&i.push("translate("+t.posOut.x+"px, "+t.posOut.y+"px)"),n.dom.el.style[e.features.transformProp]=i.join(" "),n.callActions("afterApplyStylesOut",arguments)},writeTransitionRule:function(t,e,n){var a=this,i=a.getDelay(e),o="";return o=t+" "+(n>0?n:a.mixer.config.animation.duration)+"ms "+i+"ms "+("opacity"===t?"linear":a.mixer.config.animation.easing),a.callFilters("ruleWriteTransitionRule",o,arguments)},getDelay:function(t){var e=this,n=-1;return"function"==typeof e.mixer.config.animation.staggerSequence&&(t=e.mixer.config.animation.staggerSequence.call(e,t,e.state)),n=e.mixer.staggerDuration?t*e.mixer.staggerDuration:0,e.callFilters("delayGetDelay",n,arguments)},applyTransition:function(t){var n=this,a=t.join(", ");n.callActions("beforeApplyTransition",arguments),n.dom.el.style[e.features.transitionProp]=a,n.callActions("afterApplyTransition",arguments)},handleTransitionEnd:function(t){var e=this,n=t.propertyName,a=e.mixer.config.animation.animateResizeTargets;e.callActions("beforeHandleTransitionEnd",arguments),e.isBound&&t.target.matches(e.mixer.config.selectors.target)&&(n.indexOf("transform")>-1||n.indexOf("opacity")>-1||a&&n.indexOf("height")>-1||a&&n.indexOf("width")>-1||a&&n.indexOf("margin")>-1)&&(e.callback.call(e,e.operation),e.isBound=!1,e.callback=null,e.operation=null),e.callActions("afterHandleTransitionEnd",arguments)},eventBus:function(t){var e=this;switch(e.callActions("beforeEventBus",arguments),t.type){case"webkitTransitionEnd":case"transitionend":e.handleTransitionEnd(t)}e.callActions("afterEventBus",arguments)},unbindEvents:function(){var t=this;t.callActions("beforeUnbindEvents",arguments),n.off(t.dom.el,"webkitTransitionEnd",t.handler),n.off(t.dom.el,"transitionend",t.handler),t.callActions("afterUnbindEvents",arguments)},bindEvents:function(){var t=this,a="";t.callActions("beforeBindEvents",arguments),a="webkit"===e.features.transitionPrefix?"webkitTransitionEnd":"transitionend",t.handler=function(e){return t.eventBus(e)},n.on(t.dom.el,a,t.handler),t.callActions("afterBindEvents",arguments)},getPosData:function(n){var a=this,i={},o=null,r=new e.StyleData;return a.callActions("beforeGetPosData",arguments),r.x=a.dom.el.offsetLeft,r.y=a.dom.el.offsetTop,(a.mixer.config.animation.animateResizeTargets||n)&&(o=a.dom.el.getBoundingClientRect(),r.top=o.top,r.right=o.right,r.bottom=o.bottom,r.left=o.left,r.width=o.width,r.height=o.height),a.mixer.config.animation.animateResizeTargets&&(i=t.getComputedStyle(a.dom.el),r.marginBottom=parseFloat(i.marginBottom),r.marginRight=parseFloat(i.marginRight)),a.callFilters("posDataGetPosData",r,arguments)},cleanUp:function(){var t=this;t.callActions("beforeCleanUp",arguments),t.dom.el.style[e.features.transformProp]="",t.dom.el.style[e.features.transitionProp]="",t.dom.el.style.opacity="",t.mixer.config.animation.animateResizeTargets&&(t.dom.el.style.width="",t.dom.el.style.height="",t.dom.el.style.marginRight="",t.dom.el.style.marginBottom=""),t.callActions("afterCleanUp",arguments)}}),e.Collection=function(t){var e=null,a=-1;for(this.callActions("beforeConstruct"),a=0;e=t[a];a++)this[a]=e;this.length=t.length,this.callActions("afterConstruct"),n.freeze(this)},e.BaseStatic.call(e.Collection),e.Collection.prototype=Object.create(e.Base.prototype),n.extend(e.Collection.prototype,{constructor:e.Collection,mixitup:function(t){var a=this,i=null,o=Array.prototype.slice.call(arguments),r=[],s=-1;for(this.callActions("beforeMixitup"),o.shift(),s=0;i=a[s];s++)r.push(i[t].apply(i,o));return a.callFilters("promiseMixitup",n.all(r,e.libraries),arguments)}}),e.Operation=function(){e.Base.call(this),this.callActions("beforeConstruct"),this.id="",this.args=[],this.command=null,this.showPosData=[],this.toHidePosData=[],this.startState=null,this.newState=null,this.docState=null,this.willSort=!1,this.willChangeLayout=!1,this.hasEffect=!1,this.hasFailed=!1,this.triggerElement=null,this.show=[],this.hide=[],this.matching=[],this.toShow=[],this.toHide=[],this.toMove=[],this.toRemove=[],this.startOrder=[],this.newOrder=[],this.startSort=null,this.newSort=null,this.startFilter=null,this.newFilter=null,this.startDataset=null,this.newDataset=null,this.viewportDeltaX=0,this.viewportDeltaY=0,this.startX=0,this.startY=0,this.startHeight=0,this.startWidth=0,this.newX=0,this.newY=0,this.newHeight=0,this.newWidth=0,this.startContainerClassName="",this.startDisplay="",this.newContainerClassName="",this.newDisplay="",this.callActions("afterConstruct"),n.seal(this)},e.BaseStatic.call(e.Operation),e.Operation.prototype=Object.create(e.Base.prototype),e.Operation.prototype.constructor=e.Operation,e.State=function(){e.Base.call(this),this.callActions("beforeConstruct"),this.id="",this.activeFilter=null,this.activeSort=null,this.activeContainerClassName="",this.container=null,this.targets=[],this.hide=[],this.show=[],this.matching=[],this.totalTargets=-1,this.totalShow=-1,this.totalHide=-1,this.totalMatching=-1,this.hasFailed=!1,this.triggerElement=null,this.activeDataset=null,this.callActions("afterConstruct"),n.seal(this)},e.BaseStatic.call(e.State),e.State.prototype=Object.create(e.Base.prototype),e.State.prototype.constructor=e.State,e.UserInstruction=function(){e.Base.call(this),this.callActions("beforeConstruct"),this.command={},this.animate=!1,this.callback=null,this.callActions("afterConstruct"),n.seal(this)},e.BaseStatic.call(e.UserInstruction),e.UserInstruction.prototype=Object.create(e.Base.prototype),e.UserInstruction.prototype.constructor=e.UserInstruction,e.Messages=function(){e.Base.call(this),this.callActions("beforeConstruct"),this.ERROR_FACTORY_INVALID_CONTAINER="[MixItUp] An invalid selector or element reference was passed to the mixitup factory function",this.ERROR_FACTORY_CONTAINER_NOT_FOUND="[MixItUp] The provided selector yielded no container element",this.ERROR_CONFIG_INVALID_ANIMATION_EFFECTS="[MixItUp] Invalid value for `animation.effects`",this.ERROR_CONFIG_INVALID_CONTROLS_SCOPE="[MixItUp] Invalid value for `controls.scope`",this.ERROR_CONFIG_INVALID_PROPERTY='[MixitUp] Invalid configuration object property "${erroneous}"${suggestion}',this.ERROR_CONFIG_INVALID_PROPERTY_SUGGESTION='. Did you mean "${probableMatch}"?',this.ERROR_CONFIG_DATA_UID_KEY_NOT_SET="[MixItUp] To use the dataset API, a UID key must be specified using `data.uidKey`",this.ERROR_DATASET_INVALID_UID_KEY='[MixItUp] The specified UID key "${uidKey}" is not present on one or more dataset items',this.ERROR_DATASET_DUPLICATE_UID='[MixItUp] The UID "${uid}" was found on two or more dataset items. UIDs must be unique.',this.ERROR_INSERT_INVALID_ARGUMENTS="[MixItUp] Please provider either an index or a sibling and position to insert, not both",this.ERROR_INSERT_PREEXISTING_ELEMENT="[MixItUp] An element to be inserted already exists in the container",this.ERROR_FILTER_INVALID_ARGUMENTS="[MixItUp] Please provide either a selector or collection `.filter()`, not both",this.ERROR_DATASET_NOT_SET="[MixItUp] To use the dataset API with pre-rendered targets, a starting dataset must be set using `load.dataset`",this.ERROR_DATASET_PRERENDERED_MISMATCH="[MixItUp] `load.dataset` does not match pre-rendered targets",this.ERROR_DATASET_RENDERER_NOT_SET="[MixItUp] To insert an element via the dataset API, a target renderer function must be provided to `render.target`",this.ERROR_SORT_NON_EXISTENT_ELEMENT="[MixItUp] An element to be sorted does not already exist in the container",this.WARNING_FACTORY_PREEXISTING_INSTANCE="[MixItUp] WARNING: This element already has an active MixItUp instance. The provided configuration object will be ignored. If you wish to perform additional methods on this instance, please create a reference.",this.WARNING_INSERT_NO_ELEMENTS="[MixItUp] WARNING: No valid elements were passed to `.insert()`",this.WARNING_REMOVE_NO_ELEMENTS="[MixItUp] WARNING: No valid elements were passed to `.remove()`",this.WARNING_MULTIMIX_INSTANCE_QUEUE_FULL="[MixItUp] WARNING: An operation was requested but the MixItUp instance was busy. The operation was rejected because the queue is full or queuing is disabled.",this.WARNING_GET_OPERATION_INSTANCE_BUSY="[MixItUp] WARNING: Operations can be be created while the MixItUp instance is busy.",this.WARNING_NO_PROMISE_IMPLEMENTATION="[MixItUp] WARNING: No Promise implementations could be found. If you wish to use promises with MixItUp please install an ES6 Promise polyfill.",this.WARNING_INCONSISTENT_SORTING_ATTRIBUTES='[MixItUp] WARNING: The requested sorting data attribute "${attribute}" was not present on one or more target elements which may product unexpected sort output',this.callActions("afterConstruct"),this.compileTemplates(),n.seal(this)},e.BaseStatic.call(e.Messages),e.Messages.prototype=Object.create(e.Base.prototype),e.Messages.prototype.constructor=e.Messages,e.Messages.prototype.compileTemplates=function(){var t="",e="";for(t in this)"string"==typeof(e=this[t])&&(this[n.camelCase(t)]=n.template(e))},e.messages=new e.Messages,e.Facade=function(t){e.Base.call(this),this.callActions("beforeConstruct",arguments),this.configure=t.configure.bind(t),this.show=t.show.bind(t),this.hide=t.hide.bind(t),this.filter=t.filter.bind(t),this.toggleOn=t.toggleOn.bind(t),this.toggleOff=t.toggleOff.bind(t),this.sort=t.sort.bind(t),this.changeLayout=t.changeLayout.bind(t),this.multimix=t.multimix.bind(t),this.dataset=t.dataset.bind(t),this.tween=t.tween.bind(t),this.insert=t.insert.bind(t),this.insertBefore=t.insertBefore.bind(t),this.insertAfter=t.insertAfter.bind(t),this.prepend=t.prepend.bind(t),this.append=t.append.bind(t),this.remove=t.remove.bind(t),this.destroy=t.destroy.bind(t),this.forceRefresh=t.forceRefresh.bind(t),this.forceRender=t.forceRender.bind(t),this.isMixing=t.isMixing.bind(t),this.getOperation=t.getOperation.bind(t),this.getConfig=t.getConfig.bind(t),this.getState=t.getState.bind(t),this.callActions("afterConstruct",arguments),n.freeze(this),n.seal(this)},e.BaseStatic.call(e.Facade),e.Facade.prototype=Object.create(e.Base.prototype),e.Facade.prototype.constructor=e.Facade,"object"==typeof exports&&"object"==typeof module?module.exports=e:"function"==typeof define&&define.amd?define(function(){return e}):"undefined"!=typeof t.mixitup&&"function"==typeof t.mixitup||(t.mixitup=e),e.BaseStatic.call(e.constructor),e.NAME="mixitup",e.CORE_VERSION="3.3.1"}(window); \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..e69de29 diff --git a/mixitup-3.3.1/.gitignore b/mixitup-3.3.1/.gitignore new file mode 100644 index 0000000..5e63733 --- /dev/null +++ b/mixitup-3.3.1/.gitignore @@ -0,0 +1,7 @@ +node_modules +components +.tmp +.DS_Store +.log +npm-debug.* +coverage/ \ No newline at end of file diff --git a/mixitup-3.3.1/.jscsrc b/mixitup-3.3.1/.jscsrc new file mode 100644 index 0000000..2d5d2e4 --- /dev/null +++ b/mixitup-3.3.1/.jscsrc @@ -0,0 +1,95 @@ +{ + "requireCurlyBraces": [ + "else", + "for", + "while", + "do", + "try", + "catch" + ], + "requireSpaceAfterKeywords": [ + "if", + "else", + "for", + "while", + "do", + "case", + "return", + "try", + "typeof" + ], + "safeContextKeyword": ["self"], + "maximumLineLength": { + "value": 140, + "allowComments": true, + "allowRegex": true + }, + "requireSpaceBeforeBlockStatements": true, + "requireParenthesesAroundIIFE": true, + "requireSpaceAfterLineComment": { + "allExcept": ["#", "="] + }, + "requireSpacesInConditionalExpression": true, + "disallowSpacesInNamedFunctionExpression": { + "beforeOpeningRoundBrace": true + }, + "disallowSpacesInFunctionDeclaration": { + "beforeOpeningRoundBrace": true + }, + "disallowFunctionDeclarations": false, + "requireSpaceBetweenArguments": true, + "requireMultipleVarDecl": false, + "requireBlocksOnNewline": true, + "requireSemicolons": true, + "disallowEmptyBlocks": true, + "disallowSpacesInsideArrayBrackets": true, + "disallowSpacesInsideParentheses": true, + "requireCommaBeforeLineBreak": true, + "requireLineBreakAfterVariableAssignment": true, + "requirePaddingNewlinesBeforeKeywords": [ + "do", + "for", + "if", + "switch", + "try", + "void", + "while", + "return" + ], + "requirePaddingNewLinesInObjects": true, + "disallowSpaceAfterPrefixUnaryOperators": true, + "disallowSpaceBeforePostfixUnaryOperators": true, + "disallowSpaceBeforeBinaryOperators": [ + "," + ], + "requireSpacesInForStatement": true, + "requireSpaceBeforeBinaryOperators": true, + "requireSpaceAfterBinaryOperators": true, + "disallowKeywords": [ + "with" + ], + "validateIndentation": 4, + "disallowMixedSpacesAndTabs": true, + "disallowTrailingWhitespace": true, + "disallowTrailingComma": true, + "disallowKeywordsOnNewLine": [ + "else" + ], + "requireCapitalizedConstructors": true, + "disallowNewlineBeforeBlockStatements": true, + "disallowMultipleLineStrings": true, + "disallowMultipleLineBreaks": true, + "requireSpaceBeforeObjectValues": true, + "validateQuoteMarks": "'", + "jsDoc": { + "checkAnnotations": true, + "requireParamTypes": true, + "checkParamNames": true, + "checkParamExistence": true, + // "checkRedundantParams": true, + "checkReturnTypes": true, + "requireNewlineAfterDescription": true, + // "requireParamDescription": true, + "requireDescriptionCompleteSentence": true + } +} \ No newline at end of file diff --git a/mixitup-3.3.1/.jshintrc b/mixitup-3.3.1/.jshintrc new file mode 100644 index 0000000..425b4e1 --- /dev/null +++ b/mixitup-3.3.1/.jshintrc @@ -0,0 +1,24 @@ +{ + "esnext": true, + "-W030": true, + "-W053": true, + "-W084": true, + "-W055": true, + "-W069": true, + "-W097": true, + "-W087": true, + "undef": true, + "unused": true, + "laxbreak": true, + "node": true, + "browser": true, + "predef": [ + "window", + "Promise", + "define", + "console", + "describe", + "after", + "it" + ] +} \ No newline at end of file diff --git a/mixitup-3.3.1/.travis.yml b/mixitup-3.3.1/.travis.yml new file mode 100644 index 0000000..23c6a7b --- /dev/null +++ b/mixitup-3.3.1/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - "node" +after_success: + - npm run coveralls \ No newline at end of file diff --git a/mixitup-3.3.1/CHANGELOG.md b/mixitup-3.3.1/CHANGELOG.md new file mode 100644 index 0000000..455a745 --- /dev/null +++ b/mixitup-3.3.1/CHANGELOG.md @@ -0,0 +1,94 @@ +Change Log +========== + +## 3.3.1 +- Insures dataset uses polyfilled `Array.from` for old IE support. +- Fixes support for sorting via element collection. This was not correctly implemented before although documented. +- Fixes the "filter-by-url" demo to ensure that only leading `'.'` characters are stripped off rather than any character. + +## 3.3.0 +- Introduces new internal filter hook `testResultEvaluateHideShown` allowing plugins to manipulate the result of every filter test upon a target. Provides an convenient entry point for non-selector based filtering such as range slider inputs. +- Adds range slider demo. + +## 3.2.2 +- Fixes issue where multiple toggle controls are not automatically activated when a compound selector is specified to `load.filter`. +- Fixes issue where calling `.toggleOff()` and passing a non-existent selector will deactivate all other active toggles. +- Fixes issue where padding whitespace around a DOM string when calling `.insert()` caused an exception. +- Adds additional demos for non-standard UI. + +## 3.2.1 +- Additional edge-case work relating to Dataset API fix in v3.2.0. +- Addition of `.forceRender()` mixer API method. +- Removes `.multiMix()` legacy API alias method. + +## 3.2.0 +- Removes support for legacy `$().mixItUp()` jQuery API +- Fixes issue with Dataset API causing DOM exception when dealing with certain combinations of simultaneous insertion and sorting. + +## 3.1.12 +- Fixes issue where `state.targets` does not reflect the updated sort order after a sort operation. +- Addition of `behavior.liveSort` configuration option. + +## 3.1.11 + +- Various geometry improvements related to scroll bar issues on desktop Windows and (non-inertial scroll) desktop Mac systems. +- Addition of `animation.clampWidth` configuration option. + +## 3.1.10 + +- Fixes an issue where the `activeContainerClass` did not persist between non-layout-change operations (e.g. sort, filter). + +## 3.1.9 + +- Fixes an issue relating to `animation.clampHeight` where the height was not correctly applied causing scroll jumping in certain situations. + +## 3.1.8 + +- Adds ability to bind live controls where multiple parents exist. Required for Pagination 3.2.0. + +## 3.1.7 + +- Fixed duplicate hook `beforeCacheDom` in `Target#cacheDom`. Now renamed to `afterCacheDom`. + +## 3.1.6 + +- Added composer.json + +## 3.1.5 + +- Fixed several issues relating to the Dataset API and multimix-like operations (i.e. simultaneous insertion/removal/sorting/dirty-checking) + +## 3.1.4 + +- Added ability to extend static factory methods (such as `mixitup.use`) with hooks. +- Added ability to return a single DOM element from `render.target` instead of an HTML string +- Moved target rendering functionality into `Target` class, so that targets can render themselves +- Force disable controls if dataset API is in use (if `data.uidKey` is set) + +## 3.1.3 + +- Exposed `.toggleOn()` and `.toggleOff()` API methods publicly via the mixer facade, as were accidently missed out previously. + +## 3.1.2 + +- Improved `compareVersions` util function to handle semver notation correctly (e.g. `'^'`, `'~'`, `'-beta'`, etc). +- Fixed issue with "Filtering by URL" demo that added a `#mix` segment to the URL for filter "all" + +## 3.1.1 + +- Fixed issue where `transitionend` event handlers were not rebound to re-rendered targets during dirtyCheck updates. +- Fixed issue where dataset operation objects where created on push to queue, resulting in corrupted target data. + +## 3.1.0 + +- Added `selectors.controls` configuration option to allow for further specificity of control querying +in addition to the mandatory data attributes. +- Fixed package.json issues. + +## 3.0.1 + +- Fixed issue where `layout.containerClassName` is not reflected in state object after instantiation. + +## 3.0.0 + +- Release \ No newline at end of file diff --git a/mixitup-3.3.1/README.md b/mixitup-3.3.1/README.md new file mode 100644 index 0000000..a9cdb09 --- /dev/null +++ b/mixitup-3.3.1/README.md @@ -0,0 +1,208 @@ +# MixItUp 3 + +[![Build Status](https://travis-ci.org/patrickkunka/mixitup.svg?branch=v3)](https://travis-ci.org/patrickkunka/mixitup) +[![Coverage Status](https://coveralls.io/repos/github/patrickkunka/mixitup/badge.svg?branch=v3)](https://coveralls.io/github/patrickkunka/mixitup?branch=v3) +[![jsDelivr Hits](https://data.jsdelivr.com/v1/package/gh/patrickkunka/mixitup/badge?style=rounded)](https://www.jsdelivr.com/package/gh/patrickkunka/mixitup) + +MixItUp is a high-performance, dependency-free library for animated DOM manipulation, giving you the power to filter, sort, add and remove DOM elements with beautiful animations. + +MixItUp plays nice with your existing HTML and CSS, making it a great choice for responsive layouts and compatible with inline-flow, percentages, media queries, flexbox and more. + +For a live sandbox, full documentation, tutorials and more, please visit [kunkalabs.com/mixitup](https://www.kunkalabs.com/mixitup/). + +Migrating from MixItUp 2? Check out the [MixItUp 3 Migration Guide](./docs/mixitup-3-migration-guide.md). + +#### Licensing + +MixItUp is open source and free to use for non-commercial, educational and non-profit use. For use in commercial projects, **a commercial license is required**. For licensing information and FAQs please see the [MixItUp Licenses](https://www.kunkalabs.com/mixitup/licenses/) page. + +#### Documentation + +- [Factory Function](./docs/mixitup.md) +- [Configuration Object](./docs/mixitup.Config.md) +- [Mixer API Methods](./docs/mixitup.Mixer.md) +- [State Object](./docs/mixitup.State.md) +- [Mixer Events](./docs/mixitup.Events.md) + +#### Browser Support + +MixItUp 3 has been tested for compatibility with the following browsers. + +- Chrome 16+ +- Firefox 16+ +- Safari 6.2+ +- Yandex 14+ +- Edge 13+ +- IE 10+ (with animations), IE 8-9 (no animations) + +## Getting Started + +#### Contents + +- [Building the Container](#building-the-container) +- [Building Controls](#building-controls) +- [Styling the Container](#styling-the-container) +- [Loading MixItUp](#loading-mixitup) +- [Creating a Mixer](#creating-a-mixer) +- [Configuration](#configuration) + +Most commonly, MixItUp is applied to a **"container"** of **"target"** elements, which could be a portfolio of projects, a list of blog posts, a selection of products, or any kind of UI where filtering and/or sorting would be advantageous. + +To get started, follow these simple steps: + +### Building the Container + +By default, MixItUp will query the container for targets matching the selector `'.mix'`. + +```html +
+
+
+
+
+
+``` + +Targets can be filtered using any valid selector e.g. `'.category-a'`, and are sorted via optional custom data attributes e.g. `'data-order'`. + +Further reading: [Marking-up MixItUp Containers](https://www.kunkalabs.com/tutorials/marking-up-mixitup-containers/) + +### Building Controls + +One way that filtering and sorting happens is when controls are clicked. You may use any clickable element as a control, but ` + + + +``` + +Further reading: [Filtering with MixItUp](https://www.kunkalabs.com/tutorials/filtering-with-mixitup/) + +#### Sort Controls + +Sort controls are queried based on the presence of a `data-sort` attribute, whose value takes the form of a "sort string" made up of the name of the attribute to sort by, followed by an optional colon-separated sorting order e.g. `'order'`, `'order:asc'`, `'order:desc'`. + +```html + + + +``` + +The values `'default'` and `'random'` are also valid, with `'default'` referring to the original order of target elements in the DOM at the time of mixer instantiation. + +Further reading: [Sorting with MixItUp](https://www.kunkalabs.com/tutorials/sorting-with-mixitup/) + +### Styling the Container + +While MixItUp can be added on top of any existing CSS layout, we strongly recommend inline-block or flexbox-based styling over floats and legacy grid frameworks when dealing with grid-based designs for a number of reasons. + +Further reading: [MixItUp Grid Layouts](https://www.kunkalabs.com/tutorials/mixitup-grid-layouts/) + +### Loading MixItUp + +Firstly, load the MixItUp JavaScript library using the preferred method for your project. + +#### Script Tag + +The most simple way to load MixItUp in your project is to include it via a ` + + +``` + +With this technique, the MixItUp factory function will be made available via the global variable `mixitup`. + +#### Module Import + +If you are building a modular JavaScript project with Webpack, Browserify, or RequireJS, MixItUp can be installed using your package manager of choice (e.g. npm, jspm, yarn) and then imported into any of your project's modules. + +`npm install mixitup --save` + +```js +// ES2015 + +import mixitup from 'mixitup'; +``` + +```js +// CommonJS + +var mixitup = require('mixitup'); +``` + +```js +// AMD + +require(['mixitup'], function(mixitup) { + +}); +``` + +### Creating a Mixer + +With the `mixitup()` factory function available, you may now instantiate a "mixer" on your container to enable MixItUp functionality. + +Call the factory function passing a selector string or a reference to your container element as the first parameter, and a the newly instantiated mixer will be returned. + +###### Example: Instantiating a mixer with a selector string + +```js +var mixer = mixitup('.container'); +``` + +###### Example: Instantiating a mixer with an element reference + +```js +var mixer = mixitup(containerEl); +``` + +Your mixer is now ready for you to interact with, either via its controls (see above), or its API (see [Mixer API Methods](./docs/mixitup.Mixer.md)). Click a control or call an API method to check that everything is working correctly. + +### Configuration + +If you wish to customize the functionality of your mixer, an optional "configuration object" can be passed as the second parameter to the `mixitup` function. If no configuration object is passed, the default settings will be used. + +Further reading: [Configuration Object](/docs/mixitup.Config.md) + +###### Example: Passing a configuration object + +```js +var mixer = mixitup(containerEl, { + selectors: { + target: '.blog-item' + }, + animation: { + duration: 300 + } +}); +``` + +#### Using the API + +If you wish to interact with your mixer via its API, the mixer reference returned by the factory function can be used to call API methods. + +###### Example: Calling an API method + +```js +var mixer = mixitup(containerEl); + +mixer.filter('.category-a'); +``` + +Further reading: [Mixer API Methods](./docs/mixitup.Mixer.md) + +#### Building a modern JavaScript application? + +You may wish to use MixItUp 3's new "dataset" API. Dataset is designed for use in API-driven JavaScript applications, and can be used instead of DOM-based methods such as `.filter()`, `.sort()`, `.insert()`, etc. When used, insertion, removal, sorting and pagination can be achieved purely via changes to your data model, without the uglyness of having to interact with or query the DOM directly. + +Further reading: [Using the Dataset API](https://www.kunkalabs.com/tutorials/using-the-dataset-api/) diff --git a/mixitup-3.3.1/composer.json b/mixitup-3.3.1/composer.json new file mode 100644 index 0000000..2115a8e --- /dev/null +++ b/mixitup-3.3.1/composer.json @@ -0,0 +1,19 @@ +{ + "name": "patrickkunka/mixitup", + "type": "library", + "homepage": "https://www.kunkalabs.com/mixitup/", + "license": [ + "proprietary" + ], + "authors": [ + { + "name": "KunkaLabs Limited", + "homepage": "https://www.kunkalabs.com/" + } + ], + "support": { + "docs": "https://www.kunkalabs.com/mixitup/docs/get-started/" + }, + "description": "A high-performance, dependency-free library for animated filtering, sorting and more", + "keywords": ["javascript", "animate", "filtering", "sorting"] +} \ No newline at end of file diff --git a/mixitup-3.3.1/demos/attribute-selectors/index.html b/mixitup-3.3.1/demos/attribute-selectors/index.html new file mode 100644 index 0000000..f5d73a0 --- /dev/null +++ b/mixitup-3.3.1/demos/attribute-selectors/index.html @@ -0,0 +1,50 @@ + + + + + + + + + MixItUp Demo - Querying via Attribute Selectors + + +
+ + + + + + + + +
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/attribute-selectors/style.css b/mixitup-3.3.1/demos/attribute-selectors/style.css new file mode 100644 index 0000000..075005c --- /dev/null +++ b/mixitup-3.3.1/demos/attribute-selectors/style.css @@ -0,0 +1,190 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 1rem; + background: #333; + font-size: 0.1px; +} + +.control { + position: relative; + display: inline-block; + width: 2.7rem; + height: 2.7rem; + background: #444; + cursor: pointer; + font-size: 0.1px; + color: white; + transition: background 150ms; +} + +.control:hover { + background: #3f3f3f; +} + +.control[data-filter]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + top: calc(50% - 6px); + left: calc(50% - 6px); + border: 2px solid currentColor; + border-radius: 2px; + background: currentColor; + transition: background-color 150ms, border-color 150ms; +} + +.control[data-sort]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + border-top: 2px solid; + border-left: 2px solid; + top: calc(50% - 6px); + left: calc(50% - 6px); + transform: translateY(1px) rotate(45deg); +} + +.control[data-sort*=":desc"]:after { + transform: translateY(-4px) rotate(-135deg); +} + +.mixitup-control-active { + background: #393939; +} + +.mixitup-control-active[data-filter]:after { + background: transparent; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +.control[data-filter] + .control[data-sort] { + margin-left: .75rem; +} + +.control[data-filter=".green"] { + color: #91e6c7; +} + +.control[data-filter=".blue"] { + color: #5ecdde; +} + +.control[data-filter=".pink"] { + color: #d595aa; +} + +.control[data-filter="none"] { + color: #2f2f2f; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + text-align: justify; + font-size: 0.1px; +} + +.container:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Grid Items +---------------------------------------------------------------------- */ + +.item, +.gap { + display: inline-block; + vertical-align: top; +} + +.item { + background: #fff; + border-top: .5rem solid currentColor; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; +} + +.item:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +.item.green { + color: #91e6c7; +} + +.item.pink { + color: #d595aa; +} + +.item.blue { + color: #5ecdde; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.item, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .item, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .item, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .item, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} + + diff --git a/mixitup-3.3.1/demos/basic-ie-8/index.html b/mixitup-3.3.1/demos/basic-ie-8/index.html new file mode 100644 index 0000000..1c56ca8 --- /dev/null +++ b/mixitup-3.3.1/demos/basic-ie-8/index.html @@ -0,0 +1,46 @@ + + + + + + + + + MixItUp Demo - Basic (IE8 Compatible Layout) + + +
+ + + + + + + + +
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/basic-ie-8/style.css b/mixitup-3.3.1/demos/basic-ie-8/style.css new file mode 100644 index 0000000..e421567 --- /dev/null +++ b/mixitup-3.3.1/demos/basic-ie-8/style.css @@ -0,0 +1,122 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 16px; + background: #333; + font-size: 0.1px; +} + +.control { + position: relative; + display: inline-block; + vertical-align: top; + padding: 10px; + font-size: 15px; + height: 35px; + min-width: 25px; + background: #444; + cursor: pointer; + color: white; + font-weight: 800; + transition: background 150ms; +} + +.control:hover { + background: #3f3f3f; +} + +.control[data-sort*=":desc"]:after { + transform: translateY(-4px) rotate(-135deg); +} + +.mixitup-control-active { + background: #393939; +} + +.control[data-filter="all"] { + background: #fff; +} + +.control[data-filter=".green"] { + background: #91e6c7; +} + +.control[data-filter=".blue"] { + background: #5ecdde; +} + +.control[data-filter=".pink"] { + background: #d595aa; +} + +.control[data-filter="none"] { + background: #2f2f2f; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 16px; + text-align: justify; + font-size: 0.1px; +} + +.container:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix, +.gap { + display: inline-block; + vertical-align: top; +} + +.mix { + background: #fff; + border-top: 8px solid transparent; + border-radius: 2px; + margin-bottom: 1.25%; + position: relative; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +.mix.green { + border-top-color: #91e6c7; +} + +.mix.pink { + border-top-color: #d595aa; +} + +.mix.blue { + border-top-color: #5ecdde; +} + +.mix, +.gap { + width: 24%; +} \ No newline at end of file diff --git a/mixitup-3.3.1/demos/basic/index.html b/mixitup-3.3.1/demos/basic/index.html new file mode 100644 index 0000000..7a93271 --- /dev/null +++ b/mixitup-3.3.1/demos/basic/index.html @@ -0,0 +1,46 @@ + + + + + + + + + MixItUp Demo - Basic + + +
+ + + + + + + + +
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/basic/style.css b/mixitup-3.3.1/demos/basic/style.css new file mode 100644 index 0000000..eb08e2a --- /dev/null +++ b/mixitup-3.3.1/demos/basic/style.css @@ -0,0 +1,190 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 1rem; + background: #333; + font-size: 0.1px; +} + +.control { + position: relative; + display: inline-block; + width: 2.7rem; + height: 2.7rem; + background: #444; + cursor: pointer; + font-size: 0.1px; + color: white; + transition: background 150ms; +} + +.control:hover { + background: #3f3f3f; +} + +.control[data-filter]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + top: calc(50% - 6px); + left: calc(50% - 6px); + border: 2px solid currentColor; + border-radius: 2px; + background: currentColor; + transition: background-color 150ms, border-color 150ms; +} + +.control[data-sort]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + border-top: 2px solid; + border-left: 2px solid; + top: calc(50% - 6px); + left: calc(50% - 6px); + transform: translateY(1px) rotate(45deg); +} + +.control[data-sort*=":desc"]:after { + transform: translateY(-4px) rotate(-135deg); +} + +.mixitup-control-active { + background: #393939; +} + +.mixitup-control-active[data-filter]:after { + background: transparent; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +.control[data-filter] + .control[data-sort] { + margin-left: .75rem; +} + +.control[data-filter=".green"] { + color: #91e6c7; +} + +.control[data-filter=".blue"] { + color: #5ecdde; +} + +.control[data-filter=".pink"] { + color: #d595aa; +} + +.control[data-filter="none"] { + color: #2f2f2f; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + text-align: justify; + font-size: 0.1px; +} + +.container:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix, +.gap { + display: inline-block; + vertical-align: top; +} + +.mix { + background: #fff; + border-top: .5rem solid currentColor; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +.mix.green { + color: #91e6c7; +} + +.mix.pink { + color: #d595aa; +} + +.mix.blue { + color: #5ecdde; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.mix, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .mix, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .mix, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .mix, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} + + diff --git a/mixitup-3.3.1/demos/checkboxes-with-reset-checkbox/index.html b/mixitup-3.3.1/demos/checkboxes-with-reset-checkbox/index.html new file mode 100644 index 0000000..08db519 --- /dev/null +++ b/mixitup-3.3.1/demos/checkboxes-with-reset-checkbox/index.html @@ -0,0 +1,108 @@ + + + + + + + + + MixItUp Demo - Checkboxes with Reset Checkbox + + +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ + + +
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/checkboxes-with-reset-checkbox/style.css b/mixitup-3.3.1/demos/checkboxes-with-reset-checkbox/style.css new file mode 100644 index 0000000..0883fda --- /dev/null +++ b/mixitup-3.3.1/demos/checkboxes-with-reset-checkbox/style.css @@ -0,0 +1,200 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 1rem; + background: #333; + font-size: 0.1px; +} + +.control { + position: relative; + display: inline-block; + width: 2.7rem; + height: 2.7rem; + background: #444; + cursor: pointer; + font-size: 0.1px; + color: white; + transition: background 150ms; +} + +.control:hover { + background: #3f3f3f; +} + +.control[data-filter]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + top: calc(50% - 6px); + left: calc(50% - 6px); + border: 2px solid currentColor; + border-radius: 2px; + background: currentColor; + transition: background-color 150ms, border-color 150ms; +} + +.control[data-sort]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + border-top: 2px solid; + border-left: 2px solid; + top: calc(50% - 6px); + left: calc(50% - 6px); + transform: translateY(1px) rotate(45deg); +} + +.control[data-sort*=":desc"]:after { + transform: translateY(-4px) rotate(-135deg); +} + +.mixitup-control-active { + background: #393939; +} + +.mixitup-control-active[data-filter]:after { + background: transparent; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +.checkbox-group { + display: inline-block; + padding: .5rem; + background: #2a2a2a; + margin-right: .75rem; + vertical-align: top; +} + +.checkbox { + text-align: justify; +} + +.checkbox:after { + content: ''; + display: inline-block; + width: 100%; +} + +.checkbox-input, +.checkbox-label { + display: inline-block; +} + +.checkbox-label { + color: white; + font-family: 'helvetica-neue', arial, sans-serif; + font-size: .9rem; + margin-right: .5rem; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + text-align: justify; + font-size: 0.1px; +} + +.container:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix, +.gap { + display: inline-block; + vertical-align: top; +} + +.mix { + background: #fff; + border-top: .5rem solid currentColor; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +.mix.green { + color: #91e6c7; +} + +.mix.pink { + color: #d595aa; +} + +.mix.blue { + color: #5ecdde; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.mix, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .mix, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .mix, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .mix, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} + + diff --git a/mixitup-3.3.1/demos/checkboxes/index.html b/mixitup-3.3.1/demos/checkboxes/index.html new file mode 100644 index 0000000..52f799b --- /dev/null +++ b/mixitup-3.3.1/demos/checkboxes/index.html @@ -0,0 +1,86 @@ + + + + + + + + + MixItUp Demo - Checkboxes + + +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+ + + +
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/checkboxes/style.css b/mixitup-3.3.1/demos/checkboxes/style.css new file mode 100644 index 0000000..0883fda --- /dev/null +++ b/mixitup-3.3.1/demos/checkboxes/style.css @@ -0,0 +1,200 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 1rem; + background: #333; + font-size: 0.1px; +} + +.control { + position: relative; + display: inline-block; + width: 2.7rem; + height: 2.7rem; + background: #444; + cursor: pointer; + font-size: 0.1px; + color: white; + transition: background 150ms; +} + +.control:hover { + background: #3f3f3f; +} + +.control[data-filter]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + top: calc(50% - 6px); + left: calc(50% - 6px); + border: 2px solid currentColor; + border-radius: 2px; + background: currentColor; + transition: background-color 150ms, border-color 150ms; +} + +.control[data-sort]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + border-top: 2px solid; + border-left: 2px solid; + top: calc(50% - 6px); + left: calc(50% - 6px); + transform: translateY(1px) rotate(45deg); +} + +.control[data-sort*=":desc"]:after { + transform: translateY(-4px) rotate(-135deg); +} + +.mixitup-control-active { + background: #393939; +} + +.mixitup-control-active[data-filter]:after { + background: transparent; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +.checkbox-group { + display: inline-block; + padding: .5rem; + background: #2a2a2a; + margin-right: .75rem; + vertical-align: top; +} + +.checkbox { + text-align: justify; +} + +.checkbox:after { + content: ''; + display: inline-block; + width: 100%; +} + +.checkbox-input, +.checkbox-label { + display: inline-block; +} + +.checkbox-label { + color: white; + font-family: 'helvetica-neue', arial, sans-serif; + font-size: .9rem; + margin-right: .5rem; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + text-align: justify; + font-size: 0.1px; +} + +.container:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix, +.gap { + display: inline-block; + vertical-align: top; +} + +.mix { + background: #fff; + border-top: .5rem solid currentColor; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +.mix.green { + color: #91e6c7; +} + +.mix.pink { + color: #d595aa; +} + +.mix.blue { + color: #5ecdde; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.mix, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .mix, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .mix, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .mix, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} + + diff --git a/mixitup-3.3.1/demos/dataset-empty-container/index.html b/mixitup-3.3.1/demos/dataset-empty-container/index.html new file mode 100644 index 0000000..c9aaa4f --- /dev/null +++ b/mixitup-3.3.1/demos/dataset-empty-container/index.html @@ -0,0 +1,226 @@ + + + + + + + + + MixItUp Demo - Dataset with an Empty Container + + +
+ + + + + + + + +
+ +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/dataset-empty-container/style.css b/mixitup-3.3.1/demos/dataset-empty-container/style.css new file mode 100644 index 0000000..67d44fa --- /dev/null +++ b/mixitup-3.3.1/demos/dataset-empty-container/style.css @@ -0,0 +1,199 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 1rem; + background: #333; + font-size: 0.1px; +} + +.control { + position: relative; + display: inline-block; + width: 2.7rem; + height: 2.7rem; + background: #444; + cursor: pointer; + font-size: 0.1px; + color: white; + transition: background 150ms; +} + +.control:not(.control-active):hover { + background: #3f3f3f; +} + +.control-filter:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + top: calc(50% - 6px); + left: calc(50% - 6px); + border: 2px solid currentColor; + border-radius: 2px; + background: currentColor; + transition: background-color 150ms, border-color 150ms; +} + +.control-sort:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + border-top: 2px solid; + border-left: 2px solid; + top: calc(50% - 6px); + left: calc(50% - 6px); + transform: translateY(1px) rotate(45deg); +} + +.control-sort[data-order="desc"]:after { + transform: translateY(-4px) rotate(-135deg); +} + +.control-active { + background: #393939; +} + +.control-filter.control-active:after { + background: transparent; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +.control-filter + .control-sort { + margin-left: .75rem; +} + +.control-filter[data-color="green"] { + color: #91e6c7; +} + +.control-filter[data-color="blue"] { + color: #5ecdde; +} + +.control-filter[data-color="pink"] { + color: #d595aa; +} + +.control-filter[data-color="none"] { + color: #2f2f2f; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + text-align: justify; + font-size: 0.1px; +} + +.container:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.item, +.gap { + display: inline-block; + vertical-align: top; +} + +.item { + background: #fff; + border-top: .5rem solid currentColor; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; +} + +.item:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +.item.green { + color: #91e6c7; +} + +.item.pink { + color: #d595aa; +} + +.item.blue { + color: #5ecdde; +} + +.item .published-date { + position: absolute; + top: 0; + left: 0; + padding: 1rem; + font-size: 1rem; + color: #333; + font-family: 'helvetica-neue', arial, sans-serif; + font-weight: 700; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.item, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .item, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .item, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .item, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} \ No newline at end of file diff --git a/mixitup-3.3.1/demos/dataset-pre-rendered-targets/index.html b/mixitup-3.3.1/demos/dataset-pre-rendered-targets/index.html new file mode 100644 index 0000000..9097f4d --- /dev/null +++ b/mixitup-3.3.1/demos/dataset-pre-rendered-targets/index.html @@ -0,0 +1,256 @@ + + + + + + + + + MixItUp Demo - Dataset with Pre-rendered Targets + + +
+ + + + + + + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/dataset-pre-rendered-targets/style.css b/mixitup-3.3.1/demos/dataset-pre-rendered-targets/style.css new file mode 100644 index 0000000..b88f764 --- /dev/null +++ b/mixitup-3.3.1/demos/dataset-pre-rendered-targets/style.css @@ -0,0 +1,201 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 1rem; + background: #333; + font-size: 0.1px; +} + +.control { + position: relative; + display: inline-block; + width: 2.7rem; + height: 2.7rem; + background: #444; + cursor: pointer; + font-size: 0.1px; + color: white; + transition: background 150ms; +} + +.control:not(.control-active):hover { + background: #3f3f3f; +} + +.control-filter:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + top: calc(50% - 6px); + left: calc(50% - 6px); + border: 2px solid currentColor; + border-radius: 2px; + background: currentColor; + transition: background-color 150ms, border-color 150ms; +} + +.control-sort:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + border-top: 2px solid; + border-left: 2px solid; + top: calc(50% - 6px); + left: calc(50% - 6px); + transform: translateY(1px) rotate(45deg); +} + +.control-sort[data-order="desc"]:after { + transform: translateY(-4px) rotate(-135deg); +} + +.control-active { + background: #393939; +} + +.control-filter.control-active:after { + background: transparent; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +.control-filter + .control-sort { + margin-left: .75rem; +} + +.control-filter[data-color="green"] { + color: #91e6c7; +} + +.control-filter[data-color="blue"] { + color: #5ecdde; +} + +.control-filter[data-color="pink"] { + color: #d595aa; +} + +.control-filter[data-color="none"] { + color: #2f2f2f; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + text-align: justify; + font-size: 0.1px; +} + +.container:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.item, +.gap { + display: inline-block; + vertical-align: top; +} + +.item { + background: #fff; + border-top: .5rem solid currentColor; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; +} + +.item:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +.item.green { + color: #91e6c7; +} + +.item.pink { + color: #d595aa; +} + +.item.blue { + color: #5ecdde; +} + +.item .published-date { + position: absolute; + top: 0; + left: 0; + padding: 1rem; + font-size: 1rem; + color: #333; + font-family: 'helvetica-neue', arial, sans-serif; + font-weight: 700; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.item, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .item, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .item, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .item, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} + + diff --git a/mixitup-3.3.1/demos/filtering-by-range/index.html b/mixitup-3.3.1/demos/filtering-by-range/index.html new file mode 100644 index 0000000..9cd4732 --- /dev/null +++ b/mixitup-3.3.1/demos/filtering-by-range/index.html @@ -0,0 +1,156 @@ + + + + + + + + + MixItUp Demo - Filtering by Range + + + + +
+ + + + + + + + + + + + +
+ + + +
+ +
+ + + +
+
+ +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/filtering-by-range/style.css b/mixitup-3.3.1/demos/filtering-by-range/style.css new file mode 100644 index 0000000..88f7cde --- /dev/null +++ b/mixitup-3.3.1/demos/filtering-by-range/style.css @@ -0,0 +1,280 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 1rem; + background: #333; + font-size: 0.1px; +} + +.control { + position: relative; + display: inline-block; + width: 2.7rem; + height: 2.7rem; + background: #444; + cursor: pointer; + font-size: 0.1px; + color: white; + transition: background 150ms; +} + +.control:hover { + background: #3f3f3f; +} + +.control[data-filter]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + top: calc(50% - 6px); + left: calc(50% - 6px); + border: 2px solid currentColor; + border-radius: 2px; + background: currentColor; + transition: background-color 150ms, border-color 150ms; +} + +.control[data-sort]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + border-top: 2px solid; + border-left: 2px solid; + top: calc(50% - 6px); + left: calc(50% - 6px); + transform: translateY(1px) rotate(45deg); +} + +.control[data-sort*=":desc"]:after { + transform: translateY(-4px) rotate(-135deg); +} + +.mixitup-control-active { + background: #393939; +} + +.mixitup-control-active[data-filter]:after { + background: transparent; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +.control[data-filter] + .control[data-sort] { + margin-left: .75rem; +} + +.control[data-filter=".green"] { + color: #91e6c7; +} + +.control[data-filter=".blue"] { + color: #5ecdde; +} + +.control[data-filter=".pink"] { + color: #d595aa; +} + +.control[data-filter="none"] { + color: #2f2f2f; +} + +.range-slider { + margin: 0 1rem; + display: inline-block; + vertical-align: middle; + position: relative; +} + +.range-slider:last-child { + margin-left: 0; +} + +.range-slider::before, +.range-slider::after, +.range-slider-label { + font-family: 'helvetica-neue', arial, sans-serif; +} + +.range-slider::before, +.range-slider::after { + font-size: .7rem; + color: #aaa; + content: '0'; + position: absolute; + bottom: 0; + left: 0; + padding: 0 .2rem; +} + +.range-slider::after { + content: '10'; + left: auto; + right: 0; +} + +.range-slider-label { + display: block; + font-size: .8rem; + color: #ccc; + margin-bottom: .05rem; + font-weight: 700; +} + +.range-slider:last-child .range-slider-label { + text-align: right; +} + +.range-slider-input { + position: relative; + background: transparent; + -webkit-appearance: none; + margin-bottom: .75rem; + z-index: 1; +} + +.range-slider-input::-webkit-slider-runnable-track { + width: 300px; + height: 5px; + background: #888; + border: none; + border-radius: 3px; + transition: background 150ms; +} + +.range-slider-input::-webkit-slider-thumb { + -webkit-appearance: none; + border: none; + height: 16px; + width: 16px; + border-radius: 50%; + background: #5ecdde; + margin-top: -6px; +} + +.range-slider-input:focus { + outline: none; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + text-align: justify; + font-size: 0.1px; +} + +.container:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix, +.gap { + display: inline-block; + vertical-align: top; +} + +.mix { + background: #fff; + border-top: .5rem solid currentColor; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; + font-family: 'helvetica-neue', arial, sans-serif; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +.mix.green { + color: #91e6c7; +} + +.mix.pink { + color: #d595aa; +} + +.mix.blue { + color: #5ecdde; +} + +.mix[data-size]:after { + position: absolute; + content: attr(data-size); + font-size: 1rem; + font-weight: bold; + color: #aaa; + padding: 1rem; + width: 100%; + top: 0; + left: 0; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.mix, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .mix, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .mix, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .mix, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} + + diff --git a/mixitup-3.3.1/demos/filtering-by-text-input/index.html b/mixitup-3.3.1/demos/filtering-by-text-input/index.html new file mode 100644 index 0000000..7931bb2 --- /dev/null +++ b/mixitup-3.3.1/demos/filtering-by-text-input/index.html @@ -0,0 +1,107 @@ + + + + + + + + + MixItUp Demo - Filtering by Text Input + + +
+ + + + + + + + + + +
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/filtering-by-text-input/style.css b/mixitup-3.3.1/demos/filtering-by-text-input/style.css new file mode 100644 index 0000000..17ca3c0 --- /dev/null +++ b/mixitup-3.3.1/demos/filtering-by-text-input/style.css @@ -0,0 +1,202 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 1rem; + background: #333; + font-size: 0.1px; +} + +.control { + position: relative; + display: inline-block; + width: 2.7rem; + height: 2.7rem; + background: #444; + cursor: pointer; + font-size: 0.1px; + color: white; + transition: background 150ms; +} + +.control:hover { + background: #3f3f3f; +} + +.control[data-filter]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + top: calc(50% - 6px); + left: calc(50% - 6px); + border: 2px solid currentColor; + border-radius: 2px; + background: currentColor; + transition: background-color 150ms, border-color 150ms; +} + +.control[data-sort]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + border-top: 2px solid; + border-left: 2px solid; + top: calc(50% - 6px); + left: calc(50% - 6px); + transform: translateY(1px) rotate(45deg); +} + +.control[data-sort*=":desc"]:after { + transform: translateY(-4px) rotate(-135deg); +} + +.mixitup-control-active { + background: #393939; +} + +.mixitup-control-active[data-filter]:after { + background: transparent; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +.control[data-filter] + .control[data-sort] { + margin-left: .75rem; +} + +.control[data-filter=".green"] { + color: #91e6c7; +} + +.control[data-filter=".blue"] { + color: #5ecdde; +} + +.control[data-filter=".pink"] { + color: #d595aa; +} + +.control[data-filter="none"] { + color: #2f2f2f; +} + +.input { + display: inline-block; + font-size: 1rem; + padding: .75rem; + margin-left: .75rem; + border-radius: 2px; + border: 0 none; + background: white; + font-family: 'helvetica neue', arial, sans-serif; + vertical-align: top; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + text-align: justify; + font-size: 0.1px; +} + +.container:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix, +.gap { + display: inline-block; + vertical-align: top; +} + +.mix { + background: #fff; + border-top: .5rem solid currentColor; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +.mix.green { + color: #91e6c7; +} + +.mix.pink { + color: #d595aa; +} + +.mix.blue { + color: #5ecdde; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.mix, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .mix, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .mix, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .mix, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} + + diff --git a/mixitup-3.3.1/demos/filtering-by-url/index.html b/mixitup-3.3.1/demos/filtering-by-url/index.html new file mode 100644 index 0000000..e94fcff --- /dev/null +++ b/mixitup-3.3.1/demos/filtering-by-url/index.html @@ -0,0 +1,116 @@ + + + + + + + + + MixItUp Demo - Filtering by URL + + +
+ + + + + +
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/filtering-by-url/style.css b/mixitup-3.3.1/demos/filtering-by-url/style.css new file mode 100644 index 0000000..0cefdd8 --- /dev/null +++ b/mixitup-3.3.1/demos/filtering-by-url/style.css @@ -0,0 +1,170 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 1rem; + background: #333; + font-size: 0.1px; +} + +.control { + position: relative; + display: inline-block; + width: 2.7rem; + height: 2.7rem; + background: #444; + cursor: pointer; + font-size: 0.1px; + color: white; + transition: background 150ms; +} + +.control:hover { + background: #3f3f3f; +} + +.control[data-filter]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + top: calc(50% - 6px); + left: calc(50% - 6px); + border: 2px solid currentColor; + border-radius: 2px; + background: currentColor; + transition: background-color 150ms, border-color 150ms; +} + +.mixitup-control-active { + background: #393939; +} + +.mixitup-control-active[data-filter]:after { + background: transparent; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +.control[data-filter=".green"] { + color: #91e6c7; +} + +.control[data-filter=".blue"] { + color: #5ecdde; +} + +.control[data-filter=".pink"] { + color: #d595aa; +} + +.control[data-filter="none"] { + color: #2f2f2f; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + text-align: justify; + font-size: 0.1px; +} + +.container:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix, +.gap { + display: inline-block; + vertical-align: top; +} + +.mix { + background: #fff; + border-top: .5rem solid currentColor; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +.mix.green { + color: #91e6c7; +} + +.mix.pink { + color: #d595aa; +} + +.mix.blue { + color: #5ecdde; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.mix, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .mix, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .mix, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .mix, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} + + diff --git a/mixitup-3.3.1/demos/grid-columns/index.html b/mixitup-3.3.1/demos/grid-columns/index.html new file mode 100644 index 0000000..803fa1c --- /dev/null +++ b/mixitup-3.3.1/demos/grid-columns/index.html @@ -0,0 +1,51 @@ + + + + + + + + + MixItUp Demo - Columns Grid + + +
+ + + + + + + + + + +
+ +
+
+
+
+
+
+
+
+
+
+
+ + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/grid-columns/style.css b/mixitup-3.3.1/demos/grid-columns/style.css new file mode 100644 index 0000000..2e7ac54 --- /dev/null +++ b/mixitup-3.3.1/demos/grid-columns/style.css @@ -0,0 +1,214 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 1rem; + background: #333; + font-size: 0.1px; +} + +.control { + position: relative; + display: inline-block; + vertical-align: top; + width: 2.7rem; + height: 2.7rem; + background: #444; + cursor: pointer; + font-size: 0.1px; + color: white; + transition: background 150ms; +} + +.control:hover { + background: #3f3f3f; +} + +.control[data-filter]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + top: calc(50% - 6px); + left: calc(50% - 6px); + border: 2px solid currentColor; + border-radius: 2px; + background: currentColor; + transition: background-color 150ms, border-color 150ms; +} + +.control[data-sort]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + border-top: 2px solid; + border-left: 2px solid; + top: calc(50% - 6px); + left: calc(50% - 6px); + transform: translateY(1px) rotate(45deg); +} + +.control.text { + width: auto; + height: auto; + padding: 0 1rem; + font-size: .9rem; + height: 2.7rem; + font-weight: 800; + color: white; + margin-left: .75rem; +} + +.control.text:after { + display: none; +} + +.control[data-sort*=":desc"]:after { + transform: translateY(-4px) rotate(-135deg); +} + +.mixitup-control-active { + background: #393939; +} + +.mixitup-control-active[data-filter]:after { + background: transparent; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +.control[data-filter] + .control[data-sort] { + margin-left: .75rem; +} + +.control[data-filter=".green"] { + color: #91e6c7; +} + +.control[data-filter=".blue"] { + color: #5ecdde; +} + +.control[data-filter=".pink"] { + color: #d595aa; +} + +.control[data-filter="none"] { + color: #2f2f2f; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + + -webkit-column-count: 1; + -moz-column-count: 1; + column-count: 1; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix { + display: inline-block; + vertical-align: top; + width: 100%; + background: #fff; + border-top: .5rem solid currentColor; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; + + backface-visibility: hidden; /* prevents flicker in chrome */ + will-change: transform, opacity; /* prevents flicker in safari */ +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +.mix.green { + color: #91e6c7; +} + +.mix.pink { + color: #d595aa; +} + +.mix.blue { + color: #5ecdde; +} + +.mix.pink:before { + padding-top: 75%; +} + +.mix.blue:before { + padding-top: 100%; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +@media screen and (min-width: 401px) { + .container { + -webkit-column-count: 2; + -moz-column-count: 2; + column-count: 2; + } +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .container { + -webkit-column-count: 3; + -moz-column-count: 3; + column-count: 3; + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .container { + -webkit-column-count: 4; + -moz-column-count: 4; + column-count: 4; + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .container { + -webkit-column-count: 5; + -moz-column-count: 5; + column-count: 5; + } +} \ No newline at end of file diff --git a/mixitup-3.3.1/demos/grid-flex-box-matching-heights/index.html b/mixitup-3.3.1/demos/grid-flex-box-matching-heights/index.html new file mode 100644 index 0000000..0cb3c68 --- /dev/null +++ b/mixitup-3.3.1/demos/grid-flex-box-matching-heights/index.html @@ -0,0 +1,66 @@ + + + + + + + + + MixItUp Demo - Flex-box Grid with Matching Heights + + +
+ +
+ +
+
+

Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore. Quis autem vel eum iure reprehenderit qui in ea voluptate velit similique sunt in culpa qui officia deserunt mollitia animi.

+
+ +
+

Similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum.

+
+ +
+

At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias.

+

+ +
+

Similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum.

+
+ +
+

At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias.

+

+ +
+

Similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum.

+
+ +
+

Magni dolores eos qui ratione voluptatem sequi nesciun.

+
+ +
+

Similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum.

+
+ +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/grid-flex-box-matching-heights/style.css b/mixitup-3.3.1/demos/grid-flex-box-matching-heights/style.css new file mode 100644 index 0000000..8648ac9 --- /dev/null +++ b/mixitup-3.3.1/demos/grid-flex-box-matching-heights/style.css @@ -0,0 +1,148 @@ +html, +body { + height: 100%; + background: #f2f2f2; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: antialiased; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 1rem; + background: #333; + font-size: 0.1px; +} + +.control { + position: relative; + display: inline-block; + padding: .5rem; + background: #444; + cursor: pointer; + font-size: .9rem; + font-weight: 800; + color: white; + transition: background 150ms; +} + +.control:hover { + background: #3f3f3f; +} + +.mixitup-control-active { + background: #393939; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-content: flex-start; + justify-content: space-between; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix, +.gap { + display: inline-flex; + vertical-align: top; +} + +.mix { + background: #fff; + border-top: .5rem solid currentColor; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; + color: transparent; + font-family: 'helvetica-neue', arial, sans-serif; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +.mix.green { + color: #91e6c7; +} + +.mix.pink { + color: #d595aa; +} + +.mix.blue { + color: #5ecdde; +} + +.mix p { + padding: 1rem; + display: inline-block; + font-size: calc(1vw + .5rem); + color: #333; + line-height: 1.4; + font-weight: 300; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.mix, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .mix, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .mix, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .mix, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} + + diff --git a/mixitup-3.3.1/demos/grid-flex-box/index.html b/mixitup-3.3.1/demos/grid-flex-box/index.html new file mode 100644 index 0000000..3a70bb5 --- /dev/null +++ b/mixitup-3.3.1/demos/grid-flex-box/index.html @@ -0,0 +1,27 @@ + + + + + + + + + MixItUp Demo - Flex-box Grid + + +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/grid-flex-box/style.css b/mixitup-3.3.1/demos/grid-flex-box/style.css new file mode 100644 index 0000000..e54b960 --- /dev/null +++ b/mixitup-3.3.1/demos/grid-flex-box/style.css @@ -0,0 +1,84 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-content: flex-start; + justify-content: space-between; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix, +.gap { + display: inline-block; + vertical-align: top; +} + +.mix { + background: #fff; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.mix, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .mix, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .mix, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .mix, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} + + diff --git a/mixitup-3.3.1/demos/grid-floats/index.html b/mixitup-3.3.1/demos/grid-floats/index.html new file mode 100644 index 0000000..8c39645 --- /dev/null +++ b/mixitup-3.3.1/demos/grid-floats/index.html @@ -0,0 +1,23 @@ + + + + + + + + + MixItUp Demo - Float-based Grid + + +
+
+
+
+
+
+
+
+
+
+ + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/grid-floats/style.css b/mixitup-3.3.1/demos/grid-floats/style.css new file mode 100644 index 0000000..1bf98af --- /dev/null +++ b/mixitup-3.3.1/demos/grid-floats/style.css @@ -0,0 +1,73 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem .5rem; + overflow: hidden; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix { + float: left; + margin: 0 .5rem 1rem; + background: #fff; + border-radius: 2px; + position: relative; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.mix { + width: calc(50% - 1rem); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .mix { + width: calc(100%/3 - 1rem); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .mix, + .gap { + width: calc(100%/4 - 1rem); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .mix, + .gap { + width: calc(100%/5 - 1rem); + } +} + + diff --git a/mixitup-3.3.1/demos/grid-inline-block/index.html b/mixitup-3.3.1/demos/grid-inline-block/index.html new file mode 100644 index 0000000..9ce42b4 --- /dev/null +++ b/mixitup-3.3.1/demos/grid-inline-block/index.html @@ -0,0 +1,27 @@ + + + + + + + + + MixItUp Demo - Inline-block Grid + + +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/grid-inline-block/style.css b/mixitup-3.3.1/demos/grid-inline-block/style.css new file mode 100644 index 0000000..59cfb74 --- /dev/null +++ b/mixitup-3.3.1/demos/grid-inline-block/style.css @@ -0,0 +1,87 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + text-align: justify; + font-size: 0.1px; +} + +.container:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix, +.gap { + display: inline-block; + vertical-align: top; +} + +.mix { + background: #fff; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.mix, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .mix, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .mix, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .mix, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} + + diff --git a/mixitup-3.3.1/demos/index.html b/mixitup-3.3.1/demos/index.html new file mode 100644 index 0000000..44ad133 --- /dev/null +++ b/mixitup-3.3.1/demos/index.html @@ -0,0 +1,81 @@ + + + + MixItUp Demos + + +

MixItUp Demos

+ +

The following collection of demos have been designed to demonstrate MixItUp's core functionality. They are maintained as part of the MixItUp Github repository, and can be viewed at demos.kunkalabs.com/mixitup/.

+ +

Each demo is intended to be as simple and instructive as possible and therefore intentionally avoids "magic" such as SASS, ES6, modules, or external dependencies that may require compilation in order to be usable in the browser. As such, you may run and edit these demos using any basic static file webserver, online or offline.

+ +

We add new demos to this collection regularly, but please contact us if there's a particular piece of functionality you would like to see which is missing from this list, or if you come across an issue with an existing demo.

+ +

Basic Functionality

+ + + +

Filtering

+ + + +

Non-standard UI

+ + + +

Sorting

+ + + +

Insertion

+ + + +

Removal

+ + + +

Dataset

+ + + +

Grids

+ + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/insertion-non-target-elements/index.html b/mixitup-3.3.1/demos/insertion-non-target-elements/index.html new file mode 100644 index 0000000..b536b91 --- /dev/null +++ b/mixitup-3.3.1/demos/insertion-non-target-elements/index.html @@ -0,0 +1,42 @@ + + + + + + + + + MixItUp Demo - Insertion with Non-target Elements + + +
+ + +
+ +
+ + +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/insertion-non-target-elements/style.css b/mixitup-3.3.1/demos/insertion-non-target-elements/style.css new file mode 100644 index 0000000..eb24de7 --- /dev/null +++ b/mixitup-3.3.1/demos/insertion-non-target-elements/style.css @@ -0,0 +1,174 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 1rem; + background: #333; + font-size: 0.1px; +} + +.control { + position: relative; + display: inline-block; + width: 2.7rem; + height: 2.7rem; + background: #444; + cursor: pointer; + font-size: 0.1px; + color: white; + transition: background 150ms; +} + +.control:hover { + background: #3f3f3f; +} + +.control[data-filter]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + top: calc(50% - 6px); + left: calc(50% - 6px); + border: 2px solid currentColor; + border-radius: 2px; + background: currentColor; + transition: background-color 150ms, border-color 150ms; +} + +.control[data-sort]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + border-top: 2px solid; + border-left: 2px solid; + top: calc(50% - 6px); + left: calc(50% - 6px); + transform: translateY(1px) rotate(45deg); +} + +.control[data-sort*=":desc"]:after { + transform: translateY(-4px) rotate(-135deg); +} + +.mixitup-control-active { + background: #393939; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + text-align: justify; + font-size: 0.1px; +} + +.container:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Grid Elements +---------------------------------------------------------------------- */ + +.item, +.gap { + display: inline-block; + vertical-align: top; +} + +.item { + background: #fff; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; +} + +.item:before { + content: ''; + display: inline-block; + vertical-align: middle; + padding-top: 100%; +} + +.button { + background: #ddd; + font-size: 4rem; + font-weight: bold; + text-align: center; + cursor: pointer; + transition: box-shadow 100ms; +} + +.button:hover { + box-shadow: inset 0 0 20px rgba(0,0,0, .1); +} + +.button:after { + display: inline-block; + content: '+'; + font-size: 4rem; + color: #bbb; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.item, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .item, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .item, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .item, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} + + diff --git a/mixitup-3.3.1/demos/loading-animation/index.html b/mixitup-3.3.1/demos/loading-animation/index.html new file mode 100644 index 0000000..8a90228 --- /dev/null +++ b/mixitup-3.3.1/demos/loading-animation/index.html @@ -0,0 +1,71 @@ + + + + + + + + + MixItUp Demo - Basic + + +
+ + + + + + + + +
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/loading-animation/style.css b/mixitup-3.3.1/demos/loading-animation/style.css new file mode 100644 index 0000000..dd9c77f --- /dev/null +++ b/mixitup-3.3.1/demos/loading-animation/style.css @@ -0,0 +1,198 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 1rem; + background: #333; + font-size: 0.1px; +} + +.control { + position: relative; + display: inline-block; + width: 2.7rem; + height: 2.7rem; + background: #444; + cursor: pointer; + font-size: 0.1px; + color: white; + transition: background 150ms; +} + +.control:hover { + background: #3f3f3f; +} + +.control[data-filter]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + top: calc(50% - 6px); + left: calc(50% - 6px); + border: 2px solid currentColor; + border-radius: 2px; + background: currentColor; + transition: background-color 150ms, border-color 150ms; +} + +.control[data-sort]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + border-top: 2px solid; + border-left: 2px solid; + top: calc(50% - 6px); + left: calc(50% - 6px); + transform: translateY(1px) rotate(45deg); +} + +.control[data-sort*=":desc"]:after { + transform: translateY(-4px) rotate(-135deg); +} + +.mixitup-control-active { + background: #393939; +} + +.mixitup-control-active[data-filter]:after { + background: transparent; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +.control[data-filter] + .control[data-sort] { + margin-left: .75rem; +} + +.control[data-filter=".green"] { + color: #91e6c7; +} + +.control[data-filter=".blue"] { + color: #5ecdde; +} + +.control[data-filter=".pink"] { + color: #d595aa; +} + +.control[data-filter="none"] { + color: #2f2f2f; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + text-align: justify; + font-size: 0.1px; +} + +.container:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix, +.gap { + display: inline-block; + vertical-align: top; +} + +.mix { + background: #fff; + border-top: .5rem solid currentColor; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +.mix.green { + color: #91e6c7; +} + +.mix.pink { + color: #d595aa; +} + +.mix.blue { + color: #5ecdde; +} + +.mix { + visibility: hidden; +} + +.mixitup-ready .mix { + visibility: visible; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.mix, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .mix, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .mix, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .mix, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} + + diff --git a/mixitup-3.3.1/demos/mixitup.min.js b/mixitup-3.3.1/demos/mixitup.min.js new file mode 100644 index 0000000..8562101 --- /dev/null +++ b/mixitup-3.3.1/demos/mixitup.min.js @@ -0,0 +1,18 @@ +/**! + * MixItUp v3.3.1 + * A high-performance, dependency-free library for animated filtering, sorting and more + * Build 94e0fbf6-cd0b-4987-b3c0-14b59b67b8a0 + * + * @copyright Copyright 2014-2018 KunkaLabs Limited. + * @author KunkaLabs Limited. + * @link https://www.kunkalabs.com/mixitup/ + * + * @license Commercial use requires a commercial license. + * https://www.kunkalabs.com/mixitup/licenses/ + * + * Non-commercial use permitted under same terms as CC BY-NC 3.0 license. + * http://creativecommons.org/licenses/by-nc/3.0/ + */ +!function(t){"use strict";var e=null,n=null;!function(){var e=["webkit","moz","o","ms"],n=t.document.createElement("div"),a=-1;for(a=0;a-1}}(t.Element.prototype),Object.keys||(Object.keys=function(){var t=Object.prototype.hasOwnProperty,e=!1,n=[],a=-1;return e=!{toString:null}.propertyIsEnumerable("toString"),n=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],a=n.length,function(i){var o=[],r="",s=-1;if("object"!=typeof i&&("function"!=typeof i||null===i))throw new TypeError("Object.keys called on non-object");for(r in i)t.call(i,r)&&o.push(r);if(e)for(s=0;s>>0,0===i)return-1;if(e=0,arguments.length>1&&(e=Number(arguments[1]),e!==e?e=0:0!==e&&e!==1/0&&e!==-(1/0)&&(e=(e>0||-1)*Math.floor(Math.abs(e)))),e>=i)return-1;for(n=e>=0?e:Math.max(i-Math.abs(e),0);n0)||s);g++)r.id?d=r.id:(d="MixItUp"+n.randomHex(),r.id=d),e.instances[d]instanceof e.Mixer?(l=e.instances[d],(!i||i&&i.debug&&i.debug.showWarnings!==!1)&&console.warn(e.messages.warningFactoryPreexistingInstance())):(l=new e.Mixer,l.attach(r,u,d,i),e.instances[d]=l),c=new e.Facade(l),i&&i.debug&&i.debug.enable?h.push(l):h.push(c);return f=s?new e.Collection(h):h[0]},e.use=function(t){e.Base.prototype.callActions.call(e,"beforeUse",arguments),"function"==typeof t&&"mixitup-extension"===t.TYPE?"undefined"==typeof e.extensions[t.NAME]&&(t(e),e.extensions[t.NAME]=t):t.fn&&t.fn.jquery&&(e.libraries.$=t),e.Base.prototype.callActions.call(e,"afterUse",arguments)},e.instances={},e.extensions={},e.libraries={},n={hasClass:function(t,e){return!!t.className.match(new RegExp("(\\s|^)"+e+"(\\s|$)"))},addClass:function(t,e){this.hasClass(t,e)||(t.className+=t.className?" "+e:e)},removeClass:function(t,e){if(this.hasClass(t,e)){var n=new RegExp("(\\s|^)"+e+"(\\s|$)");t.className=t.className.replace(n," ").trim()}},extend:function(t,e,n,a){var i=[],o="",r=-1;n=n||!1,a=a||!1;try{if(Array.isArray(e))for(r=0;ru&&(u=f,l=c)}throw u>1&&(s=e.messages.errorConfigInvalidPropertySuggestion({probableMatch:l})),r=e.messages.errorConfigInvalidProperty({erroneous:o,suggestion:s}),new TypeError(r)}throw t},template:function(t){for(var e=/\${([\w]*)}/g,n={},a=null;a=e.exec(t);)n[a[1]]=new RegExp("\\${"+a[1]+"}","g");return function(e){var a="",i=t;e=e||{};for(a in n)i=i.replace(n[a],"undefined"!=typeof e[a]?e[a]:"");return i}},on:function(e,n,a,i){e&&(e.addEventListener?e.addEventListener(n,a,i):e.attachEvent&&(e["e"+n+a]=a,e[n+a]=function(){e["e"+n+a](t.event)},e.attachEvent("on"+n,e[n+a])))},off:function(t,e,n){t&&(t.removeEventListener?t.removeEventListener(e,n,!1):t.detachEvent&&(t.detachEvent("on"+e,t[e+n]),t[e+n]=null))},getCustomEvent:function(e,n,a){var i=null;return a=a||t.document,"function"==typeof t.CustomEvent?i=new t.CustomEvent(e,{detail:n,bubbles:!0,cancelable:!0}):"function"==typeof a.createEvent?(i=a.createEvent("CustomEvent"),i.initCustomEvent(e,!0,!0,n)):(i=a.createEventObject(),i.type=e,i.returnValue=!1,i.cancelBubble=!1,i.detail=n),i},getOriginalEvent:function(t){return t.touches&&t.touches.length?t.touches[0]:t.changedTouches&&t.changedTouches.length?t.changedTouches[0]:t},index:function(t,e){for(var n=0;null!==(t=t.previousElementSibling);)e&&!t.matches(e)||++n;return n},camelCase:function(t){return t.toLowerCase().replace(/([_-][a-z])/g,function(t){return t.toUpperCase().replace(/[_-]/,"")})},pascalCase:function(t){return(t=this.camelCase(t)).charAt(0).toUpperCase()+t.slice(1)},dashCase:function(t){return t.replace(/([A-Z])/g,"-$1").replace(/^-/,"").toLowerCase()},isElement:function(e,n){return n=n||t.document,!!(t.HTMLElement&&e instanceof t.HTMLElement)||(!!(n.defaultView&&n.defaultView.HTMLElement&&e instanceof n.defaultView.HTMLElement)||null!==e&&1===e.nodeType&&"string"==typeof e.nodeName)},createElement:function(e,n){var a=null,i=null;for(n=n||t.document,a=n.createDocumentFragment(),i=n.createElement("div"),i.innerHTML=e.trim();i.firstChild;)a.appendChild(i.firstChild);return a},removeWhitespace:function(t){for(var e;t&&"#text"===t.nodeName;)e=t,t=t.previousSibling,e.parentElement&&e.parentElement.removeChild(e)},isEqualArray:function(t,e){var n=t.length;if(n!==e.length)return!1;for(;n--;)if(t[n]!==e[n])return!1;return!0},deepEquals:function(t,e){var n;if("object"==typeof t&&t&&"object"==typeof e&&e){if(Object.keys(t).length!==Object.keys(e).length)return!1;for(n in t)if(!e.hasOwnProperty(n)||!this.deepEquals(t[n],e[n]))return!1}else if(t!==e)return!1;return!0},arrayShuffle:function(t){for(var e=t.slice(),n=e.length,a=n,i=-1,o=[];a--;)i=~~(Math.random()*n),o=e[a],e[a]=e[i],e[i]=o;return e},arrayFromList:function(t){var e,n;try{return Array.prototype.slice.call(t)}catch(a){for(e=[],n=0;n "+n),o&&e.removeAttribute("id")),i},clean:function(t){var e=[],n=-1;for(n=0;ni)return!0}return!0},Deferred:function(){this.promise=null,this.resolve=null,this.reject=null,this.id=n.randomHex()},isEmptyObject:function(t){var e="";if("function"==typeof Object.keys)return 0===Object.keys(t).length;for(e in t)if(t.hasOwnProperty(e))return!1;return!0},getClassname:function(t,e,n){var a="";return a+=t.block,a.length&&(a+=t.delineatorElement),a+=t["element"+this.pascalCase(e)],n?(a.length&&(a+=t.delineatorModifier),a+=n):a},getProperty:function(t,e){var n=e.split("."),a=null,i="",o=0;if(!e)return t;for(a=function(t){return t?t[i]:null};o-1,e.callFilters("afterIsBound",n,arguments)},addBinding:function(t){var e=this;this.callActions("beforeAddBinding",arguments),e.isBound()||e.bound.push(t),this.callActions("afterAddBinding",arguments)},removeBinding:function(t){var n=this,a=-1;this.callActions("beforeRemoveBinding",arguments),(a=n.bound.indexOf(t))>-1&&n.bound.splice(a,1),n.bound.length<1&&(n.unbindClick(),a=e.controls.indexOf(n),e.controls.splice(a,1),"active"===n.status&&n.renderStatus(n.el,"inactive")),this.callActions("afterRemoveBinding",arguments)},bindClick:function(){var t=this;this.callActions("beforeBindClick",arguments),t.handler=function(e){t.handleClick(e)},n.on(t.el,"click",t.handler),this.callActions("afterBindClick",arguments)},unbindClick:function(){var t=this;this.callActions("beforeUnbindClick",arguments),n.off(t.el,"click",t.handler),t.handler=null,this.callActions("afterUnbindClick",arguments)},handleClick:function(t){var a=this,i=null,o=null,r=!1,s=void 0,l={},c=null,u=[],f=-1;if(this.callActions("beforeHandleClick",arguments),this.pending=0,o=a.bound[0],i=a.selector?n.closestParent(t.target,o.config.selectors.control+a.selector,!0,o.dom.document):a.el,!i)return void a.callActions("afterHandleClick",arguments);switch(a.type){case"filter":l.filter=a.filter||i.getAttribute("data-filter");break;case"sort":l.sort=a.sort||i.getAttribute("data-sort");break;case"multimix":l.filter=a.filter||i.getAttribute("data-filter"),l.sort=a.sort||i.getAttribute("data-sort");break;case"toggle":l.filter=a.filter||i.getAttribute("data-toggle"),r="live"===a.status?n.hasClass(i,a.classNames.active):"active"===a.status}for(f=0;f0||("live"===a.status?a.updateLive(t,n):(i.sort=a.sort,i.filter=a.filter,a.callFilters("actionsUpdate",i,arguments),a.parseStatusChange(a.el,t,i,n)),a.callActions("afterUpdate",arguments))},updateLive:function(t,n){var a=this,i=null,o=null,r=null,s=-1;if(a.callActions("beforeUpdateLive",arguments),a.el){for(i=a.el.querySelectorAll(a.selector),s=0;r=i[s];s++){switch(o=new e.CommandMultimix,a.type){case"filter":o.filter=r.getAttribute("data-filter");break;case"sort":o.sort=r.getAttribute("data-sort");break;case"multimix":o.filter=r.getAttribute("data-filter"),o.sort=r.getAttribute("data-sort");break;case"toggle":o.filter=r.getAttribute("data-toggle")}o=a.callFilters("actionsUpdateLive",o,arguments),a.parseStatusChange(r,t,o,n)}a.callActions("afterUpdateLive",arguments)}},parseStatusChange:function(t,e,n,a){var i=this,o="",r="",s=-1;switch(i.callActions("beforeParseStatusChange",arguments),i.type){case"filter":e.filter===n.filter?i.renderStatus(t,"active"):i.renderStatus(t,"inactive");break;case"multimix":e.sort===n.sort&&e.filter===n.filter?i.renderStatus(t,"active"):i.renderStatus(t,"inactive");break;case"sort":e.sort.match(/:asc/g)&&(o=e.sort.replace(/:asc/g,"")),e.sort===n.sort||o===n.sort?i.renderStatus(t,"active"):i.renderStatus(t,"inactive");break;case"toggle":for(a.length<1&&i.renderStatus(t,"inactive"),e.filter===n.filter&&i.renderStatus(t,"active"),s=0;s-1)throw new Error(e.messages.errorInsertPreexistingElement());c.style.display="none",s.appendChild(c),s.appendChild(i.dom.document.createTextNode(" ")),n.isElement(c,i.dom.document)&&c.matches(i.config.selectors.target)&&(l=new e.Target,l.init(c,i),l.isInDom=!0,i.targets.splice(r,0,l),r++)}i.dom.parent.insertBefore(s,o)}a.startOrder=i.origOrder=i.targets,i.callActions("afterInsertTargets",arguments)},getNextSibling:function(t,e,n){var a=this,i=null;return t=Math.max(t,0),e&&"before"===n?i=e:e&&"after"===n?i=e.nextElementSibling||null:a.targets.length>0&&"undefined"!=typeof t?i=t0&&(a.config.layout.siblingAfter?i=a.config.layout.siblingAfter:a.config.layout.siblingBefore?i=a.config.layout.siblingBefore.nextElementSibling:a.dom.parent.children[0]),a.callFilters("elementGetNextSibling",i,arguments)},filterOperation:function(t){var e=this,n=!1,a=-1,i="",o=null,r=-1;for(e.callActions("beforeFilterOperation",arguments),i=t.newFilter.action,r=0;o=t.newOrder[r];r++)n=t.newFilter.collection?t.newFilter.collection.indexOf(o.dom.el)>-1:""!==t.newFilter.selector&&o.dom.el.matches(t.newFilter.selector),e.evaluateHideShow(n,o,i,t);if(t.toRemove.length)for(r=0;o=t.show[r];r++)t.toRemove.indexOf(o)>-1&&(t.show.splice(r,1),(a=t.toShow.indexOf(o))>-1&&t.toShow.splice(a,1),t.toHide.push(o),t.hide.push(o),r--);t.matching=t.show.slice(),0===t.show.length&&""!==t.newFilter.selector&&0!==e.targets.length&&(t.hasFailed=!0),e.callActions("afterFilterOperation",arguments)},evaluateHideShow:function(t,e,n,a){var i=this,o=!1,r=Array.prototype.slice.call(arguments,1);o=i.callFilters("testResultEvaluateHideShow",t,r),i.callActions("beforeEvaluateHideShow",arguments),o===!0&&"show"===n||o===!1&&"hide"===n?(a.show.push(e),!e.isShown&&a.toShow.push(e)):(a.hide.push(e),e.isShown&&a.toHide.push(e)),i.callActions("afterEvaluateHideShow",arguments)},sortOperation:function(t){var a=this,i=[],o=null,r=null,s=-1;if(a.callActions("beforeSortOperation",arguments),t.startOrder=a.targets,t.newSort.collection){for(i=[],s=0;r=t.newSort.collection[s];s++){if(a.dom.targets.indexOf(r)<0)throw new Error(e.messages.errorSortNonExistentElement());o=new e.Target,o.init(r,a),o.isInDom=!0,i.push(o)}t.newOrder=i}else"random"===t.newSort.order?t.newOrder=n.arrayShuffle(t.startOrder):""===t.newSort.attribute?(t.newOrder=a.origOrder.slice(),"desc"===t.newSort.order&&t.newOrder.reverse()):(t.newOrder=t.startOrder.slice(),t.newOrder.sort(function(e,n){return a.compare(e,n,t.newSort)}));n.isEqualArray(t.newOrder,t.startOrder)&&(t.willSort=!1),a.callActions("afterSortOperation",arguments)},compare:function(t,e,n){var a=this,i=n.order,o=a.getAttributeValue(t,n.attribute),r=a.getAttributeValue(e,n.attribute);return isNaN(1*o)||isNaN(1*r)?(o=o.toLowerCase(),r=r.toLowerCase()):(o=1*o,r=1*r),or?"asc"===i?1:-1:o===r&&n.next?a.compare(t,e,n.next):0},getAttributeValue:function(t,n){var a=this,i="";return i=t.dom.el.getAttribute("data-"+n),null===i&&a.config.debug.showWarnings&&console.warn(e.messages.warningInconsistentSortingAttributes({attribute:"data-"+n})),a.callFilters("valueGetAttributeValue",i||0,arguments)},printSort:function(e,a){var i=this,o=e?a.newOrder:a.startOrder,r=e?a.startOrder:a.newOrder,s=o.length?o[o.length-1].dom.el.nextElementSibling:null,l=t.document.createDocumentFragment(),c=null,u=null,f=null,h=-1;for(i.callActions("beforePrintSort",arguments),h=0;u=o[h];h++)f=u.dom.el,"absolute"!==f.style.position&&(n.removeWhitespace(f.previousSibling),f.parentElement.removeChild(f));for(c=s?s.previousSibling:i.dom.parent.lastChild,c&&"#text"===c.nodeName&&n.removeWhitespace(c),h=0;u=r[h];h++)f=u.dom.el,n.isElement(l.lastChild)&&l.appendChild(t.document.createTextNode(" ")),l.appendChild(f);i.dom.parent.firstChild&&i.dom.parent.firstChild!==s&&l.insertBefore(t.document.createTextNode(" "),l.childNodes[0]),s?(l.appendChild(t.document.createTextNode(" ")),i.dom.parent.insertBefore(l,s)):i.dom.parent.appendChild(l),i.callActions("afterPrintSort",arguments)},parseSortString:function(t,a){var i=this,o=t.split(" "),r=a,s=[],l=-1;for(l=0;l-1&&(c=n.substring(l),u=s.exec(c),f=u[1]),t){case"fade":a.opacity=f?parseFloat(f):0;break;case"stagger":r.staggerDuration=f?parseFloat(f):100;break;default:if(o&&r.config.animation.reverseOut&&"scale"!==t?a[t].value=(f?parseFloat(f):e.transformDefaults[t].value)*-1:a[t].value=f?parseFloat(f):e.transformDefaults[t].value,f){for(m=0;d=h[m];m++)if(f.indexOf(d)>-1){a[t].unit=d;break}}else a[t].unit=e.transformDefaults[t].unit;i.push(t+"("+a[t].value+a[t].unit+")")}r.callActions("afterParseEffect",arguments)},buildState:function(t){var n=this,a=new e.State,i=null,o=-1;for(n.callActions("beforeBuildState",arguments),o=0;i=n.targets[o];o++)(!t.toRemove.length||t.toRemove.indexOf(i)<0)&&a.targets.push(i.dom.el);for(o=0;i=t.matching[o];o++)a.matching.push(i.dom.el);for(o=0;i=t.show[o];o++)a.show.push(i.dom.el);for(o=0;i=t.hide[o];o++)(!t.toRemove.length||t.toRemove.indexOf(i)<0)&&a.hide.push(i.dom.el);return a.id=n.id,a.container=n.dom.container,a.activeFilter=t.newFilter,a.activeSort=t.newSort,a.activeDataset=t.newDataset,a.activeContainerClassName=t.newContainerClassName,a.hasFailed=t.hasFailed,a.totalTargets=n.targets.length,a.totalShow=t.show.length,a.totalHide=t.hide.length,a.totalMatching=t.matching.length,a.triggerElement=t.triggerElement,n.callFilters("stateBuildState",a,arguments)},goMix:function(a,i){var o=this,r=null;return o.callActions("beforeGoMix",arguments),o.config.animation.duration&&o.config.animation.effects&&n.isVisible(o.dom.container)||(a=!1),i.toShow.length||i.toHide.length||i.willSort||i.willChangeLayout||(a=!1),i.startState.show.length||i.show.length||(a=!1),e.events.fire("mixStart",o.dom.container,{state:i.startState,futureState:i.newState,instance:o},o.dom.document),"function"==typeof o.config.callbacks.onMixStart&&o.config.callbacks.onMixStart.call(o.dom.container,i.startState,i.newState,o),n.removeClass(o.dom.container,n.getClassname(o.config.classNames,"container",o.config.classNames.modifierFailed)),r=o.userDeferred?o.userDeferred:o.userDeferred=n.defer(e.libraries),o.isBusy=!0,a&&e.features.has.transitions?(t.pageYOffset!==i.docState.scrollTop&&t.scrollTo(i.docState.scrollLeft,i.docState.scrollTop),o.config.animation.applyPerspective&&(o.dom.parent.style[e.features.perspectiveProp]=o.config.animation.perspectiveDistance,o.dom.parent.style[e.features.perspectiveOriginProp]=o.config.animation.perspectiveOrigin),o.config.animation.animateResizeContainer&&i.startHeight!==i.newHeight&&i.viewportDeltaY!==i.startHeight-i.newHeight&&(o.dom.parent.style.height=i.startHeight+"px"),o.config.animation.animateResizeContainer&&i.startWidth!==i.newWidth&&i.viewportDeltaX!==i.startWidth-i.newWidth&&(o.dom.parent.style.width=i.startWidth+"px"),i.startHeight===i.newHeight&&(o.dom.parent.style.height=i.startHeight+"px"),i.startWidth===i.newWidth&&(o.dom.parent.style.width=i.startWidth+"px"),i.startHeight===i.newHeight&&i.startWidth===i.newWidth&&(o.dom.parent.style.overflow="hidden"),requestAnimationFrame(function(){o.moveTargets(i)}),o.callFilters("promiseGoMix",r.promise,arguments)):(o.config.debug.fauxAsync?setTimeout(function(){o.cleanUp(i)},o.config.animation.duration):o.cleanUp(i),o.callFilters("promiseGoMix",r.promise,arguments))},getStartMixData:function(n){var a=this,i=t.getComputedStyle(a.dom.parent),o=a.dom.parent.getBoundingClientRect(),r=null,s={},l=-1,c=i[e.features.boxSizingProp];for(a.incPadding="border-box"===c,a.callActions("beforeGetStartMixData",arguments),l=0;r=n.show[l];l++)s=r.getPosData(),n.showPosData[l]={startPosData:s};for(l=0;r=n.toHide[l];l++)s=r.getPosData(),n.toHidePosData[l]={startPosData:s};n.startX=o.left,n.startY=o.top,n.startHeight=a.incPadding?o.height:o.height-parseFloat(i.paddingTop)-parseFloat(i.paddingBottom)-parseFloat(i.borderTop)-parseFloat(i.borderBottom),n.startWidth=a.incPadding?o.width:o.width-parseFloat(i.paddingLeft)-parseFloat(i.paddingRight)-parseFloat(i.borderLeft)-parseFloat(i.borderRight),a.callActions("afterGetStartMixData",arguments)},setInter:function(t){var e=this,a=null,i=-1;for(e.callActions("beforeSetInter",arguments),e.config.animation.clampHeight&&(e.dom.parent.style.height=t.startHeight+"px",e.dom.parent.style.overflow="hidden"),e.config.animation.clampWidth&&(e.dom.parent.style.width=t.startWidth+"px",e.dom.parent.style.overflow="hidden"),i=0;a=t.toShow[i];i++)a.show();t.willChangeLayout&&(n.removeClass(e.dom.container,t.startContainerClassName),n.addClass(e.dom.container,t.newContainerClassName)),e.callActions("afterSetInter",arguments)},getInterMixData:function(t){var e=this,n=null,a=-1;for(e.callActions("beforeGetInterMixData",arguments),a=0;n=t.show[a];a++)t.showPosData[a].interPosData=n.getPosData();for(a=0;n=t.toHide[a];a++)t.toHidePosData[a].interPosData=n.getPosData();e.callActions("afterGetInterMixData",arguments)},setFinal:function(t){var e=this,n=null,a=-1;for(e.callActions("beforeSetFinal",arguments),t.willSort&&e.printSort(!1,t),a=0;n=t.toHide[a];a++)n.hide();e.callActions("afterSetFinal",arguments)},getFinalMixData:function(e){var a=this,i=null,o=null,r=null,s=-1;for(a.callActions("beforeGetFinalMixData",arguments),s=0;r=e.show[s];s++)e.showPosData[s].finalPosData=r.getPosData();for(s=0;r=e.toHide[s];s++)e.toHidePosData[s].finalPosData=r.getPosData();for((a.config.animation.clampHeight||a.config.animation.clampWidth)&&(a.dom.parent.style.height=a.dom.parent.style.width=a.dom.parent.style.overflow=""),a.incPadding||(i=t.getComputedStyle(a.dom.parent)),o=a.dom.parent.getBoundingClientRect(),e.newX=o.left,e.newY=o.top,e.newHeight=a.incPadding?o.height:o.height-parseFloat(i.paddingTop)-parseFloat(i.paddingBottom)-parseFloat(i.borderTop)-parseFloat(i.borderBottom),e.newWidth=a.incPadding?o.width:o.width-parseFloat(i.paddingLeft)-parseFloat(i.paddingRight)-parseFloat(i.borderLeft)-parseFloat(i.borderRight),e.viewportDeltaX=e.docState.viewportWidth-this.dom.document.documentElement.clientWidth,e.viewportDeltaY=e.docState.viewportHeight-this.dom.document.documentElement.clientHeight,e.willSort&&a.printSort(!0,e),s=0;r=e.toShow[s];s++)r.hide();for(s=0;r=e.toHide[s];s++)r.show();e.willChangeLayout&&(n.removeClass(a.dom.container,e.newContainerClassName),n.addClass(a.dom.container,a.config.layout.containerClassName)),a.callActions("afterGetFinalMixData",arguments)},getTweenData:function(t){var n=this,a=null,i=null,o=Object.getOwnPropertyNames(n.effectsIn),r="",s=null,l=-1,c=-1,u=-1,f=-1;for(n.callActions("beforeGetTweenData",arguments),u=0;a=t.show[u];u++)for(i=t.showPosData[u],i.posIn=new e.StyleData,i.posOut=new e.StyleData,i.tweenData=new e.StyleData,a.isShown?(i.posIn.x=i.startPosData.x-i.interPosData.x,i.posIn.y=i.startPosData.y-i.interPosData.y):i.posIn.x=i.posIn.y=0,i.posOut.x=i.finalPosData.x-i.interPosData.x,i.posOut.y=i.finalPosData.y-i.interPosData.y,i.posIn.opacity=a.isShown?1:n.effectsIn.opacity,i.posOut.opacity=1,i.tweenData.opacity=i.posOut.opacity-i.posIn.opacity,a.isShown||n.config.animation.nudge||(i.posIn.x=i.posOut.x,i.posIn.y=i.posOut.y),i.tweenData.x=i.posOut.x-i.posIn.x,i.tweenData.y=i.posOut.y-i.posIn.y,n.config.animation.animateResizeTargets&&(i.posIn.width=i.startPosData.width,i.posIn.height=i.startPosData.height,l=(i.startPosData.width||i.finalPosData.width)-i.interPosData.width,i.posIn.marginRight=i.startPosData.marginRight-l,c=(i.startPosData.height||i.finalPosData.height)-i.interPosData.height,i.posIn.marginBottom=i.startPosData.marginBottom-c,i.posOut.width=i.finalPosData.width,i.posOut.height=i.finalPosData.height,l=(i.finalPosData.width||i.startPosData.width)-i.interPosData.width,i.posOut.marginRight=i.finalPosData.marginRight-l,c=(i.finalPosData.height||i.startPosData.height)-i.interPosData.height,i.posOut.marginBottom=i.finalPosData.marginBottom-c,i.tweenData.width=i.posOut.width-i.posIn.width,i.tweenData.height=i.posOut.height-i.posIn.height,i.tweenData.marginRight=i.posOut.marginRight-i.posIn.marginRight,i.tweenData.marginBottom=i.posOut.marginBottom-i.posIn.marginBottom),f=0;r=o[f];f++)s=n.effectsIn[r],s instanceof e.TransformData&&s.value&&(i.posIn[r].value=s.value,i.posOut[r].value=0,i.tweenData[r].value=i.posOut[r].value-i.posIn[r].value,i.posIn[r].unit=i.posOut[r].unit=i.tweenData[r].unit=s.unit);for(u=0;a=t.toHide[u];u++)for(i=t.toHidePosData[u],i.posIn=new e.StyleData,i.posOut=new e.StyleData,i.tweenData=new e.StyleData,i.posIn.x=a.isShown?i.startPosData.x-i.interPosData.x:0,i.posIn.y=a.isShown?i.startPosData.y-i.interPosData.y:0,i.posOut.x=n.config.animation.nudge?0:i.posIn.x,i.posOut.y=n.config.animation.nudge?0:i.posIn.y,i.tweenData.x=i.posOut.x-i.posIn.x,i.tweenData.y=i.posOut.y-i.posIn.y,n.config.animation.animateResizeTargets&&(i.posIn.width=i.startPosData.width,i.posIn.height=i.startPosData.height,l=i.startPosData.width-i.interPosData.width,i.posIn.marginRight=i.startPosData.marginRight-l,c=i.startPosData.height-i.interPosData.height,i.posIn.marginBottom=i.startPosData.marginBottom-c),i.posIn.opacity=1,i.posOut.opacity=n.effectsOut.opacity,i.tweenData.opacity=i.posOut.opacity-i.posIn.opacity,f=0;r=o[f];f++)s=n.effectsOut[r],s instanceof e.TransformData&&s.value&&(i.posIn[r].value=0,i.posOut[r].value=s.value,i.tweenData[r].value=i.posOut[r].value-i.posIn[r].value,i.posIn[r].unit=i.posOut[r].unit=i.tweenData[r].unit=s.unit);n.callActions("afterGetTweenData",arguments)},moveTargets:function(t){var a=this,i=null,o=null,r=null,s="",l=!1,c=-1,u=-1,f=a.checkProgress.bind(a);for(a.callActions("beforeMoveTargets",arguments),u=0;i=t.show[u];u++)o=new e.IMoveData,r=t.showPosData[u],s=i.isShown?"none":"show",l=a.willTransition(s,t.hasEffect,r.posIn,r.posOut),l&&c++,i.show(),o.posIn=r.posIn,o.posOut=r.posOut,o.statusChange=s,o.staggerIndex=c,o.operation=t,o.callback=l?f:null,i.move(o);for(u=0;i=t.toHide[u];u++)r=t.toHidePosData[u],o=new e.IMoveData,s="hide",l=a.willTransition(s,r.posIn,r.posOut),o.posIn=r.posIn,o.posOut=r.posOut,o.statusChange=s,o.staggerIndex=u,o.operation=t,o.callback=l?f:null,i.move(o);a.config.animation.animateResizeContainer&&(a.dom.parent.style[e.features.transitionProp]="height "+a.config.animation.duration+"ms ease, width "+a.config.animation.duration+"ms ease ",requestAnimationFrame(function(){t.startHeight!==t.newHeight&&t.viewportDeltaY!==t.startHeight-t.newHeight&&(a.dom.parent.style.height=t.newHeight+"px"),t.startWidth!==t.newWidth&&t.viewportDeltaX!==t.startWidth-t.newWidth&&(a.dom.parent.style.width=t.newWidth+"px")})),t.willChangeLayout&&(n.removeClass(a.dom.container,a.config.layout.ContainerClassName),n.addClass(a.dom.container,t.newContainerClassName)),a.callActions("afterMoveTargets",arguments)},hasEffect:function(){var t=this,e=["scale","translateX","translateY","translateZ","rotateX","rotateY","rotateZ"],n="",a=null,i=!1,o=-1,r=-1;if(1!==t.effectsIn.opacity)return t.callFilters("resultHasEffect",!0,arguments);for(r=0;n=e[r];r++)if(a=t.effectsIn[n],o="undefined"!==a.value?a.value:a,0!==o){i=!0;break}return t.callFilters("resultHasEffect",i,arguments)},willTransition:function(t,e,a,i){var o=this,r=!1;return r=!!n.isVisible(o.dom.container)&&(!!("none"!==t&&e||a.x!==i.x||a.y!==i.y)||!!o.config.animation.animateResizeTargets&&(a.width!==i.width||a.height!==i.height||a.marginRight!==i.marginRight||a.marginTop!==i.marginTop)),o.callFilters("resultWillTransition",r,arguments)},checkProgress:function(t){var e=this;e.targetsDone++,e.targetsBound===e.targetsDone&&e.cleanUp(t)},cleanUp:function(t){var a=this,i=null,o=null,r=null,s=null,l=-1;for(a.callActions("beforeCleanUp",arguments),a.targetsMoved=a.targetsImmovable=a.targetsBound=a.targetsDone=0,l=0;i=t.show[l];l++)i.cleanUp(),i.show();for(l=0;i=t.toHide[l];l++)i.cleanUp(),i.hide();if(t.willSort&&a.printSort(!1,t),a.dom.parent.style[e.features.transitionProp]=a.dom.parent.style.height=a.dom.parent.style.width=a.dom.parent.style.overflow=a.dom.parent.style[e.features.perspectiveProp]=a.dom.parent.style[e.features.perspectiveOriginProp]="",t.willChangeLayout&&(n.removeClass(a.dom.container,t.startContainerClassName),n.addClass(a.dom.container,t.newContainerClassName)),t.toRemove.length){for(l=0;i=a.targets[l];l++)t.toRemove.indexOf(i)>-1&&((o=i.dom.el.previousSibling)&&"#text"===o.nodeName&&(r=i.dom.el.nextSibling)&&"#text"===r.nodeName&&n.removeWhitespace(o),t.willSort||a.dom.parent.removeChild(i.dom.el),a.targets.splice(l,1),i.isInDom=!1,l--);a.origOrder=a.targets}t.willSort&&(a.targets=t.newOrder),a.state=t.newState,a.lastOperation=t,a.dom.targets=a.state.targets,e.events.fire("mixEnd",a.dom.container,{state:a.state,instance:a},a.dom.document),"function"==typeof a.config.callbacks.onMixEnd&&a.config.callbacks.onMixEnd.call(a.dom.container,a.state,a),t.hasFailed&&(e.events.fire("mixFail",a.dom.container,{state:a.state,instance:a},a.dom.document),"function"==typeof a.config.callbacks.onMixFail&&a.config.callbacks.onMixFail.call(a.dom.container,a.state,a),n.addClass(a.dom.container,n.getClassname(a.config.classNames,"container",a.config.classNames.modifierFailed))),"function"==typeof a.userCallback&&a.userCallback.call(a.dom.container,a.state,a),"function"==typeof a.userDeferred.resolve&&a.userDeferred.resolve(a.state),a.userCallback=null,a.userDeferred=null,a.lastClicked=null,a.isToggling=!1,a.isBusy=!1,a.queue.length&&(a.callActions("beforeReadQueueCleanUp",arguments),s=a.queue.shift(),a.userDeferred=s.deferred,a.isToggling=s.isToggling,a.lastClicked=s.triggerElement,s.instruction.command instanceof e.CommandMultimix?a.multimix.apply(a,s.args):a.dataset.apply(a,s.args)),a.callActions("afterCleanUp",arguments)},parseMultimixArgs:function(t){var a=this,i=new e.UserInstruction,o=null,r=-1;for(i.animate=a.config.animation.enable,i.command=new e.CommandMultimix,r=0;r-1?i.command.position=o:"string"==typeof o?i.command.collection=n.arrayFromList(n.createElement(o).childNodes):"object"==typeof o&&n.isElement(o,a.dom.document)?i.command.collection.length?i.command.sibling=o:i.command.collection=[o]:"object"==typeof o&&o.length?i.command.collection.length?i.command.sibling=o[0]:i.command.collection=o:"object"==typeof o&&o.childNodes&&o.childNodes.length?i.command.collection.length?i.command.sibling=o.childNodes[0]:i.command.collection=n.arrayFromList(o.childNodes):"object"==typeof o?n.extend(i.command,o):"boolean"==typeof o?i.animate=o:"function"==typeof o&&(i.callback=o));if(i.command.index&&i.command.sibling)throw new Error(e.messages.errorInsertInvalidArguments());return!i.command.collection.length&&a.config.debug.showWarnings&&console.warn(e.messages.warningInsertNoElements()),i=a.callFilters("instructionParseInsertArgs",i,arguments),n.freeze(i),i},parseRemoveArgs:function(t){var a=this,i=new e.UserInstruction,o=null,r=null,s=-1;for(i.animate=a.config.animation.enable,i.command=new e.CommandRemove,s=0;s-1&&i.command.targets.push(o);return!i.command.targets.length&&a.config.debug.showWarnings&&console.warn(e.messages.warningRemoveNoElements()),n.freeze(i),i},parseDatasetArgs:function(t){var a=this,i=new e.UserInstruction,o=null,r=-1;for(i.animate=a.config.animation.enable,i.command=new e.CommandDataset,r=0;r-1&&t.toggleArray.splice(a,1),i=t.getToggleSelector(),t.multimix({filter:i},e.animate,e.callback)},sort:function(){var t=this,e=t.parseSortArgs(arguments);return t.multimix({sort:e.command},e.animate,e.callback)},changeLayout:function(){var t=this,e=t.parseChangeLayoutArgs(arguments);return t.multimix({changeLayout:e.command},e.animate,e.callback)},dataset:function(){var t=this,n=t.parseDatasetArgs(arguments),a=null,i=null,o=!1;return t.callActions("beforeDataset",arguments),t.isBusy?(i=new e.QueueItem,i.args=arguments,i.instruction=n,t.queueMix(i)):(n.callback&&(t.userCallback=n.callback),o=n.animate^t.config.animation.enable?n.animate:t.config.animation.enable,a=t.getDataOperation(n.command.dataset),t.goMix(o,a))},multimix:function(){var t=this,n=null,a=!1,i=null,o=t.parseMultimixArgs(arguments);return t.callActions("beforeMultimix",arguments),t.isBusy?(i=new e.QueueItem,i.args=arguments,i.instruction=o,i.triggerElement=t.lastClicked,i.isToggling=t.isToggling,t.queueMix(i)):(n=t.getOperation(o.command),t.config.controls.enable&&(o.command.filter&&!t.isToggling&&(t.toggleArray.length=0,t.buildToggleArray(n.command)),t.queue.length<1&&t.updateControls(n.command)),o.callback&&(t.userCallback=o.callback),a=o.animate^t.config.animation.enable?o.animate:t.config.animation.enable,t.callFilters("operationMultimix",n,arguments),t.goMix(a,n))},getOperation:function(t){var a=this,i=t.sort,o=t.filter,r=t.changeLayout,s=t.remove,l=t.insert,c=new e.Operation;return c=a.callFilters("operationUnmappedGetOperation",c,arguments),c.id=n.randomHex(),c.command=t,c.startState=a.state,c.triggerElement=a.lastClicked,a.isBusy?(a.config.debug.showWarnings&&console.warn(e.messages.warningGetOperationInstanceBusy()),null):(l&&a.insertTargets(l,c),s&&(c.toRemove=s.targets),c.startSort=c.newSort=c.startState.activeSort,c.startOrder=c.newOrder=a.targets,i&&(c.startSort=c.startState.activeSort,c.newSort=i,c.willSort=a.willSort(i,c.startState.activeSort),c.willSort&&a.sortOperation(c)),c.startFilter=c.startState.activeFilter,o?c.newFilter=o:c.newFilter=n.extend(new e.CommandFilter,c.startFilter),"all"===c.newFilter.selector?c.newFilter.selector=a.config.selectors.target:"none"===c.newFilter.selector&&(c.newFilter.selector=""),a.filterOperation(c),c.startContainerClassName=c.startState.activeContainerClassName,r?(c.newContainerClassName=r.containerClassName,c.newContainerClassName!==c.startContainerClassName&&(c.willChangeLayout=!0)):c.newContainerClassName=c.startContainerClassName,a.config.animation.enable&&(a.getStartMixData(c),a.setInter(c),c.docState=n.getDocumentState(a.dom.document),a.getInterMixData(c),a.setFinal(c),a.getFinalMixData(c),a.parseEffects(),c.hasEffect=a.hasEffect(),a.getTweenData(c)),c.willSort&&(a.targets=c.newOrder),c.newState=a.buildState(c),a.callFilters("operationMappedGetOperation",c,arguments))},tween:function(t,e){var n=null,a=null,i=-1,o=-1;for(e=Math.min(e,1),e=Math.max(e,0),o=0;n=t.show[o];o++)a=t.showPosData[o],n.applyTween(a,e);for(o=0;n=t.hide[o];o++)n.isShown&&n.hide(),(i=t.toHide.indexOf(n))>-1&&(a=t.toHidePosData[i],n.isShown||n.show(),n.applyTween(a,e))},insert:function(){var t=this,e=t.parseInsertArgs(arguments);return t.multimix({insert:e.command},e.animate,e.callback)},insertBefore:function(){var t=this,e=t.parseInsertArgs(arguments);return t.insert(e.command.collection,"before",e.command.sibling,e.animate,e.callback)},insertAfter:function(){var t=this,e=t.parseInsertArgs(arguments);return t.insert(e.command.collection,"after",e.command.sibling,e.animate,e.callback)},prepend:function(){var t=this,e=t.parseInsertArgs(arguments);return t.insert(0,e.command.collection,e.animate,e.callback)},append:function(){var t=this,e=t.parseInsertArgs(arguments);return t.insert(t.state.totalTargets,e.command.collection,e.animate,e.callback)},remove:function(){var t=this,e=t.parseRemoveArgs(arguments);return t.multimix({remove:e.command},e.animate,e.callback)},getConfig:function(t){var e=this,a=null;return a=t?n.getProperty(e.config,t):e.config,e.callFilters("valueGetConfig",a,arguments)},configure:function(t){var e=this;e.callActions("beforeConfigure",arguments),n.extend(e.config,t,!0,!0),e.callActions("afterConfigure",arguments)},getState:function(){var t=this,a=null;return a=new e.State,n.extend(a,t.state),n.freeze(a),t.callFilters("stateGetState",a,arguments)},forceRefresh:function(){var t=this;t.indexTargets()},forceRender:function(){var t=this,e=null,n=null,a="";for(a in t.cache)e=t.cache[a],n=e.render(e.data),n!==e.dom.el&&(e.isInDom&&(e.unbindEvents(),t.dom.parent.replaceChild(n,e.dom.el)),e.isShown||(n.style.display="none"),e.dom.el=n,e.isInDom&&e.bindEvents());t.state=t.buildState(t.lastOperation)},destroy:function(t){var n=this,a=null,i=null,o=0;for(n.callActions("beforeDestroy",arguments),o=0;a=n.controls[o];o++)a.removeBinding(n);for(o=0;i=n.targets[o];o++)t&&i.show(),i.unbindEvents();n.dom.container.id.match(/^MixItUp/)&&n.dom.container.removeAttribute("id"),delete e.instances[n.id],n.callActions("afterDestroy",arguments)}}),e.IMoveData=function(){e.Base.call(this),this.callActions("beforeConstruct"),this.posIn=null,this.posOut=null,this.operation=null,this.callback=null,this.statusChange="",this.duration=-1,this.staggerIndex=-1,this.callActions("afterConstruct"),n.seal(this)},e.BaseStatic.call(e.IMoveData),e.IMoveData.prototype=Object.create(e.Base.prototype),e.IMoveData.prototype.constructor=e.IMoveData,e.TargetDom=function(){e.Base.call(this),this.callActions("beforeConstruct"),this.el=null,this.callActions("afterConstruct"),n.seal(this)},e.BaseStatic.call(e.TargetDom),e.TargetDom.prototype=Object.create(e.Base.prototype),e.TargetDom.prototype.constructor=e.TargetDom,e.Target=function(){e.Base.call(this),this.callActions("beforeConstruct"),this.id="",this.sortString="",this.mixer=null,this.callback=null,this.isShown=!1,this.isBound=!1,this.isExcluded=!1,this.isInDom=!1,this.handler=null,this.operation=null,this.data=null,this.dom=new e.TargetDom,this.callActions("afterConstruct"),n.seal(this)},e.BaseStatic.call(e.Target),e.Target.prototype=Object.create(e.Base.prototype),n.extend(e.Target.prototype,{constructor:e.Target,init:function(t,n,a){var i=this,o="";if(i.callActions("beforeInit",arguments),i.mixer=n,t||(t=i.render(a)),i.cacheDom(t),i.bindEvents(),"none"!==i.dom.el.style.display&&(i.isShown=!0),a&&n.config.data.uidKey){if("undefined"==typeof(o=a[n.config.data.uidKey])||o.toString().length<1)throw new TypeError(e.messages.errorDatasetInvalidUidKey({uidKey:n.config.data.uidKey}));i.id=o,i.data=a,n.cache[o]=i}i.callActions("afterInit",arguments)},render:function(t){var a=this,i=null,o=null,r=null,s="";if(a.callActions("beforeRender",arguments),i=a.callFilters("renderRender",a.mixer.config.render.target,arguments),"function"!=typeof i)throw new TypeError(e.messages.errorDatasetRendererNotSet());return s=i(t),s&&"object"==typeof s&&n.isElement(s)?o=s:"string"==typeof s&&(r=document.createElement("div"),r.innerHTML=s,o=r.firstElementChild),a.callFilters("elRender",o,arguments)},cacheDom:function(t){var e=this;e.callActions("beforeCacheDom",arguments),e.dom.el=t,e.callActions("afterCacheDom",arguments)},getSortString:function(t){var e=this,n=e.dom.el.getAttribute("data-"+t)||"";e.callActions("beforeGetSortString",arguments),n=isNaN(1*n)?n.toLowerCase():1*n,e.sortString=n,e.callActions("afterGetSortString",arguments)},show:function(){var t=this;t.callActions("beforeShow",arguments),t.isShown||(t.dom.el.style.display="",t.isShown=!0),t.callActions("afterShow",arguments)},hide:function(){var t=this;t.callActions("beforeHide",arguments),t.isShown&&(t.dom.el.style.display="none",t.isShown=!1),t.callActions("afterHide",arguments)},move:function(t){var e=this;e.callActions("beforeMove",arguments),e.isExcluded||e.mixer.targetsMoved++,e.applyStylesIn(t),requestAnimationFrame(function(){e.applyStylesOut(t)}),e.callActions("afterMove",arguments)},applyTween:function(t,n){var a=this,i="",o=null,r=t.posIn,s=[],l=new e.StyleData,c=-1;for(a.callActions("beforeApplyTween",arguments),l.x=r.x,l.y=r.y,0===n?a.hide():a.isShown||a.show(),c=0;i=e.features.TWEENABLE[c];c++)if(o=t.tweenData[i],"x"===i){if(!o)continue;l.x=r.x+o*n}else if("y"===i){if(!o)continue;l.y=r.y+o*n}else if(o instanceof e.TransformData){if(!o.value)continue;l[i].value=r[i].value+o.value*n,l[i].unit=o.unit,s.push(i+"("+l[i].value+o.unit+")")}else{if(!o)continue;l[i]=r[i]+o*n,a.dom.el.style[i]=l[i]}(l.x||l.y)&&s.unshift("translate("+l.x+"px, "+l.y+"px)"),s.length&&(a.dom.el.style[e.features.transformProp]=s.join(" ")),a.callActions("afterApplyTween",arguments)},applyStylesIn:function(t){var n=this,a=t.posIn,i=1!==n.mixer.effectsIn.opacity,o=[];n.callActions("beforeApplyStylesIn",arguments),o.push("translate("+a.x+"px, "+a.y+"px)"),n.mixer.config.animation.animateResizeTargets&&("show"!==t.statusChange&&(n.dom.el.style.width=a.width+"px",n.dom.el.style.height=a.height+"px"),n.dom.el.style.marginRight=a.marginRight+"px",n.dom.el.style.marginBottom=a.marginBottom+"px"),i&&(n.dom.el.style.opacity=a.opacity),"show"===t.statusChange&&(o=o.concat(n.mixer.transformIn)),n.dom.el.style[e.features.transformProp]=o.join(" "),n.callActions("afterApplyStylesIn",arguments)},applyStylesOut:function(t){var n=this,a=[],i=[],o=n.mixer.config.animation.animateResizeTargets,r="undefined"!=typeof n.mixer.effectsIn.opacity;if(n.callActions("beforeApplyStylesOut",arguments),a.push(n.writeTransitionRule(e.features.transformRule,t.staggerIndex)),"none"!==t.statusChange&&a.push(n.writeTransitionRule("opacity",t.staggerIndex,t.duration)),o&&(a.push(n.writeTransitionRule("width",t.staggerIndex,t.duration)),a.push(n.writeTransitionRule("height",t.staggerIndex,t.duration)),a.push(n.writeTransitionRule("margin",t.staggerIndex,t.duration))),!t.callback)return n.mixer.targetsImmovable++,void(n.mixer.targetsMoved===n.mixer.targetsImmovable&&n.mixer.cleanUp(t.operation));switch(n.operation=t.operation,n.callback=t.callback,!n.isExcluded&&n.mixer.targetsBound++,n.isBound=!0,n.applyTransition(a),o&&t.posOut.width>0&&t.posOut.height>0&&(n.dom.el.style.width=t.posOut.width+"px",n.dom.el.style.height=t.posOut.height+"px",n.dom.el.style.marginRight=t.posOut.marginRight+"px",n.dom.el.style.marginBottom=t.posOut.marginBottom+"px"),n.mixer.config.animation.nudge||"hide"!==t.statusChange||i.push("translate("+t.posOut.x+"px, "+t.posOut.y+"px)"),t.statusChange){case"hide":r&&(n.dom.el.style.opacity=n.mixer.effectsOut.opacity),i=i.concat(n.mixer.transformOut);break;case"show":r&&(n.dom.el.style.opacity=1)}(n.mixer.config.animation.nudge||!n.mixer.config.animation.nudge&&"hide"!==t.statusChange)&&i.push("translate("+t.posOut.x+"px, "+t.posOut.y+"px)"),n.dom.el.style[e.features.transformProp]=i.join(" "),n.callActions("afterApplyStylesOut",arguments)},writeTransitionRule:function(t,e,n){var a=this,i=a.getDelay(e),o="";return o=t+" "+(n>0?n:a.mixer.config.animation.duration)+"ms "+i+"ms "+("opacity"===t?"linear":a.mixer.config.animation.easing),a.callFilters("ruleWriteTransitionRule",o,arguments)},getDelay:function(t){var e=this,n=-1;return"function"==typeof e.mixer.config.animation.staggerSequence&&(t=e.mixer.config.animation.staggerSequence.call(e,t,e.state)),n=e.mixer.staggerDuration?t*e.mixer.staggerDuration:0,e.callFilters("delayGetDelay",n,arguments)},applyTransition:function(t){var n=this,a=t.join(", ");n.callActions("beforeApplyTransition",arguments),n.dom.el.style[e.features.transitionProp]=a,n.callActions("afterApplyTransition",arguments)},handleTransitionEnd:function(t){var e=this,n=t.propertyName,a=e.mixer.config.animation.animateResizeTargets;e.callActions("beforeHandleTransitionEnd",arguments),e.isBound&&t.target.matches(e.mixer.config.selectors.target)&&(n.indexOf("transform")>-1||n.indexOf("opacity")>-1||a&&n.indexOf("height")>-1||a&&n.indexOf("width")>-1||a&&n.indexOf("margin")>-1)&&(e.callback.call(e,e.operation),e.isBound=!1,e.callback=null,e.operation=null),e.callActions("afterHandleTransitionEnd",arguments)},eventBus:function(t){var e=this;switch(e.callActions("beforeEventBus",arguments),t.type){case"webkitTransitionEnd":case"transitionend":e.handleTransitionEnd(t)}e.callActions("afterEventBus",arguments)},unbindEvents:function(){var t=this;t.callActions("beforeUnbindEvents",arguments),n.off(t.dom.el,"webkitTransitionEnd",t.handler),n.off(t.dom.el,"transitionend",t.handler),t.callActions("afterUnbindEvents",arguments)},bindEvents:function(){var t=this,a="";t.callActions("beforeBindEvents",arguments),a="webkit"===e.features.transitionPrefix?"webkitTransitionEnd":"transitionend",t.handler=function(e){return t.eventBus(e)},n.on(t.dom.el,a,t.handler),t.callActions("afterBindEvents",arguments)},getPosData:function(n){var a=this,i={},o=null,r=new e.StyleData;return a.callActions("beforeGetPosData",arguments),r.x=a.dom.el.offsetLeft,r.y=a.dom.el.offsetTop,(a.mixer.config.animation.animateResizeTargets||n)&&(o=a.dom.el.getBoundingClientRect(),r.top=o.top,r.right=o.right,r.bottom=o.bottom,r.left=o.left,r.width=o.width,r.height=o.height),a.mixer.config.animation.animateResizeTargets&&(i=t.getComputedStyle(a.dom.el),r.marginBottom=parseFloat(i.marginBottom),r.marginRight=parseFloat(i.marginRight)),a.callFilters("posDataGetPosData",r,arguments)},cleanUp:function(){var t=this;t.callActions("beforeCleanUp",arguments),t.dom.el.style[e.features.transformProp]="",t.dom.el.style[e.features.transitionProp]="",t.dom.el.style.opacity="",t.mixer.config.animation.animateResizeTargets&&(t.dom.el.style.width="",t.dom.el.style.height="",t.dom.el.style.marginRight="",t.dom.el.style.marginBottom=""),t.callActions("afterCleanUp",arguments)}}),e.Collection=function(t){var e=null,a=-1;for(this.callActions("beforeConstruct"),a=0;e=t[a];a++)this[a]=e;this.length=t.length,this.callActions("afterConstruct"),n.freeze(this)},e.BaseStatic.call(e.Collection),e.Collection.prototype=Object.create(e.Base.prototype),n.extend(e.Collection.prototype,{constructor:e.Collection,mixitup:function(t){var a=this,i=null,o=Array.prototype.slice.call(arguments),r=[],s=-1;for(this.callActions("beforeMixitup"),o.shift(),s=0;i=a[s];s++)r.push(i[t].apply(i,o));return a.callFilters("promiseMixitup",n.all(r,e.libraries),arguments)}}),e.Operation=function(){e.Base.call(this),this.callActions("beforeConstruct"),this.id="",this.args=[],this.command=null,this.showPosData=[],this.toHidePosData=[],this.startState=null,this.newState=null,this.docState=null,this.willSort=!1,this.willChangeLayout=!1,this.hasEffect=!1,this.hasFailed=!1,this.triggerElement=null,this.show=[],this.hide=[],this.matching=[],this.toShow=[],this.toHide=[],this.toMove=[],this.toRemove=[],this.startOrder=[],this.newOrder=[],this.startSort=null,this.newSort=null,this.startFilter=null,this.newFilter=null,this.startDataset=null,this.newDataset=null,this.viewportDeltaX=0,this.viewportDeltaY=0,this.startX=0,this.startY=0,this.startHeight=0,this.startWidth=0,this.newX=0,this.newY=0,this.newHeight=0,this.newWidth=0,this.startContainerClassName="",this.startDisplay="",this.newContainerClassName="",this.newDisplay="",this.callActions("afterConstruct"),n.seal(this)},e.BaseStatic.call(e.Operation),e.Operation.prototype=Object.create(e.Base.prototype),e.Operation.prototype.constructor=e.Operation,e.State=function(){e.Base.call(this),this.callActions("beforeConstruct"),this.id="",this.activeFilter=null,this.activeSort=null,this.activeContainerClassName="",this.container=null,this.targets=[],this.hide=[],this.show=[],this.matching=[],this.totalTargets=-1,this.totalShow=-1,this.totalHide=-1,this.totalMatching=-1,this.hasFailed=!1,this.triggerElement=null,this.activeDataset=null,this.callActions("afterConstruct"),n.seal(this)},e.BaseStatic.call(e.State),e.State.prototype=Object.create(e.Base.prototype),e.State.prototype.constructor=e.State,e.UserInstruction=function(){e.Base.call(this),this.callActions("beforeConstruct"),this.command={},this.animate=!1,this.callback=null,this.callActions("afterConstruct"),n.seal(this)},e.BaseStatic.call(e.UserInstruction),e.UserInstruction.prototype=Object.create(e.Base.prototype),e.UserInstruction.prototype.constructor=e.UserInstruction,e.Messages=function(){e.Base.call(this),this.callActions("beforeConstruct"),this.ERROR_FACTORY_INVALID_CONTAINER="[MixItUp] An invalid selector or element reference was passed to the mixitup factory function",this.ERROR_FACTORY_CONTAINER_NOT_FOUND="[MixItUp] The provided selector yielded no container element",this.ERROR_CONFIG_INVALID_ANIMATION_EFFECTS="[MixItUp] Invalid value for `animation.effects`",this.ERROR_CONFIG_INVALID_CONTROLS_SCOPE="[MixItUp] Invalid value for `controls.scope`",this.ERROR_CONFIG_INVALID_PROPERTY='[MixitUp] Invalid configuration object property "${erroneous}"${suggestion}',this.ERROR_CONFIG_INVALID_PROPERTY_SUGGESTION='. Did you mean "${probableMatch}"?',this.ERROR_CONFIG_DATA_UID_KEY_NOT_SET="[MixItUp] To use the dataset API, a UID key must be specified using `data.uidKey`",this.ERROR_DATASET_INVALID_UID_KEY='[MixItUp] The specified UID key "${uidKey}" is not present on one or more dataset items',this.ERROR_DATASET_DUPLICATE_UID='[MixItUp] The UID "${uid}" was found on two or more dataset items. UIDs must be unique.',this.ERROR_INSERT_INVALID_ARGUMENTS="[MixItUp] Please provider either an index or a sibling and position to insert, not both",this.ERROR_INSERT_PREEXISTING_ELEMENT="[MixItUp] An element to be inserted already exists in the container",this.ERROR_FILTER_INVALID_ARGUMENTS="[MixItUp] Please provide either a selector or collection `.filter()`, not both",this.ERROR_DATASET_NOT_SET="[MixItUp] To use the dataset API with pre-rendered targets, a starting dataset must be set using `load.dataset`",this.ERROR_DATASET_PRERENDERED_MISMATCH="[MixItUp] `load.dataset` does not match pre-rendered targets",this.ERROR_DATASET_RENDERER_NOT_SET="[MixItUp] To insert an element via the dataset API, a target renderer function must be provided to `render.target`",this.ERROR_SORT_NON_EXISTENT_ELEMENT="[MixItUp] An element to be sorted does not already exist in the container",this.WARNING_FACTORY_PREEXISTING_INSTANCE="[MixItUp] WARNING: This element already has an active MixItUp instance. The provided configuration object will be ignored. If you wish to perform additional methods on this instance, please create a reference.",this.WARNING_INSERT_NO_ELEMENTS="[MixItUp] WARNING: No valid elements were passed to `.insert()`",this.WARNING_REMOVE_NO_ELEMENTS="[MixItUp] WARNING: No valid elements were passed to `.remove()`",this.WARNING_MULTIMIX_INSTANCE_QUEUE_FULL="[MixItUp] WARNING: An operation was requested but the MixItUp instance was busy. The operation was rejected because the queue is full or queuing is disabled.",this.WARNING_GET_OPERATION_INSTANCE_BUSY="[MixItUp] WARNING: Operations can be be created while the MixItUp instance is busy.",this.WARNING_NO_PROMISE_IMPLEMENTATION="[MixItUp] WARNING: No Promise implementations could be found. If you wish to use promises with MixItUp please install an ES6 Promise polyfill.",this.WARNING_INCONSISTENT_SORTING_ATTRIBUTES='[MixItUp] WARNING: The requested sorting data attribute "${attribute}" was not present on one or more target elements which may product unexpected sort output',this.callActions("afterConstruct"),this.compileTemplates(),n.seal(this)},e.BaseStatic.call(e.Messages),e.Messages.prototype=Object.create(e.Base.prototype),e.Messages.prototype.constructor=e.Messages,e.Messages.prototype.compileTemplates=function(){var t="",e="";for(t in this)"string"==typeof(e=this[t])&&(this[n.camelCase(t)]=n.template(e))},e.messages=new e.Messages,e.Facade=function(t){e.Base.call(this),this.callActions("beforeConstruct",arguments),this.configure=t.configure.bind(t),this.show=t.show.bind(t),this.hide=t.hide.bind(t),this.filter=t.filter.bind(t),this.toggleOn=t.toggleOn.bind(t),this.toggleOff=t.toggleOff.bind(t),this.sort=t.sort.bind(t),this.changeLayout=t.changeLayout.bind(t),this.multimix=t.multimix.bind(t),this.dataset=t.dataset.bind(t),this.tween=t.tween.bind(t),this.insert=t.insert.bind(t),this.insertBefore=t.insertBefore.bind(t),this.insertAfter=t.insertAfter.bind(t),this.prepend=t.prepend.bind(t),this.append=t.append.bind(t),this.remove=t.remove.bind(t),this.destroy=t.destroy.bind(t),this.forceRefresh=t.forceRefresh.bind(t),this.forceRender=t.forceRender.bind(t),this.isMixing=t.isMixing.bind(t),this.getOperation=t.getOperation.bind(t),this.getConfig=t.getConfig.bind(t),this.getState=t.getState.bind(t),this.callActions("afterConstruct",arguments),n.freeze(this),n.seal(this)},e.BaseStatic.call(e.Facade),e.Facade.prototype=Object.create(e.Base.prototype),e.Facade.prototype.constructor=e.Facade,"object"==typeof exports&&"object"==typeof module?module.exports=e:"function"==typeof define&&define.amd?define(function(){return e}):"undefined"!=typeof t.mixitup&&"function"==typeof t.mixitup||(t.mixitup=e),e.BaseStatic.call(e.constructor),e.NAME="mixitup",e.CORE_VERSION="3.3.1"}(window); \ No newline at end of file diff --git a/mixitup-3.3.1/demos/mock-api.js b/mixitup-3.3.1/demos/mock-api.js new file mode 100644 index 0000000..4036b92 --- /dev/null +++ b/mixitup-3.3.1/demos/mock-api.js @@ -0,0 +1,94 @@ +(function(window) { + var Api = function(dataset) { + var _db = []; + + Object.defineProperties(this, { + db: { + get: function() { + return _db.slice(); + }, + set: function(value) { + if (!Array.isArray(value)) { + throw new TypeError('[mock-api] Dataset must be an array'); + } + + if (!value.length) { + throw new TypeError('[mock-api] Dataset must contain one or more elements'); + } + + _db = value; + } + } + }); + + this.init(dataset); + }; + + Api.prototype = { + constructor: Api, + + init: function(dataset) { + this.db = dataset; + }, + + get: function(query) { + var self = this; + + return Promise.resolve() + .then(function() { + var output; + + query = Object.assign(new Api.Query(), query); + + Object.freeze(query); + + output = self.filter(self.db, query); + + output = self.sort(output, query); + + return output; + }); + }, + + filter: function(input, query) { + return input.filter(function(item) { + var key; + var value; + + for (key in query) { + if (key.match(/^\$/g)) continue; + + value = query[key]; + + if (value === 'all') return true; + + if (item[key] !== value) return false; + } + + return true; + }); + }, + + sort: function(input, query) { + return input.sort(function(a, b) { + var valueA = a[query.$sort_by]; + var valueB = b[query.$sort_by]; + + if (valueA > valueB) { + return query.$order === 'asc' ? 1 : -1; + } else if (valueA < valueB) { + return query.$order === 'asc' ? -1 : 1; + } else { + return 0; + } + }); + } + }; + + Api.Query = function() { + this.$sort_by = 'id'; + this.$order = 'asc'; + }; + + window.Api = Api; +})(window); \ No newline at end of file diff --git a/mixitup-3.3.1/demos/multiple-instances-global-control-scoping/index.html b/mixitup-3.3.1/demos/multiple-instances-global-control-scoping/index.html new file mode 100644 index 0000000..0dab0e5 --- /dev/null +++ b/mixitup-3.3.1/demos/multiple-instances-global-control-scoping/index.html @@ -0,0 +1,53 @@ + + + + + + + + + MixItUp Demo - Multiple Mixers with Global Control Scoping + + +
+ + + + + + + + +
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/multiple-instances-global-control-scoping/style.css b/mixitup-3.3.1/demos/multiple-instances-global-control-scoping/style.css new file mode 100644 index 0000000..5194a0d --- /dev/null +++ b/mixitup-3.3.1/demos/multiple-instances-global-control-scoping/style.css @@ -0,0 +1,184 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + font-size: 0.1px; + margin-bottom: 1rem; + padding: 1rem; +} + +.control { + position: relative; + display: inline-block; + width: 2.7rem; + height: 2.7rem; + background: #444; + cursor: pointer; + font-size: 0.1px; + color: white; + transition: background 150ms; +} + +.control:hover { + background: #3f3f3f; +} + +.control[data-filter]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + top: calc(50% - 6px); + left: calc(50% - 6px); + border: 2px solid currentColor; + border-radius: 2px; + background: currentColor; + transition: background-color 150ms, border-color 150ms; +} + +.control[data-sort]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + border-top: 2px solid; + border-left: 2px solid; + top: calc(50% - 6px); + left: calc(50% - 6px); + transform: translateY(1px) rotate(45deg); +} + +.control[data-sort*=":desc"]:after { + transform: translateY(-4px) rotate(-135deg); +} + +.mixitup-control-active { + background: #393939; +} + +.mixitup-control-active[data-filter]:after { + background: transparent; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +.control[data-filter=".green"] { + color: #91e6c7; +} + +.control[data-filter=".blue"] { + color: #5ecdde; +} + +.control[data-filter=".pink"] { + color: #d595aa; +} + +.control[data-filter="none"] { + color: #2f2f2f; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + text-align: justify; + font-size: 0.1px; +} + +.targets:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix, +.gap { + display: inline-block; + vertical-align: top; +} + +.mix { + background: #fff; + border-top: .5rem solid currentColor; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +.mix.green { + color: #91e6c7; +} + +.mix.pink { + color: #d595aa; +} + +.mix.blue { + color: #5ecdde; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.mix, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .mix, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .mix, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .mix, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} \ No newline at end of file diff --git a/mixitup-3.3.1/demos/multiple-instances-local-control-scoping/index.html b/mixitup-3.3.1/demos/multiple-instances-local-control-scoping/index.html new file mode 100644 index 0000000..0adbb64 --- /dev/null +++ b/mixitup-3.3.1/demos/multiple-instances-local-control-scoping/index.html @@ -0,0 +1,74 @@ + + + + + + + + + MixItUp Demo - Multiple Mixers with Local Control Scoping + + +
+
+ + + + + + + + +
+ +
+
+
+
+ +
+
+
+
+
+ +
+
+ + + + + + + + +
+ +
+
+
+
+ +
+
+
+
+
+ + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/multiple-instances-local-control-scoping/style.css b/mixitup-3.3.1/demos/multiple-instances-local-control-scoping/style.css new file mode 100644 index 0000000..42805b1 --- /dev/null +++ b/mixitup-3.3.1/demos/multiple-instances-local-control-scoping/style.css @@ -0,0 +1,186 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + font-size: 0.1px; + margin-bottom: 1rem; +} + +.control { + position: relative; + display: inline-block; + width: 2.7rem; + height: 2.7rem; + background: #444; + cursor: pointer; + font-size: 0.1px; + color: white; + transition: background 150ms; +} + +.control:hover { + background: #3f3f3f; +} + +.control[data-filter]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + top: calc(50% - 6px); + left: calc(50% - 6px); + border: 2px solid currentColor; + border-radius: 2px; + background: currentColor; + transition: background-color 150ms, border-color 150ms; +} + +.control[data-sort]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + border-top: 2px solid; + border-left: 2px solid; + top: calc(50% - 6px); + left: calc(50% - 6px); + transform: translateY(1px) rotate(45deg); +} + +.control[data-sort*=":desc"]:after { + transform: translateY(-4px) rotate(-135deg); +} + +.mixitup-control-active { + background: #393939; +} + +.mixitup-control-active[data-filter]:after { + background: transparent; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +.control[data-filter=".green"] { + color: #91e6c7; +} + +.control[data-filter=".blue"] { + color: #5ecdde; +} + +.control[data-filter=".pink"] { + color: #d595aa; +} + +.control[data-filter="none"] { + color: #2f2f2f; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; +} + +.targets { + text-align: justify; + font-size: 0.1px; +} + +.targets:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix, +.gap { + display: inline-block; + vertical-align: top; +} + +.mix { + background: #fff; + border-top: .5rem solid currentColor; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +.mix.green { + color: #91e6c7; +} + +.mix.pink { + color: #d595aa; +} + +.mix.blue { + color: #5ecdde; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.mix, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .mix, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .mix, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .mix, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} \ No newline at end of file diff --git a/mixitup-3.3.1/demos/radio-buttons/index.html b/mixitup-3.3.1/demos/radio-buttons/index.html new file mode 100644 index 0000000..b55395a --- /dev/null +++ b/mixitup-3.3.1/demos/radio-buttons/index.html @@ -0,0 +1,93 @@ + + + + + + + + + MixItUp Demo - Radio Buttons + + +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ +
+
+ + +
+ +
+ + +
+
+
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/radio-buttons/style.css b/mixitup-3.3.1/demos/radio-buttons/style.css new file mode 100644 index 0000000..9b6994a --- /dev/null +++ b/mixitup-3.3.1/demos/radio-buttons/style.css @@ -0,0 +1,200 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 1rem; + background: #333; + font-size: 0.1px; +} + +.control { + position: relative; + display: inline-block; + width: 2.7rem; + height: 2.7rem; + background: #444; + cursor: pointer; + font-size: 0.1px; + color: white; + transition: background 150ms; +} + +.control:hover { + background: #3f3f3f; +} + +.control[data-filter]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + top: calc(50% - 6px); + left: calc(50% - 6px); + border: 2px solid currentColor; + border-radius: 2px; + background: currentColor; + transition: background-color 150ms, border-color 150ms; +} + +.control[data-sort]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + border-top: 2px solid; + border-left: 2px solid; + top: calc(50% - 6px); + left: calc(50% - 6px); + transform: translateY(1px) rotate(45deg); +} + +.control[data-sort*=":desc"]:after { + transform: translateY(-4px) rotate(-135deg); +} + +.mixitup-control-active { + background: #393939; +} + +.mixitup-control-active[data-filter]:after { + background: transparent; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +.radio-group { + display: inline-block; + padding: .5rem; + background: #2a2a2a; + margin-right: .75rem; + vertical-align: top; +} + +.radio { + text-align: justify; +} + +.radio:after { + content: ''; + display: inline-block; + width: 100%; +} + +.radio-input, +.radio-label { + display: inline-block; +} + +.radio-label { + color: white; + font-family: 'helvetica-neue', arial, sans-serif; + font-size: .9rem; + margin-right: .5rem; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + text-align: justify; + font-size: 0.1px; +} + +.container:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix, +.gap { + display: inline-block; + vertical-align: top; +} + +.mix { + background: #fff; + border-top: .5rem solid currentColor; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +.mix.green { + color: #91e6c7; +} + +.mix.pink { + color: #d595aa; +} + +.mix.blue { + color: #5ecdde; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.mix, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .mix, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .mix, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .mix, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} + + diff --git a/mixitup-3.3.1/demos/removal-by-reference/index.html b/mixitup-3.3.1/demos/removal-by-reference/index.html new file mode 100644 index 0000000..0d5d9c6 --- /dev/null +++ b/mixitup-3.3.1/demos/removal-by-reference/index.html @@ -0,0 +1,62 @@ + + + + + + + + + MixItUp Demo - Removal by Reference + + +
+ + + + + + + + +
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/removal-by-reference/style.css b/mixitup-3.3.1/demos/removal-by-reference/style.css new file mode 100644 index 0000000..6be3c51 --- /dev/null +++ b/mixitup-3.3.1/demos/removal-by-reference/style.css @@ -0,0 +1,196 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 1rem; + background: #333; + font-size: 0.1px; +} + +.control { + position: relative; + display: inline-block; + width: 2.7rem; + height: 2.7rem; + background: #444; + cursor: pointer; + font-size: 0.1px; + color: white; + transition: background 150ms; +} + +.control:hover { + background: #3f3f3f; +} + +.control[data-filter]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + top: calc(50% - 6px); + left: calc(50% - 6px); + border: 2px solid currentColor; + border-radius: 2px; + background: currentColor; + transition: background-color 150ms, border-color 150ms; +} + +.control[data-sort]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + border-top: 2px solid; + border-left: 2px solid; + top: calc(50% - 6px); + left: calc(50% - 6px); + transform: translateY(1px) rotate(45deg); +} + +.control[data-sort*=":desc"]:after { + transform: translateY(-4px) rotate(-135deg); +} + +.mixitup-control-active { + background: #393939; +} + +.mixitup-control-active[data-filter]:after { + background: transparent; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +.control[data-filter] + .control[data-sort] { + margin-left: .75rem; +} + +.control[data-filter=".green"] { + color: #91e6c7; +} + +.control[data-filter=".blue"] { + color: #5ecdde; +} + +.control[data-filter=".pink"] { + color: #d595aa; +} + +.control[data-filter="none"] { + color: #2f2f2f; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + text-align: justify; + font-size: 0.1px; +} + +.container:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix, +.gap { + display: inline-block; + vertical-align: top; +} + +.mix { + background: #fff; + border-top: .5rem solid currentColor; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; + cursor: pointer; + transition: opacity 150ms; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +.mix.green { + color: #91e6c7; +} + +.mix.pink { + color: #d595aa; +} + +.mix.blue { + color: #5ecdde; +} + +.mix:hover { + opacity: 0.5; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.mix, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .mix, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .mix, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .mix, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} + + diff --git a/mixitup-3.3.1/demos/reset.css b/mixitup-3.3.1/demos/reset.css new file mode 100644 index 0000000..cd114cc --- /dev/null +++ b/mixitup-3.3.1/demos/reset.css @@ -0,0 +1,72 @@ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} + +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} + +body { + line-height: 1; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: antialiased; +} + +blockquote, q { + quotes: none; +} + +a { + text-decoration: none; +} + +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} + +button { + background: transparent; + border-radius: 0; + border: 0; + padding: 0; + + -webkit-appearance: none; + -webkit-border-radius: 0; + + user-select: none; +} + +button:focus { + outline: 0 none; +} + +button::-moz-focus-inner { + padding: 0; + border: 0; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} \ No newline at end of file diff --git a/mixitup-3.3.1/demos/select-dropdowns/index.html b/mixitup-3.3.1/demos/select-dropdowns/index.html new file mode 100644 index 0000000..c165c0c --- /dev/null +++ b/mixitup-3.3.1/demos/select-dropdowns/index.html @@ -0,0 +1,67 @@ + + + + + + + + + MixItUp Demo - Select Dropdowns + + +
+ + + +
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + diff --git a/mixitup-3.3.1/demos/select-dropdowns/style.css b/mixitup-3.3.1/demos/select-dropdowns/style.css new file mode 100644 index 0000000..c8ae360 --- /dev/null +++ b/mixitup-3.3.1/demos/select-dropdowns/style.css @@ -0,0 +1,111 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 1rem; + background: #333; + font-size: 0.1px; +} + +.controls > select { + margin-right: .75rem; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + text-align: justify; + font-size: 0.1px; +} + +.container:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix, +.gap { + display: inline-block; + vertical-align: top; +} + +.mix { + background: #fff; + border-top: .5rem solid currentColor; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +.mix.green { + color: #91e6c7; +} + +.mix.pink { + color: #d595aa; +} + +.mix.blue { + color: #5ecdde; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.mix, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .mix, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .mix, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .mix, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} \ No newline at end of file diff --git a/mixitup-3.3.1/demos/sorting-by-attribute/index.html b/mixitup-3.3.1/demos/sorting-by-attribute/index.html new file mode 100644 index 0000000..7a776be --- /dev/null +++ b/mixitup-3.3.1/demos/sorting-by-attribute/index.html @@ -0,0 +1,44 @@ + + + + + + + + + MixItUp Demo - Sorting by Attribute + + +
+ + +
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/sorting-by-attribute/style.css b/mixitup-3.3.1/demos/sorting-by-attribute/style.css new file mode 100644 index 0000000..927e310 --- /dev/null +++ b/mixitup-3.3.1/demos/sorting-by-attribute/style.css @@ -0,0 +1,157 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 1rem; + background: #333; + font-size: 0.1px; +} + +.control { + position: relative; + display: inline-block; + width: 2.7rem; + height: 2.7rem; + background: #444; + cursor: pointer; + font-size: 0.1px; + color: white; + transition: background 150ms; +} + +.control:hover { + background: #3f3f3f; +} + +.control[data-sort]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + border-top: 2px solid; + border-left: 2px solid; + top: calc(50% - 6px); + left: calc(50% - 6px); + transform: translateY(1px) rotate(45deg); +} + +.control[data-sort*=":desc"]:after { + transform: translateY(-4px) rotate(-135deg); +} + +.mixitup-control-active { + background: #393939; +} + +.mixitup-control-active[data-filter]:after { + background: transparent; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + text-align: justify; + font-size: 0.1px; +} + +.container:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix, +.gap { + display: inline-block; + vertical-align: top; +} + +.mix { + background: #fff; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; + font-family: 'helvetica-neue', arial, sans-serif; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +.mix[data-published-date]:after { + position: absolute; + content: attr(data-published-date); + font-size: 1rem; + font-weight: bold; + color: #aaa; + padding: 1rem; + width: 100%; + top: 0; + left: 0; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.mix, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .mix, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .mix, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .mix, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} + + diff --git a/mixitup-3.3.1/demos/sorting-by-default/index.html b/mixitup-3.3.1/demos/sorting-by-default/index.html new file mode 100644 index 0000000..245873c --- /dev/null +++ b/mixitup-3.3.1/demos/sorting-by-default/index.html @@ -0,0 +1,40 @@ + + + + + + + + + MixItUp Demo - Sorting by Default + + +
+ + +
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/sorting-by-default/style.css b/mixitup-3.3.1/demos/sorting-by-default/style.css new file mode 100644 index 0000000..fc9fe00 --- /dev/null +++ b/mixitup-3.3.1/demos/sorting-by-default/style.css @@ -0,0 +1,145 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 1rem; + background: #333; + font-size: 0.1px; +} + +.control { + position: relative; + display: inline-block; + width: 2.7rem; + height: 2.7rem; + background: #444; + cursor: pointer; + font-size: 0.1px; + color: white; + transition: background 150ms; +} + +.control:hover { + background: #3f3f3f; +} + +.control[data-sort]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + border-top: 2px solid; + border-left: 2px solid; + top: calc(50% - 6px); + left: calc(50% - 6px); + transform: translateY(1px) rotate(45deg); +} + +.control[data-sort*=":desc"]:after { + transform: translateY(-4px) rotate(-135deg); +} + +.mixitup-control-active { + background: #393939; +} + +.mixitup-control-active[data-filter]:after { + background: transparent; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + text-align: justify; + font-size: 0.1px; +} + +.container:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix, +.gap { + display: inline-block; + vertical-align: top; +} + +.mix { + background: #fff; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; + font-family: 'helvetica-neue', arial, sans-serif; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.mix, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .mix, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .mix, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .mix, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} + + diff --git a/mixitup-3.3.1/demos/sorting-by-multiple-attributes/index.html b/mixitup-3.3.1/demos/sorting-by-multiple-attributes/index.html new file mode 100644 index 0000000..f61d1d5 --- /dev/null +++ b/mixitup-3.3.1/demos/sorting-by-multiple-attributes/index.html @@ -0,0 +1,45 @@ + + + + + + + + + MixItUp Demo - Sorting by Multiple Attributes + + +
+ + +
+ +
+

Oscar

+

Charlie

+

Lima

+

Alpha

+

Mike

+

Zulu

+

Tango

+

Sierra

+

Bravo

+ +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/sorting-by-multiple-attributes/style.css b/mixitup-3.3.1/demos/sorting-by-multiple-attributes/style.css new file mode 100644 index 0000000..7bc430d --- /dev/null +++ b/mixitup-3.3.1/demos/sorting-by-multiple-attributes/style.css @@ -0,0 +1,165 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 1rem; + background: #333; + font-size: 0.1px; +} + +.control { + position: relative; + display: inline-block; + width: 2.7rem; + height: 2.7rem; + background: #444; + cursor: pointer; + font-size: 0.1px; + color: white; + transition: background 150ms; +} + +.control:hover { + background: #3f3f3f; +} + +.control[data-sort]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + border-top: 2px solid; + border-left: 2px solid; + top: calc(50% - 6px); + left: calc(50% - 6px); + transform: translateY(1px) rotate(45deg); +} + +.control[data-sort*=":desc"]:after { + transform: translateY(-4px) rotate(-135deg); +} + +.mixitup-control-active { + background: #393939; +} + +.mixitup-control-active[data-filter]:after { + background: transparent; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + text-align: justify; + font-size: 0.1px; +} + +.container:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix, +.gap { + display: inline-block; + vertical-align: top; +} + +.mix { + background: #fff; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; + font-family: 'helvetica-neue', arial, sans-serif; + font-size: 1rem; + font-weight: bold; + color: #aaa; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +.mix[data-published-date]:after { + position: absolute; + content: attr(data-published-date); + padding: 1rem; + width: 100%; + top: 0; + left: 0; +} + +.mix h4 { + position: absolute; + padding: 1rem; + bottom: 0; + left: 0; + color: #333; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.mix, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .mix, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .mix, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .mix, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} + + diff --git a/mixitup-3.3.1/demos/toggle-filtering-and-logic/index.html b/mixitup-3.3.1/demos/toggle-filtering-and-logic/index.html new file mode 100644 index 0000000..3a34f2a --- /dev/null +++ b/mixitup-3.3.1/demos/toggle-filtering-and-logic/index.html @@ -0,0 +1,46 @@ + + + + + + + + + MixItUp Demo - Toggle Filtering AND Logic + + +
+ + + + +
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/toggle-filtering-and-logic/style.css b/mixitup-3.3.1/demos/toggle-filtering-and-logic/style.css new file mode 100644 index 0000000..ad8b6f8 --- /dev/null +++ b/mixitup-3.3.1/demos/toggle-filtering-and-logic/style.css @@ -0,0 +1,186 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 1rem; + background: #333; + font-size: 0.1px; +} + +.control { + position: relative; + display: inline-block; + width: 2.7rem; + height: 2.7rem; + background: #444; + cursor: pointer; + font-size: 0.1px; + color: white; + transition: background 150ms; +} + +.control:hover { + background: #3f3f3f; +} + +.control[data-filter]:after, +.control[data-toggle]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + top: calc(50% - 6px); + left: calc(50% - 6px); + border: 2px solid currentColor; + border-radius: 2px; + background: currentColor; + transition: background-color 150ms, border-color 150ms; +} + +.mixitup-control-active { + background: #393939; +} + +.mixitup-control-active[data-toggle]:after { + background: transparent; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +.control[data-filter=".green"], +.control[data-toggle=".green"] { + color: #91e6c7; +} + +.control[data-filter=".blue"], +.control[data-toggle=".blue"] { + color: #5ecdde; +} + +.control[data-filter=".pink"], +.control[data-toggle=".pink"] { + color: #d595aa; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + text-align: justify; + font-size: 0.1px; +} + +.container:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix, +.gap { + display: inline-block; + vertical-align: top; +} + +.mix { + background: #fff; + border-top: .5rem solid currentColor; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; + border-top: .5rem solid transparent; +} + +.mix.green { + color: #91e6c7; +} + +.mix.pink { + color: #d595aa; +} + +.mix.blue { + color: #5ecdde; +} + +.mix.pink.green:before { + width: 100%; + border-top-color: #91e6c7; +} + +.mix.green.blue:before { + width: 100%; + border-top-color: #91e6c7; +} + +.mix.blue.pink:before { + width: 100%; + border-top-color: #d595aa; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.mix, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .mix, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .mix, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .mix, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} + + diff --git a/mixitup-3.3.1/demos/toggle-filtering-or-logic/index.html b/mixitup-3.3.1/demos/toggle-filtering-or-logic/index.html new file mode 100644 index 0000000..2d3dc31 --- /dev/null +++ b/mixitup-3.3.1/demos/toggle-filtering-or-logic/index.html @@ -0,0 +1,42 @@ + + + + + + + + + MixItUp Demo - Toggle Filtering OR Logic + + +
+ + + + +
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/demos/toggle-filtering-or-logic/style.css b/mixitup-3.3.1/demos/toggle-filtering-or-logic/style.css new file mode 100644 index 0000000..43d9388 --- /dev/null +++ b/mixitup-3.3.1/demos/toggle-filtering-or-logic/style.css @@ -0,0 +1,170 @@ +html, +body { + height: 100%; + background: #f2f2f2; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Controls +---------------------------------------------------------------------- */ + +.controls { + padding: 1rem; + background: #333; + font-size: 0.1px; +} + +.control { + position: relative; + display: inline-block; + width: 2.7rem; + height: 2.7rem; + background: #444; + cursor: pointer; + font-size: 0.1px; + color: white; + transition: background 150ms; +} + +.control:hover { + background: #3f3f3f; +} + +.control[data-filter]:after, +.control[data-toggle]:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + top: calc(50% - 6px); + left: calc(50% - 6px); + border: 2px solid currentColor; + border-radius: 2px; + background: currentColor; + transition: background-color 150ms, border-color 150ms; +} + +.mixitup-control-active { + background: #393939; +} + +.mixitup-control-active[data-toggle]:after { + background: transparent; +} + +.control:first-of-type { + border-radius: 3px 0 0 3px; +} + +.control:last-of-type { + border-radius: 0 3px 3px 0; +} + +.control[data-filter=".green"], +.control[data-toggle=".green"] { + color: #91e6c7; +} + +.control[data-filter=".blue"], +.control[data-toggle=".blue"] { + color: #5ecdde; +} + +.control[data-filter=".pink"], +.control[data-toggle=".pink"] { + color: #d595aa; +} + +/* Container +---------------------------------------------------------------------- */ + +.container { + padding: 1rem; + text-align: justify; + font-size: 0.1px; +} + +.container:after { + content: ''; + display: inline-block; + width: 100%; +} + +/* Target Elements +---------------------------------------------------------------------- */ + +.mix, +.gap { + display: inline-block; + vertical-align: top; +} + +.mix { + background: #fff; + border-top: .5rem solid currentColor; + border-radius: 2px; + margin-bottom: 1rem; + position: relative; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 56.25%; +} + +.mix.green { + color: #91e6c7; +} + +.mix.pink { + color: #d595aa; +} + +.mix.blue { + color: #5ecdde; +} + +/* Grid Breakpoints +---------------------------------------------------------------------- */ + +/* 2 Columns */ + +.mix, +.gap { + width: calc(100%/2 - (((2 - 1) * 1rem) / 2)); +} + +/* 3 Columns */ + +@media screen and (min-width: 541px) { + .mix, + .gap { + width: calc(100%/3 - (((3 - 1) * 1rem) / 3)); + } +} + +/* 4 Columns */ + +@media screen and (min-width: 961px) { + .mix, + .gap { + width: calc(100%/4 - (((4 - 1) * 1rem) / 4)); + } +} + +/* 5 Columns */ + +@media screen and (min-width: 1281px) { + .mix, + .gap { + width: calc(100%/5 - (((5 - 1) * 1rem) / 5)); + } +} + + diff --git a/mixitup-3.3.1/dist/mixitup.js b/mixitup-3.3.1/dist/mixitup.js new file mode 100644 index 0000000..a9af1f7 --- /dev/null +++ b/mixitup-3.3.1/dist/mixitup.js @@ -0,0 +1,10682 @@ +/**! + * MixItUp v3.3.1 + * A high-performance, dependency-free library for animated filtering, sorting and more + * Build 94e0fbf6-cd0b-4987-b3c0-14b59b67b8a0 + * + * @copyright Copyright 2014-2018 KunkaLabs Limited. + * @author KunkaLabs Limited. + * @link https://www.kunkalabs.com/mixitup/ + * + * @license Commercial use requires a commercial license. + * https://www.kunkalabs.com/mixitup/licenses/ + * + * Non-commercial use permitted under same terms as CC BY-NC 3.0 license. + * http://creativecommons.org/licenses/by-nc/3.0/ + */ + +(function(window) { + 'use strict'; + + var mixitup = null, + h = null; + + (function() { + var VENDORS = ['webkit', 'moz', 'o', 'ms'], + canary = window.document.createElement('div'), + i = -1; + + // window.requestAnimationFrame + + for (i = 0; i < VENDORS.length && !window.requestAnimationFrame; i++) { + window.requestAnimationFrame = window[VENDORS[i] + 'RequestAnimationFrame']; + } + + // Element.nextElementSibling + + if (typeof canary.nextElementSibling === 'undefined') { + Object.defineProperty(window.Element.prototype, 'nextElementSibling', { + get: function() { + var el = this.nextSibling; + + while (el) { + if (el.nodeType === 1) { + return el; + } + + el = el.nextSibling; + } + + return null; + } + }); + } + + // Element.matches + + (function(ElementPrototype) { + ElementPrototype.matches = + ElementPrototype.matches || + ElementPrototype.machesSelector || + ElementPrototype.mozMatchesSelector || + ElementPrototype.msMatchesSelector || + ElementPrototype.oMatchesSelector || + ElementPrototype.webkitMatchesSelector || + function (selector) { + return Array.prototype.indexOf.call(this.parentElement.querySelectorAll(selector), this) > -1; + }; + })(window.Element.prototype); + + // Object.keys + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys + + if (!Object.keys) { + Object.keys = (function() { + var hasOwnProperty = Object.prototype.hasOwnProperty, + hasDontEnumBug = false, + dontEnums = [], + dontEnumsLength = -1; + + hasDontEnumBug = !({ + toString: null + }) + .propertyIsEnumerable('toString'); + + dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' + ]; + + dontEnumsLength = dontEnums.length; + + return function(obj) { + var result = [], + prop = '', + i = -1; + + if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { + throw new TypeError('Object.keys called on non-object'); + } + + for (prop in obj) { + if (hasOwnProperty.call(obj, prop)) { + result.push(prop); + } + } + + if (hasDontEnumBug) { + for (i = 0; i < dontEnumsLength; i++) { + if (hasOwnProperty.call(obj, dontEnums[i])) { + result.push(dontEnums[i]); + } + } + } + + return result; + }; + }()); + } + + // Array.isArray + // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray + + if (!Array.isArray) { + Array.isArray = function(arg) { + return Object.prototype.toString.call(arg) === '[object Array]'; + }; + } + + // Object.create + // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/create + + if (typeof Object.create !== 'function') { + Object.create = (function(undefined) { + var Temp = function() {}; + + return function (prototype, propertiesObject) { + if (prototype !== Object(prototype) && prototype !== null) { + throw TypeError('Argument must be an object, or null'); + } + + Temp.prototype = prototype || {}; + + var result = new Temp(); + + Temp.prototype = null; + + if (propertiesObject !== undefined) { + Object.defineProperties(result, propertiesObject); + } + + if (prototype === null) { + /* jshint ignore:start */ + result.__proto__ = null; + /* jshint ignore:end */ + } + + return result; + }; + })(); + } + + // String.prototyoe.trim + + if (!String.prototype.trim) { + String.prototype.trim = function() { + return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); + }; + } + + // Array.prototype.indexOf + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf + + if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function(searchElement) { + var n, k, t, len; + + if (this === null) { + throw new TypeError(); + } + + t = Object(this); + + len = t.length >>> 0; + + if (len === 0) { + return -1; + } + + n = 0; + + if (arguments.length > 1) { + n = Number(arguments[1]); + + if (n !== n) { + n = 0; + } else if (n !== 0 && n !== Infinity && n !== -Infinity) { + n = (n > 0 || -1) * Math.floor(Math.abs(n)); + } + } + + if (n >= len) { + return -1; + } + + for (k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); k < len; k++) { + if (k in t && t[k] === searchElement) { + return k; + } + } + + return -1; + }; + } + + // Function.prototype.bind + // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind + + if (!Function.prototype.bind) { + Function.prototype.bind = function(oThis) { + var aArgs, self, FNOP, fBound; + + if (typeof this !== 'function') { + throw new TypeError(); + } + + aArgs = Array.prototype.slice.call(arguments, 1); + + self = this; + + FNOP = function() {}; + + fBound = function() { + return self.apply(this instanceof FNOP ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + if (this.prototype) { + FNOP.prototype = this.prototype; + } + + fBound.prototype = new FNOP(); + + return fBound; + }; + } + + // Element.prototype.dispatchEvent + + if (!window.Element.prototype.dispatchEvent) { + window.Element.prototype.dispatchEvent = function(event) { + try { + return this.fireEvent('on' + event.type, event); + } catch (err) {} + }; + } + })(); + + /** + * The `mixitup()` "factory" function creates and returns individual instances + * of MixItUp, known as "mixers", on which API methods can be called. + * + * When loading MixItUp via a script tag, the factory function is accessed + * via the global variable `mixitup`. When using a module loading + * system (e.g. ES2015, CommonJS, RequireJS), the factory function is + * exported into your module when you require the MixItUp library. + * + * @example + * mixitup(container [,config] [,foreignDoc]) + * + * @example Example 1: Creating a mixer instance with an element reference + * var containerEl = document.querySelector('.container'); + * + * var mixer = mixitup(containerEl); + * + * @example Example 2: Creating a mixer instance with a selector string + * var mixer = mixitup('.container'); + * + * @example Example 3: Passing a configuration object + * var mixer = mixitup(containerEl, { + * animation: { + * effects: 'fade scale(0.5)' + * } + * }); + * + * @example Example 4: Passing an iframe reference + * var mixer = mixitup(containerEl, config, foreignDocument); + * + * @global + * @namespace + * @public + * @kind function + * @since 3.0.0 + * @param {(Element|string)} container + * A DOM element or selector string representing the container(s) on which to instantiate MixItUp. + * @param {object} [config] + * An optional "configuration object" used to customize the behavior of the MixItUp instance. + * @param {object} [foreignDoc] + * An optional reference to a `document`, which can be used to control a MixItUp instance in an iframe. + * @return {mixitup.Mixer} + * A "mixer" object holding the MixItUp instance. + */ + + mixitup = function(container, config, foreignDoc) { + var el = null, + returnCollection = false, + instance = null, + facade = null, + doc = null, + output = null, + instances = [], + id = '', + elements = [], + i = -1; + + doc = foreignDoc || window.document; + + if (returnCollection = arguments[3]) { + // A non-documented 4th paramater enabling control of multiple instances + + returnCollection = typeof returnCollection === 'boolean'; + } + + if (typeof container === 'string') { + elements = doc.querySelectorAll(container); + } else if (container && typeof container === 'object' && h.isElement(container, doc)) { + elements = [container]; + } else if (container && typeof container === 'object' && container.length) { + // Although not documented, the container may also be an array-like list of + // elements such as a NodeList or jQuery collection, is returnCollection is true + + elements = container; + } else { + throw new Error(mixitup.messages.errorFactoryInvalidContainer()); + } + + if (elements.length < 1) { + throw new Error(mixitup.messages.errorFactoryContainerNotFound()); + } + + for (i = 0; el = elements[i]; i++) { + if (i > 0 && !returnCollection) break; + + if (!el.id) { + id = 'MixItUp' + h.randomHex(); + + el.id = id; + } else { + id = el.id; + } + + if (mixitup.instances[id] instanceof mixitup.Mixer) { + instance = mixitup.instances[id]; + + if (!config || (config && config.debug && config.debug.showWarnings !== false)) { + console.warn(mixitup.messages.warningFactoryPreexistingInstance()); + } + } else { + instance = new mixitup.Mixer(); + + instance.attach(el, doc, id, config); + + mixitup.instances[id] = instance; + } + + facade = new mixitup.Facade(instance); + + if (config && config.debug && config.debug.enable) { + instances.push(instance); + } else { + instances.push(facade); + } + } + + if (returnCollection) { + output = new mixitup.Collection(instances); + } else { + // Return the first instance regardless + + output = instances[0]; + } + + return output; + }; + + /** + * The `.use()` static method is used to extend the functionality of mixitup with compatible + * extensions and libraries in an environment with modular scoping e.g. ES2015, CommonJS, or RequireJS. + * + * You need only call the `.use()` function once per project, per extension, as module loaders + * will cache a single reference to MixItUp inclusive of all changes made. + * + * @example + * mixitup.use(extension) + * + * @example Example 1: Extending MixItUp with the Pagination Extension + * + * import mixitup from 'mixitup'; + * import mixitupPagination from 'mixitup-pagination'; + * + * mixitup.use(mixitupPagination); + * + * // All mixers created by the factory function in all modules will now + * // have pagination functionality + * + * var mixer = mixitup('.container'); + * + * @public + * @name use + * @memberof mixitup + * @kind function + * @static + * @since 3.0.0 + * @param {*} extension A reference to the extension or library to be used. + * @return {void} + */ + + mixitup.use = function(extension) { + mixitup.Base.prototype.callActions.call(mixitup, 'beforeUse', arguments); + + // Call the extension's factory function, passing + // the mixitup factory as a paramater + + if (typeof extension === 'function' && extension.TYPE === 'mixitup-extension') { + // Mixitup extension + + if (typeof mixitup.extensions[extension.NAME] === 'undefined') { + extension(mixitup); + + mixitup.extensions[extension.NAME] = extension; + } + } else if (extension.fn && extension.fn.jquery) { + // jQuery + + mixitup.libraries.$ = extension; + } + + mixitup.Base.prototype.callActions.call(mixitup, 'afterUse', arguments); + }; + + mixitup.instances = {}; + mixitup.extensions = {}; + mixitup.libraries = {}; + + /** + * @private + */ + + h = { + + /** + * @private + * @param {HTMLElement} el + * @param {string} cls + * @return {boolean} + */ + + hasClass: function(el, cls) { + return !!el.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')); + }, + + /** + * @private + * @param {HTMLElement} el + * @param {string} cls + * @return {void} + */ + + addClass: function(el, cls) { + if (!this.hasClass(el, cls)) el.className += el.className ? ' ' + cls : cls; + }, + + /** + * @private + * @param {HTMLElement} el + * @param {string} cls + * @return {void} + */ + + removeClass: function(el, cls) { + if (this.hasClass(el, cls)) { + var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)'); + + el.className = el.className.replace(reg, ' ').trim(); + } + }, + + /** + * Merges the properties of the source object onto the + * target object. Alters the target object. + * + * @private + * @param {object} destination + * @param {object} source + * @param {boolean} [deep=false] + * @param {boolean} [handleErrors=false] + * @return {void} + */ + + extend: function(destination, source, deep, handleErrors) { + var sourceKeys = [], + key = '', + i = -1; + + deep = deep || false; + handleErrors = handleErrors || false; + + try { + if (Array.isArray(source)) { + for (i = 0; i < source.length; i++) { + sourceKeys.push(i); + } + } else if (source) { + sourceKeys = Object.keys(source); + } + + for (i = 0; i < sourceKeys.length; i++) { + key = sourceKeys[i]; + + if (!deep || typeof source[key] !== 'object' || this.isElement(source[key])) { + // All non-object properties, or all properties if shallow extend + + destination[key] = source[key]; + } else if (Array.isArray(source[key])) { + // Arrays + + if (!destination[key]) { + destination[key] = []; + } + + this.extend(destination[key], source[key], deep, handleErrors); + } else { + // Objects + + if (!destination[key]) { + destination[key] = {}; + } + + this.extend(destination[key], source[key], deep, handleErrors); + } + } + } catch(err) { + if (handleErrors) { + this.handleExtendError(err, destination); + } else { + throw err; + } + } + + return destination; + }, + + /** + * @private + * @param {Error} err + * @param {object} destination + * @return {void} + */ + + handleExtendError: function(err, destination) { + var re = /property "?(\w*)"?[,:] object/i, + matches = null, + erroneous = '', + message = '', + suggestion = '', + probableMatch = '', + key = '', + mostMatchingChars = -1, + i = -1; + + if (err instanceof TypeError && (matches = re.exec(err.message))) { + erroneous = matches[1]; + + for (key in destination) { + i = 0; + + while (i < erroneous.length && erroneous.charAt(i) === key.charAt(i)) { + i++; + } + + if (i > mostMatchingChars) { + mostMatchingChars = i; + probableMatch = key; + } + } + + if (mostMatchingChars > 1) { + suggestion = mixitup.messages.errorConfigInvalidPropertySuggestion({ + probableMatch: probableMatch + }); + } + + message = mixitup.messages.errorConfigInvalidProperty({ + erroneous: erroneous, + suggestion: suggestion + }); + + throw new TypeError(message); + } + + throw err; + }, + + /** + * @private + * @param {string} str + * @return {function} + */ + + template: function(str) { + var re = /\${([\w]*)}/g, + dynamics = {}, + matches = null; + + while ((matches = re.exec(str))) { + dynamics[matches[1]] = new RegExp('\\${' + matches[1] + '}', 'g'); + } + + return function(data) { + var key = '', + output = str; + + data = data || {}; + + for (key in dynamics) { + output = output.replace(dynamics[key], typeof data[key] !== 'undefined' ? data[key] : ''); + } + + return output; + }; + }, + + /** + * @private + * @param {HTMLElement} el + * @param {string} type + * @param {function} fn + * @param {boolean} useCapture + * @return {void} + */ + + on: function(el, type, fn, useCapture) { + if (!el) return; + + if (el.addEventListener) { + el.addEventListener(type, fn, useCapture); + } else if (el.attachEvent) { + el['e' + type + fn] = fn; + + el[type + fn] = function() { + el['e' + type + fn](window.event); + }; + + el.attachEvent('on' + type, el[type + fn]); + } + }, + + /** + * @private + * @param {HTMLElement} el + * @param {string} type + * @param {function} fn + * @return {void} + */ + + off: function(el, type, fn) { + if (!el) return; + + if (el.removeEventListener) { + el.removeEventListener(type, fn, false); + } else if (el.detachEvent) { + el.detachEvent('on' + type, el[type + fn]); + el[type + fn] = null; + } + }, + + /** + * @private + * @param {string} eventType + * @param {object} detail + * @param {Document} [doc] + * @return {CustomEvent} + */ + + getCustomEvent: function(eventType, detail, doc) { + var event = null; + + doc = doc || window.document; + + if (typeof window.CustomEvent === 'function') { + event = new window.CustomEvent(eventType, { + detail: detail, + bubbles: true, + cancelable: true + }); + } else if (typeof doc.createEvent === 'function') { + event = doc.createEvent('CustomEvent'); + event.initCustomEvent(eventType, true, true, detail); + } else { + event = doc.createEventObject(), + event.type = eventType; + + event.returnValue = false; + event.cancelBubble = false; + event.detail = detail; + } + + return event; + }, + + /** + * @private + * @param {Event} e + * @return {Event} + */ + + getOriginalEvent: function(e) { + if (e.touches && e.touches.length) { + return e.touches[0]; + } else if (e.changedTouches && e.changedTouches.length) { + return e.changedTouches[0]; + } else { + return e; + } + }, + + /** + * @private + * @param {HTMLElement} el + * @param {string} selector + * @return {Number} + */ + + index: function(el, selector) { + var i = 0; + + while ((el = el.previousElementSibling) !== null) { + if (!selector || el.matches(selector)) { + ++i; + } + } + + return i; + }, + + /** + * Converts a dash or snake-case string to camel case. + * + * @private + * @param {string} str + * @param {boolean} [isPascal] + * @return {string} + */ + + camelCase: function(str) { + return str.toLowerCase().replace(/([_-][a-z])/g, function($1) { + return $1.toUpperCase().replace(/[_-]/, ''); + }); + }, + + /** + * Converts a dash or snake-case string to pascal case. + * + * @private + * @param {string} str + * @param {boolean} [isPascal] + * @return {string} + */ + + pascalCase: function(str) { + return (str = this.camelCase(str)).charAt(0).toUpperCase() + str.slice(1); + }, + + /** + * Converts a camel or pascal-case string to dash case. + * + * @private + * @param {string} str + * @return {string} + */ + + dashCase: function(str) { + return str.replace(/([A-Z])/g, '-$1').replace(/^-/, '').toLowerCase(); + }, + + /** + * @private + * @param {HTMLElement} el + * @param {HTMLHtmlElement} [doc] + * @return {boolean} + */ + + isElement: function(el, doc) { + doc = doc || window.document; + + if ( + window.HTMLElement && + el instanceof window.HTMLElement + ) { + return true; + } else if ( + doc.defaultView && + doc.defaultView.HTMLElement && + el instanceof doc.defaultView.HTMLElement + ) { + return true; + } else { + return ( + el !== null && + el.nodeType === 1 && + typeof el.nodeName === 'string' + ); + } + }, + + /** + * @private + * @param {string} htmlString + * @param {HTMLHtmlElement} [doc] + * @return {DocumentFragment} + */ + + createElement: function(htmlString, doc) { + var frag = null, + temp = null; + + doc = doc || window.document; + + frag = doc.createDocumentFragment(); + temp = doc.createElement('div'); + + temp.innerHTML = htmlString.trim(); + + while (temp.firstChild) { + frag.appendChild(temp.firstChild); + } + + return frag; + }, + + /** + * @private + * @param {Node} node + * @return {void} + */ + + removeWhitespace: function(node) { + var deleting; + + while (node && node.nodeName === '#text') { + deleting = node; + + node = node.previousSibling; + + deleting.parentElement && deleting.parentElement.removeChild(deleting); + } + }, + + /** + * @private + * @param {Array<*>} a + * @param {Array<*>} b + * @return {boolean} + */ + + isEqualArray: function(a, b) { + var i = a.length; + + if (i !== b.length) return false; + + while (i--) { + if (a[i] !== b[i]) return false; + } + + return true; + }, + + /** + * @private + * @param {object} a + * @param {object} b + * @return {boolean} + */ + + deepEquals: function(a, b) { + var key; + + if (typeof a === 'object' && a && typeof b === 'object' && b) { + if (Object.keys(a).length !== Object.keys(b).length) return false; + + for (key in a) { + if (!b.hasOwnProperty(key) || !this.deepEquals(a[key], b[key])) return false; + } + } else if (a !== b) { + return false; + } + + return true; + }, + + /** + * @private + * @param {Array<*>} oldArray + * @return {Array<*>} + */ + + arrayShuffle: function(oldArray) { + var newArray = oldArray.slice(), + len = newArray.length, + i = len, + p = -1, + t = []; + + while (i--) { + p = ~~(Math.random() * len); + t = newArray[i]; + + newArray[i] = newArray[p]; + newArray[p] = t; + } + + return newArray; + }, + + /** + * @private + * @param {object} list + */ + + arrayFromList: function(list) { + var output, i; + + try { + return Array.prototype.slice.call(list); + } catch(err) { + output = []; + + for (i = 0; i < list.length; i++) { + output.push(list[i]); + } + + return output; + } + }, + + /** + * @private + * @param {function} func + * @param {Number} wait + * @param {boolean} immediate + * @return {function} + */ + + debounce: function(func, wait, immediate) { + var timeout; + + return function() { + var self = this, + args = arguments, + callNow = immediate && !timeout, + later = null; + + later = function() { + timeout = null; + + if (!immediate) { + func.apply(self, args); + } + }; + + clearTimeout(timeout); + + timeout = setTimeout(later, wait); + + if (callNow) func.apply(self, args); + }; + }, + + /** + * @private + * @param {HTMLElement} element + * @return {object} + */ + + position: function(element) { + var xPosition = 0, + yPosition = 0, + offsetParent = element; + + while (element) { + xPosition -= element.scrollLeft; + yPosition -= element.scrollTop; + + if (element === offsetParent) { + xPosition += element.offsetLeft; + yPosition += element.offsetTop; + + offsetParent = element.offsetParent; + } + + element = element.parentElement; + } + + return { + x: xPosition, + y: yPosition + }; + }, + + /** + * @private + * @param {object} node1 + * @param {object} node2 + * @return {Number} + */ + + getHypotenuse: function(node1, node2) { + var distanceX = node1.x - node2.x, + distanceY = node1.y - node2.y; + + distanceX = distanceX < 0 ? distanceX * -1 : distanceX, + distanceY = distanceY < 0 ? distanceY * -1 : distanceY; + + return Math.sqrt(Math.pow(distanceX, 2) + Math.pow(distanceY, 2)); + }, + + /** + * Calcuates the area of intersection between two rectangles and expresses it as + * a ratio in comparison to the area of the first rectangle. + * + * @private + * @param {Rect} box1 + * @param {Rect} box2 + * @return {number} + */ + + getIntersectionRatio: function(box1, box2) { + var controlArea = box1.width * box1.height, + intersectionX = -1, + intersectionY = -1, + intersectionArea = -1, + ratio = -1; + + intersectionX = + Math.max(0, Math.min(box1.left + box1.width, box2.left + box2.width) - Math.max(box1.left, box2.left)); + + intersectionY = + Math.max(0, Math.min(box1.top + box1.height, box2.top + box2.height) - Math.max(box1.top, box2.top)); + + intersectionArea = intersectionY * intersectionX; + + ratio = intersectionArea / controlArea; + + return ratio; + }, + + /** + * @private + * @param {object} el + * @param {string} selector + * @param {boolean} [includeSelf] + * @param {HTMLHtmlElement} [doc] + * @return {Element|null} + */ + + closestParent: function(el, selector, includeSelf, doc) { + var parent = el.parentNode; + + doc = doc || window.document; + + if (includeSelf && el.matches(selector)) { + return el; + } + + while (parent && parent != doc.body) { + if (parent.matches && parent.matches(selector)) { + return parent; + } else if (parent.parentNode) { + parent = parent.parentNode; + } else { + return null; + } + } + + return null; + }, + + /** + * @private + * @param {HTMLElement} el + * @param {string} selector + * @param {HTMLHtmlElement} [doc] + * @return {NodeList} + */ + + children: function(el, selector, doc) { + var children = [], + tempId = ''; + + doc = doc || window.doc; + + if (el) { + if (!el.id) { + tempId = 'Temp' + this.randomHexKey(); + + el.id = tempId; + } + + children = doc.querySelectorAll('#' + el.id + ' > ' + selector); + + if (tempId) { + el.removeAttribute('id'); + } + } + + return children; + }, + + /** + * Creates a clone of a provided array, with any empty strings removed. + * + * @private + * @param {Array<*>} originalArray + * @return {Array<*>} + */ + + clean: function(originalArray) { + var cleanArray = [], + i = -1; + + for (i = 0; i < originalArray.length; i++) { + if (originalArray[i] !== '') { + cleanArray.push(originalArray[i]); + } + } + + return cleanArray; + }, + + /** + * Abstracts an ES6 promise into a q-like deferred interface for storage and deferred resolution. + * + * @private + * @param {object} libraries + * @return {h.Deferred} + */ + + defer: function(libraries) { + var deferred = null, + promiseWrapper = null, + $ = null; + + promiseWrapper = new this.Deferred(); + + if (mixitup.features.has.promises) { + // ES6 native promise or polyfill + + promiseWrapper.promise = new Promise(function(resolve, reject) { + promiseWrapper.resolve = resolve; + promiseWrapper.reject = reject; + }); + } else if (($ = (window.jQuery || libraries.$)) && typeof $.Deferred === 'function') { + // jQuery + + deferred = $.Deferred(); + + promiseWrapper.promise = deferred.promise(); + promiseWrapper.resolve = deferred.resolve; + promiseWrapper.reject = deferred.reject; + } else if (window.console) { + // No implementation + + console.warn(mixitup.messages.warningNoPromiseImplementation()); + } + + return promiseWrapper; + }, + + /** + * @private + * @param {Array} tasks + * @param {object} libraries + * @return {Promise} + */ + + all: function(tasks, libraries) { + var $ = null; + + if (mixitup.features.has.promises) { + return Promise.all(tasks); + } else if (($ = (window.jQuery || libraries.$)) && typeof $.when === 'function') { + return $.when.apply($, tasks) + .done(function() { + // jQuery when returns spread arguments rather than an array or resolutions + + return arguments; + }); + } + + // No implementation + + if (window.console) { + console.warn(mixitup.messages.warningNoPromiseImplementation()); + } + + return []; + }, + + /** + * @private + * @param {HTMLElement} el + * @param {string} property + * @param {Array} vendors + * @return {string} + */ + + getPrefix: function(el, property, vendors) { + var i = -1, + prefix = ''; + + if (h.dashCase(property) in el.style) return ''; + + for (i = 0; prefix = vendors[i]; i++) { + if (prefix + property in el.style) { + return prefix.toLowerCase(); + } + } + + return 'unsupported'; + }, + + /** + * @private + * @return {string} + */ + + randomHex: function() { + return ('00000' + (Math.random() * 16777216 << 0).toString(16)).substr(-6).toUpperCase(); + }, + + /** + * @private + * @param {HTMLDocument} [doc] + * @return {object} + */ + + getDocumentState: function(doc) { + doc = typeof doc.body === 'object' ? doc : window.document; + + return { + scrollTop: window.pageYOffset, + scrollLeft: window.pageXOffset, + docHeight: doc.documentElement.scrollHeight, + docWidth: doc.documentElement.scrollWidth, + viewportHeight: doc.documentElement.clientHeight, + viewportWidth: doc.documentElement.clientWidth + }; + }, + + /** + * @private + * @param {object} obj + * @param {function} fn + * @return {function} + */ + + bind: function(obj, fn) { + return function() { + return fn.apply(obj, arguments); + }; + }, + + /** + * @private + * @param {HTMLElement} el + * @return {boolean} + */ + + isVisible: function(el) { + var styles = null; + + if (el.offsetParent) return true; + + styles = window.getComputedStyle(el); + + if ( + styles.position === 'fixed' && + styles.visibility !== 'hidden' && + styles.opacity !== '0' + ) { + // Fixed elements report no offsetParent, + // but may still be invisible + + return true; + } + + return false; + }, + + /** + * @private + * @param {object} obj + */ + + seal: function(obj) { + if (typeof Object.seal === 'function') { + Object.seal(obj); + } + }, + + /** + * @private + * @param {object} obj + */ + + freeze: function(obj) { + if (typeof Object.freeze === 'function') { + Object.freeze(obj); + } + }, + + /** + * @private + * @param {string} control + * @param {string} specimen + * @return {boolean} + */ + + compareVersions: function(control, specimen) { + var controlParts = control.split('.'), + specimenParts = specimen.split('.'), + controlPart = -1, + specimenPart = -1, + i = -1; + + for (i = 0; i < controlParts.length; i++) { + controlPart = parseInt(controlParts[i].replace(/[^\d.]/g, '')); + specimenPart = parseInt(specimenParts[i].replace(/[^\d.]/g, '') || 0); + + if (specimenPart < controlPart) { + return false; + } else if (specimenPart > controlPart) { + return true; + } + } + + return true; + }, + + /** + * @private + * @constructor + */ + + Deferred: function() { + this.promise = null; + this.resolve = null; + this.reject = null; + this.id = h.randomHex(); + }, + + /** + * @private + * @param {object} obj + * @return {boolean} + */ + + isEmptyObject: function(obj) { + var key = ''; + + if (typeof Object.keys === 'function') { + return Object.keys(obj).length === 0; + } + + for (key in obj) { + if (obj.hasOwnProperty(key)) { + return false; + } + } + + return true; + }, + + /** + * @param {mixitup.Config.ClassNames} classNames + * @param {string} elementName + * @param {string} [modifier] + * @return {string} + */ + + getClassname: function(classNames, elementName, modifier) { + var classname = ''; + + classname += classNames.block; + + if (classname.length) { + classname += classNames.delineatorElement; + } + + classname += classNames['element' + this.pascalCase(elementName)]; + + if (!modifier) return classname; + + if (classname.length) { + classname += classNames.delineatorModifier; + } + + classname += modifier; + + return classname; + }, + + /** + * Returns the value of a property on a given object via its string key. + * + * @param {object} obj + * @param {string} stringKey + * @return {*} value + */ + + getProperty: function(obj, stringKey) { + var parts = stringKey.split('.'), + returnCurrent = null, + current = '', + i = 0; + + if (!stringKey) { + return obj; + } + + returnCurrent = function(obj) { + if (!obj) { + return null; + } else { + return obj[current]; + } + }; + + while (i < parts.length) { + current = parts[i]; + + obj = returnCurrent(obj); + + i++; + } + + if (typeof obj !== 'undefined') { + return obj; + } else { + return null; + } + } + }; + + mixitup.h = h; + + /** + * The Base class adds instance methods to all other extensible MixItUp classes, + * enabling the calling of any registered hooks. + * + * @constructor + * @namespace + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.Base = function() {}; + + mixitup.Base.prototype = { + constructor: mixitup.Base, + + /** + * Calls any registered hooks for the provided action. + * + * @memberof mixitup.Base + * @private + * @instance + * @since 2.0.0 + * @param {string} actionName + * @param {Array<*>} args + * @return {void} + */ + + callActions: function(actionName, args) { + var self = this, + hooks = self.constructor.actions[actionName], + extensionName = ''; + + if (!hooks || h.isEmptyObject(hooks)) return; + + for (extensionName in hooks) { + hooks[extensionName].apply(self, args); + } + }, + + /** + * Calls any registered hooks for the provided filter. + * + * @memberof mixitup.Base + * @private + * @instance + * @since 2.0.0 + * @param {string} filterName + * @param {*} input + * @param {Array<*>} args + * @return {*} + */ + + callFilters: function(filterName, input, args) { + var self = this, + hooks = self.constructor.filters[filterName], + output = input, + extensionName = ''; + + if (!hooks || h.isEmptyObject(hooks)) return output; + + args = args || []; + + for (extensionName in hooks) { + args = h.arrayFromList(args); + + args.unshift(output); + + output = hooks[extensionName].apply(self, args); + } + + return output; + } + }; + + /** + * The BaseStatic class holds a set of static methods which are then added to all other + * extensible MixItUp classes as a means of integrating extensions via the addition of new + * methods and/or actions and hooks. + * + * @constructor + * @namespace + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.BaseStatic = function() { + this.actions = {}; + this.filters = {}; + + /** + * Performs a shallow extend on the class's prototype, adding one or more new members to + * the class in a single operation. + * + * @memberof mixitup.BaseStatic + * @public + * @static + * @since 2.1.0 + * @param {object} extension + * @return {void} + */ + + this.extend = function(extension) { + h.extend(this.prototype, extension); + }; + + /** + * Registers a function to be called on the action hook of the provided name. + * + * @memberof mixitup.BaseStatic + * @public + * @static + * @since 2.1.0 + * @param {string} hookName + * @param {string} extensionName + * @param {function} func + * @return {void} + */ + + this.registerAction = function(hookName, extensionName, func) { + (this.actions[hookName] = this.actions[hookName] || {})[extensionName] = func; + }; + + /** + * Registers a function to be called on the filter of the provided name. + * + * @memberof mixitup.BaseStatic + * @public + * @static + * @since 2.1.0 + * @param {string} hookName + * @param {string} extensionName + * @param {function} func + * @return {void} + */ + + this.registerFilter = function(hookName, extensionName, func) { + (this.filters[hookName] = this.filters[hookName] || {})[extensionName] = func; + }; + }; + + /** + * The `mixitup.Features` class performs all feature and CSS prefix detection + * neccessary for MixItUp to function correctly, as well as storing various + * string and array constants. All feature decection is on evaluation of the + * library and stored in a singleton instance for use by other internal classes. + * + * @constructor + * @namespace + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.Features = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.boxSizingPrefix = ''; + this.transformPrefix = ''; + this.transitionPrefix = ''; + + this.boxSizingPrefix = ''; + this.transformProp = ''; + this.transformRule = ''; + this.transitionProp = ''; + this.perspectiveProp = ''; + this.perspectiveOriginProp = ''; + + this.has = new mixitup.Has(); + + this.canary = null; + + this.BOX_SIZING_PROP = 'boxSizing'; + this.TRANSITION_PROP = 'transition'; + this.TRANSFORM_PROP = 'transform'; + this.PERSPECTIVE_PROP = 'perspective'; + this.PERSPECTIVE_ORIGIN_PROP = 'perspectiveOrigin'; + this.VENDORS = ['Webkit', 'moz', 'O', 'ms']; + + this.TWEENABLE = [ + 'opacity', + 'width', 'height', + 'marginRight', 'marginBottom', + 'x', 'y', + 'scale', + 'translateX', 'translateY', 'translateZ', + 'rotateX', 'rotateY', 'rotateZ' + ]; + + this.callActions('afterConstruct'); + }; + + mixitup.BaseStatic.call(mixitup.Features); + + mixitup.Features.prototype = Object.create(mixitup.Base.prototype); + + h.extend(mixitup.Features.prototype, + /** @lends mixitup.Features */ + { + constructor: mixitup.Features, + + /** + * @private + * @return {void} + */ + + init: function() { + var self = this; + + self.callActions('beforeInit', arguments); + + self.canary = document.createElement('div'); + + self.setPrefixes(); + self.runTests(); + + self.callActions('beforeInit', arguments); + }, + + /** + * @private + * @return {void} + */ + + runTests: function() { + var self = this; + + self.callActions('beforeRunTests', arguments); + + self.has.promises = typeof window.Promise === 'function'; + self.has.transitions = self.transitionPrefix !== 'unsupported'; + + self.callActions('afterRunTests', arguments); + + h.freeze(self.has); + }, + + /** + * @private + * @return {void} + */ + + setPrefixes: function() { + var self = this; + + self.callActions('beforeSetPrefixes', arguments); + + self.transitionPrefix = h.getPrefix(self.canary, 'Transition', self.VENDORS); + self.transformPrefix = h.getPrefix(self.canary, 'Transform', self.VENDORS); + self.boxSizingPrefix = h.getPrefix(self.canary, 'BoxSizing', self.VENDORS); + + self.boxSizingProp = self.boxSizingPrefix ? + self.boxSizingPrefix + h.pascalCase(self.BOX_SIZING_PROP) : self.BOX_SIZING_PROP; + + self.transitionProp = self.transitionPrefix ? + self.transitionPrefix + h.pascalCase(self.TRANSITION_PROP) : self.TRANSITION_PROP; + + self.transformProp = self.transformPrefix ? + self.transformPrefix + h.pascalCase(self.TRANSFORM_PROP) : self.TRANSFORM_PROP; + + self.transformRule = self.transformPrefix ? + '-' + self.transformPrefix + '-' + self.TRANSFORM_PROP : self.TRANSFORM_PROP; + + self.perspectiveProp = self.transformPrefix ? + self.transformPrefix + h.pascalCase(self.PERSPECTIVE_PROP) : self.PERSPECTIVE_PROP; + + self.perspectiveOriginProp = self.transformPrefix ? + self.transformPrefix + h.pascalCase(self.PERSPECTIVE_ORIGIN_PROP) : + self.PERSPECTIVE_ORIGIN_PROP; + + self.callActions('afterSetPrefixes', arguments); + } + }); + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.Has = function() { + this.transitions = false; + this.promises = false; + + h.seal(this); + }; + + // Assign a singleton instance to `mixitup.features` and initialise: + + mixitup.features = new mixitup.Features(); + + mixitup.features.init(); + + /** + * A group of properties defining the mixer's animation and effects settings. + * + * @constructor + * @memberof mixitup.Config + * @name animation + * @namespace + * @public + * @since 2.0.0 + */ + + mixitup.ConfigAnimation = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A boolean dictating whether or not animation should be enabled for the MixItUp instance. + * If `false`, all operations will occur instantly and syncronously, although callback + * functions and any returned promises will still be fulfilled. + * + * @example Example: Create a mixer with all animations disabled + * var mixer = mixitup(containerEl, { + * animation: { + * enable: false + * } + * }); + * + * @name enable + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default true + */ + + this.enable = true; + + /** + * A string of one or more space-seperated properties to which transitions will be + * applied for all filtering animations. + * + * Properties can be listed any order or combination, although they will be applied in a specific + * predefined order to produce consistent results. + * + * To learn more about available effects, experiment with our + * sandbox demo and try out the "Export config" button in the Animation options drop down. + * + * @example Example: Apply "fade" and "translateZ" effects to all animations + * // As targets are filtered in and out, they will fade between + * // opacity 1 and 0 and transform between translateZ(-100px) and + * // translateZ(0). + * + * var mixer = mixitup(containerEl, { + * animation: { + * effects: 'fade translateZ(-100px)' + * } + * }); + * + * @name effects + * @memberof mixitup.Config.animation + * @instance + * @type {string} + * @default 'fade scale' + */ + + this.effects = 'fade scale'; + + /** + * A string of one or more space-seperated effects to be applied only to filter-in + * animations, overriding `config.animation.effects` if set. + * + * @example Example: Apply downwards vertical translate to targets being filtered in + * + * var mixer = mixitup(containerEl, { + * animation: { + * effectsIn: 'fade translateY(-100%)' + * } + * }); + * + * @name effectsIn + * @memberof mixitup.Config.animation + * @instance + * @type {string} + * @default '' + */ + + this.effectsIn = ''; + + /** + * A string of one or more space-seperated effects to be applied only to filter-out + * animations, overriding `config.animation.effects` if set. + * + * @example Example: Apply upwards vertical translate to targets being filtered out + * + * var mixer = mixitup(containerEl, { + * animation: { + * effectsOut: 'fade translateY(-100%)' + * } + * }); + * + * @name effectsOut + * @memberof mixitup.Config.animation + * @instance + * @type {string} + * @default '' + */ + + this.effectsOut = ''; + + /** + * An integer dictating the duration of all MixItUp animations in milliseconds, not + * including any additional delay apllied via the `'stagger'` effect. + * + * @example Example: Apply an animation duration of 200ms to all mixitup animations + * + * var mixer = mixitup(containerEl, { + * animation: { + * duration: 200 + * } + * }); + * + * @name duration + * @memberof mixitup.Config.animation + * @instance + * @type {number} + * @default 600 + */ + + this.duration = 600; + + /** + * A valid CSS3 transition-timing function or shorthand. For a full list of accepted + * values, visit easings.net. + * + * @example Example 1: Apply "ease-in-out" easing to all animations + * + * var mixer = mixitup(containerEl, { + * animation: { + * easing: 'ease-in-out' + * } + * }); + * + * @example Example 2: Apply a custom "cubic-bezier" easing function to all animations + * var mixer = mixitup(containerEl, { + * animation: { + * easing: 'cubic-bezier(0.645, 0.045, 0.355, 1)' + * } + * }); + * + * @name easing + * @memberof mixitup.Config.animation + * @instance + * @type {string} + * @default 'ease' + */ + + this.easing = 'ease'; + + /** + * A boolean dictating whether or not to apply perspective to the MixItUp container + * during animations. By default, perspective is always applied and creates the + * illusion of three-dimensional space for effects such as `translateZ`, `rotateX`, + * and `rotateY`. + * + * You may wish to disable this and define your own perspective settings via CSS. + * + * @example Example: Prevent perspective from being applied to any 3D transforms + * var mixer = mixitup(containerEl, { + * animation: { + * applyPerspective: false + * } + * }); + * + * @name applyPerspective + * @memberof mixitup.Config.animation + * @instance + * @type {bolean} + * @default true + */ + + this.applyPerspective = true; + + /** + * The perspective distance value to be applied to the container during animations, + * affecting any 3D-transform-based effects. + * + * @example Example: Set a perspective distance of 2000px + * var mixer = mixitup(containerEl, { + * animation: { + * effects: 'rotateY(-25deg)', + * perspectiveDistance: '2000px' + * } + * }); + * + * @name perspectiveDistance + * @memberof mixitup.Config.animation + * @instance + * @type {string} + * @default '3000px' + */ + + this.perspectiveDistance = '3000px'; + + /** + * The perspective-origin value to be applied to the container during animations, + * affecting any 3D-transform-based effects. + * + * @example Example: Set a perspective origin in the top-right of the container + * var mixer = mixitup(containerEl, { + * animation: { + * effects: 'transateZ(-200px)', + * perspectiveOrigin: '100% 0' + * } + * }); + * + * @name perspectiveOrigin + * @memberof mixitup.Config.animation + * @instance + * @type {string} + * @default '50% 50%' + */ + + this.perspectiveOrigin = '50% 50%'; + + /** + * A boolean dictating whether or not to enable the queuing of operations. + * + * If `true` (default), and a control is clicked or an API call is made while another + * operation is progress, the operation will go into the queue and will be automatically exectuted + * when the previous operaitons is finished. + * + * If `false`, any requested operations will be ignored, and the `onMixBusy` callback and `mixBusy` + * event will be fired. If `debug.showWarnings` is enabled, a console warning will also occur. + * + * @example Example: Disable queuing + * var mixer = mixitup(containerEl, { + * animation: { + * queue: false + * } + * }); + * + * @name queue + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default true + */ + + this.queue = true; + + /** + * An integer dictacting the maximum number of operations allowed in the queue at + * any time, when queuing is enabled. + * + * @example Example: Allow a maximum of 5 operations in the queue at any time + * var mixer = mixitup(containerEl, { + * animation: { + * queueLimit: 5 + * } + * }); + * + * @name queueLimit + * @memberof mixitup.Config.animation + * @instance + * @type {number} + * @default 3 + */ + + this.queueLimit = 3; + + /** + * A boolean dictating whether or not to transition the height and width of the + * container as elements are filtered in and out. If disabled, the container height + * will change abruptly. + * + * It may be desirable to disable this on mobile devices as the CSS `height` and + * `width` properties do not receive GPU-acceleration and can therefore cause stuttering. + * + * @example Example 1: Disable the transitioning of the container height and/or width + * var mixer = mixitup(containerEl, { + * animation: { + * animateResizeContainer: false + * } + * }); + * + * @example Example 2: Disable the transitioning of the container height and/or width for mobile devices only + * var mixer = mixitup(containerEl, { + * animation: { + * animateResizeContainer: myFeatureTests.isMobile ? false : true + * } + * }); + * + * @name animateResizeContainer + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default true + */ + + this.animateResizeContainer = true; + + /** + * A boolean dictating whether or not to transition the height and width of target + * elements as they change throughout the course of an animation. + * + * This is often a must for flex-box grid layouts where the size of target elements may change + * depending on final their position in relation to their siblings, or for `.changeLayout()` + * operations where the size of targets change between layouts. + * + * NB: This feature requires additional calculations and manipulation to non-hardware-accelerated + * properties which may adversely affect performance on slower devices, and is therefore + * disabled by default. + * + * @example Example: Enable the transitioning of target widths and heights + * var mixer = mixitup(containerEl, { + * animation: { + * animateResizeTargets: true + * } + * }); + * + * @name animateResizeTargets + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default false + */ + + this.animateResizeTargets = false; + + /** + * A custom function used to manipulate the order in which the stagger delay is + * incremented when using the ‘stagger’ effect. + * + * When using the 'stagger' effect, the delay applied to each target element is incremented + * based on its index. You may create a custom function to manipulate the order in which the + * delay is incremented and create engaging non-linear stagger effects. + * + * The function receives the index of the target element as a parameter, and must + * return an integer which serves as the multiplier for the stagger delay. + * + * @example Example 1: Stagger target elements by column in a 3-column grid + * var mixer = mixitup(containerEl, { + * animation: { + * effects: 'fade stagger(100ms)', + * staggerSequence: function(i) { + * return i % 3; + * } + * } + * }); + * + * @example Example 2: Using an algorithm to produce a more complex sequence + * var mixer = mixitup(containerEl, { + * animation: { + * effects: 'fade stagger(100ms)', + * staggerSequence: function(i) { + * return (2*i) - (5*((i/3) - ((1/3) * (i%3)))); + * } + * } + * }); + * + * @name staggerSequence + * @memberof mixitup.Config.animation + * @instance + * @type {function} + * @default null + */ + + this.staggerSequence = null; + + /** + * A boolean dictating whether or not to reverse the direction of `translate` + * and `rotate` transforms for elements being filtered out. + * + * It can be used to create carousel-like animations where elements enter and exit + * from opposite directions. If enabled, the effect `translateX(-100%)` for elements + * being filtered in would become `translateX(100%)` for targets being filtered out. + * + * This functionality can also be achieved by providing seperate effects + * strings for `config.animation.effectsIn` and `config.animation.effectsOut`. + * + * @example Example: Reverse the desired direction on any translate/rotate effect for targets being filtered out + * // Elements being filtered in will be translated from '100%' to '0' while + * // elements being filtered out will be translated from 0 to '-100%' + * + * var mixer = mixitup(containerEl, { + * animation: { + * effects: 'fade translateX(100%)', + * reverseOut: true, + * nudge: false // Disable nudging to create a carousel-like effect + * } + * }); + * + * @name reverseOut + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default false + */ + + this.reverseOut = false; + + /** + * A boolean dictating whether or not to "nudge" the animation path of targets + * when they are being filtered in and out simulatenously. + * + * This has been the default behavior of MixItUp since version 1, but it + * may be desirable to disable this effect when filtering directly from + * one exclusive set of targets to a different exclusive set of targets, + * to create a carousel-like effect, or a generally more subtle animation. + * + * @example Example: Disable the "nudging" of targets being filtered in and out simulatenously + * + * var mixer = mixitup(containerEl, { + * animation: { + * nudge: false + * } + * }); + * + * @name nudge + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default true + */ + + this.nudge = true; + + /** + * A boolean dictating whether or not to clamp the height of the container while MixItUp's + * geometry tests are carried out before an operation. + * + * To prevent scroll-bar flicker, clamping is turned on by default. But in the case where the + * height of the container might affect its vertical positioning in the viewport + * (e.g. a vertically-centered container), this should be turned off to ensure accurate + * test results and a smooth animation. + * + * @example Example: Disable container height-clamping + * + * var mixer = mixitup(containerEl, { + * animation: { + * clampHeight: false + * } + * }); + * + * @name clampHeight + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default true + */ + + this.clampHeight = true; + + /** + * A boolean dictating whether or not to clamp the width of the container while MixItUp's + * geometry tests are carried out before an operation. + * + * To prevent scroll-bar flicker, clamping is turned on by default. But in the case where the + * width of the container might affect its horitzontal positioning in the viewport + * (e.g. a horizontall-centered container), this should be turned off to ensure accurate + * test results and a smooth animation. + * + * @example Example: Disable container width-clamping + * + * var mixer = mixitup(containerEl, { + * animation: { + * clampWidth: false + * } + * }); + * + * @name clampWidth + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default true + */ + + this.clampWidth = true; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigAnimation); + + mixitup.ConfigAnimation.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigAnimation.prototype.constructor = mixitup.ConfigAnimation; + + /** + * A group of properties relating to the behavior of the Mixer. + * + * @constructor + * @memberof mixitup.Config + * @name behavior + * @namespace + * @public + * @since 3.1.12 + */ + + mixitup.ConfigBehavior = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A boolean dictating whether to allow "live" sorting of the mixer. + * + * Because of the expensive nature of sorting, MixItUp makes use of several + * internal optimizations to skip redundant sorting operations, such as when + * the newly requested sort command is the same as the active one. The caveat + * to this optimization is that "live" edits to the value of a target's sorting + * attribute will be ignored when requesting a re-sort by the same attribute. + * + * By setting to `behavior.liveSort` to `true`, the mixer will always re-sort + * regardless of whether or not the sorting attribute and order have changed. + * + * @example Example: Enabling `liveSort` to allow for re-sorting + * + * var mixer = mixitup(containerEl, { + * behavior: { + * liveSort: true + * }, + * load: { + * sort: 'edited:desc' + * } + * }); + * + * var target = containerEl.children[3]; + * + * console.log(target.getAttribute('data-edited')); // '2015-04-24' + * + * target.setAttribute('data-edited', '2017-08-10'); // Update the target's edited date + * + * mixer.sort('edited:desc') + * .then(function(state) { + * // The target is now at the top of the list + * + * console.log(state.targets[0] === target); // true + * }); + * + * @name liveSort + * @memberof mixitup.Config.behavior + * @instance + * @type {boolean} + * @default false + */ + + this.liveSort = false; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigBehavior); + + mixitup.ConfigBehavior.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigBehavior.prototype.constructor = mixitup.ConfigBehavior; + + /** + * A group of optional callback functions to be invoked at various + * points within the lifecycle of a mixer operation. + * + * Each function is analogous to an event of the same name triggered from the + * container element, and is invoked immediately after it. + * + * All callback functions receive the current `state` object as their first + * argument, as well as other more specific arguments described below. + * + * @constructor + * @memberof mixitup.Config + * @name callbacks + * @namespace + * @public + * @since 2.0.0 + */ + + mixitup.ConfigCallbacks = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A callback function invoked immediately after any MixItUp operation is requested + * and before animations have begun. + * + * A second `futureState` argument is passed to the function which represents the final + * state of the mixer once the requested operation has completed. + * + * @example Example: Adding an `onMixStart` callback function + * var mixer = mixitup(containerEl, { + * callbacks: { + * onMixStart: function(state, futureState) { + * console.log('Starting operation...'); + * } + * } + * }); + * + * @name onMixStart + * @memberof mixitup.Config.callbacks + * @instance + * @type {function} + * @default null + */ + + this.onMixStart = null; + + /** + * A callback function invoked when a MixItUp operation is requested while another + * operation is in progress, and the animation queue is full, or queueing + * is disabled. + * + * @example Example: Adding an `onMixBusy` callback function + * var mixer = mixitup(containerEl, { + * callbacks: { + * onMixBusy: function(state) { + * console.log('Mixer busy'); + * } + * } + * }); + * + * @name onMixBusy + * @memberof mixitup.Config.callbacks + * @instance + * @type {function} + * @default null + */ + + this.onMixBusy = null; + + /** + * A callback function invoked after any MixItUp operation has completed, and the + * state has been updated. + * + * @example Example: Adding an `onMixEnd` callback function + * var mixer = mixitup(containerEl, { + * callbacks: { + * onMixEnd: function(state) { + * console.log('Operation complete'); + * } + * } + * }); + * + * @name onMixEnd + * @memberof mixitup.Config.callbacks + * @instance + * @type {function} + * @default null + */ + + this.onMixEnd = null; + + /** + * A callback function invoked whenever an operation "fails", i.e. no targets + * could be found matching the requested filter. + * + * @example Example: Adding an `onMixFail` callback function + * var mixer = mixitup(containerEl, { + * callbacks: { + * onMixFail: function(state) { + * console.log('No items could be found matching the requested filter'); + * } + * } + * }); + * + * @name onMixFail + * @memberof mixitup.Config.callbacks + * @instance + * @type {function} + * @default null + */ + + this.onMixFail = null; + + /** + * A callback function invoked whenever a MixItUp control is clicked, and before its + * respective operation is requested. + * + * The clicked element is assigned to the `this` keyword within the function. The original + * click event is passed to the function as the second argument, which can be useful if + * using `` tags as controls where the default behavior needs to be prevented. + * + * Returning `false` from the callback will prevent the control click from triggering + * an operation. + * + * @example Example 1: Adding an `onMixClick` callback function + * var mixer = mixitup(containerEl, { + * callbacks: { + * onMixClick: function(state, originalEvent) { + * console.log('The control "' + this.innerText + '" was clicked'); + * } + * } + * }); + * + * @example Example 2: Using `onMixClick` to manipulate the original click event + * var mixer = mixitup(containerEl, { + * callbacks: { + * onMixClick: function(state, originalEvent) { + * // Prevent original click event from bubbling up: + * originalEvent.stopPropagation(); + * + * // Prevent default behavior of clicked element: + * originalEvent.preventDefault(); + * } + * } + * }); + * + * @example Example 3: Using `onMixClick` to conditionally cancel operations + * var mixer = mixitup(containerEl, { + * callbacks: { + * onMixClick: function(state, originalEvent) { + * // Perform some conditional check: + * + * if (myApp.isLoading) { + * // By returning false, we can prevent the control click from triggering an operation. + * + * return false; + * } + * } + * } + * }); + * + * @name onMixClick + * @memberof mixitup.Config.callbacks + * @instance + * @type {function} + * @default null + */ + + this.onMixClick = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigCallbacks); + + mixitup.ConfigCallbacks.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigCallbacks.prototype.constructor = mixitup.ConfigCallbacks; + + /** + * A group of properties relating to clickable control elements. + * + * @constructor + * @memberof mixitup.Config + * @name controls + * @namespace + * @public + * @since 2.0.0 + */ + + mixitup.ConfigControls = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A boolean dictating whether or not controls should be enabled for the mixer instance. + * + * If `true` (default behavior), MixItUp will search the DOM for any clickable elements with + * `data-filter`, `data-sort` or `data-toggle` attributes, and bind them for click events. + * + * If `false`, no click handlers will be bound, and all functionality must therefore be performed + * via the mixer's API methods. + * + * If you do not intend to use the default controls, setting this property to `false` will + * marginally improve the startup time of your mixer instance, and will also prevent any other active + * mixer instances in the DOM which are bound to controls from controlling the instance. + * + * @example Example: Disabling controls + * var mixer = mixitup(containerEl, { + * controls: { + * enable: false + * } + * }); + * + * // With the default controls disabled, we can only control + * // the mixer via its API methods, e.g.: + * + * mixer.filter('.cat-1'); + * + * @name enable + * @memberof mixitup.Config.controls + * @instance + * @type {boolean} + * @default true + */ + + this.enable = true; + + /** + * A boolean dictating whether or not to use event delegation when binding click events + * to the default controls. + * + * If `false` (default behavior), each control button in the DOM will be found and + * individually bound when a mixer is instantiated, with their corresponding actions + * cached for performance. + * + * If `true`, a single click handler will be applied to the `window` (or container element - see + * `config.controls.scope`), and any click events triggered by elements with `data-filter`, + * `data-sort` or `data-toggle` attributes present will be handled as they propagate upwards. + * + * If you require a user interface where control buttons may be added, removed, or changed during the + * lifetime of a mixer, `controls.live` should be set to `true`. There is a marginal but unavoidable + * performance deficit when using live controls, as the value of each control button must be read + * from the DOM in real time once the click event has propagated. + * + * @example Example: Setting live controls + * var mixer = mixitup(containerEl, { + * controls: { + * live: true + * } + * }); + * + * // Control buttons can now be added, remove and changed without breaking + * // the mixer's UI + * + * @name live + * @memberof mixitup.Config.controls + * @instance + * @type {boolean} + * @default true + */ + + this.live = false; + + /** + * A string dictating the "scope" to use when binding or querying the default controls. The available + * values are `'global'` or `'local'`. + * + * When set to `'global'` (default behavior), MixItUp will query the entire document for control buttons + * to bind, or delegate click events from (see `config.controls.live`). + * + * When set to `'local'`, MixItUp will only query (or bind click events to) its own container element. + * This may be desireable if you require multiple active mixer instances within the same document, with + * controls that would otherwise intefere with each other if scoped globally. + * + * Conversely, if you wish to control multiple instances with a single UI, you would create one + * set of controls and keep the controls scope of each mixer set to `global`. + * + * @example Example: Setting 'local' scoped controls + * var mixerOne = mixitup(containerOne, { + * controls: { + * scope: 'local' + * } + * }); + * + * var mixerTwo = mixitup(containerTwo, { + * controls: { + * scope: 'local' + * } + * }); + * + * // Both mixers can now exist within the same document with + * // isolated controls placed within their container elements. + * + * @name scope + * @memberof mixitup.Config.controls + * @instance + * @type {string} + * @default 'global' + */ + + this.scope = 'global'; // enum: ['local' ,'global'] + + /** + * A string dictating the type of logic to apply when concatenating the filter selectors of + * active toggle buttons (i.e. any clickable element with a `data-toggle` attribute). + * + * If set to `'or'` (default behavior), selectors will be concatenated together as + * a comma-seperated list. For example: + * + * `'.cat-1, .cat-2'` (shows any elements matching `'.cat-1'` OR `'.cat-2'`) + * + * If set to `'and'`, selectors will be directly concatenated together. For example: + * + * `'.cat-1.cat-2'` (shows any elements which match both `'.cat-1'` AND `'.cat-2'`) + * + * @example Example: Setting "and" toggle logic + * var mixer = mixitup(containerEl, { + * controls: { + * toggleLogic: 'and' + * } + * }); + * + * @name toggleLogic + * @memberof mixitup.Config.controls + * @instance + * @type {string} + * @default 'or' + */ + + this.toggleLogic = 'or'; // enum: ['or', 'and'] + + /** + * A string dictating the filter behavior when all toggles are inactive. + * + * When set to `'all'` (default behavior), *all* targets will be shown by default + * when no toggles are active, or at the moment all active toggles are toggled off. + * + * When set to `'none'`, no targets will be shown by default when no toggles are + * active, or at the moment all active toggles are toggled off. + * + * @example Example 1: Setting the default toggle behavior to `'all'` + * var mixer = mixitup(containerEl, { + * controls: { + * toggleDefault: 'all' + * } + * }); + * + * mixer.toggleOn('.cat-2') + * .then(function() { + * // Deactivate all active toggles + * + * return mixer.toggleOff('.cat-2') + * }) + * .then(function(state) { + * console.log(state.activeFilter.selector); // 'all' + * console.log(state.totalShow); // 12 + * }); + * + * @example Example 2: Setting the default toggle behavior to `'none'` + * var mixer = mixitup(containerEl, { + * controls: { + * toggleDefault: 'none' + * } + * }); + * + * mixer.toggleOn('.cat-2') + * .then(function() { + * // Deactivate all active toggles + * + * return mixer.toggleOff('.cat-2') + * }) + * .then(function(state) { + * console.log(state.activeFilter.selector); // 'none' + * console.log(state.totalShow); // 0 + * }); + * + * @name toggleDefault + * @memberof mixitup.Config.controls + * @instance + * @type {string} + * @default 'all' + */ + + this.toggleDefault = 'all'; // enum: ['all', 'none'] + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigControls); + + mixitup.ConfigControls.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigControls.prototype.constructor = mixitup.ConfigControls; + + /** + * A group of properties defining the output and structure of class names programmatically + * added to controls and containers to reflect the state of the mixer. + * + * Most commonly, class names are added to controls by MixItUp to indicate that + * the control is active so that it can be styled accordingly - `'mixitup-control-active'` by default. + * + * Using a "BEM" like structure, each classname is broken into the three parts: + * a block namespace (`'mixitup'`), an element name (e.g. `'control'`), and an optional modifier + * name (e.g. `'active'`) reflecting the state of the element. + * + * By default, each part of the classname is concatenated together using single hyphens as + * delineators, but this can be easily customised to match the naming convention and style of + * your project. + * + * @constructor + * @memberof mixitup.Config + * @name classNames + * @namespace + * @public + * @since 3.0.0 + */ + + mixitup.ConfigClassNames = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * The "block" portion, or top-level namespace added to the start of any class names created by MixItUp. + * + * @example Example 1: changing the `config.classNames.block` value + * var mixer = mixitup(containerEl, { + * classNames: { + * block: 'portfolio' + * } + * }); + * + * // Active control output: "portfolio-control-active" + * + * @example Example 2: Removing `config.classNames.block` + * var mixer = mixitup(containerEl, { + * classNames: { + * block: '' + * } + * }); + * + * // Active control output: "control-active" + * + * @name block + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'mixitup' + */ + + this.block = 'mixitup'; + + /** + * The "element" portion of the class name added to container. + * + * @name elementContainer + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'container' + */ + + this.elementContainer = 'container'; + + /** + * The "element" portion of the class name added to filter controls. + * + * By default, all filter, sort, multimix and toggle controls take the same element value of `'control'`, but + * each type's element value can be individually overwritten to match the unique classNames of your controls as needed. + * + * @example Example 1: changing the `config.classNames.elementFilter` value + * var mixer = mixitup(containerEl, { + * classNames: { + * elementFilter: 'filter' + * } + * }); + * + * // Active filter output: "mixitup-filter-active" + * + * @example Example 2: changing the `config.classNames.block` and `config.classNames.elementFilter` values + * var mixer = mixitup(containerEl, { + * classNames: { + * block: 'portfolio', + * elementFilter: 'filter' + * } + * }); + * + * // Active filter output: "portfolio-filter-active" + * + * @name elementFilter + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'control' + */ + + this.elementFilter = 'control'; + + /** + * The "element" portion of the class name added to sort controls. + * + * By default, all filter, sort, multimix and toggle controls take the same element value of `'control'`, but + * each type's element value can be individually overwritten to match the unique classNames of your controls as needed. + * + * @example Example 1: changing the `config.classNames.elementSort` value + * var mixer = mixitup(containerEl, { + * classNames: { + * elementSort: 'sort' + * } + * }); + * + * // Active sort output: "mixitup-sort-active" + * + * @example Example 2: changing the `config.classNames.block` and `config.classNames.elementSort` values + * var mixer = mixitup(containerEl, { + * classNames: { + * block: 'portfolio', + * elementSort: 'sort' + * } + * }); + * + * // Active sort output: "portfolio-sort-active" + * + * @name elementSort + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'control' + */ + + this.elementSort = 'control'; + + /** + * The "element" portion of the class name added to multimix controls. + * + * By default, all filter, sort, multimix and toggle controls take the same element value of `'control'`, but + * each type's element value can be individually overwritten to match the unique classNames of your controls as needed. + * + * @example Example 1: changing the `config.classNames.elementMultimix` value + * var mixer = mixitup(containerEl, { + * classNames: { + * elementMultimix: 'multimix' + * } + * }); + * + * // Active multimix output: "mixitup-multimix-active" + * + * @example Example 2: changing the `config.classNames.block` and `config.classNames.elementMultimix` values + * var mixer = mixitup(containerEl, { + * classNames: { + * block: 'portfolio', + * elementSort: 'multimix' + * } + * }); + * + * // Active multimix output: "portfolio-multimix-active" + * + * @name elementMultimix + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'control' + */ + + this.elementMultimix = 'control'; + + /** + * The "element" portion of the class name added to toggle controls. + * + * By default, all filter, sort, multimix and toggle controls take the same element value of `'control'`, but + * each type's element value can be individually overwritten to match the unique classNames of your controls as needed. + * + * @example Example 1: changing the `config.classNames.elementToggle` value + * var mixer = mixitup(containerEl, { + * classNames: { + * elementToggle: 'toggle' + * } + * }); + * + * // Active toggle output: "mixitup-toggle-active" + * + * @example Example 2: changing the `config.classNames.block` and `config.classNames.elementToggle` values + * var mixer = mixitup(containerEl, { + * classNames: { + * block: 'portfolio', + * elementToggle: 'toggle' + * } + * }); + * + * // Active toggle output: "portfolio-toggle-active" + * + * @name elementToggle + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'control' + */ + + this.elementToggle = 'control'; + + /** + * The "modifier" portion of the class name added to active controls. + * @name modifierActive + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'active' + */ + + this.modifierActive = 'active'; + + /** + * The "modifier" portion of the class name added to disabled controls. + * + * @name modifierDisabled + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'disabled' + */ + + this.modifierDisabled = 'disabled'; + + /** + * The "modifier" portion of the class name added to the container when in a "failed" state. + * + * @name modifierFailed + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'failed' + */ + + this.modifierFailed = 'failed'; + + /** + * The delineator used between the "block" and "element" portions of any class name added by MixItUp. + * + * If the block portion is ommited by setting it to an empty string, no delineator will be added. + * + * @example Example: changing the delineator to match BEM convention + * var mixer = mixitup(containerEl, { + * classNames: { + * delineatorElement: '__' + * } + * }); + * + * // example active control output: "mixitup__control-active" + * + * @name delineatorElement + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default '-' + */ + + this.delineatorElement = '-'; + + /** + * The delineator used between the "element" and "modifier" portions of any class name added by MixItUp. + * + * If the element portion is ommited by setting it to an empty string, no delineator will be added. + * + * @example Example: changing both delineators to match BEM convention + * var mixer = mixitup(containerEl, { + * classNames: { + * delineatorElement: '__' + * delineatorModifier: '--' + * } + * }); + * + * // Active control output: "mixitup__control--active" + * + * @name delineatorModifier + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default '-' + */ + + this.delineatorModifier = '-'; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigClassNames); + + mixitup.ConfigClassNames.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigClassNames.prototype.constructor = mixitup.ConfigClassNames; + + /** + * A group of properties relating to MixItUp's dataset API. + * + * @constructor + * @memberof mixitup.Config + * @name data + * @namespace + * @public + * @since 3.0.0 + */ + + mixitup.ConfigData = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A string specifying the name of the key containing your data model's unique + * identifier (UID). To use the dataset API, a UID key must be specified and + * be present and unique on all objects in the dataset you provide to MixItUp. + * + * For example, if your dataset is made up of MongoDB documents, the UID + * key would be `'id'` or `'_id'`. + * + * @example Example: Setting the UID to `'id'` + * var mixer = mixitup(containerEl, { + * data: { + * uidKey: 'id' + * } + * }); + * + * @name uidKey + * @memberof mixitup.Config.data + * @instance + * @type {string} + * @default '' + */ + + this.uidKey = ''; + + /** + * A boolean dictating whether or not MixItUp should "dirty check" each object in + * your dataset for changes whenever `.dataset()` is called, and re-render any targets + * for which a change is found. + * + * Depending on the complexity of your data model, dirty checking can be expensive + * and is therefore disabled by default. + * + * NB: For changes to be detected, a new immutable instance of the edited model must be + * provided to mixitup, rather than manipulating properties on the existing instance. + * If your changes are a result of a DB write and read, you will most likely be calling + * `.dataset()` with a clean set of objects each time, so this will not be an issue. + * + * @example Example: Enabling dirty checking + * + * var myDataset = [ + * { + * id: 0, + * title: "Blog Post Title 0" + * ... + * }, + * { + * id: 1, + * title: "Blog Post Title 1" + * ... + * } + * ]; + * + * // Instantiate a mixer with a pre-loaded dataset, and a target renderer + * // function defined + * + * var mixer = mixitup(containerEl, { + * data: { + * uidKey: 'id', + * dirtyCheck: true + * }, + * load: { + * dataset: myDataset + * }, + * render: { + * target: function() { ... } + * } + * }); + * + * // For illustration, we will clone and edit the second object in the dataset. + * // NB: this would typically be done server-side in response to a DB update, + * and then re-queried via an API. + * + * myDataset[1] = Object.assign({}, myDataset[1]); + * + * myDataset[1].title = 'Blog Post Title 11'; + * + * mixer.dataset(myDataset) + * .then(function() { + * // the target with ID "1", will be re-rendered reflecting its new title + * }); + * + * @name dirtyCheck + * @memberof mixitup.Config.data + * @instance + * @type {boolean} + * @default false + */ + + this.dirtyCheck = false; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigData); + + mixitup.ConfigData.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigData.prototype.constructor = mixitup.ConfigData; + + /** + * A group of properties allowing the toggling of various debug features. + * + * @constructor + * @memberof mixitup.Config + * @name debug + * @namespace + * @public + * @since 3.0.0 + */ + + mixitup.ConfigDebug = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A boolean dictating whether or not the mixer instance returned by the + * `mixitup()` factory function should expose private properties and methods. + * + * By default, mixer instances only expose their public API, but enabling + * debug mode will give you access to various mixer internals which may aid + * in debugging, or the authoring of extensions. + * + * @example Example: Enabling debug mode + * + * var mixer = mixitup(containerEl, { + * debug: { + * enable: true + * } + * }); + * + * // Private properties and methods will now be visible on the mixer instance: + * + * console.log(mixer); + * + * @name enable + * @memberof mixitup.Config.debug + * @instance + * @type {boolean} + * @default false + */ + + this.enable = false; + + /** + * A boolean dictating whether or not warnings should be shown when various + * common gotchas occur. + * + * Warnings are intended to provide insights during development when something + * occurs that is not a fatal, but may indicate an issue with your integration, + * and are therefore turned on by default. However, you may wish to disable + * them in production. + * + * @example Example 1: Disabling warnings + * + * var mixer = mixitup(containerEl, { + * debug: { + * showWarnings: false + * } + * }); + * + * @example Example 2: Disabling warnings based on environment + * + * var showWarnings = myAppConfig.environment === 'development' ? true : false; + * + * var mixer = mixitup(containerEl, { + * debug: { + * showWarnings: showWarnings + * } + * }); + * + * @name showWarnings + * @memberof mixitup.Config.debug + * @instance + * @type {boolean} + * @default true + */ + + this.showWarnings = true; + + /** + * Used for server-side testing only. + * + * @private + * @name fauxAsync + * @memberof mixitup.Config.debug + * @instance + * @type {boolean} + * @default false + */ + + this.fauxAsync = false; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigDebug); + + mixitup.ConfigDebug.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigDebug.prototype.constructor = mixitup.ConfigDebug; + + /** + * A group of properties relating to the layout of the container. + * + * @constructor + * @memberof mixitup.Config + * @name layout + * @namespace + * @public + * @since 3.0.0 + */ + + mixitup.ConfigLayout = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A boolean dictating whether or not mixitup should query all descendants + * of the container for targets, or only immediate children. + * + * By default, mixitup will query all descendants matching the + * `selectors.target` selector when indexing targets upon instantiation. + * This allows for targets to be nested inside a sub-container which is + * useful when ring-fencing targets from locally scoped controls in your + * markup (see `controls.scope`). + * + * However, if you are building a more complex UI requiring the nesting + * of mixers within mixers, you will most likely want to limit targets to + * immediate children of the container by setting this property to `false`. + * + * @example Example: Restricting targets to immediate children + * + * var mixer = mixitup(containerEl, { + * layout: { + * allowNestedTargets: false + * } + * }); + * + * @name allowNestedTargets + * @memberof mixitup.Config.layout + * @instance + * @type {boolean} + * @default true + */ + + this.allowNestedTargets = true; + + /** + * A string specifying an optional class name to apply to the container when in + * its default state. + * + * By changing this class name or adding a class name to the container via the + * `.changeLayout()` API method, the CSS layout of the container can be changed, + * and MixItUp will attemp to gracefully animate the container and its targets + * between states. + * + * @example Example 1: Specifying a container class name + * + * var mixer = mixitup(containerEl, { + * layout: { + * containerClassName: 'grid' + * } + * }); + * + * @example Example 2: Changing the default class name with `.changeLayout()` + * + * var mixer = mixitup(containerEl, { + * layout: { + * containerClassName: 'grid' + * } + * }); + * + * mixer.changeLayout('list') + * .then(function(state) { + * console.log(state.activeContainerClass); // "list" + * }); + * + * @name containerClassName + * @memberof mixitup.Config.layout + * @instance + * @type {string} + * @default '' + */ + + this.containerClassName = ''; + + /** + * A reference to a non-target sibling element after which to insert targets + * when there are no targets in the container. + * + * @example Example: Setting a `siblingBefore` reference element + * + * var addButton = containerEl.querySelector('button'); + * + * var mixer = mixitup(containerEl, { + * layout: { + * siblingBefore: addButton + * } + * }); + * + * @name siblingBefore + * @memberof mixitup.Config.layout + * @instance + * @type {HTMLElement} + * @default null + */ + + this.siblingBefore = null; + + /** + * A reference to a non-target sibling element before which to insert targets + * when there are no targets in the container. + * + * @example Example: Setting an `siblingAfter` reference element + * + * var gap = containerEl.querySelector('.gap'); + * + * var mixer = mixitup(containerEl, { + * layout: { + * siblingAfter: gap + * } + * }); + * + * @name siblingAfter + * @memberof mixitup.Config.layout + * @instance + * @type {HTMLElement} + * @default null + */ + + this.siblingAfter = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigLayout); + + mixitup.ConfigLayout.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigLayout.prototype.constructor = mixitup.ConfigLayout; + + /** + * A group of properties defining the initial state of the mixer on load (instantiation). + * + * @constructor + * @memberof mixitup.Config + * @name load + * @namespace + * @public + * @since 2.0.0 + */ + + mixitup.ConfigLoad = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A string defining any filtering to be statically applied to the mixer on load. + * As per the `.filter()` API, this can be any valid selector string, or the + * values `'all'` or `'none'`. + * + * @example Example 1: Defining an initial filter selector to be applied on load + * + * // The mixer will show only those targets matching '.category-a' on load. + * + * var mixer = mixitup(containerEl, { + * load: { + * filter: '.category-a' + * } + * }); + * + * @example Example 2: Hiding all targets on load + * + * // The mixer will show hide all targets on load. + * + * var mixer = mixitup(containerEl, { + * load: { + * filter: 'none' + * } + * }); + * + * @name filter + * @memberof mixitup.Config.load + * @instance + * @type {string} + * @default 'all' + */ + + this.filter = 'all'; + + /** + * A string defining any sorting to be statically applied to the mixer on load. + * As per the `.sort()` API, this should be a valid "sort string" made up of + * an attribute to sort by (or `'default'`) followed by an optional sorting + * order, or the value `'random'`; + * + * @example Example: Defining sorting to be applied on load + * + * // The mixer will sort the container by the value of the `data-published-date` + * // attribute, in descending order. + * + * var mixer = mixitup(containerEl, { + * load: { + * sort: 'published-date:desc' + * } + * }); + * + * @name sort + * @memberof mixitup.Config.load + * @instance + * @type {string} + * @default 'default:asc' + */ + + this.sort = 'default:asc'; + + /** + * An array of objects representing the underlying data of any pre-rendered targets, + * when using the `.dataset()` API. + * + * NB: If targets are pre-rendered when the mixer is instantiated, this must be set. + * + * @example Example: Defining the initial underyling dataset + * + * var myDataset = [ + * { + * id: 0, + * title: "Blog Post Title 0", + * ... + * }, + * { + * id: 1, + * title: "Blog Post Title 1", + * ... + * } + * ]; + * + * var mixer = mixitup(containerEl, { + * data: { + * uidKey: 'id' + * }, + * load: { + * dataset: myDataset + * } + * }); + * + * @name dataset + * @memberof mixitup.Config.load + * @instance + * @type {Array.} + * @default null + */ + + this.dataset = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigLoad); + + mixitup.ConfigLoad.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigLoad.prototype.constructor = mixitup.ConfigLoad; + + /** + * A group of properties defining the selectors used to query elements within a mixitup container. + * + * @constructor + * @memberof mixitup.Config + * @name selectors + * @namespace + * @public + * @since 3.0.0 + */ + + mixitup.ConfigSelectors = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A selector string used to query and index target elements within the container. + * + * By default, the class selector `'.mix'` is used, but this can be changed to an + * attribute or element selector to match the style of your project. + * + * @example Example 1: Changing the target selector + * + * var mixer = mixitup(containerEl, { + * selectors: { + * target: '.portfolio-item' + * } + * }); + * + * @example Example 2: Using an attribute selector as a target selector + * + * // The mixer will search for any children with the attribute `data-ref="mix"` + * + * var mixer = mixitup(containerEl, { + * selectors: { + * target: '[data-ref="mix"]' + * } + * }); + * + * @name target + * @memberof mixitup.Config.selectors + * @instance + * @type {string} + * @default '.mix' + */ + + this.target = '.mix'; + + /** + * A optional selector string used to add further specificity to the querying of control elements, + * in addition to their mandatory data attribute (e.g. `data-filter`, `data-toggle`, `data-sort`). + * + * This can be used if other elements in your document must contain the above attributes + * (e.g. for use in third-party scripts), and would otherwise interfere with MixItUp. Adding + * an additional `control` selector of your choice allows MixItUp to restrict event handling + * to only those elements matching the defined selector. + * + * @name control + * @memberof mixitup.Config.selectors + * @instance + * @type {string} + * @default '' + * + * @example Example 1: Adding a `selectors.control` selector + * + * var mixer = mixitup(containerEl, { + * selectors: { + * control: '.mixitup-control' + * } + * }); + * + * // Will not be handled: + * // + * + * // Will be handled: + * // + */ + + this.control = ''; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigSelectors); + + mixitup.ConfigSelectors.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigSelectors.prototype.constructor = mixitup.ConfigSelectors; + + /** + * A group of optional render functions for creating and updating elements. + * + * All render functions receive a data object, and should return a valid HTML string. + * + * @constructor + * @memberof mixitup.Config + * @name render + * @namespace + * @public + * @since 3.0.0 + */ + + mixitup.ConfigRender = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A function returning an HTML string representing a target element, or a reference to a + * single DOM element. + * + * The function is invoked as part of the `.dataset()` API, whenever a new item is added + * to the dataset, or an item in the dataset changes (if `dataset.dirtyCheck` is enabled). + * + * The function receives the relevant dataset item as its first parameter. + * + * @example Example 1: Using string concatenation + * + * var mixer = mixitup(containerEl, { + * render: { + * target: function(item) { + * return ( + * '<div class="mix">' + + * '<h2>' + item.title + '</h2>' + + * '</div>' + * ); + * } + * } + * }); + * + * @example Example 2: Using an ES2015 template literal + * + * var mixer = mixitup(containerEl, { + * render: { + * target: function(item) { + * return ( + * `<div class="mix"> + * <h2>${item.title}</h2> + * </div>` + * ); + * } + * } + * }); + * + * @example Example 3: Using a Handlebars template + * + * var targetTemplate = Handlebars.compile('<div class="mix"><h2>{{title}}</h2></div>'); + * + * var mixer = mixitup(containerEl, { + * render: { + * target: targetTemplate + * } + * }); + * + * @example Example 4: Returning a DOM element + * + * var mixer = mixitup(containerEl, { + * render: { + * target: function(item) { + * // Create a single element using your framework's built-in renderer + * + * var el = ... + * + * return el; + * } + * } + * }); + * + * @name target + * @memberof mixitup.Config.render + * @instance + * @type {function} + * @default 'null' + */ + + this.target = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigRender); + + mixitup.ConfigRender.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigRender.prototype.constructor = mixitup.ConfigRender; + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.ConfigTemplates = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigTemplates); + + mixitup.ConfigTemplates.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigTemplates.prototype.constructor = mixitup.ConfigTemplates; + + /** + * `mixitup.Config` is an interface used for customising the functionality of a + * mixer instance. It is organised into several semantically distinct sub-objects, + * each one pertaining to a particular aspect of MixItUp functionality. + * + * An object literal containing any or all of the available properies, + * known as the "configuration object", can be passed as the second parameter to + * the `mixitup` factory function when creating a mixer instance to customise its + * functionality as needed. + * + * If no configuration object is passed, the mixer instance will take on the default + * configuration values detailed below. + * + * @example Example 1: Creating and passing the configuration object + * // Create a configuration object with desired values + * + * var config = { + * animation: { + * enable: false + * }, + * selectors: { + * target: '.item' + * } + * }; + * + * // Pass the configuration object to the mixitup factory function + * + * var mixer = mixitup(containerEl, config); + * + * @example Example 2: Passing the configuration object inline + * // Typically, the configuration object is passed inline for brevity. + * + * var mixer = mixitup(containerEl, { + * controls: { + * live: true, + * toggleLogic: 'and' + * } + * }); + * + * + * @constructor + * @memberof mixitup + * @namespace + * @public + * @since 2.0.0 + */ + + mixitup.Config = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.animation = new mixitup.ConfigAnimation(); + this.behavior = new mixitup.ConfigBehavior(); + this.callbacks = new mixitup.ConfigCallbacks(); + this.controls = new mixitup.ConfigControls(); + this.classNames = new mixitup.ConfigClassNames(); + this.data = new mixitup.ConfigData(); + this.debug = new mixitup.ConfigDebug(); + this.layout = new mixitup.ConfigLayout(); + this.load = new mixitup.ConfigLoad(); + this.selectors = new mixitup.ConfigSelectors(); + this.render = new mixitup.ConfigRender(); + this.templates = new mixitup.ConfigTemplates(); + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.Config); + + mixitup.Config.prototype = Object.create(mixitup.Base.prototype); + + mixitup.Config.prototype.constructor = mixitup.Config; + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.MixerDom = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.document = null; + this.body = null; + this.container = null; + this.parent = null; + this.targets = []; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.MixerDom); + + mixitup.MixerDom.prototype = Object.create(mixitup.Base.prototype); + + mixitup.MixerDom.prototype.constructor = mixitup.MixerDom; + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.UiClassNames = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.base = ''; + this.active = ''; + this.disabled = ''; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.UiClassNames); + + mixitup.UiClassNames.prototype = Object.create(mixitup.Base.prototype); + + mixitup.UiClassNames.prototype.constructor = mixitup.UiClassNames; + + /** + * An object into which all arbitrary arguments sent to '.dataset()' are mapped. + * + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.CommandDataset = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.dataset = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.CommandDataset); + + mixitup.CommandDataset.prototype = Object.create(mixitup.Base.prototype); + + mixitup.CommandDataset.prototype.constructor = mixitup.CommandDataset; + + /** + * An object into which all arbitrary arguments sent to '.multimix()' are mapped. + * + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.CommandMultimix = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.filter = null; + this.sort = null; + this.insert = null; + this.remove = null; + this.changeLayout = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.CommandMultimix); + + mixitup.CommandMultimix.prototype = Object.create(mixitup.Base.prototype); + + mixitup.CommandMultimix.prototype.constructor = mixitup.CommandMultimix; + + /** + * An object into which all arbitrary arguments sent to '.filter()' are mapped. + * + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.CommandFilter = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.selector = ''; + this.collection = null; + this.action = 'show'; // enum: ['show', 'hide'] + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.CommandFilter); + + mixitup.CommandFilter.prototype = Object.create(mixitup.Base.prototype); + + mixitup.CommandFilter.prototype.constructor = mixitup.CommandFilter; + + /** + * An object into which all arbitrary arguments sent to '.sort()' are mapped. + * + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.CommandSort = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.sortString = ''; + this.attribute = ''; + this.order = 'asc'; + this.collection = null; + this.next = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.CommandSort); + + mixitup.CommandSort.prototype = Object.create(mixitup.Base.prototype); + + mixitup.CommandSort.prototype.constructor = mixitup.CommandSort; + + /** + * An object into which all arbitrary arguments sent to '.insert()' are mapped. + * + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.CommandInsert = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.index = 0; + this.collection = []; + this.position = 'before'; // enum: ['before', 'after'] + this.sibling = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.CommandInsert); + + mixitup.CommandInsert.prototype = Object.create(mixitup.Base.prototype); + + mixitup.CommandInsert.prototype.constructor = mixitup.CommandInsert; + + /** + * An object into which all arbitrary arguments sent to '.remove()' are mapped. + * + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.CommandRemove = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.targets = []; + this.collection = []; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.CommandRemove); + + mixitup.CommandRemove.prototype = Object.create(mixitup.Base.prototype); + + mixitup.CommandRemove.prototype.constructor = mixitup.CommandRemove; + + /** + * An object into which all arbitrary arguments sent to '.changeLayout()' are mapped. + * + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.CommandChangeLayout = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.containerClassName = ''; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.CommandChangeLayout); + + mixitup.CommandChangeLayout.prototype = Object.create(mixitup.Base.prototype); + + mixitup.CommandChangeLayout.prototype.constructor = mixitup.CommandChangeLayout; + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + * @param {string} type + * @param {string} selector + * @param {boolean} [live] + * @param {string} [parent] + * An optional string representing the name of the mixer.dom property containing a reference to a parent element. + */ + + mixitup.ControlDefinition = function(type, selector, live, parent) { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.type = type; + this.selector = selector; + this.live = live || false; + this.parent = parent || ''; + + this.callActions('afterConstruct'); + + h.freeze(this); + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ControlDefinition); + + mixitup.ControlDefinition.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ControlDefinition.prototype.constructor = mixitup.ControlDefinition; + + mixitup.controlDefinitions = []; + + mixitup.controlDefinitions.push(new mixitup.ControlDefinition('multimix', '[data-filter][data-sort]')); + mixitup.controlDefinitions.push(new mixitup.ControlDefinition('filter', '[data-filter]')); + mixitup.controlDefinitions.push(new mixitup.ControlDefinition('sort', '[data-sort]')); + mixitup.controlDefinitions.push(new mixitup.ControlDefinition('toggle', '[data-toggle]')); + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.Control = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.el = null; + this.selector = ''; + this.bound = []; + this.pending = -1; + this.type = ''; + this.status = 'inactive'; // enum: ['inactive', 'active', 'disabled', 'live'] + this.filter = ''; + this.sort = ''; + this.canDisable = false; + this.handler = null; + this.classNames = new mixitup.UiClassNames(); + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.Control); + + mixitup.Control.prototype = Object.create(mixitup.Base.prototype); + + h.extend(mixitup.Control.prototype, + /** @lends mixitup.Control */ + { + constructor: mixitup.Control, + + /** + * @private + * @param {HTMLElement} el + * @param {string} type + * @param {string} selector + */ + + init: function(el, type, selector) { + var self = this; + + this.callActions('beforeInit', arguments); + + self.el = el; + self.type = type; + self.selector = selector; + + if (self.selector) { + self.status = 'live'; + } else { + self.canDisable = typeof self.el.disable === 'boolean'; + + switch (self.type) { + case 'filter': + self.filter = self.el.getAttribute('data-filter'); + + break; + case 'toggle': + self.filter = self.el.getAttribute('data-toggle'); + + break; + case 'sort': + self.sort = self.el.getAttribute('data-sort'); + + break; + case 'multimix': + self.filter = self.el.getAttribute('data-filter'); + self.sort = self.el.getAttribute('data-sort'); + + break; + } + } + + self.bindClick(); + + mixitup.controls.push(self); + + this.callActions('afterInit', arguments); + }, + + /** + * @private + * @param {mixitup.Mixer} mixer + * @return {boolean} + */ + + isBound: function(mixer) { + var self = this, + isBound = false; + + this.callActions('beforeIsBound', arguments); + + isBound = self.bound.indexOf(mixer) > -1; + + return self.callFilters('afterIsBound', isBound, arguments); + }, + + /** + * @private + * @param {mixitup.Mixer} mixer + * @return {void} + */ + + addBinding: function(mixer) { + var self = this; + + this.callActions('beforeAddBinding', arguments); + + if (!self.isBound()) { + self.bound.push(mixer); + } + + this.callActions('afterAddBinding', arguments); + }, + + /** + * @private + * @param {mixitup.Mixer} mixer + * @return {void} + */ + + removeBinding: function(mixer) { + var self = this, + removeIndex = -1; + + this.callActions('beforeRemoveBinding', arguments); + + if ((removeIndex = self.bound.indexOf(mixer)) > -1) { + self.bound.splice(removeIndex, 1); + } + + if (self.bound.length < 1) { + // No bindings exist, unbind event click handlers + + self.unbindClick(); + + // Remove from `mixitup.controls` list + + removeIndex = mixitup.controls.indexOf(self); + + mixitup.controls.splice(removeIndex, 1); + + if (self.status === 'active') { + self.renderStatus(self.el, 'inactive'); + } + } + + this.callActions('afterRemoveBinding', arguments); + }, + + /** + * @private + * @return {void} + */ + + bindClick: function() { + var self = this; + + this.callActions('beforeBindClick', arguments); + + self.handler = function(e) { + self.handleClick(e); + }; + + h.on(self.el, 'click', self.handler); + + this.callActions('afterBindClick', arguments); + }, + + /** + * @private + * @return {void} + */ + + unbindClick: function() { + var self = this; + + this.callActions('beforeUnbindClick', arguments); + + h.off(self.el, 'click', self.handler); + + self.handler = null; + + this.callActions('afterUnbindClick', arguments); + }, + + /** + * @private + * @param {MouseEvent} e + * @return {void} + */ + + handleClick: function(e) { + var self = this, + button = null, + mixer = null, + isActive = false, + returnValue = void(0), + command = {}, + clone = null, + commands = [], + i = -1; + + this.callActions('beforeHandleClick', arguments); + + this.pending = 0; + + mixer = self.bound[0]; + + if (!self.selector) { + button = self.el; + } else { + button = h.closestParent(e.target, mixer.config.selectors.control + self.selector, true, mixer.dom.document); + } + + if (!button) { + self.callActions('afterHandleClick', arguments); + + return; + } + + switch (self.type) { + case 'filter': + command.filter = self.filter || button.getAttribute('data-filter'); + + break; + case 'sort': + command.sort = self.sort || button.getAttribute('data-sort'); + + break; + case 'multimix': + command.filter = self.filter || button.getAttribute('data-filter'); + command.sort = self.sort || button.getAttribute('data-sort'); + + break; + case 'toggle': + command.filter = self.filter || button.getAttribute('data-toggle'); + + if (self.status === 'live') { + isActive = h.hasClass(button, self.classNames.active); + } else { + isActive = self.status === 'active'; + } + + break; + } + + for (i = 0; i < self.bound.length; i++) { + // Create a clone of the command for each bound mixer instance + + clone = new mixitup.CommandMultimix(); + + h.extend(clone, command); + + commands.push(clone); + } + + commands = self.callFilters('commandsHandleClick', commands, arguments); + + self.pending = self.bound.length; + + for (i = 0; mixer = self.bound[i]; i++) { + command = commands[i]; + + if (!command) { + // An extension may set a command null to indicate that the click should not be handled + + continue; + } + + if (!mixer.lastClicked) { + mixer.lastClicked = button; + } + + mixitup.events.fire('mixClick', mixer.dom.container, { + state: mixer.state, + instance: mixer, + originalEvent: e, + control: mixer.lastClicked + }, mixer.dom.document); + + if (typeof mixer.config.callbacks.onMixClick === 'function') { + returnValue = mixer.config.callbacks.onMixClick.call(mixer.lastClicked, mixer.state, e, mixer); + + if (returnValue === false) { + // User has returned `false` from the callback, so do not handle click + + continue; + } + } + + if (self.type === 'toggle') { + isActive ? mixer.toggleOff(command.filter) : mixer.toggleOn(command.filter); + } else { + mixer.multimix(command); + } + } + + this.callActions('afterHandleClick', arguments); + }, + + /** + * @param {object} command + * @param {Array} toggleArray + * @return {void} + */ + + update: function(command, toggleArray) { + var self = this, + actions = new mixitup.CommandMultimix(); + + self.callActions('beforeUpdate', arguments); + + self.pending--; + + self.pending = Math.max(0, self.pending); + + if (self.pending > 0) return; + + if (self.status === 'live') { + // Live control (status unknown) + + self.updateLive(command, toggleArray); + } else { + // Static control + + actions.sort = self.sort; + actions.filter = self.filter; + + self.callFilters('actionsUpdate', actions, arguments); + + self.parseStatusChange(self.el, command, actions, toggleArray); + } + + self.callActions('afterUpdate', arguments); + }, + + /** + * @param {mixitup.CommandMultimix} command + * @param {Array} toggleArray + * @return {void} + */ + + updateLive: function(command, toggleArray) { + var self = this, + controlButtons = null, + actions = null, + button = null, + i = -1; + + self.callActions('beforeUpdateLive', arguments); + + if (!self.el) return; + + controlButtons = self.el.querySelectorAll(self.selector); + + for (i = 0; button = controlButtons[i]; i++) { + actions = new mixitup.CommandMultimix(); + + switch (self.type) { + case 'filter': + actions.filter = button.getAttribute('data-filter'); + + break; + case 'sort': + actions.sort = button.getAttribute('data-sort'); + + break; + case 'multimix': + actions.filter = button.getAttribute('data-filter'); + actions.sort = button.getAttribute('data-sort'); + + break; + case 'toggle': + actions.filter = button.getAttribute('data-toggle'); + + break; + } + + actions = self.callFilters('actionsUpdateLive', actions, arguments); + + self.parseStatusChange(button, command, actions, toggleArray); + } + + self.callActions('afterUpdateLive', arguments); + }, + + /** + * @param {HTMLElement} button + * @param {mixitup.CommandMultimix} command + * @param {mixitup.CommandMultimix} actions + * @param {Array} toggleArray + * @return {void} + */ + + parseStatusChange: function(button, command, actions, toggleArray) { + var self = this, + alias = '', + toggle = '', + i = -1; + + self.callActions('beforeParseStatusChange', arguments); + + switch (self.type) { + case 'filter': + if (command.filter === actions.filter) { + self.renderStatus(button, 'active'); + } else { + self.renderStatus(button, 'inactive'); + } + + break; + case 'multimix': + if (command.sort === actions.sort && command.filter === actions.filter) { + self.renderStatus(button, 'active'); + } else { + self.renderStatus(button, 'inactive'); + } + + break; + case 'sort': + if (command.sort.match(/:asc/g)) { + alias = command.sort.replace(/:asc/g, ''); + } + + if (command.sort === actions.sort || alias === actions.sort) { + self.renderStatus(button, 'active'); + } else { + self.renderStatus(button, 'inactive'); + } + + break; + case 'toggle': + if (toggleArray.length < 1) self.renderStatus(button, 'inactive'); + + if (command.filter === actions.filter) { + self.renderStatus(button, 'active'); + } + + for (i = 0; i < toggleArray.length; i++) { + toggle = toggleArray[i]; + + if (toggle === actions.filter) { + // Button matches one active toggle + + self.renderStatus(button, 'active'); + + break; + } + + self.renderStatus(button, 'inactive'); + } + + break; + } + + self.callActions('afterParseStatusChange', arguments); + }, + + /** + * @param {HTMLElement} button + * @param {string} status + * @return {void} + */ + + renderStatus: function(button, status) { + var self = this; + + self.callActions('beforeRenderStatus', arguments); + + switch (status) { + case 'active': + h.addClass(button, self.classNames.active); + h.removeClass(button, self.classNames.disabled); + + if (self.canDisable) self.el.disabled = false; + + break; + case 'inactive': + h.removeClass(button, self.classNames.active); + h.removeClass(button, self.classNames.disabled); + + if (self.canDisable) self.el.disabled = false; + + break; + case 'disabled': + if (self.canDisable) self.el.disabled = true; + + h.addClass(button, self.classNames.disabled); + h.removeClass(button, self.classNames.active); + + break; + } + + if (self.status !== 'live') { + // Update the control's status propery if not live + + self.status = status; + } + + self.callActions('afterRenderStatus', arguments); + } + }); + + mixitup.controls = []; + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.StyleData = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.x = 0; + this.y = 0; + this.top = 0; + this.right = 0; + this.bottom = 0; + this.left = 0; + this.width = 0; + this.height = 0; + this.marginRight = 0; + this.marginBottom = 0; + this.opacity = 0; + this.scale = new mixitup.TransformData(); + this.translateX = new mixitup.TransformData(); + this.translateY = new mixitup.TransformData(); + this.translateZ = new mixitup.TransformData(); + this.rotateX = new mixitup.TransformData(); + this.rotateY = new mixitup.TransformData(); + this.rotateZ = new mixitup.TransformData(); + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.StyleData); + + mixitup.StyleData.prototype = Object.create(mixitup.Base.prototype); + + mixitup.StyleData.prototype.constructor = mixitup.StyleData; + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.TransformData = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.value = 0; + this.unit = ''; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.TransformData); + + mixitup.TransformData.prototype = Object.create(mixitup.Base.prototype); + + mixitup.TransformData.prototype.constructor = mixitup.TransformData; + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.TransformDefaults = function() { + mixitup.StyleData.apply(this); + + this.callActions('beforeConstruct'); + + this.scale.value = 0.01; + this.scale.unit = ''; + + this.translateX.value = 20; + this.translateX.unit = 'px'; + + this.translateY.value = 20; + this.translateY.unit = 'px'; + + this.translateZ.value = 20; + this.translateZ.unit = 'px'; + + this.rotateX.value = 90; + this.rotateX.unit = 'deg'; + + this.rotateY.value = 90; + this.rotateY.unit = 'deg'; + + this.rotateX.value = 90; + this.rotateX.unit = 'deg'; + + this.rotateZ.value = 180; + this.rotateZ.unit = 'deg'; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.TransformDefaults); + + mixitup.TransformDefaults.prototype = Object.create(mixitup.StyleData.prototype); + + mixitup.TransformDefaults.prototype.constructor = mixitup.TransformDefaults; + + /** + * @private + * @static + * @since 3.0.0 + * @type {mixitup.TransformDefaults} + */ + + mixitup.transformDefaults = new mixitup.TransformDefaults(); + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.EventDetail = function() { + this.state = null; + this.futureState = null; + this.instance = null; + this.originalEvent = null; + }; + + /** + * The `mixitup.Events` class contains all custom events dispatched by MixItUp at various + * points within the lifecycle of a mixer operation. + * + * Each event is analogous to the callback function of the same name defined in + * the `callbacks` configuration object, and is triggered immediately before it. + * + * Events are always triggered from the container element on which MixItUp is instantiated + * upon. + * + * As with any event, registered event handlers receive the event object as a parameter + * which includes a `detail` property containting references to the current `state`, + * the `mixer` instance, and other event-specific properties described below. + * + * @constructor + * @namespace + * @memberof mixitup + * @public + * @since 3.0.0 + */ + + mixitup.Events = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A custom event triggered immediately after any MixItUp operation is requested + * and before animations have begun. + * + * The `mixStart` event also exposes a `futureState` property via the + * `event.detail` object, which represents the final state of the mixer once + * the requested operation has completed. + * + * @name mixStart + * @memberof mixitup.Events + * @static + * @type {CustomEvent} + */ + + this.mixStart = null; + + /** + * A custom event triggered when a MixItUp operation is requested while another + * operation is in progress, and the animation queue is full, or queueing + * is disabled. + * + * @name mixBusy + * @memberof mixitup.Events + * @static + * @type {CustomEvent} + */ + + this.mixBusy = null; + + /** + * A custom event triggered after any MixItUp operation has completed, and the + * state has been updated. + * + * @name mixEnd + * @memberof mixitup.Events + * @static + * @type {CustomEvent} + */ + + this.mixEnd = null; + + /** + * A custom event triggered whenever a filter operation "fails", i.e. no targets + * could be found matching the requested filter. + * + * @name mixFail + * @memberof mixitup.Events + * @static + * @type {CustomEvent} + */ + + this.mixFail = null; + + /** + * A custom event triggered whenever a MixItUp control is clicked, and before its + * respective operation is requested. + * + * This event also exposes an `originalEvent` property via the `event.detail` + * object, which holds a reference to the original click event. + * + * @name mixClick + * @memberof mixitup.Events + * @static + * @type {CustomEvent} + */ + + this.mixClick = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.Events); + + mixitup.Events.prototype = Object.create(mixitup.Base.prototype); + + mixitup.Events.prototype.constructor = mixitup.Events; + + /** + * @private + * @param {string} eventType + * @param {Element} el + * @param {object} detail + * @param {Document} [doc] + */ + + mixitup.Events.prototype.fire = function(eventType, el, detail, doc) { + var self = this, + event = null, + eventDetail = new mixitup.EventDetail(); + + self.callActions('beforeFire', arguments); + + if (typeof self[eventType] === 'undefined') { + throw new Error('Event type "' + eventType + '" not found.'); + } + + eventDetail.state = new mixitup.State(); + + h.extend(eventDetail.state, detail.state); + + if (detail.futureState) { + eventDetail.futureState = new mixitup.State(); + + h.extend(eventDetail.futureState, detail.futureState); + } + + eventDetail.instance = detail.instance; + + if (detail.originalEvent) { + eventDetail.originalEvent = detail.originalEvent; + } + + event = h.getCustomEvent(eventType, eventDetail, doc); + + self.callFilters('eventFire', event, arguments); + + el.dispatchEvent(event); + }; + + // Asign a singleton instance to `mixitup.events`: + + mixitup.events = new mixitup.Events(); + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.QueueItem = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.args = []; + this.instruction = null; + this.triggerElement = null; + this.deferred = null; + this.isToggling = false; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.QueueItem); + + mixitup.QueueItem.prototype = Object.create(mixitup.Base.prototype); + + mixitup.QueueItem.prototype.constructor = mixitup.QueueItem; + + /** + * The `mixitup.Mixer` class is used to hold discreet, user-configured + * instances of MixItUp on a provided container element. + * + * Mixer instances are returned whenever the `mixitup()` factory function is called, + * which expose a range of methods enabling API-based filtering, sorting, + * insertion, removal and more. + * + * @constructor + * @namespace + * @memberof mixitup + * @public + * @since 3.0.0 + */ + + mixitup.Mixer = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.config = new mixitup.Config(); + + this.id = ''; + + this.isBusy = false; + this.isToggling = false; + this.incPadding = true; + + this.controls = []; + this.targets = []; + this.origOrder = []; + this.cache = {}; + + this.toggleArray = []; + + this.targetsMoved = 0; + this.targetsImmovable = 0; + this.targetsBound = 0; + this.targetsDone = 0; + + this.staggerDuration = 0; + this.effectsIn = null; + this.effectsOut = null; + this.transformIn = []; + this.transformOut = []; + this.queue = []; + + this.state = null; + this.lastOperation = null; + this.lastClicked = null; + this.userCallback = null; + this.userDeferred = null; + + this.dom = new mixitup.MixerDom(); + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.Mixer); + + mixitup.Mixer.prototype = Object.create(mixitup.Base.prototype); + + h.extend(mixitup.Mixer.prototype, + /** @lends mixitup.Mixer */ + { + constructor: mixitup.Mixer, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {HTMLElement} container + * @param {HTMLElement} document + * @param {string} id + * @param {object} [config] + */ + + attach: function(container, document, id, config) { + var self = this, + target = null, + i = -1; + + self.callActions('beforeAttach', arguments); + + self.id = id; + + if (config) { + h.extend(self.config, config, true, true); + } + + self.sanitizeConfig(); + + self.cacheDom(container, document); + + if (self.config.layout.containerClassName) { + h.addClass(self.dom.container, self.config.layout.containerClassName); + } + + if (!mixitup.features.has.transitions) { + self.config.animation.enable = false; + } + + if (typeof window.console === 'undefined') { + self.config.debug.showWarnings = false; + } + + if (self.config.data.uidKey) { + // If the dataset API is in use, force disable controls + + self.config.controls.enable = false; + } + + self.indexTargets(); + + self.state = self.getInitialState(); + + for (i = 0; target = self.lastOperation.toHide[i]; i++) { + target.hide(); + } + + if (self.config.controls.enable) { + self.initControls(); + + self.buildToggleArray(null, self.state); + + self.updateControls({ + filter: self.state.activeFilter, + sort: self.state.activeSort + }); + } + + self.parseEffects(); + + self.callActions('afterAttach', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @return {void} + */ + + sanitizeConfig: function() { + var self = this; + + self.callActions('beforeSanitizeConfig', arguments); + + // Sanitize enum/string config options + + self.config.controls.scope = self.config.controls.scope.toLowerCase().trim(); + self.config.controls.toggleLogic = self.config.controls.toggleLogic.toLowerCase().trim(); + self.config.controls.toggleDefault = self.config.controls.toggleDefault.toLowerCase().trim(); + + self.config.animation.effects = self.config.animation.effects.trim(); + + self.callActions('afterSanitizeConfig', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @return {mixitup.State} + */ + + getInitialState: function() { + var self = this, + state = new mixitup.State(), + operation = new mixitup.Operation(); + + self.callActions('beforeGetInitialState', arguments); + + // Map initial values into a mock state object in order to construct an operation + + state.activeContainerClassName = self.config.layout.containerClassName; + + if (self.config.load.dataset) { + // Dataset API + + if (!self.config.data.uidKey || typeof self.config.data.uidKey !== 'string') { + throw new TypeError(mixitup.messages.errorConfigDataUidKeyNotSet()); + } + + operation.startDataset = operation.newDataset = state.activeDataset = self.config.load.dataset.slice(); + operation.startContainerClassName = operation.newContainerClassName = state.activeContainerClassName; + operation.show = self.targets.slice(); + + state = self.callFilters('stateGetInitialState', state, arguments); + } else { + // DOM API + + state.activeFilter = self.parseFilterArgs([self.config.load.filter]).command; + state.activeSort = self.parseSortArgs([self.config.load.sort]).command; + state.totalTargets = self.targets.length; + + state = self.callFilters('stateGetInitialState', state, arguments); + + if ( + state.activeSort.collection || state.activeSort.attribute || + state.activeSort.order === 'random' || state.activeSort.order === 'desc' + ) { + // Sorting on load + + operation.newSort = state.activeSort; + + self.sortOperation(operation); + + self.printSort(false, operation); + + self.targets = operation.newOrder; + } else { + operation.startOrder = operation.newOrder = self.targets; + } + + operation.startFilter = operation.newFilter = state.activeFilter; + operation.startSort = operation.newSort = state.activeSort; + operation.startContainerClassName = operation.newContainerClassName = state.activeContainerClassName; + + if (operation.newFilter.selector === 'all') { + operation.newFilter.selector = self.config.selectors.target; + } else if (operation.newFilter.selector === 'none') { + operation.newFilter.selector = ''; + } + } + + operation = self.callFilters('operationGetInitialState', operation, [state]); + + self.lastOperation = operation; + + if (operation.newFilter) { + self.filterOperation(operation); + } + + state = self.buildState(operation); + + return state; + }, + + /** + * Caches references of DOM elements neccessary for the mixer's functionality. + * + * @private + * @instance + * @since 3.0.0 + * @param {HTMLElement} el + * @param {HTMLHtmlElement} document + * @return {void} + */ + + cacheDom: function(el, document) { + var self = this; + + self.callActions('beforeCacheDom', arguments); + + self.dom.document = document; + self.dom.body = self.dom.document.querySelector('body'); + self.dom.container = el; + self.dom.parent = el; + + self.callActions('afterCacheDom', arguments); + }, + + /** + * Indexes all child elements of the mixer matching the `selectors.target` + * selector, instantiating a mixitup.Target for each one. + * + * @private + * @instance + * @since 3.0.0 + * @return {void} + */ + + indexTargets: function() { + var self = this, + target = null, + el = null, + dataset = null, + i = -1; + + self.callActions('beforeIndexTargets', arguments); + + self.dom.targets = self.config.layout.allowNestedTargets ? + self.dom.container.querySelectorAll(self.config.selectors.target) : + h.children(self.dom.container, self.config.selectors.target, self.dom.document); + + self.dom.targets = h.arrayFromList(self.dom.targets); + + self.targets = []; + + if ((dataset = self.config.load.dataset) && dataset.length !== self.dom.targets.length) { + throw new Error(mixitup.messages.errorDatasetPrerenderedMismatch()); + } + + if (self.dom.targets.length) { + for (i = 0; el = self.dom.targets[i]; i++) { + target = new mixitup.Target(); + + target.init(el, self, dataset ? dataset[i] : void(0)); + + target.isInDom = true; + + self.targets.push(target); + } + + self.dom.parent = self.dom.targets[0].parentElement === self.dom.container ? + self.dom.container : + self.dom.targets[0].parentElement; + } + + self.origOrder = self.targets; + + self.callActions('afterIndexTargets', arguments); + }, + + initControls: function() { + var self = this, + definition = '', + controlElements = null, + el = null, + parent = null, + delagators = null, + control = null, + i = -1, + j = -1; + + self.callActions('beforeInitControls', arguments); + + switch (self.config.controls.scope) { + case 'local': + parent = self.dom.container; + + break; + case 'global': + parent = self.dom.document; + + break; + default: + throw new Error(mixitup.messages.errorConfigInvalidControlsScope()); + } + + for (i = 0; definition = mixitup.controlDefinitions[i]; i++) { + if (self.config.controls.live || definition.live) { + if (definition.parent) { + delagators = self.dom[definition.parent]; + + if (!delagators || delagators.length < 0) continue; + + if (typeof delagators.length !== 'number') { + delagators = [delagators]; + } + } else { + delagators = [parent]; + } + + for (j = 0; (el = delagators[j]); j++) { + control = self.getControl(el, definition.type, definition.selector); + + self.controls.push(control); + } + } else { + controlElements = parent.querySelectorAll(self.config.selectors.control + definition.selector); + + for (j = 0; (el = controlElements[j]); j++) { + control = self.getControl(el, definition.type, ''); + + if (!control) continue; + + self.controls.push(control); + } + } + } + + self.callActions('afterInitControls', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {HTMLElement} el + * @param {string} type + * @param {string} selector + * @return {mixitup.Control|null} + */ + + getControl: function(el, type, selector) { + var self = this, + control = null, + i = -1; + + self.callActions('beforeGetControl', arguments); + + if (!selector) { + // Static controls only + + for (i = 0; control = mixitup.controls[i]; i++) { + if (control.el === el && control.isBound(self)) { + // Control already bound to this mixer (as another type). + + // NB: This prevents duplicate controls from being registered where a selector + // might collide, eg: "[data-filter]" and "[data-filter][data-sort]" + + return self.callFilters('controlGetControl', null, arguments); + } else if (control.el === el && control.type === type && control.selector === selector) { + // Another mixer is already using this control, add this mixer as a binding + + control.addBinding(self); + + return self.callFilters('controlGetControl', control, arguments); + } + } + } + + // Create new control + + control = new mixitup.Control(); + + control.init(el, type, selector); + + control.classNames.base = h.getClassname(self.config.classNames, type); + control.classNames.active = h.getClassname(self.config.classNames, type, self.config.classNames.modifierActive); + control.classNames.disabled = h.getClassname(self.config.classNames, type, self.config.classNames.modifierDisabled); + + // Add a reference to this mixer as a binding + + control.addBinding(self); + + return self.callFilters('controlGetControl', control, arguments); + }, + + /** + * Creates a compound selector by joining the `toggleArray` value as per the + * defined toggle logic. + * + * @private + * @instance + * @since 3.0.0 + * @return {string} + */ + + getToggleSelector: function() { + var self = this, + delineator = self.config.controls.toggleLogic === 'or' ? ', ' : '', + toggleSelector = ''; + + self.callActions('beforeGetToggleSelector', arguments); + + self.toggleArray = h.clean(self.toggleArray); + + toggleSelector = self.toggleArray.join(delineator); + + if (toggleSelector === '') { + toggleSelector = self.config.controls.toggleDefault; + } + + return self.callFilters('selectorGetToggleSelector', toggleSelector, arguments); + }, + + /** + * Breaks compound selector strings in an array of discreet selectors, + * as per the active `controls.toggleLogic` configuration option. Accepts + * either a dynamic command object, or a state object. + * + * @private + * @instance + * @since 2.0.0 + * @param {object} [command] + * @param {mixitup.State} [state] + * @return {void} + */ + + buildToggleArray: function(command, state) { + var self = this, + activeFilterSelector = ''; + + self.callActions('beforeBuildToggleArray', arguments); + + if (command && command.filter) { + activeFilterSelector = command.filter.selector.replace(/\s/g, ''); + } else if (state) { + activeFilterSelector = state.activeFilter.selector.replace(/\s/g, ''); + } else { + return; + } + + if (activeFilterSelector === self.config.selectors.target || activeFilterSelector === 'all') { + activeFilterSelector = ''; + } + + if (self.config.controls.toggleLogic === 'or') { + self.toggleArray = activeFilterSelector.split(','); + } else { + self.toggleArray = self.splitCompoundSelector(activeFilterSelector); + } + + self.toggleArray = h.clean(self.toggleArray); + + self.callActions('afterBuildToggleArray', arguments); + }, + + /** + * Takes a compound selector (e.g. `.cat-1.cat-2`, `[data-cat="1"][data-cat="2"]`) + * and breaks into its individual selectors. + * + * @private + * @instance + * @since 3.0.0 + * @param {string} compoundSelector + * @return {string[]} + */ + + splitCompoundSelector: function(compoundSelector) { + // Break at a `.` or `[`, capturing the delineator + + var partials = compoundSelector.split(/([\.\[])/g), + toggleArray = [], + selector = '', + i = -1; + + if (partials[0] === '') { + partials.shift(); + } + + for (i = 0; i < partials.length; i++) { + if (i % 2 === 0) { + selector = ''; + } + + selector += partials[i]; + + if (i % 2 !== 0) { + toggleArray.push(selector); + } + } + + return toggleArray; + }, + + /** + * Updates controls to their active/inactive state based on the command or + * current state of the mixer. + * + * @private + * @instance + * @since 2.0.0 + * @param {object} command + * @return {void} + */ + + updateControls: function(command) { + var self = this, + control = null, + output = new mixitup.CommandMultimix(), + i = -1; + + self.callActions('beforeUpdateControls', arguments); + + // Sanitise to defaults + + if (command.filter) { + output.filter = command.filter.selector; + } else { + output.filter = self.state.activeFilter.selector; + } + + if (command.sort) { + output.sort = self.buildSortString(command.sort); + } else { + output.sort = self.buildSortString(self.state.activeSort); + } + + if (output.filter === self.config.selectors.target) { + output.filter = 'all'; + } + + if (output.filter === '') { + output.filter = 'none'; + } + + h.freeze(output); + + for (i = 0; control = self.controls[i]; i++) { + control.update(output, self.toggleArray); + } + + self.callActions('afterUpdateControls', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {mixitup.CommandSort} command + * @return {string} + */ + + buildSortString: function(command) { + var self = this; + var output = ''; + + output += command.sortString; + + if (command.next) { + output += ' ' + self.buildSortString(command.next); + } + + return output; + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {object} command + * @param {Operation} operation + * @return {Promise.} + */ + + insertTargets: function(command, operation) { + var self = this, + nextSibling = null, + insertionIndex = -1, + frag = null, + target = null, + el = null, + i = -1; + + self.callActions('beforeInsertTargets', arguments); + + if (typeof command.index === 'undefined') command.index = 0; + + nextSibling = self.getNextSibling(command.index, command.sibling, command.position); + frag = self.dom.document.createDocumentFragment(); + + if (nextSibling) { + insertionIndex = h.index(nextSibling, self.config.selectors.target); + } else { + insertionIndex = self.targets.length; + } + + if (command.collection) { + for (i = 0; el = command.collection[i]; i++) { + if (self.dom.targets.indexOf(el) > -1) { + throw new Error(mixitup.messages.errorInsertPreexistingElement()); + } + + // Ensure elements are hidden when they are added to the DOM, so they can + // be animated in gracefully + + el.style.display = 'none'; + + frag.appendChild(el); + frag.appendChild(self.dom.document.createTextNode(' ')); + + if (!h.isElement(el, self.dom.document) || !el.matches(self.config.selectors.target)) continue; + + target = new mixitup.Target(); + + target.init(el, self); + + target.isInDom = true; + + self.targets.splice(insertionIndex, 0, target); + + insertionIndex++; + } + + self.dom.parent.insertBefore(frag, nextSibling); + } + + // Since targets have been added, the original order must be updated + + operation.startOrder = self.origOrder = self.targets; + + self.callActions('afterInsertTargets', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Number} [index] + * @param {Element} [sibling] + * @param {string} [position] + * @return {Element} + */ + + getNextSibling: function(index, sibling, position) { + var self = this, + element = null; + + index = Math.max(index, 0); + + if (sibling && position === 'before') { + // Explicit sibling + + element = sibling; + } else if (sibling && position === 'after') { + // Explicit sibling + + element = sibling.nextElementSibling || null; + } else if (self.targets.length > 0 && typeof index !== 'undefined') { + // Index and targets exist + + element = (index < self.targets.length || !self.targets.length) ? + self.targets[index].dom.el : + self.targets[self.targets.length - 1].dom.el.nextElementSibling; + } else if (self.targets.length === 0 && self.dom.parent.children.length > 0) { + // No targets but other siblings + + if (self.config.layout.siblingAfter) { + element = self.config.layout.siblingAfter; + } else if (self.config.layout.siblingBefore) { + element = self.config.layout.siblingBefore.nextElementSibling; + } else { + self.dom.parent.children[0]; + } + } else { + element === null; + } + + return self.callFilters('elementGetNextSibling', element, arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + filterOperation: function(operation) { + var self = this, + testResult = false, + index = -1, + action = '', + target = null, + i = -1; + + self.callActions('beforeFilterOperation', arguments); + + action = operation.newFilter.action; + + for (i = 0; target = operation.newOrder[i]; i++) { + if (operation.newFilter.collection) { + // show via collection + + testResult = operation.newFilter.collection.indexOf(target.dom.el) > -1; + } else { + // show via selector + + if (operation.newFilter.selector === '') { + testResult = false; + } else { + testResult = target.dom.el.matches(operation.newFilter.selector); + } + } + + self.evaluateHideShow(testResult, target, action, operation); + } + + if (operation.toRemove.length) { + for (i = 0; target = operation.show[i]; i++) { + if (operation.toRemove.indexOf(target) > -1) { + // If any shown targets should be removed, move them into the toHide array + + operation.show.splice(i, 1); + + if ((index = operation.toShow.indexOf(target)) > -1) { + operation.toShow.splice(index, 1); + } + + operation.toHide.push(target); + operation.hide.push(target); + + i--; + } + } + } + + operation.matching = operation.show.slice(); + + if (operation.show.length === 0 && operation.newFilter.selector !== '' && self.targets.length !== 0) { + operation.hasFailed = true; + } + + self.callActions('afterFilterOperation', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {boolean} testResult + * @param {Element} target + * @param {string} action + * @param {Operation} operation + * @return {void} + */ + + evaluateHideShow: function(testResult, target, action, operation) { + var self = this, + filteredTestResult = false, + args = Array.prototype.slice.call(arguments, 1); + + filteredTestResult = self.callFilters('testResultEvaluateHideShow', testResult, args); + + self.callActions('beforeEvaluateHideShow', arguments); + + if ( + filteredTestResult === true && action === 'show' || + filteredTestResult === false && action === 'hide' + ) { + operation.show.push(target); + + !target.isShown && operation.toShow.push(target); + } else { + operation.hide.push(target); + + target.isShown && operation.toHide.push(target); + } + + self.callActions('afterEvaluateHideShow', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + sortOperation: function(operation) { + var self = this, + newOrder = [], + target = null, + el = null, + i = -1; + + self.callActions('beforeSortOperation', arguments); + + operation.startOrder = self.targets; + + if (operation.newSort.collection) { + // Sort by collection + + newOrder = []; + + for (i = 0; (el = operation.newSort.collection[i]); i++) { + if (self.dom.targets.indexOf(el) < 0) { + throw new Error(mixitup.messages.errorSortNonExistentElement()); + } + + target = new mixitup.Target(); + + target.init(el, self); + + target.isInDom = true; + + newOrder.push(target); + } + + operation.newOrder = newOrder; + } else if (operation.newSort.order === 'random') { + // Sort random + + operation.newOrder = h.arrayShuffle(operation.startOrder); + } else if (operation.newSort.attribute === '') { + // Sort by default + + operation.newOrder = self.origOrder.slice(); + + if (operation.newSort.order === 'desc') { + operation.newOrder.reverse(); + } + } else { + // Sort by attribute + + operation.newOrder = operation.startOrder.slice(); + + operation.newOrder.sort(function(a, b) { + return self.compare(a, b, operation.newSort); + }); + } + + if (h.isEqualArray(operation.newOrder, operation.startOrder)) { + operation.willSort = false; + } + + self.callActions('afterSortOperation', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {mixitup.Target} a + * @param {mixitup.Target} b + * @param {mixitup.CommandSort} command + * @return {Number} + */ + + compare: function(a, b, command) { + var self = this, + order = command.order, + attrA = self.getAttributeValue(a, command.attribute), + attrB = self.getAttributeValue(b, command.attribute); + + if (isNaN(attrA * 1) || isNaN(attrB * 1)) { + attrA = attrA.toLowerCase(); + attrB = attrB.toLowerCase(); + } else { + attrA = attrA * 1; + attrB = attrB * 1; + } + + if (attrA < attrB) { + return order === 'asc' ? -1 : 1; + } + + if (attrA > attrB) { + return order === 'asc' ? 1 : -1; + } + + if (attrA === attrB && command.next) { + return self.compare(a, b, command.next); + } + + return 0; + }, + + /** + * Reads the values of any data attributes present the provided target element + * which match the current sort command. + * + * @private + * @instance + * @since 3.0.0 + * @param {mixitup.Target} target + * @param {string} [attribute] + * @return {(String|Number)} + */ + + getAttributeValue: function(target, attribute) { + var self = this, + value = ''; + + value = target.dom.el.getAttribute('data-' + attribute); + + if (value === null) { + if (self.config.debug.showWarnings) { + // Encourage users to assign values to all targets to avoid erroneous sorting + // when types are mixed + + console.warn(mixitup.messages.warningInconsistentSortingAttributes({ + attribute: 'data-' + attribute + })); + } + } + + // If an attribute is not present, return 0 as a safety value + + return self.callFilters('valueGetAttributeValue', value || 0, arguments); + }, + + /** + * Inserts elements into the DOM in the appropriate + * order using a document fragment for minimal + * DOM thrashing + * + * @private + * @instance + * @since 2.0.0 + * @param {boolean} isResetting + * @param {Operation} operation + * @return {void} + */ + + printSort: function(isResetting, operation) { + var self = this, + startOrder = isResetting ? operation.newOrder : operation.startOrder, + newOrder = isResetting ? operation.startOrder : operation.newOrder, + nextSibling = startOrder.length ? startOrder[startOrder.length - 1].dom.el.nextElementSibling : null, + frag = window.document.createDocumentFragment(), + whitespace = null, + target = null, + el = null, + i = -1; + + self.callActions('beforePrintSort', arguments); + + // Empty the container + + for (i = 0; target = startOrder[i]; i++) { + el = target.dom.el; + + if (el.style.position === 'absolute') continue; + + h.removeWhitespace(el.previousSibling); + + el.parentElement.removeChild(el); + } + + whitespace = nextSibling ? nextSibling.previousSibling : self.dom.parent.lastChild; + + if (whitespace && whitespace.nodeName === '#text') { + h.removeWhitespace(whitespace); + } + + for (i = 0; target = newOrder[i]; i++) { + // Add targets into a document fragment + + el = target.dom.el; + + if (h.isElement(frag.lastChild)) { + frag.appendChild(window.document.createTextNode(' ')); + } + + frag.appendChild(el); + } + + // Insert the document fragment into the container + // before any other non-target elements + + if (self.dom.parent.firstChild && self.dom.parent.firstChild !== nextSibling) { + frag.insertBefore(window.document.createTextNode(' '), frag.childNodes[0]); + } + + if (nextSibling) { + frag.appendChild(window.document.createTextNode(' ')); + + self.dom.parent.insertBefore(frag, nextSibling); + } else { + self.dom.parent.appendChild(frag); + } + + self.callActions('afterPrintSort', arguments); + }, + + /** + * Parses user-defined sort strings (i.e. `default:asc`) into sort commands objects. + * + * @private + * @instance + * @since 3.0.0 + * @param {string} sortString + * @param {mixitup.CommandSort} command + * @return {mixitup.CommandSort} + */ + + parseSortString: function(sortString, command) { + var self = this, + rules = sortString.split(' '), + current = command, + rule = [], + i = -1; + + // command.sortString = sortString; + + for (i = 0; i < rules.length; i++) { + rule = rules[i].split(':'); + + current.sortString = rules[i]; + current.attribute = h.dashCase(rule[0]); + current.order = rule[1] || 'asc'; + + switch (current.attribute) { + case 'default': + // treat "default" as sorting by no attribute + + current.attribute = ''; + + break; + case 'random': + // treat "random" as an order not an attribute + + current.attribute = ''; + current.order = 'random'; + + break; + } + + if (!current.attribute || current.order === 'random') break; + + if (i < rules.length - 1) { + // Embed reference to the next command + + current.next = new mixitup.CommandSort(); + + h.freeze(current); + + current = current.next; + } + } + + return self.callFilters('commandsParseSort', command, arguments); + }, + + /** + * Parses all effects out of the user-defined `animation.effects` string into + * their respective properties and units. + * + * @private + * @instance + * @since 2.0.0 + * @return {void} + */ + + parseEffects: function() { + var self = this, + transformName = '', + effectsIn = self.config.animation.effectsIn || self.config.animation.effects, + effectsOut = self.config.animation.effectsOut || self.config.animation.effects; + + self.callActions('beforeParseEffects', arguments); + + self.effectsIn = new mixitup.StyleData(); + self.effectsOut = new mixitup.StyleData(); + self.transformIn = []; + self.transformOut = []; + + self.effectsIn.opacity = self.effectsOut.opacity = 1; + + self.parseEffect('fade', effectsIn, self.effectsIn, self.transformIn); + self.parseEffect('fade', effectsOut, self.effectsOut, self.transformOut, true); + + for (transformName in mixitup.transformDefaults) { + if (!(mixitup.transformDefaults[transformName] instanceof mixitup.TransformData)) { + continue; + } + + self.parseEffect(transformName, effectsIn, self.effectsIn, self.transformIn); + self.parseEffect(transformName, effectsOut, self.effectsOut, self.transformOut, true); + } + + self.parseEffect('stagger', effectsIn, self.effectsIn, self.transformIn); + self.parseEffect('stagger', effectsOut, self.effectsOut, self.transformOut, true); + + self.callActions('afterParseEffects', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {string} effectName + * @param {string} effectString + * @param {StyleData} effects + * @param {String[]} transform + * @param {boolean} [isOut] + */ + + parseEffect: function(effectName, effectString, effects, transform, isOut) { + var self = this, + re = /\(([^)]+)\)/, + propIndex = -1, + str = '', + match = [], + val = '', + units = ['%', 'px', 'em', 'rem', 'vh', 'vw', 'deg'], + unit = '', + i = -1; + + self.callActions('beforeParseEffect', arguments); + + if (typeof effectString !== 'string') { + throw new TypeError(mixitup.messages.errorConfigInvalidAnimationEffects()); + } + + if (effectString.indexOf(effectName) < 0) { + // The effect is not present in the effects string + + if (effectName === 'stagger') { + // Reset stagger to 0 + + self.staggerDuration = 0; + } + + return; + } + + // The effect is present + + propIndex = effectString.indexOf(effectName + '('); + + if (propIndex > -1) { + // The effect has a user defined value in parentheses + + // Extract from the first parenthesis to the end of string + + str = effectString.substring(propIndex); + + // Match any number of characters between "(" and ")" + + match = re.exec(str); + + val = match[1]; + } + + switch (effectName) { + case 'fade': + effects.opacity = val ? parseFloat(val) : 0; + + break; + case 'stagger': + self.staggerDuration = val ? parseFloat(val) : 100; + + // TODO: Currently stagger must be applied globally, but + // if seperate values are specified for in/out, this should + // be respected + + break; + default: + // All other effects are transforms following the same structure + + if (isOut && self.config.animation.reverseOut && effectName !== 'scale') { + effects[effectName].value = + (val ? parseFloat(val) : mixitup.transformDefaults[effectName].value) * -1; + } else { + effects[effectName].value = + (val ? parseFloat(val) : mixitup.transformDefaults[effectName].value); + } + + if (val) { + for (i = 0; unit = units[i]; i++) { + if (val.indexOf(unit) > -1) { + effects[effectName].unit = unit; + + break; + } + } + } else { + effects[effectName].unit = mixitup.transformDefaults[effectName].unit; + } + + transform.push( + effectName + + '(' + + effects[effectName].value + + effects[effectName].unit + + ')' + ); + } + + self.callActions('afterParseEffect', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {State} + */ + + buildState: function(operation) { + var self = this, + state = new mixitup.State(), + target = null, + i = -1; + + self.callActions('beforeBuildState', arguments); + + // Map target elements into state arrays. + // the real target objects should never be exposed + + for (i = 0; target = self.targets[i]; i++) { + if (!operation.toRemove.length || operation.toRemove.indexOf(target) < 0) { + state.targets.push(target.dom.el); + } + } + + for (i = 0; target = operation.matching[i]; i++) { + state.matching.push(target.dom.el); + } + + for (i = 0; target = operation.show[i]; i++) { + state.show.push(target.dom.el); + } + + for (i = 0; target = operation.hide[i]; i++) { + if (!operation.toRemove.length || operation.toRemove.indexOf(target) < 0) { + state.hide.push(target.dom.el); + } + } + + state.id = self.id; + state.container = self.dom.container; + state.activeFilter = operation.newFilter; + state.activeSort = operation.newSort; + state.activeDataset = operation.newDataset; + state.activeContainerClassName = operation.newContainerClassName; + state.hasFailed = operation.hasFailed; + state.totalTargets = self.targets.length; + state.totalShow = operation.show.length; + state.totalHide = operation.hide.length; + state.totalMatching = operation.matching.length; + state.triggerElement = operation.triggerElement; + + return self.callFilters('stateBuildState', state, arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {boolean} shouldAnimate + * @param {Operation} operation + * @return {void} + */ + + goMix: function(shouldAnimate, operation) { + var self = this, + deferred = null; + + self.callActions('beforeGoMix', arguments); + + // If the animation duration is set to 0ms, + // or no effects specified, + // or the container is hidden + // then abort animation + + if ( + !self.config.animation.duration || !self.config.animation.effects || !h.isVisible(self.dom.container) + ) { + shouldAnimate = false; + } + + if ( + !operation.toShow.length && + !operation.toHide.length && + !operation.willSort && + !operation.willChangeLayout + ) { + // If nothing to show or hide, and not sorting or + // changing layout + + shouldAnimate = false; + } + + if ( + !operation.startState.show.length && + !operation.show.length + ) { + // If nothing currently shown, nothing to show + + shouldAnimate = false; + } + + mixitup.events.fire('mixStart', self.dom.container, { + state: operation.startState, + futureState: operation.newState, + instance: self + }, self.dom.document); + + if (typeof self.config.callbacks.onMixStart === 'function') { + self.config.callbacks.onMixStart.call( + self.dom.container, + operation.startState, + operation.newState, + self + ); + } + + h.removeClass(self.dom.container, h.getClassname(self.config.classNames, 'container', self.config.classNames.modifierFailed)); + + if (!self.userDeferred) { + // Queue empty, no pending operations + + deferred = self.userDeferred = h.defer(mixitup.libraries); + } else { + // Use existing deferred + + deferred = self.userDeferred; + } + + self.isBusy = true; + + if (!shouldAnimate || !mixitup.features.has.transitions) { + // Abort + + if (self.config.debug.fauxAsync) { + setTimeout(function() { + self.cleanUp(operation); + }, self.config.animation.duration); + } else { + self.cleanUp(operation); + } + + return self.callFilters('promiseGoMix', deferred.promise, arguments); + } + + // If we should animate and the platform supports transitions, go for it + + if (window.pageYOffset !== operation.docState.scrollTop) { + window.scrollTo(operation.docState.scrollLeft, operation.docState.scrollTop); + } + + if (self.config.animation.applyPerspective) { + self.dom.parent.style[mixitup.features.perspectiveProp] = + self.config.animation.perspectiveDistance; + + self.dom.parent.style[mixitup.features.perspectiveOriginProp] = + self.config.animation.perspectiveOrigin; + } + + if ( + self.config.animation.animateResizeContainer && + operation.startHeight !== operation.newHeight && + operation.viewportDeltaY !== operation.startHeight - operation.newHeight + ) { + self.dom.parent.style.height = operation.startHeight + 'px'; + } + + if ( + self.config.animation.animateResizeContainer && + operation.startWidth !== operation.newWidth && + operation.viewportDeltaX !== operation.startWidth - operation.newWidth + ) { + self.dom.parent.style.width = operation.startWidth + 'px'; + } + + if (operation.startHeight === operation.newHeight) { + self.dom.parent.style.height = operation.startHeight + 'px'; + } + + if (operation.startWidth === operation.newWidth) { + self.dom.parent.style.width = operation.startWidth + 'px'; + } + + if (operation.startHeight === operation.newHeight && operation.startWidth === operation.newWidth) { + self.dom.parent.style.overflow = 'hidden'; + } + + requestAnimationFrame(function() { + self.moveTargets(operation); + }); + + return self.callFilters('promiseGoMix', deferred.promise, arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + getStartMixData: function(operation) { + var self = this, + parentStyle = window.getComputedStyle(self.dom.parent), + parentRect = self.dom.parent.getBoundingClientRect(), + target = null, + data = {}, + i = -1, + boxSizing = parentStyle[mixitup.features.boxSizingProp]; + + self.incPadding = (boxSizing === 'border-box'); + + self.callActions('beforeGetStartMixData', arguments); + + for (i = 0; target = operation.show[i]; i++) { + data = target.getPosData(); + + operation.showPosData[i] = { + startPosData: data + }; + } + + for (i = 0; target = operation.toHide[i]; i++) { + data = target.getPosData(); + + operation.toHidePosData[i] = { + startPosData: data + }; + } + + operation.startX = parentRect.left; + operation.startY = parentRect.top; + + operation.startHeight = self.incPadding ? + parentRect.height : + parentRect.height - + parseFloat(parentStyle.paddingTop) - + parseFloat(parentStyle.paddingBottom) - + parseFloat(parentStyle.borderTop) - + parseFloat(parentStyle.borderBottom); + + operation.startWidth = self.incPadding ? + parentRect.width : + parentRect.width - + parseFloat(parentStyle.paddingLeft) - + parseFloat(parentStyle.paddingRight) - + parseFloat(parentStyle.borderLeft) - + parseFloat(parentStyle.borderRight); + + self.callActions('afterGetStartMixData', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + setInter: function(operation) { + var self = this, + target = null, + i = -1; + + self.callActions('beforeSetInter', arguments); + + // Prevent scrollbar flicker on non-inertial scroll platforms by clamping height/width + + if (self.config.animation.clampHeight) { + self.dom.parent.style.height = operation.startHeight + 'px'; + self.dom.parent.style.overflow = 'hidden'; + } + + if (self.config.animation.clampWidth) { + self.dom.parent.style.width = operation.startWidth + 'px'; + self.dom.parent.style.overflow = 'hidden'; + } + + for (i = 0; target = operation.toShow[i]; i++) { + target.show(); + } + + if (operation.willChangeLayout) { + h.removeClass(self.dom.container, operation.startContainerClassName); + h.addClass(self.dom.container, operation.newContainerClassName); + } + + self.callActions('afterSetInter', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + getInterMixData: function(operation) { + var self = this, + target = null, + i = -1; + + self.callActions('beforeGetInterMixData', arguments); + + for (i = 0; target = operation.show[i]; i++) { + operation.showPosData[i].interPosData = target.getPosData(); + } + + for (i = 0; target = operation.toHide[i]; i++) { + operation.toHidePosData[i].interPosData = target.getPosData(); + } + + self.callActions('afterGetInterMixData', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + setFinal: function(operation) { + var self = this, + target = null, + i = -1; + + self.callActions('beforeSetFinal', arguments); + + operation.willSort && self.printSort(false, operation); + + for (i = 0; target = operation.toHide[i]; i++) { + target.hide(); + } + + self.callActions('afterSetFinal', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + getFinalMixData: function(operation) { + var self = this, + parentStyle = null, + parentRect = null, + target = null, + i = -1; + + self.callActions('beforeGetFinalMixData', arguments); + + for (i = 0; target = operation.show[i]; i++) { + operation.showPosData[i].finalPosData = target.getPosData(); + } + + for (i = 0; target = operation.toHide[i]; i++) { + operation.toHidePosData[i].finalPosData = target.getPosData(); + } + + // Remove clamping + + if (self.config.animation.clampHeight || self.config.animation.clampWidth) { + self.dom.parent.style.height = + self.dom.parent.style.width = + self.dom.parent.style.overflow = ''; + } + + if (!self.incPadding) { + parentStyle = window.getComputedStyle(self.dom.parent); + } + + parentRect = self.dom.parent.getBoundingClientRect(); + + operation.newX = parentRect.left; + operation.newY = parentRect.top; + + operation.newHeight = self.incPadding ? + parentRect.height : + parentRect.height - + parseFloat(parentStyle.paddingTop) - + parseFloat(parentStyle.paddingBottom) - + parseFloat(parentStyle.borderTop) - + parseFloat(parentStyle.borderBottom); + + operation.newWidth = self.incPadding ? + parentRect.width : + parentRect.width - + parseFloat(parentStyle.paddingLeft) - + parseFloat(parentStyle.paddingRight) - + parseFloat(parentStyle.borderLeft) - + parseFloat(parentStyle.borderRight); + + operation.viewportDeltaX = operation.docState.viewportWidth - this.dom.document.documentElement.clientWidth; + operation.viewportDeltaY = operation.docState.viewportHeight - this.dom.document.documentElement.clientHeight; + + if (operation.willSort) { + self.printSort(true, operation); + } + + for (i = 0; target = operation.toShow[i]; i++) { + target.hide(); + } + + for (i = 0; target = operation.toHide[i]; i++) { + target.show(); + } + + if (operation.willChangeLayout) { + h.removeClass(self.dom.container, operation.newContainerClassName); + h.addClass(self.dom.container, self.config.layout.containerClassName); + } + + self.callActions('afterGetFinalMixData', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Operation} operation + */ + + getTweenData: function(operation) { + var self = this, + target = null, + posData = null, + effectNames = Object.getOwnPropertyNames(self.effectsIn), + effectName = '', + effect = null, + widthChange = -1, + heightChange = -1, + i = -1, + j = -1; + + self.callActions('beforeGetTweenData', arguments); + + for (i = 0; target = operation.show[i]; i++) { + posData = operation.showPosData[i]; + posData.posIn = new mixitup.StyleData(); + posData.posOut = new mixitup.StyleData(); + posData.tweenData = new mixitup.StyleData(); + + // Process x and y + + if (target.isShown) { + posData.posIn.x = posData.startPosData.x - posData.interPosData.x; + posData.posIn.y = posData.startPosData.y - posData.interPosData.y; + } else { + posData.posIn.x = posData.posIn.y = 0; + } + + posData.posOut.x = posData.finalPosData.x - posData.interPosData.x; + posData.posOut.y = posData.finalPosData.y - posData.interPosData.y; + + // Process opacity + + posData.posIn.opacity = target.isShown ? 1 : self.effectsIn.opacity; + posData.posOut.opacity = 1; + posData.tweenData.opacity = posData.posOut.opacity - posData.posIn.opacity; + + // Adjust x and y if not nudging + + if (!target.isShown && !self.config.animation.nudge) { + posData.posIn.x = posData.posOut.x; + posData.posIn.y = posData.posOut.y; + } + + posData.tweenData.x = posData.posOut.x - posData.posIn.x; + posData.tweenData.y = posData.posOut.y - posData.posIn.y; + + // Process width, height, and margins + + if (self.config.animation.animateResizeTargets) { + posData.posIn.width = posData.startPosData.width; + posData.posIn.height = posData.startPosData.height; + + // "||" Prevents width/height change from including 0 width/height if hiding or showing + + widthChange = (posData.startPosData.width || posData.finalPosData.width) - posData.interPosData.width; + + posData.posIn.marginRight = posData.startPosData.marginRight - widthChange; + + heightChange = (posData.startPosData.height || posData.finalPosData.height) - posData.interPosData.height; + + posData.posIn.marginBottom = posData.startPosData.marginBottom - heightChange; + + posData.posOut.width = posData.finalPosData.width; + posData.posOut.height = posData.finalPosData.height; + + widthChange = (posData.finalPosData.width || posData.startPosData.width) - posData.interPosData.width; + + posData.posOut.marginRight = posData.finalPosData.marginRight - widthChange; + + heightChange = (posData.finalPosData.height || posData.startPosData.height) - posData.interPosData.height; + + posData.posOut.marginBottom = posData.finalPosData.marginBottom - heightChange; + + posData.tweenData.width = posData.posOut.width - posData.posIn.width; + posData.tweenData.height = posData.posOut.height - posData.posIn.height; + posData.tweenData.marginRight = posData.posOut.marginRight - posData.posIn.marginRight; + posData.tweenData.marginBottom = posData.posOut.marginBottom - posData.posIn.marginBottom; + } + + // Process transforms + + for (j = 0; effectName = effectNames[j]; j++) { + effect = self.effectsIn[effectName]; + + if (!(effect instanceof mixitup.TransformData) || !effect.value) continue; + + posData.posIn[effectName].value = effect.value; + posData.posOut[effectName].value = 0; + + posData.tweenData[effectName].value = + posData.posOut[effectName].value - posData.posIn[effectName].value; + + posData.posIn[effectName].unit = + posData.posOut[effectName].unit = + posData.tweenData[effectName].unit = + effect.unit; + } + } + + for (i = 0; target = operation.toHide[i]; i++) { + posData = operation.toHidePosData[i]; + posData.posIn = new mixitup.StyleData(); + posData.posOut = new mixitup.StyleData(); + posData.tweenData = new mixitup.StyleData(); + + // Process x and y + + posData.posIn.x = target.isShown ? posData.startPosData.x - posData.interPosData.x : 0; + posData.posIn.y = target.isShown ? posData.startPosData.y - posData.interPosData.y : 0; + posData.posOut.x = self.config.animation.nudge ? 0 : posData.posIn.x; + posData.posOut.y = self.config.animation.nudge ? 0 : posData.posIn.y; + posData.tweenData.x = posData.posOut.x - posData.posIn.x; + posData.tweenData.y = posData.posOut.y - posData.posIn.y; + + // Process width, height, and margins + + if (self.config.animation.animateResizeTargets) { + posData.posIn.width = posData.startPosData.width; + posData.posIn.height = posData.startPosData.height; + + widthChange = posData.startPosData.width - posData.interPosData.width; + + posData.posIn.marginRight = posData.startPosData.marginRight - widthChange; + + heightChange = posData.startPosData.height - posData.interPosData.height; + + posData.posIn.marginBottom = posData.startPosData.marginBottom - heightChange; + } + + // Process opacity + + posData.posIn.opacity = 1; + posData.posOut.opacity = self.effectsOut.opacity; + posData.tweenData.opacity = posData.posOut.opacity - posData.posIn.opacity; + + // Process transforms + + for (j = 0; effectName = effectNames[j]; j++) { + effect = self.effectsOut[effectName]; + + if (!(effect instanceof mixitup.TransformData) || !effect.value) continue; + + posData.posIn[effectName].value = 0; + posData.posOut[effectName].value = effect.value; + + posData.tweenData[effectName].value = + posData.posOut[effectName].value - posData.posIn[effectName].value; + + posData.posIn[effectName].unit = + posData.posOut[effectName].unit = + posData.tweenData[effectName].unit = + effect.unit; + } + } + + self.callActions('afterGetTweenData', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Operation} operation + * @return {void} + */ + + moveTargets: function(operation) { + var self = this, + target = null, + moveData = null, + posData = null, + statusChange = '', + willTransition = false, + staggerIndex = -1, + i = -1, + checkProgress = self.checkProgress.bind(self); + + self.callActions('beforeMoveTargets', arguments); + + // TODO: this is an extra loop in addition to the calcs + // done in getOperation, could some of this be done there? + + for (i = 0; target = operation.show[i]; i++) { + moveData = new mixitup.IMoveData(); + posData = operation.showPosData[i]; + + statusChange = target.isShown ? 'none' : 'show'; + + willTransition = self.willTransition( + statusChange, + operation.hasEffect, + posData.posIn, + posData.posOut + ); + + if (willTransition) { + // Prevent non-transitioning targets from incrementing the staggerIndex + + staggerIndex++; + } + + target.show(); + + moveData.posIn = posData.posIn; + moveData.posOut = posData.posOut; + moveData.statusChange = statusChange; + moveData.staggerIndex = staggerIndex; + moveData.operation = operation; + moveData.callback = willTransition ? checkProgress : null; + + target.move(moveData); + } + + for (i = 0; target = operation.toHide[i]; i++) { + posData = operation.toHidePosData[i]; + moveData = new mixitup.IMoveData(); + + statusChange = 'hide'; + + willTransition = self.willTransition(statusChange, posData.posIn, posData.posOut); + + moveData.posIn = posData.posIn; + moveData.posOut = posData.posOut; + moveData.statusChange = statusChange; + moveData.staggerIndex = i; + moveData.operation = operation; + moveData.callback = willTransition ? checkProgress : null; + + target.move(moveData); + } + + if (self.config.animation.animateResizeContainer) { + self.dom.parent.style[mixitup.features.transitionProp] = + 'height ' + self.config.animation.duration + 'ms ease, ' + + 'width ' + self.config.animation.duration + 'ms ease '; + + requestAnimationFrame(function() { + if ( + operation.startHeight !== operation.newHeight && + operation.viewportDeltaY !== operation.startHeight - operation.newHeight + ) { + self.dom.parent.style.height = operation.newHeight + 'px'; + } + + if ( + operation.startWidth !== operation.newWidth && + operation.viewportDeltaX !== operation.startWidth - operation.newWidth + ) { + self.dom.parent.style.width = operation.newWidth + 'px'; + } + }); + } + + if (operation.willChangeLayout) { + h.removeClass(self.dom.container, self.config.layout.ContainerClassName); + h.addClass(self.dom.container, operation.newContainerClassName); + } + + self.callActions('afterMoveTargets', arguments); + }, + + /** + * @private + * @instance + * @return {boolean} + */ + + hasEffect: function() { + var self = this, + EFFECTABLES = [ + 'scale', + 'translateX', 'translateY', 'translateZ', + 'rotateX', 'rotateY', 'rotateZ' + ], + effectName = '', + effect = null, + result = false, + value = -1, + i = -1; + + if (self.effectsIn.opacity !== 1) { + return self.callFilters('resultHasEffect', true, arguments); + } + + for (i = 0; effectName = EFFECTABLES[i]; i++) { + effect = self.effectsIn[effectName]; + value = (typeof effect && effect.value !== 'undefined') ? + effect.value : effect; + + if (value !== 0) { + result = true; + + break; + } + } + + return self.callFilters('resultHasEffect', result, arguments); + }, + + /** + * Determines if a target element will transition in + * some fasion and therefore requires binding of + * transitionEnd + * + * @private + * @instance + * @since 3.0.0 + * @param {string} statusChange + * @param {boolean} hasEffect + * @param {StyleData} posIn + * @param {StyleData} posOut + * @return {boolean} + */ + + willTransition: function(statusChange, hasEffect, posIn, posOut) { + var self = this, + result = false; + + if (!h.isVisible(self.dom.container)) { + // If the container is not visible, the transitionEnd + // event will not occur and MixItUp will hang + + result = false; + } else if ( + (statusChange !== 'none' && hasEffect) || + posIn.x !== posOut.x || + posIn.y !== posOut.y + ) { + // If opacity and/or translate will change + + result = true; + } else if (self.config.animation.animateResizeTargets) { + // Check if width, height or margins will change + + result = ( + posIn.width !== posOut.width || + posIn.height !== posOut.height || + posIn.marginRight !== posOut.marginRight || + posIn.marginTop !== posOut.marginTop + ); + } else { + result = false; + } + + return self.callFilters('resultWillTransition', result, arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + checkProgress: function(operation) { + var self = this; + + self.targetsDone++; + + if (self.targetsBound === self.targetsDone) { + self.cleanUp(operation); + } + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + cleanUp: function(operation) { + var self = this, + target = null, + whitespaceBefore = null, + whitespaceAfter = null, + nextInQueue = null, + i = -1; + + self.callActions('beforeCleanUp', arguments); + + self.targetsMoved = + self.targetsImmovable = + self.targetsBound = + self.targetsDone = 0; + + for (i = 0; target = operation.show[i]; i++) { + target.cleanUp(); + + target.show(); + } + + for (i = 0; target = operation.toHide[i]; i++) { + target.cleanUp(); + + target.hide(); + } + + if (operation.willSort) { + self.printSort(false, operation); + } + + // Remove any styles applied to the parent container + + self.dom.parent.style[mixitup.features.transitionProp] = + self.dom.parent.style.height = + self.dom.parent.style.width = + self.dom.parent.style.overflow = + self.dom.parent.style[mixitup.features.perspectiveProp] = + self.dom.parent.style[mixitup.features.perspectiveOriginProp] = ''; + + if (operation.willChangeLayout) { + h.removeClass(self.dom.container, operation.startContainerClassName); + h.addClass(self.dom.container, operation.newContainerClassName); + } + + if (operation.toRemove.length) { + for (i = 0; target = self.targets[i]; i++) { + if (operation.toRemove.indexOf(target) > -1) { + if ( + (whitespaceBefore = target.dom.el.previousSibling) && whitespaceBefore.nodeName === '#text' && + (whitespaceAfter = target.dom.el.nextSibling) && whitespaceAfter.nodeName === '#text' + ) { + h.removeWhitespace(whitespaceBefore); + } + + if (!operation.willSort) { + // NB: Sorting will remove targets as a bi-product of `printSort()` + + self.dom.parent.removeChild(target.dom.el); + } + + self.targets.splice(i, 1); + + target.isInDom = false; + + i--; + } + } + + // Since targets have been removed, the original order must be updated + + self.origOrder = self.targets; + } + + if (operation.willSort) { + self.targets = operation.newOrder; + } + + self.state = operation.newState; + self.lastOperation = operation; + + self.dom.targets = self.state.targets; + + // mixEnd + + mixitup.events.fire('mixEnd', self.dom.container, { + state: self.state, + instance: self + }, self.dom.document); + + if (typeof self.config.callbacks.onMixEnd === 'function') { + self.config.callbacks.onMixEnd.call(self.dom.container, self.state, self); + } + + if (operation.hasFailed) { + // mixFail + + mixitup.events.fire('mixFail', self.dom.container, { + state: self.state, + instance: self + }, self.dom.document); + + if (typeof self.config.callbacks.onMixFail === 'function') { + self.config.callbacks.onMixFail.call(self.dom.container, self.state, self); + } + + h.addClass(self.dom.container, h.getClassname(self.config.classNames, 'container', self.config.classNames.modifierFailed)); + } + + // User-defined callback function + + if (typeof self.userCallback === 'function') { + self.userCallback.call(self.dom.container, self.state, self); + } + + if (typeof self.userDeferred.resolve === 'function') { + self.userDeferred.resolve(self.state); + } + + self.userCallback = null; + self.userDeferred = null; + self.lastClicked = null; + self.isToggling = false; + self.isBusy = false; + + if (self.queue.length) { + self.callActions('beforeReadQueueCleanUp', arguments); + + nextInQueue = self.queue.shift(); + + // Update non-public API properties stored in queue + + self.userDeferred = nextInQueue.deferred; + self.isToggling = nextInQueue.isToggling; + self.lastClicked = nextInQueue.triggerElement; + + if (nextInQueue.instruction.command instanceof mixitup.CommandMultimix) { + self.multimix.apply(self, nextInQueue.args); + } else { + self.dataset.apply(self, nextInQueue.args); + } + } + + self.callActions('afterCleanUp', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Array<*>} args + * @return {mixitup.UserInstruction} + */ + + parseMultimixArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + arg = null, + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandMultimix(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (arg === null) continue; + + if (typeof arg === 'object') { + h.extend(instruction.command, arg); + } else if (typeof arg === 'boolean') { + instruction.animate = arg; + } else if (typeof arg === 'function') { + instruction.callback = arg; + } + } + + // Coerce arbitrary command arguments into typed command objects + + if (instruction.command.insert && !(instruction.command.insert instanceof mixitup.CommandInsert)) { + instruction.command.insert = self.parseInsertArgs([instruction.command.insert]).command; + } + + if (instruction.command.remove && !(instruction.command.remove instanceof mixitup.CommandRemove)) { + instruction.command.remove = self.parseRemoveArgs([instruction.command.remove]).command; + } + + if (instruction.command.filter && !(instruction.command.filter instanceof mixitup.CommandFilter)) { + instruction.command.filter = self.parseFilterArgs([instruction.command.filter]).command; + } + + if (instruction.command.sort && !(instruction.command.sort instanceof mixitup.CommandSort)) { + instruction.command.sort = self.parseSortArgs([instruction.command.sort]).command; + } + + if (instruction.command.changeLayout && !(instruction.command.changeLayout instanceof mixitup.CommandChangeLayout)) { + instruction.command.changeLayout = self.parseChangeLayoutArgs([instruction.command.changeLayout]).command; + } + + instruction = self.callFilters('instructionParseMultimixArgs', instruction, arguments); + + h.freeze(instruction); + + return instruction; + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Array<*>} args + * @return {mixitup.UserInstruction} + */ + + parseFilterArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + arg = null, + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandFilter(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (typeof arg === 'string') { + // Selector + + instruction.command.selector = arg; + } else if (arg === null) { + instruction.command.collection = []; + } else if (typeof arg === 'object' && h.isElement(arg, self.dom.document)) { + // Single element + + instruction.command.collection = [arg]; + } else if (typeof arg === 'object' && typeof arg.length !== 'undefined') { + // Multiple elements in array, NodeList or jQuery collection + + instruction.command.collection = h.arrayFromList(arg); + } else if (typeof arg === 'object') { + // Filter command + + h.extend(instruction.command, arg); + } else if (typeof arg === 'boolean') { + instruction.animate = arg; + } else if (typeof arg === 'function') { + instruction.callback = arg; + } + } + + if (instruction.command.selector && instruction.command.collection) { + throw new Error(mixitup.messages.errorFilterInvalidArguments()); + } + + instruction = self.callFilters('instructionParseFilterArgs', instruction, arguments); + + h.freeze(instruction); + + return instruction; + }, + + parseSortArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + arg = null, + sortString = '', + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandSort(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (arg === null) continue; + + switch (typeof arg) { + case 'string': + // Sort string + + sortString = arg; + + break; + case 'object': + // Array of element references + + if (arg.length) { + instruction.command.collection = h.arrayFromList(arg); + } + + break; + case 'boolean': + instruction.animate = arg; + + break; + case 'function': + instruction.callback = arg; + + break; + } + } + + if (sortString) { + instruction.command = self.parseSortString(sortString, instruction.command); + } + + instruction = self.callFilters('instructionParseSortArgs', instruction, arguments); + + h.freeze(instruction); + + return instruction; + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Array<*>} args + * @return {mixitup.UserInstruction} + */ + + parseInsertArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + arg = null, + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandInsert(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (arg === null) continue; + + if (typeof arg === 'number') { + // Insert index + + instruction.command.index = arg; + } else if (typeof arg === 'string' && ['before', 'after'].indexOf(arg) > -1) { + // 'before'/'after' + + instruction.command.position = arg; + } else if (typeof arg === 'string') { + // Markup + + instruction.command.collection = + h.arrayFromList(h.createElement(arg).childNodes); + } else if (typeof arg === 'object' && h.isElement(arg, self.dom.document)) { + // Single element + + !instruction.command.collection.length ? + (instruction.command.collection = [arg]) : + (instruction.command.sibling = arg); + } else if (typeof arg === 'object' && arg.length) { + // Multiple elements in array or jQuery collection + + !instruction.command.collection.length ? + (instruction.command.collection = arg) : + instruction.command.sibling = arg[0]; + } else if (typeof arg === 'object' && arg.childNodes && arg.childNodes.length) { + // Document fragment + + !instruction.command.collection.length ? + instruction.command.collection = h.arrayFromList(arg.childNodes) : + instruction.command.sibling = arg.childNodes[0]; + } else if (typeof arg === 'object') { + // Insert command + + h.extend(instruction.command, arg); + } else if (typeof arg === 'boolean') { + instruction.animate = arg; + } else if (typeof arg === 'function') { + instruction.callback = arg; + } + } + + if (instruction.command.index && instruction.command.sibling) { + throw new Error(mixitup.messages.errorInsertInvalidArguments()); + } + + if (!instruction.command.collection.length && self.config.debug.showWarnings) { + console.warn(mixitup.messages.warningInsertNoElements()); + } + + instruction = self.callFilters('instructionParseInsertArgs', instruction, arguments); + + h.freeze(instruction); + + return instruction; + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Array<*>} args + * @return {mixitup.UserInstruction} + */ + + parseRemoveArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + target = null, + arg = null, + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandRemove(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (arg === null) continue; + + switch (typeof arg) { + case 'number': + if (self.targets[arg]) { + instruction.command.targets[0] = self.targets[arg]; + } + + break; + case 'string': + instruction.command.collection = h.arrayFromList(self.dom.parent.querySelectorAll(arg)); + + break; + case 'object': + if (arg && arg.length) { + instruction.command.collection = arg; + } else if (h.isElement(arg, self.dom.document)) { + instruction.command.collection = [arg]; + } else { + // Remove command + + h.extend(instruction.command, arg); + } + + break; + case 'boolean': + instruction.animate = arg; + + break; + case 'function': + instruction.callback = arg; + + break; + } + } + + if (instruction.command.collection.length) { + for (i = 0; target = self.targets[i]; i++) { + if (instruction.command.collection.indexOf(target.dom.el) > -1) { + instruction.command.targets.push(target); + } + } + } + + if (!instruction.command.targets.length && self.config.debug.showWarnings) { + console.warn(mixitup.messages.warningRemoveNoElements()); + } + + h.freeze(instruction); + + return instruction; + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Array<*>} args + * @return {mixitup.UserInstruction} + */ + + parseDatasetArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + arg = null, + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandDataset(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (arg === null) continue; + + switch (typeof arg) { + case 'object': + if (Array.isArray(arg) || typeof arg.length === 'number') { + instruction.command.dataset = arg; + } else { + // Change layout command + + h.extend(instruction.command, arg); + } + + break; + case 'boolean': + instruction.animate = arg; + + break; + case 'function': + instruction.callback = arg; + + break; + } + } + + h.freeze(instruction); + + return instruction; + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Array<*>} args + * @return {mixitup.UserInstruction} + */ + + parseChangeLayoutArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + arg = null, + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandChangeLayout(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (arg === null) continue; + + switch (typeof arg) { + case 'string': + instruction.command.containerClassName = arg; + + break; + case 'object': + // Change layout command + + h.extend(instruction.command, arg); + + break; + case 'boolean': + instruction.animate = arg; + + break; + case 'function': + instruction.callback = arg; + + break; + } + } + + h.freeze(instruction); + + return instruction; + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {mixitup.QueueItem} queueItem + * @return {Promise.} + */ + + queueMix: function(queueItem) { + var self = this, + deferred = null, + toggleSelector = ''; + + self.callActions('beforeQueueMix', arguments); + + deferred = h.defer(mixitup.libraries); + + if (self.config.animation.queue && self.queue.length < self.config.animation.queueLimit) { + queueItem.deferred = deferred; + + self.queue.push(queueItem); + + // Keep controls in sync with user interactions. Mixer will catch up as it drains the queue. + + if (self.config.controls.enable) { + if (self.isToggling) { + self.buildToggleArray(queueItem.instruction.command); + + toggleSelector = self.getToggleSelector(); + + self.updateControls({ + filter: { + selector: toggleSelector + } + }); + } else { + self.updateControls(queueItem.instruction.command); + } + } + } else { + if (self.config.debug.showWarnings) { + console.warn(mixitup.messages.warningMultimixInstanceQueueFull()); + } + + deferred.resolve(self.state); + + mixitup.events.fire('mixBusy', self.dom.container, { + state: self.state, + instance: self + }, self.dom.document); + + if (typeof self.config.callbacks.onMixBusy === 'function') { + self.config.callbacks.onMixBusy.call(self.dom.container, self.state, self); + } + } + + return self.callFilters('promiseQueueMix', deferred.promise, arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Array.} newDataset + * @return {Operation} + */ + + getDataOperation: function(newDataset) { + var self = this, + operation = new mixitup.Operation(), + startDataset = []; + + operation = self.callFilters('operationUnmappedGetDataOperation', operation, arguments); + + if (self.dom.targets.length && !(startDataset = (self.state.activeDataset || [])).length) { + throw new Error(mixitup.messages.errorDatasetNotSet()); + } + + operation.id = h.randomHex(); + operation.startState = self.state; + operation.startDataset = startDataset; + operation.newDataset = newDataset.slice(); + + self.diffDatasets(operation); + + operation.startOrder = self.targets; + operation.newOrder = operation.show; + + if (self.config.animation.enable) { + self.getStartMixData(operation); + self.setInter(operation); + + operation.docState = h.getDocumentState(self.dom.document); + + self.getInterMixData(operation); + self.setFinal(operation); + self.getFinalMixData(operation); + + self.parseEffects(); + + operation.hasEffect = self.hasEffect(); + + self.getTweenData(operation); + } + + self.targets = operation.show.slice(); + + operation.newState = self.buildState(operation); + + // NB: Targets to be removed must be included in `self.targets` for removal during clean up, + // but are added after state is built so that state is accurate + + Array.prototype.push.apply(self.targets, operation.toRemove); + + operation = self.callFilters('operationMappedGetDataOperation', operation, arguments); + + return operation; + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {mixitup.Operation} operation + * @return {void} + */ + + diffDatasets: function(operation) { + var self = this, + persistantStartIds = [], + persistantNewIds = [], + insertedTargets = [], + data = null, + target = null, + el = null, + frag = null, + nextEl = null, + uids = {}, + id = '', + i = -1; + + self.callActions('beforeDiffDatasets', arguments); + + for (i = 0; data = operation.newDataset[i]; i++) { + if (typeof (id = data[self.config.data.uidKey]) === 'undefined' || id.toString().length < 1) { + throw new TypeError(mixitup.messages.errorDatasetInvalidUidKey({ + uidKey: self.config.data.uidKey + })); + } + + if (!uids[id]) { + uids[id] = true; + } else { + throw new Error(mixitup.messages.errorDatasetDuplicateUid({ + uid: id + })); + } + + if ((target = self.cache[id]) instanceof mixitup.Target) { + // Already in cache + + if (self.config.data.dirtyCheck && !h.deepEquals(data, target.data)) { + // change detected + + el = target.render(data); + + target.data = data; + + if (el !== target.dom.el) { + // Update target element reference + + if (target.isInDom) { + target.unbindEvents(); + + self.dom.parent.replaceChild(el, target.dom.el); + } + + if (!target.isShown) { + el.style.display = 'none'; + } + + target.dom.el = el; + + if (target.isInDom) { + target.bindEvents(); + } + } + } + + el = target.dom.el; + } else { + // New target + + target = new mixitup.Target(); + + target.init(null, self, data); + + target.hide(); + } + + if (!target.isInDom) { + // Adding to DOM + + if (!frag) { + // Open frag + + frag = self.dom.document.createDocumentFragment(); + } + + if (frag.lastElementChild) { + frag.appendChild(self.dom.document.createTextNode(' ')); + } + + frag.appendChild(target.dom.el); + + target.isInDom = true; + + target.unbindEvents(); + target.bindEvents(); + target.hide(); + + operation.toShow.push(target); + + insertedTargets.push(target); + } else { + // Already in DOM + + nextEl = target.dom.el.nextElementSibling; + + persistantNewIds.push(id); + + if (frag) { + // Close and insert previously opened frag + + if (frag.lastElementChild) { + frag.appendChild(self.dom.document.createTextNode(' ')); + } + + self.insertDatasetFrag(frag, target.dom.el, insertedTargets); + + frag = null; + } + } + + operation.show.push(target); + } + + if (frag) { + // Unclosed frag remaining + + nextEl = nextEl || self.config.layout.siblingAfter; + + if (nextEl) { + frag.appendChild(self.dom.document.createTextNode(' ')); + } + + self.insertDatasetFrag(frag, nextEl, insertedTargets); + } + + for (i = 0; data = operation.startDataset[i]; i++) { + id = data[self.config.data.uidKey]; + + target = self.cache[id]; + + if (operation.show.indexOf(target) < 0) { + // Previously shown but now absent + + operation.hide.push(target); + operation.toHide.push(target); + operation.toRemove.push(target); + } else { + persistantStartIds.push(id); + } + } + + if (!h.isEqualArray(persistantStartIds, persistantNewIds)) { + operation.willSort = true; + } + + self.callActions('afterDiffDatasets', arguments); + }, + + /** + * @private + * @instance + * @since 3.1.5 + * @param {DocumentFragment} frag + * @param {(HTMLElement|null)} nextEl + * @param {Array.} targets + * @return {void} + */ + + insertDatasetFrag: function(frag, nextEl, targets) { + var self = this; + var insertAt = nextEl ? h.arrayFromList(self.dom.parent.children).indexOf(nextEl) : self.targets.length; + + self.dom.parent.insertBefore(frag, nextEl); + + while (targets.length) { + self.targets.splice(insertAt, 0, targets.shift()); + + insertAt++; + } + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {mixitup.CommandSort} sortCommandA + * @param {mixitup.CommandSort} sortCommandB + * @return {boolean} + */ + + willSort: function(sortCommandA, sortCommandB) { + var self = this, + result = false; + + if ( + self.config.behavior.liveSort || + sortCommandA.order === 'random' || + sortCommandA.attribute !== sortCommandB.attribute || + sortCommandA.order !== sortCommandB.order || + sortCommandA.collection !== sortCommandB.collection || + (sortCommandA.next === null && sortCommandB.next) || + (sortCommandA.next && sortCommandB.next === null) + ) { + result = true; + } else if (sortCommandA.next && sortCommandB.next) { + result = self.willSort(sortCommandA.next, sortCommandB.next); + } else { + result = false; + } + + return self.callFilters('resultWillSort', result, arguments); + }, + + /** + * A shorthand method for `.filter('all')`. Shows all targets in the container. + * + * @example + * + * .show() + * + * @example Example: Showing all targets + * + * mixer.show() + * .then(function(state) { + * console.log(state.totalShow === state.totalTargets); // true + * }); + * + * @public + * @instance + * @since 3.0.0 + * @return {Promise.} + */ + + show: function() { + var self = this; + + return self.filter('all'); + }, + + /** + * A shorthand method for `.filter('none')`. Hides all targets in the container. + * + * @example + * + * .hide() + * + * @example Example: Hiding all targets + * + * mixer.hide() + * .then(function(state) { + * console.log(state.totalShow === 0); // true + * console.log(state.totalHide === state.totalTargets); // true + * }); + * + * @public + * @instance + * @since 3.0.0 + * @return {Promise.} + */ + + hide: function() { + var self = this; + + return self.filter('none'); + }, + + /** + * Returns a boolean indicating whether or not a MixItUp operation is + * currently in progress. + * + * @example + * + * .isMixing() + * + * @example Example: Checking the status of a mixer + * + * mixer.sort('random', function() { + * console.log(mixer.isMixing()) // false + * }); + * + * console.log(mixer.isMixing()) // true + * + * @public + * @instance + * @since 2.0.0 + * @return {boolean} + */ + + isMixing: function() { + var self = this; + + return self.isBusy; + }, + + /** + * Filters all targets in the container by a provided selector string, or the values `'all'` + * or `'none'`. Only targets matching the selector will be shown. + * + * @example + * + * .filter(selector [, animate] [, callback]) + * + * @example Example 1: Filtering targets by a class selector + * + * mixer.filter('.category-a') + * .then(function(state) { + * console.log(state.totalShow === containerEl.querySelectorAll('.category-a').length); // true + * }); + * + * @example Example 2: Filtering targets by an attribute selector + * + * mixer.filter('[data-category~="a"]') + * .then(function(state) { + * console.log(state.totalShow === containerEl.querySelectorAll('[data-category~="a"]').length); // true + * }); + * + * @example Example 3: Filtering targets by a compound selector + * + * // Show only those targets with the classes 'category-a' AND 'category-b' + * + * mixer.filter('.category-a.category-c') + * .then(function(state) { + * console.log(state.totalShow === containerEl.querySelectorAll('.category-a.category-c').length); // true + * }); + * + * @example Example 4: Filtering via an element collection + * + * var collection = Array.from(container.querySelectorAll('.mix')); + * + * console.log(collection.length); // 34 + * + * // Filter the collection manually using Array.prototype.filter + * + * var filtered = collection.filter(function(target) { + * return parseInt(target.getAttribute('data-price')) > 10; + * }); + * + * console.log(filtered.length); // 22 + * + * // Pass the filtered collection to MixItUp + * + * mixer.filter(filtered) + * .then(function(state) { + * console.log(state.activeFilter.collection.length === 22); // true + * }); + * + * @public + * @instance + * @since 2.0.0 + * @param {(string|HTMLElement|Array.)} selector + * Any valid CSS selector (i.e. `'.category-a'`), or the values `'all'` or `'none'`. The filter method also accepts a reference to single target element or a collection of target elements to show. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + filter: function() { + var self = this, + instruction = self.parseFilterArgs(arguments); + + return self.multimix({ + filter: instruction.command + }, instruction.animate, instruction.callback); + }, + + /** + * Adds an additional selector to the currently active filter selector, concatenating + * as per the logic defined in `controls.toggleLogic`. + * + * @example + * + * .toggleOn(selector [, animate] [, callback]) + * + * @example Example: Toggling on a filter selector + * + * console.log(mixer.getState().activeFilter.selector); // '.category-a' + * + * mixer.toggleOn('.category-b') + * .then(function(state) { + * console.log(state.activeFilter.selector); // '.category-a, .category-b' + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {string} selector + * Any valid CSS selector (i.e. `'.category-a'`) + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + toggleOn: function() { + var self = this, + instruction = self.parseFilterArgs(arguments), + selector = instruction.command.selector, + toggleSelector = ''; + + self.isToggling = true; + + if (self.toggleArray.indexOf(selector) < 0) { + self.toggleArray.push(selector); + } + + toggleSelector = self.getToggleSelector(); + + return self.multimix({ + filter: toggleSelector + }, instruction.animate, instruction.callback); + }, + + /** + * Removes a selector from the active filter selector. + * + * @example + * + * .toggleOff(selector [, animate] [, callback]) + * + * @example Example: Toggling off a filter selector + * + * console.log(mixer.getState().activeFilter.selector); // '.category-a, .category-b' + * + * mixer.toggleOff('.category-b') + * .then(function(state) { + * console.log(state.activeFilter.selector); // '.category-a' + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {string} selector + * Any valid CSS selector (i.e. `'.category-a'`) + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + toggleOff: function() { + var self = this, + instruction = self.parseFilterArgs(arguments), + selector = instruction.command.selector, + selectorIndex = self.toggleArray.indexOf(selector), + toggleSelector = ''; + + self.isToggling = true; + + if (selectorIndex > -1) { + self.toggleArray.splice(selectorIndex, 1); + } + + toggleSelector = self.getToggleSelector(); + + return self.multimix({ + filter: toggleSelector + }, instruction.animate, instruction.callback); + }, + + /** + * Sorts all targets in the container according to a provided sort string. + * + * @example + * + * .sort(sortString [, animate] [, callback]) + * + * @example Example 1: Sorting by the default DOM order + * + * // Reverse the default order of the targets + * + * mixer.sort('default:desc') + * .then(function(state) { + * console.log(state.activeSort.attribute === 'default'); // true + * console.log(state.activeSort.order === 'desc'); // true + * }); + * + * @example Example 2: Sorting by a custom data-attribute + * + * // Sort the targets by the value of a `data-published-date` attribute + * + * mixer.sort('published-date:asc') + * .then(function(state) { + * console.log(state.activeSort.attribute === 'published-date'); // true + * console.log(state.activeSort.order === 'asc'); // true + * }); + * + * @example Example 3: Sorting by multiple attributes + * + * // Sort the targets by the value of a `data-published-date` attribute, then by `data-title` + * + * mixer.sort('published-date:desc data-title:asc') + * .then(function(state) { + * console.log(state.activeSort.attribute === 'published-date'); // true + * console.log(state.activeSort.order === 'desc'); // true + * + * console.log(state.activeSort.next.attribute === 'title'); // true + * console.log(state.activeSort.next.order === 'asc'); // true + * }); + * + * @example Example 4: Sorting by random + * + * mixer.sort('random') + * .then(function(state) { + * console.log(state.activeSort.order === 'random') // true + * }); + * + * @example Example 5: Sorting via an element collection + * + * var collection = Array.from(container.querySelectorAll('.mix')); + * + * // Swap the position of two elements in the collection: + * + * var temp = collection[1]; + * + * collection[1] = collection[0]; + * collection[0] = temp; + * + * // Pass the sorted collection to MixItUp + * + * mixer.sort(collection) + * .then(function(state) { + * console.log(state.targets[0] === collection[0]); // true + * }); + * + * @public + * @instance + * @since 2.0.0 + * @param {(string|Array.)} sortString + * A valid sort string (e.g. `'default'`, `'published-date:asc'`, or `'random'`). The sort method also accepts an array of all target elements in a user-defined order. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + sort: function() { + var self = this, + instruction = self.parseSortArgs(arguments); + + return self.multimix({ + sort: instruction.command + }, instruction.animate, instruction.callback); + }, + + /** + * Changes the layout of the container by adding, removing or updating a + * layout-specific class name. If `animation.animateResizetargets` is + * enabled, MixItUp will attempt to gracefully animate the width, height, + * and position of targets between layout states. + * + * @example + * + * .changeLayout(containerClassName [, animate] [, callback]) + * + * @example Example 1: Adding a new class name to the container + * + * mixer.changeLayout('container-list') + * .then(function(state) { + * console.log(state.activeContainerClass === 'container-list'); // true + * }); + * + * @example Example 2: Removing a previously added class name from the container + * + * mixer.changeLayout('') + * .then(function(state) { + * console.log(state.activeContainerClass === ''); // true + * }); + * + * @public + * @instance + * @since 2.0.0 + * @param {string} containerClassName + * A layout-specific class name to add to the container. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + changeLayout: function() { + var self = this, + instruction = self.parseChangeLayoutArgs(arguments); + + return self.multimix({ + changeLayout: instruction.command + }, instruction.animate, instruction.callback); + }, + + /** + * Updates the contents and order of the container to reflect the provided dataset, + * if the dataset API is in use. + * + * The dataset API is designed for use in API-driven JavaScript applications, and + * can be used instead of DOM-based methods such as `.filter()`, `.sort()`, + * `.insert()`, etc. When used, insertion, removal, sorting and pagination can be + * achieved purely via changes to your data model, without the uglyness of having + * to interact with or query the DOM directly. + * + * @example + * + * .dataset(dataset [, animate] [, callback]) + * + * @example Example 1: Rendering a dataset + * + * var myDataset = [ + * {id: 1, ...}, + * {id: 2, ...}, + * {id: 3, ...} + * ]; + * + * mixer.dataset(myDataset) + * .then(function(state) { + * console.log(state.totalShow === 3); // true + * }); + * + * @example Example 2: Sorting a dataset + * + * // Create a new dataset in reverse order + * + * var newDataset = myDataset.slice().reverse(); + * + * mixer.dataset(newDataset) + * .then(function(state) { + * console.log(state.activeDataset[0] === myDataset[2]); // true + * }); + * + * @example Example 3: Removing an item from the dataset + * + * console.log(myDataset.length); // 3 + * + * // Create a new dataset with the last item removed. + * + * var newDataset = myDataset.slice().pop(); + * + * mixer.dataset(newDataset) + * .then(function(state) { + * console.log(state.totalShow === 2); // true + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {Array.} dataset + * An array of objects, each one representing the underlying data model of a target to be rendered. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + dataset: function() { + var self = this, + instruction = self.parseDatasetArgs(arguments), + operation = null, + queueItem = null, + animate = false; + + self.callActions('beforeDataset', arguments); + + if (!self.isBusy) { + if (instruction.callback) self.userCallback = instruction.callback; + + animate = (instruction.animate ^ self.config.animation.enable) ? instruction.animate : self.config.animation.enable; + + operation = self.getDataOperation(instruction.command.dataset); + + return self.goMix(animate, operation); + } else { + queueItem = new mixitup.QueueItem(); + + queueItem.args = arguments; + queueItem.instruction = instruction; + + return self.queueMix(queueItem); + } + }, + + /** + * Performs simultaneous `filter`, `sort`, `insert`, `remove` and `changeLayout` + * operations as requested. + * + * @example + * + * .multimix(multimixCommand [, animate] [, callback]) + * + * @example Example 1: Performing simultaneous filtering and sorting + * + * mixer.multimix({ + * filter: '.category-b', + * sort: 'published-date:desc' + * }) + * .then(function(state) { + * console.log(state.activeFilter.selector === '.category-b'); // true + * console.log(state.activeSort.attribute === 'published-date'); // true + * }); + * + * @example Example 2: Performing simultaneous sorting, insertion, and removal + * + * console.log(mixer.getState().totalShow); // 6 + * + * // NB: When inserting via `multimix()`, an object should be provided as the value + * // for the `insert` portion of the command, allowing for a collection of elements + * // and an insertion index to be specified. + * + * mixer.multimix({ + * sort: 'published-date:desc', // Sort the container, including any new elements + * insert: { + * collection: [newElementReferenceA, newElementReferenceB], // Add 2 new elements at index 5 + * index: 5 + * }, + * remove: existingElementReference // Remove 1 existing element + * }) + * .then(function(state) { + * console.log(state.activeSort.attribute === 'published-date'); // true + * console.log(state.totalShow === 7); // true + * }); + * + * @public + * @instance + * @since 2.0.0 + * @param {object} multimixCommand + * An object containing one or more things to do + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + multimix: function() { + var self = this, + operation = null, + animate = false, + queueItem = null, + instruction = self.parseMultimixArgs(arguments); + + self.callActions('beforeMultimix', arguments); + + if (!self.isBusy) { + operation = self.getOperation(instruction.command); + + if (self.config.controls.enable) { + // Update controls for API calls + + if (instruction.command.filter && !self.isToggling) { + // As we are not toggling, reset the toggle array + // so new filter overrides existing toggles + + self.toggleArray.length = 0; + self.buildToggleArray(operation.command); + } + + if (self.queue.length < 1) { + self.updateControls(operation.command); + } + } + + if (instruction.callback) self.userCallback = instruction.callback; + + // Always allow the instruction to override the instance setting + + animate = (instruction.animate ^ self.config.animation.enable) ? + instruction.animate : + self.config.animation.enable; + + self.callFilters('operationMultimix', operation, arguments); + + return self.goMix(animate, operation); + } else { + queueItem = new mixitup.QueueItem(); + + queueItem.args = arguments; + queueItem.instruction = instruction; + queueItem.triggerElement = self.lastClicked; + queueItem.isToggling = self.isToggling; + + return self.queueMix(queueItem); + } + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {object} multimixCommand + * @param {boolean} [isPreFetch] + * An optional boolean indicating that the operation is being pre-fetched for execution at a later time. + * @return {Operation|null} + */ + + getOperation: function(multimixCommand) { + var self = this, + sortCommand = multimixCommand.sort, + filterCommand = multimixCommand.filter, + changeLayoutCommand = multimixCommand.changeLayout, + removeCommand = multimixCommand.remove, + insertCommand = multimixCommand.insert, + operation = new mixitup.Operation(); + + operation = self.callFilters('operationUnmappedGetOperation', operation, arguments); + + operation.id = h.randomHex(); + operation.command = multimixCommand; + operation.startState = self.state; + operation.triggerElement = self.lastClicked; + + if (self.isBusy) { + if (self.config.debug.showWarnings) { + console.warn(mixitup.messages.warningGetOperationInstanceBusy()); + } + + return null; + } + + if (insertCommand) { + self.insertTargets(insertCommand, operation); + } + + if (removeCommand) { + operation.toRemove = removeCommand.targets; + } + + operation.startSort = operation.newSort = operation.startState.activeSort; + operation.startOrder = operation.newOrder = self.targets; + + if (sortCommand) { + operation.startSort = operation.startState.activeSort; + operation.newSort = sortCommand; + + operation.willSort = self.willSort(sortCommand, operation.startState.activeSort); + + if (operation.willSort) { + self.sortOperation(operation); + } + } + + operation.startFilter = operation.startState.activeFilter; + + if (filterCommand) { + operation.newFilter = filterCommand; + } else { + operation.newFilter = h.extend(new mixitup.CommandFilter(), operation.startFilter); + } + + if (operation.newFilter.selector === 'all') { + operation.newFilter.selector = self.config.selectors.target; + } else if (operation.newFilter.selector === 'none') { + operation.newFilter.selector = ''; + } + + self.filterOperation(operation); + + operation.startContainerClassName = operation.startState.activeContainerClassName; + + if (changeLayoutCommand) { + operation.newContainerClassName = changeLayoutCommand.containerClassName; + + if (operation.newContainerClassName !== operation.startContainerClassName) { + operation.willChangeLayout = true; + } + } else { + operation.newContainerClassName = operation.startContainerClassName; + } + + if (self.config.animation.enable) { + // Populate the operation's position data + + self.getStartMixData(operation); + self.setInter(operation); + + operation.docState = h.getDocumentState(self.dom.document); + + self.getInterMixData(operation); + self.setFinal(operation); + self.getFinalMixData(operation); + + self.parseEffects(); + + operation.hasEffect = self.hasEffect(); + + self.getTweenData(operation); + } + + if (operation.willSort) { + self.targets = operation.newOrder; + } + + operation.newState = self.buildState(operation); + + return self.callFilters('operationMappedGetOperation', operation, arguments); + }, + + /** + * Renders a previously created operation at a specific point in its path, as + * determined by a multiplier between 0 and 1. + * + * @example + * .tween(operation, multiplier) + * + * @private + * @instance + * @since 3.0.0 + * @param {mixitup.Operation} operation + * An operation object created via the `getOperation` method + * + * @param {Float} multiplier + * Any number between 0 and 1 representing the percentage complete of the operation + * @return {void} + */ + + tween: function(operation, multiplier) { + var target = null, + posData = null, + toHideIndex = -1, + i = -1; + + multiplier = Math.min(multiplier, 1); + multiplier = Math.max(multiplier, 0); + + for (i = 0; target = operation.show[i]; i++) { + posData = operation.showPosData[i]; + + target.applyTween(posData, multiplier); + } + + for (i = 0; target = operation.hide[i]; i++) { + if (target.isShown) { + target.hide(); + } + + if ((toHideIndex = operation.toHide.indexOf(target)) > -1) { + posData = operation.toHidePosData[toHideIndex]; + + if (!target.isShown) { + target.show(); + } + + target.applyTween(posData, multiplier); + } + } + }, + + /** + * Inserts one or more new target elements into the container at a specified + * index. + * + * To be indexed as targets, new elements must match the `selectors.target` + * selector (`'.mix'` by default). + * + * @example + * + * .insert(newElements [, index] [, animate], [, callback]) + * + * @example Example 1: Inserting a single element via reference + * + * console.log(mixer.getState().totalShow); // 0 + * + * // Create a new element + * + * var newElement = document.createElement('div'); + * newElement.classList.add('mix'); + * + * mixer.insert(newElement) + * .then(function(state) { + * console.log(state.totalShow === 1); // true + * }); + * + * @example Example 2: Inserting a single element via HTML string + * + * console.log(mixer.getState().totalShow); // 1 + * + * // Create a new element via reference + * + * var newElementHtml = '<div class="mix"></div>'; + * + * // Create and insert the new element at index 1 + * + * mixer.insert(newElementHtml, 1) + * .then(function(state) { + * console.log(state.totalShow === 2); // true + * console.log(state.show[1].outerHTML === newElementHtml); // true + * }); + * + * @example Example 3: Inserting multiple elements via reference + * + * console.log(mixer.getState().totalShow); // 2 + * + * // Create an array of new elements to insert. + * + * var newElement1 = document.createElement('div'); + * var newElement2 = document.createElement('div'); + * + * newElement1.classList.add('mix'); + * newElement2.classList.add('mix'); + * + * var newElementsCollection = [newElement1, newElement2]; + * + * // Insert the new elements starting at index 1 + * + * mixer.insert(newElementsCollection, 1) + * .then(function(state) { + * console.log(state.totalShow === 4); // true + * console.log(state.show[1] === newElement1); // true + * console.log(state.show[2] === newElement2); // true + * }); + * + * @example Example 4: Inserting a jQuery collection object containing one or more elements + * + * console.log(mixer.getState().totalShow); // 4 + * + * var $newElement = $('<div class="mix"></div>'); + * + * // Insert the new elements starting at index 3 + * + * mixer.insert($newElement, 3) + * .then(function(state) { + * console.log(state.totalShow === 5); // true + * console.log(state.show[3] === $newElement[0]); // true + * }); + * + * @public + * @instance + * @since 2.0.0 + * @param {(HTMLElement|Array.|string)} newElements + * A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element. + * @param {number} index=0 + * The index at which to insert the new element(s). `0` by default. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + insert: function() { + var self = this, + args = self.parseInsertArgs(arguments); + + return self.multimix({ + insert: args.command + }, args.animate, args.callback); + }, + + /** + * Inserts one or more new elements before a provided reference element. + * + * @example + * + * .insertBefore(newElements, referenceElement [, animate] [, callback]) + * + * @example Example: Inserting a new element before a reference element + * + * // An existing reference element is chosen at index 2 + * + * var referenceElement = mixer.getState().show[2]; + * + * // Create a new element + * + * var newElement = document.createElement('div'); + * newElement.classList.add('mix'); + * + * mixer.insertBefore(newElement, referenceElement) + * .then(function(state) { + * // The new element is inserted into the container at index 2, before the reference element + * + * console.log(state.show[2] === newElement); // true + * + * // The reference element is now at index 3 + * + * console.log(state.show[3] === referenceElement); // true + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {(HTMLElement|Array.|string)} newElements + * A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element. + * @param {HTMLElement} referenceElement + * A reference to an existing element in the container to insert new elements before. + *@param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + insertBefore: function() { + var self = this, + args = self.parseInsertArgs(arguments); + + return self.insert(args.command.collection, 'before', args.command.sibling, args.animate, args.callback); + }, + + /** + * Inserts one or more new elements after a provided reference element. + * + * @example + * + * .insertAfter(newElements, referenceElement [, animate] [, callback]) + * + * @example Example: Inserting a new element after a reference element + * + * // An existing reference element is chosen at index 2 + * + * var referenceElement = mixer.getState().show[2]; + * + * // Create a new element + * + * var newElement = document.createElement('div'); + * newElement.classList.add('mix'); + * + * mixer.insertAfter(newElement, referenceElement) + * .then(function(state) { + * // The new element is inserted into the container at index 3, after the reference element + * + * console.log(state.show[3] === newElement); // true + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {(HTMLElement|Array.|string)} newElements + * A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element. + * @param {HTMLElement} referenceElement + * A reference to an existing element in the container to insert new elements after. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + insertAfter: function() { + var self = this, + args = self.parseInsertArgs(arguments); + + return self.insert(args.command.collection, 'after', args.command.sibling, args.animate, args.callback); + }, + + /** + * Inserts one or more new elements into the container before all existing targets. + * + * @example + * + * .prepend(newElements [,animate] [,callback]) + * + * @example Example: Prepending a new element + * + * // Create a new element + * + * var newElement = document.createElement('div'); + * newElement.classList.add('mix'); + * + * // Insert the element into the container + * + * mixer.prepend(newElement) + * .then(function(state) { + * console.log(state.show[0] === newElement); // true + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {(HTMLElement|Array.|string)} newElements + * A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + prepend: function() { + var self = this, + args = self.parseInsertArgs(arguments); + + return self.insert(0, args.command.collection, args.animate, args.callback); + }, + + /** + * Inserts one or more new elements into the container after all existing targets. + * + * @example + * + * .append(newElements [,animate] [,callback]) + * + * @example Example: Appending a new element + * + * // Create a new element + * + * var newElement = document.createElement('div'); + * newElement.classList.add('mix'); + * + * // Insert the element into the container + * + * mixer.append(newElement) + * .then(function(state) { + * console.log(state.show[state.show.length - 1] === newElement); // true + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {(HTMLElement|Array.|string)} newElements + * A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + append: function() { + var self = this, + args = self.parseInsertArgs(arguments); + + return self.insert(self.state.totalTargets, args.command.collection, args.animate, args.callback); + }, + + /** + * Removes one or more existing target elements from the container. + * + * @example + * + * .remove(elements [, animate] [, callback]) + * + * @example Example 1: Removing an element by reference + * + * var elementToRemove = containerEl.firstElementChild; + * + * mixer.remove(elementToRemove) + * .then(function(state) { + * console.log(state.targets.indexOf(elementToRemove) === -1); // true + * }); + * + * @example Example 2: Removing a collection of elements by reference + * + * var elementsToRemove = containerEl.querySelectorAll('.category-a'); + * + * console.log(elementsToRemove.length) // 3 + * + * mixer.remove(elementsToRemove) + * .then(function() { + * console.log(containerEl.querySelectorAll('.category-a').length); // 0 + * }); + * + * @example Example 3: Removing one or more elements by selector + * + * mixer.remove('.category-a') + * .then(function() { + * console.log(containerEl.querySelectorAll('.category-a').length); // 0 + * }); + * + * @example Example 4: Removing an element by index + * + * console.log(mixer.getState.totalShow); // 4 + * + * // Remove the element at index 3 + * + * mixer.remove(3) + * .then(function(state) { + * console.log(state.totalShow); // 3 + * console.log(state.show[3]); // undefined + * }); + * + * + * @public + * @instance + * @since 3.0.0 + * @param {(HTMLElement|Array.|string|number)} elements + * A reference to a single element to remove, an array-like collection of elements, a selector string, or the index of an element to remove. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + remove: function() { + var self = this, + args = self.parseRemoveArgs(arguments); + + return self.multimix({ + remove: args.command + }, args.animate, args.callback); + }, + + /** + * Retrieves the the value of any property or sub-object within the current + * mixitup configuration, or the whole configuration object. + * + * @example + * + * .getConfig([stringKey]) + * + * @example Example 1: retrieve the entire configuration object + * + * var config = mixer.getConfig(); // Config { ... } + * + * @example Example 2: retrieve a named sub-object of configuration object + * + * var animation = mixer.getConfig('animation'); // ConfigAnimation { ... } + * + * @example Example 3: retrieve a value of configuration object via a dot-notation string key + * + * var effects = mixer.getConfig('animation.effects'); // 'fade scale' + * + * @public + * @instance + * @since 2.0.0 + * @param {string} [stringKey] A "dot-notation" string key + * @return {*} + */ + + getConfig: function(stringKey) { + var self = this, + value = null; + + if (!stringKey) { + value = self.config; + } else { + value = h.getProperty(self.config, stringKey); + } + + return self.callFilters('valueGetConfig', value, arguments); + }, + + /** + * Updates the configuration of the mixer, after it has been instantiated. + * + * See the Configuration Object documentation for a full list of avilable + * configuration options. + * + * @example + * + * .configure(config) + * + * @example Example 1: Updating animation options + * + * mixer.configure({ + * animation: { + * effects: 'fade translateX(-100%)', + * duration: 300 + * } + * }); + * + * @example Example 2: Removing a callback after it has been set + * + * var mixer; + * + * function handleMixEndOnce() { + * // Do something .. + * + * // Then nullify the callback + * + * mixer.configure({ + * callbacks: { + * onMixEnd: null + * } + * }); + * }; + * + * // Instantiate a mixer with a callback defined + * + * mixer = mixitup(containerEl, { + * callbacks: { + * onMixEnd: handleMixEndOnce + * } + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {object} config + * An object containing one of more configuration options. + * @return {void} + */ + + configure: function(config) { + var self = this; + + self.callActions('beforeConfigure', arguments); + + h.extend(self.config, config, true, true); + + self.callActions('afterConfigure', arguments); + }, + + /** + * Returns an object containing information about the current state of the + * mixer. See the State Object documentation for more information. + * + * NB: State objects are immutable and should therefore be regenerated + * after any operation. + * + * @example + * + * .getState(); + * + * @example Example: Retrieving a state object + * + * var state = mixer.getState(); + * + * console.log(state.totalShow + 'targets are currently shown'); + * + * @public + * @instance + * @since 2.0.0 + * @return {mixitup.State} An object reflecting the current state of the mixer. + */ + + getState: function() { + var self = this, + state = null; + + state = new mixitup.State(); + + h.extend(state, self.state); + + h.freeze(state); + + return self.callFilters('stateGetState', state, arguments); + }, + + /** + * Forces the re-indexing all targets within the container. + * + * This should only be used if some other piece of code in your application + * has manipulated the contents of your container, which should be avoided. + * + * If you need to add or remove target elements from the container, use + * the built-in `.insert()` or `.remove()` methods, and MixItUp will keep + * itself up to date. + * + * @example + * + * .forceRefresh() + * + * @example Example: Force refreshing the mixer after external DOM manipulation + * + * console.log(mixer.getState().totalShow); // 3 + * + * // An element is removed from the container via some external DOM manipulation code: + * + * containerEl.removeChild(containerEl.firstElementChild); + * + * // The mixer does not know that the number of targets has changed: + * + * console.log(mixer.getState().totalShow); // 3 + * + * mixer.forceRefresh(); + * + * // After forceRefresh, the mixer is in sync again: + * + * console.log(mixer.getState().totalShow); // 2 + * + * @public + * @instance + * @since 2.1.2 + * @return {void} + */ + + forceRefresh: function() { + var self = this; + + self.indexTargets(); + }, + + /** + * Forces the re-rendering of all targets when using the Dataset API. + * + * By default, targets are only re-rendered when `data.dirtyCheck` is + * enabled, and an item's data has changed when `dataset()` is called. + * + * The `forceRender()` method allows for the re-rendering of all targets + * in response to some arbitrary event, such as the changing of the target + * render function. + * + * Targets are rendered against their existing data. + * + * @example + * + * .forceRender() + * + * @example Example: Force render targets after changing the target render function + * + * console.log(container.innerHTML); // ... <span class="mix">Foo</span> ... + * + * mixer.configure({ + * render: { + * target: (item) => `<a href="/${item.slug}/" class="mix">${item.title}</a>` + * } + * }); + * + * mixer.forceRender(); + * + * console.log(container.innerHTML); // ... <a href="/foo/" class="mix">Foo</a> ... + * + * @public + * @instance + * @since 3.2.1 + * @return {void} + */ + + forceRender: function() { + var self = this, + target = null, + el = null, + id = ''; + + for (id in self.cache) { + target = self.cache[id]; + + el = target.render(target.data); + + if (el !== target.dom.el) { + // Update target element reference + + if (target.isInDom) { + target.unbindEvents(); + + self.dom.parent.replaceChild(el, target.dom.el); + } + + if (!target.isShown) { + el.style.display = 'none'; + } + + target.dom.el = el; + + if (target.isInDom) { + target.bindEvents(); + } + } + } + + self.state = self.buildState(self.lastOperation); + }, + + /** + * Removes mixitup functionality from the container, unbinds all control + * event handlers, and deletes the mixer instance from MixItUp's internal + * cache. + * + * This should be performed whenever a mixer's container is removed from + * the DOM, such as during a page change in a single page application, + * or React's `componentWillUnmount()`. + * + * @example + * + * .destroy([cleanUp]) + * + * @example Example: Destroying the mixer before removing its container element + * + * mixer.destroy(); + * + * containerEl.parentElement.removeChild(containerEl); + * + * @public + * @instance + * @since 2.0.0 + * @param {boolean} [cleanUp=false] + * An optional boolean dictating whether or not to clean up any inline `display: none;` styling applied to hidden targets. + * @return {void} + */ + + destroy: function(cleanUp) { + var self = this, + control = null, + target = null, + i = 0; + + self.callActions('beforeDestroy', arguments); + + for (i = 0; control = self.controls[i]; i++) { + control.removeBinding(self); + } + + for (i = 0; target = self.targets[i]; i++) { + if (cleanUp) { + target.show(); + } + + target.unbindEvents(); + } + + if (self.dom.container.id.match(/^MixItUp/)) { + self.dom.container.removeAttribute('id'); + } + + delete mixitup.instances[self.id]; + + self.callActions('afterDestroy', arguments); + } + }); + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.IMoveData = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.posIn = null; + this.posOut = null; + this.operation = null; + this.callback = null; + this.statusChange = ''; + this.duration = -1; + this.staggerIndex = -1; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.IMoveData); + + mixitup.IMoveData.prototype = Object.create(mixitup.Base.prototype); + + mixitup.IMoveData.prototype.constructor = mixitup.IMoveData; + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.TargetDom = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.el = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.TargetDom); + + mixitup.TargetDom.prototype = Object.create(mixitup.Base.prototype); + + mixitup.TargetDom.prototype.constructor = mixitup.TargetDom; + + /** + * @constructor + * @namespace + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.Target = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.id = ''; + this.sortString = ''; + this.mixer = null; + this.callback = null; + this.isShown = false; + this.isBound = false; + this.isExcluded = false; + this.isInDom = false; + this.handler = null; + this.operation = null; + this.data = null; + this.dom = new mixitup.TargetDom(); + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.Target); + + mixitup.Target.prototype = Object.create(mixitup.Base.prototype); + + h.extend(mixitup.Target.prototype, { + constructor: mixitup.Target, + + /** + * Initialises a newly instantiated Target. + * + * @private + * @instance + * @since 3.0.0 + * @param {(Element|null)} el + * @param {object} mixer + * @param {object} [data] + * @return {void} + */ + + init: function(el, mixer, data) { + var self = this, + id = ''; + + self.callActions('beforeInit', arguments); + + self.mixer = mixer; + + if (!el) { + // If no element is provided, render it + + el = self.render(data); + } + + self.cacheDom(el); + + self.bindEvents(); + + if (self.dom.el.style.display !== 'none') { + self.isShown = true; + } + + if (data && mixer.config.data.uidKey) { + if (typeof (id = data[mixer.config.data.uidKey]) === 'undefined' || id.toString().length < 1) { + throw new TypeError(mixitup.messages.errorDatasetInvalidUidKey({ + uidKey: mixer.config.data.uidKey + })); + } + + self.id = id; + self.data = data; + + mixer.cache[id] = self; + } + + self.callActions('afterInit', arguments); + }, + + /** + * Renders the target element using a user-defined renderer function. + * + * @private + * @instance + * @since 3.1.4 + * @param {object} data + * @return {void} + */ + + render: function(data) { + var self = this, + render = null, + el = null, + temp = null, + output = ''; + + self.callActions('beforeRender', arguments); + + render = self.callFilters('renderRender', self.mixer.config.render.target, arguments); + + if (typeof render !== 'function') { + throw new TypeError(mixitup.messages.errorDatasetRendererNotSet()); + } + + output = render(data); + + if (output && typeof output === 'object' && h.isElement(output)) { + el = output; + } else if (typeof output === 'string') { + temp = document.createElement('div'); + temp.innerHTML = output; + + el = temp.firstElementChild; + } + + return self.callFilters('elRender', el, arguments); + }, + + /** + * Caches references of DOM elements neccessary for the target's functionality. + * + * @private + * @instance + * @since 3.0.0 + * @param {Element} el + * @return {void} + */ + + cacheDom: function(el) { + var self = this; + + self.callActions('beforeCacheDom', arguments); + + self.dom.el = el; + + self.callActions('afterCacheDom', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {string} attributeName + * @return {void} + */ + + getSortString: function(attributeName) { + var self = this, + value = self.dom.el.getAttribute('data-' + attributeName) || ''; + + self.callActions('beforeGetSortString', arguments); + + value = isNaN(value * 1) ? + value.toLowerCase() : + value * 1; + + self.sortString = value; + + self.callActions('afterGetSortString', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @return {void} + */ + + show: function() { + var self = this; + + self.callActions('beforeShow', arguments); + + if (!self.isShown) { + self.dom.el.style.display = ''; + + self.isShown = true; + } + + self.callActions('afterShow', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @return {void} + */ + + hide: function() { + var self = this; + + self.callActions('beforeHide', arguments); + + if (self.isShown) { + self.dom.el.style.display = 'none'; + + self.isShown = false; + } + + self.callActions('afterHide', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {mixitup.IMoveData} moveData + * @return {void} + */ + + move: function(moveData) { + var self = this; + + self.callActions('beforeMove', arguments); + + if (!self.isExcluded) { + self.mixer.targetsMoved++; + } + + self.applyStylesIn(moveData); + + requestAnimationFrame(function() { + self.applyStylesOut(moveData); + }); + + self.callActions('afterMove', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {object} posData + * @param {number} multiplier + * @return {void} + */ + + applyTween: function(posData, multiplier) { + var self = this, + propertyName = '', + tweenData = null, + posIn = posData.posIn, + currentTransformValues = [], + currentValues = new mixitup.StyleData(), + i = -1; + + self.callActions('beforeApplyTween', arguments); + + currentValues.x = posIn.x; + currentValues.y = posIn.y; + + if (multiplier === 0) { + self.hide(); + } else if (!self.isShown) { + self.show(); + } + + for (i = 0; propertyName = mixitup.features.TWEENABLE[i]; i++) { + tweenData = posData.tweenData[propertyName]; + + if (propertyName === 'x') { + if (!tweenData) continue; + + currentValues.x = posIn.x + (tweenData * multiplier); + } else if (propertyName === 'y') { + if (!tweenData) continue; + + currentValues.y = posIn.y + (tweenData * multiplier); + } else if (tweenData instanceof mixitup.TransformData) { + if (!tweenData.value) continue; + + currentValues[propertyName].value = + posIn[propertyName].value + (tweenData.value * multiplier); + + currentValues[propertyName].unit = tweenData.unit; + + currentTransformValues.push( + propertyName + '(' + currentValues[propertyName].value + tweenData.unit + ')' + ); + } else { + if (!tweenData) continue; + + currentValues[propertyName] = posIn[propertyName] + (tweenData * multiplier); + + self.dom.el.style[propertyName] = currentValues[propertyName]; + } + } + + if (currentValues.x || currentValues.y) { + currentTransformValues.unshift('translate(' + currentValues.x + 'px, ' + currentValues.y + 'px)'); + } + + if (currentTransformValues.length) { + self.dom.el.style[mixitup.features.transformProp] = currentTransformValues.join(' '); + } + + self.callActions('afterApplyTween', arguments); + }, + + /** + * Applies the initial styling to a target element before any transition + * is applied. + * + * @private + * @instance + * @param {mixitup.IMoveData} moveData + * @return {void} + */ + + applyStylesIn: function(moveData) { + var self = this, + posIn = moveData.posIn, + isFading = self.mixer.effectsIn.opacity !== 1, + transformValues = []; + + self.callActions('beforeApplyStylesIn', arguments); + + transformValues.push('translate(' + posIn.x + 'px, ' + posIn.y + 'px)'); + + if (self.mixer.config.animation.animateResizeTargets) { + if (moveData.statusChange !== 'show') { + // Don't apply posIn width or height or showing, as will be 0 + + self.dom.el.style.width = posIn.width + 'px'; + self.dom.el.style.height = posIn.height + 'px'; + } + + self.dom.el.style.marginRight = posIn.marginRight + 'px'; + self.dom.el.style.marginBottom = posIn.marginBottom + 'px'; + } + + isFading && (self.dom.el.style.opacity = posIn.opacity); + + if (moveData.statusChange === 'show') { + transformValues = transformValues.concat(self.mixer.transformIn); + } + + self.dom.el.style[mixitup.features.transformProp] = transformValues.join(' '); + + self.callActions('afterApplyStylesIn', arguments); + }, + + /** + * Applies a transition followed by the final styles for the element to + * transition towards. + * + * @private + * @instance + * @param {mixitup.IMoveData} moveData + * @return {void} + */ + + applyStylesOut: function(moveData) { + var self = this, + transitionRules = [], + transformValues = [], + isResizing = self.mixer.config.animation.animateResizeTargets, + isFading = typeof self.mixer.effectsIn.opacity !== 'undefined'; + + self.callActions('beforeApplyStylesOut', arguments); + + // Build the transition rules + + transitionRules.push(self.writeTransitionRule( + mixitup.features.transformRule, + moveData.staggerIndex + )); + + if (moveData.statusChange !== 'none') { + transitionRules.push(self.writeTransitionRule( + 'opacity', + moveData.staggerIndex, + moveData.duration + )); + } + + if (isResizing) { + transitionRules.push(self.writeTransitionRule( + 'width', + moveData.staggerIndex, + moveData.duration + )); + + transitionRules.push(self.writeTransitionRule( + 'height', + moveData.staggerIndex, + moveData.duration + )); + + transitionRules.push(self.writeTransitionRule( + 'margin', + moveData.staggerIndex, + moveData.duration + )); + } + + // If no callback was provided, the element will + // not transition in any way so tag it as "immovable" + + if (!moveData.callback) { + self.mixer.targetsImmovable++; + + if (self.mixer.targetsMoved === self.mixer.targetsImmovable) { + // If the total targets moved is equal to the + // number of immovable targets, the operation + // should be considered finished + + self.mixer.cleanUp(moveData.operation); + } + + return; + } + + // If the target will transition in some fasion, + // assign a callback function + + self.operation = moveData.operation; + self.callback = moveData.callback; + + // As long as the target is not excluded, increment + // the total number of targets bound + + !self.isExcluded && self.mixer.targetsBound++; + + // Tag the target as bound to differentiate from transitionEnd + // events that may come from stylesheet driven effects + + self.isBound = true; + + // Apply the transition + + self.applyTransition(transitionRules); + + // Apply width, height and margin negation + + if (isResizing && moveData.posOut.width > 0 && moveData.posOut.height > 0) { + self.dom.el.style.width = moveData.posOut.width + 'px'; + self.dom.el.style.height = moveData.posOut.height + 'px'; + self.dom.el.style.marginRight = moveData.posOut.marginRight + 'px'; + self.dom.el.style.marginBottom = moveData.posOut.marginBottom + 'px'; + } + + if (!self.mixer.config.animation.nudge && moveData.statusChange === 'hide') { + // If we're not nudging, the translation should be + // applied before any other transforms to prevent + // lateral movement + + transformValues.push('translate(' + moveData.posOut.x + 'px, ' + moveData.posOut.y + 'px)'); + } + + // Apply fade + + switch (moveData.statusChange) { + case 'hide': + isFading && (self.dom.el.style.opacity = self.mixer.effectsOut.opacity); + + transformValues = transformValues.concat(self.mixer.transformOut); + + break; + case 'show': + isFading && (self.dom.el.style.opacity = 1); + } + + if ( + self.mixer.config.animation.nudge || + (!self.mixer.config.animation.nudge && moveData.statusChange !== 'hide') + ) { + // Opposite of above - apply translate after + // other transform + + transformValues.push('translate(' + moveData.posOut.x + 'px, ' + moveData.posOut.y + 'px)'); + } + + // Apply transforms + + self.dom.el.style[mixitup.features.transformProp] = transformValues.join(' '); + + self.callActions('afterApplyStylesOut', arguments); + }, + + /** + * Combines the name of a CSS property with the appropriate duration and delay + * values to created a valid transition rule. + * + * @private + * @instance + * @since 3.0.0 + * @param {string} property + * @param {number} staggerIndex + * @param {number} duration + * @return {string} + */ + + writeTransitionRule: function(property, staggerIndex, duration) { + var self = this, + delay = self.getDelay(staggerIndex), + rule = ''; + + rule = property + ' ' + + (duration > 0 ? duration : self.mixer.config.animation.duration) + 'ms ' + + delay + 'ms ' + + (property === 'opacity' ? 'linear' : self.mixer.config.animation.easing); + + return self.callFilters('ruleWriteTransitionRule', rule, arguments); + }, + + /** + * Calculates the transition delay for each target element based on its index, if + * staggering is applied. If defined, A custom `animation.staggerSeqeuence` + * function can be used to manipulate the order of indices to produce custom + * stagger effects (e.g. for use in a grid with irregular row lengths). + * + * @private + * @instance + * @since 2.0.0 + * @param {number} index + * @return {number} + */ + + getDelay: function(index) { + var self = this, + delay = -1; + + if (typeof self.mixer.config.animation.staggerSequence === 'function') { + index = self.mixer.config.animation.staggerSequence.call(self, index, self.state); + } + + delay = !!self.mixer.staggerDuration ? index * self.mixer.staggerDuration : 0; + + return self.callFilters('delayGetDelay', delay, arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {string[]} rules + * @return {void} + */ + + applyTransition: function(rules) { + var self = this, + transitionString = rules.join(', '); + + self.callActions('beforeApplyTransition', arguments); + + self.dom.el.style[mixitup.features.transitionProp] = transitionString; + + self.callActions('afterApplyTransition', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Event} e + * @return {void} + */ + + handleTransitionEnd: function(e) { + var self = this, + propName = e.propertyName, + canResize = self.mixer.config.animation.animateResizeTargets; + + self.callActions('beforeHandleTransitionEnd', arguments); + + if ( + self.isBound && + e.target.matches(self.mixer.config.selectors.target) && + ( + propName.indexOf('transform') > -1 || + propName.indexOf('opacity') > -1 || + canResize && propName.indexOf('height') > -1 || + canResize && propName.indexOf('width') > -1 || + canResize && propName.indexOf('margin') > -1 + ) + ) { + self.callback.call(self, self.operation); + + self.isBound = false; + self.callback = null; + self.operation = null; + } + + self.callActions('afterHandleTransitionEnd', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Event} e + * @return {void} + */ + + eventBus: function(e) { + var self = this; + + self.callActions('beforeEventBus', arguments); + + switch (e.type) { + case 'webkitTransitionEnd': + case 'transitionend': + self.handleTransitionEnd(e); + } + + self.callActions('afterEventBus', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @return {void} + */ + + unbindEvents: function() { + var self = this; + + self.callActions('beforeUnbindEvents', arguments); + + h.off(self.dom.el, 'webkitTransitionEnd', self.handler); + h.off(self.dom.el, 'transitionend', self.handler); + + self.callActions('afterUnbindEvents', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @return {void} + */ + + bindEvents: function() { + var self = this, + transitionEndEvent = ''; + + self.callActions('beforeBindEvents', arguments); + + transitionEndEvent = mixitup.features.transitionPrefix === 'webkit' ? 'webkitTransitionEnd' : 'transitionend'; + + self.handler = function(e) { + return self.eventBus(e); + }; + + h.on(self.dom.el, transitionEndEvent, self.handler); + + self.callActions('afterBindEvents', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {boolean} [getBox] + * @return {PosData} + */ + + getPosData: function(getBox) { + var self = this, + styles = {}, + rect = null, + posData = new mixitup.StyleData(); + + self.callActions('beforeGetPosData', arguments); + + posData.x = self.dom.el.offsetLeft; + posData.y = self.dom.el.offsetTop; + + if (self.mixer.config.animation.animateResizeTargets || getBox) { + rect = self.dom.el.getBoundingClientRect(); + + posData.top = rect.top; + posData.right = rect.right; + posData.bottom = rect.bottom; + posData.left = rect.left; + + posData.width = rect.width; + posData.height = rect.height; + } + + if (self.mixer.config.animation.animateResizeTargets) { + styles = window.getComputedStyle(self.dom.el); + + posData.marginBottom = parseFloat(styles.marginBottom); + posData.marginRight = parseFloat(styles.marginRight); + } + + return self.callFilters('posDataGetPosData', posData, arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @return {void} + */ + + cleanUp: function() { + var self = this; + + self.callActions('beforeCleanUp', arguments); + + self.dom.el.style[mixitup.features.transformProp] = ''; + self.dom.el.style[mixitup.features.transitionProp] = ''; + self.dom.el.style.opacity = ''; + + if (self.mixer.config.animation.animateResizeTargets) { + self.dom.el.style.width = ''; + self.dom.el.style.height = ''; + self.dom.el.style.marginRight = ''; + self.dom.el.style.marginBottom = ''; + } + + self.callActions('afterCleanUp', arguments); + } + }); + + /** + * A jQuery-collection-like wrapper around one or more `mixitup.Mixer` instances + * allowing simultaneous control of said instances similar to the MixItUp 2 API. + * + * @example + * new mixitup.Collection(instances) + * + * @constructor + * @namespace + * @memberof mixitup + * @private + * @since 3.0.0 + * @param {mixitup.Mixer[]} instances + */ + + mixitup.Collection = function(instances) { + var instance = null, + i = -1; + + this.callActions('beforeConstruct'); + + for (i = 0; instance = instances[i]; i++) { + this[i] = instance; + } + + this.length = instances.length; + + this.callActions('afterConstruct'); + + h.freeze(this); + }; + + mixitup.BaseStatic.call(mixitup.Collection); + + mixitup.Collection.prototype = Object.create(mixitup.Base.prototype); + + h.extend(mixitup.Collection.prototype, + /** @lends mixitup.Collection */ + { + constructor: mixitup.Collection, + + /** + * Calls a method on all instances in the collection by passing the method + * name as a string followed by any applicable parameters to be curried into + * to the method. + * + * @example + * .mixitup(methodName[,arg1][,arg2..]); + * + * @example + * var collection = new Collection([mixer1, mixer2]); + * + * return collection.mixitup('filter', '.category-a') + * .then(function(states) { + * state.forEach(function(state) { + * console.log(state.activeFilter.selector); // .category-a + * }); + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {string} methodName + * @return {Promise>} + */ + + mixitup: function(methodName) { + var self = this, + instance = null, + args = Array.prototype.slice.call(arguments), + tasks = [], + i = -1; + + this.callActions('beforeMixitup'); + + args.shift(); + + for (i = 0; instance = self[i]; i++) { + tasks.push(instance[methodName].apply(instance, args)); + } + + return self.callFilters('promiseMixitup', h.all(tasks, mixitup.libraries), arguments); + } + }); + + /** + * `mixitup.Operation` objects contain all data neccessary to describe the full + * lifecycle of any MixItUp operation. They can be used to compute and store an + * operation for use at a later time (e.g. programmatic tweening). + * + * @constructor + * @namespace + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.Operation = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.id = ''; + + this.args = []; + this.command = null; + this.showPosData = []; + this.toHidePosData = []; + + this.startState = null; + this.newState = null; + this.docState = null; + + this.willSort = false; + this.willChangeLayout = false; + this.hasEffect = false; + this.hasFailed = false; + + this.triggerElement = null; + + this.show = []; + this.hide = []; + this.matching = []; + this.toShow = []; + this.toHide = []; + this.toMove = []; + this.toRemove = []; + this.startOrder = []; + this.newOrder = []; + this.startSort = null; + this.newSort = null; + this.startFilter = null; + this.newFilter = null; + this.startDataset = null; + this.newDataset = null; + this.viewportDeltaX = 0; + this.viewportDeltaY = 0; + this.startX = 0; + this.startY = 0; + this.startHeight = 0; + this.startWidth = 0; + this.newX = 0; + this.newY = 0; + this.newHeight = 0; + this.newWidth = 0; + this.startContainerClassName = ''; + this.startDisplay = ''; + this.newContainerClassName = ''; + this.newDisplay = ''; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.Operation); + + mixitup.Operation.prototype = Object.create(mixitup.Base.prototype); + + mixitup.Operation.prototype.constructor = mixitup.Operation; + + /** + * `mixitup.State` objects expose various pieces of data detailing the state of + * a MixItUp instance. They are provided at the start and end of any operation via + * callbacks and events, with the most recent state stored between operations + * for retrieval at any time via the API. + * + * @constructor + * @namespace + * @memberof mixitup + * @public + * @since 3.0.0 + */ + + mixitup.State = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * The ID of the mixer instance. + * + * @name id + * @memberof mixitup.State + * @instance + * @type {string} + * @default '' + */ + + this.id = ''; + + /** + * The currently active filter command as set by a control click or API call. + * + * @name activeFilter + * @memberof mixitup.State + * @instance + * @type {mixitup.CommandFilter} + * @default null + */ + + this.activeFilter = null; + + /** + * The currently active sort command as set by a control click or API call. + * + * @name activeSort + * @memberof mixitup.State + * @instance + * @type {mixitup.CommandSort} + * @default null + */ + + this.activeSort = null; + + /** + * The current layout-specific container class name, if applied. + * + * @name activeContainerClassName + * @memberof mixitup.State + * @instance + * @type {string} + * @default '' + */ + + this.activeContainerClassName = ''; + + /** + * A reference to the container element that the mixer is instantiated on. + * + * @name container + * @memberof mixitup.State + * @instance + * @type {Element} + * @default null + */ + + this.container = null; + + /** + * An array of all target elements indexed by the mixer. + * + * @name targets + * @memberof mixitup.State + * @instance + * @type {Array.} + * @default [] + */ + + this.targets = []; + + /** + * An array of all target elements not matching the current filter. + * + * @name hide + * @memberof mixitup.State + * @instance + * @type {Array.} + * @default [] + */ + + this.hide = []; + + /** + * An array of all target elements matching the current filter and any additional + * limits applied such as pagination. + * + * @name show + * @memberof mixitup.State + * @instance + * @type {Array.} + * @default [] + */ + + this.show = []; + + /** + * An array of all target elements matching the current filter irrespective of + * any additional limits applied such as pagination. + * + * @name matching + * @memberof mixitup.State + * @instance + * @type {Array.} + * @default [] + */ + + this.matching = []; + + /** + * An integer representing the total number of target elements indexed by the + * mixer. Equivalent to `state.targets.length`. + * + * @name totalTargets + * @memberof mixitup.State + * @instance + * @type {number} + * @default -1 + */ + + this.totalTargets = -1; + + /** + * An integer representing the total number of target elements matching the + * current filter and any additional limits applied such as pagination. + * Equivalent to `state.show.length`. + * + * @name totalShow + * @memberof mixitup.State + * @instance + * @type {number} + * @default -1 + */ + + this.totalShow = -1; + + /** + * An integer representing the total number of target elements not matching + * the current filter. Equivalent to `state.hide.length`. + * + * @name totalHide + * @memberof mixitup.State + * @instance + * @type {number} + * @default -1 + */ + + this.totalHide = -1; + + /** + * An integer representing the total number of target elements matching the + * current filter irrespective of any other limits applied such as pagination. + * Equivalent to `state.matching.length`. + * + * @name totalMatching + * @memberof mixitup.State + * @instance + * @type {number} + * @default -1 + */ + + this.totalMatching = -1; + + /** + * A boolean indicating whether the last operation "failed", i.e. no targets + * could be found matching the filter. + * + * @name hasFailed + * @memberof mixitup.State + * @instance + * @type {boolean} + * @default false + */ + + this.hasFailed = false; + + /** + * The DOM element that was clicked if the last operation was triggered by the + * clicking of a control and not an API call. + * + * @name triggerElement + * @memberof mixitup.State + * @instance + * @type {Element|null} + * @default null + */ + + this.triggerElement = null; + + /** + * The currently active dataset underlying the rendered targets, if the + * dataset API is in use. + * + * @name activeDataset + * @memberof mixitup.State + * @instance + * @type {Array.} + * @default null + */ + + this.activeDataset = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.State); + + mixitup.State.prototype = Object.create(mixitup.Base.prototype); + + mixitup.State.prototype.constructor = mixitup.State; + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.UserInstruction = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.command = {}; + this.animate = false; + this.callback = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.UserInstruction); + + mixitup.UserInstruction.prototype = Object.create(mixitup.Base.prototype); + + mixitup.UserInstruction.prototype.constructor = mixitup.UserInstruction; + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.Messages = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /* Errors + ----------------------------------------------------------------------------- */ + + this.ERROR_FACTORY_INVALID_CONTAINER = + '[MixItUp] An invalid selector or element reference was passed to the mixitup factory function'; + + this.ERROR_FACTORY_CONTAINER_NOT_FOUND = + '[MixItUp] The provided selector yielded no container element'; + + this.ERROR_CONFIG_INVALID_ANIMATION_EFFECTS = + '[MixItUp] Invalid value for `animation.effects`'; + + this.ERROR_CONFIG_INVALID_CONTROLS_SCOPE = + '[MixItUp] Invalid value for `controls.scope`'; + + this.ERROR_CONFIG_INVALID_PROPERTY = + '[MixitUp] Invalid configuration object property "${erroneous}"${suggestion}'; + + this.ERROR_CONFIG_INVALID_PROPERTY_SUGGESTION = + '. Did you mean "${probableMatch}"?'; + + this.ERROR_CONFIG_DATA_UID_KEY_NOT_SET = + '[MixItUp] To use the dataset API, a UID key must be specified using `data.uidKey`'; + + this.ERROR_DATASET_INVALID_UID_KEY = + '[MixItUp] The specified UID key "${uidKey}" is not present on one or more dataset items'; + + this.ERROR_DATASET_DUPLICATE_UID = + '[MixItUp] The UID "${uid}" was found on two or more dataset items. UIDs must be unique.'; + + this.ERROR_INSERT_INVALID_ARGUMENTS = + '[MixItUp] Please provider either an index or a sibling and position to insert, not both'; + + this.ERROR_INSERT_PREEXISTING_ELEMENT = + '[MixItUp] An element to be inserted already exists in the container'; + + this.ERROR_FILTER_INVALID_ARGUMENTS = + '[MixItUp] Please provide either a selector or collection `.filter()`, not both'; + + this.ERROR_DATASET_NOT_SET = + '[MixItUp] To use the dataset API with pre-rendered targets, a starting dataset must be set using `load.dataset`'; + + this.ERROR_DATASET_PRERENDERED_MISMATCH = + '[MixItUp] `load.dataset` does not match pre-rendered targets'; + + this.ERROR_DATASET_RENDERER_NOT_SET = + '[MixItUp] To insert an element via the dataset API, a target renderer function must be provided to `render.target`'; + + this.ERROR_SORT_NON_EXISTENT_ELEMENT = + '[MixItUp] An element to be sorted does not already exist in the container'; + + /* Warnings + ----------------------------------------------------------------------------- */ + + this.WARNING_FACTORY_PREEXISTING_INSTANCE = + '[MixItUp] WARNING: This element already has an active MixItUp instance. The provided configuration object will be ignored.' + + ' If you wish to perform additional methods on this instance, please create a reference.'; + + this.WARNING_INSERT_NO_ELEMENTS = + '[MixItUp] WARNING: No valid elements were passed to `.insert()`'; + + this.WARNING_REMOVE_NO_ELEMENTS = + '[MixItUp] WARNING: No valid elements were passed to `.remove()`'; + + this.WARNING_MULTIMIX_INSTANCE_QUEUE_FULL = + '[MixItUp] WARNING: An operation was requested but the MixItUp instance was busy. The operation was rejected because the ' + + 'queue is full or queuing is disabled.'; + + this.WARNING_GET_OPERATION_INSTANCE_BUSY = + '[MixItUp] WARNING: Operations can be be created while the MixItUp instance is busy.'; + + this.WARNING_NO_PROMISE_IMPLEMENTATION = + '[MixItUp] WARNING: No Promise implementations could be found. If you wish to use promises with MixItUp please install' + + ' an ES6 Promise polyfill.'; + + this.WARNING_INCONSISTENT_SORTING_ATTRIBUTES = + '[MixItUp] WARNING: The requested sorting data attribute "${attribute}" was not present on one or more target elements' + + ' which may product unexpected sort output'; + + this.callActions('afterConstruct'); + + this.compileTemplates(); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.Messages); + + mixitup.Messages.prototype = Object.create(mixitup.Base.prototype); + + mixitup.Messages.prototype.constructor = mixitup.Messages; + + /** + * @return {void} + */ + + mixitup.Messages.prototype.compileTemplates = function() { + var errorKey = ''; + var errorMessage = ''; + + for (errorKey in this) { + if (typeof (errorMessage = this[errorKey]) !== 'string') continue; + + this[h.camelCase(errorKey)] = h.template(errorMessage); + } + }; + + mixitup.messages = new mixitup.Messages(); + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + * @param {mixitup.Mixer} mixer + */ + + mixitup.Facade = function Mixer(mixer) { + mixitup.Base.call(this); + + this.callActions('beforeConstruct', arguments); + + this.configure = mixer.configure.bind(mixer); + this.show = mixer.show.bind(mixer); + this.hide = mixer.hide.bind(mixer); + this.filter = mixer.filter.bind(mixer); + this.toggleOn = mixer.toggleOn.bind(mixer); + this.toggleOff = mixer.toggleOff.bind(mixer); + this.sort = mixer.sort.bind(mixer); + this.changeLayout = mixer.changeLayout.bind(mixer); + this.multimix = mixer.multimix.bind(mixer); + this.dataset = mixer.dataset.bind(mixer); + this.tween = mixer.tween.bind(mixer); + this.insert = mixer.insert.bind(mixer); + this.insertBefore = mixer.insertBefore.bind(mixer); + this.insertAfter = mixer.insertAfter.bind(mixer); + this.prepend = mixer.prepend.bind(mixer); + this.append = mixer.append.bind(mixer); + this.remove = mixer.remove.bind(mixer); + this.destroy = mixer.destroy.bind(mixer); + this.forceRefresh = mixer.forceRefresh.bind(mixer); + this.forceRender = mixer.forceRender.bind(mixer); + this.isMixing = mixer.isMixing.bind(mixer); + this.getOperation = mixer.getOperation.bind(mixer); + this.getConfig = mixer.getConfig.bind(mixer); + this.getState = mixer.getState.bind(mixer); + + this.callActions('afterConstruct', arguments); + + h.freeze(this); + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.Facade); + + mixitup.Facade.prototype = Object.create(mixitup.Base.prototype); + + mixitup.Facade.prototype.constructor = mixitup.Facade; + + if (typeof exports === 'object' && typeof module === 'object') { + module.exports = mixitup; + } else if (typeof define === 'function' && define.amd) { + define(function() { + return mixitup; + }); + } else if (typeof window.mixitup === 'undefined' || typeof window.mixitup !== 'function') { + window.mixitup = mixitup; + } + mixitup.BaseStatic.call(mixitup.constructor); + + mixitup.NAME = 'mixitup'; + mixitup.CORE_VERSION = '3.3.1'; +})(window); \ No newline at end of file diff --git a/mixitup-3.3.1/dist/mixitup.min.js b/mixitup-3.3.1/dist/mixitup.min.js new file mode 100644 index 0000000..8562101 --- /dev/null +++ b/mixitup-3.3.1/dist/mixitup.min.js @@ -0,0 +1,18 @@ +/**! + * MixItUp v3.3.1 + * A high-performance, dependency-free library for animated filtering, sorting and more + * Build 94e0fbf6-cd0b-4987-b3c0-14b59b67b8a0 + * + * @copyright Copyright 2014-2018 KunkaLabs Limited. + * @author KunkaLabs Limited. + * @link https://www.kunkalabs.com/mixitup/ + * + * @license Commercial use requires a commercial license. + * https://www.kunkalabs.com/mixitup/licenses/ + * + * Non-commercial use permitted under same terms as CC BY-NC 3.0 license. + * http://creativecommons.org/licenses/by-nc/3.0/ + */ +!function(t){"use strict";var e=null,n=null;!function(){var e=["webkit","moz","o","ms"],n=t.document.createElement("div"),a=-1;for(a=0;a-1}}(t.Element.prototype),Object.keys||(Object.keys=function(){var t=Object.prototype.hasOwnProperty,e=!1,n=[],a=-1;return e=!{toString:null}.propertyIsEnumerable("toString"),n=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],a=n.length,function(i){var o=[],r="",s=-1;if("object"!=typeof i&&("function"!=typeof i||null===i))throw new TypeError("Object.keys called on non-object");for(r in i)t.call(i,r)&&o.push(r);if(e)for(s=0;s>>0,0===i)return-1;if(e=0,arguments.length>1&&(e=Number(arguments[1]),e!==e?e=0:0!==e&&e!==1/0&&e!==-(1/0)&&(e=(e>0||-1)*Math.floor(Math.abs(e)))),e>=i)return-1;for(n=e>=0?e:Math.max(i-Math.abs(e),0);n0)||s);g++)r.id?d=r.id:(d="MixItUp"+n.randomHex(),r.id=d),e.instances[d]instanceof e.Mixer?(l=e.instances[d],(!i||i&&i.debug&&i.debug.showWarnings!==!1)&&console.warn(e.messages.warningFactoryPreexistingInstance())):(l=new e.Mixer,l.attach(r,u,d,i),e.instances[d]=l),c=new e.Facade(l),i&&i.debug&&i.debug.enable?h.push(l):h.push(c);return f=s?new e.Collection(h):h[0]},e.use=function(t){e.Base.prototype.callActions.call(e,"beforeUse",arguments),"function"==typeof t&&"mixitup-extension"===t.TYPE?"undefined"==typeof e.extensions[t.NAME]&&(t(e),e.extensions[t.NAME]=t):t.fn&&t.fn.jquery&&(e.libraries.$=t),e.Base.prototype.callActions.call(e,"afterUse",arguments)},e.instances={},e.extensions={},e.libraries={},n={hasClass:function(t,e){return!!t.className.match(new RegExp("(\\s|^)"+e+"(\\s|$)"))},addClass:function(t,e){this.hasClass(t,e)||(t.className+=t.className?" "+e:e)},removeClass:function(t,e){if(this.hasClass(t,e)){var n=new RegExp("(\\s|^)"+e+"(\\s|$)");t.className=t.className.replace(n," ").trim()}},extend:function(t,e,n,a){var i=[],o="",r=-1;n=n||!1,a=a||!1;try{if(Array.isArray(e))for(r=0;ru&&(u=f,l=c)}throw u>1&&(s=e.messages.errorConfigInvalidPropertySuggestion({probableMatch:l})),r=e.messages.errorConfigInvalidProperty({erroneous:o,suggestion:s}),new TypeError(r)}throw t},template:function(t){for(var e=/\${([\w]*)}/g,n={},a=null;a=e.exec(t);)n[a[1]]=new RegExp("\\${"+a[1]+"}","g");return function(e){var a="",i=t;e=e||{};for(a in n)i=i.replace(n[a],"undefined"!=typeof e[a]?e[a]:"");return i}},on:function(e,n,a,i){e&&(e.addEventListener?e.addEventListener(n,a,i):e.attachEvent&&(e["e"+n+a]=a,e[n+a]=function(){e["e"+n+a](t.event)},e.attachEvent("on"+n,e[n+a])))},off:function(t,e,n){t&&(t.removeEventListener?t.removeEventListener(e,n,!1):t.detachEvent&&(t.detachEvent("on"+e,t[e+n]),t[e+n]=null))},getCustomEvent:function(e,n,a){var i=null;return a=a||t.document,"function"==typeof t.CustomEvent?i=new t.CustomEvent(e,{detail:n,bubbles:!0,cancelable:!0}):"function"==typeof a.createEvent?(i=a.createEvent("CustomEvent"),i.initCustomEvent(e,!0,!0,n)):(i=a.createEventObject(),i.type=e,i.returnValue=!1,i.cancelBubble=!1,i.detail=n),i},getOriginalEvent:function(t){return t.touches&&t.touches.length?t.touches[0]:t.changedTouches&&t.changedTouches.length?t.changedTouches[0]:t},index:function(t,e){for(var n=0;null!==(t=t.previousElementSibling);)e&&!t.matches(e)||++n;return n},camelCase:function(t){return t.toLowerCase().replace(/([_-][a-z])/g,function(t){return t.toUpperCase().replace(/[_-]/,"")})},pascalCase:function(t){return(t=this.camelCase(t)).charAt(0).toUpperCase()+t.slice(1)},dashCase:function(t){return t.replace(/([A-Z])/g,"-$1").replace(/^-/,"").toLowerCase()},isElement:function(e,n){return n=n||t.document,!!(t.HTMLElement&&e instanceof t.HTMLElement)||(!!(n.defaultView&&n.defaultView.HTMLElement&&e instanceof n.defaultView.HTMLElement)||null!==e&&1===e.nodeType&&"string"==typeof e.nodeName)},createElement:function(e,n){var a=null,i=null;for(n=n||t.document,a=n.createDocumentFragment(),i=n.createElement("div"),i.innerHTML=e.trim();i.firstChild;)a.appendChild(i.firstChild);return a},removeWhitespace:function(t){for(var e;t&&"#text"===t.nodeName;)e=t,t=t.previousSibling,e.parentElement&&e.parentElement.removeChild(e)},isEqualArray:function(t,e){var n=t.length;if(n!==e.length)return!1;for(;n--;)if(t[n]!==e[n])return!1;return!0},deepEquals:function(t,e){var n;if("object"==typeof t&&t&&"object"==typeof e&&e){if(Object.keys(t).length!==Object.keys(e).length)return!1;for(n in t)if(!e.hasOwnProperty(n)||!this.deepEquals(t[n],e[n]))return!1}else if(t!==e)return!1;return!0},arrayShuffle:function(t){for(var e=t.slice(),n=e.length,a=n,i=-1,o=[];a--;)i=~~(Math.random()*n),o=e[a],e[a]=e[i],e[i]=o;return e},arrayFromList:function(t){var e,n;try{return Array.prototype.slice.call(t)}catch(a){for(e=[],n=0;n "+n),o&&e.removeAttribute("id")),i},clean:function(t){var e=[],n=-1;for(n=0;ni)return!0}return!0},Deferred:function(){this.promise=null,this.resolve=null,this.reject=null,this.id=n.randomHex()},isEmptyObject:function(t){var e="";if("function"==typeof Object.keys)return 0===Object.keys(t).length;for(e in t)if(t.hasOwnProperty(e))return!1;return!0},getClassname:function(t,e,n){var a="";return a+=t.block,a.length&&(a+=t.delineatorElement),a+=t["element"+this.pascalCase(e)],n?(a.length&&(a+=t.delineatorModifier),a+=n):a},getProperty:function(t,e){var n=e.split("."),a=null,i="",o=0;if(!e)return t;for(a=function(t){return t?t[i]:null};o-1,e.callFilters("afterIsBound",n,arguments)},addBinding:function(t){var e=this;this.callActions("beforeAddBinding",arguments),e.isBound()||e.bound.push(t),this.callActions("afterAddBinding",arguments)},removeBinding:function(t){var n=this,a=-1;this.callActions("beforeRemoveBinding",arguments),(a=n.bound.indexOf(t))>-1&&n.bound.splice(a,1),n.bound.length<1&&(n.unbindClick(),a=e.controls.indexOf(n),e.controls.splice(a,1),"active"===n.status&&n.renderStatus(n.el,"inactive")),this.callActions("afterRemoveBinding",arguments)},bindClick:function(){var t=this;this.callActions("beforeBindClick",arguments),t.handler=function(e){t.handleClick(e)},n.on(t.el,"click",t.handler),this.callActions("afterBindClick",arguments)},unbindClick:function(){var t=this;this.callActions("beforeUnbindClick",arguments),n.off(t.el,"click",t.handler),t.handler=null,this.callActions("afterUnbindClick",arguments)},handleClick:function(t){var a=this,i=null,o=null,r=!1,s=void 0,l={},c=null,u=[],f=-1;if(this.callActions("beforeHandleClick",arguments),this.pending=0,o=a.bound[0],i=a.selector?n.closestParent(t.target,o.config.selectors.control+a.selector,!0,o.dom.document):a.el,!i)return void a.callActions("afterHandleClick",arguments);switch(a.type){case"filter":l.filter=a.filter||i.getAttribute("data-filter");break;case"sort":l.sort=a.sort||i.getAttribute("data-sort");break;case"multimix":l.filter=a.filter||i.getAttribute("data-filter"),l.sort=a.sort||i.getAttribute("data-sort");break;case"toggle":l.filter=a.filter||i.getAttribute("data-toggle"),r="live"===a.status?n.hasClass(i,a.classNames.active):"active"===a.status}for(f=0;f0||("live"===a.status?a.updateLive(t,n):(i.sort=a.sort,i.filter=a.filter,a.callFilters("actionsUpdate",i,arguments),a.parseStatusChange(a.el,t,i,n)),a.callActions("afterUpdate",arguments))},updateLive:function(t,n){var a=this,i=null,o=null,r=null,s=-1;if(a.callActions("beforeUpdateLive",arguments),a.el){for(i=a.el.querySelectorAll(a.selector),s=0;r=i[s];s++){switch(o=new e.CommandMultimix,a.type){case"filter":o.filter=r.getAttribute("data-filter");break;case"sort":o.sort=r.getAttribute("data-sort");break;case"multimix":o.filter=r.getAttribute("data-filter"),o.sort=r.getAttribute("data-sort");break;case"toggle":o.filter=r.getAttribute("data-toggle")}o=a.callFilters("actionsUpdateLive",o,arguments),a.parseStatusChange(r,t,o,n)}a.callActions("afterUpdateLive",arguments)}},parseStatusChange:function(t,e,n,a){var i=this,o="",r="",s=-1;switch(i.callActions("beforeParseStatusChange",arguments),i.type){case"filter":e.filter===n.filter?i.renderStatus(t,"active"):i.renderStatus(t,"inactive");break;case"multimix":e.sort===n.sort&&e.filter===n.filter?i.renderStatus(t,"active"):i.renderStatus(t,"inactive");break;case"sort":e.sort.match(/:asc/g)&&(o=e.sort.replace(/:asc/g,"")),e.sort===n.sort||o===n.sort?i.renderStatus(t,"active"):i.renderStatus(t,"inactive");break;case"toggle":for(a.length<1&&i.renderStatus(t,"inactive"),e.filter===n.filter&&i.renderStatus(t,"active"),s=0;s-1)throw new Error(e.messages.errorInsertPreexistingElement());c.style.display="none",s.appendChild(c),s.appendChild(i.dom.document.createTextNode(" ")),n.isElement(c,i.dom.document)&&c.matches(i.config.selectors.target)&&(l=new e.Target,l.init(c,i),l.isInDom=!0,i.targets.splice(r,0,l),r++)}i.dom.parent.insertBefore(s,o)}a.startOrder=i.origOrder=i.targets,i.callActions("afterInsertTargets",arguments)},getNextSibling:function(t,e,n){var a=this,i=null;return t=Math.max(t,0),e&&"before"===n?i=e:e&&"after"===n?i=e.nextElementSibling||null:a.targets.length>0&&"undefined"!=typeof t?i=t0&&(a.config.layout.siblingAfter?i=a.config.layout.siblingAfter:a.config.layout.siblingBefore?i=a.config.layout.siblingBefore.nextElementSibling:a.dom.parent.children[0]),a.callFilters("elementGetNextSibling",i,arguments)},filterOperation:function(t){var e=this,n=!1,a=-1,i="",o=null,r=-1;for(e.callActions("beforeFilterOperation",arguments),i=t.newFilter.action,r=0;o=t.newOrder[r];r++)n=t.newFilter.collection?t.newFilter.collection.indexOf(o.dom.el)>-1:""!==t.newFilter.selector&&o.dom.el.matches(t.newFilter.selector),e.evaluateHideShow(n,o,i,t);if(t.toRemove.length)for(r=0;o=t.show[r];r++)t.toRemove.indexOf(o)>-1&&(t.show.splice(r,1),(a=t.toShow.indexOf(o))>-1&&t.toShow.splice(a,1),t.toHide.push(o),t.hide.push(o),r--);t.matching=t.show.slice(),0===t.show.length&&""!==t.newFilter.selector&&0!==e.targets.length&&(t.hasFailed=!0),e.callActions("afterFilterOperation",arguments)},evaluateHideShow:function(t,e,n,a){var i=this,o=!1,r=Array.prototype.slice.call(arguments,1);o=i.callFilters("testResultEvaluateHideShow",t,r),i.callActions("beforeEvaluateHideShow",arguments),o===!0&&"show"===n||o===!1&&"hide"===n?(a.show.push(e),!e.isShown&&a.toShow.push(e)):(a.hide.push(e),e.isShown&&a.toHide.push(e)),i.callActions("afterEvaluateHideShow",arguments)},sortOperation:function(t){var a=this,i=[],o=null,r=null,s=-1;if(a.callActions("beforeSortOperation",arguments),t.startOrder=a.targets,t.newSort.collection){for(i=[],s=0;r=t.newSort.collection[s];s++){if(a.dom.targets.indexOf(r)<0)throw new Error(e.messages.errorSortNonExistentElement());o=new e.Target,o.init(r,a),o.isInDom=!0,i.push(o)}t.newOrder=i}else"random"===t.newSort.order?t.newOrder=n.arrayShuffle(t.startOrder):""===t.newSort.attribute?(t.newOrder=a.origOrder.slice(),"desc"===t.newSort.order&&t.newOrder.reverse()):(t.newOrder=t.startOrder.slice(),t.newOrder.sort(function(e,n){return a.compare(e,n,t.newSort)}));n.isEqualArray(t.newOrder,t.startOrder)&&(t.willSort=!1),a.callActions("afterSortOperation",arguments)},compare:function(t,e,n){var a=this,i=n.order,o=a.getAttributeValue(t,n.attribute),r=a.getAttributeValue(e,n.attribute);return isNaN(1*o)||isNaN(1*r)?(o=o.toLowerCase(),r=r.toLowerCase()):(o=1*o,r=1*r),or?"asc"===i?1:-1:o===r&&n.next?a.compare(t,e,n.next):0},getAttributeValue:function(t,n){var a=this,i="";return i=t.dom.el.getAttribute("data-"+n),null===i&&a.config.debug.showWarnings&&console.warn(e.messages.warningInconsistentSortingAttributes({attribute:"data-"+n})),a.callFilters("valueGetAttributeValue",i||0,arguments)},printSort:function(e,a){var i=this,o=e?a.newOrder:a.startOrder,r=e?a.startOrder:a.newOrder,s=o.length?o[o.length-1].dom.el.nextElementSibling:null,l=t.document.createDocumentFragment(),c=null,u=null,f=null,h=-1;for(i.callActions("beforePrintSort",arguments),h=0;u=o[h];h++)f=u.dom.el,"absolute"!==f.style.position&&(n.removeWhitespace(f.previousSibling),f.parentElement.removeChild(f));for(c=s?s.previousSibling:i.dom.parent.lastChild,c&&"#text"===c.nodeName&&n.removeWhitespace(c),h=0;u=r[h];h++)f=u.dom.el,n.isElement(l.lastChild)&&l.appendChild(t.document.createTextNode(" ")),l.appendChild(f);i.dom.parent.firstChild&&i.dom.parent.firstChild!==s&&l.insertBefore(t.document.createTextNode(" "),l.childNodes[0]),s?(l.appendChild(t.document.createTextNode(" ")),i.dom.parent.insertBefore(l,s)):i.dom.parent.appendChild(l),i.callActions("afterPrintSort",arguments)},parseSortString:function(t,a){var i=this,o=t.split(" "),r=a,s=[],l=-1;for(l=0;l-1&&(c=n.substring(l),u=s.exec(c),f=u[1]),t){case"fade":a.opacity=f?parseFloat(f):0;break;case"stagger":r.staggerDuration=f?parseFloat(f):100;break;default:if(o&&r.config.animation.reverseOut&&"scale"!==t?a[t].value=(f?parseFloat(f):e.transformDefaults[t].value)*-1:a[t].value=f?parseFloat(f):e.transformDefaults[t].value,f){for(m=0;d=h[m];m++)if(f.indexOf(d)>-1){a[t].unit=d;break}}else a[t].unit=e.transformDefaults[t].unit;i.push(t+"("+a[t].value+a[t].unit+")")}r.callActions("afterParseEffect",arguments)},buildState:function(t){var n=this,a=new e.State,i=null,o=-1;for(n.callActions("beforeBuildState",arguments),o=0;i=n.targets[o];o++)(!t.toRemove.length||t.toRemove.indexOf(i)<0)&&a.targets.push(i.dom.el);for(o=0;i=t.matching[o];o++)a.matching.push(i.dom.el);for(o=0;i=t.show[o];o++)a.show.push(i.dom.el);for(o=0;i=t.hide[o];o++)(!t.toRemove.length||t.toRemove.indexOf(i)<0)&&a.hide.push(i.dom.el);return a.id=n.id,a.container=n.dom.container,a.activeFilter=t.newFilter,a.activeSort=t.newSort,a.activeDataset=t.newDataset,a.activeContainerClassName=t.newContainerClassName,a.hasFailed=t.hasFailed,a.totalTargets=n.targets.length,a.totalShow=t.show.length,a.totalHide=t.hide.length,a.totalMatching=t.matching.length,a.triggerElement=t.triggerElement,n.callFilters("stateBuildState",a,arguments)},goMix:function(a,i){var o=this,r=null;return o.callActions("beforeGoMix",arguments),o.config.animation.duration&&o.config.animation.effects&&n.isVisible(o.dom.container)||(a=!1),i.toShow.length||i.toHide.length||i.willSort||i.willChangeLayout||(a=!1),i.startState.show.length||i.show.length||(a=!1),e.events.fire("mixStart",o.dom.container,{state:i.startState,futureState:i.newState,instance:o},o.dom.document),"function"==typeof o.config.callbacks.onMixStart&&o.config.callbacks.onMixStart.call(o.dom.container,i.startState,i.newState,o),n.removeClass(o.dom.container,n.getClassname(o.config.classNames,"container",o.config.classNames.modifierFailed)),r=o.userDeferred?o.userDeferred:o.userDeferred=n.defer(e.libraries),o.isBusy=!0,a&&e.features.has.transitions?(t.pageYOffset!==i.docState.scrollTop&&t.scrollTo(i.docState.scrollLeft,i.docState.scrollTop),o.config.animation.applyPerspective&&(o.dom.parent.style[e.features.perspectiveProp]=o.config.animation.perspectiveDistance,o.dom.parent.style[e.features.perspectiveOriginProp]=o.config.animation.perspectiveOrigin),o.config.animation.animateResizeContainer&&i.startHeight!==i.newHeight&&i.viewportDeltaY!==i.startHeight-i.newHeight&&(o.dom.parent.style.height=i.startHeight+"px"),o.config.animation.animateResizeContainer&&i.startWidth!==i.newWidth&&i.viewportDeltaX!==i.startWidth-i.newWidth&&(o.dom.parent.style.width=i.startWidth+"px"),i.startHeight===i.newHeight&&(o.dom.parent.style.height=i.startHeight+"px"),i.startWidth===i.newWidth&&(o.dom.parent.style.width=i.startWidth+"px"),i.startHeight===i.newHeight&&i.startWidth===i.newWidth&&(o.dom.parent.style.overflow="hidden"),requestAnimationFrame(function(){o.moveTargets(i)}),o.callFilters("promiseGoMix",r.promise,arguments)):(o.config.debug.fauxAsync?setTimeout(function(){o.cleanUp(i)},o.config.animation.duration):o.cleanUp(i),o.callFilters("promiseGoMix",r.promise,arguments))},getStartMixData:function(n){var a=this,i=t.getComputedStyle(a.dom.parent),o=a.dom.parent.getBoundingClientRect(),r=null,s={},l=-1,c=i[e.features.boxSizingProp];for(a.incPadding="border-box"===c,a.callActions("beforeGetStartMixData",arguments),l=0;r=n.show[l];l++)s=r.getPosData(),n.showPosData[l]={startPosData:s};for(l=0;r=n.toHide[l];l++)s=r.getPosData(),n.toHidePosData[l]={startPosData:s};n.startX=o.left,n.startY=o.top,n.startHeight=a.incPadding?o.height:o.height-parseFloat(i.paddingTop)-parseFloat(i.paddingBottom)-parseFloat(i.borderTop)-parseFloat(i.borderBottom),n.startWidth=a.incPadding?o.width:o.width-parseFloat(i.paddingLeft)-parseFloat(i.paddingRight)-parseFloat(i.borderLeft)-parseFloat(i.borderRight),a.callActions("afterGetStartMixData",arguments)},setInter:function(t){var e=this,a=null,i=-1;for(e.callActions("beforeSetInter",arguments),e.config.animation.clampHeight&&(e.dom.parent.style.height=t.startHeight+"px",e.dom.parent.style.overflow="hidden"),e.config.animation.clampWidth&&(e.dom.parent.style.width=t.startWidth+"px",e.dom.parent.style.overflow="hidden"),i=0;a=t.toShow[i];i++)a.show();t.willChangeLayout&&(n.removeClass(e.dom.container,t.startContainerClassName),n.addClass(e.dom.container,t.newContainerClassName)),e.callActions("afterSetInter",arguments)},getInterMixData:function(t){var e=this,n=null,a=-1;for(e.callActions("beforeGetInterMixData",arguments),a=0;n=t.show[a];a++)t.showPosData[a].interPosData=n.getPosData();for(a=0;n=t.toHide[a];a++)t.toHidePosData[a].interPosData=n.getPosData();e.callActions("afterGetInterMixData",arguments)},setFinal:function(t){var e=this,n=null,a=-1;for(e.callActions("beforeSetFinal",arguments),t.willSort&&e.printSort(!1,t),a=0;n=t.toHide[a];a++)n.hide();e.callActions("afterSetFinal",arguments)},getFinalMixData:function(e){var a=this,i=null,o=null,r=null,s=-1;for(a.callActions("beforeGetFinalMixData",arguments),s=0;r=e.show[s];s++)e.showPosData[s].finalPosData=r.getPosData();for(s=0;r=e.toHide[s];s++)e.toHidePosData[s].finalPosData=r.getPosData();for((a.config.animation.clampHeight||a.config.animation.clampWidth)&&(a.dom.parent.style.height=a.dom.parent.style.width=a.dom.parent.style.overflow=""),a.incPadding||(i=t.getComputedStyle(a.dom.parent)),o=a.dom.parent.getBoundingClientRect(),e.newX=o.left,e.newY=o.top,e.newHeight=a.incPadding?o.height:o.height-parseFloat(i.paddingTop)-parseFloat(i.paddingBottom)-parseFloat(i.borderTop)-parseFloat(i.borderBottom),e.newWidth=a.incPadding?o.width:o.width-parseFloat(i.paddingLeft)-parseFloat(i.paddingRight)-parseFloat(i.borderLeft)-parseFloat(i.borderRight),e.viewportDeltaX=e.docState.viewportWidth-this.dom.document.documentElement.clientWidth,e.viewportDeltaY=e.docState.viewportHeight-this.dom.document.documentElement.clientHeight,e.willSort&&a.printSort(!0,e),s=0;r=e.toShow[s];s++)r.hide();for(s=0;r=e.toHide[s];s++)r.show();e.willChangeLayout&&(n.removeClass(a.dom.container,e.newContainerClassName),n.addClass(a.dom.container,a.config.layout.containerClassName)),a.callActions("afterGetFinalMixData",arguments)},getTweenData:function(t){var n=this,a=null,i=null,o=Object.getOwnPropertyNames(n.effectsIn),r="",s=null,l=-1,c=-1,u=-1,f=-1;for(n.callActions("beforeGetTweenData",arguments),u=0;a=t.show[u];u++)for(i=t.showPosData[u],i.posIn=new e.StyleData,i.posOut=new e.StyleData,i.tweenData=new e.StyleData,a.isShown?(i.posIn.x=i.startPosData.x-i.interPosData.x,i.posIn.y=i.startPosData.y-i.interPosData.y):i.posIn.x=i.posIn.y=0,i.posOut.x=i.finalPosData.x-i.interPosData.x,i.posOut.y=i.finalPosData.y-i.interPosData.y,i.posIn.opacity=a.isShown?1:n.effectsIn.opacity,i.posOut.opacity=1,i.tweenData.opacity=i.posOut.opacity-i.posIn.opacity,a.isShown||n.config.animation.nudge||(i.posIn.x=i.posOut.x,i.posIn.y=i.posOut.y),i.tweenData.x=i.posOut.x-i.posIn.x,i.tweenData.y=i.posOut.y-i.posIn.y,n.config.animation.animateResizeTargets&&(i.posIn.width=i.startPosData.width,i.posIn.height=i.startPosData.height,l=(i.startPosData.width||i.finalPosData.width)-i.interPosData.width,i.posIn.marginRight=i.startPosData.marginRight-l,c=(i.startPosData.height||i.finalPosData.height)-i.interPosData.height,i.posIn.marginBottom=i.startPosData.marginBottom-c,i.posOut.width=i.finalPosData.width,i.posOut.height=i.finalPosData.height,l=(i.finalPosData.width||i.startPosData.width)-i.interPosData.width,i.posOut.marginRight=i.finalPosData.marginRight-l,c=(i.finalPosData.height||i.startPosData.height)-i.interPosData.height,i.posOut.marginBottom=i.finalPosData.marginBottom-c,i.tweenData.width=i.posOut.width-i.posIn.width,i.tweenData.height=i.posOut.height-i.posIn.height,i.tweenData.marginRight=i.posOut.marginRight-i.posIn.marginRight,i.tweenData.marginBottom=i.posOut.marginBottom-i.posIn.marginBottom),f=0;r=o[f];f++)s=n.effectsIn[r],s instanceof e.TransformData&&s.value&&(i.posIn[r].value=s.value,i.posOut[r].value=0,i.tweenData[r].value=i.posOut[r].value-i.posIn[r].value,i.posIn[r].unit=i.posOut[r].unit=i.tweenData[r].unit=s.unit);for(u=0;a=t.toHide[u];u++)for(i=t.toHidePosData[u],i.posIn=new e.StyleData,i.posOut=new e.StyleData,i.tweenData=new e.StyleData,i.posIn.x=a.isShown?i.startPosData.x-i.interPosData.x:0,i.posIn.y=a.isShown?i.startPosData.y-i.interPosData.y:0,i.posOut.x=n.config.animation.nudge?0:i.posIn.x,i.posOut.y=n.config.animation.nudge?0:i.posIn.y,i.tweenData.x=i.posOut.x-i.posIn.x,i.tweenData.y=i.posOut.y-i.posIn.y,n.config.animation.animateResizeTargets&&(i.posIn.width=i.startPosData.width,i.posIn.height=i.startPosData.height,l=i.startPosData.width-i.interPosData.width,i.posIn.marginRight=i.startPosData.marginRight-l,c=i.startPosData.height-i.interPosData.height,i.posIn.marginBottom=i.startPosData.marginBottom-c),i.posIn.opacity=1,i.posOut.opacity=n.effectsOut.opacity,i.tweenData.opacity=i.posOut.opacity-i.posIn.opacity,f=0;r=o[f];f++)s=n.effectsOut[r],s instanceof e.TransformData&&s.value&&(i.posIn[r].value=0,i.posOut[r].value=s.value,i.tweenData[r].value=i.posOut[r].value-i.posIn[r].value,i.posIn[r].unit=i.posOut[r].unit=i.tweenData[r].unit=s.unit);n.callActions("afterGetTweenData",arguments)},moveTargets:function(t){var a=this,i=null,o=null,r=null,s="",l=!1,c=-1,u=-1,f=a.checkProgress.bind(a);for(a.callActions("beforeMoveTargets",arguments),u=0;i=t.show[u];u++)o=new e.IMoveData,r=t.showPosData[u],s=i.isShown?"none":"show",l=a.willTransition(s,t.hasEffect,r.posIn,r.posOut),l&&c++,i.show(),o.posIn=r.posIn,o.posOut=r.posOut,o.statusChange=s,o.staggerIndex=c,o.operation=t,o.callback=l?f:null,i.move(o);for(u=0;i=t.toHide[u];u++)r=t.toHidePosData[u],o=new e.IMoveData,s="hide",l=a.willTransition(s,r.posIn,r.posOut),o.posIn=r.posIn,o.posOut=r.posOut,o.statusChange=s,o.staggerIndex=u,o.operation=t,o.callback=l?f:null,i.move(o);a.config.animation.animateResizeContainer&&(a.dom.parent.style[e.features.transitionProp]="height "+a.config.animation.duration+"ms ease, width "+a.config.animation.duration+"ms ease ",requestAnimationFrame(function(){t.startHeight!==t.newHeight&&t.viewportDeltaY!==t.startHeight-t.newHeight&&(a.dom.parent.style.height=t.newHeight+"px"),t.startWidth!==t.newWidth&&t.viewportDeltaX!==t.startWidth-t.newWidth&&(a.dom.parent.style.width=t.newWidth+"px")})),t.willChangeLayout&&(n.removeClass(a.dom.container,a.config.layout.ContainerClassName),n.addClass(a.dom.container,t.newContainerClassName)),a.callActions("afterMoveTargets",arguments)},hasEffect:function(){var t=this,e=["scale","translateX","translateY","translateZ","rotateX","rotateY","rotateZ"],n="",a=null,i=!1,o=-1,r=-1;if(1!==t.effectsIn.opacity)return t.callFilters("resultHasEffect",!0,arguments);for(r=0;n=e[r];r++)if(a=t.effectsIn[n],o="undefined"!==a.value?a.value:a,0!==o){i=!0;break}return t.callFilters("resultHasEffect",i,arguments)},willTransition:function(t,e,a,i){var o=this,r=!1;return r=!!n.isVisible(o.dom.container)&&(!!("none"!==t&&e||a.x!==i.x||a.y!==i.y)||!!o.config.animation.animateResizeTargets&&(a.width!==i.width||a.height!==i.height||a.marginRight!==i.marginRight||a.marginTop!==i.marginTop)),o.callFilters("resultWillTransition",r,arguments)},checkProgress:function(t){var e=this;e.targetsDone++,e.targetsBound===e.targetsDone&&e.cleanUp(t)},cleanUp:function(t){var a=this,i=null,o=null,r=null,s=null,l=-1;for(a.callActions("beforeCleanUp",arguments),a.targetsMoved=a.targetsImmovable=a.targetsBound=a.targetsDone=0,l=0;i=t.show[l];l++)i.cleanUp(),i.show();for(l=0;i=t.toHide[l];l++)i.cleanUp(),i.hide();if(t.willSort&&a.printSort(!1,t),a.dom.parent.style[e.features.transitionProp]=a.dom.parent.style.height=a.dom.parent.style.width=a.dom.parent.style.overflow=a.dom.parent.style[e.features.perspectiveProp]=a.dom.parent.style[e.features.perspectiveOriginProp]="",t.willChangeLayout&&(n.removeClass(a.dom.container,t.startContainerClassName),n.addClass(a.dom.container,t.newContainerClassName)),t.toRemove.length){for(l=0;i=a.targets[l];l++)t.toRemove.indexOf(i)>-1&&((o=i.dom.el.previousSibling)&&"#text"===o.nodeName&&(r=i.dom.el.nextSibling)&&"#text"===r.nodeName&&n.removeWhitespace(o),t.willSort||a.dom.parent.removeChild(i.dom.el),a.targets.splice(l,1),i.isInDom=!1,l--);a.origOrder=a.targets}t.willSort&&(a.targets=t.newOrder),a.state=t.newState,a.lastOperation=t,a.dom.targets=a.state.targets,e.events.fire("mixEnd",a.dom.container,{state:a.state,instance:a},a.dom.document),"function"==typeof a.config.callbacks.onMixEnd&&a.config.callbacks.onMixEnd.call(a.dom.container,a.state,a),t.hasFailed&&(e.events.fire("mixFail",a.dom.container,{state:a.state,instance:a},a.dom.document),"function"==typeof a.config.callbacks.onMixFail&&a.config.callbacks.onMixFail.call(a.dom.container,a.state,a),n.addClass(a.dom.container,n.getClassname(a.config.classNames,"container",a.config.classNames.modifierFailed))),"function"==typeof a.userCallback&&a.userCallback.call(a.dom.container,a.state,a),"function"==typeof a.userDeferred.resolve&&a.userDeferred.resolve(a.state),a.userCallback=null,a.userDeferred=null,a.lastClicked=null,a.isToggling=!1,a.isBusy=!1,a.queue.length&&(a.callActions("beforeReadQueueCleanUp",arguments),s=a.queue.shift(),a.userDeferred=s.deferred,a.isToggling=s.isToggling,a.lastClicked=s.triggerElement,s.instruction.command instanceof e.CommandMultimix?a.multimix.apply(a,s.args):a.dataset.apply(a,s.args)),a.callActions("afterCleanUp",arguments)},parseMultimixArgs:function(t){var a=this,i=new e.UserInstruction,o=null,r=-1;for(i.animate=a.config.animation.enable,i.command=new e.CommandMultimix,r=0;r-1?i.command.position=o:"string"==typeof o?i.command.collection=n.arrayFromList(n.createElement(o).childNodes):"object"==typeof o&&n.isElement(o,a.dom.document)?i.command.collection.length?i.command.sibling=o:i.command.collection=[o]:"object"==typeof o&&o.length?i.command.collection.length?i.command.sibling=o[0]:i.command.collection=o:"object"==typeof o&&o.childNodes&&o.childNodes.length?i.command.collection.length?i.command.sibling=o.childNodes[0]:i.command.collection=n.arrayFromList(o.childNodes):"object"==typeof o?n.extend(i.command,o):"boolean"==typeof o?i.animate=o:"function"==typeof o&&(i.callback=o));if(i.command.index&&i.command.sibling)throw new Error(e.messages.errorInsertInvalidArguments());return!i.command.collection.length&&a.config.debug.showWarnings&&console.warn(e.messages.warningInsertNoElements()),i=a.callFilters("instructionParseInsertArgs",i,arguments),n.freeze(i),i},parseRemoveArgs:function(t){var a=this,i=new e.UserInstruction,o=null,r=null,s=-1;for(i.animate=a.config.animation.enable,i.command=new e.CommandRemove,s=0;s-1&&i.command.targets.push(o);return!i.command.targets.length&&a.config.debug.showWarnings&&console.warn(e.messages.warningRemoveNoElements()),n.freeze(i),i},parseDatasetArgs:function(t){var a=this,i=new e.UserInstruction,o=null,r=-1;for(i.animate=a.config.animation.enable,i.command=new e.CommandDataset,r=0;r-1&&t.toggleArray.splice(a,1),i=t.getToggleSelector(),t.multimix({filter:i},e.animate,e.callback)},sort:function(){var t=this,e=t.parseSortArgs(arguments);return t.multimix({sort:e.command},e.animate,e.callback)},changeLayout:function(){var t=this,e=t.parseChangeLayoutArgs(arguments);return t.multimix({changeLayout:e.command},e.animate,e.callback)},dataset:function(){var t=this,n=t.parseDatasetArgs(arguments),a=null,i=null,o=!1;return t.callActions("beforeDataset",arguments),t.isBusy?(i=new e.QueueItem,i.args=arguments,i.instruction=n,t.queueMix(i)):(n.callback&&(t.userCallback=n.callback),o=n.animate^t.config.animation.enable?n.animate:t.config.animation.enable,a=t.getDataOperation(n.command.dataset),t.goMix(o,a))},multimix:function(){var t=this,n=null,a=!1,i=null,o=t.parseMultimixArgs(arguments);return t.callActions("beforeMultimix",arguments),t.isBusy?(i=new e.QueueItem,i.args=arguments,i.instruction=o,i.triggerElement=t.lastClicked,i.isToggling=t.isToggling,t.queueMix(i)):(n=t.getOperation(o.command),t.config.controls.enable&&(o.command.filter&&!t.isToggling&&(t.toggleArray.length=0,t.buildToggleArray(n.command)),t.queue.length<1&&t.updateControls(n.command)),o.callback&&(t.userCallback=o.callback),a=o.animate^t.config.animation.enable?o.animate:t.config.animation.enable,t.callFilters("operationMultimix",n,arguments),t.goMix(a,n))},getOperation:function(t){var a=this,i=t.sort,o=t.filter,r=t.changeLayout,s=t.remove,l=t.insert,c=new e.Operation;return c=a.callFilters("operationUnmappedGetOperation",c,arguments),c.id=n.randomHex(),c.command=t,c.startState=a.state,c.triggerElement=a.lastClicked,a.isBusy?(a.config.debug.showWarnings&&console.warn(e.messages.warningGetOperationInstanceBusy()),null):(l&&a.insertTargets(l,c),s&&(c.toRemove=s.targets),c.startSort=c.newSort=c.startState.activeSort,c.startOrder=c.newOrder=a.targets,i&&(c.startSort=c.startState.activeSort,c.newSort=i,c.willSort=a.willSort(i,c.startState.activeSort),c.willSort&&a.sortOperation(c)),c.startFilter=c.startState.activeFilter,o?c.newFilter=o:c.newFilter=n.extend(new e.CommandFilter,c.startFilter),"all"===c.newFilter.selector?c.newFilter.selector=a.config.selectors.target:"none"===c.newFilter.selector&&(c.newFilter.selector=""),a.filterOperation(c),c.startContainerClassName=c.startState.activeContainerClassName,r?(c.newContainerClassName=r.containerClassName,c.newContainerClassName!==c.startContainerClassName&&(c.willChangeLayout=!0)):c.newContainerClassName=c.startContainerClassName,a.config.animation.enable&&(a.getStartMixData(c),a.setInter(c),c.docState=n.getDocumentState(a.dom.document),a.getInterMixData(c),a.setFinal(c),a.getFinalMixData(c),a.parseEffects(),c.hasEffect=a.hasEffect(),a.getTweenData(c)),c.willSort&&(a.targets=c.newOrder),c.newState=a.buildState(c),a.callFilters("operationMappedGetOperation",c,arguments))},tween:function(t,e){var n=null,a=null,i=-1,o=-1;for(e=Math.min(e,1),e=Math.max(e,0),o=0;n=t.show[o];o++)a=t.showPosData[o],n.applyTween(a,e);for(o=0;n=t.hide[o];o++)n.isShown&&n.hide(),(i=t.toHide.indexOf(n))>-1&&(a=t.toHidePosData[i],n.isShown||n.show(),n.applyTween(a,e))},insert:function(){var t=this,e=t.parseInsertArgs(arguments);return t.multimix({insert:e.command},e.animate,e.callback)},insertBefore:function(){var t=this,e=t.parseInsertArgs(arguments);return t.insert(e.command.collection,"before",e.command.sibling,e.animate,e.callback)},insertAfter:function(){var t=this,e=t.parseInsertArgs(arguments);return t.insert(e.command.collection,"after",e.command.sibling,e.animate,e.callback)},prepend:function(){var t=this,e=t.parseInsertArgs(arguments);return t.insert(0,e.command.collection,e.animate,e.callback)},append:function(){var t=this,e=t.parseInsertArgs(arguments);return t.insert(t.state.totalTargets,e.command.collection,e.animate,e.callback)},remove:function(){var t=this,e=t.parseRemoveArgs(arguments);return t.multimix({remove:e.command},e.animate,e.callback)},getConfig:function(t){var e=this,a=null;return a=t?n.getProperty(e.config,t):e.config,e.callFilters("valueGetConfig",a,arguments)},configure:function(t){var e=this;e.callActions("beforeConfigure",arguments),n.extend(e.config,t,!0,!0),e.callActions("afterConfigure",arguments)},getState:function(){var t=this,a=null;return a=new e.State,n.extend(a,t.state),n.freeze(a),t.callFilters("stateGetState",a,arguments)},forceRefresh:function(){var t=this;t.indexTargets()},forceRender:function(){var t=this,e=null,n=null,a="";for(a in t.cache)e=t.cache[a],n=e.render(e.data),n!==e.dom.el&&(e.isInDom&&(e.unbindEvents(),t.dom.parent.replaceChild(n,e.dom.el)),e.isShown||(n.style.display="none"),e.dom.el=n,e.isInDom&&e.bindEvents());t.state=t.buildState(t.lastOperation)},destroy:function(t){var n=this,a=null,i=null,o=0;for(n.callActions("beforeDestroy",arguments),o=0;a=n.controls[o];o++)a.removeBinding(n);for(o=0;i=n.targets[o];o++)t&&i.show(),i.unbindEvents();n.dom.container.id.match(/^MixItUp/)&&n.dom.container.removeAttribute("id"),delete e.instances[n.id],n.callActions("afterDestroy",arguments)}}),e.IMoveData=function(){e.Base.call(this),this.callActions("beforeConstruct"),this.posIn=null,this.posOut=null,this.operation=null,this.callback=null,this.statusChange="",this.duration=-1,this.staggerIndex=-1,this.callActions("afterConstruct"),n.seal(this)},e.BaseStatic.call(e.IMoveData),e.IMoveData.prototype=Object.create(e.Base.prototype),e.IMoveData.prototype.constructor=e.IMoveData,e.TargetDom=function(){e.Base.call(this),this.callActions("beforeConstruct"),this.el=null,this.callActions("afterConstruct"),n.seal(this)},e.BaseStatic.call(e.TargetDom),e.TargetDom.prototype=Object.create(e.Base.prototype),e.TargetDom.prototype.constructor=e.TargetDom,e.Target=function(){e.Base.call(this),this.callActions("beforeConstruct"),this.id="",this.sortString="",this.mixer=null,this.callback=null,this.isShown=!1,this.isBound=!1,this.isExcluded=!1,this.isInDom=!1,this.handler=null,this.operation=null,this.data=null,this.dom=new e.TargetDom,this.callActions("afterConstruct"),n.seal(this)},e.BaseStatic.call(e.Target),e.Target.prototype=Object.create(e.Base.prototype),n.extend(e.Target.prototype,{constructor:e.Target,init:function(t,n,a){var i=this,o="";if(i.callActions("beforeInit",arguments),i.mixer=n,t||(t=i.render(a)),i.cacheDom(t),i.bindEvents(),"none"!==i.dom.el.style.display&&(i.isShown=!0),a&&n.config.data.uidKey){if("undefined"==typeof(o=a[n.config.data.uidKey])||o.toString().length<1)throw new TypeError(e.messages.errorDatasetInvalidUidKey({uidKey:n.config.data.uidKey}));i.id=o,i.data=a,n.cache[o]=i}i.callActions("afterInit",arguments)},render:function(t){var a=this,i=null,o=null,r=null,s="";if(a.callActions("beforeRender",arguments),i=a.callFilters("renderRender",a.mixer.config.render.target,arguments),"function"!=typeof i)throw new TypeError(e.messages.errorDatasetRendererNotSet());return s=i(t),s&&"object"==typeof s&&n.isElement(s)?o=s:"string"==typeof s&&(r=document.createElement("div"),r.innerHTML=s,o=r.firstElementChild),a.callFilters("elRender",o,arguments)},cacheDom:function(t){var e=this;e.callActions("beforeCacheDom",arguments),e.dom.el=t,e.callActions("afterCacheDom",arguments)},getSortString:function(t){var e=this,n=e.dom.el.getAttribute("data-"+t)||"";e.callActions("beforeGetSortString",arguments),n=isNaN(1*n)?n.toLowerCase():1*n,e.sortString=n,e.callActions("afterGetSortString",arguments)},show:function(){var t=this;t.callActions("beforeShow",arguments),t.isShown||(t.dom.el.style.display="",t.isShown=!0),t.callActions("afterShow",arguments)},hide:function(){var t=this;t.callActions("beforeHide",arguments),t.isShown&&(t.dom.el.style.display="none",t.isShown=!1),t.callActions("afterHide",arguments)},move:function(t){var e=this;e.callActions("beforeMove",arguments),e.isExcluded||e.mixer.targetsMoved++,e.applyStylesIn(t),requestAnimationFrame(function(){e.applyStylesOut(t)}),e.callActions("afterMove",arguments)},applyTween:function(t,n){var a=this,i="",o=null,r=t.posIn,s=[],l=new e.StyleData,c=-1;for(a.callActions("beforeApplyTween",arguments),l.x=r.x,l.y=r.y,0===n?a.hide():a.isShown||a.show(),c=0;i=e.features.TWEENABLE[c];c++)if(o=t.tweenData[i],"x"===i){if(!o)continue;l.x=r.x+o*n}else if("y"===i){if(!o)continue;l.y=r.y+o*n}else if(o instanceof e.TransformData){if(!o.value)continue;l[i].value=r[i].value+o.value*n,l[i].unit=o.unit,s.push(i+"("+l[i].value+o.unit+")")}else{if(!o)continue;l[i]=r[i]+o*n,a.dom.el.style[i]=l[i]}(l.x||l.y)&&s.unshift("translate("+l.x+"px, "+l.y+"px)"),s.length&&(a.dom.el.style[e.features.transformProp]=s.join(" ")),a.callActions("afterApplyTween",arguments)},applyStylesIn:function(t){var n=this,a=t.posIn,i=1!==n.mixer.effectsIn.opacity,o=[];n.callActions("beforeApplyStylesIn",arguments),o.push("translate("+a.x+"px, "+a.y+"px)"),n.mixer.config.animation.animateResizeTargets&&("show"!==t.statusChange&&(n.dom.el.style.width=a.width+"px",n.dom.el.style.height=a.height+"px"),n.dom.el.style.marginRight=a.marginRight+"px",n.dom.el.style.marginBottom=a.marginBottom+"px"),i&&(n.dom.el.style.opacity=a.opacity),"show"===t.statusChange&&(o=o.concat(n.mixer.transformIn)),n.dom.el.style[e.features.transformProp]=o.join(" "),n.callActions("afterApplyStylesIn",arguments)},applyStylesOut:function(t){var n=this,a=[],i=[],o=n.mixer.config.animation.animateResizeTargets,r="undefined"!=typeof n.mixer.effectsIn.opacity;if(n.callActions("beforeApplyStylesOut",arguments),a.push(n.writeTransitionRule(e.features.transformRule,t.staggerIndex)),"none"!==t.statusChange&&a.push(n.writeTransitionRule("opacity",t.staggerIndex,t.duration)),o&&(a.push(n.writeTransitionRule("width",t.staggerIndex,t.duration)),a.push(n.writeTransitionRule("height",t.staggerIndex,t.duration)),a.push(n.writeTransitionRule("margin",t.staggerIndex,t.duration))),!t.callback)return n.mixer.targetsImmovable++,void(n.mixer.targetsMoved===n.mixer.targetsImmovable&&n.mixer.cleanUp(t.operation));switch(n.operation=t.operation,n.callback=t.callback,!n.isExcluded&&n.mixer.targetsBound++,n.isBound=!0,n.applyTransition(a),o&&t.posOut.width>0&&t.posOut.height>0&&(n.dom.el.style.width=t.posOut.width+"px",n.dom.el.style.height=t.posOut.height+"px",n.dom.el.style.marginRight=t.posOut.marginRight+"px",n.dom.el.style.marginBottom=t.posOut.marginBottom+"px"),n.mixer.config.animation.nudge||"hide"!==t.statusChange||i.push("translate("+t.posOut.x+"px, "+t.posOut.y+"px)"),t.statusChange){case"hide":r&&(n.dom.el.style.opacity=n.mixer.effectsOut.opacity),i=i.concat(n.mixer.transformOut);break;case"show":r&&(n.dom.el.style.opacity=1)}(n.mixer.config.animation.nudge||!n.mixer.config.animation.nudge&&"hide"!==t.statusChange)&&i.push("translate("+t.posOut.x+"px, "+t.posOut.y+"px)"),n.dom.el.style[e.features.transformProp]=i.join(" "),n.callActions("afterApplyStylesOut",arguments)},writeTransitionRule:function(t,e,n){var a=this,i=a.getDelay(e),o="";return o=t+" "+(n>0?n:a.mixer.config.animation.duration)+"ms "+i+"ms "+("opacity"===t?"linear":a.mixer.config.animation.easing),a.callFilters("ruleWriteTransitionRule",o,arguments)},getDelay:function(t){var e=this,n=-1;return"function"==typeof e.mixer.config.animation.staggerSequence&&(t=e.mixer.config.animation.staggerSequence.call(e,t,e.state)),n=e.mixer.staggerDuration?t*e.mixer.staggerDuration:0,e.callFilters("delayGetDelay",n,arguments)},applyTransition:function(t){var n=this,a=t.join(", ");n.callActions("beforeApplyTransition",arguments),n.dom.el.style[e.features.transitionProp]=a,n.callActions("afterApplyTransition",arguments)},handleTransitionEnd:function(t){var e=this,n=t.propertyName,a=e.mixer.config.animation.animateResizeTargets;e.callActions("beforeHandleTransitionEnd",arguments),e.isBound&&t.target.matches(e.mixer.config.selectors.target)&&(n.indexOf("transform")>-1||n.indexOf("opacity")>-1||a&&n.indexOf("height")>-1||a&&n.indexOf("width")>-1||a&&n.indexOf("margin")>-1)&&(e.callback.call(e,e.operation),e.isBound=!1,e.callback=null,e.operation=null),e.callActions("afterHandleTransitionEnd",arguments)},eventBus:function(t){var e=this;switch(e.callActions("beforeEventBus",arguments),t.type){case"webkitTransitionEnd":case"transitionend":e.handleTransitionEnd(t)}e.callActions("afterEventBus",arguments)},unbindEvents:function(){var t=this;t.callActions("beforeUnbindEvents",arguments),n.off(t.dom.el,"webkitTransitionEnd",t.handler),n.off(t.dom.el,"transitionend",t.handler),t.callActions("afterUnbindEvents",arguments)},bindEvents:function(){var t=this,a="";t.callActions("beforeBindEvents",arguments),a="webkit"===e.features.transitionPrefix?"webkitTransitionEnd":"transitionend",t.handler=function(e){return t.eventBus(e)},n.on(t.dom.el,a,t.handler),t.callActions("afterBindEvents",arguments)},getPosData:function(n){var a=this,i={},o=null,r=new e.StyleData;return a.callActions("beforeGetPosData",arguments),r.x=a.dom.el.offsetLeft,r.y=a.dom.el.offsetTop,(a.mixer.config.animation.animateResizeTargets||n)&&(o=a.dom.el.getBoundingClientRect(),r.top=o.top,r.right=o.right,r.bottom=o.bottom,r.left=o.left,r.width=o.width,r.height=o.height),a.mixer.config.animation.animateResizeTargets&&(i=t.getComputedStyle(a.dom.el),r.marginBottom=parseFloat(i.marginBottom),r.marginRight=parseFloat(i.marginRight)),a.callFilters("posDataGetPosData",r,arguments)},cleanUp:function(){var t=this;t.callActions("beforeCleanUp",arguments),t.dom.el.style[e.features.transformProp]="",t.dom.el.style[e.features.transitionProp]="",t.dom.el.style.opacity="",t.mixer.config.animation.animateResizeTargets&&(t.dom.el.style.width="",t.dom.el.style.height="",t.dom.el.style.marginRight="",t.dom.el.style.marginBottom=""),t.callActions("afterCleanUp",arguments)}}),e.Collection=function(t){var e=null,a=-1;for(this.callActions("beforeConstruct"),a=0;e=t[a];a++)this[a]=e;this.length=t.length,this.callActions("afterConstruct"),n.freeze(this)},e.BaseStatic.call(e.Collection),e.Collection.prototype=Object.create(e.Base.prototype),n.extend(e.Collection.prototype,{constructor:e.Collection,mixitup:function(t){var a=this,i=null,o=Array.prototype.slice.call(arguments),r=[],s=-1;for(this.callActions("beforeMixitup"),o.shift(),s=0;i=a[s];s++)r.push(i[t].apply(i,o));return a.callFilters("promiseMixitup",n.all(r,e.libraries),arguments)}}),e.Operation=function(){e.Base.call(this),this.callActions("beforeConstruct"),this.id="",this.args=[],this.command=null,this.showPosData=[],this.toHidePosData=[],this.startState=null,this.newState=null,this.docState=null,this.willSort=!1,this.willChangeLayout=!1,this.hasEffect=!1,this.hasFailed=!1,this.triggerElement=null,this.show=[],this.hide=[],this.matching=[],this.toShow=[],this.toHide=[],this.toMove=[],this.toRemove=[],this.startOrder=[],this.newOrder=[],this.startSort=null,this.newSort=null,this.startFilter=null,this.newFilter=null,this.startDataset=null,this.newDataset=null,this.viewportDeltaX=0,this.viewportDeltaY=0,this.startX=0,this.startY=0,this.startHeight=0,this.startWidth=0,this.newX=0,this.newY=0,this.newHeight=0,this.newWidth=0,this.startContainerClassName="",this.startDisplay="",this.newContainerClassName="",this.newDisplay="",this.callActions("afterConstruct"),n.seal(this)},e.BaseStatic.call(e.Operation),e.Operation.prototype=Object.create(e.Base.prototype),e.Operation.prototype.constructor=e.Operation,e.State=function(){e.Base.call(this),this.callActions("beforeConstruct"),this.id="",this.activeFilter=null,this.activeSort=null,this.activeContainerClassName="",this.container=null,this.targets=[],this.hide=[],this.show=[],this.matching=[],this.totalTargets=-1,this.totalShow=-1,this.totalHide=-1,this.totalMatching=-1,this.hasFailed=!1,this.triggerElement=null,this.activeDataset=null,this.callActions("afterConstruct"),n.seal(this)},e.BaseStatic.call(e.State),e.State.prototype=Object.create(e.Base.prototype),e.State.prototype.constructor=e.State,e.UserInstruction=function(){e.Base.call(this),this.callActions("beforeConstruct"),this.command={},this.animate=!1,this.callback=null,this.callActions("afterConstruct"),n.seal(this)},e.BaseStatic.call(e.UserInstruction),e.UserInstruction.prototype=Object.create(e.Base.prototype),e.UserInstruction.prototype.constructor=e.UserInstruction,e.Messages=function(){e.Base.call(this),this.callActions("beforeConstruct"),this.ERROR_FACTORY_INVALID_CONTAINER="[MixItUp] An invalid selector or element reference was passed to the mixitup factory function",this.ERROR_FACTORY_CONTAINER_NOT_FOUND="[MixItUp] The provided selector yielded no container element",this.ERROR_CONFIG_INVALID_ANIMATION_EFFECTS="[MixItUp] Invalid value for `animation.effects`",this.ERROR_CONFIG_INVALID_CONTROLS_SCOPE="[MixItUp] Invalid value for `controls.scope`",this.ERROR_CONFIG_INVALID_PROPERTY='[MixitUp] Invalid configuration object property "${erroneous}"${suggestion}',this.ERROR_CONFIG_INVALID_PROPERTY_SUGGESTION='. Did you mean "${probableMatch}"?',this.ERROR_CONFIG_DATA_UID_KEY_NOT_SET="[MixItUp] To use the dataset API, a UID key must be specified using `data.uidKey`",this.ERROR_DATASET_INVALID_UID_KEY='[MixItUp] The specified UID key "${uidKey}" is not present on one or more dataset items',this.ERROR_DATASET_DUPLICATE_UID='[MixItUp] The UID "${uid}" was found on two or more dataset items. UIDs must be unique.',this.ERROR_INSERT_INVALID_ARGUMENTS="[MixItUp] Please provider either an index or a sibling and position to insert, not both",this.ERROR_INSERT_PREEXISTING_ELEMENT="[MixItUp] An element to be inserted already exists in the container",this.ERROR_FILTER_INVALID_ARGUMENTS="[MixItUp] Please provide either a selector or collection `.filter()`, not both",this.ERROR_DATASET_NOT_SET="[MixItUp] To use the dataset API with pre-rendered targets, a starting dataset must be set using `load.dataset`",this.ERROR_DATASET_PRERENDERED_MISMATCH="[MixItUp] `load.dataset` does not match pre-rendered targets",this.ERROR_DATASET_RENDERER_NOT_SET="[MixItUp] To insert an element via the dataset API, a target renderer function must be provided to `render.target`",this.ERROR_SORT_NON_EXISTENT_ELEMENT="[MixItUp] An element to be sorted does not already exist in the container",this.WARNING_FACTORY_PREEXISTING_INSTANCE="[MixItUp] WARNING: This element already has an active MixItUp instance. The provided configuration object will be ignored. If you wish to perform additional methods on this instance, please create a reference.",this.WARNING_INSERT_NO_ELEMENTS="[MixItUp] WARNING: No valid elements were passed to `.insert()`",this.WARNING_REMOVE_NO_ELEMENTS="[MixItUp] WARNING: No valid elements were passed to `.remove()`",this.WARNING_MULTIMIX_INSTANCE_QUEUE_FULL="[MixItUp] WARNING: An operation was requested but the MixItUp instance was busy. The operation was rejected because the queue is full or queuing is disabled.",this.WARNING_GET_OPERATION_INSTANCE_BUSY="[MixItUp] WARNING: Operations can be be created while the MixItUp instance is busy.",this.WARNING_NO_PROMISE_IMPLEMENTATION="[MixItUp] WARNING: No Promise implementations could be found. If you wish to use promises with MixItUp please install an ES6 Promise polyfill.",this.WARNING_INCONSISTENT_SORTING_ATTRIBUTES='[MixItUp] WARNING: The requested sorting data attribute "${attribute}" was not present on one or more target elements which may product unexpected sort output',this.callActions("afterConstruct"),this.compileTemplates(),n.seal(this)},e.BaseStatic.call(e.Messages),e.Messages.prototype=Object.create(e.Base.prototype),e.Messages.prototype.constructor=e.Messages,e.Messages.prototype.compileTemplates=function(){var t="",e="";for(t in this)"string"==typeof(e=this[t])&&(this[n.camelCase(t)]=n.template(e))},e.messages=new e.Messages,e.Facade=function(t){e.Base.call(this),this.callActions("beforeConstruct",arguments),this.configure=t.configure.bind(t),this.show=t.show.bind(t),this.hide=t.hide.bind(t),this.filter=t.filter.bind(t),this.toggleOn=t.toggleOn.bind(t),this.toggleOff=t.toggleOff.bind(t),this.sort=t.sort.bind(t),this.changeLayout=t.changeLayout.bind(t),this.multimix=t.multimix.bind(t),this.dataset=t.dataset.bind(t),this.tween=t.tween.bind(t),this.insert=t.insert.bind(t),this.insertBefore=t.insertBefore.bind(t),this.insertAfter=t.insertAfter.bind(t),this.prepend=t.prepend.bind(t),this.append=t.append.bind(t),this.remove=t.remove.bind(t),this.destroy=t.destroy.bind(t),this.forceRefresh=t.forceRefresh.bind(t),this.forceRender=t.forceRender.bind(t),this.isMixing=t.isMixing.bind(t),this.getOperation=t.getOperation.bind(t),this.getConfig=t.getConfig.bind(t),this.getState=t.getState.bind(t),this.callActions("afterConstruct",arguments),n.freeze(this),n.seal(this)},e.BaseStatic.call(e.Facade),e.Facade.prototype=Object.create(e.Base.prototype),e.Facade.prototype.constructor=e.Facade,"object"==typeof exports&&"object"==typeof module?module.exports=e:"function"==typeof define&&define.amd?define(function(){return e}):"undefined"!=typeof t.mixitup&&"function"==typeof t.mixitup||(t.mixitup=e),e.BaseStatic.call(e.constructor),e.NAME="mixitup",e.CORE_VERSION="3.3.1"}(window); \ No newline at end of file diff --git a/mixitup-3.3.1/docs/mixitup-3-migration-guide.md b/mixitup-3.3.1/docs/mixitup-3-migration-guide.md new file mode 100644 index 0000000..e16d604 --- /dev/null +++ b/mixitup-3.3.1/docs/mixitup-3-migration-guide.md @@ -0,0 +1,105 @@ +# MixItUp 3 Migration Guide + +The biggest change to MixItUp with v3, is the dropping of jQuery as a dependency. MixItUp 1 and 2 both existed as jQuery plugins, with instantiation and API calls abstracted away through a typical jQuery-plugin interface. + +With MixItUp 3, we can now interact with MixItUp instances ('mixers') directly with minimal abstraction. + +## Instantiation + +###### Example: Basic Instantiation + +```js +// MixItUp 2 + +$('.container').mixItUp(); +``` + +```js +// MixItUp 3 + +var mixer = mixitup('.container'); +``` + +###### Example: Passing the configuration object + +```js +// MixItUp 2 + +$('.container').mixItUp({ + selectors: { + target: '.item' + } +}); +``` + +```js +// MixItUp 3 + +var mixer = mixitup('.container', { + selectors: { + target: '.item' + } +}); +``` + +Note that the `mixitup()` factory function is now all lowercase, as apposed to the camel case MixItUp 2 jQuery method `.mixItUp()`. + +MixItUp 3 adds many new configuration options, and renames or removes some of those from MixItUp 2. + +*Further reading: [Configuration Object](/docs/mixitup.Config.md)* + +## Method Invocation + +```js +// MixItUp 2 + +$('.container').mixItUp('filter', '.category-a'); +``` +```js +// MixItUp 3 + +mixer.filter('.category-a'); +``` + +As you may have noticed, mixers in MixItUp 3 have many of the same API methods as were available in MixItUp 2, but are called using standard method invocation syntax, with arguments passed in the standard form rather than the jQuery-UI-like syntax of MixItUp 2. + +MixItUp 3 adds many new API methods, and renames or removes some of those from MixItUp 2. + +*Further reading: [Mixer API Methods](/docs/mixitup.Mixer.md)* + +## Promises and Callbacks + +In MixItUp 2, asynchronous operations (those involving animation) accepted an optional callback function to be invoked on completion. + +With MixItUp 3, all asynchronous methods return a promise resolving with a state object. Callback functions are still permitted as an optional argument, but promises should be considered the preferred method for dealing with asynchronous operations. + +```js +// MixItUp 2 (callbacks) + +$('.container').mixItUp('filter', '.category-a', function(state) { + // Operation finished, the new state is: + + console.log(state); +}); +``` + +```js +// MixItUp 3 (promises) + +mixer.filter('.category-a') + .then(function(state) { + // Operation finished, the new state is: + + console.log(state); + }); +``` + +## CSS + +In MixItUp 2, it was required that a CSS `display: none` rule be applied to all target elements by default, with MixItUp adding the `display` value of your choice (e.g. `inline-block`) to only those targets to be shown. This was intended to prevent a flash-of-content before MixItUp 2's loading animation started. + +With MixItUp 3, loading animations are removed by default, and mixers are instantiated synchronously and instantly. Because of this, it is assumed that all targets in the DOM are already shown, so MixItUp only needs to add `display: none` to those targets to be hidden, using whatever `display` value is declared in your CSS for shown targets. + +In short – you no longer need to set `display: none` in your CSS. Simply use whatever display value your layout would require, regardless of MixItUp. + +Loading animations are still possible in MixItUp 3 as demonstrated in the [Loading Animation](http://demos.kunkalabs.com/mixitup/loading-animation/) demo. The code for this demo is available [here](/demos/loading-animation/). diff --git a/mixitup-3.3.1/docs/mixitup.Config.md b/mixitup-3.3.1/docs/mixitup.Config.md new file mode 100644 index 0000000..d7c5b16 --- /dev/null +++ b/mixitup-3.3.1/docs/mixitup.Config.md @@ -0,0 +1,1858 @@ +# mixitup.Config + +## Overview + +`mixitup.Config` is an interface used for customising the functionality of a +mixer instance. It is organised into several semantically distinct sub-objects, +each one pertaining to a particular aspect of MixItUp functionality. + +An object literal containing any or all of the available properies, +known as the "configuration object", can be passed as the second parameter to +the `mixitup` factory function when creating a mixer instance to customise its +functionality as needed. + +If no configuration object is passed, the mixer instance will take on the default +configuration values detailed below. + +### Contents + +- [animation](#animation) +- [behavior](#behavior) +- [callbacks](#callbacks) +- [controls](#controls) +- [classNames](#classNames) +- [data](#data) +- [debug](#debug) +- [layout](#layout) +- [load](#load) +- [selectors](#selectors) +- [render](#render) + + +

animation

+ +A group of properties defining the mixer's animation and effects settings. + +### enable + + + + +A boolean dictating whether or not animation should be enabled for the MixItUp instance. +If `false`, all operations will occur instantly and syncronously, although callback +functions and any returned promises will still be fulfilled. + + +|Type | Default +|--- | --- +|`boolean`| `true` + +###### Example: Create a mixer with all animations disabled + +```js +var mixer = mixitup(containerEl, { + animation: { + enable: false + } +}); +``` +### effects + + + + +A string of one or more space-seperated properties to which transitions will be +applied for all filtering animations. + +Properties can be listed any order or combination, although they will be applied in a specific +predefined order to produce consistent results. + +To learn more about available effects, experiment with our +sandbox demo and try out the "Export config" button in the Animation options drop down. + + +|Type | Default +|--- | --- +|`string`| `'fade scale'` + +###### Example: Apply "fade" and "translateZ" effects to all animations + +```js +// As targets are filtered in and out, they will fade between +// opacity 1 and 0 and transform between translateZ(-100px) and +// translateZ(0). + +var mixer = mixitup(containerEl, { + animation: { + effects: 'fade translateZ(-100px)' + } +}); +``` +### effectsIn + + + + +A string of one or more space-seperated effects to be applied only to filter-in +animations, overriding `config.animation.effects` if set. + + +|Type | Default +|--- | --- +|`string`| `''` + +###### Example: Apply downwards vertical translate to targets being filtered in + +```js + +var mixer = mixitup(containerEl, { + animation: { + effectsIn: 'fade translateY(-100%)' + } +}); +``` +### effectsOut + + + + +A string of one or more space-seperated effects to be applied only to filter-out +animations, overriding `config.animation.effects` if set. + + +|Type | Default +|--- | --- +|`string`| `''` + +###### Example: Apply upwards vertical translate to targets being filtered out + +```js + +var mixer = mixitup(containerEl, { + animation: { + effectsOut: 'fade translateY(-100%)' + } +}); +``` +### duration + + + + +An integer dictating the duration of all MixItUp animations in milliseconds, not +including any additional delay apllied via the `'stagger'` effect. + + +|Type | Default +|--- | --- +|`number`| `600` + +###### Example: Apply an animation duration of 200ms to all mixitup animations + +```js + +var mixer = mixitup(containerEl, { + animation: { + duration: 200 + } +}); +``` +### easing + + + + +A valid CSS3 transition-timing function or shorthand. For a full list of accepted +values, visit easings.net. + + +|Type | Default +|--- | --- +|`string`| `'ease'` + +###### Example 1: Apply "ease-in-out" easing to all animations + +```js + +var mixer = mixitup(containerEl, { + animation: { + easing: 'ease-in-out' + } +}); +``` +###### Example 2: Apply a custom "cubic-bezier" easing function to all animations + +```js +var mixer = mixitup(containerEl, { + animation: { + easing: 'cubic-bezier(0.645, 0.045, 0.355, 1)' + } +}); +``` +### applyPerspective + + + + +A boolean dictating whether or not to apply perspective to the MixItUp container +during animations. By default, perspective is always applied and creates the +illusion of three-dimensional space for effects such as `translateZ`, `rotateX`, +and `rotateY`. + +You may wish to disable this and define your own perspective settings via CSS. + + +|Type | Default +|--- | --- +|`bolean`| `true` + +###### Example: Prevent perspective from being applied to any 3D transforms + +```js +var mixer = mixitup(containerEl, { + animation: { + applyPerspective: false + } +}); +``` +### perspectiveDistance + + + + +The perspective distance value to be applied to the container during animations, +affecting any 3D-transform-based effects. + + +|Type | Default +|--- | --- +|`string`| `'3000px'` + +###### Example: Set a perspective distance of 2000px + +```js +var mixer = mixitup(containerEl, { + animation: { + effects: 'rotateY(-25deg)', + perspectiveDistance: '2000px' + } +}); +``` +### perspectiveOrigin + + + + +The perspective-origin value to be applied to the container during animations, +affecting any 3D-transform-based effects. + + +|Type | Default +|--- | --- +|`string`| `'50% 50%'` + +###### Example: Set a perspective origin in the top-right of the container + +```js +var mixer = mixitup(containerEl, { + animation: { + effects: 'transateZ(-200px)', + perspectiveOrigin: '100% 0' + } +}); +``` +### queue + + + + +A boolean dictating whether or not to enable the queuing of operations. + +If `true` (default), and a control is clicked or an API call is made while another +operation is progress, the operation will go into the queue and will be automatically exectuted +when the previous operaitons is finished. + +If `false`, any requested operations will be ignored, and the `onMixBusy` callback and `mixBusy` +event will be fired. If `debug.showWarnings` is enabled, a console warning will also occur. + + +|Type | Default +|--- | --- +|`boolean`| `true` + +###### Example: Disable queuing + +```js +var mixer = mixitup(containerEl, { + animation: { + queue: false + } +}); +``` +### queueLimit + + + + +An integer dictacting the maximum number of operations allowed in the queue at +any time, when queuing is enabled. + + +|Type | Default +|--- | --- +|`number`| `3` + +###### Example: Allow a maximum of 5 operations in the queue at any time + +```js +var mixer = mixitup(containerEl, { + animation: { + queueLimit: 5 + } +}); +``` +### animateResizeContainer + + + + +A boolean dictating whether or not to transition the height and width of the +container as elements are filtered in and out. If disabled, the container height +will change abruptly. + +It may be desirable to disable this on mobile devices as the CSS `height` and +`width` properties do not receive GPU-acceleration and can therefore cause stuttering. + + +|Type | Default +|--- | --- +|`boolean`| `true` + +###### Example 1: Disable the transitioning of the container height and/or width + +```js +var mixer = mixitup(containerEl, { + animation: { + animateResizeContainer: false + } +}); +``` +###### Example 2: Disable the transitioning of the container height and/or width for mobile devices only + +```js +var mixer = mixitup(containerEl, { + animation: { + animateResizeContainer: myFeatureTests.isMobile ? false : true + } +}); +``` +### animateResizeTargets + + + + +A boolean dictating whether or not to transition the height and width of target +elements as they change throughout the course of an animation. + +This is often a must for flex-box grid layouts where the size of target elements may change +depending on final their position in relation to their siblings, or for `.changeLayout()` +operations where the size of targets change between layouts. + +NB: This feature requires additional calculations and manipulation to non-hardware-accelerated +properties which may adversely affect performance on slower devices, and is therefore +disabled by default. + + +|Type | Default +|--- | --- +|`boolean`| `false` + +###### Example: Enable the transitioning of target widths and heights + +```js +var mixer = mixitup(containerEl, { + animation: { + animateResizeTargets: true + } +}); +``` +### staggerSequence + + + + +A custom function used to manipulate the order in which the stagger delay is +incremented when using the ‘stagger’ effect. + +When using the 'stagger' effect, the delay applied to each target element is incremented +based on its index. You may create a custom function to manipulate the order in which the +delay is incremented and create engaging non-linear stagger effects. + +The function receives the index of the target element as a parameter, and must +return an integer which serves as the multiplier for the stagger delay. + + +|Type | Default +|--- | --- +|`function`| `null` + +###### Example 1: Stagger target elements by column in a 3-column grid + +```js +var mixer = mixitup(containerEl, { + animation: { + effects: 'fade stagger(100ms)', + staggerSequence: function(i) { + return i % 3; + } + } +}); +``` +###### Example 2: Using an algorithm to produce a more complex sequence + +```js +var mixer = mixitup(containerEl, { + animation: { + effects: 'fade stagger(100ms)', + staggerSequence: function(i) { + return (2*i) - (5*((i/3) - ((1/3) * (i%3)))); + } + } +}); +``` +### reverseOut + + + + +A boolean dictating whether or not to reverse the direction of `translate` +and `rotate` transforms for elements being filtered out. + +It can be used to create carousel-like animations where elements enter and exit +from opposite directions. If enabled, the effect `translateX(-100%)` for elements +being filtered in would become `translateX(100%)` for targets being filtered out. + +This functionality can also be achieved by providing seperate effects +strings for `config.animation.effectsIn` and `config.animation.effectsOut`. + + +|Type | Default +|--- | --- +|`boolean`| `false` + +###### Example: Reverse the desired direction on any translate/rotate effect for targets being filtered out + +```js +// Elements being filtered in will be translated from '100%' to '0' while +// elements being filtered out will be translated from 0 to '-100%' + +var mixer = mixitup(containerEl, { + animation: { + effects: 'fade translateX(100%)', + reverseOut: true, + nudge: false // Disable nudging to create a carousel-like effect + } +}); +``` +### nudge + + + + +A boolean dictating whether or not to "nudge" the animation path of targets +when they are being filtered in and out simulatenously. + +This has been the default behavior of MixItUp since version 1, but it +may be desirable to disable this effect when filtering directly from +one exclusive set of targets to a different exclusive set of targets, +to create a carousel-like effect, or a generally more subtle animation. + + +|Type | Default +|--- | --- +|`boolean`| `true` + +###### Example: Disable the "nudging" of targets being filtered in and out simulatenously + +```js + +var mixer = mixitup(containerEl, { + animation: { + nudge: false + } +}); +``` +### clampHeight + + + + +A boolean dictating whether or not to clamp the height of the container while MixItUp's +geometry tests are carried out before an operation. + +To prevent scroll-bar flicker, clamping is turned on by default. But in the case where the +height of the container might affect its vertical positioning in the viewport +(e.g. a vertically-centered container), this should be turned off to ensure accurate +test results and a smooth animation. + + +|Type | Default +|--- | --- +|`boolean`| `true` + +###### Example: Disable container height-clamping + +```js + +var mixer = mixitup(containerEl, { + animation: { + clampHeight: false + } +}); +``` +### clampWidth + + + + +A boolean dictating whether or not to clamp the width of the container while MixItUp's +geometry tests are carried out before an operation. + +To prevent scroll-bar flicker, clamping is turned on by default. But in the case where the +width of the container might affect its horitzontal positioning in the viewport +(e.g. a horizontall-centered container), this should be turned off to ensure accurate +test results and a smooth animation. + + +|Type | Default +|--- | --- +|`boolean`| `true` + +###### Example: Disable container width-clamping + +```js + +var mixer = mixitup(containerEl, { + animation: { + clampWidth: false + } +}); +``` + +

behavior

+ +A group of properties relating to the behavior of the Mixer. + +### liveSort + + + + +A boolean dictating whether to allow "live" sorting of the mixer. + +Because of the expensive nature of sorting, MixItUp makes use of several +internal optimizations to skip redundant sorting operations, such as when +the newly requested sort command is the same as the active one. The caveat +to this optimization is that "live" edits to the value of a target's sorting +attribute will be ignored when requesting a re-sort by the same attribute. + +By setting to `behavior.liveSort` to `true`, the mixer will always re-sort +regardless of whether or not the sorting attribute and order have changed. + + +|Type | Default +|--- | --- +|`boolean`| `false` + +###### Example: Enabling `liveSort` to allow for re-sorting + +```js + +var mixer = mixitup(containerEl, { + behavior: { + liveSort: true + }, + load: { + sort: 'edited:desc' + } +}); + +var target = containerEl.children[3]; + +console.log(target.getAttribute('data-edited')); // '2015-04-24' + +target.setAttribute('data-edited', '2017-08-10'); // Update the target's edited date + +mixer.sort('edited:desc') + .then(function(state) { + // The target is now at the top of the list + + console.log(state.targets[0] === target); // true + }); +``` + +

callbacks

+ +A group of optional callback functions to be invoked at various +points within the lifecycle of a mixer operation. + +Each function is analogous to an event of the same name triggered from the +container element, and is invoked immediately after it. + +All callback functions receive the current `state` object as their first +argument, as well as other more specific arguments described below. + +### onMixStart + + + + +A callback function invoked immediately after any MixItUp operation is requested +and before animations have begun. + +A second `futureState` argument is passed to the function which represents the final +state of the mixer once the requested operation has completed. + + +|Type | Default +|--- | --- +|`function`| `null` + +###### Example: Adding an `onMixStart` callback function + +```js +var mixer = mixitup(containerEl, { + callbacks: { + onMixStart: function(state, futureState) { + console.log('Starting operation...'); + } + } +}); +``` +### onMixBusy + + + + +A callback function invoked when a MixItUp operation is requested while another +operation is in progress, and the animation queue is full, or queueing +is disabled. + + +|Type | Default +|--- | --- +|`function`| `null` + +###### Example: Adding an `onMixBusy` callback function + +```js +var mixer = mixitup(containerEl, { + callbacks: { + onMixBusy: function(state) { + console.log('Mixer busy'); + } + } +}); +``` +### onMixEnd + + + + +A callback function invoked after any MixItUp operation has completed, and the +state has been updated. + + +|Type | Default +|--- | --- +|`function`| `null` + +###### Example: Adding an `onMixEnd` callback function + +```js +var mixer = mixitup(containerEl, { + callbacks: { + onMixEnd: function(state) { + console.log('Operation complete'); + } + } +}); +``` +### onMixFail + + + + +A callback function invoked whenever an operation "fails", i.e. no targets +could be found matching the requested filter. + + +|Type | Default +|--- | --- +|`function`| `null` + +###### Example: Adding an `onMixFail` callback function + +```js +var mixer = mixitup(containerEl, { + callbacks: { + onMixFail: function(state) { + console.log('No items could be found matching the requested filter'); + } + } +}); +``` +### onMixClick + + + + +A callback function invoked whenever a MixItUp control is clicked, and before its +respective operation is requested. + +The clicked element is assigned to the `this` keyword within the function. The original +click event is passed to the function as the second argument, which can be useful if +using `` tags as controls where the default behavior needs to be prevented. + +Returning `false` from the callback will prevent the control click from triggering +an operation. + + +|Type | Default +|--- | --- +|`function`| `null` + +###### Example 1: Adding an `onMixClick` callback function + +```js +var mixer = mixitup(containerEl, { + callbacks: { + onMixClick: function(state, originalEvent) { + console.log('The control "' + this.innerText + '" was clicked'); + } + } +}); +``` +###### Example 2: Using `onMixClick` to manipulate the original click event + +```js +var mixer = mixitup(containerEl, { + callbacks: { + onMixClick: function(state, originalEvent) { + // Prevent original click event from bubbling up: + originalEvent.stopPropagation(); + + // Prevent default behavior of clicked element: + originalEvent.preventDefault(); + } + } +}); +``` +###### Example 3: Using `onMixClick` to conditionally cancel operations + +```js +var mixer = mixitup(containerEl, { + callbacks: { + onMixClick: function(state, originalEvent) { + // Perform some conditional check: + + if (myApp.isLoading) { + // By returning false, we can prevent the control click from triggering an operation. + + return false; + } + } + } +}); +``` + +

controls

+ +A group of properties relating to clickable control elements. + +### enable + + + + +A boolean dictating whether or not controls should be enabled for the mixer instance. + +If `true` (default behavior), MixItUp will search the DOM for any clickable elements with +`data-filter`, `data-sort` or `data-toggle` attributes, and bind them for click events. + +If `false`, no click handlers will be bound, and all functionality must therefore be performed +via the mixer's API methods. + +If you do not intend to use the default controls, setting this property to `false` will +marginally improve the startup time of your mixer instance, and will also prevent any other active +mixer instances in the DOM which are bound to controls from controlling the instance. + + +|Type | Default +|--- | --- +|`boolean`| `true` + +###### Example: Disabling controls + +```js +var mixer = mixitup(containerEl, { + controls: { + enable: false + } +}); + +// With the default controls disabled, we can only control +// the mixer via its API methods, e.g.: + +mixer.filter('.cat-1'); +``` +### live + + + + +A boolean dictating whether or not to use event delegation when binding click events +to the default controls. + +If `false` (default behavior), each control button in the DOM will be found and +individually bound when a mixer is instantiated, with their corresponding actions +cached for performance. + +If `true`, a single click handler will be applied to the `window` (or container element - see +`config.controls.scope`), and any click events triggered by elements with `data-filter`, +`data-sort` or `data-toggle` attributes present will be handled as they propagate upwards. + +If you require a user interface where control buttons may be added, removed, or changed during the +lifetime of a mixer, `controls.live` should be set to `true`. There is a marginal but unavoidable +performance deficit when using live controls, as the value of each control button must be read +from the DOM in real time once the click event has propagated. + + +|Type | Default +|--- | --- +|`boolean`| `true` + +###### Example: Setting live controls + +```js +var mixer = mixitup(containerEl, { + controls: { + live: true + } +}); + +// Control buttons can now be added, remove and changed without breaking +// the mixer's UI +``` +### scope + + + + +A string dictating the "scope" to use when binding or querying the default controls. The available +values are `'global'` or `'local'`. + +When set to `'global'` (default behavior), MixItUp will query the entire document for control buttons +to bind, or delegate click events from (see `config.controls.live`). + +When set to `'local'`, MixItUp will only query (or bind click events to) its own container element. +This may be desireable if you require multiple active mixer instances within the same document, with +controls that would otherwise intefere with each other if scoped globally. + +Conversely, if you wish to control multiple instances with a single UI, you would create one +set of controls and keep the controls scope of each mixer set to `global`. + + +|Type | Default +|--- | --- +|`string`| `'global'` + +###### Example: Setting 'local' scoped controls + +```js +var mixerOne = mixitup(containerOne, { + controls: { + scope: 'local' + } +}); + +var mixerTwo = mixitup(containerTwo, { + controls: { + scope: 'local' + } +}); + +// Both mixers can now exist within the same document with +// isolated controls placed within their container elements. +``` +### toggleLogic + + + + +A string dictating the type of logic to apply when concatenating the filter selectors of +active toggle buttons (i.e. any clickable element with a `data-toggle` attribute). + +If set to `'or'` (default behavior), selectors will be concatenated together as +a comma-seperated list. For example: + +`'.cat-1, .cat-2'` (shows any elements matching `'.cat-1'` OR `'.cat-2'`) + +If set to `'and'`, selectors will be directly concatenated together. For example: + +`'.cat-1.cat-2'` (shows any elements which match both `'.cat-1'` AND `'.cat-2'`) + + +|Type | Default +|--- | --- +|`string`| `'or'` + +###### Example: Setting "and" toggle logic + +```js +var mixer = mixitup(containerEl, { + controls: { + toggleLogic: 'and' + } +}); +``` +### toggleDefault + + + + +A string dictating the filter behavior when all toggles are inactive. + +When set to `'all'` (default behavior), *all* targets will be shown by default +when no toggles are active, or at the moment all active toggles are toggled off. + +When set to `'none'`, no targets will be shown by default when no toggles are +active, or at the moment all active toggles are toggled off. + + +|Type | Default +|--- | --- +|`string`| `'all'` + +###### Example 1: Setting the default toggle behavior to `'all'` + +```js +var mixer = mixitup(containerEl, { + controls: { + toggleDefault: 'all' + } +}); + +mixer.toggleOn('.cat-2') + .then(function() { + // Deactivate all active toggles + + return mixer.toggleOff('.cat-2') + }) + .then(function(state) { + console.log(state.activeFilter.selector); // 'all' + console.log(state.totalShow); // 12 + }); +``` +###### Example 2: Setting the default toggle behavior to `'none'` + +```js +var mixer = mixitup(containerEl, { + controls: { + toggleDefault: 'none' + } +}); + +mixer.toggleOn('.cat-2') + .then(function() { + // Deactivate all active toggles + + return mixer.toggleOff('.cat-2') + }) + .then(function(state) { + console.log(state.activeFilter.selector); // 'none' + console.log(state.totalShow); // 0 + }); +``` + +

classNames

+ +A group of properties defining the output and structure of class names programmatically +added to controls and containers to reflect the state of the mixer. + +Most commonly, class names are added to controls by MixItUp to indicate that +the control is active so that it can be styled accordingly - `'mixitup-control-active'` by default. + +Using a "BEM" like structure, each classname is broken into the three parts: +a block namespace (`'mixitup'`), an element name (e.g. `'control'`), and an optional modifier +name (e.g. `'active'`) reflecting the state of the element. + +By default, each part of the classname is concatenated together using single hyphens as +delineators, but this can be easily customised to match the naming convention and style of +your project. + +### block + + + + +The "block" portion, or top-level namespace added to the start of any class names created by MixItUp. + + +|Type | Default +|--- | --- +|`string`| `'mixitup'` + +###### Example 1: changing the `config.classNames.block` value + +```js +var mixer = mixitup(containerEl, { + classNames: { + block: 'portfolio' + } +}); + +// Active control output: "portfolio-control-active" +``` +###### Example 2: Removing `config.classNames.block` + +```js +var mixer = mixitup(containerEl, { + classNames: { + block: '' + } +}); + +// Active control output: "control-active" +``` +### elementContainer + + + + +The "element" portion of the class name added to container. + + +|Type | Default +|--- | --- +|`string`| `'container'` + +### elementFilter + + + + +The "element" portion of the class name added to filter controls. + +By default, all filter, sort, multimix and toggle controls take the same element value of `'control'`, but +each type's element value can be individually overwritten to match the unique classNames of your controls as needed. + + +|Type | Default +|--- | --- +|`string`| `'control'` + +###### Example 1: changing the `config.classNames.elementFilter` value + +```js +var mixer = mixitup(containerEl, { + classNames: { + elementFilter: 'filter' + } +}); + +// Active filter output: "mixitup-filter-active" +``` +###### Example 2: changing the `config.classNames.block` and `config.classNames.elementFilter` values + +```js +var mixer = mixitup(containerEl, { + classNames: { + block: 'portfolio', + elementFilter: 'filter' + } +}); + +// Active filter output: "portfolio-filter-active" +``` +### elementSort + + + + +The "element" portion of the class name added to sort controls. + +By default, all filter, sort, multimix and toggle controls take the same element value of `'control'`, but +each type's element value can be individually overwritten to match the unique classNames of your controls as needed. + + +|Type | Default +|--- | --- +|`string`| `'control'` + +###### Example 1: changing the `config.classNames.elementSort` value + +```js +var mixer = mixitup(containerEl, { + classNames: { + elementSort: 'sort' + } +}); + +// Active sort output: "mixitup-sort-active" +``` +###### Example 2: changing the `config.classNames.block` and `config.classNames.elementSort` values + +```js +var mixer = mixitup(containerEl, { + classNames: { + block: 'portfolio', + elementSort: 'sort' + } +}); + +// Active sort output: "portfolio-sort-active" +``` +### elementMultimix + + + + +The "element" portion of the class name added to multimix controls. + +By default, all filter, sort, multimix and toggle controls take the same element value of `'control'`, but +each type's element value can be individually overwritten to match the unique classNames of your controls as needed. + + +|Type | Default +|--- | --- +|`string`| `'control'` + +###### Example 1: changing the `config.classNames.elementMultimix` value + +```js +var mixer = mixitup(containerEl, { + classNames: { + elementMultimix: 'multimix' + } +}); + +// Active multimix output: "mixitup-multimix-active" +``` +###### Example 2: changing the `config.classNames.block` and `config.classNames.elementMultimix` values + +```js +var mixer = mixitup(containerEl, { + classNames: { + block: 'portfolio', + elementSort: 'multimix' + } +}); + +// Active multimix output: "portfolio-multimix-active" +``` +### elementToggle + + + + +The "element" portion of the class name added to toggle controls. + +By default, all filter, sort, multimix and toggle controls take the same element value of `'control'`, but +each type's element value can be individually overwritten to match the unique classNames of your controls as needed. + + +|Type | Default +|--- | --- +|`string`| `'control'` + +###### Example 1: changing the `config.classNames.elementToggle` value + +```js +var mixer = mixitup(containerEl, { + classNames: { + elementToggle: 'toggle' + } +}); + +// Active toggle output: "mixitup-toggle-active" +``` +###### Example 2: changing the `config.classNames.block` and `config.classNames.elementToggle` values + +```js +var mixer = mixitup(containerEl, { + classNames: { + block: 'portfolio', + elementToggle: 'toggle' + } +}); + +// Active toggle output: "portfolio-toggle-active" +``` +### modifierActive + + + + +The "modifier" portion of the class name added to active controls. + + +|Type | Default +|--- | --- +|`string`| `'active'` + +### modifierDisabled + + + + +The "modifier" portion of the class name added to disabled controls. + + +|Type | Default +|--- | --- +|`string`| `'disabled'` + +### modifierFailed + + + + +The "modifier" portion of the class name added to the container when in a "failed" state. + + +|Type | Default +|--- | --- +|`string`| `'failed'` + +### delineatorElement + + + + +The delineator used between the "block" and "element" portions of any class name added by MixItUp. + +If the block portion is ommited by setting it to an empty string, no delineator will be added. + + +|Type | Default +|--- | --- +|`string`| `'-'` + +###### Example: changing the delineator to match BEM convention + +```js +var mixer = mixitup(containerEl, { + classNames: { + delineatorElement: '__' + } +}); + +// example active control output: "mixitup__control-active" +``` +### delineatorModifier + + + + +The delineator used between the "element" and "modifier" portions of any class name added by MixItUp. + +If the element portion is ommited by setting it to an empty string, no delineator will be added. + + +|Type | Default +|--- | --- +|`string`| `'-'` + +###### Example: changing both delineators to match BEM convention + +```js +var mixer = mixitup(containerEl, { + classNames: { + delineatorElement: '__' + delineatorModifier: '--' + } +}); + +// Active control output: "mixitup__control--active" +``` + +

data

+ +A group of properties relating to MixItUp's dataset API. + +### uidKey + + + + +A string specifying the name of the key containing your data model's unique +identifier (UID). To use the dataset API, a UID key must be specified and +be present and unique on all objects in the dataset you provide to MixItUp. + +For example, if your dataset is made up of MongoDB documents, the UID +key would be `'id'` or `'_id'`. + + +|Type | Default +|--- | --- +|`string`| `''` + +###### Example: Setting the UID to `'id'` + +```js +var mixer = mixitup(containerEl, { + data: { + uidKey: 'id' + } +}); +``` +### dirtyCheck + + + + +A boolean dictating whether or not MixItUp should "dirty check" each object in +your dataset for changes whenever `.dataset()` is called, and re-render any targets +for which a change is found. + +Depending on the complexity of your data model, dirty checking can be expensive +and is therefore disabled by default. + +NB: For changes to be detected, a new immutable instance of the edited model must be +provided to mixitup, rather than manipulating properties on the existing instance. +If your changes are a result of a DB write and read, you will most likely be calling +`.dataset()` with a clean set of objects each time, so this will not be an issue. + + +|Type | Default +|--- | --- +|`boolean`| `false` + +###### Example: Enabling dirty checking + +```js + +var myDataset = [ + { + id: 0, + title: "Blog Post Title 0" + ... + }, + { + id: 1, + title: "Blog Post Title 1" + ... + } +]; + +// Instantiate a mixer with a pre-loaded dataset, and a target renderer +// function defined + +var mixer = mixitup(containerEl, { + data: { + uidKey: 'id', + dirtyCheck: true + }, + load: { + dataset: myDataset + }, + render: { + target: function() { ... } + } +}); + +// For illustration, we will clone and edit the second object in the dataset. +// NB: this would typically be done server-side in response to a DB update, +and then re-queried via an API. + +myDataset[1] = Object.assign({}, myDataset[1]); + +myDataset[1].title = 'Blog Post Title 11'; + +mixer.dataset(myDataset) + .then(function() { + // the target with ID "1", will be re-rendered reflecting its new title + }); +``` + +

debug

+ +A group of properties allowing the toggling of various debug features. + +### enable + + + + +A boolean dictating whether or not the mixer instance returned by the +`mixitup()` factory function should expose private properties and methods. + +By default, mixer instances only expose their public API, but enabling +debug mode will give you access to various mixer internals which may aid +in debugging, or the authoring of extensions. + + +|Type | Default +|--- | --- +|`boolean`| `false` + +###### Example: Enabling debug mode + +```js + +var mixer = mixitup(containerEl, { + debug: { + enable: true + } +}); + +// Private properties and methods will now be visible on the mixer instance: + +console.log(mixer); +``` +### showWarnings + + + + +A boolean dictating whether or not warnings should be shown when various +common gotchas occur. + +Warnings are intended to provide insights during development when something +occurs that is not a fatal, but may indicate an issue with your integration, +and are therefore turned on by default. However, you may wish to disable +them in production. + + +|Type | Default +|--- | --- +|`boolean`| `true` + +###### Example 1: Disabling warnings + +```js + +var mixer = mixitup(containerEl, { + debug: { + showWarnings: false + } +}); +``` +###### Example 2: Disabling warnings based on environment + +```js + +var showWarnings = myAppConfig.environment === 'development' ? true : false; + +var mixer = mixitup(containerEl, { + debug: { + showWarnings: showWarnings + } +}); +``` + +

layout

+ +A group of properties relating to the layout of the container. + +### allowNestedTargets + + + + +A boolean dictating whether or not mixitup should query all descendants +of the container for targets, or only immediate children. + +By default, mixitup will query all descendants matching the +`selectors.target` selector when indexing targets upon instantiation. +This allows for targets to be nested inside a sub-container which is +useful when ring-fencing targets from locally scoped controls in your +markup (see `controls.scope`). + +However, if you are building a more complex UI requiring the nesting +of mixers within mixers, you will most likely want to limit targets to +immediate children of the container by setting this property to `false`. + + +|Type | Default +|--- | --- +|`boolean`| `true` + +###### Example: Restricting targets to immediate children + +```js + +var mixer = mixitup(containerEl, { + layout: { + allowNestedTargets: false + } +}); +``` +### containerClassName + + + + +A string specifying an optional class name to apply to the container when in +its default state. + +By changing this class name or adding a class name to the container via the +`.changeLayout()` API method, the CSS layout of the container can be changed, +and MixItUp will attemp to gracefully animate the container and its targets +between states. + + +|Type | Default +|--- | --- +|`string`| `''` + +###### Example 1: Specifying a container class name + +```js + +var mixer = mixitup(containerEl, { + layout: { + containerClassName: 'grid' + } +}); +``` +###### Example 2: Changing the default class name with `.changeLayout()` + +```js + +var mixer = mixitup(containerEl, { + layout: { + containerClassName: 'grid' + } +}); + +mixer.changeLayout('list') + .then(function(state) { + console.log(state.activeContainerClass); // "list" + }); +``` +### siblingBefore + + + + +A reference to a non-target sibling element after which to insert targets +when there are no targets in the container. + + +|Type | Default +|--- | --- +|`HTMLElement`| `null` + +###### Example: Setting a `siblingBefore` reference element + +```js + +var addButton = containerEl.querySelector('button'); + +var mixer = mixitup(containerEl, { + layout: { + siblingBefore: addButton + } +}); +``` +### siblingAfter + + + + +A reference to a non-target sibling element before which to insert targets +when there are no targets in the container. + + +|Type | Default +|--- | --- +|`HTMLElement`| `null` + +###### Example: Setting an `siblingAfter` reference element + +```js + +var gap = containerEl.querySelector('.gap'); + +var mixer = mixitup(containerEl, { + layout: { + siblingAfter: gap + } +}); +``` + +

load

+ +A group of properties defining the initial state of the mixer on load (instantiation). + +### filter + + + + +A string defining any filtering to be statically applied to the mixer on load. +As per the `.filter()` API, this can be any valid selector string, or the +values `'all'` or `'none'`. + + +|Type | Default +|--- | --- +|`string`| `'all'` + +###### Example 1: Defining an initial filter selector to be applied on load + +```js + +// The mixer will show only those targets matching '.category-a' on load. + +var mixer = mixitup(containerEl, { + load: { + filter: '.category-a' + } +}); +``` +###### Example 2: Hiding all targets on load + +```js + +// The mixer will show hide all targets on load. + +var mixer = mixitup(containerEl, { + load: { + filter: 'none' + } +}); +``` +### sort + + + + +A string defining any sorting to be statically applied to the mixer on load. +As per the `.sort()` API, this should be a valid "sort string" made up of +an attribute to sort by (or `'default'`) followed by an optional sorting +order, or the value `'random'`; + + +|Type | Default +|--- | --- +|`string`| `'default:asc'` + +###### Example: Defining sorting to be applied on load + +```js + +// The mixer will sort the container by the value of the `data-published-date` +// attribute, in descending order. + +var mixer = mixitup(containerEl, { + load: { + sort: 'published-date:desc' + } +}); +``` +### dataset + + + + +An array of objects representing the underlying data of any pre-rendered targets, +when using the `.dataset()` API. + +NB: If targets are pre-rendered when the mixer is instantiated, this must be set. + + +|Type | Default +|--- | --- +|`Array.`| `null` + +###### Example: Defining the initial underyling dataset + +```js + +var myDataset = [ + { + id: 0, + title: "Blog Post Title 0", + ... + }, + { + id: 1, + title: "Blog Post Title 1", + ... + } +]; + +var mixer = mixitup(containerEl, { + data: { + uidKey: 'id' + }, + load: { + dataset: myDataset + } +}); +``` + +

selectors

+ +A group of properties defining the selectors used to query elements within a mixitup container. + +### target + + + + +A selector string used to query and index target elements within the container. + +By default, the class selector `'.mix'` is used, but this can be changed to an +attribute or element selector to match the style of your project. + + +|Type | Default +|--- | --- +|`string`| `'.mix'` + +###### Example 1: Changing the target selector + +```js + +var mixer = mixitup(containerEl, { + selectors: { + target: '.portfolio-item' + } +}); +``` +###### Example 2: Using an attribute selector as a target selector + +```js + +// The mixer will search for any children with the attribute `data-ref="mix"` + +var mixer = mixitup(containerEl, { + selectors: { + target: '[data-ref="mix"]' + } +}); +``` +### control + + + + +A optional selector string used to add further specificity to the querying of control elements, +in addition to their mandatory data attribute (e.g. `data-filter`, `data-toggle`, `data-sort`). + +This can be used if other elements in your document must contain the above attributes +(e.g. for use in third-party scripts), and would otherwise interfere with MixItUp. Adding +an additional `control` selector of your choice allows MixItUp to restrict event handling +to only those elements matching the defined selector. + + +|Type | Default +|--- | --- +|`string`| `''` + +###### Example 1: Adding a `selectors.control` selector + +```js + +var mixer = mixitup(containerEl, { + selectors: { + control: '.mixitup-control' + } +}); + +// Will not be handled: +// + +// Will be handled: +// +``` + +

render

+ +A group of optional render functions for creating and updating elements. + +All render functions receive a data object, and should return a valid HTML string. + +### target + + + + +A function returning an HTML string representing a target element, or a reference to a +single DOM element. + +The function is invoked as part of the `.dataset()` API, whenever a new item is added +to the dataset, or an item in the dataset changes (if `dataset.dirtyCheck` is enabled). + +The function receives the relevant dataset item as its first parameter. + + +|Type | Default +|--- | --- +|`function`| `'null'` + +###### Example 1: Using string concatenation + +```js + +var mixer = mixitup(containerEl, { + render: { + target: function(item) { + return ( + '
' + + '

' + item.title + '

' + + '
' + ); + } + } +}); +``` +###### Example 2: Using an ES2015 template literal + +```js + +var mixer = mixitup(containerEl, { + render: { + target: function(item) { + return ( + `
+

${item.title}

+
` + ); + } + } +}); +``` +###### Example 3: Using a Handlebars template + +```js + +var targetTemplate = Handlebars.compile('

{{title}}

'); + +var mixer = mixitup(containerEl, { + render: { + target: targetTemplate + } +}); +``` +###### Example 4: Returning a DOM element + +```js + +var mixer = mixitup(containerEl, { + render: { + target: function(item) { + // Create a single element using your framework's built-in renderer + + var el = ... + + return el; + } + } +}); +``` + diff --git a/mixitup-3.3.1/docs/mixitup.Events.md b/mixitup-3.3.1/docs/mixitup.Events.md new file mode 100644 index 0000000..da5ea3e --- /dev/null +++ b/mixitup-3.3.1/docs/mixitup.Events.md @@ -0,0 +1,104 @@ +# mixitup.Events + +## Overview + +The `mixitup.Events` class contains all custom events dispatched by MixItUp at various +points within the lifecycle of a mixer operation. + +Each event is analogous to the callback function of the same name defined in +the `callbacks` configuration object, and is triggered immediately before it. + +Events are always triggered from the container element on which MixItUp is instantiated +upon. + +As with any event, registered event handlers receive the event object as a parameter +which includes a `detail` property containting references to the current `state`, +the `mixer` instance, and other event-specific properties described below. + +### Contents + +- [mixStart](#mixStart) +- [mixBusy](#mixBusy) +- [mixEnd](#mixEnd) +- [mixFail](#mixFail) +- [mixClick](#mixClick) + + +

mixStart

+ + + + +A custom event triggered immediately after any MixItUp operation is requested +and before animations have begun. + +The `mixStart` event also exposes a `futureState` property via the +`event.detail` object, which represents the final state of the mixer once +the requested operation has completed. + + +|Type +|--- +|`CustomEvent` + + +

mixBusy

+ + + + +A custom event triggered when a MixItUp operation is requested while another +operation is in progress, and the animation queue is full, or queueing +is disabled. + + +|Type +|--- +|`CustomEvent` + + +

mixEnd

+ + + + +A custom event triggered after any MixItUp operation has completed, and the +state has been updated. + + +|Type +|--- +|`CustomEvent` + + +

mixFail

+ + + + +A custom event triggered whenever a filter operation "fails", i.e. no targets +could be found matching the requested filter. + + +|Type +|--- +|`CustomEvent` + + +

mixClick

+ + + + +A custom event triggered whenever a MixItUp control is clicked, and before its +respective operation is requested. + +This event also exposes an `originalEvent` property via the `event.detail` +object, which holds a reference to the original click event. + + +|Type +|--- +|`CustomEvent` + + diff --git a/mixitup-3.3.1/docs/mixitup.Mixer.md b/mixitup-3.3.1/docs/mixitup.Mixer.md new file mode 100644 index 0000000..5534f2c --- /dev/null +++ b/mixitup-3.3.1/docs/mixitup.Mixer.md @@ -0,0 +1,1011 @@ +# mixitup.Mixer + +## Overview + +The `mixitup.Mixer` class is used to hold discreet, user-configured +instances of MixItUp on a provided container element. + +Mixer instances are returned whenever the `mixitup()` factory function is called, +which expose a range of methods enabling API-based filtering, sorting, +insertion, removal and more. + +### Contents + +- [show()](#show) +- [hide()](#hide) +- [isMixing()](#isMixing) +- [filter()](#filter) +- [toggleOn()](#toggleOn) +- [toggleOff()](#toggleOff) +- [sort()](#sort) +- [changeLayout()](#changeLayout) +- [dataset()](#dataset) +- [multimix()](#multimix) +- [insert()](#insert) +- [insertBefore()](#insertBefore) +- [insertAfter()](#insertAfter) +- [prepend()](#prepend) +- [append()](#append) +- [remove()](#remove) +- [getConfig()](#getConfig) +- [configure()](#configure) +- [getState()](#getState) +- [forceRefresh()](#forceRefresh) +- [forceRender()](#forceRender) +- [destroy()](#destroy) + + +

show()

+ +*Version added: 3.0.0* + +`.show()` + +A shorthand method for `.filter('all')`. Shows all targets in the container. + +| |Type | Name | Description +|---|--- | --- | --- +|Returns |`Promise.` | + + +###### Example: Showing all targets + +```js + +mixer.show() + .then(function(state) { + console.log(state.totalShow === state.totalTargets); // true + }); +``` + +

hide()

+ +*Version added: 3.0.0* + +`.hide()` + +A shorthand method for `.filter('none')`. Hides all targets in the container. + +| |Type | Name | Description +|---|--- | --- | --- +|Returns |`Promise.` | + + +###### Example: Hiding all targets + +```js + +mixer.hide() + .then(function(state) { + console.log(state.totalShow === 0); // true + console.log(state.totalHide === state.totalTargets); // true + }); +``` + +

isMixing()

+ +*Version added: 2.0.0* + +`.isMixing()` + +Returns a boolean indicating whether or not a MixItUp operation is +currently in progress. + +| |Type | Name | Description +|---|--- | --- | --- +|Returns |`boolean` | + + +###### Example: Checking the status of a mixer + +```js + +mixer.sort('random', function() { + console.log(mixer.isMixing()) // false +}); + +console.log(mixer.isMixing()) // true +``` + +

filter()

+ +*Version added: 2.0.0* + +`.filter(selector [, animate] [, callback])` + +Filters all targets in the container by a provided selector string, or the values `'all'` +or `'none'`. Only targets matching the selector will be shown. + +| |Type | Name | Description +|---|--- | --- | --- +|Param |`string, HTMLElement, Array.` | `selector` | Any valid CSS selector (i.e. `'.category-a'`), or the values `'all'` or `'none'`. The filter method also accepts a reference to single target element or a collection of target elements to show. +|Param |`boolean` | `[animate]` | An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. +|Param |`function` | `[callback]` | An optional callback function to be invoked after the operation has completed. +|Returns |`Promise.` | A promise resolving with the current state object. + + +###### Example 1: Filtering targets by a class selector + +```js + +mixer.filter('.category-a') + .then(function(state) { + console.log(state.totalShow === containerEl.querySelectorAll('.category-a').length); // true + }); +``` +###### Example 2: Filtering targets by an attribute selector + +```js + +mixer.filter('[data-category~="a"]') + .then(function(state) { + console.log(state.totalShow === containerEl.querySelectorAll('[data-category~="a"]').length); // true + }); +``` +###### Example 3: Filtering targets by a compound selector + +```js + +// Show only those targets with the classes 'category-a' AND 'category-b' + +mixer.filter('.category-a.category-c') + .then(function(state) { + console.log(state.totalShow === containerEl.querySelectorAll('.category-a.category-c').length); // true + }); +``` +###### Example 4: Filtering via an element collection + +```js + +var collection = Array.from(container.querySelectorAll('.mix')); + +console.log(collection.length); // 34 + +// Filter the collection manually using Array.prototype.filter + +var filtered = collection.filter(function(target) { + return parseInt(target.getAttribute('data-price')) > 10; +}); + +console.log(filtered.length); // 22 + +// Pass the filtered collection to MixItUp + +mixer.filter(filtered) + .then(function(state) { + console.log(state.activeFilter.collection.length === 22); // true + }); +``` + +

toggleOn()

+ +*Version added: 3.0.0* + +`.toggleOn(selector [, animate] [, callback])` + +Adds an additional selector to the currently active filter selector, concatenating +as per the logic defined in `controls.toggleLogic`. + +| |Type | Name | Description +|---|--- | --- | --- +|Param |`string` | `selector` | Any valid CSS selector (i.e. `'.category-a'`) +|Param |`boolean` | `[animate]` | An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. +|Param |`function` | `[callback]` | An optional callback function to be invoked after the operation has completed. +|Returns |`Promise.` | A promise resolving with the current state object. + + +###### Example: Toggling on a filter selector + +```js + +console.log(mixer.getState().activeFilter.selector); // '.category-a' + +mixer.toggleOn('.category-b') + .then(function(state) { + console.log(state.activeFilter.selector); // '.category-a, .category-b' + }); +``` + +

toggleOff()

+ +*Version added: 3.0.0* + +`.toggleOff(selector [, animate] [, callback])` + +Removes a selector from the active filter selector. + +| |Type | Name | Description +|---|--- | --- | --- +|Param |`string` | `selector` | Any valid CSS selector (i.e. `'.category-a'`) +|Param |`boolean` | `[animate]` | An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. +|Param |`function` | `[callback]` | An optional callback function to be invoked after the operation has completed. +|Returns |`Promise.` | A promise resolving with the current state object. + + +###### Example: Toggling off a filter selector + +```js + +console.log(mixer.getState().activeFilter.selector); // '.category-a, .category-b' + +mixer.toggleOff('.category-b') + .then(function(state) { + console.log(state.activeFilter.selector); // '.category-a' + }); +``` + +

sort()

+ +*Version added: 2.0.0* + +`.sort(sortString [, animate] [, callback])` + +Sorts all targets in the container according to a provided sort string. + +| |Type | Name | Description +|---|--- | --- | --- +|Param |`string, Array.` | `sortString` | A valid sort string (e.g. `'default'`, `'published-date:asc'`, or `'random'`). The sort method also accepts an array of all target elements in a user-defined order. +|Param |`boolean` | `[animate]` | An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. +|Param |`function` | `[callback]` | An optional callback function to be invoked after the operation has completed. +|Returns |`Promise.` | A promise resolving with the current state object. + + +###### Example 1: Sorting by the default DOM order + +```js + +// Reverse the default order of the targets + +mixer.sort('default:desc') + .then(function(state) { + console.log(state.activeSort.attribute === 'default'); // true + console.log(state.activeSort.order === 'desc'); // true + }); +``` +###### Example 2: Sorting by a custom data-attribute + +```js + +// Sort the targets by the value of a `data-published-date` attribute + +mixer.sort('published-date:asc') + .then(function(state) { + console.log(state.activeSort.attribute === 'published-date'); // true + console.log(state.activeSort.order === 'asc'); // true + }); +``` +###### Example 3: Sorting by multiple attributes + +```js + +// Sort the targets by the value of a `data-published-date` attribute, then by `data-title` + +mixer.sort('published-date:desc data-title:asc') + .then(function(state) { + console.log(state.activeSort.attribute === 'published-date'); // true + console.log(state.activeSort.order === 'desc'); // true + + console.log(state.activeSort.next.attribute === 'title'); // true + console.log(state.activeSort.next.order === 'asc'); // true + }); +``` +###### Example 4: Sorting by random + +```js + +mixer.sort('random') + .then(function(state) { + console.log(state.activeSort.order === 'random') // true + }); +``` +###### Example 5: Sorting via an element collection + +```js + +var collection = Array.from(container.querySelectorAll('.mix')); + +// Swap the position of two elements in the collection: + +var temp = collection[1]; + +collection[1] = collection[0]; +collection[0] = temp; + +// Pass the sorted collection to MixItUp + +mixer.sort(collection) + .then(function(state) { + console.log(state.targets[0] === collection[0]); // true + }); +``` + +

changeLayout()

+ +*Version added: 2.0.0* + +`.changeLayout(containerClassName [, animate] [, callback])` + +Changes the layout of the container by adding, removing or updating a +layout-specific class name. If `animation.animateResizetargets` is +enabled, MixItUp will attempt to gracefully animate the width, height, +and position of targets between layout states. + +| |Type | Name | Description +|---|--- | --- | --- +|Param |`string` | `containerClassName` | A layout-specific class name to add to the container. +|Param |`boolean` | `[animate]` | An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. +|Param |`function` | `[callback]` | An optional callback function to be invoked after the operation has completed. +|Returns |`Promise.` | A promise resolving with the current state object. + + +###### Example 1: Adding a new class name to the container + +```js + +mixer.changeLayout('container-list') + .then(function(state) { + console.log(state.activeContainerClass === 'container-list'); // true + }); +``` +###### Example 2: Removing a previously added class name from the container + +```js + +mixer.changeLayout('') + .then(function(state) { + console.log(state.activeContainerClass === ''); // true + }); +``` + +

dataset()

+ +*Version added: 3.0.0* + +`.dataset(dataset [, animate] [, callback])` + +Updates the contents and order of the container to reflect the provided dataset, +if the dataset API is in use. + +The dataset API is designed for use in API-driven JavaScript applications, and +can be used instead of DOM-based methods such as `.filter()`, `.sort()`, +`.insert()`, etc. When used, insertion, removal, sorting and pagination can be +achieved purely via changes to your data model, without the uglyness of having +to interact with or query the DOM directly. + +| |Type | Name | Description +|---|--- | --- | --- +|Param |`Array.` | `dataset` | An array of objects, each one representing the underlying data model of a target to be rendered. +|Param |`boolean` | `[animate]` | An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. +|Param |`function` | `[callback]` | An optional callback function to be invoked after the operation has completed. +|Returns |`Promise.` | A promise resolving with the current state object. + + +###### Example 1: Rendering a dataset + +```js + +var myDataset = [ + {id: 1, ...}, + {id: 2, ...}, + {id: 3, ...} +]; + +mixer.dataset(myDataset) + .then(function(state) { + console.log(state.totalShow === 3); // true + }); +``` +###### Example 2: Sorting a dataset + +```js + +// Create a new dataset in reverse order + +var newDataset = myDataset.slice().reverse(); + +mixer.dataset(newDataset) + .then(function(state) { + console.log(state.activeDataset[0] === myDataset[2]); // true + }); +``` +###### Example 3: Removing an item from the dataset + +```js + +console.log(myDataset.length); // 3 + +// Create a new dataset with the last item removed. + +var newDataset = myDataset.slice().pop(); + +mixer.dataset(newDataset) + .then(function(state) { + console.log(state.totalShow === 2); // true + }); +``` + +

multimix()

+ +*Version added: 2.0.0* + +`.multimix(multimixCommand [, animate] [, callback])` + +Performs simultaneous `filter`, `sort`, `insert`, `remove` and `changeLayout` +operations as requested. + +| |Type | Name | Description +|---|--- | --- | --- +|Param |`object` | `multimixCommand` | An object containing one or more things to do +|Param |`boolean` | `[animate]` | An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. +|Param |`function` | `[callback]` | An optional callback function to be invoked after the operation has completed. +|Returns |`Promise.` | A promise resolving with the current state object. + + +###### Example 1: Performing simultaneous filtering and sorting + +```js + +mixer.multimix({ + filter: '.category-b', + sort: 'published-date:desc' +}) + .then(function(state) { + console.log(state.activeFilter.selector === '.category-b'); // true + console.log(state.activeSort.attribute === 'published-date'); // true + }); +``` +###### Example 2: Performing simultaneous sorting, insertion, and removal + +```js + +console.log(mixer.getState().totalShow); // 6 + +// NB: When inserting via `multimix()`, an object should be provided as the value +// for the `insert` portion of the command, allowing for a collection of elements +// and an insertion index to be specified. + +mixer.multimix({ + sort: 'published-date:desc', // Sort the container, including any new elements + insert: { + collection: [newElementReferenceA, newElementReferenceB], // Add 2 new elements at index 5 + index: 5 + }, + remove: existingElementReference // Remove 1 existing element +}) + .then(function(state) { + console.log(state.activeSort.attribute === 'published-date'); // true + console.log(state.totalShow === 7); // true + }); +``` + +

insert()

+ +*Version added: 2.0.0* + +`.insert(newElements [, index] [, animate], [, callback])` + +Inserts one or more new target elements into the container at a specified +index. + +To be indexed as targets, new elements must match the `selectors.target` +selector (`'.mix'` by default). + +| |Type | Name | Description +|---|--- | --- | --- +|Param |`HTMLElement, Array., string` | `newElements` | A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element. +|Param |`number` | `index` | The index at which to insert the new element(s). `0` by default. +|Param |`boolean` | `[animate]` | An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. +|Param |`function` | `[callback]` | An optional callback function to be invoked after the operation has completed. +|Returns |`Promise.` | A promise resolving with the current state object. + + +###### Example 1: Inserting a single element via reference + +```js + +console.log(mixer.getState().totalShow); // 0 + +// Create a new element + +var newElement = document.createElement('div'); +newElement.classList.add('mix'); + +mixer.insert(newElement) + .then(function(state) { + console.log(state.totalShow === 1); // true + }); +``` +###### Example 2: Inserting a single element via HTML string + +```js + +console.log(mixer.getState().totalShow); // 1 + +// Create a new element via reference + +var newElementHtml = '
'; + +// Create and insert the new element at index 1 + +mixer.insert(newElementHtml, 1) + .then(function(state) { + console.log(state.totalShow === 2); // true + console.log(state.show[1].outerHTML === newElementHtml); // true + }); +``` +###### Example 3: Inserting multiple elements via reference + +```js + +console.log(mixer.getState().totalShow); // 2 + +// Create an array of new elements to insert. + +var newElement1 = document.createElement('div'); +var newElement2 = document.createElement('div'); + +newElement1.classList.add('mix'); +newElement2.classList.add('mix'); + +var newElementsCollection = [newElement1, newElement2]; + +// Insert the new elements starting at index 1 + +mixer.insert(newElementsCollection, 1) + .then(function(state) { + console.log(state.totalShow === 4); // true + console.log(state.show[1] === newElement1); // true + console.log(state.show[2] === newElement2); // true + }); +``` +###### Example 4: Inserting a jQuery collection object containing one or more elements + +```js + +console.log(mixer.getState().totalShow); // 4 + +var $newElement = $('
'); + +// Insert the new elements starting at index 3 + +mixer.insert($newElement, 3) + .then(function(state) { + console.log(state.totalShow === 5); // true + console.log(state.show[3] === $newElement[0]); // true + }); +``` + +

insertBefore()

+ +*Version added: 3.0.0* + +`.insertBefore(newElements, referenceElement [, animate] [, callback])` + +Inserts one or more new elements before a provided reference element. + +| |Type | Name | Description +|---|--- | --- | --- +|Param |`HTMLElement, Array., string` | `newElements` | A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element. +|Param |`HTMLElement` | `referenceElement` | A reference to an existing element in the container to insert new elements before. +|Param |`boolean` | `[animate]` | An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. +|Param |`function` | `[callback]` | An optional callback function to be invoked after the operation has completed. +|Returns |`Promise.` | A promise resolving with the current state object. + + +###### Example: Inserting a new element before a reference element + +```js + +// An existing reference element is chosen at index 2 + +var referenceElement = mixer.getState().show[2]; + +// Create a new element + +var newElement = document.createElement('div'); +newElement.classList.add('mix'); + +mixer.insertBefore(newElement, referenceElement) + .then(function(state) { + // The new element is inserted into the container at index 2, before the reference element + + console.log(state.show[2] === newElement); // true + + // The reference element is now at index 3 + + console.log(state.show[3] === referenceElement); // true + }); +``` + +

insertAfter()

+ +*Version added: 3.0.0* + +`.insertAfter(newElements, referenceElement [, animate] [, callback])` + +Inserts one or more new elements after a provided reference element. + +| |Type | Name | Description +|---|--- | --- | --- +|Param |`HTMLElement, Array., string` | `newElements` | A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element. +|Param |`HTMLElement` | `referenceElement` | A reference to an existing element in the container to insert new elements after. +|Param |`boolean` | `[animate]` | An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. +|Param |`function` | `[callback]` | An optional callback function to be invoked after the operation has completed. +|Returns |`Promise.` | A promise resolving with the current state object. + + +###### Example: Inserting a new element after a reference element + +```js + +// An existing reference element is chosen at index 2 + +var referenceElement = mixer.getState().show[2]; + +// Create a new element + +var newElement = document.createElement('div'); +newElement.classList.add('mix'); + +mixer.insertAfter(newElement, referenceElement) + .then(function(state) { + // The new element is inserted into the container at index 3, after the reference element + + console.log(state.show[3] === newElement); // true + }); +``` + +

prepend()

+ +*Version added: 3.0.0* + +`.prepend(newElements [,animate] [,callback])` + +Inserts one or more new elements into the container before all existing targets. + +| |Type | Name | Description +|---|--- | --- | --- +|Param |`HTMLElement, Array., string` | `newElements` | A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element. +|Param |`boolean` | `[animate]` | An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. +|Param |`function` | `[callback]` | An optional callback function to be invoked after the operation has completed. +|Returns |`Promise.` | A promise resolving with the current state object. + + +###### Example: Prepending a new element + +```js + +// Create a new element + +var newElement = document.createElement('div'); +newElement.classList.add('mix'); + +// Insert the element into the container + +mixer.prepend(newElement) + .then(function(state) { + console.log(state.show[0] === newElement); // true + }); +``` + +

append()

+ +*Version added: 3.0.0* + +`.append(newElements [,animate] [,callback])` + +Inserts one or more new elements into the container after all existing targets. + +| |Type | Name | Description +|---|--- | --- | --- +|Param |`HTMLElement, Array., string` | `newElements` | A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element. +|Param |`boolean` | `[animate]` | An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. +|Param |`function` | `[callback]` | An optional callback function to be invoked after the operation has completed. +|Returns |`Promise.` | A promise resolving with the current state object. + + +###### Example: Appending a new element + +```js + +// Create a new element + +var newElement = document.createElement('div'); +newElement.classList.add('mix'); + +// Insert the element into the container + +mixer.append(newElement) + .then(function(state) { + console.log(state.show[state.show.length - 1] === newElement); // true + }); +``` + +

remove()

+ +*Version added: 3.0.0* + +`.remove(elements [, animate] [, callback])` + +Removes one or more existing target elements from the container. + +| |Type | Name | Description +|---|--- | --- | --- +|Param |`HTMLElement, Array., string, number` | `elements` | A reference to a single element to remove, an array-like collection of elements, a selector string, or the index of an element to remove. +|Param |`boolean` | `[animate]` | An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. +|Param |`function` | `[callback]` | An optional callback function to be invoked after the operation has completed. +|Returns |`Promise.` | A promise resolving with the current state object. + + +###### Example 1: Removing an element by reference + +```js + +var elementToRemove = containerEl.firstElementChild; + +mixer.remove(elementToRemove) + .then(function(state) { + console.log(state.targets.indexOf(elementToRemove) === -1); // true + }); +``` +###### Example 2: Removing a collection of elements by reference + +```js + +var elementsToRemove = containerEl.querySelectorAll('.category-a'); + +console.log(elementsToRemove.length) // 3 + +mixer.remove(elementsToRemove) + .then(function() { + console.log(containerEl.querySelectorAll('.category-a').length); // 0 + }); +``` +###### Example 3: Removing one or more elements by selector + +```js + +mixer.remove('.category-a') + .then(function() { + console.log(containerEl.querySelectorAll('.category-a').length); // 0 + }); +``` +###### Example 4: Removing an element by index + +```js + +console.log(mixer.getState.totalShow); // 4 + +// Remove the element at index 3 + +mixer.remove(3) + .then(function(state) { + console.log(state.totalShow); // 3 + console.log(state.show[3]); // undefined + }); +``` + +

getConfig()

+ +*Version added: 2.0.0* + +`.getConfig([stringKey])` + +Retrieves the the value of any property or sub-object within the current +mixitup configuration, or the whole configuration object. + +| |Type | Name | Description +|---|--- | --- | --- +|Param |`string` | `[stringKey]` | A "dot-notation" string key +|Returns |`*` | + + +###### Example 1: retrieve the entire configuration object + +```js + +var config = mixer.getConfig(); // Config { ... } +``` +###### Example 2: retrieve a named sub-object of configuration object + +```js + +var animation = mixer.getConfig('animation'); // ConfigAnimation { ... } +``` +###### Example 3: retrieve a value of configuration object via a dot-notation string key + +```js + +var effects = mixer.getConfig('animation.effects'); // 'fade scale' +``` + +

configure()

+ +*Version added: 3.0.0* + +`.configure(config)` + +Updates the configuration of the mixer, after it has been instantiated. + +See the Configuration Object documentation for a full list of avilable +configuration options. + +| |Type | Name | Description +|---|--- | --- | --- +|Param |`object` | `config` | An object containing one of more configuration options. +|Returns |`void` | + + +###### Example 1: Updating animation options + +```js + +mixer.configure({ + animation: { + effects: 'fade translateX(-100%)', + duration: 300 + } +}); +``` +###### Example 2: Removing a callback after it has been set + +```js + +var mixer; + +function handleMixEndOnce() { + // Do something .. + + // Then nullify the callback + + mixer.configure({ + callbacks: { + onMixEnd: null + } + }); +}; + +// Instantiate a mixer with a callback defined + +mixer = mixitup(containerEl, { + callbacks: { + onMixEnd: handleMixEndOnce + } +}); +``` + +

getState()

+ +*Version added: 2.0.0* + +`.getState();` + +Returns an object containing information about the current state of the +mixer. See the State Object documentation for more information. + +NB: State objects are immutable and should therefore be regenerated +after any operation. + +| |Type | Name | Description +|---|--- | --- | --- +|Returns |`mixitup.State` | An object reflecting the current state of the mixer. + + +###### Example: Retrieving a state object + +```js + +var state = mixer.getState(); + +console.log(state.totalShow + 'targets are currently shown'); +``` + +

forceRefresh()

+ +*Version added: 2.1.2* + +`.forceRefresh()` + +Forces the re-indexing all targets within the container. + +This should only be used if some other piece of code in your application +has manipulated the contents of your container, which should be avoided. + +If you need to add or remove target elements from the container, use +the built-in `.insert()` or `.remove()` methods, and MixItUp will keep +itself up to date. + +| |Type | Name | Description +|---|--- | --- | --- +|Returns |`void` | + + +###### Example: Force refreshing the mixer after external DOM manipulation + +```js + +console.log(mixer.getState().totalShow); // 3 + +// An element is removed from the container via some external DOM manipulation code: + +containerEl.removeChild(containerEl.firstElementChild); + +// The mixer does not know that the number of targets has changed: + +console.log(mixer.getState().totalShow); // 3 + +mixer.forceRefresh(); + +// After forceRefresh, the mixer is in sync again: + +console.log(mixer.getState().totalShow); // 2 +``` + +

forceRender()

+ +*Version added: 3.2.1* + +`.forceRender()` + +Forces the re-rendering of all targets when using the Dataset API. + +By default, targets are only re-rendered when `data.dirtyCheck` is +enabled, and an item's data has changed when `dataset()` is called. + +The `forceRender()` method allows for the re-rendering of all targets +in response to some arbitrary event, such as the changing of the target +render function. + +Targets are rendered against their existing data. + +| |Type | Name | Description +|---|--- | --- | --- +|Returns |`void` | + + +###### Example: Force render targets after changing the target render function + +```js + +console.log(container.innerHTML); // ... Foo ... + +mixer.configure({ + render: { + target: (item) => `${item.title}` + } +}); + +mixer.forceRender(); + +console.log(container.innerHTML); // ... Foo ... +``` + +

destroy()

+ +*Version added: 2.0.0* + +`.destroy([cleanUp])` + +Removes mixitup functionality from the container, unbinds all control +event handlers, and deletes the mixer instance from MixItUp's internal +cache. + +This should be performed whenever a mixer's container is removed from +the DOM, such as during a page change in a single page application, +or React's `componentWillUnmount()`. + +| |Type | Name | Description +|---|--- | --- | --- +|Param |`boolean` | `[cleanUp]` | An optional boolean dictating whether or not to clean up any inline `display: none;` styling applied to hidden targets. +|Returns |`void` | + + +###### Example: Destroying the mixer before removing its container element + +```js + +mixer.destroy(); + +containerEl.parentElement.removeChild(containerEl); +``` + diff --git a/mixitup-3.3.1/docs/mixitup.State.md b/mixitup-3.3.1/docs/mixitup.State.md new file mode 100644 index 0000000..a031431 --- /dev/null +++ b/mixitup-3.3.1/docs/mixitup.State.md @@ -0,0 +1,248 @@ +# mixitup.State + +## Overview + +`mixitup.State` objects expose various pieces of data detailing the state of +a MixItUp instance. They are provided at the start and end of any operation via +callbacks and events, with the most recent state stored between operations +for retrieval at any time via the API. + +### Contents + +- [id](#id) +- [activeFilter](#activeFilter) +- [activeSort](#activeSort) +- [activeContainerClassName](#activeContainerClassName) +- [container](#container) +- [targets](#targets) +- [hide](#hide) +- [show](#show) +- [matching](#matching) +- [totalTargets](#totalTargets) +- [totalShow](#totalShow) +- [totalHide](#totalHide) +- [totalMatching](#totalMatching) +- [hasFailed](#hasFailed) +- [triggerElement](#triggerElement) +- [activeDataset](#activeDataset) + + +

id

+ + + + +The ID of the mixer instance. + + +|Type | Default +|--- | --- +|`string`| `''` + + +

activeFilter

+ + + + +The currently active filter command as set by a control click or API call. + + +|Type | Default +|--- | --- +|`mixitup.CommandFilter`| `null` + + +

activeSort

+ + + + +The currently active sort command as set by a control click or API call. + + +|Type | Default +|--- | --- +|`mixitup.CommandSort`| `null` + + +

activeContainerClassName

+ + + + +The current layout-specific container class name, if applied. + + +|Type | Default +|--- | --- +|`string`| `''` + + +

container

+ + + + +A reference to the container element that the mixer is instantiated on. + + +|Type | Default +|--- | --- +|`Element`| `null` + + +

targets

+ + + + +An array of all target elements indexed by the mixer. + + +|Type | Default +|--- | --- +|`Array.`| `[]` + + +

hide

+ + + + +An array of all target elements not matching the current filter. + + +|Type | Default +|--- | --- +|`Array.`| `[]` + + +

show

+ + + + +An array of all target elements matching the current filter and any additional +limits applied such as pagination. + + +|Type | Default +|--- | --- +|`Array.`| `[]` + + +

matching

+ + + + +An array of all target elements matching the current filter irrespective of +any additional limits applied such as pagination. + + +|Type | Default +|--- | --- +|`Array.`| `[]` + + +

totalTargets

+ + + + +An integer representing the total number of target elements indexed by the +mixer. Equivalent to `state.targets.length`. + + +|Type | Default +|--- | --- +|`number`| `-1` + + +

totalShow

+ + + + +An integer representing the total number of target elements matching the +current filter and any additional limits applied such as pagination. +Equivalent to `state.show.length`. + + +|Type | Default +|--- | --- +|`number`| `-1` + + +

totalHide

+ + + + +An integer representing the total number of target elements not matching +the current filter. Equivalent to `state.hide.length`. + + +|Type | Default +|--- | --- +|`number`| `-1` + + +

totalMatching

+ + + + +An integer representing the total number of target elements matching the +current filter irrespective of any other limits applied such as pagination. +Equivalent to `state.matching.length`. + + +|Type | Default +|--- | --- +|`number`| `-1` + + +

hasFailed

+ + + + +A boolean indicating whether the last operation "failed", i.e. no targets +could be found matching the filter. + + +|Type | Default +|--- | --- +|`boolean`| `false` + + +

triggerElement

+ + + + +The DOM element that was clicked if the last operation was triggered by the +clicking of a control and not an API call. + + +|Type | Default +|--- | --- +|`Elementnull`| `null` + + +

activeDataset

+ + + + +The currently active dataset underlying the rendered targets, if the +dataset API is in use. + + +|Type | Default +|--- | --- +|`Array.`| `null` + + diff --git a/mixitup-3.3.1/docs/mixitup.md b/mixitup-3.3.1/docs/mixitup.md new file mode 100644 index 0000000..c9f7857 --- /dev/null +++ b/mixitup-3.3.1/docs/mixitup.md @@ -0,0 +1,47 @@ +#mixitup() + +*Version added: 3.0.0* + +`mixitup(container [,config] [,foreignDoc])` + +The `mixitup()` "factory" function creates and returns individual instances +of MixItUp, known as "mixers", on which API methods can be called. + +When loading MixItUp via a script tag, the factory function is accessed +via the global variable `mixitup`. When using a module loading +system (e.g. ES2015, CommonJS, RequireJS), the factory function is +exported into your module when you require the MixItUp library. + +| |Type | Name | Description +|---|--- | --- | --- +|Param |`Element, string` | `container` | A DOM element or selector string representing the container(s) on which to instantiate MixItUp. +|Param |`object` | `[config]` | An optional "configuration object" used to customize the behavior of the MixItUp instance. +|Param |`object` | `[foreignDoc]` | An optional reference to a `document`, which can be used to control a MixItUp instance in an iframe. +|Returns |`mixitup.Mixer` | A "mixer" object holding the MixItUp instance. + +###### Example 1: Creating a mixer instance with an element reference + +```js +var containerEl = document.querySelector('.container'); + +var mixer = mixitup(containerEl); +``` +###### Example 2: Creating a mixer instance with a selector string + +```js +var mixer = mixitup('.container'); +``` +###### Example 3: Passing a configuration object + +```js +var mixer = mixitup(containerEl, { + animation: { + effects: 'fade scale(0.5)' + } +}); +``` +###### Example 4: Passing an iframe reference + +```js +var mixer = mixitup(containerEl, config, foreignDocument); +``` diff --git a/mixitup-3.3.1/gulpfile.js b/mixitup-3.3.1/gulpfile.js new file mode 100644 index 0000000..6374ff1 --- /dev/null +++ b/mixitup-3.3.1/gulpfile.js @@ -0,0 +1,90 @@ +'use strict'; + +const gulp = require('gulp'); +const jshint = require('gulp-jshint'); +const stylish = require('jshint-stylish'); +const rename = require('gulp-rename'); +const jscs = require('gulp-jscs'); +const uglify = require('gulp-uglify'); +const livereload = require('gulp-livereload'); +const exec = require('child_process').exec; + +gulp.task('default', ['watch']); + +gulp.task('watch', () => { + livereload.listen(35730); + + gulp.watch([ + './src/*.js', + './src/*.hbs' + ], ['reload-js']) + .on('change', function(e) { + console.log( + '[gulp-watch] file ' + + e.path + + ' was ' + + e.type + + ', building' + ); + }); +}); + +gulp.task('reload-js', ['build-dist'], () => { + return livereload.changed(); +}); + +gulp.task('prod', ['uglify']); + +gulp.task('uglify', ['build'], () => { + return gulp.src([ + './dist/mixitup.js' + ]) + .pipe(uglify({ + preserveComments: 'license' + })) + .pipe(rename('mixitup.min.js')) + .on('error', e => console.error('[uglify] ' + e.message)) + .pipe(gulp.dest('./dist/')) + .pipe(gulp.dest('./demos/')); +}); + +gulp.task('build', ['build-dist'], done => { + exec('node node_modules/mixitup-build/docs.js -s mixitup.js', (e, out) => { + if (out) { + console.log(out); + } + + done(e); + }); +}); + +gulp.task('build-dist', ['lint', 'code-style'], done => { + exec('node node_modules/mixitup-build/dist.js -o mixitup.js', (e, out) => { + if (out) { + console.log(out); + } + + done(e); + }); +}); + +gulp.task('lint', () => { + return gulp.src([ + './src/*.js' + ], { + base: '/' + }) + .pipe(jshint('./.jshintrc')) + .pipe(jshint.reporter(stylish)) + .pipe(jshint.reporter('fail')); +}); + +gulp.task('code-style', () => { + return gulp.src([ + './src/*.js' + ], { + base: '/' + }) + .pipe(jscs()) + .pipe(jscs.reporter()); +}); \ No newline at end of file diff --git a/mixitup-3.3.1/package.json b/mixitup-3.3.1/package.json new file mode 100644 index 0000000..a7f24bb --- /dev/null +++ b/mixitup-3.3.1/package.json @@ -0,0 +1,41 @@ +{ + "name": "mixitup", + "title": "MixItUp", + "version": "3.3.1", + "description": "A high-performance, dependency-free library for animated filtering, sorting and more", + "author": "KunkaLabs Limited", + "homepage": "https://www.kunkalabs.com/mixitup/", + "license": "SEE LICENSE IN README.md", + "main": "./dist/mixitup.js", + "repository": { + "type": "git", + "url": "https://github.com/patrickkunka/mixitup/" + }, + "scripts": { + "test": "mocha ./tests/unit/main.js", + "cover": "istanbul cover _mocha ./tests/unit/main.js", + "coveralls": "npm run cover -- --report lcovonly && cat ./coverage/lcov.info | coveralls" + }, + "devDependencies": { + "chai": "^3.5.0", + "chai-as-promised": "^5.3.0", + "chai-shallow-deep-equal": "^1.4.0", + "coveralls": "^2.11.14", + "gulp": "^3.8.8", + "gulp-jscs": "^4.0.0", + "gulp-jshint": "^1.8.5", + "gulp-livereload": "~2.1.1", + "gulp-rename": "^1.2.2", + "gulp-uglify": "~1.5.1", + "istanbul": "^0.4.5", + "jsdom": "9.4.2", + "jsdom-global": "2.0.0", + "jshint-stylish": "~1.0.0", + "merge-stream": "^0.1.7", + "mixitup-build": "git://github.com/patrickkunka/mixitup-build.git", + "mocha": "^3.0.2", + "mocha-lcov-reporter": "^1.2.0", + "vinyl-buffer": "^1.0.0", + "vinyl-source-stream": "^1.1.0" + } +} diff --git a/mixitup-3.3.1/src/banner.js b/mixitup-3.3.1/src/banner.js new file mode 100644 index 0000000..b2512c7 --- /dev/null +++ b/mixitup-3.3.1/src/banner.js @@ -0,0 +1,15 @@ +/**! + * {{title}} v{{version}} + * {{description}} + * Build {{buildId}} + * + * @copyright Copyright {{beginCopyrightYear}}-{{currentYear}} {{author}}. + * @author {{author}}. + * @link {{websiteUrl}} + * + * @license Commercial use requires a commercial license. + * {{commercialLicenseUrl}} + * + * Non-commercial use permitted under same terms as CC BY-NC 3.0 license. + * {{nonCommercialLicenseUrl}} + */ diff --git a/mixitup-3.3.1/src/base-static.js b/mixitup-3.3.1/src/base-static.js new file mode 100644 index 0000000..4a3d7b4 --- /dev/null +++ b/mixitup-3.3.1/src/base-static.js @@ -0,0 +1,68 @@ +/* global mixitup, h */ + +/** + * The BaseStatic class holds a set of static methods which are then added to all other + * extensible MixItUp classes as a means of integrating extensions via the addition of new + * methods and/or actions and hooks. + * + * @constructor + * @namespace + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.BaseStatic = function() { + this.actions = {}; + this.filters = {}; + + /** + * Performs a shallow extend on the class's prototype, adding one or more new members to + * the class in a single operation. + * + * @memberof mixitup.BaseStatic + * @public + * @static + * @since 2.1.0 + * @param {object} extension + * @return {void} + */ + + this.extend = function(extension) { + h.extend(this.prototype, extension); + }; + + /** + * Registers a function to be called on the action hook of the provided name. + * + * @memberof mixitup.BaseStatic + * @public + * @static + * @since 2.1.0 + * @param {string} hookName + * @param {string} extensionName + * @param {function} func + * @return {void} + */ + + this.registerAction = function(hookName, extensionName, func) { + (this.actions[hookName] = this.actions[hookName] || {})[extensionName] = func; + }; + + /** + * Registers a function to be called on the filter of the provided name. + * + * @memberof mixitup.BaseStatic + * @public + * @static + * @since 2.1.0 + * @param {string} hookName + * @param {string} extensionName + * @param {function} func + * @return {void} + */ + + this.registerFilter = function(hookName, extensionName, func) { + (this.filters[hookName] = this.filters[hookName] || {})[extensionName] = func; + }; +}; \ No newline at end of file diff --git a/mixitup-3.3.1/src/base.js b/mixitup-3.3.1/src/base.js new file mode 100644 index 0000000..164f58c --- /dev/null +++ b/mixitup-3.3.1/src/base.js @@ -0,0 +1,76 @@ +/* global mixitup, h */ + +/** + * The Base class adds instance methods to all other extensible MixItUp classes, + * enabling the calling of any registered hooks. + * + * @constructor + * @namespace + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.Base = function() {}; + +mixitup.Base.prototype = { + constructor: mixitup.Base, + + /** + * Calls any registered hooks for the provided action. + * + * @memberof mixitup.Base + * @private + * @instance + * @since 2.0.0 + * @param {string} actionName + * @param {Array<*>} args + * @return {void} + */ + + callActions: function(actionName, args) { + var self = this, + hooks = self.constructor.actions[actionName], + extensionName = ''; + + if (!hooks || h.isEmptyObject(hooks)) return; + + for (extensionName in hooks) { + hooks[extensionName].apply(self, args); + } + }, + + /** + * Calls any registered hooks for the provided filter. + * + * @memberof mixitup.Base + * @private + * @instance + * @since 2.0.0 + * @param {string} filterName + * @param {*} input + * @param {Array<*>} args + * @return {*} + */ + + callFilters: function(filterName, input, args) { + var self = this, + hooks = self.constructor.filters[filterName], + output = input, + extensionName = ''; + + if (!hooks || h.isEmptyObject(hooks)) return output; + + args = args || []; + + for (extensionName in hooks) { + args = h.arrayFromList(args); + + args.unshift(output); + + output = hooks[extensionName].apply(self, args); + } + + return output; + } +}; \ No newline at end of file diff --git a/mixitup-3.3.1/src/collection.js b/mixitup-3.3.1/src/collection.js new file mode 100644 index 0000000..3fadf92 --- /dev/null +++ b/mixitup-3.3.1/src/collection.js @@ -0,0 +1,86 @@ +/* global mixitup, h */ + +/** + * A jQuery-collection-like wrapper around one or more `mixitup.Mixer` instances + * allowing simultaneous control of said instances similar to the MixItUp 2 API. + * + * @example + * new mixitup.Collection(instances) + * + * @constructor + * @namespace + * @memberof mixitup + * @private + * @since 3.0.0 + * @param {mixitup.Mixer[]} instances + */ + +mixitup.Collection = function(instances) { + var instance = null, + i = -1; + + this.callActions('beforeConstruct'); + + for (i = 0; instance = instances[i]; i++) { + this[i] = instance; + } + + this.length = instances.length; + + this.callActions('afterConstruct'); + + h.freeze(this); +}; + +mixitup.BaseStatic.call(mixitup.Collection); + +mixitup.Collection.prototype = Object.create(mixitup.Base.prototype); + +h.extend(mixitup.Collection.prototype, +/** @lends mixitup.Collection */ +{ + constructor: mixitup.Collection, + + /** + * Calls a method on all instances in the collection by passing the method + * name as a string followed by any applicable parameters to be curried into + * to the method. + * + * @example + * .mixitup(methodName[,arg1][,arg2..]); + * + * @example + * var collection = new Collection([mixer1, mixer2]); + * + * return collection.mixitup('filter', '.category-a') + * .then(function(states) { + * state.forEach(function(state) { + * console.log(state.activeFilter.selector); // .category-a + * }); + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {string} methodName + * @return {Promise>} + */ + + mixitup: function(methodName) { + var self = this, + instance = null, + args = Array.prototype.slice.call(arguments), + tasks = [], + i = -1; + + this.callActions('beforeMixitup'); + + args.shift(); + + for (i = 0; instance = self[i]; i++) { + tasks.push(instance[methodName].apply(instance, args)); + } + + return self.callFilters('promiseMixitup', h.all(tasks, mixitup.libraries), arguments); + } +}); \ No newline at end of file diff --git a/mixitup-3.3.1/src/command-change-layout.js b/mixitup-3.3.1/src/command-change-layout.js new file mode 100644 index 0000000..3f6e394 --- /dev/null +++ b/mixitup-3.3.1/src/command-change-layout.js @@ -0,0 +1,28 @@ +/* global mixitup, h */ + +/** + * An object into which all arbitrary arguments sent to '.changeLayout()' are mapped. + * + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.CommandChangeLayout = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.containerClassName = ''; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.CommandChangeLayout); + +mixitup.CommandChangeLayout.prototype = Object.create(mixitup.Base.prototype); + +mixitup.CommandChangeLayout.prototype.constructor = mixitup.CommandChangeLayout; \ No newline at end of file diff --git a/mixitup-3.3.1/src/command-dataset.js b/mixitup-3.3.1/src/command-dataset.js new file mode 100644 index 0000000..8e4cb33 --- /dev/null +++ b/mixitup-3.3.1/src/command-dataset.js @@ -0,0 +1,28 @@ +/* global mixitup, h */ + +/** + * An object into which all arbitrary arguments sent to '.dataset()' are mapped. + * + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.CommandDataset = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.dataset = null; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.CommandDataset); + +mixitup.CommandDataset.prototype = Object.create(mixitup.Base.prototype); + +mixitup.CommandDataset.prototype.constructor = mixitup.CommandDataset; \ No newline at end of file diff --git a/mixitup-3.3.1/src/command-filter.js b/mixitup-3.3.1/src/command-filter.js new file mode 100644 index 0000000..8cc9a10 --- /dev/null +++ b/mixitup-3.3.1/src/command-filter.js @@ -0,0 +1,30 @@ +/* global mixitup, h */ + +/** + * An object into which all arbitrary arguments sent to '.filter()' are mapped. + * + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.CommandFilter = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.selector = ''; + this.collection = null; + this.action = 'show'; // enum: ['show', 'hide'] + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.CommandFilter); + +mixitup.CommandFilter.prototype = Object.create(mixitup.Base.prototype); + +mixitup.CommandFilter.prototype.constructor = mixitup.CommandFilter; \ No newline at end of file diff --git a/mixitup-3.3.1/src/command-insert.js b/mixitup-3.3.1/src/command-insert.js new file mode 100644 index 0000000..c1b295b --- /dev/null +++ b/mixitup-3.3.1/src/command-insert.js @@ -0,0 +1,31 @@ +/* global mixitup, h */ + +/** + * An object into which all arbitrary arguments sent to '.insert()' are mapped. + * + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.CommandInsert = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.index = 0; + this.collection = []; + this.position = 'before'; // enum: ['before', 'after'] + this.sibling = null; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.CommandInsert); + +mixitup.CommandInsert.prototype = Object.create(mixitup.Base.prototype); + +mixitup.CommandInsert.prototype.constructor = mixitup.CommandInsert; \ No newline at end of file diff --git a/mixitup-3.3.1/src/command-multimix.js b/mixitup-3.3.1/src/command-multimix.js new file mode 100644 index 0000000..8cee97d --- /dev/null +++ b/mixitup-3.3.1/src/command-multimix.js @@ -0,0 +1,32 @@ +/* global mixitup, h */ + +/** + * An object into which all arbitrary arguments sent to '.multimix()' are mapped. + * + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.CommandMultimix = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.filter = null; + this.sort = null; + this.insert = null; + this.remove = null; + this.changeLayout = null; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.CommandMultimix); + +mixitup.CommandMultimix.prototype = Object.create(mixitup.Base.prototype); + +mixitup.CommandMultimix.prototype.constructor = mixitup.CommandMultimix; \ No newline at end of file diff --git a/mixitup-3.3.1/src/command-remove.js b/mixitup-3.3.1/src/command-remove.js new file mode 100644 index 0000000..2b743d6 --- /dev/null +++ b/mixitup-3.3.1/src/command-remove.js @@ -0,0 +1,29 @@ +/* global mixitup, h */ + +/** + * An object into which all arbitrary arguments sent to '.remove()' are mapped. + * + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.CommandRemove = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.targets = []; + this.collection = []; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.CommandRemove); + +mixitup.CommandRemove.prototype = Object.create(mixitup.Base.prototype); + +mixitup.CommandRemove.prototype.constructor = mixitup.CommandRemove; \ No newline at end of file diff --git a/mixitup-3.3.1/src/command-sort.js b/mixitup-3.3.1/src/command-sort.js new file mode 100644 index 0000000..b3d747a --- /dev/null +++ b/mixitup-3.3.1/src/command-sort.js @@ -0,0 +1,32 @@ +/* global mixitup, h */ + +/** + * An object into which all arbitrary arguments sent to '.sort()' are mapped. + * + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.CommandSort = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.sortString = ''; + this.attribute = ''; + this.order = 'asc'; + this.collection = null; + this.next = null; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.CommandSort); + +mixitup.CommandSort.prototype = Object.create(mixitup.Base.prototype); + +mixitup.CommandSort.prototype.constructor = mixitup.CommandSort; \ No newline at end of file diff --git a/mixitup-3.3.1/src/config-animation.js b/mixitup-3.3.1/src/config-animation.js new file mode 100644 index 0000000..7e029d1 --- /dev/null +++ b/mixitup-3.3.1/src/config-animation.js @@ -0,0 +1,491 @@ +/* global mixitup, h */ + +/** + * A group of properties defining the mixer's animation and effects settings. + * + * @constructor + * @memberof mixitup.Config + * @name animation + * @namespace + * @public + * @since 2.0.0 + */ + +mixitup.ConfigAnimation = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A boolean dictating whether or not animation should be enabled for the MixItUp instance. + * If `false`, all operations will occur instantly and syncronously, although callback + * functions and any returned promises will still be fulfilled. + * + * @example Example: Create a mixer with all animations disabled + * var mixer = mixitup(containerEl, { + * animation: { + * enable: false + * } + * }); + * + * @name enable + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default true + */ + + this.enable = true; + + /** + * A string of one or more space-seperated properties to which transitions will be + * applied for all filtering animations. + * + * Properties can be listed any order or combination, although they will be applied in a specific + * predefined order to produce consistent results. + * + * To learn more about available effects, experiment with our + * sandbox demo and try out the "Export config" button in the Animation options drop down. + * + * @example Example: Apply "fade" and "translateZ" effects to all animations + * // As targets are filtered in and out, they will fade between + * // opacity 1 and 0 and transform between translateZ(-100px) and + * // translateZ(0). + * + * var mixer = mixitup(containerEl, { + * animation: { + * effects: 'fade translateZ(-100px)' + * } + * }); + * + * @name effects + * @memberof mixitup.Config.animation + * @instance + * @type {string} + * @default 'fade scale' + */ + + this.effects = 'fade scale'; + + /** + * A string of one or more space-seperated effects to be applied only to filter-in + * animations, overriding `config.animation.effects` if set. + * + * @example Example: Apply downwards vertical translate to targets being filtered in + * + * var mixer = mixitup(containerEl, { + * animation: { + * effectsIn: 'fade translateY(-100%)' + * } + * }); + * + * @name effectsIn + * @memberof mixitup.Config.animation + * @instance + * @type {string} + * @default '' + */ + + this.effectsIn = ''; + + /** + * A string of one or more space-seperated effects to be applied only to filter-out + * animations, overriding `config.animation.effects` if set. + * + * @example Example: Apply upwards vertical translate to targets being filtered out + * + * var mixer = mixitup(containerEl, { + * animation: { + * effectsOut: 'fade translateY(-100%)' + * } + * }); + * + * @name effectsOut + * @memberof mixitup.Config.animation + * @instance + * @type {string} + * @default '' + */ + + this.effectsOut = ''; + + /** + * An integer dictating the duration of all MixItUp animations in milliseconds, not + * including any additional delay apllied via the `'stagger'` effect. + * + * @example Example: Apply an animation duration of 200ms to all mixitup animations + * + * var mixer = mixitup(containerEl, { + * animation: { + * duration: 200 + * } + * }); + * + * @name duration + * @memberof mixitup.Config.animation + * @instance + * @type {number} + * @default 600 + */ + + this.duration = 600; + + /** + * A valid CSS3 transition-timing function or shorthand. For a full list of accepted + * values, visit easings.net. + * + * @example Example 1: Apply "ease-in-out" easing to all animations + * + * var mixer = mixitup(containerEl, { + * animation: { + * easing: 'ease-in-out' + * } + * }); + * + * @example Example 2: Apply a custom "cubic-bezier" easing function to all animations + * var mixer = mixitup(containerEl, { + * animation: { + * easing: 'cubic-bezier(0.645, 0.045, 0.355, 1)' + * } + * }); + * + * @name easing + * @memberof mixitup.Config.animation + * @instance + * @type {string} + * @default 'ease' + */ + + this.easing = 'ease'; + + /** + * A boolean dictating whether or not to apply perspective to the MixItUp container + * during animations. By default, perspective is always applied and creates the + * illusion of three-dimensional space for effects such as `translateZ`, `rotateX`, + * and `rotateY`. + * + * You may wish to disable this and define your own perspective settings via CSS. + * + * @example Example: Prevent perspective from being applied to any 3D transforms + * var mixer = mixitup(containerEl, { + * animation: { + * applyPerspective: false + * } + * }); + * + * @name applyPerspective + * @memberof mixitup.Config.animation + * @instance + * @type {bolean} + * @default true + */ + + this.applyPerspective = true; + + /** + * The perspective distance value to be applied to the container during animations, + * affecting any 3D-transform-based effects. + * + * @example Example: Set a perspective distance of 2000px + * var mixer = mixitup(containerEl, { + * animation: { + * effects: 'rotateY(-25deg)', + * perspectiveDistance: '2000px' + * } + * }); + * + * @name perspectiveDistance + * @memberof mixitup.Config.animation + * @instance + * @type {string} + * @default '3000px' + */ + + this.perspectiveDistance = '3000px'; + + /** + * The perspective-origin value to be applied to the container during animations, + * affecting any 3D-transform-based effects. + * + * @example Example: Set a perspective origin in the top-right of the container + * var mixer = mixitup(containerEl, { + * animation: { + * effects: 'transateZ(-200px)', + * perspectiveOrigin: '100% 0' + * } + * }); + * + * @name perspectiveOrigin + * @memberof mixitup.Config.animation + * @instance + * @type {string} + * @default '50% 50%' + */ + + this.perspectiveOrigin = '50% 50%'; + + /** + * A boolean dictating whether or not to enable the queuing of operations. + * + * If `true` (default), and a control is clicked or an API call is made while another + * operation is progress, the operation will go into the queue and will be automatically exectuted + * when the previous operaitons is finished. + * + * If `false`, any requested operations will be ignored, and the `onMixBusy` callback and `mixBusy` + * event will be fired. If `debug.showWarnings` is enabled, a console warning will also occur. + * + * @example Example: Disable queuing + * var mixer = mixitup(containerEl, { + * animation: { + * queue: false + * } + * }); + * + * @name queue + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default true + */ + + this.queue = true; + + /** + * An integer dictacting the maximum number of operations allowed in the queue at + * any time, when queuing is enabled. + * + * @example Example: Allow a maximum of 5 operations in the queue at any time + * var mixer = mixitup(containerEl, { + * animation: { + * queueLimit: 5 + * } + * }); + * + * @name queueLimit + * @memberof mixitup.Config.animation + * @instance + * @type {number} + * @default 3 + */ + + this.queueLimit = 3; + + /** + * A boolean dictating whether or not to transition the height and width of the + * container as elements are filtered in and out. If disabled, the container height + * will change abruptly. + * + * It may be desirable to disable this on mobile devices as the CSS `height` and + * `width` properties do not receive GPU-acceleration and can therefore cause stuttering. + * + * @example Example 1: Disable the transitioning of the container height and/or width + * var mixer = mixitup(containerEl, { + * animation: { + * animateResizeContainer: false + * } + * }); + * + * @example Example 2: Disable the transitioning of the container height and/or width for mobile devices only + * var mixer = mixitup(containerEl, { + * animation: { + * animateResizeContainer: myFeatureTests.isMobile ? false : true + * } + * }); + * + * @name animateResizeContainer + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default true + */ + + this.animateResizeContainer = true; + + /** + * A boolean dictating whether or not to transition the height and width of target + * elements as they change throughout the course of an animation. + * + * This is often a must for flex-box grid layouts where the size of target elements may change + * depending on final their position in relation to their siblings, or for `.changeLayout()` + * operations where the size of targets change between layouts. + * + * NB: This feature requires additional calculations and manipulation to non-hardware-accelerated + * properties which may adversely affect performance on slower devices, and is therefore + * disabled by default. + * + * @example Example: Enable the transitioning of target widths and heights + * var mixer = mixitup(containerEl, { + * animation: { + * animateResizeTargets: true + * } + * }); + * + * @name animateResizeTargets + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default false + */ + + this.animateResizeTargets = false; + + /** + * A custom function used to manipulate the order in which the stagger delay is + * incremented when using the ‘stagger’ effect. + * + * When using the 'stagger' effect, the delay applied to each target element is incremented + * based on its index. You may create a custom function to manipulate the order in which the + * delay is incremented and create engaging non-linear stagger effects. + * + * The function receives the index of the target element as a parameter, and must + * return an integer which serves as the multiplier for the stagger delay. + * + * @example Example 1: Stagger target elements by column in a 3-column grid + * var mixer = mixitup(containerEl, { + * animation: { + * effects: 'fade stagger(100ms)', + * staggerSequence: function(i) { + * return i % 3; + * } + * } + * }); + * + * @example Example 2: Using an algorithm to produce a more complex sequence + * var mixer = mixitup(containerEl, { + * animation: { + * effects: 'fade stagger(100ms)', + * staggerSequence: function(i) { + * return (2*i) - (5*((i/3) - ((1/3) * (i%3)))); + * } + * } + * }); + * + * @name staggerSequence + * @memberof mixitup.Config.animation + * @instance + * @type {function} + * @default null + */ + + this.staggerSequence = null; + + /** + * A boolean dictating whether or not to reverse the direction of `translate` + * and `rotate` transforms for elements being filtered out. + * + * It can be used to create carousel-like animations where elements enter and exit + * from opposite directions. If enabled, the effect `translateX(-100%)` for elements + * being filtered in would become `translateX(100%)` for targets being filtered out. + * + * This functionality can also be achieved by providing seperate effects + * strings for `config.animation.effectsIn` and `config.animation.effectsOut`. + * + * @example Example: Reverse the desired direction on any translate/rotate effect for targets being filtered out + * // Elements being filtered in will be translated from '100%' to '0' while + * // elements being filtered out will be translated from 0 to '-100%' + * + * var mixer = mixitup(containerEl, { + * animation: { + * effects: 'fade translateX(100%)', + * reverseOut: true, + * nudge: false // Disable nudging to create a carousel-like effect + * } + * }); + * + * @name reverseOut + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default false + */ + + this.reverseOut = false; + + /** + * A boolean dictating whether or not to "nudge" the animation path of targets + * when they are being filtered in and out simulatenously. + * + * This has been the default behavior of MixItUp since version 1, but it + * may be desirable to disable this effect when filtering directly from + * one exclusive set of targets to a different exclusive set of targets, + * to create a carousel-like effect, or a generally more subtle animation. + * + * @example Example: Disable the "nudging" of targets being filtered in and out simulatenously + * + * var mixer = mixitup(containerEl, { + * animation: { + * nudge: false + * } + * }); + * + * @name nudge + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default true + */ + + this.nudge = true; + + /** + * A boolean dictating whether or not to clamp the height of the container while MixItUp's + * geometry tests are carried out before an operation. + * + * To prevent scroll-bar flicker, clamping is turned on by default. But in the case where the + * height of the container might affect its vertical positioning in the viewport + * (e.g. a vertically-centered container), this should be turned off to ensure accurate + * test results and a smooth animation. + * + * @example Example: Disable container height-clamping + * + * var mixer = mixitup(containerEl, { + * animation: { + * clampHeight: false + * } + * }); + * + * @name clampHeight + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default true + */ + + this.clampHeight = true; + + /** + * A boolean dictating whether or not to clamp the width of the container while MixItUp's + * geometry tests are carried out before an operation. + * + * To prevent scroll-bar flicker, clamping is turned on by default. But in the case where the + * width of the container might affect its horitzontal positioning in the viewport + * (e.g. a horizontall-centered container), this should be turned off to ensure accurate + * test results and a smooth animation. + * + * @example Example: Disable container width-clamping + * + * var mixer = mixitup(containerEl, { + * animation: { + * clampWidth: false + * } + * }); + * + * @name clampWidth + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default true + */ + + this.clampWidth = true; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.ConfigAnimation); + +mixitup.ConfigAnimation.prototype = Object.create(mixitup.Base.prototype); + +mixitup.ConfigAnimation.prototype.constructor = mixitup.ConfigAnimation; \ No newline at end of file diff --git a/mixitup-3.3.1/src/config-behavior.js b/mixitup-3.3.1/src/config-behavior.js new file mode 100644 index 0000000..c8b6b79 --- /dev/null +++ b/mixitup-3.3.1/src/config-behavior.js @@ -0,0 +1,73 @@ +/* global mixitup, h */ + +/** + * A group of properties relating to the behavior of the Mixer. + * + * @constructor + * @memberof mixitup.Config + * @name behavior + * @namespace + * @public + * @since 3.1.12 + */ + +mixitup.ConfigBehavior = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A boolean dictating whether to allow "live" sorting of the mixer. + * + * Because of the expensive nature of sorting, MixItUp makes use of several + * internal optimizations to skip redundant sorting operations, such as when + * the newly requested sort command is the same as the active one. The caveat + * to this optimization is that "live" edits to the value of a target's sorting + * attribute will be ignored when requesting a re-sort by the same attribute. + * + * By setting to `behavior.liveSort` to `true`, the mixer will always re-sort + * regardless of whether or not the sorting attribute and order have changed. + * + * @example Example: Enabling `liveSort` to allow for re-sorting + * + * var mixer = mixitup(containerEl, { + * behavior: { + * liveSort: true + * }, + * load: { + * sort: 'edited:desc' + * } + * }); + * + * var target = containerEl.children[3]; + * + * console.log(target.getAttribute('data-edited')); // '2015-04-24' + * + * target.setAttribute('data-edited', '2017-08-10'); // Update the target's edited date + * + * mixer.sort('edited:desc') + * .then(function(state) { + * // The target is now at the top of the list + * + * console.log(state.targets[0] === target); // true + * }); + * + * @name liveSort + * @memberof mixitup.Config.behavior + * @instance + * @type {boolean} + * @default false + */ + + this.liveSort = false; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.ConfigBehavior); + +mixitup.ConfigBehavior.prototype = Object.create(mixitup.Base.prototype); + +mixitup.ConfigBehavior.prototype.constructor = mixitup.ConfigBehavior; \ No newline at end of file diff --git a/mixitup-3.3.1/src/config-callbacks.js b/mixitup-3.3.1/src/config-callbacks.js new file mode 100644 index 0000000..c9e7956 --- /dev/null +++ b/mixitup-3.3.1/src/config-callbacks.js @@ -0,0 +1,184 @@ +/* global mixitup, h */ + +/** + * A group of optional callback functions to be invoked at various + * points within the lifecycle of a mixer operation. + * + * Each function is analogous to an event of the same name triggered from the + * container element, and is invoked immediately after it. + * + * All callback functions receive the current `state` object as their first + * argument, as well as other more specific arguments described below. + * + * @constructor + * @memberof mixitup.Config + * @name callbacks + * @namespace + * @public + * @since 2.0.0 + */ + +mixitup.ConfigCallbacks = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A callback function invoked immediately after any MixItUp operation is requested + * and before animations have begun. + * + * A second `futureState` argument is passed to the function which represents the final + * state of the mixer once the requested operation has completed. + * + * @example Example: Adding an `onMixStart` callback function + * var mixer = mixitup(containerEl, { + * callbacks: { + * onMixStart: function(state, futureState) { + * console.log('Starting operation...'); + * } + * } + * }); + * + * @name onMixStart + * @memberof mixitup.Config.callbacks + * @instance + * @type {function} + * @default null + */ + + this.onMixStart = null; + + /** + * A callback function invoked when a MixItUp operation is requested while another + * operation is in progress, and the animation queue is full, or queueing + * is disabled. + * + * @example Example: Adding an `onMixBusy` callback function + * var mixer = mixitup(containerEl, { + * callbacks: { + * onMixBusy: function(state) { + * console.log('Mixer busy'); + * } + * } + * }); + * + * @name onMixBusy + * @memberof mixitup.Config.callbacks + * @instance + * @type {function} + * @default null + */ + + this.onMixBusy = null; + + /** + * A callback function invoked after any MixItUp operation has completed, and the + * state has been updated. + * + * @example Example: Adding an `onMixEnd` callback function + * var mixer = mixitup(containerEl, { + * callbacks: { + * onMixEnd: function(state) { + * console.log('Operation complete'); + * } + * } + * }); + * + * @name onMixEnd + * @memberof mixitup.Config.callbacks + * @instance + * @type {function} + * @default null + */ + + this.onMixEnd = null; + + /** + * A callback function invoked whenever an operation "fails", i.e. no targets + * could be found matching the requested filter. + * + * @example Example: Adding an `onMixFail` callback function + * var mixer = mixitup(containerEl, { + * callbacks: { + * onMixFail: function(state) { + * console.log('No items could be found matching the requested filter'); + * } + * } + * }); + * + * @name onMixFail + * @memberof mixitup.Config.callbacks + * @instance + * @type {function} + * @default null + */ + + this.onMixFail = null; + + /** + * A callback function invoked whenever a MixItUp control is clicked, and before its + * respective operation is requested. + * + * The clicked element is assigned to the `this` keyword within the function. The original + * click event is passed to the function as the second argument, which can be useful if + * using `` tags as controls where the default behavior needs to be prevented. + * + * Returning `false` from the callback will prevent the control click from triggering + * an operation. + * + * @example Example 1: Adding an `onMixClick` callback function + * var mixer = mixitup(containerEl, { + * callbacks: { + * onMixClick: function(state, originalEvent) { + * console.log('The control "' + this.innerText + '" was clicked'); + * } + * } + * }); + * + * @example Example 2: Using `onMixClick` to manipulate the original click event + * var mixer = mixitup(containerEl, { + * callbacks: { + * onMixClick: function(state, originalEvent) { + * // Prevent original click event from bubbling up: + * originalEvent.stopPropagation(); + * + * // Prevent default behavior of clicked element: + * originalEvent.preventDefault(); + * } + * } + * }); + * + * @example Example 3: Using `onMixClick` to conditionally cancel operations + * var mixer = mixitup(containerEl, { + * callbacks: { + * onMixClick: function(state, originalEvent) { + * // Perform some conditional check: + * + * if (myApp.isLoading) { + * // By returning false, we can prevent the control click from triggering an operation. + * + * return false; + * } + * } + * } + * }); + * + * @name onMixClick + * @memberof mixitup.Config.callbacks + * @instance + * @type {function} + * @default null + */ + + this.onMixClick = null; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.ConfigCallbacks); + +mixitup.ConfigCallbacks.prototype = Object.create(mixitup.Base.prototype); + +mixitup.ConfigCallbacks.prototype.constructor = mixitup.ConfigCallbacks; \ No newline at end of file diff --git a/mixitup-3.3.1/src/config-class-names.js b/mixitup-3.3.1/src/config-class-names.js new file mode 100644 index 0000000..095e586 --- /dev/null +++ b/mixitup-3.3.1/src/config-class-names.js @@ -0,0 +1,300 @@ +/* global mixitup, h */ + +/** + * A group of properties defining the output and structure of class names programmatically + * added to controls and containers to reflect the state of the mixer. + * + * Most commonly, class names are added to controls by MixItUp to indicate that + * the control is active so that it can be styled accordingly - `'mixitup-control-active'` by default. + * + * Using a "BEM" like structure, each classname is broken into the three parts: + * a block namespace (`'mixitup'`), an element name (e.g. `'control'`), and an optional modifier + * name (e.g. `'active'`) reflecting the state of the element. + * + * By default, each part of the classname is concatenated together using single hyphens as + * delineators, but this can be easily customised to match the naming convention and style of + * your project. + * + * @constructor + * @memberof mixitup.Config + * @name classNames + * @namespace + * @public + * @since 3.0.0 + */ + +mixitup.ConfigClassNames = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * The "block" portion, or top-level namespace added to the start of any class names created by MixItUp. + * + * @example Example 1: changing the `config.classNames.block` value + * var mixer = mixitup(containerEl, { + * classNames: { + * block: 'portfolio' + * } + * }); + * + * // Active control output: "portfolio-control-active" + * + * @example Example 2: Removing `config.classNames.block` + * var mixer = mixitup(containerEl, { + * classNames: { + * block: '' + * } + * }); + * + * // Active control output: "control-active" + * + * @name block + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'mixitup' + */ + + this.block = 'mixitup'; + + /** + * The "element" portion of the class name added to container. + * + * @name elementContainer + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'container' + */ + + this.elementContainer = 'container'; + + /** + * The "element" portion of the class name added to filter controls. + * + * By default, all filter, sort, multimix and toggle controls take the same element value of `'control'`, but + * each type's element value can be individually overwritten to match the unique classNames of your controls as needed. + * + * @example Example 1: changing the `config.classNames.elementFilter` value + * var mixer = mixitup(containerEl, { + * classNames: { + * elementFilter: 'filter' + * } + * }); + * + * // Active filter output: "mixitup-filter-active" + * + * @example Example 2: changing the `config.classNames.block` and `config.classNames.elementFilter` values + * var mixer = mixitup(containerEl, { + * classNames: { + * block: 'portfolio', + * elementFilter: 'filter' + * } + * }); + * + * // Active filter output: "portfolio-filter-active" + * + * @name elementFilter + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'control' + */ + + this.elementFilter = 'control'; + + /** + * The "element" portion of the class name added to sort controls. + * + * By default, all filter, sort, multimix and toggle controls take the same element value of `'control'`, but + * each type's element value can be individually overwritten to match the unique classNames of your controls as needed. + * + * @example Example 1: changing the `config.classNames.elementSort` value + * var mixer = mixitup(containerEl, { + * classNames: { + * elementSort: 'sort' + * } + * }); + * + * // Active sort output: "mixitup-sort-active" + * + * @example Example 2: changing the `config.classNames.block` and `config.classNames.elementSort` values + * var mixer = mixitup(containerEl, { + * classNames: { + * block: 'portfolio', + * elementSort: 'sort' + * } + * }); + * + * // Active sort output: "portfolio-sort-active" + * + * @name elementSort + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'control' + */ + + this.elementSort = 'control'; + + /** + * The "element" portion of the class name added to multimix controls. + * + * By default, all filter, sort, multimix and toggle controls take the same element value of `'control'`, but + * each type's element value can be individually overwritten to match the unique classNames of your controls as needed. + * + * @example Example 1: changing the `config.classNames.elementMultimix` value + * var mixer = mixitup(containerEl, { + * classNames: { + * elementMultimix: 'multimix' + * } + * }); + * + * // Active multimix output: "mixitup-multimix-active" + * + * @example Example 2: changing the `config.classNames.block` and `config.classNames.elementMultimix` values + * var mixer = mixitup(containerEl, { + * classNames: { + * block: 'portfolio', + * elementSort: 'multimix' + * } + * }); + * + * // Active multimix output: "portfolio-multimix-active" + * + * @name elementMultimix + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'control' + */ + + this.elementMultimix = 'control'; + + /** + * The "element" portion of the class name added to toggle controls. + * + * By default, all filter, sort, multimix and toggle controls take the same element value of `'control'`, but + * each type's element value can be individually overwritten to match the unique classNames of your controls as needed. + * + * @example Example 1: changing the `config.classNames.elementToggle` value + * var mixer = mixitup(containerEl, { + * classNames: { + * elementToggle: 'toggle' + * } + * }); + * + * // Active toggle output: "mixitup-toggle-active" + * + * @example Example 2: changing the `config.classNames.block` and `config.classNames.elementToggle` values + * var mixer = mixitup(containerEl, { + * classNames: { + * block: 'portfolio', + * elementToggle: 'toggle' + * } + * }); + * + * // Active toggle output: "portfolio-toggle-active" + * + * @name elementToggle + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'control' + */ + + this.elementToggle = 'control'; + + /** + * The "modifier" portion of the class name added to active controls. + * @name modifierActive + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'active' + */ + + this.modifierActive = 'active'; + + /** + * The "modifier" portion of the class name added to disabled controls. + * + * @name modifierDisabled + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'disabled' + */ + + this.modifierDisabled = 'disabled'; + + /** + * The "modifier" portion of the class name added to the container when in a "failed" state. + * + * @name modifierFailed + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'failed' + */ + + this.modifierFailed = 'failed'; + + /** + * The delineator used between the "block" and "element" portions of any class name added by MixItUp. + * + * If the block portion is ommited by setting it to an empty string, no delineator will be added. + * + * @example Example: changing the delineator to match BEM convention + * var mixer = mixitup(containerEl, { + * classNames: { + * delineatorElement: '__' + * } + * }); + * + * // example active control output: "mixitup__control-active" + * + * @name delineatorElement + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default '-' + */ + + this.delineatorElement = '-'; + + /** + * The delineator used between the "element" and "modifier" portions of any class name added by MixItUp. + * + * If the element portion is ommited by setting it to an empty string, no delineator will be added. + * + * @example Example: changing both delineators to match BEM convention + * var mixer = mixitup(containerEl, { + * classNames: { + * delineatorElement: '__' + * delineatorModifier: '--' + * } + * }); + * + * // Active control output: "mixitup__control--active" + * + * @name delineatorModifier + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default '-' + */ + + this.delineatorModifier = '-'; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.ConfigClassNames); + +mixitup.ConfigClassNames.prototype = Object.create(mixitup.Base.prototype); + +mixitup.ConfigClassNames.prototype.constructor = mixitup.ConfigClassNames; \ No newline at end of file diff --git a/mixitup-3.3.1/src/config-controls.js b/mixitup-3.3.1/src/config-controls.js new file mode 100644 index 0000000..a2fe3df --- /dev/null +++ b/mixitup-3.3.1/src/config-controls.js @@ -0,0 +1,220 @@ +/* global mixitup, h */ + +/** + * A group of properties relating to clickable control elements. + * + * @constructor + * @memberof mixitup.Config + * @name controls + * @namespace + * @public + * @since 2.0.0 + */ + +mixitup.ConfigControls = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A boolean dictating whether or not controls should be enabled for the mixer instance. + * + * If `true` (default behavior), MixItUp will search the DOM for any clickable elements with + * `data-filter`, `data-sort` or `data-toggle` attributes, and bind them for click events. + * + * If `false`, no click handlers will be bound, and all functionality must therefore be performed + * via the mixer's API methods. + * + * If you do not intend to use the default controls, setting this property to `false` will + * marginally improve the startup time of your mixer instance, and will also prevent any other active + * mixer instances in the DOM which are bound to controls from controlling the instance. + * + * @example Example: Disabling controls + * var mixer = mixitup(containerEl, { + * controls: { + * enable: false + * } + * }); + * + * // With the default controls disabled, we can only control + * // the mixer via its API methods, e.g.: + * + * mixer.filter('.cat-1'); + * + * @name enable + * @memberof mixitup.Config.controls + * @instance + * @type {boolean} + * @default true + */ + + this.enable = true; + + /** + * A boolean dictating whether or not to use event delegation when binding click events + * to the default controls. + * + * If `false` (default behavior), each control button in the DOM will be found and + * individually bound when a mixer is instantiated, with their corresponding actions + * cached for performance. + * + * If `true`, a single click handler will be applied to the `window` (or container element - see + * `config.controls.scope`), and any click events triggered by elements with `data-filter`, + * `data-sort` or `data-toggle` attributes present will be handled as they propagate upwards. + * + * If you require a user interface where control buttons may be added, removed, or changed during the + * lifetime of a mixer, `controls.live` should be set to `true`. There is a marginal but unavoidable + * performance deficit when using live controls, as the value of each control button must be read + * from the DOM in real time once the click event has propagated. + * + * @example Example: Setting live controls + * var mixer = mixitup(containerEl, { + * controls: { + * live: true + * } + * }); + * + * // Control buttons can now be added, remove and changed without breaking + * // the mixer's UI + * + * @name live + * @memberof mixitup.Config.controls + * @instance + * @type {boolean} + * @default true + */ + + this.live = false; + + /** + * A string dictating the "scope" to use when binding or querying the default controls. The available + * values are `'global'` or `'local'`. + * + * When set to `'global'` (default behavior), MixItUp will query the entire document for control buttons + * to bind, or delegate click events from (see `config.controls.live`). + * + * When set to `'local'`, MixItUp will only query (or bind click events to) its own container element. + * This may be desireable if you require multiple active mixer instances within the same document, with + * controls that would otherwise intefere with each other if scoped globally. + * + * Conversely, if you wish to control multiple instances with a single UI, you would create one + * set of controls and keep the controls scope of each mixer set to `global`. + * + * @example Example: Setting 'local' scoped controls + * var mixerOne = mixitup(containerOne, { + * controls: { + * scope: 'local' + * } + * }); + * + * var mixerTwo = mixitup(containerTwo, { + * controls: { + * scope: 'local' + * } + * }); + * + * // Both mixers can now exist within the same document with + * // isolated controls placed within their container elements. + * + * @name scope + * @memberof mixitup.Config.controls + * @instance + * @type {string} + * @default 'global' + */ + + this.scope = 'global'; // enum: ['local' ,'global'] + + /** + * A string dictating the type of logic to apply when concatenating the filter selectors of + * active toggle buttons (i.e. any clickable element with a `data-toggle` attribute). + * + * If set to `'or'` (default behavior), selectors will be concatenated together as + * a comma-seperated list. For example: + * + * `'.cat-1, .cat-2'` (shows any elements matching `'.cat-1'` OR `'.cat-2'`) + * + * If set to `'and'`, selectors will be directly concatenated together. For example: + * + * `'.cat-1.cat-2'` (shows any elements which match both `'.cat-1'` AND `'.cat-2'`) + * + * @example Example: Setting "and" toggle logic + * var mixer = mixitup(containerEl, { + * controls: { + * toggleLogic: 'and' + * } + * }); + * + * @name toggleLogic + * @memberof mixitup.Config.controls + * @instance + * @type {string} + * @default 'or' + */ + + this.toggleLogic = 'or'; // enum: ['or', 'and'] + + /** + * A string dictating the filter behavior when all toggles are inactive. + * + * When set to `'all'` (default behavior), *all* targets will be shown by default + * when no toggles are active, or at the moment all active toggles are toggled off. + * + * When set to `'none'`, no targets will be shown by default when no toggles are + * active, or at the moment all active toggles are toggled off. + * + * @example Example 1: Setting the default toggle behavior to `'all'` + * var mixer = mixitup(containerEl, { + * controls: { + * toggleDefault: 'all' + * } + * }); + * + * mixer.toggleOn('.cat-2') + * .then(function() { + * // Deactivate all active toggles + * + * return mixer.toggleOff('.cat-2') + * }) + * .then(function(state) { + * console.log(state.activeFilter.selector); // 'all' + * console.log(state.totalShow); // 12 + * }); + * + * @example Example 2: Setting the default toggle behavior to `'none'` + * var mixer = mixitup(containerEl, { + * controls: { + * toggleDefault: 'none' + * } + * }); + * + * mixer.toggleOn('.cat-2') + * .then(function() { + * // Deactivate all active toggles + * + * return mixer.toggleOff('.cat-2') + * }) + * .then(function(state) { + * console.log(state.activeFilter.selector); // 'none' + * console.log(state.totalShow); // 0 + * }); + * + * @name toggleDefault + * @memberof mixitup.Config.controls + * @instance + * @type {string} + * @default 'all' + */ + + this.toggleDefault = 'all'; // enum: ['all', 'none'] + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.ConfigControls); + +mixitup.ConfigControls.prototype = Object.create(mixitup.Base.prototype); + +mixitup.ConfigControls.prototype.constructor = mixitup.ConfigControls; \ No newline at end of file diff --git a/mixitup-3.3.1/src/config-data.js b/mixitup-3.3.1/src/config-data.js new file mode 100644 index 0000000..d2311e9 --- /dev/null +++ b/mixitup-3.3.1/src/config-data.js @@ -0,0 +1,118 @@ +/* global mixitup, h */ + +/** + * A group of properties relating to MixItUp's dataset API. + * + * @constructor + * @memberof mixitup.Config + * @name data + * @namespace + * @public + * @since 3.0.0 + */ + +mixitup.ConfigData = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A string specifying the name of the key containing your data model's unique + * identifier (UID). To use the dataset API, a UID key must be specified and + * be present and unique on all objects in the dataset you provide to MixItUp. + * + * For example, if your dataset is made up of MongoDB documents, the UID + * key would be `'id'` or `'_id'`. + * + * @example Example: Setting the UID to `'id'` + * var mixer = mixitup(containerEl, { + * data: { + * uidKey: 'id' + * } + * }); + * + * @name uidKey + * @memberof mixitup.Config.data + * @instance + * @type {string} + * @default '' + */ + + this.uidKey = ''; + + /** + * A boolean dictating whether or not MixItUp should "dirty check" each object in + * your dataset for changes whenever `.dataset()` is called, and re-render any targets + * for which a change is found. + * + * Depending on the complexity of your data model, dirty checking can be expensive + * and is therefore disabled by default. + * + * NB: For changes to be detected, a new immutable instance of the edited model must be + * provided to mixitup, rather than manipulating properties on the existing instance. + * If your changes are a result of a DB write and read, you will most likely be calling + * `.dataset()` with a clean set of objects each time, so this will not be an issue. + * + * @example Example: Enabling dirty checking + * + * var myDataset = [ + * { + * id: 0, + * title: "Blog Post Title 0" + * ... + * }, + * { + * id: 1, + * title: "Blog Post Title 1" + * ... + * } + * ]; + * + * // Instantiate a mixer with a pre-loaded dataset, and a target renderer + * // function defined + * + * var mixer = mixitup(containerEl, { + * data: { + * uidKey: 'id', + * dirtyCheck: true + * }, + * load: { + * dataset: myDataset + * }, + * render: { + * target: function() { ... } + * } + * }); + * + * // For illustration, we will clone and edit the second object in the dataset. + * // NB: this would typically be done server-side in response to a DB update, + * and then re-queried via an API. + * + * myDataset[1] = Object.assign({}, myDataset[1]); + * + * myDataset[1].title = 'Blog Post Title 11'; + * + * mixer.dataset(myDataset) + * .then(function() { + * // the target with ID "1", will be re-rendered reflecting its new title + * }); + * + * @name dirtyCheck + * @memberof mixitup.Config.data + * @instance + * @type {boolean} + * @default false + */ + + this.dirtyCheck = false; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.ConfigData); + +mixitup.ConfigData.prototype = Object.create(mixitup.Base.prototype); + +mixitup.ConfigData.prototype.constructor = mixitup.ConfigData; \ No newline at end of file diff --git a/mixitup-3.3.1/src/config-debug.js b/mixitup-3.3.1/src/config-debug.js new file mode 100644 index 0000000..f422641 --- /dev/null +++ b/mixitup-3.3.1/src/config-debug.js @@ -0,0 +1,106 @@ +/* global mixitup, h */ + +/** + * A group of properties allowing the toggling of various debug features. + * + * @constructor + * @memberof mixitup.Config + * @name debug + * @namespace + * @public + * @since 3.0.0 + */ + +mixitup.ConfigDebug = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A boolean dictating whether or not the mixer instance returned by the + * `mixitup()` factory function should expose private properties and methods. + * + * By default, mixer instances only expose their public API, but enabling + * debug mode will give you access to various mixer internals which may aid + * in debugging, or the authoring of extensions. + * + * @example Example: Enabling debug mode + * + * var mixer = mixitup(containerEl, { + * debug: { + * enable: true + * } + * }); + * + * // Private properties and methods will now be visible on the mixer instance: + * + * console.log(mixer); + * + * @name enable + * @memberof mixitup.Config.debug + * @instance + * @type {boolean} + * @default false + */ + + this.enable = false; + + /** + * A boolean dictating whether or not warnings should be shown when various + * common gotchas occur. + * + * Warnings are intended to provide insights during development when something + * occurs that is not a fatal, but may indicate an issue with your integration, + * and are therefore turned on by default. However, you may wish to disable + * them in production. + * + * @example Example 1: Disabling warnings + * + * var mixer = mixitup(containerEl, { + * debug: { + * showWarnings: false + * } + * }); + * + * @example Example 2: Disabling warnings based on environment + * + * var showWarnings = myAppConfig.environment === 'development' ? true : false; + * + * var mixer = mixitup(containerEl, { + * debug: { + * showWarnings: showWarnings + * } + * }); + * + * @name showWarnings + * @memberof mixitup.Config.debug + * @instance + * @type {boolean} + * @default true + */ + + this.showWarnings = true; + + /** + * Used for server-side testing only. + * + * @private + * @name fauxAsync + * @memberof mixitup.Config.debug + * @instance + * @type {boolean} + * @default false + */ + + this.fauxAsync = false; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.ConfigDebug); + +mixitup.ConfigDebug.prototype = Object.create(mixitup.Base.prototype); + +mixitup.ConfigDebug.prototype.constructor = mixitup.ConfigDebug; \ No newline at end of file diff --git a/mixitup-3.3.1/src/config-layout.js b/mixitup-3.3.1/src/config-layout.js new file mode 100644 index 0000000..8ed53e5 --- /dev/null +++ b/mixitup-3.3.1/src/config-layout.js @@ -0,0 +1,144 @@ +/* global mixitup, h */ + +/** + * A group of properties relating to the layout of the container. + * + * @constructor + * @memberof mixitup.Config + * @name layout + * @namespace + * @public + * @since 3.0.0 + */ + +mixitup.ConfigLayout = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A boolean dictating whether or not mixitup should query all descendants + * of the container for targets, or only immediate children. + * + * By default, mixitup will query all descendants matching the + * `selectors.target` selector when indexing targets upon instantiation. + * This allows for targets to be nested inside a sub-container which is + * useful when ring-fencing targets from locally scoped controls in your + * markup (see `controls.scope`). + * + * However, if you are building a more complex UI requiring the nesting + * of mixers within mixers, you will most likely want to limit targets to + * immediate children of the container by setting this property to `false`. + * + * @example Example: Restricting targets to immediate children + * + * var mixer = mixitup(containerEl, { + * layout: { + * allowNestedTargets: false + * } + * }); + * + * @name allowNestedTargets + * @memberof mixitup.Config.layout + * @instance + * @type {boolean} + * @default true + */ + + this.allowNestedTargets = true; + + /** + * A string specifying an optional class name to apply to the container when in + * its default state. + * + * By changing this class name or adding a class name to the container via the + * `.changeLayout()` API method, the CSS layout of the container can be changed, + * and MixItUp will attemp to gracefully animate the container and its targets + * between states. + * + * @example Example 1: Specifying a container class name + * + * var mixer = mixitup(containerEl, { + * layout: { + * containerClassName: 'grid' + * } + * }); + * + * @example Example 2: Changing the default class name with `.changeLayout()` + * + * var mixer = mixitup(containerEl, { + * layout: { + * containerClassName: 'grid' + * } + * }); + * + * mixer.changeLayout('list') + * .then(function(state) { + * console.log(state.activeContainerClass); // "list" + * }); + * + * @name containerClassName + * @memberof mixitup.Config.layout + * @instance + * @type {string} + * @default '' + */ + + this.containerClassName = ''; + + /** + * A reference to a non-target sibling element after which to insert targets + * when there are no targets in the container. + * + * @example Example: Setting a `siblingBefore` reference element + * + * var addButton = containerEl.querySelector('button'); + * + * var mixer = mixitup(containerEl, { + * layout: { + * siblingBefore: addButton + * } + * }); + * + * @name siblingBefore + * @memberof mixitup.Config.layout + * @instance + * @type {HTMLElement} + * @default null + */ + + this.siblingBefore = null; + + /** + * A reference to a non-target sibling element before which to insert targets + * when there are no targets in the container. + * + * @example Example: Setting an `siblingAfter` reference element + * + * var gap = containerEl.querySelector('.gap'); + * + * var mixer = mixitup(containerEl, { + * layout: { + * siblingAfter: gap + * } + * }); + * + * @name siblingAfter + * @memberof mixitup.Config.layout + * @instance + * @type {HTMLElement} + * @default null + */ + + this.siblingAfter = null; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.ConfigLayout); + +mixitup.ConfigLayout.prototype = Object.create(mixitup.Base.prototype); + +mixitup.ConfigLayout.prototype.constructor = mixitup.ConfigLayout; \ No newline at end of file diff --git a/mixitup-3.3.1/src/config-load.js b/mixitup-3.3.1/src/config-load.js new file mode 100644 index 0000000..4441b58 --- /dev/null +++ b/mixitup-3.3.1/src/config-load.js @@ -0,0 +1,127 @@ +/* global mixitup, h */ + +/** + * A group of properties defining the initial state of the mixer on load (instantiation). + * + * @constructor + * @memberof mixitup.Config + * @name load + * @namespace + * @public + * @since 2.0.0 + */ + +mixitup.ConfigLoad = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A string defining any filtering to be statically applied to the mixer on load. + * As per the `.filter()` API, this can be any valid selector string, or the + * values `'all'` or `'none'`. + * + * @example Example 1: Defining an initial filter selector to be applied on load + * + * // The mixer will show only those targets matching '.category-a' on load. + * + * var mixer = mixitup(containerEl, { + * load: { + * filter: '.category-a' + * } + * }); + * + * @example Example 2: Hiding all targets on load + * + * // The mixer will show hide all targets on load. + * + * var mixer = mixitup(containerEl, { + * load: { + * filter: 'none' + * } + * }); + * + * @name filter + * @memberof mixitup.Config.load + * @instance + * @type {string} + * @default 'all' + */ + + this.filter = 'all'; + + /** + * A string defining any sorting to be statically applied to the mixer on load. + * As per the `.sort()` API, this should be a valid "sort string" made up of + * an attribute to sort by (or `'default'`) followed by an optional sorting + * order, or the value `'random'`; + * + * @example Example: Defining sorting to be applied on load + * + * // The mixer will sort the container by the value of the `data-published-date` + * // attribute, in descending order. + * + * var mixer = mixitup(containerEl, { + * load: { + * sort: 'published-date:desc' + * } + * }); + * + * @name sort + * @memberof mixitup.Config.load + * @instance + * @type {string} + * @default 'default:asc' + */ + + this.sort = 'default:asc'; + + /** + * An array of objects representing the underlying data of any pre-rendered targets, + * when using the `.dataset()` API. + * + * NB: If targets are pre-rendered when the mixer is instantiated, this must be set. + * + * @example Example: Defining the initial underyling dataset + * + * var myDataset = [ + * { + * id: 0, + * title: "Blog Post Title 0", + * ... + * }, + * { + * id: 1, + * title: "Blog Post Title 1", + * ... + * } + * ]; + * + * var mixer = mixitup(containerEl, { + * data: { + * uidKey: 'id' + * }, + * load: { + * dataset: myDataset + * } + * }); + * + * @name dataset + * @memberof mixitup.Config.load + * @instance + * @type {Array.} + * @default null + */ + + this.dataset = null; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.ConfigLoad); + +mixitup.ConfigLoad.prototype = Object.create(mixitup.Base.prototype); + +mixitup.ConfigLoad.prototype.constructor = mixitup.ConfigLoad; \ No newline at end of file diff --git a/mixitup-3.3.1/src/config-render.js b/mixitup-3.3.1/src/config-render.js new file mode 100644 index 0000000..4719e7f --- /dev/null +++ b/mixitup-3.3.1/src/config-render.js @@ -0,0 +1,100 @@ +/* global mixitup, h */ + +/** + * A group of optional render functions for creating and updating elements. + * + * All render functions receive a data object, and should return a valid HTML string. + * + * @constructor + * @memberof mixitup.Config + * @name render + * @namespace + * @public + * @since 3.0.0 + */ + +mixitup.ConfigRender = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A function returning an HTML string representing a target element, or a reference to a + * single DOM element. + * + * The function is invoked as part of the `.dataset()` API, whenever a new item is added + * to the dataset, or an item in the dataset changes (if `dataset.dirtyCheck` is enabled). + * + * The function receives the relevant dataset item as its first parameter. + * + * @example Example 1: Using string concatenation + * + * var mixer = mixitup(containerEl, { + * render: { + * target: function(item) { + * return ( + * '<div class="mix">' + + * '<h2>' + item.title + '</h2>' + + * '</div>' + * ); + * } + * } + * }); + * + * @example Example 2: Using an ES2015 template literal + * + * var mixer = mixitup(containerEl, { + * render: { + * target: function(item) { + * return ( + * `<div class="mix"> + * <h2>${item.title}</h2> + * </div>` + * ); + * } + * } + * }); + * + * @example Example 3: Using a Handlebars template + * {{{{raw}}}} + * var targetTemplate = Handlebars.compile('<div class="mix"><h2>{{title}}</h2></div>'); + * {{{{/raw}}}} + * var mixer = mixitup(containerEl, { + * render: { + * target: targetTemplate + * } + * }); + * + * @example Example 4: Returning a DOM element + * + * var mixer = mixitup(containerEl, { + * render: { + * target: function(item) { + * // Create a single element using your framework's built-in renderer + * + * var el = ... + * + * return el; + * } + * } + * }); + * + * @name target + * @memberof mixitup.Config.render + * @instance + * @type {function} + * @default 'null' + */ + + this.target = null; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.ConfigRender); + +mixitup.ConfigRender.prototype = Object.create(mixitup.Base.prototype); + +mixitup.ConfigRender.prototype.constructor = mixitup.ConfigRender; \ No newline at end of file diff --git a/mixitup-3.3.1/src/config-selectors.js b/mixitup-3.3.1/src/config-selectors.js new file mode 100644 index 0000000..69e1256 --- /dev/null +++ b/mixitup-3.3.1/src/config-selectors.js @@ -0,0 +1,93 @@ +/* global mixitup, h */ + +/** + * A group of properties defining the selectors used to query elements within a mixitup container. + * + * @constructor + * @memberof mixitup.Config + * @name selectors + * @namespace + * @public + * @since 3.0.0 + */ + +mixitup.ConfigSelectors = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A selector string used to query and index target elements within the container. + * + * By default, the class selector `'.mix'` is used, but this can be changed to an + * attribute or element selector to match the style of your project. + * + * @example Example 1: Changing the target selector + * + * var mixer = mixitup(containerEl, { + * selectors: { + * target: '.portfolio-item' + * } + * }); + * + * @example Example 2: Using an attribute selector as a target selector + * + * // The mixer will search for any children with the attribute `data-ref="mix"` + * + * var mixer = mixitup(containerEl, { + * selectors: { + * target: '[data-ref="mix"]' + * } + * }); + * + * @name target + * @memberof mixitup.Config.selectors + * @instance + * @type {string} + * @default '.mix' + */ + + this.target = '.mix'; + + /** + * A optional selector string used to add further specificity to the querying of control elements, + * in addition to their mandatory data attribute (e.g. `data-filter`, `data-toggle`, `data-sort`). + * + * This can be used if other elements in your document must contain the above attributes + * (e.g. for use in third-party scripts), and would otherwise interfere with MixItUp. Adding + * an additional `control` selector of your choice allows MixItUp to restrict event handling + * to only those elements matching the defined selector. + * + * @name control + * @memberof mixitup.Config.selectors + * @instance + * @type {string} + * @default '' + * + * @example Example 1: Adding a `selectors.control` selector + * + * var mixer = mixitup(containerEl, { + * selectors: { + * control: '.mixitup-control' + * } + * }); + * + * // Will not be handled: + * // + * + * // Will be handled: + * // + */ + + this.control = ''; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.ConfigSelectors); + +mixitup.ConfigSelectors.prototype = Object.create(mixitup.Base.prototype); + +mixitup.ConfigSelectors.prototype.constructor = mixitup.ConfigSelectors; \ No newline at end of file diff --git a/mixitup-3.3.1/src/config-templates.js b/mixitup-3.3.1/src/config-templates.js new file mode 100644 index 0000000..0786acc --- /dev/null +++ b/mixitup-3.3.1/src/config-templates.js @@ -0,0 +1,24 @@ +/* global mixitup, h */ + +/** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.ConfigTemplates = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.ConfigTemplates); + +mixitup.ConfigTemplates.prototype = Object.create(mixitup.Base.prototype); + +mixitup.ConfigTemplates.prototype.constructor = mixitup.ConfigTemplates; \ No newline at end of file diff --git a/mixitup-3.3.1/src/config.js b/mixitup-3.3.1/src/config.js new file mode 100644 index 0000000..f5c8f39 --- /dev/null +++ b/mixitup-3.3.1/src/config.js @@ -0,0 +1,77 @@ +/* global mixitup, h */ + +/** + * `mixitup.Config` is an interface used for customising the functionality of a + * mixer instance. It is organised into several semantically distinct sub-objects, + * each one pertaining to a particular aspect of MixItUp functionality. + * + * An object literal containing any or all of the available properies, + * known as the "configuration object", can be passed as the second parameter to + * the `mixitup` factory function when creating a mixer instance to customise its + * functionality as needed. + * + * If no configuration object is passed, the mixer instance will take on the default + * configuration values detailed below. + * + * @example Example 1: Creating and passing the configuration object + * // Create a configuration object with desired values + * + * var config = { + * animation: { + * enable: false + * }, + * selectors: { + * target: '.item' + * } + * }; + * + * // Pass the configuration object to the mixitup factory function + * + * var mixer = mixitup(containerEl, config); + * + * @example Example 2: Passing the configuration object inline + * // Typically, the configuration object is passed inline for brevity. + * + * var mixer = mixitup(containerEl, { + * controls: { + * live: true, + * toggleLogic: 'and' + * } + * }); + * + * + * @constructor + * @memberof mixitup + * @namespace + * @public + * @since 2.0.0 + */ + +mixitup.Config = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.animation = new mixitup.ConfigAnimation(); + this.behavior = new mixitup.ConfigBehavior(); + this.callbacks = new mixitup.ConfigCallbacks(); + this.controls = new mixitup.ConfigControls(); + this.classNames = new mixitup.ConfigClassNames(); + this.data = new mixitup.ConfigData(); + this.debug = new mixitup.ConfigDebug(); + this.layout = new mixitup.ConfigLayout(); + this.load = new mixitup.ConfigLoad(); + this.selectors = new mixitup.ConfigSelectors(); + this.render = new mixitup.ConfigRender(); + this.templates = new mixitup.ConfigTemplates(); + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.Config); + +mixitup.Config.prototype = Object.create(mixitup.Base.prototype); + +mixitup.Config.prototype.constructor = mixitup.Config; \ No newline at end of file diff --git a/mixitup-3.3.1/src/control-definition.js b/mixitup-3.3.1/src/control-definition.js new file mode 100644 index 0000000..e2a483b --- /dev/null +++ b/mixitup-3.3.1/src/control-definition.js @@ -0,0 +1,42 @@ +/* global mixitup, h */ + +/** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + * @param {string} type + * @param {string} selector + * @param {boolean} [live] + * @param {string} [parent] + * An optional string representing the name of the mixer.dom property containing a reference to a parent element. + */ + +mixitup.ControlDefinition = function(type, selector, live, parent) { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.type = type; + this.selector = selector; + this.live = live || false; + this.parent = parent || ''; + + this.callActions('afterConstruct'); + + h.freeze(this); + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.ControlDefinition); + +mixitup.ControlDefinition.prototype = Object.create(mixitup.Base.prototype); + +mixitup.ControlDefinition.prototype.constructor = mixitup.ControlDefinition; + +mixitup.controlDefinitions = []; + +mixitup.controlDefinitions.push(new mixitup.ControlDefinition('multimix', '[data-filter][data-sort]')); +mixitup.controlDefinitions.push(new mixitup.ControlDefinition('filter', '[data-filter]')); +mixitup.controlDefinitions.push(new mixitup.ControlDefinition('sort', '[data-sort]')); +mixitup.controlDefinitions.push(new mixitup.ControlDefinition('toggle', '[data-toggle]')); \ No newline at end of file diff --git a/mixitup-3.3.1/src/control.js b/mixitup-3.3.1/src/control.js new file mode 100644 index 0000000..19c9d35 --- /dev/null +++ b/mixitup-3.3.1/src/control.js @@ -0,0 +1,514 @@ +/* global mixitup, h */ + +/** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.Control = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.el = null; + this.selector = ''; + this.bound = []; + this.pending = -1; + this.type = ''; + this.status = 'inactive'; // enum: ['inactive', 'active', 'disabled', 'live'] + this.filter = ''; + this.sort = ''; + this.canDisable = false; + this.handler = null; + this.classNames = new mixitup.UiClassNames(); + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.Control); + +mixitup.Control.prototype = Object.create(mixitup.Base.prototype); + +h.extend(mixitup.Control.prototype, +/** @lends mixitup.Control */ +{ + constructor: mixitup.Control, + + /** + * @private + * @param {HTMLElement} el + * @param {string} type + * @param {string} selector + */ + + init: function(el, type, selector) { + var self = this; + + this.callActions('beforeInit', arguments); + + self.el = el; + self.type = type; + self.selector = selector; + + if (self.selector) { + self.status = 'live'; + } else { + self.canDisable = typeof self.el.disable === 'boolean'; + + switch (self.type) { + case 'filter': + self.filter = self.el.getAttribute('data-filter'); + + break; + case 'toggle': + self.filter = self.el.getAttribute('data-toggle'); + + break; + case 'sort': + self.sort = self.el.getAttribute('data-sort'); + + break; + case 'multimix': + self.filter = self.el.getAttribute('data-filter'); + self.sort = self.el.getAttribute('data-sort'); + + break; + } + } + + self.bindClick(); + + mixitup.controls.push(self); + + this.callActions('afterInit', arguments); + }, + + /** + * @private + * @param {mixitup.Mixer} mixer + * @return {boolean} + */ + + isBound: function(mixer) { + var self = this, + isBound = false; + + this.callActions('beforeIsBound', arguments); + + isBound = self.bound.indexOf(mixer) > -1; + + return self.callFilters('afterIsBound', isBound, arguments); + }, + + /** + * @private + * @param {mixitup.Mixer} mixer + * @return {void} + */ + + addBinding: function(mixer) { + var self = this; + + this.callActions('beforeAddBinding', arguments); + + if (!self.isBound()) { + self.bound.push(mixer); + } + + this.callActions('afterAddBinding', arguments); + }, + + /** + * @private + * @param {mixitup.Mixer} mixer + * @return {void} + */ + + removeBinding: function(mixer) { + var self = this, + removeIndex = -1; + + this.callActions('beforeRemoveBinding', arguments); + + if ((removeIndex = self.bound.indexOf(mixer)) > -1) { + self.bound.splice(removeIndex, 1); + } + + if (self.bound.length < 1) { + // No bindings exist, unbind event click handlers + + self.unbindClick(); + + // Remove from `mixitup.controls` list + + removeIndex = mixitup.controls.indexOf(self); + + mixitup.controls.splice(removeIndex, 1); + + if (self.status === 'active') { + self.renderStatus(self.el, 'inactive'); + } + } + + this.callActions('afterRemoveBinding', arguments); + }, + + /** + * @private + * @return {void} + */ + + bindClick: function() { + var self = this; + + this.callActions('beforeBindClick', arguments); + + self.handler = function(e) { + self.handleClick(e); + }; + + h.on(self.el, 'click', self.handler); + + this.callActions('afterBindClick', arguments); + }, + + /** + * @private + * @return {void} + */ + + unbindClick: function() { + var self = this; + + this.callActions('beforeUnbindClick', arguments); + + h.off(self.el, 'click', self.handler); + + self.handler = null; + + this.callActions('afterUnbindClick', arguments); + }, + + /** + * @private + * @param {MouseEvent} e + * @return {void} + */ + + handleClick: function(e) { + var self = this, + button = null, + mixer = null, + isActive = false, + returnValue = void(0), + command = {}, + clone = null, + commands = [], + i = -1; + + this.callActions('beforeHandleClick', arguments); + + this.pending = 0; + + mixer = self.bound[0]; + + if (!self.selector) { + button = self.el; + } else { + button = h.closestParent(e.target, mixer.config.selectors.control + self.selector, true, mixer.dom.document); + } + + if (!button) { + self.callActions('afterHandleClick', arguments); + + return; + } + + switch (self.type) { + case 'filter': + command.filter = self.filter || button.getAttribute('data-filter'); + + break; + case 'sort': + command.sort = self.sort || button.getAttribute('data-sort'); + + break; + case 'multimix': + command.filter = self.filter || button.getAttribute('data-filter'); + command.sort = self.sort || button.getAttribute('data-sort'); + + break; + case 'toggle': + command.filter = self.filter || button.getAttribute('data-toggle'); + + if (self.status === 'live') { + isActive = h.hasClass(button, self.classNames.active); + } else { + isActive = self.status === 'active'; + } + + break; + } + + for (i = 0; i < self.bound.length; i++) { + // Create a clone of the command for each bound mixer instance + + clone = new mixitup.CommandMultimix(); + + h.extend(clone, command); + + commands.push(clone); + } + + commands = self.callFilters('commandsHandleClick', commands, arguments); + + self.pending = self.bound.length; + + for (i = 0; mixer = self.bound[i]; i++) { + command = commands[i]; + + if (!command) { + // An extension may set a command null to indicate that the click should not be handled + + continue; + } + + if (!mixer.lastClicked) { + mixer.lastClicked = button; + } + + mixitup.events.fire('mixClick', mixer.dom.container, { + state: mixer.state, + instance: mixer, + originalEvent: e, + control: mixer.lastClicked + }, mixer.dom.document); + + if (typeof mixer.config.callbacks.onMixClick === 'function') { + returnValue = mixer.config.callbacks.onMixClick.call(mixer.lastClicked, mixer.state, e, mixer); + + if (returnValue === false) { + // User has returned `false` from the callback, so do not handle click + + continue; + } + } + + if (self.type === 'toggle') { + isActive ? mixer.toggleOff(command.filter) : mixer.toggleOn(command.filter); + } else { + mixer.multimix(command); + } + } + + this.callActions('afterHandleClick', arguments); + }, + + /** + * @param {object} command + * @param {Array} toggleArray + * @return {void} + */ + + update: function(command, toggleArray) { + var self = this, + actions = new mixitup.CommandMultimix(); + + self.callActions('beforeUpdate', arguments); + + self.pending--; + + self.pending = Math.max(0, self.pending); + + if (self.pending > 0) return; + + if (self.status === 'live') { + // Live control (status unknown) + + self.updateLive(command, toggleArray); + } else { + // Static control + + actions.sort = self.sort; + actions.filter = self.filter; + + self.callFilters('actionsUpdate', actions, arguments); + + self.parseStatusChange(self.el, command, actions, toggleArray); + } + + self.callActions('afterUpdate', arguments); + }, + + /** + * @param {mixitup.CommandMultimix} command + * @param {Array} toggleArray + * @return {void} + */ + + updateLive: function(command, toggleArray) { + var self = this, + controlButtons = null, + actions = null, + button = null, + i = -1; + + self.callActions('beforeUpdateLive', arguments); + + if (!self.el) return; + + controlButtons = self.el.querySelectorAll(self.selector); + + for (i = 0; button = controlButtons[i]; i++) { + actions = new mixitup.CommandMultimix(); + + switch (self.type) { + case 'filter': + actions.filter = button.getAttribute('data-filter'); + + break; + case 'sort': + actions.sort = button.getAttribute('data-sort'); + + break; + case 'multimix': + actions.filter = button.getAttribute('data-filter'); + actions.sort = button.getAttribute('data-sort'); + + break; + case 'toggle': + actions.filter = button.getAttribute('data-toggle'); + + break; + } + + actions = self.callFilters('actionsUpdateLive', actions, arguments); + + self.parseStatusChange(button, command, actions, toggleArray); + } + + self.callActions('afterUpdateLive', arguments); + }, + + /** + * @param {HTMLElement} button + * @param {mixitup.CommandMultimix} command + * @param {mixitup.CommandMultimix} actions + * @param {Array} toggleArray + * @return {void} + */ + + parseStatusChange: function(button, command, actions, toggleArray) { + var self = this, + alias = '', + toggle = '', + i = -1; + + self.callActions('beforeParseStatusChange', arguments); + + switch (self.type) { + case 'filter': + if (command.filter === actions.filter) { + self.renderStatus(button, 'active'); + } else { + self.renderStatus(button, 'inactive'); + } + + break; + case 'multimix': + if (command.sort === actions.sort && command.filter === actions.filter) { + self.renderStatus(button, 'active'); + } else { + self.renderStatus(button, 'inactive'); + } + + break; + case 'sort': + if (command.sort.match(/:asc/g)) { + alias = command.sort.replace(/:asc/g, ''); + } + + if (command.sort === actions.sort || alias === actions.sort) { + self.renderStatus(button, 'active'); + } else { + self.renderStatus(button, 'inactive'); + } + + break; + case 'toggle': + if (toggleArray.length < 1) self.renderStatus(button, 'inactive'); + + if (command.filter === actions.filter) { + self.renderStatus(button, 'active'); + } + + for (i = 0; i < toggleArray.length; i++) { + toggle = toggleArray[i]; + + if (toggle === actions.filter) { + // Button matches one active toggle + + self.renderStatus(button, 'active'); + + break; + } + + self.renderStatus(button, 'inactive'); + } + + break; + } + + self.callActions('afterParseStatusChange', arguments); + }, + + /** + * @param {HTMLElement} button + * @param {string} status + * @return {void} + */ + + renderStatus: function(button, status) { + var self = this; + + self.callActions('beforeRenderStatus', arguments); + + switch (status) { + case 'active': + h.addClass(button, self.classNames.active); + h.removeClass(button, self.classNames.disabled); + + if (self.canDisable) self.el.disabled = false; + + break; + case 'inactive': + h.removeClass(button, self.classNames.active); + h.removeClass(button, self.classNames.disabled); + + if (self.canDisable) self.el.disabled = false; + + break; + case 'disabled': + if (self.canDisable) self.el.disabled = true; + + h.addClass(button, self.classNames.disabled); + h.removeClass(button, self.classNames.active); + + break; + } + + if (self.status !== 'live') { + // Update the control's status propery if not live + + self.status = status; + } + + self.callActions('afterRenderStatus', arguments); + } +}); + +mixitup.controls = []; \ No newline at end of file diff --git a/mixitup-3.3.1/src/events.js b/mixitup-3.3.1/src/events.js new file mode 100644 index 0000000..38796ad --- /dev/null +++ b/mixitup-3.3.1/src/events.js @@ -0,0 +1,166 @@ +/* global mixitup, h */ + +/** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.EventDetail = function() { + this.state = null; + this.futureState = null; + this.instance = null; + this.originalEvent = null; +}; + +/** + * The `mixitup.Events` class contains all custom events dispatched by MixItUp at various + * points within the lifecycle of a mixer operation. + * + * Each event is analogous to the callback function of the same name defined in + * the `callbacks` configuration object, and is triggered immediately before it. + * + * Events are always triggered from the container element on which MixItUp is instantiated + * upon. + * + * As with any event, registered event handlers receive the event object as a parameter + * which includes a `detail` property containting references to the current `state`, + * the `mixer` instance, and other event-specific properties described below. + * + * @constructor + * @namespace + * @memberof mixitup + * @public + * @since 3.0.0 + */ + +mixitup.Events = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A custom event triggered immediately after any MixItUp operation is requested + * and before animations have begun. + * + * The `mixStart` event also exposes a `futureState` property via the + * `event.detail` object, which represents the final state of the mixer once + * the requested operation has completed. + * + * @name mixStart + * @memberof mixitup.Events + * @static + * @type {CustomEvent} + */ + + this.mixStart = null; + + /** + * A custom event triggered when a MixItUp operation is requested while another + * operation is in progress, and the animation queue is full, or queueing + * is disabled. + * + * @name mixBusy + * @memberof mixitup.Events + * @static + * @type {CustomEvent} + */ + + this.mixBusy = null; + + /** + * A custom event triggered after any MixItUp operation has completed, and the + * state has been updated. + * + * @name mixEnd + * @memberof mixitup.Events + * @static + * @type {CustomEvent} + */ + + this.mixEnd = null; + + /** + * A custom event triggered whenever a filter operation "fails", i.e. no targets + * could be found matching the requested filter. + * + * @name mixFail + * @memberof mixitup.Events + * @static + * @type {CustomEvent} + */ + + this.mixFail = null; + + /** + * A custom event triggered whenever a MixItUp control is clicked, and before its + * respective operation is requested. + * + * This event also exposes an `originalEvent` property via the `event.detail` + * object, which holds a reference to the original click event. + * + * @name mixClick + * @memberof mixitup.Events + * @static + * @type {CustomEvent} + */ + + this.mixClick = null; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.Events); + +mixitup.Events.prototype = Object.create(mixitup.Base.prototype); + +mixitup.Events.prototype.constructor = mixitup.Events; + +/** + * @private + * @param {string} eventType + * @param {Element} el + * @param {object} detail + * @param {Document} [doc] + */ + +mixitup.Events.prototype.fire = function(eventType, el, detail, doc) { + var self = this, + event = null, + eventDetail = new mixitup.EventDetail(); + + self.callActions('beforeFire', arguments); + + if (typeof self[eventType] === 'undefined') { + throw new Error('Event type "' + eventType + '" not found.'); + } + + eventDetail.state = new mixitup.State(); + + h.extend(eventDetail.state, detail.state); + + if (detail.futureState) { + eventDetail.futureState = new mixitup.State(); + + h.extend(eventDetail.futureState, detail.futureState); + } + + eventDetail.instance = detail.instance; + + if (detail.originalEvent) { + eventDetail.originalEvent = detail.originalEvent; + } + + event = h.getCustomEvent(eventType, eventDetail, doc); + + self.callFilters('eventFire', event, arguments); + + el.dispatchEvent(event); +}; + +// Asign a singleton instance to `mixitup.events`: + +mixitup.events = new mixitup.Events(); \ No newline at end of file diff --git a/mixitup-3.3.1/src/facade.js b/mixitup-3.3.1/src/facade.js new file mode 100644 index 0000000..8203d0f --- /dev/null +++ b/mixitup-3.3.1/src/facade.js @@ -0,0 +1,51 @@ +/* global mixitup, h */ + +/** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + * @param {mixitup.Mixer} mixer + */ + +mixitup.Facade = function Mixer(mixer) { + mixitup.Base.call(this); + + this.callActions('beforeConstruct', arguments); + + this.configure = mixer.configure.bind(mixer); + this.show = mixer.show.bind(mixer); + this.hide = mixer.hide.bind(mixer); + this.filter = mixer.filter.bind(mixer); + this.toggleOn = mixer.toggleOn.bind(mixer); + this.toggleOff = mixer.toggleOff.bind(mixer); + this.sort = mixer.sort.bind(mixer); + this.changeLayout = mixer.changeLayout.bind(mixer); + this.multimix = mixer.multimix.bind(mixer); + this.dataset = mixer.dataset.bind(mixer); + this.tween = mixer.tween.bind(mixer); + this.insert = mixer.insert.bind(mixer); + this.insertBefore = mixer.insertBefore.bind(mixer); + this.insertAfter = mixer.insertAfter.bind(mixer); + this.prepend = mixer.prepend.bind(mixer); + this.append = mixer.append.bind(mixer); + this.remove = mixer.remove.bind(mixer); + this.destroy = mixer.destroy.bind(mixer); + this.forceRefresh = mixer.forceRefresh.bind(mixer); + this.forceRender = mixer.forceRender.bind(mixer); + this.isMixing = mixer.isMixing.bind(mixer); + this.getOperation = mixer.getOperation.bind(mixer); + this.getConfig = mixer.getConfig.bind(mixer); + this.getState = mixer.getState.bind(mixer); + + this.callActions('afterConstruct', arguments); + + h.freeze(this); + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.Facade); + +mixitup.Facade.prototype = Object.create(mixitup.Base.prototype); + +mixitup.Facade.prototype.constructor = mixitup.Facade; \ No newline at end of file diff --git a/mixitup-3.3.1/src/factory.js b/mixitup-3.3.1/src/factory.js new file mode 100644 index 0000000..30f03d6 --- /dev/null +++ b/mixitup-3.3.1/src/factory.js @@ -0,0 +1,187 @@ +/* global mixitup:true, h */ + +/** + * The `mixitup()` "factory" function creates and returns individual instances + * of MixItUp, known as "mixers", on which API methods can be called. + * + * When loading MixItUp via a script tag, the factory function is accessed + * via the global variable `mixitup`. When using a module loading + * system (e.g. ES2015, CommonJS, RequireJS), the factory function is + * exported into your module when you require the MixItUp library. + * + * @example + * mixitup(container [,config] [,foreignDoc]) + * + * @example Example 1: Creating a mixer instance with an element reference + * var containerEl = document.querySelector('.container'); + * + * var mixer = mixitup(containerEl); + * + * @example Example 2: Creating a mixer instance with a selector string + * var mixer = mixitup('.container'); + * + * @example Example 3: Passing a configuration object + * var mixer = mixitup(containerEl, { + * animation: { + * effects: 'fade scale(0.5)' + * } + * }); + * + * @example Example 4: Passing an iframe reference + * var mixer = mixitup(containerEl, config, foreignDocument); + * + * @global + * @namespace + * @public + * @kind function + * @since 3.0.0 + * @param {(Element|string)} container + * A DOM element or selector string representing the container(s) on which to instantiate MixItUp. + * @param {object} [config] + * An optional "configuration object" used to customize the behavior of the MixItUp instance. + * @param {object} [foreignDoc] + * An optional reference to a `document`, which can be used to control a MixItUp instance in an iframe. + * @return {mixitup.Mixer} + * A "mixer" object holding the MixItUp instance. + */ + +mixitup = function(container, config, foreignDoc) { + var el = null, + returnCollection = false, + instance = null, + facade = null, + doc = null, + output = null, + instances = [], + id = '', + elements = [], + i = -1; + + doc = foreignDoc || window.document; + + if (returnCollection = arguments[3]) { + // A non-documented 4th paramater enabling control of multiple instances + + returnCollection = typeof returnCollection === 'boolean'; + } + + if (typeof container === 'string') { + elements = doc.querySelectorAll(container); + } else if (container && typeof container === 'object' && h.isElement(container, doc)) { + elements = [container]; + } else if (container && typeof container === 'object' && container.length) { + // Although not documented, the container may also be an array-like list of + // elements such as a NodeList or jQuery collection, is returnCollection is true + + elements = container; + } else { + throw new Error(mixitup.messages.errorFactoryInvalidContainer()); + } + + if (elements.length < 1) { + throw new Error(mixitup.messages.errorFactoryContainerNotFound()); + } + + for (i = 0; el = elements[i]; i++) { + if (i > 0 && !returnCollection) break; + + if (!el.id) { + id = 'MixItUp' + h.randomHex(); + + el.id = id; + } else { + id = el.id; + } + + if (mixitup.instances[id] instanceof mixitup.Mixer) { + instance = mixitup.instances[id]; + + if (!config || (config && config.debug && config.debug.showWarnings !== false)) { + console.warn(mixitup.messages.warningFactoryPreexistingInstance()); + } + } else { + instance = new mixitup.Mixer(); + + instance.attach(el, doc, id, config); + + mixitup.instances[id] = instance; + } + + facade = new mixitup.Facade(instance); + + if (config && config.debug && config.debug.enable) { + instances.push(instance); + } else { + instances.push(facade); + } + } + + if (returnCollection) { + output = new mixitup.Collection(instances); + } else { + // Return the first instance regardless + + output = instances[0]; + } + + return output; +}; + +/** + * The `.use()` static method is used to extend the functionality of mixitup with compatible + * extensions and libraries in an environment with modular scoping e.g. ES2015, CommonJS, or RequireJS. + * + * You need only call the `.use()` function once per project, per extension, as module loaders + * will cache a single reference to MixItUp inclusive of all changes made. + * + * @example + * mixitup.use(extension) + * + * @example Example 1: Extending MixItUp with the Pagination Extension + * + * import mixitup from 'mixitup'; + * import mixitupPagination from 'mixitup-pagination'; + * + * mixitup.use(mixitupPagination); + * + * // All mixers created by the factory function in all modules will now + * // have pagination functionality + * + * var mixer = mixitup('.container'); + * + * @public + * @name use + * @memberof mixitup + * @kind function + * @static + * @since 3.0.0 + * @param {*} extension A reference to the extension or library to be used. + * @return {void} + */ + +mixitup.use = function(extension) { + mixitup.Base.prototype.callActions.call(mixitup, 'beforeUse', arguments); + + // Call the extension's factory function, passing + // the mixitup factory as a paramater + + if (typeof extension === 'function' && extension.TYPE === 'mixitup-extension') { + // Mixitup extension + + if (typeof mixitup.extensions[extension.NAME] === 'undefined') { + extension(mixitup); + + mixitup.extensions[extension.NAME] = extension; + } + } else if (extension.fn && extension.fn.jquery) { + // jQuery + + mixitup.libraries.$ = extension; + } + + mixitup.Base.prototype.callActions.call(mixitup, 'afterUse', arguments); +}; + +mixitup.instances = {}; +mixitup.extensions = {}; +mixitup.libraries = {}; \ No newline at end of file diff --git a/mixitup-3.3.1/src/features.js b/mixitup-3.3.1/src/features.js new file mode 100644 index 0000000..e6a4ee6 --- /dev/null +++ b/mixitup-3.3.1/src/features.js @@ -0,0 +1,156 @@ +/* global mixitup, h */ + +/** + * The `mixitup.Features` class performs all feature and CSS prefix detection + * neccessary for MixItUp to function correctly, as well as storing various + * string and array constants. All feature decection is on evaluation of the + * library and stored in a singleton instance for use by other internal classes. + * + * @constructor + * @namespace + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.Features = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.boxSizingPrefix = ''; + this.transformPrefix = ''; + this.transitionPrefix = ''; + + this.boxSizingPrefix = ''; + this.transformProp = ''; + this.transformRule = ''; + this.transitionProp = ''; + this.perspectiveProp = ''; + this.perspectiveOriginProp = ''; + + this.has = new mixitup.Has(); + + this.canary = null; + + this.BOX_SIZING_PROP = 'boxSizing'; + this.TRANSITION_PROP = 'transition'; + this.TRANSFORM_PROP = 'transform'; + this.PERSPECTIVE_PROP = 'perspective'; + this.PERSPECTIVE_ORIGIN_PROP = 'perspectiveOrigin'; + this.VENDORS = ['Webkit', 'moz', 'O', 'ms']; + + this.TWEENABLE = [ + 'opacity', + 'width', 'height', + 'marginRight', 'marginBottom', + 'x', 'y', + 'scale', + 'translateX', 'translateY', 'translateZ', + 'rotateX', 'rotateY', 'rotateZ' + ]; + + this.callActions('afterConstruct'); +}; + +mixitup.BaseStatic.call(mixitup.Features); + +mixitup.Features.prototype = Object.create(mixitup.Base.prototype); + +h.extend(mixitup.Features.prototype, +/** @lends mixitup.Features */ +{ + constructor: mixitup.Features, + + /** + * @private + * @return {void} + */ + + init: function() { + var self = this; + + self.callActions('beforeInit', arguments); + + self.canary = document.createElement('div'); + + self.setPrefixes(); + self.runTests(); + + self.callActions('beforeInit', arguments); + }, + + /** + * @private + * @return {void} + */ + + runTests: function() { + var self = this; + + self.callActions('beforeRunTests', arguments); + + self.has.promises = typeof window.Promise === 'function'; + self.has.transitions = self.transitionPrefix !== 'unsupported'; + + self.callActions('afterRunTests', arguments); + + h.freeze(self.has); + }, + + /** + * @private + * @return {void} + */ + + setPrefixes: function() { + var self = this; + + self.callActions('beforeSetPrefixes', arguments); + + self.transitionPrefix = h.getPrefix(self.canary, 'Transition', self.VENDORS); + self.transformPrefix = h.getPrefix(self.canary, 'Transform', self.VENDORS); + self.boxSizingPrefix = h.getPrefix(self.canary, 'BoxSizing', self.VENDORS); + + self.boxSizingProp = self.boxSizingPrefix ? + self.boxSizingPrefix + h.pascalCase(self.BOX_SIZING_PROP) : self.BOX_SIZING_PROP; + + self.transitionProp = self.transitionPrefix ? + self.transitionPrefix + h.pascalCase(self.TRANSITION_PROP) : self.TRANSITION_PROP; + + self.transformProp = self.transformPrefix ? + self.transformPrefix + h.pascalCase(self.TRANSFORM_PROP) : self.TRANSFORM_PROP; + + self.transformRule = self.transformPrefix ? + '-' + self.transformPrefix + '-' + self.TRANSFORM_PROP : self.TRANSFORM_PROP; + + self.perspectiveProp = self.transformPrefix ? + self.transformPrefix + h.pascalCase(self.PERSPECTIVE_PROP) : self.PERSPECTIVE_PROP; + + self.perspectiveOriginProp = self.transformPrefix ? + self.transformPrefix + h.pascalCase(self.PERSPECTIVE_ORIGIN_PROP) : + self.PERSPECTIVE_ORIGIN_PROP; + + self.callActions('afterSetPrefixes', arguments); + } +}); + +/** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.Has = function() { + this.transitions = false; + this.promises = false; + + h.seal(this); +}; + +// Assign a singleton instance to `mixitup.features` and initialise: + +mixitup.features = new mixitup.Features(); + +mixitup.features.init(); \ No newline at end of file diff --git a/mixitup-3.3.1/src/h.js b/mixitup-3.3.1/src/h.js new file mode 100644 index 0000000..6d43997 --- /dev/null +++ b/mixitup-3.3.1/src/h.js @@ -0,0 +1,1013 @@ +/* global mixitup, h:true */ + +/** + * @private + */ + +h = { + + /** + * @private + * @param {HTMLElement} el + * @param {string} cls + * @return {boolean} + */ + + hasClass: function(el, cls) { + return !!el.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')); + }, + + /** + * @private + * @param {HTMLElement} el + * @param {string} cls + * @return {void} + */ + + addClass: function(el, cls) { + if (!this.hasClass(el, cls)) el.className += el.className ? ' ' + cls : cls; + }, + + /** + * @private + * @param {HTMLElement} el + * @param {string} cls + * @return {void} + */ + + removeClass: function(el, cls) { + if (this.hasClass(el, cls)) { + var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)'); + + el.className = el.className.replace(reg, ' ').trim(); + } + }, + + /** + * Merges the properties of the source object onto the + * target object. Alters the target object. + * + * @private + * @param {object} destination + * @param {object} source + * @param {boolean} [deep=false] + * @param {boolean} [handleErrors=false] + * @return {void} + */ + + extend: function(destination, source, deep, handleErrors) { + var sourceKeys = [], + key = '', + i = -1; + + deep = deep || false; + handleErrors = handleErrors || false; + + try { + if (Array.isArray(source)) { + for (i = 0; i < source.length; i++) { + sourceKeys.push(i); + } + } else if (source) { + sourceKeys = Object.keys(source); + } + + for (i = 0; i < sourceKeys.length; i++) { + key = sourceKeys[i]; + + if (!deep || typeof source[key] !== 'object' || this.isElement(source[key])) { + // All non-object properties, or all properties if shallow extend + + destination[key] = source[key]; + } else if (Array.isArray(source[key])) { + // Arrays + + if (!destination[key]) { + destination[key] = []; + } + + this.extend(destination[key], source[key], deep, handleErrors); + } else { + // Objects + + if (!destination[key]) { + destination[key] = {}; + } + + this.extend(destination[key], source[key], deep, handleErrors); + } + } + } catch(err) { + if (handleErrors) { + this.handleExtendError(err, destination); + } else { + throw err; + } + } + + return destination; + }, + + /** + * @private + * @param {Error} err + * @param {object} destination + * @return {void} + */ + + handleExtendError: function(err, destination) { + var re = /property "?(\w*)"?[,:] object/i, + matches = null, + erroneous = '', + message = '', + suggestion = '', + probableMatch = '', + key = '', + mostMatchingChars = -1, + i = -1; + + if (err instanceof TypeError && (matches = re.exec(err.message))) { + erroneous = matches[1]; + + for (key in destination) { + i = 0; + + while (i < erroneous.length && erroneous.charAt(i) === key.charAt(i)) { + i++; + } + + if (i > mostMatchingChars) { + mostMatchingChars = i; + probableMatch = key; + } + } + + if (mostMatchingChars > 1) { + suggestion = mixitup.messages.errorConfigInvalidPropertySuggestion({ + probableMatch: probableMatch + }); + } + + message = mixitup.messages.errorConfigInvalidProperty({ + erroneous: erroneous, + suggestion: suggestion + }); + + throw new TypeError(message); + } + + throw err; + }, + + /** + * @private + * @param {string} str + * @return {function} + */ + + template: function(str) { + var re = /\${([\w]*)}/g, + dynamics = {}, + matches = null; + + while ((matches = re.exec(str))) { + dynamics[matches[1]] = new RegExp('\\${' + matches[1] + '}', 'g'); + } + + return function(data) { + var key = '', + output = str; + + data = data || {}; + + for (key in dynamics) { + output = output.replace(dynamics[key], typeof data[key] !== 'undefined' ? data[key] : ''); + } + + return output; + }; + }, + + /** + * @private + * @param {HTMLElement} el + * @param {string} type + * @param {function} fn + * @param {boolean} useCapture + * @return {void} + */ + + on: function(el, type, fn, useCapture) { + if (!el) return; + + if (el.addEventListener) { + el.addEventListener(type, fn, useCapture); + } else if (el.attachEvent) { + el['e' + type + fn] = fn; + + el[type + fn] = function() { + el['e' + type + fn](window.event); + }; + + el.attachEvent('on' + type, el[type + fn]); + } + }, + + /** + * @private + * @param {HTMLElement} el + * @param {string} type + * @param {function} fn + * @return {void} + */ + + off: function(el, type, fn) { + if (!el) return; + + if (el.removeEventListener) { + el.removeEventListener(type, fn, false); + } else if (el.detachEvent) { + el.detachEvent('on' + type, el[type + fn]); + el[type + fn] = null; + } + }, + + /** + * @private + * @param {string} eventType + * @param {object} detail + * @param {Document} [doc] + * @return {CustomEvent} + */ + + getCustomEvent: function(eventType, detail, doc) { + var event = null; + + doc = doc || window.document; + + if (typeof window.CustomEvent === 'function') { + event = new window.CustomEvent(eventType, { + detail: detail, + bubbles: true, + cancelable: true + }); + } else if (typeof doc.createEvent === 'function') { + event = doc.createEvent('CustomEvent'); + event.initCustomEvent(eventType, true, true, detail); + } else { + event = doc.createEventObject(), + event.type = eventType; + + event.returnValue = false; + event.cancelBubble = false; + event.detail = detail; + } + + return event; + }, + + /** + * @private + * @param {Event} e + * @return {Event} + */ + + getOriginalEvent: function(e) { + if (e.touches && e.touches.length) { + return e.touches[0]; + } else if (e.changedTouches && e.changedTouches.length) { + return e.changedTouches[0]; + } else { + return e; + } + }, + + /** + * @private + * @param {HTMLElement} el + * @param {string} selector + * @return {Number} + */ + + index: function(el, selector) { + var i = 0; + + while ((el = el.previousElementSibling) !== null) { + if (!selector || el.matches(selector)) { + ++i; + } + } + + return i; + }, + + /** + * Converts a dash or snake-case string to camel case. + * + * @private + * @param {string} str + * @param {boolean} [isPascal] + * @return {string} + */ + + camelCase: function(str) { + return str.toLowerCase().replace(/([_-][a-z])/g, function($1) { + return $1.toUpperCase().replace(/[_-]/, ''); + }); + }, + + /** + * Converts a dash or snake-case string to pascal case. + * + * @private + * @param {string} str + * @param {boolean} [isPascal] + * @return {string} + */ + + pascalCase: function(str) { + return (str = this.camelCase(str)).charAt(0).toUpperCase() + str.slice(1); + }, + + /** + * Converts a camel or pascal-case string to dash case. + * + * @private + * @param {string} str + * @return {string} + */ + + dashCase: function(str) { + return str.replace(/([A-Z])/g, '-$1').replace(/^-/, '').toLowerCase(); + }, + + /** + * @private + * @param {HTMLElement} el + * @param {HTMLHtmlElement} [doc] + * @return {boolean} + */ + + isElement: function(el, doc) { + doc = doc || window.document; + + if ( + window.HTMLElement && + el instanceof window.HTMLElement + ) { + return true; + } else if ( + doc.defaultView && + doc.defaultView.HTMLElement && + el instanceof doc.defaultView.HTMLElement + ) { + return true; + } else { + return ( + el !== null && + el.nodeType === 1 && + typeof el.nodeName === 'string' + ); + } + }, + + /** + * @private + * @param {string} htmlString + * @param {HTMLHtmlElement} [doc] + * @return {DocumentFragment} + */ + + createElement: function(htmlString, doc) { + var frag = null, + temp = null; + + doc = doc || window.document; + + frag = doc.createDocumentFragment(); + temp = doc.createElement('div'); + + temp.innerHTML = htmlString.trim(); + + while (temp.firstChild) { + frag.appendChild(temp.firstChild); + } + + return frag; + }, + + /** + * @private + * @param {Node} node + * @return {void} + */ + + removeWhitespace: function(node) { + var deleting; + + while (node && node.nodeName === '#text') { + deleting = node; + + node = node.previousSibling; + + deleting.parentElement && deleting.parentElement.removeChild(deleting); + } + }, + + /** + * @private + * @param {Array<*>} a + * @param {Array<*>} b + * @return {boolean} + */ + + isEqualArray: function(a, b) { + var i = a.length; + + if (i !== b.length) return false; + + while (i--) { + if (a[i] !== b[i]) return false; + } + + return true; + }, + + /** + * @private + * @param {object} a + * @param {object} b + * @return {boolean} + */ + + deepEquals: function(a, b) { + var key; + + if (typeof a === 'object' && a && typeof b === 'object' && b) { + if (Object.keys(a).length !== Object.keys(b).length) return false; + + for (key in a) { + if (!b.hasOwnProperty(key) || !this.deepEquals(a[key], b[key])) return false; + } + } else if (a !== b) { + return false; + } + + return true; + }, + + /** + * @private + * @param {Array<*>} oldArray + * @return {Array<*>} + */ + + arrayShuffle: function(oldArray) { + var newArray = oldArray.slice(), + len = newArray.length, + i = len, + p = -1, + t = []; + + while (i--) { + p = ~~(Math.random() * len); + t = newArray[i]; + + newArray[i] = newArray[p]; + newArray[p] = t; + } + + return newArray; + }, + + /** + * @private + * @param {object} list + */ + + arrayFromList: function(list) { + var output, i; + + try { + return Array.prototype.slice.call(list); + } catch(err) { + output = []; + + for (i = 0; i < list.length; i++) { + output.push(list[i]); + } + + return output; + } + }, + + /** + * @private + * @param {function} func + * @param {Number} wait + * @param {boolean} immediate + * @return {function} + */ + + debounce: function(func, wait, immediate) { + var timeout; + + return function() { + var self = this, + args = arguments, + callNow = immediate && !timeout, + later = null; + + later = function() { + timeout = null; + + if (!immediate) { + func.apply(self, args); + } + }; + + clearTimeout(timeout); + + timeout = setTimeout(later, wait); + + if (callNow) func.apply(self, args); + }; + }, + + /** + * @private + * @param {HTMLElement} element + * @return {object} + */ + + position: function(element) { + var xPosition = 0, + yPosition = 0, + offsetParent = element; + + while (element) { + xPosition -= element.scrollLeft; + yPosition -= element.scrollTop; + + if (element === offsetParent) { + xPosition += element.offsetLeft; + yPosition += element.offsetTop; + + offsetParent = element.offsetParent; + } + + element = element.parentElement; + } + + return { + x: xPosition, + y: yPosition + }; + }, + + /** + * @private + * @param {object} node1 + * @param {object} node2 + * @return {Number} + */ + + getHypotenuse: function(node1, node2) { + var distanceX = node1.x - node2.x, + distanceY = node1.y - node2.y; + + distanceX = distanceX < 0 ? distanceX * -1 : distanceX, + distanceY = distanceY < 0 ? distanceY * -1 : distanceY; + + return Math.sqrt(Math.pow(distanceX, 2) + Math.pow(distanceY, 2)); + }, + + /** + * Calcuates the area of intersection between two rectangles and expresses it as + * a ratio in comparison to the area of the first rectangle. + * + * @private + * @param {Rect} box1 + * @param {Rect} box2 + * @return {number} + */ + + getIntersectionRatio: function(box1, box2) { + var controlArea = box1.width * box1.height, + intersectionX = -1, + intersectionY = -1, + intersectionArea = -1, + ratio = -1; + + intersectionX = + Math.max(0, Math.min(box1.left + box1.width, box2.left + box2.width) - Math.max(box1.left, box2.left)); + + intersectionY = + Math.max(0, Math.min(box1.top + box1.height, box2.top + box2.height) - Math.max(box1.top, box2.top)); + + intersectionArea = intersectionY * intersectionX; + + ratio = intersectionArea / controlArea; + + return ratio; + }, + + /** + * @private + * @param {object} el + * @param {string} selector + * @param {boolean} [includeSelf] + * @param {HTMLHtmlElement} [doc] + * @return {Element|null} + */ + + closestParent: function(el, selector, includeSelf, doc) { + var parent = el.parentNode; + + doc = doc || window.document; + + if (includeSelf && el.matches(selector)) { + return el; + } + + while (parent && parent != doc.body) { + if (parent.matches && parent.matches(selector)) { + return parent; + } else if (parent.parentNode) { + parent = parent.parentNode; + } else { + return null; + } + } + + return null; + }, + + /** + * @private + * @param {HTMLElement} el + * @param {string} selector + * @param {HTMLHtmlElement} [doc] + * @return {NodeList} + */ + + children: function(el, selector, doc) { + var children = [], + tempId = ''; + + doc = doc || window.doc; + + if (el) { + if (!el.id) { + tempId = 'Temp' + this.randomHexKey(); + + el.id = tempId; + } + + children = doc.querySelectorAll('#' + el.id + ' > ' + selector); + + if (tempId) { + el.removeAttribute('id'); + } + } + + return children; + }, + + /** + * Creates a clone of a provided array, with any empty strings removed. + * + * @private + * @param {Array<*>} originalArray + * @return {Array<*>} + */ + + clean: function(originalArray) { + var cleanArray = [], + i = -1; + + for (i = 0; i < originalArray.length; i++) { + if (originalArray[i] !== '') { + cleanArray.push(originalArray[i]); + } + } + + return cleanArray; + }, + + /** + * Abstracts an ES6 promise into a q-like deferred interface for storage and deferred resolution. + * + * @private + * @param {object} libraries + * @return {h.Deferred} + */ + + defer: function(libraries) { + var deferred = null, + promiseWrapper = null, + $ = null; + + promiseWrapper = new this.Deferred(); + + if (mixitup.features.has.promises) { + // ES6 native promise or polyfill + + promiseWrapper.promise = new Promise(function(resolve, reject) { + promiseWrapper.resolve = resolve; + promiseWrapper.reject = reject; + }); + } else if (($ = (window.jQuery || libraries.$)) && typeof $.Deferred === 'function') { + // jQuery + + deferred = $.Deferred(); + + promiseWrapper.promise = deferred.promise(); + promiseWrapper.resolve = deferred.resolve; + promiseWrapper.reject = deferred.reject; + } else if (window.console) { + // No implementation + + console.warn(mixitup.messages.warningNoPromiseImplementation()); + } + + return promiseWrapper; + }, + + /** + * @private + * @param {Array} tasks + * @param {object} libraries + * @return {Promise} + */ + + all: function(tasks, libraries) { + var $ = null; + + if (mixitup.features.has.promises) { + return Promise.all(tasks); + } else if (($ = (window.jQuery || libraries.$)) && typeof $.when === 'function') { + return $.when.apply($, tasks) + .done(function() { + // jQuery when returns spread arguments rather than an array or resolutions + + return arguments; + }); + } + + // No implementation + + if (window.console) { + console.warn(mixitup.messages.warningNoPromiseImplementation()); + } + + return []; + }, + + /** + * @private + * @param {HTMLElement} el + * @param {string} property + * @param {Array} vendors + * @return {string} + */ + + getPrefix: function(el, property, vendors) { + var i = -1, + prefix = ''; + + if (h.dashCase(property) in el.style) return ''; + + for (i = 0; prefix = vendors[i]; i++) { + if (prefix + property in el.style) { + return prefix.toLowerCase(); + } + } + + return 'unsupported'; + }, + + /** + * @private + * @return {string} + */ + + randomHex: function() { + return ('00000' + (Math.random() * 16777216 << 0).toString(16)).substr(-6).toUpperCase(); + }, + + /** + * @private + * @param {HTMLDocument} [doc] + * @return {object} + */ + + getDocumentState: function(doc) { + doc = typeof doc.body === 'object' ? doc : window.document; + + return { + scrollTop: window.pageYOffset, + scrollLeft: window.pageXOffset, + docHeight: doc.documentElement.scrollHeight, + docWidth: doc.documentElement.scrollWidth, + viewportHeight: doc.documentElement.clientHeight, + viewportWidth: doc.documentElement.clientWidth + }; + }, + + /** + * @private + * @param {object} obj + * @param {function} fn + * @return {function} + */ + + bind: function(obj, fn) { + return function() { + return fn.apply(obj, arguments); + }; + }, + + /** + * @private + * @param {HTMLElement} el + * @return {boolean} + */ + + isVisible: function(el) { + var styles = null; + + if (el.offsetParent) return true; + + styles = window.getComputedStyle(el); + + if ( + styles.position === 'fixed' && + styles.visibility !== 'hidden' && + styles.opacity !== '0' + ) { + // Fixed elements report no offsetParent, + // but may still be invisible + + return true; + } + + return false; + }, + + /** + * @private + * @param {object} obj + */ + + seal: function(obj) { + if (typeof Object.seal === 'function') { + Object.seal(obj); + } + }, + + /** + * @private + * @param {object} obj + */ + + freeze: function(obj) { + if (typeof Object.freeze === 'function') { + Object.freeze(obj); + } + }, + + /** + * @private + * @param {string} control + * @param {string} specimen + * @return {boolean} + */ + + compareVersions: function(control, specimen) { + var controlParts = control.split('.'), + specimenParts = specimen.split('.'), + controlPart = -1, + specimenPart = -1, + i = -1; + + for (i = 0; i < controlParts.length; i++) { + controlPart = parseInt(controlParts[i].replace(/[^\d.]/g, '')); + specimenPart = parseInt(specimenParts[i].replace(/[^\d.]/g, '') || 0); + + if (specimenPart < controlPart) { + return false; + } else if (specimenPart > controlPart) { + return true; + } + } + + return true; + }, + + /** + * @private + * @constructor + */ + + Deferred: function() { + this.promise = null; + this.resolve = null; + this.reject = null; + this.id = h.randomHex(); + }, + + /** + * @private + * @param {object} obj + * @return {boolean} + */ + + isEmptyObject: function(obj) { + var key = ''; + + if (typeof Object.keys === 'function') { + return Object.keys(obj).length === 0; + } + + for (key in obj) { + if (obj.hasOwnProperty(key)) { + return false; + } + } + + return true; + }, + + /** + * @param {mixitup.Config.ClassNames} classNames + * @param {string} elementName + * @param {string} [modifier] + * @return {string} + */ + + getClassname: function(classNames, elementName, modifier) { + var classname = ''; + + classname += classNames.block; + + if (classname.length) { + classname += classNames.delineatorElement; + } + + classname += classNames['element' + this.pascalCase(elementName)]; + + if (!modifier) return classname; + + if (classname.length) { + classname += classNames.delineatorModifier; + } + + classname += modifier; + + return classname; + }, + + /** + * Returns the value of a property on a given object via its string key. + * + * @param {object} obj + * @param {string} stringKey + * @return {*} value + */ + + getProperty: function(obj, stringKey) { + var parts = stringKey.split('.'), + returnCurrent = null, + current = '', + i = 0; + + if (!stringKey) { + return obj; + } + + returnCurrent = function(obj) { + if (!obj) { + return null; + } else { + return obj[current]; + } + }; + + while (i < parts.length) { + current = parts[i]; + + obj = returnCurrent(obj); + + i++; + } + + if (typeof obj !== 'undefined') { + return obj; + } else { + return null; + } + } +}; + +mixitup.h = h; \ No newline at end of file diff --git a/mixitup-3.3.1/src/i-move-data.js b/mixitup-3.3.1/src/i-move-data.js new file mode 100644 index 0000000..139e7f7 --- /dev/null +++ b/mixitup-3.3.1/src/i-move-data.js @@ -0,0 +1,32 @@ +/* global mixitup, h */ + +/** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.IMoveData = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.posIn = null; + this.posOut = null; + this.operation = null; + this.callback = null; + this.statusChange = ''; + this.duration = -1; + this.staggerIndex = -1; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.IMoveData); + +mixitup.IMoveData.prototype = Object.create(mixitup.Base.prototype); + +mixitup.IMoveData.prototype.constructor = mixitup.IMoveData; \ No newline at end of file diff --git a/mixitup-3.3.1/src/messages.js b/mixitup-3.3.1/src/messages.js new file mode 100644 index 0000000..b6b9cd5 --- /dev/null +++ b/mixitup-3.3.1/src/messages.js @@ -0,0 +1,122 @@ +/* global mixitup, h */ + +/** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.Messages = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /* Errors + ----------------------------------------------------------------------------- */ + + this.ERROR_FACTORY_INVALID_CONTAINER = + '[MixItUp] An invalid selector or element reference was passed to the mixitup factory function'; + + this.ERROR_FACTORY_CONTAINER_NOT_FOUND = + '[MixItUp] The provided selector yielded no container element'; + + this.ERROR_CONFIG_INVALID_ANIMATION_EFFECTS = + '[MixItUp] Invalid value for `animation.effects`'; + + this.ERROR_CONFIG_INVALID_CONTROLS_SCOPE = + '[MixItUp] Invalid value for `controls.scope`'; + + this.ERROR_CONFIG_INVALID_PROPERTY = + '[MixitUp] Invalid configuration object property "${erroneous}"${suggestion}'; + + this.ERROR_CONFIG_INVALID_PROPERTY_SUGGESTION = + '. Did you mean "${probableMatch}"?'; + + this.ERROR_CONFIG_DATA_UID_KEY_NOT_SET = + '[MixItUp] To use the dataset API, a UID key must be specified using `data.uidKey`'; + + this.ERROR_DATASET_INVALID_UID_KEY = + '[MixItUp] The specified UID key "${uidKey}" is not present on one or more dataset items'; + + this.ERROR_DATASET_DUPLICATE_UID = + '[MixItUp] The UID "${uid}" was found on two or more dataset items. UIDs must be unique.'; + + this.ERROR_INSERT_INVALID_ARGUMENTS = + '[MixItUp] Please provider either an index or a sibling and position to insert, not both'; + + this.ERROR_INSERT_PREEXISTING_ELEMENT = + '[MixItUp] An element to be inserted already exists in the container'; + + this.ERROR_FILTER_INVALID_ARGUMENTS = + '[MixItUp] Please provide either a selector or collection `.filter()`, not both'; + + this.ERROR_DATASET_NOT_SET = + '[MixItUp] To use the dataset API with pre-rendered targets, a starting dataset must be set using `load.dataset`'; + + this.ERROR_DATASET_PRERENDERED_MISMATCH = + '[MixItUp] `load.dataset` does not match pre-rendered targets'; + + this.ERROR_DATASET_RENDERER_NOT_SET = + '[MixItUp] To insert an element via the dataset API, a target renderer function must be provided to `render.target`'; + + this.ERROR_SORT_NON_EXISTENT_ELEMENT = + '[MixItUp] An element to be sorted does not already exist in the container'; + + /* Warnings + ----------------------------------------------------------------------------- */ + + this.WARNING_FACTORY_PREEXISTING_INSTANCE = + '[MixItUp] WARNING: This element already has an active MixItUp instance. The provided configuration object will be ignored.' + + ' If you wish to perform additional methods on this instance, please create a reference.'; + + this.WARNING_INSERT_NO_ELEMENTS = + '[MixItUp] WARNING: No valid elements were passed to `.insert()`'; + + this.WARNING_REMOVE_NO_ELEMENTS = + '[MixItUp] WARNING: No valid elements were passed to `.remove()`'; + + this.WARNING_MULTIMIX_INSTANCE_QUEUE_FULL = + '[MixItUp] WARNING: An operation was requested but the MixItUp instance was busy. The operation was rejected because the ' + + 'queue is full or queuing is disabled.'; + + this.WARNING_GET_OPERATION_INSTANCE_BUSY = + '[MixItUp] WARNING: Operations can be be created while the MixItUp instance is busy.'; + + this.WARNING_NO_PROMISE_IMPLEMENTATION = + '[MixItUp] WARNING: No Promise implementations could be found. If you wish to use promises with MixItUp please install' + + ' an ES6 Promise polyfill.'; + + this.WARNING_INCONSISTENT_SORTING_ATTRIBUTES = + '[MixItUp] WARNING: The requested sorting data attribute "${attribute}" was not present on one or more target elements' + + ' which may product unexpected sort output'; + + this.callActions('afterConstruct'); + + this.compileTemplates(); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.Messages); + +mixitup.Messages.prototype = Object.create(mixitup.Base.prototype); + +mixitup.Messages.prototype.constructor = mixitup.Messages; + +/** + * @return {void} + */ + +mixitup.Messages.prototype.compileTemplates = function() { + var errorKey = ''; + var errorMessage = ''; + + for (errorKey in this) { + if (typeof (errorMessage = this[errorKey]) !== 'string') continue; + + this[h.camelCase(errorKey)] = h.template(errorMessage); + } +}; + +mixitup.messages = new mixitup.Messages(); \ No newline at end of file diff --git a/mixitup-3.3.1/src/mixer-dom.js b/mixitup-3.3.1/src/mixer-dom.js new file mode 100644 index 0000000..7b3f89d --- /dev/null +++ b/mixitup-3.3.1/src/mixer-dom.js @@ -0,0 +1,30 @@ +/* global mixitup, h */ + +/** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.MixerDom = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.document = null; + this.body = null; + this.container = null; + this.parent = null; + this.targets = []; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.MixerDom); + +mixitup.MixerDom.prototype = Object.create(mixitup.Base.prototype); + +mixitup.MixerDom.prototype.constructor = mixitup.MixerDom; \ No newline at end of file diff --git a/mixitup-3.3.1/src/mixer.js b/mixitup-3.3.1/src/mixer.js new file mode 100644 index 0000000..52b1d59 --- /dev/null +++ b/mixitup-3.3.1/src/mixer.js @@ -0,0 +1,4363 @@ +/* global mixitup, h */ + +/** + * The `mixitup.Mixer` class is used to hold discreet, user-configured + * instances of MixItUp on a provided container element. + * + * Mixer instances are returned whenever the `mixitup()` factory function is called, + * which expose a range of methods enabling API-based filtering, sorting, + * insertion, removal and more. + * + * @constructor + * @namespace + * @memberof mixitup + * @public + * @since 3.0.0 + */ + +mixitup.Mixer = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.config = new mixitup.Config(); + + this.id = ''; + + this.isBusy = false; + this.isToggling = false; + this.incPadding = true; + + this.controls = []; + this.targets = []; + this.origOrder = []; + this.cache = {}; + + this.toggleArray = []; + + this.targetsMoved = 0; + this.targetsImmovable = 0; + this.targetsBound = 0; + this.targetsDone = 0; + + this.staggerDuration = 0; + this.effectsIn = null; + this.effectsOut = null; + this.transformIn = []; + this.transformOut = []; + this.queue = []; + + this.state = null; + this.lastOperation = null; + this.lastClicked = null; + this.userCallback = null; + this.userDeferred = null; + + this.dom = new mixitup.MixerDom(); + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.Mixer); + +mixitup.Mixer.prototype = Object.create(mixitup.Base.prototype); + +h.extend(mixitup.Mixer.prototype, +/** @lends mixitup.Mixer */ +{ + constructor: mixitup.Mixer, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {HTMLElement} container + * @param {HTMLElement} document + * @param {string} id + * @param {object} [config] + */ + + attach: function(container, document, id, config) { + var self = this, + target = null, + i = -1; + + self.callActions('beforeAttach', arguments); + + self.id = id; + + if (config) { + h.extend(self.config, config, true, true); + } + + self.sanitizeConfig(); + + self.cacheDom(container, document); + + if (self.config.layout.containerClassName) { + h.addClass(self.dom.container, self.config.layout.containerClassName); + } + + if (!mixitup.features.has.transitions) { + self.config.animation.enable = false; + } + + if (typeof window.console === 'undefined') { + self.config.debug.showWarnings = false; + } + + if (self.config.data.uidKey) { + // If the dataset API is in use, force disable controls + + self.config.controls.enable = false; + } + + self.indexTargets(); + + self.state = self.getInitialState(); + + for (i = 0; target = self.lastOperation.toHide[i]; i++) { + target.hide(); + } + + if (self.config.controls.enable) { + self.initControls(); + + self.buildToggleArray(null, self.state); + + self.updateControls({ + filter: self.state.activeFilter, + sort: self.state.activeSort + }); + } + + self.parseEffects(); + + self.callActions('afterAttach', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @return {void} + */ + + sanitizeConfig: function() { + var self = this; + + self.callActions('beforeSanitizeConfig', arguments); + + // Sanitize enum/string config options + + self.config.controls.scope = self.config.controls.scope.toLowerCase().trim(); + self.config.controls.toggleLogic = self.config.controls.toggleLogic.toLowerCase().trim(); + self.config.controls.toggleDefault = self.config.controls.toggleDefault.toLowerCase().trim(); + + self.config.animation.effects = self.config.animation.effects.trim(); + + self.callActions('afterSanitizeConfig', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @return {mixitup.State} + */ + + getInitialState: function() { + var self = this, + state = new mixitup.State(), + operation = new mixitup.Operation(); + + self.callActions('beforeGetInitialState', arguments); + + // Map initial values into a mock state object in order to construct an operation + + state.activeContainerClassName = self.config.layout.containerClassName; + + if (self.config.load.dataset) { + // Dataset API + + if (!self.config.data.uidKey || typeof self.config.data.uidKey !== 'string') { + throw new TypeError(mixitup.messages.errorConfigDataUidKeyNotSet()); + } + + operation.startDataset = operation.newDataset = state.activeDataset = self.config.load.dataset.slice(); + operation.startContainerClassName = operation.newContainerClassName = state.activeContainerClassName; + operation.show = self.targets.slice(); + + state = self.callFilters('stateGetInitialState', state, arguments); + } else { + // DOM API + + state.activeFilter = self.parseFilterArgs([self.config.load.filter]).command; + state.activeSort = self.parseSortArgs([self.config.load.sort]).command; + state.totalTargets = self.targets.length; + + state = self.callFilters('stateGetInitialState', state, arguments); + + if ( + state.activeSort.collection || state.activeSort.attribute || + state.activeSort.order === 'random' || state.activeSort.order === 'desc' + ) { + // Sorting on load + + operation.newSort = state.activeSort; + + self.sortOperation(operation); + + self.printSort(false, operation); + + self.targets = operation.newOrder; + } else { + operation.startOrder = operation.newOrder = self.targets; + } + + operation.startFilter = operation.newFilter = state.activeFilter; + operation.startSort = operation.newSort = state.activeSort; + operation.startContainerClassName = operation.newContainerClassName = state.activeContainerClassName; + + if (operation.newFilter.selector === 'all') { + operation.newFilter.selector = self.config.selectors.target; + } else if (operation.newFilter.selector === 'none') { + operation.newFilter.selector = ''; + } + } + + operation = self.callFilters('operationGetInitialState', operation, [state]); + + self.lastOperation = operation; + + if (operation.newFilter) { + self.filterOperation(operation); + } + + state = self.buildState(operation); + + return state; + }, + + /** + * Caches references of DOM elements neccessary for the mixer's functionality. + * + * @private + * @instance + * @since 3.0.0 + * @param {HTMLElement} el + * @param {HTMLHtmlElement} document + * @return {void} + */ + + cacheDom: function(el, document) { + var self = this; + + self.callActions('beforeCacheDom', arguments); + + self.dom.document = document; + self.dom.body = self.dom.document.querySelector('body'); + self.dom.container = el; + self.dom.parent = el; + + self.callActions('afterCacheDom', arguments); + }, + + /** + * Indexes all child elements of the mixer matching the `selectors.target` + * selector, instantiating a mixitup.Target for each one. + * + * @private + * @instance + * @since 3.0.0 + * @return {void} + */ + + indexTargets: function() { + var self = this, + target = null, + el = null, + dataset = null, + i = -1; + + self.callActions('beforeIndexTargets', arguments); + + self.dom.targets = self.config.layout.allowNestedTargets ? + self.dom.container.querySelectorAll(self.config.selectors.target) : + h.children(self.dom.container, self.config.selectors.target, self.dom.document); + + self.dom.targets = h.arrayFromList(self.dom.targets); + + self.targets = []; + + if ((dataset = self.config.load.dataset) && dataset.length !== self.dom.targets.length) { + throw new Error(mixitup.messages.errorDatasetPrerenderedMismatch()); + } + + if (self.dom.targets.length) { + for (i = 0; el = self.dom.targets[i]; i++) { + target = new mixitup.Target(); + + target.init(el, self, dataset ? dataset[i] : void(0)); + + target.isInDom = true; + + self.targets.push(target); + } + + self.dom.parent = self.dom.targets[0].parentElement === self.dom.container ? + self.dom.container : + self.dom.targets[0].parentElement; + } + + self.origOrder = self.targets; + + self.callActions('afterIndexTargets', arguments); + }, + + initControls: function() { + var self = this, + definition = '', + controlElements = null, + el = null, + parent = null, + delagators = null, + control = null, + i = -1, + j = -1; + + self.callActions('beforeInitControls', arguments); + + switch (self.config.controls.scope) { + case 'local': + parent = self.dom.container; + + break; + case 'global': + parent = self.dom.document; + + break; + default: + throw new Error(mixitup.messages.errorConfigInvalidControlsScope()); + } + + for (i = 0; definition = mixitup.controlDefinitions[i]; i++) { + if (self.config.controls.live || definition.live) { + if (definition.parent) { + delagators = self.dom[definition.parent]; + + if (!delagators || delagators.length < 0) continue; + + if (typeof delagators.length !== 'number') { + delagators = [delagators]; + } + } else { + delagators = [parent]; + } + + for (j = 0; (el = delagators[j]); j++) { + control = self.getControl(el, definition.type, definition.selector); + + self.controls.push(control); + } + } else { + controlElements = parent.querySelectorAll(self.config.selectors.control + definition.selector); + + for (j = 0; (el = controlElements[j]); j++) { + control = self.getControl(el, definition.type, ''); + + if (!control) continue; + + self.controls.push(control); + } + } + } + + self.callActions('afterInitControls', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {HTMLElement} el + * @param {string} type + * @param {string} selector + * @return {mixitup.Control|null} + */ + + getControl: function(el, type, selector) { + var self = this, + control = null, + i = -1; + + self.callActions('beforeGetControl', arguments); + + if (!selector) { + // Static controls only + + for (i = 0; control = mixitup.controls[i]; i++) { + if (control.el === el && control.isBound(self)) { + // Control already bound to this mixer (as another type). + + // NB: This prevents duplicate controls from being registered where a selector + // might collide, eg: "[data-filter]" and "[data-filter][data-sort]" + + return self.callFilters('controlGetControl', null, arguments); + } else if (control.el === el && control.type === type && control.selector === selector) { + // Another mixer is already using this control, add this mixer as a binding + + control.addBinding(self); + + return self.callFilters('controlGetControl', control, arguments); + } + } + } + + // Create new control + + control = new mixitup.Control(); + + control.init(el, type, selector); + + control.classNames.base = h.getClassname(self.config.classNames, type); + control.classNames.active = h.getClassname(self.config.classNames, type, self.config.classNames.modifierActive); + control.classNames.disabled = h.getClassname(self.config.classNames, type, self.config.classNames.modifierDisabled); + + // Add a reference to this mixer as a binding + + control.addBinding(self); + + return self.callFilters('controlGetControl', control, arguments); + }, + + /** + * Creates a compound selector by joining the `toggleArray` value as per the + * defined toggle logic. + * + * @private + * @instance + * @since 3.0.0 + * @return {string} + */ + + getToggleSelector: function() { + var self = this, + delineator = self.config.controls.toggleLogic === 'or' ? ', ' : '', + toggleSelector = ''; + + self.callActions('beforeGetToggleSelector', arguments); + + self.toggleArray = h.clean(self.toggleArray); + + toggleSelector = self.toggleArray.join(delineator); + + if (toggleSelector === '') { + toggleSelector = self.config.controls.toggleDefault; + } + + return self.callFilters('selectorGetToggleSelector', toggleSelector, arguments); + }, + + /** + * Breaks compound selector strings in an array of discreet selectors, + * as per the active `controls.toggleLogic` configuration option. Accepts + * either a dynamic command object, or a state object. + * + * @private + * @instance + * @since 2.0.0 + * @param {object} [command] + * @param {mixitup.State} [state] + * @return {void} + */ + + buildToggleArray: function(command, state) { + var self = this, + activeFilterSelector = ''; + + self.callActions('beforeBuildToggleArray', arguments); + + if (command && command.filter) { + activeFilterSelector = command.filter.selector.replace(/\s/g, ''); + } else if (state) { + activeFilterSelector = state.activeFilter.selector.replace(/\s/g, ''); + } else { + return; + } + + if (activeFilterSelector === self.config.selectors.target || activeFilterSelector === 'all') { + activeFilterSelector = ''; + } + + if (self.config.controls.toggleLogic === 'or') { + self.toggleArray = activeFilterSelector.split(','); + } else { + self.toggleArray = self.splitCompoundSelector(activeFilterSelector); + } + + self.toggleArray = h.clean(self.toggleArray); + + self.callActions('afterBuildToggleArray', arguments); + }, + + /** + * Takes a compound selector (e.g. `.cat-1.cat-2`, `[data-cat="1"][data-cat="2"]`) + * and breaks into its individual selectors. + * + * @private + * @instance + * @since 3.0.0 + * @param {string} compoundSelector + * @return {string[]} + */ + + splitCompoundSelector: function(compoundSelector) { + // Break at a `.` or `[`, capturing the delineator + + var partials = compoundSelector.split(/([\.\[])/g), + toggleArray = [], + selector = '', + i = -1; + + if (partials[0] === '') { + partials.shift(); + } + + for (i = 0; i < partials.length; i++) { + if (i % 2 === 0) { + selector = ''; + } + + selector += partials[i]; + + if (i % 2 !== 0) { + toggleArray.push(selector); + } + } + + return toggleArray; + }, + + /** + * Updates controls to their active/inactive state based on the command or + * current state of the mixer. + * + * @private + * @instance + * @since 2.0.0 + * @param {object} command + * @return {void} + */ + + updateControls: function(command) { + var self = this, + control = null, + output = new mixitup.CommandMultimix(), + i = -1; + + self.callActions('beforeUpdateControls', arguments); + + // Sanitise to defaults + + if (command.filter) { + output.filter = command.filter.selector; + } else { + output.filter = self.state.activeFilter.selector; + } + + if (command.sort) { + output.sort = self.buildSortString(command.sort); + } else { + output.sort = self.buildSortString(self.state.activeSort); + } + + if (output.filter === self.config.selectors.target) { + output.filter = 'all'; + } + + if (output.filter === '') { + output.filter = 'none'; + } + + h.freeze(output); + + for (i = 0; control = self.controls[i]; i++) { + control.update(output, self.toggleArray); + } + + self.callActions('afterUpdateControls', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {mixitup.CommandSort} command + * @return {string} + */ + + buildSortString: function(command) { + var self = this; + var output = ''; + + output += command.sortString; + + if (command.next) { + output += ' ' + self.buildSortString(command.next); + } + + return output; + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {object} command + * @param {Operation} operation + * @return {Promise.} + */ + + insertTargets: function(command, operation) { + var self = this, + nextSibling = null, + insertionIndex = -1, + frag = null, + target = null, + el = null, + i = -1; + + self.callActions('beforeInsertTargets', arguments); + + if (typeof command.index === 'undefined') command.index = 0; + + nextSibling = self.getNextSibling(command.index, command.sibling, command.position); + frag = self.dom.document.createDocumentFragment(); + + if (nextSibling) { + insertionIndex = h.index(nextSibling, self.config.selectors.target); + } else { + insertionIndex = self.targets.length; + } + + if (command.collection) { + for (i = 0; el = command.collection[i]; i++) { + if (self.dom.targets.indexOf(el) > -1) { + throw new Error(mixitup.messages.errorInsertPreexistingElement()); + } + + // Ensure elements are hidden when they are added to the DOM, so they can + // be animated in gracefully + + el.style.display = 'none'; + + frag.appendChild(el); + frag.appendChild(self.dom.document.createTextNode(' ')); + + if (!h.isElement(el, self.dom.document) || !el.matches(self.config.selectors.target)) continue; + + target = new mixitup.Target(); + + target.init(el, self); + + target.isInDom = true; + + self.targets.splice(insertionIndex, 0, target); + + insertionIndex++; + } + + self.dom.parent.insertBefore(frag, nextSibling); + } + + // Since targets have been added, the original order must be updated + + operation.startOrder = self.origOrder = self.targets; + + self.callActions('afterInsertTargets', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Number} [index] + * @param {Element} [sibling] + * @param {string} [position] + * @return {Element} + */ + + getNextSibling: function(index, sibling, position) { + var self = this, + element = null; + + index = Math.max(index, 0); + + if (sibling && position === 'before') { + // Explicit sibling + + element = sibling; + } else if (sibling && position === 'after') { + // Explicit sibling + + element = sibling.nextElementSibling || null; + } else if (self.targets.length > 0 && typeof index !== 'undefined') { + // Index and targets exist + + element = (index < self.targets.length || !self.targets.length) ? + self.targets[index].dom.el : + self.targets[self.targets.length - 1].dom.el.nextElementSibling; + } else if (self.targets.length === 0 && self.dom.parent.children.length > 0) { + // No targets but other siblings + + if (self.config.layout.siblingAfter) { + element = self.config.layout.siblingAfter; + } else if (self.config.layout.siblingBefore) { + element = self.config.layout.siblingBefore.nextElementSibling; + } else { + self.dom.parent.children[0]; + } + } else { + element === null; + } + + return self.callFilters('elementGetNextSibling', element, arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + filterOperation: function(operation) { + var self = this, + testResult = false, + index = -1, + action = '', + target = null, + i = -1; + + self.callActions('beforeFilterOperation', arguments); + + action = operation.newFilter.action; + + for (i = 0; target = operation.newOrder[i]; i++) { + if (operation.newFilter.collection) { + // show via collection + + testResult = operation.newFilter.collection.indexOf(target.dom.el) > -1; + } else { + // show via selector + + if (operation.newFilter.selector === '') { + testResult = false; + } else { + testResult = target.dom.el.matches(operation.newFilter.selector); + } + } + + self.evaluateHideShow(testResult, target, action, operation); + } + + if (operation.toRemove.length) { + for (i = 0; target = operation.show[i]; i++) { + if (operation.toRemove.indexOf(target) > -1) { + // If any shown targets should be removed, move them into the toHide array + + operation.show.splice(i, 1); + + if ((index = operation.toShow.indexOf(target)) > -1) { + operation.toShow.splice(index, 1); + } + + operation.toHide.push(target); + operation.hide.push(target); + + i--; + } + } + } + + operation.matching = operation.show.slice(); + + if (operation.show.length === 0 && operation.newFilter.selector !== '' && self.targets.length !== 0) { + operation.hasFailed = true; + } + + self.callActions('afterFilterOperation', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {boolean} testResult + * @param {Element} target + * @param {string} action + * @param {Operation} operation + * @return {void} + */ + + evaluateHideShow: function(testResult, target, action, operation) { + var self = this, + filteredTestResult = false, + args = Array.prototype.slice.call(arguments, 1); + + filteredTestResult = self.callFilters('testResultEvaluateHideShow', testResult, args); + + self.callActions('beforeEvaluateHideShow', arguments); + + if ( + filteredTestResult === true && action === 'show' || + filteredTestResult === false && action === 'hide' + ) { + operation.show.push(target); + + !target.isShown && operation.toShow.push(target); + } else { + operation.hide.push(target); + + target.isShown && operation.toHide.push(target); + } + + self.callActions('afterEvaluateHideShow', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + sortOperation: function(operation) { + var self = this, + newOrder = [], + target = null, + el = null, + i = -1; + + self.callActions('beforeSortOperation', arguments); + + operation.startOrder = self.targets; + + if (operation.newSort.collection) { + // Sort by collection + + newOrder = []; + + for (i = 0; (el = operation.newSort.collection[i]); i++) { + if (self.dom.targets.indexOf(el) < 0) { + throw new Error(mixitup.messages.errorSortNonExistentElement()); + } + + target = new mixitup.Target(); + + target.init(el, self); + + target.isInDom = true; + + newOrder.push(target); + } + + operation.newOrder = newOrder; + } else if (operation.newSort.order === 'random') { + // Sort random + + operation.newOrder = h.arrayShuffle(operation.startOrder); + } else if (operation.newSort.attribute === '') { + // Sort by default + + operation.newOrder = self.origOrder.slice(); + + if (operation.newSort.order === 'desc') { + operation.newOrder.reverse(); + } + } else { + // Sort by attribute + + operation.newOrder = operation.startOrder.slice(); + + operation.newOrder.sort(function(a, b) { + return self.compare(a, b, operation.newSort); + }); + } + + if (h.isEqualArray(operation.newOrder, operation.startOrder)) { + operation.willSort = false; + } + + self.callActions('afterSortOperation', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {mixitup.Target} a + * @param {mixitup.Target} b + * @param {mixitup.CommandSort} command + * @return {Number} + */ + + compare: function(a, b, command) { + var self = this, + order = command.order, + attrA = self.getAttributeValue(a, command.attribute), + attrB = self.getAttributeValue(b, command.attribute); + + if (isNaN(attrA * 1) || isNaN(attrB * 1)) { + attrA = attrA.toLowerCase(); + attrB = attrB.toLowerCase(); + } else { + attrA = attrA * 1; + attrB = attrB * 1; + } + + if (attrA < attrB) { + return order === 'asc' ? -1 : 1; + } + + if (attrA > attrB) { + return order === 'asc' ? 1 : -1; + } + + if (attrA === attrB && command.next) { + return self.compare(a, b, command.next); + } + + return 0; + }, + + /** + * Reads the values of any data attributes present the provided target element + * which match the current sort command. + * + * @private + * @instance + * @since 3.0.0 + * @param {mixitup.Target} target + * @param {string} [attribute] + * @return {(String|Number)} + */ + + getAttributeValue: function(target, attribute) { + var self = this, + value = ''; + + value = target.dom.el.getAttribute('data-' + attribute); + + if (value === null) { + if (self.config.debug.showWarnings) { + // Encourage users to assign values to all targets to avoid erroneous sorting + // when types are mixed + + console.warn(mixitup.messages.warningInconsistentSortingAttributes({ + attribute: 'data-' + attribute + })); + } + } + + // If an attribute is not present, return 0 as a safety value + + return self.callFilters('valueGetAttributeValue', value || 0, arguments); + }, + + /** + * Inserts elements into the DOM in the appropriate + * order using a document fragment for minimal + * DOM thrashing + * + * @private + * @instance + * @since 2.0.0 + * @param {boolean} isResetting + * @param {Operation} operation + * @return {void} + */ + + printSort: function(isResetting, operation) { + var self = this, + startOrder = isResetting ? operation.newOrder : operation.startOrder, + newOrder = isResetting ? operation.startOrder : operation.newOrder, + nextSibling = startOrder.length ? startOrder[startOrder.length - 1].dom.el.nextElementSibling : null, + frag = window.document.createDocumentFragment(), + whitespace = null, + target = null, + el = null, + i = -1; + + self.callActions('beforePrintSort', arguments); + + // Empty the container + + for (i = 0; target = startOrder[i]; i++) { + el = target.dom.el; + + if (el.style.position === 'absolute') continue; + + h.removeWhitespace(el.previousSibling); + + el.parentElement.removeChild(el); + } + + whitespace = nextSibling ? nextSibling.previousSibling : self.dom.parent.lastChild; + + if (whitespace && whitespace.nodeName === '#text') { + h.removeWhitespace(whitespace); + } + + for (i = 0; target = newOrder[i]; i++) { + // Add targets into a document fragment + + el = target.dom.el; + + if (h.isElement(frag.lastChild)) { + frag.appendChild(window.document.createTextNode(' ')); + } + + frag.appendChild(el); + } + + // Insert the document fragment into the container + // before any other non-target elements + + if (self.dom.parent.firstChild && self.dom.parent.firstChild !== nextSibling) { + frag.insertBefore(window.document.createTextNode(' '), frag.childNodes[0]); + } + + if (nextSibling) { + frag.appendChild(window.document.createTextNode(' ')); + + self.dom.parent.insertBefore(frag, nextSibling); + } else { + self.dom.parent.appendChild(frag); + } + + self.callActions('afterPrintSort', arguments); + }, + + /** + * Parses user-defined sort strings (i.e. `default:asc`) into sort commands objects. + * + * @private + * @instance + * @since 3.0.0 + * @param {string} sortString + * @param {mixitup.CommandSort} command + * @return {mixitup.CommandSort} + */ + + parseSortString: function(sortString, command) { + var self = this, + rules = sortString.split(' '), + current = command, + rule = [], + i = -1; + + // command.sortString = sortString; + + for (i = 0; i < rules.length; i++) { + rule = rules[i].split(':'); + + current.sortString = rules[i]; + current.attribute = h.dashCase(rule[0]); + current.order = rule[1] || 'asc'; + + switch (current.attribute) { + case 'default': + // treat "default" as sorting by no attribute + + current.attribute = ''; + + break; + case 'random': + // treat "random" as an order not an attribute + + current.attribute = ''; + current.order = 'random'; + + break; + } + + if (!current.attribute || current.order === 'random') break; + + if (i < rules.length - 1) { + // Embed reference to the next command + + current.next = new mixitup.CommandSort(); + + h.freeze(current); + + current = current.next; + } + } + + return self.callFilters('commandsParseSort', command, arguments); + }, + + /** + * Parses all effects out of the user-defined `animation.effects` string into + * their respective properties and units. + * + * @private + * @instance + * @since 2.0.0 + * @return {void} + */ + + parseEffects: function() { + var self = this, + transformName = '', + effectsIn = self.config.animation.effectsIn || self.config.animation.effects, + effectsOut = self.config.animation.effectsOut || self.config.animation.effects; + + self.callActions('beforeParseEffects', arguments); + + self.effectsIn = new mixitup.StyleData(); + self.effectsOut = new mixitup.StyleData(); + self.transformIn = []; + self.transformOut = []; + + self.effectsIn.opacity = self.effectsOut.opacity = 1; + + self.parseEffect('fade', effectsIn, self.effectsIn, self.transformIn); + self.parseEffect('fade', effectsOut, self.effectsOut, self.transformOut, true); + + for (transformName in mixitup.transformDefaults) { + if (!(mixitup.transformDefaults[transformName] instanceof mixitup.TransformData)) { + continue; + } + + self.parseEffect(transformName, effectsIn, self.effectsIn, self.transformIn); + self.parseEffect(transformName, effectsOut, self.effectsOut, self.transformOut, true); + } + + self.parseEffect('stagger', effectsIn, self.effectsIn, self.transformIn); + self.parseEffect('stagger', effectsOut, self.effectsOut, self.transformOut, true); + + self.callActions('afterParseEffects', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {string} effectName + * @param {string} effectString + * @param {StyleData} effects + * @param {String[]} transform + * @param {boolean} [isOut] + */ + + parseEffect: function(effectName, effectString, effects, transform, isOut) { + var self = this, + re = /\(([^)]+)\)/, + propIndex = -1, + str = '', + match = [], + val = '', + units = ['%', 'px', 'em', 'rem', 'vh', 'vw', 'deg'], + unit = '', + i = -1; + + self.callActions('beforeParseEffect', arguments); + + if (typeof effectString !== 'string') { + throw new TypeError(mixitup.messages.errorConfigInvalidAnimationEffects()); + } + + if (effectString.indexOf(effectName) < 0) { + // The effect is not present in the effects string + + if (effectName === 'stagger') { + // Reset stagger to 0 + + self.staggerDuration = 0; + } + + return; + } + + // The effect is present + + propIndex = effectString.indexOf(effectName + '('); + + if (propIndex > -1) { + // The effect has a user defined value in parentheses + + // Extract from the first parenthesis to the end of string + + str = effectString.substring(propIndex); + + // Match any number of characters between "(" and ")" + + match = re.exec(str); + + val = match[1]; + } + + switch (effectName) { + case 'fade': + effects.opacity = val ? parseFloat(val) : 0; + + break; + case 'stagger': + self.staggerDuration = val ? parseFloat(val) : 100; + + // TODO: Currently stagger must be applied globally, but + // if seperate values are specified for in/out, this should + // be respected + + break; + default: + // All other effects are transforms following the same structure + + if (isOut && self.config.animation.reverseOut && effectName !== 'scale') { + effects[effectName].value = + (val ? parseFloat(val) : mixitup.transformDefaults[effectName].value) * -1; + } else { + effects[effectName].value = + (val ? parseFloat(val) : mixitup.transformDefaults[effectName].value); + } + + if (val) { + for (i = 0; unit = units[i]; i++) { + if (val.indexOf(unit) > -1) { + effects[effectName].unit = unit; + + break; + } + } + } else { + effects[effectName].unit = mixitup.transformDefaults[effectName].unit; + } + + transform.push( + effectName + + '(' + + effects[effectName].value + + effects[effectName].unit + + ')' + ); + } + + self.callActions('afterParseEffect', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {State} + */ + + buildState: function(operation) { + var self = this, + state = new mixitup.State(), + target = null, + i = -1; + + self.callActions('beforeBuildState', arguments); + + // Map target elements into state arrays. + // the real target objects should never be exposed + + for (i = 0; target = self.targets[i]; i++) { + if (!operation.toRemove.length || operation.toRemove.indexOf(target) < 0) { + state.targets.push(target.dom.el); + } + } + + for (i = 0; target = operation.matching[i]; i++) { + state.matching.push(target.dom.el); + } + + for (i = 0; target = operation.show[i]; i++) { + state.show.push(target.dom.el); + } + + for (i = 0; target = operation.hide[i]; i++) { + if (!operation.toRemove.length || operation.toRemove.indexOf(target) < 0) { + state.hide.push(target.dom.el); + } + } + + state.id = self.id; + state.container = self.dom.container; + state.activeFilter = operation.newFilter; + state.activeSort = operation.newSort; + state.activeDataset = operation.newDataset; + state.activeContainerClassName = operation.newContainerClassName; + state.hasFailed = operation.hasFailed; + state.totalTargets = self.targets.length; + state.totalShow = operation.show.length; + state.totalHide = operation.hide.length; + state.totalMatching = operation.matching.length; + state.triggerElement = operation.triggerElement; + + return self.callFilters('stateBuildState', state, arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {boolean} shouldAnimate + * @param {Operation} operation + * @return {void} + */ + + goMix: function(shouldAnimate, operation) { + var self = this, + deferred = null; + + self.callActions('beforeGoMix', arguments); + + // If the animation duration is set to 0ms, + // or no effects specified, + // or the container is hidden + // then abort animation + + if ( + !self.config.animation.duration || !self.config.animation.effects || !h.isVisible(self.dom.container) + ) { + shouldAnimate = false; + } + + if ( + !operation.toShow.length && + !operation.toHide.length && + !operation.willSort && + !operation.willChangeLayout + ) { + // If nothing to show or hide, and not sorting or + // changing layout + + shouldAnimate = false; + } + + if ( + !operation.startState.show.length && + !operation.show.length + ) { + // If nothing currently shown, nothing to show + + shouldAnimate = false; + } + + mixitup.events.fire('mixStart', self.dom.container, { + state: operation.startState, + futureState: operation.newState, + instance: self + }, self.dom.document); + + if (typeof self.config.callbacks.onMixStart === 'function') { + self.config.callbacks.onMixStart.call( + self.dom.container, + operation.startState, + operation.newState, + self + ); + } + + h.removeClass(self.dom.container, h.getClassname(self.config.classNames, 'container', self.config.classNames.modifierFailed)); + + if (!self.userDeferred) { + // Queue empty, no pending operations + + deferred = self.userDeferred = h.defer(mixitup.libraries); + } else { + // Use existing deferred + + deferred = self.userDeferred; + } + + self.isBusy = true; + + if (!shouldAnimate || !mixitup.features.has.transitions) { + // Abort + + if (self.config.debug.fauxAsync) { + setTimeout(function() { + self.cleanUp(operation); + }, self.config.animation.duration); + } else { + self.cleanUp(operation); + } + + return self.callFilters('promiseGoMix', deferred.promise, arguments); + } + + // If we should animate and the platform supports transitions, go for it + + if (window.pageYOffset !== operation.docState.scrollTop) { + window.scrollTo(operation.docState.scrollLeft, operation.docState.scrollTop); + } + + if (self.config.animation.applyPerspective) { + self.dom.parent.style[mixitup.features.perspectiveProp] = + self.config.animation.perspectiveDistance; + + self.dom.parent.style[mixitup.features.perspectiveOriginProp] = + self.config.animation.perspectiveOrigin; + } + + if ( + self.config.animation.animateResizeContainer && + operation.startHeight !== operation.newHeight && + operation.viewportDeltaY !== operation.startHeight - operation.newHeight + ) { + self.dom.parent.style.height = operation.startHeight + 'px'; + } + + if ( + self.config.animation.animateResizeContainer && + operation.startWidth !== operation.newWidth && + operation.viewportDeltaX !== operation.startWidth - operation.newWidth + ) { + self.dom.parent.style.width = operation.startWidth + 'px'; + } + + if (operation.startHeight === operation.newHeight) { + self.dom.parent.style.height = operation.startHeight + 'px'; + } + + if (operation.startWidth === operation.newWidth) { + self.dom.parent.style.width = operation.startWidth + 'px'; + } + + if (operation.startHeight === operation.newHeight && operation.startWidth === operation.newWidth) { + self.dom.parent.style.overflow = 'hidden'; + } + + requestAnimationFrame(function() { + self.moveTargets(operation); + }); + + return self.callFilters('promiseGoMix', deferred.promise, arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + getStartMixData: function(operation) { + var self = this, + parentStyle = window.getComputedStyle(self.dom.parent), + parentRect = self.dom.parent.getBoundingClientRect(), + target = null, + data = {}, + i = -1, + boxSizing = parentStyle[mixitup.features.boxSizingProp]; + + self.incPadding = (boxSizing === 'border-box'); + + self.callActions('beforeGetStartMixData', arguments); + + for (i = 0; target = operation.show[i]; i++) { + data = target.getPosData(); + + operation.showPosData[i] = { + startPosData: data + }; + } + + for (i = 0; target = operation.toHide[i]; i++) { + data = target.getPosData(); + + operation.toHidePosData[i] = { + startPosData: data + }; + } + + operation.startX = parentRect.left; + operation.startY = parentRect.top; + + operation.startHeight = self.incPadding ? + parentRect.height : + parentRect.height - + parseFloat(parentStyle.paddingTop) - + parseFloat(parentStyle.paddingBottom) - + parseFloat(parentStyle.borderTop) - + parseFloat(parentStyle.borderBottom); + + operation.startWidth = self.incPadding ? + parentRect.width : + parentRect.width - + parseFloat(parentStyle.paddingLeft) - + parseFloat(parentStyle.paddingRight) - + parseFloat(parentStyle.borderLeft) - + parseFloat(parentStyle.borderRight); + + self.callActions('afterGetStartMixData', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + setInter: function(operation) { + var self = this, + target = null, + i = -1; + + self.callActions('beforeSetInter', arguments); + + // Prevent scrollbar flicker on non-inertial scroll platforms by clamping height/width + + if (self.config.animation.clampHeight) { + self.dom.parent.style.height = operation.startHeight + 'px'; + self.dom.parent.style.overflow = 'hidden'; + } + + if (self.config.animation.clampWidth) { + self.dom.parent.style.width = operation.startWidth + 'px'; + self.dom.parent.style.overflow = 'hidden'; + } + + for (i = 0; target = operation.toShow[i]; i++) { + target.show(); + } + + if (operation.willChangeLayout) { + h.removeClass(self.dom.container, operation.startContainerClassName); + h.addClass(self.dom.container, operation.newContainerClassName); + } + + self.callActions('afterSetInter', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + getInterMixData: function(operation) { + var self = this, + target = null, + i = -1; + + self.callActions('beforeGetInterMixData', arguments); + + for (i = 0; target = operation.show[i]; i++) { + operation.showPosData[i].interPosData = target.getPosData(); + } + + for (i = 0; target = operation.toHide[i]; i++) { + operation.toHidePosData[i].interPosData = target.getPosData(); + } + + self.callActions('afterGetInterMixData', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + setFinal: function(operation) { + var self = this, + target = null, + i = -1; + + self.callActions('beforeSetFinal', arguments); + + operation.willSort && self.printSort(false, operation); + + for (i = 0; target = operation.toHide[i]; i++) { + target.hide(); + } + + self.callActions('afterSetFinal', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + getFinalMixData: function(operation) { + var self = this, + parentStyle = null, + parentRect = null, + target = null, + i = -1; + + self.callActions('beforeGetFinalMixData', arguments); + + for (i = 0; target = operation.show[i]; i++) { + operation.showPosData[i].finalPosData = target.getPosData(); + } + + for (i = 0; target = operation.toHide[i]; i++) { + operation.toHidePosData[i].finalPosData = target.getPosData(); + } + + // Remove clamping + + if (self.config.animation.clampHeight || self.config.animation.clampWidth) { + self.dom.parent.style.height = + self.dom.parent.style.width = + self.dom.parent.style.overflow = ''; + } + + if (!self.incPadding) { + parentStyle = window.getComputedStyle(self.dom.parent); + } + + parentRect = self.dom.parent.getBoundingClientRect(); + + operation.newX = parentRect.left; + operation.newY = parentRect.top; + + operation.newHeight = self.incPadding ? + parentRect.height : + parentRect.height - + parseFloat(parentStyle.paddingTop) - + parseFloat(parentStyle.paddingBottom) - + parseFloat(parentStyle.borderTop) - + parseFloat(parentStyle.borderBottom); + + operation.newWidth = self.incPadding ? + parentRect.width : + parentRect.width - + parseFloat(parentStyle.paddingLeft) - + parseFloat(parentStyle.paddingRight) - + parseFloat(parentStyle.borderLeft) - + parseFloat(parentStyle.borderRight); + + operation.viewportDeltaX = operation.docState.viewportWidth - this.dom.document.documentElement.clientWidth; + operation.viewportDeltaY = operation.docState.viewportHeight - this.dom.document.documentElement.clientHeight; + + if (operation.willSort) { + self.printSort(true, operation); + } + + for (i = 0; target = operation.toShow[i]; i++) { + target.hide(); + } + + for (i = 0; target = operation.toHide[i]; i++) { + target.show(); + } + + if (operation.willChangeLayout) { + h.removeClass(self.dom.container, operation.newContainerClassName); + h.addClass(self.dom.container, self.config.layout.containerClassName); + } + + self.callActions('afterGetFinalMixData', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Operation} operation + */ + + getTweenData: function(operation) { + var self = this, + target = null, + posData = null, + effectNames = Object.getOwnPropertyNames(self.effectsIn), + effectName = '', + effect = null, + widthChange = -1, + heightChange = -1, + i = -1, + j = -1; + + self.callActions('beforeGetTweenData', arguments); + + for (i = 0; target = operation.show[i]; i++) { + posData = operation.showPosData[i]; + posData.posIn = new mixitup.StyleData(); + posData.posOut = new mixitup.StyleData(); + posData.tweenData = new mixitup.StyleData(); + + // Process x and y + + if (target.isShown) { + posData.posIn.x = posData.startPosData.x - posData.interPosData.x; + posData.posIn.y = posData.startPosData.y - posData.interPosData.y; + } else { + posData.posIn.x = posData.posIn.y = 0; + } + + posData.posOut.x = posData.finalPosData.x - posData.interPosData.x; + posData.posOut.y = posData.finalPosData.y - posData.interPosData.y; + + // Process opacity + + posData.posIn.opacity = target.isShown ? 1 : self.effectsIn.opacity; + posData.posOut.opacity = 1; + posData.tweenData.opacity = posData.posOut.opacity - posData.posIn.opacity; + + // Adjust x and y if not nudging + + if (!target.isShown && !self.config.animation.nudge) { + posData.posIn.x = posData.posOut.x; + posData.posIn.y = posData.posOut.y; + } + + posData.tweenData.x = posData.posOut.x - posData.posIn.x; + posData.tweenData.y = posData.posOut.y - posData.posIn.y; + + // Process width, height, and margins + + if (self.config.animation.animateResizeTargets) { + posData.posIn.width = posData.startPosData.width; + posData.posIn.height = posData.startPosData.height; + + // "||" Prevents width/height change from including 0 width/height if hiding or showing + + widthChange = (posData.startPosData.width || posData.finalPosData.width) - posData.interPosData.width; + + posData.posIn.marginRight = posData.startPosData.marginRight - widthChange; + + heightChange = (posData.startPosData.height || posData.finalPosData.height) - posData.interPosData.height; + + posData.posIn.marginBottom = posData.startPosData.marginBottom - heightChange; + + posData.posOut.width = posData.finalPosData.width; + posData.posOut.height = posData.finalPosData.height; + + widthChange = (posData.finalPosData.width || posData.startPosData.width) - posData.interPosData.width; + + posData.posOut.marginRight = posData.finalPosData.marginRight - widthChange; + + heightChange = (posData.finalPosData.height || posData.startPosData.height) - posData.interPosData.height; + + posData.posOut.marginBottom = posData.finalPosData.marginBottom - heightChange; + + posData.tweenData.width = posData.posOut.width - posData.posIn.width; + posData.tweenData.height = posData.posOut.height - posData.posIn.height; + posData.tweenData.marginRight = posData.posOut.marginRight - posData.posIn.marginRight; + posData.tweenData.marginBottom = posData.posOut.marginBottom - posData.posIn.marginBottom; + } + + // Process transforms + + for (j = 0; effectName = effectNames[j]; j++) { + effect = self.effectsIn[effectName]; + + if (!(effect instanceof mixitup.TransformData) || !effect.value) continue; + + posData.posIn[effectName].value = effect.value; + posData.posOut[effectName].value = 0; + + posData.tweenData[effectName].value = + posData.posOut[effectName].value - posData.posIn[effectName].value; + + posData.posIn[effectName].unit = + posData.posOut[effectName].unit = + posData.tweenData[effectName].unit = + effect.unit; + } + } + + for (i = 0; target = operation.toHide[i]; i++) { + posData = operation.toHidePosData[i]; + posData.posIn = new mixitup.StyleData(); + posData.posOut = new mixitup.StyleData(); + posData.tweenData = new mixitup.StyleData(); + + // Process x and y + + posData.posIn.x = target.isShown ? posData.startPosData.x - posData.interPosData.x : 0; + posData.posIn.y = target.isShown ? posData.startPosData.y - posData.interPosData.y : 0; + posData.posOut.x = self.config.animation.nudge ? 0 : posData.posIn.x; + posData.posOut.y = self.config.animation.nudge ? 0 : posData.posIn.y; + posData.tweenData.x = posData.posOut.x - posData.posIn.x; + posData.tweenData.y = posData.posOut.y - posData.posIn.y; + + // Process width, height, and margins + + if (self.config.animation.animateResizeTargets) { + posData.posIn.width = posData.startPosData.width; + posData.posIn.height = posData.startPosData.height; + + widthChange = posData.startPosData.width - posData.interPosData.width; + + posData.posIn.marginRight = posData.startPosData.marginRight - widthChange; + + heightChange = posData.startPosData.height - posData.interPosData.height; + + posData.posIn.marginBottom = posData.startPosData.marginBottom - heightChange; + } + + // Process opacity + + posData.posIn.opacity = 1; + posData.posOut.opacity = self.effectsOut.opacity; + posData.tweenData.opacity = posData.posOut.opacity - posData.posIn.opacity; + + // Process transforms + + for (j = 0; effectName = effectNames[j]; j++) { + effect = self.effectsOut[effectName]; + + if (!(effect instanceof mixitup.TransformData) || !effect.value) continue; + + posData.posIn[effectName].value = 0; + posData.posOut[effectName].value = effect.value; + + posData.tweenData[effectName].value = + posData.posOut[effectName].value - posData.posIn[effectName].value; + + posData.posIn[effectName].unit = + posData.posOut[effectName].unit = + posData.tweenData[effectName].unit = + effect.unit; + } + } + + self.callActions('afterGetTweenData', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Operation} operation + * @return {void} + */ + + moveTargets: function(operation) { + var self = this, + target = null, + moveData = null, + posData = null, + statusChange = '', + willTransition = false, + staggerIndex = -1, + i = -1, + checkProgress = self.checkProgress.bind(self); + + self.callActions('beforeMoveTargets', arguments); + + // TODO: this is an extra loop in addition to the calcs + // done in getOperation, could some of this be done there? + + for (i = 0; target = operation.show[i]; i++) { + moveData = new mixitup.IMoveData(); + posData = operation.showPosData[i]; + + statusChange = target.isShown ? 'none' : 'show'; + + willTransition = self.willTransition( + statusChange, + operation.hasEffect, + posData.posIn, + posData.posOut + ); + + if (willTransition) { + // Prevent non-transitioning targets from incrementing the staggerIndex + + staggerIndex++; + } + + target.show(); + + moveData.posIn = posData.posIn; + moveData.posOut = posData.posOut; + moveData.statusChange = statusChange; + moveData.staggerIndex = staggerIndex; + moveData.operation = operation; + moveData.callback = willTransition ? checkProgress : null; + + target.move(moveData); + } + + for (i = 0; target = operation.toHide[i]; i++) { + posData = operation.toHidePosData[i]; + moveData = new mixitup.IMoveData(); + + statusChange = 'hide'; + + willTransition = self.willTransition(statusChange, posData.posIn, posData.posOut); + + moveData.posIn = posData.posIn; + moveData.posOut = posData.posOut; + moveData.statusChange = statusChange; + moveData.staggerIndex = i; + moveData.operation = operation; + moveData.callback = willTransition ? checkProgress : null; + + target.move(moveData); + } + + if (self.config.animation.animateResizeContainer) { + self.dom.parent.style[mixitup.features.transitionProp] = + 'height ' + self.config.animation.duration + 'ms ease, ' + + 'width ' + self.config.animation.duration + 'ms ease '; + + requestAnimationFrame(function() { + if ( + operation.startHeight !== operation.newHeight && + operation.viewportDeltaY !== operation.startHeight - operation.newHeight + ) { + self.dom.parent.style.height = operation.newHeight + 'px'; + } + + if ( + operation.startWidth !== operation.newWidth && + operation.viewportDeltaX !== operation.startWidth - operation.newWidth + ) { + self.dom.parent.style.width = operation.newWidth + 'px'; + } + }); + } + + if (operation.willChangeLayout) { + h.removeClass(self.dom.container, self.config.layout.ContainerClassName); + h.addClass(self.dom.container, operation.newContainerClassName); + } + + self.callActions('afterMoveTargets', arguments); + }, + + /** + * @private + * @instance + * @return {boolean} + */ + + hasEffect: function() { + var self = this, + EFFECTABLES = [ + 'scale', + 'translateX', 'translateY', 'translateZ', + 'rotateX', 'rotateY', 'rotateZ' + ], + effectName = '', + effect = null, + result = false, + value = -1, + i = -1; + + if (self.effectsIn.opacity !== 1) { + return self.callFilters('resultHasEffect', true, arguments); + } + + for (i = 0; effectName = EFFECTABLES[i]; i++) { + effect = self.effectsIn[effectName]; + value = (typeof effect && effect.value !== 'undefined') ? + effect.value : effect; + + if (value !== 0) { + result = true; + + break; + } + } + + return self.callFilters('resultHasEffect', result, arguments); + }, + + /** + * Determines if a target element will transition in + * some fasion and therefore requires binding of + * transitionEnd + * + * @private + * @instance + * @since 3.0.0 + * @param {string} statusChange + * @param {boolean} hasEffect + * @param {StyleData} posIn + * @param {StyleData} posOut + * @return {boolean} + */ + + willTransition: function(statusChange, hasEffect, posIn, posOut) { + var self = this, + result = false; + + if (!h.isVisible(self.dom.container)) { + // If the container is not visible, the transitionEnd + // event will not occur and MixItUp will hang + + result = false; + } else if ( + (statusChange !== 'none' && hasEffect) || + posIn.x !== posOut.x || + posIn.y !== posOut.y + ) { + // If opacity and/or translate will change + + result = true; + } else if (self.config.animation.animateResizeTargets) { + // Check if width, height or margins will change + + result = ( + posIn.width !== posOut.width || + posIn.height !== posOut.height || + posIn.marginRight !== posOut.marginRight || + posIn.marginTop !== posOut.marginTop + ); + } else { + result = false; + } + + return self.callFilters('resultWillTransition', result, arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + checkProgress: function(operation) { + var self = this; + + self.targetsDone++; + + if (self.targetsBound === self.targetsDone) { + self.cleanUp(operation); + } + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + cleanUp: function(operation) { + var self = this, + target = null, + whitespaceBefore = null, + whitespaceAfter = null, + nextInQueue = null, + i = -1; + + self.callActions('beforeCleanUp', arguments); + + self.targetsMoved = + self.targetsImmovable = + self.targetsBound = + self.targetsDone = 0; + + for (i = 0; target = operation.show[i]; i++) { + target.cleanUp(); + + target.show(); + } + + for (i = 0; target = operation.toHide[i]; i++) { + target.cleanUp(); + + target.hide(); + } + + if (operation.willSort) { + self.printSort(false, operation); + } + + // Remove any styles applied to the parent container + + self.dom.parent.style[mixitup.features.transitionProp] = + self.dom.parent.style.height = + self.dom.parent.style.width = + self.dom.parent.style.overflow = + self.dom.parent.style[mixitup.features.perspectiveProp] = + self.dom.parent.style[mixitup.features.perspectiveOriginProp] = ''; + + if (operation.willChangeLayout) { + h.removeClass(self.dom.container, operation.startContainerClassName); + h.addClass(self.dom.container, operation.newContainerClassName); + } + + if (operation.toRemove.length) { + for (i = 0; target = self.targets[i]; i++) { + if (operation.toRemove.indexOf(target) > -1) { + if ( + (whitespaceBefore = target.dom.el.previousSibling) && whitespaceBefore.nodeName === '#text' && + (whitespaceAfter = target.dom.el.nextSibling) && whitespaceAfter.nodeName === '#text' + ) { + h.removeWhitespace(whitespaceBefore); + } + + if (!operation.willSort) { + // NB: Sorting will remove targets as a bi-product of `printSort()` + + self.dom.parent.removeChild(target.dom.el); + } + + self.targets.splice(i, 1); + + target.isInDom = false; + + i--; + } + } + + // Since targets have been removed, the original order must be updated + + self.origOrder = self.targets; + } + + if (operation.willSort) { + self.targets = operation.newOrder; + } + + self.state = operation.newState; + self.lastOperation = operation; + + self.dom.targets = self.state.targets; + + // mixEnd + + mixitup.events.fire('mixEnd', self.dom.container, { + state: self.state, + instance: self + }, self.dom.document); + + if (typeof self.config.callbacks.onMixEnd === 'function') { + self.config.callbacks.onMixEnd.call(self.dom.container, self.state, self); + } + + if (operation.hasFailed) { + // mixFail + + mixitup.events.fire('mixFail', self.dom.container, { + state: self.state, + instance: self + }, self.dom.document); + + if (typeof self.config.callbacks.onMixFail === 'function') { + self.config.callbacks.onMixFail.call(self.dom.container, self.state, self); + } + + h.addClass(self.dom.container, h.getClassname(self.config.classNames, 'container', self.config.classNames.modifierFailed)); + } + + // User-defined callback function + + if (typeof self.userCallback === 'function') { + self.userCallback.call(self.dom.container, self.state, self); + } + + if (typeof self.userDeferred.resolve === 'function') { + self.userDeferred.resolve(self.state); + } + + self.userCallback = null; + self.userDeferred = null; + self.lastClicked = null; + self.isToggling = false; + self.isBusy = false; + + if (self.queue.length) { + self.callActions('beforeReadQueueCleanUp', arguments); + + nextInQueue = self.queue.shift(); + + // Update non-public API properties stored in queue + + self.userDeferred = nextInQueue.deferred; + self.isToggling = nextInQueue.isToggling; + self.lastClicked = nextInQueue.triggerElement; + + if (nextInQueue.instruction.command instanceof mixitup.CommandMultimix) { + self.multimix.apply(self, nextInQueue.args); + } else { + self.dataset.apply(self, nextInQueue.args); + } + } + + self.callActions('afterCleanUp', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Array<*>} args + * @return {mixitup.UserInstruction} + */ + + parseMultimixArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + arg = null, + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandMultimix(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (arg === null) continue; + + if (typeof arg === 'object') { + h.extend(instruction.command, arg); + } else if (typeof arg === 'boolean') { + instruction.animate = arg; + } else if (typeof arg === 'function') { + instruction.callback = arg; + } + } + + // Coerce arbitrary command arguments into typed command objects + + if (instruction.command.insert && !(instruction.command.insert instanceof mixitup.CommandInsert)) { + instruction.command.insert = self.parseInsertArgs([instruction.command.insert]).command; + } + + if (instruction.command.remove && !(instruction.command.remove instanceof mixitup.CommandRemove)) { + instruction.command.remove = self.parseRemoveArgs([instruction.command.remove]).command; + } + + if (instruction.command.filter && !(instruction.command.filter instanceof mixitup.CommandFilter)) { + instruction.command.filter = self.parseFilterArgs([instruction.command.filter]).command; + } + + if (instruction.command.sort && !(instruction.command.sort instanceof mixitup.CommandSort)) { + instruction.command.sort = self.parseSortArgs([instruction.command.sort]).command; + } + + if (instruction.command.changeLayout && !(instruction.command.changeLayout instanceof mixitup.CommandChangeLayout)) { + instruction.command.changeLayout = self.parseChangeLayoutArgs([instruction.command.changeLayout]).command; + } + + instruction = self.callFilters('instructionParseMultimixArgs', instruction, arguments); + + h.freeze(instruction); + + return instruction; + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Array<*>} args + * @return {mixitup.UserInstruction} + */ + + parseFilterArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + arg = null, + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandFilter(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (typeof arg === 'string') { + // Selector + + instruction.command.selector = arg; + } else if (arg === null) { + instruction.command.collection = []; + } else if (typeof arg === 'object' && h.isElement(arg, self.dom.document)) { + // Single element + + instruction.command.collection = [arg]; + } else if (typeof arg === 'object' && typeof arg.length !== 'undefined') { + // Multiple elements in array, NodeList or jQuery collection + + instruction.command.collection = h.arrayFromList(arg); + } else if (typeof arg === 'object') { + // Filter command + + h.extend(instruction.command, arg); + } else if (typeof arg === 'boolean') { + instruction.animate = arg; + } else if (typeof arg === 'function') { + instruction.callback = arg; + } + } + + if (instruction.command.selector && instruction.command.collection) { + throw new Error(mixitup.messages.errorFilterInvalidArguments()); + } + + instruction = self.callFilters('instructionParseFilterArgs', instruction, arguments); + + h.freeze(instruction); + + return instruction; + }, + + parseSortArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + arg = null, + sortString = '', + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandSort(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (arg === null) continue; + + switch (typeof arg) { + case 'string': + // Sort string + + sortString = arg; + + break; + case 'object': + // Array of element references + + if (arg.length) { + instruction.command.collection = h.arrayFromList(arg); + } + + break; + case 'boolean': + instruction.animate = arg; + + break; + case 'function': + instruction.callback = arg; + + break; + } + } + + if (sortString) { + instruction.command = self.parseSortString(sortString, instruction.command); + } + + instruction = self.callFilters('instructionParseSortArgs', instruction, arguments); + + h.freeze(instruction); + + return instruction; + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Array<*>} args + * @return {mixitup.UserInstruction} + */ + + parseInsertArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + arg = null, + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandInsert(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (arg === null) continue; + + if (typeof arg === 'number') { + // Insert index + + instruction.command.index = arg; + } else if (typeof arg === 'string' && ['before', 'after'].indexOf(arg) > -1) { + // 'before'/'after' + + instruction.command.position = arg; + } else if (typeof arg === 'string') { + // Markup + + instruction.command.collection = + h.arrayFromList(h.createElement(arg).childNodes); + } else if (typeof arg === 'object' && h.isElement(arg, self.dom.document)) { + // Single element + + !instruction.command.collection.length ? + (instruction.command.collection = [arg]) : + (instruction.command.sibling = arg); + } else if (typeof arg === 'object' && arg.length) { + // Multiple elements in array or jQuery collection + + !instruction.command.collection.length ? + (instruction.command.collection = arg) : + instruction.command.sibling = arg[0]; + } else if (typeof arg === 'object' && arg.childNodes && arg.childNodes.length) { + // Document fragment + + !instruction.command.collection.length ? + instruction.command.collection = h.arrayFromList(arg.childNodes) : + instruction.command.sibling = arg.childNodes[0]; + } else if (typeof arg === 'object') { + // Insert command + + h.extend(instruction.command, arg); + } else if (typeof arg === 'boolean') { + instruction.animate = arg; + } else if (typeof arg === 'function') { + instruction.callback = arg; + } + } + + if (instruction.command.index && instruction.command.sibling) { + throw new Error(mixitup.messages.errorInsertInvalidArguments()); + } + + if (!instruction.command.collection.length && self.config.debug.showWarnings) { + console.warn(mixitup.messages.warningInsertNoElements()); + } + + instruction = self.callFilters('instructionParseInsertArgs', instruction, arguments); + + h.freeze(instruction); + + return instruction; + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Array<*>} args + * @return {mixitup.UserInstruction} + */ + + parseRemoveArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + target = null, + arg = null, + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandRemove(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (arg === null) continue; + + switch (typeof arg) { + case 'number': + if (self.targets[arg]) { + instruction.command.targets[0] = self.targets[arg]; + } + + break; + case 'string': + instruction.command.collection = h.arrayFromList(self.dom.parent.querySelectorAll(arg)); + + break; + case 'object': + if (arg && arg.length) { + instruction.command.collection = arg; + } else if (h.isElement(arg, self.dom.document)) { + instruction.command.collection = [arg]; + } else { + // Remove command + + h.extend(instruction.command, arg); + } + + break; + case 'boolean': + instruction.animate = arg; + + break; + case 'function': + instruction.callback = arg; + + break; + } + } + + if (instruction.command.collection.length) { + for (i = 0; target = self.targets[i]; i++) { + if (instruction.command.collection.indexOf(target.dom.el) > -1) { + instruction.command.targets.push(target); + } + } + } + + if (!instruction.command.targets.length && self.config.debug.showWarnings) { + console.warn(mixitup.messages.warningRemoveNoElements()); + } + + h.freeze(instruction); + + return instruction; + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Array<*>} args + * @return {mixitup.UserInstruction} + */ + + parseDatasetArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + arg = null, + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandDataset(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (arg === null) continue; + + switch (typeof arg) { + case 'object': + if (Array.isArray(arg) || typeof arg.length === 'number') { + instruction.command.dataset = arg; + } else { + // Change layout command + + h.extend(instruction.command, arg); + } + + break; + case 'boolean': + instruction.animate = arg; + + break; + case 'function': + instruction.callback = arg; + + break; + } + } + + h.freeze(instruction); + + return instruction; + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Array<*>} args + * @return {mixitup.UserInstruction} + */ + + parseChangeLayoutArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + arg = null, + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandChangeLayout(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (arg === null) continue; + + switch (typeof arg) { + case 'string': + instruction.command.containerClassName = arg; + + break; + case 'object': + // Change layout command + + h.extend(instruction.command, arg); + + break; + case 'boolean': + instruction.animate = arg; + + break; + case 'function': + instruction.callback = arg; + + break; + } + } + + h.freeze(instruction); + + return instruction; + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {mixitup.QueueItem} queueItem + * @return {Promise.} + */ + + queueMix: function(queueItem) { + var self = this, + deferred = null, + toggleSelector = ''; + + self.callActions('beforeQueueMix', arguments); + + deferred = h.defer(mixitup.libraries); + + if (self.config.animation.queue && self.queue.length < self.config.animation.queueLimit) { + queueItem.deferred = deferred; + + self.queue.push(queueItem); + + // Keep controls in sync with user interactions. Mixer will catch up as it drains the queue. + + if (self.config.controls.enable) { + if (self.isToggling) { + self.buildToggleArray(queueItem.instruction.command); + + toggleSelector = self.getToggleSelector(); + + self.updateControls({ + filter: { + selector: toggleSelector + } + }); + } else { + self.updateControls(queueItem.instruction.command); + } + } + } else { + if (self.config.debug.showWarnings) { + console.warn(mixitup.messages.warningMultimixInstanceQueueFull()); + } + + deferred.resolve(self.state); + + mixitup.events.fire('mixBusy', self.dom.container, { + state: self.state, + instance: self + }, self.dom.document); + + if (typeof self.config.callbacks.onMixBusy === 'function') { + self.config.callbacks.onMixBusy.call(self.dom.container, self.state, self); + } + } + + return self.callFilters('promiseQueueMix', deferred.promise, arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Array.} newDataset + * @return {Operation} + */ + + getDataOperation: function(newDataset) { + var self = this, + operation = new mixitup.Operation(), + startDataset = []; + + operation = self.callFilters('operationUnmappedGetDataOperation', operation, arguments); + + if (self.dom.targets.length && !(startDataset = (self.state.activeDataset || [])).length) { + throw new Error(mixitup.messages.errorDatasetNotSet()); + } + + operation.id = h.randomHex(); + operation.startState = self.state; + operation.startDataset = startDataset; + operation.newDataset = newDataset.slice(); + + self.diffDatasets(operation); + + operation.startOrder = self.targets; + operation.newOrder = operation.show; + + if (self.config.animation.enable) { + self.getStartMixData(operation); + self.setInter(operation); + + operation.docState = h.getDocumentState(self.dom.document); + + self.getInterMixData(operation); + self.setFinal(operation); + self.getFinalMixData(operation); + + self.parseEffects(); + + operation.hasEffect = self.hasEffect(); + + self.getTweenData(operation); + } + + self.targets = operation.show.slice(); + + operation.newState = self.buildState(operation); + + // NB: Targets to be removed must be included in `self.targets` for removal during clean up, + // but are added after state is built so that state is accurate + + Array.prototype.push.apply(self.targets, operation.toRemove); + + operation = self.callFilters('operationMappedGetDataOperation', operation, arguments); + + return operation; + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {mixitup.Operation} operation + * @return {void} + */ + + diffDatasets: function(operation) { + var self = this, + persistantStartIds = [], + persistantNewIds = [], + insertedTargets = [], + data = null, + target = null, + el = null, + frag = null, + nextEl = null, + uids = {}, + id = '', + i = -1; + + self.callActions('beforeDiffDatasets', arguments); + + for (i = 0; data = operation.newDataset[i]; i++) { + if (typeof (id = data[self.config.data.uidKey]) === 'undefined' || id.toString().length < 1) { + throw new TypeError(mixitup.messages.errorDatasetInvalidUidKey({ + uidKey: self.config.data.uidKey + })); + } + + if (!uids[id]) { + uids[id] = true; + } else { + throw new Error(mixitup.messages.errorDatasetDuplicateUid({ + uid: id + })); + } + + if ((target = self.cache[id]) instanceof mixitup.Target) { + // Already in cache + + if (self.config.data.dirtyCheck && !h.deepEquals(data, target.data)) { + // change detected + + el = target.render(data); + + target.data = data; + + if (el !== target.dom.el) { + // Update target element reference + + if (target.isInDom) { + target.unbindEvents(); + + self.dom.parent.replaceChild(el, target.dom.el); + } + + if (!target.isShown) { + el.style.display = 'none'; + } + + target.dom.el = el; + + if (target.isInDom) { + target.bindEvents(); + } + } + } + + el = target.dom.el; + } else { + // New target + + target = new mixitup.Target(); + + target.init(null, self, data); + + target.hide(); + } + + if (!target.isInDom) { + // Adding to DOM + + if (!frag) { + // Open frag + + frag = self.dom.document.createDocumentFragment(); + } + + if (frag.lastElementChild) { + frag.appendChild(self.dom.document.createTextNode(' ')); + } + + frag.appendChild(target.dom.el); + + target.isInDom = true; + + target.unbindEvents(); + target.bindEvents(); + target.hide(); + + operation.toShow.push(target); + + insertedTargets.push(target); + } else { + // Already in DOM + + nextEl = target.dom.el.nextElementSibling; + + persistantNewIds.push(id); + + if (frag) { + // Close and insert previously opened frag + + if (frag.lastElementChild) { + frag.appendChild(self.dom.document.createTextNode(' ')); + } + + self.insertDatasetFrag(frag, target.dom.el, insertedTargets); + + frag = null; + } + } + + operation.show.push(target); + } + + if (frag) { + // Unclosed frag remaining + + nextEl = nextEl || self.config.layout.siblingAfter; + + if (nextEl) { + frag.appendChild(self.dom.document.createTextNode(' ')); + } + + self.insertDatasetFrag(frag, nextEl, insertedTargets); + } + + for (i = 0; data = operation.startDataset[i]; i++) { + id = data[self.config.data.uidKey]; + + target = self.cache[id]; + + if (operation.show.indexOf(target) < 0) { + // Previously shown but now absent + + operation.hide.push(target); + operation.toHide.push(target); + operation.toRemove.push(target); + } else { + persistantStartIds.push(id); + } + } + + if (!h.isEqualArray(persistantStartIds, persistantNewIds)) { + operation.willSort = true; + } + + self.callActions('afterDiffDatasets', arguments); + }, + + /** + * @private + * @instance + * @since 3.1.5 + * @param {DocumentFragment} frag + * @param {(HTMLElement|null)} nextEl + * @param {Array.} targets + * @return {void} + */ + + insertDatasetFrag: function(frag, nextEl, targets) { + var self = this; + var insertAt = nextEl ? h.arrayFromList(self.dom.parent.children).indexOf(nextEl) : self.targets.length; + + self.dom.parent.insertBefore(frag, nextEl); + + while (targets.length) { + self.targets.splice(insertAt, 0, targets.shift()); + + insertAt++; + } + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {mixitup.CommandSort} sortCommandA + * @param {mixitup.CommandSort} sortCommandB + * @return {boolean} + */ + + willSort: function(sortCommandA, sortCommandB) { + var self = this, + result = false; + + if ( + self.config.behavior.liveSort || + sortCommandA.order === 'random' || + sortCommandA.attribute !== sortCommandB.attribute || + sortCommandA.order !== sortCommandB.order || + sortCommandA.collection !== sortCommandB.collection || + (sortCommandA.next === null && sortCommandB.next) || + (sortCommandA.next && sortCommandB.next === null) + ) { + result = true; + } else if (sortCommandA.next && sortCommandB.next) { + result = self.willSort(sortCommandA.next, sortCommandB.next); + } else { + result = false; + } + + return self.callFilters('resultWillSort', result, arguments); + }, + + /** + * A shorthand method for `.filter('all')`. Shows all targets in the container. + * + * @example + * + * .show() + * + * @example Example: Showing all targets + * + * mixer.show() + * .then(function(state) { + * console.log(state.totalShow === state.totalTargets); // true + * }); + * + * @public + * @instance + * @since 3.0.0 + * @return {Promise.} + */ + + show: function() { + var self = this; + + return self.filter('all'); + }, + + /** + * A shorthand method for `.filter('none')`. Hides all targets in the container. + * + * @example + * + * .hide() + * + * @example Example: Hiding all targets + * + * mixer.hide() + * .then(function(state) { + * console.log(state.totalShow === 0); // true + * console.log(state.totalHide === state.totalTargets); // true + * }); + * + * @public + * @instance + * @since 3.0.0 + * @return {Promise.} + */ + + hide: function() { + var self = this; + + return self.filter('none'); + }, + + /** + * Returns a boolean indicating whether or not a MixItUp operation is + * currently in progress. + * + * @example + * + * .isMixing() + * + * @example Example: Checking the status of a mixer + * + * mixer.sort('random', function() { + * console.log(mixer.isMixing()) // false + * }); + * + * console.log(mixer.isMixing()) // true + * + * @public + * @instance + * @since 2.0.0 + * @return {boolean} + */ + + isMixing: function() { + var self = this; + + return self.isBusy; + }, + + /** + * Filters all targets in the container by a provided selector string, or the values `'all'` + * or `'none'`. Only targets matching the selector will be shown. + * + * @example + * + * .filter(selector [, animate] [, callback]) + * + * @example Example 1: Filtering targets by a class selector + * + * mixer.filter('.category-a') + * .then(function(state) { + * console.log(state.totalShow === containerEl.querySelectorAll('.category-a').length); // true + * }); + * + * @example Example 2: Filtering targets by an attribute selector + * + * mixer.filter('[data-category~="a"]') + * .then(function(state) { + * console.log(state.totalShow === containerEl.querySelectorAll('[data-category~="a"]').length); // true + * }); + * + * @example Example 3: Filtering targets by a compound selector + * + * // Show only those targets with the classes 'category-a' AND 'category-b' + * + * mixer.filter('.category-a.category-c') + * .then(function(state) { + * console.log(state.totalShow === containerEl.querySelectorAll('.category-a.category-c').length); // true + * }); + * + * @example Example 4: Filtering via an element collection + * + * var collection = Array.from(container.querySelectorAll('.mix')); + * + * console.log(collection.length); // 34 + * + * // Filter the collection manually using Array.prototype.filter + * + * var filtered = collection.filter(function(target) { + * return parseInt(target.getAttribute('data-price')) > 10; + * }); + * + * console.log(filtered.length); // 22 + * + * // Pass the filtered collection to MixItUp + * + * mixer.filter(filtered) + * .then(function(state) { + * console.log(state.activeFilter.collection.length === 22); // true + * }); + * + * @public + * @instance + * @since 2.0.0 + * @param {(string|HTMLElement|Array.)} selector + * Any valid CSS selector (i.e. `'.category-a'`), or the values `'all'` or `'none'`. The filter method also accepts a reference to single target element or a collection of target elements to show. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + filter: function() { + var self = this, + instruction = self.parseFilterArgs(arguments); + + return self.multimix({ + filter: instruction.command + }, instruction.animate, instruction.callback); + }, + + /** + * Adds an additional selector to the currently active filter selector, concatenating + * as per the logic defined in `controls.toggleLogic`. + * + * @example + * + * .toggleOn(selector [, animate] [, callback]) + * + * @example Example: Toggling on a filter selector + * + * console.log(mixer.getState().activeFilter.selector); // '.category-a' + * + * mixer.toggleOn('.category-b') + * .then(function(state) { + * console.log(state.activeFilter.selector); // '.category-a, .category-b' + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {string} selector + * Any valid CSS selector (i.e. `'.category-a'`) + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + toggleOn: function() { + var self = this, + instruction = self.parseFilterArgs(arguments), + selector = instruction.command.selector, + toggleSelector = ''; + + self.isToggling = true; + + if (self.toggleArray.indexOf(selector) < 0) { + self.toggleArray.push(selector); + } + + toggleSelector = self.getToggleSelector(); + + return self.multimix({ + filter: toggleSelector + }, instruction.animate, instruction.callback); + }, + + /** + * Removes a selector from the active filter selector. + * + * @example + * + * .toggleOff(selector [, animate] [, callback]) + * + * @example Example: Toggling off a filter selector + * + * console.log(mixer.getState().activeFilter.selector); // '.category-a, .category-b' + * + * mixer.toggleOff('.category-b') + * .then(function(state) { + * console.log(state.activeFilter.selector); // '.category-a' + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {string} selector + * Any valid CSS selector (i.e. `'.category-a'`) + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + toggleOff: function() { + var self = this, + instruction = self.parseFilterArgs(arguments), + selector = instruction.command.selector, + selectorIndex = self.toggleArray.indexOf(selector), + toggleSelector = ''; + + self.isToggling = true; + + if (selectorIndex > -1) { + self.toggleArray.splice(selectorIndex, 1); + } + + toggleSelector = self.getToggleSelector(); + + return self.multimix({ + filter: toggleSelector + }, instruction.animate, instruction.callback); + }, + + /** + * Sorts all targets in the container according to a provided sort string. + * + * @example + * + * .sort(sortString [, animate] [, callback]) + * + * @example Example 1: Sorting by the default DOM order + * + * // Reverse the default order of the targets + * + * mixer.sort('default:desc') + * .then(function(state) { + * console.log(state.activeSort.attribute === 'default'); // true + * console.log(state.activeSort.order === 'desc'); // true + * }); + * + * @example Example 2: Sorting by a custom data-attribute + * + * // Sort the targets by the value of a `data-published-date` attribute + * + * mixer.sort('published-date:asc') + * .then(function(state) { + * console.log(state.activeSort.attribute === 'published-date'); // true + * console.log(state.activeSort.order === 'asc'); // true + * }); + * + * @example Example 3: Sorting by multiple attributes + * + * // Sort the targets by the value of a `data-published-date` attribute, then by `data-title` + * + * mixer.sort('published-date:desc data-title:asc') + * .then(function(state) { + * console.log(state.activeSort.attribute === 'published-date'); // true + * console.log(state.activeSort.order === 'desc'); // true + * + * console.log(state.activeSort.next.attribute === 'title'); // true + * console.log(state.activeSort.next.order === 'asc'); // true + * }); + * + * @example Example 4: Sorting by random + * + * mixer.sort('random') + * .then(function(state) { + * console.log(state.activeSort.order === 'random') // true + * }); + * + * @example Example 5: Sorting via an element collection + * + * var collection = Array.from(container.querySelectorAll('.mix')); + * + * // Swap the position of two elements in the collection: + * + * var temp = collection[1]; + * + * collection[1] = collection[0]; + * collection[0] = temp; + * + * // Pass the sorted collection to MixItUp + * + * mixer.sort(collection) + * .then(function(state) { + * console.log(state.targets[0] === collection[0]); // true + * }); + * + * @public + * @instance + * @since 2.0.0 + * @param {(string|Array.)} sortString + * A valid sort string (e.g. `'default'`, `'published-date:asc'`, or `'random'`). The sort method also accepts an array of all target elements in a user-defined order. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + sort: function() { + var self = this, + instruction = self.parseSortArgs(arguments); + + return self.multimix({ + sort: instruction.command + }, instruction.animate, instruction.callback); + }, + + /** + * Changes the layout of the container by adding, removing or updating a + * layout-specific class name. If `animation.animateResizetargets` is + * enabled, MixItUp will attempt to gracefully animate the width, height, + * and position of targets between layout states. + * + * @example + * + * .changeLayout(containerClassName [, animate] [, callback]) + * + * @example Example 1: Adding a new class name to the container + * + * mixer.changeLayout('container-list') + * .then(function(state) { + * console.log(state.activeContainerClass === 'container-list'); // true + * }); + * + * @example Example 2: Removing a previously added class name from the container + * + * mixer.changeLayout('') + * .then(function(state) { + * console.log(state.activeContainerClass === ''); // true + * }); + * + * @public + * @instance + * @since 2.0.0 + * @param {string} containerClassName + * A layout-specific class name to add to the container. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + changeLayout: function() { + var self = this, + instruction = self.parseChangeLayoutArgs(arguments); + + return self.multimix({ + changeLayout: instruction.command + }, instruction.animate, instruction.callback); + }, + + /** + * Updates the contents and order of the container to reflect the provided dataset, + * if the dataset API is in use. + * + * The dataset API is designed for use in API-driven JavaScript applications, and + * can be used instead of DOM-based methods such as `.filter()`, `.sort()`, + * `.insert()`, etc. When used, insertion, removal, sorting and pagination can be + * achieved purely via changes to your data model, without the uglyness of having + * to interact with or query the DOM directly. + * + * @example + * + * .dataset(dataset [, animate] [, callback]) + * + * @example Example 1: Rendering a dataset + * + * var myDataset = [ + * {id: 1, ...}, + * {id: 2, ...}, + * {id: 3, ...} + * ]; + * + * mixer.dataset(myDataset) + * .then(function(state) { + * console.log(state.totalShow === 3); // true + * }); + * + * @example Example 2: Sorting a dataset + * + * // Create a new dataset in reverse order + * + * var newDataset = myDataset.slice().reverse(); + * + * mixer.dataset(newDataset) + * .then(function(state) { + * console.log(state.activeDataset[0] === myDataset[2]); // true + * }); + * + * @example Example 3: Removing an item from the dataset + * + * console.log(myDataset.length); // 3 + * + * // Create a new dataset with the last item removed. + * + * var newDataset = myDataset.slice().pop(); + * + * mixer.dataset(newDataset) + * .then(function(state) { + * console.log(state.totalShow === 2); // true + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {Array.} dataset + * An array of objects, each one representing the underlying data model of a target to be rendered. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + dataset: function() { + var self = this, + instruction = self.parseDatasetArgs(arguments), + operation = null, + queueItem = null, + animate = false; + + self.callActions('beforeDataset', arguments); + + if (!self.isBusy) { + if (instruction.callback) self.userCallback = instruction.callback; + + animate = (instruction.animate ^ self.config.animation.enable) ? instruction.animate : self.config.animation.enable; + + operation = self.getDataOperation(instruction.command.dataset); + + return self.goMix(animate, operation); + } else { + queueItem = new mixitup.QueueItem(); + + queueItem.args = arguments; + queueItem.instruction = instruction; + + return self.queueMix(queueItem); + } + }, + + /** + * Performs simultaneous `filter`, `sort`, `insert`, `remove` and `changeLayout` + * operations as requested. + * + * @example + * + * .multimix(multimixCommand [, animate] [, callback]) + * + * @example Example 1: Performing simultaneous filtering and sorting + * + * mixer.multimix({ + * filter: '.category-b', + * sort: 'published-date:desc' + * }) + * .then(function(state) { + * console.log(state.activeFilter.selector === '.category-b'); // true + * console.log(state.activeSort.attribute === 'published-date'); // true + * }); + * + * @example Example 2: Performing simultaneous sorting, insertion, and removal + * + * console.log(mixer.getState().totalShow); // 6 + * + * // NB: When inserting via `multimix()`, an object should be provided as the value + * // for the `insert` portion of the command, allowing for a collection of elements + * // and an insertion index to be specified. + * + * mixer.multimix({ + * sort: 'published-date:desc', // Sort the container, including any new elements + * insert: { + * collection: [newElementReferenceA, newElementReferenceB], // Add 2 new elements at index 5 + * index: 5 + * }, + * remove: existingElementReference // Remove 1 existing element + * }) + * .then(function(state) { + * console.log(state.activeSort.attribute === 'published-date'); // true + * console.log(state.totalShow === 7); // true + * }); + * + * @public + * @instance + * @since 2.0.0 + * @param {object} multimixCommand + * An object containing one or more things to do + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + multimix: function() { + var self = this, + operation = null, + animate = false, + queueItem = null, + instruction = self.parseMultimixArgs(arguments); + + self.callActions('beforeMultimix', arguments); + + if (!self.isBusy) { + operation = self.getOperation(instruction.command); + + if (self.config.controls.enable) { + // Update controls for API calls + + if (instruction.command.filter && !self.isToggling) { + // As we are not toggling, reset the toggle array + // so new filter overrides existing toggles + + self.toggleArray.length = 0; + self.buildToggleArray(operation.command); + } + + if (self.queue.length < 1) { + self.updateControls(operation.command); + } + } + + if (instruction.callback) self.userCallback = instruction.callback; + + // Always allow the instruction to override the instance setting + + animate = (instruction.animate ^ self.config.animation.enable) ? + instruction.animate : + self.config.animation.enable; + + self.callFilters('operationMultimix', operation, arguments); + + return self.goMix(animate, operation); + } else { + queueItem = new mixitup.QueueItem(); + + queueItem.args = arguments; + queueItem.instruction = instruction; + queueItem.triggerElement = self.lastClicked; + queueItem.isToggling = self.isToggling; + + return self.queueMix(queueItem); + } + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {object} multimixCommand + * @param {boolean} [isPreFetch] + * An optional boolean indicating that the operation is being pre-fetched for execution at a later time. + * @return {Operation|null} + */ + + getOperation: function(multimixCommand) { + var self = this, + sortCommand = multimixCommand.sort, + filterCommand = multimixCommand.filter, + changeLayoutCommand = multimixCommand.changeLayout, + removeCommand = multimixCommand.remove, + insertCommand = multimixCommand.insert, + operation = new mixitup.Operation(); + + operation = self.callFilters('operationUnmappedGetOperation', operation, arguments); + + operation.id = h.randomHex(); + operation.command = multimixCommand; + operation.startState = self.state; + operation.triggerElement = self.lastClicked; + + if (self.isBusy) { + if (self.config.debug.showWarnings) { + console.warn(mixitup.messages.warningGetOperationInstanceBusy()); + } + + return null; + } + + if (insertCommand) { + self.insertTargets(insertCommand, operation); + } + + if (removeCommand) { + operation.toRemove = removeCommand.targets; + } + + operation.startSort = operation.newSort = operation.startState.activeSort; + operation.startOrder = operation.newOrder = self.targets; + + if (sortCommand) { + operation.startSort = operation.startState.activeSort; + operation.newSort = sortCommand; + + operation.willSort = self.willSort(sortCommand, operation.startState.activeSort); + + if (operation.willSort) { + self.sortOperation(operation); + } + } + + operation.startFilter = operation.startState.activeFilter; + + if (filterCommand) { + operation.newFilter = filterCommand; + } else { + operation.newFilter = h.extend(new mixitup.CommandFilter(), operation.startFilter); + } + + if (operation.newFilter.selector === 'all') { + operation.newFilter.selector = self.config.selectors.target; + } else if (operation.newFilter.selector === 'none') { + operation.newFilter.selector = ''; + } + + self.filterOperation(operation); + + operation.startContainerClassName = operation.startState.activeContainerClassName; + + if (changeLayoutCommand) { + operation.newContainerClassName = changeLayoutCommand.containerClassName; + + if (operation.newContainerClassName !== operation.startContainerClassName) { + operation.willChangeLayout = true; + } + } else { + operation.newContainerClassName = operation.startContainerClassName; + } + + if (self.config.animation.enable) { + // Populate the operation's position data + + self.getStartMixData(operation); + self.setInter(operation); + + operation.docState = h.getDocumentState(self.dom.document); + + self.getInterMixData(operation); + self.setFinal(operation); + self.getFinalMixData(operation); + + self.parseEffects(); + + operation.hasEffect = self.hasEffect(); + + self.getTweenData(operation); + } + + if (operation.willSort) { + self.targets = operation.newOrder; + } + + operation.newState = self.buildState(operation); + + return self.callFilters('operationMappedGetOperation', operation, arguments); + }, + + /** + * Renders a previously created operation at a specific point in its path, as + * determined by a multiplier between 0 and 1. + * + * @example + * .tween(operation, multiplier) + * + * @private + * @instance + * @since 3.0.0 + * @param {mixitup.Operation} operation + * An operation object created via the `getOperation` method + * + * @param {Float} multiplier + * Any number between 0 and 1 representing the percentage complete of the operation + * @return {void} + */ + + tween: function(operation, multiplier) { + var target = null, + posData = null, + toHideIndex = -1, + i = -1; + + multiplier = Math.min(multiplier, 1); + multiplier = Math.max(multiplier, 0); + + for (i = 0; target = operation.show[i]; i++) { + posData = operation.showPosData[i]; + + target.applyTween(posData, multiplier); + } + + for (i = 0; target = operation.hide[i]; i++) { + if (target.isShown) { + target.hide(); + } + + if ((toHideIndex = operation.toHide.indexOf(target)) > -1) { + posData = operation.toHidePosData[toHideIndex]; + + if (!target.isShown) { + target.show(); + } + + target.applyTween(posData, multiplier); + } + } + }, + + /** + * Inserts one or more new target elements into the container at a specified + * index. + * + * To be indexed as targets, new elements must match the `selectors.target` + * selector (`'.mix'` by default). + * + * @example + * + * .insert(newElements [, index] [, animate], [, callback]) + * + * @example Example 1: Inserting a single element via reference + * + * console.log(mixer.getState().totalShow); // 0 + * + * // Create a new element + * + * var newElement = document.createElement('div'); + * newElement.classList.add('mix'); + * + * mixer.insert(newElement) + * .then(function(state) { + * console.log(state.totalShow === 1); // true + * }); + * + * @example Example 2: Inserting a single element via HTML string + * + * console.log(mixer.getState().totalShow); // 1 + * + * // Create a new element via reference + * + * var newElementHtml = '<div class="mix"></div>'; + * + * // Create and insert the new element at index 1 + * + * mixer.insert(newElementHtml, 1) + * .then(function(state) { + * console.log(state.totalShow === 2); // true + * console.log(state.show[1].outerHTML === newElementHtml); // true + * }); + * + * @example Example 3: Inserting multiple elements via reference + * + * console.log(mixer.getState().totalShow); // 2 + * + * // Create an array of new elements to insert. + * + * var newElement1 = document.createElement('div'); + * var newElement2 = document.createElement('div'); + * + * newElement1.classList.add('mix'); + * newElement2.classList.add('mix'); + * + * var newElementsCollection = [newElement1, newElement2]; + * + * // Insert the new elements starting at index 1 + * + * mixer.insert(newElementsCollection, 1) + * .then(function(state) { + * console.log(state.totalShow === 4); // true + * console.log(state.show[1] === newElement1); // true + * console.log(state.show[2] === newElement2); // true + * }); + * + * @example Example 4: Inserting a jQuery collection object containing one or more elements + * + * console.log(mixer.getState().totalShow); // 4 + * + * var $newElement = $('<div class="mix"></div>'); + * + * // Insert the new elements starting at index 3 + * + * mixer.insert($newElement, 3) + * .then(function(state) { + * console.log(state.totalShow === 5); // true + * console.log(state.show[3] === $newElement[0]); // true + * }); + * + * @public + * @instance + * @since 2.0.0 + * @param {(HTMLElement|Array.|string)} newElements + * A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element. + * @param {number} index=0 + * The index at which to insert the new element(s). `0` by default. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + insert: function() { + var self = this, + args = self.parseInsertArgs(arguments); + + return self.multimix({ + insert: args.command + }, args.animate, args.callback); + }, + + /** + * Inserts one or more new elements before a provided reference element. + * + * @example + * + * .insertBefore(newElements, referenceElement [, animate] [, callback]) + * + * @example Example: Inserting a new element before a reference element + * + * // An existing reference element is chosen at index 2 + * + * var referenceElement = mixer.getState().show[2]; + * + * // Create a new element + * + * var newElement = document.createElement('div'); + * newElement.classList.add('mix'); + * + * mixer.insertBefore(newElement, referenceElement) + * .then(function(state) { + * // The new element is inserted into the container at index 2, before the reference element + * + * console.log(state.show[2] === newElement); // true + * + * // The reference element is now at index 3 + * + * console.log(state.show[3] === referenceElement); // true + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {(HTMLElement|Array.|string)} newElements + * A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element. + * @param {HTMLElement} referenceElement + * A reference to an existing element in the container to insert new elements before. + *@param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + insertBefore: function() { + var self = this, + args = self.parseInsertArgs(arguments); + + return self.insert(args.command.collection, 'before', args.command.sibling, args.animate, args.callback); + }, + + /** + * Inserts one or more new elements after a provided reference element. + * + * @example + * + * .insertAfter(newElements, referenceElement [, animate] [, callback]) + * + * @example Example: Inserting a new element after a reference element + * + * // An existing reference element is chosen at index 2 + * + * var referenceElement = mixer.getState().show[2]; + * + * // Create a new element + * + * var newElement = document.createElement('div'); + * newElement.classList.add('mix'); + * + * mixer.insertAfter(newElement, referenceElement) + * .then(function(state) { + * // The new element is inserted into the container at index 3, after the reference element + * + * console.log(state.show[3] === newElement); // true + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {(HTMLElement|Array.|string)} newElements + * A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element. + * @param {HTMLElement} referenceElement + * A reference to an existing element in the container to insert new elements after. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + insertAfter: function() { + var self = this, + args = self.parseInsertArgs(arguments); + + return self.insert(args.command.collection, 'after', args.command.sibling, args.animate, args.callback); + }, + + /** + * Inserts one or more new elements into the container before all existing targets. + * + * @example + * + * .prepend(newElements [,animate] [,callback]) + * + * @example Example: Prepending a new element + * + * // Create a new element + * + * var newElement = document.createElement('div'); + * newElement.classList.add('mix'); + * + * // Insert the element into the container + * + * mixer.prepend(newElement) + * .then(function(state) { + * console.log(state.show[0] === newElement); // true + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {(HTMLElement|Array.|string)} newElements + * A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + prepend: function() { + var self = this, + args = self.parseInsertArgs(arguments); + + return self.insert(0, args.command.collection, args.animate, args.callback); + }, + + /** + * Inserts one or more new elements into the container after all existing targets. + * + * @example + * + * .append(newElements [,animate] [,callback]) + * + * @example Example: Appending a new element + * + * // Create a new element + * + * var newElement = document.createElement('div'); + * newElement.classList.add('mix'); + * + * // Insert the element into the container + * + * mixer.append(newElement) + * .then(function(state) { + * console.log(state.show[state.show.length - 1] === newElement); // true + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {(HTMLElement|Array.|string)} newElements + * A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + append: function() { + var self = this, + args = self.parseInsertArgs(arguments); + + return self.insert(self.state.totalTargets, args.command.collection, args.animate, args.callback); + }, + + /** + * Removes one or more existing target elements from the container. + * + * @example + * + * .remove(elements [, animate] [, callback]) + * + * @example Example 1: Removing an element by reference + * + * var elementToRemove = containerEl.firstElementChild; + * + * mixer.remove(elementToRemove) + * .then(function(state) { + * console.log(state.targets.indexOf(elementToRemove) === -1); // true + * }); + * + * @example Example 2: Removing a collection of elements by reference + * + * var elementsToRemove = containerEl.querySelectorAll('.category-a'); + * + * console.log(elementsToRemove.length) // 3 + * + * mixer.remove(elementsToRemove) + * .then(function() { + * console.log(containerEl.querySelectorAll('.category-a').length); // 0 + * }); + * + * @example Example 3: Removing one or more elements by selector + * + * mixer.remove('.category-a') + * .then(function() { + * console.log(containerEl.querySelectorAll('.category-a').length); // 0 + * }); + * + * @example Example 4: Removing an element by index + * + * console.log(mixer.getState.totalShow); // 4 + * + * // Remove the element at index 3 + * + * mixer.remove(3) + * .then(function(state) { + * console.log(state.totalShow); // 3 + * console.log(state.show[3]); // undefined + * }); + * + * + * @public + * @instance + * @since 3.0.0 + * @param {(HTMLElement|Array.|string|number)} elements + * A reference to a single element to remove, an array-like collection of elements, a selector string, or the index of an element to remove. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + remove: function() { + var self = this, + args = self.parseRemoveArgs(arguments); + + return self.multimix({ + remove: args.command + }, args.animate, args.callback); + }, + + /** + * Retrieves the the value of any property or sub-object within the current + * mixitup configuration, or the whole configuration object. + * + * @example + * + * .getConfig([stringKey]) + * + * @example Example 1: retrieve the entire configuration object + * + * var config = mixer.getConfig(); // Config { ... } + * + * @example Example 2: retrieve a named sub-object of configuration object + * + * var animation = mixer.getConfig('animation'); // ConfigAnimation { ... } + * + * @example Example 3: retrieve a value of configuration object via a dot-notation string key + * + * var effects = mixer.getConfig('animation.effects'); // 'fade scale' + * + * @public + * @instance + * @since 2.0.0 + * @param {string} [stringKey] A "dot-notation" string key + * @return {*} + */ + + getConfig: function(stringKey) { + var self = this, + value = null; + + if (!stringKey) { + value = self.config; + } else { + value = h.getProperty(self.config, stringKey); + } + + return self.callFilters('valueGetConfig', value, arguments); + }, + + /** + * Updates the configuration of the mixer, after it has been instantiated. + * + * See the Configuration Object documentation for a full list of avilable + * configuration options. + * + * @example + * + * .configure(config) + * + * @example Example 1: Updating animation options + * + * mixer.configure({ + * animation: { + * effects: 'fade translateX(-100%)', + * duration: 300 + * } + * }); + * + * @example Example 2: Removing a callback after it has been set + * + * var mixer; + * + * function handleMixEndOnce() { + * // Do something .. + * + * // Then nullify the callback + * + * mixer.configure({ + * callbacks: { + * onMixEnd: null + * } + * }); + * }; + * + * // Instantiate a mixer with a callback defined + * + * mixer = mixitup(containerEl, { + * callbacks: { + * onMixEnd: handleMixEndOnce + * } + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {object} config + * An object containing one of more configuration options. + * @return {void} + */ + + configure: function(config) { + var self = this; + + self.callActions('beforeConfigure', arguments); + + h.extend(self.config, config, true, true); + + self.callActions('afterConfigure', arguments); + }, + + /** + * Returns an object containing information about the current state of the + * mixer. See the State Object documentation for more information. + * + * NB: State objects are immutable and should therefore be regenerated + * after any operation. + * + * @example + * + * .getState(); + * + * @example Example: Retrieving a state object + * + * var state = mixer.getState(); + * + * console.log(state.totalShow + 'targets are currently shown'); + * + * @public + * @instance + * @since 2.0.0 + * @return {mixitup.State} An object reflecting the current state of the mixer. + */ + + getState: function() { + var self = this, + state = null; + + state = new mixitup.State(); + + h.extend(state, self.state); + + h.freeze(state); + + return self.callFilters('stateGetState', state, arguments); + }, + + /** + * Forces the re-indexing all targets within the container. + * + * This should only be used if some other piece of code in your application + * has manipulated the contents of your container, which should be avoided. + * + * If you need to add or remove target elements from the container, use + * the built-in `.insert()` or `.remove()` methods, and MixItUp will keep + * itself up to date. + * + * @example + * + * .forceRefresh() + * + * @example Example: Force refreshing the mixer after external DOM manipulation + * + * console.log(mixer.getState().totalShow); // 3 + * + * // An element is removed from the container via some external DOM manipulation code: + * + * containerEl.removeChild(containerEl.firstElementChild); + * + * // The mixer does not know that the number of targets has changed: + * + * console.log(mixer.getState().totalShow); // 3 + * + * mixer.forceRefresh(); + * + * // After forceRefresh, the mixer is in sync again: + * + * console.log(mixer.getState().totalShow); // 2 + * + * @public + * @instance + * @since 2.1.2 + * @return {void} + */ + + forceRefresh: function() { + var self = this; + + self.indexTargets(); + }, + + /** + * Forces the re-rendering of all targets when using the Dataset API. + * + * By default, targets are only re-rendered when `data.dirtyCheck` is + * enabled, and an item's data has changed when `dataset()` is called. + * + * The `forceRender()` method allows for the re-rendering of all targets + * in response to some arbitrary event, such as the changing of the target + * render function. + * + * Targets are rendered against their existing data. + * + * @example + * + * .forceRender() + * + * @example Example: Force render targets after changing the target render function + * + * console.log(container.innerHTML); // ... <span class="mix">Foo</span> ... + * + * mixer.configure({ + * render: { + * target: (item) => `<a href="/${item.slug}/" class="mix">${item.title}</a>` + * } + * }); + * + * mixer.forceRender(); + * + * console.log(container.innerHTML); // ... <a href="/foo/" class="mix">Foo</a> ... + * + * @public + * @instance + * @since 3.2.1 + * @return {void} + */ + + forceRender: function() { + var self = this, + target = null, + el = null, + id = ''; + + for (id in self.cache) { + target = self.cache[id]; + + el = target.render(target.data); + + if (el !== target.dom.el) { + // Update target element reference + + if (target.isInDom) { + target.unbindEvents(); + + self.dom.parent.replaceChild(el, target.dom.el); + } + + if (!target.isShown) { + el.style.display = 'none'; + } + + target.dom.el = el; + + if (target.isInDom) { + target.bindEvents(); + } + } + } + + self.state = self.buildState(self.lastOperation); + }, + + /** + * Removes mixitup functionality from the container, unbinds all control + * event handlers, and deletes the mixer instance from MixItUp's internal + * cache. + * + * This should be performed whenever a mixer's container is removed from + * the DOM, such as during a page change in a single page application, + * or React's `componentWillUnmount()`. + * + * @example + * + * .destroy([cleanUp]) + * + * @example Example: Destroying the mixer before removing its container element + * + * mixer.destroy(); + * + * containerEl.parentElement.removeChild(containerEl); + * + * @public + * @instance + * @since 2.0.0 + * @param {boolean} [cleanUp=false] + * An optional boolean dictating whether or not to clean up any inline `display: none;` styling applied to hidden targets. + * @return {void} + */ + + destroy: function(cleanUp) { + var self = this, + control = null, + target = null, + i = 0; + + self.callActions('beforeDestroy', arguments); + + for (i = 0; control = self.controls[i]; i++) { + control.removeBinding(self); + } + + for (i = 0; target = self.targets[i]; i++) { + if (cleanUp) { + target.show(); + } + + target.unbindEvents(); + } + + if (self.dom.container.id.match(/^MixItUp/)) { + self.dom.container.removeAttribute('id'); + } + + delete mixitup.instances[self.id]; + + self.callActions('afterDestroy', arguments); + } +}); \ No newline at end of file diff --git a/mixitup-3.3.1/src/module-definitions.js b/mixitup-3.3.1/src/module-definitions.js new file mode 100644 index 0000000..4630c78 --- /dev/null +++ b/mixitup-3.3.1/src/module-definitions.js @@ -0,0 +1,11 @@ +/* global module, mixitup, define */ + +if (typeof exports === 'object' && typeof module === 'object') { + module.exports = mixitup; +} else if (typeof define === 'function' && define.amd) { + define(function() { + return mixitup; + }); +} else if (typeof window.mixitup === 'undefined' || typeof window.mixitup !== 'function') { + window.mixitup = mixitup; +} \ No newline at end of file diff --git a/mixitup-3.3.1/src/operation.js b/mixitup-3.3.1/src/operation.js new file mode 100644 index 0000000..bbfc3c9 --- /dev/null +++ b/mixitup-3.3.1/src/operation.js @@ -0,0 +1,77 @@ +/* global mixitup, h */ + +/** + * `mixitup.Operation` objects contain all data neccessary to describe the full + * lifecycle of any MixItUp operation. They can be used to compute and store an + * operation for use at a later time (e.g. programmatic tweening). + * + * @constructor + * @namespace + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.Operation = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.id = ''; + + this.args = []; + this.command = null; + this.showPosData = []; + this.toHidePosData = []; + + this.startState = null; + this.newState = null; + this.docState = null; + + this.willSort = false; + this.willChangeLayout = false; + this.hasEffect = false; + this.hasFailed = false; + + this.triggerElement = null; + + this.show = []; + this.hide = []; + this.matching = []; + this.toShow = []; + this.toHide = []; + this.toMove = []; + this.toRemove = []; + this.startOrder = []; + this.newOrder = []; + this.startSort = null; + this.newSort = null; + this.startFilter = null; + this.newFilter = null; + this.startDataset = null; + this.newDataset = null; + this.viewportDeltaX = 0; + this.viewportDeltaY = 0; + this.startX = 0; + this.startY = 0; + this.startHeight = 0; + this.startWidth = 0; + this.newX = 0; + this.newY = 0; + this.newHeight = 0; + this.newWidth = 0; + this.startContainerClassName = ''; + this.startDisplay = ''; + this.newContainerClassName = ''; + this.newDisplay = ''; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.Operation); + +mixitup.Operation.prototype = Object.create(mixitup.Base.prototype); + +mixitup.Operation.prototype.constructor = mixitup.Operation; \ No newline at end of file diff --git a/mixitup-3.3.1/src/polyfills.js b/mixitup-3.3.1/src/polyfills.js new file mode 100644 index 0000000..c239cd0 --- /dev/null +++ b/mixitup-3.3.1/src/polyfills.js @@ -0,0 +1,237 @@ +(function() { + var VENDORS = ['webkit', 'moz', 'o', 'ms'], + canary = window.document.createElement('div'), + i = -1; + + // window.requestAnimationFrame + + for (i = 0; i < VENDORS.length && !window.requestAnimationFrame; i++) { + window.requestAnimationFrame = window[VENDORS[i] + 'RequestAnimationFrame']; + } + + // Element.nextElementSibling + + if (typeof canary.nextElementSibling === 'undefined') { + Object.defineProperty(window.Element.prototype, 'nextElementSibling', { + get: function() { + var el = this.nextSibling; + + while (el) { + if (el.nodeType === 1) { + return el; + } + + el = el.nextSibling; + } + + return null; + } + }); + } + + // Element.matches + + (function(ElementPrototype) { + ElementPrototype.matches = + ElementPrototype.matches || + ElementPrototype.machesSelector || + ElementPrototype.mozMatchesSelector || + ElementPrototype.msMatchesSelector || + ElementPrototype.oMatchesSelector || + ElementPrototype.webkitMatchesSelector || + function (selector) { + return Array.prototype.indexOf.call(this.parentElement.querySelectorAll(selector), this) > -1; + }; + })(window.Element.prototype); + + // Object.keys + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys + + if (!Object.keys) { + Object.keys = (function() { + var hasOwnProperty = Object.prototype.hasOwnProperty, + hasDontEnumBug = false, + dontEnums = [], + dontEnumsLength = -1; + + hasDontEnumBug = !({ + toString: null + }) + .propertyIsEnumerable('toString'); + + dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' + ]; + + dontEnumsLength = dontEnums.length; + + return function(obj) { + var result = [], + prop = '', + i = -1; + + if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { + throw new TypeError('Object.keys called on non-object'); + } + + for (prop in obj) { + if (hasOwnProperty.call(obj, prop)) { + result.push(prop); + } + } + + if (hasDontEnumBug) { + for (i = 0; i < dontEnumsLength; i++) { + if (hasOwnProperty.call(obj, dontEnums[i])) { + result.push(dontEnums[i]); + } + } + } + + return result; + }; + }()); + } + + // Array.isArray + // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray + + if (!Array.isArray) { + Array.isArray = function(arg) { + return Object.prototype.toString.call(arg) === '[object Array]'; + }; + } + + // Object.create + // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/create + + if (typeof Object.create !== 'function') { + Object.create = (function(undefined) { + var Temp = function() {}; + + return function (prototype, propertiesObject) { + if (prototype !== Object(prototype) && prototype !== null) { + throw TypeError('Argument must be an object, or null'); + } + + Temp.prototype = prototype || {}; + + var result = new Temp(); + + Temp.prototype = null; + + if (propertiesObject !== undefined) { + Object.defineProperties(result, propertiesObject); + } + + if (prototype === null) { + /* jshint ignore:start */ + result.__proto__ = null; + /* jshint ignore:end */ + } + + return result; + }; + })(); + } + + // String.prototyoe.trim + + if (!String.prototype.trim) { + String.prototype.trim = function() { + return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); + }; + } + + // Array.prototype.indexOf + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf + + if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function(searchElement) { + var n, k, t, len; + + if (this === null) { + throw new TypeError(); + } + + t = Object(this); + + len = t.length >>> 0; + + if (len === 0) { + return -1; + } + + n = 0; + + if (arguments.length > 1) { + n = Number(arguments[1]); + + if (n !== n) { + n = 0; + } else if (n !== 0 && n !== Infinity && n !== -Infinity) { + n = (n > 0 || -1) * Math.floor(Math.abs(n)); + } + } + + if (n >= len) { + return -1; + } + + for (k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); k < len; k++) { + if (k in t && t[k] === searchElement) { + return k; + } + } + + return -1; + }; + } + + // Function.prototype.bind + // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind + + if (!Function.prototype.bind) { + Function.prototype.bind = function(oThis) { + var aArgs, self, FNOP, fBound; + + if (typeof this !== 'function') { + throw new TypeError(); + } + + aArgs = Array.prototype.slice.call(arguments, 1); + + self = this; + + FNOP = function() {}; + + fBound = function() { + return self.apply(this instanceof FNOP ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + if (this.prototype) { + FNOP.prototype = this.prototype; + } + + fBound.prototype = new FNOP(); + + return fBound; + }; + } + + // Element.prototype.dispatchEvent + + if (!window.Element.prototype.dispatchEvent) { + window.Element.prototype.dispatchEvent = function(event) { + try { + return this.fireEvent('on' + event.type, event); + } catch (err) {} + }; + } +})(); \ No newline at end of file diff --git a/mixitup-3.3.1/src/queue-item.js b/mixitup-3.3.1/src/queue-item.js new file mode 100644 index 0000000..385fe21 --- /dev/null +++ b/mixitup-3.3.1/src/queue-item.js @@ -0,0 +1,30 @@ +/* global mixitup, h */ + +/** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.QueueItem = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.args = []; + this.instruction = null; + this.triggerElement = null; + this.deferred = null; + this.isToggling = false; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.QueueItem); + +mixitup.QueueItem.prototype = Object.create(mixitup.Base.prototype); + +mixitup.QueueItem.prototype.constructor = mixitup.QueueItem; \ No newline at end of file diff --git a/mixitup-3.3.1/src/state.js b/mixitup-3.3.1/src/state.js new file mode 100644 index 0000000..a5602fd --- /dev/null +++ b/mixitup-3.3.1/src/state.js @@ -0,0 +1,233 @@ +/* global mixitup, h */ + +/** + * `mixitup.State` objects expose various pieces of data detailing the state of + * a MixItUp instance. They are provided at the start and end of any operation via + * callbacks and events, with the most recent state stored between operations + * for retrieval at any time via the API. + * + * @constructor + * @namespace + * @memberof mixitup + * @public + * @since 3.0.0 + */ + +mixitup.State = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * The ID of the mixer instance. + * + * @name id + * @memberof mixitup.State + * @instance + * @type {string} + * @default '' + */ + + this.id = ''; + + /** + * The currently active filter command as set by a control click or API call. + * + * @name activeFilter + * @memberof mixitup.State + * @instance + * @type {mixitup.CommandFilter} + * @default null + */ + + this.activeFilter = null; + + /** + * The currently active sort command as set by a control click or API call. + * + * @name activeSort + * @memberof mixitup.State + * @instance + * @type {mixitup.CommandSort} + * @default null + */ + + this.activeSort = null; + + /** + * The current layout-specific container class name, if applied. + * + * @name activeContainerClassName + * @memberof mixitup.State + * @instance + * @type {string} + * @default '' + */ + + this.activeContainerClassName = ''; + + /** + * A reference to the container element that the mixer is instantiated on. + * + * @name container + * @memberof mixitup.State + * @instance + * @type {Element} + * @default null + */ + + this.container = null; + + /** + * An array of all target elements indexed by the mixer. + * + * @name targets + * @memberof mixitup.State + * @instance + * @type {Array.} + * @default [] + */ + + this.targets = []; + + /** + * An array of all target elements not matching the current filter. + * + * @name hide + * @memberof mixitup.State + * @instance + * @type {Array.} + * @default [] + */ + + this.hide = []; + + /** + * An array of all target elements matching the current filter and any additional + * limits applied such as pagination. + * + * @name show + * @memberof mixitup.State + * @instance + * @type {Array.} + * @default [] + */ + + this.show = []; + + /** + * An array of all target elements matching the current filter irrespective of + * any additional limits applied such as pagination. + * + * @name matching + * @memberof mixitup.State + * @instance + * @type {Array.} + * @default [] + */ + + this.matching = []; + + /** + * An integer representing the total number of target elements indexed by the + * mixer. Equivalent to `state.targets.length`. + * + * @name totalTargets + * @memberof mixitup.State + * @instance + * @type {number} + * @default -1 + */ + + this.totalTargets = -1; + + /** + * An integer representing the total number of target elements matching the + * current filter and any additional limits applied such as pagination. + * Equivalent to `state.show.length`. + * + * @name totalShow + * @memberof mixitup.State + * @instance + * @type {number} + * @default -1 + */ + + this.totalShow = -1; + + /** + * An integer representing the total number of target elements not matching + * the current filter. Equivalent to `state.hide.length`. + * + * @name totalHide + * @memberof mixitup.State + * @instance + * @type {number} + * @default -1 + */ + + this.totalHide = -1; + + /** + * An integer representing the total number of target elements matching the + * current filter irrespective of any other limits applied such as pagination. + * Equivalent to `state.matching.length`. + * + * @name totalMatching + * @memberof mixitup.State + * @instance + * @type {number} + * @default -1 + */ + + this.totalMatching = -1; + + /** + * A boolean indicating whether the last operation "failed", i.e. no targets + * could be found matching the filter. + * + * @name hasFailed + * @memberof mixitup.State + * @instance + * @type {boolean} + * @default false + */ + + this.hasFailed = false; + + /** + * The DOM element that was clicked if the last operation was triggered by the + * clicking of a control and not an API call. + * + * @name triggerElement + * @memberof mixitup.State + * @instance + * @type {Element|null} + * @default null + */ + + this.triggerElement = null; + + /** + * The currently active dataset underlying the rendered targets, if the + * dataset API is in use. + * + * @name activeDataset + * @memberof mixitup.State + * @instance + * @type {Array.} + * @default null + */ + + this.activeDataset = null; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.State); + +mixitup.State.prototype = Object.create(mixitup.Base.prototype); + +mixitup.State.prototype.constructor = mixitup.State; \ No newline at end of file diff --git a/mixitup-3.3.1/src/style-data.js b/mixitup-3.3.1/src/style-data.js new file mode 100644 index 0000000..8819b5b --- /dev/null +++ b/mixitup-3.3.1/src/style-data.js @@ -0,0 +1,43 @@ +/* global mixitup, h */ + +/** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.StyleData = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.x = 0; + this.y = 0; + this.top = 0; + this.right = 0; + this.bottom = 0; + this.left = 0; + this.width = 0; + this.height = 0; + this.marginRight = 0; + this.marginBottom = 0; + this.opacity = 0; + this.scale = new mixitup.TransformData(); + this.translateX = new mixitup.TransformData(); + this.translateY = new mixitup.TransformData(); + this.translateZ = new mixitup.TransformData(); + this.rotateX = new mixitup.TransformData(); + this.rotateY = new mixitup.TransformData(); + this.rotateZ = new mixitup.TransformData(); + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.StyleData); + +mixitup.StyleData.prototype = Object.create(mixitup.Base.prototype); + +mixitup.StyleData.prototype.constructor = mixitup.StyleData; \ No newline at end of file diff --git a/mixitup-3.3.1/src/target-dom.js b/mixitup-3.3.1/src/target-dom.js new file mode 100644 index 0000000..c00a768 --- /dev/null +++ b/mixitup-3.3.1/src/target-dom.js @@ -0,0 +1,26 @@ +/* global mixitup, h */ + +/** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.TargetDom = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.el = null; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.TargetDom); + +mixitup.TargetDom.prototype = Object.create(mixitup.Base.prototype); + +mixitup.TargetDom.prototype.constructor = mixitup.TargetDom; \ No newline at end of file diff --git a/mixitup-3.3.1/src/target.js b/mixitup-3.3.1/src/target.js new file mode 100644 index 0000000..357d8c2 --- /dev/null +++ b/mixitup-3.3.1/src/target.js @@ -0,0 +1,730 @@ +/* global mixitup, h */ + +/** + * @constructor + * @namespace + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.Target = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.id = ''; + this.sortString = ''; + this.mixer = null; + this.callback = null; + this.isShown = false; + this.isBound = false; + this.isExcluded = false; + this.isInDom = false; + this.handler = null; + this.operation = null; + this.data = null; + this.dom = new mixitup.TargetDom(); + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.Target); + +mixitup.Target.prototype = Object.create(mixitup.Base.prototype); + +h.extend(mixitup.Target.prototype, { + constructor: mixitup.Target, + + /** + * Initialises a newly instantiated Target. + * + * @private + * @instance + * @since 3.0.0 + * @param {(Element|null)} el + * @param {object} mixer + * @param {object} [data] + * @return {void} + */ + + init: function(el, mixer, data) { + var self = this, + id = ''; + + self.callActions('beforeInit', arguments); + + self.mixer = mixer; + + if (!el) { + // If no element is provided, render it + + el = self.render(data); + } + + self.cacheDom(el); + + self.bindEvents(); + + if (self.dom.el.style.display !== 'none') { + self.isShown = true; + } + + if (data && mixer.config.data.uidKey) { + if (typeof (id = data[mixer.config.data.uidKey]) === 'undefined' || id.toString().length < 1) { + throw new TypeError(mixitup.messages.errorDatasetInvalidUidKey({ + uidKey: mixer.config.data.uidKey + })); + } + + self.id = id; + self.data = data; + + mixer.cache[id] = self; + } + + self.callActions('afterInit', arguments); + }, + + /** + * Renders the target element using a user-defined renderer function. + * + * @private + * @instance + * @since 3.1.4 + * @param {object} data + * @return {void} + */ + + render: function(data) { + var self = this, + render = null, + el = null, + temp = null, + output = ''; + + self.callActions('beforeRender', arguments); + + render = self.callFilters('renderRender', self.mixer.config.render.target, arguments); + + if (typeof render !== 'function') { + throw new TypeError(mixitup.messages.errorDatasetRendererNotSet()); + } + + output = render(data); + + if (output && typeof output === 'object' && h.isElement(output)) { + el = output; + } else if (typeof output === 'string') { + temp = document.createElement('div'); + temp.innerHTML = output; + + el = temp.firstElementChild; + } + + return self.callFilters('elRender', el, arguments); + }, + + /** + * Caches references of DOM elements neccessary for the target's functionality. + * + * @private + * @instance + * @since 3.0.0 + * @param {Element} el + * @return {void} + */ + + cacheDom: function(el) { + var self = this; + + self.callActions('beforeCacheDom', arguments); + + self.dom.el = el; + + self.callActions('afterCacheDom', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {string} attributeName + * @return {void} + */ + + getSortString: function(attributeName) { + var self = this, + value = self.dom.el.getAttribute('data-' + attributeName) || ''; + + self.callActions('beforeGetSortString', arguments); + + value = isNaN(value * 1) ? + value.toLowerCase() : + value * 1; + + self.sortString = value; + + self.callActions('afterGetSortString', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @return {void} + */ + + show: function() { + var self = this; + + self.callActions('beforeShow', arguments); + + if (!self.isShown) { + self.dom.el.style.display = ''; + + self.isShown = true; + } + + self.callActions('afterShow', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @return {void} + */ + + hide: function() { + var self = this; + + self.callActions('beforeHide', arguments); + + if (self.isShown) { + self.dom.el.style.display = 'none'; + + self.isShown = false; + } + + self.callActions('afterHide', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {mixitup.IMoveData} moveData + * @return {void} + */ + + move: function(moveData) { + var self = this; + + self.callActions('beforeMove', arguments); + + if (!self.isExcluded) { + self.mixer.targetsMoved++; + } + + self.applyStylesIn(moveData); + + requestAnimationFrame(function() { + self.applyStylesOut(moveData); + }); + + self.callActions('afterMove', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {object} posData + * @param {number} multiplier + * @return {void} + */ + + applyTween: function(posData, multiplier) { + var self = this, + propertyName = '', + tweenData = null, + posIn = posData.posIn, + currentTransformValues = [], + currentValues = new mixitup.StyleData(), + i = -1; + + self.callActions('beforeApplyTween', arguments); + + currentValues.x = posIn.x; + currentValues.y = posIn.y; + + if (multiplier === 0) { + self.hide(); + } else if (!self.isShown) { + self.show(); + } + + for (i = 0; propertyName = mixitup.features.TWEENABLE[i]; i++) { + tweenData = posData.tweenData[propertyName]; + + if (propertyName === 'x') { + if (!tweenData) continue; + + currentValues.x = posIn.x + (tweenData * multiplier); + } else if (propertyName === 'y') { + if (!tweenData) continue; + + currentValues.y = posIn.y + (tweenData * multiplier); + } else if (tweenData instanceof mixitup.TransformData) { + if (!tweenData.value) continue; + + currentValues[propertyName].value = + posIn[propertyName].value + (tweenData.value * multiplier); + + currentValues[propertyName].unit = tweenData.unit; + + currentTransformValues.push( + propertyName + '(' + currentValues[propertyName].value + tweenData.unit + ')' + ); + } else { + if (!tweenData) continue; + + currentValues[propertyName] = posIn[propertyName] + (tweenData * multiplier); + + self.dom.el.style[propertyName] = currentValues[propertyName]; + } + } + + if (currentValues.x || currentValues.y) { + currentTransformValues.unshift('translate(' + currentValues.x + 'px, ' + currentValues.y + 'px)'); + } + + if (currentTransformValues.length) { + self.dom.el.style[mixitup.features.transformProp] = currentTransformValues.join(' '); + } + + self.callActions('afterApplyTween', arguments); + }, + + /** + * Applies the initial styling to a target element before any transition + * is applied. + * + * @private + * @instance + * @param {mixitup.IMoveData} moveData + * @return {void} + */ + + applyStylesIn: function(moveData) { + var self = this, + posIn = moveData.posIn, + isFading = self.mixer.effectsIn.opacity !== 1, + transformValues = []; + + self.callActions('beforeApplyStylesIn', arguments); + + transformValues.push('translate(' + posIn.x + 'px, ' + posIn.y + 'px)'); + + if (self.mixer.config.animation.animateResizeTargets) { + if (moveData.statusChange !== 'show') { + // Don't apply posIn width or height or showing, as will be 0 + + self.dom.el.style.width = posIn.width + 'px'; + self.dom.el.style.height = posIn.height + 'px'; + } + + self.dom.el.style.marginRight = posIn.marginRight + 'px'; + self.dom.el.style.marginBottom = posIn.marginBottom + 'px'; + } + + isFading && (self.dom.el.style.opacity = posIn.opacity); + + if (moveData.statusChange === 'show') { + transformValues = transformValues.concat(self.mixer.transformIn); + } + + self.dom.el.style[mixitup.features.transformProp] = transformValues.join(' '); + + self.callActions('afterApplyStylesIn', arguments); + }, + + /** + * Applies a transition followed by the final styles for the element to + * transition towards. + * + * @private + * @instance + * @param {mixitup.IMoveData} moveData + * @return {void} + */ + + applyStylesOut: function(moveData) { + var self = this, + transitionRules = [], + transformValues = [], + isResizing = self.mixer.config.animation.animateResizeTargets, + isFading = typeof self.mixer.effectsIn.opacity !== 'undefined'; + + self.callActions('beforeApplyStylesOut', arguments); + + // Build the transition rules + + transitionRules.push(self.writeTransitionRule( + mixitup.features.transformRule, + moveData.staggerIndex + )); + + if (moveData.statusChange !== 'none') { + transitionRules.push(self.writeTransitionRule( + 'opacity', + moveData.staggerIndex, + moveData.duration + )); + } + + if (isResizing) { + transitionRules.push(self.writeTransitionRule( + 'width', + moveData.staggerIndex, + moveData.duration + )); + + transitionRules.push(self.writeTransitionRule( + 'height', + moveData.staggerIndex, + moveData.duration + )); + + transitionRules.push(self.writeTransitionRule( + 'margin', + moveData.staggerIndex, + moveData.duration + )); + } + + // If no callback was provided, the element will + // not transition in any way so tag it as "immovable" + + if (!moveData.callback) { + self.mixer.targetsImmovable++; + + if (self.mixer.targetsMoved === self.mixer.targetsImmovable) { + // If the total targets moved is equal to the + // number of immovable targets, the operation + // should be considered finished + + self.mixer.cleanUp(moveData.operation); + } + + return; + } + + // If the target will transition in some fasion, + // assign a callback function + + self.operation = moveData.operation; + self.callback = moveData.callback; + + // As long as the target is not excluded, increment + // the total number of targets bound + + !self.isExcluded && self.mixer.targetsBound++; + + // Tag the target as bound to differentiate from transitionEnd + // events that may come from stylesheet driven effects + + self.isBound = true; + + // Apply the transition + + self.applyTransition(transitionRules); + + // Apply width, height and margin negation + + if (isResizing && moveData.posOut.width > 0 && moveData.posOut.height > 0) { + self.dom.el.style.width = moveData.posOut.width + 'px'; + self.dom.el.style.height = moveData.posOut.height + 'px'; + self.dom.el.style.marginRight = moveData.posOut.marginRight + 'px'; + self.dom.el.style.marginBottom = moveData.posOut.marginBottom + 'px'; + } + + if (!self.mixer.config.animation.nudge && moveData.statusChange === 'hide') { + // If we're not nudging, the translation should be + // applied before any other transforms to prevent + // lateral movement + + transformValues.push('translate(' + moveData.posOut.x + 'px, ' + moveData.posOut.y + 'px)'); + } + + // Apply fade + + switch (moveData.statusChange) { + case 'hide': + isFading && (self.dom.el.style.opacity = self.mixer.effectsOut.opacity); + + transformValues = transformValues.concat(self.mixer.transformOut); + + break; + case 'show': + isFading && (self.dom.el.style.opacity = 1); + } + + if ( + self.mixer.config.animation.nudge || + (!self.mixer.config.animation.nudge && moveData.statusChange !== 'hide') + ) { + // Opposite of above - apply translate after + // other transform + + transformValues.push('translate(' + moveData.posOut.x + 'px, ' + moveData.posOut.y + 'px)'); + } + + // Apply transforms + + self.dom.el.style[mixitup.features.transformProp] = transformValues.join(' '); + + self.callActions('afterApplyStylesOut', arguments); + }, + + /** + * Combines the name of a CSS property with the appropriate duration and delay + * values to created a valid transition rule. + * + * @private + * @instance + * @since 3.0.0 + * @param {string} property + * @param {number} staggerIndex + * @param {number} duration + * @return {string} + */ + + writeTransitionRule: function(property, staggerIndex, duration) { + var self = this, + delay = self.getDelay(staggerIndex), + rule = ''; + + rule = property + ' ' + + (duration > 0 ? duration : self.mixer.config.animation.duration) + 'ms ' + + delay + 'ms ' + + (property === 'opacity' ? 'linear' : self.mixer.config.animation.easing); + + return self.callFilters('ruleWriteTransitionRule', rule, arguments); + }, + + /** + * Calculates the transition delay for each target element based on its index, if + * staggering is applied. If defined, A custom `animation.staggerSeqeuence` + * function can be used to manipulate the order of indices to produce custom + * stagger effects (e.g. for use in a grid with irregular row lengths). + * + * @private + * @instance + * @since 2.0.0 + * @param {number} index + * @return {number} + */ + + getDelay: function(index) { + var self = this, + delay = -1; + + if (typeof self.mixer.config.animation.staggerSequence === 'function') { + index = self.mixer.config.animation.staggerSequence.call(self, index, self.state); + } + + delay = !!self.mixer.staggerDuration ? index * self.mixer.staggerDuration : 0; + + return self.callFilters('delayGetDelay', delay, arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {string[]} rules + * @return {void} + */ + + applyTransition: function(rules) { + var self = this, + transitionString = rules.join(', '); + + self.callActions('beforeApplyTransition', arguments); + + self.dom.el.style[mixitup.features.transitionProp] = transitionString; + + self.callActions('afterApplyTransition', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Event} e + * @return {void} + */ + + handleTransitionEnd: function(e) { + var self = this, + propName = e.propertyName, + canResize = self.mixer.config.animation.animateResizeTargets; + + self.callActions('beforeHandleTransitionEnd', arguments); + + if ( + self.isBound && + e.target.matches(self.mixer.config.selectors.target) && + ( + propName.indexOf('transform') > -1 || + propName.indexOf('opacity') > -1 || + canResize && propName.indexOf('height') > -1 || + canResize && propName.indexOf('width') > -1 || + canResize && propName.indexOf('margin') > -1 + ) + ) { + self.callback.call(self, self.operation); + + self.isBound = false; + self.callback = null; + self.operation = null; + } + + self.callActions('afterHandleTransitionEnd', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Event} e + * @return {void} + */ + + eventBus: function(e) { + var self = this; + + self.callActions('beforeEventBus', arguments); + + switch (e.type) { + case 'webkitTransitionEnd': + case 'transitionend': + self.handleTransitionEnd(e); + } + + self.callActions('afterEventBus', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @return {void} + */ + + unbindEvents: function() { + var self = this; + + self.callActions('beforeUnbindEvents', arguments); + + h.off(self.dom.el, 'webkitTransitionEnd', self.handler); + h.off(self.dom.el, 'transitionend', self.handler); + + self.callActions('afterUnbindEvents', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @return {void} + */ + + bindEvents: function() { + var self = this, + transitionEndEvent = ''; + + self.callActions('beforeBindEvents', arguments); + + transitionEndEvent = mixitup.features.transitionPrefix === 'webkit' ? 'webkitTransitionEnd' : 'transitionend'; + + self.handler = function(e) { + return self.eventBus(e); + }; + + h.on(self.dom.el, transitionEndEvent, self.handler); + + self.callActions('afterBindEvents', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {boolean} [getBox] + * @return {PosData} + */ + + getPosData: function(getBox) { + var self = this, + styles = {}, + rect = null, + posData = new mixitup.StyleData(); + + self.callActions('beforeGetPosData', arguments); + + posData.x = self.dom.el.offsetLeft; + posData.y = self.dom.el.offsetTop; + + if (self.mixer.config.animation.animateResizeTargets || getBox) { + rect = self.dom.el.getBoundingClientRect(); + + posData.top = rect.top; + posData.right = rect.right; + posData.bottom = rect.bottom; + posData.left = rect.left; + + posData.width = rect.width; + posData.height = rect.height; + } + + if (self.mixer.config.animation.animateResizeTargets) { + styles = window.getComputedStyle(self.dom.el); + + posData.marginBottom = parseFloat(styles.marginBottom); + posData.marginRight = parseFloat(styles.marginRight); + } + + return self.callFilters('posDataGetPosData', posData, arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @return {void} + */ + + cleanUp: function() { + var self = this; + + self.callActions('beforeCleanUp', arguments); + + self.dom.el.style[mixitup.features.transformProp] = ''; + self.dom.el.style[mixitup.features.transitionProp] = ''; + self.dom.el.style.opacity = ''; + + if (self.mixer.config.animation.animateResizeTargets) { + self.dom.el.style.width = ''; + self.dom.el.style.height = ''; + self.dom.el.style.marginRight = ''; + self.dom.el.style.marginBottom = ''; + } + + self.callActions('afterCleanUp', arguments); + } +}); \ No newline at end of file diff --git a/mixitup-3.3.1/src/transform-data.js b/mixitup-3.3.1/src/transform-data.js new file mode 100644 index 0000000..ac8b8d2 --- /dev/null +++ b/mixitup-3.3.1/src/transform-data.js @@ -0,0 +1,27 @@ +/* global mixitup, h */ + +/** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.TransformData = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.value = 0; + this.unit = ''; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.TransformData); + +mixitup.TransformData.prototype = Object.create(mixitup.Base.prototype); + +mixitup.TransformData.prototype.constructor = mixitup.TransformData; \ No newline at end of file diff --git a/mixitup-3.3.1/src/transform-defaults.js b/mixitup-3.3.1/src/transform-defaults.js new file mode 100644 index 0000000..5a2a692 --- /dev/null +++ b/mixitup-3.3.1/src/transform-defaults.js @@ -0,0 +1,57 @@ +/* global mixitup, h */ + +/** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.TransformDefaults = function() { + mixitup.StyleData.apply(this); + + this.callActions('beforeConstruct'); + + this.scale.value = 0.01; + this.scale.unit = ''; + + this.translateX.value = 20; + this.translateX.unit = 'px'; + + this.translateY.value = 20; + this.translateY.unit = 'px'; + + this.translateZ.value = 20; + this.translateZ.unit = 'px'; + + this.rotateX.value = 90; + this.rotateX.unit = 'deg'; + + this.rotateY.value = 90; + this.rotateY.unit = 'deg'; + + this.rotateX.value = 90; + this.rotateX.unit = 'deg'; + + this.rotateZ.value = 180; + this.rotateZ.unit = 'deg'; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.TransformDefaults); + +mixitup.TransformDefaults.prototype = Object.create(mixitup.StyleData.prototype); + +mixitup.TransformDefaults.prototype.constructor = mixitup.TransformDefaults; + +/** + * @private + * @static + * @since 3.0.0 + * @type {mixitup.TransformDefaults} + */ + +mixitup.transformDefaults = new mixitup.TransformDefaults(); \ No newline at end of file diff --git a/mixitup-3.3.1/src/ui-class-names.js b/mixitup-3.3.1/src/ui-class-names.js new file mode 100644 index 0000000..3ffa9c2 --- /dev/null +++ b/mixitup-3.3.1/src/ui-class-names.js @@ -0,0 +1,28 @@ +/* global mixitup, h */ + +/** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.UiClassNames = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.base = ''; + this.active = ''; + this.disabled = ''; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.UiClassNames); + +mixitup.UiClassNames.prototype = Object.create(mixitup.Base.prototype); + +mixitup.UiClassNames.prototype.constructor = mixitup.UiClassNames; \ No newline at end of file diff --git a/mixitup-3.3.1/src/user-instruction.js b/mixitup-3.3.1/src/user-instruction.js new file mode 100644 index 0000000..f512e64 --- /dev/null +++ b/mixitup-3.3.1/src/user-instruction.js @@ -0,0 +1,28 @@ +/* global mixitup, h */ + +/** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + +mixitup.UserInstruction = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.command = {}; + this.animate = false; + this.callback = null; + + this.callActions('afterConstruct'); + + h.seal(this); +}; + +mixitup.BaseStatic.call(mixitup.UserInstruction); + +mixitup.UserInstruction.prototype = Object.create(mixitup.Base.prototype); + +mixitup.UserInstruction.prototype.constructor = mixitup.UserInstruction; \ No newline at end of file diff --git a/mixitup-3.3.1/src/wrapper.hbs b/mixitup-3.3.1/src/wrapper.hbs new file mode 100644 index 0000000..cb1b0d7 --- /dev/null +++ b/mixitup-3.3.1/src/wrapper.hbs @@ -0,0 +1,105 @@ +{{>banner}} + +(function(window) { + 'use strict'; + + var mixitup = null, + h = null; + + {{>polyfills}} + + {{>factory}} + + {{>h}} + + {{>base}} + + {{>base-static}} + + {{>features}} + + {{>config-animation}} + + {{>config-behavior}} + + {{>config-callbacks}} + + {{>config-controls}} + + {{>config-class-names}} + + {{>config-data}} + + {{>config-debug}} + + {{>config-layout}} + + {{>config-load}} + + {{>config-selectors}} + + {{>config-render}} + + {{>config-templates}} + + {{>config}} + + {{>mixer-dom}} + + {{>ui-class-names}} + + {{>command-dataset}} + + {{>command-multimix}} + + {{>command-filter}} + + {{>command-sort}} + + {{>command-insert}} + + {{>command-remove}} + + {{>command-change-layout}} + + {{>control-definition}} + + {{>control}} + + {{>style-data}} + + {{>transform-data}} + + {{>transform-defaults}} + + {{>events}} + + {{>queue-item}} + + {{>mixer}} + + {{>i-move-data}} + + {{>target-dom}} + + {{>target}} + + {{>collection}} + + {{>operation}} + + {{>state}} + + {{>user-instruction}} + + {{>messages}} + + {{>facade}} + + {{>module-definitions}} + + mixitup.BaseStatic.call(mixitup.constructor); + + mixitup.NAME = '{{name}}'; + mixitup.CORE_VERSION = '{{version}}'; +})(window); \ No newline at end of file diff --git a/mixitup-3.3.1/tests/functional/index.html b/mixitup-3.3.1/tests/functional/index.html new file mode 100644 index 0000000..4817439 --- /dev/null +++ b/mixitup-3.3.1/tests/functional/index.html @@ -0,0 +1,101 @@ + + + + + MixItUp Demo + + + + +

Native Controls

+ +

Filters

+ + + + + + + + +

Filter Toggles

+ + + + + +

Sorts

+ + + | + + + | + + + | + + + +

MultiMix

+ + + + +

API Methods

+ +
Append
+
Prepend
+
Insert at index (5)
+
Remove by element reference (0)
+
Remove by index (3)
+
Insert Multiple via markup
+
Insert Multiple via elements


+ +
Filter Category B
+
Filter Category B + C
+
Sort Custom Ascending
+ +

+ +
3 per page
+
Infinite per page
+
10 per page
+
+ +
+
A 5
+
A 1
+
B 4
+
C 3
+
C 2
+
C 6
+ +
+
+
+
+
+
+
+ + + + +
+
+ +
+
+
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/mixitup-3.3.1/tests/functional/main.js b/mixitup-3.3.1/tests/functional/main.js new file mode 100644 index 0000000..379baaa --- /dev/null +++ b/mixitup-3.3.1/tests/functional/main.js @@ -0,0 +1,191 @@ +/* global mixitup */ + +var sandbox = document.querySelector('.sandbox'); + +var mixer = mixitup('#sandbox-1', { + animation: { + effects: 'fade', + easing: 'cubic-bezier(1, 0, 0, 1)', + duration: 400 + }, + controls: { + scope: 'global', + live: false + // toggleLogic: 'and' + }, + pagination: { + limit: 4, + maxPagers: 6 + }, + load: { + // filter: 'none', + sort: 'random' + }, + // dragndrop: { + // enable: true, + // hidePlaceholder: false, + // debounceDelay: 20, + // detection: 'collision', + // // liveSort: false + // // swap: true + // }, + callbacks: { + // onMixLift: function() { + // console.log('lift', this); + // } + } +}, null); + +console.log(mixer.getState()); + +sandbox.addEventListener('mixStart', function(e) { + console.log('mixStart', e.detail); +}); + +sandbox.addEventListener('mixEnd', function(e) { + console.log('mixEnd', e.detail); +}); + +sandbox.addEventListener('mixClick', function(e) { + console.log('mixClick', e.detail); +}); + +sandbox.addEventListener('mixBusy', function(e) { + console.log('mixBusy', e.detail); +}); + +sandbox.addEventListener('mixFail', function(e) { + console.log('mixFail', e.detail); +}); + +sandbox.classList.add('sandbox__mixitup'); + +document.querySelector('.js-append').addEventListener('click', function() { + mixer.append('
C 3
'); +}); + +document.querySelector('.js-prepend').addEventListener('click', function() { + mixer.prepend('
A 2
'); +}); + +document.querySelector('.js-insert-at-index').addEventListener('click', function() { + mixer.insert(4, '
D 5
'); +}); + +document.querySelector('.js-remove-via-element').addEventListener('click', function() { + var state = mixer.getState(); + + if (state.targets[0]) { + mixer.remove(state.targets[0]); + } +}); + +document.querySelector('.js-remove-via-index').addEventListener('click', function() { + mixer.remove(3); +}); + +document.querySelector('.js-insert-multiple-via-markup').addEventListener('click', function() { + mixer.prepend( + '
D 1
' + + '
D 2
' + + '
D 3
' + ); +}); + +document.querySelector('.js-insert-multiple-via-elements').addEventListener('click', function() { + var h = mixitup.h, + el1 = h.createElement('
D 1
').children[0], + el2 = h.createElement('
D 2
').children[0], + el3 = h.createElement('
D 3
').children[0], + elements = [el1, el2, el3]; + + // Going into the mixer backwards? + + mixer.multiMix({ + insert: elements + }); +}); + +document.querySelector('.js-api-filter').addEventListener('click', function() { + mixer.filter('.cat-2'); +}); + +document.querySelector('.js-api-filter-compound').addEventListener('click', function() { + mixer.filter('.cat-2, .cat-3'); +}); + +document.querySelector('.js-api-sort').addEventListener('click', function() { + mixer.sort('order:asc'); +}); + +document.querySelector('.js-api-limit-3').addEventListener('click', function() { + mixer.paginate({limit: 3}); +}); + +document.querySelector('.js-api-limit-inf').addEventListener('click', function() { + mixer.paginate({limit: Infinity}); +}); + +document.querySelector('.js-api-limit-10').addEventListener('click', function() { + mixer.paginate({limit: 10}); +}); + +var dataset = [{ + id: '12', + category: '1' +}]; + +var dataMixer = mixitup('#sandbox-2', { + load: { + dataset: dataset + }, + data: { + uidKey: 'id', + dirtyCheck: true + }, + render: { + target: function(data) { + return `
`; + } + } +}); + +dataset.push({ + id: '14', + category: '2' +}, { + id: '2', + category: '3' +}); + +var first; + +dataMixer.dataset(dataset) + .then(function() { + dataset.reverse(); + + return dataMixer.dataset(dataset); + }) + .then(function() { + first = dataset.shift(); + + return dataMixer.dataset(dataset); + }) + .then(function() { + dataset.push(first); + + return dataMixer.dataset(dataset); + }) + .then(function() { + dataset = [dataset[2], dataset[0]]; + + return dataMixer.dataset(dataset); + }) + .then(function() { + dataset[1] = { + id: '14', + category: '4' + }; + + return dataMixer.dataset(dataset); + }); \ No newline at end of file diff --git a/mixitup-3.3.1/tests/functional/styles.css b/mixitup-3.3.1/tests/functional/styles.css new file mode 100644 index 0000000..2e55153 --- /dev/null +++ b/mixitup-3.3.1/tests/functional/styles.css @@ -0,0 +1,136 @@ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} + +body { + background: #eee; +} + +.mixitup-control, +.mixitup-pager { + cursor: pointer; + display: inline-block; + padding: .5rem; + background: #eee; +} + +.mixitup-control-active, +.mixitup-pager-active { + font-weight: bold; +} + +.mixitup-pager-disabled { + opacity: .8; +} + +.mix { + vertical-align: top; + background: skyblue; + border-radius: 5px; + margin: 2rem; + color: transparent; + + transition: opacity 300ms; + + -webkit-user-select: none; +} + +.mix, +.gap { + display: inline-block; + width: 6%; + margin: 0 2rem; +} + +.mix { + margin: 2rem; +} + +.mix:before { + content: ''; + display: inline-block; + padding-top: 100%; +} + +.mixitup-target-placeholder { + background: transparent !important; + border: 2px dashed #ccc; + box-sizing: border-box; +} + +.mixitup-target-dragging { + opacity: .5; +} + +.mixitup-target-closest { + border: 3px solid orange; +} + +.sandbox { + overflow: hidden; + text-align: justify; + background: #333; + min-height: 2rem; +} + +.sandbox:after { + content: ''; + width: 100%; + display: inline-block; +} + +.sandbox__mixitup .mix { + +} + +.mix.cat-2 { + background: violet; +} + +.mix.cat-3 { + background: yellow; +} + +.mix.cat-4 { + background: aquamarine; +} \ No newline at end of file diff --git a/mixitup-3.3.1/tests/mock/dataset.json b/mixitup-3.3.1/tests/mock/dataset.json new file mode 100644 index 0000000..85ebc5a --- /dev/null +++ b/mixitup-3.3.1/tests/mock/dataset.json @@ -0,0 +1,38 @@ +[ + { + "id": "target-1", + "categories": ["a"], + "published": "20161102", + "views": 100 + }, + { + "id": "target-2", + "categories": ["a"], + "published": "20130501", + "views": 54 + }, + { + "id": "target-3", + "categories": ["b"], + "published": "20121231", + "views": 3 + }, + { + "id": "target-4", + "categories": ["b"], + "published": "20160407", + "views": 62 + }, + { + "id": "target-5", + "categories": ["c"], + "published": "20160820", + "views": 54 + }, + { + "id": "target-6", + "categories": ["a", "c"], + "published": "20151020", + "views": 95 + } +] \ No newline at end of file diff --git a/mixitup-3.3.1/tests/mock/dom.js b/mixitup-3.3.1/tests/mock/dom.js new file mode 100644 index 0000000..c76fd72 --- /dev/null +++ b/mixitup-3.3.1/tests/mock/dom.js @@ -0,0 +1,118 @@ +'use strict'; + +require('jsdom-global')(); + +const renderElement = (html) => { + const temp = document.createElement('div'); + + temp.innerHTML = html; + + return temp.firstElementChild; +}; + +module.exports = { + getContainer() { + return renderElement('
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
'); + }, + + getEmptyContainer() { + return renderElement('
'); + }, + + getFilterControls() { + return renderElement('
' + + '
All
' + + '
None
' + + '
Category A
' + + '
Category B
' + + '
Category C
' + + '
Category D
' + + '
Category A OR B
' + + '
Category A AND C
' + + '
Category A (attribute)
' + + '
Category A OR B (attribute)
' + + '
Category A AND C (attribute)
' + + '
Category A
' + + '
Category B
' + + '
Category C
' + + '
'); + }, + + getFilterControl() { + return renderElement('
Category D
'); + }, + + getToggleControl() { + return renderElement('
Category B
'); + }, + + getSortControl() { + return renderElement('
Views (desc) Published (asc)
'); + }, + + getSortControls() { + return renderElement('
' + + '
Default
' + + '
Default Ascending
' + + '
Default Descending
' + + '
Random
' + + '
Published Date
' + + '
Views
' + + '
Published (asc) Views (desc)
' + + '
'); + }, + + getMultimixControls() { + return renderElement('
' + + '
All / Default
' + + '
Category B / Published
' + + '
'); + }, + + getTotalWhitespace(html) { + let re = /[>? ]( )[ 'category-' + category).join(' '); + } + + get categoryList() { + return this.categories.join(' '); + } + }, + + ITEM_TEMPLATE: '
', + + ITEM_TEMPLATE_ALT: '
' +}; \ No newline at end of file diff --git a/mixitup-3.3.1/tests/mock/extension.js b/mixitup-3.3.1/tests/mock/extension.js new file mode 100644 index 0000000..806d46b --- /dev/null +++ b/mixitup-3.3.1/tests/mock/extension.js @@ -0,0 +1,37 @@ +(function(window) { + 'use strict'; + + var mixitupMockExtension = function(mixitup) { + var h = mixitup.h; + + if ( + !mixitup.CORE_VERSION || + !h.compareVersions(mixitupMockExtension.REQUIRE_CORE_VERSION, mixitup.CORE_VERSION) + ) { + throw new Error( + '[MixItUp-MockExtension] MixItUp MockExtension v' + + mixitupMockExtension.EXTENSION_VERSION + + ' requires at least MixItUp v' + + mixitupMockExtension.REQUIRE_CORE_VERSION + ); + } + + }; + + mixitupMockExtension.TYPE = 'mixitup-extension'; + mixitupMockExtension.NAME = 'mixitup-mock-extension'; + mixitupMockExtension.EXTENSION_VERSION = '1.0.0'; + mixitupMockExtension.REQUIRE_CORE_VERSION = '3.0.0'; + + if (typeof exports === 'object' && typeof module === 'object') { + module.exports = mixitupMockExtension; + } else if (typeof define === 'function' && define.amd) { + define(function() { + return mixitupMockExtension; + }); + } else if (window.mixitup && typeof window.mixitup === 'function') { + mixitupMockExtension(window.mixitup); + } else { + console.error('[MixItUp-MockExtension] MixItUp core not found'); + } +})(window); \ No newline at end of file diff --git a/mixitup-3.3.1/tests/unit/callbacks.js b/mixitup-3.3.1/tests/unit/callbacks.js new file mode 100644 index 0000000..e9a6b4d --- /dev/null +++ b/mixitup-3.3.1/tests/unit/callbacks.js @@ -0,0 +1,175 @@ +'use strict'; + +require('jsdom-global')(); + +const chai = require('chai'); +const dom = require('../mock/dom'); +const mixitup = require('../../dist/mixitup.js'); + +describe('mixitup()', () => { + it('should accept an `onMixStart` callback, invoked at the start of operations', () => { + let container = dom.getContainer(); + let wasCalled = false; + + let mixer = mixitup(container, { + callbacks: { + onMixStart: (state, futureState) => { + chai.assert.instanceOf(state, mixitup.State); + chai.assert.instanceOf(futureState, mixitup.State); + chai.assert.notEqual(state.totalShow, futureState.totalShow); + + wasCalled = true; + } + } + }); + + return mixer.hide() + .then(() => chai.assert.equal(wasCalled, true)); + }); + + it('should accept an `onMixBusy` callback, called if simulataneous operation is rejected', () => { + let container = dom.getContainer(); + let wasCalled = false; + + let mixer = mixitup(container, { + debug: { + fauxAsync: true + }, + animation: { + duration: 200, + queue: false + }, + callbacks: { + onMixBusy: (state) => { + chai.assert.instanceOf(state, mixitup.State); + + wasCalled = true; + } + } + }); + + mixer.hide(); + + return mixer.show() + .then(() => chai.assert.equal(wasCalled, true)); + }); + + it('should accept an `onMixEnd` callback, called at the end of an operation', () => { + let container = dom.getContainer(); + let wasCalled = false; + let endState; + + let mixer = mixitup(container, { + callbacks: { + onMixEnd: (state) => { + endState = state; + + chai.assert.instanceOf(state, mixitup.State); + + wasCalled = true; + } + } + }); + + return mixer.hide() + .then(state => { + chai.assert.equal(state.totalShow, endState.totalShow); + chai.assert.equal(wasCalled, true); + }); + }); + + it('should accept an `onMixFail` callback, called when a filter operation does not match any targets', () => { + let container = dom.getContainer(); + let wasCalled = false; + + let mixer = mixitup(container, { + callbacks: { + onMixFail: (state) => { + chai.assert.instanceOf(state, mixitup.State); + + wasCalled = true; + } + } + }); + + return mixer.filter('.category-x') + .then(() => { + chai.assert.equal(wasCalled, true); + }); + }); + + it('should accept an `onMixClick` callback invoked when a control is clicked', () => { + let frag = document.createDocumentFragment(); + let container = dom.getContainer(); + let controls = dom.getFilterControls(); + let wasCalled = false; + let filter = controls.querySelector('[data-filter="none"]'); + + container.insertBefore(controls, container.children[0]); + + frag.appendChild(container); + + let mixer = mixitup(container, { + controls: { + scope: 'local' + }, + callbacks: { + onMixClick: function(state, originalEvent) { + var self = this; + + chai.assert.instanceOf(state, mixitup.State); + chai.assert.instanceOf(originalEvent, window.MouseEvent); + chai.assert.equal(self, filter); + + wasCalled = true; + } + } + }, frag); + + filter.click(); + + return Promise.resolve() + .then(() => { + chai.assert.equal(wasCalled, true); + + mixer.destroy(); + }); + }); + + it('should accept an `onMixClick` callback which can be cancelled by returning false', () => { + let frag = document.createDocumentFragment(); + let container = dom.getContainer(); + let controls = dom.getFilterControls(); + let wasCalled = false; + let filter = controls.querySelector('[data-filter="none"]'); + + container.insertBefore(controls, container.children[0]); + + frag.appendChild(container); + + let mixer = mixitup(container, { + controls: { + scope: 'local' + }, + callbacks: { + onMixClick: () => { + return false; + }, + onMixEnd: () => { + // Will not be called + + wasCalled = true; + } + } + }, frag); + + filter.click(); + + return Promise.resolve() + .then(() => { + chai.assert.equal(wasCalled, false); + + mixer.destroy(); + }); + }); +}); \ No newline at end of file diff --git a/mixitup-3.3.1/tests/unit/controls-filter.js b/mixitup-3.3.1/tests/unit/controls-filter.js new file mode 100644 index 0000000..e877a92 --- /dev/null +++ b/mixitup-3.3.1/tests/unit/controls-filter.js @@ -0,0 +1,201 @@ +'use strict'; + +require('jsdom-global')(); + +const chai = require('chai'); +const dom = require('../mock/dom'); +const mixitup = require('../../dist/mixitup.js'); + +chai.use(require('chai-shallow-deep-equal')); +chai.use(require('chai-as-promised')); + +describe('Controls', () => { + describe('Filter', () => { + const frag = document.createDocumentFragment(); + const container = dom.getContainer(); + const controls = dom.getFilterControls(); + + container.insertBefore(controls, container.children[0]); + + frag.appendChild(container); + + const mixer = mixitup(container, { + controls: { + scope: 'local' + } + }, frag); + + after(() => mixer.destroy()); + + it('should detect nested filter controls and set active states upon instantiation', () => { + const filter = controls.querySelector('[data-filter="all"]'); + + chai.assert.isOk(filter.matches('.mixitup-control-active')); + }); + + it('should map filter controls with value "none" to the selector ""', () => { + const filter = controls.querySelector('[data-filter="none"]'); + + filter.click(); + + const state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, ''); + chai.assert.equal(state.totalShow, 0); + chai.assert.isOk(filter.matches('.mixitup-control-active')); + }); + + it('should map filter controls with value "all" to the target selector', () => { + const filter = controls.querySelector('[data-filter="all"]'); + + filter.click(); + + const state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '.mix'); + chai.assert.equal(state.totalHide, 0); + chai.assert.isOk(filter.matches('.mixitup-control-active')); + }); + + it('should accept filter controls with a selector value', () => { + const filter = controls.querySelector('[data-filter=".category-a"]'); + const totalMatching = container.querySelectorAll('.category-a').length; + + filter.click(); + + const state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '.category-a'); + chai.assert.equal(state.totalShow, totalMatching); + chai.assert.isOk(filter.matches('.mixitup-control-active')); + }); + + it('should accept filter controls with a compound OR selector', () => { + const filter = controls.querySelector('[data-filter=".category-a, .category-b"]'); + const totalMatching = container.querySelectorAll('.category-a, .category-b').length; + + filter.click(); + + const state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '.category-a, .category-b'); + chai.assert.equal(state.totalShow, totalMatching); + chai.assert.isOk(filter.matches('.mixitup-control-active')); + }); + + it('should accept filter controls with a compound AND selector', () => { + const filter = controls.querySelector('[data-filter=".category-a.category-c"]'); + const totalMatching = container.querySelectorAll('.category-a.category-c').length; + + filter.click(); + + const state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '.category-a.category-c'); + chai.assert.equal(state.totalShow, totalMatching); + chai.assert.isOk(filter.matches('.mixitup-control-active')); + }); + + it('should accept filter controls with an attribute selector value', () => { + const filter = controls.querySelector('.mixitup_control__attr-a'); + const totalMatching = container.querySelectorAll('[data-category="a"]').length; + + filter.click(); + + const state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '[data-category="a"]'); + chai.assert.equal(state.totalShow, totalMatching); + chai.assert.isOk(filter.matches('.mixitup-control-active')); + }); + + it('should accept filter controls with an attribute selector value', () => { + const filter = controls.querySelector('.mixitup_control__attr-a'); + const totalMatching = container.querySelectorAll('[data-category="a"]').length; + + filter.click(); + + const state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '[data-category="a"]'); + chai.assert.equal(state.totalShow, totalMatching); + chai.assert.isOk(filter.matches('.mixitup-control-active')); + }); + + it('should accept filter controls with a compound OR attribute selector value', () => { + const filter = controls.querySelector('.mixitup_control__attr-a-or-b'); + const totalMatching = container.querySelectorAll('[data-category="a"], [data-category="b"]').length; + + filter.click(); + + const state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '[data-category="a"], [data-category="b"]'); + chai.assert.equal(state.totalShow, totalMatching); + chai.assert.isOk(filter.matches('.mixitup-control-active')); + }); + + it('should accept filter controls with a compound AND attribute selector value', () => { + const filter = controls.querySelector('.mixitup_control__attr-a-and-c'); + const totalMatching = container.querySelectorAll('[data-category="a"][data-category="c"]').length; + + filter.click(); + + const state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '[data-category="a"][data-category="c"]'); + chai.assert.equal(state.totalShow, totalMatching); + chai.assert.isOk(filter.matches('.mixitup-control-active')); + }); + + it ('should allow a single set of controls to control multiple mixer instance simultanously', () => { + const frag = document.createDocumentFragment(); + + const container1 = dom.getContainer(); + const container2 = dom.getContainer(); + const controls = dom.getFilterControls(); + + frag.appendChild(controls); + frag.appendChild(container1); + frag.appendChild(container2); + + const mixer1 = mixitup(container1, {}, frag); + const mixer2 = mixitup(container2, {}, frag); + + after(() => { + mixer1.destroy(); + mixer2.destroy(); + }); + + const filter = controls.querySelector('[data-filter=".category-a"]'); + + filter.click(); + + chai.assert.equal(mixer1.getState().activeFilter.selector, '.category-a'); + chai.assert.equal(mixer2.getState().activeFilter.selector, '.category-a'); + chai.assert.isOk(filter.matches('.mixitup-control-active')); + }); + + it ('should activate the appropriate controls on load for a single selector', () => { + const frag = document.createDocumentFragment(); + + const container = dom.getContainer(); + const controls = dom.getFilterControls(); + + frag.appendChild(controls); + frag.appendChild(container); + + const mixer = mixitup(container, { + load: { + filter: '.category-a' + } + }, frag); + + after(() => mixer.destroy()); + + const filter = controls.querySelector('[data-filter=".category-a"]'); + + chai.assert.isTrue(filter.classList.contains('mixitup-control-active')); + }); + }); +}); \ No newline at end of file diff --git a/mixitup-3.3.1/tests/unit/controls-live.js b/mixitup-3.3.1/tests/unit/controls-live.js new file mode 100644 index 0000000..9984ebd --- /dev/null +++ b/mixitup-3.3.1/tests/unit/controls-live.js @@ -0,0 +1,156 @@ +'use strict'; + +require('jsdom-global')(); + +const chai = require('chai'); +const dom = require('../mock/dom'); +const mixitup = require('../../dist/mixitup.js'); + +chai.use(require('chai-shallow-deep-equal')); +chai.use(require('chai-as-promised')); + +describe('Controls', () => { + describe('Live', () => { + let frag = document.createDocumentFragment(); + let container = dom.getContainer(); + let filterControls = dom.getFilterControls(); + let sortControls = dom.getSortControls(); + + container.insertBefore(filterControls, container.children[0]); + container.insertBefore(sortControls, filterControls); + + frag.appendChild(container); + + let mixer = mixitup(container, { + controls: { + scope: 'local', + live: true + } + }, frag); + + after(() => mixer.destroy()); + + it('should detect nested controls and set active states upon instantiation', () => { + let filter = filterControls.querySelector('[data-filter="all"]'); + + chai.assert.isOk(filter.matches('.mixitup-control-active')); + }); + + it('should allow new filter controls to be added', () => { + let control = dom.getFilterControl(); + let totalMatching = container.querySelectorAll('.category-d').length; + + filterControls.appendChild(control); + + control.click(); + + let state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '.category-d'); + chai.assert.equal(state.totalShow, totalMatching); + chai.assert.isOk(control.matches('.mixitup-control-active')); + }); + + it('should allow new toggle controls to be added', () => { + let control = dom.getToggleControl(); + let totalMatching = container.querySelectorAll('.category-b, .category-d').length; + + filterControls.appendChild(control); + + control.click(); + + let state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '.category-d, .category-b'); + chai.assert.equal(state.totalShow, totalMatching); + chai.assert.isOk(control.matches('.mixitup-control-active')); + }); + + it('should allow new sort controls to be added', () => { + let control = dom.getSortControl(); + + sortControls.appendChild(control); + + control.click(); + + let state = mixer.getState(); + + chai.assert.equal(state.activeSort.sortString, 'views:desc'); + chai.assert.isOk(state.activeSort.next); + chai.assert.equal(state.activeSort.next.sortString, 'published:asc'); + chai.assert.isOk(control.matches('.mixitup-control-active')); + }); + + it ('should allow a single set of filter controls to control multiple mixer instance simultanously', () => { + let frag = document.createDocumentFragment(); + + let container1 = dom.getContainer(); + let container2 = dom.getContainer(); + let controls = dom.getFilterControls(); + let config = { + controls: { + live: true + } + }; + + frag.appendChild(controls); + frag.appendChild(container1); + frag.appendChild(container2); + + let mixer1 = mixitup(container1, config, frag); + let mixer2 = mixitup(container2, config, frag); + + after(() => { + mixer1.destroy(); + mixer2.destroy(); + }); + + let filter = controls.querySelector('[data-filter=".category-a"]'); + + filter.click(); + + chai.assert.equal(mixer1.getState().activeFilter.selector, '.category-a'); + chai.assert.equal(mixer2.getState().activeFilter.selector, '.category-a'); + chai.assert.isOk(filter.matches('.mixitup-control-active')); + }); + + it ('should restrict control clicks to only those matching is a control selector is defined', () => { + let frag = document.createDocumentFragment(); + + let container = dom.getContainer(); + let controls = dom.getFilterControls(); + + let config = { + controls: { + live: true + }, + selectors: { + control: '.mixitup-control-restrict' + } + }; + + frag.appendChild(controls); + frag.appendChild(container); + + let mixer = mixitup(container, config, frag); + + after(() => { + mixer.destroy(); + }); + + let filter1 = controls.querySelector('[data-filter=".category-a"]'); + + filter1.classList.add('mixitup-control-restrict'); + + filter1.click(); + + let filter2 = controls.querySelector('[data-filter=".category-b"]'); + + filter2.click(); + + chai.assert.equal(mixer.getState().activeFilter.selector, '.category-a'); + chai.assert.isOk(filter1.matches('.mixitup-control-active')); + chai.assert.isNotOk(filter2.matches('.mixitup-control-active')); + }); + }); +}); \ No newline at end of file diff --git a/mixitup-3.3.1/tests/unit/controls-multimix.js b/mixitup-3.3.1/tests/unit/controls-multimix.js new file mode 100644 index 0000000..572cd99 --- /dev/null +++ b/mixitup-3.3.1/tests/unit/controls-multimix.js @@ -0,0 +1,49 @@ +'use strict'; + +require('jsdom-global')(); + +const chai = require('chai'); +const dom = require('../mock/dom'); +const mixitup = require('../../dist/mixitup.js'); + +chai.use(require('chai-shallow-deep-equal')); +chai.use(require('chai-as-promised')); + +describe('Controls', () => { + describe('Multimix', () => { + let frag = document.createDocumentFragment(); + let container = dom.getContainer(); + let controls = dom.getMultimixControls(); + + container.insertBefore(controls, container.children[0]); + + frag.appendChild(container); + + let mixer = mixitup(container, { + controls: { + scope: 'local' + } + }, frag); + + after(() => mixer.destroy()); + + it('should detect nested filter controls and set active states upon instantiation', () => { + let control = controls.querySelector('[data-filter="all"][data-sort="default:asc"]'); + + chai.assert.isOk(control.matches('.mixitup-control-active')); + }); + + it('should read filter and sort actions simultaneously', () => { + let control = controls.querySelector('[data-filter=".category-b"][data-sort="published"]'); + + control.click(); + + let state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '.category-b'); + chai.assert.equal(state.activeSort.sortString, 'published'); + + chai.assert.isOk(control.matches('.mixitup-control-active')); + }); + }); +}); \ No newline at end of file diff --git a/mixitup-3.3.1/tests/unit/controls-sort.js b/mixitup-3.3.1/tests/unit/controls-sort.js new file mode 100644 index 0000000..557d4f2 --- /dev/null +++ b/mixitup-3.3.1/tests/unit/controls-sort.js @@ -0,0 +1,76 @@ +'use strict'; + +require('jsdom-global')(); + +const chai = require('chai'); +const dom = require('../mock/dom'); +const mixitup = require('../../dist/mixitup.js'); + +chai.use(require('chai-shallow-deep-equal')); +chai.use(require('chai-as-promised')); + +describe('Controls', () => { + describe('Sort', () => { + let frag = document.createDocumentFragment(); + let container = dom.getContainer(); + let controls = dom.getSortControls(); + + container.insertBefore(controls, container.children[0]); + + frag.appendChild(container); + + let mixer = mixitup(container, { + controls: { + scope: 'local' + } + }, frag); + + after(() => mixer.destroy()); + + it('should detect nested sort controls and set active states upon instantiation', () => { + let control1 = controls.querySelector('[data-sort="default"]'); + let control2 = controls.querySelector('[data-sort="default:asc"]'); + + chai.assert.isOk(control1.matches('.mixitup-control-active')); + chai.assert.isOk(control2.matches('.mixitup-control-active')); + }); + + it('should handle sort control clicks with a single sortString value', () => { + let control = controls.querySelector('[data-sort="default:desc"]'); + + control.click(); + + let state = mixer.getState(); + + chai.assert.isOk(control.matches('.mixitup-control-active')); + chai.assert.equal(state.activeSort.sortString, 'default:desc'); + chai.assert.equal(state.activeSort.attribute, ''); + chai.assert.equal(state.activeSort.order, 'desc'); + }); + + it('should handle sort control clicks with "random" value', () => { + let control = controls.querySelector('[data-sort="random"]'); + + control.click(); + + let state = mixer.getState(); + + chai.assert.isOk(control.matches('.mixitup-control-active')); + chai.assert.equal(state.activeSort.sortString, 'random'); + chai.assert.equal(state.activeSort.attribute, ''); + chai.assert.equal(state.activeSort.order, 'random'); + }); + + it('should activate buttons in response to matching API calls', () => { + let control = controls.querySelector('[data-sort="published:asc views:desc"]'); + + return mixer.sort('published:asc views:desc') + .then(state => { + chai.assert.isOk(control.matches('.mixitup-control-active')); + chai.assert.equal(state.activeSort.sortString, 'published:asc'); + chai.assert.isOk(state.activeSort.next); + chai.assert.equal(state.activeSort.next.sortString, 'views:desc'); + }); + }); + }); +}); \ No newline at end of file diff --git a/mixitup-3.3.1/tests/unit/controls-toggle.js b/mixitup-3.3.1/tests/unit/controls-toggle.js new file mode 100644 index 0000000..93450bd --- /dev/null +++ b/mixitup-3.3.1/tests/unit/controls-toggle.js @@ -0,0 +1,304 @@ +'use strict'; + +require('jsdom-global')(); + +const chai = require('chai'); +const dom = require('../mock/dom'); +const mixitup = require('../../dist/mixitup.js'); + +chai.use(require('chai-shallow-deep-equal')); +chai.use(require('chai-as-promised')); + +describe('Controls', () => { + describe('Toggle', () => { + describe('OR', () => { + let frag = document.createDocumentFragment(); + let container = dom.getContainer(); + let controls = dom.getFilterControls(); + + container.insertBefore(controls, container.children[0]); + + frag.appendChild(container); + + let mixer = mixitup(container, { + controls: { + scope: 'local' + } + }); + + after(() => mixer.destroy()); + + it('should accept toggle controls with a selector value', () => { + return mixer.hide() + .then(() => { + let toggle = controls.querySelector('[data-toggle=".category-a"]'); + + let totalMatching = container.querySelectorAll('.category-a').length; + + toggle.click(); + + let state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '.category-a'); + chai.assert.equal(state.totalShow, totalMatching); + chai.assert.isOk(toggle.matches('.mixitup-control-active')); + }); + }); + + it('should build up a compound selector as toggles are activated', () => { + let toggleA = controls.querySelector('[data-toggle=".category-a"]'); + let toggleB = controls.querySelector('[data-toggle=".category-b"]'); + + let totalMatching = container.querySelectorAll('.category-a, .category-b').length; + + toggleB.click(); + + let state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '.category-a, .category-b'); + chai.assert.equal(state.totalShow, totalMatching); + chai.assert.isOk(toggleA.matches('.mixitup-control-active')); + chai.assert.isOk(toggleB.matches('.mixitup-control-active')); + }); + + it('should break down a compound selector as toggles are deactivated', () => { + let toggle = controls.querySelector('[data-toggle=".category-a"]'); + + let totalMatching = container.querySelectorAll('.category-b').length; + + toggle.click(); + + let state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '.category-b'); + chai.assert.equal(state.totalShow, totalMatching); + chai.assert.isNotOk(toggle.matches('.mixitup-control-active')); + }); + + it('should return to "all" when all toggles are deactivated', () => { + let toggle = controls.querySelector('[data-toggle=".category-b"]'); + + toggle.click(); + + let state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '.mix'); + chai.assert.equal(state.totalHide, 0); + chai.assert.isNotOk(toggle.matches('.mixitup-control-active')); + }); + + it ('should activate the appropriate toggle controls on load for an OR compound selector', () => { + const frag = document.createDocumentFragment(); + + const container = dom.getContainer(); + const controls = dom.getFilterControls(); + + frag.appendChild(controls); + frag.appendChild(container); + + const mixer = mixitup(container, { + load: { + filter: '.category-a, .category-c' + } + }, frag); + + after(() => mixer.destroy()); + + const toggleA = controls.querySelector('[data-toggle=".category-a"]'); + const toggleC = controls.querySelector('[data-toggle=".category-c"]'); + + chai.assert.isTrue(toggleA.classList.contains('mixitup-control-active')); + chai.assert.isTrue(toggleC.classList.contains('mixitup-control-active')); + }); + }); + + describe('AND', () => { + let frag = document.createDocumentFragment(); + let container = dom.getContainer(); + let controls = dom.getFilterControls(); + + container.insertBefore(controls, container.children[0]); + + frag.appendChild(container); + + let mixer = mixitup(container, { + controls: { + scope: 'local', + toggleLogic: 'AND' + } + }); + + after(() => mixer.destroy()); + + it('should accept toggle controls with a selector value', () => { + return mixer.hide() + .then(() => { + let toggle = controls.querySelector('[data-toggle=".category-a"]'); + + let totalMatching = container.querySelectorAll('.category-a').length; + + toggle.click(); + + let state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '.category-a'); + chai.assert.equal(state.totalShow, totalMatching); + chai.assert.isOk(toggle.matches('.mixitup-control-active')); + }); + }); + + it('should build up a compound selector as toggles are activated', () => { + let toggleA = controls.querySelector('[data-toggle=".category-a"]'); + let toggleB = controls.querySelector('[data-toggle=".category-c"]'); + + let totalMatching = container.querySelectorAll('.category-a.category-c').length; + + toggleB.click(); + + let state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '.category-a.category-c'); + chai.assert.equal(state.totalShow, totalMatching); + chai.assert.isOk(toggleA.matches('.mixitup-control-active')); + chai.assert.isOk(toggleB.matches('.mixitup-control-active')); + }); + + it('should break down a compound selector as toggles are deactivated', () => { + let toggle = controls.querySelector('[data-toggle=".category-a"]'); + + let totalMatching = container.querySelectorAll('.category-c').length; + + toggle.click(); + + let state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '.category-c'); + chai.assert.equal(state.totalShow, totalMatching); + chai.assert.isNotOk(toggle.matches('.mixitup-control-active')); + }); + + it('should return to "all" when all toggles are deactivated', () => { + let toggle = controls.querySelector('[data-toggle=".category-c"]'); + + toggle.click(); + + let state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '.mix'); + chai.assert.equal(state.totalHide, 0); + chai.assert.isNotOk(toggle.matches('.mixitup-control-active')); + }); + + it('should allow toggles to activated via the API', () => { + let totalMatching = container.querySelectorAll('.category-a.category-c').length; + + mixer.toggleOn('.category-a'); + mixer.toggleOn('.category-c'); + + let state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '.category-a.category-c'); + chai.assert.equal(state.totalShow, totalMatching); + }); + + + it ('should activate the appropriate toggle controls on load for an AND compound selector', () => { + const frag = document.createDocumentFragment(); + + const container = dom.getContainer(); + const controls = dom.getFilterControls(); + + frag.appendChild(controls); + frag.appendChild(container); + + const mixer = mixitup(container, { + controls: { + toggleLogic: 'and' + }, + load: { + filter: '.category-a.category-c' + } + }, frag); + + after(() => mixer.destroy()); + + const toggleA = controls.querySelector('[data-toggle=".category-a"]'); + const toggleC = controls.querySelector('[data-toggle=".category-c"]'); + + chai.assert.isTrue(toggleA.classList.contains('mixitup-control-active')); + chai.assert.isTrue(toggleC.classList.contains('mixitup-control-active')); + }); + }); + + describe('Defaults', () => { + it('should default to "none" when all toggles are deactivated and toggleDefault is set to "none"', () => { + let frag = document.createDocumentFragment(); + let container = dom.getContainer(); + let controls = dom.getFilterControls(); + + container.insertBefore(controls, container.children[0]); + + frag.appendChild(container); + + let mixer = mixitup(container, { + controls: { + scope: 'local', + toggleDefault: 'none' + } + }); + + return mixer.hide() + .then(() => { + let toggle = controls.querySelector('[data-toggle=".category-a"]'); + + // on + toggle.click(); + + // off + toggle.click(); + + let state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, ''); + chai.assert.equal(state.totalShow, 0); + chai.assert.isNotOk(toggle.matches('.mixitup-control-active')); + + mixer.destroy(); + }); + }); + + it('should default to "all" when all toggles are deactivated', () => { + let container = dom.getContainer(); + let controls = dom.getFilterControls(); + + container.insertBefore(controls, container.children[0]); + + document.body.appendChild(container); + + let mixer = mixitup(container, { + controls: { + scope: 'local', + toggleDefault: 'all' + } + }); + + return mixer.hide() + .then(() => { + let toggle = controls.querySelector('[data-toggle=".category-a"]'); + + // on + toggle.click(); + + // off + toggle.click(); + + let state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '.mix'); + chai.assert.equal(state.totalHide, 0); + chai.assert.isNotOk(toggle.matches('.mixitup-control-active')); + }); + }); + }); + }); +}); \ No newline at end of file diff --git a/mixitup-3.3.1/tests/unit/extension.js b/mixitup-3.3.1/tests/unit/extension.js new file mode 100644 index 0000000..c450a44 --- /dev/null +++ b/mixitup-3.3.1/tests/unit/extension.js @@ -0,0 +1,19 @@ +'use strict'; + +require('jsdom-global')(); + +const chai = require('chai'); +const dom = require('../mock/dom'); +const extension = require('../mock/extension'); +const mixitup = require('../../dist/mixitup.js'); + +chai.use(require('chai-shallow-deep-equal')); +chai.use(require('chai-as-promised')); + +describe('Extension', () => { + it('should register itself via the mixitup.use() method', () => { + mixitup.use(extension); + + chai.assert.isOk(mixitup.extensions[extension.NAME]); + }); +}); \ No newline at end of file diff --git a/mixitup-3.3.1/tests/unit/factory.js b/mixitup-3.3.1/tests/unit/factory.js new file mode 100644 index 0000000..d589555 --- /dev/null +++ b/mixitup-3.3.1/tests/unit/factory.js @@ -0,0 +1,294 @@ +'use strict'; + +require('jsdom-global')(); + +const chai = require('chai'); +const dom = require('../mock/dom'); +const dataset = require('../mock/dataset'); +const mixitup = require('../../dist/mixitup.js'); + +describe('mixitup()', () => { + it('should throw an error if no container reference', () => { + chai.assert.throws(() => mixitup(), Error, mixitup.messages.errorFactoryInvalidContainer()); + }); + + it('should throw an error if a null container reference is passed', () => { + chai.assert.throws(() => mixitup(null), Error, mixitup.messages.errorFactoryInvalidContainer()); + }); + + it('should throw an error if an invalid container reference is passed', () => { + chai.assert.throws(() => mixitup({}), Error, mixitup.messages.errorFactoryInvalidContainer()); + }); + + it('should throw an error if an invalid reference or selector is passed', function() { + chai.assert.throws(() => mixitup(false), Error, mixitup.messages.errorFactoryInvalidContainer()); + }); + + it('should throw an error if an invalid configuration option is passed', function() { + let container = dom.getContainer(); + + chai.assert.throws(() => { + mixitup(container, { + animations: {} + }); + }, TypeError); + }); + + it('should accept an element reference as a container', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + + chai.assert.isOk(mixer); + + mixer.destroy(); + }); + + it('should accept a container selector', () => { + let frag = document.createDocumentFragment(); + let container = dom.getContainer(); + + frag.appendChild(container); + + let mixer = mixitup('.mixitup-container', {}, frag); + let state = mixer.getState(); + + chai.assert.isOk(mixer); + chai.assert.equal(state.container, frag.querySelector('.mixitup-container')); + + mixer.destroy(); + }); + + it('should accept a container and valid configuration object', function() { + let container = dom.getContainer(); + let mixer = mixitup(container, { + selectors: { + target: '[data-ref="mix"]' + }, + controls: { + enable: false + } + }); + + let state = mixer.getState(); + + chai.assert.isOk(mixer); + chai.assert.equal(state.activeFilter.selector, '[data-ref="mix"]'); + + mixer.destroy(); + }); + + it('should throw an error if the container selector yields no element', () => { + chai.assert.throws(() => mixitup('.invalid-container-selector'), Error, mixitup.messages.errorFactoryContainerNotFound()); + }); + + it('should return an instance of a facade by default', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + + chai.assert.instanceOf(mixer, mixitup.Facade); + + mixer.destroy(); + }); + + it('should return an instance of a mixer if debug mode enabled', () => { + let container = dom.getContainer(); + let mixer = mixitup(container, { + debug: { + enable: true + }, + controls: { + enable: false + } + }); + + chai.assert.instanceOf(mixer, mixitup.Mixer); + + mixer.destroy(); + }); + + it('should return a single instance of a mixer, wrapping the first element if multiple elements passed', () => { + let elementList = [ + dom.getContainer(), + dom.getContainer() + ]; + + let mixer = mixitup(elementList, { + debug: { + enable: true + }, + controls: { + enable: false + } + }); + + chai.assert.instanceOf(mixer, mixitup.Mixer); + chai.assert.equal(mixer.getState().container, elementList[0]); + + mixer.destroy(); + }); + + it('should return an instance of a collection if multiple elements passed and `returnCollection` specified', () => { + let elementList = [ + dom.getContainer(), + dom.getContainer() + ]; + + let collection = mixitup(elementList, void(0), void(0), true); + + chai.assert.instanceOf(collection, mixitup.Collection); + chai.assert.instanceOf(collection[0], mixitup.Facade); + chai.assert.instanceOf(collection[1], mixitup.Facade); + + collection.mixitup('destroy'); + }); + + it('should add a unique ID to the container if no ID present', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let state = mixer.getState(); + + chai.assert.equal(container.id, state.id); + + mixer.destroy(); + }); + + it('should use any existing ID on the container as the mixer ID if present', () => { + let container = dom.getContainer(); + let id = 'test-id'; + + container.id = id; + + let mixer = mixitup(container); + let state = mixer.getState(); + + chai.assert.equal(id, state.id); + + mixer.destroy(); + }); + + it('should not allow multiple instance to be instantiated on a single container', () => { + let container = dom.getContainer(); + + let mixer1 = mixitup(container, { + debug: { + enable: true + }, + controls: { + enable: false + } + }); + + let mixer2 = mixitup(container, { + debug: { + enable: true, + showWarnings: false + }, + controls: { + enable: false + } + }); + + let facade = mixitup(container); + + chai.assert.equal(mixer1, mixer2); + chai.assert.notEqual(facade, mixer1); + chai.assert.notEqual(facade, mixer2); + + mixer1.destroy(); + }); + + it('should respect a `load.filter` configuration option of none', () => { + let container = dom.getContainer(); + let mixer = mixitup(container, { + load: { + filter: 'none' + } + }); + + let state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, ''); + chai.assert.equal(state.totalShow, 0); + chai.assert.equal(state.hide[0].style.display, 'none'); + + mixer.destroy(); + }); + + it('should respect a `load.filter` configuration option of a single selector', () => { + let container = dom.getContainer(); + let mixer = mixitup(container, { + load: { + filter: '.category-a' + } + }); + + let state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '.category-a'); + chai.assert.equal(state.totalShow, 3); + + mixer.destroy(); + }); + + it('should respect a `load.filter` configuration option of a compound selector', () => { + let container = dom.getContainer(); + let mixer = mixitup(container, { + load: { + filter: '.category-a.category-c' + } + }); + + let state = mixer.getState(); + + chai.assert.equal(state.activeFilter.selector, '.category-a.category-c'); + chai.assert.equal(state.totalShow, 1); + + mixer.destroy(); + }); + + it('should respect a `load.sort` configuration option', () => { + let idsByPublishedDate = dataset.slice().sort((a, b) => { + let dateA = a.published; + let dateB = b.published; + + if (dateA < dateB) { + return -1; + } + + if (dateA > dateB) { + return 1; + } + + return 0; + }).map(item => item.id.toString()); + + let container = dom.getContainer(); + let mixer = mixitup(container, { + load: { + sort: 'published' + } + }); + + let state = mixer.getState(); + let targetIds = state.show.map(el => el.id); + + chai.assert.deepEqual(targetIds, idsByPublishedDate); + + mixer.destroy(); + }); + + it('should add a `layout.containerClassName` class if specified and be reflected in state', () => { + let container = dom.getContainer(); + let mixer = mixitup(container, { + layout: { + containerClassName: 'grid' + } + }); + + let state = mixer.getState(); + + chai.assert.equal(state.activeContainerClassName, 'grid'); + + mixer.destroy(); + }); +}); \ No newline at end of file diff --git a/mixitup-3.3.1/tests/unit/h.js b/mixitup-3.3.1/tests/unit/h.js new file mode 100644 index 0000000..de1bcc3 --- /dev/null +++ b/mixitup-3.3.1/tests/unit/h.js @@ -0,0 +1,44 @@ +'use strict'; + +const chai = require('chai'); +const mixitup = require('../../dist/mixitup.js'); + +const h = mixitup.h; + +describe('h#compareVersions()', () => { + it('should return true if versions are matching', () => { + let result = h.compareVersions('1.0.0', '1.0.0'); + + chai.assert.isOk(result); + }); + + it('should return false if specimen version is less than control', () => { + let result = h.compareVersions('1.0.0', '0.1.2'); + + chai.assert.isNotOk(result); + }); + + it('should return true if specimen version is greater than control', () => { + let result = h.compareVersions('1.0.0', '1.1.2'); + + chai.assert.isOk(result); + }); + + it('should return true if specimen version is greater than control, with double figures', () => { + let result = h.compareVersions('3.0.0', '10.1.2'); + + chai.assert.isOk(result); + }); + + it('should handle semver carat notation', () => { + let result = h.compareVersions('^3.0.0', '2.0.0'); + + chai.assert.isNotOk(result); + }); + + it('should handle semver label notation', () => { + let result = h.compareVersions('^3.0.0', '3.0.0-beta'); + + chai.assert.isOk(result); + }); +}); diff --git a/mixitup-3.3.1/tests/unit/main.js b/mixitup-3.3.1/tests/unit/main.js new file mode 100644 index 0000000..1ae556a --- /dev/null +++ b/mixitup-3.3.1/tests/unit/main.js @@ -0,0 +1,20 @@ +require('./factory'); +require('./mixer-get-state'); +require('./mixer-filter'); +require('./mixer-toggle-on-off'); +require('./mixer-sort'); +require('./mixer-dataset'); +require('./mixer-insert'); +require('./mixer-remove'); +require('./mixer-change-layout'); +require('./mixer-multimix'); +require('./mixer-get-config'); +require('./controls-filter'); +require('./controls-toggle'); +require('./controls-sort'); +require('./controls-multimix'); +require('./controls-live'); +require('./queue'); +require('./extension'); +require('./callbacks'); +require('./h'); \ No newline at end of file diff --git a/mixitup-3.3.1/tests/unit/mixer-change-layout.js b/mixitup-3.3.1/tests/unit/mixer-change-layout.js new file mode 100644 index 0000000..964b24f --- /dev/null +++ b/mixitup-3.3.1/tests/unit/mixer-change-layout.js @@ -0,0 +1,54 @@ +'use strict'; + +require('jsdom-global')(); + +const chai = require('chai'); +const dom = require('../mock/dom'); +const mixitup = require('../../dist/mixitup.js'); + +chai.use(require('chai-shallow-deep-equal')); +chai.use(require('chai-as-promised')); + +describe('mixitup.Mixer', () => { + let container = dom.getContainer(); + let newClass = 'mixitup-container__display-rows'; + let mixer = mixitup(container); + + describe('#changeLayout()', () => { + it('should add a new class name to the container', () => { + return mixer.changeLayout(newClass) + .then(state => { + chai.assert.equal(state.activeContainerClassName, newClass); + chai.assert.isOk(container.matches('.' + newClass)); + }); + }); + + it('should remove the class name from the container', () => { + return mixer.changeLayout('') + .then(state => { + chai.assert.equal(state.activeContainerClassName, ''); + chai.assert.notOk(container.matches('.' + newClass)); + }); + }); + + it('should accept a callback function which is invoked after filtering', () => { + let promise = new Promise(resolve => mixer.changeLayout(newClass, resolve)); + + chai.assert.isFulfilled(promise); + + return promise + .then(state => { + chai.assert.equal(state.activeContainerClassName, newClass); + chai.assert.isOk(container.matches('.' + newClass)); + }); + }); + + it('should accept a boolean allowing toggling off of animation', () => { + return mixer.changeLayout('', false) + .then(state => { + chai.assert.equal(state.activeContainerClassName, ''); + chai.assert.notOk(container.matches('.' + newClass)); + }); + }); + }); +}); \ No newline at end of file diff --git a/mixitup-3.3.1/tests/unit/mixer-dataset.js b/mixitup-3.3.1/tests/unit/mixer-dataset.js new file mode 100644 index 0000000..68a0a7a --- /dev/null +++ b/mixitup-3.3.1/tests/unit/mixer-dataset.js @@ -0,0 +1,301 @@ +'use strict'; + +require('jsdom-global')(); + +const chai = require('chai'); +const dom = require('../mock/dom'); +const mixitup = require('../../dist/mixitup.js'); +const JSONDataset = require('../mock/dataset'); + +const dataset = JSONDataset.map(data => new dom.Item(data)); + +chai.use(require('chai-shallow-deep-equal')); +chai.use(require('chai-as-promised')); + +describe('mixitup()', () => { + it('should throw an error if `load.dataset` does not match pre-rendered targets', () => { + const emptyContainer = dom.getEmptyContainer(); + + chai.assert.throws(() => { + mixitup(emptyContainer, { + load: { + dataset: dataset + } + }); + }, mixitup.messages.errorDatasetPrerenderedMismatch()); + }); + + it('should throw an error if UID not provided in dataset API mode', () => { + const container = dom.getContainer(); + let mixer; + + chai.assert.throws(() => { + mixer = mixitup(container, { + load: { + dataset: dataset + } + }); + }, mixitup.messages.errorConfigDataUidKeyNotSet()); + }); + + it('should instantiate in dataset API mode when provided with `load.dataset` and a matching container', () => { + const container = dom.getContainer(); + const targets = Array.prototype.slice.call(container.children); + + const mixer = mixitup(container, { + data: { + uidKey: 'id' + }, + load: { + dataset: dataset + } + }); + + const state = mixer.getState(); + + chai.assert.equal(state.activeFilter, null); + chai.assert.equal(state.activeSort, null); + chai.assert.deepEqual(state.activeDataset, dataset); + chai.assert.deepEqual(state.targets, targets); + chai.assert.deepEqual(state.show, targets); + chai.assert.deepEqual(state.matching, []); + + mixer.destroy(); + }); +}); + +describe('mixitup.Mixer', () => { + describe('#dataset()', () => { + const container = dom.getContainer(); + const workingDataset = dataset.slice(); + const config = { + data: { + uidKey: 'id', + dirtyCheck: true + }, + render: { + target: mixitup.h.template(dom.ITEM_TEMPLATE) + }, + load: { + dataset: dataset + } + }; + + const mixer = mixitup(container, config); + + const startTotalWhitespace = dom.getTotalWhitespace(container.outerHTML); + + after(() => mixer.destroy()); + + it('should throw an error if an item is added to the dataset, without a render function defined', () => { + const newDataset = dataset.slice(); + const container = dom.getContainer(); + const erMixer = mixitup(container, { + data: { + uidKey: 'id' + }, + load: { + dataset: dataset + } + }); + + newDataset.push(new dom.Item({ + id: 99, + categories: ['d'] + })); + + chai.assert.throws(() => { + erMixer.dataset(newDataset); + }, mixitup.messages.errorDatasetRendererNotSet()); + }); + + it('should throw an error if an item is added to the dataset without a valid UID', () => { + const newDataset = dataset.slice(); + const container = dom.getContainer(); + + const erMixer = mixitup(container, config); + + newDataset.push(new dom.Item({ + categories: ['d'] + })); + + chai.assert.throws(() => { + erMixer.dataset(newDataset); + }, mixitup.messages.errorDatasetInvalidUidKey({ + uidKey: 'id' + })); + }); + + it('should throw an error if an item with a duplicate UID is added to the dataset', () => { + const newDataset = dataset.slice(); + const container = dom.getContainer(); + + const erMixer = mixitup(container, config); + + newDataset.push(new dom.Item({ + id: 'target-1', + categories: ['d'] + })); + + chai.assert.throws(() => { + erMixer.dataset(newDataset); + }, mixitup.messages.errorDatasetDuplicateUid({ + uid: 'target-1' + })); + }); + + it('should insert a target when a new item is added to end of the dataset', () => { + workingDataset.push(new dom.Item({ + id: 7, + categories: ['d'] + })); + + return mixer.dataset(workingDataset) + .then((state) => { + chai.assert.equal(state.totalShow, 7); + chai.assert.equal(state.show[6].id, '7'); + chai.assert.isOk(state.show[6].matches('.category-d')); + }); + }); + + it('should insert a target when a new item is added to the start of the dataset', () => { + workingDataset.unshift(new dom.Item({ + id: 0, + categories: ['d'] + })); + + return mixer.dataset(workingDataset) + .then((state) => { + chai.assert.equal(state.totalShow, 8); + chai.assert.equal(state.show[0].id, '0'); + chai.assert.isOk(state.show[0].matches('.category-d')); + }); + }); + + it('should insert a target when a new item is added at an arbitrary point in the dataset', () => { + workingDataset.splice(3, 0, new dom.Item({ + id: 999, + categories: ['d'] + })); + + return mixer.dataset(workingDataset) + .then((state) => { + chai.assert.equal(state.totalShow, 9); + chai.assert.equal(state.show[3].id, '999'); + chai.assert.isOk(state.show[3].matches('.category-d')); + }); + }); + + it('should remove a target when an item is removed from the end of the dataset', () => { + workingDataset.pop(); + + return mixer.dataset(workingDataset) + .then((state) => { + chai.assert.equal(state.totalShow, 8); + chai.assert.notEqual(state.show[7].id, '7'); + }); + }); + + it('should remove a target when an item is removed from the start of the dataset', () => { + workingDataset.shift(); + + return mixer.dataset(workingDataset) + .then((state) => { + chai.assert.equal(state.totalShow, 7); + chai.assert.notEqual(state.show[0].id, '0'); + }); + }); + + it('should remove a target when an item is removed from an arbitary point in the dataset', () => { + const removed = workingDataset.splice(2, 1); + + chai.assert.equal(removed[0].id, 999); + + return mixer.dataset(workingDataset) + .then((state) => { + chai.assert.equal(state.totalShow, 6); + chai.assert.notEqual(state.show[2].id, '999'); + }); + }); + + it('should sort targets when the dataset is sorted', () => { + workingDataset.reverse(); + + const ids = workingDataset.map((item) => item.id.toString()); + + return mixer.dataset(workingDataset) + .then((state) => { + const elIds = state.show.map((el) => el.id); + + chai.assert.equal(state.totalShow, 6); + chai.assert.deepEqual(ids, elIds); + }); + }); + + it('should sort rerender targets if their data changes and dirtyChecking is enabled', () => { + workingDataset[0] = new dom.Item(Object.assign({}, workingDataset[0])); + + workingDataset[0].categories.push('z'); + + return mixer.dataset(workingDataset) + .then((state) => { + chai.assert.isOk(state.show[0].matches('.category-z')); + }); + }); + + it('should not insert excessive whitespace after DOM manipulations', () => { + chai.assert.equal(dom.getTotalWhitespace(container.outerHTML), startTotalWhitespace); + }); + + it('should accept a callback function which is invoked after dataset change', () => { + workingDataset.reverse(); + + const ids = workingDataset.map((item) => item.id.toString()); + + const promise = new Promise(resolve => mixer.dataset(workingDataset, resolve)); + + chai.assert.isFulfilled(promise); + + return promise + .then((state) => { + const elIds = state.show.map((el) => el.id); + + chai.assert.equal(state.totalShow, 6); + chai.assert.deepEqual(ids, elIds); + }); + }); + + it('should accept a boolean allowing toggling off of animation', () => { + workingDataset.reverse(); + + const ids = workingDataset.map((item) => item.id.toString()); + + return mixer.dataset(workingDataset, false) + .then(state => { + const elIds = state.show.map((el) => el.id); + + chai.assert.equal(state.totalShow, 6); + chai.assert.deepEqual(ids, elIds); + }); + }); + + it('should re-render targets reflective of template changes when `forceRender` is called', () => { + let firstTarget = mixer.getState().show[0]; + + chai.assert.equal(firstTarget.outerHTML, '
'); + + mixer.configure({ + render: { + target: mixitup.h.template(dom.ITEM_TEMPLATE_ALT) + } + }); + + + mixer.forceRender(); + + firstTarget = mixer.getState().show[0]; + + chai.assert.equal(firstTarget.outerHTML, '
'); + }); + }); +}); \ No newline at end of file diff --git a/mixitup-3.3.1/tests/unit/mixer-filter.js b/mixitup-3.3.1/tests/unit/mixer-filter.js new file mode 100644 index 0000000..c4bcd5c --- /dev/null +++ b/mixitup-3.3.1/tests/unit/mixer-filter.js @@ -0,0 +1,268 @@ +'use strict'; + +require('jsdom-global')(); + +const chai = require('chai'); +const dom = require('../mock/dom'); +const mixitup = require('../../dist/mixitup.js'); + +chai.use(require('chai-shallow-deep-equal')); +chai.use(require('chai-as-promised')); + +describe('mixitup.Mixer', () => { + describe('#filter()', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + + it('should accept a class selector', () => { + let matching = Array.prototype.slice.call(container.querySelectorAll('.category-a')); + + return mixer.filter('.category-a') + .then(state => { + chai.assert.equal(state.totalShow, matching.length); + chai.assert.deepEqual(state.show, matching); + chai.assert.deepEqual(state.matching, matching); + }); + }); + + it('should accept an attribute selector', () => { + let matching = Array.prototype.slice.call(container.querySelectorAll('[data-category~="a"]')); + + return mixer.filter('[data-category~="a"]') + .then(state => { + chai.assert.equal(state.totalShow, matching.length); + chai.assert.deepEqual(state.show, matching); + chai.assert.deepEqual(state.matching, matching); + }); + }); + + it('should accept a compound OR class selector', () => { + let matching = Array.prototype.slice.call(container.querySelectorAll('.category-a, .category-b')); + + return mixer.filter('.category-a, .category-b') + .then(state => { + chai.assert.equal(state.totalShow, matching.length); + chai.assert.deepEqual(state.show, matching); + chai.assert.deepEqual(state.matching, matching); + }); + }); + + it('should accept a compound AND class selector', () => { + let matching = Array.prototype.slice.call(container.querySelectorAll('.category-a.category-c')); + + return mixer.filter('.category-a.category-c') + .then(state => { + chai.assert.equal(state.totalShow, matching.length); + chai.assert.deepEqual(state.show, matching); + chai.assert.deepEqual(state.matching, matching); + }); + }); + + it('should accept a compound OR attribute selector', () => { + let matching = Array.prototype.slice.call(container.querySelectorAll('[data-category~="a"], [data-category~="c"]')); + + return mixer.filter('[data-category~="a"], [data-category~="c"]') + .then(state => { + chai.assert.equal(state.totalShow, matching.length); + chai.assert.deepEqual(state.show, matching); + chai.assert.deepEqual(state.matching, matching); + }); + }); + + it('should accept a compound AND attribute selector', () => { + let matching = Array.prototype.slice.call(container.querySelectorAll('[data-category~="a"][data-category~="c"]')); + + return mixer.filter('[data-category~="a"][data-category~="c"]') + .then(state => { + chai.assert.equal(state.totalShow, matching.length); + chai.assert.equal(state.totalShow, 1); + chai.assert.deepEqual(state.show, matching); + chai.assert.deepEqual(state.matching, matching); + }); + }); + + it('should accept "none"', () => { + return mixer.filter('none') + .then(state => { + chai.assert.equal(state.totalShow, 0); + chai.assert.equal(state.hasFailed, false); + chai.assert.deepEqual(state.hide, Array.prototype.slice.call(container.children)); + chai.assert.equal(state.activeFilter.selector, ''); + }); + }); + + it('should accept "all"', () => { + return mixer.filter('all') + .then(state => { + chai.assert.deepEqual(state.show, Array.prototype.slice.apply(container.children)); + chai.assert.deepEqual(state.show, state.targets); + }); + }); + + it('should fail if queried with a non matching selector', () => { + return mixer.filter('.non-mathing-selector') + .then(state => { + chai.assert.deepEqual(state.show, []); + chai.assert.equal(state.hasFailed, true); + }); + }); + + it('should accept a single element', () => { + let el = container.firstElementChild; + + return mixer.filter(el) + .then(state => { + chai.assert.deepEqual(state.show, [el]); + chai.assert.equal(state.activeFilter.selector, ''); + chai.assert.deepEqual(state.activeFilter.collection, [el]); + }); + }); + + it('should accept a collection of elements', () => { + let collection = [ + container.firstElementChild, + container.lastElementChild + ]; + + return mixer.filter(collection) + .then(state => { + chai.assert.deepEqual(state.show, collection); + chai.assert.equal(state.activeFilter.selector, ''); + chai.assert.deepEqual(state.activeFilter.collection, collection); + }); + }); + + it('should interpret `null` as hide all', () => { + return mixer.filter(null) + .then(state => { + chai.assert.deepEqual(state.show, []); + chai.assert.equal(state.activeFilter.selector, ''); + chai.assert.deepEqual(state.activeFilter.collection, []); + }); + }); + + it('should interpret `[]` as hide all', () => { + return mixer.filter(null) + .then(state => { + chai.assert.deepEqual(state.show, []); + chai.assert.equal(state.activeFilter.selector, ''); + chai.assert.deepEqual(state.activeFilter.collection, []); + }); + }); + + it('should accept a full CommandFilter object, allowing for inverse filtering via selector', () => { + let command = { + selector: '.category-a', + action: 'hide' + }; + + let collection = Array.prototype.slice.call(container.querySelectorAll(':not(.category-a)')); + + return mixer.filter(command) + .then(state => { + chai.assert.deepEqual(state.show, collection); + chai.assert.equal(state.activeFilter.selector, '.category-a'); + chai.assert.equal(state.activeFilter.action, 'hide'); + }); + }); + + it('should accept a full CommandFilter object, allowing for inverse filtering via a collection', () => { + let el = container.querySelector('.category-a.category-c'); + + let command = { + collection: [el], + action: 'hide' + }; + + let collection = Array.prototype.slice.call(container.querySelectorAll(':not(.category-a.category-c)')); + + return mixer.filter(command) + .then(state => { + chai.assert.deepEqual(state.show, collection); + chai.assert.deepEqual(state.activeFilter.collection, [el]); + chai.assert.equal(state.activeFilter.action, 'hide'); + }); + }); + + it('should accept a callback function which is invoked after filtering', () => { + let matching = Array.prototype.slice.call(container.querySelectorAll('.category-a')); + + let promise = new Promise(resolve => mixer.filter('.category-a', resolve)); + + chai.assert.isFulfilled(promise); + + return promise + .then(state => { + chai.assert.equal(state.totalShow, matching.length); + chai.assert.deepEqual(state.show, matching); + chai.assert.deepEqual(state.matching, matching); + }); + }); + + it('should return a promise which is resolved after filtering', () => { + let matching = Array.prototype.slice.call(container.querySelectorAll('.category-a')); + + return mixer.filter('.category-a') + .then(state => { + chai.assert.equal(state.totalShow, matching.length); + chai.assert.deepEqual(state.show, matching); + chai.assert.deepEqual(state.matching, matching); + }); + }); + + it('should accept a boolean allowing toggling off of animation', () => { + let matching = Array.prototype.slice.call(container.querySelectorAll('.category-a')); + + return mixer.filter('.category-a', false) + .then(state => { + chai.assert.equal(state.totalShow, matching.length); + chai.assert.deepEqual(state.show, matching); + chai.assert.deepEqual(state.matching, matching); + }); + }); + + it('should throw an error if both a selector and a collection are provided', () => { + let command = { + collection: [], + selector: '.selector' + }; + + chai.assert.throws(() => { + mixer.filter(command); + }, Error, mixitup.messages.errorFilterInvalidArguments()); + }); + }); +}); + +describe('mixitup.Mixer', () => { + describe('#hide()', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + + it('should hide all elements', () => { + return mixer.hide() + .then(state => { + chai.assert.equal(state.totalShow, 0); + chai.assert.equal(state.totalHide, state.targets.length); + chai.assert.equal(state.activeFilter.selector, ''); + }); + }); + }); +}); + +describe('mixitup.Mixer', () => { + describe('#show()', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + + it('should show all elements', () => { + return mixer.filter('.category-a') + .then(mixer.show) + .then(state => { + chai.assert.equal(state.totalShow, state.targets.length); + chai.assert.equal(state.totalHide, 0); + chai.assert.equal(state.activeFilter.selector, '.mix'); + }); + }); + }); +}); \ No newline at end of file diff --git a/mixitup-3.3.1/tests/unit/mixer-get-config.js b/mixitup-3.3.1/tests/unit/mixer-get-config.js new file mode 100644 index 0000000..9e3b995 --- /dev/null +++ b/mixitup-3.3.1/tests/unit/mixer-get-config.js @@ -0,0 +1,62 @@ +'use strict'; + +require('jsdom-global')(); + +const chai = require('chai'); +const dom = require('../mock/dom'); +const mixitup = require('../../dist/mixitup.js'); + +chai.use(require('chai-shallow-deep-equal')); +chai.use(require('chai-as-promised')); + +describe('mixitup.Mixer', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + + describe('#getConfig()', () => { + it('should retrieve the whole config object if no stringKey passed', () => { + let config = mixer.getConfig(); + + chai.assert.instanceOf(config, mixitup.Config); + }); + + it('should retrieve a config sub-object if single prop stringKey passed', () => { + let config = mixer.getConfig('animation'); + + chai.assert.instanceOf(config, mixitup.ConfigAnimation); + }); + + it('should retrieve a nested property value if multi-prop stringKey passed', () => { + let config = mixer.getConfig('animation.effects'); + + chai.assert.equal(typeof config, 'string'); + }); + + it('should retrieve a the current configuration, reflective of any changes', () => { + let newEffects = 'fade translateZ(-100px)'; + + mixer.configure({ + animation: { + effects: newEffects + } + }); + + let newConfig = mixer.getConfig('animation.effects'); + + chai.assert.equal(newConfig, newEffects); + }); + + it('should throw an error if an invalid configuration option is passed', function() { + chai.assert.throws(() => { + mixer.configure({ + animations: {} + }); + }, TypeError, mixitup.messages.errorConfigInvalidProperty({ + erroneous: 'animations', + suggestion: mixitup.messages.errorConfigInvalidPropertySuggestion({ + probableMatch: 'animation' + }) + })); + }); + }); +}); \ No newline at end of file diff --git a/mixitup-3.3.1/tests/unit/mixer-get-state.js b/mixitup-3.3.1/tests/unit/mixer-get-state.js new file mode 100644 index 0000000..cfa9a63 --- /dev/null +++ b/mixitup-3.3.1/tests/unit/mixer-get-state.js @@ -0,0 +1,92 @@ +'use strict'; + +require('jsdom-global')(); + +const chai = require('chai'); +const dom = require('../mock/dom'); +const mixitup = require('../../dist/mixitup.js'); + +chai.use(require('chai-shallow-deep-equal')); + +describe('mixitup.Mixer', () => { + describe('#getState()', () => { + let container = dom.getContainer(); + let id = container.id = 'test-id'; + let mixer = mixitup(container); + let state = mixer.getState(); + + after(() => mixer.destroy()); + + it('should contain an id equal to the container id', () => { + chai.assert.equal(state.container.id, id); + }); + + it('should contain a reference to the container element', () => { + chai.assert.equal(state.container, container); + }); + + it('should contain a reference to the container element', () => { + chai.assert.equal(state.container, container); + }); + + it('should contain an activeFilter object with the default selector active', () => { + chai.assert.instanceOf(state.activeFilter, mixitup.CommandFilter); + chai.assert.equal(state.activeFilter.selector, '.mix'); + }); + + it('should contain an activeSort object with the default sort string active', () => { + chai.assert.instanceOf(state.activeSort, mixitup.CommandSort); + chai.assert.equal(state.activeSort.sortString, 'default:asc'); + }); + + it('should contain an empty activeContainerClassName string', () => { + chai.assert.equal(state.activeContainerClassName, ''); + }); + + it('should contain a null activeDataset', () => { + chai.assert.deepEqual(state.activeDataset, null); + }); + + it('should contain a hasFailed boolean, set to false', () => { + chai.assert.deepEqual(state.hasFailed, false); + }); + + it('should contain a list of targets deeply equaling the contents of the container', () => { + chai.assert.deepEqual(state.targets, Array.prototype.slice.apply(container.children)); + }); + + it('should contain a totalTargets integer, equal to the number of targets in the container', () => { + chai.assert.equal(state.totalTargets, container.children.length); + }); + + it('should contain a list of targets currently shown', () => { + chai.assert.deepEqual(state.show, Array.prototype.slice.apply(container.children)); + chai.assert.deepEqual(state.show, state.targets); + }); + + it('should contain a totalShow integer, equal to the number of targets shown', () => { + chai.assert.equal(state.totalShow, container.children.length); + }); + + it('should contain a list of targets matching the active selector', () => { + chai.assert.deepEqual(state.matching, Array.prototype.slice.apply(container.children)); + chai.assert.deepEqual(state.matching, state.targets); + }); + + it('should contain a totalMatching integer, equal to the number of targets matching the active selector', () => { + chai.assert.equal(state.totalMatching, container.children.length); + }); + + it('should contain a list of targets currently hidden', () => { + chai.assert.deepEqual(state.hide, []); + }); + + it('should contain a totalShow integer, equal to the number of targets hidden', () => { + chai.assert.equal(state.totalHide, 0); + }); + + it('should contain a null triggerElement reference', () => { + chai.assert.equal(state.triggerElement, null); + }); + }); +}); \ No newline at end of file diff --git a/mixitup-3.3.1/tests/unit/mixer-insert.js b/mixitup-3.3.1/tests/unit/mixer-insert.js new file mode 100644 index 0000000..205fdbc --- /dev/null +++ b/mixitup-3.3.1/tests/unit/mixer-insert.js @@ -0,0 +1,395 @@ +'use strict'; + +require('jsdom-global')(); + +const chai = require('chai'); +const dom = require('../mock/dom'); +const mixitup = require('../../dist/mixitup.js'); + +chai.use(require('chai-shallow-deep-equal')); +chai.use(require('chai-as-promised')); + +describe('mixitup.Mixer', () => { + describe('#insert()', () => { + it('should accept an element as an argument', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget = dom.getTarget(); + + return mixer.insert(newTarget) + .then(state => { + chai.assert.equal(state.show[0].id, 7); + + mixer.destroy(); + }); + }); + + it('should accept an element and an index as arguments', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget = dom.getTarget(); + + return mixer.insert(newTarget, 3) + .then(state => { + chai.assert.equal(state.show[3].id, 7); + + mixer.destroy(); + }); + }); + + it('should accept an html string as an argument', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget = dom.getTarget(); + + return mixer.insert(newTarget.outerHTML) + .then(state => { + chai.assert.equal(state.show[0].id, 7); + + mixer.destroy(); + }); + }); + + it('should accept an html and an index as arguments', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget = dom.getTarget(); + + return mixer.insert(newTarget.outerHTML, 5) + .then(state => { + chai.assert.equal(state.show[5].id, 7); + + mixer.destroy(); + }); + }); + + it('should accept accept an element collection as an argument', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget1 = dom.getTarget(); + let newTarget2 = dom.getTarget(); + + newTarget2.id = '8'; + + return mixer.insert([newTarget1, newTarget2]) + .then(state => { + chai.assert.equal(state.show[0].id, 7); + chai.assert.equal(state.show[1].id, 8); + + mixer.destroy(); + }); + }); + + it('should accept accept a document fragment as an argument', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget = dom.getTarget(); + let frag = document.createDocumentFragment(); + + frag.appendChild(newTarget); + + return mixer.insert(frag) + .then(state => { + chai.assert.equal(state.show[0].id, 7); + + mixer.destroy(); + }); + }); + + it('should accept accept an element collection and an index as an argument', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget1 = dom.getTarget(); + let newTarget2 = dom.getTarget(); + + newTarget2.id = '8'; + + return mixer.insert([newTarget1, newTarget2], 4) + .then(state => { + chai.assert.equal(state.show[4].id, 7); + chai.assert.equal(state.show[5].id, 8); + + mixer.destroy(); + }); + }); + + it('should throw an error if an element, index and sibling are passed simultaneously', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget = dom.getTarget(); + let sibling = container.children[4]; + + chai.assert.throws(() => { + mixer.insert(newTarget, 4, sibling); + }, Error, mixitup.messages.errorInsertInvalidArguments()); + }); + + it('should accept an element and sibling reference to insert before', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget = dom.getTarget(); + let sibling = container.children[4]; + + return mixer.insert(newTarget, sibling) + .then(state => { + chai.assert.equal(state.show[4].id, '7'); + + mixer.destroy(); + }); + }); + + it('should accept an element, sibling reference and position string', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget = dom.getTarget(); + let sibling = container.children[4]; + + return mixer.insert(newTarget, sibling, 'after') + .then(state => { + chai.assert.equal(state.show[5].id, '7'); + + mixer.destroy(); + }); + }); + + it('should insert at end if the insertion index is above range', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget = dom.getTarget(); + + return mixer.insert(newTarget, 10) + .then(state => { + chai.assert.equal(state.show[6].id, '7'); + + mixer.destroy(); + }); + }); + + it('should insert at start if the insertion index is below range', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget = dom.getTarget(); + + return mixer.insert(newTarget, -2) + .then(state => { + chai.assert.equal(state.show[0].id, '7'); + + mixer.destroy(); + }); + }); + + it('should throw an error if the element to insert already exists', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget = container.children[1]; + + chai.assert.throws(() => { + mixer.insert(newTarget); + }, Error, mixitup.messages.errorInsertPreexistingElement()); + }); + + it('should allow no elements to be inserted with a warning', () => { + let container = dom.getContainer(); + let totalTargets = container.children.length; + let mixer = mixitup(container); + + return mixer.insert() + .then(state => { + chai.assert.equal(state.totalShow, totalTargets); + + mixer.destroy(); + }); + }); + + it('should accept a callback function which is invoked after insertion', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget = dom.getTarget(); + + let promise = new Promise(resolve => mixer.insert(newTarget, resolve)); + + chai.assert.isFulfilled(promise); + + return promise + .then(() => { + chai.assert.equal(newTarget.parentElement, container); + + mixer.destroy(); + }); + }); + + it('should accept a boolean allowing toggling off of animation', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget = dom.getTarget(); + + return mixer.insert(newTarget, false) + .then(() => { + chai.assert.equal(newTarget.parentElement, container); + + mixer.destroy(); + }); + }); + + it('should accept a HTML with padding whitespace as an argument', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget = ' ' + dom.getTarget().outerHTML + ' '; + + return mixer.insert(newTarget) + .then(state => { + chai.assert.equal(state.show[0].id, 7); + + mixer.destroy(); + }); + }); + }); + + describe('#prepend()', () => { + it('should insert an element at the start', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget = dom.getTarget(); + + return mixer.prepend(newTarget) + .then(state => { + chai.assert.equal(state.show[0].id, 7); + + mixer.destroy(); + }); + }); + + it('should insert a collection of elements at the start', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget1 = dom.getTarget(); + let newTarget2 = dom.getTarget(); + + newTarget2.id = '8'; + + return mixer.prepend([newTarget1, newTarget2]) + .then(state => { + chai.assert.equal(state.show[0].id, 7); + chai.assert.equal(state.show[1].id, 8); + + mixer.destroy(); + }); + }); + }); + + describe('#append()', () => { + it('should insert an element at the end', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget = dom.getTarget(); + + return mixer.append(newTarget) + .then(state => { + chai.assert.equal(state.show[6].id, 7); + + mixer.destroy(); + }); + }); + + it('should insert a collection of elements at the end', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget1 = dom.getTarget(); + let newTarget2 = dom.getTarget(); + + newTarget2.id = '8'; + + return mixer.append([newTarget1, newTarget2]) + .then(state => { + chai.assert.equal(state.show[6].id, 7); + chai.assert.equal(state.show[7].id, 8); + + mixer.destroy(); + }); + }); + + it('should accept accept a document fragment as an argument to append', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget = dom.getTarget(); + let frag = document.createDocumentFragment(); + + frag.appendChild(newTarget); + + return mixer.append(frag) + .then(state => { + chai.assert.equal(state.show[6].id, 7); + + mixer.destroy(); + }); + }); + }); + + describe('#insertBefore()', () => { + it('should insert an element before the referenced element', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget = dom.getTarget(); + let sibling = container.children[3]; + + return mixer.insertBefore(newTarget, sibling) + .then(state => { + chai.assert.equal(state.show[3].id, 7); + + mixer.destroy(); + }); + }); + + it('should insert a collection of elements before the referenced element', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget1 = dom.getTarget(); + let newTarget2 = dom.getTarget(); + let sibling = container.children[3]; + + newTarget2.id = '8'; + + return mixer.insertBefore([newTarget1, newTarget2], sibling) + .then(state => { + chai.assert.equal(state.show[3].id, 7); + chai.assert.equal(state.show[4].id, 8); + + mixer.destroy(); + }); + }); + }); + + describe('#insertAfter()', () => { + it('should insert an element after the referenced element', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget = dom.getTarget(); + let sibling = container.children[3]; + + return mixer.insertAfter(newTarget, sibling) + .then(state => { + chai.assert.equal(state.show[4].id, 7); + + mixer.destroy(); + }); + }); + + it('should insert a collection of elements after the referenced element', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + let newTarget1 = dom.getTarget(); + let newTarget2 = dom.getTarget(); + let sibling = container.children[3]; + + newTarget2.id = '8'; + + return mixer.insertAfter([newTarget1, newTarget2], sibling) + .then(state => { + chai.assert.equal(state.show[4].id, 7); + chai.assert.equal(state.show[5].id, 8); + + mixer.destroy(); + }); + }); + }); +}); \ No newline at end of file diff --git a/mixitup-3.3.1/tests/unit/mixer-multimix.js b/mixitup-3.3.1/tests/unit/mixer-multimix.js new file mode 100644 index 0000000..849fae8 --- /dev/null +++ b/mixitup-3.3.1/tests/unit/mixer-multimix.js @@ -0,0 +1,17 @@ +'use strict'; + +require('jsdom-global')(); + +const chai = require('chai'); +const dom = require('../mock/dom'); +const mixitup = require('../../dist/mixitup.js'); + +chai.use(require('chai-shallow-deep-equal')); +chai.use(require('chai-as-promised')); + +describe('mixitup.Mixer', () => { + describe('#multimix()', () => { + let container = dom.getContainer(); + let mixer = mixitup(container); + }); +}); \ No newline at end of file diff --git a/mixitup-3.3.1/tests/unit/mixer-remove.js b/mixitup-3.3.1/tests/unit/mixer-remove.js new file mode 100644 index 0000000..708ccdb --- /dev/null +++ b/mixitup-3.3.1/tests/unit/mixer-remove.js @@ -0,0 +1,111 @@ +'use strict'; + +require('jsdom-global')(); + +const chai = require('chai'); +const dom = require('../mock/dom'); +const mixitup = require('../../dist/mixitup.js'); + +chai.use(require('chai-shallow-deep-equal')); +chai.use(require('chai-as-promised')); + +describe('mixitup.Mixer', () => { + describe('#remove()', () => { + it('should accept an element as an argument', () => { + const container = dom.getContainer(); + const mixer = mixitup(container); + const toRemove = container.children[3]; + + return mixer.remove(toRemove) + .then(state => { + chai.assert.notEqual(state.show[3].id, 'target-4'); + chai.assert.equal(state.show[3].id, 'target-5'); + chai.assert.equal(state.totalShow, '5'); + + mixer.destroy(); + }); + }); + + it('should accept a collection of elements as an argument', () => { + const container = dom.getContainer(); + const mixer = mixitup(container); + const toRemove = [container.children[3], container.children[0]]; + + return mixer.remove(toRemove) + .then(state => { + chai.assert.equal(state.show[0].id, 'target-2'); + chai.assert.equal(state.show[3].id, 'target-6'); + chai.assert.equal(state.totalShow, '4'); + + mixer.destroy(); + }); + }); + + it('should accept an index as an argument', () => { + const container = dom.getContainer(); + const mixer = mixitup(container); + + return mixer.remove(3) + .then(state => { + chai.assert.equal(state.show[3].id, 'target-5'); + chai.assert.equal(state.totalShow, '5'); + + mixer.destroy(); + }); + }); + + it('should accept a selector as an argument', () => { + const container = dom.getContainer(); + const mixer = mixitup(container); + + return mixer.remove('.category-a') + .then(state => { + chai.assert.equal(state.totalShow, '3'); + + mixer.destroy(); + }); + }); + + it('should allow no elements to be removed with a warning', () => { + const container = dom.getContainer(); + const mixer = mixitup(container); + + return mixer.remove() + .then(state => { + chai.assert.equal(state.totalShow, '6'); + + mixer.destroy(); + }); + }); + + it('should accept a callback function which is invoked after removal', () => { + const container = dom.getContainer(); + const mixer = mixitup(container); + const toRemove = container.children[0]; + + const promise = new Promise(resolve => mixer.insert(mixer.remove(toRemove), resolve)); + + chai.assert.isFulfilled(promise); + + return promise + .then(() => { + chai.assert.notEqual(toRemove, container); + + mixer.destroy(); + }); + }); + + it('should accept a boolean allowing toggling off of animation', () => { + const container = dom.getContainer(); + const mixer = mixitup(container); + const toRemove = container.children[0]; + + return mixer.remove(toRemove, false) + .then(() => { + chai.assert.notEqual(toRemove, container); + + mixer.destroy(); + }); + }); + }); +}); \ No newline at end of file diff --git a/mixitup-3.3.1/tests/unit/mixer-sort.js b/mixitup-3.3.1/tests/unit/mixer-sort.js new file mode 100644 index 0000000..9290654 --- /dev/null +++ b/mixitup-3.3.1/tests/unit/mixer-sort.js @@ -0,0 +1,257 @@ +'use strict'; + +require('jsdom-global')(); + +const chai = require('chai'); +const dom = require('../mock/dom'); +const dataset = require('../mock/dataset'); +const mixitup = require('../../dist/mixitup.js'); + +chai.use(require('chai-shallow-deep-equal')); +chai.use(require('chai-as-promised')); + +describe('mixitup.Mixer', () => { + describe('#sort()', () => { + const container = dom.getContainer(); + const originalOrder = Array.prototype.slice.call(container.children); + const mixer = mixitup(container); + + const idsByPublishedDate = dataset.slice().sort((a, b) => { + const dateA = a.published; + const dateB = b.published; + + if (dateA < dateB) { + return -1; + } + + if (dateA > dateB) { + return 1; + } + + return 0; + }).map(item => item.id.toString()); + + const idsByViewsThenPublishedDate = dataset.slice().sort((a, b) => { + const viewsA = a.views; + const viewsB = b.views; + + const sortByPublishedDate = function(a, b) { + const dateA = a.published; + const dateB = b.published; + + if (dateA < dateB) { + return -1; + } + + if (dateA > dateB) { + return 1; + } + + return 0; + }; + + if (viewsA < viewsB) { + return -1; + } + + if (viewsA > viewsB) { + return 1; + } + + sortByPublishedDate(a, b); + }).map(item => item.id.toString()); + + after(() => mixer.destroy()); + + it('accepts `default` as a sort string, but should have no effect on the order', () => { + var startOrder = mixer.getState().show; + + return mixer.sort('default') + .then(state => { + chai.assert.deepEqual(startOrder, state.show); + chai.assert.equal(state.activeSort.sortString, 'default'); + chai.assert.equal(state.activeSort.order, 'asc'); + chai.assert.equal(state.activeSort.attribute, ''); + }); + }); + + it('accepts `default:asc` as a sort string, but should have no effect on the order', () => { + var startOrder = mixer.getState().show; + + return mixer.sort('default:asc') + .then(state => { + chai.assert.deepEqual(startOrder, state.show); + chai.assert.equal(state.activeSort.sortString, 'default:asc'); + chai.assert.equal(state.activeSort.order, 'asc'); + chai.assert.equal(state.activeSort.attribute, ''); + }); + }); + + it('accepts `default:desc` as a sort string, which should reverse the order', () => { + var reversedOrder = mixer.getState().show.slice().reverse(); + + return mixer.sort('default:desc') + .then(state => { + chai.assert.deepEqual(state.show, reversedOrder); + chai.assert.equal(state.activeSort.sortString, 'default:desc'); + chai.assert.equal(state.activeSort.order, 'desc'); + chai.assert.equal(state.activeSort.attribute, ''); + }); + }); + + it('should return the mixer to its original order if sorted by `default` after previous transformations', () => { + return mixer.sort('default') + .then(state => chai.assert.deepEqual(state.show, originalOrder)); + }); + + it('should accept `random` as a sort string, shuffling the targets', () => { + return mixer.sort('random') + .then(state => { + chai.assert.notDeepEqual(state.show, originalOrder); + chai.assert.equal(state.activeSort.sortString, 'random'); + chai.assert.equal(state.activeSort.order, 'random'); + chai.assert.equal(state.activeSort.attribute, ''); + }); + }); + + it('should accept a data-attribute as a sort string, sorting by the attribute\'s value', () => { + return mixer.sort('published') + .then(state => { + const targetIds = state.show.map(el => el.id); + + chai.assert.equal(state.activeSort.sortString, 'published'); + chai.assert.equal(state.activeSort.order, 'asc'); + chai.assert.equal(state.activeSort.attribute, 'published'); + + chai.assert.deepEqual(targetIds, idsByPublishedDate); + }); + }); + + it('should accept a data-attribute and an order as sorting, sorting by the attribute\'s value in the defined order', () => { + const idsByPublishedDateDesc = idsByPublishedDate.slice().reverse(); + + return mixer.sort('published:desc') + .then(state => { + const targetIds = state.show.map(el => el.id); + + chai.assert.equal(state.activeSort.sortString, 'published:desc'); + chai.assert.equal(state.activeSort.order, 'desc'); + chai.assert.equal(state.activeSort.attribute, 'published'); + + chai.assert.deepEqual(targetIds, idsByPublishedDateDesc); + }); + }); + + it('should accept multiple sort strings for multi attribute sorting', () => { + return mixer.sort('views published') + .then(state => { + const targetIds = state.show.map(el => el.id); + + chai.assert.isOk(state.activeSort.next); + chai.assert.instanceOf(state.activeSort.next, mixitup.CommandSort); + + chai.assert.equal(state.activeSort.sortString, 'views'); + chai.assert.equal(state.activeSort.order, 'asc'); + chai.assert.equal(state.activeSort.attribute, 'views'); + + chai.assert.equal(state.activeSort.next.sortString, 'published'); + chai.assert.equal(state.activeSort.next.order, 'asc'); + chai.assert.equal(state.activeSort.next.attribute, 'published'); + + chai.assert.deepEqual(targetIds, idsByViewsThenPublishedDate); + }); + }); + + it('should accept multiple sort strings with orders for multi attribute sorting', () => { + const idsByViewsThenPublishedDateDesc = idsByViewsThenPublishedDate.slice().reverse(); + + return mixer.sort('views:desc published:desc') + .then(state => { + const targetIds = state.show.map(el => el.id); + + chai.assert.deepEqual(targetIds, idsByViewsThenPublishedDateDesc); + }); + }); + + it('should accept multiple sort strings with orders for multi attribute sorting', () => { + const idsByViewsThenPublishedDateDesc = idsByViewsThenPublishedDate.slice().reverse(); + + return mixer.sort('views:desc published:desc') + .then(state => { + const targetIds = state.show.map(el => el.id); + + chai.assert.deepEqual(targetIds, idsByViewsThenPublishedDateDesc); + }); + }); + + it('should accept a collection of elements by which to sort by', () => { + const firstTarget = mixer.getState().targets[0]; + const collection = mixer.getState().targets.slice().reverse(); + + return mixer.sort(collection) + .then(state => { + const lastTarget = state.targets[state.targets.length - 1]; + + chai.assert.deepEqual(lastTarget, firstTarget); + }); + }); + + it('should error if any element in the collection provided does not exist in the container', () => { + const mixer = mixitup(container); + + const collection = [document.createElement('div')]; + + chai.assert.throws(() => mixer.sort(collection)); + }); + + it('should accept a callback function which is invoked after sorting', () => { + const promise = new Promise(resolve => mixer.sort('random', resolve)); + + chai.assert.isFulfilled(promise); + + return promise + .then(state => chai.assert.equal(state.activeSort.sortString, 'random')); + }); + + it('should accept a boolean allowing toggling off of animation', () => { + return mixer.sort('random', false) + .then(state => chai.assert.equal(state.activeSort.sortString, 'random')); + }); + + it('should resort when sorting attributes are dynamically edited, if `behavior.liveSort` is enabled', () => { + const newDate = '20170628'; + let startOrder = null; + + return mixer.sort('published', false) + .then(state => { + const target4 = container.querySelector('#target-4'); + + startOrder = state.targets; + + target4.setAttribute('data-published', newDate); + + return mixer.sort('published', false); + }) + .then(state => { + // Order has not changed + + chai.assert.deepEqual(state.targets, startOrder); + chai.assert.notEqual(state.targets[5].getAttribute('data-published'), newDate); + + mixer.configure({ + behavior: { + liveSort: true + } + }); + + return mixer.sort('published', false); + }) + .then(state => { + // Order has changed + + chai.assert.notDeepEqual(state.targets, startOrder); + chai.assert.equal(state.targets[5].getAttribute('data-published'), newDate); + }); + }); + }); +}); \ No newline at end of file diff --git a/mixitup-3.3.1/tests/unit/mixer-toggle-on-off.js b/mixitup-3.3.1/tests/unit/mixer-toggle-on-off.js new file mode 100644 index 0000000..cbe0486 --- /dev/null +++ b/mixitup-3.3.1/tests/unit/mixer-toggle-on-off.js @@ -0,0 +1,82 @@ + +'use strict'; + +require('jsdom-global')(); + +const chai = require('chai'); +const dom = require('../mock/dom'); +const mixitup = require('../../dist/mixitup.js'); + +chai.use(require('chai-shallow-deep-equal')); +chai.use(require('chai-as-promised')); + +describe('mixitup.Mixer', () => { + describe('#toggleOn()', () => { + const container = dom.getContainer(); + const mixer = mixitup(container); + + it('should activate an initial toggle', () => { + const matching = Array.prototype.slice.call(container.querySelectorAll('.category-a')); + + return mixer.toggleOn('.category-a') + .then(state => { + chai.assert.equal(state.totalShow, matching.length); + chai.assert.deepEqual(state.show, matching); + chai.assert.deepEqual(state.matching, matching); + }); + }); + + it('should activate a further toggle', () => { + const matching = Array.prototype.slice.call(container.querySelectorAll('.category-a, .category-c')); + + return mixer.toggleOn('.category-c') + .then(state => { + chai.assert.equal(state.totalShow, matching.length); + chai.assert.deepEqual(state.show, matching); + chai.assert.deepEqual(state.matching, matching); + }); + }); + + it('should activate a non-existant toggle with no effect', () => { + const matching = Array.prototype.slice.call(container.querySelectorAll('.category-a, .category-c')); + + return mixer.toggleOn('.category-z') + .then(state => { + chai.assert.equal(state.totalShow, matching.length); + chai.assert.deepEqual(state.show, matching); + chai.assert.deepEqual(state.matching, matching); + }); + }); + }); + + describe('#toggleOff()', () => { + const container = dom.getContainer(); + const mixer = mixitup(container, { + load: { + filter: '.category-a, .category-b, .category-c' + } + }); + + it('should deactivate a toggle', () => { + const matching = Array.prototype.slice.call(container.querySelectorAll('.category-a, .category-b')); + + return mixer.toggleOff('.category-c') + .then(state => { + chai.assert.equal(state.totalShow, matching.length); + chai.assert.deepEqual(state.show, matching); + chai.assert.deepEqual(state.matching, matching); + }); + }); + + it('should deactivate a non existent toggle with no effect', () => { + const matching = Array.prototype.slice.call(container.querySelectorAll('.category-a, .category-b')); + + return mixer.toggleOff('.category-z') + .then(state => { + chai.assert.equal(state.totalShow, matching.length); + chai.assert.deepEqual(state.show, matching); + chai.assert.deepEqual(state.matching, matching); + }); + }); + }); +}); diff --git a/mixitup-3.3.1/tests/unit/queue.js b/mixitup-3.3.1/tests/unit/queue.js new file mode 100644 index 0000000..fa7ae3c --- /dev/null +++ b/mixitup-3.3.1/tests/unit/queue.js @@ -0,0 +1,71 @@ +'use strict'; + +require('jsdom-global')(); + +const chai = require('chai'); +const dom = require('../mock/dom'); +const mixitup = require('../../dist/mixitup.js'); +const JSONDataset = require('../mock/dataset'); +const dataset = JSONDataset.map(data => new dom.Item(data)); + +chai.use(require('chai-shallow-deep-equal')); +chai.use(require('chai-as-promised')); + +describe('mixitup.Mixer', () => { + describe('Queue', () => { + it('should warn if too many multimix operations are pushed into the queue', () => { + let container = dom.getContainer(); + let mixer = mixitup(container, { + debug: { + fauxAsync: true + }, + animation: { + duration: 200 + } + }); + + let promise = Promise.all([ + mixer.hide(), + mixer.show(), + mixer.hide(), + mixer.show(), + mixer.hide() + ]); + + chai.assert.isFulfilled(promise); + + return promise; + }); + + it('should warn if too many dataset operations are pushed into the queue', () => { + let container = dom.getContainer(); + + let mixer = mixitup(container, { + debug: { + fauxAsync: true + }, + animation: { + duration: 200 + }, + data: { + uidKey: 'id' + }, + load: { + dataset: dataset + } + }); + + let promise = Promise.all([ + mixer.dataset([]), + mixer.dataset(dataset), + mixer.dataset([]), + mixer.dataset(dataset), + mixer.dataset([]) + ]); + + chai.assert.isFulfilled(promise); + + return promise; + }); + }); +}); diff --git a/reset.css b/reset.css new file mode 100644 index 0000000..40b4860 --- /dev/null +++ b/reset.css @@ -0,0 +1,18 @@ +*{padding: 0;margin: 0;border: 0;} +*,*:before,*:after{-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;} +:focus,:active{outline: none;} +a:focus,a:active{outline: none;} +nav,footer,header,aside{display: block;} +html,body{ + height:100%;width:100%; + font-size:100%;line-height:1;-ms-text-size-adjust:100%;-moz-text-size-adjust:100%;-webkit-text-size-adjust:100%;} +input,button,textarea{font-family:inherit;} +input::-ms-clear{display: none;} +button{cursor: pointer;} +button::-moz-focus-inner{padding:0;border:0;} +a,a:visited{text-decoration: none;} +a:hover{text-decoration: none;} +ul li{list-style: none;} +img{vertical-align: top;} +h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight: inherit;} +a{color:inherit;} \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 0000000..28df450 --- /dev/null +++ b/style.css @@ -0,0 +1,1438 @@ +/* ОБЩИЕ ЭЛЕМЕНТЫ, КОТОРЫМ МЫ ПИШЕМ СТИЛИ */ + +html, +body { + font-size: 16px; + font-family: Lato, sans-serif; + color: #424551; + line-height: 160%; +} + +.wrapper { + /* дает растягивание от контента, + по умолчанию равен высоте body */ + min-height: 100%; + height: auto; + width: 100%; + /* если ширина контента превышает ширину вью-борта, + то оверфлоу-хидден его обрезает, + также убирает ползунок */ + overflow: hidden; +} +.container { + /* контент будет ограничен по ширине, + в фигме 1230px и плюс заложили padding 30px */ + max-width: 1260px; + padding: 0 15px; + /* делаем по середине страницы контент, + 0 это сверху и снизу, + auto это справа и слева */ + margin: 0 auto; + /* сделали контейнер на всю длину экрана */ + height: 100%; +} + +.main_about { + padding: 180px 0 160px; +} +.main_directions { + padding-bottom: 180px; +} +.main_cert { + margin-bottom: -46px; +} + +.button { + display: inline-block; + letter-spacing: 0.5px; + line-height: 52px; + font-weight: 700; + padding: 0 40px; + color: white; + border-radius: 4px; + background: var( + --btn-gradient, + linear-gradient(56deg, #ff3f3a 0%, #f75e05 100%) + ); + transition: all 0.3s; +} +.button:hover { + transform: scale(1.05); +} +.button--empty { + background-image: none; + border-left: 2px solid #ff3f3a; + border-right: 2px solid #f75e05; + background-image: -webkit-linear-gradient(left, #ff3f3a, #f75e05), + -webkit-linear-gradient(left, #ff3f3a, #f75e05); + background-image: linear-gradient(left, #ff3f3a, #f75e05), + linear-gradient(left, #ff3f3a, #f75e05); + background-size: 100% 2px; + background-position: 0 100%, 0 0; + background-repeat: no-repeat; + background-clip: border-box; +} + +.button--empty span { + background: linear-gradient(55.95deg, #ff3f3a 0%, #f75e05 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.logo { +} +.heading { + color: #1e212c; +} +.heading_subtitle { + line-height: 1.5; + text-transform: uppercase; + letter-spacing: 1px; + font-weight: 700; + margin-bottom: 8px; +} +.heading_title { + font-weight: 900; + font-size: 46px; + line-height: 1.3; +} + +/* ------------------------------HEADER TOP----------------------------------------------------------- */ + +.header { + font-weight: 700; + background: url("./images/1/shapes.svg") 0 0 / cover no-repeat, + linear-gradient( + 56deg, + rgba(255, 63, 58, 0.2) 0%, + rgba(247, 94, 5, 0.2) 100% + ); + height: 100vh; + display: flex; + flex-direction: column; +} +.header_top { +} +.header_top_inner { + display: flex; + justify-content: space-between; + padding: 20px 0; + align-items: center; +} +.header_logo { + margin-right: 60px; +} +.header_nav { + margin-right: auto; +} +.header_nav-list { + display: flex; +} +.header_nav_item { + margin-right: 40px; +} +.header_nav_item:last-child { + margin-right: 0; +} +.header_nav_link { + line-height: 1.6; + color: #424551; + white-space: nowrap; + transition: all 0.2s; +} +.header_nav_link:hover { + color: #ff3f3a; +} +.header_btn_box { + display: flex; + align-items: center; +} +.haeder_top_btn { + margin-right: 36px; +} +.header_user-nav { + position: relative; + padding-left: 28px; + line-height: 1.6; +} +.header_user_btn:hover { + color: #ff3f3a; +} +.header_user-nav:before { + content: ""; + position: absolute; + top: 50%; + left: 0; + transform: translateY(-50%); + display: block; + background: url("./images/1/Profile.svg") no-repeat; + width: 20px; + height: 20px; +} +.header_user_btn { + white-space: nowrap; + line-height: 1.6; + transition: all 0.2s; +} + +/* ------------------------------HEADER BODY--------------------------------------- */ + +.header_body { + flex-grow: 1; +} +.header_body_inner { + display: flex; + flex-direction: column; + height: 100%; +} +.header_main { + display: flex; + justify-content: space-between; + align-items: center; + color: #1e212c; + margin: auto 0; +} +.header_content { + margin-right: 30px; +} +.header_play { +} +.header_play_btn { + display: inline-flex; + align-items: center; +} +.header_play_btn svg { + margin-right: 16px; + border-radius: 50%; + transition: all 0.3s; +} +.header_play_btn svg:hover { + box-shadow: 0 0 0px 16px rgba(255, 63, 58, 0.12); +} +.header_title { + font-size: 64px; + font-weight: 900; + line-height: 1.3; + letter-spacing: 1px; + max-width: 496px; + margin-bottom: 60px; +} +.header_content-buttons { + display: flex; +} +.header_content_btn { + margin-right: 24px; +} +.header_content_btn:last-child { + margin-right: 0px; +} +.header_row { + display: flex; + justify-content: space-between; + align-items: center; + color: #1e212c; + margin-bottom: 80px; +} +.header_row > span { + background-color: #ff3f3a; + height: 8px; + width: 8px; + border-radius: 50%; + margin: 0 15px; +} +.header_row_item span { + font-size: 46px; + font-weight: 900; + line-height: 1.3; + margin-right: 12px; +} +.header_row_item { + display: flex; + align-items: center; +} + +/* ------------------------MAIN-CREATEX------------------------- */ + +.about { +} +.container { +} +.about_inner { +} +.about_createx { + display: flex; + align-items: flex-end; + justify-content: space-between; + margin-bottom: 180px; + position: relative; +} +.about_createx::before { + content: ""; + position: absolute; + top: -80px; + right: -140px; + background: url("./images/2/decor/shapes.svg"); + width: 100px; + height: 85px; +} +.about_createx_content { + max-width: 600px; + margin-right: 30px; +} +.about_heading { + margin-bottom: 48px; +} +.about_text { + margin-bottom: 60px; +} +.about_paragraph--accent { + font-size: 20px; + font-weight: 700; + line-height: 1.5; + color: #1e212c; +} +.about_paragraph { + margin-bottom: 24px; +} +.about_paragraph:last-child { + margin-bottom: 0; +} +.about_btn_box { + display: flex; +} +.about_btn { + margin-right: 24px; +} +.about_btn:last-child { + margin-right: 0; +} +.about_img { +} +.about_values { +} + +/* -----------------------MAIN-VALUES---------------------------------- */ + +.about_values { + display: flex; + justify-content: space-between; +} +.about_image_box { + border-radius: 4px; + overflow: hidden; +} +.about_image_box img { + width: 100%; + height: 100%; + object-fit: cover; +} +.about_numbers { + margin-left: 30px; + align-self: center; +} +.about_number span { + font-size: 48px; + font-weight: 900; + line-height: 1.3; + letter-spacing: 1px; + color: #ff3f3a; + margin-right: 16px; +} +.about_number { + display: flex; + align-items: center; + margin-bottom: 20px; +} +.about_number:last-child { + margin-bottom: 0; +} + +/* -------------------------MAIN-DIRECTIONS---------------------------- */ + +.directions { +} +.directions_inner { + +} +.directions_inner::after { +} +.directions_heading { + margin-bottom: 90px; + text-align: center; +} +.directions_body { +} +.directions_filter_box { + display: flex; + margin-bottom: 56px; +} +.directions_filter_item { + margin-right: 45px; +} +.directions_filter_item:last-child { + margin-right: 0; +} +.directions_filter_btn { + display: block; + font-size: 16px; + font-weight: 700; + line-height: 1; + color: #9a9ca5; + border: 1px solid #9a9ca5; + border-radius: 4px; + padding: 5px 8px; + background: transparent; +} +.directions_filter_btn--active { + background: linear-gradient(56deg, #ff3f3a 0%, #f75e05 100%); + border: none; + color: white; + padding: 6px 8px; +} +.directions_list { + display: grid; + grid-template: repeat(2, auto) / repeat(3, 1fr); + gap: 30px; +} +.directions_list_item { + display: block; + border: 1px solid #e5e8ed; + border-radius: 4px; + overflow: hidden; + transition: all 1s; +} +.directions_list_link { + display: block; + border-radius: 4px; + overflow: hidden; +} +.directions_list_item:hover { + box-shadow: 0px 80px 80px -20px rgba(154, 156, 165, 0.08), + 0px 30px 24px -10px rgba(154, 156, 165, 0.05), + 0px 12px 10px -6px rgba(154, 156, 165, 0.04), + 0px 4px 4px -4px rgba(30, 33, 44, 0.03); + transition: all 0.3s; +} +.directions_list_link:hover .directions_link_btn { + color: #ff3f3a; + transition: all 0.3s; +} +.directions_image_box { + +} +.directions_image_box img{ + height: 100%; + width: 100%; +} +.directions_link_content { + padding: 24px; + display: flex; + flex-direction: column; +} +.directions_link_tag { + font-weight: 700; + line-height: 1; + color: white; + background-color: #0d39eb; + padding: 6px 8px; + border-radius: 4px; + align-self: flex-start; + margin-bottom: 20px; +} +.directions_link_text { + color: #787a80; + font-weight: 400; + line-height: 1.6; + margin-bottom: 20px; +} +.directions_link_btn { + color: #1e212c; + display: flex; + font-weight: 700; + transition: all 1s; +} +.directions_link_btn img { + display: flex; + width: 30px; + height: 30px; + align-items: center; + justify-content: center; + padding: 6px; +} +.directions_link_btn:hover { + color: #ff3f3a; + transition: all 0.3s; +} +.directions_list_item--empty { + background: #F4F5F6; + color: #424551; + font-size: 20px; + font-weight: 700; + line-height: 1.5; + text-align: center; + display: flex; + align-items: center; + justify-content: center; +} +.directions_list_item--empty span { + max-width: 242px; +} + + +.directions_list_item--marketing .directions_link_tag { + background-color: #03CEA4; +} +.directions_list_item--management .directions_link_tag { + background-color: #5A87FC; +} +.directions_list_item--hr .directions_link_tag { + background-color: #F89828; +} +.directions_list_item--design .directions_link_tag { + background-color: #F52F6E; +} +.directions_list_item--development .directions_link_tag { + background-color: #7772F1; +} +.directions_list_item--empty .directions_link_tag { + background-color: #fff; +} + +/* ------------------------------------MAIN CERTIFICATE------------------------------------------------- */ + + +.cert { + margin-top: 180px; +} +.cerf_inner { + display: flex; + justify-content: space-between; +} +.cert_content { + margin-right: 30px; +} +.cert_contect_textlogo { + +} +.cert_heading { + margin-bottom: 50px; +} +.cert_text { + margin-bottom: 30px; +} +.cert_logos { + display: flex; +} +.cert_logos-item { + margin-right: 40px; +} +.cert_logos-item:last-child { + margin-right: 0; +} +.cert_image-box { + box-shadow: + 0 80px 80px -20px rgba(154, 156, 165, 0.16), + 0 30px 24px -10px rgba(154, 156, 165, 0.10), + 0 12px 10px -6px rgba(154, 156, 165, 0.08), + 0 4px 4px -4px rgba(30, 33, 44, 0.05); +} + +/* ------------------------------MAIN SLIDER----------------------------------- */ + +.main_team { + padding: 160px 0 180px; + background: #F4F5F6; +} +.team { +} +.team_inner { +} +.team_heading { + margin-bottom: 60px; + position: relative; +} +.team_slider .slick-list { + margin: 0 -15px; +} +.team_slide { + padding: 0 15px; +} +.team_slide:hover { + +} +.team_slide_inner { + +} +.team_image_box { + border-radius: 4px; + margin-bottom: 16px; + overflow: hidden; + position: relative; +} +.team_image { + position: relative; + height: 340px; +} +.team_image img{ + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + object-fit: cover; +} +.team_socials { + position: absolute; + top: 50%; + bottom: 0; + left: 0; + right: 0; + display: flex; + align-items: flex-end; + justify-content: flex-end; + padding: 20px; + background: linear-gradient(180deg, rgba(30, 33, 44, 0.00) 43.9%, rgba(30, 33, 44, 0.03) 55.21%, rgba(30, 33, 44, 0.33) 68.73%, rgba(30, 33, 44, 0.56) 76.17%, rgba(30, 33, 44, 0.77) 83.75%, rgba(30, 33, 44, 0.92) 91.49%, #1E212C 100%); + transform: translateY(50%); + transition: all .3s; +} +.team_slide:hover .team_socials { + transform: translateY(0%); +} +.team_socials_item { + margin-right: 12px; +} +.team_socials_item:last-child { + margin-right: 0; +} +.team_socials_link{ + display: block; + line-height: 0; +} +.team_socials_link path{ + transition: all .3s; +} +.team_socials_link:hover path{ + fill: #fff; +} +.team_info { + text-align: center; +} +.team_info_name { + font-size: 20px; + font-weight: 700; + line-height: 1.5; + color: #424551; + margin-bottom: 4px; +} +.team_info_position { + font-size: 16px; + font-weight: 400; + line-height: 1.6; + color: #787A80; +} +.team_slider_arrows { + position: absolute; + right: 0; + bottom: 0; + display: flex; +} +.team_slider_arrow { + display: flex; + border-radius: 50%; + transition: all .3s; +} +.team_slider_arrow:hover { + background-color: #FF3F3A; +} +.team_slider_arrow path { + transition: all .3s; +} +.team_slider_arrow:hover path { + fill: white; +} +.team_slider-prev { + margin-right: 12px; + height: 48px; + width: 48px; + justify-content: center; + align-items: center; +} +.team_slider-next { + height: 48px; + width: 48px; + justify-content: center; + align-items: center; +} +.team_slider_arrows .slick-prev { + +} + +/* --------------------------------MAIN TESTIMONIALS--------------------------------------------- */ + +.main_testimonials { + padding-bottom: 195px; +} +.testimonials { + background: #F4F5F6; +} +.testimonials_inner { +} +.testimonials_heading { + text-align: center; + margin-bottom: 60px; + color: #1E212C; +} +.testimonials_slider_wrapper { + padding: 0 50px; + position: relative; + margin-bottom: 50px; +} +.testimonials_slider { + max-width: 1020px; + margin: 0 auto; +} +.testimonials_slide { + padding: 64px; + background: white; + border-radius: 4px; +} +.testimonials_slide_inner { + max-width: 810px; + margin: 0 auto; + padding-left: 58px; +} +.testimonials_quote { + font-size: 20px; + font-weight: 400; + line-height: 1.5; + color: #424551; + margin-bottom: 24px; + position: relative; +} +.testimonials_quote::before { + content: ""; + position: absolute; + top: 0; + left: -58px; + background: url('./images/3/braces.svg') 0 0 / 100% no-repeat; + height: 20px; + width: 28px; +} +.testimonials_user { + display: flex; + align-items: center; +} +.testimonials_user_avatar { + border-radius: 50%; + overflow: hidden; + margin-right: 20px; +} +.testimonials_info { +} +.testimonials_info_name { + font-size: 16; + font-weight: 700; + line-height: 1.6; + color: #1E212C; + margin-bottom: 4px; +} +.testimonials_info_position { + font-size: 14px; + font-weight: 400; + line-height: 1.5; +} +.testimonials_arrow path { + transition: all .3s; +} +.testimonials_arrow { + display: flex; + border-radius: 50%; + transition: all .3s; + position: relative; + transform: translateY(-50%); +} +.testimonials_arrow:hover { + background-color: #FF3F3A; +} +.testimonials_prev { + position: absolute; + top: 50%; + left: 0; + height: 48px; + width: 48px; + justify-content: center; + align-items: center; +} +.testimonials_next { + position: absolute; + top: 50%; + right: 0; + height: 48px; + width: 48px; + justify-content: center; + align-items: center; +} +.testimonials_arrow:hover path{ + fill: white; +} +.testimonials_dots .slick-dots { + display: flex; + justify-content: center; + align-items: center; +} +.testimonials_dots li { + margin-right: 12px; +} +.testimonials_dots button { + font-size: 0px; + background: transparent; + height: 30px; + width: 30px; + display: flex; + align-items: center; +} + +.testimonials_dots button::before { + content: ""; + height: 3px; + width: 100%; + background-color: #B3B7BC; + border-radius: 3px; +} +.testimonials_dots .slick-active button::before { + background-color: #424551; + transition: all .3s; +} + +/* ----------------------------------MAIN COMPANIES-------------------------------------------- */ + + +.main_companies { + padding-bottom: 177px; + +} +.companies { + background: #F4F5F6; +} +.companies_inner { +} +.companies_heading { + text-align: center; + margin-bottom: 57px; + color: #1E212C; +} +.companies_grid { + display: grid; + grid-template: repeat(2, auto) / repeat(6, 1fr); + gap: 40px 100px; +} +.companies_grid_item { +} +.companies_item_link { + display: block; +} +.companies_item_link img { + height: 100%; + width: 100%; + filter: grayscale(1); + opacity: .4; + transition: all 1.3s; +} +.companies_item_link:hover img { + opacity: 1; + filter: grayscale(0); + transition: all .3s; +} + +/* ---------------------------MAIN PROGRAM-------------------------------- */ + +.main_program { + margin-top: 116px; + margin-bottom: 186px; +} +.program { +} +.container { +} +.program_inner { + display: flex; + justify-content: space-between; + align-items: center; +} +.program_content { + margin-right: 30px; + max-width: 600px; +} +.program_heading { + margin-bottom: 60px; + color: #1E212C; +} +.program_acc { + +} +.program_acc_item { + margin-bottom: 24px; +} +.program_acc_item:last-child { + margin-bottom: 0; +} +.program_acc_link { + display: block; + padding-left: 40px; +} +.program_acc_head { + font-size: 20px; + font-weight: 700; + line-height: 1.5; + color: #1e212c; + position: relative; +} +.program_acc_head span { + font-weight: 400; + color: #FF3F3A; + margin-right: 8px; +} +.program_acc_head::before, +.program_acc_head::after { + content: ""; + position: absolute; + top: 50%; + left: -40px; + width: 18px; + height: 2px; + background-color: #FF3F3A; + border-radius: 2px; +} +.program_acc_head::before { + +} +.program_acc_head::after { + transform: rotate(90deg); + transition: all .3s; +} +.program_acc_text { + margin-top: 16px; +} +.program_acc_link--active .program_acc_head::after { + transform: rotate(0deg); +} +.program_image { +} + +/* -----------------------------------MAIN BLOG------------------------------------------- */ + +.main_blog { + margin-bottom: 213px; +} +.blog { +} +.blog_inner { + +} +.blog_top { + display: flex; + justify-content: space-between; + align-items: flex-end; + margin-bottom: 60px; +} +.blog_heading { + margin-right: 30px; +} +.heading { +} +.heading_subtitle { +} +.heading_title { +} +.blog_btn { +} +.button { +} +.blog_row { + display: flex; + margin: 0 -15px; +} +.blog_item { + padding: 0 15px; + display: block; + +} +.blog_item_link { + display: block; + transition: all .3s; + height: 100%; +} +.blog_item_link:hover { + box-shadow: 0px 30px 30px 10px rgba(154, 156, 165, 0.08), 0px 30px 24px -10px rgba(154, 156, 165, 0.05), 0px 12px 10px -6px rgba(154, 156, 165, 0.04), 0px 4px 4px -4px rgba(30, 33, 44, 0.03); +} +.blog_item_inner { +} +.blog_image_box { + border-radius: 4px; + overflow: hidden; + margin-bottom: 16px; + position: relative; + display: flex; + justify-content: center; + align-items: center; +} +.blog_image_box span { + position: absolute; + top: 12px; + left: 12px; + font-weight: 400; + line-height: 1.5; + color: #424551; + font-size: 14px; + background-color: white; + border-radius: 4px; + padding: 0px 8px; +} +.blog_image_box span img { + width: 16px; + height: 16px; + margin-top: 2px; + margin-right: 4px; +} +.blog_image_box img { + width: auto; + height: auto; + object-fit: cover; +} +.blog_item_content { +} +.blog_item_info { + display: flex; + margin: 0 -12px 8px; +} +.blog_info_el { + padding: 0 12px; + position: relative; + color: #787A80; +} +.blog_info_el::after { + content: ""; + position: absolute; + top: 50%; + right: 0; + width: 1px; + height: 12px; + background-color: #787A80; + transform: translateY(-50%); +} +.blog_info_el:last-child:after { + content: none; +} +.blog_info_dir { +} +.blog_info_date { + display: flex; + align-items: center; +} +.blog_info_date::before { + display: block; + content: ""; + width: 16px; + height: 17px; + background: url("./images/4/icons/Calendar.svg") 0 0 / 100% no-repeat; + margin-right: 9px; +} +.blog_info_time { + display: flex; + align-items: center; +} +.blog_info_time::before { + display: block; + content: ""; + width: 16px; + height: 17px; + background: url("./images/4/icons/Clock.svg") 0 0 / 100% no-repeat; + margin-right: 9px; +} +.blog_item_title { + font-size: 20px; + font-weight: 700; + line-height: 1.5; + color: #1E212C; + margin-bottom: 8px; +} +.blog_item_text { + margin-bottom: 16px; +} +.blog_item_btn { + display: flex; + align-items: center; + font-weight: 700; + line-height: 1.6; + color: #1E212C; + transition: all .3s; +} +.blog_item_btn:hover { + color: #FF3F3A; +} +.blog_item_btn img { + padding: 6px 8px; +} + +/* ---------------------------------MAIN INFO--------------------------------------------------- */ + +.main_info { + margin-bottom: 180px; +} +.info { +} +.container { +} +.info_inner { + display: flex; + align-items: flex-end; + justify-content: space-between; +} +.info_content { + margin-right: 30px; +} +.info_heading { + color: #1E212C; + margin-bottom: 40px; +} +.heading_subtitle { +} +.heading_title { +} +.info_contacts { + +} +.info_contacts_item { + margin-bottom: 24px; + display: flex; + align-items: start; + +} +.info_contacts_item svg { + margin-right: 12px; + height: 24px; + width: 24px; +} +.info_contacts_item:last-child { + margin-bottom: 0; +} +.info_contants_text { + +} +.info_item_top { + font-size: 14px; + font-weight: 700; + line-height: 1.5; + color: #787A80; +} +.info_item_top img { + margin-right: 12px; +} + +.info_item_link { + font-size: 18px; + font-weight: 400; + line-height: 1.5; + color: #1E212C; + transition: all .3s; +} +.info_item_link:hover { + color: #FF3F3A; +} +.info_item_address { + font-size: 18px; + font-weight: 400; + line-height: 1.5; + color: #1E212C; + margin-bottom: 48px; +} +.info_follow { + +} +.info_follow_head { + font-style: 16px; + font-weight: 701; + line-height: 1.5; + letter-spacing: 1px; + text-transform: uppercase; + margin-bottom: 24px; +} +.info_socials { + display: flex; +} +.info_socials_item { +} +.info_socials_link { + margin-right: 32px; +} +.info_socials_link path { + transition: all .3s; +} +.info_socials_link:hover path{ + fill: #000; +} +.info_map { + height: 412px; + width: 100%; + max-width: 705px; + overflow: hidden; +} +.info_map iframe { + border-radius: 4px; + box-shadow: 0 80px 80px -20px rgba(154, 156, 165, 0.08), + 0 30px 24px -10px rgba(154, 156, 165, 0.05), + 0 12px 10px -6px rgba(154, 156, 165, 0.04), + 0 4px 4px -4px rgba(30, 33, 44, 0.03); +} + +/* ---------------------------MAIN QUESTIONS--------------------------------- */ + +.main_questions { + margin-bottom: 180px; +} +.questions { +} +.container { +} +.questions_inner { + display: flex; + justify-content: space-between; + align-items: center; +} +.questions_content { + margin-left: 30px; +} +.questions_heading { + color: #1E212C; + margin-bottom: 40px; +} +.heading_subtitle { +} +.heading_title { +} +.questions_form { +} +.questions_form_row { + display: flex; + margin-bottom: 24px; +} +.questions_text_label { + display: block; + margin-bottom: 48px; +} + +.questions_form_label { + margin-right: 25px; + flex: 1 1 auto; +} +.questions_form_label:last-child { + margin-right: 0; +} +.questions_form_input, +.questions_form_text { + display: block; + border: 1px solid #D7DADD; + border-radius: 4px; + padding: 13px 16px; + width: 100%; + margin-top: 8px; +} +.questions_form_input::placeholder, +.questions_form_text::placeholder { + color: #9a9ca5; + font-size: 16px; + font-weight: 400; + line-height: 1.6; +} +.questions_form_text { + resize: none; + height: 128px; +} +.questions_checkbox_label { + margin-right: 50px; + display: flex; + width: 100%; +} +.questions_form_checkbox { + width: 16px; + height: 16px; + flex: 0 0 auto; + margin-top: 5px; + margin-right: 12px; + accent-color: red; +} +.questions_form-default { + +} + +.questions_form-custom { + +} +.questions_form_btn { + width: 100%; + align-self: center; + text-align: center; +} +.button { +} + +/* ------------------------------------FOOTER------------------------------------------- */ + +.footer { + color: white; + +} +.footer_top { + background-color: #1e212c; + padding-top: 80px; +} +.container { +} +.footer_top_inner { + display: grid; + grid-template-columns: repeat(5, auto); + gap: 80px; +} +.fotter_top_col { + width: auto; +} +.fotter_top_col:first-child { + +} +.footer_logo { + display: block; + margin-bottom: 24px; +} +.logo { + +} +.footer_top_text { + font-size: 12px; + font-weight: 400; + line-height: 1.5; + opacity: 0.6; + margin-bottom: 38px; +} +.footer_socials { + display: flex; + margin-bottom: 60px; +} +.footer_socials_item { + margin-right: 20px; +} +.footer_socials_item:last-child { + margin-right: 0; +} +.footer_socials_link path { + transition: all .3s; +} +.footer_socials_link:hover path { + fill: #fff; +} +.footer_top_title { + font-size: 16px; + font-weight: 701; + line-height: 1.5; + margin-bottom: 12px; + +} +.footer_top_title--title1 { + font-size: 16px; + font-weight: 701; + line-height: 1.5; + margin-bottom: 24px; + white-space: nowrap; +} +.footer_top_list { +} +.footer_top_item { + +} +.footer_item_link { + font-size: 16px; + font-weight: 400; + line-height: 1.6; + opacity: 0.6; + margin-bottom: 8px; + transition: all .3s; + display: flex; + white-space: nowrap; +} +.footer_item_link:hover { + opacity: 1; +} +.footer_item_link:last-child { + margin-bottom: 0; +} +.footer_item_link svg { + display: block; + width: 16px; + height: 16px; + margin-right: 8px; + margin-top: 4px; +} +.footer_top_form { + display: block; + overflow: hidden; + position: relative; +} +.footer_form_input { + width: 100%; + padding: 9px 12px; + border-radius: 4px; + border: 1px solid rgba(255, 255, 255, 0.20); + background: rgba(255, 255, 255, 0.12); + color: #fff; + opacity: 0.6; + font-size: 12px; + font-weight: 400; + line-height: 1.5; +} +.footer_form_btn { + display: block; + width: 16px; + height: 16px; +} +.footer_form_img { + position: absolute; + top: 50%; + right: 12px; + top: 16px; +} +.footer_form_desc { + font-size: 10px; + font-weight: 400; + line-height: 1.5; +} +.footer_bottom { + background-color: rgb(41, 44, 55); +} +.footer_bottom_inner { + display: flex; + justify-content: space-between; + padding: 20px 0px; +} +.footer_copy { + font-size: 12px; + font-weight: 400; + line-height: 1.5; + display: flex; +} +.footer_copy span { + display: block; + height: 16px; + width: 16px; + margin: 0px 4px; + align-items: center; + justify-content: center; +} +.footer_go_top { + font-size: 14px; + font-weight: 701; + line-height: 1.5; + opacity: 0.6; + transition: all .3s; +} +.footer_go_top:hover { + opacity: 1; +}