+
+{% endblock %}
+
+{% block scripts_page %}
+
+
+
+
+ {{ assets.js('js/pages/set-or-reset-password') | raw }}
+
+{% endblock %}
diff --git a/main/app/sprinkles/account/templates/pages/set-password.html.twig b/main/app/sprinkles/account/templates/pages/set-password.html.twig
new file mode 100755
index 0000000..3c4fe2b
--- /dev/null
+++ b/main/app/sprinkles/account/templates/pages/set-password.html.twig
@@ -0,0 +1,55 @@
+{% extends "pages/abstract/base.html.twig" %}
+
+{# Overrides blocks in head of base template #}
+{% block page_title %}{{translate("PASSWORD.CREATE")}}{% endblock %}
+
+{% block page_description %}{{translate("PASSWORD.CREATE.PAGE")}}{% endblock %}
+
+{% block body_attributes %}
+ class="hold-transition login-page"
+{% endblock %}
+
+{% block content %}
+
+
+
+
+
+
{{translate("PASSWORD.CREATE")}}
+
{{translate("WELCOME_TO", {'title': site.title})}} {{translate("PASSWORD.CREATE.PAGE")}}
+
+
+
+
+
+
+{% endblock %}
+
+{% block scripts_page %}
+
+
+
+
+ {{ assets.js('js/pages/set-or-reset-password') | raw }}
+
+{% endblock %}
\ No newline at end of file
diff --git a/main/app/sprinkles/account/templates/pages/sign-in.html.twig b/main/app/sprinkles/account/templates/pages/sign-in.html.twig
new file mode 100755
index 0000000..2fb6e1c
--- /dev/null
+++ b/main/app/sprinkles/account/templates/pages/sign-in.html.twig
@@ -0,0 +1,84 @@
+{% extends "pages/abstract/base.html.twig" %}
+
+{# Overrides blocks in head of base template #}
+{% block page_title %}{{translate('SIGNIN')}}{% endblock %}
+
+{% block page_description %}{{translate('PAGE.LOGIN.DESCRIPTION', {'site_name': site.title })}}{% endblock %}
+
+{% block body_attributes %}
+ class="hold-transition login-page"
+{% endblock %}
+
+{% block content %}
+
+
+{% endblock %}
+
+{% block scripts_page %}
+
+
+
+
+
+
+ {{ assets.js('js/pages/sign-in') | raw }}
+{% endblock %}
diff --git a/main/app/sprinkles/account/tests/Unit/FactoriesTest.php b/main/app/sprinkles/account/tests/Unit/FactoriesTest.php
new file mode 100755
index 0000000..ee2bf23
--- /dev/null
+++ b/main/app/sprinkles/account/tests/Unit/FactoriesTest.php
@@ -0,0 +1,30 @@
+ci->factory;
+
+ $user = $fm->create('UserFrosting\Sprinkle\Account\Database\Models\User');
+ $this->assertInstanceOf('UserFrosting\Sprinkle\Account\Database\Models\User', $user);
+ }
+}
diff --git a/main/app/sprinkles/account/tests/Unit/HasherTest.php b/main/app/sprinkles/account/tests/Unit/HasherTest.php
new file mode 100755
index 0000000..711e3cb
--- /dev/null
+++ b/main/app/sprinkles/account/tests/Unit/HasherTest.php
@@ -0,0 +1,71 @@
+getHashType($this->modernHash);
+
+ $this->assertEquals('modern', $type);
+
+ $type = $hasher->getHashType($this->legacyHash);
+
+ $this->assertEquals('legacy', $type);
+
+ $type = $hasher->getHashType($this->userCakeHash);
+
+ $this->assertEquals('sha1', $type);
+ }
+
+ public function testVerify()
+ {
+ $hasher = new Hasher;
+
+ $this->assertTrue($hasher->verify($this->plainText, $this->modernHash));
+ $this->assertTrue($hasher->verify($this->plainText, $this->legacyHash));
+ $this->assertTrue($hasher->verify($this->plainText, $this->userCakeHash));
+ }
+
+ public function testVerifyReject()
+ {
+ $hasher = new Hasher;
+
+ $this->assertFalse($hasher->verify('selleth', $this->modernHash));
+ $this->assertFalse($hasher->verify('selleth', $this->legacyHash));
+ $this->assertFalse($hasher->verify('selleth', $this->userCakeHash));
+ }
+}
diff --git a/main/app/sprinkles/admin/asset-bundles.json b/main/app/sprinkles/admin/asset-bundles.json
new file mode 100755
index 0000000..1bc1706
--- /dev/null
+++ b/main/app/sprinkles/admin/asset-bundles.json
@@ -0,0 +1,170 @@
+{
+ "bundle": {
+ "js/admin": {
+ "scripts": [
+ "vendor/moment/moment.js",
+ "userfrosting/js/handlebars-helpers.js",
+ "vendor/tablesorter/dist/js/jquery.tablesorter.js",
+ "vendor/tablesorter/dist/js/jquery.tablesorter.widgets.js",
+ "userfrosting/js/tablesorter/widget-sort2Hash.js",
+ "vendor/tablesorter/dist/js/widgets/widget-columnSelector.min.js",
+ "vendor/tablesorter/dist/js/widgets/widget-reflow.min.js",
+ "vendor/tablesorter/dist/js/widgets/widget-pager.min.js",
+ "userfrosting/js/query-string.js",
+ "userfrosting/js/uf-table.js"
+ ],
+ "options": {
+ "result": {
+ "type": {
+ "scripts": "plain"
+ }
+ }
+ }
+ },
+ "js/pages/activities": {
+ "scripts": [
+ "userfrosting/js/pages/activities.js"
+ ],
+ "options": {
+ "result": {
+ "type": {
+ "scripts": "plain"
+ }
+ }
+ }
+ },
+ "js/pages/dashboard": {
+ "scripts": [
+ "userfrosting/js/widgets/users.js",
+ "userfrosting/js/pages/dashboard.js"
+ ],
+ "options": {
+ "result": {
+ "type": {
+ "scripts": "plain"
+ }
+ }
+ }
+ },
+ "js/pages/group": {
+ "scripts": [
+ "userfrosting/js/widgets/users.js",
+ "userfrosting/js/widgets/groups.js",
+ "userfrosting/js/pages/group.js"
+ ],
+ "options": {
+ "result": {
+ "type": {
+ "scripts": "plain"
+ }
+ }
+ }
+ },
+ "js/pages/permission": {
+ "scripts": [
+ "userfrosting/js/widgets/users.js",
+ "userfrosting/js/pages/permission.js"
+ ],
+ "options": {
+ "result": {
+ "type": {
+ "scripts": "plain"
+ }
+ }
+ }
+ },
+ "js/pages/role": {
+ "scripts": [
+ "userfrosting/js/widgets/roles.js",
+ "userfrosting/js/pages/role.js"
+ ],
+ "options": {
+ "result": {
+ "type": {
+ "scripts": "plain"
+ }
+ }
+ }
+ },
+ "js/pages/user": {
+ "scripts": [
+ "userfrosting/js/widgets/users.js",
+ "userfrosting/js/pages/user.js"
+ ],
+ "options": {
+ "result": {
+ "type": {
+ "scripts": "plain"
+ }
+ }
+ }
+ },
+ "js/pages/users": {
+ "scripts": [
+ "userfrosting/js/widgets/users.js",
+ "userfrosting/js/pages/users.js"
+ ],
+ "options": {
+ "result": {
+ "type": {
+ "scripts": "plain"
+ }
+ }
+ }
+ },
+ "js/pages/groups": {
+ "scripts": [
+ "userfrosting/js/widgets/groups.js",
+ "userfrosting/js/pages/groups.js"
+ ],
+ "options": {
+ "result": {
+ "type": {
+ "scripts": "plain"
+ }
+ }
+ }
+ },
+ "js/pages/permissions": {
+ "scripts": [
+ "userfrosting/js/pages/permissions.js"
+ ],
+ "options": {
+ "result": {
+ "type": {
+ "scripts": "plain"
+ }
+ }
+ }
+ },
+ "js/pages/roles": {
+ "scripts": [
+ "userfrosting/js/widgets/roles.js",
+ "userfrosting/js/pages/roles.js"
+ ],
+ "options": {
+ "result": {
+ "type": {
+ "scripts": "plain"
+ }
+ }
+ }
+ },
+ "css/admin": {
+ "styles": [
+ "font-starcraft/css/font-starcraft.css",
+ "vendor/tablesorter/dist/css/theme.bootstrap.min.css",
+ "vendor/tablesorter/dist/css/jquery.tablesorter.pager.min.css",
+ "userfrosting/css/tablesorter-reflow.css",
+ "userfrosting/css/tablesorter-custom.css"
+ ],
+ "options": {
+ "result": {
+ "type": {
+ "styles": "plain"
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/main/app/sprinkles/admin/assets/userfrosting/css/tablesorter-custom.css b/main/app/sprinkles/admin/assets/userfrosting/css/tablesorter-custom.css
new file mode 100755
index 0000000..405c1ca
--- /dev/null
+++ b/main/app/sprinkles/admin/assets/userfrosting/css/tablesorter-custom.css
@@ -0,0 +1,30 @@
+/* ==========================================================================
+ Custom styling for tablesorter tables
+ ========================================================================== */
+
+/* Custom styling for pager controls
+ ========================================================================== */
+
+.pager-lg {
+ font-size: large;
+}
+
+.pager-control {
+ font-size: x-large;
+ padding: 4px 8px;
+ cursor: pointer;
+}
+
+/* Custom styling for panels that contain buttons in the header (eg, CSV download buttons)
+ ========================================================================== */
+
+.panel-heading-buttons h3 {
+ padding-top: 7.5px;
+}
+
+/* Don't display tablesorter filter field when disabled
+ ========================================================================== */
+
+.tablesorter thead .disabled {
+ display: none
+}
\ No newline at end of file
diff --git a/main/app/sprinkles/admin/assets/userfrosting/js/pages/activities.js b/main/app/sprinkles/admin/assets/userfrosting/js/pages/activities.js
new file mode 100755
index 0000000..89ac5c1
--- /dev/null
+++ b/main/app/sprinkles/admin/assets/userfrosting/js/pages/activities.js
@@ -0,0 +1,16 @@
+/**
+ * Page-specific Javascript file. Should generally be included as a separate asset bundle in your page template.
+ * example: {{ assets.js('js/pages/sign-in-or-register') | raw }}
+ *
+ * This script depends on uf-table.js, moment.js, handlebars-helpers.js
+ *
+ * Target page: /activities
+ */
+
+$(document).ready(function() {
+ // Set up table of activities
+ $("#widget-activities").ufTable({
+ dataUrl: site.uri.public + "/api/activities",
+ useLoadingTransition: site.uf_table.use_loading_transition
+ });
+});
diff --git a/main/app/sprinkles/admin/assets/userfrosting/js/pages/dashboard.js b/main/app/sprinkles/admin/assets/userfrosting/js/pages/dashboard.js
new file mode 100755
index 0000000..f2b8a4f
--- /dev/null
+++ b/main/app/sprinkles/admin/assets/userfrosting/js/pages/dashboard.js
@@ -0,0 +1,49 @@
+/**
+ * Page-specific Javascript file. Should generally be included as a separate asset bundle in your page template.
+ * example: {{ assets.js('js/pages/sign-in-or-register') | raw }}
+ *
+ * Target page: /dashboard
+ */
+
+$(document).ready(function() {
+ $('.js-clear-cache').click(function() {
+ $("body").ufModal({
+ sourceUrl: site.uri.public + "/modals/dashboard/clear-cache",
+ ajaxParams: {
+ slug: $(this).data('slug')
+ },
+ msgTarget: $("#alerts-page")
+ });
+
+ $("body").on('renderSuccess.ufModal', function (data) {
+ var modal = $(this).ufModal('getModal');
+ var form = modal.find('.js-form');
+
+ form.ufForm()
+ .on("submitSuccess.ufForm", function() {
+ // Reload page on success
+ window.location.reload();
+ });
+ });
+ });
+
+ // Table of site activities
+ $("#widget-activities").ufTable({
+ dataUrl: site.uri.public + "/api/activities",
+ useLoadingTransition: site.uf_table.use_loading_transition
+ });
+
+ // Table of users in current user's group
+ $("#widget-group-users").ufTable({
+ dataUrl: site.uri.public + "/api/groups/g/" + page.group_slug + "/users",
+ useLoadingTransition: site.uf_table.use_loading_transition
+ });
+
+ // Bind user creation button
+ bindUserCreationButton($("#widget-group-users"));
+
+ // Bind user table buttons
+ $("#widget-group-users").on("pagerComplete.ufTable", function () {
+ bindUserButtons($(this));
+ });
+});
diff --git a/main/app/sprinkles/admin/assets/userfrosting/js/pages/group.js b/main/app/sprinkles/admin/assets/userfrosting/js/pages/group.js
new file mode 100755
index 0000000..a1ca959
--- /dev/null
+++ b/main/app/sprinkles/admin/assets/userfrosting/js/pages/group.js
@@ -0,0 +1,24 @@
+/**
+ * Page-specific Javascript file. Should generally be included as a separate asset bundle in your page template.
+ * example: {{ assets.js('js/pages/sign-in-or-register') | raw }}
+ *
+ * This script depends on uf-table.js, moment.js, handlebars-helpers.js
+ *
+ * Target page: /groups/g/{slug}
+ */
+
+$(document).ready(function() {
+ // Control buttons
+ bindGroupButtons($("#view-group"));
+
+ // Table of users in this group
+ $("#widget-group-users").ufTable({
+ dataUrl: site.uri.public + '/api/groups/g/' + page.group_slug + '/users',
+ useLoadingTransition: site.uf_table.use_loading_transition
+ });
+
+ // Bind user table buttons
+ $("#widget-group-users").on("pagerComplete.ufTable", function () {
+ bindUserButtons($(this));
+ });
+});
diff --git a/main/app/sprinkles/admin/assets/userfrosting/js/pages/groups.js b/main/app/sprinkles/admin/assets/userfrosting/js/pages/groups.js
new file mode 100755
index 0000000..0bfc65a
--- /dev/null
+++ b/main/app/sprinkles/admin/assets/userfrosting/js/pages/groups.js
@@ -0,0 +1,24 @@
+/**
+ * Page-specific Javascript file. Should generally be included as a separate asset bundle in your page template.
+ * example: {{ assets.js('js/pages/sign-in-or-register') | raw }}
+ *
+ * This script depends on widgets/groups.js, uf-table.js, moment.js, handlebars-helpers.js
+ *
+ * Target page: /groups
+ */
+
+$(document).ready(function() {
+
+ $("#widget-groups").ufTable({
+ dataUrl: site.uri.public + "/api/groups",
+ useLoadingTransition: site.uf_table.use_loading_transition
+ });
+
+ // Bind creation button
+ bindGroupCreationButton($("#widget-groups"));
+
+ // Bind table buttons
+ $("#widget-groups").on("pagerComplete.ufTable", function () {
+ bindGroupButtons($(this));
+ });
+});
diff --git a/main/app/sprinkles/admin/assets/userfrosting/js/pages/permission.js b/main/app/sprinkles/admin/assets/userfrosting/js/pages/permission.js
new file mode 100755
index 0000000..87e851f
--- /dev/null
+++ b/main/app/sprinkles/admin/assets/userfrosting/js/pages/permission.js
@@ -0,0 +1,20 @@
+/**
+ * Page-specific Javascript file. Should generally be included as a separate asset bundle in your page template.
+ * example: {{ assets.js('js/pages/sign-in-or-register') | raw }}
+ *
+ * This script depends on uf-table.js, moment.js, handlebars-helpers.js
+ *
+ * Target page: /permissions/p/{id}
+ */
+
+$(document).ready(function() {
+ $("#widget-permission-users").ufTable({
+ dataUrl: site.uri.public + '/api/permissions/p/' + page.permission_id + '/users',
+ useLoadingTransition: site.uf_table.use_loading_transition
+ });
+
+ // Bind table buttons
+ $("#widget-permission-users").on("pagerComplete.ufTable", function () {
+ bindUserButtons($(this));
+ });
+});
diff --git a/main/app/sprinkles/admin/assets/userfrosting/js/pages/permissions.js b/main/app/sprinkles/admin/assets/userfrosting/js/pages/permissions.js
new file mode 100755
index 0000000..6266ff4
--- /dev/null
+++ b/main/app/sprinkles/admin/assets/userfrosting/js/pages/permissions.js
@@ -0,0 +1,16 @@
+/**
+ * Page-specific Javascript file. Should generally be included as a separate asset bundle in your page template.
+ * example: {{ assets.js('js/pages/sign-in-or-register') | raw }}
+ *
+ * This script depends on widgets/permissions.js, uf-table.js, moment.js, handlebars-helpers.js
+ *
+ * Target page: /permissions
+ */
+
+$(document).ready(function() {
+ // Set up table of permissions
+ $("#widget-permissions").ufTable({
+ dataUrl: site.uri.public + "/api/permissions",
+ useLoadingTransition: site.uf_table.use_loading_transition
+ });
+});
diff --git a/main/app/sprinkles/admin/assets/userfrosting/js/pages/role.js b/main/app/sprinkles/admin/assets/userfrosting/js/pages/role.js
new file mode 100755
index 0000000..8dae7f5
--- /dev/null
+++ b/main/app/sprinkles/admin/assets/userfrosting/js/pages/role.js
@@ -0,0 +1,23 @@
+/**
+ * Page-specific Javascript file. Should generally be included as a separate asset bundle in your page template.
+ * example: {{ assets.js('js/pages/sign-in-or-register') | raw }}
+ *
+ * This script depends on uf-table.js, moment.js, handlebars-helpers.js
+ *
+ * Target page: /roles/r/{slug}
+ */
+
+$(document).ready(function() {
+ // Control buttons
+ bindRoleButtons($("#view-role"));
+
+ $("#widget-role-permissions").ufTable({
+ dataUrl: site.uri.public + '/api/roles/r/' + page.role_slug + '/permissions',
+ useLoadingTransition: site.uf_table.use_loading_transition
+ });
+
+ $("#widget-role-users").ufTable({
+ dataUrl: site.uri.public + '/api/roles/r/' + page.role_slug + '/users',
+ useLoadingTransition: site.uf_table.use_loading_transition
+ });
+});
diff --git a/main/app/sprinkles/admin/assets/userfrosting/js/pages/roles.js b/main/app/sprinkles/admin/assets/userfrosting/js/pages/roles.js
new file mode 100755
index 0000000..b1febb2
--- /dev/null
+++ b/main/app/sprinkles/admin/assets/userfrosting/js/pages/roles.js
@@ -0,0 +1,24 @@
+/**
+ * Page-specific Javascript file. Should generally be included as a separate asset bundle in your page template.
+ * example: {{ assets.js('js/pages/sign-in-or-register') | raw }}
+ *
+ * This script depends on widgets/roles.js, uf-table.js, moment.js, handlebars-helpers.js
+ *
+ * Target page: /roles
+ */
+
+$(document).ready(function() {
+ // Set up table of roles
+ $("#widget-roles").ufTable({
+ dataUrl: site.uri.public + "/api/roles",
+ useLoadingTransition: site.uf_table.use_loading_transition
+ });
+
+ // Bind creation button
+ bindRoleCreationButton($("#widget-roles"));
+
+ // Bind table buttons
+ $("#widget-roles").on("pagerComplete.ufTable", function () {
+ bindRoleButtons($(this));
+ });
+});
diff --git a/main/app/sprinkles/admin/assets/userfrosting/js/pages/user.js b/main/app/sprinkles/admin/assets/userfrosting/js/pages/user.js
new file mode 100755
index 0000000..70acf7c
--- /dev/null
+++ b/main/app/sprinkles/admin/assets/userfrosting/js/pages/user.js
@@ -0,0 +1,25 @@
+/**
+ * Page-specific Javascript file. Should generally be included as a separate asset bundle in your page template.
+ * example: {{ assets.js('js/pages/sign-in-or-register') | raw }}
+ *
+ * This script depends on uf-table.js, moment.js, handlebars-helpers.js
+ *
+ * Target page: /users/u/{user_name}
+ */
+
+$(document).ready(function() {
+ // Control buttons
+ bindUserButtons($("#view-user"));
+
+ // Table of activities
+ $("#widget-user-activities").ufTable({
+ dataUrl: site.uri.public + '/api/users/u/' + page.user_name + '/activities',
+ useLoadingTransition: site.uf_table.use_loading_transition
+ });
+
+ // Table of permissions
+ $("#widget-permissions").ufTable({
+ dataUrl: site.uri.public + '/api/users/u/' + page.user_name + '/permissions',
+ useLoadingTransition: site.uf_table.use_loading_transition
+ });
+});
diff --git a/main/app/sprinkles/admin/assets/userfrosting/js/pages/users.js b/main/app/sprinkles/admin/assets/userfrosting/js/pages/users.js
new file mode 100755
index 0000000..d9e4bb7
--- /dev/null
+++ b/main/app/sprinkles/admin/assets/userfrosting/js/pages/users.js
@@ -0,0 +1,24 @@
+/**
+ * Page-specific Javascript file. Should generally be included as a separate asset bundle in your page template.
+ * example: {{ assets.js('js/pages/sign-in-or-register') | raw }}
+ *
+ * This script depends on widgets/users.js, uf-table.js, moment.js, handlebars-helpers.js
+ *
+ * Target page: /users
+ */
+
+$(document).ready(function() {
+ // Set up table of users
+ $("#widget-users").ufTable({
+ dataUrl: site.uri.public + "/api/users",
+ useLoadingTransition: site.uf_table.use_loading_transition
+ });
+
+ // Bind creation button
+ bindUserCreationButton($("#widget-users"));
+
+ // Bind table buttons
+ $("#widget-users").on("pagerComplete.ufTable", function () {
+ bindUserButtons($(this));
+ });
+});
diff --git a/main/app/sprinkles/admin/assets/userfrosting/js/widgets/groups.js b/main/app/sprinkles/admin/assets/userfrosting/js/widgets/groups.js
new file mode 100755
index 0000000..d701d81
--- /dev/null
+++ b/main/app/sprinkles/admin/assets/userfrosting/js/widgets/groups.js
@@ -0,0 +1,111 @@
+/**
+ * Groups widget. Sets up dropdowns, modals, etc for a table of groups.
+ */
+
+/**
+ * Set up the form in a modal after being successfully attached to the body.
+ */
+function attachGroupForm() {
+ $("body").on('renderSuccess.ufModal', function (data) {
+ var modal = $(this).ufModal('getModal');
+ var form = modal.find('.js-form');
+
+ /**
+ * Set up modal widgets
+ */
+ // Set up any widgets inside the modal
+ form.find(".js-select2").select2({
+ width: '100%'
+ });
+
+ // Auto-generate slug
+ form.find('input[name=name]').on('input change', function() {
+ var manualSlug = form.find('#form-group-slug-override').prop('checked');
+ if (!manualSlug) {
+ var slug = getSlug($(this).val());
+ form.find('input[name=slug]').val(slug);
+ }
+ });
+
+ form.find('#form-group-slug-override').on('change', function() {
+ if ($(this).prop('checked')) {
+ form.find('input[name=slug]').prop('readonly', false);
+ } else {
+ form.find('input[name=slug]').prop('readonly', true);
+ form.find('input[name=name]').trigger('change');
+ }
+ });
+
+ // Set icon when changed
+ form.find('input[name=icon]').on('input change', function() {
+ $(this).prev(".icon-preview").find("i").removeClass().addClass($(this).val());
+ });
+
+ // Set up the form for submission
+ form.ufForm({
+ validators: page.validators
+ }).on("submitSuccess.ufForm", function() {
+ // Reload page on success
+ window.location.reload();
+ });
+ });
+}
+
+/**
+ * Link group action buttons, for example in a table or on a specific group's page.
+ */
+function bindGroupButtons(el) {
+ /**
+ * Link row buttons after table is loaded.
+ */
+
+ /**
+ * Buttons that launch a modal dialog
+ */
+ // Edit group details button
+ el.find('.js-group-edit').click(function() {
+ $("body").ufModal({
+ sourceUrl: site.uri.public + "/modals/groups/edit",
+ ajaxParams: {
+ slug: $(this).data('slug')
+ },
+ msgTarget: $("#alerts-page")
+ });
+
+ attachGroupForm();
+ });
+
+ // Delete group button
+ el.find('.js-group-delete').click(function() {
+ $("body").ufModal({
+ sourceUrl: site.uri.public + "/modals/groups/confirm-delete",
+ ajaxParams: {
+ slug: $(this).data('slug')
+ },
+ msgTarget: $("#alerts-page")
+ });
+
+ $("body").on('renderSuccess.ufModal', function (data) {
+ var modal = $(this).ufModal('getModal');
+ var form = modal.find('.js-form');
+
+ form.ufForm()
+ .on("submitSuccess.ufForm", function() {
+ // Reload page on success
+ window.location.reload();
+ });
+ });
+ });
+}
+
+function bindGroupCreationButton(el) {
+ // Link create button
+ el.find('.js-group-create').click(function() {
+ $("body").ufModal({
+ sourceUrl: site.uri.public + "/modals/groups/create",
+ msgTarget: $("#alerts-page")
+ });
+
+ attachGroupForm();
+ });
+};
diff --git a/main/app/sprinkles/admin/assets/userfrosting/js/widgets/roles.js b/main/app/sprinkles/admin/assets/userfrosting/js/widgets/roles.js
new file mode 100755
index 0000000..0e32651
--- /dev/null
+++ b/main/app/sprinkles/admin/assets/userfrosting/js/widgets/roles.js
@@ -0,0 +1,148 @@
+/**
+ * Roles widget. Sets up dropdowns, modals, etc for a table of roles.
+ */
+
+/**
+ * Set up the form in a modal after being successfully attached to the body.
+ */
+function attachRoleForm() {
+ $("body").on('renderSuccess.ufModal', function (data) {
+ var modal = $(this).ufModal('getModal');
+ var form = modal.find('.js-form');
+
+ /**
+ * Set up modal widgets
+ */
+
+ // Auto-generate slug
+ form.find('input[name=name]').on('input change', function() {
+ var manualSlug = form.find('#form-role-slug-override').prop('checked');
+ if (!manualSlug) {
+ var slug = getSlug($(this).val());
+ form.find('input[name=slug]').val(slug);
+ }
+ });
+
+ form.find('#form-role-slug-override').on('change', function() {
+ if ($(this).prop('checked')) {
+ form.find('input[name=slug]').prop('readonly', false);
+ } else {
+ form.find('input[name=slug]').prop('readonly', true);
+ form.find('input[name=name]').trigger('change');
+ }
+ });
+
+ // Set up the form for submission
+ form.ufForm({
+ validators: page.validators
+ }).on("submitSuccess.ufForm", function() {
+ // Reload page on success
+ window.location.reload();
+ });
+ });
+}
+
+/**
+ * Link role action buttons, for example in a table or on a specific role's page.
+ */
+function bindRoleButtons(el) {
+ /**
+ * Link row buttons after table is loaded.
+ */
+
+ // Manage permissions button
+ el.find('.js-role-permissions').click(function() {
+ var slug = $(this).data('slug');
+ $("body").ufModal({
+ sourceUrl: site.uri.public + "/modals/roles/permissions",
+ ajaxParams: {
+ slug: slug
+ },
+ msgTarget: $("#alerts-page")
+ });
+
+ $("body").on('renderSuccess.ufModal', function (data) {
+ var modal = $(this).ufModal('getModal');
+ var form = modal.find('.js-form');
+
+ // Set up collection widget
+ var permissionWidget = modal.find('.js-form-permissions');
+ permissionWidget.ufCollection({
+ dropdown: {
+ ajax: {
+ url : site.uri.public + '/api/permissions'
+ },
+ placeholder : "Select a permission"
+ },
+ dropdownTemplate: modal.find('#role-permissions-select-option').html(),
+ rowTemplate : modal.find('#role-permissions-row').html()
+ });
+
+ // Get current roles and add to widget
+ $.getJSON(site.uri.public + '/api/roles/r/' + slug + '/permissions')
+ .done(function (data) {
+ $.each(data.rows, function (idx, permission) {
+ permission.text = permission.name;
+ permissionWidget.ufCollection('addRow', permission);
+ });
+ });
+
+ // Set up form for submission
+ form.ufForm({
+ }).on("submitSuccess.ufForm", function() {
+ // Reload page on success
+ window.location.reload();
+ });
+ });
+ });
+
+ /**
+ * Buttons that launch a modal dialog
+ */
+ // Edit role details button
+ el.find('.js-role-edit').click(function() {
+ $("body").ufModal({
+ sourceUrl: site.uri.public + "/modals/roles/edit",
+ ajaxParams: {
+ slug: $(this).data('slug')
+ },
+ msgTarget: $("#alerts-page")
+ });
+
+ attachRoleForm();
+ });
+
+ // Delete role button
+ el.find('.js-role-delete').click(function() {
+ $("body").ufModal({
+ sourceUrl: site.uri.public + "/modals/roles/confirm-delete",
+ ajaxParams: {
+ slug: $(this).data('slug')
+ },
+ msgTarget: $("#alerts-page")
+ });
+
+ $("body").on('renderSuccess.ufModal', function (data) {
+ var modal = $(this).ufModal('getModal');
+ var form = modal.find('.js-form');
+
+ form.ufForm()
+ .on("submitSuccess.ufForm", function() {
+ // Reload page on success
+ window.location.reload();
+ });
+ });
+ });
+}
+
+function bindRoleCreationButton(el) {
+ // Link create button
+ el.find('.js-role-create').click(function() {
+ $("body").ufModal({
+ sourceUrl: site.uri.public + "/modals/roles/create",
+ msgTarget: $("#alerts-page")
+ });
+
+ attachRoleForm();
+ });
+};
diff --git a/main/app/sprinkles/admin/assets/userfrosting/js/widgets/users.js b/main/app/sprinkles/admin/assets/userfrosting/js/widgets/users.js
new file mode 100755
index 0000000..2e153e5
--- /dev/null
+++ b/main/app/sprinkles/admin/assets/userfrosting/js/widgets/users.js
@@ -0,0 +1,277 @@
+/**
+ * Users widget. Sets up dropdowns, modals, etc for a table of users.
+ */
+
+/**
+ * Set up the form in a modal after being successfully attached to the body.
+ */
+function attachUserForm() {
+ $("body").on('renderSuccess.ufModal', function (data) {
+ var modal = $(this).ufModal('getModal');
+ var form = modal.find('.js-form');
+
+ // Set up any widgets inside the modal
+ form.find(".js-select2").select2({
+ width: '100%'
+ });
+
+ // Set up the form for submission
+ form.ufForm({
+ validators: page.validators
+ }).on("submitSuccess.ufForm", function() {
+ // Reload page on success
+ window.location.reload();
+ });
+ });
+}
+
+/**
+ * Enable/disable password fields when switch is toggled
+ */
+function toggleChangePasswordMode(el, userName, changePasswordMode) {
+ var form = el.find("form");
+ if (changePasswordMode == 'link') {
+ $(".controls-password").find("input[type='password']").prop('disabled', true);
+ // Form submits password reset request
+ form.attr({
+ method: 'POST',
+ action: site.uri.public + '/api/users/u/' + userName + '/password-reset'
+ });
+
+ var validator = form.validate();
+ if (validator) {
+ //Iterate through named elements inside of the form, and mark them as error free
+ el.find("input[type='password']").each(function() {
+ validator.successList.push(this); //mark as error free
+ });
+ validator.resetForm();//remove error class on name elements and clear history
+ validator.reset();//remove all error and success data
+ }
+ el.find("input[type='password']").closest('.form-group')
+ .removeClass('has-error has-success');
+ el.find('.form-control-feedback').each(function () {
+ $(this).remove();
+ });
+ } else {
+ $(".controls-password").find("input[type='password']").prop('disabled', false);
+ // Form submits direct password update
+ form.attr({
+ method: 'PUT',
+ action: site.uri.public + '/api/users/u/' + userName + '/password'
+ });
+ }
+}
+
+/**
+ * Update user field(s)
+ */
+function updateUser(userName, fieldName, fieldValue) {
+ var data = {
+ 'value': fieldValue
+ };
+
+ data[site.csrf.keys.name] = site.csrf.name;
+ data[site.csrf.keys.value] = site.csrf.value;
+
+ var url = site.uri.public + '/api/users/u/' + userName + '/' + fieldName;
+ var debugAjax = (typeof site !== "undefined") && site.debug.ajax;
+
+ return $.ajax({
+ type: "PUT",
+ url: url,
+ data: data,
+ dataType: debugAjax ? 'html' : 'json',
+ converters: {
+ // Override jQuery's strict JSON parsing
+ 'text json': function(result) {
+ try {
+ // First try to use native browser parsing
+ if (typeof JSON === 'object' && typeof JSON.parse === 'function') {
+ return JSON.parse(result);
+ } else {
+ return $.parseJSON(result);
+ }
+ } catch (e) {
+ // statements to handle any exceptions
+ console.log("Warning: Could not parse expected JSON response.");
+ return {};
+ }
+ }
+ }
+ }).fail(function (jqXHR) {
+ // Error messages
+ if (debugAjax && jqXHR.responseText) {
+ document.write(jqXHR.responseText);
+ document.close();
+ } else {
+ console.log("Error (" + jqXHR.status + "): " + jqXHR.responseText );
+
+ // Display errors on failure
+ // TODO: ufAlerts widget should have a 'destroy' method
+ if (!$("#alerts-page").data('ufAlerts')) {
+ $("#alerts-page").ufAlerts();
+ } else {
+ $("#alerts-page").ufAlerts('clear');
+ }
+
+ $("#alerts-page").ufAlerts('fetch').ufAlerts('render');
+ }
+
+ return jqXHR;
+ }).done(function (response) {
+ window.location.reload();
+ });
+}
+
+/**
+ * Link user action buttons, for example in a table or on a specific user's page.
+ */
+ function bindUserButtons(el) {
+
+ /**
+ * Buttons that launch a modal dialog
+ */
+ // Edit general user details button
+ el.find('.js-user-edit').click(function() {
+ $("body").ufModal({
+ sourceUrl: site.uri.public + "/modals/users/edit",
+ ajaxParams: {
+ user_name: $(this).data('user_name')
+ },
+ msgTarget: $("#alerts-page")
+ });
+
+ attachUserForm();
+ });
+
+ // Manage user roles button
+ el.find('.js-user-roles').click(function() {
+ var userName = $(this).data('user_name');
+ $("body").ufModal({
+ sourceUrl: site.uri.public + "/modals/users/roles",
+ ajaxParams: {
+ user_name: userName
+ },
+ msgTarget: $("#alerts-page")
+ });
+
+ $("body").on('renderSuccess.ufModal', function (data) {
+ var modal = $(this).ufModal('getModal');
+ var form = modal.find('.js-form');
+
+ // Set up collection widget
+ var roleWidget = modal.find('.js-form-roles');
+ roleWidget.ufCollection({
+ dropdown : {
+ ajax: {
+ url : site.uri.public + '/api/roles'
+ },
+ placeholder : "Select a role"
+ },
+ dropdownTemplate: modal.find('#user-roles-select-option').html(),
+ rowTemplate : modal.find('#user-roles-row').html()
+ });
+
+ // Get current roles and add to widget
+ $.getJSON(site.uri.public + '/api/users/u/' + userName + '/roles')
+ .done(function (data) {
+ $.each(data.rows, function (idx, role) {
+ role.text = role.name;
+ roleWidget.ufCollection('addRow', role);
+ });
+ });
+
+ // Set up form for submission
+ form.ufForm({
+ }).on("submitSuccess.ufForm", function() {
+ // Reload page on success
+ window.location.reload();
+ });
+ });
+ });
+
+ // Change user password button
+ el.find('.js-user-password').click(function() {
+ var userName = $(this).data('user_name');
+ $("body").ufModal({
+ sourceUrl: site.uri.public + "/modals/users/password",
+ ajaxParams: {
+ user_name: userName
+ },
+ msgTarget: $("#alerts-page")
+ });
+
+ $("body").on('renderSuccess.ufModal', function (data) {
+ var modal = $(this).ufModal('getModal');
+ var form = modal.find('.js-form');
+
+ // Set up form for submission
+ form.ufForm({
+ validators: page.validators
+ }).on("submitSuccess.ufForm", function() {
+ // Reload page on success
+ window.location.reload();
+ });
+
+ toggleChangePasswordMode(modal, userName, 'link');
+
+ // On submission, submit either the PUT request, or POST for a password reset, depending on the toggle state
+ modal.find("input[name='change_password_mode']").click(function() {
+ var changePasswordMode = $(this).val();
+ toggleChangePasswordMode(modal, userName, changePasswordMode);
+ });
+ });
+ });
+
+ // Delete user button
+ el.find('.js-user-delete').click(function() {
+ $("body").ufModal({
+ sourceUrl: site.uri.public + "/modals/users/confirm-delete",
+ ajaxParams: {
+ user_name: $(this).data('user_name')
+ },
+ msgTarget: $("#alerts-page")
+ });
+
+ $("body").on('renderSuccess.ufModal', function (data) {
+ var modal = $(this).ufModal('getModal');
+ var form = modal.find('.js-form');
+
+ form.ufForm()
+ .on("submitSuccess.ufForm", function() {
+ // Reload page on success
+ window.location.reload();
+ });
+ });
+ });
+
+ /**
+ * Direct action buttons
+ */
+ el.find('.js-user-activate').click(function() {
+ var btn = $(this);
+ updateUser(btn.data('user_name'), 'flag_verified', '1');
+ });
+
+ el.find('.js-user-enable').click(function () {
+ var btn = $(this);
+ updateUser(btn.data('user_name'), 'flag_enabled', '1');
+ });
+
+ el.find('.js-user-disable').click(function () {
+ var btn = $(this);
+ updateUser(btn.data('user_name'), 'flag_enabled', '0');
+ });
+}
+
+function bindUserCreationButton(el) {
+ // Link create button
+ el.find('.js-user-create').click(function() {
+ $("body").ufModal({
+ sourceUrl: site.uri.public + "/modals/users/create",
+ msgTarget: $("#alerts-page")
+ });
+
+ attachUserForm();
+ });
+};
diff --git a/main/app/sprinkles/admin/composer.json b/main/app/sprinkles/admin/composer.json
new file mode 100755
index 0000000..8ccd5c0
--- /dev/null
+++ b/main/app/sprinkles/admin/composer.json
@@ -0,0 +1,22 @@
+{
+ "name": "userfrosting/sprinkle-admin",
+ "type": "userfrosting-sprinkle",
+ "description": "Administrative management module for UserFrosting.",
+ "keywords": ["php user management", "usercake", "bootstrap"],
+ "homepage": "https://github.com/userfrosting/UserFrosting",
+ "license" : "MIT",
+ "authors" : [
+ {
+ "name": "Alexander Weissman",
+ "homepage": "https://alexanderweissman.com"
+ }
+ ],
+ "require": {
+ "php": ">=5.6"
+ },
+ "autoload": {
+ "psr-4": {
+ "UserFrosting\\Sprinkle\\Admin\\": "src/"
+ }
+ }
+}
diff --git a/main/app/sprinkles/admin/locale/ar/messages.php b/main/app/sprinkles/admin/locale/ar/messages.php
new file mode 100755
index 0000000..d4a3a44
--- /dev/null
+++ b/main/app/sprinkles/admin/locale/ar/messages.php
@@ -0,0 +1,135 @@
+ [
+ 1 => "نشاط",
+ 2 => "أنشطة",
+
+ "LAST" => "النشاط الاخير",
+ "PAGE" => "قائمة من أنشطة المستخدم",
+ "TIME" => "وقت نشاط"
+ ],
+
+ "CACHE" => [
+ "CLEAR" => "مسح ذاكرة التخزين",
+ "CLEAR_CONFIRM" => "هل أنت متأكد أنك تريد مسح ذاكرة التخزين بالموقع؟",
+ "CLEAR_CONFIRM_YES" => "نعم، إمسح ذاكرة التخزين",
+ "CLEARED" => "تم مسح ذاكرة التخزين بنجاح"
+ ],
+
+ "DASHBOARD" => "لوحة القيادة",
+ "NO_FEATURES_YET" => "لا يبدو أن أي ميزات تم إعدادها لهذا الحساب حتى الآن. ربما لم يتم تنفيذها بعد، أو ربما شخص نسي أن يعطيك الوصول. في كلتا الحالتين، نحن سعداء أن يكون لك على متن!",
+ "DELETE_MASTER" => "لا يمكنك حذف الحساب الرئيسي",
+ "DELETION_SUCCESSFUL" => "المستعمل
{{user_name}} حذف بنجاح",
+ "DETAILS_UPDATED" => "جدد تفاصيل الحساب للمستخدم
{{user_name}} ",
+ "DISABLE_MASTER" => "لا يمكنك تعطيل الحساب الرئيسي",
+ "DISABLE_SUCCESSFUL" => "حساب المستخدم
{{user_name}} عطيل بنجاح",
+
+ "ENABLE_SUCCESSFUL" => "حساب المستخدم
{{user_name}} مكين بنجاح",
+
+ "GROUP" => [
+ 1 => "مجموعة",
+ 2 => "مجموعات",
+
+ "CREATE" => "إنشاء مجموعة",
+ "DELETE" => "حذف مجموعة",
+ "DELETE_CONFIRM" => "هل أنت متأكد أنك تريد حذف مجموعة
{{name}} ?",
+ "DELETE_YES" => "نعم، إحذف مجموعة",
+ "EDIT" => "تعديل مجموعة",
+ "ICON" => "رمز المجموعة",
+ "ICON_EXPLAIN" => "رمز المستخدمين في المجموعه",
+ "INFO_PAGE" => "صفحة معلومات المجموعة ل {{name}}",
+ //"MANAGE" => "Manage group",
+ "NAME" => "أسم المجموعة",
+ "NAME_EXPLAIN" => "ادخال اسم للمجموعة",
+ "PAGE_DESCRIPTION" => "قائمة المجموعات لموقعك يوفر أدوات لإدارة التحرير وحذف مجموعات"
+ ],
+
+ "MANUALLY_ACTIVATED" => "تم تفعيل حساب{{user_name}}",
+ "MASTER_ACCOUNT_EXISTS" => "الحساب الرئيسي موجود بالفعل",
+ "MIGRATION" => [
+ "REQUIRED" => "تحديث قاعدة البيانات مطلوب"
+ ],
+
+ "PERMISSION" => [
+ 1 => "الإذن",
+ 2 => "مأذونيات",
+
+ "ASSIGN_NEW" => "تعيين إذن جديد",
+ "HOOK_CONDITION" => "الظروف",
+ "MANAGE" => "إدارة المأذونات",
+ "PAGE_DESCRIPTION" => "قائمة المأذونات لموقعك",
+ "UPDATE" => "تحديث المأذونات"
+ ],
+
+ "ROLE" => [
+ 1 => "وظيفة",
+ 2 => "وظائف",
+
+ "ASSIGN_NEW" => "تعيين دور جديد",
+ "CREATE" => "إنشاء دور",
+ "DELETE" => "حذف دور",
+ "DELETE_CONFIRM" => "هل أنت متأكد أنك تريد حذف الدور
{{name}} ?",
+ "DELETE_YES" => "نعم، حذف دور",
+ "EDIT" => "إدارة دور",
+ "INFO_PAGE" => "صفحة معلومات دور {{name}}",
+ "MANAGE" => "إدارة الوظائف",
+ "NAME" => "اسم",
+ "NAME_EXPLAIN" => "أدخل اسما للدور",
+ "PAGE_DESCRIPTION" => "قائمة الوظائف لموقعك",
+ "UPDATED" => "تحديث الوظائف"
+ ],
+
+ "SYSTEM_INFO" => [
+ "@TRANSLATION" => "معلومات الجهاز",
+
+ "DB_NAME" => "اسم قاعدة البيانات",
+ "DB_VERSION" => "إصدار قاعدة البيانات",
+ "DIRECTORY" => "دليل المشروع",
+ "PHP_VERSION" => "الإصدار PHP",
+ "SERVER" => "برنامج الخادم",
+ "SPRINKLES" => "sprinkles المحمل",
+ "UF_VERSION" => "إصدار UserFrosting",
+ "URL" => "رابط قاعدة الموقع"
+ ],
+
+ "USER" => [
+ 1 => "مستخدم",
+ 2 => "المستخدمين",
+
+ "ADMIN" => [
+ "CHANGE_PASSWORD" => "تغيير كلمة المرور للمستخدم",
+ "SEND_PASSWORD_LINK" => "إرسال المستخدم وصلة من شأنها أن تسمح لهم لاختيار كلمة المرور الخاصة بهم",
+ "SET_PASSWORD" => "تعيين كلمة المرور الخاصة بالمستخدم"
+ ],
+
+ "ACTIVATE" => "تفعيل المستخدم",
+ "CREATE" => "إنشاء مستخدم",
+ "DELETE" => "مسح المستخدم",
+ "DELETE_CONFIRM" => "هل أنت متأكد أنك تريد حذف المستخدم
{{name}} ?",
+ "DELETE_YES" => "نعم، حذف المستخدم",
+ "DISABLE" => "تعطيل المستخدم ",
+ "EDIT" => "إدارة المستخدم",
+ "ENABLE" => "تمكين المستخدم",
+ "INFO_PAGE" => "صفحة معلومات المستخدم {{name}}",
+ "PAGE_DESCRIPTION" => "قائمة المستخدمين لموقعك",
+ "LATEST" => "أحدث المستخدمين",
+ "VIEW_ALL" => "عرض جميع المستخدمين"
+ ],
+ "X_USER" => [
+ 0 => "لا يوجد اي مستخدمين",
+ 1 => "{{plural}} مستخدم",
+ 2 => "{{plural}} المستخدمين"
+ ]
+];
diff --git a/main/app/sprinkles/admin/locale/de_DE/messages.php b/main/app/sprinkles/admin/locale/de_DE/messages.php
new file mode 100755
index 0000000..6e21ab5
--- /dev/null
+++ b/main/app/sprinkles/admin/locale/de_DE/messages.php
@@ -0,0 +1,161 @@
+ [
+ 1 => "Aktivität",
+ 2 => "Aktivitäten",
+
+ "LAST" => "Letzte Aktivität",
+ "PAGE" => "Eine Auflistung der Benutzeraktivitäten",
+ "TIME" => "Aktivitätszeit"
+ ],
+
+ "CACHE" => [
+ "CLEAR" => "Cache löschen",
+ "CLEAR_CONFIRM" => "Sind Sie sicher, dass Sie den Seiten-Cache löschen möchten?",
+ "CLEAR_CONFIRM_YES" => "Ja, Cache löschen",
+ "CLEARED" => "Cache wurde erfolgreich gelöscht!"
+ ],
+
+ "DASHBOARD" => "Übersicht",
+ "NO_FEATURES_YET" => "Es sieht aus, als wären für Ihren Account noch keine Funktionen aktiviert... bisher. Entweder sie wurden bisher noch nicht implementiert, oder Ihnen fehlen noch die Berechtigungen. Trotzdem ist es schön, dass Sie auf unsere Seite gekommen sind!",
+ "DELETE_MASTER" => "Sie können das Root-Konto nicht löschen!",
+ "DELETION_SUCCESSFUL" => "Benutzer
{{user_name}} wurde erfolgreich gelöscht.",
+ "DETAILS_UPDATED" => "Konto-Daten für
{{user_name}} aktualisiert.",
+ "DISABLE_MASTER" => "Sie können das Root-Konto nicht deaktivieren!",
+ "DISABLE_SELF" => "Sie können Ihr eigenes Konto nicht deaktivieren!",
+ "DISABLE_SUCCESSFUL" => "Konto von
{{user_name}} wurde erfolgreich deaktiviert.",
+
+ "ENABLE_SUCCESSFUL" => "Konto von {{user_name}} wurde erfolgreich aktiviert.",
+
+ "GROUP" => [
+ 1 => "Gruppe",
+ 2 => "Gruppen",
+
+ "CREATE" => "Gruppe erstellen",
+ "CREATION_SUCCESSFUL" => "Die Gruppe
{{name}} wurde erfolgreich erstellt",
+ "DELETE" => "Gruppe löschen",
+ "DELETE_CONFIRM" => "Möchten Sie die Gruppe
{{name}} wirklich löschen?",
+ "DELETE_DEFAULT" => "Sie können die Gruppe
{{name}} nicht löschen, da es die Standardgruppe für neu registrierte Benutzer ist.",
+ "DELETE_YES" => "Ja, Gruppe löschen",
+ "DELETION_SUCCESSFUL" => "Die Gruppe
{{name}} wurde erfolgreich gelöscht",
+ "EDIT" => "Gruppe bearbeiten",
+ "ICON" => "Gruppensymbol",
+ "ICON_EXPLAIN" => "Symbol für Gruppenmitglieder",
+ "INFO_PAGE" => "Gruppeninformationsseite für {{name}}",
+ "MANAGE" => "Gruppe verwalten",
+ "NAME" => "Gruppenname",
+ "NAME_EXPLAIN" => "Geben Sie einen Namen für die Gruppe ein",
+ "NOT_EMPTY" => "Sie können das nicht tun, denn es sind noch Benutzer mit der Gruppe
{{name}} verbunden.",
+ "PAGE_DESCRIPTION" => "Eine Liste der Gruppen für Ihre Website. Bietet Verwaltungstools für das Bearbeiten und Löschen von Gruppen.",
+ "SUMMARY" => "Gruppen Zusammenfassung",
+ "UPDATE" => "Details für die Gruppe
{{name}} aktualisiert"
+ ],
+
+ "MANUALLY_ACTIVATED" => "{{user_name}}'s Konto wurde manuell aktiviert.",
+ "MASTER_ACCOUNT_EXISTS" => "Das Root-Konto existiert bereits!",
+ "MIGRATION" => [
+ "REQUIRED" => "Datenbankaktualisierung erforderlich"
+ ],
+
+ "PERMISSION" => [
+ 1 => "Berechtigung",
+ 2 => "Berechtigungen",
+
+ "ASSIGN_NEW" => "Neue Berechtigung zuweisen",
+ "HOOK_CONDITION" => "Haken/Bedingungen",
+ "ID" => "Berechtigungs-ID",
+ "INFO_PAGE" => "Berechtigungs Informationen für '{{name}}'",
+ "MANAGE" => "Berechtigungen verwalten",
+ "NOTE_READ_ONLY" => "
Bitte beachten Sie: Berechtigungen werden als \"Teil des Quelltexts\" gesehen und können hier nicht bearbeitet werden. Um Berechtigungen hinzuzufügen, zu bearbeiten, oder zu löschen, benutzen Sie bitte folgende Dokumentation zur
Datenbank Migration. ",
+ "PAGE_DESCRIPTION" => "Eine Liste der Berechtigungen für Ihre Website. Bietet Verwaltungstools zum Bearbeiten und Löschen von Berechtigungen.",
+ "SUMMARY" => "Berechtigungs Zusammenfassung",
+ "UPDATE" => "Berechtigungen aktualisieren",
+ "VIA_ROLES" => "Besitzt die Berechtigung durch die Rolle"
+ ],
+
+ "ROLE" => [
+ 1 => "Rolle",
+ 2 => "Rollen",
+
+ "ASSIGN_NEW" => "Neue Rolle zuweisen",
+ "CREATE" => "Rolle erstellen",
+ "CREATION_SUCCESSFUL" => "Die Rolle
{{name}} wurde erfolgreich erstellt",
+ "DELETE" => "Rolle löschen",
+ "DELETE_CONFIRM" => "Sind Sie sicher, dass Sie die Rolle
{{name}} löschen möchten?",
+ "DELETE_DEFAULT" => "Sie können die Rolle
{{name}} nicht löschen, da es eine Standardrolle für neu registrierte Benutzer ist.",
+ "DELETE_YES" => "Ja, Rolle löschen",
+ "DELETION_SUCCESSFUL" => "Die Rolle
{{name}} wurde erfolgreich gelöscht",
+ "EDIT" => "Rolle bearbeiten",
+ "HAS_USERS" => "Sie können das nicht machen weil es noch Benutzer gibt, die die Rolle
{{name}} haben.",
+ "INFO_PAGE" => "Rolleninformationsseite für {{name}}",
+ "MANAGE" => "Rollen verwalten",
+ "NAME" => "Name",
+ "NAME_EXPLAIN" => "Geben Sie einen Namen für die Rolle ein",
+ "NAME_IN_USE" => "Eine Rolle mit dem Namen
{{name}} existiert bereits",
+ "PAGE_DESCRIPTION" => "Eine Liste der Rollen für Ihre Website. Bietet Verwaltungstools zum Bearbeiten und Löschen von Rollen.",
+ "PERMISSIONS_UPDATED" => "Berechtigungen für die Rolle
{{name}} aktualisiert",
+ "SUMMARY" => "Rollen Zusammenfassung",
+ "UPDATED" => "Rollen aktualisieren"
+ ],
+
+ "SYSTEM_INFO" => [
+ "@TRANSLATION" => "System Information",
+
+ "DB_NAME" => "Name der Datenbank",
+ "DB_VERSION" => "Datenbankversion",
+ "DIRECTORY" => "Projektverzeichnis",
+ "PHP_VERSION" => "PHP-Version",
+ "SERVER" => "Web-Server-Software",
+ "SPRINKLES" => "Geladene Sprinkles",
+ "UF_VERSION" => "UserFrosting Version",
+ "URL" => "Website-Stamm-Url"
+ ],
+
+ "TOGGLE_COLUMNS" => "Spalten anpassen",
+
+ "USER" => [
+ 1 => "Benutzer",
+ 2 => "Benutzer",
+
+ "ADMIN" => [
+ "CHANGE_PASSWORD" => "Benutzerpasswort ändern",
+ "SEND_PASSWORD_LINK" => "Senden Sie dem Benutzer einen Link, der ihnen erlaubt, ihr eigenes Passwort zu wählen",
+ "SET_PASSWORD" => "Setzen Sie das Passwort des Benutzers als"
+ ],
+
+ "ACTIVATE" => "Benutzer aktivieren",
+ "CREATE" => "Benutzer erstellen",
+ "CREATED" => "Benutzer
{{user_name}} wurde erfolgreich erstellt",
+ "DELETE" => "Benutzer löschen",
+ "DELETE_CONFIRM" => "Sind Sie sicher, dass Sie den Benutzer
{{name}} löschen möchten?",
+ "DELETE_YES" => "Ja, Benutzer löschen",
+ "DISABLE" => "Benutzer deaktivieren",
+ "EDIT" => "Benutzer bearbeiten",
+ "ENABLE" => "Benutzer aktivieren",
+ "INFO_PAGE" => "Benutzerinformationsseite für {{name}}",
+ "LATEST" => "Neueste Benutzer",
+ "PAGE_DESCRIPTION" => "Eine Liste der Benutzer für Ihre Website. Bietet Management-Tools, einschließlich der Möglichkeit, Benutzerdaten bearbeiten, manuell aktivieren, Benutzer aktivieren/deaktivieren, und vieles mehr.",
+ "SUMMARY" => "Benutzer Zusammenfassung",
+ "VIEW_ALL" => "Alle Benutzer anzeigen",
+ "WITH_PERMISSION" => "Benutzer mit dieser Berechtigung"
+ ],
+ "X_USER" => [
+ 0 => "Keine Benutzer",
+ 1 => "{{plural}} Benutzer",
+ 2 => "{{plural}} Benutzer"
+ ]
+];
diff --git a/main/app/sprinkles/admin/locale/en_US/messages.php b/main/app/sprinkles/admin/locale/en_US/messages.php
new file mode 100755
index 0000000..a21e325
--- /dev/null
+++ b/main/app/sprinkles/admin/locale/en_US/messages.php
@@ -0,0 +1,160 @@
+ [
+ 1 => "Activity",
+ 2 => "Activities",
+
+ "LAST" => "Last Activity",
+ "PAGE" => "A listing of user activities",
+ "TIME" => "Activity Time"
+ ],
+
+ "CACHE" => [
+ "CLEAR" => "Clear cache",
+ "CLEAR_CONFIRM" => "Are you sure you want to clear the site cache?",
+ "CLEAR_CONFIRM_YES" => "Yes, clear cache",
+ "CLEARED" => "Cache cleared successfully !"
+ ],
+
+ "DASHBOARD" => "Dashboard",
+ "NO_FEATURES_YET" => "It doesn't look like any features have been set up for this account...yet. Maybe they haven't been implemented yet, or maybe someone forgot to give you access. Either way, we're glad to have you aboard!",
+ "DELETE_MASTER" => "You cannot delete the master account!",
+ "DELETION_SUCCESSFUL" => "User
{{user_name}} has been successfully deleted.",
+ "DETAILS_UPDATED" => "Account details updated for user
{{user_name}} ",
+ "DISABLE_MASTER" => "You cannot disable the master account!",
+ "DISABLE_SELF" => "You cannot disable your own account!",
+ "DISABLE_SUCCESSFUL" => "Account for user
{{user_name}} has been successfully disabled.",
+
+ "ENABLE_SUCCESSFUL" => "Account for user
{{user_name}} has been successfully enabled.",
+
+ "GROUP" => [
+ 1 => "Group",
+ 2 => "Groups",
+
+ "CREATE" => "Create group",
+ "CREATION_SUCCESSFUL" => "Successfully created group
{{name}} ",
+ "DELETE" => "Delete group",
+ "DELETE_CONFIRM" => "Are you sure you want to delete the group
{{name}} ?",
+ "DELETE_DEFAULT" => "You can't delete the group
{{name}} because it is the default group for newly registered users.",
+ "DELETE_YES" => "Yes, delete group",
+ "DELETION_SUCCESSFUL" => "Successfully deleted group
{{name}} ",
+ "EDIT" => "Edit group",
+ "ICON" => "Group icon",
+ "ICON_EXPLAIN" => "Icon for group members",
+ "INFO_PAGE" => "Group information page for {{name}}",
+ "MANAGE" => "Manage group",
+ "NAME" => "Group name",
+ "NAME_EXPLAIN" => "Please enter a name for the group",
+ "NOT_EMPTY" => "You can't do that because there are still users associated with the group
{{name}} .",
+ "PAGE_DESCRIPTION" => "A listing of the groups for your site. Provides management tools for editing and deleting groups.",
+ "SUMMARY" => "Group Summary",
+ "UPDATE" => "Details updated for group
{{name}} "
+ ],
+
+ "MANUALLY_ACTIVATED" => "{{user_name}}'s account has been manually activated",
+ "MASTER_ACCOUNT_EXISTS" => "The master account already exists!",
+ "MIGRATION" => [
+ "REQUIRED" => "Database update required"
+ ],
+
+ "PERMISSION" => [
+ 1 => "Permission",
+ 2 => "Permissions",
+
+ "ASSIGN_NEW" => "Assign new permission",
+ "HOOK_CONDITION" => "Hook/Conditions",
+ "ID" => "Permission ID",
+ "INFO_PAGE" => "Permission information page for '{{name}}'",
+ "MANAGE" => "Manage permissions",
+ "NOTE_READ_ONLY" => "
Please note: permissions are considered \"part of the code\" and cannot be modified through the interface. To add, remove, or modify permissions, the site maintainers will need to use a
database migration. ",
+ "PAGE_DESCRIPTION" => "A listing of the permissions for your site. Provides management tools for editing and deleting permissions.",
+ "SUMMARY" => "Permission Summary",
+ "UPDATE" => "Update permissions",
+ "VIA_ROLES" => "Has permission via roles"
+ ],
+
+ "ROLE" => [
+ 1 => "Role",
+ 2 => "Roles",
+
+ "ASSIGN_NEW" => "Assign new role",
+ "CREATE" => "Create role",
+ "CREATION_SUCCESSFUL" => "Successfully created role
{{name}} ",
+ "DELETE" => "Delete role",
+ "DELETE_CONFIRM" => "Are you sure you want to delete the role
{{name}} ?",
+ "DELETE_DEFAULT" => "You can't delete the role
{{name}} because it is a default role for newly registered users.",
+ "DELETE_YES" => "Yes, delete role",
+ "DELETION_SUCCESSFUL" => "Successfully deleted role
{{name}} ",
+ "EDIT" => "Edit role",
+ "HAS_USERS" => "You can't do that because there are still users who have the role
{{name}} .",
+ "INFO_PAGE" => "Role information page for {{name}}",
+ "MANAGE" => "Manage Roles",
+ "NAME" => "Name",
+ "NAME_EXPLAIN" => "Please enter a name for the role",
+ "NAME_IN_USE" => "A role named
{{name}} already exist",
+ "PAGE_DESCRIPTION" => "A listing of the roles for your site. Provides management tools for editing and deleting roles.",
+ "PERMISSIONS_UPDATED" => "Permissions updated for role
{{name}} ",
+ "SUMMARY" => "Role Summary",
+ "UPDATED" => "Details updated for role
{{name}} "
+ ],
+
+ "SYSTEM_INFO" => [
+ "@TRANSLATION" => "System information",
+
+ "DB_NAME" => "Database name",
+ "DB_VERSION" => "Database version",
+ "DIRECTORY" => "Project directory",
+ "PHP_VERSION" => "PHP version",
+ "SERVER" => "Webserver software",
+ "SPRINKLES" => "Loaded sprinkles",
+ "UF_VERSION" => "UserFrosting version",
+ "URL" => "Site root url"
+ ],
+
+ "TOGGLE_COLUMNS" => "Toggle columns",
+
+ "USER" => [
+ 1 => "User",
+ 2 => "Users",
+
+ "ADMIN" => [
+ "CHANGE_PASSWORD" => "Change User Password",
+ "SEND_PASSWORD_LINK" => "Send the user a link that will allow them to choose their own password",
+ "SET_PASSWORD" => "Set the user's password as"
+ ],
+
+ "ACTIVATE" => "Activate user",
+ "CREATE" => "Create user",
+ "CREATED" => "User
{{user_name}} has been successfully created",
+ "DELETE" => "Delete user",
+ "DELETE_CONFIRM" => "Are you sure you want to delete the user
{{name}} ?",
+ "DELETE_YES" => "Yes, delete user",
+ "DELETED" => "User deleted",
+ "DISABLE" => "Disable user",
+ "EDIT" => "Edit user",
+ "ENABLE" => "Enable user",
+ "INFO_PAGE" => "User information page for {{name}}",
+ "LATEST" => "Latest Users",
+ "PAGE_DESCRIPTION" => "A listing of the users for your site. Provides management tools including the ability to edit user details, manually activate users, enable/disable users, and more.",
+ "SUMMARY" => "Account Summary",
+ "VIEW_ALL" => "View all users",
+ "WITH_PERMISSION" => "Users with this permission"
+ ],
+ "X_USER" => [
+ 0 => "No users",
+ 1 => "{{plural}} user",
+ 2 => "{{plural}} users"
+ ]
+];
diff --git a/main/app/sprinkles/admin/locale/es_ES/messages.php b/main/app/sprinkles/admin/locale/es_ES/messages.php
new file mode 100755
index 0000000..a8950c0
--- /dev/null
+++ b/main/app/sprinkles/admin/locale/es_ES/messages.php
@@ -0,0 +1,164 @@
+ [
+ 1 => "Actividad",
+ 2 => "Actividades",
+
+ "LAST" => "Última actividad",
+ "PAGE" => "Una lista de las actividades del usuario",
+ "TIME" => "Tiempo de Actividad"
+ ],
+
+ "ADMIN" => [
+ "PANEL" => "Panel de administración"
+ ],
+
+ "CACHE" => [
+ "CLEAR" => "Limpiar cache",
+ "CLEAR_CONFIRM" => "¿Está seguro de que desea borrar la caché del sitio?",
+ "CLEAR_CONFIRM_YES" => "Sí, borrar caché",
+ "CLEARED" => "¡Cache borrado correctamente!"
+ ],
+
+ "DASHBOARD" => "Tablero",
+ "NO_FEATURES_YET" => "No parece que se hayan configurado funciones para esta cuenta ... todavía. Tal vez no se han implementado todavía, o tal vez alguien se olvidó de darle acceso. De cualquier manera, ¡estamos encantados de tenerte a bordo!",
+ "DELETE_MASTER" => "¡No puede eliminar la cuenta principal!",
+ "DELETION_SUCCESSFUL" => "El usuario
{{user_name}} se ha eliminado correctamente.",
+ "DETAILS_UPDATED" => "Detalles de la cuenta actualizados para el usuario
{{user_name}} ",
+ "DISABLE_MASTER" => "¡No puedes deshabilitar la cuenta principal!",
+ "DISABLE_SELF" => "¡No puedes inhabilitar tu propia cuenta!",
+ "DISABLE_SUCCESSFUL" => "La cuenta para el usuario
{{user_name}} se ha desactivado correctamente.",
+
+ "ENABLE_SUCCESSFUL" => "La cuenta para el usuario
{{user_name}} se ha habilitado correctamente.",
+
+ "GROUP" => [
+ 1 => "Grupo",
+ 2 => "Grupos",
+
+ "CREATE" => "Crea un grupo",
+ "CREATION_SUCCESSFUL" => "Grupo creado correctamente
{{name}} ",
+ "DELETE" => "Delete group",
+ "DELETE_CONFIRM" => "¿Seguro que quieres eliminar el grupo
{{name}} ?",
+ "DELETE_DEFAULT" => "No puedes eliminar el grupo
{{name}} porque es el grupo predeterminado para los usuarios recién registrados.",
+ "DELETE_YES" => "Sí, eliminar grupo",
+ "DELETION_SUCCESSFUL" => "Grupo eliminado correctamente
{{name}} ",
+ "EDIT" => "Editar grupo",
+ "ICON" => "Icono de grupo",
+ "ICON_EXPLAIN" => "Icono para los miembros del grupo",
+ "INFO_PAGE" => "Página de información de grupo para {{name}}",
+ "MANAGE" => "Administrar grupo",
+ "NAME" => "Nombre del grupo",
+ "NAME_EXPLAIN" => "Introduzca un nombre para el grupo",
+ "NOT_EMPTY" => "No puedes hacerlo porque todavía hay usuarios asociados con el grupo
{{name}} .",
+ "PAGE_DESCRIPTION" => "Un listado de los grupos para su sitio. Proporciona herramientas de administración para editar y eliminar grupos.",
+ "SUMMARY" => "Resumen del grupo",
+ "UPDATE" => "Detalles actualizados para el grupo
{{name}} "
+ ],
+
+ "MANUALLY_ACTIVATED" => "La cuenta de {{user_name}} se ha activado manualmente",
+ "MASTER_ACCOUNT_EXISTS" => "¡La cuenta maestra ya existe!",
+ "MIGRATION" => [
+ "REQUIRED" => "Se requiere actualizar la base de datos"
+ ],
+
+ "PERMISSION" => [
+ 1 => "Permiso",
+ 2 => "Permisos",
+
+ "ASSIGN_NEW" => "Asignar nuevo permiso",
+ "HOOK_CONDITION" => "Hook/Condiciones",
+ "ID" => "ID de permiso",
+ "INFO_PAGE" => "Página de autor del permiso de '{{name}}'",
+ "MANAGE" => "Administrar permisos",
+ "NOTE_READ_ONLY" => "
Tenga en cuenta: los permisos se consideran \"parte del código\" y no se pueden modificar a través de la interfaz. Para agregar, eliminar o modificar permisos, los mantenedores del sitio necesitarán usar una
migración de la base de datos . ",
+ "PAGE_DESCRIPTION" => "Una lista de los permisos para su sitio. Proporciona herramientas de administración para editar y eliminar permisos.",
+ "SUMMARY" => "Resumen del permiso",
+ "UPDATE" => "Actualizar permisos",
+ "VIA_ROLES" => "Tiene permiso para los roles"
+ ],
+
+ "ROLE" => [
+ 1 => "Rol(funcion)",
+ 2 => "Roles(funciones)",
+
+ "ASSIGN_NEW" => "Asignar nueva rol",
+ "CREATE" => "Crear un rol",
+ "CREATION_SUCCESSFUL" => "Función creada correctamente
{{name}} ",
+ "DELETE" => "Eliminar rol",
+ "DELETE_CONFIRM" => "¿Seguro que quieres eliminar la función
{{name}} ?",
+ "DELETE_DEFAULT" => "No puedes eliminar el rol
{{name}} porque es un rol predeterminado para los usuarios recién registrados.",
+ "DELETE_YES" => "Sí, borrar función",
+ "DELETION_SUCCESSFUL" => "Se ha eliminado la función
{{nombre}} ",
+ "EDIT" => "Editar función",
+ "HAS_USERS" => "No puedes hacerlo porque todavía hay usuarios que tienen el rol
{{name}} .",
+ "INFO_PAGE" => "Página de información de funciones de {{name}}",
+ "MANAGE" => "Administrar roles",
+ "NAME" => "Nombre",
+ "NAME_EXPLAIN" => "Ingrese un nombre para el rol",
+ "NAME_IN_USE" => "Ya existe un rol denominado
{{name}} ",
+ "PAGE_DESCRIPTION" => "Una lista de las funciones de su sitio. Proporciona herramientas de administración para editar y eliminar roles.",
+ "PERMISSIONS_UPDATED" => "Permisos actualizados para el rol
{{name}} ",
+ "SUMMARY" => "Resumen del rol",
+ "UPDATED" => "Detalles actualizados para el rol
{{name}} "
+ ],
+
+ "SYSTEM_INFO" => [
+ "@TRANSLATION" => "Información del sistema",
+
+ "DB_NAME" => "Nombre de la base de datos",
+ "DB_VERSION" => "Versión de base de datos",
+ "DIRECTORY" => "Directorio del proyecto",
+ "PHP_VERSION" => "Versión de PHP",
+ "SERVER" => "Software de servidor Web",
+ "SPRINKLES" => "Sprinkles cargados",
+ "UF_VERSION" => "UserFrosting versión",
+ "URL" => "URL root del sitio"
+ ],
+
+ "TOGGLE_COLUMNS" => "Alternar columnas",
+ "NO_DATA" => "No puede quedar vacio.",
+
+ "USER" => [
+ 1 => "Usuario",
+ 2 => "Usuarios",
+
+ "ADMIN" => [
+ "CHANGE_PASSWORD" => "Cambiar contraseña de usuario",
+ "SEND_PASSWORD_LINK" => "Enviar al usuario un enlace que les permita elegir su propia contraseña",
+ "SET_PASSWORD" => "Establezca la contraseña del usuario como"
+ ],
+
+ "ACTIVATE" => "Activar usuario",
+ "CREATE" => "Crear usuario",
+ "CREATED" => "Se ha creado correctamente el usuario
{{user_name}} ",
+ "DELETE" => "Borrar usuario",
+ "DELETE_CONFIRM" => "¿Seguro que desea eliminar el usuario
{{name}} ?",
+ "DELETE_YES" => "Sí, eliminar usuario",
+ "DISABLE" => "Deshabilitar usuario",
+ "EDIT" => "Editar usuario",
+ "ENABLE" => "Habilitar usuario",
+ "INFO_PAGE" => "Página de información de usuario de {{name}}",
+ "LATEST" => "Usuarios más recientes",
+ "PAGE_DESCRIPTION" => "Una lista de los usuarios para su sitio. Proporciona herramientas de administración que incluyen la capacidad de editar detalles de usuario, activar manualmente usuarios, habilitar / deshabilitar usuarios y más.",
+ "SUMMARY" => "Resumen de la cuenta",
+ "VIEW_ALL" => "Ver todos los usuarios",
+ "WITH_PERMISSION" => "Usuarios con este permiso"
+ ],
+ "X_USER" => [
+ 0 => "No hay usuarios",
+ 1 => "{{plural}} usuario",
+ 2 => "{{plural}} usuarios"
+ ]
+];
diff --git a/main/app/sprinkles/admin/locale/fa/messages.php b/main/app/sprinkles/admin/locale/fa/messages.php
new file mode 100755
index 0000000..75a8dee
--- /dev/null
+++ b/main/app/sprinkles/admin/locale/fa/messages.php
@@ -0,0 +1,158 @@
+ [
+ 1 => "فعالیت",
+ 2 => "فعالیت ها",
+
+ "LAST" => "آخرین فعالیت",
+ "PAGE" => "لیستی از فعالیت های کاربر",
+ "TIME" => "زمان فعالیت"
+ ],
+
+ "CACHE" => [
+ "CLEAR" => "پاک سازی کش",
+ "CLEAR_CONFIRM" => "آیا مطمئن هستید که میخواهید کش سایت را پاک سازی کنید؟",
+ "CLEAR_CONFIRM_YES" => "بله، کش پاک سازی شود",
+ "CLEARED" => "کش با موفقیت پاک سازی شد"
+ ],
+
+ "DASHBOARD" => "کارتابل",
+ "DELETE_MASTER" => "شما نمیتوانید کاربر اصلی را حذف کنید",
+ "DELETION_SUCCESSFUL" => "
{{user_name}} با موفقیت حذف شد.",
+ "DETAILS_UPDATED" => "جزئیات
{{user_name}} با موفقیت ذخیره شد.",
+ "DISABLE_MASTER" => "شما نمیتوانید کاربر اصلی را غیر فعال کنید.",
+ "DISABLE_SUCCESSFUL" => "حساب کاربری
{{user_name}} با موفقیت غیر فعال شد.",
+
+ "ENABLE_SUCCESSFUL" => "حساب کاربری
{{user_name}} با موفقیت فعال شد.",
+
+ "GROUP" => [
+ 1 => "گروه",
+ 2 => "گروه ها",
+
+ "CREATE" => "اضافه کردن گروه",
+ "CREATION_SUCCESSFUL" => "گروه
{{name}} با موفقیت اضافه شد",
+ "DELETE" => "حذف گروه",
+ "DELETE_CONFIRM" => "آیا مطمئن هستید که میخواهید گروه
{{name}} را حذف کنید؟",
+ "DELETE_DEFAULT" => "شما نمیتوانید گروه
{{name}} را حذف کنید چون به عنوان گروه پیش فرض برای کاربران جدید انتخاب شده است.",
+ "DELETE_YES" => "بله، گروه حذف شود",
+ "DELETION_SUCCESSFUL" => "گروه
{{name}} با موفقیت حذف شد.",
+ "EDIT" => "ویرایش گروه",
+ "ICON" => "آیکن گروه",
+ "ICON_EXPLAIN" => "آیکن برای اعضای گروه",
+ "INFO_PAGE" => "صفحه توضیحات گروه برای {{name}}",
+ "MANAGE" => "مدیریت گروه",
+ "NAME" => "نام گروه",
+ "NAME_EXPLAIN" => "لطفا نام گروه را وارد کنید",
+ "NOT_EMPTY" => "نمیتوان این کار را کرد چون هنوز کاربرانی عضو گروه
{{name}} هستند.",
+ "PAGE_DESCRIPTION" => "لیست گروه های وب سایت شما. امکان مدیریت این گروه ها در این صفحه وجود دارد.",
+ "SUMMARY" => "توضیحات گروه",
+ "UPDATE" => "اطلاعات گروه
{{name}} به روز رسانی شد."
+ ],
+
+ "MANUALLY_ACTIVATED" => "حساب کاربری {{user_name}} بصورت دستی فعال شد.",
+ "MASTER_ACCOUNT_EXISTS" => "حساب کاربری اصلی وجود دارد!",
+ "MIGRATION" => [
+ "REQUIRED" => "به روز رسانی پایگاه داده ها باید انجام شود"
+ ],
+
+ "PERMISSION" => [
+ 1 => "دسترسی",
+ 2 => "دسترسی ها",
+
+ "ASSIGN_NEW" => "دادن دسترسی",
+ "HOOK_CONDITION" => "قلاب/شرط",
+ "ID" => "آی دی دسترسی",
+ "INFO_PAGE" => "توضیحات دسترسی {{name}}",
+ "MANAGE" => "مدیریت دسترسی ها",
+ "NOTE_READ_ONLY" => "
توجه بفرمایید دسترسی ها بخشی از کد میباشند و آن ها را نمیتوان از اینترفیس تغییر داد. برای این تغییرات، مبایستی که مدیر، از
دیتابیس مایگریشن استفاده کند. ",
+ "PAGE_DESCRIPTION" => "لیست دسترسی های وب سایت شما. امکان مدیریت این دسترسی ها در این صفحه وجود دارد.",
+ "SUMMARY" => "توضیحات دسترسی ها",
+ "UPDATE" => "به روز رسانی دسترسی ها",
+ "VIA_ROLES" => "از طریق وظیفه ها دسترسی دارد"
+ ],
+
+ "ROLE" => [
+ 1 => "وظیفه",
+ 2 => "وظیفه ها",
+
+ "ASSIGN_NEW" => "دادن وظیفه",
+ "CREATE" => "ساخت وظیفه",
+ "CREATION_SUCCESSFUL" => "وظیفه
{{name}} با موفقیت ساخته شد",
+ "DELETE" => "حذف وظیفه",
+ "DELETE_CONFIRM" => "اطمینان دارید که میخواهید وظیفه
{{name}} را حذف کنید؟",
+ "DELETE_DEFAULT" => "شما نمیتوانید وظیفه
{{name}} را حذف کنید زیرا کاربرانی که تازه ثبت نام کنند، این وظیفه را دریافت خواهند کرد.",
+ "DELETE_YES" => "بله، وظیفه حذف شود",
+ "DELETION_SUCCESSFUL" => "وظیفه
{{name}} با موفقیت حذف شد",
+ "EDIT" => "ویرایش وظیفه",
+ "HAS_USERS" => "نمیتوانید این کار را انجام دهید زیرا کاربرانی وظیفه
{{name}} را هنوز دارند.",
+ "INFO_PAGE" => "صفحه توضیحات وظیفه {{name}}",
+ "MANAGE" => "مدیریت وظیفه ها",
+ "NAME" => "نام",
+ "NAME_EXPLAIN" => "لطفا برای وظیفه نامی انتخاب کنید",
+ "NAME_IN_USE" => "وظیفه ای با نام
{{name}} موجود است",
+ "PAGE_DESCRIPTION" => "لیست وظیفه های وب سایت شما. امکان مدیریت این وظیفه ها در این صفحه وجود دارد.",
+ "PERMISSIONS_UPDATED" => "دسترسی ها برای وظیفه
{{name}} به روز رسانی شد",
+ "SUMMARY" => "خلاصه وظیفه",
+ "UPDATED" => "اطلاعات وظیفه
{{name}} به روز رسانی شد"
+ ],
+
+ "SYSTEM_INFO" => [
+ "@TRANSLATION" => "توضیحات سیستم",
+
+ "DB_NAME" => "نام پایگاه داده",
+ "DB_VERSION" => "نسخه پایگاه داده",
+ "DIRECTORY" => "دایرکتوری پروژه",
+ "PHP_VERSION" => "نسخه پی اچ پی",
+ "SERVER" => "نرمافزار وب سرور",
+ "SPRINKLES" => "اسپرینکل های بارگذاری شده",
+ "UF_VERSION" => "نسخه یوزرفروستینگ",
+ "URL" => "آدرس رووت وب سایت"
+ ],
+
+ "TOGGLE_COLUMNS" => "تغییر ستون",
+
+ "USER" => [
+ 1 => "کاربر",
+ 2 => "کاربران",
+
+ "ADMIN" => [
+ "CHANGE_PASSWORD" => "تغییر گذرواژه کاربر",
+ "SEND_PASSWORD_LINK" => "برای کاربر ایمیلی ارسال شود تا گذرواژه خود را تغییر دهد",
+ "SET_PASSWORD" => "گذرواژه کاربر را انتخاب کنید"
+ ],
+
+ "ACTIVATE" => "کاربر فعال",
+ "CREATE" => "اضافه کردن کاربر",
+ "CREATED" => "کاربر
{{user_name}} با موفقیت اضافه شد",
+ "DELETE" => "حذف کاربر",
+ "DELETE_CONFIRM" => "آیا اطمینان دارید که میخواهید کاربر
{{name}} را حذف کنید؟",
+ "DELETE_YES" => "بله، کاربر حذف شود",
+ "DISABLE" => "غیر فعال سازی کاربر",
+ "EDIT" => "ویرایش کاربر",
+ "ENABLE" => "فعال سازی کاربر",
+ "INFO_PAGE" => "صفحه توضیحات کاربر {{name}}",
+ "LATEST" => "آخرین کاربران",
+ "PAGE_DESCRIPTION" => "لیستی از کاربران سایت. این صفحه به شما امکان ویرایش، فعال سازی و غیر فعال سازی کاربران را می دهد.",
+ "SUMMARY" => "خلاصه حساب",
+ "VIEW_ALL" => "تماشای همه ی کاربران",
+ "WITH_PERMISSION" => "کاربرانی که این دسترسی را دارند"
+ ],
+ "X_USER" => [
+ 0 => "هیچ کاربری",
+ 1 => "{{plural}} کاربر",
+ 2 => "{{plural}} کاربر"
+ ]
+];
diff --git a/main/app/sprinkles/admin/locale/fr_FR/messages.php b/main/app/sprinkles/admin/locale/fr_FR/messages.php
new file mode 100755
index 0000000..82bdf3e
--- /dev/null
+++ b/main/app/sprinkles/admin/locale/fr_FR/messages.php
@@ -0,0 +1,147 @@
+ [
+ 1 => "Activité",
+ 2 => "Activités",
+
+ "LAST" => "Dernière activité",
+ "PAGE" => "Une liste des activités des utilisateurs",
+ "TIME" => "Date de l'activité"
+ ],
+
+ "CACHE" => [
+ "CLEAR" => "Vider le cache",
+ "CLEAR_CONFIRM" => "Voulez-vous vraiment supprimer le cache du site?",
+ "CLEAR_CONFIRM_YES" => "Oui, vider le cache",
+ "CLEARED" => "Cache effacé avec succès !"
+ ],
+
+ "DASHBOARD" => "Tableau de bord",
+ "DELETE_MASTER" => "Vous ne pouvez pas supprimer le compte principal !",
+ "DELETION_SUCCESSFUL" => "L'utilisateur
{{user_name}} a été supprimé avec succès.",
+ "DETAILS_UPDATED" => "Les détails du compte de
{{user_name}} ont été mis à jour",
+ "DISABLE_MASTER" => "Vous ne pouvez pas désactiver le compte principal !",
+ "DISABLE_SELF" => "Vous ne pouvez pas désactiver votre propre compte !",
+ "DISABLE_SUCCESSFUL" => "Le compte de l'utilisateur
{{user_name}} a été désactivé avec succès.",
+
+ "ENABLE_SUCCESSFUL" => "Le compte de l'utilisateur
{{user_name}} a été activé avec succès.",
+
+ "GROUP" => [
+ 1 => "Groupe",
+ 2 => "Groupes",
+
+ "CREATE" => "Créer un groupe",
+ "CREATION_SUCCESSFUL" => "Successfully created group
{{name}} ",
+ "DELETE" => "Supprimer le groupe",
+ "DELETE_CONFIRM" => "Êtes-vous certain de vouloir supprimer le groupe
{{name}} ?",
+ "DELETE_DEFAULT" => "Vous ne pouvez pas supprimer le groupe
{{name}} parce que c'est le groupe par défaut pour les utilisateurs nouvellement enregistrés.",
+ "DELETE_YES" => "Oui, supprimer le groupe",
+ "DELETION_SUCCESSFUL" => "Groupe
{{name}} supprimé avec succès",
+ "EDIT" => "Modifier le groupe",
+ "ICON" => "Icône",
+ "ICON_EXPLAIN" => "Icône des membres du groupe",
+ "INFO_PAGE" => "Informations sur le groupe {{name}}",
+ "MANAGE" => "Gérer le groupe",
+ "NAME" => "Nom du groupe",
+ "NAME_EXPLAIN" => "Spécifiez le nom du groupe",
+ "NOT_EMPTY" => "Vous ne pouvez pas le faire car il y a encore des utilisateurs associés au groupe
{{name}} .",
+ "PAGE_DESCRIPTION" => "Une liste des groupes pour votre site. Fournit des outils de gestion pour éditer et supprimer des groupes.",
+ "UPDATE" => "Les détails du groupe
{{name}} ont été enregistrés"
+ ],
+
+ "MANUALLY_ACTIVATED" => "Le compte de {{user_name}} a été activé manuellement",
+ "MASTER_ACCOUNT_EXISTS" => "Le compte principal existe déjà !",
+ "MIGRATION" => [
+ "REQUIRED" => "Mise à jour de la base de données requise"
+ ],
+
+ "PERMISSION" => [
+ 1 => "Autorisation",
+ 2 => "Autorisations",
+
+ "ASSIGN_NEW" => "Assigner une nouvelle autorisation",
+ "HOOK_CONDITION" => "Hook/Conditions",
+ "MANAGE" => "Gestion des autorisations",
+ "PAGE_DESCRIPTION" => "Une liste des autorisations pour votre site. Fournit des outils de gestion pour modifier et supprimer des autorisations.",
+ "UPDATE" => "Mettre à jour les autorisations"
+ ],
+
+ "ROLE" => [
+ 1 => "Rôle",
+ 2 => "Rôles",
+
+ "ASSIGN_NEW" => "Assigner un nouveau rôle",
+ "CREATE" => "Créer un rôle",
+ "CREATION_SUCCESSFUL" => "Rôle
{{name}} créé avec succès",
+ "DELETE" => "Supprimer le rôle",
+ "DELETE_CONFIRM" => "Êtes-vous certain de vouloir supprimer le rôle
{{name}} ?",
+ "DELETE_DEFAULT" => "Vous ne pouvez pas supprimer le rôle
{{name}} parce que c'est un rôle par défaut pour les utilisateurs nouvellement enregistrés.",
+ "DELETE_YES" => "Oui, supprimer le rôle",
+ "DELETION_SUCCESSFUL" => "Rôle
{{name}} supprimé avec succès",
+ "EDIT" => "Modifier le rôle",
+ "HAS_USERS" => "Vous ne pouvez pas le faire parce qu'il y a encore des utilisateurs qui ont le rôle
{{name}} .",
+ "INFO_PAGE" => "Page d'information pour le rôle {{name}}",
+ "MANAGE" => "Gérer les rôles",
+ "NAME" => "Nom du rôle",
+ "NAME_EXPLAIN" => "Spécifiez le nom du rôle",
+ "NAME_IN_USE" => "Un rôle nommé
{{name}} existe déjà",
+ "PAGE_DESCRIPTION" => "Une liste des rôles de votre site. Fournit des outils de gestion pour modifier et supprimer des rôles.",
+ "PERMISSIONS_UPDATED" => "Autorisations mises à jour pour le rôle
{{name}} ",
+ "UPDATED" => "Détails mis à jour pour le rôle
{{name}} "
+ ],
+
+ "SYSTEM_INFO" => [
+ "@TRANSLATION" => "Informations sur le système",
+
+ "DB_NAME" => "Base de donnée",
+ "DB_VERSION" => "Version DB",
+ "DIRECTORY" => "Répertoire du projet",
+ "PHP_VERSION" => "Version de PHP",
+ "SERVER" => "Logiciel server",
+ "SPRINKLES" => "Sprinkles chargés",
+ "UF_VERSION" => "Version de UserFrosting",
+ "URL" => "Url racine"
+ ],
+
+ "USER" => [
+ 1 => "Utilisateur",
+ 2 => "Utilisateurs",
+
+ "ADMIN" => [
+ "CHANGE_PASSWORD" => "Changer le mot de passe",
+ "SEND_PASSWORD_LINK" => "Envoyer à l'utilisateur un lien qui lui permettra de choisir son propre mot de passe",
+ "SET_PASSWORD" => "Définissez le mot de passe de l'utilisateur comme"
+ ],
+
+ "ACTIVATE" => "Autoriser l'utilisateur",
+ "CREATE" => "Créer un utilisateur",
+ "CREATED" => "L'utilisateur
{{user_name}} a été créé avec succès",
+ "DELETE" => "Supprimer l'utilisateur",
+ "DELETE_CONFIRM" => "Êtes-vous certain de vouloir supprimer l'utilisateur
{{name}} ?",
+ "DELETE_YES" => "Oui, supprimer l'utilisateur",
+ "DISABLE" => "Désactiver l'utilisateur",
+ "EDIT" => "Modifier l'utilisateur",
+ "ENABLE" => "Activer l'utilisateur",
+ "INFO_PAGE" => "Page d'information de l'utilisateur pour {{name}}",
+ "PAGE_DESCRIPTION" => "Une liste des utilisateurs de votre site. Fournit des outils de gestion incluant la possibilité de modifier les détails de l'utilisateur, d'activer manuellement les utilisateurs, d'activer / désactiver les utilisateurs et plus.",
+ "LATEST" => "Derniers utilisateurs",
+ "VIEW_ALL" => "Voir tous les utilisateurs"
+ ],
+ "X_USER" => [
+ 0 => "Aucun utilisateur",
+ 1 => "{{plural}} utilisateur",
+ 2 => "{{plural}} utilisateurs"
+ ]
+];
\ No newline at end of file
diff --git a/main/app/sprinkles/admin/locale/it_IT/messages.php b/main/app/sprinkles/admin/locale/it_IT/messages.php
new file mode 100755
index 0000000..c40d5b3
--- /dev/null
+++ b/main/app/sprinkles/admin/locale/it_IT/messages.php
@@ -0,0 +1,160 @@
+ [
+ 1 => "Attività",
+ 2 => "Attività",
+
+ "LAST" => "Ultima attività",
+ "PAGE" => "Un elenco delle attività degli utenti",
+ "TIME" => "Tempo di attività"
+ ],
+
+ "CACHE" => [
+ "CLEAR" => "Cancellare la cache",
+ "CLEAR_CONFIRM" => "Sei sicuro di voler cancellare la cache del sito?",
+ "CLEAR_CONFIRM_YES" => "Sì, cancellare la cache",
+ "CLEARED" => "La cache è stata eliminata correttamente!"
+ ],
+
+ "DASHBOARD" => "Pannello di Controllo",
+ "NO_FEATURES_YET" => "Non sembra che alcune funzioni siano state create per questo account ... ancora. Forse non sono ancora state implementate, o forse qualcuno ha dimenticato di dare accesso. In entrambi i casi, siamo contenti di averti qui!",
+ "DELETE_MASTER" => "Non puoi eliminare l'account principale!",
+ "DELETION_SUCCESSFUL" => "Hai eliminato utente
{{user_name}} .",
+ "DETAILS_UPDATED" => "Dettagli degli account aggiornati per l'utente
{{user_name}} ",
+ "DISABLE_MASTER" => "Non puoi disattivare l'account principale!",
+ "DISABLE_SELF" => "Non puoi disattivare il tuo account!",
+ "DISABLE_SUCCESSFUL" => "Account per l'utente
{{user_name}} disattivato con successo!",
+ "ENABLE_SUCCESSFUL" => "Account per l'utente
{{user_name}} attivato con successo.",
+
+ "GROUP" => [
+ 1 => "Gruppo",
+ 2 => "Gruppi",
+
+ "CREATE" => "Creare un gruppo",
+ "CREATION_SUCCESSFUL" => "Ha creato con successo il gruppo
{{name}} ",
+ "DELETE" => "Elimina gruppo",
+ "DELETE_CONFIRM" => "Sei sicuro di voler eliminare il gruppo
{{name}} ?",
+ "DELETE_DEFAULT" => "Non puoi eliminare il gruppo
{{name}} perché è il gruppo predefinito per gli utenti appena registrati.",
+ "DELETE_YES" => "Sì, elimini il gruppo",
+ "DELETION_SUCCESSFUL" => "Eliminato il gruppo
{{name}} con successo",
+ "EDIT" => "Modifica gruppo",
+ "ICON" => "Icona del gruppo",
+ "ICON_EXPLAIN" => "Icona per i membri del gruppo",
+ "INFO_PAGE" => "Pagina informazioni di gruppo per
{{name}} ",
+ "MANAGE" => "Gestisci gruppo",
+ "NAME" => "Nome del gruppo",
+ "NAME_EXPLAIN" => "Inserisci un nome per il gruppo",
+ "NOT_EMPTY" => "Non puoi farlo perché ci sono ancora utenti associati al gruppo
{{name}} .",
+ "PAGE_DESCRIPTION" => "Un elenco dei gruppi per il tuo sito. Fornisce strumenti di gestione per la modifica e l'eliminazione di gruppi.",
+ "SUMMARY" => "Riepilogo del gruppo",
+ "UPDATE" => "Dettagli aggiornati per il gruppo
{{name}} ."
+ ],
+
+ "MANUALLY_ACTIVATED" => "
{{user_name}} è stato attivato manualmente",
+ "MASTER_ACCOUNT_EXISTS" => "L'account primario esiste già!",
+ "MIGRATION" => [
+ "REQUIRED" => "È necessario aggiornare il database"
+ ],
+
+ "PERMISSION" => [
+ 1 => "Autorizzazione",
+ 2 => "Autorizzazioni",
+
+ "ASSIGN_NEW" => "Assegna nuova autorizzazione",
+ "HOOK_CONDITION" => "Hook/Condizioni",
+ "ID" => "ID di autorizzazione",
+ "INFO_PAGE" => "Pagina di informazioni sulle autorizzazioni per
{{name}} ",
+ "MANAGE" => "Gestione delle autorizzazioni",
+ "NOTE_READ_ONLY" => "
Si prega di notare: le autorizzazioni sono considerate \"parte del codice\" e non possono essere modificate tramite l'interfaccia. Per aggiungere, rimuovere o modificare le autorizzazioni, i gestori del sito devono utilizzare
migrazione del database. ",
+ "PAGE_DESCRIPTION" => "Un elenco delle autorizzazioni per il tuo sito. Fornisce strumenti di gestione per la modifica e l'eliminazione delle autorizzazioni.",
+ "SUMMARY" => "Sommario delle Autorizzazioni",
+ "UPDATE" => "Aggiorna le autorizzazioni",
+ "VIA_ROLES" => "Ha autorizzazione tramite ruoli"
+ ],
+
+ "ROLE" => [
+ 1 => "Ruolo",
+ 2 => "Ruoli",
+
+ "ASSIGN_NEW" => "Assegna un nuovo ruolo",
+ "CREATE" => "Crea ruolo",
+ "CREATION_SUCCESSFUL" => "Creato con successo il ruolo
{{name}} ",
+ "DELETE" => "Elimina il ruolo",
+ "DELETE_CONFIRM" => "Sei sicuro di voler eliminare il ruolo
{{name}} ?",
+ "DELETE_DEFAULT" => "Non puoi eliminare il ruolo
{{name}} perché è un ruolo predefinito per gli utenti appena registrati.",
+ "DELETE_YES" => "Sì, elimini il ruolo",
+ "DELETION_SUCCESSFUL" => "Eliminato il ruolo
{{name}} ",
+ "EDIT" => "Modifica ruolo",
+ "HAS_USERS" => "Non puoi farlo perché ci sono ancora utenti che hanno il ruolo
{{name}} .",
+ "INFO_PAGE" => "Pagina di informazioni sui ruoli per
{{name}} ",
+ "MANAGE" => "Gestisci Ruoli",
+ "NAME" => "Nome",
+ "NAME_EXPLAIN" => "Inserisci un nome per il ruolo",
+ "NAME_IN_USE" => "Esiste già un ruolo denominato
{{name}} ",
+ "PAGE_DESCRIPTION" => "Un elenco dei ruoli per il tuo sito. Fornisce strumenti di gestione per la modifica e l'eliminazione di ruoli.",
+ "PERMISSIONS_UPDATED" => "Autorizzazioni aggiornate per ruolo
{{name}} ",
+ "SUMMARY" => "Riepilogo dei Ruoli",
+ "UPDATED" => "Dettagli aggiornati per ruolo
{{name}} "
+ ],
+
+ "SYSTEM_INFO" => [
+ "@TRANSLATION" => "Informazioni sul sistema",
+
+ "DB_NAME" => "Nome del database",
+ "DB_VERSION" => "Versione del database",
+ "DIRECTORY" => "Directory del progetto",
+ "PHP_VERSION" => "Versione PHP",
+ "SERVER" => "Software del webserver",
+ "SPRINKLES" => "Sprinkles caricati",
+ "UF_VERSION" => "Versione UserFrosting",
+ "URL" => "Url della radice del sito"
+ ],
+
+ "TOGGLE_COLUMNS" => "Scambia le colonne",
+
+ "USER" => [
+ 1 => "Utente",
+ 2 => "Utenti",
+
+ "ADMIN" => [
+ "CHANGE_PASSWORD" => "Cambia Password Utente",
+ "SEND_PASSWORD_LINK" => "Inviare all'utente un collegamento che permetterà loro di scegliere la propria password",
+ "SET_PASSWORD" => "Impostare la password dell'utente come"
+ ],
+
+ "ACTIVATE" => "Attiva l'utente",
+ "CREATE" => "Creare un utente",
+ "CREATED" => "Account per l'utente
{{user_name}} è stato creato.",
+ "DELETE" => "Elimina utente",
+ "DELETE_CONFIRM" => "Sei sicuro di voler eliminare l'utente
{{name}} ?",
+ "DELETE_YES" => "Sì, elimina l'utente",
+ "DISABLE" => "Disabilita l'utente",
+ "EDIT" => "Modifica utente",
+ "ENABLE" => "Abilita l'utente",
+ "INFO_PAGE" => "Pagina informazioni utente per
{{name}} ",
+ "LATEST" => "Ultimi Utenti",
+ "PAGE_DESCRIPTION" => "Un elenco degli utenti del tuo sito. Fornisce strumenti di gestione, tra cui la possibilità di modificare i dettagli utente, attivare manualmente gli utenti, abilitare / disabilitare gli utenti e altro ancora.",
+ "SUMMARY" => "Riepilogo account",
+ "VIEW_ALL" => "Visualizza tutti gli utenti",
+ "WITH_PERMISSION" => "Utenti con questa autorizzazione"
+ ],
+ "X_USER" => [
+ 0 => "Nessun utente",
+ 1 => "{{plural}} utente",
+ 2 => "{{plural}} utenti"
+ ]
+];
diff --git a/main/app/sprinkles/admin/locale/pt_PT/messages.php b/main/app/sprinkles/admin/locale/pt_PT/messages.php
new file mode 100755
index 0000000..0faf818
--- /dev/null
+++ b/main/app/sprinkles/admin/locale/pt_PT/messages.php
@@ -0,0 +1,139 @@
+ [
+ 1 => "Atividade",
+ 2 => "Atividades",
+
+ "LAST" => "Última atividade",
+ "PAGE" => "Lista de atividade dos utilizadores",
+ "TIME" => "Tempo da Atividade"
+ ],
+
+ "CACHE" => [
+ "CLEAR" => "Limpar cache",
+ "CLEAR_CONFIRM" => "Tem a certeza que pretende limpar a cache do site?",
+ "CLEAR_CONFIRM_YES" => "Sim, limpar cache",
+ "CLEARED" => "Cache limpa com sucesso!"
+ ],
+
+ "DASHBOARD" => "Painel de Controlo",
+ "DELETE_MASTER" => "Não pode apagar a conta principal!",
+ "DELETION_SUCCESSFUL" => "Utilizador
{{user_name}} foi removido com sucesso.",
+ "DETAILS_UPDATED" => "Detalhes de conta atualizados para o utilizador
{{user_name}} ",
+ "DISABLE_MASTER" => "Não pode desativar a conta principal!",
+ "DISABLE_SUCCESSFUL" => "Conta do utilizador
{{user_name}} foi desativada com sucesso.",
+
+ "ENABLE_SUCCESSFUL" => "Conta do utilizador
{{user_name}} foi ativada com sucesso.",
+
+ "GROUP" => [
+ 1 => "Grupo",
+ 2 => "Grupos",
+
+ "CREATE" => "Criar grupo",
+ "CREATION_SUCCESSFUL" => "Grupo criado com sucesso",
+ "DELETE" => "Remover grupo",
+ "DELETION_SUCCESSFUL" => "Grupo removido com sucesso",
+ "DELETE_CONFIRM" => "Tem a certeza que pretende remover o grupo
{{name}} ?",
+ "DELETE_YES" => "Sim, remover grupo",
+ "EDIT" => "Editar grupo",
+ "ICON" => "Icon do grupo",
+ "ICON_EXPLAIN" => "Icon para membros do grupo",
+ "INFO_PAGE" => "Página informativa do grupo {{name}}",
+ //"MANAGE" => "Manage group",
+ "NAME" => "Nome do grupo",
+ "NAME_EXPLAIN" => "Por favor introduza um nome para o grupo",
+ "PAGE_DESCRIPTION" => "Lista de grupos do site. Contém opções para editar e remover grupos."
+ ],
+
+ "MANUALLY_ACTIVATED" => "A conta de {{user_name}} foi ativada manualmente.",
+ "MASTER_ACCOUNT_EXISTS" => "A contra principal já existe!",
+ "MIGRATION" => [
+ "REQUIRED" => "É necessário uma atualização da base de dados."
+ ],
+
+ "PERMISSION" => [
+ 1 => "Permissão",
+ 2 => "Permissões",
+
+ "ASSIGN_NEW" => "Atribuir nova permissão",
+ "HOOK_CONDITION" => "Hook/Condições",
+ "MANAGE" => "Gerir permissões",
+ "PAGE_DESCRIPTION" => "Lista de permissões do site. Contém opções para editar e remover permissões.",
+ "UPDATE" => "Atualizar permissões"
+ ],
+
+ "ROLE" => [
+ 1 => "Cargo",
+ 2 => "Cargos",
+
+ "ASSIGN_NEW" => "Atribuir novo cargo",
+ "CREATE" => "Criar cargo",
+ "CREATION_SUCCESSFUL" => "Cargo criado com sucesso",
+ "DELETE" => "Remover cargo",
+ "DELETION_SUCCESSFUL" => "Cargo removido com sucesso",
+ "DELETE_CONFIRM" => "Tem a certeza que pretende remover o cargo
{{name}} ?",
+ "DELETE_YES" => "Sim, remover cargo",
+ "EDIT" => "Editar cargo",
+ "INFO_PAGE" => "Página informativa do cargo {{name}}",
+ "MANAGE" => "Gerir cargos",
+ "NAME" => "Nome",
+ "NAME_EXPLAIN" => "Por favor introduza um nome para o cargo",
+ "PAGE_DESCRIPTION" => "Lista de cargos do site. Contém opções para editar e remover cargos.",
+ "UPDATE" => "Atualizar cargos",
+ "UPDATED" => "Cargo
{{name}} atualizado"
+ ],
+
+ "SYSTEM_INFO" => [
+ "@TRANSLATION" => "Informação do sistema",
+
+ "DB_NAME" => "Nome da base de dados",
+ "DB_VERSION" => "Versão da base de dados",
+ "DIRECTORY" => "Diretório do projeto",
+ "PHP_VERSION" => "Versão PHP",
+ "SERVER" => "Software do servidor web",
+ "SPRINKLES" => "Sprinkles carregados",
+ "UF_VERSION" => "Versão do UserFrosting",
+ "URL" => "Raiz (url) do site"
+ ],
+
+ "USER" => [
+ 1 => "Utilizador",
+ 2 => "Utilizadores",
+
+ "ADMIN" => [
+ "CHANGE_PASSWORD" => "Alterar password",
+ "SEND_PASSWORD_LINK" => "Enviar um link ao utilizador que lhe permita escolher a sua password",
+ "SET_PASSWORD" => "Definir a password do utilizador como"
+ ],
+
+ "ACTIVATE" => "Ativar utilizador",
+ "CREATE" => "Criar utilizador",
+ "DELETE" => "Remover utilizador",
+ "DELETE_CONFIRM" => "Tem a certeza que pretende remover o utilizador
{{name}} ?",
+ "DELETE_YES" => "Sim, remover utilizador",
+ "DISABLE" => "Desativar utilizador",
+ "EDIT" => "Editar utilizador",
+ "ENABLE" => "Ativar utilizador",
+ "INFO_PAGE" => "Página informativa do utilizador {{name}}",
+ "PAGE_DESCRIPTION" => "Lista de utilizadores do site. Contém opções para editar detalhes, ativar/desativar utilizadores e outras.",
+ "LATEST" => "Últimos Utilizadores",
+ "VIEW_ALL" => "Ver todos os utilizadores"
+ ],
+ "X_USER" => [
+ 0 => "Nenhum utilizador",
+ 1 => "{{plural}} utilizador",
+ 2 => "{{plural}} utilizadores"
+ ]
+];
diff --git a/main/app/sprinkles/admin/locale/ru_RU/messages.php b/main/app/sprinkles/admin/locale/ru_RU/messages.php
new file mode 100755
index 0000000..d6f6e1a
--- /dev/null
+++ b/main/app/sprinkles/admin/locale/ru_RU/messages.php
@@ -0,0 +1,160 @@
+ [
+ 1 => "Активность",
+ 2 => "Активность",
+
+ "LAST" => "Последняя активность",
+ "PAGE" => "Список действий пользователя",
+ "TIME" => "Время действия"
+ ],
+
+ "CACHE" => [
+ "CLEAR" => "Очистить кэш",
+ "CLEAR_CONFIRM" => "Уверены, что хотите очистить кэш сайта?",
+ "CLEAR_CONFIRM_YES" => "Да, очистить кэш",
+ "CLEARED" => "Кэш успешно очищен !"
+ ],
+
+ "DASHBOARD" => "Панель управления",
+ "NO_FEATURES_YET" => "Похоже, некоторые функции не были настроены для этого аккаунта... пока. Возможно, они еще не реализованы, или, может быть, кто-то забыл дать вам доступ. В любом случае, мы рады вас видеть здесь!",
+ "DELETE_MASTER" => "Нельзя удалить главный аккаунт!",
+ "DELETION_SUCCESSFUL" => "Пользователь
{{user_name}} был успешно удален.",
+ "DETAILS_UPDATED" => "Данные для аккаунта
{{user_name}} обновлены",
+ "DISABLE_MASTER" => "Нельзя отключить главный аккаунт!",
+ "DISABLE_SELF" => "Вы не можете отключить собственный аккаунт!",
+ "DISABLE_SUCCESSFUL" => "Пользователь
{{user_name}} был успешно отключен.",
+
+ "ENABLE_SUCCESSFUL" => "Пользователь
{{user_name}} был успешно включен.",
+
+ "GROUP" => [
+ 1 => "Группа",
+ 2 => "Группы",
+
+ "CREATE" => "Создать группу",
+ "CREATION_SUCCESSFUL" => "Успешно создана группа
{{name}} ",
+ "DELETE" => "Удалить группу",
+ "DELETE_CONFIRM" => "Вы уверены, что вы хотите удалить группу
{{name}} ?",
+ "DELETE_DEFAULT" => "Нельзя удалить группу
{{name}} , потому что это группа по умолчанию для новых пользователей.",
+ "DELETE_YES" => "Да, удалить группу",
+ "DELETION_SUCCESSFUL" => "Успешно удалена группа
{{name}} ",
+ "EDIT" => "Редактор группы",
+ "ICON" => "Значок группы",
+ "ICON_EXPLAIN" => "Значок для членов группы",
+ "INFO_PAGE" => "Описание группы {{name}}",
+ "MANAGE" => "Управление группой",
+ "NAME" => "Имя группы",
+ "NAME_EXPLAIN" => "Пожалуйста, введите имя группы",
+ "NOT_EMPTY" => "Вы не можете сделать это, потому что до сих пор есть пользователи, связанные с группой
{{name}} .",
+ "PAGE_DESCRIPTION" => "Список групп для вашего сайта. Предоставляет инструменты управления для редактирования и удаления групп.",
+ "SUMMARY" => "Резюме группы",
+ "UPDATE" => "Информация обновлена для группы
{{name}} "
+ ],
+
+ "MANUALLY_ACTIVATED" => "{{user_name}} аккаунт был активирован вручную",
+ "MASTER_ACCOUNT_EXISTS" => "Мастер-аккаунт уже существует!",
+ "MIGRATION" => [
+ "REQUIRED" => "Необходимо обновление базы данных"
+ ],
+
+ "PERMISSION" => [
+ 1 => "Доступ",
+ 2 => "Права доступа",
+
+ "ASSIGN_NEW" => "Назначить новые права",
+ "HOOK_CONDITION" => "Привязки/Условия",
+ "ID" => "ID доступа",
+ "INFO_PAGE" => "Информация о доступах для '{{name}}'",
+ "MANAGE" => "Управление правами",
+ "NOTE_READ_ONLY" => "
Пожалуйста, обратите внимание: права и доступы считаются \"частью кода\" и не могут быть изменены через интерфейс. Чтобы добавить, удалить или изменить доступы, нужно будет использовать сайт
базы данных миграции. ",
+ "PAGE_DESCRIPTION" => "Список прав доступа для вашего сайта. Предоставляет инструменты управления для редактирования и удаления прав.",
+ "SUMMARY" => "Сводка доступов",
+ "UPDATE" => "Обновление прав",
+ "VIA_ROLES" => "Имеет права через роли"
+ ],
+
+ "ROLE" => [
+ 1 => "Роль",
+ 2 => "Роли",
+
+ "ASSIGN_NEW" => "Назначение новой роли",
+ "CREATE" => "Создать роль",
+ "CREATION_SUCCESSFUL" => "Успешно создана роль
{{name}} ",
+ "DELETE" => "Удалить роль",
+ "DELETE_CONFIRM" => "Вы уверены, что вы хотите удалить роль
{{name}} ?",
+ "DELETE_DEFAULT" => "Невозможно удалить роль
{{name}} , потому что это роль по умолчанию для новых пользователей.",
+ "DELETE_YES" => "Да, удалить роль",
+ "DELETION_SUCCESSFUL" => "Успешно удалена роль
{{name}} ",
+ "EDIT" => "Изменить роль",
+ "HAS_USERS" => "Вы не можете сделать это, потому что до сих пор есть пользователи, имеющие роль
{{name}} .",
+ "INFO_PAGE" => "Информации роли для {{name}}",
+ "MANAGE" => "Управление ролями",
+ "NAME" => "Имя",
+ "NAME_EXPLAIN" => "Пожалуйста, укажите имя для роли",
+ "NAME_IN_USE" => "Роль с именем
{{name}} уже существует",
+ "PAGE_DESCRIPTION" => "Список ролей для вашего сайта. Предоставляет инструменты управления для редактирования и удаления ролей.",
+ "PERMISSIONS_UPDATED" => "Права обновлены для роли
{{name}} ",
+ "SUMMARY" => "Основная информация",
+ "UPDATED" => "Информация для роли
{{name}} "
+ ],
+
+ "SYSTEM_INFO" => [
+ "@TRANSLATION" => "Системная информация",
+
+ "DB_NAME" => "Имя базы данных",
+ "DB_VERSION" => "Версия базы данных",
+ "DIRECTORY" => "Каталог проекта",
+ "PHP_VERSION" => "Версия PHP",
+ "SERVER" => "ПО Сервера",
+ "SPRINKLES" => "Загружены модули",
+ "UF_VERSION" => "Версия UserFrosting",
+ "URL" => "URL-адрес сайта"
+ ],
+
+ "TOGGLE_COLUMNS" => "Переключатель столбцов",
+
+ "USER" => [
+ 1 => "Пользователь",
+ 2 => "Пользователи",
+
+ "ADMIN" => [
+ "CHANGE_PASSWORD" => "Сменить пароль пользователя",
+ "SEND_PASSWORD_LINK" => "Отправить пользователю ссылку, которая позволит выбрать свой собственный пароль",
+ "SET_PASSWORD" => "Установите пароль пользователя как"
+ ],
+
+ "ACTIVATE" => "Активировать пользователя",
+ "CREATE" => "Создать пользователя",
+ "CREATED" => "Пользователь
{{user_name}} был успешно создан",
+ "DELETE" => "Удалить пользователя",
+ "DELETE_CONFIRM" => "Вы уверены, что вы хотите удалить пользователя
{{name}} ?",
+ "DELETE_YES" => "Да, удалить пользователя",
+ "DELETED" => "Пользователь удален",
+ "DISABLE" => "Отключить пользователя",
+ "EDIT" => "Изменить пользователя",
+ "ENABLE" => "Включить пользователя",
+ "INFO_PAGE" => "Информация о пользователе для {{name}}",
+ "LATEST" => "Последние пользователи",
+ "PAGE_DESCRIPTION" => "Список пользователей для вашего сайта. Предоставляет средства управления, включая возможность редактирования сведений о пользователях, ручной активации пользователей, включения/отключения пользователей и многое другое.",
+ "SUMMARY" => "Сводка аккаунта",
+ "VIEW_ALL" => "Все пользователи",
+ "WITH_PERMISSION" => "Пользователи с этим доступом"
+ ],
+ "X_USER" => [
+ 0 => "Нет пользователей",
+ 1 => "{{plural}} пользователя",
+ 2 => "{{plural}} пользователей"
+ ]
+];
diff --git a/main/app/sprinkles/admin/locale/th_TH/messages.php b/main/app/sprinkles/admin/locale/th_TH/messages.php
new file mode 100755
index 0000000..546232d
--- /dev/null
+++ b/main/app/sprinkles/admin/locale/th_TH/messages.php
@@ -0,0 +1,134 @@
+ [
+ 1 => "กิจกรรม",
+ 2 => "กิจกรรม",
+
+ "LAST" => "กิจกรรมล่าสุด",
+ "PAGE" => "รายการกิจกรรมของผู้ใช้",
+ "TIME" => "เวลาที่ทำกิจกรรม"
+ ],
+
+ "CACHE" => [
+ "CLEAR" => "ล้างแคช",
+ "CLEAR_CONFIRM" => "คุณแน่ใจหรือที่จะล้างแคชของเว็บ?",
+ "CLEAR_CONFIRM_YES" => "ใช่ ล้างแคชเลย",
+ "CLEARED" => "ล้างแคชเรียบร้อยแล้ว!"
+ ],
+
+ "DASHBOARD" => "แผงควบคุม",
+ "DELETE_MASTER" => "คุณไม่สามารถลบบัญชีหลักได้!",
+ "DELETION_SUCCESSFUL" => "ลบผู้ใช้
{{user_name}} เรียบร้อยแล้ว",
+ "DETAILS_UPDATED" => "ปรับปรุงรายระเอียดบัญชีให้กับ
{{user_name}} แล้ว",
+ "DISABLE_MASTER" => "คุณไม่สามารถปิดการใช้งานบัญชีหลัก!",
+ "DISABLE_SUCCESSFUL" => "ปิดการใช้งานบัญชีของผู้ใช้
{{user_name}} เรียบร้อยแล้ว",
+
+ "ENABLE_SUCCESSFUL" => "เปิดการใช้งานบัญชีของผู้ใช้
{{user_name}} เรียบร้อยแล้ว",
+
+ "GROUP" => [
+ 1 => "กลุ่ม",
+ 2 => "กลุ่ม",
+
+ "CREATE" => "สร้างกลุ่ม",
+ "DELETE" => "ลบกลุ่ม",
+ "DELETE_CONFIRM" => "คุณแน่ใจหรือที่จะลบกลุ่ม
{{name}} ?",
+ "DELETE_YES" => "ใช่ ลบกลุ่มนี้เลย",
+ "EDIT" => "แก้ไขกลุ่ม",
+ "ICON" => "ไอคอนกลุ่ม",
+ "ICON_EXPLAIN" => "ไอคอนสำหรับสมาชิกกลุ่ม",
+ "INFO_PAGE" => "หน้าข้อมูลกลุ่มสำหรับ {{name}}",
+ //"MANAGE" => "Manage group",
+ "NAME" => "ชื่อกลุ่ม",
+ "NAME_EXPLAIN" => "กรุณาตั้งชื่อสำหรับกลุ่มนี้",
+ "PAGE_DESCRIPTION" => "รายชื่อกลุ่มในเว็บของคุณ ประกอบไปด้วยเครื่องมือในการจัดการสำหรับการแก้ไขและลบกลุ่ม"
+ ],
+
+ "MANUALLY_ACTIVATED" => "บัญชีของ {{user_name}} ได้เปิดใช้งานเองแล้ว",
+ "MASTER_ACCOUNT_EXISTS" => "มีบัญชีหลักอยู่แล้ว!",
+ "MIGRATION" => [
+ "REQUIRED" => "ต้องการการปรับปรุงฐานข้อมูล"
+ ],
+
+ "PERMISSION" => [
+ 1 => "สิทธิการเข้าถึง",
+ 2 => "สิทธิการเข้าถึง",
+
+ "ASSIGN_NEW" => "กำหนดสิทธิการเข้าถึงใหม่",
+ "HOOK_CONDITION" => "ข้อกำหนด/เงื่อนไข",
+ "MANAGE" => "จัดการสิทธิการเข้าถึง",
+ "PAGE_DESCRIPTION" => "รายการสิทธิการเข้าถึงในเว็บของคุณ ประกอบไปด้วยเครื่องมือในการจัดการสำหรับการแก้ไขและลบสิทธิการเข้าถึง",
+ "UPDATE" => "ปรับปรุงสิทธิการเข้าถึง"
+ ],
+
+ "ROLE" => [
+ 1 => "ตำแหน่ง",
+ 2 => "ตำแหน่ง",
+
+ "ASSIGN_NEW" => "กำหนดตำแหน่งใหม่",
+ "CREATE" => "สร้างตำแหน่ง",
+ "DELETE" => "ลบตำแหน่ง",
+ "DELETE_CONFIRM" => "คุณแน่ใจหรือที่จะลบตำแหน่ง
{{name}} ?",
+ "DELETE_YES" => "ใช่ ลบตำแหน่งนี้เลย",
+ "EDIT" => "แก้ไขตำแหน่ง",
+ "INFO_PAGE" => "หน้าข้อมูลตำแหน่งสำหรับ {{name}}",
+ "MANAGE" => "จัดการตำแหน่ง",
+ "NAME" => "ชื่อ",
+ "NAME_EXPLAIN" => "กรุณาตั้งชื่อสำหรับตำแหน่งนี้",
+ "PAGE_DESCRIPTION" => "รายชื่อตำแหน่งในเว็บของคุณ ประกอบไปด้วยเครื่องมือในการจัดการสำหรับแก้ไขและลบตำแหน่ง",
+ "UPDATED" => "ปรับปรุงตำแหน่ง"
+ ],
+
+ "SYSTEM_INFO" => [
+ "@TRANSLATION" => "ข้อมูลระบบ",
+
+ "DB_NAME" => "ชื่อฐานข้อมูล",
+ "DB_VERSION" => "เวอร์ชั่นฐานข้อมูล",
+ "DIRECTORY" => "ไดเรกทอรีของโปรเจค",
+ "PHP_VERSION" => "เวอร์ชั่น PHP",
+ "SERVER" => "ซอฟต์แวร์เว็บเซิร์ฟเวอร์",
+ "SPRINKLES" => "Sprinkles ที่ถูกโหลด",
+ "UF_VERSION" => "เวอร์ชั่น UserFrosting",
+ "URL" => "URL ของรากเว็บไซต์"
+ ],
+
+ "USER" => [
+ 1 => "ผู้ใช้",
+ 2 => "ผู้ใช้",
+
+ "ADMIN" => [
+ "CHANGE_PASSWORD" => "เปลี่ยนรหัสผ่านผู้ใช้",
+ "SEND_PASSWORD_LINK" => "ส่งลิงก์ที่จะอนุญาตให้ผู้ใช้เลือกรหัสผ่านเองให้กับผู้ใช้",
+ "SET_PASSWORD" => "ตั้งรหัสผ่านของผู้ใช้เป็น"
+ ],
+
+ "ACTIVATE" => "เปิดใช้งานผู้ใช้",
+ "CREATE" => "สร้างผู้ใช้",
+ "DELETE" => "ลบผู้ใช้",
+ "DELETE_CONFIRM" => "คุณแน่ใจหรือที่จะลบผู้ใช้
{{name}} ?",
+ "DELETE_YES" => "ใช่ ลบผู้ใช้นี้เลย",
+ "DISABLE" => "ปิดการใช้งานผู้ใช้",
+ "EDIT" => "แก้ไขผู้ใช้",
+ "ENABLE" => "เปิดการใช้งานผู้ใช้",
+ "INFO_PAGE" => "หน้าข้อมูลของผู้ใช้ {{name}}",
+ "PAGE_DESCRIPTION" => "รายชื่อผู้ใช้ในเว็บของคุณ ประกอบไปด้วยเครื่องมือสำหรับการจัดการ รวมทั้งความสามารถในการแก้ไขรายละเอียดผู้ใช้ การเปิดใช้งานผู้ใช้ การเปิด/ปิดบัญชีผู้ใช้และอื่น ๆ",
+ "LATEST" => "ผู้ใช้ล่าสุด",
+ "VIEW_ALL" => "ดูผู้ใช้ทั้งหมด"
+ ],
+ "X_USER" => [
+ 0 => "ไม่มีผู้ใช้",
+ 1 => "{{plural}} ผู้ใช้",
+ 2 => "{{plural}} ผู้ใช้"
+ ]
+];
\ No newline at end of file
diff --git a/main/app/sprinkles/admin/locale/tr/messages.php b/main/app/sprinkles/admin/locale/tr/messages.php
new file mode 100755
index 0000000..25d5633
--- /dev/null
+++ b/main/app/sprinkles/admin/locale/tr/messages.php
@@ -0,0 +1,160 @@
+ [
+ 1 => "Etkinlik",
+ 2 => "Etkinlikler",
+
+ "LAST" => "Son Etkinlik",
+ "PAGE" => "Kullanıcı etkinliklerinin listesi",
+ "TIME" => "Aktivite zamanı"
+ ],
+
+ "CACHE" => [
+ "CLEAR" => "Önbelleği temizle",
+ "CLEAR_CONFIRM" => "Site önbelleğini temizlemek istediğine emin misin?",
+ "CLEAR_CONFIRM_YES" => "Evet, önbelleği temizle",
+ "CLEARED" => "Önbellek temizlenmesi başarıyla tamamlandı!"
+ ],
+
+ "DASHBOARD" => "Pano",
+ "NO_FEATURES_YET" => "Bu hesap için herhangi bir özellik ayarlanmış gibi görünmüyor... Henüz. Belki de henüz uygulanmadı, veya belki birisi size erişim vermeyi unutttu. Her iki durumda da sizi aramızda gördüğümüze sevindik!",
+ "DELETE_MASTER" => "Ana hesabı silemezsiniz!",
+ "DELETION_SUCCESSFUL" => "Kullanıcı
{{user_name}} silme işlemi başarıyla tamamlandı.",
+ "DETAILS_UPDATED" => "Kullanıcı
{{user_name}} için güncel hesap detayları",
+ "DISABLE_MASTER" => "Ana hesabı devre dışı bırakamazsınız!",
+ "DISABLE_SELF" => "Kendi hesabınızın etkinliğini sonlandıramazsınız!",
+ "DISABLE_SUCCESSFUL" => "Kullanıcı hesabın
{{user_name}} başarıyla devre dışı bırakıldı.",
+
+ "ENABLE_SUCCESSFUL" => "Kullanıcı hesabın
{{user_name}} başarıyla etkinleştirildi.",
+
+ "GROUP" => [
+ 1 => "Grup",
+ 2 => "Grıplar",
+
+ "CREATE" => "Grup oluşturmak",
+ "CREATION_SUCCESSFUL" => "Grup oluşturma başarılı
{{name}} ",
+ "DELETE" => "Grubu sil",
+ "DELETE_CONFIRM" => "Grubu silmek istediğine emin misin
{{name}} ?",
+ "DELETE_DEFAULT" => "Grubu silemezsin
{{name}} çünkü o yeni kayıtlanan kullanıcılar için varsayılan grup.",
+ "DELETE_YES" => "Evet, grubu sil",
+ "DELETION_SUCCESSFUL" => "Grup silme başarılı
{{name}} ",
+ "EDIT" => "Grubu düzenle",
+ "ICON" => "Grup ikonu",
+ "ICON_EXPLAIN" => "Grup iyileri için ikon",
+ "INFO_PAGE" => "Grup bilgisi sayfası {{name}} için",
+ "MANAGE" => "Grubu yönet",
+ "NAME" => "Grup adı",
+ "NAME_EXPLAIN" => "Lütfen grup için bir isim giriniz",
+ "NOT_EMPTY" => "Bunu yapamazsınız çünkü hala grupla ilişkili kullanıcılar var
{{name}} .",
+ "PAGE_DESCRIPTION" => "Siten için grupların bir listesi. Grupları silmek ve düzenlemek için yönetim araçları sağlar.",
+ "SUMMARY" => "Grup özeti",
+ "UPDATE" => "Grup için detaylar güncellendi
{{name}} "
+ ],
+
+ "MANUALLY_ACTIVATED" => "{{user_name}}'ın hesabı el ile aktifleştirildi",
+ "MASTER_ACCOUNT_EXISTS" => "Ana hesap zaten mevcut!",
+ "MIGRATION" => [
+ "REQUIRED" => "Veritabanını güncellemek gerek"
+ ],
+
+ "PERMISSION" => [
+ 1 => "İzin",
+ 2 => "İzinler",
+
+ "ASSIGN_NEW" => "Yeni izin ata",
+ "HOOK_CONDITION" => "Kanca/Koşullar",
+ "ID" => "İzin Kimliği",
+ "INFO_PAGE" => "{{name}} için izin bilgi sayfası",
+ "MANAGE" => "İzinleri yönet",
+ "NOTE_READ_ONLY" => "
Lütfen Dikkat izinler ''bir kodun parçası'' olarak kabul edilir ve arayüz aracılığıyla değiştirilemez. İzln eklemek, kaldırmak ya da değiştirmek için site bakımcıları bir
veritabanı geçişi kullanmalıdır",
+ "PAGE_DESCRIPTION" => "Siteniz için izinlerin bir listesi. Düzenleme yapmak ve izinleri kaldırmak yönetim araçları temin eder.",
+ "SUMMARY" => "İzin Özeti",
+ "UPDATE" => "İzinlerin Güncellenmesi",
+ "VIA_ROLES" => "Roller ile izin alımı"
+ ],
+
+ "ROLE" => [
+ 1 => "Rol",
+ 2 => "Roller",
+
+ "ASSIGN_NEW" => "Yeni rol ata",
+ "CREATE" => "Rol oluştur",
+ "CREATION_SUCCESSFUL" => "Rol oluşturma başarılı
{{name}} ",
+ "DELETE" => "Rolü sil",
+ "DELETE_CONFIRM" => "Rolü silmek istediğine emin misin
{{name}} ?",
+ "DELETE_DEFAULT" => "Rolü silemezsin
{{name}} çünkü o kaydolmuş kullanıcılar için varsayılan bir rol.",
+ "DELETE_YES" => "Evet, rolü sil",
+ "DELETION_SUCCESSFUL" => "Rol başarıyla silindi
{{name}} ",
+ "EDIT" => "Rolü düzenle",
+ "HAS_USERS" => "Bunu yapamazsın çünkü hala bu rol ile bağlantılı kullanıcılar var
{{name}} .",
+ "INFO_PAGE" => "{{name}} için rol bilgi sayfası",
+ "MANAGE" => "Rolleri yönet",
+ "NAME" => "Ad",
+ "NAME_EXPLAIN" => "Lütfen rol için bir ad giriniz",
+ "NAME_IN_USE" => "
{{name}} adında bir rol zaten mevcut",
+ "PAGE_DESCRIPTION" => "Siteniz için rollerin bir listesi. Düzenlemek ve rolleri silmek için yönetim araçları sağlar.",
+ "PERMISSIONS_UPDATED" => "Rol için izinler güncellendi
{{name}} ",
+ "SUMMARY" => "Rol özeti",
+ "UPDATED" => "Rol için detaylar güncellendi
{{name}} "
+ ],
+
+ "SYSTEM_INFO" => [
+ "@TRANSLATION" => "Sistem bilgisi",
+
+ "DB_NAME" => "Veritabanı adı",
+ "DB_VERSION" => "Veritabanı sürümü",
+ "DIRECTORY" => "Proje dizini",
+ "PHP_VERSION" => "PHP sürümü",
+ "SERVER" => "Web sunucu yazılımı",
+ "SPRINKLES" => "Yüklü serpintiler",
+ "UF_VERSION" => "UserFrosting sürümü",
+ "URL" => "Site kök url"
+ ],
+
+ "TOGGLE_COLUMNS" => "Sütünları değiştirme",
+
+ "USER" => [
+ 1 => "Kullanıcı",
+ 2 => "Kullanıcılar",
+
+ "ADMIN" => [
+ "CHANGE_PASSWORD" => "Kullanıcı şifresini değiştir",
+ "SEND_PASSWORD_LINK" => "Kullanıcıya kendi şifresini seçebileceği bir bağlantı gönder",
+ "SET_PASSWORD" => "Kullanıcının şifresi olarak ayarla"
+ ],
+
+ "ACTIVATE" => "Aktif Kullanıcı",
+ "CREATE" => "Kullanıcı oluştur",
+ "CREATED" => "Kullanıcı
{{user_name}} başarıyla oluşturuldu",
+ "DELETE" => "Kullanıcıyı sil",
+ "DELETE_CONFIRM" => "Kullanıcıyı silmek istediğinden emin misin?
{{name}} ?",
+ "DELETE_YES" => "Evet, kullanıcıyı sil",
+ "DELETED" => "Kullanıcı silindi",
+ "DISABLE" => "Kullanıcı devre dışı",
+ "EDIT" => "Kullanıcıyı düzenle",
+ "ENABLE" => "Kullanıcı etkin",
+ "INFO_PAGE" => "{{name}} için kullanıcı bilgisi",
+ "LATEST" => "Son Kullanıcılar",
+ "PAGE_DESCRIPTION" => "Siten için kullanıcıların listesi. Kullanıcı detaylarını düzenlemek, elle kullanıcıları aktifleştirmek, kullanıcıları etkinleştirme/devre dışı bırakma, ve daha fazlası için yönetimsel araçlar sağlar.",
+ "SUMMARY" => "Hesap özeti",
+ "VIEW_ALL" => "Tüm kullanıcıları göster",
+ "WITH_PERMISSION" => "Bu izni olan kullanıcılar"
+ ],
+ "X_USER" => [
+ 0 => "Kullanıcı yok",
+ 1 => "{{plural}} kullanıcı",
+ 2 => "{{plural}} kullanıcılar"
+ ]
+];
diff --git a/main/app/sprinkles/admin/locale/zh_CN/messages.php b/main/app/sprinkles/admin/locale/zh_CN/messages.php
new file mode 100755
index 0000000..2adc8c8
--- /dev/null
+++ b/main/app/sprinkles/admin/locale/zh_CN/messages.php
@@ -0,0 +1,161 @@
+ [
+ 1 => "活动",
+ 2 => "活动",
+
+ "LAST" => "最后活动",
+ "PAGE" => "用户活动列表",
+ "TIME" => "活动时间"
+ ],
+
+ "ADMIN" => [
+ "PANEL" => "控制台"
+ ],
+
+ "CACHE" => [
+ "CLEAR" => "清除缓存",
+ "CLEAR_CONFIRM" => "你确定要清除此网站的缓存?",
+ "CLEAR_CONFIRM_YES" => "是的, 清除缓存",
+ "CLEARED" => "缓存成功清除 !"
+ ],
+
+ "DASHBOARD" => "仪表盘",
+ "DELETE_MASTER" => "你不能删除超级账户!",
+ "DELETION_SUCCESSFUL" => "用户
{{user_name}} 删除成功.",
+ "DETAILS_UPDATED" => "用户
{{user_name}} 更新成功",
+ "DISABLE_MASTER" => "你不能禁用超级用户!",
+ "DISABLE_SELF" => "不能禁用你自己的账户!",
+ "DISABLE_SUCCESSFUL" => "用户名
{{user_name}} 成功禁用.",
+
+ "ENABLE_SUCCESSFUL" => "用户名
{{user_name}} 启用成功.",
+
+ "GROUP" => [
+ 1 => "组",
+ 2 => "组",
+
+ "CREATE" => "新建组",
+ "CREATION_SUCCESSFUL" => "成功创建组
{{name}} ",
+ "DELETE" => "删除组",
+ "DELETE_CONFIRM" => "确定要删除组
{{name}} ?",
+ "DELETE_DEFAULT" => "你无法删除组
{{name}} 因为这是新注册用户的默认组.",
+ "DELETE_YES" => "是, 删除组",
+ "DELETION_SUCCESSFUL" => "成功删除组
{{name}} ",
+ "EDIT" => "编辑组",
+ "ICON" => "组图标",
+ "ICON_EXPLAIN" => "组成员图标",
+ "INFO_PAGE" => "{{name}} 组的信息页",
+ "NAME" => "组名",
+ "NAME_EXPLAIN" => "请为组取一个名字",
+ "NOT_EMPTY" => "你不能这样做,因为还有用户在组
{{name}} .",
+ "PAGE_DESCRIPTION" => "网站组列表. 请管理编辑和删除组的工具.",
+ "SUMMARY" => "组简介",
+ "UPDATE" => "组
{{name}} 的信息已经更新"
+ ],
+
+ "MANUALLY_ACTIVATED" => "{{user_name}} 账户已手动激活",
+ "MASTER_ACCOUNT_EXISTS" => "超级账户已存在!",
+ "MIGRATION" => [
+ "REQUIRED" => "数据库需要更新"
+ ],
+
+ "PERMISSION" => [
+ 1 => "权限",
+ 2 => "权限",
+
+ "ASSIGN_NEW" => "分配新权限",
+ "HOOK_CONDITION" => "Hook/条件",
+ "ID" => "权限ID",
+ "INFO_PAGE" => "用户 '{{name}}' 的权限页",
+ "MANAGE" => "管理权限",
+ "NOTE_READ_ONLY" => "
请注意: 权限是 \"part of the code\" 不能通过界面修改. 如果要添加、删除、编辑权限, 网站维护者需要
进行数据库迁移. ",
+ "PAGE_DESCRIPTION" => "网站的权限列表. 提供编辑和删除权限的工具.",
+ "SUMMARY" => "权限概要",
+ "UPDATE" => "更新权限",
+ "VIA_ROLES" => "权限通过用户了"
+ ],
+
+ "ROLE" => [
+ 1 => "角色",
+ 2 => "角色",
+
+ "ASSIGN_NEW" => "分配角色",
+ "CREATE" => "创建角色",
+ "CREATION_SUCCESSFUL" => "成功创建角色
{{name}} ",
+ "DELETE" => "删除角色",
+ "DELETE_CONFIRM" => "你确定要删除角色
{{name}} ?",
+ "DELETE_DEFAULT" => "你无法删除角色
{{name}} 因为这是新注册用户的默认角色.",
+ "DELETE_YES" => "是, 删除角色",
+ "DELETION_SUCCESSFUL" => "成功删除角色
{{name}} ",
+ "EDIT" => "编辑角色",
+ "HAS_USERS" => "你不能这样做,因为还有用户拥有角色
{{name}} .",
+ "INFO_PAGE" => "{{name}}角色的权限页",
+ "MANAGE" => "管理角色",
+ "NAME" => "名字",
+ "NAME_EXPLAIN" => "请为角色输入名字",
+ "NAME_IN_USE" => "角色名
{{name}} 以存在",
+ "PAGE_DESCRIPTION" => "网站的角色列表. 请管理编辑和删除角色的工具.",
+ "PERMISSIONS_UPDATED" => "角色
{{name}} 的权限更新成功",
+ "SUMMARY" => "角色概要",
+ "UPDATED" => "角色
{{name}} 更新成功"
+ ],
+
+ "SYSTEM_INFO" => [
+ "@TRANSLATION" => "系统信息",
+
+ "DB_NAME" => "数据库",
+ "DB_VERSION" => "数据库版本",
+ "DIRECTORY" => "工程目录",
+ "PHP_VERSION" => "PHP 版本",
+ "SERVER" => "网站服务器",
+ "SPRINKLES" => "加载的 sprinkles",
+ "UF_VERSION" => "UserFrosting 版本",
+ "URL" => "网站根目录"
+ ],
+
+ "TOGGLE_COLUMNS" => "加载列",
+
+ "USER" => [
+ 1 => "用户",
+ 2 => "用户",
+
+ "ADMIN" => [
+ "CHANGE_PASSWORD" => "修改用户密码",
+ "SEND_PASSWORD_LINK" => "发送允许该用户修改密码的链接",
+ "SET_PASSWORD" => "设置用户的密码为"
+ ],
+
+ "ACTIVATE" => "激活用户",
+ "CREATE" => "新建用户",
+ "CREATED" => "用户
{{user_name}} 新建成功",
+ "DELETE" => "删除用户",
+ "DELETE_CONFIRM" => "确定要删除用户
{{name}} ?",
+ "DELETE_YES" => "是, 删除用户",
+ "DISABLE" => "禁用用户",
+ "EDIT" => "编辑用户",
+ "ENABLE" => "启用用户",
+ "INFO_PAGE" => "用户 {{name}} 的信息页",
+ "LATEST" => "最新用户",
+ "PAGE_DESCRIPTION" => "网站用户列表. 提供编辑用户信息, 手动激活用户, 启用/禁用 用户等工具.",
+ "SUMMARY" => "账户概要",
+ "VIEW_ALL" => "浏览所有用户",
+ "WITH_PERMISSION" => "有此权限的用户"
+ ],
+ "X_USER" => [
+ 0 => "没有用户",
+ 1 => "{{plural}} 用户",
+ 2 => "{{plural}} 用户"
+ ]
+];
diff --git a/main/app/sprinkles/admin/routes/activities.php b/main/app/sprinkles/admin/routes/activities.php
new file mode 100755
index 0000000..b324553
--- /dev/null
+++ b/main/app/sprinkles/admin/routes/activities.php
@@ -0,0 +1,19 @@
+group('/activities', function () {
+ $this->get('', 'UserFrosting\Sprinkle\Admin\Controller\ActivityController:pageList')
+ ->setName('uri_activities');
+})->add('authGuard');
+
+$app->group('/api/activities', function () {
+ $this->get('', 'UserFrosting\Sprinkle\Admin\Controller\ActivityController:getList');
+})->add('authGuard');
diff --git a/main/app/sprinkles/admin/routes/admin.php b/main/app/sprinkles/admin/routes/admin.php
new file mode 100755
index 0000000..1a8c31a
--- /dev/null
+++ b/main/app/sprinkles/admin/routes/admin.php
@@ -0,0 +1,23 @@
+group('/dashboard', function () {
+ $this->get('', 'UserFrosting\Sprinkle\Admin\Controller\AdminController:pageDashboard')
+ ->setName('dashboard');
+})->add('authGuard');
+
+$app->group('/api/dashboard', function () {
+ $this->post('/clear-cache', 'UserFrosting\Sprinkle\Admin\Controller\AdminController:clearCache');
+})->add('authGuard');
+
+$app->group('/modals/dashboard', function () {
+ $this->get('/clear-cache', 'UserFrosting\Sprinkle\Admin\Controller\AdminController:getModalConfirmClearCache');
+})->add('authGuard');
diff --git a/main/app/sprinkles/admin/routes/groups.php b/main/app/sprinkles/admin/routes/groups.php
new file mode 100755
index 0000000..e861960
--- /dev/null
+++ b/main/app/sprinkles/admin/routes/groups.php
@@ -0,0 +1,39 @@
+group('/groups', function () {
+ $this->get('', 'UserFrosting\Sprinkle\Admin\Controller\GroupController:pageList')
+ ->setName('uri_groups');
+
+ $this->get('/g/{slug}', 'UserFrosting\Sprinkle\Admin\Controller\GroupController:pageInfo');
+})->add('authGuard');
+
+$app->group('/api/groups', function () {
+ $this->delete('/g/{slug}', 'UserFrosting\Sprinkle\Admin\Controller\GroupController:delete');
+
+ $this->get('', 'UserFrosting\Sprinkle\Admin\Controller\GroupController:getList');
+
+ $this->get('/g/{slug}', 'UserFrosting\Sprinkle\Admin\Controller\GroupController:getInfo');
+
+ $this->get('/g/{slug}/users', 'UserFrosting\Sprinkle\Admin\Controller\GroupController:getUsers');
+
+ $this->post('', 'UserFrosting\Sprinkle\Admin\Controller\GroupController:create');
+
+ $this->put('/g/{slug}', 'UserFrosting\Sprinkle\Admin\Controller\GroupController:updateInfo');
+})->add('authGuard');
+
+$app->group('/modals/groups', function () {
+ $this->get('/confirm-delete', 'UserFrosting\Sprinkle\Admin\Controller\GroupController:getModalConfirmDelete');
+
+ $this->get('/create', 'UserFrosting\Sprinkle\Admin\Controller\GroupController:getModalCreate');
+
+ $this->get('/edit', 'UserFrosting\Sprinkle\Admin\Controller\GroupController:getModalEdit');
+})->add('authGuard');
diff --git a/main/app/sprinkles/admin/routes/permissions.php b/main/app/sprinkles/admin/routes/permissions.php
new file mode 100755
index 0000000..4df04c8
--- /dev/null
+++ b/main/app/sprinkles/admin/routes/permissions.php
@@ -0,0 +1,25 @@
+group('/permissions', function () {
+ $this->get('', 'UserFrosting\Sprinkle\Admin\Controller\PermissionController:pageList')
+ ->setName('uri_permissions');
+
+ $this->get('/p/{id}', 'UserFrosting\Sprinkle\Admin\Controller\PermissionController:pageInfo');
+})->add('authGuard');
+
+$app->group('/api/permissions', function () {
+ $this->get('', 'UserFrosting\Sprinkle\Admin\Controller\PermissionController:getList');
+
+ $this->get('/p/{id}', 'UserFrosting\Sprinkle\Admin\Controller\PermissionController:getInfo');
+
+ $this->get('/p/{id}/users', 'UserFrosting\Sprinkle\Admin\Controller\PermissionController:getUsers');
+})->add('authGuard');
diff --git a/main/app/sprinkles/admin/routes/roles.php b/main/app/sprinkles/admin/routes/roles.php
new file mode 100755
index 0000000..1de12e8
--- /dev/null
+++ b/main/app/sprinkles/admin/routes/roles.php
@@ -0,0 +1,45 @@
+group('/roles', function () {
+ $this->get('', 'UserFrosting\Sprinkle\Admin\Controller\RoleController:pageList')
+ ->setName('uri_roles');
+
+ $this->get('/r/{slug}', 'UserFrosting\Sprinkle\Admin\Controller\RoleController:pageInfo');
+})->add('authGuard');
+
+$app->group('/api/roles', function () {
+ $this->delete('/r/{slug}', 'UserFrosting\Sprinkle\Admin\Controller\RoleController:delete');
+
+ $this->get('', 'UserFrosting\Sprinkle\Admin\Controller\RoleController:getList');
+
+ $this->get('/r/{slug}', 'UserFrosting\Sprinkle\Admin\Controller\RoleController:getInfo');
+
+ $this->get('/r/{slug}/permissions', 'UserFrosting\Sprinkle\Admin\Controller\RoleController:getPermissions');
+
+ $this->get('/r/{slug}/users', 'UserFrosting\Sprinkle\Admin\Controller\RoleController:getUsers');
+
+ $this->post('', 'UserFrosting\Sprinkle\Admin\Controller\RoleController:create');
+
+ $this->put('/r/{slug}', 'UserFrosting\Sprinkle\Admin\Controller\RoleController:updateInfo');
+
+ $this->put('/r/{slug}/{field}', 'UserFrosting\Sprinkle\Admin\Controller\RoleController:updateField');
+})->add('authGuard');
+
+$app->group('/modals/roles', function () {
+ $this->get('/confirm-delete', 'UserFrosting\Sprinkle\Admin\Controller\RoleController:getModalConfirmDelete');
+
+ $this->get('/create', 'UserFrosting\Sprinkle\Admin\Controller\RoleController:getModalCreate');
+
+ $this->get('/edit', 'UserFrosting\Sprinkle\Admin\Controller\RoleController:getModalEdit');
+
+ $this->get('/permissions', 'UserFrosting\Sprinkle\Admin\Controller\RoleController:getModalEditPermissions');
+})->add('authGuard');
diff --git a/main/app/sprinkles/admin/routes/users.php b/main/app/sprinkles/admin/routes/users.php
new file mode 100755
index 0000000..f1b2243
--- /dev/null
+++ b/main/app/sprinkles/admin/routes/users.php
@@ -0,0 +1,51 @@
+group('/users', function () {
+ $this->get('', 'UserFrosting\Sprinkle\Admin\Controller\UserController:pageList')
+ ->setName('uri_users');
+
+ $this->get('/u/{user_name}', 'UserFrosting\Sprinkle\Admin\Controller\UserController:pageInfo');
+})->add('authGuard');
+
+$app->group('/api/users', function () {
+ $this->delete('/u/{user_name}', 'UserFrosting\Sprinkle\Admin\Controller\UserController:delete');
+
+ $this->get('', 'UserFrosting\Sprinkle\Admin\Controller\UserController:getList');
+
+ $this->get('/u/{user_name}', 'UserFrosting\Sprinkle\Admin\Controller\UserController:getInfo');
+
+ $this->get('/u/{user_name}/activities', 'UserFrosting\Sprinkle\Admin\Controller\UserController:getActivities');
+
+ $this->get('/u/{user_name}/roles', 'UserFrosting\Sprinkle\Admin\Controller\UserController:getRoles');
+
+ $this->get('/u/{user_name}/permissions', 'UserFrosting\Sprinkle\Admin\Controller\UserController:getPermissions');
+
+ $this->post('', 'UserFrosting\Sprinkle\Admin\Controller\UserController:create');
+
+ $this->post('/u/{user_name}/password-reset', 'UserFrosting\Sprinkle\Admin\Controller\UserController:createPasswordReset');
+
+ $this->put('/u/{user_name}', 'UserFrosting\Sprinkle\Admin\Controller\UserController:updateInfo');
+
+ $this->put('/u/{user_name}/{field}', 'UserFrosting\Sprinkle\Admin\Controller\UserController:updateField');
+})->add('authGuard');
+
+$app->group('/modals/users', function () {
+ $this->get('/confirm-delete', 'UserFrosting\Sprinkle\Admin\Controller\UserController:getModalConfirmDelete');
+
+ $this->get('/create', 'UserFrosting\Sprinkle\Admin\Controller\UserController:getModalCreate');
+
+ $this->get('/edit', 'UserFrosting\Sprinkle\Admin\Controller\UserController:getModalEdit');
+
+ $this->get('/password', 'UserFrosting\Sprinkle\Admin\Controller\UserController:getModalEditPassword');
+
+ $this->get('/roles', 'UserFrosting\Sprinkle\Admin\Controller\UserController:getModalEditRoles');
+})->add('authGuard');
diff --git a/main/app/sprinkles/admin/schema/requests/group/create.yaml b/main/app/sprinkles/admin/schema/requests/group/create.yaml
new file mode 100755
index 0000000..8f5261c
--- /dev/null
+++ b/main/app/sprinkles/admin/schema/requests/group/create.yaml
@@ -0,0 +1,35 @@
+---
+name:
+ validators:
+ required:
+ label: "&NAME"
+ message: VALIDATE.REQUIRED
+ length:
+ label: "&NAME"
+ min: 1
+ max: 255
+ message: VALIDATE.LENGTH_RANGE
+ transformations:
+ - trim
+slug:
+ validators:
+ required:
+ label: "&SLUG"
+ message: VALIDATE.REQUIRED
+ length:
+ label: "&SLUG"
+ min: 1
+ max: 255
+ message: VALIDATE.LENGTH_RANGE
+ transformations:
+ - trim
+icon:
+ validators:
+ length:
+ label: "&ICON"
+ min: 1
+ max: 100
+ message: VALIDATE.LENGTH_RANGE
+ required:
+ message: VALIDATE.REQUIRED
+description:
diff --git a/main/app/sprinkles/admin/schema/requests/group/edit-info.yaml b/main/app/sprinkles/admin/schema/requests/group/edit-info.yaml
new file mode 100755
index 0000000..6aa3f28
--- /dev/null
+++ b/main/app/sprinkles/admin/schema/requests/group/edit-info.yaml
@@ -0,0 +1,27 @@
+---
+name:
+ validators:
+ length:
+ label: "&NAME"
+ min: 1
+ max: 255
+ message: VALIDATE.LENGTH_RANGE
+ transformations:
+ - trim
+slug:
+ validators:
+ length:
+ label: "&SLUG"
+ min: 1
+ max: 255
+ message: VALIDATE.LENGTH_RANGE
+ transformations:
+ - trim
+icon:
+ validators:
+ length:
+ label: "&ICON"
+ min: 1
+ max: 100
+ message: VALIDATE.LENGTH_RANGE
+description:
diff --git a/main/app/sprinkles/admin/schema/requests/group/get-by-slug.yaml b/main/app/sprinkles/admin/schema/requests/group/get-by-slug.yaml
new file mode 100755
index 0000000..2aa41b5
--- /dev/null
+++ b/main/app/sprinkles/admin/schema/requests/group/get-by-slug.yaml
@@ -0,0 +1,6 @@
+---
+slug:
+ validators:
+ required:
+ label: "&SLUG"
+ message: VALIDATE.REQUIRED
diff --git a/main/app/sprinkles/admin/schema/requests/role/create.yaml b/main/app/sprinkles/admin/schema/requests/role/create.yaml
new file mode 100755
index 0000000..8004184
--- /dev/null
+++ b/main/app/sprinkles/admin/schema/requests/role/create.yaml
@@ -0,0 +1,26 @@
+---
+name:
+ validators:
+ required:
+ label: "&NAME"
+ message: VALIDATE.REQUIRED
+ length:
+ label: "&NAME"
+ min: 1
+ max: 255
+ message: VALIDATE.LENGTH_RANGE
+ transformations:
+ - trim
+slug:
+ validators:
+ required:
+ label: "&SLUG"
+ message: VALIDATE.REQUIRED
+ length:
+ label: "&SLUG"
+ min: 1
+ max: 255
+ message: VALIDATE.LENGTH_RANGE
+ transformations:
+ - trim
+description:
diff --git a/main/app/sprinkles/admin/schema/requests/role/edit-field.yaml b/main/app/sprinkles/admin/schema/requests/role/edit-field.yaml
new file mode 100755
index 0000000..05c1b2d
--- /dev/null
+++ b/main/app/sprinkles/admin/schema/requests/role/edit-field.yaml
@@ -0,0 +1,24 @@
+---
+name:
+ validators:
+ length:
+ label: "&NAME"
+ min: 1
+ max: 255
+ message: VALIDATE.LENGTH_RANGE
+ transformations:
+ - trim
+slug:
+ validators:
+ length:
+ label: "&SLUG"
+ min: 1
+ max: 255
+ message: VALIDATE.LENGTH_RANGE
+ transformations:
+ - trim
+description:
+permissions:
+ validators:
+ array:
+ message: VALIDATE.ARRAY
diff --git a/main/app/sprinkles/admin/schema/requests/role/edit-info.yaml b/main/app/sprinkles/admin/schema/requests/role/edit-info.yaml
new file mode 100755
index 0000000..1fa36c8
--- /dev/null
+++ b/main/app/sprinkles/admin/schema/requests/role/edit-info.yaml
@@ -0,0 +1,20 @@
+---
+name:
+ validators:
+ length:
+ label: "&NAME"
+ min: 1
+ max: 255
+ message: VALIDATE.LENGTH_RANGE
+ transformations:
+ - trim
+slug:
+ validators:
+ length:
+ label: "&SLUG"
+ min: 1
+ max: 255
+ message: VALIDATE.LENGTH_RANGE
+ transformations:
+ - trim
+description:
diff --git a/main/app/sprinkles/admin/schema/requests/role/get-by-slug.yaml b/main/app/sprinkles/admin/schema/requests/role/get-by-slug.yaml
new file mode 100755
index 0000000..2aa41b5
--- /dev/null
+++ b/main/app/sprinkles/admin/schema/requests/role/get-by-slug.yaml
@@ -0,0 +1,6 @@
+---
+slug:
+ validators:
+ required:
+ label: "&SLUG"
+ message: VALIDATE.REQUIRED
diff --git a/main/app/sprinkles/admin/schema/requests/user/create.yaml b/main/app/sprinkles/admin/schema/requests/user/create.yaml
new file mode 100755
index 0000000..7e575bc
--- /dev/null
+++ b/main/app/sprinkles/admin/schema/requests/user/create.yaml
@@ -0,0 +1,72 @@
+---
+user_name:
+ validators:
+ length:
+ label: "&USERNAME"
+ min: 1
+ max: 50
+ message: VALIDATE.LENGTH_RANGE
+ no_leading_whitespace:
+ label: "&USERNAME"
+ message: VALIDATE.NO_LEAD_WS
+ no_trailing_whitespace:
+ label: "&USERNAME"
+ message: VALIDATE.NO_TRAIL_WS
+ required:
+ label: "&USERNAME"
+ message: VALIDATE.REQUIRED
+ username:
+ label: "&USERNAME"
+ message: VALIDATE.USERNAME
+first_name:
+ validators:
+ length:
+ label: "&FIRST_NAME"
+ min: 1
+ max: 20
+ message: VALIDATE.LENGTH_RANGE
+ required:
+ label: "&FIRST_NAME"
+ message: VALIDATE.REQUIRED
+ transformations:
+ - trim
+last_name:
+ validators:
+ length:
+ label: "&LAST_NAME"
+ min: 1
+ max: 30
+ message: VALIDATE.LENGTH_RANGE
+ transformations:
+ - trim
+email:
+ validators:
+ required:
+ label: "&EMAIL"
+ message: VALIDATE.REQUIRED
+ length:
+ label: "&EMAIL"
+ min: 1
+ max: 150
+ message: VALIDATE.LENGTH_RANGE
+ email:
+ message: VALIDATE.INVALID_EMAIL
+locale:
+ default: en_US
+ validators:
+ required:
+ label: "&LOCALE"
+ domain: server
+ message: VALIDATE.REQUIRED
+ length:
+ label: "&LOCALE"
+ min: 1
+ max: 10
+ domain: server
+ message: VALIDATE.LENGTH_RANGE
+group_id:
+ validators:
+ integer:
+ label: "&GROUP"
+ domain: server
+ message: VALIDATE.INTEGER
diff --git a/main/app/sprinkles/admin/schema/requests/user/edit-field.yaml b/main/app/sprinkles/admin/schema/requests/user/edit-field.yaml
new file mode 100755
index 0000000..ab3b3aa
--- /dev/null
+++ b/main/app/sprinkles/admin/schema/requests/user/edit-field.yaml
@@ -0,0 +1,60 @@
+---
+first_name:
+ validators:
+ length:
+ label: "&FIRST_NAME"
+ min: 1
+ max: 20
+ message: VALIDATE.LENGTH_RANGE
+last_name:
+ validators:
+ length:
+ label: "&LAST_NAME"
+ min: 1
+ max: 30
+ message: VALIDATE.LENGTH_RANGE
+email:
+ validators:
+ length:
+ label: "&EMAIL"
+ min: 1
+ max: 150
+ message: VALIDATE.LENGTH_RANGE
+ email:
+ message: VALIDATE.INVALID_EMAIL
+locale:
+ validators:
+ length:
+ label: "&LOCALE"
+ min: 1
+ max: 10
+ message: VALIDATE.LENGTH_RANGE
+group_id:
+ validators:
+ integer:
+ message: VALIDATE.INTEGER
+flag_enabled:
+ validators:
+ member_of:
+ values:
+ - '0'
+ - '1'
+ message: VALIDATE.BOOLEAN
+flag_verified:
+ validators:
+ member_of:
+ values:
+ - '0'
+ - '1'
+ message: VALIDATE.BOOLEAN
+password:
+ validators:
+ length:
+ label: "&PASSWORD"
+ min: 12
+ max: 100
+ message: VALIDATE.LENGTH_RANGE
+roles:
+ validators:
+ array:
+ message: VALIDATE.ARRAY
diff --git a/main/app/sprinkles/admin/schema/requests/user/edit-info.yaml b/main/app/sprinkles/admin/schema/requests/user/edit-info.yaml
new file mode 100755
index 0000000..30ae920
--- /dev/null
+++ b/main/app/sprinkles/admin/schema/requests/user/edit-info.yaml
@@ -0,0 +1,36 @@
+---
+first_name:
+ validators:
+ length:
+ label: "&FIRST_NAME"
+ min: 1
+ max: 20
+ message: VALIDATE.LENGTH_RANGE
+last_name:
+ validators:
+ length:
+ label: "&LAST_NAME"
+ min: 1
+ max: 30
+ message: VALIDATE.LENGTH_RANGE
+email:
+ validators:
+ length:
+ label: "&EMAIL"
+ min: 1
+ max: 150
+ message: VALIDATE.LENGTH_RANGE
+ email:
+ message: VALIDATE.INVALID_EMAIL
+locale:
+ validators:
+ length:
+ label: "&LOCALE"
+ min: 1
+ max: 10
+ message: VALIDATE.LENGTH_RANGE
+group_id:
+ validators:
+ integer:
+ label: "&GROUP"
+ message: VALIDATE.INTEGER
diff --git a/main/app/sprinkles/admin/schema/requests/user/edit-password.yaml b/main/app/sprinkles/admin/schema/requests/user/edit-password.yaml
new file mode 100755
index 0000000..1d751ff
--- /dev/null
+++ b/main/app/sprinkles/admin/schema/requests/user/edit-password.yaml
@@ -0,0 +1,30 @@
+---
+value:
+ validators:
+ required:
+ domain: client
+ label: "&PASSWORD"
+ message: VALIDATE.REQUIRED
+ length:
+ domain: client
+ label: "&PASSWORD"
+ min: 12
+ max: 100
+ message: VALIDATE.LENGTH_RANGE
+passwordc:
+ validators:
+ required:
+ domain: client
+ label: "&PASSWORD.CONFIRM"
+ message: VALIDATE.REQUIRED
+ matches:
+ domain: client
+ field: value
+ label: "&PASSWORD.CONFIRM"
+ message: VALIDATE.PASSWORD_MISMATCH
+ length:
+ domain: client
+ label: "&PASSWORD.CONFIRM"
+ min: 12
+ max: 100
+ message: VALIDATE.LENGTH_RANGE
diff --git a/main/app/sprinkles/admin/schema/requests/user/get-by-username.yaml b/main/app/sprinkles/admin/schema/requests/user/get-by-username.yaml
new file mode 100755
index 0000000..97170dd
--- /dev/null
+++ b/main/app/sprinkles/admin/schema/requests/user/get-by-username.yaml
@@ -0,0 +1,6 @@
+---
+user_name:
+ validators:
+ required:
+ label: "&USERNAME"
+ message: VALIDATE.REQUIRED
diff --git a/main/app/sprinkles/admin/src/Admin.php b/main/app/sprinkles/admin/src/Admin.php
new file mode 100755
index 0000000..8a6dcc1
--- /dev/null
+++ b/main/app/sprinkles/admin/src/Admin.php
@@ -0,0 +1,20 @@
+getQueryParams();
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'uri_activities')) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ $sprunje = $classMapper->createInstance('activity_sprunje', $classMapper, $params);
+ $sprunje->extendQuery(function ($query) {
+ return $query->with('user');
+ });
+
+ // Be careful how you consume this data - it has not been escaped and contains untrusted user-supplied content.
+ // For example, if you plan to insert it into an HTML DOM, you must escape it on the client side (or use client-side templating).
+ return $sprunje->toResponse($response);
+ }
+
+ /**
+ * Renders the activity listing page.
+ *
+ * This page renders a table of user activities.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function pageList($request, $response, $args)
+ {
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'uri_activities')) {
+ throw new ForbiddenException();
+ }
+
+ return $this->ci->view->render($response, 'pages/activities.html.twig');
+ }
+}
diff --git a/main/app/sprinkles/admin/src/Controller/AdminController.php b/main/app/sprinkles/admin/src/Controller/AdminController.php
new file mode 100755
index 0000000..da4da8a
--- /dev/null
+++ b/main/app/sprinkles/admin/src/Controller/AdminController.php
@@ -0,0 +1,150 @@
+ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'uri_dashboard')) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ // Probably a better way to do this
+ $users = $classMapper->staticMethod('user', 'orderBy', 'created_at', 'desc')
+ ->take(8)
+ ->get();
+
+ // Transform the `create_at` date in "x days ago" type of string
+ $users->transform(function ($item, $key) {
+ $item->registered = Carbon::parse($item->created_at)->diffForHumans();
+ return $item;
+ });
+
+ /** @var Config $config */
+ $config = $this->ci->config;
+
+ /** @var Config $config */
+ $cache = $this->ci->cache;
+
+ // Get each sprinkle db version
+ $sprinkles = $this->ci->sprinkleManager->getSprinkleNames();
+
+ return $this->ci->view->render($response, 'pages/dashboard.html.twig', [
+ 'counter' => [
+ 'users' => $classMapper->staticMethod('user', 'count'),
+ 'roles' => $classMapper->staticMethod('role', 'count'),
+ 'groups' => $classMapper->staticMethod('group', 'count')
+ ],
+ 'info' => [
+ 'version' => [
+ 'UF' => \UserFrosting\VERSION,
+ 'php' => phpversion(),
+ 'database' => EnvironmentInfo::database()
+ ],
+ 'database' => [
+ 'name' => $config['db.default.database']
+ ],
+ 'environment' => $this->ci->environment,
+ 'path' => [
+ 'project' => \UserFrosting\ROOT_DIR
+ ]
+ ],
+ 'sprinkles' => $sprinkles,
+ 'users' => $users
+ ]);
+ }
+
+ /**
+ * Clear the site cache.
+ *
+ * This route requires authentication.
+ * Request type: POST
+ */
+ public function clearCache($request, $response, $args)
+ {
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'clear_cache')) {
+ throw new ForbiddenException();
+ }
+
+ // Flush cache
+ $this->ci->cache->flush();
+
+ /** @var MessageStream $ms */
+ $ms = $this->ci->alerts;
+
+ $ms->addMessageTranslated('success', 'CACHE.CLEARED');
+
+ return $response->withStatus(200);
+ }
+
+ /**
+ * Renders the modal form to confirm cache deletion.
+ *
+ * This does NOT render a complete page. Instead, it renders the HTML for the modal, which can be embedded in other pages.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getModalConfirmClearCache($request, $response, $args)
+ {
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'clear_cache')) {
+ throw new ForbiddenException();
+ }
+
+ return $this->ci->view->render($response, 'modals/confirm-clear-cache.html.twig', [
+ 'form' => [
+ 'action' => 'api/dashboard/clear-cache',
+ ]
+ ]);
+ }
+}
diff --git a/main/app/sprinkles/admin/src/Controller/GroupController.php b/main/app/sprinkles/admin/src/Controller/GroupController.php
new file mode 100755
index 0000000..7ca94b1
--- /dev/null
+++ b/main/app/sprinkles/admin/src/Controller/GroupController.php
@@ -0,0 +1,725 @@
+getParsedBody();
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'create_group')) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\MessageStream $ms */
+ $ms = $this->ci->alerts;
+
+ // Load the request schema
+ $schema = new RequestSchema('schema://requests/group/create.yaml');
+
+ // Whitelist and set parameter defaults
+ $transformer = new RequestDataTransformer($schema);
+ $data = $transformer->transform($params);
+
+ $error = false;
+
+ // Validate request data
+ $validator = new ServerSideValidator($schema, $this->ci->translator);
+ if (!$validator->validate($data)) {
+ $ms->addValidationErrors($validator);
+ $error = true;
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ // Check if name or slug already exists
+ if ($classMapper->staticMethod('group', 'where', 'name', $data['name'])->first()) {
+ $ms->addMessageTranslated('danger', 'GROUP.NAME.IN_USE', $data);
+ $error = true;
+ }
+
+ if ($classMapper->staticMethod('group', 'where', 'slug', $data['slug'])->first()) {
+ $ms->addMessageTranslated('danger', 'GROUP.SLUG.IN_USE', $data);
+ $error = true;
+ }
+
+ if ($error) {
+ return $response->withStatus(400);
+ }
+
+ /** @var UserFrosting\Config\Config $config */
+ $config = $this->ci->config;
+
+ // All checks passed! log events/activities and create group
+ // Begin transaction - DB will be rolled back if an exception occurs
+ Capsule::transaction( function() use ($classMapper, $data, $ms, $config, $currentUser) {
+ // Create the group
+ $group = $classMapper->createInstance('group', $data);
+
+ // Store new group to database
+ $group->save();
+
+ // Create activity record
+ $this->ci->userActivityLogger->info("User {$currentUser->user_name} created group {$group->name}.", [
+ 'type' => 'group_create',
+ 'user_id' => $currentUser->id
+ ]);
+
+ $ms->addMessageTranslated('success', 'GROUP.CREATION_SUCCESSFUL', $data);
+ });
+
+ return $response->withStatus(200);
+ }
+
+ /**
+ * Processes the request to delete an existing group.
+ *
+ * Deletes the specified group.
+ * Before doing so, checks that:
+ * 1. The user has permission to delete this group;
+ * 2. The group is not currently set as the default for new users;
+ * 3. The group is empty (does not have any users);
+ * 4. The submitted data is valid.
+ * This route requires authentication (and should generally be limited to admins or the root user).
+ * Request type: DELETE
+ */
+ public function delete($request, $response, $args)
+ {
+ $group = $this->getGroupFromParams($args);
+
+ // If the group doesn't exist, return 404
+ if (!$group) {
+ throw new NotFoundException($request, $response);
+ }
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'delete_group', [
+ 'group' => $group
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Config\Config $config */
+ $config = $this->ci->config;
+
+ // Check that we are not deleting the default group
+ // Need to use loose comparison for now, because some DBs return `id` as a string
+ if ($group->slug == $config['site.registration.user_defaults.group']) {
+ $e = new BadRequestException();
+ $e->addUserMessage('GROUP.DELETE_DEFAULT', $group->toArray());
+ throw $e;
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ // Check if there are any users in this group
+ $countGroupUsers = $classMapper->staticMethod('user', 'where', 'group_id', $group->id)->count();
+ if ($countGroupUsers > 0) {
+ $e = new BadRequestException();
+ $e->addUserMessage('GROUP.NOT_EMPTY', $group->toArray());
+ throw $e;
+ }
+
+ $groupName = $group->name;
+
+ // Begin transaction - DB will be rolled back if an exception occurs
+ Capsule::transaction( function() use ($group, $groupName, $currentUser) {
+ $group->delete();
+ unset($group);
+
+ // Create activity record
+ $this->ci->userActivityLogger->info("User {$currentUser->user_name} deleted group {$groupName}.", [
+ 'type' => 'group_delete',
+ 'user_id' => $currentUser->id
+ ]);
+ });
+
+ /** @var UserFrosting\Sprinkle\Core\MessageStream $ms */
+ $ms = $this->ci->alerts;
+
+ $ms->addMessageTranslated('success', 'GROUP.DELETION_SUCCESSFUL', [
+ 'name' => $groupName
+ ]);
+
+ return $response->withStatus(200);
+ }
+
+ /**
+ * Returns info for a single group.
+ *
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getInfo($request, $response, $args)
+ {
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'uri_groups')) {
+ throw new ForbiddenException();
+ }
+
+ $slug = $args['slug'];
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ $group = $classMapper->staticMethod('group', 'where', 'slug', $slug)->first();
+
+ // If the group doesn't exist, return 404
+ if (!$group) {
+ throw new NotFoundException($request, $response);
+ }
+
+ // Get group
+ $result = $group->toArray();
+
+ // Be careful how you consume this data - it has not been escaped and contains untrusted user-supplied content.
+ // For example, if you plan to insert it into an HTML DOM, you must escape it on the client side (or use client-side templating).
+ return $response->withJson($result, 200, JSON_PRETTY_PRINT);
+ }
+
+ /**
+ * Returns a list of Groups
+ *
+ * Generates a list of groups, optionally paginated, sorted and/or filtered.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getList($request, $response, $args)
+ {
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'uri_groups')) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ $sprunje = $classMapper->createInstance('group_sprunje', $classMapper, $params);
+
+ // Be careful how you consume this data - it has not been escaped and contains untrusted user-supplied content.
+ // For example, if you plan to insert it into an HTML DOM, you must escape it on the client side (or use client-side templating).
+ return $sprunje->toResponse($response);
+ }
+
+ public function getModalConfirmDelete($request, $response, $args)
+ {
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ $group = $this->getGroupFromParams($params);
+
+ // If the group no longer exists, forward to main group listing page
+ if (!$group) {
+ throw new NotFoundException($request, $response);
+ }
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'delete_group', [
+ 'group' => $group
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ // Check if there are any users in this group
+ $countGroupUsers = $classMapper->staticMethod('user', 'where', 'group_id', $group->id)->count();
+ if ($countGroupUsers > 0) {
+ $e = new BadRequestException();
+ $e->addUserMessage('GROUP.NOT_EMPTY', $group->toArray());
+ throw $e;
+ }
+
+ return $this->ci->view->render($response, 'modals/confirm-delete-group.html.twig', [
+ 'group' => $group,
+ 'form' => [
+ 'action' => "api/groups/g/{$group->slug}",
+ ]
+ ]);
+ }
+
+ /**
+ * Renders the modal form for creating a new group.
+ *
+ * This does NOT render a complete page. Instead, it renders the HTML for the modal, which can be embedded in other pages.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getModalCreate($request, $response, $args)
+ {
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ /** @var UserFrosting\I18n\MessageTranslator $translator */
+ $translator = $this->ci->translator;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'create_group')) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ // Create a dummy group to prepopulate fields
+ $group = $classMapper->createInstance('group', []);
+
+ $group->icon = 'fa fa-user';
+
+ $fieldNames = ['name', 'slug', 'icon', 'description'];
+ $fields = [
+ 'hidden' => [],
+ 'disabled' => []
+ ];
+
+ // Load validation rules
+ $schema = new RequestSchema('schema://requests/group/create.yaml');
+ $validator = new JqueryValidationAdapter($schema, $this->ci->translator);
+
+ return $this->ci->view->render($response, 'modals/group.html.twig', [
+ 'group' => $group,
+ 'form' => [
+ 'action' => 'api/groups',
+ 'method' => 'POST',
+ 'fields' => $fields,
+ 'submit_text' => $translator->translate('CREATE')
+ ],
+ 'page' => [
+ 'validators' => $validator->rules('json', false)
+ ]
+ ]);
+ }
+
+ /**
+ * Renders the modal form for editing an existing group.
+ *
+ * This does NOT render a complete page. Instead, it renders the HTML for the modal, which can be embedded in other pages.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getModalEdit($request, $response, $args)
+ {
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ $group = $this->getGroupFromParams($params);
+
+ // If the group doesn't exist, return 404
+ if (!$group) {
+ throw new NotFoundException($request, $response);
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ /** @var UserFrosting\I18n\MessageTranslator $translator */
+ $translator = $this->ci->translator;
+
+ // Access-controlled resource - check that currentUser has permission to edit basic fields "name", "slug", "icon", "description" for this group
+ $fieldNames = ['name', 'slug', 'icon', 'description'];
+ if (!$authorizer->checkAccess($currentUser, 'update_group_field', [
+ 'group' => $group,
+ 'fields' => $fieldNames
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ // Generate form
+ $fields = [
+ 'hidden' => [],
+ 'disabled' => []
+ ];
+
+ // Load validation rules
+ $schema = new RequestSchema('schema://requests/group/edit-info.yaml');
+ $validator = new JqueryValidationAdapter($schema, $translator);
+
+ return $this->ci->view->render($response, 'modals/group.html.twig', [
+ 'group' => $group,
+ 'form' => [
+ 'action' => "api/groups/g/{$group->slug}",
+ 'method' => 'PUT',
+ 'fields' => $fields,
+ 'submit_text' => $translator->translate('UPDATE')
+ ],
+ 'page' => [
+ 'validators' => $validator->rules('json', false)
+ ]
+ ]);
+ }
+
+ public function getUsers($request, $response, $args)
+ {
+ $group = $this->getGroupFromParams($args);
+
+ // If the group no longer exists, forward to main group listing page
+ if (!$group) {
+ throw new NotFoundException($request, $response);
+ }
+
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'view_group_field', [
+ 'group' => $group,
+ 'property' => 'users'
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ $sprunje = $classMapper->createInstance('user_sprunje', $classMapper, $params);
+ $sprunje->extendQuery(function ($query) use ($group) {
+ return $query->where('group_id', $group->id);
+ });
+
+ // Be careful how you consume this data - it has not been escaped and contains untrusted user-supplied content.
+ // For example, if you plan to insert it into an HTML DOM, you must escape it on the client side (or use client-side templating).
+ return $sprunje->toResponse($response);
+ }
+
+ /**
+ * Renders a page displaying a group's information, in read-only mode.
+ *
+ * This checks that the currently logged-in user has permission to view the requested group's info.
+ * It checks each field individually, showing only those that you have permission to view.
+ * This will also try to show buttons for deleting, and editing the group.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function pageInfo($request, $response, $args)
+ {
+ $group = $this->getGroupFromParams($args);
+
+ // If the group no longer exists, forward to main group listing page
+ if (!$group) {
+ $redirectPage = $this->ci->router->pathFor('uri_groups');
+ return $response->withRedirect($redirectPage, 404);
+ }
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'uri_group', [
+ 'group' => $group
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ // Determine fields that currentUser is authorized to view
+ $fieldNames = ['name', 'slug', 'icon', 'description'];
+
+ // Generate form
+ $fields = [
+ 'hidden' => []
+ ];
+
+ foreach ($fieldNames as $field) {
+ if (!$authorizer->checkAccess($currentUser, 'view_group_field', [
+ 'group' => $group,
+ 'property' => $field
+ ])) {
+ $fields['hidden'][] = $field;
+ }
+ }
+
+ // Determine buttons to display
+ $editButtons = [
+ 'hidden' => []
+ ];
+
+ if (!$authorizer->checkAccess($currentUser, 'update_group_field', [
+ 'group' => $group,
+ 'fields' => ['name', 'slug', 'icon', 'description']
+ ])) {
+ $editButtons['hidden'][] = 'edit';
+ }
+
+ if (!$authorizer->checkAccess($currentUser, 'delete_group', [
+ 'group' => $group
+ ])) {
+ $editButtons['hidden'][] = 'delete';
+ }
+
+ return $this->ci->view->render($response, 'pages/group.html.twig', [
+ 'group' => $group,
+ 'fields' => $fields,
+ 'tools' => $editButtons
+ ]);
+ }
+
+ /**
+ * Renders the group listing page.
+ *
+ * This page renders a table of groups, with dropdown menus for admin actions for each group.
+ * Actions typically include: edit group, delete group.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function pageList($request, $response, $args)
+ {
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'uri_groups')) {
+ throw new ForbiddenException();
+ }
+
+ return $this->ci->view->render($response, 'pages/groups.html.twig');
+ }
+
+ /**
+ * Processes the request to update an existing group's details.
+ *
+ * Processes the request from the group update form, checking that:
+ * 1. The group name/slug are not already in use;
+ * 2. The user has the necessary permissions to update the posted field(s);
+ * 3. The submitted data is valid.
+ * This route requires authentication (and should generally be limited to admins or the root user).
+ * Request type: PUT
+ * @see getModalGroupEdit
+ */
+ public function updateInfo($request, $response, $args)
+ {
+ // Get the group based on slug in URL
+ $group = $this->getGroupFromParams($args);
+
+ if (!$group) {
+ throw new NotFoundException($request, $response);
+ }
+
+ /** @var UserFrosting\Config\Config $config */
+ $config = $this->ci->config;
+
+ // Get PUT parameters: (name, slug, icon, description)
+ $params = $request->getParsedBody();
+
+ /** @var UserFrosting\Sprinkle\Core\MessageStream $ms */
+ $ms = $this->ci->alerts;
+
+ // Load the request schema
+ $schema = new RequestSchema('schema://requests/group/edit-info.yaml');
+
+ // Whitelist and set parameter defaults
+ $transformer = new RequestDataTransformer($schema);
+ $data = $transformer->transform($params);
+
+ $error = false;
+
+ // Validate request data
+ $validator = new ServerSideValidator($schema, $this->ci->translator);
+ if (!$validator->validate($data)) {
+ $ms->addValidationErrors($validator);
+ $error = true;
+ }
+
+ // Determine targeted fields
+ $fieldNames = [];
+ foreach ($data as $name => $value) {
+ $fieldNames[] = $name;
+ }
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled resource - check that currentUser has permission to edit submitted fields for this group
+ if (!$authorizer->checkAccess($currentUser, 'update_group_field', [
+ 'group' => $group,
+ 'fields' => array_values(array_unique($fieldNames))
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ // Check if name or slug already exists
+ if (
+ isset($data['name']) &&
+ $data['name'] != $group->name &&
+ $classMapper->staticMethod('group', 'where', 'name', $data['name'])->first()
+ ) {
+ $ms->addMessageTranslated('danger', 'GROUP.NAME.IN_USE', $data);
+ $error = true;
+ }
+
+ if (
+ isset($data['slug']) &&
+ $data['slug'] != $group->slug &&
+ $classMapper->staticMethod('group', 'where', 'slug', $data['slug'])->first()
+ ) {
+ $ms->addMessageTranslated('danger', 'GROUP.SLUG.IN_USE', $data);
+ $error = true;
+ }
+
+ if ($error) {
+ return $response->withStatus(400);
+ }
+
+ // Begin transaction - DB will be rolled back if an exception occurs
+ Capsule::transaction( function() use ($data, $group, $currentUser) {
+ // Update the group and generate success messages
+ foreach ($data as $name => $value) {
+ if ($value != $group->$name) {
+ $group->$name = $value;
+ }
+ }
+
+ $group->save();
+
+ // Create activity record
+ $this->ci->userActivityLogger->info("User {$currentUser->user_name} updated details for group {$group->name}.", [
+ 'type' => 'group_update_info',
+ 'user_id' => $currentUser->id
+ ]);
+ });
+
+ $ms->addMessageTranslated('success', 'GROUP.UPDATE', [
+ 'name' => $group->name
+ ]);
+
+ return $response->withStatus(200);
+ }
+
+ protected function getGroupFromParams($params)
+ {
+ // Load the request schema
+ $schema = new RequestSchema('schema://requests/group/get-by-slug.yaml');
+
+ // Whitelist and set parameter defaults
+ $transformer = new RequestDataTransformer($schema);
+ $data = $transformer->transform($params);
+
+ // Validate, and throw exception on validation errors.
+ $validator = new ServerSideValidator($schema, $this->ci->translator);
+ if (!$validator->validate($data)) {
+ // TODO: encapsulate the communication of error messages from ServerSideValidator to the BadRequestException
+ $e = new BadRequestException();
+ foreach ($validator->errors() as $idx => $field) {
+ foreach($field as $eidx => $error) {
+ $e->addUserMessage($error);
+ }
+ }
+ throw $e;
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ // Get the group
+ $group = $classMapper->staticMethod('group', 'where', 'slug', $data['slug'])
+ ->first();
+
+ return $group;
+ }
+}
diff --git a/main/app/sprinkles/admin/src/Controller/PermissionController.php b/main/app/sprinkles/admin/src/Controller/PermissionController.php
new file mode 100755
index 0000000..f3e93ce
--- /dev/null
+++ b/main/app/sprinkles/admin/src/Controller/PermissionController.php
@@ -0,0 +1,206 @@
+ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'uri_permissions')) {
+ throw new ForbiddenException();
+ }
+
+ $permissionId = $args['id'];
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ $permission = $classMapper->staticMethod('permission', 'find', $permissionId);
+
+ // If the permission doesn't exist, return 404
+ if (!$permission) {
+ throw new NotFoundException($request, $response);
+ }
+
+ // Get permission
+ $result = $permission->load('users')->toArray();
+
+ // Be careful how you consume this data - it has not been escaped and contains untrusted user-supplied content.
+ // For example, if you plan to insert it into an HTML DOM, you must escape it on the client side (or use client-side templating).
+ return $response->withJson($result, 200, JSON_PRETTY_PRINT);
+ }
+
+ /**
+ * Returns a list of Permissions
+ *
+ * Generates a list of permissions, optionally paginated, sorted and/or filtered.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getList($request, $response, $args)
+ {
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'uri_permissions')) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ $sprunje = $classMapper->createInstance('permission_sprunje', $classMapper, $params);
+
+ // Be careful how you consume this data - it has not been escaped and contains untrusted user-supplied content.
+ // For example, if you plan to insert it into an HTML DOM, you must escape it on the client side (or use client-side templating).
+ return $sprunje->toResponse($response);
+ }
+
+ /**
+ * Returns a list of Users for a specified Permission.
+ *
+ * Generates a list of users, optionally paginated, sorted and/or filtered.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getUsers($request, $response, $args)
+ {
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'uri_permissions')) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ $params['permission_id'] = $args['id'];
+
+ $sprunje = $classMapper->createInstance('permission_user_sprunje', $classMapper, $params);
+
+ $response = $sprunje->toResponse($response);
+
+ // Be careful how you consume this data - it has not been escaped and contains untrusted user-supplied content.
+ // For example, if you plan to insert it into an HTML DOM, you must escape it on the client side (or use client-side templating).
+ return $response;
+ }
+
+ /**
+ * Renders a page displaying a permission's information, in read-only mode.
+ *
+ * This checks that the currently logged-in user has permission to view permissions.
+ * Note that permissions cannot be modified through the interface. This is because
+ * permissions are highly coupled to the code and should only be modified by developers.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function pageInfo($request, $response, $args)
+ {
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'uri_permissions')) {
+ throw new ForbiddenException();
+ }
+
+ $permissionId = $args['id'];
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ $permission = $classMapper->staticMethod('permission', 'find', $permissionId);
+
+ // If the permission doesn't exist, return 404
+ if (!$permission) {
+ throw new NotFoundException($request, $response);
+ }
+
+ return $this->ci->view->render($response, 'pages/permission.html.twig', [
+ 'permission' => $permission
+ ]);
+ }
+
+ /**
+ * Renders the permission listing page.
+ *
+ * This page renders a table of permissions, with dropdown menus for admin actions for each permission.
+ * Actions typically include: edit permission, delete permission.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function pageList($request, $response, $args)
+ {
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'uri_permissions')) {
+ throw new ForbiddenException();
+ }
+
+ return $this->ci->view->render($response, 'pages/permissions.html.twig');
+ }
+}
diff --git a/main/app/sprinkles/admin/src/Controller/RoleController.php b/main/app/sprinkles/admin/src/Controller/RoleController.php
new file mode 100755
index 0000000..ab86c88
--- /dev/null
+++ b/main/app/sprinkles/admin/src/Controller/RoleController.php
@@ -0,0 +1,930 @@
+getParsedBody();
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'create_role')) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\MessageStream $ms */
+ $ms = $this->ci->alerts;
+
+ // Load the request schema
+ $schema = new RequestSchema('schema://requests/role/create.yaml');
+
+ // Whitelist and set parameter defaults
+ $transformer = new RequestDataTransformer($schema);
+ $data = $transformer->transform($params);
+
+ $error = false;
+
+ // Validate request data
+ $validator = new ServerSideValidator($schema, $this->ci->translator);
+ if (!$validator->validate($data)) {
+ $ms->addValidationErrors($validator);
+ $error = true;
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ // Check if name or slug already exists
+ if ($classMapper->staticMethod('role', 'where', 'name', $data['name'])->first()) {
+ $ms->addMessageTranslated('danger', 'ROLE.NAME_IN_USE', $data);
+ $error = true;
+ }
+
+ if ($classMapper->staticMethod('role', 'where', 'slug', $data['slug'])->first()) {
+ $ms->addMessageTranslated('danger', 'SLUG_IN_USE', $data);
+ $error = true;
+ }
+
+ if ($error) {
+ return $response->withStatus(400);
+ }
+
+ /** @var UserFrosting\Config\Config $config */
+ $config = $this->ci->config;
+
+ // All checks passed! log events/activities and create role
+ // Begin transaction - DB will be rolled back if an exception occurs
+ Capsule::transaction( function() use ($classMapper, $data, $ms, $config, $currentUser) {
+ // Create the role
+ $role = $classMapper->createInstance('role', $data);
+
+ // Store new role to database
+ $role->save();
+
+ // Create activity record
+ $this->ci->userActivityLogger->info("User {$currentUser->user_name} created role {$role->name}.", [
+ 'type' => 'role_create',
+ 'user_id' => $currentUser->id
+ ]);
+
+ $ms->addMessageTranslated('success', 'ROLE.CREATION_SUCCESSFUL', $data);
+ });
+
+ return $response->withStatus(200);
+ }
+
+ /**
+ * Processes the request to delete an existing role.
+ *
+ * Deletes the specified role.
+ * Before doing so, checks that:
+ * 1. The user has permission to delete this role;
+ * 2. The role is not a default for new users;
+ * 3. The role does not have any associated users;
+ * 4. The submitted data is valid.
+ * This route requires authentication (and should generally be limited to admins or the root user).
+ * Request type: DELETE
+ */
+ public function delete($request, $response, $args)
+ {
+ $role = $this->getRoleFromParams($args);
+
+ // If the role doesn't exist, return 404
+ if (!$role) {
+ throw new NotFoundException($request, $response);
+ }
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'delete_role', [
+ 'role' => $role
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ // Check that we are not deleting a default role
+ $defaultRoleSlugs = $classMapper->staticMethod('role', 'getDefaultSlugs');
+
+ // Need to use loose comparison for now, because some DBs return `id` as a string
+ if (in_array($role->slug, $defaultRoleSlugs)) {
+ $e = new BadRequestException();
+ $e->addUserMessage('ROLE.DELETE_DEFAULT');
+ throw $e;
+ }
+
+ // Check if there are any users associated with this role
+ $countUsers = $role->users()->count();
+ if ($countUsers > 0) {
+ $e = new BadRequestException();
+ $e->addUserMessage('ROLE.HAS_USERS');
+ throw $e;
+ }
+
+ $roleName = $role->name;
+
+ // Begin transaction - DB will be rolled back if an exception occurs
+ Capsule::transaction( function() use ($role, $roleName, $currentUser) {
+ $role->delete();
+ unset($role);
+
+ // Create activity record
+ $this->ci->userActivityLogger->info("User {$currentUser->user_name} deleted role {$roleName}.", [
+ 'type' => 'role_delete',
+ 'user_id' => $currentUser->id
+ ]);
+ });
+
+ /** @var UserFrosting\Sprinkle\Core\MessageStream $ms */
+ $ms = $this->ci->alerts;
+
+ $ms->addMessageTranslated('success', 'ROLE.DELETION_SUCCESSFUL', [
+ 'name' => $roleName
+ ]);
+
+ return $response->withStatus(200);
+ }
+
+ /**
+ * Returns info for a single role, along with associated permissions.
+ *
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getInfo($request, $response, $args)
+ {
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'uri_roles')) {
+ throw new ForbiddenException();
+ }
+
+ $slug = $args['slug'];
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ $role = $classMapper->staticMethod('role', 'where', 'slug', $slug)->first();
+
+ // If the role doesn't exist, return 404
+ if (!$role) {
+ throw new NotFoundException($request, $response);
+ }
+
+ // Get role
+ $result = $role->load('permissions')->toArray();
+
+ // Be careful how you consume this data - it has not been escaped and contains untrusted user-supplied content.
+ // For example, if you plan to insert it into an HTML DOM, you must escape it on the client side (or use client-side templating).
+ return $response->withJson($result, 200, JSON_PRETTY_PRINT);
+ }
+
+ /**
+ * Returns a list of Roles
+ *
+ * Generates a list of roles, optionally paginated, sorted and/or filtered.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getList($request, $response, $args)
+ {
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'uri_roles')) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ $sprunje = $classMapper->createInstance('role_sprunje', $classMapper, $params);
+
+ // Be careful how you consume this data - it has not been escaped and contains untrusted user-supplied content.
+ // For example, if you plan to insert it into an HTML DOM, you must escape it on the client side (or use client-side templating).
+ return $sprunje->toResponse($response);
+ }
+
+ public function getModalConfirmDelete($request, $response, $args)
+ {
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ $role = $this->getRoleFromParams($params);
+
+ // If the role no longer exists, forward to main role listing page
+ if (!$role) {
+ throw new NotFoundException($request, $response);
+ }
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'delete_role', [
+ 'role' => $role
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ // Check that we are not deleting a default role
+ $defaultRoleSlugs = $classMapper->staticMethod('role', 'getDefaultSlugs');
+
+ // Need to use loose comparison for now, because some DBs return `id` as a string
+ if (in_array($role->slug, $defaultRoleSlugs)) {
+ $e = new BadRequestException();
+ $e->addUserMessage('ROLE.DELETE_DEFAULT', $role->toArray());
+ throw $e;
+ }
+
+ // Check if there are any users associated with this role
+ $countUsers = $role->users()->count();
+ if ($countUsers > 0) {
+ $e = new BadRequestException();
+ $e->addUserMessage('ROLE.HAS_USERS', $role->toArray());
+ throw $e;
+ }
+
+ return $this->ci->view->render($response, 'modals/confirm-delete-role.html.twig', [
+ 'role' => $role,
+ 'form' => [
+ 'action' => "api/roles/r/{$role->slug}",
+ ]
+ ]);
+ }
+
+ /**
+ * Renders the modal form for creating a new role.
+ *
+ * This does NOT render a complete page. Instead, it renders the HTML for the modal, which can be embedded in other pages.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getModalCreate($request, $response, $args)
+ {
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ /** @var UserFrosting\I18n\MessageTranslator $translator */
+ $translator = $this->ci->translator;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'create_role')) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ // Create a dummy role to prepopulate fields
+ $role = $classMapper->createInstance('role', []);
+
+ $fieldNames = ['name', 'slug', 'description'];
+ $fields = [
+ 'hidden' => [],
+ 'disabled' => []
+ ];
+
+ // Load validation rules
+ $schema = new RequestSchema('schema://requests/role/create.yaml');
+ $validator = new JqueryValidationAdapter($schema, $this->ci->translator);
+
+ return $this->ci->view->render($response, 'modals/role.html.twig', [
+ 'role' => $role,
+ 'form' => [
+ 'action' => 'api/roles',
+ 'method' => 'POST',
+ 'fields' => $fields,
+ 'submit_text' => $translator->translate('CREATE')
+ ],
+ 'page' => [
+ 'validators' => $validator->rules('json', false)
+ ]
+ ]);
+ }
+
+ /**
+ * Renders the modal form for editing an existing role.
+ *
+ * This does NOT render a complete page. Instead, it renders the HTML for the modal, which can be embedded in other pages.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getModalEdit($request, $response, $args)
+ {
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ $role = $this->getRoleFromParams($params);
+
+ // If the role doesn't exist, return 404
+ if (!$role) {
+ throw new NotFoundException($request, $response);
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ /** @var UserFrosting\I18n\MessageTranslator $translator */
+ $translator = $this->ci->translator;
+
+ // Access-controlled resource - check that currentUser has permission to edit basic fields "name", "slug", "description" for this role
+ $fieldNames = ['name', 'slug', 'description'];
+ if (!$authorizer->checkAccess($currentUser, 'update_role_field', [
+ 'role' => $role,
+ 'fields' => $fieldNames
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ // Generate form
+ $fields = [
+ 'hidden' => [],
+ 'disabled' => []
+ ];
+
+ // Load validation rules
+ $schema = new RequestSchema('schema://requests/role/edit-info.yaml');
+ $validator = new JqueryValidationAdapter($schema, $translator);
+
+ return $this->ci->view->render($response, 'modals/role.html.twig', [
+ 'role' => $role,
+ 'form' => [
+ 'action' => "api/roles/r/{$role->slug}",
+ 'method' => 'PUT',
+ 'fields' => $fields,
+ 'submit_text' => $translator->translate('UPDATE')
+ ],
+ 'page' => [
+ 'validators' => $validator->rules('json', false)
+ ]
+ ]);
+ }
+
+ /**
+ * Renders the modal form for editing a role's permissions.
+ *
+ * This does NOT render a complete page. Instead, it renders the HTML for the form, which can be embedded in other pages.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getModalEditPermissions($request, $response, $args)
+ {
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ $role = $this->getRoleFromParams($params);
+
+ // If the role doesn't exist, return 404
+ if (!$role) {
+ throw new NotFoundException($request, $response);
+ }
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled resource - check that currentUser has permission to edit "permissions" field for this role
+ if (!$authorizer->checkAccess($currentUser, 'update_role_field', [
+ 'role' => $role,
+ 'fields' => ['permissions']
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ return $this->ci->view->render($response, 'modals/role-manage-permissions.html.twig', [
+ 'role' => $role
+ ]);
+ }
+
+ /**
+ * Returns a list of Permissions for a specified Role.
+ *
+ * Generates a list of permissions, optionally paginated, sorted and/or filtered.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getPermissions($request, $response, $args)
+ {
+ $role = $this->getRoleFromParams($args);
+
+ // If the role no longer exists, forward to main role listing page
+ if (!$role) {
+ throw new NotFoundException($request, $response);
+ }
+
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'view_role_field', [
+ 'role' => $role,
+ 'property' => 'permissions'
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ $sprunje = $classMapper->createInstance('permission_sprunje', $classMapper, $params);
+ $sprunje->extendQuery(function ($query) use ($role) {
+ return $query->forRole($role->id);
+ });
+
+ // Be careful how you consume this data - it has not been escaped and contains untrusted user-supplied content.
+ // For example, if you plan to insert it into an HTML DOM, you must escape it on the client side (or use client-side templating).
+ return $sprunje->toResponse($response);
+ }
+
+ /**
+ * Returns users associated with a single role.
+ *
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getUsers($request, $response, $args)
+ {
+ $role = $this->getRoleFromParams($args);
+
+ // If the role doesn't exist, return 404
+ if (!$role) {
+ throw new NotFoundException($request, $response);
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'view_role_field', [
+ 'role' => $role,
+ 'property' => 'users'
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ $sprunje = $classMapper->createInstance('user_sprunje', $classMapper, $params);
+ $sprunje->extendQuery(function ($query) use ($role) {
+ return $query->forRole($role->id);
+ });
+
+ // Be careful how you consume this data - it has not been escaped and contains untrusted user-supplied content.
+ // For example, if you plan to insert it into an HTML DOM, you must escape it on the client side (or use client-side templating).
+ return $sprunje->toResponse($response);
+ }
+
+ /**
+ * Renders a page displaying a role's information, in read-only mode.
+ *
+ * This checks that the currently logged-in user has permission to view the requested role's info.
+ * It checks each field individually, showing only those that you have permission to view.
+ * This will also try to show buttons for deleting and editing the role.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function pageInfo($request, $response, $args)
+ {
+ $role = $this->getRoleFromParams($args);
+
+ // If the role no longer exists, forward to main role listing page
+ if (!$role) {
+ $redirectPage = $this->ci->router->pathFor('uri_roles');
+ return $response->withRedirect($redirectPage, 404);
+ }
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'uri_role', [
+ 'role' => $role
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ // Determine fields that currentUser is authorized to view
+ $fieldNames = ['name', 'slug', 'description'];
+
+ // Generate form
+ $fields = [
+ 'hidden' => []
+ ];
+
+ foreach ($fieldNames as $field) {
+ if (!$authorizer->checkAccess($currentUser, 'view_role_field', [
+ 'role' => $role,
+ 'property' => $field
+ ])) {
+ $fields['hidden'][] = $field;
+ }
+ }
+
+ // Determine buttons to display
+ $editButtons = [
+ 'hidden' => []
+ ];
+
+ if (!$authorizer->checkAccess($currentUser, 'update_role_field', [
+ 'role' => $role,
+ 'fields' => ['name', 'slug', 'description']
+ ])) {
+ $editButtons['hidden'][] = 'edit';
+ }
+
+ if (!$authorizer->checkAccess($currentUser, 'delete_role', [
+ 'role' => $role
+ ])) {
+ $editButtons['hidden'][] = 'delete';
+ }
+
+ return $this->ci->view->render($response, 'pages/role.html.twig', [
+ 'role' => $role,
+ 'fields' => $fields,
+ 'tools' => $editButtons
+ ]);
+ }
+
+ /**
+ * Renders the role listing page.
+ *
+ * This page renders a table of roles, with dropdown menus for admin actions for each role.
+ * Actions typically include: edit role, delete role.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function pageList($request, $response, $args)
+ {
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'uri_roles')) {
+ throw new ForbiddenException();
+ }
+
+ return $this->ci->view->render($response, 'pages/roles.html.twig');
+ }
+
+ /**
+ * Processes the request to update an existing role's details.
+ *
+ * Processes the request from the role update form, checking that:
+ * 1. The role name/slug are not already in use;
+ * 2. The user has the necessary permissions to update the posted field(s);
+ * 3. The submitted data is valid.
+ * This route requires authentication (and should generally be limited to admins or the root user).
+ * Request type: PUT
+ * @see getModalRoleEdit
+ */
+ public function updateInfo($request, $response, $args)
+ {
+ // Get the role based on slug in the URL
+ $role = $this->getRoleFromParams($args);
+
+ if (!$role) {
+ throw new NotFoundException($request, $response);
+ }
+
+ /** @var UserFrosting\Config\Config $config */
+ $config = $this->ci->config;
+
+ // Get PUT parameters: (name, slug, description)
+ $params = $request->getParsedBody();
+
+ /** @var UserFrosting\I18n\MessageTranslator $translator */
+ $ms = $this->ci->alerts;
+
+ // Load the request schema
+ $schema = new RequestSchema('schema://requests/role/edit-info.yaml');
+
+ // Whitelist and set parameter defaults
+ $transformer = new RequestDataTransformer($schema);
+ $data = $transformer->transform($params);
+
+ $error = false;
+
+ // Validate request data
+ $validator = new ServerSideValidator($schema, $this->ci->translator);
+ if (!$validator->validate($data)) {
+ $ms->addValidationErrors($validator);
+ $error = true;
+ }
+
+ // Determine targeted fields
+ $fieldNames = [];
+ foreach ($data as $name => $value) {
+ $fieldNames[] = $name;
+ }
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled resource - check that currentUser has permission to edit submitted fields for this role
+ if (!$authorizer->checkAccess($currentUser, 'update_role_field', [
+ 'role' => $role,
+ 'fields' => array_values(array_unique($fieldNames))
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ // Check if name or slug already exists
+ if (
+ isset($data['name']) &&
+ $data['name'] != $role->name &&
+ $classMapper->staticMethod('role', 'where', 'name', $data['name'])->first()
+ ) {
+ $ms->addMessageTranslated('danger', 'ROLE.NAME_IN_USE', $data);
+ $error = true;
+ }
+
+ if (
+ isset($data['slug']) &&
+ $data['slug'] != $role->slug &&
+ $classMapper->staticMethod('role', 'where', 'slug', $data['slug'])->first()
+ ) {
+ $ms->addMessageTranslated('danger', 'SLUG_IN_USE', $data);
+ $error = true;
+ }
+
+ if ($error) {
+ return $response->withStatus(400);
+ }
+
+ // Begin transaction - DB will be rolled back if an exception occurs
+ Capsule::transaction( function() use ($data, $role, $currentUser) {
+ // Update the role and generate success messages
+ foreach ($data as $name => $value) {
+ if ($value != $role->$name){
+ $role->$name = $value;
+ }
+ }
+
+ $role->save();
+
+ // Create activity record
+ $this->ci->userActivityLogger->info("User {$currentUser->user_name} updated details for role {$role->name}.", [
+ 'type' => 'role_update_info',
+ 'user_id' => $currentUser->id
+ ]);
+ });
+
+ $ms->addMessageTranslated('success', 'ROLE.UPDATED', [
+ 'name' => $role->name
+ ]);
+
+ return $response->withStatus(200);
+ }
+
+ /**
+ * Processes the request to update a specific field for an existing role, including permissions.
+ *
+ * Processes the request from the role update form, checking that:
+ * 1. The logged-in user has the necessary permissions to update the putted field(s);
+ * 2. The submitted data is valid.
+ * This route requires authentication.
+ * Request type: PUT
+ */
+ public function updateField($request, $response, $args)
+ {
+ // Get the username from the URL
+ $role = $this->getRoleFromParams($args);
+
+ if (!$role) {
+ throw new NotFoundException($request, $response);
+ }
+
+ // Get key->value pair from URL and request body
+ $fieldName = $args['field'];
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled resource - check that currentUser has permission to edit the specified field for this user
+ if (!$authorizer->checkAccess($currentUser, 'update_role_field', [
+ 'role' => $role,
+ 'fields' => [$fieldName]
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Config\Config $config */
+ $config = $this->ci->config;
+
+ // Get PUT parameters: value
+ $put = $request->getParsedBody();
+
+ if (!isset($put['value'])) {
+ throw new BadRequestException();
+ }
+
+ $params = [
+ $fieldName => $put['value']
+ ];
+
+ // Validate key -> value pair
+
+ // Load the request schema
+ $schema = new RequestSchema('schema://requests/role/edit-field.yaml');
+
+ // Whitelist and set parameter defaults
+ $transformer = new RequestDataTransformer($schema);
+ $data = $transformer->transform($params);
+
+ // Validate, and throw exception on validation errors.
+ $validator = new ServerSideValidator($schema, $this->ci->translator);
+ if (!$validator->validate($data)) {
+ // TODO: encapsulate the communication of error messages from ServerSideValidator to the BadRequestException
+ $e = new BadRequestException();
+ foreach ($validator->errors() as $idx => $field) {
+ foreach($field as $eidx => $error) {
+ $e->addUserMessage($error);
+ }
+ }
+ throw $e;
+ }
+
+ // Get validated and transformed value
+ $fieldValue = $data[$fieldName];
+
+ /** @var UserFrosting\I18n\MessageTranslator $translator */
+ $ms = $this->ci->alerts;
+
+ // Begin transaction - DB will be rolled back if an exception occurs
+ Capsule::transaction( function() use ($fieldName, $fieldValue, $role, $currentUser) {
+ if ($fieldName == 'permissions') {
+ $newPermissions = collect($fieldValue)->pluck('permission_id')->all();
+ $role->permissions()->sync($newPermissions);
+ } else {
+ $role->$fieldName = $fieldValue;
+ $role->save();
+ }
+
+ // Create activity record
+ $this->ci->userActivityLogger->info("User {$currentUser->user_name} updated property '$fieldName' for role {$role->name}.", [
+ 'type' => 'role_update_field',
+ 'user_id' => $currentUser->id
+ ]);
+ });
+
+ // Add success messages
+ if ($fieldName == 'permissions') {
+ $ms->addMessageTranslated('success', 'ROLE.PERMISSIONS_UPDATED', [
+ 'name' => $role->name
+ ]);
+ } else {
+ $ms->addMessageTranslated('success', 'ROLE.UPDATED', [
+ 'name' => $role->name
+ ]);
+ }
+
+ return $response->withStatus(200);
+ }
+
+ protected function getRoleFromParams($params)
+ {
+ // Load the request schema
+ $schema = new RequestSchema('schema://requests/role/get-by-slug.yaml');
+
+ // Whitelist and set parameter defaults
+ $transformer = new RequestDataTransformer($schema);
+ $data = $transformer->transform($params);
+
+ // Validate, and throw exception on validation errors.
+ $validator = new ServerSideValidator($schema, $this->ci->translator);
+ if (!$validator->validate($data)) {
+ // TODO: encapsulate the communication of error messages from ServerSideValidator to the BadRequestException
+ $e = new BadRequestException();
+ foreach ($validator->errors() as $idx => $field) {
+ foreach($field as $eidx => $error) {
+ $e->addUserMessage($error);
+ }
+ }
+ throw $e;
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ // Get the role
+ $role = $classMapper->staticMethod('role', 'where', 'slug', $data['slug'])
+ ->first();
+
+ return $role;
+ }
+}
diff --git a/main/app/sprinkles/admin/src/Controller/UserController.php b/main/app/sprinkles/admin/src/Controller/UserController.php
new file mode 100755
index 0000000..5bece6a
--- /dev/null
+++ b/main/app/sprinkles/admin/src/Controller/UserController.php
@@ -0,0 +1,1261 @@
+getParsedBody();
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'create_user')) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\MessageStream $ms */
+ $ms = $this->ci->alerts;
+
+ // Load the request schema
+ $schema = new RequestSchema('schema://requests/user/create.yaml');
+
+ // Whitelist and set parameter defaults
+ $transformer = new RequestDataTransformer($schema);
+ $data = $transformer->transform($params);
+
+ $error = FALSE;
+
+ // Validate request data
+ $validator = new ServerSideValidator($schema, $this->ci->translator);
+ if (!$validator->validate($data)) {
+ $ms->addValidationErrors($validator);
+ $error = TRUE;
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ // Check if username or email already exists
+ if ($classMapper->staticMethod('user', 'findUnique', $data['user_name'], 'user_name')) {
+ $ms->addMessageTranslated('danger', 'USERNAME.IN_USE', $data);
+ $error = TRUE;
+ }
+
+ if ($classMapper->staticMethod('user', 'findUnique', $data['email'], 'email')) {
+ $ms->addMessageTranslated('danger', 'EMAIL.IN_USE', $data);
+ $error = TRUE;
+ }
+
+ if ($error) {
+ return $response->withStatus(400);
+ }
+
+ /** @var UserFrosting\Config\Config $config */
+ $config = $this->ci->config;
+
+ // If currentUser does not have permission to set the group, but they try to set it to something other than their own group,
+ // throw an exception.
+ if (!$authorizer->checkAccess($currentUser, 'create_user_field', [
+ 'fields' => ['group']
+ ])) {
+ if (isset($data['group_id']) && $data['group_id'] != $currentUser->group_id) {
+ throw new ForbiddenException();
+ }
+ }
+
+ // In any case, set the group id if not otherwise set
+ if (!isset($data['group_id'])) {
+ $data['group_id'] = $currentUser->group_id;
+ }
+
+ $data['flag_verified'] = 1;
+ // Set password as empty on initial creation. We will then send email so new user can set it themselves via a verification token
+ $data['password'] = '';
+
+ // All checks passed! log events/activities, create user, and send verification email (if required)
+ // Begin transaction - DB will be rolled back if an exception occurs
+ Capsule::transaction(function () use ($classMapper, $data, $ms, $config, $currentUser) {
+ // Create the user
+ $user = $classMapper->createInstance('user', $data);
+
+ // Store new user to database
+ $user->save();
+
+ // Create activity record
+ $this->ci->userActivityLogger->info("User {$currentUser->user_name} created a new account for {$user->user_name}.", [
+ 'type' => 'account_create',
+ 'user_id' => $currentUser->id
+ ]);
+
+ // Load default roles
+ $defaultRoleSlugs = $classMapper->staticMethod('role', 'getDefaultSlugs');
+ $defaultRoles = $classMapper->staticMethod('role', 'whereIn', 'slug', $defaultRoleSlugs)->get();
+ $defaultRoleIds = $defaultRoles->pluck('id')->all();
+
+ // Attach default roles
+ $user->roles()->attach($defaultRoleIds);
+
+ // Try to generate a new password request
+ $passwordRequest = $this->ci->repoPasswordReset->create($user, $config['password_reset.timeouts.create']);
+
+ // Create and send welcome email with password set link
+ $message = new TwigMailMessage($this->ci->view, 'mail/password-create.html.twig');
+
+ $message->from($config['address_book.admin'])
+ ->addEmailRecipient(new EmailRecipient($user->email, $user->full_name))
+ ->addParams([
+ 'user' => $user,
+ 'create_password_expiration' => $config['password_reset.timeouts.create'] / 3600 . ' hours',
+ 'token' => $passwordRequest->getToken()
+ ]);
+
+ $this->ci->mailer->send($message);
+
+ $ms->addMessageTranslated('success', 'USER.CREATED', $data);
+ });
+
+ return $response->withStatus(200);
+ }
+
+ /**
+ * Processes the request to send a user a password reset email.
+ *
+ * Processes the request from the user update form, checking that:
+ * 1. The target user's new email address, if specified, is not already in use;
+ * 2. The logged-in user has the necessary permissions to update the posted field(s);
+ * 3. We're not trying to disable the master account;
+ * 4. The submitted data is valid.
+ * This route requires authentication.
+ * Request type: POST
+ */
+ public function createPasswordReset($request, $response, $args) {
+ // Get the username from the URL
+ $user = $this->getUserFromParams($args);
+
+ if (!$user) {
+ throw new NotFoundException($request, $response);
+ }
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled resource - check that currentUser has permission to edit "password" for this user
+ if (!$authorizer->checkAccess($currentUser, 'update_user_field', [
+ 'user' => $user,
+ 'fields' => ['password']
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Config\Config $config */
+ $config = $this->ci->config;
+
+ /** @var UserFrosting\Sprinkle\Core\MessageStream $ms */
+ $ms = $this->ci->alerts;
+
+ // Begin transaction - DB will be rolled back if an exception occurs
+ Capsule::transaction(function () use ($user, $config) {
+
+ // Create a password reset and shoot off an email
+ $passwordReset = $this->ci->repoPasswordReset->create($user, $config['password_reset.timeouts.reset']);
+
+ // Create and send welcome email with password set link
+ $message = new TwigMailMessage($this->ci->view, 'mail/password-reset.html.twig');
+
+ $message->from($config['address_book.admin'])
+ ->addEmailRecipient(new EmailRecipient($user->email, $user->full_name))
+ ->addParams([
+ 'user' => $user,
+ 'token' => $passwordReset->getToken(),
+ 'request_date' => Carbon::now()->format('Y-m-d H:i:s')
+ ]);
+
+ $this->ci->mailer->send($message);
+ });
+
+ $ms->addMessageTranslated('success', 'PASSWORD.FORGET.REQUEST_SENT', [
+ 'email' => $user->email
+ ]);
+ return $response->withStatus(200);
+ }
+
+ /**
+ * Processes the request to delete an existing user.
+ *
+ * Deletes the specified user, removing any existing associations.
+ * Before doing so, checks that:
+ * 1. You are not trying to delete the master account;
+ * 2. You have permission to delete the target user's account.
+ * This route requires authentication (and should generally be limited to admins or the root user).
+ * Request type: DELETE
+ */
+ public function delete($request, $response, $args) {
+ $user = $this->getUserFromParams($args);
+
+ // If the user doesn't exist, return 404
+ if (!$user) {
+ throw new NotFoundException($request, $response);
+ }
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'delete_user', [
+ 'user' => $user
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Config\Config $config */
+ $config = $this->ci->config;
+
+ // Check that we are not deleting the master account
+ // Need to use loose comparison for now, because some DBs return `id` as a string
+ if ($user->id == $config['reserved_user_ids.master']) {
+ $e = new BadRequestException();
+ $e->addUserMessage('DELETE_MASTER');
+ throw $e;
+ }
+
+ $userName = $user->user_name;
+
+ // Begin transaction - DB will be rolled back if an exception occurs
+ Capsule::transaction(function () use ($user, $userName, $currentUser) {
+ $user->delete();
+ unset($user);
+
+ // Create activity record
+ $this->ci->userActivityLogger->info("User {$currentUser->user_name} deleted the account for {$userName}.", [
+ 'type' => 'account_delete',
+ 'user_id' => $currentUser->id
+ ]);
+ });
+
+ /** @var UserFrosting\Sprinkle\Core\MessageStream $ms */
+ $ms = $this->ci->alerts;
+
+ $ms->addMessageTranslated('success', 'DELETION_SUCCESSFUL', [
+ 'user_name' => $userName
+ ]);
+
+ return $response->withStatus(200);
+ }
+
+ /**
+ * Returns activity history for a single user.
+ *
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getActivities($request, $response, $args) {
+ $user = $this->getUserFromParams($args);
+
+ // If the user doesn't exist, return 404
+ if (!$user) {
+ throw new NotFoundException($request, $response);
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'view_user_field', [
+ 'user' => $user,
+ 'property' => 'activities'
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ $sprunje = $classMapper->createInstance('activity_sprunje', $classMapper, $params);
+
+ $sprunje->extendQuery(function ($query) use ($user) {
+ return $query->where('user_id', $user->id);
+ });
+
+ // Be careful how you consume this data - it has not been escaped and contains untrusted user-supplied content.
+ // For example, if you plan to insert it into an HTML DOM, you must escape it on the client side (or use client-side templating).
+ return $sprunje->toResponse($response);
+ }
+
+ /**
+ * Returns info for a single user.
+ *
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getInfo($request, $response, $args) {
+ $user = $this->getUserFromParams($args);
+
+ // If the user doesn't exist, return 404
+ if (!$user) {
+ throw new NotFoundException($request, $response);
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ // Join user's most recent activity
+ $user = $classMapper->createInstance('user')
+ ->where('user_name', $user->user_name)
+ ->joinLastActivity()
+ ->with('lastActivity', 'group')
+ ->first();
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'uri_user', [
+ 'user' => $user
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ $result = $user->toArray();
+
+ // Be careful how you consume this data - it has not been escaped and contains untrusted user-supplied content.
+ // For example, if you plan to insert it into an HTML DOM, you must escape it on the client side (or use client-side templating).
+ return $response->withJson($result, 200, JSON_PRETTY_PRINT);
+ }
+
+ /**
+ * Returns a list of Users
+ *
+ * Generates a list of users, optionally paginated, sorted and/or filtered.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getList($request, $response, $args) {
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'uri_users')) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ $sprunje = $classMapper->createInstance('user_sprunje', $classMapper, $params);
+
+ // Be careful how you consume this data - it has not been escaped and contains untrusted user-supplied content.
+ // For example, if you plan to insert it into an HTML DOM, you must escape it on the client side (or use client-side templating).
+ return $sprunje->toResponse($response);
+ }
+
+ /**
+ * Renders the modal form to confirm user deletion.
+ *
+ * This does NOT render a complete page. Instead, it renders the HTML for the modal, which can be embedded in other pages.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getModalConfirmDelete($request, $response, $args) {
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ $user = $this->getUserFromParams($params);
+
+ // If the user doesn't exist, return 404
+ if (!$user) {
+ throw new NotFoundException($request, $response);
+ }
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'delete_user', [
+ 'user' => $user
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Config\Config $config */
+ $config = $this->ci->config;
+
+ // Check that we are not deleting the master account
+ // Need to use loose comparison for now, because some DBs return `id` as a string
+ if ($user->id == $config['reserved_user_ids.master']) {
+ $e = new BadRequestException();
+ $e->addUserMessage('DELETE_MASTER');
+ throw $e;
+ }
+
+ return $this->ci->view->render($response, 'modals/confirm-delete-user.html.twig', [
+ 'user' => $user,
+ 'form' => [
+ 'action' => "api/users/u/{$user->user_name}",
+ ]
+ ]);
+ }
+
+ /**
+ * Renders the modal form for creating a new user.
+ *
+ * This does NOT render a complete page. Instead, it renders the HTML for the modal, which can be embedded in other pages.
+ * If the currently logged-in user has permission to modify user group membership, then the group toggle will be displayed.
+ * Otherwise, the user will be added to the default group and receive the default roles automatically.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getModalCreate($request, $response, $args) {
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ /** @var UserFrosting\I18n\MessageTranslator $translator */
+ $translator = $this->ci->translator;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'create_user')) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ /** @var UserFrosting\Config\Config $config */
+ $config = $this->ci->config;
+
+ // Determine form fields to hide/disable
+ // TODO: come back to this when we finish implementing theming
+ $fields = [
+ 'hidden' => ['theme'],
+ 'disabled' => []
+ ];
+
+ // Get a list of all locales
+ $locales = $config->getDefined('site.locales.available');
+
+ // Determine if currentUser has permission to modify the group. If so, show the 'group' dropdown.
+ // Otherwise, set to the currentUser's group and disable the dropdown.
+ if ($authorizer->checkAccess($currentUser, 'create_user_field', [
+ 'fields' => ['group']
+ ])) {
+ // Get a list of all groups
+ $groups = $classMapper->staticMethod('group', 'all');
+ } else {
+ // Get the current user's group
+ $groups = $currentUser->group()->get();
+ $fields['disabled'][] = 'group';
+ }
+
+ // Create a dummy user to prepopulate fields
+ $data = [
+ 'group_id' => $currentUser->group_id,
+ 'locale' => $config['site.registration.user_defaults.locale'],
+ 'theme' => ''
+ ];
+
+ $user = $classMapper->createInstance('user', $data);
+
+ // Load validation rules
+ $schema = new RequestSchema('schema://requests/user/create.yaml');
+ $validator = new JqueryValidationAdapter($schema, $this->ci->translator);
+
+ return $this->ci->view->render($response, 'modals/user.html.twig', [
+ 'user' => $user,
+ 'groups' => $groups,
+ 'locales' => $locales,
+ 'form' => [
+ 'action' => 'api/users',
+ 'method' => 'POST',
+ 'fields' => $fields,
+ 'submit_text' => $translator->translate('CREATE')
+ ],
+ 'page' => [
+ 'validators' => $validator->rules('json', FALSE)
+ ]
+ ]);
+ }
+
+ /**
+ * Renders the modal form for editing an existing user.
+ *
+ * This does NOT render a complete page. Instead, it renders the HTML for the modal, which can be embedded in other pages.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getModalEdit($request, $response, $args) {
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ $user = $this->getUserFromParams($params);
+
+ // If the user doesn't exist, return 404
+ if (!$user) {
+ throw new NotFoundException($request, $response);
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ // Get the user to edit
+ $user = $classMapper->staticMethod('user', 'where', 'user_name', $user->user_name)
+ ->with('group')
+ ->first();
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled resource - check that currentUser has permission to edit basic fields "name", "email", "locale" for this user
+ $fieldNames = ['name', 'email', 'locale'];
+ if (!$authorizer->checkAccess($currentUser, 'update_user_field', [
+ 'user' => $user,
+ 'fields' => $fieldNames
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ // Get a list of all groups
+ $groups = $classMapper->staticMethod('group', 'all');
+
+ /** @var UserFrosting\Config\Config $config */
+ $config = $this->ci->config;
+
+ // Get a list of all locales
+ $locales = $config->getDefined('site.locales.available');
+
+ // Generate form
+ $fields = [
+ 'hidden' => ['theme'],
+ 'disabled' => ['user_name']
+ ];
+
+ // Disable group field if currentUser doesn't have permission to modify group
+ if (!$authorizer->checkAccess($currentUser, 'update_user_field', [
+ 'user' => $user,
+ 'fields' => ['group']
+ ])) {
+ $fields['disabled'][] = 'group';
+ }
+
+ // Load validation rules
+ $schema = new RequestSchema('schema://requests/user/edit-info.yaml');
+ $validator = new JqueryValidationAdapter($schema, $this->ci->translator);
+
+ $translator = $this->ci->translator;
+
+ return $this->ci->view->render($response, 'modals/user.html.twig', [
+ 'user' => $user,
+ 'groups' => $groups,
+ 'locales' => $locales,
+ 'form' => [
+ 'action' => "api/users/u/{$user->user_name}",
+ 'method' => 'PUT',
+ 'fields' => $fields,
+ 'submit_text' => $translator->translate('UPDATE')
+ ],
+ 'page' => [
+ 'validators' => $validator->rules('json', FALSE)
+ ]
+ ]);
+ }
+
+ /**
+ * Renders the modal form for editing a user's password.
+ *
+ * This does NOT render a complete page. Instead, it renders the HTML for the form, which can be embedded in other pages.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getModalEditPassword($request, $response, $args) {
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ $user = $this->getUserFromParams($params);
+
+ // If the user doesn't exist, return 404
+ if (!$user) {
+ throw new NotFoundException($request, $response);
+ }
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled resource - check that currentUser has permission to edit "password" field for this user
+ if (!$authorizer->checkAccess($currentUser, 'update_user_field', [
+ 'user' => $user,
+ 'fields' => ['password']
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ // Load validation rules
+ $schema = new RequestSchema('schema://requests/user/edit-password.yaml');
+ $validator = new JqueryValidationAdapter($schema, $this->ci->translator);
+
+ return $this->ci->view->render($response, 'modals/user-set-password.html.twig', [
+ 'user' => $user,
+ 'page' => [
+ 'validators' => $validator->rules('json', FALSE)
+ ]
+ ]);
+ }
+
+ /**
+ * Renders the modal form for editing a user's roles.
+ *
+ * This does NOT render a complete page. Instead, it renders the HTML for the form, which can be embedded in other pages.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getModalEditRoles($request, $response, $args) {
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ $user = $this->getUserFromParams($params);
+
+ // If the user doesn't exist, return 404
+ if (!$user) {
+ throw new NotFoundException($request, $response);
+ }
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled resource - check that currentUser has permission to edit "roles" field for this user
+ if (!$authorizer->checkAccess($currentUser, 'update_user_field', [
+ 'user' => $user,
+ 'fields' => ['roles']
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ return $this->ci->view->render($response, 'modals/user-manage-roles.html.twig', [
+ 'user' => $user
+ ]);
+ }
+
+ /**
+ * Returns a list of effective Permissions for a specified User.
+ *
+ * Generates a list of permissions, optionally paginated, sorted and/or filtered.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getPermissions($request, $response, $args) {
+ $user = $this->getUserFromParams($args);
+
+ // If the user doesn't exist, return 404
+ if (!$user) {
+ throw new NotFoundException($request, $response);
+ }
+
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'view_user_field', [
+ 'user' => $user,
+ 'property' => 'permissions'
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ $params['user_id'] = $user->id;
+ $sprunje = $classMapper->createInstance('user_permission_sprunje', $classMapper, $params);
+
+ $response = $sprunje->toResponse($response);
+
+ // Be careful how you consume this data - it has not been escaped and contains untrusted user-supplied content.
+ // For example, if you plan to insert it into an HTML DOM, you must escape it on the client side (or use client-side templating).
+ return $response;
+ }
+
+ /**
+ * Returns roles associated with a single user.
+ *
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function getRoles($request, $response, $args) {
+ $user = $this->getUserFromParams($args);
+
+ // If the user doesn't exist, return 404
+ if (!$user) {
+ throw new NotFoundException($request, $response);
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ // GET parameters
+ $params = $request->getQueryParams();
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'view_user_field', [
+ 'user' => $user,
+ 'property' => 'roles'
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ $sprunje = $classMapper->createInstance('role_sprunje', $classMapper, $params);
+ $sprunje->extendQuery(function ($query) use ($user) {
+ return $query->forUser($user->id);
+ });
+
+ // Be careful how you consume this data - it has not been escaped and contains untrusted user-supplied content.
+ // For example, if you plan to insert it into an HTML DOM, you must escape it on the client side (or use client-side templating).
+ return $sprunje->toResponse($response);
+ }
+
+ /**
+ * Renders a page displaying a user's information, in read-only mode.
+ *
+ * This checks that the currently logged-in user has permission to view the requested user's info.
+ * It checks each field individually, showing only those that you have permission to view.
+ * This will also try to show buttons for activating, disabling/enabling, deleting, and editing the user.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function pageInfo($request, $response, $args) {
+ $user = $this->getUserFromParams($args);
+
+ // If the user no longer exists, forward to main user listing page
+ if (!$user) {
+ $usersPage = $this->ci->router->pathFor('uri_users');
+ return $response->withRedirect($usersPage, 404);
+ }
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'uri_user', [
+ 'user' => $user
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Config\Config $config */
+ $config = $this->ci->config;
+
+ // Get a list of all locales
+ $locales = $config->getDefined('site.locales.available');
+
+ // Determine fields that currentUser is authorized to view
+ $fieldNames = ['user_name', 'name', 'email', 'locale', 'group', 'roles'];
+
+ // Generate form
+ $fields = [
+ // Always hide these
+ 'hidden' => ['theme']
+ ];
+
+ // Determine which fields should be hidden
+ foreach ($fieldNames as $field) {
+ if (!$authorizer->checkAccess($currentUser, 'view_user_field', [
+ 'user' => $user,
+ 'property' => $field
+ ])) {
+ $fields['hidden'][] = $field;
+ }
+ }
+
+ // Determine buttons to display
+ $editButtons = [
+ 'hidden' => []
+ ];
+
+ if (!$authorizer->checkAccess($currentUser, 'update_user_field', [
+ 'user' => $user,
+ 'fields' => ['name', 'email', 'locale']
+ ])) {
+ $editButtons['hidden'][] = 'edit';
+ }
+
+ if (!$authorizer->checkAccess($currentUser, 'update_user_field', [
+ 'user' => $user,
+ 'fields' => ['flag_enabled']
+ ])) {
+ $editButtons['hidden'][] = 'enable';
+ }
+
+ if (!$authorizer->checkAccess($currentUser, 'update_user_field', [
+ 'user' => $user,
+ 'fields' => ['flag_verified']
+ ])) {
+ $editButtons['hidden'][] = 'activate';
+ }
+
+ if (!$authorizer->checkAccess($currentUser, 'update_user_field', [
+ 'user' => $user,
+ 'fields' => ['password']
+ ])) {
+ $editButtons['hidden'][] = 'password';
+ }
+
+ if (!$authorizer->checkAccess($currentUser, 'update_user_field', [
+ 'user' => $user,
+ 'fields' => ['roles']
+ ])) {
+ $editButtons['hidden'][] = 'roles';
+ }
+
+ if (!$authorizer->checkAccess($currentUser, 'delete_user', [
+ 'user' => $user
+ ])) {
+ $editButtons['hidden'][] = 'delete';
+ }
+
+ // Determine widgets to display
+ $widgets = [
+ 'hidden' => []
+ ];
+
+ if (!$authorizer->checkAccess($currentUser, 'view_user_field', [
+ 'user' => $user,
+ 'property' => 'permissions'
+ ])) {
+ $widgets['hidden'][] = 'permissions';
+ }
+
+ if (!$authorizer->checkAccess($currentUser, 'view_user_field', [
+ 'user' => $user,
+ 'property' => 'activities'
+ ])) {
+ $widgets['hidden'][] = 'activities';
+ }
+
+ return $this->ci->view->render($response, 'pages/user.html.twig', [
+ 'user' => $user,
+ 'locales' => $locales,
+ 'fields' => $fields,
+ 'tools' => $editButtons,
+ 'widgets' => $widgets
+ ]);
+ }
+
+ /**
+ * Renders the user listing page.
+ *
+ * This page renders a table of users, with dropdown menus for admin actions for each user.
+ * Actions typically include: edit user details, activate user, enable/disable user, delete user.
+ * This page requires authentication.
+ * Request type: GET
+ */
+ public function pageList($request, $response, $args) {
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled page
+ if (!$authorizer->checkAccess($currentUser, 'uri_users')) {
+ throw new ForbiddenException();
+ }
+
+ return $this->ci->view->render($response, 'pages/users.html.twig');
+ }
+
+ /**
+ * Processes the request to update an existing user's basic details (first_name, last_name, email, locale, group_id)
+ *
+ * Processes the request from the user update form, checking that:
+ * 1. The target user's new email address, if specified, is not already in use;
+ * 2. The logged-in user has the necessary permissions to update the putted field(s);
+ * 3. The submitted data is valid.
+ * This route requires authentication.
+ * Request type: PUT
+ */
+ public function updateInfo($request, $response, $args) {
+ // Get the username from the URL
+ $user = $this->getUserFromParams($args);
+
+ if (!$user) {
+ throw new NotFoundException($request, $response);
+ }
+
+ /** @var UserFrosting\Config\Config $config */
+ $config = $this->ci->config;
+
+ // Get PUT parameters
+ $params = $request->getParsedBody();
+
+ /** @var UserFrosting\Sprinkle\Core\MessageStream $ms */
+ $ms = $this->ci->alerts;
+
+ // Load the request schema
+ $schema = new RequestSchema('schema://requests/user/edit-info.yaml');
+
+ // Whitelist and set parameter defaults
+ $transformer = new RequestDataTransformer($schema);
+ $data = $transformer->transform($params);
+
+ $error = FALSE;
+
+ // Validate request data
+ $validator = new ServerSideValidator($schema, $this->ci->translator);
+ if (!$validator->validate($data)) {
+ $ms->addValidationErrors($validator);
+ $error = TRUE;
+ }
+
+ // Determine targeted fields
+ $fieldNames = [];
+ foreach ($data as $name => $value) {
+ if ($name == 'first_name' || $name == 'last_name') {
+ $fieldNames[] = 'name';
+ } elseif ($name == 'group_id') {
+ $fieldNames[] = 'group';
+ } else {
+ $fieldNames[] = $name;
+ }
+ }
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled resource - check that currentUser has permission to edit submitted fields for this user
+ if (!$authorizer->checkAccess($currentUser, 'update_user_field', [
+ 'user' => $user,
+ 'fields' => array_values(array_unique($fieldNames))
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ // Only the master account can edit the master account!
+ if (
+ ($user->id == $config['reserved_user_ids.master']) &&
+ ($currentUser->id != $config['reserved_user_ids.master'])
+ ) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ // Check if email already exists
+ if (
+ isset($data['email']) &&
+ $data['email'] != $user->email &&
+ $classMapper->staticMethod('user', 'findUnique', $data['email'], 'email')
+ ) {
+ $ms->addMessageTranslated('danger', 'EMAIL.IN_USE', $data);
+ $error = TRUE;
+ }
+
+ if ($error) {
+ return $response->withStatus(400);
+ }
+
+ // Begin transaction - DB will be rolled back if an exception occurs
+ Capsule::transaction(function () use ($data, $user, $currentUser) {
+ // Update the user and generate success messages
+ foreach ($data as $name => $value) {
+ if ($value != $user->$name) {
+ $user->$name = $value;
+ }
+ }
+
+ $user->save();
+
+ // Create activity record
+ $this->ci->userActivityLogger->info("User {$currentUser->user_name} updated basic account info for user {$user->user_name}.", [
+ 'type' => 'account_update_info',
+ 'user_id' => $currentUser->id
+ ]);
+ });
+
+ $ms->addMessageTranslated('success', 'DETAILS_UPDATED', [
+ 'user_name' => $user->user_name
+ ]);
+ return $response->withStatus(200);
+ }
+
+ /**
+ * Processes the request to update a specific field for an existing user.
+ *
+ * Supports editing all user fields, including password, enabled/disabled status and verification status.
+ * Processes the request from the user update form, checking that:
+ * 1. The logged-in user has the necessary permissions to update the putted field(s);
+ * 2. We're not trying to disable the master account;
+ * 3. The submitted data is valid.
+ * This route requires authentication.
+ * Request type: PUT
+ */
+ public function updateField($request, $response, $args) {
+ // Get the username from the URL
+ $user = $this->getUserFromParams($args);
+
+ if (!$user) {
+ throw new NotFoundException($request, $response);
+ }
+
+ // Get key->value pair from URL and request body
+ $fieldName = $args['field'];
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
+ $authorizer = $this->ci->authorizer;
+
+ /** @var UserFrosting\Sprinkle\Account\Database\Models\User $currentUser */
+ $currentUser = $this->ci->currentUser;
+
+ // Access-controlled resource - check that currentUser has permission to edit the specified field for this user
+ if (!$authorizer->checkAccess($currentUser, 'update_user_field', [
+ 'user' => $user,
+ 'fields' => [$fieldName]
+ ])) {
+ throw new ForbiddenException();
+ }
+
+ /** @var UserFrosting\Config\Config $config */
+ $config = $this->ci->config;
+
+ // Only the master account can edit the master account!
+ if (
+ ($user->id == $config['reserved_user_ids.master']) &&
+ ($currentUser->id != $config['reserved_user_ids.master'])
+ ) {
+ throw new ForbiddenException();
+ }
+
+ // Get PUT parameters: value
+ $put = $request->getParsedBody();
+
+ if (!isset($put['value'])) {
+ throw new BadRequestException();
+ }
+
+ // Create and validate key -> value pair
+ $params = [
+ $fieldName => $put['value']
+ ];
+
+ // Load the request schema
+ $schema = new RequestSchema('schema://requests/user/edit-field.yaml');
+
+ // Whitelist and set parameter defaults
+ $transformer = new RequestDataTransformer($schema);
+ $data = $transformer->transform($params);
+
+ // Validate, and throw exception on validation errors.
+ $validator = new ServerSideValidator($schema, $this->ci->translator);
+ if (!$validator->validate($data)) {
+ // TODO: encapsulate the communication of error messages from ServerSideValidator to the BadRequestException
+ $e = new BadRequestException();
+ foreach ($validator->errors() as $idx => $field) {
+ foreach ($field as $eidx => $error) {
+ $e->addUserMessage($error);
+ }
+ }
+ throw $e;
+ }
+
+ // Get validated and transformed value
+ $fieldValue = $data[$fieldName];
+
+ /** @var UserFrosting\Sprinkle\Core\MessageStream $ms */
+ $ms = $this->ci->alerts;
+
+ // Special checks and transformations for certain fields
+ if ($fieldName == 'flag_enabled') {
+ // Check that we are not disabling the master account
+ if (
+ ($user->id == $config['reserved_user_ids.master']) &&
+ ($fieldValue == '0')
+ ) {
+ $e = new BadRequestException();
+ $e->addUserMessage('DISABLE_MASTER');
+ throw $e;
+ } elseif (
+ ($user->id == $currentUser->id) &&
+ ($fieldValue == '0')
+ ) {
+ $e = new BadRequestException();
+ $e->addUserMessage('DISABLE_SELF');
+ throw $e;
+ }
+ } elseif ($fieldName == 'password') {
+ $fieldValue = Password::hash($fieldValue);
+ }
+
+ // Begin transaction - DB will be rolled back if an exception occurs
+ Capsule::transaction(function () use ($fieldName, $fieldValue, $user, $currentUser) {
+ if ($fieldName == 'roles') {
+ $newRoles = collect($fieldValue)->pluck('role_id')->all();
+ $user->roles()->sync($newRoles);
+ } else {
+ $user->$fieldName = $fieldValue;
+ $user->save();
+ }
+
+ // Create activity record
+ $this->ci->userActivityLogger->info("User {$currentUser->user_name} updated property '$fieldName' for user {$user->user_name}.", [
+ 'type' => 'account_update_field',
+ 'user_id' => $currentUser->id
+ ]);
+ });
+
+ // Add success messages
+ if ($fieldName == 'flag_enabled') {
+ if ($fieldValue == '1') {
+ $ms->addMessageTranslated('success', 'ENABLE_SUCCESSFUL', [
+ 'user_name' => $user->user_name
+ ]);
+ } else {
+ $ms->addMessageTranslated('success', 'DISABLE_SUCCESSFUL', [
+ 'user_name' => $user->user_name
+ ]);
+ }
+ } elseif ($fieldName == 'flag_verified') {
+ $ms->addMessageTranslated('success', 'MANUALLY_ACTIVATED', [
+ 'user_name' => $user->user_name
+ ]);
+ } else {
+ $ms->addMessageTranslated('success', 'DETAILS_UPDATED', [
+ 'user_name' => $user->user_name
+ ]);
+ }
+
+ return $response->withStatus(200);
+ }
+
+ protected function getUserFromParams($params) {
+ // Load the request schema
+ $schema = new RequestSchema('schema://requests/user/get-by-username.yaml');
+
+ // Whitelist and set parameter defaults
+ $transformer = new RequestDataTransformer($schema);
+ $data = $transformer->transform($params);
+
+ // Validate, and throw exception on validation errors.
+ $validator = new ServerSideValidator($schema, $this->ci->translator);
+ if (!$validator->validate($data)) {
+ // TODO: encapsulate the communication of error messages from ServerSideValidator to the BadRequestException
+ $e = new BadRequestException();
+ foreach ($validator->errors() as $idx => $field) {
+ foreach ($field as $eidx => $error) {
+ $e->addUserMessage($error);
+ }
+ }
+ throw $e;
+ }
+
+ /** @var UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
+ $classMapper = $this->ci->classMapper;
+
+ // Get the user to delete
+ $user = $classMapper->staticMethod('user', 'where', 'user_name', $data['user_name'])
+ ->first();
+
+ return $user;
+ }
+}
diff --git a/main/app/sprinkles/admin/src/ServicesProvider/ServicesProvider.php b/main/app/sprinkles/admin/src/ServicesProvider/ServicesProvider.php
new file mode 100755
index 0000000..061d90c
--- /dev/null
+++ b/main/app/sprinkles/admin/src/ServicesProvider/ServicesProvider.php
@@ -0,0 +1,84 @@
+extend('classMapper', function ($classMapper, $c) {
+ $classMapper->setClassMapping('activity_sprunje', 'UserFrosting\Sprinkle\Admin\Sprunje\ActivitySprunje');
+ $classMapper->setClassMapping('group_sprunje', 'UserFrosting\Sprinkle\Admin\Sprunje\GroupSprunje');
+ $classMapper->setClassMapping('permission_sprunje', 'UserFrosting\Sprinkle\Admin\Sprunje\PermissionSprunje');
+ $classMapper->setClassMapping('permission_user_sprunje', 'UserFrosting\Sprinkle\Admin\Sprunje\PermissionUserSprunje');
+ $classMapper->setClassMapping('role_sprunje', 'UserFrosting\Sprinkle\Admin\Sprunje\RoleSprunje');
+ $classMapper->setClassMapping('user_sprunje', 'UserFrosting\Sprinkle\Admin\Sprunje\UserSprunje');
+ $classMapper->setClassMapping('user_permission_sprunje', 'UserFrosting\Sprinkle\Admin\Sprunje\UserPermissionSprunje');
+ return $classMapper;
+ });
+
+ /**
+ * Returns a callback that handles setting the `UF-Redirect` header after a successful login.
+ *
+ * Overrides the service definition in the account Sprinkle.
+ */
+ $container['redirect.onLogin'] = function ($c) {
+ /**
+ * This method is invoked when a user completes the login process.
+ *
+ * Returns a callback that handles setting the `UF-Redirect` header after a successful login.
+ * @param \Psr\Http\Message\ServerRequestInterface $request
+ * @param \Psr\Http\Message\ResponseInterface $response
+ * @param array $args
+ * @return \Psr\Http\Message\ResponseInterface
+ */
+ return function (Request $request, Response $response, array $args) use ($c) {
+ // Backwards compatibility for the deprecated determineRedirectOnLogin service
+ if ($c->has('determineRedirectOnLogin')) {
+ $determineRedirectOnLogin = $c->determineRedirectOnLogin;
+
+ return $determineRedirectOnLogin($response)->withStatus(200);
+ }
+
+ /** @var UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager */
+ $authorizer = $c->authorizer;
+
+ $currentUser = $c->authenticator->user();
+
+ if ($authorizer->checkAccess($currentUser, 'uri_dashboard')) {
+ return $response->withHeader('UF-Redirect', $c->router->pathFor('dashboard'));
+ } elseif ($authorizer->checkAccess($currentUser, 'uri_account_settings')) {
+ return $response->withHeader('UF-Redirect', $c->router->pathFor('settings'));
+ } else {
+ return $response->withHeader('UF-Redirect', $c->router->pathFor('index'));
+ }
+ };
+ };
+ }
+}
diff --git a/main/app/sprinkles/admin/src/Sprunje/ActivitySprunje.php b/main/app/sprinkles/admin/src/Sprunje/ActivitySprunje.php
new file mode 100755
index 0000000..da4f0e3
--- /dev/null
+++ b/main/app/sprinkles/admin/src/Sprunje/ActivitySprunje.php
@@ -0,0 +1,80 @@
+classMapper->createInstance('activity');
+
+ return $query->joinUser();
+ }
+
+ /**
+ * Filter LIKE the user info.
+ *
+ * @param Builder $query
+ * @param mixed $value
+ * @return $this
+ */
+ protected function filterUser($query, $value)
+ {
+ // Split value on separator for OR queries
+ $values = explode($this->orSeparator, $value);
+ $query->where(function ($query) use ($values) {
+ foreach ($values as $value) {
+ $query->orLike('users.first_name', $value)
+ ->orLike('users.last_name', $value)
+ ->orLike('users.email', $value);
+ }
+ });
+ return $this;
+ }
+
+ /**
+ * Sort based on user last name.
+ *
+ * @param Builder $query
+ * @param string $direction
+ * @return $this
+ */
+ protected function sortUser($query, $direction)
+ {
+ $query->orderBy('users.last_name', $direction);
+ return $this;
+ }
+}
diff --git a/main/app/sprinkles/admin/src/Sprunje/GroupSprunje.php b/main/app/sprinkles/admin/src/Sprunje/GroupSprunje.php
new file mode 100755
index 0000000..7c75691
--- /dev/null
+++ b/main/app/sprinkles/admin/src/Sprunje/GroupSprunje.php
@@ -0,0 +1,42 @@
+classMapper->createInstance('group')->newQuery();
+ }
+}
diff --git a/main/app/sprinkles/admin/src/Sprunje/PermissionSprunje.php b/main/app/sprinkles/admin/src/Sprunje/PermissionSprunje.php
new file mode 100755
index 0000000..c1803f1
--- /dev/null
+++ b/main/app/sprinkles/admin/src/Sprunje/PermissionSprunje.php
@@ -0,0 +1,93 @@
+classMapper->createInstance('permission')->newQuery();
+ }
+
+ /**
+ * Filter LIKE the slug, conditions, or description.
+ *
+ * @param Builder $query
+ * @param mixed $value
+ * @return $this
+ */
+ protected function filterInfo($query, $value)
+ {
+ return $this->filterProperties($query, $value);
+ }
+
+ /**
+ * Filter LIKE the slug, conditions, or description.
+ *
+ * @param Builder $query
+ * @param mixed $value
+ * @return $this
+ */
+ protected function filterProperties($query, $value)
+ {
+ // Split value on separator for OR queries
+ $values = explode($this->orSeparator, $value);
+ $query->where(function ($query) use ($values) {
+ foreach ($values as $value) {
+ $query->orLike('slug', $value)
+ ->orLike('conditions', $value)
+ ->orLike('description', $value);
+ }
+ });
+ return $this;
+ }
+
+ /**
+ * Sort based on slug.
+ *
+ * @param Builder $query
+ * @param string $direction
+ * @return $this
+ */
+ protected function sortProperties($query, $direction)
+ {
+ $query->orderBy('slug', $direction);
+ return $this;
+ }
+}
diff --git a/main/app/sprinkles/admin/src/Sprunje/PermissionUserSprunje.php b/main/app/sprinkles/admin/src/Sprunje/PermissionUserSprunje.php
new file mode 100755
index 0000000..242681d
--- /dev/null
+++ b/main/app/sprinkles/admin/src/Sprunje/PermissionUserSprunje.php
@@ -0,0 +1,48 @@
+options['permission_id'])) {
+ throw new BadRequestException();
+ }
+
+ $permission = $this->classMapper->staticMethod('permission', 'find', $this->options['permission_id']);
+
+ // If the permission doesn't exist, return 404
+ if (!$permission) {
+ throw new NotFoundException;
+ }
+
+ // Get permission users
+ $query = $permission->users()->withVia('roles_via');
+
+ return $query;
+ }
+}
diff --git a/main/app/sprinkles/admin/src/Sprunje/RoleSprunje.php b/main/app/sprinkles/admin/src/Sprunje/RoleSprunje.php
new file mode 100755
index 0000000..c5e0f8b
--- /dev/null
+++ b/main/app/sprinkles/admin/src/Sprunje/RoleSprunje.php
@@ -0,0 +1,67 @@
+classMapper->createInstance('role')->newQuery();
+ }
+
+ /**
+ * Filter LIKE name OR description.
+ *
+ * @param Builder $query
+ * @param mixed $value
+ * @return $this
+ */
+ protected function filterInfo($query, $value)
+ {
+ // Split value on separator for OR queries
+ $values = explode($this->orSeparator, $value);
+ $query->where(function ($query) use ($values) {
+ foreach ($values as $value) {
+ $query->orLike('name', $value)
+ ->orLike('description', $value);
+ }
+ });
+ return $this;
+ }
+}
diff --git a/main/app/sprinkles/admin/src/Sprunje/UserPermissionSprunje.php b/main/app/sprinkles/admin/src/Sprunje/UserPermissionSprunje.php
new file mode 100755
index 0000000..6142e74
--- /dev/null
+++ b/main/app/sprinkles/admin/src/Sprunje/UserPermissionSprunje.php
@@ -0,0 +1,48 @@
+options['user_id'])) {
+ throw new BadRequestException();
+ }
+
+ $user = $this->classMapper->staticMethod('user', 'find', $this->options['user_id']);
+
+ // If the user doesn't exist, return 404
+ if (!$user) {
+ throw new NotFoundException;
+ }
+
+ // Get user permissions
+ $query = $user->permissions()->withVia('roles_via');
+
+ return $query;
+ }
+}
diff --git a/main/app/sprinkles/admin/src/Sprunje/UserSprunje.php b/main/app/sprinkles/admin/src/Sprunje/UserSprunje.php
new file mode 100755
index 0000000..12378f9
--- /dev/null
+++ b/main/app/sprinkles/admin/src/Sprunje/UserSprunje.php
@@ -0,0 +1,185 @@
+classMapper->createInstance('user');
+
+ // Join user's most recent activity
+ return $query->joinLastActivity()->with('lastActivity');
+ }
+
+ /**
+ * Filter LIKE the last activity description.
+ *
+ * @param Builder $query
+ * @param mixed $value
+ * @return $this
+ */
+ protected function filterLastActivity($query, $value)
+ {
+ // Split value on separator for OR queries
+ $values = explode($this->orSeparator, $value);
+ $query->where(function ($query) use ($values) {
+ foreach ($values as $value) {
+ $query->orLike('activities.description', $value);
+ }
+ });
+ return $this;
+ }
+
+ /**
+ * Filter LIKE the first name, last name, or email.
+ *
+ * @param Builder $query
+ * @param mixed $value
+ * @return $this
+ */
+ protected function filterName($query, $value)
+ {
+ // Split value on separator for OR queries
+ $values = explode($this->orSeparator, $value);
+ $query->where(function ($query) use ($values) {
+ foreach ($values as $value) {
+ $query->orLike('first_name', $value)
+ ->orLike('last_name', $value)
+ ->orLike('email', $value);
+ }
+ });
+ return $this;
+ }
+
+ /**
+ * Filter by status (active, disabled, unactivated)
+ *
+ * @param Builder $query
+ * @param mixed $value
+ * @return $this
+ */
+ protected function filterStatus($query, $value)
+ {
+ // Split value on separator for OR queries
+ $values = explode($this->orSeparator, $value);
+ $query->where(function ($query) use ($values) {
+ foreach ($values as $value) {
+ if ($value == 'disabled') {
+ $query->orWhere('flag_enabled', 0);
+ } elseif ($value == 'unactivated') {
+ $query->orWhere('flag_verified', 0);
+ } elseif ($value == 'active') {
+ $query->orWhere(function ($query) {
+ $query->where('flag_enabled', 1)->where('flag_verified', 1);
+ });
+ }
+ }
+ });
+ return $this;
+ }
+
+ /**
+ * Return a list of possible user statuses.
+ *
+ * @return array
+ */
+ protected function listStatus()
+ {
+ return [
+ [
+ 'value' => 'active',
+ 'text' => Translator::translate('ACTIVE')
+ ],
+ [
+ 'value' => 'unactivated',
+ 'text' => Translator::translate('UNACTIVATED')
+ ],
+ [
+ 'value' => 'disabled',
+ 'text' => Translator::translate('DISABLED')
+ ]
+ ];
+ }
+
+ /**
+ * Sort based on last activity time.
+ *
+ * @param Builder $query
+ * @param string $direction
+ * @return $this
+ */
+ protected function sortLastActivity($query, $direction)
+ {
+ $query->orderBy('activities.occurred_at', $direction);
+ return $this;
+ }
+
+ /**
+ * Sort based on last name.
+ *
+ * @param Builder $query
+ * @param string $direction
+ * @return $this
+ */
+ protected function sortName($query, $direction)
+ {
+ $query->orderBy('last_name', $direction);
+ return $this;
+ }
+
+ /**
+ * Sort active, unactivated, disabled
+ *
+ * @param Builder $query
+ * @param string $direction
+ * @return $this
+ */
+ protected function sortStatus($query, $direction)
+ {
+ $query->orderBy('flag_enabled', $direction)->orderBy('flag_verified', $direction);
+ return $this;
+ }
+}
diff --git a/main/app/sprinkles/admin/templates/forms/group.html.twig b/main/app/sprinkles/admin/templates/forms/group.html.twig
new file mode 100755
index 0000000..36d6632
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/forms/group.html.twig
@@ -0,0 +1,69 @@
+
+
+
diff --git a/main/app/sprinkles/admin/templates/forms/role.html.twig b/main/app/sprinkles/admin/templates/forms/role.html.twig
new file mode 100755
index 0000000..46a4477
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/forms/role.html.twig
@@ -0,0 +1,56 @@
+
+ {% include "forms/csrf.html.twig" %}
+
+
+
+ {% if 'name' not in form.fields.hidden %}
+
+ {% endif %}
+ {% if 'slug' not in form.fields.hidden %}
+
+ {% endif %}
+ {% if 'description' not in fields.hidden %}
+
+
+ {{translate("DESCRIPTION")}}
+ {{role.description}}
+
+
+ {% endif %}
+
+
+
+ {{form.submit_text}}
+
+
+ {{translate("CANCEL")}}
+
+
+
+
+
diff --git a/main/app/sprinkles/admin/templates/forms/user.html.twig b/main/app/sprinkles/admin/templates/forms/user.html.twig
new file mode 100755
index 0000000..3ee7fc9
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/forms/user.html.twig
@@ -0,0 +1,125 @@
+
+ {% include "forms/csrf.html.twig" %}
+
+
+
+ {% block user_form %}
+ {% if 'user_name' not in form.fields.hidden %}
+
+ {% endif %}
+ {% if 'group' not in form.fields.hidden %}
+
+ {% endif %}
+ {% if 'name' not in form.fields.hidden %}
+
+
+ {% endif %}
+ {% if 'email' not in form.fields.hidden %}
+
+ {% endif %}
+ {% if 'theme' not in form.fields.hidden %}
+
+ {% endif %}
+ {% if 'locale' not in form.fields.hidden %}
+
+ {% endif %}
+ {% endblock %}
+
+
+
+ {{form.submit_text}}
+
+
+ {{translate('CANCEL')}}
+
+
+
+
+
diff --git a/main/app/sprinkles/admin/templates/mail/password-create.html.twig b/main/app/sprinkles/admin/templates/mail/password-create.html.twig
new file mode 100755
index 0000000..854eb77
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/mail/password-create.html.twig
@@ -0,0 +1,19 @@
+{% block subject %}
+ {{site.title}} - please set a password for your new account
+{% endblock %}
+
+{% block body %}
+
+ Dear {{user.first_name}},
+
+
+ Someone has created an account for you with {{site.title}} ({{site.uri.public}}). Your username is {{user.user_name}} .
+
+
+ To access your account, you must first create a password by visiting: {{site.uri.public}}/account/set-password/confirm?token={{token}} . This link has been generated especially for you, and will expire in {{create_password_expiration}}. Do not share it with anyone!
+
+
+ With regards,
+ The {{site.title}} Team
+
+{% endblock %}
\ No newline at end of file
diff --git a/main/app/sprinkles/admin/templates/modals/confirm-clear-cache.html.twig b/main/app/sprinkles/admin/templates/modals/confirm-clear-cache.html.twig
new file mode 100755
index 0000000..e5457d3
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/modals/confirm-clear-cache.html.twig
@@ -0,0 +1,17 @@
+{% extends "modals/modal.html.twig" %}
+
+{% block modal_title %}{{translate("CACHE.CLEAR")}}{% endblock %}
+
+{% block modal_body %}
+
+ {% include "forms/csrf.html.twig" %}
+
+
+ {{translate("CACHE.CLEAR_CONFIRM")}}{{translate("DELETE_CANNOT_UNDONE")}}
+
+
+ {{translate("CACHE.CLEAR_CONFIRM_YES")}}
+ {{translate("CANCEL")}}
+
+
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/modals/confirm-delete-group.html.twig b/main/app/sprinkles/admin/templates/modals/confirm-delete-group.html.twig
new file mode 100755
index 0000000..7889a1e
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/modals/confirm-delete-group.html.twig
@@ -0,0 +1,17 @@
+{% extends "modals/modal.html.twig" %}
+
+{% block modal_title %}{{translate("GROUP.DELETE")}}{% endblock %}
+
+{% block modal_body %}
+
+ {% include "forms/csrf.html.twig" %}
+
+
+ {{translate("GROUP.DELETE_CONFIRM", {name: group.name})}}{{translate("DELETE_CANNOT_UNDONE")}}
+
+
+ {{translate("GROUP.DELETE_YES")}}
+ {{translate("CANCEL")}}
+
+
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/modals/confirm-delete-role.html.twig b/main/app/sprinkles/admin/templates/modals/confirm-delete-role.html.twig
new file mode 100755
index 0000000..618039b
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/modals/confirm-delete-role.html.twig
@@ -0,0 +1,17 @@
+{% extends "modals/modal.html.twig" %}
+
+{% block modal_title %}{{translate("ROLE.DELETE")}}{% endblock %}
+
+{% block modal_body %}
+
+ {% include "forms/csrf.html.twig" %}
+
+
+ {{translate("ROLE.DELETE_CONFIRM", {name: role.name})}}{{translate("DELETE_CANNOT_UNDONE")}}
+
+
+ {{translate("ROLE.DELETE_YES")}}
+ {{translate("CANCEL")}}
+
+
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/modals/confirm-delete-user.html.twig b/main/app/sprinkles/admin/templates/modals/confirm-delete-user.html.twig
new file mode 100755
index 0000000..ce86301
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/modals/confirm-delete-user.html.twig
@@ -0,0 +1,17 @@
+{% extends "modals/modal.html.twig" %}
+
+{% block modal_title %}{{translate("USER.DELETE")}}{% endblock %}
+
+{% block modal_body %}
+
+ {% include "forms/csrf.html.twig" %}
+
+
+ {{translate("USER.DELETE_CONFIRM", {name: user.user_name})}}{{translate("DELETE_CANNOT_UNDONE")}}
+
+
+ {{translate("USER.DELETE_YES")}}
+ {{translate("CANCEL")}}
+
+
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/modals/group.html.twig b/main/app/sprinkles/admin/templates/modals/group.html.twig
new file mode 100755
index 0000000..be2d98c
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/modals/group.html.twig
@@ -0,0 +1,7 @@
+{% extends "modals/modal.html.twig" %}
+
+{% block modal_title %}{{translate('GROUP')}}{% endblock %}
+
+{% block modal_body %}
+ {% include "forms/group.html.twig" %}
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/modals/role-manage-permissions.html.twig b/main/app/sprinkles/admin/templates/modals/role-manage-permissions.html.twig
new file mode 100755
index 0000000..3914d2e
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/modals/role-manage-permissions.html.twig
@@ -0,0 +1,94 @@
+{% extends "modals/modal.html.twig" %}
+
+{% block modal_title %}{{translate("PERMISSION.MANAGE")}}{% endblock %}
+
+{% block modal_size %}modal-lg{% endblock %}
+
+{% block modal_body %}
+
+ {% include "forms/csrf.html.twig" %}
+
+
+
+
+
+
+ {{translate("PERMISSION.UPDATE")}}
+
+
+ {{translate('CANCEL')}}
+
+
+
+
+{# This contains a series of
+
+
+{% endverbatim %}
+
+
+
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/modals/role.html.twig b/main/app/sprinkles/admin/templates/modals/role.html.twig
new file mode 100755
index 0000000..6346461
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/modals/role.html.twig
@@ -0,0 +1,7 @@
+{% extends "modals/modal.html.twig" %}
+
+{% block modal_title %}{{translate('ROLE')}}{% endblock %}
+
+{% block modal_body %}
+ {% include "forms/role.html.twig" %}
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/modals/user-manage-roles.html.twig b/main/app/sprinkles/admin/templates/modals/user-manage-roles.html.twig
new file mode 100755
index 0000000..b41c60b
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/modals/user-manage-roles.html.twig
@@ -0,0 +1,77 @@
+{% extends "modals/modal.html.twig" %}
+
+{% block modal_title %}{{translate("ROLE.MANAGE")}}{% endblock %}
+
+{% block modal_body %}
+
+ {% include "forms/csrf.html.twig" %}
+
+
+
+
+
+
+ {{translate("UPDATE")}}
+
+
+ {{translate('CANCEL')}}
+
+
+
+
+{# This contains a series of
+
+
+{% endverbatim %}
+
+
+
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/modals/user-set-password.html.twig b/main/app/sprinkles/admin/templates/modals/user-set-password.html.twig
new file mode 100755
index 0000000..922d4e2
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/modals/user-set-password.html.twig
@@ -0,0 +1,62 @@
+{% extends "modals/modal.html.twig" %}
+
+{% block modal_title %}{{translate("USER.ADMIN.CHANGE_PASSWORD")}}{% endblock %}
+
+{% block modal_body %}
+
+ {% include "forms/csrf.html.twig" %}
+
+
+
+
+
+
+
+
+
+
+ {{translate("USER.ADMIN.SEND_PASSWORD_LINK")}}
+
+
+
+
+
+
+
+ {{translate("USER.ADMIN.SET_PASSWORD")}}:
+
+
+
+
+
+
+
+ {{translate('SUBMIT')}}
+
+
+ {{translate('CANCEL')}}
+
+
+
+
+
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/modals/user.html.twig b/main/app/sprinkles/admin/templates/modals/user.html.twig
new file mode 100755
index 0000000..892fe4f
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/modals/user.html.twig
@@ -0,0 +1,7 @@
+{% extends "modals/modal.html.twig" %}
+
+{% block modal_title %}{{translate('USER')}}{% endblock %}
+
+{% block modal_body %}
+ {% include "forms/user.html.twig" %}
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/navigation/navbar.html.twig b/main/app/sprinkles/admin/templates/navigation/navbar.html.twig
new file mode 100755
index 0000000..b2cf699
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/navigation/navbar.html.twig
@@ -0,0 +1,15 @@
+{% block dashboard_navbar %}
+
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/navigation/sidebar-menu.html.twig b/main/app/sprinkles/admin/templates/navigation/sidebar-menu.html.twig
new file mode 100755
index 0000000..bde2674
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/navigation/sidebar-menu.html.twig
@@ -0,0 +1,43 @@
+{% block navigation %}
+ {% if checkAccess('uri_dashboard') %}
+
+ {{ translate("DASHBOARD") }}
+
+ {% endif %}
+ {% if checkAccess('uri_users') %}
+
+ {{ translate("USER", 2) }}
+
+ {% elseif checkAccess('uri_group', {
+ 'group': current_user.group
+ }) %}
+
+ {{ translate("GROUP.MANAGE") }}
+
+ {% endif %}
+ {% if checkAccess('uri_activities') %}
+
+ {{ translate("ACTIVITY", 2) }}
+
+ {% endif %}
+ {% if checkAccess('uri_roles') %}
+
+ {{ translate("ROLE", 2) }}
+
+ {% endif %}
+ {% if checkAccess('uri_permissions') %}
+
+ {{ translate("PERMISSION", 2) }}
+
+ {% endif %}
+ {% if checkAccess('uri_groups') %}
+
+ {{ translate("GROUP", 2) }}
+
+ {% endif %}
+ {% if checkAccess('update_site_config') %}
+
+ {{ translate("SITE.CONFIG.MANAGER") }}
+
+ {% endif %}
+{% endblock %}
\ No newline at end of file
diff --git a/main/app/sprinkles/admin/templates/navigation/sidebar-user.html.twig b/main/app/sprinkles/admin/templates/navigation/sidebar-user.html.twig
new file mode 100755
index 0000000..018e644
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/navigation/sidebar-user.html.twig
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
{{current_user.first_name}} {{current_user.last_name}}
+
{{current_user.group.name}}
+
+
\ No newline at end of file
diff --git a/main/app/sprinkles/admin/templates/navigation/sidebar.html.twig b/main/app/sprinkles/admin/templates/navigation/sidebar.html.twig
new file mode 100755
index 0000000..1b2939e
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/navigation/sidebar.html.twig
@@ -0,0 +1,10 @@
+{% block sidebar_user %}
+ {% include 'navigation/sidebar-user.html.twig' %}
+{% endblock %}
+
+{% block sidebar_menu %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/main/app/sprinkles/admin/templates/navigation/user-card.html.twig b/main/app/sprinkles/admin/templates/navigation/user-card.html.twig
new file mode 100755
index 0000000..36fdb4b
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/navigation/user-card.html.twig
@@ -0,0 +1,8 @@
+{% extends "@account/navigation/user-card.html.twig" %}
+
+{% block userCard_menu %}
+ {% if checkAccess('uri_dashboard') %}
+
{{translate("DASHBOARD")}}
+ {% endif %}
+ {{ parent() }}
+{% endblock %}
\ No newline at end of file
diff --git a/main/app/sprinkles/admin/templates/pages/abstract/dashboard.html.twig b/main/app/sprinkles/admin/templates/pages/abstract/dashboard.html.twig
new file mode 100755
index 0000000..2a53de4
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/pages/abstract/dashboard.html.twig
@@ -0,0 +1,87 @@
+{% extends "pages/abstract/base.html.twig" %}
+
+{% block stylesheets_page_group %}
+
+ {{ assets.css('css/admin') | raw }}
+{% endblock %}
+
+{% block body_attributes %}
+ {% if current_user.isMaster() %}
+ class="hold-transition skin-red sidebar-mini"
+ {% else %}
+ class="hold-transition skin-{{site.AdminLTE.skin}} sidebar-mini"
+ {% endif %}
+{% endblock %}
+
+{% block content %}
+ {# This needs to be here (early in the body) to make sure the animation doesn't fire #}
+
+
+
+
+
+
+ {% block navbar_logo %}
+
+
+ {{site.title}}
+
+ {% endblock %}
+
+
+
+
+
+ {% include "navigation/navbar.html.twig" %}
+
+
+
+
+
+
+
+
+ {% block content_header %}
+
+ {% endblock %}
+
+ {% block body_matter %}{% endblock %}
+
+
+
+
+
+ {% block footer %}
+ {% include "pages/partials/footer.html.twig" %}
+ {% endblock %}
+
+
+
+
+{% endblock %}
+
+{% block scripts_page_group %}
+ {{ assets.js('js/admin') | raw }}
+{% endblock %}
\ No newline at end of file
diff --git a/main/app/sprinkles/admin/templates/pages/activities.html.twig b/main/app/sprinkles/admin/templates/pages/activities.html.twig
new file mode 100755
index 0000000..bcbd9c6
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/pages/activities.html.twig
@@ -0,0 +1,46 @@
+{% extends "pages/abstract/dashboard.html.twig" %}
+
+{% block stylesheets_page %}
+
+ {{ assets.css('css/form-widgets') | raw }}
+{% endblock %}
+
+{# Overrides blocks in head of base template #}
+{% block page_title %}{{ translate("ACTIVITY", 2) }}{% endblock %}
+
+{% block page_description %}{{ translate("ACTIVITY.PAGE") }}.{% endblock %}
+
+{% block body_matter %}
+
+{% endblock %}
+{% block scripts_page %}
+
+
+
+
+ {{ assets.js('js/form-widgets') | raw }}
+
+
+ {{ assets.js('js/pages/activities') | raw }}
+
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/pages/dashboard.html.twig b/main/app/sprinkles/admin/templates/pages/dashboard.html.twig
new file mode 100755
index 0000000..f9c85a3
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/pages/dashboard.html.twig
@@ -0,0 +1,282 @@
+{% extends "pages/abstract/dashboard.html.twig" %}
+
+{# Overrides blocks in head of base template #}
+{% block page_title %}{{ translate("DASHBOARD") }}{% endblock %}
+{% block page_description %}{% endblock %}
+
+{% block body_matter %}
+
+ {% if checkAccess('uri_users') %}
+
+
+
+ {% elseif checkAccess('uri_group', {
+ 'group': current_user.group
+ }) %}
+
+
+
+
+
+
{{current_user.group.name}}
+
+
+
+
+
+
+
+
+
+
+ {{ translate("USER", 2) }}
+ {{current_user.group.users.count}}
+
+
+
+
+
+
+
+
+ {% endif %}
+
+
+
+ {% if checkAccess('uri_users') or checkAccess('view_system_info') %}
+
+ {% if checkAccess('uri_users') %}
+
+
+ {% endif %}
+
+ {% if checkAccess('view_system_info') %}
+
+
+
+
+
+
+
+ {{translate("SYSTEM_INFO.UF_VERSION")}}
+ {{info.version.UF}}
+
+ {{translate("SYSTEM_INFO.PHP_VERSION")}}
+ {{info.version.php}}
+
+ {{translate("SYSTEM_INFO.SERVER")}}
+ {{info.environment.SERVER_SOFTWARE}}
+
+ {{translate("SYSTEM_INFO.DB_VERSION")}}
+ {{info.version.database.type}} {{info.version.database.version}}
+
+ {{translate("SYSTEM_INFO.DB_NAME")}}
+ {{info.database.name}}
+
+ {{translate("SYSTEM_INFO.DIRECTORY")}}
+ {{info.path.project}}
+
+ {{translate("SYSTEM_INFO.URL")}}
+ {{site.uri.public}}
+
+ {{translate("SYSTEM_INFO.SPRINKLES")}}
+
+
+ {% for sprinkle in sprinkles %}
+
+ {{sprinkle}}
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% endif %}
+
+
+ {% endif %}
+
+ {% if checkAccess('uri_activities') %}
+
+ {% elseif checkAccess('view_group_field', {
+ 'group': current_user.group,
+ 'property': 'users'
+ }) %}
+
+ {% else %}
+
+
+ {% endif %}
+
+
+{% endblock %}
+
+{% block scripts_page %}
+
+
+
+
+ {{ assets.js('js/pages/dashboard') | raw }}
+
+{% endblock %}
\ No newline at end of file
diff --git a/main/app/sprinkles/admin/templates/pages/group.html.twig b/main/app/sprinkles/admin/templates/pages/group.html.twig
new file mode 100755
index 0000000..bf4d275
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/pages/group.html.twig
@@ -0,0 +1,106 @@
+{% extends "pages/abstract/dashboard.html.twig" %}
+
+{% block stylesheets_page %}
+
+ {{ assets.css('css/form-widgets') | raw }}
+{% endblock %}
+
+{# Overrides blocks in head of base template #}
+{% block page_title %}{{ translate("GROUP", 2) }} | {{group.name}}{% endblock %}
+
+{% block page_description %}{{ translate("GROUP.INFO_PAGE", {name: group.name}) }}{% endblock %}
+
+{% block body_matter %}
+
+
+
+
+
+
+
+
+
+
{{group.name}}
+
+ {% if 'description' not in fields.hidden %}
+
+ {{group.description}}
+
+ {% endif %}
+ {% if 'users' not in fields.hidden %}
+
+
{{ translate('USER', 2)}}
+
+ {{group.users.count}}
+
+ {% endif %}
+ {% block group_profile %}{% endblock %}
+
+
+
+
+
+{% endblock %}
+{% block scripts_page %}
+
+
+
+
+ {{ assets.js('js/form-widgets') | raw }}
+
+
+ {{ assets.js('js/pages/group') | raw }}
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/pages/groups.html.twig b/main/app/sprinkles/admin/templates/pages/groups.html.twig
new file mode 100755
index 0000000..35e9a88
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/pages/groups.html.twig
@@ -0,0 +1,52 @@
+{% extends "pages/abstract/dashboard.html.twig" %}
+
+{% block stylesheets_page %}
+
+ {{ assets.css('css/form-widgets') | raw }}
+{% endblock %}
+
+{# Overrides blocks in head of base template #}
+{% block page_title %}{{ translate("GROUP", 2) }}{% endblock %}
+
+{% block page_description %}{{ translate("GROUP.PAGE_DESCRIPTION") }}{% endblock %}
+
+{% block body_matter %}
+
+{% endblock %}
+{% block scripts_page %}
+
+
+
+
+ {{ assets.js('js/form-widgets') | raw }}
+
+
+ {{ assets.js('js/pages/groups') | raw }}
+
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/pages/permission.html.twig b/main/app/sprinkles/admin/templates/pages/permission.html.twig
new file mode 100755
index 0000000..6adc014
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/pages/permission.html.twig
@@ -0,0 +1,91 @@
+{% extends "pages/abstract/dashboard.html.twig" %}
+
+{% block stylesheets_page %}
+
+ {{ assets.css('css/form-widgets') | raw }}
+{% endblock %}
+
+{# Overrides blocks in head of base template #}
+{% block page_title %}{{translate("PERMISSION", 2)}} | {{permission.name}}{% endblock %}
+
+{% block page_description %}{{translate("PERMISSION.INFO_PAGE", {name: permission.name})}}{% endblock %}
+
+{% block body_matter %}
+
+
+
+
+
+
+
+
+
+
{{permission.name}}
+
+
+ {{permission.description}}
+
+
+
{{translate("PERMISSION.ID")}}:
+
+ {{permission.id}}
+
+
+
+
{{translate("SLUG_CONDITION")}}
+
+
+
+ {{permission.slug}}
+
+
+ ↳ {{permission.conditions}}
+
+
+
+
+
+
+
+{% endblock %}
+{% block scripts_page %}
+
+
+
+
+ {{ assets.js('js/form-widgets') | raw }}
+
+
+ {{ assets.js('js/pages/permission') | raw }}
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/pages/permissions.html.twig b/main/app/sprinkles/admin/templates/pages/permissions.html.twig
new file mode 100755
index 0000000..2696209
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/pages/permissions.html.twig
@@ -0,0 +1,45 @@
+{% extends "pages/abstract/dashboard.html.twig" %}
+
+{% block stylesheets_page %}
+
+ {{ assets.css('css/form-widgets') | raw }}
+{% endblock %}
+
+{# Overrides blocks in head of base template #}
+{% block page_title %}{{ translate("PERMISSION", 2)}}{% endblock %}
+
+{% block page_description %}{{ translate("PERMISSION.PAGE_DESCRIPTION")}}{% endblock %}
+
+{% block body_matter %}
+
+{% endblock %}
+{% block scripts_page %}
+
+
+
+
+ {{ assets.js('js/form-widgets') | raw }}
+
+
+ {{ assets.js('js/pages/permissions') | raw }}
+
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/pages/role.html.twig b/main/app/sprinkles/admin/templates/pages/role.html.twig
new file mode 100755
index 0000000..daf1004
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/pages/role.html.twig
@@ -0,0 +1,129 @@
+{% extends "pages/abstract/dashboard.html.twig" %}
+
+{% block stylesheets_page %}
+
+ {{ assets.css('css/form-widgets') | raw }}
+{% endblock %}
+
+{# Overrides blocks in head of base template #}
+{% block page_title %}{{translate("ROLE", 2)}} | {{role.name}}{% endblock %}
+
+{% block page_description %}{{translate("ROLE.INFO_PAGE", {name: role.name})}}{% endblock %}
+
+{% block body_matter %}
+
+
+
+
+
+
+
+
+
+
{{role.name}}
+
+ {% if 'description' not in fields.hidden %}
+
+ {{role.description}}
+
+ {% endif %}
+ {% if 'users' not in fields.hidden %}
+
+
{{ translate('USER', 2)}}
+
+ {{role.users.count}}
+
+ {% endif %}
+
+
+
+
+
+
+{% endblock %}
+{% block scripts_page %}
+
+
+
+
+ {{ assets.js('js/form-widgets') | raw }}
+
+
+ {{ assets.js('js/pages/role') | raw }}
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/pages/roles.html.twig b/main/app/sprinkles/admin/templates/pages/roles.html.twig
new file mode 100755
index 0000000..c5b3995
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/pages/roles.html.twig
@@ -0,0 +1,50 @@
+{% extends "pages/abstract/dashboard.html.twig" %}
+
+{% block stylesheets_page %}
+
+ {{ assets.css('css/form-widgets') | raw }}
+{% endblock %}
+
+{# Overrides blocks in head of base template #}
+{% block page_title %}{{ translate("ROLE", 2)}}{% endblock %}
+
+{% block page_description %}{{ translate("ROLE.PAGE_DESCRIPTION")}}{% endblock %}
+
+{% block body_matter %}
+
+{% endblock %}
+{% block scripts_page %}
+
+
+
+
+ {{ assets.js('js/form-widgets') | raw }}
+
+
+ {{ assets.js('js/pages/roles') | raw }}
+
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/pages/user.html.twig b/main/app/sprinkles/admin/templates/pages/user.html.twig
new file mode 100755
index 0000000..d9c9ab2
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/pages/user.html.twig
@@ -0,0 +1,195 @@
+{% extends "pages/abstract/dashboard.html.twig" %}
+
+{% block stylesheets_page %}
+
+ {{ assets.css('css/form-widgets') | raw }}
+{% endblock %}
+
+{# Overrides blocks in head of base template #}
+{% block page_title %}{{ translate("USER", 2)}} | {{user.full_name}}{% endblock %}
+
+{% block page_description %}{{ translate("USER.INFO_PAGE", {name: user.user_name}) }}{% endblock %}
+
+{% block body_matter %}
+ {% block group_box %}
+ {% endblock %}
+
+
+
+ {% block user_box %}
+
+
+
+
+
+
{{user.full_name}}
+
+ {% if user.flag_enabled == 0 %}
+
+ {% endif %}
+ {% if user.flag_verified == 0 %}
+
+ {% endif %}
+
+
{{user.user_name}}{% if 'group' not in fields.hidden %} • {{user.group.name}}{% endif %}
+
+ {% if 'email' not in fields.hidden %}
+
+
{{translate("EMAIL")}}
+
+
+ {{user.email}}
+
+ {% endif %}
+
+ {% if 'locale' not in fields.hidden %}
+
+
{{translate("LOCALE")}}
+
+ {{locales[user.locale]}}
+
+ {% endif %}
+
+ {% block user_profile %}{% endblock %}
+
+ {% if 'roles' not in fields.hidden %}
+
+
{{translate("ROLE", 2)}}
+
+ {% for role in user.roles %}
+ {{role.name}}
+ {% endfor %}
+
+ {% endif %}
+
+
+ {% endblock %}
+
+
+ {% if 'activities' not in widgets.hidden %}
+
+ {% block activity_box %}
+
+ {% endblock %}
+
+ {% endif %}
+
+ {% if 'permissions' not in widgets.hidden %}
+
+ {% endif %}
+{% endblock %}
+{% block scripts_page %}
+
+
+
+
+ {{ assets.js('js/form-widgets') | raw }}
+
+
+ {{ assets.js('js/pages/user') | raw }}
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/pages/users.html.twig b/main/app/sprinkles/admin/templates/pages/users.html.twig
new file mode 100755
index 0000000..3e4642d
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/pages/users.html.twig
@@ -0,0 +1,53 @@
+{% extends "pages/abstract/dashboard.html.twig" %}
+
+{% block stylesheets_page %}
+
+ {{ assets.css('css/form-widgets') | raw }}
+{% endblock %}
+
+{# Overrides blocks in head of base template #}
+{% block page_title %}{{ translate("USER", 2)}}{% endblock %}
+
+{% block page_description %}{{ translate("USER.PAGE_DESCRIPTION")}}{% endblock %}
+
+{% block body_matter %}
+
+{% endblock %}
+{% block scripts_page %}
+
+
+
+
+ {{ assets.js('js/form-widgets') | raw }}
+
+
+ {{ assets.js('js/pages/users') | raw }}
+
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/tables/activities.html.twig b/main/app/sprinkles/admin/templates/tables/activities.html.twig
new file mode 100755
index 0000000..d70541b
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/tables/activities.html.twig
@@ -0,0 +1,73 @@
+{# This partial template renders a table of user activities, to be populated with rows via an AJAX request.
+ # This extends a generic template for paginated tables.
+ #
+ # Note that this template contains a "skeleton" table with an empty table body, and then a block of Handlebars templates which are used
+ # to render the table cells with the data from the AJAX request.
+#}
+
+{% extends "tables/table-paginated.html.twig" %}
+
+{% block table %}
+
+
+
+ {{translate('ACTIVITY.TIME')}}
+ {% if 'user' in table.columns %}
+ {{translate('USER')}}
+ {% endif %}
+ {{translate("DESCRIPTION")}}
+
+
+
+
+
+{% endblock %}
+
+{% block table_cell_templates %}
+ {# This contains a series of
+
+
+
+
+ {% endverbatim %}
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/tables/groups.html.twig b/main/app/sprinkles/admin/templates/tables/groups.html.twig
new file mode 100755
index 0000000..2c5a84a
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/tables/groups.html.twig
@@ -0,0 +1,69 @@
+{# This partial template renders a table of groups, to be populated with rows via an AJAX request.
+ # This extends a generic template for paginated tables.
+ #
+ # Note that this template contains a "skeleton" table with an empty table body, and then a block of Handlebars templates which are used
+ # to render the table cells with the data from the AJAX request.
+#}
+
+{% extends "tables/table-paginated.html.twig" %}
+
+{% block table %}
+
+
+
+ {{translate('GROUP')}}
+ {{translate("DESCRIPTION")}}
+ {{translate("ACTIONS")}}
+
+
+
+
+
+{% endblock %}
+
+{% block table_cell_templates %}
+ {# This contains a series of
+
+
+
+
+ {% endverbatim %}
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/tables/permissions.html.twig b/main/app/sprinkles/admin/templates/tables/permissions.html.twig
new file mode 100755
index 0000000..92e236a
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/tables/permissions.html.twig
@@ -0,0 +1,66 @@
+{# This partial template renders a table of permissions, to be populated with rows via an AJAX request.
+ # This extends a generic template for paginated tables.
+ #
+ # Note that this template contains a "skeleton" table with an empty table body, and then a block of Handlebars templates which are used
+ # to render the table cells with the data from the AJAX request.
+#}
+
+{% extends "tables/table-paginated.html.twig" %}
+
+{% block table %}
+
+
+
+ {{translate('PERMISSION')}}
+ {{translate('SLUG_CONDITION')}}
+ {% if 'via_roles' in table.columns %}
+ {{translate('PERMISSION.VIA_ROLES')}}
+ {% endif %}
+
+
+
+
+
+{% endblock %}
+
+{% block table_cell_templates %}
+ {# This contains a series of
+
+
+
+
+ {% endverbatim %}
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/tables/roles.html.twig b/main/app/sprinkles/admin/templates/tables/roles.html.twig
new file mode 100755
index 0000000..dbdb49e
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/tables/roles.html.twig
@@ -0,0 +1,74 @@
+{# This partial template renders a table of roles, to be populated with rows via an AJAX request.
+ # This extends a generic template for paginated tables.
+ #
+ # Note that this template contains a "skeleton" table with an empty table body, and then a block of Handlebars templates which are used
+ # to render the table cells with the data from the AJAX request.
+#}
+
+{% extends "tables/table-paginated.html.twig" %}
+
+{% block table %}
+
+
+
+ {{translate('ROLE')}}
+ {{translate('DESCRIPTION')}}
+ {{translate('ACTIONS')}}
+
+
+
+
+
+{% endblock %}
+
+{% block table_cell_templates %}
+ {# This contains a series of
+
+
+
+
+ {% endverbatim %}
+{% endblock %}
diff --git a/main/app/sprinkles/admin/templates/tables/users.html.twig b/main/app/sprinkles/admin/templates/tables/users.html.twig
new file mode 100755
index 0000000..1cebb47
--- /dev/null
+++ b/main/app/sprinkles/admin/templates/tables/users.html.twig
@@ -0,0 +1,149 @@
+{# This partial template renders a table of users, to be populated with rows via an AJAX request.
+ # This extends a generic template for paginated tables.
+ #
+ # Note that this template contains a "skeleton" table with an empty table body, and then a block of Handlebars templates which are used
+ # to render the table cells with the data from the AJAX request.
+#}
+
+{% extends "tables/table-paginated.html.twig" %}
+
+{% block table %}
+
+
+
+ {{translate('USER')}}
+ {% if 'last_activity' in table.columns %}
+ {{translate("ACTIVITY.LAST")}}
+ {% endif %}
+ {% if 'via_roles' in table.columns %}
+ {{translate('PERMISSION.VIA_ROLES')}}
+ {% endif %}
+ {{translate("STATUS")}}
+ {{translate("ACTIONS")}}
+
+
+
+
+
+{% endblock %}
+
+{% block table_cell_templates %}
+ {# This contains a series of
+
+
+
+
+
+
+
+ {% endverbatim %}
+{% endblock %}
diff --git a/main/app/sprinkles/admin/tests/Integration/SprunjeTests.php b/main/app/sprinkles/admin/tests/Integration/SprunjeTests.php
new file mode 100755
index 0000000..9eb4122
--- /dev/null
+++ b/main/app/sprinkles/admin/tests/Integration/SprunjeTests.php
@@ -0,0 +1,111 @@
+classMapper = new ClassMapper();
+ }
+
+ /**
+ * Tests...
+ */
+ public function testUserPermissionSprunje()
+ {
+ $fm = $this->ci->factory;
+
+ // Generate some test models
+ $users = $fm->seed(3, 'UserFrosting\Sprinkle\Account\Database\Models\User');
+ $roles = $fm->seed(3, 'UserFrosting\Sprinkle\Account\Database\Models\Role');
+ $permissions = $fm->seed(3, 'UserFrosting\Sprinkle\Account\Database\Models\Permission');
+
+ // Create some relationships
+ $roles[0]->permissions()->attach($permissions[1]);
+ $roles[0]->permissions()->attach($permissions[2]);
+ $roles[1]->permissions()->attach($permissions[2]);
+ $roles[2]->permissions()->attach($permissions[0]);
+ $roles[2]->permissions()->attach($permissions[1]);
+
+ $users[0]->roles()->attach($roles[1]);
+ $users[0]->roles()->attach($roles[2]);
+ $users[1]->roles()->attach($roles[0]);
+ $users[1]->roles()->attach($roles[1]);
+ $users[2]->roles()->attach($roles[1]);
+
+ $this->classMapper->setClassMapping('user', 'UserFrosting\Sprinkle\Account\Database\Models\User');
+
+ // Test user 0
+ $sprunje = new UserPermissionSprunje($this->classMapper, [
+ 'user_id' => $users[0]->id
+ ]);
+
+ list($count, $countFiltered, $models) = $sprunje->getModels();
+
+ // Check that counts are correct
+ $this->assertEquals(count($models), $count);
+ $this->assertEquals(count($models), $countFiltered);
+
+ // Ignore pivot and roles_via. These are covered by the tests for the relationships themselves.
+ static::ignoreRelations($models);
+ $this->assertCollectionsSame(collect($permissions), $models);
+
+ // Test user 1
+ $sprunje = new UserPermissionSprunje($this->classMapper, [
+ 'user_id' => $users[1]->id
+ ]);
+
+ list($count, $countFiltered, $models) = $sprunje->getModels();
+
+ // Check that counts are correct
+ $this->assertEquals(count($models), $count);
+ $this->assertEquals(count($models), $countFiltered);
+
+ // Ignore pivot and roles_via. These are covered by the tests for the relationships themselves.
+ static::ignoreRelations($models);
+ $this->assertCollectionsSame(collect([
+ $permissions[1],
+ $permissions[2]
+ ]), $models);
+
+ // Test user 2
+ $sprunje = new UserPermissionSprunje($this->classMapper, [
+ 'user_id' => $users[2]->id
+ ]);
+
+ list($count, $countFiltered, $models) = $sprunje->getModels();
+
+ // Check that counts are correct
+ $this->assertEquals(count($models), $count);
+ $this->assertEquals(count($models), $countFiltered);
+
+ // Ignore pivot and roles_via. These are covered by the tests for the relationships themselves.
+ static::ignoreRelations($models);
+ $this->assertCollectionsSame(collect([
+ $permissions[2]
+ ]), $models);
+ }
+}
diff --git a/main/app/sprinkles/core/asset-bundles.json b/main/app/sprinkles/core/asset-bundles.json
new file mode 100755
index 0000000..053fffe
--- /dev/null
+++ b/main/app/sprinkles/core/asset-bundles.json
@@ -0,0 +1,113 @@
+{
+ "bundle": {
+ "js/main": {
+ "scripts": [
+ "vendor/bootstrap/dist/js/bootstrap.js",
+ "vendor/handlebars/handlebars.js",
+ "vendor/jquery-validation/dist/jquery.validate.js",
+ "vendor/jquery-validation/dist/additional-methods.js",
+ "vendor/jquery-slimscroll/jquery.slimscroll.js",
+ "vendor/icheck/icheck.min.js",
+ "vendor/fastclick/lib/fastclick.js",
+ "vendor/select2/dist/js/select2.full.js",
+ "vendor/clipboard/dist/clipboard.js",
+ "userfrosting/js/attrchange.js",
+ "userfrosting/js/AdminLTE.js",
+ "userfrosting/js/AdminLTE-custom.js",
+ "userfrosting/js/fortress-jqueryvalidation-methods.js",
+ "userfrosting/js/uf-jqueryvalidation-config.js",
+ "userfrosting/js/uf-alerts.js",
+ "userfrosting/js/uf-form.js",
+ "userfrosting/js/uf-modal.js",
+ "userfrosting/js/uf-copy.js",
+ "userfrosting/js/uf-init.js"
+ ],
+ "options": {
+ "result": {
+ "type": {
+ "scripts": "plain"
+ }
+ }
+ }
+ },
+ "js/form-widgets": {
+ "scripts": [
+ "vendor/speakingurl/speakingurl.min.js",
+ "userfrosting/js/uf-collection.js"
+ ],
+ "options": {
+ "result": {
+ "type": {
+ "scripts": "plain"
+ }
+ }
+ }
+ },
+ "js/main-site": {
+ "scripts": [
+ "SiteAssets/js/jquery.js",
+ "SiteAssets/js/fontawesome.js",
+ "SiteAssets/js/modernizr.js",
+ "SiteAssets/js/linkify.js",
+ "SiteAssets/js/language.js",
+ "SiteAssets/js/encryption.js",
+ "SiteAssets/js/chat.js",
+ "SiteAssets/js/slick.js",
+ "SiteAssets/js/main.js"
+ ],
+ "options": {
+ "result": {
+ "type": {
+ "scripts": "plain"
+ }
+ }
+ }
+ },
+ "css/main": {
+ "styles": [
+ "vendor/font-awesome/css/font-awesome.css",
+ "vendor/bootstrap/dist/css/bootstrap.css",
+ "vendor/select2/dist/css/select2.css",
+ "vendor/icheck/skins/square/_all.css",
+ "vendor/ionicons/css/ionicons.css",
+ "userfrosting/css/uf-jqueryvalidation.css",
+ "userfrosting/css/uf-alerts.css",
+ "userfrosting/css/AdminLTE.css",
+ "userfrosting/css/AdminLTE-skins-all.css",
+ "userfrosting/css/userfrosting.css"
+ ],
+ "options": {
+ "result": {
+ "type": {
+ "styles": "plain"
+ }
+ }
+ }
+ },
+ "css/form-widgets": {
+ "styles": [
+ "userfrosting/css/uf-collection.css"
+ ],
+ "options": {
+ "result": {
+ "type": {
+ "styles": "plain"
+ }
+ }
+ }
+ },
+ "css/main-site": {
+ "styles": [
+ "SiteAssets/css/slick.css",
+ "SiteAssets/css/main.css"
+ ],
+ "options": {
+ "result": {
+ "type": {
+ "styles": "plain"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/css/main.css b/main/app/sprinkles/core/assets/SiteAssets/css/main.css
new file mode 100644
index 0000000..68bb4a3
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/css/main.css
@@ -0,0 +1,250 @@
+/******
+GENERAL
+******/
+* {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-overflow-scrolling: touch;
+
+ outline: none;
+ ie-dummy: expression(this.hideFocus=true);
+}
+
+::-webkit-scrollbar {
+ width: 0;
+ background: transparent;
+}
+
+body {
+ font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
+ height: 100%;
+ width: 100%;
+ overflow: hidden;
+ background-color: #0c1d34;
+ color: #426A91;
+ margin: 0;
+ padding: 0;
+ border: 0;
+}
+
+.main {
+ height: 100%;
+}
+
+a {
+ text-decoration: none;
+ color: #4a93c0;
+}
+
+hr {
+ width: 100%;
+ display: block;
+ height: 1px;
+ border: 0;
+ border-top: 1px solid #112a42;
+ margin: 1em 0;
+ padding: 0;
+}
+
+/*******
+FLICKITY
+*******/
+.main-carousel {
+ background: #EEE;
+}
+
+.carousel-cell {
+ width: 100%;
+ height: calc(100vh - 75px);
+ height: -moz-calc(100vh - 75px);
+ height: -webkit-calc(100vh - 75px);
+ height: -o-calc(100vh - 75px);
+ height: calc(100vh - 75px);
+ background: #0c1d34;
+ counter-increment: carousel-cell;
+}
+
+/******
+HEADER
+******/
+.header {
+ display: flex;
+ flex-wrap: nowrap;
+ justify-content: space-between;
+ align-content: center;
+ margin: 20px;
+}
+
+.LeftButtonHeader {
+ width: 20px;
+ height: 20px;
+ -webkit-filter: invert(.5);
+ -moz-filter: invert(.5);
+}
+
+.HeaderCaption {
+ color: lightgrey;
+}
+
+.RightButtonHeader {
+ width: 20px;
+ height: 20px;
+ -webkit-filter: invert(.5);
+ -moz-filter: invert(.5);
+}
+
+/***********
+GENERAL TABS
+***********/
+.MainInTab {
+ height: 100%;
+}
+
+/**********
+CHAT WINDOW
+**********/
+.ChatWindow {
+ position: relative;
+ height: 100%;
+ margin: 5px;
+}
+
+.ChatMessages {
+ overflow-y: scroll;
+ max-height: calc(100% - 135px); /* navbar + input + some margin*/
+ max-height: -moz-calc(100% - 135px);
+ max-height: -webkit-calc(100% - 135px);
+ max-height: -o-calc(100% - 135px);
+ height: 100%;
+ width: 100%;
+}
+
+.ChatMessage {
+ display: block;
+ position: relative;
+ min-width: 50px;
+ max-width: 50%;
+ width: auto;
+ height: auto;
+ word-wrap: break-word;
+ text-align: center;
+ padding: 10px;
+}
+
+.ServerChatMessage {
+ display: inline-block;
+ position: relative;
+ left: 50%;
+ transform: translateX(-50%);
+ text-align: center;
+ padding: 5px;
+ -webkit-border-radius: 25px;
+ -moz-border-radius: 25px;
+ border-radius: 25px;
+ background: linear-gradient(to right, #ad4eff, #7c41f9);
+ color: #FFF;
+ font-size: x-small;
+}
+
+.MessageSent {
+ float: right;
+ background-color: #12213b;
+}
+
+.AloneMessage {
+ -webkit-border-radius: 25px;
+ -moz-border-radius: 25px;
+ border-radius: 25px;
+}
+
+.TopMessage {
+ -webkit-border-radius: 25px 25px 10px 10px;
+ -moz-border-radius: 25px 25px 10px 10px;
+ border-radius: 25px 25px 10px 10px;
+}
+
+.MiddleMessage {
+ -webkit-border-radius: 10px;
+ -moz-border-radius: 10px;
+ border-radius: 10px;
+}
+
+.BottomMessage {
+ -webkit-border-radius: 10px 10px 25px 25px;
+ -moz-border-radius: 10px 10px 25px 25px;
+ border-radius: 10px 10px 25px 25px;
+}
+
+.MessageReceived {
+ float: left;
+ background-color: #13223c;
+}
+
+.ChatInput {
+ position: absolute;
+ margin: auto;
+ bottom: 90px; /* 75+15 */
+ left: 0;
+ z-index: 600;
+ width: 100%;
+ height: 40px;
+ border: none;
+ color: #FFF;
+ background-color: #13223C;
+}
+
+#ChatTextInput {
+ display: none;
+}
+
+/*****
+NAVBAR
+*****/
+.Navbar {
+ display: flex;
+ flex-wrap: nowrap;
+ justify-content: center;
+ align-content: center;
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ height: 75px;
+ width: 100%;
+ background-color: #13223c;
+}
+
+.NavbarIconWrap {
+ margin: auto;
+}
+
+.NavbarIconWrap img {
+ height: 30px;
+ width: 30px;
+ filter: invert(.5);
+ -webkit-filter: invert(.5);
+ -moz-filter: invert(.5);
+}
+
+.NavbarLine {
+ position: absolute;
+ bottom: 73px;
+ left: 0;
+ width: 20% !important;
+ height: 2px;
+ background: #eb12b5;
+}
+
+.ActiveTab {
+ -webkit-transition: -moz-transform .3s ease-out;
+ -moz-transition: -webkit-transform .3s ease-out;
+
+ filter: invert(19%) sepia(93%) saturate(4612%) hue-rotate(303deg) brightness(98%) contrast(101%);
+ -webkit-filter: invert(19%) sepia(93%) saturate(4612%) hue-rotate(303deg) brightness(98%) contrast(101%);
+ -moz-filter: invert(19%) sepia(93%) saturate(4612%) hue-rotate(303deg) brightness(98%) contrast(101%);
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/css/slick.css b/main/app/sprinkles/core/assets/SiteAssets/css/slick.css
new file mode 100644
index 0000000..f2465cb
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/css/slick.css
@@ -0,0 +1,2 @@
+.slick-slider{position:relative;display:block;box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-touch-callout:none;-khtml-user-select:none;-ms-touch-action:pan-y;touch-action:pan-y;-webkit-tap-highlight-color:transparent}.slick-list{position:relative;display:block;overflow:hidden;margin:0;padding:0}.slick-list:focus{outline:0}.slick-list.dragging{cursor:pointer;cursor:hand}.slick-slider .slick-list,.slick-slider .slick-track{-webkit-transform:translate3d(0,0,0);-moz-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);-o-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.slick-track{position:relative;top:0;left:0;display:block;margin-left:auto;margin-right:auto}.slick-track:after,.slick-track:before{display:table;content:''}.slick-track:after{clear:both}.slick-loading .slick-track{visibility:hidden}.slick-slide{display:none;float:left;height:100%;min-height:1px}[dir=rtl] .slick-slide{float:right}.slick-slide img{display:block}.slick-slide.slick-loading img{display:none}.slick-slide.dragging img{pointer-events:none}.slick-initialized .slick-slide{display:block}.slick-loading .slick-slide{visibility:hidden}.slick-vertical .slick-slide{display:block;height:auto;border:1px solid transparent}.slick-arrow.slick-hidden{display:none}
+/*# sourceMappingURL=slick.min.css.map */
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/icons/BurgerMenuShort.svg b/main/app/sprinkles/core/assets/SiteAssets/icons/BurgerMenuShort.svg
new file mode 100644
index 0000000..1bf78bd
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/icons/BurgerMenuShort.svg
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/main/app/sprinkles/core/assets/SiteAssets/icons/ExploreGlobeOutline.svg b/main/app/sprinkles/core/assets/SiteAssets/icons/ExploreGlobeOutline.svg
new file mode 100644
index 0000000..6778254
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/icons/ExploreGlobeOutline.svg
@@ -0,0 +1,210 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/main/app/sprinkles/core/assets/SiteAssets/icons/FriendFeedOutline.svg b/main/app/sprinkles/core/assets/SiteAssets/icons/FriendFeedOutline.svg
new file mode 100644
index 0000000..4243deb
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/icons/FriendFeedOutline.svg
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/main/app/sprinkles/core/assets/SiteAssets/icons/MessageBubbleOutline.svg b/main/app/sprinkles/core/assets/SiteAssets/icons/MessageBubbleOutline.svg
new file mode 100644
index 0000000..81ea5cc
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/icons/MessageBubbleOutline.svg
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/main/app/sprinkles/core/assets/SiteAssets/icons/UserGroupOutline.svg b/main/app/sprinkles/core/assets/SiteAssets/icons/UserGroupOutline.svg
new file mode 100644
index 0000000..ca8c11c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/icons/UserGroupOutline.svg
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/main/app/sprinkles/core/assets/SiteAssets/icons/UserOutline.svg b/main/app/sprinkles/core/assets/SiteAssets/icons/UserOutline.svg
new file mode 100644
index 0000000..6051129
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/icons/UserOutline.svg
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/main/app/sprinkles/core/assets/SiteAssets/js/chat.js b/main/app/sprinkles/core/assets/SiteAssets/js/chat.js
new file mode 100644
index 0000000..bcb910b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/js/chat.js
@@ -0,0 +1,117 @@
+/************
+ GENERATE KEYS
+ ************/
+if (localStorage.getItem('KeysGenerated') === null || localStorage.getItem('KeysGenerated') !== "true") {
+ // GENERATE -- LATER ON LOGIN!
+ var EncryptionPhrase = "PASSWORD 123"; // THE USERS PASSWORD
+ var RSABitLength = 1024;
+ var PrivateKeyString = cryptico.generateRSAKey(EncryptionPhrase, RSABitLength);
+ var PublicKeyString = cryptico.publicKeyString(PrivateKeyString);
+ // SAVE TO DATABASE
+ $.ajax({
+ type: "POST",
+ url: "assets/php/SavePublicKey.php",
+ data: {
+ UserID: "1", // TEMPORARY
+ PublicKeyString: PublicKeyString
+ },
+ async: true,
+ error: function () {
+ console.error("Error while saving public key to database!");
+ },
+ success: function () {
+ localStorage.setItem('KeysGenerated', "true");
+ }
+ });
+}
+
+
+/******
+ GENERAL
+ ******/
+
+var ChatTextInput = $("#ChatTextInput");
+var SubscribeTextInput = $("#SubscribeTextInput");
+var ChatMessages = $("#ChatMessages");
+
+var WebSocket = new WebSocket('wss://marvinborner.ddnss.de:1337');
+
+WebSocket.onopen = function () {
+ console.log("Chat connection established!");
+};
+
+WebSocket.onmessage = function (e) {
+ var LastMessage = $(".ChatMessage:last");
+ var MessageObject = JSON.parse(e.data);
+ if (MessageObject.ServerMessage === false) {
+ if (MessageObject.WasHimself === true) { //MessageObject.Username
+ if (!LastMessage.hasClass("MessageSent")) {
+ ChatMessages.append("
" + MessageObject.Message + "
");
+ } else if (LastMessage.hasClass("MessageSent")) {
+ if (LastMessage.hasClass("AloneMessage")) {
+ LastMessage.removeClass("AloneMessage");
+ LastMessage.addClass("TopMessage");
+ } else if (LastMessage.hasClass("BottomMessage")) {
+ LastMessage.removeClass("BottomMessage");
+ LastMessage.addClass("MiddleMessage");
+ }
+ ChatMessages.append("
" + MessageObject.Message + "
");
+ }
+ $('.MessageSent').linkify({
+ target: "_blank"
+ });
+ } else if (MessageObject.WasHimself === false) {
+ if (!LastMessage.hasClass("MessageReceived")) {
+ ChatMessages.append("
" + MessageObject.Message + "
");
+ } else if (LastMessage.hasClass("MessageReceived")) {
+ if (LastMessage.hasClass("AloneMessage")) {
+ LastMessage.removeClass("AloneMessage");
+ LastMessage.addClass("TopMessage");
+ } else if (LastMessage.hasClass("BottomMessage")) {
+ LastMessage.removeClass("BottomMessage");
+ LastMessage.addClass("MiddleMessage");
+ }
+ ChatMessages.append("
" + MessageObject.Message + "
");
+ }
+ $('.MessageReceived').linkify({
+ target: "_blank"
+ });
+ }
+ } else if (MessageObject.ServerMessage === true) {
+ if (MessageObject.ServerMessageType === "GroupJoin") {
+ if (MessageObject.WasHimself === false) {
+ ChatMessages.append("
" + MessageObject.Username + " .
");
+ } else if (MessageObject.WasHimself === true) {
+ ChatMessages.empty();
+ ChatMessages.append("
" + MessageObject.GroupName + " .
");
+ }
+ } else if (MessageObject.ServerMessageType === "UserDisconnect") {
+ ChatMessages.append("
" + MessageObject.Username + " .
");
+ }
+ }
+ initiateLanguage(); // need further work (performance)
+};
+
+ChatTextInput.keyup(function (e) {
+ if (e.keyCode === 13) {
+ sendMessage(ChatTextInput.val());
+ ChatTextInput.val("");
+ }
+});
+
+SubscribeTextInput.keyup(function (e) {
+ if (e.keyCode === 13) {
+ subscribe(SubscribeTextInput.val());
+ }
+});
+
+function subscribe(channel) {
+ WebSocket.send(JSON.stringify({ClientMessageType: "Subscribe", Channel: channel}));
+ SubscribeTextInput.hide();
+ ChatTextInput.show();
+}
+
+function sendMessage(msg) {
+ WebSocket.send(JSON.stringify({ClientMessageType: "Message", Message: msg}));
+ ChatTextInput.val("");
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/js/encryption.js b/main/app/sprinkles/core/assets/SiteAssets/js/encryption.js
new file mode 100644
index 0000000..7826bbb
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/js/encryption.js
@@ -0,0 +1,3407 @@
+// Copyright (c) 2005 Tom Wu
+// All Rights Reserved.
+// See "LICENSE" for details.
+// Basic JavaScript BN library - subset useful for RSA encryption.
+
+// Bits per digit
+var dbits;
+
+// JavaScript engine analysis
+var canary = 0xdeadbeefcafe;
+var j_lm = ((canary & 0xffffff) == 0xefcafe);
+
+// (public) Constructor
+
+function BigInteger(a, b, c) {
+ if (a != null) if ("number" === typeof a) this.fromNumber(a, b, c);
+ else if (b == null && "string" !== typeof a) this.fromString(a, 256);
+ else this.fromString(a, b);
+}
+
+// return new, unset BigInteger
+
+function nbi() {
+ return new BigInteger(null);
+}
+
+// am: Compute w_j += (x*this_i), propagate carries,
+// c is initial carry, returns final carry.
+// c < 3*dvalue, x < 2*dvalue, this_i < dvalue
+// We need to select the fastest one that works in this environment.
+// am1: use a single mult and divide to get the high bits,
+// max digit bits should be 26 because
+// max internal value = 2*dvalue^2-2*dvalue (< 2^53)
+
+function am1(i, x, w, j, c, n) {
+ while (--n >= 0) {
+ var v = x * this[i++] + w[j] + c;
+ c = Math.floor(v / 0x4000000);
+ w[j++] = v & 0x3ffffff;
+ }
+ return c;
+}
+
+// am2 avoids a big mult-and-extract completely.
+// Max digit bits should be <= 30 because we do bitwise ops
+// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31)
+
+function am2(i, x, w, j, c, n) {
+ var xl = x & 0x7fff,
+ xh = x >> 15;
+ while (--n >= 0) {
+ var l = this[i] & 0x7fff;
+ var h = this[i++] >> 15;
+ var m = xh * l + h * xl;
+ l = xl * l + ((m & 0x7fff) << 15) + w[j] + (c & 0x3fffffff);
+ c = (l >>> 30) + (m >>> 15) + xh * h + (c >>> 30);
+ w[j++] = l & 0x3fffffff;
+ }
+ return c;
+}
+
+// Alternately, set max digit bits to 28 since some
+// browsers slow down when dealing with 32-bit numbers.
+
+function am3(i, x, w, j, c, n) {
+ var xl = x & 0x3fff,
+ xh = x >> 14;
+ while (--n >= 0) {
+ var l = this[i] & 0x3fff;
+ var h = this[i++] >> 14;
+ var m = xh * l + h * xl;
+ l = xl * l + ((m & 0x3fff) << 14) + w[j] + c;
+ c = (l >> 28) + (m >> 14) + xh * h;
+ w[j++] = l & 0xfffffff;
+ }
+ return c;
+}
+
+if (j_lm && (navigator.appName == "Microsoft Internet Explorer")) {
+ BigInteger.prototype.am = am2;
+ dbits = 30;
+}
+else if (j_lm && (navigator.appName != "Netscape")) {
+ BigInteger.prototype.am = am1;
+ dbits = 26;
+}
+else { // Mozilla/Netscape seems to prefer am3
+ BigInteger.prototype.am = am3;
+ dbits = 28;
+}
+
+BigInteger.prototype.DB = dbits;
+BigInteger.prototype.DM = ((1 << dbits) - 1);
+BigInteger.prototype.DV = (1 << dbits);
+
+var BI_FP = 52;
+BigInteger.prototype.FV = Math.pow(2, BI_FP);
+BigInteger.prototype.F1 = BI_FP - dbits;
+BigInteger.prototype.F2 = 2 * dbits - BI_FP;
+
+// Digit conversions
+var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz";
+var BI_RC = new Array();
+var rr, vv;
+rr = "0".charCodeAt(0);
+for (vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv;
+rr = "a".charCodeAt(0);
+for (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
+rr = "A".charCodeAt(0);
+for (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
+
+function int2char(n) {
+ return BI_RM.charAt(n);
+}
+
+function intAt(s, i) {
+ var c = BI_RC[s.charCodeAt(i)];
+ return (c == null) ? -1 : c;
+}
+
+// (protected) copy this to r
+
+function bnpCopyTo(r) {
+ for (var i = this.t - 1; i >= 0; --i) r[i] = this[i];
+ r.t = this.t;
+ r.s = this.s;
+}
+
+// (protected) set from integer value x, -DV <= x < DV
+
+function bnpFromInt(x) {
+ this.t = 1;
+ this.s = (x < 0) ? -1 : 0;
+ if (x > 0) this[0] = x;
+ else if (x < -1) this[0] = x + DV;
+ else this.t = 0;
+}
+
+// return bigint initialized to value
+
+function nbv(i) {
+ var r = nbi();
+ r.fromInt(i);
+ return r;
+}
+
+// (protected) set from string and radix
+
+function bnpFromString(s, b) {
+ var k;
+ if (b == 16) k = 4;
+ else if (b == 8) k = 3;
+ else if (b == 256) k = 8; // byte array
+ else if (b == 2) k = 1;
+ else if (b == 32) k = 5;
+ else if (b == 4) k = 2;
+ else {
+ this.fromRadix(s, b);
+ return;
+ }
+ this.t = 0;
+ this.s = 0;
+ var i = s.length,
+ mi = false,
+ sh = 0;
+ while (--i >= 0) {
+ var x = (k == 8) ? s[i] & 0xff : intAt(s, i);
+ if (x < 0) {
+ if (s.charAt(i) == "-") mi = true;
+ continue;
+ }
+ mi = false;
+ if (sh == 0) this[this.t++] = x;
+ else if (sh + k > this.DB) {
+ this[this.t - 1] |= (x & ((1 << (this.DB - sh)) - 1)) << sh;
+ this[this.t++] = (x >> (this.DB - sh));
+ }
+ else this[this.t - 1] |= x << sh;
+ sh += k;
+ if (sh >= this.DB) sh -= this.DB;
+ }
+ if (k == 8 && (s[0] & 0x80) != 0) {
+ this.s = -1;
+ if (sh > 0) this[this.t - 1] |= ((1 << (this.DB - sh)) - 1) << sh;
+ }
+ this.clamp();
+ if (mi) BigInteger.ZERO.subTo(this, this);
+}
+
+// (protected) clamp off excess high words
+
+function bnpClamp() {
+ var c = this.s & this.DM;
+ while (this.t > 0 && this[this.t - 1] == c) --this.t;
+}
+
+// (public) return string representation in given radix
+
+function bnToString(b) {
+ if (this.s < 0) return "-" + this.negate().toString(b);
+ var k;
+ if (b == 16) k = 4;
+ else if (b == 8) k = 3;
+ else if (b == 2) k = 1;
+ else if (b == 32) k = 5;
+ else if (b == 64) k = 6;
+ else if (b == 4) k = 2;
+ else return this.toRadix(b);
+ var km = (1 << k) - 1,
+ d, m = false,
+ r = "",
+ i = this.t;
+ var p = this.DB - (i * this.DB) % k;
+ if (i-- > 0) {
+ if (p < this.DB && (d = this[i] >> p) > 0) {
+ m = true;
+ r = int2char(d);
+ }
+ while (i >= 0) {
+ if (p < k) {
+ d = (this[i] & ((1 << p) - 1)) << (k - p);
+ d |= this[--i] >> (p += this.DB - k);
+ }
+ else {
+ d = (this[i] >> (p -= k)) & km;
+ if (p <= 0) {
+ p += this.DB;
+ --i;
+ }
+ }
+ if (d > 0) m = true;
+ if (m) r += int2char(d);
+ }
+ }
+ return m ? r : "0";
+}
+
+// (public) -this
+
+function bnNegate() {
+ var r = nbi();
+ BigInteger.ZERO.subTo(this, r);
+ return r;
+}
+
+// (public) |this|
+
+function bnAbs() {
+ return (this.s < 0) ? this.negate() : this;
+}
+
+// (public) return + if this > a, - if this < a, 0 if equal
+
+function bnCompareTo(a) {
+ var r = this.s - a.s;
+ if (r != 0) return r;
+ var i = this.t;
+ r = i - a.t;
+ if (r != 0) return r;
+ while (--i >= 0) if ((r = this[i] - a[i]) != 0) return r;
+ return 0;
+}
+
+// returns bit length of the integer x
+
+function nbits(x) {
+ var r = 1,
+ t;
+ if ((t = x >>> 16) != 0) {
+ x = t;
+ r += 16;
+ }
+ if ((t = x >> 8) != 0) {
+ x = t;
+ r += 8;
+ }
+ if ((t = x >> 4) != 0) {
+ x = t;
+ r += 4;
+ }
+ if ((t = x >> 2) != 0) {
+ x = t;
+ r += 2;
+ }
+ if ((t = x >> 1) != 0) {
+ x = t;
+ r += 1;
+ }
+ return r;
+}
+
+// (public) return the number of bits in "this"
+
+function bnBitLength() {
+ if (this.t <= 0) return 0;
+ return this.DB * (this.t - 1) + nbits(this[this.t - 1] ^ (this.s & this.DM));
+}
+
+// (protected) r = this << n*DB
+
+function bnpDLShiftTo(n, r) {
+ var i;
+ for (i = this.t - 1; i >= 0; --i) r[i + n] = this[i];
+ for (i = n - 1; i >= 0; --i) r[i] = 0;
+ r.t = this.t + n;
+ r.s = this.s;
+}
+
+// (protected) r = this >> n*DB
+
+function bnpDRShiftTo(n, r) {
+ for (var i = n; i < this.t; ++i) r[i - n] = this[i];
+ r.t = Math.max(this.t - n, 0);
+ r.s = this.s;
+}
+
+// (protected) r = this << n
+
+function bnpLShiftTo(n, r) {
+ var bs = n % this.DB;
+ var cbs = this.DB - bs;
+ var bm = (1 << cbs) - 1;
+ var ds = Math.floor(n / this.DB),
+ c = (this.s << bs) & this.DM,
+ i;
+ for (i = this.t - 1; i >= 0; --i) {
+ r[i + ds + 1] = (this[i] >> cbs) | c;
+ c = (this[i] & bm) << bs;
+ }
+ for (i = ds - 1; i >= 0; --i) r[i] = 0;
+ r[ds] = c;
+ r.t = this.t + ds + 1;
+ r.s = this.s;
+ r.clamp();
+}
+
+// (protected) r = this >> n
+
+function bnpRShiftTo(n, r) {
+ r.s = this.s;
+ var ds = Math.floor(n / this.DB);
+ if (ds >= this.t) {
+ r.t = 0;
+ return;
+ }
+ var bs = n % this.DB;
+ var cbs = this.DB - bs;
+ var bm = (1 << bs) - 1;
+ r[0] = this[ds] >> bs;
+ for (var i = ds + 1; i < this.t; ++i) {
+ r[i - ds - 1] |= (this[i] & bm) << cbs;
+ r[i - ds] = this[i] >> bs;
+ }
+ if (bs > 0) r[this.t - ds - 1] |= (this.s & bm) << cbs;
+ r.t = this.t - ds;
+ r.clamp();
+}
+
+// (protected) r = this - a
+
+function bnpSubTo(a, r) {
+ var i = 0,
+ c = 0,
+ m = Math.min(a.t, this.t);
+ while (i < m) {
+ c += this[i] - a[i];
+ r[i++] = c & this.DM;
+ c >>= this.DB;
+ }
+ if (a.t < this.t) {
+ c -= a.s;
+ while (i < this.t) {
+ c += this[i];
+ r[i++] = c & this.DM;
+ c >>= this.DB;
+ }
+ c += this.s;
+ }
+ else {
+ c += this.s;
+ while (i < a.t) {
+ c -= a[i];
+ r[i++] = c & this.DM;
+ c >>= this.DB;
+ }
+ c -= a.s;
+ }
+ r.s = (c < 0) ? -1 : 0;
+ if (c < -1) r[i++] = this.DV + c;
+ else if (c > 0) r[i++] = c;
+ r.t = i;
+ r.clamp();
+}
+
+// (protected) r = this * a, r != this,a (HAC 14.12)
+// "this" should be the larger one if appropriate.
+
+function bnpMultiplyTo(a, r) {
+ var x = this.abs(),
+ y = a.abs();
+ var i = x.t;
+ r.t = i + y.t;
+ while (--i >= 0) r[i] = 0;
+ for (i = 0; i < y.t; ++i) r[i + x.t] = x.am(0, y[i], r, i, 0, x.t);
+ r.s = 0;
+ r.clamp();
+ if (this.s != a.s) BigInteger.ZERO.subTo(r, r);
+}
+
+// (protected) r = this^2, r != this (HAC 14.16)
+
+function bnpSquareTo(r) {
+ var x = this.abs();
+ var i = r.t = 2 * x.t;
+ while (--i >= 0) r[i] = 0;
+ for (i = 0; i < x.t - 1; ++i) {
+ var c = x.am(i, x[i], r, 2 * i, 0, 1);
+ if ((r[i + x.t] += x.am(i + 1, 2 * x[i], r, 2 * i + 1, c, x.t - i - 1)) >= x.DV) {
+ r[i + x.t] -= x.DV;
+ r[i + x.t + 1] = 1;
+ }
+ }
+ if (r.t > 0) r[r.t - 1] += x.am(i, x[i], r, 2 * i, 0, 1);
+ r.s = 0;
+ r.clamp();
+}
+
+// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20)
+// r != q, this != m. q or r may be null.
+
+function bnpDivRemTo(m, q, r) {
+ var pm = m.abs();
+ if (pm.t <= 0) return;
+ var pt = this.abs();
+ if (pt.t < pm.t) {
+ if (q != null) q.fromInt(0);
+ if (r != null) this.copyTo(r);
+ return;
+ }
+ if (r == null) r = nbi();
+ var y = nbi(),
+ ts = this.s,
+ ms = m.s;
+ var nsh = this.DB - nbits(pm[pm.t - 1]); // normalize modulus
+ if (nsh > 0) {
+ pm.lShiftTo(nsh, y);
+ pt.lShiftTo(nsh, r);
+ }
+ else {
+ pm.copyTo(y);
+ pt.copyTo(r);
+ }
+ var ys = y.t;
+ var y0 = y[ys - 1];
+ if (y0 == 0) return;
+ var yt = y0 * (1 << this.F1) + ((ys > 1) ? y[ys - 2] >> this.F2 : 0);
+ var d1 = this.FV / yt,
+ d2 = (1 << this.F1) / yt,
+ e = 1 << this.F2;
+ var i = r.t,
+ j = i - ys,
+ t = (q == null) ? nbi() : q;
+ y.dlShiftTo(j, t);
+ if (r.compareTo(t) >= 0) {
+ r[r.t++] = 1;
+ r.subTo(t, r);
+ }
+ BigInteger.ONE.dlShiftTo(ys, t);
+ t.subTo(y, y); // "negative" y so we can replace sub with am later
+ while (y.t < ys) y[y.t++] = 0;
+ while (--j >= 0) {
+ // Estimate quotient digit
+ var qd = (r[--i] == y0) ? this.DM : Math.floor(r[i] * d1 + (r[i - 1] + e) * d2);
+ if ((r[i] += y.am(0, qd, r, j, 0, ys)) < qd) { // Try it out
+ y.dlShiftTo(j, t);
+ r.subTo(t, r);
+ while (r[i] < --qd) r.subTo(t, r);
+ }
+ }
+ if (q != null) {
+ r.drShiftTo(ys, q);
+ if (ts != ms) BigInteger.ZERO.subTo(q, q);
+ }
+ r.t = ys;
+ r.clamp();
+ if (nsh > 0) r.rShiftTo(nsh, r); // Denormalize remainder
+ if (ts < 0) BigInteger.ZERO.subTo(r, r);
+}
+
+// (public) this mod a
+
+function bnMod(a) {
+ var r = nbi();
+ this.abs().divRemTo(a, null, r);
+ if (this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r, r);
+ return r;
+}
+
+// Modular reduction using "classic" algorithm
+
+function Classic(m) {
+ this.m = m;
+}
+
+function cConvert(x) {
+ if (x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);
+ else return x;
+}
+
+function cRevert(x) {
+ return x;
+}
+
+function cReduce(x) {
+ x.divRemTo(this.m, null, x);
+}
+
+function cMulTo(x, y, r) {
+ x.multiplyTo(y, r);
+ this.reduce(r);
+}
+
+function cSqrTo(x, r) {
+ x.squareTo(r);
+ this.reduce(r);
+}
+
+Classic.prototype.convert = cConvert;
+Classic.prototype.revert = cRevert;
+Classic.prototype.reduce = cReduce;
+Classic.prototype.mulTo = cMulTo;
+Classic.prototype.sqrTo = cSqrTo;
+
+// (protected) return "-1/this % 2^DB"; useful for Mont. reduction
+// justification:
+// xy == 1 (mod m)
+// xy = 1+km
+// xy(2-xy) = (1+km)(1-km)
+// x[y(2-xy)] = 1-k^2m^2
+// x[y(2-xy)] == 1 (mod m^2)
+// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2
+// should reduce x and y(2-xy) by m^2 at each step to keep size bounded.
+// JS multiply "overflows" differently from C/C++, so care is needed here.
+
+function bnpInvDigit() {
+ if (this.t < 1) return 0;
+ var x = this[0];
+ if ((x & 1) == 0) return 0;
+ var y = x & 3; // y == 1/x mod 2^2
+ y = (y * (2 - (x & 0xf) * y)) & 0xf; // y == 1/x mod 2^4
+ y = (y * (2 - (x & 0xff) * y)) & 0xff; // y == 1/x mod 2^8
+ y = (y * (2 - (((x & 0xffff) * y) & 0xffff))) & 0xffff; // y == 1/x mod 2^16
+ // last step - calculate inverse mod DV directly;
+ // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints
+ y = (y * (2 - x * y % this.DV)) % this.DV; // y == 1/x mod 2^dbits
+ // we really want the negative inverse, and -DV < y < DV
+ return (y > 0) ? this.DV - y : -y;
+}
+
+// Montgomery reduction
+
+function Montgomery(m) {
+ this.m = m;
+ this.mp = m.invDigit();
+ this.mpl = this.mp & 0x7fff;
+ this.mph = this.mp >> 15;
+ this.um = (1 << (m.DB - 15)) - 1;
+ this.mt2 = 2 * m.t;
+}
+
+// xR mod m
+
+function montConvert(x) {
+ var r = nbi();
+ x.abs().dlShiftTo(this.m.t, r);
+ r.divRemTo(this.m, null, r);
+ if (x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r, r);
+ return r;
+}
+
+// x/R mod m
+
+function montRevert(x) {
+ var r = nbi();
+ x.copyTo(r);
+ this.reduce(r);
+ return r;
+}
+
+// x = x/R mod m (HAC 14.32)
+
+function montReduce(x) {
+ while (x.t <= this.mt2) // pad x so am has enough room later
+ x[x.t++] = 0;
+ for (var i = 0; i < this.m.t; ++i) {
+ // faster way of calculating u0 = x[i]*mp mod DV
+ var j = x[i] & 0x7fff;
+ var u0 = (j * this.mpl + (((j * this.mph + (x[i] >> 15) * this.mpl) & this.um) << 15)) & x.DM;
+ // use am to combine the multiply-shift-add into one call
+ j = i + this.m.t;
+ x[j] += this.m.am(0, u0, x, i, 0, this.m.t);
+ // propagate carry
+ while (x[j] >= x.DV) {
+ x[j] -= x.DV;
+ x[++j]++;
+ }
+ }
+ x.clamp();
+ x.drShiftTo(this.m.t, x);
+ if (x.compareTo(this.m) >= 0) x.subTo(this.m, x);
+}
+
+// r = "x^2/R mod m"; x != r
+
+function montSqrTo(x, r) {
+ x.squareTo(r);
+ this.reduce(r);
+}
+
+// r = "xy/R mod m"; x,y != r
+
+function montMulTo(x, y, r) {
+ x.multiplyTo(y, r);
+ this.reduce(r);
+}
+
+Montgomery.prototype.convert = montConvert;
+Montgomery.prototype.revert = montRevert;
+Montgomery.prototype.reduce = montReduce;
+Montgomery.prototype.mulTo = montMulTo;
+Montgomery.prototype.sqrTo = montSqrTo;
+
+// (protected) true iff this is even
+
+function bnpIsEven() {
+ return ((this.t > 0) ? (this[0] & 1) : this.s) == 0;
+}
+
+// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79)
+
+function bnpExp(e, z) {
+ if (e > 0xffffffff || e < 1) return BigInteger.ONE;
+ var r = nbi(),
+ r2 = nbi(),
+ g = z.convert(this),
+ i = nbits(e) - 1;
+ g.copyTo(r);
+ while (--i >= 0) {
+ z.sqrTo(r, r2);
+ if ((e & (1 << i)) > 0) z.mulTo(r2, g, r);
+ else {
+ var t = r;
+ r = r2;
+ r2 = t;
+ }
+ }
+ return z.revert(r);
+}
+
+// (public) this^e % m, 0 <= e < 2^32
+
+function bnModPowInt(e, m) {
+ var z;
+ if (e < 256 || m.isEven()) z = new Classic(m);
+ else z = new Montgomery(m);
+ return this.exp(e, z);
+}
+
+// protected
+BigInteger.prototype.copyTo = bnpCopyTo;
+BigInteger.prototype.fromInt = bnpFromInt;
+BigInteger.prototype.fromString = bnpFromString;
+BigInteger.prototype.clamp = bnpClamp;
+BigInteger.prototype.dlShiftTo = bnpDLShiftTo;
+BigInteger.prototype.drShiftTo = bnpDRShiftTo;
+BigInteger.prototype.lShiftTo = bnpLShiftTo;
+BigInteger.prototype.rShiftTo = bnpRShiftTo;
+BigInteger.prototype.subTo = bnpSubTo;
+BigInteger.prototype.multiplyTo = bnpMultiplyTo;
+BigInteger.prototype.squareTo = bnpSquareTo;
+BigInteger.prototype.divRemTo = bnpDivRemTo;
+BigInteger.prototype.invDigit = bnpInvDigit;
+BigInteger.prototype.isEven = bnpIsEven;
+BigInteger.prototype.exp = bnpExp;
+
+// public
+BigInteger.prototype.toString = bnToString;
+BigInteger.prototype.negate = bnNegate;
+BigInteger.prototype.abs = bnAbs;
+BigInteger.prototype.compareTo = bnCompareTo;
+BigInteger.prototype.bitLength = bnBitLength;
+BigInteger.prototype.mod = bnMod;
+BigInteger.prototype.modPowInt = bnModPowInt;
+
+// "constants"
+BigInteger.ZERO = nbv(0);
+BigInteger.ONE = nbv(1);
+
+
+function bnClone() {
+ var r = nbi();
+ this.copyTo(r);
+ return r;
+}
+
+// (public) return value as integer
+
+function bnIntValue() {
+ if (this.s < 0) {
+ if (this.t == 1) return this[0] - this.DV;
+ else if (this.t == 0) return -1;
+ }
+ else if (this.t == 1) return this[0];
+ else if (this.t == 0) return 0;
+ // assumes 16 < DB < 32
+ return ((this[1] & ((1 << (32 - this.DB)) - 1)) << this.DB) | this[0];
+}
+
+// (public) return value as byte
+
+function bnByteValue() {
+ return (this.t == 0) ? this.s : (this[0] << 24) >> 24;
+}
+
+// (public) return value as short (assumes DB>=16)
+
+function bnShortValue() {
+ return (this.t == 0) ? this.s : (this[0] << 16) >> 16;
+}
+
+// (protected) return x s.t. r^x < DV
+
+function bnpChunkSize(r) {
+ return Math.floor(Math.LN2 * this.DB / Math.log(r));
+}
+
+// (public) 0 if this == 0, 1 if this > 0
+
+function bnSigNum() {
+ if (this.s < 0) return -1;
+ else if (this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0;
+ else return 1;
+}
+
+// (protected) convert to radix string
+
+function bnpToRadix(b) {
+ if (b == null) b = 10;
+ if (this.signum() == 0 || b < 2 || b > 36) return "0";
+ var cs = this.chunkSize(b);
+ var a = Math.pow(b, cs);
+ var d = nbv(a),
+ y = nbi(),
+ z = nbi(),
+ r = "";
+ this.divRemTo(d, y, z);
+ while (y.signum() > 0) {
+ r = (a + z.intValue()).toString(b).substr(1) + r;
+ y.divRemTo(d, y, z);
+ }
+ return z.intValue().toString(b) + r;
+}
+
+// (protected) convert from radix string
+
+function bnpFromRadix(s, b) {
+ this.fromInt(0);
+ if (b == null) b = 10;
+ var cs = this.chunkSize(b);
+ var d = Math.pow(b, cs),
+ mi = false,
+ j = 0,
+ w = 0;
+ for (var i = 0; i < s.length; ++i) {
+ var x = intAt(s, i);
+ if (x < 0) {
+ if (s.charAt(i) == "-" && this.signum() == 0) mi = true;
+ continue;
+ }
+ w = b * w + x;
+ if (++j >= cs) {
+ this.dMultiply(d);
+ this.dAddOffset(w, 0);
+ j = 0;
+ w = 0;
+ }
+ }
+ if (j > 0) {
+ this.dMultiply(Math.pow(b, j));
+ this.dAddOffset(w, 0);
+ }
+ if (mi) BigInteger.ZERO.subTo(this, this);
+}
+
+// (protected) alternate constructor
+
+function bnpFromNumber(a, b, c) {
+ if ("number" == typeof b) {
+ // new BigInteger(int,int,RNG)
+ if (a < 2) this.fromInt(1);
+ else {
+ this.fromNumber(a, c);
+ if (!this.testBit(a - 1)) // force MSB set
+ this.bitwiseTo(BigInteger.ONE.shiftLeft(a - 1), op_or, this);
+ if (this.isEven()) this.dAddOffset(1, 0); // force odd
+ while (!this.isProbablePrime(b)) {
+ this.dAddOffset(2, 0);
+ if (this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a - 1), this);
+ }
+ }
+ }
+ else {
+ // new BigInteger(int,RNG)
+ var x = new Array(),
+ t = a & 7;
+ x.length = (a >> 3) + 1;
+ b.nextBytes(x);
+ if (t > 0) x[0] &= ((1 << t) - 1);
+ else x[0] = 0;
+ this.fromString(x, 256);
+ }
+}
+
+// (public) convert to bigendian byte array
+
+function bnToByteArray() {
+ var i = this.t,
+ r = new Array();
+ r[0] = this.s;
+ var p = this.DB - (i * this.DB) % 8,
+ d, k = 0;
+ if (i-- > 0) {
+ if (p < this.DB && (d = this[i] >> p) != (this.s & this.DM) >> p) r[k++] = d | (this.s << (this.DB - p));
+ while (i >= 0) {
+ if (p < 8) {
+ d = (this[i] & ((1 << p) - 1)) << (8 - p);
+ d |= this[--i] >> (p += this.DB - 8);
+ }
+ else {
+ d = (this[i] >> (p -= 8)) & 0xff;
+ if (p <= 0) {
+ p += this.DB;
+ --i;
+ }
+ }
+ if ((d & 0x80) != 0) d |= -256;
+ if (k == 0 && (this.s & 0x80) != (d & 0x80)) ++k;
+ if (k > 0 || d != this.s) r[k++] = d;
+ }
+ }
+ return r;
+}
+
+function bnEquals(a) {
+ return (this.compareTo(a) == 0);
+}
+
+function bnMin(a) {
+ return (this.compareTo(a) < 0) ? this : a;
+}
+
+function bnMax(a) {
+ return (this.compareTo(a) > 0) ? this : a;
+}
+
+// (protected) r = this op a (bitwise)
+
+function bnpBitwiseTo(a, op, r) {
+ var i, f, m = Math.min(a.t, this.t);
+ for (i = 0; i < m; ++i) r[i] = op(this[i], a[i]);
+ if (a.t < this.t) {
+ f = a.s & this.DM;
+ for (i = m; i < this.t; ++i) r[i] = op(this[i], f);
+ r.t = this.t;
+ }
+ else {
+ f = this.s & this.DM;
+ for (i = m; i < a.t; ++i) r[i] = op(f, a[i]);
+ r.t = a.t;
+ }
+ r.s = op(this.s, a.s);
+ r.clamp();
+}
+
+// (public) this & a
+
+function op_and(x, y) {
+ return x & y;
+}
+
+function bnAnd(a) {
+ var r = nbi();
+ this.bitwiseTo(a, op_and, r);
+ return r;
+}
+
+// (public) this | a
+
+function op_or(x, y) {
+ return x | y;
+}
+
+function bnOr(a) {
+ var r = nbi();
+ this.bitwiseTo(a, op_or, r);
+ return r;
+}
+
+// (public) this ^ a
+
+function op_xor(x, y) {
+ return x ^ y;
+}
+
+function bnXor(a) {
+ var r = nbi();
+ this.bitwiseTo(a, op_xor, r);
+ return r;
+}
+
+// (public) this & ~a
+
+function op_andnot(x, y) {
+ return x & ~y;
+}
+
+function bnAndNot(a) {
+ var r = nbi();
+ this.bitwiseTo(a, op_andnot, r);
+ return r;
+}
+
+// (public) ~this
+
+function bnNot() {
+ var r = nbi();
+ for (var i = 0; i < this.t; ++i) r[i] = this.DM & ~this[i];
+ r.t = this.t;
+ r.s = ~this.s;
+ return r;
+}
+
+// (public) this << n
+
+function bnShiftLeft(n) {
+ var r = nbi();
+ if (n < 0) this.rShiftTo(-n, r);
+ else this.lShiftTo(n, r);
+ return r;
+}
+
+// (public) this >> n
+
+function bnShiftRight(n) {
+ var r = nbi();
+ if (n < 0) this.lShiftTo(-n, r);
+ else this.rShiftTo(n, r);
+ return r;
+}
+
+// return index of lowest 1-bit in x, x < 2^31
+
+function lbit(x) {
+ if (x == 0) return -1;
+ var r = 0;
+ if ((x & 0xffff) == 0) {
+ x >>= 16;
+ r += 16;
+ }
+ if ((x & 0xff) == 0) {
+ x >>= 8;
+ r += 8;
+ }
+ if ((x & 0xf) == 0) {
+ x >>= 4;
+ r += 4;
+ }
+ if ((x & 3) == 0) {
+ x >>= 2;
+ r += 2;
+ }
+ if ((x & 1) == 0) ++r;
+ return r;
+}
+
+// (public) returns index of lowest 1-bit (or -1 if none)
+
+function bnGetLowestSetBit() {
+ for (var i = 0; i < this.t; ++i)
+ if (this[i] != 0) return i * this.DB + lbit(this[i]);
+ if (this.s < 0) return this.t * this.DB;
+ return -1;
+}
+
+// return number of 1 bits in x
+
+function cbit(x) {
+ var r = 0;
+ while (x != 0) {
+ x &= x - 1;
+ ++r;
+ }
+ return r;
+}
+
+// (public) return number of set bits
+
+function bnBitCount() {
+ var r = 0,
+ x = this.s & this.DM;
+ for (var i = 0; i < this.t; ++i) r += cbit(this[i] ^ x);
+ return r;
+}
+
+// (public) true iff nth bit is set
+
+function bnTestBit(n) {
+ var j = Math.floor(n / this.DB);
+ if (j >= this.t) return (this.s != 0);
+ return ((this[j] & (1 << (n % this.DB))) != 0);
+}
+
+// (protected) this op (1<
>= this.DB;
+ }
+ if (a.t < this.t) {
+ c += a.s;
+ while (i < this.t) {
+ c += this[i];
+ r[i++] = c & this.DM;
+ c >>= this.DB;
+ }
+ c += this.s;
+ }
+ else {
+ c += this.s;
+ while (i < a.t) {
+ c += a[i];
+ r[i++] = c & this.DM;
+ c >>= this.DB;
+ }
+ c += a.s;
+ }
+ r.s = (c < 0) ? -1 : 0;
+ if (c > 0) r[i++] = c;
+ else if (c < -1) r[i++] = this.DV + c;
+ r.t = i;
+ r.clamp();
+}
+
+// (public) this + a
+
+function bnAdd(a) {
+ var r = nbi();
+ this.addTo(a, r);
+ return r;
+}
+
+// (public) this - a
+
+function bnSubtract(a) {
+ var r = nbi();
+ this.subTo(a, r);
+ return r;
+}
+
+// (public) this * a
+
+function bnMultiply(a) {
+ var r = nbi();
+ this.multiplyTo(a, r);
+ return r;
+}
+
+// (public) this^2
+
+function bnSquare() {
+ var r = nbi();
+ this.squareTo(r);
+ return r;
+}
+
+// (public) this / a
+
+function bnDivide(a) {
+ var r = nbi();
+ this.divRemTo(a, r, null);
+ return r;
+}
+
+// (public) this % a
+
+function bnRemainder(a) {
+ var r = nbi();
+ this.divRemTo(a, null, r);
+ return r;
+}
+
+// (public) [this/a,this%a]
+
+function bnDivideAndRemainder(a) {
+ var q = nbi(),
+ r = nbi();
+ this.divRemTo(a, q, r);
+ return new Array(q, r);
+}
+
+// (protected) this *= n, this >= 0, 1 < n < DV
+
+function bnpDMultiply(n) {
+ this[this.t] = this.am(0, n - 1, this, 0, 0, this.t);
+ ++this.t;
+ this.clamp();
+}
+
+// (protected) this += n << w words, this >= 0
+
+function bnpDAddOffset(n, w) {
+ if (n == 0) return;
+ while (this.t <= w) this[this.t++] = 0;
+ this[w] += n;
+ while (this[w] >= this.DV) {
+ this[w] -= this.DV;
+ if (++w >= this.t) this[this.t++] = 0;
+ ++this[w];
+ }
+}
+
+// A "null" reducer
+
+function NullExp() {
+}
+
+function nNop(x) {
+ return x;
+}
+
+function nMulTo(x, y, r) {
+ x.multiplyTo(y, r);
+}
+
+function nSqrTo(x, r) {
+ x.squareTo(r);
+}
+
+NullExp.prototype.convert = nNop;
+NullExp.prototype.revert = nNop;
+NullExp.prototype.mulTo = nMulTo;
+NullExp.prototype.sqrTo = nSqrTo;
+
+// (public) this^e
+
+function bnPow(e) {
+ return this.exp(e, new NullExp());
+}
+
+// (protected) r = lower n words of "this * a", a.t <= n
+// "this" should be the larger one if appropriate.
+
+function bnpMultiplyLowerTo(a, n, r) {
+ var i = Math.min(this.t + a.t, n);
+ r.s = 0; // assumes a,this >= 0
+ r.t = i;
+ while (i > 0) r[--i] = 0;
+ var j;
+ for (j = r.t - this.t; i < j; ++i) r[i + this.t] = this.am(0, a[i], r, i, 0, this.t);
+ for (j = Math.min(a.t, n); i < j; ++i) this.am(0, a[i], r, i, 0, n - i);
+ r.clamp();
+}
+
+// (protected) r = "this * a" without lower n words, n > 0
+// "this" should be the larger one if appropriate.
+
+function bnpMultiplyUpperTo(a, n, r) {
+ --n;
+ var i = r.t = this.t + a.t - n;
+ r.s = 0; // assumes a,this >= 0
+ while (--i >= 0) r[i] = 0;
+ for (i = Math.max(n - this.t, 0); i < a.t; ++i)
+ r[this.t + i - n] = this.am(n - i, a[i], r, 0, 0, this.t + i - n);
+ r.clamp();
+ r.drShiftTo(1, r);
+}
+
+// Barrett modular reduction
+
+function Barrett(m) {
+ // setup Barrett
+ this.r2 = nbi();
+ this.q3 = nbi();
+ BigInteger.ONE.dlShiftTo(2 * m.t, this.r2);
+ this.mu = this.r2.divide(m);
+ this.m = m;
+}
+
+function barrettConvert(x) {
+ if (x.s < 0 || x.t > 2 * this.m.t) return x.mod(this.m);
+ else if (x.compareTo(this.m) < 0) return x;
+ else {
+ var r = nbi();
+ x.copyTo(r);
+ this.reduce(r);
+ return r;
+ }
+}
+
+function barrettRevert(x) {
+ return x;
+}
+
+// x = x mod m (HAC 14.42)
+
+function barrettReduce(x) {
+ x.drShiftTo(this.m.t - 1, this.r2);
+ if (x.t > this.m.t + 1) {
+ x.t = this.m.t + 1;
+ x.clamp();
+ }
+ this.mu.multiplyUpperTo(this.r2, this.m.t + 1, this.q3);
+ this.m.multiplyLowerTo(this.q3, this.m.t + 1, this.r2);
+ while (x.compareTo(this.r2) < 0) x.dAddOffset(1, this.m.t + 1);
+ x.subTo(this.r2, x);
+ while (x.compareTo(this.m) >= 0) x.subTo(this.m, x);
+}
+
+// r = x^2 mod m; x != r
+
+function barrettSqrTo(x, r) {
+ x.squareTo(r);
+ this.reduce(r);
+}
+
+// r = x*y mod m; x,y != r
+
+function barrettMulTo(x, y, r) {
+ x.multiplyTo(y, r);
+ this.reduce(r);
+}
+
+Barrett.prototype.convert = barrettConvert;
+Barrett.prototype.revert = barrettRevert;
+Barrett.prototype.reduce = barrettReduce;
+Barrett.prototype.mulTo = barrettMulTo;
+Barrett.prototype.sqrTo = barrettSqrTo;
+
+// (public) this^e % m (HAC 14.85)
+
+function bnModPow(e, m) {
+ var i = e.bitLength(),
+ k, r = nbv(1),
+ z;
+ if (i <= 0) return r;
+ else if (i < 18) k = 1;
+ else if (i < 48) k = 3;
+ else if (i < 144) k = 4;
+ else if (i < 768) k = 5;
+ else k = 6;
+ if (i < 8) z = new Classic(m);
+ else if (m.isEven()) z = new Barrett(m);
+ else z = new Montgomery(m);
+
+ // precomputation
+ var g = new Array(),
+ n = 3,
+ k1 = k - 1,
+ km = (1 << k) - 1;
+ g[1] = z.convert(this);
+ if (k > 1) {
+ var g2 = nbi();
+ z.sqrTo(g[1], g2);
+ while (n <= km) {
+ g[n] = nbi();
+ z.mulTo(g2, g[n - 2], g[n]);
+ n += 2;
+ }
+ }
+
+ var j = e.t - 1,
+ w, is1 = true,
+ r2 = nbi(),
+ t;
+ i = nbits(e[j]) - 1;
+ while (j >= 0) {
+ if (i >= k1) w = (e[j] >> (i - k1)) & km;
+ else {
+ w = (e[j] & ((1 << (i + 1)) - 1)) << (k1 - i);
+ if (j > 0) w |= e[j - 1] >> (this.DB + i - k1);
+ }
+
+ n = k;
+ while ((w & 1) == 0) {
+ w >>= 1;
+ --n;
+ }
+ if ((i -= n) < 0) {
+ i += this.DB;
+ --j;
+ }
+ if (is1) { // ret == 1, don't bother squaring or multiplying it
+ g[w].copyTo(r);
+ is1 = false;
+ }
+ else {
+ while (n > 1) {
+ z.sqrTo(r, r2);
+ z.sqrTo(r2, r);
+ n -= 2;
+ }
+ if (n > 0) z.sqrTo(r, r2);
+ else {
+ t = r;
+ r = r2;
+ r2 = t;
+ }
+ z.mulTo(r2, g[w], r);
+ }
+
+ while (j >= 0 && (e[j] & (1 << i)) == 0) {
+ z.sqrTo(r, r2);
+ t = r;
+ r = r2;
+ r2 = t;
+ if (--i < 0) {
+ i = this.DB - 1;
+ --j;
+ }
+ }
+ }
+ return z.revert(r);
+}
+
+// (public) gcd(this,a) (HAC 14.54)
+
+function bnGCD(a) {
+ var x = (this.s < 0) ? this.negate() : this.clone();
+ var y = (a.s < 0) ? a.negate() : a.clone();
+ if (x.compareTo(y) < 0) {
+ var t = x;
+ x = y;
+ y = t;
+ }
+ var i = x.getLowestSetBit(),
+ g = y.getLowestSetBit();
+ if (g < 0) return x;
+ if (i < g) g = i;
+ if (g > 0) {
+ x.rShiftTo(g, x);
+ y.rShiftTo(g, y);
+ }
+ while (x.signum() > 0) {
+ if ((i = x.getLowestSetBit()) > 0) x.rShiftTo(i, x);
+ if ((i = y.getLowestSetBit()) > 0) y.rShiftTo(i, y);
+ if (x.compareTo(y) >= 0) {
+ x.subTo(y, x);
+ x.rShiftTo(1, x);
+ }
+ else {
+ y.subTo(x, y);
+ y.rShiftTo(1, y);
+ }
+ }
+ if (g > 0) y.lShiftTo(g, y);
+ return y;
+}
+
+// (protected) this % n, n < 2^26
+
+function bnpModInt(n) {
+ if (n <= 0) return 0;
+ var d = this.DV % n,
+ r = (this.s < 0) ? n - 1 : 0;
+ if (this.t > 0) if (d == 0) r = this[0] % n;
+ else for (var i = this.t - 1; i >= 0; --i) r = (d * r + this[i]) % n;
+ return r;
+}
+
+// (public) 1/this % m (HAC 14.61)
+
+function bnModInverse(m) {
+ var ac = m.isEven();
+ if ((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO;
+ var u = m.clone(),
+ v = this.clone();
+ var a = nbv(1),
+ b = nbv(0),
+ c = nbv(0),
+ d = nbv(1);
+ while (u.signum() != 0) {
+ while (u.isEven()) {
+ u.rShiftTo(1, u);
+ if (ac) {
+ if (!a.isEven() || !b.isEven()) {
+ a.addTo(this, a);
+ b.subTo(m, b);
+ }
+ a.rShiftTo(1, a);
+ }
+ else if (!b.isEven()) b.subTo(m, b);
+ b.rShiftTo(1, b);
+ }
+ while (v.isEven()) {
+ v.rShiftTo(1, v);
+ if (ac) {
+ if (!c.isEven() || !d.isEven()) {
+ c.addTo(this, c);
+ d.subTo(m, d);
+ }
+ c.rShiftTo(1, c);
+ }
+ else if (!d.isEven()) d.subTo(m, d);
+ d.rShiftTo(1, d);
+ }
+ if (u.compareTo(v) >= 0) {
+ u.subTo(v, u);
+ if (ac) a.subTo(c, a);
+ b.subTo(d, b);
+ }
+ else {
+ v.subTo(u, v);
+ if (ac) c.subTo(a, c);
+ d.subTo(b, d);
+ }
+ }
+ if (v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO;
+ if (d.compareTo(m) >= 0) return d.subtract(m);
+ if (d.signum() < 0) d.addTo(m, d);
+ else return d;
+ if (d.signum() < 0) return d.add(m);
+ else return d;
+}
+
+var lowprimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997];
+var lplim = (1 << 26) / lowprimes[lowprimes.length - 1];
+
+// (public) test primality with certainty >= 1-.5^t
+
+function bnIsProbablePrime(t) {
+ var i, x = this.abs();
+ if (x.t == 1 && x[0] <= lowprimes[lowprimes.length - 1]) {
+ for (i = 0; i < lowprimes.length; ++i)
+ if (x[0] == lowprimes[i]) return true;
+ return false;
+ }
+ if (x.isEven()) return false;
+ i = 1;
+ while (i < lowprimes.length) {
+ var m = lowprimes[i],
+ j = i + 1;
+ while (j < lowprimes.length && m < lplim) m *= lowprimes[j++];
+ m = x.modInt(m);
+ while (i < j) if (m % lowprimes[i++] == 0) return false;
+ }
+ return x.millerRabin(t);
+}
+
+// (protected) true if probably prime (HAC 4.24, Miller-Rabin)
+
+function bnpMillerRabin(t) {
+ var n1 = this.subtract(BigInteger.ONE);
+ var k = n1.getLowestSetBit();
+ if (k <= 0) return false;
+ var r = n1.shiftRight(k);
+ t = (t + 1) >> 1;
+ if (t > lowprimes.length) t = lowprimes.length;
+ var a = nbi();
+ for (var i = 0; i < t; ++i) {
+ //Pick bases at random, instead of starting at 2
+ a.fromInt(lowprimes[Math.floor(Math.random() * lowprimes.length)]);
+ var y = a.modPow(r, this);
+ if (y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) {
+ var j = 1;
+ while (j++ < k && y.compareTo(n1) != 0) {
+ y = y.modPowInt(2, this);
+ if (y.compareTo(BigInteger.ONE) == 0) return false;
+ }
+ if (y.compareTo(n1) != 0) return false;
+ }
+ }
+ return true;
+}
+
+// protected
+BigInteger.prototype.chunkSize = bnpChunkSize;
+BigInteger.prototype.toRadix = bnpToRadix;
+BigInteger.prototype.fromRadix = bnpFromRadix;
+BigInteger.prototype.fromNumber = bnpFromNumber;
+BigInteger.prototype.bitwiseTo = bnpBitwiseTo;
+BigInteger.prototype.changeBit = bnpChangeBit;
+BigInteger.prototype.addTo = bnpAddTo;
+BigInteger.prototype.dMultiply = bnpDMultiply;
+BigInteger.prototype.dAddOffset = bnpDAddOffset;
+BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo;
+BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo;
+BigInteger.prototype.modInt = bnpModInt;
+BigInteger.prototype.millerRabin = bnpMillerRabin;
+
+// public
+BigInteger.prototype.clone = bnClone;
+BigInteger.prototype.intValue = bnIntValue;
+BigInteger.prototype.byteValue = bnByteValue;
+BigInteger.prototype.shortValue = bnShortValue;
+BigInteger.prototype.signum = bnSigNum;
+BigInteger.prototype.toByteArray = bnToByteArray;
+BigInteger.prototype.equals = bnEquals;
+BigInteger.prototype.min = bnMin;
+BigInteger.prototype.max = bnMax;
+BigInteger.prototype.and = bnAnd;
+BigInteger.prototype.or = bnOr;
+BigInteger.prototype.xor = bnXor;
+BigInteger.prototype.andNot = bnAndNot;
+BigInteger.prototype.not = bnNot;
+BigInteger.prototype.shiftLeft = bnShiftLeft;
+BigInteger.prototype.shiftRight = bnShiftRight;
+BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit;
+BigInteger.prototype.bitCount = bnBitCount;
+BigInteger.prototype.testBit = bnTestBit;
+BigInteger.prototype.setBit = bnSetBit;
+BigInteger.prototype.clearBit = bnClearBit;
+BigInteger.prototype.flipBit = bnFlipBit;
+BigInteger.prototype.add = bnAdd;
+BigInteger.prototype.subtract = bnSubtract;
+BigInteger.prototype.multiply = bnMultiply;
+BigInteger.prototype.divide = bnDivide;
+BigInteger.prototype.remainder = bnRemainder;
+BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder;
+BigInteger.prototype.modPow = bnModPow;
+BigInteger.prototype.modInverse = bnModInverse;
+BigInteger.prototype.pow = bnPow;
+BigInteger.prototype.gcd = bnGCD;
+BigInteger.prototype.isProbablePrime = bnIsProbablePrime;
+
+// JSBN-specific extension
+BigInteger.prototype.square = bnSquare;
+
+// seedrandom.js version 2.0.
+// Author: David Bau 4/2/2011
+//
+// Defines a method Math.seedrandom() that, when called, substitutes
+// an explicitly seeded RC4-based algorithm for Math.random(). Also
+// supports automatic seeding from local or network sources of entropy.
+//
+// Usage:
+//
+//
+//
+// Math.seedrandom('yipee'); Sets Math.random to a function that is
+// initialized using the given explicit seed.
+//
+// Math.seedrandom(); Sets Math.random to a function that is
+// seeded using the current time, dom state,
+// and other accumulated local entropy.
+// The generated seed string is returned.
+//
+// Math.seedrandom('yowza', true);
+// Seeds using the given explicit seed mixed
+// together with accumulated entropy.
+//
+//
+// Seeds using physical random bits downloaded
+// from random.org.
+//
+// Seeds using urandom bits from call.jsonlib.com,
+// which is faster than random.org.
+//
+// Examples:
+//
+// Math.seedrandom("hello"); // Use "hello" as the seed.
+// document.write(Math.random()); // Always 0.5463663768140734
+// document.write(Math.random()); // Always 0.43973793770592234
+// var rng1 = Math.random; // Remember the current prng.
+//
+// var autoseed = Math.seedrandom(); // New prng with an automatic seed.
+// document.write(Math.random()); // Pretty much unpredictable.
+//
+// Math.random = rng1; // Continue "hello" prng sequence.
+// document.write(Math.random()); // Always 0.554769432473455
+//
+// Math.seedrandom(autoseed); // Restart at the previous seed.
+// document.write(Math.random()); // Repeat the 'unpredictable' value.
+//
+// Notes:
+//
+// Each time seedrandom('arg') is called, entropy from the passed seed
+// is accumulated in a pool to help generate future seeds for the
+// zero-argument form of Math.seedrandom, so entropy can be injected over
+// time by calling seedrandom with explicit data repeatedly.
+//
+// On speed - This javascript implementation of Math.random() is about
+// 3-10x slower than the built-in Math.random() because it is not native
+// code, but this is typically fast enough anyway. Seeding is more expensive,
+// especially if you use auto-seeding. Some details (timings on Chrome 4):
+//
+// Our Math.random() - avg less than 0.002 milliseconds per call
+// seedrandom('explicit') - avg less than 0.5 milliseconds per call
+// seedrandom('explicit', true) - avg less than 2 milliseconds per call
+// seedrandom() - avg about 38 milliseconds per call
+//
+// LICENSE (BSD):
+//
+// Copyright 2010 David Bau, all rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// 3. Neither the name of this module nor the names of its contributors may
+// be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+/**
+ * All code is in an anonymous closure to keep the global namespace clean.
+ *
+ * @param {number=} overflow
+ * @param {number=} startdenom
+ */
+(function (pool, math, width, chunks, significance, overflow, startdenom) {
+
+
+ //
+ // seedrandom()
+ // This is the seedrandom function described above.
+ //
+ math['seedrandom'] = function seedrandom(seed, use_entropy) {
+ var key = [];
+ var arc4;
+
+ // Flatten the seed string or build one from local entropy if needed.
+ seed = mixkey(flatten(
+ use_entropy ? [seed, pool] : arguments.length ? seed : [new Date().getTime(), pool, window], 3), key);
+
+ // Use the seed to initialize an ARC4 generator.
+ arc4 = new ARC4(key);
+
+ // Mix the randomness into accumulated entropy.
+ mixkey(arc4.S, pool);
+
+ // Override Math.random
+ // This function returns a random double in [0, 1) that contains
+ // randomness in every bit of the mantissa of the IEEE 754 value.
+ math['random'] = function random() { // Closure to return a random double:
+ var n = arc4.g(chunks); // Start with a numerator n < 2 ^ 48
+ var d = startdenom; // and denominator d = 2 ^ 48.
+ var x = 0; // and no 'extra last byte'.
+ while (n < significance) { // Fill up all significant digits by
+ n = (n + x) * width; // shifting numerator and
+ d *= width; // denominator and generating a
+ x = arc4.g(1); // new least-significant-byte.
+ }
+ while (n >= overflow) { // To avoid rounding up, before adding
+ n /= 2; // last byte, shift everything
+ d /= 2; // right using integer math until
+ x >>>= 1; // we have exactly the desired bits.
+ }
+ return (n + x) / d; // Form the number within [0, 1).
+ };
+
+ // Return the seed that was used
+ return seed;
+ };
+
+ //
+ // ARC4
+ //
+ // An ARC4 implementation. The constructor takes a key in the form of
+ // an array of at most (width) integers that should be 0 <= x < (width).
+ //
+ // The g(count) method returns a pseudorandom integer that concatenates
+ // the next (count) outputs from ARC4. Its return value is a number x
+ // that is in the range 0 <= x < (width ^ count).
+ //
+ /** @constructor */
+
+ function ARC4(key) {
+ var t, u, me = this,
+ keylen = key.length;
+ var i = 0,
+ j = me.i = me.j = me.m = 0;
+ me.S = [];
+ me.c = [];
+
+ // The empty key [] is treated as [0].
+ if (!keylen) {
+ key = [keylen++];
+ }
+
+ // Set up S using the standard key scheduling algorithm.
+ while (i < width) {
+ me.S[i] = i++;
+ }
+ for (i = 0; i < width; i++) {
+ t = me.S[i];
+ j = lowbits(j + t + key[i % keylen]);
+ u = me.S[j];
+ me.S[i] = u;
+ me.S[j] = t;
+ }
+
+ // The "g" method returns the next (count) outputs as one number.
+ me.g = function getnext(count) {
+ var s = me.S;
+ var i = lowbits(me.i + 1);
+ var t = s[i];
+ var j = lowbits(me.j + t);
+ var u = s[j];
+ s[i] = u;
+ s[j] = t;
+ var r = s[lowbits(t + u)];
+ while (--count) {
+ i = lowbits(i + 1);
+ t = s[i];
+ j = lowbits(j + t);
+ u = s[j];
+ s[i] = u;
+ s[j] = t;
+ r = r * width + s[lowbits(t + u)];
+ }
+ me.i = i;
+ me.j = j;
+ return r;
+ };
+ // For robust unpredictability discard an initial batch of values.
+ // See http://www.rsa.com/rsalabs/node.asp?id=2009
+ me.g(width);
+ }
+
+ //
+ // flatten()
+ // Converts an object tree to nested arrays of strings.
+ //
+ /** @param {Object=} result
+ * @param {string=} prop
+ * @param {string=} typ */
+
+ function flatten(obj, depth, result, prop, typ) {
+ result = [];
+ typ = typeof (obj);
+ if (depth && typ == 'object') {
+ for (prop in obj) {
+ if (prop.indexOf('S') < 5) { // Avoid FF3 bug (local/sessionStorage)
+ try {
+ result.push(flatten(obj[prop], depth - 1));
+ }
+ catch (e) {
+ }
+ }
+ }
+ }
+ return (result.length ? result : obj + (typ != 'string' ? '\0' : ''));
+ }
+
+ //
+ // mixkey()
+ // Mixes a string seed into a key that is an array of integers, and
+ // returns a shortened string seed that is equivalent to the result key.
+ //
+ /** @param {number=} smear
+ * @param {number=} j */
+
+ function mixkey(seed, key, smear, j) {
+ seed += ''; // Ensure the seed is a string
+ smear = 0;
+ for (j = 0; j < seed.length; j++) {
+ key[lowbits(j)] = lowbits((smear ^= key[lowbits(j)] * 19) + seed.charCodeAt(j));
+ }
+ seed = '';
+ for (j in key) {
+ seed += String.fromCharCode(key[j]);
+ }
+ return seed;
+ }
+
+ //
+ // lowbits()
+ // A quick "n mod width" for width a power of 2.
+ //
+
+
+ function lowbits(n) {
+ return n & (width - 1);
+ }
+
+ //
+ // The following constants are related to IEEE 754 limits.
+ //
+ startdenom = math.pow(width, chunks);
+ significance = math.pow(2, significance);
+ overflow = significance * 2;
+
+ //
+ // When seedrandom.js is loaded, we immediately mix a few bits
+ // from the built-in RNG into the entropy pool. Because we do
+ // not want to intefere with determinstic PRNG state later,
+ // seedrandom will not call math.random on its own again after
+ // initialization.
+ //
+ mixkey(math.random(), pool);
+
+ // End anonymous scope, and pass initial values.
+})([], // pool: entropy pool starts empty
+ Math, // math: package containing random, pow, and seedrandom
+ 256, // width: each RC4 output is 0 <= x < 256
+ 6, // chunks: at least six RC4 outputs for each double
+ 52 // significance: there are 52 significant digits in a double
+);
+
+
+// This is not really a random number generator object, and two SeededRandom
+// objects will conflict with one another, but it's good enough for generating
+// the rsa key.
+function SeededRandom() {
+}
+
+function SRnextBytes(ba) {
+ var i;
+ for (i = 0; i < ba.length; i++) {
+ ba[i] = Math.floor(Math.random() * 256);
+ }
+}
+
+SeededRandom.prototype.nextBytes = SRnextBytes;
+
+// prng4.js - uses Arcfour as a PRNG
+
+function Arcfour() {
+ this.i = 0;
+ this.j = 0;
+ this.S = new Array();
+}
+
+// Initialize arcfour context from key, an array of ints, each from [0..255]
+function ARC4init(key) {
+ var i, j, t;
+ for (i = 0; i < 256; ++i)
+ this.S[i] = i;
+ j = 0;
+ for (i = 0; i < 256; ++i) {
+ j = (j + this.S[i] + key[i % key.length]) & 255;
+ t = this.S[i];
+ this.S[i] = this.S[j];
+ this.S[j] = t;
+ }
+ this.i = 0;
+ this.j = 0;
+}
+
+function ARC4next() {
+ var t;
+ this.i = (this.i + 1) & 255;
+ this.j = (this.j + this.S[this.i]) & 255;
+ t = this.S[this.i];
+ this.S[this.i] = this.S[this.j];
+ this.S[this.j] = t;
+ return this.S[(t + this.S[this.i]) & 255];
+}
+
+Arcfour.prototype.init = ARC4init;
+Arcfour.prototype.next = ARC4next;
+
+// Plug in your RNG constructor here
+function prng_newstate() {
+ return new Arcfour();
+}
+
+// Pool size must be a multiple of 4 and greater than 32.
+// An array of bytes the size of the pool will be passed to init()
+var rng_psize = 256;
+
+// Random number generator - requires a PRNG backend, e.g. prng4.js
+
+// For best results, put code like
+//
+// in your main HTML document.
+
+var rng_state;
+var rng_pool;
+var rng_pptr;
+
+// Mix in a 32-bit integer into the pool
+function rng_seed_int(x) {
+ rng_pool[rng_pptr++] ^= x & 255;
+ rng_pool[rng_pptr++] ^= (x >> 8) & 255;
+ rng_pool[rng_pptr++] ^= (x >> 16) & 255;
+ rng_pool[rng_pptr++] ^= (x >> 24) & 255;
+ if (rng_pptr >= rng_psize) rng_pptr -= rng_psize;
+}
+
+// Mix in the current time (w/milliseconds) into the pool
+function rng_seed_time() {
+ rng_seed_int(new Date().getTime());
+}
+
+// Initialize the pool with junk if needed.
+if (rng_pool == null) {
+ rng_pool = new Array();
+ rng_pptr = 0;
+ var t;
+ if (navigator.appName == "Netscape" && navigator.appVersion < "5" && window.crypto) {
+ // Extract entropy (256 bits) from NS4 RNG if available
+ var z = window.crypto.random(32);
+ for (t = 0; t < z.length; ++t)
+ rng_pool[rng_pptr++] = z.charCodeAt(t) & 255;
+ }
+ while (rng_pptr < rng_psize) { // extract some randomness from Math.random()
+ t = Math.floor(65536 * Math.random());
+ rng_pool[rng_pptr++] = t >>> 8;
+ rng_pool[rng_pptr++] = t & 255;
+ }
+ rng_pptr = 0;
+ rng_seed_time();
+ //rng_seed_int(window.screenX);
+ //rng_seed_int(window.screenY);
+}
+
+function rng_get_byte() {
+ if (rng_state == null) {
+ rng_seed_time();
+ rng_state = prng_newstate();
+ rng_state.init(rng_pool);
+ for (rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr)
+ rng_pool[rng_pptr] = 0;
+ rng_pptr = 0;
+ //rng_pool = null;
+ }
+ // TODO: allow reseeding after first request
+ return rng_state.next();
+}
+
+function rng_get_bytes(ba) {
+ var i;
+ for (i = 0; i < ba.length; ++i) ba[i] = rng_get_byte();
+}
+
+function SecureRandom() {
+}
+
+SecureRandom.prototype.nextBytes = rng_get_bytes;
+
+
+/**
+ *
+ * Secure Hash Algorithm (SHA256)
+ * http://www.webtoolkit.info/
+ *
+ * Original code by Angel Marin, Paul Johnston.
+ *
+ **/
+
+function SHA256(s) {
+
+ var chrsz = 8;
+ var hexcase = 0;
+
+ function safe_add(x, y) {
+ var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+ var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+ return (msw << 16) | (lsw & 0xFFFF);
+ }
+
+ function S(X, n) {
+ return (X >>> n) | (X << (32 - n));
+ }
+
+ function R(X, n) {
+ return (X >>> n);
+ }
+
+ function Ch(x, y, z) {
+ return ((x & y) ^ ((~x) & z));
+ }
+
+ function Maj(x, y, z) {
+ return ((x & y) ^ (x & z) ^ (y & z));
+ }
+
+ function Sigma0256(x) {
+ return (S(x, 2) ^ S(x, 13) ^ S(x, 22));
+ }
+
+ function Sigma1256(x) {
+ return (S(x, 6) ^ S(x, 11) ^ S(x, 25));
+ }
+
+ function Gamma0256(x) {
+ return (S(x, 7) ^ S(x, 18) ^ R(x, 3));
+ }
+
+ function Gamma1256(x) {
+ return (S(x, 17) ^ S(x, 19) ^ R(x, 10));
+ }
+
+ function core_sha256(m, l) {
+ var K = new Array(0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786, 0xFC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x6CA6351, 0x14292967, 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2);
+ var HASH = new Array(0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19);
+ var W = new Array(64);
+ var a, b, c, d, e, f, g, h, i, j;
+ var T1, T2;
+
+ m[l >> 5] |= 0x80 << (24 - l % 32);
+ m[((l + 64 >> 9) << 4) + 15] = l;
+
+ for (var i = 0; i < m.length; i += 16) {
+ a = HASH[0];
+ b = HASH[1];
+ c = HASH[2];
+ d = HASH[3];
+ e = HASH[4];
+ f = HASH[5];
+ g = HASH[6];
+ h = HASH[7];
+
+ for (var j = 0; j < 64; j++) {
+ if (j < 16) W[j] = m[j + i];
+ else W[j] = safe_add(safe_add(safe_add(Gamma1256(W[j - 2]), W[j - 7]), Gamma0256(W[j - 15])), W[j - 16]);
+
+ T1 = safe_add(safe_add(safe_add(safe_add(h, Sigma1256(e)), Ch(e, f, g)), K[j]), W[j]);
+ T2 = safe_add(Sigma0256(a), Maj(a, b, c));
+
+ h = g;
+ g = f;
+ f = e;
+ e = safe_add(d, T1);
+ d = c;
+ c = b;
+ b = a;
+ a = safe_add(T1, T2);
+ }
+
+ HASH[0] = safe_add(a, HASH[0]);
+ HASH[1] = safe_add(b, HASH[1]);
+ HASH[2] = safe_add(c, HASH[2]);
+ HASH[3] = safe_add(d, HASH[3]);
+ HASH[4] = safe_add(e, HASH[4]);
+ HASH[5] = safe_add(f, HASH[5]);
+ HASH[6] = safe_add(g, HASH[6]);
+ HASH[7] = safe_add(h, HASH[7]);
+ }
+ return HASH;
+ }
+
+ function str2binb(str) {
+ var bin = Array();
+ var mask = (1 << chrsz) - 1;
+ for (var i = 0; i < str.length * chrsz; i += chrsz) {
+ bin[i >> 5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i % 32);
+ }
+ return bin;
+ }
+
+ function Utf8Encode(string) {
+ string = string.replace(/\r\n/g, "\n");
+ var utftext = "";
+
+ for (var n = 0; n < string.length; n++) {
+
+ var c = string.charCodeAt(n);
+
+ if (c < 128) {
+ utftext += String.fromCharCode(c);
+ }
+ else if ((c > 127) && (c < 2048)) {
+ utftext += String.fromCharCode((c >> 6) | 192);
+ utftext += String.fromCharCode((c & 63) | 128);
+ }
+ else {
+ utftext += String.fromCharCode((c >> 12) | 224);
+ utftext += String.fromCharCode(((c >> 6) & 63) | 128);
+ utftext += String.fromCharCode((c & 63) | 128);
+ }
+
+ }
+
+ return utftext;
+ }
+
+ function binb2hex(binarray) {
+ var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
+ var str = "";
+ for (var i = 0; i < binarray.length * 4; i++) {
+ str += hex_tab.charAt((binarray[i >> 2] >> ((3 - i % 4) * 8 + 4)) & 0xF) +
+ hex_tab.charAt((binarray[i >> 2] >> ((3 - i % 4) * 8)) & 0xF);
+ }
+ return str;
+ }
+
+ s = Utf8Encode(s);
+ return binb2hex(core_sha256(str2binb(s), s.length * chrsz));
+}
+
+var sha256 = {}
+sha256.hex = function (s) {
+ return SHA256(s);
+}
+
+/**
+ *
+ * Secure Hash Algorithm (SHA1)
+ * http://www.webtoolkit.info/
+ *
+ **/
+
+function SHA1(msg) {
+
+ function rotate_left(n, s) {
+ var t4 = (n << s) | (n >>> (32 - s));
+ return t4;
+ };
+
+ function lsb_hex(val) {
+ var str = "";
+ var i;
+ var vh;
+ var vl;
+
+ for (i = 0; i <= 6; i += 2) {
+ vh = (val >>> (i * 4 + 4)) & 0x0f;
+ vl = (val >>> (i * 4)) & 0x0f;
+ str += vh.toString(16) + vl.toString(16);
+ }
+ return str;
+ };
+
+ function cvt_hex(val) {
+ var str = "";
+ var i;
+ var v;
+
+ for (i = 7; i >= 0; i--) {
+ v = (val >>> (i * 4)) & 0x0f;
+ str += v.toString(16);
+ }
+ return str;
+ };
+
+
+ function Utf8Encode(string) {
+ string = string.replace(/\r\n/g, "\n");
+ var utftext = "";
+
+ for (var n = 0; n < string.length; n++) {
+
+ var c = string.charCodeAt(n);
+
+ if (c < 128) {
+ utftext += String.fromCharCode(c);
+ }
+ else if ((c > 127) && (c < 2048)) {
+ utftext += String.fromCharCode((c >> 6) | 192);
+ utftext += String.fromCharCode((c & 63) | 128);
+ }
+ else {
+ utftext += String.fromCharCode((c >> 12) | 224);
+ utftext += String.fromCharCode(((c >> 6) & 63) | 128);
+ utftext += String.fromCharCode((c & 63) | 128);
+ }
+
+ }
+
+ return utftext;
+ };
+
+ var blockstart;
+ var i, j;
+ var W = new Array(80);
+ var H0 = 0x67452301;
+ var H1 = 0xEFCDAB89;
+ var H2 = 0x98BADCFE;
+ var H3 = 0x10325476;
+ var H4 = 0xC3D2E1F0;
+ var A, B, C, D, E;
+ var temp;
+
+ msg = Utf8Encode(msg);
+
+ var msg_len = msg.length;
+
+ var word_array = new Array();
+ for (i = 0; i < msg_len - 3; i += 4) {
+ j = msg.charCodeAt(i) << 24 | msg.charCodeAt(i + 1) << 16 |
+ msg.charCodeAt(i + 2) << 8 | msg.charCodeAt(i + 3);
+ word_array.push(j);
+ }
+
+ switch (msg_len % 4) {
+ case 0:
+ i = 0x080000000;
+ break;
+ case 1:
+ i = msg.charCodeAt(msg_len - 1) << 24 | 0x0800000;
+ break;
+
+ case 2:
+ i = msg.charCodeAt(msg_len - 2) << 24 | msg.charCodeAt(msg_len - 1) << 16 | 0x08000;
+ break;
+
+ case 3:
+ i = msg.charCodeAt(msg_len - 3) << 24 | msg.charCodeAt(msg_len - 2) << 16 | msg.charCodeAt(msg_len - 1) << 8 | 0x80;
+ break;
+ }
+
+ word_array.push(i);
+
+ while ((word_array.length % 16) != 14) word_array.push(0);
+
+ word_array.push(msg_len >>> 29);
+ word_array.push((msg_len << 3) & 0x0ffffffff);
+
+
+ for (blockstart = 0; blockstart < word_array.length; blockstart += 16) {
+
+ for (i = 0; i < 16; i++) W[i] = word_array[blockstart + i];
+ for (i = 16; i <= 79; i++) W[i] = rotate_left(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1);
+
+ A = H0;
+ B = H1;
+ C = H2;
+ D = H3;
+ E = H4;
+
+ for (i = 0; i <= 19; i++) {
+ temp = (rotate_left(A, 5) + ((B & C) | (~B & D)) + E + W[i] + 0x5A827999) & 0x0ffffffff;
+ E = D;
+ D = C;
+ C = rotate_left(B, 30);
+ B = A;
+ A = temp;
+ }
+
+ for (i = 20; i <= 39; i++) {
+ temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1) & 0x0ffffffff;
+ E = D;
+ D = C;
+ C = rotate_left(B, 30);
+ B = A;
+ A = temp;
+ }
+
+ for (i = 40; i <= 59; i++) {
+ temp = (rotate_left(A, 5) + ((B & C) | (B & D) | (C & D)) + E + W[i] + 0x8F1BBCDC) & 0x0ffffffff;
+ E = D;
+ D = C;
+ C = rotate_left(B, 30);
+ B = A;
+ A = temp;
+ }
+
+ for (i = 60; i <= 79; i++) {
+ temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6) & 0x0ffffffff;
+ E = D;
+ D = C;
+ C = rotate_left(B, 30);
+ B = A;
+ A = temp;
+ }
+
+ H0 = (H0 + A) & 0x0ffffffff;
+ H1 = (H1 + B) & 0x0ffffffff;
+ H2 = (H2 + C) & 0x0ffffffff;
+ H3 = (H3 + D) & 0x0ffffffff;
+ H4 = (H4 + E) & 0x0ffffffff;
+
+ }
+
+ var temp = cvt_hex(H0) + cvt_hex(H1) + cvt_hex(H2) + cvt_hex(H3) + cvt_hex(H4);
+
+ return temp.toLowerCase();
+
+}
+
+var sha1 = {}
+sha1.hex = function (s) {
+ return SHA1(s);
+}
+
+/**
+ *
+ * MD5 (Message-Digest Algorithm)
+ * http://www.webtoolkit.info/
+ *
+ **/
+
+var MD5 = function (string) {
+
+ function RotateLeft(lValue, iShiftBits) {
+ return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
+ }
+
+ function AddUnsigned(lX, lY) {
+ var lX4, lY4, lX8, lY8, lResult;
+ lX8 = (lX & 0x80000000);
+ lY8 = (lY & 0x80000000);
+ lX4 = (lX & 0x40000000);
+ lY4 = (lY & 0x40000000);
+ lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
+ if (lX4 & lY4) {
+ return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
+ }
+ if (lX4 | lY4) {
+ if (lResult & 0x40000000) {
+ return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
+ } else {
+ return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
+ }
+ } else {
+ return (lResult ^ lX8 ^ lY8);
+ }
+ }
+
+ function F(x, y, z) {
+ return (x & y) | ((~x) & z);
+ }
+
+ function G(x, y, z) {
+ return (x & z) | (y & (~z));
+ }
+
+ function H(x, y, z) {
+ return (x ^ y ^ z);
+ }
+
+ function I(x, y, z) {
+ return (y ^ (x | (~z)));
+ }
+
+ function FF(a, b, c, d, x, s, ac) {
+ a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac));
+ return AddUnsigned(RotateLeft(a, s), b);
+ };
+
+ function GG(a, b, c, d, x, s, ac) {
+ a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac));
+ return AddUnsigned(RotateLeft(a, s), b);
+ };
+
+ function HH(a, b, c, d, x, s, ac) {
+ a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac));
+ return AddUnsigned(RotateLeft(a, s), b);
+ };
+
+ function II(a, b, c, d, x, s, ac) {
+ a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac));
+ return AddUnsigned(RotateLeft(a, s), b);
+ };
+
+ function ConvertToWordArray(string) {
+ var lWordCount;
+ var lMessageLength = string.length;
+ var lNumberOfWords_temp1 = lMessageLength + 8;
+ var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;
+ var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
+ var lWordArray = Array(lNumberOfWords - 1);
+ var lBytePosition = 0;
+ var lByteCount = 0;
+ while (lByteCount < lMessageLength) {
+ lWordCount = (lByteCount - (lByteCount % 4)) / 4;
+ lBytePosition = (lByteCount % 4) * 8;
+ lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition));
+ lByteCount++;
+ }
+ lWordCount = (lByteCount - (lByteCount % 4)) / 4;
+ lBytePosition = (lByteCount % 4) * 8;
+ lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
+ lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
+ lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
+ return lWordArray;
+ };
+
+ function WordToHex(lValue) {
+ var WordToHexValue = "", WordToHexValue_temp = "", lByte, lCount;
+ for (lCount = 0; lCount <= 3; lCount++) {
+ lByte = (lValue >>> (lCount * 8)) & 255;
+ WordToHexValue_temp = "0" + lByte.toString(16);
+ WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2);
+ }
+ return WordToHexValue;
+ };
+
+ function Utf8Encode(string) {
+ string = string.replace(/\r\n/g, "\n");
+ var utftext = "";
+
+ for (var n = 0; n < string.length; n++) {
+
+ var c = string.charCodeAt(n);
+
+ if (c < 128) {
+ utftext += String.fromCharCode(c);
+ }
+ else if ((c > 127) && (c < 2048)) {
+ utftext += String.fromCharCode((c >> 6) | 192);
+ utftext += String.fromCharCode((c & 63) | 128);
+ }
+ else {
+ utftext += String.fromCharCode((c >> 12) | 224);
+ utftext += String.fromCharCode(((c >> 6) & 63) | 128);
+ utftext += String.fromCharCode((c & 63) | 128);
+ }
+
+ }
+
+ return utftext;
+ };
+
+ var x = Array();
+ var k, AA, BB, CC, DD, a, b, c, d;
+ var S11 = 7, S12 = 12, S13 = 17, S14 = 22;
+ var S21 = 5, S22 = 9, S23 = 14, S24 = 20;
+ var S31 = 4, S32 = 11, S33 = 16, S34 = 23;
+ var S41 = 6, S42 = 10, S43 = 15, S44 = 21;
+
+ string = Utf8Encode(string);
+
+ x = ConvertToWordArray(string);
+
+ a = 0x67452301;
+ b = 0xEFCDAB89;
+ c = 0x98BADCFE;
+ d = 0x10325476;
+
+ for (k = 0; k < x.length; k += 16) {
+ AA = a;
+ BB = b;
+ CC = c;
+ DD = d;
+ a = FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
+ d = FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
+ c = FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
+ b = FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
+ a = FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
+ d = FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
+ c = FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
+ b = FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
+ a = FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
+ d = FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
+ c = FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
+ b = FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
+ a = FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
+ d = FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
+ c = FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
+ b = FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
+ a = GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
+ d = GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
+ c = GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
+ b = GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
+ a = GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
+ d = GG(d, a, b, c, x[k + 10], S22, 0x2441453);
+ c = GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
+ b = GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
+ a = GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
+ d = GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
+ c = GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
+ b = GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
+ a = GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
+ d = GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
+ c = GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
+ b = GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
+ a = HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
+ d = HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
+ c = HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
+ b = HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
+ a = HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
+ d = HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
+ c = HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
+ b = HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
+ a = HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
+ d = HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
+ c = HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
+ b = HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
+ a = HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
+ d = HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
+ c = HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
+ b = HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
+ a = II(a, b, c, d, x[k + 0], S41, 0xF4292244);
+ d = II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
+ c = II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
+ b = II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
+ a = II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
+ d = II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
+ c = II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
+ b = II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
+ a = II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
+ d = II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
+ c = II(c, d, a, b, x[k + 6], S43, 0xA3014314);
+ b = II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
+ a = II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
+ d = II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
+ c = II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
+ b = II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
+ a = AddUnsigned(a, AA);
+ b = AddUnsigned(b, BB);
+ c = AddUnsigned(c, CC);
+ d = AddUnsigned(d, DD);
+ }
+
+ var temp = WordToHex(a) + WordToHex(b) + WordToHex(c) + WordToHex(d);
+
+ return temp.toLowerCase();
+}
+
+// Depends on jsbn.js and rng.js
+// Version 1.1: support utf-8 encoding in pkcs1pad2
+// convert a (hex) string to a bignum object
+
+
+function parseBigInt(str, r) {
+ return new BigInteger(str, r);
+}
+
+function linebrk(s, n) {
+ var ret = "";
+ var i = 0;
+ while (i + n < s.length) {
+ ret += s.substring(i, i + n) + "\n";
+ i += n;
+ }
+ return ret + s.substring(i, s.length);
+}
+
+function byte2Hex(b) {
+ if (b < 0x10) return "0" + b.toString(16);
+ else return b.toString(16);
+}
+
+// PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint
+
+
+function pkcs1pad2(s, n) {
+ if (n < s.length + 11) { // TODO: fix for utf-8
+ //alert("Message too long for RSA (n=" + n + ", l=" + s.length + ")");
+ //return null;
+ throw "Message too long for RSA (n=" + n + ", l=" + s.length + ")";
+ }
+ var ba = new Array();
+ var i = s.length - 1;
+ while (i >= 0 && n > 0) {
+ var c = s.charCodeAt(i--);
+ if (c < 128) { // encode using utf-8
+ ba[--n] = c;
+ }
+ else if ((c > 127) && (c < 2048)) {
+ ba[--n] = (c & 63) | 128;
+ ba[--n] = (c >> 6) | 192;
+ }
+ else {
+ ba[--n] = (c & 63) | 128;
+ ba[--n] = ((c >> 6) & 63) | 128;
+ ba[--n] = (c >> 12) | 224;
+ }
+ }
+ ba[--n] = 0;
+ var rng = new SecureRandom();
+ var x = new Array();
+ while (n > 2) { // random non-zero pad
+ x[0] = 0;
+ while (x[0] == 0) rng.nextBytes(x);
+ ba[--n] = x[0];
+ }
+ ba[--n] = 2;
+ ba[--n] = 0;
+ return new BigInteger(ba);
+}
+
+// "empty" RSA key constructor
+
+
+function RSAKey() {
+ this.n = null;
+ this.e = 0;
+ this.d = null;
+ this.p = null;
+ this.q = null;
+ this.dmp1 = null;
+ this.dmq1 = null;
+ this.coeff = null;
+}
+
+// Set the public key fields N and e from hex strings
+
+
+function RSASetPublic(N, E) {
+ if (N != null && E != null && N.length > 0 && E.length > 0) {
+ this.n = parseBigInt(N, 16);
+ this.e = parseInt(E, 16);
+ }
+ else alert("Invalid RSA public key");
+}
+
+// Perform raw public operation on "x": return x^e (mod n)
+
+
+function RSADoPublic(x) {
+ return x.modPowInt(this.e, this.n);
+}
+
+// Return the PKCS#1 RSA encryption of "text" as an even-length hex string
+
+
+function RSAEncrypt(text) {
+ var m = pkcs1pad2(text, (this.n.bitLength() + 7) >> 3);
+ if (m == null) return null;
+ var c = this.doPublic(m);
+ if (c == null) return null;
+ var h = c.toString(16);
+ if ((h.length & 1) == 0) return h;
+ else return "0" + h;
+}
+
+// Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string
+//function RSAEncryptB64(text) {
+// var h = this.encrypt(text);
+// if(h) return hex2b64(h); else return null;
+//}
+// protected
+RSAKey.prototype.doPublic = RSADoPublic;
+
+// public
+RSAKey.prototype.setPublic = RSASetPublic;
+RSAKey.prototype.encrypt = RSAEncrypt;
+
+// Version 1.1: support utf-8 decoding in pkcs1unpad2
+// Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext
+
+function pkcs1unpad2(d, n) {
+ var b = d.toByteArray();
+ var i = 0;
+ while (i < b.length && b[i] == 0) ++i;
+ if (b.length - i != n - 1 || b[i] != 2) return null;
+ ++i;
+ while (b[i] != 0)
+ if (++i >= b.length) return null;
+ var ret = "";
+ while (++i < b.length) {
+ var c = b[i] & 255;
+ if (c < 128) { // utf-8 decode
+ ret += String.fromCharCode(c);
+ }
+ else if ((c > 191) && (c < 224)) {
+ ret += String.fromCharCode(((c & 31) << 6) | (b[i + 1] & 63));
+ ++i;
+ }
+ else {
+ ret += String.fromCharCode(((c & 15) << 12) | ((b[i + 1] & 63) << 6) | (b[i + 2] & 63));
+ i += 2;
+ }
+ }
+ return ret;
+}
+
+// Set the private key fields N, e, and d from hex strings
+function RSASetPrivate(N, E, D) {
+ if (N != null && E != null && N.length > 0 && E.length > 0) {
+ this.n = parseBigInt(N, 16);
+ this.e = parseInt(E, 16);
+ this.d = parseBigInt(D, 16);
+ }
+ else alert("Invalid RSA private key");
+}
+
+// Set the private key fields N, e, d and CRT params from hex strings
+function RSASetPrivateEx(N, E, D, P, Q, DP, DQ, C) {
+ if (N != null && E != null && N.length > 0 && E.length > 0) {
+ this.n = parseBigInt(N, 16);
+ this.e = parseInt(E, 16);
+ this.d = parseBigInt(D, 16);
+ this.p = parseBigInt(P, 16);
+ this.q = parseBigInt(Q, 16);
+ this.dmp1 = parseBigInt(DP, 16);
+ this.dmq1 = parseBigInt(DQ, 16);
+ this.coeff = parseBigInt(C, 16);
+ }
+ else alert("Invalid RSA private key");
+}
+
+// Generate a new random private key B bits long, using public expt E
+function RSAGenerate(B, E) {
+ var rng = new SeededRandom();
+ var qs = B >> 1;
+ this.e = parseInt(E, 16);
+ var ee = new BigInteger(E, 16);
+ for (; ;) {
+ for (; ;) {
+ this.p = new BigInteger(B - qs, 1, rng);
+ if (this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.p.isProbablePrime(10)) break;
+ }
+ for (; ;) {
+ this.q = new BigInteger(qs, 1, rng);
+ if (this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.q.isProbablePrime(10)) break;
+ }
+ if (this.p.compareTo(this.q) <= 0) {
+ var t = this.p;
+ this.p = this.q;
+ this.q = t;
+ }
+ var p1 = this.p.subtract(BigInteger.ONE);
+ var q1 = this.q.subtract(BigInteger.ONE);
+ var phi = p1.multiply(q1);
+ if (phi.gcd(ee).compareTo(BigInteger.ONE) == 0) {
+ this.n = this.p.multiply(this.q);
+ this.d = ee.modInverse(phi);
+ this.dmp1 = this.d.mod(p1);
+ this.dmq1 = this.d.mod(q1);
+ this.coeff = this.q.modInverse(this.p);
+ break;
+ }
+ }
+}
+
+// Perform raw private operation on "x": return x^d (mod n)
+function RSADoPrivate(x) {
+ if (this.p == null || this.q == null) return x.modPow(this.d, this.n);
+ // TODO: re-calculate any missing CRT params
+ var xp = x.mod(this.p).modPow(this.dmp1, this.p);
+ var xq = x.mod(this.q).modPow(this.dmq1, this.q);
+ while (xp.compareTo(xq) < 0)
+ xp = xp.add(this.p);
+ return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq);
+}
+
+// Return the PKCS#1 RSA decryption of "ctext".
+// "ctext" is an even-length hex string and the output is a plain string.
+function RSADecrypt(ctext) {
+ var c = parseBigInt(ctext, 16);
+ var m = this.doPrivate(c);
+ if (m == null) return null;
+ return pkcs1unpad2(m, (this.n.bitLength() + 7) >> 3);
+}
+
+// protected
+RSAKey.prototype.doPrivate = RSADoPrivate;
+
+// public
+RSAKey.prototype.setPrivate = RSASetPrivate;
+RSAKey.prototype.setPrivateEx = RSASetPrivateEx;
+RSAKey.prototype.generate = RSAGenerate;
+RSAKey.prototype.decrypt = RSADecrypt;
+
+
+//
+// rsa-sign.js - adding signing functions to RSAKey class.
+//
+//
+// version: 1.0 (2010-Jun-03)
+//
+// Copyright (c) 2010 Kenji Urushima (kenji.urushima@gmail.com)
+//
+// This software is licensed under the terms of the MIT License.
+// http://www.opensource.org/licenses/mit-license.php
+//
+// The above copyright and license notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// Depends on:
+// function sha1.hex(s) of sha1.js
+// jsbn.js
+// jsbn2.js
+// rsa.js
+// rsa2.js
+//
+// keysize / pmstrlen
+// 512 / 128
+// 1024 / 256
+// 2048 / 512
+// 4096 / 1024
+// As for _RSASGIN_DIHEAD values for each hash algorithm, see PKCS#1 v2.1 spec (p38).
+var _RSASIGN_DIHEAD = [];
+_RSASIGN_DIHEAD['sha1'] = "3021300906052b0e03021a05000414";
+_RSASIGN_DIHEAD['sha256'] = "3031300d060960864801650304020105000420";
+//_RSASIGN_DIHEAD['md2'] = "3020300c06082a864886f70d020205000410";
+//_RSASIGN_DIHEAD['md5'] = "3020300c06082a864886f70d020505000410";
+//_RSASIGN_DIHEAD['sha384'] = "3041300d060960864801650304020205000430";
+//_RSASIGN_DIHEAD['sha512'] = "3051300d060960864801650304020305000440";
+var _RSASIGN_HASHHEXFUNC = [];
+_RSASIGN_HASHHEXFUNC['sha1'] = sha1.hex;
+_RSASIGN_HASHHEXFUNC['sha256'] = sha256.hex;
+
+// ========================================================================
+// Signature Generation
+// ========================================================================
+
+function _rsasign_getHexPaddedDigestInfoForString(s, keySize, hashAlg) {
+ var pmStrLen = keySize / 4;
+ var hashFunc = _RSASIGN_HASHHEXFUNC[hashAlg];
+ var sHashHex = hashFunc(s);
+
+ var sHead = "0001";
+ var sTail = "00" + _RSASIGN_DIHEAD[hashAlg] + sHashHex;
+ var sMid = "";
+ var fLen = pmStrLen - sHead.length - sTail.length;
+ for (var i = 0; i < fLen; i += 2) {
+ sMid += "ff";
+ }
+ sPaddedMessageHex = sHead + sMid + sTail;
+ return sPaddedMessageHex;
+}
+
+function _rsasign_signString(s, hashAlg) {
+ var hPM = _rsasign_getHexPaddedDigestInfoForString(s, this.n.bitLength(), hashAlg);
+ var biPaddedMessage = parseBigInt(hPM, 16);
+ var biSign = this.doPrivate(biPaddedMessage);
+ var hexSign = biSign.toString(16);
+ return hexSign;
+}
+
+function _rsasign_signStringWithSHA1(s) {
+ var hPM = _rsasign_getHexPaddedDigestInfoForString(s, this.n.bitLength(), 'sha1');
+ var biPaddedMessage = parseBigInt(hPM, 16);
+ var biSign = this.doPrivate(biPaddedMessage);
+ var hexSign = biSign.toString(16);
+ return hexSign;
+}
+
+function _rsasign_signStringWithSHA256(s) {
+ var hPM = _rsasign_getHexPaddedDigestInfoForString(s, this.n.bitLength(), 'sha256');
+ var biPaddedMessage = parseBigInt(hPM, 16);
+ var biSign = this.doPrivate(biPaddedMessage);
+ var hexSign = biSign.toString(16);
+ return hexSign;
+}
+
+// ========================================================================
+// Signature Verification
+// ========================================================================
+
+function _rsasign_getDecryptSignatureBI(biSig, hN, hE) {
+ var rsa = new RSAKey();
+ rsa.setPublic(hN, hE);
+ var biDecryptedSig = rsa.doPublic(biSig);
+ return biDecryptedSig;
+}
+
+function _rsasign_getHexDigestInfoFromSig(biSig, hN, hE) {
+ var biDecryptedSig = _rsasign_getDecryptSignatureBI(biSig, hN, hE);
+ var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, '');
+ return hDigestInfo;
+}
+
+function _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo) {
+ for (var algName in _RSASIGN_DIHEAD) {
+ var head = _RSASIGN_DIHEAD[algName];
+ var len = head.length;
+ if (hDigestInfo.substring(0, len) == head) {
+ var a = [algName, hDigestInfo.substring(len)];
+ return a;
+ }
+ }
+ return [];
+}
+
+function _rsasign_verifySignatureWithArgs(sMsg, biSig, hN, hE) {
+ var hDigestInfo = _rsasign_getHexDigestInfoFromSig(biSig, hN, hE);
+ var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo);
+ if (digestInfoAry.length == 0) return false;
+ var algName = digestInfoAry[0];
+ var diHashValue = digestInfoAry[1];
+ var ff = _RSASIGN_HASHHEXFUNC[algName];
+ var msgHashValue = ff(sMsg);
+ return (diHashValue == msgHashValue);
+}
+
+function _rsasign_verifyHexSignatureForMessage(hSig, sMsg) {
+ var biSig = parseBigInt(hSig, 16);
+ var result = _rsasign_verifySignatureWithArgs(sMsg, biSig, this.n.toString(16), this.e.toString(16));
+ return result;
+}
+
+function _rsasign_verifyString(sMsg, hSig) {
+ hSig = hSig.replace(/[ \n]+/g, "");
+ var biSig = parseBigInt(hSig, 16);
+ var biDecryptedSig = this.doPublic(biSig);
+ var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, '');
+ var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo);
+
+ if (digestInfoAry.length == 0) return false;
+ var algName = digestInfoAry[0];
+ var diHashValue = digestInfoAry[1];
+ var ff = _RSASIGN_HASHHEXFUNC[algName];
+ var msgHashValue = ff(sMsg);
+ return (diHashValue == msgHashValue);
+}
+
+RSAKey.prototype.signString = _rsasign_signString;
+RSAKey.prototype.signStringWithSHA1 = _rsasign_signStringWithSHA1;
+RSAKey.prototype.signStringWithSHA256 = _rsasign_signStringWithSHA256;
+
+RSAKey.prototype.verifyString = _rsasign_verifyString;
+RSAKey.prototype.verifyHexSignatureForMessage = _rsasign_verifyHexSignatureForMessage;
+
+/*
+ * jsaes version 0.1 - Copyright 2006 B. Poettering
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+// later modifications by wwwtyro@github
+
+var aes = (function () {
+
+ var my = {};
+
+ my.Sbox = new Array(99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22);
+
+ my.ShiftRowTab = new Array(0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11);
+
+ my.Init = function () {
+ my.Sbox_Inv = new Array(256);
+ for (var i = 0; i < 256; i++)
+ my.Sbox_Inv[my.Sbox[i]] = i;
+
+ my.ShiftRowTab_Inv = new Array(16);
+ for (var i = 0; i < 16; i++)
+ my.ShiftRowTab_Inv[my.ShiftRowTab[i]] = i;
+
+ my.xtime = new Array(256);
+ for (var i = 0; i < 128; i++) {
+ my.xtime[i] = i << 1;
+ my.xtime[128 + i] = (i << 1) ^ 0x1b;
+ }
+ }
+
+ my.Done = function () {
+ delete my.Sbox_Inv;
+ delete my.ShiftRowTab_Inv;
+ delete my.xtime;
+ }
+
+ my.ExpandKey = function (key) {
+ var kl = key.length,
+ ks, Rcon = 1;
+ switch (kl) {
+ case 16:
+ ks = 16 * (10 + 1);
+ break;
+ case 24:
+ ks = 16 * (12 + 1);
+ break;
+ case 32:
+ ks = 16 * (14 + 1);
+ break;
+ default:
+ alert("my.ExpandKey: Only key lengths of 16, 24 or 32 bytes allowed!");
+ }
+ for (var i = kl; i < ks; i += 4) {
+ var temp = key.slice(i - 4, i);
+ if (i % kl == 0) {
+ temp = new Array(my.Sbox[temp[1]] ^ Rcon, my.Sbox[temp[2]], my.Sbox[temp[3]], my.Sbox[temp[0]]);
+ if ((Rcon <<= 1) >= 256) Rcon ^= 0x11b;
+ }
+ else if ((kl > 24) && (i % kl == 16)) temp = new Array(my.Sbox[temp[0]], my.Sbox[temp[1]], my.Sbox[temp[2]], my.Sbox[temp[3]]);
+ for (var j = 0; j < 4; j++)
+ key[i + j] = key[i + j - kl] ^ temp[j];
+ }
+ }
+
+ my.Encrypt = function (block, key) {
+ var l = key.length;
+ my.AddRoundKey(block, key.slice(0, 16));
+ for (var i = 16; i < l - 16; i += 16) {
+ my.SubBytes(block, my.Sbox);
+ my.ShiftRows(block, my.ShiftRowTab);
+ my.MixColumns(block);
+ my.AddRoundKey(block, key.slice(i, i + 16));
+ }
+ my.SubBytes(block, my.Sbox);
+ my.ShiftRows(block, my.ShiftRowTab);
+ my.AddRoundKey(block, key.slice(i, l));
+ }
+
+ my.Decrypt = function (block, key) {
+ var l = key.length;
+ my.AddRoundKey(block, key.slice(l - 16, l));
+ my.ShiftRows(block, my.ShiftRowTab_Inv);
+ my.SubBytes(block, my.Sbox_Inv);
+ for (var i = l - 32; i >= 16; i -= 16) {
+ my.AddRoundKey(block, key.slice(i, i + 16));
+ my.MixColumns_Inv(block);
+ my.ShiftRows(block, my.ShiftRowTab_Inv);
+ my.SubBytes(block, my.Sbox_Inv);
+ }
+ my.AddRoundKey(block, key.slice(0, 16));
+ }
+
+ my.SubBytes = function (state, sbox) {
+ for (var i = 0; i < 16; i++)
+ state[i] = sbox[state[i]];
+ }
+
+ my.AddRoundKey = function (state, rkey) {
+ for (var i = 0; i < 16; i++)
+ state[i] ^= rkey[i];
+ }
+
+ my.ShiftRows = function (state, shifttab) {
+ var h = new Array().concat(state);
+ for (var i = 0; i < 16; i++)
+ state[i] = h[shifttab[i]];
+ }
+
+ my.MixColumns = function (state) {
+ for (var i = 0; i < 16; i += 4) {
+ var s0 = state[i + 0],
+ s1 = state[i + 1];
+ var s2 = state[i + 2],
+ s3 = state[i + 3];
+ var h = s0 ^ s1 ^ s2 ^ s3;
+ state[i + 0] ^= h ^ my.xtime[s0 ^ s1];
+ state[i + 1] ^= h ^ my.xtime[s1 ^ s2];
+ state[i + 2] ^= h ^ my.xtime[s2 ^ s3];
+ state[i + 3] ^= h ^ my.xtime[s3 ^ s0];
+ }
+ }
+
+ my.MixColumns_Inv = function (state) {
+ for (var i = 0; i < 16; i += 4) {
+ var s0 = state[i + 0],
+ s1 = state[i + 1];
+ var s2 = state[i + 2],
+ s3 = state[i + 3];
+ var h = s0 ^ s1 ^ s2 ^ s3;
+ var xh = my.xtime[h];
+ var h1 = my.xtime[my.xtime[xh ^ s0 ^ s2]] ^ h;
+ var h2 = my.xtime[my.xtime[xh ^ s1 ^ s3]] ^ h;
+ state[i + 0] ^= h1 ^ my.xtime[s0 ^ s1];
+ state[i + 1] ^= h2 ^ my.xtime[s1 ^ s2];
+ state[i + 2] ^= h1 ^ my.xtime[s2 ^ s3];
+ state[i + 3] ^= h2 ^ my.xtime[s3 ^ s0];
+ }
+ }
+
+ return my;
+
+}());
+
+
+var cryptico = (function () {
+
+ var my = {};
+
+ aes.Init();
+
+ var base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+
+ my.b256to64 = function (t) {
+ var a, c, n;
+ var r = '', l = 0, s = 0;
+ var tl = t.length;
+ for (n = 0; n < tl; n++) {
+ c = t.charCodeAt(n);
+ if (s == 0) {
+ r += base64Chars.charAt((c >> 2) & 63);
+ a = (c & 3) << 4;
+ }
+ else if (s == 1) {
+ r += base64Chars.charAt((a | (c >> 4) & 15));
+ a = (c & 15) << 2;
+ }
+ else if (s == 2) {
+ r += base64Chars.charAt(a | ((c >> 6) & 3));
+ l += 1;
+ r += base64Chars.charAt(c & 63);
+ }
+ l += 1;
+ s += 1;
+ if (s == 3) s = 0;
+ }
+ if (s > 0) {
+ r += base64Chars.charAt(a);
+ l += 1;
+ r += '=';
+ l += 1;
+ }
+ if (s == 1) {
+ r += '=';
+ }
+ return r;
+ }
+
+ my.b64to256 = function (t) {
+ var c, n;
+ var r = '', s = 0, a = 0;
+ var tl = t.length;
+ for (n = 0; n < tl; n++) {
+ c = base64Chars.indexOf(t.charAt(n));
+ if (c >= 0) {
+ if (s) r += String.fromCharCode(a | (c >> (6 - s)) & 255);
+ s = (s + 2) & 7;
+ a = (c << s) & 255;
+ }
+ }
+ return r;
+ }
+
+ my.b16to64 = function (h) {
+ var i;
+ var c;
+ var ret = "";
+ if (h.length % 2 == 1) {
+ h = "0" + h;
+ }
+ for (i = 0; i + 3 <= h.length; i += 3) {
+ c = parseInt(h.substring(i, i + 3), 16);
+ ret += base64Chars.charAt(c >> 6) + base64Chars.charAt(c & 63);
+ }
+ if (i + 1 == h.length) {
+ c = parseInt(h.substring(i, i + 1), 16);
+ ret += base64Chars.charAt(c << 2);
+ }
+ else if (i + 2 == h.length) {
+ c = parseInt(h.substring(i, i + 2), 16);
+ ret += base64Chars.charAt(c >> 2) + base64Chars.charAt((c & 3) << 4);
+ }
+ while ((ret.length & 3) > 0) ret += "=";
+ return ret;
+ }
+
+ my.b64to16 = function (s) {
+ var ret = "";
+ var i;
+ var k = 0;
+ var slop;
+ for (i = 0; i < s.length; ++i) {
+ if (s.charAt(i) == "=") break;
+ v = base64Chars.indexOf(s.charAt(i));
+ if (v < 0) continue;
+ if (k == 0) {
+ ret += int2char(v >> 2);
+ slop = v & 3;
+ k = 1;
+ }
+ else if (k == 1) {
+ ret += int2char((slop << 2) | (v >> 4));
+ slop = v & 0xf;
+ k = 2;
+ }
+ else if (k == 2) {
+ ret += int2char(slop);
+ ret += int2char(v >> 2);
+ slop = v & 3;
+ k = 3;
+ }
+ else {
+ ret += int2char((slop << 2) | (v >> 4));
+ ret += int2char(v & 0xf);
+ k = 0;
+ }
+ }
+ if (k == 1) ret += int2char(slop << 2);
+ return ret;
+ }
+
+ // Converts a string to a byte array.
+ my.string2bytes = function (string) {
+ var bytes = new Array();
+ for (var i = 0; i < string.length; i++) {
+ bytes.push(string.charCodeAt(i));
+ }
+ return bytes;
+ }
+
+ // Converts a byte array to a string.
+ my.bytes2string = function (bytes) {
+ var string = "";
+ for (var i = 0; i < bytes.length; i++) {
+ string += String.fromCharCode(bytes[i]);
+ }
+ return string;
+ }
+
+ // Returns a XOR b, where a and b are 16-byte byte arrays.
+ my.blockXOR = function (a, b) {
+ var xor = new Array(16);
+ for (var i = 0; i < 16; i++) {
+ xor[i] = a[i] ^ b[i];
+ }
+ return xor;
+ }
+
+ // Returns a 16-byte initialization vector.
+ my.blockIV = function () {
+ var r = new SecureRandom();
+ var IV = new Array(16);
+ r.nextBytes(IV);
+ return IV;
+ }
+
+ // Returns a copy of bytes with zeros appended to the end
+ // so that the (length of bytes) % 16 == 0.
+ my.pad16 = function (bytes) {
+ var newBytes = bytes.slice(0);
+ var padding = (16 - (bytes.length % 16)) % 16;
+ for (i = bytes.length; i < bytes.length + padding; i++) {
+ newBytes.push(0);
+ }
+ return newBytes;
+ }
+
+ // Removes trailing zeros from a byte array.
+ my.depad = function (bytes) {
+ var newBytes = bytes.slice(0);
+ while (newBytes[newBytes.length - 1] == 0) {
+ newBytes = newBytes.slice(0, newBytes.length - 1);
+ }
+ return newBytes;
+ }
+
+ // AES CBC Encryption.
+ my.encryptAESCBC = function (plaintext, key) {
+ var exkey = key.slice(0);
+ aes.ExpandKey(exkey);
+ var blocks = my.string2bytes(plaintext);
+ blocks = my.pad16(blocks);
+ var encryptedBlocks = my.blockIV();
+ for (var i = 0; i < blocks.length / 16; i++) {
+ var tempBlock = blocks.slice(i * 16, i * 16 + 16);
+ var prevBlock = encryptedBlocks.slice((i) * 16, (i) * 16 + 16);
+ tempBlock = my.blockXOR(prevBlock, tempBlock);
+ aes.Encrypt(tempBlock, exkey);
+ encryptedBlocks = encryptedBlocks.concat(tempBlock);
+ }
+ var ciphertext = my.bytes2string(encryptedBlocks);
+ return my.b256to64(ciphertext)
+ }
+
+ // AES CBC Decryption.
+ my.decryptAESCBC = function (encryptedText, key) {
+ var exkey = key.slice(0);
+ aes.ExpandKey(exkey);
+ var encryptedText = my.b64to256(encryptedText);
+ var encryptedBlocks = my.string2bytes(encryptedText);
+ var decryptedBlocks = new Array();
+ for (var i = 1; i < encryptedBlocks.length / 16; i++) {
+ var tempBlock = encryptedBlocks.slice(i * 16, i * 16 + 16);
+ var prevBlock = encryptedBlocks.slice((i - 1) * 16, (i - 1) * 16 + 16);
+ aes.Decrypt(tempBlock, exkey);
+ tempBlock = my.blockXOR(prevBlock, tempBlock);
+ decryptedBlocks = decryptedBlocks.concat(tempBlock);
+ }
+ decryptedBlocks = my.depad(decryptedBlocks);
+ return my.bytes2string(decryptedBlocks);
+ }
+
+ // Wraps a string to 60 characters.
+ my.wrap60 = function (string) {
+ var outstr = "";
+ for (var i = 0; i < string.length; i++) {
+ if (i % 60 == 0 && i != 0) outstr += "\n";
+ outstr += string[i];
+ }
+ return outstr;
+ }
+
+ // Generate a random key for the AES-encrypted message.
+ my.generateAESKey = function () {
+ var key = new Array(32);
+ var r = new SecureRandom();
+ r.nextBytes(key);
+ return key;
+ }
+
+ // Generates an RSA key from a passphrase.
+ my.generateRSAKey = function (passphrase, bitlength) {
+ Math.seedrandom(sha256.hex(passphrase));
+ var rsa = new RSAKey();
+ rsa.generate(bitlength, "03");
+ return rsa;
+ }
+
+ // Returns the ascii-armored version of the public key.
+ my.publicKeyString = function (rsakey) {
+ pubkey = my.b16to64(rsakey.n.toString(16));
+ return pubkey;
+ }
+
+ // Returns an MD5 sum of a publicKeyString for easier identification.
+ my.publicKeyID = function (publicKeyString) {
+ return MD5(publicKeyString);
+ }
+
+ my.publicKeyFromString = function (string) {
+ var N = my.b64to16(string.split("|")[0]);
+ var E = "03";
+ var rsa = new RSAKey();
+ rsa.setPublic(N, E);
+ return rsa
+ }
+
+ my.encrypt = function (plaintext, publickeystring, signingkey) {
+ var cipherblock = "";
+ var aeskey = my.generateAESKey();
+ try {
+ var publickey = my.publicKeyFromString(publickeystring);
+ cipherblock += my.b16to64(publickey.encrypt(my.bytes2string(aeskey))) + "?";
+ }
+ catch (err) {
+ return {status: "Invalid public key"};
+ }
+ if (signingkey) {
+ signString = cryptico.b16to64(signingkey.signString(plaintext, "sha256"));
+ plaintext += "::52cee64bb3a38f6403386519a39ac91c::";
+ plaintext += cryptico.publicKeyString(signingkey);
+ plaintext += "::52cee64bb3a38f6403386519a39ac91c::";
+ plaintext += signString;
+ }
+ cipherblock += my.encryptAESCBC(plaintext, aeskey);
+ return {status: "success", cipher: cipherblock};
+ }
+
+ my.decrypt = function (ciphertext, key) {
+ var cipherblock = ciphertext.split("?");
+ var aeskey = key.decrypt(my.b64to16(cipherblock[0]));
+ if (aeskey == null) {
+ return {status: "failure"};
+ }
+ aeskey = my.string2bytes(aeskey);
+ var plaintext = my.decryptAESCBC(cipherblock[1], aeskey).split("::52cee64bb3a38f6403386519a39ac91c::");
+ if (plaintext.length == 3) {
+ var publickey = my.publicKeyFromString(plaintext[1]);
+ var signature = my.b64to16(plaintext[2]);
+ if (publickey.verifyString(plaintext[0], signature)) {
+ return {
+ status: "success",
+ plaintext: plaintext[0],
+ signature: "verified",
+ publicKeyString: my.publicKeyString(publickey)
+ };
+ }
+ else {
+ return {
+ status: "success",
+ plaintext: plaintext[0],
+ signature: "forged",
+ publicKeyString: my.publicKeyString(publickey)
+ };
+ }
+ }
+ else {
+ return {status: "success", plaintext: plaintext[0], signature: "unsigned"};
+ }
+ }
+
+ return my;
+
+}());
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/js/fontawesome.js b/main/app/sprinkles/core/assets/SiteAssets/js/fontawesome.js
new file mode 100644
index 0000000..497accc
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/js/fontawesome.js
@@ -0,0 +1,5 @@
+/*!
+ * Font Awesome Free 5.0.10 by @fontawesome - https://fontawesome.com
+ * License - https://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
+ */
+!function(){"use strict";var c={};try{"undefined"!=typeof window&&(c=window)}catch(c){}var l=(c.navigator||{}).userAgent,h=void 0===l?"":l,v=c,z=(~h.indexOf("MSIE")||h.indexOf("Trident/"),"___FONT_AWESOME___"),e=function(){try{return!0}catch(c){return!1}}(),a=[1,2,3,4,5,6,7,8,9,10],m=a.concat([11,12,13,14,15,16,17,18,19,20]);["xs","sm","lg","fw","ul","li","border","pull-left","pull-right","spin","pulse","rotate-90","rotate-180","rotate-270","flip-horizontal","flip-vertical","stack","stack-1x","stack-2x","inverse","layers","layers-text","layers-counter"].concat(a.map(function(c){return c+"x"})).concat(m.map(function(c){return"w-"+c}));var s=v||{};s[z]||(s[z]={}),s[z].styles||(s[z].styles={}),s[z].hooks||(s[z].hooks={}),s[z].shims||(s[z].shims=[]);var t=s[z],f=Object.assign||function(c){for(var l=1;l>>0;h--;)l[h]=c[h];return l}function X(c){return c.classList?D(c.classList):(c.getAttribute("class")||"").split(" ").filter(function(c){return c})}function Y(c,l){var h,v=l.split("-"),z=v[0],e=v.slice(1).join("-");return z!==c||""===e||(h=e,~d.indexOf(h))?null:e}function U(c){return(""+c).replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">")}function K(h){return Object.keys(h||{}).reduce(function(c,l){return c+(l+": ")+h[l]+";"},"")}function G(c){return c.size!==I.size||c.x!==I.x||c.y!==I.y||c.rotate!==I.rotate||c.flipX||c.flipY}function J(c){var l=c.transform,h=c.containerWidth,v=c.iconWidth;return{outer:{transform:"translate("+h/2+" 256)"},inner:{transform:"translate("+32*l.x+", "+32*l.y+") "+" "+("scale("+l.size/16*(l.flipX?-1:1)+", "+l.size/16*(l.flipY?-1:1)+") ")+" "+("rotate("+l.rotate+" 0 0)")},path:{transform:"translate("+v/2*-1+" -256)"}}}var Q={x:0,y:0,width:"100%",height:"100%"},Z=function(c){var l=c.children,h=c.attributes,v=c.main,z=c.mask,e=c.transform,a=v.width,m=v.icon,s=z.width,t=z.icon,f=J({transform:e,containerWidth:s,iconWidth:a}),r={tag:"rect",attributes:k({},Q,{fill:"white"})},M={tag:"g",attributes:k({},f.inner),children:[{tag:"path",attributes:k({},m.attributes,f.path,{fill:"black"})}]},i={tag:"g",attributes:k({},f.outer),children:[M]},n="mask-"+B(),H="clip-"+B(),V={tag:"defs",children:[{tag:"clipPath",attributes:{id:H},children:[t]},{tag:"mask",attributes:k({},Q,{id:n,maskUnits:"userSpaceOnUse",maskContentUnits:"userSpaceOnUse"}),children:[r,i]}]};return l.push(V,{tag:"rect",attributes:k({fill:"currentColor","clip-path":"url(#"+H+")",mask:"url(#"+n+")"},Q)}),{children:l,attributes:h}},$=function(c){var l=c.children,h=c.attributes,v=c.main,z=c.transform,e=K(c.styles);if(0"+a.map(uc).join("")+""+l+">"}var dc=function(){};function pc(c){return"string"==typeof(c.getAttribute?c.getAttribute(g):null)}var bc={replace:function(c){var l=c[0],h=c[1].map(function(c){return uc(c)}).join("\n");if(l.parentNode&&l.outerHTML)l.outerHTML=h+(O.keepOriginalSource&&"svg"!==l.tagName.toLowerCase()?"\x3c!-- "+l.outerHTML+" --\x3e":"");else if(l.parentNode){var v=document.createElement("span");l.parentNode.replaceChild(v,l),v.outerHTML=h}},nest:function(c){var l=c[0],h=c[1];if(~X(l).indexOf(O.replacementClass))return bc.replace(c);var v=new RegExp(O.familyPrefix+"-.*");delete h[0].attributes.style;var z=h[0].attributes.class.split(" ").reduce(function(c,l){return l===O.replacementClass||l.match(v)?c.toSvg.push(l):c.toNode.push(l),c},{toNode:[],toSvg:[]});h[0].attributes.class=z.toSvg.join(" ");var e=h.map(function(c){return uc(c)}).join("\n");l.setAttribute("class",z.toNode.join(" ")),l.setAttribute(g,""),l.innerHTML=e}};function gc(h,c){var v="function"==typeof c?c:dc;0===h.length?v():(m.requestAnimationFrame||function(c){return c()})(function(){var c=!0===O.autoReplaceSvg?bc.replace:bc[O.autoReplaceSvg]||bc.replace,l=sc.begin("mutate");h.map(c),l(),v()})}var yc=!1;var wc=null;var kc=function(c){var l=c.getAttribute("style"),h=[];return l&&(h=l.split(";").reduce(function(c,l){var h=l.split(":"),v=h[0],z=h.slice(1);return v&&0li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:solid .08em #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.fa-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{-webkit-transform:scale(1,-1);transform:scale(1,-1)}.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1,-1);transform:scale(-1,-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-rotate-90{-webkit-filter:none;filter:none}.fa-stack{display:inline-block;height:2em;position:relative;width:2em}.fa-stack-1x,.fa-stack-2x{bottom:0;left:0;margin:auto;position:absolute;right:0;top:0}.svg-inline--fa.fa-stack-1x{height:1em;width:1em}.svg-inline--fa.fa-stack-2x{height:2em;width:2em}.fa-inverse{color:#fff}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}";if("fa"!==l||h!==c){var z=new RegExp("\\.fa\\-","g"),e=new RegExp("\\."+c,"g");v=v.replace(z,"."+l+"-").replace(e,"."+h)}return v};var Qc=function(){function c(){y(this,c),this.definitions={}}return w(c,[{key:"add",value:function(){for(var l=this,c=arguments.length,h=Array(c),v=0;va?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b=a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="
",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML=" ",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML=" ","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML=" ",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;
+ if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML=" a ",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="x ",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML=" ",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function ab(){return!0}function bb(){return!1}function cb(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h ]","i"),hb=/^\s+/,ib=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,jb=/<([\w:]+)/,kb=/\s*$/g,rb={option:[1,""," "],legend:[1,""," "],area:[1,""," "],param:[1,""," "],thead:[1,""],tr:[2,""],col:[2,""],td:[3,""],_default:k.htmlSerialize?[0,"",""]:[1,"X","
"]},sb=db(y),tb=sb.appendChild(y.createElement("div"));rb.optgroup=rb.option,rb.tbody=rb.tfoot=rb.colgroup=rb.caption=rb.thead,rb.th=rb.td;function ub(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ub(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function vb(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wb(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xb(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function yb(a){var b=pb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function zb(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Ab(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Bb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xb(b).text=a.text,yb(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!gb.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(tb.innerHTML=a.outerHTML,tb.removeChild(f=tb.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ub(f),h=ub(a),g=0;null!=(e=h[g]);++g)d[g]&&Bb(e,d[g]);if(b)if(c)for(h=h||ub(a),d=d||ub(f),g=0;null!=(e=h[g]);g++)Ab(e,d[g]);else Ab(a,f);return d=ub(f,"script"),d.length>0&&zb(d,!i&&ub(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=db(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(lb.test(f)){h=h||o.appendChild(b.createElement("div")),i=(jb.exec(f)||["",""])[1].toLowerCase(),l=rb[i]||rb._default,h.innerHTML=l[1]+f.replace(ib,"<$1>$2>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&hb.test(f)&&p.push(b.createTextNode(hb.exec(f)[0])),!k.tbody){f="table"!==i||kb.test(f)?""!==l[1]||kb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ub(p,"input"),vb),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ub(o.appendChild(f),"script"),g&&zb(h),c)){e=0;while(f=h[e++])ob.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ub(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&zb(ub(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ub(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fb,""):void 0;if(!("string"!=typeof a||mb.test(a)||!k.htmlSerialize&&gb.test(a)||!k.leadingWhitespace&&hb.test(a)||rb[(jb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ib,"<$1>$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ub(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ub(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&nb.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ub(i,"script"),xb),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ub(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,yb),j=0;f>j;j++)d=g[j],ob.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qb,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Cb,Db={};function Eb(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fb(a){var b=y,c=Db[a];return c||(c=Eb(a,b),"none"!==c&&c||(Cb=(Cb||m("")).appendTo(b.documentElement),b=(Cb[0].contentWindow||Cb[0].contentDocument).document,b.write(),b.close(),c=Eb(a,b),Cb.detach()),Db[a]=c),c}!function(){var a;k.shrinkWrapBlocks=function(){if(null!=a)return a;a=!1;var b,c,d;return c=y.getElementsByTagName("body")[0],c&&c.style?(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1",b.appendChild(y.createElement("div")).style.width="5px",a=3!==b.offsetWidth),c.removeChild(d),a):void 0}}();var Gb=/^margin/,Hb=new RegExp("^("+S+")(?!px)[a-z%]+$","i"),Ib,Jb,Kb=/^(top|right|bottom|left)$/;a.getComputedStyle?(Ib=function(a){return a.ownerDocument.defaultView.getComputedStyle(a,null)},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c.getPropertyValue(b)||c[b]:void 0,c&&(""!==g||m.contains(a.ownerDocument,a)||(g=m.style(a,b)),Hb.test(g)&&Gb.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0===g?g:g+""}):y.documentElement.currentStyle&&(Ib=function(a){return a.currentStyle},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c[b]:void 0,null==g&&h&&h[b]&&(g=h[b]),Hb.test(g)&&!Kb.test(b)&&(d=h.left,e=a.runtimeStyle,f=e&&e.left,f&&(e.left=a.currentStyle.left),h.left="fontSize"===b?"1em":g,g=h.pixelLeft+"px",h.left=d,f&&(e.left=f)),void 0===g?g:g+""||"auto"});function Lb(a,b){return{get:function(){var c=a();if(null!=c)return c?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d,e,f,g,h;if(b=y.createElement("div"),b.innerHTML=" a ",d=b.getElementsByTagName("a")[0],c=d&&d.style){c.cssText="float:left;opacity:.5",k.opacity="0.5"===c.opacity,k.cssFloat=!!c.cssFloat,b.style.backgroundClip="content-box",b.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===b.style.backgroundClip,k.boxSizing=""===c.boxSizing||""===c.MozBoxSizing||""===c.WebkitBoxSizing,m.extend(k,{reliableHiddenOffsets:function(){return null==g&&i(),g},boxSizingReliable:function(){return null==f&&i(),f},pixelPosition:function(){return null==e&&i(),e},reliableMarginRight:function(){return null==h&&i(),h}});function i(){var b,c,d,i;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),b.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",e=f=!1,h=!0,a.getComputedStyle&&(e="1%"!==(a.getComputedStyle(b,null)||{}).top,f="4px"===(a.getComputedStyle(b,null)||{width:"4px"}).width,i=b.appendChild(y.createElement("div")),i.style.cssText=b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",i.style.marginRight=i.style.width="0",b.style.width="1px",h=!parseFloat((a.getComputedStyle(i,null)||{}).marginRight)),b.innerHTML="",i=b.getElementsByTagName("td"),i[0].style.cssText="margin:0;border:0;padding:0;display:none",g=0===i[0].offsetHeight,g&&(i[0].style.display="",i[1].style.display="none",g=0===i[0].offsetHeight),c.removeChild(d))}}}(),m.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var Mb=/alpha\([^)]*\)/i,Nb=/opacity\s*=\s*([^)]*)/,Ob=/^(none|table(?!-c[ea]).+)/,Pb=new RegExp("^("+S+")(.*)$","i"),Qb=new RegExp("^([+-])=("+S+")","i"),Rb={position:"absolute",visibility:"hidden",display:"block"},Sb={letterSpacing:"0",fontWeight:"400"},Tb=["Webkit","O","Moz","ms"];function Ub(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=Tb.length;while(e--)if(b=Tb[e]+c,b in a)return b;return d}function Vb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=m._data(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&U(d)&&(f[g]=m._data(d,"olddisplay",Fb(d.nodeName)))):(e=U(d),(c&&"none"!==c||!e)&&m._data(d,"olddisplay",e?c:m.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}function Wb(a,b,c){var d=Pb.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Xb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=m.css(a,c+T[f],!0,e)),d?("content"===c&&(g-=m.css(a,"padding"+T[f],!0,e)),"margin"!==c&&(g-=m.css(a,"border"+T[f]+"Width",!0,e))):(g+=m.css(a,"padding"+T[f],!0,e),"padding"!==c&&(g+=m.css(a,"border"+T[f]+"Width",!0,e)));return g}function Yb(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=Ib(a),g=k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=Jb(a,b,f),(0>e||null==e)&&(e=a.style[b]),Hb.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Xb(a,b,c||(g?"border":"content"),d,f)+"px"}m.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Jb(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":k.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=m.camelCase(b),i=a.style;if(b=m.cssProps[h]||(m.cssProps[h]=Ub(i,h)),g=m.cssHooks[b]||m.cssHooks[h],void 0===c)return g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b];if(f=typeof c,"string"===f&&(e=Qb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(m.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||m.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),!(g&&"set"in g&&void 0===(c=g.set(a,c,d)))))try{i[b]=c}catch(j){}}},css:function(a,b,c,d){var e,f,g,h=m.camelCase(b);return b=m.cssProps[h]||(m.cssProps[h]=Ub(a.style,h)),g=m.cssHooks[b]||m.cssHooks[h],g&&"get"in g&&(f=g.get(a,!0,c)),void 0===f&&(f=Jb(a,b,d)),"normal"===f&&b in Sb&&(f=Sb[b]),""===c||c?(e=parseFloat(f),c===!0||m.isNumeric(e)?e||0:f):f}}),m.each(["height","width"],function(a,b){m.cssHooks[b]={get:function(a,c,d){return c?Ob.test(m.css(a,"display"))&&0===a.offsetWidth?m.swap(a,Rb,function(){return Yb(a,b,d)}):Yb(a,b,d):void 0},set:function(a,c,d){var e=d&&Ib(a);return Wb(a,c,d?Xb(a,b,d,k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,e),e):0)}}}),k.opacity||(m.cssHooks.opacity={get:function(a,b){return Nb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=m.isNumeric(b)?"alpha(opacity="+100*b+")":"",f=d&&d.filter||c.filter||"";c.zoom=1,(b>=1||""===b)&&""===m.trim(f.replace(Mb,""))&&c.removeAttribute&&(c.removeAttribute("filter"),""===b||d&&!d.filter)||(c.filter=Mb.test(f)?f.replace(Mb,e):f+" "+e)}}),m.cssHooks.marginRight=Lb(k.reliableMarginRight,function(a,b){return b?m.swap(a,{display:"inline-block"},Jb,[a,"marginRight"]):void 0}),m.each({margin:"",padding:"",border:"Width"},function(a,b){m.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+T[d]+b]=f[d]||f[d-2]||f[0];return e}},Gb.test(a)||(m.cssHooks[a+b].set=Wb)}),m.fn.extend({css:function(a,b){return V(this,function(a,b,c){var d,e,f={},g=0;if(m.isArray(b)){for(d=Ib(a),e=b.length;e>g;g++)f[b[g]]=m.css(a,b[g],!1,d);return f}return void 0!==c?m.style(a,b,c):m.css(a,b)},a,b,arguments.length>1)},show:function(){return Vb(this,!0)},hide:function(){return Vb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){U(this)?m(this).show():m(this).hide()})}});function Zb(a,b,c,d,e){return new Zb.prototype.init(a,b,c,d,e)}m.Tween=Zb,Zb.prototype={constructor:Zb,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(m.cssNumber[c]?"":"px")
+ },cur:function(){var a=Zb.propHooks[this.prop];return a&&a.get?a.get(this):Zb.propHooks._default.get(this)},run:function(a){var b,c=Zb.propHooks[this.prop];return this.pos=b=this.options.duration?m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Zb.propHooks._default.set(this),this}},Zb.prototype.init.prototype=Zb.prototype,Zb.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Zb.propHooks.scrollTop=Zb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Zb.prototype.init,m.fx.step={};var $b,_b,ac=/^(?:toggle|show|hide)$/,bc=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cc=/queueHooks$/,dc=[ic],ec={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bc.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bc.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fc(){return setTimeout(function(){$b=void 0}),$b=m.now()}function gc(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hc(a,b,c){for(var d,e=(ec[b]||[]).concat(ec["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ic(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fb(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fb(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ac.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fb(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hc(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jc(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kc(a,b,c){var d,e,f=0,g=dc.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$b||fc(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$b||fc(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jc(k,j.opts.specialEasing);g>f;f++)if(d=dc[f].call(j,a,k,j.opts))return d;return m.map(k,hc,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}m.Animation=m.extend(kc,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],ec[c]=ec[c]||[],ec[c].unshift(b)},prefilter:function(a,b){b?dc.unshift(a):dc.push(a)}}),m.speed=function(a,b,c){var d=a&&"object"==typeof a?m.extend({},a):{complete:c||!c&&b||m.isFunction(a)&&a,duration:a,easing:c&&b||b&&!m.isFunction(b)&&b};return d.duration=m.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in m.fx.speeds?m.fx.speeds[d.duration]:m.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){m.isFunction(d.old)&&d.old.call(this),d.queue&&m.dequeue(this,d.queue)},d},m.fn.extend({fadeTo:function(a,b,c,d){return this.filter(U).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=m.isEmptyObject(a),f=m.speed(b,c,d),g=function(){var b=kc(this,m.extend({},a),f);(e||m._data(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=m.timers,g=m._data(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&cc.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&m.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=m._data(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=m.timers,g=d?d.length:0;for(c.finish=!0,m.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),m.each(["toggle","show","hide"],function(a,b){var c=m.fn[b];m.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(gc(b,!0),a,d,e)}}),m.each({slideDown:gc("show"),slideUp:gc("hide"),slideToggle:gc("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){m.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),m.timers=[],m.fx.tick=function(){var a,b=m.timers,c=0;for($b=m.now();ca ",d=b.getElementsByTagName("a")[0],c=y.createElement("select"),e=c.appendChild(y.createElement("option")),a=b.getElementsByTagName("input")[0],d.style.cssText="top:1px",k.getSetAttribute="t"!==b.className,k.style=/top/.test(d.getAttribute("style")),k.hrefNormalized="/a"===d.getAttribute("href"),k.checkOn=!!a.value,k.optSelected=e.selected,k.enctype=!!y.createElement("form").enctype,c.disabled=!0,k.optDisabled=!e.disabled,a=y.createElement("input"),a.setAttribute("value",""),k.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),k.radioValue="t"===a.value}();var lc=/\r/g;m.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=m.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,m(this).val()):a,null==e?e="":"number"==typeof e?e+="":m.isArray(e)&&(e=m.map(e,function(a){return null==a?"":a+""})),b=m.valHooks[this.type]||m.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=m.valHooks[e.type]||m.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(lc,""):null==c?"":c)}}}),m.extend({valHooks:{option:{get:function(a){var b=m.find.attr(a,"value");return null!=b?b:m.trim(m.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&m.nodeName(c.parentNode,"optgroup"))){if(b=m(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=m.makeArray(b),g=e.length;while(g--)if(d=e[g],m.inArray(m.valHooks.option.get(d),f)>=0)try{d.selected=c=!0}catch(h){d.scrollHeight}else d.selected=!1;return c||(a.selectedIndex=-1),e}}}}),m.each(["radio","checkbox"],function(){m.valHooks[this]={set:function(a,b){return m.isArray(b)?a.checked=m.inArray(m(a).val(),b)>=0:void 0}},k.checkOn||(m.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var mc,nc,oc=m.expr.attrHandle,pc=/^(?:checked|selected)$/i,qc=k.getSetAttribute,rc=k.input;m.fn.extend({attr:function(a,b){return V(this,m.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){m.removeAttr(this,a)})}}),m.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===K?m.prop(a,b,c):(1===f&&m.isXMLDoc(a)||(b=b.toLowerCase(),d=m.attrHooks[b]||(m.expr.match.bool.test(b)?nc:mc)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=m.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void m.removeAttr(a,b))},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=m.propFix[c]||c,m.expr.match.bool.test(c)?rc&&qc||!pc.test(c)?a[d]=!1:a[m.camelCase("default-"+c)]=a[d]=!1:m.attr(a,c,""),a.removeAttribute(qc?c:d)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&m.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),nc={set:function(a,b,c){return b===!1?m.removeAttr(a,c):rc&&qc||!pc.test(c)?a.setAttribute(!qc&&m.propFix[c]||c,c):a[m.camelCase("default-"+c)]=a[c]=!0,c}},m.each(m.expr.match.bool.source.match(/\w+/g),function(a,b){var c=oc[b]||m.find.attr;oc[b]=rc&&qc||!pc.test(b)?function(a,b,d){var e,f;return d||(f=oc[b],oc[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,oc[b]=f),e}:function(a,b,c){return c?void 0:a[m.camelCase("default-"+b)]?b.toLowerCase():null}}),rc&&qc||(m.attrHooks.value={set:function(a,b,c){return m.nodeName(a,"input")?void(a.defaultValue=b):mc&&mc.set(a,b,c)}}),qc||(mc={set:function(a,b,c){var d=a.getAttributeNode(c);return d||a.setAttributeNode(d=a.ownerDocument.createAttribute(c)),d.value=b+="","value"===c||b===a.getAttribute(c)?b:void 0}},oc.id=oc.name=oc.coords=function(a,b,c){var d;return c?void 0:(d=a.getAttributeNode(b))&&""!==d.value?d.value:null},m.valHooks.button={get:function(a,b){var c=a.getAttributeNode(b);return c&&c.specified?c.value:void 0},set:mc.set},m.attrHooks.contenteditable={set:function(a,b,c){mc.set(a,""===b?!1:b,c)}},m.each(["width","height"],function(a,b){m.attrHooks[b]={set:function(a,c){return""===c?(a.setAttribute(b,"auto"),c):void 0}}})),k.style||(m.attrHooks.style={get:function(a){return a.style.cssText||void 0},set:function(a,b){return a.style.cssText=b+""}});var sc=/^(?:input|select|textarea|button|object)$/i,tc=/^(?:a|area)$/i;m.fn.extend({prop:function(a,b){return V(this,m.prop,a,b,arguments.length>1)},removeProp:function(a){return a=m.propFix[a]||a,this.each(function(){try{this[a]=void 0,delete this[a]}catch(b){}})}}),m.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!m.isXMLDoc(a),f&&(b=m.propFix[b]||b,e=m.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=m.find.attr(a,"tabindex");return b?parseInt(b,10):sc.test(a.nodeName)||tc.test(a.nodeName)&&a.href?0:-1}}}}),k.hrefNormalized||m.each(["href","src"],function(a,b){m.propHooks[b]={get:function(a){return a.getAttribute(b,4)}}}),k.optSelected||(m.propHooks.selected={get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}}),m.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){m.propFix[this.toLowerCase()]=this}),k.enctype||(m.propFix.enctype="encoding");var uc=/[\t\r\n\f]/g;m.fn.extend({addClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j="string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).addClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=m.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j=0===arguments.length||"string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).removeClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?m.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(m.isFunction(a)?function(c){m(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=m(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===K||"boolean"===c)&&(this.className&&m._data(this,"__className__",this.className),this.className=this.className||a===!1?"":m._data(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(uc," ").indexOf(b)>=0)return!0;return!1}}),m.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){m.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),m.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var vc=m.now(),wc=/\?/,xc=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;m.parseJSON=function(b){if(a.JSON&&a.JSON.parse)return a.JSON.parse(b+"");var c,d=null,e=m.trim(b+"");return e&&!m.trim(e.replace(xc,function(a,b,e,f){return c&&b&&(d=0),0===d?a:(c=e||b,d+=!f-!e,"")}))?Function("return "+e)():m.error("Invalid JSON: "+b)},m.parseXML=function(b){var c,d;if(!b||"string"!=typeof b)return null;try{a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b))}catch(e){c=void 0}return c&&c.documentElement&&!c.getElementsByTagName("parsererror").length||m.error("Invalid XML: "+b),c};var yc,zc,Ac=/#.*$/,Bc=/([?&])_=[^&]*/,Cc=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Dc=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Ec=/^(?:GET|HEAD)$/,Fc=/^\/\//,Gc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Hc={},Ic={},Jc="*/".concat("*");try{zc=location.href}catch(Kc){zc=y.createElement("a"),zc.href="",zc=zc.href}yc=Gc.exec(zc.toLowerCase())||[];function Lc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(m.isFunction(c))while(d=f[e++])"+"===d.charAt(0)?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Mc(a,b,c,d){var e={},f=a===Ic;function g(h){var i;return e[h]=!0,m.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Nc(a,b){var c,d,e=m.ajaxSettings.flatOptions||{};for(d in b)void 0!==b[d]&&((e[d]?a:c||(c={}))[d]=b[d]);return c&&m.extend(!0,a,c),a}function Oc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===e&&(e=a.mimeType||b.getResponseHeader("Content-Type"));if(e)for(g in h)if(h[g]&&h[g].test(e)){i.unshift(g);break}if(i[0]in c)f=i[0];else{for(g in c){if(!i[0]||a.converters[g+" "+i[0]]){f=g;break}d||(d=g)}f=f||d}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function Pc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}m.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:zc,type:"GET",isLocal:Dc.test(yc[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Jc,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":m.parseJSON,"text xml":m.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Nc(Nc(a,m.ajaxSettings),b):Nc(m.ajaxSettings,a)},ajaxPrefilter:Lc(Hc),ajaxTransport:Lc(Ic),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=m.ajaxSetup({},b),l=k.context||k,n=k.context&&(l.nodeType||l.jquery)?m(l):m.event,o=m.Deferred(),p=m.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!j){j={};while(b=Cc.exec(f))j[b[1].toLowerCase()]=b[2]}b=j[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?f:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return i&&i.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||zc)+"").replace(Ac,"").replace(Fc,yc[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=m.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(c=Gc.exec(k.url.toLowerCase()),k.crossDomain=!(!c||c[1]===yc[1]&&c[2]===yc[2]&&(c[3]||("http:"===c[1]?"80":"443"))===(yc[3]||("http:"===yc[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=m.param(k.data,k.traditional)),Mc(Hc,k,b,v),2===t)return v;h=k.global,h&&0===m.active++&&m.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!Ec.test(k.type),e=k.url,k.hasContent||(k.data&&(e=k.url+=(wc.test(e)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=Bc.test(e)?e.replace(Bc,"$1_="+vc++):e+(wc.test(e)?"&":"?")+"_="+vc++)),k.ifModified&&(m.lastModified[e]&&v.setRequestHeader("If-Modified-Since",m.lastModified[e]),m.etag[e]&&v.setRequestHeader("If-None-Match",m.etag[e])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+Jc+"; q=0.01":""):k.accepts["*"]);for(d in k.headers)v.setRequestHeader(d,k.headers[d]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(d in{success:1,error:1,complete:1})v[d](k[d]);if(i=Mc(Ic,k,b,v)){v.readyState=1,h&&n.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,i.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,c,d){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),i=void 0,f=d||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,c&&(u=Oc(k,v,c)),u=Pc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(m.lastModified[e]=w),w=v.getResponseHeader("etag"),w&&(m.etag[e]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,h&&n.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),h&&(n.trigger("ajaxComplete",[v,k]),--m.active||m.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return m.get(a,b,c,"json")},getScript:function(a,b){return m.get(a,void 0,b,"script")}}),m.each(["get","post"],function(a,b){m[b]=function(a,c,d,e){return m.isFunction(c)&&(e=e||d,d=c,c=void 0),m.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),m.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){m.fn[b]=function(a){return this.on(b,a)}}),m._evalUrl=function(a){return m.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},m.fn.extend({wrapAll:function(a){if(m.isFunction(a))return this.each(function(b){m(this).wrapAll(a.call(this,b))});if(this[0]){var b=m(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&1===a.firstChild.nodeType)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return this.each(m.isFunction(a)?function(b){m(this).wrapInner(a.call(this,b))}:function(){var b=m(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=m.isFunction(a);return this.each(function(c){m(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){m.nodeName(this,"body")||m(this).replaceWith(this.childNodes)}).end()}}),m.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0||!k.reliableHiddenOffsets()&&"none"===(a.style&&a.style.display||m.css(a,"display"))},m.expr.filters.visible=function(a){return!m.expr.filters.hidden(a)};var Qc=/%20/g,Rc=/\[\]$/,Sc=/\r?\n/g,Tc=/^(?:submit|button|image|reset|file)$/i,Uc=/^(?:input|select|textarea|keygen)/i;function Vc(a,b,c,d){var e;if(m.isArray(b))m.each(b,function(b,e){c||Rc.test(a)?d(a,e):Vc(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==m.type(b))d(a,b);else for(e in b)Vc(a+"["+e+"]",b[e],c,d)}m.param=function(a,b){var c,d=[],e=function(a,b){b=m.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=m.ajaxSettings&&m.ajaxSettings.traditional),m.isArray(a)||a.jquery&&!m.isPlainObject(a))m.each(a,function(){e(this.name,this.value)});else for(c in a)Vc(c,a[c],b,e);return d.join("&").replace(Qc,"+")},m.fn.extend({serialize:function(){return m.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=m.prop(this,"elements");return a?m.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!m(this).is(":disabled")&&Uc.test(this.nodeName)&&!Tc.test(a)&&(this.checked||!W.test(a))}).map(function(a,b){var c=m(this).val();return null==c?null:m.isArray(c)?m.map(c,function(a){return{name:b.name,value:a.replace(Sc,"\r\n")}}):{name:b.name,value:c.replace(Sc,"\r\n")}}).get()}}),m.ajaxSettings.xhr=void 0!==a.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&Zc()||$c()}:Zc;var Wc=0,Xc={},Yc=m.ajaxSettings.xhr();a.ActiveXObject&&m(a).on("unload",function(){for(var a in Xc)Xc[a](void 0,!0)}),k.cors=!!Yc&&"withCredentials"in Yc,Yc=k.ajax=!!Yc,Yc&&m.ajaxTransport(function(a){if(!a.crossDomain||k.cors){var b;return{send:function(c,d){var e,f=a.xhr(),g=++Wc;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)void 0!==c[e]&&f.setRequestHeader(e,c[e]+"");f.send(a.hasContent&&a.data||null),b=function(c,e){var h,i,j;if(b&&(e||4===f.readyState))if(delete Xc[g],b=void 0,f.onreadystatechange=m.noop,e)4!==f.readyState&&f.abort();else{j={},h=f.status,"string"==typeof f.responseText&&(j.text=f.responseText);try{i=f.statusText}catch(k){i=""}h||!a.isLocal||a.crossDomain?1223===h&&(h=204):h=j.text?200:404}j&&d(h,i,j,f.getAllResponseHeaders())},a.async?4===f.readyState?setTimeout(b):f.onreadystatechange=Xc[g]=b:b()},abort:function(){b&&b(void 0,!0)}}}});function Zc(){try{return new a.XMLHttpRequest}catch(b){}}function $c(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}m.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return m.globalEval(a),a}}}),m.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),m.ajaxTransport("script",function(a){if(a.crossDomain){var b,c=y.head||m("head")[0]||y.documentElement;return{send:function(d,e){b=y.createElement("script"),b.async=!0,a.scriptCharset&&(b.charset=a.scriptCharset),b.src=a.url,b.onload=b.onreadystatechange=function(a,c){(c||!b.readyState||/loaded|complete/.test(b.readyState))&&(b.onload=b.onreadystatechange=null,b.parentNode&&b.parentNode.removeChild(b),b=null,c||e(200,"success"))},c.insertBefore(b,c.firstChild)},abort:function(){b&&b.onload(void 0,!0)}}}});var _c=[],ad=/(=)\?(?=&|$)|\?\?/;m.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=_c.pop()||m.expando+"_"+vc++;return this[a]=!0,a}}),m.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(ad.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&ad.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=m.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(ad,"$1"+e):b.jsonp!==!1&&(b.url+=(wc.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||m.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,_c.push(e)),g&&m.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),m.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||y;var d=u.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=m.buildFragment([a],b,e),e&&e.length&&m(e).remove(),m.merge([],d.childNodes))};var bd=m.fn.load;m.fn.load=function(a,b,c){if("string"!=typeof a&&bd)return bd.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=m.trim(a.slice(h,a.length)),a=a.slice(0,h)),m.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(f="POST"),g.length>0&&m.ajax({url:a,type:f,dataType:"html",data:b}).done(function(a){e=arguments,g.html(d?m("").append(m.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,e||[a.responseText,b,a])}),this},m.expr.filters.animated=function(a){return m.grep(m.timers,function(b){return a===b.elem}).length};var cd=a.document.documentElement;function dd(a){return m.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}m.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=m.css(a,"position"),l=m(a),n={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=m.css(a,"top"),i=m.css(a,"left"),j=("absolute"===k||"fixed"===k)&&m.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),m.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(n.top=b.top-h.top+g),null!=b.left&&(n.left=b.left-h.left+e),"using"in b?b.using.call(a,n):l.css(n)}},m.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){m.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,m.contains(b,e)?(typeof e.getBoundingClientRect!==K&&(d=e.getBoundingClientRect()),c=dd(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===m.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),m.nodeName(a[0],"html")||(c=a.offset()),c.top+=m.css(a[0],"borderTopWidth",!0),c.left+=m.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-m.css(d,"marginTop",!0),left:b.left-c.left-m.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||cd;while(a&&!m.nodeName(a,"html")&&"static"===m.css(a,"position"))a=a.offsetParent;return a||cd})}}),m.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);m.fn[a]=function(d){return V(this,function(a,d,e){var f=dd(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?m(f).scrollLeft():e,c?e:m(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),m.each(["top","left"],function(a,b){m.cssHooks[b]=Lb(k.pixelPosition,function(a,c){return c?(c=Jb(a,b),Hb.test(c)?m(a).position()[b]+"px":c):void 0})}),m.each({Height:"height",Width:"width"},function(a,b){m.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){m.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return V(this,function(b,c,d){var e;return m.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?m.css(b,c,g):m.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),m.fn.size=function(){return this.length},m.fn.andSelf=m.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return m});var ed=a.jQuery,fd=a.$;return m.noConflict=function(b){return a.$===m&&(a.$=fd),b&&a.jQuery===m&&(a.jQuery=ed),m},typeof b===K&&(a.jQuery=a.$=m),m});
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/js/language.js b/main/app/sprinkles/core/assets/SiteAssets/js/language.js
new file mode 100644
index 0000000..abe6a65
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/js/language.js
@@ -0,0 +1,36 @@
+function Translate() {
+ //initialization
+ this.init = function (lng) {
+ this.attribute = 'data-lang';
+ this.lng = lng;
+ };
+ //translate
+ this.process = function () {
+ var _self = this;
+ var xrhFile = new XMLHttpRequest();
+ //load content data
+ xrhFile.open("GET", "assets/languages/json/Translations.json", true);
+ xrhFile.onreadystatechange = function () {
+ if (xrhFile.readyState === 4) {
+ if (xrhFile.status === 200 || xrhFile.status === 0) {
+ var LngObject = JSON.parse(xrhFile.responseText);
+ var allDom = document.getElementsByTagName("*");
+ for (var i = 0; i < allDom.length; i++) {
+ var elem = allDom[i];
+ var key = elem.getAttribute(_self.attribute);
+
+ if (key != null) {
+ //console.log("Language initialized with language pack: " + _self.lng);
+ elem.innerHTML = LngObject[_self.lng][key];
+ }
+ }
+ }
+ }
+ };
+ xrhFile.send();
+ }
+}
+
+$(document).ready(function () {
+ initiateLanguage();
+});
diff --git a/main/app/sprinkles/core/assets/SiteAssets/js/linkify.js b/main/app/sprinkles/core/assets/SiteAssets/js/linkify.js
new file mode 100644
index 0000000..2778a90
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/js/linkify.js
@@ -0,0 +1,369 @@
+!function () {
+ "use strict";
+ var n = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (n) {
+ return typeof n
+ } : function (n) {
+ return n && "function" == typeof Symbol && n.constructor === Symbol && n !== Symbol.prototype ? "symbol" : typeof n
+ };
+ !function (e) {
+ function a(n, e) {
+ var a = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : {}, t = Object.create(n.prototype);
+ for (var o in a) t[o] = a[o];
+ return t.constructor = e, e.prototype = t, e
+ }
+
+ function t(n) {
+ n = n || {}, this.defaultProtocol = n.hasOwnProperty("defaultProtocol") ? n.defaultProtocol : h.defaultProtocol, this.events = n.hasOwnProperty("events") ? n.events : h.events, this.format = n.hasOwnProperty("format") ? n.format : h.format, this.formatHref = n.hasOwnProperty("formatHref") ? n.formatHref : h.formatHref, this.nl2br = n.hasOwnProperty("nl2br") ? n.nl2br : h.nl2br, this.tagName = n.hasOwnProperty("tagName") ? n.tagName : h.tagName, this.target = n.hasOwnProperty("target") ? n.target : h.target, this.validate = n.hasOwnProperty("validate") ? n.validate : h.validate, this.ignoreTags = [], this.attributes = n.attributes || n.linkAttributes || h.attributes, this.className = n.hasOwnProperty("className") ? n.className : n.linkClass || h.className;
+ for (var e = n.hasOwnProperty("ignoreTags") ? n.ignoreTags : h.ignoreTags, a = 0; a < e.length; a++) this.ignoreTags.push(e[a].toUpperCase())
+ }
+
+ function o(n, e) {
+ for (var a = 0; a < n.length; a++) if (n[a] === e) return !0;
+ return !1
+ }
+
+ function r(n) {
+ return n
+ }
+
+ function i(n, e) {
+ return "url" === e ? "_blank" : null
+ }
+
+ function s() {
+ return function (n) {
+ this.j = [], this.T = n || null
+ }
+ }
+
+ function c(n, e, a, t) {
+ for (var o = 0, r = n.length, i = e, s = [], c = void 0; o < r && (c = i.next(n[o]));) i = c, o++;
+ if (o >= r) return [];
+ for (; o < r - 1;) c = new m(t), s.push(c), i.on(n[o], c), i = c, o++;
+ return c = new m(a), s.push(c), i.on(n[r - 1], c), s
+ }
+
+ function l() {
+ return function (n) {
+ n && (this.v = n)
+ }
+ }
+
+ function u(n) {
+ var e = n ? {v: n} : {};
+ return a(d, l(), e)
+ }
+
+ function g(n) {
+ return n instanceof x || n instanceof C
+ }
+
+ var h = {
+ defaultProtocol: "http",
+ events: null,
+ format: r,
+ formatHref: r,
+ nl2br: !1,
+ tagName: "a",
+ target: i,
+ validate: !0,
+ ignoreTags: [],
+ attributes: null,
+ className: "linkified"
+ };
+ t.prototype = {
+ resolve: function (n) {
+ var e = n.toHref(this.defaultProtocol);
+ return {
+ formatted: this.get("format", n.toString(), n),
+ formattedHref: this.get("formatHref", e, n),
+ tagName: this.get("tagName", e, n),
+ className: this.get("className", e, n),
+ target: this.get("target", e, n),
+ events: this.getObject("events", e, n),
+ attributes: this.getObject("attributes", e, n)
+ }
+ }, check: function (n) {
+ return this.get("validate", n.toString(), n)
+ }, get: function (e, a, t) {
+ var o = void 0, r = this[e];
+ if (!r) return r;
+ switch ("undefined" == typeof r ? "undefined" : n(r)) {
+ case"function":
+ return r(a, t.type);
+ case"object":
+ return o = r.hasOwnProperty(t.type) ? r[t.type] : h[e], "function" == typeof o ? o(a, t.type) : o
+ }
+ return r
+ }, getObject: function (n, e, a) {
+ var t = this[n];
+ return "function" == typeof t ? t(e, a.type) : t
+ }
+ };
+ var b = Object.freeze({defaults: h, Options: t, contains: o}), p = s();
+ p.prototype = {
+ defaultTransition: !1, on: function (n, e) {
+ if (n instanceof Array) {
+ for (var a = 0; a < n.length; a++) this.j.push([n[a], e]);
+ return this
+ }
+ return this.j.push([n, e]), this
+ }, next: function (n) {
+ for (var e = 0; e < this.j.length; e++) {
+ var a = this.j[e], t = a[0], o = a[1];
+ if (this.test(n, t)) return o
+ }
+ return this.defaultTransition
+ }, accepts: function () {
+ return !!this.T
+ }, test: function (n, e) {
+ return n === e
+ }, emit: function () {
+ return this.T
+ }
+ };
+ var m = a(p, s(), {
+ test: function (n, e) {
+ return n === e || e instanceof RegExp && e.test(n)
+ }
+ }), f = a(p, s(), {
+ jump: function (n) {
+ var e = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : null, a = this.next(new n(""));
+ return a === this.defaultTransition ? (a = new this.constructor(e), this.on(n, a)) : e && (a.T = e), a
+ }, test: function (n, e) {
+ return n instanceof e
+ }
+ }), d = l();
+ d.prototype = {
+ toString: function () {
+ return this.v + ""
+ }
+ };
+ var x = u(), y = u("@"), v = u(":"), k = u("."), w = u(), j = u(), z = u("\n"), O = u(), q = u("+"), N = u("#"),
+ S = u(), T = u("mailto:"), A = u("?"), L = u("/"), P = u("_"), E = u(), C = u(), R = u(), H = u("{"),
+ B = u("["), U = u("<"), M = u("("), D = u("}"), I = u("]"), K = u(">"), _ = u(")"), G = u("&"),
+ Y = Object.freeze({
+ Base: d,
+ DOMAIN: x,
+ AT: y,
+ COLON: v,
+ DOT: k,
+ PUNCTUATION: w,
+ LOCALHOST: j,
+ NL: z,
+ NUM: O,
+ PLUS: q,
+ POUND: N,
+ QUERY: A,
+ PROTOCOL: S,
+ MAILTO: T,
+ SLASH: L,
+ UNDERSCORE: P,
+ SYM: E,
+ TLD: C,
+ WS: R,
+ OPENBRACE: H,
+ OPENBRACKET: B,
+ OPENANGLEBRACKET: U,
+ OPENPAREN: M,
+ CLOSEBRACE: D,
+ CLOSEBRACKET: I,
+ CLOSEANGLEBRACKET: K,
+ CLOSEPAREN: _,
+ AMPERSAND: G
+ }),
+ Q = "aaa|aarp|abarth|abb|abbott|abbvie|abc|able|abogado|abudhabi|ac|academy|accenture|accountant|accountants|aco|active|actor|ad|adac|ads|adult|ae|aeg|aero|aetna|af|afamilycompany|afl|africa|ag|agakhan|agency|ai|aig|aigo|airbus|airforce|airtel|akdn|al|alfaromeo|alibaba|alipay|allfinanz|allstate|ally|alsace|alstom|am|americanexpress|americanfamily|amex|amfam|amica|amsterdam|analytics|android|anquan|anz|ao|aol|apartments|app|apple|aq|aquarelle|ar|arab|aramco|archi|army|arpa|art|arte|as|asda|asia|associates|at|athleta|attorney|au|auction|audi|audible|audio|auspost|author|auto|autos|avianca|aw|aws|ax|axa|az|azure|ba|baby|baidu|banamex|bananarepublic|band|bank|bar|barcelona|barclaycard|barclays|barefoot|bargains|baseball|basketball|bauhaus|bayern|bb|bbc|bbt|bbva|bcg|bcn|bd|be|beats|beauty|beer|bentley|berlin|best|bestbuy|bet|bf|bg|bh|bharti|bi|bible|bid|bike|bing|bingo|bio|biz|bj|black|blackfriday|blanco|blockbuster|blog|bloomberg|blue|bm|bms|bmw|bn|bnl|bnpparibas|bo|boats|boehringer|bofa|bom|bond|boo|book|booking|boots|bosch|bostik|boston|bot|boutique|box|br|bradesco|bridgestone|broadway|broker|brother|brussels|bs|bt|budapest|bugatti|build|builders|business|buy|buzz|bv|bw|by|bz|bzh|ca|cab|cafe|cal|call|calvinklein|cam|camera|camp|cancerresearch|canon|capetown|capital|capitalone|car|caravan|cards|care|career|careers|cars|cartier|casa|case|caseih|cash|casino|cat|catering|catholic|cba|cbn|cbre|cbs|cc|cd|ceb|center|ceo|cern|cf|cfa|cfd|cg|ch|chanel|channel|chase|chat|cheap|chintai|chloe|christmas|chrome|chrysler|church|ci|cipriani|circle|cisco|citadel|citi|citic|city|cityeats|ck|cl|claims|cleaning|click|clinic|clinique|clothing|cloud|club|clubmed|cm|cn|co|coach|codes|coffee|college|cologne|com|comcast|commbank|community|company|compare|computer|comsec|condos|construction|consulting|contact|contractors|cooking|cookingchannel|cool|coop|corsica|country|coupon|coupons|courses|cr|credit|creditcard|creditunion|cricket|crown|crs|cruise|cruises|csc|cu|cuisinella|cv|cw|cx|cy|cymru|cyou|cz|dabur|dad|dance|data|date|dating|datsun|day|dclk|dds|de|deal|dealer|deals|degree|delivery|dell|deloitte|delta|democrat|dental|dentist|desi|design|dev|dhl|diamonds|diet|digital|direct|directory|discount|discover|dish|diy|dj|dk|dm|dnp|do|docs|doctor|dodge|dog|doha|domains|dot|download|drive|dtv|dubai|duck|dunlop|duns|dupont|durban|dvag|dvr|dz|earth|eat|ec|eco|edeka|edu|education|ee|eg|email|emerck|energy|engineer|engineering|enterprises|epost|epson|equipment|er|ericsson|erni|es|esq|estate|esurance|et|etisalat|eu|eurovision|eus|events|everbank|exchange|expert|exposed|express|extraspace|fage|fail|fairwinds|faith|family|fan|fans|farm|farmers|fashion|fast|fedex|feedback|ferrari|ferrero|fi|fiat|fidelity|fido|film|final|finance|financial|fire|firestone|firmdale|fish|fishing|fit|fitness|fj|fk|flickr|flights|flir|florist|flowers|fly|fm|fo|foo|food|foodnetwork|football|ford|forex|forsale|forum|foundation|fox|fr|free|fresenius|frl|frogans|frontdoor|frontier|ftr|fujitsu|fujixerox|fun|fund|furniture|futbol|fyi|ga|gal|gallery|gallo|gallup|game|games|gap|garden|gb|gbiz|gd|gdn|ge|gea|gent|genting|george|gf|gg|ggee|gh|gi|gift|gifts|gives|giving|gl|glade|glass|gle|global|globo|gm|gmail|gmbh|gmo|gmx|gn|godaddy|gold|goldpoint|golf|goo|goodhands|goodyear|goog|google|gop|got|gov|gp|gq|gr|grainger|graphics|gratis|green|gripe|grocery|group|gs|gt|gu|guardian|gucci|guge|guide|guitars|guru|gw|gy|hair|hamburg|hangout|haus|hbo|hdfc|hdfcbank|health|healthcare|help|helsinki|here|hermes|hgtv|hiphop|hisamitsu|hitachi|hiv|hk|hkt|hm|hn|hockey|holdings|holiday|homedepot|homegoods|homes|homesense|honda|honeywell|horse|hospital|host|hosting|hot|hoteles|hotels|hotmail|house|how|hr|hsbc|ht|htc|hu|hughes|hyatt|hyundai|ibm|icbc|ice|icu|id|ie|ieee|ifm|ikano|il|im|imamat|imdb|immo|immobilien|in|industries|infiniti|info|ing|ink|institute|insurance|insure|int|intel|international|intuit|investments|io|ipiranga|iq|ir|irish|is|iselect|ismaili|ist|istanbul|it|itau|itv|iveco|iwc|jaguar|java|jcb|jcp|je|jeep|jetzt|jewelry|jio|jlc|jll|jm|jmp|jnj|jo|jobs|joburg|jot|joy|jp|jpmorgan|jprs|juegos|juniper|kaufen|kddi|ke|kerryhotels|kerrylogistics|kerryproperties|kfh|kg|kh|ki|kia|kim|kinder|kindle|kitchen|kiwi|km|kn|koeln|komatsu|kosher|kp|kpmg|kpn|kr|krd|kred|kuokgroup|kw|ky|kyoto|kz|la|lacaixa|ladbrokes|lamborghini|lamer|lancaster|lancia|lancome|land|landrover|lanxess|lasalle|lat|latino|latrobe|law|lawyer|lb|lc|lds|lease|leclerc|lefrak|legal|lego|lexus|lgbt|li|liaison|lidl|life|lifeinsurance|lifestyle|lighting|like|lilly|limited|limo|lincoln|linde|link|lipsy|live|living|lixil|lk|loan|loans|locker|locus|loft|lol|london|lotte|lotto|love|lpl|lplfinancial|lr|ls|lt|ltd|ltda|lu|lundbeck|lupin|luxe|luxury|lv|ly|ma|macys|madrid|maif|maison|makeup|man|management|mango|map|market|marketing|markets|marriott|marshalls|maserati|mattel|mba|mc|mckinsey|md|me|med|media|meet|melbourne|meme|memorial|men|menu|meo|merckmsd|metlife|mg|mh|miami|microsoft|mil|mini|mint|mit|mitsubishi|mk|ml|mlb|mls|mm|mma|mn|mo|mobi|mobile|mobily|moda|moe|moi|mom|monash|money|monster|mopar|mormon|mortgage|moscow|moto|motorcycles|mov|movie|movistar|mp|mq|mr|ms|msd|mt|mtn|mtr|mu|museum|mutual|mv|mw|mx|my|mz|na|nab|nadex|nagoya|name|nationwide|natura|navy|nba|nc|ne|nec|net|netbank|netflix|network|neustar|new|newholland|news|next|nextdirect|nexus|nf|nfl|ng|ngo|nhk|ni|nico|nike|nikon|ninja|nissan|nissay|nl|no|nokia|northwesternmutual|norton|now|nowruz|nowtv|np|nr|nra|nrw|ntt|nu|nyc|nz|obi|observer|off|office|okinawa|olayan|olayangroup|oldnavy|ollo|om|omega|one|ong|onl|online|onyourside|ooo|open|oracle|orange|org|organic|origins|osaka|otsuka|ott|ovh|pa|page|panasonic|panerai|paris|pars|partners|parts|party|passagens|pay|pccw|pe|pet|pf|pfizer|pg|ph|pharmacy|phd|philips|phone|photo|photography|photos|physio|piaget|pics|pictet|pictures|pid|pin|ping|pink|pioneer|pizza|pk|pl|place|play|playstation|plumbing|plus|pm|pn|pnc|pohl|poker|politie|porn|post|pr|pramerica|praxi|press|prime|pro|prod|productions|prof|progressive|promo|properties|property|protection|pru|prudential|ps|pt|pub|pw|pwc|py|qa|qpon|quebec|quest|qvc|racing|radio|raid|re|read|realestate|realtor|realty|recipes|red|redstone|redumbrella|rehab|reise|reisen|reit|reliance|ren|rent|rentals|repair|report|republican|rest|restaurant|review|reviews|rexroth|rich|richardli|ricoh|rightathome|ril|rio|rip|rmit|ro|rocher|rocks|rodeo|rogers|room|rs|rsvp|ru|rugby|ruhr|run|rw|rwe|ryukyu|sa|saarland|safe|safety|sakura|sale|salon|samsclub|samsung|sandvik|sandvikcoromant|sanofi|sap|sapo|sarl|sas|save|saxo|sb|sbi|sbs|sc|sca|scb|schaeffler|schmidt|scholarships|school|schule|schwarz|science|scjohnson|scor|scot|sd|se|search|seat|secure|security|seek|select|sener|services|ses|seven|sew|sex|sexy|sfr|sg|sh|shangrila|sharp|shaw|shell|shia|shiksha|shoes|shop|shopping|shouji|show|showtime|shriram|si|silk|sina|singles|site|sj|sk|ski|skin|sky|skype|sl|sling|sm|smart|smile|sn|sncf|so|soccer|social|softbank|software|sohu|solar|solutions|song|sony|soy|space|spiegel|spot|spreadbetting|sr|srl|srt|st|stada|staples|star|starhub|statebank|statefarm|statoil|stc|stcgroup|stockholm|storage|store|stream|studio|study|style|su|sucks|supplies|supply|support|surf|surgery|suzuki|sv|swatch|swiftcover|swiss|sx|sy|sydney|symantec|systems|sz|tab|taipei|talk|taobao|target|tatamotors|tatar|tattoo|tax|taxi|tc|tci|td|tdk|team|tech|technology|tel|telecity|telefonica|temasek|tennis|teva|tf|tg|th|thd|theater|theatre|tiaa|tickets|tienda|tiffany|tips|tires|tirol|tj|tjmaxx|tjx|tk|tkmaxx|tl|tm|tmall|tn|to|today|tokyo|tools|top|toray|toshiba|total|tours|town|toyota|toys|tr|trade|trading|training|travel|travelchannel|travelers|travelersinsurance|trust|trv|tt|tube|tui|tunes|tushu|tv|tvs|tw|tz|ua|ubank|ubs|uconnect|ug|uk|unicom|university|uno|uol|ups|us|uy|uz|va|vacations|vana|vanguard|vc|ve|vegas|ventures|verisign|versicherung|vet|vg|vi|viajes|video|vig|viking|villas|vin|vip|virgin|visa|vision|vista|vistaprint|viva|vivo|vlaanderen|vn|vodka|volkswagen|volvo|vote|voting|voto|voyage|vu|vuelos|wales|walmart|walter|wang|wanggou|warman|watch|watches|weather|weatherchannel|webcam|weber|website|wed|wedding|weibo|weir|wf|whoswho|wien|wiki|williamhill|win|windows|wine|winners|wme|wolterskluwer|woodside|work|works|world|wow|ws|wtc|wtf|xbox|xerox|xfinity|xihuan|xin|xn--11b4c3d|xn--1ck2e1b|xn--1qqw23a|xn--2scrj9c|xn--30rr7y|xn--3bst00m|xn--3ds443g|xn--3e0b707e|xn--3hcrj9c|xn--3oq18vl8pn36a|xn--3pxu8k|xn--42c2d9a|xn--45br5cyl|xn--45brj9c|xn--45q11c|xn--4gbrim|xn--54b7fta0cc|xn--55qw42g|xn--55qx5d|xn--5su34j936bgsg|xn--5tzm5g|xn--6frz82g|xn--6qq986b3xl|xn--80adxhks|xn--80ao21a|xn--80aqecdr1a|xn--80asehdb|xn--80aswg|xn--8y0a063a|xn--90a3ac|xn--90ae|xn--90ais|xn--9dbq2a|xn--9et52u|xn--9krt00a|xn--b4w605ferd|xn--bck1b9a5dre4c|xn--c1avg|xn--c2br7g|xn--cck2b3b|xn--cg4bki|xn--clchc0ea0b2g2a9gcd|xn--czr694b|xn--czrs0t|xn--czru2d|xn--d1acj3b|xn--d1alf|xn--e1a4c|xn--eckvdtc9d|xn--efvy88h|xn--estv75g|xn--fct429k|xn--fhbei|xn--fiq228c5hs|xn--fiq64b|xn--fiqs8s|xn--fiqz9s|xn--fjq720a|xn--flw351e|xn--fpcrj9c3d|xn--fzc2c9e2c|xn--fzys8d69uvgm|xn--g2xx48c|xn--gckr3f0f|xn--gecrj9c|xn--gk3at1e|xn--h2breg3eve|xn--h2brj9c|xn--h2brj9c8c|xn--hxt814e|xn--i1b6b1a6a2e|xn--imr513n|xn--io0a7i|xn--j1aef|xn--j1amh|xn--j6w193g|xn--jlq61u9w7b|xn--jvr189m|xn--kcrx77d1x4a|xn--kprw13d|xn--kpry57d|xn--kpu716f|xn--kput3i|xn--l1acc|xn--lgbbat1ad8j|xn--mgb9awbf|xn--mgba3a3ejt|xn--mgba3a4f16a|xn--mgba7c0bbn0a|xn--mgbaakc7dvf|xn--mgbaam7a8h|xn--mgbab2bd|xn--mgbai9azgqp6j|xn--mgbayh7gpa|xn--mgbb9fbpob|xn--mgbbh1a|xn--mgbbh1a71e|xn--mgbc0a9azcg|xn--mgbca7dzdo|xn--mgberp4a5d4ar|xn--mgbgu82a|xn--mgbi4ecexp|xn--mgbpl2fh|xn--mgbt3dhd|xn--mgbtx2b|xn--mgbx4cd0ab|xn--mix891f|xn--mk1bu44c|xn--mxtq1m|xn--ngbc5azd|xn--ngbe9e0a|xn--ngbrx|xn--node|xn--nqv7f|xn--nqv7fs00ema|xn--nyqy26a|xn--o3cw4h|xn--ogbpf8fl|xn--p1acf|xn--p1ai|xn--pbt977c|xn--pgbs0dh|xn--pssy2u|xn--q9jyb4c|xn--qcka1pmc|xn--qxam|xn--rhqv96g|xn--rovu88b|xn--rvc1e0am3e|xn--s9brj9c|xn--ses554g|xn--t60b56a|xn--tckwe|xn--tiq49xqyj|xn--unup4y|xn--vermgensberater-ctb|xn--vermgensberatung-pwb|xn--vhquv|xn--vuq861b|xn--w4r85el8fhu5dnra|xn--w4rs40l|xn--wgbh1c|xn--wgbl6a|xn--xhq521b|xn--xkc2al3hye2a|xn--xkc2dl3a5ee0h|xn--y9a3aq|xn--yfro4i67o|xn--ygbi2ammx|xn--zfr164b|xperia|xxx|xyz|yachts|yahoo|yamaxun|yandex|ye|yodobashi|yoga|yokohama|you|youtube|yt|yun|za|zappos|zara|zero|zip|zippo|zm|zone|zuerich|zw".split("|"),
+ W = "0123456789".split(""), X = "0123456789abcdefghijklmnopqrstuvwxyz".split(""),
+ Z = [" ", "\f", "\r", "\t", "\x0B", " ", " ", ""], F = [], J = function (n) {
+ return new m(n)
+ }, V = J(), $ = J(O), nn = J(x), en = J(), an = J(R);
+ V.on("@", J(y)).on(".", J(k)).on("+", J(q)).on("#", J(N)).on("?", J(A)).on("/", J(L)).on("_", J(P)).on(":", J(v)).on("{", J(H)).on("[", J(B)).on("<", J(U)).on("(", J(M)).on("}", J(D)).on("]", J(I)).on(">", J(K)).on(")", J(_)).on("&", J(G)).on([",", ";", "!", '"', "'"], J(w)), V.on("\n", J(z)).on(Z, an), an.on(Z, an);
+ for (var tn = 0; tn < Q.length; tn++) {
+ var on = c(Q[tn], V, C, x);
+ F.push.apply(F, on)
+ }
+ var rn = c("file", V, x, x), sn = c("ftp", V, x, x), cn = c("http", V, x, x), ln = c("mailto", V, x, x);
+ F.push.apply(F, rn), F.push.apply(F, sn), F.push.apply(F, cn), F.push.apply(F, ln);
+ var un = rn.pop(), gn = sn.pop(), hn = cn.pop(), bn = ln.pop(), pn = J(x), mn = J(S), fn = J(T);
+ gn.on("s", pn).on(":", mn), hn.on("s", pn).on(":", mn), F.push(pn), un.on(":", mn), pn.on(":", mn), bn.on(":", fn);
+ var dn = c("localhost", V, j, x);
+ F.push.apply(F, dn), V.on(W, $), $.on("-", en).on(W, $).on(X, nn), nn.on("-", en).on(X, nn);
+ for (var xn = 0; xn < F.length; xn++) F[xn].on("-", en).on(X, nn);
+ en.on("-", en).on(W, nn).on(X, nn), V.defaultTransition = J(E);
+ var yn = function (n) {
+ for (var e = n.replace(/[A-Z]/g, function (n) {
+ return n.toLowerCase()
+ }), a = n.length, t = [], o = 0; o < a;) {
+ for (var r = V, i = null, s = 0, c = null, l = -1; o < a && (i = r.next(e[o]));) r = i, r.accepts() ? (l = 0, c = r) : l >= 0 && l++, s++, o++;
+ if (!(l < 0)) {
+ o -= l, s -= l;
+ var u = c.emit();
+ t.push(new u(n.substr(o - s, s)))
+ }
+ }
+ return t
+ }, vn = V, kn = Object.freeze({State: m, TOKENS: Y, run: yn, start: vn}), wn = l();
+ wn.prototype = {
+ type: "token", isLink: !1, toString: function () {
+ for (var n = [], e = 0; e < this.v.length; e++) n.push(this.v[e].toString());
+ return n.join("")
+ }, toHref: function () {
+ return this.toString()
+ }, toObject: function () {
+ var n = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : "http";
+ return {type: this.type, value: this.toString(), href: this.toHref(n)}
+ }
+ };
+ var jn = a(wn, l(), {type: "email", isLink: !0}), zn = a(wn, l(), {
+ type: "email", isLink: !0, toHref: function () {
+ return "mailto:" + this.toString()
+ }
+ }), On = a(wn, l(), {type: "text"}), qn = a(wn, l(), {type: "nl"}), Nn = a(wn, l(), {
+ type: "url", isLink: !0, toHref: function () {
+ for (var n = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : "http", e = !1, a = !1, t = this.v, o = [], r = 0; t[r] instanceof S;) e = !0, o.push(t[r].toString().toLowerCase()), r++;
+ for (; t[r] instanceof L;) a = !0, o.push(t[r].toString()), r++;
+ for (; g(t[r]);) o.push(t[r].toString().toLowerCase()), r++;
+ for (; r < t.length; r++) o.push(t[r].toString());
+ return o = o.join(""), e || a || (o = n + "://" + o), o
+ }, hasProtocol: function () {
+ return this.v[0] instanceof S
+ }
+ }), Sn = Object.freeze({Base: wn, MAILTOEMAIL: jn, EMAIL: zn, NL: qn, TEXT: On, URL: Nn}), Tn = function (n) {
+ return new f(n)
+ }, An = Tn(), Ln = Tn(), Pn = Tn(), En = Tn(), Cn = Tn(), Rn = Tn(), Hn = Tn(), Bn = Tn(Nn), Un = Tn(),
+ Mn = Tn(Nn), Dn = Tn(Nn), In = Tn(), Kn = Tn(), _n = Tn(), Gn = Tn(), Yn = Tn(), Qn = Tn(Nn), Wn = Tn(Nn),
+ Xn = Tn(Nn), Zn = Tn(Nn), Fn = Tn(), Jn = Tn(), Vn = Tn(), $n = Tn(), ne = Tn(), ee = Tn(), ae = Tn(zn),
+ te = Tn(), oe = Tn(zn), re = Tn(jn), ie = Tn(), se = Tn(), ce = Tn(), le = Tn(), ue = Tn(qn);
+ An.on(z, ue).on(S, Ln).on(T, Pn).on(L, En), Ln.on(L, En), En.on(L, Cn), An.on(C, Rn).on(x, Rn).on(j, Bn).on(O, Rn), Cn.on(C, Dn).on(x, Dn).on(O, Dn).on(j, Dn), Rn.on(k, Hn), ne.on(k, ee), Hn.on(C, Bn).on(x, Rn).on(O, Rn).on(j, Rn), ee.on(C, ae).on(x, ne).on(O, ne).on(j, ne), Bn.on(k, Hn), ae.on(k, ee), Bn.on(v, Un).on(L, Dn), Un.on(O, Mn), Mn.on(L, Dn), ae.on(v, te), te.on(O, oe);
+ var ge = [x, y, j, O, q, N, S, L, C, P, E, G], he = [v, k, A, w, D, I, K, _, H, B, U, M];
+ Dn.on(H, Kn).on(B, _n).on(U, Gn).on(M, Yn), In.on(H, Kn).on(B, _n).on(U, Gn).on(M, Yn), Kn.on(D, Dn), _n.on(I, Dn), Gn.on(K, Dn), Yn.on(_, Dn), Qn.on(D, Dn), Wn.on(I, Dn), Xn.on(K, Dn), Zn.on(_, Dn), Fn.on(D, Dn), Jn.on(I, Dn), Vn.on(K, Dn), $n.on(_, Dn), Kn.on(ge, Qn), _n.on(ge, Wn), Gn.on(ge, Xn), Yn.on(ge, Zn), Kn.on(he, Fn), _n.on(he, Jn), Gn.on(he, Vn), Yn.on(he, $n), Qn.on(ge, Qn), Wn.on(ge, Wn), Xn.on(ge, Xn), Zn.on(ge, Zn), Qn.on(he, Qn), Wn.on(he, Wn), Xn.on(he, Xn), Zn.on(he, Zn), Fn.on(ge, Qn), Jn.on(ge, Wn), Vn.on(ge, Xn), $n.on(ge, Zn), Fn.on(he, Fn), Jn.on(he, Jn), Vn.on(he, Vn), $n.on(he, $n), Dn.on(ge, Dn), In.on(ge, Dn), Dn.on(he, In), In.on(he, In), Pn.on(C, re).on(x, re).on(O, re).on(j, re), re.on(ge, re).on(he, ie), ie.on(ge, re).on(he, ie);
+ var be = [x, O, q, N, A, P, E, G, C];
+ Rn.on(be, se).on(y, ce), Bn.on(be, se).on(y, ce), Hn.on(be, se), se.on(be, se).on(y, ce).on(k, le), le.on(be, se), ce.on(C, ne).on(x, ne).on(j, ae);
+ var pe = function (n) {
+ for (var e = n.length, a = 0, t = [], o = []; a < e;) {
+ for (var r = An, i = null, s = null, c = 0, l = null, u = -1; a < e && !(i = r.next(n[a]));) o.push(n[a++]);
+ for (; a < e && (s = i || r.next(n[a]));) i = null, r = s, r.accepts() ? (u = 0, l = r) : u >= 0 && u++, a++, c++;
+ if (u < 0) for (var g = a - c; g < a; g++) o.push(n[g]); else {
+ o.length > 0 && (t.push(new On(o)), o = []), a -= u, c -= u;
+ var h = l.emit();
+ t.push(new h(n.slice(a - c, a)))
+ }
+ }
+ return o.length > 0 && t.push(new On(o)), t
+ }, me = Object.freeze({State: f, TOKENS: Sn, run: pe, start: An});
+ Array.isArray || (Array.isArray = function (n) {
+ return "[object Array]" === Object.prototype.toString.call(n)
+ });
+ var fe = function (n) {
+ return pe(yn(n))
+ }, de = function (n) {
+ for (var e = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : null, a = fe(n), t = [], o = 0; o < a.length; o++) {
+ var r = a[o];
+ !r.isLink || e && r.type !== e || t.push(r.toObject())
+ }
+ return t
+ }, xe = function (n) {
+ var e = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : null, a = fe(n);
+ return 1 === a.length && a[0].isLink && (!e || a[0].type === e)
+ };
+ e.find = de, e.inherits = a, e.options = b, e.parser = me, e.scanner = kn, e.test = xe, e.tokenize = fe
+ }(self.linkify = self.linkify || {})
+}();
+
+"use strict";
+!function (e, n, t) {
+ var i = function (n) {
+ function t(e, n, t) {
+ var i = t[t.length - 1];
+ e.replaceChild(i, n);
+ for (var a = t.length - 2; a >= 0; a--) e.insertBefore(t[a], i), i = t[a]
+ }
+
+ function i(e, n, t) {
+ for (var i = [], a = e, r = Array.isArray(a), o = 0, a = r ? a : a[Symbol.iterator](); ;) {
+ var l;
+ if (r) {
+ if (o >= a.length) break;
+ l = a[o++]
+ } else {
+ if (o = a.next(), o.done) break;
+ l = o.value
+ }
+ var f = l;
+ if ("nl" === f.type && n.nl2br) i.push(t.createElement("br")); else if (f.isLink && n.check(f)) {
+ var s = n.resolve(f), c = s.formatted, u = s.formattedHref, y = s.tagName, d = s.className,
+ m = s.target, k = s.events, h = s.attributes, v = t.createElement(y);
+ if (v.setAttribute("href", u), d && v.setAttribute("class", d), m && v.setAttribute("target", m), h) for (var g in h) v.setAttribute(g, h[g]);
+ if (k) for (var b in k) v.addEventListener ? v.addEventListener(b, k[b]) : v.attachEvent && v.attachEvent("on" + b, k[b]);
+ v.appendChild(t.createTextNode(c)), i.push(v)
+ } else i.push(t.createTextNode(f.toString()))
+ }
+ return i
+ }
+
+ function a(e, n, r) {
+ if (!e || e.nodeType !== u) throw new Error("Cannot linkify " + e + " - Invalid DOM Node type");
+ var o = n.ignoreTags;
+ if ("A" === e.tagName || f.contains(o, e.tagName)) return e;
+ for (var s = e.firstChild; s;) {
+ var d = void 0, m = void 0, k = void 0;
+ switch (s.nodeType) {
+ case u:
+ a(s, n, r);
+ break;
+ case y:
+ if (d = s.nodeValue, m = l(d), 0 === m.length || 1 === m.length && m[0] instanceof c) break;
+ k = i(m, n, r), t(e, s, k), s = k[k.length - 1]
+ }
+ s = s.nextSibling
+ }
+ return e
+ }
+
+ function r(n, t) {
+ var i = arguments.length > 2 && void 0 !== arguments[2] && arguments[2];
+ try {
+ i = i || document || e && e.document || global && global.document
+ } catch (r) {
+ }
+ if (!i) throw new Error("Cannot find document implementation. If you are in a non-browser environment like Node.js, pass the document implementation as the third argument to linkifyElement.");
+ return t = new s(t), a(n, t, i)
+ }
+
+ function o(n) {
+ function t(e) {
+ return e = r.normalize(e), this.each(function () {
+ r.helper(this, e, i)
+ })
+ }
+
+ var i = arguments.length > 1 && void 0 !== arguments[1] && arguments[1];
+ n.fn = n.fn || {};
+ try {
+ i = i || document || e && e.document || global && global.document
+ } catch (a) {
+ }
+ if (!i) throw new Error("Cannot find document implementation. If you are in a non-browser environment like Node.js, pass the document implementation as the second argument to linkify/jquery");
+ "function" != typeof n.fn.linkify && (n.fn.linkify = t, n(i).ready(function () {
+ n("[data-linkify]").each(function () {
+ var e = n(this), t = e.data(), i = t.linkify, a = t.linkifyNlbr,
+ o = {nl2br: !!a && 0 !== a && "false" !== a};
+ "linkifyAttributes" in t && (o.attributes = t.linkifyAttributes), "linkifyDefaultProtocol" in t && (o.defaultProtocol = t.linkifyDefaultProtocol), "linkifyEvents" in t && (o.events = t.linkifyEvents), "linkifyFormat" in t && (o.format = t.linkifyFormat), "linkifyFormatHref" in t && (o.formatHref = t.linkifyFormatHref), "linkifyTagname" in t && (o.tagName = t.linkifyTagname), "linkifyTarget" in t && (o.target = t.linkifyTarget), "linkifyValidate" in t && (o.validate = t.linkifyValidate), "linkifyIgnoreTags" in t && (o.ignoreTags = t.linkifyIgnoreTags), "linkifyClassName" in t ? o.className = t.linkifyClassName : "linkifyLinkclass" in t && (o.className = t.linkifyLinkclass), o = r.normalize(o);
+ var l = "this" === i ? e : e.find(i);
+ l.linkify(o)
+ })
+ }))
+ }
+
+ var l = n.tokenize, f = n.options, s = f.Options, c = n.parser.TOKENS.TEXT, u = 1, y = 3;
+ r.helper = a, r.normalize = function (e) {
+ return new s(e)
+ };
+ try {
+ !(void 0).define && (e.linkifyElement = r)
+ } catch (d) {
+ }
+ return o
+ }(n);
+ "function" != typeof t.fn.linkify && i(t)
+}(window, linkify, jQuery);
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/js/main.js b/main/app/sprinkles/core/assets/SiteAssets/js/main.js
new file mode 100644
index 0000000..d2123f5
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/js/main.js
@@ -0,0 +1,91 @@
+var MainTabWindows = $(".MainTabWindows");
+var NavbarIconWrap = $(".NavbarIconWrap");
+var Navbar = $(".Navbar");
+var NavbarLine = $(".NavbarLine");
+
+
+/******
+ NAVBAR
+ *****/
+var $el, leftPos, newWidth;
+NavbarLine
+ .css("left", $(".ActiveTab").position().left)
+ .data("origLeft", NavbarLine.position().left)
+ .data("origWidth", NavbarLine.width());
+NavbarIconWrap.on("click", function () {
+ NavbarIconWrap.removeClass("ActiveTab");
+ $(this).addClass("ActiveTab");
+ var index = $(this).attr('id');
+ MainTabWindows.slick('slickGoTo', index);
+ //$('.MainTabWindows').flickity().flickity('select', index);
+ $el = $(this);
+ leftPos = $el.position().left;
+ NavbarLine.stop().animate({
+ left: leftPos,
+ width: newWidth
+ }, 300);
+});
+
+/********
+ FLICKITY
+ *******/
+MainTabWindows.slick({
+ initialSlide: 2,
+ mobileFirst: true,
+ nextArrow: "",
+ prevArrow: "",
+ infinite: false,
+ zIndex: 500
+});
+
+MainTabWindows.on('beforeChange', function (event, slick, currentSlide, nextSlide) {
+ //console.log(nextSlide);
+ NavbarIconWrap.removeClass("ActiveTab");
+ $el = $("#" + nextSlide);
+ $el.addClass("ActiveTab");
+ leftPos = $el.position().left;
+ NavbarLine.stop().animate({
+ left: leftPos,
+ width: newWidth
+ }, 300);
+});
+
+/*
+$('.MainTabWindows').flickity({
+ cellAlign: 'left',
+ prevNextButtons: false,
+ pageDots: false,
+ friction: 0.3,
+ dragThreshold: ($("body").width() * 0.5),
+ initialIndex: 2,
+ wrapAround: true,
+ maxSwipeWidth: 0,
+ on: {
+ change: function (index) {
+ $(".NavbarIconWrap").removeClass("ActiveTab");
+ $el = $("#" + index);
+ $el.addClass("ActiveTab");
+ leftPos = $el.position().left;
+ $magicLine.stop().animate({
+ left: leftPos,
+ width: newWidth
+ }, 300);
+ },
+ dragStart: function () {
+ $(".ActiveTab").css({ transform: 'scale(1.05)' });
+ },
+ dragEnd: function () {
+ $(".NavbarIconWrap").css({ transform: 'scale(1.0)' });
+ }/*,
+ scroll: function (event, progress) {
+ var TotalWidth = $("body").width();
+ console.log(progress / 10);
+ leftPos = ((progress / 1000) * TotalWidth + 'px');
+ $magicLine.stop().animate({
+ left: leftPos,
+ width: newWidth
+ });
+ }*
+ }
+});
+*/
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/js/modernizr.js b/main/app/sprinkles/core/assets/SiteAssets/js/modernizr.js
new file mode 100644
index 0000000..40dd2a9
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/js/modernizr.js
@@ -0,0 +1 @@
+window.Modernizr=function(e,t,n){function r(e){b.cssText=e}function o(e,t){return r(S.join(e+";")+(t||""))}function a(e,t){return typeof e===t}function i(e,t){return!!~(""+e).indexOf(t)}function c(e,t){for(var r in e){var o=e[r];if(!i(o,"-")&&b[o]!==n)return"pfx"==t?o:!0}return!1}function s(e,t,r){for(var o in e){var i=t[e[o]];if(i!==n)return r===!1?e[o]:a(i,"function")?i.bind(r||t):i}return!1}function u(e,t,n){var r=e.charAt(0).toUpperCase()+e.slice(1),o=(e+" "+k.join(r+" ")+r).split(" ");return a(t,"string")||a(t,"undefined")?c(o,t):(o=(e+" "+T.join(r+" ")+r).split(" "),s(o,t,n))}function l(){p.input=function(n){for(var r=0,o=n.length;o>r;r++)j[n[r]]=!!(n[r]in E);return j.list&&(j.list=!(!t.createElement("datalist")||!e.HTMLDataListElement)),j}("autocomplete autofocus list placeholder max min multiple pattern required step".split(" ")),p.inputtypes=function(e){for(var r,o,a,i=0,c=e.length;c>i;i++)E.setAttribute("type",o=e[i]),r="text"!==E.type,r&&(E.value=x,E.style.cssText="position:absolute;visibility:hidden;",/^range$/.test(o)&&E.style.WebkitAppearance!==n?(g.appendChild(E),a=t.defaultView,r=a.getComputedStyle&&"textfield"!==a.getComputedStyle(E,null).WebkitAppearance&&0!==E.offsetHeight,g.removeChild(E)):/^(search|tel)$/.test(o)||(r=/^(url|email)$/.test(o)?E.checkValidity&&E.checkValidity()===!1:E.value!=x)),P[e[i]]=!!r;return P}("search tel url email datetime date month week time datetime-local number range color".split(" "))}var d,f,m="2.8.3",p={},h=!0,g=t.documentElement,v="modernizr",y=t.createElement(v),b=y.style,E=t.createElement("input"),x=":)",w={}.toString,S=" -webkit- -moz- -o- -ms- ".split(" "),C="Webkit Moz O ms",k=C.split(" "),T=C.toLowerCase().split(" "),N={svg:"http://www.w3.org/2000/svg"},M={},P={},j={},$=[],D=$.slice,F=function(e,n,r,o){var a,i,c,s,u=t.createElement("div"),l=t.body,d=l||t.createElement("body");if(parseInt(r,10))for(;r--;)c=t.createElement("div"),c.id=o?o[r]:v+(r+1),u.appendChild(c);return a=["",'"].join(""),u.id=v,(l?u:d).innerHTML+=a,d.appendChild(u),l||(d.style.background="",d.style.overflow="hidden",s=g.style.overflow,g.style.overflow="hidden",g.appendChild(d)),i=n(u,e),l?u.parentNode.removeChild(u):(d.parentNode.removeChild(d),g.style.overflow=s),!!i},z=function(t){var n=e.matchMedia||e.msMatchMedia;if(n)return n(t)&&n(t).matches||!1;var r;return F("@media "+t+" { #"+v+" { position: absolute; } }",function(t){r="absolute"==(e.getComputedStyle?getComputedStyle(t,null):t.currentStyle).position}),r},A=function(){function e(e,o){o=o||t.createElement(r[e]||"div"),e="on"+e;var i=e in o;return i||(o.setAttribute||(o=t.createElement("div")),o.setAttribute&&o.removeAttribute&&(o.setAttribute(e,""),i=a(o[e],"function"),a(o[e],"undefined")||(o[e]=n),o.removeAttribute(e))),o=null,i}var r={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return e}(),L={}.hasOwnProperty;f=a(L,"undefined")||a(L.call,"undefined")?function(e,t){return t in e&&a(e.constructor.prototype[t],"undefined")}:function(e,t){return L.call(e,t)},Function.prototype.bind||(Function.prototype.bind=function(e){var t=this;if("function"!=typeof t)throw new TypeError;var n=D.call(arguments,1),r=function(){if(this instanceof r){var o=function(){};o.prototype=t.prototype;var a=new o,i=t.apply(a,n.concat(D.call(arguments)));return Object(i)===i?i:a}return t.apply(e,n.concat(D.call(arguments)))};return r}),M.flexbox=function(){return u("flexWrap")},M.flexboxlegacy=function(){return u("boxDirection")},M.canvas=function(){var e=t.createElement("canvas");return!(!e.getContext||!e.getContext("2d"))},M.canvastext=function(){return!(!p.canvas||!a(t.createElement("canvas").getContext("2d").fillText,"function"))},M.webgl=function(){return!!e.WebGLRenderingContext},M.touch=function(){var n;return"ontouchstart"in e||e.DocumentTouch&&t instanceof DocumentTouch?n=!0:F(["@media (",S.join("touch-enabled),("),v,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(e){n=9===e.offsetTop}),n},M.geolocation=function(){return"geolocation"in navigator},M.postmessage=function(){return!!e.postMessage},M.websqldatabase=function(){return!!e.openDatabase},M.indexedDB=function(){return!!u("indexedDB",e)},M.hashchange=function(){return A("hashchange",e)&&(t.documentMode===n||t.documentMode>7)},M.history=function(){return!(!e.history||!history.pushState)},M.draganddrop=function(){var e=t.createElement("div");return"draggable"in e||"ondragstart"in e&&"ondrop"in e},M.websockets=function(){return"WebSocket"in e||"MozWebSocket"in e},M.rgba=function(){return r("background-color:rgba(150,255,150,.5)"),i(b.backgroundColor,"rgba")},M.hsla=function(){return r("background-color:hsla(120,40%,100%,.5)"),i(b.backgroundColor,"rgba")||i(b.backgroundColor,"hsla")},M.multiplebgs=function(){return r("background:url(https://),url(https://),red url(https://)"),/(url\s*\(.*?){3}/.test(b.background)},M.backgroundsize=function(){return u("backgroundSize")},M.borderimage=function(){return u("borderImage")},M.borderradius=function(){return u("borderRadius")},M.boxshadow=function(){return u("boxShadow")},M.textshadow=function(){return""===t.createElement("div").style.textShadow},M.opacity=function(){return o("opacity:.55"),/^0.55$/.test(b.opacity)},M.cssanimations=function(){return u("animationName")},M.csscolumns=function(){return u("columnCount")},M.cssgradients=function(){var e="background-image:",t="gradient(linear,left top,right bottom,from(#9f9),to(white));",n="linear-gradient(left top,#9f9, white);";return r((e+"-webkit- ".split(" ").join(t+e)+S.join(n+e)).slice(0,-e.length)),i(b.backgroundImage,"gradient")},M.cssreflections=function(){return u("boxReflect")},M.csstransforms=function(){return!!u("transform")},M.csstransforms3d=function(){var e=!!u("perspective");return e&&"webkitPerspective"in g.style&&F("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(t){e=9===t.offsetLeft&&3===t.offsetHeight}),e},M.csstransitions=function(){return u("transition")},M.fontface=function(){var e;return F('@font-face {font-family:"font";src:url("https://")}',function(n,r){var o=t.getElementById("smodernizr"),a=o.sheet||o.styleSheet,i=a?a.cssRules&&a.cssRules[0]?a.cssRules[0].cssText:a.cssText||"":"";e=/src/i.test(i)&&0===i.indexOf(r.split(" ")[0])}),e},M.generatedcontent=function(){var e;return F(["#",v,"{font:0/0 a}#",v,':after{content:"',x,'";visibility:hidden;font:3px/1 a}'].join(""),function(t){e=t.offsetHeight>=3}),e},M.video=function(){var e=t.createElement("video"),n=!1;try{(n=!!e.canPlayType)&&(n=new Boolean(n),n.ogg=e.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,""),n.h264=e.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,""),n.webm=e.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,""))}catch(r){}return n},M.audio=function(){var e=t.createElement("audio"),n=!1;try{(n=!!e.canPlayType)&&(n=new Boolean(n),n.ogg=e.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),n.mp3=e.canPlayType("audio/mpeg;").replace(/^no$/,""),n.wav=e.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),n.m4a=(e.canPlayType("audio/x-m4a;")||e.canPlayType("audio/aac;")).replace(/^no$/,""))}catch(r){}return n},M.localstorage=function(){try{return localStorage.setItem(v,v),localStorage.removeItem(v),!0}catch(e){return!1}},M.sessionstorage=function(){try{return sessionStorage.setItem(v,v),sessionStorage.removeItem(v),!0}catch(e){return!1}},M.webworkers=function(){return!!e.Worker},M.applicationcache=function(){return!!e.applicationCache},M.svg=function(){return!!t.createElementNS&&!!t.createElementNS(N.svg,"svg").createSVGRect},M.inlinesvg=function(){var e=t.createElement("div");return e.innerHTML="
",(e.firstChild&&e.firstChild.namespaceURI)==N.svg},M.smil=function(){return!!t.createElementNS&&/SVGAnimate/.test(w.call(t.createElementNS(N.svg,"animate")))},M.svgclippaths=function(){return!!t.createElementNS&&/SVGClipPath/.test(w.call(t.createElementNS(N.svg,"clipPath")))};for(var H in M)f(M,H)&&(d=H.toLowerCase(),p[d]=M[H](),$.push((p[d]?"":"no-")+d));return p.input||l(),p.addTest=function(e,t){if("object"==typeof e)for(var r in e)f(e,r)&&p.addTest(r,e[r]);else{if(e=e.toLowerCase(),p[e]!==n)return p;t="function"==typeof t?t():t,"undefined"!=typeof h&&h&&(g.className+=" "+(t?"":"no-")+e),p[e]=t}return p},r(""),y=E=null,function(e,t){function n(e,t){var n=e.createElement("p"),r=e.getElementsByTagName("head")[0]||e.documentElement;return n.innerHTML="x",r.insertBefore(n.lastChild,r.firstChild)}function r(){var e=y.elements;return"string"==typeof e?e.split(" "):e}function o(e){var t=v[e[h]];return t||(t={},g++,e[h]=g,v[g]=t),t}function a(e,n,r){if(n||(n=t),l)return n.createElement(e);r||(r=o(n));var a;return a=r.cache[e]?r.cache[e].cloneNode():p.test(e)?(r.cache[e]=r.createElem(e)).cloneNode():r.createElem(e),!a.canHaveChildren||m.test(e)||a.tagUrn?a:r.frag.appendChild(a)}function i(e,n){if(e||(e=t),l)return e.createDocumentFragment();n=n||o(e);for(var a=n.frag.cloneNode(),i=0,c=r(),s=c.length;s>i;i++)a.createElement(c[i]);return a}function c(e,t){t.cache||(t.cache={},t.createElem=e.createElement,t.createFrag=e.createDocumentFragment,t.frag=t.createFrag()),e.createElement=function(n){return y.shivMethods?a(n,e,t):t.createElem(n)},e.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+r().join().replace(/[\w\-]+/g,function(e){return t.createElem(e),t.frag.createElement(e),'c("'+e+'")'})+");return n}")(y,t.frag)}function s(e){e||(e=t);var r=o(e);return!y.shivCSS||u||r.hasCSS||(r.hasCSS=!!n(e,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||c(e,r),e}var u,l,d="3.7.0",f=e.html5||{},m=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,h="_html5shiv",g=0,v={};!function(){try{var e=t.createElement("a");e.innerHTML="
",u="hidden"in e,l=1==e.childNodes.length||function(){t.createElement("a");var e=t.createDocumentFragment();return"undefined"==typeof e.cloneNode||"undefined"==typeof e.createDocumentFragment||"undefined"==typeof e.createElement}()}catch(n){u=!0,l=!0}}();var y={elements:f.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:d,shivCSS:f.shivCSS!==!1,supportsUnknownElements:l,shivMethods:f.shivMethods!==!1,type:"default",shivDocument:s,createElement:a,createDocumentFragment:i};e.html5=y,s(t)}(this,t),p._version=m,p._prefixes=S,p._domPrefixes=T,p._cssomPrefixes=k,p.mq=z,p.hasEvent=A,p.testProp=function(e){return c([e])},p.testAllProps=u,p.testStyles=F,p.prefixed=function(e,t,n){return t?u(e,t,n):u(e,"pfx")},g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(h?" js "+$.join(" "):""),p}(this,this.document);
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/js/slick.js b/main/app/sprinkles/core/assets/SiteAssets/js/slick.js
new file mode 100644
index 0000000..c697993
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/js/slick.js
@@ -0,0 +1,556 @@
+!function (i) {
+ "use strict";
+ "function" == typeof define && define.amd ? define(["jquery"], i) : "undefined" != typeof exports ? module.exports = i(require("jquery")) : i(jQuery)
+}(function (i) {
+ "use strict";
+ var e = window.Slick || {};
+ (e = function () {
+ var e = 0;
+ return function (t, o) {
+ var s, n = this;
+ n.defaults = {
+ accessibility: !0,
+ adaptiveHeight: !1,
+ appendArrows: i(t),
+ appendDots: i(t),
+ arrows: !0,
+ asNavFor: null,
+ prevArrow: '
Previous ',
+ nextArrow: '
Next ',
+ autoplay: !1,
+ autoplaySpeed: 3e3,
+ centerMode: !1,
+ centerPadding: "50px",
+ cssEase: "ease",
+ customPaging: function (e, t) {
+ return i('
').text(t + 1)
+ },
+ dots: !1,
+ dotsClass: "slick-dots",
+ draggable: !0,
+ easing: "linear",
+ edgeFriction: .35,
+ fade: !1,
+ focusOnSelect: !1,
+ focusOnChange: !1,
+ infinite: !0,
+ initialSlide: 0,
+ lazyLoad: "ondemand",
+ mobileFirst: !1,
+ pauseOnHover: !0,
+ pauseOnFocus: !0,
+ pauseOnDotsHover: !1,
+ respondTo: "window",
+ responsive: null,
+ rows: 1,
+ rtl: !1,
+ slide: "",
+ slidesPerRow: 1,
+ slidesToShow: 1,
+ slidesToScroll: 1,
+ speed: 500,
+ swipe: !0,
+ swipeToSlide: !1,
+ touchMove: !0,
+ touchThreshold: 5,
+ useCSS: !0,
+ useTransform: !0,
+ variableWidth: !1,
+ vertical: !1,
+ verticalSwiping: !1,
+ waitForAnimate: !0,
+ zIndex: 1e3
+ }, n.initials = {
+ animating: !1,
+ dragging: !1,
+ autoPlayTimer: null,
+ currentDirection: 0,
+ currentLeft: null,
+ currentSlide: 0,
+ direction: 1,
+ $dots: null,
+ listWidth: null,
+ listHeight: null,
+ loadIndex: 0,
+ $nextArrow: null,
+ $prevArrow: null,
+ scrolling: !1,
+ slideCount: null,
+ slideWidth: null,
+ $slideTrack: null,
+ $slides: null,
+ sliding: !1,
+ slideOffset: 0,
+ swipeLeft: null,
+ swiping: !1,
+ $list: null,
+ touchObject: {},
+ transformsEnabled: !1,
+ unslicked: !1
+ }, i.extend(n, n.initials), n.activeBreakpoint = null, n.animType = null, n.animProp = null, n.breakpoints = [], n.breakpointSettings = [], n.cssTransitions = !1, n.focussed = !1, n.interrupted = !1, n.hidden = "hidden", n.paused = !0, n.positionProp = null, n.respondTo = null, n.rowCount = 1, n.shouldClick = !0, n.$slider = i(t), n.$slidesCache = null, n.transformType = null, n.transitionType = null, n.visibilityChange = "visibilitychange", n.windowWidth = 0, n.windowTimer = null, s = i(t).data("slick") || {}, n.options = i.extend({}, n.defaults, o, s), n.currentSlide = n.options.initialSlide, n.originalSettings = n.options, void 0 !== document.mozHidden ? (n.hidden = "mozHidden", n.visibilityChange = "mozvisibilitychange") : void 0 !== document.webkitHidden && (n.hidden = "webkitHidden", n.visibilityChange = "webkitvisibilitychange"), n.autoPlay = i.proxy(n.autoPlay, n), n.autoPlayClear = i.proxy(n.autoPlayClear, n), n.autoPlayIterator = i.proxy(n.autoPlayIterator, n), n.changeSlide = i.proxy(n.changeSlide, n), n.clickHandler = i.proxy(n.clickHandler, n), n.selectHandler = i.proxy(n.selectHandler, n), n.setPosition = i.proxy(n.setPosition, n), n.swipeHandler = i.proxy(n.swipeHandler, n), n.dragHandler = i.proxy(n.dragHandler, n), n.keyHandler = i.proxy(n.keyHandler, n), n.instanceUid = e++, n.htmlExpr = /^(?:\s*(<[\w\W]+>)[^>]*)$/, n.registerBreakpoints(), n.init(!0)
+ }
+ }()).prototype.activateADA = function () {
+ this.$slideTrack.find(".slick-active").attr({"aria-hidden": "false"}).find("a, input, button, select").attr({tabindex: "0"})
+ }, e.prototype.addSlide = e.prototype.slickAdd = function (e, t, o) {
+ var s = this;
+ if ("boolean" == typeof t) o = t, t = null; else if (t < 0 || t >= s.slideCount) return !1;
+ s.unload(), "number" == typeof t ? 0 === t && 0 === s.$slides.length ? i(e).appendTo(s.$slideTrack) : o ? i(e).insertBefore(s.$slides.eq(t)) : i(e).insertAfter(s.$slides.eq(t)) : !0 === o ? i(e).prependTo(s.$slideTrack) : i(e).appendTo(s.$slideTrack), s.$slides = s.$slideTrack.children(this.options.slide), s.$slideTrack.children(this.options.slide).detach(), s.$slideTrack.append(s.$slides), s.$slides.each(function (e, t) {
+ i(t).attr("data-slick-index", e)
+ }), s.$slidesCache = s.$slides, s.reinit()
+ }, e.prototype.animateHeight = function () {
+ var i = this;
+ if (1 === i.options.slidesToShow && !0 === i.options.adaptiveHeight && !1 === i.options.vertical) {
+ var e = i.$slides.eq(i.currentSlide).outerHeight(!0);
+ i.$list.animate({height: e}, i.options.speed)
+ }
+ }, e.prototype.animateSlide = function (e, t) {
+ var o = {}, s = this;
+ s.animateHeight(), !0 === s.options.rtl && !1 === s.options.vertical && (e = -e), !1 === s.transformsEnabled ? !1 === s.options.vertical ? s.$slideTrack.animate({left: e}, s.options.speed, s.options.easing, t) : s.$slideTrack.animate({top: e}, s.options.speed, s.options.easing, t) : !1 === s.cssTransitions ? (!0 === s.options.rtl && (s.currentLeft = -s.currentLeft), i({animStart: s.currentLeft}).animate({animStart: e}, {
+ duration: s.options.speed,
+ easing: s.options.easing,
+ step: function (i) {
+ i = Math.ceil(i), !1 === s.options.vertical ? (o[s.animType] = "translate(" + i + "px, 0px)", s.$slideTrack.css(o)) : (o[s.animType] = "translate(0px," + i + "px)", s.$slideTrack.css(o))
+ },
+ complete: function () {
+ t && t.call()
+ }
+ })) : (s.applyTransition(), e = Math.ceil(e), !1 === s.options.vertical ? o[s.animType] = "translate3d(" + e + "px, 0px, 0px)" : o[s.animType] = "translate3d(0px," + e + "px, 0px)", s.$slideTrack.css(o), t && setTimeout(function () {
+ s.disableTransition(), t.call()
+ }, s.options.speed))
+ }, e.prototype.getNavTarget = function () {
+ var e = this, t = e.options.asNavFor;
+ return t && null !== t && (t = i(t).not(e.$slider)), t
+ }, e.prototype.asNavFor = function (e) {
+ var t = this.getNavTarget();
+ null !== t && "object" == typeof t && t.each(function () {
+ var t = i(this).slick("getSlick");
+ t.unslicked || t.slideHandler(e, !0)
+ })
+ }, e.prototype.applyTransition = function (i) {
+ var e = this, t = {};
+ !1 === e.options.fade ? t[e.transitionType] = e.transformType + " " + e.options.speed + "ms " + e.options.cssEase : t[e.transitionType] = "opacity " + e.options.speed + "ms " + e.options.cssEase, !1 === e.options.fade ? e.$slideTrack.css(t) : e.$slides.eq(i).css(t)
+ }, e.prototype.autoPlay = function () {
+ var i = this;
+ i.autoPlayClear(), i.slideCount > i.options.slidesToShow && (i.autoPlayTimer = setInterval(i.autoPlayIterator, i.options.autoplaySpeed))
+ }, e.prototype.autoPlayClear = function () {
+ var i = this;
+ i.autoPlayTimer && clearInterval(i.autoPlayTimer)
+ }, e.prototype.autoPlayIterator = function () {
+ var i = this, e = i.currentSlide + i.options.slidesToScroll;
+ i.paused || i.interrupted || i.focussed || (!1 === i.options.infinite && (1 === i.direction && i.currentSlide + 1 === i.slideCount - 1 ? i.direction = 0 : 0 === i.direction && (e = i.currentSlide - i.options.slidesToScroll, i.currentSlide - 1 == 0 && (i.direction = 1))), i.slideHandler(e))
+ }, e.prototype.buildArrows = function () {
+ var e = this;
+ !0 === e.options.arrows && (e.$prevArrow = i(e.options.prevArrow).addClass("slick-arrow"), e.$nextArrow = i(e.options.nextArrow).addClass("slick-arrow"), e.slideCount > e.options.slidesToShow ? (e.$prevArrow.removeClass("slick-hidden").removeAttr("aria-hidden tabindex"), e.$nextArrow.removeClass("slick-hidden").removeAttr("aria-hidden tabindex"), e.htmlExpr.test(e.options.prevArrow) && e.$prevArrow.prependTo(e.options.appendArrows), e.htmlExpr.test(e.options.nextArrow) && e.$nextArrow.appendTo(e.options.appendArrows), !0 !== e.options.infinite && e.$prevArrow.addClass("slick-disabled").attr("aria-disabled", "true")) : e.$prevArrow.add(e.$nextArrow).addClass("slick-hidden").attr({
+ "aria-disabled": "true",
+ tabindex: "-1"
+ }))
+ }, e.prototype.buildDots = function () {
+ var e, t, o = this;
+ if (!0 === o.options.dots) {
+ for (o.$slider.addClass("slick-dotted"), t = i("
").addClass(o.options.dotsClass), e = 0; e <= o.getDotCount(); e += 1) t.append(i("
").append(o.options.customPaging.call(this, o, e)));
+ o.$dots = t.appendTo(o.options.appendDots), o.$dots.find("li").first().addClass("slick-active")
+ }
+ }, e.prototype.buildOut = function () {
+ var e = this;
+ e.$slides = e.$slider.children(e.options.slide + ":not(.slick-cloned)").addClass("slick-slide"), e.slideCount = e.$slides.length, e.$slides.each(function (e, t) {
+ i(t).attr("data-slick-index", e).data("originalStyling", i(t).attr("style") || "")
+ }), e.$slider.addClass("slick-slider"), e.$slideTrack = 0 === e.slideCount ? i('
').appendTo(e.$slider) : e.$slides.wrapAll('
').parent(), e.$list = e.$slideTrack.wrap('
').parent(), e.$slideTrack.css("opacity", 0), !0 !== e.options.centerMode && !0 !== e.options.swipeToSlide || (e.options.slidesToScroll = 1), i("img[data-lazy]", e.$slider).not("[src]").addClass("slick-loading"), e.setupInfinite(), e.buildArrows(), e.buildDots(), e.updateDots(), e.setSlideClasses("number" == typeof e.currentSlide ? e.currentSlide : 0), !0 === e.options.draggable && e.$list.addClass("draggable")
+ }, e.prototype.buildRows = function () {
+ var i, e, t, o, s, n, r, l = this;
+ if (o = document.createDocumentFragment(), n = l.$slider.children(), l.options.rows > 1) {
+ for (r = l.options.slidesPerRow * l.options.rows, s = Math.ceil(n.length / r), i = 0; i < s; i++) {
+ var d = document.createElement("div");
+ for (e = 0; e < l.options.rows; e++) {
+ var a = document.createElement("div");
+ for (t = 0; t < l.options.slidesPerRow; t++) {
+ var c = i * r + (e * l.options.slidesPerRow + t);
+ n.get(c) && a.appendChild(n.get(c))
+ }
+ d.appendChild(a)
+ }
+ o.appendChild(d)
+ }
+ l.$slider.empty().append(o), l.$slider.children().children().children().css({
+ width: 100 / l.options.slidesPerRow + "%",
+ display: "inline-block"
+ })
+ }
+ }, e.prototype.checkResponsive = function (e, t) {
+ var o, s, n, r = this, l = !1, d = r.$slider.width(), a = window.innerWidth || i(window).width();
+ if ("window" === r.respondTo ? n = a : "slider" === r.respondTo ? n = d : "min" === r.respondTo && (n = Math.min(a, d)), r.options.responsive && r.options.responsive.length && null !== r.options.responsive) {
+ s = null;
+ for (o in r.breakpoints) r.breakpoints.hasOwnProperty(o) && (!1 === r.originalSettings.mobileFirst ? n < r.breakpoints[o] && (s = r.breakpoints[o]) : n > r.breakpoints[o] && (s = r.breakpoints[o]));
+ null !== s ? null !== r.activeBreakpoint ? (s !== r.activeBreakpoint || t) && (r.activeBreakpoint = s, "unslick" === r.breakpointSettings[s] ? r.unslick(s) : (r.options = i.extend({}, r.originalSettings, r.breakpointSettings[s]), !0 === e && (r.currentSlide = r.options.initialSlide), r.refresh(e)), l = s) : (r.activeBreakpoint = s, "unslick" === r.breakpointSettings[s] ? r.unslick(s) : (r.options = i.extend({}, r.originalSettings, r.breakpointSettings[s]), !0 === e && (r.currentSlide = r.options.initialSlide), r.refresh(e)), l = s) : null !== r.activeBreakpoint && (r.activeBreakpoint = null, r.options = r.originalSettings, !0 === e && (r.currentSlide = r.options.initialSlide), r.refresh(e), l = s), e || !1 === l || r.$slider.trigger("breakpoint", [r, l])
+ }
+ }, e.prototype.changeSlide = function (e, t) {
+ var o, s, n, r = this, l = i(e.currentTarget);
+ switch (l.is("a") && e.preventDefault(), l.is("li") || (l = l.closest("li")), n = r.slideCount % r.options.slidesToScroll != 0, o = n ? 0 : (r.slideCount - r.currentSlide) % r.options.slidesToScroll, e.data.message) {
+ case"previous":
+ s = 0 === o ? r.options.slidesToScroll : r.options.slidesToShow - o, r.slideCount > r.options.slidesToShow && r.slideHandler(r.currentSlide - s, !1, t);
+ break;
+ case"next":
+ s = 0 === o ? r.options.slidesToScroll : o, r.slideCount > r.options.slidesToShow && r.slideHandler(r.currentSlide + s, !1, t);
+ break;
+ case"index":
+ var d = 0 === e.data.index ? 0 : e.data.index || l.index() * r.options.slidesToScroll;
+ r.slideHandler(r.checkNavigable(d), !1, t), l.children().trigger("focus");
+ break;
+ default:
+ return
+ }
+ }, e.prototype.checkNavigable = function (i) {
+ var e, t;
+ if (e = this.getNavigableIndexes(), t = 0, i > e[e.length - 1]) i = e[e.length - 1]; else for (var o in e) {
+ if (i < e[o]) {
+ i = t;
+ break
+ }
+ t = e[o]
+ }
+ return i
+ }, e.prototype.cleanUpEvents = function () {
+ var e = this;
+ e.options.dots && null !== e.$dots && (i("li", e.$dots).off("click.slick", e.changeSlide).off("mouseenter.slick", i.proxy(e.interrupt, e, !0)).off("mouseleave.slick", i.proxy(e.interrupt, e, !1)), !0 === e.options.accessibility && e.$dots.off("keydown.slick", e.keyHandler)), e.$slider.off("focus.slick blur.slick"), !0 === e.options.arrows && e.slideCount > e.options.slidesToShow && (e.$prevArrow && e.$prevArrow.off("click.slick", e.changeSlide), e.$nextArrow && e.$nextArrow.off("click.slick", e.changeSlide), !0 === e.options.accessibility && (e.$prevArrow && e.$prevArrow.off("keydown.slick", e.keyHandler), e.$nextArrow && e.$nextArrow.off("keydown.slick", e.keyHandler))), e.$list.off("touchstart.slick mousedown.slick", e.swipeHandler), e.$list.off("touchmove.slick mousemove.slick", e.swipeHandler), e.$list.off("touchend.slick mouseup.slick", e.swipeHandler), e.$list.off("touchcancel.slick mouseleave.slick", e.swipeHandler), e.$list.off("click.slick", e.clickHandler), i(document).off(e.visibilityChange, e.visibility), e.cleanUpSlideEvents(), !0 === e.options.accessibility && e.$list.off("keydown.slick", e.keyHandler), !0 === e.options.focusOnSelect && i(e.$slideTrack).children().off("click.slick", e.selectHandler), i(window).off("orientationchange.slick.slick-" + e.instanceUid, e.orientationChange), i(window).off("resize.slick.slick-" + e.instanceUid, e.resize), i("[draggable!=true]", e.$slideTrack).off("dragstart", e.preventDefault), i(window).off("load.slick.slick-" + e.instanceUid, e.setPosition)
+ }, e.prototype.cleanUpSlideEvents = function () {
+ var e = this;
+ e.$list.off("mouseenter.slick", i.proxy(e.interrupt, e, !0)), e.$list.off("mouseleave.slick", i.proxy(e.interrupt, e, !1))
+ }, e.prototype.cleanUpRows = function () {
+ var i, e = this;
+ e.options.rows > 1 && ((i = e.$slides.children().children()).removeAttr("style"), e.$slider.empty().append(i))
+ }, e.prototype.clickHandler = function (i) {
+ !1 === this.shouldClick && (i.stopImmediatePropagation(), i.stopPropagation(), i.preventDefault())
+ }, e.prototype.destroy = function (e) {
+ var t = this;
+ t.autoPlayClear(), t.touchObject = {}, t.cleanUpEvents(), i(".slick-cloned", t.$slider).detach(), t.$dots && t.$dots.remove(), t.$prevArrow && t.$prevArrow.length && (t.$prevArrow.removeClass("slick-disabled slick-arrow slick-hidden").removeAttr("aria-hidden aria-disabled tabindex").css("display", ""), t.htmlExpr.test(t.options.prevArrow) && t.$prevArrow.remove()), t.$nextArrow && t.$nextArrow.length && (t.$nextArrow.removeClass("slick-disabled slick-arrow slick-hidden").removeAttr("aria-hidden aria-disabled tabindex").css("display", ""), t.htmlExpr.test(t.options.nextArrow) && t.$nextArrow.remove()), t.$slides && (t.$slides.removeClass("slick-slide slick-active slick-center slick-visible slick-current").removeAttr("aria-hidden").removeAttr("data-slick-index").each(function () {
+ i(this).attr("style", i(this).data("originalStyling"))
+ }), t.$slideTrack.children(this.options.slide).detach(), t.$slideTrack.detach(), t.$list.detach(), t.$slider.append(t.$slides)), t.cleanUpRows(), t.$slider.removeClass("slick-slider"), t.$slider.removeClass("slick-initialized"), t.$slider.removeClass("slick-dotted"), t.unslicked = !0, e || t.$slider.trigger("destroy", [t])
+ }, e.prototype.disableTransition = function (i) {
+ var e = this, t = {};
+ t[e.transitionType] = "", !1 === e.options.fade ? e.$slideTrack.css(t) : e.$slides.eq(i).css(t)
+ }, e.prototype.fadeSlide = function (i, e) {
+ var t = this;
+ !1 === t.cssTransitions ? (t.$slides.eq(i).css({zIndex: t.options.zIndex}), t.$slides.eq(i).animate({opacity: 1}, t.options.speed, t.options.easing, e)) : (t.applyTransition(i), t.$slides.eq(i).css({
+ opacity: 1,
+ zIndex: t.options.zIndex
+ }), e && setTimeout(function () {
+ t.disableTransition(i), e.call()
+ }, t.options.speed))
+ }, e.prototype.fadeSlideOut = function (i) {
+ var e = this;
+ !1 === e.cssTransitions ? e.$slides.eq(i).animate({
+ opacity: 0,
+ zIndex: e.options.zIndex - 2
+ }, e.options.speed, e.options.easing) : (e.applyTransition(i), e.$slides.eq(i).css({
+ opacity: 0,
+ zIndex: e.options.zIndex - 2
+ }))
+ }, e.prototype.filterSlides = e.prototype.slickFilter = function (i) {
+ var e = this;
+ null !== i && (e.$slidesCache = e.$slides, e.unload(), e.$slideTrack.children(this.options.slide).detach(), e.$slidesCache.filter(i).appendTo(e.$slideTrack), e.reinit())
+ }, e.prototype.focusHandler = function () {
+ var e = this;
+ e.$slider.off("focus.slick blur.slick").on("focus.slick blur.slick", "*", function (t) {
+ t.stopImmediatePropagation();
+ var o = i(this);
+ setTimeout(function () {
+ e.options.pauseOnFocus && (e.focussed = o.is(":focus"), e.autoPlay())
+ }, 0)
+ })
+ }, e.prototype.getCurrent = e.prototype.slickCurrentSlide = function () {
+ return this.currentSlide
+ }, e.prototype.getDotCount = function () {
+ var i = this, e = 0, t = 0, o = 0;
+ if (!0 === i.options.infinite) if (i.slideCount <= i.options.slidesToShow) ++o; else for (; e < i.slideCount;) ++o, e = t + i.options.slidesToScroll, t += i.options.slidesToScroll <= i.options.slidesToShow ? i.options.slidesToScroll : i.options.slidesToShow; else if (!0 === i.options.centerMode) o = i.slideCount; else if (i.options.asNavFor) for (; e < i.slideCount;) ++o, e = t + i.options.slidesToScroll, t += i.options.slidesToScroll <= i.options.slidesToShow ? i.options.slidesToScroll : i.options.slidesToShow; else o = 1 + Math.ceil((i.slideCount - i.options.slidesToShow) / i.options.slidesToScroll);
+ return o - 1
+ }, e.prototype.getLeft = function (i) {
+ var e, t, o, s, n = this, r = 0;
+ return n.slideOffset = 0, t = n.$slides.first().outerHeight(!0), !0 === n.options.infinite ? (n.slideCount > n.options.slidesToShow && (n.slideOffset = n.slideWidth * n.options.slidesToShow * -1, s = -1, !0 === n.options.vertical && !0 === n.options.centerMode && (2 === n.options.slidesToShow ? s = -1.5 : 1 === n.options.slidesToShow && (s = -2)), r = t * n.options.slidesToShow * s), n.slideCount % n.options.slidesToScroll != 0 && i + n.options.slidesToScroll > n.slideCount && n.slideCount > n.options.slidesToShow && (i > n.slideCount ? (n.slideOffset = (n.options.slidesToShow - (i - n.slideCount)) * n.slideWidth * -1, r = (n.options.slidesToShow - (i - n.slideCount)) * t * -1) : (n.slideOffset = n.slideCount % n.options.slidesToScroll * n.slideWidth * -1, r = n.slideCount % n.options.slidesToScroll * t * -1))) : i + n.options.slidesToShow > n.slideCount && (n.slideOffset = (i + n.options.slidesToShow - n.slideCount) * n.slideWidth, r = (i + n.options.slidesToShow - n.slideCount) * t), n.slideCount <= n.options.slidesToShow && (n.slideOffset = 0, r = 0), !0 === n.options.centerMode && n.slideCount <= n.options.slidesToShow ? n.slideOffset = n.slideWidth * Math.floor(n.options.slidesToShow) / 2 - n.slideWidth * n.slideCount / 2 : !0 === n.options.centerMode && !0 === n.options.infinite ? n.slideOffset += n.slideWidth * Math.floor(n.options.slidesToShow / 2) - n.slideWidth : !0 === n.options.centerMode && (n.slideOffset = 0, n.slideOffset += n.slideWidth * Math.floor(n.options.slidesToShow / 2)), e = !1 === n.options.vertical ? i * n.slideWidth * -1 + n.slideOffset : i * t * -1 + r, !0 === n.options.variableWidth && (o = n.slideCount <= n.options.slidesToShow || !1 === n.options.infinite ? n.$slideTrack.children(".slick-slide").eq(i) : n.$slideTrack.children(".slick-slide").eq(i + n.options.slidesToShow), e = !0 === n.options.rtl ? o[0] ? -1 * (n.$slideTrack.width() - o[0].offsetLeft - o.width()) : 0 : o[0] ? -1 * o[0].offsetLeft : 0, !0 === n.options.centerMode && (o = n.slideCount <= n.options.slidesToShow || !1 === n.options.infinite ? n.$slideTrack.children(".slick-slide").eq(i) : n.$slideTrack.children(".slick-slide").eq(i + n.options.slidesToShow + 1), e = !0 === n.options.rtl ? o[0] ? -1 * (n.$slideTrack.width() - o[0].offsetLeft - o.width()) : 0 : o[0] ? -1 * o[0].offsetLeft : 0, e += (n.$list.width() - o.outerWidth()) / 2)), e
+ }, e.prototype.getOption = e.prototype.slickGetOption = function (i) {
+ return this.options[i]
+ }, e.prototype.getNavigableIndexes = function () {
+ var i, e = this, t = 0, o = 0, s = [];
+ for (!1 === e.options.infinite ? i = e.slideCount : (t = -1 * e.options.slidesToScroll, o = -1 * e.options.slidesToScroll, i = 2 * e.slideCount); t < i;) s.push(t), t = o + e.options.slidesToScroll, o += e.options.slidesToScroll <= e.options.slidesToShow ? e.options.slidesToScroll : e.options.slidesToShow;
+ return s
+ }, e.prototype.getSlick = function () {
+ return this
+ }, e.prototype.getSlideCount = function () {
+ var e, t, o = this;
+ return t = !0 === o.options.centerMode ? o.slideWidth * Math.floor(o.options.slidesToShow / 2) : 0, !0 === o.options.swipeToSlide ? (o.$slideTrack.find(".slick-slide").each(function (s, n) {
+ if (n.offsetLeft - t + i(n).outerWidth() / 2 > -1 * o.swipeLeft) return e = n, !1
+ }), Math.abs(i(e).attr("data-slick-index") - o.currentSlide) || 1) : o.options.slidesToScroll
+ }, e.prototype.goTo = e.prototype.slickGoTo = function (i, e) {
+ this.changeSlide({data: {message: "index", index: parseInt(i)}}, e)
+ }, e.prototype.init = function (e) {
+ var t = this;
+ i(t.$slider).hasClass("slick-initialized") || (i(t.$slider).addClass("slick-initialized"), t.buildRows(), t.buildOut(), t.setProps(), t.startLoad(), t.loadSlider(), t.initializeEvents(), t.updateArrows(), t.updateDots(), t.checkResponsive(!0), t.focusHandler()), e && t.$slider.trigger("init", [t]), !0 === t.options.accessibility && t.initADA(), t.options.autoplay && (t.paused = !1, t.autoPlay())
+ }, e.prototype.initADA = function () {
+ var e = this, t = Math.ceil(e.slideCount / e.options.slidesToShow),
+ o = e.getNavigableIndexes().filter(function (i) {
+ return i >= 0 && i < e.slideCount
+ });
+ e.$slides.add(e.$slideTrack.find(".slick-cloned")).attr({
+ "aria-hidden": "true",
+ tabindex: "-1"
+ }).find("a, input, button, select").attr({tabindex: "-1"}), null !== e.$dots && (e.$slides.not(e.$slideTrack.find(".slick-cloned")).each(function (t) {
+ var s = o.indexOf(t);
+ i(this).attr({
+ role: "tabpanel",
+ id: "slick-slide" + e.instanceUid + t,
+ tabindex: -1
+ }), -1 !== s && i(this).attr({"aria-describedby": "slick-slide-control" + e.instanceUid + s})
+ }), e.$dots.attr("role", "tablist").find("li").each(function (s) {
+ var n = o[s];
+ i(this).attr({role: "presentation"}), i(this).find("button").first().attr({
+ role: "tab",
+ id: "slick-slide-control" + e.instanceUid + s,
+ "aria-controls": "slick-slide" + e.instanceUid + n,
+ "aria-label": s + 1 + " of " + t,
+ "aria-selected": null,
+ tabindex: "-1"
+ })
+ }).eq(e.currentSlide).find("button").attr({"aria-selected": "true", tabindex: "0"}).end());
+ for (var s = e.currentSlide, n = s + e.options.slidesToShow; s < n; s++) e.$slides.eq(s).attr("tabindex", 0);
+ e.activateADA()
+ }, e.prototype.initArrowEvents = function () {
+ var i = this;
+ !0 === i.options.arrows && i.slideCount > i.options.slidesToShow && (i.$prevArrow.off("click.slick").on("click.slick", {message: "previous"}, i.changeSlide), i.$nextArrow.off("click.slick").on("click.slick", {message: "next"}, i.changeSlide), !0 === i.options.accessibility && (i.$prevArrow.on("keydown.slick", i.keyHandler), i.$nextArrow.on("keydown.slick", i.keyHandler)))
+ }, e.prototype.initDotEvents = function () {
+ var e = this;
+ !0 === e.options.dots && (i("li", e.$dots).on("click.slick", {message: "index"}, e.changeSlide), !0 === e.options.accessibility && e.$dots.on("keydown.slick", e.keyHandler)), !0 === e.options.dots && !0 === e.options.pauseOnDotsHover && i("li", e.$dots).on("mouseenter.slick", i.proxy(e.interrupt, e, !0)).on("mouseleave.slick", i.proxy(e.interrupt, e, !1))
+ }, e.prototype.initSlideEvents = function () {
+ var e = this;
+ e.options.pauseOnHover && (e.$list.on("mouseenter.slick", i.proxy(e.interrupt, e, !0)), e.$list.on("mouseleave.slick", i.proxy(e.interrupt, e, !1)))
+ }, e.prototype.initializeEvents = function () {
+ var e = this;
+ e.initArrowEvents(), e.initDotEvents(), e.initSlideEvents(), e.$list.on("touchstart.slick mousedown.slick", {action: "start"}, e.swipeHandler), e.$list.on("touchmove.slick mousemove.slick", {action: "move"}, e.swipeHandler), e.$list.on("touchend.slick mouseup.slick", {action: "end"}, e.swipeHandler), e.$list.on("touchcancel.slick mouseleave.slick", {action: "end"}, e.swipeHandler), e.$list.on("click.slick", e.clickHandler), i(document).on(e.visibilityChange, i.proxy(e.visibility, e)), !0 === e.options.accessibility && e.$list.on("keydown.slick", e.keyHandler), !0 === e.options.focusOnSelect && i(e.$slideTrack).children().on("click.slick", e.selectHandler), i(window).on("orientationchange.slick.slick-" + e.instanceUid, i.proxy(e.orientationChange, e)), i(window).on("resize.slick.slick-" + e.instanceUid, i.proxy(e.resize, e)), i("[draggable!=true]", e.$slideTrack).on("dragstart", e.preventDefault), i(window).on("load.slick.slick-" + e.instanceUid, e.setPosition), i(e.setPosition)
+ }, e.prototype.initUI = function () {
+ var i = this;
+ !0 === i.options.arrows && i.slideCount > i.options.slidesToShow && (i.$prevArrow.show(), i.$nextArrow.show()), !0 === i.options.dots && i.slideCount > i.options.slidesToShow && i.$dots.show()
+ }, e.prototype.keyHandler = function (i) {
+ var e = this;
+ i.target.tagName.match("TEXTAREA|INPUT|SELECT") || (37 === i.keyCode && !0 === e.options.accessibility ? e.changeSlide({data: {message: !0 === e.options.rtl ? "next" : "previous"}}) : 39 === i.keyCode && !0 === e.options.accessibility && e.changeSlide({data: {message: !0 === e.options.rtl ? "previous" : "next"}}))
+ }, e.prototype.lazyLoad = function () {
+ function e(e) {
+ i("img[data-lazy]", e).each(function () {
+ var e = i(this), t = i(this).attr("data-lazy"), o = i(this).attr("data-srcset"),
+ s = i(this).attr("data-sizes") || n.$slider.attr("data-sizes"), r = document.createElement("img");
+ r.onload = function () {
+ e.animate({opacity: 0}, 100, function () {
+ o && (e.attr("srcset", o), s && e.attr("sizes", s)), e.attr("src", t).animate({opacity: 1}, 200, function () {
+ e.removeAttr("data-lazy data-srcset data-sizes").removeClass("slick-loading")
+ }), n.$slider.trigger("lazyLoaded", [n, e, t])
+ })
+ }, r.onerror = function () {
+ e.removeAttr("data-lazy").removeClass("slick-loading").addClass("slick-lazyload-error"), n.$slider.trigger("lazyLoadError", [n, e, t])
+ }, r.src = t
+ })
+ }
+
+ var t, o, s, n = this;
+ if (!0 === n.options.centerMode ? !0 === n.options.infinite ? s = (o = n.currentSlide + (n.options.slidesToShow / 2 + 1)) + n.options.slidesToShow + 2 : (o = Math.max(0, n.currentSlide - (n.options.slidesToShow / 2 + 1)), s = n.options.slidesToShow / 2 + 1 + 2 + n.currentSlide) : (o = n.options.infinite ? n.options.slidesToShow + n.currentSlide : n.currentSlide, s = Math.ceil(o + n.options.slidesToShow), !0 === n.options.fade && (o > 0 && o--, s <= n.slideCount && s++)), t = n.$slider.find(".slick-slide").slice(o, s), "anticipated" === n.options.lazyLoad) for (var r = o - 1, l = s, d = n.$slider.find(".slick-slide"), a = 0; a < n.options.slidesToScroll; a++) r < 0 && (r = n.slideCount - 1), t = (t = t.add(d.eq(r))).add(d.eq(l)), r--, l++;
+ e(t), n.slideCount <= n.options.slidesToShow ? e(n.$slider.find(".slick-slide")) : n.currentSlide >= n.slideCount - n.options.slidesToShow ? e(n.$slider.find(".slick-cloned").slice(0, n.options.slidesToShow)) : 0 === n.currentSlide && e(n.$slider.find(".slick-cloned").slice(-1 * n.options.slidesToShow))
+ }, e.prototype.loadSlider = function () {
+ var i = this;
+ i.setPosition(), i.$slideTrack.css({opacity: 1}), i.$slider.removeClass("slick-loading"), i.initUI(), "progressive" === i.options.lazyLoad && i.progressiveLazyLoad()
+ }, e.prototype.next = e.prototype.slickNext = function () {
+ this.changeSlide({data: {message: "next"}})
+ }, e.prototype.orientationChange = function () {
+ var i = this;
+ i.checkResponsive(), i.setPosition()
+ }, e.prototype.pause = e.prototype.slickPause = function () {
+ var i = this;
+ i.autoPlayClear(), i.paused = !0
+ }, e.prototype.play = e.prototype.slickPlay = function () {
+ var i = this;
+ i.autoPlay(), i.options.autoplay = !0, i.paused = !1, i.focussed = !1, i.interrupted = !1
+ }, e.prototype.postSlide = function (e) {
+ var t = this;
+ t.unslicked || (t.$slider.trigger("afterChange", [t, e]), t.animating = !1, t.slideCount > t.options.slidesToShow && t.setPosition(), t.swipeLeft = null, t.options.autoplay && t.autoPlay(), !0 === t.options.accessibility && (t.initADA(), t.options.focusOnChange && i(t.$slides.get(t.currentSlide)).attr("tabindex", 0).focus()))
+ }, e.prototype.prev = e.prototype.slickPrev = function () {
+ this.changeSlide({data: {message: "previous"}})
+ }, e.prototype.preventDefault = function (i) {
+ i.preventDefault()
+ }, e.prototype.progressiveLazyLoad = function (e) {
+ e = e || 1;
+ var t, o, s, n, r, l = this, d = i("img[data-lazy]", l.$slider);
+ d.length ? (t = d.first(), o = t.attr("data-lazy"), s = t.attr("data-srcset"), n = t.attr("data-sizes") || l.$slider.attr("data-sizes"), (r = document.createElement("img")).onload = function () {
+ s && (t.attr("srcset", s), n && t.attr("sizes", n)), t.attr("src", o).removeAttr("data-lazy data-srcset data-sizes").removeClass("slick-loading"), !0 === l.options.adaptiveHeight && l.setPosition(), l.$slider.trigger("lazyLoaded", [l, t, o]), l.progressiveLazyLoad()
+ }, r.onerror = function () {
+ e < 3 ? setTimeout(function () {
+ l.progressiveLazyLoad(e + 1)
+ }, 500) : (t.removeAttr("data-lazy").removeClass("slick-loading").addClass("slick-lazyload-error"), l.$slider.trigger("lazyLoadError", [l, t, o]), l.progressiveLazyLoad())
+ }, r.src = o) : l.$slider.trigger("allImagesLoaded", [l])
+ }, e.prototype.refresh = function (e) {
+ var t, o, s = this;
+ o = s.slideCount - s.options.slidesToShow, !s.options.infinite && s.currentSlide > o && (s.currentSlide = o), s.slideCount <= s.options.slidesToShow && (s.currentSlide = 0), t = s.currentSlide, s.destroy(!0), i.extend(s, s.initials, {currentSlide: t}), s.init(), e || s.changeSlide({
+ data: {
+ message: "index",
+ index: t
+ }
+ }, !1)
+ }, e.prototype.registerBreakpoints = function () {
+ var e, t, o, s = this, n = s.options.responsive || null;
+ if ("array" === i.type(n) && n.length) {
+ s.respondTo = s.options.respondTo || "window";
+ for (e in n) if (o = s.breakpoints.length - 1, n.hasOwnProperty(e)) {
+ for (t = n[e].breakpoint; o >= 0;) s.breakpoints[o] && s.breakpoints[o] === t && s.breakpoints.splice(o, 1), o--;
+ s.breakpoints.push(t), s.breakpointSettings[t] = n[e].settings
+ }
+ s.breakpoints.sort(function (i, e) {
+ return s.options.mobileFirst ? i - e : e - i
+ })
+ }
+ }, e.prototype.reinit = function () {
+ var e = this;
+ e.$slides = e.$slideTrack.children(e.options.slide).addClass("slick-slide"), e.slideCount = e.$slides.length, e.currentSlide >= e.slideCount && 0 !== e.currentSlide && (e.currentSlide = e.currentSlide - e.options.slidesToScroll), e.slideCount <= e.options.slidesToShow && (e.currentSlide = 0), e.registerBreakpoints(), e.setProps(), e.setupInfinite(), e.buildArrows(), e.updateArrows(), e.initArrowEvents(), e.buildDots(), e.updateDots(), e.initDotEvents(), e.cleanUpSlideEvents(), e.initSlideEvents(), e.checkResponsive(!1, !0), !0 === e.options.focusOnSelect && i(e.$slideTrack).children().on("click.slick", e.selectHandler), e.setSlideClasses("number" == typeof e.currentSlide ? e.currentSlide : 0), e.setPosition(), e.focusHandler(), e.paused = !e.options.autoplay, e.autoPlay(), e.$slider.trigger("reInit", [e])
+ }, e.prototype.resize = function () {
+ var e = this;
+ i(window).width() !== e.windowWidth && (clearTimeout(e.windowDelay), e.windowDelay = window.setTimeout(function () {
+ e.windowWidth = i(window).width(), e.checkResponsive(), e.unslicked || e.setPosition()
+ }, 50))
+ }, e.prototype.removeSlide = e.prototype.slickRemove = function (i, e, t) {
+ var o = this;
+ if (i = "boolean" == typeof i ? !0 === (e = i) ? 0 : o.slideCount - 1 : !0 === e ? --i : i, o.slideCount < 1 || i < 0 || i > o.slideCount - 1) return !1;
+ o.unload(), !0 === t ? o.$slideTrack.children().remove() : o.$slideTrack.children(this.options.slide).eq(i).remove(), o.$slides = o.$slideTrack.children(this.options.slide), o.$slideTrack.children(this.options.slide).detach(), o.$slideTrack.append(o.$slides), o.$slidesCache = o.$slides, o.reinit()
+ }, e.prototype.setCSS = function (i) {
+ var e, t, o = this, s = {};
+ !0 === o.options.rtl && (i = -i), e = "left" == o.positionProp ? Math.ceil(i) + "px" : "0px", t = "top" == o.positionProp ? Math.ceil(i) + "px" : "0px", s[o.positionProp] = i, !1 === o.transformsEnabled ? o.$slideTrack.css(s) : (s = {}, !1 === o.cssTransitions ? (s[o.animType] = "translate(" + e + ", " + t + ")", o.$slideTrack.css(s)) : (s[o.animType] = "translate3d(" + e + ", " + t + ", 0px)", o.$slideTrack.css(s)))
+ }, e.prototype.setDimensions = function () {
+ var i = this;
+ !1 === i.options.vertical ? !0 === i.options.centerMode && i.$list.css({padding: "0px " + i.options.centerPadding}) : (i.$list.height(i.$slides.first().outerHeight(!0) * i.options.slidesToShow), !0 === i.options.centerMode && i.$list.css({padding: i.options.centerPadding + " 0px"})), i.listWidth = i.$list.width(), i.listHeight = i.$list.height(), !1 === i.options.vertical && !1 === i.options.variableWidth ? (i.slideWidth = Math.ceil(i.listWidth / i.options.slidesToShow), i.$slideTrack.width(Math.ceil(i.slideWidth * i.$slideTrack.children(".slick-slide").length))) : !0 === i.options.variableWidth ? i.$slideTrack.width(5e3 * i.slideCount) : (i.slideWidth = Math.ceil(i.listWidth), i.$slideTrack.height(Math.ceil(i.$slides.first().outerHeight(!0) * i.$slideTrack.children(".slick-slide").length)));
+ var e = i.$slides.first().outerWidth(!0) - i.$slides.first().width();
+ !1 === i.options.variableWidth && i.$slideTrack.children(".slick-slide").width(i.slideWidth - e)
+ }, e.prototype.setFade = function () {
+ var e, t = this;
+ t.$slides.each(function (o, s) {
+ e = t.slideWidth * o * -1, !0 === t.options.rtl ? i(s).css({
+ position: "relative",
+ right: e,
+ top: 0,
+ zIndex: t.options.zIndex - 2,
+ opacity: 0
+ }) : i(s).css({position: "relative", left: e, top: 0, zIndex: t.options.zIndex - 2, opacity: 0})
+ }), t.$slides.eq(t.currentSlide).css({zIndex: t.options.zIndex - 1, opacity: 1})
+ }, e.prototype.setHeight = function () {
+ var i = this;
+ if (1 === i.options.slidesToShow && !0 === i.options.adaptiveHeight && !1 === i.options.vertical) {
+ var e = i.$slides.eq(i.currentSlide).outerHeight(!0);
+ i.$list.css("height", e)
+ }
+ }, e.prototype.setOption = e.prototype.slickSetOption = function () {
+ var e, t, o, s, n, r = this, l = !1;
+ if ("object" === i.type(arguments[0]) ? (o = arguments[0], l = arguments[1], n = "multiple") : "string" === i.type(arguments[0]) && (o = arguments[0], s = arguments[1], l = arguments[2], "responsive" === arguments[0] && "array" === i.type(arguments[1]) ? n = "responsive" : void 0 !== arguments[1] && (n = "single")), "single" === n) r.options[o] = s; else if ("multiple" === n) i.each(o, function (i, e) {
+ r.options[i] = e
+ }); else if ("responsive" === n) for (t in s) if ("array" !== i.type(r.options.responsive)) r.options.responsive = [s[t]]; else {
+ for (e = r.options.responsive.length - 1; e >= 0;) r.options.responsive[e].breakpoint === s[t].breakpoint && r.options.responsive.splice(e, 1), e--;
+ r.options.responsive.push(s[t])
+ }
+ l && (r.unload(), r.reinit())
+ }, e.prototype.setPosition = function () {
+ var i = this;
+ i.setDimensions(), i.setHeight(), !1 === i.options.fade ? i.setCSS(i.getLeft(i.currentSlide)) : i.setFade(), i.$slider.trigger("setPosition", [i])
+ }, e.prototype.setProps = function () {
+ var i = this, e = document.body.style;
+ i.positionProp = !0 === i.options.vertical ? "top" : "left", "top" === i.positionProp ? i.$slider.addClass("slick-vertical") : i.$slider.removeClass("slick-vertical"), void 0 === e.WebkitTransition && void 0 === e.MozTransition && void 0 === e.msTransition || !0 === i.options.useCSS && (i.cssTransitions = !0), i.options.fade && ("number" == typeof i.options.zIndex ? i.options.zIndex < 3 && (i.options.zIndex = 3) : i.options.zIndex = i.defaults.zIndex), void 0 !== e.OTransform && (i.animType = "OTransform", i.transformType = "-o-transform", i.transitionType = "OTransition", void 0 === e.perspectiveProperty && void 0 === e.webkitPerspective && (i.animType = !1)), void 0 !== e.MozTransform && (i.animType = "MozTransform", i.transformType = "-moz-transform", i.transitionType = "MozTransition", void 0 === e.perspectiveProperty && void 0 === e.MozPerspective && (i.animType = !1)), void 0 !== e.webkitTransform && (i.animType = "webkitTransform", i.transformType = "-webkit-transform", i.transitionType = "webkitTransition", void 0 === e.perspectiveProperty && void 0 === e.webkitPerspective && (i.animType = !1)), void 0 !== e.msTransform && (i.animType = "msTransform", i.transformType = "-ms-transform", i.transitionType = "msTransition", void 0 === e.msTransform && (i.animType = !1)), void 0 !== e.transform && !1 !== i.animType && (i.animType = "transform", i.transformType = "transform", i.transitionType = "transition"), i.transformsEnabled = i.options.useTransform && null !== i.animType && !1 !== i.animType
+ }, e.prototype.setSlideClasses = function (i) {
+ var e, t, o, s, n = this;
+ if (t = n.$slider.find(".slick-slide").removeClass("slick-active slick-center slick-current").attr("aria-hidden", "true"), n.$slides.eq(i).addClass("slick-current"), !0 === n.options.centerMode) {
+ var r = n.options.slidesToShow % 2 == 0 ? 1 : 0;
+ e = Math.floor(n.options.slidesToShow / 2), !0 === n.options.infinite && (i >= e && i <= n.slideCount - 1 - e ? n.$slides.slice(i - e + r, i + e + 1).addClass("slick-active").attr("aria-hidden", "false") : (o = n.options.slidesToShow + i, t.slice(o - e + 1 + r, o + e + 2).addClass("slick-active").attr("aria-hidden", "false")), 0 === i ? t.eq(t.length - 1 - n.options.slidesToShow).addClass("slick-center") : i === n.slideCount - 1 && t.eq(n.options.slidesToShow).addClass("slick-center")), n.$slides.eq(i).addClass("slick-center")
+ } else i >= 0 && i <= n.slideCount - n.options.slidesToShow ? n.$slides.slice(i, i + n.options.slidesToShow).addClass("slick-active").attr("aria-hidden", "false") : t.length <= n.options.slidesToShow ? t.addClass("slick-active").attr("aria-hidden", "false") : (s = n.slideCount % n.options.slidesToShow, o = !0 === n.options.infinite ? n.options.slidesToShow + i : i, n.options.slidesToShow == n.options.slidesToScroll && n.slideCount - i < n.options.slidesToShow ? t.slice(o - (n.options.slidesToShow - s), o + s).addClass("slick-active").attr("aria-hidden", "false") : t.slice(o, o + n.options.slidesToShow).addClass("slick-active").attr("aria-hidden", "false"));
+ "ondemand" !== n.options.lazyLoad && "anticipated" !== n.options.lazyLoad || n.lazyLoad()
+ }, e.prototype.setupInfinite = function () {
+ var e, t, o, s = this;
+ if (!0 === s.options.fade && (s.options.centerMode = !1), !0 === s.options.infinite && !1 === s.options.fade && (t = null, s.slideCount > s.options.slidesToShow)) {
+ for (o = !0 === s.options.centerMode ? s.options.slidesToShow + 1 : s.options.slidesToShow, e = s.slideCount; e > s.slideCount - o; e -= 1) t = e - 1, i(s.$slides[t]).clone(!0).attr("id", "").attr("data-slick-index", t - s.slideCount).prependTo(s.$slideTrack).addClass("slick-cloned");
+ for (e = 0; e < o + s.slideCount; e += 1) t = e, i(s.$slides[t]).clone(!0).attr("id", "").attr("data-slick-index", t + s.slideCount).appendTo(s.$slideTrack).addClass("slick-cloned");
+ s.$slideTrack.find(".slick-cloned").find("[id]").each(function () {
+ i(this).attr("id", "")
+ })
+ }
+ }, e.prototype.interrupt = function (i) {
+ var e = this;
+ i || e.autoPlay(), e.interrupted = i
+ }, e.prototype.selectHandler = function (e) {
+ var t = this, o = i(e.target).is(".slick-slide") ? i(e.target) : i(e.target).parents(".slick-slide"),
+ s = parseInt(o.attr("data-slick-index"));
+ s || (s = 0), t.slideCount <= t.options.slidesToShow ? t.slideHandler(s, !1, !0) : t.slideHandler(s)
+ }, e.prototype.slideHandler = function (i, e, t) {
+ var o, s, n, r, l, d = null, a = this;
+ if (e = e || !1, !(!0 === a.animating && !0 === a.options.waitForAnimate || !0 === a.options.fade && a.currentSlide === i)) if (!1 === e && a.asNavFor(i), o = i, d = a.getLeft(o), r = a.getLeft(a.currentSlide), a.currentLeft = null === a.swipeLeft ? r : a.swipeLeft, !1 === a.options.infinite && !1 === a.options.centerMode && (i < 0 || i > a.getDotCount() * a.options.slidesToScroll)) !1 === a.options.fade && (o = a.currentSlide, !0 !== t ? a.animateSlide(r, function () {
+ a.postSlide(o)
+ }) : a.postSlide(o)); else if (!1 === a.options.infinite && !0 === a.options.centerMode && (i < 0 || i > a.slideCount - a.options.slidesToScroll)) !1 === a.options.fade && (o = a.currentSlide, !0 !== t ? a.animateSlide(r, function () {
+ a.postSlide(o)
+ }) : a.postSlide(o)); else {
+ if (a.options.autoplay && clearInterval(a.autoPlayTimer), s = o < 0 ? a.slideCount % a.options.slidesToScroll != 0 ? a.slideCount - a.slideCount % a.options.slidesToScroll : a.slideCount + o : o >= a.slideCount ? a.slideCount % a.options.slidesToScroll != 0 ? 0 : o - a.slideCount : o, a.animating = !0, a.$slider.trigger("beforeChange", [a, a.currentSlide, s]), n = a.currentSlide, a.currentSlide = s, a.setSlideClasses(a.currentSlide), a.options.asNavFor && (l = (l = a.getNavTarget()).slick("getSlick")).slideCount <= l.options.slidesToShow && l.setSlideClasses(a.currentSlide), a.updateDots(), a.updateArrows(), !0 === a.options.fade) return !0 !== t ? (a.fadeSlideOut(n), a.fadeSlide(s, function () {
+ a.postSlide(s)
+ })) : a.postSlide(s), void a.animateHeight();
+ !0 !== t ? a.animateSlide(d, function () {
+ a.postSlide(s)
+ }) : a.postSlide(s)
+ }
+ }, e.prototype.startLoad = function () {
+ var i = this;
+ !0 === i.options.arrows && i.slideCount > i.options.slidesToShow && (i.$prevArrow.hide(), i.$nextArrow.hide()), !0 === i.options.dots && i.slideCount > i.options.slidesToShow && i.$dots.hide(), i.$slider.addClass("slick-loading")
+ }, e.prototype.swipeDirection = function () {
+ var i, e, t, o, s = this;
+ return i = s.touchObject.startX - s.touchObject.curX, e = s.touchObject.startY - s.touchObject.curY, t = Math.atan2(e, i), (o = Math.round(180 * t / Math.PI)) < 0 && (o = 360 - Math.abs(o)), o <= 45 && o >= 0 ? !1 === s.options.rtl ? "left" : "right" : o <= 360 && o >= 315 ? !1 === s.options.rtl ? "left" : "right" : o >= 135 && o <= 225 ? !1 === s.options.rtl ? "right" : "left" : !0 === s.options.verticalSwiping ? o >= 35 && o <= 135 ? "down" : "up" : "vertical"
+ }, e.prototype.swipeEnd = function (i) {
+ var e, t, o = this;
+ if (o.dragging = !1, o.swiping = !1, o.scrolling) return o.scrolling = !1, !1;
+ if (o.interrupted = !1, o.shouldClick = !(o.touchObject.swipeLength > 10), void 0 === o.touchObject.curX) return !1;
+ if (!0 === o.touchObject.edgeHit && o.$slider.trigger("edge", [o, o.swipeDirection()]), o.touchObject.swipeLength >= o.touchObject.minSwipe) {
+ switch (t = o.swipeDirection()) {
+ case"left":
+ case"down":
+ e = o.options.swipeToSlide ? o.checkNavigable(o.currentSlide + o.getSlideCount()) : o.currentSlide + o.getSlideCount(), o.currentDirection = 0;
+ break;
+ case"right":
+ case"up":
+ e = o.options.swipeToSlide ? o.checkNavigable(o.currentSlide - o.getSlideCount()) : o.currentSlide - o.getSlideCount(), o.currentDirection = 1
+ }
+ "vertical" != t && (o.slideHandler(e), o.touchObject = {}, o.$slider.trigger("swipe", [o, t]))
+ } else o.touchObject.startX !== o.touchObject.curX && (o.slideHandler(o.currentSlide), o.touchObject = {})
+ }, e.prototype.swipeHandler = function (i) {
+ var e = this;
+ if (!(!1 === e.options.swipe || "ontouchend" in document && !1 === e.options.swipe || !1 === e.options.draggable && -1 !== i.type.indexOf("mouse"))) switch (e.touchObject.fingerCount = i.originalEvent && void 0 !== i.originalEvent.touches ? i.originalEvent.touches.length : 1, e.touchObject.minSwipe = e.listWidth / e.options.touchThreshold, !0 === e.options.verticalSwiping && (e.touchObject.minSwipe = e.listHeight / e.options.touchThreshold), i.data.action) {
+ case"start":
+ e.swipeStart(i);
+ break;
+ case"move":
+ e.swipeMove(i);
+ break;
+ case"end":
+ e.swipeEnd(i)
+ }
+ }, e.prototype.swipeMove = function (i) {
+ var e, t, o, s, n, r, l = this;
+ return n = void 0 !== i.originalEvent ? i.originalEvent.touches : null, !(!l.dragging || l.scrolling || n && 1 !== n.length) && (e = l.getLeft(l.currentSlide), l.touchObject.curX = void 0 !== n ? n[0].pageX : i.clientX, l.touchObject.curY = void 0 !== n ? n[0].pageY : i.clientY, l.touchObject.swipeLength = Math.round(Math.sqrt(Math.pow(l.touchObject.curX - l.touchObject.startX, 2))), r = Math.round(Math.sqrt(Math.pow(l.touchObject.curY - l.touchObject.startY, 2))), !l.options.verticalSwiping && !l.swiping && r > 4 ? (l.scrolling = !0, !1) : (!0 === l.options.verticalSwiping && (l.touchObject.swipeLength = r), t = l.swipeDirection(), void 0 !== i.originalEvent && l.touchObject.swipeLength > 4 && (l.swiping = !0, i.preventDefault()), s = (!1 === l.options.rtl ? 1 : -1) * (l.touchObject.curX > l.touchObject.startX ? 1 : -1), !0 === l.options.verticalSwiping && (s = l.touchObject.curY > l.touchObject.startY ? 1 : -1), o = l.touchObject.swipeLength, l.touchObject.edgeHit = !1, !1 === l.options.infinite && (0 === l.currentSlide && "right" === t || l.currentSlide >= l.getDotCount() && "left" === t) && (o = l.touchObject.swipeLength * l.options.edgeFriction, l.touchObject.edgeHit = !0), !1 === l.options.vertical ? l.swipeLeft = e + o * s : l.swipeLeft = e + o * (l.$list.height() / l.listWidth) * s, !0 === l.options.verticalSwiping && (l.swipeLeft = e + o * s), !0 !== l.options.fade && !1 !== l.options.touchMove && (!0 === l.animating ? (l.swipeLeft = null, !1) : void l.setCSS(l.swipeLeft))))
+ }, e.prototype.swipeStart = function (i) {
+ var e, t = this;
+ if (t.interrupted = !0, 1 !== t.touchObject.fingerCount || t.slideCount <= t.options.slidesToShow) return t.touchObject = {}, !1;
+ void 0 !== i.originalEvent && void 0 !== i.originalEvent.touches && (e = i.originalEvent.touches[0]), t.touchObject.startX = t.touchObject.curX = void 0 !== e ? e.pageX : i.clientX, t.touchObject.startY = t.touchObject.curY = void 0 !== e ? e.pageY : i.clientY, t.dragging = !0
+ }, e.prototype.unfilterSlides = e.prototype.slickUnfilter = function () {
+ var i = this;
+ null !== i.$slidesCache && (i.unload(), i.$slideTrack.children(this.options.slide).detach(), i.$slidesCache.appendTo(i.$slideTrack), i.reinit())
+ }, e.prototype.unload = function () {
+ var e = this;
+ i(".slick-cloned", e.$slider).remove(), e.$dots && e.$dots.remove(), e.$prevArrow && e.htmlExpr.test(e.options.prevArrow) && e.$prevArrow.remove(), e.$nextArrow && e.htmlExpr.test(e.options.nextArrow) && e.$nextArrow.remove(), e.$slides.removeClass("slick-slide slick-active slick-visible slick-current").attr("aria-hidden", "true").css("width", "")
+ }, e.prototype.unslick = function (i) {
+ var e = this;
+ e.$slider.trigger("unslick", [e, i]), e.destroy()
+ }, e.prototype.updateArrows = function () {
+ var i = this;
+ Math.floor(i.options.slidesToShow / 2), !0 === i.options.arrows && i.slideCount > i.options.slidesToShow && !i.options.infinite && (i.$prevArrow.removeClass("slick-disabled").attr("aria-disabled", "false"), i.$nextArrow.removeClass("slick-disabled").attr("aria-disabled", "false"), 0 === i.currentSlide ? (i.$prevArrow.addClass("slick-disabled").attr("aria-disabled", "true"), i.$nextArrow.removeClass("slick-disabled").attr("aria-disabled", "false")) : i.currentSlide >= i.slideCount - i.options.slidesToShow && !1 === i.options.centerMode ? (i.$nextArrow.addClass("slick-disabled").attr("aria-disabled", "true"), i.$prevArrow.removeClass("slick-disabled").attr("aria-disabled", "false")) : i.currentSlide >= i.slideCount - 1 && !0 === i.options.centerMode && (i.$nextArrow.addClass("slick-disabled").attr("aria-disabled", "true"), i.$prevArrow.removeClass("slick-disabled").attr("aria-disabled", "false")))
+ }, e.prototype.updateDots = function () {
+ var i = this;
+ null !== i.$dots && (i.$dots.find("li").removeClass("slick-active").end(), i.$dots.find("li").eq(Math.floor(i.currentSlide / i.options.slidesToScroll)).addClass("slick-active"))
+ }, e.prototype.visibility = function () {
+ var i = this;
+ i.options.autoplay && (document[i.hidden] ? i.interrupted = !0 : i.interrupted = !1)
+ }, i.fn.slick = function () {
+ var i, t, o = this, s = arguments[0], n = Array.prototype.slice.call(arguments, 1), r = o.length;
+ for (i = 0; i < r; i++) if ("object" == typeof s || void 0 === s ? o[i].slick = new e(o[i], s) : t = o[i].slick[s].apply(o[i].slick, n), void 0 !== t) return t;
+ return o
+ }
+});
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/languages/ExcelFile.xls b/main/app/sprinkles/core/assets/SiteAssets/languages/ExcelFile.xls
new file mode 100644
index 0000000..ffc5552
Binary files /dev/null and b/main/app/sprinkles/core/assets/SiteAssets/languages/ExcelFile.xls differ
diff --git a/main/app/sprinkles/core/assets/SiteAssets/languages/json/Translations.json b/main/app/sprinkles/core/assets/SiteAssets/languages/json/Translations.json
new file mode 100644
index 0000000..3406ffb
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/languages/json/Translations.json
@@ -0,0 +1,50 @@
+{
+ "en": {
+ "Translator": "Marvin Borner",
+ "you": "you",
+ "Feed": "New",
+ "Explore": "Explore",
+ "Chat": "Chat",
+ "Friends": "Friends",
+ "Personal": "Personal",
+ "joined the group": "joined the group",
+ "You joined the group": "You joined the group",
+ "has disconnected from the server": "has disconnected from the server"
+ },
+ "de": {
+ "Translator": "Marvin Borner",
+ "you": "du",
+ "Feed": "Neues",
+ "Explore": "Entdecken",
+ "Chat": "Nachrichten",
+ "Friends": "Freunde",
+ "Personal": "Persönliche Daten",
+ "joined the group": "ist der Gruppe beigetreten",
+ "You joined the group": "Du bist der Gruppe beigetreten",
+ "has disconnected from the server": "hat sich vom Server getrennt"
+ },
+ "fr": {
+ "Translator": "Marvin Borner (non-french)",
+ "you": "vous",
+ "Feed": "D'actualités",
+ "Explore": "Dépister",
+ "Chat": "Message",
+ "Friends": "Camarades",
+ "Personal": "Personnelles",
+ "joined the group": "s'est joint au groupe",
+ "You joined the group": "Vous avez rejoint le groupe",
+ "has disconnected from the server": "s'est déconnecté du serveur."
+ },
+ "kl": {
+ "Translator": "Marvin Borner (probably needs corrections)",
+ "you": "SoH",
+ "Feed": "De'",
+ "Explore": "Tu'",
+ "Chat": "Qin",
+ "Friends": "Jup",
+ "Personal": "Ghot",
+ "joined the group": "ghom muv",
+ "You joined the group": "Ghom muv SoH",
+ "has disconnected from the server": "disconnected vo' jabwI'"
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/languages/json/de.json b/main/app/sprinkles/core/assets/SiteAssets/languages/json/de.json
new file mode 100644
index 0000000..74d522a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/languages/json/de.json
@@ -0,0 +1,14 @@
+{
+ "de": {
+ "Translator": "Marvin Borner",
+ "you": "du",
+ "Feed": "Neues",
+ "Explore": "Entdecken",
+ "Chat": "Nachrichten",
+ "Friends": "Freunde",
+ "Personal": "Persönliche Daten",
+ "joined the group": "ist der Gruppe beigetreten",
+ "You joined the group": "Du bist der Gruppe beigetreten",
+ "has disconnected from the server": "hat sich vom Server getrennt"
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/languages/json/en.json b/main/app/sprinkles/core/assets/SiteAssets/languages/json/en.json
new file mode 100644
index 0000000..981bb83
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/languages/json/en.json
@@ -0,0 +1,14 @@
+{
+ "en": {
+ "Translator": "Marvin Borner",
+ "you": "you",
+ "Feed": "New",
+ "Explore": "Explore",
+ "Chat": "Chat",
+ "Friends": "Friends",
+ "Personal": "Personal",
+ "joined the group": "joined the group",
+ "You joined the group": "You joined the group",
+ "has disconnected from the server": "has disconnected from the server"
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/languages/json/fr.json b/main/app/sprinkles/core/assets/SiteAssets/languages/json/fr.json
new file mode 100644
index 0000000..5c59619
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/languages/json/fr.json
@@ -0,0 +1,14 @@
+{
+ "fr": {
+ "Translator": "Marvin Borner (non-french)",
+ "you": "vous",
+ "Feed": "D'actualités",
+ "Explore": "Dépister",
+ "Chat": "Message",
+ "Friends": "Camarades",
+ "Personal": "Personnelles",
+ "joined the group": "s'est joint au groupe",
+ "You joined the group": "Vous avez rejoint le groupe",
+ "has disconnected from the server": "s'est déconnecté du serveur."
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/languages/json/kl.json b/main/app/sprinkles/core/assets/SiteAssets/languages/json/kl.json
new file mode 100644
index 0000000..b21768e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/languages/json/kl.json
@@ -0,0 +1,14 @@
+{
+ "kl": {
+ "Translator": "Marvin Borner (probably needs corrections)",
+ "you": "SoH",
+ "Feed": "De'",
+ "Explore": "Tu'",
+ "Chat": "Qin",
+ "Friends": "Jup",
+ "Personal": "Ghot",
+ "joined the group": "ghom muv",
+ "You joined the group": "Ghom muv SoH",
+ "has disconnected from the server": "disconnected vo' jabwI'"
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/Chatserver/bin/WebChatServer.php b/main/app/sprinkles/core/assets/SiteAssets/php/Chatserver/bin/WebChatServer.php
new file mode 100644
index 0000000..15f573b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/Chatserver/bin/WebChatServer.php
@@ -0,0 +1,18 @@
+run();
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/Chatserver/src/ChatProcessor.php b/main/app/sprinkles/core/assets/SiteAssets/php/Chatserver/src/ChatProcessor.php
new file mode 100644
index 0000000..da78c9b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/Chatserver/src/ChatProcessor.php
@@ -0,0 +1,118 @@
+clients = new \SplObjectStorage;
+ $this->subscriptions = [];
+ $this->users = [];
+ $this->connectedUsersNames = [];
+ }
+
+ public function onOpen(ConnectionInterface $conn) {
+ $generator = new Alliteration();
+ $this->clients->attach($conn);
+ $this->users[$conn->resourceId] = $conn;
+ $this->connectedUsersNames[$conn->resourceId] = $generator->getName();
+ }
+
+ /*public function onMessage(ConnectionInterface $from, $msg) {
+ $numRecv = count($this->clients) - 1;
+ echo sprintf('Connection %d sending message "%s" to %d other connection%s' . "\n"
+ , $from->resourceId, $msg, $numRecv, $numRecv == 1 ? '' : 's');
+
+ foreach ($this->clients as $client) {
+ if ($from === $client) {
+ $client->send("
You - " . $msg);
+ } else {
+ $client->send("
" . $from->resourceId . " - " . $msg);
+ }
+ }
+ }
+ */
+
+ public function onMessage(ConnectionInterface $conn, MessageInterface $msg) {
+ $data = json_decode($msg);
+ switch ($data->ClientMessageType) {
+ case "Subscribe":
+ $this->subscriptions[$conn->resourceId] = $data->Channel;
+ foreach ($this->subscriptions as $id => $channel) {
+ if ($this->subscriptions[$conn->resourceId] == $channel) {
+ $MessageObject = new \stdClass();
+ $MessageObject->ServerMessage = true;
+ $MessageObject->ServerMessageType = "GroupJoin";
+ $MessageObject->GroupName = $channel;
+ $MessageObject->Username = $this->connectedUsersNames[$conn->resourceId];
+ if ($id === $conn->resourceId) {
+ $MessageObject->WasHimself = true;
+ } else {
+ $MessageObject->WasHimself = false;
+ }
+ $MessageJson = json_encode($MessageObject, true);
+ $this->users[$id]->send($MessageJson);
+ }
+ }
+ break;
+ case "Message":
+ if (isset($this->subscriptions[$conn->resourceId])) {
+ $target = $this->subscriptions[$conn->resourceId];
+ foreach ($this->subscriptions as $id => $channel) {
+ if ($channel == $target) {
+ $MessageObject = new \stdClass();
+ $MessageObject->ServerMessage = false;
+ $MessageObject->Username = $this->connectedUsersNames[$conn->resourceId];
+ $MessageObject->Message = htmlspecialchars($data->Message);
+ if ($id === $conn->resourceId) {
+ $MessageObject->WasHimself = true;
+ } else {
+ $MessageObject->WasHimself = false;
+ }
+ $MessageJson = json_encode($MessageObject, true);
+ $this->users[$id]->send($MessageJson);
+ }
+ }
+ }
+ }
+ }
+
+ public function onClose(ConnectionInterface $conn) {
+ $this->clients->detach($conn);
+ foreach ($this->clients as $client) {
+ if (isset($this->subscriptions[$conn->resourceId])) {
+ $target = $this->subscriptions[$conn->resourceId];
+ foreach ($this->subscriptions as $id => $channel) {
+ if ($channel == $target) {
+ $MessageObject = new \stdClass();
+ $MessageObject->ServerMessage = true;
+ $MessageObject->ServerMessageType = "UserDisconnect";
+ $MessageObject->Username = $this->connectedUsersNames[$conn->resourceId];
+ $MessageJson = json_encode($MessageObject, true);
+ $this->users[$id]->send($MessageJson);
+ }
+ }
+ }
+ }
+ unset($this->users[$conn->resourceId]);
+ unset($this->subscriptions[$conn->resourceId]);
+ unset($this->connectedUsersNames[$conn->resourceId]);
+ }
+
+ public function onError(ConnectionInterface $conn, \Exception $e) {
+ echo "An error has occurred: {$e->getMessage()}\n";
+
+ $conn->close();
+ }
+}
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/SavePublicKey.php b/main/app/sprinkles/core/assets/SiteAssets/php/SavePublicKey.php
new file mode 100644
index 0000000..725a005
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/SavePublicKey.php
@@ -0,0 +1,17 @@
+prepare("SELECT count(*) FROM `PublicKeys` WHERE UserID = :UserID");
+ $CheckIfAlreadySetStmt->bindValue(':UserID', $_POST['UserID']);
+ $CheckIfAlreadySetStmt->execute();
+ $CheckIfAlreadySetRes = $CheckIfAlreadySetStmt->fetchColumn();
+ if ($CheckIfAlreadySetRes == 1) {
+ $UpdatePublicKeyStmt = $conn->prepare("UPDATE `PublicKeys` SET PublicKeyString = :PublicKeyString WHERE UserID = :UserID");
+ $UpdatePublicKeyStmt->execute(array('PublicKeyString' => $_POST["PublicKeyString"], 'UserID' => $_POST["UserID"]));
+ } else if ($CheckIfAlreadySetRes == 0) {
+ $InsertPublicKeyStmt = $conn->prepare("INSERT INTO `PublicKeys` (UserID, PublicKeyString) VALUES (:UserID, :PublicKeyString)");
+ $InsertPublicKeyStmt->execute(array('PublicKeyString' => $_POST["PublicKeyString"], 'UserID' => $_POST["UserID"]));
+ }
+} else {
+ http_response_code(400);
+}
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/composer.json b/main/app/sprinkles/core/assets/SiteAssets/php/composer.json
new file mode 100644
index 0000000..8406940
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/composer.json
@@ -0,0 +1,11 @@
+{
+ "autoload": {
+ "psr-4": {
+ "Websocket\\": "Chatserver/src"
+ }
+ },
+ "require": {
+ "cboden/ratchet": "^0.4.1",
+ "nubs/random-name-generator": "^2.1"
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/composer.lock b/main/app/sprinkles/core/assets/SiteAssets/php/composer.lock
new file mode 100644
index 0000000..a852b73
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/composer.lock
@@ -0,0 +1,943 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "7db98e9dbf466f436f9127ff8289eac0",
+ "packages": [
+ {
+ "name": "cboden/ratchet",
+ "version": "v0.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ratchetphp/Ratchet.git",
+ "reference": "0d31f3a8ad4795fd48397712709e55cd07f51360"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ratchetphp/Ratchet/zipball/0d31f3a8ad4795fd48397712709e55cd07f51360",
+ "reference": "0d31f3a8ad4795fd48397712709e55cd07f51360",
+ "shasum": ""
+ },
+ "require": {
+ "guzzlehttp/psr7": "^1.0",
+ "php": ">=5.4.2",
+ "ratchet/rfc6455": "^0.2",
+ "react/socket": "^1.0 || ^0.8 || ^0.7 || ^0.6 || ^0.5",
+ "symfony/http-foundation": "^2.6|^3.0|^4.0",
+ "symfony/routing": "^2.6|^3.0|^4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Ratchet\\": "src/Ratchet"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "role": "Developer"
+ }
+ ],
+ "description": "PHP WebSocket library",
+ "homepage": "http://socketo.me",
+ "keywords": [
+ "Ratchet",
+ "WebSockets",
+ "server",
+ "sockets",
+ "websocket"
+ ],
+ "time": "2017-12-12T00:49:31+00:00"
+ },
+ {
+ "name": "evenement/evenement",
+ "version": "v3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/igorw/evenement.git",
+ "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7",
+ "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "Evenement": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Igor Wiedler",
+ "email": "igor@wiedler.ch"
+ }
+ ],
+ "description": "Événement is a very simple event dispatching library for PHP",
+ "keywords": [
+ "event-dispatcher",
+ "event-emitter"
+ ],
+ "time": "2017-07-23T21:35:13+00:00"
+ },
+ {
+ "name": "guzzlehttp/psr7",
+ "version": "1.4.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/psr7.git",
+ "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c",
+ "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0",
+ "psr/http-message": "~1.0"
+ },
+ "provide": {
+ "psr/http-message-implementation": "1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.4-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Psr7\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Tobias Schultze",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "PSR-7 message implementation that also provides common utility methods",
+ "keywords": [
+ "http",
+ "message",
+ "request",
+ "response",
+ "stream",
+ "uri",
+ "url"
+ ],
+ "time": "2017-03-20T17:10:46+00:00"
+ },
+ {
+ "name": "nubs/random-name-generator",
+ "version": "v2.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nubs/random-name-generator.git",
+ "reference": "7004eb1724e1c4a154553e44312b7045fe412b77"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nubs/random-name-generator/zipball/7004eb1724e1c4a154553e44312b7045fe412b77",
+ "reference": "7004eb1724e1c4a154553e44312b7045fe412b77",
+ "shasum": ""
+ },
+ "require": {
+ "php": "~5.6 || ~7.0"
+ },
+ "require-dev": {
+ "cinam/randomizer": ">=1.1.1,<2.0",
+ "phpunit/phpunit": "~5.0",
+ "satooshi/php-coveralls": "~1.0",
+ "squizlabs/php_codesniffer": "~2.3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Nubs\\RandomNameGenerator\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Spencer Rinehart",
+ "email": "anubis@overthemonkey.com",
+ "role": "Developer"
+ }
+ ],
+ "description": "A library to create interesting, sometimes entertaining, random names.",
+ "keywords": [
+ "alliteration",
+ "generator",
+ "random",
+ "video game"
+ ],
+ "time": "2016-12-04T01:57:19+00:00"
+ },
+ {
+ "name": "paragonie/random_compat",
+ "version": "v2.0.12",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/paragonie/random_compat.git",
+ "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/paragonie/random_compat/zipball/258c89a6b97de7dfaf5b8c7607d0478e236b04fb",
+ "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.2.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "4.*|5.*"
+ },
+ "suggest": {
+ "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "lib/random.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Paragon Initiative Enterprises",
+ "email": "security@paragonie.com",
+ "homepage": "https://paragonie.com"
+ }
+ ],
+ "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
+ "keywords": [
+ "csprng",
+ "pseudorandom",
+ "random"
+ ],
+ "time": "2018-04-04T21:24:14+00:00"
+ },
+ {
+ "name": "psr/http-message",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "time": "2016-08-06T14:39:51+00:00"
+ },
+ {
+ "name": "ratchet/rfc6455",
+ "version": "v0.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ratchetphp/RFC6455.git",
+ "reference": "cc8a1a46a703ce01de10fdb5fab387381b66edc8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ratchetphp/RFC6455/zipball/cc8a1a46a703ce01de10fdb5fab387381b66edc8",
+ "reference": "cc8a1a46a703ce01de10fdb5fab387381b66edc8",
+ "shasum": ""
+ },
+ "require": {
+ "guzzlehttp/psr7": "^1.0",
+ "php": ">=5.4.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "4.8.*",
+ "react/http": "^0.4.1",
+ "react/socket-client": "^0.4.3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Ratchet\\RFC6455\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "role": "Developer"
+ }
+ ],
+ "description": "RFC6455 WebSocket protocol handler",
+ "homepage": "http://socketo.me",
+ "keywords": [
+ "WebSockets",
+ "rfc6455",
+ "websocket"
+ ],
+ "time": "2017-09-13T13:49:42+00:00"
+ },
+ {
+ "name": "react/cache",
+ "version": "v0.4.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/cache.git",
+ "reference": "75494f26b4ef089db9bf8c90b63c296246e099e8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/cache/zipball/75494f26b4ef089db9bf8c90b63c296246e099e8",
+ "reference": "75494f26b4ef089db9bf8c90b63c296246e099e8",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0",
+ "react/promise": "~2.0|~1.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Cache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Async, Promise-based cache interface for ReactPHP",
+ "keywords": [
+ "cache",
+ "caching",
+ "promise",
+ "reactphp"
+ ],
+ "time": "2017-12-20T16:47:13+00:00"
+ },
+ {
+ "name": "react/dns",
+ "version": "v0.4.13",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/dns.git",
+ "reference": "7d1e08c300fd7de600810883386ee5e2a64898f4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/dns/zipball/7d1e08c300fd7de600810883386ee5e2a64898f4",
+ "reference": "7d1e08c300fd7de600810883386ee5e2a64898f4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0",
+ "react/cache": "~0.4.0|~0.3.0",
+ "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5",
+ "react/promise": "^2.1 || ^1.2.1",
+ "react/promise-timer": "^1.2",
+ "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4.5"
+ },
+ "require-dev": {
+ "clue/block-react": "^1.2",
+ "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Dns\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Async DNS resolver for ReactPHP",
+ "keywords": [
+ "async",
+ "dns",
+ "dns-resolver",
+ "reactphp"
+ ],
+ "time": "2018-02-27T12:51:22+00:00"
+ },
+ {
+ "name": "react/event-loop",
+ "version": "v0.5.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/event-loop.git",
+ "reference": "e1e0647a5c6e2c86013a24e9c8252113df86105a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/event-loop/zipball/e1e0647a5c6e2c86013a24e9c8252113df86105a",
+ "reference": "e1e0647a5c6e2c86013a24e9c8252113df86105a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8.35 || ^5.7 || ^6.4"
+ },
+ "suggest": {
+ "ext-event": "~1.0 for ExtEventLoop",
+ "ext-pcntl": "For signal handling support when using the StreamSelectLoop"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\EventLoop\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.",
+ "keywords": [
+ "asynchronous",
+ "event-loop"
+ ],
+ "time": "2018-04-09T11:59:21+00:00"
+ },
+ {
+ "name": "react/promise",
+ "version": "v2.5.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/promise.git",
+ "reference": "62785ae604c8d69725d693eb370e1d67e94c4053"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/promise/zipball/62785ae604c8d69725d693eb370e1d67e94c4053",
+ "reference": "62785ae604c8d69725d693eb370e1d67e94c4053",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Promise\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com"
+ }
+ ],
+ "description": "A lightweight implementation of CommonJS Promises/A for PHP",
+ "keywords": [
+ "promise",
+ "promises"
+ ],
+ "time": "2017-03-25T12:08:31+00:00"
+ },
+ {
+ "name": "react/promise-timer",
+ "version": "v1.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/promise-timer.git",
+ "reference": "9b4cd9cbe7457e0d87fe8aa7ccceab8a2c830fbd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/9b4cd9cbe7457e0d87fe8aa7ccceab8a2c830fbd",
+ "reference": "9b4cd9cbe7457e0d87fe8aa7ccceab8a2c830fbd",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3",
+ "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5",
+ "react/promise": "~2.1|~1.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Promise\\Timer\\": "src/"
+ },
+ "files": [
+ "src/functions.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@lueck.tv"
+ }
+ ],
+ "description": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.",
+ "homepage": "https://github.com/react/promise-timer",
+ "keywords": [
+ "async",
+ "event-loop",
+ "promise",
+ "reactphp",
+ "timeout",
+ "timer"
+ ],
+ "time": "2017-12-22T15:41:41+00:00"
+ },
+ {
+ "name": "react/socket",
+ "version": "v0.8.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/socket.git",
+ "reference": "d3957313c92b539537fccc80170c05a27ec25796"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/socket/zipball/d3957313c92b539537fccc80170c05a27ec25796",
+ "reference": "d3957313c92b539537fccc80170c05a27ec25796",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "php": ">=5.3.0",
+ "react/dns": "^0.4.13",
+ "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5",
+ "react/promise": "^2.1 || ^1.2",
+ "react/promise-timer": "~1.0",
+ "react/stream": "^1.0 || ^0.7.1"
+ },
+ "require-dev": {
+ "clue/block-react": "^1.2",
+ "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Socket\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP",
+ "keywords": [
+ "Connection",
+ "Socket",
+ "async",
+ "reactphp",
+ "stream"
+ ],
+ "time": "2018-02-28T09:32:38+00:00"
+ },
+ {
+ "name": "react/stream",
+ "version": "v0.7.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/stream.git",
+ "reference": "10100896018fd847a257cd81143b8e1b7be08e40"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/stream/zipball/10100896018fd847a257cd81143b8e1b7be08e40",
+ "reference": "10100896018fd847a257cd81143b8e1b7be08e40",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "php": ">=5.3.8",
+ "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5"
+ },
+ "require-dev": {
+ "clue/stream-filter": "~1.2",
+ "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Stream\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP",
+ "keywords": [
+ "event-driven",
+ "io",
+ "non-blocking",
+ "pipe",
+ "reactphp",
+ "readable",
+ "stream",
+ "writable"
+ ],
+ "time": "2018-01-19T15:04:38+00:00"
+ },
+ {
+ "name": "symfony/http-foundation",
+ "version": "v3.4.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/http-foundation.git",
+ "reference": "b11e6d165ff4cbf5685d185ab19a90f2f3bb7d1e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/b11e6d165ff4cbf5685d185ab19a90f2f3bb7d1e",
+ "reference": "b11e6d165ff4cbf5685d185ab19a90f2f3bb7d1e",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.5.9|>=7.0.8",
+ "symfony/polyfill-mbstring": "~1.1",
+ "symfony/polyfill-php70": "~1.6"
+ },
+ "require-dev": {
+ "symfony/expression-language": "~2.8|~3.0|~4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.4-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\HttpFoundation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony HttpFoundation Component",
+ "homepage": "https://symfony.com",
+ "time": "2018-04-03T05:22:50+00:00"
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b",
+ "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "time": "2018-01-30T19:27:44+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php70",
+ "version": "v1.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php70.git",
+ "reference": "3532bfcd8f933a7816f3a0a59682fc404776600f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/3532bfcd8f933a7816f3a0a59682fc404776600f",
+ "reference": "3532bfcd8f933a7816f3a0a59682fc404776600f",
+ "shasum": ""
+ },
+ "require": {
+ "paragonie/random_compat": "~1.0|~2.0",
+ "php": ">=5.3.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Php70\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ],
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "time": "2018-01-30T19:27:44+00:00"
+ },
+ {
+ "name": "symfony/routing",
+ "version": "v3.4.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/routing.git",
+ "reference": "50f333b707bef9f6972ad04e6df3ec8875c9a67c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/routing/zipball/50f333b707bef9f6972ad04e6df3ec8875c9a67c",
+ "reference": "50f333b707bef9f6972ad04e6df3ec8875c9a67c",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.5.9|>=7.0.8"
+ },
+ "conflict": {
+ "symfony/config": "<3.3.1",
+ "symfony/dependency-injection": "<3.3",
+ "symfony/yaml": "<3.4"
+ },
+ "require-dev": {
+ "doctrine/annotations": "~1.0",
+ "doctrine/common": "~2.2",
+ "psr/log": "~1.0",
+ "symfony/config": "^3.3.1|~4.0",
+ "symfony/dependency-injection": "~3.3|~4.0",
+ "symfony/expression-language": "~2.8|~3.0|~4.0",
+ "symfony/http-foundation": "~2.8|~3.0|~4.0",
+ "symfony/yaml": "~3.4|~4.0"
+ },
+ "suggest": {
+ "doctrine/annotations": "For using the annotation loader",
+ "symfony/config": "For using the all-in-one router or any loader",
+ "symfony/dependency-injection": "For loading routes from a service",
+ "symfony/expression-language": "For using expression matching",
+ "symfony/http-foundation": "For using a Symfony Request object",
+ "symfony/yaml": "For using the YAML loader"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.4-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Routing\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Routing Component",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "router",
+ "routing",
+ "uri",
+ "url"
+ ],
+ "time": "2018-04-04T13:22:16+00:00"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/scripts.php b/main/app/sprinkles/core/assets/SiteAssets/php/scripts.php
new file mode 100644
index 0000000..c2a8dbc
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/scripts.php
@@ -0,0 +1,12 @@
+add('assets/js/fontawesome.js');
+$minifier->add('assets/js/modernizr.js');
+$minifier->add('assets/js/language.js');
+$minifier->add('assets/js/encryption.js');
+$minifier->add('assets/js/chat.js');
+$minifier->add('assets/js/slick.js');
+$minifier->add('assets/js/main.js');
+echo $minifier->minify();
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/stylesheet.php b/main/app/sprinkles/core/assets/SiteAssets/php/stylesheet.php
new file mode 100644
index 0000000..66dbc50
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/stylesheet.php
@@ -0,0 +1,6 @@
+add('assets/css/main.css');
+echo $minifier->minify();
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/autoload.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/autoload.php
new file mode 100644
index 0000000..fca3b98
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/autoload.php
@@ -0,0 +1,7 @@
+> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;'
+ - php -m
+ - composer install --dev --prefer-source
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/CHANGELOG.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/CHANGELOG.md
new file mode 100644
index 0000000..5169993
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/CHANGELOG.md
@@ -0,0 +1,135 @@
+CHANGELOG
+=========
+
+### Legend
+
+* "BC": Backwards compatibility break (from public component APIs)
+* "BF": Bug fix
+
+---
+
+* 0.4.1 (2017-12-11)
+ * Only enableKeepAlive in App if no WsServer passed allowing user to set their own timeout duration
+ * Support Symfony 4
+ * BF: Plug NOOP controller in connection from router in case of misbehaving client
+ * BF: Raise error from invalid WAMP payload
+
+* 0.4 (2017-09-14)
+ * BC: $conn->WebSocket->request replaced with $conn->httpRequest which is a PSR-7 object
+ * Binary messages now supported via Ratchet\WebSocket\MessageComponentInterface
+ * Added heartbeat support via ping/pong in WsServer
+ * BC: No longer support old (and insecure) Hixie76 and Hybi protocols
+ * BC: No longer support disabling UTF-8 checks
+ * BC: The Session component implements HttpServerInterface instead of WsServerInterface
+ * BC: PHP 5.3 no longer supported
+ * BC: Update to newer version of react/socket dependency
+ * BC: WAMP topics reduced to 0 subscriptions are deleted, new subs to same name will result in new Topic instance
+ * Significant performance enhancements
+
+* 0.3.6 (2017-01-06)
+ * BF: Keep host and scheme in HTTP request object attatched to connection
+ * BF: Return correct HTTP response (405) when non-GET request made
+
+* 0.3.5 (2016-05-25)
+ * BF: Unmask responding close frame
+ * Added write handler for PHP session serializer
+
+* 0.3.4 (2015-12-23)
+ * BF: Edge case where version check wasn't run on message coalesce
+ * BF: Session didn't start when using pdo_sqlite
+ * BF: WAMP currie prefix check when using '#'
+ * Compatibility with Symfony 3
+
+* 0.3.3 (2015-05-26)
+ * BF: Framing bug on large messages upon TCP fragmentation
+ * BF: Symfony Router query parameter defaults applied to Request
+ * BF: WAMP CURIE on all URIs
+ * OriginCheck rules applied to FlashPolicy
+ * Switched from PSR-0 to PSR-4
+
+* 0.3.2 (2014-06-08)
+ * BF: No messages after closing handshake (fixed rare race condition causing 100% CPU)
+ * BF: Fixed accidental BC break from v0.3.1
+ * Added autoDelete parameter to Topic to destroy when empty of connections
+ * Exposed React Socket on IoServer (allowing FlashPolicy shutdown in App)
+ * Normalized Exceptions in WAMP
+
+* 0.3.1 (2014-05-26)
+ * Added query parameter support to Router, set in HTTP request (ws://server?hello=world)
+ * HHVM compatibility
+ * BF: React/0.4 support; CPU starvation bug fixes
+ * BF: Allow App::route to ignore Host header
+ * Added expected filters to WAMP Topic broadcast method
+ * Resource cleanup in WAMP TopicManager
+
+* 0.3.0 (2013-10-14)
+ * Added the `App` class to help making Ratchet so easy to use it's silly
+ * BC: Require hostname to do HTTP Host header match and do Origin HTTP header check, verify same name by default, helping prevent CSRF attacks
+ * Added Symfony/2.2 based HTTP Router component to allowing for a single Ratchet server to handle multiple apps -> Ratchet\Http\Router
+ * BC: Decoupled HTTP from WebSocket component -> Ratchet\Http\HttpServer
+ * BF: Single sub-protocol selection to conform with RFC6455
+ * BF: Sanity checks on WAMP protocol to prevent errors
+
+* 0.2.8 (2013-09-19)
+ * React 0.3 support
+
+* 0.2.7 (2013-06-09)
+ * BF: Sub-protocol negotation with Guzzle 3.6
+
+* 0.2.6 (2013-06-01)
+ * Guzzle 3.6 support
+
+* 0.2.5 (2013-04-01)
+ * Fixed Hixie-76 handshake bug
+
+* 0.2.4 (2013-03-09)
+ * Support for Symfony 2.2 and Guzzle 2.3
+ * Minor bug fixes when handling errors
+
+* 0.2.3 (2012-11-21)
+ * Bumped dep: Guzzle to v3, React to v0.2.4
+ * More tests
+
+* 0.2.2 (2012-10-20)
+ * Bumped deps to use React v0.2
+
+* 0.2.1 (2012-10-13)
+ * BF: No more UTF-8 warnings in browsers (no longer sending empty sub-protocol string)
+ * Documentation corrections
+ * Using new composer structure
+
+* 0.2 (2012-09-07)
+ * Ratchet passes every non-binary-frame test from the Autobahn Testsuite
+ * Major performance improvements
+ * BC: Renamed "WampServer" to "ServerProtocol"
+ * BC: New "WampServer" component passes Topic container objects of subscribed Connections
+ * Option to turn off UTF-8 checks in order to increase performance
+ * Switched dependency guzzle/guzzle to guzzle/http (no API changes)
+ * mbstring no longer required
+
+* 0.1.5 (2012-07-12)
+ * BF: Error where service wouldn't run on PHP <= 5.3.8
+ * Dependency library updates
+
+* 0.1.4 (2012-06-17)
+ * Fixed dozens of failing AB tests
+ * BF: Proper socket buffer handling
+
+* 0.1.3 (2012-06-15)
+ * Major refactor inside WebSocket protocol handling, more loosley coupled
+ * BF: Proper error handling on failed WebSocket connections
+ * BF: Handle TCP message concatenation
+ * Inclusion of the AutobahnTestSuite checking WebSocket protocol compliance
+ * mb_string now a requirement
+
+* 0.1.2 (2012-05-19)
+ * BC/BF: Updated WAMP API to coincide with the official spec
+ * Tweaks to improve running as a long lived process
+
+* 0.1.1 (2012-05-14)
+ * Separated interfaces allowing WebSockets to support multiple sub protocols
+ * BF: remoteAddress variable on connections returns proper value
+
+* 0.1 (2012-05-11)
+ * First release with components: IoServer, WsServer, SessionProvider, WampServer, FlashPolicy, IpBlackList
+ * I/O now handled by React, making Ratchet fully asynchronous
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/LICENSE b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/LICENSE
new file mode 100644
index 0000000..24abba1
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2011-2017 Chris Boden
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/Makefile b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/Makefile
new file mode 100644
index 0000000..a2526c0
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/Makefile
@@ -0,0 +1,42 @@
+# This file is intended to ease the author's development and testing process
+# Users do not need to use `make`; Ratchet does not need to be compiled
+
+test:
+ phpunit
+
+cover:
+ phpunit --coverage-text --coverage-html=reports/coverage
+
+abtests:
+ ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8001 LibEvent &
+ ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8002 StreamSelect &
+ ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8004 LibEv &
+ wstest -m testeeserver -w ws://localhost:8000 &
+ sleep 1
+ wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-all.json
+ killall php wstest
+
+abtest:
+ ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8000 StreamSelect &
+ sleep 1
+ wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-quick.json
+ killall php
+
+profile:
+ php -d 'xdebug.profiler_enable=1' tests/autobahn/bin/fuzzingserver.php 8000 LibEvent &
+ sleep 1
+ wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-profile.json
+ killall php
+
+apidocs:
+ apigen --title Ratchet -d reports/api \
+ -s src/ \
+ -s vendor/ratchet/rfc6455/src \
+ -s vendor/react/event-loop/src \
+ -s vendor/react/socket/src \
+ -s vendor/react/stream/src \
+ -s vendor/psr/http-message/src \
+ -s vendor/symfony/http-foundation/Session \
+ -s vendor/symfony/routing \
+ -s vendor/evenement/evenement/src/Evenement \
+ --exclude=vendor/symfony/routing/Tests \
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/README.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/README.md
new file mode 100644
index 0000000..3f1a345
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/README.md
@@ -0,0 +1,83 @@
+# Ratchet
+
+[](http://travis-ci.org/ratchetphp/Ratchet)
+[](http://socketo.me/reports/ab/index.html)
+[](https://packagist.org/packages/cboden/ratchet)
+
+A PHP library for asynchronously serving WebSockets.
+Build up your application through simple interfaces and re-use your application without changing any of its code just by combining different components.
+
+## Requirements
+
+Shell access is required and root access is recommended.
+To avoid proxy/firewall blockage it's recommended WebSockets are requested on port 80 or 443 (SSL), which requires root access.
+In order to do this, along with your sync web stack, you can either use a reverse proxy or two separate machines.
+You can find more details in the [server conf docs](http://socketo.me/docs/deploy#serverconfiguration).
+
+### Documentation
+
+User and API documentation is available on Ratchet's website: http://socketo.me
+
+See https://github.com/cboden/Ratchet-examples for some out-of-the-box working demos using Ratchet.
+
+Need help? Have a question? Want to provide feedback? Write a message on the [Google Groups Mailing List](https://groups.google.com/forum/#!forum/ratchet-php).
+
+---
+
+### A quick example
+
+```php
+clients = new \SplObjectStorage;
+ }
+
+ public function onOpen(ConnectionInterface $conn) {
+ $this->clients->attach($conn);
+ }
+
+ public function onMessage(ConnectionInterface $from, $msg) {
+ foreach ($this->clients as $client) {
+ if ($from != $client) {
+ $client->send($msg);
+ }
+ }
+ }
+
+ public function onClose(ConnectionInterface $conn) {
+ $this->clients->detach($conn);
+ }
+
+ public function onError(ConnectionInterface $conn, \Exception $e) {
+ $conn->close();
+ }
+}
+
+ // Run the server application through the WebSocket protocol on port 8080
+ $app = new Ratchet\App('localhost', 8080);
+ $app->route('/chat', new MyChat);
+ $app->route('/echo', new Ratchet\Server\EchoServer, array('*'));
+ $app->run();
+```
+
+ $ php chat.php
+
+```javascript
+ // Then some JavaScript in the browser:
+ var conn = new WebSocket('ws://localhost:8080/echo');
+ conn.onmessage = function(e) { console.log(e.data); };
+ conn.onopen = function(e) { conn.send('Hello Me!'); };
+```
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/composer.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/composer.json
new file mode 100644
index 0000000..9529618
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/composer.json
@@ -0,0 +1,36 @@
+{
+ "name": "cboden/ratchet"
+ , "type": "library"
+ , "description": "PHP WebSocket library"
+ , "keywords": ["WebSockets", "Server", "Ratchet", "Sockets", "WebSocket"]
+ , "homepage": "http://socketo.me"
+ , "license": "MIT"
+ , "authors": [
+ {
+ "name": "Chris Boden"
+ , "email": "cboden@gmail.com"
+ , "role": "Developer"
+ }
+ ]
+ , "support": {
+ "forum": "https://groups.google.com/forum/#!forum/ratchet-php"
+ , "issues": "https://github.com/ratchetphp/Ratchet/issues"
+ , "irc": "irc://irc.freenode.org/reactphp"
+ }
+ , "autoload": {
+ "psr-4": {
+ "Ratchet\\": "src/Ratchet"
+ }
+ }
+ , "require": {
+ "php": ">=5.4.2"
+ , "ratchet/rfc6455": "^0.2"
+ , "react/socket": "^1.0 || ^0.8 || ^0.7 || ^0.6 || ^0.5"
+ , "guzzlehttp/psr7": "^1.0"
+ , "symfony/http-foundation": "^2.6|^3.0|^4.0"
+ , "symfony/routing": "^2.6|^3.0|^4.0"
+ }
+ , "require-dev": {
+ "phpunit/phpunit": "~4.8"
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/phpunit.xml.dist b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/phpunit.xml.dist
new file mode 100644
index 0000000..0cc5451
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/phpunit.xml.dist
@@ -0,0 +1,30 @@
+
+
+
+
+
+ ./tests/unit/
+
+
+
+
+
+ ./tests/integration/
+
+
+
+
+
+ ./src/
+
+
+
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/AbstractConnectionDecorator.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/AbstractConnectionDecorator.php
new file mode 100644
index 0000000..9707951
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/AbstractConnectionDecorator.php
@@ -0,0 +1,41 @@
+wrappedConn = $conn;
+ }
+
+ /**
+ * @return ConnectionInterface
+ */
+ protected function getConnection() {
+ return $this->wrappedConn;
+ }
+
+ public function __set($name, $value) {
+ $this->wrappedConn->$name = $value;
+ }
+
+ public function __get($name) {
+ return $this->wrappedConn->$name;
+ }
+
+ public function __isset($name) {
+ return isset($this->wrappedConn->$name);
+ }
+
+ public function __unset($name) {
+ unset($this->wrappedConn->$name);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/App.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/App.php
new file mode 100644
index 0000000..f378534
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/App.php
@@ -0,0 +1,145 @@
+httpHost = $httpHost;
+ $this->port = $port;
+
+ $socket = new Reactor($address . ':' . $port, $loop);
+
+ $this->routes = new RouteCollection;
+ $this->_server = new IoServer(new HttpServer(new Router(new UrlMatcher($this->routes, new RequestContext))), $socket, $loop);
+
+ $policy = new FlashPolicy;
+ $policy->addAllowedAccess($httpHost, 80);
+ $policy->addAllowedAccess($httpHost, $port);
+
+ if (80 == $port) {
+ $flashUri = '0.0.0.0:843';
+ } else {
+ $flashUri = 8843;
+ }
+ $flashSock = new Reactor($flashUri, $loop);
+ $this->flashServer = new IoServer($policy, $flashSock);
+ }
+
+ /**
+ * Add an endpoint/application to the server
+ * @param string $path The URI the client will connect to
+ * @param ComponentInterface $controller Your application to server for the route. If not specified, assumed to be for a WebSocket
+ * @param array $allowedOrigins An array of hosts allowed to connect (same host by default), ['*'] for any
+ * @param string $httpHost Override the $httpHost variable provided in the __construct
+ * @return ComponentInterface|WsServer
+ */
+ public function route($path, ComponentInterface $controller, array $allowedOrigins = array(), $httpHost = null) {
+ if ($controller instanceof HttpServerInterface || $controller instanceof WsServer) {
+ $decorated = $controller;
+ } elseif ($controller instanceof WampServerInterface) {
+ $decorated = new WsServer(new WampServer($controller));
+ $decorated->enableKeepAlive($this->_server->loop);
+ } elseif ($controller instanceof MessageComponentInterface) {
+ $decorated = new WsServer($controller);
+ $decorated->enableKeepAlive($this->_server->loop);
+ } else {
+ $decorated = $controller;
+ }
+
+ if ($httpHost === null) {
+ $httpHost = $this->httpHost;
+ }
+
+ $allowedOrigins = array_values($allowedOrigins);
+ if (0 === count($allowedOrigins)) {
+ $allowedOrigins[] = $httpHost;
+ }
+ if ('*' !== $allowedOrigins[0]) {
+ $decorated = new OriginCheck($decorated, $allowedOrigins);
+ }
+
+ //allow origins in flash policy server
+ if(empty($this->flashServer) === false) {
+ foreach($allowedOrigins as $allowedOrgin) {
+ $this->flashServer->app->addAllowedAccess($allowedOrgin, $this->port);
+ }
+ }
+
+ $this->routes->add('rr-' . ++$this->_routeCounter, new Route($path, array('_controller' => $decorated), array('Origin' => $this->httpHost), array(), $httpHost, array(), array('GET')));
+
+ return $decorated;
+ }
+
+ /**
+ * Run the server by entering the event loop
+ */
+ public function run() {
+ $this->_server->run();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/ComponentInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/ComponentInterface.php
new file mode 100644
index 0000000..37e41b1
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/ComponentInterface.php
@@ -0,0 +1,31 @@
+ \Ratchet\VERSION
+ ], $additional_headers));
+
+ $conn->send(gPsr\str($response));
+ $conn->close();
+ }
+}
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Http/HttpRequestParser.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Http/HttpRequestParser.php
new file mode 100644
index 0000000..9c44114
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Http/HttpRequestParser.php
@@ -0,0 +1,64 @@
+httpBuffer)) {
+ $context->httpBuffer = '';
+ }
+
+ $context->httpBuffer .= $data;
+
+ if (strlen($context->httpBuffer) > (int)$this->maxSize) {
+ throw new \OverflowException("Maximum buffer size of {$this->maxSize} exceeded parsing HTTP header");
+ }
+
+ if ($this->isEom($context->httpBuffer)) {
+ $request = $this->parse($context->httpBuffer);
+
+ unset($context->httpBuffer);
+
+ return $request;
+ }
+ }
+
+ /**
+ * Determine if the message has been buffered as per the HTTP specification
+ * @param string $message
+ * @return boolean
+ */
+ public function isEom($message) {
+ return (boolean)strpos($message, static::EOM);
+ }
+
+ /**
+ * @param string $headers
+ * @return \Psr\Http\Message\RequestInterface
+ */
+ public function parse($headers) {
+ return gPsr\parse_request($headers);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Http/HttpServer.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Http/HttpServer.php
new file mode 100644
index 0000000..bbd8d53
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Http/HttpServer.php
@@ -0,0 +1,76 @@
+_httpServer = $component;
+ $this->_reqParser = new HttpRequestParser;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onOpen(ConnectionInterface $conn) {
+ $conn->httpHeadersReceived = false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onMessage(ConnectionInterface $from, $msg) {
+ if (true !== $from->httpHeadersReceived) {
+ try {
+ if (null === ($request = $this->_reqParser->onMessage($from, $msg))) {
+ return;
+ }
+ } catch (\OverflowException $oe) {
+ return $this->close($from, 413);
+ }
+
+ $from->httpHeadersReceived = true;
+
+ return $this->_httpServer->onOpen($from, $request);
+ }
+
+ $this->_httpServer->onMessage($from, $msg);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onClose(ConnectionInterface $conn) {
+ if ($conn->httpHeadersReceived) {
+ $this->_httpServer->onClose($conn);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onError(ConnectionInterface $conn, \Exception $e) {
+ if ($conn->httpHeadersReceived) {
+ $this->_httpServer->onError($conn, $e);
+ } else {
+ $this->close($conn, 500);
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Http/HttpServerInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Http/HttpServerInterface.php
new file mode 100644
index 0000000..2c37c49
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Http/HttpServerInterface.php
@@ -0,0 +1,14 @@
+_component = $component;
+ $this->allowedOrigins += $allowed;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) {
+ $header = (string)$request->getHeader('Origin')[0];
+ $origin = parse_url($header, PHP_URL_HOST) ?: $header;
+
+ if (!in_array($origin, $this->allowedOrigins)) {
+ return $this->close($conn, 403);
+ }
+
+ return $this->_component->onOpen($conn, $request);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function onMessage(ConnectionInterface $from, $msg) {
+ return $this->_component->onMessage($from, $msg);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function onClose(ConnectionInterface $conn) {
+ return $this->_component->onClose($conn);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function onError(ConnectionInterface $conn, \Exception $e) {
+ return $this->_component->onError($conn, $e);
+ }
+}
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Http/Router.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Http/Router.php
new file mode 100644
index 0000000..df7fe82
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Http/Router.php
@@ -0,0 +1,96 @@
+_matcher = $matcher;
+ $this->_noopController = new NoOpHttpServerController;
+ }
+
+ /**
+ * {@inheritdoc}
+ * @throws \UnexpectedValueException If a controller is not \Ratchet\Http\HttpServerInterface
+ */
+ public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) {
+ if (null === $request) {
+ throw new \UnexpectedValueException('$request can not be null');
+ }
+
+ $conn->controller = $this->_noopController;
+
+ $uri = $request->getUri();
+
+ $context = $this->_matcher->getContext();
+ $context->setMethod($request->getMethod());
+ $context->setHost($uri->getHost());
+
+ try {
+ $route = $this->_matcher->match($uri->getPath());
+ } catch (MethodNotAllowedException $nae) {
+ return $this->close($conn, 405, array('Allow' => $nae->getAllowedMethods()));
+ } catch (ResourceNotFoundException $nfe) {
+ return $this->close($conn, 404);
+ }
+
+ if (is_string($route['_controller']) && class_exists($route['_controller'])) {
+ $route['_controller'] = new $route['_controller'];
+ }
+
+ if (!($route['_controller'] instanceof HttpServerInterface)) {
+ throw new \UnexpectedValueException('All routes must implement Ratchet\Http\HttpServerInterface');
+ }
+
+ $parameters = [];
+ foreach($route as $key => $value) {
+ if ((is_string($key)) && ('_' !== substr($key, 0, 1))) {
+ $parameters[$key] = $value;
+ }
+ }
+ $parameters = array_merge($parameters, gPsr\parse_query($uri->getQuery() ?: ''));
+
+ $request = $request->withUri($uri->withQuery(gPsr\build_query($parameters)));
+
+ $conn->controller = $route['_controller'];
+ $conn->controller->onOpen($conn, $request);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onMessage(ConnectionInterface $from, $msg) {
+ $from->controller->onMessage($from, $msg);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onClose(ConnectionInterface $conn) {
+ if (isset($conn->controller)) {
+ $conn->controller->onClose($conn);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onError(ConnectionInterface $conn, \Exception $e) {
+ if (isset($conn->controller)) {
+ $conn->controller->onError($conn, $e);
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/MessageComponentInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/MessageComponentInterface.php
new file mode 100644
index 0000000..b4a92e2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/MessageComponentInterface.php
@@ -0,0 +1,5 @@
+send($msg);
+ }
+
+ public function onClose(ConnectionInterface $conn) {
+ }
+
+ public function onError(ConnectionInterface $conn, \Exception $e) {
+ $conn->close();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Server/FlashPolicy.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Server/FlashPolicy.php
new file mode 100644
index 0000000..4a1b8bd
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Server/FlashPolicy.php
@@ -0,0 +1,200 @@
+
';
+
+ /**
+ * Stores an array of allowed domains and their ports
+ * @var array
+ */
+ protected $_access = array();
+
+ /**
+ * @var string
+ */
+ protected $_siteControl = '';
+
+ /**
+ * @var string
+ */
+ protected $_cache = '';
+
+ /**
+ * @var string
+ */
+ protected $_cacheValid = false;
+
+ /**
+ * Add a domain to an allowed access list.
+ *
+ * @param string $domain Specifies a requesting domain to be granted access. Both named domains and IP
+ * addresses are acceptable values. Subdomains are considered different domains. A wildcard (*) can
+ * be used to match all domains when used alone, or multiple domains (subdomains) when used as a
+ * prefix for an explicit, second-level domain name separated with a dot (.)
+ * @param string $ports A comma-separated list of ports or range of ports that a socket connection
+ * is allowed to connect to. A range of ports is specified through a dash (-) between two port numbers.
+ * Ranges can be used with individual ports when separated with a comma. A single wildcard (*) can
+ * be used to allow all ports.
+ * @param bool $secure
+ * @throws \UnexpectedValueException
+ * @return FlashPolicy
+ */
+ public function addAllowedAccess($domain, $ports = '*', $secure = false) {
+ if (!$this->validateDomain($domain)) {
+ throw new \UnexpectedValueException('Invalid domain');
+ }
+
+ if (!$this->validatePorts($ports)) {
+ throw new \UnexpectedValueException('Invalid Port');
+ }
+
+ $this->_access[] = array($domain, $ports, (boolean)$secure);
+ $this->_cacheValid = false;
+
+ return $this;
+ }
+
+ /**
+ * Removes all domains from the allowed access list.
+ *
+ * @return \Ratchet\Server\FlashPolicy
+ */
+ public function clearAllowedAccess() {
+ $this->_access = array();
+ $this->_cacheValid = false;
+
+ return $this;
+ }
+
+ /**
+ * site-control defines the meta-policy for the current domain. A meta-policy specifies acceptable
+ * domain policy files other than the master policy file located in the target domain's root and named
+ * crossdomain.xml.
+ *
+ * @param string $permittedCrossDomainPolicies
+ * @throws \UnexpectedValueException
+ * @return FlashPolicy
+ */
+ public function setSiteControl($permittedCrossDomainPolicies = 'all') {
+ if (!$this->validateSiteControl($permittedCrossDomainPolicies)) {
+ throw new \UnexpectedValueException('Invalid site control set');
+ }
+
+ $this->_siteControl = $permittedCrossDomainPolicies;
+ $this->_cacheValid = false;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onOpen(ConnectionInterface $conn) {
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onMessage(ConnectionInterface $from, $msg) {
+ if (!$this->_cacheValid) {
+ $this->_cache = $this->renderPolicy()->asXML();
+ $this->_cacheValid = true;
+ }
+
+ $from->send($this->_cache . "\0");
+ $from->close();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onClose(ConnectionInterface $conn) {
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onError(ConnectionInterface $conn, \Exception $e) {
+ $conn->close();
+ }
+
+ /**
+ * Builds the crossdomain file based on the template policy
+ *
+ * @throws \UnexpectedValueException
+ * @return \SimpleXMLElement
+ */
+ public function renderPolicy() {
+ $policy = new \SimpleXMLElement($this->_policy);
+
+ $siteControl = $policy->addChild('site-control');
+
+ if ($this->_siteControl == '') {
+ $this->setSiteControl();
+ }
+
+ $siteControl->addAttribute('permitted-cross-domain-policies', $this->_siteControl);
+
+ if (empty($this->_access)) {
+ throw new \UnexpectedValueException('You must add a domain through addAllowedAccess()');
+ }
+
+ foreach ($this->_access as $access) {
+ $tmp = $policy->addChild('allow-access-from');
+ $tmp->addAttribute('domain', $access[0]);
+ $tmp->addAttribute('to-ports', $access[1]);
+ $tmp->addAttribute('secure', ($access[2] === true) ? 'true' : 'false');
+ }
+
+ return $policy;
+ }
+
+ /**
+ * Make sure the proper site control was passed
+ *
+ * @param string $permittedCrossDomainPolicies
+ * @return bool
+ */
+ public function validateSiteControl($permittedCrossDomainPolicies) {
+ //'by-content-type' and 'by-ftp-filename' are not available for sockets
+ return (bool)in_array($permittedCrossDomainPolicies, array('none', 'master-only', 'all'));
+ }
+
+ /**
+ * Validate for proper domains (wildcards allowed)
+ *
+ * @param string $domain
+ * @return bool
+ */
+ public function validateDomain($domain) {
+ return (bool)preg_match("/^((http(s)?:\/\/)?([a-z0-9-_]+\.|\*\.)*([a-z0-9-_\.]+)|\*)$/i", $domain);
+ }
+
+ /**
+ * Make sure valid ports were passed
+ *
+ * @param string $port
+ * @return bool
+ */
+ public function validatePorts($port) {
+ return (bool)preg_match('/^(\*|(\d+[,-]?)*\d+)$/', $port);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Server/IoConnection.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Server/IoConnection.php
new file mode 100644
index 0000000..9f864bb
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Server/IoConnection.php
@@ -0,0 +1,38 @@
+conn = $conn;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function send($data) {
+ $this->conn->write($data);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function close() {
+ $this->conn->end();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Server/IoServer.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Server/IoServer.php
new file mode 100644
index 0000000..b3fb7e0
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Server/IoServer.php
@@ -0,0 +1,140 @@
+loop = $loop;
+ $this->app = $app;
+ $this->socket = $socket;
+
+ $socket->on('connection', array($this, 'handleConnect'));
+ }
+
+ /**
+ * @param \Ratchet\MessageComponentInterface $component The application that I/O will call when events are received
+ * @param int $port The port to server sockets on
+ * @param string $address The address to receive sockets on (0.0.0.0 means receive connections from any)
+ * @return IoServer
+ */
+ public static function factory(MessageComponentInterface $component, $port = 80, $address = '0.0.0.0') {
+ $loop = LoopFactory::create();
+ $socket = new Reactor($address . ':' . $port, $loop);
+
+ return new static($component, $socket, $loop);
+ }
+
+ /**
+ * Run the application by entering the event loop
+ * @throws \RuntimeException If a loop was not previously specified
+ */
+ public function run() {
+ if (null === $this->loop) {
+ throw new \RuntimeException("A React Loop was not provided during instantiation");
+ }
+
+ // @codeCoverageIgnoreStart
+ $this->loop->run();
+ // @codeCoverageIgnoreEnd
+ }
+
+ /**
+ * Triggered when a new connection is received from React
+ * @param \React\Socket\ConnectionInterface $conn
+ */
+ public function handleConnect($conn) {
+ $conn->decor = new IoConnection($conn);
+ $conn->decor->resourceId = (int)$conn->stream;
+
+ $uri = $conn->getRemoteAddress();
+ $conn->decor->remoteAddress = trim(
+ parse_url((strpos($uri, '://') === false ? 'tcp://' : '') . $uri, PHP_URL_HOST),
+ '[]'
+ );
+
+ $this->app->onOpen($conn->decor);
+
+ $conn->on('data', function ($data) use ($conn) {
+ $this->handleData($data, $conn);
+ });
+ $conn->on('close', function () use ($conn) {
+ $this->handleEnd($conn);
+ });
+ $conn->on('error', function (\Exception $e) use ($conn) {
+ $this->handleError($e, $conn);
+ });
+ }
+
+ /**
+ * Data has been received from React
+ * @param string $data
+ * @param \React\Socket\ConnectionInterface $conn
+ */
+ public function handleData($data, $conn) {
+ try {
+ $this->app->onMessage($conn->decor, $data);
+ } catch (\Exception $e) {
+ $this->handleError($e, $conn);
+ }
+ }
+
+ /**
+ * A connection has been closed by React
+ * @param \React\Socket\ConnectionInterface $conn
+ */
+ public function handleEnd($conn) {
+ try {
+ $this->app->onClose($conn->decor);
+ } catch (\Exception $e) {
+ $this->handleError($e, $conn);
+ }
+
+ unset($conn->decor);
+ }
+
+ /**
+ * An error has occurred, let the listening application know
+ * @param \Exception $e
+ * @param \React\Socket\ConnectionInterface $conn
+ */
+ public function handleError(\Exception $e, $conn) {
+ $this->app->onError($conn->decor, $e);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Server/IpBlackList.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Server/IpBlackList.php
new file mode 100644
index 0000000..9342254
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Server/IpBlackList.php
@@ -0,0 +1,111 @@
+_decorating = $component;
+ }
+
+ /**
+ * Add an address to the blacklist that will not be allowed to connect to your application
+ * @param string $ip IP address to block from connecting to your application
+ * @return IpBlackList
+ */
+ public function blockAddress($ip) {
+ $this->_blacklist[$ip] = true;
+
+ return $this;
+ }
+
+ /**
+ * Unblock an address so they can access your application again
+ * @param string $ip IP address to unblock from connecting to your application
+ * @return IpBlackList
+ */
+ public function unblockAddress($ip) {
+ if (isset($this->_blacklist[$this->filterAddress($ip)])) {
+ unset($this->_blacklist[$this->filterAddress($ip)]);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @param string $address
+ * @return bool
+ */
+ public function isBlocked($address) {
+ return (isset($this->_blacklist[$this->filterAddress($address)]));
+ }
+
+ /**
+ * Get an array of all the addresses blocked
+ * @return array
+ */
+ public function getBlockedAddresses() {
+ return array_keys($this->_blacklist);
+ }
+
+ /**
+ * @param string $address
+ * @return string
+ */
+ public function filterAddress($address) {
+ if (strstr($address, ':') && substr_count($address, '.') == 3) {
+ list($address, $port) = explode(':', $address);
+ }
+
+ return $address;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function onOpen(ConnectionInterface $conn) {
+ if ($this->isBlocked($conn->remoteAddress)) {
+ return $conn->close();
+ }
+
+ return $this->_decorating->onOpen($conn);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function onMessage(ConnectionInterface $from, $msg) {
+ return $this->_decorating->onMessage($from, $msg);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function onClose(ConnectionInterface $conn) {
+ if (!$this->isBlocked($conn->remoteAddress)) {
+ $this->_decorating->onClose($conn);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function onError(ConnectionInterface $conn, \Exception $e) {
+ if (!$this->isBlocked($conn->remoteAddress)) {
+ $this->_decorating->onError($conn, $e);
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Session/Serialize/HandlerInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Session/Serialize/HandlerInterface.php
new file mode 100644
index 0000000..b83635f
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Session/Serialize/HandlerInterface.php
@@ -0,0 +1,16 @@
+ $bucketData) {
+ $preSerialized[] = $bucket . '|' . serialize($bucketData);
+ }
+ $serialized = implode('', $preSerialized);
+ }
+
+ return $serialized;
+ }
+
+ /**
+ * {@inheritdoc}
+ * @link http://ca2.php.net/manual/en/function.session-decode.php#108037 Code from this comment on php.net
+ * @throws \UnexpectedValueException If there is a problem parsing the data
+ */
+ public function unserialize($raw) {
+ $returnData = array();
+ $offset = 0;
+
+ while ($offset < strlen($raw)) {
+ if (!strstr(substr($raw, $offset), "|")) {
+ throw new \UnexpectedValueException("invalid data, remaining: " . substr($raw, $offset));
+ }
+
+ $pos = strpos($raw, "|", $offset);
+ $num = $pos - $offset;
+ $varname = substr($raw, $offset, $num);
+ $offset += $num + 1;
+ $data = unserialize(substr($raw, $offset));
+
+ $returnData[$varname] = $data;
+ $offset += strlen(serialize($data));
+ }
+
+ return $returnData;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Session/SessionProvider.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Session/SessionProvider.php
new file mode 100644
index 0000000..44276c5
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Session/SessionProvider.php
@@ -0,0 +1,243 @@
+_app = $app;
+ $this->_handler = $handler;
+ $this->_null = new NullSessionHandler;
+
+ ini_set('session.auto_start', 0);
+ ini_set('session.cache_limiter', '');
+ ini_set('session.use_cookies', 0);
+
+ $this->setOptions($options);
+
+ if (null === $serializer) {
+ $serialClass = __NAMESPACE__ . "\\Serialize\\{$this->toClassCase(ini_get('session.serialize_handler'))}Handler"; // awesome/terrible hack, eh?
+ if (!class_exists($serialClass)) {
+ throw new \RuntimeException('Unable to parse session serialize handler');
+ }
+
+ $serializer = new $serialClass;
+ }
+
+ $this->_serializer = $serializer;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) {
+ $sessionName = ini_get('session.name');
+
+ $id = array_reduce($request->getHeader('Cookie'), function($accumulator, $cookie) use ($sessionName) {
+ if ($accumulator) {
+ return $accumulator;
+ }
+
+ $crumbs = $this->parseCookie($cookie);
+
+ return isset($crumbs['cookies'][$sessionName]) ? $crumbs['cookies'][$sessionName] : false;
+ }, false);
+
+ if (null === $request || false === $id) {
+ $saveHandler = $this->_null;
+ $id = '';
+ } else {
+ $saveHandler = $this->_handler;
+ }
+
+ $conn->Session = new Session(new VirtualSessionStorage($saveHandler, $id, $this->_serializer));
+
+ if (ini_get('session.auto_start')) {
+ $conn->Session->start();
+ }
+
+ return $this->_app->onOpen($conn, $request);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function onMessage(ConnectionInterface $from, $msg) {
+ return $this->_app->onMessage($from, $msg);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function onClose(ConnectionInterface $conn) {
+ // "close" session for Connection
+
+ return $this->_app->onClose($conn);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function onError(ConnectionInterface $conn, \Exception $e) {
+ return $this->_app->onError($conn, $e);
+ }
+
+ /**
+ * Set all the php session. ini options
+ * © Symfony
+ * @param array $options
+ * @return array
+ */
+ protected function setOptions(array $options) {
+ $all = array(
+ 'auto_start', 'cache_limiter', 'cookie_domain', 'cookie_httponly',
+ 'cookie_lifetime', 'cookie_path', 'cookie_secure',
+ 'entropy_file', 'entropy_length', 'gc_divisor',
+ 'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character',
+ 'hash_function', 'name', 'referer_check',
+ 'serialize_handler', 'use_cookies',
+ 'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled',
+ 'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name',
+ 'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags'
+ );
+
+ foreach ($all as $key) {
+ if (!array_key_exists($key, $options)) {
+ $options[$key] = ini_get("session.{$key}");
+ } else {
+ ini_set("session.{$key}", $options[$key]);
+ }
+ }
+
+ return $options;
+ }
+
+ /**
+ * @param string $langDef Input to convert
+ * @return string
+ */
+ protected function toClassCase($langDef) {
+ return str_replace(' ', '', ucwords(str_replace('_', ' ', $langDef)));
+ }
+
+ /**
+ * Taken from Guzzle3
+ */
+ private static $cookieParts = array(
+ 'domain' => 'Domain',
+ 'path' => 'Path',
+ 'max_age' => 'Max-Age',
+ 'expires' => 'Expires',
+ 'version' => 'Version',
+ 'secure' => 'Secure',
+ 'port' => 'Port',
+ 'discard' => 'Discard',
+ 'comment' => 'Comment',
+ 'comment_url' => 'Comment-Url',
+ 'http_only' => 'HttpOnly'
+ );
+
+ /**
+ * Taken from Guzzle3
+ */
+ private function parseCookie($cookie, $host = null, $path = null, $decode = false) {
+ // Explode the cookie string using a series of semicolons
+ $pieces = array_filter(array_map('trim', explode(';', $cookie)));
+
+ // The name of the cookie (first kvp) must include an equal sign.
+ if (empty($pieces) || !strpos($pieces[0], '=')) {
+ return false;
+ }
+
+ // Create the default return array
+ $data = array_merge(array_fill_keys(array_keys(self::$cookieParts), null), array(
+ 'cookies' => array(),
+ 'data' => array(),
+ 'path' => $path ?: '/',
+ 'http_only' => false,
+ 'discard' => false,
+ 'domain' => $host
+ ));
+ $foundNonCookies = 0;
+
+ // Add the cookie pieces into the parsed data array
+ foreach ($pieces as $part) {
+
+ $cookieParts = explode('=', $part, 2);
+ $key = trim($cookieParts[0]);
+
+ if (count($cookieParts) == 1) {
+ // Can be a single value (e.g. secure, httpOnly)
+ $value = true;
+ } else {
+ // Be sure to strip wrapping quotes
+ $value = trim($cookieParts[1], " \n\r\t\0\x0B\"");
+ if ($decode) {
+ $value = urldecode($value);
+ }
+ }
+
+ // Only check for non-cookies when cookies have been found
+ if (!empty($data['cookies'])) {
+ foreach (self::$cookieParts as $mapValue => $search) {
+ if (!strcasecmp($search, $key)) {
+ $data[$mapValue] = $mapValue == 'port' ? array_map('trim', explode(',', $value)) : $value;
+ $foundNonCookies++;
+ continue 2;
+ }
+ }
+ }
+
+ // If cookies have not yet been retrieved, or this value was not found in the pieces array, treat it as a
+ // cookie. IF non-cookies have been parsed, then this isn't a cookie, it's cookie data. Cookies then data.
+ $data[$foundNonCookies ? 'data' : 'cookies'][$key] = $value;
+ }
+
+ // Calculate the expires date
+ if (!$data['expires'] && $data['max_age']) {
+ $data['expires'] = time() + (int) $data['max_age'];
+ }
+
+ return $data;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Session/Storage/Proxy/VirtualProxy.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Session/Storage/Proxy/VirtualProxy.php
new file mode 100644
index 0000000..b478d03
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Session/Storage/Proxy/VirtualProxy.php
@@ -0,0 +1,54 @@
+saveHandlerName = 'user';
+ $this->_sessionName = ini_get('session.name');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getId() {
+ return $this->_sessionId;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setId($id) {
+ $this->_sessionId = $id;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName() {
+ return $this->_sessionName;
+ }
+
+ /**
+ * DO NOT CALL THIS METHOD
+ * @internal
+ */
+ public function setName($name) {
+ throw new \RuntimeException("Can not change session name in VirtualProxy");
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Session/Storage/VirtualSessionStorage.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Session/Storage/VirtualSessionStorage.php
new file mode 100644
index 0000000..daa10bb
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Session/Storage/VirtualSessionStorage.php
@@ -0,0 +1,88 @@
+setSaveHandler($handler);
+ $this->saveHandler->setId($sessionId);
+ $this->_serializer = $serializer;
+ $this->setMetadataBag(null);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function start() {
+ if ($this->started && !$this->closed) {
+ return true;
+ }
+
+ // You have to call Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler::open() to use
+ // pdo_sqlite (and possible pdo_*) as session storage, if you are using a DSN string instead of a \PDO object
+ // in the constructor. The method arguments are filled with the values, which are also used by the symfony
+ // framework in this case. This must not be the best choice, but it works.
+ $this->saveHandler->open(session_save_path(), session_name());
+
+ $rawData = $this->saveHandler->read($this->saveHandler->getId());
+ $sessionData = $this->_serializer->unserialize($rawData);
+
+ $this->loadSession($sessionData);
+
+ if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) {
+ $this->saveHandler->setActive(false);
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function regenerate($destroy = false, $lifetime = null) {
+ // .. ?
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function save() {
+ // get the data from the bags?
+ // serialize the data
+ // save the data using the saveHandler
+// $this->saveHandler->write($this->saveHandler->getId(),
+
+ if (!$this->saveHandler->isWrapper() && !$this->getSaveHandler()->isSessionHandlerInterface()) {
+ $this->saveHandler->setActive(false);
+ }
+
+ $this->closed = true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setSaveHandler($saveHandler = null) {
+ if (!($saveHandler instanceof \SessionHandlerInterface)) {
+ throw new \InvalidArgumentException('Handler must be instance of SessionHandlerInterface');
+ }
+
+ if (!($saveHandler instanceof VirtualProxy)) {
+ $saveHandler = new VirtualProxy($saveHandler);
+ }
+
+ $this->saveHandler = $saveHandler;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/Exception.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/Exception.php
new file mode 100644
index 0000000..6c824da
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/Exception.php
@@ -0,0 +1,5 @@
+_decorating = $serverComponent;
+ $this->connections = new \SplObjectStorage;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getSubProtocols() {
+ if ($this->_decorating instanceof WsServerInterface) {
+ $subs = $this->_decorating->getSubProtocols();
+ $subs[] = 'wamp';
+
+ return $subs;
+ }
+
+ return ['wamp'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onOpen(ConnectionInterface $conn) {
+ $decor = new WampConnection($conn);
+ $this->connections->attach($conn, $decor);
+
+ $this->_decorating->onOpen($decor);
+ }
+
+ /**
+ * {@inheritdoc}
+ * @throws \Ratchet\Wamp\Exception
+ * @throws \Ratchet\Wamp\JsonException
+ */
+ public function onMessage(ConnectionInterface $from, $msg) {
+ $from = $this->connections[$from];
+
+ if (null === ($json = @json_decode($msg, true))) {
+ throw new JsonException;
+ }
+
+ if (!is_array($json) || $json !== array_values($json)) {
+ throw new Exception("Invalid WAMP message format");
+ }
+
+ if (isset($json[1]) && !(is_string($json[1]) || is_numeric($json[1]))) {
+ throw new Exception('Invalid Topic, must be a string');
+ }
+
+ switch ($json[0]) {
+ case static::MSG_PREFIX:
+ $from->WAMP->prefixes[$json[1]] = $json[2];
+ break;
+
+ case static::MSG_CALL:
+ array_shift($json);
+ $callID = array_shift($json);
+ $procURI = array_shift($json);
+
+ if (count($json) == 1 && is_array($json[0])) {
+ $json = $json[0];
+ }
+
+ $this->_decorating->onCall($from, $callID, $from->getUri($procURI), $json);
+ break;
+
+ case static::MSG_SUBSCRIBE:
+ $this->_decorating->onSubscribe($from, $from->getUri($json[1]));
+ break;
+
+ case static::MSG_UNSUBSCRIBE:
+ $this->_decorating->onUnSubscribe($from, $from->getUri($json[1]));
+ break;
+
+ case static::MSG_PUBLISH:
+ $exclude = (array_key_exists(3, $json) ? $json[3] : null);
+ if (!is_array($exclude)) {
+ if (true === (boolean)$exclude) {
+ $exclude = [$from->WAMP->sessionId];
+ } else {
+ $exclude = [];
+ }
+ }
+
+ $eligible = (array_key_exists(4, $json) ? $json[4] : []);
+
+ $this->_decorating->onPublish($from, $from->getUri($json[1]), $json[2], $exclude, $eligible);
+ break;
+
+ default:
+ throw new Exception('Invalid WAMP message type');
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onClose(ConnectionInterface $conn) {
+ $decor = $this->connections[$conn];
+ $this->connections->detach($conn);
+
+ $this->_decorating->onClose($decor);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onError(ConnectionInterface $conn, \Exception $e) {
+ return $this->_decorating->onError($this->connections[$conn], $e);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/Topic.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/Topic.php
new file mode 100644
index 0000000..bca8f67
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/Topic.php
@@ -0,0 +1,99 @@
+id = $topicId;
+ $this->subscribers = new \SplObjectStorage;
+ }
+
+ /**
+ * @return string
+ */
+ public function getId() {
+ return $this->id;
+ }
+
+ public function __toString() {
+ return $this->getId();
+ }
+
+ /**
+ * Send a message to all the connections in this topic
+ * @param string|array $msg Payload to publish
+ * @param array $exclude A list of session IDs the message should be excluded from (blacklist)
+ * @param array $eligible A list of session Ids the message should be send to (whitelist)
+ * @return Topic The same Topic object to chain
+ */
+ public function broadcast($msg, array $exclude = array(), array $eligible = array()) {
+ $useEligible = (bool)count($eligible);
+ foreach ($this->subscribers as $client) {
+ if (in_array($client->WAMP->sessionId, $exclude)) {
+ continue;
+ }
+
+ if ($useEligible && !in_array($client->WAMP->sessionId, $eligible)) {
+ continue;
+ }
+
+ $client->event($this->id, $msg);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @param WampConnection $conn
+ * @return boolean
+ */
+ public function has(ConnectionInterface $conn) {
+ return $this->subscribers->contains($conn);
+ }
+
+ /**
+ * @param WampConnection $conn
+ * @return Topic
+ */
+ public function add(ConnectionInterface $conn) {
+ $this->subscribers->attach($conn);
+
+ return $this;
+ }
+
+ /**
+ * @param WampConnection $conn
+ * @return Topic
+ */
+ public function remove(ConnectionInterface $conn) {
+ if ($this->subscribers->contains($conn)) {
+ $this->subscribers->detach($conn);
+ }
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getIterator() {
+ return $this->subscribers;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function count() {
+ return $this->subscribers->count();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/TopicManager.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/TopicManager.php
new file mode 100644
index 0000000..dd06ada
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/TopicManager.php
@@ -0,0 +1,125 @@
+app = $app;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onOpen(ConnectionInterface $conn) {
+ $conn->WAMP->subscriptions = new \SplObjectStorage;
+ $this->app->onOpen($conn);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onCall(ConnectionInterface $conn, $id, $topic, array $params) {
+ $this->app->onCall($conn, $id, $this->getTopic($topic), $params);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onSubscribe(ConnectionInterface $conn, $topic) {
+ $topicObj = $this->getTopic($topic);
+
+ if ($conn->WAMP->subscriptions->contains($topicObj)) {
+ return;
+ }
+
+ $this->topicLookup[$topic]->add($conn);
+ $conn->WAMP->subscriptions->attach($topicObj);
+ $this->app->onSubscribe($conn, $topicObj);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onUnsubscribe(ConnectionInterface $conn, $topic) {
+ $topicObj = $this->getTopic($topic);
+
+ if (!$conn->WAMP->subscriptions->contains($topicObj)) {
+ return;
+ }
+
+ $this->cleanTopic($topicObj, $conn);
+
+ $this->app->onUnsubscribe($conn, $topicObj);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude, array $eligible) {
+ $this->app->onPublish($conn, $this->getTopic($topic), $event, $exclude, $eligible);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onClose(ConnectionInterface $conn) {
+ $this->app->onClose($conn);
+
+ foreach ($this->topicLookup as $topic) {
+ $this->cleanTopic($topic, $conn);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onError(ConnectionInterface $conn, \Exception $e) {
+ $this->app->onError($conn, $e);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getSubProtocols() {
+ if ($this->app instanceof WsServerInterface) {
+ return $this->app->getSubProtocols();
+ }
+
+ return array();
+ }
+
+ /**
+ * @param string
+ * @return Topic
+ */
+ protected function getTopic($topic) {
+ if (!array_key_exists($topic, $this->topicLookup)) {
+ $this->topicLookup[$topic] = new Topic($topic);
+ }
+
+ return $this->topicLookup[$topic];
+ }
+
+ protected function cleanTopic(Topic $topic, ConnectionInterface $conn) {
+ if ($conn->WAMP->subscriptions->contains($topic)) {
+ $conn->WAMP->subscriptions->detach($topic);
+ }
+
+ $this->topicLookup[$topic->getId()]->remove($conn);
+
+ if (0 === $topic->count()) {
+ unset($this->topicLookup[$topic->getId()]);
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/WampConnection.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/WampConnection.php
new file mode 100644
index 0000000..dda1e4e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/WampConnection.php
@@ -0,0 +1,115 @@
+WAMP = new \StdClass;
+ $this->WAMP->sessionId = str_replace('.', '', uniqid(mt_rand(), true));
+ $this->WAMP->prefixes = array();
+
+ $this->send(json_encode(array(WAMP::MSG_WELCOME, $this->WAMP->sessionId, 1, \Ratchet\VERSION)));
+ }
+
+ /**
+ * Successfully respond to a call made by the client
+ * @param string $id The unique ID given by the client to respond to
+ * @param array $data an object or array
+ * @return WampConnection
+ */
+ public function callResult($id, $data = array()) {
+ return $this->send(json_encode(array(WAMP::MSG_CALL_RESULT, $id, $data)));
+ }
+
+ /**
+ * Respond with an error to a client call
+ * @param string $id The unique ID given by the client to respond to
+ * @param string $errorUri The URI given to identify the specific error
+ * @param string $desc A developer-oriented description of the error
+ * @param string $details An optional human readable detail message to send back
+ * @return WampConnection
+ */
+ public function callError($id, $errorUri, $desc = '', $details = null) {
+ if ($errorUri instanceof Topic) {
+ $errorUri = (string)$errorUri;
+ }
+
+ $data = array(WAMP::MSG_CALL_ERROR, $id, $errorUri, $desc);
+
+ if (null !== $details) {
+ $data[] = $details;
+ }
+
+ return $this->send(json_encode($data));
+ }
+
+ /**
+ * @param string $topic The topic to broadcast to
+ * @param mixed $msg Data to send with the event. Anything that is json'able
+ * @return WampConnection
+ */
+ public function event($topic, $msg) {
+ return $this->send(json_encode(array(WAMP::MSG_EVENT, (string)$topic, $msg)));
+ }
+
+ /**
+ * @param string $curie
+ * @param string $uri
+ * @return WampConnection
+ */
+ public function prefix($curie, $uri) {
+ $this->WAMP->prefixes[$curie] = (string)$uri;
+
+ return $this->send(json_encode(array(WAMP::MSG_PREFIX, $curie, (string)$uri)));
+ }
+
+ /**
+ * Get the full request URI from the connection object if a prefix has been established for it
+ * @param string $uri
+ * @return string
+ */
+ public function getUri($uri) {
+ $curieSeperator = ':';
+
+ if (preg_match('/http(s*)\:\/\//', $uri) == false) {
+ if (strpos($uri, $curieSeperator) !== false) {
+ list($prefix, $action) = explode($curieSeperator, $uri);
+
+ if(isset($this->WAMP->prefixes[$prefix]) === true){
+ return $this->WAMP->prefixes[$prefix] . '#' . $action;
+ }
+ }
+ }
+
+ return $uri;
+ }
+
+ /**
+ * @internal
+ */
+ public function send($data) {
+ $this->getConnection()->send($data);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function close($opt = null) {
+ $this->getConnection()->close($opt);
+ }
+
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/WampServer.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/WampServer.php
new file mode 100644
index 0000000..5d710aa
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/WampServer.php
@@ -0,0 +1,67 @@
+wampProtocol = new ServerProtocol(new TopicManager($app));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onOpen(ConnectionInterface $conn) {
+ $this->wampProtocol->onOpen($conn);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onMessage(ConnectionInterface $conn, $msg) {
+ try {
+ $this->wampProtocol->onMessage($conn, $msg);
+ } catch (Exception $we) {
+ $conn->close(1007);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onClose(ConnectionInterface $conn) {
+ $this->wampProtocol->onClose($conn);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onError(ConnectionInterface $conn, \Exception $e) {
+ $this->wampProtocol->onError($conn, $e);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getSubProtocols() {
+ return $this->wampProtocol->getSubProtocols();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/WampServerInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/WampServerInterface.php
new file mode 100644
index 0000000..15c521d
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/WampServerInterface.php
@@ -0,0 +1,43 @@
+connection = $conn;
+ $this->buffer = $buffer;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/MessageCallableInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/MessageCallableInterface.php
new file mode 100644
index 0000000..b5c094e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/MessageCallableInterface.php
@@ -0,0 +1,8 @@
+WebSocket->closing) {
+ if (!($msg instanceof DataInterface)) {
+ $msg = new Frame($msg);
+ }
+
+ $this->getConnection()->send($msg->getContents());
+ }
+
+ return $this;
+ }
+
+ /**
+ * @param int|\Ratchet\RFC6455\Messaging\DataInterface
+ */
+ public function close($code = 1000) {
+ if ($this->WebSocket->closing) {
+ return;
+ }
+
+ if ($code instanceof DataInterface) {
+ $this->send($code);
+ } else {
+ $this->send(new Frame(pack('n', $code), true, Frame::OP_CLOSE));
+ }
+
+ $this->getConnection()->close();
+
+ $this->WebSocket->closing = true;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/WsServer.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/WsServer.php
new file mode 100644
index 0000000..8030604
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/WsServer.php
@@ -0,0 +1,225 @@
+msgCb = function(ConnectionInterface $conn, MessageInterface $msg) {
+ $this->delegate->onMessage($conn, $msg);
+ };
+ } elseif ($component instanceof DataComponentInterface) {
+ $this->msgCb = function(ConnectionInterface $conn, MessageInterface $msg) {
+ $this->delegate->onMessage($conn, $msg->getPayload());
+ };
+ } else {
+ throw new \UnexpectedValueException('Expected instance of \Ratchet\WebSocket\MessageComponentInterface or \Ratchet\MessageComponentInterface');
+ }
+
+ if (bin2hex('✓') !== 'e29c93') {
+ throw new \DomainException('Bad encoding, unicode character ✓ did not match expected value. Ensure charset UTF-8 and check ini val mbstring.func_autoload');
+ }
+
+ $this->delegate = $component;
+ $this->connections = new \SplObjectStorage;
+
+ $this->closeFrameChecker = new CloseFrameChecker;
+ $this->handshakeNegotiator = new ServerNegotiator(new RequestVerifier);
+ $this->handshakeNegotiator->setStrictSubProtocolCheck(true);
+
+ if ($component instanceof WsServerInterface) {
+ $this->handshakeNegotiator->setSupportedSubProtocols($component->getSubProtocols());
+ }
+
+ $this->pongReceiver = function() {};
+
+ $reusableUnderflowException = new \UnderflowException;
+ $this->ueFlowFactory = function() use ($reusableUnderflowException) {
+ return $reusableUnderflowException;
+ };
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) {
+ if (null === $request) {
+ throw new \UnexpectedValueException('$request can not be null');
+ }
+
+ $conn->httpRequest = $request;
+
+ $conn->WebSocket = new \StdClass;
+ $conn->WebSocket->closing = false;
+
+ $response = $this->handshakeNegotiator->handshake($request)->withHeader('X-Powered-By', \Ratchet\VERSION);
+
+ $conn->send(gPsr\str($response));
+
+ if (101 !== $response->getStatusCode()) {
+ return $conn->close();
+ }
+
+ $wsConn = new WsConnection($conn);
+
+ $streamer = new MessageBuffer(
+ $this->closeFrameChecker,
+ function(MessageInterface $msg) use ($wsConn) {
+ $cb = $this->msgCb;
+ $cb($wsConn, $msg);
+ },
+ function(FrameInterface $frame) use ($wsConn) {
+ $this->onControlFrame($frame, $wsConn);
+ },
+ true,
+ $this->ueFlowFactory
+ );
+
+ $this->connections->attach($conn, new ConnContext($wsConn, $streamer));
+
+ return $this->delegate->onOpen($wsConn);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onMessage(ConnectionInterface $from, $msg) {
+ if ($from->WebSocket->closing) {
+ return;
+ }
+
+ $this->connections[$from]->buffer->onData($msg);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onClose(ConnectionInterface $conn) {
+ if ($this->connections->contains($conn)) {
+ $context = $this->connections[$conn];
+ $this->connections->detach($conn);
+
+ $this->delegate->onClose($context->connection);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onError(ConnectionInterface $conn, \Exception $e) {
+ if ($this->connections->contains($conn)) {
+ $this->delegate->onError($this->connections[$conn]->connection, $e);
+ } else {
+ $conn->close();
+ }
+ }
+
+ public function onControlFrame(FrameInterface $frame, WsConnection $conn) {
+ switch ($frame->getOpCode()) {
+ case Frame::OP_CLOSE:
+ $conn->close($frame);
+ break;
+ case Frame::OP_PING:
+ $conn->send(new Frame($frame->getPayload(), true, Frame::OP_PONG));
+ break;
+ case Frame::OP_PONG:
+ $pongReceiver = $this->pongReceiver;
+ $pongReceiver($frame, $conn);
+ break;
+ }
+ }
+
+ public function setStrictSubProtocolCheck($enable) {
+ $this->handshakeNegotiator->setStrictSubProtocolCheck($enable);
+ }
+
+ public function enableKeepAlive(LoopInterface $loop, $interval = 30) {
+ $lastPing = new Frame(uniqid(), true, Frame::OP_PING);
+ $pingedConnections = new \SplObjectStorage;
+ $splClearer = new \SplObjectStorage;
+
+ $this->pongReceiver = function(FrameInterface $frame, $wsConn) use ($pingedConnections, &$lastPing) {
+ if ($frame->getPayload() === $lastPing->getPayload()) {
+ $pingedConnections->detach($wsConn);
+ }
+ };
+
+ $loop->addPeriodicTimer((int)$interval, function() use ($pingedConnections, &$lastPing, $splClearer) {
+ foreach ($pingedConnections as $wsConn) {
+ $wsConn->close();
+ }
+ $pingedConnections->removeAllExcept($splClearer);
+
+ $lastPing = new Frame(uniqid(), true, Frame::OP_PING);
+
+ foreach ($this->connections as $key => $conn) {
+ $wsConn = $this->connections[$conn]->connection;
+
+ $wsConn->send($lastPing);
+ $pingedConnections->attach($wsConn);
+ }
+ });
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/WsServerInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/WsServerInterface.php
new file mode 100644
index 0000000..15d1f7b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/WsServerInterface.php
@@ -0,0 +1,14 @@
+send($msg);
+ }
+
+ public function onOpen(ConnectionInterface $conn) {
+ }
+
+ public function onClose(ConnectionInterface $conn) {
+ }
+
+ public function onError(ConnectionInterface $conn, \Exception $e) {
+ }
+}
+
+ $port = $argc > 1 ? $argv[1] : 8000;
+ $impl = sprintf('React\EventLoop\%sLoop', $argc > 2 ? $argv[2] : 'StreamSelect');
+
+ $loop = new $impl;
+ $sock = new React\Socket\Server('0.0.0.0:' . $port, $loop);
+
+ $wsServer = new Ratchet\WebSocket\WsServer(new BinaryEcho);
+ // This is enabled to test https://github.com/ratchetphp/Ratchet/issues/430
+ // The time is left at 10 minutes so that it will not try to every ping anything
+ // This causes the Ratchet server to crash on test 2.7
+ $wsServer->enableKeepAlive($loop, 600);
+
+ $app = new Ratchet\Http\HttpServer($wsServer);
+
+ $server = new Ratchet\Server\IoServer($app, $sock, $loop);
+ $server->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/autobahn/fuzzingclient-all.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/autobahn/fuzzingclient-all.json
new file mode 100644
index 0000000..0494cf3
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/autobahn/fuzzingclient-all.json
@@ -0,0 +1,15 @@
+{
+ "options": {"failByDrop": false}
+ , "outdir": "reports/ab"
+
+ , "servers": [
+ {"agent": "Ratchet/0.4 libevent", "url": "ws://localhost:8001", "options": {"version": 18}}
+ , {"agent": "Ratchet/0.4 libev", "url": "ws://localhost:8004", "options": {"version": 18}}
+ , {"agent": "Ratchet/0.4 streams", "url": "ws://localhost:8002", "options": {"version": 18}}
+ , {"agent": "AutobahnTestSuite/0.5.9", "url": "ws://localhost:8000", "options": {"version": 18}}
+ ]
+
+ , "cases": ["*"]
+ , "exclude-cases": []
+ , "exclude-agent-cases": {}
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/autobahn/fuzzingclient-profile.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/autobahn/fuzzingclient-profile.json
new file mode 100644
index 0000000..e81a9fd
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/autobahn/fuzzingclient-profile.json
@@ -0,0 +1,12 @@
+{
+ "options": {"failByDrop": false}
+ , "outdir": "reports/profile"
+
+ , "servers": [
+ {"agent": "Ratchet", "url": "ws://localhost:8000", "options": {"version": 18}}
+ ]
+
+ , "cases": ["9.7.4"]
+ , "exclude-cases": ["1.2.*", "2.3", "2.4", "2.6", "9.2.*", "9.4.*", "9.6.*", "9.8.*"]
+ , "exclude-agent-cases": {}
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/autobahn/fuzzingclient-quick.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/autobahn/fuzzingclient-quick.json
new file mode 100644
index 0000000..c92e805
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/autobahn/fuzzingclient-quick.json
@@ -0,0 +1,12 @@
+{
+ "options": {"failByDrop": false}
+ , "outdir": "reports/rfc"
+
+ , "servers": [
+ {"agent": "Ratchet", "url": "ws://localhost:8000", "options": {"version": 18}}
+ ]
+
+ , "cases": ["*"]
+ , "exclude-cases": []
+ , "exclude-agent-cases": {}
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/bootstrap.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/bootstrap.php
new file mode 100644
index 0000000..40791ba
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/bootstrap.php
@@ -0,0 +1,4 @@
+addPsr4('Ratchet\\', __DIR__ . '/helpers/Ratchet');
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/AbstractMessageComponentTestCase.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/AbstractMessageComponentTestCase.php
new file mode 100644
index 0000000..8c298e5
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/AbstractMessageComponentTestCase.php
@@ -0,0 +1,50 @@
+_app = $this->getMock($this->getComponentClassString());
+ $decorator = $this->getDecoratorClassString();
+ $this->_serv = new $decorator($this->_app);
+ $this->_conn = $this->getMock('\Ratchet\ConnectionInterface');
+
+ $this->doOpen($this->_conn);
+ }
+
+ protected function doOpen($conn) {
+ $this->_serv->onOpen($conn);
+ }
+
+ public function isExpectedConnection() {
+ return new \PHPUnit_Framework_Constraint_IsInstanceOf($this->getConnectionClassString());
+ }
+
+ public function testOpen() {
+ $this->_app->expects($this->once())->method('onOpen')->with($this->isExpectedConnection());
+ $this->doOpen($this->getMock('\Ratchet\ConnectionInterface'));
+ }
+
+ public function testOnClose() {
+ $this->_app->expects($this->once())->method('onClose')->with($this->isExpectedConnection());
+ $this->_serv->onClose($this->_conn);
+ }
+
+ public function testOnError() {
+ $e = new \Exception('Whoops!');
+ $this->_app->expects($this->once())->method('onError')->with($this->isExpectedConnection(), $e);
+ $this->_serv->onError($this->_conn, $e);
+ }
+
+ public function passthroughMessageTest($value) {
+ $this->_app->expects($this->once())->method('onMessage')->with($this->isExpectedConnection(), $value);
+ $this->_serv->onMessage($this->_conn, $value);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/Component.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/Component.php
new file mode 100644
index 0000000..e152988
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/Component.php
@@ -0,0 +1,35 @@
+last[__FUNCTION__] = func_get_args();
+ }
+
+ public function onOpen(ConnectionInterface $conn) {
+ $this->last[__FUNCTION__] = func_get_args();
+ }
+
+ public function onMessage(ConnectionInterface $from, $msg) {
+ $this->last[__FUNCTION__] = func_get_args();
+ }
+
+ public function onClose(ConnectionInterface $conn) {
+ $this->last[__FUNCTION__] = func_get_args();
+ }
+
+ public function onError(ConnectionInterface $conn, \Exception $e) {
+ $this->last[__FUNCTION__] = func_get_args();
+ }
+
+ public function getSubProtocols() {
+ return $this->protocols;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/Connection.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/Connection.php
new file mode 100644
index 0000000..5918296
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/Connection.php
@@ -0,0 +1,20 @@
+ ''
+ , 'close' => false
+ );
+
+ public $remoteAddress = '127.0.0.1';
+
+ public function send($data) {
+ $this->last[__FUNCTION__] = $data;
+ }
+
+ public function close() {
+ $this->last[__FUNCTION__] = true;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/ConnectionDecorator.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/ConnectionDecorator.php
new file mode 100644
index 0000000..5570c07
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/ConnectionDecorator.php
@@ -0,0 +1,22 @@
+ ''
+ , 'end' => false
+ );
+
+ public function send($data) {
+ $this->last[__FUNCTION__] = $data;
+
+ $this->getConnection()->send($data);
+ }
+
+ public function close() {
+ $this->last[__FUNCTION__] = true;
+
+ $this->getConnection()->close();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/WampComponent.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/WampComponent.php
new file mode 100644
index 0000000..cd526cb
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/WampComponent.php
@@ -0,0 +1,43 @@
+protocols;
+ }
+
+ public function onCall(ConnectionInterface $conn, $id, $procURI, array $params) {
+ $this->last[__FUNCTION__] = func_get_args();
+ }
+
+ public function onSubscribe(ConnectionInterface $conn, $topic) {
+ $this->last[__FUNCTION__] = func_get_args();
+ }
+
+ public function onUnSubscribe(ConnectionInterface $conn, $topic) {
+ $this->last[__FUNCTION__] = func_get_args();
+ }
+
+ public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude, array $eligible) {
+ $this->last[__FUNCTION__] = func_get_args();
+ }
+
+ public function onOpen(ConnectionInterface $conn) {
+ $this->last[__FUNCTION__] = func_get_args();
+ }
+
+ public function onClose(ConnectionInterface $conn) {
+ $this->last[__FUNCTION__] = func_get_args();
+ }
+
+ public function onError(ConnectionInterface $conn, \Exception $e) {
+ $this->last[__FUNCTION__] = func_get_args();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/NullComponent.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/NullComponent.php
new file mode 100644
index 0000000..90def21
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/NullComponent.php
@@ -0,0 +1,28 @@
+mock = $this->getMock('\Ratchet\ConnectionInterface');
+ $this->l1 = new ConnectionDecorator($this->mock);
+ $this->l2 = new ConnectionDecorator($this->l1);
+ }
+
+ public function testGet() {
+ $var = 'hello';
+ $val = 'world';
+
+ $this->mock->$var = $val;
+
+ $this->assertEquals($val, $this->l1->$var);
+ $this->assertEquals($val, $this->l2->$var);
+ }
+
+ public function testSet() {
+ $var = 'Chris';
+ $val = 'Boden';
+
+ $this->l1->$var = $val;
+
+ $this->assertEquals($val, $this->mock->$var);
+ }
+
+ public function testSetLevel2() {
+ $var = 'Try';
+ $val = 'Again';
+
+ $this->l2->$var = $val;
+
+ $this->assertEquals($val, $this->mock->$var);
+ }
+
+ public function testIsSetTrue() {
+ $var = 'PHP';
+ $val = 'Ratchet';
+
+ $this->mock->$var = $val;
+
+ $this->assertTrue(isset($this->l1->$var));
+ $this->assertTrue(isset($this->l2->$var));
+ }
+
+ public function testIsSetFalse() {
+ $var = 'herp';
+ $val = 'derp';
+
+ $this->assertFalse(isset($this->l1->$var));
+ $this->assertFalse(isset($this->l2->$var));
+ }
+
+ public function testUnset() {
+ $var = 'Flying';
+ $val = 'Monkey';
+
+ $this->mock->$var = $val;
+ unset($this->l1->$var);
+
+ $this->assertFalse(isset($this->mock->$var));
+ }
+
+ public function testUnsetLevel2() {
+ $var = 'Flying';
+ $val = 'Monkey';
+
+ $this->mock->$var = $val;
+ unset($this->l2->$var);
+
+ $this->assertFalse(isset($this->mock->$var));
+ }
+
+ public function testGetConnection() {
+ $class = new \ReflectionClass('\\Ratchet\\AbstractConnectionDecorator');
+ $method = $class->getMethod('getConnection');
+ $method->setAccessible(true);
+
+ $conn = $method->invokeArgs($this->l1, array());
+
+ $this->assertSame($this->mock, $conn);
+ }
+
+ public function testGetConnectionLevel2() {
+ $class = new \ReflectionClass('\\Ratchet\\AbstractConnectionDecorator');
+ $method = $class->getMethod('getConnection');
+ $method->setAccessible(true);
+
+ $conn = $method->invokeArgs($this->l2, array());
+
+ $this->assertSame($this->l1, $conn);
+ }
+
+ public function testWrapperCanStoreSelfInDecorator() {
+ $this->mock->decorator = $this->l1;
+
+ $this->assertSame($this->l1, $this->l2->decorator);
+ }
+
+ public function testDecoratorRecursion() {
+ $this->mock->decorator = new \stdClass;
+ $this->mock->decorator->conn = $this->l1;
+
+ $this->assertSame($this->l1, $this->mock->decorator->conn);
+ $this->assertSame($this->l1, $this->l1->decorator->conn);
+ $this->assertSame($this->l1, $this->l2->decorator->conn);
+ }
+
+ public function testDecoratorRecursionLevel2() {
+ $this->mock->decorator = new \stdClass;
+ $this->mock->decorator->conn = $this->l2;
+
+ $this->assertSame($this->l2, $this->mock->decorator->conn);
+ $this->assertSame($this->l2, $this->l1->decorator->conn);
+ $this->assertSame($this->l2, $this->l2->decorator->conn);
+
+ // just for fun
+ $this->assertSame($this->l2, $this->l2->decorator->conn->decorator->conn->decorator->conn);
+ }
+
+ public function testWarningGettingNothing() {
+ $this->setExpectedException('PHPUnit_Framework_Error');
+ $var = $this->mock->nonExistant;
+ }
+
+ public function testWarningGettingNothingLevel1() {
+ $this->setExpectedException('PHPUnit_Framework_Error');
+ $var = $this->l1->nonExistant;
+ }
+
+ public function testWarningGettingNothingLevel2() {
+ $this->setExpectedException('PHPUnit_Framework_Error');
+ $var = $this->l2->nonExistant;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Http/HttpRequestParserTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Http/HttpRequestParserTest.php
new file mode 100644
index 0000000..6af8402
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Http/HttpRequestParserTest.php
@@ -0,0 +1,50 @@
+parser = new HttpRequestParser;
+ }
+
+ public function headersProvider() {
+ return array(
+ array(false, "GET / HTTP/1.1\r\nHost: socketo.me\r\n")
+ , array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n")
+ , array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n1")
+ , array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\nHixie✖")
+ , array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\nHixie✖\r\n\r\n")
+ , array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\nHixie\r\n")
+ );
+ }
+
+ /**
+ * @dataProvider headersProvider
+ */
+ public function testIsEom($expected, $message) {
+ $this->assertEquals($expected, $this->parser->isEom($message));
+ }
+
+ public function testBufferOverflowResponse() {
+ $conn = $this->getMock('\Ratchet\ConnectionInterface');
+
+ $this->parser->maxSize = 20;
+
+ $this->assertNull($this->parser->onMessage($conn, "GET / HTTP/1.1\r\n"));
+
+ $this->setExpectedException('OverflowException');
+
+ $this->parser->onMessage($conn, "Header-Is: Too Big");
+ }
+
+ public function testReturnTypeIsRequest() {
+ $conn = $this->getMock('\Ratchet\ConnectionInterface');
+ $return = $this->parser->onMessage($conn, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n");
+
+ $this->assertInstanceOf('\Psr\Http\Message\RequestInterface', $return);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Http/HttpServerTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Http/HttpServerTest.php
new file mode 100644
index 0000000..7041d66
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Http/HttpServerTest.php
@@ -0,0 +1,64 @@
+_conn->httpHeadersReceived = true;
+ }
+
+ public function getConnectionClassString() {
+ return '\Ratchet\ConnectionInterface';
+ }
+
+ public function getDecoratorClassString() {
+ return '\Ratchet\Http\HttpServer';
+ }
+
+ public function getComponentClassString() {
+ return '\Ratchet\Http\HttpServerInterface';
+ }
+
+ public function testOpen() {
+ $headers = "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n";
+
+ $this->_conn->httpHeadersReceived = false;
+ $this->_app->expects($this->once())->method('onOpen')->with($this->isExpectedConnection());
+ $this->_serv->onMessage($this->_conn, $headers);
+ }
+
+ public function testOnMessageAfterHeaders() {
+ $headers = "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n";
+ $this->_conn->httpHeadersReceived = false;
+ $this->_serv->onMessage($this->_conn, $headers);
+
+ $message = "Hello World!";
+ $this->_app->expects($this->once())->method('onMessage')->with($this->isExpectedConnection(), $message);
+ $this->_serv->onMessage($this->_conn, $message);
+ }
+
+ public function testBufferOverflow() {
+ $this->_conn->expects($this->once())->method('close');
+ $this->_conn->httpHeadersReceived = false;
+
+ $this->_serv->onMessage($this->_conn, str_repeat('a', 5000));
+ }
+
+ public function testCloseIfNotEstablished() {
+ $this->_conn->httpHeadersReceived = false;
+ $this->_conn->expects($this->once())->method('close');
+ $this->_serv->onError($this->_conn, new \Exception('Whoops!'));
+ }
+
+ public function testBufferHeaders() {
+ $this->_conn->httpHeadersReceived = false;
+ $this->_app->expects($this->never())->method('onOpen');
+ $this->_app->expects($this->never())->method('onMessage');
+
+ $this->_serv->onMessage($this->_conn, "GET / HTTP/1.1");
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Http/OriginCheckTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Http/OriginCheckTest.php
new file mode 100644
index 0000000..c1c4012
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Http/OriginCheckTest.php
@@ -0,0 +1,46 @@
+_reqStub = $this->getMock('Psr\Http\Message\RequestInterface');
+ $this->_reqStub->expects($this->any())->method('getHeader')->will($this->returnValue(['localhost']));
+
+ parent::setUp();
+
+ $this->_serv->allowedOrigins[] = 'localhost';
+ }
+
+ protected function doOpen($conn) {
+ $this->_serv->onOpen($conn, $this->_reqStub);
+ }
+
+ public function getConnectionClassString() {
+ return '\Ratchet\ConnectionInterface';
+ }
+
+ public function getDecoratorClassString() {
+ return '\Ratchet\Http\OriginCheck';
+ }
+
+ public function getComponentClassString() {
+ return '\Ratchet\Http\HttpServerInterface';
+ }
+
+ public function testCloseOnNonMatchingOrigin() {
+ $this->_serv->allowedOrigins = ['socketo.me'];
+ $this->_conn->expects($this->once())->method('close');
+
+ $this->_serv->onOpen($this->_conn, $this->_reqStub);
+ }
+
+ public function testOnMessage() {
+ $this->passthroughMessageTest('Hello World!');
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Http/RouterTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Http/RouterTest.php
new file mode 100644
index 0000000..1ca4cbc
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Http/RouterTest.php
@@ -0,0 +1,165 @@
+_conn = $this->getMock('\Ratchet\ConnectionInterface');
+ $this->_uri = $this->getMock('Psr\Http\Message\UriInterface');
+ $this->_req = $this->getMock('\Psr\Http\Message\RequestInterface');
+ $this->_req
+ ->expects($this->any())
+ ->method('getUri')
+ ->will($this->returnValue($this->_uri));
+ $this->_matcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');
+ $this->_matcher
+ ->expects($this->any())
+ ->method('getContext')
+ ->will($this->returnValue($this->getMock('Symfony\Component\Routing\RequestContext')));
+ $this->_router = new Router($this->_matcher);
+
+ $this->_uri->expects($this->any())->method('getPath')->will($this->returnValue('ws://doesnt.matter/'));
+ $this->_uri->expects($this->any())->method('withQuery')->with($this->callback(function($val) {
+ $this->setResult($val);
+
+ return true;
+ }))->will($this->returnSelf());
+ $this->_uri->expects($this->any())->method('getQuery')->will($this->returnCallback([$this, 'getResult']));
+ $this->_req->expects($this->any())->method('withUri')->will($this->returnSelf());
+ }
+
+ public function testFourOhFour() {
+ $this->_conn->expects($this->once())->method('close');
+
+ $nope = new ResourceNotFoundException;
+ $this->_matcher->expects($this->any())->method('match')->will($this->throwException($nope));
+
+ $this->_router->onOpen($this->_conn, $this->_req);
+ }
+
+ public function testNullRequest() {
+ $this->setExpectedException('\UnexpectedValueException');
+ $this->_router->onOpen($this->_conn);
+ }
+
+ public function testControllerIsMessageComponentInterface() {
+ $this->setExpectedException('\UnexpectedValueException');
+ $this->_matcher->expects($this->any())->method('match')->will($this->returnValue(array('_controller' => new \StdClass)));
+ $this->_router->onOpen($this->_conn, $this->_req);
+ }
+
+ public function testControllerOnOpen() {
+ $controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
+ $this->_matcher->expects($this->any())->method('match')->will($this->returnValue(array('_controller' => $controller)));
+ $this->_router->onOpen($this->_conn, $this->_req);
+
+ $expectedConn = new \PHPUnit_Framework_Constraint_IsInstanceOf('\Ratchet\ConnectionInterface');
+ $controller->expects($this->once())->method('onOpen')->with($expectedConn, $this->_req);
+
+ $this->_matcher->expects($this->any())->method('match')->will($this->returnValue(array('_controller' => $controller)));
+ $this->_router->onOpen($this->_conn, $this->_req);
+ }
+
+ public function testControllerOnMessageBubbles() {
+ $message = "The greatest trick the Devil ever pulled was convincing the world he didn't exist";
+ $controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
+ $controller->expects($this->once())->method('onMessage')->with($this->_conn, $message);
+
+ $this->_conn->controller = $controller;
+
+ $this->_router->onMessage($this->_conn, $message);
+ }
+
+ public function testControllerOnCloseBubbles() {
+ $controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
+ $controller->expects($this->once())->method('onClose')->with($this->_conn);
+
+ $this->_conn->controller = $controller;
+
+ $this->_router->onClose($this->_conn);
+ }
+
+ public function testControllerOnErrorBubbles() {
+ $e= new \Exception('One cannot be betrayed if one has no exceptions');
+ $controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
+ $controller->expects($this->once())->method('onError')->with($this->_conn, $e);
+
+ $this->_conn->controller = $controller;
+
+ $this->_router->onError($this->_conn, $e);
+ }
+
+ public function testRouterGeneratesRouteParameters() {
+ /** @var $controller WsServerInterface */
+ $controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
+ /** @var $matcher UrlMatcherInterface */
+ $this->_matcher->expects($this->any())->method('match')->will(
+ $this->returnValue(['_controller' => $controller, 'foo' => 'bar', 'baz' => 'qux'])
+ );
+ $conn = $this->getMock('Ratchet\Mock\Connection');
+
+ $router = new Router($this->_matcher);
+
+ $router->onOpen($conn, $this->_req);
+
+ $this->assertEquals('foo=bar&baz=qux', $this->_req->getUri()->getQuery());
+ }
+
+ public function testQueryParams() {
+ $controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
+ $this->_matcher->expects($this->any())->method('match')->will(
+ $this->returnValue(['_controller' => $controller, 'foo' => 'bar', 'baz' => 'qux'])
+ );
+
+ $conn = $this->getMock('Ratchet\Mock\Connection');
+ $request = $this->getMock('Psr\Http\Message\RequestInterface');
+ $uri = new \GuzzleHttp\Psr7\Uri('ws://doesnt.matter/endpoint?hello=world&foo=nope');
+
+ $request->expects($this->any())->method('getUri')->will($this->returnCallback(function() use (&$uri) {
+ return $uri;
+ }));
+ $request->expects($this->any())->method('withUri')->with($this->callback(function($url) use (&$uri) {
+ $uri = $url;
+
+ return true;
+ }))->will($this->returnSelf());
+
+ $router = new Router($this->_matcher);
+ $router->onOpen($conn, $request);
+
+ $this->assertEquals('foo=nope&baz=qux&hello=world', $request->getUri()->getQuery());
+ $this->assertEquals('ws', $request->getUri()->getScheme());
+ $this->assertEquals('doesnt.matter', $request->getUri()->getHost());
+ }
+
+ public function testImpatientClientOverflow() {
+ $this->_conn->expects($this->once())->method('close');
+
+ $header = "GET /nope HTTP/1.1
+Upgrade: websocket
+Connection: upgrade
+Host: localhost
+Origin: http://localhost
+Sec-WebSocket-Version: 13\r\n\r\n";
+
+ $app = new HttpServer(new Router(new UrlMatcher(new RouteCollection, new RequestContext)));
+ $app->onOpen($this->_conn);
+ $app->onMessage($this->_conn, $header);
+ $app->onMessage($this->_conn, 'Silly body');
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Server/EchoServerTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Server/EchoServerTest.php
new file mode 100644
index 0000000..47fb0e2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Server/EchoServerTest.php
@@ -0,0 +1,26 @@
+_conn = $this->getMock('\Ratchet\ConnectionInterface');
+ $this->_comp = new EchoServer;
+ }
+
+ public function testMessageEchod() {
+ $message = 'Tillsonburg, my back still aches when I hear that word.';
+ $this->_conn->expects($this->once())->method('send')->with($message);
+ $this->_comp->onMessage($this->_conn, $message);
+ }
+
+ public function testErrorClosesConnection() {
+ ob_start();
+ $this->_conn->expects($this->once())->method('close');
+ $this->_comp->onError($this->_conn, new \Exception);
+ ob_end_clean();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Server/FlashPolicyComponentTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Server/FlashPolicyComponentTest.php
new file mode 100644
index 0000000..38fc96a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Server/FlashPolicyComponentTest.php
@@ -0,0 +1,152 @@
+_policy = new FlashPolicy();
+ }
+
+ public function testPolicyRender() {
+ $this->_policy->setSiteControl('all');
+ $this->_policy->addAllowedAccess('example.com', '*');
+ $this->_policy->addAllowedAccess('dev.example.com', '*');
+
+ $this->assertInstanceOf('SimpleXMLElement', $this->_policy->renderPolicy());
+ }
+
+ public function testInvalidPolicyReader() {
+ $this->setExpectedException('UnexpectedValueException');
+ $this->_policy->renderPolicy();
+ }
+
+ public function testInvalidDomainPolicyReader() {
+ $this->setExpectedException('UnexpectedValueException');
+ $this->_policy->setSiteControl('all');
+ $this->_policy->addAllowedAccess('dev.example.*', '*');
+ $this->_policy->renderPolicy();
+ }
+
+ /**
+ * @dataProvider siteControl
+ */
+ public function testSiteControlValidation($accept, $permittedCrossDomainPolicies) {
+ $this->assertEquals($accept, $this->_policy->validateSiteControl($permittedCrossDomainPolicies));
+ }
+
+ public static function siteControl() {
+ return array(
+ array(true, 'all')
+ , array(true, 'none')
+ , array(true, 'master-only')
+ , array(false, 'by-content-type')
+ , array(false, 'by-ftp-filename')
+ , array(false, '')
+ , array(false, 'all ')
+ , array(false, 'asdf')
+ , array(false, '@893830')
+ , array(false, '*')
+ );
+ }
+
+ /**
+ * @dataProvider URI
+ */
+ public function testDomainValidation($accept, $domain) {
+ $this->assertEquals($accept, $this->_policy->validateDomain($domain));
+ }
+
+ public static function URI() {
+ return array(
+ array(true, '*')
+ , array(true, 'example.com')
+ , array(true, 'exam-ple.com')
+ , array(true, '*.example.com')
+ , array(true, 'www.example.com')
+ , array(true, 'dev.dev.example.com')
+ , array(true, 'http://example.com')
+ , array(true, 'https://example.com')
+ , array(true, 'http://*.example.com')
+ , array(false, 'exam*ple.com')
+ , array(true, '127.0.255.1')
+ , array(true, 'localhost')
+ , array(false, 'www.example.*')
+ , array(false, 'www.exa*le.com')
+ , array(false, 'www.example.*com')
+ , array(false, '*.example.*')
+ , array(false, 'gasldf*$#a0sdf0a8sdf')
+ );
+ }
+
+ /**
+ * @dataProvider ports
+ */
+ public function testPortValidation($accept, $ports) {
+ $this->assertEquals($accept, $this->_policy->validatePorts($ports));
+ }
+
+ public static function ports() {
+ return array(
+ array(true, '*')
+ , array(true, '80')
+ , array(true, '80,443')
+ , array(true, '507,516-523')
+ , array(true, '507,516-523,333')
+ , array(true, '507,516-523,507,516-523')
+ , array(false, '516-')
+ , array(true, '516-523,11')
+ , array(false, '516,-523,11')
+ , array(false, 'example')
+ , array(false, 'asdf,123')
+ , array(false, '--')
+ , array(false, ',,,')
+ , array(false, '838*')
+ );
+ }
+
+ public function testAddAllowedAccessOnlyAcceptsValidPorts() {
+ $this->setExpectedException('UnexpectedValueException');
+
+ $this->_policy->addAllowedAccess('*', 'nope');
+ }
+
+ public function testSetSiteControlThrowsException() {
+ $this->setExpectedException('UnexpectedValueException');
+
+ $this->_policy->setSiteControl('nope');
+ }
+
+ public function testErrorClosesConnection() {
+ $conn = $this->getMock('\\Ratchet\\ConnectionInterface');
+ $conn->expects($this->once())->method('close');
+
+ $this->_policy->onError($conn, new \Exception);
+ }
+
+ public function testOnMessageSendsString() {
+ $this->_policy->addAllowedAccess('*', '*');
+
+ $conn = $this->getMock('\\Ratchet\\ConnectionInterface');
+ $conn->expects($this->once())->method('send')->with($this->isType('string'));
+
+ $this->_policy->onMessage($conn, ' ');
+ }
+
+ public function testOnOpenExists() {
+ $this->assertTrue(method_exists($this->_policy, 'onOpen'));
+ $conn = $this->getMock('\Ratchet\ConnectionInterface');
+ $this->_policy->onOpen($conn);
+ }
+
+ public function testOnCloseExists() {
+ $this->assertTrue(method_exists($this->_policy, 'onClose'));
+ $conn = $this->getMock('\Ratchet\ConnectionInterface');
+ $this->_policy->onClose($conn);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Server/IoConnectionTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Server/IoConnectionTest.php
new file mode 100644
index 0000000..07130f6
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Server/IoConnectionTest.php
@@ -0,0 +1,32 @@
+sock = $this->getMock('\\React\\Socket\\ConnectionInterface');
+ $this->conn = new IoConnection($this->sock);
+ }
+
+ public function testCloseBubbles() {
+ $this->sock->expects($this->once())->method('end');
+ $this->conn->close();
+ }
+
+ public function testSendBubbles() {
+ $msg = '6 hour rides are productive';
+
+ $this->sock->expects($this->once())->method('write')->with($msg);
+ $this->conn->send($msg);
+ }
+
+ public function testSendReturnsSelf() {
+ $this->assertSame($this->conn, $this->conn->send('fluent interface'));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Server/IoServerTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Server/IoServerTest.php
new file mode 100644
index 0000000..284fbde
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Server/IoServerTest.php
@@ -0,0 +1,118 @@
+app = $this->getMock('\\Ratchet\\MessageComponentInterface');
+
+ $loop = new StreamSelectLoop;
+ $this->reactor = new Server(0, $loop);
+
+ $uri = $this->reactor->getAddress();
+ $this->port = parse_url((strpos($uri, '://') === false ? 'tcp://' : '') . $uri, PHP_URL_PORT);
+ $this->server = new IoServer($this->app, $this->reactor, $loop);
+ }
+
+ public function testOnOpen() {
+ $this->app->expects($this->once())->method('onOpen')->with($this->isInstanceOf('\\Ratchet\\ConnectionInterface'));
+
+ $client = stream_socket_client("tcp://localhost:{$this->port}");
+
+ $this->server->loop->tick();
+
+ //$this->assertTrue(is_string($this->app->last['onOpen'][0]->remoteAddress));
+ //$this->assertTrue(is_int($this->app->last['onOpen'][0]->resourceId));
+ }
+
+ public function testOnData() {
+ $msg = 'Hello World!';
+
+ $this->app->expects($this->once())->method('onMessage')->with(
+ $this->isInstanceOf('\\Ratchet\\ConnectionInterface')
+ , $msg
+ );
+
+ $client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
+ socket_set_option($client, SOL_SOCKET, SO_REUSEADDR, 1);
+ socket_set_option($client, SOL_SOCKET, SO_SNDBUF, 4096);
+ socket_set_block($client);
+ socket_connect($client, 'localhost', $this->port);
+
+ $this->server->loop->tick();
+
+ socket_write($client, $msg);
+ $this->server->loop->tick();
+
+ socket_shutdown($client, 1);
+ socket_shutdown($client, 0);
+ socket_close($client);
+
+ $this->server->loop->tick();
+ }
+
+ public function testOnClose() {
+ $this->app->expects($this->once())->method('onClose')->with($this->isInstanceOf('\\Ratchet\\ConnectionInterface'));
+
+ $client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
+ socket_set_option($client, SOL_SOCKET, SO_REUSEADDR, 1);
+ socket_set_option($client, SOL_SOCKET, SO_SNDBUF, 4096);
+ socket_set_block($client);
+ socket_connect($client, 'localhost', $this->port);
+
+ $this->server->loop->tick();
+
+ socket_shutdown($client, 1);
+ socket_shutdown($client, 0);
+ socket_close($client);
+
+ $this->server->loop->tick();
+ }
+
+ public function testFactory() {
+ $this->assertInstanceOf('\\Ratchet\\Server\\IoServer', IoServer::factory($this->app, 0));
+ }
+
+ public function testNoLoopProvidedError() {
+ $this->setExpectedException('RuntimeException');
+
+ $io = new IoServer($this->app, $this->reactor);
+ $io->run();
+ }
+
+ public function testOnErrorPassesException() {
+ $conn = $this->getMock('\\React\\Socket\\ConnectionInterface');
+ $conn->decor = $this->getMock('\\Ratchet\\ConnectionInterface');
+ $err = new \Exception("Nope");
+
+ $this->app->expects($this->once())->method('onError')->with($conn->decor, $err);
+
+ $this->server->handleError($err, $conn);
+ }
+
+ public function onErrorCalledWhenExceptionThrown() {
+ $this->markTestIncomplete("Need to learn how to throw an exception from a mock");
+
+ $conn = $this->getMock('\\React\\Socket\\ConnectionInterface');
+ $this->server->handleConnect($conn);
+
+ $e = new \Exception;
+ $this->app->expects($this->once())->method('onMessage')->with($this->isInstanceOf('\\Ratchet\\ConnectionInterface'), 'f')->will($e);
+ $this->app->expects($this->once())->method('onError')->with($this->instanceOf('\\Ratchet\\ConnectionInterface', $e));
+
+ $this->server->handleData('f', $conn);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Server/IpBlackListComponentTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Server/IpBlackListComponentTest.php
new file mode 100644
index 0000000..90f4185
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Server/IpBlackListComponentTest.php
@@ -0,0 +1,125 @@
+mock = $this->getMock('\\Ratchet\\MessageComponentInterface');
+ $this->blocker = new IpBlackList($this->mock);
+ }
+
+ public function testOnOpen() {
+ $this->mock->expects($this->exactly(3))->method('onOpen');
+
+ $conn1 = $this->newConn();
+ $conn2 = $this->newConn();
+ $conn3 = $this->newConn();
+
+ $this->blocker->onOpen($conn1);
+ $this->blocker->onOpen($conn3);
+ $this->blocker->onOpen($conn2);
+ }
+
+ public function testBlockDoesNotTriggerOnOpen() {
+ $conn = $this->newConn();
+
+ $this->blocker->blockAddress($conn->remoteAddress);
+
+ $this->mock->expects($this->never())->method('onOpen');
+
+ $ret = $this->blocker->onOpen($conn);
+ }
+
+ public function testBlockDoesNotTriggerOnClose() {
+ $conn = $this->newConn();
+
+ $this->blocker->blockAddress($conn->remoteAddress);
+
+ $this->mock->expects($this->never())->method('onClose');
+
+ $ret = $this->blocker->onOpen($conn);
+ }
+
+ public function testOnMessageDecoration() {
+ $conn = $this->newConn();
+ $msg = 'Hello not being blocked';
+
+ $this->mock->expects($this->once())->method('onMessage')->with($conn, $msg);
+
+ $this->blocker->onMessage($conn, $msg);
+ }
+
+ public function testOnCloseDecoration() {
+ $conn = $this->newConn();
+
+ $this->mock->expects($this->once())->method('onClose')->with($conn);
+
+ $this->blocker->onClose($conn);
+ }
+
+ public function testBlockClosesConnection() {
+ $conn = $this->newConn();
+ $this->blocker->blockAddress($conn->remoteAddress);
+
+ $conn->expects($this->once())->method('close');
+
+ $this->blocker->onOpen($conn);
+ }
+
+ public function testAddAndRemoveWithFluentInterfaces() {
+ $blockOne = '127.0.0.1';
+ $blockTwo = '192.168.1.1';
+ $unblock = '75.119.207.140';
+
+ $this->blocker
+ ->blockAddress($unblock)
+ ->blockAddress($blockOne)
+ ->unblockAddress($unblock)
+ ->blockAddress($blockTwo)
+ ;
+
+ $this->assertEquals(array($blockOne, $blockTwo), $this->blocker->getBlockedAddresses());
+ }
+
+ public function testDecoratorPassesErrors() {
+ $conn = $this->newConn();
+ $e = new \Exception('I threw an error');
+
+ $this->mock->expects($this->once())->method('onError')->with($conn, $e);
+
+ $this->blocker->onError($conn, $e);
+ }
+
+ public function addressProvider() {
+ return array(
+ array('127.0.0.1', '127.0.0.1')
+ , array('localhost', 'localhost')
+ , array('fe80::1%lo0', 'fe80::1%lo0')
+ , array('127.0.0.1', '127.0.0.1:6392')
+ );
+ }
+
+ /**
+ * @dataProvider addressProvider
+ */
+ public function testFilterAddress($expected, $input) {
+ $this->assertEquals($expected, $this->blocker->filterAddress($input));
+ }
+
+ public function testUnblockingSilentlyFails() {
+ $this->assertInstanceOf('\\Ratchet\\Server\\IpBlackList', $this->blocker->unblockAddress('localhost'));
+ }
+
+ protected function newConn() {
+ $conn = $this->getMock('\\Ratchet\\ConnectionInterface');
+ $conn->remoteAddress = '127.0.0.1';
+
+ return $conn;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Session/Serialize/PhpHandlerTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Session/Serialize/PhpHandlerTest.php
new file mode 100644
index 0000000..4acf5bc
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Session/Serialize/PhpHandlerTest.php
@@ -0,0 +1,43 @@
+_handler = new PhpHandler;
+ }
+
+ public function serializedProvider() {
+ return array(
+ array(
+ '_sf2_attributes|a:2:{s:5:"hello";s:5:"world";s:4:"last";i:1332872102;}_sf2_flashes|a:0:{}'
+ , array(
+ '_sf2_attributes' => array(
+ 'hello' => 'world'
+ , 'last' => 1332872102
+ )
+ , '_sf2_flashes' => array()
+ )
+ )
+ );
+ }
+
+ /**
+ * @dataProvider serializedProvider
+ */
+ public function testUnserialize($in, $expected) {
+ $this->assertEquals($expected, $this->_handler->unserialize($in));
+ }
+
+ /**
+ * @dataProvider serializedProvider
+ */
+ public function testSerialize($serialized, $original) {
+ $this->assertEquals($serialized, $this->_handler->serialize($original));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Session/SessionComponentTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Session/SessionComponentTest.php
new file mode 100644
index 0000000..ebfdde4
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Session/SessionComponentTest.php
@@ -0,0 +1,124 @@
+markTestSkipped('Dependency of Symfony HttpFoundation failed');
+ }
+
+ parent::setUp();
+ $this->_serv = new SessionProvider($this->_app, new NullSessionHandler);
+ }
+
+ public function tearDown() {
+ ini_set('session.serialize_handler', 'php');
+ }
+
+ public function getConnectionClassString() {
+ return '\Ratchet\ConnectionInterface';
+ }
+
+ public function getDecoratorClassString() {
+ return '\Ratchet\NullComponent';
+ }
+
+ public function getComponentClassString() {
+ return '\Ratchet\Http\HttpServerInterface';
+ }
+
+ public function classCaseProvider() {
+ return array(
+ array('php', 'Php')
+ , array('php_binary', 'PhpBinary')
+ );
+ }
+
+ /**
+ * @dataProvider classCaseProvider
+ */
+ public function testToClassCase($in, $out) {
+ $ref = new \ReflectionClass('\\Ratchet\\Session\\SessionProvider');
+ $method = $ref->getMethod('toClassCase');
+ $method->setAccessible(true);
+
+ $component = new SessionProvider($this->getMock($this->getComponentClassString()), $this->getMock('\SessionHandlerInterface'));
+ $this->assertEquals($out, $method->invokeArgs($component, array($in)));
+ }
+
+ /**
+ * I think I have severely butchered this test...it's not so much of a unit test as it is a full-fledged component test
+ */
+ public function testConnectionValueFromPdo() {
+ if (!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) {
+ return $this->markTestSkipped('Session test requires PDO and pdo_sqlite');
+ }
+
+ $sessionId = md5('testSession');
+
+ $dbOptions = array(
+ 'db_table' => 'sessions'
+ , 'db_id_col' => 'sess_id'
+ , 'db_data_col' => 'sess_data'
+ , 'db_time_col' => 'sess_time'
+ , 'db_lifetime_col' => 'sess_lifetime'
+ );
+
+ $pdo = new \PDO("sqlite::memory:");
+ $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+ $pdo->exec(vsprintf("CREATE TABLE %s (%s TEXT NOT NULL PRIMARY KEY, %s BLOB NOT NULL, %s INTEGER NOT NULL, %s INTEGER)", $dbOptions));
+
+ $pdoHandler = new PdoSessionHandler($pdo, $dbOptions);
+ $pdoHandler->write($sessionId, '_sf2_attributes|a:2:{s:5:"hello";s:5:"world";s:4:"last";i:1332872102;}_sf2_flashes|a:0:{}');
+
+ $component = new SessionProvider($this->getMock($this->getComponentClassString()), $pdoHandler, array('auto_start' => 1));
+ $connection = $this->getMock('Ratchet\\ConnectionInterface');
+
+ $headers = $this->getMock('Psr\Http\Message\RequestInterface');
+ $headers->expects($this->once())->method('getHeader')->will($this->returnValue([ini_get('session.name') . "={$sessionId};"]));
+
+ $component->onOpen($connection, $headers);
+
+ $this->assertEquals('world', $connection->Session->get('hello'));
+ }
+
+ protected function newConn() {
+ $conn = $this->getMock('Ratchet\ConnectionInterface');
+
+ $headers = $this->getMock('Psr\Http\Message\Request', array('getCookie'), array('POST', '/', array()));
+ $headers->expects($this->once())->method('getCookie', array(ini_get('session.name')))->will($this->returnValue(null));
+
+ return $conn;
+ }
+
+ public function testOnMessageDecorator() {
+ $message = "Database calls are usually blocking :(";
+ $this->_app->expects($this->once())->method('onMessage')->with($this->isExpectedConnection(), $message);
+ $this->_serv->onMessage($this->_conn, $message);
+ }
+
+ public function testRejectInvalidSeralizers() {
+ if (!function_exists('wddx_serialize_value')) {
+ $this->markTestSkipped();
+ }
+
+ ini_set('session.serialize_handler', 'wddx');
+ $this->setExpectedException('\RuntimeException');
+ new SessionProvider($this->getMock($this->getComponentClassString()), $this->getMock('\SessionHandlerInterface'));
+ }
+
+ protected function doOpen($conn) {
+ $request = $this->getMock('Psr\Http\Message\RequestInterface');
+ $request->expects($this->any())->method('getHeader')->will($this->returnValue([]));
+
+ $this->_serv->onOpen($conn, $request);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Session/Storage/VirtualSessionStoragePDOTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Session/Storage/VirtualSessionStoragePDOTest.php
new file mode 100644
index 0000000..2727484
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Session/Storage/VirtualSessionStoragePDOTest.php
@@ -0,0 +1,53 @@
+markTestSkipped('Session test requires PDO and pdo_sqlite');
+ }
+
+ $schema = <<
_pathToDB = tempnam(sys_get_temp_dir(), 'SQ3');;
+ $dsn = 'sqlite:' . $this->_pathToDB;
+
+ $pdo = new \PDO($dsn);
+ $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+ $pdo->exec($schema);
+ $pdo = null;
+
+ $sessionHandler = new PdoSessionHandler($dsn);
+ $serializer = new PhpHandler();
+ $this->_virtualSessionStorage = new VirtualSessionStorage($sessionHandler, 'foobar', $serializer);
+ $this->_virtualSessionStorage->registerBag(new FlashBag());
+ $this->_virtualSessionStorage->registerBag(new AttributeBag());
+ }
+
+ public function tearDown() {
+ unlink($this->_pathToDB);
+ }
+
+ public function testStartWithDSN() {
+ $this->_virtualSessionStorage->start();
+
+ $this->assertTrue($this->_virtualSessionStorage->isStarted());
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Wamp/ServerProtocolTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Wamp/ServerProtocolTest.php
new file mode 100644
index 0000000..8ff68c2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Wamp/ServerProtocolTest.php
@@ -0,0 +1,295 @@
+_app = new TestComponent;
+ $this->_comp = new ServerProtocol($this->_app);
+ }
+
+ protected function newConn() {
+ return new Connection;
+ }
+
+ public function invalidMessageProvider() {
+ return [
+ [0]
+ , [3]
+ , [4]
+ , [8]
+ , [9]
+ ];
+ }
+
+ /**
+ * @dataProvider invalidMessageProvider
+ */
+ public function testInvalidMessages($type) {
+ $this->setExpectedException('\Ratchet\Wamp\Exception');
+
+ $conn = $this->newConn();
+ $this->_comp->onOpen($conn);
+ $this->_comp->onMessage($conn, json_encode([$type]));
+ }
+
+ public function testWelcomeMessage() {
+ $conn = $this->newConn();
+
+ $this->_comp->onOpen($conn);
+
+ $message = $conn->last['send'];
+ $json = json_decode($message);
+
+ $this->assertEquals(4, count($json));
+ $this->assertEquals(0, $json[0]);
+ $this->assertTrue(is_string($json[1]));
+ $this->assertEquals(1, $json[2]);
+ }
+
+ public function testSubscribe() {
+ $uri = 'http://example.com';
+ $clientMessage = array(5, $uri);
+
+ $conn = $this->newConn();
+
+ $this->_comp->onOpen($conn);
+ $this->_comp->onMessage($conn, json_encode($clientMessage));
+
+ $this->assertEquals($uri, $this->_app->last['onSubscribe'][1]);
+ }
+
+ public function testUnSubscribe() {
+ $uri = 'http://example.com/endpoint';
+ $clientMessage = array(6, $uri);
+
+ $conn = $this->newConn();
+
+ $this->_comp->onOpen($conn);
+ $this->_comp->onMessage($conn, json_encode($clientMessage));
+
+ $this->assertEquals($uri, $this->_app->last['onUnSubscribe'][1]);
+ }
+
+ public function callProvider() {
+ return [
+ [2, 'a', 'b']
+ , [2, ['a', 'b']]
+ , [1, 'one']
+ , [3, 'one', 'two', 'three']
+ , [3, ['un', 'deux', 'trois']]
+ , [2, 'hi', ['hello', 'world']]
+ , [2, ['hello', 'world'], 'hi']
+ , [2, ['hello' => 'world', 'herp' => 'derp']]
+ ];
+ }
+
+ /**
+ * @dataProvider callProvider
+ */
+ public function testCall() {
+ $args = func_get_args();
+ $paramNum = array_shift($args);
+
+ $uri = 'http://example.com/endpoint/' . rand(1, 100);
+ $id = uniqid('', false);
+ $clientMessage = array_merge(array(2, $id, $uri), $args);
+
+ $conn = $this->newConn();
+
+ $this->_comp->onOpen($conn);
+ $this->_comp->onMessage($conn, json_encode($clientMessage));
+
+ $this->assertEquals($id, $this->_app->last['onCall'][1]);
+ $this->assertEquals($uri, $this->_app->last['onCall'][2]);
+
+ $this->assertEquals($paramNum, count($this->_app->last['onCall'][3]));
+ }
+
+ public function testPublish() {
+ $conn = $this->newConn();
+
+ $topic = 'pubsubhubbub';
+ $event = 'Here I am, publishing data';
+
+ $clientMessage = array(7, $topic, $event);
+
+ $this->_comp->onOpen($conn);
+ $this->_comp->onMessage($conn, json_encode($clientMessage));
+
+ $this->assertEquals($topic, $this->_app->last['onPublish'][1]);
+ $this->assertEquals($event, $this->_app->last['onPublish'][2]);
+ $this->assertEquals(array(), $this->_app->last['onPublish'][3]);
+ $this->assertEquals(array(), $this->_app->last['onPublish'][4]);
+ }
+
+ public function testPublishAndExcludeMe() {
+ $conn = $this->newConn();
+
+ $this->_comp->onOpen($conn);
+ $this->_comp->onMessage($conn, json_encode(array(7, 'topic', 'event', true)));
+
+ $this->assertEquals($conn->WAMP->sessionId, $this->_app->last['onPublish'][3][0]);
+ }
+
+ public function testPublishAndEligible() {
+ $conn = $this->newConn();
+
+ $buddy = uniqid('', false);
+ $friend = uniqid('', false);
+
+ $this->_comp->onOpen($conn);
+ $this->_comp->onMessage($conn, json_encode(array(7, 'topic', 'event', false, array($buddy, $friend))));
+
+ $this->assertEquals(array(), $this->_app->last['onPublish'][3]);
+ $this->assertEquals(2, count($this->_app->last['onPublish'][4]));
+ }
+
+ public function eventProvider() {
+ return array(
+ array('http://example.com', array('one', 'two'))
+ , array('curie', array(array('hello' => 'world', 'herp' => 'derp')))
+ );
+ }
+
+ /**
+ * @dataProvider eventProvider
+ */
+ public function testEvent($topic, $payload) {
+ $conn = new WampConnection($this->newConn());
+ $conn->event($topic, $payload);
+
+ $eventString = $conn->last['send'];
+
+ $this->assertSame(array(8, $topic, $payload), json_decode($eventString, true));
+ }
+
+ public function testOnClosePropagation() {
+ $conn = new Connection;
+
+ $this->_comp->onOpen($conn);
+ $this->_comp->onClose($conn);
+
+ $class = new \ReflectionClass('\\Ratchet\\Wamp\\WampConnection');
+ $method = $class->getMethod('getConnection');
+ $method->setAccessible(true);
+
+ $check = $method->invokeArgs($this->_app->last['onClose'][0], array());
+
+ $this->assertSame($conn, $check);
+ }
+
+ public function testOnErrorPropagation() {
+ $conn = new Connection;
+
+ $e = new \Exception('Nope');
+
+ $this->_comp->onOpen($conn);
+ $this->_comp->onError($conn, $e);
+
+ $class = new \ReflectionClass('\\Ratchet\\Wamp\\WampConnection');
+ $method = $class->getMethod('getConnection');
+ $method->setAccessible(true);
+
+ $check = $method->invokeArgs($this->_app->last['onError'][0], array());
+
+ $this->assertSame($conn, $check);
+ $this->assertSame($e, $this->_app->last['onError'][1]);
+ }
+
+ public function testPrefix() {
+ $conn = new WampConnection($this->newConn());
+ $this->_comp->onOpen($conn);
+
+ $prefix = 'incoming';
+ $fullURI = "http://example.com/$prefix";
+ $method = 'call';
+
+ $this->_comp->onMessage($conn, json_encode(array(1, $prefix, $fullURI)));
+
+ $this->assertEquals($fullURI, $conn->WAMP->prefixes[$prefix]);
+ $this->assertEquals("$fullURI#$method", $conn->getUri("$prefix:$method"));
+ }
+
+ public function testMessageMustBeJson() {
+ $this->setExpectedException('\\Ratchet\\Wamp\\JsonException');
+
+ $conn = new Connection;
+
+ $this->_comp->onOpen($conn);
+ $this->_comp->onMessage($conn, 'Hello World!');
+ }
+
+ public function testGetSubProtocolsReturnsArray() {
+ $this->assertTrue(is_array($this->_comp->getSubProtocols()));
+ }
+
+ public function testGetSubProtocolsGetFromApp() {
+ $this->_app->protocols = array('hello', 'world');
+
+ $this->assertGreaterThanOrEqual(3, count($this->_comp->getSubProtocols()));
+ }
+
+ public function testWampOnMessageApp() {
+ $app = $this->getMock('\\Ratchet\\Wamp\\WampServerInterface');
+ $wamp = new ServerProtocol($app);
+
+ $this->assertContains('wamp', $wamp->getSubProtocols());
+ }
+
+ public function badFormatProvider() {
+ return array(
+ array(json_encode(true))
+ , array('{"valid":"json", "invalid": "message"}')
+ , array('{"0": "fail", "hello": "world"}')
+ );
+ }
+
+ /**
+ * @dataProvider badFormatProvider
+ */
+ public function testValidJsonButInvalidProtocol($message) {
+ $this->setExpectedException('\Ratchet\Wamp\Exception');
+
+ $conn = $this->newConn();
+ $this->_comp->onOpen($conn);
+ $this->_comp->onMessage($conn, $message);
+ }
+
+ public function testBadClientInputFromNonStringTopic() {
+ $this->setExpectedException('\Ratchet\Wamp\Exception');
+
+ $conn = new WampConnection($this->newConn());
+ $this->_comp->onOpen($conn);
+
+ $this->_comp->onMessage($conn, json_encode([5, ['hells', 'nope']]));
+ }
+
+ public function testBadPrefixWithNonStringTopic() {
+ $this->setExpectedException('\Ratchet\Wamp\Exception');
+
+ $conn = new WampConnection($this->newConn());
+ $this->_comp->onOpen($conn);
+
+ $this->_comp->onMessage($conn, json_encode([1, ['hells', 'nope'], ['bad', 'input']]));
+ }
+
+ public function testBadPublishWithNonStringTopic() {
+ $this->setExpectedException('\Ratchet\Wamp\Exception');
+
+ $conn = new WampConnection($this->newConn());
+ $this->_comp->onOpen($conn);
+
+ $this->_comp->onMessage($conn, json_encode([7, ['bad', 'input'], 'Hider']));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Wamp/TopicManagerTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Wamp/TopicManagerTest.php
new file mode 100644
index 0000000..b21b6bc
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Wamp/TopicManagerTest.php
@@ -0,0 +1,226 @@
+conn = $this->getMock('\Ratchet\ConnectionInterface');
+ $this->mock = $this->getMock('\Ratchet\Wamp\WampServerInterface');
+ $this->mngr = new TopicManager($this->mock);
+
+ $this->conn->WAMP = new \StdClass;
+ $this->mngr->onOpen($this->conn);
+ }
+
+ public function testGetTopicReturnsTopicObject() {
+ $class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
+ $method = $class->getMethod('getTopic');
+ $method->setAccessible(true);
+
+ $topic = $method->invokeArgs($this->mngr, array('The Topic'));
+
+ $this->assertInstanceOf('Ratchet\Wamp\Topic', $topic);
+ }
+
+ public function testGetTopicCreatesTopicWithSameName() {
+ $name = 'The Topic';
+
+ $class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
+ $method = $class->getMethod('getTopic');
+ $method->setAccessible(true);
+
+ $topic = $method->invokeArgs($this->mngr, array($name));
+
+ $this->assertEquals($name, $topic->getId());
+ }
+
+ public function testGetTopicReturnsSameObject() {
+ $class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
+ $method = $class->getMethod('getTopic');
+ $method->setAccessible(true);
+
+ $topic = $method->invokeArgs($this->mngr, array('No copy'));
+ $again = $method->invokeArgs($this->mngr, array('No copy'));
+
+ $this->assertSame($topic, $again);
+ }
+
+ public function testOnOpen() {
+ $this->mock->expects($this->once())->method('onOpen');
+ $this->mngr->onOpen($this->conn);
+ }
+
+ public function testOnCall() {
+ $id = uniqid();
+
+ $this->mock->expects($this->once())->method('onCall')->with(
+ $this->conn
+ , $id
+ , $this->isInstanceOf('Ratchet\Wamp\Topic')
+ , array()
+ );
+
+ $this->mngr->onCall($this->conn, $id, 'new topic', array());
+ }
+
+ public function testOnSubscribeCreatesTopicObject() {
+ $this->mock->expects($this->once())->method('onSubscribe')->with(
+ $this->conn, $this->isInstanceOf('Ratchet\Wamp\Topic')
+ );
+
+ $this->mngr->onSubscribe($this->conn, 'new topic');
+ }
+
+ public function testTopicIsInConnectionOnSubscribe() {
+ $name = 'New Topic';
+
+ $class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
+ $method = $class->getMethod('getTopic');
+ $method->setAccessible(true);
+
+ $topic = $method->invokeArgs($this->mngr, array($name));
+
+ $this->mngr->onSubscribe($this->conn, $name);
+
+ $this->assertTrue($this->conn->WAMP->subscriptions->contains($topic));
+ }
+
+ public function testDoubleSubscriptionFiresOnce() {
+ $this->mock->expects($this->exactly(1))->method('onSubscribe');
+
+ $this->mngr->onSubscribe($this->conn, 'same topic');
+ $this->mngr->onSubscribe($this->conn, 'same topic');
+ }
+
+ public function testUnsubscribeEvent() {
+ $name = 'in and out';
+ $this->mock->expects($this->once())->method('onUnsubscribe')->with(
+ $this->conn, $this->isInstanceOf('Ratchet\Wamp\Topic')
+ );
+
+ $this->mngr->onSubscribe($this->conn, $name);
+ $this->mngr->onUnsubscribe($this->conn, $name);
+ }
+
+ public function testUnsubscribeFiresOnce() {
+ $name = 'getting sleepy';
+ $this->mock->expects($this->exactly(1))->method('onUnsubscribe');
+
+ $this->mngr->onSubscribe($this->conn, $name);
+ $this->mngr->onUnsubscribe($this->conn, $name);
+ $this->mngr->onUnsubscribe($this->conn, $name);
+ }
+
+ public function testUnsubscribeRemovesTopicFromConnection() {
+ $name = 'Bye Bye Topic';
+
+ $class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
+ $method = $class->getMethod('getTopic');
+ $method->setAccessible(true);
+
+ $topic = $method->invokeArgs($this->mngr, array($name));
+
+ $this->mngr->onSubscribe($this->conn, $name);
+ $this->mngr->onUnsubscribe($this->conn, $name);
+
+ $this->assertFalse($this->conn->WAMP->subscriptions->contains($topic));
+ }
+
+ public function testOnPublishBubbles() {
+ $msg = 'Cover all the code!';
+
+ $this->mock->expects($this->once())->method('onPublish')->with(
+ $this->conn
+ , $this->isInstanceOf('Ratchet\Wamp\Topic')
+ , $msg
+ , $this->isType('array')
+ , $this->isType('array')
+ );
+
+ $this->mngr->onPublish($this->conn, 'topic coverage', $msg, array(), array());
+ }
+
+ public function testOnCloseBubbles() {
+ $this->mock->expects($this->once())->method('onClose')->with($this->conn);
+ $this->mngr->onClose($this->conn);
+ }
+
+ protected function topicProvider($name) {
+ $class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
+ $method = $class->getMethod('getTopic');
+ $method->setAccessible(true);
+
+ $attribute = $class->getProperty('topicLookup');
+ $attribute->setAccessible(true);
+
+ $topic = $method->invokeArgs($this->mngr, array($name));
+
+ return array($topic, $attribute);
+ }
+
+ public function testConnIsRemovedFromTopicOnClose() {
+ $name = 'State Testing';
+ list($topic, $attribute) = $this->topicProvider($name);
+
+ $this->assertCount(1, $attribute->getValue($this->mngr));
+
+ $this->mngr->onSubscribe($this->conn, $name);
+ $this->mngr->onClose($this->conn);
+
+ $this->assertFalse($topic->has($this->conn));
+ }
+
+ public static function topicConnExpectationProvider() {
+ return [
+ [ 'onClose', 0]
+ , ['onUnsubscribe', 0]
+ ];
+ }
+
+ /**
+ * @dataProvider topicConnExpectationProvider
+ */
+ public function testTopicRetentionFromLeavingConnections($methodCall, $expectation) {
+ $topicName = 'checkTopic';
+ list($topic, $attribute) = $this->topicProvider($topicName);
+
+ $this->mngr->onSubscribe($this->conn, $topicName);
+ call_user_func_array(array($this->mngr, $methodCall), array($this->conn, $topicName));
+
+ $this->assertCount($expectation, $attribute->getValue($this->mngr));
+ }
+
+ public function testOnErrorBubbles() {
+ $e = new \Exception('All work and no play makes Chris a dull boy');
+ $this->mock->expects($this->once())->method('onError')->with($this->conn, $e);
+
+ $this->mngr->onError($this->conn, $e);
+ }
+
+ public function testGetSubProtocolsReturnsArray() {
+ $this->assertInternalType('array', $this->mngr->getSubProtocols());
+ }
+
+ public function testGetSubProtocolsBubbles() {
+ $subs = array('hello', 'world');
+ $app = $this->getMock('Ratchet\Wamp\Stub\WsWampServerInterface');
+ $app->expects($this->once())->method('getSubProtocols')->will($this->returnValue($subs));
+ $mngr = new TopicManager($app);
+
+ $this->assertEquals($subs, $mngr->getSubProtocols());
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Wamp/TopicTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Wamp/TopicTest.php
new file mode 100644
index 0000000..b8685b7
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Wamp/TopicTest.php
@@ -0,0 +1,164 @@
+assertEquals($id, $topic->getId());
+ }
+
+ public function testAddAndCount() {
+ $topic = new Topic('merp');
+
+ $topic->add($this->newConn());
+ $topic->add($this->newConn());
+ $topic->add($this->newConn());
+
+ $this->assertEquals(3, count($topic));
+ }
+
+ public function testRemove() {
+ $topic = new Topic('boop');
+ $tracked = $this->newConn();
+
+ $topic->add($this->newConn());
+ $topic->add($tracked);
+ $topic->add($this->newConn());
+
+ $topic->remove($tracked);
+
+ $this->assertEquals(2, count($topic));
+ }
+
+ public function testBroadcast() {
+ $msg = 'Hello World!';
+ $name = 'Batman';
+ $protocol = json_encode(array(8, $name, $msg));
+
+ $first = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
+ $second = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
+
+ $first->expects($this->once())
+ ->method('send')
+ ->with($this->equalTo($protocol));
+
+ $second->expects($this->once())
+ ->method('send')
+ ->with($this->equalTo($protocol));
+
+ $topic = new Topic($name);
+ $topic->add($first);
+ $topic->add($second);
+
+ $topic->broadcast($msg);
+ }
+
+ public function testBroadcastWithExclude() {
+ $msg = 'Hello odd numbers';
+ $name = 'Excluding';
+ $protocol = json_encode(array(8, $name, $msg));
+
+ $first = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
+ $second = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
+ $third = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
+
+ $first->expects($this->once())
+ ->method('send')
+ ->with($this->equalTo($protocol));
+
+ $second->expects($this->never())->method('send');
+
+ $third->expects($this->once())
+ ->method('send')
+ ->with($this->equalTo($protocol));
+
+ $topic = new Topic($name);
+ $topic->add($first);
+ $topic->add($second);
+ $topic->add($third);
+
+ $topic->broadcast($msg, array($second->WAMP->sessionId));
+ }
+
+ public function testBroadcastWithEligible() {
+ $msg = 'Hello white list';
+ $name = 'Eligible';
+ $protocol = json_encode(array(8, $name, $msg));
+
+ $first = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
+ $second = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
+ $third = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
+
+ $first->expects($this->once())
+ ->method('send')
+ ->with($this->equalTo($protocol));
+
+ $second->expects($this->never())->method('send');
+
+ $third->expects($this->once())
+ ->method('send')
+ ->with($this->equalTo($protocol));
+
+ $topic = new Topic($name);
+ $topic->add($first);
+ $topic->add($second);
+ $topic->add($third);
+
+ $topic->broadcast($msg, array(), array($first->WAMP->sessionId, $third->WAMP->sessionId));
+ }
+
+ public function testIterator() {
+ $first = $this->newConn();
+ $second = $this->newConn();
+ $third = $this->newConn();
+
+ $topic = new Topic('Joker');
+ $topic->add($first)->add($second)->add($third);
+
+ $check = array($first, $second, $third);
+
+ foreach ($topic as $mock) {
+ $this->assertNotSame(false, array_search($mock, $check));
+ }
+ }
+
+ public function testToString() {
+ $name = 'Bane';
+ $topic = new Topic($name);
+
+ $this->assertEquals($name, (string)$topic);
+ }
+
+ public function testDoesHave() {
+ $conn = $this->newConn();
+ $topic = new Topic('Two Face');
+ $topic->add($conn);
+
+ $this->assertTrue($topic->has($conn));
+ }
+
+ public function testDoesNotHave() {
+ $conn = $this->newConn();
+ $topic = new Topic('Alfred');
+
+ $this->assertFalse($topic->has($conn));
+ }
+
+ public function testDoesNotHaveAfterRemove() {
+ $conn = $this->newConn();
+ $topic = new Topic('Ras');
+
+ $topic->add($conn)->remove($conn);
+
+ $this->assertFalse($topic->has($conn));
+ }
+
+ protected function newConn() {
+ return new WampConnection($this->getMock('\\Ratchet\\ConnectionInterface'));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Wamp/WampConnectionTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Wamp/WampConnectionTest.php
new file mode 100644
index 0000000..adf59d5
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Wamp/WampConnectionTest.php
@@ -0,0 +1,77 @@
+mock = $this->getMock('\\Ratchet\\ConnectionInterface');
+ $this->conn = new WampConnection($this->mock);
+ }
+
+ public function testCallResult() {
+ $callId = uniqid();
+ $data = array('hello' => 'world', 'herp' => 'derp');
+
+ $this->mock->expects($this->once())->method('send')->with(json_encode(array(3, $callId, $data)));
+
+ $this->conn->callResult($callId, $data);
+ }
+
+ public function testCallError() {
+ $callId = uniqid();
+ $uri = 'http://example.com/end/point';
+
+ $this->mock->expects($this->once())->method('send')->with(json_encode(array(4, $callId, $uri, '')));
+
+ $this->conn->callError($callId, $uri);
+ }
+
+ public function testCallErrorWithTopic() {
+ $callId = uniqid();
+ $uri = 'http://example.com/end/point';
+
+ $this->mock->expects($this->once())->method('send')->with(json_encode(array(4, $callId, $uri, '')));
+
+ $this->conn->callError($callId, new Topic($uri));
+ }
+
+ public function testDetailedCallError() {
+ $callId = uniqid();
+ $uri = 'http://example.com/end/point';
+ $desc = 'beep boop beep';
+ $detail = 'Error: Too much awesome';
+
+ $this->mock->expects($this->once())->method('send')->with(json_encode(array(4, $callId, $uri, $desc, $detail)));
+
+ $this->conn->callError($callId, $uri, $desc, $detail);
+ }
+
+ public function testPrefix() {
+ $shortOut = 'outgoing';
+ $longOut = 'http://example.com/outgoing';
+
+ $this->mock->expects($this->once())->method('send')->with(json_encode(array(1, $shortOut, $longOut)));
+
+ $this->conn->prefix($shortOut, $longOut);
+ }
+
+ public function testGetUriWhenNoCurieGiven() {
+ $uri = 'http://example.com/noshort';
+
+ $this->assertEquals($uri, $this->conn->getUri($uri));
+ }
+
+ public function testClose() {
+ $mock = $this->getMock('\\Ratchet\\ConnectionInterface');
+ $conn = new WampConnection($mock);
+
+ $mock->expects($this->once())->method('close');
+
+ $conn->close();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Wamp/WampServerTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Wamp/WampServerTest.php
new file mode 100644
index 0000000..626b1ce
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/cboden/ratchet/tests/unit/Wamp/WampServerTest.php
@@ -0,0 +1,49 @@
+_app->expects($this->once())->method('onPublish')->with(
+ $this->isExpectedConnection()
+ , new \PHPUnit_Framework_Constraint_IsInstanceOf('\Ratchet\Wamp\Topic')
+ , $published
+ , array()
+ , array()
+ );
+
+ $this->_serv->onMessage($this->_conn, json_encode(array(7, 'topic', $published)));
+ }
+
+ public function testGetSubProtocols() {
+ // todo: could expand on this
+ $this->assertInternalType('array', $this->_serv->getSubProtocols());
+ }
+
+ public function testConnectionClosesOnInvalidJson() {
+ $this->_conn->expects($this->once())->method('close');
+ $this->_serv->onMessage($this->_conn, 'invalid json');
+ }
+
+ public function testConnectionClosesOnProtocolError() {
+ $this->_conn->expects($this->once())->method('close');
+ $this->_serv->onMessage($this->_conn, json_encode(array('valid' => 'json', 'invalid' => 'protocol')));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/ClassLoader.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/ClassLoader.php
new file mode 100644
index 0000000..dc02dfb
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/ClassLoader.php
@@ -0,0 +1,445 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ * $loader = new \Composer\Autoload\ClassLoader();
+ *
+ * // register classes with namespaces
+ * $loader->add('Symfony\Component', __DIR__.'/component');
+ * $loader->add('Symfony', __DIR__.'/framework');
+ *
+ * // activate the autoloader
+ * $loader->register();
+ *
+ * // to enable searching the include path (eg. for PEAR packages)
+ * $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier
+ * @author Jordi Boggiano
+ * @see http://www.php-fig.org/psr/psr-0/
+ * @see http://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+ // PSR-4
+ private $prefixLengthsPsr4 = array();
+ private $prefixDirsPsr4 = array();
+ private $fallbackDirsPsr4 = array();
+
+ // PSR-0
+ private $prefixesPsr0 = array();
+ private $fallbackDirsPsr0 = array();
+
+ private $useIncludePath = false;
+ private $classMap = array();
+ private $classMapAuthoritative = false;
+ private $missingClasses = array();
+ private $apcuPrefix;
+
+ public function getPrefixes()
+ {
+ if (!empty($this->prefixesPsr0)) {
+ return call_user_func_array('array_merge', $this->prefixesPsr0);
+ }
+
+ return array();
+ }
+
+ public function getPrefixesPsr4()
+ {
+ return $this->prefixDirsPsr4;
+ }
+
+ public function getFallbackDirs()
+ {
+ return $this->fallbackDirsPsr0;
+ }
+
+ public function getFallbackDirsPsr4()
+ {
+ return $this->fallbackDirsPsr4;
+ }
+
+ public function getClassMap()
+ {
+ return $this->classMap;
+ }
+
+ /**
+ * @param array $classMap Class to filename map
+ */
+ public function addClassMap(array $classMap)
+ {
+ if ($this->classMap) {
+ $this->classMap = array_merge($this->classMap, $classMap);
+ } else {
+ $this->classMap = $classMap;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix, either
+ * appending or prepending to the ones previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param array|string $paths The PSR-0 root directories
+ * @param bool $prepend Whether to prepend the directories
+ */
+ public function add($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ if ($prepend) {
+ $this->fallbackDirsPsr0 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr0
+ );
+ } else {
+ $this->fallbackDirsPsr0 = array_merge(
+ $this->fallbackDirsPsr0,
+ (array) $paths
+ );
+ }
+
+ return;
+ }
+
+ $first = $prefix[0];
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+ return;
+ }
+ if ($prepend) {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixesPsr0[$first][$prefix]
+ );
+ } else {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $this->prefixesPsr0[$first][$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace, either
+ * appending or prepending to the ones previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param array|string $paths The PSR-4 base directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function addPsr4($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ // Register directories for the root namespace.
+ if ($prepend) {
+ $this->fallbackDirsPsr4 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr4
+ );
+ } else {
+ $this->fallbackDirsPsr4 = array_merge(
+ $this->fallbackDirsPsr4,
+ (array) $paths
+ );
+ }
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+ // Register directories for a new namespace.
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ } elseif ($prepend) {
+ // Prepend directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixDirsPsr4[$prefix]
+ );
+ } else {
+ // Append directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $this->prefixDirsPsr4[$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix,
+ * replacing any others previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param array|string $paths The PSR-0 base directories
+ */
+ public function set($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr0 = (array) $paths;
+ } else {
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace,
+ * replacing any others previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param array|string $paths The PSR-4 base directories
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setPsr4($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr4 = (array) $paths;
+ } else {
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Turns on searching the include path for class files.
+ *
+ * @param bool $useIncludePath
+ */
+ public function setUseIncludePath($useIncludePath)
+ {
+ $this->useIncludePath = $useIncludePath;
+ }
+
+ /**
+ * Can be used to check if the autoloader uses the include path to check
+ * for classes.
+ *
+ * @return bool
+ */
+ public function getUseIncludePath()
+ {
+ return $this->useIncludePath;
+ }
+
+ /**
+ * Turns off searching the prefix and fallback directories for classes
+ * that have not been registered with the class map.
+ *
+ * @param bool $classMapAuthoritative
+ */
+ public function setClassMapAuthoritative($classMapAuthoritative)
+ {
+ $this->classMapAuthoritative = $classMapAuthoritative;
+ }
+
+ /**
+ * Should class lookup fail if not found in the current class map?
+ *
+ * @return bool
+ */
+ public function isClassMapAuthoritative()
+ {
+ return $this->classMapAuthoritative;
+ }
+
+ /**
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+ *
+ * @param string|null $apcuPrefix
+ */
+ public function setApcuPrefix($apcuPrefix)
+ {
+ $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
+ }
+
+ /**
+ * The APCu prefix in use, or null if APCu caching is not enabled.
+ *
+ * @return string|null
+ */
+ public function getApcuPrefix()
+ {
+ return $this->apcuPrefix;
+ }
+
+ /**
+ * Registers this instance as an autoloader.
+ *
+ * @param bool $prepend Whether to prepend the autoloader or not
+ */
+ public function register($prepend = false)
+ {
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+ }
+
+ /**
+ * Unregisters this instance as an autoloader.
+ */
+ public function unregister()
+ {
+ spl_autoload_unregister(array($this, 'loadClass'));
+ }
+
+ /**
+ * Loads the given class or interface.
+ *
+ * @param string $class The name of the class
+ * @return bool|null True if loaded, null otherwise
+ */
+ public function loadClass($class)
+ {
+ if ($file = $this->findFile($class)) {
+ includeFile($file);
+
+ return true;
+ }
+ }
+
+ /**
+ * Finds the path to the file where the class is defined.
+ *
+ * @param string $class The name of the class
+ *
+ * @return string|false The path if found, false otherwise
+ */
+ public function findFile($class)
+ {
+ // class map lookup
+ if (isset($this->classMap[$class])) {
+ return $this->classMap[$class];
+ }
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+ return false;
+ }
+ if (null !== $this->apcuPrefix) {
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+ if ($hit) {
+ return $file;
+ }
+ }
+
+ $file = $this->findFileWithExtension($class, '.php');
+
+ // Search for Hack files if we are running on HHVM
+ if (false === $file && defined('HHVM_VERSION')) {
+ $file = $this->findFileWithExtension($class, '.hh');
+ }
+
+ if (null !== $this->apcuPrefix) {
+ apcu_add($this->apcuPrefix.$class, $file);
+ }
+
+ if (false === $file) {
+ // Remember that this class does not exist.
+ $this->missingClasses[$class] = true;
+ }
+
+ return $file;
+ }
+
+ private function findFileWithExtension($class, $ext)
+ {
+ // PSR-4 lookup
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+ $first = $class[0];
+ if (isset($this->prefixLengthsPsr4[$first])) {
+ $subPath = $class;
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
+ $subPath = substr($subPath, 0, $lastPos);
+ $search = $subPath.'\\';
+ if (isset($this->prefixDirsPsr4[$search])) {
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
+ if (file_exists($file = $dir . $pathEnd)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-4 fallback dirs
+ foreach ($this->fallbackDirsPsr4 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 lookup
+ if (false !== $pos = strrpos($class, '\\')) {
+ // namespaced class name
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+ } else {
+ // PEAR-like class name
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+ }
+
+ if (isset($this->prefixesPsr0[$first])) {
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($dirs as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-0 fallback dirs
+ foreach ($this->fallbackDirsPsr0 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 include paths.
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+ return $file;
+ }
+
+ return false;
+ }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ */
+function includeFile($file)
+{
+ include $file;
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/LICENSE b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/LICENSE
new file mode 100644
index 0000000..f27399a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/LICENSE
@@ -0,0 +1,21 @@
+
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/autoload_classmap.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/autoload_classmap.php
new file mode 100644
index 0000000..a883873
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/autoload_classmap.php
@@ -0,0 +1,84 @@
+ $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php',
+ 'AssertionError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php',
+ 'CssAtCharsetParserPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtCharsetToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtFontFaceDeclarationToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtFontFaceEndToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtFontFaceParserPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtFontFaceStartToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtImportParserPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtImportToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtKeyframesEndToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtKeyframesParserPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtKeyframesRulesetDeclarationToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtKeyframesRulesetEndToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtKeyframesRulesetStartToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtKeyframesStartToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtMediaEndToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtMediaParserPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtMediaStartToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtPageDeclarationToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtPageEndToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtPageParserPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtPageStartToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtVariablesDeclarationToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtVariablesEndToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtVariablesParserPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtVariablesStartToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssCommentParserPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssCommentToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssCompressColorValuesMinifierPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssCompressExpressionValuesMinifierPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssCompressUnitValuesMinifierPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssConvertFontWeightMinifierPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssConvertHslColorsMinifierPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssConvertLevel3AtKeyframesMinifierFilter' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssConvertLevel3PropertiesMinifierFilter' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssConvertNamedColorsMinifierPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssConvertRgbColorsMinifierPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssError' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssExpressionParserPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssImportImportsMinifierFilter' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssMin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssMinifier' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssNullToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssOtbsFormatter' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssParser' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssRemoveCommentsMinifierFilter' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssRemoveEmptyAtBlocksMinifierFilter' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssRemoveEmptyRulesetsMinifierFilter' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssRemoveLastDelarationSemiColonMinifierFilter' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssRulesetDeclarationToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssRulesetEndToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssRulesetParserPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssRulesetStartToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssSortRulesetPropertiesMinifierFilter' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssStringParserPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssUrlParserPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssVariablesMinifierFilter' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssVariablesMinifierPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'CssWhitesmithsFormatter' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'DivisionByZeroError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php',
+ 'Error' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/Error.php',
+ 'ParseError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ParseError.php',
+ 'SessionUpdateTimestampHandlerInterface' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php',
+ 'TypeError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/TypeError.php',
+ 'aCssAtBlockEndToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'aCssAtBlockStartToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'aCssDeclarationToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'aCssFormatter' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'aCssMinifierFilter' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'aCssMinifierPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'aCssParserPlugin' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'aCssRulesetEndToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'aCssRulesetStartToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+ 'aCssToken' => $vendorDir . '/natxet/CssMin/src/CssMin.php',
+);
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/autoload_files.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/autoload_files.php
new file mode 100644
index 0000000..295c7b6
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/autoload_files.php
@@ -0,0 +1,15 @@
+ $vendorDir . '/react/promise/src/functions_include.php',
+ '5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
+ '6b06ce8ccf69c43a60a1e48495a034c9' => $vendorDir . '/react/promise-timer/src/functions.php',
+ '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
+ '023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php',
+ 'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
+);
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/autoload_namespaces.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/autoload_namespaces.php
new file mode 100644
index 0000000..02066fb
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/autoload_namespaces.php
@@ -0,0 +1,10 @@
+ array($vendorDir . '/evenement/evenement/src'),
+);
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/autoload_psr4.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/autoload_psr4.php
new file mode 100644
index 0000000..9c1906b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/autoload_psr4.php
@@ -0,0 +1,28 @@
+ array($baseDir . '/Chatserver/src'),
+ 'Symfony\\Polyfill\\Php70\\' => array($vendorDir . '/symfony/polyfill-php70'),
+ 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
+ 'Symfony\\Component\\Routing\\' => array($vendorDir . '/symfony/routing'),
+ 'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'),
+ 'React\\Stream\\' => array($vendorDir . '/react/stream/src'),
+ 'React\\Socket\\' => array($vendorDir . '/react/socket/src'),
+ 'React\\Promise\\Timer\\' => array($vendorDir . '/react/promise-timer/src'),
+ 'React\\Promise\\' => array($vendorDir . '/react/promise/src'),
+ 'React\\EventLoop\\' => array($vendorDir . '/react/event-loop/src'),
+ 'React\\Dns\\' => array($vendorDir . '/react/dns/src'),
+ 'React\\Cache\\' => array($vendorDir . '/react/cache/src'),
+ 'Ratchet\\RFC6455\\' => array($vendorDir . '/ratchet/rfc6455/src'),
+ 'Ratchet\\' => array($vendorDir . '/cboden/ratchet/src/Ratchet'),
+ 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
+ 'Nubs\\RandomNameGenerator\\' => array($vendorDir . '/nubs/random-name-generator/src'),
+ 'MatthiasMullie\\PathConverter\\' => array($vendorDir . '/matthiasmullie/path-converter/src'),
+ 'MatthiasMullie\\Minify\\' => array($vendorDir . '/matthiasmullie/minify/src'),
+ 'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
+);
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/autoload_real.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/autoload_real.php
new file mode 100644
index 0000000..8fc42a0
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/autoload_real.php
@@ -0,0 +1,70 @@
+= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
+ if ($useStaticLoader) {
+ require_once __DIR__ . '/autoload_static.php';
+
+ call_user_func(\Composer\Autoload\ComposerStaticInit3c5661e077098f105cbab5a541fd4883::getInitializer($loader));
+ } else {
+ $map = require __DIR__ . '/autoload_namespaces.php';
+ foreach ($map as $namespace => $path) {
+ $loader->set($namespace, $path);
+ }
+
+ $map = require __DIR__ . '/autoload_psr4.php';
+ foreach ($map as $namespace => $path) {
+ $loader->setPsr4($namespace, $path);
+ }
+
+ $classMap = require __DIR__ . '/autoload_classmap.php';
+ if ($classMap) {
+ $loader->addClassMap($classMap);
+ }
+ }
+
+ $loader->register(true);
+
+ if ($useStaticLoader) {
+ $includeFiles = Composer\Autoload\ComposerStaticInit3c5661e077098f105cbab5a541fd4883::$files;
+ } else {
+ $includeFiles = require __DIR__ . '/autoload_files.php';
+ }
+ foreach ($includeFiles as $fileIdentifier => $file) {
+ composerRequire3c5661e077098f105cbab5a541fd4883($fileIdentifier, $file);
+ }
+
+ return $loader;
+ }
+}
+
+function composerRequire3c5661e077098f105cbab5a541fd4883($fileIdentifier, $file)
+{
+ if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
+ require $file;
+
+ $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/autoload_static.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/autoload_static.php
new file mode 100644
index 0000000..b5b4c27
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/autoload_static.php
@@ -0,0 +1,238 @@
+ __DIR__ . '/..' . '/react/promise/src/functions_include.php',
+ '5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php',
+ '6b06ce8ccf69c43a60a1e48495a034c9' => __DIR__ . '/..' . '/react/promise-timer/src/functions.php',
+ '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
+ '023d27dca8066ef29e6739335ea73bad' => __DIR__ . '/..' . '/symfony/polyfill-php70/bootstrap.php',
+ 'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
+ );
+
+ public static $prefixLengthsPsr4 = array (
+ 'W' =>
+ array (
+ 'Websocket\\' => 10,
+ ),
+ 'S' =>
+ array (
+ 'Symfony\\Polyfill\\Php70\\' => 23,
+ 'Symfony\\Polyfill\\Mbstring\\' => 26,
+ 'Symfony\\Component\\Routing\\' => 26,
+ 'Symfony\\Component\\HttpFoundation\\' => 33,
+ ),
+ 'R' =>
+ array (
+ 'React\\Stream\\' => 13,
+ 'React\\Socket\\' => 13,
+ 'React\\Promise\\Timer\\' => 20,
+ 'React\\Promise\\' => 14,
+ 'React\\EventLoop\\' => 16,
+ 'React\\Dns\\' => 10,
+ 'React\\Cache\\' => 12,
+ 'Ratchet\\RFC6455\\' => 16,
+ 'Ratchet\\' => 8,
+ ),
+ 'P' =>
+ array (
+ 'Psr\\Http\\Message\\' => 17,
+ ),
+ 'N' =>
+ array (
+ 'Nubs\\RandomNameGenerator\\' => 25,
+ ),
+ 'M' =>
+ array (
+ 'MatthiasMullie\\PathConverter\\' => 29,
+ 'MatthiasMullie\\Minify\\' => 22,
+ ),
+ 'G' =>
+ array (
+ 'GuzzleHttp\\Psr7\\' => 16,
+ ),
+ );
+
+ public static $prefixDirsPsr4 = array (
+ 'Websocket\\' =>
+ array (
+ 0 => __DIR__ . '/../..' . '/Chatserver/src',
+ ),
+ 'Symfony\\Polyfill\\Php70\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/symfony/polyfill-php70',
+ ),
+ 'Symfony\\Polyfill\\Mbstring\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
+ ),
+ 'Symfony\\Component\\Routing\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/symfony/routing',
+ ),
+ 'Symfony\\Component\\HttpFoundation\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/symfony/http-foundation',
+ ),
+ 'React\\Stream\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/react/stream/src',
+ ),
+ 'React\\Socket\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/react/socket/src',
+ ),
+ 'React\\Promise\\Timer\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/react/promise-timer/src',
+ ),
+ 'React\\Promise\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/react/promise/src',
+ ),
+ 'React\\EventLoop\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/react/event-loop/src',
+ ),
+ 'React\\Dns\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/react/dns/src',
+ ),
+ 'React\\Cache\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/react/cache/src',
+ ),
+ 'Ratchet\\RFC6455\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/ratchet/rfc6455/src',
+ ),
+ 'Ratchet\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/cboden/ratchet/src/Ratchet',
+ ),
+ 'Psr\\Http\\Message\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/psr/http-message/src',
+ ),
+ 'Nubs\\RandomNameGenerator\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/nubs/random-name-generator/src',
+ ),
+ 'MatthiasMullie\\PathConverter\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/matthiasmullie/path-converter/src',
+ ),
+ 'MatthiasMullie\\Minify\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/matthiasmullie/minify/src',
+ ),
+ 'GuzzleHttp\\Psr7\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
+ ),
+ );
+
+ public static $prefixesPsr0 = array (
+ 'E' =>
+ array (
+ 'Evenement' =>
+ array (
+ 0 => __DIR__ . '/..' . '/evenement/evenement/src',
+ ),
+ ),
+ );
+
+ public static $classMap = array (
+ 'ArithmeticError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php',
+ 'AssertionError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php',
+ 'CssAtCharsetParserPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtCharsetToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtFontFaceDeclarationToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtFontFaceEndToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtFontFaceParserPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtFontFaceStartToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtImportParserPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtImportToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtKeyframesEndToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtKeyframesParserPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtKeyframesRulesetDeclarationToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtKeyframesRulesetEndToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtKeyframesRulesetStartToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtKeyframesStartToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtMediaEndToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtMediaParserPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtMediaStartToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtPageDeclarationToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtPageEndToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtPageParserPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtPageStartToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtVariablesDeclarationToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtVariablesEndToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtVariablesParserPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssAtVariablesStartToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssCommentParserPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssCommentToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssCompressColorValuesMinifierPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssCompressExpressionValuesMinifierPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssCompressUnitValuesMinifierPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssConvertFontWeightMinifierPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssConvertHslColorsMinifierPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssConvertLevel3AtKeyframesMinifierFilter' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssConvertLevel3PropertiesMinifierFilter' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssConvertNamedColorsMinifierPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssConvertRgbColorsMinifierPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssError' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssExpressionParserPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssImportImportsMinifierFilter' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssMin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssMinifier' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssNullToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssOtbsFormatter' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssParser' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssRemoveCommentsMinifierFilter' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssRemoveEmptyAtBlocksMinifierFilter' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssRemoveEmptyRulesetsMinifierFilter' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssRemoveLastDelarationSemiColonMinifierFilter' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssRulesetDeclarationToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssRulesetEndToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssRulesetParserPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssRulesetStartToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssSortRulesetPropertiesMinifierFilter' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssStringParserPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssUrlParserPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssVariablesMinifierFilter' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssVariablesMinifierPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'CssWhitesmithsFormatter' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'DivisionByZeroError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php',
+ 'Error' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/Error.php',
+ 'ParseError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ParseError.php',
+ 'SessionUpdateTimestampHandlerInterface' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php',
+ 'TypeError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/TypeError.php',
+ 'aCssAtBlockEndToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'aCssAtBlockStartToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'aCssDeclarationToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'aCssFormatter' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'aCssMinifierFilter' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'aCssMinifierPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'aCssParserPlugin' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'aCssRulesetEndToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'aCssRulesetStartToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ 'aCssToken' => __DIR__ . '/..' . '/natxet/CssMin/src/CssMin.php',
+ );
+
+ public static function getInitializer(ClassLoader $loader)
+ {
+ return \Closure::bind(function () use ($loader) {
+ $loader->prefixLengthsPsr4 = ComposerStaticInit3c5661e077098f105cbab5a541fd4883::$prefixLengthsPsr4;
+ $loader->prefixDirsPsr4 = ComposerStaticInit3c5661e077098f105cbab5a541fd4883::$prefixDirsPsr4;
+ $loader->prefixesPsr0 = ComposerStaticInit3c5661e077098f105cbab5a541fd4883::$prefixesPsr0;
+ $loader->classMap = ComposerStaticInit3c5661e077098f105cbab5a541fd4883::$classMap;
+
+ }, null, ClassLoader::class);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/installed.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/installed.json
new file mode 100644
index 0000000..c8ca472
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/composer/installed.json
@@ -0,0 +1,1125 @@
+[
+ {
+ "name": "cboden/ratchet",
+ "version": "v0.4.1",
+ "version_normalized": "0.4.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ratchetphp/Ratchet.git",
+ "reference": "0d31f3a8ad4795fd48397712709e55cd07f51360"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ratchetphp/Ratchet/zipball/0d31f3a8ad4795fd48397712709e55cd07f51360",
+ "reference": "0d31f3a8ad4795fd48397712709e55cd07f51360",
+ "shasum": ""
+ },
+ "require": {
+ "guzzlehttp/psr7": "^1.0",
+ "php": ">=5.4.2",
+ "ratchet/rfc6455": "^0.2",
+ "react/socket": "^1.0 || ^0.8 || ^0.7 || ^0.6 || ^0.5",
+ "symfony/http-foundation": "^2.6|^3.0|^4.0",
+ "symfony/routing": "^2.6|^3.0|^4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8"
+ },
+ "time": "2017-12-12T00:49:31+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Ratchet\\": "src/Ratchet"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "role": "Developer"
+ }
+ ],
+ "description": "PHP WebSocket library",
+ "homepage": "http://socketo.me",
+ "keywords": [
+ "Ratchet",
+ "WebSockets",
+ "server",
+ "sockets",
+ "websocket"
+ ]
+ },
+ {
+ "name": "evenement/evenement",
+ "version": "v3.0.1",
+ "version_normalized": "3.0.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/igorw/evenement.git",
+ "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7",
+ "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6.0"
+ },
+ "time": "2017-07-23T21:35:13+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-0": {
+ "Evenement": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Igor Wiedler",
+ "email": "igor@wiedler.ch"
+ }
+ ],
+ "description": "Événement is a very simple event dispatching library for PHP",
+ "keywords": [
+ "event-dispatcher",
+ "event-emitter"
+ ]
+ },
+ {
+ "name": "guzzlehttp/psr7",
+ "version": "1.4.2",
+ "version_normalized": "1.4.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/psr7.git",
+ "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c",
+ "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0",
+ "psr/http-message": "~1.0"
+ },
+ "provide": {
+ "psr/http-message-implementation": "1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.0"
+ },
+ "time": "2017-03-20T17:10:46+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.4-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Psr7\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Tobias Schultze",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "PSR-7 message implementation that also provides common utility methods",
+ "keywords": [
+ "http",
+ "message",
+ "request",
+ "response",
+ "stream",
+ "uri",
+ "url"
+ ]
+ },
+ {
+ "name": "matthiasmullie/minify",
+ "version": "1.3.59",
+ "version_normalized": "1.3.59.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/matthiasmullie/minify.git",
+ "reference": "62dac3bce06de66f0d71fe6490cf1c508d3c3ff7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/62dac3bce06de66f0d71fe6490cf1c508d3c3ff7",
+ "reference": "62dac3bce06de66f0d71fe6490cf1c508d3c3ff7",
+ "shasum": ""
+ },
+ "require": {
+ "ext-pcre": "*",
+ "matthiasmullie/path-converter": "~1.1",
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "~2.0",
+ "matthiasmullie/scrapbook": "~1.0",
+ "phpunit/phpunit": "~4.8"
+ },
+ "suggest": {
+ "psr/cache-implementation": "Cache implementation to use with Minify::cache"
+ },
+ "time": "2018-02-02T12:44:18+00:00",
+ "bin": [
+ "bin/minifycss",
+ "bin/minifyjs"
+ ],
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "MatthiasMullie\\Minify\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Matthias Mullie",
+ "email": "minify@mullie.eu",
+ "homepage": "http://www.mullie.eu",
+ "role": "Developer"
+ }
+ ],
+ "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.",
+ "homepage": "http://www.minifier.org",
+ "keywords": [
+ "JS",
+ "css",
+ "javascript",
+ "minifier",
+ "minify"
+ ]
+ },
+ {
+ "name": "matthiasmullie/path-converter",
+ "version": "1.1.1",
+ "version_normalized": "1.1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/matthiasmullie/path-converter.git",
+ "reference": "3082a6838be02b930239a97d38b5c9da4d693aca"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/3082a6838be02b930239a97d38b5c9da4d693aca",
+ "reference": "3082a6838be02b930239a97d38b5c9da4d693aca",
+ "shasum": ""
+ },
+ "require": {
+ "ext-pcre": "*",
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8"
+ },
+ "time": "2018-02-02T11:30:10+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "MatthiasMullie\\PathConverter\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Matthias Mullie",
+ "email": "pathconverter@mullie.eu",
+ "homepage": "http://www.mullie.eu",
+ "role": "Developer"
+ }
+ ],
+ "description": "Relative path converter",
+ "homepage": "http://github.com/matthiasmullie/path-converter",
+ "keywords": [
+ "converter",
+ "path",
+ "paths",
+ "relative"
+ ]
+ },
+ {
+ "name": "natxet/CssMin",
+ "version": "v3.0.6",
+ "version_normalized": "3.0.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/natxet/CssMin.git",
+ "reference": "d5d9f4c3e5cedb1ae96a95a21731f8790e38f1dd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/natxet/CssMin/zipball/d5d9f4c3e5cedb1ae96a95a21731f8790e38f1dd",
+ "reference": "d5d9f4c3e5cedb1ae96a95a21731f8790e38f1dd",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.0"
+ },
+ "time": "2018-01-09T11:15:01+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Joe Scylla",
+ "email": "joe.scylla@gmail.com",
+ "homepage": "https://profiles.google.com/joe.scylla"
+ }
+ ],
+ "description": "Minifying CSS",
+ "homepage": "http://code.google.com/p/cssmin/",
+ "keywords": [
+ "css",
+ "minify"
+ ]
+ },
+ {
+ "name": "nubs/random-name-generator",
+ "version": "v2.1.0",
+ "version_normalized": "2.1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nubs/random-name-generator.git",
+ "reference": "7004eb1724e1c4a154553e44312b7045fe412b77"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nubs/random-name-generator/zipball/7004eb1724e1c4a154553e44312b7045fe412b77",
+ "reference": "7004eb1724e1c4a154553e44312b7045fe412b77",
+ "shasum": ""
+ },
+ "require": {
+ "php": "~5.6 || ~7.0"
+ },
+ "require-dev": {
+ "cinam/randomizer": ">=1.1.1,<2.0",
+ "phpunit/phpunit": "~5.0",
+ "satooshi/php-coveralls": "~1.0",
+ "squizlabs/php_codesniffer": "~2.3"
+ },
+ "time": "2016-12-04T01:57:19+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Nubs\\RandomNameGenerator\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Spencer Rinehart",
+ "email": "anubis@overthemonkey.com",
+ "role": "Developer"
+ }
+ ],
+ "description": "A library to create interesting, sometimes entertaining, random names.",
+ "keywords": [
+ "alliteration",
+ "generator",
+ "random",
+ "video game"
+ ]
+ },
+ {
+ "name": "paragonie/random_compat",
+ "version": "v2.0.12",
+ "version_normalized": "2.0.12.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/paragonie/random_compat.git",
+ "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/paragonie/random_compat/zipball/258c89a6b97de7dfaf5b8c7607d0478e236b04fb",
+ "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.2.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "4.*|5.*"
+ },
+ "suggest": {
+ "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
+ },
+ "time": "2018-04-04T21:24:14+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "files": [
+ "lib/random.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Paragon Initiative Enterprises",
+ "email": "security@paragonie.com",
+ "homepage": "https://paragonie.com"
+ }
+ ],
+ "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
+ "keywords": [
+ "csprng",
+ "pseudorandom",
+ "random"
+ ]
+ },
+ {
+ "name": "psr/http-message",
+ "version": "1.0.1",
+ "version_normalized": "1.0.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "time": "2016-08-06T14:39:51+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ]
+ },
+ {
+ "name": "ratchet/rfc6455",
+ "version": "v0.2.3",
+ "version_normalized": "0.2.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ratchetphp/RFC6455.git",
+ "reference": "cc8a1a46a703ce01de10fdb5fab387381b66edc8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ratchetphp/RFC6455/zipball/cc8a1a46a703ce01de10fdb5fab387381b66edc8",
+ "reference": "cc8a1a46a703ce01de10fdb5fab387381b66edc8",
+ "shasum": ""
+ },
+ "require": {
+ "guzzlehttp/psr7": "^1.0",
+ "php": ">=5.4.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "4.8.*",
+ "react/http": "^0.4.1",
+ "react/socket-client": "^0.4.3"
+ },
+ "time": "2017-09-13T13:49:42+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Ratchet\\RFC6455\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "role": "Developer"
+ }
+ ],
+ "description": "RFC6455 WebSocket protocol handler",
+ "homepage": "http://socketo.me",
+ "keywords": [
+ "WebSockets",
+ "rfc6455",
+ "websocket"
+ ]
+ },
+ {
+ "name": "react/cache",
+ "version": "v0.4.2",
+ "version_normalized": "0.4.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/cache.git",
+ "reference": "75494f26b4ef089db9bf8c90b63c296246e099e8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/cache/zipball/75494f26b4ef089db9bf8c90b63c296246e099e8",
+ "reference": "75494f26b4ef089db9bf8c90b63c296246e099e8",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0",
+ "react/promise": "~2.0|~1.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35"
+ },
+ "time": "2017-12-20T16:47:13+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "React\\Cache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Async, Promise-based cache interface for ReactPHP",
+ "keywords": [
+ "cache",
+ "caching",
+ "promise",
+ "reactphp"
+ ]
+ },
+ {
+ "name": "react/dns",
+ "version": "v0.4.13",
+ "version_normalized": "0.4.13.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/dns.git",
+ "reference": "7d1e08c300fd7de600810883386ee5e2a64898f4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/dns/zipball/7d1e08c300fd7de600810883386ee5e2a64898f4",
+ "reference": "7d1e08c300fd7de600810883386ee5e2a64898f4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0",
+ "react/cache": "~0.4.0|~0.3.0",
+ "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5",
+ "react/promise": "^2.1 || ^1.2.1",
+ "react/promise-timer": "^1.2",
+ "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4.5"
+ },
+ "require-dev": {
+ "clue/block-react": "^1.2",
+ "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35"
+ },
+ "time": "2018-02-27T12:51:22+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "React\\Dns\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Async DNS resolver for ReactPHP",
+ "keywords": [
+ "async",
+ "dns",
+ "dns-resolver",
+ "reactphp"
+ ]
+ },
+ {
+ "name": "react/event-loop",
+ "version": "v0.5.1",
+ "version_normalized": "0.5.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/event-loop.git",
+ "reference": "e1e0647a5c6e2c86013a24e9c8252113df86105a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/event-loop/zipball/e1e0647a5c6e2c86013a24e9c8252113df86105a",
+ "reference": "e1e0647a5c6e2c86013a24e9c8252113df86105a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8.35 || ^5.7 || ^6.4"
+ },
+ "suggest": {
+ "ext-event": "~1.0 for ExtEventLoop",
+ "ext-pcntl": "For signal handling support when using the StreamSelectLoop"
+ },
+ "time": "2018-04-09T11:59:21+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "React\\EventLoop\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.",
+ "keywords": [
+ "asynchronous",
+ "event-loop"
+ ]
+ },
+ {
+ "name": "react/promise",
+ "version": "v2.5.1",
+ "version_normalized": "2.5.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/promise.git",
+ "reference": "62785ae604c8d69725d693eb370e1d67e94c4053"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/promise/zipball/62785ae604c8d69725d693eb370e1d67e94c4053",
+ "reference": "62785ae604c8d69725d693eb370e1d67e94c4053",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8"
+ },
+ "time": "2017-03-25T12:08:31+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "React\\Promise\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com"
+ }
+ ],
+ "description": "A lightweight implementation of CommonJS Promises/A for PHP",
+ "keywords": [
+ "promise",
+ "promises"
+ ]
+ },
+ {
+ "name": "react/promise-timer",
+ "version": "v1.2.1",
+ "version_normalized": "1.2.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/promise-timer.git",
+ "reference": "9b4cd9cbe7457e0d87fe8aa7ccceab8a2c830fbd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/9b4cd9cbe7457e0d87fe8aa7ccceab8a2c830fbd",
+ "reference": "9b4cd9cbe7457e0d87fe8aa7ccceab8a2c830fbd",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3",
+ "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5",
+ "react/promise": "~2.1|~1.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35"
+ },
+ "time": "2017-12-22T15:41:41+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "React\\Promise\\Timer\\": "src/"
+ },
+ "files": [
+ "src/functions.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@lueck.tv"
+ }
+ ],
+ "description": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.",
+ "homepage": "https://github.com/react/promise-timer",
+ "keywords": [
+ "async",
+ "event-loop",
+ "promise",
+ "reactphp",
+ "timeout",
+ "timer"
+ ]
+ },
+ {
+ "name": "react/socket",
+ "version": "v0.8.10",
+ "version_normalized": "0.8.10.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/socket.git",
+ "reference": "d3957313c92b539537fccc80170c05a27ec25796"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/socket/zipball/d3957313c92b539537fccc80170c05a27ec25796",
+ "reference": "d3957313c92b539537fccc80170c05a27ec25796",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "php": ">=5.3.0",
+ "react/dns": "^0.4.13",
+ "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5",
+ "react/promise": "^2.1 || ^1.2",
+ "react/promise-timer": "~1.0",
+ "react/stream": "^1.0 || ^0.7.1"
+ },
+ "require-dev": {
+ "clue/block-react": "^1.2",
+ "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35"
+ },
+ "time": "2018-02-28T09:32:38+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "React\\Socket\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP",
+ "keywords": [
+ "Connection",
+ "Socket",
+ "async",
+ "reactphp",
+ "stream"
+ ]
+ },
+ {
+ "name": "react/stream",
+ "version": "v0.7.7",
+ "version_normalized": "0.7.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/stream.git",
+ "reference": "10100896018fd847a257cd81143b8e1b7be08e40"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/stream/zipball/10100896018fd847a257cd81143b8e1b7be08e40",
+ "reference": "10100896018fd847a257cd81143b8e1b7be08e40",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "php": ">=5.3.8",
+ "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5"
+ },
+ "require-dev": {
+ "clue/stream-filter": "~1.2",
+ "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35"
+ },
+ "time": "2018-01-19T15:04:38+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "React\\Stream\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP",
+ "keywords": [
+ "event-driven",
+ "io",
+ "non-blocking",
+ "pipe",
+ "reactphp",
+ "readable",
+ "stream",
+ "writable"
+ ]
+ },
+ {
+ "name": "symfony/http-foundation",
+ "version": "v3.4.8",
+ "version_normalized": "3.4.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/http-foundation.git",
+ "reference": "b11e6d165ff4cbf5685d185ab19a90f2f3bb7d1e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/b11e6d165ff4cbf5685d185ab19a90f2f3bb7d1e",
+ "reference": "b11e6d165ff4cbf5685d185ab19a90f2f3bb7d1e",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.5.9|>=7.0.8",
+ "symfony/polyfill-mbstring": "~1.1",
+ "symfony/polyfill-php70": "~1.6"
+ },
+ "require-dev": {
+ "symfony/expression-language": "~2.8|~3.0|~4.0"
+ },
+ "time": "2018-04-03T05:22:50+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.4-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\HttpFoundation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony HttpFoundation Component",
+ "homepage": "https://symfony.com"
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.7.0",
+ "version_normalized": "1.7.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b",
+ "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "time": "2018-01-30T19:27:44+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.7-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ]
+ },
+ {
+ "name": "symfony/polyfill-php70",
+ "version": "v1.7.0",
+ "version_normalized": "1.7.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php70.git",
+ "reference": "3532bfcd8f933a7816f3a0a59682fc404776600f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/3532bfcd8f933a7816f3a0a59682fc404776600f",
+ "reference": "3532bfcd8f933a7816f3a0a59682fc404776600f",
+ "shasum": ""
+ },
+ "require": {
+ "paragonie/random_compat": "~1.0|~2.0",
+ "php": ">=5.3.3"
+ },
+ "time": "2018-01-30T19:27:44+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.7-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Php70\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ],
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ]
+ },
+ {
+ "name": "symfony/routing",
+ "version": "v3.4.8",
+ "version_normalized": "3.4.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/routing.git",
+ "reference": "50f333b707bef9f6972ad04e6df3ec8875c9a67c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/routing/zipball/50f333b707bef9f6972ad04e6df3ec8875c9a67c",
+ "reference": "50f333b707bef9f6972ad04e6df3ec8875c9a67c",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.5.9|>=7.0.8"
+ },
+ "conflict": {
+ "symfony/config": "<3.3.1",
+ "symfony/dependency-injection": "<3.3",
+ "symfony/yaml": "<3.4"
+ },
+ "require-dev": {
+ "doctrine/annotations": "~1.0",
+ "doctrine/common": "~2.2",
+ "psr/log": "~1.0",
+ "symfony/config": "^3.3.1|~4.0",
+ "symfony/dependency-injection": "~3.3|~4.0",
+ "symfony/expression-language": "~2.8|~3.0|~4.0",
+ "symfony/http-foundation": "~2.8|~3.0|~4.0",
+ "symfony/yaml": "~3.4|~4.0"
+ },
+ "suggest": {
+ "doctrine/annotations": "For using the annotation loader",
+ "symfony/config": "For using the all-in-one router or any loader",
+ "symfony/dependency-injection": "For loading routes from a service",
+ "symfony/expression-language": "For using expression matching",
+ "symfony/http-foundation": "For using a Symfony Request object",
+ "symfony/yaml": "For using the YAML loader"
+ },
+ "time": "2018-04-04T13:22:16+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.4-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Routing\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Routing Component",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "router",
+ "routing",
+ "uri",
+ "url"
+ ]
+ }
+]
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/.gitignore b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/.gitignore
new file mode 100644
index 0000000..987e2a2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/.gitignore
@@ -0,0 +1,2 @@
+composer.lock
+vendor
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/.travis.yml b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/.travis.yml
new file mode 100644
index 0000000..65ba0ce
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/.travis.yml
@@ -0,0 +1,24 @@
+language: php
+
+php:
+ - 7.0
+ - 7.1
+ - hhvm
+ - nightly
+
+matrix:
+ allow_failures:
+ - php: hhvm
+ - php: nightly
+
+before_script:
+ - wget http://getcomposer.org/composer.phar
+ - php composer.phar install
+
+script:
+ - ./vendor/bin/phpunit --coverage-text
+ - php -n examples/benchmark-emit-no-arguments.php
+ - php -n examples/benchmark-emit-one-argument.php
+ - php -n examples/benchmark-emit.php
+ - php -n examples/benchmark-emit-once.php
+ - php -n examples/benchmark-remove-listener-once.php
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/CHANGELOG.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/CHANGELOG.md
new file mode 100644
index 0000000..568f229
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/CHANGELOG.md
@@ -0,0 +1,35 @@
+CHANGELOG
+=========
+
+
+* v3.0.1 (2017-07-23)
+
+ * Resolved regression introduced in once listeners in v3.0.0 [#49](https://github.com/igorw/evenement/pull/49)
+
+* v3.0.0 (2017-07-23)
+
+ * Passing null as event name throw exception [#46](https://github.com/igorw/evenement/pull/46), and [#47](https://github.com/igorw/evenement/pull/47)
+ * Performance improvements [#39](https://github.com/igorw/evenement/pull/39), and [#45](https://github.com/igorw/evenement/pull/45)
+ * Remove once listeners [#44](https://github.com/igorw/evenement/pull/44), [#45](https://github.com/igorw/evenement/pull/45)
+
+* v2.1.0 (2017-07-17)
+
+ * Chaining for "on" method [#30](https://github.com/igorw/evenement/pull/30)
+ * Unit tests (on Travis) improvements [#33](https://github.com/igorw/evenement/pull/33), [#36](https://github.com/igorw/evenement/pull/36), and [#37](https://github.com/igorw/evenement/pull/37)
+ * Benchmarks added [#35](https://github.com/igorw/evenement/pull/35), and [#40](https://github.com/igorw/evenement/pull/40)
+ * Minor performance improvements [#42](https://github.com/igorw/evenement/pull/42), and [#38](https://github.com/igorw/evenement/pull/38)
+
+* v2.0.0 (2012-11-02)
+
+ * Require PHP >=5.4.0
+ * Added EventEmitterTrait
+ * Removed EventEmitter2
+
+* v1.1.0 (2017-07-17)
+
+ * Chaining for "on" method [#29](https://github.com/igorw/evenement/pull/29)
+ * Minor performance improvements [#43](https://github.com/igorw/evenement/pull/43)
+
+* v1.0.0 (2012-05-30)
+
+ * Inital stable release
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/LICENSE b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/LICENSE
new file mode 100644
index 0000000..d9a37d0
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2011 Igor Wiedler
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/README.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/README.md
new file mode 100644
index 0000000..9443011
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/README.md
@@ -0,0 +1,83 @@
+# Événement
+
+Événement is a very simple event dispatching library for PHP.
+
+It has the same design goals as [Silex](http://silex-project.org) and
+[Pimple](http://pimple-project.org), to empower the user while staying concise
+and simple.
+
+It is very strongly inspired by the EventEmitter API found in
+[node.js](http://nodejs.org).
+
+[](http://travis-ci.org/igorw/evenement)
+
+## Fetch
+
+The recommended way to install Événement is [through composer](http://getcomposer.org).
+
+Just create a composer.json file for your project:
+
+```JSON
+{
+ "require": {
+ "evenement/evenement": "^3.0 || ^2.0"
+ }
+}
+```
+
+**Note:** The `3.x` version of Événement requires PHP 7 and the `2.x` version requires PHP 5.4. If you are
+using PHP 5.3, please use the `1.x` version:
+
+```JSON
+{
+ "require": {
+ "evenement/evenement": "^1.0"
+ }
+}
+```
+
+And run these two commands to install it:
+
+ $ curl -s http://getcomposer.org/installer | php
+ $ php composer.phar install
+
+Now you can add the autoloader, and you will have access to the library:
+
+```php
+on('user.created', function (User $user) use ($logger) {
+ $logger->log(sprintf("User '%s' was created.", $user->getLogin()));
+});
+```
+
+### Emitting Events
+
+```php
+emit('user.created', [$user]);
+```
+
+Tests
+-----
+
+ $ ./vendor/bin/phpunit
+
+License
+-------
+MIT, see LICENSE.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/composer.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/composer.json
new file mode 100644
index 0000000..cbb4827
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/composer.json
@@ -0,0 +1,29 @@
+{
+ "name": "evenement/evenement",
+ "description": "Événement is a very simple event dispatching library for PHP",
+ "keywords": ["event-dispatcher", "event-emitter"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Igor Wiedler",
+ "email": "igor@wiedler.ch"
+ }
+ ],
+ "require": {
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6.0"
+ },
+ "autoload": {
+ "psr-0": {
+ "Evenement": "src"
+ }
+ },
+ "autoload-dev": {
+ "psr-0": {
+ "Evenement": "tests"
+ },
+ "files": ["tests/Evenement/Tests/functions.php"]
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/doc/00-intro.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/doc/00-intro.md
new file mode 100644
index 0000000..6c28a2a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/doc/00-intro.md
@@ -0,0 +1,28 @@
+# Introduction
+
+Événement is is French and means "event". The événement library aims to
+provide a simple way of subscribing to events and notifying those subscribers
+whenever an event occurs.
+
+The API that it exposes is almost a direct port of the EventEmitter API found
+in node.js. It also includes an "EventEmitter". There are some minor
+differences however.
+
+The EventEmitter is an implementation of the publish-subscribe pattern, which
+is a generalized version of the observer pattern. The observer pattern
+specifies an observable subject, which observers can register themselves to.
+Once something interesting happens, the subject notifies its observers.
+
+Pub/sub takes the same idea but encapsulates the observation logic inside a
+separate object which manages all of its subscribers or listeners. Subscribers
+are bound to an event name, and will only receive notifications of the events
+they subscribed to.
+
+**TLDR: What does evenement do, in short? It provides a mapping from event
+names to a list of listener functions and triggers each listener for a given
+event when it is emitted.**
+
+Why do we do this, you ask? To achieve decoupling.
+
+It allows you to design a system where the core will emit events, and modules
+are able to subscribe to these events. And respond to them.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/doc/01-api.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/doc/01-api.md
new file mode 100644
index 0000000..17ba333
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/doc/01-api.md
@@ -0,0 +1,91 @@
+# API
+
+The API that événement exposes is defined by the
+`Evenement\EventEmitterInterface`. The interface is useful if you want to
+define an interface that extends the emitter and implicitly defines certain
+events to be emitted, or if you want to type hint an `EventEmitter` to be
+passed to a method without coupling to the specific implementation.
+
+## on($event, callable $listener)
+
+Allows you to subscribe to an event.
+
+Example:
+
+```php
+$emitter->on('user.created', function (User $user) use ($logger) {
+ $logger->log(sprintf("User '%s' was created.", $user->getLogin()));
+});
+```
+
+Since the listener can be any callable, you could also use an instance method
+instead of the anonymous function:
+
+```php
+$loggerSubscriber = new LoggerSubscriber($logger);
+$emitter->on('user.created', array($loggerSubscriber, 'onUserCreated'));
+```
+
+This has the benefit that listener does not even need to know that the emitter
+exists.
+
+You can also accept more than one parameter for the listener:
+
+```php
+$emitter->on('numbers_added', function ($result, $a, $b) {});
+```
+
+## once($event, callable $listener)
+
+Convenience method that adds a listener which is guaranteed to only be called
+once.
+
+Example:
+
+```php
+$conn->once('connected', function () use ($conn, $data) {
+ $conn->send($data);
+});
+```
+
+## emit($event, array $arguments = [])
+
+Emit an event, which will call all listeners.
+
+Example:
+
+```php
+$conn->emit('data', [$data]);
+```
+
+The second argument to emit is an array of listener arguments. This is how you
+specify more args:
+
+```php
+$result = $a + $b;
+$emitter->emit('numbers_added', [$result, $a, $b]);
+```
+
+## listeners($event)
+
+Allows you to inspect the listeners attached to an event. Particularly useful
+to check if there are any listeners at all.
+
+Example:
+
+```php
+$e = new \RuntimeException('Everything is broken!');
+if (0 === count($emitter->listeners('error'))) {
+ throw $e;
+}
+```
+
+## removeListener($event, callable $listener)
+
+Remove a specific listener for a specific event.
+
+## removeAllListeners($event = null)
+
+Remove all listeners for a specific event or all listeners all together. This
+is useful for long-running processes, where you want to remove listeners in
+order to allow them to get garbage collected.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/doc/02-plugin-system.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/doc/02-plugin-system.md
new file mode 100644
index 0000000..6a08371
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/doc/02-plugin-system.md
@@ -0,0 +1,155 @@
+# Example: Plugin system
+
+In this example I will show you how to create a generic plugin system with
+événement where plugins can alter the behaviour of the app. The app is a blog.
+Boring, I know. By using the EventEmitter it will be easy to extend this blog
+with additional functionality without modifying the core system.
+
+The blog is quite basic. Users are able to create blog posts when they log in.
+The users are stored in a static config file, so there is no sign up process.
+Once logged in they get a "new post" link which gives them a form where they
+can create a new blog post with plain HTML. That will store the post in a
+document database. The index lists all blog post titles by date descending.
+Clicking on the post title will take you to the full post.
+
+## Plugin structure
+
+The goal of the plugin system is to allow features to be added to the blog
+without modifying any core files of the blog.
+
+The plugins are managed through a config file, `plugins.json`. This JSON file
+contains a JSON-encoded list of class-names for plugin classes. This allows
+you to enable and disable plugins in a central location. The initial
+`plugins.json` is just an empty array:
+```json
+[]
+```
+
+A plugin class must implement the `PluginInterface`:
+```php
+interface PluginInterface
+{
+ function attachEvents(EventEmitterInterface $emitter);
+}
+```
+
+The `attachEvents` method allows the plugin to attach any events to the
+emitter. For example:
+```php
+class FooPlugin implements PluginInterface
+{
+ public function attachEvents(EventEmitterInterface $emitter)
+ {
+ $emitter->on('foo', function () {
+ echo 'bar!';
+ });
+ }
+}
+```
+
+The blog system creates an emitter instance and loads the plugins:
+```php
+$emitter = new EventEmitter();
+
+$pluginClasses = json_decode(file_get_contents('plugins.json'), true);
+foreach ($pluginClasses as $pluginClass) {
+ $plugin = new $pluginClass();
+ $pluginClass->attachEvents($emitter);
+}
+```
+
+This is the base system. There are no plugins yet, and there are no events yet
+either. That's because I don't know which extension points will be needed. I
+will add them on demand.
+
+## Feature: Markdown
+
+Writing blog posts in HTML sucks! Wouldn't it be great if I could write them
+in a nice format such as markdown, and have that be converted to HTML for me?
+
+This feature will need two extension points. I need to be able to mark posts
+as markdown, and I need to be able to hook into the rendering of the post body
+and convert it from markdown to HTML. So the blog needs two new events:
+`post.create` and `post.render`.
+
+In the code that creates the post, I'll insert the `post.create` event:
+```php
+class PostEvent
+{
+ public $post;
+
+ public function __construct(array $post)
+ {
+ $this->post = $post;
+ }
+}
+
+$post = createPostFromRequest($_POST);
+
+$event = new PostEvent($post);
+$emitter->emit('post.create', [$event]);
+$post = $event->post;
+
+$db->save('post', $post);
+```
+
+This shows that you can wrap a value in an event object to make it mutable,
+allowing listeners to change it.
+
+The same thing for the `post.render` event:
+```php
+public function renderPostBody(array $post)
+{
+ $emitter = $this->emitter;
+
+ $event = new PostEvent($post);
+ $emitter->emit('post.render', [$event]);
+ $post = $event->post;
+
+ return $post['body'];
+}
+
+= $post['title'] %>
+= renderPostBody($post) %>
+```
+
+Ok, the events are in place. It's time to create the first plugin, woohoo! I
+will call this the `MarkdownPlugin`, so here's `plugins.json`:
+```json
+[
+ "MarkdownPlugin"
+]
+```
+
+The `MarkdownPlugin` class will be autoloaded, so I don't have to worry about
+including any files. I just have to worry about implementing the plugin class.
+The `markdown` function represents a markdown to HTML converter.
+```php
+class MarkdownPlugin implements PluginInterface
+{
+ public function attachEvents(EventEmitterInterface $emitter)
+ {
+ $emitter->on('post.create', function (PostEvent $event) {
+ $event->post['format'] = 'markdown';
+ });
+
+ $emitter->on('post.render', function (PostEvent $event) {
+ if (isset($event->post['format']) && 'markdown' === $event->post['format']) {
+ $event->post['body'] = markdown($event->post['body']);
+ }
+ });
+ }
+}
+```
+
+There you go, the blog now renders posts as markdown. But all of the previous
+posts before the addition of the markdown plugin are still rendered correctly
+as raw HTML.
+
+## Feature: Comments
+
+TODO
+
+## Feature: Comment spam control
+
+TODO
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/examples/benchmark-emit-no-arguments.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/examples/benchmark-emit-no-arguments.php
new file mode 100644
index 0000000..53d7f4b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/examples/benchmark-emit-no-arguments.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+const ITERATIONS = 10000000;
+
+use Evenement\EventEmitter;
+
+require __DIR__.'/../vendor/autoload.php';
+
+$emitter = new EventEmitter();
+
+$emitter->on('event', function () {});
+
+$start = microtime(true);
+for ($i = 0; $i < ITERATIONS; $i++) {
+ $emitter->emit('event');
+}
+$time = microtime(true) - $start;
+
+echo 'Emitting ', number_format(ITERATIONS), ' events took: ', number_format($time, 2), 's', PHP_EOL;
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/examples/benchmark-emit-once.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/examples/benchmark-emit-once.php
new file mode 100644
index 0000000..74f4d17
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/examples/benchmark-emit-once.php
@@ -0,0 +1,30 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+ini_set('memory_limit', '512M');
+
+const ITERATIONS = 100000;
+
+use Evenement\EventEmitter;
+
+require __DIR__.'/../vendor/autoload.php';
+
+$emitter = new EventEmitter();
+
+for ($i = 0; $i < ITERATIONS; $i++) {
+ $emitter->once('event', function ($a, $b, $c) {});
+}
+
+$start = microtime(true);
+$emitter->emit('event', [1, 2, 3]);
+$time = microtime(true) - $start;
+
+echo 'Emitting one event to ', number_format(ITERATIONS), ' once listeners took: ', number_format($time, 2), 's', PHP_EOL;
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/examples/benchmark-emit-one-argument.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/examples/benchmark-emit-one-argument.php
new file mode 100644
index 0000000..39fc4ba
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/examples/benchmark-emit-one-argument.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+const ITERATIONS = 10000000;
+
+use Evenement\EventEmitter;
+
+require __DIR__.'/../vendor/autoload.php';
+
+$emitter = new EventEmitter();
+
+$emitter->on('event', function ($a) {});
+
+$start = microtime(true);
+for ($i = 0; $i < ITERATIONS; $i++) {
+ $emitter->emit('event', [1]);
+}
+$time = microtime(true) - $start;
+
+echo 'Emitting ', number_format(ITERATIONS), ' events took: ', number_format($time, 2), 's', PHP_EOL;
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/examples/benchmark-emit.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/examples/benchmark-emit.php
new file mode 100644
index 0000000..3ab639e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/examples/benchmark-emit.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+const ITERATIONS = 10000000;
+
+use Evenement\EventEmitter;
+
+require __DIR__.'/../vendor/autoload.php';
+
+$emitter = new EventEmitter();
+
+$emitter->on('event', function ($a, $b, $c) {});
+
+$start = microtime(true);
+for ($i = 0; $i < ITERATIONS; $i++) {
+ $emitter->emit('event', [1, 2, 3]);
+}
+$time = microtime(true) - $start;
+
+echo 'Emitting ', number_format(ITERATIONS), ' events took: ', number_format($time, 2), 's', PHP_EOL;
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/examples/benchmark-remove-listener-once.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/examples/benchmark-remove-listener-once.php
new file mode 100644
index 0000000..414be3b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/examples/benchmark-remove-listener-once.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+ini_set('memory_limit', '512M');
+
+const ITERATIONS = 100000;
+
+use Evenement\EventEmitter;
+
+require __DIR__.'/../vendor/autoload.php';
+
+$emitter = new EventEmitter();
+
+$listeners = [];
+for ($i = 0; $i < ITERATIONS; $i++) {
+ $listeners[] = function ($a, $b, $c) {};
+}
+
+$start = microtime(true);
+foreach ($listeners as $listener) {
+ $emitter->once('event', $listener);
+}
+$time = microtime(true) - $start;
+echo 'Adding ', number_format(ITERATIONS), ' once listeners took: ', number_format($time, 2), 's', PHP_EOL;
+
+$start = microtime(true);
+foreach ($listeners as $listener) {
+ $emitter->removeListener('event', $listener);
+}
+$time = microtime(true) - $start;
+echo 'Removing ', number_format(ITERATIONS), ' once listeners took: ', number_format($time, 2), 's', PHP_EOL;
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/phpunit.xml.dist b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/phpunit.xml.dist
new file mode 100644
index 0000000..70bc693
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/phpunit.xml.dist
@@ -0,0 +1,24 @@
+
+
+
+
+
+ ./tests/Evenement/
+
+
+
+
+
+ ./src/
+
+
+
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/src/Evenement/EventEmitter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/src/Evenement/EventEmitter.php
new file mode 100644
index 0000000..db189b9
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/src/Evenement/EventEmitter.php
@@ -0,0 +1,17 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Evenement;
+
+class EventEmitter implements EventEmitterInterface
+{
+ use EventEmitterTrait;
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/src/Evenement/EventEmitterInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/src/Evenement/EventEmitterInterface.php
new file mode 100644
index 0000000..310631a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/src/Evenement/EventEmitterInterface.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Evenement;
+
+interface EventEmitterInterface
+{
+ public function on($event, callable $listener);
+ public function once($event, callable $listener);
+ public function removeListener($event, callable $listener);
+ public function removeAllListeners($event = null);
+ public function listeners($event = null);
+ public function emit($event, array $arguments = []);
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php
new file mode 100644
index 0000000..a78e65c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php
@@ -0,0 +1,135 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Evenement;
+
+use InvalidArgumentException;
+
+trait EventEmitterTrait
+{
+ protected $listeners = [];
+ protected $onceListeners = [];
+
+ public function on($event, callable $listener)
+ {
+ if ($event === null) {
+ throw new InvalidArgumentException('event name must not be null');
+ }
+
+ if (!isset($this->listeners[$event])) {
+ $this->listeners[$event] = [];
+ }
+
+ $this->listeners[$event][] = $listener;
+
+ return $this;
+ }
+
+ public function once($event, callable $listener)
+ {
+ if ($event === null) {
+ throw new InvalidArgumentException('event name must not be null');
+ }
+
+ if (!isset($this->onceListeners[$event])) {
+ $this->onceListeners[$event] = [];
+ }
+
+ $this->onceListeners[$event][] = $listener;
+
+ return $this;
+ }
+
+ public function removeListener($event, callable $listener)
+ {
+ if ($event === null) {
+ throw new InvalidArgumentException('event name must not be null');
+ }
+
+ if (isset($this->listeners[$event])) {
+ $index = \array_search($listener, $this->listeners[$event], true);
+ if (false !== $index) {
+ unset($this->listeners[$event][$index]);
+ if (\count($this->listeners[$event]) === 0) {
+ unset($this->listeners[$event]);
+ }
+ }
+ }
+
+ if (isset($this->onceListeners[$event])) {
+ $index = \array_search($listener, $this->onceListeners[$event], true);
+ if (false !== $index) {
+ unset($this->onceListeners[$event][$index]);
+ if (\count($this->onceListeners[$event]) === 0) {
+ unset($this->onceListeners[$event]);
+ }
+ }
+ }
+ }
+
+ public function removeAllListeners($event = null)
+ {
+ if ($event !== null) {
+ unset($this->listeners[$event]);
+ } else {
+ $this->listeners = [];
+ }
+
+ if ($event !== null) {
+ unset($this->onceListeners[$event]);
+ } else {
+ $this->onceListeners = [];
+ }
+ }
+
+ public function listeners($event = null): array
+ {
+ if ($event === null) {
+ $events = [];
+ $eventNames = \array_unique(
+ \array_merge(\array_keys($this->listeners), \array_keys($this->onceListeners))
+ );
+ foreach ($eventNames as $eventName) {
+ $events[$eventName] = \array_merge(
+ isset($this->listeners[$eventName]) ? $this->listeners[$eventName] : [],
+ isset($this->onceListeners[$eventName]) ? $this->onceListeners[$eventName] : []
+ );
+ }
+ return $events;
+ }
+
+ return \array_merge(
+ isset($this->listeners[$event]) ? $this->listeners[$event] : [],
+ isset($this->onceListeners[$event]) ? $this->onceListeners[$event] : []
+ );
+ }
+
+ public function emit($event, array $arguments = [])
+ {
+ if ($event === null) {
+ throw new InvalidArgumentException('event name must not be null');
+ }
+
+ if (isset($this->listeners[$event])) {
+ foreach ($this->listeners[$event] as $listener) {
+ $listener(...$arguments);
+ }
+ }
+
+ if (isset($this->onceListeners[$event])) {
+ $listeners = $this->onceListeners[$event];
+ unset($this->onceListeners[$event]);
+ foreach ($listeners as $listener) {
+ $listener(...$arguments);
+ }
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/tests/Evenement/Tests/EventEmitterTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/tests/Evenement/Tests/EventEmitterTest.php
new file mode 100644
index 0000000..28f3011
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/tests/Evenement/Tests/EventEmitterTest.php
@@ -0,0 +1,438 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Evenement\Tests;
+
+use Evenement\EventEmitter;
+use InvalidArgumentException;
+use PHPUnit\Framework\TestCase;
+
+class EventEmitterTest extends TestCase
+{
+ private $emitter;
+
+ public function setUp()
+ {
+ $this->emitter = new EventEmitter();
+ }
+
+ public function testAddListenerWithLambda()
+ {
+ $this->emitter->on('foo', function () {});
+ }
+
+ public function testAddListenerWithMethod()
+ {
+ $listener = new Listener();
+ $this->emitter->on('foo', [$listener, 'onFoo']);
+ }
+
+ public function testAddListenerWithStaticMethod()
+ {
+ $this->emitter->on('bar', ['Evenement\Tests\Listener', 'onBar']);
+ }
+
+ public function testAddListenerWithInvalidListener()
+ {
+ try {
+ $this->emitter->on('foo', 'not a callable');
+ $this->fail();
+ } catch (\Exception $e) {
+ } catch (\TypeError $e) {
+ }
+ }
+
+ public function testOnce()
+ {
+ $listenerCalled = 0;
+
+ $this->emitter->once('foo', function () use (&$listenerCalled) {
+ $listenerCalled++;
+ });
+
+ $this->assertSame(0, $listenerCalled);
+
+ $this->emitter->emit('foo');
+
+ $this->assertSame(1, $listenerCalled);
+
+ $this->emitter->emit('foo');
+
+ $this->assertSame(1, $listenerCalled);
+ }
+
+ public function testOnceWithArguments()
+ {
+ $capturedArgs = [];
+
+ $this->emitter->once('foo', function ($a, $b) use (&$capturedArgs) {
+ $capturedArgs = array($a, $b);
+ });
+
+ $this->emitter->emit('foo', array('a', 'b'));
+
+ $this->assertSame(array('a', 'b'), $capturedArgs);
+ }
+
+ public function testEmitWithoutArguments()
+ {
+ $listenerCalled = false;
+
+ $this->emitter->on('foo', function () use (&$listenerCalled) {
+ $listenerCalled = true;
+ });
+
+ $this->assertSame(false, $listenerCalled);
+ $this->emitter->emit('foo');
+ $this->assertSame(true, $listenerCalled);
+ }
+
+ public function testEmitWithOneArgument()
+ {
+ $test = $this;
+
+ $listenerCalled = false;
+
+ $this->emitter->on('foo', function ($value) use (&$listenerCalled, $test) {
+ $listenerCalled = true;
+
+ $test->assertSame('bar', $value);
+ });
+
+ $this->assertSame(false, $listenerCalled);
+ $this->emitter->emit('foo', ['bar']);
+ $this->assertSame(true, $listenerCalled);
+ }
+
+ public function testEmitWithTwoArguments()
+ {
+ $test = $this;
+
+ $listenerCalled = false;
+
+ $this->emitter->on('foo', function ($arg1, $arg2) use (&$listenerCalled, $test) {
+ $listenerCalled = true;
+
+ $test->assertSame('bar', $arg1);
+ $test->assertSame('baz', $arg2);
+ });
+
+ $this->assertSame(false, $listenerCalled);
+ $this->emitter->emit('foo', ['bar', 'baz']);
+ $this->assertSame(true, $listenerCalled);
+ }
+
+ public function testEmitWithNoListeners()
+ {
+ $this->emitter->emit('foo');
+ $this->emitter->emit('foo', ['bar']);
+ $this->emitter->emit('foo', ['bar', 'baz']);
+ }
+
+ public function testEmitWithTwoListeners()
+ {
+ $listenersCalled = 0;
+
+ $this->emitter->on('foo', function () use (&$listenersCalled) {
+ $listenersCalled++;
+ });
+
+ $this->emitter->on('foo', function () use (&$listenersCalled) {
+ $listenersCalled++;
+ });
+
+ $this->assertSame(0, $listenersCalled);
+ $this->emitter->emit('foo');
+ $this->assertSame(2, $listenersCalled);
+ }
+
+ public function testRemoveListenerMatching()
+ {
+ $listenersCalled = 0;
+
+ $listener = function () use (&$listenersCalled) {
+ $listenersCalled++;
+ };
+
+ $this->emitter->on('foo', $listener);
+ $this->emitter->removeListener('foo', $listener);
+
+ $this->assertSame(0, $listenersCalled);
+ $this->emitter->emit('foo');
+ $this->assertSame(0, $listenersCalled);
+ }
+
+ public function testRemoveListenerNotMatching()
+ {
+ $listenersCalled = 0;
+
+ $listener = function () use (&$listenersCalled) {
+ $listenersCalled++;
+ };
+
+ $this->emitter->on('foo', $listener);
+ $this->emitter->removeListener('bar', $listener);
+
+ $this->assertSame(0, $listenersCalled);
+ $this->emitter->emit('foo');
+ $this->assertSame(1, $listenersCalled);
+ }
+
+ public function testRemoveAllListenersMatching()
+ {
+ $listenersCalled = 0;
+
+ $this->emitter->on('foo', function () use (&$listenersCalled) {
+ $listenersCalled++;
+ });
+
+ $this->emitter->removeAllListeners('foo');
+
+ $this->assertSame(0, $listenersCalled);
+ $this->emitter->emit('foo');
+ $this->assertSame(0, $listenersCalled);
+ }
+
+ public function testRemoveAllListenersNotMatching()
+ {
+ $listenersCalled = 0;
+
+ $this->emitter->on('foo', function () use (&$listenersCalled) {
+ $listenersCalled++;
+ });
+
+ $this->emitter->removeAllListeners('bar');
+
+ $this->assertSame(0, $listenersCalled);
+ $this->emitter->emit('foo');
+ $this->assertSame(1, $listenersCalled);
+ }
+
+ public function testRemoveAllListenersWithoutArguments()
+ {
+ $listenersCalled = 0;
+
+ $this->emitter->on('foo', function () use (&$listenersCalled) {
+ $listenersCalled++;
+ });
+
+ $this->emitter->on('bar', function () use (&$listenersCalled) {
+ $listenersCalled++;
+ });
+
+ $this->emitter->removeAllListeners();
+
+ $this->assertSame(0, $listenersCalled);
+ $this->emitter->emit('foo');
+ $this->emitter->emit('bar');
+ $this->assertSame(0, $listenersCalled);
+ }
+
+ public function testCallablesClosure()
+ {
+ $calledWith = null;
+
+ $this->emitter->on('foo', function ($data) use (&$calledWith) {
+ $calledWith = $data;
+ });
+
+ $this->emitter->emit('foo', ['bar']);
+
+ self::assertSame('bar', $calledWith);
+ }
+
+ public function testCallablesClass()
+ {
+ $listener = new Listener();
+ $this->emitter->on('foo', [$listener, 'onFoo']);
+
+ $this->emitter->emit('foo', ['bar']);
+
+ self::assertSame(['bar'], $listener->getData());
+ }
+
+
+ public function testCallablesClassInvoke()
+ {
+ $listener = new Listener();
+ $this->emitter->on('foo', $listener);
+
+ $this->emitter->emit('foo', ['bar']);
+
+ self::assertSame(['bar'], $listener->getMagicData());
+ }
+
+ public function testCallablesStaticClass()
+ {
+ $this->emitter->on('foo', '\Evenement\Tests\Listener::onBar');
+
+ $this->emitter->emit('foo', ['bar']);
+
+ self::assertSame(['bar'], Listener::getStaticData());
+ }
+
+ public function testCallablesFunction()
+ {
+ $this->emitter->on('foo', '\Evenement\Tests\setGlobalTestData');
+
+ $this->emitter->emit('foo', ['bar']);
+
+ self::assertSame('bar', $GLOBALS['evenement-evenement-test-data']);
+
+ unset($GLOBALS['evenement-evenement-test-data']);
+ }
+
+ public function testListeners()
+ {
+ $onA = function () {};
+ $onB = function () {};
+ $onC = function () {};
+ $onceA = function () {};
+ $onceB = function () {};
+ $onceC = function () {};
+
+ self::assertCount(0, $this->emitter->listeners('event'));
+ $this->emitter->on('event', $onA);
+ self::assertCount(1, $this->emitter->listeners('event'));
+ self::assertSame([$onA], $this->emitter->listeners('event'));
+ $this->emitter->once('event', $onceA);
+ self::assertCount(2, $this->emitter->listeners('event'));
+ self::assertSame([$onA, $onceA], $this->emitter->listeners('event'));
+ $this->emitter->once('event', $onceB);
+ self::assertCount(3, $this->emitter->listeners('event'));
+ self::assertSame([$onA, $onceA, $onceB], $this->emitter->listeners('event'));
+ $this->emitter->on('event', $onB);
+ self::assertCount(4, $this->emitter->listeners('event'));
+ self::assertSame([$onA, $onB, $onceA, $onceB], $this->emitter->listeners('event'));
+ $this->emitter->removeListener('event', $onceA);
+ self::assertCount(3, $this->emitter->listeners('event'));
+ self::assertSame([$onA, $onB, $onceB], $this->emitter->listeners('event'));
+ $this->emitter->once('event', $onceC);
+ self::assertCount(4, $this->emitter->listeners('event'));
+ self::assertSame([$onA, $onB, $onceB, $onceC], $this->emitter->listeners('event'));
+ $this->emitter->on('event', $onC);
+ self::assertCount(5, $this->emitter->listeners('event'));
+ self::assertSame([$onA, $onB, $onC, $onceB, $onceC], $this->emitter->listeners('event'));
+ $this->emitter->once('event', $onceA);
+ self::assertCount(6, $this->emitter->listeners('event'));
+ self::assertSame([$onA, $onB, $onC, $onceB, $onceC, $onceA], $this->emitter->listeners('event'));
+ $this->emitter->removeListener('event', $onB);
+ self::assertCount(5, $this->emitter->listeners('event'));
+ self::assertSame([$onA, $onC, $onceB, $onceC, $onceA], $this->emitter->listeners('event'));
+ $this->emitter->emit('event');
+ self::assertCount(2, $this->emitter->listeners('event'));
+ self::assertSame([$onA, $onC], $this->emitter->listeners('event'));
+ }
+
+ public function testOnceCallIsNotRemovedWhenWorkingOverOnceListeners()
+ {
+ $aCalled = false;
+ $aCallable = function () use (&$aCalled) {
+ $aCalled = true;
+ };
+ $bCalled = false;
+ $bCallable = function () use (&$bCalled, $aCallable) {
+ $bCalled = true;
+ $this->emitter->once('event', $aCallable);
+ };
+ $this->emitter->once('event', $bCallable);
+
+ self::assertFalse($aCalled);
+ self::assertFalse($bCalled);
+ $this->emitter->emit('event');
+
+ self::assertFalse($aCalled);
+ self::assertTrue($bCalled);
+ $this->emitter->emit('event');
+
+ self::assertTrue($aCalled);
+ self::assertTrue($bCalled);
+ }
+
+ public function testEventNameMustBeStringOn()
+ {
+ self::expectException(InvalidArgumentException::class);
+ self::expectExceptionMessage('event name must not be null');
+
+ $this->emitter->on(null, function () {});
+ }
+
+ public function testEventNameMustBeStringOnce()
+ {
+ self::expectException(InvalidArgumentException::class);
+ self::expectExceptionMessage('event name must not be null');
+
+ $this->emitter->once(null, function () {});
+ }
+
+ public function testEventNameMustBeStringRemoveListener()
+ {
+ self::expectException(InvalidArgumentException::class);
+ self::expectExceptionMessage('event name must not be null');
+
+ $this->emitter->removeListener(null, function () {});
+ }
+
+ public function testEventNameMustBeStringEmit()
+ {
+ self::expectException(InvalidArgumentException::class);
+ self::expectExceptionMessage('event name must not be null');
+
+ $this->emitter->emit(null);
+ }
+
+ public function testListenersGetAll()
+ {
+ $a = function () {};
+ $b = function () {};
+ $c = function () {};
+ $d = function () {};
+
+ $this->emitter->once('event2', $c);
+ $this->emitter->on('event', $a);
+ $this->emitter->once('event', $b);
+ $this->emitter->on('event', $c);
+ $this->emitter->once('event', $d);
+
+ self::assertSame(
+ [
+ 'event' => [
+ $a,
+ $c,
+ $b,
+ $d,
+ ],
+ 'event2' => [
+ $c,
+ ],
+ ],
+ $this->emitter->listeners()
+ );
+ }
+
+ public function testOnceNestedCallRegression()
+ {
+ $first = 0;
+ $second = 0;
+
+ $this->emitter->once('event', function () use (&$first, &$second) {
+ $first++;
+ $this->emitter->once('event', function () use (&$second) {
+ $second++;
+ });
+ $this->emitter->emit('event');
+ });
+ $this->emitter->emit('event');
+
+ self::assertSame(1, $first);
+ self::assertSame(1, $second);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/tests/Evenement/Tests/Listener.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/tests/Evenement/Tests/Listener.php
new file mode 100644
index 0000000..df17424
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/tests/Evenement/Tests/Listener.php
@@ -0,0 +1,51 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Evenement\Tests;
+
+class Listener
+{
+ private $data = [];
+
+ private $magicData = [];
+
+ private static $staticData = [];
+
+ public function onFoo($data)
+ {
+ $this->data[] = $data;
+ }
+
+ public function __invoke($data)
+ {
+ $this->magicData[] = $data;
+ }
+
+ public static function onBar($data)
+ {
+ self::$staticData[] = $data;
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ public function getMagicData()
+ {
+ return $this->magicData;
+ }
+
+ public static function getStaticData()
+ {
+ return self::$staticData;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/tests/Evenement/Tests/functions.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/tests/Evenement/Tests/functions.php
new file mode 100644
index 0000000..7f11f5b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/evenement/evenement/tests/Evenement/Tests/functions.php
@@ -0,0 +1,17 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Evenement\Tests;
+
+function setGlobalTestData($data)
+{
+ $GLOBALS['evenement-evenement-test-data'] = $data;
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/CHANGELOG.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/CHANGELOG.md
new file mode 100644
index 0000000..5c252b3
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/CHANGELOG.md
@@ -0,0 +1,110 @@
+# CHANGELOG
+
+## 1.4.2 - 2017-03-20
+
+* Reverted BC break to `Uri::resolve` and `Uri::removeDotSegments` by removing
+ calls to `trigger_error` when deprecated methods are invoked.
+
+## 1.4.1 - 2017-02-27
+
+* Reverted BC break by reintroducing behavior to automagically fix a URI with a
+ relative path and an authority by adding a leading slash to the path. It's only
+ deprecated now.
+* Added triggering of silenced deprecation warnings.
+
+## 1.4.0 - 2017-02-21
+
+* Fix `Stream::read` when length parameter <= 0.
+* `copy_to_stream` reads bytes in chunks instead of `maxLen` into memory.
+* Fix `ServerRequest::getUriFromGlobals` when `Host` header contains port.
+* Ensure `ServerRequest::getUriFromGlobals` returns a URI in absolute form.
+* Allow `parse_response` to parse a response without delimiting space and reason.
+* Ensure each URI modification results in a valid URI according to PSR-7 discussions.
+ Invalid modifications will throw an exception instead of returning a wrong URI or
+ doing some magic.
+ - `(new Uri)->withPath('foo')->withHost('example.com')` will throw an exception
+ because the path of a URI with an authority must start with a slash "/" or be empty
+ - `(new Uri())->withScheme('http')` will return `'http://localhost'`
+* Fix compatibility of URIs with `file` scheme and empty host.
+* Added common URI utility methods based on RFC 3986 (see documentation in the readme):
+ - `Uri::isDefaultPort`
+ - `Uri::isAbsolute`
+ - `Uri::isNetworkPathReference`
+ - `Uri::isAbsolutePathReference`
+ - `Uri::isRelativePathReference`
+ - `Uri::isSameDocumentReference`
+ - `Uri::composeComponents`
+ - `UriNormalizer::normalize`
+ - `UriNormalizer::isEquivalent`
+ - `UriResolver::relativize`
+* Deprecated `Uri::resolve` in favor of `UriResolver::resolve`
+* Deprecated `Uri::removeDotSegments` in favor of `UriResolver::removeDotSegments`
+
+## 1.3.1 - 2016-06-25
+
+* Fix `Uri::__toString` for network path references, e.g. `//example.org`.
+* Fix missing lowercase normalization for host.
+* Fix handling of URI components in case they are `'0'` in a lot of places,
+ e.g. as a user info password.
+* Fix `Uri::withAddedHeader` to correctly merge headers with different case.
+* Fix trimming of header values in `Uri::withAddedHeader`. Header values may
+ be surrounded by whitespace which should be ignored according to RFC 7230
+ Section 3.2.4. This does not apply to header names.
+* Fix `Uri::withAddedHeader` with an array of header values.
+* Fix `Uri::resolve` when base path has no slash and handling of fragment.
+* Fix handling of encoding in `Uri::with(out)QueryValue` so one can pass the
+ key/value both in encoded as well as decoded form to those methods. This is
+ consistent with withPath, withQuery etc.
+* Fix `ServerRequest::withoutAttribute` when attribute value is null.
+
+## 1.3.0 - 2016-04-13
+
+* Added remaining interfaces needed for full PSR7 compatibility
+ (ServerRequestInterface, UploadedFileInterface, etc.).
+* Added support for stream_for from scalars.
+* Can now extend Uri.
+* Fixed a bug in validating request methods by making it more permissive.
+
+## 1.2.3 - 2016-02-18
+
+* Fixed support in `GuzzleHttp\Psr7\CachingStream` for seeking forward on remote
+ streams, which can sometimes return fewer bytes than requested with `fread`.
+* Fixed handling of gzipped responses with FNAME headers.
+
+## 1.2.2 - 2016-01-22
+
+* Added support for URIs without any authority.
+* Added support for HTTP 451 'Unavailable For Legal Reasons.'
+* Added support for using '0' as a filename.
+* Added support for including non-standard ports in Host headers.
+
+## 1.2.1 - 2015-11-02
+
+* Now supporting negative offsets when seeking to SEEK_END.
+
+## 1.2.0 - 2015-08-15
+
+* Body as `"0"` is now properly added to a response.
+* Now allowing forward seeking in CachingStream.
+* Now properly parsing HTTP requests that contain proxy targets in
+ `parse_request`.
+* functions.php is now conditionally required.
+* user-info is no longer dropped when resolving URIs.
+
+## 1.1.0 - 2015-06-24
+
+* URIs can now be relative.
+* `multipart/form-data` headers are now overridden case-insensitively.
+* URI paths no longer encode the following characters because they are allowed
+ in URIs: "(", ")", "*", "!", "'"
+* A port is no longer added to a URI when the scheme is missing and no port is
+ present.
+
+## 1.0.0 - 2015-05-19
+
+Initial release.
+
+Currently unsupported:
+
+- `Psr\Http\Message\ServerRequestInterface`
+- `Psr\Http\Message\UploadedFileInterface`
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/LICENSE b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/LICENSE
new file mode 100644
index 0000000..581d95f
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015 Michael Dowling, https://github.com/mtdowling
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/README.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/README.md
new file mode 100644
index 0000000..1649935
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/README.md
@@ -0,0 +1,739 @@
+# PSR-7 Message Implementation
+
+This repository contains a full [PSR-7](http://www.php-fig.org/psr/psr-7/)
+message implementation, several stream decorators, and some helpful
+functionality like query string parsing.
+
+
+[](https://travis-ci.org/guzzle/psr7)
+
+
+# Stream implementation
+
+This package comes with a number of stream implementations and stream
+decorators.
+
+
+## AppendStream
+
+`GuzzleHttp\Psr7\AppendStream`
+
+Reads from multiple streams, one after the other.
+
+```php
+use GuzzleHttp\Psr7;
+
+$a = Psr7\stream_for('abc, ');
+$b = Psr7\stream_for('123.');
+$composed = new Psr7\AppendStream([$a, $b]);
+
+$composed->addStream(Psr7\stream_for(' Above all listen to me'));
+
+echo $composed; // abc, 123. Above all listen to me.
+```
+
+
+## BufferStream
+
+`GuzzleHttp\Psr7\BufferStream`
+
+Provides a buffer stream that can be written to fill a buffer, and read
+from to remove bytes from the buffer.
+
+This stream returns a "hwm" metadata value that tells upstream consumers
+what the configured high water mark of the stream is, or the maximum
+preferred size of the buffer.
+
+```php
+use GuzzleHttp\Psr7;
+
+// When more than 1024 bytes are in the buffer, it will begin returning
+// false to writes. This is an indication that writers should slow down.
+$buffer = new Psr7\BufferStream(1024);
+```
+
+
+## CachingStream
+
+The CachingStream is used to allow seeking over previously read bytes on
+non-seekable streams. This can be useful when transferring a non-seekable
+entity body fails due to needing to rewind the stream (for example, resulting
+from a redirect). Data that is read from the remote stream will be buffered in
+a PHP temp stream so that previously read bytes are cached first in memory,
+then on disk.
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\stream_for(fopen('http://www.google.com', 'r'));
+$stream = new Psr7\CachingStream($original);
+
+$stream->read(1024);
+echo $stream->tell();
+// 1024
+
+$stream->seek(0);
+echo $stream->tell();
+// 0
+```
+
+
+## DroppingStream
+
+`GuzzleHttp\Psr7\DroppingStream`
+
+Stream decorator that begins dropping data once the size of the underlying
+stream becomes too full.
+
+```php
+use GuzzleHttp\Psr7;
+
+// Create an empty stream
+$stream = Psr7\stream_for();
+
+// Start dropping data when the stream has more than 10 bytes
+$dropping = new Psr7\DroppingStream($stream, 10);
+
+$dropping->write('01234567890123456789');
+echo $stream; // 0123456789
+```
+
+
+## FnStream
+
+`GuzzleHttp\Psr7\FnStream`
+
+Compose stream implementations based on a hash of functions.
+
+Allows for easy testing and extension of a provided stream without needing
+to create a concrete class for a simple extension point.
+
+```php
+
+use GuzzleHttp\Psr7;
+
+$stream = Psr7\stream_for('hi');
+$fnStream = Psr7\FnStream::decorate($stream, [
+ 'rewind' => function () use ($stream) {
+ echo 'About to rewind - ';
+ $stream->rewind();
+ echo 'rewound!';
+ }
+]);
+
+$fnStream->rewind();
+// Outputs: About to rewind - rewound!
+```
+
+
+## InflateStream
+
+`GuzzleHttp\Psr7\InflateStream`
+
+Uses PHP's zlib.inflate filter to inflate deflate or gzipped content.
+
+This stream decorator skips the first 10 bytes of the given stream to remove
+the gzip header, converts the provided stream to a PHP stream resource,
+then appends the zlib.inflate filter. The stream is then converted back
+to a Guzzle stream resource to be used as a Guzzle stream.
+
+
+## LazyOpenStream
+
+`GuzzleHttp\Psr7\LazyOpenStream`
+
+Lazily reads or writes to a file that is opened only after an IO operation
+take place on the stream.
+
+```php
+use GuzzleHttp\Psr7;
+
+$stream = new Psr7\LazyOpenStream('/path/to/file', 'r');
+// The file has not yet been opened...
+
+echo $stream->read(10);
+// The file is opened and read from only when needed.
+```
+
+
+## LimitStream
+
+`GuzzleHttp\Psr7\LimitStream`
+
+LimitStream can be used to read a subset or slice of an existing stream object.
+This can be useful for breaking a large file into smaller pieces to be sent in
+chunks (e.g. Amazon S3's multipart upload API).
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\stream_for(fopen('/tmp/test.txt', 'r+'));
+echo $original->getSize();
+// >>> 1048576
+
+// Limit the size of the body to 1024 bytes and start reading from byte 2048
+$stream = new Psr7\LimitStream($original, 1024, 2048);
+echo $stream->getSize();
+// >>> 1024
+echo $stream->tell();
+// >>> 0
+```
+
+
+## MultipartStream
+
+`GuzzleHttp\Psr7\MultipartStream`
+
+Stream that when read returns bytes for a streaming multipart or
+multipart/form-data stream.
+
+
+## NoSeekStream
+
+`GuzzleHttp\Psr7\NoSeekStream`
+
+NoSeekStream wraps a stream and does not allow seeking.
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\stream_for('foo');
+$noSeek = new Psr7\NoSeekStream($original);
+
+echo $noSeek->read(3);
+// foo
+var_export($noSeek->isSeekable());
+// false
+$noSeek->seek(0);
+var_export($noSeek->read(3));
+// NULL
+```
+
+
+## PumpStream
+
+`GuzzleHttp\Psr7\PumpStream`
+
+Provides a read only stream that pumps data from a PHP callable.
+
+When invoking the provided callable, the PumpStream will pass the amount of
+data requested to read to the callable. The callable can choose to ignore
+this value and return fewer or more bytes than requested. Any extra data
+returned by the provided callable is buffered internally until drained using
+the read() function of the PumpStream. The provided callable MUST return
+false when there is no more data to read.
+
+
+## Implementing stream decorators
+
+Creating a stream decorator is very easy thanks to the
+`GuzzleHttp\Psr7\StreamDecoratorTrait`. This trait provides methods that
+implement `Psr\Http\Message\StreamInterface` by proxying to an underlying
+stream. Just `use` the `StreamDecoratorTrait` and implement your custom
+methods.
+
+For example, let's say we wanted to call a specific function each time the last
+byte is read from a stream. This could be implemented by overriding the
+`read()` method.
+
+```php
+use Psr\Http\Message\StreamInterface;
+use GuzzleHttp\Psr7\StreamDecoratorTrait;
+
+class EofCallbackStream implements StreamInterface
+{
+ use StreamDecoratorTrait;
+
+ private $callback;
+
+ public function __construct(StreamInterface $stream, callable $cb)
+ {
+ $this->stream = $stream;
+ $this->callback = $cb;
+ }
+
+ public function read($length)
+ {
+ $result = $this->stream->read($length);
+
+ // Invoke the callback when EOF is hit.
+ if ($this->eof()) {
+ call_user_func($this->callback);
+ }
+
+ return $result;
+ }
+}
+```
+
+This decorator could be added to any existing stream and used like so:
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\stream_for('foo');
+
+$eofStream = new EofCallbackStream($original, function () {
+ echo 'EOF!';
+});
+
+$eofStream->read(2);
+$eofStream->read(1);
+// echoes "EOF!"
+$eofStream->seek(0);
+$eofStream->read(3);
+// echoes "EOF!"
+```
+
+
+## PHP StreamWrapper
+
+You can use the `GuzzleHttp\Psr7\StreamWrapper` class if you need to use a
+PSR-7 stream as a PHP stream resource.
+
+Use the `GuzzleHttp\Psr7\StreamWrapper::getResource()` method to create a PHP
+stream from a PSR-7 stream.
+
+```php
+use GuzzleHttp\Psr7\StreamWrapper;
+
+$stream = GuzzleHttp\Psr7\stream_for('hello!');
+$resource = StreamWrapper::getResource($stream);
+echo fread($resource, 6); // outputs hello!
+```
+
+
+# Function API
+
+There are various functions available under the `GuzzleHttp\Psr7` namespace.
+
+
+## `function str`
+
+`function str(MessageInterface $message)`
+
+Returns the string representation of an HTTP message.
+
+```php
+$request = new GuzzleHttp\Psr7\Request('GET', 'http://example.com');
+echo GuzzleHttp\Psr7\str($request);
+```
+
+
+## `function uri_for`
+
+`function uri_for($uri)`
+
+This function accepts a string or `Psr\Http\Message\UriInterface` and returns a
+UriInterface for the given value. If the value is already a `UriInterface`, it
+is returned as-is.
+
+```php
+$uri = GuzzleHttp\Psr7\uri_for('http://example.com');
+assert($uri === GuzzleHttp\Psr7\uri_for($uri));
+```
+
+
+## `function stream_for`
+
+`function stream_for($resource = '', array $options = [])`
+
+Create a new stream based on the input type.
+
+Options is an associative array that can contain the following keys:
+
+* - metadata: Array of custom metadata.
+* - size: Size of the stream.
+
+This method accepts the following `$resource` types:
+
+- `Psr\Http\Message\StreamInterface`: Returns the value as-is.
+- `string`: Creates a stream object that uses the given string as the contents.
+- `resource`: Creates a stream object that wraps the given PHP stream resource.
+- `Iterator`: If the provided value implements `Iterator`, then a read-only
+ stream object will be created that wraps the given iterable. Each time the
+ stream is read from, data from the iterator will fill a buffer and will be
+ continuously called until the buffer is equal to the requested read size.
+ Subsequent read calls will first read from the buffer and then call `next`
+ on the underlying iterator until it is exhausted.
+- `object` with `__toString()`: If the object has the `__toString()` method,
+ the object will be cast to a string and then a stream will be returned that
+ uses the string value.
+- `NULL`: When `null` is passed, an empty stream object is returned.
+- `callable` When a callable is passed, a read-only stream object will be
+ created that invokes the given callable. The callable is invoked with the
+ number of suggested bytes to read. The callable can return any number of
+ bytes, but MUST return `false` when there is no more data to return. The
+ stream object that wraps the callable will invoke the callable until the
+ number of requested bytes are available. Any additional bytes will be
+ buffered and used in subsequent reads.
+
+```php
+$stream = GuzzleHttp\Psr7\stream_for('foo');
+$stream = GuzzleHttp\Psr7\stream_for(fopen('/path/to/file', 'r'));
+
+$generator function ($bytes) {
+ for ($i = 0; $i < $bytes; $i++) {
+ yield ' ';
+ }
+}
+
+$stream = GuzzleHttp\Psr7\stream_for($generator(100));
+```
+
+
+## `function parse_header`
+
+`function parse_header($header)`
+
+Parse an array of header values containing ";" separated data into an array of
+associative arrays representing the header key value pair data of the header.
+When a parameter does not contain a value, but just contains a key, this
+function will inject a key with a '' string value.
+
+
+## `function normalize_header`
+
+`function normalize_header($header)`
+
+Converts an array of header values that may contain comma separated headers
+into an array of headers with no comma separated values.
+
+
+## `function modify_request`
+
+`function modify_request(RequestInterface $request, array $changes)`
+
+Clone and modify a request with the given changes. This method is useful for
+reducing the number of clones needed to mutate a message.
+
+The changes can be one of:
+
+- method: (string) Changes the HTTP method.
+- set_headers: (array) Sets the given headers.
+- remove_headers: (array) Remove the given headers.
+- body: (mixed) Sets the given body.
+- uri: (UriInterface) Set the URI.
+- query: (string) Set the query string value of the URI.
+- version: (string) Set the protocol version.
+
+
+## `function rewind_body`
+
+`function rewind_body(MessageInterface $message)`
+
+Attempts to rewind a message body and throws an exception on failure. The body
+of the message will only be rewound if a call to `tell()` returns a value other
+than `0`.
+
+
+## `function try_fopen`
+
+`function try_fopen($filename, $mode)`
+
+Safely opens a PHP stream resource using a filename.
+
+When fopen fails, PHP normally raises a warning. This function adds an error
+handler that checks for errors and throws an exception instead.
+
+
+## `function copy_to_string`
+
+`function copy_to_string(StreamInterface $stream, $maxLen = -1)`
+
+Copy the contents of a stream into a string until the given number of bytes
+have been read.
+
+
+## `function copy_to_stream`
+
+`function copy_to_stream(StreamInterface $source, StreamInterface $dest, $maxLen = -1)`
+
+Copy the contents of a stream into another stream until the given number of
+bytes have been read.
+
+
+## `function hash`
+
+`function hash(StreamInterface $stream, $algo, $rawOutput = false)`
+
+Calculate a hash of a Stream. This method reads the entire stream to calculate
+a rolling hash (based on PHP's hash_init functions).
+
+
+## `function readline`
+
+`function readline(StreamInterface $stream, $maxLength = null)`
+
+Read a line from the stream up to the maximum allowed buffer length.
+
+
+## `function parse_request`
+
+`function parse_request($message)`
+
+Parses a request message string into a request object.
+
+
+## `function parse_response`
+
+`function parse_response($message)`
+
+Parses a response message string into a response object.
+
+
+## `function parse_query`
+
+`function parse_query($str, $urlEncoding = true)`
+
+Parse a query string into an associative array.
+
+If multiple values are found for the same key, the value of that key value pair
+will become an array. This function does not parse nested PHP style arrays into
+an associative array (e.g., `foo[a]=1&foo[b]=2` will be parsed into
+`['foo[a]' => '1', 'foo[b]' => '2']`).
+
+
+## `function build_query`
+
+`function build_query(array $params, $encoding = PHP_QUERY_RFC3986)`
+
+Build a query string from an array of key value pairs.
+
+This function can use the return value of parse_query() to build a query string.
+This function does not modify the provided keys when an array is encountered
+(like http_build_query would).
+
+
+## `function mimetype_from_filename`
+
+`function mimetype_from_filename($filename)`
+
+Determines the mimetype of a file by looking at its extension.
+
+
+## `function mimetype_from_extension`
+
+`function mimetype_from_extension($extension)`
+
+Maps a file extensions to a mimetype.
+
+
+# Additional URI Methods
+
+Aside from the standard `Psr\Http\Message\UriInterface` implementation in form of the `GuzzleHttp\Psr7\Uri` class,
+this library also provides additional functionality when working with URIs as static methods.
+
+## URI Types
+
+An instance of `Psr\Http\Message\UriInterface` can either be an absolute URI or a relative reference.
+An absolute URI has a scheme. A relative reference is used to express a URI relative to another URI,
+the base URI. Relative references can be divided into several forms according to
+[RFC 3986 Section 4.2](https://tools.ietf.org/html/rfc3986#section-4.2):
+
+- network-path references, e.g. `//example.com/path`
+- absolute-path references, e.g. `/path`
+- relative-path references, e.g. `subpath`
+
+The following methods can be used to identify the type of the URI.
+
+### `GuzzleHttp\Psr7\Uri::isAbsolute`
+
+`public static function isAbsolute(UriInterface $uri): bool`
+
+Whether the URI is absolute, i.e. it has a scheme.
+
+### `GuzzleHttp\Psr7\Uri::isNetworkPathReference`
+
+`public static function isNetworkPathReference(UriInterface $uri): bool`
+
+Whether the URI is a network-path reference. A relative reference that begins with two slash characters is
+termed an network-path reference.
+
+### `GuzzleHttp\Psr7\Uri::isAbsolutePathReference`
+
+`public static function isAbsolutePathReference(UriInterface $uri): bool`
+
+Whether the URI is a absolute-path reference. A relative reference that begins with a single slash character is
+termed an absolute-path reference.
+
+### `GuzzleHttp\Psr7\Uri::isRelativePathReference`
+
+`public static function isRelativePathReference(UriInterface $uri): bool`
+
+Whether the URI is a relative-path reference. A relative reference that does not begin with a slash character is
+termed a relative-path reference.
+
+### `GuzzleHttp\Psr7\Uri::isSameDocumentReference`
+
+`public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool`
+
+Whether the URI is a same-document reference. A same-document reference refers to a URI that is, aside from its
+fragment component, identical to the base URI. When no base URI is given, only an empty URI reference
+(apart from its fragment) is considered a same-document reference.
+
+## URI Components
+
+Additional methods to work with URI components.
+
+### `GuzzleHttp\Psr7\Uri::isDefaultPort`
+
+`public static function isDefaultPort(UriInterface $uri): bool`
+
+Whether the URI has the default port of the current scheme. `Psr\Http\Message\UriInterface::getPort` may return null
+or the standard port. This method can be used independently of the implementation.
+
+### `GuzzleHttp\Psr7\Uri::composeComponents`
+
+`public static function composeComponents($scheme, $authority, $path, $query, $fragment): string`
+
+Composes a URI reference string from its various components according to
+[RFC 3986 Section 5.3](https://tools.ietf.org/html/rfc3986#section-5.3). Usually this method does not need to be called
+manually but instead is used indirectly via `Psr\Http\Message\UriInterface::__toString`.
+
+### `GuzzleHttp\Psr7\Uri::fromParts`
+
+`public static function fromParts(array $parts): UriInterface`
+
+Creates a URI from a hash of [`parse_url`](http://php.net/manual/en/function.parse-url.php) components.
+
+
+### `GuzzleHttp\Psr7\Uri::withQueryValue`
+
+`public static function withQueryValue(UriInterface $uri, $key, $value): UriInterface`
+
+Creates a new URI with a specific query string value. Any existing query string values that exactly match the
+provided key are removed and replaced with the given key value pair. A value of null will set the query string
+key without a value, e.g. "key" instead of "key=value".
+
+
+### `GuzzleHttp\Psr7\Uri::withoutQueryValue`
+
+`public static function withoutQueryValue(UriInterface $uri, $key): UriInterface`
+
+Creates a new URI with a specific query string value removed. Any existing query string values that exactly match the
+provided key are removed.
+
+## Reference Resolution
+
+`GuzzleHttp\Psr7\UriResolver` provides methods to resolve a URI reference in the context of a base URI according
+to [RFC 3986 Section 5](https://tools.ietf.org/html/rfc3986#section-5). This is for example also what web browsers
+do when resolving a link in a website based on the current request URI.
+
+### `GuzzleHttp\Psr7\UriResolver::resolve`
+
+`public static function resolve(UriInterface $base, UriInterface $rel): UriInterface`
+
+Converts the relative URI into a new URI that is resolved against the base URI.
+
+### `GuzzleHttp\Psr7\UriResolver::removeDotSegments`
+
+`public static function removeDotSegments(string $path): string`
+
+Removes dot segments from a path and returns the new path according to
+[RFC 3986 Section 5.2.4](https://tools.ietf.org/html/rfc3986#section-5.2.4).
+
+### `GuzzleHttp\Psr7\UriResolver::relativize`
+
+`public static function relativize(UriInterface $base, UriInterface $target): UriInterface`
+
+Returns the target URI as a relative reference from the base URI. This method is the counterpart to resolve():
+
+```php
+(string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
+```
+
+One use-case is to use the current request URI as base URI and then generate relative links in your documents
+to reduce the document size or offer self-contained downloadable document archives.
+
+```php
+$base = new Uri('http://example.com/a/b/');
+echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'.
+echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'.
+echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
+echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'.
+```
+
+## Normalization and Comparison
+
+`GuzzleHttp\Psr7\UriNormalizer` provides methods to normalize and compare URIs according to
+[RFC 3986 Section 6](https://tools.ietf.org/html/rfc3986#section-6).
+
+### `GuzzleHttp\Psr7\UriNormalizer::normalize`
+
+`public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS): UriInterface`
+
+Returns a normalized URI. The scheme and host component are already normalized to lowercase per PSR-7 UriInterface.
+This methods adds additional normalizations that can be configured with the `$flags` parameter which is a bitmask
+of normalizations to apply. The following normalizations are available:
+
+- `UriNormalizer::PRESERVING_NORMALIZATIONS`
+
+ Default normalizations which only include the ones that preserve semantics.
+
+- `UriNormalizer::CAPITALIZE_PERCENT_ENCODING`
+
+ All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized.
+
+ Example: `http://example.org/a%c2%b1b` → `http://example.org/a%C2%B1b`
+
+- `UriNormalizer::DECODE_UNRESERVED_CHARACTERS`
+
+ Decodes percent-encoded octets of unreserved characters. For consistency, percent-encoded octets in the ranges of
+ ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should
+ not be created by URI producers and, when found in a URI, should be decoded to their corresponding unreserved
+ characters by URI normalizers.
+
+ Example: `http://example.org/%7Eusern%61me/` → `http://example.org/~username/`
+
+- `UriNormalizer::CONVERT_EMPTY_PATH`
+
+ Converts the empty path to "/" for http and https URIs.
+
+ Example: `http://example.org` → `http://example.org/`
+
+- `UriNormalizer::REMOVE_DEFAULT_HOST`
+
+ Removes the default host of the given URI scheme from the URI. Only the "file" scheme defines the default host
+ "localhost". All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile` are equivalent according to
+ RFC 3986.
+
+ Example: `file://localhost/myfile` → `file:///myfile`
+
+- `UriNormalizer::REMOVE_DEFAULT_PORT`
+
+ Removes the default port of the given URI scheme from the URI.
+
+ Example: `http://example.org:80/` → `http://example.org/`
+
+- `UriNormalizer::REMOVE_DOT_SEGMENTS`
+
+ Removes unnecessary dot-segments. Dot-segments in relative-path references are not removed as it would
+ change the semantics of the URI reference.
+
+ Example: `http://example.org/../a/b/../c/./d.html` → `http://example.org/a/c/d.html`
+
+- `UriNormalizer::REMOVE_DUPLICATE_SLASHES`
+
+ Paths which include two or more adjacent slashes are converted to one. Webservers usually ignore duplicate slashes
+ and treat those URIs equivalent. But in theory those URIs do not need to be equivalent. So this normalization
+ may change the semantics. Encoded slashes (%2F) are not removed.
+
+ Example: `http://example.org//foo///bar.html` → `http://example.org/foo/bar.html`
+
+- `UriNormalizer::SORT_QUERY_PARAMETERS`
+
+ Sort query parameters with their values in alphabetical order. However, the order of parameters in a URI may be
+ significant (this is not defined by the standard). So this normalization is not safe and may change the semantics
+ of the URI.
+
+ Example: `?lang=en&article=fred` → `?article=fred&lang=en`
+
+### `GuzzleHttp\Psr7\UriNormalizer::isEquivalent`
+
+`public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS): bool`
+
+Whether two URIs can be considered equivalent. Both URIs are normalized automatically before comparison with the given
+`$normalizations` bitmask. The method also accepts relative URI references and returns true when they are equivalent.
+This of course assumes they will be resolved against the same base URI. If this is not the case, determination of
+equivalence or difference of relative references does not mean anything.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/composer.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/composer.json
new file mode 100644
index 0000000..b1c5a90
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/composer.json
@@ -0,0 +1,39 @@
+{
+ "name": "guzzlehttp/psr7",
+ "type": "library",
+ "description": "PSR-7 message implementation that also provides common utility methods",
+ "keywords": ["request", "response", "message", "stream", "http", "uri", "url"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Tobias Schultze",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "require": {
+ "php": ">=5.4.0",
+ "psr/http-message": "~1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.0"
+ },
+ "provide": {
+ "psr/http-message-implementation": "1.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Psr7\\": "src/"
+ },
+ "files": ["src/functions_include.php"]
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.4-dev"
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/AppendStream.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/AppendStream.php
new file mode 100644
index 0000000..23039fd
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/AppendStream.php
@@ -0,0 +1,233 @@
+addStream($stream);
+ }
+ }
+
+ public function __toString()
+ {
+ try {
+ $this->rewind();
+ return $this->getContents();
+ } catch (\Exception $e) {
+ return '';
+ }
+ }
+
+ /**
+ * Add a stream to the AppendStream
+ *
+ * @param StreamInterface $stream Stream to append. Must be readable.
+ *
+ * @throws \InvalidArgumentException if the stream is not readable
+ */
+ public function addStream(StreamInterface $stream)
+ {
+ if (!$stream->isReadable()) {
+ throw new \InvalidArgumentException('Each stream must be readable');
+ }
+
+ // The stream is only seekable if all streams are seekable
+ if (!$stream->isSeekable()) {
+ $this->seekable = false;
+ }
+
+ $this->streams[] = $stream;
+ }
+
+ public function getContents()
+ {
+ return copy_to_string($this);
+ }
+
+ /**
+ * Closes each attached stream.
+ *
+ * {@inheritdoc}
+ */
+ public function close()
+ {
+ $this->pos = $this->current = 0;
+
+ foreach ($this->streams as $stream) {
+ $stream->close();
+ }
+
+ $this->streams = [];
+ }
+
+ /**
+ * Detaches each attached stream
+ *
+ * {@inheritdoc}
+ */
+ public function detach()
+ {
+ $this->close();
+ $this->detached = true;
+ }
+
+ public function tell()
+ {
+ return $this->pos;
+ }
+
+ /**
+ * Tries to calculate the size by adding the size of each stream.
+ *
+ * If any of the streams do not return a valid number, then the size of the
+ * append stream cannot be determined and null is returned.
+ *
+ * {@inheritdoc}
+ */
+ public function getSize()
+ {
+ $size = 0;
+
+ foreach ($this->streams as $stream) {
+ $s = $stream->getSize();
+ if ($s === null) {
+ return null;
+ }
+ $size += $s;
+ }
+
+ return $size;
+ }
+
+ public function eof()
+ {
+ return !$this->streams ||
+ ($this->current >= count($this->streams) - 1 &&
+ $this->streams[$this->current]->eof());
+ }
+
+ public function rewind()
+ {
+ $this->seek(0);
+ }
+
+ /**
+ * Attempts to seek to the given position. Only supports SEEK_SET.
+ *
+ * {@inheritdoc}
+ */
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ if (!$this->seekable) {
+ throw new \RuntimeException('This AppendStream is not seekable');
+ } elseif ($whence !== SEEK_SET) {
+ throw new \RuntimeException('The AppendStream can only seek with SEEK_SET');
+ }
+
+ $this->pos = $this->current = 0;
+
+ // Rewind each stream
+ foreach ($this->streams as $i => $stream) {
+ try {
+ $stream->rewind();
+ } catch (\Exception $e) {
+ throw new \RuntimeException('Unable to seek stream '
+ . $i . ' of the AppendStream', 0, $e);
+ }
+ }
+
+ // Seek to the actual position by reading from each stream
+ while ($this->pos < $offset && !$this->eof()) {
+ $result = $this->read(min(8096, $offset - $this->pos));
+ if ($result === '') {
+ break;
+ }
+ }
+ }
+
+ /**
+ * Reads from all of the appended streams until the length is met or EOF.
+ *
+ * {@inheritdoc}
+ */
+ public function read($length)
+ {
+ $buffer = '';
+ $total = count($this->streams) - 1;
+ $remaining = $length;
+ $progressToNext = false;
+
+ while ($remaining > 0) {
+
+ // Progress to the next stream if needed.
+ if ($progressToNext || $this->streams[$this->current]->eof()) {
+ $progressToNext = false;
+ if ($this->current === $total) {
+ break;
+ }
+ $this->current++;
+ }
+
+ $result = $this->streams[$this->current]->read($remaining);
+
+ // Using a loose comparison here to match on '', false, and null
+ if ($result == null) {
+ $progressToNext = true;
+ continue;
+ }
+
+ $buffer .= $result;
+ $remaining = $length - strlen($buffer);
+ }
+
+ $this->pos += strlen($buffer);
+
+ return $buffer;
+ }
+
+ public function isReadable()
+ {
+ return true;
+ }
+
+ public function isWritable()
+ {
+ return false;
+ }
+
+ public function isSeekable()
+ {
+ return $this->seekable;
+ }
+
+ public function write($string)
+ {
+ throw new \RuntimeException('Cannot write to an AppendStream');
+ }
+
+ public function getMetadata($key = null)
+ {
+ return $key ? null : [];
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/BufferStream.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/BufferStream.php
new file mode 100644
index 0000000..af4d4c2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/BufferStream.php
@@ -0,0 +1,137 @@
+hwm = $hwm;
+ }
+
+ public function __toString()
+ {
+ return $this->getContents();
+ }
+
+ public function getContents()
+ {
+ $buffer = $this->buffer;
+ $this->buffer = '';
+
+ return $buffer;
+ }
+
+ public function close()
+ {
+ $this->buffer = '';
+ }
+
+ public function detach()
+ {
+ $this->close();
+ }
+
+ public function getSize()
+ {
+ return strlen($this->buffer);
+ }
+
+ public function isReadable()
+ {
+ return true;
+ }
+
+ public function isWritable()
+ {
+ return true;
+ }
+
+ public function isSeekable()
+ {
+ return false;
+ }
+
+ public function rewind()
+ {
+ $this->seek(0);
+ }
+
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ throw new \RuntimeException('Cannot seek a BufferStream');
+ }
+
+ public function eof()
+ {
+ return strlen($this->buffer) === 0;
+ }
+
+ public function tell()
+ {
+ throw new \RuntimeException('Cannot determine the position of a BufferStream');
+ }
+
+ /**
+ * Reads data from the buffer.
+ */
+ public function read($length)
+ {
+ $currentLength = strlen($this->buffer);
+
+ if ($length >= $currentLength) {
+ // No need to slice the buffer because we don't have enough data.
+ $result = $this->buffer;
+ $this->buffer = '';
+ } else {
+ // Slice up the result to provide a subset of the buffer.
+ $result = substr($this->buffer, 0, $length);
+ $this->buffer = substr($this->buffer, $length);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Writes data to the buffer.
+ */
+ public function write($string)
+ {
+ $this->buffer .= $string;
+
+ // TODO: What should happen here?
+ if (strlen($this->buffer) >= $this->hwm) {
+ return false;
+ }
+
+ return strlen($string);
+ }
+
+ public function getMetadata($key = null)
+ {
+ if ($key == 'hwm') {
+ return $this->hwm;
+ }
+
+ return $key ? null : [];
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/CachingStream.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/CachingStream.php
new file mode 100644
index 0000000..ed68f08
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/CachingStream.php
@@ -0,0 +1,138 @@
+remoteStream = $stream;
+ $this->stream = $target ?: new Stream(fopen('php://temp', 'r+'));
+ }
+
+ public function getSize()
+ {
+ return max($this->stream->getSize(), $this->remoteStream->getSize());
+ }
+
+ public function rewind()
+ {
+ $this->seek(0);
+ }
+
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ if ($whence == SEEK_SET) {
+ $byte = $offset;
+ } elseif ($whence == SEEK_CUR) {
+ $byte = $offset + $this->tell();
+ } elseif ($whence == SEEK_END) {
+ $size = $this->remoteStream->getSize();
+ if ($size === null) {
+ $size = $this->cacheEntireStream();
+ }
+ $byte = $size + $offset;
+ } else {
+ throw new \InvalidArgumentException('Invalid whence');
+ }
+
+ $diff = $byte - $this->stream->getSize();
+
+ if ($diff > 0) {
+ // Read the remoteStream until we have read in at least the amount
+ // of bytes requested, or we reach the end of the file.
+ while ($diff > 0 && !$this->remoteStream->eof()) {
+ $this->read($diff);
+ $diff = $byte - $this->stream->getSize();
+ }
+ } else {
+ // We can just do a normal seek since we've already seen this byte.
+ $this->stream->seek($byte);
+ }
+ }
+
+ public function read($length)
+ {
+ // Perform a regular read on any previously read data from the buffer
+ $data = $this->stream->read($length);
+ $remaining = $length - strlen($data);
+
+ // More data was requested so read from the remote stream
+ if ($remaining) {
+ // If data was written to the buffer in a position that would have
+ // been filled from the remote stream, then we must skip bytes on
+ // the remote stream to emulate overwriting bytes from that
+ // position. This mimics the behavior of other PHP stream wrappers.
+ $remoteData = $this->remoteStream->read(
+ $remaining + $this->skipReadBytes
+ );
+
+ if ($this->skipReadBytes) {
+ $len = strlen($remoteData);
+ $remoteData = substr($remoteData, $this->skipReadBytes);
+ $this->skipReadBytes = max(0, $this->skipReadBytes - $len);
+ }
+
+ $data .= $remoteData;
+ $this->stream->write($remoteData);
+ }
+
+ return $data;
+ }
+
+ public function write($string)
+ {
+ // When appending to the end of the currently read stream, you'll want
+ // to skip bytes from being read from the remote stream to emulate
+ // other stream wrappers. Basically replacing bytes of data of a fixed
+ // length.
+ $overflow = (strlen($string) + $this->tell()) - $this->remoteStream->tell();
+ if ($overflow > 0) {
+ $this->skipReadBytes += $overflow;
+ }
+
+ return $this->stream->write($string);
+ }
+
+ public function eof()
+ {
+ return $this->stream->eof() && $this->remoteStream->eof();
+ }
+
+ /**
+ * Close both the remote stream and buffer stream
+ */
+ public function close()
+ {
+ $this->remoteStream->close() && $this->stream->close();
+ }
+
+ private function cacheEntireStream()
+ {
+ $target = new FnStream(['write' => 'strlen']);
+ copy_to_stream($this, $target);
+
+ return $this->tell();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/DroppingStream.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/DroppingStream.php
new file mode 100644
index 0000000..8935c80
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/DroppingStream.php
@@ -0,0 +1,42 @@
+stream = $stream;
+ $this->maxLength = $maxLength;
+ }
+
+ public function write($string)
+ {
+ $diff = $this->maxLength - $this->stream->getSize();
+
+ // Begin returning 0 when the underlying stream is too large.
+ if ($diff <= 0) {
+ return 0;
+ }
+
+ // Write the stream or a subset of the stream if needed.
+ if (strlen($string) < $diff) {
+ return $this->stream->write($string);
+ }
+
+ return $this->stream->write(substr($string, 0, $diff));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/FnStream.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/FnStream.php
new file mode 100644
index 0000000..cc9b445
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/FnStream.php
@@ -0,0 +1,149 @@
+methods = $methods;
+
+ // Create the functions on the class
+ foreach ($methods as $name => $fn) {
+ $this->{'_fn_' . $name} = $fn;
+ }
+ }
+
+ /**
+ * Lazily determine which methods are not implemented.
+ * @throws \BadMethodCallException
+ */
+ public function __get($name)
+ {
+ throw new \BadMethodCallException(str_replace('_fn_', '', $name)
+ . '() is not implemented in the FnStream');
+ }
+
+ /**
+ * The close method is called on the underlying stream only if possible.
+ */
+ public function __destruct()
+ {
+ if (isset($this->_fn_close)) {
+ call_user_func($this->_fn_close);
+ }
+ }
+
+ /**
+ * Adds custom functionality to an underlying stream by intercepting
+ * specific method calls.
+ *
+ * @param StreamInterface $stream Stream to decorate
+ * @param array $methods Hash of method name to a closure
+ *
+ * @return FnStream
+ */
+ public static function decorate(StreamInterface $stream, array $methods)
+ {
+ // If any of the required methods were not provided, then simply
+ // proxy to the decorated stream.
+ foreach (array_diff(self::$slots, array_keys($methods)) as $diff) {
+ $methods[$diff] = [$stream, $diff];
+ }
+
+ return new self($methods);
+ }
+
+ public function __toString()
+ {
+ return call_user_func($this->_fn___toString);
+ }
+
+ public function close()
+ {
+ return call_user_func($this->_fn_close);
+ }
+
+ public function detach()
+ {
+ return call_user_func($this->_fn_detach);
+ }
+
+ public function getSize()
+ {
+ return call_user_func($this->_fn_getSize);
+ }
+
+ public function tell()
+ {
+ return call_user_func($this->_fn_tell);
+ }
+
+ public function eof()
+ {
+ return call_user_func($this->_fn_eof);
+ }
+
+ public function isSeekable()
+ {
+ return call_user_func($this->_fn_isSeekable);
+ }
+
+ public function rewind()
+ {
+ call_user_func($this->_fn_rewind);
+ }
+
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ call_user_func($this->_fn_seek, $offset, $whence);
+ }
+
+ public function isWritable()
+ {
+ return call_user_func($this->_fn_isWritable);
+ }
+
+ public function write($string)
+ {
+ return call_user_func($this->_fn_write, $string);
+ }
+
+ public function isReadable()
+ {
+ return call_user_func($this->_fn_isReadable);
+ }
+
+ public function read($length)
+ {
+ return call_user_func($this->_fn_read, $length);
+ }
+
+ public function getContents()
+ {
+ return call_user_func($this->_fn_getContents);
+ }
+
+ public function getMetadata($key = null)
+ {
+ return call_user_func($this->_fn_getMetadata, $key);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/InflateStream.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/InflateStream.php
new file mode 100644
index 0000000..0051d3f
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/InflateStream.php
@@ -0,0 +1,52 @@
+read(10);
+ $filenameHeaderLength = $this->getLengthOfPossibleFilenameHeader($stream, $header);
+ // Skip the header, that is 10 + length of filename + 1 (nil) bytes
+ $stream = new LimitStream($stream, -1, 10 + $filenameHeaderLength);
+ $resource = StreamWrapper::getResource($stream);
+ stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ);
+ $this->stream = new Stream($resource);
+ }
+
+ /**
+ * @param StreamInterface $stream
+ * @param $header
+ * @return int
+ */
+ private function getLengthOfPossibleFilenameHeader(StreamInterface $stream, $header)
+ {
+ $filename_header_length = 0;
+
+ if (substr(bin2hex($header), 6, 2) === '08') {
+ // we have a filename, read until nil
+ $filename_header_length = 1;
+ while ($stream->read(1) !== chr(0)) {
+ $filename_header_length++;
+ }
+ }
+
+ return $filename_header_length;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/LazyOpenStream.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/LazyOpenStream.php
new file mode 100644
index 0000000..02cec3a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/LazyOpenStream.php
@@ -0,0 +1,39 @@
+filename = $filename;
+ $this->mode = $mode;
+ }
+
+ /**
+ * Creates the underlying stream lazily when required.
+ *
+ * @return StreamInterface
+ */
+ protected function createStream()
+ {
+ return stream_for(try_fopen($this->filename, $this->mode));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/LimitStream.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/LimitStream.php
new file mode 100644
index 0000000..3c13d4f
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/LimitStream.php
@@ -0,0 +1,155 @@
+stream = $stream;
+ $this->setLimit($limit);
+ $this->setOffset($offset);
+ }
+
+ public function eof()
+ {
+ // Always return true if the underlying stream is EOF
+ if ($this->stream->eof()) {
+ return true;
+ }
+
+ // No limit and the underlying stream is not at EOF
+ if ($this->limit == -1) {
+ return false;
+ }
+
+ return $this->stream->tell() >= $this->offset + $this->limit;
+ }
+
+ /**
+ * Returns the size of the limited subset of data
+ * {@inheritdoc}
+ */
+ public function getSize()
+ {
+ if (null === ($length = $this->stream->getSize())) {
+ return null;
+ } elseif ($this->limit == -1) {
+ return $length - $this->offset;
+ } else {
+ return min($this->limit, $length - $this->offset);
+ }
+ }
+
+ /**
+ * Allow for a bounded seek on the read limited stream
+ * {@inheritdoc}
+ */
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ if ($whence !== SEEK_SET || $offset < 0) {
+ throw new \RuntimeException(sprintf(
+ 'Cannot seek to offset % with whence %s',
+ $offset,
+ $whence
+ ));
+ }
+
+ $offset += $this->offset;
+
+ if ($this->limit !== -1) {
+ if ($offset > $this->offset + $this->limit) {
+ $offset = $this->offset + $this->limit;
+ }
+ }
+
+ $this->stream->seek($offset);
+ }
+
+ /**
+ * Give a relative tell()
+ * {@inheritdoc}
+ */
+ public function tell()
+ {
+ return $this->stream->tell() - $this->offset;
+ }
+
+ /**
+ * Set the offset to start limiting from
+ *
+ * @param int $offset Offset to seek to and begin byte limiting from
+ *
+ * @throws \RuntimeException if the stream cannot be seeked.
+ */
+ public function setOffset($offset)
+ {
+ $current = $this->stream->tell();
+
+ if ($current !== $offset) {
+ // If the stream cannot seek to the offset position, then read to it
+ if ($this->stream->isSeekable()) {
+ $this->stream->seek($offset);
+ } elseif ($current > $offset) {
+ throw new \RuntimeException("Could not seek to stream offset $offset");
+ } else {
+ $this->stream->read($offset - $current);
+ }
+ }
+
+ $this->offset = $offset;
+ }
+
+ /**
+ * Set the limit of bytes that the decorator allows to be read from the
+ * stream.
+ *
+ * @param int $limit Number of bytes to allow to be read from the stream.
+ * Use -1 for no limit.
+ */
+ public function setLimit($limit)
+ {
+ $this->limit = $limit;
+ }
+
+ public function read($length)
+ {
+ if ($this->limit == -1) {
+ return $this->stream->read($length);
+ }
+
+ // Check if the current position is less than the total allowed
+ // bytes + original offset
+ $remaining = ($this->offset + $this->limit) - $this->stream->tell();
+ if ($remaining > 0) {
+ // Only return the amount of requested data, ensuring that the byte
+ // limit is not exceeded
+ return $this->stream->read(min($remaining, $length));
+ }
+
+ return '';
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/MessageTrait.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/MessageTrait.php
new file mode 100644
index 0000000..1e4da64
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/MessageTrait.php
@@ -0,0 +1,183 @@
+ array of values */
+ private $headers = [];
+
+ /** @var array Map of lowercase header name => original name at registration */
+ private $headerNames = [];
+
+ /** @var string */
+ private $protocol = '1.1';
+
+ /** @var StreamInterface */
+ private $stream;
+
+ public function getProtocolVersion()
+ {
+ return $this->protocol;
+ }
+
+ public function withProtocolVersion($version)
+ {
+ if ($this->protocol === $version) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->protocol = $version;
+ return $new;
+ }
+
+ public function getHeaders()
+ {
+ return $this->headers;
+ }
+
+ public function hasHeader($header)
+ {
+ return isset($this->headerNames[strtolower($header)]);
+ }
+
+ public function getHeader($header)
+ {
+ $header = strtolower($header);
+
+ if (!isset($this->headerNames[$header])) {
+ return [];
+ }
+
+ $header = $this->headerNames[$header];
+
+ return $this->headers[$header];
+ }
+
+ public function getHeaderLine($header)
+ {
+ return implode(', ', $this->getHeader($header));
+ }
+
+ public function withHeader($header, $value)
+ {
+ if (!is_array($value)) {
+ $value = [$value];
+ }
+
+ $value = $this->trimHeaderValues($value);
+ $normalized = strtolower($header);
+
+ $new = clone $this;
+ if (isset($new->headerNames[$normalized])) {
+ unset($new->headers[$new->headerNames[$normalized]]);
+ }
+ $new->headerNames[$normalized] = $header;
+ $new->headers[$header] = $value;
+
+ return $new;
+ }
+
+ public function withAddedHeader($header, $value)
+ {
+ if (!is_array($value)) {
+ $value = [$value];
+ }
+
+ $value = $this->trimHeaderValues($value);
+ $normalized = strtolower($header);
+
+ $new = clone $this;
+ if (isset($new->headerNames[$normalized])) {
+ $header = $this->headerNames[$normalized];
+ $new->headers[$header] = array_merge($this->headers[$header], $value);
+ } else {
+ $new->headerNames[$normalized] = $header;
+ $new->headers[$header] = $value;
+ }
+
+ return $new;
+ }
+
+ public function withoutHeader($header)
+ {
+ $normalized = strtolower($header);
+
+ if (!isset($this->headerNames[$normalized])) {
+ return $this;
+ }
+
+ $header = $this->headerNames[$normalized];
+
+ $new = clone $this;
+ unset($new->headers[$header], $new->headerNames[$normalized]);
+
+ return $new;
+ }
+
+ public function getBody()
+ {
+ if (!$this->stream) {
+ $this->stream = stream_for('');
+ }
+
+ return $this->stream;
+ }
+
+ public function withBody(StreamInterface $body)
+ {
+ if ($body === $this->stream) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->stream = $body;
+ return $new;
+ }
+
+ private function setHeaders(array $headers)
+ {
+ $this->headerNames = $this->headers = [];
+ foreach ($headers as $header => $value) {
+ if (!is_array($value)) {
+ $value = [$value];
+ }
+
+ $value = $this->trimHeaderValues($value);
+ $normalized = strtolower($header);
+ if (isset($this->headerNames[$normalized])) {
+ $header = $this->headerNames[$normalized];
+ $this->headers[$header] = array_merge($this->headers[$header], $value);
+ } else {
+ $this->headerNames[$normalized] = $header;
+ $this->headers[$header] = $value;
+ }
+ }
+ }
+
+ /**
+ * Trims whitespace from the header values.
+ *
+ * Spaces and tabs ought to be excluded by parsers when extracting the field value from a header field.
+ *
+ * header-field = field-name ":" OWS field-value OWS
+ * OWS = *( SP / HTAB )
+ *
+ * @param string[] $values Header values
+ *
+ * @return string[] Trimmed header values
+ *
+ * @see https://tools.ietf.org/html/rfc7230#section-3.2.4
+ */
+ private function trimHeaderValues(array $values)
+ {
+ return array_map(function ($value) {
+ return trim($value, " \t");
+ }, $values);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/MultipartStream.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/MultipartStream.php
new file mode 100644
index 0000000..c0fd584
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/MultipartStream.php
@@ -0,0 +1,153 @@
+boundary = $boundary ?: sha1(uniqid('', true));
+ $this->stream = $this->createStream($elements);
+ }
+
+ /**
+ * Get the boundary
+ *
+ * @return string
+ */
+ public function getBoundary()
+ {
+ return $this->boundary;
+ }
+
+ public function isWritable()
+ {
+ return false;
+ }
+
+ /**
+ * Get the headers needed before transferring the content of a POST file
+ */
+ private function getHeaders(array $headers)
+ {
+ $str = '';
+ foreach ($headers as $key => $value) {
+ $str .= "{$key}: {$value}\r\n";
+ }
+
+ return "--{$this->boundary}\r\n" . trim($str) . "\r\n\r\n";
+ }
+
+ /**
+ * Create the aggregate stream that will be used to upload the POST data
+ */
+ protected function createStream(array $elements)
+ {
+ $stream = new AppendStream();
+
+ foreach ($elements as $element) {
+ $this->addElement($stream, $element);
+ }
+
+ // Add the trailing boundary with CRLF
+ $stream->addStream(stream_for("--{$this->boundary}--\r\n"));
+
+ return $stream;
+ }
+
+ private function addElement(AppendStream $stream, array $element)
+ {
+ foreach (['contents', 'name'] as $key) {
+ if (!array_key_exists($key, $element)) {
+ throw new \InvalidArgumentException("A '{$key}' key is required");
+ }
+ }
+
+ $element['contents'] = stream_for($element['contents']);
+
+ if (empty($element['filename'])) {
+ $uri = $element['contents']->getMetadata('uri');
+ if (substr($uri, 0, 6) !== 'php://') {
+ $element['filename'] = $uri;
+ }
+ }
+
+ list($body, $headers) = $this->createElement(
+ $element['name'],
+ $element['contents'],
+ isset($element['filename']) ? $element['filename'] : null,
+ isset($element['headers']) ? $element['headers'] : []
+ );
+
+ $stream->addStream(stream_for($this->getHeaders($headers)));
+ $stream->addStream($body);
+ $stream->addStream(stream_for("\r\n"));
+ }
+
+ /**
+ * @return array
+ */
+ private function createElement($name, StreamInterface $stream, $filename, array $headers)
+ {
+ // Set a default content-disposition header if one was no provided
+ $disposition = $this->getHeader($headers, 'content-disposition');
+ if (!$disposition) {
+ $headers['Content-Disposition'] = ($filename === '0' || $filename)
+ ? sprintf('form-data; name="%s"; filename="%s"',
+ $name,
+ basename($filename))
+ : "form-data; name=\"{$name}\"";
+ }
+
+ // Set a default content-length header if one was no provided
+ $length = $this->getHeader($headers, 'content-length');
+ if (!$length) {
+ if ($length = $stream->getSize()) {
+ $headers['Content-Length'] = (string) $length;
+ }
+ }
+
+ // Set a default Content-Type if one was not supplied
+ $type = $this->getHeader($headers, 'content-type');
+ if (!$type && ($filename === '0' || $filename)) {
+ if ($type = mimetype_from_filename($filename)) {
+ $headers['Content-Type'] = $type;
+ }
+ }
+
+ return [$stream, $headers];
+ }
+
+ private function getHeader(array $headers, $key)
+ {
+ $lowercaseHeader = strtolower($key);
+ foreach ($headers as $k => $v) {
+ if (strtolower($k) === $lowercaseHeader) {
+ return $v;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/NoSeekStream.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/NoSeekStream.php
new file mode 100644
index 0000000..2332218
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/NoSeekStream.php
@@ -0,0 +1,22 @@
+source = $source;
+ $this->size = isset($options['size']) ? $options['size'] : null;
+ $this->metadata = isset($options['metadata']) ? $options['metadata'] : [];
+ $this->buffer = new BufferStream();
+ }
+
+ public function __toString()
+ {
+ try {
+ return copy_to_string($this);
+ } catch (\Exception $e) {
+ return '';
+ }
+ }
+
+ public function close()
+ {
+ $this->detach();
+ }
+
+ public function detach()
+ {
+ $this->tellPos = false;
+ $this->source = null;
+ }
+
+ public function getSize()
+ {
+ return $this->size;
+ }
+
+ public function tell()
+ {
+ return $this->tellPos;
+ }
+
+ public function eof()
+ {
+ return !$this->source;
+ }
+
+ public function isSeekable()
+ {
+ return false;
+ }
+
+ public function rewind()
+ {
+ $this->seek(0);
+ }
+
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ throw new \RuntimeException('Cannot seek a PumpStream');
+ }
+
+ public function isWritable()
+ {
+ return false;
+ }
+
+ public function write($string)
+ {
+ throw new \RuntimeException('Cannot write to a PumpStream');
+ }
+
+ public function isReadable()
+ {
+ return true;
+ }
+
+ public function read($length)
+ {
+ $data = $this->buffer->read($length);
+ $readLen = strlen($data);
+ $this->tellPos += $readLen;
+ $remaining = $length - $readLen;
+
+ if ($remaining) {
+ $this->pump($remaining);
+ $data .= $this->buffer->read($remaining);
+ $this->tellPos += strlen($data) - $readLen;
+ }
+
+ return $data;
+ }
+
+ public function getContents()
+ {
+ $result = '';
+ while (!$this->eof()) {
+ $result .= $this->read(1000000);
+ }
+
+ return $result;
+ }
+
+ public function getMetadata($key = null)
+ {
+ if (!$key) {
+ return $this->metadata;
+ }
+
+ return isset($this->metadata[$key]) ? $this->metadata[$key] : null;
+ }
+
+ private function pump($length)
+ {
+ if ($this->source) {
+ do {
+ $data = call_user_func($this->source, $length);
+ if ($data === false || $data === null) {
+ $this->source = null;
+ return;
+ }
+ $this->buffer->write($data);
+ $length -= strlen($data);
+ } while ($length > 0);
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/Request.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/Request.php
new file mode 100644
index 0000000..0828548
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/Request.php
@@ -0,0 +1,142 @@
+method = strtoupper($method);
+ $this->uri = $uri;
+ $this->setHeaders($headers);
+ $this->protocol = $version;
+
+ if (!$this->hasHeader('Host')) {
+ $this->updateHostFromUri();
+ }
+
+ if ($body !== '' && $body !== null) {
+ $this->stream = stream_for($body);
+ }
+ }
+
+ public function getRequestTarget()
+ {
+ if ($this->requestTarget !== null) {
+ return $this->requestTarget;
+ }
+
+ $target = $this->uri->getPath();
+ if ($target == '') {
+ $target = '/';
+ }
+ if ($this->uri->getQuery() != '') {
+ $target .= '?' . $this->uri->getQuery();
+ }
+
+ return $target;
+ }
+
+ public function withRequestTarget($requestTarget)
+ {
+ if (preg_match('#\s#', $requestTarget)) {
+ throw new InvalidArgumentException(
+ 'Invalid request target provided; cannot contain whitespace'
+ );
+ }
+
+ $new = clone $this;
+ $new->requestTarget = $requestTarget;
+ return $new;
+ }
+
+ public function getMethod()
+ {
+ return $this->method;
+ }
+
+ public function withMethod($method)
+ {
+ $new = clone $this;
+ $new->method = strtoupper($method);
+ return $new;
+ }
+
+ public function getUri()
+ {
+ return $this->uri;
+ }
+
+ public function withUri(UriInterface $uri, $preserveHost = false)
+ {
+ if ($uri === $this->uri) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->uri = $uri;
+
+ if (!$preserveHost) {
+ $new->updateHostFromUri();
+ }
+
+ return $new;
+ }
+
+ private function updateHostFromUri()
+ {
+ $host = $this->uri->getHost();
+
+ if ($host == '') {
+ return;
+ }
+
+ if (($port = $this->uri->getPort()) !== null) {
+ $host .= ':' . $port;
+ }
+
+ if (isset($this->headerNames['host'])) {
+ $header = $this->headerNames['host'];
+ } else {
+ $header = 'Host';
+ $this->headerNames['host'] = 'Host';
+ }
+ // Ensure Host is the first header.
+ // See: http://tools.ietf.org/html/rfc7230#section-5.4
+ $this->headers = [$header => [$host]] + $this->headers;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/Response.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/Response.php
new file mode 100644
index 0000000..2830c6c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/Response.php
@@ -0,0 +1,132 @@
+ 'Continue',
+ 101 => 'Switching Protocols',
+ 102 => 'Processing',
+ 200 => 'OK',
+ 201 => 'Created',
+ 202 => 'Accepted',
+ 203 => 'Non-Authoritative Information',
+ 204 => 'No Content',
+ 205 => 'Reset Content',
+ 206 => 'Partial Content',
+ 207 => 'Multi-status',
+ 208 => 'Already Reported',
+ 300 => 'Multiple Choices',
+ 301 => 'Moved Permanently',
+ 302 => 'Found',
+ 303 => 'See Other',
+ 304 => 'Not Modified',
+ 305 => 'Use Proxy',
+ 306 => 'Switch Proxy',
+ 307 => 'Temporary Redirect',
+ 400 => 'Bad Request',
+ 401 => 'Unauthorized',
+ 402 => 'Payment Required',
+ 403 => 'Forbidden',
+ 404 => 'Not Found',
+ 405 => 'Method Not Allowed',
+ 406 => 'Not Acceptable',
+ 407 => 'Proxy Authentication Required',
+ 408 => 'Request Time-out',
+ 409 => 'Conflict',
+ 410 => 'Gone',
+ 411 => 'Length Required',
+ 412 => 'Precondition Failed',
+ 413 => 'Request Entity Too Large',
+ 414 => 'Request-URI Too Large',
+ 415 => 'Unsupported Media Type',
+ 416 => 'Requested range not satisfiable',
+ 417 => 'Expectation Failed',
+ 418 => 'I\'m a teapot',
+ 422 => 'Unprocessable Entity',
+ 423 => 'Locked',
+ 424 => 'Failed Dependency',
+ 425 => 'Unordered Collection',
+ 426 => 'Upgrade Required',
+ 428 => 'Precondition Required',
+ 429 => 'Too Many Requests',
+ 431 => 'Request Header Fields Too Large',
+ 451 => 'Unavailable For Legal Reasons',
+ 500 => 'Internal Server Error',
+ 501 => 'Not Implemented',
+ 502 => 'Bad Gateway',
+ 503 => 'Service Unavailable',
+ 504 => 'Gateway Time-out',
+ 505 => 'HTTP Version not supported',
+ 506 => 'Variant Also Negotiates',
+ 507 => 'Insufficient Storage',
+ 508 => 'Loop Detected',
+ 511 => 'Network Authentication Required',
+ ];
+
+ /** @var string */
+ private $reasonPhrase = '';
+
+ /** @var int */
+ private $statusCode = 200;
+
+ /**
+ * @param int $status Status code
+ * @param array $headers Response headers
+ * @param string|null|resource|StreamInterface $body Response body
+ * @param string $version Protocol version
+ * @param string|null $reason Reason phrase (when empty a default will be used based on the status code)
+ */
+ public function __construct(
+ $status = 200,
+ array $headers = [],
+ $body = null,
+ $version = '1.1',
+ $reason = null
+ ) {
+ $this->statusCode = (int) $status;
+
+ if ($body !== '' && $body !== null) {
+ $this->stream = stream_for($body);
+ }
+
+ $this->setHeaders($headers);
+ if ($reason == '' && isset(self::$phrases[$this->statusCode])) {
+ $this->reasonPhrase = self::$phrases[$this->statusCode];
+ } else {
+ $this->reasonPhrase = (string) $reason;
+ }
+
+ $this->protocol = $version;
+ }
+
+ public function getStatusCode()
+ {
+ return $this->statusCode;
+ }
+
+ public function getReasonPhrase()
+ {
+ return $this->reasonPhrase;
+ }
+
+ public function withStatus($code, $reasonPhrase = '')
+ {
+ $new = clone $this;
+ $new->statusCode = (int) $code;
+ if ($reasonPhrase == '' && isset(self::$phrases[$new->statusCode])) {
+ $reasonPhrase = self::$phrases[$new->statusCode];
+ }
+ $new->reasonPhrase = $reasonPhrase;
+ return $new;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/ServerRequest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/ServerRequest.php
new file mode 100644
index 0000000..575aab8
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/ServerRequest.php
@@ -0,0 +1,358 @@
+serverParams = $serverParams;
+
+ parent::__construct($method, $uri, $headers, $body, $version);
+ }
+
+ /**
+ * Return an UploadedFile instance array.
+ *
+ * @param array $files A array which respect $_FILES structure
+ * @throws InvalidArgumentException for unrecognized values
+ * @return array
+ */
+ public static function normalizeFiles(array $files)
+ {
+ $normalized = [];
+
+ foreach ($files as $key => $value) {
+ if ($value instanceof UploadedFileInterface) {
+ $normalized[$key] = $value;
+ } elseif (is_array($value) && isset($value['tmp_name'])) {
+ $normalized[$key] = self::createUploadedFileFromSpec($value);
+ } elseif (is_array($value)) {
+ $normalized[$key] = self::normalizeFiles($value);
+ continue;
+ } else {
+ throw new InvalidArgumentException('Invalid value in files specification');
+ }
+ }
+
+ return $normalized;
+ }
+
+ /**
+ * Create and return an UploadedFile instance from a $_FILES specification.
+ *
+ * If the specification represents an array of values, this method will
+ * delegate to normalizeNestedFileSpec() and return that return value.
+ *
+ * @param array $value $_FILES struct
+ * @return array|UploadedFileInterface
+ */
+ private static function createUploadedFileFromSpec(array $value)
+ {
+ if (is_array($value['tmp_name'])) {
+ return self::normalizeNestedFileSpec($value);
+ }
+
+ return new UploadedFile(
+ $value['tmp_name'],
+ (int) $value['size'],
+ (int) $value['error'],
+ $value['name'],
+ $value['type']
+ );
+ }
+
+ /**
+ * Normalize an array of file specifications.
+ *
+ * Loops through all nested files and returns a normalized array of
+ * UploadedFileInterface instances.
+ *
+ * @param array $files
+ * @return UploadedFileInterface[]
+ */
+ private static function normalizeNestedFileSpec(array $files = [])
+ {
+ $normalizedFiles = [];
+
+ foreach (array_keys($files['tmp_name']) as $key) {
+ $spec = [
+ 'tmp_name' => $files['tmp_name'][$key],
+ 'size' => $files['size'][$key],
+ 'error' => $files['error'][$key],
+ 'name' => $files['name'][$key],
+ 'type' => $files['type'][$key],
+ ];
+ $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec);
+ }
+
+ return $normalizedFiles;
+ }
+
+ /**
+ * Return a ServerRequest populated with superglobals:
+ * $_GET
+ * $_POST
+ * $_COOKIE
+ * $_FILES
+ * $_SERVER
+ *
+ * @return ServerRequestInterface
+ */
+ public static function fromGlobals()
+ {
+ $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';
+ $headers = function_exists('getallheaders') ? getallheaders() : [];
+ $uri = self::getUriFromGlobals();
+ $body = new LazyOpenStream('php://input', 'r+');
+ $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1';
+
+ $serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER);
+
+ return $serverRequest
+ ->withCookieParams($_COOKIE)
+ ->withQueryParams($_GET)
+ ->withParsedBody($_POST)
+ ->withUploadedFiles(self::normalizeFiles($_FILES));
+ }
+
+ /**
+ * Get a Uri populated with values from $_SERVER.
+ *
+ * @return UriInterface
+ */
+ public static function getUriFromGlobals() {
+ $uri = new Uri('');
+
+ $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http');
+
+ $hasPort = false;
+ if (isset($_SERVER['HTTP_HOST'])) {
+ $hostHeaderParts = explode(':', $_SERVER['HTTP_HOST']);
+ $uri = $uri->withHost($hostHeaderParts[0]);
+ if (isset($hostHeaderParts[1])) {
+ $hasPort = true;
+ $uri = $uri->withPort($hostHeaderParts[1]);
+ }
+ } elseif (isset($_SERVER['SERVER_NAME'])) {
+ $uri = $uri->withHost($_SERVER['SERVER_NAME']);
+ } elseif (isset($_SERVER['SERVER_ADDR'])) {
+ $uri = $uri->withHost($_SERVER['SERVER_ADDR']);
+ }
+
+ if (!$hasPort && isset($_SERVER['SERVER_PORT'])) {
+ $uri = $uri->withPort($_SERVER['SERVER_PORT']);
+ }
+
+ $hasQuery = false;
+ if (isset($_SERVER['REQUEST_URI'])) {
+ $requestUriParts = explode('?', $_SERVER['REQUEST_URI']);
+ $uri = $uri->withPath($requestUriParts[0]);
+ if (isset($requestUriParts[1])) {
+ $hasQuery = true;
+ $uri = $uri->withQuery($requestUriParts[1]);
+ }
+ }
+
+ if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) {
+ $uri = $uri->withQuery($_SERVER['QUERY_STRING']);
+ }
+
+ return $uri;
+ }
+
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getServerParams()
+ {
+ return $this->serverParams;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getUploadedFiles()
+ {
+ return $this->uploadedFiles;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withUploadedFiles(array $uploadedFiles)
+ {
+ $new = clone $this;
+ $new->uploadedFiles = $uploadedFiles;
+
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCookieParams()
+ {
+ return $this->cookieParams;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withCookieParams(array $cookies)
+ {
+ $new = clone $this;
+ $new->cookieParams = $cookies;
+
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getQueryParams()
+ {
+ return $this->queryParams;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withQueryParams(array $query)
+ {
+ $new = clone $this;
+ $new->queryParams = $query;
+
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getParsedBody()
+ {
+ return $this->parsedBody;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withParsedBody($data)
+ {
+ $new = clone $this;
+ $new->parsedBody = $data;
+
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getAttribute($attribute, $default = null)
+ {
+ if (false === array_key_exists($attribute, $this->attributes)) {
+ return $default;
+ }
+
+ return $this->attributes[$attribute];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withAttribute($attribute, $value)
+ {
+ $new = clone $this;
+ $new->attributes[$attribute] = $value;
+
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withoutAttribute($attribute)
+ {
+ if (false === array_key_exists($attribute, $this->attributes)) {
+ return $this;
+ }
+
+ $new = clone $this;
+ unset($new->attributes[$attribute]);
+
+ return $new;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/Stream.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/Stream.php
new file mode 100644
index 0000000..e336628
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/Stream.php
@@ -0,0 +1,257 @@
+ [
+ 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true,
+ 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true,
+ 'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true,
+ 'x+t' => true, 'c+t' => true, 'a+' => true
+ ],
+ 'write' => [
+ 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true,
+ 'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true,
+ 'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true,
+ 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true
+ ]
+ ];
+
+ /**
+ * This constructor accepts an associative array of options.
+ *
+ * - size: (int) If a read stream would otherwise have an indeterminate
+ * size, but the size is known due to foreknowledge, then you can
+ * provide that size, in bytes.
+ * - metadata: (array) Any additional metadata to return when the metadata
+ * of the stream is accessed.
+ *
+ * @param resource $stream Stream resource to wrap.
+ * @param array $options Associative array of options.
+ *
+ * @throws \InvalidArgumentException if the stream is not a stream resource
+ */
+ public function __construct($stream, $options = [])
+ {
+ if (!is_resource($stream)) {
+ throw new \InvalidArgumentException('Stream must be a resource');
+ }
+
+ if (isset($options['size'])) {
+ $this->size = $options['size'];
+ }
+
+ $this->customMetadata = isset($options['metadata'])
+ ? $options['metadata']
+ : [];
+
+ $this->stream = $stream;
+ $meta = stream_get_meta_data($this->stream);
+ $this->seekable = $meta['seekable'];
+ $this->readable = isset(self::$readWriteHash['read'][$meta['mode']]);
+ $this->writable = isset(self::$readWriteHash['write'][$meta['mode']]);
+ $this->uri = $this->getMetadata('uri');
+ }
+
+ public function __get($name)
+ {
+ if ($name == 'stream') {
+ throw new \RuntimeException('The stream is detached');
+ }
+
+ throw new \BadMethodCallException('No value for ' . $name);
+ }
+
+ /**
+ * Closes the stream when the destructed
+ */
+ public function __destruct()
+ {
+ $this->close();
+ }
+
+ public function __toString()
+ {
+ try {
+ $this->seek(0);
+ return (string) stream_get_contents($this->stream);
+ } catch (\Exception $e) {
+ return '';
+ }
+ }
+
+ public function getContents()
+ {
+ $contents = stream_get_contents($this->stream);
+
+ if ($contents === false) {
+ throw new \RuntimeException('Unable to read stream contents');
+ }
+
+ return $contents;
+ }
+
+ public function close()
+ {
+ if (isset($this->stream)) {
+ if (is_resource($this->stream)) {
+ fclose($this->stream);
+ }
+ $this->detach();
+ }
+ }
+
+ public function detach()
+ {
+ if (!isset($this->stream)) {
+ return null;
+ }
+
+ $result = $this->stream;
+ unset($this->stream);
+ $this->size = $this->uri = null;
+ $this->readable = $this->writable = $this->seekable = false;
+
+ return $result;
+ }
+
+ public function getSize()
+ {
+ if ($this->size !== null) {
+ return $this->size;
+ }
+
+ if (!isset($this->stream)) {
+ return null;
+ }
+
+ // Clear the stat cache if the stream has a URI
+ if ($this->uri) {
+ clearstatcache(true, $this->uri);
+ }
+
+ $stats = fstat($this->stream);
+ if (isset($stats['size'])) {
+ $this->size = $stats['size'];
+ return $this->size;
+ }
+
+ return null;
+ }
+
+ public function isReadable()
+ {
+ return $this->readable;
+ }
+
+ public function isWritable()
+ {
+ return $this->writable;
+ }
+
+ public function isSeekable()
+ {
+ return $this->seekable;
+ }
+
+ public function eof()
+ {
+ return !$this->stream || feof($this->stream);
+ }
+
+ public function tell()
+ {
+ $result = ftell($this->stream);
+
+ if ($result === false) {
+ throw new \RuntimeException('Unable to determine stream position');
+ }
+
+ return $result;
+ }
+
+ public function rewind()
+ {
+ $this->seek(0);
+ }
+
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ if (!$this->seekable) {
+ throw new \RuntimeException('Stream is not seekable');
+ } elseif (fseek($this->stream, $offset, $whence) === -1) {
+ throw new \RuntimeException('Unable to seek to stream position '
+ . $offset . ' with whence ' . var_export($whence, true));
+ }
+ }
+
+ public function read($length)
+ {
+ if (!$this->readable) {
+ throw new \RuntimeException('Cannot read from non-readable stream');
+ }
+ if ($length < 0) {
+ throw new \RuntimeException('Length parameter cannot be negative');
+ }
+
+ if (0 === $length) {
+ return '';
+ }
+
+ $string = fread($this->stream, $length);
+ if (false === $string) {
+ throw new \RuntimeException('Unable to read from stream');
+ }
+
+ return $string;
+ }
+
+ public function write($string)
+ {
+ if (!$this->writable) {
+ throw new \RuntimeException('Cannot write to a non-writable stream');
+ }
+
+ // We can't know the size after writing anything
+ $this->size = null;
+ $result = fwrite($this->stream, $string);
+
+ if ($result === false) {
+ throw new \RuntimeException('Unable to write to stream');
+ }
+
+ return $result;
+ }
+
+ public function getMetadata($key = null)
+ {
+ if (!isset($this->stream)) {
+ return $key ? null : [];
+ } elseif (!$key) {
+ return $this->customMetadata + stream_get_meta_data($this->stream);
+ } elseif (isset($this->customMetadata[$key])) {
+ return $this->customMetadata[$key];
+ }
+
+ $meta = stream_get_meta_data($this->stream);
+
+ return isset($meta[$key]) ? $meta[$key] : null;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php
new file mode 100644
index 0000000..daec6f5
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php
@@ -0,0 +1,149 @@
+stream = $stream;
+ }
+
+ /**
+ * Magic method used to create a new stream if streams are not added in
+ * the constructor of a decorator (e.g., LazyOpenStream).
+ *
+ * @param string $name Name of the property (allows "stream" only).
+ *
+ * @return StreamInterface
+ */
+ public function __get($name)
+ {
+ if ($name == 'stream') {
+ $this->stream = $this->createStream();
+ return $this->stream;
+ }
+
+ throw new \UnexpectedValueException("$name not found on class");
+ }
+
+ public function __toString()
+ {
+ try {
+ if ($this->isSeekable()) {
+ $this->seek(0);
+ }
+ return $this->getContents();
+ } catch (\Exception $e) {
+ // Really, PHP? https://bugs.php.net/bug.php?id=53648
+ trigger_error('StreamDecorator::__toString exception: '
+ . (string) $e, E_USER_ERROR);
+ return '';
+ }
+ }
+
+ public function getContents()
+ {
+ return copy_to_string($this);
+ }
+
+ /**
+ * Allow decorators to implement custom methods
+ *
+ * @param string $method Missing method name
+ * @param array $args Method arguments
+ *
+ * @return mixed
+ */
+ public function __call($method, array $args)
+ {
+ $result = call_user_func_array([$this->stream, $method], $args);
+
+ // Always return the wrapped object if the result is a return $this
+ return $result === $this->stream ? $this : $result;
+ }
+
+ public function close()
+ {
+ $this->stream->close();
+ }
+
+ public function getMetadata($key = null)
+ {
+ return $this->stream->getMetadata($key);
+ }
+
+ public function detach()
+ {
+ return $this->stream->detach();
+ }
+
+ public function getSize()
+ {
+ return $this->stream->getSize();
+ }
+
+ public function eof()
+ {
+ return $this->stream->eof();
+ }
+
+ public function tell()
+ {
+ return $this->stream->tell();
+ }
+
+ public function isReadable()
+ {
+ return $this->stream->isReadable();
+ }
+
+ public function isWritable()
+ {
+ return $this->stream->isWritable();
+ }
+
+ public function isSeekable()
+ {
+ return $this->stream->isSeekable();
+ }
+
+ public function rewind()
+ {
+ $this->seek(0);
+ }
+
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ $this->stream->seek($offset, $whence);
+ }
+
+ public function read($length)
+ {
+ return $this->stream->read($length);
+ }
+
+ public function write($string)
+ {
+ return $this->stream->write($string);
+ }
+
+ /**
+ * Implement in subclasses to dynamically create streams when requested.
+ *
+ * @return StreamInterface
+ * @throws \BadMethodCallException
+ */
+ protected function createStream()
+ {
+ throw new \BadMethodCallException('Not implemented');
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/StreamWrapper.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/StreamWrapper.php
new file mode 100644
index 0000000..cf7b223
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/StreamWrapper.php
@@ -0,0 +1,121 @@
+isReadable()) {
+ $mode = $stream->isWritable() ? 'r+' : 'r';
+ } elseif ($stream->isWritable()) {
+ $mode = 'w';
+ } else {
+ throw new \InvalidArgumentException('The stream must be readable, '
+ . 'writable, or both.');
+ }
+
+ return fopen('guzzle://stream', $mode, null, stream_context_create([
+ 'guzzle' => ['stream' => $stream]
+ ]));
+ }
+
+ /**
+ * Registers the stream wrapper if needed
+ */
+ public static function register()
+ {
+ if (!in_array('guzzle', stream_get_wrappers())) {
+ stream_wrapper_register('guzzle', __CLASS__);
+ }
+ }
+
+ public function stream_open($path, $mode, $options, &$opened_path)
+ {
+ $options = stream_context_get_options($this->context);
+
+ if (!isset($options['guzzle']['stream'])) {
+ return false;
+ }
+
+ $this->mode = $mode;
+ $this->stream = $options['guzzle']['stream'];
+
+ return true;
+ }
+
+ public function stream_read($count)
+ {
+ return $this->stream->read($count);
+ }
+
+ public function stream_write($data)
+ {
+ return (int) $this->stream->write($data);
+ }
+
+ public function stream_tell()
+ {
+ return $this->stream->tell();
+ }
+
+ public function stream_eof()
+ {
+ return $this->stream->eof();
+ }
+
+ public function stream_seek($offset, $whence)
+ {
+ $this->stream->seek($offset, $whence);
+
+ return true;
+ }
+
+ public function stream_stat()
+ {
+ static $modeMap = [
+ 'r' => 33060,
+ 'r+' => 33206,
+ 'w' => 33188
+ ];
+
+ return [
+ 'dev' => 0,
+ 'ino' => 0,
+ 'mode' => $modeMap[$this->mode],
+ 'nlink' => 0,
+ 'uid' => 0,
+ 'gid' => 0,
+ 'rdev' => 0,
+ 'size' => $this->stream->getSize() ?: 0,
+ 'atime' => 0,
+ 'mtime' => 0,
+ 'ctime' => 0,
+ 'blksize' => 0,
+ 'blocks' => 0
+ ];
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/UploadedFile.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/UploadedFile.php
new file mode 100644
index 0000000..e62bd5c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/UploadedFile.php
@@ -0,0 +1,316 @@
+setError($errorStatus);
+ $this->setSize($size);
+ $this->setClientFilename($clientFilename);
+ $this->setClientMediaType($clientMediaType);
+
+ if ($this->isOk()) {
+ $this->setStreamOrFile($streamOrFile);
+ }
+ }
+
+ /**
+ * Depending on the value set file or stream variable
+ *
+ * @param mixed $streamOrFile
+ * @throws InvalidArgumentException
+ */
+ private function setStreamOrFile($streamOrFile)
+ {
+ if (is_string($streamOrFile)) {
+ $this->file = $streamOrFile;
+ } elseif (is_resource($streamOrFile)) {
+ $this->stream = new Stream($streamOrFile);
+ } elseif ($streamOrFile instanceof StreamInterface) {
+ $this->stream = $streamOrFile;
+ } else {
+ throw new InvalidArgumentException(
+ 'Invalid stream or file provided for UploadedFile'
+ );
+ }
+ }
+
+ /**
+ * @param int $error
+ * @throws InvalidArgumentException
+ */
+ private function setError($error)
+ {
+ if (false === is_int($error)) {
+ throw new InvalidArgumentException(
+ 'Upload file error status must be an integer'
+ );
+ }
+
+ if (false === in_array($error, UploadedFile::$errors)) {
+ throw new InvalidArgumentException(
+ 'Invalid error status for UploadedFile'
+ );
+ }
+
+ $this->error = $error;
+ }
+
+ /**
+ * @param int $size
+ * @throws InvalidArgumentException
+ */
+ private function setSize($size)
+ {
+ if (false === is_int($size)) {
+ throw new InvalidArgumentException(
+ 'Upload file size must be an integer'
+ );
+ }
+
+ $this->size = $size;
+ }
+
+ /**
+ * @param mixed $param
+ * @return boolean
+ */
+ private function isStringOrNull($param)
+ {
+ return in_array(gettype($param), ['string', 'NULL']);
+ }
+
+ /**
+ * @param mixed $param
+ * @return boolean
+ */
+ private function isStringNotEmpty($param)
+ {
+ return is_string($param) && false === empty($param);
+ }
+
+ /**
+ * @param string|null $clientFilename
+ * @throws InvalidArgumentException
+ */
+ private function setClientFilename($clientFilename)
+ {
+ if (false === $this->isStringOrNull($clientFilename)) {
+ throw new InvalidArgumentException(
+ 'Upload file client filename must be a string or null'
+ );
+ }
+
+ $this->clientFilename = $clientFilename;
+ }
+
+ /**
+ * @param string|null $clientMediaType
+ * @throws InvalidArgumentException
+ */
+ private function setClientMediaType($clientMediaType)
+ {
+ if (false === $this->isStringOrNull($clientMediaType)) {
+ throw new InvalidArgumentException(
+ 'Upload file client media type must be a string or null'
+ );
+ }
+
+ $this->clientMediaType = $clientMediaType;
+ }
+
+ /**
+ * Return true if there is no upload error
+ *
+ * @return boolean
+ */
+ private function isOk()
+ {
+ return $this->error === UPLOAD_ERR_OK;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isMoved()
+ {
+ return $this->moved;
+ }
+
+ /**
+ * @throws RuntimeException if is moved or not ok
+ */
+ private function validateActive()
+ {
+ if (false === $this->isOk()) {
+ throw new RuntimeException('Cannot retrieve stream due to upload error');
+ }
+
+ if ($this->isMoved()) {
+ throw new RuntimeException('Cannot retrieve stream after it has already been moved');
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ * @throws RuntimeException if the upload was not successful.
+ */
+ public function getStream()
+ {
+ $this->validateActive();
+
+ if ($this->stream instanceof StreamInterface) {
+ return $this->stream;
+ }
+
+ return new LazyOpenStream($this->file, 'r+');
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see http://php.net/is_uploaded_file
+ * @see http://php.net/move_uploaded_file
+ * @param string $targetPath Path to which to move the uploaded file.
+ * @throws RuntimeException if the upload was not successful.
+ * @throws InvalidArgumentException if the $path specified is invalid.
+ * @throws RuntimeException on any error during the move operation, or on
+ * the second or subsequent call to the method.
+ */
+ public function moveTo($targetPath)
+ {
+ $this->validateActive();
+
+ if (false === $this->isStringNotEmpty($targetPath)) {
+ throw new InvalidArgumentException(
+ 'Invalid path provided for move operation; must be a non-empty string'
+ );
+ }
+
+ if ($this->file) {
+ $this->moved = php_sapi_name() == 'cli'
+ ? rename($this->file, $targetPath)
+ : move_uploaded_file($this->file, $targetPath);
+ } else {
+ copy_to_stream(
+ $this->getStream(),
+ new LazyOpenStream($targetPath, 'w')
+ );
+
+ $this->moved = true;
+ }
+
+ if (false === $this->moved) {
+ throw new RuntimeException(
+ sprintf('Uploaded file could not be moved to %s', $targetPath)
+ );
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return int|null The file size in bytes or null if unknown.
+ */
+ public function getSize()
+ {
+ return $this->size;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see http://php.net/manual/en/features.file-upload.errors.php
+ * @return int One of PHP's UPLOAD_ERR_XXX constants.
+ */
+ public function getError()
+ {
+ return $this->error;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return string|null The filename sent by the client or null if none
+ * was provided.
+ */
+ public function getClientFilename()
+ {
+ return $this->clientFilename;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getClientMediaType()
+ {
+ return $this->clientMediaType;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/Uri.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/Uri.php
new file mode 100644
index 0000000..f46c1db
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/Uri.php
@@ -0,0 +1,702 @@
+ 80,
+ 'https' => 443,
+ 'ftp' => 21,
+ 'gopher' => 70,
+ 'nntp' => 119,
+ 'news' => 119,
+ 'telnet' => 23,
+ 'tn3270' => 23,
+ 'imap' => 143,
+ 'pop' => 110,
+ 'ldap' => 389,
+ ];
+
+ private static $charUnreserved = 'a-zA-Z0-9_\-\.~';
+ private static $charSubDelims = '!\$&\'\(\)\*\+,;=';
+ private static $replaceQuery = ['=' => '%3D', '&' => '%26'];
+
+ /** @var string Uri scheme. */
+ private $scheme = '';
+
+ /** @var string Uri user info. */
+ private $userInfo = '';
+
+ /** @var string Uri host. */
+ private $host = '';
+
+ /** @var int|null Uri port. */
+ private $port;
+
+ /** @var string Uri path. */
+ private $path = '';
+
+ /** @var string Uri query string. */
+ private $query = '';
+
+ /** @var string Uri fragment. */
+ private $fragment = '';
+
+ /**
+ * @param string $uri URI to parse
+ */
+ public function __construct($uri = '')
+ {
+ // weak type check to also accept null until we can add scalar type hints
+ if ($uri != '') {
+ $parts = parse_url($uri);
+ if ($parts === false) {
+ throw new \InvalidArgumentException("Unable to parse URI: $uri");
+ }
+ $this->applyParts($parts);
+ }
+ }
+
+ public function __toString()
+ {
+ return self::composeComponents(
+ $this->scheme,
+ $this->getAuthority(),
+ $this->path,
+ $this->query,
+ $this->fragment
+ );
+ }
+
+ /**
+ * Composes a URI reference string from its various components.
+ *
+ * Usually this method does not need to be called manually but instead is used indirectly via
+ * `Psr\Http\Message\UriInterface::__toString`.
+ *
+ * PSR-7 UriInterface treats an empty component the same as a missing component as
+ * getQuery(), getFragment() etc. always return a string. This explains the slight
+ * difference to RFC 3986 Section 5.3.
+ *
+ * Another adjustment is that the authority separator is added even when the authority is missing/empty
+ * for the "file" scheme. This is because PHP stream functions like `file_get_contents` only work with
+ * `file:///myfile` but not with `file:/myfile` although they are equivalent according to RFC 3986. But
+ * `file:///` is the more common syntax for the file scheme anyway (Chrome for example redirects to
+ * that format).
+ *
+ * @param string $scheme
+ * @param string $authority
+ * @param string $path
+ * @param string $query
+ * @param string $fragment
+ *
+ * @return string
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-5.3
+ */
+ public static function composeComponents($scheme, $authority, $path, $query, $fragment)
+ {
+ $uri = '';
+
+ // weak type checks to also accept null until we can add scalar type hints
+ if ($scheme != '') {
+ $uri .= $scheme . ':';
+ }
+
+ if ($authority != ''|| $scheme === 'file') {
+ $uri .= '//' . $authority;
+ }
+
+ $uri .= $path;
+
+ if ($query != '') {
+ $uri .= '?' . $query;
+ }
+
+ if ($fragment != '') {
+ $uri .= '#' . $fragment;
+ }
+
+ return $uri;
+ }
+
+ /**
+ * Whether the URI has the default port of the current scheme.
+ *
+ * `Psr\Http\Message\UriInterface::getPort` may return null or the standard port. This method can be used
+ * independently of the implementation.
+ *
+ * @param UriInterface $uri
+ *
+ * @return bool
+ */
+ public static function isDefaultPort(UriInterface $uri)
+ {
+ return $uri->getPort() === null
+ || (isset(self::$defaultPorts[$uri->getScheme()]) && $uri->getPort() === self::$defaultPorts[$uri->getScheme()]);
+ }
+
+ /**
+ * Whether the URI is absolute, i.e. it has a scheme.
+ *
+ * An instance of UriInterface can either be an absolute URI or a relative reference. This method returns true
+ * if it is the former. An absolute URI has a scheme. A relative reference is used to express a URI relative
+ * to another URI, the base URI. Relative references can be divided into several forms:
+ * - network-path references, e.g. '//example.com/path'
+ * - absolute-path references, e.g. '/path'
+ * - relative-path references, e.g. 'subpath'
+ *
+ * @param UriInterface $uri
+ *
+ * @return bool
+ * @see Uri::isNetworkPathReference
+ * @see Uri::isAbsolutePathReference
+ * @see Uri::isRelativePathReference
+ * @link https://tools.ietf.org/html/rfc3986#section-4
+ */
+ public static function isAbsolute(UriInterface $uri)
+ {
+ return $uri->getScheme() !== '';
+ }
+
+ /**
+ * Whether the URI is a network-path reference.
+ *
+ * A relative reference that begins with two slash characters is termed an network-path reference.
+ *
+ * @param UriInterface $uri
+ *
+ * @return bool
+ * @link https://tools.ietf.org/html/rfc3986#section-4.2
+ */
+ public static function isNetworkPathReference(UriInterface $uri)
+ {
+ return $uri->getScheme() === '' && $uri->getAuthority() !== '';
+ }
+
+ /**
+ * Whether the URI is a absolute-path reference.
+ *
+ * A relative reference that begins with a single slash character is termed an absolute-path reference.
+ *
+ * @param UriInterface $uri
+ *
+ * @return bool
+ * @link https://tools.ietf.org/html/rfc3986#section-4.2
+ */
+ public static function isAbsolutePathReference(UriInterface $uri)
+ {
+ return $uri->getScheme() === ''
+ && $uri->getAuthority() === ''
+ && isset($uri->getPath()[0])
+ && $uri->getPath()[0] === '/';
+ }
+
+ /**
+ * Whether the URI is a relative-path reference.
+ *
+ * A relative reference that does not begin with a slash character is termed a relative-path reference.
+ *
+ * @param UriInterface $uri
+ *
+ * @return bool
+ * @link https://tools.ietf.org/html/rfc3986#section-4.2
+ */
+ public static function isRelativePathReference(UriInterface $uri)
+ {
+ return $uri->getScheme() === ''
+ && $uri->getAuthority() === ''
+ && (!isset($uri->getPath()[0]) || $uri->getPath()[0] !== '/');
+ }
+
+ /**
+ * Whether the URI is a same-document reference.
+ *
+ * A same-document reference refers to a URI that is, aside from its fragment
+ * component, identical to the base URI. When no base URI is given, only an empty
+ * URI reference (apart from its fragment) is considered a same-document reference.
+ *
+ * @param UriInterface $uri The URI to check
+ * @param UriInterface|null $base An optional base URI to compare against
+ *
+ * @return bool
+ * @link https://tools.ietf.org/html/rfc3986#section-4.4
+ */
+ public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null)
+ {
+ if ($base !== null) {
+ $uri = UriResolver::resolve($base, $uri);
+
+ return ($uri->getScheme() === $base->getScheme())
+ && ($uri->getAuthority() === $base->getAuthority())
+ && ($uri->getPath() === $base->getPath())
+ && ($uri->getQuery() === $base->getQuery());
+ }
+
+ return $uri->getScheme() === '' && $uri->getAuthority() === '' && $uri->getPath() === '' && $uri->getQuery() === '';
+ }
+
+ /**
+ * Removes dot segments from a path and returns the new path.
+ *
+ * @param string $path
+ *
+ * @return string
+ *
+ * @deprecated since version 1.4. Use UriResolver::removeDotSegments instead.
+ * @see UriResolver::removeDotSegments
+ */
+ public static function removeDotSegments($path)
+ {
+ return UriResolver::removeDotSegments($path);
+ }
+
+ /**
+ * Converts the relative URI into a new URI that is resolved against the base URI.
+ *
+ * @param UriInterface $base Base URI
+ * @param string|UriInterface $rel Relative URI
+ *
+ * @return UriInterface
+ *
+ * @deprecated since version 1.4. Use UriResolver::resolve instead.
+ * @see UriResolver::resolve
+ */
+ public static function resolve(UriInterface $base, $rel)
+ {
+ if (!($rel instanceof UriInterface)) {
+ $rel = new self($rel);
+ }
+
+ return UriResolver::resolve($base, $rel);
+ }
+
+ /**
+ * Creates a new URI with a specific query string value removed.
+ *
+ * Any existing query string values that exactly match the provided key are
+ * removed.
+ *
+ * @param UriInterface $uri URI to use as a base.
+ * @param string $key Query string key to remove.
+ *
+ * @return UriInterface
+ */
+ public static function withoutQueryValue(UriInterface $uri, $key)
+ {
+ $current = $uri->getQuery();
+ if ($current === '') {
+ return $uri;
+ }
+
+ $decodedKey = rawurldecode($key);
+ $result = array_filter(explode('&', $current), function ($part) use ($decodedKey) {
+ return rawurldecode(explode('=', $part)[0]) !== $decodedKey;
+ });
+
+ return $uri->withQuery(implode('&', $result));
+ }
+
+ /**
+ * Creates a new URI with a specific query string value.
+ *
+ * Any existing query string values that exactly match the provided key are
+ * removed and replaced with the given key value pair.
+ *
+ * A value of null will set the query string key without a value, e.g. "key"
+ * instead of "key=value".
+ *
+ * @param UriInterface $uri URI to use as a base.
+ * @param string $key Key to set.
+ * @param string|null $value Value to set
+ *
+ * @return UriInterface
+ */
+ public static function withQueryValue(UriInterface $uri, $key, $value)
+ {
+ $current = $uri->getQuery();
+
+ if ($current === '') {
+ $result = [];
+ } else {
+ $decodedKey = rawurldecode($key);
+ $result = array_filter(explode('&', $current), function ($part) use ($decodedKey) {
+ return rawurldecode(explode('=', $part)[0]) !== $decodedKey;
+ });
+ }
+
+ // Query string separators ("=", "&") within the key or value need to be encoded
+ // (while preventing double-encoding) before setting the query string. All other
+ // chars that need percent-encoding will be encoded by withQuery().
+ $key = strtr($key, self::$replaceQuery);
+
+ if ($value !== null) {
+ $result[] = $key . '=' . strtr($value, self::$replaceQuery);
+ } else {
+ $result[] = $key;
+ }
+
+ return $uri->withQuery(implode('&', $result));
+ }
+
+ /**
+ * Creates a URI from a hash of `parse_url` components.
+ *
+ * @param array $parts
+ *
+ * @return UriInterface
+ * @link http://php.net/manual/en/function.parse-url.php
+ *
+ * @throws \InvalidArgumentException If the components do not form a valid URI.
+ */
+ public static function fromParts(array $parts)
+ {
+ $uri = new self();
+ $uri->applyParts($parts);
+ $uri->validateState();
+
+ return $uri;
+ }
+
+ public function getScheme()
+ {
+ return $this->scheme;
+ }
+
+ public function getAuthority()
+ {
+ $authority = $this->host;
+ if ($this->userInfo !== '') {
+ $authority = $this->userInfo . '@' . $authority;
+ }
+
+ if ($this->port !== null) {
+ $authority .= ':' . $this->port;
+ }
+
+ return $authority;
+ }
+
+ public function getUserInfo()
+ {
+ return $this->userInfo;
+ }
+
+ public function getHost()
+ {
+ return $this->host;
+ }
+
+ public function getPort()
+ {
+ return $this->port;
+ }
+
+ public function getPath()
+ {
+ return $this->path;
+ }
+
+ public function getQuery()
+ {
+ return $this->query;
+ }
+
+ public function getFragment()
+ {
+ return $this->fragment;
+ }
+
+ public function withScheme($scheme)
+ {
+ $scheme = $this->filterScheme($scheme);
+
+ if ($this->scheme === $scheme) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->scheme = $scheme;
+ $new->removeDefaultPort();
+ $new->validateState();
+
+ return $new;
+ }
+
+ public function withUserInfo($user, $password = null)
+ {
+ $info = $user;
+ if ($password != '') {
+ $info .= ':' . $password;
+ }
+
+ if ($this->userInfo === $info) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->userInfo = $info;
+ $new->validateState();
+
+ return $new;
+ }
+
+ public function withHost($host)
+ {
+ $host = $this->filterHost($host);
+
+ if ($this->host === $host) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->host = $host;
+ $new->validateState();
+
+ return $new;
+ }
+
+ public function withPort($port)
+ {
+ $port = $this->filterPort($port);
+
+ if ($this->port === $port) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->port = $port;
+ $new->removeDefaultPort();
+ $new->validateState();
+
+ return $new;
+ }
+
+ public function withPath($path)
+ {
+ $path = $this->filterPath($path);
+
+ if ($this->path === $path) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->path = $path;
+ $new->validateState();
+
+ return $new;
+ }
+
+ public function withQuery($query)
+ {
+ $query = $this->filterQueryAndFragment($query);
+
+ if ($this->query === $query) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->query = $query;
+
+ return $new;
+ }
+
+ public function withFragment($fragment)
+ {
+ $fragment = $this->filterQueryAndFragment($fragment);
+
+ if ($this->fragment === $fragment) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->fragment = $fragment;
+
+ return $new;
+ }
+
+ /**
+ * Apply parse_url parts to a URI.
+ *
+ * @param array $parts Array of parse_url parts to apply.
+ */
+ private function applyParts(array $parts)
+ {
+ $this->scheme = isset($parts['scheme'])
+ ? $this->filterScheme($parts['scheme'])
+ : '';
+ $this->userInfo = isset($parts['user']) ? $parts['user'] : '';
+ $this->host = isset($parts['host'])
+ ? $this->filterHost($parts['host'])
+ : '';
+ $this->port = isset($parts['port'])
+ ? $this->filterPort($parts['port'])
+ : null;
+ $this->path = isset($parts['path'])
+ ? $this->filterPath($parts['path'])
+ : '';
+ $this->query = isset($parts['query'])
+ ? $this->filterQueryAndFragment($parts['query'])
+ : '';
+ $this->fragment = isset($parts['fragment'])
+ ? $this->filterQueryAndFragment($parts['fragment'])
+ : '';
+ if (isset($parts['pass'])) {
+ $this->userInfo .= ':' . $parts['pass'];
+ }
+
+ $this->removeDefaultPort();
+ }
+
+ /**
+ * @param string $scheme
+ *
+ * @return string
+ *
+ * @throws \InvalidArgumentException If the scheme is invalid.
+ */
+ private function filterScheme($scheme)
+ {
+ if (!is_string($scheme)) {
+ throw new \InvalidArgumentException('Scheme must be a string');
+ }
+
+ return strtolower($scheme);
+ }
+
+ /**
+ * @param string $host
+ *
+ * @return string
+ *
+ * @throws \InvalidArgumentException If the host is invalid.
+ */
+ private function filterHost($host)
+ {
+ if (!is_string($host)) {
+ throw new \InvalidArgumentException('Host must be a string');
+ }
+
+ return strtolower($host);
+ }
+
+ /**
+ * @param int|null $port
+ *
+ * @return int|null
+ *
+ * @throws \InvalidArgumentException If the port is invalid.
+ */
+ private function filterPort($port)
+ {
+ if ($port === null) {
+ return null;
+ }
+
+ $port = (int) $port;
+ if (1 > $port || 0xffff < $port) {
+ throw new \InvalidArgumentException(
+ sprintf('Invalid port: %d. Must be between 1 and 65535', $port)
+ );
+ }
+
+ return $port;
+ }
+
+ private function removeDefaultPort()
+ {
+ if ($this->port !== null && self::isDefaultPort($this)) {
+ $this->port = null;
+ }
+ }
+
+ /**
+ * Filters the path of a URI
+ *
+ * @param string $path
+ *
+ * @return string
+ *
+ * @throws \InvalidArgumentException If the path is invalid.
+ */
+ private function filterPath($path)
+ {
+ if (!is_string($path)) {
+ throw new \InvalidArgumentException('Path must be a string');
+ }
+
+ return preg_replace_callback(
+ '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/',
+ [$this, 'rawurlencodeMatchZero'],
+ $path
+ );
+ }
+
+ /**
+ * Filters the query string or fragment of a URI.
+ *
+ * @param string $str
+ *
+ * @return string
+ *
+ * @throws \InvalidArgumentException If the query or fragment is invalid.
+ */
+ private function filterQueryAndFragment($str)
+ {
+ if (!is_string($str)) {
+ throw new \InvalidArgumentException('Query and fragment must be a string');
+ }
+
+ return preg_replace_callback(
+ '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/',
+ [$this, 'rawurlencodeMatchZero'],
+ $str
+ );
+ }
+
+ private function rawurlencodeMatchZero(array $match)
+ {
+ return rawurlencode($match[0]);
+ }
+
+ private function validateState()
+ {
+ if ($this->host === '' && ($this->scheme === 'http' || $this->scheme === 'https')) {
+ $this->host = self::HTTP_DEFAULT_HOST;
+ }
+
+ if ($this->getAuthority() === '') {
+ if (0 === strpos($this->path, '//')) {
+ throw new \InvalidArgumentException('The path of a URI without an authority must not start with two slashes "//"');
+ }
+ if ($this->scheme === '' && false !== strpos(explode('/', $this->path, 2)[0], ':')) {
+ throw new \InvalidArgumentException('A relative URI must not have a path beginning with a segment containing a colon');
+ }
+ } elseif (isset($this->path[0]) && $this->path[0] !== '/') {
+ @trigger_error(
+ 'The path of a URI with an authority must start with a slash "/" or be empty. Automagically fixing the URI ' .
+ 'by adding a leading slash to the path is deprecated since version 1.4 and will throw an exception instead.',
+ E_USER_DEPRECATED
+ );
+ $this->path = '/'. $this->path;
+ //throw new \InvalidArgumentException('The path of a URI with an authority must start with a slash "/" or be empty');
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/UriNormalizer.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/UriNormalizer.php
new file mode 100644
index 0000000..384c29e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/UriNormalizer.php
@@ -0,0 +1,216 @@
+getPath() === '' &&
+ ($uri->getScheme() === 'http' || $uri->getScheme() === 'https')
+ ) {
+ $uri = $uri->withPath('/');
+ }
+
+ if ($flags & self::REMOVE_DEFAULT_HOST && $uri->getScheme() === 'file' && $uri->getHost() === 'localhost') {
+ $uri = $uri->withHost('');
+ }
+
+ if ($flags & self::REMOVE_DEFAULT_PORT && $uri->getPort() !== null && Uri::isDefaultPort($uri)) {
+ $uri = $uri->withPort(null);
+ }
+
+ if ($flags & self::REMOVE_DOT_SEGMENTS && !Uri::isRelativePathReference($uri)) {
+ $uri = $uri->withPath(UriResolver::removeDotSegments($uri->getPath()));
+ }
+
+ if ($flags & self::REMOVE_DUPLICATE_SLASHES) {
+ $uri = $uri->withPath(preg_replace('#//++#', '/', $uri->getPath()));
+ }
+
+ if ($flags & self::SORT_QUERY_PARAMETERS && $uri->getQuery() !== '') {
+ $queryKeyValues = explode('&', $uri->getQuery());
+ sort($queryKeyValues);
+ $uri = $uri->withQuery(implode('&', $queryKeyValues));
+ }
+
+ return $uri;
+ }
+
+ /**
+ * Whether two URIs can be considered equivalent.
+ *
+ * Both URIs are normalized automatically before comparison with the given $normalizations bitmask. The method also
+ * accepts relative URI references and returns true when they are equivalent. This of course assumes they will be
+ * resolved against the same base URI. If this is not the case, determination of equivalence or difference of
+ * relative references does not mean anything.
+ *
+ * @param UriInterface $uri1 An URI to compare
+ * @param UriInterface $uri2 An URI to compare
+ * @param int $normalizations A bitmask of normalizations to apply, see constants
+ *
+ * @return bool
+ * @link https://tools.ietf.org/html/rfc3986#section-6.1
+ */
+ public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS)
+ {
+ return (string) self::normalize($uri1, $normalizations) === (string) self::normalize($uri2, $normalizations);
+ }
+
+ private static function capitalizePercentEncoding(UriInterface $uri)
+ {
+ $regex = '/(?:%[A-Fa-f0-9]{2})++/';
+
+ $callback = function (array $match) {
+ return strtoupper($match[0]);
+ };
+
+ return
+ $uri->withPath(
+ preg_replace_callback($regex, $callback, $uri->getPath())
+ )->withQuery(
+ preg_replace_callback($regex, $callback, $uri->getQuery())
+ );
+ }
+
+ private static function decodeUnreservedCharacters(UriInterface $uri)
+ {
+ $regex = '/%(?:2D|2E|5F|7E|3[0-9]|[46][1-9A-F]|[57][0-9A])/i';
+
+ $callback = function (array $match) {
+ return rawurldecode($match[0]);
+ };
+
+ return
+ $uri->withPath(
+ preg_replace_callback($regex, $callback, $uri->getPath())
+ )->withQuery(
+ preg_replace_callback($regex, $callback, $uri->getQuery())
+ );
+ }
+
+ private function __construct()
+ {
+ // cannot be instantiated
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/UriResolver.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/UriResolver.php
new file mode 100644
index 0000000..c1cb8a2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/UriResolver.php
@@ -0,0 +1,219 @@
+getScheme() != '') {
+ return $rel->withPath(self::removeDotSegments($rel->getPath()));
+ }
+
+ if ($rel->getAuthority() != '') {
+ $targetAuthority = $rel->getAuthority();
+ $targetPath = self::removeDotSegments($rel->getPath());
+ $targetQuery = $rel->getQuery();
+ } else {
+ $targetAuthority = $base->getAuthority();
+ if ($rel->getPath() === '') {
+ $targetPath = $base->getPath();
+ $targetQuery = $rel->getQuery() != '' ? $rel->getQuery() : $base->getQuery();
+ } else {
+ if ($rel->getPath()[0] === '/') {
+ $targetPath = $rel->getPath();
+ } else {
+ if ($targetAuthority != '' && $base->getPath() === '') {
+ $targetPath = '/' . $rel->getPath();
+ } else {
+ $lastSlashPos = strrpos($base->getPath(), '/');
+ if ($lastSlashPos === false) {
+ $targetPath = $rel->getPath();
+ } else {
+ $targetPath = substr($base->getPath(), 0, $lastSlashPos + 1) . $rel->getPath();
+ }
+ }
+ }
+ $targetPath = self::removeDotSegments($targetPath);
+ $targetQuery = $rel->getQuery();
+ }
+ }
+
+ return new Uri(Uri::composeComponents(
+ $base->getScheme(),
+ $targetAuthority,
+ $targetPath,
+ $targetQuery,
+ $rel->getFragment()
+ ));
+ }
+
+ /**
+ * Returns the target URI as a relative reference from the base URI.
+ *
+ * This method is the counterpart to resolve():
+ *
+ * (string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
+ *
+ * One use-case is to use the current request URI as base URI and then generate relative links in your documents
+ * to reduce the document size or offer self-contained downloadable document archives.
+ *
+ * $base = new Uri('http://example.com/a/b/');
+ * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'.
+ * echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'.
+ * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
+ * echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'.
+ *
+ * This method also accepts a target that is already relative and will try to relativize it further. Only a
+ * relative-path reference will be returned as-is.
+ *
+ * echo UriResolver::relativize($base, new Uri('/a/b/c')); // prints 'c' as well
+ *
+ * @param UriInterface $base Base URI
+ * @param UriInterface $target Target URI
+ *
+ * @return UriInterface The relative URI reference
+ */
+ public static function relativize(UriInterface $base, UriInterface $target)
+ {
+ if ($target->getScheme() !== '' &&
+ ($base->getScheme() !== $target->getScheme() || $target->getAuthority() === '' && $base->getAuthority() !== '')
+ ) {
+ return $target;
+ }
+
+ if (Uri::isRelativePathReference($target)) {
+ // As the target is already highly relative we return it as-is. It would be possible to resolve
+ // the target with `$target = self::resolve($base, $target);` and then try make it more relative
+ // by removing a duplicate query. But let's not do that automatically.
+ return $target;
+ }
+
+ if ($target->getAuthority() !== '' && $base->getAuthority() !== $target->getAuthority()) {
+ return $target->withScheme('');
+ }
+
+ // We must remove the path before removing the authority because if the path starts with two slashes, the URI
+ // would turn invalid. And we also cannot set a relative path before removing the authority, as that is also
+ // invalid.
+ $emptyPathUri = $target->withScheme('')->withPath('')->withUserInfo('')->withPort(null)->withHost('');
+
+ if ($base->getPath() !== $target->getPath()) {
+ return $emptyPathUri->withPath(self::getRelativePath($base, $target));
+ }
+
+ if ($base->getQuery() === $target->getQuery()) {
+ // Only the target fragment is left. And it must be returned even if base and target fragment are the same.
+ return $emptyPathUri->withQuery('');
+ }
+
+ // If the base URI has a query but the target has none, we cannot return an empty path reference as it would
+ // inherit the base query component when resolving.
+ if ($target->getQuery() === '') {
+ $segments = explode('/', $target->getPath());
+ $lastSegment = end($segments);
+
+ return $emptyPathUri->withPath($lastSegment === '' ? './' : $lastSegment);
+ }
+
+ return $emptyPathUri;
+ }
+
+ private static function getRelativePath(UriInterface $base, UriInterface $target)
+ {
+ $sourceSegments = explode('/', $base->getPath());
+ $targetSegments = explode('/', $target->getPath());
+ array_pop($sourceSegments);
+ $targetLastSegment = array_pop($targetSegments);
+ foreach ($sourceSegments as $i => $segment) {
+ if (isset($targetSegments[$i]) && $segment === $targetSegments[$i]) {
+ unset($sourceSegments[$i], $targetSegments[$i]);
+ } else {
+ break;
+ }
+ }
+ $targetSegments[] = $targetLastSegment;
+ $relativePath = str_repeat('../', count($sourceSegments)) . implode('/', $targetSegments);
+
+ // A reference to am empty last segment or an empty first sub-segment must be prefixed with "./".
+ // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
+ // as the first segment of a relative-path reference, as it would be mistaken for a scheme name.
+ if ('' === $relativePath || false !== strpos(explode('/', $relativePath, 2)[0], ':')) {
+ $relativePath = "./$relativePath";
+ } elseif ('/' === $relativePath[0]) {
+ if ($base->getAuthority() != '' && $base->getPath() === '') {
+ // In this case an extra slash is added by resolve() automatically. So we must not add one here.
+ $relativePath = ".$relativePath";
+ } else {
+ $relativePath = "./$relativePath";
+ }
+ }
+
+ return $relativePath;
+ }
+
+ private function __construct()
+ {
+ // cannot be instantiated
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/functions.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/functions.php
new file mode 100644
index 0000000..e40348d
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/functions.php
@@ -0,0 +1,828 @@
+getMethod() . ' '
+ . $message->getRequestTarget())
+ . ' HTTP/' . $message->getProtocolVersion();
+ if (!$message->hasHeader('host')) {
+ $msg .= "\r\nHost: " . $message->getUri()->getHost();
+ }
+ } elseif ($message instanceof ResponseInterface) {
+ $msg = 'HTTP/' . $message->getProtocolVersion() . ' '
+ . $message->getStatusCode() . ' '
+ . $message->getReasonPhrase();
+ } else {
+ throw new \InvalidArgumentException('Unknown message type');
+ }
+
+ foreach ($message->getHeaders() as $name => $values) {
+ $msg .= "\r\n{$name}: " . implode(', ', $values);
+ }
+
+ return "{$msg}\r\n\r\n" . $message->getBody();
+}
+
+/**
+ * Returns a UriInterface for the given value.
+ *
+ * This function accepts a string or {@see Psr\Http\Message\UriInterface} and
+ * returns a UriInterface for the given value. If the value is already a
+ * `UriInterface`, it is returned as-is.
+ *
+ * @param string|UriInterface $uri
+ *
+ * @return UriInterface
+ * @throws \InvalidArgumentException
+ */
+function uri_for($uri)
+{
+ if ($uri instanceof UriInterface) {
+ return $uri;
+ } elseif (is_string($uri)) {
+ return new Uri($uri);
+ }
+
+ throw new \InvalidArgumentException('URI must be a string or UriInterface');
+}
+
+/**
+ * Create a new stream based on the input type.
+ *
+ * Options is an associative array that can contain the following keys:
+ * - metadata: Array of custom metadata.
+ * - size: Size of the stream.
+ *
+ * @param resource|string|null|int|float|bool|StreamInterface|callable $resource Entity body data
+ * @param array $options Additional options
+ *
+ * @return Stream
+ * @throws \InvalidArgumentException if the $resource arg is not valid.
+ */
+function stream_for($resource = '', array $options = [])
+{
+ if (is_scalar($resource)) {
+ $stream = fopen('php://temp', 'r+');
+ if ($resource !== '') {
+ fwrite($stream, $resource);
+ fseek($stream, 0);
+ }
+ return new Stream($stream, $options);
+ }
+
+ switch (gettype($resource)) {
+ case 'resource':
+ return new Stream($resource, $options);
+ case 'object':
+ if ($resource instanceof StreamInterface) {
+ return $resource;
+ } elseif ($resource instanceof \Iterator) {
+ return new PumpStream(function () use ($resource) {
+ if (!$resource->valid()) {
+ return false;
+ }
+ $result = $resource->current();
+ $resource->next();
+ return $result;
+ }, $options);
+ } elseif (method_exists($resource, '__toString')) {
+ return stream_for((string) $resource, $options);
+ }
+ break;
+ case 'NULL':
+ return new Stream(fopen('php://temp', 'r+'), $options);
+ }
+
+ if (is_callable($resource)) {
+ return new PumpStream($resource, $options);
+ }
+
+ throw new \InvalidArgumentException('Invalid resource type: ' . gettype($resource));
+}
+
+/**
+ * Parse an array of header values containing ";" separated data into an
+ * array of associative arrays representing the header key value pair
+ * data of the header. When a parameter does not contain a value, but just
+ * contains a key, this function will inject a key with a '' string value.
+ *
+ * @param string|array $header Header to parse into components.
+ *
+ * @return array Returns the parsed header values.
+ */
+function parse_header($header)
+{
+ static $trimmed = "\"' \n\t\r";
+ $params = $matches = [];
+
+ foreach (normalize_header($header) as $val) {
+ $part = [];
+ foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) {
+ if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) {
+ $m = $matches[0];
+ if (isset($m[1])) {
+ $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed);
+ } else {
+ $part[] = trim($m[0], $trimmed);
+ }
+ }
+ }
+ if ($part) {
+ $params[] = $part;
+ }
+ }
+
+ return $params;
+}
+
+/**
+ * Converts an array of header values that may contain comma separated
+ * headers into an array of headers with no comma separated values.
+ *
+ * @param string|array $header Header to normalize.
+ *
+ * @return array Returns the normalized header field values.
+ */
+function normalize_header($header)
+{
+ if (!is_array($header)) {
+ return array_map('trim', explode(',', $header));
+ }
+
+ $result = [];
+ foreach ($header as $value) {
+ foreach ((array) $value as $v) {
+ if (strpos($v, ',') === false) {
+ $result[] = $v;
+ continue;
+ }
+ foreach (preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $v) as $vv) {
+ $result[] = trim($vv);
+ }
+ }
+ }
+
+ return $result;
+}
+
+/**
+ * Clone and modify a request with the given changes.
+ *
+ * The changes can be one of:
+ * - method: (string) Changes the HTTP method.
+ * - set_headers: (array) Sets the given headers.
+ * - remove_headers: (array) Remove the given headers.
+ * - body: (mixed) Sets the given body.
+ * - uri: (UriInterface) Set the URI.
+ * - query: (string) Set the query string value of the URI.
+ * - version: (string) Set the protocol version.
+ *
+ * @param RequestInterface $request Request to clone and modify.
+ * @param array $changes Changes to apply.
+ *
+ * @return RequestInterface
+ */
+function modify_request(RequestInterface $request, array $changes)
+{
+ if (!$changes) {
+ return $request;
+ }
+
+ $headers = $request->getHeaders();
+
+ if (!isset($changes['uri'])) {
+ $uri = $request->getUri();
+ } else {
+ // Remove the host header if one is on the URI
+ if ($host = $changes['uri']->getHost()) {
+ $changes['set_headers']['Host'] = $host;
+
+ if ($port = $changes['uri']->getPort()) {
+ $standardPorts = ['http' => 80, 'https' => 443];
+ $scheme = $changes['uri']->getScheme();
+ if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) {
+ $changes['set_headers']['Host'] .= ':'.$port;
+ }
+ }
+ }
+ $uri = $changes['uri'];
+ }
+
+ if (!empty($changes['remove_headers'])) {
+ $headers = _caseless_remove($changes['remove_headers'], $headers);
+ }
+
+ if (!empty($changes['set_headers'])) {
+ $headers = _caseless_remove(array_keys($changes['set_headers']), $headers);
+ $headers = $changes['set_headers'] + $headers;
+ }
+
+ if (isset($changes['query'])) {
+ $uri = $uri->withQuery($changes['query']);
+ }
+
+ if ($request instanceof ServerRequestInterface) {
+ return new ServerRequest(
+ isset($changes['method']) ? $changes['method'] : $request->getMethod(),
+ $uri,
+ $headers,
+ isset($changes['body']) ? $changes['body'] : $request->getBody(),
+ isset($changes['version'])
+ ? $changes['version']
+ : $request->getProtocolVersion(),
+ $request->getServerParams()
+ );
+ }
+
+ return new Request(
+ isset($changes['method']) ? $changes['method'] : $request->getMethod(),
+ $uri,
+ $headers,
+ isset($changes['body']) ? $changes['body'] : $request->getBody(),
+ isset($changes['version'])
+ ? $changes['version']
+ : $request->getProtocolVersion()
+ );
+}
+
+/**
+ * Attempts to rewind a message body and throws an exception on failure.
+ *
+ * The body of the message will only be rewound if a call to `tell()` returns a
+ * value other than `0`.
+ *
+ * @param MessageInterface $message Message to rewind
+ *
+ * @throws \RuntimeException
+ */
+function rewind_body(MessageInterface $message)
+{
+ $body = $message->getBody();
+
+ if ($body->tell()) {
+ $body->rewind();
+ }
+}
+
+/**
+ * Safely opens a PHP stream resource using a filename.
+ *
+ * When fopen fails, PHP normally raises a warning. This function adds an
+ * error handler that checks for errors and throws an exception instead.
+ *
+ * @param string $filename File to open
+ * @param string $mode Mode used to open the file
+ *
+ * @return resource
+ * @throws \RuntimeException if the file cannot be opened
+ */
+function try_fopen($filename, $mode)
+{
+ $ex = null;
+ set_error_handler(function () use ($filename, $mode, &$ex) {
+ $ex = new \RuntimeException(sprintf(
+ 'Unable to open %s using mode %s: %s',
+ $filename,
+ $mode,
+ func_get_args()[1]
+ ));
+ });
+
+ $handle = fopen($filename, $mode);
+ restore_error_handler();
+
+ if ($ex) {
+ /** @var $ex \RuntimeException */
+ throw $ex;
+ }
+
+ return $handle;
+}
+
+/**
+ * Copy the contents of a stream into a string until the given number of
+ * bytes have been read.
+ *
+ * @param StreamInterface $stream Stream to read
+ * @param int $maxLen Maximum number of bytes to read. Pass -1
+ * to read the entire stream.
+ * @return string
+ * @throws \RuntimeException on error.
+ */
+function copy_to_string(StreamInterface $stream, $maxLen = -1)
+{
+ $buffer = '';
+
+ if ($maxLen === -1) {
+ while (!$stream->eof()) {
+ $buf = $stream->read(1048576);
+ // Using a loose equality here to match on '' and false.
+ if ($buf == null) {
+ break;
+ }
+ $buffer .= $buf;
+ }
+ return $buffer;
+ }
+
+ $len = 0;
+ while (!$stream->eof() && $len < $maxLen) {
+ $buf = $stream->read($maxLen - $len);
+ // Using a loose equality here to match on '' and false.
+ if ($buf == null) {
+ break;
+ }
+ $buffer .= $buf;
+ $len = strlen($buffer);
+ }
+
+ return $buffer;
+}
+
+/**
+ * Copy the contents of a stream into another stream until the given number
+ * of bytes have been read.
+ *
+ * @param StreamInterface $source Stream to read from
+ * @param StreamInterface $dest Stream to write to
+ * @param int $maxLen Maximum number of bytes to read. Pass -1
+ * to read the entire stream.
+ *
+ * @throws \RuntimeException on error.
+ */
+function copy_to_stream(
+ StreamInterface $source,
+ StreamInterface $dest,
+ $maxLen = -1
+) {
+ $bufferSize = 8192;
+
+ if ($maxLen === -1) {
+ while (!$source->eof()) {
+ if (!$dest->write($source->read($bufferSize))) {
+ break;
+ }
+ }
+ } else {
+ $remaining = $maxLen;
+ while ($remaining > 0 && !$source->eof()) {
+ $buf = $source->read(min($bufferSize, $remaining));
+ $len = strlen($buf);
+ if (!$len) {
+ break;
+ }
+ $remaining -= $len;
+ $dest->write($buf);
+ }
+ }
+}
+
+/**
+ * Calculate a hash of a Stream
+ *
+ * @param StreamInterface $stream Stream to calculate the hash for
+ * @param string $algo Hash algorithm (e.g. md5, crc32, etc)
+ * @param bool $rawOutput Whether or not to use raw output
+ *
+ * @return string Returns the hash of the stream
+ * @throws \RuntimeException on error.
+ */
+function hash(
+ StreamInterface $stream,
+ $algo,
+ $rawOutput = false
+) {
+ $pos = $stream->tell();
+
+ if ($pos > 0) {
+ $stream->rewind();
+ }
+
+ $ctx = hash_init($algo);
+ while (!$stream->eof()) {
+ hash_update($ctx, $stream->read(1048576));
+ }
+
+ $out = hash_final($ctx, (bool) $rawOutput);
+ $stream->seek($pos);
+
+ return $out;
+}
+
+/**
+ * Read a line from the stream up to the maximum allowed buffer length
+ *
+ * @param StreamInterface $stream Stream to read from
+ * @param int $maxLength Maximum buffer length
+ *
+ * @return string|bool
+ */
+function readline(StreamInterface $stream, $maxLength = null)
+{
+ $buffer = '';
+ $size = 0;
+
+ while (!$stream->eof()) {
+ // Using a loose equality here to match on '' and false.
+ if (null == ($byte = $stream->read(1))) {
+ return $buffer;
+ }
+ $buffer .= $byte;
+ // Break when a new line is found or the max length - 1 is reached
+ if ($byte === "\n" || ++$size === $maxLength - 1) {
+ break;
+ }
+ }
+
+ return $buffer;
+}
+
+/**
+ * Parses a request message string into a request object.
+ *
+ * @param string $message Request message string.
+ *
+ * @return Request
+ */
+function parse_request($message)
+{
+ $data = _parse_message($message);
+ $matches = [];
+ if (!preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) {
+ throw new \InvalidArgumentException('Invalid request string');
+ }
+ $parts = explode(' ', $data['start-line'], 3);
+ $version = isset($parts[2]) ? explode('/', $parts[2])[1] : '1.1';
+
+ $request = new Request(
+ $parts[0],
+ $matches[1] === '/' ? _parse_request_uri($parts[1], $data['headers']) : $parts[1],
+ $data['headers'],
+ $data['body'],
+ $version
+ );
+
+ return $matches[1] === '/' ? $request : $request->withRequestTarget($parts[1]);
+}
+
+/**
+ * Parses a response message string into a response object.
+ *
+ * @param string $message Response message string.
+ *
+ * @return Response
+ */
+function parse_response($message)
+{
+ $data = _parse_message($message);
+ // According to https://tools.ietf.org/html/rfc7230#section-3.1.2 the space
+ // between status-code and reason-phrase is required. But browsers accept
+ // responses without space and reason as well.
+ if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) {
+ throw new \InvalidArgumentException('Invalid response string');
+ }
+ $parts = explode(' ', $data['start-line'], 3);
+
+ return new Response(
+ $parts[1],
+ $data['headers'],
+ $data['body'],
+ explode('/', $parts[0])[1],
+ isset($parts[2]) ? $parts[2] : null
+ );
+}
+
+/**
+ * Parse a query string into an associative array.
+ *
+ * If multiple values are found for the same key, the value of that key
+ * value pair will become an array. This function does not parse nested
+ * PHP style arrays into an associative array (e.g., foo[a]=1&foo[b]=2 will
+ * be parsed into ['foo[a]' => '1', 'foo[b]' => '2']).
+ *
+ * @param string $str Query string to parse
+ * @param bool|string $urlEncoding How the query string is encoded
+ *
+ * @return array
+ */
+function parse_query($str, $urlEncoding = true)
+{
+ $result = [];
+
+ if ($str === '') {
+ return $result;
+ }
+
+ if ($urlEncoding === true) {
+ $decoder = function ($value) {
+ return rawurldecode(str_replace('+', ' ', $value));
+ };
+ } elseif ($urlEncoding == PHP_QUERY_RFC3986) {
+ $decoder = 'rawurldecode';
+ } elseif ($urlEncoding == PHP_QUERY_RFC1738) {
+ $decoder = 'urldecode';
+ } else {
+ $decoder = function ($str) { return $str; };
+ }
+
+ foreach (explode('&', $str) as $kvp) {
+ $parts = explode('=', $kvp, 2);
+ $key = $decoder($parts[0]);
+ $value = isset($parts[1]) ? $decoder($parts[1]) : null;
+ if (!isset($result[$key])) {
+ $result[$key] = $value;
+ } else {
+ if (!is_array($result[$key])) {
+ $result[$key] = [$result[$key]];
+ }
+ $result[$key][] = $value;
+ }
+ }
+
+ return $result;
+}
+
+/**
+ * Build a query string from an array of key value pairs.
+ *
+ * This function can use the return value of parse_query() to build a query
+ * string. This function does not modify the provided keys when an array is
+ * encountered (like http_build_query would).
+ *
+ * @param array $params Query string parameters.
+ * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
+ * to encode using RFC3986, or PHP_QUERY_RFC1738
+ * to encode using RFC1738.
+ * @return string
+ */
+function build_query(array $params, $encoding = PHP_QUERY_RFC3986)
+{
+ if (!$params) {
+ return '';
+ }
+
+ if ($encoding === false) {
+ $encoder = function ($str) { return $str; };
+ } elseif ($encoding === PHP_QUERY_RFC3986) {
+ $encoder = 'rawurlencode';
+ } elseif ($encoding === PHP_QUERY_RFC1738) {
+ $encoder = 'urlencode';
+ } else {
+ throw new \InvalidArgumentException('Invalid type');
+ }
+
+ $qs = '';
+ foreach ($params as $k => $v) {
+ $k = $encoder($k);
+ if (!is_array($v)) {
+ $qs .= $k;
+ if ($v !== null) {
+ $qs .= '=' . $encoder($v);
+ }
+ $qs .= '&';
+ } else {
+ foreach ($v as $vv) {
+ $qs .= $k;
+ if ($vv !== null) {
+ $qs .= '=' . $encoder($vv);
+ }
+ $qs .= '&';
+ }
+ }
+ }
+
+ return $qs ? (string) substr($qs, 0, -1) : '';
+}
+
+/**
+ * Determines the mimetype of a file by looking at its extension.
+ *
+ * @param $filename
+ *
+ * @return null|string
+ */
+function mimetype_from_filename($filename)
+{
+ return mimetype_from_extension(pathinfo($filename, PATHINFO_EXTENSION));
+}
+
+/**
+ * Maps a file extensions to a mimetype.
+ *
+ * @param $extension string The file extension.
+ *
+ * @return string|null
+ * @link http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types
+ */
+function mimetype_from_extension($extension)
+{
+ static $mimetypes = [
+ '7z' => 'application/x-7z-compressed',
+ 'aac' => 'audio/x-aac',
+ 'ai' => 'application/postscript',
+ 'aif' => 'audio/x-aiff',
+ 'asc' => 'text/plain',
+ 'asf' => 'video/x-ms-asf',
+ 'atom' => 'application/atom+xml',
+ 'avi' => 'video/x-msvideo',
+ 'bmp' => 'image/bmp',
+ 'bz2' => 'application/x-bzip2',
+ 'cer' => 'application/pkix-cert',
+ 'crl' => 'application/pkix-crl',
+ 'crt' => 'application/x-x509-ca-cert',
+ 'css' => 'text/css',
+ 'csv' => 'text/csv',
+ 'cu' => 'application/cu-seeme',
+ 'deb' => 'application/x-debian-package',
+ 'doc' => 'application/msword',
+ 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+ 'dvi' => 'application/x-dvi',
+ 'eot' => 'application/vnd.ms-fontobject',
+ 'eps' => 'application/postscript',
+ 'epub' => 'application/epub+zip',
+ 'etx' => 'text/x-setext',
+ 'flac' => 'audio/flac',
+ 'flv' => 'video/x-flv',
+ 'gif' => 'image/gif',
+ 'gz' => 'application/gzip',
+ 'htm' => 'text/html',
+ 'html' => 'text/html',
+ 'ico' => 'image/x-icon',
+ 'ics' => 'text/calendar',
+ 'ini' => 'text/plain',
+ 'iso' => 'application/x-iso9660-image',
+ 'jar' => 'application/java-archive',
+ 'jpe' => 'image/jpeg',
+ 'jpeg' => 'image/jpeg',
+ 'jpg' => 'image/jpeg',
+ 'js' => 'text/javascript',
+ 'json' => 'application/json',
+ 'latex' => 'application/x-latex',
+ 'log' => 'text/plain',
+ 'm4a' => 'audio/mp4',
+ 'm4v' => 'video/mp4',
+ 'mid' => 'audio/midi',
+ 'midi' => 'audio/midi',
+ 'mov' => 'video/quicktime',
+ 'mp3' => 'audio/mpeg',
+ 'mp4' => 'video/mp4',
+ 'mp4a' => 'audio/mp4',
+ 'mp4v' => 'video/mp4',
+ 'mpe' => 'video/mpeg',
+ 'mpeg' => 'video/mpeg',
+ 'mpg' => 'video/mpeg',
+ 'mpg4' => 'video/mp4',
+ 'oga' => 'audio/ogg',
+ 'ogg' => 'audio/ogg',
+ 'ogv' => 'video/ogg',
+ 'ogx' => 'application/ogg',
+ 'pbm' => 'image/x-portable-bitmap',
+ 'pdf' => 'application/pdf',
+ 'pgm' => 'image/x-portable-graymap',
+ 'png' => 'image/png',
+ 'pnm' => 'image/x-portable-anymap',
+ 'ppm' => 'image/x-portable-pixmap',
+ 'ppt' => 'application/vnd.ms-powerpoint',
+ 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+ 'ps' => 'application/postscript',
+ 'qt' => 'video/quicktime',
+ 'rar' => 'application/x-rar-compressed',
+ 'ras' => 'image/x-cmu-raster',
+ 'rss' => 'application/rss+xml',
+ 'rtf' => 'application/rtf',
+ 'sgm' => 'text/sgml',
+ 'sgml' => 'text/sgml',
+ 'svg' => 'image/svg+xml',
+ 'swf' => 'application/x-shockwave-flash',
+ 'tar' => 'application/x-tar',
+ 'tif' => 'image/tiff',
+ 'tiff' => 'image/tiff',
+ 'torrent' => 'application/x-bittorrent',
+ 'ttf' => 'application/x-font-ttf',
+ 'txt' => 'text/plain',
+ 'wav' => 'audio/x-wav',
+ 'webm' => 'video/webm',
+ 'wma' => 'audio/x-ms-wma',
+ 'wmv' => 'video/x-ms-wmv',
+ 'woff' => 'application/x-font-woff',
+ 'wsdl' => 'application/wsdl+xml',
+ 'xbm' => 'image/x-xbitmap',
+ 'xls' => 'application/vnd.ms-excel',
+ 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ 'xml' => 'application/xml',
+ 'xpm' => 'image/x-xpixmap',
+ 'xwd' => 'image/x-xwindowdump',
+ 'yaml' => 'text/yaml',
+ 'yml' => 'text/yaml',
+ 'zip' => 'application/zip',
+ ];
+
+ $extension = strtolower($extension);
+
+ return isset($mimetypes[$extension])
+ ? $mimetypes[$extension]
+ : null;
+}
+
+/**
+ * Parses an HTTP message into an associative array.
+ *
+ * The array contains the "start-line" key containing the start line of
+ * the message, "headers" key containing an associative array of header
+ * array values, and a "body" key containing the body of the message.
+ *
+ * @param string $message HTTP request or response to parse.
+ *
+ * @return array
+ * @internal
+ */
+function _parse_message($message)
+{
+ if (!$message) {
+ throw new \InvalidArgumentException('Invalid message');
+ }
+
+ // Iterate over each line in the message, accounting for line endings
+ $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE);
+ $result = ['start-line' => array_shift($lines), 'headers' => [], 'body' => ''];
+ array_shift($lines);
+
+ for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) {
+ $line = $lines[$i];
+ // If two line breaks were encountered, then this is the end of body
+ if (empty($line)) {
+ if ($i < $totalLines - 1) {
+ $result['body'] = implode('', array_slice($lines, $i + 2));
+ }
+ break;
+ }
+ if (strpos($line, ':')) {
+ $parts = explode(':', $line, 2);
+ $key = trim($parts[0]);
+ $value = isset($parts[1]) ? trim($parts[1]) : '';
+ $result['headers'][$key][] = $value;
+ }
+ }
+
+ return $result;
+}
+
+/**
+ * Constructs a URI for an HTTP request message.
+ *
+ * @param string $path Path from the start-line
+ * @param array $headers Array of headers (each value an array).
+ *
+ * @return string
+ * @internal
+ */
+function _parse_request_uri($path, array $headers)
+{
+ $hostKey = array_filter(array_keys($headers), function ($k) {
+ return strtolower($k) === 'host';
+ });
+
+ // If no host is found, then a full URI cannot be constructed.
+ if (!$hostKey) {
+ return $path;
+ }
+
+ $host = $headers[reset($hostKey)][0];
+ $scheme = substr($host, -4) === ':443' ? 'https' : 'http';
+
+ return $scheme . '://' . $host . '/' . ltrim($path, '/');
+}
+
+/** @internal */
+function _caseless_remove($keys, array $data)
+{
+ $result = [];
+
+ foreach ($keys as &$key) {
+ $key = strtolower($key);
+ }
+
+ foreach ($data as $k => $v) {
+ if (!in_array(strtolower($k), $keys)) {
+ $result[$k] = $v;
+ }
+ }
+
+ return $result;
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/functions_include.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/functions_include.php
new file mode 100644
index 0000000..96a4a83
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/guzzlehttp/psr7/src/functions_include.php
@@ -0,0 +1,6 @@
+ 1.0.0"
+gem "sass"
+gem "compass"
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/LICENSE b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/LICENSE
new file mode 100644
index 0000000..f5dedf4
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2010-2015 OpenSky Project Inc
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/README.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/README.md
new file mode 100644
index 0000000..7ec0058
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/README.md
@@ -0,0 +1,345 @@
+# Assetic [](https://travis-ci.org/kriswallsmith/assetic)  #
+
+Assetic is an asset management framework for PHP.
+
+``` php
+dump();
+```
+
+Assets
+------
+
+An Assetic asset is something with filterable content that can be loaded and
+dumped. An asset also includes metadata, some of which can be manipulated and
+some of which is immutable.
+
+| **Property** | **Accessor** | **Mutator** |
+|--------------|-----------------|---------------|
+| content | getContent | setContent |
+| mtime | getLastModified | n/a |
+| source root | getSourceRoot | n/a |
+| source path | getSourcePath | n/a |
+| target path | getTargetPath | setTargetPath |
+
+The "target path" property denotes where an asset (or an collection of assets) should be dumped.
+
+Filters
+-------
+
+Filters can be applied to manipulate assets.
+
+``` php
+dump();
+```
+
+The filters applied to the collection will cascade to each asset leaf if you
+iterate over it.
+
+``` php
+dump();
+}
+```
+
+The core provides the following filters in the `Assetic\Filter` namespace:
+
+ * `AutoprefixerFilter`: Parse and update vendor-specific properties using autoprefixer
+ * `CoffeeScriptFilter`: compiles CoffeeScript into Javascript
+ * `CompassFilter`: Compass CSS authoring framework
+ * `CssEmbedFilter`: embeds image data in your stylesheets
+ * `CssImportFilter`: inlines imported stylesheets
+ * `CssMinFilter`: minifies CSS
+ * `CleanCssFilter`: minifies CSS
+ * `CssRewriteFilter`: fixes relative URLs in CSS assets when moving to a new URL
+ * `DartFilter`: compiles Javascript using dart2js
+ * `EmberPrecompileFilter`: precompiles Handlebars templates into Javascript for use in the Ember.js framework
+ * `GoogleClosure\CompilerApiFilter`: compiles Javascript using the Google Closure Compiler API
+ * `GoogleClosure\CompilerJarFilter`: compiles Javascript using the Google Closure Compiler JAR
+ * `GssFilter`: compliles CSS using the Google Closure Stylesheets Compiler
+ * `HandlebarsFilter`: compiles Handlebars templates into Javascript
+ * `JpegoptimFilter`: optimize your JPEGs
+ * `JpegtranFilter`: optimize your JPEGs
+ * `JSMinFilter`: minifies Javascript
+ * `JSMinPlusFilter`: minifies Javascript
+ * `JSqueezeFilter`: compresses Javascript
+ * `LessFilter`: parses LESS into CSS (using less.js with node.js)
+ * `LessphpFilter`: parses LESS into CSS (using lessphp)
+ * `OptiPngFilter`: optimize your PNGs
+ * `PackagerFilter`: parses Javascript for packager tags
+ * `PackerFilter`: compresses Javascript using Dean Edwards's Packer
+ * `PhpCssEmbedFilter`: embeds image data in your stylesheet
+ * `PngoutFilter`: optimize your PNGs
+ * `ReactJsxFilter`: compiles React JSX into JavaScript
+ * `Sass\SassFilter`: parses SASS into CSS
+ * `Sass\ScssFilter`: parses SCSS into CSS
+ * `SassphpFilter`: parses Sass into CSS using the sassphp bindings for Libsass
+ * `ScssphpFilter`: parses SCSS using scssphp
+ * `SeparatorFilter`: inserts a separator between assets to prevent merge failures
+ * `SprocketsFilter`: Sprockets Javascript dependency management
+ * `StylusFilter`: parses STYL into CSS
+ * `TypeScriptFilter`: parses TypeScript into Javascript
+ * `UglifyCssFilter`: minifies CSS
+ * `UglifyJs2Filter`: minifies Javascript
+ * `UglifyJsFilter`: minifies Javascript
+ * `Yui\CssCompressorFilter`: compresses CSS using the YUI compressor
+ * `Yui\JsCompressorFilter`: compresses Javascript using the YUI compressor
+
+Asset Manager
+-------------
+
+An asset manager is provided for organizing assets.
+
+``` php
+set('jquery', new FileAsset('/path/to/jquery.js'));
+$am->set('base_css', new GlobAsset('/path/to/css/*'));
+```
+
+The asset manager can also be used to reference assets to avoid duplication.
+
+``` php
+set('my_plugin', new AssetCollection(array(
+ new AssetReference($am, 'jquery'),
+ new FileAsset('/path/to/jquery.plugin.js'),
+)));
+```
+
+Filter Manager
+--------------
+
+A filter manager is also provided for organizing filters.
+
+``` php
+set('sass', new SassFilter('/path/to/parser/sass'));
+$fm->set('yui_css', new Yui\CssCompressorFilter('/path/to/yuicompressor.jar'));
+```
+
+Asset Factory
+-------------
+
+If you'd rather not create all these objects by hand, you can use the asset
+factory, which will do most of the work for you.
+
+``` php
+setAssetManager($am);
+$factory->setFilterManager($fm);
+$factory->setDebug(true);
+
+$css = $factory->createAsset(array(
+ '@reset', // load the asset manager's "reset" asset
+ 'css/src/*.scss', // load every scss files from "/path/to/asset/directory/css/src/"
+), array(
+ 'scss', // filter through the filter manager's "scss" filter
+ '?yui_css', // don't use this filter in debug mode
+));
+
+echo $css->dump();
+```
+
+The `AssetFactory` is constructed with a root directory which is used as the base directory for relative asset paths.
+
+Prefixing a filter name with a question mark, as `yui_css` is here, will cause
+that filter to be omitted when the factory is in debug mode.
+
+You can also register [Workers](src/Assetic/Factory/Worker/WorkerInterface.php) on the factory and all assets created
+by it will be passed to the worker's `process()` method before being returned. See _Cache Busting_ below for an example.
+
+Dumping Assets to static files
+------------------------------
+
+You can dump all the assets an AssetManager holds to files in a directory. This will probably be below your webserver's document root
+so the files can be served statically.
+
+``` php
+writeManagerAssets($am);
+```
+
+This will make use of the assets' target path.
+
+Cache Busting
+-------------
+
+If you serve your assets from static files as just described, you can use the CacheBustingWorker to rewrite the target
+paths for assets. It will insert an identifier before the filename extension that is unique for a particular version
+of the asset.
+
+This identifier is based on the modification time of the asset and will also take depended-on assets into
+consideration if the applied filters support it.
+
+``` php
+setAssetManager($am);
+$factory->setFilterManager($fm);
+$factory->setDebug(true);
+$factory->addWorker(new CacheBustingWorker());
+
+$css = $factory->createAsset(array(
+ '@reset', // load the asset manager's "reset" asset
+ 'css/src/*.scss', // load every scss files from "/path/to/asset/directory/css/src/"
+), array(
+ 'scss', // filter through the filter manager's "scss" filter
+ '?yui_css', // don't use this filter in debug mode
+));
+
+echo $css->dump();
+```
+
+Internal caching
+-------
+
+A simple caching mechanism is provided to avoid unnecessary work.
+
+``` php
+dump();
+$js->dump();
+$js->dump();
+```
+
+Twig
+----
+
+To use the Assetic [Twig][3] extension you must register it to your Twig
+environment:
+
+``` php
+addExtension(new AsseticExtension($factory));
+```
+
+Once in place, the extension exposes a stylesheets and a javascripts tag with a syntax similar
+to what the asset factory uses:
+
+``` html+jinja
+{% stylesheets '/path/to/sass/main.sass' filter='sass,?yui_css' output='css/all.css' %}
+
+{% endstylesheets %}
+```
+
+This example will render one `link` element on the page that includes a URL
+where the filtered asset can be found.
+
+When the extension is in debug mode, this same tag will render multiple `link`
+elements, one for each asset referenced by the `css/src/*.sass` glob. The
+specified filters will still be applied, unless they are marked as optional
+using the `?` prefix.
+
+This behavior can also be triggered by setting a `debug` attribute on the tag:
+
+``` html+jinja
+{% stylesheets 'css/*' debug=true %} ... {% stylesheets %}
+```
+
+These assets need to be written to the web directory so these URLs don't
+return 404 errors.
+
+``` php
+setLoader('twig', new TwigFormulaLoader($twig));
+
+// loop through all your templates
+foreach ($templates as $template) {
+ $resource = new TwigResource($twigLoader, $template);
+ $am->addResource($resource, 'twig');
+}
+
+$writer = new AssetWriter('/path/to/web');
+$writer->writeManagerAssets($am);
+```
+
+---
+
+Assetic is based on the Python [webassets][1] library (available on
+[GitHub][2]).
+
+[1]: http://elsdoerfer.name/docs/webassets
+[2]: https://github.com/miracle2k/webassets
+[3]: http://twig.sensiolabs.org
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/composer.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/composer.json
new file mode 100644
index 0000000..80755d7
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/composer.json
@@ -0,0 +1,56 @@
+{
+ "name": "kriswallsmith/assetic",
+ "description": "Asset Management for PHP",
+ "keywords": [ "assets", "compression", "minification" ],
+ "homepage": "https://github.com/kriswallsmith/assetic",
+ "type": "library",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Kris Wallsmith",
+ "email": "kris.wallsmith@gmail.com",
+ "homepage": "http://kriswallsmith.net/"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.1",
+ "symfony/process": "~2.1|~3.0"
+ },
+ "conflict": {
+ "twig/twig": "<1.27"
+ },
+ "require-dev": {
+ "leafo/lessphp": "^0.3.7",
+ "leafo/scssphp": "~0.1",
+ "meenie/javascript-packer": "^1.1",
+ "mrclay/minify": "<2.3",
+ "natxet/cssmin": "3.0.4",
+ "patchwork/jsqueeze": "~1.0|~2.0",
+ "phpunit/phpunit": "~4.8 || ^5.6",
+ "psr/log": "~1.0",
+ "ptachoire/cssembed": "~1.0",
+ "symfony/phpunit-bridge": "~2.7|~3.0",
+ "twig/twig": "~1.23|~2.0",
+ "yfix/packager": "dev-master"
+ },
+ "suggest": {
+ "twig/twig": "Assetic provides the integration with the Twig templating engine",
+ "leafo/lessphp": "Assetic provides the integration with the lessphp LESS compiler",
+ "leafo/scssphp": "Assetic provides the integration with the scssphp SCSS compiler",
+ "ptachoire/cssembed": "Assetic provides the integration with phpcssembed to embed data uris",
+ "leafo/scssphp-compass": "Assetic provides the integration with the SCSS compass plugin",
+ "patchwork/jsqueeze": "Assetic provides the integration with the JSqueeze JavaScript compressor"
+ },
+ "autoload": {
+ "psr-0": { "Assetic": "src/" },
+ "files": [ "src/functions.php" ]
+ },
+ "config": {
+ "bin-dir": "bin"
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.4-dev"
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/package.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/package.json
new file mode 100644
index 0000000..5369739
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/package.json
@@ -0,0 +1,19 @@
+{
+ "devDependencies": {
+ "uglifycss": "*",
+ "coffee-script": "*",
+ "stylus": "*",
+ "nib": "*",
+ "ember-precompile": "*",
+ "typescript": "*",
+ "less": "*",
+ "handlebars": "*",
+ "uglify-js": "*",
+ "autoprefixer": "*",
+ "autoprefixer-5": "^1.x",
+ "autoprefixer-cli": "*",
+ "roole": "*",
+ "react-tools": "*",
+ "clean-css": "*"
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCache.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCache.php
new file mode 100644
index 0000000..7ce62b5
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCache.php
@@ -0,0 +1,174 @@
+
+ */
+class AssetCache implements AssetInterface
+{
+ private $asset;
+ private $cache;
+
+ public function __construct(AssetInterface $asset, CacheInterface $cache)
+ {
+ $this->asset = $asset;
+ $this->cache = $cache;
+ }
+
+ public function ensureFilter(FilterInterface $filter)
+ {
+ $this->asset->ensureFilter($filter);
+ }
+
+ public function getFilters()
+ {
+ return $this->asset->getFilters();
+ }
+
+ public function clearFilters()
+ {
+ $this->asset->clearFilters();
+ }
+
+ public function load(FilterInterface $additionalFilter = null)
+ {
+ $cacheKey = self::getCacheKey($this->asset, $additionalFilter, 'load');
+ if ($this->cache->has($cacheKey)) {
+ $this->asset->setContent($this->cache->get($cacheKey));
+
+ return;
+ }
+
+ $this->asset->load($additionalFilter);
+ $this->cache->set($cacheKey, $this->asset->getContent());
+ }
+
+ public function dump(FilterInterface $additionalFilter = null)
+ {
+ $cacheKey = self::getCacheKey($this->asset, $additionalFilter, 'dump');
+ if ($this->cache->has($cacheKey)) {
+ return $this->cache->get($cacheKey);
+ }
+
+ $content = $this->asset->dump($additionalFilter);
+ $this->cache->set($cacheKey, $content);
+
+ return $content;
+ }
+
+ public function getContent()
+ {
+ return $this->asset->getContent();
+ }
+
+ public function setContent($content)
+ {
+ $this->asset->setContent($content);
+ }
+
+ public function getSourceRoot()
+ {
+ return $this->asset->getSourceRoot();
+ }
+
+ public function getSourcePath()
+ {
+ return $this->asset->getSourcePath();
+ }
+
+ public function getSourceDirectory()
+ {
+ return $this->asset->getSourceDirectory();
+ }
+
+ public function getTargetPath()
+ {
+ return $this->asset->getTargetPath();
+ }
+
+ public function setTargetPath($targetPath)
+ {
+ $this->asset->setTargetPath($targetPath);
+ }
+
+ public function getLastModified()
+ {
+ return $this->asset->getLastModified();
+ }
+
+ public function getVars()
+ {
+ return $this->asset->getVars();
+ }
+
+ public function setValues(array $values)
+ {
+ $this->asset->setValues($values);
+ }
+
+ public function getValues()
+ {
+ return $this->asset->getValues();
+ }
+
+ /**
+ * Returns a cache key for the current asset.
+ *
+ * The key is composed of everything but an asset's content:
+ *
+ * * source root
+ * * source path
+ * * target url
+ * * last modified
+ * * filters
+ *
+ * @param AssetInterface $asset The asset
+ * @param FilterInterface $additionalFilter Any additional filter being applied
+ * @param string $salt Salt for the key
+ *
+ * @return string A key for identifying the current asset
+ */
+ private static function getCacheKey(AssetInterface $asset, FilterInterface $additionalFilter = null, $salt = '')
+ {
+ if ($additionalFilter) {
+ $asset = clone $asset;
+ $asset->ensureFilter($additionalFilter);
+ }
+
+ $cacheKey = $asset->getSourceRoot();
+ $cacheKey .= $asset->getSourcePath();
+ $cacheKey .= $asset->getTargetPath();
+ $cacheKey .= $asset->getLastModified();
+
+ foreach ($asset->getFilters() as $filter) {
+ if ($filter instanceof HashableInterface) {
+ $cacheKey .= $filter->hash();
+ } else {
+ $cacheKey .= serialize($filter);
+ }
+ }
+
+ if ($values = $asset->getValues()) {
+ asort($values);
+ $cacheKey .= serialize($values);
+ }
+
+ return md5($cacheKey.$salt);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCollection.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCollection.php
new file mode 100644
index 0000000..3141af5
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCollection.php
@@ -0,0 +1,238 @@
+
+ */
+class AssetCollection implements \IteratorAggregate, AssetCollectionInterface
+{
+ private $assets;
+ private $filters;
+ private $sourceRoot;
+ private $targetPath;
+ private $content;
+ private $clones;
+ private $vars;
+ private $values;
+
+ /**
+ * Constructor.
+ *
+ * @param array $assets Assets for the current collection
+ * @param array $filters Filters for the current collection
+ * @param string $sourceRoot The root directory
+ * @param array $vars
+ */
+ public function __construct($assets = array(), $filters = array(), $sourceRoot = null, array $vars = array())
+ {
+ $this->assets = array();
+ foreach ($assets as $asset) {
+ $this->add($asset);
+ }
+
+ $this->filters = new FilterCollection($filters);
+ $this->sourceRoot = $sourceRoot;
+ $this->clones = new \SplObjectStorage();
+ $this->vars = $vars;
+ $this->values = array();
+ }
+
+ public function __clone()
+ {
+ $this->filters = clone $this->filters;
+ $this->clones = new \SplObjectStorage();
+ }
+
+ public function all()
+ {
+ return $this->assets;
+ }
+
+ public function add(AssetInterface $asset)
+ {
+ $this->assets[] = $asset;
+ }
+
+ public function removeLeaf(AssetInterface $needle, $graceful = false)
+ {
+ foreach ($this->assets as $i => $asset) {
+ $clone = isset($this->clones[$asset]) ? $this->clones[$asset] : null;
+ if (in_array($needle, array($asset, $clone), true)) {
+ unset($this->clones[$asset], $this->assets[$i]);
+
+ return true;
+ }
+
+ if ($asset instanceof AssetCollectionInterface && $asset->removeLeaf($needle, true)) {
+ return true;
+ }
+ }
+
+ if ($graceful) {
+ return false;
+ }
+
+ throw new \InvalidArgumentException('Leaf not found.');
+ }
+
+ public function replaceLeaf(AssetInterface $needle, AssetInterface $replacement, $graceful = false)
+ {
+ foreach ($this->assets as $i => $asset) {
+ $clone = isset($this->clones[$asset]) ? $this->clones[$asset] : null;
+ if (in_array($needle, array($asset, $clone), true)) {
+ unset($this->clones[$asset]);
+ $this->assets[$i] = $replacement;
+
+ return true;
+ }
+
+ if ($asset instanceof AssetCollectionInterface && $asset->replaceLeaf($needle, $replacement, true)) {
+ return true;
+ }
+ }
+
+ if ($graceful) {
+ return false;
+ }
+
+ throw new \InvalidArgumentException('Leaf not found.');
+ }
+
+ public function ensureFilter(FilterInterface $filter)
+ {
+ $this->filters->ensure($filter);
+ }
+
+ public function getFilters()
+ {
+ return $this->filters->all();
+ }
+
+ public function clearFilters()
+ {
+ $this->filters->clear();
+ $this->clones = new \SplObjectStorage();
+ }
+
+ public function load(FilterInterface $additionalFilter = null)
+ {
+ // loop through leaves and load each asset
+ $parts = array();
+ foreach ($this as $asset) {
+ $asset->load($additionalFilter);
+ $parts[] = $asset->getContent();
+ }
+
+ $this->content = implode("\n", $parts);
+ }
+
+ public function dump(FilterInterface $additionalFilter = null)
+ {
+ // loop through leaves and dump each asset
+ $parts = array();
+ foreach ($this as $asset) {
+ $parts[] = $asset->dump($additionalFilter);
+ }
+
+ return implode("\n", $parts);
+ }
+
+ public function getContent()
+ {
+ return $this->content;
+ }
+
+ public function setContent($content)
+ {
+ $this->content = $content;
+ }
+
+ public function getSourceRoot()
+ {
+ return $this->sourceRoot;
+ }
+
+ public function getSourcePath()
+ {
+ }
+
+ public function getSourceDirectory()
+ {
+ }
+
+ public function getTargetPath()
+ {
+ return $this->targetPath;
+ }
+
+ public function setTargetPath($targetPath)
+ {
+ $this->targetPath = $targetPath;
+ }
+
+ /**
+ * Returns the highest last-modified value of all assets in the current collection.
+ *
+ * @return integer|null A UNIX timestamp
+ */
+ public function getLastModified()
+ {
+ if (!count($this->assets)) {
+ return;
+ }
+
+ $mtime = 0;
+ foreach ($this as $asset) {
+ $assetMtime = $asset->getLastModified();
+ if ($assetMtime > $mtime) {
+ $mtime = $assetMtime;
+ }
+ }
+
+ return $mtime;
+ }
+
+ /**
+ * Returns an iterator for looping recursively over unique leaves.
+ */
+ public function getIterator()
+ {
+ return new \RecursiveIteratorIterator(new AssetCollectionFilterIterator(new AssetCollectionIterator($this, $this->clones)));
+ }
+
+ public function getVars()
+ {
+ return $this->vars;
+ }
+
+ public function setValues(array $values)
+ {
+ $this->values = $values;
+
+ foreach ($this as $asset) {
+ $asset->setValues(array_intersect_key($values, array_flip($asset->getVars())));
+ }
+ }
+
+ public function getValues()
+ {
+ return $this->values;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCollectionInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCollectionInterface.php
new file mode 100644
index 0000000..f029d1b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCollectionInterface.php
@@ -0,0 +1,59 @@
+
+ */
+interface AssetCollectionInterface extends AssetInterface, \Traversable
+{
+ /**
+ * Returns all child assets.
+ *
+ * @return array An array of AssetInterface objects
+ */
+ public function all();
+
+ /**
+ * Adds an asset to the current collection.
+ *
+ * @param AssetInterface $asset An asset
+ */
+ public function add(AssetInterface $asset);
+
+ /**
+ * Removes a leaf.
+ *
+ * @param AssetInterface $leaf The leaf to remove
+ * @param Boolean $graceful Whether the failure should return false or throw an exception
+ *
+ * @return Boolean Whether the asset has been found
+ *
+ * @throws \InvalidArgumentException If the asset cannot be found
+ */
+ public function removeLeaf(AssetInterface $leaf, $graceful = false);
+
+ /**
+ * Replaces an existing leaf with a new one.
+ *
+ * @param AssetInterface $needle The current asset to replace
+ * @param AssetInterface $replacement The new asset
+ * @param Boolean $graceful Whether the failure should return false or throw an exception
+ *
+ * @return Boolean Whether the asset has been found
+ *
+ * @throws \InvalidArgumentException If the asset cannot be found
+ */
+ public function replaceLeaf(AssetInterface $needle, AssetInterface $replacement, $graceful = false);
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetInterface.php
new file mode 100644
index 0000000..b1d655c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetInterface.php
@@ -0,0 +1,166 @@
+
+ */
+interface AssetInterface
+{
+ /**
+ * Ensures the current asset includes the supplied filter.
+ *
+ * @param FilterInterface $filter A filter
+ */
+ public function ensureFilter(FilterInterface $filter);
+
+ /**
+ * Returns an array of filters currently applied.
+ *
+ * @return array An array of filters
+ */
+ public function getFilters();
+
+ /**
+ * Clears all filters from the current asset.
+ */
+ public function clearFilters();
+
+ /**
+ * Loads the asset into memory and applies load filters.
+ *
+ * You may provide an additional filter to apply during load.
+ *
+ * @param FilterInterface $additionalFilter An additional filter
+ */
+ public function load(FilterInterface $additionalFilter = null);
+
+ /**
+ * Applies dump filters and returns the asset as a string.
+ *
+ * You may provide an additional filter to apply during dump.
+ *
+ * Dumping an asset should not change its state.
+ *
+ * If the current asset has not been loaded yet, it should be
+ * automatically loaded at this time.
+ *
+ * @param FilterInterface $additionalFilter An additional filter
+ *
+ * @return string The filtered content of the current asset
+ */
+ public function dump(FilterInterface $additionalFilter = null);
+
+ /**
+ * Returns the loaded content of the current asset.
+ *
+ * @return string The content
+ */
+ public function getContent();
+
+ /**
+ * Sets the content of the current asset.
+ *
+ * Filters can use this method to change the content of the asset.
+ *
+ * @param string $content The asset content
+ */
+ public function setContent($content);
+
+ /**
+ * Returns an absolute path or URL to the source asset's root directory.
+ *
+ * This value should be an absolute path to a directory in the filesystem,
+ * an absolute URL with no path, or null.
+ *
+ * For example:
+ *
+ * * '/path/to/web'
+ * * 'http://example.com'
+ * * null
+ *
+ * @return string|null The asset's root
+ */
+ public function getSourceRoot();
+
+ /**
+ * Returns the relative path for the source asset.
+ *
+ * This value can be combined with the asset's source root (if both are
+ * non-null) to get something compatible with file_get_contents().
+ *
+ * For example:
+ *
+ * * 'js/main.js'
+ * * 'main.js'
+ * * null
+ *
+ * @return string|null The source asset path
+ */
+ public function getSourcePath();
+
+ /**
+ * Returns the asset's source directory.
+ *
+ * The source directory is the directory the asset was located in
+ * and can be used to resolve references relative to an asset.
+ *
+ * @return string|null The asset's source directory
+ */
+ public function getSourceDirectory();
+
+ /**
+ * Returns the URL for the current asset.
+ *
+ * @return string|null A web URL where the asset will be dumped
+ */
+ public function getTargetPath();
+
+ /**
+ * Sets the URL for the current asset.
+ *
+ * @param string $targetPath A web URL where the asset will be dumped
+ */
+ public function setTargetPath($targetPath);
+
+ /**
+ * Returns the time the current asset was last modified.
+ *
+ * @return integer|null A UNIX timestamp
+ */
+ public function getLastModified();
+
+ /**
+ * Returns an array of variable names for this asset.
+ *
+ * @return array
+ */
+ public function getVars();
+
+ /**
+ * Sets the values for the asset's variables.
+ *
+ * @param array $values
+ */
+ public function setValues(array $values);
+
+ /**
+ * Returns the current values for this asset.
+ *
+ * @return array an array of strings
+ */
+ public function getValues();
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetReference.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetReference.php
new file mode 100644
index 0000000..f66b769
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetReference.php
@@ -0,0 +1,164 @@
+
+ */
+class AssetReference implements AssetInterface
+{
+ private $am;
+ private $name;
+ private $filters = array();
+ private $clone = false;
+ private $asset;
+
+ public function __construct(AssetManager $am, $name)
+ {
+ $this->am = $am;
+ $this->name = $name;
+ }
+
+ public function __clone()
+ {
+ $this->clone = true;
+
+ if ($this->asset) {
+ $this->asset = clone $this->asset;
+ }
+ }
+
+ public function ensureFilter(FilterInterface $filter)
+ {
+ $this->filters[] = $filter;
+ }
+
+ public function getFilters()
+ {
+ $this->flushFilters();
+
+ return $this->callAsset(__FUNCTION__);
+ }
+
+ public function clearFilters()
+ {
+ $this->filters = array();
+ $this->callAsset(__FUNCTION__);
+ }
+
+ public function load(FilterInterface $additionalFilter = null)
+ {
+ $this->flushFilters();
+
+ return $this->callAsset(__FUNCTION__, array($additionalFilter));
+ }
+
+ public function dump(FilterInterface $additionalFilter = null)
+ {
+ $this->flushFilters();
+
+ return $this->callAsset(__FUNCTION__, array($additionalFilter));
+ }
+
+ public function getContent()
+ {
+ return $this->callAsset(__FUNCTION__);
+ }
+
+ public function setContent($content)
+ {
+ $this->callAsset(__FUNCTION__, array($content));
+ }
+
+ public function getSourceRoot()
+ {
+ return $this->callAsset(__FUNCTION__);
+ }
+
+ public function getSourcePath()
+ {
+ return $this->callAsset(__FUNCTION__);
+ }
+
+ public function getSourceDirectory()
+ {
+ return $this->callAsset(__FUNCTION__);
+ }
+
+ public function getTargetPath()
+ {
+ return $this->callAsset(__FUNCTION__);
+ }
+
+ public function setTargetPath($targetPath)
+ {
+ $this->callAsset(__FUNCTION__, array($targetPath));
+ }
+
+ public function getLastModified()
+ {
+ return $this->callAsset(__FUNCTION__);
+ }
+
+ public function getVars()
+ {
+ return $this->callAsset(__FUNCTION__);
+ }
+
+ public function getValues()
+ {
+ return $this->callAsset(__FUNCTION__);
+ }
+
+ public function setValues(array $values)
+ {
+ $this->callAsset(__FUNCTION__, array($values));
+ }
+
+ // private
+
+ private function callAsset($method, $arguments = array())
+ {
+ $asset = $this->resolve();
+
+ return call_user_func_array(array($asset, $method), $arguments);
+ }
+
+ private function flushFilters()
+ {
+ $asset = $this->resolve();
+
+ while ($filter = array_shift($this->filters)) {
+ $asset->ensureFilter($filter);
+ }
+ }
+
+ private function resolve()
+ {
+ if ($this->asset) {
+ return $this->asset;
+ }
+
+ $asset = $this->am->get($this->name);
+
+ if ($this->clone) {
+ $asset = $this->asset = clone $asset;
+ }
+
+ return $asset;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/BaseAsset.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/BaseAsset.php
new file mode 100644
index 0000000..093b92a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/BaseAsset.php
@@ -0,0 +1,181 @@
+
+ */
+abstract class BaseAsset implements AssetInterface
+{
+ private $filters;
+ private $sourceRoot;
+ private $sourcePath;
+ private $sourceDir;
+ private $targetPath;
+ private $content;
+ private $loaded;
+ private $vars;
+ private $values;
+
+ /**
+ * Constructor.
+ *
+ * @param array $filters Filters for the asset
+ * @param string $sourceRoot The root directory
+ * @param string $sourcePath The asset path
+ * @param array $vars
+ */
+ public function __construct($filters = array(), $sourceRoot = null, $sourcePath = null, array $vars = array())
+ {
+ $this->filters = new FilterCollection($filters);
+ $this->sourceRoot = $sourceRoot;
+ $this->sourcePath = $sourcePath;
+ if ($sourcePath && $sourceRoot) {
+ $this->sourceDir = dirname("$sourceRoot/$sourcePath");
+ }
+ $this->vars = $vars;
+ $this->values = array();
+ $this->loaded = false;
+ }
+
+ public function __clone()
+ {
+ $this->filters = clone $this->filters;
+ }
+
+ public function ensureFilter(FilterInterface $filter)
+ {
+ $this->filters->ensure($filter);
+ }
+
+ public function getFilters()
+ {
+ return $this->filters->all();
+ }
+
+ public function clearFilters()
+ {
+ $this->filters->clear();
+ }
+
+ /**
+ * Encapsulates asset loading logic.
+ *
+ * @param string $content The asset content
+ * @param FilterInterface $additionalFilter An additional filter
+ */
+ protected function doLoad($content, FilterInterface $additionalFilter = null)
+ {
+ $filter = clone $this->filters;
+ if ($additionalFilter) {
+ $filter->ensure($additionalFilter);
+ }
+
+ $asset = clone $this;
+ $asset->setContent($content);
+
+ $filter->filterLoad($asset);
+ $this->content = $asset->getContent();
+
+ $this->loaded = true;
+ }
+
+ public function dump(FilterInterface $additionalFilter = null)
+ {
+ if (!$this->loaded) {
+ $this->load();
+ }
+
+ $filter = clone $this->filters;
+ if ($additionalFilter) {
+ $filter->ensure($additionalFilter);
+ }
+
+ $asset = clone $this;
+ $filter->filterDump($asset);
+
+ return $asset->getContent();
+ }
+
+ public function getContent()
+ {
+ return $this->content;
+ }
+
+ public function setContent($content)
+ {
+ $this->content = $content;
+ }
+
+ public function getSourceRoot()
+ {
+ return $this->sourceRoot;
+ }
+
+ public function getSourcePath()
+ {
+ return $this->sourcePath;
+ }
+
+ public function getSourceDirectory()
+ {
+ return $this->sourceDir;
+ }
+
+ public function getTargetPath()
+ {
+ return $this->targetPath;
+ }
+
+ public function setTargetPath($targetPath)
+ {
+ if ($this->vars) {
+ foreach ($this->vars as $var) {
+ if (false === strpos($targetPath, $var)) {
+ throw new \RuntimeException(sprintf('The asset target path "%s" must contain the variable "{%s}".', $targetPath, $var));
+ }
+ }
+ }
+
+ $this->targetPath = $targetPath;
+ }
+
+ public function getVars()
+ {
+ return $this->vars;
+ }
+
+ public function setValues(array $values)
+ {
+ foreach ($values as $var => $v) {
+ if (!in_array($var, $this->vars, true)) {
+ throw new \InvalidArgumentException(sprintf('The asset with source path "%s" has no variable named "%s".', $this->sourcePath, $var));
+ }
+ }
+
+ $this->values = $values;
+ $this->loaded = false;
+ }
+
+ public function getValues()
+ {
+ return $this->values;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/FileAsset.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/FileAsset.php
new file mode 100644
index 0000000..2a33e08
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/FileAsset.php
@@ -0,0 +1,78 @@
+
+ */
+class FileAsset extends BaseAsset
+{
+ private $source;
+
+ /**
+ * Constructor.
+ *
+ * @param string $source An absolute path
+ * @param array $filters An array of filters
+ * @param string $sourceRoot The source asset root directory
+ * @param string $sourcePath The source asset path
+ * @param array $vars
+ *
+ * @throws \InvalidArgumentException If the supplied root doesn't match the source when guessing the path
+ */
+ public function __construct($source, $filters = array(), $sourceRoot = null, $sourcePath = null, array $vars = array())
+ {
+ if (null === $sourceRoot) {
+ $sourceRoot = dirname($source);
+ if (null === $sourcePath) {
+ $sourcePath = basename($source);
+ }
+ } elseif (null === $sourcePath) {
+ if (0 !== strpos($source, $sourceRoot)) {
+ throw new \InvalidArgumentException(sprintf('The source "%s" is not in the root directory "%s"', $source, $sourceRoot));
+ }
+
+ $sourcePath = substr($source, strlen($sourceRoot) + 1);
+ }
+
+ $this->source = $source;
+
+ parent::__construct($filters, $sourceRoot, $sourcePath, $vars);
+ }
+
+ public function load(FilterInterface $additionalFilter = null)
+ {
+ $source = VarUtils::resolve($this->source, $this->getVars(), $this->getValues());
+
+ if (!is_file($source)) {
+ throw new \RuntimeException(sprintf('The source file "%s" does not exist.', $source));
+ }
+
+ $this->doLoad(file_get_contents($source), $additionalFilter);
+ }
+
+ public function getLastModified()
+ {
+ $source = VarUtils::resolve($this->source, $this->getVars(), $this->getValues());
+
+ if (!is_file($source)) {
+ throw new \RuntimeException(sprintf('The source file "%s" does not exist.', $source));
+ }
+
+ return filemtime($source);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/GlobAsset.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/GlobAsset.php
new file mode 100644
index 0000000..6444e61
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/GlobAsset.php
@@ -0,0 +1,115 @@
+
+ */
+class GlobAsset extends AssetCollection
+{
+ private $globs;
+ private $initialized;
+
+ /**
+ * Constructor.
+ *
+ * @param string|array $globs A single glob path or array of paths
+ * @param array $filters An array of filters
+ * @param string $root The root directory
+ * @param array $vars
+ */
+ public function __construct($globs, $filters = array(), $root = null, array $vars = array())
+ {
+ $this->globs = (array) $globs;
+ $this->initialized = false;
+
+ parent::__construct(array(), $filters, $root, $vars);
+ }
+
+ public function all()
+ {
+ if (!$this->initialized) {
+ $this->initialize();
+ }
+
+ return parent::all();
+ }
+
+ public function load(FilterInterface $additionalFilter = null)
+ {
+ if (!$this->initialized) {
+ $this->initialize();
+ }
+
+ parent::load($additionalFilter);
+ }
+
+ public function dump(FilterInterface $additionalFilter = null)
+ {
+ if (!$this->initialized) {
+ $this->initialize();
+ }
+
+ return parent::dump($additionalFilter);
+ }
+
+ public function getLastModified()
+ {
+ if (!$this->initialized) {
+ $this->initialize();
+ }
+
+ return parent::getLastModified();
+ }
+
+ public function getIterator()
+ {
+ if (!$this->initialized) {
+ $this->initialize();
+ }
+
+ return parent::getIterator();
+ }
+
+ public function setValues(array $values)
+ {
+ parent::setValues($values);
+ $this->initialized = false;
+ }
+
+ /**
+ * Initializes the collection based on the glob(s) passed in.
+ */
+ private function initialize()
+ {
+ foreach ($this->globs as $glob) {
+ $glob = VarUtils::resolve($glob, $this->getVars(), $this->getValues());
+
+ if (false !== $paths = glob($glob)) {
+ foreach ($paths as $path) {
+ if (is_file($path)) {
+ $asset = new FileAsset($path, array(), $this->getSourceRoot(), null, $this->getVars());
+ $asset->setValues($this->getValues());
+ $this->add($asset);
+ }
+ }
+ }
+ }
+
+ $this->initialized = true;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/HttpAsset.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/HttpAsset.php
new file mode 100644
index 0000000..cd56761
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/HttpAsset.php
@@ -0,0 +1,79 @@
+
+ */
+class HttpAsset extends BaseAsset
+{
+ private $sourceUrl;
+ private $ignoreErrors;
+
+ /**
+ * Constructor.
+ *
+ * @param string $sourceUrl The source URL
+ * @param array $filters An array of filters
+ * @param Boolean $ignoreErrors
+ * @param array $vars
+ *
+ * @throws \InvalidArgumentException If the first argument is not an URL
+ */
+ public function __construct($sourceUrl, $filters = array(), $ignoreErrors = false, array $vars = array())
+ {
+ if (0 === strpos($sourceUrl, '//')) {
+ $sourceUrl = 'http:'.$sourceUrl;
+ } elseif (false === strpos($sourceUrl, '://')) {
+ throw new \InvalidArgumentException(sprintf('"%s" is not a valid URL.', $sourceUrl));
+ }
+
+ $this->sourceUrl = $sourceUrl;
+ $this->ignoreErrors = $ignoreErrors;
+
+ list($scheme, $url) = explode('://', $sourceUrl, 2);
+ list($host, $path) = explode('/', $url, 2);
+
+ parent::__construct($filters, $scheme.'://'.$host, $path, $vars);
+ }
+
+ public function load(FilterInterface $additionalFilter = null)
+ {
+ $content = @file_get_contents(
+ VarUtils::resolve($this->sourceUrl, $this->getVars(), $this->getValues())
+ );
+
+ if (false === $content && !$this->ignoreErrors) {
+ throw new \RuntimeException(sprintf('Unable to load asset from URL "%s"', $this->sourceUrl));
+ }
+
+ $this->doLoad($content, $additionalFilter);
+ }
+
+ public function getLastModified()
+ {
+ if (false !== @file_get_contents($this->sourceUrl, false, stream_context_create(array('http' => array('method' => 'HEAD'))))) {
+ foreach ($http_response_header as $header) {
+ if (0 === stripos($header, 'Last-Modified: ')) {
+ list(, $mtime) = explode(':', $header, 2);
+
+ return strtotime(trim($mtime));
+ }
+ }
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/Iterator/AssetCollectionFilterIterator.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/Iterator/AssetCollectionFilterIterator.php
new file mode 100644
index 0000000..fae5d13
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/Iterator/AssetCollectionFilterIterator.php
@@ -0,0 +1,84 @@
+
+ */
+class AssetCollectionFilterIterator extends \RecursiveFilterIterator
+{
+ private $visited;
+ private $sources;
+
+ /**
+ * Constructor.
+ *
+ * @param AssetCollectionIterator $iterator The inner iterator
+ * @param array $visited An array of visited asset objects
+ * @param array $sources An array of visited source strings
+ */
+ public function __construct(AssetCollectionIterator $iterator, array $visited = array(), array $sources = array())
+ {
+ parent::__construct($iterator);
+
+ $this->visited = $visited;
+ $this->sources = $sources;
+ }
+
+ /**
+ * Determines whether the current asset is a duplicate.
+ *
+ * De-duplication is performed based on either strict equality or by
+ * matching sources.
+ *
+ * @return Boolean Returns true if we have not seen this asset yet
+ */
+ public function accept()
+ {
+ $asset = $this->getInnerIterator()->current(true);
+ $duplicate = false;
+
+ // check strict equality
+ if (in_array($asset, $this->visited, true)) {
+ $duplicate = true;
+ } else {
+ $this->visited[] = $asset;
+ }
+
+ // check source
+ $sourceRoot = $asset->getSourceRoot();
+ $sourcePath = $asset->getSourcePath();
+ if ($sourceRoot && $sourcePath) {
+ $source = $sourceRoot.'/'.$sourcePath;
+ if (in_array($source, $this->sources)) {
+ $duplicate = true;
+ } else {
+ $this->sources[] = $source;
+ }
+ }
+
+ return !$duplicate;
+ }
+
+ /**
+ * Passes visited objects and source URLs to the child iterator.
+ */
+ public function getChildren()
+ {
+ return new self($this->getInnerIterator()->getChildren(), $this->visited, $this->sources);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/Iterator/AssetCollectionIterator.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/Iterator/AssetCollectionIterator.php
new file mode 100644
index 0000000..e4cf128
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/Iterator/AssetCollectionIterator.php
@@ -0,0 +1,128 @@
+
+ */
+class AssetCollectionIterator implements \RecursiveIterator
+{
+ private $assets;
+ private $filters;
+ private $vars;
+ private $output;
+ private $clones;
+
+ public function __construct(AssetCollectionInterface $coll, \SplObjectStorage $clones)
+ {
+ $this->assets = $coll->all();
+ $this->filters = $coll->getFilters();
+ $this->vars = $coll->getVars();
+ $this->output = $coll->getTargetPath();
+ $this->clones = $clones;
+
+ if (false === $pos = strrpos($this->output, '.')) {
+ $this->output .= '_*';
+ } else {
+ $this->output = substr($this->output, 0, $pos).'_*'.substr($this->output, $pos);
+ }
+ }
+
+ /**
+ * Returns a copy of the current asset with filters and a target URL applied.
+ *
+ * @param Boolean $raw Returns the unmodified asset if true
+ *
+ * @return \Assetic\Asset\AssetInterface
+ */
+ public function current($raw = false)
+ {
+ $asset = current($this->assets);
+
+ if ($raw) {
+ return $asset;
+ }
+
+ // clone once
+ if (!isset($this->clones[$asset])) {
+ $clone = $this->clones[$asset] = clone $asset;
+
+ // generate a target path based on asset name
+ $name = sprintf('%s_%d', pathinfo($asset->getSourcePath(), PATHINFO_FILENAME) ?: 'part', $this->key() + 1);
+
+ $name = $this->removeDuplicateVar($name);
+
+ $clone->setTargetPath(str_replace('*', $name, $this->output));
+ } else {
+ $clone = $this->clones[$asset];
+ }
+
+ // cascade filters
+ foreach ($this->filters as $filter) {
+ $clone->ensureFilter($filter);
+ }
+
+ return $clone;
+ }
+
+ public function key()
+ {
+ return key($this->assets);
+ }
+
+ public function next()
+ {
+ return next($this->assets);
+ }
+
+ public function rewind()
+ {
+ return reset($this->assets);
+ }
+
+ public function valid()
+ {
+ return false !== current($this->assets);
+ }
+
+ public function hasChildren()
+ {
+ return current($this->assets) instanceof AssetCollectionInterface;
+ }
+
+ /**
+ * @uses current()
+ */
+ public function getChildren()
+ {
+ return new self($this->current(), $this->clones);
+ }
+
+ private function removeDuplicateVar($name)
+ {
+ foreach ($this->vars as $var) {
+ $var = '{'.$var.'}';
+ if (false !== strpos($name, $var) && false !== strpos($this->output, $var)) {
+ $name = str_replace($var, '', $name);
+ }
+ }
+
+ return $name;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/StringAsset.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/StringAsset.php
new file mode 100644
index 0000000..d9b9a88
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Asset/StringAsset.php
@@ -0,0 +1,55 @@
+
+ */
+class StringAsset extends BaseAsset
+{
+ private $string;
+ private $lastModified;
+
+ /**
+ * Constructor.
+ *
+ * @param string $content The content of the asset
+ * @param array $filters Filters for the asset
+ * @param string $sourceRoot The source asset root directory
+ * @param string $sourcePath The source asset path
+ */
+ public function __construct($content, $filters = array(), $sourceRoot = null, $sourcePath = null)
+ {
+ $this->string = $content;
+
+ parent::__construct($filters, $sourceRoot, $sourcePath);
+ }
+
+ public function load(FilterInterface $additionalFilter = null)
+ {
+ $this->doLoad($this->string, $additionalFilter);
+ }
+
+ public function setLastModified($lastModified)
+ {
+ $this->lastModified = $lastModified;
+ }
+
+ public function getLastModified()
+ {
+ return $this->lastModified;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/AssetManager.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/AssetManager.php
new file mode 100644
index 0000000..9b8ee12
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/AssetManager.php
@@ -0,0 +1,89 @@
+
+ */
+class AssetManager
+{
+ private $assets = array();
+
+ /**
+ * Gets an asset by name.
+ *
+ * @param string $name The asset name
+ *
+ * @return AssetInterface The asset
+ *
+ * @throws \InvalidArgumentException If there is no asset by that name
+ */
+ public function get($name)
+ {
+ if (!isset($this->assets[$name])) {
+ throw new \InvalidArgumentException(sprintf('There is no "%s" asset.', $name));
+ }
+
+ return $this->assets[$name];
+ }
+
+ /**
+ * Checks if the current asset manager has a certain asset.
+ *
+ * @param string $name an asset name
+ *
+ * @return Boolean True if the asset has been set, false if not
+ */
+ public function has($name)
+ {
+ return isset($this->assets[$name]);
+ }
+
+ /**
+ * Registers an asset to the current asset manager.
+ *
+ * @param string $name The asset name
+ * @param AssetInterface $asset The asset
+ *
+ * @throws \InvalidArgumentException If the asset name is invalid
+ */
+ public function set($name, AssetInterface $asset)
+ {
+ if (!ctype_alnum(str_replace('_', '', $name))) {
+ throw new \InvalidArgumentException(sprintf('The name "%s" is invalid.', $name));
+ }
+
+ $this->assets[$name] = $asset;
+ }
+
+ /**
+ * Returns an array of asset names.
+ *
+ * @return array An array of asset names
+ */
+ public function getNames()
+ {
+ return array_keys($this->assets);
+ }
+
+ /**
+ * Clears all assets.
+ */
+ public function clear()
+ {
+ $this->assets = array();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/AssetWriter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/AssetWriter.php
new file mode 100644
index 0000000..4f010a4
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/AssetWriter.php
@@ -0,0 +1,94 @@
+
+ * @author Johannes M. Schmitt
+ */
+class AssetWriter
+{
+ private $dir;
+ private $values;
+
+ /**
+ * Constructor.
+ *
+ * @param string $dir The base web directory
+ * @param array $values Variable values
+ *
+ * @throws \InvalidArgumentException if a variable value is not a string
+ */
+ public function __construct($dir, array $values = array())
+ {
+ foreach ($values as $var => $vals) {
+ foreach ($vals as $value) {
+ if (!is_string($value)) {
+ throw new \InvalidArgumentException(sprintf('All variable values must be strings, but got %s for variable "%s".', json_encode($value), $var));
+ }
+ }
+ }
+
+ $this->dir = $dir;
+ $this->values = $values;
+ }
+
+ public function writeManagerAssets(AssetManager $am)
+ {
+ foreach ($am->getNames() as $name) {
+ $this->writeAsset($am->get($name));
+ }
+ }
+
+ public function writeAsset(AssetInterface $asset)
+ {
+ foreach (VarUtils::getCombinations($asset->getVars(), $this->values) as $combination) {
+ $asset->setValues($combination);
+
+ static::write(
+ $this->dir.'/'.VarUtils::resolve(
+ $asset->getTargetPath(),
+ $asset->getVars(),
+ $asset->getValues()
+ ),
+ $asset->dump()
+ );
+ }
+ }
+
+ protected static function write($path, $contents)
+ {
+ if (!is_dir($dir = dirname($path)) && false === @mkdir($dir, 0777, true)) {
+ throw new \RuntimeException('Unable to create directory '.$dir);
+ }
+
+ if (false === @file_put_contents($path, $contents)) {
+ throw new \RuntimeException('Unable to write file '.$path);
+ }
+ }
+
+ /**
+ * Not used.
+ *
+ * This method is provided for backward compatibility with certain versions
+ * of AsseticBundle.
+ */
+ private function getCombinations(array $vars)
+ {
+ return VarUtils::getCombinations($vars, $this->values);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Cache/ApcCache.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Cache/ApcCache.php
new file mode 100644
index 0000000..8c7aa11
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Cache/ApcCache.php
@@ -0,0 +1,66 @@
+
+ */
+class ApcCache implements CacheInterface
+{
+ public $ttl = 0;
+
+ /**
+ * @see CacheInterface::has()
+ */
+ public function has($key)
+ {
+ return apc_exists($key);
+ }
+
+ /**
+ * @see CacheInterface::get()
+ */
+ public function get($key)
+ {
+ $value = apc_fetch($key, $success);
+
+ if (!$success) {
+ throw new \RuntimeException('There is no cached value for '.$key);
+ }
+
+ return $value;
+ }
+
+ /**
+ * @see CacheInterface::set()
+ */
+ public function set($key, $value)
+ {
+ $store = apc_store($key, $value, $this->ttl);
+
+ if (!$store) {
+ throw new \RuntimeException('Unable to store "'.$key.'" for '.$this->ttl.' seconds.');
+ }
+
+ return $store;
+ }
+
+ /**
+ * @see CacheInterface::remove()
+ */
+ public function remove($key)
+ {
+ return apc_delete($key);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Cache/ArrayCache.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Cache/ArrayCache.php
new file mode 100644
index 0000000..7f357ac
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Cache/ArrayCache.php
@@ -0,0 +1,58 @@
+
+ */
+class ArrayCache implements CacheInterface
+{
+ private $cache = array();
+
+ /**
+ * @see CacheInterface::has()
+ */
+ public function has($key)
+ {
+ return isset($this->cache[$key]);
+ }
+
+ /**
+ * @see CacheInterface::get()
+ */
+ public function get($key)
+ {
+ if (!$this->has($key)) {
+ throw new \RuntimeException('There is no cached value for '.$key);
+ }
+
+ return $this->cache[$key];
+ }
+
+ /**
+ * @see CacheInterface::set()
+ */
+ public function set($key, $value)
+ {
+ $this->cache[$key] = $value;
+ }
+
+ /**
+ * @see CacheInterface::remove()
+ */
+ public function remove($key)
+ {
+ unset($this->cache[$key]);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Cache/CacheInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Cache/CacheInterface.php
new file mode 100644
index 0000000..be13310
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Cache/CacheInterface.php
@@ -0,0 +1,53 @@
+
+ */
+interface CacheInterface
+{
+ /**
+ * Checks if the cache has a value for a key.
+ *
+ * @param string $key A unique key
+ *
+ * @return Boolean Whether the cache has a value for this key
+ */
+ public function has($key);
+
+ /**
+ * Returns the value for a key.
+ *
+ * @param string $key A unique key
+ *
+ * @return string|null The value in the cache
+ */
+ public function get($key);
+
+ /**
+ * Sets a value in the cache.
+ *
+ * @param string $key A unique key
+ * @param string $value The value to cache
+ */
+ public function set($key, $value);
+
+ /**
+ * Removes a value from the cache.
+ *
+ * @param string $key A unique key
+ */
+ public function remove($key);
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Cache/ConfigCache.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Cache/ConfigCache.php
new file mode 100644
index 0000000..e285e0b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Cache/ConfigCache.php
@@ -0,0 +1,123 @@
+
+ */
+class ConfigCache
+{
+ private $dir;
+
+ /**
+ * Construct.
+ *
+ * @param string $dir The cache directory
+ */
+ public function __construct($dir)
+ {
+ $this->dir = $dir;
+ }
+
+ /**
+ * Checks of the cache has a file.
+ *
+ * @param string $resource A cache key
+ *
+ * @return Boolean True if a file exists
+ */
+ public function has($resource)
+ {
+ return file_exists($this->getSourcePath($resource));
+ }
+
+ /**
+ * Writes a value to a file.
+ *
+ * @param string $resource A cache key
+ * @param mixed $value A value to cache
+ */
+ public function set($resource, $value)
+ {
+ $path = $this->getSourcePath($resource);
+
+ if (!is_dir($dir = dirname($path)) && false === @mkdir($dir, 0777, true)) {
+ // @codeCoverageIgnoreStart
+ throw new \RuntimeException('Unable to create directory '.$dir);
+ // @codeCoverageIgnoreEnd
+ }
+
+ if (false === @file_put_contents($path, sprintf("getSourcePath($resource);
+
+ if (!file_exists($path)) {
+ throw new \RuntimeException('There is no cached value for '.$resource);
+ }
+
+ return include $path;
+ }
+
+ /**
+ * Returns a timestamp for when the cache was created.
+ *
+ * @param string $resource A cache key
+ *
+ * @return integer A UNIX timestamp
+ */
+ public function getTimestamp($resource)
+ {
+ $path = $this->getSourcePath($resource);
+
+ if (!file_exists($path)) {
+ throw new \RuntimeException('There is no cached value for '.$resource);
+ }
+
+ if (false === $mtime = @filemtime($path)) {
+ // @codeCoverageIgnoreStart
+ throw new \RuntimeException('Unable to determine file mtime for '.$path);
+ // @codeCoverageIgnoreEnd
+ }
+
+ return $mtime;
+ }
+
+ /**
+ * Returns the path where the file corresponding to the supplied cache key can be included from.
+ *
+ * @param string $resource A cache key
+ *
+ * @return string A file path
+ */
+ private function getSourcePath($resource)
+ {
+ $key = md5($resource);
+
+ return $this->dir.'/'.$key[0].'/'.$key.'.php';
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Cache/ExpiringCache.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Cache/ExpiringCache.php
new file mode 100644
index 0000000..46ef85f
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Cache/ExpiringCache.php
@@ -0,0 +1,60 @@
+
+ */
+class ExpiringCache implements CacheInterface
+{
+ private $cache;
+ private $lifetime;
+
+ public function __construct(CacheInterface $cache, $lifetime)
+ {
+ $this->cache = $cache;
+ $this->lifetime = $lifetime;
+ }
+
+ public function has($key)
+ {
+ if ($this->cache->has($key)) {
+ if (time() < $this->cache->get($key.'.expires')) {
+ return true;
+ }
+
+ $this->cache->remove($key.'.expires');
+ $this->cache->remove($key);
+ }
+
+ return false;
+ }
+
+ public function get($key)
+ {
+ return $this->cache->get($key);
+ }
+
+ public function set($key, $value)
+ {
+ $this->cache->set($key.'.expires', time() + $this->lifetime);
+ $this->cache->set($key, $value);
+ }
+
+ public function remove($key)
+ {
+ $this->cache->remove($key.'.expires');
+ $this->cache->remove($key);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Cache/FilesystemCache.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Cache/FilesystemCache.php
new file mode 100644
index 0000000..f8eddfd
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Cache/FilesystemCache.php
@@ -0,0 +1,65 @@
+
+ */
+class FilesystemCache implements CacheInterface
+{
+ private $dir;
+
+ public function __construct($dir)
+ {
+ $this->dir = $dir;
+ }
+
+ public function has($key)
+ {
+ return file_exists($this->dir.'/'.$key);
+ }
+
+ public function get($key)
+ {
+ $path = $this->dir.'/'.$key;
+
+ if (!file_exists($path)) {
+ throw new \RuntimeException('There is no cached value for '.$key);
+ }
+
+ return file_get_contents($path);
+ }
+
+ public function set($key, $value)
+ {
+ if (!is_dir($this->dir) && false === @mkdir($this->dir, 0777, true)) {
+ throw new \RuntimeException('Unable to create directory '.$this->dir);
+ }
+
+ $path = $this->dir.'/'.$key;
+
+ if (false === @file_put_contents($path, $value)) {
+ throw new \RuntimeException('Unable to write file '.$path);
+ }
+ }
+
+ public function remove($key)
+ {
+ $path = $this->dir.'/'.$key;
+
+ if (file_exists($path) && false === @unlink($path)) {
+ throw new \RuntimeException('Unable to remove file '.$path);
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Exception/Exception.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Exception/Exception.php
new file mode 100644
index 0000000..1bd4acc
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Exception/Exception.php
@@ -0,0 +1,21 @@
+
+ */
+interface Exception
+{
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Exception/FilterException.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Exception/FilterException.php
new file mode 100644
index 0000000..03a230b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Exception/FilterException.php
@@ -0,0 +1,73 @@
+
+ */
+class FilterException extends \RuntimeException implements Exception
+{
+ private $originalMessage;
+ private $input;
+
+ public static function fromProcess(Process $proc)
+ {
+ $message = sprintf("An error occurred while running:\n%s", $proc->getCommandLine());
+
+ $errorOutput = $proc->getErrorOutput();
+ if (!empty($errorOutput)) {
+ $message .= "\n\nError Output:\n".str_replace("\r", '', $errorOutput);
+ }
+
+ $output = $proc->getOutput();
+ if (!empty($output)) {
+ $message .= "\n\nOutput:\n".str_replace("\r", '', $output);
+ }
+
+ return new self($message);
+ }
+
+ public function __construct($message, $code = 0, \Exception $previous = null)
+ {
+ parent::__construct($message, $code, $previous);
+
+ $this->originalMessage = $message;
+ }
+
+ public function setInput($input)
+ {
+ $this->input = $input;
+ $this->updateMessage();
+
+ return $this;
+ }
+
+ public function getInput()
+ {
+ return $this->input;
+ }
+
+ private function updateMessage()
+ {
+ $message = $this->originalMessage;
+
+ if (!empty($this->input)) {
+ $message .= "\n\nInput:\n".$this->input;
+ }
+
+ $this->message = $message;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticExtension.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticExtension.php
new file mode 100644
index 0000000..951e1c8
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticExtension.php
@@ -0,0 +1,76 @@
+factory = $factory;
+ $this->functions = array();
+ $this->valueSupplier = $valueSupplier;
+
+ foreach ($functions as $function => $options) {
+ if (is_integer($function) && is_string($options)) {
+ $this->functions[$options] = array('filter' => $options);
+ } else {
+ $this->functions[$function] = $options + array('filter' => $function);
+ }
+ }
+ }
+
+ public function getTokenParsers()
+ {
+ return array(
+ new AsseticTokenParser($this->factory, 'javascripts', 'js/*.js'),
+ new AsseticTokenParser($this->factory, 'stylesheets', 'css/*.css'),
+ new AsseticTokenParser($this->factory, 'image', 'images/*', true),
+ );
+ }
+
+ public function getFunctions()
+ {
+ $functions = array();
+ foreach ($this->functions as $function => $filter) {
+ $functions[] = new AsseticFilterFunction($function);
+ }
+
+ return $functions;
+ }
+
+ public function getGlobals()
+ {
+ return array(
+ 'assetic' => array(
+ 'debug' => $this->factory->isDebug(),
+ 'vars' => null !== $this->valueSupplier ? new ValueContainer($this->valueSupplier) : array(),
+ ),
+ );
+ }
+
+ public function getFilterInvoker($function)
+ {
+ return new AsseticFilterInvoker($this->factory, $this->functions[$function]);
+ }
+
+ public function getName()
+ {
+ return 'assetic';
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterFunction.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterFunction.php
new file mode 100644
index 0000000..2c2b13c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterFunction.php
@@ -0,0 +1,24 @@
+ false,
+ 'needs_context' => false,
+ 'node_class' => '\Assetic\Extension\Twig\AsseticFilterNode',
+ )));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterInvoker.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterInvoker.php
new file mode 100644
index 0000000..1b70e43
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterInvoker.php
@@ -0,0 +1,59 @@
+
+ */
+class AsseticFilterInvoker
+{
+ private $factory;
+ private $filters;
+ private $options;
+
+ public function __construct($factory, $filter)
+ {
+ $this->factory = $factory;
+
+ if (is_array($filter) && isset($filter['filter'])) {
+ $this->filters = (array) $filter['filter'];
+ $this->options = isset($filter['options']) ? (array) $filter['options'] : array();
+ } else {
+ $this->filters = (array) $filter;
+ $this->options = array();
+ }
+ }
+
+ public function getFactory()
+ {
+ return $this->factory;
+ }
+
+ public function getFilters()
+ {
+ return $this->filters;
+ }
+
+ public function getOptions()
+ {
+ return $this->options;
+ }
+
+ public function invoke($input, array $options = array())
+ {
+ $asset = $this->factory->createAsset($input, $this->filters, $options + $this->options);
+
+ return $asset->getTargetPath();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterNode.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterNode.php
new file mode 100644
index 0000000..3fe05ac
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterNode.php
@@ -0,0 +1,22 @@
+raw(sprintf('$this->env->getExtension(\'Assetic\\Extension\\Twig\\AsseticExtension\')->getFilterInvoker(\'%s\')->invoke', $this->getAttribute('name')));
+
+ $this->compileArguments($compiler);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticNode.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticNode.php
new file mode 100644
index 0000000..950e46c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticNode.php
@@ -0,0 +1,165 @@
+ $body);
+
+ $attributes = array_replace(
+ array('debug' => null, 'combine' => null, 'var_name' => 'asset_url'),
+ $attributes,
+ array('asset' => $asset, 'inputs' => $inputs, 'filters' => $filters, 'name' => $name)
+ );
+
+ parent::__construct($nodes, $attributes, $lineno, $tag);
+ }
+
+ public function compile(\Twig_Compiler $compiler)
+ {
+ $compiler->addDebugInfo($this);
+
+ $combine = $this->getAttribute('combine');
+ $debug = $this->getAttribute('debug');
+
+ if (null === $combine && null !== $debug) {
+ $combine = !$debug;
+ }
+
+ if (null === $combine) {
+ $compiler
+ ->write("if (isset(\$context['assetic']['debug']) && \$context['assetic']['debug']) {\n")
+ ->indent()
+ ;
+
+ $this->compileDebug($compiler);
+
+ $compiler
+ ->outdent()
+ ->write("} else {\n")
+ ->indent()
+ ;
+
+ $this->compileAsset($compiler, $this->getAttribute('asset'), $this->getAttribute('name'));
+
+ $compiler
+ ->outdent()
+ ->write("}\n")
+ ;
+ } elseif ($combine) {
+ $this->compileAsset($compiler, $this->getAttribute('asset'), $this->getAttribute('name'));
+ } else {
+ $this->compileDebug($compiler);
+ }
+
+ $compiler
+ ->write('unset($context[')
+ ->repr($this->getAttribute('var_name'))
+ ->raw("]);\n")
+ ;
+ }
+
+ protected function compileDebug(\Twig_Compiler $compiler)
+ {
+ $i = 0;
+ foreach ($this->getAttribute('asset') as $leaf) {
+ $leafName = $this->getAttribute('name').'_'.$i++;
+ $this->compileAsset($compiler, $leaf, $leafName);
+ }
+ }
+
+ protected function compileAsset(\Twig_Compiler $compiler, AssetInterface $asset, $name)
+ {
+ if ($vars = $asset->getVars()) {
+ $compiler->write("// check variable conditions\n");
+
+ foreach ($vars as $var) {
+ $compiler
+ ->write("if (!isset(\$context['assetic']['vars']['$var'])) {\n")
+ ->indent()
+ ->write("throw new \RuntimeException(sprintf('The asset \"".$name."\" expected variable \"".$var."\" to be set, but got only these vars: %s. Did you set-up a value supplier?', isset(\$context['assetic']['vars']) && \$context['assetic']['vars'] ? implode(', ', \$context['assetic']['vars']) : '# none #'));\n")
+ ->outdent()
+ ->write("}\n")
+ ;
+ }
+
+ $compiler->raw("\n");
+ }
+
+ $compiler
+ ->write("// asset \"$name\"\n")
+ ->write('$context[')
+ ->repr($this->getAttribute('var_name'))
+ ->raw('] = ')
+ ;
+
+ $this->compileAssetUrl($compiler, $asset, $name);
+
+ $compiler
+ ->raw(";\n")
+ ->subcompile($this->getNode('body'))
+ ;
+ }
+
+ protected function compileAssetUrl(\Twig_Compiler $compiler, AssetInterface $asset, $name)
+ {
+ if (!$vars = $asset->getVars()) {
+ $compiler->repr($asset->getTargetPath());
+
+ return;
+ }
+
+ $compiler
+ ->raw("strtr(")
+ ->string($asset->getTargetPath())
+ ->raw(", array(");
+
+ $first = true;
+ foreach ($vars as $var) {
+ if (!$first) {
+ $compiler->raw(", ");
+ }
+ $first = false;
+
+ $compiler
+ ->string("{".$var."}")
+ ->raw(" => \$context['assetic']['vars']['$var']")
+ ;
+ }
+
+ $compiler
+ ->raw("))")
+ ;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticTokenParser.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticTokenParser.php
new file mode 100644
index 0000000..614f567
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticTokenParser.php
@@ -0,0 +1,198 @@
+factory = $factory;
+ $this->tag = $tag;
+ $this->output = $output;
+ $this->single = $single;
+ $this->extensions = $extensions;
+ }
+
+ public function parse(\Twig_Token $token)
+ {
+ $inputs = array();
+ $filters = array();
+ $name = null;
+ $attributes = array(
+ 'output' => $this->output,
+ 'var_name' => 'asset_url',
+ 'vars' => array(),
+ );
+
+ $stream = $this->parser->getStream();
+ while (!$stream->test(\Twig_Token::BLOCK_END_TYPE)) {
+ if ($stream->test(\Twig_Token::STRING_TYPE)) {
+ // '@jquery', 'js/src/core/*', 'js/src/extra.js'
+ $inputs[] = $stream->next()->getValue();
+ } elseif ($stream->test(\Twig_Token::NAME_TYPE, 'filter')) {
+ // filter='yui_js'
+ $stream->next();
+ $stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
+ $filters = array_merge($filters, array_filter(array_map('trim', explode(',', $stream->expect(\Twig_Token::STRING_TYPE)->getValue()))));
+ } elseif ($stream->test(\Twig_Token::NAME_TYPE, 'output')) {
+ // output='js/packed/*.js' OR output='js/core.js'
+ $stream->next();
+ $stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
+ $attributes['output'] = $stream->expect(\Twig_Token::STRING_TYPE)->getValue();
+ } elseif ($stream->test(\Twig_Token::NAME_TYPE, 'name')) {
+ // name='core_js'
+ $stream->next();
+ $stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
+ $name = $stream->expect(\Twig_Token::STRING_TYPE)->getValue();
+ } elseif ($stream->test(\Twig_Token::NAME_TYPE, 'as')) {
+ // as='the_url'
+ $stream->next();
+ $stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
+ $attributes['var_name'] = $stream->expect(\Twig_Token::STRING_TYPE)->getValue();
+ } elseif ($stream->test(\Twig_Token::NAME_TYPE, 'debug')) {
+ // debug=true
+ $stream->next();
+ $stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
+ $attributes['debug'] = 'true' == $stream->expect(\Twig_Token::NAME_TYPE, array('true', 'false'))->getValue();
+ } elseif ($stream->test(\Twig_Token::NAME_TYPE, 'combine')) {
+ // combine=true
+ $stream->next();
+ $stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
+ $attributes['combine'] = 'true' == $stream->expect(\Twig_Token::NAME_TYPE, array('true', 'false'))->getValue();
+ } elseif ($stream->test(\Twig_Token::NAME_TYPE, 'vars')) {
+ // vars=['locale','browser']
+ $stream->next();
+ $stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
+ $stream->expect(\Twig_Token::PUNCTUATION_TYPE, '[');
+
+ while ($stream->test(\Twig_Token::STRING_TYPE)) {
+ $attributes['vars'][] = $stream->expect(\Twig_Token::STRING_TYPE)->getValue();
+
+ if (!$stream->test(\Twig_Token::PUNCTUATION_TYPE, ',')) {
+ break;
+ }
+
+ $stream->next();
+ }
+
+ $stream->expect(\Twig_Token::PUNCTUATION_TYPE, ']');
+ } elseif ($stream->test(\Twig_Token::NAME_TYPE, $this->extensions)) {
+ // an arbitrary configured attribute
+ $key = $stream->next()->getValue();
+ $stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
+ $attributes[$key] = $stream->expect(\Twig_Token::STRING_TYPE)->getValue();
+ } else {
+ $token = $stream->getCurrent();
+ throw new \Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s"', \Twig_Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $stream->getFilename());
+ }
+ }
+
+ $stream->expect(\Twig_Token::BLOCK_END_TYPE);
+
+ $body = $this->parser->subparse(array($this, 'testEndTag'), true);
+
+ $stream->expect(\Twig_Token::BLOCK_END_TYPE);
+
+ if ($this->single && 1 < count($inputs)) {
+ $inputs = array_slice($inputs, -1);
+ }
+
+ if (!$name) {
+ $name = $this->factory->generateAssetName($inputs, $filters, $attributes);
+ }
+
+ $asset = $this->factory->createAsset($inputs, $filters, $attributes + array('name' => $name));
+
+ return $this->createBodyNode($asset, $body, $inputs, $filters, $name, $attributes, $token->getLine(), $this->getTag());
+ }
+
+ public function getTag()
+ {
+ return $this->tag;
+ }
+
+ public function testEndTag(\Twig_Token $token)
+ {
+ return $token->test(array('end'.$this->getTag()));
+ }
+
+ /**
+ * @param AssetInterface $asset
+ * @param \Twig_Node $body
+ * @param array $inputs
+ * @param array $filters
+ * @param string $name
+ * @param array $attributes
+ * @param int $lineno
+ * @param string $tag
+ *
+ * @return \Twig_Node
+ */
+ protected function createBodyNode(AssetInterface $asset, \Twig_Node $body, array $inputs, array $filters, $name, array $attributes = array(), $lineno = 0, $tag = null)
+ {
+ $reflector = new \ReflectionMethod($this, 'createNode');
+
+ if (__CLASS__ !== $reflector->getDeclaringClass()->name) {
+ @trigger_error(sprintf('Overwriting %s::createNode is deprecated since 1.3. Overwrite %s instead.', __CLASS__, __METHOD__), E_USER_DEPRECATED);
+
+ return $this->createNode($asset, $body, $inputs, $filters, $name, $attributes, $lineno, $tag);
+ }
+
+ return new AsseticNode($asset, $body, $inputs, $filters, $name, $attributes, $lineno, $tag);
+ }
+
+ /**
+ * @param AssetInterface $asset
+ * @param \Twig_NodeInterface $body
+ * @param array $inputs
+ * @param array $filters
+ * @param string $name
+ * @param array $attributes
+ * @param int $lineno
+ * @param string $tag
+ *
+ * @return \Twig_Node
+ *
+ * @deprecated since 1.3.0, to be removed in 2.0. Use createBodyNode instead.
+ */
+ protected function createNode(AssetInterface $asset, \Twig_NodeInterface $body, array $inputs, array $filters, $name, array $attributes = array(), $lineno = 0, $tag = null)
+ {
+ @trigger_error(sprintf('The %s method is deprecated since 1.3 and will be removed in 2.0. Use createBodyNode instead.', __METHOD__), E_USER_DEPRECATED);
+
+ if (!$body instanceof \Twig_Node) {
+ throw new \InvalidArgumentException('The body must be a Twig_Node. Custom implementations of Twig_NodeInterface are not supported.');
+ }
+
+ return new AsseticNode($asset, $body, $inputs, $filters, $name, $attributes, $lineno, $tag);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/TwigFormulaLoader.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/TwigFormulaLoader.php
new file mode 100644
index 0000000..2c12d7e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/TwigFormulaLoader.php
@@ -0,0 +1,108 @@
+
+ */
+class TwigFormulaLoader implements FormulaLoaderInterface
+{
+ private $twig;
+ private $logger;
+
+ public function __construct(\Twig_Environment $twig, LoggerInterface $logger = null)
+ {
+ $this->twig = $twig;
+ $this->logger = $logger;
+ }
+
+ public function load(ResourceInterface $resource)
+ {
+ try {
+ $tokens = $this->twig->tokenize(new \Twig_Source($resource->getContent(), (string) $resource));
+ $nodes = $this->twig->parse($tokens);
+ } catch (\Exception $e) {
+ if ($this->logger) {
+ $this->logger->error(sprintf('The template "%s" contains an error: %s', $resource, $e->getMessage()));
+ }
+
+ return array();
+ }
+
+ return $this->loadNode($nodes);
+ }
+
+ /**
+ * Loads assets from the supplied node.
+ *
+ * @param \Twig_Node $node
+ *
+ * @return array An array of asset formulae indexed by name
+ */
+ private function loadNode(\Twig_Node $node)
+ {
+ $formulae = array();
+
+ if ($node instanceof AsseticNode) {
+ $formulae[$node->getAttribute('name')] = array(
+ $node->getAttribute('inputs'),
+ $node->getAttribute('filters'),
+ array(
+ 'output' => $node->getAttribute('asset')->getTargetPath(),
+ 'name' => $node->getAttribute('name'),
+ 'debug' => $node->getAttribute('debug'),
+ 'combine' => $node->getAttribute('combine'),
+ 'vars' => $node->getAttribute('vars'),
+ ),
+ );
+ } elseif ($node instanceof AsseticFilterNode) {
+ $name = $node->getAttribute('name');
+
+ $arguments = array();
+ foreach ($node->getNode('arguments') as $argument) {
+ $arguments[] = eval('return '.$this->twig->compile($argument).';');
+ }
+
+ $invoker = $this->twig->getExtension('Assetic\Extension\Twig\AsseticExtension')->getFilterInvoker($name);
+
+ $inputs = isset($arguments[0]) ? (array) $arguments[0] : array();
+ $filters = $invoker->getFilters();
+ $options = array_replace($invoker->getOptions(), isset($arguments[1]) ? $arguments[1] : array());
+
+ if (!isset($options['name'])) {
+ $options['name'] = $invoker->getFactory()->generateAssetName($inputs, $filters, $options);
+ }
+
+ $formulae[$options['name']] = array($inputs, $filters, $options);
+ }
+
+ foreach ($node as $child) {
+ if ($child instanceof \Twig_Node) {
+ $formulae += $this->loadNode($child);
+ }
+ }
+
+ if ($node->hasAttribute('embedded_templates')) {
+ foreach ($node->getAttribute('embedded_templates') as $child) {
+ $formulae += $this->loadNode($child);
+ }
+ }
+
+ return $formulae;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/TwigResource.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/TwigResource.php
new file mode 100644
index 0000000..21d040a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/TwigResource.php
@@ -0,0 +1,56 @@
+
+ */
+class TwigResource implements ResourceInterface
+{
+ private $loader;
+ private $name;
+
+ public function __construct(\Twig_LoaderInterface $loader, $name)
+ {
+ $this->loader = $loader;
+ $this->name = $name;
+ }
+
+ public function getContent()
+ {
+ try {
+ return method_exists($this->loader, 'getSourceContext')
+ ? $this->loader->getSourceContext($this->name)->getCode()
+ : $this->loader->getSource($this->name);
+ } catch (\Twig_Error_Loader $e) {
+ return '';
+ }
+ }
+
+ public function isFresh($timestamp)
+ {
+ try {
+ return $this->loader->isFresh($this->name, $timestamp);
+ } catch (\Twig_Error_Loader $e) {
+ return false;
+ }
+ }
+
+ public function __toString()
+ {
+ return $this->name;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/ValueContainer.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/ValueContainer.php
new file mode 100644
index 0000000..e197224
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/ValueContainer.php
@@ -0,0 +1,79 @@
+
+ */
+class ValueContainer implements \ArrayAccess, \IteratorAggregate, \Countable
+{
+ private $values;
+ private $valueSupplier;
+
+ public function __construct(ValueSupplierInterface $valueSupplier)
+ {
+ $this->valueSupplier = $valueSupplier;
+ }
+
+ public function offsetExists($offset)
+ {
+ $this->initialize();
+
+ return array_key_exists($offset, $this->values);
+ }
+
+ public function offsetGet($offset)
+ {
+ $this->initialize();
+
+ if (!array_key_exists($offset, $this->values)) {
+ throw new \OutOfRangeException(sprintf('The variable "%s" does not exist.', $offset));
+ }
+
+ return $this->values[$offset];
+ }
+
+ public function offsetSet($offset, $value)
+ {
+ throw new \BadMethodCallException('The ValueContainer is read-only.');
+ }
+
+ public function offsetUnset($offset)
+ {
+ throw new \BadMethodCallException('The ValueContainer is read-only.');
+ }
+
+ public function getIterator()
+ {
+ $this->initialize();
+
+ return new \ArrayIterator($this->values);
+ }
+
+ public function count()
+ {
+ $this->initialize();
+
+ return count($this->values);
+ }
+
+ private function initialize()
+ {
+ if (null === $this->values) {
+ $this->values = $this->valueSupplier->getValues();
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/AssetFactory.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/AssetFactory.php
new file mode 100644
index 0000000..e267196
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/AssetFactory.php
@@ -0,0 +1,424 @@
+
+ */
+class AssetFactory
+{
+ private $root;
+ private $debug;
+ private $output;
+ private $workers;
+ private $am;
+ private $fm;
+
+ /**
+ * Constructor.
+ *
+ * @param string $root The default root directory
+ * @param Boolean $debug Filters prefixed with a "?" will be omitted in debug mode
+ */
+ public function __construct($root, $debug = false)
+ {
+ $this->root = rtrim($root, '/');
+ $this->debug = $debug;
+ $this->output = 'assetic/*';
+ $this->workers = array();
+ }
+
+ /**
+ * Sets debug mode for the current factory.
+ *
+ * @param Boolean $debug Debug mode
+ */
+ public function setDebug($debug)
+ {
+ $this->debug = $debug;
+ }
+
+ /**
+ * Checks if the factory is in debug mode.
+ *
+ * @return Boolean Debug mode
+ */
+ public function isDebug()
+ {
+ return $this->debug;
+ }
+
+ /**
+ * Sets the default output string.
+ *
+ * @param string $output The default output string
+ */
+ public function setDefaultOutput($output)
+ {
+ $this->output = $output;
+ }
+
+ /**
+ * Adds a factory worker.
+ *
+ * @param WorkerInterface $worker A worker
+ */
+ public function addWorker(WorkerInterface $worker)
+ {
+ $this->workers[] = $worker;
+ }
+
+ /**
+ * Returns the current asset manager.
+ *
+ * @return AssetManager|null The asset manager
+ */
+ public function getAssetManager()
+ {
+ return $this->am;
+ }
+
+ /**
+ * Sets the asset manager to use when creating asset references.
+ *
+ * @param AssetManager $am The asset manager
+ */
+ public function setAssetManager(AssetManager $am)
+ {
+ $this->am = $am;
+ }
+
+ /**
+ * Returns the current filter manager.
+ *
+ * @return FilterManager|null The filter manager
+ */
+ public function getFilterManager()
+ {
+ return $this->fm;
+ }
+
+ /**
+ * Sets the filter manager to use when adding filters.
+ *
+ * @param FilterManager $fm The filter manager
+ */
+ public function setFilterManager(FilterManager $fm)
+ {
+ $this->fm = $fm;
+ }
+
+ /**
+ * Creates a new asset.
+ *
+ * Prefixing a filter name with a question mark will cause it to be
+ * omitted when the factory is in debug mode.
+ *
+ * Available options:
+ *
+ * * output: An output string
+ * * name: An asset name for interpolation in output patterns
+ * * debug: Forces debug mode on or off for this asset
+ * * root: An array or string of more root directories
+ *
+ * @param array|string $inputs An array of input strings
+ * @param array|string $filters An array of filter names
+ * @param array $options An array of options
+ *
+ * @return AssetCollection An asset collection
+ */
+ public function createAsset($inputs = array(), $filters = array(), array $options = array())
+ {
+ if (!is_array($inputs)) {
+ $inputs = array($inputs);
+ }
+
+ if (!is_array($filters)) {
+ $filters = array($filters);
+ }
+
+ if (!isset($options['output'])) {
+ $options['output'] = $this->output;
+ }
+
+ if (!isset($options['vars'])) {
+ $options['vars'] = array();
+ }
+
+ if (!isset($options['debug'])) {
+ $options['debug'] = $this->debug;
+ }
+
+ if (!isset($options['root'])) {
+ $options['root'] = array($this->root);
+ } else {
+ if (!is_array($options['root'])) {
+ $options['root'] = array($options['root']);
+ }
+
+ $options['root'][] = $this->root;
+ }
+
+ if (!isset($options['name'])) {
+ $options['name'] = $this->generateAssetName($inputs, $filters, $options);
+ }
+
+ $asset = $this->createAssetCollection(array(), $options);
+ $extensions = array();
+
+ // inner assets
+ foreach ($inputs as $input) {
+ if (is_array($input)) {
+ // nested formula
+ $asset->add(call_user_func_array(array($this, 'createAsset'), $input));
+ } else {
+ $asset->add($this->parseInput($input, $options));
+ $extensions[pathinfo($input, PATHINFO_EXTENSION)] = true;
+ }
+ }
+
+ // filters
+ foreach ($filters as $filter) {
+ if ('?' != $filter[0]) {
+ $asset->ensureFilter($this->getFilter($filter));
+ } elseif (!$options['debug']) {
+ $asset->ensureFilter($this->getFilter(substr($filter, 1)));
+ }
+ }
+
+ // append variables
+ if (!empty($options['vars'])) {
+ $toAdd = array();
+ foreach ($options['vars'] as $var) {
+ if (false !== strpos($options['output'], '{'.$var.'}')) {
+ continue;
+ }
+
+ $toAdd[] = '{'.$var.'}';
+ }
+
+ if ($toAdd) {
+ $options['output'] = str_replace('*', '*.'.implode('.', $toAdd), $options['output']);
+ }
+ }
+
+ // append consensus extension if missing
+ if (1 == count($extensions) && !pathinfo($options['output'], PATHINFO_EXTENSION) && $extension = key($extensions)) {
+ $options['output'] .= '.'.$extension;
+ }
+
+ // output --> target url
+ $asset->setTargetPath(str_replace('*', $options['name'], $options['output']));
+
+ // apply workers and return
+ return $this->applyWorkers($asset);
+ }
+
+ public function generateAssetName($inputs, $filters, $options = array())
+ {
+ foreach (array_diff(array_keys($options), array('output', 'debug', 'root')) as $key) {
+ unset($options[$key]);
+ }
+
+ ksort($options);
+
+ return substr(sha1(serialize($inputs).serialize($filters).serialize($options)), 0, 7);
+ }
+
+ public function getLastModified(AssetInterface $asset)
+ {
+ $mtime = 0;
+ foreach ($asset instanceof AssetCollectionInterface ? $asset : array($asset) as $leaf) {
+ $mtime = max($mtime, $leaf->getLastModified());
+
+ if (!$filters = $leaf->getFilters()) {
+ continue;
+ }
+
+ $prevFilters = array();
+ foreach ($filters as $filter) {
+ $prevFilters[] = $filter;
+
+ if (!$filter instanceof DependencyExtractorInterface) {
+ continue;
+ }
+
+ // extract children from leaf after running all preceeding filters
+ $clone = clone $leaf;
+ $clone->clearFilters();
+ foreach (array_slice($prevFilters, 0, -1) as $prevFilter) {
+ $clone->ensureFilter($prevFilter);
+ }
+ $clone->load();
+
+ foreach ($filter->getChildren($this, $clone->getContent(), $clone->getSourceDirectory()) as $child) {
+ $mtime = max($mtime, $this->getLastModified($child));
+ }
+ }
+ }
+
+ return $mtime;
+ }
+
+ /**
+ * Parses an input string string into an asset.
+ *
+ * The input string can be one of the following:
+ *
+ * * A reference: If the string starts with an "at" sign it will be interpreted as a reference to an asset in the asset manager
+ * * An absolute URL: If the string contains "://" or starts with "//" it will be interpreted as an HTTP asset
+ * * A glob: If the string contains a "*" it will be interpreted as a glob
+ * * A path: Otherwise the string is interpreted as a filesystem path
+ *
+ * Both globs and paths will be absolutized using the current root directory.
+ *
+ * @param string $input An input string
+ * @param array $options An array of options
+ *
+ * @return AssetInterface An asset
+ */
+ protected function parseInput($input, array $options = array())
+ {
+ if ('@' == $input[0]) {
+ return $this->createAssetReference(substr($input, 1));
+ }
+
+ if (false !== strpos($input, '://') || 0 === strpos($input, '//')) {
+ return $this->createHttpAsset($input, $options['vars']);
+ }
+
+ if (self::isAbsolutePath($input)) {
+ if ($root = self::findRootDir($input, $options['root'])) {
+ $path = ltrim(substr($input, strlen($root)), '/');
+ } else {
+ $path = null;
+ }
+ } else {
+ $root = $this->root;
+ $path = $input;
+ $input = $this->root.'/'.$path;
+ }
+
+ if (false !== strpos($input, '*')) {
+ return $this->createGlobAsset($input, $root, $options['vars']);
+ }
+
+ return $this->createFileAsset($input, $root, $path, $options['vars']);
+ }
+
+ protected function createAssetCollection(array $assets = array(), array $options = array())
+ {
+ return new AssetCollection($assets, array(), null, isset($options['vars']) ? $options['vars'] : array());
+ }
+
+ protected function createAssetReference($name)
+ {
+ if (!$this->am) {
+ throw new \LogicException('There is no asset manager.');
+ }
+
+ return new AssetReference($this->am, $name);
+ }
+
+ protected function createHttpAsset($sourceUrl, $vars)
+ {
+ return new HttpAsset($sourceUrl, array(), false, $vars);
+ }
+
+ protected function createGlobAsset($glob, $root = null, $vars)
+ {
+ return new GlobAsset($glob, array(), $root, $vars);
+ }
+
+ protected function createFileAsset($source, $root = null, $path = null, $vars)
+ {
+ return new FileAsset($source, array(), $root, $path, $vars);
+ }
+
+ protected function getFilter($name)
+ {
+ if (!$this->fm) {
+ throw new \LogicException('There is no filter manager.');
+ }
+
+ return $this->fm->get($name);
+ }
+
+ /**
+ * Filters an asset collection through the factory workers.
+ *
+ * Each leaf asset will be processed first, followed by the asset
+ * collection itself.
+ *
+ * @param AssetCollectionInterface $asset An asset collection
+ *
+ * @return AssetCollectionInterface
+ */
+ private function applyWorkers(AssetCollectionInterface $asset)
+ {
+ foreach ($asset as $leaf) {
+ foreach ($this->workers as $worker) {
+ $retval = $worker->process($leaf, $this);
+
+ if ($retval instanceof AssetInterface && $leaf !== $retval) {
+ $asset->replaceLeaf($leaf, $retval);
+ }
+ }
+ }
+
+ foreach ($this->workers as $worker) {
+ $retval = $worker->process($asset, $this);
+
+ if ($retval instanceof AssetInterface) {
+ $asset = $retval;
+ }
+ }
+
+ return $asset instanceof AssetCollectionInterface ? $asset : $this->createAssetCollection(array($asset));
+ }
+
+ private static function isAbsolutePath($path)
+ {
+ return '/' == $path[0] || '\\' == $path[0] || (3 < strlen($path) && ctype_alpha($path[0]) && $path[1] == ':' && ('\\' == $path[2] || '/' == $path[2]));
+ }
+
+ /**
+ * Loops through the root directories and returns the first match.
+ *
+ * @param string $path An absolute path
+ * @param array $roots An array of root directories
+ *
+ * @return string|null The matching root directory, if found
+ */
+ private static function findRootDir($path, array $roots)
+ {
+ foreach ($roots as $root) {
+ if (0 === strpos($path, $root)) {
+ return $root;
+ }
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/LazyAssetManager.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/LazyAssetManager.php
new file mode 100644
index 0000000..bef72e5
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/LazyAssetManager.php
@@ -0,0 +1,210 @@
+
+ */
+class LazyAssetManager extends AssetManager
+{
+ private $factory;
+ private $loaders;
+ private $resources;
+ private $formulae;
+ private $loaded;
+ private $loading;
+
+ /**
+ * Constructor.
+ *
+ * @param AssetFactory $factory The asset factory
+ * @param array $loaders An array of loaders indexed by alias
+ */
+ public function __construct(AssetFactory $factory, $loaders = array())
+ {
+ $this->factory = $factory;
+ $this->loaders = array();
+ $this->resources = array();
+ $this->formulae = array();
+ $this->loaded = false;
+ $this->loading = false;
+
+ foreach ($loaders as $alias => $loader) {
+ $this->setLoader($alias, $loader);
+ }
+ }
+
+ /**
+ * Adds a loader to the asset manager.
+ *
+ * @param string $alias An alias for the loader
+ * @param FormulaLoaderInterface $loader A loader
+ */
+ public function setLoader($alias, FormulaLoaderInterface $loader)
+ {
+ $this->loaders[$alias] = $loader;
+ $this->loaded = false;
+ }
+
+ /**
+ * Adds a resource to the asset manager.
+ *
+ * @param ResourceInterface $resource A resource
+ * @param string $loader The loader alias for this resource
+ */
+ public function addResource(ResourceInterface $resource, $loader)
+ {
+ $this->resources[$loader][] = $resource;
+ $this->loaded = false;
+ }
+
+ /**
+ * Returns an array of resources.
+ *
+ * @return array An array of resources
+ */
+ public function getResources()
+ {
+ $resources = array();
+ foreach ($this->resources as $r) {
+ $resources = array_merge($resources, $r);
+ }
+
+ return $resources;
+ }
+
+ /**
+ * Checks for an asset formula.
+ *
+ * @param string $name An asset name
+ *
+ * @return Boolean If there is a formula
+ */
+ public function hasFormula($name)
+ {
+ if (!$this->loaded) {
+ $this->load();
+ }
+
+ return isset($this->formulae[$name]);
+ }
+
+ /**
+ * Returns an asset's formula.
+ *
+ * @param string $name An asset name
+ *
+ * @return array The formula
+ *
+ * @throws \InvalidArgumentException If there is no formula by that name
+ */
+ public function getFormula($name)
+ {
+ if (!$this->loaded) {
+ $this->load();
+ }
+
+ if (!isset($this->formulae[$name])) {
+ throw new \InvalidArgumentException(sprintf('There is no "%s" formula.', $name));
+ }
+
+ return $this->formulae[$name];
+ }
+
+ /**
+ * Sets a formula on the asset manager.
+ *
+ * @param string $name An asset name
+ * @param array $formula A formula
+ */
+ public function setFormula($name, array $formula)
+ {
+ $this->formulae[$name] = $formula;
+ }
+
+ /**
+ * Loads formulae from resources.
+ *
+ * @throws \LogicException If a resource has been added to an invalid loader
+ */
+ public function load()
+ {
+ if ($this->loading) {
+ return;
+ }
+
+ if ($diff = array_diff(array_keys($this->resources), array_keys($this->loaders))) {
+ throw new \LogicException('The following loader(s) are not registered: '.implode(', ', $diff));
+ }
+
+ $this->loading = true;
+
+ foreach ($this->resources as $loader => $resources) {
+ foreach ($resources as $resource) {
+ $this->formulae = array_replace($this->formulae, $this->loaders[$loader]->load($resource));
+ }
+ }
+
+ $this->loaded = true;
+ $this->loading = false;
+ }
+
+ public function get($name)
+ {
+ if (!$this->loaded) {
+ $this->load();
+ }
+
+ if (!parent::has($name) && isset($this->formulae[$name])) {
+ list($inputs, $filters, $options) = $this->formulae[$name];
+ $options['name'] = $name;
+ parent::set($name, $this->factory->createAsset($inputs, $filters, $options));
+ }
+
+ return parent::get($name);
+ }
+
+ public function has($name)
+ {
+ if (!$this->loaded) {
+ $this->load();
+ }
+
+ return isset($this->formulae[$name]) || parent::has($name);
+ }
+
+ public function getNames()
+ {
+ if (!$this->loaded) {
+ $this->load();
+ }
+
+ return array_unique(array_merge(parent::getNames(), array_keys($this->formulae)));
+ }
+
+ public function isDebug()
+ {
+ return $this->factory->isDebug();
+ }
+
+ public function getLastModified(AssetInterface $asset)
+ {
+ return $this->factory->getLastModified($asset);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/BasePhpFormulaLoader.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/BasePhpFormulaLoader.php
new file mode 100644
index 0000000..4c747df
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/BasePhpFormulaLoader.php
@@ -0,0 +1,160 @@
+
+ */
+abstract class BasePhpFormulaLoader implements FormulaLoaderInterface
+{
+ protected $factory;
+ protected $prototypes;
+
+ public function __construct(AssetFactory $factory)
+ {
+ $this->factory = $factory;
+ $this->prototypes = array();
+
+ foreach ($this->registerPrototypes() as $prototype => $options) {
+ $this->addPrototype($prototype, $options);
+ }
+ }
+
+ public function addPrototype($prototype, array $options = array())
+ {
+ $tokens = token_get_all('prototypes[$prototype] = array($tokens, $options);
+ }
+
+ public function load(ResourceInterface $resource)
+ {
+ if (!$nbProtos = count($this->prototypes)) {
+ throw new \LogicException('There are no prototypes registered.');
+ }
+
+ $buffers = array_fill(0, $nbProtos, '');
+ $bufferLevels = array_fill(0, $nbProtos, 0);
+ $buffersInWildcard = array();
+
+ $tokens = token_get_all($resource->getContent());
+ $calls = array();
+
+ while ($token = array_shift($tokens)) {
+ $current = self::tokenToString($token);
+ // loop through each prototype (by reference)
+ foreach (array_keys($this->prototypes) as $i) {
+ $prototype = & $this->prototypes[$i][0];
+ $options = $this->prototypes[$i][1];
+ $buffer = & $buffers[$i];
+ $level = & $bufferLevels[$i];
+
+ if (isset($buffersInWildcard[$i])) {
+ switch ($current) {
+ case '(': ++$level; break;
+ case ')': --$level; break;
+ }
+
+ $buffer .= $current;
+
+ if (!$level) {
+ $calls[] = array($buffer.';', $options);
+ $buffer = '';
+ unset($buffersInWildcard[$i]);
+ }
+ } elseif ($current == self::tokenToString(current($prototype))) {
+ $buffer .= $current;
+ if ('*' == self::tokenToString(next($prototype))) {
+ $buffersInWildcard[$i] = true;
+ ++$level;
+ }
+ } else {
+ reset($prototype);
+ unset($buffersInWildcard[$i]);
+ $buffer = '';
+ }
+ }
+ }
+
+ $formulae = array();
+ foreach ($calls as $call) {
+ $formulae += call_user_func_array(array($this, 'processCall'), $call);
+ }
+
+ return $formulae;
+ }
+
+ private function processCall($call, array $protoOptions = array())
+ {
+ $tmp = FilesystemUtils::createTemporaryFile('php_formula_loader');
+ file_put_contents($tmp, implode("\n", array(
+ 'registerSetupCode(),
+ $call,
+ 'echo serialize($_call);',
+ )));
+ $args = unserialize(shell_exec('php '.escapeshellarg($tmp)));
+ unlink($tmp);
+
+ $inputs = isset($args[0]) ? self::argumentToArray($args[0]) : array();
+ $filters = isset($args[1]) ? self::argumentToArray($args[1]) : array();
+ $options = isset($args[2]) ? $args[2] : array();
+
+ if (!isset($options['debug'])) {
+ $options['debug'] = $this->factory->isDebug();
+ }
+
+ if (!is_array($options)) {
+ throw new \RuntimeException('The third argument must be omitted, null or an array.');
+ }
+
+ // apply the prototype options
+ $options += $protoOptions;
+
+ if (!isset($options['name'])) {
+ $options['name'] = $this->factory->generateAssetName($inputs, $filters, $options);
+ }
+
+ return array($options['name'] => array($inputs, $filters, $options));
+ }
+
+ /**
+ * Returns an array of prototypical calls and options.
+ *
+ * @return array Prototypes and options
+ */
+ abstract protected function registerPrototypes();
+
+ /**
+ * Returns setup code for the reflection scriptlet.
+ *
+ * @return string Some PHP setup code
+ */
+ abstract protected function registerSetupCode();
+
+ protected static function tokenToString($token)
+ {
+ return is_array($token) ? $token[1] : $token;
+ }
+
+ protected static function argumentToArray($argument)
+ {
+ return is_array($argument) ? $argument : array_filter(array_map('trim', explode(',', $argument)));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/CachedFormulaLoader.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/CachedFormulaLoader.php
new file mode 100644
index 0000000..9ab002b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/CachedFormulaLoader.php
@@ -0,0 +1,68 @@
+
+ */
+class CachedFormulaLoader implements FormulaLoaderInterface
+{
+ private $loader;
+ private $configCache;
+ private $debug;
+
+ /**
+ * Constructor.
+ *
+ * When the loader is in debug mode it will ensure the cached formulae
+ * are fresh before returning them.
+ *
+ * @param FormulaLoaderInterface $loader A formula loader
+ * @param ConfigCache $configCache A config cache
+ * @param Boolean $debug The debug mode
+ */
+ public function __construct(FormulaLoaderInterface $loader, ConfigCache $configCache, $debug = false)
+ {
+ $this->loader = $loader;
+ $this->configCache = $configCache;
+ $this->debug = $debug;
+ }
+
+ public function load(ResourceInterface $resources)
+ {
+ if (!$resources instanceof IteratorResourceInterface) {
+ $resources = array($resources);
+ }
+
+ $formulae = array();
+
+ foreach ($resources as $resource) {
+ $id = (string) $resource;
+ if (!$this->configCache->has($id) || ($this->debug && !$resource->isFresh($this->configCache->getTimestamp($id)))) {
+ $formulae += $this->loader->load($resource);
+ $this->configCache->set($id, $formulae);
+ } else {
+ $formulae += $this->configCache->get($id);
+ }
+ }
+
+ return $formulae;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/FormulaLoaderInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/FormulaLoaderInterface.php
new file mode 100644
index 0000000..fc45e86
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/FormulaLoaderInterface.php
@@ -0,0 +1,34 @@
+
+ */
+interface FormulaLoaderInterface
+{
+ /**
+ * Loads formulae from a resource.
+ *
+ * Formulae should be loaded the same regardless of the current debug
+ * mode. Debug considerations should happen downstream.
+ *
+ * @param ResourceInterface $resource A resource
+ *
+ * @return array An array of formulae
+ */
+ public function load(ResourceInterface $resource);
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/FunctionCallsFormulaLoader.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/FunctionCallsFormulaLoader.php
new file mode 100644
index 0000000..58b56e1
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/FunctionCallsFormulaLoader.php
@@ -0,0 +1,53 @@
+
+ */
+class FunctionCallsFormulaLoader extends BasePhpFormulaLoader
+{
+ protected function registerPrototypes()
+ {
+ return array(
+ 'assetic_javascripts(*)' => array('output' => 'js/*.js'),
+ 'assetic_stylesheets(*)' => array('output' => 'css/*.css'),
+ 'assetic_image(*)' => array('output' => 'images/*'),
+ );
+ }
+
+ protected function registerSetupCode()
+ {
+ return <<<'EOF'
+function assetic_javascripts()
+{
+ global $_call;
+ $_call = func_get_args();
+}
+
+function assetic_stylesheets()
+{
+ global $_call;
+ $_call = func_get_args();
+}
+
+function assetic_image()
+{
+ global $_call;
+ $_call = func_get_args();
+}
+
+EOF;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/CoalescingDirectoryResource.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/CoalescingDirectoryResource.php
new file mode 100644
index 0000000..6c08900
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/CoalescingDirectoryResource.php
@@ -0,0 +1,112 @@
+
+ */
+class CoalescingDirectoryResource implements IteratorResourceInterface
+{
+ private $directories;
+
+ public function __construct($directories)
+ {
+ $this->directories = array();
+
+ foreach ($directories as $directory) {
+ $this->addDirectory($directory);
+ }
+ }
+
+ public function addDirectory(IteratorResourceInterface $directory)
+ {
+ $this->directories[] = $directory;
+ }
+
+ public function isFresh($timestamp)
+ {
+ foreach ($this->getFileResources() as $file) {
+ if (!$file->isFresh($timestamp)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public function getContent()
+ {
+ $parts = array();
+ foreach ($this->getFileResources() as $file) {
+ $parts[] = $file->getContent();
+ }
+
+ return implode("\n", $parts);
+ }
+
+ /**
+ * Returns a string to uniquely identify the current resource.
+ *
+ * @return string An identifying string
+ */
+ public function __toString()
+ {
+ $parts = array();
+ foreach ($this->directories as $directory) {
+ $parts[] = (string) $directory;
+ }
+
+ return implode(',', $parts);
+ }
+
+ public function getIterator()
+ {
+ return new \ArrayIterator($this->getFileResources());
+ }
+
+ /**
+ * Returns the relative version of a filename.
+ *
+ * @param ResourceInterface $file The file
+ * @param ResourceInterface $directory The directory
+ *
+ * @return string The name to compare with files from other directories
+ */
+ protected function getRelativeName(ResourceInterface $file, ResourceInterface $directory)
+ {
+ return substr((string) $file, strlen((string) $directory));
+ }
+
+ /**
+ * Performs the coalesce.
+ *
+ * @return array An array of file resources
+ */
+ private function getFileResources()
+ {
+ $paths = array();
+
+ foreach ($this->directories as $directory) {
+ foreach ($directory as $file) {
+ $relative = $this->getRelativeName($file, $directory);
+
+ if (!isset($paths[$relative])) {
+ $paths[$relative] = $file;
+ }
+ }
+ }
+
+ return array_values($paths);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/DirectoryResource.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/DirectoryResource.php
new file mode 100644
index 0000000..c823de2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/DirectoryResource.php
@@ -0,0 +1,133 @@
+
+ */
+class DirectoryResource implements IteratorResourceInterface
+{
+ private $path;
+ private $pattern;
+
+ /**
+ * Constructor.
+ *
+ * @param string $path A directory path
+ * @param string $pattern A filename pattern
+ */
+ public function __construct($path, $pattern = null)
+ {
+ if (DIRECTORY_SEPARATOR != substr($path, -1)) {
+ $path .= DIRECTORY_SEPARATOR;
+ }
+
+ $this->path = $path;
+ $this->pattern = $pattern;
+ }
+
+ public function isFresh($timestamp)
+ {
+ if (!is_dir($this->path) || filemtime($this->path) > $timestamp) {
+ return false;
+ }
+
+ foreach ($this as $resource) {
+ if (!$resource->isFresh($timestamp)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the combined content of all inner resources.
+ */
+ public function getContent()
+ {
+ $content = array();
+ foreach ($this as $resource) {
+ $content[] = $resource->getContent();
+ }
+
+ return implode("\n", $content);
+ }
+
+ public function __toString()
+ {
+ return $this->path;
+ }
+
+ public function getIterator()
+ {
+ return is_dir($this->path)
+ ? new DirectoryResourceIterator($this->getInnerIterator())
+ : new \EmptyIterator();
+ }
+
+ protected function getInnerIterator()
+ {
+ return new DirectoryResourceFilterIterator(new \RecursiveDirectoryIterator($this->path, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS), $this->pattern);
+ }
+}
+
+/**
+ * An iterator that converts file objects into file resources.
+ *
+ * @author Kris Wallsmith
+ * @access private
+ */
+class DirectoryResourceIterator extends \RecursiveIteratorIterator
+{
+ public function current()
+ {
+ return new FileResource(parent::current()->getPathname());
+ }
+}
+
+/**
+ * Filters files by a basename pattern.
+ *
+ * @author Kris Wallsmith
+ * @access private
+ */
+class DirectoryResourceFilterIterator extends \RecursiveFilterIterator
+{
+ protected $pattern;
+
+ public function __construct(\RecursiveDirectoryIterator $iterator, $pattern = null)
+ {
+ parent::__construct($iterator);
+
+ $this->pattern = $pattern;
+ }
+
+ public function accept()
+ {
+ $file = $this->current();
+ $name = $file->getBasename();
+
+ if ($file->isDir()) {
+ return '.' != $name[0];
+ }
+
+ return null === $this->pattern || 0 < preg_match($this->pattern, $name);
+ }
+
+ public function getChildren()
+ {
+ return new self(new \RecursiveDirectoryIterator($this->current()->getPathname(), \RecursiveDirectoryIterator::FOLLOW_SYMLINKS), $this->pattern);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/FileResource.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/FileResource.php
new file mode 100644
index 0000000..b7760e1
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/FileResource.php
@@ -0,0 +1,47 @@
+
+ */
+class FileResource implements ResourceInterface
+{
+ private $path;
+
+ /**
+ * Constructor.
+ *
+ * @param string $path The path to a file
+ */
+ public function __construct($path)
+ {
+ $this->path = $path;
+ }
+
+ public function isFresh($timestamp)
+ {
+ return file_exists($this->path) && filemtime($this->path) <= $timestamp;
+ }
+
+ public function getContent()
+ {
+ return file_exists($this->path) ? file_get_contents($this->path) : '';
+ }
+
+ public function __toString()
+ {
+ return $this->path;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/IteratorResourceInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/IteratorResourceInterface.php
new file mode 100644
index 0000000..3357332
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/IteratorResourceInterface.php
@@ -0,0 +1,21 @@
+
+ */
+interface IteratorResourceInterface extends ResourceInterface, \IteratorAggregate
+{
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/ResourceInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/ResourceInterface.php
new file mode 100644
index 0000000..7eebbd8
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/ResourceInterface.php
@@ -0,0 +1,43 @@
+
+ */
+interface ResourceInterface
+{
+ /**
+ * Checks if a timestamp represents the latest resource.
+ *
+ * @param integer $timestamp A UNIX timestamp
+ *
+ * @return Boolean True if the timestamp is up to date
+ */
+ public function isFresh($timestamp);
+
+ /**
+ * Returns the content of the resource.
+ *
+ * @return string The content
+ */
+ public function getContent();
+
+ /**
+ * Returns a unique string for the current resource.
+ *
+ * @return string A unique string to identity the current resource
+ */
+ public function __toString();
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/CacheBustingWorker.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/CacheBustingWorker.php
new file mode 100644
index 0000000..712c90d
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/CacheBustingWorker.php
@@ -0,0 +1,70 @@
+
+ */
+class CacheBustingWorker implements WorkerInterface
+{
+ private $separator;
+
+ public function __construct($separator = '-')
+ {
+ $this->separator = $separator;
+ }
+
+ public function process(AssetInterface $asset, AssetFactory $factory)
+ {
+ if (!$path = $asset->getTargetPath()) {
+ // no path to work with
+ return;
+ }
+
+ if (!$search = pathinfo($path, PATHINFO_EXTENSION)) {
+ // nothing to replace
+ return;
+ }
+
+ $replace = $this->separator.$this->getHash($asset, $factory).'.'.$search;
+ if (preg_match('/'.preg_quote($replace, '/').'$/', $path)) {
+ // already replaced
+ return;
+ }
+
+ $asset->setTargetPath(
+ preg_replace('/\.'.preg_quote($search, '/').'$/', $replace, $path)
+ );
+ }
+
+ protected function getHash(AssetInterface $asset, AssetFactory $factory)
+ {
+ $hash = hash_init('sha1');
+
+ hash_update($hash, $factory->getLastModified($asset));
+
+ if ($asset instanceof AssetCollectionInterface) {
+ foreach ($asset as $i => $leaf) {
+ $sourcePath = $leaf->getSourcePath();
+ hash_update($hash, $sourcePath ?: $i);
+ }
+ }
+
+ return substr(hash_final($hash), 0, 7);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/EnsureFilterWorker.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/EnsureFilterWorker.php
new file mode 100644
index 0000000..80fd44a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/EnsureFilterWorker.php
@@ -0,0 +1,61 @@
+
+ * @todo A better asset-matcher mechanism
+ */
+class EnsureFilterWorker implements WorkerInterface
+{
+ const CHECK_SOURCE = 1;
+ const CHECK_TARGET = 2;
+
+ private $pattern;
+ private $filter;
+ private $flags;
+
+ /**
+ * Constructor.
+ *
+ * @param string $pattern A regex for checking the asset's target URL
+ * @param FilterInterface $filter A filter to apply if the regex matches
+ * @param integer $flags Flags for what to check
+ */
+ public function __construct($pattern, FilterInterface $filter, $flags = null)
+ {
+ if (null === $flags) {
+ $flags = self::CHECK_SOURCE | self::CHECK_TARGET;
+ }
+
+ $this->pattern = $pattern;
+ $this->filter = $filter;
+ $this->flags = $flags;
+ }
+
+ public function process(AssetInterface $asset, AssetFactory $factory)
+ {
+ if (
+ (self::CHECK_SOURCE === (self::CHECK_SOURCE & $this->flags) && preg_match($this->pattern, $asset->getSourcePath()))
+ ||
+ (self::CHECK_TARGET === (self::CHECK_TARGET & $this->flags) && preg_match($this->pattern, $asset->getTargetPath()))
+ ) {
+ $asset->ensureFilter($this->filter);
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/WorkerInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/WorkerInterface.php
new file mode 100644
index 0000000..e86cc7b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/WorkerInterface.php
@@ -0,0 +1,33 @@
+
+ */
+interface WorkerInterface
+{
+ /**
+ * Processes an asset.
+ *
+ * @param AssetInterface $asset An asset
+ * @param AssetFactory $factory The factory
+ *
+ * @return AssetInterface|null May optionally return a replacement asset
+ */
+ public function process(AssetInterface $asset, AssetFactory $factory);
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/AutoprefixerFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/AutoprefixerFilter.php
new file mode 100644
index 0000000..5182161
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/AutoprefixerFilter.php
@@ -0,0 +1,87 @@
+
+ */
+class AutoprefixerFilter extends BaseNodeFilter
+{
+ /**
+ * @var string
+ */
+ private $autoprefixerBin;
+
+ /**
+ * @var array
+ */
+ private $browsers = array();
+
+ public function __construct($autoprefixerBin)
+ {
+ $this->autoprefixerBin = $autoprefixerBin;
+ }
+
+ /**
+ * @param array $browsers
+ */
+ public function setBrowsers(array $browsers)
+ {
+ $this->browsers = $browsers;
+ }
+
+ /**
+ * @param string $browser
+ */
+ public function addBrowser($browser)
+ {
+ $this->browsers[] = $browser;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ $input = $asset->getContent();
+ $pb = $this->createProcessBuilder(array($this->autoprefixerBin));
+
+ $pb->setInput($input);
+ if ($this->browsers) {
+ $pb->add('-b')->add(implode(',', $this->browsers));
+ }
+
+ $output = FilesystemUtils::createTemporaryFile('autoprefixer');
+ $pb->add('-o')->add($output);
+
+ $proc = $pb->getProcess();
+ if (0 !== $proc->run()) {
+ throw FilterException::fromProcess($proc)->setInput($asset->getContent());
+ }
+
+ $asset->setContent(file_get_contents($output));
+ unlink($output);
+ }
+
+ /**
+ * Filters an asset just before it's dumped.
+ *
+ * @param AssetInterface $asset An asset
+ */
+ public function filterDump(AssetInterface $asset)
+ {
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseCssFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseCssFilter.php
new file mode 100644
index 0000000..8d65271
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseCssFilter.php
@@ -0,0 +1,54 @@
+
+ */
+abstract class BaseCssFilter implements FilterInterface
+{
+ /**
+ * @see CssUtils::filterReferences()
+ */
+ protected function filterReferences($content, $callback, $limit = -1, &$count = 0)
+ {
+ return CssUtils::filterReferences($content, $callback, $limit, $count);
+ }
+
+ /**
+ * @see CssUtils::filterUrls()
+ */
+ protected function filterUrls($content, $callback, $limit = -1, &$count = 0)
+ {
+ return CssUtils::filterUrls($content, $callback, $limit, $count);
+ }
+
+ /**
+ * @see CssUtils::filterImports()
+ */
+ protected function filterImports($content, $callback, $limit = -1, &$count = 0, $includeUrl = true)
+ {
+ return CssUtils::filterImports($content, $callback, $limit, $count, $includeUrl);
+ }
+
+ /**
+ * @see CssUtils::filterIEFilters()
+ */
+ protected function filterIEFilters($content, $callback, $limit = -1, &$count = 0)
+ {
+ return CssUtils::filterIEFilters($content, $callback, $limit, $count);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseNodeFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseNodeFilter.php
new file mode 100644
index 0000000..64e5a13
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseNodeFilter.php
@@ -0,0 +1,44 @@
+nodePaths;
+ }
+
+ public function setNodePaths(array $nodePaths)
+ {
+ $this->nodePaths = $nodePaths;
+ }
+
+ public function addNodePath($nodePath)
+ {
+ $this->nodePaths[] = $nodePath;
+ }
+
+ protected function createProcessBuilder(array $arguments = array())
+ {
+ $pb = parent::createProcessBuilder($arguments);
+
+ if ($this->nodePaths) {
+ $this->mergeEnv($pb);
+ $pb->setEnv('NODE_PATH', implode(PATH_SEPARATOR, $this->nodePaths));
+ }
+
+ return $pb;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseProcessFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseProcessFilter.php
new file mode 100644
index 0000000..642495a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseProcessFilter.php
@@ -0,0 +1,57 @@
+timeout = $timeout;
+ }
+
+ /**
+ * Creates a new process builder.
+ *
+ * @param array $arguments An optional array of arguments
+ *
+ * @return ProcessBuilder A new process builder
+ */
+ protected function createProcessBuilder(array $arguments = array())
+ {
+ $pb = new ProcessBuilder($arguments);
+
+ if (null !== $this->timeout) {
+ $pb->setTimeout($this->timeout);
+ }
+
+ return $pb;
+ }
+
+ protected function mergeEnv(ProcessBuilder $pb)
+ {
+ foreach (array_filter($_SERVER, 'is_scalar') as $key => $value) {
+ $pb->setEnv($key, $value);
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CallablesFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CallablesFilter.php
new file mode 100644
index 0000000..b81f201
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CallablesFilter.php
@@ -0,0 +1,62 @@
+
+ */
+class CallablesFilter implements FilterInterface, DependencyExtractorInterface
+{
+ private $loader;
+ private $dumper;
+ private $extractor;
+
+ /**
+ * @param callable|null $loader
+ * @param callable|null $dumper
+ * @param callable|null $extractor
+ */
+ public function __construct($loader = null, $dumper = null, $extractor = null)
+ {
+ $this->loader = $loader;
+ $this->dumper = $dumper;
+ $this->extractor = $extractor;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ if (null !== $callable = $this->loader) {
+ $callable($asset);
+ }
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ if (null !== $callable = $this->dumper) {
+ $callable($asset);
+ }
+ }
+
+ public function getChildren(AssetFactory $factory, $content, $loadPath = null)
+ {
+ if (null !== $callable = $this->extractor) {
+ return $callable($factory, $content, $loadPath);
+ }
+
+ return array();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CleanCssFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CleanCssFilter.php
new file mode 100644
index 0000000..ada5499
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CleanCssFilter.php
@@ -0,0 +1,343 @@
+
+ */
+class CleanCssFilter extends BaseNodeFilter
+{
+ private $cleanCssBin;
+ private $nodeBin;
+
+ private $keepLineBreaks;
+ private $compatibility;
+ private $debug;
+ private $rootPath;
+ private $skipImport = true;
+ private $timeout;
+ private $semanticMerging;
+ private $roundingPrecision;
+ private $removeSpecialComments;
+ private $onlyKeepFirstSpecialComment;
+ private $skipAdvanced;
+ private $skipAggresiveMerging;
+ private $skipImportFrom;
+ private $mediaMerging;
+ private $skipRebase;
+ private $skipRestructuring;
+ private $skipShorthandCompacting;
+ private $sourceMap;
+ private $sourceMapInlineSources;
+
+
+ /**
+ * @param string $cleanCssBin Absolute path to the cleancss executable
+ * @param string $nodeBin Absolute path to the folder containg node.js executable
+ */
+ public function __construct($cleanCssBin = '/usr/bin/cleancss', $nodeBin = null)
+ {
+ $this->cleanCssBin = $cleanCssBin;
+ $this->nodeBin = $nodeBin;
+ }
+
+ /**
+ * Keep line breaks
+ * @param bool $keepLineBreaks True to enable
+ */
+ public function setKeepLineBreaks($keepLineBreaks)
+ {
+ $this->keepLineBreaks = $keepLineBreaks;
+ }
+
+ /**
+ * Remove all special comments
+ * @param bool $removeSpecialComments True to enable
+ */ // i.e. /*! comment */
+ public function setRemoveSpecialComments($removeSpecialComments)
+ {
+ $this->removeSpecialComments = $removeSpecialComments;
+ }
+
+ /**
+ * Remove all special comments except the first one
+ * @param bool $onlyKeepFirstSpecialComment True to enable
+ */
+ public function setOnlyKeepFirstSpecialComment($onlyKeepFirstSpecialComment)
+ {
+ $this->onlyKeepFirstSpecialComment = $onlyKeepFirstSpecialComment;
+ }
+ /**
+ * Enables unsafe mode by assuming BEM-like semantic stylesheets (warning, this may break your styling!)
+ * @param bool $semanticMerging True to enable
+ */
+ public function setSemanticMerging($semanticMerging)
+ {
+ $this->semanticMerging = $semanticMerging;
+ }
+
+ /**
+ * A root path to which resolve absolute @import rules
+ * @param string $rootPath
+ */
+ public function setRootPath($rootPath)
+ {
+ $this->rootPath = $rootPath;
+ }
+
+ /**
+ * Disable @import processing
+ * @param bool $skipImport True to enable
+ */
+ public function setSkipImport($skipImport)
+ {
+ $this->skipImport = $skipImport;
+ }
+ /**
+ * Per connection timeout when fetching remote @imports; defaults to 5 seconds
+ * @param int $timeout
+ */
+ public function setTimeout($timeout)
+ {
+ $this->timeout = $timeout;
+ }
+
+ /**
+ * Disable URLs rebasing
+ * @param bool $skipRebase True to enable
+ */
+ public function setSkipRebase($skipRebase)
+ {
+ $this->skipRebase = $skipRebase;
+ }
+
+ /**
+ * Disable restructuring optimizations
+ * @param bool $skipRestructuring True to enable
+ */
+ public function setSkipRestructuring($skipRestructuring)
+ {
+ $this->skipRestructuring = $skipRestructuring;
+ }
+
+ /**
+ * Disable shorthand compacting
+ * @param bool $skipShorthandCompacting True to enable
+ */
+ public function setSkipShorthandCompacting($skipShorthandCompacting)
+ {
+ $this->skipShorthandCompacting = $skipShorthandCompacting;
+ }
+
+ /**
+ * Enables building input's source map
+ * @param bool $sourceMap True to enable
+ */
+ public function setSourceMap($sourceMap)
+ {
+ $this->sourceMap = $sourceMap;
+ }
+
+ /**
+ * Enables inlining sources inside source maps
+ * @param bool $sourceMapInlineSources True to enable
+ */
+ public function setSourceMapInlineSources($sourceMapInlineSources)
+ {
+ $this->sourceMapInlineSources = $sourceMapInlineSources;
+ }
+
+ /**
+ * Disable advanced optimizations - selector & property merging, reduction, etc.
+ * @param bool $skipAdvanced True to enable
+ */
+ public function setSkipAdvanced($skipAdvanced)
+ {
+ $this->skipAdvanced = $skipAdvanced;
+ }
+
+ /**
+ * Disable properties merging based on their order
+ * @param bool $skipAggresiveMerging True to enable
+ */
+ public function setSkipAggresiveMerging($skipAggresiveMerging)
+ {
+ $this->skipAggresiveMerging = $skipAggresiveMerging;
+ }
+
+ /**
+ * Disable @import processing for specified rules
+ * @param string $skipImportFrom
+ */
+ public function setSkipImportFrom($skipImportFrom)
+ {
+ $this->skipImportFrom = $skipImportFrom;
+ }
+
+ /**
+ * Disable @media merging
+ * @param bool $mediaMerging True to enable
+ */
+ public function setMediaMerging($mediaMerging)
+ {
+ $this->mediaMerging = $mediaMerging;
+ }
+
+ /**
+ * Rounds to `N` decimal places. Defaults to 2. -1 disables rounding.
+ * @param int $roundingPrecision
+ */
+ public function setRoundingPrecision($roundingPrecision)
+ {
+ $this->roundingPrecision = $roundingPrecision;
+ }
+
+ /**
+ * Force compatibility mode (see https://github.com/jakubpawlowicz/clean-css/blob/master/README.md#how-to-set-compatibility-mode for advanced examples)
+ * @param string $compatibility
+ */
+ public function setCompatibility($compatibility)
+ {
+ $this->compatibility = $compatibility;
+ }
+
+ /**
+ * Shows debug information (minification time & compression efficiency)
+ * @param bool $debug True to enable
+ */
+ public function setDebug($debug)
+ {
+ $this->debug = $debug;
+ }
+
+
+ /**
+ * @see Assetic\Filter\FilterInterface::filterLoad()
+ */
+ public function filterLoad(AssetInterface $asset)
+ {
+ }
+
+
+ /**
+ * Run the asset through CleanCss
+ *
+ * @see Assetic\Filter\FilterInterface::filterDump()
+ */
+ public function filterDump(AssetInterface $asset)
+ {
+ $pb = $this->createProcessBuilder($this->nodeBin
+ ? array($this->nodeBin, $this->cleanCssBin)
+ : array($this->cleanCssBin));
+
+ if ($this->keepLineBreaks) {
+ $pb->add('--keep-line-breaks');
+ }
+
+ if ($this->compatibility) {
+ $pb->add('--compatibility ' .$this->compatibility);
+ }
+
+ if ($this->debug) {
+ $pb->add('--debug');
+ }
+
+ if ($this->rootPath) {
+ $pb->add('--root ' .$this->rootPath);
+ }
+
+ if ($this->skipImport) {
+ $pb->add('--skip-import');
+ }
+
+ if ($this->timeout) {
+ $pb->add('--timeout ' .$this->timeout);
+ }
+
+ if ($this->roundingPrecision) {
+ $pb->add('--rounding-precision ' .$this->roundingPrecision);
+ }
+
+ if ($this->removeSpecialComments) {
+ $pb->add('--s0');
+ }
+
+ if ($this->onlyKeepFirstSpecialComment) {
+ $pb->add('--s1');
+ }
+
+ if ($this->semanticMerging) {
+ $pb->add('--semantic-merging');
+ }
+
+ if ($this->skipAdvanced) {
+ $pb->add('--skip-advanced');
+ }
+
+ if ($this->skipAggresiveMerging) {
+ $pb->add('--skip-aggressive-merging');
+ }
+
+ if ($this->skipImportFrom) {
+ $pb->add('--skip-import-from ' .$this->skipImportFrom);
+ }
+
+ if ($this->mediaMerging) {
+ $pb->add('--skip-media-merging');
+ }
+
+ if ($this->skipRebase) {
+ $pb->add('--skip-rebase');
+ }
+
+ if ($this->skipRestructuring) {
+ $pb->add('--skip-restructuring');
+ }
+
+ if ($this->skipShorthandCompacting) {
+ $pb->add('--skip-shorthand-compacting');
+ }
+
+ if ($this->sourceMap) {
+ $pb->add('--source-map');
+ }
+
+ if ($this->sourceMapInlineSources) {
+ $pb->add('--source-map-inline-sources');
+ }
+ // input and output files
+ $input = tempnam(sys_get_temp_dir(), 'input');
+
+ file_put_contents($input, $asset->getContent());
+ $pb->add($input);
+
+ $proc = $pb->getProcess();
+ $code = $proc->run();
+ unlink($input);
+
+ if (127 === $code) {
+ throw new \RuntimeException('Path to node executable could not be resolved.');
+ }
+
+ if (0 !== $code) {
+ throw FilterException::fromProcess($proc)->setInput($asset->getContent());
+ }
+
+ $asset->setContent($proc->getOutput());
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CoffeeScriptFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CoffeeScriptFilter.php
new file mode 100644
index 0000000..a5cc818
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CoffeeScriptFilter.php
@@ -0,0 +1,83 @@
+
+ */
+class CoffeeScriptFilter extends BaseNodeFilter
+{
+ private $coffeeBin;
+ private $nodeBin;
+
+ // coffee options
+ private $bare;
+ private $noHeader;
+
+ public function __construct($coffeeBin = '/usr/bin/coffee', $nodeBin = null)
+ {
+ $this->coffeeBin = $coffeeBin;
+ $this->nodeBin = $nodeBin;
+ }
+
+ public function setBare($bare)
+ {
+ $this->bare = $bare;
+ }
+
+ public function setNoHeader($noHeader)
+ {
+ $this->noHeader = $noHeader;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ $input = FilesystemUtils::createTemporaryFile('coffee');
+ file_put_contents($input, $asset->getContent());
+
+ $pb = $this->createProcessBuilder($this->nodeBin
+ ? array($this->nodeBin, $this->coffeeBin)
+ : array($this->coffeeBin));
+
+ $pb->add('-cp');
+
+ if ($this->bare) {
+ $pb->add('--bare');
+ }
+
+ if ($this->noHeader) {
+ $pb->add('--no-header');
+ }
+
+ $pb->add($input);
+ $proc = $pb->getProcess();
+ $code = $proc->run();
+ unlink($input);
+
+ if (0 !== $code) {
+ throw FilterException::fromProcess($proc)->setInput($asset->getContent());
+ }
+
+ $asset->setContent($proc->getOutput());
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CompassFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CompassFilter.php
new file mode 100644
index 0000000..d80a1ab
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CompassFilter.php
@@ -0,0 +1,391 @@
+
+ */
+class CompassFilter extends BaseSassFilter
+{
+ private $compassPath;
+ private $rubyPath;
+ private $scss;
+
+ // sass options
+ private $unixNewlines;
+ private $debugInfo;
+ private $cacheLocation;
+ private $noCache;
+
+ // compass options
+ private $force;
+ private $style;
+ private $quiet;
+ private $boring;
+ private $noLineComments;
+ private $imagesDir;
+ private $javascriptsDir;
+ private $fontsDir;
+ private $relativeAssets;
+
+ // compass configuration file options
+ private $plugins = array();
+ private $httpPath;
+ private $httpImagesPath;
+ private $httpFontsPath;
+ private $httpGeneratedImagesPath;
+ private $generatedImagesPath;
+ private $httpJavascriptsPath;
+ private $homeEnv = true;
+
+ public function __construct($compassPath = '/usr/bin/compass', $rubyPath = null)
+ {
+ $this->compassPath = $compassPath;
+ $this->rubyPath = $rubyPath;
+ $this->cacheLocation = FilesystemUtils::getTemporaryDirectory();
+
+ if ('cli' !== php_sapi_name()) {
+ $this->boring = true;
+ }
+ }
+
+ public function setScss($scss)
+ {
+ $this->scss = $scss;
+ }
+
+ // sass options setters
+ public function setUnixNewlines($unixNewlines)
+ {
+ $this->unixNewlines = $unixNewlines;
+ }
+
+ public function setDebugInfo($debugInfo)
+ {
+ $this->debugInfo = $debugInfo;
+ }
+
+ public function setCacheLocation($cacheLocation)
+ {
+ $this->cacheLocation = $cacheLocation;
+ }
+
+ public function setNoCache($noCache)
+ {
+ $this->noCache = $noCache;
+ }
+
+ // compass options setters
+ public function setForce($force)
+ {
+ $this->force = $force;
+ }
+
+ public function setStyle($style)
+ {
+ $this->style = $style;
+ }
+
+ public function setQuiet($quiet)
+ {
+ $this->quiet = $quiet;
+ }
+
+ public function setBoring($boring)
+ {
+ $this->boring = $boring;
+ }
+
+ public function setNoLineComments($noLineComments)
+ {
+ $this->noLineComments = $noLineComments;
+ }
+
+ public function setImagesDir($imagesDir)
+ {
+ $this->imagesDir = $imagesDir;
+ }
+
+ public function setJavascriptsDir($javascriptsDir)
+ {
+ $this->javascriptsDir = $javascriptsDir;
+ }
+
+ public function setFontsDir($fontsDir)
+ {
+ $this->fontsDir = $fontsDir;
+ }
+
+ // compass configuration file options setters
+ public function setPlugins(array $plugins)
+ {
+ $this->plugins = $plugins;
+ }
+
+ public function addPlugin($plugin)
+ {
+ $this->plugins[] = $plugin;
+ }
+
+ public function setHttpPath($httpPath)
+ {
+ $this->httpPath = $httpPath;
+ }
+
+ public function setHttpImagesPath($httpImagesPath)
+ {
+ $this->httpImagesPath = $httpImagesPath;
+ }
+
+ public function setHttpFontsPath($httpFontsPath)
+ {
+ $this->httpFontsPath = $httpFontsPath;
+ }
+
+ public function setHttpGeneratedImagesPath($httpGeneratedImagesPath)
+ {
+ $this->httpGeneratedImagesPath = $httpGeneratedImagesPath;
+ }
+
+ public function setGeneratedImagesPath($generatedImagesPath)
+ {
+ $this->generatedImagesPath = $generatedImagesPath;
+ }
+
+ public function setHttpJavascriptsPath($httpJavascriptsPath)
+ {
+ $this->httpJavascriptsPath = $httpJavascriptsPath;
+ }
+
+ public function setHomeEnv($homeEnv)
+ {
+ $this->homeEnv = $homeEnv;
+ }
+
+ public function setRelativeAssets($relativeAssets)
+ {
+ $this->relativeAssets = $relativeAssets;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ $loadPaths = $this->loadPaths;
+ if ($dir = $asset->getSourceDirectory()) {
+ $loadPaths[] = $dir;
+ }
+
+ $tempDir = $this->cacheLocation ? $this->cacheLocation : FilesystemUtils::getTemporaryDirectory();
+
+ $compassProcessArgs = array(
+ $this->compassPath,
+ 'compile',
+ $tempDir,
+ );
+ if (null !== $this->rubyPath) {
+ $compassProcessArgs = array_merge(explode(' ', $this->rubyPath), $compassProcessArgs);
+ }
+
+ $pb = $this->createProcessBuilder($compassProcessArgs);
+
+ if ($this->force) {
+ $pb->add('--force');
+ }
+
+ if ($this->style) {
+ $pb->add('--output-style')->add($this->style);
+ }
+
+ if ($this->quiet) {
+ $pb->add('--quiet');
+ }
+
+ if ($this->boring) {
+ $pb->add('--boring');
+ }
+
+ if ($this->noLineComments) {
+ $pb->add('--no-line-comments');
+ }
+
+ // these three options are not passed into the config file
+ // because like this, compass adapts this to be xxx_dir or xxx_path
+ // whether it's an absolute path or not
+ if ($this->imagesDir) {
+ $pb->add('--images-dir')->add($this->imagesDir);
+ }
+
+ if ($this->relativeAssets) {
+ $pb->add('--relative-assets');
+ }
+
+ if ($this->javascriptsDir) {
+ $pb->add('--javascripts-dir')->add($this->javascriptsDir);
+ }
+
+ if ($this->fontsDir) {
+ $pb->add('--fonts-dir')->add($this->fontsDir);
+ }
+
+ // options in config file
+ $optionsConfig = array();
+
+ if (!empty($loadPaths)) {
+ $optionsConfig['additional_import_paths'] = $loadPaths;
+ }
+
+ if ($this->unixNewlines) {
+ $optionsConfig['sass_options']['unix_newlines'] = true;
+ }
+
+ if ($this->debugInfo) {
+ $optionsConfig['sass_options']['debug_info'] = true;
+ }
+
+ if ($this->cacheLocation) {
+ $optionsConfig['sass_options']['cache_location'] = $this->cacheLocation;
+ }
+
+ if ($this->noCache) {
+ $optionsConfig['sass_options']['no_cache'] = true;
+ }
+
+ if ($this->httpPath) {
+ $optionsConfig['http_path'] = $this->httpPath;
+ }
+
+ if ($this->httpImagesPath) {
+ $optionsConfig['http_images_path'] = $this->httpImagesPath;
+ }
+
+ if ($this->httpFontsPath) {
+ $optionsConfig['http_fonts_path'] = $this->httpFontsPath;
+ }
+
+ if ($this->httpGeneratedImagesPath) {
+ $optionsConfig['http_generated_images_path'] = $this->httpGeneratedImagesPath;
+ }
+
+ if ($this->generatedImagesPath) {
+ $optionsConfig['generated_images_path'] = $this->generatedImagesPath;
+ }
+
+ if ($this->httpJavascriptsPath) {
+ $optionsConfig['http_javascripts_path'] = $this->httpJavascriptsPath;
+ }
+
+ // options in configuration file
+ if (count($optionsConfig)) {
+ $config = array();
+ foreach ($this->plugins as $plugin) {
+ $config[] = sprintf("require '%s'", addcslashes($plugin, '\\'));
+ }
+ foreach ($optionsConfig as $name => $value) {
+ if (!is_array($value)) {
+ $config[] = sprintf('%s = "%s"', $name, addcslashes($value, '\\'));
+ } elseif (!empty($value)) {
+ $config[] = sprintf('%s = %s', $name, $this->formatArrayToRuby($value));
+ }
+ }
+
+ $configFile = tempnam($tempDir, 'assetic_compass');
+ file_put_contents($configFile, implode("\n", $config)."\n");
+ $pb->add('--config')->add($configFile);
+ }
+
+ $pb->add('--sass-dir')->add('')->add('--css-dir')->add('');
+
+ // compass choose the type (sass or scss from the filename)
+ if (null !== $this->scss) {
+ $type = $this->scss ? 'scss' : 'sass';
+ } elseif ($path = $asset->getSourcePath()) {
+ // FIXME: what if the extension is something else?
+ $type = pathinfo($path, PATHINFO_EXTENSION);
+ } else {
+ $type = 'scss';
+ }
+
+ $tempName = tempnam($tempDir, 'assetic_compass');
+ unlink($tempName); // FIXME: don't use tempnam() here
+
+ // input
+ $input = $tempName.'.'.$type;
+
+ // work-around for https://github.com/chriseppstein/compass/issues/748
+ if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
+ $input = str_replace('\\', '/', $input);
+ }
+
+ $pb->add($input);
+ file_put_contents($input, $asset->getContent());
+
+ // output
+ $output = $tempName.'.css';
+
+ if ($this->homeEnv) {
+ // it's not really usefull but... https://github.com/chriseppstein/compass/issues/376
+ $pb->setEnv('HOME', FilesystemUtils::getTemporaryDirectory());
+ $this->mergeEnv($pb);
+ }
+
+ $proc = $pb->getProcess();
+ $code = $proc->run();
+
+ if (0 !== $code) {
+ unlink($input);
+ if (isset($configFile)) {
+ unlink($configFile);
+ }
+
+ throw FilterException::fromProcess($proc)->setInput($asset->getContent());
+ }
+
+ $asset->setContent(file_get_contents($output));
+
+ unlink($input);
+ unlink($output);
+ if (isset($configFile)) {
+ unlink($configFile);
+ }
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ }
+
+ private function formatArrayToRuby($array)
+ {
+ $output = array();
+
+ // does we have an associative array ?
+ if (count(array_filter(array_keys($array), "is_numeric")) != count($array)) {
+ foreach ($array as $name => $value) {
+ $output[] = sprintf(' :%s => "%s"', $name, addcslashes($value, '\\'));
+ }
+ $output = "{\n".implode(",\n", $output)."\n}";
+ } else {
+ foreach ($array as $name => $value) {
+ $output[] = sprintf(' "%s"', addcslashes($value, '\\'));
+ }
+ $output = "[\n".implode(",\n", $output)."\n]";
+ }
+
+ return $output;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssCacheBustingFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssCacheBustingFilter.php
new file mode 100644
index 0000000..5bf8cec
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssCacheBustingFilter.php
@@ -0,0 +1,65 @@
+
+ */
+class CssCacheBustingFilter extends BaseCssFilter
+{
+ private $version;
+ private $format = '%s?%s';
+
+ public function setVersion($version)
+ {
+ $this->version = $version;
+ }
+
+ public function setFormat($versionFormat)
+ {
+ $this->format = $versionFormat;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ if (!$this->version) {
+ return;
+ }
+
+ $version = $this->version;
+ $format = $this->format;
+
+ $asset->setContent($this->filterReferences(
+ $asset->getContent(),
+ function ($matches) use ($version, $format) {
+ if (0 === strpos($matches['url'], 'data:')) {
+ return $matches[0];
+ }
+
+ return str_replace(
+ $matches['url'],
+ sprintf($format, $matches['url'], $version),
+ $matches[0]
+ );
+ }
+ ));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssEmbedFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssEmbedFilter.php
new file mode 100644
index 0000000..17970a9
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssEmbedFilter.php
@@ -0,0 +1,143 @@
+
+ */
+class CssEmbedFilter extends BaseProcessFilter implements DependencyExtractorInterface
+{
+ private $jarPath;
+ private $javaPath;
+ private $charset;
+ private $mhtml; // Enable MHTML mode.
+ private $mhtmlRoot; // Use as the MHTML root for the file.
+ private $root; // Prepends to all relative URLs.
+ private $skipMissing; // Don't throw an error for missing image files.
+ private $maxUriLength; // Maximum length for a data URI. Defaults to 32768.
+ private $maxImageSize; // Maximum image size (in bytes) to convert.
+
+ public function __construct($jarPath, $javaPath = '/usr/bin/java')
+ {
+ $this->jarPath = $jarPath;
+ $this->javaPath = $javaPath;
+ }
+
+ public function setCharset($charset)
+ {
+ $this->charset = $charset;
+ }
+
+ public function setMhtml($mhtml)
+ {
+ $this->mhtml = $mhtml;
+ }
+
+ public function setMhtmlRoot($mhtmlRoot)
+ {
+ $this->mhtmlRoot = $mhtmlRoot;
+ }
+
+ public function setRoot($root)
+ {
+ $this->root = $root;
+ }
+
+ public function setSkipMissing($skipMissing)
+ {
+ $this->skipMissing = $skipMissing;
+ }
+
+ public function setMaxUriLength($maxUriLength)
+ {
+ $this->maxUriLength = $maxUriLength;
+ }
+
+ public function setMaxImageSize($maxImageSize)
+ {
+ $this->maxImageSize = $maxImageSize;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ $pb = $this->createProcessBuilder(array(
+ $this->javaPath,
+ '-jar',
+ $this->jarPath,
+ ));
+
+ if (null !== $this->charset) {
+ $pb->add('--charset')->add($this->charset);
+ }
+
+ if ($this->mhtml) {
+ $pb->add('--mhtml');
+ }
+
+ if (null !== $this->mhtmlRoot) {
+ $pb->add('--mhtmlroot')->add($this->mhtmlRoot);
+ }
+
+ // automatically define root if not already defined
+ if (null === $this->root) {
+ if ($dir = $asset->getSourceDirectory()) {
+ $pb->add('--root')->add($dir);
+ }
+ } else {
+ $pb->add('--root')->add($this->root);
+ }
+
+ if ($this->skipMissing) {
+ $pb->add('--skip-missing');
+ }
+
+ if (null !== $this->maxUriLength) {
+ $pb->add('--max-uri-length')->add($this->maxUriLength);
+ }
+
+ if (null !== $this->maxImageSize) {
+ $pb->add('--max-image-size')->add($this->maxImageSize);
+ }
+
+ // input
+ $pb->add($input = FilesystemUtils::createTemporaryFile('cssembed'));
+ file_put_contents($input, $asset->getContent());
+
+ $proc = $pb->getProcess();
+ $code = $proc->run();
+ unlink($input);
+
+ if (0 !== $code) {
+ throw FilterException::fromProcess($proc)->setInput($asset->getContent());
+ }
+
+ $asset->setContent($proc->getOutput());
+ }
+
+ public function getChildren(AssetFactory $factory, $content, $loadPath = null)
+ {
+ // todo
+ return array();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssImportFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssImportFilter.php
new file mode 100644
index 0000000..77ec1c4
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssImportFilter.php
@@ -0,0 +1,108 @@
+
+ */
+class CssImportFilter extends BaseCssFilter implements DependencyExtractorInterface
+{
+ private $importFilter;
+
+ /**
+ * Constructor.
+ *
+ * @param FilterInterface $importFilter Filter for each imported asset
+ */
+ public function __construct(FilterInterface $importFilter = null)
+ {
+ $this->importFilter = $importFilter ?: new CssRewriteFilter();
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ $importFilter = $this->importFilter;
+ $sourceRoot = $asset->getSourceRoot();
+ $sourcePath = $asset->getSourcePath();
+
+ $callback = function ($matches) use ($importFilter, $sourceRoot, $sourcePath) {
+ if (!$matches['url'] || null === $sourceRoot) {
+ return $matches[0];
+ }
+
+ $importRoot = $sourceRoot;
+
+ if (false !== strpos($matches['url'], '://')) {
+ // absolute
+ list($importScheme, $tmp) = explode('://', $matches['url'], 2);
+ list($importHost, $importPath) = explode('/', $tmp, 2);
+ $importRoot = $importScheme.'://'.$importHost;
+ } elseif (0 === strpos($matches['url'], '//')) {
+ // protocol-relative
+ list($importHost, $importPath) = explode('/', substr($matches['url'], 2), 2);
+ $importRoot = '//'.$importHost;
+ } elseif ('/' == $matches['url'][0]) {
+ // root-relative
+ $importPath = substr($matches['url'], 1);
+ } elseif (null !== $sourcePath) {
+ // document-relative
+ $importPath = $matches['url'];
+ if ('.' != $sourceDir = dirname($sourcePath)) {
+ $importPath = $sourceDir.'/'.$importPath;
+ }
+ } else {
+ return $matches[0];
+ }
+
+ $importSource = $importRoot.'/'.$importPath;
+ if (false !== strpos($importSource, '://') || 0 === strpos($importSource, '//')) {
+ $import = new HttpAsset($importSource, array($importFilter), true);
+ } elseif ('css' != pathinfo($importPath, PATHINFO_EXTENSION) || !file_exists($importSource)) {
+ // ignore non-css and non-existant imports
+ return $matches[0];
+ } else {
+ $import = new FileAsset($importSource, array($importFilter), $importRoot, $importPath);
+ }
+
+ $import->setTargetPath($sourcePath);
+
+ return $import->dump();
+ };
+
+ $content = $asset->getContent();
+ $lastHash = md5($content);
+
+ do {
+ $content = $this->filterImports($content, $callback);
+ $hash = md5($content);
+ } while ($lastHash != $hash && $lastHash = $hash);
+
+ $asset->setContent($content);
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ }
+
+ public function getChildren(AssetFactory $factory, $content, $loadPath = null)
+ {
+ // todo
+ return array();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssMinFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssMinFilter.php
new file mode 100644
index 0000000..6f0b0d2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssMinFilter.php
@@ -0,0 +1,72 @@
+
+ */
+class CssMinFilter implements FilterInterface
+{
+ private $filters;
+ private $plugins;
+
+ public function __construct()
+ {
+ $this->filters = array();
+ $this->plugins = array();
+ }
+
+ public function setFilters(array $filters)
+ {
+ $this->filters = $filters;
+ }
+
+ public function setFilter($name, $value)
+ {
+ $this->filters[$name] = $value;
+ }
+
+ public function setPlugins(array $plugins)
+ {
+ $this->plugins = $plugins;
+ }
+
+ public function setPlugin($name, $value)
+ {
+ $this->plugins[$name] = $value;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ $filters = $this->filters;
+ $plugins = $this->plugins;
+
+ if (isset($filters['ImportImports']) && true === $filters['ImportImports']) {
+ if ($dir = $asset->getSourceDirectory()) {
+ $filters['ImportImports'] = array('BasePath' => $dir);
+ } else {
+ unset($filters['ImportImports']);
+ }
+ }
+
+ $asset->setContent(\CssMin::minify($asset->getContent(), $filters, $plugins));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssRewriteFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssRewriteFilter.php
new file mode 100644
index 0000000..c2250c3
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssRewriteFilter.php
@@ -0,0 +1,102 @@
+
+ */
+class CssRewriteFilter extends BaseCssFilter
+{
+ public function filterLoad(AssetInterface $asset)
+ {
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ $sourceBase = $asset->getSourceRoot();
+ $sourcePath = $asset->getSourcePath();
+ $targetPath = $asset->getTargetPath();
+
+ if (null === $sourcePath || null === $targetPath || $sourcePath == $targetPath) {
+ return;
+ }
+
+ // learn how to get from the target back to the source
+ if (false !== strpos($sourceBase, '://')) {
+ list($scheme, $url) = explode('://', $sourceBase.'/'.$sourcePath, 2);
+ list($host, $path) = explode('/', $url, 2);
+
+ $host = $scheme.'://'.$host.'/';
+ $path = false === strpos($path, '/') ? '' : dirname($path);
+ $path .= '/';
+ } else {
+ // assume source and target are on the same host
+ $host = '';
+
+ // pop entries off the target until it fits in the source
+ if ('.' == dirname($sourcePath)) {
+ $path = str_repeat('../', substr_count($targetPath, '/'));
+ } elseif ('.' == $targetDir = dirname($targetPath)) {
+ $path = dirname($sourcePath).'/';
+ } else {
+ $path = '';
+ while (0 !== strpos($sourcePath, $targetDir)) {
+ if (false !== $pos = strrpos($targetDir, '/')) {
+ $targetDir = substr($targetDir, 0, $pos);
+ $path .= '../';
+ } else {
+ $targetDir = '';
+ $path .= '../';
+ break;
+ }
+ }
+ $path .= ltrim(substr(dirname($sourcePath).'/', strlen($targetDir)), '/');
+ }
+ }
+
+ $content = $this->filterReferences($asset->getContent(), function ($matches) use ($host, $path) {
+ if (false !== strpos($matches['url'], '://') || 0 === strpos($matches['url'], '//') || 0 === strpos($matches['url'], 'data:')) {
+ // absolute or protocol-relative or data uri
+ return $matches[0];
+ }
+
+ if (isset($matches['url'][0]) && '/' == $matches['url'][0]) {
+ // root relative
+ return str_replace($matches['url'], $host.$matches['url'], $matches[0]);
+ }
+
+ // document relative
+ $url = $matches['url'];
+ while (0 === strpos($url, '../') && 2 <= substr_count($path, '/')) {
+ $path = substr($path, 0, strrpos(rtrim($path, '/'), '/') + 1);
+ $url = substr($url, 3);
+ }
+
+ $parts = array();
+ foreach (explode('/', $host.$path.$url) as $part) {
+ if ('..' === $part && count($parts) && '..' !== end($parts)) {
+ array_pop($parts);
+ } else {
+ $parts[] = $part;
+ }
+ }
+
+ return str_replace($matches['url'], implode('/', $parts), $matches[0]);
+ });
+
+ $asset->setContent($content);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/DartFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/DartFilter.php
new file mode 100644
index 0000000..12a3918
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/DartFilter.php
@@ -0,0 +1,73 @@
+dartBin = $dartBin;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ $input = FilesystemUtils::createTemporaryFile('dart');
+ $output = FilesystemUtils::createTemporaryFile('dart');
+
+ file_put_contents($input, $asset->getContent());
+
+ $pb = $this->createProcessBuilder()
+ ->add($this->dartBin)
+ ->add('-o'.$output)
+ ->add($input)
+ ;
+
+ $proc = $pb->getProcess();
+ $code = $proc->run();
+ unlink($input);
+
+ if (0 !== $code) {
+ $this->cleanup($output);
+
+ throw FilterException::fromProcess($proc);
+ }
+
+ if (!file_exists($output)) {
+ throw new \RuntimeException('Error creating output file.');
+ }
+
+ $asset->setContent(file_get_contents($output));
+ $this->cleanup($output);
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ }
+
+ private function cleanup($file)
+ {
+ foreach (glob($file.'*') as $related) {
+ unlink($related);
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/DependencyExtractorInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/DependencyExtractorInterface.php
new file mode 100644
index 0000000..6b9a8fb
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/DependencyExtractorInterface.php
@@ -0,0 +1,34 @@
+
+ */
+interface DependencyExtractorInterface extends FilterInterface
+{
+ /**
+ * Returns child assets.
+ *
+ * @param AssetFactory $factory The asset factory
+ * @param string $content The asset content
+ * @param string $loadPath An optional load path
+ *
+ * @return AssetInterface[] Child assets
+ */
+ public function getChildren(AssetFactory $factory, $content, $loadPath = null);
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/EmberPrecompileFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/EmberPrecompileFilter.php
new file mode 100644
index 0000000..313d4a0
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/EmberPrecompileFilter.php
@@ -0,0 +1,87 @@
+
+ */
+class EmberPrecompileFilter extends BaseNodeFilter
+{
+ private $emberBin;
+ private $nodeBin;
+
+ public function __construct($handlebarsBin = '/usr/bin/ember-precompile', $nodeBin = null)
+ {
+ $this->emberBin = $handlebarsBin;
+ $this->nodeBin = $nodeBin;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ $pb = $this->createProcessBuilder($this->nodeBin
+ ? array($this->nodeBin, $this->emberBin)
+ : array($this->emberBin));
+
+ if ($sourcePath = $asset->getSourcePath()) {
+ $templateName = basename($sourcePath);
+ } else {
+ throw new \LogicException('The embed-precompile filter requires that assets have a source path set');
+ }
+
+ $inputDirPath = FilesystemUtils::createThrowAwayDirectory('ember_in');
+ $inputPath = $inputDirPath.DIRECTORY_SEPARATOR.$templateName;
+ $outputPath = FilesystemUtils::createTemporaryFile('ember_out');
+
+ file_put_contents($inputPath, $asset->getContent());
+
+ $pb->add($inputPath)->add('-f')->add($outputPath);
+
+ $process = $pb->getProcess();
+ $returnCode = $process->run();
+
+ unlink($inputPath);
+ rmdir($inputDirPath);
+
+ if (127 === $returnCode) {
+ throw new \RuntimeException('Path to node executable could not be resolved.');
+ }
+
+ if (0 !== $returnCode) {
+ if (file_exists($outputPath)) {
+ unlink($outputPath);
+ }
+ throw FilterException::fromProcess($process)->setInput($asset->getContent());
+ }
+
+ if (!file_exists($outputPath)) {
+ throw new \RuntimeException('Error creating output file.');
+ }
+
+ $compiledJs = file_get_contents($outputPath);
+ unlink($outputPath);
+
+ $asset->setContent($compiledJs);
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/FilterCollection.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/FilterCollection.php
new file mode 100644
index 0000000..0fcd54e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/FilterCollection.php
@@ -0,0 +1,82 @@
+
+ */
+class FilterCollection implements FilterInterface, \IteratorAggregate, \Countable
+{
+ private $filters = array();
+
+ public function __construct($filters = array())
+ {
+ foreach ($filters as $filter) {
+ $this->ensure($filter);
+ }
+ }
+
+ /**
+ * Checks that the current collection contains the supplied filter.
+ *
+ * If the supplied filter is another filter collection, each of its
+ * filters will be checked.
+ */
+ public function ensure(FilterInterface $filter)
+ {
+ if ($filter instanceof \Traversable) {
+ foreach ($filter as $f) {
+ $this->ensure($f);
+ }
+ } elseif (!in_array($filter, $this->filters, true)) {
+ $this->filters[] = $filter;
+ }
+ }
+
+ public function all()
+ {
+ return $this->filters;
+ }
+
+ public function clear()
+ {
+ $this->filters = array();
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ foreach ($this->filters as $filter) {
+ $filter->filterLoad($asset);
+ }
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ foreach ($this->filters as $filter) {
+ $filter->filterDump($asset);
+ }
+ }
+
+ public function getIterator()
+ {
+ return new \ArrayIterator($this->filters);
+ }
+
+ public function count()
+ {
+ return count($this->filters);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/FilterInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/FilterInterface.php
new file mode 100644
index 0000000..797ac68
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/FilterInterface.php
@@ -0,0 +1,36 @@
+
+ */
+interface FilterInterface
+{
+ /**
+ * Filters an asset after it has been loaded.
+ *
+ * @param AssetInterface $asset An asset
+ */
+ public function filterLoad(AssetInterface $asset);
+
+ /**
+ * Filters an asset just before it's dumped.
+ *
+ * @param AssetInterface $asset An asset
+ */
+ public function filterDump(AssetInterface $asset);
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/BaseCompilerFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/BaseCompilerFilter.php
new file mode 100644
index 0000000..2509f4a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/BaseCompilerFilter.php
@@ -0,0 +1,101 @@
+
+ */
+abstract class BaseCompilerFilter implements FilterInterface
+{
+ // compilation levels
+ const COMPILE_WHITESPACE_ONLY = 'WHITESPACE_ONLY';
+ const COMPILE_SIMPLE_OPTIMIZATIONS = 'SIMPLE_OPTIMIZATIONS';
+ const COMPILE_ADVANCED_OPTIMIZATIONS = 'ADVANCED_OPTIMIZATIONS';
+
+ // formatting modes
+ const FORMAT_PRETTY_PRINT = 'pretty_print';
+ const FORMAT_PRINT_INPUT_DELIMITER = 'print_input_delimiter';
+
+ // warning levels
+ const LEVEL_QUIET = 'QUIET';
+ const LEVEL_DEFAULT = 'DEFAULT';
+ const LEVEL_VERBOSE = 'VERBOSE';
+
+ // languages
+ const LANGUAGE_ECMASCRIPT3 = 'ECMASCRIPT3';
+ const LANGUAGE_ECMASCRIPT5 = 'ECMASCRIPT5';
+ const LANGUAGE_ECMASCRIPT5_STRICT = 'ECMASCRIPT5_STRICT';
+
+ protected $timeout;
+ protected $compilationLevel;
+ protected $jsExterns;
+ protected $externsUrl;
+ protected $excludeDefaultExterns;
+ protected $formatting;
+ protected $useClosureLibrary;
+ protected $warningLevel;
+ protected $language;
+
+ public function setTimeout($timeout)
+ {
+ $this->timeout = $timeout;
+ }
+
+ public function setCompilationLevel($compilationLevel)
+ {
+ $this->compilationLevel = $compilationLevel;
+ }
+
+ public function setJsExterns($jsExterns)
+ {
+ $this->jsExterns = $jsExterns;
+ }
+
+ public function setExternsUrl($externsUrl)
+ {
+ $this->externsUrl = $externsUrl;
+ }
+
+ public function setExcludeDefaultExterns($excludeDefaultExterns)
+ {
+ $this->excludeDefaultExterns = $excludeDefaultExterns;
+ }
+
+ public function setFormatting($formatting)
+ {
+ $this->formatting = $formatting;
+ }
+
+ public function setUseClosureLibrary($useClosureLibrary)
+ {
+ $this->useClosureLibrary = $useClosureLibrary;
+ }
+
+ public function setWarningLevel($warningLevel)
+ {
+ $this->warningLevel = $warningLevel;
+ }
+
+ public function setLanguage($language)
+ {
+ $this->language = $language;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/CompilerApiFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/CompilerApiFilter.php
new file mode 100644
index 0000000..3478057
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/CompilerApiFilter.php
@@ -0,0 +1,130 @@
+
+ */
+class CompilerApiFilter extends BaseCompilerFilter
+{
+ private $proxy;
+ private $proxyFullUri;
+
+ public function setProxy($proxy)
+ {
+ $this->proxy = $proxy;
+ }
+
+ public function setProxyFullUri($proxyFullUri)
+ {
+ $this->proxyFullUri = $proxyFullUri;
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ $query = array(
+ 'js_code' => $asset->getContent(),
+ 'output_format' => 'json',
+ 'output_info' => 'compiled_code',
+ );
+
+ if (null !== $this->compilationLevel) {
+ $query['compilation_level'] = $this->compilationLevel;
+ }
+
+ if (null !== $this->jsExterns) {
+ $query['js_externs'] = $this->jsExterns;
+ }
+
+ if (null !== $this->externsUrl) {
+ $query['externs_url'] = $this->externsUrl;
+ }
+
+ if (null !== $this->excludeDefaultExterns) {
+ $query['exclude_default_externs'] = $this->excludeDefaultExterns ? 'true' : 'false';
+ }
+
+ if (null !== $this->formatting) {
+ $query['formatting'] = $this->formatting;
+ }
+
+ if (null !== $this->useClosureLibrary) {
+ $query['use_closure_library'] = $this->useClosureLibrary ? 'true' : 'false';
+ }
+
+ if (null !== $this->warningLevel) {
+ $query['warning_level'] = $this->warningLevel;
+ }
+
+ if (null !== $this->language) {
+ $query['language'] = $this->language;
+ }
+
+ if (preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen'))) {
+ $contextOptions = array('http' => array(
+ 'method' => 'POST',
+ 'header' => 'Content-Type: application/x-www-form-urlencoded',
+ 'content' => http_build_query($query),
+ ));
+ if (null !== $this->timeout) {
+ $contextOptions['http']['timeout'] = $this->timeout;
+ }
+ if ($this->proxy) {
+ $contextOptions['http']['proxy'] = $this->proxy;
+ $contextOptions['http']['request_fulluri'] = (Boolean) $this->proxyFullUri;
+ }
+ $context = stream_context_create($contextOptions);
+
+ $response = file_get_contents('http://closure-compiler.appspot.com/compile', false, $context);
+ $data = json_decode($response);
+ } elseif (defined('CURLOPT_POST') && !in_array('curl_init', explode(',', ini_get('disable_functions')))) {
+ $ch = curl_init('http://closure-compiler.appspot.com/compile');
+ curl_setopt($ch, CURLOPT_POST, true);
+ curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded'));
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $query);
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
+ if (null !== $this->timeout) {
+ curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
+ }
+ if ($this->proxy) {
+ curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, true);
+ curl_setopt($ch, CURLOPT_PROXY, $this->proxy);
+ }
+ $response = curl_exec($ch);
+ curl_close($ch);
+
+ $data = json_decode($response);
+ } else {
+ throw new \RuntimeException("There is no known way to contact closure compiler available");
+ }
+
+ if (isset($data->serverErrors) && 0 < count($data->serverErrors)) {
+ // @codeCoverageIgnoreStart
+ throw new \RuntimeException(sprintf('The Google Closure Compiler API threw some server errors: '.print_r($data->serverErrors, true)));
+ // @codeCoverageIgnoreEnd
+ }
+
+ if (isset($data->errors) && 0 < count($data->errors)) {
+ // @codeCoverageIgnoreStart
+ throw new \RuntimeException(sprintf('The Google Closure Compiler API threw some errors: '.print_r($data->errors, true)));
+ // @codeCoverageIgnoreEnd
+ }
+
+ $asset->setContent($data->compiledCode);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/CompilerJarFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/CompilerJarFilter.php
new file mode 100644
index 0000000..7cd340e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/CompilerJarFilter.php
@@ -0,0 +1,112 @@
+
+ */
+class CompilerJarFilter extends BaseCompilerFilter
+{
+ private $jarPath;
+ private $javaPath;
+ private $flagFile;
+
+ public function __construct($jarPath, $javaPath = '/usr/bin/java')
+ {
+ $this->jarPath = $jarPath;
+ $this->javaPath = $javaPath;
+ }
+
+ public function setFlagFile($flagFile)
+ {
+ $this->flagFile = $flagFile;
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ $is64bit = PHP_INT_SIZE === 8;
+ $cleanup = array();
+
+ $pb = new ProcessBuilder(array_merge(
+ array($this->javaPath),
+ $is64bit
+ ? array('-server', '-XX:+TieredCompilation')
+ : array('-client', '-d32'),
+ array('-jar', $this->jarPath)
+ ));
+
+ if (null !== $this->timeout) {
+ $pb->setTimeout($this->timeout);
+ }
+
+ if (null !== $this->compilationLevel) {
+ $pb->add('--compilation_level')->add($this->compilationLevel);
+ }
+
+ if (null !== $this->jsExterns) {
+ $cleanup[] = $externs = FilesystemUtils::createTemporaryFile('google_closure');
+ file_put_contents($externs, $this->jsExterns);
+ $pb->add('--externs')->add($externs);
+ }
+
+ if (null !== $this->externsUrl) {
+ $cleanup[] = $externs = FilesystemUtils::createTemporaryFile('google_closure');
+ file_put_contents($externs, file_get_contents($this->externsUrl));
+ $pb->add('--externs')->add($externs);
+ }
+
+ if (null !== $this->excludeDefaultExterns) {
+ $pb->add('--use_only_custom_externs');
+ }
+
+ if (null !== $this->formatting) {
+ $pb->add('--formatting')->add($this->formatting);
+ }
+
+ if (null !== $this->useClosureLibrary) {
+ $pb->add('--manage_closure_dependencies');
+ }
+
+ if (null !== $this->warningLevel) {
+ $pb->add('--warning_level')->add($this->warningLevel);
+ }
+
+ if (null !== $this->language) {
+ $pb->add('--language_in')->add($this->language);
+ }
+
+ if (null !== $this->flagFile) {
+ $pb->add('--flagfile')->add($this->flagFile);
+ }
+
+ $pb->add('--js')->add($cleanup[] = $input = FilesystemUtils::createTemporaryFile('google_closure'));
+ file_put_contents($input, $asset->getContent());
+
+ $proc = $pb->getProcess();
+ $code = $proc->run();
+ array_map('unlink', $cleanup);
+
+ if (0 !== $code) {
+ throw FilterException::fromProcess($proc)->setInput($asset->getContent());
+ }
+
+ $asset->setContent($proc->getOutput());
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/GssFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/GssFilter.php
new file mode 100644
index 0000000..4b55610
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/GssFilter.php
@@ -0,0 +1,142 @@
+
+ */
+class GssFilter extends BaseProcessFilter
+{
+ private $jarPath;
+ private $javaPath;
+ private $allowUnrecognizedFunctions;
+ private $allowedNonStandardFunctions;
+ private $copyrightNotice;
+ private $define;
+ private $gssFunctionMapProvider;
+ private $inputOrientation;
+ private $outputOrientation;
+ private $prettyPrint;
+
+ public function __construct($jarPath, $javaPath = '/usr/bin/java')
+ {
+ $this->jarPath = $jarPath;
+ $this->javaPath = $javaPath;
+ }
+
+ public function setAllowUnrecognizedFunctions($allowUnrecognizedFunctions)
+ {
+ $this->allowUnrecognizedFunctions = $allowUnrecognizedFunctions;
+ }
+
+ public function setAllowedNonStandardFunctions($allowNonStandardFunctions)
+ {
+ $this->allowedNonStandardFunctions = $allowNonStandardFunctions;
+ }
+
+ public function setCopyrightNotice($copyrightNotice)
+ {
+ $this->copyrightNotice = $copyrightNotice;
+ }
+
+ public function setDefine($define)
+ {
+ $this->define = $define;
+ }
+
+ public function setGssFunctionMapProvider($gssFunctionMapProvider)
+ {
+ $this->gssFunctionMapProvider = $gssFunctionMapProvider;
+ }
+
+ public function setInputOrientation($inputOrientation)
+ {
+ $this->inputOrientation = $inputOrientation;
+ }
+
+ public function setOutputOrientation($outputOrientation)
+ {
+ $this->outputOrientation = $outputOrientation;
+ }
+
+ public function setPrettyPrint($prettyPrint)
+ {
+ $this->prettyPrint = $prettyPrint;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ $cleanup = array();
+
+ $pb = $this->createProcessBuilder(array(
+ $this->javaPath,
+ '-jar',
+ $this->jarPath,
+ ));
+
+ if (null !== $this->allowUnrecognizedFunctions) {
+ $pb->add('--allow-unrecognized-functions');
+ }
+
+ if (null !== $this->allowedNonStandardFunctions) {
+ $pb->add('--allowed_non_standard_functions')->add($this->allowedNonStandardFunctions);
+ }
+
+ if (null !== $this->copyrightNotice) {
+ $pb->add('--copyright-notice')->add($this->copyrightNotice);
+ }
+
+ if (null !== $this->define) {
+ $pb->add('--define')->add($this->define);
+ }
+
+ if (null !== $this->gssFunctionMapProvider) {
+ $pb->add('--gss-function-map-provider')->add($this->gssFunctionMapProvider);
+ }
+
+ if (null !== $this->inputOrientation) {
+ $pb->add('--input-orientation')->add($this->inputOrientation);
+ }
+
+ if (null !== $this->outputOrientation) {
+ $pb->add('--output-orientation')->add($this->outputOrientation);
+ }
+
+ if (null !== $this->prettyPrint) {
+ $pb->add('--pretty-print');
+ }
+
+ $pb->add($cleanup[] = $input = FilesystemUtils::createTemporaryFile('gss'));
+ file_put_contents($input, $asset->getContent());
+
+ $proc = $pb->getProcess();
+ $code = $proc->run();
+ array_map('unlink', $cleanup);
+
+ if (0 !== $code) {
+ throw FilterException::fromProcess($proc)->setInput($asset->getContent());
+ }
+
+ $asset->setContent($proc->getOutput());
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/HandlebarsFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/HandlebarsFilter.php
new file mode 100644
index 0000000..f34927f
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/HandlebarsFilter.php
@@ -0,0 +1,106 @@
+
+ */
+class HandlebarsFilter extends BaseNodeFilter
+{
+ private $handlebarsBin;
+ private $nodeBin;
+
+ private $minimize = false;
+ private $simple = false;
+
+ public function __construct($handlebarsBin = '/usr/bin/handlebars', $nodeBin = null)
+ {
+ $this->handlebarsBin = $handlebarsBin;
+ $this->nodeBin = $nodeBin;
+ }
+
+ public function setMinimize($minimize)
+ {
+ $this->minimize = $minimize;
+ }
+
+ public function setSimple($simple)
+ {
+ $this->simple = $simple;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ $pb = $this->createProcessBuilder($this->nodeBin
+ ? array($this->nodeBin, $this->handlebarsBin)
+ : array($this->handlebarsBin));
+
+ if ($sourcePath = $asset->getSourcePath()) {
+ $templateName = basename($sourcePath);
+ } else {
+ throw new \LogicException('The handlebars filter requires that assets have a source path set');
+ }
+
+ $inputDirPath = FilesystemUtils::createThrowAwayDirectory('handlebars_in');
+ $inputPath = $inputDirPath.DIRECTORY_SEPARATOR.$templateName;
+ $outputPath = FilesystemUtils::createTemporaryFile('handlebars_out');
+
+ file_put_contents($inputPath, $asset->getContent());
+
+ $pb->add($inputPath)->add('-f')->add($outputPath);
+
+ if ($this->minimize) {
+ $pb->add('--min');
+ }
+
+ if ($this->simple) {
+ $pb->add('--simple');
+ }
+
+ $process = $pb->getProcess();
+ $returnCode = $process->run();
+
+ unlink($inputPath);
+ rmdir($inputDirPath);
+
+ if (127 === $returnCode) {
+ throw new \RuntimeException('Path to node executable could not be resolved.');
+ }
+
+ if (0 !== $returnCode) {
+ if (file_exists($outputPath)) {
+ unlink($outputPath);
+ }
+ throw FilterException::fromProcess($process)->setInput($asset->getContent());
+ }
+
+ if (!file_exists($outputPath)) {
+ throw new \RuntimeException('Error creating output file.');
+ }
+
+ $compiledJs = file_get_contents($outputPath);
+ unlink($outputPath);
+
+ $asset->setContent($compiledJs);
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/HashableInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/HashableInterface.php
new file mode 100644
index 0000000..cd3ffda
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/HashableInterface.php
@@ -0,0 +1,27 @@
+
+ */
+interface HashableInterface
+{
+ /**
+ * Generates a hash for the object
+ *
+ * @return string Object hash
+ */
+ public function hash();
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/JSMinFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/JSMinFilter.php
new file mode 100644
index 0000000..884656b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/JSMinFilter.php
@@ -0,0 +1,34 @@
+
+ */
+class JSMinFilter implements FilterInterface
+{
+ public function filterLoad(AssetInterface $asset)
+ {
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ $asset->setContent(\JSMin::minify($asset->getContent()));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/JSMinPlusFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/JSMinPlusFilter.php
new file mode 100644
index 0000000..14cd085
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/JSMinPlusFilter.php
@@ -0,0 +1,34 @@
+
+ */
+class JSMinPlusFilter implements FilterInterface
+{
+ public function filterLoad(AssetInterface $asset)
+ {
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ $asset->setContent(\JSMinPlus::minify($asset->getContent()));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/JSqueezeFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/JSqueezeFilter.php
new file mode 100644
index 0000000..07c7fd6
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/JSqueezeFilter.php
@@ -0,0 +1,77 @@
+
+ */
+class JSqueezeFilter implements FilterInterface
+{
+ private $singleLine = true;
+ private $keepImportantComments = true;
+ private $className;
+ private $specialVarRx = false;
+ private $defaultRx;
+
+ public function __construct()
+ {
+ // JSqueeze is namespaced since 2.x, this works with both 1.x and 2.x
+ if (class_exists('\\Patchwork\\JSqueeze')) {
+ $this->className = '\\Patchwork\\JSqueeze';
+ $this->defaultRx = \Patchwork\JSqueeze::SPECIAL_VAR_PACKER;
+ } else {
+ $this->className = '\\JSqueeze';
+ $this->defaultRx = \JSqueeze::SPECIAL_VAR_RX;
+ }
+ }
+
+ public function setSingleLine($bool)
+ {
+ $this->singleLine = (bool) $bool;
+ }
+
+ // call setSpecialVarRx(true) to enable global var/method/property
+ // renaming with the default regex (for 1.x or 2.x)
+ public function setSpecialVarRx($specialVarRx)
+ {
+ if (true === $specialVarRx) {
+ $this->specialVarRx = $this->defaultRx;
+ } else {
+ $this->specialVarRx = $specialVarRx;
+ }
+ }
+
+ public function keepImportantComments($bool)
+ {
+ $this->keepImportantComments = (bool) $bool;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ $parser = new $this->className();
+ $asset->setContent($parser->squeeze(
+ $asset->getContent(),
+ $this->singleLine,
+ $this->keepImportantComments,
+ $this->specialVarRx
+ ));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/JpegoptimFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/JpegoptimFilter.php
new file mode 100644
index 0000000..fcc1489
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/JpegoptimFilter.php
@@ -0,0 +1,81 @@
+
+ */
+class JpegoptimFilter extends BaseProcessFilter
+{
+ private $jpegoptimBin;
+ private $stripAll;
+ private $max;
+
+ /**
+ * Constructor.
+ *
+ * @param string $jpegoptimBin Path to the jpegoptim binary
+ */
+ public function __construct($jpegoptimBin = '/usr/bin/jpegoptim')
+ {
+ $this->jpegoptimBin = $jpegoptimBin;
+ }
+
+ public function setStripAll($stripAll)
+ {
+ $this->stripAll = $stripAll;
+ }
+
+ public function setMax($max)
+ {
+ $this->max = $max;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ $pb = $this->createProcessBuilder(array($this->jpegoptimBin));
+
+ if ($this->stripAll) {
+ $pb->add('--strip-all');
+ }
+
+ if ($this->max) {
+ $pb->add('--max='.$this->max);
+ }
+
+ $pb->add($input = FilesystemUtils::createTemporaryFile('jpegoptim'));
+ file_put_contents($input, $asset->getContent());
+
+ $proc = $pb->getProcess();
+ $proc->run();
+
+ if (false !== strpos($proc->getOutput(), 'ERROR')) {
+ unlink($input);
+ throw FilterException::fromProcess($proc)->setInput($asset->getContent());
+ }
+
+ $asset->setContent(file_get_contents($input));
+
+ unlink($input);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/JpegtranFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/JpegtranFilter.php
new file mode 100644
index 0000000..7836538
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/JpegtranFilter.php
@@ -0,0 +1,103 @@
+
+ */
+class JpegtranFilter extends BaseProcessFilter
+{
+ const COPY_NONE = 'none';
+ const COPY_COMMENTS = 'comments';
+ const COPY_ALL = 'all';
+
+ private $jpegtranBin;
+ private $optimize;
+ private $copy;
+ private $progressive;
+ private $restart;
+
+ /**
+ * Constructor.
+ *
+ * @param string $jpegtranBin Path to the jpegtran binary
+ */
+ public function __construct($jpegtranBin = '/usr/bin/jpegtran')
+ {
+ $this->jpegtranBin = $jpegtranBin;
+ }
+
+ public function setOptimize($optimize)
+ {
+ $this->optimize = $optimize;
+ }
+
+ public function setCopy($copy)
+ {
+ $this->copy = $copy;
+ }
+
+ public function setProgressive($progressive)
+ {
+ $this->progressive = $progressive;
+ }
+
+ public function setRestart($restart)
+ {
+ $this->restart = $restart;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ $pb = $this->createProcessBuilder(array($this->jpegtranBin));
+
+ if ($this->optimize) {
+ $pb->add('-optimize');
+ }
+
+ if ($this->copy) {
+ $pb->add('-copy')->add($this->copy);
+ }
+
+ if ($this->progressive) {
+ $pb->add('-progressive');
+ }
+
+ if (null !== $this->restart) {
+ $pb->add('-restart')->add($this->restart);
+ }
+
+ $pb->add($input = FilesystemUtils::createTemporaryFile('jpegtran'));
+ file_put_contents($input, $asset->getContent());
+
+ $proc = $pb->getProcess();
+ $code = $proc->run();
+ unlink($input);
+
+ if (0 !== $code) {
+ throw FilterException::fromProcess($proc)->setInput($asset->getContent());
+ }
+
+ $asset->setContent($proc->getOutput());
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/LessFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/LessFilter.php
new file mode 100644
index 0000000..7ca5cd7
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/LessFilter.php
@@ -0,0 +1,206 @@
+
+ */
+class LessFilter extends BaseNodeFilter implements DependencyExtractorInterface
+{
+ private $nodeBin;
+
+ /**
+ * @var array
+ */
+ private $treeOptions;
+
+ /**
+ * @var array
+ */
+ private $parserOptions;
+
+ /**
+ * Load Paths
+ *
+ * A list of paths which less will search for includes.
+ *
+ * @var array
+ */
+ protected $loadPaths = array();
+
+ /**
+ * Constructor.
+ *
+ * @param string $nodeBin The path to the node binary
+ * @param array $nodePaths An array of node paths
+ */
+ public function __construct($nodeBin = '/usr/bin/node', array $nodePaths = array())
+ {
+ $this->nodeBin = $nodeBin;
+ $this->setNodePaths($nodePaths);
+ $this->treeOptions = array();
+ $this->parserOptions = array();
+ }
+
+ /**
+ * @param bool $compress
+ */
+ public function setCompress($compress)
+ {
+ $this->addTreeOption('compress', $compress);
+ }
+
+ public function setLoadPaths(array $loadPaths)
+ {
+ $this->loadPaths = $loadPaths;
+ }
+
+ /**
+ * Adds a path where less will search for includes
+ *
+ * @param string $path Load path (absolute)
+ */
+ public function addLoadPath($path)
+ {
+ $this->loadPaths[] = $path;
+ }
+
+ /**
+ * @param string $code
+ * @param string $value
+ */
+ public function addTreeOption($code, $value)
+ {
+ $this->treeOptions[$code] = $value;
+ }
+
+ /**
+ * @param string $code
+ * @param string $value
+ */
+ public function addParserOption($code, $value)
+ {
+ $this->parserOptions[$code] = $value;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ static $format = <<<'EOF'
+var less = require('less');
+var sys = require(process.binding('natives').util ? 'util' : 'sys');
+
+less.render(%s, %s, function(error, css) {
+ if (error) {
+ less.writeError(error);
+ process.exit(2);
+ }
+ try {
+ if (typeof css == 'string') {
+ sys.print(css);
+ } else {
+ sys.print(css.css);
+ }
+ } catch (e) {
+ less.writeError(error);
+ process.exit(3);
+ }
+});
+
+EOF;
+
+ // parser options
+ $parserOptions = $this->parserOptions;
+ if ($dir = $asset->getSourceDirectory()) {
+ $parserOptions['paths'] = array($dir);
+ $parserOptions['filename'] = basename($asset->getSourcePath());
+ }
+
+ foreach ($this->loadPaths as $loadPath) {
+ $parserOptions['paths'][] = $loadPath;
+ }
+
+ $pb = $this->createProcessBuilder();
+
+ $pb->add($this->nodeBin)->add($input = FilesystemUtils::createTemporaryFile('less'));
+ file_put_contents($input, sprintf($format,
+ json_encode($asset->getContent()),
+ json_encode(array_merge($parserOptions, $this->treeOptions))
+ ));
+
+ $proc = $pb->getProcess();
+ $code = $proc->run();
+ unlink($input);
+
+ if (0 !== $code) {
+ throw FilterException::fromProcess($proc)->setInput($asset->getContent());
+ }
+
+ $asset->setContent($proc->getOutput());
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ }
+
+ /**
+ * @todo support for import-once
+ * @todo support for import (less) "lib.css"
+ */
+ public function getChildren(AssetFactory $factory, $content, $loadPath = null)
+ {
+ $loadPaths = $this->loadPaths;
+ if (null !== $loadPath) {
+ $loadPaths[] = $loadPath;
+ }
+
+ if (empty($loadPaths)) {
+ return array();
+ }
+
+ $children = array();
+ foreach (LessUtils::extractImports($content) as $reference) {
+ if ('.css' === substr($reference, -4)) {
+ // skip normal css imports
+ // todo: skip imports with media queries
+ continue;
+ }
+
+ if ('.less' !== substr($reference, -5)) {
+ $reference .= '.less';
+ }
+
+ foreach ($loadPaths as $loadPath) {
+ if (file_exists($file = $loadPath.'/'.$reference)) {
+ $coll = $factory->createAsset($file, array(), array('root' => $loadPath));
+ foreach ($coll as $leaf) {
+ $leaf->ensureFilter($this);
+ $children[] = $leaf;
+ goto next_reference;
+ }
+ }
+ }
+
+ next_reference:
+ }
+
+ return $children;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/LessphpFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/LessphpFilter.php
new file mode 100644
index 0000000..6116f58
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/LessphpFilter.php
@@ -0,0 +1,167 @@
+
+ * @author Kris Wallsmith
+ */
+class LessphpFilter implements DependencyExtractorInterface
+{
+ private $presets = array();
+ private $formatter;
+ private $preserveComments;
+ private $customFunctions = array();
+ private $options = array();
+
+ /**
+ * Lessphp Load Paths
+ *
+ * @var array
+ */
+ protected $loadPaths = array();
+
+ /**
+ * Adds a load path to the paths used by lessphp
+ *
+ * @param string $path Load Path
+ */
+ public function addLoadPath($path)
+ {
+ $this->loadPaths[] = $path;
+ }
+
+ /**
+ * Sets load paths used by lessphp
+ *
+ * @param array $loadPaths Load paths
+ */
+ public function setLoadPaths(array $loadPaths)
+ {
+ $this->loadPaths = $loadPaths;
+ }
+
+ public function setPresets(array $presets)
+ {
+ $this->presets = $presets;
+ }
+
+ public function setOptions(array $options)
+ {
+ $this->options = $options;
+ }
+
+ /**
+ * @param string $formatter One of "lessjs", "compressed", or "classic".
+ */
+ public function setFormatter($formatter)
+ {
+ $this->formatter = $formatter;
+ }
+
+ /**
+ * @param boolean $preserveComments
+ */
+ public function setPreserveComments($preserveComments)
+ {
+ $this->preserveComments = $preserveComments;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ $lc = new \lessc();
+ if ($dir = $asset->getSourceDirectory()) {
+ $lc->importDir = $dir;
+ }
+
+ foreach ($this->loadPaths as $loadPath) {
+ $lc->addImportDir($loadPath);
+ }
+
+ foreach ($this->customFunctions as $name => $callable) {
+ $lc->registerFunction($name, $callable);
+ }
+
+ if ($this->formatter) {
+ $lc->setFormatter($this->formatter);
+ }
+
+ if (null !== $this->preserveComments) {
+ $lc->setPreserveComments($this->preserveComments);
+ }
+
+ if (method_exists($lc, 'setOptions') && count($this->options) > 0 ) {
+ $lc->setOptions($this->options);
+ }
+
+ $asset->setContent($lc->parse($asset->getContent(), $this->presets));
+ }
+
+ public function registerFunction($name, $callable)
+ {
+ $this->customFunctions[$name] = $callable;
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ }
+
+ public function getChildren(AssetFactory $factory, $content, $loadPath = null)
+ {
+ $loadPaths = $this->loadPaths;
+ if (null !== $loadPath) {
+ $loadPaths[] = $loadPath;
+ }
+
+ if (empty($loadPaths)) {
+ return array();
+ }
+
+ $children = array();
+ foreach (LessUtils::extractImports($content) as $reference) {
+ if ('.css' === substr($reference, -4)) {
+ // skip normal css imports
+ // todo: skip imports with media queries
+ continue;
+ }
+
+ if ('.less' !== substr($reference, -5)) {
+ $reference .= '.less';
+ }
+
+ foreach ($loadPaths as $loadPath) {
+ if (file_exists($file = $loadPath.'/'.$reference)) {
+ $coll = $factory->createAsset($file, array(), array('root' => $loadPath));
+ foreach ($coll as $leaf) {
+ $leaf->ensureFilter($this);
+ $children[] = $leaf;
+ goto next_reference;
+ }
+ }
+ }
+
+ next_reference:
+ }
+
+ return $children;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/MinifyCssCompressorFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/MinifyCssCompressorFilter.php
new file mode 100644
index 0000000..180667a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/MinifyCssCompressorFilter.php
@@ -0,0 +1,35 @@
+
+ * @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
+ */
+class MinifyCssCompressorFilter implements FilterInterface
+{
+ public function filterLoad(AssetInterface $asset)
+ {
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ $asset->setContent(\Minify_CSS_Compressor::process($asset->getContent()));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/OptiPngFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/OptiPngFilter.php
new file mode 100644
index 0000000..5bc1d12
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/OptiPngFilter.php
@@ -0,0 +1,75 @@
+
+ */
+class OptiPngFilter extends BaseProcessFilter
+{
+ private $optipngBin;
+ private $level;
+
+ /**
+ * Constructor.
+ *
+ * @param string $optipngBin Path to the optipng binary
+ */
+ public function __construct($optipngBin = '/usr/bin/optipng')
+ {
+ $this->optipngBin = $optipngBin;
+ }
+
+ public function setLevel($level)
+ {
+ $this->level = $level;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ $pb = $this->createProcessBuilder(array($this->optipngBin));
+
+ if (null !== $this->level) {
+ $pb->add('-o')->add($this->level);
+ }
+
+ $pb->add('-out')->add($output = FilesystemUtils::createTemporaryFile('optipng_out'));
+ unlink($output);
+
+ $pb->add($input = FilesystemUtils::createTemporaryFile('optinpg_in'));
+ file_put_contents($input, $asset->getContent());
+
+ $proc = $pb->getProcess();
+ $code = $proc->run();
+
+ if (0 !== $code) {
+ unlink($input);
+ throw FilterException::fromProcess($proc)->setInput($asset->getContent());
+ }
+
+ $asset->setContent(file_get_contents($output));
+
+ unlink($input);
+ unlink($output);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/PackagerFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/PackagerFilter.php
new file mode 100644
index 0000000..23ceb4e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/PackagerFilter.php
@@ -0,0 +1,65 @@
+
+ */
+class PackagerFilter implements FilterInterface
+{
+ private $packages;
+
+ public function __construct(array $packages = array())
+ {
+ $this->packages = $packages;
+ }
+
+ public function addPackage($package)
+ {
+ $this->packages[] = $package;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ static $manifest = <<getContent());
+
+ $packager = new \Packager(array_merge(array($package), $this->packages));
+ $content = $packager->build(array(), array(), array('Application'.$hash));
+
+ unlink($package.'/package.yml');
+ unlink($package.'/source.js');
+ rmdir($package);
+
+ $asset->setContent($content);
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/PackerFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/PackerFilter.php
new file mode 100644
index 0000000..6dbe5f3
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/PackerFilter.php
@@ -0,0 +1,56 @@
+
+ */
+class PackerFilter implements FilterInterface
+{
+ protected $encoding = 'None';
+
+ protected $fastDecode = true;
+
+ protected $specialChars = false;
+
+ public function setEncoding($encoding)
+ {
+ $this->encoding = $encoding;
+ }
+
+ public function setFastDecode($fastDecode)
+ {
+ $this->fastDecode = (bool) $fastDecode;
+ }
+
+ public function setSpecialChars($specialChars)
+ {
+ $this->specialChars = (bool) $specialChars;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ $packer = new \JavaScriptPacker($asset->getContent(), $this->encoding, $this->fastDecode, $this->specialChars);
+ $asset->setContent($packer->pack());
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/PhpCssEmbedFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/PhpCssEmbedFilter.php
new file mode 100644
index 0000000..b1ff33f
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/PhpCssEmbedFilter.php
@@ -0,0 +1,52 @@
+
+ * @link https://github.com/krichprollsch/phpCssEmbed
+ */
+class PhpCssEmbedFilter implements DependencyExtractorInterface
+{
+ private $presets = array();
+
+ public function setPresets(array $presets)
+ {
+ $this->presets = $presets;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ $pce = new CssEmbed();
+ if ($dir = $asset->getSourceDirectory()) {
+ $pce->setRootDir($dir);
+ }
+
+ $asset->setContent($pce->embedString($asset->getContent()));
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ }
+
+ public function getChildren(AssetFactory $factory, $content, $loadPath = null)
+ {
+ // todo
+ return array();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/PngoutFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/PngoutFilter.php
new file mode 100644
index 0000000..592ef3a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/PngoutFilter.php
@@ -0,0 +1,128 @@
+
+ */
+class PngoutFilter extends BaseProcessFilter
+{
+ // -c#
+ const COLOR_GREY = '0';
+ const COLOR_RGB = '2';
+ const COLOR_PAL = '3';
+ const COLOR_GRAY_ALPHA = '4';
+ const COLOR_RGB_ALPHA = '6';
+
+ // -f#
+ const FILTER_NONE = '0';
+ const FILTER_X = '1';
+ const FILTER_Y = '2';
+ const FILTER_X_Y = '3';
+ const FILTER_PAETH = '4';
+ const FILTER_MIXED = '5';
+
+ // -s#
+ const STRATEGY_XTREME = '0';
+ const STRATEGY_INTENSE = '1';
+ const STRATEGY_LONGEST_MATCH = '2';
+ const STRATEGY_HUFFMAN_ONLY = '3';
+ const STRATEGY_UNCOMPRESSED = '4';
+
+ private $pngoutBin;
+ private $color;
+ private $filter;
+ private $strategy;
+ private $blockSplitThreshold;
+
+ /**
+ * Constructor.
+ *
+ * @param string $pngoutBin Path to the pngout binary
+ */
+ public function __construct($pngoutBin = '/usr/bin/pngout')
+ {
+ $this->pngoutBin = $pngoutBin;
+ }
+
+ public function setColor($color)
+ {
+ $this->color = $color;
+ }
+
+ public function setFilter($filter)
+ {
+ $this->filter = $filter;
+ }
+
+ public function setStrategy($strategy)
+ {
+ $this->strategy = $strategy;
+ }
+
+ public function setBlockSplitThreshold($blockSplitThreshold)
+ {
+ $this->blockSplitThreshold = $blockSplitThreshold;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ $pb = $this->createProcessBuilder(array($this->pngoutBin));
+
+ if (null !== $this->color) {
+ $pb->add('-c'.$this->color);
+ }
+
+ if (null !== $this->filter) {
+ $pb->add('-f'.$this->filter);
+ }
+
+ if (null !== $this->strategy) {
+ $pb->add('-s'.$this->strategy);
+ }
+
+ if (null !== $this->blockSplitThreshold) {
+ $pb->add('-b'.$this->blockSplitThreshold);
+ }
+
+ $pb->add($input = FilesystemUtils::createTemporaryFile('pngout_in'));
+ file_put_contents($input, $asset->getContent());
+
+ $output = FilesystemUtils::createTemporaryFile('pngout_out');
+ unlink($output);
+ $pb->add($output .= '.png');
+
+ $proc = $pb->getProcess();
+ $code = $proc->run();
+
+ if (0 !== $code) {
+ unlink($input);
+ throw FilterException::fromProcess($proc)->setInput($asset->getContent());
+ }
+
+ $asset->setContent(file_get_contents($output));
+
+ unlink($input);
+ unlink($output);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/ReactJsxFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/ReactJsxFilter.php
new file mode 100644
index 0000000..dc4f218
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/ReactJsxFilter.php
@@ -0,0 +1,75 @@
+
+ */
+class ReactJsxFilter extends BaseNodeFilter
+{
+ private $jsxBin;
+ private $nodeBin;
+
+ public function __construct($jsxBin = '/usr/bin/jsx', $nodeBin = null)
+ {
+ $this->jsxBin = $jsxBin;
+ $this->nodeBin = $nodeBin;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ $builder = $this->createProcessBuilder($this->nodeBin
+ ? array($this->nodeBin, $this->jsxBin)
+ : array($this->jsxBin));
+
+ $inputDir = FilesystemUtils::createThrowAwayDirectory('jsx_in');
+ $inputFile = $inputDir.DIRECTORY_SEPARATOR.'asset.js';
+ $outputDir = FilesystemUtils::createThrowAwayDirectory('jsx_out');
+ $outputFile = $outputDir.DIRECTORY_SEPARATOR.'asset.js';
+
+ // create the asset file
+ file_put_contents($inputFile, $asset->getContent());
+
+ $builder
+ ->add($inputDir)
+ ->add($outputDir)
+ ->add('--no-cache-dir')
+ ;
+
+ $proc = $builder->getProcess();
+ $code = $proc->run();
+
+ // remove the input directory and asset file
+ unlink($inputFile);
+ rmdir($inputDir);
+
+ if (0 !== $code) {
+ if (file_exists($outputFile)) {
+ unlink($outputFile);
+ }
+
+ if (file_exists($outputDir)) {
+ rmdir($outputDir);
+ }
+
+ throw FilterException::fromProcess($proc);
+ }
+
+ $asset->setContent(file_get_contents($outputFile));
+
+ // remove the output directory and processed asset file
+ unlink($outputFile);
+ rmdir($outputDir);
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/RooleFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/RooleFilter.php
new file mode 100644
index 0000000..cbcaf4c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/RooleFilter.php
@@ -0,0 +1,84 @@
+
+ */
+class RooleFilter extends BaseNodeFilter implements DependencyExtractorInterface
+{
+ private $rooleBin;
+ private $nodeBin;
+
+ /**
+ * Constructor
+ *
+ * @param string $rooleBin The path to the roole binary
+ * @param string $nodeBin The path to the node binary
+ */
+ public function __construct($rooleBin = '/usr/bin/roole', $nodeBin = null)
+ {
+ $this->rooleBin = $rooleBin;
+ $this->nodeBin = $nodeBin;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ $input = FilesystemUtils::createTemporaryFile('roole');
+ $output = $input.'.css';
+
+ file_put_contents($input, $asset->getContent());
+
+ $pb = $this->createProcessBuilder($this->nodeBin
+ ? array($this->nodeBin, $this->rooleBin)
+ : array($this->rooleBin));
+
+ $pb->add($input);
+
+ $proc = $pb->getProcess();
+ $code = $proc->run();
+ unlink($input);
+
+ if (0 !== $code) {
+ if (file_exists($output)) {
+ unlink($output);
+ }
+
+ throw FilterException::fromProcess($proc);
+ }
+
+ if (!file_exists($output)) {
+ throw new \RuntimeException('Error creating output file.');
+ }
+
+ $asset->setContent(file_get_contents($output));
+ unlink($output);
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ }
+
+ public function getChildren(AssetFactory $factory, $content, $loadPath = null)
+ {
+ // todo
+ return array();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/BaseSassFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/BaseSassFilter.php
new file mode 100644
index 0000000..68903f0
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/BaseSassFilter.php
@@ -0,0 +1,95 @@
+loadPaths = $loadPaths;
+ }
+
+ public function addLoadPath($loadPath)
+ {
+ $this->loadPaths[] = $loadPath;
+ }
+
+ public function getChildren(AssetFactory $factory, $content, $loadPath = null)
+ {
+ $loadPaths = $this->loadPaths;
+ if ($loadPath) {
+ array_unshift($loadPaths, $loadPath);
+ }
+
+ if (!$loadPaths) {
+ return array();
+ }
+
+ $children = array();
+ foreach (SassUtils::extractImports($content) as $reference) {
+ if ('.css' === substr($reference, -4)) {
+ // skip normal css imports
+ // todo: skip imports with media queries
+ continue;
+ }
+
+ // the reference may or may not have an extension or be a partial
+ if (pathinfo($reference, PATHINFO_EXTENSION)) {
+ $needles = array(
+ $reference,
+ self::partialize($reference),
+ );
+ } else {
+ $needles = array(
+ $reference.'.scss',
+ $reference.'.sass',
+ self::partialize($reference).'.scss',
+ self::partialize($reference).'.sass',
+ );
+ }
+
+ foreach ($loadPaths as $loadPath) {
+ foreach ($needles as $needle) {
+ if (file_exists($file = $loadPath.'/'.$needle)) {
+ $coll = $factory->createAsset($file, array(), array('root' => $loadPath));
+ foreach ($coll as $leaf) {
+ /** @var $leaf AssetInterface */
+ $leaf->ensureFilter($this);
+ $children[] = $leaf;
+ goto next_reference;
+ }
+ }
+ }
+ }
+
+ next_reference:
+ }
+
+ return $children;
+ }
+
+ private static function partialize($reference)
+ {
+ $parts = pathinfo($reference);
+
+ if ('.' === $parts['dirname']) {
+ $partial = '_'.$parts['filename'];
+ } else {
+ $partial = $parts['dirname'].DIRECTORY_SEPARATOR.'_'.$parts['filename'];
+ }
+
+ if (isset($parts['extension'])) {
+ $partial .= '.'.$parts['extension'];
+ }
+
+ return $partial;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/SassFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/SassFilter.php
new file mode 100644
index 0000000..68da116
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/SassFilter.php
@@ -0,0 +1,186 @@
+
+ */
+class SassFilter extends BaseSassFilter
+{
+ const STYLE_NESTED = 'nested';
+ const STYLE_EXPANDED = 'expanded';
+ const STYLE_COMPACT = 'compact';
+ const STYLE_COMPRESSED = 'compressed';
+
+ private $sassPath;
+ private $rubyPath;
+ private $unixNewlines;
+ private $scss;
+ private $style;
+ private $precision;
+ private $quiet;
+ private $debugInfo;
+ private $lineNumbers;
+ private $sourceMap;
+ private $cacheLocation;
+ private $noCache;
+ private $compass;
+
+ public function __construct($sassPath = '/usr/bin/sass', $rubyPath = null)
+ {
+ $this->sassPath = $sassPath;
+ $this->rubyPath = $rubyPath;
+ $this->cacheLocation = FilesystemUtils::getTemporaryDirectory();
+ }
+
+ public function setUnixNewlines($unixNewlines)
+ {
+ $this->unixNewlines = $unixNewlines;
+ }
+
+ public function setScss($scss)
+ {
+ $this->scss = $scss;
+ }
+
+ public function setStyle($style)
+ {
+ $this->style = $style;
+ }
+
+ public function setPrecision($precision)
+ {
+ $this->precision = $precision;
+ }
+
+ public function setQuiet($quiet)
+ {
+ $this->quiet = $quiet;
+ }
+
+ public function setDebugInfo($debugInfo)
+ {
+ $this->debugInfo = $debugInfo;
+ }
+
+ public function setLineNumbers($lineNumbers)
+ {
+ $this->lineNumbers = $lineNumbers;
+ }
+
+ public function setSourceMap($sourceMap)
+ {
+ $this->sourceMap = $sourceMap;
+ }
+
+ public function setCacheLocation($cacheLocation)
+ {
+ $this->cacheLocation = $cacheLocation;
+ }
+
+ public function setNoCache($noCache)
+ {
+ $this->noCache = $noCache;
+ }
+
+ public function setCompass($compass)
+ {
+ $this->compass = $compass;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ $sassProcessArgs = array($this->sassPath);
+ if (null !== $this->rubyPath) {
+ $sassProcessArgs = array_merge(explode(' ', $this->rubyPath), $sassProcessArgs);
+ }
+
+ $pb = $this->createProcessBuilder($sassProcessArgs);
+
+ if ($dir = $asset->getSourceDirectory()) {
+ $pb->add('--load-path')->add($dir);
+ }
+
+ if ($this->unixNewlines) {
+ $pb->add('--unix-newlines');
+ }
+
+ if (true === $this->scss || (null === $this->scss && 'scss' == pathinfo($asset->getSourcePath(), PATHINFO_EXTENSION))) {
+ $pb->add('--scss');
+ }
+
+ if ($this->style) {
+ $pb->add('--style')->add($this->style);
+ }
+
+ if ($this->precision) {
+ $pb->add('--precision')->add($this->precision);
+ }
+
+ if ($this->quiet) {
+ $pb->add('--quiet');
+ }
+
+ if ($this->debugInfo) {
+ $pb->add('--debug-info');
+ }
+
+ if ($this->lineNumbers) {
+ $pb->add('--line-numbers');
+ }
+
+ if ($this->sourceMap) {
+ $pb->add('--sourcemap');
+ }
+
+ foreach ($this->loadPaths as $loadPath) {
+ $pb->add('--load-path')->add($loadPath);
+ }
+
+ if ($this->cacheLocation) {
+ $pb->add('--cache-location')->add($this->cacheLocation);
+ }
+
+ if ($this->noCache) {
+ $pb->add('--no-cache');
+ }
+
+ if ($this->compass) {
+ $pb->add('--compass');
+ }
+
+ // input
+ $pb->add($input = FilesystemUtils::createTemporaryFile('sass'));
+ file_put_contents($input, $asset->getContent());
+
+ $proc = $pb->getProcess();
+ $code = $proc->run();
+ unlink($input);
+
+ if (0 !== $code) {
+ throw FilterException::fromProcess($proc)->setInput($asset->getContent());
+ }
+
+ $asset->setContent($proc->getOutput());
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/ScssFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/ScssFilter.php
new file mode 100644
index 0000000..f8be046
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/ScssFilter.php
@@ -0,0 +1,28 @@
+
+ */
+class ScssFilter extends SassFilter
+{
+ public function __construct($sassPath = '/usr/bin/sass', $rubyPath = null)
+ {
+ parent::__construct($sassPath, $rubyPath);
+
+ $this->setScss(true);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/SassphpFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/SassphpFilter.php
new file mode 100644
index 0000000..5d4ed1e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/SassphpFilter.php
@@ -0,0 +1,132 @@
+
+ */
+class SassphpFilter implements DependencyExtractorInterface
+{
+ private $includePaths = array();
+ private $outputStyle;
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ $sass = new \Sass();
+
+ $includePaths = array_merge(
+ array($asset->getSourceDirectory()),
+ $this->includePaths
+ );
+ $sass->setIncludePath(implode(':', $includePaths));
+
+ if ($this->outputStyle) {
+ $sass->setStyle($this->outputStyle);
+ }
+
+ $css = $sass->compile($asset->getContent());
+
+ $asset->setContent($css);
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ }
+
+ public function setOutputStyle($outputStyle)
+ {
+ $this->outputStyle = $outputStyle;
+ }
+
+ public function setIncludePaths(array $paths)
+ {
+ $this->includePaths = $paths;
+ }
+
+ public function addIncludePath($path)
+ {
+ $this->includePaths[] = $path;
+ }
+
+ public function getChildren(AssetFactory $factory, $content, $loadPath = null)
+ {
+ $children = array();
+
+ $includePaths = $this->includePaths;
+ if (null !== $loadPath && !in_array($loadPath, $includePaths)) {
+ array_unshift($includePaths, $loadPath);
+ }
+
+ if (empty($includePaths)) {
+ return $children;
+ }
+
+ foreach (CssUtils::extractImports($content) as $reference) {
+ if ('.css' === substr($reference, -4)) {
+ continue;
+ }
+
+ // the reference may or may not have an extension or be a partial
+ if (pathinfo($reference, PATHINFO_EXTENSION)) {
+ $needles = array(
+ $reference,
+ $this->partialize($reference),
+ );
+ } else {
+ $needles = array(
+ $reference . '.scss',
+ $this->partialize($reference) . '.scss',
+ );
+ }
+
+ foreach ($includePaths as $includePath) {
+ foreach ($needles as $needle) {
+ if (file_exists($file = $includePath . '/' . $needle)) {
+ $child = $factory->createAsset($file, array(), array('root' => $includePath));
+ $children[] = $child;
+ $child->load();
+ $children = array_merge(
+ $children,
+ $this->getChildren($factory, $child->getContent(), $includePath)
+ );
+ }
+ }
+ }
+ }
+
+ return $children;
+ }
+
+ private function partialize($reference)
+ {
+ $parts = pathinfo($reference);
+
+ if ('.' === $parts['dirname']) {
+ $partial = '_' . $parts['filename'];
+ } else {
+ $partial = $parts['dirname'] . DIRECTORY_SEPARATOR . '_' . $parts['filename'];
+ }
+
+ if (isset($parts['extension'])) {
+ $partial .= '.' . $parts['extension'];
+ }
+
+ return $partial;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/ScssphpFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/ScssphpFilter.php
new file mode 100644
index 0000000..c2252a1
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/ScssphpFilter.php
@@ -0,0 +1,147 @@
+
+ */
+class ScssphpFilter implements DependencyExtractorInterface
+{
+ private $compass = false;
+ private $importPaths = array();
+ private $customFunctions = array();
+ private $formatter;
+ private $variables = array();
+
+ public function enableCompass($enable = true)
+ {
+ $this->compass = (Boolean) $enable;
+ }
+
+ public function isCompassEnabled()
+ {
+ return $this->compass;
+ }
+
+ public function setFormatter($formatter)
+ {
+ $legacyFormatters = array(
+ 'scss_formatter' => 'Leafo\ScssPhp\Formatter\Expanded',
+ 'scss_formatter_nested' => 'Leafo\ScssPhp\Formatter\Nested',
+ 'scss_formatter_compressed' => 'Leafo\ScssPhp\Formatter\Compressed',
+ 'scss_formatter_crunched' => 'Leafo\ScssPhp\Formatter\Crunched',
+ );
+
+ if (isset($legacyFormatters[$formatter])) {
+ @trigger_error(sprintf('The scssphp formatter `%s` is deprecated. Use `%s` instead.', $formatter, $legacyFormatters[$formatter]), E_USER_DEPRECATED);
+
+ $formatter = $legacyFormatters[$formatter];
+ }
+
+ $this->formatter = $formatter;
+ }
+
+ public function setVariables(array $variables)
+ {
+ $this->variables = $variables;
+ }
+
+ public function addVariable($variable)
+ {
+ $this->variables[] = $variable;
+ }
+
+ public function setImportPaths(array $paths)
+ {
+ $this->importPaths = $paths;
+ }
+
+ public function addImportPath($path)
+ {
+ $this->importPaths[] = $path;
+ }
+
+ public function registerFunction($name, $callable)
+ {
+ $this->customFunctions[$name] = $callable;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ $sc = new Compiler();
+
+ if ($this->compass) {
+ new \scss_compass($sc);
+ }
+
+ if ($dir = $asset->getSourceDirectory()) {
+ $sc->addImportPath($dir);
+ }
+
+ foreach ($this->importPaths as $path) {
+ $sc->addImportPath($path);
+ }
+
+ foreach ($this->customFunctions as $name => $callable) {
+ $sc->registerFunction($name, $callable);
+ }
+
+ if ($this->formatter) {
+ $sc->setFormatter($this->formatter);
+ }
+
+ if (!empty($this->variables)) {
+ $sc->setVariables($this->variables);
+ }
+
+ $asset->setContent($sc->compile($asset->getContent()));
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ }
+
+ public function getChildren(AssetFactory $factory, $content, $loadPath = null)
+ {
+ $sc = new Compiler();
+ if ($loadPath !== null) {
+ $sc->addImportPath($loadPath);
+ }
+
+ foreach ($this->importPaths as $path) {
+ $sc->addImportPath($path);
+ }
+
+ $children = array();
+ foreach (CssUtils::extractImports($content) as $match) {
+ $file = $sc->findImport($match);
+ if ($file) {
+ $children[] = $child = $factory->createAsset($file, array(), array('root' => $loadPath));
+ $child->load();
+ $children = array_merge($children, $this->getChildren($factory, $child->getContent(), $loadPath));
+ }
+ }
+
+ return $children;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/SeparatorFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/SeparatorFilter.php
new file mode 100644
index 0000000..a2c02ab
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/SeparatorFilter.php
@@ -0,0 +1,47 @@
+
+ */
+class SeparatorFilter implements FilterInterface
+{
+ /**
+ * @var string
+ */
+ private $separator;
+
+ /**
+ * Constructor.
+ *
+ * @param string $separator Separator to use between assets
+ */
+ public function __construct($separator = ';')
+ {
+ $this->separator = $separator;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ $asset->setContent($asset->getContent() . $this->separator);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/SprocketsFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/SprocketsFilter.php
new file mode 100644
index 0000000..ed46b0f
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/SprocketsFilter.php
@@ -0,0 +1,152 @@
+
+ */
+class SprocketsFilter extends BaseProcessFilter implements DependencyExtractorInterface
+{
+ private $sprocketsLib;
+ private $rubyBin;
+ private $includeDirs;
+ private $assetRoot;
+
+ /**
+ * Constructor.
+ *
+ * @param string $sprocketsLib Path to the Sprockets lib/ directory
+ * @param string $rubyBin Path to the ruby binary
+ */
+ public function __construct($sprocketsLib = null, $rubyBin = '/usr/bin/ruby')
+ {
+ $this->sprocketsLib = $sprocketsLib;
+ $this->rubyBin = $rubyBin;
+ $this->includeDirs = array();
+ }
+
+ public function addIncludeDir($directory)
+ {
+ $this->includeDirs[] = $directory;
+ }
+
+ public function setAssetRoot($assetRoot)
+ {
+ $this->assetRoot = $assetRoot;
+ }
+
+ /**
+ * Hack around a bit, get the job done.
+ */
+ public function filterLoad(AssetInterface $asset)
+ {
+ static $format = <<<'EOF'
+#!/usr/bin/env ruby
+
+require %s
+%s
+options = { :load_path => [],
+ :source_files => [%s],
+ :expand_paths => false }
+
+%ssecretary = Sprockets::Secretary.new(options)
+secretary.install_assets if options[:asset_root]
+print secretary.concatenation
+
+EOF;
+
+ $more = '';
+
+ foreach ($this->includeDirs as $directory) {
+ $more .= 'options[:load_path] << '.var_export($directory, true)."\n";
+ }
+
+ if (null !== $this->assetRoot) {
+ $more .= 'options[:asset_root] = '.var_export($this->assetRoot, true)."\n";
+ }
+
+ if ($more) {
+ $more .= "\n";
+ }
+
+ $tmpAsset = FilesystemUtils::createTemporaryFile('sprockets_asset');
+ file_put_contents($tmpAsset, $asset->getContent());
+
+ $input = FilesystemUtils::createTemporaryFile('sprockets_in');
+ file_put_contents($input, sprintf($format,
+ $this->sprocketsLib
+ ? sprintf('File.join(%s, \'sprockets\')', var_export($this->sprocketsLib, true))
+ : '\'sprockets\'',
+ $this->getHack($asset),
+ var_export($tmpAsset, true),
+ $more
+ ));
+
+ $pb = $this->createProcessBuilder(array(
+ $this->rubyBin,
+ $input,
+ ));
+
+ $proc = $pb->getProcess();
+ $code = $proc->run();
+ unlink($tmpAsset);
+ unlink($input);
+
+ if (0 !== $code) {
+ throw FilterException::fromProcess($proc)->setInput($asset->getContent());
+ }
+
+ $asset->setContent($proc->getOutput());
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ }
+
+ public function getChildren(AssetFactory $factory, $content, $loadPath = null)
+ {
+ // todo
+ return array();
+ }
+
+ private function getHack(AssetInterface $asset)
+ {
+ static $format = <<<'EOF'
+
+module Sprockets
+ class Preprocessor
+ protected
+ def pathname_for_relative_require_from(source_line)
+ Sprockets::Pathname.new(@environment, File.join(%s, location_from(source_line)))
+ end
+ end
+end
+
+EOF;
+
+ if ($dir = $asset->getSourceDirectory()) {
+ return sprintf($format, var_export($dir, true));
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/StylusFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/StylusFilter.php
new file mode 100644
index 0000000..aed7861
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/StylusFilter.php
@@ -0,0 +1,126 @@
+
+ */
+class StylusFilter extends BaseNodeFilter implements DependencyExtractorInterface
+{
+ private $nodeBin;
+ private $compress;
+ private $useNib;
+
+ /**
+ * Constructs filter.
+ *
+ * @param string $nodeBin The path to the node binary
+ * @param array $nodePaths An array of node paths
+ */
+ public function __construct($nodeBin = '/usr/bin/node', array $nodePaths = array())
+ {
+ $this->nodeBin = $nodeBin;
+ $this->setNodePaths($nodePaths);
+ }
+
+ /**
+ * Enable output compression.
+ *
+ * @param boolean $compress
+ */
+ public function setCompress($compress)
+ {
+ $this->compress = $compress;
+ }
+
+ /**
+ * Enable the use of Nib
+ *
+ * @param boolean $useNib
+ */
+ public function setUseNib($useNib)
+ {
+ $this->useNib = $useNib;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function filterLoad(AssetInterface $asset)
+ {
+ static $format = <<<'EOF'
+var stylus = require('stylus');
+var sys = require(process.binding('natives').util ? 'util' : 'sys');
+
+stylus(%s, %s)%s.render(function(e, css){
+ if (e) {
+ throw e;
+ }
+
+ sys.print(css);
+ process.exit(0);
+});
+
+EOF;
+
+ // parser options
+ $parserOptions = array();
+ if ($dir = $asset->getSourceDirectory()) {
+ $parserOptions['paths'] = array($dir);
+ $parserOptions['filename'] = basename($asset->getSourcePath());
+ }
+
+ if (null !== $this->compress) {
+ $parserOptions['compress'] = $this->compress;
+ }
+
+ $pb = $this->createProcessBuilder();
+
+ $pb->add($this->nodeBin)->add($input = FilesystemUtils::createTemporaryFile('stylus'));
+ file_put_contents($input, sprintf($format,
+ json_encode($asset->getContent()),
+ json_encode($parserOptions),
+ $this->useNib ? '.use(require(\'nib\')())' : ''
+ ));
+
+ $proc = $pb->getProcess();
+ $code = $proc->run();
+ unlink($input);
+
+ if (0 !== $code) {
+ throw FilterException::fromProcess($proc)->setInput($asset->getContent());
+ }
+
+ $asset->setContent($proc->getOutput());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function filterDump(AssetInterface $asset)
+ {
+ }
+
+ public function getChildren(AssetFactory $factory, $content, $loadPath = null)
+ {
+ // todo
+ return array();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/TypeScriptFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/TypeScriptFilter.php
new file mode 100644
index 0000000..24aee31
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/TypeScriptFilter.php
@@ -0,0 +1,80 @@
+
+ */
+class TypeScriptFilter extends BaseNodeFilter
+{
+ private $tscBin;
+ private $nodeBin;
+
+ public function __construct($tscBin = '/usr/bin/tsc', $nodeBin = null)
+ {
+ $this->tscBin = $tscBin;
+ $this->nodeBin = $nodeBin;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ $pb = $this->createProcessBuilder($this->nodeBin
+ ? array($this->nodeBin, $this->tscBin)
+ : array($this->tscBin));
+
+ if ($sourcePath = $asset->getSourcePath()) {
+ $templateName = basename($sourcePath);
+ } else {
+ $templateName = 'asset';
+ }
+
+ $inputDirPath = FilesystemUtils::createThrowAwayDirectory('typescript_in');
+ $inputPath = $inputDirPath.DIRECTORY_SEPARATOR.$templateName.'.ts';
+ $outputPath = FilesystemUtils::createTemporaryFile('typescript_out');
+
+ file_put_contents($inputPath, $asset->getContent());
+
+ $pb->add($inputPath)->add('--out')->add($outputPath);
+
+ $proc = $pb->getProcess();
+ $code = $proc->run();
+ unlink($inputPath);
+ rmdir($inputDirPath);
+
+ if (0 !== $code) {
+ if (file_exists($outputPath)) {
+ unlink($outputPath);
+ }
+ throw FilterException::fromProcess($proc)->setInput($asset->getContent());
+ }
+
+ if (!file_exists($outputPath)) {
+ throw new \RuntimeException('Error creating output file.');
+ }
+
+ $compiledJs = file_get_contents($outputPath);
+ unlink($outputPath);
+
+ $asset->setContent($compiledJs);
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyCssFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyCssFilter.php
new file mode 100644
index 0000000..d534fa3
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyCssFilter.php
@@ -0,0 +1,120 @@
+
+ */
+class UglifyCssFilter extends BaseNodeFilter
+{
+ private $uglifycssBin;
+ private $nodeBin;
+
+ private $expandVars;
+ private $uglyComments;
+ private $cuteComments;
+
+ /**
+ * @param string $uglifycssBin Absolute path to the uglifycss executable
+ * @param string $nodeBin Absolute path to the folder containg node.js executable
+ */
+ public function __construct($uglifycssBin = '/usr/bin/uglifycss', $nodeBin = null)
+ {
+ $this->uglifycssBin = $uglifycssBin;
+ $this->nodeBin = $nodeBin;
+ }
+
+ /**
+ * Expand variables
+ * @param bool $expandVars True to enable
+ */
+ public function setExpandVars($expandVars)
+ {
+ $this->expandVars = $expandVars;
+ }
+
+ /**
+ * Remove newlines within preserved comments
+ * @param bool $uglyComments True to enable
+ */
+ public function setUglyComments($uglyComments)
+ {
+ $this->uglyComments = $uglyComments;
+ }
+
+ /**
+ * Preserve newlines within and around preserved comments
+ * @param bool $cuteComments True to enable
+ */
+ public function setCuteComments($cuteComments)
+ {
+ $this->cuteComments = $cuteComments;
+ }
+
+ /**
+ * @see Assetic\Filter\FilterInterface::filterLoad()
+ */
+ public function filterLoad(AssetInterface $asset)
+ {
+ }
+
+ /**
+ * Run the asset through UglifyJs
+ *
+ * @see Assetic\Filter\FilterInterface::filterDump()
+ */
+ public function filterDump(AssetInterface $asset)
+ {
+ $pb = $this->createProcessBuilder($this->nodeBin
+ ? array($this->nodeBin, $this->uglifycssBin)
+ : array($this->uglifycssBin));
+
+ if ($this->expandVars) {
+ $pb->add('--expand-vars');
+ }
+
+ if ($this->uglyComments) {
+ $pb->add('--ugly-comments');
+ }
+
+ if ($this->cuteComments) {
+ $pb->add('--cute-comments');
+ }
+
+ // input and output files
+ $input = FilesystemUtils::createTemporaryFile('uglifycss');
+
+ file_put_contents($input, $asset->getContent());
+ $pb->add($input);
+
+ $proc = $pb->getProcess();
+ $code = $proc->run();
+ unlink($input);
+
+ if (127 === $code) {
+ throw new \RuntimeException('Path to node executable could not be resolved.');
+ }
+
+ if (0 !== $code) {
+ throw FilterException::fromProcess($proc)->setInput($asset->getContent());
+ }
+
+ $asset->setContent($proc->getOutput());
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyJs2Filter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyJs2Filter.php
new file mode 100644
index 0000000..c0441ae
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyJs2Filter.php
@@ -0,0 +1,152 @@
+
+ */
+class UglifyJs2Filter extends BaseNodeFilter
+{
+ private $uglifyjsBin;
+ private $nodeBin;
+ private $compress;
+ private $beautify;
+ private $mangle;
+ private $screwIe8;
+ private $comments;
+ private $wrap;
+ private $defines;
+
+ public function __construct($uglifyjsBin = '/usr/bin/uglifyjs', $nodeBin = null)
+ {
+ $this->uglifyjsBin = $uglifyjsBin;
+ $this->nodeBin = $nodeBin;
+ }
+
+ public function setCompress($compress)
+ {
+ $this->compress = $compress;
+ }
+
+ public function setBeautify($beautify)
+ {
+ $this->beautify = $beautify;
+ }
+
+ public function setMangle($mangle)
+ {
+ $this->mangle = $mangle;
+ }
+
+ public function setScrewIe8($screwIe8)
+ {
+ $this->screwIe8 = $screwIe8;
+ }
+
+ public function setComments($comments)
+ {
+ $this->comments = $comments;
+ }
+
+ public function setWrap($wrap)
+ {
+ $this->wrap = $wrap;
+ }
+
+ public function setDefines(array $defines)
+ {
+ $this->defines = $defines;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ $pb = $this->createProcessBuilder(
+ $this->nodeBin
+ ? array($this->nodeBin, $this->uglifyjsBin)
+ : array($this->uglifyjsBin)
+ );
+
+ if ($this->compress) {
+ $pb->add('--compress');
+
+ if (is_string($this->compress) && !empty($this->compress)) {
+ $pb->add($this->compress);
+ }
+ }
+
+ if ($this->beautify) {
+ $pb->add('--beautify');
+ }
+
+ if ($this->mangle) {
+ $pb->add('--mangle');
+ }
+
+ if ($this->screwIe8) {
+ $pb->add('--screw-ie8');
+ }
+
+ if ($this->comments) {
+ $pb->add('--comments')->add(true === $this->comments ? 'all' : $this->comments);
+ }
+
+ if ($this->wrap) {
+ $pb->add('--wrap')->add($this->wrap);
+ }
+
+ if ($this->defines) {
+ $pb->add('--define')->add(implode(',', $this->defines));
+ }
+
+ // input and output files
+ $input = FilesystemUtils::createTemporaryFile('uglifyjs2_in');
+ $output = FilesystemUtils::createTemporaryFile('uglifyjs2_out');
+
+ file_put_contents($input, $asset->getContent());
+ $pb->add('-o')->add($output)->add($input);
+
+ $proc = $pb->getProcess();
+ $code = $proc->run();
+ unlink($input);
+
+ if (0 !== $code) {
+ if (file_exists($output)) {
+ unlink($output);
+ }
+
+ if (127 === $code) {
+ throw new \RuntimeException('Path to node executable could not be resolved.');
+ }
+
+ throw FilterException::fromProcess($proc)->setInput($asset->getContent());
+ }
+
+ if (!file_exists($output)) {
+ throw new \RuntimeException('Error creating output file.');
+ }
+
+ $asset->setContent(file_get_contents($output));
+
+ unlink($output);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyJsFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyJsFilter.php
new file mode 100644
index 0000000..ff9ad66
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyJsFilter.php
@@ -0,0 +1,160 @@
+
+ */
+class UglifyJsFilter extends BaseNodeFilter
+{
+ private $uglifyjsBin;
+ private $nodeBin;
+
+ private $noCopyright;
+ private $beautify;
+ private $unsafe;
+ private $mangle;
+ private $defines;
+
+ /**
+ * @param string $uglifyjsBin Absolute path to the uglifyjs executable
+ * @param string $nodeBin Absolute path to the folder containg node.js executable
+ */
+ public function __construct($uglifyjsBin = '/usr/bin/uglifyjs', $nodeBin = null)
+ {
+ $this->uglifyjsBin = $uglifyjsBin;
+ $this->nodeBin = $nodeBin;
+ }
+
+ /**
+ * Removes the first block of comments as well
+ * @param bool $noCopyright True to enable
+ */
+ public function setNoCopyright($noCopyright)
+ {
+ $this->noCopyright = $noCopyright;
+ }
+
+ /**
+ * Output indented code
+ * @param bool $beautify True to enable
+ */
+ public function setBeautify($beautify)
+ {
+ $this->beautify = $beautify;
+ }
+
+ /**
+ * Enable additional optimizations that are known to be unsafe in some situations.
+ * @param bool $unsafe True to enable
+ */
+ public function setUnsafe($unsafe)
+ {
+ $this->unsafe = $unsafe;
+ }
+
+ /**
+ * Safely mangle variable and function names for greater file compress.
+ * @param bool $mangle True to enable
+ */
+ public function setMangle($mangle)
+ {
+ $this->mangle = $mangle;
+ }
+
+ public function setDefines(array $defines)
+ {
+ $this->defines = $defines;
+ }
+
+ /**
+ * @see Assetic\Filter\FilterInterface::filterLoad()
+ */
+ public function filterLoad(AssetInterface $asset)
+ {
+ }
+
+ /**
+ * Run the asset through UglifyJs
+ *
+ * @see Assetic\Filter\FilterInterface::filterDump()
+ */
+ public function filterDump(AssetInterface $asset)
+ {
+ $pb = $this->createProcessBuilder(
+ $this->nodeBin
+ ? array($this->nodeBin, $this->uglifyjsBin)
+ : array($this->uglifyjsBin)
+ );
+
+ if ($this->noCopyright) {
+ $pb->add('--no-copyright');
+ }
+
+ if ($this->beautify) {
+ $pb->add('--beautify');
+ }
+
+ if ($this->unsafe) {
+ $pb->add('--unsafe');
+ }
+
+ if (false === $this->mangle) {
+ $pb->add('--no-mangle');
+ }
+
+ if ($this->defines) {
+ foreach ($this->defines as $define) {
+ $pb->add('-d')->add($define);
+ }
+ }
+
+ // input and output files
+ $input = FilesystemUtils::createTemporaryFile('uglifyjs_in');
+ $output = FilesystemUtils::createTemporaryFile('uglifyjs_out');
+
+ file_put_contents($input, $asset->getContent());
+ $pb->add('-o')->add($output)->add($input);
+
+ $proc = $pb->getProcess();
+ $code = $proc->run();
+ unlink($input);
+
+ if (0 !== $code) {
+ if (file_exists($output)) {
+ unlink($output);
+ }
+
+ if (127 === $code) {
+ throw new \RuntimeException('Path to node executable could not be resolved.');
+ }
+
+ throw FilterException::fromProcess($proc)->setInput($asset->getContent());
+ }
+
+ if (!file_exists($output)) {
+ throw new \RuntimeException('Error creating output file.');
+ }
+
+ $uglifiedJs = file_get_contents($output);
+ unlink($output);
+
+ $asset->setContent($uglifiedJs);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/BaseCompressorFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/BaseCompressorFilter.php
new file mode 100644
index 0000000..b3ec267
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/BaseCompressorFilter.php
@@ -0,0 +1,117 @@
+
+ */
+abstract class BaseCompressorFilter extends BaseProcessFilter
+{
+ private $jarPath;
+ private $javaPath;
+ private $charset;
+ private $lineBreak;
+ private $stackSize;
+
+ public function __construct($jarPath, $javaPath = '/usr/bin/java')
+ {
+ $this->jarPath = $jarPath;
+ $this->javaPath = $javaPath;
+ }
+
+ public function setCharset($charset)
+ {
+ $this->charset = $charset;
+ }
+
+ public function setLineBreak($lineBreak)
+ {
+ $this->lineBreak = $lineBreak;
+ }
+
+ public function setStackSize($stackSize)
+ {
+ $this->stackSize = $stackSize;
+ }
+
+ public function filterLoad(AssetInterface $asset)
+ {
+ }
+
+ /**
+ * Compresses a string.
+ *
+ * @param string $content The content to compress
+ * @param string $type The type of content, either "js" or "css"
+ * @param array $options An indexed array of additional options
+ *
+ * @return string The compressed content
+ */
+ protected function compress($content, $type, $options = array())
+ {
+ $pb = $this->createProcessBuilder(array($this->javaPath));
+
+ if (null !== $this->stackSize) {
+ $pb->add('-Xss'.$this->stackSize);
+ }
+
+ $pb->add('-jar')->add($this->jarPath);
+
+ foreach ($options as $option) {
+ $pb->add($option);
+ }
+
+ if (null !== $this->charset) {
+ $pb->add('--charset')->add($this->charset);
+ }
+
+ if (null !== $this->lineBreak) {
+ $pb->add('--line-break')->add($this->lineBreak);
+ }
+
+ // input and output files
+ $tempDir = FilesystemUtils::getTemporaryDirectory();
+ $input = tempnam($tempDir, 'assetic_yui_input');
+ $output = tempnam($tempDir, 'assetic_yui_output');
+ file_put_contents($input, $content);
+ $pb->add('-o')->add($output)->add('--type')->add($type)->add($input);
+
+ $proc = $pb->getProcess();
+ $code = $proc->run();
+ unlink($input);
+
+ if (0 !== $code) {
+ if (file_exists($output)) {
+ unlink($output);
+ }
+
+ throw FilterException::fromProcess($proc)->setInput($content);
+ }
+
+ if (!file_exists($output)) {
+ throw new \RuntimeException('Error creating output file.');
+ }
+
+ $retval = file_get_contents($output);
+ unlink($output);
+
+ return $retval;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/CssCompressorFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/CssCompressorFilter.php
new file mode 100644
index 0000000..077c6d5
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/CssCompressorFilter.php
@@ -0,0 +1,28 @@
+
+ */
+class CssCompressorFilter extends BaseCompressorFilter
+{
+ public function filterDump(AssetInterface $asset)
+ {
+ $asset->setContent($this->compress($asset->getContent(), 'css'));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/JsCompressorFilter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/JsCompressorFilter.php
new file mode 100644
index 0000000..b2a59fb
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/JsCompressorFilter.php
@@ -0,0 +1,61 @@
+
+ */
+class JsCompressorFilter extends BaseCompressorFilter
+{
+ private $nomunge;
+ private $preserveSemi;
+ private $disableOptimizations;
+
+ public function setNomunge($nomunge = true)
+ {
+ $this->nomunge = $nomunge;
+ }
+
+ public function setPreserveSemi($preserveSemi)
+ {
+ $this->preserveSemi = $preserveSemi;
+ }
+
+ public function setDisableOptimizations($disableOptimizations)
+ {
+ $this->disableOptimizations = $disableOptimizations;
+ }
+
+ public function filterDump(AssetInterface $asset)
+ {
+ $options = array();
+
+ if ($this->nomunge) {
+ $options[] = '--nomunge';
+ }
+
+ if ($this->preserveSemi) {
+ $options[] = '--preserve-semi';
+ }
+
+ if ($this->disableOptimizations) {
+ $options[] = '--disable-optimizations';
+ }
+
+ $asset->setContent($this->compress($asset->getContent(), 'js', $options));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/FilterManager.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/FilterManager.php
new file mode 100644
index 0000000..e2820d9
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/FilterManager.php
@@ -0,0 +1,64 @@
+
+ */
+class FilterManager
+{
+ private $filters = array();
+
+ public function set($alias, FilterInterface $filter)
+ {
+ $this->checkName($alias);
+
+ $this->filters[$alias] = $filter;
+ }
+
+ public function get($alias)
+ {
+ if (!isset($this->filters[$alias])) {
+ throw new \InvalidArgumentException(sprintf('There is no "%s" filter.', $alias));
+ }
+
+ return $this->filters[$alias];
+ }
+
+ public function has($alias)
+ {
+ return isset($this->filters[$alias]);
+ }
+
+ public function getNames()
+ {
+ return array_keys($this->filters);
+ }
+
+ /**
+ * Checks that a name is valid.
+ *
+ * @param string $name An asset name candidate
+ *
+ * @throws \InvalidArgumentException If the asset name is invalid
+ */
+ protected function checkName($name)
+ {
+ if (!ctype_alnum(str_replace('_', '', $name))) {
+ throw new \InvalidArgumentException(sprintf('The name "%s" is invalid.', $name));
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Util/CssUtils.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Util/CssUtils.php
new file mode 100644
index 0000000..ebc44b0
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Util/CssUtils.php
@@ -0,0 +1,138 @@
+
+ */
+abstract class CssUtils
+{
+ const REGEX_URLS = '/url\((["\']?)(?P.*?)(\\1)\)/';
+ const REGEX_IMPORTS = '/@import (?:url\()?(\'|"|)(?P[^\'"\)\n\r]*)\1\)?;?/';
+ const REGEX_IMPORTS_NO_URLS = '/@import (?!url\()(\'|"|)(?P[^\'"\)\n\r]*)\1;?/';
+ const REGEX_IE_FILTERS = '/src=(["\']?)(?P.*?)\\1/';
+ const REGEX_COMMENTS = '/(\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)/';
+
+ /**
+ * Filters all references -- url() and "@import" -- through a callable.
+ *
+ * @param string $content The CSS
+ * @param callable $callback A PHP callable
+ *
+ * @return string The filtered CSS
+ */
+ public static function filterReferences($content, $callback)
+ {
+ $content = static::filterUrls($content, $callback);
+ $content = static::filterImports($content, $callback, false);
+ $content = static::filterIEFilters($content, $callback);
+
+ return $content;
+ }
+
+ /**
+ * Filters all CSS url()'s through a callable.
+ *
+ * @param string $content The CSS
+ * @param callable $callback A PHP callable
+ *
+ * @return string The filtered CSS
+ */
+ public static function filterUrls($content, $callback)
+ {
+ $pattern = static::REGEX_URLS;
+
+ return static::filterCommentless($content, function ($part) use (& $callback, $pattern) {
+ return preg_replace_callback($pattern, $callback, $part);
+ });
+ }
+
+ /**
+ * Filters all CSS imports through a callable.
+ *
+ * @param string $content The CSS
+ * @param callable $callback A PHP callable
+ * @param Boolean $includeUrl Whether to include url() in the pattern
+ *
+ * @return string The filtered CSS
+ */
+ public static function filterImports($content, $callback, $includeUrl = true)
+ {
+ $pattern = $includeUrl ? static::REGEX_IMPORTS : static::REGEX_IMPORTS_NO_URLS;
+
+ return static::filterCommentless($content, function ($part) use (& $callback, $pattern) {
+ return preg_replace_callback($pattern, $callback, $part);
+ });
+ }
+
+ /**
+ * Filters all IE filters (AlphaImageLoader filter) through a callable.
+ *
+ * @param string $content The CSS
+ * @param callable $callback A PHP callable
+ *
+ * @return string The filtered CSS
+ */
+ public static function filterIEFilters($content, $callback)
+ {
+ $pattern = static::REGEX_IE_FILTERS;
+
+ return static::filterCommentless($content, function ($part) use (& $callback, $pattern) {
+ return preg_replace_callback($pattern, $callback, $part);
+ });
+ }
+
+ /**
+ * Filters each non-comment part through a callable.
+ *
+ * @param string $content The CSS
+ * @param callable $callback A PHP callable
+ *
+ * @return string The filtered CSS
+ */
+ public static function filterCommentless($content, $callback)
+ {
+ $result = '';
+ foreach (preg_split(static::REGEX_COMMENTS, $content, -1, PREG_SPLIT_DELIM_CAPTURE) as $part) {
+ if (!preg_match(static::REGEX_COMMENTS, $part, $match) || $part != $match[0]) {
+ $part = call_user_func($callback, $part);
+ }
+
+ $result .= $part;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Extracts all references from the supplied CSS content.
+ *
+ * @param string $content The CSS content
+ *
+ * @return array An array of unique URLs
+ */
+ public static function extractImports($content)
+ {
+ $imports = array();
+ static::filterImports($content, function ($matches) use (&$imports) {
+ $imports[] = $matches['url'];
+ });
+
+ return array_unique(array_filter($imports));
+ }
+
+ final private function __construct()
+ {
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Util/FilesystemUtils.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Util/FilesystemUtils.php
new file mode 100644
index 0000000..90e6512
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Util/FilesystemUtils.php
@@ -0,0 +1,84 @@
+
+ */
+class FilesystemUtils
+{
+ /**
+ * Recursively removes a directory from the filesystem.
+ */
+ public static function removeDirectory($directory)
+ {
+ $inner = new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS);
+ $outer = new \RecursiveIteratorIterator($inner, \RecursiveIteratorIterator::SELF_FIRST);
+
+ // remove the files first
+ foreach ($outer as $file) {
+ if ($file->isFile()) {
+ unlink($file);
+ }
+ }
+
+ // remove the sub-directories next
+ $files = iterator_to_array($outer);
+ foreach (array_reverse($files) as $file) {
+ /** @var \SplFileInfo $file */
+ if ($file->isDir()) {
+ rmdir($file);
+ }
+ }
+
+ // finally the directory itself
+ rmdir($directory);
+ }
+
+ /**
+ * Creates a throw-away directory.
+ *
+ * This is not considered a "temporary" directory because it will not be
+ * automatically deleted at the end of the request or process. It must be
+ * deleted manually.
+ *
+ * @param string $prefix A prefix for the directory name
+ *
+ * @return string The directory path
+ */
+ public static function createThrowAwayDirectory($prefix)
+ {
+ $directory = self::getTemporaryDirectory().DIRECTORY_SEPARATOR.uniqid('assetic_'.$prefix);
+ mkdir($directory);
+
+ return $directory;
+ }
+
+ /**
+ * Creates a temporary file.
+ *
+ * @param string $prefix A prefix for the file name
+ *
+ * @return string The file path
+ */
+ public static function createTemporaryFile($prefix)
+ {
+ return tempnam(self::getTemporaryDirectory(), 'assetic_'.$prefix);
+ }
+
+ public static function getTemporaryDirectory()
+ {
+ return realpath(sys_get_temp_dir());
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Util/LessUtils.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Util/LessUtils.php
new file mode 100644
index 0000000..1b53f2b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Util/LessUtils.php
@@ -0,0 +1,24 @@
+
+ */
+abstract class LessUtils extends CssUtils
+{
+ const REGEX_IMPORTS = '/@import(?:-once)? (?:\([a-z]*\) )?(?:url\()?(\'|"|)(?P[^\'"\)\n\r]*)\1\)?;?/';
+ const REGEX_IMPORTS_NO_URLS = '/@import(?:-once)? (?:\([a-z]*\) )?(?!url\()(\'|"|)(?P[^\'"\)\n\r]*)\1;?/';
+ const REGEX_COMMENTS = '/((?:\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)|\/\/[^\n]+)/';
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Util/SassUtils.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Util/SassUtils.php
new file mode 100644
index 0000000..9c07615
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Util/SassUtils.php
@@ -0,0 +1,22 @@
+
+ */
+abstract class SassUtils extends CssUtils
+{
+ const REGEX_COMMENTS = '/((?:\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)|\/\/[^\n]+)/';
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Util/TraversableString.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Util/TraversableString.php
new file mode 100644
index 0000000..400e97c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Util/TraversableString.php
@@ -0,0 +1,44 @@
+
+ */
+class TraversableString implements \IteratorAggregate, \Countable
+{
+ private $one;
+ private $many;
+
+ public function __construct($one, array $many)
+ {
+ $this->one = $one;
+ $this->many = $many;
+ }
+
+ public function getIterator()
+ {
+ return new \ArrayIterator($this->many);
+ }
+
+ public function count()
+ {
+ return count($this->many);
+ }
+
+ public function __toString()
+ {
+ return (string) $this->one;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Util/VarUtils.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Util/VarUtils.php
new file mode 100644
index 0000000..d9464c0
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/Util/VarUtils.php
@@ -0,0 +1,84 @@
+
+ */
+abstract class VarUtils
+{
+ /**
+ * Resolves variable placeholders.
+ *
+ * @param string $template A template string
+ * @param array $vars Variable names
+ * @param array $values Variable values
+ *
+ * @return string The resolved string
+ *
+ * @throws \InvalidArgumentException If there is a variable with no value
+ */
+ public static function resolve($template, array $vars, array $values)
+ {
+ $map = array();
+ foreach ($vars as $var) {
+ if (false === strpos($template, '{'.$var.'}')) {
+ continue;
+ }
+
+ if (!isset($values[$var])) {
+ throw new \InvalidArgumentException(sprintf('The template "%s" contains the variable "%s", but was not given any value for it.', $template, $var));
+ }
+
+ $map['{'.$var.'}'] = $values[$var];
+ }
+
+ return strtr($template, $map);
+ }
+
+ public static function getCombinations(array $vars, array $values)
+ {
+ if (!$vars) {
+ return array(array());
+ }
+
+ $combinations = array();
+ $nbValues = array();
+ foreach ($values as $var => $vals) {
+ if (!in_array($var, $vars, true)) {
+ continue;
+ }
+
+ $nbValues[$var] = count($vals);
+ }
+
+ for ($i = array_product($nbValues), $c = $i * 2; $i < $c; $i++) {
+ $k = $i;
+ $combination = array();
+
+ foreach ($vars as $var) {
+ $combination[$var] = $values[$var][$k % $nbValues[$var]];
+ $k = intval($k / $nbValues[$var]);
+ }
+
+ $combinations[] = $combination;
+ }
+
+ return $combinations;
+ }
+
+ final private function __construct()
+ {
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/ValueSupplierInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/ValueSupplierInterface.php
new file mode 100644
index 0000000..a99874e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/Assetic/ValueSupplierInterface.php
@@ -0,0 +1,29 @@
+
+ */
+interface ValueSupplierInterface
+{
+ /**
+ * Returns a map of values.
+ *
+ * @return array
+ */
+ public function getValues();
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/functions.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/functions.php
new file mode 100644
index 0000000..2af5bde
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/kriswallsmith/assetic/src/functions.php
@@ -0,0 +1,125 @@
+factory = $factory;
+}
+
+/**
+ * Returns an array of javascript URLs.
+ *
+ * @param array|string $inputs Input strings
+ * @param array|string $filters Filter names
+ * @param array $options An array of options
+ *
+ * @return array An array of javascript URLs
+ */
+function assetic_javascripts($inputs = array(), $filters = array(), array $options = array())
+{
+ if (!isset($options['output'])) {
+ $options['output'] = 'js/*.js';
+ }
+
+ return _assetic_urls($inputs, $filters, $options);
+}
+
+/**
+ * Returns an array of stylesheet URLs.
+ *
+ * @param array|string $inputs Input strings
+ * @param array|string $filters Filter names
+ * @param array $options An array of options
+ *
+ * @return array An array of stylesheet URLs
+ */
+function assetic_stylesheets($inputs = array(), $filters = array(), array $options = array())
+{
+ if (!isset($options['output'])) {
+ $options['output'] = 'css/*.css';
+ }
+
+ return _assetic_urls($inputs, $filters, $options);
+}
+
+/**
+ * Returns an image URL.
+ *
+ * @param string $input An input
+ * @param array|string $filters Filter names
+ * @param array $options An array of options
+ *
+ * @return string An image URL
+ */
+function assetic_image($input, $filters = array(), array $options = array())
+{
+ if (!isset($options['output'])) {
+ $options['output'] = 'images/*';
+ }
+
+ $urls = _assetic_urls($input, $filters, $options);
+
+ return current($urls);
+}
+
+/**
+ * Returns an array of asset urls.
+ *
+ * @param array|string $inputs Input strings
+ * @param array|string $filters Filter names
+ * @param array $options An array of options
+ *
+ * @return array An array of URLs
+ */
+function _assetic_urls($inputs = array(), $filters = array(), array $options = array())
+{
+ global $_assetic;
+
+ if (!is_array($inputs)) {
+ $inputs = array_filter(array_map('trim', explode(',', $inputs)));
+ }
+
+ if (!is_array($filters)) {
+ $filters = array_filter(array_map('trim', explode(',', $filters)));
+ }
+
+ $coll = $_assetic->factory->createAsset($inputs, $filters, $options);
+
+ $debug = isset($options['debug']) ? $options['debug'] : $_assetic->factory->isDebug();
+ $combine = isset($options['combine']) ? $options['combine'] : !$debug;
+
+ $one = $coll->getTargetPath();
+ if ($combine) {
+ $many = array($one);
+ } else {
+ $many = array();
+ foreach ($coll as $leaf) {
+ $many[] = $leaf->getTargetPath();
+ }
+ }
+
+ return new TraversableString($one, $many);
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/lmammino/jsmin4assetic/LICENSE b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/lmammino/jsmin4assetic/LICENSE
new file mode 100644
index 0000000..1f2dfac
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/lmammino/jsmin4assetic/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2013 Luciano Mammino
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/lmammino/jsmin4assetic/README.markdown b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/lmammino/jsmin4assetic/README.markdown
new file mode 100644
index 0000000..a910ce6
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/lmammino/jsmin4assetic/README.markdown
@@ -0,0 +1,21 @@
+JSMin4Assetic
+-------------
+
+Slight modified version of [nick4fake/JsMin](https://github.com/nick4fake/JsMin/) that works with [Assetic](https://github.com/kriswallsmith/assetic).
+
+The fastest solution to use JsMin and Assetic if you use [Composer](http://getcomposer.org/).
+
+
+Installation
+============
+
+It's heavily suggested to use
+Add the following lines to your `composer.json`
+
+``` javascript
+{
+ "require": {
+ "lmammino/jsmin4assetic": "1.0.*"
+ }
+}
+```
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/lmammino/jsmin4assetic/composer.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/lmammino/jsmin4assetic/composer.json
new file mode 100644
index 0000000..d65325d
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/lmammino/jsmin4assetic/composer.json
@@ -0,0 +1,26 @@
+{
+ "name": "lmammino/jsmin4assetic",
+ "type": "library",
+ "description":"Library for minifying JavaScript files using jsmin with assetic and composer",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Douglas Crockford",
+ "homepage":"http://www.crockford.com"
+ },
+ {
+ "name": "Bogdan Yurov",
+ "email":"bogdan@yurov.me"
+ },
+ {
+ "name": "Luciano Mammino",
+ "email":"lmammino@oryzone.com"
+ }
+ ],
+ "require": {
+ "php":">=5.3"
+ },
+ "autoload": {
+ "classmap": ["src/"]
+ }
+}
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/lmammino/jsmin4assetic/src/JSMin.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/lmammino/jsmin4assetic/src/JSMin.php
new file mode 100644
index 0000000..a825229
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/lmammino/jsmin4assetic/src/JSMin.php
@@ -0,0 +1,379 @@
+
+ * $minifiedJs = \JSMin\Minify::minify($js);
+ *
+ *
+ * This is a modified port of jsmin.c. Improvements:
+ *
+ * Does not choke on some regexp literals containing quote characters. E.g. /'/
+ *
+ * Spaces are preserved after some add/sub operators, so they are not mistakenly
+ * converted to post-inc/dec. E.g. a + ++b -> a+ ++b
+ *
+ * Preserves multi-line comments that begin with /*!
+ *
+ * PHP 5 or higher is required.
+ *
+ * Permission is hereby granted to use this version of the library under the
+ * same terms as jsmin.c, which has the following license:
+ *
+ * --
+ * Copyright (c) 2002 Douglas Crockford (www.crockford.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * The Software shall be used for Good, not Evil.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * --
+ *
+ * @package JSMin
+ * @author Ryan Grove (PHP port)
+ * @author Steve Clay (modifications + cleanup)
+ * @author Andrea Giammarchi (spaceBeforeRegExp)
+ * @author Luciano Mammino (assetic adaptation)
+ * @copyright 2002 Douglas Crockford (jsmin.c)
+ * @copyright 2008 Ryan Grove (PHP port)
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @link http://code.google.com/p/jsmin-php/
+ */
+class JSMin {
+
+ const ORD_LF = 10;
+ const ORD_SPACE = 32;
+ const ACTION_KEEP_A = 1;
+ const ACTION_DELETE_A = 2;
+ const ACTION_DELETE_A_B = 3;
+
+ protected $a = "\n";
+ protected $b = '';
+ protected $input = '';
+ protected $inputIndex = 0;
+ protected $inputLength = 0;
+ protected $lookAhead = null;
+ protected $output = '';
+ protected $lastByteOut = '';
+
+ /**
+ * Minify Javascript.
+ *
+ * @param string $js Javascript to be minified
+ *
+ * @return string
+ */
+ public static function minify($js) {
+ $jsmin = new static($js);
+ return $jsmin->min();
+ }
+
+ /**
+ * @param string $input
+ */
+ public function __construct($input) {
+ $this->input = $input;
+ }
+
+ /**
+ * Perform minification, return result
+ *
+ * @return string
+ */
+ public function min() {
+ if ($this->output !== '') { // min already run
+ return $this->output;
+ }
+
+ $mbIntEnc = null;
+ if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
+ $mbIntEnc = mb_internal_encoding();
+ mb_internal_encoding('8bit');
+ }
+ $this->input = str_replace("\r\n", "\n", $this->input);
+ $this->inputLength = strlen($this->input);
+
+ $this->action(self::ACTION_DELETE_A_B);
+
+ while ($this->a !== null) {
+ // determine next command
+ $command = self::ACTION_KEEP_A; // default
+ if ($this->a === ' ') {
+ if (($this->lastByteOut === '+' || $this->lastByteOut === '-')
+ && ($this->b === $this->lastByteOut)
+ ) {
+ // Don't delete this space. If we do, the addition/subtraction
+ // could be parsed as a post-increment
+ } elseif (!$this->isAlphaNum($this->b)) {
+ $command = self::ACTION_DELETE_A;
+ }
+ } elseif ($this->a === "\n") {
+ if ($this->b === ' ') {
+ $command = self::ACTION_DELETE_A_B;
+ // in case of mbstring.func_overload & 2, must check for null b,
+ // otherwise mb_strpos will give WARNING
+ } elseif ($this->b === null
+ || (false === strpos('{[(+-', $this->b)
+ && !$this->isAlphaNum($this->b))
+ ) {
+ $command = self::ACTION_DELETE_A;
+ }
+ } elseif (!$this->isAlphaNum($this->a)) {
+ if ($this->b === ' '
+ || ($this->b === "\n"
+ && (false === strpos('}])+-"\'', $this->a)))
+ ) {
+ $command = self::ACTION_DELETE_A_B;
+ }
+ }
+ $this->action($command);
+ }
+ $this->output = trim($this->output);
+
+ if ($mbIntEnc !== null) {
+ mb_internal_encoding($mbIntEnc);
+ }
+ return $this->output;
+ }
+
+ /**
+ * ACTION_KEEP_A = Output A. Copy B to A. Get the next B.
+ * ACTION_DELETE_A = Copy B to A. Get the next B.
+ * ACTION_DELETE_A_B = Get the next B.
+ *
+ * @param int $command
+ * @throws JSMinUnterminatedRegExpException|JSMinUnterminatedStringException
+ */
+ protected function action($command) {
+ if ($command === self::ACTION_DELETE_A_B
+ && $this->b === ' '
+ && ($this->a === '+' || $this->a === '-')
+ ) {
+ // Note: we're at an addition/substraction operator; the inputIndex
+ // will certainly be a valid index
+ if ($this->input[$this->inputIndex] === $this->a) {
+ // This is "+ +" or "- -". Don't delete the space.
+ $command = self::ACTION_KEEP_A;
+ }
+ }
+ switch ($command) {
+ case self::ACTION_KEEP_A:
+ $this->output .= $this->a;
+ $this->lastByteOut = $this->a;
+
+ // fallthrough
+ case self::ACTION_DELETE_A:
+ $this->a = $this->b;
+ if ($this->a === "'" || $this->a === '"') { // string literal
+ $str = $this->a; // in case needed for exception
+ while (true) {
+ $this->output .= $this->a;
+ $this->lastByteOut = $this->a;
+
+ $this->a = $this->get();
+ if ($this->a === $this->b) { // end quote
+ break;
+ }
+ if (ord($this->a) <= self::ORD_LF) {
+ throw new JSMinUnterminatedStringException(
+ "JSMin: Unterminated String at byte "
+ . $this->inputIndex . ": {$str}");
+ }
+ $str .= $this->a;
+ if ($this->a === '\\') {
+ $this->output .= $this->a;
+ $this->lastByteOut = $this->a;
+
+ $this->a = $this->get();
+ $str .= $this->a;
+ }
+ }
+ }
+ // fallthrough
+ case self::ACTION_DELETE_A_B:
+ $this->b = $this->next();
+ if ($this->b === '/' && $this->isRegexpLiteral()) { // RegExp literal
+ $this->output .= $this->a . $this->b;
+ $pattern = '/'; // in case needed for exception
+ while (true) {
+ $this->a = $this->get();
+ $pattern .= $this->a;
+ if ($this->a === '/') { // end pattern
+ break; // while (true)
+ } elseif ($this->a === '\\') {
+ $this->output .= $this->a;
+ $this->a = $this->get();
+ $pattern .= $this->a;
+ } elseif (ord($this->a) <= self::ORD_LF) {
+ throw new JSMinUnterminatedRegExpException(
+ "JSMin: Unterminated RegExp at byte "
+ . $this->inputIndex . ": {$pattern}");
+ }
+ $this->output .= $this->a;
+ $this->lastByteOut = $this->a;
+ }
+ $this->b = $this->next();
+ }
+ // end case ACTION_DELETE_A_B
+ }
+ }
+
+ /**
+ * @return bool
+ */
+ protected function isRegexpLiteral() {
+ if (false !== strpos("\n{;(,=:[!&|?", $this->a)) { // we aren't dividing
+ return true;
+ }
+ if (' ' === $this->a) {
+ $length = strlen($this->output);
+ if ($length < 2) { // weird edge case
+ return true;
+ }
+ // you can't divide a keyword
+ if (preg_match('/(?:case|else|in|return|typeof)$/', $this->output, $m)) {
+ if ($this->output === $m[0]) { // odd but could happen
+ return true;
+ }
+ // make sure it's a keyword, not end of an identifier
+ $charBeforeKeyword = substr($this->output, $length - strlen($m[0]) - 1, 1);
+ if (!$this->isAlphaNum($charBeforeKeyword)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get next char. Convert ctrl char to space.
+ *
+ * @return string
+ */
+ protected function get() {
+ $c = $this->lookAhead;
+ $this->lookAhead = null;
+ if ($c === null) {
+ if ($this->inputIndex < $this->inputLength) {
+ $c = $this->input[$this->inputIndex];
+ $this->inputIndex += 1;
+ } else {
+ return null;
+ }
+ }
+ if ($c === "\r" || $c === "\n") {
+ return "\n";
+ }
+ if (ord($c) < self::ORD_SPACE) { // control char
+ return ' ';
+ }
+ return $c;
+ }
+
+ /**
+ * Get next char. If is ctrl character, translate to a space or newline.
+ *
+ * @return string
+ */
+ protected function peek() {
+ $this->lookAhead = $this->get();
+ return $this->lookAhead;
+ }
+
+ /**
+ * Is $c a letter, digit, underscore, dollar sign, escape, or non-ASCII?
+ *
+ * @param string $c
+ *
+ * @return bool
+ */
+ protected function isAlphaNum($c) {
+ return (preg_match('/^[0-9a-zA-Z_\\$\\\\]$/', $c) || ord($c) > 126);
+ }
+
+ /**
+ * @return string
+ */
+ protected function singleLineComment() {
+ $comment = '';
+ while (true) {
+ $get = $this->get();
+ $comment .= $get;
+ if (ord($get) <= self::ORD_LF) { // EOL reached
+ // if IE conditional comment
+ if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
+ return "/{$comment}";
+ }
+ return $get;
+ }
+ }
+ }
+
+ /**
+ * @return string
+ * @throws JSMinUnterminatedCommentException
+ */
+ protected function multipleLineComment() {
+ $this->get();
+ $comment = '';
+ while (true) {
+ $get = $this->get();
+ if ($get === '*') {
+ if ($this->peek() === '/') { // end of comment reached
+ $this->get();
+ // if comment preserved by YUI Compressor
+ if (0 === strpos($comment, '!')) {
+ return "\n/*!" . substr($comment, 1) . "*/\n";
+ }
+ // if IE conditional comment
+ if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
+ return "/*{$comment}*/";
+ }
+ return ' ';
+ }
+ } elseif ($get === null) {
+ throw new JSMinUnterminatedCommentException(
+ "JSMin: Unterminated comment at byte "
+ . $this->inputIndex . ": /*{$comment}");
+ }
+ $comment .= $get;
+ }
+ }
+
+ /**
+ * Get the next character, skipping over comments.
+ * Some comments may be preserved.
+ *
+ * @return string
+ */
+ protected function next() {
+ $get = $this->get();
+ if ($get !== '/') {
+ return $get;
+ }
+ switch ($this->peek()) {
+ case '/':
+ return $this->singleLineComment();
+ case '*':
+ return $this->multipleLineComment();
+ default:
+ return $get;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/lmammino/jsmin4assetic/src/JSMinUnterminatedCommentException.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/lmammino/jsmin4assetic/src/JSMinUnterminatedCommentException.php
new file mode 100644
index 0000000..7ffc3b2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/lmammino/jsmin4assetic/src/JSMinUnterminatedCommentException.php
@@ -0,0 +1,5 @@
+minify();
+} catch (Exception $e) {
+ fwrite(STDERR, $e->getMessage(), PHP_EOL);
+ exit(1);
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/bin/minifyjs b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/bin/minifyjs
new file mode 100644
index 0000000..4cbe63f
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/bin/minifyjs
@@ -0,0 +1,45 @@
+#!/usr/bin/env php
+minify();
+} catch (Exception $e) {
+ fwrite(STDERR, $e->getMessage(), PHP_EOL);
+ exit(1);
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/composer.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/composer.json
new file mode 100644
index 0000000..6d81b4f
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/composer.json
@@ -0,0 +1,38 @@
+{
+ "name": "matthiasmullie/minify",
+ "type": "library",
+ "description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.",
+ "keywords": ["minify", "minifier", "css", "js", "javascript"],
+ "homepage": "http://www.minifier.org",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Matthias Mullie",
+ "homepage": "http://www.mullie.eu",
+ "email": "minify@mullie.eu",
+ "role": "Developer"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.0",
+ "ext-pcre": "*",
+ "matthiasmullie/path-converter": "~1.1"
+ },
+ "require-dev": {
+ "matthiasmullie/scrapbook": "~1.0",
+ "phpunit/phpunit": "~4.8",
+ "friendsofphp/php-cs-fixer": "~2.0"
+ },
+ "suggest": {
+ "psr/cache-implementation": "Cache implementation to use with Minify::cache"
+ },
+ "autoload": {
+ "psr-4": {
+ "MatthiasMullie\\Minify\\": "src/"
+ }
+ },
+ "bin": [
+ "bin/minifycss",
+ "bin/minifyjs"
+ ]
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/data/js/keywords_after.txt b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/data/js/keywords_after.txt
new file mode 100644
index 0000000..5c8cba7
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/data/js/keywords_after.txt
@@ -0,0 +1,7 @@
+in
+public
+extends
+private
+protected
+implements
+instanceof
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/data/js/keywords_before.txt b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/data/js/keywords_before.txt
new file mode 100644
index 0000000..5abf357
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/data/js/keywords_before.txt
@@ -0,0 +1,26 @@
+do
+in
+let
+new
+var
+case
+else
+enum
+void
+with
+class
+const
+yield
+delete
+export
+import
+public
+static
+typeof
+extends
+package
+private
+function
+protected
+implements
+instanceof
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/data/js/keywords_reserved.txt b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/data/js/keywords_reserved.txt
new file mode 100644
index 0000000..2a3ad3c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/data/js/keywords_reserved.txt
@@ -0,0 +1,63 @@
+do
+if
+in
+for
+let
+new
+try
+var
+case
+else
+enum
+eval
+null
+this
+true
+void
+with
+break
+catch
+class
+const
+false
+super
+throw
+while
+yield
+delete
+export
+import
+public
+return
+static
+switch
+typeof
+default
+extends
+finally
+package
+private
+continue
+debugger
+function
+arguments
+interface
+protected
+implements
+instanceof
+abstract
+boolean
+byte
+char
+double
+final
+float
+goto
+int
+long
+native
+short
+synchronized
+throws
+transient
+volatile
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/data/js/operators.txt b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/data/js/operators.txt
new file mode 100644
index 0000000..e66229a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/data/js/operators.txt
@@ -0,0 +1,46 @@
++
+-
+*
+/
+%
+=
++=
+-=
+*=
+/=
+%=
+<<=
+>>=
+>>>=
+&=
+^=
+|=
+&
+|
+^
+~
+<<
+>>
+>>>
+==
+===
+!=
+!==
+>
+<
+>=
+<=
+&&
+||
+!
+.
+[
+]
+?
+:
+,
+;
+(
+)
+{
+}
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/data/js/operators_after.txt b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/data/js/operators_after.txt
new file mode 100644
index 0000000..71a9b70
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/data/js/operators_after.txt
@@ -0,0 +1,43 @@
++
+-
+*
+/
+%
+=
++=
+-=
+*=
+/=
+%=
+<<=
+>>=
+>>>=
+&=
+^=
+|=
+&
+|
+^
+<<
+>>
+>>>
+==
+===
+!=
+!==
+>
+<
+>=
+<=
+&&
+||
+.
+[
+]
+?
+:
+,
+;
+(
+)
+}
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/data/js/operators_before.txt b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/data/js/operators_before.txt
new file mode 100644
index 0000000..ff50d87
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/data/js/operators_before.txt
@@ -0,0 +1,43 @@
++
+-
+*
+/
+%
+=
++=
+-=
+*=
+/=
+%=
+<<=
+>>=
+>>>=
+&=
+^=
+|=
+&
+|
+^
+~
+<<
+>>
+>>>
+==
+===
+!=
+!==
+>
+<
+>=
+<=
+&&
+||
+!
+.
+[
+?
+:
+,
+;
+(
+{
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/docker-compose.yml b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/docker-compose.yml
new file mode 100644
index 0000000..5413e24
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/docker-compose.yml
@@ -0,0 +1,31 @@
+version: '2.1'
+services:
+ php:
+ build:
+ context: .
+ dockerfile: Dockerfile
+ volumes:
+ - ./src:/var/www/src
+ - ./data:/var/www/data
+ - ./tests:/var/www/tests
+ - ./phpunit.xml.dist:/var/www/phpunit.xml.dist
+ '7.2':
+ extends: php
+ build:
+ args:
+ version: 7.2-cli
+ '7.1':
+ extends: php
+ build:
+ args:
+ version: 7.1-cli
+ '7.0':
+ extends: php
+ build:
+ args:
+ version: 7.0-cli
+ '5.6':
+ extends: php
+ build:
+ args:
+ version: 5.6-cli
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/src/CSS.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/src/CSS.php
new file mode 100644
index 0000000..742f043
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/src/CSS.php
@@ -0,0 +1,736 @@
+
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+
+namespace MatthiasMullie\Minify;
+
+use MatthiasMullie\Minify\Exceptions\FileImportException;
+use MatthiasMullie\PathConverter\ConverterInterface;
+use MatthiasMullie\PathConverter\Converter;
+
+/**
+ * CSS minifier
+ *
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
+ *
+ * @package Minify
+ * @author Matthias Mullie
+ * @author Tijs Verkoyen
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+class CSS extends Minify
+{
+ /**
+ * @var int maximum inport size in kB
+ */
+ protected $maxImportSize = 5;
+
+ /**
+ * @var string[] valid import extensions
+ */
+ protected $importExtensions = array(
+ 'gif' => 'data:image/gif',
+ 'png' => 'data:image/png',
+ 'jpe' => 'data:image/jpeg',
+ 'jpg' => 'data:image/jpeg',
+ 'jpeg' => 'data:image/jpeg',
+ 'svg' => 'data:image/svg+xml',
+ 'woff' => 'data:application/x-font-woff',
+ 'tif' => 'image/tiff',
+ 'tiff' => 'image/tiff',
+ 'xbm' => 'image/x-xbitmap',
+ );
+
+ /**
+ * Set the maximum size if files to be imported.
+ *
+ * Files larger than this size (in kB) will not be imported into the CSS.
+ * Importing files into the CSS as data-uri will save you some connections,
+ * but we should only import relatively small decorative images so that our
+ * CSS file doesn't get too bulky.
+ *
+ * @param int $size Size in kB
+ */
+ public function setMaxImportSize($size)
+ {
+ $this->maxImportSize = $size;
+ }
+
+ /**
+ * Set the type of extensions to be imported into the CSS (to save network
+ * connections).
+ * Keys of the array should be the file extensions & respective values
+ * should be the data type.
+ *
+ * @param string[] $extensions Array of file extensions
+ */
+ public function setImportExtensions(array $extensions)
+ {
+ $this->importExtensions = $extensions;
+ }
+
+ /**
+ * Move any import statements to the top.
+ *
+ * @param string $content Nearly finished CSS content
+ *
+ * @return string
+ */
+ protected function moveImportsToTop($content)
+ {
+ if (preg_match_all('/(;?)(@import (?url\()?(?P["\']?).+?(?P=quotes)(?(url)\)))/', $content, $matches)) {
+ // remove from content
+ foreach ($matches[0] as $import) {
+ $content = str_replace($import, '', $content);
+ }
+
+ // add to top
+ $content = implode(';', $matches[2]).';'.trim($content, ';');
+ }
+
+ return $content;
+ }
+
+ /**
+ * Combine CSS from import statements.
+ *
+ * @import's will be loaded and their content merged into the original file,
+ * to save HTTP requests.
+ *
+ * @param string $source The file to combine imports for
+ * @param string $content The CSS content to combine imports for
+ * @param string[] $parents Parent paths, for circular reference checks
+ *
+ * @return string
+ *
+ * @throws FileImportException
+ */
+ protected function combineImports($source, $content, $parents)
+ {
+ $importRegexes = array(
+ // @import url(xxx)
+ '/
+ # import statement
+ @import
+
+ # whitespace
+ \s+
+
+ # open url()
+ url\(
+
+ # (optional) open path enclosure
+ (?P["\']?)
+
+ # fetch path
+ (?P.+?)
+
+ # (optional) close path enclosure
+ (?P=quotes)
+
+ # close url()
+ \)
+
+ # (optional) trailing whitespace
+ \s*
+
+ # (optional) media statement(s)
+ (?P[^;]*)
+
+ # (optional) trailing whitespace
+ \s*
+
+ # (optional) closing semi-colon
+ ;?
+
+ /ix',
+
+ // @import 'xxx'
+ '/
+
+ # import statement
+ @import
+
+ # whitespace
+ \s+
+
+ # open path enclosure
+ (?P["\'])
+
+ # fetch path
+ (?P.+?)
+
+ # close path enclosure
+ (?P=quotes)
+
+ # (optional) trailing whitespace
+ \s*
+
+ # (optional) media statement(s)
+ (?P[^;]*)
+
+ # (optional) trailing whitespace
+ \s*
+
+ # (optional) closing semi-colon
+ ;?
+
+ /ix',
+ );
+
+ // find all relative imports in css
+ $matches = array();
+ foreach ($importRegexes as $importRegex) {
+ if (preg_match_all($importRegex, $content, $regexMatches, PREG_SET_ORDER)) {
+ $matches = array_merge($matches, $regexMatches);
+ }
+ }
+
+ $search = array();
+ $replace = array();
+
+ // loop the matches
+ foreach ($matches as $match) {
+ // get the path for the file that will be imported
+ $importPath = dirname($source).'/'.$match['path'];
+
+ // only replace the import with the content if we can grab the
+ // content of the file
+ if (!$this->canImportByPath($match['path']) || !$this->canImportFile($importPath)) {
+ continue;
+ }
+
+ // check if current file was not imported previously in the same
+ // import chain.
+ if (in_array($importPath, $parents)) {
+ throw new FileImportException('Failed to import file "'.$importPath.'": circular reference detected.');
+ }
+
+ // grab referenced file & minify it (which may include importing
+ // yet other @import statements recursively)
+ $minifier = new static($importPath);
+ $importContent = $minifier->execute($source, $parents);
+
+ // check if this is only valid for certain media
+ if (!empty($match['media'])) {
+ $importContent = '@media '.$match['media'].'{'.$importContent.'}';
+ }
+
+ // add to replacement array
+ $search[] = $match[0];
+ $replace[] = $importContent;
+ }
+
+ // replace the import statements
+ return str_replace($search, $replace, $content);
+ }
+
+ /**
+ * Import files into the CSS, base64-ized.
+ *
+ * @url(image.jpg) images will be loaded and their content merged into the
+ * original file, to save HTTP requests.
+ *
+ * @param string $source The file to import files for
+ * @param string $content The CSS content to import files for
+ *
+ * @return string
+ */
+ protected function importFiles($source, $content)
+ {
+ $regex = '/url\((["\']?)(.+?)\\1\)/i';
+ if ($this->importExtensions && preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {
+ $search = array();
+ $replace = array();
+
+ // loop the matches
+ foreach ($matches as $match) {
+ $extension = substr(strrchr($match[2], '.'), 1);
+ if ($extension && !array_key_exists($extension, $this->importExtensions)) {
+ continue;
+ }
+
+ // get the path for the file that will be imported
+ $path = $match[2];
+ $path = dirname($source).'/'.$path;
+
+ // only replace the import with the content if we're able to get
+ // the content of the file, and it's relatively small
+ if ($this->canImportFile($path) && $this->canImportBySize($path)) {
+ // grab content && base64-ize
+ $importContent = $this->load($path);
+ $importContent = base64_encode($importContent);
+
+ // build replacement
+ $search[] = $match[0];
+ $replace[] = 'url('.$this->importExtensions[$extension].';base64,'.$importContent.')';
+ }
+ }
+
+ // replace the import statements
+ $content = str_replace($search, $replace, $content);
+ }
+
+ return $content;
+ }
+
+ /**
+ * Minify the data.
+ * Perform CSS optimizations.
+ *
+ * @param string[optional] $path Path to write the data to
+ * @param string[] $parents Parent paths, for circular reference checks
+ *
+ * @return string The minified data
+ */
+ public function execute($path = null, $parents = array())
+ {
+ $content = '';
+
+ // loop CSS data (raw data and files)
+ foreach ($this->data as $source => $css) {
+ /*
+ * Let's first take out strings & comments, since we can't just
+ * remove whitespace anywhere. If whitespace occurs inside a string,
+ * we should leave it alone. E.g.:
+ * p { content: "a test" }
+ */
+ $this->extractStrings();
+ $this->stripComments();
+ $css = $this->replace($css);
+
+ $css = $this->stripWhitespace($css);
+ $css = $this->shortenHex($css);
+ $css = $this->shortenZeroes($css);
+ $css = $this->shortenFontWeights($css);
+ $css = $this->stripEmptyTags($css);
+
+ // restore the string we've extracted earlier
+ $css = $this->restoreExtractedData($css);
+
+ $source = is_int($source) ? '' : $source;
+ $parents = $source ? array_merge($parents, array($source)) : $parents;
+ $css = $this->combineImports($source, $css, $parents);
+ $css = $this->importFiles($source, $css);
+
+ /*
+ * If we'll save to a new path, we'll have to fix the relative paths
+ * to be relative no longer to the source file, but to the new path.
+ * If we don't write to a file, fall back to same path so no
+ * conversion happens (because we still want it to go through most
+ * of the move code, which also addresses url() & @import syntax...)
+ */
+ $converter = $this->getPathConverter($source, $path ?: $source);
+ $css = $this->move($converter, $css);
+
+ // combine css
+ $content .= $css;
+ }
+
+ $content = $this->moveImportsToTop($content);
+
+ return $content;
+ }
+
+ /**
+ * Moving a css file should update all relative urls.
+ * Relative references (e.g. ../images/image.gif) in a certain css file,
+ * will have to be updated when a file is being saved at another location
+ * (e.g. ../../images/image.gif, if the new CSS file is 1 folder deeper).
+ *
+ * @param ConverterInterface $converter Relative path converter
+ * @param string $content The CSS content to update relative urls for
+ *
+ * @return string
+ */
+ protected function move(ConverterInterface $converter, $content)
+ {
+ /*
+ * Relative path references will usually be enclosed by url(). @import
+ * is an exception, where url() is not necessary around the path (but is
+ * allowed).
+ * This *could* be 1 regular expression, where both regular expressions
+ * in this array are on different sides of a |. But we're using named
+ * patterns in both regexes, the same name on both regexes. This is only
+ * possible with a (?J) modifier, but that only works after a fairly
+ * recent PCRE version. That's why I'm doing 2 separate regular
+ * expressions & combining the matches after executing of both.
+ */
+ $relativeRegexes = array(
+ // url(xxx)
+ '/
+ # open url()
+ url\(
+
+ \s*
+
+ # open path enclosure
+ (?P["\'])?
+
+ # fetch path
+ (?P.+?)
+
+ # close path enclosure
+ (?(quotes)(?P=quotes))
+
+ \s*
+
+ # close url()
+ \)
+
+ /ix',
+
+ // @import "xxx"
+ '/
+ # import statement
+ @import
+
+ # whitespace
+ \s+
+
+ # we don\'t have to check for @import url(), because the
+ # condition above will already catch these
+
+ # open path enclosure
+ (?P["\'])
+
+ # fetch path
+ (?P.+?)
+
+ # close path enclosure
+ (?P=quotes)
+
+ /ix',
+ );
+
+ // find all relative urls in css
+ $matches = array();
+ foreach ($relativeRegexes as $relativeRegex) {
+ if (preg_match_all($relativeRegex, $content, $regexMatches, PREG_SET_ORDER)) {
+ $matches = array_merge($matches, $regexMatches);
+ }
+ }
+
+ $search = array();
+ $replace = array();
+
+ // loop all urls
+ foreach ($matches as $match) {
+ // determine if it's a url() or an @import match
+ $type = (strpos($match[0], '@import') === 0 ? 'import' : 'url');
+
+ $url = $match['path'];
+ if ($this->canImportByPath($url)) {
+ // attempting to interpret GET-params makes no sense, so let's discard them for awhile
+ $params = strrchr($url, '?');
+ $url = $params ? substr($url, 0, -strlen($params)) : $url;
+
+ // fix relative url
+ $url = $converter->convert($url);
+
+ // now that the path has been converted, re-apply GET-params
+ $url .= $params;
+ }
+
+ /*
+ * Urls with control characters above 0x7e should be quoted.
+ * According to Mozilla's parser, whitespace is only allowed at the
+ * end of unquoted urls.
+ * Urls with `)` (as could happen with data: uris) should also be
+ * quoted to avoid being confused for the url() closing parentheses.
+ * And urls with a # have also been reported to cause issues.
+ * Urls with quotes inside should also remain escaped.
+ *
+ * @see https://developer.mozilla.org/nl/docs/Web/CSS/url#The_url()_functional_notation
+ * @see https://hg.mozilla.org/mozilla-central/rev/14abca4e7378
+ * @see https://github.com/matthiasmullie/minify/issues/193
+ */
+ $url = trim($url);
+ if (preg_match('/[\s\)\'"#\x{7f}-\x{9f}]/u', $url)) {
+ $url = $match['quotes'] . $url . $match['quotes'];
+ }
+
+ // build replacement
+ $search[] = $match[0];
+ if ($type === 'url') {
+ $replace[] = 'url('.$url.')';
+ } elseif ($type === 'import') {
+ $replace[] = '@import "'.$url.'"';
+ }
+ }
+
+ // replace urls
+ return str_replace($search, $replace, $content);
+ }
+
+ /**
+ * Shorthand hex color codes.
+ * #FF0000 -> #F00.
+ *
+ * @param string $content The CSS content to shorten the hex color codes for
+ *
+ * @return string
+ */
+ protected function shortenHex($content)
+ {
+ $content = preg_replace('/(?<=[: ])#([0-9a-z])\\1([0-9a-z])\\2([0-9a-z])\\3(?=[; }])/i', '#$1$2$3', $content);
+
+ // we can shorten some even more by replacing them with their color name
+ $colors = array(
+ '#F0FFFF' => 'azure',
+ '#F5F5DC' => 'beige',
+ '#A52A2A' => 'brown',
+ '#FF7F50' => 'coral',
+ '#FFD700' => 'gold',
+ '#808080' => 'gray',
+ '#008000' => 'green',
+ '#4B0082' => 'indigo',
+ '#FFFFF0' => 'ivory',
+ '#F0E68C' => 'khaki',
+ '#FAF0E6' => 'linen',
+ '#800000' => 'maroon',
+ '#000080' => 'navy',
+ '#808000' => 'olive',
+ '#CD853F' => 'peru',
+ '#FFC0CB' => 'pink',
+ '#DDA0DD' => 'plum',
+ '#800080' => 'purple',
+ '#F00' => 'red',
+ '#FA8072' => 'salmon',
+ '#A0522D' => 'sienna',
+ '#C0C0C0' => 'silver',
+ '#FFFAFA' => 'snow',
+ '#D2B48C' => 'tan',
+ '#FF6347' => 'tomato',
+ '#EE82EE' => 'violet',
+ '#F5DEB3' => 'wheat',
+ );
+
+ return preg_replace_callback(
+ '/(?<=[: ])('.implode(array_keys($colors), '|').')(?=[; }])/i',
+ function ($match) use ($colors) {
+ return $colors[strtoupper($match[0])];
+ },
+ $content
+ );
+ }
+
+ /**
+ * Shorten CSS font weights.
+ *
+ * @param string $content The CSS content to shorten the font weights for
+ *
+ * @return string
+ */
+ protected function shortenFontWeights($content)
+ {
+ $weights = array(
+ 'normal' => 400,
+ 'bold' => 700,
+ );
+
+ $callback = function ($match) use ($weights) {
+ return $match[1].$weights[$match[2]];
+ };
+
+ return preg_replace_callback('/(font-weight\s*:\s*)('.implode('|', array_keys($weights)).')(?=[;}])/', $callback, $content);
+ }
+
+ /**
+ * Shorthand 0 values to plain 0, instead of e.g. -0em.
+ *
+ * @param string $content The CSS content to shorten the zero values for
+ *
+ * @return string
+ */
+ protected function shortenZeroes($content)
+ {
+ // we don't want to strip units in `calc()` expressions:
+ // `5px - 0px` is valid, but `5px - 0` is not
+ // `10px * 0` is valid (equates to 0), and so is `10 * 0px`, but
+ // `10 * 0` is invalid
+ // best to just leave `calc()`s alone, even if they could be optimized
+ // (which is a whole other undertaking, where units & order of
+ // operations all need to be considered...)
+ $calcs = $this->findCalcs($content);
+ $content = str_replace($calcs, array_keys($calcs), $content);
+
+ // reusable bits of code throughout these regexes:
+ // before & after are used to make sure we don't match lose unintended
+ // 0-like values (e.g. in #000, or in http://url/1.0)
+ // units can be stripped from 0 values, or used to recognize non 0
+ // values (where wa may be able to strip a .0 suffix)
+ $before = '(?<=[:(, ])';
+ $after = '(?=[ ,);}])';
+ $units = '(em|ex|%|px|cm|mm|in|pt|pc|ch|rem|vh|vw|vmin|vmax|vm)';
+
+ // strip units after zeroes (0px -> 0)
+ // NOTE: it should be safe to remove all units for a 0 value, but in
+ // practice, Webkit (especially Safari) seems to stumble over at least
+ // 0%, potentially other units as well. Only stripping 'px' for now.
+ // @see https://github.com/matthiasmullie/minify/issues/60
+ $content = preg_replace('/'.$before.'(-?0*(\.0+)?)(?<=0)px'.$after.'/', '\\1', $content);
+
+ // strip 0-digits (.0 -> 0)
+ $content = preg_replace('/'.$before.'\.0+'.$units.'?'.$after.'/', '0\\1', $content);
+ // strip trailing 0: 50.10 -> 50.1, 50.10px -> 50.1px
+ $content = preg_replace('/'.$before.'(-?[0-9]+\.[0-9]+)0+'.$units.'?'.$after.'/', '\\1\\2', $content);
+ // strip trailing 0: 50.00 -> 50, 50.00px -> 50px
+ $content = preg_replace('/'.$before.'(-?[0-9]+)\.0+'.$units.'?'.$after.'/', '\\1\\2', $content);
+ // strip leading 0: 0.1 -> .1, 01.1 -> 1.1
+ $content = preg_replace('/'.$before.'(-?)0+([0-9]*\.[0-9]+)'.$units.'?'.$after.'/', '\\1\\2\\3', $content);
+
+ // strip negative zeroes (-0 -> 0) & truncate zeroes (00 -> 0)
+ $content = preg_replace('/'.$before.'-?0+'.$units.'?'.$after.'/', '0\\1', $content);
+
+ // IE doesn't seem to understand a unitless flex-basis value (correct -
+ // it goes against the spec), so let's add it in again (make it `%`,
+ // which is only 1 char: 0%, 0px, 0 anything, it's all just the same)
+ // @see https://developer.mozilla.org/nl/docs/Web/CSS/flex
+ $content = preg_replace('/flex:([0-9]+\s[0-9]+\s)0([;\}])/', 'flex:${1}0%${2}', $content);
+ $content = preg_replace('/flex-basis:0([;\}])/', 'flex-basis:0%${1}', $content);
+
+ // restore `calc()` expressions
+ $content = str_replace(array_keys($calcs), $calcs, $content);
+
+ return $content;
+ }
+
+ /**
+ * Strip empty tags from source code.
+ *
+ * @param string $content
+ *
+ * @return string
+ */
+ protected function stripEmptyTags($content)
+ {
+ $content = preg_replace('/(?<=^)[^\{\};]+\{\s*\}/', '', $content);
+ $content = preg_replace('/(?<=(\}|;))[^\{\};]+\{\s*\}/', '', $content);
+
+ return $content;
+ }
+
+ /**
+ * Strip comments from source code.
+ */
+ protected function stripComments()
+ {
+ $this->registerPattern('/\/\*.*?\*\//s', '');
+ }
+
+ /**
+ * Strip whitespace.
+ *
+ * @param string $content The CSS content to strip the whitespace for
+ *
+ * @return string
+ */
+ protected function stripWhitespace($content)
+ {
+ // remove leading & trailing whitespace
+ $content = preg_replace('/^\s*/m', '', $content);
+ $content = preg_replace('/\s*$/m', '', $content);
+
+ // replace newlines with a single space
+ $content = preg_replace('/\s+/', ' ', $content);
+
+ // remove whitespace around meta characters
+ // inspired by stackoverflow.com/questions/15195750/minify-compress-css-with-regex
+ $content = preg_replace('/\s*([\*$~^|]?+=|[{};,>~]|!important\b)\s*/', '$1', $content);
+ $content = preg_replace('/([\[(:])\s+/', '$1', $content);
+ $content = preg_replace('/\s+([\]\)])/', '$1', $content);
+ $content = preg_replace('/\s+(:)(?![^\}]*\{)/', '$1', $content);
+
+ // whitespace around + and - can only be stripped inside some pseudo-
+ // classes, like `:nth-child(3+2n)`
+ // not in things like `calc(3px + 2px)`, shorthands like `3px -2px`, or
+ // selectors like `div.weird- p`
+ $pseudos = array('nth-child', 'nth-last-child', 'nth-last-of-type', 'nth-of-type');
+ $content = preg_replace('/:('.implode('|', $pseudos).')\(\s*([+-]?)\s*(.+?)\s*([+-]?)\s*(.*?)\s*\)/', ':$1($2$3$4$5)', $content);
+
+ // remove semicolon/whitespace followed by closing bracket
+ $content = str_replace(';}', '}', $content);
+
+ return trim($content);
+ }
+
+ /**
+ * Find all `calc()` occurrences.
+ *
+ * @param string $content The CSS content to find `calc()`s in.
+ *
+ * @return string[]
+ */
+ protected function findCalcs($content)
+ {
+ $results = array();
+ preg_match_all('/calc(\(.+?)(?=$|;|calc\()/', $content, $matches, PREG_SET_ORDER);
+
+ foreach ($matches as $match) {
+ $length = strlen($match[1]);
+ $expr = '';
+ $opened = 0;
+
+ for ($i = 0; $i < $length; $i++) {
+ $char = $match[1][$i];
+ $expr .= $char;
+ if ($char === '(') {
+ $opened++;
+ } elseif ($char === ')' && --$opened === 0) {
+ break;
+ }
+ }
+
+ $results['calc('.count($results).')'] = 'calc'.$expr;
+ }
+
+ return $results;
+ }
+
+ /**
+ * Check if file is small enough to be imported.
+ *
+ * @param string $path The path to the file
+ *
+ * @return bool
+ */
+ protected function canImportBySize($path)
+ {
+ return ($size = @filesize($path)) && $size <= $this->maxImportSize * 1024;
+ }
+
+ /**
+ * Check if file a file can be imported, going by the path.
+ *
+ * @param string $path
+ *
+ * @return bool
+ */
+ protected function canImportByPath($path)
+ {
+ return preg_match('/^(data:|https?:|\\/)/', $path) === 0;
+ }
+
+ /**
+ * Return a converter to update relative paths to be relative to the new
+ * destination.
+ *
+ * @param string $source
+ * @param string $target
+ *
+ * @return ConverterInterface
+ */
+ protected function getPathConverter($source, $target)
+ {
+ return new Converter($source, $target);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/src/Exception.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/src/Exception.php
new file mode 100644
index 0000000..d03898f
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/src/Exception.php
@@ -0,0 +1,20 @@
+
+ */
+namespace MatthiasMullie\Minify;
+
+/**
+ * Base Exception Class
+ * @deprecated Use Exceptions\BasicException instead
+ *
+ * @package Minify
+ * @author Matthias Mullie
+ */
+abstract class Exception extends \Exception
+{
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/src/Exceptions/BasicException.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/src/Exceptions/BasicException.php
new file mode 100644
index 0000000..af5e81b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/src/Exceptions/BasicException.php
@@ -0,0 +1,23 @@
+
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+namespace MatthiasMullie\Minify\Exceptions;
+
+use MatthiasMullie\Minify\Exception;
+
+/**
+ * Basic Exception Class
+ *
+ * @package Minify\Exception
+ * @author Matthias Mullie
+ */
+abstract class BasicException extends Exception
+{
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/src/Exceptions/FileImportException.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/src/Exceptions/FileImportException.php
new file mode 100644
index 0000000..912a2c9
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/src/Exceptions/FileImportException.php
@@ -0,0 +1,21 @@
+
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+namespace MatthiasMullie\Minify\Exceptions;
+
+/**
+ * File Import Exception Class
+ *
+ * @package Minify\Exception
+ * @author Matthias Mullie
+ */
+class FileImportException extends BasicException
+{
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/src/Exceptions/IOException.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/src/Exceptions/IOException.php
new file mode 100644
index 0000000..b172eb4
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/src/Exceptions/IOException.php
@@ -0,0 +1,21 @@
+
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+namespace MatthiasMullie\Minify\Exceptions;
+
+/**
+ * IO Exception Class
+ *
+ * @package Minify\Exception
+ * @author Matthias Mullie
+ */
+class IOException extends BasicException
+{
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/src/JS.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/src/JS.php
new file mode 100644
index 0000000..651d8be
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/minify/src/JS.php
@@ -0,0 +1,598 @@
+
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+namespace MatthiasMullie\Minify;
+
+/**
+ * JavaScript Minifier Class
+ *
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
+ *
+ * @package Minify
+ * @author Matthias Mullie
+ * @author Tijs Verkoyen
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+class JS extends Minify
+{
+ /**
+ * Var-matching regex based on http://stackoverflow.com/a/9337047/802993.
+ *
+ * Note that regular expressions using that bit must have the PCRE_UTF8
+ * pattern modifier (/u) set.
+ *
+ * @var string
+ */
+ const REGEX_VARIABLE = '\b[$A-Z\_a-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\x{02c1}\x{02c6}-\x{02d1}\x{02e0}-\x{02e4}\x{02ec}\x{02ee}\x{0370}-\x{0374}\x{0376}\x{0377}\x{037a}-\x{037d}\x{0386}\x{0388}-\x{038a}\x{038c}\x{038e}-\x{03a1}\x{03a3}-\x{03f5}\x{03f7}-\x{0481}\x{048a}-\x{0527}\x{0531}-\x{0556}\x{0559}\x{0561}-\x{0587}\x{05d0}-\x{05ea}\x{05f0}-\x{05f2}\x{0620}-\x{064a}\x{066e}\x{066f}\x{0671}-\x{06d3}\x{06d5}\x{06e5}\x{06e6}\x{06ee}\x{06ef}\x{06fa}-\x{06fc}\x{06ff}\x{0710}\x{0712}-\x{072f}\x{074d}-\x{07a5}\x{07b1}\x{07ca}-\x{07ea}\x{07f4}\x{07f5}\x{07fa}\x{0800}-\x{0815}\x{081a}\x{0824}\x{0828}\x{0840}-\x{0858}\x{08a0}\x{08a2}-\x{08ac}\x{0904}-\x{0939}\x{093d}\x{0950}\x{0958}-\x{0961}\x{0971}-\x{0977}\x{0979}-\x{097f}\x{0985}-\x{098c}\x{098f}\x{0990}\x{0993}-\x{09a8}\x{09aa}-\x{09b0}\x{09b2}\x{09b6}-\x{09b9}\x{09bd}\x{09ce}\x{09dc}\x{09dd}\x{09df}-\x{09e1}\x{09f0}\x{09f1}\x{0a05}-\x{0a0a}\x{0a0f}\x{0a10}\x{0a13}-\x{0a28}\x{0a2a}-\x{0a30}\x{0a32}\x{0a33}\x{0a35}\x{0a36}\x{0a38}\x{0a39}\x{0a59}-\x{0a5c}\x{0a5e}\x{0a72}-\x{0a74}\x{0a85}-\x{0a8d}\x{0a8f}-\x{0a91}\x{0a93}-\x{0aa8}\x{0aaa}-\x{0ab0}\x{0ab2}\x{0ab3}\x{0ab5}-\x{0ab9}\x{0abd}\x{0ad0}\x{0ae0}\x{0ae1}\x{0b05}-\x{0b0c}\x{0b0f}\x{0b10}\x{0b13}-\x{0b28}\x{0b2a}-\x{0b30}\x{0b32}\x{0b33}\x{0b35}-\x{0b39}\x{0b3d}\x{0b5c}\x{0b5d}\x{0b5f}-\x{0b61}\x{0b71}\x{0b83}\x{0b85}-\x{0b8a}\x{0b8e}-\x{0b90}\x{0b92}-\x{0b95}\x{0b99}\x{0b9a}\x{0b9c}\x{0b9e}\x{0b9f}\x{0ba3}\x{0ba4}\x{0ba8}-\x{0baa}\x{0bae}-\x{0bb9}\x{0bd0}\x{0c05}-\x{0c0c}\x{0c0e}-\x{0c10}\x{0c12}-\x{0c28}\x{0c2a}-\x{0c33}\x{0c35}-\x{0c39}\x{0c3d}\x{0c58}\x{0c59}\x{0c60}\x{0c61}\x{0c85}-\x{0c8c}\x{0c8e}-\x{0c90}\x{0c92}-\x{0ca8}\x{0caa}-\x{0cb3}\x{0cb5}-\x{0cb9}\x{0cbd}\x{0cde}\x{0ce0}\x{0ce1}\x{0cf1}\x{0cf2}\x{0d05}-\x{0d0c}\x{0d0e}-\x{0d10}\x{0d12}-\x{0d3a}\x{0d3d}\x{0d4e}\x{0d60}\x{0d61}\x{0d7a}-\x{0d7f}\x{0d85}-\x{0d96}\x{0d9a}-\x{0db1}\x{0db3}-\x{0dbb}\x{0dbd}\x{0dc0}-\x{0dc6}\x{0e01}-\x{0e30}\x{0e32}\x{0e33}\x{0e40}-\x{0e46}\x{0e81}\x{0e82}\x{0e84}\x{0e87}\x{0e88}\x{0e8a}\x{0e8d}\x{0e94}-\x{0e97}\x{0e99}-\x{0e9f}\x{0ea1}-\x{0ea3}\x{0ea5}\x{0ea7}\x{0eaa}\x{0eab}\x{0ead}-\x{0eb0}\x{0eb2}\x{0eb3}\x{0ebd}\x{0ec0}-\x{0ec4}\x{0ec6}\x{0edc}-\x{0edf}\x{0f00}\x{0f40}-\x{0f47}\x{0f49}-\x{0f6c}\x{0f88}-\x{0f8c}\x{1000}-\x{102a}\x{103f}\x{1050}-\x{1055}\x{105a}-\x{105d}\x{1061}\x{1065}\x{1066}\x{106e}-\x{1070}\x{1075}-\x{1081}\x{108e}\x{10a0}-\x{10c5}\x{10c7}\x{10cd}\x{10d0}-\x{10fa}\x{10fc}-\x{1248}\x{124a}-\x{124d}\x{1250}-\x{1256}\x{1258}\x{125a}-\x{125d}\x{1260}-\x{1288}\x{128a}-\x{128d}\x{1290}-\x{12b0}\x{12b2}-\x{12b5}\x{12b8}-\x{12be}\x{12c0}\x{12c2}-\x{12c5}\x{12c8}-\x{12d6}\x{12d8}-\x{1310}\x{1312}-\x{1315}\x{1318}-\x{135a}\x{1380}-\x{138f}\x{13a0}-\x{13f4}\x{1401}-\x{166c}\x{166f}-\x{167f}\x{1681}-\x{169a}\x{16a0}-\x{16ea}\x{16ee}-\x{16f0}\x{1700}-\x{170c}\x{170e}-\x{1711}\x{1720}-\x{1731}\x{1740}-\x{1751}\x{1760}-\x{176c}\x{176e}-\x{1770}\x{1780}-\x{17b3}\x{17d7}\x{17dc}\x{1820}-\x{1877}\x{1880}-\x{18a8}\x{18aa}\x{18b0}-\x{18f5}\x{1900}-\x{191c}\x{1950}-\x{196d}\x{1970}-\x{1974}\x{1980}-\x{19ab}\x{19c1}-\x{19c7}\x{1a00}-\x{1a16}\x{1a20}-\x{1a54}\x{1aa7}\x{1b05}-\x{1b33}\x{1b45}-\x{1b4b}\x{1b83}-\x{1ba0}\x{1bae}\x{1baf}\x{1bba}-\x{1be5}\x{1c00}-\x{1c23}\x{1c4d}-\x{1c4f}\x{1c5a}-\x{1c7d}\x{1ce9}-\x{1cec}\x{1cee}-\x{1cf1}\x{1cf5}\x{1cf6}\x{1d00}-\x{1dbf}\x{1e00}-\x{1f15}\x{1f18}-\x{1f1d}\x{1f20}-\x{1f45}\x{1f48}-\x{1f4d}\x{1f50}-\x{1f57}\x{1f59}\x{1f5b}\x{1f5d}\x{1f5f}-\x{1f7d}\x{1f80}-\x{1fb4}\x{1fb6}-\x{1fbc}\x{1fbe}\x{1fc2}-\x{1fc4}\x{1fc6}-\x{1fcc}\x{1fd0}-\x{1fd3}\x{1fd6}-\x{1fdb}\x{1fe0}-\x{1fec}\x{1ff2}-\x{1ff4}\x{1ff6}-\x{1ffc}\x{2071}\x{207f}\x{2090}-\x{209c}\x{2102}\x{2107}\x{210a}-\x{2113}\x{2115}\x{2119}-\x{211d}\x{2124}\x{2126}\x{2128}\x{212a}-\x{212d}\x{212f}-\x{2139}\x{213c}-\x{213f}\x{2145}-\x{2149}\x{214e}\x{2160}-\x{2188}\x{2c00}-\x{2c2e}\x{2c30}-\x{2c5e}\x{2c60}-\x{2ce4}\x{2ceb}-\x{2cee}\x{2cf2}\x{2cf3}\x{2d00}-\x{2d25}\x{2d27}\x{2d2d}\x{2d30}-\x{2d67}\x{2d6f}\x{2d80}-\x{2d96}\x{2da0}-\x{2da6}\x{2da8}-\x{2dae}\x{2db0}-\x{2db6}\x{2db8}-\x{2dbe}\x{2dc0}-\x{2dc6}\x{2dc8}-\x{2dce}\x{2dd0}-\x{2dd6}\x{2dd8}-\x{2dde}\x{2e2f}\x{3005}-\x{3007}\x{3021}-\x{3029}\x{3031}-\x{3035}\x{3038}-\x{303c}\x{3041}-\x{3096}\x{309d}-\x{309f}\x{30a1}-\x{30fa}\x{30fc}-\x{30ff}\x{3105}-\x{312d}\x{3131}-\x{318e}\x{31a0}-\x{31ba}\x{31f0}-\x{31ff}\x{3400}-\x{4db5}\x{4e00}-\x{9fcc}\x{a000}-\x{a48c}\x{a4d0}-\x{a4fd}\x{a500}-\x{a60c}\x{a610}-\x{a61f}\x{a62a}\x{a62b}\x{a640}-\x{a66e}\x{a67f}-\x{a697}\x{a6a0}-\x{a6ef}\x{a717}-\x{a71f}\x{a722}-\x{a788}\x{a78b}-\x{a78e}\x{a790}-\x{a793}\x{a7a0}-\x{a7aa}\x{a7f8}-\x{a801}\x{a803}-\x{a805}\x{a807}-\x{a80a}\x{a80c}-\x{a822}\x{a840}-\x{a873}\x{a882}-\x{a8b3}\x{a8f2}-\x{a8f7}\x{a8fb}\x{a90a}-\x{a925}\x{a930}-\x{a946}\x{a960}-\x{a97c}\x{a984}-\x{a9b2}\x{a9cf}\x{aa00}-\x{aa28}\x{aa40}-\x{aa42}\x{aa44}-\x{aa4b}\x{aa60}-\x{aa76}\x{aa7a}\x{aa80}-\x{aaaf}\x{aab1}\x{aab5}\x{aab6}\x{aab9}-\x{aabd}\x{aac0}\x{aac2}\x{aadb}-\x{aadd}\x{aae0}-\x{aaea}\x{aaf2}-\x{aaf4}\x{ab01}-\x{ab06}\x{ab09}-\x{ab0e}\x{ab11}-\x{ab16}\x{ab20}-\x{ab26}\x{ab28}-\x{ab2e}\x{abc0}-\x{abe2}\x{ac00}-\x{d7a3}\x{d7b0}-\x{d7c6}\x{d7cb}-\x{d7fb}\x{f900}-\x{fa6d}\x{fa70}-\x{fad9}\x{fb00}-\x{fb06}\x{fb13}-\x{fb17}\x{fb1d}\x{fb1f}-\x{fb28}\x{fb2a}-\x{fb36}\x{fb38}-\x{fb3c}\x{fb3e}\x{fb40}\x{fb41}\x{fb43}\x{fb44}\x{fb46}-\x{fbb1}\x{fbd3}-\x{fd3d}\x{fd50}-\x{fd8f}\x{fd92}-\x{fdc7}\x{fdf0}-\x{fdfb}\x{fe70}-\x{fe74}\x{fe76}-\x{fefc}\x{ff21}-\x{ff3a}\x{ff41}-\x{ff5a}\x{ff66}-\x{ffbe}\x{ffc2}-\x{ffc7}\x{ffca}-\x{ffcf}\x{ffd2}-\x{ffd7}\x{ffda}-\x{ffdc}][$A-Z\_a-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\x{02c1}\x{02c6}-\x{02d1}\x{02e0}-\x{02e4}\x{02ec}\x{02ee}\x{0370}-\x{0374}\x{0376}\x{0377}\x{037a}-\x{037d}\x{0386}\x{0388}-\x{038a}\x{038c}\x{038e}-\x{03a1}\x{03a3}-\x{03f5}\x{03f7}-\x{0481}\x{048a}-\x{0527}\x{0531}-\x{0556}\x{0559}\x{0561}-\x{0587}\x{05d0}-\x{05ea}\x{05f0}-\x{05f2}\x{0620}-\x{064a}\x{066e}\x{066f}\x{0671}-\x{06d3}\x{06d5}\x{06e5}\x{06e6}\x{06ee}\x{06ef}\x{06fa}-\x{06fc}\x{06ff}\x{0710}\x{0712}-\x{072f}\x{074d}-\x{07a5}\x{07b1}\x{07ca}-\x{07ea}\x{07f4}\x{07f5}\x{07fa}\x{0800}-\x{0815}\x{081a}\x{0824}\x{0828}\x{0840}-\x{0858}\x{08a0}\x{08a2}-\x{08ac}\x{0904}-\x{0939}\x{093d}\x{0950}\x{0958}-\x{0961}\x{0971}-\x{0977}\x{0979}-\x{097f}\x{0985}-\x{098c}\x{098f}\x{0990}\x{0993}-\x{09a8}\x{09aa}-\x{09b0}\x{09b2}\x{09b6}-\x{09b9}\x{09bd}\x{09ce}\x{09dc}\x{09dd}\x{09df}-\x{09e1}\x{09f0}\x{09f1}\x{0a05}-\x{0a0a}\x{0a0f}\x{0a10}\x{0a13}-\x{0a28}\x{0a2a}-\x{0a30}\x{0a32}\x{0a33}\x{0a35}\x{0a36}\x{0a38}\x{0a39}\x{0a59}-\x{0a5c}\x{0a5e}\x{0a72}-\x{0a74}\x{0a85}-\x{0a8d}\x{0a8f}-\x{0a91}\x{0a93}-\x{0aa8}\x{0aaa}-\x{0ab0}\x{0ab2}\x{0ab3}\x{0ab5}-\x{0ab9}\x{0abd}\x{0ad0}\x{0ae0}\x{0ae1}\x{0b05}-\x{0b0c}\x{0b0f}\x{0b10}\x{0b13}-\x{0b28}\x{0b2a}-\x{0b30}\x{0b32}\x{0b33}\x{0b35}-\x{0b39}\x{0b3d}\x{0b5c}\x{0b5d}\x{0b5f}-\x{0b61}\x{0b71}\x{0b83}\x{0b85}-\x{0b8a}\x{0b8e}-\x{0b90}\x{0b92}-\x{0b95}\x{0b99}\x{0b9a}\x{0b9c}\x{0b9e}\x{0b9f}\x{0ba3}\x{0ba4}\x{0ba8}-\x{0baa}\x{0bae}-\x{0bb9}\x{0bd0}\x{0c05}-\x{0c0c}\x{0c0e}-\x{0c10}\x{0c12}-\x{0c28}\x{0c2a}-\x{0c33}\x{0c35}-\x{0c39}\x{0c3d}\x{0c58}\x{0c59}\x{0c60}\x{0c61}\x{0c85}-\x{0c8c}\x{0c8e}-\x{0c90}\x{0c92}-\x{0ca8}\x{0caa}-\x{0cb3}\x{0cb5}-\x{0cb9}\x{0cbd}\x{0cde}\x{0ce0}\x{0ce1}\x{0cf1}\x{0cf2}\x{0d05}-\x{0d0c}\x{0d0e}-\x{0d10}\x{0d12}-\x{0d3a}\x{0d3d}\x{0d4e}\x{0d60}\x{0d61}\x{0d7a}-\x{0d7f}\x{0d85}-\x{0d96}\x{0d9a}-\x{0db1}\x{0db3}-\x{0dbb}\x{0dbd}\x{0dc0}-\x{0dc6}\x{0e01}-\x{0e30}\x{0e32}\x{0e33}\x{0e40}-\x{0e46}\x{0e81}\x{0e82}\x{0e84}\x{0e87}\x{0e88}\x{0e8a}\x{0e8d}\x{0e94}-\x{0e97}\x{0e99}-\x{0e9f}\x{0ea1}-\x{0ea3}\x{0ea5}\x{0ea7}\x{0eaa}\x{0eab}\x{0ead}-\x{0eb0}\x{0eb2}\x{0eb3}\x{0ebd}\x{0ec0}-\x{0ec4}\x{0ec6}\x{0edc}-\x{0edf}\x{0f00}\x{0f40}-\x{0f47}\x{0f49}-\x{0f6c}\x{0f88}-\x{0f8c}\x{1000}-\x{102a}\x{103f}\x{1050}-\x{1055}\x{105a}-\x{105d}\x{1061}\x{1065}\x{1066}\x{106e}-\x{1070}\x{1075}-\x{1081}\x{108e}\x{10a0}-\x{10c5}\x{10c7}\x{10cd}\x{10d0}-\x{10fa}\x{10fc}-\x{1248}\x{124a}-\x{124d}\x{1250}-\x{1256}\x{1258}\x{125a}-\x{125d}\x{1260}-\x{1288}\x{128a}-\x{128d}\x{1290}-\x{12b0}\x{12b2}-\x{12b5}\x{12b8}-\x{12be}\x{12c0}\x{12c2}-\x{12c5}\x{12c8}-\x{12d6}\x{12d8}-\x{1310}\x{1312}-\x{1315}\x{1318}-\x{135a}\x{1380}-\x{138f}\x{13a0}-\x{13f4}\x{1401}-\x{166c}\x{166f}-\x{167f}\x{1681}-\x{169a}\x{16a0}-\x{16ea}\x{16ee}-\x{16f0}\x{1700}-\x{170c}\x{170e}-\x{1711}\x{1720}-\x{1731}\x{1740}-\x{1751}\x{1760}-\x{176c}\x{176e}-\x{1770}\x{1780}-\x{17b3}\x{17d7}\x{17dc}\x{1820}-\x{1877}\x{1880}-\x{18a8}\x{18aa}\x{18b0}-\x{18f5}\x{1900}-\x{191c}\x{1950}-\x{196d}\x{1970}-\x{1974}\x{1980}-\x{19ab}\x{19c1}-\x{19c7}\x{1a00}-\x{1a16}\x{1a20}-\x{1a54}\x{1aa7}\x{1b05}-\x{1b33}\x{1b45}-\x{1b4b}\x{1b83}-\x{1ba0}\x{1bae}\x{1baf}\x{1bba}-\x{1be5}\x{1c00}-\x{1c23}\x{1c4d}-\x{1c4f}\x{1c5a}-\x{1c7d}\x{1ce9}-\x{1cec}\x{1cee}-\x{1cf1}\x{1cf5}\x{1cf6}\x{1d00}-\x{1dbf}\x{1e00}-\x{1f15}\x{1f18}-\x{1f1d}\x{1f20}-\x{1f45}\x{1f48}-\x{1f4d}\x{1f50}-\x{1f57}\x{1f59}\x{1f5b}\x{1f5d}\x{1f5f}-\x{1f7d}\x{1f80}-\x{1fb4}\x{1fb6}-\x{1fbc}\x{1fbe}\x{1fc2}-\x{1fc4}\x{1fc6}-\x{1fcc}\x{1fd0}-\x{1fd3}\x{1fd6}-\x{1fdb}\x{1fe0}-\x{1fec}\x{1ff2}-\x{1ff4}\x{1ff6}-\x{1ffc}\x{2071}\x{207f}\x{2090}-\x{209c}\x{2102}\x{2107}\x{210a}-\x{2113}\x{2115}\x{2119}-\x{211d}\x{2124}\x{2126}\x{2128}\x{212a}-\x{212d}\x{212f}-\x{2139}\x{213c}-\x{213f}\x{2145}-\x{2149}\x{214e}\x{2160}-\x{2188}\x{2c00}-\x{2c2e}\x{2c30}-\x{2c5e}\x{2c60}-\x{2ce4}\x{2ceb}-\x{2cee}\x{2cf2}\x{2cf3}\x{2d00}-\x{2d25}\x{2d27}\x{2d2d}\x{2d30}-\x{2d67}\x{2d6f}\x{2d80}-\x{2d96}\x{2da0}-\x{2da6}\x{2da8}-\x{2dae}\x{2db0}-\x{2db6}\x{2db8}-\x{2dbe}\x{2dc0}-\x{2dc6}\x{2dc8}-\x{2dce}\x{2dd0}-\x{2dd6}\x{2dd8}-\x{2dde}\x{2e2f}\x{3005}-\x{3007}\x{3021}-\x{3029}\x{3031}-\x{3035}\x{3038}-\x{303c}\x{3041}-\x{3096}\x{309d}-\x{309f}\x{30a1}-\x{30fa}\x{30fc}-\x{30ff}\x{3105}-\x{312d}\x{3131}-\x{318e}\x{31a0}-\x{31ba}\x{31f0}-\x{31ff}\x{3400}-\x{4db5}\x{4e00}-\x{9fcc}\x{a000}-\x{a48c}\x{a4d0}-\x{a4fd}\x{a500}-\x{a60c}\x{a610}-\x{a61f}\x{a62a}\x{a62b}\x{a640}-\x{a66e}\x{a67f}-\x{a697}\x{a6a0}-\x{a6ef}\x{a717}-\x{a71f}\x{a722}-\x{a788}\x{a78b}-\x{a78e}\x{a790}-\x{a793}\x{a7a0}-\x{a7aa}\x{a7f8}-\x{a801}\x{a803}-\x{a805}\x{a807}-\x{a80a}\x{a80c}-\x{a822}\x{a840}-\x{a873}\x{a882}-\x{a8b3}\x{a8f2}-\x{a8f7}\x{a8fb}\x{a90a}-\x{a925}\x{a930}-\x{a946}\x{a960}-\x{a97c}\x{a984}-\x{a9b2}\x{a9cf}\x{aa00}-\x{aa28}\x{aa40}-\x{aa42}\x{aa44}-\x{aa4b}\x{aa60}-\x{aa76}\x{aa7a}\x{aa80}-\x{aaaf}\x{aab1}\x{aab5}\x{aab6}\x{aab9}-\x{aabd}\x{aac0}\x{aac2}\x{aadb}-\x{aadd}\x{aae0}-\x{aaea}\x{aaf2}-\x{aaf4}\x{ab01}-\x{ab06}\x{ab09}-\x{ab0e}\x{ab11}-\x{ab16}\x{ab20}-\x{ab26}\x{ab28}-\x{ab2e}\x{abc0}-\x{abe2}\x{ac00}-\x{d7a3}\x{d7b0}-\x{d7c6}\x{d7cb}-\x{d7fb}\x{f900}-\x{fa6d}\x{fa70}-\x{fad9}\x{fb00}-\x{fb06}\x{fb13}-\x{fb17}\x{fb1d}\x{fb1f}-\x{fb28}\x{fb2a}-\x{fb36}\x{fb38}-\x{fb3c}\x{fb3e}\x{fb40}\x{fb41}\x{fb43}\x{fb44}\x{fb46}-\x{fbb1}\x{fbd3}-\x{fd3d}\x{fd50}-\x{fd8f}\x{fd92}-\x{fdc7}\x{fdf0}-\x{fdfb}\x{fe70}-\x{fe74}\x{fe76}-\x{fefc}\x{ff21}-\x{ff3a}\x{ff41}-\x{ff5a}\x{ff66}-\x{ffbe}\x{ffc2}-\x{ffc7}\x{ffca}-\x{ffcf}\x{ffd2}-\x{ffd7}\x{ffda}-\x{ffdc}0-9\x{0300}-\x{036f}\x{0483}-\x{0487}\x{0591}-\x{05bd}\x{05bf}\x{05c1}\x{05c2}\x{05c4}\x{05c5}\x{05c7}\x{0610}-\x{061a}\x{064b}-\x{0669}\x{0670}\x{06d6}-\x{06dc}\x{06df}-\x{06e4}\x{06e7}\x{06e8}\x{06ea}-\x{06ed}\x{06f0}-\x{06f9}\x{0711}\x{0730}-\x{074a}\x{07a6}-\x{07b0}\x{07c0}-\x{07c9}\x{07eb}-\x{07f3}\x{0816}-\x{0819}\x{081b}-\x{0823}\x{0825}-\x{0827}\x{0829}-\x{082d}\x{0859}-\x{085b}\x{08e4}-\x{08fe}\x{0900}-\x{0903}\x{093a}-\x{093c}\x{093e}-\x{094f}\x{0951}-\x{0957}\x{0962}\x{0963}\x{0966}-\x{096f}\x{0981}-\x{0983}\x{09bc}\x{09be}-\x{09c4}\x{09c7}\x{09c8}\x{09cb}-\x{09cd}\x{09d7}\x{09e2}\x{09e3}\x{09e6}-\x{09ef}\x{0a01}-\x{0a03}\x{0a3c}\x{0a3e}-\x{0a42}\x{0a47}\x{0a48}\x{0a4b}-\x{0a4d}\x{0a51}\x{0a66}-\x{0a71}\x{0a75}\x{0a81}-\x{0a83}\x{0abc}\x{0abe}-\x{0ac5}\x{0ac7}-\x{0ac9}\x{0acb}-\x{0acd}\x{0ae2}\x{0ae3}\x{0ae6}-\x{0aef}\x{0b01}-\x{0b03}\x{0b3c}\x{0b3e}-\x{0b44}\x{0b47}\x{0b48}\x{0b4b}-\x{0b4d}\x{0b56}\x{0b57}\x{0b62}\x{0b63}\x{0b66}-\x{0b6f}\x{0b82}\x{0bbe}-\x{0bc2}\x{0bc6}-\x{0bc8}\x{0bca}-\x{0bcd}\x{0bd7}\x{0be6}-\x{0bef}\x{0c01}-\x{0c03}\x{0c3e}-\x{0c44}\x{0c46}-\x{0c48}\x{0c4a}-\x{0c4d}\x{0c55}\x{0c56}\x{0c62}\x{0c63}\x{0c66}-\x{0c6f}\x{0c82}\x{0c83}\x{0cbc}\x{0cbe}-\x{0cc4}\x{0cc6}-\x{0cc8}\x{0cca}-\x{0ccd}\x{0cd5}\x{0cd6}\x{0ce2}\x{0ce3}\x{0ce6}-\x{0cef}\x{0d02}\x{0d03}\x{0d3e}-\x{0d44}\x{0d46}-\x{0d48}\x{0d4a}-\x{0d4d}\x{0d57}\x{0d62}\x{0d63}\x{0d66}-\x{0d6f}\x{0d82}\x{0d83}\x{0dca}\x{0dcf}-\x{0dd4}\x{0dd6}\x{0dd8}-\x{0ddf}\x{0df2}\x{0df3}\x{0e31}\x{0e34}-\x{0e3a}\x{0e47}-\x{0e4e}\x{0e50}-\x{0e59}\x{0eb1}\x{0eb4}-\x{0eb9}\x{0ebb}\x{0ebc}\x{0ec8}-\x{0ecd}\x{0ed0}-\x{0ed9}\x{0f18}\x{0f19}\x{0f20}-\x{0f29}\x{0f35}\x{0f37}\x{0f39}\x{0f3e}\x{0f3f}\x{0f71}-\x{0f84}\x{0f86}\x{0f87}\x{0f8d}-\x{0f97}\x{0f99}-\x{0fbc}\x{0fc6}\x{102b}-\x{103e}\x{1040}-\x{1049}\x{1056}-\x{1059}\x{105e}-\x{1060}\x{1062}-\x{1064}\x{1067}-\x{106d}\x{1071}-\x{1074}\x{1082}-\x{108d}\x{108f}-\x{109d}\x{135d}-\x{135f}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}\x{1753}\x{1772}\x{1773}\x{17b4}-\x{17d3}\x{17dd}\x{17e0}-\x{17e9}\x{180b}-\x{180d}\x{1810}-\x{1819}\x{18a9}\x{1920}-\x{192b}\x{1930}-\x{193b}\x{1946}-\x{194f}\x{19b0}-\x{19c0}\x{19c8}\x{19c9}\x{19d0}-\x{19d9}\x{1a17}-\x{1a1b}\x{1a55}-\x{1a5e}\x{1a60}-\x{1a7c}\x{1a7f}-\x{1a89}\x{1a90}-\x{1a99}\x{1b00}-\x{1b04}\x{1b34}-\x{1b44}\x{1b50}-\x{1b59}\x{1b6b}-\x{1b73}\x{1b80}-\x{1b82}\x{1ba1}-\x{1bad}\x{1bb0}-\x{1bb9}\x{1be6}-\x{1bf3}\x{1c24}-\x{1c37}\x{1c40}-\x{1c49}\x{1c50}-\x{1c59}\x{1cd0}-\x{1cd2}\x{1cd4}-\x{1ce8}\x{1ced}\x{1cf2}-\x{1cf4}\x{1dc0}-\x{1de6}\x{1dfc}-\x{1dff}\x{200c}\x{200d}\x{203f}\x{2040}\x{2054}\x{20d0}-\x{20dc}\x{20e1}\x{20e5}-\x{20f0}\x{2cef}-\x{2cf1}\x{2d7f}\x{2de0}-\x{2dff}\x{302a}-\x{302f}\x{3099}\x{309a}\x{a620}-\x{a629}\x{a66f}\x{a674}-\x{a67d}\x{a69f}\x{a6f0}\x{a6f1}\x{a802}\x{a806}\x{a80b}\x{a823}-\x{a827}\x{a880}\x{a881}\x{a8b4}-\x{a8c4}\x{a8d0}-\x{a8d9}\x{a8e0}-\x{a8f1}\x{a900}-\x{a909}\x{a926}-\x{a92d}\x{a947}-\x{a953}\x{a980}-\x{a983}\x{a9b3}-\x{a9c0}\x{a9d0}-\x{a9d9}\x{aa29}-\x{aa36}\x{aa43}\x{aa4c}\x{aa4d}\x{aa50}-\x{aa59}\x{aa7b}\x{aab0}\x{aab2}-\x{aab4}\x{aab7}\x{aab8}\x{aabe}\x{aabf}\x{aac1}\x{aaeb}-\x{aaef}\x{aaf5}\x{aaf6}\x{abe3}-\x{abea}\x{abec}\x{abed}\x{abf0}-\x{abf9}\x{fb1e}\x{fe00}-\x{fe0f}\x{fe20}-\x{fe26}\x{fe33}\x{fe34}\x{fe4d}-\x{fe4f}\x{ff10}-\x{ff19}\x{ff3f}]*\b';
+
+ /**
+ * Full list of JavaScript reserved words.
+ * Will be loaded from /data/js/keywords_reserved.txt.
+ *
+ * @see https://mathiasbynens.be/notes/reserved-keywords
+ *
+ * @var string[]
+ */
+ protected $keywordsReserved = array();
+
+ /**
+ * List of JavaScript reserved words that accept a
+ * after them. Some end of lines are not the end of a statement, like with
+ * these keywords.
+ *
+ * E.g.: we shouldn't insert a ; after this else
+ * else
+ * console.log('this is quite fine')
+ *
+ * Will be loaded from /data/js/keywords_before.txt
+ *
+ * @var string[]
+ */
+ protected $keywordsBefore = array();
+
+ /**
+ * List of JavaScript reserved words that accept a
+ * before them. Some end of lines are not the end of a statement, like when
+ * continued by one of these keywords on the newline.
+ *
+ * E.g.: we shouldn't insert a ; before this instanceof
+ * variable
+ * instanceof String
+ *
+ * Will be loaded from /data/js/keywords_after.txt
+ *
+ * @var string[]
+ */
+ protected $keywordsAfter = array();
+
+ /**
+ * List of all JavaScript operators.
+ *
+ * Will be loaded from /data/js/operators.txt
+ *
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
+ *
+ * @var string[]
+ */
+ protected $operators = array();
+
+ /**
+ * List of JavaScript operators that accept a after
+ * them. Some end of lines are not the end of a statement, like with these
+ * operators.
+ *
+ * Note: Most operators are fine, we've only removed ++ and --.
+ * ++ & -- have to be joined with the value they're in-/decrementing.
+ *
+ * Will be loaded from /data/js/operators_before.txt
+ *
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
+ *
+ * @var string[]
+ */
+ protected $operatorsBefore = array();
+
+ /**
+ * List of JavaScript operators that accept a before
+ * them. Some end of lines are not the end of a statement, like when
+ * continued by one of these operators on the newline.
+ *
+ * Note: Most operators are fine, we've only removed ), ], ++, --, ! and ~.
+ * There can't be a newline separating ! or ~ and whatever it is negating.
+ * ++ & -- have to be joined with the value they're in-/decrementing.
+ * ) & ] are "special" in that they have lots or usecases. () for example
+ * is used for function calls, for grouping, in if () and for (), ...
+ *
+ * Will be loaded from /data/js/operators_after.txt
+ *
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
+ *
+ * @var string[]
+ */
+ protected $operatorsAfter = array();
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __construct()
+ {
+ call_user_func_array(array('parent', '__construct'), func_get_args());
+
+ $dataDir = __DIR__.'/../data/js/';
+ $options = FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES;
+ $this->keywordsReserved = file($dataDir.'keywords_reserved.txt', $options);
+ $this->keywordsBefore = file($dataDir.'keywords_before.txt', $options);
+ $this->keywordsAfter = file($dataDir.'keywords_after.txt', $options);
+ $this->operators = file($dataDir.'operators.txt', $options);
+ $this->operatorsBefore = file($dataDir.'operators_before.txt', $options);
+ $this->operatorsAfter = file($dataDir.'operators_after.txt', $options);
+ }
+
+ /**
+ * Minify the data.
+ * Perform JS optimizations.
+ *
+ * @param string[optional] $path Path to write the data to
+ *
+ * @return string The minified data
+ */
+ public function execute($path = null)
+ {
+ $content = '';
+
+ /*
+ * Let's first take out strings, comments and regular expressions.
+ * All of these can contain JS code-like characters, and we should make
+ * sure any further magic ignores anything inside of these.
+ *
+ * Consider this example, where we should not strip any whitespace:
+ * var str = "a test";
+ *
+ * Comments will be removed altogether, strings and regular expressions
+ * will be replaced by placeholder text, which we'll restore later.
+ */
+ $this->extractStrings('\'"`');
+ $this->stripComments();
+ $this->extractRegex();
+
+ // loop files
+ foreach ($this->data as $source => $js) {
+ // take out strings, comments & regex (for which we've registered
+ // the regexes just a few lines earlier)
+ $js = $this->replace($js);
+
+ $js = $this->propertyNotation($js);
+ $js = $this->shortenBools($js);
+ $js = $this->stripWhitespace($js);
+
+ // combine js: separating the scripts by a ;
+ $content .= $js.";";
+ }
+
+ // clean up leftover `;`s from the combination of multiple scripts
+ $content = ltrim($content, ';');
+ $content = (string) substr($content, 0, -1);
+
+ /*
+ * Earlier, we extracted strings & regular expressions and replaced them
+ * with placeholder text. This will restore them.
+ */
+ $content = $this->restoreExtractedData($content);
+
+ return $content;
+ }
+
+ /**
+ * Strip comments from source code.
+ */
+ protected function stripComments()
+ {
+ // single-line comments
+ $this->registerPattern('/\/\/.*$/m', '');
+
+ // multi-line comments
+ $this->registerPattern('/\/\*.*?\*\//s', '');
+ }
+
+ /**
+ * JS can have /-delimited regular expressions, like: /ab+c/.match(string).
+ *
+ * The content inside the regex can contain characters that may be confused
+ * for JS code: e.g. it could contain whitespace it needs to match & we
+ * don't want to strip whitespace in there.
+ *
+ * The regex can be pretty simple: we don't have to care about comments,
+ * (which also use slashes) because stripComments() will have stripped those
+ * already.
+ *
+ * This method will replace all string content with simple REGEX#
+ * placeholder text, so we've rid all regular expressions from characters
+ * that may be misinterpreted. Original regex content will be saved in
+ * $this->extracted and after doing all other minifying, we can restore the
+ * original content via restoreRegex()
+ */
+ protected function extractRegex()
+ {
+ // PHP only supports $this inside anonymous functions since 5.4
+ $minifier = $this;
+ $callback = function ($match) use ($minifier) {
+ $count = count($minifier->extracted);
+ $placeholder = '"'.$count.'"';
+ $minifier->extracted[$placeholder] = $match[0];
+
+ return $placeholder;
+ };
+
+ // match all chars except `/` and `\`
+ // `\` is allowed though, along with whatever char follows (which is the
+ // one being escaped)
+ // this should allow all chars, except for an unescaped `/` (= the one
+ // closing the regex)
+ // then also ignore bare `/` inside `[]`, where they don't need to be
+ // escaped: anything inside `[]` can be ignored safely
+ $pattern = '\\/(?:[^\\[\\/\\\\\n\r]+|(?:\\\\.)+|(?:\\[(?:[^\\]\\\\\n\r]+|(?:\\\\.)+)+\\])+)++\\/[gimy]*';
+
+ // a regular expression can only be followed by a few operators or some
+ // of the RegExp methods (a `\` followed by a variable or value is
+ // likely part of a division, not a regex)
+ $keywords = array('do', 'in', 'new', 'else', 'throw', 'yield', 'delete', 'return', 'typeof');
+ $before = '([=:,;\+\-\*\/\}\(\{\[&\|!]|^|'.implode('|', $keywords).')\s*';
+ $propertiesAndMethods = array(
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Properties_2
+ 'constructor',
+ 'flags',
+ 'global',
+ 'ignoreCase',
+ 'multiline',
+ 'source',
+ 'sticky',
+ 'unicode',
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Methods_2
+ 'compile(',
+ 'exec(',
+ 'test(',
+ 'toSource(',
+ 'toString(',
+ );
+ $delimiters = array_fill(0, count($propertiesAndMethods), '/');
+ $propertiesAndMethods = array_map('preg_quote', $propertiesAndMethods, $delimiters);
+ $after = '(?=\s*[\.,;\)\}&\|+]|\/\/|$|\.('.implode('|', $propertiesAndMethods).'))';
+ $this->registerPattern('/'.$before.'\K'.$pattern.$after.'/', $callback);
+
+ // regular expressions following a `)` are rather annoying to detect...
+ // quite often, `/` after `)` is a division operator & if it happens to
+ // be followed by another one (or a comment), it is likely to be
+ // confused for a regular expression
+ // however, it's perfectly possible for a regex to follow a `)`: after
+ // a single-line `if()`, `while()`, ... statement, for example
+ // since, when they occur like that, they're always the start of a
+ // statement, there's only a limited amount of ways they can be useful:
+ // by calling the regex methods directly
+ // if a regex following `)` is not followed by `.`,
+ // it's quite likely not a regex
+ $before = '\)\s*';
+ $after = '(?=\s*\.('.implode('|', $propertiesAndMethods).'))';
+ $this->registerPattern('/'.$before.'\K'.$pattern.$after.'/', $callback);
+
+ // 1 more edge case: a regex can be followed by a lot more operators or
+ // keywords if there's a newline (ASI) in between, where the operator
+ // actually starts a new statement
+ // (https://github.com/matthiasmullie/minify/issues/56)
+ $operators = $this->getOperatorsForRegex($this->operatorsBefore, '/');
+ $operators += $this->getOperatorsForRegex($this->keywordsReserved, '/');
+ $after = '(?=\s*\n\s*('.implode('|', $operators).'))';
+ $this->registerPattern('/'.$pattern.$after.'/', $callback);
+ }
+
+ /**
+ * Strip whitespace.
+ *
+ * We won't strip *all* whitespace, but as much as possible. The thing that
+ * we'll preserve are newlines we're unsure about.
+ * JavaScript doesn't require statements to be terminated with a semicolon.
+ * It will automatically fix missing semicolons with ASI (automatic semi-
+ * colon insertion) at the end of line causing errors (without semicolon.)
+ *
+ * Because it's sometimes hard to tell if a newline is part of a statement
+ * that should be terminated or not, we'll just leave some of them alone.
+ *
+ * @param string $content The content to strip the whitespace for
+ *
+ * @return string
+ */
+ protected function stripWhitespace($content)
+ {
+ // uniform line endings, make them all line feed
+ $content = str_replace(array("\r\n", "\r"), "\n", $content);
+
+ // collapse all non-line feed whitespace into a single space
+ $content = preg_replace('/[^\S\n]+/', ' ', $content);
+
+ // strip leading & trailing whitespace
+ $content = str_replace(array(" \n", "\n "), "\n", $content);
+
+ // collapse consecutive line feeds into just 1
+ $content = preg_replace('/\n+/', "\n", $content);
+
+ $operatorsBefore = $this->getOperatorsForRegex($this->operatorsBefore, '/');
+ $operatorsAfter = $this->getOperatorsForRegex($this->operatorsAfter, '/');
+ $operators = $this->getOperatorsForRegex($this->operators, '/');
+ $keywordsBefore = $this->getKeywordsForRegex($this->keywordsBefore, '/');
+ $keywordsAfter = $this->getKeywordsForRegex($this->keywordsAfter, '/');
+
+ // strip whitespace that ends in (or next line begin with) an operator
+ // that allows statements to be broken up over multiple lines
+ unset($operatorsBefore['+'], $operatorsBefore['-'], $operatorsAfter['+'], $operatorsAfter['-']);
+ $content = preg_replace(
+ array(
+ '/('.implode('|', $operatorsBefore).')\s+/',
+ '/\s+('.implode('|', $operatorsAfter).')/',
+ ), '\\1', $content
+ );
+
+ // make sure + and - can't be mistaken for, or joined into ++ and --
+ $content = preg_replace(
+ array(
+ '/(?%&|', $delimiter);
+ $operators['='] = '(?keywordsReserved;
+ $callback = function ($match) use ($minifier, $keywords) {
+ $property = trim($minifier->extracted[$match[1]], '\'"');
+
+ /*
+ * Check if the property is a reserved keyword. In this context (as
+ * property of an object literal/array) it shouldn't matter, but IE8
+ * freaks out with "Expected identifier".
+ */
+ if (in_array($property, $keywords)) {
+ return $match[0];
+ }
+
+ /*
+ * See if the property is in a variable-like format (e.g.
+ * array['key-here'] can't be replaced by array.key-here since '-'
+ * is not a valid character there.
+ */
+ if (!preg_match('/^'.$minifier::REGEX_VARIABLE.'$/u', $property)) {
+ return $match[0];
+ }
+
+ return '.'.$property;
+ };
+
+ /*
+ * Figure out if previous character is a variable name (of the array
+ * we want to use property notation on) - this is to make sure
+ * standalone ['value'] arrays aren't confused for keys-of-an-array.
+ * We can (and only have to) check the last character, because PHP's
+ * regex implementation doesn't allow unfixed-length look-behind
+ * assertions.
+ */
+ preg_match('/(\[[^\]]+\])[^\]]*$/', static::REGEX_VARIABLE, $previousChar);
+ $previousChar = $previousChar[1];
+
+ /*
+ * Make sure word preceding the ['value'] is not a keyword, e.g.
+ * return['x']. Because -again- PHP's regex implementation doesn't allow
+ * unfixed-length look-behind assertions, I'm just going to do a lot of
+ * separate look-behind assertions, one for each keyword.
+ */
+ $keywords = $this->getKeywordsForRegex($keywords);
+ $keywords = '(?
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+namespace MatthiasMullie\Minify;
+
+use MatthiasMullie\Minify\Exceptions\IOException;
+use Psr\Cache\CacheItemInterface;
+
+/**
+ * Abstract minifier class.
+ *
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
+ *
+ * @package Minify
+ * @author Matthias Mullie
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+abstract class Minify
+{
+ /**
+ * The data to be minified.
+ *
+ * @var string[]
+ */
+ protected $data = array();
+
+ /**
+ * Array of patterns to match.
+ *
+ * @var string[]
+ */
+ protected $patterns = array();
+
+ /**
+ * This array will hold content of strings and regular expressions that have
+ * been extracted from the JS source code, so we can reliably match "code",
+ * without having to worry about potential "code-like" characters inside.
+ *
+ * @var string[]
+ */
+ public $extracted = array();
+
+ /**
+ * Init the minify class - optionally, code may be passed along already.
+ */
+ public function __construct(/* $data = null, ... */)
+ {
+ // it's possible to add the source through the constructor as well ;)
+ if (func_num_args()) {
+ call_user_func_array(array($this, 'add'), func_get_args());
+ }
+ }
+
+ /**
+ * Add a file or straight-up code to be minified.
+ *
+ * @param string|string[] $data
+ *
+ * @return static
+ */
+ public function add($data /* $data = null, ... */)
+ {
+ // bogus "usage" of parameter $data: scrutinizer warns this variable is
+ // not used (we're using func_get_args instead to support overloading),
+ // but it still needs to be defined because it makes no sense to have
+ // this function without argument :)
+ $args = array($data) + func_get_args();
+
+ // this method can be overloaded
+ foreach ($args as $data) {
+ if (is_array($data)) {
+ call_user_func_array(array($this, 'add'), $data);
+ continue;
+ }
+
+ // redefine var
+ $data = (string) $data;
+
+ // load data
+ $value = $this->load($data);
+ $key = ($data != $value) ? $data : count($this->data);
+
+ // replace CR linefeeds etc.
+ // @see https://github.com/matthiasmullie/minify/pull/139
+ $value = str_replace(array("\r\n", "\r"), "\n", $value);
+
+ // store data
+ $this->data[$key] = $value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Minify the data & (optionally) saves it to a file.
+ *
+ * @param string[optional] $path Path to write the data to
+ *
+ * @return string The minified data
+ */
+ public function minify($path = null)
+ {
+ $content = $this->execute($path);
+
+ // save to path
+ if ($path !== null) {
+ $this->save($content, $path);
+ }
+
+ return $content;
+ }
+
+ /**
+ * Minify & gzip the data & (optionally) saves it to a file.
+ *
+ * @param string[optional] $path Path to write the data to
+ * @param int[optional] $level Compression level, from 0 to 9
+ *
+ * @return string The minified & gzipped data
+ */
+ public function gzip($path = null, $level = 9)
+ {
+ $content = $this->execute($path);
+ $content = gzencode($content, $level, FORCE_GZIP);
+
+ // save to path
+ if ($path !== null) {
+ $this->save($content, $path);
+ }
+
+ return $content;
+ }
+
+ /**
+ * Minify the data & write it to a CacheItemInterface object.
+ *
+ * @param CacheItemInterface $item Cache item to write the data to
+ *
+ * @return CacheItemInterface Cache item with the minifier data
+ */
+ public function cache(CacheItemInterface $item)
+ {
+ $content = $this->execute();
+ $item->set($content);
+
+ return $item;
+ }
+
+ /**
+ * Minify the data.
+ *
+ * @param string[optional] $path Path to write the data to
+ *
+ * @return string The minified data
+ */
+ abstract public function execute($path = null);
+
+ /**
+ * Load data.
+ *
+ * @param string $data Either a path to a file or the content itself
+ *
+ * @return string
+ */
+ protected function load($data)
+ {
+ // check if the data is a file
+ if ($this->canImportFile($data)) {
+ $data = file_get_contents($data);
+
+ // strip BOM, if any
+ if (substr($data, 0, 3) == "\xef\xbb\xbf") {
+ $data = substr($data, 3);
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * Save to file.
+ *
+ * @param string $content The minified data
+ * @param string $path The path to save the minified data to
+ *
+ * @throws IOException
+ */
+ protected function save($content, $path)
+ {
+ $handler = $this->openFileForWriting($path);
+
+ $this->writeToFile($handler, $content);
+
+ @fclose($handler);
+ }
+
+ /**
+ * Register a pattern to execute against the source content.
+ *
+ * @param string $pattern PCRE pattern
+ * @param string|callable $replacement Replacement value for matched pattern
+ */
+ protected function registerPattern($pattern, $replacement = '')
+ {
+ // study the pattern, we'll execute it more than once
+ $pattern .= 'S';
+
+ $this->patterns[] = array($pattern, $replacement);
+ }
+
+ /**
+ * We can't "just" run some regular expressions against JavaScript: it's a
+ * complex language. E.g. having an occurrence of // xyz would be a comment,
+ * unless it's used within a string. Of you could have something that looks
+ * like a 'string', but inside a comment.
+ * The only way to accurately replace these pieces is to traverse the JS one
+ * character at a time and try to find whatever starts first.
+ *
+ * @param string $content The content to replace patterns in
+ *
+ * @return string The (manipulated) content
+ */
+ protected function replace($content)
+ {
+ $processed = '';
+ $positions = array_fill(0, count($this->patterns), -1);
+ $matches = array();
+
+ while ($content) {
+ // find first match for all patterns
+ foreach ($this->patterns as $i => $pattern) {
+ list($pattern, $replacement) = $pattern;
+
+ // no need to re-run matches that are still in the part of the
+ // content that hasn't been processed
+ if ($positions[$i] >= 0) {
+ continue;
+ }
+
+ $match = null;
+ if (preg_match($pattern, $content, $match, PREG_OFFSET_CAPTURE)) {
+ $matches[$i] = $match;
+
+ // we'll store the match position as well; that way, we
+ // don't have to redo all preg_matches after changing only
+ // the first (we'll still know where those others are)
+ $positions[$i] = $match[0][1];
+ } else {
+ // if the pattern couldn't be matched, there's no point in
+ // executing it again in later runs on this same content;
+ // ignore this one until we reach end of content
+ unset($matches[$i]);
+ $positions[$i] = strlen($content);
+ }
+ }
+
+ // no more matches to find: everything's been processed, break out
+ if (!$matches) {
+ $processed .= $content;
+ break;
+ }
+
+ // see which of the patterns actually found the first thing (we'll
+ // only want to execute that one, since we're unsure if what the
+ // other found was not inside what the first found)
+ $discardLength = min($positions);
+ $firstPattern = array_search($discardLength, $positions);
+ $match = $matches[$firstPattern][0][0];
+
+ // execute the pattern that matches earliest in the content string
+ list($pattern, $replacement) = $this->patterns[$firstPattern];
+ $replacement = $this->replacePattern($pattern, $replacement, $content);
+
+ // figure out which part of the string was unmatched; that's the
+ // part we'll execute the patterns on again next
+ $content = (string) substr($content, $discardLength);
+ $unmatched = (string) substr($content, strpos($content, $match) + strlen($match));
+
+ // move the replaced part to $processed and prepare $content to
+ // again match batch of patterns against
+ $processed .= substr($replacement, 0, strlen($replacement) - strlen($unmatched));
+ $content = $unmatched;
+
+ // first match has been replaced & that content is to be left alone,
+ // the next matches will start after this replacement, so we should
+ // fix their offsets
+ foreach ($positions as $i => $position) {
+ $positions[$i] -= $discardLength + strlen($match);
+ }
+ }
+
+ return $processed;
+ }
+
+ /**
+ * This is where a pattern is matched against $content and the matches
+ * are replaced by their respective value.
+ * This function will be called plenty of times, where $content will always
+ * move up 1 character.
+ *
+ * @param string $pattern Pattern to match
+ * @param string|callable $replacement Replacement value
+ * @param string $content Content to match pattern against
+ *
+ * @return string
+ */
+ protected function replacePattern($pattern, $replacement, $content)
+ {
+ if (is_callable($replacement)) {
+ return preg_replace_callback($pattern, $replacement, $content, 1, $count);
+ } else {
+ return preg_replace($pattern, $replacement, $content, 1, $count);
+ }
+ }
+
+ /**
+ * Strings are a pattern we need to match, in order to ignore potential
+ * code-like content inside them, but we just want all of the string
+ * content to remain untouched.
+ *
+ * This method will replace all string content with simple STRING#
+ * placeholder text, so we've rid all strings from characters that may be
+ * misinterpreted. Original string content will be saved in $this->extracted
+ * and after doing all other minifying, we can restore the original content
+ * via restoreStrings().
+ *
+ * @param string[optional] $chars
+ * @param string[optional] $placeholderPrefix
+ */
+ protected function extractStrings($chars = '\'"', $placeholderPrefix = '')
+ {
+ // PHP only supports $this inside anonymous functions since 5.4
+ $minifier = $this;
+ $callback = function ($match) use ($minifier, $placeholderPrefix) {
+ // check the second index here, because the first always contains a quote
+ if ($match[2] === '') {
+ /*
+ * Empty strings need no placeholder; they can't be confused for
+ * anything else anyway.
+ * But we still needed to match them, for the extraction routine
+ * to skip over this particular string.
+ */
+ return $match[0];
+ }
+
+ $count = count($minifier->extracted);
+ $placeholder = $match[1].$placeholderPrefix.$count.$match[1];
+ $minifier->extracted[$placeholder] = $match[1].$match[2].$match[1];
+
+ return $placeholder;
+ };
+
+ /*
+ * The \\ messiness explained:
+ * * Don't count ' or " as end-of-string if it's escaped (has backslash
+ * in front of it)
+ * * Unless... that backslash itself is escaped (another leading slash),
+ * in which case it's no longer escaping the ' or "
+ * * So there can be either no backslash, or an even number
+ * * multiply all of that times 4, to account for the escaping that has
+ * to be done to pass the backslash into the PHP string without it being
+ * considered as escape-char (times 2) and to get it in the regex,
+ * escaped (times 2)
+ */
+ $this->registerPattern('/(['.$chars.'])(.*?(?extracted.
+ *
+ * @param string $content
+ *
+ * @return string
+ */
+ protected function restoreExtractedData($content)
+ {
+ if (!$this->extracted) {
+ // nothing was extracted, nothing to restore
+ return $content;
+ }
+
+ $content = strtr($content, $this->extracted);
+
+ $this->extracted = array();
+
+ return $content;
+ }
+
+ /**
+ * Check if the path is a regular file and can be read.
+ *
+ * @param string $path
+ *
+ * @return bool
+ */
+ protected function canImportFile($path)
+ {
+ $parsed = parse_url($path);
+ if (
+ // file is elsewhere
+ isset($parsed['host']) ||
+ // file responds to queries (may change, or need to bypass cache)
+ isset($parsed['query'])
+ ) {
+ return false;
+ }
+
+ return strlen($path) < PHP_MAXPATHLEN && @is_file($path) && is_readable($path);
+ }
+
+ /**
+ * Attempts to open file specified by $path for writing.
+ *
+ * @param string $path The path to the file
+ *
+ * @return resource Specifier for the target file
+ *
+ * @throws IOException
+ */
+ protected function openFileForWriting($path)
+ {
+ if (($handler = @fopen($path, 'w')) === false) {
+ throw new IOException('The file "'.$path.'" could not be opened for writing. Check if PHP has enough permissions.');
+ }
+
+ return $handler;
+ }
+
+ /**
+ * Attempts to write $content to the file specified by $handler. $path is used for printing exceptions.
+ *
+ * @param resource $handler The resource to write to
+ * @param string $content The content to write
+ * @param string $path The path to the file (for exception printing only)
+ *
+ * @throws IOException
+ */
+ protected function writeToFile($handler, $content, $path = '')
+ {
+ if (($result = @fwrite($handler, $content)) === false || ($result < strlen($content))) {
+ throw new IOException('The file "'.$path.'" could not be written to. Check your disk space and file permissions.');
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/path-converter/LICENSE b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/path-converter/LICENSE
new file mode 100644
index 0000000..491295a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/path-converter/LICENSE
@@ -0,0 +1,18 @@
+Copyright (c) 2015 Matthias Mullie
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/path-converter/composer.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/path-converter/composer.json
new file mode 100644
index 0000000..1cb6a4c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/path-converter/composer.json
@@ -0,0 +1,28 @@
+{
+ "name": "matthiasmullie/path-converter",
+ "type": "library",
+ "description": "Relative path converter",
+ "keywords": ["relative", "path", "converter", "paths"],
+ "homepage": "http://github.com/matthiasmullie/path-converter",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Matthias Mullie",
+ "homepage": "http://www.mullie.eu",
+ "email": "pathconverter@mullie.eu",
+ "role": "Developer"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.0",
+ "ext-pcre": "*"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8"
+ },
+ "autoload": {
+ "psr-4": {
+ "MatthiasMullie\\PathConverter\\": "src/"
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/path-converter/src/Converter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/path-converter/src/Converter.php
new file mode 100644
index 0000000..b92b24c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/path-converter/src/Converter.php
@@ -0,0 +1,195 @@
+
+ * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+class Converter implements ConverterInterface
+{
+ /**
+ * @var string
+ */
+ protected $from;
+
+ /**
+ * @var string
+ */
+ protected $to;
+
+ /**
+ * @param string $from The original base path (directory, not file!)
+ * @param string $to The new base path (directory, not file!)
+ */
+ public function __construct($from, $to)
+ {
+ $shared = $this->shared($from, $to);
+ if ($shared === '') {
+ // when both paths have nothing in common, one of them is probably
+ // absolute while the other is relative
+ $cwd = getcwd();
+ $from = strpos($from, $cwd) === 0 ? $from : $cwd.'/'.$from;
+ $to = strpos($to, $cwd) === 0 ? $to : $cwd.'/'.$to;
+
+ // or traveling the tree via `..`
+ // attempt to resolve path, or assume it's fine if it doesn't exist
+ $from = @realpath($from) ?: $from;
+ $to = @realpath($to) ?: $to;
+ }
+
+ $from = $this->dirname($from);
+ $to = $this->dirname($to);
+
+ $from = $this->normalize($from);
+ $to = $this->normalize($to);
+
+ $this->from = $from;
+ $this->to = $to;
+ }
+
+ /**
+ * Normalize path.
+ *
+ * @param string $path
+ *
+ * @return string
+ */
+ protected function normalize($path)
+ {
+ // deal with different operating systems' directory structure
+ $path = rtrim(str_replace(DIRECTORY_SEPARATOR, '/', $path), '/');
+
+ /*
+ * Example:
+ * /home/forkcms/frontend/cache/compiled_templates/../../core/layout/css/../images/img.gif
+ * to
+ * /home/forkcms/frontend/core/layout/images/img.gif
+ */
+ do {
+ $path = preg_replace('/[^\/]+(? $chunk) {
+ if (isset($path2[$i]) && $path1[$i] == $path2[$i]) {
+ $shared[] = $chunk;
+ } else {
+ break;
+ }
+ }
+
+ return implode('/', $shared);
+ }
+
+ /**
+ * Convert paths relative from 1 file to another.
+ *
+ * E.g.
+ * ../images/img.gif relative to /home/forkcms/frontend/core/layout/css
+ * should become:
+ * ../../core/layout/images/img.gif relative to
+ * /home/forkcms/frontend/cache/minified_css
+ *
+ * @param string $path The relative path that needs to be converted
+ *
+ * @return string The new relative path
+ */
+ public function convert($path)
+ {
+ // quit early if conversion makes no sense
+ if ($this->from === $this->to) {
+ return $path;
+ }
+
+ $path = $this->normalize($path);
+ // if we're not dealing with a relative path, just return absolute
+ if (strpos($path, '/') === 0) {
+ return $path;
+ }
+
+ // normalize paths
+ $path = $this->normalize($this->from.'/'.$path);
+
+ // strip shared ancestor paths
+ $shared = $this->shared($path, $this->to);
+ $path = mb_substr($path, mb_strlen($shared));
+ $to = mb_substr($this->to, mb_strlen($shared));
+
+ // add .. for every directory that needs to be traversed to new path
+ $to = str_repeat('../', mb_substr_count($to, '/'));
+
+ return $to.ltrim($path, '/');
+ }
+
+ /**
+ * Attempt to get the directory name from a path.
+ *
+ * @param string $path
+ *
+ * @return string
+ */
+ protected function dirname($path)
+ {
+ if (@is_file($path)) {
+ return dirname($path);
+ }
+
+ if (@is_dir($path)) {
+ return rtrim($path, '/');
+ }
+
+ // no known file/dir, start making assumptions
+
+ // ends in / = dir
+ if (mb_substr($path, -1) === '/') {
+ return rtrim($path, '/');
+ }
+
+ // has a dot in the name, likely a file
+ if (preg_match('/.*\..*$/', basename($path)) !== 0) {
+ return dirname($path);
+ }
+
+ // you're on your own here!
+ return $path;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/path-converter/src/ConverterInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/path-converter/src/ConverterInterface.php
new file mode 100644
index 0000000..dc1b765
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/path-converter/src/ConverterInterface.php
@@ -0,0 +1,24 @@
+
+ * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+interface ConverterInterface
+{
+ /**
+ * Convert file paths.
+ *
+ * @param string $path The path to be converted
+ *
+ * @return string The new path
+ */
+ public function convert($path);
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/path-converter/src/NoConverter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/path-converter/src/NoConverter.php
new file mode 100644
index 0000000..2fcfd0f
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/matthiasmullie/path-converter/src/NoConverter.php
@@ -0,0 +1,23 @@
+
+ * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+class NoConverter implements ConverterInterface
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function convert($path)
+ {
+ return $path;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/natxet/CssMin/README b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/natxet/CssMin/README
new file mode 100644
index 0000000..b93fa4d
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/natxet/CssMin/README
@@ -0,0 +1,3 @@
+This is just a copy of the CssMin by Joe Scylla to format it for Composer.
+Composer: http://packagist.org/about-composer
+CssMin: http://code.google.com/p/cssmin/
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/natxet/CssMin/composer.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/natxet/CssMin/composer.json
new file mode 100644
index 0000000..f477e9a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/natxet/CssMin/composer.json
@@ -0,0 +1,26 @@
+{
+ "name": "natxet/cssmin",
+ "description": "Minifying CSS",
+ "type": "library",
+ "keywords": ["css","minify"],
+ "homepage": "http://code.google.com/p/cssmin/",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Joe Scylla",
+ "email": "joe.scylla@gmail.com",
+ "homepage": "https://profiles.google.com/joe.scylla"
+ }
+ ],
+ "require": {
+ "php": ">=5.0"
+ },
+ "autoload": {
+ "classmap": ["src/"]
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0-dev"
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/natxet/CssMin/src/CssMin.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/natxet/CssMin/src/CssMin.php
new file mode 100644
index 0000000..835b27a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/natxet/CssMin/src/CssMin.php
@@ -0,0 +1,5155 @@
+
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ * --
+ *
+ * @package CssMin
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+/**
+ * Abstract definition of a CSS token class.
+ *
+ * Every token has to extend this class.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+abstract class aCssToken
+{
+ /**
+ * Returns the token as string.
+ *
+ * @return string
+ */
+ abstract public function __toString();
+}
+
+/**
+ * Abstract definition of a for a ruleset start token.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+abstract class aCssRulesetStartToken extends aCssToken
+{
+
+}
+
+/**
+ * Abstract definition of a for ruleset end token.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+abstract class aCssRulesetEndToken extends aCssToken
+{
+ /**
+ * Implements {@link aCssToken::__toString()}.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return "}";
+ }
+}
+
+/**
+ * Abstract definition of a parser plugin.
+ *
+ * Every parser plugin have to extend this class. A parser plugin contains the logic to parse one or aspects of a
+ * stylesheet.
+ *
+ * @package CssMin/Parser/Plugins
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+abstract class aCssParserPlugin
+{
+ /**
+ * Plugin configuration.
+ *
+ * @var array
+ */
+ protected $configuration = array();
+ /**
+ * The CssParser of the plugin.
+ *
+ * @var CssParser
+ */
+ protected $parser = null;
+ /**
+ * Plugin buffer.
+ *
+ * @var string
+ */
+ protected $buffer = "";
+ /**
+ * Constructor.
+ *
+ * @param CssParser $parser The CssParser object of this plugin.
+ * @param array $configuration Plugin configuration [optional]
+ * @return void
+ */
+ public function __construct(CssParser $parser, array $configuration = null)
+ {
+ $this->configuration = $configuration;
+ $this->parser = $parser;
+ }
+ /**
+ * Returns the array of chars triggering the parser plugin.
+ *
+ * @return array
+ */
+ abstract public function getTriggerChars();
+ /**
+ * Returns the array of states triggering the parser plugin or FALSE if every state will trigger the parser plugin.
+ *
+ * @return array
+ */
+ abstract public function getTriggerStates();
+ /**
+ * Parser routine of the plugin.
+ *
+ * @param integer $index Current index
+ * @param string $char Current char
+ * @param string $previousChar Previous char
+ * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
+ */
+ abstract public function parse($index, $char, $previousChar, $state);
+}
+
+/**
+ * Abstract definition of a minifier plugin class.
+ *
+ * Minifier plugin process the parsed tokens one by one to apply changes to the token. Every minifier plugin has to
+ * extend this class.
+ *
+ * @package CssMin/Minifier/Plugins
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+abstract class aCssMinifierPlugin
+{
+ /**
+ * Plugin configuration.
+ *
+ * @var array
+ */
+ protected $configuration = array();
+ /**
+ * The CssMinifier of the plugin.
+ *
+ * @var CssMinifier
+ */
+ protected $minifier = null;
+ /**
+ * Constructor.
+ *
+ * @param CssMinifier $minifier The CssMinifier object of this plugin.
+ * @param array $configuration Plugin configuration [optional]
+ * @return void
+ */
+ public function __construct(CssMinifier $minifier, array $configuration = array())
+ {
+ $this->configuration = $configuration;
+ $this->minifier = $minifier;
+ }
+ /**
+ * Apply the plugin to the token.
+ *
+ * @param aCssToken $token Token to process
+ * @return boolean Return TRUE to break the processing of this token; FALSE to continue
+ */
+ abstract public function apply(aCssToken &$token);
+ /**
+ * --
+ *
+ * @return array
+ */
+ abstract public function getTriggerTokens();
+}
+
+/**
+ * Abstract definition of a minifier filter class.
+ *
+ * Minifier filters allows a pre-processing of the parsed token to add, edit or delete tokens. Every minifier filter
+ * has to extend this class.
+ *
+ * @package CssMin/Minifier/Filters
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+abstract class aCssMinifierFilter
+{
+ /**
+ * Filter configuration.
+ *
+ * @var array
+ */
+ protected $configuration = array();
+ /**
+ * The CssMinifier of the filter.
+ *
+ * @var CssMinifier
+ */
+ protected $minifier = null;
+ /**
+ * Constructor.
+ *
+ * @param CssMinifier $minifier The CssMinifier object of this plugin.
+ * @param array $configuration Filter configuration [optional]
+ * @return void
+ */
+ public function __construct(CssMinifier $minifier, array $configuration = array())
+ {
+ $this->configuration = $configuration;
+ $this->minifier = $minifier;
+ }
+ /**
+ * Filter the tokens.
+ *
+ * @param array $tokens Array of objects of type aCssToken
+ * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
+ */
+ abstract public function apply(array &$tokens);
+}
+
+/**
+ * Abstract formatter definition.
+ *
+ * Every formatter have to extend this class.
+ *
+ * @package CssMin/Formatter
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+abstract class aCssFormatter
+{
+ /**
+ * Indent string.
+ *
+ * @var string
+ */
+ protected $indent = " ";
+ /**
+ * Declaration padding.
+ *
+ * @var integer
+ */
+ protected $padding = 0;
+ /**
+ * Tokens.
+ *
+ * @var array
+ */
+ protected $tokens = array();
+ /**
+ * Constructor.
+ *
+ * @param array $tokens Array of CssToken
+ * @param string $indent Indent string [optional]
+ * @param integer $padding Declaration value padding [optional]
+ */
+ public function __construct(array $tokens, $indent = null, $padding = null)
+ {
+ $this->tokens = $tokens;
+ $this->indent = !is_null($indent) ? $indent : $this->indent;
+ $this->padding = !is_null($padding) ? $padding : $this->padding;
+ }
+ /**
+ * Returns the array of aCssToken as formatted string.
+ *
+ * @return string
+ */
+ abstract public function __toString();
+}
+
+/**
+ * Abstract definition of a ruleset declaration token.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+abstract class aCssDeclarationToken extends aCssToken
+{
+ /**
+ * Is the declaration flagged as important?
+ *
+ * @var boolean
+ */
+ public $IsImportant = false;
+ /**
+ * Is the declaration flagged as last one of the ruleset?
+ *
+ * @var boolean
+ */
+ public $IsLast = false;
+ /**
+ * Property name of the declaration.
+ *
+ * @var string
+ */
+ public $Property = "";
+ /**
+ * Value of the declaration.
+ *
+ * @var string
+ */
+ public $Value = "";
+ /**
+ * Set the properties of the @font-face declaration.
+ *
+ * @param string $property Property of the declaration
+ * @param string $value Value of the declaration
+ * @param boolean $isImportant Is the !important flag is set?
+ * @param boolean $IsLast Is the declaration the last one of the block?
+ * @return void
+ */
+ public function __construct($property, $value, $isImportant = false, $isLast = false)
+ {
+ $this->Property = $property;
+ $this->Value = $value;
+ $this->IsImportant = $isImportant;
+ $this->IsLast = $isLast;
+ }
+ /**
+ * Implements {@link aCssToken::__toString()}.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->Property . ":" . $this->Value . ($this->IsImportant ? " !important" : "") . ($this->IsLast ? "" : ";");
+ }
+}
+
+/**
+ * Abstract definition of a for at-rule block start token.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+abstract class aCssAtBlockStartToken extends aCssToken
+{
+
+}
+
+/**
+ * Abstract definition of a for at-rule block end token.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+abstract class aCssAtBlockEndToken extends aCssToken
+{
+ /**
+ * Implements {@link aCssToken::__toString()}.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return "}";
+ }
+}
+
+/**
+ * {@link aCssFromatter Formatter} returning the CSS source in {@link http://goo.gl/etzLs Whitesmiths indent style}.
+ *
+ * @package CssMin/Formatter
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssWhitesmithsFormatter extends aCssFormatter
+{
+ /**
+ * Implements {@link aCssFormatter::__toString()}.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ $r = array();
+ $level = 0;
+ for ($i = 0, $l = count($this->tokens); $i < $l; $i++)
+ {
+ $token = $this->tokens[$i];
+ $class = get_class($token);
+ $indent = str_repeat($this->indent, $level);
+ if ($class === "CssCommentToken")
+ {
+ $lines = array_map("trim", explode("\n", $token->Comment));
+ for ($ii = 0, $ll = count($lines); $ii < $ll; $ii++)
+ {
+ $r[] = $indent . (substr($lines[$ii], 0, 1) == "*" ? " " : "") . $lines[$ii];
+ }
+ }
+ elseif ($class === "CssAtCharsetToken")
+ {
+ $r[] = $indent . "@charset " . $token->Charset . ";";
+ }
+ elseif ($class === "CssAtFontFaceStartToken")
+ {
+ $r[] = $indent . "@font-face";
+ $r[] = $this->indent . $indent . "{";
+ $level++;
+ }
+ elseif ($class === "CssAtImportToken")
+ {
+ $r[] = $indent . "@import " . $token->Import . " " . implode(", ", $token->MediaTypes) . ";";
+ }
+ elseif ($class === "CssAtKeyframesStartToken")
+ {
+ $r[] = $indent . "@keyframes " . $token->Name;
+ $r[] = $this->indent . $indent . "{";
+ $level++;
+ }
+ elseif ($class === "CssAtMediaStartToken")
+ {
+ $r[] = $indent . "@media " . implode(", ", $token->MediaTypes);
+ $r[] = $this->indent . $indent . "{";
+ $level++;
+ }
+ elseif ($class === "CssAtPageStartToken")
+ {
+ $r[] = $indent . "@page";
+ $r[] = $this->indent . $indent . "{";
+ $level++;
+ }
+ elseif ($class === "CssAtVariablesStartToken")
+ {
+ $r[] = $indent . "@variables " . implode(", ", $token->MediaTypes);
+ $r[] = $this->indent . $indent . "{";
+ $level++;
+ }
+ elseif ($class === "CssRulesetStartToken" || $class === "CssAtKeyframesRulesetStartToken")
+ {
+ $r[] = $indent . implode(", ", $token->Selectors);
+ $r[] = $this->indent . $indent . "{";
+ $level++;
+ }
+ elseif ($class === "CssAtFontFaceDeclarationToken"
+ || $class === "CssAtKeyframesRulesetDeclarationToken"
+ || $class === "CssAtPageDeclarationToken"
+ || $class === "CssAtVariablesDeclarationToken"
+ || $class === "CssRulesetDeclarationToken"
+ )
+ {
+ $declaration = $indent . $token->Property . ": ";
+ if ($this->padding)
+ {
+ $declaration = str_pad($declaration, $this->padding, " ", STR_PAD_RIGHT);
+ }
+ $r[] = $declaration . $token->Value . ($token->IsImportant ? " !important" : "") . ";";
+ }
+ elseif ($class === "CssAtFontFaceEndToken"
+ || $class === "CssAtMediaEndToken"
+ || $class === "CssAtKeyframesEndToken"
+ || $class === "CssAtKeyframesRulesetEndToken"
+ || $class === "CssAtPageEndToken"
+ || $class === "CssAtVariablesEndToken"
+ || $class === "CssRulesetEndToken"
+ )
+ {
+ $r[] = $indent . "}";
+ $level--;
+ }
+ }
+ return implode("\n", $r);
+ }
+}
+
+/**
+ * This {@link aCssMinifierPlugin} will process var-statement and sets the declaration value to the variable value.
+ *
+ * This plugin only apply the variable values. The variable values itself will get parsed by the
+ * {@link CssVariablesMinifierFilter}.
+ *
+ * Example:
+ *
+ * @variables
+ * {
+ * defaultColor: black;
+ * }
+ * color: var(defaultColor);
+ *
+ *
+ * Will get converted to:
+ *
+ * color:black;
+ *
+ *
+ * @package CssMin/Minifier/Plugins
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssVariablesMinifierPlugin extends aCssMinifierPlugin
+{
+ /**
+ * Regular expression matching a value.
+ *
+ * @var string
+ */
+ private $reMatch = "/var\((.+)\)/iSU";
+ /**
+ * Parsed variables.
+ *
+ * @var array
+ */
+ private $variables = null;
+ /**
+ * Returns the variables.
+ *
+ * @return array
+ */
+ public function getVariables()
+ {
+ return $this->variables;
+ }
+ /**
+ * Implements {@link aCssMinifierPlugin::minify()}.
+ *
+ * @param aCssToken $token Token to process
+ * @return boolean Return TRUE to break the processing of this token; FALSE to continue
+ */
+ public function apply(aCssToken &$token)
+ {
+ if (stripos($token->Value, "var") !== false && preg_match_all($this->reMatch, $token->Value, $m))
+ {
+ $mediaTypes = $token->MediaTypes;
+ if (!in_array("all", $mediaTypes))
+ {
+ $mediaTypes[] = "all";
+ }
+ for ($i = 0, $l = count($m[0]); $i < $l; $i++)
+ {
+ $variable = trim($m[1][$i]);
+ foreach ($mediaTypes as $mediaType)
+ {
+ if (isset($this->variables[$mediaType], $this->variables[$mediaType][$variable]))
+ {
+ // Variable value found => set the declaration value to the variable value and return
+ $token->Value = str_replace($m[0][$i], $this->variables[$mediaType][$variable], $token->Value);
+ continue 2;
+ }
+ }
+ // If no value was found trigger an error and replace the token with a CssNullToken
+ CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": No value found for variable " . $variable . "
in media types " . implode(", ", $mediaTypes) . "
", (string) $token));
+ $token = new CssNullToken();
+ return true;
+ }
+ }
+ return false;
+ }
+ /**
+ * Implements {@link aMinifierPlugin::getTriggerTokens()}
+ *
+ * @return array
+ */
+ public function getTriggerTokens()
+ {
+ return array
+ (
+ "CssAtFontFaceDeclarationToken",
+ "CssAtPageDeclarationToken",
+ "CssRulesetDeclarationToken"
+ );
+ }
+ /**
+ * Sets the variables.
+ *
+ * @param array $variables Variables to set
+ * @return void
+ */
+ public function setVariables(array $variables)
+ {
+ $this->variables = $variables;
+ }
+}
+
+/**
+ * This {@link aCssMinifierFilter minifier filter} will parse the variable declarations out of @variables at-rule
+ * blocks. The variables will get store in the {@link CssVariablesMinifierPlugin} that will apply the variables to
+ * declaration.
+ *
+ * @package CssMin/Minifier/Filters
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssVariablesMinifierFilter extends aCssMinifierFilter
+{
+ /**
+ * Implements {@link aCssMinifierFilter::filter()}.
+ *
+ * @param array $tokens Array of objects of type aCssToken
+ * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
+ */
+ public function apply(array &$tokens)
+ {
+ $variables = array();
+ $defaultMediaTypes = array("all");
+ $mediaTypes = array();
+ $remove = array();
+ for($i = 0, $l = count($tokens); $i < $l; $i++)
+ {
+ // @variables at-rule block found
+ if (get_class($tokens[$i]) === "CssAtVariablesStartToken")
+ {
+ $remove[] = $i;
+ $mediaTypes = (count($tokens[$i]->MediaTypes) == 0 ? $defaultMediaTypes : $tokens[$i]->MediaTypes);
+ foreach ($mediaTypes as $mediaType)
+ {
+ if (!isset($variables[$mediaType]))
+ {
+ $variables[$mediaType] = array();
+ }
+ }
+ // Read the variable declaration tokens
+ for($i = $i; $i < $l; $i++)
+ {
+ // Found a variable declaration => read the variable values
+ if (get_class($tokens[$i]) === "CssAtVariablesDeclarationToken")
+ {
+ foreach ($mediaTypes as $mediaType)
+ {
+ $variables[$mediaType][$tokens[$i]->Property] = $tokens[$i]->Value;
+ }
+ $remove[] = $i;
+ }
+ // Found the variables end token => break;
+ elseif (get_class($tokens[$i]) === "CssAtVariablesEndToken")
+ {
+ $remove[] = $i;
+ break;
+ }
+ }
+ }
+ }
+ // Variables in @variables at-rule blocks
+ foreach($variables as $mediaType => $null)
+ {
+ foreach($variables[$mediaType] as $variable => $value)
+ {
+ // If a var() statement in a variable value found...
+ if (stripos($value, "var") !== false && preg_match_all("/var\((.+)\)/iSU", $value, $m))
+ {
+ // ... then replace the var() statement with the variable values.
+ for ($i = 0, $l = count($m[0]); $i < $l; $i++)
+ {
+ $variables[$mediaType][$variable] = str_replace($m[0][$i], (isset($variables[$mediaType][$m[1][$i]]) ? $variables[$mediaType][$m[1][$i]] : ""), $variables[$mediaType][$variable]);
+ }
+ }
+ }
+ }
+ // Remove the complete @variables at-rule block
+ foreach ($remove as $i)
+ {
+ $tokens[$i] = null;
+ }
+ if (!($plugin = $this->minifier->getPlugin("CssVariablesMinifierPlugin")))
+ {
+ CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": The plugin CssVariablesMinifierPlugin
was not found but is required for " . __CLASS__ . "
"));
+ }
+ else
+ {
+ $plugin->setVariables($variables);
+ }
+ return count($remove);
+ }
+}
+
+/**
+ * {@link aCssParserPlugin Parser plugin} for preserve parsing url() values.
+ *
+ * This plugin return no {@link aCssToken CssToken} but ensures that url() values will get parsed properly.
+ *
+ * @package CssMin/Parser/Plugins
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssUrlParserPlugin extends aCssParserPlugin
+{
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerChars()}.
+ *
+ * @return array
+ */
+ public function getTriggerChars()
+ {
+ return array("(", ")");
+ }
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerStates()}.
+ *
+ * @return array
+ */
+ public function getTriggerStates()
+ {
+ return false;
+ }
+ /**
+ * Implements {@link aCssParserPlugin::parse()}.
+ *
+ * @param integer $index Current index
+ * @param string $char Current char
+ * @param string $previousChar Previous char
+ * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
+ */
+ public function parse($index, $char, $previousChar, $state)
+ {
+ // Start of string
+ if ($char === "(" && strtolower(substr($this->parser->getSource(), $index - 3, 4)) === "url(" && $state !== "T_URL")
+ {
+ $this->parser->pushState("T_URL");
+ $this->parser->setExclusive(__CLASS__);
+ }
+ // Escaped LF in url => remove escape backslash and LF
+ elseif ($char === "\n" && $previousChar === "\\" && $state === "T_URL")
+ {
+ $this->parser->setBuffer(substr($this->parser->getBuffer(), 0, -2));
+ }
+ // Parse error: Unescaped LF in string literal
+ elseif ($char === "\n" && $previousChar !== "\\" && $state === "T_URL")
+ {
+ $line = $this->parser->getBuffer();
+ $this->parser->setBuffer(substr($this->parser->getBuffer(), 0, -1) . ")"); // Replace the LF with the url string delimiter
+ $this->parser->popState();
+ $this->parser->unsetExclusive();
+ CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated string literal", $line . "_"));
+ }
+ // End of string
+ elseif ($char === ")" && $state === "T_URL")
+ {
+ $this->parser->popState();
+ $this->parser->unsetExclusive();
+ }
+ else
+ {
+ return false;
+ }
+ return true;
+ }
+}
+
+/**
+ * {@link aCssParserPlugin Parser plugin} for preserve parsing string values.
+ *
+ * This plugin return no {@link aCssToken CssToken} but ensures that string values will get parsed properly.
+ *
+ * @package CssMin/Parser/Plugins
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssStringParserPlugin extends aCssParserPlugin
+{
+ /**
+ * Current string delimiter char.
+ *
+ * @var string
+ */
+ private $delimiterChar = null;
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerChars()}.
+ *
+ * @return array
+ */
+ public function getTriggerChars()
+ {
+ return array("\"", "'", "\n");
+ }
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerStates()}.
+ *
+ * @return array
+ */
+ public function getTriggerStates()
+ {
+ return false;
+ }
+ /**
+ * Implements {@link aCssParserPlugin::parse()}.
+ *
+ * @param integer $index Current index
+ * @param string $char Current char
+ * @param string $previousChar Previous char
+ * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
+ */
+ public function parse($index, $char, $previousChar, $state)
+ {
+ // Start of string
+ if (($char === "\"" || $char === "'") && $state !== "T_STRING")
+ {
+ $this->delimiterChar = $char;
+ $this->parser->pushState("T_STRING");
+ $this->parser->setExclusive(__CLASS__);
+ }
+ // Escaped LF in string => remove escape backslash and LF
+ elseif ($char === "\n" && $previousChar === "\\" && $state === "T_STRING")
+ {
+ $this->parser->setBuffer(substr($this->parser->getBuffer(), 0, -2));
+ }
+ // Parse error: Unescaped LF in string literal
+ elseif ($char === "\n" && $previousChar !== "\\" && $state === "T_STRING")
+ {
+ $line = $this->parser->getBuffer();
+ $this->parser->popState();
+ $this->parser->unsetExclusive();
+ $this->parser->setBuffer(substr($this->parser->getBuffer(), 0, -1) . $this->delimiterChar); // Replace the LF with the current string char
+ CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated string literal", $line . "_"));
+ $this->delimiterChar = null;
+ }
+ // End of string
+ elseif ($char === $this->delimiterChar && $state === "T_STRING")
+ {
+ // If the Previous char is a escape char count the amount of the previous escape chars. If the amount of
+ // escape chars is uneven do not end the string
+ if ($previousChar == "\\")
+ {
+ $source = $this->parser->getSource();
+ $c = 1;
+ $i = $index - 2;
+ while (substr($source, $i, 1) === "\\")
+ {
+ $c++; $i--;
+ }
+ if ($c % 2)
+ {
+ return false;
+ }
+ }
+ $this->parser->popState();
+ $this->parser->unsetExclusive();
+ $this->delimiterChar = null;
+ }
+ else
+ {
+ return false;
+ }
+ return true;
+ }
+}
+
+/**
+ * This {@link aCssMinifierFilter minifier filter} sorts the ruleset declarations of a ruleset by name.
+ *
+ * @package CssMin/Minifier/Filters
+ * @link http://code.google.com/p/cssmin/
+ * @author Rowan Beentje
+ * @copyright Rowan Beentje
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssSortRulesetPropertiesMinifierFilter extends aCssMinifierFilter
+{
+ /**
+ * Implements {@link aCssMinifierFilter::filter()}.
+ *
+ * @param array $tokens Array of objects of type aCssToken
+ * @return integer Count of added, changed or removed tokens; a return value larger than 0 will rebuild the array
+ */
+ public function apply(array &$tokens)
+ {
+ $r = 0;
+ for ($i = 0, $l = count($tokens); $i < $l; $i++)
+ {
+ // Only look for ruleset start rules
+ if (get_class($tokens[$i]) !== "CssRulesetStartToken") { continue; }
+ // Look for the corresponding ruleset end
+ $endIndex = false;
+ for ($ii = $i + 1; $ii < $l; $ii++)
+ {
+ if (get_class($tokens[$ii]) !== "CssRulesetEndToken") { continue; }
+ $endIndex = $ii;
+ break;
+ }
+ if (!$endIndex) { break; }
+ $startIndex = $i;
+ $i = $endIndex;
+ // Skip if there's only one token in this ruleset
+ if ($endIndex - $startIndex <= 2) { continue; }
+ // Ensure that everything between the start and end is a declaration token, for safety
+ for ($ii = $startIndex + 1; $ii < $endIndex; $ii++)
+ {
+ if (get_class($tokens[$ii]) !== "CssRulesetDeclarationToken") { continue(2); }
+ }
+ $declarations = array_slice($tokens, $startIndex + 1, $endIndex - $startIndex - 1);
+ // Check whether a sort is required
+ $sortRequired = $lastPropertyName = false;
+ foreach ($declarations as $declaration)
+ {
+ if ($lastPropertyName)
+ {
+ if (strcmp($lastPropertyName, $declaration->Property) > 0)
+ {
+ $sortRequired = true;
+ break;
+ }
+ }
+ $lastPropertyName = $declaration->Property;
+ }
+ if (!$sortRequired) { continue; }
+ // Arrange the declarations alphabetically by name
+ usort($declarations, array(__CLASS__, "userDefinedSort1"));
+ // Update "IsLast" property
+ for ($ii = 0, $ll = count($declarations) - 1; $ii <= $ll; $ii++)
+ {
+ if ($ii == $ll)
+ {
+ $declarations[$ii]->IsLast = true;
+ }
+ else
+ {
+ $declarations[$ii]->IsLast = false;
+ }
+ }
+ // Splice back into the array.
+ array_splice($tokens, $startIndex + 1, $endIndex - $startIndex - 1, $declarations);
+ $r += $endIndex - $startIndex - 1;
+ }
+ return $r;
+ }
+ /**
+ * User defined sort function.
+ *
+ * @return integer
+ */
+ public static function userDefinedSort1($a, $b)
+ {
+ return strcmp($a->Property, $b->Property);
+ }
+}
+
+/**
+ * This {@link aCssToken CSS token} represents the start of a ruleset.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssRulesetStartToken extends aCssRulesetStartToken
+{
+ /**
+ * Array of selectors.
+ *
+ * @var array
+ */
+ public $Selectors = array();
+ /**
+ * Set the properties of a ruleset token.
+ *
+ * @param array $selectors Selectors of the ruleset
+ * @return void
+ */
+ public function __construct(array $selectors = array())
+ {
+ $this->Selectors = $selectors;
+ }
+ /**
+ * Implements {@link aCssToken::__toString()}.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return implode(",", $this->Selectors) . "{";
+ }
+}
+
+/**
+ * {@link aCssParserPlugin Parser plugin} for parsing ruleset block with including declarations.
+ *
+ * Found rulesets will add a {@link CssRulesetStartToken} and {@link CssRulesetEndToken} to the
+ * parser; including declarations as {@link CssRulesetDeclarationToken}.
+ *
+ * @package CssMin/Parser/Plugins
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssRulesetParserPlugin extends aCssParserPlugin
+{
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerChars()}.
+ *
+ * @return array
+ */
+ public function getTriggerChars()
+ {
+ return array(",", "{", "}", ":", ";");
+ }
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerStates()}.
+ *
+ * @return array
+ */
+ public function getTriggerStates()
+ {
+ return array("T_DOCUMENT", "T_AT_MEDIA", "T_RULESET::SELECTORS", "T_RULESET", "T_RULESET_DECLARATION");
+ }
+ /**
+ * Selectors.
+ *
+ * @var array
+ */
+ private $selectors = array();
+ /**
+ * Implements {@link aCssParserPlugin::parse()}.
+ *
+ * @param integer $index Current index
+ * @param string $char Current char
+ * @param string $previousChar Previous char
+ * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
+ */
+ public function parse($index, $char, $previousChar, $state)
+ {
+ // Start of Ruleset and selectors
+ if ($char === "," && ($state === "T_DOCUMENT" || $state === "T_AT_MEDIA" || $state === "T_RULESET::SELECTORS"))
+ {
+ if ($state !== "T_RULESET::SELECTORS")
+ {
+ $this->parser->pushState("T_RULESET::SELECTORS");
+ }
+ $this->selectors[] = $this->parser->getAndClearBuffer(",{");
+ }
+ // End of selectors and start of declarations
+ elseif ($char === "{" && ($state === "T_DOCUMENT" || $state === "T_AT_MEDIA" || $state === "T_RULESET::SELECTORS"))
+ {
+ if ($this->parser->getBuffer() !== "")
+ {
+ $this->selectors[] = $this->parser->getAndClearBuffer(",{");
+ if ($state == "T_RULESET::SELECTORS")
+ {
+ $this->parser->popState();
+ }
+ $this->parser->pushState("T_RULESET");
+ $this->parser->appendToken(new CssRulesetStartToken($this->selectors));
+ $this->selectors = array();
+ }
+ }
+ // Start of declaration
+ elseif ($char === ":" && $state === "T_RULESET")
+ {
+ $this->parser->pushState("T_RULESET_DECLARATION");
+ $this->buffer = $this->parser->getAndClearBuffer(":;", true);
+ }
+ // Unterminated ruleset declaration
+ elseif ($char === ":" && $state === "T_RULESET_DECLARATION")
+ {
+ // Ignore Internet Explorer filter declarations
+ if ($this->buffer === "filter")
+ {
+ return false;
+ }
+ CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated declaration", $this->buffer . ":" . $this->parser->getBuffer() . "_"));
+ }
+ // End of declaration
+ elseif (($char === ";" || $char === "}") && $state === "T_RULESET_DECLARATION")
+ {
+ $value = $this->parser->getAndClearBuffer(";}");
+ if (strtolower(substr($value, -10, 10)) === "!important")
+ {
+ $value = trim(substr($value, 0, -10));
+ $isImportant = true;
+ }
+ else
+ {
+ $isImportant = false;
+ }
+ $this->parser->popState();
+ $this->parser->appendToken(new CssRulesetDeclarationToken($this->buffer, $value, $this->parser->getMediaTypes(), $isImportant));
+ // Declaration ends with a right curly brace; so we have to end the ruleset
+ if ($char === "}")
+ {
+ $this->parser->appendToken(new CssRulesetEndToken());
+ $this->parser->popState();
+ }
+ $this->buffer = "";
+ }
+ // End of ruleset
+ elseif ($char === "}" && $state === "T_RULESET")
+ {
+ $this->parser->popState();
+ $this->parser->clearBuffer();
+ $this->parser->appendToken(new CssRulesetEndToken());
+ $this->buffer = "";
+ $this->selectors = array();
+ }
+ else
+ {
+ return false;
+ }
+ return true;
+ }
+}
+
+/**
+ * This {@link aCssToken CSS token} represents the end of a ruleset.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssRulesetEndToken extends aCssRulesetEndToken
+{
+
+}
+
+/**
+ * This {@link aCssToken CSS token} represents a ruleset declaration.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssRulesetDeclarationToken extends aCssDeclarationToken
+{
+ /**
+ * Media types of the declaration.
+ *
+ * @var array
+ */
+ public $MediaTypes = array("all");
+ /**
+ * Set the properties of a ddocument- or at-rule @media level declaration.
+ *
+ * @param string $property Property of the declaration
+ * @param string $value Value of the declaration
+ * @param mixed $mediaTypes Media types of the declaration
+ * @param boolean $isImportant Is the !important flag is set
+ * @param boolean $isLast Is the declaration the last one of the ruleset
+ * @return void
+ */
+ public function __construct($property, $value, $mediaTypes = null, $isImportant = false, $isLast = false)
+ {
+ parent::__construct($property, $value, $isImportant, $isLast);
+ $this->MediaTypes = $mediaTypes ? $mediaTypes : array("all");
+ }
+}
+
+/**
+ * This {@link aCssMinifierFilter minifier filter} sets the IsLast property of any last declaration in a ruleset,
+ * @font-face at-rule or @page at-rule block. If the property IsLast is TRUE the decrations will get stringified
+ * without tailing semicolon.
+ *
+ * @package CssMin/Minifier/Filters
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssRemoveLastDelarationSemiColonMinifierFilter extends aCssMinifierFilter
+{
+ /**
+ * Implements {@link aCssMinifierFilter::filter()}.
+ *
+ * @param array $tokens Array of objects of type aCssToken
+ * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
+ */
+ public function apply(array &$tokens)
+ {
+ for ($i = 0, $l = count($tokens); $i < $l; $i++)
+ {
+ $current = get_class($tokens[$i]);
+ $next = isset($tokens[$i+1]) ? get_class($tokens[$i+1]) : false;
+ if (($current === "CssRulesetDeclarationToken" && $next === "CssRulesetEndToken") ||
+ ($current === "CssAtFontFaceDeclarationToken" && $next === "CssAtFontFaceEndToken") ||
+ ($current === "CssAtPageDeclarationToken" && $next === "CssAtPageEndToken"))
+ {
+ $tokens[$i]->IsLast = true;
+ }
+ }
+ return 0;
+ }
+}
+
+/**
+ * This {@link aCssMinifierFilter minifier filter} will remove any empty rulesets (including @keyframes at-rule block
+ * rulesets).
+ *
+ * @package CssMin/Minifier/Filters
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssRemoveEmptyRulesetsMinifierFilter extends aCssMinifierFilter
+{
+ /**
+ * Implements {@link aCssMinifierFilter::filter()}.
+ *
+ * @param array $tokens Array of objects of type aCssToken
+ * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
+ */
+ public function apply(array &$tokens)
+ {
+ $r = 0;
+ for ($i = 0, $l = count($tokens); $i < $l; $i++)
+ {
+ $current = get_class($tokens[$i]);
+ $next = isset($tokens[$i + 1]) ? get_class($tokens[$i + 1]) : false;
+ if (($current === "CssRulesetStartToken" && $next === "CssRulesetEndToken") ||
+ ($current === "CssAtKeyframesRulesetStartToken" && $next === "CssAtKeyframesRulesetEndToken" && !array_intersect(array("from", "0%", "to", "100%"), array_map("strtolower", $tokens[$i]->Selectors)))
+ )
+ {
+ $tokens[$i] = null;
+ $tokens[$i + 1] = null;
+ $i++;
+ $r = $r + 2;
+ }
+ }
+ return $r;
+ }
+}
+
+/**
+ * This {@link aCssMinifierFilter minifier filter} will remove any empty @font-face, @keyframes, @media and @page
+ * at-rule blocks.
+ *
+ * @package CssMin/Minifier/Filters
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssRemoveEmptyAtBlocksMinifierFilter extends aCssMinifierFilter
+{
+ /**
+ * Implements {@link aCssMinifierFilter::filter()}.
+ *
+ * @param array $tokens Array of objects of type aCssToken
+ * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
+ */
+ public function apply(array &$tokens)
+ {
+ $r = 0;
+ for ($i = 0, $l = count($tokens); $i < $l; $i++)
+ {
+ $current = get_class($tokens[$i]);
+ $next = isset($tokens[$i + 1]) ? get_class($tokens[$i + 1]) : false;
+ if (($current === "CssAtFontFaceStartToken" && $next === "CssAtFontFaceEndToken") ||
+ ($current === "CssAtKeyframesStartToken" && $next === "CssAtKeyframesEndToken") ||
+ ($current === "CssAtPageStartToken" && $next === "CssAtPageEndToken") ||
+ ($current === "CssAtMediaStartToken" && $next === "CssAtMediaEndToken"))
+ {
+ $tokens[$i] = null;
+ $tokens[$i + 1] = null;
+ $i++;
+ $r = $r + 2;
+ }
+ }
+ return $r;
+ }
+}
+
+/**
+ * This {@link aCssMinifierFilter minifier filter} will remove any comments from the array of parsed tokens.
+ *
+ * @package CssMin/Minifier/Filters
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssRemoveCommentsMinifierFilter extends aCssMinifierFilter
+{
+ /**
+ * Regular expression whitelisting any important comments to preserve.
+ *
+ * @var string
+ */
+ private $whitelistPattern = '/(^\/\*!|@preserve|copyright|license|author|https?:|www\.)/i';
+
+ /**
+ * Implements {@link aCssMinifierFilter::filter()}.
+ *
+ * @param array $tokens Array of objects of type aCssToken
+ * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
+ */
+ public function apply(array &$tokens)
+ {
+ $r = 0;
+ for ($i = 0, $l = count($tokens); $i < $l; $i++)
+ {
+ if (get_class($tokens[$i]) === "CssCommentToken")
+ {
+ if (!preg_match($this->whitelistPattern, $tokens[$i]->Comment))
+ {
+ $tokens[$i] = null;
+ $r++;
+ }
+ }
+ }
+ return $r;
+ }
+}
+
+/**
+ * CSS Parser.
+ *
+ * @package CssMin/Parser
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssParser
+{
+ /**
+ * Parse buffer.
+ *
+ * @var string
+ */
+ private $buffer = "";
+ /**
+ * {@link aCssParserPlugin Plugins}.
+ *
+ * @var array
+ */
+ private $plugins = array();
+ /**
+ * Source to parse.
+ *
+ * @var string
+ */
+ private $source = "";
+ /**
+ * Current state.
+ *
+ * @var integer
+ */
+ private $state = "T_DOCUMENT";
+ /**
+ * Exclusive state.
+ *
+ * @var string
+ */
+ private $stateExclusive = false;
+ /**
+ * Media types state.
+ *
+ * @var mixed
+ */
+ private $stateMediaTypes = false;
+ /**
+ * State stack.
+ *
+ * @var array
+ */
+ private $states = array("T_DOCUMENT");
+ /**
+ * Parsed tokens.
+ *
+ * @var array
+ */
+ private $tokens = array();
+ /**
+ * Constructer.
+ *
+ * Create instances of the used {@link aCssParserPlugin plugins}.
+ *
+ * @param string $source CSS source [optional]
+ * @param array $plugins Plugin configuration [optional]
+ * @return void
+ */
+ public function __construct($source = null, array $plugins = null)
+ {
+ $plugins = array_merge(array
+ (
+ "Comment" => true,
+ "String" => true,
+ "Url" => true,
+ "Expression" => true,
+ "Ruleset" => true,
+ "AtCharset" => true,
+ "AtFontFace" => true,
+ "AtImport" => true,
+ "AtKeyframes" => true,
+ "AtMedia" => true,
+ "AtPage" => true,
+ "AtVariables" => true
+ ), is_array($plugins) ? $plugins : array());
+ // Create plugin instances
+ foreach ($plugins as $name => $config)
+ {
+ if ($config !== false)
+ {
+ $class = "Css" . $name . "ParserPlugin";
+ $config = is_array($config) ? $config : array();
+ if (class_exists($class))
+ {
+ $this->plugins[] = new $class($this, $config);
+ }
+ else
+ {
+ CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": The plugin " . $name . "
with the class name " . $class . "
was not found"));
+ }
+ }
+ }
+ if (!is_null($source))
+ {
+ $this->parse($source);
+ }
+ }
+ /**
+ * Append a token to the array of tokens.
+ *
+ * @param aCssToken $token Token to append
+ * @return void
+ */
+ public function appendToken(aCssToken $token)
+ {
+ $this->tokens[] = $token;
+ }
+ /**
+ * Clears the current buffer.
+ *
+ * @return void
+ */
+ public function clearBuffer()
+ {
+ $this->buffer = "";
+ }
+ /**
+ * Returns and clear the current buffer.
+ *
+ * @param string $trim Chars to use to trim the returned buffer
+ * @param boolean $tolower if TRUE the returned buffer will get converted to lower case
+ * @return string
+ */
+ public function getAndClearBuffer($trim = "", $tolower = false)
+ {
+ $r = $this->getBuffer($trim, $tolower);
+ $this->buffer = "";
+ return $r;
+ }
+ /**
+ * Returns the current buffer.
+ *
+ * @param string $trim Chars to use to trim the returned buffer
+ * @param boolean $tolower if TRUE the returned buffer will get converted to lower case
+ * @return string
+ */
+ public function getBuffer($trim = "", $tolower = false)
+ {
+ $r = $this->buffer;
+ if ($trim)
+ {
+ $r = trim($r, " \t\n\r\0\x0B" . $trim);
+ }
+ if ($tolower)
+ {
+ $r = strtolower($r);
+ }
+ return $r;
+ }
+ /**
+ * Returns the current media types state.
+ *
+ * @return array
+ */
+ public function getMediaTypes()
+ {
+ return $this->stateMediaTypes;
+ }
+ /**
+ * Returns the CSS source.
+ *
+ * @return string
+ */
+ public function getSource()
+ {
+ return $this->source;
+ }
+ /**
+ * Returns the current state.
+ *
+ * @return integer The current state
+ */
+ public function getState()
+ {
+ return $this->state;
+ }
+ /**
+ * Returns a plugin by class name.
+ *
+ * @param string $name Class name of the plugin
+ * @return aCssParserPlugin
+ */
+ public function getPlugin($class)
+ {
+ static $index = null;
+ if (is_null($index))
+ {
+ $index = array();
+ for ($i = 0, $l = count($this->plugins); $i < $l; $i++)
+ {
+ $index[get_class($this->plugins[$i])] = $i;
+ }
+ }
+ return isset($index[$class]) ? $this->plugins[$index[$class]] : false;
+ }
+ /**
+ * Returns the parsed tokens.
+ *
+ * @return array
+ */
+ public function getTokens()
+ {
+ return $this->tokens;
+ }
+ /**
+ * Returns if the current state equals the passed state.
+ *
+ * @param integer $state State to compare with the current state
+ * @return boolean TRUE is the state equals to the passed state; FALSE if not
+ */
+ public function isState($state)
+ {
+ return ($this->state == $state);
+ }
+ /**
+ * Parse the CSS source and return a array with parsed tokens.
+ *
+ * @param string $source CSS source
+ * @return array Array with tokens
+ */
+ public function parse($source)
+ {
+ // Reset
+ $this->source = "";
+ $this->tokens = array();
+ // Create a global and plugin lookup table for trigger chars; set array of plugins as local variable and create
+ // several helper variables for plugin handling
+ $globalTriggerChars = "";
+ $plugins = $this->plugins;
+ $pluginCount = count($plugins);
+ $pluginIndex = array();
+ $pluginTriggerStates = array();
+ $pluginTriggerChars = array();
+ for ($i = 0, $l = count($plugins); $i < $l; $i++)
+ {
+ $tPluginClassName = get_class($plugins[$i]);
+ $pluginTriggerChars[$i] = implode("", $plugins[$i]->getTriggerChars());
+ $tPluginTriggerStates = $plugins[$i]->getTriggerStates();
+ $pluginTriggerStates[$i] = $tPluginTriggerStates === false ? false : "|" . implode("|", $tPluginTriggerStates) . "|";
+ $pluginIndex[$tPluginClassName] = $i;
+ for ($ii = 0, $ll = strlen($pluginTriggerChars[$i]); $ii < $ll; $ii++)
+ {
+ $c = substr($pluginTriggerChars[$i], $ii, 1);
+ if (strpos($globalTriggerChars, $c) === false)
+ {
+ $globalTriggerChars .= $c;
+ }
+ }
+ }
+ // Normalise line endings
+ $source = str_replace("\r\n", "\n", $source); // Windows to Unix line endings
+ $source = str_replace("\r", "\n", $source); // Mac to Unix line endings
+ $this->source = $source;
+ // Variables
+ $buffer = &$this->buffer;
+ $exclusive = &$this->stateExclusive;
+ $state = &$this->state;
+ $c = $p = null;
+ // --
+ for ($i = 0, $l = strlen($source); $i < $l; $i++)
+ {
+ // Set the current Char
+ $c = $source[$i]; // Is faster than: $c = substr($source, $i, 1);
+ // Normalize and filter double whitespace characters
+ if ($exclusive === false)
+ {
+ if ($c === "\n" || $c === "\t")
+ {
+ $c = " ";
+ }
+ if ($c === " " && $p === " ")
+ {
+ continue;
+ }
+ }
+ $buffer .= $c;
+
+ // Fix case when value of url() contains parentheses, for example: url("data: ... ()")
+ if ($this->getState() == 'T_URL' && $c == ')') {
+ $trimmedBuffer = trim($buffer);
+ if (preg_match('@url\((\s+)?".+@', $trimmedBuffer)
+ && !preg_match('@url\((\s+)?".+"\)@', $trimmedBuffer)
+ || preg_match('@url\((\s+)?\'.+@', $trimmedBuffer)
+ && !preg_match('@url\((\s+)?\'.+\'\)@', $trimmedBuffer)
+ ) {
+ $p = $c; // Set the parent char
+ continue;
+ }
+ }
+
+ // Extended processing only if the current char is a global trigger char
+ if (strpos($globalTriggerChars, $c) !== false)
+ {
+ // Exclusive state is set; process with the exclusive plugin
+ if ($exclusive)
+ {
+ $tPluginIndex = $pluginIndex[$exclusive];
+ if (strpos($pluginTriggerChars[$tPluginIndex], $c) !== false && ($pluginTriggerStates[$tPluginIndex] === false || strpos($pluginTriggerStates[$tPluginIndex], $state) !== false))
+ {
+ $r = $plugins[$tPluginIndex]->parse($i, $c, $p, $state);
+ // Return value is TRUE => continue with next char
+ if ($r === true)
+ {
+ continue;
+ }
+ // Return value is numeric => set new index and continue with next char
+ elseif ($r !== false && $r != $i)
+ {
+ $i = $r;
+ continue;
+ }
+ }
+ }
+ // Else iterate through the plugins
+ else
+ {
+ $triggerState = "|" . $state . "|";
+ for ($ii = 0, $ll = $pluginCount; $ii < $ll; $ii++)
+ {
+ // Only process if the current char is one of the plugin trigger chars
+ if (strpos($pluginTriggerChars[$ii], $c) !== false && ($pluginTriggerStates[$ii] === false || strpos($pluginTriggerStates[$ii], $triggerState) !== false))
+ {
+ // Process with the plugin
+ $r = $plugins[$ii]->parse($i, $c, $p, $state);
+ // Return value is TRUE => break the plugin loop and and continue with next char
+ if ($r === true)
+ {
+ break;
+ }
+ // Return value is numeric => set new index, break the plugin loop and and continue with next char
+ elseif ($r !== false && $r != $i)
+ {
+ $i = $r;
+ break;
+ }
+ }
+ }
+ }
+ }
+ $p = $c; // Set the parent char
+ }
+ return $this->tokens;
+ }
+ /**
+ * Remove the last state of the state stack and return the removed stack value.
+ *
+ * @return integer Removed state value
+ */
+ public function popState()
+ {
+ $r = array_pop($this->states);
+ $this->state = $this->states[count($this->states) - 1];
+ return $r;
+ }
+ /**
+ * Adds a new state onto the state stack.
+ *
+ * @param integer $state State to add onto the state stack.
+ * @return integer The index of the added state in the state stacks
+ */
+ public function pushState($state)
+ {
+ $r = array_push($this->states, $state);
+ $this->state = $this->states[count($this->states) - 1];
+ return $r;
+ }
+ /**
+ * Sets/restores the buffer.
+ *
+ * @param string $buffer Buffer to set
+ * @return void
+ */
+ public function setBuffer($buffer)
+ {
+ $this->buffer = $buffer;
+ }
+ /**
+ * Set the exclusive state.
+ *
+ * @param string $exclusive Exclusive state
+ * @return void
+ */
+ public function setExclusive($exclusive)
+ {
+ $this->stateExclusive = $exclusive;
+ }
+ /**
+ * Set the media types state.
+ *
+ * @param array $mediaTypes Media types state
+ * @return void
+ */
+ public function setMediaTypes(array $mediaTypes)
+ {
+ $this->stateMediaTypes = $mediaTypes;
+ }
+ /**
+ * Sets the current state in the state stack; equals to {@link CssParser::popState()} + {@link CssParser::pushState()}.
+ *
+ * @param integer $state State to set
+ * @return integer
+ */
+ public function setState($state)
+ {
+ $r = array_pop($this->states);
+ array_push($this->states, $state);
+ $this->state = $this->states[count($this->states) - 1];
+ return $r;
+ }
+ /**
+ * Removes the exclusive state.
+ *
+ * @return void
+ */
+ public function unsetExclusive()
+ {
+ $this->stateExclusive = false;
+ }
+ /**
+ * Removes the media types state.
+ *
+ * @return void
+ */
+ public function unsetMediaTypes()
+ {
+ $this->stateMediaTypes = false;
+ }
+}
+
+/**
+ * {@link aCssFromatter Formatter} returning the CSS source in {@link http://goo.gl/j4XdU OTBS indent style} (The One True Brace Style).
+ *
+ * @package CssMin/Formatter
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssOtbsFormatter extends aCssFormatter
+{
+ /**
+ * Implements {@link aCssFormatter::__toString()}.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ $r = array();
+ $level = 0;
+ for ($i = 0, $l = count($this->tokens); $i < $l; $i++)
+ {
+ $token = $this->tokens[$i];
+ $class = get_class($token);
+ $indent = str_repeat($this->indent, $level);
+ if ($class === "CssCommentToken")
+ {
+ $lines = array_map("trim", explode("\n", $token->Comment));
+ for ($ii = 0, $ll = count($lines); $ii < $ll; $ii++)
+ {
+ $r[] = $indent . (substr($lines[$ii], 0, 1) == "*" ? " " : "") . $lines[$ii];
+ }
+ }
+ elseif ($class === "CssAtCharsetToken")
+ {
+ $r[] = $indent . "@charset " . $token->Charset . ";";
+ }
+ elseif ($class === "CssAtFontFaceStartToken")
+ {
+ $r[] = $indent . "@font-face {";
+ $level++;
+ }
+ elseif ($class === "CssAtImportToken")
+ {
+ $r[] = $indent . "@import " . $token->Import . " " . implode(", ", $token->MediaTypes) . ";";
+ }
+ elseif ($class === "CssAtKeyframesStartToken")
+ {
+ $r[] = $indent . "@keyframes " . $token->Name . " {";
+ $level++;
+ }
+ elseif ($class === "CssAtMediaStartToken")
+ {
+ $r[] = $indent . "@media " . implode(", ", $token->MediaTypes) . " {";
+ $level++;
+ }
+ elseif ($class === "CssAtPageStartToken")
+ {
+ $r[] = $indent . "@page {";
+ $level++;
+ }
+ elseif ($class === "CssAtVariablesStartToken")
+ {
+ $r[] = $indent . "@variables " . implode(", ", $token->MediaTypes) . " {";
+ $level++;
+ }
+ elseif ($class === "CssRulesetStartToken" || $class === "CssAtKeyframesRulesetStartToken")
+ {
+ $r[] = $indent . implode(", ", $token->Selectors) . " {";
+ $level++;
+ }
+ elseif ($class === "CssAtFontFaceDeclarationToken"
+ || $class === "CssAtKeyframesRulesetDeclarationToken"
+ || $class === "CssAtPageDeclarationToken"
+ || $class === "CssAtVariablesDeclarationToken"
+ || $class === "CssRulesetDeclarationToken"
+ )
+ {
+ $declaration = $indent . $token->Property . ": ";
+ if ($this->padding)
+ {
+ $declaration = str_pad($declaration, $this->padding, " ", STR_PAD_RIGHT);
+ }
+ $r[] = $declaration . $token->Value . ($token->IsImportant ? " !important" : "") . ";";
+ }
+ elseif ($class === "CssAtFontFaceEndToken"
+ || $class === "CssAtMediaEndToken"
+ || $class === "CssAtKeyframesEndToken"
+ || $class === "CssAtKeyframesRulesetEndToken"
+ || $class === "CssAtPageEndToken"
+ || $class === "CssAtVariablesEndToken"
+ || $class === "CssRulesetEndToken"
+ )
+ {
+ $level--;
+ $r[] = str_repeat($indent, $level) . "}";
+ }
+ }
+ return implode("\n", $r);
+ }
+}
+
+/**
+ * This {@link aCssToken CSS token} is a utility token that extends {@link aNullToken} and returns only a empty string.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssNullToken extends aCssToken
+{
+ /**
+ * Implements {@link aCssToken::__toString()}.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return "";
+ }
+}
+
+/**
+ * CSS Minifier.
+ *
+ * @package CssMin/Minifier
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssMinifier
+{
+ /**
+ * {@link aCssMinifierFilter Filters}.
+ *
+ * @var array
+ */
+ private $filters = array();
+ /**
+ * {@link aCssMinifierPlugin Plugins}.
+ *
+ * @var array
+ */
+ private $plugins = array();
+ /**
+ * Minified source.
+ *
+ * @var string
+ */
+ private $minified = "";
+ /**
+ * Constructer.
+ *
+ * Creates instances of {@link aCssMinifierFilter filters} and {@link aCssMinifierPlugin plugins}.
+ *
+ * @param string $source CSS source [optional]
+ * @param array $filters Filter configuration [optional]
+ * @param array $plugins Plugin configuration [optional]
+ * @return void
+ */
+ public function __construct($source = null, array $filters = null, array $plugins = null)
+ {
+ $filters = array_merge(array
+ (
+ "ImportImports" => false,
+ "RemoveComments" => true,
+ "RemoveEmptyRulesets" => true,
+ "RemoveEmptyAtBlocks" => true,
+ "ConvertLevel3Properties" => false,
+ "ConvertLevel3AtKeyframes" => false,
+ "Variables" => true,
+ "RemoveLastDelarationSemiColon" => true
+ ), is_array($filters) ? $filters : array());
+ $plugins = array_merge(array
+ (
+ "Variables" => true,
+ "ConvertFontWeight" => false,
+ "ConvertHslColors" => false,
+ "ConvertRgbColors" => false,
+ "ConvertNamedColors" => false,
+ "CompressColorValues" => false,
+ "CompressUnitValues" => false,
+ "CompressExpressionValues" => false
+ ), is_array($plugins) ? $plugins : array());
+ // Filters
+ foreach ($filters as $name => $config)
+ {
+ if ($config !== false)
+ {
+ $class = "Css" . $name . "MinifierFilter";
+ $config = is_array($config) ? $config : array();
+ if (class_exists($class))
+ {
+ $this->filters[] = new $class($this, $config);
+ }
+ else
+ {
+ CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": The filter " . $name . "
with the class name " . $class . "
was not found"));
+ }
+ }
+ }
+ // Plugins
+ foreach ($plugins as $name => $config)
+ {
+ if ($config !== false)
+ {
+ $class = "Css" . $name . "MinifierPlugin";
+ $config = is_array($config) ? $config : array();
+ if (class_exists($class))
+ {
+ $this->plugins[] = new $class($this, $config);
+ }
+ else
+ {
+ CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": The plugin " . $name . "
with the class name " . $class . "
was not found"));
+ }
+ }
+ }
+ // --
+ if (!is_null($source))
+ {
+ $this->minify($source);
+ }
+ }
+ /**
+ * Returns the minified Source.
+ *
+ * @return string
+ */
+ public function getMinified()
+ {
+ return $this->minified;
+ }
+ /**
+ * Returns a plugin by class name.
+ *
+ * @param string $name Class name of the plugin
+ * @return aCssMinifierPlugin
+ */
+ public function getPlugin($class)
+ {
+ static $index = null;
+ if (is_null($index))
+ {
+ $index = array();
+ for ($i = 0, $l = count($this->plugins); $i < $l; $i++)
+ {
+ $index[get_class($this->plugins[$i])] = $i;
+ }
+ }
+ return isset($index[$class]) ? $this->plugins[$index[$class]] : false;
+ }
+ /**
+ * Minifies the CSS source.
+ *
+ * @param string $source CSS source
+ * @return string
+ */
+ public function minify($source)
+ {
+ // Variables
+ $r = "";
+ $parser = new CssParser($source);
+ $tokens = $parser->getTokens();
+ $filters = $this->filters;
+ $filterCount = count($this->filters);
+ $plugins = $this->plugins;
+ $pluginCount = count($plugins);
+ $pluginIndex = array();
+ $pluginTriggerTokens = array();
+ $globalTriggerTokens = array();
+ for ($i = 0, $l = count($plugins); $i < $l; $i++)
+ {
+ $tPluginClassName = get_class($plugins[$i]);
+ $pluginTriggerTokens[$i] = $plugins[$i]->getTriggerTokens();
+ foreach ($pluginTriggerTokens[$i] as $v)
+ {
+ if (!in_array($v, $globalTriggerTokens))
+ {
+ $globalTriggerTokens[] = $v;
+ }
+ }
+ $pluginTriggerTokens[$i] = "|" . implode("|", $pluginTriggerTokens[$i]) . "|";
+ $pluginIndex[$tPluginClassName] = $i;
+ }
+ $globalTriggerTokens = "|" . implode("|", $globalTriggerTokens) . "|";
+ /*
+ * Apply filters
+ */
+ for($i = 0; $i < $filterCount; $i++)
+ {
+ // Apply the filter; if the return value is larger than 0...
+ if ($filters[$i]->apply($tokens) > 0)
+ {
+ // ...then filter null values and rebuild the token array
+ $tokens = array_values(array_filter($tokens));
+ }
+ }
+ $tokenCount = count($tokens);
+ /*
+ * Apply plugins
+ */
+ for($i = 0; $i < $tokenCount; $i++)
+ {
+ $triggerToken = "|" . get_class($tokens[$i]) . "|";
+ if (strpos($globalTriggerTokens, $triggerToken) !== false)
+ {
+ for($ii = 0; $ii < $pluginCount; $ii++)
+ {
+ if (strpos($pluginTriggerTokens[$ii], $triggerToken) !== false || $pluginTriggerTokens[$ii] === false)
+ {
+ // Apply the plugin; if the return value is TRUE continue to the next token
+ if ($plugins[$ii]->apply($tokens[$i]) === true)
+ {
+ continue 2;
+ }
+ }
+ }
+ }
+ }
+ // Stringify the tokens
+ for($i = 0; $i < $tokenCount; $i++)
+ {
+ $r .= (string) $tokens[$i];
+ }
+ $this->minified = $r;
+ return $r;
+ }
+}
+
+/**
+ * CssMin - A (simple) css minifier with benefits
+ *
+ * --
+ * Copyright (c) 2011 Joe Scylla
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ * --
+ *
+ * @package CssMin
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssMin
+{
+ /**
+ * Index of classes
+ *
+ * @var array
+ */
+ private static $classIndex = array();
+ /**
+ * Parse/minify errors
+ *
+ * @var array
+ */
+ private static $errors = array();
+ /**
+ * Verbose output.
+ *
+ * @var boolean
+ */
+ private static $isVerbose = false;
+ /**
+ * {@link http://goo.gl/JrW54 Autoload} function of CssMin.
+ *
+ * @param string $class Name of the class
+ * @return void
+ */
+ public static function autoload($class)
+ {
+ if (isset(self::$classIndex[$class]))
+ {
+ require(self::$classIndex[$class]);
+ }
+ }
+ /**
+ * Return errors
+ *
+ * @return array of {CssError}.
+ */
+ public static function getErrors()
+ {
+ return self::$errors;
+ }
+ /**
+ * Returns if there were errors.
+ *
+ * @return boolean
+ */
+ public static function hasErrors()
+ {
+ return count(self::$errors) > 0;
+ }
+ /**
+ * Initialises CssMin.
+ *
+ * @return void
+ */
+ public static function initialise()
+ {
+ // Create the class index for autoloading or including
+ $paths = array(dirname(__FILE__));
+ for ($i = 0; $i < count($paths); $i++)
+ {
+ $path = $paths[$i];
+ $subDirectorys = glob($path . "*", GLOB_MARK | GLOB_ONLYDIR | GLOB_NOSORT);
+ if (is_array($subDirectorys))
+ {
+ foreach ($subDirectorys as $subDirectory)
+ {
+ $paths[] = $subDirectory;
+ }
+ }
+ $files = glob($path . "*.php", 0);
+ if (is_array($files))
+ {
+ foreach ($files as $file)
+ {
+ $class = substr(basename($file), 0, -4);
+ self::$classIndex[$class] = $file;
+ }
+ }
+ }
+ krsort(self::$classIndex);
+ // Only use autoloading if spl_autoload_register() is available and no __autoload() is defined (because
+ // __autoload() breaks if spl_autoload_register() is used.
+ if (function_exists("spl_autoload_register") && !is_callable("__autoload"))
+ {
+ spl_autoload_register(array(__CLASS__, "autoload"));
+ }
+ // Otherwise include all class files
+ else
+ {
+ foreach (self::$classIndex as $class => $file)
+ {
+ if (!class_exists($class))
+ {
+ require_once($file);
+ }
+ }
+ }
+ }
+ /**
+ * Minifies CSS source.
+ *
+ * @param string $source CSS source
+ * @param array $filters Filter configuration [optional]
+ * @param array $plugins Plugin configuration [optional]
+ * @return string Minified CSS
+ */
+ public static function minify($source, array $filters = null, array $plugins = null)
+ {
+ self::$errors = array();
+ $minifier = new CssMinifier($source, $filters, $plugins);
+ return $minifier->getMinified();
+ }
+ /**
+ * Parse the CSS source.
+ *
+ * @param string $source CSS source
+ * @param array $plugins Plugin configuration [optional]
+ * @return array Array of aCssToken
+ */
+ public static function parse($source, array $plugins = null)
+ {
+ self::$errors = array();
+ $parser = new CssParser($source, $plugins);
+ return $parser->getTokens();
+ }
+ /**
+ * --
+ *
+ * @param boolean $to
+ * @return boolean
+ */
+ public static function setVerbose($to)
+ {
+ self::$isVerbose = (boolean) $to;
+ return self::$isVerbose;
+ }
+ /**
+ * --
+ *
+ * @param CssError $error
+ * @return void
+ */
+ public static function triggerError(CssError $error)
+ {
+ self::$errors[] = $error;
+ if (self::$isVerbose)
+ {
+ trigger_error((string) $error, E_USER_WARNING);
+ }
+ }
+}
+// Initialises CssMin
+CssMin::initialise();
+
+/**
+ * This {@link aCssMinifierFilter minifier filter} import external css files defined with the @import at-rule into the
+ * current stylesheet.
+ *
+ * @package CssMin/Minifier/Filters
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssImportImportsMinifierFilter extends aCssMinifierFilter
+{
+ /**
+ * Array with already imported external stylesheets.
+ *
+ * @var array
+ */
+ private $imported = array();
+ /**
+ * Implements {@link aCssMinifierFilter::filter()}.
+ *
+ * @param array $tokens Array of objects of type aCssToken
+ * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
+ */
+ public function apply(array &$tokens)
+ {
+ if (!isset($this->configuration["BasePath"]) || !is_dir($this->configuration["BasePath"]))
+ {
+ CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Base path " . ($this->configuration["BasePath"] ? $this->configuration["BasePath"] : "null"). "
is not a directory"));
+ return 0;
+ }
+ for ($i = 0, $l = count($tokens); $i < $l; $i++)
+ {
+ if (get_class($tokens[$i]) === "CssAtImportToken")
+ {
+ $import = $this->configuration["BasePath"] . "/" . $tokens[$i]->Import;
+ // Import file was not found/is not a file
+ if (!is_file($import))
+ {
+ CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Import file " . $import. "
was not found.", (string) $tokens[$i]));
+ }
+ // Import file already imported; remove this @import at-rule to prevent recursions
+ elseif (in_array($import, $this->imported))
+ {
+ CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Import file " . $import. "
was already imported.", (string) $tokens[$i]));
+ $tokens[$i] = null;
+ }
+ else
+ {
+ $this->imported[] = $import;
+ $parser = new CssParser(file_get_contents($import));
+ $import = $parser->getTokens();
+ // The @import at-rule has media types defined requiring special handling
+ if (count($tokens[$i]->MediaTypes) > 0 && !(count($tokens[$i]->MediaTypes) == 1 && $tokens[$i]->MediaTypes[0] == "all"))
+ {
+ $blocks = array();
+ /*
+ * Filter or set media types of @import at-rule or remove the @import at-rule if no media type is matching the parent @import at-rule
+ */
+ for($ii = 0, $ll = count($import); $ii < $ll; $ii++)
+ {
+ if (get_class($import[$ii]) === "CssAtImportToken")
+ {
+ // @import at-rule defines no media type or only the "all" media type; set the media types to the one defined in the parent @import at-rule
+ if (count($import[$ii]->MediaTypes) == 0 || (count($import[$ii]->MediaTypes) == 1 && $import[$ii]->MediaTypes[0] == "all"))
+ {
+ $import[$ii]->MediaTypes = $tokens[$i]->MediaTypes;
+ }
+ // @import at-rule defineds one or more media types; filter out media types not matching with the parent @import at-rule
+ elseif (count($import[$ii]->MediaTypes) > 0)
+ {
+ foreach ($import[$ii]->MediaTypes as $index => $mediaType)
+ {
+ if (!in_array($mediaType, $tokens[$i]->MediaTypes))
+ {
+ unset($import[$ii]->MediaTypes[$index]);
+ }
+ }
+ $import[$ii]->MediaTypes = array_values($import[$ii]->MediaTypes);
+ // If there are no media types left in the @import at-rule remove the @import at-rule
+ if (count($import[$ii]->MediaTypes) == 0)
+ {
+ $import[$ii] = null;
+ }
+ }
+ }
+ }
+ /*
+ * Remove media types of @media at-rule block not defined in the @import at-rule
+ */
+ for($ii = 0, $ll = count($import); $ii < $ll; $ii++)
+ {
+ if (get_class($import[$ii]) === "CssAtMediaStartToken")
+ {
+ foreach ($import[$ii]->MediaTypes as $index => $mediaType)
+ {
+ if (!in_array($mediaType, $tokens[$i]->MediaTypes))
+ {
+ unset($import[$ii]->MediaTypes[$index]);
+ }
+ $import[$ii]->MediaTypes = array_values($import[$ii]->MediaTypes);
+ }
+ }
+ }
+ /*
+ * If no media types left of the @media at-rule block remove the complete block
+ */
+ for($ii = 0, $ll = count($import); $ii < $ll; $ii++)
+ {
+ if (get_class($import[$ii]) === "CssAtMediaStartToken")
+ {
+ if (count($import[$ii]->MediaTypes) === 0)
+ {
+ for ($iii = $ii; $iii < $ll; $iii++)
+ {
+ if (get_class($import[$iii]) === "CssAtMediaEndToken")
+ {
+ break;
+ }
+ }
+ if (get_class($import[$iii]) === "CssAtMediaEndToken")
+ {
+ array_splice($import, $ii, $iii - $ii + 1, array());
+ $ll = count($import);
+ }
+ }
+ }
+ }
+ /*
+ * If the media types of the @media at-rule equals the media types defined in the @import
+ * at-rule remove the CssAtMediaStartToken and CssAtMediaEndToken token
+ */
+ for($ii = 0, $ll = count($import); $ii < $ll; $ii++)
+ {
+ if (get_class($import[$ii]) === "CssAtMediaStartToken" && count(array_diff($tokens[$i]->MediaTypes, $import[$ii]->MediaTypes)) === 0)
+ {
+ for ($iii = $ii; $iii < $ll; $iii++)
+ {
+ if (get_class($import[$iii]) == "CssAtMediaEndToken")
+ {
+ break;
+ }
+ }
+ if (get_class($import[$iii]) == "CssAtMediaEndToken")
+ {
+ unset($import[$ii]);
+ unset($import[$iii]);
+ $import = array_values($import);
+ $ll = count($import);
+ }
+ }
+ }
+ /**
+ * Extract CssAtImportToken and CssAtCharsetToken tokens
+ */
+ for($ii = 0, $ll = count($import); $ii < $ll; $ii++)
+ {
+ $class = get_class($import[$ii]);
+ if ($class === "CssAtImportToken" || $class === "CssAtCharsetToken")
+ {
+ $blocks = array_merge($blocks, array_splice($import, $ii, 1, array()));
+ $ll = count($import);
+ }
+ }
+ /*
+ * Extract the @font-face, @media and @page at-rule block
+ */
+ for($ii = 0, $ll = count($import); $ii < $ll; $ii++)
+ {
+ $class = get_class($import[$ii]);
+ if ($class === "CssAtFontFaceStartToken" || $class === "CssAtMediaStartToken" || $class === "CssAtPageStartToken" || $class === "CssAtVariablesStartToken")
+ {
+ for ($iii = $ii; $iii < $ll; $iii++)
+ {
+ $class = get_class($import[$iii]);
+ if ($class === "CssAtFontFaceEndToken" || $class === "CssAtMediaEndToken" || $class === "CssAtPageEndToken" || $class === "CssAtVariablesEndToken")
+ {
+ break;
+ }
+ }
+ $class = get_class($import[$iii]);
+ if (isset($import[$iii]) && ($class === "CssAtFontFaceEndToken" || $class === "CssAtMediaEndToken" || $class === "CssAtPageEndToken" || $class === "CssAtVariablesEndToken"))
+ {
+ $blocks = array_merge($blocks, array_splice($import, $ii, $iii - $ii + 1, array()));
+ $ll = count($import);
+ }
+ }
+ }
+ // Create the import array with extracted tokens and the rulesets wrapped into a @media at-rule block
+ $import = array_merge($blocks, array(new CssAtMediaStartToken($tokens[$i]->MediaTypes)), $import, array(new CssAtMediaEndToken()));
+ }
+ // Insert the imported tokens
+ array_splice($tokens, $i, 1, $import);
+ // Modify parameters of the for-loop
+ $i--;
+ $l = count($tokens);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * {@link aCssParserPlugin Parser plugin} for preserve parsing expression() declaration values.
+ *
+ * This plugin return no {@link aCssToken CssToken} but ensures that expression() declaration values will get parsed
+ * properly.
+ *
+ * @package CssMin/Parser/Plugins
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssExpressionParserPlugin extends aCssParserPlugin
+{
+ /**
+ * Count of left braces.
+ *
+ * @var integer
+ */
+ private $leftBraces = 0;
+ /**
+ * Count of right braces.
+ *
+ * @var integer
+ */
+ private $rightBraces = 0;
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerChars()}.
+ *
+ * @return array
+ */
+ public function getTriggerChars()
+ {
+ return array("(", ")", ";", "}");
+ }
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerStates()}.
+ *
+ * @return array
+ */
+ public function getTriggerStates()
+ {
+ return false;
+ }
+ /**
+ * Implements {@link aCssParserPlugin::parse()}.
+ *
+ * @param integer $index Current index
+ * @param string $char Current char
+ * @param string $previousChar Previous char
+ * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
+ */
+ public function parse($index, $char, $previousChar, $state)
+ {
+ // Start of expression
+ if ($char === "(" && strtolower(substr($this->parser->getSource(), $index - 10, 11)) === "expression(" && $state !== "T_EXPRESSION")
+ {
+ $this->parser->pushState("T_EXPRESSION");
+ $this->leftBraces++;
+ }
+ // Count left braces
+ elseif ($char === "(" && $state === "T_EXPRESSION")
+ {
+ $this->leftBraces++;
+ }
+ // Count right braces
+ elseif ($char === ")" && $state === "T_EXPRESSION")
+ {
+ $this->rightBraces++;
+ }
+ // Possible end of expression; if left and right braces are equal the expressen ends
+ elseif (($char === ";" || $char === "}") && $state === "T_EXPRESSION" && $this->leftBraces === $this->rightBraces)
+ {
+ $this->leftBraces = $this->rightBraces = 0;
+ $this->parser->popState();
+ return $index - 1;
+ }
+ else
+ {
+ return false;
+ }
+ return true;
+ }
+}
+
+/**
+ * CSS Error.
+ *
+ * @package CssMin
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssError
+{
+ /**
+ * File.
+ *
+ * @var string
+ */
+ public $File = "";
+ /**
+ * Line.
+ *
+ * @var integer
+ */
+ public $Line = 0;
+ /**
+ * Error message.
+ *
+ * @var string
+ */
+ public $Message = "";
+ /**
+ * Source.
+ *
+ * @var string
+ */
+ public $Source = "";
+ /**
+ * Constructor triggering the error.
+ *
+ * @param string $message Error message
+ * @param string $source Corresponding line [optional]
+ * @return void
+ */
+ public function __construct($file, $line, $message, $source = "")
+ {
+ $this->File = $file;
+ $this->Line = $line;
+ $this->Message = $message;
+ $this->Source = $source;
+ }
+ /**
+ * Returns the error as formatted string.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->Message . ($this->Source ? ": " . $this->Source . "
": "") . " in file " . $this->File . " at line " . $this->Line;
+ }
+}
+
+/**
+ * This {@link aCssMinifierPlugin} will convert a color value in rgb notation to hexadecimal notation.
+ *
+ * Example:
+ *
+ * color: rgb(200,60%,5);
+ *
+ *
+ * Will get converted to:
+ *
+ * color:#c89905;
+ *
+ *
+ * @package CssMin/Minifier/Plugins
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssConvertRgbColorsMinifierPlugin extends aCssMinifierPlugin
+{
+ /**
+ * Regular expression matching the value.
+ *
+ * @var string
+ */
+ private $reMatch = "/rgb\s*\(\s*([0-9%]+)\s*,\s*([0-9%]+)\s*,\s*([0-9%]+)\s*\)/iS";
+ /**
+ * Implements {@link aCssMinifierPlugin::minify()}.
+ *
+ * @param aCssToken $token Token to process
+ * @return boolean Return TRUE to break the processing of this token; FALSE to continue
+ */
+ public function apply(aCssToken &$token)
+ {
+ if (stripos($token->Value, "rgb") !== false && preg_match($this->reMatch, $token->Value, $m))
+ {
+ for ($i = 1, $l = count($m); $i < $l; $i++)
+ {
+ if (strpos("%", $m[$i]) !== false)
+ {
+ $m[$i] = substr($m[$i], 0, -1);
+ $m[$i] = (int) (256 * ($m[$i] / 100));
+ }
+ $m[$i] = str_pad(dechex($m[$i]), 2, "0", STR_PAD_LEFT);
+ }
+ $token->Value = str_replace($m[0], "#" . $m[1] . $m[2] . $m[3], $token->Value);
+ }
+ return false;
+ }
+ /**
+ * Implements {@link aMinifierPlugin::getTriggerTokens()}
+ *
+ * @return array
+ */
+ public function getTriggerTokens()
+ {
+ return array
+ (
+ "CssAtFontFaceDeclarationToken",
+ "CssAtPageDeclarationToken",
+ "CssRulesetDeclarationToken"
+ );
+ }
+}
+
+/**
+ * This {@link aCssMinifierPlugin} will convert named color values to hexadecimal notation.
+ *
+ * Example:
+ *
+ * color: black;
+ * border: 1px solid indigo;
+ *
+ *
+ * Will get converted to:
+ *
+ * color:#000;
+ * border:1px solid #4b0082;
+ *
+ *
+ * @package CssMin/Minifier/Plugins
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssConvertNamedColorsMinifierPlugin extends aCssMinifierPlugin
+{
+
+ /**
+ * Regular expression matching the value.
+ *
+ * @var string
+ */
+ private $reMatch = null;
+ /**
+ * Transformation table used by the {@link CssConvertNamedColorsMinifierPlugin::reReplace() replacement method}.
+ *
+ * @var array
+ */
+ private $transformation = array
+ (
+ "aliceblue" => "#f0f8ff",
+ "antiquewhite" => "#faebd7",
+ "aqua" => "#0ff",
+ "aquamarine" => "#7fffd4",
+ "azure" => "#f0ffff",
+ "beige" => "#f5f5dc",
+ "black" => "#000",
+ "blue" => "#00f",
+ "blueviolet" => "#8a2be2",
+ "brown" => "#a52a2a",
+ "burlywood" => "#deb887",
+ "cadetblue" => "#5f9ea0",
+ "chartreuse" => "#7fff00",
+ "chocolate" => "#d2691e",
+ "coral" => "#ff7f50",
+ "cornflowerblue" => "#6495ed",
+ "cornsilk" => "#fff8dc",
+ "crimson" => "#dc143c",
+ "darkblue" => "#00008b",
+ "darkcyan" => "#008b8b",
+ "darkgoldenrod" => "#b8860b",
+ "darkgray" => "#a9a9a9",
+ "darkgreen" => "#006400",
+ "darkkhaki" => "#bdb76b",
+ "darkmagenta" => "#8b008b",
+ "darkolivegreen" => "#556b2f",
+ "darkorange" => "#ff8c00",
+ "darkorchid" => "#9932cc",
+ "darkred" => "#8b0000",
+ "darksalmon" => "#e9967a",
+ "darkseagreen" => "#8fbc8f",
+ "darkslateblue" => "#483d8b",
+ "darkslategray" => "#2f4f4f",
+ "darkturquoise" => "#00ced1",
+ "darkviolet" => "#9400d3",
+ "deeppink" => "#ff1493",
+ "deepskyblue" => "#00bfff",
+ "dimgray" => "#696969",
+ "dodgerblue" => "#1e90ff",
+ "firebrick" => "#b22222",
+ "floralwhite" => "#fffaf0",
+ "forestgreen" => "#228b22",
+ "fuchsia" => "#f0f",
+ "gainsboro" => "#dcdcdc",
+ "ghostwhite" => "#f8f8ff",
+ "gold" => "#ffd700",
+ "goldenrod" => "#daa520",
+ "gray" => "#808080",
+ "green" => "#008000",
+ "greenyellow" => "#adff2f",
+ "honeydew" => "#f0fff0",
+ "hotpink" => "#ff69b4",
+ "indianred" => "#cd5c5c",
+ "indigo" => "#4b0082",
+ "ivory" => "#fffff0",
+ "khaki" => "#f0e68c",
+ "lavender" => "#e6e6fa",
+ "lavenderblush" => "#fff0f5",
+ "lawngreen" => "#7cfc00",
+ "lemonchiffon" => "#fffacd",
+ "lightblue" => "#add8e6",
+ "lightcoral" => "#f08080",
+ "lightcyan" => "#e0ffff",
+ "lightgoldenrodyellow" => "#fafad2",
+ "lightgreen" => "#90ee90",
+ "lightgrey" => "#d3d3d3",
+ "lightpink" => "#ffb6c1",
+ "lightsalmon" => "#ffa07a",
+ "lightseagreen" => "#20b2aa",
+ "lightskyblue" => "#87cefa",
+ "lightslategray" => "#789",
+ "lightsteelblue" => "#b0c4de",
+ "lightyellow" => "#ffffe0",
+ "lime" => "#0f0",
+ "limegreen" => "#32cd32",
+ "linen" => "#faf0e6",
+ "maroon" => "#800000",
+ "mediumaquamarine" => "#66cdaa",
+ "mediumblue" => "#0000cd",
+ "mediumorchid" => "#ba55d3",
+ "mediumpurple" => "#9370db",
+ "mediumseagreen" => "#3cb371",
+ "mediumslateblue" => "#7b68ee",
+ "mediumspringgreen" => "#00fa9a",
+ "mediumturquoise" => "#48d1cc",
+ "mediumvioletred" => "#c71585",
+ "midnightblue" => "#191970",
+ "mintcream" => "#f5fffa",
+ "mistyrose" => "#ffe4e1",
+ "moccasin" => "#ffe4b5",
+ "navajowhite" => "#ffdead",
+ "navy" => "#000080",
+ "oldlace" => "#fdf5e6",
+ "olive" => "#808000",
+ "olivedrab" => "#6b8e23",
+ "orange" => "#ffa500",
+ "orangered" => "#ff4500",
+ "orchid" => "#da70d6",
+ "palegoldenrod" => "#eee8aa",
+ "palegreen" => "#98fb98",
+ "paleturquoise" => "#afeeee",
+ "palevioletred" => "#db7093",
+ "papayawhip" => "#ffefd5",
+ "peachpuff" => "#ffdab9",
+ "peru" => "#cd853f",
+ "pink" => "#ffc0cb",
+ "plum" => "#dda0dd",
+ "powderblue" => "#b0e0e6",
+ "purple" => "#800080",
+ "red" => "#f00",
+ "rosybrown" => "#bc8f8f",
+ "royalblue" => "#4169e1",
+ "saddlebrown" => "#8b4513",
+ "salmon" => "#fa8072",
+ "sandybrown" => "#f4a460",
+ "seagreen" => "#2e8b57",
+ "seashell" => "#fff5ee",
+ "sienna" => "#a0522d",
+ "silver" => "#c0c0c0",
+ "skyblue" => "#87ceeb",
+ "slateblue" => "#6a5acd",
+ "slategray" => "#708090",
+ "snow" => "#fffafa",
+ "springgreen" => "#00ff7f",
+ "steelblue" => "#4682b4",
+ "tan" => "#d2b48c",
+ "teal" => "#008080",
+ "thistle" => "#d8bfd8",
+ "tomato" => "#ff6347",
+ "turquoise" => "#40e0d0",
+ "violet" => "#ee82ee",
+ "wheat" => "#f5deb3",
+ "white" => "#fff",
+ "whitesmoke" => "#f5f5f5",
+ "yellow" => "#ff0",
+ "yellowgreen" => "#9acd32"
+ );
+ /**
+ * Overwrites {@link aCssMinifierPlugin::__construct()}.
+ *
+ * The constructor will create the {@link CssConvertNamedColorsMinifierPlugin::$reMatch replace regular expression}
+ * based on the {@link CssConvertNamedColorsMinifierPlugin::$transformation transformation table}.
+ *
+ * @param CssMinifier $minifier The CssMinifier object of this plugin.
+ * @param array $configuration Plugin configuration [optional]
+ * @return void
+ */
+ public function __construct(CssMinifier $minifier, array $configuration = array())
+ {
+ $this->reMatch = "/(^|\s)+(" . implode("|", array_keys($this->transformation)) . ")(\s|$)+/iS";
+ parent::__construct($minifier, $configuration);
+ }
+ /**
+ * Implements {@link aCssMinifierPlugin::minify()}.
+ *
+ * @param aCssToken $token Token to process
+ * @return boolean Return TRUE to break the processing of this token; FALSE to continue
+ */
+ public function apply(aCssToken &$token)
+ {
+ $lcValue = strtolower($token->Value);
+ // Declaration value equals a value in the transformation table => simple replace
+ if (isset($this->transformation[$lcValue]))
+ {
+ $token->Value = $this->transformation[$lcValue];
+ }
+ // Declaration value contains a value in the transformation table => regular expression replace
+ elseif (preg_match($this->reMatch, $token->Value))
+ {
+ $token->Value = preg_replace_callback($this->reMatch, array($this, 'reReplace'), $token->Value);
+ }
+ return false;
+ }
+ /**
+ * Callback for replacement value.
+ *
+ * @param array $match
+ * @return string
+ */
+ private function reReplace($match)
+ {
+ return $match[1] . $this->transformation[strtolower($match[2])] . $match[3];
+ }
+ /**
+ * Implements {@link aMinifierPlugin::getTriggerTokens()}
+ *
+ * @return array
+ */
+ public function getTriggerTokens()
+ {
+ return array
+ (
+ "CssAtFontFaceDeclarationToken",
+ "CssAtPageDeclarationToken",
+ "CssRulesetDeclarationToken"
+ );
+ }
+}
+
+/**
+ * This {@link aCssMinifierFilter minifier filter} triggers on CSS Level 3 properties and will add declaration tokens
+ * with browser-specific properties.
+ *
+ * @package CssMin/Minifier/Filters
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssConvertLevel3PropertiesMinifierFilter extends aCssMinifierFilter
+{
+ /**
+ * Css property transformations table. Used to convert CSS3 and proprietary properties to the browser-specific
+ * counterparts.
+ *
+ * @var array
+ */
+ private $transformations = array
+ (
+ // Property Array(Mozilla, Webkit, Opera, Internet Explorer); NULL values are placeholders and will get ignored
+ "animation" => array(null, "-webkit-animation", null, null),
+ "animation-delay" => array(null, "-webkit-animation-delay", null, null),
+ "animation-direction" => array(null, "-webkit-animation-direction", null, null),
+ "animation-duration" => array(null, "-webkit-animation-duration", null, null),
+ "animation-fill-mode" => array(null, "-webkit-animation-fill-mode", null, null),
+ "animation-iteration-count" => array(null, "-webkit-animation-iteration-count", null, null),
+ "animation-name" => array(null, "-webkit-animation-name", null, null),
+ "animation-play-state" => array(null, "-webkit-animation-play-state", null, null),
+ "animation-timing-function" => array(null, "-webkit-animation-timing-function", null, null),
+ "appearance" => array("-moz-appearance", "-webkit-appearance", null, null),
+ "backface-visibility" => array(null, "-webkit-backface-visibility", null, null),
+ "background-clip" => array(null, "-webkit-background-clip", null, null),
+ "background-composite" => array(null, "-webkit-background-composite", null, null),
+ "background-inline-policy" => array("-moz-background-inline-policy", null, null, null),
+ "background-origin" => array(null, "-webkit-background-origin", null, null),
+ "background-position-x" => array(null, null, null, "-ms-background-position-x"),
+ "background-position-y" => array(null, null, null, "-ms-background-position-y"),
+ "background-size" => array(null, "-webkit-background-size", null, null),
+ "behavior" => array(null, null, null, "-ms-behavior"),
+ "binding" => array("-moz-binding", null, null, null),
+ "border-after" => array(null, "-webkit-border-after", null, null),
+ "border-after-color" => array(null, "-webkit-border-after-color", null, null),
+ "border-after-style" => array(null, "-webkit-border-after-style", null, null),
+ "border-after-width" => array(null, "-webkit-border-after-width", null, null),
+ "border-before" => array(null, "-webkit-border-before", null, null),
+ "border-before-color" => array(null, "-webkit-border-before-color", null, null),
+ "border-before-style" => array(null, "-webkit-border-before-style", null, null),
+ "border-before-width" => array(null, "-webkit-border-before-width", null, null),
+ "border-border-bottom-colors" => array("-moz-border-bottom-colors", null, null, null),
+ "border-bottom-left-radius" => array("-moz-border-radius-bottomleft", "-webkit-border-bottom-left-radius", null, null),
+ "border-bottom-right-radius" => array("-moz-border-radius-bottomright", "-webkit-border-bottom-right-radius", null, null),
+ "border-end" => array("-moz-border-end", "-webkit-border-end", null, null),
+ "border-end-color" => array("-moz-border-end-color", "-webkit-border-end-color", null, null),
+ "border-end-style" => array("-moz-border-end-style", "-webkit-border-end-style", null, null),
+ "border-end-width" => array("-moz-border-end-width", "-webkit-border-end-width", null, null),
+ "border-fit" => array(null, "-webkit-border-fit", null, null),
+ "border-horizontal-spacing" => array(null, "-webkit-border-horizontal-spacing", null, null),
+ "border-image" => array("-moz-border-image", "-webkit-border-image", null, null),
+ "border-left-colors" => array("-moz-border-left-colors", null, null, null),
+ "border-radius" => array("-moz-border-radius", "-webkit-border-radius", null, null),
+ "border-border-right-colors" => array("-moz-border-right-colors", null, null, null),
+ "border-start" => array("-moz-border-start", "-webkit-border-start", null, null),
+ "border-start-color" => array("-moz-border-start-color", "-webkit-border-start-color", null, null),
+ "border-start-style" => array("-moz-border-start-style", "-webkit-border-start-style", null, null),
+ "border-start-width" => array("-moz-border-start-width", "-webkit-border-start-width", null, null),
+ "border-top-colors" => array("-moz-border-top-colors", null, null, null),
+ "border-top-left-radius" => array("-moz-border-radius-topleft", "-webkit-border-top-left-radius", null, null),
+ "border-top-right-radius" => array("-moz-border-radius-topright", "-webkit-border-top-right-radius", null, null),
+ "border-vertical-spacing" => array(null, "-webkit-border-vertical-spacing", null, null),
+ "box-align" => array("-moz-box-align", "-webkit-box-align", null, null),
+ "box-direction" => array("-moz-box-direction", "-webkit-box-direction", null, null),
+ "box-flex" => array("-moz-box-flex", "-webkit-box-flex", null, null),
+ "box-flex-group" => array(null, "-webkit-box-flex-group", null, null),
+ "box-flex-lines" => array(null, "-webkit-box-flex-lines", null, null),
+ "box-ordinal-group" => array("-moz-box-ordinal-group", "-webkit-box-ordinal-group", null, null),
+ "box-orient" => array("-moz-box-orient", "-webkit-box-orient", null, null),
+ "box-pack" => array("-moz-box-pack", "-webkit-box-pack", null, null),
+ "box-reflect" => array(null, "-webkit-box-reflect", null, null),
+ "box-shadow" => array("-moz-box-shadow", "-webkit-box-shadow", null, null),
+ "box-sizing" => array("-moz-box-sizing", null, null, null),
+ "color-correction" => array(null, "-webkit-color-correction", null, null),
+ "column-break-after" => array(null, "-webkit-column-break-after", null, null),
+ "column-break-before" => array(null, "-webkit-column-break-before", null, null),
+ "column-break-inside" => array(null, "-webkit-column-break-inside", null, null),
+ "column-count" => array("-moz-column-count", "-webkit-column-count", null, null),
+ "column-gap" => array("-moz-column-gap", "-webkit-column-gap", null, null),
+ "column-rule" => array("-moz-column-rule", "-webkit-column-rule", null, null),
+ "column-rule-color" => array("-moz-column-rule-color", "-webkit-column-rule-color", null, null),
+ "column-rule-style" => array("-moz-column-rule-style", "-webkit-column-rule-style", null, null),
+ "column-rule-width" => array("-moz-column-rule-width", "-webkit-column-rule-width", null, null),
+ "column-span" => array(null, "-webkit-column-span", null, null),
+ "column-width" => array("-moz-column-width", "-webkit-column-width", null, null),
+ "columns" => array(null, "-webkit-columns", null, null),
+ "filter" => array(__CLASS__, "filter"),
+ "float-edge" => array("-moz-float-edge", null, null, null),
+ "font-feature-settings" => array("-moz-font-feature-settings", null, null, null),
+ "font-language-override" => array("-moz-font-language-override", null, null, null),
+ "font-size-delta" => array(null, "-webkit-font-size-delta", null, null),
+ "font-smoothing" => array(null, "-webkit-font-smoothing", null, null),
+ "force-broken-image-icon" => array("-moz-force-broken-image-icon", null, null, null),
+ "highlight" => array(null, "-webkit-highlight", null, null),
+ "hyphenate-character" => array(null, "-webkit-hyphenate-character", null, null),
+ "hyphenate-locale" => array(null, "-webkit-hyphenate-locale", null, null),
+ "hyphens" => array(null, "-webkit-hyphens", null, null),
+ "force-broken-image-icon" => array("-moz-image-region", null, null, null),
+ "ime-mode" => array(null, null, null, "-ms-ime-mode"),
+ "interpolation-mode" => array(null, null, null, "-ms-interpolation-mode"),
+ "layout-flow" => array(null, null, null, "-ms-layout-flow"),
+ "layout-grid" => array(null, null, null, "-ms-layout-grid"),
+ "layout-grid-char" => array(null, null, null, "-ms-layout-grid-char"),
+ "layout-grid-line" => array(null, null, null, "-ms-layout-grid-line"),
+ "layout-grid-mode" => array(null, null, null, "-ms-layout-grid-mode"),
+ "layout-grid-type" => array(null, null, null, "-ms-layout-grid-type"),
+ "line-break" => array(null, "-webkit-line-break", null, "-ms-line-break"),
+ "line-clamp" => array(null, "-webkit-line-clamp", null, null),
+ "line-grid-mode" => array(null, null, null, "-ms-line-grid-mode"),
+ "logical-height" => array(null, "-webkit-logical-height", null, null),
+ "logical-width" => array(null, "-webkit-logical-width", null, null),
+ "margin-after" => array(null, "-webkit-margin-after", null, null),
+ "margin-after-collapse" => array(null, "-webkit-margin-after-collapse", null, null),
+ "margin-before" => array(null, "-webkit-margin-before", null, null),
+ "margin-before-collapse" => array(null, "-webkit-margin-before-collapse", null, null),
+ "margin-bottom-collapse" => array(null, "-webkit-margin-bottom-collapse", null, null),
+ "margin-collapse" => array(null, "-webkit-margin-collapse", null, null),
+ "margin-end" => array("-moz-margin-end", "-webkit-margin-end", null, null),
+ "margin-start" => array("-moz-margin-start", "-webkit-margin-start", null, null),
+ "margin-top-collapse" => array(null, "-webkit-margin-top-collapse", null, null),
+ "marquee " => array(null, "-webkit-marquee", null, null),
+ "marquee-direction" => array(null, "-webkit-marquee-direction", null, null),
+ "marquee-increment" => array(null, "-webkit-marquee-increment", null, null),
+ "marquee-repetition" => array(null, "-webkit-marquee-repetition", null, null),
+ "marquee-speed" => array(null, "-webkit-marquee-speed", null, null),
+ "marquee-style" => array(null, "-webkit-marquee-style", null, null),
+ "mask" => array(null, "-webkit-mask", null, null),
+ "mask-attachment" => array(null, "-webkit-mask-attachment", null, null),
+ "mask-box-image" => array(null, "-webkit-mask-box-image", null, null),
+ "mask-clip" => array(null, "-webkit-mask-clip", null, null),
+ "mask-composite" => array(null, "-webkit-mask-composite", null, null),
+ "mask-image" => array(null, "-webkit-mask-image", null, null),
+ "mask-origin" => array(null, "-webkit-mask-origin", null, null),
+ "mask-position" => array(null, "-webkit-mask-position", null, null),
+ "mask-position-x" => array(null, "-webkit-mask-position-x", null, null),
+ "mask-position-y" => array(null, "-webkit-mask-position-y", null, null),
+ "mask-repeat" => array(null, "-webkit-mask-repeat", null, null),
+ "mask-repeat-x" => array(null, "-webkit-mask-repeat-x", null, null),
+ "mask-repeat-y" => array(null, "-webkit-mask-repeat-y", null, null),
+ "mask-size" => array(null, "-webkit-mask-size", null, null),
+ "match-nearest-mail-blockquote-color" => array(null, "-webkit-match-nearest-mail-blockquote-color", null, null),
+ "max-logical-height" => array(null, "-webkit-max-logical-height", null, null),
+ "max-logical-width" => array(null, "-webkit-max-logical-width", null, null),
+ "min-logical-height" => array(null, "-webkit-min-logical-height", null, null),
+ "min-logical-width" => array(null, "-webkit-min-logical-width", null, null),
+ "object-fit" => array(null, null, "-o-object-fit", null),
+ "object-position" => array(null, null, "-o-object-position", null),
+ "opacity" => array(__CLASS__, "opacity"),
+ "outline-radius" => array("-moz-outline-radius", null, null, null),
+ "outline-bottom-left-radius" => array("-moz-outline-radius-bottomleft", null, null, null),
+ "outline-bottom-right-radius" => array("-moz-outline-radius-bottomright", null, null, null),
+ "outline-top-left-radius" => array("-moz-outline-radius-topleft", null, null, null),
+ "outline-top-right-radius" => array("-moz-outline-radius-topright", null, null, null),
+ "padding-after" => array(null, "-webkit-padding-after", null, null),
+ "padding-before" => array(null, "-webkit-padding-before", null, null),
+ "padding-end" => array("-moz-padding-end", "-webkit-padding-end", null, null),
+ "padding-start" => array("-moz-padding-start", "-webkit-padding-start", null, null),
+ "perspective" => array(null, "-webkit-perspective", null, null),
+ "perspective-origin" => array(null, "-webkit-perspective-origin", null, null),
+ "perspective-origin-x" => array(null, "-webkit-perspective-origin-x", null, null),
+ "perspective-origin-y" => array(null, "-webkit-perspective-origin-y", null, null),
+ "rtl-ordering" => array(null, "-webkit-rtl-ordering", null, null),
+ "scrollbar-3dlight-color" => array(null, null, null, "-ms-scrollbar-3dlight-color"),
+ "scrollbar-arrow-color" => array(null, null, null, "-ms-scrollbar-arrow-color"),
+ "scrollbar-base-color" => array(null, null, null, "-ms-scrollbar-base-color"),
+ "scrollbar-darkshadow-color" => array(null, null, null, "-ms-scrollbar-darkshadow-color"),
+ "scrollbar-face-color" => array(null, null, null, "-ms-scrollbar-face-color"),
+ "scrollbar-highlight-color" => array(null, null, null, "-ms-scrollbar-highlight-color"),
+ "scrollbar-shadow-color" => array(null, null, null, "-ms-scrollbar-shadow-color"),
+ "scrollbar-track-color" => array(null, null, null, "-ms-scrollbar-track-color"),
+ "stack-sizing" => array("-moz-stack-sizing", null, null, null),
+ "svg-shadow" => array(null, "-webkit-svg-shadow", null, null),
+ "tab-size" => array("-moz-tab-size", null, "-o-tab-size", null),
+ "table-baseline" => array(null, null, "-o-table-baseline", null),
+ "text-align-last" => array(null, null, null, "-ms-text-align-last"),
+ "text-autospace" => array(null, null, null, "-ms-text-autospace"),
+ "text-combine" => array(null, "-webkit-text-combine", null, null),
+ "text-decorations-in-effect" => array(null, "-webkit-text-decorations-in-effect", null, null),
+ "text-emphasis" => array(null, "-webkit-text-emphasis", null, null),
+ "text-emphasis-color" => array(null, "-webkit-text-emphasis-color", null, null),
+ "text-emphasis-position" => array(null, "-webkit-text-emphasis-position", null, null),
+ "text-emphasis-style" => array(null, "-webkit-text-emphasis-style", null, null),
+ "text-fill-color" => array(null, "-webkit-text-fill-color", null, null),
+ "text-justify" => array(null, null, null, "-ms-text-justify"),
+ "text-kashida-space" => array(null, null, null, "-ms-text-kashida-space"),
+ "text-overflow" => array(null, null, "-o-text-overflow", "-ms-text-overflow"),
+ "text-security" => array(null, "-webkit-text-security", null, null),
+ "text-size-adjust" => array(null, "-webkit-text-size-adjust", null, "-ms-text-size-adjust"),
+ "text-stroke" => array(null, "-webkit-text-stroke", null, null),
+ "text-stroke-color" => array(null, "-webkit-text-stroke-color", null, null),
+ "text-stroke-width" => array(null, "-webkit-text-stroke-width", null, null),
+ "text-underline-position" => array(null, null, null, "-ms-text-underline-position"),
+ "transform" => array("-moz-transform", "-webkit-transform", "-o-transform", null),
+ "transform-origin" => array("-moz-transform-origin", "-webkit-transform-origin", "-o-transform-origin", null),
+ "transform-origin-x" => array(null, "-webkit-transform-origin-x", null, null),
+ "transform-origin-y" => array(null, "-webkit-transform-origin-y", null, null),
+ "transform-origin-z" => array(null, "-webkit-transform-origin-z", null, null),
+ "transform-style" => array(null, "-webkit-transform-style", null, null),
+ "transition" => array("-moz-transition", "-webkit-transition", "-o-transition", null),
+ "transition-delay" => array("-moz-transition-delay", "-webkit-transition-delay", "-o-transition-delay", null),
+ "transition-duration" => array("-moz-transition-duration", "-webkit-transition-duration", "-o-transition-duration", null),
+ "transition-property" => array("-moz-transition-property", "-webkit-transition-property", "-o-transition-property", null),
+ "transition-timing-function" => array("-moz-transition-timing-function", "-webkit-transition-timing-function", "-o-transition-timing-function", null),
+ "user-drag" => array(null, "-webkit-user-drag", null, null),
+ "user-focus" => array("-moz-user-focus", null, null, null),
+ "user-input" => array("-moz-user-input", null, null, null),
+ "user-modify" => array("-moz-user-modify", "-webkit-user-modify", null, null),
+ "user-select" => array("-moz-user-select", "-webkit-user-select", null, null),
+ "white-space" => array(__CLASS__, "whiteSpace"),
+ "window-shadow" => array("-moz-window-shadow", null, null, null),
+ "word-break" => array(null, null, null, "-ms-word-break"),
+ "word-wrap" => array(null, null, null, "-ms-word-wrap"),
+ "writing-mode" => array(null, "-webkit-writing-mode", null, "-ms-writing-mode"),
+ "zoom" => array(null, null, null, "-ms-zoom")
+ );
+ /**
+ * Implements {@link aCssMinifierFilter::filter()}.
+ *
+ * @param array $tokens Array of objects of type aCssToken
+ * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
+ */
+ public function apply(array &$tokens)
+ {
+ $r = 0;
+ $transformations = &$this->transformations;
+ for ($i = 0, $l = count($tokens); $i < $l; $i++)
+ {
+ if (get_class($tokens[$i]) === "CssRulesetDeclarationToken")
+ {
+ $tProperty = $tokens[$i]->Property;
+ if (isset($transformations[$tProperty]))
+ {
+ $result = array();
+ if (is_callable($transformations[$tProperty]))
+ {
+ $result = call_user_func_array($transformations[$tProperty], array($tokens[$i]));
+ if (!is_array($result) && is_object($result))
+ {
+ $result = array($result);
+ }
+ }
+ else
+ {
+ $tValue = $tokens[$i]->Value;
+ $tMediaTypes = $tokens[$i]->MediaTypes;
+ foreach ($transformations[$tProperty] as $property)
+ {
+ if ($property !== null)
+ {
+ $result[] = new CssRulesetDeclarationToken($property, $tValue, $tMediaTypes);
+ }
+ }
+ }
+ if (count($result) > 0)
+ {
+ array_splice($tokens, $i + 1, 0, $result);
+ $i += count($result);
+ $l += count($result);
+ }
+ }
+ }
+ }
+ return $r;
+ }
+ /**
+ * Transforms the Internet Explorer specific declaration property "filter" to Internet Explorer 8+ compatible
+ * declaratiopn property "-ms-filter".
+ *
+ * @param aCssToken $token
+ * @return array
+ */
+ private static function filter($token)
+ {
+ $r = array
+ (
+ new CssRulesetDeclarationToken("-ms-filter", "\"" . $token->Value . "\"", $token->MediaTypes),
+ );
+ return $r;
+ }
+ /**
+ * Transforms "opacity: {value}" into browser specific counterparts.
+ *
+ * @param aCssToken $token
+ * @return array
+ */
+ private static function opacity($token)
+ {
+ // Calculate the value for Internet Explorer filter statement
+ $ieValue = (int) ((float) $token->Value * 100);
+ $r = array
+ (
+ // Internet Explorer >= 8
+ new CssRulesetDeclarationToken("-ms-filter", "\"alpha(opacity=" . $ieValue . ")\"", $token->MediaTypes),
+ // Internet Explorer >= 4 <= 7
+ new CssRulesetDeclarationToken("filter", "alpha(opacity=" . $ieValue . ")", $token->MediaTypes),
+ new CssRulesetDeclarationToken("zoom", "1", $token->MediaTypes)
+ );
+ return $r;
+ }
+ /**
+ * Transforms "white-space: pre-wrap" into browser specific counterparts.
+ *
+ * @param aCssToken $token
+ * @return array
+ */
+ private static function whiteSpace($token)
+ {
+ if (strtolower($token->Value) === "pre-wrap")
+ {
+ $r = array
+ (
+ // Firefox < 3
+ new CssRulesetDeclarationToken("white-space", "-moz-pre-wrap", $token->MediaTypes),
+ // Webkit
+ new CssRulesetDeclarationToken("white-space", "-webkit-pre-wrap", $token->MediaTypes),
+ // Opera >= 4 <= 6
+ new CssRulesetDeclarationToken("white-space", "-pre-wrap", $token->MediaTypes),
+ // Opera >= 7
+ new CssRulesetDeclarationToken("white-space", "-o-pre-wrap", $token->MediaTypes),
+ // Internet Explorer >= 5.5
+ new CssRulesetDeclarationToken("word-wrap", "break-word", $token->MediaTypes)
+ );
+ return $r;
+ }
+ else
+ {
+ return array();
+ }
+ }
+}
+
+/**
+ * This {@link aCssMinifierFilter minifier filter} will convert @keyframes at-rule block to browser specific counterparts.
+ *
+ * @package CssMin/Minifier/Filters
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssConvertLevel3AtKeyframesMinifierFilter extends aCssMinifierFilter
+{
+ /**
+ * Implements {@link aCssMinifierFilter::filter()}.
+ *
+ * @param array $tokens Array of objects of type aCssToken
+ * @return integer Count of added, changed or removed tokens; a return value larger than 0 will rebuild the array
+ */
+ public function apply(array &$tokens)
+ {
+ $r = 0;
+ $transformations = array("-moz-keyframes", "-webkit-keyframes");
+ for ($i = 0, $l = count($tokens); $i < $l; $i++)
+ {
+ if (get_class($tokens[$i]) === "CssAtKeyframesStartToken")
+ {
+ for ($ii = $i; $ii < $l; $ii++)
+ {
+ if (get_class($tokens[$ii]) === "CssAtKeyframesEndToken")
+ {
+ break;
+ }
+ }
+ if (get_class($tokens[$ii]) === "CssAtKeyframesEndToken")
+ {
+ $add = array();
+ $source = array();
+ for ($iii = $i; $iii <= $ii; $iii++)
+ {
+ $source[] = clone($tokens[$iii]);
+ }
+ foreach ($transformations as $transformation)
+ {
+ $t = array();
+ foreach ($source as $token)
+ {
+ $t[] = clone($token);
+ }
+ $t[0]->AtRuleName = $transformation;
+ $add = array_merge($add, $t);
+ }
+ if (isset($this->configuration["RemoveSource"]) && $this->configuration["RemoveSource"] === true)
+ {
+ array_splice($tokens, $i, $ii - $i + 1, $add);
+ }
+ else
+ {
+ array_splice($tokens, $ii + 1, 0, $add);
+ }
+ $l = count($tokens);
+ $i = $ii + count($add);
+ $r += count($add);
+ }
+ }
+ }
+ return $r;
+ }
+}
+
+/**
+ * This {@link aCssMinifierPlugin} will convert a color value in hsl notation to hexadecimal notation.
+ *
+ * Example:
+ *
+ * color: hsl(232,36%,48%);
+ *
+ *
+ * Will get converted to:
+ *
+ * color:#4e5aa7;
+ *
+ *
+ * @package CssMin/Minifier/Plugins
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssConvertHslColorsMinifierPlugin extends aCssMinifierPlugin
+{
+ /**
+ * Regular expression matching the value.
+ *
+ * @var string
+ */
+ private $reMatch = "/^hsl\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*%\s*,\s*([0-9]+)\s*%\s*\)/iS";
+ /**
+ * Implements {@link aCssMinifierPlugin::minify()}.
+ *
+ * @param aCssToken $token Token to process
+ * @return boolean Return TRUE to break the processing of this token; FALSE to continue
+ */
+ public function apply(aCssToken &$token)
+ {
+ if (stripos($token->Value, "hsl") !== false && preg_match($this->reMatch, $token->Value, $m))
+ {
+ $token->Value = str_replace($m[0], $this->hsl2hex($m[1], $m[2], $m[3]), $token->Value);
+ }
+ return false;
+ }
+ /**
+ * Implements {@link aMinifierPlugin::getTriggerTokens()}
+ *
+ * @return array
+ */
+ public function getTriggerTokens()
+ {
+ return array
+ (
+ "CssAtFontFaceDeclarationToken",
+ "CssAtPageDeclarationToken",
+ "CssRulesetDeclarationToken"
+ );
+ }
+ /**
+ * Convert a HSL value to hexadecimal notation.
+ *
+ * Based on: {@link http://www.easyrgb.com/index.php?X=MATH&H=19#text19}.
+ *
+ * @param integer $hue Hue
+ * @param integer $saturation Saturation
+ * @param integer $lightness Lightnesss
+ * @return string
+ */
+ private function hsl2hex($hue, $saturation, $lightness)
+ {
+ $hue = $hue / 360;
+ $saturation = $saturation / 100;
+ $lightness = $lightness / 100;
+ if ($saturation == 0)
+ {
+ $red = $lightness * 255;
+ $green = $lightness * 255;
+ $blue = $lightness * 255;
+ }
+ else
+ {
+ if ($lightness < 0.5 )
+ {
+ $v2 = $lightness * (1 + $saturation);
+ }
+ else
+ {
+ $v2 = ($lightness + $saturation) - ($saturation * $lightness);
+ }
+ $v1 = 2 * $lightness - $v2;
+ $red = 255 * self::hue2rgb($v1, $v2, $hue + (1 / 3));
+ $green = 255 * self::hue2rgb($v1, $v2, $hue);
+ $blue = 255 * self::hue2rgb($v1, $v2, $hue - (1 / 3));
+ }
+ return "#" . str_pad(dechex(round($red)), 2, "0", STR_PAD_LEFT) . str_pad(dechex(round($green)), 2, "0", STR_PAD_LEFT) . str_pad(dechex(round($blue)), 2, "0", STR_PAD_LEFT);
+ }
+ /**
+ * Apply hue to a rgb color value.
+ *
+ * @param integer $v1 Value 1
+ * @param integer $v2 Value 2
+ * @param integer $hue Hue
+ * @return integer
+ */
+ private function hue2rgb($v1, $v2, $hue)
+ {
+ if ($hue < 0)
+ {
+ $hue += 1;
+ }
+ if ($hue > 1)
+ {
+ $hue -= 1;
+ }
+ if ((6 * $hue) < 1)
+ {
+ return ($v1 + ($v2 - $v1) * 6 * $hue);
+ }
+ if ((2 * $hue) < 1)
+ {
+ return ($v2);
+ }
+ if ((3 * $hue) < 2)
+ {
+ return ($v1 + ($v2 - $v1) * (( 2 / 3) - $hue) * 6);
+ }
+ return $v1;
+ }
+}
+
+/**
+ * This {@link aCssMinifierPlugin} will convert the font-weight values normal and bold to their numeric notation.
+ *
+ * Example:
+ *
+ * font-weight: normal;
+ * font: bold 11px monospace;
+ *
+ *
+ * Will get converted to:
+ *
+ * font-weight:400;
+ * font:700 11px monospace;
+ *
+ *
+ * @package CssMin/Minifier/Pluginsn
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssConvertFontWeightMinifierPlugin extends aCssMinifierPlugin
+{
+ /**
+ * Array of included declaration properties this plugin will process; others declaration properties will get
+ * ignored.
+ *
+ * @var array
+ */
+ private $include = array
+ (
+ "font",
+ "font-weight"
+ );
+ /**
+ * Regular expression matching the value.
+ *
+ * @var string
+ */
+ private $reMatch = null;
+ /**
+ * Transformation table used by the {@link CssConvertFontWeightMinifierPlugin::reReplace() replacement method}.
+ *
+ * @var array
+ */
+ private $transformation = array
+ (
+ "normal" => "400",
+ "bold" => "700"
+ );
+ /**
+ * Overwrites {@link aCssMinifierPlugin::__construct()}.
+ *
+ * The constructor will create the {@link CssConvertFontWeightMinifierPlugin::$reMatch replace regular expression}
+ * based on the {@link CssConvertFontWeightMinifierPlugin::$transformation transformation table}.
+ *
+ * @param CssMinifier $minifier The CssMinifier object of this plugin.
+ * @return void
+ */
+ public function __construct(CssMinifier $minifier)
+ {
+ $this->reMatch = "/(^|\s)+(" . implode("|", array_keys($this->transformation)). ")(\s|$)+/iS";
+ parent::__construct($minifier);
+ }
+ /**
+ * Implements {@link aCssMinifierPlugin::minify()}.
+ *
+ * @param aCssToken $token Token to process
+ * @return boolean Return TRUE to break the processing of this token; FALSE to continue
+ */
+ public function apply(aCssToken &$token)
+ {
+ if (in_array($token->Property, $this->include) && preg_match($this->reMatch, $token->Value, $m))
+ {
+ $token->Value = preg_replace_callback($this->reMatch, array($this, 'reReplace'), $token->Value);
+ }
+ return false;
+ }
+ /**
+ * Callback for replacement value.
+ *
+ * @param array $match
+ * @return string
+ */
+ private function reReplace($match)
+ {
+ return $match[1] . $this->transformation[strtolower($match[2])] . $match[3];
+ }
+ /**
+ * Implements {@link aMinifierPlugin::getTriggerTokens()}
+ *
+ * @return array
+ */
+ public function getTriggerTokens()
+ {
+ return array
+ (
+ "CssAtFontFaceDeclarationToken",
+ "CssAtPageDeclarationToken",
+ "CssRulesetDeclarationToken"
+ );
+ }
+}
+
+/**
+ * This {@link aCssMinifierPlugin} will compress several unit values to their short notations. Examples:
+ *
+ *
+ * padding: 0.5em;
+ * border: 0px;
+ * margin: 0 0 0 0;
+ *
+ *
+ * Will get compressed to:
+ *
+ *
+ * padding:.5px;
+ * border:0;
+ * margin:0;
+ *
+ *
+ * --
+ *
+ * @package CssMin/Minifier/Plugins
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssCompressUnitValuesMinifierPlugin extends aCssMinifierPlugin
+{
+ /**
+ * Regular expression used for matching and replacing unit values.
+ *
+ * @var array
+ */
+ private $re = array
+ (
+ "/(^| |-)0\.([0-9]+?)(0+)?(%|em|ex|px|in|cm|mm|pt|pc)/iS" => "\${1}.\${2}\${4}",
+ "/(^| )-?(\.?)0(%|em|ex|px|in|cm|mm|pt|pc)/iS" => "\${1}0",
+ );
+ /**
+ * Regular expression used for matching and replacing unit values only for non-blacklisted declarations.
+ *
+ * @var array
+ */
+ private $reBlacklisted = array(
+ "/(^0\s0\s0\s0)|(^0\s0\s0$)|(^0\s0$)/iS" => "0",
+ );
+ /**
+ * Blacklisted properties for the above regular expression.
+ *
+ * @var array
+ */
+ private $propertiesBlacklist = array('background-position');
+ /**
+ * Regular expression matching the value.
+ *
+ * @var string
+ */
+ private $reMatch = "/(^| |-)0\.([0-9]+?)(0+)?(%|em|ex|px|in|cm|mm|pt|pc)|(^| )-?(\.?)0(%|em|ex|px|in|cm|mm|pt|pc)|(^0\s0\s0\s0$)|(^0\s0\s0$)|(^0\s0$)/iS";
+ /**
+ * Implements {@link aCssMinifierPlugin::minify()}.
+ *
+ * @param aCssToken $token Token to process
+ * @return boolean Return TRUE to break the processing of this token; FALSE to continue
+ */
+ public function apply(aCssToken &$token)
+ {
+ if (preg_match($this->reMatch, $token->Value))
+ {
+ foreach ($this->re as $reMatch => $reReplace)
+ {
+ $token->Value = preg_replace($reMatch, $reReplace, $token->Value);
+ }
+ if (!in_array($token->Property, $this->propertiesBlacklist))
+ {
+ foreach ($this->reBlacklisted as $reMatch => $reReplace)
+ {
+ $token->Value = preg_replace($reMatch, $reReplace, $token->Value);
+ }
+ }
+ }
+ return false;
+ }
+ /**
+ * Implements {@link aMinifierPlugin::getTriggerTokens()}
+ *
+ * @return array
+ */
+ public function getTriggerTokens()
+ {
+ return array
+ (
+ "CssAtFontFaceDeclarationToken",
+ "CssAtPageDeclarationToken",
+ "CssRulesetDeclarationToken"
+ );
+ }
+}
+
+/**
+ * This {@link aCssMinifierPlugin} compress the content of expresssion() declaration values.
+ *
+ * For compression of expressions {@link https://github.com/rgrove/jsmin-php/ JSMin} will get used. JSMin have to be
+ * already included or loadable via {@link http://goo.gl/JrW54 PHP autoloading}.
+ *
+ * @package CssMin/Minifier/Plugins
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssCompressExpressionValuesMinifierPlugin extends aCssMinifierPlugin
+{
+ /**
+ * Implements {@link aCssMinifierPlugin::minify()}.
+ *
+ * @param aCssToken $token Token to process
+ * @return boolean Return TRUE to break the processing of this token; FALSE to continue
+ */
+ public function apply(aCssToken &$token)
+ {
+ if (class_exists("JSMin") && stripos($token->Value, "expression(") !== false)
+ {
+ $value = $token->Value;
+ $value = substr($token->Value, stripos($token->Value, "expression(") + 10);
+ $value = trim(JSMin::minify($value));
+ $token->Value = "expression(" . $value . ")";
+ }
+ return false;
+ }
+ /**
+ * Implements {@link aMinifierPlugin::getTriggerTokens()}
+ *
+ * @return array
+ */
+ public function getTriggerTokens()
+ {
+ return array
+ (
+ "CssAtFontFaceDeclarationToken",
+ "CssAtPageDeclarationToken",
+ "CssRulesetDeclarationToken"
+ );
+ }
+}
+
+/**
+ * This {@link aCssMinifierPlugin} will convert hexadecimal color value with 6 chars to their 3 char hexadecimal
+ * notation (if possible).
+ *
+ * Example:
+ *
+ * color: #aabbcc;
+ *
+ *
+ * Will get converted to:
+ *
+ * color:#abc;
+ *
+ *
+ * @package CssMin/Minifier/Plugins
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssCompressColorValuesMinifierPlugin extends aCssMinifierPlugin
+{
+ /**
+ * Regular expression matching 6 char hexadecimal color values.
+ *
+ * @var string
+ */
+ private $reMatch = "/\#([0-9a-f]{6})/iS";
+ /**
+ * Implements {@link aCssMinifierPlugin::minify()}.
+ *
+ * @param aCssToken $token Token to process
+ * @return boolean Return TRUE to break the processing of this token; FALSE to continue
+ */
+ public function apply(aCssToken &$token)
+ {
+ if (strpos($token->Value, "#") !== false && preg_match($this->reMatch, $token->Value, $m))
+ {
+ $value = strtolower($m[1]);
+ if ($value[0] == $value[1] && $value[2] == $value[3] && $value[4] == $value[5])
+ {
+ $token->Value = str_replace($m[0], "#" . $value[0] . $value[2] . $value[4], $token->Value);
+ }
+ }
+ return false;
+ }
+ /**
+ * Implements {@link aMinifierPlugin::getTriggerTokens()}
+ *
+ * @return array
+ */
+ public function getTriggerTokens()
+ {
+ return array
+ (
+ "CssAtFontFaceDeclarationToken",
+ "CssAtPageDeclarationToken",
+ "CssRulesetDeclarationToken"
+ );
+ }
+}
+
+/**
+ * This {@link aCssToken CSS token} represents a CSS comment.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssCommentToken extends aCssToken
+{
+ /**
+ * Comment as Text.
+ *
+ * @var string
+ */
+ public $Comment = "";
+ /**
+ * Set the properties of a comment token.
+ *
+ * @param string $comment Comment including comment delimiters
+ * @return void
+ */
+ public function __construct($comment)
+ {
+ $this->Comment = $comment;
+ }
+ /**
+ * Implements {@link aCssToken::__toString()}.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->Comment;
+ }
+}
+
+/**
+ * {@link aCssParserPlugin Parser plugin} for parsing comments.
+ *
+ * Adds a {@link CssCommentToken} to the parser if a comment was found.
+ *
+ * @package CssMin/Parser/Plugins
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssCommentParserPlugin extends aCssParserPlugin
+{
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerChars()}.
+ *
+ * @return array
+ */
+ public function getTriggerChars()
+ {
+ return array("*", "/");
+ }
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerStates()}.
+ *
+ * @return array
+ */
+ public function getTriggerStates()
+ {
+ return false;
+ }
+ /**
+ * Stored buffer for restore.
+ *
+ * @var string
+ */
+ private $restoreBuffer = "";
+ /**
+ * Implements {@link aCssParserPlugin::parse()}.
+ *
+ * @param integer $index Current index
+ * @param string $char Current char
+ * @param string $previousChar Previous char
+ * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
+ */
+ public function parse($index, $char, $previousChar, $state)
+ {
+ if ($char === "*" && $previousChar === "/" && $state !== "T_COMMENT")
+ {
+ $this->parser->pushState("T_COMMENT");
+ $this->parser->setExclusive(__CLASS__);
+ $this->restoreBuffer = substr($this->parser->getAndClearBuffer(), 0, -2);
+ }
+ elseif ($char === "/" && $previousChar === "*" && $state === "T_COMMENT")
+ {
+ $this->parser->popState();
+ $this->parser->unsetExclusive();
+ $this->parser->appendToken(new CssCommentToken("/*" . $this->parser->getAndClearBuffer()));
+ $this->parser->setBuffer($this->restoreBuffer);
+ }
+ else
+ {
+ return false;
+ }
+ return true;
+ }
+}
+
+/**
+ * This {@link aCssToken CSS token} represents the start of a @variables at-rule block.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtVariablesStartToken extends aCssAtBlockStartToken
+{
+ /**
+ * Media types of the @variables at-rule block.
+ *
+ * @var array
+ */
+ public $MediaTypes = array();
+ /**
+ * Set the properties of a @variables at-rule token.
+ *
+ * @param array $mediaTypes Media types
+ * @return void
+ */
+ public function __construct($mediaTypes = null)
+ {
+ $this->MediaTypes = $mediaTypes ? $mediaTypes : array("all");
+ }
+ /**
+ * Implements {@link aCssToken::__toString()}.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return "";
+ }
+}
+
+/**
+ * {@link aCssParserPlugin Parser plugin} for parsing @variables at-rule block with including declarations.
+ *
+ * Found @variables at-rule blocks will add a {@link CssAtVariablesStartToken} and {@link CssAtVariablesEndToken} to the
+ * parser; including declarations as {@link CssAtVariablesDeclarationToken}.
+ *
+ * @package CssMin/Parser/Plugins
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtVariablesParserPlugin extends aCssParserPlugin
+{
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerChars()}.
+ *
+ * @return array
+ */
+ public function getTriggerChars()
+ {
+ return array("@", "{", "}", ":", ";");
+ }
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerStates()}.
+ *
+ * @return array
+ */
+ public function getTriggerStates()
+ {
+ return array("T_DOCUMENT", "T_AT_VARIABLES::PREPARE", "T_AT_VARIABLES", "T_AT_VARIABLES_DECLARATION");
+ }
+ /**
+ * Implements {@link aCssParserPlugin::parse()}.
+ *
+ * @param integer $index Current index
+ * @param string $char Current char
+ * @param string $previousChar Previous char
+ * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
+ */
+ public function parse($index, $char, $previousChar, $state)
+ {
+ // Start of @variables at-rule block
+ if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 10)) === "@variables")
+ {
+ $this->parser->pushState("T_AT_VARIABLES::PREPARE");
+ $this->parser->clearBuffer();
+ return $index + 10;
+ }
+ // Start of @variables declarations
+ elseif ($char === "{" && $state === "T_AT_VARIABLES::PREPARE")
+ {
+ $this->parser->setState("T_AT_VARIABLES");
+ $mediaTypes = array_filter(array_map("trim", explode(",", $this->parser->getAndClearBuffer("{"))));
+ $this->parser->appendToken(new CssAtVariablesStartToken($mediaTypes));
+ }
+ // Start of @variables declaration
+ if ($char === ":" && $state === "T_AT_VARIABLES")
+ {
+ $this->buffer = $this->parser->getAndClearBuffer(":");
+ $this->parser->pushState("T_AT_VARIABLES_DECLARATION");
+ }
+ // Unterminated @variables declaration
+ elseif ($char === ":" && $state === "T_AT_VARIABLES_DECLARATION")
+ {
+ // Ignore Internet Explorer filter declarations
+ if ($this->buffer === "filter")
+ {
+ return false;
+ }
+ CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated @variables declaration", $this->buffer . ":" . $this->parser->getBuffer() . "_"));
+ }
+ // End of @variables declaration
+ elseif (($char === ";" || $char === "}") && $state === "T_AT_VARIABLES_DECLARATION")
+ {
+ $value = $this->parser->getAndClearBuffer(";}");
+ if (strtolower(substr($value, -10, 10)) === "!important")
+ {
+ $value = trim(substr($value, 0, -10));
+ $isImportant = true;
+ }
+ else
+ {
+ $isImportant = false;
+ }
+ $this->parser->popState();
+ $this->parser->appendToken(new CssAtVariablesDeclarationToken($this->buffer, $value, $isImportant));
+ $this->buffer = "";
+ }
+ // End of @variables at-rule block
+ elseif ($char === "}" && $state === "T_AT_VARIABLES")
+ {
+ $this->parser->popState();
+ $this->parser->clearBuffer();
+ $this->parser->appendToken(new CssAtVariablesEndToken());
+ }
+ else
+ {
+ return false;
+ }
+ return true;
+ }
+}
+
+/**
+ * This {@link aCssToken CSS token} represents the end of a @variables at-rule block.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtVariablesEndToken extends aCssAtBlockEndToken
+{
+ /**
+ * Implements {@link aCssToken::__toString()}.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return "";
+ }
+}
+
+/**
+ * This {@link aCssToken CSS token} represents a declaration of a @variables at-rule block.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtVariablesDeclarationToken extends aCssDeclarationToken
+{
+ /**
+ * Implements {@link aCssToken::__toString()}.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return "";
+ }
+}
+
+/**
+ * This {@link aCssToken CSS token} represents the start of a @page at-rule block.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtPageStartToken extends aCssAtBlockStartToken
+{
+ /**
+ * Selector.
+ *
+ * @var string
+ */
+ public $Selector = "";
+ /**
+ * Sets the properties of the @page at-rule.
+ *
+ * @param string $selector Selector
+ * @return void
+ */
+ public function __construct($selector = "")
+ {
+ $this->Selector = $selector;
+ }
+ /**
+ * Implements {@link aCssToken::__toString()}.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return "@page" . ($this->Selector ? " " . $this->Selector : "") . "{";
+ }
+}
+
+/**
+ * {@link aCssParserPlugin Parser plugin} for parsing @page at-rule block with including declarations.
+ *
+ * Found @page at-rule blocks will add a {@link CssAtPageStartToken} and {@link CssAtPageEndToken} to the
+ * parser; including declarations as {@link CssAtPageDeclarationToken}.
+ *
+ * @package CssMin/Parser/Plugins
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtPageParserPlugin extends aCssParserPlugin
+{
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerChars()}.
+ *
+ * @return array
+ */
+ public function getTriggerChars()
+ {
+ return array("@", "{", "}", ":", ";");
+ }
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerStates()}.
+ *
+ * @return array
+ */
+ public function getTriggerStates()
+ {
+ return array("T_DOCUMENT", "T_AT_PAGE::SELECTOR", "T_AT_PAGE", "T_AT_PAGE_DECLARATION");
+ }
+ /**
+ * Implements {@link aCssParserPlugin::parse()}.
+ *
+ * @param integer $index Current index
+ * @param string $char Current char
+ * @param string $previousChar Previous char
+ * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
+ */
+ public function parse($index, $char, $previousChar, $state)
+ {
+ // Start of @page at-rule block
+ if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 5)) === "@page")
+ {
+ $this->parser->pushState("T_AT_PAGE::SELECTOR");
+ $this->parser->clearBuffer();
+ return $index + 5;
+ }
+ // Start of @page declarations
+ elseif ($char === "{" && $state === "T_AT_PAGE::SELECTOR")
+ {
+ $selector = $this->parser->getAndClearBuffer("{");
+ $this->parser->setState("T_AT_PAGE");
+ $this->parser->clearBuffer();
+ $this->parser->appendToken(new CssAtPageStartToken($selector));
+ }
+ // Start of @page declaration
+ elseif ($char === ":" && $state === "T_AT_PAGE")
+ {
+ $this->parser->pushState("T_AT_PAGE_DECLARATION");
+ $this->buffer = $this->parser->getAndClearBuffer(":", true);
+ }
+ // Unterminated @font-face declaration
+ elseif ($char === ":" && $state === "T_AT_PAGE_DECLARATION")
+ {
+ // Ignore Internet Explorer filter declarations
+ if ($this->buffer === "filter")
+ {
+ return false;
+ }
+ CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated @page declaration", $this->buffer . ":" . $this->parser->getBuffer() . "_"));
+ }
+ // End of @page declaration
+ elseif (($char === ";" || $char === "}") && $state == "T_AT_PAGE_DECLARATION")
+ {
+ $value = $this->parser->getAndClearBuffer(";}");
+ if (strtolower(substr($value, -10, 10)) == "!important")
+ {
+ $value = trim(substr($value, 0, -10));
+ $isImportant = true;
+ }
+ else
+ {
+ $isImportant = false;
+ }
+ $this->parser->popState();
+ $this->parser->appendToken(new CssAtPageDeclarationToken($this->buffer, $value, $isImportant));
+ // --
+ if ($char === "}")
+ {
+ $this->parser->popState();
+ $this->parser->appendToken(new CssAtPageEndToken());
+ }
+ $this->buffer = "";
+ }
+ // End of @page at-rule block
+ elseif ($char === "}" && $state === "T_AT_PAGE")
+ {
+ $this->parser->popState();
+ $this->parser->clearBuffer();
+ $this->parser->appendToken(new CssAtPageEndToken());
+ }
+ else
+ {
+ return false;
+ }
+ return true;
+ }
+}
+
+/**
+ * This {@link aCssToken CSS token} represents the end of a @page at-rule block.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtPageEndToken extends aCssAtBlockEndToken
+{
+
+}
+
+/**
+ * This {@link aCssToken CSS token} represents a declaration of a @page at-rule block.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtPageDeclarationToken extends aCssDeclarationToken
+{
+
+}
+
+/**
+ * This {@link aCssToken CSS token} represents the start of a @media at-rule block.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtMediaStartToken extends aCssAtBlockStartToken
+{
+ /**
+ * Sets the properties of the @media at-rule.
+ *
+ * @param array $mediaTypes Media types
+ * @return void
+ */
+ public function __construct(array $mediaTypes = array())
+ {
+ $this->MediaTypes = $mediaTypes;
+ }
+ /**
+ * Implements {@link aCssToken::__toString()}.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return "@media " . implode(",", $this->MediaTypes) . "{";
+ }
+}
+
+/**
+ * {@link aCssParserPlugin Parser plugin} for parsing @media at-rule block.
+ *
+ * Found @media at-rule blocks will add a {@link CssAtMediaStartToken} and {@link CssAtMediaEndToken} to the parser.
+ * This plugin will also set the the current media types using {@link CssParser::setMediaTypes()} and
+ * {@link CssParser::unsetMediaTypes()}.
+ *
+ * @package CssMin/Parser/Plugins
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtMediaParserPlugin extends aCssParserPlugin
+{
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerChars()}.
+ *
+ * @return array
+ */
+ public function getTriggerChars()
+ {
+ return array("@", "{", "}");
+ }
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerStates()}.
+ *
+ * @return array
+ */
+ public function getTriggerStates()
+ {
+ return array("T_DOCUMENT", "T_AT_MEDIA::PREPARE", "T_AT_MEDIA");
+ }
+ /**
+ * Implements {@link aCssParserPlugin::parse()}.
+ *
+ * @param integer $index Current index
+ * @param string $char Current char
+ * @param string $previousChar Previous char
+ * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
+ */
+ public function parse($index, $char, $previousChar, $state)
+ {
+ if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 6)) === "@media")
+ {
+ $this->parser->pushState("T_AT_MEDIA::PREPARE");
+ $this->parser->clearBuffer();
+ return $index + 6;
+ }
+ elseif ($char === "{" && $state === "T_AT_MEDIA::PREPARE")
+ {
+ $mediaTypes = array_filter(array_map("trim", explode(",", $this->parser->getAndClearBuffer("{"))));
+ $this->parser->setMediaTypes($mediaTypes);
+ $this->parser->setState("T_AT_MEDIA");
+ $this->parser->appendToken(new CssAtMediaStartToken($mediaTypes));
+ }
+ elseif ($char === "}" && $state === "T_AT_MEDIA")
+ {
+ $this->parser->appendToken(new CssAtMediaEndToken());
+ $this->parser->clearBuffer();
+ $this->parser->unsetMediaTypes();
+ $this->parser->popState();
+ }
+ else
+ {
+ return false;
+ }
+ return true;
+ }
+}
+
+/**
+ * This {@link aCssToken CSS token} represents the end of a @media at-rule block.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtMediaEndToken extends aCssAtBlockEndToken
+{
+
+}
+
+/**
+ * This {@link aCssToken CSS token} represents the start of a @keyframes at-rule block.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtKeyframesStartToken extends aCssAtBlockStartToken
+{
+ /**
+ * Name of the at-rule.
+ *
+ * @var string
+ */
+ public $AtRuleName = "keyframes";
+ /**
+ * Name
+ *
+ * @var string
+ */
+ public $Name = "";
+ /**
+ * Sets the properties of the @page at-rule.
+ *
+ * @param string $selector Selector
+ * @return void
+ */
+ public function __construct($name, $atRuleName = null)
+ {
+ $this->Name = $name;
+ if (!is_null($atRuleName))
+ {
+ $this->AtRuleName = $atRuleName;
+ }
+ }
+ /**
+ * Implements {@link aCssToken::__toString()}.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ if ($this->AtRuleName === "-moz-keyframes")
+ {
+ return "@-moz-keyframes " . $this->Name . " {";
+ }
+ return "@" . $this->AtRuleName . " " . $this->Name . "{";
+ }
+}
+
+/**
+ * This {@link aCssToken CSS token} represents the start of a ruleset of a @keyframes at-rule block.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtKeyframesRulesetStartToken extends aCssRulesetStartToken
+{
+ /**
+ * Array of selectors.
+ *
+ * @var array
+ */
+ public $Selectors = array();
+ /**
+ * Set the properties of a ruleset token.
+ *
+ * @param array $selectors Selectors of the ruleset
+ * @return void
+ */
+ public function __construct(array $selectors = array())
+ {
+ $this->Selectors = $selectors;
+ }
+ /**
+ * Implements {@link aCssToken::__toString()}.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return implode(",", $this->Selectors) . "{";
+ }
+}
+
+/**
+ * This {@link aCssToken CSS token} represents the end of a ruleset of a @keyframes at-rule block.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtKeyframesRulesetEndToken extends aCssRulesetEndToken
+{
+
+}
+
+/**
+ * This {@link aCssToken CSS token} represents a ruleset declaration of a @keyframes at-rule block.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtKeyframesRulesetDeclarationToken extends aCssDeclarationToken
+{
+
+}
+
+/**
+ * {@link aCssParserPlugin Parser plugin} for parsing @keyframes at-rule blocks, rulesets and declarations.
+ *
+ * @package CssMin/Parser/Plugins
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtKeyframesParserPlugin extends aCssParserPlugin
+{
+ /**
+ * @var string Keyword
+ */
+ private $atRuleName = "";
+ /**
+ * Selectors.
+ *
+ * @var array
+ */
+ private $selectors = array();
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerChars()}.
+ *
+ * @return array
+ */
+ public function getTriggerChars()
+ {
+ return array("@", "{", "}", ":", ",", ";");
+ }
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerStates()}.
+ *
+ * @return array
+ */
+ public function getTriggerStates()
+ {
+ return array("T_DOCUMENT", "T_AT_KEYFRAMES::NAME", "T_AT_KEYFRAMES", "T_AT_KEYFRAMES_RULESETS", "T_AT_KEYFRAMES_RULESET", "T_AT_KEYFRAMES_RULESET_DECLARATION");
+ }
+ /**
+ * Implements {@link aCssParserPlugin::parse()}.
+ *
+ * @param integer $index Current index
+ * @param string $char Current char
+ * @param string $previousChar Previous char
+ * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
+ */
+ public function parse($index, $char, $previousChar, $state)
+ {
+ // Start of @keyframes at-rule block
+ if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 10)) === "@keyframes")
+ {
+ $this->atRuleName = "keyframes";
+ $this->parser->pushState("T_AT_KEYFRAMES::NAME");
+ $this->parser->clearBuffer();
+ return $index + 10;
+ }
+ // Start of @keyframes at-rule block (@-moz-keyframes)
+ elseif ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 15)) === "@-moz-keyframes")
+ {
+ $this->atRuleName = "-moz-keyframes";
+ $this->parser->pushState("T_AT_KEYFRAMES::NAME");
+ $this->parser->clearBuffer();
+ return $index + 15;
+ }
+ // Start of @keyframes at-rule block (@-webkit-keyframes)
+ elseif ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 18)) === "@-webkit-keyframes")
+ {
+ $this->atRuleName = "-webkit-keyframes";
+ $this->parser->pushState("T_AT_KEYFRAMES::NAME");
+ $this->parser->clearBuffer();
+ return $index + 18;
+ }
+ // Start of @keyframes at-rule block (@-o-keyframes)
+ elseif ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 13)) === "@-o-keyframes")
+ {
+ $this->atRuleName = "-o-keyframes";
+ $this->parser->pushState("T_AT_KEYFRAMES::NAME");
+ $this->parser->clearBuffer();
+ return $index + 13;
+ }
+ // Start of @keyframes at-rule block (@-ms-keyframes)
+ elseif ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 14)) === "@-ms-keyframes")
+ {
+ $this->atRuleName = "-ms-keyframes";
+ $this->parser->pushState("T_AT_KEYFRAMES::NAME");
+ $this->parser->clearBuffer();
+ return $index + 14;
+ }
+ // Start of @keyframes rulesets
+ elseif ($char === "{" && $state === "T_AT_KEYFRAMES::NAME")
+ {
+ $name = $this->parser->getAndClearBuffer("{\"'");
+ $this->parser->setState("T_AT_KEYFRAMES_RULESETS");
+ $this->parser->clearBuffer();
+ $this->parser->appendToken(new CssAtKeyframesStartToken($name, $this->atRuleName));
+ }
+ // Start of @keyframe ruleset and selectors
+ if ($char === "," && $state === "T_AT_KEYFRAMES_RULESETS")
+ {
+ $this->selectors[] = $this->parser->getAndClearBuffer(",{");
+ }
+ // Start of a @keyframes ruleset
+ elseif ($char === "{" && $state === "T_AT_KEYFRAMES_RULESETS")
+ {
+ if ($this->parser->getBuffer() !== "")
+ {
+ $this->selectors[] = $this->parser->getAndClearBuffer(",{");
+ $this->parser->pushState("T_AT_KEYFRAMES_RULESET");
+ $this->parser->appendToken(new CssAtKeyframesRulesetStartToken($this->selectors));
+ $this->selectors = array();
+ }
+ }
+ // Start of @keyframes ruleset declaration
+ elseif ($char === ":" && $state === "T_AT_KEYFRAMES_RULESET")
+ {
+ $this->parser->pushState("T_AT_KEYFRAMES_RULESET_DECLARATION");
+ $this->buffer = $this->parser->getAndClearBuffer(":;", true);
+ }
+ // Unterminated @keyframes ruleset declaration
+ elseif ($char === ":" && $state === "T_AT_KEYFRAMES_RULESET_DECLARATION")
+ {
+ // Ignore Internet Explorer filter declarations
+ if ($this->buffer === "filter")
+ {
+ return false;
+ }
+ CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated @keyframes ruleset declaration", $this->buffer . ":" . $this->parser->getBuffer() . "_"));
+ }
+ // End of declaration
+ elseif (($char === ";" || $char === "}") && $state === "T_AT_KEYFRAMES_RULESET_DECLARATION")
+ {
+ $value = $this->parser->getAndClearBuffer(";}");
+ if (strtolower(substr($value, -10, 10)) === "!important")
+ {
+ $value = trim(substr($value, 0, -10));
+ $isImportant = true;
+ }
+ else
+ {
+ $isImportant = false;
+ }
+ $this->parser->popState();
+ $this->parser->appendToken(new CssAtKeyframesRulesetDeclarationToken($this->buffer, $value, $isImportant));
+ // Declaration ends with a right curly brace; so we have to end the ruleset
+ if ($char === "}")
+ {
+ $this->parser->appendToken(new CssAtKeyframesRulesetEndToken());
+ $this->parser->popState();
+ }
+ $this->buffer = "";
+ }
+ // End of @keyframes ruleset
+ elseif ($char === "}" && $state === "T_AT_KEYFRAMES_RULESET")
+ {
+ $this->parser->clearBuffer();
+
+ $this->parser->popState();
+ $this->parser->appendToken(new CssAtKeyframesRulesetEndToken());
+ }
+ // End of @keyframes rulesets
+ elseif ($char === "}" && $state === "T_AT_KEYFRAMES_RULESETS")
+ {
+ $this->parser->clearBuffer();
+ $this->parser->popState();
+ $this->parser->appendToken(new CssAtKeyframesEndToken());
+ }
+ else
+ {
+ return false;
+ }
+ return true;
+ }
+}
+
+/**
+ * This {@link aCssToken CSS token} represents the end of a @keyframes at-rule block.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtKeyframesEndToken extends aCssAtBlockEndToken
+{
+
+}
+
+/**
+ * This {@link aCssToken CSS token} represents a @import at-rule.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1.b1 (2001-02-22)
+ */
+class CssAtImportToken extends aCssToken
+{
+ /**
+ * Import path of the @import at-rule.
+ *
+ * @var string
+ */
+ public $Import = "";
+ /**
+ * Media types of the @import at-rule.
+ *
+ * @var array
+ */
+ public $MediaTypes = array();
+ /**
+ * Set the properties of a @import at-rule token.
+ *
+ * @param string $import Import path
+ * @param array $mediaTypes Media types
+ * @return void
+ */
+ public function __construct($import, $mediaTypes)
+ {
+ $this->Import = $import;
+ $this->MediaTypes = $mediaTypes ? $mediaTypes : array();
+ }
+ /**
+ * Implements {@link aCssToken::__toString()}.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return "@import \"" . $this->Import . "\"" . (count($this->MediaTypes) > 0 ? " " . implode(",", $this->MediaTypes) : ""). ";";
+ }
+}
+
+/**
+ * {@link aCssParserPlugin Parser plugin} for parsing @import at-rule.
+ *
+ * If a @import at-rule was found this plugin will add a {@link CssAtImportToken} to the parser.
+ *
+ * @package CssMin/Parser/Plugins
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtImportParserPlugin extends aCssParserPlugin
+{
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerChars()}.
+ *
+ * @return array
+ */
+ public function getTriggerChars()
+ {
+ return array("@", ";", ",", "\n");
+ }
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerStates()}.
+ *
+ * @return array
+ */
+ public function getTriggerStates()
+ {
+ return array("T_DOCUMENT", "T_AT_IMPORT");
+ }
+ /**
+ * Implements {@link aCssParserPlugin::parse()}.
+ *
+ * @param integer $index Current index
+ * @param string $char Current char
+ * @param string $previousChar Previous char
+ * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
+ */
+ public function parse($index, $char, $previousChar, $state)
+ {
+ if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 7)) === "@import")
+ {
+ $this->parser->pushState("T_AT_IMPORT");
+ $this->parser->clearBuffer();
+ return $index + 7;
+ }
+ elseif (($char === ";" || $char === "\n") && $state === "T_AT_IMPORT")
+ {
+ $this->buffer = $this->parser->getAndClearBuffer(";");
+ $pos = false;
+ foreach (array(")", "\"", "'") as $needle)
+ {
+ if (($pos = strrpos($this->buffer, $needle)) !== false)
+ {
+ break;
+ }
+ }
+ $import = substr($this->buffer, 0, $pos + 1);
+ if (stripos($import, "url(") === 0)
+ {
+ $import = substr($import, 4, -1);
+ }
+ $import = trim($import, " \t\n\r\0\x0B'\"");
+ $mediaTypes = array_filter(array_map("trim", explode(",", trim(substr($this->buffer, $pos + 1), " \t\n\r\0\x0B{"))));
+ if ($pos)
+ {
+ $this->parser->appendToken(new CssAtImportToken($import, $mediaTypes));
+ }
+ else
+ {
+ CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Invalid @import at-rule syntax", $this->buffer));
+ }
+ $this->parser->popState();
+ }
+ else
+ {
+ return false;
+ }
+ return true;
+ }
+}
+
+/**
+ * This {@link aCssToken CSS token} represents the start of a @font-face at-rule block.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtFontFaceStartToken extends aCssAtBlockStartToken
+{
+ /**
+ * Implements {@link aCssToken::__toString()}.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return "@font-face{";
+ }
+}
+
+/**
+ * {@link aCssParserPlugin Parser plugin} for parsing @font-face at-rule block with including declarations.
+ *
+ * Found @font-face at-rule blocks will add a {@link CssAtFontFaceStartToken} and {@link CssAtFontFaceEndToken} to the
+ * parser; including declarations as {@link CssAtFontFaceDeclarationToken}.
+ *
+ * @package CssMin/Parser/Plugins
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtFontFaceParserPlugin extends aCssParserPlugin
+{
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerChars()}.
+ *
+ * @return array
+ */
+ public function getTriggerChars()
+ {
+ return array("@", "{", "}", ":", ";");
+ }
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerStates()}.
+ *
+ * @return array
+ */
+ public function getTriggerStates()
+ {
+ return array("T_DOCUMENT", "T_AT_FONT_FACE::PREPARE", "T_AT_FONT_FACE", "T_AT_FONT_FACE_DECLARATION");
+ }
+ /**
+ * Implements {@link aCssParserPlugin::parse()}.
+ *
+ * @param integer $index Current index
+ * @param string $char Current char
+ * @param string $previousChar Previous char
+ * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
+ */
+ public function parse($index, $char, $previousChar, $state)
+ {
+ // Start of @font-face at-rule block
+ if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 10)) === "@font-face")
+ {
+ $this->parser->pushState("T_AT_FONT_FACE::PREPARE");
+ $this->parser->clearBuffer();
+ return $index + 10 - 1;
+ }
+ // Start of @font-face declarations
+ elseif ($char === "{" && $state === "T_AT_FONT_FACE::PREPARE")
+ {
+ $this->parser->setState("T_AT_FONT_FACE");
+ $this->parser->clearBuffer();
+ $this->parser->appendToken(new CssAtFontFaceStartToken());
+ }
+ // Start of @font-face declaration
+ elseif ($char === ":" && $state === "T_AT_FONT_FACE")
+ {
+ $this->parser->pushState("T_AT_FONT_FACE_DECLARATION");
+ $this->buffer = $this->parser->getAndClearBuffer(":", true);
+ }
+ // Unterminated @font-face declaration
+ elseif ($char === ":" && $state === "T_AT_FONT_FACE_DECLARATION")
+ {
+ // Ignore Internet Explorer filter declarations
+ if ($this->buffer === "filter")
+ {
+ return false;
+ }
+ CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated @font-face declaration", $this->buffer . ":" . $this->parser->getBuffer() . "_"));
+ }
+ // End of @font-face declaration
+ elseif (($char === ";" || $char === "}") && $state === "T_AT_FONT_FACE_DECLARATION")
+ {
+ $value = $this->parser->getAndClearBuffer(";}");
+ if (strtolower(substr($value, -10, 10)) === "!important")
+ {
+ $value = trim(substr($value, 0, -10));
+ $isImportant = true;
+ }
+ else
+ {
+ $isImportant = false;
+ }
+ $this->parser->popState();
+ $this->parser->appendToken(new CssAtFontFaceDeclarationToken($this->buffer, $value, $isImportant));
+ $this->buffer = "";
+ // --
+ if ($char === "}")
+ {
+ $this->parser->appendToken(new CssAtFontFaceEndToken());
+ $this->parser->popState();
+ }
+ }
+ // End of @font-face at-rule block
+ elseif ($char === "}" && $state === "T_AT_FONT_FACE")
+ {
+ $this->parser->appendToken(new CssAtFontFaceEndToken());
+ $this->parser->clearBuffer();
+ $this->parser->popState();
+ }
+ else
+ {
+ return false;
+ }
+ return true;
+ }
+}
+
+/**
+ * This {@link aCssToken CSS token} represents the end of a @font-face at-rule block.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtFontFaceEndToken extends aCssAtBlockEndToken
+{
+
+}
+
+/**
+ * This {@link aCssToken CSS token} represents a declaration of a @font-face at-rule block.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtFontFaceDeclarationToken extends aCssDeclarationToken
+{
+
+}
+
+/**
+ * This {@link aCssToken CSS token} represents a @charset at-rule.
+ *
+ * @package CssMin/Tokens
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtCharsetToken extends aCssToken
+{
+ /**
+ * Charset of the @charset at-rule.
+ *
+ * @var string
+ */
+ public $Charset = "";
+ /**
+ * Set the properties of @charset at-rule token.
+ *
+ * @param string $charset Charset of the @charset at-rule token
+ * @return void
+ */
+ public function __construct($charset)
+ {
+ $this->Charset = $charset;
+ }
+ /**
+ * Implements {@link aCssToken::__toString()}.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return "@charset " . $this->Charset . ";";
+ }
+}
+
+/**
+ * {@link aCssParserPlugin Parser plugin} for parsing @charset at-rule.
+ *
+ * If a @charset at-rule was found this plugin will add a {@link CssAtCharsetToken} to the parser.
+ *
+ * @package CssMin/Parser/Plugins
+ * @link http://code.google.com/p/cssmin/
+ * @author Joe Scylla
+ * @copyright 2008 - 2011 Joe Scylla
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @version 3.0.1
+ */
+class CssAtCharsetParserPlugin extends aCssParserPlugin
+{
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerChars()}.
+ *
+ * @return array
+ */
+ public function getTriggerChars()
+ {
+ return array("@", ";", "\n");
+ }
+ /**
+ * Implements {@link aCssParserPlugin::getTriggerStates()}.
+ *
+ * @return array
+ */
+ public function getTriggerStates()
+ {
+ return array("T_DOCUMENT", "T_AT_CHARSET");
+ }
+ /**
+ * Implements {@link aCssParserPlugin::parse()}.
+ *
+ * @param integer $index Current index
+ * @param string $char Current char
+ * @param string $previousChar Previous char
+ * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
+ */
+ public function parse($index, $char, $previousChar, $state)
+ {
+ if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 8)) === "@charset")
+ {
+ $this->parser->pushState("T_AT_CHARSET");
+ $this->parser->clearBuffer();
+ return $index + 8;
+ }
+ elseif (($char === ";" || $char === "\n") && $state === "T_AT_CHARSET")
+ {
+ $charset = $this->parser->getAndClearBuffer(";");
+ $this->parser->popState();
+ $this->parser->appendToken(new CssAtCharsetToken($charset));
+ }
+ else
+ {
+ return false;
+ }
+ return true;
+ }
+}
+
+?>
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/.coveralls.yml b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/.coveralls.yml
new file mode 100644
index 0000000..4eecff5
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/.coveralls.yml
@@ -0,0 +1,3 @@
+service_name: travis-ci
+coverage_clover: clover.xml
+json_path: coveralls-upload.json
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/.gitignore b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/.gitignore
new file mode 100644
index 0000000..c98f719
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/.gitignore
@@ -0,0 +1,2 @@
+/coverage/
+/vendor/
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/.scrutinizer.yml b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/.scrutinizer.yml
new file mode 100644
index 0000000..4efaf59
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/.scrutinizer.yml
@@ -0,0 +1,19 @@
+filter:
+ excluded_paths:
+ - 'vendor/*'
+ - 'tests/*'
+before_commands:
+ - 'composer install'
+tools:
+ php_analyzer: true
+ php_mess_detector: true
+ php_code_sniffer:
+ config:
+ standard: PSR1
+ sensiolabs_security_checker: true
+ php_loc:
+ excluded_dirs:
+ - vendor
+ - tests
+ php_pdepend: true
+ php_sim: true
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/.travis.yml b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/.travis.yml
new file mode 100644
index 0000000..eca1932
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/.travis.yml
@@ -0,0 +1,9 @@
+language: php
+sudo: false
+php:
+ - 5.6
+ - 7.0
+ - hhvm
+install: composer install
+script: ./vendor/bin/phpunit --coverage-clover clover.xml
+after_success: sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then ./vendor/bin/coveralls -v; fi'
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/CONTRIBUTING.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/CONTRIBUTING.md
new file mode 100644
index 0000000..478fae4
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/CONTRIBUTING.md
@@ -0,0 +1,38 @@
+# Contribution Guidelines
+This project is wide open to contributions. You can use GitHub to [report
+issues][issues] or [submit pull requests][pull-requests]. When opening pull
+requests, it is recommended to follow these guidelines in order to grease the
+wheels, so to speak.
+
+Please include as many details as you can in any issues and pull requests.
+Understanding how you are using the library and exactly what problems you are
+having can also help make things move quickly.
+
+## Building
+There is an included [build script][build-script] that you can execute locally
+to run the code through the [PSR-1 coding standard][psr-1] and the
+[PHPUnit][phpunit] test suite. Code coverage is kept at 100% according to
+PHPUnit's code coverage report. This is inforced using
+[coveralls][coveralls].
+
+Alternatively, you can use [Docker][docker] and/or [Docker
+Compose][docker-compose] to execute the build:
+```bash
+docker-compose run build
+```
+
+## Automated builds
+Pull requests will automatically have a build kicked off on [Travis
+CI][travis-ci] and [Scrutinizer CI][scrutinizer-ci]. The results of these
+builds are monitored closely for all pull requests.
+
+[issues]: https://github.com/nubs/random-name-generator/issues
+[pull-requests]: https://github.com/nubs/random-name-generator/pulls
+[build-script]: https://github.com/nubs/random-name-generator/blob/master/build.php
+[psr-1]: http://www.php-fig.org/psr/psr-1/ "PSR-1 - Basic Coding Standard"
+[phpunit]: http://phpunit.de/ "PHPUnit - The PHP Testing Framework"
+[travis-ci]: https://travis-ci.org/nubs/random-name-generator
+[scrutinizer-ci]: https://scrutinizer-ci.com/g/nubs/random-name-generator/
+[coveralls]: https://coveralls.io/github/nubs/random-name-generator
+[docker]: https://docker.com/ "Docker - Build, Ship, and Run Any App, Anywhere"
+[docker-compose]: https://www.docker.com/docker-compose
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/Dockerfile.tests b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/Dockerfile.tests
new file mode 100644
index 0000000..50b01c1
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/Dockerfile.tests
@@ -0,0 +1,5 @@
+FROM nubs/phpunit
+
+MAINTAINER Spencer Rinehart
+
+CMD ["./build.php"]
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/LICENSE b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/LICENSE
new file mode 100644
index 0000000..4efa9e9
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014-2016 Spencer Rinehart
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/README.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/README.md
new file mode 100644
index 0000000..70d76cf
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/README.md
@@ -0,0 +1,91 @@
+# Random Name Generator
+A PHP library to create interesting, sometimes entertaining, random names.
+
+[](https://travis-ci.org/nubs/random-name-generator)
+[](https://scrutinizer-ci.com/g/nubs/random-name-generator/)
+[](https://coveralls.io/github/nubs/random-name-generator?branch=master)
+
+[](https://packagist.org/packages/nubs/random-name-generator)
+[](https://packagist.org/packages/nubs/random-name-generator)
+[](https://packagist.org/packages/nubs/random-name-generator)
+
+[](https://www.versioneye.com/user/projects/537d561814c15855aa000019)
+
+## Requirements
+This library requires PHP 5.6, or newer.
+
+## Installation
+This package uses [composer](https://getcomposer.org) so you can just add
+`nubs/random-name-generator` as a dependency to your `composer.json` file or
+execute the following command:
+
+```bash
+composer require nubs/random-name-generator
+```
+
+## Generators
+
+### All
+The "all" generator will utilize all other configured generators to generate
+random names. It will select from the list of generators randomly and then
+use them to generate a random name using their functionality.
+
+#### Usage
+```php
+$generator = \Nubs\RandomNameGenerator\All::create();
+echo $generator->getName();
+```
+
+Alternatively, if you want to configure/build the generators to use instead of
+using all of the available generators, you can construct them yourself:
+
+```php
+$generator = new \Nubs\RandomNameGenerator\All(
+ [
+ new \Nubs\RandomNameGenerator\Alliteration(),
+ new \Nubs\RandomNameGenerator\Vgng()
+ ]
+);
+```
+
+### Video Game Names
+The video game name generator is based off of [prior](http://videogamena.me/)
+[art](https://github.com/nullpuppy/vgng). It will generate unique names based
+off of "typical" video games.
+
+#### Examples
+* *Kamikaze Bubblegum Warrior*
+* *Rockin' Valkyrie Gaiden*
+* *Neurotic Jackhammer Detective*
+* *My Little Mountain Climber Conflict*
+* *Small-Time Princess vs. The Space Mutants*
+
+You can also use [this web example](http://sam.sbat.com/) to see more example
+video game names generated by this library.
+
+#### Usage
+```php
+$generator = new \Nubs\RandomNameGenerator\Vgng();
+echo $generator->getName();
+```
+
+## Alliterative Names
+The alliteration name generator is based off of a list of
+[adjectives](http://grammar.yourdictionary.com/parts-of-speech/adjectives/list-of-adjective-words.html)
+and a list of [animals](https://animalcorner.co.uk/animals/).
+
+#### Examples
+* *Agreeable Anaconda*
+* *Disturbed Duck*
+* *Misty Meerkat*
+* *Prickly Pig*
+
+#### Usage
+```php
+$generator = new \Nubs\RandomNameGenerator\Alliteration();
+echo $generator->getName();
+```
+
+## License
+random-name-generator is licensed under the MIT license. See
+[LICENSE](LICENSE) for the full license text.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/build.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/build.php
new file mode 100644
index 0000000..f285d55
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/build.php
@@ -0,0 +1,25 @@
+#!/usr/bin/env php
+process(['standard' => ['PSR1'], 'files' => ['src', 'tests', 'build.php']]);
+if ($phpcsViolations > 0) {
+ exit(1);
+}
+
+$phpunitConfiguration = PHPUnit_Util_Configuration::getInstance(__DIR__ . '/phpunit.xml');
+$phpunitArguments = ['coverageHtml' => __DIR__ . '/coverage', 'configuration' => $phpunitConfiguration];
+$testRunner = new PHPUnit_TextUI_TestRunner();
+$result = $testRunner->doRun($phpunitConfiguration->getTestSuiteConfiguration(), $phpunitArguments, false);
+if (!$result->wasSuccessful()) {
+ exit(1);
+}
+
+$coverageReport = $result->getCodeCoverage()->getReport();
+if ($coverageReport->getNumExecutedLines() !== $coverageReport->getNumExecutableLines()) {
+ file_put_contents('php://stderr', "Code coverage was NOT 100%\n");
+ exit(1);
+}
+
+file_put_contents('php://stderr', "Code coverage was 100%\n");
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/composer.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/composer.json
new file mode 100644
index 0000000..d91fbff
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/composer.json
@@ -0,0 +1,30 @@
+{
+ "name": "nubs/random-name-generator",
+ "description": "A library to create interesting, sometimes entertaining, random names.",
+ "keywords": ["random", "generator", "video game", "alliteration"],
+ "authors": [
+ {
+ "name": "Spencer Rinehart",
+ "email": "anubis@overthemonkey.com",
+ "role": "Developer"
+ }
+ ],
+ "license": "MIT",
+ "require": {
+ "php": "~5.6 || ~7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~5.0",
+ "satooshi/php-coveralls": "~1.0",
+ "cinam/randomizer": ">=1.1.1,<2.0",
+ "squizlabs/php_codesniffer": "~2.3"
+ },
+ "autoload": {
+ "psr-4": {
+ "Nubs\\RandomNameGenerator\\": "src"
+ }
+ },
+ "scripts": {
+ "test": "./build.php"
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/composer.lock b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/composer.lock
new file mode 100644
index 0000000..77d0fdb
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/composer.lock
@@ -0,0 +1,1963 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "This file is @generated automatically"
+ ],
+ "hash": "4b2c771eec058567f987575b9b3199a2",
+ "content-hash": "826850e9b398ef46a5bc5fec486788f6",
+ "packages": [],
+ "packages-dev": [
+ {
+ "name": "cinam/randomizer",
+ "version": "1.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/cinam/randomizer.git",
+ "reference": "beca7e3ad5b93cebdb897cd47247b19a109b970f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/cinam/randomizer/zipball/beca7e3ad5b93cebdb897cd47247b19a109b970f",
+ "reference": "beca7e3ad5b93cebdb897cd47247b19a109b970f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "3.7.*"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Cinam\\Randomizer\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "cinam",
+ "email": "cinam@hotmail.com"
+ }
+ ],
+ "description": "Tools for generating random values.",
+ "homepage": "http://github.com/cinam/randomizer",
+ "keywords": [
+ "random",
+ "random numbers",
+ "random values"
+ ],
+ "time": "2014-06-01 07:27:32"
+ },
+ {
+ "name": "doctrine/instantiator",
+ "version": "1.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/instantiator.git",
+ "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d",
+ "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3,<8.0-DEV"
+ },
+ "require-dev": {
+ "athletic/athletic": "~0.1.8",
+ "ext-pdo": "*",
+ "ext-phar": "*",
+ "phpunit/phpunit": "~4.0",
+ "squizlabs/php_codesniffer": "~2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com",
+ "homepage": "http://ocramius.github.com/"
+ }
+ ],
+ "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
+ "homepage": "https://github.com/doctrine/instantiator",
+ "keywords": [
+ "constructor",
+ "instantiate"
+ ],
+ "time": "2015-06-14 21:17:01"
+ },
+ {
+ "name": "guzzle/guzzle",
+ "version": "v3.9.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/guzzle3.git",
+ "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/0645b70d953bc1c067bbc8d5bc53194706b628d9",
+ "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9",
+ "shasum": ""
+ },
+ "require": {
+ "ext-curl": "*",
+ "php": ">=5.3.3",
+ "symfony/event-dispatcher": "~2.1"
+ },
+ "replace": {
+ "guzzle/batch": "self.version",
+ "guzzle/cache": "self.version",
+ "guzzle/common": "self.version",
+ "guzzle/http": "self.version",
+ "guzzle/inflection": "self.version",
+ "guzzle/iterator": "self.version",
+ "guzzle/log": "self.version",
+ "guzzle/parser": "self.version",
+ "guzzle/plugin": "self.version",
+ "guzzle/plugin-async": "self.version",
+ "guzzle/plugin-backoff": "self.version",
+ "guzzle/plugin-cache": "self.version",
+ "guzzle/plugin-cookie": "self.version",
+ "guzzle/plugin-curlauth": "self.version",
+ "guzzle/plugin-error-response": "self.version",
+ "guzzle/plugin-history": "self.version",
+ "guzzle/plugin-log": "self.version",
+ "guzzle/plugin-md5": "self.version",
+ "guzzle/plugin-mock": "self.version",
+ "guzzle/plugin-oauth": "self.version",
+ "guzzle/service": "self.version",
+ "guzzle/stream": "self.version"
+ },
+ "require-dev": {
+ "doctrine/cache": "~1.3",
+ "monolog/monolog": "~1.0",
+ "phpunit/phpunit": "3.7.*",
+ "psr/log": "~1.0",
+ "symfony/class-loader": "~2.1",
+ "zendframework/zend-cache": "2.*,<2.3",
+ "zendframework/zend-log": "2.*,<2.3"
+ },
+ "suggest": {
+ "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.9-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "Guzzle": "src/",
+ "Guzzle\\Tests": "tests/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Guzzle Community",
+ "homepage": "https://github.com/guzzle/guzzle/contributors"
+ }
+ ],
+ "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": [
+ "client",
+ "curl",
+ "framework",
+ "http",
+ "http client",
+ "rest",
+ "web service"
+ ],
+ "abandoned": "guzzlehttp/guzzle",
+ "time": "2015-03-18 18:23:50"
+ },
+ {
+ "name": "myclabs/deep-copy",
+ "version": "1.5.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/DeepCopy.git",
+ "reference": "a8773992b362b58498eed24bf85005f363c34771"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/a8773992b362b58498eed24bf85005f363c34771",
+ "reference": "a8773992b362b58498eed24bf85005f363c34771",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "doctrine/collections": "1.*",
+ "phpunit/phpunit": "~4.1"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "DeepCopy\\": "src/DeepCopy/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Create deep copies (clones) of your objects",
+ "homepage": "https://github.com/myclabs/DeepCopy",
+ "keywords": [
+ "clone",
+ "copy",
+ "duplicate",
+ "object",
+ "object graph"
+ ],
+ "time": "2015-11-20 12:04:31"
+ },
+ {
+ "name": "phpdocumentor/reflection-common",
+ "version": "1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
+ "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c",
+ "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.5"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.6"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": [
+ "src"
+ ]
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jaap van Otterdijk",
+ "email": "opensource@ijaap.nl"
+ }
+ ],
+ "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
+ "homepage": "http://www.phpdoc.org",
+ "keywords": [
+ "FQSEN",
+ "phpDocumentor",
+ "phpdoc",
+ "reflection",
+ "static analysis"
+ ],
+ "time": "2015-12-27 11:43:31"
+ },
+ {
+ "name": "phpdocumentor/reflection-docblock",
+ "version": "3.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
+ "reference": "9270140b940ff02e58ec577c237274e92cd40cdd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9270140b940ff02e58ec577c237274e92cd40cdd",
+ "reference": "9270140b940ff02e58ec577c237274e92cd40cdd",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.5",
+ "phpdocumentor/reflection-common": "^1.0@dev",
+ "phpdocumentor/type-resolver": "^0.2.0",
+ "webmozart/assert": "^1.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "^0.9.4",
+ "phpunit/phpunit": "^4.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": [
+ "src/"
+ ]
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mike van Riel",
+ "email": "me@mikevanriel.com"
+ }
+ ],
+ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
+ "time": "2016-06-10 09:48:41"
+ },
+ {
+ "name": "phpdocumentor/type-resolver",
+ "version": "0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/TypeResolver.git",
+ "reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b39c7a5b194f9ed7bd0dd345c751007a41862443",
+ "reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.5",
+ "phpdocumentor/reflection-common": "^1.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "^0.9.4",
+ "phpunit/phpunit": "^5.2||^4.8.24"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": [
+ "src/"
+ ]
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mike van Riel",
+ "email": "me@mikevanriel.com"
+ }
+ ],
+ "time": "2016-06-10 07:14:17"
+ },
+ {
+ "name": "phpspec/prophecy",
+ "version": "v1.6.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpspec/prophecy.git",
+ "reference": "58a8137754bc24b25740d4281399a4a3596058e0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpspec/prophecy/zipball/58a8137754bc24b25740d4281399a4a3596058e0",
+ "reference": "58a8137754bc24b25740d4281399a4a3596058e0",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/instantiator": "^1.0.2",
+ "php": "^5.3|^7.0",
+ "phpdocumentor/reflection-docblock": "^2.0|^3.0.2",
+ "sebastian/comparator": "^1.1",
+ "sebastian/recursion-context": "^1.0"
+ },
+ "require-dev": {
+ "phpspec/phpspec": "^2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.6.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "Prophecy\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Konstantin Kudryashov",
+ "email": "ever.zet@gmail.com",
+ "homepage": "http://everzet.com"
+ },
+ {
+ "name": "Marcello Duarte",
+ "email": "marcello.duarte@gmail.com"
+ }
+ ],
+ "description": "Highly opinionated mocking framework for PHP 5.3+",
+ "homepage": "https://github.com/phpspec/prophecy",
+ "keywords": [
+ "Double",
+ "Dummy",
+ "fake",
+ "mock",
+ "spy",
+ "stub"
+ ],
+ "time": "2016-06-07 08:13:47"
+ },
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "5f3f7e736d6319d5f1fc402aff8b026da26709a3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5f3f7e736d6319d5f1fc402aff8b026da26709a3",
+ "reference": "5f3f7e736d6319d5f1fc402aff8b026da26709a3",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.6 || ^7.0",
+ "phpunit/php-file-iterator": "~1.3",
+ "phpunit/php-text-template": "~1.2",
+ "phpunit/php-token-stream": "^1.4.2",
+ "sebastian/code-unit-reverse-lookup": "~1.0",
+ "sebastian/environment": "^1.3.2 || ^2.0",
+ "sebastian/version": "~1.0|~2.0"
+ },
+ "require-dev": {
+ "ext-xdebug": ">=2.1.4",
+ "phpunit/phpunit": "^5.4"
+ },
+ "suggest": {
+ "ext-dom": "*",
+ "ext-xdebug": ">=2.4.0",
+ "ext-xmlwriter": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sb@sebastian-bergmann.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "keywords": [
+ "coverage",
+ "testing",
+ "xunit"
+ ],
+ "time": "2016-07-26 14:39:29"
+ },
+ {
+ "name": "phpunit/php-file-iterator",
+ "version": "1.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0",
+ "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.4.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sb@sebastian-bergmann.de",
+ "role": "lead"
+ }
+ ],
+ "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+ "keywords": [
+ "filesystem",
+ "iterator"
+ ],
+ "time": "2015-06-21 13:08:43"
+ },
+ {
+ "name": "phpunit/php-text-template",
+ "version": "1.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
+ "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Simple template engine.",
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+ "keywords": [
+ "template"
+ ],
+ "time": "2015-06-21 13:50:34"
+ },
+ {
+ "name": "phpunit/php-timer",
+ "version": "1.0.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260",
+ "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4|~5"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sb@sebastian-bergmann.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Utility class for timing",
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
+ "keywords": [
+ "timer"
+ ],
+ "time": "2016-05-12 18:03:57"
+ },
+ {
+ "name": "phpunit/php-token-stream",
+ "version": "1.4.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-token-stream.git",
+ "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da",
+ "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da",
+ "shasum": ""
+ },
+ "require": {
+ "ext-tokenizer": "*",
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.4-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Wrapper around PHP's tokenizer extension.",
+ "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
+ "keywords": [
+ "tokenizer"
+ ],
+ "time": "2015-09-15 10:49:45"
+ },
+ {
+ "name": "phpunit/phpunit",
+ "version": "5.5.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "46ec2d1522ae8c9a12aca6b7650e0be78bbb0502"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/46ec2d1522ae8c9a12aca6b7650e0be78bbb0502",
+ "reference": "46ec2d1522ae8c9a12aca6b7650e0be78bbb0502",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-pcre": "*",
+ "ext-reflection": "*",
+ "ext-spl": "*",
+ "myclabs/deep-copy": "~1.3",
+ "php": "^5.6 || ^7.0",
+ "phpspec/prophecy": "^1.3.1",
+ "phpunit/php-code-coverage": "^4.0.1",
+ "phpunit/php-file-iterator": "~1.4",
+ "phpunit/php-text-template": "~1.2",
+ "phpunit/php-timer": "^1.0.6",
+ "phpunit/phpunit-mock-objects": "^3.2",
+ "sebastian/comparator": "~1.1",
+ "sebastian/diff": "~1.2",
+ "sebastian/environment": "^1.3 || ^2.0",
+ "sebastian/exporter": "~1.2",
+ "sebastian/global-state": "~1.0",
+ "sebastian/object-enumerator": "~1.0",
+ "sebastian/resource-operations": "~1.0",
+ "sebastian/version": "~1.0|~2.0",
+ "symfony/yaml": "~2.1|~3.0"
+ },
+ "conflict": {
+ "phpdocumentor/reflection-docblock": "3.0.2"
+ },
+ "suggest": {
+ "phpunit/php-invoker": "~1.1"
+ },
+ "bin": [
+ "phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.5.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "The PHP Unit Testing framework.",
+ "homepage": "https://phpunit.de/",
+ "keywords": [
+ "phpunit",
+ "testing",
+ "xunit"
+ ],
+ "time": "2016-08-18 11:10:44"
+ },
+ {
+ "name": "phpunit/phpunit-mock-objects",
+ "version": "3.2.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
+ "reference": "4e83390f64e7ce04fcaec2ce95cd72823b431d19"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/4e83390f64e7ce04fcaec2ce95cd72823b431d19",
+ "reference": "4e83390f64e7ce04fcaec2ce95cd72823b431d19",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/instantiator": "^1.0.2",
+ "php": "^5.6 || ^7.0",
+ "phpunit/php-text-template": "^1.2",
+ "sebastian/exporter": "^1.2"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^5.4"
+ },
+ "suggest": {
+ "ext-soap": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.2.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sb@sebastian-bergmann.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Mock Object library for PHPUnit",
+ "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
+ "keywords": [
+ "mock",
+ "xunit"
+ ],
+ "time": "2016-08-17 09:33:51"
+ },
+ {
+ "name": "psr/log",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b",
+ "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b",
+ "shasum": ""
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "Psr\\Log\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for logging libraries",
+ "keywords": [
+ "log",
+ "psr",
+ "psr-3"
+ ],
+ "time": "2012-12-21 11:40:51"
+ },
+ {
+ "name": "satooshi/php-coveralls",
+ "version": "v1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/satooshi/php-coveralls.git",
+ "reference": "da51d304fe8622bf9a6da39a8446e7afd432115c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/satooshi/php-coveralls/zipball/da51d304fe8622bf9a6da39a8446e7afd432115c",
+ "reference": "da51d304fe8622bf9a6da39a8446e7afd432115c",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-simplexml": "*",
+ "guzzle/guzzle": "^2.8|^3.0",
+ "php": ">=5.3.3",
+ "psr/log": "^1.0",
+ "symfony/config": "^2.1|^3.0",
+ "symfony/console": "^2.1|^3.0",
+ "symfony/stopwatch": "^2.0|^3.0",
+ "symfony/yaml": "^2.0|^3.0"
+ },
+ "suggest": {
+ "symfony/http-kernel": "Allows Symfony integration"
+ },
+ "bin": [
+ "bin/coveralls"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Satooshi\\": "src/Satooshi/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Kitamura Satoshi",
+ "email": "with.no.parachute@gmail.com",
+ "homepage": "https://www.facebook.com/satooshi.jp"
+ }
+ ],
+ "description": "PHP client library for Coveralls API",
+ "homepage": "https://github.com/satooshi/php-coveralls",
+ "keywords": [
+ "ci",
+ "coverage",
+ "github",
+ "test"
+ ],
+ "time": "2016-01-20 17:35:46"
+ },
+ {
+ "name": "sebastian/code-unit-reverse-lookup",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+ "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/c36f5e7cfce482fde5bf8d10d41a53591e0198fe",
+ "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Looks up which function or method a line of code belongs to",
+ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+ "time": "2016-02-13 06:45:14"
+ },
+ {
+ "name": "sebastian/comparator",
+ "version": "1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "937efb279bd37a375bcadf584dec0726f84dbf22"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22",
+ "reference": "937efb279bd37a375bcadf584dec0726f84dbf22",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3",
+ "sebastian/diff": "~1.2",
+ "sebastian/exporter": "~1.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.4"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.2.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@2bepublished.at"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides the functionality to compare PHP values for equality",
+ "homepage": "http://www.github.com/sebastianbergmann/comparator",
+ "keywords": [
+ "comparator",
+ "compare",
+ "equality"
+ ],
+ "time": "2015-07-26 15:48:44"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "1.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e",
+ "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.4-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Kore Nordmann",
+ "email": "mail@kore-nordmann.de"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Diff implementation",
+ "homepage": "https://github.com/sebastianbergmann/diff",
+ "keywords": [
+ "diff"
+ ],
+ "time": "2015-12-08 07:14:41"
+ },
+ {
+ "name": "sebastian/environment",
+ "version": "1.3.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/environment.git",
+ "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/be2c607e43ce4c89ecd60e75c6a85c126e754aea",
+ "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3.3 || ^7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8 || ^5.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.3.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides functionality to handle HHVM/PHP environments",
+ "homepage": "http://www.github.com/sebastianbergmann/environment",
+ "keywords": [
+ "Xdebug",
+ "environment",
+ "hhvm"
+ ],
+ "time": "2016-08-18 05:49:44"
+ },
+ {
+ "name": "sebastian/exporter",
+ "version": "1.2.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/exporter.git",
+ "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4",
+ "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3",
+ "sebastian/recursion-context": "~1.0"
+ },
+ "require-dev": {
+ "ext-mbstring": "*",
+ "phpunit/phpunit": "~4.4"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.3.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@2bepublished.at"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ }
+ ],
+ "description": "Provides the functionality to export PHP variables for visualization",
+ "homepage": "http://www.github.com/sebastianbergmann/exporter",
+ "keywords": [
+ "export",
+ "exporter"
+ ],
+ "time": "2016-06-17 09:04:28"
+ },
+ {
+ "name": "sebastian/global-state",
+ "version": "1.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4",
+ "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.2"
+ },
+ "suggest": {
+ "ext-uopz": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Snapshotting of global state",
+ "homepage": "http://www.github.com/sebastianbergmann/global-state",
+ "keywords": [
+ "global state"
+ ],
+ "time": "2015-10-12 03:26:01"
+ },
+ {
+ "name": "sebastian/object-enumerator",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "d4ca2fb70344987502567bc50081c03e6192fb26"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/d4ca2fb70344987502567bc50081c03e6192fb26",
+ "reference": "d4ca2fb70344987502567bc50081c03e6192fb26",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6",
+ "sebastian/recursion-context": "~1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+ "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+ "time": "2016-01-28 13:25:10"
+ },
+ {
+ "name": "sebastian/recursion-context",
+ "version": "1.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "913401df809e99e4f47b27cdd781f4a258d58791"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791",
+ "reference": "913401df809e99e4f47b27cdd781f4a258d58791",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.4"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ }
+ ],
+ "description": "Provides functionality to recursively process PHP variables",
+ "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
+ "time": "2015-11-11 19:50:13"
+ },
+ {
+ "name": "sebastian/resource-operations",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/resource-operations.git",
+ "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
+ "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides a list of PHP built-in functions that operate on resources",
+ "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
+ "time": "2015-07-28 20:34:47"
+ },
+ {
+ "name": "sebastian/version",
+ "version": "2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5",
+ "reference": "c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+ "homepage": "https://github.com/sebastianbergmann/version",
+ "time": "2016-02-04 12:56:52"
+ },
+ {
+ "name": "squizlabs/php_codesniffer",
+ "version": "2.6.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
+ "reference": "4edb770cb853def6e60c93abb088ad5ac2010c83"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/4edb770cb853def6e60c93abb088ad5ac2010c83",
+ "reference": "4edb770cb853def6e60c93abb088ad5ac2010c83",
+ "shasum": ""
+ },
+ "require": {
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": ">=5.1.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.0"
+ },
+ "bin": [
+ "scripts/phpcs",
+ "scripts/phpcbf"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "CodeSniffer.php",
+ "CodeSniffer/CLI.php",
+ "CodeSniffer/Exception.php",
+ "CodeSniffer/File.php",
+ "CodeSniffer/Fixer.php",
+ "CodeSniffer/Report.php",
+ "CodeSniffer/Reporting.php",
+ "CodeSniffer/Sniff.php",
+ "CodeSniffer/Tokens.php",
+ "CodeSniffer/Reports/",
+ "CodeSniffer/Tokenizers/",
+ "CodeSniffer/DocGenerators/",
+ "CodeSniffer/Standards/AbstractPatternSniff.php",
+ "CodeSniffer/Standards/AbstractScopeSniff.php",
+ "CodeSniffer/Standards/AbstractVariableSniff.php",
+ "CodeSniffer/Standards/IncorrectPatternException.php",
+ "CodeSniffer/Standards/Generic/Sniffs/",
+ "CodeSniffer/Standards/MySource/Sniffs/",
+ "CodeSniffer/Standards/PEAR/Sniffs/",
+ "CodeSniffer/Standards/PSR1/Sniffs/",
+ "CodeSniffer/Standards/PSR2/Sniffs/",
+ "CodeSniffer/Standards/Squiz/Sniffs/",
+ "CodeSniffer/Standards/Zend/Sniffs/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Greg Sherwood",
+ "role": "lead"
+ }
+ ],
+ "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
+ "homepage": "http://www.squizlabs.com/php-codesniffer",
+ "keywords": [
+ "phpcs",
+ "standards"
+ ],
+ "time": "2016-07-13 23:29:13"
+ },
+ {
+ "name": "symfony/config",
+ "version": "v3.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/config.git",
+ "reference": "a7630397b91be09cdd2fe57fd13612e258700598"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/config/zipball/a7630397b91be09cdd2fe57fd13612e258700598",
+ "reference": "a7630397b91be09cdd2fe57fd13612e258700598",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.5.9",
+ "symfony/filesystem": "~2.8|~3.0"
+ },
+ "suggest": {
+ "symfony/yaml": "To use the yaml reference dumper"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.1-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Config\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Config Component",
+ "homepage": "https://symfony.com",
+ "time": "2016-07-26 08:04:17"
+ },
+ {
+ "name": "symfony/console",
+ "version": "v3.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/console.git",
+ "reference": "f9e638e8149e9e41b570ff092f8007c477ef0ce5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/console/zipball/f9e638e8149e9e41b570ff092f8007c477ef0ce5",
+ "reference": "f9e638e8149e9e41b570ff092f8007c477ef0ce5",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.5.9",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "require-dev": {
+ "psr/log": "~1.0",
+ "symfony/event-dispatcher": "~2.8|~3.0",
+ "symfony/process": "~2.8|~3.0"
+ },
+ "suggest": {
+ "psr/log": "For using the console logger",
+ "symfony/event-dispatcher": "",
+ "symfony/process": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.1-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Console\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Console Component",
+ "homepage": "https://symfony.com",
+ "time": "2016-07-26 08:04:17"
+ },
+ {
+ "name": "symfony/event-dispatcher",
+ "version": "v2.8.9",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/event-dispatcher.git",
+ "reference": "889983a79a043dfda68f38c38b6dba092dd49cd8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/889983a79a043dfda68f38c38b6dba092dd49cd8",
+ "reference": "889983a79a043dfda68f38c38b6dba092dd49cd8",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.9"
+ },
+ "require-dev": {
+ "psr/log": "~1.0",
+ "symfony/config": "~2.0,>=2.0.5|~3.0.0",
+ "symfony/dependency-injection": "~2.6|~3.0.0",
+ "symfony/expression-language": "~2.6|~3.0.0",
+ "symfony/stopwatch": "~2.3|~3.0.0"
+ },
+ "suggest": {
+ "symfony/dependency-injection": "",
+ "symfony/http-kernel": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.8-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\EventDispatcher\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony EventDispatcher Component",
+ "homepage": "https://symfony.com",
+ "time": "2016-07-28 16:56:28"
+ },
+ {
+ "name": "symfony/filesystem",
+ "version": "v3.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/filesystem.git",
+ "reference": "bb29adceb552d202b6416ede373529338136e84f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/bb29adceb552d202b6416ede373529338136e84f",
+ "reference": "bb29adceb552d202b6416ede373529338136e84f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.5.9"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.1-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Filesystem\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Filesystem Component",
+ "homepage": "https://symfony.com",
+ "time": "2016-07-20 05:44:26"
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "dff51f72b0706335131b00a7f49606168c582594"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/dff51f72b0706335131b00a7f49606168c582594",
+ "reference": "dff51f72b0706335131b00a7f49606168c582594",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.2-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "time": "2016-05-18 14:26:46"
+ },
+ {
+ "name": "symfony/stopwatch",
+ "version": "v3.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/stopwatch.git",
+ "reference": "bb42806b12c5f89db4ebf64af6741afe6d8457e1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/stopwatch/zipball/bb42806b12c5f89db4ebf64af6741afe6d8457e1",
+ "reference": "bb42806b12c5f89db4ebf64af6741afe6d8457e1",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.5.9"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.1-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Stopwatch\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Stopwatch Component",
+ "homepage": "https://symfony.com",
+ "time": "2016-06-29 05:41:56"
+ },
+ {
+ "name": "symfony/yaml",
+ "version": "v3.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/yaml.git",
+ "reference": "1819adf2066880c7967df7180f4f662b6f0567ac"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/1819adf2066880c7967df7180f4f662b6f0567ac",
+ "reference": "1819adf2066880c7967df7180f4f662b6f0567ac",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.5.9"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.1-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Yaml\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Yaml Component",
+ "homepage": "https://symfony.com",
+ "time": "2016-07-17 14:02:08"
+ },
+ {
+ "name": "webmozart/assert",
+ "version": "1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/webmozart/assert.git",
+ "reference": "bb2d123231c095735130cc8f6d31385a44c7b308"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/webmozart/assert/zipball/bb2d123231c095735130cc8f6d31385a44c7b308",
+ "reference": "bb2d123231c095735130cc8f6d31385a44c7b308",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3.3|^7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.6",
+ "sebastian/version": "^1.0.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.2-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Webmozart\\Assert\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "Assertions to validate method input/output with nice error messages.",
+ "keywords": [
+ "assert",
+ "check",
+ "validate"
+ ],
+ "time": "2016-08-09 15:02:57"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "php": "~5.6 || ~7.0"
+ },
+ "platform-dev": []
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/docker-compose.yml b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/docker-compose.yml
new file mode 100644
index 0000000..e52fe45
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/docker-compose.yml
@@ -0,0 +1,8 @@
+version: "2"
+services:
+ build:
+ build:
+ context: .
+ dockerfile: Dockerfile.tests
+ volumes:
+ - .:/code
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/phpunit.xml b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/phpunit.xml
new file mode 100644
index 0000000..dc845da
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/phpunit.xml
@@ -0,0 +1,10 @@
+
+
+ ./tests
+
+
+
+ src
+
+
+
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/src/AbstractGenerator.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/src/AbstractGenerator.php
new file mode 100644
index 0000000..abfae12
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/src/AbstractGenerator.php
@@ -0,0 +1,19 @@
+getName();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/src/All.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/src/All.php
new file mode 100644
index 0000000..d044c74
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/src/All.php
@@ -0,0 +1,62 @@
+_generators = $generators;
+ $this->_randomizer = $randomizer;
+ }
+
+ /**
+ * Constructs an All Generator using the default list of generators.
+ *
+ * @api
+ * @param \Cinam\Randomizer\Randomizer $randomizer The random number generator.
+ * @return \Nubs\RandomNameGenerator\All The constructed generator.
+ */
+ public static function create(Randomizer $randomizer = null)
+ {
+ return new self([new Alliteration($randomizer), new Vgng($randomizer)], $randomizer);
+ }
+
+ /**
+ * Gets a randomly generated name using the configured generators.
+ *
+ * @api
+ * @return string A random name.
+ */
+ public function getName()
+ {
+ return $this->_getRandomGenerator()->getName();
+ }
+
+ /**
+ * Get a random generator from the list of generators.
+ *
+ * @return \Nubs\RandomNameGenerator\Generator A random generator.
+ */
+ protected function _getRandomGenerator()
+ {
+ return $this->_randomizer ? $this->_randomizer->getArrayValue($this->_generators) : $this->_generators[array_rand($this->_generators)];
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/src/Alliteration.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/src/Alliteration.php
new file mode 100644
index 0000000..68ef3a2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/src/Alliteration.php
@@ -0,0 +1,59 @@
+_randomizer = $randomizer;
+ $this->_adjectives = file(__DIR__ . '/adjectives.txt', FILE_IGNORE_NEW_LINES);
+ $this->_nouns = file(__DIR__ . '/nouns.txt', FILE_IGNORE_NEW_LINES);
+ }
+
+ /**
+ * Gets a randomly generated alliterative name.
+ *
+ * @api
+ * @return string A random alliterative name.
+ */
+ public function getName()
+ {
+ $adjective = $this->_getRandomWord($this->_adjectives);
+ $noun = $this->_getRandomWord($this->_nouns, $adjective[0]);
+
+ return ucwords("{$adjective} {$noun}");
+ }
+
+ /**
+ * Get a random word from the list of words, optionally filtering by starting letter.
+ *
+ * @param array $words An array of words to choose from.
+ * @param string $startingLetter The desired starting letter of the word.
+ * @return string The random word.
+ */
+ protected function _getRandomWord(array $words, $startingLetter = null)
+ {
+ $wordsToSearch = $startingLetter === null ? $words : preg_grep("/^{$startingLetter}/", $words);
+ return $this->_randomizer ? $this->_randomizer->getArrayValue($wordsToSearch) : $wordsToSearch[array_rand($wordsToSearch)];
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/src/Generator.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/src/Generator.php
new file mode 100644
index 0000000..572c990
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/src/Generator.php
@@ -0,0 +1,16 @@
+_randomizer = $randomizer;
+ $this->_definitionSets = array_map(
+ [$this, '_parseSection'],
+ $this->_getSections($this->_getFileContents())
+ );
+ }
+
+ /**
+ * Gets a randomly generated video game name.
+ *
+ * @api
+ * @return string A random video game name.
+ */
+ public function getName()
+ {
+ $similarWords = [];
+ $words = [];
+
+ foreach ($this->_definitionSets as $definitionSet) {
+ $word = $this->_getUniqueWord($definitionSet, $similarWords);
+ $words[] = $word['word'];
+ $similarWords[] = $word['word'];
+ $similarWords = array_merge($similarWords, $word['similarWords']);
+ }
+
+ return implode(' ', $words);
+ }
+
+ /**
+ * Get a definition from the definitions that does not exist already.
+ *
+ * @param array $definitions The word list to pick from.
+ * @param array $existingWords The already-chosen words to avoid.
+ * @return array The definition of a previously unchosen word.
+ */
+ protected function _getUniqueWord(array $definitions, array $existingWords)
+ {
+ $definition = $this->_randomizer ? $this->_randomizer->getArrayValue($definitions) : $definitions[array_rand($definitions)];
+
+ if (array_search($definition['word'], $existingWords) === false) {
+ return $definition;
+ }
+
+ return $this->_getUniqueWord($definitions, $existingWords);
+ }
+
+ /**
+ * Gets the file contents of the video_game_names.txt file.
+ *
+ * @return string The video_game_names.txt contents.
+ */
+ protected function _getFileContents()
+ {
+ return file_get_contents(__DIR__ . '/video_game_names.txt');
+ }
+
+ /**
+ * Separates the contents into each of the word list sections.
+ *
+ * This builder operates by picking a random word from each of a consecutive
+ * list of word lists. These separate lists are separated by a line
+ * consisting of four hyphens in the file.
+ *
+ * @param string $contents The file contents.
+ * @return array Each section split into its own string.
+ */
+ protected function _getSections($contents)
+ {
+ return array_map('trim', explode('----', $contents));
+ }
+
+ /**
+ * Parses the given section into the final definitions.
+ *
+ * @param string $section The newline-separated list of words in a section.
+ * @return array The parsed section into its final form.
+ */
+ protected function _parseSection($section)
+ {
+ return array_map(
+ [$this, '_parseDefinition'],
+ $this->_getDefinitionsFromSection($section)
+ );
+ }
+
+ /**
+ * Gets the separate definitions from the given section.
+ *
+ * @param string $section The newline-separated list of words in a section.
+ * @return array Each word split out, but unparsed.
+ */
+ protected function _getDefinitionsFromSection($section)
+ {
+ return array_map('trim', explode("\n", $section));
+ }
+
+ /**
+ * Parses a single definition into its component pieces.
+ *
+ * The format of a definition in a file is word[^similarWord|...].
+ *
+ * @param string $definition The definition.
+ * @return array The formatted definition.
+ */
+ protected function _parseDefinition($definition)
+ {
+ $word = strtok($definition, '^');
+ $similarWords = array_filter(explode('|', strtok('^')));
+
+ return ['word' => $word, 'similarWords' => $similarWords];
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/src/adjectives.txt b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/src/adjectives.txt
new file mode 100644
index 0000000..f8d3247
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/src/adjectives.txt
@@ -0,0 +1,233 @@
+adorable
+adventurous
+aggressive
+agreeable
+alert
+alive
+amused
+angry
+annoyed
+annoying
+anxious
+arrogant
+ashamed
+attractive
+average
+awful
+bad
+beautiful
+better
+bewildered
+black
+bloody
+blue
+blue-eyed
+blushing
+bored
+brainy
+brave
+breakable
+bright
+busy
+calm
+careful
+cautious
+charming
+cheerful
+clean
+clear
+clever
+cloudy
+clumsy
+colorful
+combative
+comfortable
+concerned
+condemned
+confused
+cooperative
+courageous
+crazy
+creepy
+crowded
+cruel
+curious
+cute
+dangerous
+dark
+dead
+defeated
+defiant
+delightful
+depressed
+determined
+different
+difficult
+disgusted
+distinct
+disturbed
+dizzy
+doubtful
+drab
+dull
+eager
+easy
+elated
+elegant
+embarrassed
+enchanting
+encouraging
+energetic
+enthusiastic
+envious
+evil
+excited
+expensive
+exuberant
+fair
+faithful
+famous
+fancy
+fantastic
+fierce
+filthy
+fine
+foolish
+fragile
+frail
+frantic
+friendly
+frightened
+funny
+gentle
+gifted
+glamorous
+gleaming
+glorious
+good
+gorgeous
+graceful
+grieving
+grotesque
+grumpy
+handsome
+happy
+healthy
+helpful
+helpless
+hilarious
+homeless
+homely
+horrible
+hungry
+hurt
+ill
+important
+impossible
+inexpensive
+innocent
+inquisitive
+itchy
+jealous
+jittery
+jolly
+joyous
+kind
+lazy
+light
+lively
+lonely
+long
+lovely
+lucky
+magnificent
+misty
+modern
+motionless
+muddy
+mushy
+mysterious
+nasty
+naughty
+nervous
+nice
+nutty
+obedient
+obnoxious
+odd
+old-fashioned
+open
+outrageous
+outstanding
+panicky
+perfect
+plain
+pleasant
+poised
+poor
+powerful
+precious
+prickly
+proud
+puzzled
+quaint
+real
+relieved
+repulsive
+rich
+scary
+selfish
+shiny
+shy
+silly
+sleepy
+smiling
+smoggy
+sore
+sparkling
+splendid
+spotless
+stormy
+strange
+stupid
+successful
+super
+talented
+tame
+tender
+tense
+terrible
+testy
+thankful
+thoughtful
+thoughtless
+tired
+tough
+troubled
+ugliest
+ugly
+uninterested
+unsightly
+unusual
+upset
+uptight
+vast
+victorious
+vivacious
+wandering
+weary
+wicked
+wide-eyed
+wild
+witty
+worrisome
+worried
+wrong
+xenophobic
+xanthous
+xerothermic
+yawning
+yellowed
+yucky
+zany
+zealous
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/src/nouns.txt b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/src/nouns.txt
new file mode 100644
index 0000000..4e4adc2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/src/nouns.txt
@@ -0,0 +1,313 @@
+aardvark
+addax
+albatross
+alligator
+alpaca
+anaconda
+angelfish
+anteater
+antelope
+ant
+ape
+armadillo
+baboon
+badger
+barracuda
+bat
+batfish
+bear
+beaver
+bee
+beetle
+bird
+bison
+boar
+booby
+buffalo
+bug
+butterfly
+buzzard
+caiman
+camel
+capuchin
+capybara
+caracal
+cardinal
+caribou
+cassowary
+cat
+caterpillar
+centipede
+chamois
+cheetah
+chicken
+chimpanzee
+chinchilla
+chipmunk
+cicada
+civet
+cobra
+cockroach
+cod
+constrictor
+copperhead
+cormorant
+corncrake
+cottonmouth
+cowfish
+cow
+coyote
+crab
+crane
+crayfish
+crocodile
+crossbill
+curlew
+deer
+dingo
+dog
+dogfish
+dolphin
+donkey
+dormouse
+dotterel
+dove
+dragonfly
+duck
+dugong
+dunlin
+eagle
+earthworm
+echidna
+eel
+eland
+elephant
+elk
+emu
+falcon
+ferret
+finch
+fish
+flamingo
+flatworm
+fly
+fowl
+fox
+frog
+gannet
+gaur
+gazelle
+gecko
+gemsbok
+gentoo
+gerbil
+gerenuk
+gharial
+gibbon
+giraffe
+gnat
+gnu
+goat
+goldfinch
+goosander
+goose
+gorilla
+goshawk
+grasshopper
+grebe
+grivet
+grouse
+guanaco
+gull
+hamerkop
+hamster
+hare
+hawk
+hedgehog
+heron
+herring
+hippopotamus
+hoopoe
+hornet
+horse
+hummingbird
+hyena
+ibex
+ibis
+iguana
+impala
+jackal
+jaguar
+jay
+jellyfish
+kangaroo
+katipo
+kea
+kestrel
+kingfisher
+kinkajou
+kitten
+koala
+kookaburra
+kouprey
+kudu
+ladybird
+lapwing
+lark
+lemur
+leopard
+lion
+lizard
+llama
+lobster
+locust
+loris
+louse
+lynx
+lyrebird
+macaque
+macaw
+magpie
+mallard
+mamba
+manatee
+mandrill
+mantis
+manx
+markhor
+marten
+meerkat
+millipede
+mink
+mockingbird
+mole
+mongoose
+monkey
+moose
+mosquito
+moth
+mouse
+narwhal
+newt
+nightingale
+ocelot
+octopus
+okapi
+opossum
+orangutan
+oryx
+osprey
+ostrich
+otter
+owl
+ox
+oyster
+oystercatcher
+panda
+panther
+parrot
+partridge
+peacock
+peafowl
+peccary
+pelican
+penguin
+petrel
+pheasant
+pig
+pigeon
+pintail
+piranha
+platypus
+plover
+polecat
+pollan
+pony
+porcupine
+porpoise
+puffin
+puma
+pygmy
+quagga
+quail
+quelea
+quetzal
+quoll
+rabbit
+raccoon
+rat
+ratel
+rattlesnake
+raven
+ray
+reindeer
+rhinoceros
+rook
+sable
+salamander
+salmon
+sandpiper
+sardine
+scarab
+seahorse
+seal
+serval
+shark
+sheep
+shrew
+shrike
+skimmer
+skipper
+skunk
+skylark
+sloth
+snail
+snake
+spider
+squirrel
+stag
+starling
+stoat
+stork
+swan
+swiftlet
+tamarin
+tapir
+tarantula
+tarsier
+teira
+termite
+tern
+thrush
+tiger
+toad
+tortoise
+toucan
+trout
+tuatara
+turkey
+turtle
+unicorn
+vendace
+vicuña
+vole
+vulture
+wallaby
+walrus
+warbler
+wasp
+weasel
+weevil
+whale
+wildebeest
+willet
+wolf
+wolverine
+wombat
+worm
+wren
+wryneck
+xenomorph
+yacare
+yak
+zebra
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/src/video_game_names.txt b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/src/video_game_names.txt
new file mode 100644
index 0000000..a2bcaa2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/src/video_game_names.txt
@@ -0,0 +1,1276 @@
+3D
+8-Bit
+A Boy and His
+Action
+Advanced^Advance
+Adventures of the^Adventure
+Aero
+African^in Africa
+Alcoholic
+Alien
+Allied
+All-American
+All-Night
+All-Star^Star|Starring Mickey Mouse|Stars|Superstar
+Almighty
+Amateur
+Amazing
+Amazon
+American
+Amish
+Amphibious
+Ancient
+Android^Cyborg
+Angry
+Apathetic
+Aquatic
+Arcane
+Armored
+Art of
+Asian
+Astral
+Attack of the^Attack
+Atomic^Nuclear
+Australian
+Awesome
+Barbie's
+Battle^Battleship|Battalion
+Battlefield:^Battle|Battleship|Battalion
+Beautiful^Beautician
+Bewildering
+Biblical
+Big^Big Game Hunter
+Big Bird's^Big Game Hunter
+Big-Time^Big Game Hunter
+Bionic
+Bizarre
+Bizarro
+Black
+Blasphemous
+Blazing
+Bling Bling
+Blissful
+Blocky
+Bloody^Blood|Bloodbath|of the Blood God
+Bonk's
+Boring
+Bouncin'
+Brain-Damaged
+British
+Britney Spears'
+BudgetSoft Presents:
+Caesar's
+Canadian
+Cantankerous
+Caribbean
+Catholic
+Celebrity
+Celtic
+Charlie Brown's
+Children of the
+Chillin'
+Chinese
+Chocolate
+Christian
+Claustrophobic
+College
+Colonial
+Combat
+Communist
+Confusing
+Cool
+Corporate
+Cosmic
+Crazy
+Create Your Own^Creator
+Creepy
+Cthulhu's
+Curse of the
+Custom
+Cyber
+Cybernetic
+Cyborg^Android
+Dance Dance^Breakdancing|Dance|Dance Mix|Dance Party|Dancers|Square Dancing
+Dangerous
+Darkest
+Day of the
+Dead or Alive^Death|Deathmatch|of Death|of the Dead
+Deadly^Death|Deathmatch|of Death|of the Dead
+Death-Defying^Death|Deathmatch|of Death|of the Dead
+Deep Space^of the Deep
+Def Jam
+Demonic
+Depressing
+Deranged
+Derek Smart's
+Dirty
+Disney's
+Distinguished
+Disturbing
+Divine
+Donkey Kong's
+Double
+Downtown
+Dr.
+Dracula's
+Drug-Induced
+Drunken
+Duke Nukem:
+Dwarven^Dwarf|Gnome|Midget
+Dynamite
+Ebony
+Eco-Friendly
+Educational
+Elderly
+Electric
+Elegant
+Elite
+Elmo's
+Emo
+Endless
+Enormous
+Enraged
+Epic
+Erotic
+Escape from the
+Eternal
+European
+Everybody Hates the
+Everybody Loves the
+Exciting
+Excruciating
+Explosive^Explosion
+Exquisite
+Extreme^X-Treme
+Fabulous
+Fancy
+Fantastic
+Fantasy
+Fatal
+Feverish
+Fiery
+Final
+Final Fantasy^Fantasy
+First-Person
+Fisher Price
+Flamboyant
+Fluffy
+Flying
+Forbidden
+Forgotten
+Frankenstein's
+French
+Frisky
+Fruity
+Full Metal
+Funky^Funk
+Furry
+Future
+Galactic
+Generic
+Geriatric
+German
+Ghetto
+Giant
+Glowing
+Go Go
+God of
+Golden
+Gothic^Goth
+Grand
+Great
+Grimy
+Guitar
+Happy
+Hardcore
+Haunted
+Hazardous
+Heavy
+Heavy Metal^Metal
+Heinous
+Helicopter
+Heroic^Hero|Heroes
+Hidden
+Hideous
+High-Speed^Speed
+Hillbilly
+Hindu
+Hip-Hop^Hippo
+History of the
+Hitler's^Nazi
+Ho-Hum
+Holy
+Horrifying
+Hyper
+Imperial
+Impossible
+In Search of
+In Search of the
+In the Lost Kingdom of
+In Your Face
+Inappropriate
+Inbred
+Incomprehensible
+Incredible
+Indian
+Indiana Jones and the
+Inept
+Infinite
+Ingenious
+Insane
+Intellectual
+Intelligent
+Intense
+Interactive
+International
+Internet
+Interstellar
+Invisible
+Irish
+Iron
+Irresistible
+Irritating
+Islamic
+Italian
+It's a Mad, Mad^Madness
+Jackie Chan's
+Jamaican
+Japanese
+Jedi
+Jewish
+Johnny Turbo's
+John Romero's
+Kabuki
+Kamikaze
+Kermit's
+Killer
+Kinect
+King of^King|Kingdom
+Kinky
+Kirby's
+Kosher
+Kung-fu
+Jack Thompson's
+Lair of the
+Latino
+Lazy
+Legacy of
+Legend of^Legend|Legends
+Legend of the^Legend|Legends
+Legendary^Legend|Legends
+Leisure Suit
+Lethal
+Little
+Looney Tunes
+Lord of the^Lord
+Lost
+Lovely^Love|of Love|Romance
+Low G
+Lucky
+Luigi's
+M.C. Escher's
+Madden
+Magic^of Magic|of Might and Magic
+Magical^Magic|of Magic|of Might and Magic
+Magnetic
+Major
+Manic^Mania|Maniac
+Maniac^Mania
+Mario's
+Mary Kate and Ashley's
+Master Chief's^Master
+Masters of^Master
+Masters of the^Master
+Maximum
+Mechanized
+Medieval
+Mega
+Mega Man's
+Merciless
+Metal
+Mexican
+Michael Jackson's
+Mickey's
+Micro
+Middle-Eastern^in the Middle East
+Mighty
+Mind-Bending
+Miniature^Dwarf|Gnome|Midget
+Miracle
+Monster^Monster Truck
+Monty Python's
+Morbid
+Morbidly Obese
+Mr.
+MTV's
+Muppet
+Musical^DJ|Music
+My First
+My Little
+My Very Own
+Mysterious^of Mystery
+Mystery^of Mystery
+Mystic^of Mystery
+Mystical^of Mystery
+Mythical
+Narcoleptic
+Nasty
+National Lampoon's
+Naughty
+NBA
+NCAA
+Nerf
+Neo
+Neon
+Neurotic
+New
+Night of the^Knights|Night|Nightmare
+Nighttime^Knights|Night|Nightmare
+Nihilistic
+Ninja
+No One Can Stop the
+Nostalgic
+Nuclear^Atomic
+Nudist
+Obsessive-Compulsive
+Occult
+Olympic
+Omega
+Orbital
+Pagan
+Panzer
+Papal
+Paranoid
+Pathetic
+Peaceful
+Perfect
+Perverted
+Phoenix Wright:
+Pixellated
+Planet of the^Planet
+Political
+Post-Apocalyptic
+Prehistoric
+Preschool
+Presidential
+Primal
+Pro
+Profane
+Professional^Pro
+Psychedelic
+Psycho
+Queen of the^Princess
+Quiet
+Rad
+Radical
+Radioactive
+Raging^Rage
+Real
+Red Hot
+Regal
+Relentless
+Religious
+Remote
+Renegade
+Retarded
+Retro
+Return of^Returns|Strikes Again|Strikes Back
+Return of the^Returns|Strikes Again|Strikes Back
+Revenge of^Revenge|- The Revenge
+Revenge of the^Revenge|- The Revenge
+Rise of the
+Robot
+Robotic^Robot
+Rock 'n' Roll
+Rocket-Powered
+Rockin'
+Rogue
+Romantic
+Royal
+Rural
+Rushing^Rush
+Russian
+Samba de
+Samurai
+Satan's
+Savage
+Save Yourself from the
+Scandinavian
+Scooby Doo and the
+Scottish
+Screaming
+Search for the
+Secret of the
+Sensual^Sex
+Sexy^Sex
+Shadow of the^Shadow
+Shady
+Shameful
+Shrunken^Midget
+Sid Meier's
+Silent
+Silly
+Sim^Simulator
+Sinister
+Sleazy
+Sleepy
+Small-Time
+Sonic's
+Soviet
+Space
+Special^Special Edition
+Spectacular
+Spectral^Ghost
+Spirit of the
+Spooky
+Spunky
+Star^All-Stars|Starring Mickey Mouse|Stars|Superstar
+Star Trek^All-Stars|Star|Starring Mickey Mouse|Stars|Superstar
+Star Wars^All-Stars|Star|Starring Mickey Mouse|Stars|Superstar
+Stealth
+Stoic
+Strategic
+Street
+Stupendous
+Stylish
+Subatomic
+Subterranean^Underground|Underworld
+Summer
+Super
+Super Sexy^Sex|Superstar
+Supreme
+Surprise
+Tactical^Tactics
+Tasteless
+Team
+Teenage
+Telekinetic
+Terrible
+The
+The Care Bears'
+The Castle of
+The Castle of the
+The Glory of
+The Great
+The Harlem Globetrotters:
+The Hunt For the
+The Incredible
+The Infernal
+The Last
+The Muppets^Muppets
+The Quest for the^Quest
+The Secret Weapon of the
+The Simpsons'
+The Sims:
+The Six Million Dollar
+Third-World
+Throbbing
+Tiger Woods'
+Tiny^Midget
+Tom Clancy's
+Tony Hawk's
+Topsy-Turvy
+Toxic
+Transvestite
+Trendy
+Tribal
+Tropical
+True Crime:
+Turbo
+Twin
+Twisted
+Ultimate
+Ultra
+Ultraviolent^Ultra
+Unbelievable
+Undead
+Undercover^Under Fire|Underwear|Underground|Underworld
+Underground^Under Fire|Underwear|Underground|Underworld
+Underwater^Under Fire|Underwear|Underground|Underworld
+Unforgettable
+Unholy
+Unpleasant
+Unreal
+Unremarkable
+Unstoppable
+Urban
+Vampire
+Vegetarian
+Viking
+Violent
+Virtua
+Virtual
+Wacky
+Wandering
+War of the^Warfare|Warrior|Wars|- Total War
+We Love
+Weary
+Wild^Gone Wild
+Wonderous
+Wooden
+World^World Cup|World Tour
+World of^World|World Cup|World Tour
+Wrath of the
+WWII^Warfare|Warrior|Wars|World|World Cup|World Tour|- Total War
+Ye Olde
+Yoshi's
+Zany
+Zombie^Zombies
+----
+3D
+Acid
+Aerobics
+Afro
+Alien
+Alligator
+Amish
+Android
+Animal
+Arcade
+Architecture
+Army
+Assault
+Axe
+Badminton^Baseball|Basketball|Boxing|Football|Paintbrawl|Pinball|Polo
+Baking
+Ballet
+Balloon
+Banana
+Bandicoot
+Banjo
+Barbarian
+Barcode
+Baseball^Base|Baseball|Basketball|Boxing|Football|Paintbrawl|Pinball|Polo
+Basketball^Baseball|Basketball|Boxing|Football|Paintbrawl|Pinball|Polo
+Bass
+Batman
+Battle^Battalion
+Battleship^Battle|Battalion
+Bazooka
+Beach
+Beast
+Beat
+Beautician
+Bedtime
+Bible
+Big Game Hunter^Hunt|Hunter
+Bimbo
+Bingo
+Biplane
+Blade
+Blimp
+Blood^Bloodbath|of the Blood God
+BMX
+Bobsled
+Bomb
+Bomberman
+Bong
+Bongo
+Booty
+Bow Hunter^Hunt|Hunter
+Bowling^Baseball|Basketball|Boxing|Football|Paintbrawl|Pinball|Polo
+Boxing^Baseball|Basketball|Boxing|Football|Paintbrawl|Pinball|Polo
+Breakdancing^Dance Mix|Dance Party|Dancers
+Bubble
+Bubblegum
+Buddhist
+Bungie
+Burger
+Business
+Cannibal
+Car
+Cardboard
+Carnival
+Casino
+Castlevania
+Catapult
+Caveman^Man
+Chainsaw
+Chase
+Cheese
+Chef
+Chess
+Chicken
+Chipmunk
+Chocobo
+Circus
+City
+College
+Combat
+Computer
+Conga
+Cookie
+Cooking
+Cowboy
+Cricket^Baseball|Basketball|Boxing|Football|Paintbrawl|Pinball|Polo
+Croquet^Baseball|Basketball|Boxing|Football|Paintbrawl|Pinball|Polo
+Crowbar
+Crystal
+Cyborg
+Dance^Dance Mix|Dance Party|Dancers
+Dating
+Death^Deathmatch|of Death|of the Dead
+Deer Hunter
+Demon
+Dentist
+Desert^in the Desert
+Devil
+Dinosaur
+Disco
+Dodgeball^Baseball|Basketball|Boxing|Football|Paintbrawl|Pinball|Polo
+Dog
+Donkey
+Dragon
+Driving
+Drug-Dealing
+Duck
+Dungeon
+Dwarf
+Elevator
+Equestrian
+Fashion
+Fantasy
+Farm^Farmer
+Fencing
+Fighter^Fight|Fight Club
+Fire
+Fishing^Baseball|Basketball|Boxing|Football|Paintbrawl|Pinball|Polo
+Flatulence
+Florist
+Football^Baseball|Basketball|Boxing|Football|Paintbrawl|Pinball|Polo
+Forklift
+Frisbee
+Frog
+Fun
+Fun Noodle
+Funk
+Furry
+Ghost
+Gimp
+Gnome
+Go-Kart
+Goblin
+Godzilla
+Golf
+Gopher
+Goth
+Graveyard
+Grizzly Bear
+Guitar
+Gun
+Hair Salon
+Hammer
+Hamster
+Handgun
+Hang Glider
+Hardware
+Harpoon
+Harvest
+Helicopter
+Hillbilly
+Hippo
+Hitman^Man
+Hobo
+Hockey
+Hoedown^Beatdown|Showdown|Smackdown|Takedown
+Hovercraft
+Horse Racing^Baseball|Basketball|Boxing|Football|Paintbrawl|Pinball|Polo
+Ice
+Ice Cream
+Indian
+Insect
+Internet
+Jackhammer
+Janitor
+Jazz
+Jetpack
+Jetski
+Juggalo
+Jungle
+Kabuki
+Kangaroo
+Karaoke
+Karate
+Kart
+Katana
+Kitchen
+Kung-fu
+Lacrosse^Baseball|Basketball|Boxing|Football|Paintbrawl|Pinball|Polo
+Landmine
+Laser
+Lawnmower
+Lego
+Leisure Suit
+Lightning
+Limbo
+Lizard
+Llama
+Love^of Love|Romance
+Lowrider
+Mafia
+Magic^of Magic|of Might and Magic
+Mahjong
+Makeout
+Makeover
+Mall
+Manlove
+Matador
+Math
+Maze
+Mech
+Metal
+Midget
+Military
+Mind Control
+Monkey
+Monster
+Monster Truck
+Moon
+Moped
+Motorcycle
+Motocross
+Mountain Climber
+Mummy
+Mushroom
+Music^DJ
+Mutant
+NASCAR
+Nazi
+Night^Knights|Nightmare
+Ninja
+Nuclear
+Nudist
+Octopus
+Office
+Ostrich
+Outlaw
+Pachinko
+Paintball^Baseball|Basketball|Boxing|Football|Paintbrawl|Pinball|Polo
+Penguin
+Piano
+Pinball^Baseball|Basketball|Boxing|Football|Paintbrawl|Pinball|Polo
+Ping Pong^Baseball|Basketball|Boxing|Football|Paintbrawl|Pinball|Polo
+Pirate
+Platypus
+Plumber
+Plunger
+Pogo
+Pokemon
+Police
+Polka
+Pony
+Porn
+Princess
+Prison
+Programming
+Punching
+Puppy
+Puzzle
+Quantum
+Quiz
+Rabbit
+Raccoon
+Racing^Racer
+Railroad
+Rainbow
+River
+Robot
+Rocket
+Rodeo
+Rollerball^Baseball|Basketball|Boxing|Football|Paintbrawl|Pinball|Polo
+Rugby^Baseball|Basketball|Boxing|Football|Paintbrawl|Pinball|Polo
+Sailboat
+Sailor
+Samurai
+Sandwich
+Scooter
+Scorched Earth
+Sewer
+Sex
+Shadow
+Shark
+Shaving
+Shock
+Shopping
+Shotgun
+Skate
+Skydiving
+Sloth
+Sniper
+Snowboard
+Soccer
+Software
+Spatula
+Speed
+Spelling
+Spelunking
+Spider
+Spork
+Square Dancing^Dance Mix|Dance Party|Dancers
+Squirrel
+Stapler
+STD
+Stick
+Stunt
+Submarine
+Sumo
+Sudoku
+Sunshine
+Surf
+Surgery
+Sushi
+Sword
+Tank
+Techno
+Tennis
+Terrorist
+Tetris
+Theme Park^Park
+Thief
+Thunder
+Toon
+Trailer Park^Park
+Train
+Trampoline
+Transvestite
+Tricycle
+Turtle
+Typing
+UFO
+Underwear^Under Fire|Underground|Underworld
+Unicorn
+Unicycle
+Valkyrie
+Vampire
+Vegetarian
+Vigilante
+Viking
+Vocabulary
+Volleyball^Baseball|Basketball|Boxing|Football|Paintbrawl|Pinball|Polo
+Wagon
+Walrus
+Wedding
+Weight Loss
+Werewolf
+Whale
+Wheelchair
+Wizard
+Workout
+Worm
+Wrestling
+Writing
+WWE
+WWII^Warfare|Warrior|Wars|World|World Cup|World Tour|- Total War
+Yak
+Yeti
+Yoga
+Zamboni
+Zombie^Zombies
+----
+- 2nd Impact
+- 3rd Strike
+1942
+25th Anniversary Edition
+2K
+2000
+3000
+3D
+64
+95
+Academy
+Advance
+Adventure
+Agent
+All-Stars
+Alpha
+Anarchy
+Annihilation
+Anthology
+Apocalypse
+Arena
+Armada
+Armageddon
+Assassins
+Assault
+at the Olympics
+Attack
+Babies
+Bandit
+Bandits
+Bastards
+Battle
+Battalion
+Base
+Baseball
+Basketball
+Beatdown
+Beta
+Blast
+Blaster
+Bloodbath
+Boxing
+Boy
+Brawl
+Brothers
+Camp
+Caper
+Carnage
+Castle
+CD
+Challenge
+Championship
+Chase
+Choreographer
+Chronicles
+City
+Co-Op
+Collection
+- Collector's Edition
+College
+Colosseum
+Combat
+Commander
+Commando
+Competition
+Conflict
+Connection
+Conquest
+Conspiracy
+Conundrum
+Corps
+Country
+Creator
+Crime Scene Investigation
+Crisis
+Crusade
+Crusader
+Dance Mix
+Dance Party
+Dancers
+Daredevils
+Dash
+Deathmatch
+Deluxe
+Demolition
+Derby
+Desperadoes
+Destruction
+Detective
+Diesel
+Disaster
+DJ
+Domination
+Dreamland
+DS
+Dudes
+Dungeon
+DX
+Dynasty
+Dystopia
+Empire
+Encounter
+Enforcer
+Epidemic
+Espionage
+EX
+Exhibition
+Experience
+Expert
+Explorer
+Explosion
+Express
+Extra
+Extravaganza
+Factory
+Family
+Fandango
+Fantasy
+Farmer
+Fest
+Feud
+Fever
+Fiasco
+Fiesta
+Fight
+Fight Club
+Fighter
+Football
+For Kids
+Force
+Forever
+Fortress
+Freak
+Frenzy
+from Hell
+from Mars
+from Outer Space
+from Planet X
+Fun
+Gaiden
+Gang
+Girl
+Gold
+Gone Wild
+Gladiator
+Groove
+GT
+Havoc
+Hell
+Hero
+Heroes
+Hoedown
+Hop-A-Bout
+Horde
+Horror
+Hospital
+- Hot Pursuit
+House
+Hunt
+Hunter
+II
+III
+Ignition
+in Africa
+in Busytown
+in Crazyland
+in Middle-Earth
+in My Pocket
+in Space
+in the Bayou
+in the Dark
+in the Desert
+in the Hood
+in the Magic Kingdom
+in the Middle East
+in the Outback
+in the Salad Kingdom
+in the Sky
+in Toyland
+in Vegas
+Incident
+Inferno
+Insanity
+Inspector
+Insurrection
+Interactive
+Interceptor
+Invaders
+Invasion
+Island
+Jam
+Jamboree
+Jihad
+Joe
+Journey
+Jr.
+Kid
+Kids
+King
+Kingdom
+Knights
+Kombat
+Legend
+Legends
+- Limited Edition
+Live
+Lord
+Machine
+Madness
+Man
+Mania
+Maniac
+Mansion
+Marines
+Massacre
+Master
+Maxx
+Mayhem
+Melee
+Mission
+Munchers
+Nation
+Nightmare
+Nitro
+Odyssey
+of Death
+of Doom
+of Fury
+of Love
+of Magic
+of Might and Magic
+of Mystery
+of the Blood God
+of the Damned
+of the Dead
+of the Deep
+of the Third Reich
+on the Oregon Trail
+Offensive
+Omega
+on the High Seas
+On The Road
+on Wheels
+Online
+Onslaught
+Operation
+Operatives
+Oppression
+Orchestra
+Over Normandy
+Overdrive
+Overload
+Overlords
+Paintbrawl
+Palace
+Panic
+Paratroopers
+Park
+Party
+Patrol
+Phonics
+Pimps
+Pinball
+Pioneer
+Planet
+Playhouse
+Plus
+Police
+Polo
+Posse
+Power
+Preacher
+Princess
+Pro
+Project
+Prophecy
+Psychiatrist
+Punch-Out!!
+Punishment
+Quest
+Quiz
+Racer
+Rage
+Raider
+Rally
+Rampage
+Rangers
+Ransom
+Rave
+Rebellion
+Reloaded
+Remix
+Rescue
+Restaurant
+Returns
+Revenge
+Revisited
+Revolution
+Rider
+Riders
+Rocket
+Romance
+Romp
+Roundup
+Runner
+Rush
+Safari
+Saga
+Saloon
+Scam
+Scandal
+School
+Shack
+Shoot
+Shootout
+Showdown
+Siege
+Simulator
+Sisters
+Slam
+Slaughter
+Slayer
+Smackdown
+Smash
+Smuggler
+Solid
+Soldier
+Special Edition
+Spectacular
+Spies
+Spree
+Squadron
+Stadium
+Starring Mickey Mouse
+Stars
+Story
+Strike Force
+Strikes Again
+Strikes Back
+Struggle
+Studio
+Summit
+Summoner
+Superstar
+Symphony
+Syndicate
+Syndrome
+Tactics
+Takedown
+Tale
+Task Force
+Temple
+Terror
+Thieves
+- The Card Game
+- The Dark Project
+- The Gathering Storm
+- The Lost Levels
+- The Movie
+- The Next Generation
+- The Quickening
+- The Resistance
+- The Revenge
+Through Time
+Throwdown
+- Total War
+Tournament
+Trader
+Train
+Trainer
+Training
+Tribe
+Trilogy
+Trivia
+Troopers
+Turbo
+Tycoon
+Ultra
+Unit
+Uncensored
+Underground
+Underworld
+Universe
+Unleashed
+Uprising
+Vengeance
+Voyage
+vs. Capcom
+vs. Street Fighter
+vs. The Space Mutants
+Warfare
+Warrior
+Wars
+Wasteland
+with Friends
+World
+World Cup
+World Tour
+Wranglers
+X
+XP
+XXX
+X-treme
+Yoga
+Z
+Zombies
+Zone
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/tests/AllTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/tests/AllTest.php
new file mode 100644
index 0000000..8049ab7
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/tests/AllTest.php
@@ -0,0 +1,72 @@
+
+ */
+class AllTest extends PHPUnit_Framework_TestCase
+{
+ /**
+ * Verify basic behavior of getName().
+ *
+ * @test
+ * @covers ::__construct
+ * @covers ::create
+ * @covers ::getName
+ * @uses \Nubs\RandomNameGenerator\Alliteration
+ * @uses \Nubs\RandomNameGenerator\Vgng
+ *
+ * @return void
+ */
+ public function getNameBasic()
+ {
+ $generator = All::create();
+ $name = $generator->getName();
+ $this->assertRegexp('/.+/', $name);
+ }
+
+ /**
+ * Verify basic behavior of getName() with a forced random generator.
+ *
+ * @test
+ * @covers ::__construct
+ * @covers ::create
+ * @covers ::getName
+ * @uses \Nubs\RandomNameGenerator\Alliteration
+ *
+ * @return void
+ */
+ public function getNameForced()
+ {
+ $numberGenerator = $this->createMock('\Cinam\Randomizer\NumberGenerator');
+ $numberGenerator->expects($this->exactly(2))->method('getInt')->will($this->onConsecutiveCalls(20, 5));
+ $randomizer = new Randomizer($numberGenerator);
+
+ $generator = new All([new Alliteration($randomizer)]);
+ $this->assertSame('Black Bear', $generator->getName());
+ }
+
+ /**
+ * Verify basic behavior of __toString().
+ *
+ * @test
+ * @covers ::__construct
+ * @covers ::create
+ * @covers ::__toString
+ * @covers ::getName
+ * @uses \Nubs\RandomNameGenerator\Alliteration
+ * @uses \Nubs\RandomNameGenerator\Vgng
+ *
+ * @return void
+ */
+ public function toStringBasic()
+ {
+ $generator = All::create();
+ $name = (string)$generator;
+ $this->assertRegexp('/.+/', $name);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/tests/AlliterationTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/tests/AlliterationTest.php
new file mode 100644
index 0000000..0b47343
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/tests/AlliterationTest.php
@@ -0,0 +1,66 @@
+
+ */
+class AlliterationTest extends PHPUnit_Framework_TestCase
+{
+ /**
+ * Verify basic behavior of getName().
+ *
+ * @test
+ * @covers ::__construct
+ * @covers ::getName
+ *
+ * @return void
+ */
+ public function getNameBasic()
+ {
+ $generator = new Alliteration();
+ $parts = explode(' ', $generator->getName());
+ $this->assertSame(2, count($parts));
+ $this->assertSame($parts[0][0], $parts[1][0]);
+ }
+
+ /**
+ * Verify basic behavior of getName() with a forced random generator.
+ *
+ * @test
+ * @covers ::__construct
+ * @covers ::getName
+ *
+ * @return void
+ */
+ public function getNameForced()
+ {
+ $numberGenerator = $this->createMock('\Cinam\Randomizer\NumberGenerator');
+ $numberGenerator->expects($this->exactly(2))->method('getInt')->will($this->onConsecutiveCalls(20, 5));
+ $randomizer = new Randomizer($numberGenerator);
+
+ $generator = new Alliteration($randomizer);
+ $this->assertSame('Black Bear', $generator->getName());
+ }
+
+ /**
+ * Verify basic behavior of __toString().
+ *
+ * @test
+ * @covers ::__construct
+ * @covers ::__toString
+ * @covers ::getName
+ *
+ * @return void
+ */
+ public function toStringBasic()
+ {
+ $generator = new Alliteration();
+ $parts = explode(' ', (string)$generator);
+ $this->assertSame(2, count($parts));
+ $this->assertSame($parts[0][0], $parts[1][0]);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/tests/VgngTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/tests/VgngTest.php
new file mode 100644
index 0000000..a301b81
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/nubs/random-name-generator/tests/VgngTest.php
@@ -0,0 +1,67 @@
+
+ */
+class VgngTest extends PHPUnit_Framework_TestCase
+{
+ /**
+ * Verify that getName returns the expected name.
+ *
+ * @test
+ * @covers ::__construct
+ * @covers ::getName
+ */
+ public function getNameBasic()
+ {
+ $numberGenerator = $this->createMock('\Cinam\Randomizer\NumberGenerator');
+ $numberGenerator->expects($this->exactly(3))->method('getInt')->will($this->returnValue(1));
+ $randomizer = new Randomizer($numberGenerator);
+
+ $vgng = new Vgng($randomizer);
+
+ $this->assertSame('8-Bit Acid - 3rd Strike', $vgng->getName());
+ }
+
+ /**
+ * Verify that getName returns a name without similar strings.
+ *
+ * @test
+ * @covers ::__construct
+ * @covers ::getName
+ */
+ public function getNameSimilarName()
+ {
+ $numberGenerator = $this->createMock('\Cinam\Randomizer\NumberGenerator');
+ $numberGenerator->expects($this->exactly(4))->method('getInt')->will($this->onConsecutiveCalls(0, 0, 2, 10));
+ $randomizer = new Randomizer($numberGenerator);
+
+ $vgng = new Vgng($randomizer);
+
+ $this->assertSame('3D Aerobics Academy', $vgng->getName());
+ }
+
+ /**
+ * Verify that toString returns the expected name.
+ *
+ * @test
+ * @covers ::__construct
+ * @covers ::__toString
+ * @covers ::getName
+ */
+ public function toStringBasic()
+ {
+ $numberGenerator = $this->createMock('\Cinam\Randomizer\NumberGenerator');
+ $numberGenerator->expects($this->exactly(3))->method('getInt')->will($this->returnValue(1));
+ $randomizer = new Randomizer($numberGenerator);
+
+ $vgng = new Vgng($randomizer);
+
+ $this->assertSame('8-Bit Acid - 3rd Strike', (string)$vgng);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/LICENSE b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/LICENSE
new file mode 100644
index 0000000..45c7017
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Paragon Initiative Enterprises
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/build-phar.sh b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/build-phar.sh
new file mode 100644
index 0000000..b4a5ba3
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/build-phar.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+basedir=$( dirname $( readlink -f ${BASH_SOURCE[0]} ) )
+
+php -dphar.readonly=0 "$basedir/other/build_phar.php" $*
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/composer.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/composer.json
new file mode 100644
index 0000000..1c5978c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/composer.json
@@ -0,0 +1,37 @@
+{
+ "name": "paragonie/random_compat",
+ "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
+ "keywords": [
+ "csprng",
+ "random",
+ "pseudorandom"
+ ],
+ "license": "MIT",
+ "type": "library",
+ "authors": [
+ {
+ "name": "Paragon Initiative Enterprises",
+ "email": "security@paragonie.com",
+ "homepage": "https://paragonie.com"
+ }
+ ],
+ "support": {
+ "issues": "https://github.com/paragonie/random_compat/issues",
+ "email": "info@paragonie.com",
+ "source": "https://github.com/paragonie/random_compat"
+ },
+ "require": {
+ "php": ">=5.2.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "4.*|5.*"
+ },
+ "suggest": {
+ "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
+ },
+ "autoload": {
+ "files": [
+ "lib/random.php"
+ ]
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey
new file mode 100644
index 0000000..eb50ebf
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey
@@ -0,0 +1,5 @@
+-----BEGIN PUBLIC KEY-----
+MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEEd+wCqJDrx5B4OldM0dQE0ZMX+lx1ZWm
+pui0SUqD4G29L3NGsz9UhJ/0HjBdbnkhIK5xviT0X5vtjacF6ajgcCArbTB+ds+p
++h7Q084NuSuIpNb6YPfoUFgC/CL9kAoc
+-----END PUBLIC KEY-----
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc
new file mode 100644
index 0000000..6a1d7f3
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc
@@ -0,0 +1,11 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v2.0.22 (MingW32)
+
+iQEcBAABAgAGBQJWtW1hAAoJEGuXocKCZATaJf0H+wbZGgskK1dcRTsuVJl9IWip
+QwGw/qIKI280SD6/ckoUMxKDCJiFuPR14zmqnS36k7N5UNPnpdTJTS8T11jttSpg
+1LCmgpbEIpgaTah+cELDqFCav99fS+bEiAL5lWDAHBTE/XPjGVCqeehyPYref4IW
+NDBIEsvnHPHPLsn6X5jq4+Yj5oUixgxaMPiR+bcO4Sh+RzOVB6i2D0upWfRXBFXA
+NNnsg9/zjvoC7ZW73y9uSH+dPJTt/Vgfeiv52/v41XliyzbUyLalf02GNPY+9goV
+JHG1ulEEBJOCiUD9cE1PUIJwHA/HqyhHIvV350YoEFiHl8iSwm7SiZu5kPjaq74=
+=B6+8
+-----END PGP SIGNATURE-----
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/byte_safe_strings.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/byte_safe_strings.php
new file mode 100644
index 0000000..3de86b2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/byte_safe_strings.php
@@ -0,0 +1,181 @@
+ RandomCompat_strlen($binary_string)) {
+ return '';
+ }
+
+ return (string) mb_substr($binary_string, $start, $length, '8bit');
+ }
+
+ } else {
+
+ /**
+ * substr() implementation that isn't brittle to mbstring.func_overload
+ *
+ * This version just uses the default substr()
+ *
+ * @param string $binary_string
+ * @param int $start
+ * @param int $length (optional)
+ *
+ * @throws TypeError
+ *
+ * @return string
+ */
+ function RandomCompat_substr($binary_string, $start, $length = null)
+ {
+ if (!is_string($binary_string)) {
+ throw new TypeError(
+ 'RandomCompat_substr(): First argument should be a string'
+ );
+ }
+
+ if (!is_int($start)) {
+ throw new TypeError(
+ 'RandomCompat_substr(): Second argument should be an integer'
+ );
+ }
+
+ if ($length !== null) {
+ if (!is_int($length)) {
+ throw new TypeError(
+ 'RandomCompat_substr(): Third argument should be an integer, or omitted'
+ );
+ }
+
+ return (string) substr($binary_string, $start, $length);
+ }
+
+ return (string) substr($binary_string, $start);
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/cast_to_int.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/cast_to_int.php
new file mode 100644
index 0000000..9a4fab9
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/cast_to_int.php
@@ -0,0 +1,75 @@
+ operators might accidentally let a float
+ * through.
+ *
+ * @param int|float $number The number we want to convert to an int
+ * @param bool $fail_open Set to true to not throw an exception
+ *
+ * @return float|int
+ * @psalm-suppress InvalidReturnType
+ *
+ * @throws TypeError
+ */
+ function RandomCompat_intval($number, $fail_open = false)
+ {
+ if (is_int($number) || is_float($number)) {
+ $number += 0;
+ } elseif (is_numeric($number)) {
+ $number += 0;
+ }
+
+ if (
+ is_float($number)
+ &&
+ $number > ~PHP_INT_MAX
+ &&
+ $number < PHP_INT_MAX
+ ) {
+ $number = (int) $number;
+ }
+
+ if (is_int($number)) {
+ return (int) $number;
+ } elseif (!$fail_open) {
+ throw new TypeError(
+ 'Expected an integer.'
+ );
+ }
+ return $number;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/error_polyfill.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/error_polyfill.php
new file mode 100644
index 0000000..6a91990
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/error_polyfill.php
@@ -0,0 +1,49 @@
+= 70000) {
+ return;
+}
+
+if (!defined('RANDOM_COMPAT_READ_BUFFER')) {
+ define('RANDOM_COMPAT_READ_BUFFER', 8);
+}
+
+$RandomCompatDIR = dirname(__FILE__);
+
+require_once $RandomCompatDIR . '/byte_safe_strings.php';
+require_once $RandomCompatDIR . '/cast_to_int.php';
+require_once $RandomCompatDIR . '/error_polyfill.php';
+
+if (!is_callable('random_bytes')) {
+ /**
+ * PHP 5.2.0 - 5.6.x way to implement random_bytes()
+ *
+ * We use conditional statements here to define the function in accordance
+ * to the operating environment. It's a micro-optimization.
+ *
+ * In order of preference:
+ * 1. Use libsodium if available.
+ * 2. fread() /dev/urandom if available (never on Windows)
+ * 3. mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM)
+ * 4. COM('CAPICOM.Utilities.1')->GetRandom()
+ *
+ * See RATIONALE.md for our reasoning behind this particular order
+ */
+ if (extension_loaded('libsodium')) {
+ // See random_bytes_libsodium.php
+ if (PHP_VERSION_ID >= 50300 && is_callable('\\Sodium\\randombytes_buf')) {
+ require_once $RandomCompatDIR . '/random_bytes_libsodium.php';
+ } elseif (method_exists('Sodium', 'randombytes_buf')) {
+ require_once $RandomCompatDIR . '/random_bytes_libsodium_legacy.php';
+ }
+ }
+
+ /**
+ * Reading directly from /dev/urandom:
+ */
+ if (DIRECTORY_SEPARATOR === '/') {
+ // DIRECTORY_SEPARATOR === '/' on Unix-like OSes -- this is a fast
+ // way to exclude Windows.
+ $RandomCompatUrandom = true;
+ $RandomCompat_basedir = ini_get('open_basedir');
+
+ if (!empty($RandomCompat_basedir)) {
+ $RandomCompat_open_basedir = explode(
+ PATH_SEPARATOR,
+ strtolower($RandomCompat_basedir)
+ );
+ $RandomCompatUrandom = (array() !== array_intersect(
+ array('/dev', '/dev/', '/dev/urandom'),
+ $RandomCompat_open_basedir
+ ));
+ $RandomCompat_open_basedir = null;
+ }
+
+ if (
+ !is_callable('random_bytes')
+ &&
+ $RandomCompatUrandom
+ &&
+ @is_readable('/dev/urandom')
+ ) {
+ // Error suppression on is_readable() in case of an open_basedir
+ // or safe_mode failure. All we care about is whether or not we
+ // can read it at this point. If the PHP environment is going to
+ // panic over trying to see if the file can be read in the first
+ // place, that is not helpful to us here.
+
+ // See random_bytes_dev_urandom.php
+ require_once $RandomCompatDIR . '/random_bytes_dev_urandom.php';
+ }
+ // Unset variables after use
+ $RandomCompat_basedir = null;
+ } else {
+ $RandomCompatUrandom = false;
+ }
+
+ /**
+ * mcrypt_create_iv()
+ *
+ * We only want to use mcypt_create_iv() if:
+ *
+ * - random_bytes() hasn't already been defined
+ * - the mcrypt extensions is loaded
+ * - One of these two conditions is true:
+ * - We're on Windows (DIRECTORY_SEPARATOR !== '/')
+ * - We're not on Windows and /dev/urandom is readabale
+ * (i.e. we're not in a chroot jail)
+ * - Special case:
+ * - If we're not on Windows, but the PHP version is between
+ * 5.6.10 and 5.6.12, we don't want to use mcrypt. It will
+ * hang indefinitely. This is bad.
+ * - If we're on Windows, we want to use PHP >= 5.3.7 or else
+ * we get insufficient entropy errors.
+ */
+ if (
+ !is_callable('random_bytes')
+ &&
+ // Windows on PHP < 5.3.7 is broken, but non-Windows is not known to be.
+ (DIRECTORY_SEPARATOR === '/' || PHP_VERSION_ID >= 50307)
+ &&
+ // Prevent this code from hanging indefinitely on non-Windows;
+ // see https://bugs.php.net/bug.php?id=69833
+ (
+ DIRECTORY_SEPARATOR !== '/' ||
+ (PHP_VERSION_ID <= 50609 || PHP_VERSION_ID >= 50613)
+ )
+ &&
+ extension_loaded('mcrypt')
+ ) {
+ // See random_bytes_mcrypt.php
+ require_once $RandomCompatDIR . '/random_bytes_mcrypt.php';
+ }
+ $RandomCompatUrandom = null;
+
+ /**
+ * This is a Windows-specific fallback, for when the mcrypt extension
+ * isn't loaded.
+ */
+ if (
+ !is_callable('random_bytes')
+ &&
+ extension_loaded('com_dotnet')
+ &&
+ class_exists('COM')
+ ) {
+ $RandomCompat_disabled_classes = preg_split(
+ '#\s*,\s*#',
+ strtolower(ini_get('disable_classes'))
+ );
+
+ if (!in_array('com', $RandomCompat_disabled_classes)) {
+ try {
+ $RandomCompatCOMtest = new COM('CAPICOM.Utilities.1');
+ if (method_exists($RandomCompatCOMtest, 'GetRandom')) {
+ // See random_bytes_com_dotnet.php
+ require_once $RandomCompatDIR . '/random_bytes_com_dotnet.php';
+ }
+ } catch (com_exception $e) {
+ // Don't try to use it.
+ }
+ }
+ $RandomCompat_disabled_classes = null;
+ $RandomCompatCOMtest = null;
+ }
+
+ /**
+ * throw new Exception
+ */
+ if (!is_callable('random_bytes')) {
+ /**
+ * We don't have any more options, so let's throw an exception right now
+ * and hope the developer won't let it fail silently.
+ *
+ * @param mixed $length
+ * @psalm-suppress MissingReturnType
+ * @throws Exception
+ * @return string
+ */
+ function random_bytes($length)
+ {
+ unset($length); // Suppress "variable not used" warnings.
+ throw new Exception(
+ 'There is no suitable CSPRNG installed on your system'
+ );
+ return '';
+ }
+ }
+}
+
+if (!is_callable('random_int')) {
+ require_once $RandomCompatDIR . '/random_int.php';
+}
+
+$RandomCompatDIR = null;
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php
new file mode 100644
index 0000000..fc1926e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php
@@ -0,0 +1,88 @@
+GetRandom($bytes, 0));
+ if (RandomCompat_strlen($buf) >= $bytes) {
+ /**
+ * Return our random entropy buffer here:
+ */
+ return RandomCompat_substr($buf, 0, $bytes);
+ }
+ ++$execCount;
+ } while ($execCount < $bytes);
+
+ /**
+ * If we reach here, PHP has failed us.
+ */
+ throw new Exception(
+ 'Could not gather sufficient random data'
+ );
+ }
+}
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php
new file mode 100644
index 0000000..df5b915
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php
@@ -0,0 +1,167 @@
+ 0);
+
+ /**
+ * Is our result valid?
+ */
+ if (is_string($buf)) {
+ if (RandomCompat_strlen($buf) === $bytes) {
+ /**
+ * Return our random entropy buffer here:
+ */
+ return $buf;
+ }
+ }
+ }
+
+ /**
+ * If we reach here, PHP has failed us.
+ */
+ throw new Exception(
+ 'Error reading from source device'
+ );
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php
new file mode 100644
index 0000000..4af1a24
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php
@@ -0,0 +1,88 @@
+ 2147483647) {
+ $buf = '';
+ for ($i = 0; $i < $bytes; $i += 1073741824) {
+ $n = ($bytes - $i) > 1073741824
+ ? 1073741824
+ : $bytes - $i;
+ $buf .= \Sodium\randombytes_buf($n);
+ }
+ } else {
+ $buf = \Sodium\randombytes_buf($bytes);
+ }
+
+ if ($buf !== false) {
+ if (RandomCompat_strlen($buf) === $bytes) {
+ return $buf;
+ }
+ }
+
+ /**
+ * If we reach here, PHP has failed us.
+ */
+ throw new Exception(
+ 'Could not gather sufficient random data'
+ );
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php
new file mode 100644
index 0000000..705af52
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php
@@ -0,0 +1,92 @@
+ 2147483647) {
+ for ($i = 0; $i < $bytes; $i += 1073741824) {
+ $n = ($bytes - $i) > 1073741824
+ ? 1073741824
+ : $bytes - $i;
+ $buf .= Sodium::randombytes_buf((int) $n);
+ }
+ } else {
+ $buf .= Sodium::randombytes_buf((int) $bytes);
+ }
+
+ if (is_string($buf)) {
+ if (RandomCompat_strlen($buf) === $bytes) {
+ return $buf;
+ }
+ }
+
+ /**
+ * If we reach here, PHP has failed us.
+ */
+ throw new Exception(
+ 'Could not gather sufficient random data'
+ );
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php
new file mode 100644
index 0000000..aac9c01
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php
@@ -0,0 +1,77 @@
+ operators might accidentally let a float
+ * through.
+ */
+
+ try {
+ $min = RandomCompat_intval($min);
+ } catch (TypeError $ex) {
+ throw new TypeError(
+ 'random_int(): $min must be an integer'
+ );
+ }
+
+ try {
+ $max = RandomCompat_intval($max);
+ } catch (TypeError $ex) {
+ throw new TypeError(
+ 'random_int(): $max must be an integer'
+ );
+ }
+
+ /**
+ * Now that we've verified our weak typing system has given us an integer,
+ * let's validate the logic then we can move forward with generating random
+ * integers along a given range.
+ */
+ if ($min > $max) {
+ throw new Error(
+ 'Minimum value must be less than or equal to the maximum value'
+ );
+ }
+
+ if ($max === $min) {
+ return (int) $min;
+ }
+
+ /**
+ * Initialize variables to 0
+ *
+ * We want to store:
+ * $bytes => the number of random bytes we need
+ * $mask => an integer bitmask (for use with the &) operator
+ * so we can minimize the number of discards
+ */
+ $attempts = $bits = $bytes = $mask = $valueShift = 0;
+
+ /**
+ * At this point, $range is a positive number greater than 0. It might
+ * overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to
+ * a float and we will lose some precision.
+ */
+ $range = $max - $min;
+
+ /**
+ * Test for integer overflow:
+ */
+ if (!is_int($range)) {
+
+ /**
+ * Still safely calculate wider ranges.
+ * Provided by @CodesInChaos, @oittaa
+ *
+ * @ref https://gist.github.com/CodesInChaos/03f9ea0b58e8b2b8d435
+ *
+ * We use ~0 as a mask in this case because it generates all 1s
+ *
+ * @ref https://eval.in/400356 (32-bit)
+ * @ref http://3v4l.org/XX9r5 (64-bit)
+ */
+ $bytes = PHP_INT_SIZE;
+ $mask = ~0;
+
+ } else {
+
+ /**
+ * $bits is effectively ceil(log($range, 2)) without dealing with
+ * type juggling
+ */
+ while ($range > 0) {
+ if ($bits % 8 === 0) {
+ ++$bytes;
+ }
+ ++$bits;
+ $range >>= 1;
+ $mask = $mask << 1 | 1;
+ }
+ $valueShift = $min;
+ }
+
+ $val = 0;
+ /**
+ * Now that we have our parameters set up, let's begin generating
+ * random integers until one falls between $min and $max
+ */
+ do {
+ /**
+ * The rejection probability is at most 0.5, so this corresponds
+ * to a failure probability of 2^-128 for a working RNG
+ */
+ if ($attempts > 128) {
+ throw new Exception(
+ 'random_int: RNG is broken - too many rejections'
+ );
+ }
+
+ /**
+ * Let's grab the necessary number of random bytes
+ */
+ $randomByteString = random_bytes($bytes);
+
+ /**
+ * Let's turn $randomByteString into an integer
+ *
+ * This uses bitwise operators (<< and |) to build an integer
+ * out of the values extracted from ord()
+ *
+ * Example: [9F] | [6D] | [32] | [0C] =>
+ * 159 + 27904 + 3276800 + 201326592 =>
+ * 204631455
+ */
+ $val &= 0;
+ for ($i = 0; $i < $bytes; ++$i) {
+ $val |= ord($randomByteString[$i]) << ($i * 8);
+ }
+
+ /**
+ * Apply mask
+ */
+ $val &= $mask;
+ $val += $valueShift;
+
+ ++$attempts;
+ /**
+ * If $val overflows to a floating point number,
+ * ... or is larger than $max,
+ * ... or smaller than $min,
+ * then try again.
+ */
+ } while (!is_int($val) || $val > $max || $val < $min);
+
+ return (int) $val;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/other/build_phar.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/other/build_phar.php
new file mode 100644
index 0000000..70ef4b2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/other/build_phar.php
@@ -0,0 +1,57 @@
+buildFromDirectory(dirname(__DIR__).'/lib');
+rename(
+ dirname(__DIR__).'/lib/index.php',
+ dirname(__DIR__).'/lib/random.php'
+);
+
+/**
+ * If we pass an (optional) path to a private key as a second argument, we will
+ * sign the Phar with OpenSSL.
+ *
+ * If you leave this out, it will produce an unsigned .phar!
+ */
+if ($argc > 1) {
+ if (!@is_readable($argv[1])) {
+ echo 'Could not read the private key file:', $argv[1], "\n";
+ exit(255);
+ }
+ $pkeyFile = file_get_contents($argv[1]);
+
+ $private = openssl_get_privatekey($pkeyFile);
+ if ($private !== false) {
+ $pkey = '';
+ openssl_pkey_export($private, $pkey);
+ $phar->setSignatureAlgorithm(Phar::OPENSSL, $pkey);
+
+ /**
+ * Save the corresponding public key to the file
+ */
+ if (!@is_readable($dist.'/random_compat.phar.pubkey')) {
+ $details = openssl_pkey_get_details($private);
+ file_put_contents(
+ $dist.'/random_compat.phar.pubkey',
+ $details['key']
+ );
+ }
+ } else {
+ echo 'An error occurred reading the private key from OpenSSL.', "\n";
+ exit(255);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/psalm-autoload.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/psalm-autoload.php
new file mode 100644
index 0000000..d71d1b8
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/paragonie/random_compat/psalm-autoload.php
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/psr/http-message/CHANGELOG.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/psr/http-message/CHANGELOG.md
new file mode 100644
index 0000000..74b1ef9
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/psr/http-message/CHANGELOG.md
@@ -0,0 +1,36 @@
+# Changelog
+
+All notable changes to this project will be documented in this file, in reverse chronological order by release.
+
+## 1.0.1 - 2016-08-06
+
+### Added
+
+- Nothing.
+
+### Deprecated
+
+- Nothing.
+
+### Removed
+
+- Nothing.
+
+### Fixed
+
+- Updated all `@return self` annotation references in interfaces to use
+ `@return static`, which more closelly follows the semantics of the
+ specification.
+- Updated the `MessageInterface::getHeaders()` return annotation to use the
+ value `string[][]`, indicating the format is a nested array of strings.
+- Updated the `@link` annotation for `RequestInterface::withRequestTarget()`
+ to point to the correct section of RFC 7230.
+- Updated the `ServerRequestInterface::withUploadedFiles()` parameter annotation
+ to add the parameter name (`$uploadedFiles`).
+- Updated a `@throws` annotation for the `UploadedFileInterface::moveTo()`
+ method to correctly reference the method parameter (it was referencing an
+ incorrect parameter name previously).
+
+## 1.0.0 - 2016-05-18
+
+Initial stable release; reflects accepted PSR-7 specification.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/psr/http-message/LICENSE b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/psr/http-message/LICENSE
new file mode 100644
index 0000000..c2d8e45
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/psr/http-message/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014 PHP Framework Interoperability Group
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/psr/http-message/README.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/psr/http-message/README.md
new file mode 100644
index 0000000..2818533
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/psr/http-message/README.md
@@ -0,0 +1,13 @@
+PSR Http Message
+================
+
+This repository holds all interfaces/classes/traits related to
+[PSR-7](http://www.php-fig.org/psr/psr-7/).
+
+Note that this is not a HTTP message implementation of its own. It is merely an
+interface that describes a HTTP message. See the specification for more details.
+
+Usage
+-----
+
+We'll certainly need some stuff in here.
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/psr/http-message/composer.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/psr/http-message/composer.json
new file mode 100644
index 0000000..b0d2937
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/psr/http-message/composer.json
@@ -0,0 +1,26 @@
+{
+ "name": "psr/http-message",
+ "description": "Common interface for HTTP messages",
+ "keywords": ["psr", "psr-7", "http", "http-message", "request", "response"],
+ "homepage": "https://github.com/php-fig/http-message",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/psr/http-message/src/MessageInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/psr/http-message/src/MessageInterface.php
new file mode 100644
index 0000000..dd46e5e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/psr/http-message/src/MessageInterface.php
@@ -0,0 +1,187 @@
+getHeaders() as $name => $values) {
+ * echo $name . ": " . implode(", ", $values);
+ * }
+ *
+ * // Emit headers iteratively:
+ * foreach ($message->getHeaders() as $name => $values) {
+ * foreach ($values as $value) {
+ * header(sprintf('%s: %s', $name, $value), false);
+ * }
+ * }
+ *
+ * While header names are not case-sensitive, getHeaders() will preserve the
+ * exact case in which headers were originally specified.
+ *
+ * @return string[][] Returns an associative array of the message's headers. Each
+ * key MUST be a header name, and each value MUST be an array of strings
+ * for that header.
+ */
+ public function getHeaders();
+
+ /**
+ * Checks if a header exists by the given case-insensitive name.
+ *
+ * @param string $name Case-insensitive header field name.
+ * @return bool Returns true if any header names match the given header
+ * name using a case-insensitive string comparison. Returns false if
+ * no matching header name is found in the message.
+ */
+ public function hasHeader($name);
+
+ /**
+ * Retrieves a message header value by the given case-insensitive name.
+ *
+ * This method returns an array of all the header values of the given
+ * case-insensitive header name.
+ *
+ * If the header does not appear in the message, this method MUST return an
+ * empty array.
+ *
+ * @param string $name Case-insensitive header field name.
+ * @return string[] An array of string values as provided for the given
+ * header. If the header does not appear in the message, this method MUST
+ * return an empty array.
+ */
+ public function getHeader($name);
+
+ /**
+ * Retrieves a comma-separated string of the values for a single header.
+ *
+ * This method returns all of the header values of the given
+ * case-insensitive header name as a string concatenated together using
+ * a comma.
+ *
+ * NOTE: Not all header values may be appropriately represented using
+ * comma concatenation. For such headers, use getHeader() instead
+ * and supply your own delimiter when concatenating.
+ *
+ * If the header does not appear in the message, this method MUST return
+ * an empty string.
+ *
+ * @param string $name Case-insensitive header field name.
+ * @return string A string of values as provided for the given header
+ * concatenated together using a comma. If the header does not appear in
+ * the message, this method MUST return an empty string.
+ */
+ public function getHeaderLine($name);
+
+ /**
+ * Return an instance with the provided value replacing the specified header.
+ *
+ * While header names are case-insensitive, the casing of the header will
+ * be preserved by this function, and returned from getHeaders().
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * new and/or updated header and value.
+ *
+ * @param string $name Case-insensitive header field name.
+ * @param string|string[] $value Header value(s).
+ * @return static
+ * @throws \InvalidArgumentException for invalid header names or values.
+ */
+ public function withHeader($name, $value);
+
+ /**
+ * Return an instance with the specified header appended with the given value.
+ *
+ * Existing values for the specified header will be maintained. The new
+ * value(s) will be appended to the existing list. If the header did not
+ * exist previously, it will be added.
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * new header and/or value.
+ *
+ * @param string $name Case-insensitive header field name to add.
+ * @param string|string[] $value Header value(s).
+ * @return static
+ * @throws \InvalidArgumentException for invalid header names or values.
+ */
+ public function withAddedHeader($name, $value);
+
+ /**
+ * Return an instance without the specified header.
+ *
+ * Header resolution MUST be done without case-sensitivity.
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that removes
+ * the named header.
+ *
+ * @param string $name Case-insensitive header field name to remove.
+ * @return static
+ */
+ public function withoutHeader($name);
+
+ /**
+ * Gets the body of the message.
+ *
+ * @return StreamInterface Returns the body as a stream.
+ */
+ public function getBody();
+
+ /**
+ * Return an instance with the specified message body.
+ *
+ * The body MUST be a StreamInterface object.
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return a new instance that has the
+ * new body stream.
+ *
+ * @param StreamInterface $body Body.
+ * @return static
+ * @throws \InvalidArgumentException When the body is not valid.
+ */
+ public function withBody(StreamInterface $body);
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/psr/http-message/src/RequestInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/psr/http-message/src/RequestInterface.php
new file mode 100644
index 0000000..a96d4fd
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/psr/http-message/src/RequestInterface.php
@@ -0,0 +1,129 @@
+getQuery()`
+ * or from the `QUERY_STRING` server param.
+ *
+ * @return array
+ */
+ public function getQueryParams();
+
+ /**
+ * Return an instance with the specified query string arguments.
+ *
+ * These values SHOULD remain immutable over the course of the incoming
+ * request. They MAY be injected during instantiation, such as from PHP's
+ * $_GET superglobal, or MAY be derived from some other value such as the
+ * URI. In cases where the arguments are parsed from the URI, the data
+ * MUST be compatible with what PHP's parse_str() would return for
+ * purposes of how duplicate query parameters are handled, and how nested
+ * sets are handled.
+ *
+ * Setting query string arguments MUST NOT change the URI stored by the
+ * request, nor the values in the server params.
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * updated query string arguments.
+ *
+ * @param array $query Array of query string arguments, typically from
+ * $_GET.
+ * @return static
+ */
+ public function withQueryParams(array $query);
+
+ /**
+ * Retrieve normalized file upload data.
+ *
+ * This method returns upload metadata in a normalized tree, with each leaf
+ * an instance of Psr\Http\Message\UploadedFileInterface.
+ *
+ * These values MAY be prepared from $_FILES or the message body during
+ * instantiation, or MAY be injected via withUploadedFiles().
+ *
+ * @return array An array tree of UploadedFileInterface instances; an empty
+ * array MUST be returned if no data is present.
+ */
+ public function getUploadedFiles();
+
+ /**
+ * Create a new instance with the specified uploaded files.
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * updated body parameters.
+ *
+ * @param array $uploadedFiles An array tree of UploadedFileInterface instances.
+ * @return static
+ * @throws \InvalidArgumentException if an invalid structure is provided.
+ */
+ public function withUploadedFiles(array $uploadedFiles);
+
+ /**
+ * Retrieve any parameters provided in the request body.
+ *
+ * If the request Content-Type is either application/x-www-form-urlencoded
+ * or multipart/form-data, and the request method is POST, this method MUST
+ * return the contents of $_POST.
+ *
+ * Otherwise, this method may return any results of deserializing
+ * the request body content; as parsing returns structured content, the
+ * potential types MUST be arrays or objects only. A null value indicates
+ * the absence of body content.
+ *
+ * @return null|array|object The deserialized body parameters, if any.
+ * These will typically be an array or object.
+ */
+ public function getParsedBody();
+
+ /**
+ * Return an instance with the specified body parameters.
+ *
+ * These MAY be injected during instantiation.
+ *
+ * If the request Content-Type is either application/x-www-form-urlencoded
+ * or multipart/form-data, and the request method is POST, use this method
+ * ONLY to inject the contents of $_POST.
+ *
+ * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of
+ * deserializing the request body content. Deserialization/parsing returns
+ * structured data, and, as such, this method ONLY accepts arrays or objects,
+ * or a null value if nothing was available to parse.
+ *
+ * As an example, if content negotiation determines that the request data
+ * is a JSON payload, this method could be used to create a request
+ * instance with the deserialized parameters.
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * updated body parameters.
+ *
+ * @param null|array|object $data The deserialized body data. This will
+ * typically be in an array or object.
+ * @return static
+ * @throws \InvalidArgumentException if an unsupported argument type is
+ * provided.
+ */
+ public function withParsedBody($data);
+
+ /**
+ * Retrieve attributes derived from the request.
+ *
+ * The request "attributes" may be used to allow injection of any
+ * parameters derived from the request: e.g., the results of path
+ * match operations; the results of decrypting cookies; the results of
+ * deserializing non-form-encoded message bodies; etc. Attributes
+ * will be application and request specific, and CAN be mutable.
+ *
+ * @return array Attributes derived from the request.
+ */
+ public function getAttributes();
+
+ /**
+ * Retrieve a single derived request attribute.
+ *
+ * Retrieves a single derived request attribute as described in
+ * getAttributes(). If the attribute has not been previously set, returns
+ * the default value as provided.
+ *
+ * This method obviates the need for a hasAttribute() method, as it allows
+ * specifying a default value to return if the attribute is not found.
+ *
+ * @see getAttributes()
+ * @param string $name The attribute name.
+ * @param mixed $default Default value to return if the attribute does not exist.
+ * @return mixed
+ */
+ public function getAttribute($name, $default = null);
+
+ /**
+ * Return an instance with the specified derived request attribute.
+ *
+ * This method allows setting a single derived request attribute as
+ * described in getAttributes().
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * updated attribute.
+ *
+ * @see getAttributes()
+ * @param string $name The attribute name.
+ * @param mixed $value The value of the attribute.
+ * @return static
+ */
+ public function withAttribute($name, $value);
+
+ /**
+ * Return an instance that removes the specified derived request attribute.
+ *
+ * This method allows removing a single derived request attribute as
+ * described in getAttributes().
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that removes
+ * the attribute.
+ *
+ * @see getAttributes()
+ * @param string $name The attribute name.
+ * @return static
+ */
+ public function withoutAttribute($name);
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/psr/http-message/src/StreamInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/psr/http-message/src/StreamInterface.php
new file mode 100644
index 0000000..f68f391
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/psr/http-message/src/StreamInterface.php
@@ -0,0 +1,158 @@
+
+ * [user-info@]host[:port]
+ *
+ *
+ * If the port component is not set or is the standard port for the current
+ * scheme, it SHOULD NOT be included.
+ *
+ * @see https://tools.ietf.org/html/rfc3986#section-3.2
+ * @return string The URI authority, in "[user-info@]host[:port]" format.
+ */
+ public function getAuthority();
+
+ /**
+ * Retrieve the user information component of the URI.
+ *
+ * If no user information is present, this method MUST return an empty
+ * string.
+ *
+ * If a user is present in the URI, this will return that value;
+ * additionally, if the password is also present, it will be appended to the
+ * user value, with a colon (":") separating the values.
+ *
+ * The trailing "@" character is not part of the user information and MUST
+ * NOT be added.
+ *
+ * @return string The URI user information, in "username[:password]" format.
+ */
+ public function getUserInfo();
+
+ /**
+ * Retrieve the host component of the URI.
+ *
+ * If no host is present, this method MUST return an empty string.
+ *
+ * The value returned MUST be normalized to lowercase, per RFC 3986
+ * Section 3.2.2.
+ *
+ * @see http://tools.ietf.org/html/rfc3986#section-3.2.2
+ * @return string The URI host.
+ */
+ public function getHost();
+
+ /**
+ * Retrieve the port component of the URI.
+ *
+ * If a port is present, and it is non-standard for the current scheme,
+ * this method MUST return it as an integer. If the port is the standard port
+ * used with the current scheme, this method SHOULD return null.
+ *
+ * If no port is present, and no scheme is present, this method MUST return
+ * a null value.
+ *
+ * If no port is present, but a scheme is present, this method MAY return
+ * the standard port for that scheme, but SHOULD return null.
+ *
+ * @return null|int The URI port.
+ */
+ public function getPort();
+
+ /**
+ * Retrieve the path component of the URI.
+ *
+ * The path can either be empty or absolute (starting with a slash) or
+ * rootless (not starting with a slash). Implementations MUST support all
+ * three syntaxes.
+ *
+ * Normally, the empty path "" and absolute path "/" are considered equal as
+ * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically
+ * do this normalization because in contexts with a trimmed base path, e.g.
+ * the front controller, this difference becomes significant. It's the task
+ * of the user to handle both "" and "/".
+ *
+ * The value returned MUST be percent-encoded, but MUST NOT double-encode
+ * any characters. To determine what characters to encode, please refer to
+ * RFC 3986, Sections 2 and 3.3.
+ *
+ * As an example, if the value should include a slash ("/") not intended as
+ * delimiter between path segments, that value MUST be passed in encoded
+ * form (e.g., "%2F") to the instance.
+ *
+ * @see https://tools.ietf.org/html/rfc3986#section-2
+ * @see https://tools.ietf.org/html/rfc3986#section-3.3
+ * @return string The URI path.
+ */
+ public function getPath();
+
+ /**
+ * Retrieve the query string of the URI.
+ *
+ * If no query string is present, this method MUST return an empty string.
+ *
+ * The leading "?" character is not part of the query and MUST NOT be
+ * added.
+ *
+ * The value returned MUST be percent-encoded, but MUST NOT double-encode
+ * any characters. To determine what characters to encode, please refer to
+ * RFC 3986, Sections 2 and 3.4.
+ *
+ * As an example, if a value in a key/value pair of the query string should
+ * include an ampersand ("&") not intended as a delimiter between values,
+ * that value MUST be passed in encoded form (e.g., "%26") to the instance.
+ *
+ * @see https://tools.ietf.org/html/rfc3986#section-2
+ * @see https://tools.ietf.org/html/rfc3986#section-3.4
+ * @return string The URI query string.
+ */
+ public function getQuery();
+
+ /**
+ * Retrieve the fragment component of the URI.
+ *
+ * If no fragment is present, this method MUST return an empty string.
+ *
+ * The leading "#" character is not part of the fragment and MUST NOT be
+ * added.
+ *
+ * The value returned MUST be percent-encoded, but MUST NOT double-encode
+ * any characters. To determine what characters to encode, please refer to
+ * RFC 3986, Sections 2 and 3.5.
+ *
+ * @see https://tools.ietf.org/html/rfc3986#section-2
+ * @see https://tools.ietf.org/html/rfc3986#section-3.5
+ * @return string The URI fragment.
+ */
+ public function getFragment();
+
+ /**
+ * Return an instance with the specified scheme.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified scheme.
+ *
+ * Implementations MUST support the schemes "http" and "https" case
+ * insensitively, and MAY accommodate other schemes if required.
+ *
+ * An empty scheme is equivalent to removing the scheme.
+ *
+ * @param string $scheme The scheme to use with the new instance.
+ * @return static A new instance with the specified scheme.
+ * @throws \InvalidArgumentException for invalid or unsupported schemes.
+ */
+ public function withScheme($scheme);
+
+ /**
+ * Return an instance with the specified user information.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified user information.
+ *
+ * Password is optional, but the user information MUST include the
+ * user; an empty string for the user is equivalent to removing user
+ * information.
+ *
+ * @param string $user The user name to use for authority.
+ * @param null|string $password The password associated with $user.
+ * @return static A new instance with the specified user information.
+ */
+ public function withUserInfo($user, $password = null);
+
+ /**
+ * Return an instance with the specified host.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified host.
+ *
+ * An empty host value is equivalent to removing the host.
+ *
+ * @param string $host The hostname to use with the new instance.
+ * @return static A new instance with the specified host.
+ * @throws \InvalidArgumentException for invalid hostnames.
+ */
+ public function withHost($host);
+
+ /**
+ * Return an instance with the specified port.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified port.
+ *
+ * Implementations MUST raise an exception for ports outside the
+ * established TCP and UDP port ranges.
+ *
+ * A null value provided for the port is equivalent to removing the port
+ * information.
+ *
+ * @param null|int $port The port to use with the new instance; a null value
+ * removes the port information.
+ * @return static A new instance with the specified port.
+ * @throws \InvalidArgumentException for invalid ports.
+ */
+ public function withPort($port);
+
+ /**
+ * Return an instance with the specified path.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified path.
+ *
+ * The path can either be empty or absolute (starting with a slash) or
+ * rootless (not starting with a slash). Implementations MUST support all
+ * three syntaxes.
+ *
+ * If the path is intended to be domain-relative rather than path relative then
+ * it must begin with a slash ("/"). Paths not starting with a slash ("/")
+ * are assumed to be relative to some base path known to the application or
+ * consumer.
+ *
+ * Users can provide both encoded and decoded path characters.
+ * Implementations ensure the correct encoding as outlined in getPath().
+ *
+ * @param string $path The path to use with the new instance.
+ * @return static A new instance with the specified path.
+ * @throws \InvalidArgumentException for invalid paths.
+ */
+ public function withPath($path);
+
+ /**
+ * Return an instance with the specified query string.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified query string.
+ *
+ * Users can provide both encoded and decoded query characters.
+ * Implementations ensure the correct encoding as outlined in getQuery().
+ *
+ * An empty query string value is equivalent to removing the query string.
+ *
+ * @param string $query The query string to use with the new instance.
+ * @return static A new instance with the specified query string.
+ * @throws \InvalidArgumentException for invalid query strings.
+ */
+ public function withQuery($query);
+
+ /**
+ * Return an instance with the specified URI fragment.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified URI fragment.
+ *
+ * Users can provide both encoded and decoded fragment characters.
+ * Implementations ensure the correct encoding as outlined in getFragment().
+ *
+ * An empty fragment value is equivalent to removing the fragment.
+ *
+ * @param string $fragment The fragment to use with the new instance.
+ * @return static A new instance with the specified fragment.
+ */
+ public function withFragment($fragment);
+
+ /**
+ * Return the string representation as a URI reference.
+ *
+ * Depending on which components of the URI are present, the resulting
+ * string is either a full URI or relative reference according to RFC 3986,
+ * Section 4.1. The method concatenates the various components of the URI,
+ * using the appropriate delimiters:
+ *
+ * - If a scheme is present, it MUST be suffixed by ":".
+ * - If an authority is present, it MUST be prefixed by "//".
+ * - The path can be concatenated without delimiters. But there are two
+ * cases where the path has to be adjusted to make the URI reference
+ * valid as PHP does not allow to throw an exception in __toString():
+ * - If the path is rootless and an authority is present, the path MUST
+ * be prefixed by "/".
+ * - If the path is starting with more than one "/" and no authority is
+ * present, the starting slashes MUST be reduced to one.
+ * - If a query is present, it MUST be prefixed by "?".
+ * - If a fragment is present, it MUST be prefixed by "#".
+ *
+ * @see http://tools.ietf.org/html/rfc3986#section-4.1
+ * @return string
+ */
+ public function __toString();
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/.gitignore b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/.gitignore
new file mode 100644
index 0000000..42ab5d5
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/.gitignore
@@ -0,0 +1,4 @@
+composer.lock
+vendor
+tests/ab/reports
+reports
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/.travis.yml b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/.travis.yml
new file mode 100644
index 0000000..11d51b4
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/.travis.yml
@@ -0,0 +1,20 @@
+language: php
+
+php:
+ - 5.4
+ - 5.5
+ - 5.6
+ - 7
+ - hhvm
+
+before_install:
+ - export PATH=$HOME/.local/bin:$PATH
+ - pip install --user autobahntestsuite
+ - pip list --user autobahntestsuite
+
+before_script:
+ - composer install
+ - sh tests/ab/run_ab_tests.sh
+
+script:
+ - vendor/bin/phpunit
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/LICENSE b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/LICENSE
new file mode 100644
index 0000000..7f8c128
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2011-2016 Chris Boden
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/README.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/README.md
new file mode 100644
index 0000000..7c09148
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/README.md
@@ -0,0 +1,13 @@
+# RFC6455 - The WebSocket Protocol
+
+[](https://travis-ci.org/ratchetphp/RFC6455)
+
+
+This library a protocol handler for the RFC6455 specification.
+It contains components for both server and client side handshake and messaging protocol negotation.
+
+Aspects that are left open to interpertation in the specification are also left open in this library.
+It is up to the implementation to determine how those interpertations are to be dealt with.
+
+This library is independent, framework agnostic, and does not deal with any I/O.
+HTTP upgrade negotiation integration points are handled with PSR-7 interfaces.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/composer.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/composer.json
new file mode 100644
index 0000000..224066b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/composer.json
@@ -0,0 +1,32 @@
+{
+ "name": "ratchet/rfc6455",
+ "type": "library",
+ "description": "RFC6455 WebSocket protocol handler",
+ "keywords": ["WebSockets", "websocket", "RFC6455"],
+ "homepage": "http://socketo.me",
+ "license": "MIT",
+ "authors": [{
+ "name": "Chris Boden"
+ , "email": "cboden@gmail.com"
+ , "role": "Developer"
+ }],
+ "support": {
+ "forum": "https://groups.google.com/forum/#!forum/ratchet-php"
+ , "issues": "https://github.com/ratchetphp/RFC6455/issues"
+ , "irc": "irc://irc.freenode.org/reactphp"
+ },
+ "autoload": {
+ "psr-4": {
+ "Ratchet\\RFC6455\\": "src"
+ }
+ },
+ "require": {
+ "php": ">=5.4.2",
+ "guzzlehttp/psr7": "^1.0"
+ },
+ "require-dev": {
+ "react/http": "^0.4.1",
+ "react/socket-client": "^0.4.3",
+ "phpunit/phpunit": "4.8.*"
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/phpunit.xml.dist b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/phpunit.xml.dist
new file mode 100644
index 0000000..8f2e7d1
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/phpunit.xml.dist
@@ -0,0 +1,27 @@
+
+
+
+
+
+ tests
+
+ test/ab
+
+
+
+
+
+
+ ./src/
+
+
+
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Handshake/ClientNegotiator.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Handshake/ClientNegotiator.php
new file mode 100644
index 0000000..70856df
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Handshake/ClientNegotiator.php
@@ -0,0 +1,53 @@
+verifier = new ResponseVerifier;
+
+ $this->defaultHeader = new Request('GET', '', [
+ 'Connection' => 'Upgrade'
+ , 'Upgrade' => 'websocket'
+ , 'Sec-WebSocket-Version' => $this->getVersion()
+ , 'User-Agent' => "Ratchet"
+ ]);
+ }
+
+ public function generateRequest(UriInterface $uri) {
+ return $this->defaultHeader->withUri($uri)
+ ->withHeader("Sec-WebSocket-Key", $this->generateKey());
+ }
+
+ public function validateResponse(RequestInterface $request, ResponseInterface $response) {
+ return $this->verifier->verifyAll($request, $response);
+ }
+
+ public function generateKey() {
+ $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwzyz1234567890+/=';
+ $charRange = strlen($chars) - 1;
+ $key = '';
+ for ($i = 0; $i < 16; $i++) {
+ $key .= $chars[mt_rand(0, $charRange)];
+ }
+
+ return base64_encode($key);
+ }
+
+ public function getVersion() {
+ return 13;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Handshake/NegotiatorInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Handshake/NegotiatorInterface.php
new file mode 100644
index 0000000..c152eca
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Handshake/NegotiatorInterface.php
@@ -0,0 +1,47 @@
+verifyMethod($request->getMethod());
+ $passes += (int)$this->verifyHTTPVersion($request->getProtocolVersion());
+ $passes += (int)$this->verifyRequestURI($request->getUri()->getPath());
+ $passes += (int)$this->verifyHost($request->getHeader('Host'));
+ $passes += (int)$this->verifyUpgradeRequest($request->getHeader('Upgrade'));
+ $passes += (int)$this->verifyConnection($request->getHeader('Connection'));
+ $passes += (int)$this->verifyKey($request->getHeader('Sec-WebSocket-Key'));
+ $passes += (int)$this->verifyVersion($request->getHeader('Sec-WebSocket-Version'));
+
+ return (8 === $passes);
+ }
+
+ /**
+ * Test the HTTP method. MUST be "GET"
+ * @param string
+ * @return bool
+ */
+ public function verifyMethod($val) {
+ return ('get' === strtolower($val));
+ }
+
+ /**
+ * Test the HTTP version passed. MUST be 1.1 or greater
+ * @param string|int
+ * @return bool
+ */
+ public function verifyHTTPVersion($val) {
+ return (1.1 <= (double)$val);
+ }
+
+ /**
+ * @param string
+ * @return bool
+ */
+ public function verifyRequestURI($val) {
+ if ($val[0] !== '/') {
+ return false;
+ }
+
+ if (false !== strstr($val, '#')) {
+ return false;
+ }
+
+ if (!extension_loaded('mbstring')) {
+ return true;
+ }
+
+ return mb_check_encoding($val, 'US-ASCII');
+ }
+
+ /**
+ * @param array $hostHeader
+ * @return bool
+ * @todo Once I fix HTTP::getHeaders just verify this isn't NULL or empty...or maybe need to verify it's a valid domain??? Or should it equal $_SERVER['HOST'] ?
+ */
+ public function verifyHost(array $hostHeader) {
+ return (1 === count($hostHeader));
+ }
+
+ /**
+ * Verify the Upgrade request to WebSockets.
+ * @param array $upgradeHeader MUST equal "websocket"
+ * @return bool
+ */
+ public function verifyUpgradeRequest(array $upgradeHeader) {
+ return (1 === count($upgradeHeader) && 'websocket' === strtolower($upgradeHeader[0]));
+ }
+
+ /**
+ * Verify the Connection header
+ * @param array $connectionHeader MUST include "Upgrade"
+ * @return bool
+ */
+ public function verifyConnection(array $connectionHeader) {
+ foreach ($connectionHeader as $l) {
+ $upgrades = array_filter(
+ array_map('trim', array_map('strtolower', explode(',', $l))),
+ function ($x) {
+ return 'upgrade' === $x;
+ }
+ );
+ if (count($upgrades) > 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This function verifies the nonce is valid (64 big encoded, 16 bytes random string)
+ * @param array $keyHeader
+ * @return bool
+ * @todo The spec says we don't need to base64_decode - can I just check if the length is 24 and not decode?
+ * @todo Check the spec to see what the encoding of the key could be
+ */
+ public function verifyKey(array $keyHeader) {
+ return (1 === count($keyHeader) && 16 === strlen(base64_decode($keyHeader[0])));
+ }
+
+ /**
+ * Verify the version passed matches this RFC
+ * @param string|int $versionHeader MUST equal 13|"13"
+ * @return bool
+ */
+ public function verifyVersion($versionHeader) {
+ return (1 === count($versionHeader) && static::VERSION === (int)$versionHeader[0]);
+ }
+
+ /**
+ * @todo Write logic for this method. See section 4.2.1.8
+ */
+ public function verifyProtocol($val) {
+ }
+
+ /**
+ * @todo Write logic for this method. See section 4.2.1.9
+ */
+ public function verifyExtensions($val) {
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Handshake/ResponseVerifier.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Handshake/ResponseVerifier.php
new file mode 100644
index 0000000..de03f53
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Handshake/ResponseVerifier.php
@@ -0,0 +1,52 @@
+verifyStatus($response->getStatusCode());
+ $passes += (int)$this->verifyUpgrade($response->getHeader('Upgrade'));
+ $passes += (int)$this->verifyConnection($response->getHeader('Connection'));
+ $passes += (int)$this->verifySecWebSocketAccept(
+ $response->getHeader('Sec-WebSocket-Accept')
+ , $request->getHeader('Sec-WebSocket-Key')
+ );
+ $passes += (int)$this->verifySubProtocol(
+ $request->getHeader('Sec-WebSocket-Protocol')
+ , $response->getHeader('Sec-WebSocket-Protocol')
+ );
+
+ return (5 === $passes);
+ }
+
+ public function verifyStatus($status) {
+ return ((int)$status === 101);
+ }
+
+ public function verifyUpgrade(array $upgrade) {
+ return (in_array('websocket', array_map('strtolower', $upgrade)));
+ }
+
+ public function verifyConnection(array $connection) {
+ return (in_array('upgrade', array_map('strtolower', $connection)));
+ }
+
+ public function verifySecWebSocketAccept($swa, $key) {
+ return (
+ 1 === count($swa) &&
+ 1 === count($key) &&
+ $swa[0] === $this->sign($key[0])
+ );
+ }
+
+ public function sign($key) {
+ return base64_encode(sha1($key . NegotiatorInterface::GUID, true));
+ }
+
+ public function verifySubProtocol(array $requestHeader, array $responseHeader) {
+ return 0 === count($responseHeader) || count(array_intersect($responseHeader, $requestHeader)) > 0;
+ }
+}
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Handshake/ServerNegotiator.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Handshake/ServerNegotiator.php
new file mode 100644
index 0000000..5a0073b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Handshake/ServerNegotiator.php
@@ -0,0 +1,136 @@
+verifier = $requestVerifier;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isProtocol(RequestInterface $request) {
+ return $this->verifier->verifyVersion($request->getHeader('Sec-WebSocket-Version'));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getVersionNumber() {
+ return RequestVerifier::VERSION;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function handshake(RequestInterface $request) {
+ if (true !== $this->verifier->verifyMethod($request->getMethod())) {
+ return new Response(405, ['Allow' => 'GET']);
+ }
+
+ if (true !== $this->verifier->verifyHTTPVersion($request->getProtocolVersion())) {
+ return new Response(505);
+ }
+
+ if (true !== $this->verifier->verifyRequestURI($request->getUri()->getPath())) {
+ return new Response(400);
+ }
+
+ if (true !== $this->verifier->verifyHost($request->getHeader('Host'))) {
+ return new Response(400);
+ }
+
+ $upgradeSuggestion = [
+ 'Connection' => 'Upgrade',
+ 'Upgrade' => 'websocket',
+ 'Sec-WebSocket-Version' => $this->getVersionNumber()
+ ];
+ if (count($this->_supportedSubProtocols) > 0) {
+ $upgradeSuggestion['Sec-WebSocket-Protocol'] = implode(', ', $this->_supportedSubProtocols);
+ }
+ if (true !== $this->verifier->verifyUpgradeRequest($request->getHeader('Upgrade'))) {
+ return new Response(426, $upgradeSuggestion, null, '1.1', 'Upgrade header MUST be provided');
+ }
+
+ if (true !== $this->verifier->verifyConnection($request->getHeader('Connection'))) {
+ return new Response(400, [], null, '1.1', 'Connection Upgrade MUST be requested');
+ }
+
+ if (true !== $this->verifier->verifyKey($request->getHeader('Sec-WebSocket-Key'))) {
+ return new Response(400, [], null, '1.1', 'Invalid Sec-WebSocket-Key');
+ }
+
+ if (true !== $this->verifier->verifyVersion($request->getHeader('Sec-WebSocket-Version'))) {
+ return new Response(426, $upgradeSuggestion);
+ }
+
+ $headers = [];
+ $subProtocols = $request->getHeader('Sec-WebSocket-Protocol');
+ if (count($subProtocols) > 0 || (count($this->_supportedSubProtocols) > 0 && $this->_strictSubProtocols)) {
+ $subProtocols = array_map('trim', explode(',', implode(',', $subProtocols)));
+
+ $match = array_reduce($subProtocols, function($accumulator, $protocol) {
+ return $accumulator ?: (isset($this->_supportedSubProtocols[$protocol]) ? $protocol : null);
+ }, null);
+
+ if ($this->_strictSubProtocols && null === $match) {
+ return new Response(426, $upgradeSuggestion, null, '1.1', 'No Sec-WebSocket-Protocols requested supported');
+ }
+
+ if (null !== $match) {
+ $headers['Sec-WebSocket-Protocol'] = $match;
+ }
+ }
+
+ return new Response(101, array_merge($headers, [
+ 'Upgrade' => 'websocket'
+ , 'Connection' => 'Upgrade'
+ , 'Sec-WebSocket-Accept' => $this->sign((string)$request->getHeader('Sec-WebSocket-Key')[0])
+ , 'X-Powered-By' => 'Ratchet'
+ ]));
+ }
+
+ /**
+ * Used when doing the handshake to encode the key, verifying client/server are speaking the same language
+ * @param string $key
+ * @return string
+ * @internal
+ */
+ public function sign($key) {
+ return base64_encode(sha1($key . static::GUID, true));
+ }
+
+ /**
+ * @param array $protocols
+ */
+ function setSupportedSubProtocols(array $protocols) {
+ $this->_supportedSubProtocols = array_flip($protocols);
+ }
+
+ /**
+ * If enabled and support for a subprotocol has been added handshake
+ * will not upgrade if a match between request and supported subprotocols
+ * @param boolean $enable
+ * @todo Consider extending this interface and moving this there.
+ * The spec does says the server can fail for this reason, but
+ * it is not a requirement. This is an implementation detail.
+ */
+ function setStrictSubProtocolCheck($enable) {
+ $this->_strictSubProtocols = (boolean)$enable;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Messaging/CloseFrameChecker.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Messaging/CloseFrameChecker.php
new file mode 100644
index 0000000..3d800e5
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Messaging/CloseFrameChecker.php
@@ -0,0 +1,24 @@
+validCloseCodes = [
+ Frame::CLOSE_NORMAL,
+ Frame::CLOSE_GOING_AWAY,
+ Frame::CLOSE_PROTOCOL,
+ Frame::CLOSE_BAD_DATA,
+ Frame::CLOSE_BAD_PAYLOAD,
+ Frame::CLOSE_POLICY,
+ Frame::CLOSE_TOO_BIG,
+ Frame::CLOSE_MAND_EXT,
+ Frame::CLOSE_SRV_ERR,
+ ];
+ }
+
+ public function __invoke($val) {
+ return ($val >= 3000 && $val <= 4999) || in_array($val, $this->validCloseCodes);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Messaging/DataInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Messaging/DataInterface.php
new file mode 100644
index 0000000..18aa2e3
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Messaging/DataInterface.php
@@ -0,0 +1,34 @@
+ $ufExceptionFactory
+ */
+ public function __construct($payload = null, $final = true, $opcode = 1, callable $ufExceptionFactory = null) {
+ $this->ufeg = $ufExceptionFactory ?: function($msg = '') {
+ return new \UnderflowException($msg);
+ };
+
+ if (null === $payload) {
+ return;
+ }
+
+ $this->defPayLen = strlen($payload);
+ $this->firstByte = ($final ? 128 : 0) + $opcode;
+ $this->secondByte = $this->defPayLen;
+ $this->isCoalesced = true;
+
+ $ext = '';
+ if ($this->defPayLen > 65535) {
+ $ext = pack('NN', 0, $this->defPayLen);
+ $this->secondByte = 127;
+ } elseif ($this->defPayLen > 125) {
+ $ext = pack('n', $this->defPayLen);
+ $this->secondByte = 126;
+ }
+
+ $this->data = chr($this->firstByte) . chr($this->secondByte) . $ext . $payload;
+ $this->bytesRecvd = 2 + strlen($ext) + $this->defPayLen;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isCoalesced() {
+ if (true === $this->isCoalesced) {
+ return true;
+ }
+
+ try {
+ $payload_length = $this->getPayloadLength();
+ $payload_start = $this->getPayloadStartingByte();
+ } catch (\UnderflowException $e) {
+ return false;
+ }
+
+ $this->isCoalesced = $this->bytesRecvd >= $payload_length + $payload_start;
+
+ return $this->isCoalesced;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addBuffer($buf) {
+ $len = strlen($buf);
+
+ $this->data .= $buf;
+ $this->bytesRecvd += $len;
+
+ if ($this->firstByte === -1 && $this->bytesRecvd !== 0) {
+ $this->firstByte = ord($this->data[0]);
+ }
+
+ if ($this->secondByte === -1 && $this->bytesRecvd >= 2) {
+ $this->secondByte = ord($this->data[1]);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isFinal() {
+ if (-1 === $this->firstByte) {
+ throw call_user_func($this->ufeg, 'Not enough bytes received to determine if this is the final frame in message');
+ }
+
+ return 128 === ($this->firstByte & 128);
+ }
+
+ /**
+ * @return boolean
+ * @throws \UnderflowException
+ */
+ public function getRsv1() {
+ if (-1 === $this->firstByte) {
+ throw call_user_func($this->ufeg, 'Not enough bytes received to determine reserved bit');
+ }
+
+ return 64 === ($this->firstByte & 64);
+ }
+
+ /**
+ * @return boolean
+ * @throws \UnderflowException
+ */
+ public function getRsv2() {
+ if (-1 === $this->firstByte) {
+ throw call_user_func($this->ufeg, 'Not enough bytes received to determine reserved bit');
+ }
+
+ return 32 === ($this->firstByte & 32);
+ }
+
+ /**
+ * @return boolean
+ * @throws \UnderflowException
+ */
+ public function getRsv3() {
+ if (-1 === $this->firstByte) {
+ throw call_user_func($this->ufeg, 'Not enough bytes received to determine reserved bit');
+ }
+
+ return 16 === ($this->firstByte & 16);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isMasked() {
+ if (-1 === $this->secondByte) {
+ throw call_user_func($this->ufeg, "Not enough bytes received ({$this->bytesRecvd}) to determine if mask is set");
+ }
+
+ return 128 === ($this->secondByte & 128);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getMaskingKey() {
+ if (!$this->isMasked()) {
+ return '';
+ }
+
+ $start = 1 + $this->getNumPayloadBytes();
+
+ if ($this->bytesRecvd < $start + static::MASK_LENGTH) {
+ throw call_user_func($this->ufeg, 'Not enough data buffered to calculate the masking key');
+ }
+
+ return substr($this->data, $start, static::MASK_LENGTH);
+ }
+
+ /**
+ * Create a 4 byte masking key
+ * @return string
+ */
+ public function generateMaskingKey() {
+ $mask = '';
+
+ for ($i = 1; $i <= static::MASK_LENGTH; $i++) {
+ $mask .= chr(rand(32, 126));
+ }
+
+ return $mask;
+ }
+
+ /**
+ * Apply a mask to the payload
+ * @param string|null If NULL is passed a masking key will be generated
+ * @throws \OutOfBoundsException
+ * @throws \InvalidArgumentException If there is an issue with the given masking key
+ * @return Frame
+ */
+ public function maskPayload($maskingKey = null) {
+ if (null === $maskingKey) {
+ $maskingKey = $this->generateMaskingKey();
+ }
+
+ if (static::MASK_LENGTH !== strlen($maskingKey)) {
+ throw new \InvalidArgumentException("Masking key must be " . static::MASK_LENGTH ." characters");
+ }
+
+ if (extension_loaded('mbstring') && true !== mb_check_encoding($maskingKey, 'US-ASCII')) {
+ throw new \OutOfBoundsException("Masking key MUST be ASCII");
+ }
+
+ $this->unMaskPayload();
+
+ $this->secondByte = $this->secondByte | 128;
+ $this->data[1] = chr($this->secondByte);
+
+ $this->data = substr_replace($this->data, $maskingKey, $this->getNumPayloadBytes() + 1, 0);
+
+ $this->bytesRecvd += static::MASK_LENGTH;
+ $this->data = substr_replace($this->data, $this->applyMask($maskingKey), $this->getPayloadStartingByte(), $this->getPayloadLength());
+
+ return $this;
+ }
+
+ /**
+ * Remove a mask from the payload
+ * @throws \UnderFlowException If the frame is not coalesced
+ * @return Frame
+ */
+ public function unMaskPayload() {
+ if (!$this->isCoalesced()) {
+ throw call_user_func($this->ufeg, 'Frame must be coalesced before applying mask');
+ }
+
+ if (!$this->isMasked()) {
+ return $this;
+ }
+
+ $maskingKey = $this->getMaskingKey();
+
+ $this->secondByte = $this->secondByte & ~128;
+ $this->data[1] = chr($this->secondByte);
+
+ $this->data = substr_replace($this->data, '', $this->getNumPayloadBytes() + 1, static::MASK_LENGTH);
+
+ $this->bytesRecvd -= static::MASK_LENGTH;
+ $this->data = substr_replace($this->data, $this->applyMask($maskingKey), $this->getPayloadStartingByte(), $this->getPayloadLength());
+
+ return $this;
+ }
+
+ /**
+ * Apply a mask to a string or the payload of the instance
+ * @param string $maskingKey The 4 character masking key to be applied
+ * @param string|null $payload A string to mask or null to use the payload
+ * @throws \UnderflowException If using the payload but enough hasn't been buffered
+ * @return string The masked string
+ */
+ public function applyMask($maskingKey, $payload = null) {
+ if (null === $payload) {
+ if (!$this->isCoalesced()) {
+ throw call_user_func($this->ufeg, 'Frame must be coalesced to apply a mask');
+ }
+
+ $payload = substr($this->data, $this->getPayloadStartingByte(), $this->getPayloadLength());
+ }
+
+ $len = strlen($payload);
+
+ if (0 === $len) {
+ return '';
+ }
+
+ return $payload ^ str_pad('', $len, $maskingKey, STR_PAD_RIGHT);
+
+ // TODO: Remove this before publish - keeping methods here to compare performance (above is faster but need control against v0.3.3)
+
+ $applied = '';
+ for ($i = 0, $len = strlen($payload); $i < $len; $i++) {
+ $applied .= $payload[$i] ^ $maskingKey[$i % static::MASK_LENGTH];
+ }
+
+ return $applied;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getOpcode() {
+ if (-1 === $this->firstByte) {
+ throw call_user_func($this->ufeg, 'Not enough bytes received to determine opcode');
+ }
+
+ return ($this->firstByte & ~240);
+ }
+
+ /**
+ * Gets the decimal value of bits 9 (10th) through 15 inclusive
+ * @return int
+ * @throws \UnderflowException If the buffer doesn't have enough data to determine this
+ */
+ protected function getFirstPayloadVal() {
+ if (-1 === $this->secondByte) {
+ throw call_user_func($this->ufeg, 'Not enough bytes received');
+ }
+
+ return $this->secondByte & 127;
+ }
+
+ /**
+ * @return int (7|23|71) Number of bits defined for the payload length in the fame
+ * @throws \UnderflowException
+ */
+ protected function getNumPayloadBits() {
+ if (-1 === $this->secondByte) {
+ throw call_user_func($this->ufeg, 'Not enough bytes received');
+ }
+
+ // By default 7 bits are used to describe the payload length
+ // These are bits 9 (10th) through 15 inclusive
+ $bits = 7;
+
+ // Get the value of those bits
+ $check = $this->getFirstPayloadVal();
+
+ // If the value is 126 the 7 bits plus the next 16 are used to describe the payload length
+ if ($check >= 126) {
+ $bits += 16;
+ }
+
+ // If the value of the initial payload length are is 127 an additional 48 bits are used to describe length
+ // Note: The documentation specifies the length is to be 63 bits, but I think that's a typo and is 64 (16+48)
+ if ($check === 127) {
+ $bits += 48;
+ }
+
+ return $bits;
+ }
+
+ /**
+ * This just returns the number of bytes used in the frame to describe the payload length (as opposed to # of bits)
+ * @see getNumPayloadBits
+ */
+ protected function getNumPayloadBytes() {
+ return (1 + $this->getNumPayloadBits()) / 8;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getPayloadLength() {
+ if ($this->defPayLen !== -1) {
+ return $this->defPayLen;
+ }
+
+ $this->defPayLen = $this->getFirstPayloadVal();
+ if ($this->defPayLen <= 125) {
+ return $this->getPayloadLength();
+ }
+
+ $byte_length = $this->getNumPayloadBytes();
+ if ($this->bytesRecvd < 1 + $byte_length) {
+ $this->defPayLen = -1;
+ throw call_user_func($this->ufeg, 'Not enough data buffered to determine payload length');
+ }
+
+ $len = 0;
+ for ($i = 2; $i <= $byte_length; $i++) {
+ $len <<= 8;
+ $len += ord($this->data[$i]);
+ }
+
+ $this->defPayLen = $len;
+
+ return $this->getPayloadLength();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getPayloadStartingByte() {
+ return 1 + $this->getNumPayloadBytes() + ($this->isMasked() ? static::MASK_LENGTH : 0);
+ }
+
+ /**
+ * {@inheritdoc}
+ * @todo Consider not checking mask, always returning the payload, masked or not
+ */
+ public function getPayload() {
+ if (!$this->isCoalesced()) {
+ throw call_user_func($this->ufeg, 'Can not return partial message');
+ }
+
+ return $this->__toString();
+ }
+
+ /**
+ * Get the raw contents of the frame
+ * @todo This is untested, make sure the substr is right - trying to return the frame w/o the overflow
+ */
+ public function getContents() {
+ return substr($this->data, 0, $this->getPayloadStartingByte() + $this->getPayloadLength());
+ }
+
+ public function __toString() {
+ $payload = (string)substr($this->data, $this->getPayloadStartingByte(), $this->getPayloadLength());
+
+ if ($this->isMasked()) {
+ $payload = $this->applyMask($this->getMaskingKey(), $payload);
+ }
+
+ return $payload;
+ }
+
+ /**
+ * Sometimes clients will concatenate more than one frame over the wire
+ * This method will take the extra bytes off the end and return them
+ * @return string
+ */
+ public function extractOverflow() {
+ if ($this->isCoalesced()) {
+ $endPoint = $this->getPayloadLength();
+ $endPoint += $this->getPayloadStartingByte();
+
+ if ($this->bytesRecvd > $endPoint) {
+ $overflow = substr($this->data, $endPoint);
+ $this->data = substr($this->data, 0, $endPoint);
+
+ return $overflow;
+ }
+ }
+
+ return '';
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Messaging/FrameInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Messaging/FrameInterface.php
new file mode 100644
index 0000000..dc24091
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Messaging/FrameInterface.php
@@ -0,0 +1,38 @@
+_frames = new \SplDoublyLinkedList;
+ }
+
+ public function getIterator() {
+ return $this->_frames;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function count() {
+ return count($this->_frames);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isCoalesced() {
+ if (count($this->_frames) == 0) {
+ return false;
+ }
+
+ $last = $this->_frames->top();
+
+ return ($last->isCoalesced() && $last->isFinal());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addFrame(FrameInterface $fragment) {
+ $this->_frames->push($fragment);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getOpcode() {
+ if (count($this->_frames) == 0) {
+ throw new \UnderflowException('No frames have been added to this message');
+ }
+
+ return $this->_frames->bottom()->getOpcode();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getPayloadLength() {
+ $len = 0;
+
+ foreach ($this->_frames as $frame) {
+ try {
+ $len += $frame->getPayloadLength();
+ } catch (\UnderflowException $e) {
+ // Not an error, want the current amount buffered
+ }
+ }
+
+ return $len;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getPayload() {
+ if (!$this->isCoalesced()) {
+ throw new \UnderflowException('Message has not been put back together yet');
+ }
+
+ return $this->__toString();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getContents() {
+ if (!$this->isCoalesced()) {
+ throw new \UnderflowException("Message has not been put back together yet");
+ }
+
+ $buffer = '';
+
+ foreach ($this->_frames as $frame) {
+ $buffer .= $frame->getContents();
+ }
+
+ return $buffer;
+ }
+
+ public function __toString() {
+ $buffer = '';
+
+ foreach ($this->_frames as $frame) {
+ $buffer .= $frame->getPayload();
+ }
+
+ return $buffer;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isBinary() {
+ if ($this->_frames->isEmpty()) {
+ throw new \UnderflowException('Not enough data has been received to determine if message is binary');
+ }
+
+ return Frame::OP_BINARY === $this->_frames->bottom()->getOpcode();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Messaging/MessageBuffer.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Messaging/MessageBuffer.php
new file mode 100644
index 0000000..07ff4f1
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Messaging/MessageBuffer.php
@@ -0,0 +1,231 @@
+closeFrameChecker = $frameChecker;
+ $this->checkForMask = (bool)$expectMask;
+
+ $this->exceptionFactory ?: $this->exceptionFactory = function($msg) {
+ return new \UnderflowException($msg);
+ };
+
+ $this->onMessage = $onMessage;
+ $this->onControl = $onControl ?: function() {};
+ }
+
+ public function onData($data) {
+ while (strlen($data) > 0) {
+ $data = $this->processData($data);
+ }
+ }
+
+ /**
+ * @param string $data
+ * @return null
+ */
+ private function processData($data) {
+ $this->messageBuffer ?: $this->messageBuffer = $this->newMessage();
+ $this->frameBuffer ?: $this->frameBuffer = $this->newFrame();
+
+ $this->frameBuffer->addBuffer($data);
+ if (!$this->frameBuffer->isCoalesced()) {
+ return '';
+ }
+
+ $onMessage = $this->onMessage;
+ $onControl = $this->onControl;
+
+ $this->frameBuffer = $this->frameCheck($this->frameBuffer);
+
+ $overflow = $this->frameBuffer->extractOverflow();
+ $this->frameBuffer->unMaskPayload();
+
+ $opcode = $this->frameBuffer->getOpcode();
+
+ if ($opcode > 2) {
+ $onControl($this->frameBuffer);
+
+ if (Frame::OP_CLOSE === $opcode) {
+ return '';
+ }
+ } else {
+ $this->messageBuffer->addFrame($this->frameBuffer);
+ }
+
+ $this->frameBuffer = null;
+
+ if ($this->messageBuffer->isCoalesced()) {
+ $msgCheck = $this->checkMessage($this->messageBuffer);
+ if (true !== $msgCheck) {
+ $onControl($this->newCloseFrame($msgCheck, 'Ratchet detected an invalid UTF-8 payload'));
+ } else {
+ $onMessage($this->messageBuffer);
+ }
+
+ $this->messageBuffer = null;
+ }
+
+ return $overflow;
+ }
+
+ /**
+ * Check a frame to be added to the current message buffer
+ * @param \Ratchet\RFC6455\Messaging\FrameInterface|FrameInterface $frame
+ * @return \Ratchet\RFC6455\Messaging\FrameInterface|FrameInterface
+ */
+ public function frameCheck(FrameInterface $frame) {
+ if (false !== $frame->getRsv1() ||
+ false !== $frame->getRsv2() ||
+ false !== $frame->getRsv3()
+ ) {
+ return $this->newCloseFrame(Frame::CLOSE_PROTOCOL, 'Ratchet detected an invalid reserve code');
+ }
+
+ if ($this->checkForMask && !$frame->isMasked()) {
+ return $this->newCloseFrame(Frame::CLOSE_PROTOCOL, 'Ratchet detected an incorrect frame mask');
+ }
+
+ $opcode = $frame->getOpcode();
+
+ if ($opcode > 2) {
+ if ($frame->getPayloadLength() > 125 || !$frame->isFinal()) {
+ return $this->newCloseFrame(Frame::CLOSE_PROTOCOL, 'Ratchet detected a mismatch between final bit and indicated payload length');
+ }
+
+ switch ($opcode) {
+ case Frame::OP_CLOSE:
+ $closeCode = 0;
+
+ $bin = $frame->getPayload();
+
+ if (empty($bin)) {
+ return $this->newCloseFrame(Frame::CLOSE_NORMAL);
+ }
+
+ if (strlen($bin) === 1) {
+ return $this->newCloseFrame(Frame::CLOSE_PROTOCOL, 'Ratchet detected an invalid close code');
+ }
+
+ if (strlen($bin) >= 2) {
+ list($closeCode) = array_merge(unpack('n*', substr($bin, 0, 2)));
+ }
+
+ $checker = $this->closeFrameChecker;
+ if (!$checker($closeCode)) {
+ return $this->newCloseFrame(Frame::CLOSE_PROTOCOL, 'Ratchet detected an invalid close code');
+ }
+
+ if (!$this->checkUtf8(substr($bin, 2))) {
+ return $this->newCloseFrame(Frame::CLOSE_BAD_PAYLOAD, 'Ratchet detected an invalid UTF-8 payload in the close reason');
+ }
+
+ return $frame;
+ break;
+ case Frame::OP_PING:
+ case Frame::OP_PONG:
+ break;
+ default:
+ return $this->newCloseFrame(Frame::CLOSE_PROTOCOL, 'Ratchet detected an invalid OP code');
+ break;
+ }
+
+ return $frame;
+ }
+
+ if (Frame::OP_CONTINUE === $frame->getOpcode() && 0 === count($this->messageBuffer)) {
+ return $this->newCloseFrame(Frame::CLOSE_PROTOCOL, 'Ratchet detected the first frame of a message was a continue');
+ }
+
+ if (count($this->messageBuffer) > 0 && Frame::OP_CONTINUE !== $frame->getOpcode()) {
+ return $this->newCloseFrame(Frame::CLOSE_PROTOCOL, 'Ratchet detected invalid OP code when expecting continue frame');
+ }
+
+ return $frame;
+ }
+
+ /**
+ * Determine if a message is valid
+ * @param \Ratchet\RFC6455\Messaging\MessageInterface
+ * @return bool|int true if valid - false if incomplete - int of recommended close code
+ */
+ public function checkMessage(MessageInterface $message) {
+ if (!$message->isBinary()) {
+ if (!$this->checkUtf8($message->getPayload())) {
+ return Frame::CLOSE_BAD_PAYLOAD;
+ }
+ }
+
+ return true;
+ }
+
+ private function checkUtf8($string) {
+ if (extension_loaded('mbstring')) {
+ return mb_check_encoding($string, 'UTF-8');
+ }
+
+ return preg_match('//u', $string);
+ }
+
+ /**
+ * @return \Ratchet\RFC6455\Messaging\MessageInterface
+ */
+ public function newMessage() {
+ return new Message;
+ }
+
+ /**
+ * @param string|null $payload
+ * @param bool|null $final
+ * @param int|null $opcode
+ * @return \Ratchet\RFC6455\Messaging\FrameInterface
+ */
+ public function newFrame($payload = null, $final = null, $opcode = null) {
+ return new Frame($payload, $final, $opcode, $this->exceptionFactory);
+ }
+
+ public function newCloseFrame($code, $reason = '') {
+ return $this->newFrame(pack('n', $code) . $reason, true, Frame::OP_CLOSE);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Messaging/MessageInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Messaging/MessageInterface.php
new file mode 100644
index 0000000..fd7212e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/src/Messaging/MessageInterface.php
@@ -0,0 +1,20 @@
+markTestSkipped('Autobahn TestSuite results not found');
+ }
+
+ $resultsJson = file_get_contents($fileName);
+ $results = json_decode($resultsJson);
+ $agentName = array_keys(get_object_vars($results))[0];
+
+ foreach ($results->$agentName as $name => $result) {
+ if ($result->behavior === "INFORMATIONAL") {
+ continue;
+ }
+
+ $this->assertTrue(in_array($result->behavior, ["OK", "NON-STRICT"]), "Autobahn test case " . $name . " in " . $fileName);
+ }
+ }
+
+ public function testAutobahnClientResults() {
+ $this->verifyAutobahnResults(__DIR__ . '/ab/reports/clients/index.json');
+ }
+
+ public function testAutobahnServerResults() {
+ $this->verifyAutobahnResults(__DIR__ . '/ab/reports/servers/index.json');
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/ab/clientRunner.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/ab/clientRunner.php
new file mode 100644
index 0000000..0c5578a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/ab/clientRunner.php
@@ -0,0 +1,228 @@
+createCached('8.8.8.8', $loop);
+
+$factory = new \React\SocketClient\Connector($loop, $dnsResolver);
+
+function echoStreamerFactory($conn)
+{
+ return new \Ratchet\RFC6455\Messaging\MessageBuffer(
+ new \Ratchet\RFC6455\Messaging\CloseFrameChecker,
+ function (\Ratchet\RFC6455\Messaging\MessageInterface $msg) use ($conn) {
+ /** @var Frame $frame */
+ foreach ($msg as $frame) {
+ $frame->maskPayload();
+ }
+ $conn->write($msg->getContents());
+ },
+ function (\Ratchet\RFC6455\Messaging\FrameInterface $frame) use ($conn) {
+ switch ($frame->getOpcode()) {
+ case Frame::OP_PING:
+ return $conn->write((new Frame($frame->getPayload(), true, Frame::OP_PONG))->maskPayload()->getContents());
+ break;
+ case Frame::OP_CLOSE:
+ return $conn->end((new Frame($frame->getPayload(), true, Frame::OP_CLOSE))->maskPayload()->getContents());
+ break;
+ }
+ },
+ false
+ );
+}
+
+function getTestCases() {
+ global $factory;
+ global $testServer;
+
+ $deferred = new Deferred();
+
+ $factory->create($testServer, 9001)->then(function (\React\Stream\Stream $stream) use ($deferred) {
+ $cn = new \Ratchet\RFC6455\Handshake\ClientNegotiator();
+ $cnRequest = $cn->generateRequest(new Uri('ws://127.0.0.1:9001/getCaseCount'));
+
+ $rawResponse = "";
+ $response = null;
+
+ /** @var \Ratchet\RFC6455\Messaging\Streaming\MessageBuffer $ms */
+ $ms = null;
+
+ $stream->on('data', function ($data) use ($stream, &$rawResponse, &$response, &$ms, $cn, $deferred, &$context, $cnRequest) {
+ if ($response === null) {
+ $rawResponse .= $data;
+ $pos = strpos($rawResponse, "\r\n\r\n");
+ if ($pos) {
+ $data = substr($rawResponse, $pos + 4);
+ $rawResponse = substr($rawResponse, 0, $pos + 4);
+ $response = \GuzzleHttp\Psr7\parse_response($rawResponse);
+
+ if (!$cn->validateResponse($cnRequest, $response)) {
+ $stream->end();
+ $deferred->reject();
+ } else {
+ $ms = new \Ratchet\RFC6455\Messaging\MessageBuffer(
+ new \Ratchet\RFC6455\Messaging\CloseFrameChecker,
+ function (\Ratchet\RFC6455\Messaging\MessageInterface $msg) use ($deferred, $stream) {
+ $deferred->resolve($msg->getPayload());
+ $stream->close();
+ },
+ null,
+ false
+ );
+ }
+ }
+ }
+
+ // feed the message streamer
+ if ($ms) {
+ $ms->onData($data);
+ }
+ });
+
+ $stream->write(\GuzzleHttp\Psr7\str($cnRequest));
+ });
+
+ return $deferred->promise();
+}
+
+function runTest($case)
+{
+ global $factory;
+ global $testServer;
+
+ $casePath = "/runCase?case={$case}&agent=" . AGENT;
+
+ $deferred = new Deferred();
+
+ $factory->create($testServer, 9001)->then(function (\React\Stream\Stream $stream) use ($deferred, $casePath, $case) {
+ $cn = new \Ratchet\RFC6455\Handshake\ClientNegotiator();
+ $cnRequest = $cn->generateRequest(new Uri('ws://127.0.0.1:9001' . $casePath));
+
+ $rawResponse = "";
+ $response = null;
+
+ $ms = null;
+
+ $stream->on('data', function ($data) use ($stream, &$rawResponse, &$response, &$ms, $cn, $deferred, &$context, $cnRequest) {
+ if ($response === null) {
+ $rawResponse .= $data;
+ $pos = strpos($rawResponse, "\r\n\r\n");
+ if ($pos) {
+ $data = substr($rawResponse, $pos + 4);
+ $rawResponse = substr($rawResponse, 0, $pos + 4);
+ $response = \GuzzleHttp\Psr7\parse_response($rawResponse);
+
+ if (!$cn->validateResponse($cnRequest, $response)) {
+ $stream->end();
+ $deferred->reject();
+ } else {
+ $ms = echoStreamerFactory($stream);
+ }
+ }
+ }
+
+ // feed the message streamer
+ if ($ms) {
+ $ms->onData($data);
+ }
+ });
+
+ $stream->on('close', function () use ($deferred) {
+ $deferred->resolve();
+ });
+
+ $stream->write(\GuzzleHttp\Psr7\str($cnRequest));
+ });
+
+ return $deferred->promise();
+}
+
+function createReport() {
+ global $factory;
+ global $testServer;
+
+ $deferred = new Deferred();
+
+ $factory->create($testServer, 9001)->then(function (\React\Stream\Stream $stream) use ($deferred) {
+ $reportPath = "/updateReports?agent=" . AGENT . "&shutdownOnComplete=true";
+ $cn = new \Ratchet\RFC6455\Handshake\ClientNegotiator();
+ $cnRequest = $cn->generateRequest(new Uri('ws://127.0.0.1:9001' . $reportPath));
+
+ $rawResponse = "";
+ $response = null;
+
+ /** @var \Ratchet\RFC6455\Messaging\MessageBuffer $ms */
+ $ms = null;
+
+ $stream->on('data', function ($data) use ($stream, &$rawResponse, &$response, &$ms, $cn, $deferred, &$context, $cnRequest) {
+ if ($response === null) {
+ $rawResponse .= $data;
+ $pos = strpos($rawResponse, "\r\n\r\n");
+ if ($pos) {
+ $data = substr($rawResponse, $pos + 4);
+ $rawResponse = substr($rawResponse, 0, $pos + 4);
+ $response = \GuzzleHttp\Psr7\parse_response($rawResponse);
+
+ if (!$cn->validateResponse($cnRequest, $response)) {
+ $stream->end();
+ $deferred->reject();
+ } else {
+ $ms = new \Ratchet\RFC6455\Messaging\MessageBuffer(
+ new \Ratchet\RFC6455\Messaging\CloseFrameChecker,
+ function (\Ratchet\RFC6455\Messaging\MessageInterface $msg) use ($deferred, $stream) {
+ $deferred->resolve($msg->getPayload());
+ $stream->close();
+ },
+ null,
+ false
+ );
+ }
+ }
+ }
+
+ // feed the message streamer
+ if ($ms) {
+ $ms->onData($data);
+ }
+ });
+
+ $stream->write(\GuzzleHttp\Psr7\str($cnRequest));
+ });
+
+ return $deferred->promise();
+}
+
+
+$testPromises = [];
+
+getTestCases()->then(function ($count) use ($loop) {
+ $allDeferred = new Deferred();
+
+ $runNextCase = function () use (&$i, &$runNextCase, $count, $allDeferred) {
+ $i++;
+ if ($i > $count) {
+ $allDeferred->resolve();
+ return;
+ }
+ runTest($i)->then($runNextCase);
+ };
+
+ $i = 0;
+ $runNextCase();
+
+ $allDeferred->promise()->then(function () {
+ createReport();
+ });
+});
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/ab/fuzzingclient.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/ab/fuzzingclient.json
new file mode 100644
index 0000000..d2fd0d0
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/ab/fuzzingclient.json
@@ -0,0 +1,14 @@
+{
+ "options": {
+ "failByDrop": false
+ }
+ , "outdir": "./reports/servers"
+ , "servers": [{
+ "agent": "RatchetRFC/0.1.0"
+ , "url": "ws://localhost:9001"
+ , "options": {"version": 18}
+ }]
+ , "cases": ["*"]
+ , "exclude-cases": ["6.4.*", "12.*","13.*"]
+ , "exclude-agent-cases": {}
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/ab/fuzzingserver.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/ab/fuzzingserver.json
new file mode 100644
index 0000000..0422560
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/ab/fuzzingserver.json
@@ -0,0 +1,10 @@
+{
+ "url": "ws://127.0.0.1:9001"
+ , "options": {
+ "failByDrop": false
+ }
+ , "outdir": "./reports/clients"
+ , "cases": ["*"]
+ , "exclude-cases": ["6.4.*", "12.*", "13.*"]
+ , "exclude-agent-cases": {}
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/ab/run_ab_tests.sh b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/ab/run_ab_tests.sh
new file mode 100644
index 0000000..8fa9ced
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/ab/run_ab_tests.sh
@@ -0,0 +1,11 @@
+cd tests/ab
+
+wstest -m fuzzingserver -s fuzzingserver.json &
+sleep 5
+php clientRunner.php
+
+sleep 2
+
+php startServer.php &
+sleep 3
+wstest -m fuzzingclient -s fuzzingclient.json
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/ab/startServer.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/ab/startServer.php
new file mode 100644
index 0000000..b256ec2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/ab/startServer.php
@@ -0,0 +1,55 @@
+on('request', function (\React\Http\Request $request, \React\Http\Response $response) use ($negotiator, $closeFrameChecker, $uException) {
+ $psrRequest = new \GuzzleHttp\Psr7\Request($request->getMethod(), $request->getPath(), $request->getHeaders());
+
+ $negotiatorResponse = $negotiator->handshake($psrRequest);
+
+ $response->writeHead(
+ $negotiatorResponse->getStatusCode(),
+ array_merge(
+ $negotiatorResponse->getHeaders(),
+ ["Content-Length" => "0"]
+ )
+ );
+
+ if ($negotiatorResponse->getStatusCode() !== 101) {
+ $response->end();
+ return;
+ }
+
+ $parser = new \Ratchet\RFC6455\Messaging\MessageBuffer($closeFrameChecker, function(MessageInterface $message) use ($response) {
+ $response->write($message->getContents());
+ }, function(FrameInterface $frame) use ($response, &$parser) {
+ switch ($frame->getOpCode()) {
+ case Frame::OP_CLOSE:
+ $response->end($frame->getContents());
+ break;
+ case Frame::OP_PING:
+ $response->write($parser->newFrame($frame->getPayload(), true, Frame::OP_PONG)->getContents());
+ break;
+ }
+ }, true, function() use ($uException) {
+ return $uException;
+ });
+
+ $request->on('data', [$parser, 'onData']);
+});
+
+$socket->listen(9001, '0.0.0.0');
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/bootstrap.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/bootstrap.php
new file mode 100644
index 0000000..511b041
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/bootstrap.php
@@ -0,0 +1,19 @@
+addPsr4('Ratchet\\RFC6455\\Test\\', __DIR__);
+ break;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/unit/Handshake/RequestVerifierTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/unit/Handshake/RequestVerifierTest.php
new file mode 100644
index 0000000..239de33
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/unit/Handshake/RequestVerifierTest.php
@@ -0,0 +1,177 @@
+_v = new RequestVerifier();
+ }
+
+ public static function methodProvider() {
+ return array(
+ array(true, 'GET'),
+ array(true, 'get'),
+ array(true, 'Get'),
+ array(false, 'POST'),
+ array(false, 'DELETE'),
+ array(false, 'PUT'),
+ array(false, 'PATCH')
+ );
+ }
+ /**
+ * @dataProvider methodProvider
+ */
+ public function testMethodMustBeGet($result, $in) {
+ $this->assertEquals($result, $this->_v->verifyMethod($in));
+ }
+
+ public static function httpVersionProvider() {
+ return array(
+ array(true, 1.1),
+ array(true, '1.1'),
+ array(true, 1.2),
+ array(true, '1.2'),
+ array(true, 2),
+ array(true, '2'),
+ array(true, '2.0'),
+ array(false, '1.0'),
+ array(false, 1),
+ array(false, '0.9'),
+ array(false, ''),
+ array(false, 'hello')
+ );
+ }
+
+ /**
+ * @dataProvider httpVersionProvider
+ */
+ public function testHttpVersionIsAtLeast1Point1($expected, $in) {
+ $this->assertEquals($expected, $this->_v->verifyHTTPVersion($in));
+ }
+
+ public static function uRIProvider() {
+ return array(
+ array(true, '/chat'),
+ array(true, '/hello/world?key=val'),
+ array(false, '/chat#bad'),
+ array(false, 'nope'),
+ array(false, '/ ಠ_ಠ '),
+ array(false, '/✖')
+ );
+ }
+
+ /**
+ * @dataProvider URIProvider
+ */
+ public function testRequestUri($expected, $in) {
+ $this->assertEquals($expected, $this->_v->verifyRequestURI($in));
+ }
+
+ public static function hostProvider() {
+ return array(
+ array(true, ['server.example.com']),
+ array(false, [])
+ );
+ }
+
+ /**
+ * @dataProvider HostProvider
+ */
+ public function testVerifyHostIsSet($expected, $in) {
+ $this->assertEquals($expected, $this->_v->verifyHost($in));
+ }
+
+ public static function upgradeProvider() {
+ return array(
+ array(true, ['websocket']),
+ array(true, ['Websocket']),
+ array(true, ['webSocket']),
+ array(false, []),
+ array(false, [''])
+ );
+ }
+
+ /**
+ * @dataProvider upgradeProvider
+ */
+ public function testVerifyUpgradeIsWebSocket($expected, $val) {
+ $this->assertEquals($expected, $this->_v->verifyUpgradeRequest($val));
+ }
+
+ public static function connectionProvider() {
+ return array(
+ array(true, ['Upgrade']),
+ array(true, ['upgrade']),
+ array(true, ['keep-alive', 'Upgrade']),
+ array(true, ['Upgrade', 'keep-alive']),
+ array(true, ['keep-alive', 'Upgrade', 'something']),
+ // as seen in Firefox 47.0.1 - see https://github.com/ratchetphp/RFC6455/issues/14
+ array(true, ['keep-alive, Upgrade']),
+ array(true, ['Upgrade, keep-alive']),
+ array(true, ['keep-alive, Upgrade, something']),
+ array(true, ['keep-alive, Upgrade', 'something']),
+ array(false, ['']),
+ array(false, [])
+ );
+ }
+
+ /**
+ * @dataProvider connectionProvider
+ */
+ public function testConnectionHeaderVerification($expected, $val) {
+ $this->assertEquals($expected, $this->_v->verifyConnection($val));
+ }
+
+ public static function keyProvider() {
+ return array(
+ array(true, ['hkfa1L7uwN6DCo4IS3iWAw==']),
+ array(true, ['765vVoQpKSGJwPzJIMM2GA==']),
+ array(true, ['AQIDBAUGBwgJCgsMDQ4PEC==']),
+ array(true, ['axa2B/Yz2CdpfQAY2Q5P7w==']),
+ array(false, [0]),
+ array(false, ['Hello World']),
+ array(false, ['1234567890123456']),
+ array(false, ['123456789012345678901234']),
+ array(true, [base64_encode('UTF8allthngs+✓')]),
+ array(true, ['dGhlIHNhbXBsZSBub25jZQ==']),
+ array(false, []),
+ array(false, ['dGhlIHNhbXBsZSBub25jZQ==', 'Some other value']),
+ array(false, ['Some other value', 'dGhlIHNhbXBsZSBub25jZQ=='])
+ );
+ }
+
+ /**
+ * @dataProvider keyProvider
+ */
+ public function testKeyIsBase64Encoded16BitNonce($expected, $val) {
+ $this->assertEquals($expected, $this->_v->verifyKey($val));
+ }
+
+ public static function versionProvider() {
+ return array(
+ array(true, [13]),
+ array(true, ['13']),
+ array(false, [12]),
+ array(false, [14]),
+ array(false, ['14']),
+ array(false, ['hi']),
+ array(false, ['']),
+ array(false, [])
+ );
+ }
+
+ /**
+ * @dataProvider versionProvider
+ */
+ public function testVersionEquals13($expected, $in) {
+ $this->assertEquals($expected, $this->_v->verifyVersion($in));
+ }
+}
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/unit/Handshake/ResponseVerifierTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/unit/Handshake/ResponseVerifierTest.php
new file mode 100644
index 0000000..312930e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/unit/Handshake/ResponseVerifierTest.php
@@ -0,0 +1,34 @@
+_v = new ResponseVerifier;
+ }
+
+ public static function subProtocolsProvider() {
+ return [
+ [true, ['a'], ['a']]
+ , [true, ['b', 'a'], ['c', 'd', 'a']]
+ , [false, ['a', 'b', 'c'], ['d']]
+ , [true, [], []]
+ , [true, ['a', 'b'], []]
+ ];
+ }
+
+ /**
+ * @dataProvider subProtocolsProvider
+ */
+ public function testVerifySubProtocol($expected, $response, $request) {
+ $this->assertEquals($expected, $this->_v->verifySubProtocol($response, $request));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/unit/Handshake/ServerNegotiatorTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/unit/Handshake/ServerNegotiatorTest.php
new file mode 100644
index 0000000..9c9aa8d
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/unit/Handshake/ServerNegotiatorTest.php
@@ -0,0 +1,175 @@
+handshake($request);
+
+ $this->assertEquals('1.1', $response->getProtocolVersion());
+ $this->assertEquals(426, $response->getStatusCode());
+ $this->assertEquals('Upgrade header MUST be provided', $response->getReasonPhrase());
+ $this->assertEquals('Upgrade', $response->getHeaderLine('Connection'));
+ $this->assertEquals('websocket', $response->getHeaderLine('Upgrade'));
+ $this->assertEquals('13', $response->getHeaderLine('Sec-WebSocket-Version'));
+ }
+
+ public function testNoConnectionUpgradeRequested() {
+ $negotiator = new ServerNegotiator(new RequestVerifier());
+
+ $requestText = 'GET / HTTP/1.1
+Host: 127.0.0.1:6789
+Connection: keep-alive
+Pragma: no-cache
+Cache-Control: no-cache
+Upgrade: websocket
+Upgrade-Insecure-Requests: 1
+User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
+Accept-Encoding: gzip, deflate, sdch, br
+Accept-Language: en-US,en;q=0.8';
+
+ $request = \GuzzleHttp\Psr7\parse_request($requestText);
+
+ $response = $negotiator->handshake($request);
+
+ $this->assertEquals('1.1', $response->getProtocolVersion());
+ $this->assertEquals(400, $response->getStatusCode());
+ $this->assertEquals('Connection Upgrade MUST be requested', $response->getReasonPhrase());
+ }
+
+ public function testInvalidSecWebsocketKey() {
+ $negotiator = new ServerNegotiator(new RequestVerifier());
+
+ $requestText = 'GET / HTTP/1.1
+Host: 127.0.0.1:6789
+Connection: Upgrade
+Pragma: no-cache
+Cache-Control: no-cache
+Upgrade: websocket
+Sec-WebSocket-Key: 12345
+Upgrade-Insecure-Requests: 1
+User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
+Accept-Encoding: gzip, deflate, sdch, br
+Accept-Language: en-US,en;q=0.8';
+
+ $request = \GuzzleHttp\Psr7\parse_request($requestText);
+
+ $response = $negotiator->handshake($request);
+
+ $this->assertEquals('1.1', $response->getProtocolVersion());
+ $this->assertEquals(400, $response->getStatusCode());
+ $this->assertEquals('Invalid Sec-WebSocket-Key', $response->getReasonPhrase());
+ }
+
+ public function testInvalidSecWebsocketVersion() {
+ $negotiator = new ServerNegotiator(new RequestVerifier());
+
+ $requestText = 'GET / HTTP/1.1
+Host: 127.0.0.1:6789
+Connection: Upgrade
+Pragma: no-cache
+Cache-Control: no-cache
+Upgrade: websocket
+Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+Upgrade-Insecure-Requests: 1
+User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
+Accept-Encoding: gzip, deflate, sdch, br
+Accept-Language: en-US,en;q=0.8';
+
+ $request = \GuzzleHttp\Psr7\parse_request($requestText);
+
+ $response = $negotiator->handshake($request);
+
+ $this->assertEquals('1.1', $response->getProtocolVersion());
+ $this->assertEquals(426, $response->getStatusCode());
+ $this->assertEquals('Upgrade Required', $response->getReasonPhrase());
+ $this->assertEquals('Upgrade', $response->getHeaderLine('Connection'));
+ $this->assertEquals('websocket', $response->getHeaderLine('Upgrade'));
+ $this->assertEquals('13', $response->getHeaderLine('Sec-WebSocket-Version'));
+ }
+
+ public function testBadSubprotocolResponse() {
+ $negotiator = new ServerNegotiator(new RequestVerifier());
+ $negotiator->setStrictSubProtocolCheck(true);
+ $negotiator->setSupportedSubProtocols([]);
+
+ $requestText = 'GET / HTTP/1.1
+Host: 127.0.0.1:6789
+Connection: Upgrade
+Pragma: no-cache
+Cache-Control: no-cache
+Upgrade: websocket
+Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+Sec-WebSocket-Version: 13
+Sec-WebSocket-Protocol: someprotocol
+Upgrade-Insecure-Requests: 1
+User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
+Accept-Encoding: gzip, deflate, sdch, br
+Accept-Language: en-US,en;q=0.8';
+
+ $request = \GuzzleHttp\Psr7\parse_request($requestText);
+
+ $response = $negotiator->handshake($request);
+
+ $this->assertEquals('1.1', $response->getProtocolVersion());
+ $this->assertEquals(426, $response->getStatusCode());
+ $this->assertEquals('No Sec-WebSocket-Protocols requested supported', $response->getReasonPhrase());
+ $this->assertEquals('Upgrade', $response->getHeaderLine('Connection'));
+ $this->assertEquals('websocket', $response->getHeaderLine('Upgrade'));
+ $this->assertEquals('13', $response->getHeaderLine('Sec-WebSocket-Version'));
+ }
+
+ public function testNonStrictSubprotocolDoesNotIncludeHeaderWhenNoneAgreedOn() {
+ $negotiator = new ServerNegotiator(new RequestVerifier());
+ $negotiator->setStrictSubProtocolCheck(false);
+ $negotiator->setSupportedSubProtocols(['someproto']);
+
+ $requestText = 'GET / HTTP/1.1
+Host: 127.0.0.1:6789
+Connection: Upgrade
+Pragma: no-cache
+Cache-Control: no-cache
+Upgrade: websocket
+Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+Sec-WebSocket-Version: 13
+Sec-WebSocket-Protocol: someotherproto
+Upgrade-Insecure-Requests: 1
+User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
+Accept-Encoding: gzip, deflate, sdch, br
+Accept-Language: en-US,en;q=0.8';
+
+ $request = \GuzzleHttp\Psr7\parse_request($requestText);
+
+ $response = $negotiator->handshake($request);
+
+ $this->assertEquals('1.1', $response->getProtocolVersion());
+ $this->assertEquals(101, $response->getStatusCode());
+ $this->assertEquals('Upgrade', $response->getHeaderLine('Connection'));
+ $this->assertEquals('websocket', $response->getHeaderLine('Upgrade'));
+ $this->assertFalse($response->hasHeader('Sec-WebSocket-Protocol'));
+ }
+}
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/unit/Messaging/FrameTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/unit/Messaging/FrameTest.php
new file mode 100644
index 0000000..b73f600
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/unit/Messaging/FrameTest.php
@@ -0,0 +1,501 @@
+_frame = new Frame;
+ }
+
+ /**
+ * Encode the fake binary string to send over the wire
+ * @param string of 1's and 0's
+ * @return string
+ */
+ public static function encode($in) {
+ if (strlen($in) > 8) {
+ $out = '';
+ while (strlen($in) >= 8) {
+ $out .= static::encode(substr($in, 0, 8));
+ $in = substr($in, 8);
+ }
+ return $out;
+ }
+ return chr(bindec($in));
+ }
+
+ /**
+ * This is a data provider
+ * param string The UTF8 message
+ * param string The WebSocket framed message, then base64_encoded
+ */
+ public static function UnframeMessageProvider() {
+ return array(
+ array('Hello World!', 'gYydAIfa1WXrtvIg0LXvbOP7'),
+ array('!@#$%^&*()-=_+[]{}\|/.,<>`~', 'gZv+h96r38f9j9vZ+IHWrvOWoayF9oX6gtfRqfKXwOeg'),
+ array('ಠ_ಠ', 'gYfnSpu5B/g75gf4Ow=='),
+ array(
+ "The quick brown fox jumps over the lazy dog. All work and no play makes Chris a dull boy. I'm trying to get past 128 characters for a unit test here...",
+ 'gf4Amahb14P8M7Kj2S6+4MN7tfHHLLmjzjSvo8IuuvPbe7j1zSn398A+9+/JIa6jzDSwrYh7lu/Ee6Ds2jD34sY/9+3He6fvySL37skwsvCIGL/xwSj34og/ou/Ee7Xs0XX3o+F8uqPcKa7qxjz398d7sObce6fi2y/3sppj9+DAOqXiyy+y8dt7sezae7aj3TW+94gvsvDce7/m2j75rYY='
+ )
+ );
+ }
+
+ public static function underflowProvider() {
+ return array(
+ array('isFinal', ''),
+ array('getRsv1', ''),
+ array('getRsv2', ''),
+ array('getRsv3', ''),
+ array('getOpcode', ''),
+ array('isMasked', '10000001'),
+ array('getPayloadLength', '10000001'),
+ array('getPayloadLength', '1000000111111110'),
+ array('getMaskingKey', '1000000110000111'),
+ array('getPayload', '100000011000000100011100101010101001100111110100')
+ );
+ }
+
+ /**
+ * @dataProvider underflowProvider
+ *
+ * @covers Ratchet\RFC6455\Messaging\Frame::isFinal
+ * @covers Ratchet\RFC6455\Messaging\Frame::getRsv1
+ * @covers Ratchet\RFC6455\Messaging\Frame::getRsv2
+ * @covers Ratchet\RFC6455\Messaging\Frame::getRsv3
+ * @covers Ratchet\RFC6455\Messaging\Frame::getOpcode
+ * @covers Ratchet\RFC6455\Messaging\Frame::isMasked
+ * @covers Ratchet\RFC6455\Messaging\Frame::getPayloadLength
+ * @covers Ratchet\RFC6455\Messaging\Frame::getMaskingKey
+ * @covers Ratchet\RFC6455\Messaging\Frame::getPayload
+ */
+ public function testUnderflowExceptionFromAllTheMethodsMimickingBuffering($method, $bin) {
+ $this->setExpectedException('\UnderflowException');
+ if (!empty($bin)) {
+ $this->_frame->addBuffer(static::encode($bin));
+ }
+ call_user_func(array($this->_frame, $method));
+ }
+
+ /**
+ * A data provider for testing the first byte of a WebSocket frame
+ * param bool Given, is the byte indicate this is the final frame
+ * param int Given, what is the expected opcode
+ * param string of 0|1 Each character represents a bit in the byte
+ */
+ public static function firstByteProvider() {
+ return array(
+ array(false, false, false, true, 8, '00011000'),
+ array(true, false, true, false, 10, '10101010'),
+ array(false, false, false, false, 15, '00001111'),
+ array(true, false, false, false, 1, '10000001'),
+ array(true, true, true, true, 15, '11111111'),
+ array(true, true, false, false, 7, '11000111')
+ );
+ }
+
+ /**
+ * @dataProvider firstByteProvider
+ * covers Ratchet\RFC6455\Messaging\Frame::isFinal
+ */
+ public function testFinCodeFromBits($fin, $rsv1, $rsv2, $rsv3, $opcode, $bin) {
+ $this->_frame->addBuffer(static::encode($bin));
+ $this->assertEquals($fin, $this->_frame->isFinal());
+ }
+
+ /**
+ * @dataProvider firstByteProvider
+ * covers Ratchet\RFC6455\Messaging\Frame::getRsv1
+ * covers Ratchet\RFC6455\Messaging\Frame::getRsv2
+ * covers Ratchet\RFC6455\Messaging\Frame::getRsv3
+ */
+ public function testGetRsvFromBits($fin, $rsv1, $rsv2, $rsv3, $opcode, $bin) {
+ $this->_frame->addBuffer(static::encode($bin));
+ $this->assertEquals($rsv1, $this->_frame->getRsv1());
+ $this->assertEquals($rsv2, $this->_frame->getRsv2());
+ $this->assertEquals($rsv3, $this->_frame->getRsv3());
+ }
+
+ /**
+ * @dataProvider firstByteProvider
+ * covers Ratchet\RFC6455\Messaging\Frame::getOpcode
+ */
+ public function testOpcodeFromBits($fin, $rsv1, $rsv2, $rsv3, $opcode, $bin) {
+ $this->_frame->addBuffer(static::encode($bin));
+ $this->assertEquals($opcode, $this->_frame->getOpcode());
+ }
+
+ /**
+ * @dataProvider UnframeMessageProvider
+ * covers Ratchet\RFC6455\Messaging\Frame::isFinal
+ */
+ public function testFinCodeFromFullMessage($msg, $encoded) {
+ $this->_frame->addBuffer(base64_decode($encoded));
+ $this->assertTrue($this->_frame->isFinal());
+ }
+
+ /**
+ * @dataProvider UnframeMessageProvider
+ * covers Ratchet\RFC6455\Messaging\Frame::getOpcode
+ */
+ public function testOpcodeFromFullMessage($msg, $encoded) {
+ $this->_frame->addBuffer(base64_decode($encoded));
+ $this->assertEquals(1, $this->_frame->getOpcode());
+ }
+
+ public static function payloadLengthDescriptionProvider() {
+ return array(
+ array(7, '01110101'),
+ array(7, '01111101'),
+ array(23, '01111110'),
+ array(71, '01111111'),
+ array(7, '00000000'), // Should this throw an exception? Can a payload be empty?
+ array(7, '00000001')
+ );
+ }
+
+ /**
+ * @dataProvider payloadLengthDescriptionProvider
+ * covers Ratchet\RFC6455\Messaging\Frame::addBuffer
+ * covers Ratchet\RFC6455\Messaging\Frame::getFirstPayloadVal
+ */
+ public function testFirstPayloadDesignationValue($bits, $bin) {
+ $this->_frame->addBuffer(static::encode($this->_firstByteFinText));
+ $this->_frame->addBuffer(static::encode($bin));
+ $ref = new \ReflectionClass($this->_frame);
+ $cb = $ref->getMethod('getFirstPayloadVal');
+ $cb->setAccessible(true);
+ $this->assertEquals(bindec($bin), $cb->invoke($this->_frame));
+ }
+
+ /**
+ * covers Ratchet\RFC6455\Messaging\Frame::getFirstPayloadVal
+ */
+ public function testFirstPayloadValUnderflow() {
+ $ref = new \ReflectionClass($this->_frame);
+ $cb = $ref->getMethod('getFirstPayloadVal');
+ $cb->setAccessible(true);
+ $this->setExpectedException('UnderflowException');
+ $cb->invoke($this->_frame);
+ }
+
+ /**
+ * @dataProvider payloadLengthDescriptionProvider
+ * covers Ratchet\RFC6455\Messaging\Frame::getNumPayloadBits
+ */
+ public function testDetermineHowManyBitsAreUsedToDescribePayload($expected_bits, $bin) {
+ $this->_frame->addBuffer(static::encode($this->_firstByteFinText));
+ $this->_frame->addBuffer(static::encode($bin));
+ $ref = new \ReflectionClass($this->_frame);
+ $cb = $ref->getMethod('getNumPayloadBits');
+ $cb->setAccessible(true);
+ $this->assertEquals($expected_bits, $cb->invoke($this->_frame));
+ }
+
+ /**
+ * covers Ratchet\RFC6455\Messaging\Frame::getNumPayloadBits
+ */
+ public function testgetNumPayloadBitsUnderflow() {
+ $ref = new \ReflectionClass($this->_frame);
+ $cb = $ref->getMethod('getNumPayloadBits');
+ $cb->setAccessible(true);
+ $this->setExpectedException('UnderflowException');
+ $cb->invoke($this->_frame);
+ }
+
+ public function secondByteProvider() {
+ return array(
+ array(true, 1, '10000001'),
+ array(false, 1, '00000001'),
+ array(true, 125, $this->_secondByteMaskedSPL)
+ );
+ }
+ /**
+ * @dataProvider secondByteProvider
+ * covers Ratchet\RFC6455\Messaging\Frame::isMasked
+ */
+ public function testIsMaskedReturnsExpectedValue($masked, $payload_length, $bin) {
+ $this->_frame->addBuffer(static::encode($this->_firstByteFinText));
+ $this->_frame->addBuffer(static::encode($bin));
+ $this->assertEquals($masked, $this->_frame->isMasked());
+ }
+
+ /**
+ * @dataProvider UnframeMessageProvider
+ * covers Ratchet\RFC6455\Messaging\Frame::isMasked
+ */
+ public function testIsMaskedFromFullMessage($msg, $encoded) {
+ $this->_frame->addBuffer(base64_decode($encoded));
+ $this->assertTrue($this->_frame->isMasked());
+ }
+
+ /**
+ * @dataProvider secondByteProvider
+ * covers Ratchet\RFC6455\Messaging\Frame::getPayloadLength
+ */
+ public function testGetPayloadLengthWhenOnlyFirstFrameIsUsed($masked, $payload_length, $bin) {
+ $this->_frame->addBuffer(static::encode($this->_firstByteFinText));
+ $this->_frame->addBuffer(static::encode($bin));
+ $this->assertEquals($payload_length, $this->_frame->getPayloadLength());
+ }
+
+ /**
+ * @dataProvider UnframeMessageProvider
+ * covers Ratchet\RFC6455\Messaging\Frame::getPayloadLength
+ * @todo Not yet testing when second additional payload length descriptor
+ */
+ public function testGetPayloadLengthFromFullMessage($msg, $encoded) {
+ $this->_frame->addBuffer(base64_decode($encoded));
+ $this->assertEquals(strlen($msg), $this->_frame->getPayloadLength());
+ }
+
+ public function maskingKeyProvider() {
+ $frame = new Frame;
+ return array(
+ array($frame->generateMaskingKey()),
+ array($frame->generateMaskingKey()),
+ array($frame->generateMaskingKey())
+ );
+ }
+
+ /**
+ * @dataProvider maskingKeyProvider
+ * covers Ratchet\RFC6455\Messaging\Frame::getMaskingKey
+ * @todo I I wrote the dataProvider incorrectly, skipping for now
+ */
+ public function testGetMaskingKey($mask) {
+ $this->_frame->addBuffer(static::encode($this->_firstByteFinText));
+ $this->_frame->addBuffer(static::encode($this->_secondByteMaskedSPL));
+ $this->_frame->addBuffer($mask);
+ $this->assertEquals($mask, $this->_frame->getMaskingKey());
+ }
+
+ /**
+ * covers Ratchet\RFC6455\Messaging\Frame::getMaskingKey
+ */
+ public function testGetMaskingKeyOnUnmaskedPayload() {
+ $frame = new Frame('Hello World!');
+ $this->assertEquals('', $frame->getMaskingKey());
+ }
+
+ /**
+ * @dataProvider UnframeMessageProvider
+ * covers Ratchet\RFC6455\Messaging\Frame::getPayload
+ * @todo Move this test to bottom as it requires all methods of the class
+ */
+ public function testUnframeFullMessage($unframed, $base_framed) {
+ $this->_frame->addBuffer(base64_decode($base_framed));
+ $this->assertEquals($unframed, $this->_frame->getPayload());
+ }
+
+ public static function messageFragmentProvider() {
+ return array(
+ array(false, '', '', '', '', '')
+ );
+ }
+
+ /**
+ * @dataProvider UnframeMessageProvider
+ * covers Ratchet\RFC6455\Messaging\Frame::getPayload
+ */
+ public function testCheckPiecingTogetherMessage($msg, $encoded) {
+ $framed = base64_decode($encoded);
+ for ($i = 0, $len = strlen($framed);$i < $len; $i++) {
+ $this->_frame->addBuffer(substr($framed, $i, 1));
+ }
+ $this->assertEquals($msg, $this->_frame->getPayload());
+ }
+
+ /**
+ * covers Ratchet\RFC6455\Messaging\Frame::__construct
+ * covers Ratchet\RFC6455\Messaging\Frame::getPayloadLength
+ * covers Ratchet\RFC6455\Messaging\Frame::getPayload
+ */
+ public function testLongCreate() {
+ $len = 65525;
+ $pl = $this->generateRandomString($len);
+ $frame = new Frame($pl, true, Frame::OP_PING);
+ $this->assertTrue($frame->isFinal());
+ $this->assertEquals(Frame::OP_PING, $frame->getOpcode());
+ $this->assertFalse($frame->isMasked());
+ $this->assertEquals($len, $frame->getPayloadLength());
+ $this->assertEquals($pl, $frame->getPayload());
+ }
+
+ /**
+ * covers Ratchet\RFC6455\Messaging\Frame::__construct
+ * covers Ratchet\RFC6455\Messaging\Frame::getPayloadLength
+ */
+ public function testReallyLongCreate() {
+ $len = 65575;
+ $frame = new Frame($this->generateRandomString($len));
+ $this->assertEquals($len, $frame->getPayloadLength());
+ }
+ /**
+ * covers Ratchet\RFC6455\Messaging\Frame::__construct
+ * covers Ratchet\RFC6455\Messaging\Frame::extractOverflow
+ */
+ public function testExtractOverflow() {
+ $string1 = $this->generateRandomString();
+ $frame1 = new Frame($string1);
+ $string2 = $this->generateRandomString();
+ $frame2 = new Frame($string2);
+ $cat = new Frame;
+ $cat->addBuffer($frame1->getContents() . $frame2->getContents());
+ $this->assertEquals($frame1->getContents(), $cat->getContents());
+ $this->assertEquals($string1, $cat->getPayload());
+ $uncat = new Frame;
+ $uncat->addBuffer($cat->extractOverflow());
+ $this->assertEquals($string1, $cat->getPayload());
+ $this->assertEquals($string2, $uncat->getPayload());
+ }
+
+ /**
+ * covers Ratchet\RFC6455\Messaging\Frame::extractOverflow
+ */
+ public function testEmptyExtractOverflow() {
+ $string = $this->generateRandomString();
+ $frame = new Frame($string);
+ $this->assertEquals($string, $frame->getPayload());
+ $this->assertEquals('', $frame->extractOverflow());
+ $this->assertEquals($string, $frame->getPayload());
+ }
+
+ /**
+ * covers Ratchet\RFC6455\Messaging\Frame::getContents
+ */
+ public function testGetContents() {
+ $msg = 'The quick brown fox jumps over the lazy dog.';
+ $frame1 = new Frame($msg);
+ $frame2 = new Frame($msg);
+ $frame2->maskPayload();
+ $this->assertNotEquals($frame1->getContents(), $frame2->getContents());
+ $this->assertEquals(strlen($frame1->getContents()) + 4, strlen($frame2->getContents()));
+ }
+
+ /**
+ * covers Ratchet\RFC6455\Messaging\Frame::maskPayload
+ */
+ public function testMasking() {
+ $msg = 'The quick brown fox jumps over the lazy dog.';
+ $frame = new Frame($msg);
+ $frame->maskPayload();
+ $this->assertTrue($frame->isMasked());
+ $this->assertEquals($msg, $frame->getPayload());
+ }
+
+ /**
+ * covers Ratchet\RFC6455\Messaging\Frame::unMaskPayload
+ */
+ public function testUnMaskPayload() {
+ $string = $this->generateRandomString();
+ $frame = new Frame($string);
+ $frame->maskPayload()->unMaskPayload();
+ $this->assertFalse($frame->isMasked());
+ $this->assertEquals($string, $frame->getPayload());
+ }
+
+ /**
+ * covers Ratchet\RFC6455\Messaging\Frame::generateMaskingKey
+ */
+ public function testGenerateMaskingKey() {
+ $dupe = false;
+ $done = array();
+ for ($i = 0; $i < 10; $i++) {
+ $new = $this->_frame->generateMaskingKey();
+ if (in_array($new, $done)) {
+ $dupe = true;
+ }
+ $done[] = $new;
+ }
+ $this->assertEquals(4, strlen($new));
+ $this->assertFalse($dupe);
+ }
+
+ /**
+ * covers Ratchet\RFC6455\Messaging\Frame::maskPayload
+ */
+ public function testGivenMaskIsValid() {
+ $this->setExpectedException('InvalidArgumentException');
+ $this->_frame->maskPayload('hello world');
+ }
+
+ /**
+ * covers Ratchet\RFC6455\Messaging\Frame::maskPayload
+ */
+ public function testGivenMaskIsValidAscii() {
+ if (!extension_loaded('mbstring')) {
+ $this->markTestSkipped("mbstring required for this test");
+ return;
+ }
+ $this->setExpectedException('OutOfBoundsException');
+ $this->_frame->maskPayload('x✖');
+ }
+
+ protected function generateRandomString($length = 10, $addSpaces = true, $addNumbers = true) {
+ $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"$%&/()=[]{}'; // ยง
+ $useChars = array();
+ for($i = 0; $i < $length; $i++) {
+ $useChars[] = $characters[mt_rand(0, strlen($characters) - 1)];
+ }
+ if($addSpaces === true) {
+ array_push($useChars, ' ', ' ', ' ', ' ', ' ', ' ');
+ }
+ if($addNumbers === true) {
+ array_push($useChars, rand(0, 9), rand(0, 9), rand(0, 9));
+ }
+ shuffle($useChars);
+ $randomString = trim(implode('', $useChars));
+ $randomString = substr($randomString, 0, $length);
+ return $randomString;
+ }
+
+ /**
+ * There was a frame boundary issue when the first 3 bytes of a frame with a payload greater than
+ * 126 was added to the frame buffer and then Frame::getPayloadLength was called. It would cause the frame
+ * to set the payload length to 126 and then not recalculate it once the full length information was available.
+ *
+ * This is fixed by setting the defPayLen back to -1 before the underflow exception is thrown.
+ *
+ * covers Ratchet\RFC6455\Messaging\Frame::getPayloadLength
+ * covers Ratchet\RFC6455\Messaging\Frame::extractOverflow
+ */
+ public function testFrameDeliveredOneByteAtATime() {
+ $startHeader = "\x01\x7e\x01\x00"; // header for a text frame of 256 - non-final
+ $framePayload = str_repeat("*", 256);
+ $rawOverflow = "xyz";
+ $rawFrame = $startHeader . $framePayload . $rawOverflow;
+ $frame = new Frame();
+ $payloadLen = 256;
+ for ($i = 0; $i < strlen($rawFrame); $i++) {
+ $frame->addBuffer($rawFrame[$i]);
+ try {
+ // payloadLen will
+ $payloadLen = $frame->getPayloadLength();
+ } catch (\UnderflowException $e) {
+ if ($i > 2) { // we should get an underflow on 0,1,2
+ $this->fail("Underflow exception when the frame length should be available");
+ }
+ }
+ if ($payloadLen !== 256) {
+ $this->fail("Payload length of " . $payloadLen . " should have been 256.");
+ }
+ }
+ // make sure the overflow is good
+ $this->assertEquals($rawOverflow, $frame->extractOverflow());
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/unit/Messaging/MessageBufferTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/unit/Messaging/MessageBufferTest.php
new file mode 100644
index 0000000..c33ff0c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/unit/Messaging/MessageBufferTest.php
@@ -0,0 +1,39 @@
+getContents();
+
+ $data = str_repeat($frameRaw, 1000);
+
+ $messageCount = 0;
+
+ $messageBuffer = new MessageBuffer(
+ new CloseFrameChecker(),
+ function (Message $message) use (&$messageCount) {
+ $messageCount++;
+ $this->assertEquals('a', $message->getPayload());
+ },
+ null,
+ false
+ );
+
+ $messageBuffer->onData($data);
+
+ $this->assertEquals(1000, $messageCount);
+ }
+}
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/unit/Messaging/MessageTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/unit/Messaging/MessageTest.php
new file mode 100644
index 0000000..1f7eab5
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/ratchet/rfc6455/tests/unit/Messaging/MessageTest.php
@@ -0,0 +1,58 @@
+message = new Message;
+ }
+
+ public function testNoFrames() {
+ $this->assertFalse($this->message->isCoalesced());
+ }
+
+ public function testNoFramesOpCode() {
+ $this->setExpectedException('UnderflowException');
+ $this->message->getOpCode();
+ }
+
+ public function testFragmentationPayload() {
+ $a = 'Hello ';
+ $b = 'World!';
+ $f1 = new Frame($a, false);
+ $f2 = new Frame($b, true, Frame::OP_CONTINUE);
+ $this->message->addFrame($f1)->addFrame($f2);
+ $this->assertEquals(strlen($a . $b), $this->message->getPayloadLength());
+ $this->assertEquals($a . $b, $this->message->getPayload());
+ }
+
+ public function testUnbufferedFragment() {
+ $this->message->addFrame(new Frame('The quick brow', false));
+ $this->setExpectedException('UnderflowException');
+ $this->message->getPayload();
+ }
+
+ public function testGetOpCode() {
+ $this->message
+ ->addFrame(new Frame('The quick brow', false, Frame::OP_TEXT))
+ ->addFrame(new Frame('n fox jumps ov', false, Frame::OP_CONTINUE))
+ ->addFrame(new Frame('er the lazy dog', true, Frame::OP_CONTINUE))
+ ;
+ $this->assertEquals(Frame::OP_TEXT, $this->message->getOpCode());
+ }
+
+ public function testGetUnBufferedPayloadLength() {
+ $this->message
+ ->addFrame(new Frame('The quick brow', false, Frame::OP_TEXT))
+ ->addFrame(new Frame('n fox jumps ov', false, Frame::OP_CONTINUE))
+ ;
+ $this->assertEquals(28, $this->message->getPayloadLength());
+ }
+}
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/.gitignore b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/.gitignore
new file mode 100644
index 0000000..987e2a2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/.gitignore
@@ -0,0 +1,2 @@
+composer.lock
+vendor
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/.travis.yml b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/.travis.yml
new file mode 100644
index 0000000..290df75
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/.travis.yml
@@ -0,0 +1,25 @@
+language: php
+
+php:
+# - 5.3 # requires old distro, see below
+ - 5.4
+ - 5.5
+ - 5.6
+ - 7
+ - hhvm
+
+# lock distro so new future defaults will not break the build
+dist: trusty
+
+matrix:
+ include:
+ - php: 5.3
+ dist: precise
+
+sudo: false
+
+install:
+ - composer install --no-interaction
+
+script:
+ - ./vendor/bin/phpunit --coverage-text
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/CHANGELOG.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/CHANGELOG.md
new file mode 100644
index 0000000..19d1801
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/CHANGELOG.md
@@ -0,0 +1,35 @@
+# Changelog
+
+## 0.4.2 (2017-12-20)
+
+* Improve documentation with usage and installation instructions
+ (#10 by @clue)
+
+* Improve test suite by adding PHPUnit to `require-dev` and
+ add forward compatibility with PHPUnit 5 and PHPUnit 6 and
+ sanitize Composer autoload paths
+ (#14 by @shaunbramley and #12 and #18 by @clue)
+
+## 0.4.1 (2016-02-25)
+
+* Repository maintenance, split off from main repo, improve test suite and documentation
+* First class support for PHP7 and HHVM (#9 by @clue)
+* Adjust compatibility to 5.3 (#7 by @clue)
+
+## 0.4.0 (2014-02-02)
+
+* BC break: Bump minimum PHP version to PHP 5.4, remove 5.3 specific hacks
+* BC break: Update to React/Promise 2.0
+* Dependency: Autoloading and filesystem structure now PSR-4 instead of PSR-0
+
+## 0.3.2 (2013-05-10)
+
+* Version bump
+
+## 0.3.0 (2013-04-14)
+
+* Version bump
+
+## 0.2.6 (2012-12-26)
+
+* Feature: New cache component, used by DNS
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/LICENSE b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/LICENSE
new file mode 100644
index 0000000..a808108
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2012 Igor Wiedler, Chris Boden
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/README.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/README.md
new file mode 100644
index 0000000..70ad40a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/README.md
@@ -0,0 +1,171 @@
+# Cache Component
+
+[](http://travis-ci.org/reactphp/cache) [](https://codeclimate.com/github/reactphp/cache)
+
+Async, [Promise](https://github.com/reactphp/promise)-based cache interface
+for [ReactPHP](https://reactphp.org/).
+
+The cache component provides a
+[Promise](https://github.com/reactphp/promise)-based
+[`CacheInterface`](#cacheinterface) and an in-memory [`ArrayCache`](#arraycache)
+implementation of that.
+This allows consumers to type hint against the interface and third parties to
+provide alternate implementations.
+
+**Table of Contents**
+
+* [Usage](#usage)
+ * [CacheInterface](#cacheinterface)
+ * [get()](#get)
+ * [set()](#set)
+ * [remove()](#remove)
+ * [ArrayCache](#arraycache)
+* [Common usage](#common-usage)
+ * [Fallback get](#fallback-get)
+ * [Fallback-get-and-set](#fallback-get-and-set)
+* [Install](#install)
+* [Tests](#tests)
+* [License](#license)
+
+## Usage
+
+### CacheInterface
+
+The `CacheInterface` describes the main interface of this component.
+This allows consumers to type hint against the interface and third parties to
+provide alternate implementations.
+
+#### get()
+
+```php
+$cache
+ ->get('foo')
+ ->then('var_dump');
+```
+
+This example fetches the value of the key `foo` and passes it to the
+`var_dump` function. You can use any of the composition provided by
+[promises](https://github.com/reactphp/promise).
+
+If the key `foo` does not exist, the promise will be rejected.
+
+#### set()
+
+```php
+$cache->set('foo', 'bar');
+```
+
+This example eventually sets the value of the key `foo` to `bar`. If it
+already exists, it is overridden. No guarantees are made as to when the cache
+value is set. If the cache implementation has to go over the network to store
+it, it may take a while.
+
+#### remove()
+
+```php
+$cache->remove('foo');
+```
+
+This example eventually removes the key `foo` from the cache. As with `set`,
+this may not happen instantly.
+
+### ArrayCache
+
+The `ArrayCache` provides an in-memory implementation of the
+[`CacheInterface`](#cacheinterface).
+
+```php
+$cache = new ArrayCache();
+
+$cache->set('foo', 'bar');
+```
+
+## Common usage
+
+### Fallback get
+
+A common use case of caches is to attempt fetching a cached value and as a
+fallback retrieve it from the original data source if not found. Here is an
+example of that:
+
+```php
+$cache
+ ->get('foo')
+ ->then(null, 'getFooFromDb')
+ ->then('var_dump');
+```
+
+First an attempt is made to retrieve the value of `foo`. A promise rejection
+handler of the function `getFooFromDb` is registered. `getFooFromDb` is a
+function (can be any PHP callable) that will be called if the key does not
+exist in the cache.
+
+`getFooFromDb` can handle the missing key by returning a promise for the
+actual value from the database (or any other data source). As a result, this
+chain will correctly fall back, and provide the value in both cases.
+
+### Fallback get and set
+
+To expand on the fallback get example, often you want to set the value on the
+cache after fetching it from the data source.
+
+```php
+$cache
+ ->get('foo')
+ ->then(null, array($this, 'getAndCacheFooFromDb'))
+ ->then('var_dump');
+
+public function getAndCacheFooFromDb()
+{
+ return $this->db
+ ->get('foo')
+ ->then(array($this, 'cacheFooFromDb'));
+}
+
+public function cacheFooFromDb($foo)
+{
+ $this->cache->set('foo', $foo);
+
+ return $foo;
+}
+```
+
+By using chaining you can easily conditionally cache the value if it is
+fetched from the database.
+
+## Install
+
+The recommended way to install this library is [through Composer](https://getcomposer.org).
+[New to Composer?](https://getcomposer.org/doc/00-intro.md)
+
+This will install the latest supported version:
+
+```bash
+$ composer require react/cache:^0.4.2
+```
+
+See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
+
+This project aims to run on any platform and thus does not require any PHP
+extensions and supports running on legacy PHP 5.3 through current PHP 7+ and
+HHVM.
+It's *highly recommended to use PHP 7+* for this project.
+
+## Tests
+
+To run the test suite, you first need to clone this repo and then install all
+dependencies [through Composer](https://getcomposer.org):
+
+```bash
+$ composer install
+```
+
+To run the test suite, go to the project root and run:
+
+```bash
+$ php vendor/bin/phpunit
+```
+
+## License
+
+MIT, see [LICENSE file](LICENSE).
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/composer.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/composer.json
new file mode 100644
index 0000000..51573b6
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/composer.json
@@ -0,0 +1,19 @@
+{
+ "name": "react/cache",
+ "description": "Async, Promise-based cache interface for ReactPHP",
+ "keywords": ["cache", "caching", "promise", "ReactPHP"],
+ "license": "MIT",
+ "require": {
+ "php": ">=5.3.0",
+ "react/promise": "~2.0|~1.1"
+ },
+ "autoload": {
+ "psr-4": { "React\\Cache\\": "src/" }
+ },
+ "autoload-dev": {
+ "psr-4": { "React\\Tests\\Cache\\": "tests/" }
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35"
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/phpunit.xml.dist b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/phpunit.xml.dist
new file mode 100644
index 0000000..d02182f
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/phpunit.xml.dist
@@ -0,0 +1,20 @@
+
+
+
+
+
+ ./tests/
+
+
+
+
+
+ ./src/
+
+
+
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/src/ArrayCache.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/src/ArrayCache.php
new file mode 100644
index 0000000..03dcc15
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/src/ArrayCache.php
@@ -0,0 +1,29 @@
+data[$key])) {
+ return Promise\reject();
+ }
+
+ return Promise\resolve($this->data[$key]);
+ }
+
+ public function set($key, $value)
+ {
+ $this->data[$key] = $value;
+ }
+
+ public function remove($key)
+ {
+ unset($this->data[$key]);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/src/CacheInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/src/CacheInterface.php
new file mode 100644
index 0000000..fd5f2d5
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/src/CacheInterface.php
@@ -0,0 +1,13 @@
+cache = new ArrayCache();
+ }
+
+ /** @test */
+ public function getShouldRejectPromiseForNonExistentKey()
+ {
+ $this->cache
+ ->get('foo')
+ ->then(
+ $this->expectCallableNever(),
+ $this->expectCallableOnce()
+ );
+ }
+
+ /** @test */
+ public function setShouldSetKey()
+ {
+ $this->cache
+ ->set('foo', 'bar');
+
+ $success = $this->createCallableMock();
+ $success
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with('bar');
+
+ $this->cache
+ ->get('foo')
+ ->then($success);
+ }
+
+ /** @test */
+ public function removeShouldRemoveKey()
+ {
+ $this->cache
+ ->set('foo', 'bar');
+
+ $this->cache
+ ->remove('foo');
+
+ $this->cache
+ ->get('foo')
+ ->then(
+ $this->expectCallableNever(),
+ $this->expectCallableOnce()
+ );
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/tests/CallableStub.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/tests/CallableStub.php
new file mode 100644
index 0000000..2f547cd
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/cache/tests/CallableStub.php
@@ -0,0 +1,10 @@
+createCallableMock();
+ $mock
+ ->expects($this->exactly($amount))
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ protected function expectCallableOnce()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ protected function expectCallableNever()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->never())
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ protected function createCallableMock()
+ {
+ return $this->getMockBuilder('React\Tests\Cache\CallableStub')->getMock();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/.gitignore b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/.gitignore
new file mode 100644
index 0000000..19982ea
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/.gitignore
@@ -0,0 +1,2 @@
+composer.lock
+vendor
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/.travis.yml b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/.travis.yml
new file mode 100644
index 0000000..41921e3
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/.travis.yml
@@ -0,0 +1,29 @@
+language: php
+
+php:
+# - 5.3 # requires old distro, see below
+ - 5.4
+ - 5.5
+ - 5.6
+ - 7.0
+ - 7.1
+ - 7.2
+ - hhvm # ignore errors, see below
+
+# lock distro so new future defaults will not break the build
+dist: trusty
+
+matrix:
+ include:
+ - php: 5.3
+ dist: precise
+ allow_failures:
+ - php: hhvm
+
+sudo: false
+
+install:
+ - composer install --no-interaction
+
+script:
+ - vendor/bin/phpunit --coverage-text
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/CHANGELOG.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/CHANGELOG.md
new file mode 100644
index 0000000..adad0a7
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/CHANGELOG.md
@@ -0,0 +1,179 @@
+# Changelog
+
+## 0.4.13 (2018-02-27)
+
+* Add `Config::loadSystemConfigBlocking()` to load default system config
+ and support parsing DNS config on all supported platforms
+ (`/etc/resolv.conf` on Unix/Linux/Mac and WMIC on Windows)
+ (#92, #93, #94 and #95 by @clue)
+
+ ```php
+ $config = Config::loadSystemConfigBlocking();
+ $server = $config->nameservers ? reset($config->nameservers) : '8.8.8.8';
+ ```
+
+* Remove unneeded cyclic dependency on react/socket
+ (#96 by @clue)
+
+## 0.4.12 (2018-01-14)
+
+* Improve test suite by adding forward compatibility with PHPUnit 6,
+ test against PHP 7.2, fix forward compatibility with upcoming EventLoop releases,
+ add test group to skip integration tests relying on internet connection
+ and add minor documentation improvements.
+ (#85 and #87 by @carusogabriel, #88 and #89 by @clue and #83 by @jsor)
+
+## 0.4.11 (2017-08-25)
+
+* Feature: Support resolving from default hosts file
+ (#75, #76 and #77 by @clue)
+
+ This means that resolving hosts such as `localhost` will now work as
+ expected across all platforms with no changes required:
+
+ ```php
+ $resolver->resolve('localhost')->then(function ($ip) {
+ echo 'IP: ' . $ip;
+ });
+ ```
+
+ The new `HostsExecutor` exists for advanced usage and is otherwise used
+ internally for this feature.
+
+## 0.4.10 (2017-08-10)
+
+* Feature: Forward compatibility with EventLoop v1.0 and v0.5 and
+ lock minimum dependencies and work around circular dependency for tests
+ (#70 and #71 by @clue)
+
+* Fix: Work around DNS timeout issues for Windows users
+ (#74 by @clue)
+
+* Documentation and examples for advanced usage
+ (#66 by @WyriHaximus)
+
+* Remove broken TCP code, do not retry with invalid TCP query
+ (#73 by @clue)
+
+* Improve test suite by fixing HHVM build for now again and ignore future HHVM build errors and
+ lock Travis distro so new defaults will not break the build and
+ fix failing tests for PHP 7.1
+ (#68 by @WyriHaximus and #69 and #72 by @clue)
+
+## 0.4.9 (2017-05-01)
+
+* Feature: Forward compatibility with upcoming Socket v1.0 and v0.8
+ (#61 by @clue)
+
+## 0.4.8 (2017-04-16)
+
+* Feature: Add support for the AAAA record type to the protocol parser
+ (#58 by @othillo)
+
+* Feature: Add support for the PTR record type to the protocol parser
+ (#59 by @othillo)
+
+## 0.4.7 (2017-03-31)
+
+* Feature: Forward compatibility with upcoming Socket v0.6 and v0.7 component
+ (#57 by @clue)
+
+## 0.4.6 (2017-03-11)
+
+* Fix: Fix DNS timeout issues for Windows users and add forward compatibility
+ with Stream v0.5 and upcoming v0.6
+ (#53 by @clue)
+
+* Improve test suite by adding PHPUnit to `require-dev`
+ (#54 by @clue)
+
+## 0.4.5 (2017-03-02)
+
+* Fix: Ensure we ignore the case of the answer
+ (#51 by @WyriHaximus)
+
+* Feature: Add `TimeoutExecutor` and simplify internal APIs to allow internal
+ code re-use for upcoming versions.
+ (#48 and #49 by @clue)
+
+## 0.4.4 (2017-02-13)
+
+* Fix: Fix handling connection and stream errors
+ (#45 by @clue)
+
+* Feature: Add examples and forward compatibility with upcoming Socket v0.5 component
+ (#46 and #47 by @clue)
+
+## 0.4.3 (2016-07-31)
+
+* Feature: Allow for cache adapter injection (#38 by @WyriHaximus)
+
+ ```php
+ $factory = new React\Dns\Resolver\Factory();
+
+ $cache = new MyCustomCacheInstance();
+ $resolver = $factory->createCached('8.8.8.8', $loop, $cache);
+ ```
+
+* Feature: Support Promise cancellation (#35 by @clue)
+
+ ```php
+ $promise = $resolver->resolve('reactphp.org');
+
+ $promise->cancel();
+ ```
+
+## 0.4.2 (2016-02-24)
+
+* Repository maintenance, split off from main repo, improve test suite and documentation
+* First class support for PHP7 and HHVM (#34 by @clue)
+* Adjust compatibility to 5.3 (#30 by @clue)
+
+## 0.4.1 (2014-04-13)
+
+* Bug fix: Fixed PSR-4 autoload path (@marcj/WyriHaximus)
+
+## 0.4.0 (2014-02-02)
+
+* BC break: Bump minimum PHP version to PHP 5.4, remove 5.3 specific hacks
+* BC break: Update to React/Promise 2.0
+* Bug fix: Properly resolve CNAME aliases
+* Dependency: Autoloading and filesystem structure now PSR-4 instead of PSR-0
+* Bump React dependencies to v0.4
+
+## 0.3.2 (2013-05-10)
+
+* Feature: Support default port for IPv6 addresses (@clue)
+
+## 0.3.0 (2013-04-14)
+
+* Bump React dependencies to v0.3
+
+## 0.2.6 (2012-12-26)
+
+* Feature: New cache component, used by DNS
+
+## 0.2.5 (2012-11-26)
+
+* Version bump
+
+## 0.2.4 (2012-11-18)
+
+* Feature: Change to promise-based API (@jsor)
+
+## 0.2.3 (2012-11-14)
+
+* Version bump
+
+## 0.2.2 (2012-10-28)
+
+* Feature: DNS executor timeout handling (@arnaud-lb)
+* Feature: DNS retry executor (@arnaud-lb)
+
+## 0.2.1 (2012-10-14)
+
+* Minor adjustments to DNS parser
+
+## 0.2.0 (2012-09-10)
+
+* Feature: DNS resolver
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/LICENSE b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/LICENSE
new file mode 100644
index 0000000..a808108
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2012 Igor Wiedler, Chris Boden
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/README.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/README.md
new file mode 100644
index 0000000..ed86667
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/README.md
@@ -0,0 +1,209 @@
+# Dns
+
+[](https://travis-ci.org/reactphp/dns)
+
+Async DNS resolver for [ReactPHP](https://reactphp.org/).
+
+The main point of the DNS component is to provide async DNS resolution.
+However, it is really a toolkit for working with DNS messages, and could
+easily be used to create a DNS server.
+
+**Table of contents**
+
+* [Basic usage](#basic-usage)
+* [Caching](#caching)
+ * [Custom cache adapter](#custom-cache-adapter)
+* [Advanced usage](#advanced-usage)
+ * [HostsFileExecutor](#hostsfileexecutor)
+* [Install](#install)
+* [Tests](#tests)
+* [License](#license)
+* [References](#references)
+
+## Basic usage
+
+The most basic usage is to just create a resolver through the resolver
+factory. All you need to give it is a nameserver, then you can start resolving
+names, baby!
+
+```php
+$loop = React\EventLoop\Factory::create();
+
+$config = React\Dns\Config\Config::loadSystemConfigBlocking();
+$server = $config->nameservers ? reset($config->nameservers) : '8.8.8.8';
+
+$factory = new React\Dns\Resolver\Factory();
+$dns = $factory->create($server, $loop);
+
+$dns->resolve('igor.io')->then(function ($ip) {
+ echo "Host: $ip\n";
+});
+
+$loop->run();
+```
+
+See also the [first example](examples).
+
+The `Config` class can be used to load the system default config. This is an
+operation that may access the filesystem and block. Ideally, this method should
+thus be executed only once before the loop starts and not repeatedly while it is
+running.
+Note that this class may return an *empty* configuration if the system config
+can not be loaded. As such, you'll likely want to apply a default nameserver
+as above if none can be found.
+
+> Note that the factory loads the hosts file from the filesystem once when
+ creating the resolver instance.
+ Ideally, this method should thus be executed only once before the loop starts
+ and not repeatedly while it is running.
+
+Pending DNS queries can be cancelled by cancelling its pending promise like so:
+
+```php
+$promise = $resolver->resolve('reactphp.org');
+
+$promise->cancel();
+```
+
+But there's more.
+
+## Caching
+
+You can cache results by configuring the resolver to use a `CachedExecutor`:
+
+```php
+$loop = React\EventLoop\Factory::create();
+
+$config = React\Dns\Config\Config::loadSystemConfigBlocking();
+$server = $config->nameservers ? reset($config->nameservers) : '8.8.8.8';
+
+$factory = new React\Dns\Resolver\Factory();
+$dns = $factory->createCached($server, $loop);
+
+$dns->resolve('igor.io')->then(function ($ip) {
+ echo "Host: $ip\n";
+});
+
+...
+
+$dns->resolve('igor.io')->then(function ($ip) {
+ echo "Host: $ip\n";
+});
+
+$loop->run();
+```
+
+If the first call returns before the second, only one query will be executed.
+The second result will be served from an in memory cache.
+This is particularly useful for long running scripts where the same hostnames
+have to be looked up multiple times.
+
+See also the [third example](examples).
+
+### Custom cache adapter
+
+By default, the above will use an in memory cache.
+
+You can also specify a custom cache implementing [`CacheInterface`](https://github.com/reactphp/cache) to handle the record cache instead:
+
+```php
+$cache = new React\Cache\ArrayCache();
+$loop = React\EventLoop\Factory::create();
+$factory = new React\Dns\Resolver\Factory();
+$dns = $factory->createCached('8.8.8.8', $loop, $cache);
+```
+
+See also the wiki for possible [cache implementations](https://github.com/reactphp/react/wiki/Users#cache-implementations).
+
+## Advanced Usage
+
+For more advanced usages one can utilize the `React\Dns\Query\Executor` directly.
+The following example looks up the `IPv6` address for `igor.io`.
+
+```php
+$loop = Factory::create();
+
+$executor = new Executor($loop, new Parser(), new BinaryDumper(), null);
+
+$executor->query(
+ '8.8.8.8:53',
+ new Query($name, Message::TYPE_AAAA, Message::CLASS_IN, time())
+)->done(function (Message $message) {
+ foreach ($message->answers as $answer) {
+ echo 'IPv6: ' . $answer->data . PHP_EOL;
+ }
+}, 'printf');
+
+$loop->run();
+
+```
+
+See also the [fourth example](examples).
+
+### HostsFileExecutor
+
+Note that the above `Executor` class always performs an actual DNS query.
+If you also want to take entries from your hosts file into account, you may
+use this code:
+
+```php
+$hosts = \React\Dns\Config\HostsFile::loadFromPathBlocking();
+
+$executor = new Executor($loop, new Parser(), new BinaryDumper(), null);
+$executor = new HostsFileExecutor($hosts, $executor);
+
+$executor->query(
+ '8.8.8.8:53',
+ new Query('localhost', Message::TYPE_A, Message::CLASS_IN, time())
+);
+```
+
+## Install
+
+The recommended way to install this library is [through Composer](https://getcomposer.org).
+[New to Composer?](https://getcomposer.org/doc/00-intro.md)
+
+This will install the latest supported version:
+
+```bash
+$ composer require react/dns:^0.4.13
+```
+
+See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
+
+This project aims to run on any platform and thus does not require any PHP
+extensions and supports running on legacy PHP 5.3 through current PHP 7+ and
+HHVM.
+It's *highly recommended to use PHP 7+* for this project.
+
+## Tests
+
+To run the test suite, you first need to clone this repo and then install all
+dependencies [through Composer](https://getcomposer.org):
+
+```bash
+$ composer install
+```
+
+To run the test suite, go to the project root and run:
+
+```bash
+$ php vendor/bin/phpunit
+```
+
+The test suite also contains a number of functional integration tests that rely
+on a stable internet connection.
+If you do not want to run these, they can simply be skipped like this:
+
+```bash
+$ php vendor/bin/phpunit --exclude-group internet
+```
+
+## License
+
+MIT, see [LICENSE file](LICENSE).
+
+## References
+
+* [RFC 1034](https://tools.ietf.org/html/rfc1034) Domain Names - Concepts and Facilities
+* [RFC 1035](https://tools.ietf.org/html/rfc1035) Domain Names - Implementation and Specification
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/composer.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/composer.json
new file mode 100644
index 0000000..510a43c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/composer.json
@@ -0,0 +1,24 @@
+{
+ "name": "react/dns",
+ "description": "Async DNS resolver for ReactPHP",
+ "keywords": ["dns", "dns-resolver", "ReactPHP", "async"],
+ "license": "MIT",
+ "require": {
+ "php": ">=5.3.0",
+ "react/cache": "~0.4.0|~0.3.0",
+ "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5",
+ "react/promise": "^2.1 || ^1.2.1",
+ "react/promise-timer": "^1.2",
+ "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4.5"
+ },
+ "require-dev": {
+ "clue/block-react": "^1.2",
+ "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35"
+ },
+ "autoload": {
+ "psr-4": { "React\\Dns\\": "src" }
+ },
+ "autoload-dev": {
+ "psr-4": { "React\\Tests\\Dns\\": "tests" }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/examples/01-one.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/examples/01-one.php
new file mode 100644
index 0000000..5db164f
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/examples/01-one.php
@@ -0,0 +1,22 @@
+nameservers ? reset($config->nameservers) : '8.8.8.8';
+
+$factory = new Factory();
+$resolver = $factory->create($server, $loop);
+
+$name = isset($argv[1]) ? $argv[1] : 'www.google.com';
+
+$resolver->resolve($name)->then(function ($ip) use ($name) {
+ echo 'IP for ' . $name . ': ' . $ip . PHP_EOL;
+}, 'printf');
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/examples/02-concurrent.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/examples/02-concurrent.php
new file mode 100644
index 0000000..87e3f5c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/examples/02-concurrent.php
@@ -0,0 +1,27 @@
+nameservers ? reset($config->nameservers) : '8.8.8.8';
+
+$factory = new Factory();
+$resolver = $factory->create($server, $loop);
+
+$names = array_slice($argv, 1);
+if (!$names) {
+ $names = array('google.com', 'www.google.com', 'gmail.com');
+}
+
+foreach ($names as $name) {
+ $resolver->resolve($name)->then(function ($ip) use ($name) {
+ echo 'IP for ' . $name . ': ' . $ip . PHP_EOL;
+ }, 'printf');
+}
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/examples/03-cached.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/examples/03-cached.php
new file mode 100644
index 0000000..e76a27c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/examples/03-cached.php
@@ -0,0 +1,40 @@
+nameservers ? reset($config->nameservers) : '8.8.8.8';
+
+$factory = new Factory();
+$resolver = $factory->createCached($server, $loop);
+
+$name = isset($argv[1]) ? $argv[1] : 'www.google.com';
+
+$resolver->resolve($name)->then(function ($ip) use ($name) {
+ echo 'IP for ' . $name . ': ' . $ip . PHP_EOL;
+}, 'printf');
+
+$loop->addTimer(1.0, function() use ($name, $resolver) {
+ $resolver->resolve($name)->then(function ($ip) use ($name) {
+ echo 'IP for ' . $name . ': ' . $ip . PHP_EOL;
+ }, 'printf');
+});
+
+$loop->addTimer(2.0, function() use ($name, $resolver) {
+ $resolver->resolve($name)->then(function ($ip) use ($name) {
+ echo 'IP for ' . $name . ': ' . $ip . PHP_EOL;
+ }, 'printf');
+});
+
+$loop->addTimer(3.0, function() use ($name, $resolver) {
+ $resolver->resolve($name)->then(function ($ip) use ($name) {
+ echo 'IP for ' . $name . ': ' . $ip . PHP_EOL;
+ }, 'printf');
+});
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/examples/04-query-a-and-aaaa.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/examples/04-query-a-and-aaaa.php
new file mode 100644
index 0000000..6c46bbf
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/examples/04-query-a-and-aaaa.php
@@ -0,0 +1,32 @@
+query('8.8.8.8:53', $ipv4Query)->done(function (Message $message) {
+ foreach ($message->answers as $answer) {
+ echo 'IPv4: ' . $answer->data . PHP_EOL;
+ }
+}, 'printf');
+$executor->query('8.8.8.8:53', $ipv6Query)->done(function (Message $message) {
+ foreach ($message->answers as $answer) {
+ echo 'IPv6: ' . $answer->data . PHP_EOL;
+ }
+}, 'printf');
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/phpunit.xml.dist b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/phpunit.xml.dist
new file mode 100644
index 0000000..13d3fab
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/phpunit.xml.dist
@@ -0,0 +1,25 @@
+
+
+
+
+
+ ./tests/
+
+
+
+
+
+ ./src/
+
+
+
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/BadServerException.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/BadServerException.php
new file mode 100644
index 0000000..3bf50f1
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/BadServerException.php
@@ -0,0 +1,7 @@
+nameservers = $matches[1];
+
+ return $config;
+ }
+
+ /**
+ * Loads the DNS configurations from Windows's WMIC (from the given command or default command)
+ *
+ * Note that this method blocks while loading the given command and should
+ * thus be used with care! While this should be relatively fast for normal
+ * WMIC commands, it remains unknown if this may block under certain
+ * circumstances. In particular, this method should only be executed before
+ * the loop starts, not while it is running.
+ *
+ * Note that this method will only try to execute the given command try to
+ * parse its output, irrespective of whether this command exists. In
+ * particular, this command is only available on Windows. Currently, this
+ * will only parse valid nameserver entries from the command output and will
+ * ignore all other output without complaining.
+ *
+ * Note that the previous section implies that this may return an empty
+ * `Config` object if no valid nameserver entries can be found.
+ *
+ * @param ?string $command (advanced) should not be given (NULL) unless you know what you're doing
+ * @return self
+ * @link https://ss64.com/nt/wmic.html
+ */
+ public static function loadWmicBlocking($command = null)
+ {
+ $contents = shell_exec($command === null ? 'wmic NICCONFIG get "DNSServerSearchOrder" /format:CSV' : $command);
+ preg_match_all('/(?<=[{;,"])([\da-f.:]{4,})(?=[};,"])/i', $contents, $matches);
+
+ $config = new self();
+ $config->nameservers = $matches[1];
+
+ return $config;
+ }
+
+ public $nameservers = array();
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Config/FilesystemFactory.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Config/FilesystemFactory.php
new file mode 100644
index 0000000..68cec3e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Config/FilesystemFactory.php
@@ -0,0 +1,73 @@
+loop = $loop;
+ }
+
+ public function create($filename)
+ {
+ return $this
+ ->loadEtcResolvConf($filename)
+ ->then(array($this, 'parseEtcResolvConf'));
+ }
+
+ /**
+ * @param string $contents
+ * @return Promise
+ * @deprecated see Config instead
+ */
+ public function parseEtcResolvConf($contents)
+ {
+ return Promise\resolve(Config::loadResolvConfBlocking(
+ 'data://text/plain;base64,' . base64_encode($contents)
+ ));
+ }
+
+ public function loadEtcResolvConf($filename)
+ {
+ if (!file_exists($filename)) {
+ return Promise\reject(new \InvalidArgumentException("The filename for /etc/resolv.conf given does not exist: $filename"));
+ }
+
+ try {
+ $deferred = new Deferred();
+
+ $fd = fopen($filename, 'r');
+ stream_set_blocking($fd, 0);
+
+ $contents = '';
+
+ $stream = class_exists('React\Stream\ReadableResourceStream') ? new ReadableResourceStream($fd, $this->loop) : new Stream($fd, $this->loop);
+ $stream->on('data', function ($data) use (&$contents) {
+ $contents .= $data;
+ });
+ $stream->on('end', function () use (&$contents, $deferred) {
+ $deferred->resolve($contents);
+ });
+ $stream->on('error', function ($error) use ($deferred) {
+ $deferred->reject($error);
+ });
+
+ return $deferred->promise();
+ } catch (\Exception $e) {
+ return Promise\reject($e);
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Config/HostsFile.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Config/HostsFile.php
new file mode 100644
index 0000000..5b6277e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Config/HostsFile.php
@@ -0,0 +1,151 @@
+contents = $contents;
+ }
+
+ /**
+ * Returns all IPs for the given hostname
+ *
+ * @param string $name
+ * @return string[]
+ */
+ public function getIpsForHost($name)
+ {
+ $name = strtolower($name);
+
+ $ips = array();
+ foreach (preg_split('/\r?\n/', $this->contents) as $line) {
+ $parts = preg_split('/\s+/', $line);
+ $ip = array_shift($parts);
+ if ($parts && array_search($name, $parts) !== false) {
+ // remove IPv6 zone ID (`fe80::1%lo0` => `fe80:1`)
+ if (strpos($ip, ':') !== false && ($pos = strpos($ip, '%')) !== false) {
+ $ip = substr($ip, 0, $pos);
+ }
+
+ if (@inet_pton($ip) !== false) {
+ $ips[] = $ip;
+ }
+ }
+ }
+
+ return $ips;
+ }
+
+ /**
+ * Returns all hostnames for the given IPv4 or IPv6 address
+ *
+ * @param string $ip
+ * @return string[]
+ */
+ public function getHostsForIp($ip)
+ {
+ // check binary representation of IP to avoid string case and short notation
+ $ip = @inet_pton($ip);
+ if ($ip === false) {
+ return array();
+ }
+
+ $names = array();
+ foreach (preg_split('/\r?\n/', $this->contents) as $line) {
+ $parts = preg_split('/\s+/', $line, null, PREG_SPLIT_NO_EMPTY);
+ $addr = array_shift($parts);
+
+ // remove IPv6 zone ID (`fe80::1%lo0` => `fe80:1`)
+ if (strpos($addr, ':') !== false && ($pos = strpos($addr, '%')) !== false) {
+ $addr = substr($addr, 0, $pos);
+ }
+
+ if (@inet_pton($addr) === $ip) {
+ foreach ($parts as $part) {
+ $names[] = $part;
+ }
+ }
+ }
+
+ return $names;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Model/HeaderBag.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Model/HeaderBag.php
new file mode 100644
index 0000000..193e65c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Model/HeaderBag.php
@@ -0,0 +1,56 @@
+ 0,
+ 'anCount' => 0,
+ 'nsCount' => 0,
+ 'arCount' => 0,
+ 'qr' => 0,
+ 'opcode' => Message::OPCODE_QUERY,
+ 'aa' => 0,
+ 'tc' => 0,
+ 'rd' => 0,
+ 'ra' => 0,
+ 'z' => 0,
+ 'rcode' => Message::RCODE_OK,
+ );
+
+ public function get($name)
+ {
+ return isset($this->attributes[$name]) ? $this->attributes[$name] : null;
+ }
+
+ public function set($name, $value)
+ {
+ $this->attributes[$name] = $value;
+ }
+
+ public function isQuery()
+ {
+ return 0 === $this->attributes['qr'];
+ }
+
+ public function isResponse()
+ {
+ return 1 === $this->attributes['qr'];
+ }
+
+ public function isTruncated()
+ {
+ return 1 === $this->attributes['tc'];
+ }
+
+ public function populateCounts(Message $message)
+ {
+ $this->attributes['qdCount'] = count($message->questions);
+ $this->attributes['anCount'] = count($message->answers);
+ $this->attributes['nsCount'] = count($message->authority);
+ $this->attributes['arCount'] = count($message->additional);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Model/Message.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Model/Message.php
new file mode 100644
index 0000000..715cb1f
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Model/Message.php
@@ -0,0 +1,100 @@
+header->set('id', self::generateId());
+ $request->header->set('rd', 1);
+ $request->questions[] = (array) $query;
+ $request->prepare();
+
+ return $request;
+ }
+
+ /**
+ * Creates a new response message for the given query with the given answer records
+ *
+ * @param Query $query
+ * @param Record[] $answers
+ * @return self
+ */
+ public static function createResponseWithAnswersForQuery(Query $query, array $answers)
+ {
+ $response = new Message();
+ $response->header->set('id', self::generateId());
+ $response->header->set('qr', 1);
+ $response->header->set('opcode', Message::OPCODE_QUERY);
+ $response->header->set('rd', 1);
+ $response->header->set('rcode', Message::RCODE_OK);
+
+ $response->questions[] = (array) $query;
+
+ foreach ($answers as $record) {
+ $response->answers[] = $record;
+ }
+
+ $response->prepare();
+
+ return $response;
+ }
+
+ private static function generateId()
+ {
+ return mt_rand(0, 0xffff);
+ }
+
+ public $data = '';
+
+ public $header;
+ public $questions = array();
+ public $answers = array();
+ public $authority = array();
+ public $additional = array();
+
+ public $consumed = 0;
+
+ public function __construct()
+ {
+ $this->header = new HeaderBag();
+ }
+
+ public function prepare()
+ {
+ $this->header->populateCounts($this);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Model/Record.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Model/Record.php
new file mode 100644
index 0000000..029d232
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Model/Record.php
@@ -0,0 +1,21 @@
+name = $name;
+ $this->type = $type;
+ $this->class = $class;
+ $this->ttl = $ttl;
+ $this->data = $data;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Protocol/BinaryDumper.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Protocol/BinaryDumper.php
new file mode 100644
index 0000000..35d6ae6
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Protocol/BinaryDumper.php
@@ -0,0 +1,62 @@
+headerToBinary($message->header);
+ $data .= $this->questionToBinary($message->questions);
+
+ return $data;
+ }
+
+ private function headerToBinary(HeaderBag $header)
+ {
+ $data = '';
+
+ $data .= pack('n', $header->get('id'));
+
+ $flags = 0x00;
+ $flags = ($flags << 1) | $header->get('qr');
+ $flags = ($flags << 4) | $header->get('opcode');
+ $flags = ($flags << 1) | $header->get('aa');
+ $flags = ($flags << 1) | $header->get('tc');
+ $flags = ($flags << 1) | $header->get('rd');
+ $flags = ($flags << 1) | $header->get('ra');
+ $flags = ($flags << 3) | $header->get('z');
+ $flags = ($flags << 4) | $header->get('rcode');
+
+ $data .= pack('n', $flags);
+
+ $data .= pack('n', $header->get('qdCount'));
+ $data .= pack('n', $header->get('anCount'));
+ $data .= pack('n', $header->get('nsCount'));
+ $data .= pack('n', $header->get('arCount'));
+
+ return $data;
+ }
+
+ private function questionToBinary(array $questions)
+ {
+ $data = '';
+
+ foreach ($questions as $question) {
+ $labels = explode('.', $question['name']);
+ foreach ($labels as $label) {
+ $data .= chr(strlen($label)).$label;
+ }
+ $data .= "\x00";
+
+ $data .= pack('n*', $question['type'], $question['class']);
+ }
+
+ return $data;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Protocol/Parser.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Protocol/Parser.php
new file mode 100644
index 0000000..1191cd3
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Protocol/Parser.php
@@ -0,0 +1,254 @@
+parse($data, $message) !== $message) {
+ throw new InvalidArgumentException('Unable to parse binary message');
+ }
+
+ return $message;
+ }
+
+ /**
+ * @deprecated unused, exists for BC only
+ */
+ public function parseChunk($data, Message $message)
+ {
+ return $this->parse($data, $message);
+ }
+
+ private function parse($data, Message $message)
+ {
+ $message->data .= $data;
+
+ if (!$message->header->get('id')) {
+ if (!$this->parseHeader($message)) {
+ return;
+ }
+ }
+
+ if ($message->header->get('qdCount') != count($message->questions)) {
+ if (!$this->parseQuestion($message)) {
+ return;
+ }
+ }
+
+ if ($message->header->get('anCount') != count($message->answers)) {
+ if (!$this->parseAnswer($message)) {
+ return;
+ }
+ }
+
+ return $message;
+ }
+
+ public function parseHeader(Message $message)
+ {
+ if (strlen($message->data) < 12) {
+ return;
+ }
+
+ $header = substr($message->data, 0, 12);
+ $message->consumed += 12;
+
+ list($id, $fields, $qdCount, $anCount, $nsCount, $arCount) = array_values(unpack('n*', $header));
+
+ $rcode = $fields & bindec('1111');
+ $z = ($fields >> 4) & bindec('111');
+ $ra = ($fields >> 7) & 1;
+ $rd = ($fields >> 8) & 1;
+ $tc = ($fields >> 9) & 1;
+ $aa = ($fields >> 10) & 1;
+ $opcode = ($fields >> 11) & bindec('1111');
+ $qr = ($fields >> 15) & 1;
+
+ $vars = compact('id', 'qdCount', 'anCount', 'nsCount', 'arCount',
+ 'qr', 'opcode', 'aa', 'tc', 'rd', 'ra', 'z', 'rcode');
+
+
+ foreach ($vars as $name => $value) {
+ $message->header->set($name, $value);
+ }
+
+ return $message;
+ }
+
+ public function parseQuestion(Message $message)
+ {
+ if (strlen($message->data) < 2) {
+ return;
+ }
+
+ $consumed = $message->consumed;
+
+ list($labels, $consumed) = $this->readLabels($message->data, $consumed);
+
+ if (null === $labels) {
+ return;
+ }
+
+ if (strlen($message->data) - $consumed < 4) {
+ return;
+ }
+
+ list($type, $class) = array_values(unpack('n*', substr($message->data, $consumed, 4)));
+ $consumed += 4;
+
+ $message->consumed = $consumed;
+
+ $message->questions[] = array(
+ 'name' => implode('.', $labels),
+ 'type' => $type,
+ 'class' => $class,
+ );
+
+ if ($message->header->get('qdCount') != count($message->questions)) {
+ return $this->parseQuestion($message);
+ }
+
+ return $message;
+ }
+
+ public function parseAnswer(Message $message)
+ {
+ if (strlen($message->data) < 2) {
+ return;
+ }
+
+ $consumed = $message->consumed;
+
+ list($labels, $consumed) = $this->readLabels($message->data, $consumed);
+
+ if (null === $labels) {
+ return;
+ }
+
+ if (strlen($message->data) - $consumed < 10) {
+ return;
+ }
+
+ list($type, $class) = array_values(unpack('n*', substr($message->data, $consumed, 4)));
+ $consumed += 4;
+
+ list($ttl) = array_values(unpack('N', substr($message->data, $consumed, 4)));
+ $consumed += 4;
+
+ list($rdLength) = array_values(unpack('n', substr($message->data, $consumed, 2)));
+ $consumed += 2;
+
+ $rdata = null;
+
+ if (Message::TYPE_A === $type || Message::TYPE_AAAA === $type) {
+ $ip = substr($message->data, $consumed, $rdLength);
+ $consumed += $rdLength;
+
+ $rdata = inet_ntop($ip);
+ }
+
+ if (Message::TYPE_CNAME === $type || Message::TYPE_PTR === $type) {
+ list($bodyLabels, $consumed) = $this->readLabels($message->data, $consumed);
+
+ $rdata = implode('.', $bodyLabels);
+ }
+
+ $message->consumed = $consumed;
+
+ $name = implode('.', $labels);
+ $ttl = $this->signedLongToUnsignedLong($ttl);
+ $record = new Record($name, $type, $class, $ttl, $rdata);
+
+ $message->answers[] = $record;
+
+ if ($message->header->get('anCount') != count($message->answers)) {
+ return $this->parseAnswer($message);
+ }
+
+ return $message;
+ }
+
+ private function readLabels($data, $consumed)
+ {
+ $labels = array();
+
+ while (true) {
+ if ($this->isEndOfLabels($data, $consumed)) {
+ $consumed += 1;
+ break;
+ }
+
+ if ($this->isCompressedLabel($data, $consumed)) {
+ list($newLabels, $consumed) = $this->getCompressedLabel($data, $consumed);
+ $labels = array_merge($labels, $newLabels);
+ break;
+ }
+
+ $length = ord(substr($data, $consumed, 1));
+ $consumed += 1;
+
+ if (strlen($data) - $consumed < $length) {
+ return array(null, null);
+ }
+
+ $labels[] = substr($data, $consumed, $length);
+ $consumed += $length;
+ }
+
+ return array($labels, $consumed);
+ }
+
+ public function isEndOfLabels($data, $consumed)
+ {
+ $length = ord(substr($data, $consumed, 1));
+ return 0 === $length;
+ }
+
+ public function getCompressedLabel($data, $consumed)
+ {
+ list($nameOffset, $consumed) = $this->getCompressedLabelOffset($data, $consumed);
+ list($labels) = $this->readLabels($data, $nameOffset);
+
+ return array($labels, $consumed);
+ }
+
+ public function isCompressedLabel($data, $consumed)
+ {
+ $mask = 0xc000; // 1100000000000000
+ list($peek) = array_values(unpack('n', substr($data, $consumed, 2)));
+
+ return (bool) ($peek & $mask);
+ }
+
+ public function getCompressedLabelOffset($data, $consumed)
+ {
+ $mask = 0x3fff; // 0011111111111111
+ list($peek) = array_values(unpack('n', substr($data, $consumed, 2)));
+
+ return array($peek & $mask, $consumed + 2);
+ }
+
+ public function signedLongToUnsignedLong($i)
+ {
+ return $i & 0x80000000 ? $i - 0xffffffff : $i;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/CachedExecutor.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/CachedExecutor.php
new file mode 100644
index 0000000..285936d
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/CachedExecutor.php
@@ -0,0 +1,55 @@
+executor = $executor;
+ $this->cache = $cache;
+ }
+
+ public function query($nameserver, Query $query)
+ {
+ $executor = $this->executor;
+ $cache = $this->cache;
+
+ return $this->cache
+ ->lookup($query)
+ ->then(
+ function ($cachedRecords) use ($query) {
+ return Message::createResponseWithAnswersForQuery($query, $cachedRecords);
+ },
+ function () use ($executor, $cache, $nameserver, $query) {
+ return $executor
+ ->query($nameserver, $query)
+ ->then(function ($response) use ($cache, $query) {
+ $cache->storeResponseMessage($query->currentTime, $response);
+ return $response;
+ });
+ }
+ );
+ }
+
+ /**
+ * @deprecated unused, exists for BC only
+ */
+ public function buildResponse(Query $query, array $cachedRecords)
+ {
+ return Message::createResponseWithAnswersForQuery($query, $cachedRecords);
+ }
+
+ /**
+ * @deprecated unused, exists for BC only
+ */
+ protected function generateId()
+ {
+ return mt_rand(0, 0xffff);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/CancellationException.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/CancellationException.php
new file mode 100644
index 0000000..ac30f4c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/CancellationException.php
@@ -0,0 +1,7 @@
+loop = $loop;
+ $this->parser = $parser;
+ $this->dumper = $dumper;
+ $this->timeout = $timeout;
+ }
+
+ public function query($nameserver, Query $query)
+ {
+ $request = Message::createRequestForQuery($query);
+
+ $queryData = $this->dumper->toBinary($request);
+ $transport = strlen($queryData) > 512 ? 'tcp' : 'udp';
+
+ return $this->doQuery($nameserver, $transport, $queryData, $query->name);
+ }
+
+ /**
+ * @deprecated unused, exists for BC only
+ */
+ public function prepareRequest(Query $query)
+ {
+ return Message::createRequestForQuery($query);
+ }
+
+ public function doQuery($nameserver, $transport, $queryData, $name)
+ {
+ // we only support UDP right now
+ if ($transport !== 'udp') {
+ return Promise\reject(new \RuntimeException(
+ 'DNS query for ' . $name . ' failed: Requested transport "' . $transport . '" not available, only UDP is supported in this version'
+ ));
+ }
+
+ $that = $this;
+ $parser = $this->parser;
+ $loop = $this->loop;
+
+ // UDP connections are instant, so try this without a timer
+ try {
+ $conn = $this->createConnection($nameserver, $transport);
+ } catch (\Exception $e) {
+ return Promise\reject(new \RuntimeException('DNS query for ' . $name . ' failed: ' . $e->getMessage(), 0, $e));
+ }
+
+ $deferred = new Deferred(function ($resolve, $reject) use (&$timer, $loop, &$conn, $name) {
+ $reject(new CancellationException(sprintf('DNS query for %s has been cancelled', $name)));
+
+ if ($timer !== null) {
+ $loop->cancelTimer($timer);
+ }
+ $conn->close();
+ });
+
+ $timer = null;
+ if ($this->timeout !== null) {
+ $timer = $this->loop->addTimer($this->timeout, function () use (&$conn, $name, $deferred) {
+ $conn->close();
+ $deferred->reject(new TimeoutException(sprintf("DNS query for %s timed out", $name)));
+ });
+ }
+
+ $conn->on('data', function ($data) use ($conn, $parser, $deferred, $timer, $loop, $name) {
+ $conn->end();
+ if ($timer !== null) {
+ $loop->cancelTimer($timer);
+ }
+
+ try {
+ $response = $parser->parseMessage($data);
+ } catch (\Exception $e) {
+ $deferred->reject($e);
+ return;
+ }
+
+ if ($response->header->isTruncated()) {
+ $deferred->reject(new \RuntimeException('DNS query for ' . $name . ' failed: The server returned a truncated result for a UDP query, but retrying via TCP is currently not supported'));
+ return;
+ }
+
+ $deferred->resolve($response);
+ });
+ $conn->write($queryData);
+
+ return $deferred->promise();
+ }
+
+ /**
+ * @deprecated unused, exists for BC only
+ */
+ protected function generateId()
+ {
+ return mt_rand(0, 0xffff);
+ }
+
+ /**
+ * @param string $nameserver
+ * @param string $transport
+ * @return \React\Stream\DuplexStreamInterface
+ */
+ protected function createConnection($nameserver, $transport)
+ {
+ $fd = @stream_socket_client("$transport://$nameserver", $errno, $errstr, 0, STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT);
+ if ($fd === false) {
+ throw new \RuntimeException('Unable to connect to DNS server: ' . $errstr, $errno);
+ }
+
+ // Instantiate stream instance around this stream resource.
+ // This ought to be replaced with a datagram socket in the future.
+ // Temporary work around for Windows 10: buffer whole UDP response
+ // @coverageIgnoreStart
+ if (!class_exists('React\Stream\Stream')) {
+ // prefer DuplexResourceStream as of react/stream v0.7.0
+ $conn = new DuplexResourceStream($fd, $this->loop, -1);
+ } else {
+ // use legacy Stream class for react/stream < v0.7.0
+ $conn = new Stream($fd, $this->loop);
+ $conn->bufferSize = null;
+ }
+ // @coverageIgnoreEnd
+
+ return $conn;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/ExecutorInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/ExecutorInterface.php
new file mode 100644
index 0000000..2f7a635
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/ExecutorInterface.php
@@ -0,0 +1,8 @@
+hosts = $hosts;
+ $this->fallback = $fallback;
+ }
+
+ public function query($nameserver, Query $query)
+ {
+ if ($query->class === Message::CLASS_IN && ($query->type === Message::TYPE_A || $query->type === Message::TYPE_AAAA)) {
+ // forward lookup for type A or AAAA
+ $records = array();
+ $expectsColon = $query->type === Message::TYPE_AAAA;
+ foreach ($this->hosts->getIpsForHost($query->name) as $ip) {
+ // ensure this is an IPv4/IPV6 address according to query type
+ if ((strpos($ip, ':') !== false) === $expectsColon) {
+ $records[] = new Record($query->name, $query->type, $query->class, 0, $ip);
+ }
+ }
+
+ if ($records) {
+ return Promise\resolve(
+ Message::createResponseWithAnswersForQuery($query, $records)
+ );
+ }
+ } elseif ($query->class === Message::CLASS_IN && $query->type === Message::TYPE_PTR) {
+ // reverse lookup: extract IPv4 or IPv6 from special `.arpa` domain
+ $ip = $this->getIpFromHost($query->name);
+
+ if ($ip !== null) {
+ $records = array();
+ foreach ($this->hosts->getHostsForIp($ip) as $host) {
+ $records[] = new Record($query->name, $query->type, $query->class, 0, $host);
+ }
+
+ if ($records) {
+ return Promise\resolve(
+ Message::createResponseWithAnswersForQuery($query, $records)
+ );
+ }
+ }
+ }
+
+ return $this->fallback->query($nameserver, $query);
+ }
+
+ private function getIpFromHost($host)
+ {
+ if (substr($host, -13) === '.in-addr.arpa') {
+ // IPv4: read as IP and reverse bytes
+ $ip = @inet_pton(substr($host, 0, -13));
+ if ($ip === false || isset($ip[4])) {
+ return null;
+ }
+
+ return inet_ntop(strrev($ip));
+ } elseif (substr($host, -9) === '.ip6.arpa') {
+ // IPv6: replace dots, reverse nibbles and interpret as hexadecimal string
+ $ip = @inet_ntop(pack('H*', strrev(str_replace('.', '', substr($host, 0, -9)))));
+ if ($ip === false) {
+ return null;
+ }
+
+ return $ip;
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/Query.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/Query.php
new file mode 100644
index 0000000..aef6e05
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/Query.php
@@ -0,0 +1,19 @@
+name = $name;
+ $this->type = $type;
+ $this->class = $class;
+ $this->currentTime = $currentTime;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/RecordBag.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/RecordBag.php
new file mode 100644
index 0000000..358cf5d
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/RecordBag.php
@@ -0,0 +1,27 @@
+records[$record->data] = array($currentTime + $record->ttl, $record);
+ }
+
+ public function all()
+ {
+ return array_values(array_map(
+ function ($value) {
+ list($expiresAt, $record) = $value;
+ return $record;
+ },
+ $this->records
+ ));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/RecordCache.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/RecordCache.php
new file mode 100644
index 0000000..b8142d3
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/RecordCache.php
@@ -0,0 +1,82 @@
+cache = $cache;
+ }
+
+ public function lookup(Query $query)
+ {
+ $id = $this->serializeQueryToIdentity($query);
+
+ $expiredAt = $this->expiredAt;
+
+ return $this->cache
+ ->get($id)
+ ->then(function ($value) use ($query, $expiredAt) {
+ $recordBag = unserialize($value);
+
+ if (null !== $expiredAt && $expiredAt <= $query->currentTime) {
+ return Promise\reject();
+ }
+
+ return $recordBag->all();
+ });
+ }
+
+ public function storeResponseMessage($currentTime, Message $message)
+ {
+ foreach ($message->answers as $record) {
+ $this->storeRecord($currentTime, $record);
+ }
+ }
+
+ public function storeRecord($currentTime, Record $record)
+ {
+ $id = $this->serializeRecordToIdentity($record);
+
+ $cache = $this->cache;
+
+ $this->cache
+ ->get($id)
+ ->then(
+ function ($value) {
+ return unserialize($value);
+ },
+ function ($e) {
+ return new RecordBag();
+ }
+ )
+ ->then(function ($recordBag) use ($id, $currentTime, $record, $cache) {
+ $recordBag->set($currentTime, $record);
+ $cache->set($id, serialize($recordBag));
+ });
+ }
+
+ public function expire($currentTime)
+ {
+ $this->expiredAt = $currentTime;
+ }
+
+ public function serializeQueryToIdentity(Query $query)
+ {
+ return sprintf('%s:%s:%s', $query->name, $query->type, $query->class);
+ }
+
+ public function serializeRecordToIdentity(Record $record)
+ {
+ return sprintf('%s:%s:%s', $record->name, $record->type, $record->class);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/RetryExecutor.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/RetryExecutor.php
new file mode 100644
index 0000000..90353e5
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/RetryExecutor.php
@@ -0,0 +1,44 @@
+executor = $executor;
+ $this->retries = $retries;
+ }
+
+ public function query($nameserver, Query $query)
+ {
+ return $this->tryQuery($nameserver, $query, $this->retries);
+ }
+
+ public function tryQuery($nameserver, Query $query, $retries)
+ {
+ $that = $this;
+ $errorback = function ($error) use ($nameserver, $query, $retries, $that) {
+ if (!$error instanceof TimeoutException) {
+ throw $error;
+ }
+ if (0 >= $retries) {
+ throw new \RuntimeException(
+ sprintf("DNS query for %s failed: too many retries", $query->name),
+ 0,
+ $error
+ );
+ }
+ return $that->tryQuery($nameserver, $query, $retries-1);
+ };
+
+ return $this->executor
+ ->query($nameserver, $query)
+ ->then(null, $errorback);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/TimeoutException.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/TimeoutException.php
new file mode 100644
index 0000000..90bf806
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Query/TimeoutException.php
@@ -0,0 +1,7 @@
+executor = $executor;
+ $this->loop = $loop;
+ $this->timeout = $timeout;
+ }
+
+ public function query($nameserver, Query $query)
+ {
+ return Timer\timeout($this->executor->query($nameserver, $query), $this->timeout, $this->loop)->then(null, function ($e) use ($query) {
+ if ($e instanceof Timer\TimeoutException) {
+ $e = new TimeoutException(sprintf("DNS query for %s timed out", $query->name), 0, $e);
+ }
+ throw $e;
+ });
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/RecordNotFoundException.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/RecordNotFoundException.php
new file mode 100644
index 0000000..0028413
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/RecordNotFoundException.php
@@ -0,0 +1,7 @@
+addPortToServerIfMissing($nameserver);
+ $executor = $this->decorateHostsFileExecutor($this->createRetryExecutor($loop));
+
+ return new Resolver($nameserver, $executor);
+ }
+
+ public function createCached($nameserver, LoopInterface $loop, CacheInterface $cache = null)
+ {
+ if (!($cache instanceof CacheInterface)) {
+ $cache = new ArrayCache();
+ }
+
+ $nameserver = $this->addPortToServerIfMissing($nameserver);
+ $executor = $this->decorateHostsFileExecutor($this->createCachedExecutor($loop, $cache));
+
+ return new Resolver($nameserver, $executor);
+ }
+
+ /**
+ * Tries to load the hosts file and decorates the given executor on success
+ *
+ * @param ExecutorInterface $executor
+ * @return ExecutorInterface
+ * @codeCoverageIgnore
+ */
+ private function decorateHostsFileExecutor(ExecutorInterface $executor)
+ {
+ try {
+ $executor = new HostsFileExecutor(
+ HostsFile::loadFromPathBlocking(),
+ $executor
+ );
+ } catch (\RuntimeException $e) {
+ // ignore this file if it can not be loaded
+ }
+
+ // Windows does not store localhost in hosts file by default but handles this internally
+ // To compensate for this, we explicitly use hard-coded defaults for localhost
+ if (DIRECTORY_SEPARATOR === '\\') {
+ $executor = new HostsFileExecutor(
+ new HostsFile("127.0.0.1 localhost\n::1 localhost"),
+ $executor
+ );
+ }
+
+ return $executor;
+ }
+
+ protected function createExecutor(LoopInterface $loop)
+ {
+ return new TimeoutExecutor(
+ new Executor($loop, new Parser(), new BinaryDumper(), null),
+ 5.0,
+ $loop
+ );
+ }
+
+ protected function createRetryExecutor(LoopInterface $loop)
+ {
+ return new RetryExecutor($this->createExecutor($loop));
+ }
+
+ protected function createCachedExecutor(LoopInterface $loop, CacheInterface $cache)
+ {
+ return new CachedExecutor($this->createRetryExecutor($loop), new RecordCache($cache));
+ }
+
+ protected function addPortToServerIfMissing($nameserver)
+ {
+ if (strpos($nameserver, '[') === false && substr_count($nameserver, ':') >= 2) {
+ // several colons, but not enclosed in square brackets => enclose IPv6 address in square brackets
+ $nameserver = '[' . $nameserver . ']';
+ }
+ // assume a dummy scheme when checking for the port, otherwise parse_url() fails
+ if (parse_url('dummy://' . $nameserver, PHP_URL_PORT) === null) {
+ $nameserver .= ':53';
+ }
+
+ return $nameserver;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Resolver/Resolver.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Resolver/Resolver.php
new file mode 100644
index 0000000..4a4983a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/src/Resolver/Resolver.php
@@ -0,0 +1,100 @@
+nameserver = $nameserver;
+ $this->executor = $executor;
+ }
+
+ public function resolve($domain)
+ {
+ $query = new Query($domain, Message::TYPE_A, Message::CLASS_IN, time());
+ $that = $this;
+
+ return $this->executor
+ ->query($this->nameserver, $query)
+ ->then(function (Message $response) use ($query, $that) {
+ return $that->extractAddress($query, $response);
+ });
+ }
+
+ public function extractAddress(Query $query, Message $response)
+ {
+ $answers = $response->answers;
+
+ $addresses = $this->resolveAliases($answers, $query->name);
+
+ if (0 === count($addresses)) {
+ $message = 'DNS Request did not return valid answer.';
+ throw new RecordNotFoundException($message);
+ }
+
+ $address = $addresses[array_rand($addresses)];
+ return $address;
+ }
+
+ public function resolveAliases(array $answers, $name)
+ {
+ $named = $this->filterByName($answers, $name);
+ $aRecords = $this->filterByType($named, Message::TYPE_A);
+ $cnameRecords = $this->filterByType($named, Message::TYPE_CNAME);
+
+ if ($aRecords) {
+ return $this->mapRecordData($aRecords);
+ }
+
+ if ($cnameRecords) {
+ $aRecords = array();
+
+ $cnames = $this->mapRecordData($cnameRecords);
+ foreach ($cnames as $cname) {
+ $targets = $this->filterByName($answers, $cname);
+ $aRecords = array_merge(
+ $aRecords,
+ $this->resolveAliases($answers, $cname)
+ );
+ }
+
+ return $aRecords;
+ }
+
+ return array();
+ }
+
+ private function filterByName(array $answers, $name)
+ {
+ return $this->filterByField($answers, 'name', $name);
+ }
+
+ private function filterByType(array $answers, $type)
+ {
+ return $this->filterByField($answers, 'type', $type);
+ }
+
+ private function filterByField(array $answers, $field, $value)
+ {
+ $value = strtolower($value);
+ return array_filter($answers, function ($answer) use ($field, $value) {
+ return $value === strtolower($answer->$field);
+ });
+ }
+
+ private function mapRecordData(array $records)
+ {
+ return array_map(function ($record) {
+ return $record->data;
+ }, $records);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/CallableStub.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/CallableStub.php
new file mode 100644
index 0000000..a34a263
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/CallableStub.php
@@ -0,0 +1,10 @@
+assertInstanceOf('React\Dns\Config\Config', $config);
+ }
+
+ public function testLoadsDefaultPath()
+ {
+ if (DIRECTORY_SEPARATOR === '\\') {
+ $this->markTestSkipped('Not supported on Windows');
+ }
+
+ $config = Config::loadResolvConfBlocking();
+
+ $this->assertInstanceOf('React\Dns\Config\Config', $config);
+ }
+
+ public function testLoadsFromExplicitPath()
+ {
+ $config = Config::loadResolvConfBlocking(__DIR__ . '/../Fixtures/etc/resolv.conf');
+
+ $this->assertEquals(array('8.8.8.8'), $config->nameservers);
+ }
+
+ /**
+ * @expectedException RuntimeException
+ */
+ public function testLoadThrowsWhenPathIsInvalid()
+ {
+ Config::loadResolvConfBlocking(__DIR__ . '/invalid.conf');
+ }
+
+ public function testParsesSingleEntryFile()
+ {
+ $contents = 'nameserver 8.8.8.8';
+ $expected = array('8.8.8.8');
+
+ $config = Config::loadResolvConfBlocking('data://text/plain;base64,' . base64_encode($contents));
+ $this->assertEquals($expected, $config->nameservers);
+ }
+
+ public function testParsesNameserverEntriesFromAverageFileCorrectly()
+ {
+ $contents = '#
+# Mac OS X Notice
+#
+# This file is not used by the host name and address resolution
+# or the DNS query routing mechanisms used by most processes on
+# this Mac OS X system.
+#
+# This file is automatically generated.
+#
+domain v.cablecom.net
+nameserver 127.0.0.1
+nameserver ::1
+';
+ $expected = array('127.0.0.1', '::1');
+
+ $config = Config::loadResolvConfBlocking('data://text/plain;base64,' . base64_encode($contents));
+ $this->assertEquals($expected, $config->nameservers);
+ }
+
+ public function testParsesEmptyFileWithoutNameserverEntries()
+ {
+ $contents = '';
+ $expected = array();
+
+ $config = Config::loadResolvConfBlocking('data://text/plain;base64,');
+ $this->assertEquals($expected, $config->nameservers);
+ }
+
+ public function testParsesFileAndIgnoresCommentsAndInvalidNameserverEntries()
+ {
+ $contents = '
+# nameserver 1.2.3.4
+; nameserver 2.3.4.5
+
+nameserver 3.4.5.6 # nope
+nameserver 4.5.6.7 5.6.7.8
+ nameserver 6.7.8.9
+NameServer 7.8.9.10
+';
+ $expected = array();
+
+ $config = Config::loadResolvConfBlocking('data://text/plain;base64,' . base64_encode($contents));
+ $this->assertEquals($expected, $config->nameservers);
+ }
+
+ public function testLoadsFromWmicOnWindows()
+ {
+ if (DIRECTORY_SEPARATOR !== '\\') {
+ $this->markTestSkipped('Only on Windows');
+ }
+
+ $config = Config::loadWmicBlocking();
+
+ $this->assertInstanceOf('React\Dns\Config\Config', $config);
+ }
+
+ public function testLoadsSingleEntryFromWmicOutput()
+ {
+ $contents = '
+Node,DNSServerSearchOrder
+ACE,
+ACE,{192.168.2.1}
+ACE,
+';
+ $expected = array('192.168.2.1');
+
+ $config = Config::loadWmicBlocking($this->echoCommand($contents));
+
+ $this->assertEquals($expected, $config->nameservers);
+ }
+
+ public function testLoadsEmptyListFromWmicOutput()
+ {
+ $contents = '
+Node,DNSServerSearchOrder
+ACE,
+';
+ $expected = array();
+
+ $config = Config::loadWmicBlocking($this->echoCommand($contents));
+
+ $this->assertEquals($expected, $config->nameservers);
+ }
+
+ public function testLoadsSingleEntryForMultipleNicsFromWmicOutput()
+ {
+ $contents = '
+Node,DNSServerSearchOrder
+ACE,
+ACE,{192.168.2.1}
+ACE,
+ACE,{192.168.2.2}
+ACE,
+';
+ $expected = array('192.168.2.1', '192.168.2.2');
+
+ $config = Config::loadWmicBlocking($this->echoCommand($contents));
+
+ $this->assertEquals($expected, $config->nameservers);
+ }
+
+ public function testLoadsMultipleEntriesForSingleNicWithSemicolonFromWmicOutput()
+ {
+ $contents = '
+Node,DNSServerSearchOrder
+ACE,
+ACE,{192.168.2.1;192.168.2.2}
+ACE,
+';
+ $expected = array('192.168.2.1', '192.168.2.2');
+
+ $config = Config::loadWmicBlocking($this->echoCommand($contents));
+
+ $this->assertEquals($expected, $config->nameservers);
+ }
+
+ public function testLoadsMultipleEntriesForSingleNicWithQuotesFromWmicOutput()
+ {
+ $contents = '
+Node,DNSServerSearchOrder
+ACE,
+ACE,{"192.168.2.1","192.168.2.2"}
+ACE,
+';
+ $expected = array('192.168.2.1', '192.168.2.2');
+
+ $config = Config::loadWmicBlocking($this->echoCommand($contents));
+
+ $this->assertEquals($expected, $config->nameservers);
+ }
+
+ private function echoCommand($output)
+ {
+ return 'echo ' . escapeshellarg($output);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Config/FilesystemFactoryTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Config/FilesystemFactoryTest.php
new file mode 100644
index 0000000..bb9eac7
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Config/FilesystemFactoryTest.php
@@ -0,0 +1,70 @@
+getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $factory = new FilesystemFactory($loop);
+ $factory->parseEtcResolvConf($contents)->then(function ($config) use (&$capturedConfig) {
+ $capturedConfig = $config;
+ });
+
+ $this->assertNotNull($capturedConfig);
+ $this->assertSame($expected, $capturedConfig->nameservers);
+ }
+
+ /** @test */
+ public function createShouldLoadStuffFromFilesystem()
+ {
+ $this->markTestIncomplete('Filesystem API is incomplete');
+
+ $expected = array('8.8.8.8');
+
+ $triggerListener = null;
+ $capturedConfig = null;
+
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop
+ ->expects($this->once())
+ ->method('addReadStream')
+ ->will($this->returnCallback(function ($stream, $listener) use (&$triggerListener) {
+ $triggerListener = function () use ($stream, $listener) {
+ call_user_func($listener, $stream);
+ };
+ }));
+
+ $factory = new FilesystemFactory($loop);
+ $factory->create(__DIR__.'/../Fixtures/etc/resolv.conf')->then(function ($config) use (&$capturedConfig) {
+ $capturedConfig = $config;
+ });
+
+ $triggerListener();
+
+ $this->assertNotNull($capturedConfig);
+ $this->assertSame($expected, $capturedConfig->nameservers);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Config/HostsFileTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Config/HostsFileTest.php
new file mode 100644
index 0000000..ff74ad2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Config/HostsFileTest.php
@@ -0,0 +1,170 @@
+assertInstanceOf('React\Dns\Config\HostsFile', $hosts);
+ }
+
+ public function testDefaultShouldHaveLocalhostMapped()
+ {
+ if (DIRECTORY_SEPARATOR === '\\') {
+ $this->markTestSkipped('Not supported on Windows');
+ }
+
+ $hosts = HostsFile::loadFromPathBlocking();
+
+ $this->assertContains('127.0.0.1', $hosts->getIpsForHost('localhost'));
+ }
+
+ /**
+ * @expectedException RuntimeException
+ */
+ public function testLoadThrowsForInvalidPath()
+ {
+ HostsFile::loadFromPathBlocking('does/not/exist');
+ }
+
+ public function testContainsSingleLocalhostEntry()
+ {
+ $hosts = new HostsFile('127.0.0.1 localhost');
+
+ $this->assertEquals(array('127.0.0.1'), $hosts->getIpsForHost('localhost'));
+ $this->assertEquals(array(), $hosts->getIpsForHost('example.com'));
+ }
+
+ public function testNonIpReturnsNothingForInvalidHosts()
+ {
+ $hosts = new HostsFile('a b');
+
+ $this->assertEquals(array(), $hosts->getIpsForHost('a'));
+ $this->assertEquals(array(), $hosts->getIpsForHost('b'));
+ }
+
+ public function testIgnoresIpv6ZoneId()
+ {
+ $hosts = new HostsFile('fe80::1%lo0 localhost');
+
+ $this->assertEquals(array('fe80::1'), $hosts->getIpsForHost('localhost'));
+ }
+
+ public function testSkipsComments()
+ {
+ $hosts = new HostsFile('# start' . PHP_EOL .'#127.0.0.1 localhost' . PHP_EOL . '127.0.0.2 localhost # example.com');
+
+ $this->assertEquals(array('127.0.0.2'), $hosts->getIpsForHost('localhost'));
+ $this->assertEquals(array(), $hosts->getIpsForHost('example.com'));
+ }
+
+ public function testContainsSingleLocalhostEntryWithCaseIgnored()
+ {
+ $hosts = new HostsFile('127.0.0.1 LocalHost');
+
+ $this->assertEquals(array('127.0.0.1'), $hosts->getIpsForHost('LOCALHOST'));
+ }
+
+ public function testEmptyFileContainsNothing()
+ {
+ $hosts = new HostsFile('');
+
+ $this->assertEquals(array(), $hosts->getIpsForHost('example.com'));
+ }
+
+ public function testSingleEntryWithMultipleNames()
+ {
+ $hosts = new HostsFile('127.0.0.1 localhost example.com');
+
+ $this->assertEquals(array('127.0.0.1'), $hosts->getIpsForHost('example.com'));
+ $this->assertEquals(array('127.0.0.1'), $hosts->getIpsForHost('localhost'));
+ }
+
+ public function testMergesEntriesOverMultipleLines()
+ {
+ $hosts = new HostsFile("127.0.0.1 localhost\n127.0.0.2 localhost\n127.0.0.3 a localhost b\n127.0.0.4 a localhost");
+
+ $this->assertEquals(array('127.0.0.1', '127.0.0.2', '127.0.0.3', '127.0.0.4'), $hosts->getIpsForHost('localhost'));
+ }
+
+ public function testMergesIpv4AndIpv6EntriesOverMultipleLines()
+ {
+ $hosts = new HostsFile("127.0.0.1 localhost\n::1 localhost");
+
+ $this->assertEquals(array('127.0.0.1', '::1'), $hosts->getIpsForHost('localhost'));
+ }
+
+ public function testReverseLookup()
+ {
+ $hosts = new HostsFile('127.0.0.1 localhost');
+
+ $this->assertEquals(array('localhost'), $hosts->getHostsForIp('127.0.0.1'));
+ $this->assertEquals(array(), $hosts->getHostsForIp('192.168.1.1'));
+ }
+
+ public function testReverseSkipsComments()
+ {
+ $hosts = new HostsFile("# start\n#127.0.0.1 localhosted\n127.0.0.2\tlocalhost\t# example.com\n\t127.0.0.3\t\texample.org\t\t");
+
+ $this->assertEquals(array(), $hosts->getHostsForIp('127.0.0.1'));
+ $this->assertEquals(array('localhost'), $hosts->getHostsForIp('127.0.0.2'));
+ $this->assertEquals(array('example.org'), $hosts->getHostsForIp('127.0.0.3'));
+ }
+
+ public function testReverseNonIpReturnsNothing()
+ {
+ $hosts = new HostsFile('127.0.0.1 localhost');
+
+ $this->assertEquals(array(), $hosts->getHostsForIp('localhost'));
+ $this->assertEquals(array(), $hosts->getHostsForIp('127.0.0.1.1'));
+ }
+
+ public function testReverseNonIpReturnsNothingForInvalidHosts()
+ {
+ $hosts = new HostsFile('a b');
+
+ $this->assertEquals(array(), $hosts->getHostsForIp('a'));
+ $this->assertEquals(array(), $hosts->getHostsForIp('b'));
+ }
+
+ public function testReverseLookupReturnsLowerCaseHost()
+ {
+ $hosts = new HostsFile('127.0.0.1 LocalHost');
+
+ $this->assertEquals(array('localhost'), $hosts->getHostsForIp('127.0.0.1'));
+ }
+
+ public function testReverseLookupChecksNormalizedIpv6()
+ {
+ $hosts = new HostsFile('FE80::00a1 localhost');
+
+ $this->assertEquals(array('localhost'), $hosts->getHostsForIp('fe80::A1'));
+ }
+
+ public function testReverseLookupIgnoresIpv6ZoneId()
+ {
+ $hosts = new HostsFile('fe80::1%lo0 localhost');
+
+ $this->assertEquals(array('localhost'), $hosts->getHostsForIp('fe80::1'));
+ }
+
+ public function testReverseLookupReturnsMultipleHostsOverSingleLine()
+ {
+ $hosts = new HostsFile("::1 ip6-localhost ip6-loopback");
+
+ $this->assertEquals(array('ip6-localhost', 'ip6-loopback'), $hosts->getHostsForIp('::1'));
+ }
+
+ public function testReverseLookupReturnsMultipleHostsOverMultipleLines()
+ {
+ $hosts = new HostsFile("::1 ip6-localhost\n::1 ip6-loopback");
+
+ $this->assertEquals(array('ip6-localhost', 'ip6-loopback'), $hosts->getHostsForIp('::1'));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Fixtures/etc/resolv.conf b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Fixtures/etc/resolv.conf
new file mode 100644
index 0000000..cae093a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Fixtures/etc/resolv.conf
@@ -0,0 +1 @@
+nameserver 8.8.8.8
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/FunctionalResolverTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/FunctionalResolverTest.php
new file mode 100644
index 0000000..0807e86
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/FunctionalResolverTest.php
@@ -0,0 +1,71 @@
+loop = LoopFactory::create();
+
+ $factory = new Factory();
+ $this->resolver = $factory->create('8.8.8.8', $this->loop);
+ }
+
+ public function testResolveLocalhostResolves()
+ {
+ $promise = $this->resolver->resolve('localhost');
+ $promise->then($this->expectCallableOnce(), $this->expectCallableNever());
+
+ $this->loop->run();
+ }
+
+ /**
+ * @group internet
+ */
+ public function testResolveGoogleResolves()
+ {
+ $promise = $this->resolver->resolve('google.com');
+ $promise->then($this->expectCallableOnce(), $this->expectCallableNever());
+
+ $this->loop->run();
+ }
+
+ /**
+ * @group internet
+ */
+ public function testResolveInvalidRejects()
+ {
+ $promise = $this->resolver->resolve('example.invalid');
+ $promise->then($this->expectCallableNever(), $this->expectCallableOnce());
+
+ $this->loop->run();
+ }
+
+ public function testResolveCancelledRejectsImmediately()
+ {
+ $promise = $this->resolver->resolve('google.com');
+ $promise->then($this->expectCallableNever(), $this->expectCallableOnce());
+ $promise->cancel();
+
+ $time = microtime(true);
+ $this->loop->run();
+ $time = microtime(true) - $time;
+
+ $this->assertLessThan(0.1, $time);
+ }
+
+ public function testInvalidResolverDoesNotResolveGoogle()
+ {
+ $factory = new Factory();
+ $this->resolver = $factory->create('255.255.255.255', $this->loop);
+
+ $promise = $this->resolver->resolve('google.com');
+ $promise->then($this->expectCallableNever(), $this->expectCallableOnce());
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Model/MessageTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Model/MessageTest.php
new file mode 100644
index 0000000..53d6b28
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Model/MessageTest.php
@@ -0,0 +1,30 @@
+assertTrue($request->header->isQuery());
+ $this->assertSame(1, $request->header->get('rd'));
+ }
+
+ public function testCreateResponseWithNoAnswers()
+ {
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+ $answers = array();
+ $request = Message::createResponseWithAnswersForQuery($query, $answers);
+
+ $this->assertFalse($request->header->isQuery());
+ $this->assertTrue($request->header->isResponse());
+ $this->assertEquals(0, $request->header->get('anCount'));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Protocol/BinaryDumperTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Protocol/BinaryDumperTest.php
new file mode 100644
index 0000000..bf60ca9
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Protocol/BinaryDumperTest.php
@@ -0,0 +1,48 @@
+formatHexDump(str_replace(' ', '', $data), 2);
+
+ $request = new Message();
+ $request->header->set('id', 0x7262);
+ $request->header->set('rd', 1);
+
+ $request->questions[] = array(
+ 'name' => 'igor.io',
+ 'type' => Message::TYPE_A,
+ 'class' => Message::CLASS_IN,
+ );
+
+ $request->prepare();
+
+ $dumper = new BinaryDumper();
+ $data = $dumper->toBinary($request);
+ $data = $this->convertBinaryToHexDump($data);
+
+ $this->assertSame($expected, $data);
+ }
+
+ private function convertBinaryToHexDump($input)
+ {
+ return $this->formatHexDump(implode('', unpack('H*', $input)));
+ }
+
+ private function formatHexDump($input)
+ {
+ return implode(' ', str_split($input, 2));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Protocol/ParserTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Protocol/ParserTest.php
new file mode 100644
index 0000000..195fad2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Protocol/ParserTest.php
@@ -0,0 +1,343 @@
+parser = new Parser();
+ }
+
+ /**
+ * @dataProvider provideConvertTcpDumpToBinary
+ */
+ public function testConvertTcpDumpToBinary($expected, $data)
+ {
+ $this->assertSame($expected, $this->convertTcpDumpToBinary($data));
+ }
+
+ public function provideConvertTcpDumpToBinary()
+ {
+ return array(
+ array(chr(0x72).chr(0x62), "72 62"),
+ array(chr(0x72).chr(0x62).chr(0x01).chr(0x00), "72 62 01 00"),
+ array(chr(0x72).chr(0x62).chr(0x01).chr(0x00).chr(0x00).chr(0x01), "72 62 01 00 00 01"),
+ array(chr(0x01).chr(0x00).chr(0x01), "01 00 01"),
+ );
+ }
+
+ public function testParseRequest()
+ {
+ $data = "";
+ $data .= "72 62 01 00 00 01 00 00 00 00 00 00"; // header
+ $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io
+ $data .= "00 01 00 01"; // question: type A, class IN
+
+ $data = $this->convertTcpDumpToBinary($data);
+
+ $request = $this->parser->parseMessage($data);
+
+ $header = $request->header;
+ $this->assertSame(0x7262, $header->get('id'));
+ $this->assertSame(1, $header->get('qdCount'));
+ $this->assertSame(0, $header->get('anCount'));
+ $this->assertSame(0, $header->get('nsCount'));
+ $this->assertSame(0, $header->get('arCount'));
+ $this->assertSame(0, $header->get('qr'));
+ $this->assertSame(Message::OPCODE_QUERY, $header->get('opcode'));
+ $this->assertSame(0, $header->get('aa'));
+ $this->assertSame(0, $header->get('tc'));
+ $this->assertSame(1, $header->get('rd'));
+ $this->assertSame(0, $header->get('ra'));
+ $this->assertSame(0, $header->get('z'));
+ $this->assertSame(Message::RCODE_OK, $header->get('rcode'));
+
+ $this->assertCount(1, $request->questions);
+ $this->assertSame('igor.io', $request->questions[0]['name']);
+ $this->assertSame(Message::TYPE_A, $request->questions[0]['type']);
+ $this->assertSame(Message::CLASS_IN, $request->questions[0]['class']);
+ }
+
+ public function testParseResponse()
+ {
+ $data = "";
+ $data .= "72 62 81 80 00 01 00 01 00 00 00 00"; // header
+ $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io
+ $data .= "00 01 00 01"; // question: type A, class IN
+ $data .= "c0 0c"; // answer: offset pointer to igor.io
+ $data .= "00 01 00 01"; // answer: type A, class IN
+ $data .= "00 01 51 80"; // answer: ttl 86400
+ $data .= "00 04"; // answer: rdlength 4
+ $data .= "b2 4f a9 83"; // answer: rdata 178.79.169.131
+
+ $data = $this->convertTcpDumpToBinary($data);
+
+ $response = $this->parser->parseMessage($data);
+
+ $header = $response->header;
+ $this->assertSame(0x7262, $header->get('id'));
+ $this->assertSame(1, $header->get('qdCount'));
+ $this->assertSame(1, $header->get('anCount'));
+ $this->assertSame(0, $header->get('nsCount'));
+ $this->assertSame(0, $header->get('arCount'));
+ $this->assertSame(1, $header->get('qr'));
+ $this->assertSame(Message::OPCODE_QUERY, $header->get('opcode'));
+ $this->assertSame(0, $header->get('aa'));
+ $this->assertSame(0, $header->get('tc'));
+ $this->assertSame(1, $header->get('rd'));
+ $this->assertSame(1, $header->get('ra'));
+ $this->assertSame(0, $header->get('z'));
+ $this->assertSame(Message::RCODE_OK, $header->get('rcode'));
+
+ $this->assertCount(1, $response->questions);
+ $this->assertSame('igor.io', $response->questions[0]['name']);
+ $this->assertSame(Message::TYPE_A, $response->questions[0]['type']);
+ $this->assertSame(Message::CLASS_IN, $response->questions[0]['class']);
+
+ $this->assertCount(1, $response->answers);
+ $this->assertSame('igor.io', $response->answers[0]->name);
+ $this->assertSame(Message::TYPE_A, $response->answers[0]->type);
+ $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
+ $this->assertSame(86400, $response->answers[0]->ttl);
+ $this->assertSame('178.79.169.131', $response->answers[0]->data);
+ }
+
+ public function testParseQuestionWithTwoQuestions()
+ {
+ $data = "";
+ $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io
+ $data .= "00 01 00 01"; // question: type A, class IN
+ $data .= "03 77 77 77 04 69 67 6f 72 02 69 6f 00"; // question: www.igor.io
+ $data .= "00 01 00 01"; // question: type A, class IN
+
+ $data = $this->convertTcpDumpToBinary($data);
+
+ $request = new Message();
+ $request->header->set('qdCount', 2);
+ $request->data = $data;
+
+ $this->parser->parseQuestion($request);
+
+ $this->assertCount(2, $request->questions);
+ $this->assertSame('igor.io', $request->questions[0]['name']);
+ $this->assertSame(Message::TYPE_A, $request->questions[0]['type']);
+ $this->assertSame(Message::CLASS_IN, $request->questions[0]['class']);
+ $this->assertSame('www.igor.io', $request->questions[1]['name']);
+ $this->assertSame(Message::TYPE_A, $request->questions[1]['type']);
+ $this->assertSame(Message::CLASS_IN, $request->questions[1]['class']);
+ }
+
+ public function testParseAnswerWithInlineData()
+ {
+ $data = "";
+ $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
+ $data .= "00 01 00 01"; // answer: type A, class IN
+ $data .= "00 01 51 80"; // answer: ttl 86400
+ $data .= "00 04"; // answer: rdlength 4
+ $data .= "b2 4f a9 83"; // answer: rdata 178.79.169.131
+
+ $data = $this->convertTcpDumpToBinary($data);
+
+ $response = new Message();
+ $response->header->set('anCount', 1);
+ $response->data = $data;
+
+ $this->parser->parseAnswer($response);
+
+ $this->assertCount(1, $response->answers);
+ $this->assertSame('igor.io', $response->answers[0]->name);
+ $this->assertSame(Message::TYPE_A, $response->answers[0]->type);
+ $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
+ $this->assertSame(86400, $response->answers[0]->ttl);
+ $this->assertSame('178.79.169.131', $response->answers[0]->data);
+ }
+
+ public function testParseResponseWithCnameAndOffsetPointers()
+ {
+ $data = "";
+ $data .= "9e 8d 81 80 00 01 00 01 00 00 00 00"; // header
+ $data .= "04 6d 61 69 6c 06 67 6f 6f 67 6c 65 03 63 6f 6d 00"; // question: mail.google.com
+ $data .= "00 05 00 01"; // question: type CNAME, class IN
+ $data .= "c0 0c"; // answer: offset pointer to mail.google.com
+ $data .= "00 05 00 01"; // answer: type CNAME, class IN
+ $data .= "00 00 a8 9c"; // answer: ttl 43164
+ $data .= "00 0f"; // answer: rdlength 15
+ $data .= "0a 67 6f 6f 67 6c 65 6d 61 69 6c 01 6c"; // answer: rdata googlemail.l.
+ $data .= "c0 11"; // answer: rdata offset pointer to google.com
+
+ $data = $this->convertTcpDumpToBinary($data);
+
+ $response = $this->parser->parseMessage($data);
+
+ $this->assertCount(1, $response->questions);
+ $this->assertSame('mail.google.com', $response->questions[0]['name']);
+ $this->assertSame(Message::TYPE_CNAME, $response->questions[0]['type']);
+ $this->assertSame(Message::CLASS_IN, $response->questions[0]['class']);
+
+ $this->assertCount(1, $response->answers);
+ $this->assertSame('mail.google.com', $response->answers[0]->name);
+ $this->assertSame(Message::TYPE_CNAME, $response->answers[0]->type);
+ $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
+ $this->assertSame(43164, $response->answers[0]->ttl);
+ $this->assertSame('googlemail.l.google.com', $response->answers[0]->data);
+ }
+
+ public function testParseAAAAResponse()
+ {
+ $data = "";
+ $data .= "cd 72 81 80 00 01 00 01 00 00 00 00 06"; // header
+ $data .= "67 6f 6f 67 6c 65 03 63 6f 6d 00"; // question: google.com
+ $data .= "00 1c 00 01"; // question: type AAAA, class IN
+ $data .= "c0 0c"; // answer: offset pointer to google.com
+ $data .= "00 1c 00 01"; // answer: type AAAA, class IN
+ $data .= "00 00 01 2b"; // answer: ttl 299
+ $data .= "00 10"; // answer: rdlength 16
+ $data .= "2a 00 14 50 40 09 08 09 00 00 00 00 00 00 20 0e"; // answer: 2a00:1450:4009:809::200e
+
+ $data = $this->convertTcpDumpToBinary($data);
+
+ $response = $this->parser->parseMessage($data);
+
+ $header = $response->header;
+ $this->assertSame(0xcd72, $header->get('id'));
+ $this->assertSame(1, $header->get('qdCount'));
+ $this->assertSame(1, $header->get('anCount'));
+ $this->assertSame(0, $header->get('nsCount'));
+ $this->assertSame(0, $header->get('arCount'));
+ $this->assertSame(1, $header->get('qr'));
+ $this->assertSame(Message::OPCODE_QUERY, $header->get('opcode'));
+ $this->assertSame(0, $header->get('aa'));
+ $this->assertSame(0, $header->get('tc'));
+ $this->assertSame(1, $header->get('rd'));
+ $this->assertSame(1, $header->get('ra'));
+ $this->assertSame(0, $header->get('z'));
+ $this->assertSame(Message::RCODE_OK, $header->get('rcode'));
+
+ $this->assertCount(1, $response->questions);
+ $this->assertSame('google.com', $response->questions[0]['name']);
+ $this->assertSame(Message::TYPE_AAAA, $response->questions[0]['type']);
+ $this->assertSame(Message::CLASS_IN, $response->questions[0]['class']);
+
+ $this->assertCount(1, $response->answers);
+ $this->assertSame('google.com', $response->answers[0]->name);
+ $this->assertSame(Message::TYPE_AAAA, $response->answers[0]->type);
+ $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
+ $this->assertSame(299, $response->answers[0]->ttl);
+ $this->assertSame('2a00:1450:4009:809::200e', $response->answers[0]->data);
+ }
+
+ public function testParseResponseWithTwoAnswers()
+ {
+ $data = "";
+ $data .= "bc 73 81 80 00 01 00 02 00 00 00 00"; // header
+ $data .= "02 69 6f 0d 77 68 6f 69 73 2d 73 65 72 76 65 72 73 03 6e 65 74 00";
+ // question: io.whois-servers.net
+ $data .= "00 01 00 01"; // question: type A, class IN
+ $data .= "c0 0c"; // answer: offset pointer to io.whois-servers.net
+ $data .= "00 05 00 01"; // answer: type CNAME, class IN
+ $data .= "00 00 00 29"; // answer: ttl 41
+ $data .= "00 0e"; // answer: rdlength 14
+ $data .= "05 77 68 6f 69 73 03 6e 69 63 02 69 6f 00"; // answer: rdata whois.nic.io
+ $data .= "c0 32"; // answer: offset pointer to whois.nic.io
+ $data .= "00 01 00 01"; // answer: type CNAME, class IN
+ $data .= "00 00 0d f7"; // answer: ttl 3575
+ $data .= "00 04"; // answer: rdlength 4
+ $data .= "c1 df 4e 98"; // answer: rdata 193.223.78.152
+
+ $data = $this->convertTcpDumpToBinary($data);
+
+ $response = $this->parser->parseMessage($data);
+
+ $this->assertCount(1, $response->questions);
+ $this->assertSame('io.whois-servers.net', $response->questions[0]['name']);
+ $this->assertSame(Message::TYPE_A, $response->questions[0]['type']);
+ $this->assertSame(Message::CLASS_IN, $response->questions[0]['class']);
+
+ $this->assertCount(2, $response->answers);
+
+ $this->assertSame('io.whois-servers.net', $response->answers[0]->name);
+ $this->assertSame(Message::TYPE_CNAME, $response->answers[0]->type);
+ $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
+ $this->assertSame(41, $response->answers[0]->ttl);
+ $this->assertSame('whois.nic.io', $response->answers[0]->data);
+
+ $this->assertSame('whois.nic.io', $response->answers[1]->name);
+ $this->assertSame(Message::TYPE_A, $response->answers[1]->type);
+ $this->assertSame(Message::CLASS_IN, $response->answers[1]->class);
+ $this->assertSame(3575, $response->answers[1]->ttl);
+ $this->assertSame('193.223.78.152', $response->answers[1]->data);
+ }
+
+ public function testParsePTRResponse()
+ {
+ $data = "";
+ $data .= "5d d8 81 80 00 01 00 01 00 00 00 00"; // header
+ $data .= "01 34 01 34 01 38 01 38 07 69 6e"; // question: 4.4.8.8.in-addr.arpa
+ $data .= "2d 61 64 64 72 04 61 72 70 61 00"; // question (continued)
+ $data .= "00 0c 00 01"; // question: type PTR, class IN
+ $data .= "c0 0c"; // answer: offset pointer to rdata
+ $data .= "00 0c 00 01"; // answer: type PTR, class IN
+ $data .= "00 01 51 7f"; // answer: ttl 86399
+ $data .= "00 20"; // answer: rdlength 32
+ $data .= "13 67 6f 6f 67 6c 65 2d 70 75 62 6c 69 63 2d 64"; // answer: rdata google-public-dns-b.google.com.
+ $data .= "6e 73 2d 62 06 67 6f 6f 67 6c 65 03 63 6f 6d 00";
+
+ $data = $this->convertTcpDumpToBinary($data);
+
+ $response = $this->parser->parseMessage($data);
+
+ $header = $response->header;
+ $this->assertSame(0x5dd8, $header->get('id'));
+ $this->assertSame(1, $header->get('qdCount'));
+ $this->assertSame(1, $header->get('anCount'));
+ $this->assertSame(0, $header->get('nsCount'));
+ $this->assertSame(0, $header->get('arCount'));
+ $this->assertSame(1, $header->get('qr'));
+ $this->assertSame(Message::OPCODE_QUERY, $header->get('opcode'));
+ $this->assertSame(0, $header->get('aa'));
+ $this->assertSame(0, $header->get('tc'));
+ $this->assertSame(1, $header->get('rd'));
+ $this->assertSame(1, $header->get('ra'));
+ $this->assertSame(0, $header->get('z'));
+ $this->assertSame(Message::RCODE_OK, $header->get('rcode'));
+
+ $this->assertCount(1, $response->questions);
+ $this->assertSame('4.4.8.8.in-addr.arpa', $response->questions[0]['name']);
+ $this->assertSame(Message::TYPE_PTR, $response->questions[0]['type']);
+ $this->assertSame(Message::CLASS_IN, $response->questions[0]['class']);
+
+ $this->assertCount(1, $response->answers);
+ $this->assertSame('4.4.8.8.in-addr.arpa', $response->answers[0]->name);
+ $this->assertSame(Message::TYPE_PTR, $response->answers[0]->type);
+ $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
+ $this->assertSame(86399, $response->answers[0]->ttl);
+ $this->assertSame('google-public-dns-b.google.com', $response->answers[0]->data);
+ }
+
+ /**
+ * @expectedException InvalidArgumentException
+ */
+ public function testParseIncomplete()
+ {
+ $data = "";
+ $data .= "72 62 01 00 00 01 00 00 00 00 00 00"; // header
+ $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io
+ //$data .= "00 01 00 01"; // question: type A, class IN
+
+ $data = $this->convertTcpDumpToBinary($data);
+
+ $this->parser->parseMessage($data);
+ }
+
+ private function convertTcpDumpToBinary($input)
+ {
+ // sudo ngrep -d en1 -x port 53
+
+ return pack('H*', str_replace(' ', '', $input));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Query/CachedExecutorTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Query/CachedExecutorTest.php
new file mode 100644
index 0000000..d08ed05
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Query/CachedExecutorTest.php
@@ -0,0 +1,100 @@
+createExecutorMock();
+ $executor
+ ->expects($this->once())
+ ->method('query')
+ ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query'))
+ ->will($this->returnValue($this->createPromiseMock()));
+
+ $cache = $this->getMockBuilder('React\Dns\Query\RecordCache')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $cache
+ ->expects($this->once())
+ ->method('lookup')
+ ->will($this->returnValue(Promise\reject()));
+ $cachedExecutor = new CachedExecutor($executor, $cache);
+
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+ $cachedExecutor->query('8.8.8.8', $query);
+ }
+
+ /**
+ * @covers React\Dns\Query\CachedExecutor
+ * @test
+ */
+ public function callingQueryTwiceShouldUseCachedResult()
+ {
+ $cachedRecords = array(new Record('igor.io', Message::TYPE_A, Message::CLASS_IN));
+
+ $executor = $this->createExecutorMock();
+ $executor
+ ->expects($this->once())
+ ->method('query')
+ ->will($this->callQueryCallbackWithAddress('178.79.169.131'));
+
+ $cache = $this->getMockBuilder('React\Dns\Query\RecordCache')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $cache
+ ->expects($this->at(0))
+ ->method('lookup')
+ ->with($this->isInstanceOf('React\Dns\Query\Query'))
+ ->will($this->returnValue(Promise\reject()));
+ $cache
+ ->expects($this->at(1))
+ ->method('storeResponseMessage')
+ ->with($this->isType('integer'), $this->isInstanceOf('React\Dns\Model\Message'));
+ $cache
+ ->expects($this->at(2))
+ ->method('lookup')
+ ->with($this->isInstanceOf('React\Dns\Query\Query'))
+ ->will($this->returnValue(Promise\resolve($cachedRecords)));
+
+ $cachedExecutor = new CachedExecutor($executor, $cache);
+
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+ $cachedExecutor->query('8.8.8.8', $query, function () {}, function () {});
+ $cachedExecutor->query('8.8.8.8', $query, function () {}, function () {});
+ }
+
+ private function callQueryCallbackWithAddress($address)
+ {
+ return $this->returnCallback(function ($nameserver, $query) use ($address) {
+ $response = new Message();
+ $response->header->set('qr', 1);
+ $response->questions[] = new Record($query->name, $query->type, $query->class);
+ $response->answers[] = new Record($query->name, $query->type, $query->class, 3600, $address);
+
+ return Promise\resolve($response);
+ });
+ }
+
+ private function createExecutorMock()
+ {
+ return $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock();
+ }
+
+ private function createPromiseMock()
+ {
+ return $this->getMockBuilder('React\Promise\PromiseInterface')->getMock();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Query/ExecutorTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Query/ExecutorTest.php
new file mode 100644
index 0000000..0d7ac1d
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Query/ExecutorTest.php
@@ -0,0 +1,308 @@
+loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $this->parser = $this->getMockBuilder('React\Dns\Protocol\Parser')->getMock();
+ $this->dumper = new BinaryDumper();
+
+ $this->executor = new Executor($this->loop, $this->parser, $this->dumper);
+ }
+
+ /** @test */
+ public function queryShouldCreateUdpRequest()
+ {
+ $timer = $this->createTimerMock();
+ $this->loop
+ ->expects($this->any())
+ ->method('addTimer')
+ ->will($this->returnValue($timer));
+
+ $this->executor = $this->createExecutorMock();
+ $this->executor
+ ->expects($this->once())
+ ->method('createConnection')
+ ->with('8.8.8.8:53', 'udp')
+ ->will($this->returnNewConnectionMock(false));
+
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+ $this->executor->query('8.8.8.8:53', $query);
+ }
+
+ /** @test */
+ public function resolveShouldRejectIfRequestIsLargerThan512Bytes()
+ {
+ $query = new Query(str_repeat('a', 512).'.igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+ $promise = $this->executor->query('8.8.8.8:53', $query);
+
+ $this->setExpectedException('RuntimeException', 'DNS query for ' . $query->name . ' failed: Requested transport "tcp" not available, only UDP is supported in this version');
+ Block\await($promise, $this->loop);
+ }
+
+ /** @test */
+ public function resolveShouldCloseConnectionWhenCancelled()
+ {
+ $conn = $this->createConnectionMock(false);
+ $conn->expects($this->once())->method('close');
+
+ $timer = $this->createTimerMock();
+ $this->loop
+ ->expects($this->any())
+ ->method('addTimer')
+ ->will($this->returnValue($timer));
+
+ $this->executor = $this->createExecutorMock();
+ $this->executor
+ ->expects($this->once())
+ ->method('createConnection')
+ ->with('8.8.8.8:53', 'udp')
+ ->will($this->returnValue($conn));
+
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+ $promise = $this->executor->query('8.8.8.8:53', $query);
+
+ $promise->cancel();
+
+ $this->setExpectedException('React\Dns\Query\CancellationException', 'DNS query for igor.io has been cancelled');
+ Block\await($promise, $this->loop);
+ }
+
+ /** @test */
+ public function resolveShouldNotStartOrCancelTimerWhenCancelledWithTimeoutIsNull()
+ {
+ $this->loop
+ ->expects($this->never())
+ ->method('addTimer');
+
+ $this->executor = new Executor($this->loop, $this->parser, $this->dumper, null);
+
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+ $promise = $this->executor->query('127.0.0.1:53', $query);
+
+ $promise->cancel();
+
+ $this->setExpectedException('React\Dns\Query\CancellationException', 'DNS query for igor.io has been cancelled');
+ Block\await($promise, $this->loop);
+ }
+
+ /** @test */
+ public function resolveShouldRejectIfResponseIsTruncated()
+ {
+ $timer = $this->createTimerMock();
+
+ $this->loop
+ ->expects($this->any())
+ ->method('addTimer')
+ ->will($this->returnValue($timer));
+
+ $this->parser
+ ->expects($this->once())
+ ->method('parseMessage')
+ ->will($this->returnTruncatedResponse());
+
+ $this->executor = $this->createExecutorMock();
+ $this->executor
+ ->expects($this->once())
+ ->method('createConnection')
+ ->with('8.8.8.8:53', 'udp')
+ ->will($this->returnNewConnectionMock());
+
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+ $this->executor->query('8.8.8.8:53', $query);
+ }
+
+ /** @test */
+ public function resolveShouldFailIfUdpThrow()
+ {
+ $this->loop
+ ->expects($this->never())
+ ->method('addTimer');
+
+ $this->parser
+ ->expects($this->never())
+ ->method('parseMessage');
+
+ $this->executor = $this->createExecutorMock();
+ $this->executor
+ ->expects($this->once())
+ ->method('createConnection')
+ ->with('8.8.8.8:53', 'udp')
+ ->will($this->throwException(new \Exception('Nope')));
+
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+ $promise = $this->executor->query('8.8.8.8:53', $query);
+
+ $this->setExpectedException('RuntimeException', 'DNS query for igor.io failed: Nope');
+ Block\await($promise, $this->loop);
+ }
+
+ /** @test */
+ public function resolveShouldCancelTimerWhenFullResponseIsReceived()
+ {
+ $conn = $this->createConnectionMock();
+
+ $this->parser
+ ->expects($this->once())
+ ->method('parseMessage')
+ ->will($this->returnStandardResponse());
+
+ $this->executor = $this->createExecutorMock();
+ $this->executor
+ ->expects($this->at(0))
+ ->method('createConnection')
+ ->with('8.8.8.8:53', 'udp')
+ ->will($this->returnNewConnectionMock());
+
+
+ $timer = $this->createTimerMock();
+
+ $this->loop
+ ->expects($this->once())
+ ->method('addTimer')
+ ->with(5, $this->isInstanceOf('Closure'))
+ ->will($this->returnValue($timer));
+
+ $this->loop
+ ->expects($this->once())
+ ->method('cancelTimer')
+ ->with($timer);
+
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+ $this->executor->query('8.8.8.8:53', $query);
+ }
+
+ /** @test */
+ public function resolveShouldCloseConnectionOnTimeout()
+ {
+ $this->executor = $this->createExecutorMock();
+ $this->executor
+ ->expects($this->at(0))
+ ->method('createConnection')
+ ->with('8.8.8.8:53', 'udp')
+ ->will($this->returnNewConnectionMock(false));
+
+ $timer = $this->createTimerMock();
+
+ $this->loop
+ ->expects($this->never())
+ ->method('cancelTimer');
+
+ $this->loop
+ ->expects($this->once())
+ ->method('addTimer')
+ ->with(5, $this->isInstanceOf('Closure'))
+ ->will($this->returnCallback(function ($time, $callback) use (&$timerCallback, $timer) {
+ $timerCallback = $callback;
+ return $timer;
+ }));
+
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+ $promise = $this->executor->query('8.8.8.8:53', $query);
+
+ $this->assertNotNull($timerCallback);
+ $timerCallback();
+
+ $this->setExpectedException('React\Dns\Query\TimeoutException', 'DNS query for igor.io timed out');
+ Block\await($promise, $this->loop);
+ }
+
+ private function returnStandardResponse()
+ {
+ $that = $this;
+ $callback = function ($data) use ($that) {
+ $response = new Message();
+ $that->convertMessageToStandardResponse($response);
+ return $response;
+ };
+
+ return $this->returnCallback($callback);
+ }
+
+ private function returnTruncatedResponse()
+ {
+ $that = $this;
+ $callback = function ($data) use ($that) {
+ $response = new Message();
+ $that->convertMessageToTruncatedResponse($response);
+ return $response;
+ };
+
+ return $this->returnCallback($callback);
+ }
+
+ public function convertMessageToStandardResponse(Message $response)
+ {
+ $response->header->set('qr', 1);
+ $response->questions[] = new Record('igor.io', Message::TYPE_A, Message::CLASS_IN);
+ $response->answers[] = new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131');
+ $response->prepare();
+
+ return $response;
+ }
+
+ public function convertMessageToTruncatedResponse(Message $response)
+ {
+ $this->convertMessageToStandardResponse($response);
+ $response->header->set('tc', 1);
+ $response->prepare();
+
+ return $response;
+ }
+
+ private function returnNewConnectionMock($emitData = true)
+ {
+ $conn = $this->createConnectionMock($emitData);
+
+ $callback = function () use ($conn) {
+ return $conn;
+ };
+
+ return $this->returnCallback($callback);
+ }
+
+ private function createConnectionMock($emitData = true)
+ {
+ $conn = $this->getMockBuilder('React\Stream\DuplexStreamInterface')->getMock();
+ $conn
+ ->expects($this->any())
+ ->method('on')
+ ->with('data', $this->isInstanceOf('Closure'))
+ ->will($this->returnCallback(function ($name, $callback) use ($emitData) {
+ $emitData && $callback(null);
+ }));
+
+ return $conn;
+ }
+
+ private function createTimerMock()
+ {
+ return $this->getMockBuilder(
+ interface_exists('React\EventLoop\TimerInterface') ? 'React\EventLoop\TimerInterface' : 'React\EventLoop\Timer\TimerInterface'
+ )->getMock();
+ }
+
+ private function createExecutorMock()
+ {
+ return $this->getMockBuilder('React\Dns\Query\Executor')
+ ->setConstructorArgs(array($this->loop, $this->parser, $this->dumper))
+ ->setMethods(array('createConnection'))
+ ->getMock();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Query/HostsFileExecutorTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Query/HostsFileExecutorTest.php
new file mode 100644
index 0000000..70d877e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Query/HostsFileExecutorTest.php
@@ -0,0 +1,126 @@
+hosts = $this->getMockBuilder('React\Dns\Config\HostsFile')->disableOriginalConstructor()->getMock();
+ $this->fallback = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock();
+ $this->executor = new HostsFileExecutor($this->hosts, $this->fallback);
+ }
+
+ public function testDoesNotTryToGetIpsForMxQuery()
+ {
+ $this->hosts->expects($this->never())->method('getIpsForHost');
+ $this->fallback->expects($this->once())->method('query');
+
+ $this->executor->query('8.8.8.8', new Query('google.com', Message::TYPE_MX, Message::CLASS_IN, 0));
+ }
+
+ public function testFallsBackIfNoIpsWereFound()
+ {
+ $this->hosts->expects($this->once())->method('getIpsForHost')->willReturn(array());
+ $this->fallback->expects($this->once())->method('query');
+
+ $this->executor->query('8.8.8.8', new Query('google.com', Message::TYPE_A, Message::CLASS_IN, 0));
+ }
+
+ public function testReturnsResponseMessageIfIpsWereFound()
+ {
+ $this->hosts->expects($this->once())->method('getIpsForHost')->willReturn(array('127.0.0.1'));
+ $this->fallback->expects($this->never())->method('query');
+
+ $ret = $this->executor->query('8.8.8.8', new Query('google.com', Message::TYPE_A, Message::CLASS_IN, 0));
+ }
+
+ public function testFallsBackIfNoIpv4Matches()
+ {
+ $this->hosts->expects($this->once())->method('getIpsForHost')->willReturn(array('::1'));
+ $this->fallback->expects($this->once())->method('query');
+
+ $ret = $this->executor->query('8.8.8.8', new Query('google.com', Message::TYPE_A, Message::CLASS_IN, 0));
+ }
+
+ public function testReturnsResponseMessageIfIpv6AddressesWereFound()
+ {
+ $this->hosts->expects($this->once())->method('getIpsForHost')->willReturn(array('::1'));
+ $this->fallback->expects($this->never())->method('query');
+
+ $ret = $this->executor->query('8.8.8.8', new Query('google.com', Message::TYPE_AAAA, Message::CLASS_IN, 0));
+ }
+
+ public function testFallsBackIfNoIpv6Matches()
+ {
+ $this->hosts->expects($this->once())->method('getIpsForHost')->willReturn(array('127.0.0.1'));
+ $this->fallback->expects($this->once())->method('query');
+
+ $ret = $this->executor->query('8.8.8.8', new Query('google.com', Message::TYPE_AAAA, Message::CLASS_IN, 0));
+ }
+
+ public function testDoesReturnReverseIpv4Lookup()
+ {
+ $this->hosts->expects($this->once())->method('getHostsForIp')->with('127.0.0.1')->willReturn(array('localhost'));
+ $this->fallback->expects($this->never())->method('query');
+
+ $this->executor->query('8.8.8.8', new Query('1.0.0.127.in-addr.arpa', Message::TYPE_PTR, Message::CLASS_IN, 0));
+ }
+
+ public function testFallsBackIfNoReverseIpv4Matches()
+ {
+ $this->hosts->expects($this->once())->method('getHostsForIp')->with('127.0.0.1')->willReturn(array());
+ $this->fallback->expects($this->once())->method('query');
+
+ $this->executor->query('8.8.8.8', new Query('1.0.0.127.in-addr.arpa', Message::TYPE_PTR, Message::CLASS_IN, 0));
+ }
+
+ public function testDoesReturnReverseIpv6Lookup()
+ {
+ $this->hosts->expects($this->once())->method('getHostsForIp')->with('2a02:2e0:3fe:100::6')->willReturn(array('ip6-localhost'));
+ $this->fallback->expects($this->never())->method('query');
+
+ $this->executor->query('8.8.8.8', new Query('6.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.e.f.3.0.0.e.2.0.2.0.a.2.ip6.arpa', Message::TYPE_PTR, Message::CLASS_IN, 0));
+ }
+
+ public function testFallsBackForInvalidAddress()
+ {
+ $this->hosts->expects($this->never())->method('getHostsForIp');
+ $this->fallback->expects($this->once())->method('query');
+
+ $this->executor->query('8.8.8.8', new Query('example.com', Message::TYPE_PTR, Message::CLASS_IN, 0));
+ }
+
+ public function testReverseFallsBackForInvalidIpv4Address()
+ {
+ $this->hosts->expects($this->never())->method('getHostsForIp');
+ $this->fallback->expects($this->once())->method('query');
+
+ $this->executor->query('8.8.8.8', new Query('::1.in-addr.arpa', Message::TYPE_PTR, Message::CLASS_IN, 0));
+ }
+
+ public function testReverseFallsBackForInvalidLengthIpv6Address()
+ {
+ $this->hosts->expects($this->never())->method('getHostsForIp');
+ $this->fallback->expects($this->once())->method('query');
+
+ $this->executor->query('8.8.8.8', new Query('abcd.ip6.arpa', Message::TYPE_PTR, Message::CLASS_IN, 0));
+ }
+
+ public function testReverseFallsBackForInvalidHexIpv6Address()
+ {
+ $this->hosts->expects($this->never())->method('getHostsForIp');
+ $this->fallback->expects($this->once())->method('query');
+
+ $this->executor->query('8.8.8.8', new Query('zZz.ip6.arpa', Message::TYPE_PTR, Message::CLASS_IN, 0));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Query/RecordBagTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Query/RecordBagTest.php
new file mode 100644
index 0000000..83b8934
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Query/RecordBagTest.php
@@ -0,0 +1,64 @@
+assertSame(array(), $recordBag->all());
+ }
+
+ /**
+ * @covers React\Dns\Query\RecordBag
+ * @test
+ */
+ public function setShouldSetTheValue()
+ {
+ $currentTime = 1345656451;
+
+ $recordBag = new RecordBag();
+ $recordBag->set($currentTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600));
+
+ $records = $recordBag->all();
+ $this->assertCount(1, $records);
+ $this->assertSame('igor.io', $records[0]->name);
+ $this->assertSame(Message::TYPE_A, $records[0]->type);
+ $this->assertSame(Message::CLASS_IN, $records[0]->class);
+ }
+
+ /**
+ * @covers React\Dns\Query\RecordBag
+ * @test
+ */
+ public function setShouldSetManyValues()
+ {
+ $currentTime = 1345656451;
+
+ $recordBag = new RecordBag();
+ $recordBag->set($currentTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'));
+ $recordBag->set($currentTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.132'));
+
+ $records = $recordBag->all();
+ $this->assertCount(2, $records);
+ $this->assertSame('igor.io', $records[0]->name);
+ $this->assertSame(Message::TYPE_A, $records[0]->type);
+ $this->assertSame(Message::CLASS_IN, $records[0]->class);
+ $this->assertSame('178.79.169.131', $records[0]->data);
+ $this->assertSame('igor.io', $records[1]->name);
+ $this->assertSame(Message::TYPE_A, $records[1]->type);
+ $this->assertSame(Message::CLASS_IN, $records[1]->class);
+ $this->assertSame('178.79.169.132', $records[1]->data);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Query/RecordCacheTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Query/RecordCacheTest.php
new file mode 100644
index 0000000..399fbe8
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Query/RecordCacheTest.php
@@ -0,0 +1,123 @@
+lookup($query);
+
+ $this->assertInstanceOf('React\Promise\RejectedPromise', $promise);
+ }
+
+ /**
+ * @covers React\Dns\Query\RecordCache
+ * @test
+ */
+ public function storeRecordShouldMakeLookupSucceed()
+ {
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+
+ $cache = new RecordCache(new ArrayCache());
+ $cache->storeRecord($query->currentTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'));
+ $promise = $cache->lookup($query);
+
+ $this->assertInstanceOf('React\Promise\FulfilledPromise', $promise);
+ $cachedRecords = $this->getPromiseValue($promise);
+
+ $this->assertCount(1, $cachedRecords);
+ $this->assertSame('178.79.169.131', $cachedRecords[0]->data);
+ }
+
+ /**
+ * @covers React\Dns\Query\RecordCache
+ * @test
+ */
+ public function storeTwoRecordsShouldReturnBoth()
+ {
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+
+ $cache = new RecordCache(new ArrayCache());
+ $cache->storeRecord($query->currentTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'));
+ $cache->storeRecord($query->currentTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.132'));
+ $promise = $cache->lookup($query);
+
+ $this->assertInstanceOf('React\Promise\FulfilledPromise', $promise);
+ $cachedRecords = $this->getPromiseValue($promise);
+
+ $this->assertCount(2, $cachedRecords);
+ $this->assertSame('178.79.169.131', $cachedRecords[0]->data);
+ $this->assertSame('178.79.169.132', $cachedRecords[1]->data);
+ }
+
+ /**
+ * @covers React\Dns\Query\RecordCache
+ * @test
+ */
+ public function storeResponseMessageShouldStoreAllAnswerValues()
+ {
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+
+ $response = new Message();
+ $response->answers[] = new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131');
+ $response->answers[] = new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.132');
+ $response->prepare();
+
+ $cache = new RecordCache(new ArrayCache());
+ $cache->storeResponseMessage($query->currentTime, $response);
+ $promise = $cache->lookup($query);
+
+ $this->assertInstanceOf('React\Promise\FulfilledPromise', $promise);
+ $cachedRecords = $this->getPromiseValue($promise);
+
+ $this->assertCount(2, $cachedRecords);
+ $this->assertSame('178.79.169.131', $cachedRecords[0]->data);
+ $this->assertSame('178.79.169.132', $cachedRecords[1]->data);
+ }
+
+ /**
+ * @covers React\Dns\Query\RecordCache
+ * @test
+ */
+ public function expireShouldExpireDeadRecords()
+ {
+ $cachedTime = 1345656451;
+ $currentTime = $cachedTime + 3605;
+
+ $cache = new RecordCache(new ArrayCache());
+ $cache->storeRecord($cachedTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'));
+ $cache->expire($currentTime);
+
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, $currentTime);
+ $promise = $cache->lookup($query);
+
+ $this->assertInstanceOf('React\Promise\RejectedPromise', $promise);
+ }
+
+ private function getPromiseValue(PromiseInterface $promise)
+ {
+ $capturedValue = null;
+
+ $promise->then(function ($value) use (&$capturedValue) {
+ $capturedValue = $value;
+ });
+
+ return $capturedValue;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Query/RetryExecutorTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Query/RetryExecutorTest.php
new file mode 100644
index 0000000..8950f84
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Query/RetryExecutorTest.php
@@ -0,0 +1,197 @@
+createExecutorMock();
+ $executor
+ ->expects($this->once())
+ ->method('query')
+ ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query'))
+ ->will($this->returnValue($this->expectPromiseOnce()));
+
+ $retryExecutor = new RetryExecutor($executor, 2);
+
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+ $retryExecutor->query('8.8.8.8', $query);
+ }
+
+ /**
+ * @covers React\Dns\Query\RetryExecutor
+ * @test
+ */
+ public function queryShouldRetryQueryOnTimeout()
+ {
+ $response = $this->createStandardResponse();
+
+ $executor = $this->createExecutorMock();
+ $executor
+ ->expects($this->exactly(2))
+ ->method('query')
+ ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query'))
+ ->will($this->onConsecutiveCalls(
+ $this->returnCallback(function ($domain, $query) {
+ return Promise\reject(new TimeoutException("timeout"));
+ }),
+ $this->returnCallback(function ($domain, $query) use ($response) {
+ return Promise\resolve($response);
+ })
+ ));
+
+ $callback = $this->createCallableMock();
+ $callback
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->isInstanceOf('React\Dns\Model\Message'));
+
+ $errorback = $this->expectCallableNever();
+
+ $retryExecutor = new RetryExecutor($executor, 2);
+
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+ $retryExecutor->query('8.8.8.8', $query)->then($callback, $errorback);
+ }
+
+ /**
+ * @covers React\Dns\Query\RetryExecutor
+ * @test
+ */
+ public function queryShouldStopRetryingAfterSomeAttempts()
+ {
+ $executor = $this->createExecutorMock();
+ $executor
+ ->expects($this->exactly(3))
+ ->method('query')
+ ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query'))
+ ->will($this->returnCallback(function ($domain, $query) {
+ return Promise\reject(new TimeoutException("timeout"));
+ }));
+
+ $callback = $this->expectCallableNever();
+
+ $errorback = $this->createCallableMock();
+ $errorback
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->isInstanceOf('RuntimeException'));
+
+ $retryExecutor = new RetryExecutor($executor, 2);
+
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+ $retryExecutor->query('8.8.8.8', $query)->then($callback, $errorback);
+ }
+
+ /**
+ * @covers React\Dns\Query\RetryExecutor
+ * @test
+ */
+ public function queryShouldForwardNonTimeoutErrors()
+ {
+ $executor = $this->createExecutorMock();
+ $executor
+ ->expects($this->once())
+ ->method('query')
+ ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query'))
+ ->will($this->returnCallback(function ($domain, $query) {
+ return Promise\reject(new \Exception);
+ }));
+
+ $callback = $this->expectCallableNever();
+
+ $errorback = $this->createCallableMock();
+ $errorback
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->isInstanceOf('Exception'));
+
+ $retryExecutor = new RetryExecutor($executor, 2);
+
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+ $retryExecutor->query('8.8.8.8', $query)->then($callback, $errorback);
+ }
+
+ /**
+ * @covers React\Dns\Query\RetryExecutor
+ * @test
+ */
+ public function queryShouldCancelQueryOnCancel()
+ {
+ $cancelled = 0;
+
+ $executor = $this->createExecutorMock();
+ $executor
+ ->expects($this->once())
+ ->method('query')
+ ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query'))
+ ->will($this->returnCallback(function ($domain, $query) use (&$cancelled) {
+ $deferred = new Deferred(function ($resolve, $reject) use (&$cancelled) {
+ ++$cancelled;
+ $reject(new CancellationException('Cancelled'));
+ });
+
+ return $deferred->promise();
+ })
+ );
+
+ $retryExecutor = new RetryExecutor($executor, 2);
+
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+ $promise = $retryExecutor->query('8.8.8.8', $query);
+
+ $promise->then($this->expectCallableNever(), $this->expectCallableOnce());
+
+ $this->assertEquals(0, $cancelled);
+ $promise->cancel();
+ $this->assertEquals(1, $cancelled);
+ }
+
+ protected function expectPromiseOnce($return = null)
+ {
+ $mock = $this->createPromiseMock();
+ $mock
+ ->expects($this->once())
+ ->method('then')
+ ->will($this->returnValue($return));
+
+ return $mock;
+ }
+
+ protected function createExecutorMock()
+ {
+ return $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock();
+ }
+
+ protected function createPromiseMock()
+ {
+ return $this->getMockBuilder('React\Promise\PromiseInterface')->getMock();
+ }
+
+ protected function createStandardResponse()
+ {
+ $response = new Message();
+ $response->header->set('qr', 1);
+ $response->questions[] = new Record('igor.io', Message::TYPE_A, Message::CLASS_IN);
+ $response->answers[] = new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131');
+ $response->prepare();
+
+ return $response;
+ }
+}
+
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Query/TimeoutExecutorTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Query/TimeoutExecutorTest.php
new file mode 100644
index 0000000..0d37fb4
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Query/TimeoutExecutorTest.php
@@ -0,0 +1,115 @@
+loop = Factory::create();
+
+ $this->wrapped = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock();
+
+ $this->executor = new TimeoutExecutor($this->wrapped, 5.0, $this->loop);
+ }
+
+ public function testCancellingPromiseWillCancelWrapped()
+ {
+ $cancelled = 0;
+
+ $this->wrapped
+ ->expects($this->once())
+ ->method('query')
+ ->will($this->returnCallback(function ($domain, $query) use (&$cancelled) {
+ $deferred = new Deferred(function ($resolve, $reject) use (&$cancelled) {
+ ++$cancelled;
+ $reject(new CancellationException('Cancelled'));
+ });
+
+ return $deferred->promise();
+ }));
+
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+ $promise = $this->executor->query('8.8.8.8:53', $query);
+
+ $this->assertEquals(0, $cancelled);
+ $promise->cancel();
+ $this->assertEquals(1, $cancelled);
+
+ $promise->then($this->expectCallableNever(), $this->expectCallableOnce());
+ }
+
+ public function testResolvesPromiseWhenWrappedResolves()
+ {
+ $this->wrapped
+ ->expects($this->once())
+ ->method('query')
+ ->willReturn(Promise\resolve('0.0.0.0'));
+
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+ $promise = $this->executor->query('8.8.8.8:53', $query);
+
+ $promise->then($this->expectCallableOnce(), $this->expectCallableNever());
+ }
+
+ public function testRejectsPromiseWhenWrappedRejects()
+ {
+ $this->wrapped
+ ->expects($this->once())
+ ->method('query')
+ ->willReturn(Promise\reject(new \RuntimeException()));
+
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+ $promise = $this->executor->query('8.8.8.8:53', $query);
+
+ $promise->then($this->expectCallableNever(), $this->expectCallableOnceWith(new \RuntimeException()));
+ }
+
+ public function testWrappedWillBeCancelledOnTimeout()
+ {
+ $this->executor = new TimeoutExecutor($this->wrapped, 0, $this->loop);
+
+ $cancelled = 0;
+
+ $this->wrapped
+ ->expects($this->once())
+ ->method('query')
+ ->will($this->returnCallback(function ($domain, $query) use (&$cancelled) {
+ $deferred = new Deferred(function ($resolve, $reject) use (&$cancelled) {
+ ++$cancelled;
+ $reject(new CancellationException('Cancelled'));
+ });
+
+ return $deferred->promise();
+ }));
+
+ $callback = $this->expectCallableNever();
+
+ $errorback = $this->createCallableMock();
+ $errorback
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->logicalAnd(
+ $this->isInstanceOf('React\Dns\Query\TimeoutException'),
+ $this->attribute($this->equalTo('DNS query for igor.io timed out'), 'message')
+ ));
+
+ $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451);
+ $this->executor->query('8.8.8.8:53', $query)->then($callback, $errorback);
+
+ $this->assertEquals(0, $cancelled);
+
+ $this->loop->run();
+
+ $this->assertEquals(1, $cancelled);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Resolver/FactoryTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Resolver/FactoryTest.php
new file mode 100644
index 0000000..acaeac0
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Resolver/FactoryTest.php
@@ -0,0 +1,131 @@
+getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $factory = new Factory();
+ $resolver = $factory->create('8.8.8.8:53', $loop);
+
+ $this->assertInstanceOf('React\Dns\Resolver\Resolver', $resolver);
+ }
+
+ /** @test */
+ public function createWithoutPortShouldCreateResolverWithDefaultPort()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $factory = new Factory();
+ $resolver = $factory->create('8.8.8.8', $loop);
+
+ $this->assertInstanceOf('React\Dns\Resolver\Resolver', $resolver);
+ $this->assertSame('8.8.8.8:53', $this->getResolverPrivateMemberValue($resolver, 'nameserver'));
+ }
+
+ /** @test */
+ public function createCachedShouldCreateResolverWithCachedExecutor()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $factory = new Factory();
+ $resolver = $factory->createCached('8.8.8.8:53', $loop);
+
+ $this->assertInstanceOf('React\Dns\Resolver\Resolver', $resolver);
+ $executor = $this->getResolverPrivateExecutor($resolver);
+ $this->assertInstanceOf('React\Dns\Query\CachedExecutor', $executor);
+ $recordCache = $this->getCachedExecutorPrivateMemberValue($executor, 'cache');
+ $recordCacheCache = $this->getRecordCachePrivateMemberValue($recordCache, 'cache');
+ $this->assertInstanceOf('React\Cache\CacheInterface', $recordCacheCache);
+ $this->assertInstanceOf('React\Cache\ArrayCache', $recordCacheCache);
+ }
+
+ /** @test */
+ public function createCachedShouldCreateResolverWithCachedExecutorWithCustomCache()
+ {
+ $cache = $this->getMockBuilder('React\Cache\CacheInterface')->getMock();
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $factory = new Factory();
+ $resolver = $factory->createCached('8.8.8.8:53', $loop, $cache);
+
+ $this->assertInstanceOf('React\Dns\Resolver\Resolver', $resolver);
+ $executor = $this->getResolverPrivateExecutor($resolver);
+ $this->assertInstanceOf('React\Dns\Query\CachedExecutor', $executor);
+ $recordCache = $this->getCachedExecutorPrivateMemberValue($executor, 'cache');
+ $recordCacheCache = $this->getRecordCachePrivateMemberValue($recordCache, 'cache');
+ $this->assertInstanceOf('React\Cache\CacheInterface', $recordCacheCache);
+ $this->assertSame($cache, $recordCacheCache);
+ }
+
+ /**
+ * @test
+ * @dataProvider factoryShouldAddDefaultPortProvider
+ */
+ public function factoryShouldAddDefaultPort($input, $expected)
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $factory = new Factory();
+ $resolver = $factory->create($input, $loop);
+
+ $this->assertInstanceOf('React\Dns\Resolver\Resolver', $resolver);
+ $this->assertSame($expected, $this->getResolverPrivateMemberValue($resolver, 'nameserver'));
+ }
+
+ public static function factoryShouldAddDefaultPortProvider()
+ {
+ return array(
+ array('8.8.8.8', '8.8.8.8:53'),
+ array('1.2.3.4:5', '1.2.3.4:5'),
+ array('localhost', 'localhost:53'),
+ array('localhost:1234', 'localhost:1234'),
+ array('::1', '[::1]:53'),
+ array('[::1]:53', '[::1]:53')
+ );
+ }
+
+ private function getResolverPrivateExecutor($resolver)
+ {
+ $executor = $this->getResolverPrivateMemberValue($resolver, 'executor');
+
+ // extract underlying executor that may be wrapped in multiple layers of hosts file executors
+ while ($executor instanceof HostsFileExecutor) {
+ $reflector = new \ReflectionProperty('React\Dns\Query\HostsFileExecutor', 'fallback');
+ $reflector->setAccessible(true);
+
+ $executor = $reflector->getValue($executor);
+ }
+
+ return $executor;
+ }
+
+ private function getResolverPrivateMemberValue($resolver, $field)
+ {
+ $reflector = new \ReflectionProperty('React\Dns\Resolver\Resolver', $field);
+ $reflector->setAccessible(true);
+ return $reflector->getValue($resolver);
+ }
+
+ private function getCachedExecutorPrivateMemberValue($resolver, $field)
+ {
+ $reflector = new \ReflectionProperty('React\Dns\Query\CachedExecutor', $field);
+ $reflector->setAccessible(true);
+ return $reflector->getValue($resolver);
+ }
+
+ private function getRecordCachePrivateMemberValue($resolver, $field)
+ {
+ $reflector = new \ReflectionProperty('React\Dns\Query\RecordCache', $field);
+ $reflector->setAccessible(true);
+ return $reflector->getValue($resolver);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Resolver/ResolveAliasesTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Resolver/ResolveAliasesTest.php
new file mode 100644
index 0000000..b5175e3
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Resolver/ResolveAliasesTest.php
@@ -0,0 +1,100 @@
+createExecutorMock();
+ $resolver = new Resolver('8.8.8.8:53', $executor);
+
+ $answers = $resolver->resolveAliases($answers, $name);
+
+ $this->assertEquals($expectedAnswers, $answers);
+ }
+
+ public function provideAliasedAnswers()
+ {
+ return array(
+ array(
+ array('178.79.169.131'),
+ array(
+ new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'),
+ ),
+ 'igor.io',
+ ),
+ array(
+ array('178.79.169.131', '178.79.169.132', '178.79.169.133'),
+ array(
+ new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'),
+ new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.132'),
+ new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.133'),
+ ),
+ 'igor.io',
+ ),
+ array(
+ array('178.79.169.131'),
+ array(
+ new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'),
+ new Record('foo.igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'),
+ new Record('bar.igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'),
+ ),
+ 'igor.io',
+ ),
+ array(
+ array(),
+ array(
+ new Record('foo.igor.io', Message::TYPE_A, Message::CLASS_IN),
+ new Record('bar.igor.io', Message::TYPE_A, Message::CLASS_IN),
+ ),
+ 'igor.io',
+ ),
+ array(
+ array('178.79.169.131'),
+ array(
+ new Record('igor.io', Message::TYPE_CNAME, Message::CLASS_IN, 3600, 'foo.igor.io'),
+ new Record('foo.igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'),
+ ),
+ 'igor.io',
+ ),
+ array(
+ array('178.79.169.131'),
+ array(
+ new Record('igor.io', Message::TYPE_CNAME, Message::CLASS_IN, 3600, 'foo.igor.io'),
+ new Record('foo.igor.io', Message::TYPE_CNAME, Message::CLASS_IN, 3600, 'bar.igor.io'),
+ new Record('bar.igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'),
+ ),
+ 'igor.io',
+ ),
+ array(
+ array('178.79.169.131', '178.79.169.132', '178.79.169.133'),
+ array(
+ new Record('igor.io', Message::TYPE_CNAME, Message::CLASS_IN, 3600, 'foo.igor.io'),
+ new Record('foo.igor.io', Message::TYPE_CNAME, Message::CLASS_IN, 3600, 'bar.igor.io'),
+ new Record('bar.igor.io', Message::TYPE_CNAME, Message::CLASS_IN, 3600, 'baz.igor.io'),
+ new Record('bar.igor.io', Message::TYPE_CNAME, Message::CLASS_IN, 3600, 'qux.igor.io'),
+ new Record('baz.igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'),
+ new Record('baz.igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.132'),
+ new Record('qux.igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.133'),
+ ),
+ 'igor.io',
+ ),
+ );
+ }
+
+ private function createExecutorMock()
+ {
+ return $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Resolver/ResolverTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Resolver/ResolverTest.php
new file mode 100644
index 0000000..e11509b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/Resolver/ResolverTest.php
@@ -0,0 +1,129 @@
+createExecutorMock();
+ $executor
+ ->expects($this->once())
+ ->method('query')
+ ->with($this->anything(), $this->isInstanceOf('React\Dns\Query\Query'))
+ ->will($this->returnCallback(function ($nameserver, $query) {
+ $response = new Message();
+ $response->header->set('qr', 1);
+ $response->questions[] = new Record($query->name, $query->type, $query->class);
+ $response->answers[] = new Record($query->name, $query->type, $query->class, 3600, '178.79.169.131');
+
+ return Promise\resolve($response);
+ }));
+
+ $resolver = new Resolver('8.8.8.8:53', $executor);
+ $resolver->resolve('igor.io')->then($this->expectCallableOnceWith('178.79.169.131'));
+ }
+
+ /** @test */
+ public function resolveShouldQueryARecordsAndIgnoreCase()
+ {
+ $executor = $this->createExecutorMock();
+ $executor
+ ->expects($this->once())
+ ->method('query')
+ ->with($this->anything(), $this->isInstanceOf('React\Dns\Query\Query'))
+ ->will($this->returnCallback(function ($nameserver, $query) {
+ $response = new Message();
+ $response->header->set('qr', 1);
+ $response->questions[] = new Record('Blog.wyrihaximus.net', $query->type, $query->class);
+ $response->answers[] = new Record('Blog.wyrihaximus.net', $query->type, $query->class, 3600, '178.79.169.131');
+
+ return Promise\resolve($response);
+ }));
+
+ $resolver = new Resolver('8.8.8.8:53', $executor);
+ $resolver->resolve('blog.wyrihaximus.net')->then($this->expectCallableOnceWith('178.79.169.131'));
+ }
+
+ /** @test */
+ public function resolveShouldFilterByName()
+ {
+ $executor = $this->createExecutorMock();
+ $executor
+ ->expects($this->once())
+ ->method('query')
+ ->with($this->anything(), $this->isInstanceOf('React\Dns\Query\Query'))
+ ->will($this->returnCallback(function ($nameserver, $query) {
+ $response = new Message();
+ $response->header->set('qr', 1);
+ $response->questions[] = new Record($query->name, $query->type, $query->class);
+ $response->answers[] = new Record('foo.bar', $query->type, $query->class, 3600, '178.79.169.131');
+
+ return Promise\resolve($response);
+ }));
+
+ $errback = $this->expectCallableOnceWith($this->isInstanceOf('React\Dns\RecordNotFoundException'));
+
+ $resolver = new Resolver('8.8.8.8:53', $executor);
+ $resolver->resolve('igor.io')->then($this->expectCallableNever(), $errback);
+ }
+
+ /** @test */
+ public function resolveWithNoAnswersShouldThrowException()
+ {
+ $executor = $this->createExecutorMock();
+ $executor
+ ->expects($this->once())
+ ->method('query')
+ ->with($this->anything(), $this->isInstanceOf('React\Dns\Query\Query'))
+ ->will($this->returnCallback(function ($nameserver, $query) {
+ $response = new Message();
+ $response->header->set('qr', 1);
+ $response->questions[] = new Record($query->name, $query->type, $query->class);
+
+ return Promise\resolve($response);
+ }));
+
+ $errback = $this->expectCallableOnceWith($this->isInstanceOf('React\Dns\RecordNotFoundException'));
+
+ $resolver = new Resolver('8.8.8.8:53', $executor);
+ $resolver->resolve('igor.io')->then($this->expectCallableNever(), $errback);
+ }
+
+ /**
+ * @test
+ */
+ public function resolveWithNoAnswersShouldCallErrbackIfGiven()
+ {
+ $executor = $this->createExecutorMock();
+ $executor
+ ->expects($this->once())
+ ->method('query')
+ ->with($this->anything(), $this->isInstanceOf('React\Dns\Query\Query'))
+ ->will($this->returnCallback(function ($nameserver, $query) {
+ $response = new Message();
+ $response->header->set('qr', 1);
+ $response->questions[] = new Record($query->name, $query->type, $query->class);
+
+ return Promise\resolve($response);
+ }));
+
+ $errback = $this->expectCallableOnceWith($this->isInstanceOf('React\Dns\RecordNotFoundException'));
+
+ $resolver = new Resolver('8.8.8.8:53', $executor);
+ $resolver->resolve('igor.io')->then($this->expectCallableNever(), $errback);
+ }
+
+ private function createExecutorMock()
+ {
+ return $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/TestCase.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/TestCase.php
new file mode 100644
index 0000000..a5a22bf
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/dns/tests/TestCase.php
@@ -0,0 +1,61 @@
+createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ protected function expectCallableOnceWith($value)
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($value);
+
+ return $mock;
+ }
+
+ protected function expectCallableNever()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->never())
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ protected function createCallableMock()
+ {
+ return $this->getMockBuilder('React\Tests\Dns\CallableStub')->getMock();
+ }
+
+ public function setExpectedException($exception, $exceptionMessage = '', $exceptionCode = null)
+ {
+ if (method_exists($this, 'expectException')) {
+ // PHPUnit 5
+ $this->expectException($exception);
+ if ($exceptionMessage !== '') {
+ $this->expectExceptionMessage($exceptionMessage);
+ }
+ if ($exceptionCode !== null) {
+ $this->expectExceptionCode($exceptionCode);
+ }
+ } else {
+ // legacy PHPUnit 4
+ parent::setExpectedException($exception, $exceptionMessage, $exceptionCode);
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/.gitignore b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/.gitignore
new file mode 100644
index 0000000..81b9258
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/.gitignore
@@ -0,0 +1,3 @@
+composer.lock
+phpunit.xml
+vendor
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/.travis.yml b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/.travis.yml
new file mode 100644
index 0000000..7af713a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/.travis.yml
@@ -0,0 +1,39 @@
+language: php
+
+php:
+# - 5.3 # requires old distro, see below
+ - 5.4
+ - 5.5
+ - 5.6
+ - 7.0
+ - 7.1
+ - 7.2
+ - hhvm # ignore errors, see below
+
+# lock distro so new future defaults will not break the build
+dist: trusty
+
+matrix:
+ include:
+ - php: 5.3
+ dist: precise
+ allow_failures:
+ - php: hhvm
+
+sudo: false
+
+addons:
+ apt:
+ packages:
+ - libevent-dev # Used by 'event' and 'libevent' PHP extensions
+
+cache:
+ directories:
+ - $HOME/.composer/cache/files
+
+install:
+ - ./travis-init.sh
+ - composer install
+
+script:
+ - ./vendor/bin/phpunit --coverage-text
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/CHANGELOG.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/CHANGELOG.md
new file mode 100644
index 0000000..c291840
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/CHANGELOG.md
@@ -0,0 +1,316 @@
+# Changelog
+
+## 0.5.1 (2018-04-09)
+
+* New loop: ExtEvLoop (PECL ext-ev) (#148 by @kaduev13)
+
+## 0.5.0 (2018-04-05)
+
+A major feature release with a significant documentation overhaul and long overdue API cleanup!
+
+This update involves a number of BC breaks due to dropped support for deprecated
+functionality. We've tried hard to avoid BC breaks where possible and minimize
+impact otherwise. We expect that most consumers of this package will actually
+not be affected by any BC breaks, see below for more details.
+
+We realize that the changes listed below may seem overwhelming, but we've tried
+to be very clear about any possible BC breaks. Don't worry: In fact, all ReactPHP
+components are already compatible and support both this new release as well as
+providing backwards compatibility with the last release.
+
+* Feature / BC break: Add support for signal handling via new
+ `LoopInterface::addSignal()` and `LoopInterface::removeSignal()` methods.
+ (#104 by @WyriHaximus and #111 and #150 by @clue)
+
+ ```php
+ $loop->addSignal(SIGINT, function () {
+ echo 'CTRL-C';
+ });
+ ```
+
+* Feature: Significant documentation updates for `LoopInterface` and `Factory`.
+ (#100, #119, #126, #127, #159 and #160 by @clue, #113 by @WyriHaximus and #81 and #91 by @jsor)
+
+* Feature: Add examples to ease getting started
+ (#99, #100 and #125 by @clue, #59 by @WyriHaximus and #143 by @jsor)
+
+* Feature: Documentation for advanced timer concepts, such as monotonic time source vs wall-clock time
+ and high precision timers with millisecond accuracy or below.
+ (#130 and #157 by @clue)
+
+* Feature: Documentation for advanced stream concepts, such as edge-triggered event listeners
+ and stream buffers and allow throwing Exception if stream resource is not supported.
+ (#129 and #158 by @clue)
+
+* Feature: Throw `BadMethodCallException` on manual loop creation when required extension isn't installed.
+ (#153 by @WyriHaximus)
+
+* Feature / BC break: First class support for legacy PHP 5.3 through PHP 7.2 and HHVM
+ and remove all `callable` type hints for consistency reasons.
+ (#141 and #151 by @clue)
+
+* BC break: Documentation for timer API and clean up unneeded timer API.
+ (#102 by @clue)
+
+ Remove `TimerInterface::cancel()`, use `LoopInterface::cancelTimer()` instead:
+
+ ```php
+ // old (method invoked on timer instance)
+ $timer->cancel();
+
+ // already supported before: invoke method on loop instance
+ $loop->cancelTimer($timer);
+ ```
+
+ Remove unneeded `TimerInterface::setData()` and `TimerInterface::getData()`,
+ use closure binding to add arbitrary data to timer instead:
+
+ ```php
+ // old (limited setData() and getData() only allows single variable)
+ $name = 'Tester';
+ $timer = $loop->addTimer(1.0, function ($timer) {
+ echo 'Hello ' . $timer->getData() . PHP_EOL;
+ });
+ $timer->setData($name);
+
+ // already supported before: closure binding allows any number of variables
+ $name = 'Tester';
+ $loop->addTimer(1.0, function () use ($name) {
+ echo 'Hello ' . $name . PHP_EOL;
+ });
+ ```
+
+ Remove unneeded `TimerInterface::getLoop()`, use closure binding instead:
+
+ ```php
+ // old (getLoop() called on timer instance)
+ $loop->addTimer(0.1, function ($timer) {
+ $timer->getLoop()->stop();
+ });
+
+ // already supported before: use closure binding as usual
+ $loop->addTimer(0.1, function () use ($loop) {
+ $loop->stop();
+ });
+ ```
+
+* BC break: Remove unneeded `LoopInterface::isTimerActive()` and
+ `TimerInterface::isActive()` to reduce API surface.
+ (#133 by @clue)
+
+ ```php
+ // old (method on timer instance or on loop instance)
+ $timer->isActive();
+ $loop->isTimerActive($timer);
+ ```
+
+* BC break: Move `TimerInterface` one level up to `React\EventLoop\TimerInterface`.
+ (#138 by @WyriHaximus)
+
+ ```php
+ // old (notice obsolete "Timer" namespace)
+ assert($timer instanceof React\EventLoop\Timer\TimerInterface);
+
+ // new
+ assert($timer instanceof React\EventLoop\TimerInterface);
+ ```
+
+* BC break: Remove unneeded `LoopInterface::nextTick()` (and internal `NextTickQueue`),
+ use `LoopInterface::futureTick()` instead.
+ (#30 by @clue)
+
+ ```php
+ // old (removed)
+ $loop->nextTick(function () {
+ echo 'tick';
+ });
+
+ // already supported before
+ $loop->futureTick(function () {
+ echo 'tick';
+ });
+ ```
+
+* BC break: Remove unneeded `$loop` argument for `LoopInterface::futureTick()`
+ (and fix internal cyclic dependency).
+ (#103 by @clue)
+
+ ```php
+ // old ($loop gets passed by default)
+ $loop->futureTick(function ($loop) {
+ $loop->stop();
+ });
+
+ // already supported before: use closure binding as usual
+ $loop->futureTick(function () use ($loop) {
+ $loop->stop();
+ });
+ ```
+
+* BC break: Remove unneeded `LoopInterface::tick()`.
+ (#72 by @jsor)
+
+ ```php
+ // old (removed)
+ $loop->tick();
+
+ // suggested work around for testing purposes only
+ $loop->futureTick(function () use ($loop) {
+ $loop->stop();
+ });
+ ```
+
+* BC break: Documentation for advanced stream API and clean up unneeded stream API.
+ (#110 by @clue)
+
+ Remove unneeded `$loop` argument for `LoopInterface::addReadStream()`
+ and `LoopInterface::addWriteStream()`, use closure binding instead:
+
+ ```php
+ // old ($loop gets passed by default)
+ $loop->addReadStream($stream, function ($stream, $loop) {
+ $loop->removeReadStream($stream);
+ });
+
+ // already supported before: use closure binding as usual
+ $loop->addReadStream($stream, function ($stream) use ($loop) {
+ $loop->removeReadStream($stream);
+ });
+ ```
+
+* BC break: Remove unneeded `LoopInterface::removeStream()` method,
+ use `LoopInterface::removeReadStream()` and `LoopInterface::removeWriteStream()` instead.
+ (#118 by @clue)
+
+ ```php
+ // old
+ $loop->removeStream($stream);
+
+ // already supported before
+ $loop->removeReadStream($stream);
+ $loop->removeWriteStream($stream);
+ ```
+
+* BC break: Rename `LibEventLoop` to `ExtLibeventLoop` and `LibEvLoop` to `ExtLibevLoop`
+ for consistent naming for event loop implementations.
+ (#128 by @clue)
+
+* BC break: Remove optional `EventBaseConfig` argument from `ExtEventLoop`
+ and make its `FEATURE_FDS` enabled by default.
+ (#156 by @WyriHaximus)
+
+* BC break: Mark all classes as final to discourage inheritance.
+ (#131 by @clue)
+
+* Fix: Fix `ExtEventLoop` to keep track of stream resources (refcount)
+ (#123 by @clue)
+
+* Fix: Ensure large timer interval does not overflow on 32bit systems
+ (#132 by @clue)
+
+* Fix: Fix separately removing readable and writable side of stream when closing
+ (#139 by @clue)
+
+* Fix: Properly clean up event watchers for `ext-event` and `ext-libev`
+ (#149 by @clue)
+
+* Fix: Minor code cleanup and remove unneeded references
+ (#145 by @seregazhuk)
+
+* Fix: Discourage outdated `ext-libevent` on PHP 7
+ (#62 by @cboden)
+
+* Improve test suite by adding forward compatibility with PHPUnit 6 and PHPUnit 5,
+ lock Travis distro so new defaults will not break the build,
+ improve test suite to be less fragile and increase test timeouts,
+ test against PHP 7.2 and reduce fwrite() call length to one chunk.
+ (#106 and #144 by @clue, #120 and #124 by @carusogabriel, #147 by nawarian and #92 by @kelunik)
+
+* A number of changes were originally planned for this release but have been backported
+ to the last `v0.4.3` already: #74, #76, #79, #81 (refs #65, #66, #67), #88 and #93
+
+## 0.4.3 (2017-04-27)
+
+* Bug fix: Bugfix in the usage sample code #57 (@dandelionred)
+* Improvement: Remove branch-alias definition #53 (@WyriHaximus)
+* Improvement: StreamSelectLoop: Use fresh time so Timers added during stream events are accurate #51 (@andrewminerd)
+* Improvement: Avoid deprecation warnings in test suite due to deprecation of getMock() in PHPUnit #68 (@martinschroeder)
+* Improvement: Add PHPUnit 4.8 to require-dev #69 (@shaunbramley)
+* Improvement: Increase test timeouts for HHVM and unify timeout handling #70 (@clue)
+* Improvement: Travis improvements (backported from #74) #75 (@clue)
+* Improvement: Test suite now uses socket pairs instead of memory streams #66 (@martinschroeder)
+* Improvement: StreamSelectLoop: Test suite uses signal constant names in data provider #67 (@martinschroeder)
+* Improvement: ExtEventLoop: No longer suppress all errors #65 (@mamciek)
+* Improvement: Readme cleanup #89 (@jsor)
+* Improvement: Restructure and improve README #90 (@jsor)
+* Bug fix: StreamSelectLoop: Fix erroneous zero-time sleep (backport to 0.4) #94 (@jsor)
+
+## 0.4.2 (2016-03-07)
+
+* Bug fix: No longer error when signals sent to StreamSelectLoop
+* Support HHVM and PHP7 (@ondrejmirtes, @cebe)
+* Feature: Added support for EventConfig for ExtEventLoop (@steverhoades)
+* Bug fix: Fixed an issue loading loop extension libs via autoloader (@czarpino)
+
+## 0.4.1 (2014-04-13)
+
+* Bug fix: null timeout in StreamSelectLoop causing 100% CPU usage (@clue)
+* Bug fix: v0.3.4 changes merged for v0.4.1
+
+## 0.4.0 (2014-02-02)
+
+* Feature: Added `EventLoopInterface::nextTick()`, implemented in all event loops (@jmalloc)
+* Feature: Added `EventLoopInterface::futureTick()`, implemented in all event loops (@jmalloc)
+* Feature: Added `ExtEventLoop` implementation using pecl/event (@jmalloc)
+* BC break: Bump minimum PHP version to PHP 5.4, remove 5.3 specific hacks
+* BC break: New method: `EventLoopInterface::nextTick()`
+* BC break: New method: `EventLoopInterface::futureTick()`
+* Dependency: Autoloading and filesystem structure now PSR-4 instead of PSR-0
+
+## 0.3.5 (2016-12-28)
+
+This is a compatibility release that eases upgrading to the v0.4 release branch.
+You should consider upgrading to the v0.4 release branch.
+
+* Feature: Cap min timer interval at 1µs, thus improving compatibility with v0.4
+ (#47 by @clue)
+
+## 0.3.4 (2014-03-30)
+
+* Bug fix: Changed StreamSelectLoop to use non-blocking behavior on tick() (@astephens25)
+
+## 0.3.3 (2013-07-08)
+
+* Bug fix: No error on removing non-existent streams (@clue)
+* Bug fix: Do not silently remove feof listeners in `LibEvLoop`
+
+## 0.3.0 (2013-04-14)
+
+* BC break: New timers API (@nrk)
+* BC break: Remove check on return value from stream callbacks (@nrk)
+
+## 0.2.7 (2013-01-05)
+
+* Bug fix: Fix libevent timers with PHP 5.3
+* Bug fix: Fix libevent timer cancellation (@nrk)
+
+## 0.2.6 (2012-12-26)
+
+* Bug fix: Plug memory issue in libevent timers (@cameronjacobson)
+* Bug fix: Correctly pause LibEvLoop on stop()
+
+## 0.2.3 (2012-11-14)
+
+* Feature: LibEvLoop, integration of `php-libev`
+
+## 0.2.0 (2012-09-10)
+
+* Version bump
+
+## 0.1.1 (2012-07-12)
+
+* Version bump
+
+## 0.1.0 (2012-07-11)
+
+* First tagged release
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/LICENSE b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/LICENSE
new file mode 100644
index 0000000..a808108
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2012 Igor Wiedler, Chris Boden
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/README.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/README.md
new file mode 100644
index 0000000..207e7f4
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/README.md
@@ -0,0 +1,702 @@
+# EventLoop Component
+
+[](https://travis-ci.org/reactphp/event-loop)
+
+[ReactPHP](https://reactphp.org/)'s core reactor event loop that libraries can use for evented I/O.
+
+In order for async based libraries to be interoperable, they need to use the
+same event loop. This component provides a common `LoopInterface` that any
+library can target. This allows them to be used in the same loop, with one
+single [`run()`](#run) call that is controlled by the user.
+
+**Table of Contents**
+
+* [Quickstart example](#quickstart-example)
+* [Usage](#usage)
+ * [Factory](#factory)
+ * [create()](#create)
+ * [Loop implementations](#loop-implementations)
+ * [StreamSelectLoop](#streamselectloop)
+ * [ExtEventLoop](#exteventloop)
+ * [ExtLibeventLoop](#extlibeventloop)
+ * [ExtLibevLoop](#extlibevloop)
+ * [ExtEvLoop](#extevloop)
+ * [LoopInterface](#loopinterface)
+ * [run()](#run)
+ * [stop()](#stop)
+ * [addTimer()](#addtimer)
+ * [addPeriodicTimer()](#addperiodictimer)
+ * [cancelTimer()](#canceltimer)
+ * [futureTick()](#futuretick)
+ * [addSignal()](#addsignal)
+ * [removeSignal()](#removesignal)
+ * [addReadStream()](#addreadstream)
+ * [addWriteStream()](#addwritestream)
+ * [removeReadStream()](#removereadstream)
+ * [removeWriteStream()](#removewritestream)
+* [Install](#install)
+* [Tests](#tests)
+* [License](#license)
+* [More](#more)
+
+## Quickstart example
+
+Here is an async HTTP server built with just the event loop.
+
+```php
+$loop = React\EventLoop\Factory::create();
+
+$server = stream_socket_server('tcp://127.0.0.1:8080');
+stream_set_blocking($server, false);
+
+$loop->addReadStream($server, function ($server) use ($loop) {
+ $conn = stream_socket_accept($server);
+ $data = "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nHi\n";
+ $loop->addWriteStream($conn, function ($conn) use (&$data, $loop) {
+ $written = fwrite($conn, $data);
+ if ($written === strlen($data)) {
+ fclose($conn);
+ $loop->removeWriteStream($conn);
+ } else {
+ $data = substr($data, $written);
+ }
+ });
+});
+
+$loop->addPeriodicTimer(5, function () {
+ $memory = memory_get_usage() / 1024;
+ $formatted = number_format($memory, 3).'K';
+ echo "Current memory usage: {$formatted}\n";
+});
+
+$loop->run();
+```
+
+See also the [examples](examples).
+
+## Usage
+
+Typical applications use a single event loop which is created at the beginning
+and run at the end of the program.
+
+```php
+// [1]
+$loop = React\EventLoop\Factory::create();
+
+// [2]
+$loop->addPeriodicTimer(1, function () {
+ echo "Tick\n";
+});
+
+$stream = new React\Stream\ReadableResourceStream(
+ fopen('file.txt', 'r'),
+ $loop
+);
+
+// [3]
+$loop->run();
+```
+
+1. The loop instance is created at the beginning of the program. A convenience
+ factory [`React\EventLoop\Factory::create()`](#create) is provided by this library which
+ picks the best available [loop implementation](#loop-implementations).
+2. The loop instance is used directly or passed to library and application code.
+ In this example, a periodic timer is registered with the event loop which
+ simply outputs `Tick` every second and a
+ [readable stream](https://github.com/reactphp/stream#readableresourcestream)
+ is created by using ReactPHP's
+ [stream component](https://github.com/reactphp/stream) for demonstration
+ purposes.
+3. The loop is run with a single [`$loop->run()`](#run) call at the end of the program.
+
+### Factory
+
+The `Factory` class exists as a convenient way to pick the best available
+[event loop implementation](#loop-implementations).
+
+#### create()
+
+The `create(): LoopInterface` method can be used to create a new event loop
+instance:
+
+```php
+$loop = React\EventLoop\Factory::create();
+```
+
+This method always returns an instance implementing [`LoopInterface`](#loopinterface),
+the actual [event loop implementation](#loop-implementations) is an implementation detail.
+
+This method should usually only be called once at the beginning of the program.
+
+### Loop implementations
+
+In addition to the [`LoopInterface`](#loopinterface), there are a number of
+event loop implementations provided.
+
+All of the event loops support these features:
+
+* File descriptor polling
+* One-off timers
+* Periodic timers
+* Deferred execution on future loop tick
+
+For most consumers of this package, the underlying event loop implementation is
+an implementation detail.
+You should use the [`Factory`](#factory) to automatically create a new instance.
+
+Advanced! If you explicitly need a certain event loop implementation, you can
+manually instantiate one of the following classes.
+Note that you may have to install the required PHP extensions for the respective
+event loop implementation first or they will throw a `BadMethodCallException` on creation.
+
+#### StreamSelectLoop
+
+A `stream_select()` based event loop.
+
+This uses the [`stream_select()`](http://php.net/manual/en/function.stream-select.php)
+function and is the only implementation which works out of the box with PHP.
+
+This event loop works out of the box on PHP 5.3 through PHP 7+ and HHVM.
+This means that no installation is required and this library works on all
+platforms and supported PHP versions.
+Accordingly, the [`Factory`](#factory) will use this event loop by default if
+you do not install any of the event loop extensions listed below.
+
+Under the hood, it does a simple `select` system call.
+This system call is limited to the maximum file descriptor number of
+`FD_SETSIZE` (platform dependent, commonly 1024) and scales with `O(m)`
+(`m` being the maximum file descriptor number passed).
+This means that you may run into issues when handling thousands of streams
+concurrently and you may want to look into using one of the alternative
+event loop implementations listed below in this case.
+If your use case is among the many common use cases that involve handling only
+dozens or a few hundred streams at once, then this event loop implementation
+performs really well.
+
+If you want to use signal handling (see also [`addSignal()`](#addsignal) below),
+this event loop implementation requires `ext-pcntl`.
+This extension is only available for Unix-like platforms and does not support
+Windows.
+It is commonly installed as part of many PHP distributions.
+If this extension is missing (or you're running on Windows), signal handling is
+not supported and throws a `BadMethodCallException` instead.
+
+This event loop is known to rely on wall-clock time to schedule future
+timers, because a monotonic time source is not available in PHP by default.
+While this does not affect many common use cases, this is an important
+distinction for programs that rely on a high time precision or on systems
+that are subject to discontinuous time adjustments (time jumps).
+This means that if you schedule a timer to trigger in 30s and then adjust
+your system time forward by 20s, the timer may trigger in 10s.
+See also [`addTimer()`](#addtimer) for more details.
+
+#### ExtEventLoop
+
+An `ext-event` based event loop.
+
+This uses the [`event` PECL extension](https://pecl.php.net/package/event).
+It supports the same backends as libevent.
+
+This loop is known to work with PHP 5.4 through PHP 7+.
+
+#### ExtEvLoop
+
+An `ext-ev` based event loop.
+
+This loop uses the [`ev` PECL extension](https://pecl.php.net/package/ev), that
+provides an interface to `libev` library.
+
+This loop is known to work with PHP 5.4 through PHP 7+.
+
+
+#### ExtLibeventLoop
+
+An `ext-libevent` based event loop.
+
+This uses the [`libevent` PECL extension](https://pecl.php.net/package/libevent).
+`libevent` itself supports a number of system-specific backends (epoll, kqueue).
+
+This event loop does only work with PHP 5.
+An [unofficial update](https://github.com/php/pecl-event-libevent/pull/2) for
+PHP 7 does exist, but it is known to cause regular crashes due to `SEGFAULT`s.
+To reiterate: Using this event loop on PHP 7 is not recommended.
+Accordingly, the [`Factory`](#factory) will not try to use this event loop on
+PHP 7.
+
+This event loop is known to trigger a readable listener only if
+the stream *becomes* readable (edge-triggered) and may not trigger if the
+stream has already been readable from the beginning.
+This also implies that a stream may not be recognized as readable when data
+is still left in PHP's internal stream buffers.
+As such, it's recommended to use `stream_set_read_buffer($stream, 0);`
+to disable PHP's internal read buffer in this case.
+See also [`addReadStream()`](#addreadstream) for more details.
+
+#### ExtLibevLoop
+
+An `ext-libev` based event loop.
+
+This uses an [unofficial `libev` extension](https://github.com/m4rw3r/php-libev).
+It supports the same backends as libevent.
+
+This loop does only work with PHP 5.
+An update for PHP 7 is [unlikely](https://github.com/m4rw3r/php-libev/issues/8)
+to happen any time soon.
+
+### LoopInterface
+
+#### run()
+
+The `run(): void` method can be used to
+run the event loop until there are no more tasks to perform.
+
+For many applications, this method is the only directly visible
+invocation on the event loop.
+As a rule of thumb, it is usally recommended to attach everything to the
+same loop instance and then run the loop once at the bottom end of the
+application.
+
+```php
+$loop->run();
+```
+
+This method will keep the loop running until there are no more tasks
+to perform. In other words: This method will block until the last
+timer, stream and/or signal has been removed.
+
+Likewise, it is imperative to ensure the application actually invokes
+this method once. Adding listeners to the loop and missing to actually
+run it will result in the application exiting without actually waiting
+for any of the attached listeners.
+
+This method MUST NOT be called while the loop is already running.
+This method MAY be called more than once after it has explicity been
+[`stop()`ped](#stop) or after it automatically stopped because it
+previously did no longer have anything to do.
+
+#### stop()
+
+The `stop(): void` method can be used to
+instruct a running event loop to stop.
+
+This method is considered advanced usage and should be used with care.
+As a rule of thumb, it is usually recommended to let the loop stop
+only automatically when it no longer has anything to do.
+
+This method can be used to explicitly instruct the event loop to stop:
+
+```php
+$loop->addTimer(3.0, function () use ($loop) {
+ $loop->stop();
+});
+```
+
+Calling this method on a loop instance that is not currently running or
+on a loop instance that has already been stopped has no effect.
+
+#### addTimer()
+
+The `addTimer(float $interval, callable $callback): TimerInterface` method can be used to
+enqueue a callback to be invoked once after the given interval.
+
+The timer callback function MUST be able to accept a single parameter,
+the timer instance as also returned by this method or you MAY use a
+function which has no parameters at all.
+
+The timer callback function MUST NOT throw an `Exception`.
+The return value of the timer callback function will be ignored and has
+no effect, so for performance reasons you're recommended to not return
+any excessive data structures.
+
+Unlike [`addPeriodicTimer()`](#addperiodictimer), this method will ensure
+the callback will be invoked only once after the given interval.
+You can invoke [`cancelTimer`](#canceltimer) to cancel a pending timer.
+
+```php
+$loop->addTimer(0.8, function () {
+ echo 'world!' . PHP_EOL;
+});
+
+$loop->addTimer(0.3, function () {
+ echo 'hello ';
+});
+```
+
+See also [example #1](examples).
+
+If you want to access any variables within your callback function, you
+can bind arbitrary data to a callback closure like this:
+
+```php
+function hello($name, LoopInterface $loop)
+{
+ $loop->addTimer(1.0, function () use ($name) {
+ echo "hello $name\n";
+ });
+}
+
+hello('Tester', $loop);
+```
+
+This interface does not enforce any particular timer resolution, so
+special care may have to be taken if you rely on very high precision with
+millisecond accuracy or below. Event loop implementations SHOULD work on
+a best effort basis and SHOULD provide at least millisecond accuracy
+unless otherwise noted. Many existing event loop implementations are
+known to provide microsecond accuracy, but it's generally not recommended
+to rely on this high precision.
+
+Similarly, the execution order of timers scheduled to execute at the
+same time (within its possible accuracy) is not guaranteed.
+
+This interface suggests that event loop implementations SHOULD use a
+monotonic time source if available. Given that a monotonic time source is
+not available on PHP by default, event loop implementations MAY fall back
+to using wall-clock time.
+While this does not affect many common use cases, this is an important
+distinction for programs that rely on a high time precision or on systems
+that are subject to discontinuous time adjustments (time jumps).
+This means that if you schedule a timer to trigger in 30s and then adjust
+your system time forward by 20s, the timer SHOULD still trigger in 30s.
+See also [event loop implementations](#loop-implementations) for more details.
+
+#### addPeriodicTimer()
+
+The `addPeriodicTimer(float $interval, callable $callback): TimerInterface` method can be used to
+enqueue a callback to be invoked repeatedly after the given interval.
+
+The timer callback function MUST be able to accept a single parameter,
+the timer instance as also returned by this method or you MAY use a
+function which has no parameters at all.
+
+The timer callback function MUST NOT throw an `Exception`.
+The return value of the timer callback function will be ignored and has
+no effect, so for performance reasons you're recommended to not return
+any excessive data structures.
+
+Unlike [`addTimer()`](#addtimer), this method will ensure the the
+callback will be invoked infinitely after the given interval or until you
+invoke [`cancelTimer`](#canceltimer).
+
+```php
+$timer = $loop->addPeriodicTimer(0.1, function () {
+ echo 'tick!' . PHP_EOL;
+});
+
+$loop->addTimer(1.0, function () use ($loop, $timer) {
+ $loop->cancelTimer($timer);
+ echo 'Done' . PHP_EOL;
+});
+```
+
+See also [example #2](examples).
+
+If you want to limit the number of executions, you can bind
+arbitrary data to a callback closure like this:
+
+```php
+function hello($name, LoopInterface $loop)
+{
+ $n = 3;
+ $loop->addPeriodicTimer(1.0, function ($timer) use ($name, $loop, &$n) {
+ if ($n > 0) {
+ --$n;
+ echo "hello $name\n";
+ } else {
+ $loop->cancelTimer($timer);
+ }
+ });
+}
+
+hello('Tester', $loop);
+```
+
+This interface does not enforce any particular timer resolution, so
+special care may have to be taken if you rely on very high precision with
+millisecond accuracy or below. Event loop implementations SHOULD work on
+a best effort basis and SHOULD provide at least millisecond accuracy
+unless otherwise noted. Many existing event loop implementations are
+known to provide microsecond accuracy, but it's generally not recommended
+to rely on this high precision.
+
+Similarly, the execution order of timers scheduled to execute at the
+same time (within its possible accuracy) is not guaranteed.
+
+This interface suggests that event loop implementations SHOULD use a
+monotonic time source if available. Given that a monotonic time source is
+not available on PHP by default, event loop implementations MAY fall back
+to using wall-clock time.
+While this does not affect many common use cases, this is an important
+distinction for programs that rely on a high time precision or on systems
+that are subject to discontinuous time adjustments (time jumps).
+This means that if you schedule a timer to trigger in 30s and then adjust
+your system time forward by 20s, the timer SHOULD still trigger in 30s.
+See also [event loop implementations](#loop-implementations) for more details.
+
+Additionally, periodic timers may be subject to timer drift due to
+re-scheduling after each invocation. As such, it's generally not
+recommended to rely on this for high precision intervals with millisecond
+accuracy or below.
+
+#### cancelTimer()
+
+The `cancelTimer(TimerInterface $timer): void` method can be used to
+cancel a pending timer.
+
+See also [`addPeriodicTimer()`](#addperiodictimer) and [example #2](examples).
+
+Calling this method on a timer instance that has not been added to this
+loop instance or on a timer that has already been cancelled has no effect.
+
+#### futureTick()
+
+The `futureTick(callable $listener): void` method can be used to
+schedule a callback to be invoked on a future tick of the event loop.
+
+This works very much similar to timers with an interval of zero seconds,
+but does not require the overhead of scheduling a timer queue.
+
+The tick callback function MUST be able to accept zero parameters.
+
+The tick callback function MUST NOT throw an `Exception`.
+The return value of the tick callback function will be ignored and has
+no effect, so for performance reasons you're recommended to not return
+any excessive data structures.
+
+If you want to access any variables within your callback function, you
+can bind arbitrary data to a callback closure like this:
+
+```php
+function hello($name, LoopInterface $loop)
+{
+ $loop->futureTick(function () use ($name) {
+ echo "hello $name\n";
+ });
+}
+
+hello('Tester', $loop);
+```
+
+Unlike timers, tick callbacks are guaranteed to be executed in the order
+they are enqueued.
+Also, once a callback is enqueued, there's no way to cancel this operation.
+
+This is often used to break down bigger tasks into smaller steps (a form
+of cooperative multitasking).
+
+```php
+$loop->futureTick(function () {
+ echo 'b';
+});
+$loop->futureTick(function () {
+ echo 'c';
+});
+echo 'a';
+```
+
+See also [example #3](examples).
+
+#### addSignal()
+
+The `addSignal(int $signal, callable $listener): void` method can be used to
+register a listener to be notified when a signal has been caught by this process.
+
+This is useful to catch user interrupt signals or shutdown signals from
+tools like `supervisor` or `systemd`.
+
+The listener callback function MUST be able to accept a single parameter,
+the signal added by this method or you MAY use a function which
+has no parameters at all.
+
+The listener callback function MUST NOT throw an `Exception`.
+The return value of the listener callback function will be ignored and has
+no effect, so for performance reasons you're recommended to not return
+any excessive data structures.
+
+```php
+$loop->addSignal(SIGINT, function (int $signal) {
+ echo 'Caught user interrupt signal' . PHP_EOL;
+});
+```
+
+See also [example #4](examples).
+
+Signaling is only available on Unix-like platform, Windows isn't
+supported due to operating system limitations.
+This method may throw a `BadMethodCallException` if signals aren't
+supported on this platform, for example when required extensions are
+missing.
+
+**Note: A listener can only be added once to the same signal, any
+attempts to add it more then once will be ignored.**
+
+#### removeSignal()
+
+The `removeSignal(int $signal, callable $listener): void` method can be used to
+remove a previously added signal listener.
+
+```php
+$loop->removeSignal(SIGINT, $listener);
+```
+
+Any attempts to remove listeners that aren't registered will be ignored.
+
+#### addReadStream()
+
+> Advanced! Note that this low-level API is considered advanced usage.
+ Most use cases should probably use the higher-level
+ [readable Stream API](https://github.com/reactphp/stream#readablestreaminterface)
+ instead.
+
+The `addReadStream(resource $stream, callable $callback): void` method can be used to
+register a listener to be notified when a stream is ready to read.
+
+The first parameter MUST be a valid stream resource that supports
+checking whether it is ready to read by this loop implementation.
+A single stream resource MUST NOT be added more than once.
+Instead, either call [`removeReadStream()`](#removereadstream) first or
+react to this event with a single listener and then dispatch from this
+listener. This method MAY throw an `Exception` if the given resource type
+is not supported by this loop implementation.
+
+The listener callback function MUST be able to accept a single parameter,
+the stream resource added by this method or you MAY use a function which
+has no parameters at all.
+
+The listener callback function MUST NOT throw an `Exception`.
+The return value of the listener callback function will be ignored and has
+no effect, so for performance reasons you're recommended to not return
+any excessive data structures.
+
+If you want to access any variables within your callback function, you
+can bind arbitrary data to a callback closure like this:
+
+```php
+$loop->addReadStream($stream, function ($stream) use ($name) {
+ echo $name . ' said: ' . fread($stream);
+});
+```
+
+See also [example #11](examples).
+
+You can invoke [`removeReadStream()`](#removereadstream) to remove the
+read event listener for this stream.
+
+The execution order of listeners when multiple streams become ready at
+the same time is not guaranteed.
+
+Some event loop implementations are known to only trigger the listener if
+the stream *becomes* readable (edge-triggered) and may not trigger if the
+stream has already been readable from the beginning.
+This also implies that a stream may not be recognized as readable when data
+is still left in PHP's internal stream buffers.
+As such, it's recommended to use `stream_set_read_buffer($stream, 0);`
+to disable PHP's internal read buffer in this case.
+
+#### addWriteStream()
+
+> Advanced! Note that this low-level API is considered advanced usage.
+ Most use cases should probably use the higher-level
+ [writable Stream API](https://github.com/reactphp/stream#writablestreaminterface)
+ instead.
+
+The `addWriteStream(resource $stream, callable $callback): void` method can be used to
+register a listener to be notified when a stream is ready to write.
+
+The first parameter MUST be a valid stream resource that supports
+checking whether it is ready to write by this loop implementation.
+A single stream resource MUST NOT be added more than once.
+Instead, either call [`removeWriteStream()`](#removewritestream) first or
+react to this event with a single listener and then dispatch from this
+listener. This method MAY throw an `Exception` if the given resource type
+is not supported by this loop implementation.
+
+The listener callback function MUST be able to accept a single parameter,
+the stream resource added by this method or you MAY use a function which
+has no parameters at all.
+
+The listener callback function MUST NOT throw an `Exception`.
+The return value of the listener callback function will be ignored and has
+no effect, so for performance reasons you're recommended to not return
+any excessive data structures.
+
+If you want to access any variables within your callback function, you
+can bind arbitrary data to a callback closure like this:
+
+```php
+$loop->addWriteStream($stream, function ($stream) use ($name) {
+ fwrite($stream, 'Hello ' . $name);
+});
+```
+
+See also [example #12](examples).
+
+You can invoke [`removeWriteStream()`](#removewritestream) to remove the
+write event listener for this stream.
+
+The execution order of listeners when multiple streams become ready at
+the same time is not guaranteed.
+
+#### removeReadStream()
+
+The `removeReadStream(resource $stream): void` method can be used to
+remove the read event listener for the given stream.
+
+Removing a stream from the loop that has already been removed or trying
+to remove a stream that was never added or is invalid has no effect.
+
+#### removeWriteStream()
+
+The `removeWriteStream(resource $stream): void` method can be used to
+remove the write event listener for the given stream.
+
+Removing a stream from the loop that has already been removed or trying
+to remove a stream that was never added or is invalid has no effect.
+
+## Install
+
+The recommended way to install this library is [through Composer](https://getcomposer.org).
+[New to Composer?](https://getcomposer.org/doc/00-intro.md)
+
+This will install the latest supported version:
+
+```bash
+$ composer require react/event-loop:^0.5.1
+```
+
+See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
+
+This project aims to run on any platform and thus does not require any PHP
+extensions and supports running on legacy PHP 5.3 through current PHP 7+ and
+HHVM.
+It's *highly recommended to use PHP 7+* for this project.
+
+Installing any of the event loop extensions is suggested, but entirely optional.
+See also [event loop implementations](#loop-implementations) for more details.
+
+## Tests
+
+To run the test suite, you first need to clone this repo and then install all
+dependencies [through Composer](https://getcomposer.org):
+
+```bash
+$ composer install
+```
+
+To run the test suite, go to the project root and run:
+
+```bash
+$ php vendor/bin/phpunit
+```
+
+## License
+
+MIT, see [LICENSE file](LICENSE).
+
+## More
+
+* See our [Stream component](https://github.com/reactphp/stream) for more
+ information on how streams are used in real-world applications.
+* See our [users wiki](https://github.com/reactphp/react/wiki/Users) and the
+ [dependents on Packagist](https://packagist.org/packages/react/event-loop/dependents)
+ for a list of packages that use the EventLoop in real-world applications.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/composer.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/composer.json
new file mode 100644
index 0000000..24974ec
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/composer.json
@@ -0,0 +1,21 @@
+{
+ "name": "react/event-loop",
+ "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.",
+ "keywords": ["event-loop", "asynchronous"],
+ "license": "MIT",
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8.35 || ^5.7 || ^6.4"
+ },
+ "suggest": {
+ "ext-event": "~1.0 for ExtEventLoop",
+ "ext-pcntl": "For signal handling support when using the StreamSelectLoop"
+ },
+ "autoload": {
+ "psr-4": {
+ "React\\EventLoop\\": "src"
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/01-timers.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/01-timers.php
new file mode 100644
index 0000000..e6107e4
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/01-timers.php
@@ -0,0 +1,15 @@
+addTimer(0.8, function () {
+ echo 'world!' . PHP_EOL;
+});
+
+$loop->addTimer(0.3, function () {
+ echo 'hello ';
+});
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/02-periodic.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/02-periodic.php
new file mode 100644
index 0000000..5e138a6
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/02-periodic.php
@@ -0,0 +1,16 @@
+addPeriodicTimer(0.1, function () {
+ echo 'tick!' . PHP_EOL;
+});
+
+$loop->addTimer(1.0, function () use ($loop, $timer) {
+ $loop->cancelTimer($timer);
+ echo 'Done' . PHP_EOL;
+});
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/03-ticks.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/03-ticks.php
new file mode 100644
index 0000000..3f36c6d
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/03-ticks.php
@@ -0,0 +1,15 @@
+futureTick(function () {
+ echo 'b';
+});
+$loop->futureTick(function () {
+ echo 'c';
+});
+echo 'a';
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/04-signals.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/04-signals.php
new file mode 100644
index 0000000..90b6898
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/04-signals.php
@@ -0,0 +1,19 @@
+addSignal(SIGINT, $func = function ($signal) use ($loop, &$func) {
+ echo 'Signal: ', (string)$signal, PHP_EOL;
+ $loop->removeSignal(SIGINT, $func);
+});
+
+echo 'Listening for SIGINT. Use "kill -SIGINT ' . getmypid() . '" or CTRL+C' . PHP_EOL;
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/11-consume-stdin.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/11-consume-stdin.php
new file mode 100644
index 0000000..2a77245
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/11-consume-stdin.php
@@ -0,0 +1,30 @@
+addReadStream(STDIN, function ($stream) use ($loop) {
+ $chunk = fread($stream, 64 * 1024);
+
+ // reading nothing means we reached EOF
+ if ($chunk === '') {
+ $loop->removeReadStream($stream);
+ stream_set_blocking($stream, true);
+ fclose($stream);
+ return;
+ }
+
+ echo strlen($chunk) . ' bytes' . PHP_EOL;
+});
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/12-generate-yes.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/12-generate-yes.php
new file mode 100644
index 0000000..ebc2beb
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/12-generate-yes.php
@@ -0,0 +1,41 @@
+addWriteStream(STDOUT, function ($stdout) use ($loop, &$data) {
+ // try to write data
+ $r = fwrite($stdout, $data);
+
+ // nothing could be written despite being writable => closed
+ if ($r === 0) {
+ $loop->removeWriteStream($stdout);
+ fclose($stdout);
+ stream_set_blocking($stdout, true);
+ fwrite(STDERR, 'Stopped because STDOUT closed' . PHP_EOL);
+
+ return;
+ }
+
+ // implement a very simple ring buffer, unless everything has been written at once:
+ // everything written in this iteration will be appended for next iteration
+ if (isset($data[$r])) {
+ $data = substr($data, $r) . substr($data, 0, $r);
+ }
+});
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/13-http-client-blocking.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/13-http-client-blocking.php
new file mode 100644
index 0000000..a2dde55
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/13-http-client-blocking.php
@@ -0,0 +1,35 @@
+addReadStream($stream, function ($stream) use ($loop) {
+ $chunk = fread($stream, 64 * 1024);
+
+ // reading nothing means we reached EOF
+ if ($chunk === '') {
+ echo '[END]' . PHP_EOL;
+ $loop->removeReadStream($stream);
+ fclose($stream);
+ return;
+ }
+
+ echo $chunk;
+});
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/14-http-client-async.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/14-http-client-async.php
new file mode 100644
index 0000000..c82c988
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/14-http-client-async.php
@@ -0,0 +1,63 @@
+addPeriodicTimer(0.01, function () {
+ echo '.';
+});
+
+// wait for connection success/error
+$loop->addWriteStream($stream, function ($stream) use ($loop, $timer) {
+ $loop->removeWriteStream($stream);
+ $loop->cancelTimer($timer);
+
+ // check for socket error (connection rejected)
+ if (stream_socket_get_name($stream, true) === false) {
+ echo '[unable to connect]' . PHP_EOL;
+ exit(1);
+ } else {
+ echo '[connected]' . PHP_EOL;
+ }
+
+ // send HTTP request
+ fwrite($stream, "GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n");
+
+ // wait for HTTP response
+ $loop->addReadStream($stream, function ($stream) use ($loop) {
+ $chunk = fread($stream, 64 * 1024);
+
+ // reading nothing means we reached EOF
+ if ($chunk === '') {
+ echo '[END]' . PHP_EOL;
+ $loop->removeReadStream($stream);
+ fclose($stream);
+ return;
+ }
+
+ echo $chunk;
+ });
+});
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/21-http-server.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/21-http-server.php
new file mode 100644
index 0000000..89520ce
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/21-http-server.php
@@ -0,0 +1,36 @@
+addReadStream($server, function ($server) use ($loop) {
+ $conn = stream_socket_accept($server);
+ $data = "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nHi\n";
+ $loop->addWriteStream($conn, function ($conn) use (&$data, $loop) {
+ $written = fwrite($conn, $data);
+ if ($written === strlen($data)) {
+ fclose($conn);
+ $loop->removeWriteStream($conn);
+ } else {
+ $data = substr($data, $written);
+ }
+ });
+});
+
+$loop->addPeriodicTimer(5, function () {
+ $memory = memory_get_usage() / 1024;
+ $formatted = number_format($memory, 3).'K';
+ echo "Current memory usage: {$formatted}\n";
+});
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/91-benchmark-ticks.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/91-benchmark-ticks.php
new file mode 100644
index 0000000..3f4690b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/91-benchmark-ticks.php
@@ -0,0 +1,15 @@
+futureTick(function () { });
+}
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/92-benchmark-timers.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/92-benchmark-timers.php
new file mode 100644
index 0000000..e2e02e4
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/92-benchmark-timers.php
@@ -0,0 +1,15 @@
+addTimer(0, function () { });
+}
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/93-benchmark-ticks-delay.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/93-benchmark-ticks-delay.php
new file mode 100644
index 0000000..95ee78c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/93-benchmark-ticks-delay.php
@@ -0,0 +1,22 @@
+ 0) {
+ --$ticks;
+ //$loop->addTimer(0, $tick);
+ $loop->futureTick($tick);
+ } else {
+ echo 'done';
+ }
+};
+
+$tick();
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/94-benchmark-timers-delay.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/94-benchmark-timers-delay.php
new file mode 100644
index 0000000..2d6cfa2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/94-benchmark-timers-delay.php
@@ -0,0 +1,22 @@
+ 0) {
+ --$ticks;
+ //$loop->futureTick($tick);
+ $loop->addTimer(0, $tick);
+ } else {
+ echo 'done';
+ }
+};
+
+$tick();
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/95-benchmark-memory.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/95-benchmark-memory.php
new file mode 100644
index 0000000..084c404
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/examples/95-benchmark-memory.php
@@ -0,0 +1,67 @@
+addTimer($t, function () use ($loop) {
+ $loop->stop();
+ });
+
+}
+
+$loop->addPeriodicTimer(0.001, function () use (&$runs, $loop) {
+ $runs++;
+
+ $loop->addPeriodicTimer(1, function (TimerInterface $timer) use ($loop) {
+ $loop->cancelTimer($timer);
+ });
+});
+
+$loop->addPeriodicTimer($r, function () use (&$runs) {
+ $kmem = round(memory_get_usage() / 1024);
+ $kmemReal = round(memory_get_usage(true) / 1024);
+ echo "Runs:\t\t\t$runs\n";
+ echo "Memory (internal):\t$kmem KiB\n";
+ echo "Memory (real):\t\t$kmemReal KiB\n";
+ echo str_repeat('-', 50), "\n";
+});
+
+echo "PHP Version:\t\t", phpversion(), "\n";
+echo "Loop\t\t\t", get_class($loop), "\n";
+echo "Time\t\t\t", date('r'), "\n";
+
+echo str_repeat('-', 50), "\n";
+
+$beginTime = time();
+$loop->run();
+$endTime = time();
+$timeTaken = $endTime - $beginTime;
+
+echo "PHP Version:\t\t", phpversion(), "\n";
+echo "Loop\t\t\t", get_class($loop), "\n";
+echo "Time\t\t\t", date('r'), "\n";
+echo "Time taken\t\t", $timeTaken, " seconds\n";
+echo "Runs per second\t\t", round($runs / $timeTaken), "\n";
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/phpunit.xml.dist b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/phpunit.xml.dist
new file mode 100644
index 0000000..cba6d4d
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/phpunit.xml.dist
@@ -0,0 +1,25 @@
+
+
+
+
+
+ ./tests/
+
+
+
+
+
+ ./src/
+
+
+
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/ExtEvLoop.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/ExtEvLoop.php
new file mode 100644
index 0000000..74db6d0
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/ExtEvLoop.php
@@ -0,0 +1,252 @@
+loop = new EvLoop();
+ $this->futureTickQueue = new FutureTickQueue();
+ $this->timers = new SplObjectStorage();
+ $this->signals = new SignalsHandler();
+ }
+
+ public function addReadStream($stream, $listener)
+ {
+ $key = (int)$stream;
+
+ if (isset($this->readStreams[$key])) {
+ return;
+ }
+
+ $callback = $this->getStreamListenerClosure($stream, $listener);
+ $event = $this->loop->io($stream, Ev::READ, $callback);
+ $this->readStreams[$key] = $event;
+ }
+
+ /**
+ * @param resource $stream
+ * @param callable $listener
+ *
+ * @return \Closure
+ */
+ private function getStreamListenerClosure($stream, $listener)
+ {
+ return function () use ($stream, $listener) {
+ call_user_func($listener, $stream);
+ };
+ }
+
+ public function addWriteStream($stream, $listener)
+ {
+ $key = (int)$stream;
+
+ if (isset($this->writeStreams[$key])) {
+ return;
+ }
+
+ $callback = $this->getStreamListenerClosure($stream, $listener);
+ $event = $this->loop->io($stream, Ev::WRITE, $callback);
+ $this->writeStreams[$key] = $event;
+ }
+
+ public function removeReadStream($stream)
+ {
+ $key = (int)$stream;
+
+ if (!isset($this->readStreams[$key])) {
+ return;
+ }
+
+ $this->readStreams[$key]->stop();
+ unset($this->readStreams[$key]);
+ }
+
+ public function removeWriteStream($stream)
+ {
+ $key = (int)$stream;
+
+ if (!isset($this->writeStreams[$key])) {
+ return;
+ }
+
+ $this->writeStreams[$key]->stop();
+ unset($this->writeStreams[$key]);
+ }
+
+ public function addTimer($interval, $callback)
+ {
+ $timer = new Timer($interval, $callback, false);
+
+ $that = $this;
+ $timers = $this->timers;
+ $callback = function () use ($timer, $timers, $that) {
+ call_user_func($timer->getCallback(), $timer);
+
+ if ($timers->contains($timer)) {
+ $that->cancelTimer($timer);
+ }
+ };
+
+ $event = $this->loop->timer($timer->getInterval(), 0.0, $callback);
+ $this->timers->attach($timer, $event);
+
+ return $timer;
+ }
+
+ public function addPeriodicTimer($interval, $callback)
+ {
+ $timer = new Timer($interval, $callback, true);
+
+ $callback = function () use ($timer) {
+ call_user_func($timer->getCallback(), $timer);
+ };
+
+ $event = $this->loop->timer($interval, $interval, $callback);
+ $this->timers->attach($timer, $event);
+
+ return $timer;
+ }
+
+ public function cancelTimer(TimerInterface $timer)
+ {
+ if (!isset($this->timers[$timer])) {
+ return;
+ }
+
+ $event = $this->timers[$timer];
+ $event->stop();
+ $this->timers->detach($timer);
+ }
+
+ public function futureTick($listener)
+ {
+ $this->futureTickQueue->add($listener);
+ }
+
+ public function run()
+ {
+ $this->running = true;
+
+ while ($this->running) {
+ $this->futureTickQueue->tick();
+
+ $hasPendingCallbacks = !$this->futureTickQueue->isEmpty();
+ $wasJustStopped = !$this->running;
+ $nothingLeftToDo = !$this->readStreams
+ && !$this->writeStreams
+ && !$this->timers->count()
+ && $this->signals->isEmpty();
+
+ $flags = Ev::RUN_ONCE;
+ if ($wasJustStopped || $hasPendingCallbacks) {
+ $flags |= Ev::RUN_NOWAIT;
+ } elseif ($nothingLeftToDo) {
+ break;
+ }
+
+ $this->loop->run($flags);
+ }
+ }
+
+ public function stop()
+ {
+ $this->running = false;
+ }
+
+ public function __destruct()
+ {
+ /** @var TimerInterface $timer */
+ foreach ($this->timers as $timer) {
+ $this->cancelTimer($timer);
+ }
+
+ foreach ($this->readStreams as $key => $stream) {
+ $this->removeReadStream($key);
+ }
+
+ foreach ($this->writeStreams as $key => $stream) {
+ $this->removeWriteStream($key);
+ }
+ }
+
+ public function addSignal($signal, $listener)
+ {
+ $this->signals->add($signal, $listener);
+
+ if (!isset($this->signalEvents[$signal])) {
+ $this->signalEvents[$signal] = $this->loop->signal($signal, function() use ($signal) {
+ $this->signals->call($signal);
+ });
+ }
+ }
+
+ public function removeSignal($signal, $listener)
+ {
+ $this->signals->remove($signal, $listener);
+
+ if (isset($this->signalEvents[$signal])) {
+ $this->signalEvents[$signal]->stop();
+ unset($this->signalEvents[$signal]);
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/ExtEventLoop.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/ExtEventLoop.php
new file mode 100644
index 0000000..622dd47
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/ExtEventLoop.php
@@ -0,0 +1,259 @@
+requireFeatures(EventBaseConfig::FEATURE_FDS);
+
+ $this->eventBase = new EventBase($config);
+ $this->futureTickQueue = new FutureTickQueue();
+ $this->timerEvents = new SplObjectStorage();
+ $this->signals = new SignalsHandler();
+
+ $this->createTimerCallback();
+ $this->createStreamCallback();
+ }
+
+ public function addReadStream($stream, $listener)
+ {
+ $key = (int) $stream;
+ if (isset($this->readListeners[$key])) {
+ return;
+ }
+
+ $event = new Event($this->eventBase, $stream, Event::PERSIST | Event::READ, $this->streamCallback);
+ $event->add();
+ $this->readEvents[$key] = $event;
+ $this->readListeners[$key] = $listener;
+
+ // ext-event does not increase refcount on stream resources for PHP 7+
+ // manually keep track of stream resource to prevent premature garbage collection
+ if (PHP_VERSION_ID >= 70000) {
+ $this->readRefs[$key] = $stream;
+ }
+ }
+
+ public function addWriteStream($stream, $listener)
+ {
+ $key = (int) $stream;
+ if (isset($this->writeListeners[$key])) {
+ return;
+ }
+
+ $event = new Event($this->eventBase, $stream, Event::PERSIST | Event::WRITE, $this->streamCallback);
+ $event->add();
+ $this->writeEvents[$key] = $event;
+ $this->writeListeners[$key] = $listener;
+
+ // ext-event does not increase refcount on stream resources for PHP 7+
+ // manually keep track of stream resource to prevent premature garbage collection
+ if (PHP_VERSION_ID >= 70000) {
+ $this->writeRefs[$key] = $stream;
+ }
+ }
+
+ public function removeReadStream($stream)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->readEvents[$key])) {
+ $this->readEvents[$key]->free();
+ unset(
+ $this->readEvents[$key],
+ $this->readListeners[$key],
+ $this->readRefs[$key]
+ );
+ }
+ }
+
+ public function removeWriteStream($stream)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->writeEvents[$key])) {
+ $this->writeEvents[$key]->free();
+ unset(
+ $this->writeEvents[$key],
+ $this->writeListeners[$key],
+ $this->writeRefs[$key]
+ );
+ }
+ }
+
+ public function addTimer($interval, $callback)
+ {
+ $timer = new Timer($interval, $callback, false);
+
+ $this->scheduleTimer($timer);
+
+ return $timer;
+ }
+
+ public function addPeriodicTimer($interval, $callback)
+ {
+ $timer = new Timer($interval, $callback, true);
+
+ $this->scheduleTimer($timer);
+
+ return $timer;
+ }
+
+ public function cancelTimer(TimerInterface $timer)
+ {
+ if ($this->timerEvents->contains($timer)) {
+ $this->timerEvents[$timer]->free();
+ $this->timerEvents->detach($timer);
+ }
+ }
+
+ public function futureTick($listener)
+ {
+ $this->futureTickQueue->add($listener);
+ }
+
+ public function addSignal($signal, $listener)
+ {
+ $this->signals->add($signal, $listener);
+
+ if (!isset($this->signalEvents[$signal])) {
+ $this->signalEvents[$signal] = Event::signal($this->eventBase, $signal, array($this->signals, 'call'));
+ $this->signalEvents[$signal]->add();
+ }
+ }
+
+ public function removeSignal($signal, $listener)
+ {
+ $this->signals->remove($signal, $listener);
+
+ if (isset($this->signalEvents[$signal]) && $this->signals->count($signal) === 0) {
+ $this->signalEvents[$signal]->free();
+ unset($this->signalEvents[$signal]);
+ }
+ }
+
+ public function run()
+ {
+ $this->running = true;
+
+ while ($this->running) {
+ $this->futureTickQueue->tick();
+
+ $flags = EventBase::LOOP_ONCE;
+ if (!$this->running || !$this->futureTickQueue->isEmpty()) {
+ $flags |= EventBase::LOOP_NONBLOCK;
+ } elseif (!$this->readEvents && !$this->writeEvents && !$this->timerEvents->count() && $this->signals->isEmpty()) {
+ break;
+ }
+
+ $this->eventBase->loop($flags);
+ }
+ }
+
+ public function stop()
+ {
+ $this->running = false;
+ }
+
+ /**
+ * Schedule a timer for execution.
+ *
+ * @param TimerInterface $timer
+ */
+ private function scheduleTimer(TimerInterface $timer)
+ {
+ $flags = Event::TIMEOUT;
+
+ if ($timer->isPeriodic()) {
+ $flags |= Event::PERSIST;
+ }
+
+ $event = new Event($this->eventBase, -1, $flags, $this->timerCallback, $timer);
+ $this->timerEvents[$timer] = $event;
+
+ $event->add($timer->getInterval());
+ }
+
+ /**
+ * Create a callback used as the target of timer events.
+ *
+ * A reference is kept to the callback for the lifetime of the loop
+ * to prevent "Cannot destroy active lambda function" fatal error from
+ * the event extension.
+ */
+ private function createTimerCallback()
+ {
+ $timers = $this->timerEvents;
+ $this->timerCallback = function ($_, $__, $timer) use ($timers) {
+ call_user_func($timer->getCallback(), $timer);
+
+ if (!$timer->isPeriodic() && $timers->contains($timer)) {
+ $this->cancelTimer($timer);
+ }
+ };
+ }
+
+ /**
+ * Create a callback used as the target of stream events.
+ *
+ * A reference is kept to the callback for the lifetime of the loop
+ * to prevent "Cannot destroy active lambda function" fatal error from
+ * the event extension.
+ */
+ private function createStreamCallback()
+ {
+ $read =& $this->readListeners;
+ $write =& $this->writeListeners;
+ $this->streamCallback = function ($stream, $flags) use (&$read, &$write) {
+ $key = (int) $stream;
+
+ if (Event::READ === (Event::READ & $flags) && isset($read[$key])) {
+ call_user_func($read[$key], $stream);
+ }
+
+ if (Event::WRITE === (Event::WRITE & $flags) && isset($write[$key])) {
+ call_user_func($write[$key], $stream);
+ }
+ };
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/ExtLibevLoop.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/ExtLibevLoop.php
new file mode 100644
index 0000000..d3b0df8
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/ExtLibevLoop.php
@@ -0,0 +1,199 @@
+loop = new EventLoop();
+ $this->futureTickQueue = new FutureTickQueue();
+ $this->timerEvents = new SplObjectStorage();
+ $this->signals = new SignalsHandler();
+ }
+
+ public function addReadStream($stream, $listener)
+ {
+ if (isset($this->readEvents[(int) $stream])) {
+ return;
+ }
+
+ $callback = function () use ($stream, $listener) {
+ call_user_func($listener, $stream);
+ };
+
+ $event = new IOEvent($callback, $stream, IOEvent::READ);
+ $this->loop->add($event);
+
+ $this->readEvents[(int) $stream] = $event;
+ }
+
+ public function addWriteStream($stream, $listener)
+ {
+ if (isset($this->writeEvents[(int) $stream])) {
+ return;
+ }
+
+ $callback = function () use ($stream, $listener) {
+ call_user_func($listener, $stream);
+ };
+
+ $event = new IOEvent($callback, $stream, IOEvent::WRITE);
+ $this->loop->add($event);
+
+ $this->writeEvents[(int) $stream] = $event;
+ }
+
+ public function removeReadStream($stream)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->readEvents[$key])) {
+ $this->readEvents[$key]->stop();
+ $this->loop->remove($this->readEvents[$key]);
+ unset($this->readEvents[$key]);
+ }
+ }
+
+ public function removeWriteStream($stream)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->writeEvents[$key])) {
+ $this->writeEvents[$key]->stop();
+ $this->loop->remove($this->writeEvents[$key]);
+ unset($this->writeEvents[$key]);
+ }
+ }
+
+ public function addTimer($interval, $callback)
+ {
+ $timer = new Timer( $interval, $callback, false);
+
+ $that = $this;
+ $timers = $this->timerEvents;
+ $callback = function () use ($timer, $timers, $that) {
+ call_user_func($timer->getCallback(), $timer);
+
+ if ($timers->contains($timer)) {
+ $that->cancelTimer($timer);
+ }
+ };
+
+ $event = new TimerEvent($callback, $timer->getInterval());
+ $this->timerEvents->attach($timer, $event);
+ $this->loop->add($event);
+
+ return $timer;
+ }
+
+ public function addPeriodicTimer($interval, $callback)
+ {
+ $timer = new Timer($interval, $callback, true);
+
+ $callback = function () use ($timer) {
+ call_user_func($timer->getCallback(), $timer);
+ };
+
+ $event = new TimerEvent($callback, $interval, $interval);
+ $this->timerEvents->attach($timer, $event);
+ $this->loop->add($event);
+
+ return $timer;
+ }
+
+ public function cancelTimer(TimerInterface $timer)
+ {
+ if (isset($this->timerEvents[$timer])) {
+ $this->loop->remove($this->timerEvents[$timer]);
+ $this->timerEvents->detach($timer);
+ }
+ }
+
+ public function futureTick($listener)
+ {
+ $this->futureTickQueue->add($listener);
+ }
+
+ public function addSignal($signal, $listener)
+ {
+ $this->signals->add($signal, $listener);
+
+ if (!isset($this->signalEvents[$signal])) {
+ $signals = $this->signals;
+ $this->signalEvents[$signal] = new SignalEvent(function () use ($signals, $signal) {
+ $signals->call($signal);
+ }, $signal);
+ $this->loop->add($this->signalEvents[$signal]);
+ }
+ }
+
+ public function removeSignal($signal, $listener)
+ {
+ $this->signals->remove($signal, $listener);
+
+ if (isset($this->signalEvents[$signal]) && $this->signals->count($signal) === 0) {
+ $this->signalEvents[$signal]->stop();
+ $this->loop->remove($this->signalEvents[$signal]);
+ unset($this->signalEvents[$signal]);
+ }
+ }
+
+ public function run()
+ {
+ $this->running = true;
+
+ while ($this->running) {
+ $this->futureTickQueue->tick();
+
+ $flags = EventLoop::RUN_ONCE;
+ if (!$this->running || !$this->futureTickQueue->isEmpty()) {
+ $flags |= EventLoop::RUN_NOWAIT;
+ } elseif (!$this->readEvents && !$this->writeEvents && !$this->timerEvents->count() && $this->signals->isEmpty()) {
+ break;
+ }
+
+ $this->loop->run($flags);
+ }
+ }
+
+ public function stop()
+ {
+ $this->running = false;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/ExtLibeventLoop.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/ExtLibeventLoop.php
new file mode 100644
index 0000000..427f8db
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/ExtLibeventLoop.php
@@ -0,0 +1,283 @@
+eventBase = event_base_new();
+ $this->futureTickQueue = new FutureTickQueue();
+ $this->timerEvents = new SplObjectStorage();
+ $this->signals = new SignalsHandler();
+
+ $this->createTimerCallback();
+ $this->createStreamCallback();
+ }
+
+ public function addReadStream($stream, $listener)
+ {
+ $key = (int) $stream;
+ if (isset($this->readListeners[$key])) {
+ return;
+ }
+
+ $event = event_new();
+ event_set($event, $stream, EV_PERSIST | EV_READ, $this->streamCallback);
+ event_base_set($event, $this->eventBase);
+ event_add($event);
+
+ $this->readEvents[$key] = $event;
+ $this->readListeners[$key] = $listener;
+ }
+
+ public function addWriteStream($stream, $listener)
+ {
+ $key = (int) $stream;
+ if (isset($this->writeListeners[$key])) {
+ return;
+ }
+
+ $event = event_new();
+ event_set($event, $stream, EV_PERSIST | EV_WRITE, $this->streamCallback);
+ event_base_set($event, $this->eventBase);
+ event_add($event);
+
+ $this->writeEvents[$key] = $event;
+ $this->writeListeners[$key] = $listener;
+ }
+
+ public function removeReadStream($stream)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->readListeners[$key])) {
+ $event = $this->readEvents[$key];
+ event_del($event);
+ event_free($event);
+
+ unset(
+ $this->readEvents[$key],
+ $this->readListeners[$key]
+ );
+ }
+ }
+
+ public function removeWriteStream($stream)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->writeListeners[$key])) {
+ $event = $this->writeEvents[$key];
+ event_del($event);
+ event_free($event);
+
+ unset(
+ $this->writeEvents[$key],
+ $this->writeListeners[$key]
+ );
+ }
+ }
+
+ public function addTimer($interval, $callback)
+ {
+ $timer = new Timer($interval, $callback, false);
+
+ $this->scheduleTimer($timer);
+
+ return $timer;
+ }
+
+ public function addPeriodicTimer($interval, $callback)
+ {
+ $timer = new Timer($interval, $callback, true);
+
+ $this->scheduleTimer($timer);
+
+ return $timer;
+ }
+
+ public function cancelTimer(TimerInterface $timer)
+ {
+ if ($this->timerEvents->contains($timer)) {
+ $event = $this->timerEvents[$timer];
+ event_del($event);
+ event_free($event);
+
+ $this->timerEvents->detach($timer);
+ }
+ }
+
+ public function futureTick($listener)
+ {
+ $this->futureTickQueue->add($listener);
+ }
+
+ public function addSignal($signal, $listener)
+ {
+ $this->signals->add($signal, $listener);
+
+ if (!isset($this->signalEvents[$signal])) {
+ $this->signalEvents[$signal] = event_new();
+ event_set($this->signalEvents[$signal], $signal, EV_PERSIST | EV_SIGNAL, array($this->signals, 'call'));
+ event_base_set($this->signalEvents[$signal], $this->eventBase);
+ event_add($this->signalEvents[$signal]);
+ }
+ }
+
+ public function removeSignal($signal, $listener)
+ {
+ $this->signals->remove($signal, $listener);
+
+ if (isset($this->signalEvents[$signal]) && $this->signals->count($signal) === 0) {
+ event_del($this->signalEvents[$signal]);
+ event_free($this->signalEvents[$signal]);
+ unset($this->signalEvents[$signal]);
+ }
+ }
+
+ public function run()
+ {
+ $this->running = true;
+
+ while ($this->running) {
+ $this->futureTickQueue->tick();
+
+ $flags = EVLOOP_ONCE;
+ if (!$this->running || !$this->futureTickQueue->isEmpty()) {
+ $flags |= EVLOOP_NONBLOCK;
+ } elseif (!$this->readEvents && !$this->writeEvents && !$this->timerEvents->count() && $this->signals->isEmpty()) {
+ break;
+ }
+
+ event_base_loop($this->eventBase, $flags);
+ }
+ }
+
+ public function stop()
+ {
+ $this->running = false;
+ }
+
+ /**
+ * Schedule a timer for execution.
+ *
+ * @param TimerInterface $timer
+ */
+ private function scheduleTimer(TimerInterface $timer)
+ {
+ $this->timerEvents[$timer] = $event = event_timer_new();
+
+ event_timer_set($event, $this->timerCallback, $timer);
+ event_base_set($event, $this->eventBase);
+ event_add($event, $timer->getInterval() * self::MICROSECONDS_PER_SECOND);
+ }
+
+ /**
+ * Create a callback used as the target of timer events.
+ *
+ * A reference is kept to the callback for the lifetime of the loop
+ * to prevent "Cannot destroy active lambda function" fatal error from
+ * the event extension.
+ */
+ private function createTimerCallback()
+ {
+ $that = $this;
+ $timers = $this->timerEvents;
+ $this->timerCallback = function ($_, $__, $timer) use ($timers, $that) {
+ call_user_func($timer->getCallback(), $timer);
+
+ // Timer already cancelled ...
+ if (!$timers->contains($timer)) {
+ return;
+ }
+
+ // Reschedule periodic timers ...
+ if ($timer->isPeriodic()) {
+ event_add(
+ $timers[$timer],
+ $timer->getInterval() * ExtLibeventLoop::MICROSECONDS_PER_SECOND
+ );
+
+ // Clean-up one shot timers ...
+ } else {
+ $that->cancelTimer($timer);
+ }
+ };
+ }
+
+ /**
+ * Create a callback used as the target of stream events.
+ *
+ * A reference is kept to the callback for the lifetime of the loop
+ * to prevent "Cannot destroy active lambda function" fatal error from
+ * the event extension.
+ */
+ private function createStreamCallback()
+ {
+ $read =& $this->readListeners;
+ $write =& $this->writeListeners;
+ $this->streamCallback = function ($stream, $flags) use (&$read, &$write) {
+ $key = (int) $stream;
+
+ if (EV_READ === (EV_READ & $flags) && isset($read[$key])) {
+ call_user_func($read[$key], $stream);
+ }
+
+ if (EV_WRITE === (EV_WRITE & $flags) && isset($write[$key])) {
+ call_user_func($write[$key], $stream);
+ }
+ };
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/Factory.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/Factory.php
new file mode 100644
index 0000000..b46fc07
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/Factory.php
@@ -0,0 +1,41 @@
+addReadStream($stream, function ($stream) use ($name) {
+ * echo $name . ' said: ' . fread($stream);
+ * });
+ * ```
+ *
+ * See also [example #11](examples).
+ *
+ * You can invoke [`removeReadStream()`](#removereadstream) to remove the
+ * read event listener for this stream.
+ *
+ * The execution order of listeners when multiple streams become ready at
+ * the same time is not guaranteed.
+ *
+ * @param resource $stream The PHP stream resource to check.
+ * @param callable $listener Invoked when the stream is ready.
+ * @throws \Exception if the given resource type is not supported by this loop implementation
+ * @see self::removeReadStream()
+ */
+ public function addReadStream($stream, $listener);
+
+ /**
+ * [Advanced] Register a listener to be notified when a stream is ready to write.
+ *
+ * Note that this low-level API is considered advanced usage.
+ * Most use cases should probably use the higher-level
+ * [writable Stream API](https://github.com/reactphp/stream#writablestreaminterface)
+ * instead.
+ *
+ * The first parameter MUST be a valid stream resource that supports
+ * checking whether it is ready to write by this loop implementation.
+ * A single stream resource MUST NOT be added more than once.
+ * Instead, either call [`removeWriteStream()`](#removewritestream) first or
+ * react to this event with a single listener and then dispatch from this
+ * listener. This method MAY throw an `Exception` if the given resource type
+ * is not supported by this loop implementation.
+ *
+ * The listener callback function MUST be able to accept a single parameter,
+ * the stream resource added by this method or you MAY use a function which
+ * has no parameters at all.
+ *
+ * The listener callback function MUST NOT throw an `Exception`.
+ * The return value of the listener callback function will be ignored and has
+ * no effect, so for performance reasons you're recommended to not return
+ * any excessive data structures.
+ *
+ * If you want to access any variables within your callback function, you
+ * can bind arbitrary data to a callback closure like this:
+ *
+ * ```php
+ * $loop->addWriteStream($stream, function ($stream) use ($name) {
+ * fwrite($stream, 'Hello ' . $name);
+ * });
+ * ```
+ *
+ * See also [example #12](examples).
+ *
+ * You can invoke [`removeWriteStream()`](#removewritestream) to remove the
+ * write event listener for this stream.
+ *
+ * The execution order of listeners when multiple streams become ready at
+ * the same time is not guaranteed.
+ *
+ * Some event loop implementations are known to only trigger the listener if
+ * the stream *becomes* readable (edge-triggered) and may not trigger if the
+ * stream has already been readable from the beginning.
+ * This also implies that a stream may not be recognized as readable when data
+ * is still left in PHP's internal stream buffers.
+ * As such, it's recommended to use `stream_set_read_buffer($stream, 0);`
+ * to disable PHP's internal read buffer in this case.
+ *
+ * @param resource $stream The PHP stream resource to check.
+ * @param callable $listener Invoked when the stream is ready.
+ * @throws \Exception if the given resource type is not supported by this loop implementation
+ * @see self::removeWriteStream()
+ */
+ public function addWriteStream($stream, $listener);
+
+ /**
+ * Remove the read event listener for the given stream.
+ *
+ * Removing a stream from the loop that has already been removed or trying
+ * to remove a stream that was never added or is invalid has no effect.
+ *
+ * @param resource $stream The PHP stream resource.
+ */
+ public function removeReadStream($stream);
+
+ /**
+ * Remove the write event listener for the given stream.
+ *
+ * Removing a stream from the loop that has already been removed or trying
+ * to remove a stream that was never added or is invalid has no effect.
+ *
+ * @param resource $stream The PHP stream resource.
+ */
+ public function removeWriteStream($stream);
+
+ /**
+ * Enqueue a callback to be invoked once after the given interval.
+ *
+ * The timer callback function MUST be able to accept a single parameter,
+ * the timer instance as also returned by this method or you MAY use a
+ * function which has no parameters at all.
+ *
+ * The timer callback function MUST NOT throw an `Exception`.
+ * The return value of the timer callback function will be ignored and has
+ * no effect, so for performance reasons you're recommended to not return
+ * any excessive data structures.
+ *
+ * Unlike [`addPeriodicTimer()`](#addperiodictimer), this method will ensure
+ * the callback will be invoked only once after the given interval.
+ * You can invoke [`cancelTimer`](#canceltimer) to cancel a pending timer.
+ *
+ * ```php
+ * $loop->addTimer(0.8, function () {
+ * echo 'world!' . PHP_EOL;
+ * });
+ *
+ * $loop->addTimer(0.3, function () {
+ * echo 'hello ';
+ * });
+ * ```
+ *
+ * See also [example #1](examples).
+ *
+ * If you want to access any variables within your callback function, you
+ * can bind arbitrary data to a callback closure like this:
+ *
+ * ```php
+ * function hello($name, LoopInterface $loop)
+ * {
+ * $loop->addTimer(1.0, function () use ($name) {
+ * echo "hello $name\n";
+ * });
+ * }
+ *
+ * hello('Tester', $loop);
+ * ```
+ *
+ * This interface does not enforce any particular timer resolution, so
+ * special care may have to be taken if you rely on very high precision with
+ * millisecond accuracy or below. Event loop implementations SHOULD work on
+ * a best effort basis and SHOULD provide at least millisecond accuracy
+ * unless otherwise noted. Many existing event loop implementations are
+ * known to provide microsecond accuracy, but it's generally not recommended
+ * to rely on this high precision.
+ *
+ * Similarly, the execution order of timers scheduled to execute at the
+ * same time (within its possible accuracy) is not guaranteed.
+ *
+ * This interface suggests that event loop implementations SHOULD use a
+ * monotonic time source if available. Given that a monotonic time source is
+ * not available on PHP by default, event loop implementations MAY fall back
+ * to using wall-clock time.
+ * While this does not affect many common use cases, this is an important
+ * distinction for programs that rely on a high time precision or on systems
+ * that are subject to discontinuous time adjustments (time jumps).
+ * This means that if you schedule a timer to trigger in 30s and then adjust
+ * your system time forward by 20s, the timer SHOULD still trigger in 30s.
+ * See also [event loop implementations](#loop-implementations) for more details.
+ *
+ * @param int|float $interval The number of seconds to wait before execution.
+ * @param callable $callback The callback to invoke.
+ *
+ * @return TimerInterface
+ */
+ public function addTimer($interval, $callback);
+
+ /**
+ * Enqueue a callback to be invoked repeatedly after the given interval.
+ *
+ * The timer callback function MUST be able to accept a single parameter,
+ * the timer instance as also returned by this method or you MAY use a
+ * function which has no parameters at all.
+ *
+ * The timer callback function MUST NOT throw an `Exception`.
+ * The return value of the timer callback function will be ignored and has
+ * no effect, so for performance reasons you're recommended to not return
+ * any excessive data structures.
+ *
+ * Unlike [`addTimer()`](#addtimer), this method will ensure the the
+ * callback will be invoked infinitely after the given interval or until you
+ * invoke [`cancelTimer`](#canceltimer).
+ *
+ * ```php
+ * $timer = $loop->addPeriodicTimer(0.1, function () {
+ * echo 'tick!' . PHP_EOL;
+ * });
+ *
+ * $loop->addTimer(1.0, function () use ($loop, $timer) {
+ * $loop->cancelTimer($timer);
+ * echo 'Done' . PHP_EOL;
+ * });
+ * ```
+ *
+ * See also [example #2](examples).
+ *
+ * If you want to limit the number of executions, you can bind
+ * arbitrary data to a callback closure like this:
+ *
+ * ```php
+ * function hello($name, LoopInterface $loop)
+ * {
+ * $n = 3;
+ * $loop->addPeriodicTimer(1.0, function ($timer) use ($name, $loop, &$n) {
+ * if ($n > 0) {
+ * --$n;
+ * echo "hello $name\n";
+ * } else {
+ * $loop->cancelTimer($timer);
+ * }
+ * });
+ * }
+ *
+ * hello('Tester', $loop);
+ * ```
+ *
+ * This interface does not enforce any particular timer resolution, so
+ * special care may have to be taken if you rely on very high precision with
+ * millisecond accuracy or below. Event loop implementations SHOULD work on
+ * a best effort basis and SHOULD provide at least millisecond accuracy
+ * unless otherwise noted. Many existing event loop implementations are
+ * known to provide microsecond accuracy, but it's generally not recommended
+ * to rely on this high precision.
+ *
+ * Similarly, the execution order of timers scheduled to execute at the
+ * same time (within its possible accuracy) is not guaranteed.
+ *
+ * This interface suggests that event loop implementations SHOULD use a
+ * monotonic time source if available. Given that a monotonic time source is
+ * not available on PHP by default, event loop implementations MAY fall back
+ * to using wall-clock time.
+ * While this does not affect many common use cases, this is an important
+ * distinction for programs that rely on a high time precision or on systems
+ * that are subject to discontinuous time adjustments (time jumps).
+ * This means that if you schedule a timer to trigger in 30s and then adjust
+ * your system time forward by 20s, the timer SHOULD still trigger in 30s.
+ * See also [event loop implementations](#loop-implementations) for more details.
+ *
+ * Additionally, periodic timers may be subject to timer drift due to
+ * re-scheduling after each invocation. As such, it's generally not
+ * recommended to rely on this for high precision intervals with millisecond
+ * accuracy or below.
+ *
+ * @param int|float $interval The number of seconds to wait before execution.
+ * @param callable $callback The callback to invoke.
+ *
+ * @return TimerInterface
+ */
+ public function addPeriodicTimer($interval, $callback);
+
+ /**
+ * Cancel a pending timer.
+ *
+ * See also [`addPeriodicTimer()`](#addperiodictimer) and [example #2](examples).
+ *
+ * Calling this method on a timer instance that has not been added to this
+ * loop instance or on a timer that has already been cancelled has no effect.
+ *
+ * @param TimerInterface $timer The timer to cancel.
+ *
+ * @return void
+ */
+ public function cancelTimer(TimerInterface $timer);
+
+ /**
+ * Schedule a callback to be invoked on a future tick of the event loop.
+ *
+ * This works very much similar to timers with an interval of zero seconds,
+ * but does not require the overhead of scheduling a timer queue.
+ *
+ * The tick callback function MUST be able to accept zero parameters.
+ *
+ * The tick callback function MUST NOT throw an `Exception`.
+ * The return value of the tick callback function will be ignored and has
+ * no effect, so for performance reasons you're recommended to not return
+ * any excessive data structures.
+ *
+ * If you want to access any variables within your callback function, you
+ * can bind arbitrary data to a callback closure like this:
+ *
+ * ```php
+ * function hello($name, LoopInterface $loop)
+ * {
+ * $loop->futureTick(function () use ($name) {
+ * echo "hello $name\n";
+ * });
+ * }
+ *
+ * hello('Tester', $loop);
+ * ```
+ *
+ * Unlike timers, tick callbacks are guaranteed to be executed in the order
+ * they are enqueued.
+ * Also, once a callback is enqueued, there's no way to cancel this operation.
+ *
+ * This is often used to break down bigger tasks into smaller steps (a form
+ * of cooperative multitasking).
+ *
+ * ```php
+ * $loop->futureTick(function () {
+ * echo 'b';
+ * });
+ * $loop->futureTick(function () {
+ * echo 'c';
+ * });
+ * echo 'a';
+ * ```
+ *
+ * See also [example #3](examples).
+ *
+ * @param callable $listener The callback to invoke.
+ *
+ * @return void
+ */
+ public function futureTick($listener);
+
+ /**
+ * Register a listener to be notified when a signal has been caught by this process.
+ *
+ * This is useful to catch user interrupt signals or shutdown signals from
+ * tools like `supervisor` or `systemd`.
+ *
+ * The listener callback function MUST be able to accept a single parameter,
+ * the signal added by this method or you MAY use a function which
+ * has no parameters at all.
+ *
+ * The listener callback function MUST NOT throw an `Exception`.
+ * The return value of the listener callback function will be ignored and has
+ * no effect, so for performance reasons you're recommended to not return
+ * any excessive data structures.
+ *
+ * ```php
+ * $loop->addSignal(SIGINT, function (int $signal) {
+ * echo 'Caught user interrupt signal' . PHP_EOL;
+ * });
+ * ```
+ *
+ * See also [example #4](examples).
+ *
+ * Signaling is only available on Unix-like platform, Windows isn't
+ * supported due to operating system limitations.
+ * This method may throw a `BadMethodCallException` if signals aren't
+ * supported on this platform, for example when required extensions are
+ * missing.
+ *
+ * **Note: A listener can only be added once to the same signal, any
+ * attempts to add it more then once will be ignored.**
+ *
+ * @param int $signal
+ * @param callable $listener
+ *
+ * @throws \BadMethodCallException when signals aren't supported on this
+ * platform, for example when required extensions are missing.
+ *
+ * @return void
+ */
+ public function addSignal($signal, $listener);
+
+ /**
+ * Removes a previously added signal listener.
+ *
+ * ```php
+ * $loop->removeSignal(SIGINT, $listener);
+ * ```
+ *
+ * Any attempts to remove listeners that aren't registered will be ignored.
+ *
+ * @param int $signal
+ * @param callable $listener
+ *
+ * @return void
+ */
+ public function removeSignal($signal, $listener);
+
+ /**
+ * Run the event loop until there are no more tasks to perform.
+ *
+ * For many applications, this method is the only directly visible
+ * invocation on the event loop.
+ * As a rule of thumb, it is usally recommended to attach everything to the
+ * same loop instance and then run the loop once at the bottom end of the
+ * application.
+ *
+ * ```php
+ * $loop->run();
+ * ```
+ *
+ * This method will keep the loop running until there are no more tasks
+ * to perform. In other words: This method will block until the last
+ * timer, stream and/or signal has been removed.
+ *
+ * Likewise, it is imperative to ensure the application actually invokes
+ * this method once. Adding listeners to the loop and missing to actually
+ * run it will result in the application exiting without actually waiting
+ * for any of the attached listeners.
+ *
+ * This method MUST NOT be called while the loop is already running.
+ * This method MAY be called more than once after it has explicity been
+ * [`stop()`ped](#stop) or after it automatically stopped because it
+ * previously did no longer have anything to do.
+ *
+ * @return void
+ */
+ public function run();
+
+ /**
+ * Instruct a running event loop to stop.
+ *
+ * This method is considered advanced usage and should be used with care.
+ * As a rule of thumb, it is usually recommended to let the loop stop
+ * only automatically when it no longer has anything to do.
+ *
+ * This method can be used to explicitly instruct the event loop to stop:
+ *
+ * ```php
+ * $loop->addTimer(3.0, function () use ($loop) {
+ * $loop->stop();
+ * });
+ * ```
+ *
+ * Calling this method on a loop instance that is not currently running or
+ * on a loop instance that has already been stopped has no effect.
+ *
+ * @return void
+ */
+ public function stop();
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/SignalsHandler.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/SignalsHandler.php
new file mode 100644
index 0000000..523e1ca
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/SignalsHandler.php
@@ -0,0 +1,63 @@
+signals[$signal])) {
+ $this->signals[$signal] = array();
+ }
+
+ if (in_array($listener, $this->signals[$signal])) {
+ return;
+ }
+
+ $this->signals[$signal][] = $listener;
+ }
+
+ public function remove($signal, $listener)
+ {
+ if (!isset($this->signals[$signal])) {
+ return;
+ }
+
+ $index = \array_search($listener, $this->signals[$signal], true);
+ unset($this->signals[$signal][$index]);
+
+ if (isset($this->signals[$signal]) && \count($this->signals[$signal]) === 0) {
+ unset($this->signals[$signal]);
+ }
+ }
+
+ public function call($signal)
+ {
+ if (!isset($this->signals[$signal])) {
+ return;
+ }
+
+ foreach ($this->signals[$signal] as $listener) {
+ \call_user_func($listener, $signal);
+ }
+ }
+
+ public function count($signal)
+ {
+ if (!isset($this->signals[$signal])) {
+ return 0;
+ }
+
+ return \count($this->signals[$signal]);
+ }
+
+ public function isEmpty()
+ {
+ return !$this->signals;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/StreamSelectLoop.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/StreamSelectLoop.php
new file mode 100644
index 0000000..e82e9e4
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/StreamSelectLoop.php
@@ -0,0 +1,275 @@
+futureTickQueue = new FutureTickQueue();
+ $this->timers = new Timers();
+ $this->pcntl = extension_loaded('pcntl');
+ $this->signals = new SignalsHandler();
+ }
+
+ public function addReadStream($stream, $listener)
+ {
+ $key = (int) $stream;
+
+ if (!isset($this->readStreams[$key])) {
+ $this->readStreams[$key] = $stream;
+ $this->readListeners[$key] = $listener;
+ }
+ }
+
+ public function addWriteStream($stream, $listener)
+ {
+ $key = (int) $stream;
+
+ if (!isset($this->writeStreams[$key])) {
+ $this->writeStreams[$key] = $stream;
+ $this->writeListeners[$key] = $listener;
+ }
+ }
+
+ public function removeReadStream($stream)
+ {
+ $key = (int) $stream;
+
+ unset(
+ $this->readStreams[$key],
+ $this->readListeners[$key]
+ );
+ }
+
+ public function removeWriteStream($stream)
+ {
+ $key = (int) $stream;
+
+ unset(
+ $this->writeStreams[$key],
+ $this->writeListeners[$key]
+ );
+ }
+
+ public function addTimer($interval, $callback)
+ {
+ $timer = new Timer($interval, $callback, false);
+
+ $this->timers->add($timer);
+
+ return $timer;
+ }
+
+ public function addPeriodicTimer($interval, $callback)
+ {
+ $timer = new Timer($interval, $callback, true);
+
+ $this->timers->add($timer);
+
+ return $timer;
+ }
+
+ public function cancelTimer(TimerInterface $timer)
+ {
+ $this->timers->cancel($timer);
+ }
+
+ public function futureTick($listener)
+ {
+ $this->futureTickQueue->add($listener);
+ }
+
+ public function addSignal($signal, $listener)
+ {
+ if ($this->pcntl === false) {
+ throw new \BadMethodCallException('Event loop feature "signals" isn\'t supported by the "StreamSelectLoop"');
+ }
+
+ $first = $this->signals->count($signal) === 0;
+ $this->signals->add($signal, $listener);
+
+ if ($first) {
+ \pcntl_signal($signal, array($this->signals, 'call'));
+ }
+ }
+
+ public function removeSignal($signal, $listener)
+ {
+ if (!$this->signals->count($signal)) {
+ return;
+ }
+
+ $this->signals->remove($signal, $listener);
+
+ if ($this->signals->count($signal) === 0) {
+ \pcntl_signal($signal, SIG_DFL);
+ }
+ }
+
+ public function run()
+ {
+ $this->running = true;
+
+ while ($this->running) {
+ $this->futureTickQueue->tick();
+
+ $this->timers->tick();
+
+ // Future-tick queue has pending callbacks ...
+ if (!$this->running || !$this->futureTickQueue->isEmpty()) {
+ $timeout = 0;
+
+ // There is a pending timer, only block until it is due ...
+ } elseif ($scheduledAt = $this->timers->getFirst()) {
+ $timeout = $scheduledAt - $this->timers->getTime();
+ if ($timeout < 0) {
+ $timeout = 0;
+ } else {
+ // Convert float seconds to int microseconds.
+ // Ensure we do not exceed maximum integer size, which may
+ // cause the loop to tick once every ~35min on 32bit systems.
+ $timeout *= self::MICROSECONDS_PER_SECOND;
+ $timeout = $timeout > PHP_INT_MAX ? PHP_INT_MAX : (int)$timeout;
+ }
+
+ // The only possible event is stream or signal activity, so wait forever ...
+ } elseif ($this->readStreams || $this->writeStreams || !$this->signals->isEmpty()) {
+ $timeout = null;
+
+ // There's nothing left to do ...
+ } else {
+ break;
+ }
+
+ $this->waitForStreamActivity($timeout);
+ }
+ }
+
+ public function stop()
+ {
+ $this->running = false;
+ }
+
+ /**
+ * Wait/check for stream activity, or until the next timer is due.
+ *
+ * @param integer|null $timeout Activity timeout in microseconds, or null to wait forever.
+ */
+ private function waitForStreamActivity($timeout)
+ {
+ $read = $this->readStreams;
+ $write = $this->writeStreams;
+
+ $available = $this->streamSelect($read, $write, $timeout);
+ if ($this->pcntl) {
+ \pcntl_signal_dispatch();
+ }
+ if (false === $available) {
+ // if a system call has been interrupted,
+ // we cannot rely on it's outcome
+ return;
+ }
+
+ foreach ($read as $stream) {
+ $key = (int) $stream;
+
+ if (isset($this->readListeners[$key])) {
+ call_user_func($this->readListeners[$key], $stream);
+ }
+ }
+
+ foreach ($write as $stream) {
+ $key = (int) $stream;
+
+ if (isset($this->writeListeners[$key])) {
+ call_user_func($this->writeListeners[$key], $stream);
+ }
+ }
+ }
+
+ /**
+ * Emulate a stream_select() implementation that does not break when passed
+ * empty stream arrays.
+ *
+ * @param array &$read An array of read streams to select upon.
+ * @param array &$write An array of write streams to select upon.
+ * @param integer|null $timeout Activity timeout in microseconds, or null to wait forever.
+ *
+ * @return integer|false The total number of streams that are ready for read/write.
+ * Can return false if stream_select() is interrupted by a signal.
+ */
+ private function streamSelect(array &$read, array &$write, $timeout)
+ {
+ if ($read || $write) {
+ $except = null;
+
+ // suppress warnings that occur, when stream_select is interrupted by a signal
+ return @stream_select($read, $write, $except, $timeout === null ? null : 0, $timeout);
+ }
+
+ $timeout && usleep($timeout);
+
+ return 0;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/Tick/FutureTickQueue.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/Tick/FutureTickQueue.php
new file mode 100644
index 0000000..c79afc5
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/Tick/FutureTickQueue.php
@@ -0,0 +1,60 @@
+queue = new SplQueue();
+ }
+
+ /**
+ * Add a callback to be invoked on a future tick of the event loop.
+ *
+ * Callbacks are guaranteed to be executed in the order they are enqueued.
+ *
+ * @param callable $listener The callback to invoke.
+ */
+ public function add($listener)
+ {
+ $this->queue->enqueue($listener);
+ }
+
+ /**
+ * Flush the callback queue.
+ */
+ public function tick()
+ {
+ // Only invoke as many callbacks as were on the queue when tick() was called.
+ $count = $this->queue->count();
+
+ while ($count--) {
+ call_user_func(
+ $this->queue->dequeue()
+ );
+ }
+ }
+
+ /**
+ * Check if the next tick queue is empty.
+ *
+ * @return boolean
+ */
+ public function isEmpty()
+ {
+ return $this->queue->isEmpty();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/Timer/Timer.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/Timer/Timer.php
new file mode 100644
index 0000000..da3602a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/Timer/Timer.php
@@ -0,0 +1,55 @@
+interval = (float) $interval;
+ $this->callback = $callback;
+ $this->periodic = (bool) $periodic;
+ }
+
+ public function getInterval()
+ {
+ return $this->interval;
+ }
+
+ public function getCallback()
+ {
+ return $this->callback;
+ }
+
+ public function isPeriodic()
+ {
+ return $this->periodic;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/Timer/Timers.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/Timer/Timers.php
new file mode 100644
index 0000000..17bbdac
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/Timer/Timers.php
@@ -0,0 +1,109 @@
+timers = new SplObjectStorage();
+ $this->scheduler = new SplPriorityQueue();
+ }
+
+ public function updateTime()
+ {
+ return $this->time = microtime(true);
+ }
+
+ public function getTime()
+ {
+ return $this->time ?: $this->updateTime();
+ }
+
+ public function add(TimerInterface $timer)
+ {
+ $interval = $timer->getInterval();
+ $scheduledAt = $interval + microtime(true);
+
+ $this->timers->attach($timer, $scheduledAt);
+ $this->scheduler->insert($timer, -$scheduledAt);
+ }
+
+ public function contains(TimerInterface $timer)
+ {
+ return $this->timers->contains($timer);
+ }
+
+ public function cancel(TimerInterface $timer)
+ {
+ $this->timers->detach($timer);
+ }
+
+ public function getFirst()
+ {
+ while ($this->scheduler->count()) {
+ $timer = $this->scheduler->top();
+
+ if ($this->timers->contains($timer)) {
+ return $this->timers[$timer];
+ }
+
+ $this->scheduler->extract();
+ }
+
+ return null;
+ }
+
+ public function isEmpty()
+ {
+ return count($this->timers) === 0;
+ }
+
+ public function tick()
+ {
+ $time = $this->updateTime();
+ $timers = $this->timers;
+ $scheduler = $this->scheduler;
+
+ while (!$scheduler->isEmpty()) {
+ $timer = $scheduler->top();
+
+ if (!isset($timers[$timer])) {
+ $scheduler->extract();
+ $timers->detach($timer);
+
+ continue;
+ }
+
+ if ($timers[$timer] >= $time) {
+ break;
+ }
+
+ $scheduler->extract();
+ call_user_func($timer->getCallback(), $timer);
+
+ if ($timer->isPeriodic() && isset($timers[$timer])) {
+ $timers[$timer] = $scheduledAt = $timer->getInterval() + $time;
+ $scheduler->insert($timer, -$scheduledAt);
+ } else {
+ $timers->detach($timer);
+ }
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/TimerInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/TimerInterface.php
new file mode 100644
index 0000000..cdcf773
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/src/TimerInterface.php
@@ -0,0 +1,27 @@
+tickTimeout = 0.02;
+ $this->loop = $this->createLoop();
+ }
+
+ abstract public function createLoop();
+
+ public function createSocketPair()
+ {
+ $domain = (DIRECTORY_SEPARATOR === '\\') ? STREAM_PF_INET : STREAM_PF_UNIX;
+ $sockets = stream_socket_pair($domain, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
+
+ foreach ($sockets as $socket) {
+ if (function_exists('stream_set_read_buffer')) {
+ stream_set_read_buffer($socket, 0);
+ }
+ }
+
+ return $sockets;
+ }
+
+ public function testAddReadStream()
+ {
+ list ($input, $output) = $this->createSocketPair();
+
+ $this->loop->addReadStream($input, $this->expectCallableExactly(2));
+
+ fwrite($output, "foo\n");
+ $this->tickLoop($this->loop);
+
+ fwrite($output, "bar\n");
+ $this->tickLoop($this->loop);
+ }
+
+ public function testAddReadStreamIgnoresSecondCallable()
+ {
+ list ($input, $output) = $this->createSocketPair();
+
+ $this->loop->addReadStream($input, $this->expectCallableExactly(2));
+ $this->loop->addReadStream($input, $this->expectCallableNever());
+
+ fwrite($output, "foo\n");
+ $this->tickLoop($this->loop);
+
+ fwrite($output, "bar\n");
+ $this->tickLoop($this->loop);
+ }
+
+ public function testAddReadStreamReceivesDataFromStreamReference()
+ {
+ $this->received = '';
+ $this->subAddReadStreamReceivesDataFromStreamReference();
+ $this->assertEquals('', $this->received);
+
+ $this->assertRunFasterThan($this->tickTimeout * 2);
+ $this->assertEquals('[hello]X', $this->received);
+ }
+
+ /**
+ * Helper for above test. This happens in another helper method to verify
+ * the loop keeps track of assigned stream resources (refcount).
+ */
+ private function subAddReadStreamReceivesDataFromStreamReference()
+ {
+ list ($input, $output) = $this->createSocketPair();
+
+ fwrite($input, 'hello');
+ fclose($input);
+
+ $loop = $this->loop;
+ $received =& $this->received;
+ $loop->addReadStream($output, function ($output) use ($loop, &$received) {
+ $chunk = fread($output, 1024);
+ if ($chunk === '') {
+ $received .= 'X';
+ $loop->removeReadStream($output);
+ fclose($output);
+ } else {
+ $received .= '[' . $chunk . ']';
+ }
+ });
+ }
+
+ public function testAddWriteStream()
+ {
+ list ($input) = $this->createSocketPair();
+
+ $this->loop->addWriteStream($input, $this->expectCallableExactly(2));
+ $this->tickLoop($this->loop);
+ $this->tickLoop($this->loop);
+ }
+
+ public function testAddWriteStreamIgnoresSecondCallable()
+ {
+ list ($input) = $this->createSocketPair();
+
+ $this->loop->addWriteStream($input, $this->expectCallableExactly(2));
+ $this->loop->addWriteStream($input, $this->expectCallableNever());
+ $this->tickLoop($this->loop);
+ $this->tickLoop($this->loop);
+ }
+
+ public function testRemoveReadStreamInstantly()
+ {
+ list ($input, $output) = $this->createSocketPair();
+
+ $this->loop->addReadStream($input, $this->expectCallableNever());
+ $this->loop->removeReadStream($input);
+
+ fwrite($output, "bar\n");
+ $this->tickLoop($this->loop);
+ }
+
+ public function testRemoveReadStreamAfterReading()
+ {
+ list ($input, $output) = $this->createSocketPair();
+
+ $this->loop->addReadStream($input, $this->expectCallableOnce());
+
+ fwrite($output, "foo\n");
+ $this->tickLoop($this->loop);
+
+ $this->loop->removeReadStream($input);
+
+ fwrite($output, "bar\n");
+ $this->tickLoop($this->loop);
+ }
+
+ public function testRemoveWriteStreamInstantly()
+ {
+ list ($input) = $this->createSocketPair();
+
+ $this->loop->addWriteStream($input, $this->expectCallableNever());
+ $this->loop->removeWriteStream($input);
+ $this->tickLoop($this->loop);
+ }
+
+ public function testRemoveWriteStreamAfterWriting()
+ {
+ list ($input) = $this->createSocketPair();
+
+ $this->loop->addWriteStream($input, $this->expectCallableOnce());
+ $this->tickLoop($this->loop);
+
+ $this->loop->removeWriteStream($input);
+ $this->tickLoop($this->loop);
+ }
+
+ public function testRemoveStreamForReadOnly()
+ {
+ list ($input, $output) = $this->createSocketPair();
+
+ $this->loop->addReadStream($input, $this->expectCallableNever());
+ $this->loop->addWriteStream($output, $this->expectCallableOnce());
+ $this->loop->removeReadStream($input);
+
+ fwrite($output, "foo\n");
+ $this->tickLoop($this->loop);
+ }
+
+ public function testRemoveStreamForWriteOnly()
+ {
+ list ($input, $output) = $this->createSocketPair();
+
+ fwrite($output, "foo\n");
+
+ $this->loop->addReadStream($input, $this->expectCallableOnce());
+ $this->loop->addWriteStream($output, $this->expectCallableNever());
+ $this->loop->removeWriteStream($output);
+
+ $this->tickLoop($this->loop);
+ }
+
+ public function testRemoveReadAndWriteStreamFromLoopOnceResourceClosesEndsLoop()
+ {
+ list($stream, $other) = $this->createSocketPair();
+ stream_set_blocking($stream, false);
+ stream_set_blocking($other, false);
+
+ // dummy writable handler
+ $this->loop->addWriteStream($stream, function () { });
+
+ // remove stream when the stream is readable (closes)
+ $loop = $this->loop;
+ $loop->addReadStream($stream, function ($stream) use ($loop) {
+ $loop->removeReadStream($stream);
+ $loop->removeWriteStream($stream);
+ fclose($stream);
+ });
+
+ // close other side
+ fclose($other);
+
+ $this->assertRunFasterThan($this->tickTimeout);
+ }
+
+ public function testRemoveReadAndWriteStreamFromLoopOnceResourceClosesOnEndOfFileEndsLoop()
+ {
+ list($stream, $other) = $this->createSocketPair();
+ stream_set_blocking($stream, false);
+ stream_set_blocking($other, false);
+
+ // dummy writable handler
+ $this->loop->addWriteStream($stream, function () { });
+
+ // remove stream when the stream is readable (closes)
+ $loop = $this->loop;
+ $loop->addReadStream($stream, function ($stream) use ($loop) {
+ $data = fread($stream, 1024);
+ if ($data !== '') {
+ return;
+ }
+
+ $loop->removeReadStream($stream);
+ $loop->removeWriteStream($stream);
+ fclose($stream);
+ });
+
+ // send data and close stream
+ fwrite($other, str_repeat('.', static::PHP_DEFAULT_CHUNK_SIZE));
+ $this->loop->addTimer(0.01, function () use ($other) {
+ fclose($other);
+ });
+
+ $this->assertRunFasterThan(0.1);
+ }
+
+ public function testRemoveReadAndWriteStreamFromLoopWithClosingResourceEndsLoop()
+ {
+ // get only one part of the pair to ensure the other side will close immediately
+ list($stream) = $this->createSocketPair();
+ stream_set_blocking($stream, false);
+
+ // dummy writable handler
+ $this->loop->addWriteStream($stream, function () { });
+
+ // remove stream when the stream is readable (closes)
+ $loop = $this->loop;
+ $loop->addReadStream($stream, function ($stream) use ($loop) {
+ $loop->removeReadStream($stream);
+ $loop->removeWriteStream($stream);
+ fclose($stream);
+ });
+
+ $this->assertRunFasterThan($this->tickTimeout);
+ }
+
+ public function testRemoveInvalid()
+ {
+ list ($stream) = $this->createSocketPair();
+
+ // remove a valid stream from the event loop that was never added in the first place
+ $this->loop->removeReadStream($stream);
+ $this->loop->removeWriteStream($stream);
+
+ $this->assertTrue(true);
+ }
+
+ /** @test */
+ public function emptyRunShouldSimplyReturn()
+ {
+ $this->assertRunFasterThan($this->tickTimeout);
+ }
+
+ /** @test */
+ public function runShouldReturnWhenNoMoreFds()
+ {
+ list ($input, $output) = $this->createSocketPair();
+
+ $loop = $this->loop;
+ $this->loop->addReadStream($input, function ($stream) use ($loop) {
+ $loop->removeReadStream($stream);
+ });
+
+ fwrite($output, "foo\n");
+
+ $this->assertRunFasterThan($this->tickTimeout * 2);
+ }
+
+ /** @test */
+ public function stopShouldStopRunningLoop()
+ {
+ list ($input, $output) = $this->createSocketPair();
+
+ $loop = $this->loop;
+ $this->loop->addReadStream($input, function ($stream) use ($loop) {
+ $loop->stop();
+ });
+
+ fwrite($output, "foo\n");
+
+ $this->assertRunFasterThan($this->tickTimeout * 2);
+ }
+
+ public function testStopShouldPreventRunFromBlocking()
+ {
+ $that = $this;
+ $this->loop->addTimer(
+ 1,
+ function () use ($that) {
+ $that->fail('Timer was executed.');
+ }
+ );
+
+ $loop = $this->loop;
+ $this->loop->futureTick(
+ function () use ($loop) {
+ $loop->stop();
+ }
+ );
+
+ $this->assertRunFasterThan($this->tickTimeout * 2);
+ }
+
+ public function testIgnoreRemovedCallback()
+ {
+ // two independent streams, both should be readable right away
+ list ($input1, $output1) = $this->createSocketPair();
+ list ($input2, $output2) = $this->createSocketPair();
+
+ $called = false;
+
+ $loop = $this->loop;
+ $loop->addReadStream($input1, function ($stream) use (& $called, $loop, $input2) {
+ // stream1 is readable, remove stream2 as well => this will invalidate its callback
+ $loop->removeReadStream($stream);
+ $loop->removeReadStream($input2);
+
+ $called = true;
+ });
+
+ // this callback would have to be called as well, but the first stream already removed us
+ $that = $this;
+ $loop->addReadStream($input2, function () use (& $called, $that) {
+ if ($called) {
+ $that->fail('Callback 2 must not be called after callback 1 was called');
+ }
+ });
+
+ fwrite($output1, "foo\n");
+ fwrite($output2, "foo\n");
+
+ $loop->run();
+
+ $this->assertTrue($called);
+ }
+
+ public function testFutureTickEventGeneratedByFutureTick()
+ {
+ $loop = $this->loop;
+ $this->loop->futureTick(
+ function () use ($loop) {
+ $loop->futureTick(
+ function () {
+ echo 'future-tick' . PHP_EOL;
+ }
+ );
+ }
+ );
+
+ $this->expectOutputString('future-tick' . PHP_EOL);
+
+ $this->loop->run();
+ }
+
+ public function testFutureTick()
+ {
+ $called = false;
+
+ $callback = function () use (&$called) {
+ $called = true;
+ };
+
+ $this->loop->futureTick($callback);
+
+ $this->assertFalse($called);
+
+ $this->tickLoop($this->loop);
+
+ $this->assertTrue($called);
+ }
+
+ public function testFutureTickFiresBeforeIO()
+ {
+ list ($stream) = $this->createSocketPair();
+
+ $this->loop->addWriteStream(
+ $stream,
+ function () {
+ echo 'stream' . PHP_EOL;
+ }
+ );
+
+ $this->loop->futureTick(
+ function () {
+ echo 'future-tick' . PHP_EOL;
+ }
+ );
+
+ $this->expectOutputString('future-tick' . PHP_EOL . 'stream' . PHP_EOL);
+
+ $this->tickLoop($this->loop);
+ }
+
+ public function testRecursiveFutureTick()
+ {
+ list ($stream) = $this->createSocketPair();
+
+ $loop = $this->loop;
+ $this->loop->addWriteStream(
+ $stream,
+ function () use ($stream, $loop) {
+ echo 'stream' . PHP_EOL;
+ $loop->removeWriteStream($stream);
+ }
+ );
+
+ $this->loop->futureTick(
+ function () use ($loop) {
+ echo 'future-tick-1' . PHP_EOL;
+ $loop->futureTick(
+ function () {
+ echo 'future-tick-2' . PHP_EOL;
+ }
+ );
+ }
+ );
+
+ $this->expectOutputString('future-tick-1' . PHP_EOL . 'stream' . PHP_EOL . 'future-tick-2' . PHP_EOL);
+
+ $this->loop->run();
+ }
+
+ public function testRunWaitsForFutureTickEvents()
+ {
+ list ($stream) = $this->createSocketPair();
+
+ $loop = $this->loop;
+ $this->loop->addWriteStream(
+ $stream,
+ function () use ($stream, $loop) {
+ $loop->removeWriteStream($stream);
+ $loop->futureTick(
+ function () {
+ echo 'future-tick' . PHP_EOL;
+ }
+ );
+ }
+ );
+
+ $this->expectOutputString('future-tick' . PHP_EOL);
+
+ $this->loop->run();
+ }
+
+ public function testFutureTickEventGeneratedByTimer()
+ {
+ $loop = $this->loop;
+ $this->loop->addTimer(
+ 0.001,
+ function () use ($loop) {
+ $loop->futureTick(
+ function () {
+ echo 'future-tick' . PHP_EOL;
+ }
+ );
+ }
+ );
+
+ $this->expectOutputString('future-tick' . PHP_EOL);
+
+ $this->loop->run();
+ }
+
+ public function testRemoveSignalNotRegisteredIsNoOp()
+ {
+ $this->loop->removeSignal(SIGINT, function () { });
+ $this->assertTrue(true);
+ }
+
+ public function testSignal()
+ {
+ if (!function_exists('posix_kill') || !function_exists('posix_getpid')) {
+ $this->markTestSkipped('Signal test skipped because functions "posix_kill" and "posix_getpid" are missing.');
+ }
+
+ $called = false;
+ $calledShouldNot = true;
+
+ $timer = $this->loop->addPeriodicTimer(1, function () {});
+
+ $this->loop->addSignal(SIGUSR2, $func2 = function () use (&$calledShouldNot) {
+ $calledShouldNot = false;
+ });
+
+ $loop = $this->loop;
+ $this->loop->addSignal(SIGUSR1, $func1 = function () use (&$func1, &$func2, &$called, $timer, $loop) {
+ $called = true;
+ $loop->removeSignal(SIGUSR1, $func1);
+ $loop->removeSignal(SIGUSR2, $func2);
+ $loop->cancelTimer($timer);
+ });
+
+ $this->loop->futureTick(function () {
+ posix_kill(posix_getpid(), SIGUSR1);
+ });
+
+ $this->loop->run();
+
+ $this->assertTrue($called);
+ $this->assertTrue($calledShouldNot);
+ }
+
+ public function testSignalMultipleUsagesForTheSameListener()
+ {
+ $funcCallCount = 0;
+ $func = function () use (&$funcCallCount) {
+ $funcCallCount++;
+ };
+ $this->loop->addTimer(1, function () {});
+
+ $this->loop->addSignal(SIGUSR1, $func);
+ $this->loop->addSignal(SIGUSR1, $func);
+
+ $this->loop->addTimer(0.4, function () {
+ posix_kill(posix_getpid(), SIGUSR1);
+ });
+ $loop = $this->loop;
+ $this->loop->addTimer(0.9, function () use (&$func, $loop) {
+ $loop->removeSignal(SIGUSR1, $func);
+ });
+
+ $this->loop->run();
+
+ $this->assertSame(1, $funcCallCount);
+ }
+
+ public function testSignalsKeepTheLoopRunning()
+ {
+ $loop = $this->loop;
+ $function = function () {};
+ $this->loop->addSignal(SIGUSR1, $function);
+ $this->loop->addTimer(1.5, function () use ($function, $loop) {
+ $loop->removeSignal(SIGUSR1, $function);
+ $loop->stop();
+ });
+
+ $this->assertRunSlowerThan(1.5);
+ }
+
+ public function testSignalsKeepTheLoopRunningAndRemovingItStopsTheLoop()
+ {
+ $loop = $this->loop;
+ $function = function () {};
+ $this->loop->addSignal(SIGUSR1, $function);
+ $this->loop->addTimer(1.5, function () use ($function, $loop) {
+ $loop->removeSignal(SIGUSR1, $function);
+ });
+
+ $this->assertRunFasterThan(1.6);
+ }
+
+ public function testTimerIntervalCanBeFarInFuture()
+ {
+ // get only one part of the pair to ensure the other side will close immediately
+ list($stream) = $this->createSocketPair();
+
+ // start a timer very far in the future
+ $timer = $this->loop->addTimer(PHP_INT_MAX, function () { });
+
+ // remove stream and timer when the stream is readable (closes)
+ $loop = $this->loop;
+ $this->loop->addReadStream($stream, function ($stream) use ($timer, $loop) {
+ $loop->removeReadStream($stream);
+ $loop->cancelTimer($timer);
+ });
+
+ $this->assertRunFasterThan($this->tickTimeout);
+ }
+
+ private function assertRunSlowerThan($minInterval)
+ {
+ $start = microtime(true);
+
+ $this->loop->run();
+
+ $end = microtime(true);
+ $interval = $end - $start;
+
+ $this->assertLessThan($interval, $minInterval);
+ }
+
+ private function assertRunFasterThan($maxInterval)
+ {
+ $start = microtime(true);
+
+ $this->loop->run();
+
+ $end = microtime(true);
+ $interval = $end - $start;
+
+ $this->assertLessThan($maxInterval, $interval);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/CallableStub.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/CallableStub.php
new file mode 100644
index 0000000..913d403
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/CallableStub.php
@@ -0,0 +1,10 @@
+markTestSkipped('ExtEvLoop tests skipped because ext-ev extension is not installed.');
+ }
+
+ return new ExtEvLoop();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/ExtEventLoopTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/ExtEventLoopTest.php
new file mode 100644
index 0000000..2f88d18
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/ExtEventLoopTest.php
@@ -0,0 +1,84 @@
+markTestSkipped('libevent tests skipped on linux due to linux epoll issues.');
+ }
+
+ if (!extension_loaded('event')) {
+ $this->markTestSkipped('ext-event tests skipped because ext-event is not installed.');
+ }
+
+ return new ExtEventLoop();
+ }
+
+ public function createStream()
+ {
+ // Use a FIFO on linux to get around lack of support for disk-based file
+ // descriptors when using the EPOLL back-end.
+ if ('Linux' === PHP_OS) {
+ $this->fifoPath = tempnam(sys_get_temp_dir(), 'react-');
+
+ unlink($this->fifoPath);
+
+ posix_mkfifo($this->fifoPath, 0600);
+
+ $stream = fopen($this->fifoPath, 'r+');
+
+ // ext-event (as of 1.8.1) does not yet support in-memory temporary
+ // streams. Setting maxmemory:0 and performing a write forces PHP to
+ // back this temporary stream with a real file.
+ //
+ // This problem is mentioned at https://bugs.php.net/bug.php?id=64652&edit=3
+ // but remains unresolved (despite that issue being closed).
+ } else {
+ $stream = fopen('php://temp/maxmemory:0', 'r+');
+
+ fwrite($stream, 'x');
+ ftruncate($stream, 0);
+ }
+
+ return $stream;
+ }
+
+ public function writeToStream($stream, $content)
+ {
+ if ('Linux' !== PHP_OS) {
+ return parent::writeToStream($stream, $content);
+ }
+
+ fwrite($stream, $content);
+ }
+
+ /**
+ * @group epoll-readable-error
+ */
+ public function testCanUseReadableStreamWithFeatureFds()
+ {
+ if (PHP_VERSION_ID > 70000) {
+ $this->markTestSkipped('Memory stream not supported');
+ }
+
+ $this->loop = $this->createLoop(true);
+
+ $input = fopen('php://temp/maxmemory:0', 'r+');
+
+ fwrite($input, 'x');
+ ftruncate($input, 0);
+
+ $this->loop->addReadStream($input, $this->expectCallableExactly(2));
+
+ fwrite($input, "foo\n");
+ $this->tickLoop($this->loop);
+
+ fwrite($input, "bar\n");
+ $this->tickLoop($this->loop);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/ExtLibevLoopTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/ExtLibevLoopTest.php
new file mode 100644
index 0000000..19a5e87
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/ExtLibevLoopTest.php
@@ -0,0 +1,22 @@
+markTestSkipped('libev tests skipped because ext-libev is not installed.');
+ }
+
+ return new ExtLibevLoop();
+ }
+
+ public function testLibEvConstructor()
+ {
+ $loop = new ExtLibevLoop();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/ExtLibeventLoopTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/ExtLibeventLoopTest.php
new file mode 100644
index 0000000..8497065
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/ExtLibeventLoopTest.php
@@ -0,0 +1,58 @@
+markTestSkipped('libevent tests skipped on linux due to linux epoll issues.');
+ }
+
+ if (!function_exists('event_base_new')) {
+ $this->markTestSkipped('libevent tests skipped because ext-libevent is not installed.');
+ }
+
+ return new ExtLibeventLoop();
+ }
+
+ public function tearDown()
+ {
+ if (file_exists($this->fifoPath)) {
+ unlink($this->fifoPath);
+ }
+ }
+
+ public function createStream()
+ {
+ if ('Linux' !== PHP_OS) {
+ return parent::createStream();
+ }
+
+ $this->fifoPath = tempnam(sys_get_temp_dir(), 'react-');
+
+ unlink($this->fifoPath);
+
+ // Use a FIFO on linux to get around lack of support for disk-based file
+ // descriptors when using the EPOLL back-end.
+ posix_mkfifo($this->fifoPath, 0600);
+
+ $stream = fopen($this->fifoPath, 'r+');
+
+ return $stream;
+ }
+
+ public function writeToStream($stream, $content)
+ {
+ if ('Linux' !== PHP_OS) {
+ return parent::writeToStream($stream, $content);
+ }
+
+ fwrite($stream, $content);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/SignalsHandlerTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/SignalsHandlerTest.php
new file mode 100644
index 0000000..f8b7df3
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/SignalsHandlerTest.php
@@ -0,0 +1,55 @@
+assertSame(0, $callCount);
+
+ $signals->add(SIGUSR1, $func);
+ $this->assertSame(0, $callCount);
+
+ $signals->add(SIGUSR1, $func);
+ $this->assertSame(0, $callCount);
+
+ $signals->add(SIGUSR1, $func);
+ $this->assertSame(0, $callCount);
+
+ $signals->call(SIGUSR1);
+ $this->assertSame(1, $callCount);
+
+ $signals->add(SIGUSR2, $func);
+ $this->assertSame(1, $callCount);
+
+ $signals->add(SIGUSR2, $func);
+ $this->assertSame(1, $callCount);
+
+ $signals->call(SIGUSR2);
+ $this->assertSame(2, $callCount);
+
+ $signals->remove(SIGUSR2, $func);
+ $this->assertSame(2, $callCount);
+
+ $signals->remove(SIGUSR2, $func);
+ $this->assertSame(2, $callCount);
+
+ $signals->call(SIGUSR2);
+ $this->assertSame(2, $callCount);
+
+ $signals->remove(SIGUSR1, $func);
+ $this->assertSame(2, $callCount);
+
+ $signals->call(SIGUSR1);
+ $this->assertSame(2, $callCount);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/StreamSelectLoopTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/StreamSelectLoopTest.php
new file mode 100644
index 0000000..bd19e1c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/StreamSelectLoopTest.php
@@ -0,0 +1,148 @@
+getName(false), 'testSignal', 10) === 0 && extension_loaded('pcntl')) {
+ $this->resetSignalHandlers();
+ }
+ }
+
+ public function createLoop()
+ {
+ return new StreamSelectLoop();
+ }
+
+ public function testStreamSelectTimeoutEmulation()
+ {
+ $this->loop->addTimer(
+ 0.05,
+ $this->expectCallableOnce()
+ );
+
+ $start = microtime(true);
+
+ $this->loop->run();
+
+ $end = microtime(true);
+ $interval = $end - $start;
+
+ $this->assertGreaterThan(0.04, $interval);
+ }
+
+ public function signalProvider()
+ {
+ return array(
+ array('SIGUSR1'),
+ array('SIGHUP'),
+ array('SIGTERM'),
+ );
+ }
+
+ /**
+ * Test signal interrupt when no stream is attached to the loop
+ * @dataProvider signalProvider
+ */
+ public function testSignalInterruptNoStream($signal)
+ {
+ if (!extension_loaded('pcntl')) {
+ $this->markTestSkipped('"pcntl" extension is required to run this test.');
+ }
+
+ // dispatch signal handler every 10ms for 0.1s
+ $check = $this->loop->addPeriodicTimer(0.01, function() {
+ pcntl_signal_dispatch();
+ });
+ $loop = $this->loop;
+ $loop->addTimer(0.1, function () use ($check, $loop) {
+ $loop->cancelTimer($check);
+ });
+
+ $handled = false;
+ $this->assertTrue(pcntl_signal(constant($signal), function () use (&$handled) {
+ $handled = true;
+ }));
+
+ // spawn external process to send signal to current process id
+ $this->forkSendSignal($signal);
+
+ $this->loop->run();
+ $this->assertTrue($handled);
+ }
+
+ /**
+ * Test signal interrupt when a stream is attached to the loop
+ * @dataProvider signalProvider
+ */
+ public function testSignalInterruptWithStream($signal)
+ {
+ if (!extension_loaded('pcntl')) {
+ $this->markTestSkipped('"pcntl" extension is required to run this test.');
+ }
+
+ // dispatch signal handler every 10ms
+ $this->loop->addPeriodicTimer(0.01, function() {
+ pcntl_signal_dispatch();
+ });
+
+ // add stream to the loop
+ $loop = $this->loop;
+ list($writeStream, $readStream) = $this->createSocketPair();
+ $loop->addReadStream($readStream, function ($stream) use ($loop) {
+ /** @var $loop LoopInterface */
+ $read = fgets($stream);
+ if ($read === "end loop\n") {
+ $loop->stop();
+ }
+ });
+ $this->loop->addTimer(0.1, function() use ($writeStream) {
+ fwrite($writeStream, "end loop\n");
+ });
+
+ $handled = false;
+ $this->assertTrue(pcntl_signal(constant($signal), function () use (&$handled) {
+ $handled = true;
+ }));
+
+ // spawn external process to send signal to current process id
+ $this->forkSendSignal($signal);
+
+ $this->loop->run();
+
+ $this->assertTrue($handled);
+ }
+
+ /**
+ * reset all signal handlers to default
+ */
+ protected function resetSignalHandlers()
+ {
+ foreach($this->signalProvider() as $signal) {
+ pcntl_signal(constant($signal[0]), SIG_DFL);
+ }
+ }
+
+ /**
+ * fork child process to send signal to current process id
+ */
+ protected function forkSendSignal($signal)
+ {
+ $currentPid = posix_getpid();
+ $childPid = pcntl_fork();
+ if ($childPid == -1) {
+ $this->fail("Failed to fork child process!");
+ } else if ($childPid === 0) {
+ // this is executed in the child process
+ usleep(20000);
+ posix_kill($currentPid, constant($signal));
+ die();
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/TestCase.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/TestCase.php
new file mode 100644
index 0000000..dbdd54c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/TestCase.php
@@ -0,0 +1,53 @@
+createCallableMock();
+ $mock
+ ->expects($this->exactly($amount))
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ protected function expectCallableOnce()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ protected function expectCallableNever()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->never())
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ protected function createCallableMock()
+ {
+ return $this->getMockBuilder('React\Tests\EventLoop\CallableStub')->getMock();
+ }
+
+ protected function tickLoop(LoopInterface $loop)
+ {
+ $loop->futureTick(function () use ($loop) {
+ $loop->stop();
+ });
+
+ $loop->run();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/Timer/AbstractTimerTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/Timer/AbstractTimerTest.php
new file mode 100644
index 0000000..294e683
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/Timer/AbstractTimerTest.php
@@ -0,0 +1,122 @@
+createLoop();
+
+ $timer = $loop->addTimer(0.001, $this->expectCallableNever());
+
+ $this->assertInstanceOf('React\EventLoop\TimerInterface', $timer);
+ $this->assertFalse($timer->isPeriodic());
+ }
+
+ public function testAddTimerWillBeInvokedOnceAndBlocksLoopWhenRunning()
+ {
+ $loop = $this->createLoop();
+
+ $loop->addTimer(0.001, $this->expectCallableOnce());
+
+ $start = microtime(true);
+ $loop->run();
+ $end = microtime(true);
+
+ // make no strict assumptions about actual time interval.
+ // must be at least 0.001s (1ms) and should not take longer than 0.1s
+ $this->assertGreaterThanOrEqual(0.001, $end - $start);
+ $this->assertLessThan(0.1, $end - $start);
+ }
+
+ public function testAddPeriodicTimerReturnsPeriodicTimerInstance()
+ {
+ $loop = $this->createLoop();
+
+ $periodic = $loop->addPeriodicTimer(0.1, $this->expectCallableNever());
+
+ $this->assertInstanceOf('React\EventLoop\TimerInterface', $periodic);
+ $this->assertTrue($periodic->isPeriodic());
+ }
+
+ public function testAddPeriodicTimerWillBeInvokedUntilItIsCancelled()
+ {
+ $loop = $this->createLoop();
+
+ $periodic = $loop->addPeriodicTimer(0.1, $this->expectCallableExactly(3));
+
+ // make no strict assumptions about actual time interval.
+ // leave some room to ensure this ticks exactly 3 times.
+ $loop->addTimer(0.399, function () use ($loop, $periodic) {
+ $loop->cancelTimer($periodic);
+ });
+
+ $loop->run();
+ }
+
+ public function testAddPeriodicTimerWillBeInvokedWithMaximumAccuracyUntilItIsCancelled()
+ {
+ $loop = $this->createLoop();
+
+ $i = 0;
+ $periodic = $loop->addPeriodicTimer(0.001, function () use (&$i) {
+ ++$i;
+ });
+
+ $loop->addTimer(0.02, function () use ($loop, $periodic) {
+ $loop->cancelTimer($periodic);
+ });
+
+ $loop->run();
+
+ // make no strict assumptions about number of invocations.
+ // we know it must be no more than 20 times and should at least be
+ // invoked twice for really slow loops
+ $this->assertLessThanOrEqual(20, $i);
+ $this->assertGreaterThan(2, $i);
+ }
+
+ public function testAddPeriodicTimerCancelsItself()
+ {
+ $loop = $this->createLoop();
+
+ $i = 0;
+ $loop->addPeriodicTimer(0.001, function ($timer) use (&$i, $loop) {
+ $i++;
+
+ if ($i === 5) {
+ $loop->cancelTimer($timer);
+ }
+ });
+
+ $start = microtime(true);
+ $loop->run();
+ $end = microtime(true);
+
+ $this->assertEquals(5, $i);
+
+ // make no strict assumptions about time interval.
+ // 5 invocations must take at least 0.005s (5ms) and should not take
+ // longer than 0.1s for slower loops.
+ $this->assertGreaterThanOrEqual(0.005, $end - $start);
+ $this->assertLessThan(0.1, $end - $start);
+ }
+
+ public function testMinimumIntervalOneMicrosecond()
+ {
+ $loop = $this->createLoop();
+
+ $timer = $loop->addTimer(0, function () {});
+
+ $this->assertEquals(0.000001, $timer->getInterval());
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/Timer/ExtEvTimerTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/Timer/ExtEvTimerTest.php
new file mode 100644
index 0000000..bfa9186
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/Timer/ExtEvTimerTest.php
@@ -0,0 +1,17 @@
+markTestSkipped('ExtEvLoop tests skipped because ext-ev extension is not installed.');
+ }
+
+ return new ExtEvLoop();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/Timer/ExtEventTimerTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/Timer/ExtEventTimerTest.php
new file mode 100644
index 0000000..a7a6d00
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/Timer/ExtEventTimerTest.php
@@ -0,0 +1,17 @@
+markTestSkipped('ext-event tests skipped because ext-event is not installed.');
+ }
+
+ return new ExtEventLoop();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/Timer/ExtLibevTimerTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/Timer/ExtLibevTimerTest.php
new file mode 100644
index 0000000..65e82be
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/Timer/ExtLibevTimerTest.php
@@ -0,0 +1,17 @@
+markTestSkipped('libev tests skipped because ext-libev is not installed.');
+ }
+
+ return new ExtLibevLoop();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/Timer/ExtLibeventTimerTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/Timer/ExtLibeventTimerTest.php
new file mode 100644
index 0000000..9089b9a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/Timer/ExtLibeventTimerTest.php
@@ -0,0 +1,17 @@
+markTestSkipped('libevent tests skipped because ext-libevent is not installed.');
+ }
+
+ return new ExtLibeventLoop();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/Timer/StreamSelectTimerTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/Timer/StreamSelectTimerTest.php
new file mode 100644
index 0000000..cfe1d7d
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/Timer/StreamSelectTimerTest.php
@@ -0,0 +1,13 @@
+tick();
+
+ // simulate a bunch of processing on stream events,
+ // part of which schedules a future timer...
+ sleep(1);
+ $timers->add(new Timer(0.5, function () {
+ $this->fail("Timer shouldn't be called");
+ }));
+
+ $timers->tick();
+
+ $this->assertTrue(true);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/bootstrap.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/bootstrap.php
new file mode 100644
index 0000000..ea7dd4c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/tests/bootstrap.php
@@ -0,0 +1,15 @@
+addPsr4('React\\Tests\\EventLoop\\', __DIR__);
+
+if (!defined('SIGUSR1')) {
+ define('SIGUSR1', 1);
+}
+
+if (!defined('SIGUSR2')) {
+ define('SIGUSR2', 2);
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/travis-init.sh b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/travis-init.sh
new file mode 100644
index 0000000..29ce884
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/event-loop/travis-init.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+set -e
+set -o pipefail
+
+if [[ "$TRAVIS_PHP_VERSION" != "hhvm" &&
+ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]]; then
+
+ # install 'event' and 'ev' PHP extension
+ if [[ "$TRAVIS_PHP_VERSION" != "5.3" ]]; then
+ echo "yes" | pecl install event
+ echo "yes" | pecl install ev
+ fi
+
+ # install 'libevent' PHP extension (does not support php 7)
+ if [[ "$TRAVIS_PHP_VERSION" != "7.0" &&
+ "$TRAVIS_PHP_VERSION" != "7.1" &&
+ "$TRAVIS_PHP_VERSION" != "7.2" ]]; then
+ curl http://pecl.php.net/get/libevent-0.1.0.tgz | tar -xz
+ pushd libevent-0.1.0
+ phpize
+ ./configure
+ make
+ make install
+ popd
+ echo "extension=libevent.so" >> "$(php -r 'echo php_ini_loaded_file();')"
+ fi
+
+ # install 'libev' PHP extension (does not support php 7)
+ if [[ "$TRAVIS_PHP_VERSION" != "7.0" &&
+ "$TRAVIS_PHP_VERSION" != "7.1" &&
+ "$TRAVIS_PHP_VERSION" != "7.2" ]]; then
+ git clone --recursive https://github.com/m4rw3r/php-libev
+ pushd php-libev
+ phpize
+ ./configure --with-libev
+ make
+ make install
+ popd
+ echo "extension=libev.so" >> "$(php -r 'echo php_ini_loaded_file();')"
+ fi
+
+fi
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/.gitignore b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/.gitignore
new file mode 100644
index 0000000..de4a392
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/.gitignore
@@ -0,0 +1,2 @@
+/vendor
+/composer.lock
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/.travis.yml b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/.travis.yml
new file mode 100644
index 0000000..a71864a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/.travis.yml
@@ -0,0 +1,26 @@
+language: php
+
+php:
+# - 5.3 # requires old distro, see below
+ - 5.4
+ - 5.5
+ - 5.6
+ - 7.0
+ - 7.1
+ - hhvm # ignore errors, see below
+
+# lock distro so new future defaults will not break the build
+dist: trusty
+
+matrix:
+ include:
+ - php: 5.3
+ dist: precise
+ allow_failures:
+ - php: hhvm
+
+install:
+ - composer install --no-interaction
+
+script:
+ - vendor/bin/phpunit --coverage-text
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/CHANGELOG.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/CHANGELOG.md
new file mode 100644
index 0000000..0a21244
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/CHANGELOG.md
@@ -0,0 +1,40 @@
+# Changelog
+
+## 1.2.1 (2017-12-22)
+
+* README improvements
+ (#28 by @jsor)
+
+* Improve test suite by adding forward compatiblity with PHPUnit 6 and
+ fix test suite forward compatibility with upcoming EventLoop releases
+ (#30 and #31 by @clue)
+
+## 1.2.0 (2017-08-08)
+
+* Feature: Only start timers if input Promise is still pending and
+ return a settled output promise if the input is already settled.
+ (#25 by @clue)
+
+* Feature: Cap minimum timer interval at 1µs across all versions
+ (#23 by @clue)
+
+* Feature: Forward compatibility with EventLoop v1.0 and v0.5
+ (#27 by @clue)
+
+* Improve test suite by adding PHPUnit to require-dev and
+ lock Travis distro so new defaults will not break the build
+ (#24 and #26 by @clue)
+
+## 1.1.1 (2016-12-27)
+
+* Improve test suite to use PSR-4 autoloader and proper namespaces.
+ (#21 by @clue)
+
+## 1.1.0 (2016-02-29)
+
+* Feature: Support promise cancellation for all timer primitives
+ (#18 by @clue)
+
+## 1.0.0 (2015-09-29)
+
+* First tagged release
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/LICENSE b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/LICENSE
new file mode 100644
index 0000000..dc09d1e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Christian Lück
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/README.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/README.md
new file mode 100644
index 0000000..2ea94fa
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/README.md
@@ -0,0 +1,372 @@
+# PromiseTimer
+
+[](https://travis-ci.org/reactphp/promise-timer)
+
+A trivial implementation of timeouts for `Promise`s, built on top of [ReactPHP](https://reactphp.org/).
+
+**Table of contents**
+
+* [Usage](#usage)
+ * [timeout()](#timeout)
+ * [Timeout cancellation](#timeout-cancellation)
+ * [Cancellation handler](#cancellation-handler)
+ * [Input cancellation](#input-cancellation)
+ * [Output cancellation](#output-cancellation)
+ * [Collections](#collections)
+ * [resolve()](#resolve)
+ * [Resolve cancellation](#resolve-cancellation)
+ * [reject()](#reject)
+ * [Reject cancellation](#reject-cancellation)
+ * [TimeoutException](#timeoutexception)
+* [Install](#install)
+* [Tests](#tests)
+* [License](#license)
+
+## Usage
+
+This lightweight library consists only of a few simple functions.
+All functions reside under the `React\Promise\Timer` namespace.
+
+The below examples assume you use an import statement similar to this:
+
+```php
+use React\Promise\Timer;
+
+Timer\timeout(…);
+```
+
+Alternatively, you can also refer to them with their fully-qualified name:
+
+```php
+\React\Promise\Timer\timeout(…);
+```
+
+### timeout()
+
+The `timeout(PromiseInterface $promise, $time, LoopInterface $loop)` function
+can be used to *cancel* operations that take *too long*.
+You need to pass in an input `$promise` that represents a pending operation and timeout parameters.
+It returns a new `Promise` with the following resolution behavior:
+
+* If the input `$promise` resolves before `$time` seconds, resolve the resulting promise with its fulfillment value.
+* If the input `$promise` rejects before `$time` seconds, reject the resulting promise with its rejection value.
+* If the input `$promise` does not settle before `$time` seconds, *cancel* the operation and reject the resulting promise with a [`TimeoutException`](#timeoutexception).
+
+Internally, the given `$time` value will be used to start a timer that will
+*cancel* the pending operation once it triggers.
+This implies that if you pass a really small (or negative) value, it will still
+start a timer and will thus trigger at the earliest possible time in the future.
+
+If the input `$promise` is already settled, then the resulting promise will
+resolve or reject immediately without starting a timer at all.
+
+A common use case for handling only resolved values looks like this:
+
+```php
+$promise = accessSomeRemoteResource();
+Timer\timeout($promise, 10.0, $loop)->then(function ($value) {
+ // the operation finished within 10.0 seconds
+});
+```
+
+A more complete example could look like this:
+
+```php
+$promise = accessSomeRemoteResource();
+Timer\timeout($promise, 10.0, $loop)->then(
+ function ($value) {
+ // the operation finished within 10.0 seconds
+ },
+ function ($error) {
+ if ($error instanceof Timer\TimeoutException) {
+ // the operation has failed due to a timeout
+ } else {
+ // the input operation has failed due to some other error
+ }
+ }
+);
+```
+
+Or if you're using [react/promise v2.2.0](https://github.com/reactphp/promise) or up:
+
+```php
+Timer\timeout($promise, 10.0, $loop)
+ ->then(function ($value) {
+ // the operation finished within 10.0 seconds
+ })
+ ->otherwise(function (Timer\TimeoutException $error) {
+ // the operation has failed due to a timeout
+ })
+ ->otherwise(function ($error) {
+ // the input operation has failed due to some other error
+ })
+;
+```
+
+#### Timeout cancellation
+
+As discussed above, the [`timeout()`](#timeout) function will *cancel* the
+underlying operation if it takes *too long*.
+This means that you can be sure the resulting promise will then be rejected
+with a [`TimeoutException`](#timeoutexception).
+
+However, what happens to the underlying input `$promise` is a bit more tricky:
+Once the timer fires, we will try to call
+[`$promise->cancel()`](https://github.com/reactphp/promise#cancellablepromiseinterfacecancel)
+on the input `$promise` which in turn invokes its [cancellation handler](#cancellation-handler).
+
+This means that it's actually up the input `$promise` to handle
+[cancellation support](https://github.com/reactphp/promise#cancellablepromiseinterface).
+
+* A common use case involves cleaning up any resources like open network sockets or
+ file handles or terminating external processes or timers.
+
+* If the given input `$promise` does not support cancellation, then this is a NO-OP.
+ This means that while the resulting promise will still be rejected, the underlying
+ input `$promise` may still be pending and can hence continue consuming resources.
+
+See the following chapter for more details on the cancellation handler.
+
+#### Cancellation handler
+
+For example, an implementation for the above operation could look like this:
+
+```php
+function accessSomeRemoteResource()
+{
+ return new Promise(
+ function ($resolve, $reject) use (&$socket) {
+ // this will be called once the promise is created
+ // a common use case involves opening any resources and eventually resolving
+ $socket = createSocket();
+ $socket->on('data', function ($data) use ($resolve) {
+ $resolve($data);
+ });
+ },
+ function ($resolve, $reject) use (&$socket) {
+ // this will be called once calling `cancel()` on this promise
+ // a common use case involves cleaning any resources and then rejecting
+ $socket->close();
+ $reject(new \RuntimeException('Operation cancelled'));
+ }
+ );
+}
+```
+
+In this example, calling `$promise->cancel()` will invoke the registered cancellation
+handler which then closes the network socket and rejects the `Promise` instance.
+
+If no cancellation handler is passed to the `Promise` constructor, then invoking
+its `cancel()` method it is effectively a NO-OP.
+This means that it may still be pending and can hence continue consuming resources.
+
+For more details on the promise cancellation, please refer to the
+[Promise documentation](https://github.com/reactphp/promise#cancellablepromiseinterface).
+
+#### Input cancellation
+
+Irrespective of the timout handling, you can also explicitly `cancel()` the
+input `$promise` at any time.
+This means that the `timeout()` handling does not affect cancellation of the
+input `$promise`, as demonstrated in the following example:
+
+```php
+$promise = accessSomeRemoteResource();
+$timeout = Timer\timeout($promise, 10.0, $loop);
+
+$promise->cancel();
+```
+
+The registered [cancellation handler](#cancellation-handler) is responsible for
+handling the `cancel()` call:
+
+* A described above, a common use involves resource cleanup and will then *reject*
+ the `Promise`.
+ If the input `$promise` is being rejected, then the timeout will be aborted
+ and the resulting promise will also be rejected.
+* If the input `$promise` is still pending, then the timout will continue
+ running until the timer expires.
+ The same happens if the input `$promise` does not register a
+ [cancellation handler](#cancellation-handler).
+
+#### Output cancellation
+
+Similarily, you can also explicitly `cancel()` the resulting promise like this:
+
+```php
+$promise = accessSomeRemoteResource();
+$timeout = Timer\timeout($promise, 10.0, $loop);
+
+$timeout->cancel();
+```
+
+Note how this looks very similar to the above [input cancellation](#input-cancellation)
+example. Accordingly, it also behaves very similar.
+
+Calling `cancel()` on the resulting promise will merely try
+to `cancel()` the input `$promise`.
+This means that we do not take over responsibility of the outcome and it's
+entirely up to the input `$promise` to handle cancellation support.
+
+The registered [cancellation handler](#cancellation-handler) is responsible for
+handling the `cancel()` call:
+
+* As described above, a common use involves resource cleanup and will then *reject*
+ the `Promise`.
+ If the input `$promise` is being rejected, then the timeout will be aborted
+ and the resulting promise will also be rejected.
+* If the input `$promise` is still pending, then the timout will continue
+ running until the timer expires.
+ The same happens if the input `$promise` does not register a
+ [cancellation handler](#cancellation-handler).
+
+To re-iterate, note that calling `cancel()` on the resulting promise will merely
+try to cancel the input `$promise` only.
+It is then up to the cancellation handler of the input promise to settle the promise.
+If the input promise is still pending when the timeout occurs, then the normal
+[timeout cancellation](#timeout-cancellation) handling will trigger, effectively rejecting
+the output promise with a [`TimeoutException`](#timeoutexception).
+
+This is done for consistency with the [timeout cancellation](#timeout-cancellation)
+handling and also because it is assumed this is often used like this:
+
+```php
+$timeout = Timer\timeout(accessSomeRemoteResource(), 10.0, $loop);
+
+$timeout->cancel();
+```
+
+As described above, this example works as expected and cleans up any resources
+allocated for the input `$promise`.
+
+Note that if the given input `$promise` does not support cancellation, then this
+is a NO-OP.
+This means that while the resulting promise will still be rejected after the
+timeout, the underlying input `$promise` may still be pending and can hence
+continue consuming resources.
+
+#### Collections
+
+If you want to wait for multiple promises to resolve, you can use the normal promise primitives like this:
+
+```php
+$promises = array(
+ accessSomeRemoteResource(),
+ accessSomeRemoteResource(),
+ accessSomeRemoteResource()
+);
+
+$promise = \React\Promise\all($promises);
+
+Timer\timeout($promise, 10, $loop)->then(function ($values) {
+ // *all* promises resolved
+});
+```
+
+The applies to all promise collection primitives alike, i.e. `all()`, `race()`, `any()`, `some()` etc.
+
+For more details on the promise primitives, please refer to the
+[Promise documentation](https://github.com/reactphp/promise#functions).
+
+### resolve()
+
+The `resolve($time, LoopInterface $loop)` function can be used to create a new Promise that
+resolves in `$time` seconds with the `$time` as the fulfillment value.
+
+```php
+Timer\resolve(1.5, $loop)->then(function ($time) {
+ echo 'Thanks for waiting ' . $time . ' seconds' . PHP_EOL;
+});
+```
+
+Internally, the given `$time` value will be used to start a timer that will
+resolve the promise once it triggers.
+This implies that if you pass a really small (or negative) value, it will still
+start a timer and will thus trigger at the earliest possible time in the future.
+
+#### Resolve cancellation
+
+You can explicitly `cancel()` the resulting timer promise at any time:
+
+```php
+$timer = Timer\resolve(2.0, $loop);
+
+$timer->cancel();
+```
+
+This will abort the timer and *reject* with a `RuntimeException`.
+
+### reject()
+
+The `reject($time, LoopInterface $loop)` function can be used to create a new Promise
+which rejects in `$time` seconds with a `TimeoutException`.
+
+```php
+Timer\reject(2.0, $loop)->then(null, function (TimeoutException $e) {
+ echo 'Rejected after ' . $e->getTimeout() . ' seconds ' . PHP_EOL;
+});
+```
+
+Internally, the given `$time` value will be used to start a timer that will
+reject the promise once it triggers.
+This implies that if you pass a really small (or negative) value, it will still
+start a timer and will thus trigger at the earliest possible time in the future.
+
+This function complements the [`resolve()`](#resolve) function
+and can be used as a basic building block for higher-level promise consumers.
+
+#### Reject cancellation
+
+You can explicitly `cancel()` the resulting timer promise at any time:
+
+```php
+$timer = Timer\reject(2.0, $loop);
+
+$timer->cancel();
+```
+
+This will abort the timer and *reject* with a `RuntimeException`.
+
+### TimeoutException
+
+The `TimeoutException` extends PHP's built-in `RuntimeException`.
+
+The `getTimeout()` method can be used to get the timeout value in seconds.
+
+## Install
+
+The recommended way to install this library is [through Composer](https://getcomposer.org).
+[New to Composer?](https://getcomposer.org/doc/00-intro.md)
+
+This project follows [SemVer](http://semver.org/).
+This will install the latest supported version:
+
+```bash
+$ composer require react/promise-timer:^1.2.1
+```
+
+See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
+
+This project aims to run on any platform and thus does not require any PHP
+extensions and supports running on legacy PHP 5.3 through current PHP 7+ and
+HHVM.
+It's *highly recommended to use PHP 7+* for this project.
+
+## Tests
+
+To run the test suite, you first need to clone this repo and then install all
+dependencies [through Composer](https://getcomposer.org):
+
+```bash
+$ composer install
+```
+
+To run the test suite, go to the project root and run:
+
+```bash
+$ php vendor/bin/phpunit
+```
+
+## License
+
+MIT, see [LICENSE file](LICENSE).
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/composer.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/composer.json
new file mode 100644
index 0000000..e425dc6
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/composer.json
@@ -0,0 +1,28 @@
+{
+ "name": "react/promise-timer",
+ "description": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.",
+ "keywords": ["Promise", "timeout", "timer", "event-loop", "ReactPHP", "async"],
+ "homepage": "https://github.com/react/promise-timer",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@lueck.tv"
+ }
+ ],
+ "autoload": {
+ "psr-4": { "React\\Promise\\Timer\\": "src/" },
+ "files": [ "src/functions.php" ]
+ },
+ "autoload-dev": {
+ "psr-4": { "React\\Tests\\Promise\\Timer\\": "tests/" }
+ },
+ "require": {
+ "php": ">=5.3",
+ "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5",
+ "react/promise": "~2.1|~1.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35"
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/phpunit.xml.dist b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/phpunit.xml.dist
new file mode 100644
index 0000000..bb79fba
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/phpunit.xml.dist
@@ -0,0 +1,19 @@
+
+
+
+
+
+ ./tests/
+
+
+
+
+ ./src/
+
+
+
\ No newline at end of file
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/src/TimeoutException.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/src/TimeoutException.php
new file mode 100644
index 0000000..18ea72f
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/src/TimeoutException.php
@@ -0,0 +1,22 @@
+timeout = $timeout;
+ }
+
+ public function getTimeout()
+ {
+ return $this->timeout;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/src/functions.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/src/functions.php
new file mode 100644
index 0000000..6ad9867
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/src/functions.php
@@ -0,0 +1,70 @@
+then(function ($v) use (&$timer, $loop, $resolve) {
+ if ($timer) {
+ $loop->cancelTimer($timer);
+ }
+ $timer = false;
+ $resolve($v);
+ }, function ($v) use (&$timer, $loop, $reject) {
+ if ($timer) {
+ $loop->cancelTimer($timer);
+ }
+ $timer = false;
+ $reject($v);
+ });
+
+ // promise already resolved => no need to start timer
+ if ($timer === false) {
+ return;
+ }
+
+ // start timeout timer which will cancel the input promise
+ $timer = $loop->addTimer($time, function () use ($time, $promise, $reject) {
+ $reject(new TimeoutException($time, 'Timed out after ' . $time . ' seconds'));
+
+ if ($promise instanceof CancellablePromiseInterface) {
+ $promise->cancel();
+ }
+ });
+ }, $canceller);
+}
+
+function resolve($time, LoopInterface $loop)
+{
+ return new Promise(function ($resolve) use ($loop, $time, &$timer) {
+ // resolve the promise when the timer fires in $time seconds
+ $timer = $loop->addTimer($time, function () use ($time, $resolve) {
+ $resolve($time);
+ });
+ }, function ($resolveUnused, $reject) use (&$timer, $loop) {
+ // cancelling this promise will cancel the timer and reject
+ $loop->cancelTimer($timer);
+ $reject(new \RuntimeException('Timer cancelled'));
+ });
+}
+
+function reject($time, LoopInterface $loop)
+{
+ return resolve($time, $loop)->then(function ($time) {
+ throw new TimeoutException($time, 'Timer expired after ' . $time . ' seconds');
+ });
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/tests/CallableStub.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/tests/CallableStub.php
new file mode 100644
index 0000000..a391aa5
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/tests/CallableStub.php
@@ -0,0 +1,10 @@
+loop);
+
+ $this->expectPromisePending($promise);
+ }
+
+ public function testPromiseExpiredIsPendingWithoutRunningLoop()
+ {
+ $promise = Timer\reject(-1, $this->loop);
+
+ $this->expectPromisePending($promise);
+ }
+
+ public function testPromiseWillBeRejectedOnTimeout()
+ {
+ $promise = Timer\reject(0.01, $this->loop);
+
+ $this->loop->run();
+
+ $this->expectPromiseRejected($promise);
+ }
+
+ public function testPromiseExpiredWillBeRejectedOnTimeout()
+ {
+ $promise = Timer\reject(-1, $this->loop);
+
+ $this->loop->run();
+
+ $this->expectPromiseRejected($promise);
+ }
+
+ public function testCancelingPromiseWillRejectTimer()
+ {
+ $promise = Timer\reject(0.01, $this->loop);
+
+ $promise->cancel();
+
+ $this->expectPromiseRejected($promise);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/tests/FunctionResolveTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/tests/FunctionResolveTest.php
new file mode 100644
index 0000000..0bfdc21
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/tests/FunctionResolveTest.php
@@ -0,0 +1,71 @@
+loop);
+
+ $this->expectPromisePending($promise);
+ }
+
+ public function testPromiseExpiredIsPendingWithoutRunningLoop()
+ {
+ $promise = Timer\resolve(-1, $this->loop);
+
+ $this->expectPromisePending($promise);
+ }
+
+ public function testPromiseWillBeResolvedOnTimeout()
+ {
+ $promise = Timer\resolve(0.01, $this->loop);
+
+ $this->loop->run();
+
+ $this->expectPromiseResolved($promise);
+ }
+
+ public function testPromiseExpiredWillBeResolvedOnTimeout()
+ {
+ $promise = Timer\resolve(-1, $this->loop);
+
+ $this->loop->run();
+
+ $this->expectPromiseResolved($promise);
+ }
+
+ public function testWillStartLoopTimer()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('addTimer')->with($this->equalTo(0.01));
+
+ Timer\resolve(0.01, $loop);
+ }
+
+ public function testCancellingPromiseWillCancelLoopTimer()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $timer = $this->getMockBuilder(interface_exists('React\EventLoop\TimerInterface') ? 'React\EventLoop\TimerInterface' : 'React\EventLoop\Timer\TimerInterface')->getMock();
+ $loop->expects($this->once())->method('addTimer')->will($this->returnValue($timer));
+
+ $promise = Timer\resolve(0.01, $loop);
+
+ $loop->expects($this->once())->method('cancelTimer')->with($this->equalTo($timer));
+
+ $promise->cancel();
+ }
+
+ public function testCancelingPromiseWillRejectTimer()
+ {
+ $promise = Timer\resolve(0.01, $this->loop);
+
+ $promise->cancel();
+
+ $this->expectPromiseRejected($promise);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/tests/FunctionTimeoutTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/tests/FunctionTimeoutTest.php
new file mode 100644
index 0000000..aaca2da
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/tests/FunctionTimeoutTest.php
@@ -0,0 +1,169 @@
+loop);
+
+ $this->expectPromiseResolved($promise);
+ }
+
+ public function testResolvedExpiredWillResolveRightAway()
+ {
+ $promise = Promise\resolve();
+
+ $promise = Timer\timeout($promise, -1, $this->loop);
+
+ $this->expectPromiseResolved($promise);
+ }
+
+ public function testResolvedWillNotStartTimer()
+ {
+ $promise = Promise\resolve();
+
+ Timer\timeout($promise, 3, $this->loop);
+
+ $time = microtime(true);
+ $this->loop->run();
+ $time = microtime(true) - $time;
+
+ $this->assertLessThan(0.5, $time);
+ }
+
+ public function testRejectedWillRejectRightAway()
+ {
+ $promise = Promise\reject();
+
+ $promise = Timer\timeout($promise, 3, $this->loop);
+
+ $this->expectPromiseRejected($promise);
+ }
+
+ public function testRejectedWillNotStartTimer()
+ {
+ $promise = Promise\reject();
+
+ Timer\timeout($promise, 3, $this->loop);
+
+ $time = microtime(true);
+ $this->loop->run();
+ $time = microtime(true) - $time;
+
+ $this->assertLessThan(0.5, $time);
+ }
+
+ public function testPendingWillRejectOnTimeout()
+ {
+ $promise = $this->getMockBuilder('React\Promise\PromiseInterface')->getMock();
+
+ $promise = Timer\timeout($promise, 0.01, $this->loop);
+
+ $this->loop->run();
+
+ $this->expectPromiseRejected($promise);
+ }
+
+ public function testPendingCancellableWillBeCancelledOnTimeout()
+ {
+ $promise = $this->getMockBuilder('React\Promise\CancellablePromiseInterface')->getMock();
+ $promise->expects($this->once())->method('cancel');
+
+ Timer\timeout($promise, 0.01, $this->loop);
+
+ $this->loop->run();
+ }
+
+ public function testCancelTimeoutWithoutCancellationhandlerWillNotCancelTimerAndWillNotReject()
+ {
+ $promise = new \React\Promise\Promise(function () { });
+
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $timer = $this->getMockBuilder('React\EventLoop\Timer\TimerInterface')->getMock();
+ $loop->expects($this->once())->method('addTimer')->will($this->returnValue($timer));
+ $loop->expects($this->never())->method('cancelTimer');
+
+ $timeout = Timer\timeout($promise, 0.01, $loop);
+
+ $timeout->cancel();
+
+ $this->expectPromisePending($timeout);
+ }
+
+ public function testResolvedPromiseWillNotStartTimer()
+ {
+ $promise = new \React\Promise\Promise(function ($resolve) { $resolve(true); });
+
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->never())->method('addTimer');
+
+ $timeout = Timer\timeout($promise, 0.01, $loop);
+
+ $this->expectPromiseResolved($timeout);
+ }
+
+ public function testRejectedPromiseWillNotStartTimer()
+ {
+ $promise = Promise\reject(new \RuntimeException());
+
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->never())->method('addTimer');
+
+ $timeout = Timer\timeout($promise, 0.01, $loop);
+
+ $this->expectPromiseRejected($timeout);
+ }
+
+ public function testCancelTimeoutWillCancelGivenPromise()
+ {
+ $promise = new \React\Promise\Promise(function () { }, $this->expectCallableOnce());
+
+ $timeout = Timer\timeout($promise, 0.01, $this->loop);
+
+ $timeout->cancel();
+ }
+
+ public function testCancelGivenPromiseWillReject()
+ {
+ $promise = new \React\Promise\Promise(function () { }, function ($resolve, $reject) { $reject(); });
+
+ $timeout = Timer\timeout($promise, 0.01, $this->loop);
+
+ $promise->cancel();
+
+ $this->expectPromiseRejected($promise);
+ $this->expectPromiseRejected($timeout);
+ }
+
+ public function testCancelTimeoutWillRejectIfGivenPromiseWillReject()
+ {
+ $promise = new \React\Promise\Promise(function () { }, function ($resolve, $reject) { $reject(); });
+
+ $timeout = Timer\timeout($promise, 0.01, $this->loop);
+
+ $timeout->cancel();
+
+ $this->expectPromiseRejected($promise);
+ $this->expectPromiseRejected($timeout);
+ }
+
+ public function testCancelTimeoutWillResolveIfGivenPromiseWillResolve()
+ {
+ $promise = new \React\Promise\Promise(function () { }, function ($resolve, $reject) { $resolve(); });
+
+ $timeout = Timer\timeout($promise, 0.01, $this->loop);
+
+ $timeout->cancel();
+
+ $this->expectPromiseResolved($promise);
+ $this->expectPromiseResolved($timeout);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/tests/TestCase.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/tests/TestCase.php
new file mode 100644
index 0000000..9d8d49a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/tests/TestCase.php
@@ -0,0 +1,61 @@
+loop = Factory::create();
+ }
+
+ protected function expectCallableOnce()
+ {
+ $mock = $this->createCallableMock();
+
+ $mock
+ ->expects($this->once())
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ protected function expectCallableNever()
+ {
+ $mock = $this->createCallableMock();
+
+ $mock
+ ->expects($this->never())
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ /**
+ * @link https://github.com/reactphp/react/blob/master/tests/React/Tests/Socket/TestCase.php (taken from reactphp/react)
+ */
+ protected function createCallableMock()
+ {
+ return $this->getMockBuilder('React\Tests\Promise\Timer\CallableStub')->getMock();
+ }
+
+ protected function expectPromiseRejected($promise)
+ {
+ return $promise->then($this->expectCallableNever(), $this->expectCallableOnce());
+ }
+
+ protected function expectPromiseResolved($promise)
+ {
+ return $promise->then($this->expectCallableOnce(), $this->expectCallableNever());
+ }
+
+ protected function expectPromisePending($promise)
+ {
+ return $promise->then($this->expectCallableNever(), $this->expectCallableNever());
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/tests/TimeoutExceptionTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/tests/TimeoutExceptionTest.php
new file mode 100644
index 0000000..e9bedd9
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise-timer/tests/TimeoutExceptionTest.php
@@ -0,0 +1,15 @@
+assertEquals(10, $e->getTimeout());
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/.gitignore b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/.gitignore
new file mode 100644
index 0000000..5241c60
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/.gitignore
@@ -0,0 +1,5 @@
+composer.lock
+composer.phar
+phpunit.xml
+build/
+vendor/
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/.travis.yml b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/.travis.yml
new file mode 100644
index 0000000..5d0c6ab
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/.travis.yml
@@ -0,0 +1,22 @@
+language: php
+
+php:
+ - 5.4
+ - 5.5
+ - 5.6
+ - 7.0
+ - nightly
+ - hhvm
+
+before_install:
+ - composer self-update
+
+install:
+ - composer install
+
+script:
+ - ./vendor/bin/phpunit -v --coverage-text --coverage-clover=./build/logs/clover.xml
+
+after_script:
+ - if [ -f ./build/logs/clover.xml ]; then travis_retry composer require satooshi/php-coveralls --no-interaction --update-with-dependencies; fi
+ - if [ -f ./build/logs/clover.xml ]; then php vendor/bin/coveralls -v; fi
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/CHANGELOG.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/CHANGELOG.md
new file mode 100644
index 0000000..484e542
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/CHANGELOG.md
@@ -0,0 +1,96 @@
+CHANGELOG for 2.x
+=================
+
+* 2.5.1 (2017-03-25)
+
+ * Fix circular references when resolving with a promise which follows
+ itself (#94).
+
+* 2.5.0 (2016-12-22)
+
+ * Revert automatic cancellation of pending collection promises once the
+ output promise resolves. This was introduced in 42d86b7 (PR #36, released
+ in [v2.3.0](https://github.com/reactphp/promise/releases/tag/v2.3.0)) and
+ was both unintended and backward incompatible.
+
+ If you need automatic cancellation, you can use something like:
+
+ ```php
+ function allAndCancel(array $promises)
+ {
+ return \React\Promise\all($promises)
+ ->always(function() use ($promises) {
+ foreach ($promises as $promise) {
+ if ($promise instanceof \React\Promise\CancellablePromiseInterface) {
+ $promise->cancel();
+ }
+ }
+ });
+ }
+ ```
+ * `all()` and `map()` functions now preserve the order of the array (#77).
+ * Fix circular references when resolving a promise with itself (#71).
+
+* 2.4.1 (2016-05-03)
+
+ * Fix `some()` not cancelling pending promises when too much input promises
+ reject (16ff799).
+
+* 2.4.0 (2016-03-31)
+
+ * Support foreign thenables in `resolve()`.
+ Any object that provides a `then()` method is now assimilated to a trusted
+ promise that follows the state of this thenable (#52).
+ * Fix `some()` and `any()` for input arrays containing not enough items
+ (#34).
+
+* 2.3.0 (2016-03-24)
+
+ * Allow cancellation of promises returned by functions working on promise
+ collections (#36).
+ * Handle `\Throwable` in the same way as `\Exception` (#51 by @joshdifabio).
+
+* 2.2.2 (2016-02-26)
+
+ * Fix cancellation handlers called multiple times (#47 by @clue).
+
+* 2.2.1 (2015-07-03)
+
+ * Fix stack error when resolving a promise in its own fulfillment or
+ rejection handlers.
+
+* 2.2.0 (2014-12-30)
+
+ * Introduce new `ExtendedPromiseInterface` implemented by all promises.
+ * Add new `done()` method (part of the `ExtendedPromiseInterface`).
+ * Add new `otherwise()` method (part of the `ExtendedPromiseInterface`).
+ * Add new `always()` method (part of the `ExtendedPromiseInterface`).
+ * Add new `progress()` method (part of the `ExtendedPromiseInterface`).
+ * Rename `Deferred::progress` to `Deferred::notify` to avoid confusion with
+ `ExtendedPromiseInterface::progress` (a `Deferred::progress` alias is
+ still available for backward compatibility)
+ * `resolve()` now always returns a `ExtendedPromiseInterface`.
+
+* 2.1.0 (2014-10-15)
+
+ * Introduce new `CancellablePromiseInterface` implemented by all promises.
+ * Add new `cancel()` method (part of the `CancellablePromiseInterface`).
+
+* 2.0.0 (2013-12-10)
+
+ New major release. The goal is to streamline the API and to make it more
+ compliant with other promise libraries and especially with the new upcoming
+ [ES6 promises specification](https://github.com/domenic/promises-unwrapping/).
+
+ * Add standalone Promise class.
+ * Add new `race()` function.
+ * BC break: Bump minimum PHP version to PHP 5.4.
+ * BC break: Remove `ResolverInterface` and `PromiseInterface` from
+ `Deferred`.
+ * BC break: Change signature of `PromiseInterface`.
+ * BC break: Remove `When` and `Util` classes and move static methods to
+ functions.
+ * BC break: `FulfilledPromise` and `RejectedPromise` now throw an exception
+ when initialized with a promise instead of a value/reason.
+ * BC break: `Deferred::resolve()` and `Deferred::reject()` no longer return
+ a promise.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/LICENSE b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/LICENSE
new file mode 100644
index 0000000..5919d20
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2012-2016 Jan Sorgalla
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/README.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/README.md
new file mode 100644
index 0000000..9c0558c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/README.md
@@ -0,0 +1,840 @@
+React/Promise
+=============
+
+A lightweight implementation of
+[CommonJS Promises/A](http://wiki.commonjs.org/wiki/Promises/A) for PHP.
+
+[](http://travis-ci.org/reactphp/promise)
+[](https://coveralls.io/github/reactphp/promise?branch=master)
+
+Table of Contents
+-----------------
+
+1. [Introduction](#introduction)
+2. [Concepts](#concepts)
+ * [Deferred](#deferred)
+ * [Promise](#promise)
+3. [API](#api)
+ * [Deferred](#deferred-1)
+ * [Deferred::promise()](#deferredpromise)
+ * [Deferred::resolve()](#deferredresolve)
+ * [Deferred::reject()](#deferredreject)
+ * [Deferred::notify()](#deferrednotify)
+ * [PromiseInterface](#promiseinterface)
+ * [PromiseInterface::then()](#promiseinterfacethen)
+ * [ExtendedPromiseInterface](#extendedpromiseinterface)
+ * [ExtendedPromiseInterface::done()](#extendedpromiseinterfacedone)
+ * [ExtendedPromiseInterface::otherwise()](#extendedpromiseinterfaceotherwise)
+ * [ExtendedPromiseInterface::always()](#extendedpromiseinterfacealways)
+ * [ExtendedPromiseInterface::progress()](#extendedpromiseinterfaceprogress)
+ * [CancellablePromiseInterface](#cancellablepromiseinterface)
+ * [CancellablePromiseInterface::cancel()](#cancellablepromiseinterfacecancel)
+ * [Promise](#promise-1)
+ * [FulfilledPromise](#fulfilledpromise)
+ * [RejectedPromise](#rejectedpromise)
+ * [LazyPromise](#lazypromise)
+ * [Functions](#functions)
+ * [resolve()](#resolve)
+ * [reject()](#reject)
+ * [all()](#all)
+ * [race()](#race)
+ * [any()](#any)
+ * [some()](#some)
+ * [map()](#map)
+ * [reduce()](#reduce)
+ * [PromisorInterface](#promisorinterface)
+4. [Examples](#examples)
+ * [How to use Deferred](#how-to-use-deferred)
+ * [How promise forwarding works](#how-promise-forwarding-works)
+ * [Resolution forwarding](#resolution-forwarding)
+ * [Rejection forwarding](#rejection-forwarding)
+ * [Mixed resolution and rejection forwarding](#mixed-resolution-and-rejection-forwarding)
+ * [Progress event forwarding](#progress-event-forwarding)
+ * [done() vs. then()](#done-vs-then)
+5. [Credits](#credits)
+6. [License](#license)
+
+Introduction
+------------
+
+React/Promise is a library implementing
+[CommonJS Promises/A](http://wiki.commonjs.org/wiki/Promises/A) for PHP.
+
+It also provides several other useful promise-related concepts, such as joining
+multiple promises and mapping and reducing collections of promises.
+
+If you've never heard about promises before,
+[read this first](https://gist.github.com/3889970).
+
+Concepts
+--------
+
+### Deferred
+
+A **Deferred** represents a computation or unit of work that may not have
+completed yet. Typically (but not always), that computation will be something
+that executes asynchronously and completes at some point in the future.
+
+### Promise
+
+While a deferred represents the computation itself, a **Promise** represents
+the result of that computation. Thus, each deferred has a promise that acts as
+a placeholder for its actual result.
+
+API
+---
+
+### Deferred
+
+A deferred represents an operation whose resolution is pending. It has separate
+promise and resolver parts.
+
+```php
+$deferred = new React\Promise\Deferred();
+
+$promise = $deferred->promise();
+
+$deferred->resolve(mixed $value = null);
+$deferred->reject(mixed $reason = null);
+$deferred->notify(mixed $update = null);
+```
+
+The `promise` method returns the promise of the deferred.
+
+The `resolve` and `reject` methods control the state of the deferred.
+
+The `notify` method is for progress notification.
+
+The constructor of the `Deferred` accepts an optional `$canceller` argument.
+See [Promise](#promise-1) for more information.
+
+#### Deferred::promise()
+
+```php
+$promise = $deferred->promise();
+```
+
+Returns the promise of the deferred, which you can hand out to others while
+keeping the authority to modify its state to yourself.
+
+#### Deferred::resolve()
+
+```php
+$deferred->resolve(mixed $value = null);
+```
+
+Resolves the promise returned by `promise()`. All consumers are notified by
+having `$onFulfilled` (which they registered via `$promise->then()`) called with
+`$value`.
+
+If `$value` itself is a promise, the promise will transition to the state of
+this promise once it is resolved.
+
+#### Deferred::reject()
+
+```php
+$deferred->reject(mixed $reason = null);
+```
+
+Rejects the promise returned by `promise()`, signalling that the deferred's
+computation failed.
+All consumers are notified by having `$onRejected` (which they registered via
+`$promise->then()`) called with `$reason`.
+
+If `$reason` itself is a promise, the promise will be rejected with the outcome
+of this promise regardless whether it fulfills or rejects.
+
+#### Deferred::notify()
+
+```php
+$deferred->notify(mixed $update = null);
+```
+
+Triggers progress notifications, to indicate to consumers that the computation
+is making progress toward its result.
+
+All consumers are notified by having `$onProgress` (which they registered via
+`$promise->then()`) called with `$update`.
+
+### PromiseInterface
+
+The promise interface provides the common interface for all promise
+implementations.
+
+A promise represents an eventual outcome, which is either fulfillment (success)
+and an associated value, or rejection (failure) and an associated reason.
+
+Once in the fulfilled or rejected state, a promise becomes immutable.
+Neither its state nor its result (or error) can be modified.
+
+#### Implementations
+
+* [Promise](#promise-1)
+* [FulfilledPromise](#fulfilledpromise)
+* [RejectedPromise](#rejectedpromise)
+* [LazyPromise](#lazypromise)
+
+#### PromiseInterface::then()
+
+```php
+$transformedPromise = $promise->then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null);
+```
+
+Transforms a promise's value by applying a function to the promise's fulfillment
+or rejection value. Returns a new promise for the transformed result.
+
+The `then()` method registers new fulfilled, rejection and progress handlers
+with a promise (all parameters are optional):
+
+ * `$onFulfilled` will be invoked once the promise is fulfilled and passed
+ the result as the first argument.
+ * `$onRejected` will be invoked once the promise is rejected and passed the
+ reason as the first argument.
+ * `$onProgress` will be invoked whenever the producer of the promise
+ triggers progress notifications and passed a single argument (whatever it
+ wants) to indicate progress.
+
+It returns a new promise that will fulfill with the return value of either
+`$onFulfilled` or `$onRejected`, whichever is called, or will reject with
+the thrown exception if either throws.
+
+A promise makes the following guarantees about handlers registered in
+the same call to `then()`:
+
+ 1. Only one of `$onFulfilled` or `$onRejected` will be called,
+ never both.
+ 2. `$onFulfilled` and `$onRejected` will never be called more
+ than once.
+ 3. `$onProgress` may be called multiple times.
+
+#### See also
+
+* [resolve()](#resolve) - Creating a resolved promise
+* [reject()](#reject) - Creating a rejected promise
+* [ExtendedPromiseInterface::done()](#extendedpromiseinterfacedone)
+* [done() vs. then()](#done-vs-then)
+
+### ExtendedPromiseInterface
+
+The ExtendedPromiseInterface extends the PromiseInterface with useful shortcut
+and utility methods which are not part of the Promises/A specification.
+
+#### Implementations
+
+* [Promise](#promise-1)
+* [FulfilledPromise](#fulfilledpromise)
+* [RejectedPromise](#rejectedpromise)
+* [LazyPromise](#lazypromise)
+
+#### ExtendedPromiseInterface::done()
+
+```php
+$promise->done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null);
+```
+
+Consumes the promise's ultimate value if the promise fulfills, or handles the
+ultimate error.
+
+It will cause a fatal error if either `$onFulfilled` or `$onRejected` throw or
+return a rejected promise.
+
+Since the purpose of `done()` is consumption rather than transformation,
+`done()` always returns `null`.
+
+#### See also
+
+* [PromiseInterface::then()](#promiseinterfacethen)
+* [done() vs. then()](#done-vs-then)
+
+#### ExtendedPromiseInterface::otherwise()
+
+```php
+$promise->otherwise(callable $onRejected);
+```
+
+Registers a rejection handler for promise. It is a shortcut for:
+
+```php
+$promise->then(null, $onRejected);
+```
+
+Additionally, you can type hint the `$reason` argument of `$onRejected` to catch
+only specific errors.
+
+```php
+$promise
+ ->otherwise(function (\RuntimeException $reason) {
+ // Only catch \RuntimeException instances
+ // All other types of errors will propagate automatically
+ })
+ ->otherwise(function ($reason) {
+ // Catch other errors
+ )};
+```
+
+#### ExtendedPromiseInterface::always()
+
+```php
+$newPromise = $promise->always(callable $onFulfilledOrRejected);
+```
+
+Allows you to execute "cleanup" type tasks in a promise chain.
+
+It arranges for `$onFulfilledOrRejected` to be called, with no arguments,
+when the promise is either fulfilled or rejected.
+
+* If `$promise` fulfills, and `$onFulfilledOrRejected` returns successfully,
+ `$newPromise` will fulfill with the same value as `$promise`.
+* If `$promise` fulfills, and `$onFulfilledOrRejected` throws or returns a
+ rejected promise, `$newPromise` will reject with the thrown exception or
+ rejected promise's reason.
+* If `$promise` rejects, and `$onFulfilledOrRejected` returns successfully,
+ `$newPromise` will reject with the same reason as `$promise`.
+* If `$promise` rejects, and `$onFulfilledOrRejected` throws or returns a
+ rejected promise, `$newPromise` will reject with the thrown exception or
+ rejected promise's reason.
+
+`always()` behaves similarly to the synchronous finally statement. When combined
+with `otherwise()`, `always()` allows you to write code that is similar to the familiar
+synchronous catch/finally pair.
+
+Consider the following synchronous code:
+
+```php
+try {
+ return doSomething();
+} catch(\Exception $e) {
+ return handleError($e);
+} finally {
+ cleanup();
+}
+```
+
+Similar asynchronous code (with `doSomething()` that returns a promise) can be
+written:
+
+```php
+return doSomething()
+ ->otherwise('handleError')
+ ->always('cleanup');
+```
+
+#### ExtendedPromiseInterface::progress()
+
+```php
+$promise->progress(callable $onProgress);
+```
+
+Registers a handler for progress updates from promise. It is a shortcut for:
+
+```php
+$promise->then(null, null, $onProgress);
+```
+
+### CancellablePromiseInterface
+
+A cancellable promise provides a mechanism for consumers to notify the creator
+of the promise that they are not longer interested in the result of an
+operation.
+
+#### CancellablePromiseInterface::cancel()
+
+``` php
+$promise->cancel();
+```
+
+The `cancel()` method notifies the creator of the promise that there is no
+further interest in the results of the operation.
+
+Once a promise is settled (either fulfilled or rejected), calling `cancel()` on
+a promise has no effect.
+
+#### Implementations
+
+* [Promise](#promise-1)
+* [FulfilledPromise](#fulfilledpromise)
+* [RejectedPromise](#rejectedpromise)
+* [LazyPromise](#lazypromise)
+
+### Promise
+
+Creates a promise whose state is controlled by the functions passed to
+`$resolver`.
+
+```php
+$resolver = function (callable $resolve, callable $reject, callable $notify) {
+ // Do some work, possibly asynchronously, and then
+ // resolve or reject. You can notify of progress events
+ // along the way if you want/need.
+
+ $resolve($awesomeResult);
+ // or $resolve($anotherPromise);
+ // or $reject($nastyError);
+ // or $notify($progressNotification);
+};
+
+$canceller = function (callable $resolve, callable $reject, callable $progress) {
+ // Cancel/abort any running operations like network connections, streams etc.
+
+ $reject(new \Exception('Promise cancelled'));
+};
+
+$promise = new React\Promise\Promise($resolver, $canceller);
+```
+
+The promise constructor receives a resolver function and an optional canceller
+function which both will be called with 3 arguments:
+
+ * `$resolve($value)` - Primary function that seals the fate of the
+ returned promise. Accepts either a non-promise value, or another promise.
+ When called with a non-promise value, fulfills promise with that value.
+ When called with another promise, e.g. `$resolve($otherPromise)`, promise's
+ fate will be equivalent to that of `$otherPromise`.
+ * `$reject($reason)` - Function that rejects the promise.
+ * `$notify($update)` - Function that issues progress events for the promise.
+
+If the resolver or canceller throw an exception, the promise will be rejected
+with that thrown exception as the rejection reason.
+
+The resolver function will be called immediately, the canceller function only
+once all consumers called the `cancel()` method of the promise.
+
+### FulfilledPromise
+
+Creates a already fulfilled promise.
+
+```php
+$promise = React\Promise\FulfilledPromise($value);
+```
+
+Note, that `$value` **cannot** be a promise. It's recommended to use
+[resolve()](#resolve) for creating resolved promises.
+
+### RejectedPromise
+
+Creates a already rejected promise.
+
+```php
+$promise = React\Promise\RejectedPromise($reason);
+```
+
+Note, that `$reason` **cannot** be a promise. It's recommended to use
+[reject()](#reject) for creating rejected promises.
+
+### LazyPromise
+
+Creates a promise which will be lazily initialized by `$factory` once a consumer
+calls the `then()` method.
+
+```php
+$factory = function () {
+ $deferred = new React\Promise\Deferred();
+
+ // Do some heavy stuff here and resolve the deferred once completed
+
+ return $deferred->promise();
+};
+
+$promise = React\Promise\LazyPromise($factory);
+
+// $factory will only be executed once we call then()
+$promise->then(function ($value) {
+});
+```
+
+### Functions
+
+Useful functions for creating, joining, mapping and reducing collections of
+promises.
+
+All functions working on promise collections (like `all()`, `race()`, `some()`
+etc.) support cancellation. This means, if you call `cancel()` on the returned
+promise, all promises in the collection are cancelled. If the collection itself
+is a promise which resolves to an array, this promise is also cancelled.
+
+#### resolve()
+
+```php
+$promise = React\Promise\resolve(mixed $promiseOrValue);
+```
+
+Creates a promise for the supplied `$promiseOrValue`.
+
+If `$promiseOrValue` is a value, it will be the resolution value of the
+returned promise.
+
+If `$promiseOrValue` is a thenable (any object that provides a `then()` method),
+a trusted promise that follows the state of the thenable is returned.
+
+If `$promiseOrValue` is a promise, it will be returned as is.
+
+Note: The promise returned is always a promise implementing
+[ExtendedPromiseInterface](#extendedpromiseinterface). If you pass in a custom
+promise which only implements [PromiseInterface](#promiseinterface), this
+promise will be assimilated to a extended promise following `$promiseOrValue`.
+
+#### reject()
+
+```php
+$promise = React\Promise\reject(mixed $promiseOrValue);
+```
+
+Creates a rejected promise for the supplied `$promiseOrValue`.
+
+If `$promiseOrValue` is a value, it will be the rejection value of the
+returned promise.
+
+If `$promiseOrValue` is a promise, its completion value will be the rejected
+value of the returned promise.
+
+This can be useful in situations where you need to reject a promise without
+throwing an exception. For example, it allows you to propagate a rejection with
+the value of another promise.
+
+#### all()
+
+```php
+$promise = React\Promise\all(array|React\Promise\PromiseInterface $promisesOrValues);
+```
+
+Returns a promise that will resolve only once all the items in
+`$promisesOrValues` have resolved. The resolution value of the returned promise
+will be an array containing the resolution values of each of the items in
+`$promisesOrValues`.
+
+#### race()
+
+```php
+$promise = React\Promise\race(array|React\Promise\PromiseInterface $promisesOrValues);
+```
+
+Initiates a competitive race that allows one winner. Returns a promise which is
+resolved in the same way the first settled promise resolves.
+
+#### any()
+
+```php
+$promise = React\Promise\any(array|React\Promise\PromiseInterface $promisesOrValues);
+```
+
+Returns a promise that will resolve when any one of the items in
+`$promisesOrValues` resolves. The resolution value of the returned promise
+will be the resolution value of the triggering item.
+
+The returned promise will only reject if *all* items in `$promisesOrValues` are
+rejected. The rejection value will be an array of all rejection reasons.
+
+The returned promise will also reject with a `React\Promise\Exception\LengthException`
+if `$promisesOrValues` contains 0 items.
+
+#### some()
+
+```php
+$promise = React\Promise\some(array|React\Promise\PromiseInterface $promisesOrValues, integer $howMany);
+```
+
+Returns a promise that will resolve when `$howMany` of the supplied items in
+`$promisesOrValues` resolve. The resolution value of the returned promise
+will be an array of length `$howMany` containing the resolution values of the
+triggering items.
+
+The returned promise will reject if it becomes impossible for `$howMany` items
+to resolve (that is, when `(count($promisesOrValues) - $howMany) + 1` items
+reject). The rejection value will be an array of
+`(count($promisesOrValues) - $howMany) + 1` rejection reasons.
+
+The returned promise will also reject with a `React\Promise\Exception\LengthException`
+if `$promisesOrValues` contains less items than `$howMany`.
+
+#### map()
+
+```php
+$promise = React\Promise\map(array|React\Promise\PromiseInterface $promisesOrValues, callable $mapFunc);
+```
+
+Traditional map function, similar to `array_map()`, but allows input to contain
+promises and/or values, and `$mapFunc` may return either a value or a promise.
+
+The map function receives each item as argument, where item is a fully resolved
+value of a promise or value in `$promisesOrValues`.
+
+#### reduce()
+
+```php
+$promise = React\Promise\reduce(array|React\Promise\PromiseInterface $promisesOrValues, callable $reduceFunc , $initialValue = null);
+```
+
+Traditional reduce function, similar to `array_reduce()`, but input may contain
+promises and/or values, and `$reduceFunc` may return either a value or a
+promise, *and* `$initialValue` may be a promise or a value for the starting
+value.
+
+### PromisorInterface
+
+The `React\Promise\PromisorInterface` provides a common interface for objects
+that provide a promise. `React\Promise\Deferred` implements it, but since it
+is part of the public API anyone can implement it.
+
+Examples
+--------
+
+### How to use Deferred
+
+```php
+function getAwesomeResultPromise()
+{
+ $deferred = new React\Promise\Deferred();
+
+ // Execute a Node.js-style function using the callback pattern
+ computeAwesomeResultAsynchronously(function ($error, $result) use ($deferred) {
+ if ($error) {
+ $deferred->reject($error);
+ } else {
+ $deferred->resolve($result);
+ }
+ });
+
+ // Return the promise
+ return $deferred->promise();
+}
+
+getAwesomeResultPromise()
+ ->then(
+ function ($value) {
+ // Deferred resolved, do something with $value
+ },
+ function ($reason) {
+ // Deferred rejected, do something with $reason
+ },
+ function ($update) {
+ // Progress notification triggered, do something with $update
+ }
+ );
+```
+
+### How promise forwarding works
+
+A few simple examples to show how the mechanics of Promises/A forwarding works.
+These examples are contrived, of course, and in real usage, promise chains will
+typically be spread across several function calls, or even several levels of
+your application architecture.
+
+#### Resolution forwarding
+
+Resolved promises forward resolution values to the next promise.
+The first promise, `$deferred->promise()`, will resolve with the value passed
+to `$deferred->resolve()` below.
+
+Each call to `then()` returns a new promise that will resolve with the return
+value of the previous handler. This creates a promise "pipeline".
+
+```php
+$deferred = new React\Promise\Deferred();
+
+$deferred->promise()
+ ->then(function ($x) {
+ // $x will be the value passed to $deferred->resolve() below
+ // and returns a *new promise* for $x + 1
+ return $x + 1;
+ })
+ ->then(function ($x) {
+ // $x === 2
+ // This handler receives the return value of the
+ // previous handler.
+ return $x + 1;
+ })
+ ->then(function ($x) {
+ // $x === 3
+ // This handler receives the return value of the
+ // previous handler.
+ return $x + 1;
+ })
+ ->then(function ($x) {
+ // $x === 4
+ // This handler receives the return value of the
+ // previous handler.
+ echo 'Resolve ' . $x;
+ });
+
+$deferred->resolve(1); // Prints "Resolve 4"
+```
+
+#### Rejection forwarding
+
+Rejected promises behave similarly, and also work similarly to try/catch:
+When you catch an exception, you must rethrow for it to propagate.
+
+Similarly, when you handle a rejected promise, to propagate the rejection,
+"rethrow" it by either returning a rejected promise, or actually throwing
+(since promise translates thrown exceptions into rejections)
+
+```php
+$deferred = new React\Promise\Deferred();
+
+$deferred->promise()
+ ->then(function ($x) {
+ throw new \Exception($x + 1);
+ })
+ ->otherwise(function (\Exception $x) {
+ // Propagate the rejection
+ throw $x;
+ })
+ ->otherwise(function (\Exception $x) {
+ // Can also propagate by returning another rejection
+ return React\Promise\reject(
+ new \Exception($x->getMessage() + 1)
+ );
+ })
+ ->otherwise(function ($x) {
+ echo 'Reject ' . $x->getMessage(); // 3
+ });
+
+$deferred->resolve(1); // Prints "Reject 3"
+```
+
+#### Mixed resolution and rejection forwarding
+
+Just like try/catch, you can choose to propagate or not. Mixing resolutions and
+rejections will still forward handler results in a predictable way.
+
+```php
+$deferred = new React\Promise\Deferred();
+
+$deferred->promise()
+ ->then(function ($x) {
+ return $x + 1;
+ })
+ ->then(function ($x) {
+ throw new \Exception($x + 1);
+ })
+ ->otherwise(function (\Exception $x) {
+ // Handle the rejection, and don't propagate.
+ // This is like catch without a rethrow
+ return $x->getMessage() + 1;
+ })
+ ->then(function ($x) {
+ echo 'Mixed ' . $x; // 4
+ });
+
+$deferred->resolve(1); // Prints "Mixed 4"
+```
+
+#### Progress event forwarding
+
+In the same way as resolution and rejection handlers, your progress handler
+**MUST** return a progress event to be propagated to the next link in the chain.
+If you return nothing, `null` will be propagated.
+
+Also in the same way as resolutions and rejections, if you don't register a
+progress handler, the update will be propagated through.
+
+If your progress handler throws an exception, the exception will be propagated
+to the next link in the chain. The best thing to do is to ensure your progress
+handlers do not throw exceptions.
+
+This gives you the opportunity to transform progress events at each step in the
+chain so that they are meaningful to the next step. It also allows you to choose
+not to transform them, and simply let them propagate untransformed, by not
+registering a progress handler.
+
+```php
+$deferred = new React\Promise\Deferred();
+
+$deferred->promise()
+ ->progress(function ($update) {
+ return $update + 1;
+ })
+ ->progress(function ($update) {
+ echo 'Progress ' . $update; // 2
+ });
+
+$deferred->notify(1); // Prints "Progress 2"
+```
+
+### done() vs. then()
+
+The golden rule is:
+
+ Either return your promise, or call done() on it.
+
+At a first glance, `then()` and `done()` seem very similar. However, there are
+important distinctions.
+
+The intent of `then()` is to transform a promise's value and to pass or return
+a new promise for the transformed value along to other parts of your code.
+
+The intent of `done()` is to consume a promise's value, transferring
+responsibility for the value to your code.
+
+In addition to transforming a value, `then()` allows you to recover from, or
+propagate intermediate errors. Any errors that are not handled will be caught
+by the promise machinery and used to reject the promise returned by `then()`.
+
+Calling `done()` transfers all responsibility for errors to your code. If an
+error (either a thrown exception or returned rejection) escapes the
+`$onFulfilled` or `$onRejected` callbacks you provide to done, it will be
+rethrown in an uncatchable way causing a fatal error.
+
+```php
+function getJsonResult()
+{
+ return queryApi()
+ ->then(
+ // Transform API results to an object
+ function ($jsonResultString) {
+ return json_decode($jsonResultString);
+ },
+ // Transform API errors to an exception
+ function ($jsonErrorString) {
+ $object = json_decode($jsonErrorString);
+ throw new ApiErrorException($object->errorMessage);
+ }
+ );
+}
+
+// Here we provide no rejection handler. If the promise returned has been
+// rejected, the ApiErrorException will be thrown
+getJsonResult()
+ ->done(
+ // Consume transformed object
+ function ($jsonResultObject) {
+ // Do something with $jsonResultObject
+ }
+ );
+
+// Here we provide a rejection handler which will either throw while debugging
+// or log the exception
+getJsonResult()
+ ->done(
+ function ($jsonResultObject) {
+ // Do something with $jsonResultObject
+ },
+ function (ApiErrorException $exception) {
+ if (isDebug()) {
+ throw $exception;
+ } else {
+ logException($exception);
+ }
+ }
+ );
+```
+
+Note that if a rejection value is not an instance of `\Exception`, it will be
+wrapped in an exception of the type `React\Promise\UnhandledRejectionException`.
+
+You can get the original rejection reason by calling `$exception->getReason()`.
+
+Credits
+-------
+
+React/Promise is a port of [when.js](https://github.com/cujojs/when)
+by [Brian Cavalier](https://github.com/briancavalier).
+
+Also, large parts of the documentation have been ported from the when.js
+[Wiki](https://github.com/cujojs/when/wiki) and the
+[API docs](https://github.com/cujojs/when/blob/master/docs/api.md).
+
+License
+-------
+
+React/Promise is released under the [MIT](https://github.com/reactphp/promise/blob/master/LICENSE) license.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/composer.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/composer.json
new file mode 100644
index 0000000..2fc4809
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/composer.json
@@ -0,0 +1,29 @@
+{
+ "name": "react/promise",
+ "description": "A lightweight implementation of CommonJS Promises/A for PHP",
+ "license": "MIT",
+ "authors": [
+ {"name": "Jan Sorgalla", "email": "jsorgalla@gmail.com"}
+ ],
+ "require": {
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8"
+ },
+ "autoload": {
+ "psr-4": {
+ "React\\Promise\\": "src/"
+ },
+ "files": ["src/functions_include.php"]
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "React\\Promise\\": "tests/fixtures"
+ }
+ },
+ "keywords": [
+ "promise",
+ "promises"
+ ]
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/phpunit.xml.dist b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/phpunit.xml.dist
new file mode 100644
index 0000000..b9a689d
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/phpunit.xml.dist
@@ -0,0 +1,28 @@
+
+
+
+
+
+ ./tests/
+
+
+
+
+
+ ./src/
+
+ ./src/functions_include.php
+
+
+
+
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/CancellablePromiseInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/CancellablePromiseInterface.php
new file mode 100644
index 0000000..896db2d
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/CancellablePromiseInterface.php
@@ -0,0 +1,11 @@
+started) {
+ return;
+ }
+
+ $this->started = true;
+ $this->drain();
+ }
+
+ public function enqueue($cancellable)
+ {
+ if (!method_exists($cancellable, 'then') || !method_exists($cancellable, 'cancel')) {
+ return;
+ }
+
+ $length = array_push($this->queue, $cancellable);
+
+ if ($this->started && 1 === $length) {
+ $this->drain();
+ }
+ }
+
+ private function drain()
+ {
+ for ($i = key($this->queue); isset($this->queue[$i]); $i++) {
+ $cancellable = $this->queue[$i];
+
+ $exception = null;
+
+ try {
+ $cancellable->cancel();
+ } catch (\Throwable $exception) {
+ } catch (\Exception $exception) {
+ }
+
+ unset($this->queue[$i]);
+
+ if ($exception) {
+ throw $exception;
+ }
+ }
+
+ $this->queue = [];
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/Deferred.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/Deferred.php
new file mode 100644
index 0000000..f23980c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/Deferred.php
@@ -0,0 +1,60 @@
+canceller = $canceller;
+ }
+
+ public function promise()
+ {
+ if (null === $this->promise) {
+ $this->promise = new Promise(function ($resolve, $reject, $notify) {
+ $this->resolveCallback = $resolve;
+ $this->rejectCallback = $reject;
+ $this->notifyCallback = $notify;
+ }, $this->canceller);
+ }
+
+ return $this->promise;
+ }
+
+ public function resolve($value = null)
+ {
+ $this->promise();
+
+ call_user_func($this->resolveCallback, $value);
+ }
+
+ public function reject($reason = null)
+ {
+ $this->promise();
+
+ call_user_func($this->rejectCallback, $reason);
+ }
+
+ public function notify($update = null)
+ {
+ $this->promise();
+
+ call_user_func($this->notifyCallback, $update);
+ }
+
+ /**
+ * @deprecated 2.2.0
+ * @see Deferred::notify()
+ */
+ public function progress($update = null)
+ {
+ $this->notify($update);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/Exception/LengthException.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/Exception/LengthException.php
new file mode 100644
index 0000000..775c48d
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/Exception/LengthException.php
@@ -0,0 +1,7 @@
+value = $value;
+ }
+
+ public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
+ {
+ if (null === $onFulfilled) {
+ return $this;
+ }
+
+ try {
+ return resolve($onFulfilled($this->value));
+ } catch (\Throwable $exception) {
+ return new RejectedPromise($exception);
+ } catch (\Exception $exception) {
+ return new RejectedPromise($exception);
+ }
+ }
+
+ public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
+ {
+ if (null === $onFulfilled) {
+ return;
+ }
+
+ $result = $onFulfilled($this->value);
+
+ if ($result instanceof ExtendedPromiseInterface) {
+ $result->done();
+ }
+ }
+
+ public function otherwise(callable $onRejected)
+ {
+ return $this;
+ }
+
+ public function always(callable $onFulfilledOrRejected)
+ {
+ return $this->then(function ($value) use ($onFulfilledOrRejected) {
+ return resolve($onFulfilledOrRejected())->then(function () use ($value) {
+ return $value;
+ });
+ });
+ }
+
+ public function progress(callable $onProgress)
+ {
+ return $this;
+ }
+
+ public function cancel()
+ {
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/LazyPromise.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/LazyPromise.php
new file mode 100644
index 0000000..7e3a3d3
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/LazyPromise.php
@@ -0,0 +1,63 @@
+factory = $factory;
+ }
+
+ public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
+ {
+ return $this->promise()->then($onFulfilled, $onRejected, $onProgress);
+ }
+
+ public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
+ {
+ return $this->promise()->done($onFulfilled, $onRejected, $onProgress);
+ }
+
+ public function otherwise(callable $onRejected)
+ {
+ return $this->promise()->otherwise($onRejected);
+ }
+
+ public function always(callable $onFulfilledOrRejected)
+ {
+ return $this->promise()->always($onFulfilledOrRejected);
+ }
+
+ public function progress(callable $onProgress)
+ {
+ return $this->promise()->progress($onProgress);
+ }
+
+ public function cancel()
+ {
+ return $this->promise()->cancel();
+ }
+
+ /**
+ * @internal
+ * @see Promise::settle()
+ */
+ public function promise()
+ {
+ if (null === $this->promise) {
+ try {
+ $this->promise = resolve(call_user_func($this->factory));
+ } catch (\Throwable $exception) {
+ $this->promise = new RejectedPromise($exception);
+ } catch (\Exception $exception) {
+ $this->promise = new RejectedPromise($exception);
+ }
+ }
+
+ return $this->promise;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/Promise.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/Promise.php
new file mode 100644
index 0000000..0261eb3
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/Promise.php
@@ -0,0 +1,216 @@
+canceller = $canceller;
+ $this->call($resolver);
+ }
+
+ public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
+ {
+ if (null !== $this->result) {
+ return $this->result->then($onFulfilled, $onRejected, $onProgress);
+ }
+
+ if (null === $this->canceller) {
+ return new static($this->resolver($onFulfilled, $onRejected, $onProgress));
+ }
+
+ $this->requiredCancelRequests++;
+
+ return new static($this->resolver($onFulfilled, $onRejected, $onProgress), function () {
+ if (++$this->cancelRequests < $this->requiredCancelRequests) {
+ return;
+ }
+
+ $this->cancel();
+ });
+ }
+
+ public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
+ {
+ if (null !== $this->result) {
+ return $this->result->done($onFulfilled, $onRejected, $onProgress);
+ }
+
+ $this->handlers[] = function (ExtendedPromiseInterface $promise) use ($onFulfilled, $onRejected) {
+ $promise
+ ->done($onFulfilled, $onRejected);
+ };
+
+ if ($onProgress) {
+ $this->progressHandlers[] = $onProgress;
+ }
+ }
+
+ public function otherwise(callable $onRejected)
+ {
+ return $this->then(null, function ($reason) use ($onRejected) {
+ if (!_checkTypehint($onRejected, $reason)) {
+ return new RejectedPromise($reason);
+ }
+
+ return $onRejected($reason);
+ });
+ }
+
+ public function always(callable $onFulfilledOrRejected)
+ {
+ return $this->then(function ($value) use ($onFulfilledOrRejected) {
+ return resolve($onFulfilledOrRejected())->then(function () use ($value) {
+ return $value;
+ });
+ }, function ($reason) use ($onFulfilledOrRejected) {
+ return resolve($onFulfilledOrRejected())->then(function () use ($reason) {
+ return new RejectedPromise($reason);
+ });
+ });
+ }
+
+ public function progress(callable $onProgress)
+ {
+ return $this->then(null, null, $onProgress);
+ }
+
+ public function cancel()
+ {
+ if (null === $this->canceller || null !== $this->result) {
+ return;
+ }
+
+ $canceller = $this->canceller;
+ $this->canceller = null;
+
+ $this->call($canceller);
+ }
+
+ private function resolver(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
+ {
+ return function ($resolve, $reject, $notify) use ($onFulfilled, $onRejected, $onProgress) {
+ if ($onProgress) {
+ $progressHandler = function ($update) use ($notify, $onProgress) {
+ try {
+ $notify($onProgress($update));
+ } catch (\Throwable $e) {
+ $notify($e);
+ } catch (\Exception $e) {
+ $notify($e);
+ }
+ };
+ } else {
+ $progressHandler = $notify;
+ }
+
+ $this->handlers[] = function (ExtendedPromiseInterface $promise) use ($onFulfilled, $onRejected, $resolve, $reject, $progressHandler) {
+ $promise
+ ->then($onFulfilled, $onRejected)
+ ->done($resolve, $reject, $progressHandler);
+ };
+
+ $this->progressHandlers[] = $progressHandler;
+ };
+ }
+
+ private function resolve($value = null)
+ {
+ if (null !== $this->result) {
+ return;
+ }
+
+ $this->settle(resolve($value));
+ }
+
+ private function reject($reason = null)
+ {
+ if (null !== $this->result) {
+ return;
+ }
+
+ $this->settle(reject($reason));
+ }
+
+ private function notify($update = null)
+ {
+ if (null !== $this->result) {
+ return;
+ }
+
+ foreach ($this->progressHandlers as $handler) {
+ $handler($update);
+ }
+ }
+
+ private function settle(ExtendedPromiseInterface $promise)
+ {
+ $promise = $this->unwrap($promise);
+
+ $handlers = $this->handlers;
+
+ $this->progressHandlers = $this->handlers = [];
+ $this->result = $promise;
+
+ foreach ($handlers as $handler) {
+ $handler($promise);
+ }
+ }
+
+ private function unwrap($promise)
+ {
+ $promise = $this->extract($promise);
+
+ while ($promise instanceof self && null !== $promise->result) {
+ $promise = $this->extract($promise->result);
+ }
+
+ return $promise;
+ }
+
+ private function extract($promise)
+ {
+ if ($promise instanceof LazyPromise) {
+ $promise = $promise->promise();
+ }
+
+ if ($promise === $this) {
+ return new RejectedPromise(
+ new \LogicException('Cannot resolve a promise with itself.')
+ );
+ }
+
+ return $promise;
+ }
+
+ private function call(callable $callback)
+ {
+ try {
+ $callback(
+ function ($value = null) {
+ $this->resolve($value);
+ },
+ function ($reason = null) {
+ $this->reject($reason);
+ },
+ function ($update = null) {
+ $this->notify($update);
+ }
+ );
+ } catch (\Throwable $e) {
+ $this->reject($e);
+ } catch (\Exception $e) {
+ $this->reject($e);
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/PromiseInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/PromiseInterface.php
new file mode 100644
index 0000000..d80d114
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/PromiseInterface.php
@@ -0,0 +1,11 @@
+reason = $reason;
+ }
+
+ public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
+ {
+ if (null === $onRejected) {
+ return $this;
+ }
+
+ try {
+ return resolve($onRejected($this->reason));
+ } catch (\Throwable $exception) {
+ return new RejectedPromise($exception);
+ } catch (\Exception $exception) {
+ return new RejectedPromise($exception);
+ }
+ }
+
+ public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
+ {
+ if (null === $onRejected) {
+ throw UnhandledRejectionException::resolve($this->reason);
+ }
+
+ $result = $onRejected($this->reason);
+
+ if ($result instanceof self) {
+ throw UnhandledRejectionException::resolve($result->reason);
+ }
+
+ if ($result instanceof ExtendedPromiseInterface) {
+ $result->done();
+ }
+ }
+
+ public function otherwise(callable $onRejected)
+ {
+ if (!_checkTypehint($onRejected, $this->reason)) {
+ return $this;
+ }
+
+ return $this->then(null, $onRejected);
+ }
+
+ public function always(callable $onFulfilledOrRejected)
+ {
+ return $this->then(null, function ($reason) use ($onFulfilledOrRejected) {
+ return resolve($onFulfilledOrRejected())->then(function () use ($reason) {
+ return new RejectedPromise($reason);
+ });
+ });
+ }
+
+ public function progress(callable $onProgress)
+ {
+ return $this;
+ }
+
+ public function cancel()
+ {
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/UnhandledRejectionException.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/UnhandledRejectionException.php
new file mode 100644
index 0000000..a44b7a1
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/UnhandledRejectionException.php
@@ -0,0 +1,31 @@
+reason = $reason;
+
+ $message = sprintf('Unhandled Rejection: %s', json_encode($reason));
+
+ parent::__construct($message, 0);
+ }
+
+ public function getReason()
+ {
+ return $this->reason;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/functions.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/functions.php
new file mode 100644
index 0000000..70c0eb7
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/functions.php
@@ -0,0 +1,244 @@
+then($resolve, $reject, $notify);
+ }, $canceller);
+ }
+
+ return new FulfilledPromise($promiseOrValue);
+}
+
+function reject($promiseOrValue = null)
+{
+ if ($promiseOrValue instanceof PromiseInterface) {
+ return resolve($promiseOrValue)->then(function ($value) {
+ return new RejectedPromise($value);
+ });
+ }
+
+ return new RejectedPromise($promiseOrValue);
+}
+
+function all($promisesOrValues)
+{
+ return map($promisesOrValues, function ($val) {
+ return $val;
+ });
+}
+
+function race($promisesOrValues)
+{
+ $cancellationQueue = new CancellationQueue();
+ $cancellationQueue->enqueue($promisesOrValues);
+
+ return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $cancellationQueue) {
+ resolve($promisesOrValues)
+ ->done(function ($array) use ($cancellationQueue, $resolve, $reject, $notify) {
+ if (!is_array($array) || !$array) {
+ $resolve();
+ return;
+ }
+
+ foreach ($array as $promiseOrValue) {
+ $cancellationQueue->enqueue($promiseOrValue);
+
+ resolve($promiseOrValue)
+ ->done($resolve, $reject, $notify);
+ }
+ }, $reject, $notify);
+ }, $cancellationQueue);
+}
+
+function any($promisesOrValues)
+{
+ return some($promisesOrValues, 1)
+ ->then(function ($val) {
+ return array_shift($val);
+ });
+}
+
+function some($promisesOrValues, $howMany)
+{
+ $cancellationQueue = new CancellationQueue();
+ $cancellationQueue->enqueue($promisesOrValues);
+
+ return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $howMany, $cancellationQueue) {
+ resolve($promisesOrValues)
+ ->done(function ($array) use ($howMany, $cancellationQueue, $resolve, $reject, $notify) {
+ if (!is_array($array) || $howMany < 1) {
+ $resolve([]);
+ return;
+ }
+
+ $len = count($array);
+
+ if ($len < $howMany) {
+ throw new Exception\LengthException(
+ sprintf(
+ 'Input array must contain at least %d item%s but contains only %s item%s.',
+ $howMany,
+ 1 === $howMany ? '' : 's',
+ $len,
+ 1 === $len ? '' : 's'
+ )
+ );
+ }
+
+ $toResolve = $howMany;
+ $toReject = ($len - $toResolve) + 1;
+ $values = [];
+ $reasons = [];
+
+ foreach ($array as $i => $promiseOrValue) {
+ $fulfiller = function ($val) use ($i, &$values, &$toResolve, $toReject, $resolve) {
+ if ($toResolve < 1 || $toReject < 1) {
+ return;
+ }
+
+ $values[$i] = $val;
+
+ if (0 === --$toResolve) {
+ $resolve($values);
+ }
+ };
+
+ $rejecter = function ($reason) use ($i, &$reasons, &$toReject, $toResolve, $reject) {
+ if ($toResolve < 1 || $toReject < 1) {
+ return;
+ }
+
+ $reasons[$i] = $reason;
+
+ if (0 === --$toReject) {
+ $reject($reasons);
+ }
+ };
+
+ $cancellationQueue->enqueue($promiseOrValue);
+
+ resolve($promiseOrValue)
+ ->done($fulfiller, $rejecter, $notify);
+ }
+ }, $reject, $notify);
+ }, $cancellationQueue);
+}
+
+function map($promisesOrValues, callable $mapFunc)
+{
+ $cancellationQueue = new CancellationQueue();
+ $cancellationQueue->enqueue($promisesOrValues);
+
+ return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $mapFunc, $cancellationQueue) {
+ resolve($promisesOrValues)
+ ->done(function ($array) use ($mapFunc, $cancellationQueue, $resolve, $reject, $notify) {
+ if (!is_array($array) || !$array) {
+ $resolve([]);
+ return;
+ }
+
+ $toResolve = count($array);
+ $values = [];
+
+ foreach ($array as $i => $promiseOrValue) {
+ $cancellationQueue->enqueue($promiseOrValue);
+ $values[$i] = null;
+
+ resolve($promiseOrValue)
+ ->then($mapFunc)
+ ->done(
+ function ($mapped) use ($i, &$values, &$toResolve, $resolve) {
+ $values[$i] = $mapped;
+
+ if (0 === --$toResolve) {
+ $resolve($values);
+ }
+ },
+ $reject,
+ $notify
+ );
+ }
+ }, $reject, $notify);
+ }, $cancellationQueue);
+}
+
+function reduce($promisesOrValues, callable $reduceFunc, $initialValue = null)
+{
+ $cancellationQueue = new CancellationQueue();
+ $cancellationQueue->enqueue($promisesOrValues);
+
+ return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $reduceFunc, $initialValue, $cancellationQueue) {
+ resolve($promisesOrValues)
+ ->done(function ($array) use ($reduceFunc, $initialValue, $cancellationQueue, $resolve, $reject, $notify) {
+ if (!is_array($array)) {
+ $array = [];
+ }
+
+ $total = count($array);
+ $i = 0;
+
+ // Wrap the supplied $reduceFunc with one that handles promises and then
+ // delegates to the supplied.
+ $wrappedReduceFunc = function ($current, $val) use ($reduceFunc, $cancellationQueue, $total, &$i) {
+ $cancellationQueue->enqueue($val);
+
+ return $current
+ ->then(function ($c) use ($reduceFunc, $total, &$i, $val) {
+ return resolve($val)
+ ->then(function ($value) use ($reduceFunc, $total, &$i, $c) {
+ return $reduceFunc($c, $value, $i++, $total);
+ });
+ });
+ };
+
+ $cancellationQueue->enqueue($initialValue);
+
+ array_reduce($array, $wrappedReduceFunc, resolve($initialValue))
+ ->done($resolve, $reject, $notify);
+ }, $reject, $notify);
+ }, $cancellationQueue);
+}
+
+// Internal functions
+function _checkTypehint(callable $callback, $object)
+{
+ if (!is_object($object)) {
+ return true;
+ }
+
+ if (is_array($callback)) {
+ $callbackReflection = new \ReflectionMethod($callback[0], $callback[1]);
+ } elseif (is_object($callback) && !$callback instanceof \Closure) {
+ $callbackReflection = new \ReflectionMethod($callback, '__invoke');
+ } else {
+ $callbackReflection = new \ReflectionFunction($callback);
+ }
+
+ $parameters = $callbackReflection->getParameters();
+
+ if (!isset($parameters[0])) {
+ return true;
+ }
+
+ $expectedException = $parameters[0];
+
+ if (!$expectedException->getClass()) {
+ return true;
+ }
+
+ return $expectedException->getClass()->isInstance($object);
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/functions_include.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/functions_include.php
new file mode 100644
index 0000000..c71decb
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/src/functions_include.php
@@ -0,0 +1,5 @@
+enqueue($p);
+
+ $cancellationQueue();
+
+ $this->assertTrue($p->cancelCalled);
+ }
+
+ /** @test */
+ public function ignoresSimpleCancellable()
+ {
+ $p = new SimpleTestCancellable();
+
+ $cancellationQueue = new CancellationQueue();
+ $cancellationQueue->enqueue($p);
+
+ $cancellationQueue();
+
+ $this->assertFalse($p->cancelCalled);
+ }
+
+ /** @test */
+ public function callsCancelOnPromisesEnqueuedBeforeStart()
+ {
+ $d1 = $this->getCancellableDeferred();
+ $d2 = $this->getCancellableDeferred();
+
+ $cancellationQueue = new CancellationQueue();
+ $cancellationQueue->enqueue($d1->promise());
+ $cancellationQueue->enqueue($d2->promise());
+
+ $cancellationQueue();
+ }
+
+ /** @test */
+ public function callsCancelOnPromisesEnqueuedAfterStart()
+ {
+ $d1 = $this->getCancellableDeferred();
+ $d2 = $this->getCancellableDeferred();
+
+ $cancellationQueue = new CancellationQueue();
+
+ $cancellationQueue();
+
+ $cancellationQueue->enqueue($d2->promise());
+ $cancellationQueue->enqueue($d1->promise());
+ }
+
+ /** @test */
+ public function doesNotCallCancelTwiceWhenStartedTwice()
+ {
+ $d = $this->getCancellableDeferred();
+
+ $cancellationQueue = new CancellationQueue();
+ $cancellationQueue->enqueue($d->promise());
+
+ $cancellationQueue();
+ $cancellationQueue();
+ }
+
+ /** @test */
+ public function rethrowsExceptionsThrownFromCancel()
+ {
+ $this->setExpectedException('\Exception', 'test');
+
+ $mock = $this
+ ->getMockBuilder('React\Promise\CancellablePromiseInterface')
+ ->getMock();
+ $mock
+ ->expects($this->once())
+ ->method('cancel')
+ ->will($this->throwException(new \Exception('test')));
+
+ $cancellationQueue = new CancellationQueue();
+ $cancellationQueue->enqueue($mock);
+
+ $cancellationQueue();
+ }
+
+ private function getCancellableDeferred()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke');
+
+ return new Deferred($mock);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/DeferredTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/DeferredTest.php
new file mode 100644
index 0000000..16212e9
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/DeferredTest.php
@@ -0,0 +1,42 @@
+ [$d, 'promise'],
+ 'resolve' => [$d, 'resolve'],
+ 'reject' => [$d, 'reject'],
+ 'notify' => [$d, 'progress'],
+ 'settle' => [$d, 'resolve'],
+ ]);
+ }
+
+ /** @test */
+ public function progressIsAnAliasForNotify()
+ {
+ $deferred = new Deferred();
+
+ $sentinel = new \stdClass();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($sentinel);
+
+ $deferred->promise()
+ ->then($this->expectCallableNever(), $this->expectCallableNever(), $mock);
+
+ $deferred->progress($sentinel);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FulfilledPromiseTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FulfilledPromiseTest.php
new file mode 100644
index 0000000..97fc8f6
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FulfilledPromiseTest.php
@@ -0,0 +1,50 @@
+ function () use (&$promise) {
+ if (!$promise) {
+ throw new \LogicException('FulfilledPromise must be resolved before obtaining the promise');
+ }
+
+ return $promise;
+ },
+ 'resolve' => function ($value = null) use (&$promise) {
+ if (!$promise) {
+ $promise = new FulfilledPromise($value);
+ }
+ },
+ 'reject' => function () {
+ throw new \LogicException('You cannot call reject() for React\Promise\FulfilledPromise');
+ },
+ 'notify' => function () {
+ // no-op
+ },
+ 'settle' => function ($value = null) use (&$promise) {
+ if (!$promise) {
+ $promise = new FulfilledPromise($value);
+ }
+ },
+ ]);
+ }
+
+ /** @test */
+ public function shouldThrowExceptionIfConstructedWithAPromise()
+ {
+ $this->setExpectedException('\InvalidArgumentException');
+
+ return new FulfilledPromise(new FulfilledPromise());
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionAllTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionAllTest.php
new file mode 100644
index 0000000..74c1d7c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionAllTest.php
@@ -0,0 +1,114 @@
+createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([]));
+
+ all([])
+ ->then($mock);
+ }
+
+ /** @test */
+ public function shouldResolveValuesArray()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([1, 2, 3]));
+
+ all([1, 2, 3])
+ ->then($mock);
+ }
+
+ /** @test */
+ public function shouldResolvePromisesArray()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([1, 2, 3]));
+
+ all([resolve(1), resolve(2), resolve(3)])
+ ->then($mock);
+ }
+
+ /** @test */
+ public function shouldResolveSparseArrayInput()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([null, 1, null, 1, 1]));
+
+ all([null, 1, null, 1, 1])
+ ->then($mock);
+ }
+
+ /** @test */
+ public function shouldRejectIfAnyInputPromiseRejects()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(2));
+
+ all([resolve(1), reject(2), resolve(3)])
+ ->then($this->expectCallableNever(), $mock);
+ }
+
+ /** @test */
+ public function shouldAcceptAPromiseForAnArray()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([1, 2, 3]));
+
+ all(resolve([1, 2, 3]))
+ ->then($mock);
+ }
+
+ /** @test */
+ public function shouldResolveToEmptyArrayWhenInputPromiseDoesNotResolveToArray()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([]));
+
+ all(resolve(1))
+ ->then($mock);
+ }
+
+ /** @test */
+ public function shouldPreserveTheOrderOfArrayWhenResolvingAsyncPromises()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([1, 2, 3]));
+
+ $deferred = new Deferred();
+
+ all([resolve(1), $deferred->promise(), resolve(3)])
+ ->then($mock);
+
+ $deferred->resolve(2);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionAnyTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionAnyTest.php
new file mode 100644
index 0000000..140b551
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionAnyTest.php
@@ -0,0 +1,204 @@
+createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with(
+ $this->callback(function($exception){
+ return $exception instanceof LengthException &&
+ 'Input array must contain at least 1 item but contains only 0 items.' === $exception->getMessage();
+ })
+ );
+
+ any([])
+ ->then($this->expectCallableNever(), $mock);
+ }
+
+ /** @test */
+ public function shouldResolveToNullWithNonArrayInput()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(null));
+
+ any(null)
+ ->then($mock);
+ }
+
+ /** @test */
+ public function shouldResolveWithAnInputValue()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ any([1, 2, 3])
+ ->then($mock);
+ }
+
+ /** @test */
+ public function shouldResolveWithAPromisedInputValue()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ any([resolve(1), resolve(2), resolve(3)])
+ ->then($mock);
+ }
+
+ /** @test */
+ public function shouldRejectWithAllRejectedInputValuesIfAllInputsAreRejected()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([0 => 1, 1 => 2, 2 => 3]));
+
+ any([reject(1), reject(2), reject(3)])
+ ->then($this->expectCallableNever(), $mock);
+ }
+
+ /** @test */
+ public function shouldResolveWhenFirstInputPromiseResolves()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ any([resolve(1), reject(2), reject(3)])
+ ->then($mock);
+ }
+
+ /** @test */
+ public function shouldAcceptAPromiseForAnArray()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ any(resolve([1, 2, 3]))
+ ->then($mock);
+ }
+
+ /** @test */
+ public function shouldResolveToNullArrayWhenInputPromiseDoesNotResolveToArray()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(null));
+
+ any(resolve(1))
+ ->then($mock);
+ }
+
+ /** @test */
+ public function shouldNotRelyOnArryIndexesWhenUnwrappingToASingleResolutionValue()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(2));
+
+ $d1 = new Deferred();
+ $d2 = new Deferred();
+
+ any(['abc' => $d1->promise(), 1 => $d2->promise()])
+ ->then($mock);
+
+ $d2->resolve(2);
+ $d1->resolve(1);
+ }
+
+ /** @test */
+ public function shouldRejectWhenInputPromiseRejects()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(null));
+
+ any(reject())
+ ->then($this->expectCallableNever(), $mock);
+ }
+
+ /** @test */
+ public function shouldCancelInputPromise()
+ {
+ $mock = $this
+ ->getMockBuilder('React\Promise\CancellablePromiseInterface')
+ ->getMock();
+ $mock
+ ->expects($this->once())
+ ->method('cancel');
+
+ any($mock)->cancel();
+ }
+
+ /** @test */
+ public function shouldCancelInputArrayPromises()
+ {
+ $mock1 = $this
+ ->getMockBuilder('React\Promise\CancellablePromiseInterface')
+ ->getMock();
+ $mock1
+ ->expects($this->once())
+ ->method('cancel');
+
+ $mock2 = $this
+ ->getMockBuilder('React\Promise\CancellablePromiseInterface')
+ ->getMock();
+ $mock2
+ ->expects($this->once())
+ ->method('cancel');
+
+ any([$mock1, $mock2])->cancel();
+ }
+
+ /** @test */
+ public function shouldNotCancelOtherPendingInputArrayPromisesIfOnePromiseFulfills()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->never())
+ ->method('__invoke');
+
+
+ $deferred = New Deferred($mock);
+ $deferred->resolve();
+
+ $mock2 = $this
+ ->getMockBuilder('React\Promise\CancellablePromiseInterface')
+ ->getMock();
+ $mock2
+ ->expects($this->never())
+ ->method('cancel');
+
+ some([$deferred->promise(), $mock2], 1)->cancel();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionCheckTypehintTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionCheckTypehintTest.php
new file mode 100644
index 0000000..8449bc1
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionCheckTypehintTest.php
@@ -0,0 +1,118 @@
+assertTrue(_checkTypehint(function (\InvalidArgumentException $e) {
+ }, new \InvalidArgumentException()));
+ $this->assertfalse(_checkTypehint(function (\InvalidArgumentException $e) {
+ }, new \Exception()));
+ }
+
+ /** @test */
+ public function shouldAcceptFunctionStringCallbackWithTypehint()
+ {
+ $this->assertTrue(_checkTypehint('React\Promise\testCallbackWithTypehint', new \InvalidArgumentException()));
+ $this->assertfalse(_checkTypehint('React\Promise\testCallbackWithTypehint', new \Exception()));
+ }
+
+ /** @test */
+ public function shouldAcceptInvokableObjectCallbackWithTypehint()
+ {
+ $this->assertTrue(_checkTypehint(new TestCallbackWithTypehintClass(), new \InvalidArgumentException()));
+ $this->assertfalse(_checkTypehint(new TestCallbackWithTypehintClass(), new \Exception()));
+ }
+
+ /** @test */
+ public function shouldAcceptObjectMethodCallbackWithTypehint()
+ {
+ $this->assertTrue(_checkTypehint([new TestCallbackWithTypehintClass(), 'testCallback'], new \InvalidArgumentException()));
+ $this->assertfalse(_checkTypehint([new TestCallbackWithTypehintClass(), 'testCallback'], new \Exception()));
+ }
+
+ /** @test */
+ public function shouldAcceptStaticClassCallbackWithTypehint()
+ {
+ $this->assertTrue(_checkTypehint(['React\Promise\TestCallbackWithTypehintClass', 'testCallbackStatic'], new \InvalidArgumentException()));
+ $this->assertfalse(_checkTypehint(['React\Promise\TestCallbackWithTypehintClass', 'testCallbackStatic'], new \Exception()));
+ }
+
+ /** @test */
+ public function shouldAcceptClosureCallbackWithoutTypehint()
+ {
+ $this->assertTrue(_checkTypehint(function (\InvalidArgumentException $e) {
+ }, new \InvalidArgumentException()));
+ }
+
+ /** @test */
+ public function shouldAcceptFunctionStringCallbackWithoutTypehint()
+ {
+ $this->assertTrue(_checkTypehint('React\Promise\testCallbackWithoutTypehint', new \InvalidArgumentException()));
+ }
+
+ /** @test */
+ public function shouldAcceptInvokableObjectCallbackWithoutTypehint()
+ {
+ $this->assertTrue(_checkTypehint(new TestCallbackWithoutTypehintClass(), new \InvalidArgumentException()));
+ }
+
+ /** @test */
+ public function shouldAcceptObjectMethodCallbackWithoutTypehint()
+ {
+ $this->assertTrue(_checkTypehint([new TestCallbackWithoutTypehintClass(), 'testCallback'], new \InvalidArgumentException()));
+ }
+
+ /** @test */
+ public function shouldAcceptStaticClassCallbackWithoutTypehint()
+ {
+ $this->assertTrue(_checkTypehint(['React\Promise\TestCallbackWithoutTypehintClass', 'testCallbackStatic'], new \InvalidArgumentException()));
+ }
+}
+
+function testCallbackWithTypehint(\InvalidArgumentException $e)
+{
+}
+
+function testCallbackWithoutTypehint()
+{
+}
+
+class TestCallbackWithTypehintClass
+{
+ public function __invoke(\InvalidArgumentException $e)
+ {
+
+ }
+
+ public function testCallback(\InvalidArgumentException $e)
+ {
+
+ }
+
+ public static function testCallbackStatic(\InvalidArgumentException $e)
+ {
+
+ }
+}
+
+class TestCallbackWithoutTypehintClass
+{
+ public function __invoke()
+ {
+
+ }
+
+ public function testCallback()
+ {
+
+ }
+
+ public static function testCallbackStatic()
+ {
+
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionMapTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionMapTest.php
new file mode 100644
index 0000000..1ea560a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionMapTest.php
@@ -0,0 +1,198 @@
+createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([2, 4, 6]));
+
+ map(
+ [1, 2, 3],
+ $this->mapper()
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldMapInputPromisesArray()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([2, 4, 6]));
+
+ map(
+ [resolve(1), resolve(2), resolve(3)],
+ $this->mapper()
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldMapMixedInputArray()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([2, 4, 6]));
+
+ map(
+ [1, resolve(2), 3],
+ $this->mapper()
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldMapInputWhenMapperReturnsAPromise()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([2, 4, 6]));
+
+ map(
+ [1, 2, 3],
+ $this->promiseMapper()
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldAcceptAPromiseForAnArray()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([2, 4, 6]));
+
+ map(
+ resolve([1, resolve(2), 3]),
+ $this->mapper()
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldResolveToEmptyArrayWhenInputPromiseDoesNotResolveToArray()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([]));
+
+ map(
+ resolve(1),
+ $this->mapper()
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldPreserveTheOrderOfArrayWhenResolvingAsyncPromises()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([2, 4, 6]));
+
+ $deferred = new Deferred();
+
+ map(
+ [resolve(1), $deferred->promise(), resolve(3)],
+ $this->mapper()
+ )->then($mock);
+
+ $deferred->resolve(2);
+ }
+
+ /** @test */
+ public function shouldRejectWhenInputContainsRejection()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(2));
+
+ map(
+ [resolve(1), reject(2), resolve(3)],
+ $this->mapper()
+ )->then($this->expectCallableNever(), $mock);
+ }
+
+ /** @test */
+ public function shouldRejectWhenInputPromiseRejects()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(null));
+
+ map(
+ reject(),
+ $this->mapper()
+ )->then($this->expectCallableNever(), $mock);
+ }
+
+ /** @test */
+ public function shouldCancelInputPromise()
+ {
+ $mock = $this
+ ->getMockBuilder('React\Promise\CancellablePromiseInterface')
+ ->getMock();
+ $mock
+ ->expects($this->once())
+ ->method('cancel');
+
+ map(
+ $mock,
+ $this->mapper()
+ )->cancel();
+ }
+
+ /** @test */
+ public function shouldCancelInputArrayPromises()
+ {
+ $mock1 = $this
+ ->getMockBuilder('React\Promise\CancellablePromiseInterface')
+ ->getMock();
+ $mock1
+ ->expects($this->once())
+ ->method('cancel');
+
+ $mock2 = $this
+ ->getMockBuilder('React\Promise\CancellablePromiseInterface')
+ ->getMock();
+ $mock2
+ ->expects($this->once())
+ ->method('cancel');
+
+ map(
+ [$mock1, $mock2],
+ $this->mapper()
+ )->cancel();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionRaceTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionRaceTest.php
new file mode 100644
index 0000000..83770ec
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionRaceTest.php
@@ -0,0 +1,211 @@
+createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(null));
+
+ race(
+ []
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldResolveValuesArray()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ race(
+ [1, 2, 3]
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldResolvePromisesArray()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(2));
+
+ $d1 = new Deferred();
+ $d2 = new Deferred();
+ $d3 = new Deferred();
+
+ race(
+ [$d1->promise(), $d2->promise(), $d3->promise()]
+ )->then($mock);
+
+ $d2->resolve(2);
+
+ $d1->resolve(1);
+ $d3->resolve(3);
+ }
+
+ /** @test */
+ public function shouldResolveSparseArrayInput()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(null));
+
+ race(
+ [null, 1, null, 2, 3]
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldRejectIfFirstSettledPromiseRejects()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(2));
+
+ $d1 = new Deferred();
+ $d2 = new Deferred();
+ $d3 = new Deferred();
+
+ race(
+ [$d1->promise(), $d2->promise(), $d3->promise()]
+ )->then($this->expectCallableNever(), $mock);
+
+ $d2->reject(2);
+
+ $d1->resolve(1);
+ $d3->resolve(3);
+ }
+
+ /** @test */
+ public function shouldAcceptAPromiseForAnArray()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ race(
+ resolve([1, 2, 3])
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldResolveToNullWhenInputPromiseDoesNotResolveToArray()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(null));
+
+ race(
+ resolve(1)
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldRejectWhenInputPromiseRejects()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(null));
+
+ race(
+ reject()
+ )->then($this->expectCallableNever(), $mock);
+ }
+
+ /** @test */
+ public function shouldCancelInputPromise()
+ {
+ $mock = $this
+ ->getMockBuilder('React\Promise\CancellablePromiseInterface')
+ ->getMock();
+ $mock
+ ->expects($this->once())
+ ->method('cancel');
+
+ race($mock)->cancel();
+ }
+
+ /** @test */
+ public function shouldCancelInputArrayPromises()
+ {
+ $mock1 = $this
+ ->getMockBuilder('React\Promise\CancellablePromiseInterface')
+ ->getMock();
+ $mock1
+ ->expects($this->once())
+ ->method('cancel');
+
+ $mock2 = $this
+ ->getMockBuilder('React\Promise\CancellablePromiseInterface')
+ ->getMock();
+ $mock2
+ ->expects($this->once())
+ ->method('cancel');
+
+ race([$mock1, $mock2])->cancel();
+ }
+
+ /** @test */
+ public function shouldNotCancelOtherPendingInputArrayPromisesIfOnePromiseFulfills()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->never())
+ ->method('__invoke');
+
+ $deferred = New Deferred($mock);
+ $deferred->resolve();
+
+ $mock2 = $this
+ ->getMockBuilder('React\Promise\CancellablePromiseInterface')
+ ->getMock();
+ $mock2
+ ->expects($this->never())
+ ->method('cancel');
+
+ race([$deferred->promise(), $mock2])->cancel();
+ }
+
+ /** @test */
+ public function shouldNotCancelOtherPendingInputArrayPromisesIfOnePromiseRejects()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->never())
+ ->method('__invoke');
+
+ $deferred = New Deferred($mock);
+ $deferred->reject();
+
+ $mock2 = $this
+ ->getMockBuilder('React\Promise\CancellablePromiseInterface')
+ ->getMock();
+ $mock2
+ ->expects($this->never())
+ ->method('cancel');
+
+ race([$deferred->promise(), $mock2])->cancel();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionReduceTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionReduceTest.php
new file mode 100644
index 0000000..8b43a87
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionReduceTest.php
@@ -0,0 +1,347 @@
+createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(6));
+
+ reduce(
+ [1, 2, 3],
+ $this->plus()
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldReduceValuesWithInitialValue()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(7));
+
+ reduce(
+ [1, 2, 3],
+ $this->plus(),
+ 1
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldReduceValuesWithInitialPromise()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(7));
+
+ reduce(
+ [1, 2, 3],
+ $this->plus(),
+ resolve(1)
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldReducePromisedValuesWithoutInitialValue()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(6));
+
+ reduce(
+ [resolve(1), resolve(2), resolve(3)],
+ $this->plus()
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldReducePromisedValuesWithInitialValue()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(7));
+
+ reduce(
+ [resolve(1), resolve(2), resolve(3)],
+ $this->plus(),
+ 1
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldReducePromisedValuesWithInitialPromise()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(7));
+
+ reduce(
+ [resolve(1), resolve(2), resolve(3)],
+ $this->plus(),
+ resolve(1)
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldReduceEmptyInputWithInitialValue()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ reduce(
+ [],
+ $this->plus(),
+ 1
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldReduceEmptyInputWithInitialPromise()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ reduce(
+ [],
+ $this->plus(),
+ resolve(1)
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldRejectWhenInputContainsRejection()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(2));
+
+ reduce(
+ [resolve(1), reject(2), resolve(3)],
+ $this->plus(),
+ resolve(1)
+ )->then($this->expectCallableNever(), $mock);
+ }
+
+ /** @test */
+ public function shouldResolveWithNullWhenInputIsEmptyAndNoInitialValueOrPromiseProvided()
+ {
+ // Note: this is different from when.js's behavior!
+ // In when.reduce(), this rejects with a TypeError exception (following
+ // JavaScript's [].reduce behavior.
+ // We're following PHP's array_reduce behavior and resolve with NULL.
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(null));
+
+ reduce(
+ [],
+ $this->plus()
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldAllowSparseArrayInputWithoutInitialValue()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(3));
+
+ reduce(
+ [null, null, 1, null, 1, 1],
+ $this->plus()
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldAllowSparseArrayInputWithInitialValue()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(4));
+
+ reduce(
+ [null, null, 1, null, 1, 1],
+ $this->plus(),
+ 1
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldReduceInInputOrder()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo('123'));
+
+ reduce(
+ [1, 2, 3],
+ $this->append(),
+ ''
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldAcceptAPromiseForAnArray()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo('123'));
+
+ reduce(
+ resolve([1, 2, 3]),
+ $this->append(),
+ ''
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldResolveToInitialValueWhenInputPromiseDoesNotResolveToAnArray()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ reduce(
+ resolve(1),
+ $this->plus(),
+ 1
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldProvideCorrectBasisValue()
+ {
+ $insertIntoArray = function ($arr, $val, $i) {
+ $arr[$i] = $val;
+
+ return $arr;
+ };
+
+ $d1 = new Deferred();
+ $d2 = new Deferred();
+ $d3 = new Deferred();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([1, 2, 3]));
+
+ reduce(
+ [$d1->promise(), $d2->promise(), $d3->promise()],
+ $insertIntoArray,
+ []
+ )->then($mock);
+
+ $d3->resolve(3);
+ $d1->resolve(1);
+ $d2->resolve(2);
+ }
+
+ /** @test */
+ public function shouldRejectWhenInputPromiseRejects()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(null));
+
+ reduce(
+ reject(),
+ $this->plus(),
+ 1
+ )->then($this->expectCallableNever(), $mock);
+ }
+
+ /** @test */
+ public function shouldCancelInputPromise()
+ {
+ $mock = $this
+ ->getMockBuilder('React\Promise\CancellablePromiseInterface')
+ ->getMock();
+ $mock
+ ->expects($this->once())
+ ->method('cancel');
+
+ reduce(
+ $mock,
+ $this->plus(),
+ 1
+ )->cancel();
+ }
+
+ /** @test */
+ public function shouldCancelInputArrayPromises()
+ {
+ $mock1 = $this
+ ->getMockBuilder('React\Promise\CancellablePromiseInterface')
+ ->getMock();
+ $mock1
+ ->expects($this->once())
+ ->method('cancel');
+
+ $mock2 = $this
+ ->getMockBuilder('React\Promise\CancellablePromiseInterface')
+ ->getMock();
+ $mock2
+ ->expects($this->once())
+ ->method('cancel');
+
+ reduce(
+ [$mock1, $mock2],
+ $this->plus(),
+ 1
+ )->cancel();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionRejectTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionRejectTest.php
new file mode 100644
index 0000000..84b8ec6
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionRejectTest.php
@@ -0,0 +1,64 @@
+createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($expected));
+
+ reject($expected)
+ ->then(
+ $this->expectCallableNever(),
+ $mock
+ );
+ }
+
+ /** @test */
+ public function shouldRejectAFulfilledPromise()
+ {
+ $expected = 123;
+
+ $resolved = new FulfilledPromise($expected);
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($expected));
+
+ reject($resolved)
+ ->then(
+ $this->expectCallableNever(),
+ $mock
+ );
+ }
+
+ /** @test */
+ public function shouldRejectARejectedPromise()
+ {
+ $expected = 123;
+
+ $resolved = new RejectedPromise($expected);
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($expected));
+
+ reject($resolved)
+ ->then(
+ $this->expectCallableNever(),
+ $mock
+ );
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionResolveTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionResolveTest.php
new file mode 100644
index 0000000..53126bc
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionResolveTest.php
@@ -0,0 +1,171 @@
+createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($expected));
+
+ resolve($expected)
+ ->then(
+ $mock,
+ $this->expectCallableNever()
+ );
+ }
+
+ /** @test */
+ public function shouldResolveAFulfilledPromise()
+ {
+ $expected = 123;
+
+ $resolved = new FulfilledPromise($expected);
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($expected));
+
+ resolve($resolved)
+ ->then(
+ $mock,
+ $this->expectCallableNever()
+ );
+ }
+
+ /** @test */
+ public function shouldResolveAThenable()
+ {
+ $thenable = new SimpleFulfilledTestThenable();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo('foo'));
+
+ resolve($thenable)
+ ->then(
+ $mock,
+ $this->expectCallableNever()
+ );
+ }
+
+ /** @test */
+ public function shouldResolveACancellableThenable()
+ {
+ $thenable = new SimpleTestCancellableThenable();
+
+ $promise = resolve($thenable);
+ $promise->cancel();
+
+ $this->assertTrue($thenable->cancelCalled);
+ }
+
+ /** @test */
+ public function shouldRejectARejectedPromise()
+ {
+ $expected = 123;
+
+ $resolved = new RejectedPromise($expected);
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($expected));
+
+ resolve($resolved)
+ ->then(
+ $this->expectCallableNever(),
+ $mock
+ );
+ }
+
+ /** @test */
+ public function shouldSupportDeepNestingInPromiseChains()
+ {
+ $d = new Deferred();
+ $d->resolve(false);
+
+ $result = resolve(resolve($d->promise()->then(function ($val) {
+ $d = new Deferred();
+ $d->resolve($val);
+
+ $identity = function ($val) {
+ return $val;
+ };
+
+ return resolve($d->promise()->then($identity))->then(
+ function ($val) {
+ return !$val;
+ }
+ );
+ })));
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(true));
+
+ $result->then($mock);
+ }
+
+ /** @test */
+ public function shouldSupportVeryDeepNestedPromises()
+ {
+ $deferreds = [];
+
+ // @TODO Increase count once global-queue is merged
+ for ($i = 0; $i < 10; $i++) {
+ $deferreds[] = $d = new Deferred();
+ $p = $d->promise();
+
+ $last = $p;
+ for ($j = 0; $j < 10; $j++) {
+ $last = $last->then(function($result) {
+ return $result;
+ });
+ }
+ }
+
+ $p = null;
+ foreach ($deferreds as $d) {
+ if ($p) {
+ $d->resolve($p);
+ }
+
+ $p = $d->promise();
+ }
+
+ $deferreds[0]->resolve(true);
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(true));
+
+ $deferreds[0]->promise()->then($mock);
+ }
+
+ /** @test */
+ public function returnsExtendePromiseForSimplePromise()
+ {
+ $promise = $this
+ ->getMockBuilder('React\Promise\PromiseInterface')
+ ->getMock();
+
+ $this->assertInstanceOf('React\Promise\ExtendedPromiseInterface', resolve($promise));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionSomeTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionSomeTest.php
new file mode 100644
index 0000000..276b54b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/FunctionSomeTest.php
@@ -0,0 +1,258 @@
+createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with(
+ $this->callback(function($exception){
+ return $exception instanceof LengthException &&
+ 'Input array must contain at least 1 item but contains only 0 items.' === $exception->getMessage();
+ })
+ );
+
+ some(
+ [],
+ 1
+ )->then($this->expectCallableNever(), $mock);
+ }
+
+ /** @test */
+ public function shouldRejectWithLengthExceptionWithInputArrayContainingNotEnoughItems()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with(
+ $this->callback(function($exception){
+ return $exception instanceof LengthException &&
+ 'Input array must contain at least 4 items but contains only 3 items.' === $exception->getMessage();
+ })
+ );
+
+ some(
+ [1, 2, 3],
+ 4
+ )->then($this->expectCallableNever(), $mock);
+ }
+
+ /** @test */
+ public function shouldResolveToEmptyArrayWithNonArrayInput()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([]));
+
+ some(
+ null,
+ 1
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldResolveValuesArray()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([1, 2]));
+
+ some(
+ [1, 2, 3],
+ 2
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldResolvePromisesArray()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([1, 2]));
+
+ some(
+ [resolve(1), resolve(2), resolve(3)],
+ 2
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldResolveSparseArrayInput()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([null, 1]));
+
+ some(
+ [null, 1, null, 2, 3],
+ 2
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldRejectIfAnyInputPromiseRejectsBeforeDesiredNumberOfInputsAreResolved()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([1 => 2, 2 => 3]));
+
+ some(
+ [resolve(1), reject(2), reject(3)],
+ 2
+ )->then($this->expectCallableNever(), $mock);
+ }
+
+ /** @test */
+ public function shouldAcceptAPromiseForAnArray()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([1, 2]));
+
+ some(
+ resolve([1, 2, 3]),
+ 2
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldResolveWithEmptyArrayIfHowManyIsLessThanOne()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([]));
+
+ some(
+ [1],
+ 0
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldResolveToEmptyArrayWhenInputPromiseDoesNotResolveToArray()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo([]));
+
+ some(
+ resolve(1),
+ 1
+ )->then($mock);
+ }
+
+ /** @test */
+ public function shouldRejectWhenInputPromiseRejects()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(null));
+
+ some(
+ reject(),
+ 1
+ )->then($this->expectCallableNever(), $mock);
+ }
+
+ /** @test */
+ public function shouldCancelInputPromise()
+ {
+ $mock = $this
+ ->getMockBuilder('React\Promise\CancellablePromiseInterface')
+ ->getMock();
+ $mock
+ ->expects($this->once())
+ ->method('cancel');
+
+ some($mock, 1)->cancel();
+ }
+
+ /** @test */
+ public function shouldCancelInputArrayPromises()
+ {
+ $mock1 = $this
+ ->getMockBuilder('React\Promise\CancellablePromiseInterface')
+ ->getMock();
+ $mock1
+ ->expects($this->once())
+ ->method('cancel');
+
+ $mock2 = $this
+ ->getMockBuilder('React\Promise\CancellablePromiseInterface')
+ ->getMock();
+ $mock2
+ ->expects($this->once())
+ ->method('cancel');
+
+ some([$mock1, $mock2], 1)->cancel();
+ }
+
+ /** @test */
+ public function shouldNotCancelOtherPendingInputArrayPromisesIfEnoughPromisesFulfill()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->never())
+ ->method('__invoke');
+
+ $deferred = New Deferred($mock);
+ $deferred->resolve();
+
+ $mock2 = $this
+ ->getMockBuilder('React\Promise\CancellablePromiseInterface')
+ ->getMock();
+ $mock2
+ ->expects($this->never())
+ ->method('cancel');
+
+ some([$deferred->promise(), $mock2], 1);
+ }
+
+ /** @test */
+ public function shouldNotCancelOtherPendingInputArrayPromisesIfEnoughPromisesReject()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->never())
+ ->method('__invoke');
+
+ $deferred = New Deferred($mock);
+ $deferred->reject();
+
+ $mock2 = $this
+ ->getMockBuilder('React\Promise\CancellablePromiseInterface')
+ ->getMock();
+ $mock2
+ ->expects($this->never())
+ ->method('cancel');
+
+ some([$deferred->promise(), $mock2], 2);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/LazyPromiseTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/LazyPromiseTest.php
new file mode 100644
index 0000000..b630881
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/LazyPromiseTest.php
@@ -0,0 +1,107 @@
+promise();
+ };
+
+ return new CallbackPromiseAdapter([
+ 'promise' => function () use ($factory) {
+ return new LazyPromise($factory);
+ },
+ 'resolve' => [$d, 'resolve'],
+ 'reject' => [$d, 'reject'],
+ 'notify' => [$d, 'progress'],
+ 'settle' => [$d, 'resolve'],
+ ]);
+ }
+
+ /** @test */
+ public function shouldNotCallFactoryIfThenIsNotInvoked()
+ {
+ $factory = $this->createCallableMock();
+ $factory
+ ->expects($this->never())
+ ->method('__invoke');
+
+ new LazyPromise($factory);
+ }
+
+ /** @test */
+ public function shouldCallFactoryIfThenIsInvoked()
+ {
+ $factory = $this->createCallableMock();
+ $factory
+ ->expects($this->once())
+ ->method('__invoke');
+
+ $p = new LazyPromise($factory);
+ $p->then();
+ }
+
+ /** @test */
+ public function shouldReturnPromiseFromFactory()
+ {
+ $factory = $this->createCallableMock();
+ $factory
+ ->expects($this->once())
+ ->method('__invoke')
+ ->will($this->returnValue(new FulfilledPromise(1)));
+
+ $onFulfilled = $this->createCallableMock();
+ $onFulfilled
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $p = new LazyPromise($factory);
+
+ $p->then($onFulfilled);
+ }
+
+ /** @test */
+ public function shouldReturnPromiseIfFactoryReturnsNull()
+ {
+ $factory = $this->createCallableMock();
+ $factory
+ ->expects($this->once())
+ ->method('__invoke')
+ ->will($this->returnValue(null));
+
+ $p = new LazyPromise($factory);
+ $this->assertInstanceOf('React\\Promise\\PromiseInterface', $p->then());
+ }
+
+ /** @test */
+ public function shouldReturnRejectedPromiseIfFactoryThrowsException()
+ {
+ $exception = new \Exception();
+
+ $factory = $this->createCallableMock();
+ $factory
+ ->expects($this->once())
+ ->method('__invoke')
+ ->will($this->throwException($exception));
+
+ $onRejected = $this->createCallableMock();
+ $onRejected
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($exception));
+
+ $p = new LazyPromise($factory);
+
+ $p->then($this->expectCallableNever(), $onRejected);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseAdapter/CallbackPromiseAdapter.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseAdapter/CallbackPromiseAdapter.php
new file mode 100644
index 0000000..bdedf46
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseAdapter/CallbackPromiseAdapter.php
@@ -0,0 +1,40 @@
+callbacks = $callbacks;
+ }
+
+ public function promise()
+ {
+ return call_user_func_array($this->callbacks['promise'], func_get_args());
+ }
+
+ public function resolve()
+ {
+ return call_user_func_array($this->callbacks['resolve'], func_get_args());
+ }
+
+ public function reject()
+ {
+ return call_user_func_array($this->callbacks['reject'], func_get_args());
+ }
+
+ public function notify()
+ {
+ return call_user_func_array($this->callbacks['notify'], func_get_args());
+ }
+
+ public function settle()
+ {
+ return call_user_func_array($this->callbacks['settle'], func_get_args());
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseAdapter/PromiseAdapterInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseAdapter/PromiseAdapterInterface.php
new file mode 100644
index 0000000..9157cd4
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseAdapter/PromiseAdapterInterface.php
@@ -0,0 +1,14 @@
+ function () use ($promise) {
+ return $promise;
+ },
+ 'resolve' => $resolveCallback,
+ 'reject' => $rejectCallback,
+ 'notify' => $progressCallback,
+ 'settle' => $resolveCallback,
+ ]);
+ }
+
+ /** @test */
+ public function shouldRejectIfResolverThrowsException()
+ {
+ $exception = new \Exception('foo');
+
+ $promise = new Promise(function () use ($exception) {
+ throw $exception;
+ });
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($exception));
+
+ $promise
+ ->then($this->expectCallableNever(), $mock);
+ }
+
+ /** @test */
+ public function shouldFulfillIfFullfilledWithSimplePromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo('foo'));
+
+ $adapter->promise()
+ ->then($mock);
+
+ $adapter->resolve(new SimpleFulfilledTestPromise());
+ }
+
+ /** @test */
+ public function shouldRejectIfRejectedWithSimplePromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo('foo'));
+
+ $adapter->promise()
+ ->then($this->expectCallableNever(), $mock);
+
+ $adapter->resolve(new SimpleRejectedTestPromise());
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/CancelTestTrait.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/CancelTestTrait.php
new file mode 100644
index 0000000..d722d75
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/CancelTestTrait.php
@@ -0,0 +1,231 @@
+createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->isType('callable'), $this->isType('callable'), $this->isType('callable'));
+
+ $adapter = $this->getPromiseTestAdapter($mock);
+
+ $adapter->promise()->cancel();
+ }
+
+ /** @test */
+ public function cancelShouldFulfillPromiseIfCancellerFulfills()
+ {
+ $adapter = $this->getPromiseTestAdapter(function ($resolve) {
+ $resolve(1);
+ });
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->promise()
+ ->then($mock, $this->expectCallableNever());
+
+ $adapter->promise()->cancel();
+ }
+
+ /** @test */
+ public function cancelShouldRejectPromiseIfCancellerRejects()
+ {
+ $adapter = $this->getPromiseTestAdapter(function ($resolve, $reject) {
+ $reject(1);
+ });
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->promise()
+ ->then($this->expectCallableNever(), $mock);
+
+ $adapter->promise()->cancel();
+ }
+
+ /** @test */
+ public function cancelShouldRejectPromiseWithExceptionIfCancellerThrows()
+ {
+ $e = new \Exception();
+
+ $adapter = $this->getPromiseTestAdapter(function () use ($e) {
+ throw $e;
+ });
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($e));
+
+ $adapter->promise()
+ ->then($this->expectCallableNever(), $mock);
+
+ $adapter->promise()->cancel();
+ }
+
+ /** @test */
+ public function cancelShouldProgressPromiseIfCancellerNotifies()
+ {
+ $adapter = $this->getPromiseTestAdapter(function ($resolve, $reject, $progress) {
+ $progress(1);
+ });
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->promise()
+ ->then($this->expectCallableNever(), $this->expectCallableNever(), $mock);
+
+ $adapter->promise()->cancel();
+ }
+
+ /** @test */
+ public function cancelShouldCallCancellerOnlyOnceIfCancellerResolves()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->will($this->returnCallback(function ($resolve) {
+ $resolve();
+ }));
+
+ $adapter = $this->getPromiseTestAdapter($mock);
+
+ $adapter->promise()->cancel();
+ $adapter->promise()->cancel();
+ }
+
+ /** @test */
+ public function cancelShouldHaveNoEffectIfCancellerDoesNothing()
+ {
+ $adapter = $this->getPromiseTestAdapter(function () {});
+
+ $adapter->promise()
+ ->then($this->expectCallableNever(), $this->expectCallableNever());
+
+ $adapter->promise()->cancel();
+ $adapter->promise()->cancel();
+ }
+
+ /** @test */
+ public function cancelShouldCallCancellerFromDeepNestedPromiseChain()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke');
+
+ $adapter = $this->getPromiseTestAdapter($mock);
+
+ $promise = $adapter->promise()
+ ->then(function () {
+ return new Promise\Promise(function () {});
+ })
+ ->then(function () {
+ $d = new Promise\Deferred();
+
+ return $d->promise();
+ })
+ ->then(function () {
+ return new Promise\Promise(function () {});
+ });
+
+ $promise->cancel();
+ }
+
+ /** @test */
+ public function cancelCalledOnChildrenSouldOnlyCancelWhenAllChildrenCancelled()
+ {
+ $adapter = $this->getPromiseTestAdapter($this->expectCallableNever());
+
+ $child1 = $adapter->promise()
+ ->then()
+ ->then();
+
+ $adapter->promise()
+ ->then();
+
+ $child1->cancel();
+ }
+
+ /** @test */
+ public function cancelShouldTriggerCancellerWhenAllChildrenCancel()
+ {
+ $adapter = $this->getPromiseTestAdapter($this->expectCallableOnce());
+
+ $child1 = $adapter->promise()
+ ->then()
+ ->then();
+
+ $child2 = $adapter->promise()
+ ->then();
+
+ $child1->cancel();
+ $child2->cancel();
+ }
+
+ /** @test */
+ public function cancelShouldNotTriggerCancellerWhenCancellingOneChildrenMultipleTimes()
+ {
+ $adapter = $this->getPromiseTestAdapter($this->expectCallableNever());
+
+ $child1 = $adapter->promise()
+ ->then()
+ ->then();
+
+ $child2 = $adapter->promise()
+ ->then();
+
+ $child1->cancel();
+ $child1->cancel();
+ }
+
+ /** @test */
+ public function cancelShouldTriggerCancellerOnlyOnceWhenCancellingMultipleTimes()
+ {
+ $adapter = $this->getPromiseTestAdapter($this->expectCallableOnce());
+
+ $adapter->promise()->cancel();
+ $adapter->promise()->cancel();
+ }
+
+ /** @test */
+ public function cancelShouldAlwaysTriggerCancellerWhenCalledOnRootPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter($this->expectCallableOnce());
+
+ $adapter->promise()
+ ->then()
+ ->then();
+
+ $adapter->promise()
+ ->then();
+
+ $adapter->promise()->cancel();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/FullTestTrait.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/FullTestTrait.php
new file mode 100644
index 0000000..3ce45d6
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/FullTestTrait.php
@@ -0,0 +1,15 @@
+getPromiseTestAdapter();
+
+ $sentinel = new \stdClass();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($sentinel);
+
+ $adapter->promise()
+ ->then($this->expectCallableNever(), $this->expectCallableNever(), $mock);
+
+ $adapter->notify($sentinel);
+ }
+
+ /** @test */
+ public function notifyShouldPropagateProgressToDownstreamPromises()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $sentinel = new \stdClass();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->will($this->returnArgument(0));
+
+ $mock2 = $this->createCallableMock();
+ $mock2
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($sentinel);
+
+ $adapter->promise()
+ ->then(
+ $this->expectCallableNever(),
+ $this->expectCallableNever(),
+ $mock
+ )
+ ->then(
+ $this->expectCallableNever(),
+ $this->expectCallableNever(),
+ $mock2
+ );
+
+ $adapter->notify($sentinel);
+ }
+
+ /** @test */
+ public function notifyShouldPropagateTransformedProgressToDownstreamPromises()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $sentinel = new \stdClass();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->will($this->returnValue($sentinel));
+
+ $mock2 = $this->createCallableMock();
+ $mock2
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($sentinel);
+
+ $adapter->promise()
+ ->then(
+ $this->expectCallableNever(),
+ $this->expectCallableNever(),
+ $mock
+ )
+ ->then(
+ $this->expectCallableNever(),
+ $this->expectCallableNever(),
+ $mock2
+ );
+
+ $adapter->notify(1);
+ }
+
+ /** @test */
+ public function notifyShouldPropagateCaughtExceptionValueAsProgress()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $exception = new \Exception();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->will($this->throwException($exception));
+
+ $mock2 = $this->createCallableMock();
+ $mock2
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($exception));
+
+ $adapter->promise()
+ ->then(
+ $this->expectCallableNever(),
+ $this->expectCallableNever(),
+ $mock
+ )
+ ->then(
+ $this->expectCallableNever(),
+ $this->expectCallableNever(),
+ $mock2
+ );
+
+ $adapter->notify(1);
+ }
+
+ /** @test */
+ public function notifyShouldForwardProgressEventsWhenIntermediaryCallbackTiedToAResolvedPromiseReturnsAPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+ $adapter2 = $this->getPromiseTestAdapter();
+
+ $promise2 = $adapter2->promise();
+
+ $sentinel = new \stdClass();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($sentinel);
+
+ // resolve BEFORE attaching progress handler
+ $adapter->resolve();
+
+ $adapter->promise()
+ ->then(function () use ($promise2) {
+ return $promise2;
+ })
+ ->then(
+ $this->expectCallableNever(),
+ $this->expectCallableNever(),
+ $mock
+ );
+
+ $adapter2->notify($sentinel);
+ }
+
+ /** @test */
+ public function notifyShouldForwardProgressEventsWhenIntermediaryCallbackTiedToAnUnresolvedPromiseReturnsAPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+ $adapter2 = $this->getPromiseTestAdapter();
+
+ $promise2 = $adapter2->promise();
+
+ $sentinel = new \stdClass();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($sentinel);
+
+ $adapter->promise()
+ ->then(function () use ($promise2) {
+ return $promise2;
+ })
+ ->then(
+ $this->expectCallableNever(),
+ $this->expectCallableNever(),
+ $mock
+ );
+
+ // resolve AFTER attaching progress handler
+ $adapter->resolve();
+ $adapter2->notify($sentinel);
+ }
+
+ /** @test */
+ public function notifyShouldForwardProgressWhenResolvedWithAnotherPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+ $adapter2 = $this->getPromiseTestAdapter();
+
+ $sentinel = new \stdClass();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->will($this->returnValue($sentinel));
+
+ $mock2 = $this->createCallableMock();
+ $mock2
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($sentinel);
+
+ $adapter->promise()
+ ->then(
+ $this->expectCallableNever(),
+ $this->expectCallableNever(),
+ $mock
+ )
+ ->then(
+ $this->expectCallableNever(),
+ $this->expectCallableNever(),
+ $mock2
+ );
+
+ $adapter->resolve($adapter2->promise());
+ $adapter2->notify($sentinel);
+ }
+
+ /** @test */
+ public function notifyShouldAllowResolveAfterProgress()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->at(0))
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+ $mock
+ ->expects($this->at(1))
+ ->method('__invoke')
+ ->with($this->identicalTo(2));
+
+ $adapter->promise()
+ ->then(
+ $mock,
+ $this->expectCallableNever(),
+ $mock
+ );
+
+ $adapter->notify(1);
+ $adapter->resolve(2);
+ }
+
+ /** @test */
+ public function notifyShouldAllowRejectAfterProgress()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->at(0))
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+ $mock
+ ->expects($this->at(1))
+ ->method('__invoke')
+ ->with($this->identicalTo(2));
+
+ $adapter->promise()
+ ->then(
+ $this->expectCallableNever(),
+ $mock,
+ $mock
+ );
+
+ $adapter->notify(1);
+ $adapter->reject(2);
+ }
+
+ /** @test */
+ public function notifyShouldReturnSilentlyOnProgressWhenAlreadyRejected()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $adapter->reject(1);
+
+ $this->assertNull($adapter->notify());
+ }
+
+ /** @test */
+ public function notifyShouldInvokeProgressHandler()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->promise()->progress($mock);
+ $adapter->notify(1);
+ }
+
+ /** @test */
+ public function notifyShouldInvokeProgressHandlerFromDone()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $this->assertNull($adapter->promise()->done(null, null, $mock));
+ $adapter->notify(1);
+ }
+
+ /** @test */
+ public function notifyShouldThrowExceptionThrownProgressHandlerFromDone()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->setExpectedException('\Exception', 'UnhandledRejectionException');
+
+ $this->assertNull($adapter->promise()->done(null, null, function () {
+ throw new \Exception('UnhandledRejectionException');
+ }));
+ $adapter->notify(1);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/PromiseFulfilledTestTrait.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/PromiseFulfilledTestTrait.php
new file mode 100644
index 0000000..428230b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/PromiseFulfilledTestTrait.php
@@ -0,0 +1,351 @@
+getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->resolve(1);
+ $adapter->resolve(2);
+
+ $adapter->promise()
+ ->then(
+ $mock,
+ $this->expectCallableNever()
+ );
+ }
+
+ /** @test */
+ public function fulfilledPromiseShouldInvokeNewlyAddedCallback()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $adapter->resolve(1);
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->promise()
+ ->then($mock, $this->expectCallableNever());
+ }
+
+ /** @test */
+ public function thenShouldForwardResultWhenCallbackIsNull()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->resolve(1);
+ $adapter->promise()
+ ->then(
+ null,
+ $this->expectCallableNever()
+ )
+ ->then(
+ $mock,
+ $this->expectCallableNever()
+ );
+ }
+
+ /** @test */
+ public function thenShouldForwardCallbackResultToNextCallback()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(2));
+
+ $adapter->resolve(1);
+ $adapter->promise()
+ ->then(
+ function ($val) {
+ return $val + 1;
+ },
+ $this->expectCallableNever()
+ )
+ ->then(
+ $mock,
+ $this->expectCallableNever()
+ );
+ }
+
+ /** @test */
+ public function thenShouldForwardPromisedCallbackResultValueToNextCallback()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(2));
+
+ $adapter->resolve(1);
+ $adapter->promise()
+ ->then(
+ function ($val) {
+ return \React\Promise\resolve($val + 1);
+ },
+ $this->expectCallableNever()
+ )
+ ->then(
+ $mock,
+ $this->expectCallableNever()
+ );
+ }
+
+ /** @test */
+ public function thenShouldSwitchFromCallbacksToErrbacksWhenCallbackReturnsARejection()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(2));
+
+ $adapter->resolve(1);
+ $adapter->promise()
+ ->then(
+ function ($val) {
+ return \React\Promise\reject($val + 1);
+ },
+ $this->expectCallableNever()
+ )
+ ->then(
+ $this->expectCallableNever(),
+ $mock
+ );
+ }
+
+ /** @test */
+ public function thenShouldSwitchFromCallbacksToErrbacksWhenCallbackThrows()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $exception = new \Exception();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->will($this->throwException($exception));
+
+ $mock2 = $this->createCallableMock();
+ $mock2
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($exception));
+
+ $adapter->resolve(1);
+ $adapter->promise()
+ ->then(
+ $mock,
+ $this->expectCallableNever()
+ )
+ ->then(
+ $this->expectCallableNever(),
+ $mock2
+ );
+ }
+
+ /** @test */
+ public function cancelShouldReturnNullForFulfilledPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $adapter->resolve();
+
+ $this->assertNull($adapter->promise()->cancel());
+ }
+
+ /** @test */
+ public function cancelShouldHaveNoEffectForFulfilledPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter($this->expectCallableNever());
+
+ $adapter->resolve();
+
+ $adapter->promise()->cancel();
+ }
+
+ /** @test */
+ public function doneShouldInvokeFulfillmentHandlerForFulfilledPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->resolve(1);
+ $this->assertNull($adapter->promise()->done($mock));
+ }
+
+ /** @test */
+ public function doneShouldThrowExceptionThrownFulfillmentHandlerForFulfilledPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->setExpectedException('\Exception', 'UnhandledRejectionException');
+
+ $adapter->resolve(1);
+ $this->assertNull($adapter->promise()->done(function () {
+ throw new \Exception('UnhandledRejectionException');
+ }));
+ }
+
+ /** @test */
+ public function doneShouldThrowUnhandledRejectionExceptionWhenFulfillmentHandlerRejectsForFulfilledPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->setExpectedException('React\\Promise\\UnhandledRejectionException');
+
+ $adapter->resolve(1);
+ $this->assertNull($adapter->promise()->done(function () {
+ return \React\Promise\reject();
+ }));
+ }
+
+ /** @test */
+ public function otherwiseShouldNotInvokeRejectionHandlerForFulfilledPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $adapter->resolve(1);
+ $adapter->promise()->otherwise($this->expectCallableNever());
+ }
+
+ /** @test */
+ public function alwaysShouldNotSuppressValueForFulfilledPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $value = new \stdClass();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($value));
+
+ $adapter->resolve($value);
+ $adapter->promise()
+ ->always(function () {})
+ ->then($mock);
+ }
+
+ /** @test */
+ public function alwaysShouldNotSuppressValueWhenHandlerReturnsANonPromiseForFulfilledPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $value = new \stdClass();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($value));
+
+ $adapter->resolve($value);
+ $adapter->promise()
+ ->always(function () {
+ return 1;
+ })
+ ->then($mock);
+ }
+
+ /** @test */
+ public function alwaysShouldNotSuppressValueWhenHandlerReturnsAPromiseForFulfilledPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $value = new \stdClass();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($value));
+
+ $adapter->resolve($value);
+ $adapter->promise()
+ ->always(function () {
+ return \React\Promise\resolve(1);
+ })
+ ->then($mock);
+ }
+
+ /** @test */
+ public function alwaysShouldRejectWhenHandlerThrowsForFulfilledPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $exception = new \Exception();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($exception));
+
+ $adapter->resolve(1);
+ $adapter->promise()
+ ->always(function () use ($exception) {
+ throw $exception;
+ })
+ ->then(null, $mock);
+ }
+
+ /** @test */
+ public function alwaysShouldRejectWhenHandlerRejectsForFulfilledPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $exception = new \Exception();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($exception));
+
+ $adapter->resolve(1);
+ $adapter->promise()
+ ->always(function () use ($exception) {
+ return \React\Promise\reject($exception);
+ })
+ ->then(null, $mock);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/PromisePendingTestTrait.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/PromisePendingTestTrait.php
new file mode 100644
index 0000000..a4f48ee
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/PromisePendingTestTrait.php
@@ -0,0 +1,68 @@
+getPromiseTestAdapter();
+
+ $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->then());
+ }
+
+ /** @test */
+ public function thenShouldReturnAllowNullForPendingPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->then(null, null, null));
+ }
+
+ /** @test */
+ public function cancelShouldReturnNullForPendingPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->assertNull($adapter->promise()->cancel());
+ }
+
+ /** @test */
+ public function doneShouldReturnNullForPendingPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->assertNull($adapter->promise()->done());
+ }
+
+ /** @test */
+ public function doneShouldReturnAllowNullForPendingPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->assertNull($adapter->promise()->done(null, null, null));
+ }
+
+ /** @test */
+ public function otherwiseShouldNotInvokeRejectionHandlerForPendingPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $adapter->settle();
+ $adapter->promise()->otherwise($this->expectCallableNever());
+ }
+
+ /** @test */
+ public function alwaysShouldReturnAPromiseForPendingPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->always(function () {}));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/PromiseRejectedTestTrait.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/PromiseRejectedTestTrait.php
new file mode 100644
index 0000000..98d1dcf
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/PromiseRejectedTestTrait.php
@@ -0,0 +1,512 @@
+getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->reject(1);
+ $adapter->reject(2);
+
+ $adapter->promise()
+ ->then(
+ $this->expectCallableNever(),
+ $mock
+ );
+ }
+
+ /** @test */
+ public function rejectedPromiseShouldInvokeNewlyAddedCallback()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $adapter->reject(1);
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->promise()
+ ->then($this->expectCallableNever(), $mock);
+ }
+
+ /** @test */
+ public function shouldForwardUndefinedRejectionValue()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with(null);
+
+ $adapter->reject(1);
+ $adapter->promise()
+ ->then(
+ $this->expectCallableNever(),
+ function () {
+ // Presence of rejection handler is enough to switch back
+ // to resolve mode, even though it returns undefined.
+ // The ONLY way to propagate a rejection is to re-throw or
+ // return a rejected promise;
+ }
+ )
+ ->then(
+ $mock,
+ $this->expectCallableNever()
+ );
+ }
+
+ /** @test */
+ public function shouldSwitchFromErrbacksToCallbacksWhenErrbackDoesNotExplicitlyPropagate()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(2));
+
+ $adapter->reject(1);
+ $adapter->promise()
+ ->then(
+ $this->expectCallableNever(),
+ function ($val) {
+ return $val + 1;
+ }
+ )
+ ->then(
+ $mock,
+ $this->expectCallableNever()
+ );
+ }
+
+ /** @test */
+ public function shouldSwitchFromErrbacksToCallbacksWhenErrbackReturnsAResolution()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(2));
+
+ $adapter->reject(1);
+ $adapter->promise()
+ ->then(
+ $this->expectCallableNever(),
+ function ($val) {
+ return \React\Promise\resolve($val + 1);
+ }
+ )
+ ->then(
+ $mock,
+ $this->expectCallableNever()
+ );
+ }
+
+ /** @test */
+ public function shouldPropagateRejectionsWhenErrbackThrows()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $exception = new \Exception();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->will($this->throwException($exception));
+
+ $mock2 = $this->createCallableMock();
+ $mock2
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($exception));
+
+ $adapter->reject(1);
+ $adapter->promise()
+ ->then(
+ $this->expectCallableNever(),
+ $mock
+ )
+ ->then(
+ $this->expectCallableNever(),
+ $mock2
+ );
+ }
+
+ /** @test */
+ public function shouldPropagateRejectionsWhenErrbackReturnsARejection()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(2));
+
+ $adapter->reject(1);
+ $adapter->promise()
+ ->then(
+ $this->expectCallableNever(),
+ function ($val) {
+ return \React\Promise\reject($val + 1);
+ }
+ )
+ ->then(
+ $this->expectCallableNever(),
+ $mock
+ );
+ }
+
+ /** @test */
+ public function doneShouldInvokeRejectionHandlerForRejectedPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->reject(1);
+ $this->assertNull($adapter->promise()->done(null, $mock));
+ }
+
+ /** @test */
+ public function doneShouldThrowExceptionThrownByRejectionHandlerForRejectedPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->setExpectedException('\Exception', 'UnhandledRejectionException');
+
+ $adapter->reject(1);
+ $this->assertNull($adapter->promise()->done(null, function () {
+ throw new \Exception('UnhandledRejectionException');
+ }));
+ }
+
+ /** @test */
+ public function doneShouldThrowUnhandledRejectionExceptionWhenRejectedWithNonExceptionForRejectedPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->setExpectedException('React\\Promise\\UnhandledRejectionException');
+
+ $adapter->reject(1);
+ $this->assertNull($adapter->promise()->done());
+ }
+
+ /** @test */
+ public function unhandledRejectionExceptionThrownByDoneHoldsRejectionValue()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $expected = new \stdClass();
+
+ $adapter->reject($expected);
+
+ try {
+ $adapter->promise()->done();
+ } catch (UnhandledRejectionException $e) {
+ $this->assertSame($expected, $e->getReason());
+ return;
+ }
+
+ $this->fail();
+ }
+
+ /** @test */
+ public function doneShouldThrowUnhandledRejectionExceptionWhenRejectionHandlerRejectsForRejectedPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->setExpectedException('React\\Promise\\UnhandledRejectionException');
+
+ $adapter->reject(1);
+ $this->assertNull($adapter->promise()->done(null, function () {
+ return \React\Promise\reject();
+ }));
+ }
+
+ /** @test */
+ public function doneShouldThrowRejectionExceptionWhenRejectionHandlerRejectsWithExceptionForRejectedPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->setExpectedException('\Exception', 'UnhandledRejectionException');
+
+ $adapter->reject(1);
+ $this->assertNull($adapter->promise()->done(null, function () {
+ return \React\Promise\reject(new \Exception('UnhandledRejectionException'));
+ }));
+ }
+
+ /** @test */
+ public function doneShouldThrowExceptionProvidedAsRejectionValueForRejectedPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->setExpectedException('\Exception', 'UnhandledRejectionException');
+
+ $adapter->reject(new \Exception('UnhandledRejectionException'));
+ $this->assertNull($adapter->promise()->done());
+ }
+
+ /** @test */
+ public function doneShouldThrowWithDeepNestingPromiseChainsForRejectedPromise()
+ {
+ $this->setExpectedException('\Exception', 'UnhandledRejectionException');
+
+ $exception = new \Exception('UnhandledRejectionException');
+
+ $d = new Deferred();
+ $d->resolve();
+
+ $result = \React\Promise\resolve(\React\Promise\resolve($d->promise()->then(function () use ($exception) {
+ $d = new Deferred();
+ $d->resolve();
+
+ return \React\Promise\resolve($d->promise()->then(function () {}))->then(
+ function () use ($exception) {
+ throw $exception;
+ }
+ );
+ })));
+
+ $result->done();
+ }
+
+ /** @test */
+ public function doneShouldRecoverWhenRejectionHandlerCatchesExceptionForRejectedPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $adapter->reject(new \Exception('UnhandledRejectionException'));
+ $this->assertNull($adapter->promise()->done(null, function (\Exception $e) {
+
+ }));
+ }
+
+ /** @test */
+ public function otherwiseShouldInvokeRejectionHandlerForRejectedPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->reject(1);
+ $adapter->promise()->otherwise($mock);
+ }
+
+ /** @test */
+ public function otherwiseShouldInvokeNonTypeHintedRejectionHandlerIfReasonIsAnExceptionForRejectedPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $exception = new \Exception();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($exception));
+
+ $adapter->reject($exception);
+ $adapter->promise()
+ ->otherwise(function ($reason) use ($mock) {
+ $mock($reason);
+ });
+ }
+
+ /** @test */
+ public function otherwiseShouldInvokeRejectionHandlerIfReasonMatchesTypehintForRejectedPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $exception = new \InvalidArgumentException();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($exception));
+
+ $adapter->reject($exception);
+ $adapter->promise()
+ ->otherwise(function (\InvalidArgumentException $reason) use ($mock) {
+ $mock($reason);
+ });
+ }
+
+ /** @test */
+ public function otherwiseShouldNotInvokeRejectionHandlerIfReaonsDoesNotMatchTypehintForRejectedPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $exception = new \Exception();
+
+ $mock = $this->expectCallableNever();
+
+ $adapter->reject($exception);
+ $adapter->promise()
+ ->otherwise(function (\InvalidArgumentException $reason) use ($mock) {
+ $mock($reason);
+ });
+ }
+
+ /** @test */
+ public function alwaysShouldNotSuppressRejectionForRejectedPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $exception = new \Exception();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($exception));
+
+ $adapter->reject($exception);
+ $adapter->promise()
+ ->always(function () {})
+ ->then(null, $mock);
+ }
+
+ /** @test */
+ public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsANonPromiseForRejectedPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $exception = new \Exception();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($exception));
+
+ $adapter->reject($exception);
+ $adapter->promise()
+ ->always(function () {
+ return 1;
+ })
+ ->then(null, $mock);
+ }
+
+ /** @test */
+ public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsAPromiseForRejectedPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $exception = new \Exception();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($exception));
+
+ $adapter->reject($exception);
+ $adapter->promise()
+ ->always(function () {
+ return \React\Promise\resolve(1);
+ })
+ ->then(null, $mock);
+ }
+
+ /** @test */
+ public function alwaysShouldRejectWhenHandlerThrowsForRejectedPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $exception1 = new \Exception();
+ $exception2 = new \Exception();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($exception2));
+
+ $adapter->reject($exception1);
+ $adapter->promise()
+ ->always(function () use ($exception2) {
+ throw $exception2;
+ })
+ ->then(null, $mock);
+ }
+
+ /** @test */
+ public function alwaysShouldRejectWhenHandlerRejectsForRejectedPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $exception1 = new \Exception();
+ $exception2 = new \Exception();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($exception2));
+
+ $adapter->reject($exception1);
+ $adapter->promise()
+ ->always(function () use ($exception2) {
+ return \React\Promise\reject($exception2);
+ })
+ ->then(null, $mock);
+ }
+
+ /** @test */
+ public function cancelShouldReturnNullForRejectedPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $adapter->reject();
+
+ $this->assertNull($adapter->promise()->cancel());
+ }
+
+ /** @test */
+ public function cancelShouldHaveNoEffectForRejectedPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter($this->expectCallableNever());
+
+ $adapter->reject();
+
+ $adapter->promise()->cancel();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/PromiseSettledTestTrait.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/PromiseSettledTestTrait.php
new file mode 100644
index 0000000..e363b6d
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/PromiseSettledTestTrait.php
@@ -0,0 +1,86 @@
+getPromiseTestAdapter();
+
+ $adapter->settle();
+ $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->then());
+ }
+
+ /** @test */
+ public function thenShouldReturnAllowNullForSettledPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $adapter->settle();
+ $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->then(null, null, null));
+ }
+
+ /** @test */
+ public function cancelShouldReturnNullForSettledPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $adapter->settle();
+
+ $this->assertNull($adapter->promise()->cancel());
+ }
+
+ /** @test */
+ public function cancelShouldHaveNoEffectForSettledPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter($this->expectCallableNever());
+
+ $adapter->settle();
+
+ $adapter->promise()->cancel();
+ }
+
+ /** @test */
+ public function doneShouldReturnNullForSettledPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $adapter->settle();
+ $this->assertNull($adapter->promise()->done(null, function () {}));
+ }
+
+ /** @test */
+ public function doneShouldReturnAllowNullForSettledPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $adapter->settle();
+ $this->assertNull($adapter->promise()->done(null, function () {}, null));
+ }
+
+ /** @test */
+ public function progressShouldNotInvokeProgressHandlerForSettledPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $adapter->settle();
+ $adapter->promise()->progress($this->expectCallableNever());
+ $adapter->notify();
+ }
+
+ /** @test */
+ public function alwaysShouldReturnAPromiseForSettledPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $adapter->settle();
+ $this->assertInstanceOf('React\\Promise\\PromiseInterface', $adapter->promise()->always(function () {}));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/RejectTestTrait.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/RejectTestTrait.php
new file mode 100644
index 0000000..063f178
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/RejectTestTrait.php
@@ -0,0 +1,368 @@
+getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->promise()
+ ->then($this->expectCallableNever(), $mock);
+
+ $adapter->reject(1);
+ }
+
+ /** @test */
+ public function rejectShouldRejectWithFulfilledPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->promise()
+ ->then($this->expectCallableNever(), $mock);
+
+ $adapter->reject(Promise\resolve(1));
+ }
+
+ /** @test */
+ public function rejectShouldRejectWithRejectedPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->promise()
+ ->then($this->expectCallableNever(), $mock);
+
+ $adapter->reject(Promise\reject(1));
+ }
+
+ /** @test */
+ public function rejectShouldForwardReasonWhenCallbackIsNull()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->promise()
+ ->then(
+ $this->expectCallableNever()
+ )
+ ->then(
+ $this->expectCallableNever(),
+ $mock
+ );
+
+ $adapter->reject(1);
+ }
+
+ /** @test */
+ public function rejectShouldMakePromiseImmutable()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->promise()
+ ->then(null, function ($value) use ($adapter) {
+ $adapter->reject(3);
+
+ return Promise\reject($value);
+ })
+ ->then(
+ $this->expectCallableNever(),
+ $mock
+ );
+
+ $adapter->reject(1);
+ $adapter->reject(2);
+ }
+
+ /** @test */
+ public function notifyShouldInvokeOtherwiseHandler()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->promise()
+ ->otherwise($mock);
+
+ $adapter->reject(1);
+ }
+
+ /** @test */
+ public function doneShouldInvokeRejectionHandler()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $this->assertNull($adapter->promise()->done(null, $mock));
+ $adapter->reject(1);
+ }
+
+ /** @test */
+ public function doneShouldThrowExceptionThrownByRejectionHandler()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->setExpectedException('\Exception', 'UnhandledRejectionException');
+
+ $this->assertNull($adapter->promise()->done(null, function () {
+ throw new \Exception('UnhandledRejectionException');
+ }));
+ $adapter->reject(1);
+ }
+
+ /** @test */
+ public function doneShouldThrowUnhandledRejectionExceptionWhenRejectedWithNonException()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->setExpectedException('React\\Promise\\UnhandledRejectionException');
+
+ $this->assertNull($adapter->promise()->done());
+ $adapter->reject(1);
+ }
+
+ /** @test */
+ public function doneShouldThrowUnhandledRejectionExceptionWhenRejectionHandlerRejects()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->setExpectedException('React\\Promise\\UnhandledRejectionException');
+
+ $this->assertNull($adapter->promise()->done(null, function () {
+ return \React\Promise\reject();
+ }));
+ $adapter->reject(1);
+ }
+
+ /** @test */
+ public function doneShouldThrowRejectionExceptionWhenRejectionHandlerRejectsWithException()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->setExpectedException('\Exception', 'UnhandledRejectionException');
+
+ $this->assertNull($adapter->promise()->done(null, function () {
+ return \React\Promise\reject(new \Exception('UnhandledRejectionException'));
+ }));
+ $adapter->reject(1);
+ }
+
+ /** @test */
+ public function doneShouldThrowUnhandledRejectionExceptionWhenRejectionHandlerRetunsPendingPromiseWhichRejectsLater()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->setExpectedException('React\\Promise\\UnhandledRejectionException');
+
+ $d = new Deferred();
+ $promise = $d->promise();
+
+ $this->assertNull($adapter->promise()->done(null, function () use ($promise) {
+ return $promise;
+ }));
+ $adapter->reject(1);
+ $d->reject(1);
+ }
+
+ /** @test */
+ public function doneShouldThrowExceptionProvidedAsRejectionValue()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->setExpectedException('\Exception', 'UnhandledRejectionException');
+
+ $this->assertNull($adapter->promise()->done());
+ $adapter->reject(new \Exception('UnhandledRejectionException'));
+ }
+
+ /** @test */
+ public function doneShouldThrowWithDeepNestingPromiseChains()
+ {
+ $this->setExpectedException('\Exception', 'UnhandledRejectionException');
+
+ $exception = new \Exception('UnhandledRejectionException');
+
+ $d = new Deferred();
+
+ $result = \React\Promise\resolve(\React\Promise\resolve($d->promise()->then(function () use ($exception) {
+ $d = new Deferred();
+ $d->resolve();
+
+ return \React\Promise\resolve($d->promise()->then(function () {}))->then(
+ function () use ($exception) {
+ throw $exception;
+ }
+ );
+ })));
+
+ $result->done();
+
+ $d->resolve();
+ }
+
+ /** @test */
+ public function doneShouldRecoverWhenRejectionHandlerCatchesException()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->assertNull($adapter->promise()->done(null, function (\Exception $e) {
+
+ }));
+ $adapter->reject(new \Exception('UnhandledRejectionException'));
+ }
+
+ /** @test */
+ public function alwaysShouldNotSuppressRejection()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $exception = new \Exception();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($exception));
+
+ $adapter->promise()
+ ->always(function () {})
+ ->then(null, $mock);
+
+ $adapter->reject($exception);
+ }
+
+ /** @test */
+ public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsANonPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $exception = new \Exception();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($exception));
+
+ $adapter->promise()
+ ->always(function () {
+ return 1;
+ })
+ ->then(null, $mock);
+
+ $adapter->reject($exception);
+ }
+
+ /** @test */
+ public function alwaysShouldNotSuppressRejectionWhenHandlerReturnsAPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $exception = new \Exception();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($exception));
+
+ $adapter->promise()
+ ->always(function () {
+ return \React\Promise\resolve(1);
+ })
+ ->then(null, $mock);
+
+ $adapter->reject($exception);
+ }
+
+ /** @test */
+ public function alwaysShouldRejectWhenHandlerThrowsForRejection()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $exception = new \Exception();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($exception));
+
+ $adapter->promise()
+ ->always(function () use ($exception) {
+ throw $exception;
+ })
+ ->then(null, $mock);
+
+ $adapter->reject($exception);
+ }
+
+ /** @test */
+ public function alwaysShouldRejectWhenHandlerRejectsForRejection()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $exception = new \Exception();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($exception));
+
+ $adapter->promise()
+ ->always(function () use ($exception) {
+ return \React\Promise\reject($exception);
+ })
+ ->then(null, $mock);
+
+ $adapter->reject($exception);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/ResolveTestTrait.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/ResolveTestTrait.php
new file mode 100644
index 0000000..0736d35
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/PromiseTest/ResolveTestTrait.php
@@ -0,0 +1,312 @@
+getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->promise()
+ ->then($mock);
+
+ $adapter->resolve(1);
+ }
+
+ /** @test */
+ public function resolveShouldResolveWithPromisedValue()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->promise()
+ ->then($mock);
+
+ $adapter->resolve(Promise\resolve(1));
+ }
+
+ /** @test */
+ public function resolveShouldRejectWhenResolvedWithRejectedPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->promise()
+ ->then($this->expectCallableNever(), $mock);
+
+ $adapter->resolve(Promise\reject(1));
+ }
+
+ /** @test */
+ public function resolveShouldForwardValueWhenCallbackIsNull()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->promise()
+ ->then(
+ null,
+ $this->expectCallableNever()
+ )
+ ->then(
+ $mock,
+ $this->expectCallableNever()
+ );
+
+ $adapter->resolve(1);
+ }
+
+ /** @test */
+ public function resolveShouldMakePromiseImmutable()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $adapter->promise()
+ ->then(function ($value) use ($adapter) {
+ $adapter->resolve(3);
+
+ return $value;
+ })
+ ->then(
+ $mock,
+ $this->expectCallableNever()
+ );
+
+ $adapter->resolve(1);
+ $adapter->resolve(2);
+ }
+
+ /**
+ * @test
+ */
+ public function resolveShouldRejectWhenResolvedWithItself()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with(new \LogicException('Cannot resolve a promise with itself.'));
+
+ $adapter->promise()
+ ->then(
+ $this->expectCallableNever(),
+ $mock
+ );
+
+ $adapter->resolve($adapter->promise());
+ }
+
+ /**
+ * @test
+ */
+ public function resolveShouldRejectWhenResolvedWithAPromiseWhichFollowsItself()
+ {
+ $adapter1 = $this->getPromiseTestAdapter();
+ $adapter2 = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with(new \LogicException('Cannot resolve a promise with itself.'));
+
+ $promise1 = $adapter1->promise();
+
+ $promise2 = $adapter2->promise();
+
+ $promise2->then(
+ $this->expectCallableNever(),
+ $mock
+ );
+
+ $adapter1->resolve($promise2);
+ $adapter2->resolve($promise1);
+ }
+
+ /** @test */
+ public function doneShouldInvokeFulfillmentHandler()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo(1));
+
+ $this->assertNull($adapter->promise()->done($mock));
+ $adapter->resolve(1);
+ }
+
+ /** @test */
+ public function doneShouldThrowExceptionThrownFulfillmentHandler()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->setExpectedException('\Exception', 'UnhandledRejectionException');
+
+ $this->assertNull($adapter->promise()->done(function () {
+ throw new \Exception('UnhandledRejectionException');
+ }));
+ $adapter->resolve(1);
+ }
+
+ /** @test */
+ public function doneShouldThrowUnhandledRejectionExceptionWhenFulfillmentHandlerRejects()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $this->setExpectedException('React\\Promise\\UnhandledRejectionException');
+
+ $this->assertNull($adapter->promise()->done(function () {
+ return \React\Promise\reject();
+ }));
+ $adapter->resolve(1);
+ }
+
+ /** @test */
+ public function alwaysShouldNotSuppressValue()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $value = new \stdClass();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($value));
+
+ $adapter->promise()
+ ->always(function () {})
+ ->then($mock);
+
+ $adapter->resolve($value);
+ }
+
+ /** @test */
+ public function alwaysShouldNotSuppressValueWhenHandlerReturnsANonPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $value = new \stdClass();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($value));
+
+ $adapter->promise()
+ ->always(function () {
+ return 1;
+ })
+ ->then($mock);
+
+ $adapter->resolve($value);
+ }
+
+ /** @test */
+ public function alwaysShouldNotSuppressValueWhenHandlerReturnsAPromise()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $value = new \stdClass();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($value));
+
+ $adapter->promise()
+ ->always(function () {
+ return \React\Promise\resolve(1);
+ })
+ ->then($mock);
+
+ $adapter->resolve($value);
+ }
+
+ /** @test */
+ public function alwaysShouldRejectWhenHandlerThrowsForFulfillment()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $exception = new \Exception();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($exception));
+
+ $adapter->promise()
+ ->always(function () use ($exception) {
+ throw $exception;
+ })
+ ->then(null, $mock);
+
+ $adapter->resolve(1);
+ }
+
+ /** @test */
+ public function alwaysShouldRejectWhenHandlerRejectsForFulfillment()
+ {
+ $adapter = $this->getPromiseTestAdapter();
+
+ $exception = new \Exception();
+
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($this->identicalTo($exception));
+
+ $adapter->promise()
+ ->always(function () use ($exception) {
+ return \React\Promise\reject($exception);
+ })
+ ->then(null, $mock);
+
+ $adapter->resolve(1);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/RejectedPromiseTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/RejectedPromiseTest.php
new file mode 100644
index 0000000..c886b00
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/RejectedPromiseTest.php
@@ -0,0 +1,50 @@
+ function () use (&$promise) {
+ if (!$promise) {
+ throw new \LogicException('RejectedPromise must be rejected before obtaining the promise');
+ }
+
+ return $promise;
+ },
+ 'resolve' => function () {
+ throw new \LogicException('You cannot call resolve() for React\Promise\RejectedPromise');
+ },
+ 'reject' => function ($reason = null) use (&$promise) {
+ if (!$promise) {
+ $promise = new RejectedPromise($reason);
+ }
+ },
+ 'notify' => function () {
+ // no-op
+ },
+ 'settle' => function ($reason = null) use (&$promise) {
+ if (!$promise) {
+ $promise = new RejectedPromise($reason);
+ }
+ },
+ ]);
+ }
+
+ /** @test */
+ public function shouldThrowExceptionIfConstructedWithAPromise()
+ {
+ $this->setExpectedException('\InvalidArgumentException');
+
+ return new RejectedPromise(new RejectedPromise());
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/Stub/CallableStub.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/Stub/CallableStub.php
new file mode 100644
index 0000000..0120893
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/Stub/CallableStub.php
@@ -0,0 +1,10 @@
+createCallableMock();
+ $mock
+ ->expects($this->exactly($amount))
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ public function expectCallableOnce()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ public function expectCallableNever()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->never())
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ public function createCallableMock()
+ {
+ return $this
+ ->getMockBuilder('React\\Promise\Stub\CallableStub')
+ ->getMock();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/bootstrap.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/bootstrap.php
new file mode 100644
index 0000000..9b7f872
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/bootstrap.php
@@ -0,0 +1,7 @@
+addPsr4('React\\Promise\\', __DIR__);
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/fixtures/SimpleFulfilledTestPromise.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/fixtures/SimpleFulfilledTestPromise.php
new file mode 100644
index 0000000..ef4d530
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/fixtures/SimpleFulfilledTestPromise.php
@@ -0,0 +1,21 @@
+cancelCalled = true;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/fixtures/SimpleTestCancellableThenable.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/fixtures/SimpleTestCancellableThenable.php
new file mode 100644
index 0000000..c0f1593
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/promise/tests/fixtures/SimpleTestCancellableThenable.php
@@ -0,0 +1,18 @@
+cancelCalled = true;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/.gitignore b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/.gitignore
new file mode 100644
index 0000000..987e2a2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/.gitignore
@@ -0,0 +1,2 @@
+composer.lock
+vendor
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/.travis.yml b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/.travis.yml
new file mode 100644
index 0000000..917dc0c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/.travis.yml
@@ -0,0 +1,49 @@
+language: php
+
+php:
+# - 5.3 # requires old distro, see below
+ - 5.4
+ - 5.5
+ - 5.6
+ - 7.0
+ - 7.1
+ - 7.2
+# - 7.0 # Mac OS X, ignore errors, see below
+ - hhvm # ignore errors, see below
+
+# lock distro so new future defaults will not break the build
+dist: trusty
+
+matrix:
+ include:
+ - php: 5.3
+ dist: precise
+ include:
+ - os: osx
+ language: generic
+ php: 7.0 # just to look right on travis
+ env:
+ - PACKAGE: php70
+ allow_failures:
+ - php: hhvm
+ - os: osx
+
+sudo: false
+
+install:
+ # OSX install inspired by https://github.com/kiler129/TravisCI-OSX-PHP
+ - |
+ if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
+ brew tap homebrew/homebrew-php
+ echo "Installing PHP ..."
+ brew install "${PACKAGE}"
+ brew install "${PACKAGE}"-xdebug
+ brew link "${PACKAGE}"
+ echo "Installing composer ..."
+ curl -s http://getcomposer.org/installer | php
+ mv composer.phar /usr/local/bin/composer
+ fi
+ - composer install --no-interaction
+
+script:
+ - ./vendor/bin/phpunit --coverage-text
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/CHANGELOG.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/CHANGELOG.md
new file mode 100644
index 0000000..03c2eec
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/CHANGELOG.md
@@ -0,0 +1,451 @@
+# Changelog
+
+## 0.8.10 (2018-02-28)
+
+* Feature: Update DNS dependency to support loading system default DNS
+ nameserver config on all supported platforms
+ (`/etc/resolv.conf` on Unix/Linux/Mac/Docker/WSL and WMIC on Windows)
+ (#152 by @clue)
+
+ This means that connecting to hosts that are managed by a local DNS server,
+ such as a corporate DNS server or when using Docker containers, will now
+ work as expected across all platforms with no changes required:
+
+ ```php
+ $connector = new Connector($loop);
+ $connector->connect('intranet.example:80')->then(function ($connection) {
+ // …
+ });
+ ```
+
+## 0.8.9 (2018-01-18)
+
+* Feature: Support explicitly choosing TLS version to negotiate with remote side
+ by respecting `crypto_method` context parameter for all classes.
+ (#149 by @clue)
+
+ By default, all connector and server classes support TLSv1.0+ and exclude
+ support for legacy SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly
+ choose the TLS version you want to negotiate with the remote side:
+
+ ```php
+ // new: now supports 'crypto_method` context parameter for all classes
+ $connector = new Connector($loop, array(
+ 'tls' => array(
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
+ )
+ ));
+ ```
+
+* Minor internal clean up to unify class imports
+ (#148 by @clue)
+
+## 0.8.8 (2018-01-06)
+
+* Improve test suite by adding test group to skip integration tests relying on
+ internet connection and fix minor documentation typo.
+ (#146 by @clue and #145 by @cn007b)
+
+## 0.8.7 (2017-12-24)
+
+* Fix: Fix closing socket resource before removing from loop
+ (#141 by @clue)
+
+ This fixes the root cause of an uncaught `Exception` that only manifested
+ itself after the recent Stream v0.7.4 component update and only if you're
+ using `ext-event` (`ExtEventLoop`).
+
+* Improve test suite by testing against PHP 7.2
+ (#140 by @carusogabriel)
+
+## 0.8.6 (2017-11-18)
+
+* Feature: Add Unix domain socket (UDS) support to `Server` with `unix://` URI scheme
+ and add advanced `UnixServer` class.
+ (#120 by @andig)
+
+ ```php
+ // new: Server now supports "unix://" scheme
+ $server = new Server('unix:///tmp/server.sock', $loop);
+
+ // new: advanced usage
+ $server = new UnixServer('/tmp/server.sock', $loop);
+ ```
+
+* Restructure examples to ease getting started
+ (#136 by @clue)
+
+* Improve test suite by adding forward compatibility with PHPUnit 6 and
+ ignore Mac OS X test failures for now until Travis tests work again
+ (#133 by @gabriel-caruso and #134 by @clue)
+
+## 0.8.5 (2017-10-23)
+
+* Fix: Work around PHP bug with Unix domain socket (UDS) paths for Mac OS X
+ (#123 by @andig)
+
+* Fix: Fix `SecureServer` to return `null` URI if server socket is already closed
+ (#129 by @clue)
+
+* Improve test suite by adding forward compatibility with PHPUnit v5 and
+ forward compatibility with upcoming EventLoop releases in tests and
+ test Mac OS X on Travis
+ (#122 by @andig and #125, #127 and #130 by @clue)
+
+* Readme improvements
+ (#118 by @jsor)
+
+## 0.8.4 (2017-09-16)
+
+* Feature: Add `FixedUriConnector` decorator to use fixed, preconfigured URI instead
+ (#117 by @clue)
+
+ This can be useful for consumers that do not support certain URIs, such as
+ when you want to explicitly connect to a Unix domain socket (UDS) path
+ instead of connecting to a default address assumed by an higher-level API:
+
+ ```php
+ $connector = new FixedUriConnector(
+ 'unix:///var/run/docker.sock',
+ new UnixConnector($loop)
+ );
+
+ // destination will be ignored, actually connects to Unix domain socket
+ $promise = $connector->connect('localhost:80');
+ ```
+
+## 0.8.3 (2017-09-08)
+
+* Feature: Reduce memory consumption for failed connections
+ (#113 by @valga)
+
+* Fix: Work around write chunk size for TLS streams for PHP < 7.1.14
+ (#114 by @clue)
+
+## 0.8.2 (2017-08-25)
+
+* Feature: Update DNS dependency to support hosts file on all platforms
+ (#112 by @clue)
+
+ This means that connecting to hosts such as `localhost` will now work as
+ expected across all platforms with no changes required:
+
+ ```php
+ $connector = new Connector($loop);
+ $connector->connect('localhost:8080')->then(function ($connection) {
+ // …
+ });
+ ```
+
+## 0.8.1 (2017-08-15)
+
+* Feature: Forward compatibility with upcoming EventLoop v1.0 and v0.5 and
+ target evenement 3.0 a long side 2.0 and 1.0
+ (#104 by @clue and #111 by @WyriHaximus)
+
+* Improve test suite by locking Travis distro so new defaults will not break the build and
+ fix HHVM build for now again and ignore future HHVM build errors
+ (#109 and #110 by @clue)
+
+* Minor documentation fixes
+ (#103 by @christiaan and #108 by @hansott)
+
+## 0.8.0 (2017-05-09)
+
+* Feature: New `Server` class now acts as a facade for existing server classes
+ and renamed old `Server` to `TcpServer` for advanced usage.
+ (#96 and #97 by @clue)
+
+ The `Server` class is now the main class in this package that implements the
+ `ServerInterface` and allows you to accept incoming streaming connections,
+ such as plaintext TCP/IP or secure TLS connection streams.
+
+ > This is not a BC break and consumer code does not have to be updated.
+
+* Feature / BC break: All addresses are now URIs that include the URI scheme
+ (#98 by @clue)
+
+ ```diff
+ - $parts = parse_url('tcp://' . $conn->getRemoteAddress());
+ + $parts = parse_url($conn->getRemoteAddress());
+ ```
+
+* Fix: Fix `unix://` addresses for Unix domain socket (UDS) paths
+ (#100 by @clue)
+
+* Feature: Forward compatibility with Stream v1.0 and v0.7
+ (#99 by @clue)
+
+## 0.7.2 (2017-04-24)
+
+* Fix: Work around latest PHP 7.0.18 and 7.1.4 no longer accepting full URIs
+ (#94 by @clue)
+
+## 0.7.1 (2017-04-10)
+
+* Fix: Ignore HHVM errors when closing connection that is already closing
+ (#91 by @clue)
+
+## 0.7.0 (2017-04-10)
+
+* Feature: Merge SocketClient component into this component
+ (#87 by @clue)
+
+ This means that this package now provides async, streaming plaintext TCP/IP
+ and secure TLS socket server and client connections for ReactPHP.
+
+ ```
+ $connector = new React\Socket\Connector($loop);
+ $connector->connect('google.com:80')->then(function (ConnectionInterface $conn) {
+ $connection->write('…');
+ });
+ ```
+
+ Accordingly, the `ConnectionInterface` is now used to represent both incoming
+ server side connections as well as outgoing client side connections.
+
+ If you've previously used the SocketClient component to establish outgoing
+ client connections, upgrading should take no longer than a few minutes.
+ All classes have been merged as-is from the latest `v0.7.0` release with no
+ other changes, so you can simply update your code to use the updated namespace
+ like this:
+
+ ```php
+ // old from SocketClient component and namespace
+ $connector = new React\SocketClient\Connector($loop);
+ $connector->connect('google.com:80')->then(function (ConnectionInterface $conn) {
+ $connection->write('…');
+ });
+
+ // new
+ $connector = new React\Socket\Connector($loop);
+ $connector->connect('google.com:80')->then(function (ConnectionInterface $conn) {
+ $connection->write('…');
+ });
+ ```
+
+## 0.6.0 (2017-04-04)
+
+* Feature: Add `LimitingServer` to limit and keep track of open connections
+ (#86 by @clue)
+
+ ```php
+ $server = new Server(0, $loop);
+ $server = new LimitingServer($server, 100);
+
+ $server->on('connection', function (ConnectionInterface $connection) {
+ $connection->write('hello there!' . PHP_EOL);
+ …
+ });
+ ```
+
+* Feature / BC break: Add `pause()` and `resume()` methods to limit active
+ connections
+ (#84 by @clue)
+
+ ```php
+ $server = new Server(0, $loop);
+ $server->pause();
+
+ $loop->addTimer(1.0, function() use ($server) {
+ $server->resume();
+ });
+ ```
+
+## 0.5.1 (2017-03-09)
+
+* Feature: Forward compatibility with Stream v0.5 and upcoming v0.6
+ (#79 by @clue)
+
+## 0.5.0 (2017-02-14)
+
+* Feature / BC break: Replace `listen()` call with URIs passed to constructor
+ and reject listening on hostnames with `InvalidArgumentException`
+ and replace `ConnectionException` with `RuntimeException` for consistency
+ (#61, #66 and #72 by @clue)
+
+ ```php
+ // old
+ $server = new Server($loop);
+ $server->listen(8080);
+
+ // new
+ $server = new Server(8080, $loop);
+ ```
+
+ Similarly, you can now pass a full listening URI to the constructor to change
+ the listening host:
+
+ ```php
+ // old
+ $server = new Server($loop);
+ $server->listen(8080, '127.0.0.1');
+
+ // new
+ $server = new Server('127.0.0.1:8080', $loop);
+ ```
+
+ Trying to start listening on (DNS) host names will now throw an
+ `InvalidArgumentException`, use IP addresses instead:
+
+ ```php
+ // old
+ $server = new Server($loop);
+ $server->listen(8080, 'localhost');
+
+ // new
+ $server = new Server('127.0.0.1:8080', $loop);
+ ```
+
+ If trying to listen fails (such as if port is already in use or port below
+ 1024 may require root access etc.), it will now throw a `RuntimeException`,
+ the `ConnectionException` class has been removed:
+
+ ```php
+ // old: throws React\Socket\ConnectionException
+ $server = new Server($loop);
+ $server->listen(80);
+
+ // new: throws RuntimeException
+ $server = new Server(80, $loop);
+ ```
+
+* Feature / BC break: Rename `shutdown()` to `close()` for consistency throughout React
+ (#62 by @clue)
+
+ ```php
+ // old
+ $server->shutdown();
+
+ // new
+ $server->close();
+ ```
+
+* Feature / BC break: Replace `getPort()` with `getAddress()`
+ (#67 by @clue)
+
+ ```php
+ // old
+ echo $server->getPort(); // 8080
+
+ // new
+ echo $server->getAddress(); // 127.0.0.1:8080
+ ```
+
+* Feature / BC break: `getRemoteAddress()` returns full address instead of only IP
+ (#65 by @clue)
+
+ ```php
+ // old
+ echo $connection->getRemoteAddress(); // 192.168.0.1
+
+ // new
+ echo $connection->getRemoteAddress(); // 192.168.0.1:51743
+ ```
+
+* Feature / BC break: Add `getLocalAddress()` method
+ (#68 by @clue)
+
+ ```php
+ echo $connection->getLocalAddress(); // 127.0.0.1:8080
+ ```
+
+* BC break: The `Server` and `SecureServer` class are now marked `final`
+ and you can no longer `extend` them
+ (which was never documented or recommended anyway).
+ Public properties and event handlers are now internal only.
+ Please use composition instead of extension.
+ (#71, #70 and #69 by @clue)
+
+## 0.4.6 (2017-01-26)
+
+* Feature: Support socket context options passed to `Server`
+ (#64 by @clue)
+
+* Fix: Properly return `null` for unknown addresses
+ (#63 by @clue)
+
+* Improve documentation for `ServerInterface` and lock test suite requirements
+ (#60 by @clue, #57 by @shaunbramley)
+
+## 0.4.5 (2017-01-08)
+
+* Feature: Add `SecureServer` for secure TLS connections
+ (#55 by @clue)
+
+* Add functional integration tests
+ (#54 by @clue)
+
+## 0.4.4 (2016-12-19)
+
+* Feature / Fix: `ConnectionInterface` should extend `DuplexStreamInterface` + documentation
+ (#50 by @clue)
+
+* Feature / Fix: Improve test suite and switch to normal stream handler
+ (#51 by @clue)
+
+* Feature: Add examples
+ (#49 by @clue)
+
+## 0.4.3 (2016-03-01)
+
+* Bug fix: Suppress errors on stream_socket_accept to prevent PHP from crashing
+* Support for PHP7 and HHVM
+* Support PHP 5.3 again
+
+## 0.4.2 (2014-05-25)
+
+* Verify stream is a valid resource in Connection
+
+## 0.4.1 (2014-04-13)
+
+* Bug fix: Check read buffer for data before shutdown signal and end emit (@ArtyDev)
+* Bug fix: v0.3.4 changes merged for v0.4.1
+
+## 0.3.4 (2014-03-30)
+
+* Bug fix: Reset socket to non-blocking after shutting down (PHP bug)
+
+## 0.4.0 (2014-02-02)
+
+* BC break: Bump minimum PHP version to PHP 5.4, remove 5.3 specific hacks
+* BC break: Update to React/Promise 2.0
+* BC break: Update to Evenement 2.0
+* Dependency: Autoloading and filesystem structure now PSR-4 instead of PSR-0
+* Bump React dependencies to v0.4
+
+## 0.3.3 (2013-07-08)
+
+* Version bump
+
+## 0.3.2 (2013-05-10)
+
+* Version bump
+
+## 0.3.1 (2013-04-21)
+
+* Feature: Support binding to IPv6 addresses (@clue)
+
+## 0.3.0 (2013-04-14)
+
+* Bump React dependencies to v0.3
+
+## 0.2.6 (2012-12-26)
+
+* Version bump
+
+## 0.2.3 (2012-11-14)
+
+* Version bump
+
+## 0.2.0 (2012-09-10)
+
+* Bump React dependencies to v0.2
+
+## 0.1.1 (2012-07-12)
+
+* Version bump
+
+## 0.1.0 (2012-07-11)
+
+* First tagged release
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/LICENSE b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/LICENSE
new file mode 100644
index 0000000..a808108
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2012 Igor Wiedler, Chris Boden
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/README.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/README.md
new file mode 100644
index 0000000..e8b53a0
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/README.md
@@ -0,0 +1,1419 @@
+# Socket
+
+[](https://travis-ci.org/reactphp/socket)
+
+Async, streaming plaintext TCP/IP and secure TLS socket server and client
+connections for [ReactPHP](https://reactphp.org/).
+
+The socket library provides re-usable interfaces for a socket-layer
+server and client based on the [`EventLoop`](https://github.com/reactphp/event-loop)
+and [`Stream`](https://github.com/reactphp/stream) components.
+Its server component allows you to build networking servers that accept incoming
+connections from networking clients (such as an HTTP server).
+Its client component allows you to build networking clients that establish
+outgoing connections to networking servers (such as an HTTP or database client).
+This library provides async, streaming means for all of this, so you can
+handle multiple concurrent connections without blocking.
+
+**Table of Contents**
+
+* [Quickstart example](#quickstart-example)
+* [Connection usage](#connection-usage)
+ * [ConnectionInterface](#connectioninterface)
+ * [getRemoteAddress()](#getremoteaddress)
+ * [getLocalAddress()](#getlocaladdress)
+* [Server usage](#server-usage)
+ * [ServerInterface](#serverinterface)
+ * [connection event](#connection-event)
+ * [error event](#error-event)
+ * [getAddress()](#getaddress)
+ * [pause()](#pause)
+ * [resume()](#resume)
+ * [close()](#close)
+ * [Server](#server)
+ * [Advanced server usage](#advanced-server-usage)
+ * [TcpServer](#tcpserver)
+ * [SecureServer](#secureserver)
+ * [UnixServer](#unixserver)
+ * [LimitingServer](#limitingserver)
+ * [getConnections()](#getconnections)
+* [Client usage](#client-usage)
+ * [ConnectorInterface](#connectorinterface)
+ * [connect()](#connect)
+ * [Connector](#connector)
+ * [Advanced client usage](#advanced-client-usage)
+ * [TcpConnector](#tcpconnector)
+ * [DnsConnector](#dnsconnector)
+ * [SecureConnector](#secureconnector)
+ * [TimeoutConnector](#timeoutconnector)
+ * [UnixConnector](#unixconnector)
+ * [FixUriConnector](#fixeduriconnector)
+* [Install](#install)
+* [Tests](#tests)
+* [License](#license)
+
+## Quickstart example
+
+Here is a server that closes the connection if you send it anything:
+
+```php
+$loop = React\EventLoop\Factory::create();
+$socket = new React\Socket\Server('127.0.0.1:8080', $loop);
+
+$socket->on('connection', function (ConnectionInterface $conn) {
+ $conn->write("Hello " . $conn->getRemoteAddress() . "!\n");
+ $conn->write("Welcome to this amazing server!\n");
+ $conn->write("Here's a tip: don't say anything.\n");
+
+ $conn->on('data', function ($data) use ($conn) {
+ $conn->close();
+ });
+});
+
+$loop->run();
+```
+
+See also the [examples](examples).
+
+Here's a client that outputs the output of said server and then attempts to
+send it a string:
+
+```php
+$loop = React\EventLoop\Factory::create();
+$connector = new React\Socket\Connector($loop);
+
+$connector->connect('127.0.0.1:8080')->then(function (ConnectionInterface $conn) use ($loop) {
+ $conn->pipe(new React\Stream\WritableResourceStream(STDOUT, $loop));
+ $conn->write("Hello World!\n");
+});
+
+$loop->run();
+```
+
+## Connection usage
+
+### ConnectionInterface
+
+The `ConnectionInterface` is used to represent any incoming and outgoing
+connection, such as a normal TCP/IP connection.
+
+An incoming or outgoing connection is a duplex stream (both readable and
+writable) that implements React's
+[`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface).
+It contains additional properties for the local and remote address (client IP)
+where this connection has been established to/from.
+
+Most commonly, instances implementing this `ConnectionInterface` are emitted
+by all classes implementing the [`ServerInterface`](#serverinterface) and
+used by all classes implementing the [`ConnectorInterface`](#connectorinterface).
+
+Because the `ConnectionInterface` implements the underlying
+[`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface)
+you can use any of its events and methods as usual:
+
+```php
+$connection->on('data', function ($chunk) {
+ echo $chunk;
+});
+
+$connection->on('end', function () {
+ echo 'ended';
+});
+
+$connection->on('error', function (Exception $e) {
+ echo 'error: ' . $e->getMessage();
+});
+
+$connection->on('close', function () {
+ echo 'closed';
+});
+
+$connection->write($data);
+$connection->end($data = null);
+$connection->close();
+// …
+```
+
+For more details, see the
+[`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface).
+
+#### getRemoteAddress()
+
+The `getRemoteAddress(): ?string` method returns the full remote address
+(URI) where this connection has been established with.
+
+```php
+$address = $connection->getRemoteAddress();
+echo 'Connection with ' . $address . PHP_EOL;
+```
+
+If the remote address can not be determined or is unknown at this time (such as
+after the connection has been closed), it MAY return a `NULL` value instead.
+
+Otherwise, it will return the full address (URI) as a string value, such
+as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`,
+`unix://example.sock` or `unix:///path/to/example.sock`.
+Note that individual URI components are application specific and depend
+on the underlying transport protocol.
+
+If this is a TCP/IP based connection and you only want the remote IP, you may
+use something like this:
+
+```php
+$address = $connection->getRemoteAddress();
+$ip = trim(parse_url($address, PHP_URL_HOST), '[]');
+echo 'Connection with ' . $ip . PHP_EOL;
+```
+
+#### getLocalAddress()
+
+The `getLocalAddress(): ?string` method returns the full local address
+(URI) where this connection has been established with.
+
+```php
+$address = $connection->getLocalAddress();
+echo 'Connection with ' . $address . PHP_EOL;
+```
+
+If the local address can not be determined or is unknown at this time (such as
+after the connection has been closed), it MAY return a `NULL` value instead.
+
+Otherwise, it will return the full address (URI) as a string value, such
+as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`,
+`unix://example.sock` or `unix:///path/to/example.sock`.
+Note that individual URI components are application specific and depend
+on the underlying transport protocol.
+
+This method complements the [`getRemoteAddress()`](#getremoteaddress) method,
+so they should not be confused.
+
+If your `TcpServer` instance is listening on multiple interfaces (e.g. using
+the address `0.0.0.0`), you can use this method to find out which interface
+actually accepted this connection (such as a public or local interface).
+
+If your system has multiple interfaces (e.g. a WAN and a LAN interface),
+you can use this method to find out which interface was actually
+used for this connection.
+
+## Server usage
+
+### ServerInterface
+
+The `ServerInterface` is responsible for providing an interface for accepting
+incoming streaming connections, such as a normal TCP/IP connection.
+
+Most higher-level components (such as a HTTP server) accept an instance
+implementing this interface to accept incoming streaming connections.
+This is usually done via dependency injection, so it's fairly simple to actually
+swap this implementation against any other implementation of this interface.
+This means that you SHOULD typehint against this interface instead of a concrete
+implementation of this interface.
+
+Besides defining a few methods, this interface also implements the
+[`EventEmitterInterface`](https://github.com/igorw/evenement)
+which allows you to react to certain events.
+
+#### connection event
+
+The `connection` event will be emitted whenever a new connection has been
+established, i.e. a new client connects to this server socket:
+
+```php
+$server->on('connection', function (ConnectionInterface $connection) {
+ echo 'new connection' . PHP_EOL;
+});
+```
+
+See also the [`ConnectionInterface`](#connectioninterface) for more details
+about handling the incoming connection.
+
+#### error event
+
+The `error` event will be emitted whenever there's an error accepting a new
+connection from a client.
+
+```php
+$server->on('error', function (Exception $e) {
+ echo 'error: ' . $e->getMessage() . PHP_EOL;
+});
+```
+
+Note that this is not a fatal error event, i.e. the server keeps listening for
+new connections even after this event.
+
+
+#### getAddress()
+
+The `getAddress(): ?string` method can be used to
+return the full address (URI) this server is currently listening on.
+
+```php
+$address = $server->getAddress();
+echo 'Server listening on ' . $address . PHP_EOL;
+```
+
+If the address can not be determined or is unknown at this time (such as
+after the socket has been closed), it MAY return a `NULL` value instead.
+
+Otherwise, it will return the full address (URI) as a string value, such
+as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`
+`unix://example.sock` or `unix:///path/to/example.sock`.
+Note that individual URI components are application specific and depend
+on the underlying transport protocol.
+
+If this is a TCP/IP based server and you only want the local port, you may
+use something like this:
+
+```php
+$address = $server->getAddress();
+$port = parse_url($address, PHP_URL_PORT);
+echo 'Server listening on port ' . $port . PHP_EOL;
+```
+
+#### pause()
+
+The `pause(): void` method can be used to
+pause accepting new incoming connections.
+
+Removes the socket resource from the EventLoop and thus stop accepting
+new connections. Note that the listening socket stays active and is not
+closed.
+
+This means that new incoming connections will stay pending in the
+operating system backlog until its configurable backlog is filled.
+Once the backlog is filled, the operating system may reject further
+incoming connections until the backlog is drained again by resuming
+to accept new connections.
+
+Once the server is paused, no futher `connection` events SHOULD
+be emitted.
+
+```php
+$server->pause();
+
+$server->on('connection', assertShouldNeverCalled());
+```
+
+This method is advisory-only, though generally not recommended, the
+server MAY continue emitting `connection` events.
+
+Unless otherwise noted, a successfully opened server SHOULD NOT start
+in paused state.
+
+You can continue processing events by calling `resume()` again.
+
+Note that both methods can be called any number of times, in particular
+calling `pause()` more than once SHOULD NOT have any effect.
+Similarly, calling this after `close()` is a NO-OP.
+
+#### resume()
+
+The `resume(): void` method can be used to
+resume accepting new incoming connections.
+
+Re-attach the socket resource to the EventLoop after a previous `pause()`.
+
+```php
+$server->pause();
+
+$loop->addTimer(1.0, function () use ($server) {
+ $server->resume();
+});
+```
+
+Note that both methods can be called any number of times, in particular
+calling `resume()` without a prior `pause()` SHOULD NOT have any effect.
+Similarly, calling this after `close()` is a NO-OP.
+
+#### close()
+
+The `close(): void` method can be used to
+shut down this listening socket.
+
+This will stop listening for new incoming connections on this socket.
+
+```php
+echo 'Shutting down server socket' . PHP_EOL;
+$server->close();
+```
+
+Calling this method more than once on the same instance is a NO-OP.
+
+### Server
+
+The `Server` class is the main class in this package that implements the
+[`ServerInterface`](#serverinterface) and allows you to accept incoming
+streaming connections, such as plaintext TCP/IP or secure TLS connection streams.
+Connections can also be accepted on Unix domain sockets.
+
+```php
+$server = new Server(8080, $loop);
+```
+
+As above, the `$uri` parameter can consist of only a port, in which case the
+server will default to listening on the localhost address `127.0.0.1`,
+which means it will not be reachable from outside of this system.
+
+In order to use a random port assignment, you can use the port `0`:
+
+```php
+$server = new Server(0, $loop);
+$address = $server->getAddress();
+```
+
+In order to change the host the socket is listening on, you can provide an IP
+address through the first parameter provided to the constructor, optionally
+preceded by the `tcp://` scheme:
+
+```php
+$server = new Server('192.168.0.1:8080', $loop);
+```
+
+If you want to listen on an IPv6 address, you MUST enclose the host in square
+brackets:
+
+```php
+$server = new Server('[::1]:8080', $loop);
+```
+
+To listen on a Unix domain socket (UDS) path, you MUST prefix the URI with the
+`unix://` scheme:
+
+```php
+$server = new Server('unix:///tmp/server.sock', $loop);
+```
+
+If the given URI is invalid, does not contain a port, any other scheme or if it
+contains a hostname, it will throw an `InvalidArgumentException`:
+
+```php
+// throws InvalidArgumentException due to missing port
+$server = new Server('127.0.0.1', $loop);
+```
+
+If the given URI appears to be valid, but listening on it fails (such as if port
+is already in use or port below 1024 may require root access etc.), it will
+throw a `RuntimeException`:
+
+```php
+$first = new Server(8080, $loop);
+
+// throws RuntimeException because port is already in use
+$second = new Server(8080, $loop);
+```
+
+> Note that these error conditions may vary depending on your system and/or
+ configuration.
+ See the exception message and code for more details about the actual error
+ condition.
+
+Optionally, you can specify [TCP socket context options](http://php.net/manual/en/context.socket.php)
+for the underlying stream socket resource like this:
+
+```php
+$server = new Server('[::1]:8080', $loop, array(
+ 'tcp' => array(
+ 'backlog' => 200,
+ 'so_reuseport' => true,
+ 'ipv6_v6only' => true
+ )
+));
+```
+
+> Note that available [socket context options](http://php.net/manual/en/context.socket.php),
+ their defaults and effects of changing these may vary depending on your system
+ and/or PHP version.
+ Passing unknown context options has no effect.
+ For BC reasons, you can also pass the TCP socket context options as a simple
+ array without wrapping this in another array under the `tcp` key.
+
+You can start a secure TLS (formerly known as SSL) server by simply prepending
+the `tls://` URI scheme.
+Internally, it will wait for plaintext TCP/IP connections and then performs a
+TLS handshake for each connection.
+It thus requires valid [TLS context options](http://php.net/manual/en/context.ssl.php),
+which in its most basic form may look something like this if you're using a
+PEM encoded certificate file:
+
+```php
+$server = new Server('tls://127.0.0.1:8080', $loop, array(
+ 'tls' => array(
+ 'local_cert' => 'server.pem'
+ )
+));
+```
+
+> Note that the certificate file will not be loaded on instantiation but when an
+ incoming connection initializes its TLS context.
+ This implies that any invalid certificate file paths or contents will only cause
+ an `error` event at a later time.
+
+If your private key is encrypted with a passphrase, you have to specify it
+like this:
+
+```php
+$server = new Server('tls://127.0.0.1:8000', $loop, array(
+ 'tls' => array(
+ 'local_cert' => 'server.pem',
+ 'passphrase' => 'secret'
+ )
+));
+```
+
+By default, this server supports TLSv1.0+ and excludes support for legacy
+SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you
+want to negotiate with the remote side:
+
+```php
+$server = new Server('tls://127.0.0.1:8000', $loop, array(
+ 'tls' => array(
+ 'local_cert' => 'server.pem',
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER
+ )
+));
+```
+
+> Note that available [TLS context options](http://php.net/manual/en/context.ssl.php),
+ their defaults and effects of changing these may vary depending on your system
+ and/or PHP version.
+ The outer context array allows you to also use `tcp` (and possibly more)
+ context options at the same time.
+ Passing unknown context options has no effect.
+ If you do not use the `tls://` scheme, then passing `tls` context options
+ has no effect.
+
+Whenever a client connects, it will emit a `connection` event with a connection
+instance implementing [`ConnectionInterface`](#connectioninterface):
+
+```php
+$server->on('connection', function (ConnectionInterface $connection) {
+ echo 'Plaintext connection from ' . $connection->getRemoteAddress() . PHP_EOL;
+
+ $connection->write('hello there!' . PHP_EOL);
+ …
+});
+```
+
+See also the [`ServerInterface`](#serverinterface) for more details.
+
+> Note that the `Server` class is a concrete implementation for TCP/IP sockets.
+ If you want to typehint in your higher-level protocol implementation, you SHOULD
+ use the generic [`ServerInterface`](#serverinterface) instead.
+
+### Advanced server usage
+
+#### TcpServer
+
+The `TcpServer` class implements the [`ServerInterface`](#serverinterface) and
+is responsible for accepting plaintext TCP/IP connections.
+
+```php
+$server = new TcpServer(8080, $loop);
+```
+
+As above, the `$uri` parameter can consist of only a port, in which case the
+server will default to listening on the localhost address `127.0.0.1`,
+which means it will not be reachable from outside of this system.
+
+In order to use a random port assignment, you can use the port `0`:
+
+```php
+$server = new TcpServer(0, $loop);
+$address = $server->getAddress();
+```
+
+In order to change the host the socket is listening on, you can provide an IP
+address through the first parameter provided to the constructor, optionally
+preceded by the `tcp://` scheme:
+
+```php
+$server = new TcpServer('192.168.0.1:8080', $loop);
+```
+
+If you want to listen on an IPv6 address, you MUST enclose the host in square
+brackets:
+
+```php
+$server = new TcpServer('[::1]:8080', $loop);
+```
+
+If the given URI is invalid, does not contain a port, any other scheme or if it
+contains a hostname, it will throw an `InvalidArgumentException`:
+
+```php
+// throws InvalidArgumentException due to missing port
+$server = new TcpServer('127.0.0.1', $loop);
+```
+
+If the given URI appears to be valid, but listening on it fails (such as if port
+is already in use or port below 1024 may require root access etc.), it will
+throw a `RuntimeException`:
+
+```php
+$first = new TcpServer(8080, $loop);
+
+// throws RuntimeException because port is already in use
+$second = new TcpServer(8080, $loop);
+```
+
+> Note that these error conditions may vary depending on your system and/or
+configuration.
+See the exception message and code for more details about the actual error
+condition.
+
+Optionally, you can specify [socket context options](http://php.net/manual/en/context.socket.php)
+for the underlying stream socket resource like this:
+
+```php
+$server = new TcpServer('[::1]:8080', $loop, array(
+ 'backlog' => 200,
+ 'so_reuseport' => true,
+ 'ipv6_v6only' => true
+));
+```
+
+> Note that available [socket context options](http://php.net/manual/en/context.socket.php),
+their defaults and effects of changing these may vary depending on your system
+and/or PHP version.
+Passing unknown context options has no effect.
+
+Whenever a client connects, it will emit a `connection` event with a connection
+instance implementing [`ConnectionInterface`](#connectioninterface):
+
+```php
+$server->on('connection', function (ConnectionInterface $connection) {
+ echo 'Plaintext connection from ' . $connection->getRemoteAddress() . PHP_EOL;
+
+ $connection->write('hello there!' . PHP_EOL);
+ …
+});
+```
+
+See also the [`ServerInterface`](#serverinterface) for more details.
+
+#### SecureServer
+
+The `SecureServer` class implements the [`ServerInterface`](#serverinterface)
+and is responsible for providing a secure TLS (formerly known as SSL) server.
+
+It does so by wrapping a [`TcpServer`](#tcpserver) instance which waits for plaintext
+TCP/IP connections and then performs a TLS handshake for each connection.
+It thus requires valid [TLS context options](http://php.net/manual/en/context.ssl.php),
+which in its most basic form may look something like this if you're using a
+PEM encoded certificate file:
+
+```php
+$server = new TcpServer(8000, $loop);
+$server = new SecureServer($server, $loop, array(
+ 'local_cert' => 'server.pem'
+));
+```
+
+> Note that the certificate file will not be loaded on instantiation but when an
+incoming connection initializes its TLS context.
+This implies that any invalid certificate file paths or contents will only cause
+an `error` event at a later time.
+
+If your private key is encrypted with a passphrase, you have to specify it
+like this:
+
+```php
+$server = new TcpServer(8000, $loop);
+$server = new SecureServer($server, $loop, array(
+ 'local_cert' => 'server.pem',
+ 'passphrase' => 'secret'
+));
+```
+
+By default, this server supports TLSv1.0+ and excludes support for legacy
+SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you
+want to negotiate with the remote side:
+
+```php
+$server = new TcpServer(8000, $loop);
+$server = new SecureServer($server, $loop, array(
+ 'local_cert' => 'server.pem',
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER
+));
+```
+
+> Note that available [TLS context options](http://php.net/manual/en/context.ssl.php),
+their defaults and effects of changing these may vary depending on your system
+and/or PHP version.
+Passing unknown context options has no effect.
+
+Whenever a client completes the TLS handshake, it will emit a `connection` event
+with a connection instance implementing [`ConnectionInterface`](#connectioninterface):
+
+```php
+$server->on('connection', function (ConnectionInterface $connection) {
+ echo 'Secure connection from' . $connection->getRemoteAddress() . PHP_EOL;
+
+ $connection->write('hello there!' . PHP_EOL);
+ …
+});
+```
+
+Whenever a client fails to perform a successful TLS handshake, it will emit an
+`error` event and then close the underlying TCP/IP connection:
+
+```php
+$server->on('error', function (Exception $e) {
+ echo 'Error' . $e->getMessage() . PHP_EOL;
+});
+```
+
+See also the [`ServerInterface`](#serverinterface) for more details.
+
+Note that the `SecureServer` class is a concrete implementation for TLS sockets.
+If you want to typehint in your higher-level protocol implementation, you SHOULD
+use the generic [`ServerInterface`](#serverinterface) instead.
+
+> Advanced usage: Despite allowing any `ServerInterface` as first parameter,
+you SHOULD pass a `TcpServer` instance as first parameter, unless you
+know what you're doing.
+Internally, the `SecureServer` has to set the required TLS context options on
+the underlying stream resources.
+These resources are not exposed through any of the interfaces defined in this
+package, but only through the internal `Connection` class.
+The `TcpServer` class is guaranteed to emit connections that implement
+the `ConnectionInterface` and uses the internal `Connection` class in order to
+expose these underlying resources.
+If you use a custom `ServerInterface` and its `connection` event does not
+meet this requirement, the `SecureServer` will emit an `error` event and
+then close the underlying connection.
+
+#### UnixServer
+
+The `UnixServer` class implements the [`ServerInterface`](#serverinterface) and
+is responsible for accepting connections on Unix domain sockets (UDS).
+
+```php
+$server = new UnixServer('/tmp/server.sock', $loop);
+```
+
+As above, the `$uri` parameter can consist of only a socket path or socket path
+prefixed by the `unix://` scheme.
+
+If the given URI appears to be valid, but listening on it fails (such as if the
+socket is already in use or the file not accessible etc.), it will throw a
+`RuntimeException`:
+
+```php
+$first = new UnixServer('/tmp/same.sock', $loop);
+
+// throws RuntimeException because socket is already in use
+$second = new UnixServer('/tmp/same.sock', $loop);
+```
+
+Whenever a client connects, it will emit a `connection` event with a connection
+instance implementing [`ConnectionInterface`](#connectioninterface):
+
+```php
+$server->on('connection', function (ConnectionInterface $connection) {
+ echo 'New connection' . PHP_EOL;
+
+ $connection->write('hello there!' . PHP_EOL);
+ …
+});
+```
+
+See also the [`ServerInterface`](#serverinterface) for more details.
+
+#### LimitingServer
+
+The `LimitingServer` decorator wraps a given `ServerInterface` and is responsible
+for limiting and keeping track of open connections to this server instance.
+
+Whenever the underlying server emits a `connection` event, it will check its
+limits and then either
+ - keep track of this connection by adding it to the list of
+ open connections and then forward the `connection` event
+ - or reject (close) the connection when its limits are exceeded and will
+ forward an `error` event instead.
+
+Whenever a connection closes, it will remove this connection from the list of
+open connections.
+
+```php
+$server = new LimitingServer($server, 100);
+$server->on('connection', function (ConnectionInterface $connection) {
+ $connection->write('hello there!' . PHP_EOL);
+ …
+});
+```
+
+See also the [second example](examples) for more details.
+
+You have to pass a maximum number of open connections to ensure
+the server will automatically reject (close) connections once this limit
+is exceeded. In this case, it will emit an `error` event to inform about
+this and no `connection` event will be emitted.
+
+```php
+$server = new LimitingServer($server, 100);
+$server->on('connection', function (ConnectionInterface $connection) {
+ $connection->write('hello there!' . PHP_EOL);
+ …
+});
+```
+
+You MAY pass a `null` limit in order to put no limit on the number of
+open connections and keep accepting new connection until you run out of
+operating system resources (such as open file handles). This may be
+useful if you do not want to take care of applying a limit but still want
+to use the `getConnections()` method.
+
+You can optionally configure the server to pause accepting new
+connections once the connection limit is reached. In this case, it will
+pause the underlying server and no longer process any new connections at
+all, thus also no longer closing any excessive connections.
+The underlying operating system is responsible for keeping a backlog of
+pending connections until its limit is reached, at which point it will
+start rejecting further connections.
+Once the server is below the connection limit, it will continue consuming
+connections from the backlog and will process any outstanding data on
+each connection.
+This mode may be useful for some protocols that are designed to wait for
+a response message (such as HTTP), but may be less useful for other
+protocols that demand immediate responses (such as a "welcome" message in
+an interactive chat).
+
+```php
+$server = new LimitingServer($server, 100, true);
+$server->on('connection', function (ConnectionInterface $connection) {
+ $connection->write('hello there!' . PHP_EOL);
+ …
+});
+```
+
+##### getConnections()
+
+The `getConnections(): ConnectionInterface[]` method can be used to
+return an array with all currently active connections.
+
+```php
+foreach ($server->getConnection() as $connection) {
+ $connection->write('Hi!');
+}
+```
+
+## Client usage
+
+### ConnectorInterface
+
+The `ConnectorInterface` is responsible for providing an interface for
+establishing streaming connections, such as a normal TCP/IP connection.
+
+This is the main interface defined in this package and it is used throughout
+React's vast ecosystem.
+
+Most higher-level components (such as HTTP, database or other networking
+service clients) accept an instance implementing this interface to create their
+TCP/IP connection to the underlying networking service.
+This is usually done via dependency injection, so it's fairly simple to actually
+swap this implementation against any other implementation of this interface.
+
+The interface only offers a single method:
+
+#### connect()
+
+The `connect(string $uri): PromiseInterface` method
+can be used to create a streaming connection to the given remote address.
+
+It returns a [Promise](https://github.com/reactphp/promise) which either
+fulfills with a stream implementing [`ConnectionInterface`](#connectioninterface)
+on success or rejects with an `Exception` if the connection is not successful:
+
+```php
+$connector->connect('google.com:443')->then(
+ function (ConnectionInterface $connection) {
+ // connection successfully established
+ },
+ function (Exception $error) {
+ // failed to connect due to $error
+ }
+);
+```
+
+See also [`ConnectionInterface`](#connectioninterface) for more details.
+
+The returned Promise MUST be implemented in such a way that it can be
+cancelled when it is still pending. Cancelling a pending promise MUST
+reject its value with an `Exception`. It SHOULD clean up any underlying
+resources and references as applicable:
+
+```php
+$promise = $connector->connect($uri);
+
+$promise->cancel();
+```
+
+### Connector
+
+The `Connector` class is the main class in this package that implements the
+[`ConnectorInterface`](#connectorinterface) and allows you to create streaming connections.
+
+You can use this connector to create any kind of streaming connections, such
+as plaintext TCP/IP, secure TLS or local Unix connection streams.
+
+It binds to the main event loop and can be used like this:
+
+```php
+$loop = React\EventLoop\Factory::create();
+$connector = new Connector($loop);
+
+$connector->connect($uri)->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+
+$loop->run();
+```
+
+In order to create a plaintext TCP/IP connection, you can simply pass a host
+and port combination like this:
+
+```php
+$connector->connect('www.google.com:80')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+```
+
+> If you do no specify a URI scheme in the destination URI, it will assume
+ `tcp://` as a default and establish a plaintext TCP/IP connection.
+ Note that TCP/IP connections require a host and port part in the destination
+ URI like above, all other URI components are optional.
+
+In order to create a secure TLS connection, you can use the `tls://` URI scheme
+like this:
+
+```php
+$connector->connect('tls://www.google.com:443')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+```
+
+In order to create a local Unix domain socket connection, you can use the
+`unix://` URI scheme like this:
+
+```php
+$connector->connect('unix:///tmp/demo.sock')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+```
+
+> The [`getRemoteAddress()`](#getremoteaddress) method will return the target
+ Unix domain socket (UDS) path as given to the `connect()` method, including
+ the `unix://` scheme, for example `unix:///tmp/demo.sock`.
+ The [`getLocalAddress()`](#getlocaladdress) method will most likely return a
+ `null` value as this value is not applicable to UDS connections here.
+
+Under the hood, the `Connector` is implemented as a *higher-level facade*
+for the lower-level connectors implemented in this package. This means it
+also shares all of their features and implementation details.
+If you want to typehint in your higher-level protocol implementation, you SHOULD
+use the generic [`ConnectorInterface`](#connectorinterface) instead.
+
+The `Connector` class will try to detect your system DNS settings (and uses
+Google's public DNS server `8.8.8.8` as a fallback if unable to determine your
+system settings) to resolve all public hostnames into underlying IP addresses by
+default.
+If you explicitly want to use a custom DNS server (such as a local DNS relay or
+a company wide DNS server), you can set up the `Connector` like this:
+
+```php
+$connector = new Connector($loop, array(
+ 'dns' => '127.0.1.1'
+));
+
+$connector->connect('localhost:80')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+```
+
+If you do not want to use a DNS resolver at all and want to connect to IP
+addresses only, you can also set up your `Connector` like this:
+
+```php
+$connector = new Connector($loop, array(
+ 'dns' => false
+));
+
+$connector->connect('127.0.0.1:80')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+```
+
+Advanced: If you need a custom DNS `Resolver` instance, you can also set up
+your `Connector` like this:
+
+```php
+$dnsResolverFactory = new React\Dns\Resolver\Factory();
+$resolver = $dnsResolverFactory->createCached('127.0.1.1', $loop);
+
+$connector = new Connector($loop, array(
+ 'dns' => $resolver
+));
+
+$connector->connect('localhost:80')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+```
+
+By default, the `tcp://` and `tls://` URI schemes will use timeout value that
+repects your `default_socket_timeout` ini setting (which defaults to 60s).
+If you want a custom timeout value, you can simply pass this like this:
+
+```php
+$connector = new Connector($loop, array(
+ 'timeout' => 10.0
+));
+```
+
+Similarly, if you do not want to apply a timeout at all and let the operating
+system handle this, you can pass a boolean flag like this:
+
+```php
+$connector = new Connector($loop, array(
+ 'timeout' => false
+));
+```
+
+By default, the `Connector` supports the `tcp://`, `tls://` and `unix://`
+URI schemes. If you want to explicitly prohibit any of these, you can simply
+pass boolean flags like this:
+
+```php
+// only allow secure TLS connections
+$connector = new Connector($loop, array(
+ 'tcp' => false,
+ 'tls' => true,
+ 'unix' => false,
+));
+
+$connector->connect('tls://google.com:443')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+```
+
+The `tcp://` and `tls://` also accept additional context options passed to
+the underlying connectors.
+If you want to explicitly pass additional context options, you can simply
+pass arrays of context options like this:
+
+```php
+// allow insecure TLS connections
+$connector = new Connector($loop, array(
+ 'tcp' => array(
+ 'bindto' => '192.168.0.1:0'
+ ),
+ 'tls' => array(
+ 'verify_peer' => false,
+ 'verify_peer_name' => false
+ ),
+));
+
+$connector->connect('tls://localhost:443')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+```
+
+By default, this connector supports TLSv1.0+ and excludes support for legacy
+SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you
+want to negotiate with the remote side:
+
+```php
+$connector = new Connector($loop, array(
+ 'tls' => array(
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
+ )
+));
+```
+
+> For more details about context options, please refer to the PHP documentation
+ about [socket context options](http://php.net/manual/en/context.socket.php)
+ and [SSL context options](http://php.net/manual/en/context.ssl.php).
+
+Advanced: By default, the `Connector` supports the `tcp://`, `tls://` and
+`unix://` URI schemes.
+For this, it sets up the required connector classes automatically.
+If you want to explicitly pass custom connectors for any of these, you can simply
+pass an instance implementing the `ConnectorInterface` like this:
+
+```php
+$dnsResolverFactory = new React\Dns\Resolver\Factory();
+$resolver = $dnsResolverFactory->createCached('127.0.1.1', $loop);
+$tcp = new DnsConnector(new TcpConnector($loop), $resolver);
+
+$tls = new SecureConnector($tcp, $loop);
+
+$unix = new UnixConnector($loop);
+
+$connector = new Connector($loop, array(
+ 'tcp' => $tcp,
+ 'tls' => $tls,
+ 'unix' => $unix,
+
+ 'dns' => false,
+ 'timeout' => false,
+));
+
+$connector->connect('google.com:80')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+```
+
+> Internally, the `tcp://` connector will always be wrapped by the DNS resolver,
+ unless you disable DNS like in the above example. In this case, the `tcp://`
+ connector receives the actual hostname instead of only the resolved IP address
+ and is thus responsible for performing the lookup.
+ Internally, the automatically created `tls://` connector will always wrap the
+ underlying `tcp://` connector for establishing the underlying plaintext
+ TCP/IP connection before enabling secure TLS mode. If you want to use a custom
+ underlying `tcp://` connector for secure TLS connections only, you may
+ explicitly pass a `tls://` connector like above instead.
+ Internally, the `tcp://` and `tls://` connectors will always be wrapped by
+ `TimeoutConnector`, unless you disable timeouts like in the above example.
+
+### Advanced client usage
+
+#### TcpConnector
+
+The `React\Socket\TcpConnector` class implements the
+[`ConnectorInterface`](#connectorinterface) and allows you to create plaintext
+TCP/IP connections to any IP-port-combination:
+
+```php
+$tcpConnector = new React\Socket\TcpConnector($loop);
+
+$tcpConnector->connect('127.0.0.1:80')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+
+$loop->run();
+```
+
+See also the [examples](examples).
+
+Pending connection attempts can be cancelled by cancelling its pending promise like so:
+
+```php
+$promise = $tcpConnector->connect('127.0.0.1:80');
+
+$promise->cancel();
+```
+
+Calling `cancel()` on a pending promise will close the underlying socket
+resource, thus cancelling the pending TCP/IP connection, and reject the
+resulting promise.
+
+You can optionally pass additional
+[socket context options](http://php.net/manual/en/context.socket.php)
+to the constructor like this:
+
+```php
+$tcpConnector = new React\Socket\TcpConnector($loop, array(
+ 'bindto' => '192.168.0.1:0'
+));
+```
+
+Note that this class only allows you to connect to IP-port-combinations.
+If the given URI is invalid, does not contain a valid IP address and port
+or contains any other scheme, it will reject with an
+`InvalidArgumentException`:
+
+If the given URI appears to be valid, but connecting to it fails (such as if
+the remote host rejects the connection etc.), it will reject with a
+`RuntimeException`.
+
+If you want to connect to hostname-port-combinations, see also the following chapter.
+
+> Advanced usage: Internally, the `TcpConnector` allocates an empty *context*
+resource for each stream resource.
+If the destination URI contains a `hostname` query parameter, its value will
+be used to set up the TLS peer name.
+This is used by the `SecureConnector` and `DnsConnector` to verify the peer
+name and can also be used if you want a custom TLS peer name.
+
+#### DnsConnector
+
+The `DnsConnector` class implements the
+[`ConnectorInterface`](#connectorinterface) and allows you to create plaintext
+TCP/IP connections to any hostname-port-combination.
+
+It does so by decorating a given `TcpConnector` instance so that it first
+looks up the given domain name via DNS (if applicable) and then establishes the
+underlying TCP/IP connection to the resolved target IP address.
+
+Make sure to set up your DNS resolver and underlying TCP connector like this:
+
+```php
+$dnsResolverFactory = new React\Dns\Resolver\Factory();
+$dns = $dnsResolverFactory->createCached('8.8.8.8', $loop);
+
+$dnsConnector = new React\Socket\DnsConnector($tcpConnector, $dns);
+
+$dnsConnector->connect('www.google.com:80')->then(function (ConnectionInterface $connection) {
+ $connection->write('...');
+ $connection->end();
+});
+
+$loop->run();
+```
+
+See also the [examples](examples).
+
+Pending connection attempts can be cancelled by cancelling its pending promise like so:
+
+```php
+$promise = $dnsConnector->connect('www.google.com:80');
+
+$promise->cancel();
+```
+
+Calling `cancel()` on a pending promise will cancel the underlying DNS lookup
+and/or the underlying TCP/IP connection and reject the resulting promise.
+
+> Advanced usage: Internally, the `DnsConnector` relies on a `Resolver` to
+look up the IP address for the given hostname.
+It will then replace the hostname in the destination URI with this IP and
+append a `hostname` query parameter and pass this updated URI to the underlying
+connector.
+The underlying connector is thus responsible for creating a connection to the
+target IP address, while this query parameter can be used to check the original
+hostname and is used by the `TcpConnector` to set up the TLS peer name.
+If a `hostname` is given explicitly, this query parameter will not be modified,
+which can be useful if you want a custom TLS peer name.
+
+#### SecureConnector
+
+The `SecureConnector` class implements the
+[`ConnectorInterface`](#connectorinterface) and allows you to create secure
+TLS (formerly known as SSL) connections to any hostname-port-combination.
+
+It does so by decorating a given `DnsConnector` instance so that it first
+creates a plaintext TCP/IP connection and then enables TLS encryption on this
+stream.
+
+```php
+$secureConnector = new React\Socket\SecureConnector($dnsConnector, $loop);
+
+$secureConnector->connect('www.google.com:443')->then(function (ConnectionInterface $connection) {
+ $connection->write("GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n");
+ ...
+});
+
+$loop->run();
+```
+
+See also the [examples](examples).
+
+Pending connection attempts can be cancelled by cancelling its pending promise like so:
+
+```php
+$promise = $secureConnector->connect('www.google.com:443');
+
+$promise->cancel();
+```
+
+Calling `cancel()` on a pending promise will cancel the underlying TCP/IP
+connection and/or the SSL/TLS negotiation and reject the resulting promise.
+
+You can optionally pass additional
+[SSL context options](http://php.net/manual/en/context.ssl.php)
+to the constructor like this:
+
+```php
+$secureConnector = new React\Socket\SecureConnector($dnsConnector, $loop, array(
+ 'verify_peer' => false,
+ 'verify_peer_name' => false
+));
+```
+
+By default, this connector supports TLSv1.0+ and excludes support for legacy
+SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you
+want to negotiate with the remote side:
+
+```php
+$secureConnector = new React\Socket\SecureConnector($dnsConnector, $loop, array(
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
+));
+```
+
+> Advanced usage: Internally, the `SecureConnector` relies on setting up the
+required *context options* on the underlying stream resource.
+It should therefor be used with a `TcpConnector` somewhere in the connector
+stack so that it can allocate an empty *context* resource for each stream
+resource and verify the peer name.
+Failing to do so may result in a TLS peer name mismatch error or some hard to
+trace race conditions, because all stream resources will use a single, shared
+*default context* resource otherwise.
+
+#### TimeoutConnector
+
+The `TimeoutConnector` class implements the
+[`ConnectorInterface`](#connectorinterface) and allows you to add timeout
+handling to any existing connector instance.
+
+It does so by decorating any given [`ConnectorInterface`](#connectorinterface)
+instance and starting a timer that will automatically reject and abort any
+underlying connection attempt if it takes too long.
+
+```php
+$timeoutConnector = new React\Socket\TimeoutConnector($connector, 3.0, $loop);
+
+$timeoutConnector->connect('google.com:80')->then(function (ConnectionInterface $connection) {
+ // connection succeeded within 3.0 seconds
+});
+```
+
+See also any of the [examples](examples).
+
+Pending connection attempts can be cancelled by cancelling its pending promise like so:
+
+```php
+$promise = $timeoutConnector->connect('google.com:80');
+
+$promise->cancel();
+```
+
+Calling `cancel()` on a pending promise will cancel the underlying connection
+attempt, abort the timer and reject the resulting promise.
+
+#### UnixConnector
+
+The `UnixConnector` class implements the
+[`ConnectorInterface`](#connectorinterface) and allows you to connect to
+Unix domain socket (UDS) paths like this:
+
+```php
+$connector = new React\Socket\UnixConnector($loop);
+
+$connector->connect('/tmp/demo.sock')->then(function (ConnectionInterface $connection) {
+ $connection->write("HELLO\n");
+});
+
+$loop->run();
+```
+
+Connecting to Unix domain sockets is an atomic operation, i.e. its promise will
+settle (either resolve or reject) immediately.
+As such, calling `cancel()` on the resulting promise has no effect.
+
+> The [`getRemoteAddress()`](#getremoteaddress) method will return the target
+ Unix domain socket (UDS) path as given to the `connect()` method, prepended
+ with the `unix://` scheme, for example `unix:///tmp/demo.sock`.
+ The [`getLocalAddress()`](#getlocaladdress) method will most likely return a
+ `null` value as this value is not applicable to UDS connections here.
+
+#### FixedUriConnector
+
+The `FixedUriConnector` class implements the
+[`ConnectorInterface`](#connectorinterface) and decorates an existing Connector
+to always use a fixed, preconfigured URI.
+
+This can be useful for consumers that do not support certain URIs, such as
+when you want to explicitly connect to a Unix domain socket (UDS) path
+instead of connecting to a default address assumed by an higher-level API:
+
+```php
+$connector = new FixedUriConnector(
+ 'unix:///var/run/docker.sock',
+ new UnixConnector($loop)
+);
+
+// destination will be ignored, actually connects to Unix domain socket
+$promise = $connector->connect('localhost:80');
+```
+
+## Install
+
+The recommended way to install this library is [through Composer](https://getcomposer.org).
+[New to Composer?](https://getcomposer.org/doc/00-intro.md)
+
+This will install the latest supported version:
+
+```bash
+$ composer require react/socket:^0.8.10
+```
+
+See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
+
+This project aims to run on any platform and thus does not require any PHP
+extensions and supports running on legacy PHP 5.3 through current PHP 7+ and HHVM.
+It's *highly recommended to use PHP 7+* for this project, partly due to its vast
+performance improvements and partly because legacy PHP versions require several
+workarounds as described below.
+
+Secure TLS connections received some major upgrades starting with PHP 5.6, with
+the defaults now being more secure, while older versions required explicit
+context options.
+This library does not take responsibility over these context options, so it's
+up to consumers of this library to take care of setting appropriate context
+options as described above.
+
+All versions of PHP prior to 5.6.8 suffered from a buffering issue where reading
+from a streaming TLS connection could be one `data` event behind.
+This library implements a work-around to try to flush the complete incoming
+data buffers on these legacy PHP versions, which has a penalty of around 10% of
+throughput on all connections.
+With this work-around, we have not been able to reproduce this issue anymore,
+but we have seen reports of people saying this could still affect some of the
+older PHP versions (`5.5.23`, `5.6.7`, and `5.6.8`).
+Note that this only affects *some* higher-level streaming protocols, such as
+IRC over TLS, but should not affect HTTP over TLS (HTTPS).
+Further investigation of this issue is needed.
+For more insights, this issue is also covered by our test suite.
+
+PHP < 7.1.4 (and PHP < 7.0.18) suffers from a bug when writing big
+chunks of data over TLS streams at once.
+We try to work around this by limiting the write chunk size to 8192
+bytes for older PHP versions only.
+This is only a work-around and has a noticable performance penalty on
+affected versions.
+
+This project also supports running on HHVM.
+Note that really old HHVM < 3.8 does not support secure TLS connections, as it
+lacks the required `stream_socket_enable_crypto()` function.
+As such, trying to create a secure TLS connections on affected versions will
+return a rejected promise instead.
+This issue is also covered by our test suite, which will skip related tests
+on affected versions.
+
+## Tests
+
+To run the test suite, you first need to clone this repo and then install all
+dependencies [through Composer](https://getcomposer.org):
+
+```bash
+$ composer install
+```
+
+To run the test suite, go to the project root and run:
+
+```bash
+$ php vendor/bin/phpunit
+```
+
+The test suite also contains a number of functional integration tests that rely
+on a stable internet connection.
+If you do not want to run these, they can simply be skipped like this:
+
+```bash
+$ php vendor/bin/phpunit --exclude-group internet
+```
+
+## License
+
+MIT, see [LICENSE file](LICENSE).
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/composer.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/composer.json
new file mode 100644
index 0000000..bc85aab
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/composer.json
@@ -0,0 +1,29 @@
+{
+ "name": "react/socket",
+ "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP",
+ "keywords": ["async", "socket", "stream", "connection", "ReactPHP"],
+ "license": "MIT",
+ "require": {
+ "php": ">=5.3.0",
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "react/dns": "^0.4.13",
+ "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5",
+ "react/stream": "^1.0 || ^0.7.1",
+ "react/promise": "^2.1 || ^1.2",
+ "react/promise-timer": "~1.0"
+ },
+ "require-dev": {
+ "clue/block-react": "^1.2",
+ "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35"
+ },
+ "autoload": {
+ "psr-4": {
+ "React\\Socket\\": "src"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "React\\Tests\\Socket\\": "tests"
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/01-echo-server.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/01-echo-server.php
new file mode 100644
index 0000000..2c0be57
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/01-echo-server.php
@@ -0,0 +1,42 @@
+ array(
+ 'local_cert' => isset($argv[2]) ? $argv[2] : (__DIR__ . '/localhost.pem')
+ )
+));
+
+$server->on('connection', function (ConnectionInterface $conn) {
+ echo '[' . $conn->getRemoteAddress() . ' connected]' . PHP_EOL;
+ $conn->pipe($conn);
+});
+
+$server->on('error', 'printf');
+
+echo 'Listening on ' . $server->getAddress() . PHP_EOL;
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/02-chat-server.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/02-chat-server.php
new file mode 100644
index 0000000..46439e0
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/02-chat-server.php
@@ -0,0 +1,59 @@
+ array(
+ 'local_cert' => isset($argv[2]) ? $argv[2] : (__DIR__ . '/localhost.pem')
+ )
+));
+
+$server = new LimitingServer($server, null);
+
+$server->on('connection', function (ConnectionInterface $client) use ($server) {
+ // whenever a new message comes in
+ $client->on('data', function ($data) use ($client, $server) {
+ // remove any non-word characters (just for the demo)
+ $data = trim(preg_replace('/[^\w\d \.\,\-\!\?]/u', '', $data));
+
+ // ignore empty messages
+ if ($data === '') {
+ return;
+ }
+
+ // prefix with client IP and broadcast to all connected clients
+ $data = trim(parse_url($client->getRemoteAddress(), PHP_URL_HOST), '[]') . ': ' . $data . PHP_EOL;
+ foreach ($server->getConnections() as $connection) {
+ $connection->write($data);
+ }
+ });
+});
+
+$server->on('error', 'printf');
+
+echo 'Listening on ' . $server->getAddress() . PHP_EOL;
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/03-http-server.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/03-http-server.php
new file mode 100644
index 0000000..eb6d454
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/03-http-server.php
@@ -0,0 +1,57 @@
+ array(
+ 'local_cert' => isset($argv[2]) ? $argv[2] : (__DIR__ . '/localhost.pem')
+ )
+));
+
+$server->on('connection', function (ConnectionInterface $conn) {
+ $conn->once('data', function () use ($conn) {
+ $body = "Hello world! \r\n";
+ $conn->end("HTTP/1.1 200 OK\r\nContent-Length: " . strlen($body) . "\r\nConnection: close\r\n\r\n" . $body);
+ });
+});
+
+$server->on('error', 'printf');
+
+echo 'Listening on ' . strtr($server->getAddress(), array('tcp:' => 'http:', 'tls:' => 'https:')) . PHP_EOL;
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/11-http-client.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/11-http-client.php
new file mode 100644
index 0000000..2b64a43
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/11-http-client.php
@@ -0,0 +1,36 @@
+connect($host. ':80')->then(function (ConnectionInterface $connection) use ($host) {
+ $connection->on('data', function ($data) {
+ echo $data;
+ });
+ $connection->on('close', function () {
+ echo '[CLOSED]' . PHP_EOL;
+ });
+
+ $connection->write("GET / HTTP/1.0\r\nHost: $host\r\n\r\n");
+}, 'printf');
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/12-https-client.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/12-https-client.php
new file mode 100644
index 0000000..6e3f279
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/12-https-client.php
@@ -0,0 +1,36 @@
+connect('tls://' . $host . ':443')->then(function (ConnectionInterface $connection) use ($host) {
+ $connection->on('data', function ($data) {
+ echo $data;
+ });
+ $connection->on('close', function () {
+ echo '[CLOSED]' . PHP_EOL;
+ });
+
+ $connection->write("GET / HTTP/1.0\r\nHost: $host\r\n\r\n");
+}, 'printf');
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/21-netcat-client.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/21-netcat-client.php
new file mode 100644
index 0000000..9140e2c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/21-netcat-client.php
@@ -0,0 +1,68 @@
+' . PHP_EOL);
+ exit(1);
+}
+
+$loop = Factory::create();
+$connector = new Connector($loop);
+
+$stdin = new ReadableResourceStream(STDIN, $loop);
+$stdin->pause();
+$stdout = new WritableResourceStream(STDOUT, $loop);
+$stderr = new WritableResourceStream(STDERR, $loop);
+
+$stderr->write('Connecting' . PHP_EOL);
+
+$connector->connect($argv[1])->then(function (ConnectionInterface $connection) use ($stdin, $stdout, $stderr) {
+ // pipe everything from STDIN into connection
+ $stdin->resume();
+ $stdin->pipe($connection);
+
+ // pipe everything from connection to STDOUT
+ $connection->pipe($stdout);
+
+ // report errors to STDERR
+ $connection->on('error', function ($error) use ($stderr) {
+ $stderr->write('Stream ERROR: ' . $error . PHP_EOL);
+ });
+
+ // report closing and stop reading from input
+ $connection->on('close', function () use ($stderr, $stdin) {
+ $stderr->write('[CLOSED]' . PHP_EOL);
+ $stdin->close();
+ });
+
+ $stderr->write('Connected' . PHP_EOL);
+}, function ($error) use ($stderr) {
+ $stderr->write('Connection ERROR: ' . $error . PHP_EOL);
+});
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/22-http-client.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/22-http-client.php
new file mode 100644
index 0000000..fcb8107
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/22-http-client.php
@@ -0,0 +1,60 @@
+' . PHP_EOL);
+ exit(1);
+}
+
+$loop = Factory::create();
+$connector = new Connector($loop);
+
+if (!isset($parts['port'])) {
+ $parts['port'] = $parts['scheme'] === 'https' ? 443 : 80;
+}
+
+$host = $parts['host'];
+if (($parts['scheme'] === 'http' && $parts['port'] !== 80) || ($parts['scheme'] === 'https' && $parts['port'] !== 443)) {
+ $host .= ':' . $parts['port'];
+}
+$target = ($parts['scheme'] === 'https' ? 'tls' : 'tcp') . '://' . $parts['host'] . ':' . $parts['port'];
+$resource = isset($parts['path']) ? $parts['path'] : '/';
+if (isset($parts['query'])) {
+ $resource .= '?' . $parts['query'];
+}
+
+$stdout = new WritableResourceStream(STDOUT, $loop);
+
+$connector->connect($target)->then(function (ConnectionInterface $connection) use ($resource, $host, $stdout) {
+ $connection->pipe($stdout);
+
+ $connection->write("GET $resource HTTP/1.0\r\nHost: $host\r\n\r\n");
+}, 'printf');
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/91-benchmark-server.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/91-benchmark-server.php
new file mode 100644
index 0000000..420d474
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/91-benchmark-server.php
@@ -0,0 +1,60 @@
+ array(
+ 'local_cert' => isset($argv[2]) ? $argv[2] : (__DIR__ . '/localhost.pem')
+ )
+));
+
+$server->on('connection', function (ConnectionInterface $conn) use ($loop) {
+ echo '[connected]' . PHP_EOL;
+
+ // count the number of bytes received from this connection
+ $bytes = 0;
+ $conn->on('data', function ($chunk) use (&$bytes) {
+ $bytes += strlen($chunk);
+ });
+
+ // report average throughput once client disconnects
+ $t = microtime(true);
+ $conn->on('close', function () use ($conn, $t, &$bytes) {
+ $t = microtime(true) - $t;
+ echo '[disconnected after receiving ' . $bytes . ' bytes in ' . round($t, 3) . 's => ' . round($bytes / $t / 1024 / 1024, 1) . ' MiB/s]' . PHP_EOL;
+ });
+});
+
+$server->on('error', 'printf');
+
+echo 'Listening on ' . $server->getAddress() . PHP_EOL;
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/99-generate-self-signed.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/99-generate-self-signed.php
new file mode 100644
index 0000000..00f9314
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/99-generate-self-signed.php
@@ -0,0 +1,31 @@
+ secret.pem
+
+// certificate details (Distinguished Name)
+// (OpenSSL applies defaults to missing fields)
+$dn = array(
+ "commonName" => isset($argv[1]) ? $argv[1] : "localhost",
+// "countryName" => "AU",
+// "stateOrProvinceName" => "Some-State",
+// "localityName" => "London",
+// "organizationName" => "Internet Widgits Pty Ltd",
+// "organizationalUnitName" => "R&D",
+// "emailAddress" => "admin@example.com"
+);
+
+// create certificate which is valid for ~10 years
+$privkey = openssl_pkey_new();
+$cert = openssl_csr_new($dn, $privkey);
+$cert = openssl_csr_sign($cert, null, $privkey, 3650);
+
+// export public and (optionally encrypted) private key in PEM format
+openssl_x509_export($cert, $out);
+echo $out;
+
+$passphrase = isset($argv[2]) ? $argv[2] : null;
+openssl_pkey_export($privkey, $out, $passphrase);
+echo $out;
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/localhost.pem b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/localhost.pem
new file mode 100644
index 0000000..be69279
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/localhost.pem
@@ -0,0 +1,49 @@
+-----BEGIN CERTIFICATE-----
+MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBZMRIwEAYDVQQDDAkxMjcu
+MC4wLjExCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQK
+DBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTYxMjMwMTQ1OTA2WhcNMjYx
+MjI4MTQ1OTA2WjBZMRIwEAYDVQQDDAkxMjcuMC4wLjExCzAJBgNVBAYTAkFVMRMw
+EQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0
+eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC8SZWNS+Ktg0Py
+W8dx5uXZ+ZUawd3wnzLMHW7EhoUpIrIdp3kDU9NezF68dOhPMJY/Kh+6btRCxWXN
+2OVTqS5Xi826j3TSE07iF83JRLeveW0PcodjUBd+RzdwCWWo2pfMJz4v7x1wu1c9
+zNi6JxxpDAXTFSB4GiWsI4tFu2XmMRhfm6LRK4WPfsZIJKokdiG5fKSPDn7nrVj0
+UUXr2eBsEAzdwL14U9+mwbLdaAkz3qK3fqi8sEC09lEWm95gKMOhkQf5qvXODtT4
+wdVrrKDTyehLv0xaItnUDnXzrkMBU5QS9TQzzqSW6ZaBsSxtONEFUiXiN9dtyXsY
+YCUE54G/AgMBAAGjUDBOMB0GA1UdDgQWBBQ2GRz3QsQzdXaTMnPVCKfpigA10DAf
+BgNVHSMEGDAWgBQ2GRz3QsQzdXaTMnPVCKfpigA10DAMBgNVHRMEBTADAQH/MA0G
+CSqGSIb3DQEBBQUAA4IBAQA77iZ4KrpPY18Ezjt0mngYAuAxunKddXYdLZ2khywN
+0uI/VzYnkFVtrsC7y2jLHSxlmE2/viPPGZDUplENV2acN6JNW+tlt7/bsrQHDQw3
+7VCF27EWiDxHsaghhLkqC+kcop5YR5c0oDQTdEWEKSbow2zayUXDYbRRs76SClTe
+824Yul+Ts8Mka+AX2PXDg47iZ84fJRN/nKavcJUTJ2iS1uYw0GNnFMge/uwsfMR3
+V47qN0X5emky8fcq99FlMCbcy0gHAeSWAjClgr2dd2i0LDatUbj7YmdmFcskOgII
+IwGfvuWR2yPevYGAE0QgFeLHniN3RW8zmpnX/XtrJ4a7
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC8SZWNS+Ktg0Py
+W8dx5uXZ+ZUawd3wnzLMHW7EhoUpIrIdp3kDU9NezF68dOhPMJY/Kh+6btRCxWXN
+2OVTqS5Xi826j3TSE07iF83JRLeveW0PcodjUBd+RzdwCWWo2pfMJz4v7x1wu1c9
+zNi6JxxpDAXTFSB4GiWsI4tFu2XmMRhfm6LRK4WPfsZIJKokdiG5fKSPDn7nrVj0
+UUXr2eBsEAzdwL14U9+mwbLdaAkz3qK3fqi8sEC09lEWm95gKMOhkQf5qvXODtT4
+wdVrrKDTyehLv0xaItnUDnXzrkMBU5QS9TQzzqSW6ZaBsSxtONEFUiXiN9dtyXsY
+YCUE54G/AgMBAAECggEBAKiO/3FE1CMddkCLZVtUp8ShqJgRokx9WI5ecwFApAkV
+ZHsjqDQQYRNmxhDUX/w0tOzLGyhde2xjJyZG29YviKsbHwu6zYwbeOzy/mkGOaK/
+g6DmmMmRs9Z6juifoQCu4GIFZ6il2adIL2vF7OeJh+eKudQj/7NFRSB7mXzNrQWK
+tZY3eux5zXWmio7pgZrx1HFZQiiL9NVLwT9J7oBnaoO3fREiu5J2xBpljG9Cr0j1
+LLiVLhukWJYRlHDtGt1CzI9w8iKo44PCRzpKyxpbsOrQxeSyEWUYQRv9VHA59LC7
+tVAJTbnTX1BNHkGZkOkoOpoZLwBaM2XbbDtcOGCAZMECgYEA+mTURFQ85/pxawvk
+9ndqZ+5He1u/bMLYIJDp0hdB/vgD+vw3gb2UyRwp0I6Wc6Si4FEEnbY7L0pzWsiR
+43CpLs+cyLfnD9NycuIasxs5fKb/1s1nGTkRAp7x9x/ZTtEf8v4YTmmMXFHzdo7V
+pv+czO89ppEDkxEtMf/b5SifhO8CgYEAwIDIUvXLduGhL+RPDwjc2SKdydXGV6om
+OEdt/V8oS801Z7k8l3gHXFm7zL/MpHmh9cag+F9dHK42kw2RSjDGsBlXXiAO1Z0I
+2A34OdPw/kow8fmIKWTMu3+28Kca+3RmUqeyaq0vazQ/bWMO9px+Ud3YfLo1Tn5I
+li0MecAx8DECgYEAvsLceKYYtL83c09fg2oc1ctSCCgw4WJcGAtvJ9DyRZacKbXH
+b/+H/+OF8879zmKqd+0hcCnqUzAMTCisBLPLIM+o6b45ufPkqKObpcJi/JWaKgLY
+vf2c+Psw6o4IF6T5Cz4MNIjzF06UBknxecYZpoPJ20F1kLCwVvxPgfl99l8CgYAb
+XfOcv67WTstgiJ+oroTfJamy+P5ClkDqvVTosW+EHz9ZaJ8xlXHOcj9do2LPey9I
+Rp250azmF+pQS5x9JKQKgv/FtN8HBVUtigbhCb14GUoODICMCfWFLmnumoMefnTR
+iV+3BLn6Dqp5vZxx+NuIffZ5/Or5JsDhALSGVomC8QKBgAi3Z/dNQrDHfkXMNn/L
++EAoLuAbFgLs76r9VGgNaRQ/q5gex2bZEGoBj4Sxvs95NUIcfD9wKT7FF8HdxARv
+y3o6Bfc8Xp9So9SlFXrje+gkdEJ0rQR67d+XBuJZh86bXJHVrMwpoNL+ahLGdVSe
+81oh1uCH1YPLM29hPyaohxL8
+-----END PRIVATE KEY-----
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/localhost_swordfish.pem b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/localhost_swordfish.pem
new file mode 100644
index 0000000..7d1ee80
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/examples/localhost_swordfish.pem
@@ -0,0 +1,51 @@
+-----BEGIN CERTIFICATE-----
+MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBZMRIwEAYDVQQDDAkxMjcu
+MC4wLjExCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQK
+DBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTYxMjMwMTQxMDQzWhcNMjYx
+MjI4MTQxMDQzWjBZMRIwEAYDVQQDDAkxMjcuMC4wLjExCzAJBgNVBAYTAkFVMRMw
+EQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0
+eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRXt83SrKIHr/i
+3lc8O8pz6NHE1DNHJa4xg2xalXWzCEV6m1qLd9VdaLT9cJD1afNmEMBgY6RblNL/
+paJWVoR9MOUeIoYl2PrhUCxsf7h6MRtezQQe3e+n+/0XunF0JUQIZuJqbxfRk5WT
+XmYnphqOZKEcistAYvFBjzl/D+Cl/nYsreADc+t9l5Vni89oTWEuqIrsM4WUZqqB
+VMAakd2nZJLWIrMxq9hbW1XNukOQfcmZVFTC6CUnLq8qzGbtfZYBuMBACnL1k/E/
+yPaAgR46l14VAcndDUJBtMeL2qYuNwvXQhg3KuBmpTUpH+yzxU+4T3lmv0xXmPqu
+ySH3xvW3AgMBAAGjUDBOMB0GA1UdDgQWBBRu68WTI4pVeTB7wuG9QGI3Ie441TAf
+BgNVHSMEGDAWgBRu68WTI4pVeTB7wuG9QGI3Ie441TAMBgNVHRMEBTADAQH/MA0G
+CSqGSIb3DQEBBQUAA4IBAQCc4pEjEHO47VRJkbHgC+c2gAVgxekkaA1czBA1uAvh
+ILRda0NLlvyftbjaG0zZp2ABUCfRfksl/Pf/PzWLUMEuH/9kEW2rgP43z6YgiL6k
+kBPlmAU607UjD726RPGkw8QPSXS/dWiNJ5CBpPWLpxC45pokqItYbY0ijQ5Piq09
+TchYlCX044oSRnPiP394PQ3HVdaGhJB2DnjDq3in5dVivFf8EdgzQSvp/wXy3WQs
+uFSVonSnrZGY/4AgT3psGaQ6fqKb4SBoqtf5bFQvp1XNNRkuEJnS/0dygEya0c+c
+aCe/1gXC2wDjx0/TekY5m1Nyw5SY6z7stOqL/ekwgejt
+-----END CERTIFICATE-----
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIG7idPRLgiHkCAggA
+MBQGCCqGSIb3DQMHBAg+MLPdepHWSwSCBMgVW9LseCjfTAmF9U1qRnKsq3kIwEnW
+6aERBqs/mnmEhrXgZYgcvRRK7kD12TdHt/Nz46Ymu0h+Lrvuwtl1fHQUARTk/gFh
+onLhc9kjMUhLRIR007vJe3HvWOb/v+SBSDB38OpUxUwJmBVBuSaYLWVuPR6J5kUj
+xOgBS049lN3E9cfrHvb3bF/epIQrU0OgfyyxEvIi5n30y+tlRn3y68PY6Qd46t4Y
+UN5VZUwvJBgoRy9TGxSkiSRjhxC2PWpLYq/HMzDbcRcFF5dVAIioUd/VZ7fdgBfA
+uMW4SFfpFLDUX0aaYe+ZdA5tM0Bc0cOtG8Z0sc9JYDNNcvjmSiGCi646h8F0D3O6
+JKAQMMxQGWiyQeJ979LVjtq4lJESXA8VEKz9rV03y5xunmFCLy6dGt+6GJwXgabn
+OH7nvEv4GqAOqKc6E9je4JM+AF/oUazrfPse1KEEtsPKarazjCB/SKYtHyDJaavD
+GGjtiU9zWwGMOgIDyNmXe3ga7/TWoGOAg5YlTr6Hbq2Y/5ycgjAgPFjuXtvnoT0+
+mF5TnNfMAqTgQsE2gjhonK1pdlOen0lN5FtoUXp3CXU0dOq0J70GiX+1YA7VDn30
+n5WNAgfOXX3l3E95jGN370pHXyli5RUNW0NZVHV+22jlNWCVtQHUh+DVswQZg+i5
++DqaIHz2jUetMo7gWtqGn/wwSopOs87VM1rcALhZL4EsJ+Zy81I/hA32RNnGbuol
+NAiZh+0KrtTcc/fPunpd8vRtOwGphM11dKucozUufuiPG2inR3aEqt5yNx54ec/f
+J6nryWRYiHEA/rCU9MSBM9cqKFtEmy9/8oxV41/SPxhXjHwDlABWTtFuJ3pf2sOF
+ILSYYFwB0ZGvdjE5yAJFBr9efno/L9fafmGk7a3vmVgK2AmUC9VNB5XHw1GjF8OP
+aQAXe4md9Bh0jk/D/iyp7e7IWNssul/7XejhabidWgFj6EXc9YxE59+FlhDqyMhn
+V6houc+QeUXuwsAKgRJJhJtpv/QSZ5BI3esxHHUt3ayGnvhFElpAc0t7C/EiXKIv
+DAFYP2jksBqijM8YtEgPWYzEP5buYxZnf/LK7FDocLsNcdF38UaKBbeF90e7bR8j
+SHspG9aJWICu8Yawnh8zuy/vQv+h9gWyGodd2p9lQzlbRXrutbwfmPf7xP6nzT9i
+9GcugJxTaZgkCfhhHxFk/nRHS2NAzagKVib1xkUlZJg2hX0fIFUdYteL1GGTvOx5
+m3mTOino4T19z9SEdZYb2OHYh29e/T74bJiLCYdXwevSYHxfZc8pYAf0jp4UnMT2
+f7B0ctX1iXuQ2uZVuxh+U1Mcu+v0gDla1jWh7AhcePSi4xBNUCak0kQip6r5e6Oi
+r4MIyMRk/Pc5pzEKo8G6nk26rNvX3aRvECoVfmK7IVdsqZ6IXlt9kOmWx3IeKzrO
+J5DxpzW+9oIRZJgPTkc4/XRb0tFmFQYTiChiQ1AJUEiCX0GpkFf7cq61aLGYtWyn
+vL2lmQhljzjrDo15hKErvk7eBZW7GW/6j/m/PfRdcBI4ceuP9zWQXnDOd9zmaE4b
+q3bJ+IbbyVZA2WwyzN7umCKWghsiPMAolxEnYM9JRf8BcqeqQiwVZlfO5KFuN6Ze
+le4=
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/phpunit.xml.dist b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/phpunit.xml.dist
new file mode 100644
index 0000000..13d3fab
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/phpunit.xml.dist
@@ -0,0 +1,25 @@
+
+
+
+
+
+ ./tests/
+
+
+
+
+
+ ./src/
+
+
+
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/Connection.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/Connection.php
new file mode 100644
index 0000000..c6267cc
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/Connection.php
@@ -0,0 +1,178 @@
+= 70100 && PHP_VERSION_ID < 70104));
+
+ $this->input = new DuplexResourceStream(
+ $resource,
+ $loop,
+ $clearCompleteBuffer ? -1 : null,
+ new WritableResourceStream($resource, $loop, null, $limitWriteChunks ? 8192 : null)
+ );
+
+ $this->stream = $resource;
+
+ Util::forwardEvents($this->input, $this, array('data', 'end', 'error', 'close', 'pipe', 'drain'));
+
+ $this->input->on('close', array($this, 'close'));
+ }
+
+ public function isReadable()
+ {
+ return $this->input->isReadable();
+ }
+
+ public function isWritable()
+ {
+ return $this->input->isWritable();
+ }
+
+ public function pause()
+ {
+ $this->input->pause();
+ }
+
+ public function resume()
+ {
+ $this->input->resume();
+ }
+
+ public function pipe(WritableStreamInterface $dest, array $options = array())
+ {
+ return $this->input->pipe($dest, $options);
+ }
+
+ public function write($data)
+ {
+ return $this->input->write($data);
+ }
+
+ public function end($data = null)
+ {
+ $this->input->end($data);
+ }
+
+ public function close()
+ {
+ $this->input->close();
+ $this->handleClose();
+ $this->removeAllListeners();
+ }
+
+ public function handleClose()
+ {
+ if (!is_resource($this->stream)) {
+ return;
+ }
+
+ // Try to cleanly shut down socket and ignore any errors in case other
+ // side already closed. Shutting down may return to blocking mode on
+ // some legacy versions, so reset to non-blocking just in case before
+ // continuing to close the socket resource.
+ // Underlying Stream implementation will take care of closing file
+ // handle, so we otherwise keep this open here.
+ @stream_socket_shutdown($this->stream, STREAM_SHUT_RDWR);
+ stream_set_blocking($this->stream, false);
+ }
+
+ public function getRemoteAddress()
+ {
+ return $this->parseAddress(@stream_socket_get_name($this->stream, true));
+ }
+
+ public function getLocalAddress()
+ {
+ return $this->parseAddress(@stream_socket_get_name($this->stream, false));
+ }
+
+ private function parseAddress($address)
+ {
+ if ($address === false) {
+ return null;
+ }
+
+ if ($this->unix) {
+ // remove trailing colon from address for HHVM < 3.19: https://3v4l.org/5C1lo
+ // note that technically ":" is a valid address, so keep this in place otherwise
+ if (substr($address, -1) === ':' && defined('HHVM_VERSION_ID') && HHVM_VERSION_ID < 31900) {
+ $address = (string)substr($address, 0, -1);
+ }
+
+ // work around unknown addresses should return null value: https://3v4l.org/5C1lo and https://bugs.php.net/bug.php?id=74556
+ // PHP uses "\0" string and HHVM uses empty string (colon removed above)
+ if ($address === '' || $address[0] === "\x00" ) {
+ return null;
+ }
+
+ return 'unix://' . $address;
+ }
+
+ // check if this is an IPv6 address which includes multiple colons but no square brackets
+ $pos = strrpos($address, ':');
+ if ($pos !== false && strpos($address, ':') < $pos && substr($address, 0, 1) !== '[') {
+ $port = substr($address, $pos + 1);
+ $address = '[' . substr($address, 0, $pos) . ']:' . $port;
+ }
+
+ return ($this->encryptionEnabled ? 'tls' : 'tcp') . '://' . $address;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/ConnectionInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/ConnectionInterface.php
new file mode 100644
index 0000000..64613b5
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/ConnectionInterface.php
@@ -0,0 +1,119 @@
+on('data', function ($chunk) {
+ * echo $chunk;
+ * });
+ *
+ * $connection->on('end', function () {
+ * echo 'ended';
+ * });
+ *
+ * $connection->on('error', function (Exception $e) {
+ * echo 'error: ' . $e->getMessage();
+ * });
+ *
+ * $connection->on('close', function () {
+ * echo 'closed';
+ * });
+ *
+ * $connection->write($data);
+ * $connection->end($data = null);
+ * $connection->close();
+ * // …
+ * ```
+ *
+ * For more details, see the
+ * [`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface).
+ *
+ * @see DuplexStreamInterface
+ * @see ServerInterface
+ * @see ConnectorInterface
+ */
+interface ConnectionInterface extends DuplexStreamInterface
+{
+ /**
+ * Returns the full remote address (URI) where this connection has been established with
+ *
+ * ```php
+ * $address = $connection->getRemoteAddress();
+ * echo 'Connection with ' . $address . PHP_EOL;
+ * ```
+ *
+ * If the remote address can not be determined or is unknown at this time (such as
+ * after the connection has been closed), it MAY return a `NULL` value instead.
+ *
+ * Otherwise, it will return the full address (URI) as a string value, such
+ * as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`,
+ * `unix://example.sock` or `unix:///path/to/example.sock`.
+ * Note that individual URI components are application specific and depend
+ * on the underlying transport protocol.
+ *
+ * If this is a TCP/IP based connection and you only want the remote IP, you may
+ * use something like this:
+ *
+ * ```php
+ * $address = $connection->getRemoteAddress();
+ * $ip = trim(parse_url($address, PHP_URL_HOST), '[]');
+ * echo 'Connection with ' . $ip . PHP_EOL;
+ * ```
+ *
+ * @return ?string remote address (URI) or null if unknown
+ */
+ public function getRemoteAddress();
+
+ /**
+ * Returns the full local address (full URI with scheme, IP and port) where this connection has been established with
+ *
+ * ```php
+ * $address = $connection->getLocalAddress();
+ * echo 'Connection with ' . $address . PHP_EOL;
+ * ```
+ *
+ * If the local address can not be determined or is unknown at this time (such as
+ * after the connection has been closed), it MAY return a `NULL` value instead.
+ *
+ * Otherwise, it will return the full address (URI) as a string value, such
+ * as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`,
+ * `unix://example.sock` or `unix:///path/to/example.sock`.
+ * Note that individual URI components are application specific and depend
+ * on the underlying transport protocol.
+ *
+ * This method complements the [`getRemoteAddress()`](#getremoteaddress) method,
+ * so they should not be confused.
+ *
+ * If your `TcpServer` instance is listening on multiple interfaces (e.g. using
+ * the address `0.0.0.0`), you can use this method to find out which interface
+ * actually accepted this connection (such as a public or local interface).
+ *
+ * If your system has multiple interfaces (e.g. a WAN and a LAN interface),
+ * you can use this method to find out which interface was actually
+ * used for this connection.
+ *
+ * @return ?string local address (URI) or null if unknown
+ * @see self::getRemoteAddress()
+ */
+ public function getLocalAddress();
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/Connector.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/Connector.php
new file mode 100644
index 0000000..75276bc
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/Connector.php
@@ -0,0 +1,136 @@
+ true,
+ 'tls' => true,
+ 'unix' => true,
+
+ 'dns' => true,
+ 'timeout' => true,
+ );
+
+ if ($options['timeout'] === true) {
+ $options['timeout'] = (float)ini_get("default_socket_timeout");
+ }
+
+ if ($options['tcp'] instanceof ConnectorInterface) {
+ $tcp = $options['tcp'];
+ } else {
+ $tcp = new TcpConnector(
+ $loop,
+ is_array($options['tcp']) ? $options['tcp'] : array()
+ );
+ }
+
+ if ($options['dns'] !== false) {
+ if ($options['dns'] instanceof Resolver) {
+ $resolver = $options['dns'];
+ } else {
+ if ($options['dns'] !== true) {
+ $server = $options['dns'];
+ } else {
+ // try to load nameservers from system config or default to Google's public DNS
+ $config = Config::loadSystemConfigBlocking();
+ $server = $config->nameservers ? reset($config->nameservers) : '8.8.8.8';
+ }
+
+ $factory = new Factory();
+ $resolver = $factory->create(
+ $server,
+ $loop
+ );
+ }
+
+ $tcp = new DnsConnector($tcp, $resolver);
+ }
+
+ if ($options['tcp'] !== false) {
+ $options['tcp'] = $tcp;
+
+ if ($options['timeout'] !== false) {
+ $options['tcp'] = new TimeoutConnector(
+ $options['tcp'],
+ $options['timeout'],
+ $loop
+ );
+ }
+
+ $this->connectors['tcp'] = $options['tcp'];
+ }
+
+ if ($options['tls'] !== false) {
+ if (!$options['tls'] instanceof ConnectorInterface) {
+ $options['tls'] = new SecureConnector(
+ $tcp,
+ $loop,
+ is_array($options['tls']) ? $options['tls'] : array()
+ );
+ }
+
+ if ($options['timeout'] !== false) {
+ $options['tls'] = new TimeoutConnector(
+ $options['tls'],
+ $options['timeout'],
+ $loop
+ );
+ }
+
+ $this->connectors['tls'] = $options['tls'];
+ }
+
+ if ($options['unix'] !== false) {
+ if (!$options['unix'] instanceof ConnectorInterface) {
+ $options['unix'] = new UnixConnector($loop);
+ }
+ $this->connectors['unix'] = $options['unix'];
+ }
+ }
+
+ public function connect($uri)
+ {
+ $scheme = 'tcp';
+ if (strpos($uri, '://') !== false) {
+ $scheme = (string)substr($uri, 0, strpos($uri, '://'));
+ }
+
+ if (!isset($this->connectors[$scheme])) {
+ return Promise\reject(new RuntimeException(
+ 'No connector available for URI scheme "' . $scheme . '"'
+ ));
+ }
+
+ return $this->connectors[$scheme]->connect($uri);
+ }
+}
+
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/ConnectorInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/ConnectorInterface.php
new file mode 100644
index 0000000..196d01a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/ConnectorInterface.php
@@ -0,0 +1,58 @@
+connect('google.com:443')->then(
+ * function (ConnectionInterface $connection) {
+ * // connection successfully established
+ * },
+ * function (Exception $error) {
+ * // failed to connect due to $error
+ * }
+ * );
+ * ```
+ *
+ * The returned Promise MUST be implemented in such a way that it can be
+ * cancelled when it is still pending. Cancelling a pending promise MUST
+ * reject its value with an Exception. It SHOULD clean up any underlying
+ * resources and references as applicable.
+ *
+ * ```php
+ * $promise = $connector->connect($uri);
+ *
+ * $promise->cancel();
+ * ```
+ *
+ * @param string $uri
+ * @return \React\Promise\PromiseInterface resolves with a stream implementing ConnectionInterface on success or rejects with an Exception on error
+ * @see ConnectionInterface
+ */
+ public function connect($uri);
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/DnsConnector.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/DnsConnector.php
new file mode 100644
index 0000000..90170e5
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/DnsConnector.php
@@ -0,0 +1,111 @@
+connector = $connector;
+ $this->resolver = $resolver;
+ }
+
+ public function connect($uri)
+ {
+ if (strpos($uri, '://') === false) {
+ $parts = parse_url('tcp://' . $uri);
+ unset($parts['scheme']);
+ } else {
+ $parts = parse_url($uri);
+ }
+
+ if (!$parts || !isset($parts['host'])) {
+ return Promise\reject(new InvalidArgumentException('Given URI "' . $uri . '" is invalid'));
+ }
+
+ $host = trim($parts['host'], '[]');
+ $connector = $this->connector;
+
+ return $this
+ ->resolveHostname($host)
+ ->then(function ($ip) use ($connector, $host, $parts) {
+ $uri = '';
+
+ // prepend original scheme if known
+ if (isset($parts['scheme'])) {
+ $uri .= $parts['scheme'] . '://';
+ }
+
+ if (strpos($ip, ':') !== false) {
+ // enclose IPv6 addresses in square brackets before appending port
+ $uri .= '[' . $ip . ']';
+ } else {
+ $uri .= $ip;
+ }
+
+ // append original port if known
+ if (isset($parts['port'])) {
+ $uri .= ':' . $parts['port'];
+ }
+
+ // append orignal path if known
+ if (isset($parts['path'])) {
+ $uri .= $parts['path'];
+ }
+
+ // append original query if known
+ if (isset($parts['query'])) {
+ $uri .= '?' . $parts['query'];
+ }
+
+ // append original hostname as query if resolved via DNS and if
+ // destination URI does not contain "hostname" query param already
+ $args = array();
+ parse_str(isset($parts['query']) ? $parts['query'] : '', $args);
+ if ($host !== $ip && !isset($args['hostname'])) {
+ $uri .= (isset($parts['query']) ? '&' : '?') . 'hostname=' . rawurlencode($host);
+ }
+
+ // append original fragment if known
+ if (isset($parts['fragment'])) {
+ $uri .= '#' . $parts['fragment'];
+ }
+
+ return $connector->connect($uri);
+ });
+ }
+
+ private function resolveHostname($host)
+ {
+ if (false !== filter_var($host, FILTER_VALIDATE_IP)) {
+ return Promise\resolve($host);
+ }
+
+ $promise = $this->resolver->resolve($host);
+
+ return new Promise\Promise(
+ function ($resolve, $reject) use ($promise) {
+ // resolve/reject with result of DNS lookup
+ $promise->then($resolve, $reject);
+ },
+ function ($_, $reject) use ($promise) {
+ // cancellation should reject connection attempt
+ $reject(new RuntimeException('Connection attempt cancelled during DNS lookup'));
+
+ // (try to) cancel pending DNS lookup
+ if ($promise instanceof CancellablePromiseInterface) {
+ $promise->cancel();
+ }
+ }
+ );
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/FixedUriConnector.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/FixedUriConnector.php
new file mode 100644
index 0000000..057bcdf
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/FixedUriConnector.php
@@ -0,0 +1,41 @@
+connect('localhost:80');
+ * ```
+ */
+class FixedUriConnector implements ConnectorInterface
+{
+ private $uri;
+ private $connector;
+
+ /**
+ * @param string $uri
+ * @param ConnectorInterface $connector
+ */
+ public function __construct($uri, ConnectorInterface $connector)
+ {
+ $this->uri = $uri;
+ $this->connector = $connector;
+ }
+
+ public function connect($_)
+ {
+ return $this->connector->connect($this->uri);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/LimitingServer.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/LimitingServer.php
new file mode 100644
index 0000000..c7874ee
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/LimitingServer.php
@@ -0,0 +1,203 @@
+on('connection', function (ConnectionInterface $connection) {
+ * $connection->write('hello there!' . PHP_EOL);
+ * …
+ * });
+ * ```
+ *
+ * See also the `ServerInterface` for more details.
+ *
+ * @see ServerInterface
+ * @see ConnectionInterface
+ */
+class LimitingServer extends EventEmitter implements ServerInterface
+{
+ private $connections = array();
+ private $server;
+ private $limit;
+
+ private $pauseOnLimit = false;
+ private $autoPaused = false;
+ private $manuPaused = false;
+
+ /**
+ * Instantiates a new LimitingServer.
+ *
+ * You have to pass a maximum number of open connections to ensure
+ * the server will automatically reject (close) connections once this limit
+ * is exceeded. In this case, it will emit an `error` event to inform about
+ * this and no `connection` event will be emitted.
+ *
+ * ```php
+ * $server = new LimitingServer($server, 100);
+ * $server->on('connection', function (ConnectionInterface $connection) {
+ * $connection->write('hello there!' . PHP_EOL);
+ * …
+ * });
+ * ```
+ *
+ * You MAY pass a `null` limit in order to put no limit on the number of
+ * open connections and keep accepting new connection until you run out of
+ * operating system resources (such as open file handles). This may be
+ * useful if you do not want to take care of applying a limit but still want
+ * to use the `getConnections()` method.
+ *
+ * You can optionally configure the server to pause accepting new
+ * connections once the connection limit is reached. In this case, it will
+ * pause the underlying server and no longer process any new connections at
+ * all, thus also no longer closing any excessive connections.
+ * The underlying operating system is responsible for keeping a backlog of
+ * pending connections until its limit is reached, at which point it will
+ * start rejecting further connections.
+ * Once the server is below the connection limit, it will continue consuming
+ * connections from the backlog and will process any outstanding data on
+ * each connection.
+ * This mode may be useful for some protocols that are designed to wait for
+ * a response message (such as HTTP), but may be less useful for other
+ * protocols that demand immediate responses (such as a "welcome" message in
+ * an interactive chat).
+ *
+ * ```php
+ * $server = new LimitingServer($server, 100, true);
+ * $server->on('connection', function (ConnectionInterface $connection) {
+ * $connection->write('hello there!' . PHP_EOL);
+ * …
+ * });
+ * ```
+ *
+ * @param ServerInterface $server
+ * @param int|null $connectionLimit
+ * @param bool $pauseOnLimit
+ */
+ public function __construct(ServerInterface $server, $connectionLimit, $pauseOnLimit = false)
+ {
+ $this->server = $server;
+ $this->limit = $connectionLimit;
+ if ($connectionLimit !== null) {
+ $this->pauseOnLimit = $pauseOnLimit;
+ }
+
+ $this->server->on('connection', array($this, 'handleConnection'));
+ $this->server->on('error', array($this, 'handleError'));
+ }
+
+ /**
+ * Returns an array with all currently active connections
+ *
+ * ```php
+ * foreach ($server->getConnection() as $connection) {
+ * $connection->write('Hi!');
+ * }
+ * ```
+ *
+ * @return ConnectionInterface[]
+ */
+ public function getConnections()
+ {
+ return $this->connections;
+ }
+
+ public function getAddress()
+ {
+ return $this->server->getAddress();
+ }
+
+ public function pause()
+ {
+ if (!$this->manuPaused) {
+ $this->manuPaused = true;
+
+ if (!$this->autoPaused) {
+ $this->server->pause();
+ }
+ }
+ }
+
+ public function resume()
+ {
+ if ($this->manuPaused) {
+ $this->manuPaused = false;
+
+ if (!$this->autoPaused) {
+ $this->server->resume();
+ }
+ }
+ }
+
+ public function close()
+ {
+ $this->server->close();
+ }
+
+ /** @internal */
+ public function handleConnection(ConnectionInterface $connection)
+ {
+ // close connection if limit exceeded
+ if ($this->limit !== null && count($this->connections) >= $this->limit) {
+ $this->handleError(new OverflowException('Connection closed because server reached connection limit'));
+ $connection->close();
+ return;
+ }
+
+ $this->connections[] = $connection;
+ $that = $this;
+ $connection->on('close', function () use ($that, $connection) {
+ $that->handleDisconnection($connection);
+ });
+
+ // pause accepting new connections if limit exceeded
+ if ($this->pauseOnLimit && !$this->autoPaused && count($this->connections) >= $this->limit) {
+ $this->autoPaused = true;
+
+ if (!$this->manuPaused) {
+ $this->server->pause();
+ }
+ }
+
+ $this->emit('connection', array($connection));
+ }
+
+ /** @internal */
+ public function handleDisconnection(ConnectionInterface $connection)
+ {
+ unset($this->connections[array_search($connection, $this->connections)]);
+
+ // continue accepting new connection if below limit
+ if ($this->autoPaused && count($this->connections) < $this->limit) {
+ $this->autoPaused = false;
+
+ if (!$this->manuPaused) {
+ $this->server->resume();
+ }
+ }
+ }
+
+ /** @internal */
+ public function handleError(Exception $error)
+ {
+ $this->emit('error', array($error));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/SecureConnector.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/SecureConnector.php
new file mode 100644
index 0000000..f04183d
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/SecureConnector.php
@@ -0,0 +1,64 @@
+connector = $connector;
+ $this->streamEncryption = new StreamEncryption($loop, false);
+ $this->context = $context;
+ }
+
+ public function connect($uri)
+ {
+ if (!function_exists('stream_socket_enable_crypto')) {
+ return Promise\reject(new BadMethodCallException('Encryption not supported on your platform (HHVM < 3.8?)')); // @codeCoverageIgnore
+ }
+
+ if (strpos($uri, '://') === false) {
+ $uri = 'tls://' . $uri;
+ }
+
+ $parts = parse_url($uri);
+ if (!$parts || !isset($parts['scheme']) || $parts['scheme'] !== 'tls') {
+ return Promise\reject(new InvalidArgumentException('Given URI "' . $uri . '" is invalid'));
+ }
+
+ $uri = str_replace('tls://', '', $uri);
+ $context = $this->context;
+
+ $encryption = $this->streamEncryption;
+ return $this->connector->connect($uri)->then(function (ConnectionInterface $connection) use ($context, $encryption) {
+ // (unencrypted) TCP/IP connection succeeded
+
+ if (!$connection instanceof Connection) {
+ $connection->close();
+ throw new UnexpectedValueException('Base connector does not use internal Connection class exposing stream resource');
+ }
+
+ // set required SSL/TLS context options
+ foreach ($context as $name => $value) {
+ stream_context_set_option($connection->stream, 'ssl', $name, $value);
+ }
+
+ // try to enable encryption
+ return $encryption->enable($connection)->then(null, function ($error) use ($connection) {
+ // establishing encryption failed => close invalid connection and return error
+ $connection->close();
+ throw $error;
+ });
+ });
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/SecureServer.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/SecureServer.php
new file mode 100644
index 0000000..302ae93
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/SecureServer.php
@@ -0,0 +1,192 @@
+on('connection', function (ConnectionInterface $connection) {
+ * echo 'Secure connection from' . $connection->getRemoteAddress() . PHP_EOL;
+ *
+ * $connection->write('hello there!' . PHP_EOL);
+ * …
+ * });
+ * ```
+ *
+ * Whenever a client fails to perform a successful TLS handshake, it will emit an
+ * `error` event and then close the underlying TCP/IP connection:
+ *
+ * ```php
+ * $server->on('error', function (Exception $e) {
+ * echo 'Error' . $e->getMessage() . PHP_EOL;
+ * });
+ * ```
+ *
+ * See also the `ServerInterface` for more details.
+ *
+ * Note that the `SecureServer` class is a concrete implementation for TLS sockets.
+ * If you want to typehint in your higher-level protocol implementation, you SHOULD
+ * use the generic `ServerInterface` instead.
+ *
+ * @see ServerInterface
+ * @see ConnectionInterface
+ */
+final class SecureServer extends EventEmitter implements ServerInterface
+{
+ private $tcp;
+ private $encryption;
+ private $context;
+
+ /**
+ * Creates a secure TLS server and starts waiting for incoming connections
+ *
+ * It does so by wrapping a `TcpServer` instance which waits for plaintext
+ * TCP/IP connections and then performs a TLS handshake for each connection.
+ * It thus requires valid [TLS context options],
+ * which in its most basic form may look something like this if you're using a
+ * PEM encoded certificate file:
+ *
+ * ```php
+ * $server = new TcpServer(8000, $loop);
+ * $server = new SecureServer($server, $loop, array(
+ * 'local_cert' => 'server.pem'
+ * ));
+ * ```
+ *
+ * Note that the certificate file will not be loaded on instantiation but when an
+ * incoming connection initializes its TLS context.
+ * This implies that any invalid certificate file paths or contents will only cause
+ * an `error` event at a later time.
+ *
+ * If your private key is encrypted with a passphrase, you have to specify it
+ * like this:
+ *
+ * ```php
+ * $server = new TcpServer(8000, $loop);
+ * $server = new SecureServer($server, $loop, array(
+ * 'local_cert' => 'server.pem',
+ * 'passphrase' => 'secret'
+ * ));
+ * ```
+ *
+ * Note that available [TLS context options],
+ * their defaults and effects of changing these may vary depending on your system
+ * and/or PHP version.
+ * Passing unknown context options has no effect.
+ *
+ * Advanced usage: Despite allowing any `ServerInterface` as first parameter,
+ * you SHOULD pass a `TcpServer` instance as first parameter, unless you
+ * know what you're doing.
+ * Internally, the `SecureServer` has to set the required TLS context options on
+ * the underlying stream resources.
+ * These resources are not exposed through any of the interfaces defined in this
+ * package, but only through the internal `Connection` class.
+ * The `TcpServer` class is guaranteed to emit connections that implement
+ * the `ConnectionInterface` and uses the internal `Connection` class in order to
+ * expose these underlying resources.
+ * If you use a custom `ServerInterface` and its `connection` event does not
+ * meet this requirement, the `SecureServer` will emit an `error` event and
+ * then close the underlying connection.
+ *
+ * @param ServerInterface|TcpServer $tcp
+ * @param LoopInterface $loop
+ * @param array $context
+ * @throws BadMethodCallException for legacy HHVM < 3.8 due to lack of support
+ * @see TcpServer
+ * @link http://php.net/manual/en/context.ssl.php for TLS context options
+ */
+ public function __construct(ServerInterface $tcp, LoopInterface $loop, array $context)
+ {
+ if (!function_exists('stream_socket_enable_crypto')) {
+ throw new BadMethodCallException('Encryption not supported on your platform (HHVM < 3.8?)'); // @codeCoverageIgnore
+ }
+
+ // default to empty passphrase to suppress blocking passphrase prompt
+ $context += array(
+ 'passphrase' => ''
+ );
+
+ $this->tcp = $tcp;
+ $this->encryption = new StreamEncryption($loop);
+ $this->context = $context;
+
+ $that = $this;
+ $this->tcp->on('connection', function ($connection) use ($that) {
+ $that->handleConnection($connection);
+ });
+ $this->tcp->on('error', function ($error) use ($that) {
+ $that->emit('error', array($error));
+ });
+ }
+
+ public function getAddress()
+ {
+ $address = $this->tcp->getAddress();
+ if ($address === null) {
+ return null;
+ }
+
+ return str_replace('tcp://' , 'tls://', $address);
+ }
+
+ public function pause()
+ {
+ $this->tcp->pause();
+ }
+
+ public function resume()
+ {
+ $this->tcp->resume();
+ }
+
+ public function close()
+ {
+ return $this->tcp->close();
+ }
+
+ /** @internal */
+ public function handleConnection(ConnectionInterface $connection)
+ {
+ if (!$connection instanceof Connection) {
+ $this->emit('error', array(new UnexpectedValueException('Base server does not use internal Connection class exposing stream resource')));
+ $connection->end();
+ return;
+ }
+
+ foreach ($this->context as $name => $value) {
+ stream_context_set_option($connection->stream, 'ssl', $name, $value);
+ }
+
+ $that = $this;
+
+ $this->encryption->enable($connection)->then(
+ function ($conn) use ($that) {
+ $that->emit('connection', array($conn));
+ },
+ function ($error) use ($that, $connection) {
+ $that->emit('error', array($error));
+ $connection->end();
+ }
+ );
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/Server.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/Server.php
new file mode 100644
index 0000000..72712e4
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/Server.php
@@ -0,0 +1,73 @@
+ $context);
+ }
+
+ // apply default options if not explicitly given
+ $context += array(
+ 'tcp' => array(),
+ 'tls' => array(),
+ 'unix' => array()
+ );
+
+ $scheme = 'tcp';
+ $pos = strpos($uri, '://');
+ if ($pos !== false) {
+ $scheme = substr($uri, 0, $pos);
+ }
+
+ if ($scheme === 'unix') {
+ $server = new UnixServer($uri, $loop, $context['unix']);
+ } else {
+ $server = new TcpServer(str_replace('tls://', '', $uri), $loop, $context['tcp']);
+
+ if ($scheme === 'tls') {
+ $server = new SecureServer($server, $loop, $context['tls']);
+ }
+ }
+
+ $this->server = $server;
+
+ $that = $this;
+ $server->on('connection', function (ConnectionInterface $conn) use ($that) {
+ $that->emit('connection', array($conn));
+ });
+ $server->on('error', function (Exception $error) use ($that) {
+ $that->emit('error', array($error));
+ });
+ }
+
+ public function getAddress()
+ {
+ return $this->server->getAddress();
+ }
+
+ public function pause()
+ {
+ $this->server->pause();
+ }
+
+ public function resume()
+ {
+ $this->server->resume();
+ }
+
+ public function close()
+ {
+ $this->server->close();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/ServerInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/ServerInterface.php
new file mode 100644
index 0000000..5319678
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/ServerInterface.php
@@ -0,0 +1,151 @@
+on('connection', function (ConnectionInterface $connection) {
+ * echo 'new connection' . PHP_EOL;
+ * });
+ * ```
+ *
+ * See also the `ConnectionInterface` for more details about handling the
+ * incoming connection.
+ *
+ * error event:
+ * The `error` event will be emitted whenever there's an error accepting a new
+ * connection from a client.
+ *
+ * ```php
+ * $server->on('error', function (Exception $e) {
+ * echo 'error: ' . $e->getMessage() . PHP_EOL;
+ * });
+ * ```
+ *
+ * Note that this is not a fatal error event, i.e. the server keeps listening for
+ * new connections even after this event.
+ *
+ * @see ConnectionInterface
+ */
+interface ServerInterface extends EventEmitterInterface
+{
+ /**
+ * Returns the full address (URI) this server is currently listening on
+ *
+ * ```php
+ * $address = $server->getAddress();
+ * echo 'Server listening on ' . $address . PHP_EOL;
+ * ```
+ *
+ * If the address can not be determined or is unknown at this time (such as
+ * after the socket has been closed), it MAY return a `NULL` value instead.
+ *
+ * Otherwise, it will return the full address (URI) as a string value, such
+ * as `tcp://127.0.0.1:8080`, `tcp://[::1]:80` or `tls://127.0.0.1:443`.
+ * Note that individual URI components are application specific and depend
+ * on the underlying transport protocol.
+ *
+ * If this is a TCP/IP based server and you only want the local port, you may
+ * use something like this:
+ *
+ * ```php
+ * $address = $server->getAddress();
+ * $port = parse_url($address, PHP_URL_PORT);
+ * echo 'Server listening on port ' . $port . PHP_EOL;
+ * ```
+ *
+ * @return ?string the full listening address (URI) or NULL if it is unknown (not applicable to this server socket or already closed)
+ */
+ public function getAddress();
+
+ /**
+ * Pauses accepting new incoming connections.
+ *
+ * Removes the socket resource from the EventLoop and thus stop accepting
+ * new connections. Note that the listening socket stays active and is not
+ * closed.
+ *
+ * This means that new incoming connections will stay pending in the
+ * operating system backlog until its configurable backlog is filled.
+ * Once the backlog is filled, the operating system may reject further
+ * incoming connections until the backlog is drained again by resuming
+ * to accept new connections.
+ *
+ * Once the server is paused, no futher `connection` events SHOULD
+ * be emitted.
+ *
+ * ```php
+ * $server->pause();
+ *
+ * $server->on('connection', assertShouldNeverCalled());
+ * ```
+ *
+ * This method is advisory-only, though generally not recommended, the
+ * server MAY continue emitting `connection` events.
+ *
+ * Unless otherwise noted, a successfully opened server SHOULD NOT start
+ * in paused state.
+ *
+ * You can continue processing events by calling `resume()` again.
+ *
+ * Note that both methods can be called any number of times, in particular
+ * calling `pause()` more than once SHOULD NOT have any effect.
+ * Similarly, calling this after `close()` is a NO-OP.
+ *
+ * @see self::resume()
+ * @return void
+ */
+ public function pause();
+
+ /**
+ * Resumes accepting new incoming connections.
+ *
+ * Re-attach the socket resource to the EventLoop after a previous `pause()`.
+ *
+ * ```php
+ * $server->pause();
+ *
+ * $loop->addTimer(1.0, function () use ($server) {
+ * $server->resume();
+ * });
+ * ```
+ *
+ * Note that both methods can be called any number of times, in particular
+ * calling `resume()` without a prior `pause()` SHOULD NOT have any effect.
+ * Similarly, calling this after `close()` is a NO-OP.
+ *
+ * @see self::pause()
+ * @return void
+ */
+ public function resume();
+
+ /**
+ * Shuts down this listening socket
+ *
+ * This will stop listening for new incoming connections on this socket.
+ *
+ * Calling this method more than once on the same instance is a NO-OP.
+ *
+ * @return void
+ */
+ public function close();
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/StreamEncryption.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/StreamEncryption.php
new file mode 100644
index 0000000..ba5d472
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/StreamEncryption.php
@@ -0,0 +1,146 @@
+loop = $loop;
+ $this->server = $server;
+
+ // support TLSv1.0+ by default and exclude legacy SSLv2/SSLv3.
+ // PHP 5.6+ supports bitmasks, legacy PHP only supports predefined
+ // constants, so apply accordingly below.
+ // Also, since PHP 5.6.7 up until before PHP 7.2.0 the main constant did
+ // only support TLSv1.0, so we explicitly apply all versions.
+ // @link http://php.net/manual/en/migration56.openssl.php#migration56.openssl.crypto-method
+ // @link https://3v4l.org/plbFn
+ if ($server) {
+ $this->method = STREAM_CRYPTO_METHOD_TLS_SERVER;
+
+ if (defined('STREAM_CRYPTO_METHOD_TLSv1_0_SERVER')) {
+ $this->method |= STREAM_CRYPTO_METHOD_TLSv1_0_SERVER;
+ }
+ if (defined('STREAM_CRYPTO_METHOD_TLSv1_1_SERVER')) {
+ $this->method |= STREAM_CRYPTO_METHOD_TLSv1_1_SERVER;
+ }
+ if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_SERVER')) {
+ $this->method |= STREAM_CRYPTO_METHOD_TLSv1_2_SERVER;
+ }
+ } else {
+ $this->method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
+
+ if (defined('STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT')) {
+ $this->method |= STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT;
+ }
+ if (defined('STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT')) {
+ $this->method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
+ }
+ if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
+ $this->method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
+ }
+ }
+ }
+
+ public function enable(Connection $stream)
+ {
+ return $this->toggle($stream, true);
+ }
+
+ public function disable(Connection $stream)
+ {
+ return $this->toggle($stream, false);
+ }
+
+ public function toggle(Connection $stream, $toggle)
+ {
+ // pause actual stream instance to continue operation on raw stream socket
+ $stream->pause();
+
+ // TODO: add write() event to make sure we're not sending any excessive data
+
+ $deferred = new Deferred(function ($_, $reject) use ($toggle) {
+ // cancelling this leaves this stream in an inconsistent state…
+ $reject(new RuntimeException('Cancelled toggling encryption ' . $toggle ? 'on' : 'off'));
+ });
+
+ // get actual stream socket from stream instance
+ $socket = $stream->stream;
+
+ // get crypto method from context options or use global setting from constructor
+ $method = $this->method;
+ $context = stream_context_get_options($socket);
+ if (isset($context['ssl']['crypto_method'])) {
+ $method = $context['ssl']['crypto_method'];
+ }
+
+ $that = $this;
+ $toggleCrypto = function () use ($socket, $deferred, $toggle, $method, $that) {
+ $that->toggleCrypto($socket, $deferred, $toggle, $method);
+ };
+
+ $this->loop->addReadStream($socket, $toggleCrypto);
+
+ if (!$this->server) {
+ $toggleCrypto();
+ }
+
+ $loop = $this->loop;
+
+ return $deferred->promise()->then(function () use ($stream, $socket, $loop, $toggle) {
+ $loop->removeReadStream($socket);
+
+ $stream->encryptionEnabled = $toggle;
+ $stream->resume();
+
+ return $stream;
+ }, function($error) use ($stream, $socket, $loop) {
+ $loop->removeReadStream($socket);
+ $stream->resume();
+ throw $error;
+ });
+ }
+
+ public function toggleCrypto($socket, Deferred $deferred, $toggle, $method)
+ {
+ set_error_handler(array($this, 'handleError'));
+ $result = stream_socket_enable_crypto($socket, $toggle, $method);
+ restore_error_handler();
+
+ if (true === $result) {
+ $deferred->resolve();
+ } else if (false === $result) {
+ $deferred->reject(new UnexpectedValueException(
+ sprintf("Unable to complete SSL/TLS handshake: %s", $this->errstr),
+ $this->errno
+ ));
+ } else {
+ // need more data, will retry
+ }
+ }
+
+ public function handleError($errno, $errstr)
+ {
+ $this->errstr = str_replace(array("\r", "\n"), ' ', $errstr);
+ $this->errno = $errno;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/TcpConnector.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/TcpConnector.php
new file mode 100644
index 0000000..90d7df1
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/TcpConnector.php
@@ -0,0 +1,122 @@
+loop = $loop;
+ $this->context = $context;
+ }
+
+ public function connect($uri)
+ {
+ if (strpos($uri, '://') === false) {
+ $uri = 'tcp://' . $uri;
+ }
+
+ $parts = parse_url($uri);
+ if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') {
+ return Promise\reject(new InvalidArgumentException('Given URI "' . $uri . '" is invalid'));
+ }
+
+ $ip = trim($parts['host'], '[]');
+ if (false === filter_var($ip, FILTER_VALIDATE_IP)) {
+ return Promise\reject(new InvalidArgumentException('Given URI "' . $ip . '" does not contain a valid host IP'));
+ }
+
+ // use context given in constructor
+ $context = array(
+ 'socket' => $this->context
+ );
+
+ // parse arguments from query component of URI
+ $args = array();
+ if (isset($parts['query'])) {
+ parse_str($parts['query'], $args);
+ }
+
+ // If an original hostname has been given, use this for TLS setup.
+ // This can happen due to layers of nested connectors, such as a
+ // DnsConnector reporting its original hostname.
+ // These context options are here in case TLS is enabled later on this stream.
+ // If TLS is not enabled later, this doesn't hurt either.
+ if (isset($args['hostname'])) {
+ $context['ssl'] = array(
+ 'SNI_enabled' => true,
+ 'peer_name' => $args['hostname']
+ );
+
+ // Legacy PHP < 5.6 ignores peer_name and requires legacy context options instead.
+ // The SNI_server_name context option has to be set here during construction,
+ // as legacy PHP ignores any values set later.
+ if (PHP_VERSION_ID < 50600) {
+ $context['ssl'] += array(
+ 'SNI_server_name' => $args['hostname'],
+ 'CN_match' => $args['hostname']
+ );
+ }
+ }
+
+ // latest versions of PHP no longer accept any other URI components and
+ // HHVM fails to parse URIs with a query but no path, so let's simplify our URI here
+ $remote = 'tcp://' . $parts['host'] . ':' . $parts['port'];
+
+ $socket = @stream_socket_client(
+ $remote,
+ $errno,
+ $errstr,
+ 0,
+ STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT,
+ stream_context_create($context)
+ );
+
+ if (false === $socket) {
+ return Promise\reject(new RuntimeException(
+ sprintf("Connection to %s failed: %s", $uri, $errstr),
+ $errno
+ ));
+ }
+
+ stream_set_blocking($socket, 0);
+
+ // wait for connection
+
+ return $this->waitForStreamOnce($socket);
+ }
+
+ private function waitForStreamOnce($stream)
+ {
+ $loop = $this->loop;
+
+ return new Promise\Promise(function ($resolve, $reject) use ($loop, $stream) {
+ $loop->addWriteStream($stream, function ($stream) use ($loop, $resolve, $reject) {
+ $loop->removeWriteStream($stream);
+
+ // The following hack looks like the only way to
+ // detect connection refused errors with PHP's stream sockets.
+ if (false === stream_socket_get_name($stream, true)) {
+ fclose($stream);
+
+ $reject(new RuntimeException('Connection refused'));
+ } else {
+ $resolve(new Connection($stream, $loop));
+ }
+ });
+ }, function () use ($loop, $stream) {
+ $loop->removeWriteStream($stream);
+ fclose($stream);
+
+ throw new RuntimeException('Cancelled while waiting for TCP/IP connection to be established');
+ });
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/TcpServer.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/TcpServer.php
new file mode 100644
index 0000000..119e177
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/TcpServer.php
@@ -0,0 +1,236 @@
+on('connection', function (ConnectionInterface $connection) {
+ * echo 'Plaintext connection from ' . $connection->getRemoteAddress() . PHP_EOL;
+ * $connection->write('hello there!' . PHP_EOL);
+ * …
+ * });
+ * ```
+ *
+ * See also the `ServerInterface` for more details.
+ *
+ * @see ServerInterface
+ * @see ConnectionInterface
+ */
+final class TcpServer extends EventEmitter implements ServerInterface
+{
+ private $master;
+ private $loop;
+ private $listening = false;
+
+ /**
+ * Creates a plaintext TCP/IP socket server and starts listening on the given address
+ *
+ * This starts accepting new incoming connections on the given address.
+ * See also the `connection event` documented in the `ServerInterface`
+ * for more details.
+ *
+ * ```php
+ * $server = new TcpServer(8080, $loop);
+ * ```
+ *
+ * As above, the `$uri` parameter can consist of only a port, in which case the
+ * server will default to listening on the localhost address `127.0.0.1`,
+ * which means it will not be reachable from outside of this system.
+ *
+ * In order to use a random port assignment, you can use the port `0`:
+ *
+ * ```php
+ * $server = new TcpServer(0, $loop);
+ * $address = $server->getAddress();
+ * ```
+ *
+ * In order to change the host the socket is listening on, you can provide an IP
+ * address through the first parameter provided to the constructor, optionally
+ * preceded by the `tcp://` scheme:
+ *
+ * ```php
+ * $server = new TcpServer('192.168.0.1:8080', $loop);
+ * ```
+ *
+ * If you want to listen on an IPv6 address, you MUST enclose the host in square
+ * brackets:
+ *
+ * ```php
+ * $server = new TcpServer('[::1]:8080', $loop);
+ * ```
+ *
+ * If the given URI is invalid, does not contain a port, any other scheme or if it
+ * contains a hostname, it will throw an `InvalidArgumentException`:
+ *
+ * ```php
+ * // throws InvalidArgumentException due to missing port
+ * $server = new TcpServer('127.0.0.1', $loop);
+ * ```
+ *
+ * If the given URI appears to be valid, but listening on it fails (such as if port
+ * is already in use or port below 1024 may require root access etc.), it will
+ * throw a `RuntimeException`:
+ *
+ * ```php
+ * $first = new TcpServer(8080, $loop);
+ *
+ * // throws RuntimeException because port is already in use
+ * $second = new TcpServer(8080, $loop);
+ * ```
+ *
+ * Note that these error conditions may vary depending on your system and/or
+ * configuration.
+ * See the exception message and code for more details about the actual error
+ * condition.
+ *
+ * Optionally, you can specify [socket context options](http://php.net/manual/en/context.socket.php)
+ * for the underlying stream socket resource like this:
+ *
+ * ```php
+ * $server = new TcpServer('[::1]:8080', $loop, array(
+ * 'backlog' => 200,
+ * 'so_reuseport' => true,
+ * 'ipv6_v6only' => true
+ * ));
+ * ```
+ *
+ * Note that available [socket context options](http://php.net/manual/en/context.socket.php),
+ * their defaults and effects of changing these may vary depending on your system
+ * and/or PHP version.
+ * Passing unknown context options has no effect.
+ *
+ * @param string|int $uri
+ * @param LoopInterface $loop
+ * @param array $context
+ * @throws InvalidArgumentException if the listening address is invalid
+ * @throws RuntimeException if listening on this address fails (already in use etc.)
+ */
+ public function __construct($uri, LoopInterface $loop, array $context = array())
+ {
+ $this->loop = $loop;
+
+ // a single port has been given => assume localhost
+ if ((string)(int)$uri === (string)$uri) {
+ $uri = '127.0.0.1:' . $uri;
+ }
+
+ // assume default scheme if none has been given
+ if (strpos($uri, '://') === false) {
+ $uri = 'tcp://' . $uri;
+ }
+
+ // parse_url() does not accept null ports (random port assignment) => manually remove
+ if (substr($uri, -2) === ':0') {
+ $parts = parse_url(substr($uri, 0, -2));
+ if ($parts) {
+ $parts['port'] = 0;
+ }
+ } else {
+ $parts = parse_url($uri);
+ }
+
+ // ensure URI contains TCP scheme, host and port
+ if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') {
+ throw new InvalidArgumentException('Invalid URI "' . $uri . '" given');
+ }
+
+ if (false === filter_var(trim($parts['host'], '[]'), FILTER_VALIDATE_IP)) {
+ throw new InvalidArgumentException('Given URI "' . $uri . '" does not contain a valid host IP');
+ }
+
+ $this->master = @stream_socket_server(
+ $uri,
+ $errno,
+ $errstr,
+ STREAM_SERVER_BIND | STREAM_SERVER_LISTEN,
+ stream_context_create(array('socket' => $context))
+ );
+ if (false === $this->master) {
+ throw new RuntimeException('Failed to listen on "' . $uri . '": ' . $errstr, $errno);
+ }
+ stream_set_blocking($this->master, 0);
+
+ $this->resume();
+ }
+
+ public function getAddress()
+ {
+ if (!is_resource($this->master)) {
+ return null;
+ }
+
+ $address = stream_socket_get_name($this->master, false);
+
+ // check if this is an IPv6 address which includes multiple colons but no square brackets
+ $pos = strrpos($address, ':');
+ if ($pos !== false && strpos($address, ':') < $pos && substr($address, 0, 1) !== '[') {
+ $port = substr($address, $pos + 1);
+ $address = '[' . substr($address, 0, $pos) . ']:' . $port;
+ }
+
+ return 'tcp://' . $address;
+ }
+
+ public function pause()
+ {
+ if (!$this->listening) {
+ return;
+ }
+
+ $this->loop->removeReadStream($this->master);
+ $this->listening = false;
+ }
+
+ public function resume()
+ {
+ if ($this->listening || !is_resource($this->master)) {
+ return;
+ }
+
+ $that = $this;
+ $this->loop->addReadStream($this->master, function ($master) use ($that) {
+ $newSocket = @stream_socket_accept($master);
+ if (false === $newSocket) {
+ $that->emit('error', array(new RuntimeException('Error accepting new connection')));
+
+ return;
+ }
+ $that->handleConnection($newSocket);
+ });
+ $this->listening = true;
+ }
+
+ public function close()
+ {
+ if (!is_resource($this->master)) {
+ return;
+ }
+
+ $this->pause();
+ fclose($this->master);
+ $this->removeAllListeners();
+ }
+
+ /** @internal */
+ public function handleConnection($socket)
+ {
+ $this->emit('connection', array(
+ new Connection($socket, $this->loop)
+ ));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/TimeoutConnector.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/TimeoutConnector.php
new file mode 100644
index 0000000..d4eba2e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/TimeoutConnector.php
@@ -0,0 +1,25 @@
+connector = $connector;
+ $this->timeout = $timeout;
+ $this->loop = $loop;
+ }
+
+ public function connect($uri)
+ {
+ return Timer\timeout($this->connector->connect($uri), $this->timeout, $this->loop);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/UnixConnector.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/UnixConnector.php
new file mode 100644
index 0000000..9b84ab0
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/UnixConnector.php
@@ -0,0 +1,44 @@
+loop = $loop;
+ }
+
+ public function connect($path)
+ {
+ if (strpos($path, '://') === false) {
+ $path = 'unix://' . $path;
+ } elseif (substr($path, 0, 7) !== 'unix://') {
+ return Promise\reject(new InvalidArgumentException('Given URI "' . $path . '" is invalid'));
+ }
+
+ $resource = @stream_socket_client($path, $errno, $errstr, 1.0);
+
+ if (!$resource) {
+ return Promise\reject(new RuntimeException('Unable to connect to unix domain socket "' . $path . '": ' . $errstr, $errno));
+ }
+
+ $connection = new Connection($resource, $this->loop);
+ $connection->unix = true;
+
+ return Promise\resolve($connection);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/UnixServer.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/UnixServer.php
new file mode 100644
index 0000000..8f1ed98
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/src/UnixServer.php
@@ -0,0 +1,130 @@
+loop = $loop;
+
+ if (strpos($path, '://') === false) {
+ $path = 'unix://' . $path;
+ } elseif (substr($path, 0, 7) !== 'unix://') {
+ throw new InvalidArgumentException('Given URI "' . $path . '" is invalid');
+ }
+
+ $this->master = @stream_socket_server(
+ $path,
+ $errno,
+ $errstr,
+ STREAM_SERVER_BIND | STREAM_SERVER_LISTEN,
+ stream_context_create(array('socket' => $context))
+ );
+ if (false === $this->master) {
+ throw new RuntimeException('Failed to listen on unix domain socket "' . $path . '": ' . $errstr, $errno);
+ }
+ stream_set_blocking($this->master, 0);
+
+ $this->resume();
+ }
+
+ public function getAddress()
+ {
+ if (!is_resource($this->master)) {
+ return null;
+ }
+
+ return 'unix://' . stream_socket_get_name($this->master, false);
+ }
+
+ public function pause()
+ {
+ if (!$this->listening) {
+ return;
+ }
+
+ $this->loop->removeReadStream($this->master);
+ $this->listening = false;
+ }
+
+ public function resume()
+ {
+ if ($this->listening || !is_resource($this->master)) {
+ return;
+ }
+
+ $that = $this;
+ $this->loop->addReadStream($this->master, function ($master) use ($that) {
+ $newSocket = @stream_socket_accept($master);
+ if (false === $newSocket) {
+ $that->emit('error', array(new RuntimeException('Error accepting new connection')));
+
+ return;
+ }
+ $that->handleConnection($newSocket);
+ });
+ $this->listening = true;
+ }
+
+ public function close()
+ {
+ if (!is_resource($this->master)) {
+ return;
+ }
+
+ $this->pause();
+ fclose($this->master);
+ $this->removeAllListeners();
+ }
+
+ /** @internal */
+ public function handleConnection($socket)
+ {
+ $connection = new Connection($socket, $this->loop);
+ $connection->unix = true;
+
+ $this->emit('connection', array(
+ $connection
+ ));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/ConnectionTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/ConnectionTest.php
new file mode 100644
index 0000000..d3563df
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/ConnectionTest.php
@@ -0,0 +1,47 @@
+markTestSkipped('HHVM does not support socket operation on test memory stream');
+ }
+
+ $resource = fopen('php://memory', 'r+');
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $connection = new Connection($resource, $loop);
+ $connection->close();
+
+ $this->assertFalse(is_resource($resource));
+ }
+
+ public function testCloseConnectionWillRemoveResourceFromLoopBeforeClosingResource()
+ {
+ if (defined('HHVM_VERSION')) {
+ $this->markTestSkipped('HHVM does not support socket operation on test memory stream');
+ }
+
+ $resource = fopen('php://memory', 'r+');
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('addWriteStream')->with($resource);
+
+ $onRemove = null;
+ $loop->expects($this->once())->method('removeWriteStream')->with($this->callback(function ($param) use (&$onRemove) {
+ $onRemove = is_resource($param);
+ return true;
+ }));
+
+ $connection = new Connection($resource, $loop);
+ $connection->write('test');
+ $connection->close();
+
+ $this->assertTrue($onRemove);
+ $this->assertFalse(is_resource($resource));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/ConnectorTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/ConnectorTest.php
new file mode 100644
index 0000000..c8eb19b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/ConnectorTest.php
@@ -0,0 +1,128 @@
+getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $promise = new Promise(function () { });
+ $tcp = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $tcp->expects($this->once())->method('connect')->with('127.0.0.1:80')->willReturn($promise);
+
+ $connector = new Connector($loop, array(
+ 'tcp' => $tcp
+ ));
+
+ $connector->connect('127.0.0.1:80');
+ }
+
+ public function testConnectorPassedThroughHostnameIfDnsIsDisabled()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $promise = new Promise(function () { });
+ $tcp = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $tcp->expects($this->once())->method('connect')->with('tcp://google.com:80')->willReturn($promise);
+
+ $connector = new Connector($loop, array(
+ 'tcp' => $tcp,
+ 'dns' => false
+ ));
+
+ $connector->connect('tcp://google.com:80');
+ }
+
+ public function testConnectorWithUnknownSchemeAlwaysFails()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $connector = new Connector($loop);
+
+ $promise = $connector->connect('unknown://google.com:80');
+ $promise->then(null, $this->expectCallableOnce());
+ }
+
+ public function testConnectorWithDisabledTcpDefaultSchemeAlwaysFails()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $connector = new Connector($loop, array(
+ 'tcp' => false
+ ));
+
+ $promise = $connector->connect('google.com:80');
+ $promise->then(null, $this->expectCallableOnce());
+ }
+
+ public function testConnectorWithDisabledTcpSchemeAlwaysFails()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $connector = new Connector($loop, array(
+ 'tcp' => false
+ ));
+
+ $promise = $connector->connect('tcp://google.com:80');
+ $promise->then(null, $this->expectCallableOnce());
+ }
+
+ public function testConnectorWithDisabledTlsSchemeAlwaysFails()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $connector = new Connector($loop, array(
+ 'tls' => false
+ ));
+
+ $promise = $connector->connect('tls://google.com:443');
+ $promise->then(null, $this->expectCallableOnce());
+ }
+
+ public function testConnectorWithDisabledUnixSchemeAlwaysFails()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $connector = new Connector($loop, array(
+ 'unix' => false
+ ));
+
+ $promise = $connector->connect('unix://demo.sock');
+ $promise->then(null, $this->expectCallableOnce());
+ }
+
+ public function testConnectorUsesGivenResolverInstance()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $promise = new Promise(function () { });
+ $resolver = $this->getMockBuilder('React\Dns\Resolver\Resolver')->disableOriginalConstructor()->getMock();
+ $resolver->expects($this->once())->method('resolve')->with('google.com')->willReturn($promise);
+
+ $connector = new Connector($loop, array(
+ 'dns' => $resolver
+ ));
+
+ $connector->connect('google.com:80');
+ }
+
+ public function testConnectorUsesResolvedHostnameIfDnsIsUsed()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $promise = new Promise(function ($resolve) { $resolve('127.0.0.1'); });
+ $resolver = $this->getMockBuilder('React\Dns\Resolver\Resolver')->disableOriginalConstructor()->getMock();
+ $resolver->expects($this->once())->method('resolve')->with('google.com')->willReturn($promise);
+
+ $promise = new Promise(function () { });
+ $tcp = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $tcp->expects($this->once())->method('connect')->with('tcp://127.0.0.1:80?hostname=google.com')->willReturn($promise);
+
+ $connector = new Connector($loop, array(
+ 'tcp' => $tcp,
+ 'dns' => $resolver
+ ));
+
+ $connector->connect('tcp://google.com:80');
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/DnsConnectorTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/DnsConnectorTest.php
new file mode 100644
index 0000000..3c94c39
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/DnsConnectorTest.php
@@ -0,0 +1,111 @@
+tcp = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $this->resolver = $this->getMockBuilder('React\Dns\Resolver\Resolver')->disableOriginalConstructor()->getMock();
+
+ $this->connector = new DnsConnector($this->tcp, $this->resolver);
+ }
+
+ public function testPassByResolverIfGivenIp()
+ {
+ $this->resolver->expects($this->never())->method('resolve');
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('127.0.0.1:80'))->will($this->returnValue(Promise\reject()));
+
+ $this->connector->connect('127.0.0.1:80');
+ }
+
+ public function testPassThroughResolverIfGivenHost()
+ {
+ $this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('google.com'))->will($this->returnValue(Promise\resolve('1.2.3.4')));
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('1.2.3.4:80?hostname=google.com'))->will($this->returnValue(Promise\reject()));
+
+ $this->connector->connect('google.com:80');
+ }
+
+ public function testPassThroughResolverIfGivenHostWhichResolvesToIpv6()
+ {
+ $this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('google.com'))->will($this->returnValue(Promise\resolve('::1')));
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('[::1]:80?hostname=google.com'))->will($this->returnValue(Promise\reject()));
+
+ $this->connector->connect('google.com:80');
+ }
+
+ public function testPassByResolverIfGivenCompleteUri()
+ {
+ $this->resolver->expects($this->never())->method('resolve');
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('scheme://127.0.0.1:80/path?query#fragment'))->will($this->returnValue(Promise\reject()));
+
+ $this->connector->connect('scheme://127.0.0.1:80/path?query#fragment');
+ }
+
+ public function testPassThroughResolverIfGivenCompleteUri()
+ {
+ $this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('google.com'))->will($this->returnValue(Promise\resolve('1.2.3.4')));
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('scheme://1.2.3.4:80/path?query&hostname=google.com#fragment'))->will($this->returnValue(Promise\reject()));
+
+ $this->connector->connect('scheme://google.com:80/path?query#fragment');
+ }
+
+ public function testPassThroughResolverIfGivenExplicitHost()
+ {
+ $this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('google.com'))->will($this->returnValue(Promise\resolve('1.2.3.4')));
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('scheme://1.2.3.4:80/?hostname=google.de'))->will($this->returnValue(Promise\reject()));
+
+ $this->connector->connect('scheme://google.com:80/?hostname=google.de');
+ }
+
+ public function testRejectsImmediatelyIfUriIsInvalid()
+ {
+ $this->resolver->expects($this->never())->method('resolve');
+ $this->tcp->expects($this->never())->method('connect');
+
+ $promise = $this->connector->connect('////');
+
+ $promise->then($this->expectCallableNever(), $this->expectCallableOnce());
+ }
+
+ public function testSkipConnectionIfDnsFails()
+ {
+ $this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('example.invalid'))->will($this->returnValue(Promise\reject()));
+ $this->tcp->expects($this->never())->method('connect');
+
+ $this->connector->connect('example.invalid:80');
+ }
+
+ public function testCancelDuringDnsCancelsDnsAndDoesNotStartTcpConnection()
+ {
+ $pending = new Promise\Promise(function () { }, $this->expectCallableOnce());
+ $this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('example.com'))->will($this->returnValue($pending));
+ $this->tcp->expects($this->never())->method('connect');
+
+ $promise = $this->connector->connect('example.com:80');
+ $promise->cancel();
+
+ $promise->then($this->expectCallableNever(), $this->expectCallableOnce());
+ }
+
+ public function testCancelDuringTcpConnectionCancelsTcpConnection()
+ {
+ $pending = new Promise\Promise(function () { }, function () { throw new \Exception(); });
+ $this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('example.com'))->will($this->returnValue(Promise\resolve('1.2.3.4')));
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('1.2.3.4:80?hostname=example.com'))->will($this->returnValue($pending));
+
+ $promise = $this->connector->connect('example.com:80');
+ $promise->cancel();
+
+ $promise->then($this->expectCallableNever(), $this->expectCallableOnce());
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/FixedUriConnectorTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/FixedUriConnectorTest.php
new file mode 100644
index 0000000..f42d74f
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/FixedUriConnectorTest.php
@@ -0,0 +1,19 @@
+getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $base->expects($this->once())->method('connect')->with('test')->willReturn('ret');
+
+ $connector = new FixedUriConnector('test', $base);
+
+ $this->assertEquals('ret', $connector->connect('ignored'));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/FunctionalConnectorTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/FunctionalConnectorTest.php
new file mode 100644
index 0000000..6611352
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/FunctionalConnectorTest.php
@@ -0,0 +1,32 @@
+on('connection', $this->expectCallableOnce());
+ $server->on('connection', array($server, 'close'));
+
+ $connector = new Connector($loop);
+
+ $connection = Block\await($connector->connect('localhost:9998'), $loop, self::TIMEOUT);
+
+ $this->assertInstanceOf('React\Socket\ConnectionInterface', $connection);
+
+ $connection->close();
+ $server->close();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/FunctionalSecureServerTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/FunctionalSecureServerTest.php
new file mode 100644
index 0000000..78a59d0
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/FunctionalSecureServerTest.php
@@ -0,0 +1,438 @@
+markTestSkipped('Not supported on your platform (outdated HHVM?)');
+ }
+ }
+
+ public function testEmitsConnectionForNewConnection()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableOnce());
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ Block\await($promise, $loop, self::TIMEOUT);
+ }
+
+ public function testWritesDataToConnection()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableOnce());
+
+ $server->on('connection', function (ConnectionInterface $conn) {
+ $conn->write('foo');
+ });
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $local = Block\await($promise, $loop, self::TIMEOUT);
+ /* @var $local ConnectionInterface */
+
+ $local->on('data', $this->expectCallableOnceWith('foo'));
+
+ Block\sleep(self::TIMEOUT, $loop);
+ }
+
+ public function testWritesDataInMultipleChunksToConnection()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableOnce());
+
+ $server->on('connection', function (ConnectionInterface $conn) {
+ $conn->write(str_repeat('*', 400000));
+ });
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $local = Block\await($promise, $loop, self::TIMEOUT);
+ /* @var $local React\Stream\Stream */
+
+ $received = 0;
+ $local->on('data', function ($chunk) use (&$received) {
+ $received += strlen($chunk);
+ });
+
+ Block\sleep(self::TIMEOUT, $loop);
+
+ $this->assertEquals(400000, $received);
+ }
+
+ public function testWritesMoreDataInMultipleChunksToConnection()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableOnce());
+
+ $server->on('connection', function (ConnectionInterface $conn) {
+ $conn->write(str_repeat('*', 2000000));
+ });
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $local = Block\await($promise, $loop, self::TIMEOUT);
+ /* @var $local React\Stream\Stream */
+
+ $received = 0;
+ $local->on('data', function ($chunk) use (&$received) {
+ $received += strlen($chunk);
+ });
+
+ Block\sleep(self::TIMEOUT, $loop);
+
+ $this->assertEquals(2000000, $received);
+ }
+
+ public function testEmitsDataFromConnection()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableOnce());
+
+ $once = $this->expectCallableOnceWith('foo');
+ $server->on('connection', function (ConnectionInterface $conn) use ($once) {
+ $conn->on('data', $once);
+ });
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $local = Block\await($promise, $loop, self::TIMEOUT);
+ /* @var $local React\Stream\Stream */
+
+ $local->write("foo");
+
+ Block\sleep(self::TIMEOUT, $loop);
+ }
+
+ public function testEmitsDataInMultipleChunksFromConnection()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableOnce());
+
+ $received = 0;
+ $server->on('connection', function (ConnectionInterface $conn) use (&$received) {
+ $conn->on('data', function ($chunk) use (&$received) {
+ $received += strlen($chunk);
+ });
+ });
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $local = Block\await($promise, $loop, self::TIMEOUT);
+ /* @var $local React\Stream\Stream */
+
+ $local->write(str_repeat('*', 400000));
+
+ Block\sleep(self::TIMEOUT, $loop);
+
+ $this->assertEquals(400000, $received);
+ }
+
+ public function testPipesDataBackInMultipleChunksFromConnection()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableOnce());
+
+ $server->on('connection', function (ConnectionInterface $conn) use (&$received) {
+ $conn->pipe($conn);
+ });
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $local = Block\await($promise, $loop, self::TIMEOUT);
+ /* @var $local React\Stream\Stream */
+
+ $received = 0;
+ $local->on('data', function ($chunk) use (&$received) {
+ $received += strlen($chunk);
+ });
+
+ $local->write(str_repeat('*', 400000));
+
+ Block\sleep(self::TIMEOUT, $loop);
+
+ $this->assertEquals(400000, $received);
+ }
+
+ /**
+ * @requires PHP 5.6
+ */
+ public function testEmitsConnectionForNewTlsv11Connection()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem',
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_1_SERVER
+ ));
+ $server->on('connection', $this->expectCallableOnce());
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false,
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ Block\await($promise, $loop, self::TIMEOUT);
+ }
+
+ /**
+ * @requires PHP 5.6
+ */
+ public function testEmitsErrorForClientWithTlsVersionMismatch()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem',
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_1_SERVER|STREAM_CRYPTO_METHOD_TLSv1_2_SERVER
+ ));
+ $server->on('connection', $this->expectCallableNever());
+ $server->on('error', $this->expectCallableOnce());
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false,
+ 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $this->setExpectedException('RuntimeException', 'handshake');
+ Block\await($promise, $loop, self::TIMEOUT);
+ }
+
+ public function testEmitsConnectionForNewConnectionWithEncryptedCertificate()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost_swordfish.pem',
+ 'passphrase' => 'swordfish'
+ ));
+ $server->on('connection', $this->expectCallableOnce());
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ Block\await($promise, $loop, self::TIMEOUT);
+ }
+
+ public function testEmitsErrorForServerWithInvalidCertificate()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => 'invalid.pem'
+ ));
+ $server->on('connection', $this->expectCallableNever());
+ $server->on('error', $this->expectCallableOnce());
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $this->setExpectedException('RuntimeException', 'handshake');
+ Block\await($promise, $loop, self::TIMEOUT);
+ }
+
+ public function testEmitsErrorForServerWithEncryptedCertificateMissingPassphrase()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost_swordfish.pem'
+ ));
+ $server->on('connection', $this->expectCallableNever());
+ $server->on('error', $this->expectCallableOnce());
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $this->setExpectedException('RuntimeException', 'handshake');
+ Block\await($promise, $loop, self::TIMEOUT);
+ }
+
+ public function testEmitsErrorForServerWithEncryptedCertificateWithInvalidPassphrase()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost_swordfish.pem',
+ 'passphrase' => 'nope'
+ ));
+ $server->on('connection', $this->expectCallableNever());
+ $server->on('error', $this->expectCallableOnce());
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $this->setExpectedException('RuntimeException', 'handshake');
+ Block\await($promise, $loop, self::TIMEOUT);
+ }
+
+ public function testEmitsErrorForConnectionWithPeerVerification()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableNever());
+ $server->on('error', $this->expectCallableOnce());
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => true
+ ));
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then(null, $this->expectCallableOnce());
+ Block\sleep(self::TIMEOUT, $loop);
+ }
+
+ public function testEmitsErrorIfConnectionIsCancelled()
+ {
+ if (PHP_OS !== 'Linux') {
+ $this->markTestSkipped('Linux only (OS is ' . PHP_OS . ')');
+ }
+
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableNever());
+ $server->on('error', $this->expectCallableOnce());
+
+ $connector = new SecureConnector(new TcpConnector($loop), $loop, array(
+ 'verify_peer' => false
+ ));
+ $promise = $connector->connect($server->getAddress());
+ $promise->cancel();
+
+ $promise->then(null, $this->expectCallableOnce());
+ Block\sleep(self::TIMEOUT, $loop);
+ }
+
+ public function testEmitsNothingIfConnectionIsIdle()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableNever());
+ $server->on('error', $this->expectCallableNever());
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect(str_replace('tls://', '', $server->getAddress()));
+
+ $promise->then($this->expectCallableOnce());
+ Block\sleep(self::TIMEOUT, $loop);
+ }
+
+ public function testEmitsErrorIfConnectionIsNotSecureHandshake()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new SecureServer($server, $loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $server->on('connection', $this->expectCallableNever());
+ $server->on('error', $this->expectCallableOnce());
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect(str_replace('tls://', '', $server->getAddress()));
+
+ $promise->then(function (ConnectionInterface $stream) {
+ $stream->write("GET / HTTP/1.0\r\n\r\n");
+ });
+
+ Block\sleep(self::TIMEOUT, $loop);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/FunctionalTcpServerTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/FunctionalTcpServerTest.php
new file mode 100644
index 0000000..ec7855e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/FunctionalTcpServerTest.php
@@ -0,0 +1,324 @@
+on('connection', $this->expectCallableOnce());
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+ }
+
+ public function testEmitsNoConnectionForNewConnectionWhenPaused()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server->on('connection', $this->expectCallableNever());
+ $server->pause();
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+ }
+
+ public function testEmitsConnectionForNewConnectionWhenResumedAfterPause()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server->on('connection', $this->expectCallableOnce());
+ $server->pause();
+ $server->resume();
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+ }
+
+ public function testEmitsConnectionWithRemoteIp()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $peer = null;
+ $server->on('connection', function (ConnectionInterface $conn) use (&$peer) {
+ $peer = $conn->getRemoteAddress();
+ });
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertContains('127.0.0.1:', $peer);
+ }
+
+ public function testEmitsConnectionWithLocalIp()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $local = null;
+ $server->on('connection', function (ConnectionInterface $conn) use (&$local) {
+ $local = $conn->getLocalAddress();
+ });
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertContains('127.0.0.1:', $local);
+ $this->assertEquals($server->getAddress(), $local);
+ }
+
+ public function testEmitsConnectionWithLocalIpDespiteListeningOnAll()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer('0.0.0.0:0', $loop);
+ $local = null;
+ $server->on('connection', function (ConnectionInterface $conn) use (&$local) {
+ $local = $conn->getLocalAddress();
+ });
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertContains('127.0.0.1:', $local);
+ }
+
+ public function testEmitsConnectionWithRemoteIpAfterConnectionIsClosedByPeer()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $peer = null;
+ $server->on('connection', function (ConnectionInterface $conn) use (&$peer) {
+ $conn->on('close', function () use ($conn, &$peer) {
+ $peer = $conn->getRemoteAddress();
+ });
+ });
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $client = Block\await($promise, $loop, 0.1);
+ $client->end();
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertContains('127.0.0.1:', $peer);
+ }
+
+ public function testEmitsConnectionWithRemoteNullAddressAfterConnectionIsClosedLocally()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $peer = null;
+ $server->on('connection', function (ConnectionInterface $conn) use (&$peer) {
+ $conn->close();
+ $peer = $conn->getRemoteAddress();
+ });
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertNull($peer);
+ }
+
+ public function testEmitsConnectionEvenIfConnectionIsCancelled()
+ {
+ if (PHP_OS !== 'Linux') {
+ $this->markTestSkipped('Linux only (OS is ' . PHP_OS . ')');
+ }
+
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server->on('connection', $this->expectCallableOnce());
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+ $promise->cancel();
+
+ $promise->then(null, $this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+ }
+
+ public function testEmitsConnectionForNewIpv6Connection()
+ {
+ $loop = Factory::create();
+
+ try {
+ $server = new TcpServer('[::1]:0', $loop);
+ } catch (\RuntimeException $e) {
+ $this->markTestSkipped('Unable to start IPv6 server socket (not available on your platform?)');
+ }
+
+ $server->on('connection', $this->expectCallableOnce());
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+ }
+
+ public function testEmitsConnectionWithRemoteIpv6()
+ {
+ $loop = Factory::create();
+
+ try {
+ $server = new TcpServer('[::1]:0', $loop);
+ } catch (\RuntimeException $e) {
+ $this->markTestSkipped('Unable to start IPv6 server socket (not available on your platform?)');
+ }
+
+ $peer = null;
+ $server->on('connection', function (ConnectionInterface $conn) use (&$peer) {
+ $peer = $conn->getRemoteAddress();
+ });
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertContains('[::1]:', $peer);
+ }
+
+ public function testEmitsConnectionWithLocalIpv6()
+ {
+ $loop = Factory::create();
+
+ try {
+ $server = new TcpServer('[::1]:0', $loop);
+ } catch (\RuntimeException $e) {
+ $this->markTestSkipped('Unable to start IPv6 server socket (not available on your platform?)');
+ }
+
+ $local = null;
+ $server->on('connection', function (ConnectionInterface $conn) use (&$local) {
+ $local = $conn->getLocalAddress();
+ });
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertContains('[::1]:', $local);
+ $this->assertEquals($server->getAddress(), $local);
+ }
+
+ public function testEmitsConnectionWithInheritedContextOptions()
+ {
+ if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '3.13', '<')) {
+ // https://3v4l.org/hB4Tc
+ $this->markTestSkipped('Not supported on legacy HHVM < 3.13');
+ }
+
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop, array(
+ 'backlog' => 4
+ ));
+
+ $all = null;
+ $server->on('connection', function (ConnectionInterface $conn) use (&$all) {
+ $all = stream_context_get_options($conn->stream);
+ });
+
+ $connector = new TcpConnector($loop);
+ $promise = $connector->connect($server->getAddress());
+
+ $promise->then($this->expectCallableOnce());
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertEquals(array('socket' => array('backlog' => 4)), $all);
+ }
+
+ /**
+ * @expectedException InvalidArgumentException
+ */
+ public function testFailsToListenOnInvalidUri()
+ {
+ $loop = Factory::create();
+
+ new TcpServer('///', $loop);
+ }
+
+ /**
+ * @expectedException InvalidArgumentException
+ */
+ public function testFailsToListenOnUriWithoutPort()
+ {
+ $loop = Factory::create();
+
+ new TcpServer('127.0.0.1', $loop);
+ }
+
+ /**
+ * @expectedException InvalidArgumentException
+ */
+ public function testFailsToListenOnUriWithWrongScheme()
+ {
+ $loop = Factory::create();
+
+ new TcpServer('udp://127.0.0.1:0', $loop);
+ }
+
+ /**
+ * @expectedException InvalidArgumentException
+ */
+ public function testFailsToListenOnUriWIthHostname()
+ {
+ $loop = Factory::create();
+
+ new TcpServer('localhost:8080', $loop);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/IntegrationTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/IntegrationTest.php
new file mode 100644
index 0000000..24dbe37
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/IntegrationTest.php
@@ -0,0 +1,171 @@
+connect('google.com:80'), $loop);
+
+ $this->assertContains(':80', $conn->getRemoteAddress());
+ $this->assertNotEquals('google.com:80', $conn->getRemoteAddress());
+
+ $conn->write("GET / HTTP/1.0\r\n\r\n");
+
+ $response = $this->buffer($conn, $loop, self::TIMEOUT);
+
+ $this->assertRegExp('#^HTTP/1\.0#', $response);
+ }
+
+ /** @test */
+ public function gettingEncryptedStuffFromGoogleShouldWork()
+ {
+ if (!function_exists('stream_socket_enable_crypto')) {
+ $this->markTestSkipped('Not supported on your platform (outdated HHVM?)');
+ }
+
+ $loop = Factory::create();
+ $secureConnector = new Connector($loop);
+
+ $conn = Block\await($secureConnector->connect('tls://google.com:443'), $loop);
+
+ $conn->write("GET / HTTP/1.0\r\n\r\n");
+
+ $response = $this->buffer($conn, $loop, self::TIMEOUT);
+
+ $this->assertRegExp('#^HTTP/1\.0#', $response);
+ }
+
+ /** @test */
+ public function gettingEncryptedStuffFromGoogleShouldWorkIfHostIsResolvedFirst()
+ {
+ if (!function_exists('stream_socket_enable_crypto')) {
+ $this->markTestSkipped('Not supported on your platform (outdated HHVM?)');
+ }
+
+ $loop = Factory::create();
+
+ $factory = new ResolverFactory();
+ $dns = $factory->create('8.8.8.8', $loop);
+
+ $connector = new DnsConnector(
+ new SecureConnector(
+ new TcpConnector($loop),
+ $loop
+ ),
+ $dns
+ );
+
+ $conn = Block\await($connector->connect('google.com:443'), $loop);
+
+ $conn->write("GET / HTTP/1.0\r\n\r\n");
+
+ $response = $this->buffer($conn, $loop, self::TIMEOUT);
+
+ $this->assertRegExp('#^HTTP/1\.0#', $response);
+ }
+
+ /** @test */
+ public function gettingPlaintextStuffFromEncryptedGoogleShouldNotWork()
+ {
+ $loop = Factory::create();
+ $connector = new Connector($loop);
+
+ $conn = Block\await($connector->connect('google.com:443'), $loop);
+
+ $this->assertContains(':443', $conn->getRemoteAddress());
+ $this->assertNotEquals('google.com:443', $conn->getRemoteAddress());
+
+ $conn->write("GET / HTTP/1.0\r\n\r\n");
+
+ $response = $this->buffer($conn, $loop, self::TIMEOUT);
+
+ $this->assertNotRegExp('#^HTTP/1\.0#', $response);
+ }
+
+ public function testConnectingFailsIfDnsUsesInvalidResolver()
+ {
+ $loop = Factory::create();
+
+ $factory = new ResolverFactory();
+ $dns = $factory->create('demo.invalid', $loop);
+
+ $connector = new Connector($loop, array(
+ 'dns' => $dns
+ ));
+
+ $this->setExpectedException('RuntimeException');
+ Block\await($connector->connect('google.com:80'), $loop, self::TIMEOUT);
+ }
+
+ public function testConnectingFailsIfTimeoutIsTooSmall()
+ {
+ if (!function_exists('stream_socket_enable_crypto')) {
+ $this->markTestSkipped('Not supported on your platform (outdated HHVM?)');
+ }
+
+ $loop = Factory::create();
+
+ $connector = new Connector($loop, array(
+ 'timeout' => 0.001
+ ));
+
+ $this->setExpectedException('RuntimeException');
+ Block\await($connector->connect('google.com:80'), $loop, self::TIMEOUT);
+ }
+
+ public function testSelfSignedRejectsIfVerificationIsEnabled()
+ {
+ if (!function_exists('stream_socket_enable_crypto')) {
+ $this->markTestSkipped('Not supported on your platform (outdated HHVM?)');
+ }
+
+ $loop = Factory::create();
+
+ $connector = new Connector($loop, array(
+ 'tls' => array(
+ 'verify_peer' => true
+ )
+ ));
+
+ $this->setExpectedException('RuntimeException');
+ Block\await($connector->connect('tls://self-signed.badssl.com:443'), $loop, self::TIMEOUT);
+ }
+
+ public function testSelfSignedResolvesIfVerificationIsDisabled()
+ {
+ if (!function_exists('stream_socket_enable_crypto')) {
+ $this->markTestSkipped('Not supported on your platform (outdated HHVM?)');
+ }
+
+ $loop = Factory::create();
+
+ $connector = new Connector($loop, array(
+ 'tls' => array(
+ 'verify_peer' => false
+ )
+ ));
+
+ $conn = Block\await($connector->connect('tls://self-signed.badssl.com:443'), $loop, self::TIMEOUT);
+ $conn->close();
+
+ // if we reach this, then everything is good
+ $this->assertNull(null);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/LimitingServerTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/LimitingServerTest.php
new file mode 100644
index 0000000..2cc9a58
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/LimitingServerTest.php
@@ -0,0 +1,195 @@
+getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('getAddress')->willReturn('127.0.0.1:1234');
+
+ $server = new LimitingServer($tcp, 100);
+
+ $this->assertEquals('127.0.0.1:1234', $server->getAddress());
+ }
+
+ public function testPauseWillBePassedThroughToTcpServer()
+ {
+ $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('pause');
+
+ $server = new LimitingServer($tcp, 100);
+
+ $server->pause();
+ }
+
+ public function testPauseTwiceWillBePassedThroughToTcpServerOnce()
+ {
+ $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('pause');
+
+ $server = new LimitingServer($tcp, 100);
+
+ $server->pause();
+ $server->pause();
+ }
+
+ public function testResumeWillBePassedThroughToTcpServer()
+ {
+ $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('resume');
+
+ $server = new LimitingServer($tcp, 100);
+
+ $server->pause();
+ $server->resume();
+ }
+
+ public function testResumeTwiceWillBePassedThroughToTcpServerOnce()
+ {
+ $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('resume');
+
+ $server = new LimitingServer($tcp, 100);
+
+ $server->pause();
+ $server->resume();
+ $server->resume();
+ }
+
+ public function testCloseWillBePassedThroughToTcpServer()
+ {
+ $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('close');
+
+ $server = new LimitingServer($tcp, 100);
+
+ $server->close();
+ }
+
+ public function testSocketErrorWillBeForwarded()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $tcp = new TcpServer(0, $loop);
+
+ $server = new LimitingServer($tcp, 100);
+
+ $server->on('error', $this->expectCallableOnce());
+
+ $tcp->emit('error', array(new \RuntimeException('test')));
+ }
+
+ public function testSocketConnectionWillBeForwarded()
+ {
+ $connection = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
+
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $tcp = new TcpServer(0, $loop);
+
+ $server = new LimitingServer($tcp, 100);
+ $server->on('connection', $this->expectCallableOnceWith($connection));
+ $server->on('error', $this->expectCallableNever());
+
+ $tcp->emit('connection', array($connection));
+
+ $this->assertEquals(array($connection), $server->getConnections());
+ }
+
+ public function testSocketConnectionWillBeClosedOnceLimitIsReached()
+ {
+ $first = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
+ $first->expects($this->never())->method('close');
+ $second = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
+ $second->expects($this->once())->method('close');
+
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $tcp = new TcpServer(0, $loop);
+
+ $server = new LimitingServer($tcp, 1);
+ $server->on('connection', $this->expectCallableOnceWith($first));
+ $server->on('error', $this->expectCallableOnce());
+
+ $tcp->emit('connection', array($first));
+ $tcp->emit('connection', array($second));
+ }
+
+ public function testPausingServerWillBePausedOnceLimitIsReached()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('addReadStream');
+ $loop->expects($this->once())->method('removeReadStream');
+
+ $tcp = new TcpServer(0, $loop);
+
+ $connection = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
+
+ $server = new LimitingServer($tcp, 1, true);
+
+ $tcp->emit('connection', array($connection));
+ }
+
+ public function testSocketDisconnectionWillRemoveFromList()
+ {
+ $loop = Factory::create();
+
+ $tcp = new TcpServer(0, $loop);
+
+ $socket = stream_socket_client($tcp->getAddress());
+ fclose($socket);
+
+ $server = new LimitingServer($tcp, 100);
+ $server->on('connection', $this->expectCallableOnce());
+ $server->on('error', $this->expectCallableNever());
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertEquals(array(), $server->getConnections());
+ }
+
+ public function testPausingServerWillEmitOnlyOneButAcceptTwoConnectionsDueToOperatingSystem()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(0, $loop);
+ $server = new LimitingServer($server, 1, true);
+ $server->on('connection', $this->expectCallableOnce());
+ $server->on('error', $this->expectCallableNever());
+
+ $first = stream_socket_client($server->getAddress());
+ $second = stream_socket_client($server->getAddress());
+
+ Block\sleep(0.1, $loop);
+
+ fclose($first);
+ fclose($second);
+ }
+
+ public function testPausingServerWillEmitTwoConnectionsFromBacklog()
+ {
+ $loop = Factory::create();
+
+ $twice = $this->createCallableMock();
+ $twice->expects($this->exactly(2))->method('__invoke');
+
+ $server = new TcpServer(0, $loop);
+ $server = new LimitingServer($server, 1, true);
+ $server->on('connection', $twice);
+ $server->on('error', $this->expectCallableNever());
+
+ $first = stream_socket_client($server->getAddress());
+ fclose($first);
+ $second = stream_socket_client($server->getAddress());
+ fclose($second);
+
+ Block\sleep(0.1, $loop);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/SecureConnectorTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/SecureConnectorTest.php
new file mode 100644
index 0000000..0b3a702
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/SecureConnectorTest.php
@@ -0,0 +1,74 @@
+markTestSkipped('Not supported on your platform (outdated HHVM?)');
+ }
+
+ $this->loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $this->tcp = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $this->connector = new SecureConnector($this->tcp, $this->loop);
+ }
+
+ public function testConnectionWillWaitForTcpConnection()
+ {
+ $pending = new Promise\Promise(function () { });
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('example.com:80'))->will($this->returnValue($pending));
+
+ $promise = $this->connector->connect('example.com:80');
+
+ $this->assertInstanceOf('React\Promise\PromiseInterface', $promise);
+ }
+
+ public function testConnectionWithCompleteUriWillBePassedThroughExpectForScheme()
+ {
+ $pending = new Promise\Promise(function () { });
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('example.com:80/path?query#fragment'))->will($this->returnValue($pending));
+
+ $this->connector->connect('tls://example.com:80/path?query#fragment');
+ }
+
+ public function testConnectionToInvalidSchemeWillReject()
+ {
+ $this->tcp->expects($this->never())->method('connect');
+
+ $promise = $this->connector->connect('tcp://example.com:80');
+
+ $promise->then(null, $this->expectCallableOnce());
+ }
+
+ public function testCancelDuringTcpConnectionCancelsTcpConnection()
+ {
+ $pending = new Promise\Promise(function () { }, function () { throw new \Exception(); });
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('example.com:80'))->will($this->returnValue($pending));
+
+ $promise = $this->connector->connect('example.com:80');
+ $promise->cancel();
+
+ $promise->then($this->expectCallableNever(), $this->expectCallableOnce());
+ }
+
+ public function testConnectionWillBeClosedAndRejectedIfConnectioIsNoStream()
+ {
+ $connection = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
+ $connection->expects($this->once())->method('close');
+
+ $this->tcp->expects($this->once())->method('connect')->with($this->equalTo('example.com:80'))->willReturn(Promise\resolve($connection));
+
+ $promise = $this->connector->connect('example.com:80');
+
+ $promise->then($this->expectCallableNever(), $this->expectCallableOnce());
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/SecureIntegrationTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/SecureIntegrationTest.php
new file mode 100644
index 0000000..8c9ba14
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/SecureIntegrationTest.php
@@ -0,0 +1,204 @@
+markTestSkipped('Not supported on your platform (outdated HHVM?)');
+ }
+
+ $this->loop = LoopFactory::create();
+ $this->server = new TcpServer(0, $this->loop);
+ $this->server = new SecureServer($this->server, $this->loop, array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ ));
+ $this->address = $this->server->getAddress();
+ $this->connector = new SecureConnector(new TcpConnector($this->loop), $this->loop, array('verify_peer' => false));
+ }
+
+ public function tearDown()
+ {
+ if ($this->server !== null) {
+ $this->server->close();
+ $this->server = null;
+ }
+ }
+
+ public function testConnectToServer()
+ {
+ $client = Block\await($this->connector->connect($this->address), $this->loop, self::TIMEOUT);
+ /* @var $client ConnectionInterface */
+
+ $client->close();
+
+ // if we reach this, then everything is good
+ $this->assertNull(null);
+ }
+
+ public function testConnectToServerEmitsConnection()
+ {
+ $promiseServer = $this->createPromiseForEvent($this->server, 'connection', $this->expectCallableOnce());
+
+ $promiseClient = $this->connector->connect($this->address);
+
+ list($_, $client) = Block\awaitAll(array($promiseServer, $promiseClient), $this->loop, self::TIMEOUT);
+ /* @var $client ConnectionInterface */
+
+ $client->close();
+ }
+
+ public function testSendSmallDataToServerReceivesOneChunk()
+ {
+ // server expects one connection which emits one data event
+ $received = new Deferred();
+ $this->server->on('connection', function (ConnectionInterface $peer) use ($received) {
+ $peer->on('data', function ($chunk) use ($received) {
+ $received->resolve($chunk);
+ });
+ });
+
+ $client = Block\await($this->connector->connect($this->address), $this->loop, self::TIMEOUT);
+ /* @var $client ConnectionInterface */
+
+ $client->write('hello');
+
+ // await server to report one "data" event
+ $data = Block\await($received->promise(), $this->loop, self::TIMEOUT);
+
+ $client->close();
+
+ $this->assertEquals('hello', $data);
+ }
+
+ public function testSendDataWithEndToServerReceivesAllData()
+ {
+ $disconnected = new Deferred();
+ $this->server->on('connection', function (ConnectionInterface $peer) use ($disconnected) {
+ $received = '';
+ $peer->on('data', function ($chunk) use (&$received) {
+ $received .= $chunk;
+ });
+ $peer->on('close', function () use (&$received, $disconnected) {
+ $disconnected->resolve($received);
+ });
+ });
+
+ $client = Block\await($this->connector->connect($this->address), $this->loop, self::TIMEOUT);
+ /* @var $client ConnectionInterface */
+
+ $data = str_repeat('a', 200000);
+ $client->end($data);
+
+ // await server to report connection "close" event
+ $received = Block\await($disconnected->promise(), $this->loop, self::TIMEOUT);
+
+ $this->assertEquals($data, $received);
+ }
+
+ public function testSendDataWithoutEndingToServerReceivesAllData()
+ {
+ $received = '';
+ $this->server->on('connection', function (ConnectionInterface $peer) use (&$received) {
+ $peer->on('data', function ($chunk) use (&$received) {
+ $received .= $chunk;
+ });
+ });
+
+ $client = Block\await($this->connector->connect($this->address), $this->loop, self::TIMEOUT);
+ /* @var $client ConnectionInterface */
+
+ $data = str_repeat('d', 200000);
+ $client->write($data);
+
+ // buffer incoming data for 0.1s (should be plenty of time)
+ Block\sleep(0.1, $this->loop);
+
+ $client->close();
+
+ $this->assertEquals($data, $received);
+ }
+
+ public function testConnectToServerWhichSendsSmallDataReceivesOneChunk()
+ {
+ $this->server->on('connection', function (ConnectionInterface $peer) {
+ $peer->write('hello');
+ });
+
+ $client = Block\await($this->connector->connect($this->address), $this->loop, self::TIMEOUT);
+ /* @var $client ConnectionInterface */
+
+ // await client to report one "data" event
+ $receive = $this->createPromiseForEvent($client, 'data', $this->expectCallableOnceWith('hello'));
+ Block\await($receive, $this->loop, self::TIMEOUT);
+
+ $client->close();
+ }
+
+ public function testConnectToServerWhichSendsDataWithEndReceivesAllData()
+ {
+ $data = str_repeat('b', 100000);
+ $this->server->on('connection', function (ConnectionInterface $peer) use ($data) {
+ $peer->end($data);
+ });
+
+ $client = Block\await($this->connector->connect($this->address), $this->loop, self::TIMEOUT);
+ /* @var $client ConnectionInterface */
+
+ // await data from client until it closes
+ $received = $this->buffer($client, $this->loop, self::TIMEOUT);
+
+ $this->assertEquals($data, $received);
+ }
+
+ public function testConnectToServerWhichSendsDataWithoutEndingReceivesAllData()
+ {
+ $data = str_repeat('c', 100000);
+ $this->server->on('connection', function (ConnectionInterface $peer) use ($data) {
+ $peer->write($data);
+ });
+
+ $client = Block\await($this->connector->connect($this->address), $this->loop, self::TIMEOUT);
+ /* @var $client ConnectionInterface */
+
+ // buffer incoming data for 0.1s (should be plenty of time)
+ $received = '';
+ $client->on('data', function ($chunk) use (&$received) {
+ $received .= $chunk;
+ });
+ Block\sleep(0.1, $this->loop);
+
+ $client->close();
+
+ $this->assertEquals($data, $received);
+ }
+
+ private function createPromiseForEvent(EventEmitterInterface $emitter, $event, $fn)
+ {
+ return new Promise(function ($resolve) use ($emitter, $event, $fn) {
+ $emitter->on($event, function () use ($resolve, $fn) {
+ $resolve(call_user_func_array($fn, func_get_args()));
+ });
+ });
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/SecureServerTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/SecureServerTest.php
new file mode 100644
index 0000000..92c641f
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/SecureServerTest.php
@@ -0,0 +1,105 @@
+markTestSkipped('Not supported on your platform (outdated HHVM?)');
+ }
+ }
+
+ public function testGetAddressWillBePassedThroughToTcpServer()
+ {
+ $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('getAddress')->willReturn('tcp://127.0.0.1:1234');
+
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $server = new SecureServer($tcp, $loop, array());
+
+ $this->assertEquals('tls://127.0.0.1:1234', $server->getAddress());
+ }
+
+ public function testGetAddressWillReturnNullIfTcpServerReturnsNull()
+ {
+ $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('getAddress')->willReturn(null);
+
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $server = new SecureServer($tcp, $loop, array());
+
+ $this->assertNull($server->getAddress());
+ }
+
+ public function testPauseWillBePassedThroughToTcpServer()
+ {
+ $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('pause');
+
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $server = new SecureServer($tcp, $loop, array());
+
+ $server->pause();
+ }
+
+ public function testResumeWillBePassedThroughToTcpServer()
+ {
+ $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('resume');
+
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $server = new SecureServer($tcp, $loop, array());
+
+ $server->resume();
+ }
+
+ public function testCloseWillBePassedThroughToTcpServer()
+ {
+ $tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
+ $tcp->expects($this->once())->method('close');
+
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $server = new SecureServer($tcp, $loop, array());
+
+ $server->close();
+ }
+
+ public function testConnectionWillBeEndedWithErrorIfItIsNotAStream()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $tcp = new TcpServer(0, $loop);
+
+ $connection = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
+ $connection->expects($this->once())->method('end');
+
+ $server = new SecureServer($tcp, $loop, array());
+
+ $server->on('error', $this->expectCallableOnce());
+
+ $tcp->emit('connection', array($connection));
+ }
+
+ public function testSocketErrorWillBeForwarded()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $tcp = new TcpServer(0, $loop);
+
+ $server = new SecureServer($tcp, $loop, array());
+
+ $server->on('error', $this->expectCallableOnce());
+
+ $tcp->emit('error', array(new \RuntimeException('test')));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/ServerTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/ServerTest.php
new file mode 100644
index 0000000..14fdb2c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/ServerTest.php
@@ -0,0 +1,173 @@
+assertNotEquals(0, $server->getAddress());
+ $server->close();
+ }
+
+ /**
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructorThrowsForInvalidUri()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $server = new Server('invalid URI', $loop);
+ }
+
+ public function testConstructorCreatesExpectedTcpServer()
+ {
+ $loop = Factory::create();
+
+ $server = new Server(0, $loop);
+
+ $connector = new TcpConnector($loop);
+ $connector->connect($server->getAddress())
+ ->then($this->expectCallableOnce(), $this->expectCallableNever());
+
+ $connection = Block\await($connector->connect($server->getAddress()), $loop, self::TIMEOUT);
+
+ $connection->close();
+ $server->close();
+ }
+
+ public function testConstructorCreatesExpectedUnixServer()
+ {
+ $loop = Factory::create();
+
+ $server = new Server($this->getRandomSocketUri(), $loop);
+
+ $connector = new UnixConnector($loop);
+ $connector->connect($server->getAddress())
+ ->then($this->expectCallableOnce(), $this->expectCallableNever());
+
+ $connection = Block\await($connector->connect($server->getAddress()), $loop, self::TIMEOUT);
+
+ $connection->close();
+ $server->close();
+ }
+
+ public function testEmitsConnectionForNewConnection()
+ {
+ $loop = Factory::create();
+
+ $server = new Server(0, $loop);
+ $server->on('connection', $this->expectCallableOnce());
+
+ $client = stream_socket_client($server->getAddress());
+
+ Block\sleep(0.1, $loop);
+ }
+
+ public function testDoesNotEmitConnectionForNewConnectionToPausedServer()
+ {
+ $loop = Factory::create();
+
+ $server = new Server(0, $loop);
+ $server->pause();
+ $server->on('connection', $this->expectCallableNever());
+
+ $client = stream_socket_client($server->getAddress());
+
+ Block\sleep(0.1, $loop);
+ }
+
+ public function testDoesEmitConnectionForNewConnectionToResumedServer()
+ {
+ $loop = Factory::create();
+
+ $server = new Server(0, $loop);
+ $server->pause();
+ $server->on('connection', $this->expectCallableOnce());
+
+ $client = stream_socket_client($server->getAddress());
+
+ Block\sleep(0.1, $loop);
+
+ $server->resume();
+ Block\sleep(0.1, $loop);
+ }
+
+ public function testDoesNotAllowConnectionToClosedServer()
+ {
+ $loop = Factory::create();
+
+ $server = new Server(0, $loop);
+ $server->on('connection', $this->expectCallableNever());
+ $address = $server->getAddress();
+ $server->close();
+
+ $client = @stream_socket_client($address);
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertFalse($client);
+ }
+
+ public function testEmitsConnectionWithInheritedContextOptions()
+ {
+ if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '3.13', '<')) {
+ // https://3v4l.org/hB4Tc
+ $this->markTestSkipped('Not supported on legacy HHVM < 3.13');
+ }
+
+ $loop = Factory::create();
+
+ $server = new Server(0, $loop, array(
+ 'backlog' => 4
+ ));
+
+ $all = null;
+ $server->on('connection', function (ConnectionInterface $conn) use (&$all) {
+ $all = stream_context_get_options($conn->stream);
+ });
+
+ $client = stream_socket_client($server->getAddress());
+
+ Block\sleep(0.1, $loop);
+
+ $this->assertEquals(array('socket' => array('backlog' => 4)), $all);
+ }
+
+ public function testDoesNotEmitSecureConnectionForNewPlainConnection()
+ {
+ if (!function_exists('stream_socket_enable_crypto')) {
+ $this->markTestSkipped('Not supported on your platform (outdated HHVM?)');
+ }
+
+ $loop = Factory::create();
+
+ $server = new Server('tls://127.0.0.1:0', $loop, array(
+ 'tls' => array(
+ 'local_cert' => __DIR__ . '/../examples/localhost.pem'
+ )
+ ));
+ $server->on('connection', $this->expectCallableNever());
+
+ $client = stream_socket_client(str_replace('tls://', '', $server->getAddress()));
+
+ Block\sleep(0.1, $loop);
+ }
+
+ private function getRandomSocketUri()
+ {
+ return "unix://" . sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid(rand(), true) . '.sock';
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/Stub/CallableStub.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/Stub/CallableStub.php
new file mode 100644
index 0000000..1b197eb
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/Stub/CallableStub.php
@@ -0,0 +1,10 @@
+data .= $data;
+
+ return true;
+ }
+
+ public function end($data = null)
+ {
+ }
+
+ public function close()
+ {
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ public function getRemoteAddress()
+ {
+ return '127.0.0.1';
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/Stub/ServerStub.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/Stub/ServerStub.php
new file mode 100644
index 0000000..d9e74f4
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/Stub/ServerStub.php
@@ -0,0 +1,18 @@
+connect('127.0.0.1:9999')
+ ->then($this->expectCallableNever(), $this->expectCallableOnce());
+
+ $loop->run();
+ }
+
+ /** @test */
+ public function connectionToTcpServerShouldAddResourceToLoop()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $connector = new TcpConnector($loop);
+
+ $server = new TcpServer(0, $loop);
+
+ $valid = false;
+ $loop->expects($this->once())->method('addWriteStream')->with($this->callback(function ($arg) use (&$valid) {
+ $valid = is_resource($arg);
+ return true;
+ }));
+ $connector->connect($server->getAddress());
+
+ $this->assertTrue($valid);
+ }
+
+ /** @test */
+ public function connectionToTcpServerShouldSucceed()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(9999, $loop);
+ $server->on('connection', $this->expectCallableOnce());
+ $server->on('connection', array($server, 'close'));
+
+ $connector = new TcpConnector($loop);
+
+ $connection = Block\await($connector->connect('127.0.0.1:9999'), $loop, self::TIMEOUT);
+
+ $this->assertInstanceOf('React\Socket\ConnectionInterface', $connection);
+
+ $connection->close();
+ }
+
+ /** @test */
+ public function connectionToTcpServerShouldSucceedWithRemoteAdressSameAsTarget()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(9999, $loop);
+ $server->on('connection', array($server, 'close'));
+
+ $connector = new TcpConnector($loop);
+
+ $connection = Block\await($connector->connect('127.0.0.1:9999'), $loop, self::TIMEOUT);
+ /* @var $connection ConnectionInterface */
+
+ $this->assertEquals('tcp://127.0.0.1:9999', $connection->getRemoteAddress());
+
+ $connection->close();
+ }
+
+ /** @test */
+ public function connectionToTcpServerShouldSucceedWithLocalAdressOnLocalhost()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(9999, $loop);
+ $server->on('connection', array($server, 'close'));
+
+ $connector = new TcpConnector($loop);
+
+ $connection = Block\await($connector->connect('127.0.0.1:9999'), $loop, self::TIMEOUT);
+ /* @var $connection ConnectionInterface */
+
+ $this->assertContains('tcp://127.0.0.1:', $connection->getLocalAddress());
+ $this->assertNotEquals('tcp://127.0.0.1:9999', $connection->getLocalAddress());
+
+ $connection->close();
+ }
+
+ /** @test */
+ public function connectionToTcpServerShouldSucceedWithNullAddressesAfterConnectionClosed()
+ {
+ $loop = Factory::create();
+
+ $server = new TcpServer(9999, $loop);
+ $server->on('connection', array($server, 'close'));
+
+ $connector = new TcpConnector($loop);
+
+ $connection = Block\await($connector->connect('127.0.0.1:9999'), $loop, self::TIMEOUT);
+ /* @var $connection ConnectionInterface */
+
+ $connection->close();
+
+ $this->assertNull($connection->getRemoteAddress());
+ $this->assertNull($connection->getLocalAddress());
+ }
+
+ /** @test */
+ public function connectionToTcpServerWillCloseWhenOtherSideCloses()
+ {
+ $loop = Factory::create();
+
+ // immediately close connection and server once connection is in
+ $server = new TcpServer(0, $loop);
+ $server->on('connection', function (ConnectionInterface $conn) use ($server) {
+ $conn->close();
+ $server->close();
+ });
+
+ $once = $this->expectCallableOnce();
+ $connector = new TcpConnector($loop);
+ $connector->connect($server->getAddress())->then(function (ConnectionInterface $conn) use ($once) {
+ $conn->write('hello');
+ $conn->on('close', $once);
+ });
+
+ $loop->run();
+ }
+
+ /** @test */
+ public function connectionToEmptyIp6PortShouldFail()
+ {
+ $loop = Factory::create();
+
+ $connector = new TcpConnector($loop);
+ $connector
+ ->connect('[::1]:9999')
+ ->then($this->expectCallableNever(), $this->expectCallableOnce());
+
+ $loop->run();
+ }
+
+ /** @test */
+ public function connectionToIp6TcpServerShouldSucceed()
+ {
+ $loop = Factory::create();
+
+ try {
+ $server = new TcpServer('[::1]:9999', $loop);
+ } catch (\Exception $e) {
+ $this->markTestSkipped('Unable to start IPv6 server socket (IPv6 not supported on this system?)');
+ }
+
+ $server->on('connection', $this->expectCallableOnce());
+ $server->on('connection', array($server, 'close'));
+
+ $connector = new TcpConnector($loop);
+
+ $connection = Block\await($connector->connect('[::1]:9999'), $loop, self::TIMEOUT);
+ /* @var $connection ConnectionInterface */
+
+ $this->assertEquals('tcp://[::1]:9999', $connection->getRemoteAddress());
+
+ $this->assertContains('tcp://[::1]:', $connection->getLocalAddress());
+ $this->assertNotEquals('tcp://[::1]:9999', $connection->getLocalAddress());
+
+ $connection->close();
+ }
+
+ /** @test */
+ public function connectionToHostnameShouldFailImmediately()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $connector = new TcpConnector($loop);
+ $connector->connect('www.google.com:80')->then(
+ $this->expectCallableNever(),
+ $this->expectCallableOnce()
+ );
+ }
+
+ /** @test */
+ public function connectionToInvalidPortShouldFailImmediately()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $connector = new TcpConnector($loop);
+ $connector->connect('255.255.255.255:12345678')->then(
+ $this->expectCallableNever(),
+ $this->expectCallableOnce()
+ );
+ }
+
+ /** @test */
+ public function connectionToInvalidSchemeShouldFailImmediately()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+
+ $connector = new TcpConnector($loop);
+ $connector->connect('tls://google.com:443')->then(
+ $this->expectCallableNever(),
+ $this->expectCallableOnce()
+ );
+ }
+
+ /** @test */
+ public function cancellingConnectionShouldRemoveResourceFromLoopAndCloseResource()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $connector = new TcpConnector($loop);
+
+ $server = new TcpServer(0, $loop);
+
+ $loop->expects($this->once())->method('addWriteStream');
+ $promise = $connector->connect($server->getAddress());
+
+ $resource = null;
+ $valid = false;
+ $loop->expects($this->once())->method('removeWriteStream')->with($this->callback(function ($arg) use (&$resource, &$valid) {
+ $resource = $arg;
+ $valid = is_resource($arg);
+ return true;
+ }));
+ $promise->cancel();
+
+ $this->assertTrue($valid);
+ $this->assertFalse(is_resource($resource));
+ }
+
+ /** @test */
+ public function cancellingConnectionShouldRejectPromise()
+ {
+ $loop = Factory::create();
+ $connector = new TcpConnector($loop);
+
+ $server = new TcpServer(0, $loop);
+
+ $promise = $connector->connect($server->getAddress());
+ $promise->cancel();
+
+ $this->setExpectedException('RuntimeException', 'Cancelled');
+ Block\await($promise, $loop);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/TcpServerTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/TcpServerTest.php
new file mode 100644
index 0000000..72b3c28
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/TcpServerTest.php
@@ -0,0 +1,285 @@
+loop = $this->createLoop();
+ $this->server = new TcpServer(0, $this->loop);
+
+ $this->port = parse_url($this->server->getAddress(), PHP_URL_PORT);
+ }
+
+ /**
+ * @covers React\Socket\TcpServer::handleConnection
+ */
+ public function testConnection()
+ {
+ $client = stream_socket_client('tcp://localhost:'.$this->port);
+
+ $this->server->on('connection', $this->expectCallableOnce());
+
+ $this->tick();
+ }
+
+ /**
+ * @covers React\Socket\TcpServer::handleConnection
+ */
+ public function testConnectionWithManyClients()
+ {
+ $client1 = stream_socket_client('tcp://localhost:'.$this->port);
+ $client2 = stream_socket_client('tcp://localhost:'.$this->port);
+ $client3 = stream_socket_client('tcp://localhost:'.$this->port);
+
+ $this->server->on('connection', $this->expectCallableExactly(3));
+ $this->tick();
+ $this->tick();
+ $this->tick();
+ }
+
+ public function testDataEventWillNotBeEmittedWhenClientSendsNoData()
+ {
+ $client = stream_socket_client('tcp://localhost:'.$this->port);
+
+ $mock = $this->expectCallableNever();
+
+ $this->server->on('connection', function ($conn) use ($mock) {
+ $conn->on('data', $mock);
+ });
+ $this->tick();
+ $this->tick();
+ }
+
+ public function testDataWillBeEmittedWithDataClientSends()
+ {
+ $client = stream_socket_client('tcp://localhost:'.$this->port);
+
+ fwrite($client, "foo\n");
+
+ $mock = $this->expectCallableOnceWith("foo\n");
+
+ $this->server->on('connection', function ($conn) use ($mock) {
+ $conn->on('data', $mock);
+ });
+ $this->tick();
+ $this->tick();
+ }
+
+ public function testDataWillBeEmittedEvenWhenClientShutsDownAfterSending()
+ {
+ $client = stream_socket_client('tcp://localhost:' . $this->port);
+ fwrite($client, "foo\n");
+ stream_socket_shutdown($client, STREAM_SHUT_WR);
+
+ $mock = $this->expectCallableOnceWith("foo\n");
+
+ $this->server->on('connection', function ($conn) use ($mock) {
+ $conn->on('data', $mock);
+ });
+ $this->tick();
+ $this->tick();
+ }
+
+ public function testLoopWillEndWhenServerIsClosed()
+ {
+ // explicitly unset server because we already call close()
+ $this->server->close();
+ $this->server = null;
+
+ $this->loop->run();
+
+ // if we reach this, then everything is good
+ $this->assertNull(null);
+ }
+
+ public function testCloseTwiceIsNoOp()
+ {
+ $this->server->close();
+ $this->server->close();
+
+ // if we reach this, then everything is good
+ $this->assertNull(null);
+ }
+
+ public function testGetAddressAfterCloseReturnsNull()
+ {
+ $this->server->close();
+ $this->assertNull($this->server->getAddress());
+ }
+
+ public function testLoopWillEndWhenServerIsClosedAfterSingleConnection()
+ {
+ $client = stream_socket_client('tcp://localhost:' . $this->port);
+
+ // explicitly unset server because we only accept a single connection
+ // and then already call close()
+ $server = $this->server;
+ $this->server = null;
+
+ $server->on('connection', function ($conn) use ($server) {
+ $conn->close();
+ $server->close();
+ });
+
+ $this->loop->run();
+
+ // if we reach this, then everything is good
+ $this->assertNull(null);
+ }
+
+ public function testDataWillBeEmittedInMultipleChunksWhenClientSendsExcessiveAmounts()
+ {
+ $client = stream_socket_client('tcp://localhost:' . $this->port);
+ $stream = new DuplexResourceStream($client, $this->loop);
+
+ $bytes = 1024 * 1024;
+ $stream->end(str_repeat('*', $bytes));
+
+ $mock = $this->expectCallableOnce();
+
+ // explicitly unset server because we only accept a single connection
+ // and then already call close()
+ $server = $this->server;
+ $this->server = null;
+
+ $received = 0;
+ $server->on('connection', function ($conn) use ($mock, &$received, $server) {
+ // count number of bytes received
+ $conn->on('data', function ($data) use (&$received) {
+ $received += strlen($data);
+ });
+
+ $conn->on('end', $mock);
+
+ // do not await any further connections in order to let the loop terminate
+ $server->close();
+ });
+
+ $this->loop->run();
+
+ $this->assertEquals($bytes, $received);
+ }
+
+ public function testConnectionDoesNotEndWhenClientDoesNotClose()
+ {
+ $client = stream_socket_client('tcp://localhost:'.$this->port);
+
+ $mock = $this->expectCallableNever();
+
+ $this->server->on('connection', function ($conn) use ($mock) {
+ $conn->on('end', $mock);
+ });
+ $this->tick();
+ $this->tick();
+ }
+
+ /**
+ * @covers React\Socket\Connection::end
+ */
+ public function testConnectionDoesEndWhenClientCloses()
+ {
+ $client = stream_socket_client('tcp://localhost:'.$this->port);
+
+ fclose($client);
+
+ $mock = $this->expectCallableOnce();
+
+ $this->server->on('connection', function ($conn) use ($mock) {
+ $conn->on('end', $mock);
+ });
+ $this->tick();
+ $this->tick();
+ }
+
+ public function testCtorAddsResourceToLoop()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('addReadStream');
+
+ $server = new TcpServer(0, $loop);
+ }
+
+ public function testResumeWithoutPauseIsNoOp()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('addReadStream');
+
+ $server = new TcpServer(0, $loop);
+ $server->resume();
+ }
+
+ public function testPauseRemovesResourceFromLoop()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('removeReadStream');
+
+ $server = new TcpServer(0, $loop);
+ $server->pause();
+ }
+
+ public function testPauseAfterPauseIsNoOp()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('removeReadStream');
+
+ $server = new TcpServer(0, $loop);
+ $server->pause();
+ $server->pause();
+ }
+
+ public function testCloseRemovesResourceFromLoop()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('removeReadStream');
+
+ $server = new TcpServer(0, $loop);
+ $server->close();
+ }
+
+ /**
+ * @expectedException RuntimeException
+ */
+ public function testListenOnBusyPortThrows()
+ {
+ if (DIRECTORY_SEPARATOR === '\\') {
+ $this->markTestSkipped('Windows supports listening on same port multiple times');
+ }
+
+ $another = new TcpServer($this->port, $this->loop);
+ }
+
+ /**
+ * @covers React\Socket\TcpServer::close
+ */
+ public function tearDown()
+ {
+ if ($this->server) {
+ $this->server->close();
+ }
+ }
+
+ private function tick()
+ {
+ Block\sleep(0, $this->loop);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/TestCase.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/TestCase.php
new file mode 100644
index 0000000..e87fc2f
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/TestCase.php
@@ -0,0 +1,101 @@
+createCallableMock();
+ $mock
+ ->expects($this->exactly($amount))
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ protected function expectCallableOnce()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ protected function expectCallableOnceWith($value)
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($value);
+
+ return $mock;
+ }
+
+ protected function expectCallableNever()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->never())
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ protected function createCallableMock()
+ {
+ return $this->getMockBuilder('React\Tests\Socket\Stub\CallableStub')->getMock();
+ }
+
+ protected function buffer(ReadableStreamInterface $stream, LoopInterface $loop, $timeout)
+ {
+ if (!$stream->isReadable()) {
+ return '';
+ }
+
+ return Block\await(new Promise(
+ function ($resolve, $reject) use ($stream) {
+ $buffer = '';
+ $stream->on('data', function ($chunk) use (&$buffer) {
+ $buffer .= $chunk;
+ });
+
+ $stream->on('error', $reject);
+
+ $stream->on('close', function () use (&$buffer, $resolve) {
+ $resolve($buffer);
+ });
+ },
+ function () use ($stream) {
+ $stream->close();
+ throw new \RuntimeException();
+ }
+ ), $loop, $timeout);
+ }
+
+ public function setExpectedException($exception, $exceptionMessage = '', $exceptionCode = null)
+ {
+ if (method_exists($this, 'expectException')) {
+ // PHPUnit 5+
+ $this->expectException($exception);
+ if ($exceptionMessage !== '') {
+ $this->expectExceptionMessage($exceptionMessage);
+ }
+ if ($exceptionCode !== null) {
+ $this->expectExceptionCode($exceptionCode);
+ }
+ } else {
+ // legacy PHPUnit 4
+ parent::setExpectedException($exception, $exceptionMessage, $exceptionCode);
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/TimeoutConnectorTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/TimeoutConnectorTest.php
new file mode 100644
index 0000000..64787d9
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/TimeoutConnectorTest.php
@@ -0,0 +1,103 @@
+getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $connector->expects($this->once())->method('connect')->with('google.com:80')->will($this->returnValue($promise));
+
+ $loop = Factory::create();
+
+ $timeout = new TimeoutConnector($connector, 0.01, $loop);
+
+ $timeout->connect('google.com:80')->then(
+ $this->expectCallableNever(),
+ $this->expectCallableOnce()
+ );
+
+ $loop->run();
+ }
+
+ public function testRejectsWhenConnectorRejects()
+ {
+ $promise = Promise\reject(new \RuntimeException());
+
+ $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $connector->expects($this->once())->method('connect')->with('google.com:80')->will($this->returnValue($promise));
+
+ $loop = Factory::create();
+
+ $timeout = new TimeoutConnector($connector, 5.0, $loop);
+
+ $timeout->connect('google.com:80')->then(
+ $this->expectCallableNever(),
+ $this->expectCallableOnce()
+ );
+
+ $loop->run();
+ }
+
+ public function testResolvesWhenConnectorResolves()
+ {
+ $promise = Promise\resolve();
+
+ $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $connector->expects($this->once())->method('connect')->with('google.com:80')->will($this->returnValue($promise));
+
+ $loop = Factory::create();
+
+ $timeout = new TimeoutConnector($connector, 5.0, $loop);
+
+ $timeout->connect('google.com:80')->then(
+ $this->expectCallableOnce(),
+ $this->expectCallableNever()
+ );
+
+ $loop->run();
+ }
+
+ public function testRejectsAndCancelsPendingPromiseOnTimeout()
+ {
+ $promise = new Promise\Promise(function () { }, $this->expectCallableOnce());
+
+ $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $connector->expects($this->once())->method('connect')->with('google.com:80')->will($this->returnValue($promise));
+
+ $loop = Factory::create();
+
+ $timeout = new TimeoutConnector($connector, 0.01, $loop);
+
+ $timeout->connect('google.com:80')->then(
+ $this->expectCallableNever(),
+ $this->expectCallableOnce()
+ );
+
+ $loop->run();
+ }
+
+ public function testCancelsPendingPromiseOnCancel()
+ {
+ $promise = new Promise\Promise(function () { }, function () { throw new \Exception(); });
+
+ $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
+ $connector->expects($this->once())->method('connect')->with('google.com:80')->will($this->returnValue($promise));
+
+ $loop = Factory::create();
+
+ $timeout = new TimeoutConnector($connector, 0.01, $loop);
+
+ $out = $timeout->connect('google.com:80');
+ $out->cancel();
+
+ $out->then($this->expectCallableNever(), $this->expectCallableOnce());
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/UnixConnectorTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/UnixConnectorTest.php
new file mode 100644
index 0000000..1564064
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/UnixConnectorTest.php
@@ -0,0 +1,64 @@
+loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $this->connector = new UnixConnector($this->loop);
+ }
+
+ public function testInvalid()
+ {
+ $promise = $this->connector->connect('google.com:80');
+ $promise->then(null, $this->expectCallableOnce());
+ }
+
+ public function testInvalidScheme()
+ {
+ $promise = $this->connector->connect('tcp://google.com:80');
+ $promise->then(null, $this->expectCallableOnce());
+ }
+
+ public function testValid()
+ {
+ // random unix domain socket path
+ $path = sys_get_temp_dir() . '/test' . uniqid() . '.sock';
+
+ // temporarily create unix domain socket server to connect to
+ $server = stream_socket_server('unix://' . $path, $errno, $errstr);
+
+ // skip test if we can not create a test server (Windows etc.)
+ if (!$server) {
+ $this->markTestSkipped('Unable to create socket "' . $path . '": ' . $errstr . '(' . $errno .')');
+ return;
+ }
+
+ // tests succeeds if we get notified of successful connection
+ $promise = $this->connector->connect($path);
+ $promise->then($this->expectCallableOnce());
+
+ // remember remote and local address of this connection and close again
+ $remote = $local = false;
+ $promise->then(function(ConnectionInterface $conn) use (&$remote, &$local) {
+ $remote = $conn->getRemoteAddress();
+ $local = $conn->getLocalAddress();
+ $conn->close();
+ });
+
+ // clean up server
+ fclose($server);
+ unlink($path);
+
+ $this->assertNull($local);
+ $this->assertEquals('unix://' . $path, $remote);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/UnixServerTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/UnixServerTest.php
new file mode 100644
index 0000000..10f7e4f
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/socket/tests/UnixServerTest.php
@@ -0,0 +1,283 @@
+loop = Factory::create();
+ $this->uds = $this->getRandomSocketUri();
+ $this->server = new UnixServer($this->uds, $this->loop);
+ }
+
+ /**
+ * @covers React\Socket\UnixServer::handleConnection
+ */
+ public function testConnection()
+ {
+ $client = stream_socket_client($this->uds);
+
+ $this->server->on('connection', $this->expectCallableOnce());
+ $this->tick();
+ }
+
+ /**
+ * @covers React\Socket\UnixServer::handleConnection
+ */
+ public function testConnectionWithManyClients()
+ {
+ $client1 = stream_socket_client($this->uds);
+ $client2 = stream_socket_client($this->uds);
+ $client3 = stream_socket_client($this->uds);
+
+ $this->server->on('connection', $this->expectCallableExactly(3));
+ $this->tick();
+ $this->tick();
+ $this->tick();
+ }
+
+ public function testDataEventWillNotBeEmittedWhenClientSendsNoData()
+ {
+ $client = stream_socket_client($this->uds);
+
+ $mock = $this->expectCallableNever();
+
+ $this->server->on('connection', function ($conn) use ($mock) {
+ $conn->on('data', $mock);
+ });
+ $this->tick();
+ $this->tick();
+ }
+
+ public function testDataWillBeEmittedWithDataClientSends()
+ {
+ $client = stream_socket_client($this->uds);
+
+ fwrite($client, "foo\n");
+
+ $mock = $this->expectCallableOnceWith("foo\n");
+
+ $this->server->on('connection', function ($conn) use ($mock) {
+ $conn->on('data', $mock);
+ });
+ $this->tick();
+ $this->tick();
+ }
+
+ public function testDataWillBeEmittedEvenWhenClientShutsDownAfterSending()
+ {
+ $client = stream_socket_client($this->uds);
+ fwrite($client, "foo\n");
+ stream_socket_shutdown($client, STREAM_SHUT_WR);
+
+ $mock = $this->expectCallableOnceWith("foo\n");
+
+ $this->server->on('connection', function ($conn) use ($mock) {
+ $conn->on('data', $mock);
+ });
+ $this->tick();
+ $this->tick();
+ }
+
+ public function testLoopWillEndWhenServerIsClosed()
+ {
+ // explicitly unset server because we already call close()
+ $this->server->close();
+ $this->server = null;
+
+ $this->loop->run();
+
+ // if we reach this, then everything is good
+ $this->assertNull(null);
+ }
+
+ public function testCloseTwiceIsNoOp()
+ {
+ $this->server->close();
+ $this->server->close();
+
+ // if we reach this, then everything is good
+ $this->assertNull(null);
+ }
+
+ public function testGetAddressAfterCloseReturnsNull()
+ {
+ $this->server->close();
+ $this->assertNull($this->server->getAddress());
+ }
+
+ public function testLoopWillEndWhenServerIsClosedAfterSingleConnection()
+ {
+ $client = stream_socket_client($this->uds);
+
+ // explicitly unset server because we only accept a single connection
+ // and then already call close()
+ $server = $this->server;
+ $this->server = null;
+
+ $server->on('connection', function ($conn) use ($server) {
+ $conn->close();
+ $server->close();
+ });
+
+ $this->loop->run();
+
+ // if we reach this, then everything is good
+ $this->assertNull(null);
+ }
+
+ public function testDataWillBeEmittedInMultipleChunksWhenClientSendsExcessiveAmounts()
+ {
+ $client = stream_socket_client($this->uds);
+ $stream = new DuplexResourceStream($client, $this->loop);
+
+ $bytes = 1024 * 1024;
+ $stream->end(str_repeat('*', $bytes));
+
+ $mock = $this->expectCallableOnce();
+
+ // explicitly unset server because we only accept a single connection
+ // and then already call close()
+ $server = $this->server;
+ $this->server = null;
+
+ $received = 0;
+ $server->on('connection', function ($conn) use ($mock, &$received, $server) {
+ // count number of bytes received
+ $conn->on('data', function ($data) use (&$received) {
+ $received += strlen($data);
+ });
+
+ $conn->on('end', $mock);
+
+ // do not await any further connections in order to let the loop terminate
+ $server->close();
+ });
+
+ $this->loop->run();
+
+ $this->assertEquals($bytes, $received);
+ }
+
+ public function testConnectionDoesNotEndWhenClientDoesNotClose()
+ {
+ $client = stream_socket_client($this->uds);
+
+ $mock = $this->expectCallableNever();
+
+ $this->server->on('connection', function ($conn) use ($mock) {
+ $conn->on('end', $mock);
+ });
+ $this->tick();
+ $this->tick();
+ }
+
+ /**
+ * @covers React\Socket\Connection::end
+ */
+ public function testConnectionDoesEndWhenClientCloses()
+ {
+ $client = stream_socket_client($this->uds);
+
+ fclose($client);
+
+ $mock = $this->expectCallableOnce();
+
+ $this->server->on('connection', function ($conn) use ($mock) {
+ $conn->on('end', $mock);
+ });
+ $this->tick();
+ $this->tick();
+ }
+
+ public function testCtorAddsResourceToLoop()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('addReadStream');
+
+ $server = new UnixServer($this->getRandomSocketUri(), $loop);
+ }
+
+ public function testResumeWithoutPauseIsNoOp()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('addReadStream');
+
+ $server = new UnixServer($this->getRandomSocketUri(), $loop);
+ $server->resume();
+ }
+
+ public function testPauseRemovesResourceFromLoop()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('removeReadStream');
+
+ $server = new UnixServer($this->getRandomSocketUri(), $loop);
+ $server->pause();
+ }
+
+ public function testPauseAfterPauseIsNoOp()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('removeReadStream');
+
+ $server = new UnixServer($this->getRandomSocketUri(), $loop);
+ $server->pause();
+ $server->pause();
+ }
+
+ public function testCloseRemovesResourceFromLoop()
+ {
+ $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ $loop->expects($this->once())->method('removeReadStream');
+
+ $server = new UnixServer($this->getRandomSocketUri(), $loop);
+ $server->close();
+ }
+
+ /**
+ * @expectedException RuntimeException
+ */
+ public function testListenOnBusyPortThrows()
+ {
+ if (DIRECTORY_SEPARATOR === '\\') {
+ $this->markTestSkipped('Windows supports listening on same port multiple times');
+ }
+
+ $another = new UnixServer($this->uds, $this->loop);
+ }
+
+ /**
+ * @covers React\Socket\UnixServer::close
+ */
+ public function tearDown()
+ {
+ if ($this->server) {
+ $this->server->close();
+ }
+ }
+
+ private function getRandomSocketUri()
+ {
+ return "unix://" . sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid(rand(), true) . '.sock';
+ }
+
+ private function tick()
+ {
+ Block\sleep(0, $this->loop);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/.gitignore b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/.gitignore
new file mode 100644
index 0000000..987e2a2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/.gitignore
@@ -0,0 +1,2 @@
+composer.lock
+vendor
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/.travis.yml b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/.travis.yml
new file mode 100644
index 0000000..f4e3376
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/.travis.yml
@@ -0,0 +1,50 @@
+language: php
+
+php:
+# - 5.3 # requires old distro, see below
+ - 5.4
+ - 5.5
+ - 5.6
+ - 7.0
+# - 7.0 # Mac OS X test setup, ignore errors, see below
+ - 7.1
+ - 7.2
+ - nightly # ignore errors, see below
+ - hhvm # ignore errors, see below
+
+# lock distro so new future defaults will not break the build
+dist: trusty
+
+matrix:
+ include:
+ - php: 5.3
+ dist: precise
+ include:
+ - os: osx
+ language: generic
+ php: 7.0 # just to look right on travis
+ env:
+ - PACKAGE: php70
+ allow_failures:
+ - php: nightly
+ - php: hhvm
+ - os: osx
+
+install:
+ # OSX install inspired by https://github.com/kiler129/TravisCI-OSX-PHP
+ - |
+ if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
+ brew tap homebrew/homebrew-php
+ echo "Installing PHP ..."
+ brew install "${PACKAGE}"
+ brew install "${PACKAGE}"-xdebug
+ brew link "${PACKAGE}"
+ echo "Installing composer ..."
+ curl -s http://getcomposer.org/installer | php
+ mv composer.phar /usr/local/bin/composer
+ fi
+ - composer install --no-interaction
+
+script:
+ - vendor/bin/phpunit --coverage-text
+ - time php examples/91-benchmark-throughput.php
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/CHANGELOG.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/CHANGELOG.md
new file mode 100644
index 0000000..f64815d
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/CHANGELOG.md
@@ -0,0 +1,377 @@
+# Changelog
+
+## 0.7.7 (2018-01-19)
+
+* Improve test suite by fixing forward compatibility with upcoming EventLoop
+ releases, avoid risky tests and add test group to skip integration tests
+ relying on internet connection and apply appropriate test timeouts.
+ (#128, #131 and #132 by @clue)
+
+## 0.7.6 (2017-12-21)
+
+* Fix: Work around reading from unbuffered pipe stream in legacy PHP < 5.4.28 and PHP < 5.5.12
+ (#126 by @clue)
+
+* Improve test suite by simplifying test bootstrapping logic via Composer and
+ test against PHP 7.2
+ (#127 by @clue and #124 by @carusogabriel)
+
+## 0.7.5 (2017-11-20)
+
+* Fix: Igore excessive `fopen()` mode flags for `WritableResourceStream`
+ (#119 by @clue)
+
+* Fix: Fix forward compatibility with upcoming EventLoop releases
+ (#121 by @clue)
+
+* Restructure examples to ease getting started
+ (#123 by @clue)
+
+* Improve test suite by adding forward compatibility with PHPUnit 6 and
+ ignore Mac OS X test failures for now until Travis tests work again
+ (#122 by @gabriel-caruso and #120 by @clue)
+
+## 0.7.4 (2017-10-11)
+
+* Fix: Remove event listeners from `CompositeStream` once closed and
+ remove undocumented left-over `close` event argument
+ (#116 by @clue)
+
+* Minor documentation improvements: Fix wrong class name in example,
+ fix typos in README and
+ fix forward compatibility with upcoming EventLoop releases in example
+ (#113 by @docteurklein and #114 and #115 by @clue)
+
+* Improve test suite by running against Mac OS X on Travis
+ (#112 by @clue)
+
+## 0.7.3 (2017-08-05)
+
+* Improvement: Support Événement 3.0 a long side 2.0 and 1.0
+ (#108 by @WyriHaximus)
+
+* Readme: Corrected loop initialization in usage example
+ (#109 by @pulyavin)
+
+* Travis: Lock linux distribution preventing future builds from breaking
+ (#110 by @clue)
+
+## 0.7.2 (2017-06-15)
+
+* Bug fix: WritableResourceStream: Close the underlying stream when closing the stream.
+ (#107 by @WyriHaximus)
+
+## 0.7.1 (2017-05-20)
+
+* Feature: Add optional `$writeChunkSize` parameter to limit maximum number of
+ bytes to write at once.
+ (#105 by @clue)
+
+ ```php
+ $stream = new WritableResourceStream(STDOUT, $loop, null, 8192);
+ ```
+
+* Ignore HHVM test failures for now until Travis tests work again
+ (#106 by @clue)
+
+## 0.7.0 (2017-05-04)
+
+* Removed / BC break: Remove deprecated and unneeded functionality
+ (#45, #87, #90, #91 and #93 by @clue)
+
+ * Remove deprecated `Stream` class, use `DuplexResourceStream` instead
+ (#87 by @clue)
+
+ * Remove public `$buffer` property, use new constructor parameters instead
+ (#91 by @clue)
+
+ * Remove public `$stream` property from all resource streams
+ (#90 by @clue)
+
+ * Remove undocumented and now unused `ReadableStream` and `WritableStream`
+ (#93 by @clue)
+
+ * Remove `BufferedSink`
+ (#45 by @clue)
+
+* Feature / BC break: Simplify `ThroughStream` by using data callback instead of
+ inheritance. It is now a direct implementation of `DuplexStreamInterface`.
+ (#88 and #89 by @clue)
+
+ ```php
+ $through = new ThroughStream(function ($data) {
+ return json_encode($data) . PHP_EOL;
+ });
+ $through->on('data', $this->expectCallableOnceWith("[2, true]\n"));
+
+ $through->write(array(2, true));
+ ```
+
+* Feature / BC break: The `CompositeStream` starts closed if either side is
+ already closed and forwards pause to pipe source on first write attempt.
+ (#96 and #103 by @clue)
+
+ If either side of the composite stream closes, it will also close the other
+ side. We now also ensure that if either side is already closed during
+ instantiation, it will also close the other side.
+
+* BC break: Mark all classes as `final` and
+ mark internal API as `private` to discourage inheritance
+ (#95 and #99 by @clue)
+
+* Feature / BC break: Only emit `error` event for fatal errors
+ (#92 by @clue)
+
+ > The `error` event was previously also allowed to be emitted for non-fatal
+ errors, but our implementations actually only ever emitted this as a fatal
+ error and then closed the stream.
+
+* Feature: Explicitly allow custom events and exclude any semantics
+ (#97 by @clue)
+
+* Support legacy PHP 5.3 through PHP 7.1 and HHVM and improve usage documentation
+ (#100 and #102 by @clue)
+
+* Actually require all dependencies so this is self-contained and improve
+ forward compatibility with EventLoop v1.0 and v0.5
+ (#94 and #98 by @clue)
+
+## 0.6.0 (2017-03-26)
+
+* Feature / Fix / BC break: Add `DuplexResourceStream` and deprecate `Stream`
+ (#85 by @clue)
+
+ ```php
+ // old (does still work for BC reasons)
+ $stream = new Stream($connection, $loop);
+
+ // new
+ $stream = new DuplexResourceStream($connection, $loop);
+ ```
+
+ Note that the `DuplexResourceStream` now rejects read-only or write-only
+ streams, so this may affect BC. If you want a read-only or write-only
+ resource, use `ReadableResourceStream` or `WritableResourceStream` instead of
+ `DuplexResourceStream`.
+
+ > BC note: This class was previously called `Stream`. The `Stream` class still
+ exists for BC reasons and will be removed in future versions of this package.
+
+* Feature / BC break: Add `WritableResourceStream` (previously called `Buffer`)
+ (#84 by @clue)
+
+ ```php
+ // old
+ $stream = new Buffer(STDOUT, $loop);
+
+ // new
+ $stream = new WritableResourceStream(STDOUT, $loop);
+ ```
+
+* Feature: Add `ReadableResourceStream`
+ (#83 by @clue)
+
+ ```php
+ $stream = new ReadableResourceStream(STDIN, $loop);
+ ```
+
+* Fix / BC Break: Enforce using non-blocking I/O
+ (#46 by @clue)
+
+ > BC note: This is known to affect process pipes on Windows which do not
+ support non-blocking I/O and could thus block the whole EventLoop previously.
+
+* Feature / Fix / BC break: Consistent semantics for
+ `DuplexStreamInterface::end()` to ensure it SHOULD also end readable side
+ (#86 by @clue)
+
+* Fix: Do not use unbuffered reads on pipe streams for legacy PHP < 5.4
+ (#80 by @clue)
+
+## 0.5.0 (2017-03-08)
+
+* Feature / BC break: Consistent `end` event semantics (EOF)
+ (#70 by @clue)
+
+ The `end` event will now only be emitted for a *successful* end, not if the
+ stream closes due to an unrecoverable `error` event or if you call `close()`
+ explicitly.
+ If you want to detect when the stream closes (terminates), use the `close`
+ event instead.
+
+* BC break: Remove custom (undocumented) `full-drain` event from `Buffer`
+ (#63 and #68 by @clue)
+
+ > The `full-drain` event was undocumented and mostly used internally.
+ Relying on this event has attracted some low-quality code in the past, so
+ we've removed this from the public API in order to work out a better
+ solution instead.
+ If you want to detect when the buffer finishes flushing data to the stream,
+ you may want to look into its `end()` method or the `close` event instead.
+
+* Feature / BC break: Consistent event semantics and documentation,
+ explicitly state *when* events will be emitted and *which* arguments they
+ receive.
+ (#73 and #69 by @clue)
+
+ The documentation now explicitly defines each event and its arguments.
+ Custom events and event arguments are still supported.
+ Most notably, all defined events only receive inherently required event
+ arguments and no longer transmit the instance they are emitted on for
+ consistency and performance reasons.
+
+ ```php
+ // old (inconsistent and not supported by all implementations)
+ $stream->on('data', function ($data, $stream) {
+ // process $data
+ });
+
+ // new (consistent throughout the whole ecosystem)
+ $stream->on('data', function ($data) use ($stream) {
+ // process $data
+ });
+ ```
+
+ > This mostly adds documentation (and thus some stricter, consistent
+ definitions) for the existing behavior, it does NOT define any major
+ changes otherwise.
+ Most existing code should be compatible with these changes, unless
+ it relied on some undocumented/unintended semantics.
+
+* Feature / BC break: Consistent method semantics and documentation
+ (#72 by @clue)
+
+ > This mostly adds documentation (and thus some stricter, consistent
+ definitions) for the existing behavior, it does NOT define any major
+ changes otherwise.
+ Most existing code should be compatible with these changes, unless
+ it relied on some undocumented/unintended semantics.
+
+* Feature: Consistent `pipe()` semantics for closed and closing streams
+ (#71 from @clue)
+
+ The source stream will now always be paused via `pause()` when the
+ destination stream closes. Also, properly stop piping if the source
+ stream closes and remove all event forwarding.
+
+* Improve test suite by adding PHPUnit to `require-dev` and improving coverage.
+ (#74 and #75 by @clue, #66 by @nawarian)
+
+## 0.4.6 (2017-01-25)
+
+* Feature: The `Buffer` can now be injected into the `Stream` (or be used standalone)
+ (#62 by @clue)
+
+* Fix: Forward `close` event only once for `CompositeStream` and `ThroughStream`
+ (#60 by @clue)
+
+* Fix: Consistent `close` event behavior for `Buffer`
+ (#61 by @clue)
+
+## 0.4.5 (2016-11-13)
+
+* Feature: Support setting read buffer size to `null` (infinite)
+ (#42 by @clue)
+
+* Fix: Do not emit `full-drain` event if `Buffer` is closed during `drain` event
+ (#55 by @clue)
+
+* Vastly improved performance by factor of 10x to 20x.
+ Raise default buffer sizes to 64 KiB and simplify and improve error handling
+ and unneeded function calls.
+ (#53, #55, #56 by @clue)
+
+## 0.4.4 (2016-08-22)
+
+* Bug fix: Emit `error` event and close `Stream` when accessing the underlying
+ stream resource fails with a permanent error.
+ (#52 and #40 by @clue, #25 by @lysenkobv)
+
+* Bug fix: Do not emit empty `data` event if nothing has been read (stream reached EOF)
+ (#39 by @clue)
+
+* Bug fix: Ignore empty writes to `Buffer`
+ (#51 by @clue)
+
+* Add benchmarking script to measure throughput in CI
+ (#41 by @clue)
+
+## 0.4.3 (2015-10-07)
+
+* Bug fix: Read buffer to 0 fixes error with libevent and large quantity of I/O (@mbonneau)
+* Bug fix: No double-write during drain call (@arnaud-lb)
+* Bug fix: Support HHVM (@clue)
+* Adjust compatibility to 5.3 (@clue)
+
+## 0.4.2 (2014-09-09)
+
+* Added DuplexStreamInterface
+* Stream sets stream resources to non-blocking
+* Fixed potential race condition in pipe
+
+## 0.4.1 (2014-04-13)
+
+* Bug fix: v0.3.4 changes merged for v0.4.1
+
+## 0.3.4 (2014-03-30)
+
+* Bug fix: [Stream] Fixed 100% CPU spike from non-empty write buffer on closed stream
+
+## 0.4.0 (2014-02-02)
+
+* BC break: Bump minimum PHP version to PHP 5.4, remove 5.3 specific hacks
+* BC break: Update to Evenement 2.0
+* Dependency: Autoloading and filesystem structure now PSR-4 instead of PSR-0
+
+## 0.3.3 (2013-07-08)
+
+* Bug fix: [Stream] Correctly detect closed connections
+
+## 0.3.2 (2013-05-10)
+
+* Bug fix: [Stream] Make sure CompositeStream is closed properly
+
+## 0.3.1 (2013-04-21)
+
+* Bug fix: [Stream] Allow any `ReadableStreamInterface` on `BufferedSink::createPromise()`
+
+## 0.3.0 (2013-04-14)
+
+* Feature: [Stream] Factory method for BufferedSink
+
+## 0.2.6 (2012-12-26)
+
+* Version bump
+
+## 0.2.5 (2012-11-26)
+
+* Feature: Make BufferedSink trigger progress events on the promise (@jsor)
+
+## 0.2.4 (2012-11-18)
+
+* Feature: Added ThroughStream, CompositeStream, ReadableStream and WritableStream
+* Feature: Added BufferedSink
+
+## 0.2.3 (2012-11-14)
+
+* Version bump
+
+## 0.2.2 (2012-10-28)
+
+* Version bump
+
+## 0.2.1 (2012-10-14)
+
+* Bug fix: Check for EOF in `Buffer::write()`
+
+## 0.2.0 (2012-09-10)
+
+* Version bump
+
+## 0.1.1 (2012-07-12)
+
+* Bug fix: Testing and functional against PHP >= 5.3.3 and <= 5.3.8
+
+## 0.1.0 (2012-07-11)
+
+* First tagged release
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/LICENSE b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/LICENSE
new file mode 100644
index 0000000..a808108
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2012 Igor Wiedler, Chris Boden
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/README.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/README.md
new file mode 100644
index 0000000..c362534
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/README.md
@@ -0,0 +1,1224 @@
+# Stream
+
+[](https://travis-ci.org/reactphp/stream)
+
+Event-driven readable and writable streams for non-blocking I/O in [ReactPHP](https://reactphp.org/).
+
+In order to make the [EventLoop](https://github.com/reactphp/event-loop)
+easier to use, this component introduces the powerful concept of "streams".
+Streams allow you to efficiently process huge amounts of data (such as a multi
+Gigabyte file download) in small chunks without having to store everything in
+memory at once.
+They are very similar to the streams found in PHP itself,
+but have an interface more suited for async, non-blocking I/O.
+
+**Table of contents**
+
+* [Stream usage](#stream-usage)
+ * [ReadableStreamInterface](#readablestreaminterface)
+ * [data event](#data-event)
+ * [end event](#end-event)
+ * [error event](#error-event)
+ * [close event](#close-event)
+ * [isReadable()](#isreadable)
+ * [pause()](#pause)
+ * [resume()](#resume)
+ * [pipe()](#pipe)
+ * [close()](#close)
+ * [WritableStreamInterface](#writablestreaminterface)
+ * [drain event](#drain-event)
+ * [pipe event](#pipe-event)
+ * [error event](#error-event-1)
+ * [close event](#close-event-1)
+ * [isWritable()](#iswritable)
+ * [write()](#write)
+ * [end()](#end)
+ * [close()](#close-1)
+ * [DuplexStreamInterface](#duplexstreaminterface)
+* [Creating streams](#creating-streams)
+ * [ReadableResourceStream](#readableresourcestream)
+ * [WritableResourceStream](#writableresourcestream)
+ * [DuplexResourceStream](#duplexresourcestream)
+ * [ThroughStream](#throughstream)
+ * [CompositeStream](#compositestream)
+* [Usage](#usage)
+* [Install](#install)
+* [Tests](#tests)
+* [License](#license)
+* [More](#more)
+
+## Stream usage
+
+ReactPHP uses the concept of "streams" throughout its ecosystem to provide a
+consistent higher-level abstraction for processing streams of arbitrary data
+contents and size.
+While a stream itself is a quite low-level concept, it can be used as a powerful
+abstraction to build higher-level components and protocols on top.
+
+If you're new to this concept, it helps to think of them as a water pipe:
+You can consume water from a source or you can produce water and forward (pipe)
+it to any destination (sink).
+
+Similarly, streams can either be
+
+* readable (such as `STDIN` terminal input) or
+* writable (such as `STDOUT` terminal output) or
+* duplex (both readable *and* writable, such as a TCP/IP connection)
+
+Accordingly, this package defines the following three interfaces
+
+* [`ReadableStreamInterface`](#readablestreaminterface)
+* [`WritableStreamInterface`](#writablestreaminterface)
+* [`DuplexStreamInterface`](#duplexstreaminterface)
+
+### ReadableStreamInterface
+
+The `ReadableStreamInterface` is responsible for providing an interface for
+read-only streams and the readable side of duplex streams.
+
+Besides defining a few methods, this interface also implements the
+`EventEmitterInterface` which allows you to react to certain events.
+
+The event callback functions MUST be a valid `callable` that obeys strict
+parameter definitions and MUST accept event parameters exactly as documented.
+The event callback functions MUST NOT throw an `Exception`.
+The return value of the event callback functions will be ignored and has no
+effect, so for performance reasons you're recommended to not return any
+excessive data structures.
+
+Every implementation of this interface MUST follow these event semantics in
+order to be considered a well-behaving stream.
+
+> Note that higher-level implementations of this interface may choose to
+ define additional events with dedicated semantics not defined as part of
+ this low-level stream specification. Conformance with these event semantics
+ is out of scope for this interface, so you may also have to refer to the
+ documentation of such a higher-level implementation.
+
+#### data event
+
+The `data` event will be emitted whenever some data was read/received
+from this source stream.
+The event receives a single mixed argument for incoming data.
+
+```php
+$stream->on('data', function ($data) {
+ echo $data;
+});
+```
+
+This event MAY be emitted any number of times, which may be zero times if
+this stream does not send any data at all.
+It SHOULD not be emitted after an `end` or `close` event.
+
+The given `$data` argument may be of mixed type, but it's usually
+recommended it SHOULD be a `string` value or MAY use a type that allows
+representation as a `string` for maximum compatibility.
+
+Many common streams (such as a TCP/IP connection or a file-based stream)
+will emit the raw (binary) payload data that is received over the wire as
+chunks of `string` values.
+
+Due to the stream-based nature of this, the sender may send any number
+of chunks with varying sizes. There are no guarantees that these chunks
+will be received with the exact same framing the sender intended to send.
+In other words, many lower-level protocols (such as TCP/IP) transfer the
+data in chunks that may be anywhere between single-byte values to several
+dozens of kilobytes. You may want to apply a higher-level protocol to
+these low-level data chunks in order to achieve proper message framing.
+
+#### end event
+
+The `end` event will be emitted once the source stream has successfully
+reached the end of the stream (EOF).
+
+```php
+$stream->on('end', function () {
+ echo 'END';
+});
+```
+
+This event SHOULD be emitted once or never at all, depending on whether
+a successful end was detected.
+It SHOULD NOT be emitted after a previous `end` or `close` event.
+It MUST NOT be emitted if the stream closes due to a non-successful
+end, such as after a previous `error` event.
+
+After the stream is ended, it MUST switch to non-readable mode,
+see also `isReadable()`.
+
+This event will only be emitted if the *end* was reached successfully,
+not if the stream was interrupted by an unrecoverable error or explicitly
+closed. Not all streams know this concept of a "successful end".
+Many use-cases involve detecting when the stream closes (terminates)
+instead, in this case you should use the `close` event.
+After the stream emits an `end` event, it SHOULD usually be followed by a
+`close` event.
+
+Many common streams (such as a TCP/IP connection or a file-based stream)
+will emit this event if either the remote side closes the connection or
+a file handle was successfully read until reaching its end (EOF).
+
+Note that this event should not be confused with the `end()` method.
+This event defines a successful end *reading* from a source stream, while
+the `end()` method defines *writing* a successful end to a destination
+stream.
+
+#### error event
+
+The `error` event will be emitted once a fatal error occurs, usually while
+trying to read from this stream.
+The event receives a single `Exception` argument for the error instance.
+
+```php
+$server->on('error', function (Exception $e) {
+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
+});
+```
+
+This event SHOULD be emitted once the stream detects a fatal error, such
+as a fatal transmission error or after an unexpected `data` or premature
+`end` event.
+It SHOULD NOT be emitted after a previous `error`, `end` or `close` event.
+It MUST NOT be emitted if this is not a fatal error condition, such as
+a temporary network issue that did not cause any data to be lost.
+
+After the stream errors, it MUST close the stream and SHOULD thus be
+followed by a `close` event and then switch to non-readable mode, see
+also `close()` and `isReadable()`.
+
+Many common streams (such as a TCP/IP connection or a file-based stream)
+only deal with data transmission and do not make assumption about data
+boundaries (such as unexpected `data` or premature `end` events).
+In other words, many lower-level protocols (such as TCP/IP) may choose
+to only emit this for a fatal transmission error once and will then
+close (terminate) the stream in response.
+
+If this stream is a `DuplexStreamInterface`, you should also notice
+how the writable side of the stream also implements an `error` event.
+In other words, an error may occur while either reading or writing the
+stream which should result in the same error processing.
+
+#### close event
+
+The `close` event will be emitted once the stream closes (terminates).
+
+```php
+$stream->on('close', function () {
+ echo 'CLOSED';
+});
+```
+
+This event SHOULD be emitted once or never at all, depending on whether
+the stream ever terminates.
+It SHOULD NOT be emitted after a previous `close` event.
+
+After the stream is closed, it MUST switch to non-readable mode,
+see also `isReadable()`.
+
+Unlike the `end` event, this event SHOULD be emitted whenever the stream
+closes, irrespective of whether this happens implicitly due to an
+unrecoverable error or explicitly when either side closes the stream.
+If you only want to detect a *successful* end, you should use the `end`
+event instead.
+
+Many common streams (such as a TCP/IP connection or a file-based stream)
+will likely choose to emit this event after reading a *successful* `end`
+event or after a fatal transmission `error` event.
+
+If this stream is a `DuplexStreamInterface`, you should also notice
+how the writable side of the stream also implements a `close` event.
+In other words, after receiving this event, the stream MUST switch into
+non-writable AND non-readable mode, see also `isWritable()`.
+Note that this event should not be confused with the `end` event.
+
+#### isReadable()
+
+The `isReadable(): bool` method can be used to
+check whether this stream is in a readable state (not closed already).
+
+This method can be used to check if the stream still accepts incoming
+data events or if it is ended or closed already.
+Once the stream is non-readable, no further `data` or `end` events SHOULD
+be emitted.
+
+```php
+assert($stream->isReadable() === false);
+
+$stream->on('data', assertNeverCalled());
+$stream->on('end', assertNeverCalled());
+```
+
+A successfully opened stream always MUST start in readable mode.
+
+Once the stream ends or closes, it MUST switch to non-readable mode.
+This can happen any time, explicitly through `close()` or
+implicitly due to a remote close or an unrecoverable transmission error.
+Once a stream has switched to non-readable mode, it MUST NOT transition
+back to readable mode.
+
+If this stream is a `DuplexStreamInterface`, you should also notice
+how the writable side of the stream also implements an `isWritable()`
+method. Unless this is a half-open duplex stream, they SHOULD usually
+have the same return value.
+
+#### pause()
+
+The `pause(): void` method can be used to
+pause reading incoming data events.
+
+Removes the data source file descriptor from the event loop. This
+allows you to throttle incoming data.
+
+Unless otherwise noted, a successfully opened stream SHOULD NOT start
+in paused state.
+
+Once the stream is paused, no futher `data` or `end` events SHOULD
+be emitted.
+
+```php
+$stream->pause();
+
+$stream->on('data', assertShouldNeverCalled());
+$stream->on('end', assertShouldNeverCalled());
+```
+
+This method is advisory-only, though generally not recommended, the
+stream MAY continue emitting `data` events.
+
+You can continue processing events by calling `resume()` again.
+
+Note that both methods can be called any number of times, in particular
+calling `pause()` more than once SHOULD NOT have any effect.
+
+See also `resume()`.
+
+#### resume()
+
+The `resume(): void` method can be used to
+resume reading incoming data events.
+
+Re-attach the data source after a previous `pause()`.
+
+```php
+$stream->pause();
+
+$loop->addTimer(1.0, function () use ($stream) {
+ $stream->resume();
+});
+```
+
+Note that both methods can be called any number of times, in particular
+calling `resume()` without a prior `pause()` SHOULD NOT have any effect.
+
+See also `pause()`.
+
+#### pipe()
+
+The `pipe(WritableStreamInterface $dest, array $options = [])` method can be used to
+pipe all the data from this readable source into the given writable destination.
+
+Automatically sends all incoming data to the destination.
+Automatically throttles the source based on what the destination can handle.
+
+```php
+$source->pipe($dest);
+```
+
+Similarly, you can also pipe an instance implementing `DuplexStreamInterface`
+into itself in order to write back all the data that is received.
+This may be a useful feature for a TCP/IP echo service:
+
+```php
+$connection->pipe($connection);
+```
+
+This method returns the destination stream as-is, which can be used to
+set up chains of piped streams:
+
+```php
+$source->pipe($decodeGzip)->pipe($filterBadWords)->pipe($dest);
+```
+
+By default, this will call `end()` on the destination stream once the
+source stream emits an `end` event. This can be disabled like this:
+
+```php
+$source->pipe($dest, array('end' => false));
+```
+
+Note that this only applies to the `end` event.
+If an `error` or explicit `close` event happens on the source stream,
+you'll have to manually close the destination stream:
+
+```php
+$source->pipe($dest);
+$source->on('close', function () use ($dest) {
+ $dest->end('BYE!');
+});
+```
+
+If the source stream is not readable (closed state), then this is a NO-OP.
+
+```php
+$source->close();
+$source->pipe($dest); // NO-OP
+```
+
+If the destinantion stream is not writable (closed state), then this will simply
+throttle (pause) the source stream:
+
+```php
+$dest->close();
+$source->pipe($dest); // calls $source->pause()
+```
+
+Similarly, if the destination stream is closed while the pipe is still
+active, it will also throttle (pause) the source stream:
+
+```php
+$source->pipe($dest);
+$dest->close(); // calls $source->pause()
+```
+
+Once the pipe is set up successfully, the destination stream MUST emit
+a `pipe` event with this source stream an event argument.
+
+#### close()
+
+The `close(): void` method can be used to
+close the stream (forcefully).
+
+This method can be used to (forcefully) close the stream.
+
+```php
+$stream->close();
+```
+
+Once the stream is closed, it SHOULD emit a `close` event.
+Note that this event SHOULD NOT be emitted more than once, in particular
+if this method is called multiple times.
+
+After calling this method, the stream MUST switch into a non-readable
+mode, see also `isReadable()`.
+This means that no further `data` or `end` events SHOULD be emitted.
+
+```php
+$stream->close();
+assert($stream->isReadable() === false);
+
+$stream->on('data', assertNeverCalled());
+$stream->on('end', assertNeverCalled());
+```
+
+If this stream is a `DuplexStreamInterface`, you should also notice
+how the writable side of the stream also implements a `close()` method.
+In other words, after calling this method, the stream MUST switch into
+non-writable AND non-readable mode, see also `isWritable()`.
+Note that this method should not be confused with the `end()` method.
+
+### WritableStreamInterface
+
+The `WritableStreamInterface` is responsible for providing an interface for
+write-only streams and the writable side of duplex streams.
+
+Besides defining a few methods, this interface also implements the
+`EventEmitterInterface` which allows you to react to certain events.
+
+The event callback functions MUST be a valid `callable` that obeys strict
+parameter definitions and MUST accept event parameters exactly as documented.
+The event callback functions MUST NOT throw an `Exception`.
+The return value of the event callback functions will be ignored and has no
+effect, so for performance reasons you're recommended to not return any
+excessive data structures.
+
+Every implementation of this interface MUST follow these event semantics in
+order to be considered a well-behaving stream.
+
+> Note that higher-level implementations of this interface may choose to
+ define additional events with dedicated semantics not defined as part of
+ this low-level stream specification. Conformance with these event semantics
+ is out of scope for this interface, so you may also have to refer to the
+ documentation of such a higher-level implementation.
+
+#### drain event
+
+The `drain` event will be emitted whenever the write buffer became full
+previously and is now ready to accept more data.
+
+```php
+$stream->on('drain', function () use ($stream) {
+ echo 'Stream is now ready to accept more data';
+});
+```
+
+This event SHOULD be emitted once every time the buffer became full
+previously and is now ready to accept more data.
+In other words, this event MAY be emitted any number of times, which may
+be zero times if the buffer never became full in the first place.
+This event SHOULD NOT be emitted if the buffer has not become full
+previously.
+
+This event is mostly used internally, see also `write()` for more details.
+
+#### pipe event
+
+The `pipe` event will be emitted whenever a readable stream is `pipe()`d
+into this stream.
+The event receives a single `ReadableStreamInterface` argument for the
+source stream.
+
+```php
+$stream->on('pipe', function (ReadableStreamInterface $source) use ($stream) {
+ echo 'Now receiving piped data';
+
+ // explicitly close target if source emits an error
+ $source->on('error', function () use ($stream) {
+ $stream->close();
+ });
+});
+
+$source->pipe($stream);
+```
+
+This event MUST be emitted once for each readable stream that is
+successfully piped into this destination stream.
+In other words, this event MAY be emitted any number of times, which may
+be zero times if no stream is ever piped into this stream.
+This event MUST NOT be emitted if either the source is not readable
+(closed already) or this destination is not writable (closed already).
+
+This event is mostly used internally, see also `pipe()` for more details.
+
+#### error event
+
+The `error` event will be emitted once a fatal error occurs, usually while
+trying to write to this stream.
+The event receives a single `Exception` argument for the error instance.
+
+```php
+$stream->on('error', function (Exception $e) {
+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
+});
+```
+
+This event SHOULD be emitted once the stream detects a fatal error, such
+as a fatal transmission error.
+It SHOULD NOT be emitted after a previous `error` or `close` event.
+It MUST NOT be emitted if this is not a fatal error condition, such as
+a temporary network issue that did not cause any data to be lost.
+
+After the stream errors, it MUST close the stream and SHOULD thus be
+followed by a `close` event and then switch to non-writable mode, see
+also `close()` and `isWritable()`.
+
+Many common streams (such as a TCP/IP connection or a file-based stream)
+only deal with data transmission and may choose
+to only emit this for a fatal transmission error once and will then
+close (terminate) the stream in response.
+
+If this stream is a `DuplexStreamInterface`, you should also notice
+how the readable side of the stream also implements an `error` event.
+In other words, an error may occur while either reading or writing the
+stream which should result in the same error processing.
+
+#### close event
+
+The `close` event will be emitted once the stream closes (terminates).
+
+```php
+$stream->on('close', function () {
+ echo 'CLOSED';
+});
+```
+
+This event SHOULD be emitted once or never at all, depending on whether
+the stream ever terminates.
+It SHOULD NOT be emitted after a previous `close` event.
+
+After the stream is closed, it MUST switch to non-writable mode,
+see also `isWritable()`.
+
+This event SHOULD be emitted whenever the stream closes, irrespective of
+whether this happens implicitly due to an unrecoverable error or
+explicitly when either side closes the stream.
+
+Many common streams (such as a TCP/IP connection or a file-based stream)
+will likely choose to emit this event after flushing the buffer from
+the `end()` method, after receiving a *successful* `end` event or after
+a fatal transmission `error` event.
+
+If this stream is a `DuplexStreamInterface`, you should also notice
+how the readable side of the stream also implements a `close` event.
+In other words, after receiving this event, the stream MUST switch into
+non-writable AND non-readable mode, see also `isReadable()`.
+Note that this event should not be confused with the `end` event.
+
+#### isWritable()
+
+The `isWritable(): bool` method can be used to
+check whether this stream is in a writable state (not closed already).
+
+This method can be used to check if the stream still accepts writing
+any data or if it is ended or closed already.
+Writing any data to a non-writable stream is a NO-OP:
+
+```php
+assert($stream->isWritable() === false);
+
+$stream->write('end'); // NO-OP
+$stream->end('end'); // NO-OP
+```
+
+A successfully opened stream always MUST start in writable mode.
+
+Once the stream ends or closes, it MUST switch to non-writable mode.
+This can happen any time, explicitly through `end()` or `close()` or
+implicitly due to a remote close or an unrecoverable transmission error.
+Once a stream has switched to non-writable mode, it MUST NOT transition
+back to writable mode.
+
+If this stream is a `DuplexStreamInterface`, you should also notice
+how the readable side of the stream also implements an `isReadable()`
+method. Unless this is a half-open duplex stream, they SHOULD usually
+have the same return value.
+
+#### write()
+
+The `write(mixed $data): bool` method can be used to
+write some data into the stream.
+
+A successful write MUST be confirmed with a boolean `true`, which means
+that either the data was written (flushed) immediately or is buffered and
+scheduled for a future write. Note that this interface gives you no
+control over explicitly flushing the buffered data, as finding the
+appropriate time for this is beyond the scope of this interface and left
+up to the implementation of this interface.
+
+Many common streams (such as a TCP/IP connection or file-based stream)
+may choose to buffer all given data and schedule a future flush by using
+an underlying EventLoop to check when the resource is actually writable.
+
+If a stream cannot handle writing (or flushing) the data, it SHOULD emit
+an `error` event and MAY `close()` the stream if it can not recover from
+this error.
+
+If the internal buffer is full after adding `$data`, then `write()`
+SHOULD return `false`, indicating that the caller should stop sending
+data until the buffer drains.
+The stream SHOULD send a `drain` event once the buffer is ready to accept
+more data.
+
+Similarly, if the the stream is not writable (already in a closed state)
+it MUST NOT process the given `$data` and SHOULD return `false`,
+indicating that the caller should stop sending data.
+
+The given `$data` argument MAY be of mixed type, but it's usually
+recommended it SHOULD be a `string` value or MAY use a type that allows
+representation as a `string` for maximum compatibility.
+
+Many common streams (such as a TCP/IP connection or a file-based stream)
+will only accept the raw (binary) payload data that is transferred over
+the wire as chunks of `string` values.
+
+Due to the stream-based nature of this, the sender may send any number
+of chunks with varying sizes. There are no guarantees that these chunks
+will be received with the exact same framing the sender intended to send.
+In other words, many lower-level protocols (such as TCP/IP) transfer the
+data in chunks that may be anywhere between single-byte values to several
+dozens of kilobytes. You may want to apply a higher-level protocol to
+these low-level data chunks in order to achieve proper message framing.
+
+#### end()
+
+The `end(mixed $data = null): void` method can be used to
+successfully end the stream (after optionally sending some final data).
+
+This method can be used to successfully end the stream, i.e. close
+the stream after sending out all data that is currently buffered.
+
+```php
+$stream->write('hello');
+$stream->write('world');
+$stream->end();
+```
+
+If there's no data currently buffered and nothing to be flushed, then
+this method MAY `close()` the stream immediately.
+
+If there's still data in the buffer that needs to be flushed first, then
+this method SHOULD try to write out this data and only then `close()`
+the stream.
+Once the stream is closed, it SHOULD emit a `close` event.
+
+Note that this interface gives you no control over explicitly flushing
+the buffered data, as finding the appropriate time for this is beyond the
+scope of this interface and left up to the implementation of this
+interface.
+
+Many common streams (such as a TCP/IP connection or file-based stream)
+may choose to buffer all given data and schedule a future flush by using
+an underlying EventLoop to check when the resource is actually writable.
+
+You can optionally pass some final data that is written to the stream
+before ending the stream. If a non-`null` value is given as `$data`, then
+this method will behave just like calling `write($data)` before ending
+with no data.
+
+```php
+// shorter version
+$stream->end('bye');
+
+// same as longer version
+$stream->write('bye');
+$stream->end();
+```
+
+After calling this method, the stream MUST switch into a non-writable
+mode, see also `isWritable()`.
+This means that no further writes are possible, so any additional
+`write()` or `end()` calls have no effect.
+
+```php
+$stream->end();
+assert($stream->isWritable() === false);
+
+$stream->write('nope'); // NO-OP
+$stream->end(); // NO-OP
+```
+
+If this stream is a `DuplexStreamInterface`, calling this method SHOULD
+also end its readable side, unless the stream supports half-open mode.
+In other words, after calling this method, these streams SHOULD switch
+into non-writable AND non-readable mode, see also `isReadable()`.
+This implies that in this case, the stream SHOULD NOT emit any `data`
+or `end` events anymore.
+Streams MAY choose to use the `pause()` method logic for this, but
+special care may have to be taken to ensure a following call to the
+`resume()` method SHOULD NOT continue emitting readable events.
+
+Note that this method should not be confused with the `close()` method.
+
+#### close()
+
+The `close(): void` method can be used to
+close the stream (forcefully).
+
+This method can be used to forcefully close the stream, i.e. close
+the stream without waiting for any buffered data to be flushed.
+If there's still data in the buffer, this data SHOULD be discarded.
+
+```php
+$stream->close();
+```
+
+Once the stream is closed, it SHOULD emit a `close` event.
+Note that this event SHOULD NOT be emitted more than once, in particular
+if this method is called multiple times.
+
+After calling this method, the stream MUST switch into a non-writable
+mode, see also `isWritable()`.
+This means that no further writes are possible, so any additional
+`write()` or `end()` calls have no effect.
+
+```php
+$stream->close();
+assert($stream->isWritable() === false);
+
+$stream->write('nope'); // NO-OP
+$stream->end(); // NO-OP
+```
+
+Note that this method should not be confused with the `end()` method.
+Unlike the `end()` method, this method does not take care of any existing
+buffers and simply discards any buffer contents.
+Likewise, this method may also be called after calling `end()` on a
+stream in order to stop waiting for the stream to flush its final data.
+
+```php
+$stream->end();
+$loop->addTimer(1.0, function () use ($stream) {
+ $stream->close();
+});
+```
+
+If this stream is a `DuplexStreamInterface`, you should also notice
+how the readable side of the stream also implements a `close()` method.
+In other words, after calling this method, the stream MUST switch into
+non-writable AND non-readable mode, see also `isReadable()`.
+
+### DuplexStreamInterface
+
+The `DuplexStreamInterface` is responsible for providing an interface for
+duplex streams (both readable and writable).
+
+It builds on top of the existing interfaces for readable and writable streams
+and follows the exact same method and event semantics.
+If you're new to this concept, you should look into the
+`ReadableStreamInterface` and `WritableStreamInterface` first.
+
+Besides defining a few methods, this interface also implements the
+`EventEmitterInterface` which allows you to react to the same events defined
+on the `ReadbleStreamInterface` and `WritableStreamInterface`.
+
+The event callback functions MUST be a valid `callable` that obeys strict
+parameter definitions and MUST accept event parameters exactly as documented.
+The event callback functions MUST NOT throw an `Exception`.
+The return value of the event callback functions will be ignored and has no
+effect, so for performance reasons you're recommended to not return any
+excessive data structures.
+
+Every implementation of this interface MUST follow these event semantics in
+order to be considered a well-behaving stream.
+
+> Note that higher-level implementations of this interface may choose to
+ define additional events with dedicated semantics not defined as part of
+ this low-level stream specification. Conformance with these event semantics
+ is out of scope for this interface, so you may also have to refer to the
+ documentation of such a higher-level implementation.
+
+See also [`ReadableStreamInterface`](#readablestreaminterface) and
+[`WritableStreamInterface`](#writablestreaminterface) for more details.
+
+## Creating streams
+
+ReactPHP uses the concept of "streams" throughout its ecosystem, so that
+many higher-level consumers of this package only deal with
+[stream usage](#stream-usage).
+This implies that stream instances are most often created within some
+higher-level components and many consumers never actually have to deal with
+creating a stream instance.
+
+* Use [react/socket](https://github.com/reactphp/socket)
+ if you want to accept incoming or establish outgoing plaintext TCP/IP or
+ secure TLS socket connection streams.
+* Use [react/http](https://github.com/reactphp/http)
+ if you want to receive an incoming HTTP request body streams.
+* Use [react/child-process](https://github.com/reactphp/child-process)
+ if you want to communicate with child processes via process pipes such as
+ STDIN, STDOUT, STDERR etc.
+* Use experimental [react/filesystem](https://github.com/reactphp/filesystem)
+ if you want to read from / write to the filesystem.
+* See also the last chapter for [more real-world applications](#more).
+
+However, if you are writing a lower-level component or want to create a stream
+instance from a stream resource, then the following chapter is for you.
+
+> Note that the following examples use `fopen()` and `stream_socket_client()`
+ for illustration purposes only.
+ These functions SHOULD NOT be used in a truly async program because each call
+ may take several seconds to complete and would block the EventLoop otherwise.
+ Additionally, the `fopen()` call will return a file handle on some platforms
+ which may or may not be supported by all EventLoop implementations.
+ As an alternative, you may want to use higher-level libraries listed above.
+
+### ReadableResourceStream
+
+The `ReadableResourceStream` is a concrete implementation of the
+[`ReadableStreamInterface`](#readablestreaminterface) for PHP's stream resources.
+
+This can be used to represent a read-only resource like a file stream opened in
+readable mode or a stream such as `STDIN`:
+
+```php
+$stream = new ReadableResourceStream(STDIN, $loop);
+$stream->on('data', function ($chunk) {
+ echo $chunk;
+});
+$stream->on('end', function () {
+ echo 'END';
+});
+```
+
+See also [`ReadableStreamInterface`](#readablestreaminterface) for more details.
+
+The first parameter given to the constructor MUST be a valid stream resource
+that is opened in reading mode (e.g. `fopen()` mode `r`).
+Otherwise, it will throw an `InvalidArgumentException`:
+
+```php
+// throws InvalidArgumentException
+$stream = new ReadableResourceStream(false, $loop);
+```
+
+See also the [`DuplexResourceStream`](#readableresourcestream) for read-and-write
+stream resources otherwise.
+
+Internally, this class tries to enable non-blocking mode on the stream resource
+which may not be supported for all stream resources.
+Most notably, this is not supported by pipes on Windows (STDIN etc.).
+If this fails, it will throw a `RuntimeException`:
+
+```php
+// throws RuntimeException on Windows
+$stream = new ReadableResourceStream(STDIN, $loop);
+```
+
+Once the constructor is called with a valid stream resource, this class will
+take care of the underlying stream resource.
+You SHOULD only use its public API and SHOULD NOT interfere with the underlying
+stream resource manually.
+
+This class takes an optional `int|null $readChunkSize` parameter that controls
+the maximum buffer size in bytes to read at once from the stream.
+You can use a `null` value here in order to apply its default value.
+This value SHOULD NOT be changed unless you know what you're doing.
+This can be a positive number which means that up to X bytes will be read
+at once from the underlying stream resource. Note that the actual number
+of bytes read may be lower if the stream resource has less than X bytes
+currently available.
+This can be `-1` which means "read everything available" from the
+underlying stream resource.
+This should read until the stream resource is not readable anymore
+(i.e. underlying buffer drained), note that this does not neccessarily
+mean it reached EOF.
+
+```php
+$stream = new ReadableResourceStream(STDIN, $loop, 8192);
+```
+
+> PHP bug warning: If the PHP process has explicitly been started without a
+ `STDIN` stream, then trying to read from `STDIN` may return data from
+ another stream resource. This does not happen if you start this with an empty
+ stream like `php test.php < /dev/null` instead of `php test.php <&-`.
+ See [#81](https://github.com/reactphp/stream/issues/81) for more details.
+
+### WritableResourceStream
+
+The `WritableResourceStream` is a concrete implementation of the
+[`WritableStreamInterface`](#writablestreaminterface) for PHP's stream resources.
+
+This can be used to represent a write-only resource like a file stream opened in
+writable mode or a stream such as `STDOUT` or `STDERR`:
+
+```php
+$stream = new WritableResourceStream(STDOUT, $loop);
+$stream->write('hello!');
+$stream->end();
+```
+
+See also [`WritableStreamInterface`](#writablestreaminterface) for more details.
+
+The first parameter given to the constructor MUST be a valid stream resource
+that is opened for writing.
+Otherwise, it will throw an `InvalidArgumentException`:
+
+```php
+// throws InvalidArgumentException
+$stream = new WritableResourceStream(false, $loop);
+```
+
+See also the [`DuplexResourceStream`](#readableresourcestream) for read-and-write
+stream resources otherwise.
+
+Internally, this class tries to enable non-blocking mode on the stream resource
+which may not be supported for all stream resources.
+Most notably, this is not supported by pipes on Windows (STDOUT, STDERR etc.).
+If this fails, it will throw a `RuntimeException`:
+
+```php
+// throws RuntimeException on Windows
+$stream = new WritableResourceStream(STDOUT, $loop);
+```
+
+Once the constructor is called with a valid stream resource, this class will
+take care of the underlying stream resource.
+You SHOULD only use its public API and SHOULD NOT interfere with the underlying
+stream resource manually.
+
+Any `write()` calls to this class will not be performed instantly, but will
+be performed asynchronously, once the EventLoop reports the stream resource is
+ready to accept data.
+For this, it uses an in-memory buffer string to collect all outstanding writes.
+This buffer has a soft-limit applied which defines how much data it is willing
+to accept before the caller SHOULD stop sending further data.
+
+This class takes an optional `int|null $writeBufferSoftLimit` parameter that controls
+this maximum buffer size in bytes.
+You can use a `null` value here in order to apply its default value.
+This value SHOULD NOT be changed unless you know what you're doing.
+
+```php
+$stream = new WritableResourceStream(STDOUT, $loop, 8192);
+```
+
+This class takes an optional `int|null $writeChunkSize` parameter that controls
+this maximum buffer size in bytes to write at once to the stream.
+You can use a `null` value here in order to apply its default value.
+This value SHOULD NOT be changed unless you know what you're doing.
+This can be a positive number which means that up to X bytes will be written
+at once to the underlying stream resource. Note that the actual number
+of bytes written may be lower if the stream resource has less than X bytes
+currently available.
+This can be `-1` which means "write everything available" to the
+underlying stream resource.
+
+```php
+$stream = new WritableResourceStream(STDOUT, $loop, null, 8192);
+```
+
+See also [`write()`](#write) for more details.
+
+### DuplexResourceStream
+
+The `DuplexResourceStream` is a concrete implementation of the
+[`DuplexStreamInterface`](#duplexstreaminterface) for PHP's stream resources.
+
+This can be used to represent a read-and-write resource like a file stream opened
+in read and write mode mode or a stream such as a TCP/IP connection:
+
+```php
+$conn = stream_socket_client('tcp://google.com:80');
+$stream = new DuplexResourceStream($conn, $loop);
+$stream->write('hello!');
+$stream->end();
+```
+
+See also [`DuplexStreamInterface`](#duplexstreaminterface) for more details.
+
+The first parameter given to the constructor MUST be a valid stream resource
+that is opened for reading *and* writing.
+Otherwise, it will throw an `InvalidArgumentException`:
+
+```php
+// throws InvalidArgumentException
+$stream = new DuplexResourceStream(false, $loop);
+```
+
+See also the [`ReadableResourceStream`](#readableresourcestream) for read-only
+and the [`WritableResourceStream`](#writableresourcestream) for write-only
+stream resources otherwise.
+
+Internally, this class tries to enable non-blocking mode on the stream resource
+which may not be supported for all stream resources.
+Most notably, this is not supported by pipes on Windows (STDOUT, STDERR etc.).
+If this fails, it will throw a `RuntimeException`:
+
+```php
+// throws RuntimeException on Windows
+$stream = new DuplexResourceStream(STDOUT, $loop);
+```
+
+Once the constructor is called with a valid stream resource, this class will
+take care of the underlying stream resource.
+You SHOULD only use its public API and SHOULD NOT interfere with the underlying
+stream resource manually.
+
+This class takes an optional `int|null $readChunkSize` parameter that controls
+the maximum buffer size in bytes to read at once from the stream.
+You can use a `null` value here in order to apply its default value.
+This value SHOULD NOT be changed unless you know what you're doing.
+This can be a positive number which means that up to X bytes will be read
+at once from the underlying stream resource. Note that the actual number
+of bytes read may be lower if the stream resource has less than X bytes
+currently available.
+This can be `-1` which means "read everything available" from the
+underlying stream resource.
+This should read until the stream resource is not readable anymore
+(i.e. underlying buffer drained), note that this does not neccessarily
+mean it reached EOF.
+
+```php
+$conn = stream_socket_client('tcp://google.com:80');
+$stream = new DuplexResourceStream($conn, $loop, 8192);
+```
+
+Any `write()` calls to this class will not be performed instantly, but will
+be performed asynchronously, once the EventLoop reports the stream resource is
+ready to accept data.
+For this, it uses an in-memory buffer string to collect all outstanding writes.
+This buffer has a soft-limit applied which defines how much data it is willing
+to accept before the caller SHOULD stop sending further data.
+
+This class takes another optional `WritableStreamInterface|null $buffer` parameter
+that controls this write behavior of this stream.
+You can use a `null` value here in order to apply its default value.
+This value SHOULD NOT be changed unless you know what you're doing.
+
+If you want to change the write buffer soft limit, you can pass an instance of
+[`WritableResourceStream`](#writableresourcestream) like this:
+
+```php
+$conn = stream_socket_client('tcp://google.com:80');
+$buffer = new WritableResourceStream($conn, $loop, 8192);
+$stream = new DuplexResourceStream($conn, $loop, null, $buffer);
+```
+
+See also [`WritableResourceStream`](#writableresourcestream) for more details.
+
+### ThroughStream
+
+The `ThroughStream` implements the
+[`DuplexStreamInterface`](#duplexstreaminterface) and will simply pass any data
+you write to it through to its readable end.
+
+```php
+$through = new ThroughStream();
+$through->on('data', $this->expectCallableOnceWith('hello'));
+
+$through->write('hello');
+```
+
+Similarly, the [`end()` method](#end) will end the stream and emit an
+[`end` event](#end-event) and then [`close()`](#close-1) the stream.
+The [`close()` method](#close-1) will close the stream and emit a
+[`close` event](#close-event).
+Accordingly, this is can also be used in a [`pipe()`](#pipe) context like this:
+
+```php
+$through = new ThroughStream();
+$source->pipe($through)->pipe($dest);
+```
+
+Optionally, its constructor accepts any callable function which will then be
+used to *filter* any data written to it. This function receives a single data
+argument as passed to the writable side and must return the data as it will be
+passed to its readable end:
+
+```php
+$through = new ThroughStream('strtoupper');
+$source->pipe($through)->pipe($dest);
+```
+
+Note that this class makes no assumptions about any data types. This can be
+used to convert data, for example for transforming any structured data into
+a newline-delimited JSON (NDJSON) stream like this:
+
+```php
+$through = new ThroughStream(function ($data) {
+ return json_encode($data) . PHP_EOL;
+});
+$through->on('data', $this->expectCallableOnceWith("[2, true]\n"));
+
+$through->write(array(2, true));
+```
+
+The callback function is allowed to throw an `Exception`. In this case,
+the stream will emit an `error` event and then [`close()`](#close-1) the stream.
+
+```php
+$through = new ThroughStream(function ($data) {
+ if (!is_string($data)) {
+ throw new \UnexpectedValueException('Only strings allowed');
+ }
+ return $data;
+});
+$through->on('error', $this->expectCallableOnce()));
+$through->on('close', $this->expectCallableOnce()));
+$through->on('data', $this->expectCallableNever()));
+
+$through->write(2);
+```
+
+### CompositeStream
+
+The `CompositeStream` implements the
+[`DuplexStreamInterface`](#duplexstreaminterface) and can be used to create a
+single duplex stream from two individual streams implementing
+[`ReadableStreamInterface`](#readablestreaminterface) and
+[`WritableStreamInterface`](#writablestreaminterface) respectively.
+
+This is useful for some APIs which may require a single
+[`DuplexStreamInterface`](#duplexstreaminterface) or simply because it's often
+more convenient to work with a single stream instance like this:
+
+```php
+$stdin = new ReadableResourceStream(STDIN, $loop);
+$stdout = new WritableResourceStream(STDOUT, $loop);
+
+$stdio = new CompositeStream($stdin, $stdout);
+
+$stdio->on('data', function ($chunk) use ($stdio) {
+ $stdio->write('You said: ' . $chunk);
+});
+```
+
+This is a well-behaving stream which forwards all stream events from the
+underlying streams and forwards all streams calls to the underlying streams.
+
+If you `write()` to the duplex stream, it will simply `write()` to the
+writable side and return its status.
+
+If you `end()` the duplex stream, it will `end()` the writable side and will
+`pause()` the readable side.
+
+If you `close()` the duplex stream, both input streams will be closed.
+If either of the two input streams emits a `close` event, the duplex stream
+will also close.
+If either of the two input streams is already closed while constructing the
+duplex stream, it will `close()` the other side and return a closed stream.
+
+## Usage
+
+The following example can be used to pipe the contents of a source file into
+a destination file without having to ever read the whole file into memory:
+
+```php
+$loop = new React\EventLoop\StreamSelectLoop;
+
+$source = new React\Stream\ReadableResourceStream(fopen('source.txt', 'r'), $loop);
+$dest = new React\Stream\WritableResourceStream(fopen('destination.txt', 'w'), $loop);
+
+$source->pipe($dest);
+
+$loop->run();
+```
+
+> Note that this example uses `fopen()` for illustration purposes only.
+ This should not be used in a truly async program because the filesystem is
+ inherently blocking and each call could potentially take several seconds.
+ See also [creating streams](#creating-streams) for more sophisticated
+ examples.
+
+## Install
+
+The recommended way to install this library is [through Composer](https://getcomposer.org).
+[New to Composer?](https://getcomposer.org/doc/00-intro.md)
+
+This will install the latest supported version:
+
+```bash
+$ composer require react/stream:^0.7.7
+```
+
+See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
+
+This project aims to run on any platform and thus does not require any PHP
+extensions and supports running on legacy PHP 5.3 through current PHP 7+ and HHVM.
+It's *highly recommended to use PHP 7+* for this project due to its vast
+performance improvements.
+
+## Tests
+
+To run the test suite, you first need to clone this repo and then install all
+dependencies [through Composer](https://getcomposer.org):
+
+```bash
+$ composer install
+```
+
+To run the test suite, go to the project root and run:
+
+```bash
+$ php vendor/bin/phpunit
+```
+
+The test suite also contains a number of functional integration tests that rely
+on a stable internet connection.
+If you do not want to run these, they can simply be skipped like this:
+
+```bash
+$ php vendor/bin/phpunit --exclude-group internet
+```
+
+## License
+
+MIT, see [LICENSE file](LICENSE).
+
+## More
+
+* See [creating streams](#creating-streams) for more information on how streams
+ are created in real-world applications.
+* See our [users wiki](https://github.com/reactphp/react/wiki/Users) and the
+ [dependents on Packagist](https://packagist.org/packages/react/stream/dependents)
+ for a list of packages that use streams in real-world applications.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/composer.json b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/composer.json
new file mode 100644
index 0000000..f6faa66
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/composer.json
@@ -0,0 +1,25 @@
+{
+ "name": "react/stream",
+ "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP",
+ "keywords": ["event-driven", "readable", "writable", "stream", "non-blocking", "io", "pipe", "ReactPHP"],
+ "license": "MIT",
+ "require": {
+ "php": ">=5.3.8",
+ "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5",
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35",
+ "clue/stream-filter": "~1.2"
+ },
+ "autoload": {
+ "psr-4": {
+ "React\\Stream\\": "src"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "React\\Tests\\Stream\\": "tests"
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/examples/01-http.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/examples/01-http.php
new file mode 100644
index 0000000..3687f7c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/examples/01-http.php
@@ -0,0 +1,40 @@
+on('data', function ($chunk) {
+ echo $chunk;
+});
+$stream->on('close', function () {
+ echo '[CLOSED]' . PHP_EOL;
+});
+
+$stream->write("GET / HTTP/1.0\r\nHost: $host\r\n\r\n");
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/examples/02-https.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/examples/02-https.php
new file mode 100644
index 0000000..163f7c8
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/examples/02-https.php
@@ -0,0 +1,40 @@
+on('data', function ($chunk) {
+ echo $chunk;
+});
+$stream->on('close', function () {
+ echo '[CLOSED]' . PHP_EOL;
+});
+
+$stream->write("GET / HTTP/1.0\r\nHost: $host\r\n\r\n");
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/examples/11-cat.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/examples/11-cat.php
new file mode 100644
index 0000000..90fadc0
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/examples/11-cat.php
@@ -0,0 +1,28 @@
+pipe($stdout);
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/examples/91-benchmark-throughput.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/examples/91-benchmark-throughput.php
new file mode 100644
index 0000000..ecf695c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/examples/91-benchmark-throughput.php
@@ -0,0 +1,62 @@
+write('NOTICE: The "xdebug" extension is loaded, this has a major impact on performance.' . PHP_EOL);
+}
+$info->write('piping from ' . $if . ' to ' . $of . ' (for max ' . $t . ' second(s)) ...'. PHP_EOL);
+
+// setup input and output streams and pipe inbetween
+$fh = fopen($if, 'r');
+$in = new React\Stream\ReadableResourceStream($fh, $loop);
+$out = new React\Stream\WritableResourceStream(fopen($of, 'w'), $loop);
+$in->pipe($out);
+
+// stop input stream in $t seconds
+$start = microtime(true);
+$timeout = $loop->addTimer($t, function () use ($in, &$bytes) {
+ $in->close();
+});
+
+// print stream position once stream closes
+$in->on('close', function () use ($fh, $start, $loop, $timeout, $info) {
+ $t = microtime(true) - $start;
+ $loop->cancelTimer($timeout);
+
+ $bytes = ftell($fh);
+
+ $info->write('read ' . $bytes . ' byte(s) in ' . round($t, 3) . ' second(s) => ' . round($bytes / 1024 / 1024 / $t, 1) . ' MiB/s' . PHP_EOL);
+ $info->write('peak memory usage of ' . round(memory_get_peak_usage(true) / 1024 / 1024, 1) . ' MiB' . PHP_EOL);
+});
+
+$loop->run();
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/phpunit.xml.dist b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/phpunit.xml.dist
new file mode 100644
index 0000000..13d3fab
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/phpunit.xml.dist
@@ -0,0 +1,25 @@
+
+
+
+
+
+ ./tests/
+
+
+
+
+
+ ./src/
+
+
+
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/CompositeStream.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/CompositeStream.php
new file mode 100644
index 0000000..153f2a3
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/CompositeStream.php
@@ -0,0 +1,82 @@
+readable = $readable;
+ $this->writable = $writable;
+
+ if (!$readable->isReadable() || !$writable->isWritable()) {
+ return $this->close();
+ }
+
+ Util::forwardEvents($this->readable, $this, array('data', 'end', 'error'));
+ Util::forwardEvents($this->writable, $this, array('drain', 'error', 'pipe'));
+
+ $this->readable->on('close', array($this, 'close'));
+ $this->writable->on('close', array($this, 'close'));
+ }
+
+ public function isReadable()
+ {
+ return $this->readable->isReadable();
+ }
+
+ public function pause()
+ {
+ $this->readable->pause();
+ }
+
+ public function resume()
+ {
+ if (!$this->writable->isWritable()) {
+ return;
+ }
+
+ $this->readable->resume();
+ }
+
+ public function pipe(WritableStreamInterface $dest, array $options = array())
+ {
+ return Util::pipe($this, $dest, $options);
+ }
+
+ public function isWritable()
+ {
+ return $this->writable->isWritable();
+ }
+
+ public function write($data)
+ {
+ return $this->writable->write($data);
+ }
+
+ public function end($data = null)
+ {
+ $this->readable->pause();
+ $this->writable->end($data);
+ }
+
+ public function close()
+ {
+ if ($this->closed) {
+ return;
+ }
+
+ $this->closed = true;
+ $this->readable->close();
+ $this->writable->close();
+
+ $this->emit('close');
+ $this->removeAllListeners();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/DuplexResourceStream.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/DuplexResourceStream.php
new file mode 100644
index 0000000..982ebb0
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/DuplexResourceStream.php
@@ -0,0 +1,224 @@
+isLegacyPipe($stream)) {
+ stream_set_read_buffer($stream, 0);
+ }
+
+ if ($buffer === null) {
+ $buffer = new WritableResourceStream($stream, $loop);
+ }
+
+ $this->stream = $stream;
+ $this->loop = $loop;
+ $this->bufferSize = ($readChunkSize === null) ? 65536 : (int)$readChunkSize;
+ $this->buffer = $buffer;
+
+ $that = $this;
+
+ $this->buffer->on('error', function ($error) use ($that) {
+ $that->emit('error', array($error));
+ });
+
+ $this->buffer->on('close', array($this, 'close'));
+
+ $this->buffer->on('drain', function () use ($that) {
+ $that->emit('drain');
+ });
+
+ $this->resume();
+ }
+
+ public function isReadable()
+ {
+ return $this->readable;
+ }
+
+ public function isWritable()
+ {
+ return $this->writable;
+ }
+
+ public function pause()
+ {
+ if ($this->listening) {
+ $this->loop->removeReadStream($this->stream);
+ $this->listening = false;
+ }
+ }
+
+ public function resume()
+ {
+ if (!$this->listening && $this->readable) {
+ $this->loop->addReadStream($this->stream, array($this, 'handleData'));
+ $this->listening = true;
+ }
+ }
+
+ public function write($data)
+ {
+ if (!$this->writable) {
+ return false;
+ }
+
+ return $this->buffer->write($data);
+ }
+
+ public function close()
+ {
+ if (!$this->writable && !$this->closing) {
+ return;
+ }
+
+ $this->closing = false;
+
+ $this->readable = false;
+ $this->writable = false;
+
+ $this->emit('close');
+ $this->pause();
+ $this->buffer->close();
+ $this->removeAllListeners();
+
+ if (is_resource($this->stream)) {
+ fclose($this->stream);
+ }
+ }
+
+ public function end($data = null)
+ {
+ if (!$this->writable) {
+ return;
+ }
+
+ $this->closing = true;
+
+ $this->readable = false;
+ $this->writable = false;
+ $this->pause();
+
+ $this->buffer->end($data);
+ }
+
+ public function pipe(WritableStreamInterface $dest, array $options = array())
+ {
+ return Util::pipe($this, $dest, $options);
+ }
+
+ /** @internal */
+ public function handleData($stream)
+ {
+ $error = null;
+ set_error_handler(function ($errno, $errstr, $errfile, $errline) use (&$error) {
+ $error = new \ErrorException(
+ $errstr,
+ 0,
+ $errno,
+ $errfile,
+ $errline
+ );
+ });
+
+ $data = stream_get_contents($stream, $this->bufferSize);
+
+ restore_error_handler();
+
+ if ($error !== null) {
+ $this->emit('error', array(new \RuntimeException('Unable to read from stream: ' . $error->getMessage(), 0, $error)));
+ $this->close();
+ return;
+ }
+
+ if ($data !== '') {
+ $this->emit('data', array($data));
+ } else{
+ // no data read => we reached the end and close the stream
+ $this->emit('end');
+ $this->close();
+ }
+ }
+
+ /**
+ * Returns whether this is a pipe resource in a legacy environment
+ *
+ * This works around a legacy PHP bug (#61019) that was fixed in PHP 5.4.28+
+ * and PHP 5.5.12+ and newer.
+ *
+ * @param resource $resource
+ * @return bool
+ * @link https://github.com/reactphp/child-process/issues/40
+ *
+ * @codeCoverageIgnore
+ */
+ private function isLegacyPipe($resource)
+ {
+ if (PHP_VERSION_ID < 50428 || (PHP_VERSION_ID >= 50500 && PHP_VERSION_ID < 50512)) {
+ $meta = stream_get_meta_data($resource);
+
+ if (isset($meta['stream_type']) && $meta['stream_type'] === 'STDIO') {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/DuplexStreamInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/DuplexStreamInterface.php
new file mode 100644
index 0000000..631ce31
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/DuplexStreamInterface.php
@@ -0,0 +1,39 @@
+ Note that higher-level implementations of this interface may choose to
+ * define additional events with dedicated semantics not defined as part of
+ * this low-level stream specification. Conformance with these event semantics
+ * is out of scope for this interface, so you may also have to refer to the
+ * documentation of such a higher-level implementation.
+ *
+ * @see ReadableStreamInterface
+ * @see WritableStreamInterface
+ */
+interface DuplexStreamInterface extends ReadableStreamInterface, WritableStreamInterface
+{
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/ReadableResourceStream.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/ReadableResourceStream.php
new file mode 100644
index 0000000..015a96b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/ReadableResourceStream.php
@@ -0,0 +1,177 @@
+isLegacyPipe($stream)) {
+ stream_set_read_buffer($stream, 0);
+ }
+
+ $this->stream = $stream;
+ $this->loop = $loop;
+ $this->bufferSize = ($readChunkSize === null) ? 65536 : (int)$readChunkSize;
+
+ $this->resume();
+ }
+
+ public function isReadable()
+ {
+ return !$this->closed;
+ }
+
+ public function pause()
+ {
+ if ($this->listening) {
+ $this->loop->removeReadStream($this->stream);
+ $this->listening = false;
+ }
+ }
+
+ public function resume()
+ {
+ if (!$this->listening && !$this->closed) {
+ $this->loop->addReadStream($this->stream, array($this, 'handleData'));
+ $this->listening = true;
+ }
+ }
+
+ public function pipe(WritableStreamInterface $dest, array $options = array())
+ {
+ return Util::pipe($this, $dest, $options);
+ }
+
+ public function close()
+ {
+ if ($this->closed) {
+ return;
+ }
+
+ $this->closed = true;
+
+ $this->emit('close');
+ $this->pause();
+ $this->removeAllListeners();
+
+ if (is_resource($this->stream)) {
+ fclose($this->stream);
+ }
+ }
+
+ /** @internal */
+ public function handleData()
+ {
+ $error = null;
+ set_error_handler(function ($errno, $errstr, $errfile, $errline) use (&$error) {
+ $error = new \ErrorException(
+ $errstr,
+ 0,
+ $errno,
+ $errfile,
+ $errline
+ );
+ });
+
+ $data = stream_get_contents($this->stream, $this->bufferSize);
+
+ restore_error_handler();
+
+ if ($error !== null) {
+ $this->emit('error', array(new \RuntimeException('Unable to read from stream: ' . $error->getMessage(), 0, $error)));
+ $this->close();
+ return;
+ }
+
+ if ($data !== '') {
+ $this->emit('data', array($data));
+ } else{
+ // no data read => we reached the end and close the stream
+ $this->emit('end');
+ $this->close();
+ }
+ }
+
+ /**
+ * Returns whether this is a pipe resource in a legacy environment
+ *
+ * This works around a legacy PHP bug (#61019) that was fixed in PHP 5.4.28+
+ * and PHP 5.5.12+ and newer.
+ *
+ * @param resource $resource
+ * @return bool
+ * @link https://github.com/reactphp/child-process/issues/40
+ *
+ * @codeCoverageIgnore
+ */
+ private function isLegacyPipe($resource)
+ {
+ if (PHP_VERSION_ID < 50428 || (PHP_VERSION_ID >= 50500 && PHP_VERSION_ID < 50512)) {
+ $meta = stream_get_meta_data($resource);
+
+ if (isset($meta['stream_type']) && $meta['stream_type'] === 'STDIO') {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/ReadableStreamInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/ReadableStreamInterface.php
new file mode 100644
index 0000000..2b4c3d0
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/ReadableStreamInterface.php
@@ -0,0 +1,362 @@
+on('data', function ($data) {
+ * echo $data;
+ * });
+ * ```
+ *
+ * This event MAY be emitted any number of times, which may be zero times if
+ * this stream does not send any data at all.
+ * It SHOULD not be emitted after an `end` or `close` event.
+ *
+ * The given `$data` argument may be of mixed type, but it's usually
+ * recommended it SHOULD be a `string` value or MAY use a type that allows
+ * representation as a `string` for maximum compatibility.
+ *
+ * Many common streams (such as a TCP/IP connection or a file-based stream)
+ * will emit the raw (binary) payload data that is received over the wire as
+ * chunks of `string` values.
+ *
+ * Due to the stream-based nature of this, the sender may send any number
+ * of chunks with varying sizes. There are no guarantees that these chunks
+ * will be received with the exact same framing the sender intended to send.
+ * In other words, many lower-level protocols (such as TCP/IP) transfer the
+ * data in chunks that may be anywhere between single-byte values to several
+ * dozens of kilobytes. You may want to apply a higher-level protocol to
+ * these low-level data chunks in order to achieve proper message framing.
+ *
+ * end event:
+ * The `end` event will be emitted once the source stream has successfully
+ * reached the end of the stream (EOF).
+ *
+ * ```php
+ * $stream->on('end', function () {
+ * echo 'END';
+ * });
+ * ```
+ *
+ * This event SHOULD be emitted once or never at all, depending on whether
+ * a successful end was detected.
+ * It SHOULD NOT be emitted after a previous `end` or `close` event.
+ * It MUST NOT be emitted if the stream closes due to a non-successful
+ * end, such as after a previous `error` event.
+ *
+ * After the stream is ended, it MUST switch to non-readable mode,
+ * see also `isReadable()`.
+ *
+ * This event will only be emitted if the *end* was reached successfully,
+ * not if the stream was interrupted by an unrecoverable error or explicitly
+ * closed. Not all streams know this concept of a "successful end".
+ * Many use-cases involve detecting when the stream closes (terminates)
+ * instead, in this case you should use the `close` event.
+ * After the stream emits an `end` event, it SHOULD usually be followed by a
+ * `close` event.
+ *
+ * Many common streams (such as a TCP/IP connection or a file-based stream)
+ * will emit this event if either the remote side closes the connection or
+ * a file handle was successfully read until reaching its end (EOF).
+ *
+ * Note that this event should not be confused with the `end()` method.
+ * This event defines a successful end *reading* from a source stream, while
+ * the `end()` method defines *writing* a successful end to a destination
+ * stream.
+ *
+ * error event:
+ * The `error` event will be emitted once a fatal error occurs, usually while
+ * trying to read from this stream.
+ * The event receives a single `Exception` argument for the error instance.
+ *
+ * ```php
+ * $stream->on('error', function (Exception $e) {
+ * echo 'Error: ' . $e->getMessage() . PHP_EOL;
+ * });
+ * ```
+ *
+ * This event SHOULD be emitted once the stream detects a fatal error, such
+ * as a fatal transmission error or after an unexpected `data` or premature
+ * `end` event.
+ * It SHOULD NOT be emitted after a previous `error`, `end` or `close` event.
+ * It MUST NOT be emitted if this is not a fatal error condition, such as
+ * a temporary network issue that did not cause any data to be lost.
+ *
+ * After the stream errors, it MUST close the stream and SHOULD thus be
+ * followed by a `close` event and then switch to non-readable mode, see
+ * also `close()` and `isReadable()`.
+ *
+ * Many common streams (such as a TCP/IP connection or a file-based stream)
+ * only deal with data transmission and do not make assumption about data
+ * boundaries (such as unexpected `data` or premature `end` events).
+ * In other words, many lower-level protocols (such as TCP/IP) may choose
+ * to only emit this for a fatal transmission error once and will then
+ * close (terminate) the stream in response.
+ *
+ * If this stream is a `DuplexStreamInterface`, you should also notice
+ * how the writable side of the stream also implements an `error` event.
+ * In other words, an error may occur while either reading or writing the
+ * stream which should result in the same error processing.
+ *
+ * close event:
+ * The `close` event will be emitted once the stream closes (terminates).
+ *
+ * ```php
+ * $stream->on('close', function () {
+ * echo 'CLOSED';
+ * });
+ * ```
+ *
+ * This event SHOULD be emitted once or never at all, depending on whether
+ * the stream ever terminates.
+ * It SHOULD NOT be emitted after a previous `close` event.
+ *
+ * After the stream is closed, it MUST switch to non-readable mode,
+ * see also `isReadable()`.
+ *
+ * Unlike the `end` event, this event SHOULD be emitted whenever the stream
+ * closes, irrespective of whether this happens implicitly due to an
+ * unrecoverable error or explicitly when either side closes the stream.
+ * If you only want to detect a *successful* end, you should use the `end`
+ * event instead.
+ *
+ * Many common streams (such as a TCP/IP connection or a file-based stream)
+ * will likely choose to emit this event after reading a *successful* `end`
+ * event or after a fatal transmission `error` event.
+ *
+ * If this stream is a `DuplexStreamInterface`, you should also notice
+ * how the writable side of the stream also implements a `close` event.
+ * In other words, after receiving this event, the stream MUST switch into
+ * non-writable AND non-readable mode, see also `isWritable()`.
+ * Note that this event should not be confused with the `end` event.
+ *
+ * The event callback functions MUST be a valid `callable` that obeys strict
+ * parameter definitions and MUST accept event parameters exactly as documented.
+ * The event callback functions MUST NOT throw an `Exception`.
+ * The return value of the event callback functions will be ignored and has no
+ * effect, so for performance reasons you're recommended to not return any
+ * excessive data structures.
+ *
+ * Every implementation of this interface MUST follow these event semantics in
+ * order to be considered a well-behaving stream.
+ *
+ * > Note that higher-level implementations of this interface may choose to
+ * define additional events with dedicated semantics not defined as part of
+ * this low-level stream specification. Conformance with these event semantics
+ * is out of scope for this interface, so you may also have to refer to the
+ * documentation of such a higher-level implementation.
+ *
+ * @see EventEmitterInterface
+ */
+interface ReadableStreamInterface extends EventEmitterInterface
+{
+ /**
+ * Checks whether this stream is in a readable state (not closed already).
+ *
+ * This method can be used to check if the stream still accepts incoming
+ * data events or if it is ended or closed already.
+ * Once the stream is non-readable, no further `data` or `end` events SHOULD
+ * be emitted.
+ *
+ * ```php
+ * assert($stream->isReadable() === false);
+ *
+ * $stream->on('data', assertNeverCalled());
+ * $stream->on('end', assertNeverCalled());
+ * ```
+ *
+ * A successfully opened stream always MUST start in readable mode.
+ *
+ * Once the stream ends or closes, it MUST switch to non-readable mode.
+ * This can happen any time, explicitly through `close()` or
+ * implicitly due to a remote close or an unrecoverable transmission error.
+ * Once a stream has switched to non-readable mode, it MUST NOT transition
+ * back to readable mode.
+ *
+ * If this stream is a `DuplexStreamInterface`, you should also notice
+ * how the writable side of the stream also implements an `isWritable()`
+ * method. Unless this is a half-open duplex stream, they SHOULD usually
+ * have the same return value.
+ *
+ * @return bool
+ */
+ public function isReadable();
+
+ /**
+ * Pauses reading incoming data events.
+ *
+ * Removes the data source file descriptor from the event loop. This
+ * allows you to throttle incoming data.
+ *
+ * Unless otherwise noted, a successfully opened stream SHOULD NOT start
+ * in paused state.
+ *
+ * Once the stream is paused, no futher `data` or `end` events SHOULD
+ * be emitted.
+ *
+ * ```php
+ * $stream->pause();
+ *
+ * $stream->on('data', assertShouldNeverCalled());
+ * $stream->on('end', assertShouldNeverCalled());
+ * ```
+ *
+ * This method is advisory-only, though generally not recommended, the
+ * stream MAY continue emitting `data` events.
+ *
+ * You can continue processing events by calling `resume()` again.
+ *
+ * Note that both methods can be called any number of times, in particular
+ * calling `pause()` more than once SHOULD NOT have any effect.
+ *
+ * @see self::resume()
+ * @return void
+ */
+ public function pause();
+
+ /**
+ * Resumes reading incoming data events.
+ *
+ * Re-attach the data source after a previous `pause()`.
+ *
+ * ```php
+ * $stream->pause();
+ *
+ * $loop->addTimer(1.0, function () use ($stream) {
+ * $stream->resume();
+ * });
+ * ```
+ *
+ * Note that both methods can be called any number of times, in particular
+ * calling `resume()` without a prior `pause()` SHOULD NOT have any effect.
+ *
+ * @see self::pause()
+ * @return void
+ */
+ public function resume();
+
+ /**
+ * Pipes all the data from this readable source into the given writable destination.
+ *
+ * Automatically sends all incoming data to the destination.
+ * Automatically throttles the source based on what the destination can handle.
+ *
+ * ```php
+ * $source->pipe($dest);
+ * ```
+ *
+ * Similarly, you can also pipe an instance implementing `DuplexStreamInterface`
+ * into itself in order to write back all the data that is received.
+ * This may be a useful feature for a TCP/IP echo service:
+ *
+ * ```php
+ * $connection->pipe($connection);
+ * ```
+ *
+ * This method returns the destination stream as-is, which can be used to
+ * set up chains of piped streams:
+ *
+ * ```php
+ * $source->pipe($decodeGzip)->pipe($filterBadWords)->pipe($dest);
+ * ```
+ *
+ * By default, this will call `end()` on the destination stream once the
+ * source stream emits an `end` event. This can be disabled like this:
+ *
+ * ```php
+ * $source->pipe($dest, array('end' => false));
+ * ```
+ *
+ * Note that this only applies to the `end` event.
+ * If an `error` or explicit `close` event happens on the source stream,
+ * you'll have to manually close the destination stream:
+ *
+ * ```php
+ * $source->pipe($dest);
+ * $source->on('close', function () use ($dest) {
+ * $dest->end('BYE!');
+ * });
+ * ```
+ *
+ * If the source stream is not readable (closed state), then this is a NO-OP.
+ *
+ * ```php
+ * $source->close();
+ * $source->pipe($dest); // NO-OP
+ * ```
+ *
+ * If the destinantion stream is not writable (closed state), then this will simply
+ * throttle (pause) the source stream:
+ *
+ * ```php
+ * $dest->close();
+ * $source->pipe($dest); // calls $source->pause()
+ * ```
+ *
+ * Similarly, if the destination stream is closed while the pipe is still
+ * active, it will also throttle (pause) the source stream:
+ *
+ * ```php
+ * $source->pipe($dest);
+ * $dest->close(); // calls $source->pause()
+ * ```
+ *
+ * Once the pipe is set up successfully, the destination stream MUST emit
+ * a `pipe` event with this source stream an event argument.
+ *
+ * @param WritableStreamInterface $dest
+ * @param array $options
+ * @return WritableStreamInterface $dest stream as-is
+ */
+ public function pipe(WritableStreamInterface $dest, array $options = array());
+
+ /**
+ * Closes the stream (forcefully).
+ *
+ * This method can be used to (forcefully) close the stream.
+ *
+ * ```php
+ * $stream->close();
+ * ```
+ *
+ * Once the stream is closed, it SHOULD emit a `close` event.
+ * Note that this event SHOULD NOT be emitted more than once, in particular
+ * if this method is called multiple times.
+ *
+ * After calling this method, the stream MUST switch into a non-readable
+ * mode, see also `isReadable()`.
+ * This means that no further `data` or `end` events SHOULD be emitted.
+ *
+ * ```php
+ * $stream->close();
+ * assert($stream->isReadable() === false);
+ *
+ * $stream->on('data', assertNeverCalled());
+ * $stream->on('end', assertNeverCalled());
+ * ```
+ *
+ * If this stream is a `DuplexStreamInterface`, you should also notice
+ * how the writable side of the stream also implements a `close()` method.
+ * In other words, after calling this method, the stream MUST switch into
+ * non-writable AND non-readable mode, see also `isWritable()`.
+ * Note that this method should not be confused with the `end()` method.
+ *
+ * @return void
+ * @see WritableStreamInterface::close()
+ */
+ public function close();
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/ThroughStream.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/ThroughStream.php
new file mode 100644
index 0000000..da2fbb0
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/ThroughStream.php
@@ -0,0 +1,190 @@
+on('data', $this->expectCallableOnceWith('hello'));
+ *
+ * $through->write('hello');
+ * ```
+ *
+ * Similarly, the [`end()` method](#end) will end the stream and emit an
+ * [`end` event](#end-event) and then [`close()`](#close-1) the stream.
+ * The [`close()` method](#close-1) will close the stream and emit a
+ * [`close` event](#close-event).
+ * Accordingly, this is can also be used in a [`pipe()`](#pipe) context like this:
+ *
+ * ```php
+ * $through = new ThroughStream();
+ * $source->pipe($through)->pipe($dest);
+ * ```
+ *
+ * Optionally, its constructor accepts any callable function which will then be
+ * used to *filter* any data written to it. This function receives a single data
+ * argument as passed to the writable side and must return the data as it will be
+ * passed to its readable end:
+ *
+ * ```php
+ * $through = new ThroughStream('strtoupper');
+ * $source->pipe($through)->pipe($dest);
+ * ```
+ *
+ * Note that this class makes no assumptions about any data types. This can be
+ * used to convert data, for example for transforming any structured data into
+ * a newline-delimited JSON (NDJSON) stream like this:
+ *
+ * ```php
+ * $through = new ThroughStream(function ($data) {
+ * return json_encode($data) . PHP_EOL;
+ * });
+ * $through->on('data', $this->expectCallableOnceWith("[2, true]\n"));
+ *
+ * $through->write(array(2, true));
+ * ```
+ *
+ * The callback function is allowed to throw an `Exception`. In this case,
+ * the stream will emit an `error` event and then [`close()`](#close-1) the stream.
+ *
+ * ```php
+ * $through = new ThroughStream(function ($data) {
+ * if (!is_string($data)) {
+ * throw new \UnexpectedValueException('Only strings allowed');
+ * }
+ * return $data;
+ * });
+ * $through->on('error', $this->expectCallableOnce()));
+ * $through->on('close', $this->expectCallableOnce()));
+ * $through->on('data', $this->expectCallableNever()));
+ *
+ * $through->write(2);
+ * ```
+ *
+ * @see WritableStreamInterface::write()
+ * @see WritableStreamInterface::end()
+ * @see DuplexStreamInterface::close()
+ * @see WritableStreamInterface::pipe()
+ */
+final class ThroughStream extends EventEmitter implements DuplexStreamInterface
+{
+ private $readable = true;
+ private $writable = true;
+ private $closed = false;
+ private $paused = false;
+ private $drain = false;
+ private $callback;
+
+ public function __construct($callback = null)
+ {
+ if ($callback !== null && !is_callable($callback)) {
+ throw new InvalidArgumentException('Invalid transformation callback given');
+ }
+
+ $this->callback = $callback;
+ }
+
+ public function pause()
+ {
+ $this->paused = true;
+ }
+
+ public function resume()
+ {
+ if ($this->drain) {
+ $this->drain = false;
+ $this->emit('drain');
+ }
+ $this->paused = false;
+ }
+
+ public function pipe(WritableStreamInterface $dest, array $options = array())
+ {
+ return Util::pipe($this, $dest, $options);
+ }
+
+ public function isReadable()
+ {
+ return $this->readable;
+ }
+
+ public function isWritable()
+ {
+ return $this->writable;
+ }
+
+ public function write($data)
+ {
+ if (!$this->writable) {
+ return false;
+ }
+
+ if ($this->callback !== null) {
+ try {
+ $data = call_user_func($this->callback, $data);
+ } catch (\Exception $e) {
+ $this->emit('error', array($e));
+ $this->close();
+
+ return false;
+ }
+ }
+
+ $this->emit('data', array($data));
+
+ if ($this->paused) {
+ $this->drain = true;
+ return false;
+ }
+
+ return true;
+ }
+
+ public function end($data = null)
+ {
+ if (!$this->writable) {
+ return;
+ }
+
+ if (null !== $data) {
+ $this->write($data);
+
+ // return if write() already caused the stream to close
+ if (!$this->writable) {
+ return;
+ }
+ }
+
+ $this->readable = false;
+ $this->writable = false;
+ $this->paused = true;
+ $this->drain = false;
+
+ $this->emit('end');
+ $this->close();
+ }
+
+ public function close()
+ {
+ if ($this->closed) {
+ return;
+ }
+
+ $this->readable = false;
+ $this->writable = false;
+ $this->closed = true;
+ $this->paused = true;
+ $this->drain = false;
+ $this->callback = null;
+
+ $this->emit('close');
+ $this->removeAllListeners();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/Util.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/Util.php
new file mode 100644
index 0000000..14ddcfc
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/Util.php
@@ -0,0 +1,75 @@
+ NO-OP
+ if (!$source->isReadable()) {
+ return $dest;
+ }
+
+ // destination not writable => just pause() source
+ if (!$dest->isWritable()) {
+ $source->pause();
+
+ return $dest;
+ }
+
+ $dest->emit('pipe', array($source));
+
+ // forward all source data events as $dest->write()
+ $source->on('data', $dataer = function ($data) use ($source, $dest) {
+ $feedMore = $dest->write($data);
+
+ if (false === $feedMore) {
+ $source->pause();
+ }
+ });
+ $dest->on('close', function () use ($source, $dataer) {
+ $source->removeListener('data', $dataer);
+ $source->pause();
+ });
+
+ // forward destination drain as $source->resume()
+ $dest->on('drain', $drainer = function () use ($source) {
+ $source->resume();
+ });
+ $source->on('close', function () use ($dest, $drainer) {
+ $dest->removeListener('drain', $drainer);
+ });
+
+ // forward end event from source as $dest->end()
+ $end = isset($options['end']) ? $options['end'] : true;
+ if ($end) {
+ $source->on('end', $ender = function () use ($dest) {
+ $dest->end();
+ });
+ $dest->on('close', function () use ($source, $ender) {
+ $source->removeListener('end', $ender);
+ });
+ }
+
+ return $dest;
+ }
+
+ public static function forwardEvents($source, $target, array $events)
+ {
+ foreach ($events as $event) {
+ $source->on($event, function () use ($event, $target) {
+ $target->emit($event, func_get_args());
+ });
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/WritableResourceStream.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/WritableResourceStream.php
new file mode 100644
index 0000000..7e04205
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/WritableResourceStream.php
@@ -0,0 +1,171 @@
+stream = $stream;
+ $this->loop = $loop;
+ $this->softLimit = ($writeBufferSoftLimit === null) ? 65536 : (int)$writeBufferSoftLimit;
+ $this->writeChunkSize = ($writeChunkSize === null) ? -1 : (int)$writeChunkSize;
+ }
+
+ public function isWritable()
+ {
+ return $this->writable;
+ }
+
+ public function write($data)
+ {
+ if (!$this->writable) {
+ return false;
+ }
+
+ $this->data .= $data;
+
+ if (!$this->listening && $this->data !== '') {
+ $this->listening = true;
+
+ $this->loop->addWriteStream($this->stream, array($this, 'handleWrite'));
+ }
+
+ return !isset($this->data[$this->softLimit - 1]);
+ }
+
+ public function end($data = null)
+ {
+ if (null !== $data) {
+ $this->write($data);
+ }
+
+ $this->writable = false;
+
+ // close immediately if buffer is already empty
+ // otherwise wait for buffer to flush first
+ if ($this->data === '') {
+ $this->close();
+ }
+ }
+
+ public function close()
+ {
+ if ($this->closed) {
+ return;
+ }
+
+ if ($this->listening) {
+ $this->listening = false;
+ $this->loop->removeWriteStream($this->stream);
+ }
+
+ $this->closed = true;
+ $this->writable = false;
+ $this->data = '';
+
+ $this->emit('close');
+ $this->removeAllListeners();
+
+ if (is_resource($this->stream)) {
+ fclose($this->stream);
+ }
+ }
+
+ /** @internal */
+ public function handleWrite()
+ {
+ $error = null;
+ set_error_handler(function ($errno, $errstr, $errfile, $errline) use (&$error) {
+ $error = array(
+ 'message' => $errstr,
+ 'number' => $errno,
+ 'file' => $errfile,
+ 'line' => $errline
+ );
+ });
+
+ if ($this->writeChunkSize === -1) {
+ $sent = fwrite($this->stream, $this->data);
+ } else {
+ $sent = fwrite($this->stream, $this->data, $this->writeChunkSize);
+ }
+
+ restore_error_handler();
+
+ // Only report errors if *nothing* could be sent.
+ // Any hard (permanent) error will fail to send any data at all.
+ // Sending excessive amounts of data will only flush *some* data and then
+ // report a temporary error (EAGAIN) which we do not raise here in order
+ // to keep the stream open for further tries to write.
+ // Should this turn out to be a permanent error later, it will eventually
+ // send *nothing* and we can detect this.
+ if ($sent === 0 || $sent === false) {
+ if ($error !== null) {
+ $error = new \ErrorException(
+ $error['message'],
+ 0,
+ $error['number'],
+ $error['file'],
+ $error['line']
+ );
+ }
+
+ $this->emit('error', array(new \RuntimeException('Unable to write to stream: ' . ($error !== null ? $error->getMessage() : 'Unknown error'), 0, $error)));
+ $this->close();
+
+ return;
+ }
+
+ $exceeded = isset($this->data[$this->softLimit - 1]);
+ $this->data = (string) substr($this->data, $sent);
+
+ // buffer has been above limit and is now below limit
+ if ($exceeded && !isset($this->data[$this->softLimit - 1])) {
+ $this->emit('drain');
+ }
+
+ // buffer is now completely empty => stop trying to write
+ if ($this->data === '') {
+ // stop waiting for resource to be writable
+ if ($this->listening) {
+ $this->loop->removeWriteStream($this->stream);
+ $this->listening = false;
+ }
+
+ // buffer is end()ing and now completely empty => close buffer
+ if (!$this->writable) {
+ $this->close();
+ }
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/WritableStreamInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/WritableStreamInterface.php
new file mode 100644
index 0000000..3bc932e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/src/WritableStreamInterface.php
@@ -0,0 +1,347 @@
+on('drain', function () use ($stream) {
+ * echo 'Stream is now ready to accept more data';
+ * });
+ * ```
+ *
+ * This event SHOULD be emitted once every time the buffer became full
+ * previously and is now ready to accept more data.
+ * In other words, this event MAY be emitted any number of times, which may
+ * be zero times if the buffer never became full in the first place.
+ * This event SHOULD NOT be emitted if the buffer has not become full
+ * previously.
+ *
+ * This event is mostly used internally, see also `write()` for more details.
+ *
+ * pipe event:
+ * The `pipe` event will be emitted whenever a readable stream is `pipe()`d
+ * into this stream.
+ * The event receives a single `ReadableStreamInterface` argument for the
+ * source stream.
+ *
+ * ```php
+ * $stream->on('pipe', function (ReadableStreamInterface $source) use ($stream) {
+ * echo 'Now receiving piped data';
+ *
+ * // explicitly close target if source emits an error
+ * $source->on('error', function () use ($stream) {
+ * $stream->close();
+ * });
+ * });
+ *
+ * $source->pipe($stream);
+ * ```
+ *
+ * This event MUST be emitted once for each readable stream that is
+ * successfully piped into this destination stream.
+ * In other words, this event MAY be emitted any number of times, which may
+ * be zero times if no stream is ever piped into this stream.
+ * This event MUST NOT be emitted if either the source is not readable
+ * (closed already) or this destination is not writable (closed already).
+ *
+ * This event is mostly used internally, see also `pipe()` for more details.
+ *
+ * error event:
+ * The `error` event will be emitted once a fatal error occurs, usually while
+ * trying to write to this stream.
+ * The event receives a single `Exception` argument for the error instance.
+ *
+ * ```php
+ * $stream->on('error', function (Exception $e) {
+ * echo 'Error: ' . $e->getMessage() . PHP_EOL;
+ * });
+ * ```
+ *
+ * This event SHOULD be emitted once the stream detects a fatal error, such
+ * as a fatal transmission error.
+ * It SHOULD NOT be emitted after a previous `error` or `close` event.
+ * It MUST NOT be emitted if this is not a fatal error condition, such as
+ * a temporary network issue that did not cause any data to be lost.
+ *
+ * After the stream errors, it MUST close the stream and SHOULD thus be
+ * followed by a `close` event and then switch to non-writable mode, see
+ * also `close()` and `isWritable()`.
+ *
+ * Many common streams (such as a TCP/IP connection or a file-based stream)
+ * only deal with data transmission and may choose
+ * to only emit this for a fatal transmission error once and will then
+ * close (terminate) the stream in response.
+ *
+ * If this stream is a `DuplexStreamInterface`, you should also notice
+ * how the readable side of the stream also implements an `error` event.
+ * In other words, an error may occur while either reading or writing the
+ * stream which should result in the same error processing.
+ *
+ * close event:
+ * The `close` event will be emitted once the stream closes (terminates).
+ *
+ * ```php
+ * $stream->on('close', function () {
+ * echo 'CLOSED';
+ * });
+ * ```
+ *
+ * This event SHOULD be emitted once or never at all, depending on whether
+ * the stream ever terminates.
+ * It SHOULD NOT be emitted after a previous `close` event.
+ *
+ * After the stream is closed, it MUST switch to non-writable mode,
+ * see also `isWritable()`.
+ *
+ * This event SHOULD be emitted whenever the stream closes, irrespective of
+ * whether this happens implicitly due to an unrecoverable error or
+ * explicitly when either side closes the stream.
+ *
+ * Many common streams (such as a TCP/IP connection or a file-based stream)
+ * will likely choose to emit this event after flushing the buffer from
+ * the `end()` method, after receiving a *successful* `end` event or after
+ * a fatal transmission `error` event.
+ *
+ * If this stream is a `DuplexStreamInterface`, you should also notice
+ * how the readable side of the stream also implements a `close` event.
+ * In other words, after receiving this event, the stream MUST switch into
+ * non-writable AND non-readable mode, see also `isReadable()`.
+ * Note that this event should not be confused with the `end` event.
+ *
+ * The event callback functions MUST be a valid `callable` that obeys strict
+ * parameter definitions and MUST accept event parameters exactly as documented.
+ * The event callback functions MUST NOT throw an `Exception`.
+ * The return value of the event callback functions will be ignored and has no
+ * effect, so for performance reasons you're recommended to not return any
+ * excessive data structures.
+ *
+ * Every implementation of this interface MUST follow these event semantics in
+ * order to be considered a well-behaving stream.
+ *
+ * > Note that higher-level implementations of this interface may choose to
+ * define additional events with dedicated semantics not defined as part of
+ * this low-level stream specification. Conformance with these event semantics
+ * is out of scope for this interface, so you may also have to refer to the
+ * documentation of such a higher-level implementation.
+ *
+ * @see EventEmitterInterface
+ * @see DuplexStreamInterface
+ */
+interface WritableStreamInterface extends EventEmitterInterface
+{
+ /**
+ * Checks whether this stream is in a writable state (not closed already).
+ *
+ * This method can be used to check if the stream still accepts writing
+ * any data or if it is ended or closed already.
+ * Writing any data to a non-writable stream is a NO-OP:
+ *
+ * ```php
+ * assert($stream->isWritable() === false);
+ *
+ * $stream->write('end'); // NO-OP
+ * $stream->end('end'); // NO-OP
+ * ```
+ *
+ * A successfully opened stream always MUST start in writable mode.
+ *
+ * Once the stream ends or closes, it MUST switch to non-writable mode.
+ * This can happen any time, explicitly through `end()` or `close()` or
+ * implicitly due to a remote close or an unrecoverable transmission error.
+ * Once a stream has switched to non-writable mode, it MUST NOT transition
+ * back to writable mode.
+ *
+ * If this stream is a `DuplexStreamInterface`, you should also notice
+ * how the readable side of the stream also implements an `isReadable()`
+ * method. Unless this is a half-open duplex stream, they SHOULD usually
+ * have the same return value.
+ *
+ * @return bool
+ */
+ public function isWritable();
+
+ /**
+ * Write some data into the stream.
+ *
+ * A successful write MUST be confirmed with a boolean `true`, which means
+ * that either the data was written (flushed) immediately or is buffered and
+ * scheduled for a future write. Note that this interface gives you no
+ * control over explicitly flushing the buffered data, as finding the
+ * appropriate time for this is beyond the scope of this interface and left
+ * up to the implementation of this interface.
+ *
+ * Many common streams (such as a TCP/IP connection or file-based stream)
+ * may choose to buffer all given data and schedule a future flush by using
+ * an underlying EventLoop to check when the resource is actually writable.
+ *
+ * If a stream cannot handle writing (or flushing) the data, it SHOULD emit
+ * an `error` event and MAY `close()` the stream if it can not recover from
+ * this error.
+ *
+ * If the internal buffer is full after adding `$data`, then `write()`
+ * SHOULD return `false`, indicating that the caller should stop sending
+ * data until the buffer drains.
+ * The stream SHOULD send a `drain` event once the buffer is ready to accept
+ * more data.
+ *
+ * Similarly, if the the stream is not writable (already in a closed state)
+ * it MUST NOT process the given `$data` and SHOULD return `false`,
+ * indicating that the caller should stop sending data.
+ *
+ * The given `$data` argument MAY be of mixed type, but it's usually
+ * recommended it SHOULD be a `string` value or MAY use a type that allows
+ * representation as a `string` for maximum compatibility.
+ *
+ * Many common streams (such as a TCP/IP connection or a file-based stream)
+ * will only accept the raw (binary) payload data that is transferred over
+ * the wire as chunks of `string` values.
+ *
+ * Due to the stream-based nature of this, the sender may send any number
+ * of chunks with varying sizes. There are no guarantees that these chunks
+ * will be received with the exact same framing the sender intended to send.
+ * In other words, many lower-level protocols (such as TCP/IP) transfer the
+ * data in chunks that may be anywhere between single-byte values to several
+ * dozens of kilobytes. You may want to apply a higher-level protocol to
+ * these low-level data chunks in order to achieve proper message framing.
+ *
+ * @param mixed|string $data
+ * @return bool
+ */
+ public function write($data);
+
+ /**
+ * Successfully ends the stream (after optionally sending some final data).
+ *
+ * This method can be used to successfully end the stream, i.e. close
+ * the stream after sending out all data that is currently buffered.
+ *
+ * ```php
+ * $stream->write('hello');
+ * $stream->write('world');
+ * $stream->end();
+ * ```
+ *
+ * If there's no data currently buffered and nothing to be flushed, then
+ * this method MAY `close()` the stream immediately.
+ *
+ * If there's still data in the buffer that needs to be flushed first, then
+ * this method SHOULD try to write out this data and only then `close()`
+ * the stream.
+ * Once the stream is closed, it SHOULD emit a `close` event.
+ *
+ * Note that this interface gives you no control over explicitly flushing
+ * the buffered data, as finding the appropriate time for this is beyond the
+ * scope of this interface and left up to the implementation of this
+ * interface.
+ *
+ * Many common streams (such as a TCP/IP connection or file-based stream)
+ * may choose to buffer all given data and schedule a future flush by using
+ * an underlying EventLoop to check when the resource is actually writable.
+ *
+ * You can optionally pass some final data that is written to the stream
+ * before ending the stream. If a non-`null` value is given as `$data`, then
+ * this method will behave just like calling `write($data)` before ending
+ * with no data.
+ *
+ * ```php
+ * // shorter version
+ * $stream->end('bye');
+ *
+ * // same as longer version
+ * $stream->write('bye');
+ * $stream->end();
+ * ```
+ *
+ * After calling this method, the stream MUST switch into a non-writable
+ * mode, see also `isWritable()`.
+ * This means that no further writes are possible, so any additional
+ * `write()` or `end()` calls have no effect.
+ *
+ * ```php
+ * $stream->end();
+ * assert($stream->isWritable() === false);
+ *
+ * $stream->write('nope'); // NO-OP
+ * $stream->end(); // NO-OP
+ * ```
+ *
+ * If this stream is a `DuplexStreamInterface`, calling this method SHOULD
+ * also end its readable side, unless the stream supports half-open mode.
+ * In other words, after calling this method, these streams SHOULD switch
+ * into non-writable AND non-readable mode, see also `isReadable()`.
+ * This implies that in this case, the stream SHOULD NOT emit any `data`
+ * or `end` events anymore.
+ * Streams MAY choose to use the `pause()` method logic for this, but
+ * special care may have to be taken to ensure a following call to the
+ * `resume()` method SHOULD NOT continue emitting readable events.
+ *
+ * Note that this method should not be confused with the `close()` method.
+ *
+ * @param mixed|string|null $data
+ * @return void
+ */
+ public function end($data = null);
+
+ /**
+ * Closes the stream (forcefully).
+ *
+ * This method can be used to forcefully close the stream, i.e. close
+ * the stream without waiting for any buffered data to be flushed.
+ * If there's still data in the buffer, this data SHOULD be discarded.
+ *
+ * ```php
+ * $stream->close();
+ * ```
+ *
+ * Once the stream is closed, it SHOULD emit a `close` event.
+ * Note that this event SHOULD NOT be emitted more than once, in particular
+ * if this method is called multiple times.
+ *
+ * After calling this method, the stream MUST switch into a non-writable
+ * mode, see also `isWritable()`.
+ * This means that no further writes are possible, so any additional
+ * `write()` or `end()` calls have no effect.
+ *
+ * ```php
+ * $stream->close();
+ * assert($stream->isWritable() === false);
+ *
+ * $stream->write('nope'); // NO-OP
+ * $stream->end(); // NO-OP
+ * ```
+ *
+ * Note that this method should not be confused with the `end()` method.
+ * Unlike the `end()` method, this method does not take care of any existing
+ * buffers and simply discards any buffer contents.
+ * Likewise, this method may also be called after calling `end()` on a
+ * stream in order to stop waiting for the stream to flush its final data.
+ *
+ * ```php
+ * $stream->end();
+ * $loop->addTimer(1.0, function () use ($stream) {
+ * $stream->close();
+ * });
+ * ```
+ *
+ * If this stream is a `DuplexStreamInterface`, you should also notice
+ * how the readable side of the stream also implements a `close()` method.
+ * In other words, after calling this method, the stream MUST switch into
+ * non-writable AND non-readable mode, see also `isReadable()`.
+ *
+ * @return void
+ * @see ReadableStreamInterface::close()
+ */
+ public function close();
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/CallableStub.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/CallableStub.php
new file mode 100644
index 0000000..31cc834
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/CallableStub.php
@@ -0,0 +1,10 @@
+getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->once())
+ ->method('isReadable')
+ ->willReturn(true);
+ $readable
+ ->expects($this->once())
+ ->method('close');
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->once())
+ ->method('isWritable')
+ ->willReturn(false);
+
+ $composite = new CompositeStream($readable, $writable);
+
+ $composite->on('close', $this->expectCallableNever());
+ $composite->close();
+ }
+
+ /** @test */
+ public function itShouldCloseWritableIfNotReadable()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->once())
+ ->method('isReadable')
+ ->willReturn(false);
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->once())
+ ->method('close');
+
+ $composite = new CompositeStream($readable, $writable);
+
+ $composite->on('close', $this->expectCallableNever());
+ $composite->close();
+ }
+
+ /** @test */
+ public function itShouldForwardWritableCallsToWritableStream()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->once())
+ ->method('isReadable')
+ ->willReturn(true);
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->once())
+ ->method('write')
+ ->with('foo');
+ $writable
+ ->expects($this->exactly(2))
+ ->method('isWritable')
+ ->willReturn(true);
+
+ $composite = new CompositeStream($readable, $writable);
+ $composite->write('foo');
+ $composite->isWritable();
+ }
+
+ /** @test */
+ public function itShouldForwardReadableCallsToReadableStream()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->exactly(2))
+ ->method('isReadable')
+ ->willReturn(true);
+ $readable
+ ->expects($this->once())
+ ->method('pause');
+ $readable
+ ->expects($this->once())
+ ->method('resume');
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->any())
+ ->method('isWritable')
+ ->willReturn(true);
+
+ $composite = new CompositeStream($readable, $writable);
+ $composite->isReadable();
+ $composite->pause();
+ $composite->resume();
+ }
+
+ /** @test */
+ public function itShouldNotForwardResumeIfStreamIsNotWritable()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->once())
+ ->method('isReadable')
+ ->willReturn(true);
+ $readable
+ ->expects($this->never())
+ ->method('resume');
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->exactly(2))
+ ->method('isWritable')
+ ->willReturnOnConsecutiveCalls(true, false);
+
+ $composite = new CompositeStream($readable, $writable);
+ $composite->resume();
+ }
+
+ /** @test */
+ public function endShouldDelegateToWritableWithData()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->once())
+ ->method('isReadable')
+ ->willReturn(true);
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->once())
+ ->method('isWritable')
+ ->willReturn(true);
+ $writable
+ ->expects($this->once())
+ ->method('end')
+ ->with('foo');
+
+ $composite = new CompositeStream($readable, $writable);
+ $composite->end('foo');
+ }
+
+ /** @test */
+ public function closeShouldCloseBothStreams()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->once())
+ ->method('isReadable')
+ ->willReturn(true);
+ $readable
+ ->expects($this->once())
+ ->method('close');
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->once())
+ ->method('isWritable')
+ ->willReturn(true);
+ $writable
+ ->expects($this->once())
+ ->method('close');
+
+ $composite = new CompositeStream($readable, $writable);
+ $composite->close();
+ }
+
+ /** @test */
+ public function itShouldForwardCloseOnlyOnce()
+ {
+ $readable = new ThroughStream();
+ $writable = new ThroughStream();
+
+ $composite = new CompositeStream($readable, $writable);
+ $composite->on('close', $this->expectCallableOnce());
+
+ $readable->close();
+ $writable->close();
+ }
+
+ /** @test */
+ public function itShouldForwardCloseAndRemoveAllListeners()
+ {
+ $in = new ThroughStream();
+
+ $composite = new CompositeStream($in, $in);
+ $composite->on('close', $this->expectCallableOnce());
+
+ $this->assertTrue($composite->isReadable());
+ $this->assertTrue($composite->isWritable());
+ $this->assertCount(1, $composite->listeners('close'));
+
+ $composite->close();
+
+ $this->assertFalse($composite->isReadable());
+ $this->assertFalse($composite->isWritable());
+ $this->assertCount(0, $composite->listeners('close'));
+ }
+
+ /** @test */
+ public function itShouldReceiveForwardedEvents()
+ {
+ $readable = new ThroughStream();
+ $writable = new ThroughStream();
+
+ $composite = new CompositeStream($readable, $writable);
+ $composite->on('data', $this->expectCallableOnce());
+ $composite->on('drain', $this->expectCallableOnce());
+
+ $readable->emit('data', array('foo'));
+ $writable->emit('drain');
+ }
+
+ /** @test */
+ public function itShouldHandlePipingCorrectly()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->once())
+ ->method('isReadable')
+ ->willReturn(true);
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable->expects($this->any())->method('isWritable')->willReturn(True);
+ $writable
+ ->expects($this->once())
+ ->method('write')
+ ->with('foo');
+
+ $composite = new CompositeStream($readable, $writable);
+
+ $input = new ThroughStream();
+ $input->pipe($composite);
+ $input->emit('data', array('foo'));
+ }
+
+ /** @test */
+ public function itShouldForwardPipeCallsToReadableStream()
+ {
+ $readable = new ThroughStream();
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable->expects($this->any())->method('isWritable')->willReturn(True);
+
+ $composite = new CompositeStream($readable, $writable);
+
+ $output = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $output->expects($this->any())->method('isWritable')->willReturn(True);
+ $output
+ ->expects($this->once())
+ ->method('write')
+ ->with('foo');
+
+ $composite->pipe($output);
+ $readable->emit('data', array('foo'));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/DuplexResourceStreamIntegrationTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/DuplexResourceStreamIntegrationTest.php
new file mode 100644
index 0000000..fb5f02a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/DuplexResourceStreamIntegrationTest.php
@@ -0,0 +1,352 @@
+markTestSkipped('Loop implementation not available');
+ }
+
+ $loop = $loopFactory();
+
+ list($sockA, $sockB) = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, 0);
+
+ $bufferSize = 4096;
+ $streamA = new DuplexResourceStream($sockA, $loop, $bufferSize);
+ $streamB = new DuplexResourceStream($sockB, $loop, $bufferSize);
+
+ $testString = str_repeat("*", $bufferSize + 1);
+
+ $buffer = "";
+ $streamB->on('data', function ($data) use (&$buffer) {
+ $buffer .= $data;
+ });
+
+ $streamA->write($testString);
+
+ $this->loopTick($loop);
+ $this->loopTick($loop);
+ $this->loopTick($loop);
+
+ $streamA->close();
+ $streamB->close();
+
+ $this->assertEquals($testString, $buffer);
+ }
+
+ /**
+ * @dataProvider loopProvider
+ */
+ public function testWriteLargeChunk($condition, $loopFactory)
+ {
+ if (true !== $condition()) {
+ return $this->markTestSkipped('Loop implementation not available');
+ }
+
+ $loop = $loopFactory();
+
+ list($sockA, $sockB) = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, 0);
+
+ $streamA = new DuplexResourceStream($sockA, $loop);
+ $streamB = new DuplexResourceStream($sockB, $loop);
+
+ // limit seems to be 192 KiB
+ $size = 256 * 1024;
+
+ // sending side sends and expects clean close with no errors
+ $streamA->end(str_repeat('*', $size));
+ $streamA->on('close', $this->expectCallableOnce());
+ $streamA->on('error', $this->expectCallableNever());
+
+ // receiving side counts bytes and expects clean close with no errors
+ $received = 0;
+ $streamB->on('data', function ($chunk) use (&$received) {
+ $received += strlen($chunk);
+ });
+ $streamB->on('close', $this->expectCallableOnce());
+ $streamB->on('error', $this->expectCallableNever());
+
+ $loop->run();
+
+ $streamA->close();
+ $streamB->close();
+
+ $this->assertEquals($size, $received);
+ }
+
+ /**
+ * @dataProvider loopProvider
+ */
+ public function testDoesNotEmitDataIfNothingHasBeenWritten($condition, $loopFactory)
+ {
+ if (true !== $condition()) {
+ return $this->markTestSkipped('Loop implementation not available');
+ }
+
+ $loop = $loopFactory();
+
+ list($sockA, $sockB) = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, 0);
+
+ $streamA = new DuplexResourceStream($sockA, $loop);
+ $streamB = new DuplexResourceStream($sockB, $loop);
+
+ // end streamA without writing any data
+ $streamA->end();
+
+ // streamB should not emit any data
+ $streamB->on('data', $this->expectCallableNever());
+
+ $loop->run();
+
+ $streamA->close();
+ $streamB->close();
+ }
+
+ /**
+ * @dataProvider loopProvider
+ */
+ public function testDoesNotWriteDataIfRemoteSideFromPairHasBeenClosed($condition, $loopFactory)
+ {
+ if (true !== $condition()) {
+ return $this->markTestSkipped('Loop implementation not available');
+ }
+
+ $loop = $loopFactory();
+
+ list($sockA, $sockB) = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, 0);
+
+ $streamA = new DuplexResourceStream($sockA, $loop);
+ $streamB = new DuplexResourceStream($sockB, $loop);
+
+ // end streamA without writing any data
+ $streamA->pause();
+ $streamA->write('hello');
+ $streamA->on('close', $this->expectCallableOnce());
+
+ $streamB->on('data', $this->expectCallableNever());
+ $streamB->close();
+
+ $loop->run();
+
+ $streamA->close();
+ $streamB->close();
+ }
+
+ /**
+ * @dataProvider loopProvider
+ */
+ public function testDoesNotWriteDataIfServerSideHasBeenClosed($condition, $loopFactory)
+ {
+ if (true !== $condition()) {
+ return $this->markTestSkipped('Loop implementation not available');
+ }
+
+ $loop = $loopFactory();
+
+ $server = stream_socket_server('tcp://127.0.0.1:0');
+
+ $client = stream_socket_client(stream_socket_get_name($server, false));
+ $peer = stream_socket_accept($server);
+
+ $streamA = new DuplexResourceStream($client, $loop);
+ $streamB = new DuplexResourceStream($peer, $loop);
+
+ // end streamA without writing any data
+ $streamA->pause();
+ $streamA->write('hello');
+ $streamA->on('close', $this->expectCallableOnce());
+
+ $streamB->on('data', $this->expectCallableNever());
+ $streamB->close();
+
+ $loop->run();
+
+ $streamA->close();
+ $streamB->close();
+ }
+
+ /**
+ * @dataProvider loopProvider
+ */
+ public function testDoesNotWriteDataIfClientSideHasBeenClosed($condition, $loopFactory)
+ {
+ if (true !== $condition()) {
+ return $this->markTestSkipped('Loop implementation not available');
+ }
+
+ $loop = $loopFactory();
+
+ $server = stream_socket_server('tcp://127.0.0.1:0');
+
+ $client = stream_socket_client(stream_socket_get_name($server, false));
+ $peer = stream_socket_accept($server);
+
+ $streamA = new DuplexResourceStream($peer, $loop);
+ $streamB = new DuplexResourceStream($client, $loop);
+
+ // end streamA without writing any data
+ $streamA->pause();
+ $streamA->write('hello');
+ $streamA->on('close', $this->expectCallableOnce());
+
+ $streamB->on('data', $this->expectCallableNever());
+ $streamB->close();
+
+ $loop->run();
+
+ $streamA->close();
+ $streamB->close();
+ }
+
+ /**
+ * @dataProvider loopProvider
+ */
+ public function testReadsSingleChunkFromProcessPipe($condition, $loopFactory)
+ {
+ if (true !== $condition()) {
+ return $this->markTestSkipped('Loop implementation not available');
+ }
+
+ $loop = $loopFactory();
+
+ $stream = new ReadableResourceStream(popen('echo test', 'r'), $loop);
+ $stream->on('data', $this->expectCallableOnceWith("test\n"));
+ $stream->on('end', $this->expectCallableOnce());
+ $stream->on('error', $this->expectCallableNever());
+
+ $loop->run();
+ }
+
+ /**
+ * @dataProvider loopProvider
+ */
+ public function testReadsMultipleChunksFromProcessPipe($condition, $loopFactory)
+ {
+ if (true !== $condition()) {
+ return $this->markTestSkipped('Loop implementation not available');
+ }
+
+ $loop = $loopFactory();
+
+ $stream = new ReadableResourceStream(popen('echo a;sleep 0.1;echo b;sleep 0.1;echo c', 'r'), $loop);
+
+ $buffer = '';
+ $stream->on('data', function ($chunk) use (&$buffer) {
+ $buffer .= $chunk;
+ });
+
+ $stream->on('end', $this->expectCallableOnce());
+ $stream->on('error', $this->expectCallableNever());
+
+ $loop->run();
+
+ $this->assertEquals("a\n" . "b\n" . "c\n", $buffer);
+ }
+
+ /**
+ * @dataProvider loopProvider
+ */
+ public function testReadsLongChunksFromProcessPipe($condition, $loopFactory)
+ {
+ if (true !== $condition()) {
+ return $this->markTestSkipped('Loop implementation not available');
+ }
+
+ $loop = $loopFactory();
+
+ $stream = new ReadableResourceStream(popen('dd if=/dev/zero bs=12345 count=1234 2>&-', 'r'), $loop);
+
+ $bytes = 0;
+ $stream->on('data', function ($chunk) use (&$bytes) {
+ $bytes += strlen($chunk);
+ });
+
+ $stream->on('end', $this->expectCallableOnce());
+ $stream->on('error', $this->expectCallableNever());
+
+ $loop->run();
+
+ $this->assertEquals(12345 * 1234, $bytes);
+ }
+
+ /**
+ * @dataProvider loopProvider
+ */
+ public function testReadsNothingFromProcessPipeWithNoOutput($condition, $loopFactory)
+ {
+ if (true !== $condition()) {
+ return $this->markTestSkipped('Loop implementation not available');
+ }
+
+ $loop = $loopFactory();
+
+ $stream = new ReadableResourceStream(popen('true', 'r'), $loop);
+ $stream->on('data', $this->expectCallableNever());
+ $stream->on('end', $this->expectCallableOnce());
+ $stream->on('error', $this->expectCallableNever());
+
+ $loop->run();
+ }
+
+ private function loopTick(LoopInterface $loop)
+ {
+ $loop->addTimer(0, function () use ($loop) {
+ $loop->stop();
+ });
+ $loop->run();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/DuplexResourceStreamTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/DuplexResourceStreamTest.php
new file mode 100644
index 0000000..3212ae8
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/DuplexResourceStreamTest.php
@@ -0,0 +1,495 @@
+createLoopMock();
+
+ new DuplexResourceStream($stream, $loop);
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::__construct
+ * @doesNotPerformAssertions
+ */
+ public function testConstructorWithExcessiveMode()
+ {
+ // excessive flags are ignored for temp streams, so we have to use a file stream
+ $name = tempnam(sys_get_temp_dir(), 'test');
+ $stream = @fopen($name, 'r+eANYTHING');
+ unlink($name);
+
+ $loop = $this->createLoopMock();
+ $buffer = new DuplexResourceStream($stream, $loop);
+ $buffer->close();
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::__construct
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructorThrowsExceptionOnInvalidStream()
+ {
+ $loop = $this->createLoopMock();
+
+ new DuplexResourceStream('breakme', $loop);
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::__construct
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructorThrowsExceptionOnWriteOnlyStream()
+ {
+ if (defined('HHVM_VERSION')) {
+ $this->markTestSkipped('HHVM does not report fopen mode for STDOUT');
+ }
+
+ $loop = $this->createLoopMock();
+
+ new DuplexResourceStream(STDOUT, $loop);
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::__construct
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructorThrowsExceptionOnWriteOnlyStreamWithExcessiveMode()
+ {
+ // excessive flags are ignored for temp streams, so we have to use a file stream
+ $name = tempnam(sys_get_temp_dir(), 'test');
+ $stream = fopen($name, 'weANYTHING');
+ unlink($name);
+
+ $loop = $this->createLoopMock();
+ new DuplexResourceStream($stream, $loop);
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::__construct
+ * @expectedException RunTimeException
+ */
+ public function testConstructorThrowsExceptionIfStreamDoesNotSupportNonBlocking()
+ {
+ if (!in_array('blocking', stream_get_wrappers())) {
+ stream_wrapper_register('blocking', 'React\Tests\Stream\EnforceBlockingWrapper');
+ }
+
+ $stream = fopen('blocking://test', 'r+');
+ $loop = $this->createLoopMock();
+
+ new DuplexResourceStream($stream, $loop);
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::__construct
+ * @doesNotPerformAssertions
+ */
+ public function testConstructorAcceptsBuffer()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+
+ $conn = new DuplexResourceStream($stream, $loop, null, $buffer);
+ }
+
+ public function testCloseShouldEmitCloseEvent()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->on('close', $this->expectCallableOnce());
+ $conn->on('end', $this->expectCallableNever());
+
+ $conn->close();
+
+ $this->assertFalse($conn->isReadable());
+ }
+
+ public function testEndShouldEndBuffer()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $buffer->expects($this->once())->method('end')->with('foo');
+
+ $conn = new DuplexResourceStream($stream, $loop, null, $buffer);
+ $conn->end('foo');
+ }
+
+
+ public function testEndAfterCloseIsNoOp()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $buffer->expects($this->never())->method('end');
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->close();
+ $conn->end();
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::__construct
+ * @covers React\Stream\DuplexResourceStream::handleData
+ */
+ public function testDataEvent()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $capturedData = null;
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->on('data', function ($data) use (&$capturedData) {
+ $capturedData = $data;
+ });
+
+ fwrite($stream, "foobar\n");
+ rewind($stream);
+
+ $conn->handleData($stream);
+ $this->assertSame("foobar\n", $capturedData);
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::__construct
+ * @covers React\Stream\DuplexResourceStream::handleData
+ */
+ public function testDataEventDoesEmitOneChunkMatchingBufferSize()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $capturedData = null;
+
+ $conn = new DuplexResourceStream($stream, $loop, 4321);
+ $conn->on('data', function ($data) use (&$capturedData) {
+ $capturedData = $data;
+ });
+
+ fwrite($stream, str_repeat("a", 100000));
+ rewind($stream);
+
+ $conn->handleData($stream);
+
+ $this->assertTrue($conn->isReadable());
+ $this->assertEquals(4321, strlen($capturedData));
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::__construct
+ * @covers React\Stream\DuplexResourceStream::handleData
+ */
+ public function testDataEventDoesEmitOneChunkUntilStreamEndsWhenBufferSizeIsInfinite()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $capturedData = null;
+
+ $conn = new DuplexResourceStream($stream, $loop, -1);
+
+ $conn->on('data', function ($data) use (&$capturedData) {
+ $capturedData = $data;
+ });
+
+ fwrite($stream, str_repeat("a", 100000));
+ rewind($stream);
+
+ $conn->handleData($stream);
+
+ $this->assertTrue($conn->isReadable());
+ $this->assertEquals(100000, strlen($capturedData));
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::handleData
+ */
+ public function testEmptyStreamShouldNotEmitData()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->on('data', $this->expectCallableNever());
+
+ $conn->handleData($stream);
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::write
+ */
+ public function testWrite()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createWriteableLoopMock();
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->write("foo\n");
+
+ rewind($stream);
+ $this->assertSame("foo\n", fgets($stream));
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::end
+ * @covers React\Stream\DuplexResourceStream::isReadable
+ * @covers React\Stream\DuplexResourceStream::isWritable
+ */
+ public function testEnd()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->end();
+
+ $this->assertFalse(is_resource($stream));
+ $this->assertFalse($conn->isReadable());
+ $this->assertFalse($conn->isWritable());
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::end
+ */
+ public function testEndRemovesReadStreamFromLoop()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+ $loop->expects($this->once())->method('removeReadStream')->with($stream);
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->end('bye');
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::pause
+ */
+ public function testPauseRemovesReadStreamFromLoop()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+ $loop->expects($this->once())->method('removeReadStream')->with($stream);
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->pause();
+ $conn->pause();
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::pause
+ */
+ public function testResumeDoesAddStreamToLoopOnlyOnce()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->resume();
+ $conn->resume();
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::close
+ */
+ public function testCloseRemovesReadStreamFromLoop()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+ $loop->expects($this->once())->method('removeReadStream')->with($stream);
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->close();
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::close
+ */
+ public function testCloseAfterPauseRemovesReadStreamFromLoopOnlyOnce()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+ $loop->expects($this->once())->method('removeReadStream')->with($stream);
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->pause();
+ $conn->close();
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::close
+ */
+ public function testResumeAfterCloseDoesAddReadStreamToLoopOnlyOnce()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->close();
+ $conn->resume();
+ }
+
+ public function testEndedStreamsShouldNotWrite()
+ {
+ $file = tempnam(sys_get_temp_dir(), 'reactphptest_');
+ $stream = fopen($file, 'r+');
+ $loop = $this->createWriteableLoopMock();
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->write("foo\n");
+ $conn->end();
+
+ $res = $conn->write("bar\n");
+ $stream = fopen($file, 'r');
+
+ $this->assertSame("foo\n", fgets($stream));
+ $this->assertFalse($res);
+
+ unlink($file);
+ }
+
+ public function testPipeShouldReturnDestination()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $dest = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+
+ $this->assertSame($dest, $conn->pipe($dest));
+ }
+
+ public function testBufferEventsShouldBubbleUp()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop);
+ $conn = new DuplexResourceStream($stream, $loop, null, $buffer);
+
+ $conn->on('drain', $this->expectCallableOnce());
+ $conn->on('error', $this->expectCallableOnce());
+
+ $buffer->emit('drain');
+ $buffer->emit('error', array(new \RuntimeException('Whoops')));
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::handleData
+ */
+ public function testClosingStreamInDataEventShouldNotTriggerError()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->on('error', $this->expectCallableNever());
+ $conn->on('data', function ($data) use ($conn) {
+ $conn->close();
+ });
+
+ fwrite($stream, "foobar\n");
+ rewind($stream);
+
+ $conn->handleData($stream);
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::handleData
+ */
+ public function testDataFiltered()
+ {
+ $stream = fopen('php://temp', 'r+');
+
+ // add a filter which removes every 'a' when reading
+ Filter\append($stream, function ($chunk) {
+ return str_replace('a', '', $chunk);
+ }, STREAM_FILTER_READ);
+
+ $loop = $this->createLoopMock();
+
+ $capturedData = null;
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->on('data', function ($data) use (&$capturedData) {
+ $capturedData = $data;
+ });
+
+ fwrite($stream, "foobar\n");
+ rewind($stream);
+
+ $conn->handleData($stream);
+ $this->assertSame("foobr\n", $capturedData);
+ }
+
+ /**
+ * @covers React\Stream\DuplexResourceStream::handleData
+ */
+ public function testDataErrorShouldEmitErrorAndClose()
+ {
+ $stream = fopen('php://temp', 'r+');
+
+ // add a filter which returns an error when encountering an 'a' when reading
+ Filter\append($stream, function ($chunk) {
+ if (strpos($chunk, 'a') !== false) {
+ throw new \Exception('Invalid');
+ }
+ return $chunk;
+ }, STREAM_FILTER_READ);
+
+ $loop = $this->createLoopMock();
+
+ $conn = new DuplexResourceStream($stream, $loop);
+ $conn->on('data', $this->expectCallableNever());
+ $conn->on('error', $this->expectCallableOnce());
+ $conn->on('close', $this->expectCallableOnce());
+
+ fwrite($stream, "foobar\n");
+ rewind($stream);
+
+ $conn->handleData($stream);
+ }
+
+ private function createWriteableLoopMock()
+ {
+ $loop = $this->createLoopMock();
+ $loop
+ ->expects($this->once())
+ ->method('addWriteStream')
+ ->will($this->returnCallback(function ($stream, $listener) {
+ call_user_func($listener, $stream);
+ }));
+
+ return $loop;
+ }
+
+ private function createLoopMock()
+ {
+ return $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/EnforceBlockingWrapper.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/EnforceBlockingWrapper.php
new file mode 100644
index 0000000..39c0487
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/EnforceBlockingWrapper.php
@@ -0,0 +1,35 @@
+on('data', function ($chunk) use (&$buffer) {
+ $buffer .= $chunk;
+ });
+
+ $stream->on('error', $this->expectCallableNever());
+
+ $stream->write("POST /post HTTP/1.0\r\nHost: httpbin.org\r\nContent-Length: $size\r\n\r\n" . str_repeat('.', $size));
+
+ $this->awaitStreamClose($stream, $loop);
+
+ $this->assertNotEquals('', $buffer);
+ }
+
+ public function testUploadBiggerBlockPlain()
+ {
+ $size = 50 * 1000;
+ $stream = stream_socket_client('tcp://httpbin.org:80');
+
+ $loop = Factory::create();
+ $stream = new DuplexResourceStream($stream, $loop);
+
+ $buffer = '';
+ $stream->on('data', function ($chunk) use (&$buffer) {
+ $buffer .= $chunk;
+ });
+
+ $stream->on('error', $this->expectCallableNever());
+
+ $stream->write("POST /post HTTP/1.0\r\nHost: httpbin.org\r\nContent-Length: $size\r\n\r\n" . str_repeat('.', $size));
+
+ $this->awaitStreamClose($stream, $loop);
+
+ $this->assertNotEquals('', $buffer);
+ }
+
+ public function testUploadKilobyteSecure()
+ {
+ $size = 1000;
+ $stream = stream_socket_client('tls://httpbin.org:443');
+
+ $loop = Factory::create();
+ $stream = new DuplexResourceStream($stream, $loop);
+
+ $buffer = '';
+ $stream->on('data', function ($chunk) use (&$buffer) {
+ $buffer .= $chunk;
+ });
+
+ $stream->on('error', $this->expectCallableNever());
+
+ $stream->write("POST /post HTTP/1.0\r\nHost: httpbin.org\r\nContent-Length: $size\r\n\r\n" . str_repeat('.', $size));
+
+ $this->awaitStreamClose($stream, $loop);
+
+ $this->assertNotEquals('', $buffer);
+ }
+
+ public function testUploadBiggerBlockSecureRequiresSmallerChunkSize()
+ {
+ $size = 50 * 1000;
+ $stream = stream_socket_client('tls://httpbin.org:443');
+
+ $loop = Factory::create();
+ $stream = new DuplexResourceStream(
+ $stream,
+ $loop,
+ null,
+ new WritableResourceStream($stream, $loop, null, 8192)
+ );
+
+ $buffer = '';
+ $stream->on('data', function ($chunk) use (&$buffer) {
+ $buffer .= $chunk;
+ });
+
+ $stream->on('error', $this->expectCallableNever());
+
+ $stream->write("POST /post HTTP/1.0\r\nHost: httpbin.org\r\nContent-Length: $size\r\n\r\n" . str_repeat('.', $size));
+
+ $this->awaitStreamClose($stream, $loop);
+
+ $this->assertNotEquals('', $buffer);
+ }
+
+ private function awaitStreamClose(DuplexResourceStream $stream, LoopInterface $loop, $timeout = 10.0)
+ {
+ $stream->on('close', function () use ($loop) {
+ $loop->stop();
+ });
+
+ $that = $this;
+ $loop->addTimer($timeout, function () use ($loop, $that) {
+ $loop->stop();
+ $that->fail('Timed out while waiting for stream to close');
+ });
+
+ $loop->run();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/ReadableResourceStreamTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/ReadableResourceStreamTest.php
new file mode 100644
index 0000000..20da96f
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/ReadableResourceStreamTest.php
@@ -0,0 +1,372 @@
+createLoopMock();
+
+ new ReadableResourceStream($stream, $loop);
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::__construct
+ * @doesNotPerformAssertions
+ */
+ public function testConstructorWithExcessiveMode()
+ {
+ // excessive flags are ignored for temp streams, so we have to use a file stream
+ $name = tempnam(sys_get_temp_dir(), 'test');
+ $stream = @fopen($name, 'r+eANYTHING');
+ unlink($name);
+
+ $loop = $this->createLoopMock();
+ $buffer = new ReadableResourceStream($stream, $loop);
+ $buffer->close();
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::__construct
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructorThrowsExceptionOnInvalidStream()
+ {
+ $loop = $this->createLoopMock();
+
+ new ReadableResourceStream(false, $loop);
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::__construct
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructorThrowsExceptionOnWriteOnlyStream()
+ {
+ if (defined('HHVM_VERSION')) {
+ $this->markTestSkipped('HHVM does not report fopen mode for STDOUT');
+ }
+
+ $loop = $this->createLoopMock();
+
+ new ReadableResourceStream(STDOUT, $loop);
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::__construct
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructorThrowsExceptionOnWriteOnlyStreamWithExcessiveMode()
+ {
+ // excessive flags are ignored for temp streams, so we have to use a file stream
+ $name = tempnam(sys_get_temp_dir(), 'test');
+ $stream = fopen($name, 'weANYTHING');
+ unlink($name);
+
+ $loop = $this->createLoopMock();
+ new ReadableResourceStream($stream, $loop);
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::__construct
+ * @expectedException RuntimeException
+ */
+ public function testConstructorThrowsExceptionIfStreamDoesNotSupportNonBlocking()
+ {
+ if (!in_array('blocking', stream_get_wrappers())) {
+ stream_wrapper_register('blocking', 'React\Tests\Stream\EnforceBlockingWrapper');
+ }
+
+ $stream = fopen('blocking://test', 'r+');
+ $loop = $this->createLoopMock();
+
+ new ReadableResourceStream($stream, $loop);
+ }
+
+
+ public function testCloseShouldEmitCloseEvent()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->on('close', $this->expectCallableOnce());
+
+ $conn->close();
+
+ $this->assertFalse($conn->isReadable());
+ }
+
+ public function testCloseTwiceShouldEmitCloseEventOnce()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->on('close', $this->expectCallableOnce());
+
+ $conn->close();
+ $conn->close();
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::__construct
+ * @covers React\Stream\ReadableResourceStream::handleData
+ */
+ public function testDataEvent()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $capturedData = null;
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->on('data', function ($data) use (&$capturedData) {
+ $capturedData = $data;
+ });
+
+ fwrite($stream, "foobar\n");
+ rewind($stream);
+
+ $conn->handleData($stream);
+ $this->assertSame("foobar\n", $capturedData);
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::__construct
+ * @covers React\Stream\ReadableResourceStream::handleData
+ */
+ public function testDataEventDoesEmitOneChunkMatchingBufferSize()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $capturedData = null;
+
+ $conn = new ReadableResourceStream($stream, $loop, 4321);
+ $conn->on('data', function ($data) use (&$capturedData) {
+ $capturedData = $data;
+ });
+
+ fwrite($stream, str_repeat("a", 100000));
+ rewind($stream);
+
+ $conn->handleData($stream);
+
+ $this->assertTrue($conn->isReadable());
+ $this->assertEquals(4321, strlen($capturedData));
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::__construct
+ * @covers React\Stream\ReadableResourceStream::handleData
+ */
+ public function testDataEventDoesEmitOneChunkUntilStreamEndsWhenBufferSizeIsInfinite()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $capturedData = null;
+
+ $conn = new ReadableResourceStream($stream, $loop, -1);
+
+ $conn->on('data', function ($data) use (&$capturedData) {
+ $capturedData = $data;
+ });
+
+ fwrite($stream, str_repeat("a", 100000));
+ rewind($stream);
+
+ $conn->handleData($stream);
+
+ $this->assertTrue($conn->isReadable());
+ $this->assertEquals(100000, strlen($capturedData));
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::handleData
+ */
+ public function testEmptyStreamShouldNotEmitData()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->on('data', $this->expectCallableNever());
+
+ $conn->handleData($stream);
+ }
+
+ public function testPipeShouldReturnDestination()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $dest = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+
+ $this->assertSame($dest, $conn->pipe($dest));
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::handleData
+ */
+ public function testClosingStreamInDataEventShouldNotTriggerError()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->on('error', $this->expectCallableNever());
+ $conn->on('data', function ($data) use ($conn) {
+ $conn->close();
+ });
+
+ fwrite($stream, "foobar\n");
+ rewind($stream);
+
+ $conn->handleData($stream);
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::pause
+ */
+ public function testPauseRemovesReadStreamFromLoop()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+ $loop->expects($this->once())->method('removeReadStream')->with($stream);
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->pause();
+ $conn->pause();
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::pause
+ */
+ public function testResumeDoesAddStreamToLoopOnlyOnce()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->resume();
+ $conn->resume();
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::close
+ */
+ public function testCloseRemovesReadStreamFromLoop()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+ $loop->expects($this->once())->method('removeReadStream')->with($stream);
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->close();
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::close
+ */
+ public function testCloseAfterPauseRemovesReadStreamFromLoopOnce()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+ $loop->expects($this->once())->method('removeReadStream')->with($stream);
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->pause();
+ $conn->close();
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::close
+ */
+ public function testResumeAfterCloseDoesAddReadStreamToLoopOnlyOnce()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addReadStream')->with($stream);
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->close();
+ $conn->resume();
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::handleData
+ */
+ public function testDataFiltered()
+ {
+ $stream = fopen('php://temp', 'r+');
+
+ // add a filter which removes every 'a' when reading
+ Filter\append($stream, function ($chunk) {
+ return str_replace('a', '', $chunk);
+ }, STREAM_FILTER_READ);
+
+ $loop = $this->createLoopMock();
+
+ $capturedData = null;
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->on('data', function ($data) use (&$capturedData) {
+ $capturedData = $data;
+ });
+
+ fwrite($stream, "foobar\n");
+ rewind($stream);
+
+ $conn->handleData($stream);
+ $this->assertSame("foobr\n", $capturedData);
+ }
+
+ /**
+ * @covers React\Stream\ReadableResourceStream::handleData
+ */
+ public function testDataErrorShouldEmitErrorAndClose()
+ {
+ $stream = fopen('php://temp', 'r+');
+
+ // add a filter which returns an error when encountering an 'a' when reading
+ Filter\append($stream, function ($chunk) {
+ if (strpos($chunk, 'a') !== false) {
+ throw new \Exception('Invalid');
+ }
+ return $chunk;
+ }, STREAM_FILTER_READ);
+
+ $loop = $this->createLoopMock();
+
+ $conn = new ReadableResourceStream($stream, $loop);
+ $conn->on('data', $this->expectCallableNever());
+ $conn->on('error', $this->expectCallableOnce());
+ $conn->on('close', $this->expectCallableOnce());
+
+ fwrite($stream, "foobar\n");
+ rewind($stream);
+
+ $conn->handleData($stream);
+ }
+
+ private function createLoopMock()
+ {
+ return $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/Stub/ReadableStreamStub.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/Stub/ReadableStreamStub.php
new file mode 100644
index 0000000..6984f24
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/Stub/ReadableStreamStub.php
@@ -0,0 +1,61 @@
+emit('data', array($data));
+ }
+
+ // trigger error event
+ public function error($error)
+ {
+ $this->emit('error', array($error));
+ }
+
+ // trigger end event
+ public function end()
+ {
+ $this->emit('end', array());
+ }
+
+ public function pause()
+ {
+ $this->paused = true;
+ }
+
+ public function resume()
+ {
+ $this->paused = false;
+ }
+
+ public function close()
+ {
+ $this->readable = false;
+
+ $this->emit('close');
+ }
+
+ public function pipe(WritableStreamInterface $dest, array $options = array())
+ {
+ Util::pipe($this, $dest, $options);
+
+ return $dest;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/TestCase.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/TestCase.php
new file mode 100644
index 0000000..c8fc1db
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/TestCase.php
@@ -0,0 +1,54 @@
+createCallableMock();
+ $mock
+ ->expects($this->exactly($amount))
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ protected function expectCallableOnce()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->once())
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ protected function expectCallableOnceWith($value)
+ {
+ $callback = $this->createCallableMock();
+ $callback
+ ->expects($this->once())
+ ->method('__invoke')
+ ->with($value);
+
+ return $callback;
+ }
+
+ protected function expectCallableNever()
+ {
+ $mock = $this->createCallableMock();
+ $mock
+ ->expects($this->never())
+ ->method('__invoke');
+
+ return $mock;
+ }
+
+ protected function createCallableMock()
+ {
+ return $this->getMockBuilder('React\Tests\Stream\CallableStub')->getMock();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/ThroughStreamTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/ThroughStreamTest.php
new file mode 100644
index 0000000..a98badf
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/ThroughStreamTest.php
@@ -0,0 +1,267 @@
+write('foo');
+
+ $this->assertTrue($ret);
+ }
+
+ /** @test */
+ public function itShouldEmitAnyDataWrittenToIt()
+ {
+ $through = new ThroughStream();
+ $through->on('data', $this->expectCallableOnceWith('foo'));
+ $through->write('foo');
+ }
+
+ /** @test */
+ public function itShouldEmitAnyDataWrittenToItPassedThruFunction()
+ {
+ $through = new ThroughStream('strtoupper');
+ $through->on('data', $this->expectCallableOnceWith('FOO'));
+ $through->write('foo');
+ }
+
+ /** @test */
+ public function itShouldEmitAnyDataWrittenToItPassedThruCallback()
+ {
+ $through = new ThroughStream('strtoupper');
+ $through->on('data', $this->expectCallableOnceWith('FOO'));
+ $through->write('foo');
+ }
+
+ /** @test */
+ public function itShouldEmitErrorAndCloseIfCallbackThrowsException()
+ {
+ $through = new ThroughStream(function () {
+ throw new \RuntimeException();
+ });
+ $through->on('error', $this->expectCallableOnce());
+ $through->on('close', $this->expectCallableOnce());
+ $through->on('data', $this->expectCallableNever());
+ $through->on('end', $this->expectCallableNever());
+
+ $through->write('foo');
+
+ $this->assertFalse($through->isReadable());
+ $this->assertFalse($through->isWritable());
+ }
+
+ /** @test */
+ public function itShouldEmitErrorAndCloseIfCallbackThrowsExceptionOnEnd()
+ {
+ $through = new ThroughStream(function () {
+ throw new \RuntimeException();
+ });
+ $through->on('error', $this->expectCallableOnce());
+ $through->on('close', $this->expectCallableOnce());
+ $through->on('data', $this->expectCallableNever());
+ $through->on('end', $this->expectCallableNever());
+
+ $through->end('foo');
+
+ $this->assertFalse($through->isReadable());
+ $this->assertFalse($through->isWritable());
+ }
+
+ /** @test */
+ public function itShouldReturnFalseForAnyDataWrittenToItWhenPaused()
+ {
+ $through = new ThroughStream();
+ $through->pause();
+ $ret = $through->write('foo');
+
+ $this->assertFalse($ret);
+ }
+
+ /** @test */
+ public function itShouldEmitDrainOnResumeAfterReturnFalseForAnyDataWrittenToItWhenPaused()
+ {
+ $through = new ThroughStream();
+ $through->pause();
+ $through->write('foo');
+
+ $through->on('drain', $this->expectCallableOnce());
+ $through->resume();
+ }
+
+ /** @test */
+ public function itShouldReturnTrueForAnyDataWrittenToItWhenResumedAfterPause()
+ {
+ $through = new ThroughStream();
+ $through->on('drain', $this->expectCallableNever());
+ $through->pause();
+ $through->resume();
+ $ret = $through->write('foo');
+
+ $this->assertTrue($ret);
+ }
+
+ /** @test */
+ public function pipingStuffIntoItShouldWork()
+ {
+ $readable = new ThroughStream();
+
+ $through = new ThroughStream();
+ $through->on('data', $this->expectCallableOnceWith('foo'));
+
+ $readable->pipe($through);
+ $readable->emit('data', array('foo'));
+ }
+
+ /** @test */
+ public function endShouldEmitEndAndClose()
+ {
+ $through = new ThroughStream();
+ $through->on('data', $this->expectCallableNever());
+ $through->on('end', $this->expectCallableOnce());
+ $through->on('close', $this->expectCallableOnce());
+ $through->end();
+ }
+
+ /** @test */
+ public function endShouldCloseTheStream()
+ {
+ $through = new ThroughStream();
+ $through->on('data', $this->expectCallableNever());
+ $through->end();
+
+ $this->assertFalse($through->isReadable());
+ $this->assertFalse($through->isWritable());
+ }
+
+ /** @test */
+ public function endShouldWriteDataBeforeClosing()
+ {
+ $through = new ThroughStream();
+ $through->on('data', $this->expectCallableOnceWith('foo'));
+ $through->end('foo');
+
+ $this->assertFalse($through->isReadable());
+ $this->assertFalse($through->isWritable());
+ }
+
+ /** @test */
+ public function endTwiceShouldOnlyEmitOnce()
+ {
+ $through = new ThroughStream();
+ $through->on('data', $this->expectCallableOnce('first'));
+ $through->end('first');
+ $through->end('ignored');
+ }
+
+ /** @test */
+ public function writeAfterEndShouldReturnFalse()
+ {
+ $through = new ThroughStream();
+ $through->on('data', $this->expectCallableNever());
+ $through->end();
+
+ $this->assertFalse($through->write('foo'));
+ }
+
+ /** @test */
+ public function writeDataWillCloseStreamShouldReturnFalse()
+ {
+ $through = new ThroughStream();
+ $through->on('data', array($through, 'close'));
+
+ $this->assertFalse($through->write('foo'));
+ }
+
+ /** @test */
+ public function writeDataToPausedShouldReturnFalse()
+ {
+ $through = new ThroughStream();
+ $through->pause();
+
+ $this->assertFalse($through->write('foo'));
+ }
+
+ /** @test */
+ public function writeDataToResumedShouldReturnTrue()
+ {
+ $through = new ThroughStream();
+ $through->pause();
+ $through->resume();
+
+ $this->assertTrue($through->write('foo'));
+ }
+
+ /** @test */
+ public function itShouldBeReadableByDefault()
+ {
+ $through = new ThroughStream();
+ $this->assertTrue($through->isReadable());
+ }
+
+ /** @test */
+ public function itShouldBeWritableByDefault()
+ {
+ $through = new ThroughStream();
+ $this->assertTrue($through->isWritable());
+ }
+
+ /** @test */
+ public function closeShouldCloseOnce()
+ {
+ $through = new ThroughStream();
+
+ $through->on('close', $this->expectCallableOnce());
+
+ $through->close();
+
+ $this->assertFalse($through->isReadable());
+ $this->assertFalse($through->isWritable());
+ }
+
+ /** @test */
+ public function doubleCloseShouldCloseOnce()
+ {
+ $through = new ThroughStream();
+
+ $through->on('close', $this->expectCallableOnce());
+
+ $through->close();
+ $through->close();
+
+ $this->assertFalse($through->isReadable());
+ $this->assertFalse($through->isWritable());
+ }
+
+ /** @test */
+ public function pipeShouldPipeCorrectly()
+ {
+ $output = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $output->expects($this->any())->method('isWritable')->willReturn(True);
+ $output
+ ->expects($this->once())
+ ->method('write')
+ ->with('foo');
+
+ $through = new ThroughStream();
+ $through->pipe($output);
+ $through->write('foo');
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/UtilTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/UtilTest.php
new file mode 100644
index 0000000..3d113ab
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/UtilTest.php
@@ -0,0 +1,273 @@
+getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+
+ $ret = Util::pipe($readable, $writable);
+
+ $this->assertSame($writable, $ret);
+ }
+
+ public function testPipeNonReadableSourceShouldDoNothing()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->any())
+ ->method('isReadable')
+ ->willReturn(false);
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->never())
+ ->method('isWritable');
+ $writable
+ ->expects($this->never())
+ ->method('end');
+
+ Util::pipe($readable, $writable);
+ }
+
+ public function testPipeIntoNonWritableDestinationShouldPauseSource()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->any())
+ ->method('isReadable')
+ ->willReturn(true);
+ $readable
+ ->expects($this->once())
+ ->method('pause');
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->any())
+ ->method('isWritable')
+ ->willReturn(false);
+ $writable
+ ->expects($this->never())
+ ->method('end');
+
+ Util::pipe($readable, $writable);
+ }
+
+ public function testPipeClosingDestPausesSource()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable
+ ->expects($this->any())
+ ->method('isReadable')
+ ->willReturn(true);
+ $readable
+ ->expects($this->once())
+ ->method('pause');
+
+ $writable = new ThroughStream();
+
+ Util::pipe($readable, $writable);
+
+ $writable->close();
+ }
+
+ public function testPipeWithEnd()
+ {
+ $readable = new Stub\ReadableStreamStub();
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->any())
+ ->method('isWritable')
+ ->willReturn(true);
+ $writable
+ ->expects($this->once())
+ ->method('end');
+
+ Util::pipe($readable, $writable);
+
+ $readable->end();
+ }
+
+ public function testPipeWithoutEnd()
+ {
+ $readable = new Stub\ReadableStreamStub();
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->any())
+ ->method('isWritable')
+ ->willReturn(true);
+ $writable
+ ->expects($this->never())
+ ->method('end');
+
+ Util::pipe($readable, $writable, array('end' => false));
+
+ $readable->end();
+ }
+
+ public function testPipeWithTooSlowWritableShouldPauseReadable()
+ {
+ $readable = new Stub\ReadableStreamStub();
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->any())
+ ->method('isWritable')
+ ->willReturn(true);
+ $writable
+ ->expects($this->once())
+ ->method('write')
+ ->with('some data')
+ ->will($this->returnValue(false));
+
+ $readable->pipe($writable);
+
+ $this->assertFalse($readable->paused);
+ $readable->write('some data');
+ $this->assertTrue($readable->paused);
+ }
+
+ public function testPipeWithTooSlowWritableShouldResumeOnDrain()
+ {
+ $readable = new Stub\ReadableStreamStub();
+
+ $onDrain = null;
+
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable
+ ->expects($this->any())
+ ->method('isWritable')
+ ->willReturn(true);
+ $writable
+ ->expects($this->any())
+ ->method('on')
+ ->will($this->returnCallback(function ($name, $callback) use (&$onDrain) {
+ if ($name === 'drain') {
+ $onDrain = $callback;
+ }
+ }));
+
+ $readable->pipe($writable);
+ $readable->pause();
+
+ $this->assertTrue($readable->paused);
+ $this->assertNotNull($onDrain);
+ $onDrain();
+ $this->assertFalse($readable->paused);
+ }
+
+ public function testPipeWithWritableResourceStream()
+ {
+ $readable = new Stub\ReadableStreamStub();
+
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $buffer = new WritableResourceStream($stream, $loop);
+
+ $readable->pipe($buffer);
+
+ $readable->write('hello, I am some ');
+ $readable->write('random data');
+
+ $buffer->handleWrite();
+ rewind($stream);
+ $this->assertSame('hello, I am some random data', stream_get_contents($stream));
+ }
+
+ public function testPipeSetsUpListeners()
+ {
+ $source = new ThroughStream();
+ $dest = new ThroughStream();
+
+ $this->assertCount(0, $source->listeners('data'));
+ $this->assertCount(0, $source->listeners('end'));
+ $this->assertCount(0, $dest->listeners('drain'));
+
+ Util::pipe($source, $dest);
+
+ $this->assertCount(1, $source->listeners('data'));
+ $this->assertCount(1, $source->listeners('end'));
+ $this->assertCount(1, $dest->listeners('drain'));
+ }
+
+ public function testPipeClosingSourceRemovesListeners()
+ {
+ $source = new ThroughStream();
+ $dest = new ThroughStream();
+
+ Util::pipe($source, $dest);
+
+ $source->close();
+
+ $this->assertCount(0, $source->listeners('data'));
+ $this->assertCount(0, $source->listeners('end'));
+ $this->assertCount(0, $dest->listeners('drain'));
+ }
+
+ public function testPipeClosingDestRemovesListeners()
+ {
+ $source = new ThroughStream();
+ $dest = new ThroughStream();
+
+ Util::pipe($source, $dest);
+
+ $dest->close();
+
+ $this->assertCount(0, $source->listeners('data'));
+ $this->assertCount(0, $source->listeners('end'));
+ $this->assertCount(0, $dest->listeners('drain'));
+ }
+
+ public function testPipeDuplexIntoSelfEndsOnEnd()
+ {
+ $readable = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
+ $readable->expects($this->any())->method('isReadable')->willReturn(true);
+ $writable = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();
+ $writable->expects($this->any())->method('isWritable')->willReturn(true);
+ $duplex = new CompositeStream($readable, $writable);
+
+ Util::pipe($duplex, $duplex);
+
+ $writable->expects($this->once())->method('end');
+
+ $duplex->emit('end');
+ }
+
+ /** @test */
+ public function forwardEventsShouldSetupForwards()
+ {
+ $source = new ThroughStream();
+ $target = new ThroughStream();
+
+ Util::forwardEvents($source, $target, array('data'));
+ $target->on('data', $this->expectCallableOnce());
+ $target->on('foo', $this->expectCallableNever());
+
+ $source->emit('data', array('hello'));
+ $source->emit('foo', array('bar'));
+ }
+
+ private function createLoopMock()
+ {
+ return $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ }
+
+ private function notEqualTo($value)
+ {
+ return new \PHPUnit_Framework_Constraint_Not($value);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/WritableStreamResourceTest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/WritableStreamResourceTest.php
new file mode 100644
index 0000000..05bce9c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/react/stream/tests/WritableStreamResourceTest.php
@@ -0,0 +1,534 @@
+createLoopMock();
+
+ new WritableResourceStream($stream, $loop);
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::__construct
+ * @doesNotPerformAssertions
+ */
+ public function testConstructorWithExcessiveMode()
+ {
+ // excessive flags are ignored for temp streams, so we have to use a file stream
+ $name = tempnam(sys_get_temp_dir(), 'test');
+ $stream = @fopen($name, 'w+eANYTHING');
+ unlink($name);
+
+ $loop = $this->createLoopMock();
+ $buffer = new WritableResourceStream($stream, $loop);
+ $buffer->close();
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::__construct
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructorThrowsIfNotAValidStreamResource()
+ {
+ $stream = null;
+ $loop = $this->createLoopMock();
+
+ new WritableResourceStream($stream, $loop);
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::__construct
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructorThrowsExceptionOnReadOnlyStream()
+ {
+ $stream = fopen('php://temp', 'r');
+ $loop = $this->createLoopMock();
+
+ new WritableResourceStream($stream, $loop);
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::__construct
+ * @expectedException InvalidArgumentException
+ */
+ public function testConstructorThrowsExceptionOnReadOnlyStreamWithExcessiveMode()
+ {
+ // excessive flags are ignored for temp streams, so we have to use a file stream
+ $name = tempnam(sys_get_temp_dir(), 'test');
+ $stream = fopen($name, 'reANYTHING');
+ unlink($name);
+
+ $loop = $this->createLoopMock();
+ new WritableResourceStream($stream, $loop);
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::__construct
+ * @expectedException RuntimeException
+ */
+ public function testConstructorThrowsExceptionIfStreamDoesNotSupportNonBlocking()
+ {
+ if (!in_array('blocking', stream_get_wrappers())) {
+ stream_wrapper_register('blocking', 'React\Tests\Stream\EnforceBlockingWrapper');
+ }
+
+ $stream = fopen('blocking://test', 'r+');
+ $loop = $this->createLoopMock();
+
+ new WritableResourceStream($stream, $loop);
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::write
+ * @covers React\Stream\WritableResourceStream::handleWrite
+ */
+ public function testWrite()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createWriteableLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop);
+ $buffer->on('error', $this->expectCallableNever());
+
+ $buffer->write("foobar\n");
+ rewind($stream);
+ $this->assertSame("foobar\n", fread($stream, 1024));
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::write
+ */
+ public function testWriteWithDataDoesAddResourceToLoop()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('addWriteStream')->with($this->equalTo($stream));
+
+ $buffer = new WritableResourceStream($stream, $loop);
+
+ $buffer->write("foobar\n");
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::write
+ * @covers React\Stream\WritableResourceStream::handleWrite
+ */
+ public function testEmptyWriteDoesNotAddToLoop()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->never())->method('addWriteStream');
+
+ $buffer = new WritableResourceStream($stream, $loop);
+
+ $buffer->write("");
+ $buffer->write(null);
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::write
+ * @covers React\Stream\WritableResourceStream::handleWrite
+ */
+ public function testWriteReturnsFalseWhenWritableResourceStreamIsFull()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createWriteableLoopMock();
+ $loop->preventWrites = true;
+
+ $buffer = new WritableResourceStream($stream, $loop, 4);
+ $buffer->on('error', $this->expectCallableNever());
+
+ $this->assertTrue($buffer->write("foo"));
+ $loop->preventWrites = false;
+ $this->assertFalse($buffer->write("bar\n"));
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::write
+ */
+ public function testWriteReturnsFalseWhenWritableResourceStreamIsExactlyFull()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop, 3);
+
+ $this->assertFalse($buffer->write("foo"));
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::write
+ * @covers React\Stream\WritableResourceStream::handleWrite
+ */
+ public function testWriteDetectsWhenOtherSideIsClosed()
+ {
+ list($a, $b) = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
+
+ $loop = $this->createWriteableLoopMock();
+
+ $buffer = new WritableResourceStream($a, $loop, 4);
+ $buffer->on('error', $this->expectCallableOnce());
+
+ fclose($b);
+
+ $buffer->write("foo");
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::write
+ * @covers React\Stream\WritableResourceStream::handleWrite
+ */
+ public function testEmitsDrainAfterWriteWhichExceedsBuffer()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop, 2);
+ $buffer->on('error', $this->expectCallableNever());
+ $buffer->on('drain', $this->expectCallableOnce());
+
+ $buffer->write("foo");
+ $buffer->handleWrite();
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::write
+ * @covers React\Stream\WritableResourceStream::handleWrite
+ */
+ public function testWriteInDrain()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop, 2);
+ $buffer->on('error', $this->expectCallableNever());
+
+ $buffer->once('drain', function () use ($buffer) {
+ $buffer->write("bar\n");
+ $buffer->handleWrite();
+ });
+
+ $this->assertFalse($buffer->write("foo\n"));
+ $buffer->handleWrite();
+
+ fseek($stream, 0);
+ $this->assertSame("foo\nbar\n", stream_get_contents($stream));
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::write
+ * @covers React\Stream\WritableResourceStream::handleWrite
+ */
+ public function testDrainAfterWrite()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop, 2);
+
+ $buffer->on('drain', $this->expectCallableOnce());
+
+ $buffer->write("foo");
+ $buffer->handleWrite();
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::handleWrite
+ */
+ public function testDrainAfterWriteWillRemoveResourceFromLoopWithoutClosing()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('removeWriteStream')->with($stream);
+
+ $buffer = new WritableResourceStream($stream, $loop, 2);
+
+ $buffer->on('drain', $this->expectCallableOnce());
+
+ $buffer->on('close', $this->expectCallableNever());
+
+ $buffer->write("foo");
+ $buffer->handleWrite();
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::handleWrite
+ */
+ public function testClosingDuringDrainAfterWriteWillRemoveResourceFromLoopOnceAndClose()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $loop->expects($this->once())->method('removeWriteStream')->with($stream);
+
+ $buffer = new WritableResourceStream($stream, $loop, 2);
+
+ $buffer->on('drain', function () use ($buffer) {
+ $buffer->close();
+ });
+
+ $buffer->on('close', $this->expectCallableOnce());
+
+ $buffer->write("foo");
+ $buffer->handleWrite();
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::end
+ */
+ public function testEndWithoutDataClosesImmediatelyIfWritableResourceStreamIsEmpty()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop);
+ $buffer->on('error', $this->expectCallableNever());
+ $buffer->on('close', $this->expectCallableOnce());
+
+ $this->assertTrue($buffer->isWritable());
+ $buffer->end();
+ $this->assertFalse($buffer->isWritable());
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::end
+ */
+ public function testEndWithoutDataDoesNotCloseIfWritableResourceStreamIsFull()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop);
+ $buffer->on('error', $this->expectCallableNever());
+ $buffer->on('close', $this->expectCallableNever());
+
+ $buffer->write('foo');
+
+ $this->assertTrue($buffer->isWritable());
+ $buffer->end();
+ $this->assertFalse($buffer->isWritable());
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::end
+ */
+ public function testEndWithDataClosesImmediatelyIfWritableResourceStreamFlushes()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $filterBuffer = '';
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop);
+ $buffer->on('error', $this->expectCallableNever());
+ $buffer->on('close', $this->expectCallableOnce());
+
+ Filter\append($stream, function ($chunk) use (&$filterBuffer) {
+ $filterBuffer .= $chunk;
+ return $chunk;
+ });
+
+ $this->assertTrue($buffer->isWritable());
+ $buffer->end('final words');
+ $this->assertFalse($buffer->isWritable());
+
+ $buffer->handleWrite();
+ $this->assertSame('final words', $filterBuffer);
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::end
+ */
+ public function testEndWithDataDoesNotCloseImmediatelyIfWritableResourceStreamIsFull()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop);
+ $buffer->on('error', $this->expectCallableNever());
+ $buffer->on('close', $this->expectCallableNever());
+
+ $buffer->write('foo');
+
+ $this->assertTrue($buffer->isWritable());
+ $buffer->end('final words');
+ $this->assertFalse($buffer->isWritable());
+
+ rewind($stream);
+ $this->assertSame('', stream_get_contents($stream));
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::isWritable
+ * @covers React\Stream\WritableResourceStream::close
+ */
+ public function testClose()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop);
+ $buffer->on('error', $this->expectCallableNever());
+ $buffer->on('close', $this->expectCallableOnce());
+
+ $this->assertTrue($buffer->isWritable());
+ $buffer->close();
+ $this->assertFalse($buffer->isWritable());
+
+ $this->assertEquals(array(), $buffer->listeners('close'));
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::close
+ */
+ public function testClosingAfterWriteRemovesStreamFromLoop()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $buffer = new WritableResourceStream($stream, $loop);
+
+ $loop->expects($this->once())->method('removeWriteStream')->with($stream);
+
+ $buffer->write('foo');
+ $buffer->close();
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::close
+ */
+ public function testClosingWithoutWritingDoesNotRemoveStreamFromLoop()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+ $buffer = new WritableResourceStream($stream, $loop);
+
+ $loop->expects($this->never())->method('removeWriteStream');
+
+ $buffer->close();
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::close
+ */
+ public function testDoubleCloseWillEmitOnlyOnce()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop);
+ $buffer->on('close', $this->expectCallableOnce());
+
+ $buffer->close();
+ $buffer->close();
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::write
+ * @covers React\Stream\WritableResourceStream::close
+ */
+ public function testWritingToClosedWritableResourceStreamShouldNotWriteToStream()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $filterBuffer = '';
+ $loop = $this->createLoopMock();
+
+ $buffer = new WritableResourceStream($stream, $loop);
+
+ Filter\append($stream, function ($chunk) use (&$filterBuffer) {
+ $filterBuffer .= $chunk;
+ return $chunk;
+ });
+
+ $buffer->close();
+
+ $buffer->write('foo');
+
+ $buffer->handleWrite();
+ $this->assertSame('', $filterBuffer);
+ }
+
+ /**
+ * @covers React\Stream\WritableResourceStream::handleWrite
+ */
+ public function testErrorWhenStreamResourceIsInvalid()
+ {
+ $stream = fopen('php://temp', 'r+');
+ $loop = $this->createWriteableLoopMock();
+
+ $error = null;
+
+ $buffer = new WritableResourceStream($stream, $loop);
+ $buffer->on('error', function ($message) use (&$error) {
+ $error = $message;
+ });
+
+ // invalidate stream resource
+ fclose($stream);
+
+ $buffer->write('Attempting to write to bad stream');
+
+ $this->assertInstanceOf('Exception', $error);
+
+ // the error messages differ between PHP versions, let's just check substrings
+ $this->assertContains('Unable to write to stream: ', $error->getMessage());
+ $this->assertContains(' not a valid stream resource', $error->getMessage(), '', true);
+ }
+
+ public function testWritingToClosedStream()
+ {
+ if ('Darwin' === PHP_OS) {
+ $this->markTestSkipped('OS X issue with shutting down pair for writing');
+ }
+
+ list($a, $b) = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
+ $loop = $this->createLoopMock();
+
+ $error = null;
+
+ $buffer = new WritableResourceStream($a, $loop);
+ $buffer->on('error', function($message) use (&$error) {
+ $error = $message;
+ });
+
+ $buffer->write('foo');
+ $buffer->handleWrite();
+ stream_socket_shutdown($b, STREAM_SHUT_RD);
+ stream_socket_shutdown($a, STREAM_SHUT_RD);
+ $buffer->write('bar');
+ $buffer->handleWrite();
+
+ $this->assertInstanceOf('Exception', $error);
+ $this->assertSame('Unable to write to stream: fwrite(): send of 3 bytes failed with errno=32 Broken pipe', $error->getMessage());
+ }
+
+ private function createWriteableLoopMock()
+ {
+ $loop = $this->createLoopMock();
+ $loop->preventWrites = false;
+ $loop
+ ->expects($this->any())
+ ->method('addWriteStream')
+ ->will($this->returnCallback(function ($stream, $listener) use ($loop) {
+ if (!$loop->preventWrites) {
+ call_user_func($listener, $stream);
+ }
+ }));
+
+ return $loop;
+ }
+
+ private function createLoopMock()
+ {
+ return $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/.gitignore b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/.gitignore
new file mode 100644
index 0000000..c49a5d8
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/.gitignore
@@ -0,0 +1,3 @@
+vendor/
+composer.lock
+phpunit.xml
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/AcceptHeader.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/AcceptHeader.php
new file mode 100644
index 0000000..d174026
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/AcceptHeader.php
@@ -0,0 +1,168 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * Represents an Accept-* header.
+ *
+ * An accept header is compound with a list of items,
+ * sorted by descending quality.
+ *
+ * @author Jean-François Simon
+ */
+class AcceptHeader
+{
+ /**
+ * @var AcceptHeaderItem[]
+ */
+ private $items = array();
+
+ /**
+ * @var bool
+ */
+ private $sorted = true;
+
+ /**
+ * @param AcceptHeaderItem[] $items
+ */
+ public function __construct(array $items)
+ {
+ foreach ($items as $item) {
+ $this->add($item);
+ }
+ }
+
+ /**
+ * Builds an AcceptHeader instance from a string.
+ *
+ * @param string $headerValue
+ *
+ * @return self
+ */
+ public static function fromString($headerValue)
+ {
+ $index = 0;
+
+ return new self(array_map(function ($itemValue) use (&$index) {
+ $item = AcceptHeaderItem::fromString($itemValue);
+ $item->setIndex($index++);
+
+ return $item;
+ }, preg_split('/\s*(?:,*("[^"]+"),*|,*(\'[^\']+\'),*|,+)\s*/', $headerValue, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE)));
+ }
+
+ /**
+ * Returns header value's string representation.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return implode(',', $this->items);
+ }
+
+ /**
+ * Tests if header has given value.
+ *
+ * @param string $value
+ *
+ * @return bool
+ */
+ public function has($value)
+ {
+ return isset($this->items[$value]);
+ }
+
+ /**
+ * Returns given value's item, if exists.
+ *
+ * @param string $value
+ *
+ * @return AcceptHeaderItem|null
+ */
+ public function get($value)
+ {
+ return isset($this->items[$value]) ? $this->items[$value] : null;
+ }
+
+ /**
+ * Adds an item.
+ *
+ * @return $this
+ */
+ public function add(AcceptHeaderItem $item)
+ {
+ $this->items[$item->getValue()] = $item;
+ $this->sorted = false;
+
+ return $this;
+ }
+
+ /**
+ * Returns all items.
+ *
+ * @return AcceptHeaderItem[]
+ */
+ public function all()
+ {
+ $this->sort();
+
+ return $this->items;
+ }
+
+ /**
+ * Filters items on their value using given regex.
+ *
+ * @param string $pattern
+ *
+ * @return self
+ */
+ public function filter($pattern)
+ {
+ return new self(array_filter($this->items, function (AcceptHeaderItem $item) use ($pattern) {
+ return preg_match($pattern, $item->getValue());
+ }));
+ }
+
+ /**
+ * Returns first item.
+ *
+ * @return AcceptHeaderItem|null
+ */
+ public function first()
+ {
+ $this->sort();
+
+ return !empty($this->items) ? reset($this->items) : null;
+ }
+
+ /**
+ * Sorts items by descending quality.
+ */
+ private function sort()
+ {
+ if (!$this->sorted) {
+ uasort($this->items, function (AcceptHeaderItem $a, AcceptHeaderItem $b) {
+ $qA = $a->getQuality();
+ $qB = $b->getQuality();
+
+ if ($qA === $qB) {
+ return $a->getIndex() > $b->getIndex() ? 1 : -1;
+ }
+
+ return $qA > $qB ? -1 : 1;
+ });
+
+ $this->sorted = true;
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/AcceptHeaderItem.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/AcceptHeaderItem.php
new file mode 100644
index 0000000..c69dbbb
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/AcceptHeaderItem.php
@@ -0,0 +1,209 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * Represents an Accept-* header item.
+ *
+ * @author Jean-François Simon
+ */
+class AcceptHeaderItem
+{
+ private $value;
+ private $quality = 1.0;
+ private $index = 0;
+ private $attributes = array();
+
+ /**
+ * @param string $value
+ * @param array $attributes
+ */
+ public function __construct($value, array $attributes = array())
+ {
+ $this->value = $value;
+ foreach ($attributes as $name => $value) {
+ $this->setAttribute($name, $value);
+ }
+ }
+
+ /**
+ * Builds an AcceptHeaderInstance instance from a string.
+ *
+ * @param string $itemValue
+ *
+ * @return self
+ */
+ public static function fromString($itemValue)
+ {
+ $bits = preg_split('/\s*(?:;*("[^"]+");*|;*(\'[^\']+\');*|;+)\s*/', $itemValue, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
+ $value = array_shift($bits);
+ $attributes = array();
+
+ $lastNullAttribute = null;
+ foreach ($bits as $bit) {
+ if (($start = substr($bit, 0, 1)) === ($end = substr($bit, -1)) && ('"' === $start || '\'' === $start)) {
+ $attributes[$lastNullAttribute] = substr($bit, 1, -1);
+ } elseif ('=' === $end) {
+ $lastNullAttribute = $bit = substr($bit, 0, -1);
+ $attributes[$bit] = null;
+ } else {
+ $parts = explode('=', $bit);
+ $attributes[$parts[0]] = isset($parts[1]) && strlen($parts[1]) > 0 ? $parts[1] : '';
+ }
+ }
+
+ return new self(($start = substr($value, 0, 1)) === ($end = substr($value, -1)) && ('"' === $start || '\'' === $start) ? substr($value, 1, -1) : $value, $attributes);
+ }
+
+ /**
+ * Returns header value's string representation.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ $string = $this->value.($this->quality < 1 ? ';q='.$this->quality : '');
+ if (count($this->attributes) > 0) {
+ $string .= ';'.implode(';', array_map(function ($name, $value) {
+ return sprintf(preg_match('/[,;=]/', $value) ? '%s="%s"' : '%s=%s', $name, $value);
+ }, array_keys($this->attributes), $this->attributes));
+ }
+
+ return $string;
+ }
+
+ /**
+ * Set the item value.
+ *
+ * @param string $value
+ *
+ * @return $this
+ */
+ public function setValue($value)
+ {
+ $this->value = $value;
+
+ return $this;
+ }
+
+ /**
+ * Returns the item value.
+ *
+ * @return string
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Set the item quality.
+ *
+ * @param float $quality
+ *
+ * @return $this
+ */
+ public function setQuality($quality)
+ {
+ $this->quality = $quality;
+
+ return $this;
+ }
+
+ /**
+ * Returns the item quality.
+ *
+ * @return float
+ */
+ public function getQuality()
+ {
+ return $this->quality;
+ }
+
+ /**
+ * Set the item index.
+ *
+ * @param int $index
+ *
+ * @return $this
+ */
+ public function setIndex($index)
+ {
+ $this->index = $index;
+
+ return $this;
+ }
+
+ /**
+ * Returns the item index.
+ *
+ * @return int
+ */
+ public function getIndex()
+ {
+ return $this->index;
+ }
+
+ /**
+ * Tests if an attribute exists.
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ public function hasAttribute($name)
+ {
+ return isset($this->attributes[$name]);
+ }
+
+ /**
+ * Returns an attribute by its name.
+ *
+ * @param string $name
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function getAttribute($name, $default = null)
+ {
+ return isset($this->attributes[$name]) ? $this->attributes[$name] : $default;
+ }
+
+ /**
+ * Returns all attributes.
+ *
+ * @return array
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * Set an attribute.
+ *
+ * @param string $name
+ * @param string $value
+ *
+ * @return $this
+ */
+ public function setAttribute($name, $value)
+ {
+ if ('q' === $name) {
+ $this->quality = (float) $value;
+ } else {
+ $this->attributes[$name] = (string) $value;
+ }
+
+ return $this;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/ApacheRequest.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/ApacheRequest.php
new file mode 100644
index 0000000..84803eb
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/ApacheRequest.php
@@ -0,0 +1,43 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * Request represents an HTTP request from an Apache server.
+ *
+ * @author Fabien Potencier
+ */
+class ApacheRequest extends Request
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function prepareRequestUri()
+ {
+ return $this->server->get('REQUEST_URI');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function prepareBaseUrl()
+ {
+ $baseUrl = $this->server->get('SCRIPT_NAME');
+
+ if (false === strpos($this->server->get('REQUEST_URI'), $baseUrl)) {
+ // assume mod_rewrite
+ return rtrim(dirname($baseUrl), '/\\');
+ }
+
+ return $baseUrl;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/BinaryFileResponse.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/BinaryFileResponse.php
new file mode 100644
index 0000000..1010223
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/BinaryFileResponse.php
@@ -0,0 +1,359 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+use Symfony\Component\HttpFoundation\File\File;
+use Symfony\Component\HttpFoundation\File\Exception\FileException;
+
+/**
+ * BinaryFileResponse represents an HTTP response delivering a file.
+ *
+ * @author Niklas Fiekas
+ * @author stealth35
+ * @author Igor Wiedler
+ * @author Jordan Alliot
+ * @author Sergey Linnik
+ */
+class BinaryFileResponse extends Response
+{
+ protected static $trustXSendfileTypeHeader = false;
+
+ /**
+ * @var File
+ */
+ protected $file;
+ protected $offset;
+ protected $maxlen;
+ protected $deleteFileAfterSend = false;
+
+ /**
+ * @param \SplFileInfo|string $file The file to stream
+ * @param int $status The response status code
+ * @param array $headers An array of response headers
+ * @param bool $public Files are public by default
+ * @param null|string $contentDisposition The type of Content-Disposition to set automatically with the filename
+ * @param bool $autoEtag Whether the ETag header should be automatically set
+ * @param bool $autoLastModified Whether the Last-Modified header should be automatically set
+ */
+ public function __construct($file, $status = 200, $headers = array(), $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
+ {
+ parent::__construct(null, $status, $headers);
+
+ $this->setFile($file, $contentDisposition, $autoEtag, $autoLastModified);
+
+ if ($public) {
+ $this->setPublic();
+ }
+ }
+
+ /**
+ * @param \SplFileInfo|string $file The file to stream
+ * @param int $status The response status code
+ * @param array $headers An array of response headers
+ * @param bool $public Files are public by default
+ * @param null|string $contentDisposition The type of Content-Disposition to set automatically with the filename
+ * @param bool $autoEtag Whether the ETag header should be automatically set
+ * @param bool $autoLastModified Whether the Last-Modified header should be automatically set
+ *
+ * @return static
+ */
+ public static function create($file = null, $status = 200, $headers = array(), $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
+ {
+ return new static($file, $status, $headers, $public, $contentDisposition, $autoEtag, $autoLastModified);
+ }
+
+ /**
+ * Sets the file to stream.
+ *
+ * @param \SplFileInfo|string $file The file to stream
+ * @param string $contentDisposition
+ * @param bool $autoEtag
+ * @param bool $autoLastModified
+ *
+ * @return $this
+ *
+ * @throws FileException
+ */
+ public function setFile($file, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
+ {
+ if (!$file instanceof File) {
+ if ($file instanceof \SplFileInfo) {
+ $file = new File($file->getPathname());
+ } else {
+ $file = new File((string) $file);
+ }
+ }
+
+ if (!$file->isReadable()) {
+ throw new FileException('File must be readable.');
+ }
+
+ $this->file = $file;
+
+ if ($autoEtag) {
+ $this->setAutoEtag();
+ }
+
+ if ($autoLastModified) {
+ $this->setAutoLastModified();
+ }
+
+ if ($contentDisposition) {
+ $this->setContentDisposition($contentDisposition);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Gets the file.
+ *
+ * @return File The file to stream
+ */
+ public function getFile()
+ {
+ return $this->file;
+ }
+
+ /**
+ * Automatically sets the Last-Modified header according the file modification date.
+ */
+ public function setAutoLastModified()
+ {
+ $this->setLastModified(\DateTime::createFromFormat('U', $this->file->getMTime()));
+
+ return $this;
+ }
+
+ /**
+ * Automatically sets the ETag header according to the checksum of the file.
+ */
+ public function setAutoEtag()
+ {
+ $this->setEtag(base64_encode(hash_file('sha256', $this->file->getPathname(), true)));
+
+ return $this;
+ }
+
+ /**
+ * Sets the Content-Disposition header with the given filename.
+ *
+ * @param string $disposition ResponseHeaderBag::DISPOSITION_INLINE or ResponseHeaderBag::DISPOSITION_ATTACHMENT
+ * @param string $filename Optionally use this UTF-8 encoded filename instead of the real name of the file
+ * @param string $filenameFallback A fallback filename, containing only ASCII characters. Defaults to an automatically encoded filename
+ *
+ * @return $this
+ */
+ public function setContentDisposition($disposition, $filename = '', $filenameFallback = '')
+ {
+ if ('' === $filename) {
+ $filename = $this->file->getFilename();
+ }
+
+ if ('' === $filenameFallback && (!preg_match('/^[\x20-\x7e]*$/', $filename) || false !== strpos($filename, '%'))) {
+ $encoding = mb_detect_encoding($filename, null, true) ?: '8bit';
+
+ for ($i = 0, $filenameLength = mb_strlen($filename, $encoding); $i < $filenameLength; ++$i) {
+ $char = mb_substr($filename, $i, 1, $encoding);
+
+ if ('%' === $char || ord($char) < 32 || ord($char) > 126) {
+ $filenameFallback .= '_';
+ } else {
+ $filenameFallback .= $char;
+ }
+ }
+ }
+
+ $dispositionHeader = $this->headers->makeDisposition($disposition, $filename, $filenameFallback);
+ $this->headers->set('Content-Disposition', $dispositionHeader);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function prepare(Request $request)
+ {
+ if (!$this->headers->has('Content-Type')) {
+ $this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream');
+ }
+
+ if ('HTTP/1.0' !== $request->server->get('SERVER_PROTOCOL')) {
+ $this->setProtocolVersion('1.1');
+ }
+
+ $this->ensureIEOverSSLCompatibility($request);
+
+ $this->offset = 0;
+ $this->maxlen = -1;
+
+ if (false === $fileSize = $this->file->getSize()) {
+ return $this;
+ }
+ $this->headers->set('Content-Length', $fileSize);
+
+ if (!$this->headers->has('Accept-Ranges')) {
+ // Only accept ranges on safe HTTP methods
+ $this->headers->set('Accept-Ranges', $request->isMethodSafe(false) ? 'bytes' : 'none');
+ }
+
+ if (self::$trustXSendfileTypeHeader && $request->headers->has('X-Sendfile-Type')) {
+ // Use X-Sendfile, do not send any content.
+ $type = $request->headers->get('X-Sendfile-Type');
+ $path = $this->file->getRealPath();
+ // Fall back to scheme://path for stream wrapped locations.
+ if (false === $path) {
+ $path = $this->file->getPathname();
+ }
+ if ('x-accel-redirect' === strtolower($type)) {
+ // Do X-Accel-Mapping substitutions.
+ // @link http://wiki.nginx.org/X-accel#X-Accel-Redirect
+ foreach (explode(',', $request->headers->get('X-Accel-Mapping', '')) as $mapping) {
+ $mapping = explode('=', $mapping, 2);
+
+ if (2 === count($mapping)) {
+ $pathPrefix = trim($mapping[0]);
+ $location = trim($mapping[1]);
+
+ if (substr($path, 0, strlen($pathPrefix)) === $pathPrefix) {
+ $path = $location.substr($path, strlen($pathPrefix));
+ break;
+ }
+ }
+ }
+ }
+ $this->headers->set($type, $path);
+ $this->maxlen = 0;
+ } elseif ($request->headers->has('Range')) {
+ // Process the range headers.
+ if (!$request->headers->has('If-Range') || $this->hasValidIfRangeHeader($request->headers->get('If-Range'))) {
+ $range = $request->headers->get('Range');
+
+ list($start, $end) = explode('-', substr($range, 6), 2) + array(0);
+
+ $end = ('' === $end) ? $fileSize - 1 : (int) $end;
+
+ if ('' === $start) {
+ $start = $fileSize - $end;
+ $end = $fileSize - 1;
+ } else {
+ $start = (int) $start;
+ }
+
+ if ($start <= $end) {
+ if ($start < 0 || $end > $fileSize - 1) {
+ $this->setStatusCode(416);
+ $this->headers->set('Content-Range', sprintf('bytes */%s', $fileSize));
+ } elseif (0 !== $start || $end !== $fileSize - 1) {
+ $this->maxlen = $end < $fileSize ? $end - $start + 1 : -1;
+ $this->offset = $start;
+
+ $this->setStatusCode(206);
+ $this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize));
+ $this->headers->set('Content-Length', $end - $start + 1);
+ }
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ private function hasValidIfRangeHeader($header)
+ {
+ if ($this->getEtag() === $header) {
+ return true;
+ }
+
+ if (null === $lastModified = $this->getLastModified()) {
+ return false;
+ }
+
+ return $lastModified->format('D, d M Y H:i:s').' GMT' === $header;
+ }
+
+ /**
+ * Sends the file.
+ *
+ * {@inheritdoc}
+ */
+ public function sendContent()
+ {
+ if (!$this->isSuccessful()) {
+ return parent::sendContent();
+ }
+
+ if (0 === $this->maxlen) {
+ return $this;
+ }
+
+ $out = fopen('php://output', 'wb');
+ $file = fopen($this->file->getPathname(), 'rb');
+
+ stream_copy_to_stream($file, $out, $this->maxlen, $this->offset);
+
+ fclose($out);
+ fclose($file);
+
+ if ($this->deleteFileAfterSend) {
+ unlink($this->file->getPathname());
+ }
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @throws \LogicException when the content is not null
+ */
+ public function setContent($content)
+ {
+ if (null !== $content) {
+ throw new \LogicException('The content cannot be set on a BinaryFileResponse instance.');
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return false
+ */
+ public function getContent()
+ {
+ return false;
+ }
+
+ /**
+ * Trust X-Sendfile-Type header.
+ */
+ public static function trustXSendfileTypeHeader()
+ {
+ self::$trustXSendfileTypeHeader = true;
+ }
+
+ /**
+ * If this is set to true, the file will be unlinked after the request is send
+ * Note: If the X-Sendfile header is used, the deleteFileAfterSend setting will not be used.
+ *
+ * @param bool $shouldDelete
+ *
+ * @return $this
+ */
+ public function deleteFileAfterSend($shouldDelete)
+ {
+ $this->deleteFileAfterSend = $shouldDelete;
+
+ return $this;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/CHANGELOG.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/CHANGELOG.md
new file mode 100644
index 0000000..ee5b6ce
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/CHANGELOG.md
@@ -0,0 +1,159 @@
+CHANGELOG
+=========
+
+3.4.0
+-----
+
+ * implemented PHP 7.0's `SessionUpdateTimestampHandlerInterface` with a new
+ `AbstractSessionHandler` base class and a new `StrictSessionHandler` wrapper
+ * deprecated the `WriteCheckSessionHandler`, `NativeSessionHandler` and `NativeProxy` classes
+ * deprecated setting session save handlers that do not implement `\SessionHandlerInterface` in `NativeSessionStorage::setSaveHandler()`
+ * deprecated using `MongoDbSessionHandler` with the legacy mongo extension; use it with the mongodb/mongodb package and ext-mongodb instead
+ * deprecated `MemcacheSessionHandler`; use `MemcachedSessionHandler` instead
+
+3.3.0
+-----
+
+ * the `Request::setTrustedProxies()` method takes a new `$trustedHeaderSet` argument,
+ see http://symfony.com/doc/current/components/http_foundation/trusting_proxies.html for more info,
+ * deprecated the `Request::setTrustedHeaderName()` and `Request::getTrustedHeaderName()` methods,
+ * added `File\Stream`, to be passed to `BinaryFileResponse` when the size of the served file is unknown,
+ disabling `Range` and `Content-Length` handling, switching to chunked encoding instead
+ * added the `Cookie::fromString()` method that allows to create a cookie from a
+ raw header string
+
+3.1.0
+-----
+
+ * Added support for creating `JsonResponse` with a string of JSON data
+
+3.0.0
+-----
+
+ * The precedence of parameters returned from `Request::get()` changed from "GET, PATH, BODY" to "PATH, GET, BODY"
+
+2.8.0
+-----
+
+ * Finding deep items in `ParameterBag::get()` is deprecated since version 2.8 and
+ will be removed in 3.0.
+
+2.6.0
+-----
+
+ * PdoSessionHandler changes
+ - implemented different session locking strategies to prevent loss of data by concurrent access to the same session
+ - [BC BREAK] save session data in a binary column without base64_encode
+ - [BC BREAK] added lifetime column to the session table which allows to have different lifetimes for each session
+ - implemented lazy connections that are only opened when a session is used by either passing a dsn string
+ explicitly or falling back to session.save_path ini setting
+ - added a createTable method that initializes a correctly defined table depending on the database vendor
+
+2.5.0
+-----
+
+ * added `JsonResponse::setEncodingOptions()` & `JsonResponse::getEncodingOptions()` for easier manipulation
+ of the options used while encoding data to JSON format.
+
+2.4.0
+-----
+
+ * added RequestStack
+ * added Request::getEncodings()
+ * added accessors methods to session handlers
+
+2.3.0
+-----
+
+ * added support for ranges of IPs in trusted proxies
+ * `UploadedFile::isValid` now returns false if the file was not uploaded via HTTP (in a non-test mode)
+ * Improved error-handling of `\Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler`
+ to ensure the supplied PDO handler throws Exceptions on error (as the class expects). Added related test cases
+ to verify that Exceptions are properly thrown when the PDO queries fail.
+
+2.2.0
+-----
+
+ * fixed the Request::create() precedence (URI information always take precedence now)
+ * added Request::getTrustedProxies()
+ * deprecated Request::isProxyTrusted()
+ * [BC BREAK] JsonResponse does not turn a top level empty array to an object anymore, use an ArrayObject to enforce objects
+ * added a IpUtils class to check if an IP belongs to a CIDR
+ * added Request::getRealMethod() to get the "real" HTTP method (getMethod() returns the "intended" HTTP method)
+ * disabled _method request parameter support by default (call Request::enableHttpMethodParameterOverride() to
+ enable it, and Request::getHttpMethodParameterOverride() to check if it is supported)
+ * Request::splitHttpAcceptHeader() method is deprecated and will be removed in 2.3
+ * Deprecated Flashbag::count() and \Countable interface, will be removed in 2.3
+
+2.1.0
+-----
+
+ * added Request::getSchemeAndHttpHost() and Request::getUserInfo()
+ * added a fluent interface to the Response class
+ * added Request::isProxyTrusted()
+ * added JsonResponse
+ * added a getTargetUrl method to RedirectResponse
+ * added support for streamed responses
+ * made Response::prepare() method the place to enforce HTTP specification
+ * [BC BREAK] moved management of the locale from the Session class to the Request class
+ * added a generic access to the PHP built-in filter mechanism: ParameterBag::filter()
+ * made FileBinaryMimeTypeGuesser command configurable
+ * added Request::getUser() and Request::getPassword()
+ * added support for the PATCH method in Request
+ * removed the ContentTypeMimeTypeGuesser class as it is deprecated and never used on PHP 5.3
+ * added ResponseHeaderBag::makeDisposition() (implements RFC 6266)
+ * made mimetype to extension conversion configurable
+ * [BC BREAK] Moved all session related classes and interfaces into own namespace, as
+ `Symfony\Component\HttpFoundation\Session` and renamed classes accordingly.
+ Session handlers are located in the subnamespace `Symfony\Component\HttpFoundation\Session\Handler`.
+ * SessionHandlers must implement `\SessionHandlerInterface` or extend from the
+ `Symfony\Component\HttpFoundation\Storage\Handler\NativeSessionHandler` base class.
+ * Added internal storage driver proxy mechanism for forward compatibility with
+ PHP 5.4 `\SessionHandler` class.
+ * Added session handlers for custom Memcache, Memcached and Null session save handlers.
+ * [BC BREAK] Removed `NativeSessionStorage` and replaced with `NativeFileSessionHandler`.
+ * [BC BREAK] `SessionStorageInterface` methods removed: `write()`, `read()` and
+ `remove()`. Added `getBag()`, `registerBag()`. The `NativeSessionStorage` class
+ is a mediator for the session storage internals including the session handlers
+ which do the real work of participating in the internal PHP session workflow.
+ * [BC BREAK] Introduced mock implementations of `SessionStorage` to enable unit
+ and functional testing without starting real PHP sessions. Removed
+ `ArraySessionStorage`, and replaced with `MockArraySessionStorage` for unit
+ tests; removed `FilesystemSessionStorage`, and replaced with`MockFileSessionStorage`
+ for functional tests. These do not interact with global session ini
+ configuration values, session functions or `$_SESSION` superglobal. This means
+ they can be configured directly allowing multiple instances to work without
+ conflicting in the same PHP process.
+ * [BC BREAK] Removed the `close()` method from the `Session` class, as this is
+ now redundant.
+ * Deprecated the following methods from the Session class: `setFlash()`, `setFlashes()`
+ `getFlash()`, `hasFlash()`, and `removeFlash()`. Use `getFlashBag()` instead
+ which returns a `FlashBagInterface`.
+ * `Session->clear()` now only clears session attributes as before it cleared
+ flash messages and attributes. `Session->getFlashBag()->all()` clears flashes now.
+ * Session data is now managed by `SessionBagInterface` to better encapsulate
+ session data.
+ * Refactored session attribute and flash messages system to their own
+ `SessionBagInterface` implementations.
+ * Added `FlashBag`. Flashes expire when retrieved by `get()` or `all()`. This
+ implementation is ESI compatible.
+ * Added `AutoExpireFlashBag` (default) to replicate Symfony 2.0.x auto expire
+ behaviour of messages auto expiring after one page page load. Messages must
+ be retrieved by `get()` or `all()`.
+ * Added `Symfony\Component\HttpFoundation\Attribute\AttributeBag` to replicate
+ attributes storage behaviour from 2.0.x (default).
+ * Added `Symfony\Component\HttpFoundation\Attribute\NamespacedAttributeBag` for
+ namespace session attributes.
+ * Flash API can stores messages in an array so there may be multiple messages
+ per flash type. The old `Session` class API remains without BC break as it
+ will allow single messages as before.
+ * Added basic session meta-data to the session to record session create time,
+ last updated time, and the lifetime of the session cookie that was provided
+ to the client.
+ * Request::getClientIp() method doesn't take a parameter anymore but bases
+ itself on the trustProxy parameter.
+ * Added isMethod() to Request object.
+ * [BC BREAK] The methods `getPathInfo()`, `getBaseUrl()` and `getBasePath()` of
+ a `Request` now all return a raw value (vs a urldecoded value before). Any call
+ to one of these methods must be checked and wrapped in a `rawurldecode()` if
+ needed.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Cookie.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Cookie.php
new file mode 100644
index 0000000..4519a6a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Cookie.php
@@ -0,0 +1,289 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * Represents a cookie.
+ *
+ * @author Johannes M. Schmitt
+ */
+class Cookie
+{
+ protected $name;
+ protected $value;
+ protected $domain;
+ protected $expire;
+ protected $path;
+ protected $secure;
+ protected $httpOnly;
+ private $raw;
+ private $sameSite;
+
+ const SAMESITE_LAX = 'lax';
+ const SAMESITE_STRICT = 'strict';
+
+ /**
+ * Creates cookie from raw header string.
+ *
+ * @param string $cookie
+ * @param bool $decode
+ *
+ * @return static
+ */
+ public static function fromString($cookie, $decode = false)
+ {
+ $data = array(
+ 'expires' => 0,
+ 'path' => '/',
+ 'domain' => null,
+ 'secure' => false,
+ 'httponly' => false,
+ 'raw' => !$decode,
+ 'samesite' => null,
+ );
+ foreach (explode(';', $cookie) as $part) {
+ if (false === strpos($part, '=')) {
+ $key = trim($part);
+ $value = true;
+ } else {
+ list($key, $value) = explode('=', trim($part), 2);
+ $key = trim($key);
+ $value = trim($value);
+ }
+ if (!isset($data['name'])) {
+ $data['name'] = $decode ? urldecode($key) : $key;
+ $data['value'] = true === $value ? null : ($decode ? urldecode($value) : $value);
+ continue;
+ }
+ switch ($key = strtolower($key)) {
+ case 'name':
+ case 'value':
+ break;
+ case 'max-age':
+ $data['expires'] = time() + (int) $value;
+ break;
+ default:
+ $data[$key] = $value;
+ break;
+ }
+ }
+
+ return new static($data['name'], $data['value'], $data['expires'], $data['path'], $data['domain'], $data['secure'], $data['httponly'], $data['raw'], $data['samesite']);
+ }
+
+ /**
+ * @param string $name The name of the cookie
+ * @param string|null $value The value of the cookie
+ * @param int|string|\DateTimeInterface $expire The time the cookie expires
+ * @param string $path The path on the server in which the cookie will be available on
+ * @param string|null $domain The domain that the cookie is available to
+ * @param bool $secure Whether the cookie should only be transmitted over a secure HTTPS connection from the client
+ * @param bool $httpOnly Whether the cookie will be made accessible only through the HTTP protocol
+ * @param bool $raw Whether the cookie value should be sent with no url encoding
+ * @param string|null $sameSite Whether the cookie will be available for cross-site requests
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function __construct($name, $value = null, $expire = 0, $path = '/', $domain = null, $secure = false, $httpOnly = true, $raw = false, $sameSite = null)
+ {
+ // from PHP source code
+ if (preg_match("/[=,; \t\r\n\013\014]/", $name)) {
+ throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name));
+ }
+
+ if (empty($name)) {
+ throw new \InvalidArgumentException('The cookie name cannot be empty.');
+ }
+
+ // convert expiration time to a Unix timestamp
+ if ($expire instanceof \DateTimeInterface) {
+ $expire = $expire->format('U');
+ } elseif (!is_numeric($expire)) {
+ $expire = strtotime($expire);
+
+ if (false === $expire) {
+ throw new \InvalidArgumentException('The cookie expiration time is not valid.');
+ }
+ }
+
+ $this->name = $name;
+ $this->value = $value;
+ $this->domain = $domain;
+ $this->expire = 0 < $expire ? (int) $expire : 0;
+ $this->path = empty($path) ? '/' : $path;
+ $this->secure = (bool) $secure;
+ $this->httpOnly = (bool) $httpOnly;
+ $this->raw = (bool) $raw;
+
+ if (null !== $sameSite) {
+ $sameSite = strtolower($sameSite);
+ }
+
+ if (!in_array($sameSite, array(self::SAMESITE_LAX, self::SAMESITE_STRICT, null), true)) {
+ throw new \InvalidArgumentException('The "sameSite" parameter value is not valid.');
+ }
+
+ $this->sameSite = $sameSite;
+ }
+
+ /**
+ * Returns the cookie as a string.
+ *
+ * @return string The cookie
+ */
+ public function __toString()
+ {
+ $str = ($this->isRaw() ? $this->getName() : urlencode($this->getName())).'=';
+
+ if ('' === (string) $this->getValue()) {
+ $str .= 'deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; max-age=-31536001';
+ } else {
+ $str .= $this->isRaw() ? $this->getValue() : rawurlencode($this->getValue());
+
+ if (0 !== $this->getExpiresTime()) {
+ $str .= '; expires='.gmdate('D, d-M-Y H:i:s T', $this->getExpiresTime()).'; max-age='.$this->getMaxAge();
+ }
+ }
+
+ if ($this->getPath()) {
+ $str .= '; path='.$this->getPath();
+ }
+
+ if ($this->getDomain()) {
+ $str .= '; domain='.$this->getDomain();
+ }
+
+ if (true === $this->isSecure()) {
+ $str .= '; secure';
+ }
+
+ if (true === $this->isHttpOnly()) {
+ $str .= '; httponly';
+ }
+
+ if (null !== $this->getSameSite()) {
+ $str .= '; samesite='.$this->getSameSite();
+ }
+
+ return $str;
+ }
+
+ /**
+ * Gets the name of the cookie.
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Gets the value of the cookie.
+ *
+ * @return string|null
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Gets the domain that the cookie is available to.
+ *
+ * @return string|null
+ */
+ public function getDomain()
+ {
+ return $this->domain;
+ }
+
+ /**
+ * Gets the time the cookie expires.
+ *
+ * @return int
+ */
+ public function getExpiresTime()
+ {
+ return $this->expire;
+ }
+
+ /**
+ * Gets the max-age attribute.
+ *
+ * @return int
+ */
+ public function getMaxAge()
+ {
+ return 0 !== $this->expire ? $this->expire - time() : 0;
+ }
+
+ /**
+ * Gets the path on the server in which the cookie will be available on.
+ *
+ * @return string
+ */
+ public function getPath()
+ {
+ return $this->path;
+ }
+
+ /**
+ * Checks whether the cookie should only be transmitted over a secure HTTPS connection from the client.
+ *
+ * @return bool
+ */
+ public function isSecure()
+ {
+ return $this->secure;
+ }
+
+ /**
+ * Checks whether the cookie will be made accessible only through the HTTP protocol.
+ *
+ * @return bool
+ */
+ public function isHttpOnly()
+ {
+ return $this->httpOnly;
+ }
+
+ /**
+ * Whether this cookie is about to be cleared.
+ *
+ * @return bool
+ */
+ public function isCleared()
+ {
+ return $this->expire < time();
+ }
+
+ /**
+ * Checks if the cookie value should be sent with no url encoding.
+ *
+ * @return bool
+ */
+ public function isRaw()
+ {
+ return $this->raw;
+ }
+
+ /**
+ * Gets the SameSite attribute.
+ *
+ * @return string|null
+ */
+ public function getSameSite()
+ {
+ return $this->sameSite;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Exception/ConflictingHeadersException.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Exception/ConflictingHeadersException.php
new file mode 100644
index 0000000..5fcf5b4
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Exception/ConflictingHeadersException.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Exception;
+
+/**
+ * The HTTP request contains headers with conflicting information.
+ *
+ * @author Magnus Nordlander
+ */
+class ConflictingHeadersException extends \UnexpectedValueException implements RequestExceptionInterface
+{
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Exception/RequestExceptionInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Exception/RequestExceptionInterface.php
new file mode 100644
index 0000000..478d0dc
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Exception/RequestExceptionInterface.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Exception;
+
+/**
+ * Interface for Request exceptions.
+ *
+ * Exceptions implementing this interface should trigger an HTTP 400 response in the application code.
+ */
+interface RequestExceptionInterface
+{
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Exception/SuspiciousOperationException.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Exception/SuspiciousOperationException.php
new file mode 100644
index 0000000..ae7a5f1
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Exception/SuspiciousOperationException.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Exception;
+
+/**
+ * Raised when a user has performed an operation that should be considered
+ * suspicious from a security perspective.
+ */
+class SuspiciousOperationException extends \UnexpectedValueException implements RequestExceptionInterface
+{
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/ExpressionRequestMatcher.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/ExpressionRequestMatcher.php
new file mode 100644
index 0000000..e9c8441
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/ExpressionRequestMatcher.php
@@ -0,0 +1,47 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
+
+/**
+ * ExpressionRequestMatcher uses an expression to match a Request.
+ *
+ * @author Fabien Potencier
+ */
+class ExpressionRequestMatcher extends RequestMatcher
+{
+ private $language;
+ private $expression;
+
+ public function setExpression(ExpressionLanguage $language, $expression)
+ {
+ $this->language = $language;
+ $this->expression = $expression;
+ }
+
+ public function matches(Request $request)
+ {
+ if (!$this->language) {
+ throw new \LogicException('Unable to match the request as the expression language is not available.');
+ }
+
+ return $this->language->evaluate($this->expression, array(
+ 'request' => $request,
+ 'method' => $request->getMethod(),
+ 'path' => rawurldecode($request->getPathInfo()),
+ 'host' => $request->getHost(),
+ 'ip' => $request->getClientIp(),
+ 'attributes' => $request->attributes->all(),
+ )) && parent::matches($request);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php
new file mode 100644
index 0000000..3b8e41d
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\Exception;
+
+/**
+ * Thrown when the access on a file was denied.
+ *
+ * @author Bernhard Schussek
+ */
+class AccessDeniedException extends FileException
+{
+ /**
+ * @param string $path The path to the accessed file
+ */
+ public function __construct($path)
+ {
+ parent::__construct(sprintf('The file %s could not be accessed', $path));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/Exception/FileException.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/Exception/FileException.php
new file mode 100644
index 0000000..fad5133
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/Exception/FileException.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\Exception;
+
+/**
+ * Thrown when an error occurred in the component File.
+ *
+ * @author Bernhard Schussek
+ */
+class FileException extends \RuntimeException
+{
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php
new file mode 100644
index 0000000..bfcc37e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\Exception;
+
+/**
+ * Thrown when a file was not found.
+ *
+ * @author Bernhard Schussek
+ */
+class FileNotFoundException extends FileException
+{
+ /**
+ * @param string $path The path to the file that was not found
+ */
+ public function __construct($path)
+ {
+ parent::__construct(sprintf('The file "%s" does not exist', $path));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php
new file mode 100644
index 0000000..0444b87
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\Exception;
+
+class UnexpectedTypeException extends FileException
+{
+ public function __construct($value, $expectedType)
+ {
+ parent::__construct(sprintf('Expected argument of type %s, %s given', $expectedType, is_object($value) ? get_class($value) : gettype($value)));
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/Exception/UploadException.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/Exception/UploadException.php
new file mode 100644
index 0000000..7074e76
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/Exception/UploadException.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\Exception;
+
+/**
+ * Thrown when an error occurred during file upload.
+ *
+ * @author Bernhard Schussek
+ */
+class UploadException extends FileException
+{
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/File.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/File.php
new file mode 100644
index 0000000..e2a6768
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/File.php
@@ -0,0 +1,136 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File;
+
+use Symfony\Component\HttpFoundation\File\Exception\FileException;
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
+use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser;
+
+/**
+ * A file in the file system.
+ *
+ * @author Bernhard Schussek
+ */
+class File extends \SplFileInfo
+{
+ /**
+ * Constructs a new file from the given path.
+ *
+ * @param string $path The path to the file
+ * @param bool $checkPath Whether to check the path or not
+ *
+ * @throws FileNotFoundException If the given path is not a file
+ */
+ public function __construct($path, $checkPath = true)
+ {
+ if ($checkPath && !is_file($path)) {
+ throw new FileNotFoundException($path);
+ }
+
+ parent::__construct($path);
+ }
+
+ /**
+ * Returns the extension based on the mime type.
+ *
+ * If the mime type is unknown, returns null.
+ *
+ * This method uses the mime type as guessed by getMimeType()
+ * to guess the file extension.
+ *
+ * @return string|null The guessed extension or null if it cannot be guessed
+ *
+ * @see ExtensionGuesser
+ * @see getMimeType()
+ */
+ public function guessExtension()
+ {
+ $type = $this->getMimeType();
+ $guesser = ExtensionGuesser::getInstance();
+
+ return $guesser->guess($type);
+ }
+
+ /**
+ * Returns the mime type of the file.
+ *
+ * The mime type is guessed using a MimeTypeGuesser instance, which uses finfo(),
+ * mime_content_type() and the system binary "file" (in this order), depending on
+ * which of those are available.
+ *
+ * @return string|null The guessed mime type (e.g. "application/pdf")
+ *
+ * @see MimeTypeGuesser
+ */
+ public function getMimeType()
+ {
+ $guesser = MimeTypeGuesser::getInstance();
+
+ return $guesser->guess($this->getPathname());
+ }
+
+ /**
+ * Moves the file to a new location.
+ *
+ * @param string $directory The destination folder
+ * @param string $name The new file name
+ *
+ * @return self A File object representing the new file
+ *
+ * @throws FileException if the target file could not be created
+ */
+ public function move($directory, $name = null)
+ {
+ $target = $this->getTargetFile($directory, $name);
+
+ if (!@rename($this->getPathname(), $target)) {
+ $error = error_get_last();
+ throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error['message'])));
+ }
+
+ @chmod($target, 0666 & ~umask());
+
+ return $target;
+ }
+
+ protected function getTargetFile($directory, $name = null)
+ {
+ if (!is_dir($directory)) {
+ if (false === @mkdir($directory, 0777, true) && !is_dir($directory)) {
+ throw new FileException(sprintf('Unable to create the "%s" directory', $directory));
+ }
+ } elseif (!is_writable($directory)) {
+ throw new FileException(sprintf('Unable to write in the "%s" directory', $directory));
+ }
+
+ $target = rtrim($directory, '/\\').DIRECTORY_SEPARATOR.(null === $name ? $this->getBasename() : $this->getName($name));
+
+ return new self($target, false);
+ }
+
+ /**
+ * Returns locale independent base name of the given path.
+ *
+ * @param string $name The new file name
+ *
+ * @return string containing
+ */
+ protected function getName($name)
+ {
+ $originalName = str_replace('\\', '/', $name);
+ $pos = strrpos($originalName, '/');
+ $originalName = false === $pos ? $originalName : substr($originalName, $pos + 1);
+
+ return $originalName;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php
new file mode 100644
index 0000000..263fb32
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php
@@ -0,0 +1,94 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\MimeType;
+
+/**
+ * A singleton mime type to file extension guesser.
+ *
+ * A default guesser is provided.
+ * You can register custom guessers by calling the register()
+ * method on the singleton instance:
+ *
+ * $guesser = ExtensionGuesser::getInstance();
+ * $guesser->register(new MyCustomExtensionGuesser());
+ *
+ * The last registered guesser is preferred over previously registered ones.
+ */
+class ExtensionGuesser implements ExtensionGuesserInterface
+{
+ /**
+ * The singleton instance.
+ *
+ * @var ExtensionGuesser
+ */
+ private static $instance = null;
+
+ /**
+ * All registered ExtensionGuesserInterface instances.
+ *
+ * @var array
+ */
+ protected $guessers = array();
+
+ /**
+ * Returns the singleton instance.
+ *
+ * @return self
+ */
+ public static function getInstance()
+ {
+ if (null === self::$instance) {
+ self::$instance = new self();
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Registers all natively provided extension guessers.
+ */
+ private function __construct()
+ {
+ $this->register(new MimeTypeExtensionGuesser());
+ }
+
+ /**
+ * Registers a new extension guesser.
+ *
+ * When guessing, this guesser is preferred over previously registered ones.
+ */
+ public function register(ExtensionGuesserInterface $guesser)
+ {
+ array_unshift($this->guessers, $guesser);
+ }
+
+ /**
+ * Tries to guess the extension.
+ *
+ * The mime type is passed to each registered mime type guesser in reverse order
+ * of their registration (last registered is queried first). Once a guesser
+ * returns a value that is not NULL, this method terminates and returns the
+ * value.
+ *
+ * @param string $mimeType The mime type
+ *
+ * @return string The guessed extension or NULL, if none could be guessed
+ */
+ public function guess($mimeType)
+ {
+ foreach ($this->guessers as $guesser) {
+ if (null !== $extension = $guesser->guess($mimeType)) {
+ return $extension;
+ }
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php
new file mode 100644
index 0000000..d19a0e5
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\MimeType;
+
+/**
+ * Guesses the file extension corresponding to a given mime type.
+ */
+interface ExtensionGuesserInterface
+{
+ /**
+ * Makes a best guess for a file extension, given a mime type.
+ *
+ * @param string $mimeType The mime type
+ *
+ * @return string The guessed extension or NULL, if none could be guessed
+ */
+ public function guess($mimeType);
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php
new file mode 100644
index 0000000..c2ac676
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php
@@ -0,0 +1,85 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\MimeType;
+
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
+
+/**
+ * Guesses the mime type with the binary "file" (only available on *nix).
+ *
+ * @author Bernhard Schussek
+ */
+class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
+{
+ private $cmd;
+
+ /**
+ * The $cmd pattern must contain a "%s" string that will be replaced
+ * with the file name to guess.
+ *
+ * The command output must start with the mime type of the file.
+ *
+ * @param string $cmd The command to run to get the mime type of a file
+ */
+ public function __construct($cmd = 'file -b --mime %s 2>/dev/null')
+ {
+ $this->cmd = $cmd;
+ }
+
+ /**
+ * Returns whether this guesser is supported on the current OS.
+ *
+ * @return bool
+ */
+ public static function isSupported()
+ {
+ return '\\' !== DIRECTORY_SEPARATOR && function_exists('passthru') && function_exists('escapeshellarg');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function guess($path)
+ {
+ if (!is_file($path)) {
+ throw new FileNotFoundException($path);
+ }
+
+ if (!is_readable($path)) {
+ throw new AccessDeniedException($path);
+ }
+
+ if (!self::isSupported()) {
+ return;
+ }
+
+ ob_start();
+
+ // need to use --mime instead of -i. see #6641
+ passthru(sprintf($this->cmd, escapeshellarg($path)), $return);
+ if ($return > 0) {
+ ob_end_clean();
+
+ return;
+ }
+
+ $type = trim(ob_get_clean());
+
+ if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\.]+)#i', $type, $match)) {
+ // it's not a type, but an error message
+ return;
+ }
+
+ return $match[1];
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php
new file mode 100644
index 0000000..9b42835
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php
@@ -0,0 +1,69 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\MimeType;
+
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
+
+/**
+ * Guesses the mime type using the PECL extension FileInfo.
+ *
+ * @author Bernhard Schussek
+ */
+class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface
+{
+ private $magicFile;
+
+ /**
+ * @param string $magicFile A magic file to use with the finfo instance
+ *
+ * @see http://www.php.net/manual/en/function.finfo-open.php
+ */
+ public function __construct($magicFile = null)
+ {
+ $this->magicFile = $magicFile;
+ }
+
+ /**
+ * Returns whether this guesser is supported on the current OS/PHP setup.
+ *
+ * @return bool
+ */
+ public static function isSupported()
+ {
+ return function_exists('finfo_open');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function guess($path)
+ {
+ if (!is_file($path)) {
+ throw new FileNotFoundException($path);
+ }
+
+ if (!is_readable($path)) {
+ throw new AccessDeniedException($path);
+ }
+
+ if (!self::isSupported()) {
+ return;
+ }
+
+ if (!$finfo = new \finfo(FILEINFO_MIME_TYPE, $this->magicFile)) {
+ return;
+ }
+
+ return $finfo->file($path);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php
new file mode 100644
index 0000000..77bf51b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php
@@ -0,0 +1,808 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\MimeType;
+
+/**
+ * Provides a best-guess mapping of mime type to file extension.
+ */
+class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
+{
+ /**
+ * A map of mime types and their default extensions.
+ *
+ * This list has been placed under the public domain by the Apache HTTPD project.
+ * This list has been updated from upstream on 2013-04-23.
+ *
+ * @see http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
+ */
+ protected $defaultExtensions = array(
+ 'application/andrew-inset' => 'ez',
+ 'application/applixware' => 'aw',
+ 'application/atom+xml' => 'atom',
+ 'application/atomcat+xml' => 'atomcat',
+ 'application/atomsvc+xml' => 'atomsvc',
+ 'application/ccxml+xml' => 'ccxml',
+ 'application/cdmi-capability' => 'cdmia',
+ 'application/cdmi-container' => 'cdmic',
+ 'application/cdmi-domain' => 'cdmid',
+ 'application/cdmi-object' => 'cdmio',
+ 'application/cdmi-queue' => 'cdmiq',
+ 'application/cu-seeme' => 'cu',
+ 'application/davmount+xml' => 'davmount',
+ 'application/docbook+xml' => 'dbk',
+ 'application/dssc+der' => 'dssc',
+ 'application/dssc+xml' => 'xdssc',
+ 'application/ecmascript' => 'ecma',
+ 'application/emma+xml' => 'emma',
+ 'application/epub+zip' => 'epub',
+ 'application/exi' => 'exi',
+ 'application/font-tdpfr' => 'pfr',
+ 'application/gml+xml' => 'gml',
+ 'application/gpx+xml' => 'gpx',
+ 'application/gxf' => 'gxf',
+ 'application/hyperstudio' => 'stk',
+ 'application/inkml+xml' => 'ink',
+ 'application/ipfix' => 'ipfix',
+ 'application/java-archive' => 'jar',
+ 'application/java-serialized-object' => 'ser',
+ 'application/java-vm' => 'class',
+ 'application/javascript' => 'js',
+ 'application/json' => 'json',
+ 'application/jsonml+json' => 'jsonml',
+ 'application/lost+xml' => 'lostxml',
+ 'application/mac-binhex40' => 'hqx',
+ 'application/mac-compactpro' => 'cpt',
+ 'application/mads+xml' => 'mads',
+ 'application/marc' => 'mrc',
+ 'application/marcxml+xml' => 'mrcx',
+ 'application/mathematica' => 'ma',
+ 'application/mathml+xml' => 'mathml',
+ 'application/mbox' => 'mbox',
+ 'application/mediaservercontrol+xml' => 'mscml',
+ 'application/metalink+xml' => 'metalink',
+ 'application/metalink4+xml' => 'meta4',
+ 'application/mets+xml' => 'mets',
+ 'application/mods+xml' => 'mods',
+ 'application/mp21' => 'm21',
+ 'application/mp4' => 'mp4s',
+ 'application/msword' => 'doc',
+ 'application/mxf' => 'mxf',
+ 'application/octet-stream' => 'bin',
+ 'application/oda' => 'oda',
+ 'application/oebps-package+xml' => 'opf',
+ 'application/ogg' => 'ogx',
+ 'application/omdoc+xml' => 'omdoc',
+ 'application/onenote' => 'onetoc',
+ 'application/oxps' => 'oxps',
+ 'application/patch-ops-error+xml' => 'xer',
+ 'application/pdf' => 'pdf',
+ 'application/pgp-encrypted' => 'pgp',
+ 'application/pgp-signature' => 'asc',
+ 'application/pics-rules' => 'prf',
+ 'application/pkcs10' => 'p10',
+ 'application/pkcs7-mime' => 'p7m',
+ 'application/pkcs7-signature' => 'p7s',
+ 'application/pkcs8' => 'p8',
+ 'application/pkix-attr-cert' => 'ac',
+ 'application/pkix-cert' => 'cer',
+ 'application/pkix-crl' => 'crl',
+ 'application/pkix-pkipath' => 'pkipath',
+ 'application/pkixcmp' => 'pki',
+ 'application/pls+xml' => 'pls',
+ 'application/postscript' => 'ai',
+ 'application/prs.cww' => 'cww',
+ 'application/pskc+xml' => 'pskcxml',
+ 'application/rdf+xml' => 'rdf',
+ 'application/reginfo+xml' => 'rif',
+ 'application/relax-ng-compact-syntax' => 'rnc',
+ 'application/resource-lists+xml' => 'rl',
+ 'application/resource-lists-diff+xml' => 'rld',
+ 'application/rls-services+xml' => 'rs',
+ 'application/rpki-ghostbusters' => 'gbr',
+ 'application/rpki-manifest' => 'mft',
+ 'application/rpki-roa' => 'roa',
+ 'application/rsd+xml' => 'rsd',
+ 'application/rss+xml' => 'rss',
+ 'application/rtf' => 'rtf',
+ 'application/sbml+xml' => 'sbml',
+ 'application/scvp-cv-request' => 'scq',
+ 'application/scvp-cv-response' => 'scs',
+ 'application/scvp-vp-request' => 'spq',
+ 'application/scvp-vp-response' => 'spp',
+ 'application/sdp' => 'sdp',
+ 'application/set-payment-initiation' => 'setpay',
+ 'application/set-registration-initiation' => 'setreg',
+ 'application/shf+xml' => 'shf',
+ 'application/smil+xml' => 'smi',
+ 'application/sparql-query' => 'rq',
+ 'application/sparql-results+xml' => 'srx',
+ 'application/srgs' => 'gram',
+ 'application/srgs+xml' => 'grxml',
+ 'application/sru+xml' => 'sru',
+ 'application/ssdl+xml' => 'ssdl',
+ 'application/ssml+xml' => 'ssml',
+ 'application/tei+xml' => 'tei',
+ 'application/thraud+xml' => 'tfi',
+ 'application/timestamped-data' => 'tsd',
+ 'application/vnd.3gpp.pic-bw-large' => 'plb',
+ 'application/vnd.3gpp.pic-bw-small' => 'psb',
+ 'application/vnd.3gpp.pic-bw-var' => 'pvb',
+ 'application/vnd.3gpp2.tcap' => 'tcap',
+ 'application/vnd.3m.post-it-notes' => 'pwn',
+ 'application/vnd.accpac.simply.aso' => 'aso',
+ 'application/vnd.accpac.simply.imp' => 'imp',
+ 'application/vnd.acucobol' => 'acu',
+ 'application/vnd.acucorp' => 'atc',
+ 'application/vnd.adobe.air-application-installer-package+zip' => 'air',
+ 'application/vnd.adobe.formscentral.fcdt' => 'fcdt',
+ 'application/vnd.adobe.fxp' => 'fxp',
+ 'application/vnd.adobe.xdp+xml' => 'xdp',
+ 'application/vnd.adobe.xfdf' => 'xfdf',
+ 'application/vnd.ahead.space' => 'ahead',
+ 'application/vnd.airzip.filesecure.azf' => 'azf',
+ 'application/vnd.airzip.filesecure.azs' => 'azs',
+ 'application/vnd.amazon.ebook' => 'azw',
+ 'application/vnd.americandynamics.acc' => 'acc',
+ 'application/vnd.amiga.ami' => 'ami',
+ 'application/vnd.android.package-archive' => 'apk',
+ 'application/vnd.anser-web-certificate-issue-initiation' => 'cii',
+ 'application/vnd.anser-web-funds-transfer-initiation' => 'fti',
+ 'application/vnd.antix.game-component' => 'atx',
+ 'application/vnd.apple.installer+xml' => 'mpkg',
+ 'application/vnd.apple.mpegurl' => 'm3u8',
+ 'application/vnd.aristanetworks.swi' => 'swi',
+ 'application/vnd.astraea-software.iota' => 'iota',
+ 'application/vnd.audiograph' => 'aep',
+ 'application/vnd.blueice.multipass' => 'mpm',
+ 'application/vnd.bmi' => 'bmi',
+ 'application/vnd.businessobjects' => 'rep',
+ 'application/vnd.chemdraw+xml' => 'cdxml',
+ 'application/vnd.chipnuts.karaoke-mmd' => 'mmd',
+ 'application/vnd.cinderella' => 'cdy',
+ 'application/vnd.claymore' => 'cla',
+ 'application/vnd.cloanto.rp9' => 'rp9',
+ 'application/vnd.clonk.c4group' => 'c4g',
+ 'application/vnd.cluetrust.cartomobile-config' => 'c11amc',
+ 'application/vnd.cluetrust.cartomobile-config-pkg' => 'c11amz',
+ 'application/vnd.commonspace' => 'csp',
+ 'application/vnd.contact.cmsg' => 'cdbcmsg',
+ 'application/vnd.cosmocaller' => 'cmc',
+ 'application/vnd.crick.clicker' => 'clkx',
+ 'application/vnd.crick.clicker.keyboard' => 'clkk',
+ 'application/vnd.crick.clicker.palette' => 'clkp',
+ 'application/vnd.crick.clicker.template' => 'clkt',
+ 'application/vnd.crick.clicker.wordbank' => 'clkw',
+ 'application/vnd.criticaltools.wbs+xml' => 'wbs',
+ 'application/vnd.ctc-posml' => 'pml',
+ 'application/vnd.cups-ppd' => 'ppd',
+ 'application/vnd.curl.car' => 'car',
+ 'application/vnd.curl.pcurl' => 'pcurl',
+ 'application/vnd.dart' => 'dart',
+ 'application/vnd.data-vision.rdz' => 'rdz',
+ 'application/vnd.dece.data' => 'uvf',
+ 'application/vnd.dece.ttml+xml' => 'uvt',
+ 'application/vnd.dece.unspecified' => 'uvx',
+ 'application/vnd.dece.zip' => 'uvz',
+ 'application/vnd.denovo.fcselayout-link' => 'fe_launch',
+ 'application/vnd.dna' => 'dna',
+ 'application/vnd.dolby.mlp' => 'mlp',
+ 'application/vnd.dpgraph' => 'dpg',
+ 'application/vnd.dreamfactory' => 'dfac',
+ 'application/vnd.ds-keypoint' => 'kpxx',
+ 'application/vnd.dvb.ait' => 'ait',
+ 'application/vnd.dvb.service' => 'svc',
+ 'application/vnd.dynageo' => 'geo',
+ 'application/vnd.ecowin.chart' => 'mag',
+ 'application/vnd.enliven' => 'nml',
+ 'application/vnd.epson.esf' => 'esf',
+ 'application/vnd.epson.msf' => 'msf',
+ 'application/vnd.epson.quickanime' => 'qam',
+ 'application/vnd.epson.salt' => 'slt',
+ 'application/vnd.epson.ssf' => 'ssf',
+ 'application/vnd.eszigno3+xml' => 'es3',
+ 'application/vnd.ezpix-album' => 'ez2',
+ 'application/vnd.ezpix-package' => 'ez3',
+ 'application/vnd.fdf' => 'fdf',
+ 'application/vnd.fdsn.mseed' => 'mseed',
+ 'application/vnd.fdsn.seed' => 'seed',
+ 'application/vnd.flographit' => 'gph',
+ 'application/vnd.fluxtime.clip' => 'ftc',
+ 'application/vnd.framemaker' => 'fm',
+ 'application/vnd.frogans.fnc' => 'fnc',
+ 'application/vnd.frogans.ltf' => 'ltf',
+ 'application/vnd.fsc.weblaunch' => 'fsc',
+ 'application/vnd.fujitsu.oasys' => 'oas',
+ 'application/vnd.fujitsu.oasys2' => 'oa2',
+ 'application/vnd.fujitsu.oasys3' => 'oa3',
+ 'application/vnd.fujitsu.oasysgp' => 'fg5',
+ 'application/vnd.fujitsu.oasysprs' => 'bh2',
+ 'application/vnd.fujixerox.ddd' => 'ddd',
+ 'application/vnd.fujixerox.docuworks' => 'xdw',
+ 'application/vnd.fujixerox.docuworks.binder' => 'xbd',
+ 'application/vnd.fuzzysheet' => 'fzs',
+ 'application/vnd.genomatix.tuxedo' => 'txd',
+ 'application/vnd.geogebra.file' => 'ggb',
+ 'application/vnd.geogebra.tool' => 'ggt',
+ 'application/vnd.geometry-explorer' => 'gex',
+ 'application/vnd.geonext' => 'gxt',
+ 'application/vnd.geoplan' => 'g2w',
+ 'application/vnd.geospace' => 'g3w',
+ 'application/vnd.gmx' => 'gmx',
+ 'application/vnd.google-earth.kml+xml' => 'kml',
+ 'application/vnd.google-earth.kmz' => 'kmz',
+ 'application/vnd.grafeq' => 'gqf',
+ 'application/vnd.groove-account' => 'gac',
+ 'application/vnd.groove-help' => 'ghf',
+ 'application/vnd.groove-identity-message' => 'gim',
+ 'application/vnd.groove-injector' => 'grv',
+ 'application/vnd.groove-tool-message' => 'gtm',
+ 'application/vnd.groove-tool-template' => 'tpl',
+ 'application/vnd.groove-vcard' => 'vcg',
+ 'application/vnd.hal+xml' => 'hal',
+ 'application/vnd.handheld-entertainment+xml' => 'zmm',
+ 'application/vnd.hbci' => 'hbci',
+ 'application/vnd.hhe.lesson-player' => 'les',
+ 'application/vnd.hp-hpgl' => 'hpgl',
+ 'application/vnd.hp-hpid' => 'hpid',
+ 'application/vnd.hp-hps' => 'hps',
+ 'application/vnd.hp-jlyt' => 'jlt',
+ 'application/vnd.hp-pcl' => 'pcl',
+ 'application/vnd.hp-pclxl' => 'pclxl',
+ 'application/vnd.hydrostatix.sof-data' => 'sfd-hdstx',
+ 'application/vnd.ibm.minipay' => 'mpy',
+ 'application/vnd.ibm.modcap' => 'afp',
+ 'application/vnd.ibm.rights-management' => 'irm',
+ 'application/vnd.ibm.secure-container' => 'sc',
+ 'application/vnd.iccprofile' => 'icc',
+ 'application/vnd.igloader' => 'igl',
+ 'application/vnd.immervision-ivp' => 'ivp',
+ 'application/vnd.immervision-ivu' => 'ivu',
+ 'application/vnd.insors.igm' => 'igm',
+ 'application/vnd.intercon.formnet' => 'xpw',
+ 'application/vnd.intergeo' => 'i2g',
+ 'application/vnd.intu.qbo' => 'qbo',
+ 'application/vnd.intu.qfx' => 'qfx',
+ 'application/vnd.ipunplugged.rcprofile' => 'rcprofile',
+ 'application/vnd.irepository.package+xml' => 'irp',
+ 'application/vnd.is-xpr' => 'xpr',
+ 'application/vnd.isac.fcs' => 'fcs',
+ 'application/vnd.jam' => 'jam',
+ 'application/vnd.jcp.javame.midlet-rms' => 'rms',
+ 'application/vnd.jisp' => 'jisp',
+ 'application/vnd.joost.joda-archive' => 'joda',
+ 'application/vnd.kahootz' => 'ktz',
+ 'application/vnd.kde.karbon' => 'karbon',
+ 'application/vnd.kde.kchart' => 'chrt',
+ 'application/vnd.kde.kformula' => 'kfo',
+ 'application/vnd.kde.kivio' => 'flw',
+ 'application/vnd.kde.kontour' => 'kon',
+ 'application/vnd.kde.kpresenter' => 'kpr',
+ 'application/vnd.kde.kspread' => 'ksp',
+ 'application/vnd.kde.kword' => 'kwd',
+ 'application/vnd.kenameaapp' => 'htke',
+ 'application/vnd.kidspiration' => 'kia',
+ 'application/vnd.kinar' => 'kne',
+ 'application/vnd.koan' => 'skp',
+ 'application/vnd.kodak-descriptor' => 'sse',
+ 'application/vnd.las.las+xml' => 'lasxml',
+ 'application/vnd.llamagraphics.life-balance.desktop' => 'lbd',
+ 'application/vnd.llamagraphics.life-balance.exchange+xml' => 'lbe',
+ 'application/vnd.lotus-1-2-3' => '123',
+ 'application/vnd.lotus-approach' => 'apr',
+ 'application/vnd.lotus-freelance' => 'pre',
+ 'application/vnd.lotus-notes' => 'nsf',
+ 'application/vnd.lotus-organizer' => 'org',
+ 'application/vnd.lotus-screencam' => 'scm',
+ 'application/vnd.lotus-wordpro' => 'lwp',
+ 'application/vnd.macports.portpkg' => 'portpkg',
+ 'application/vnd.mcd' => 'mcd',
+ 'application/vnd.medcalcdata' => 'mc1',
+ 'application/vnd.mediastation.cdkey' => 'cdkey',
+ 'application/vnd.mfer' => 'mwf',
+ 'application/vnd.mfmp' => 'mfm',
+ 'application/vnd.micrografx.flo' => 'flo',
+ 'application/vnd.micrografx.igx' => 'igx',
+ 'application/vnd.mif' => 'mif',
+ 'application/vnd.mobius.daf' => 'daf',
+ 'application/vnd.mobius.dis' => 'dis',
+ 'application/vnd.mobius.mbk' => 'mbk',
+ 'application/vnd.mobius.mqy' => 'mqy',
+ 'application/vnd.mobius.msl' => 'msl',
+ 'application/vnd.mobius.plc' => 'plc',
+ 'application/vnd.mobius.txf' => 'txf',
+ 'application/vnd.mophun.application' => 'mpn',
+ 'application/vnd.mophun.certificate' => 'mpc',
+ 'application/vnd.mozilla.xul+xml' => 'xul',
+ 'application/vnd.ms-artgalry' => 'cil',
+ 'application/vnd.ms-cab-compressed' => 'cab',
+ 'application/vnd.ms-excel' => 'xls',
+ 'application/vnd.ms-excel.addin.macroenabled.12' => 'xlam',
+ 'application/vnd.ms-excel.sheet.binary.macroenabled.12' => 'xlsb',
+ 'application/vnd.ms-excel.sheet.macroenabled.12' => 'xlsm',
+ 'application/vnd.ms-excel.template.macroenabled.12' => 'xltm',
+ 'application/vnd.ms-fontobject' => 'eot',
+ 'application/vnd.ms-htmlhelp' => 'chm',
+ 'application/vnd.ms-ims' => 'ims',
+ 'application/vnd.ms-lrm' => 'lrm',
+ 'application/vnd.ms-officetheme' => 'thmx',
+ 'application/vnd.ms-pki.seccat' => 'cat',
+ 'application/vnd.ms-pki.stl' => 'stl',
+ 'application/vnd.ms-powerpoint' => 'ppt',
+ 'application/vnd.ms-powerpoint.addin.macroenabled.12' => 'ppam',
+ 'application/vnd.ms-powerpoint.presentation.macroenabled.12' => 'pptm',
+ 'application/vnd.ms-powerpoint.slide.macroenabled.12' => 'sldm',
+ 'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => 'ppsm',
+ 'application/vnd.ms-powerpoint.template.macroenabled.12' => 'potm',
+ 'application/vnd.ms-project' => 'mpp',
+ 'application/vnd.ms-word.document.macroenabled.12' => 'docm',
+ 'application/vnd.ms-word.template.macroenabled.12' => 'dotm',
+ 'application/vnd.ms-works' => 'wps',
+ 'application/vnd.ms-wpl' => 'wpl',
+ 'application/vnd.ms-xpsdocument' => 'xps',
+ 'application/vnd.mseq' => 'mseq',
+ 'application/vnd.musician' => 'mus',
+ 'application/vnd.muvee.style' => 'msty',
+ 'application/vnd.mynfc' => 'taglet',
+ 'application/vnd.neurolanguage.nlu' => 'nlu',
+ 'application/vnd.nitf' => 'ntf',
+ 'application/vnd.noblenet-directory' => 'nnd',
+ 'application/vnd.noblenet-sealer' => 'nns',
+ 'application/vnd.noblenet-web' => 'nnw',
+ 'application/vnd.nokia.n-gage.data' => 'ngdat',
+ 'application/vnd.nokia.n-gage.symbian.install' => 'n-gage',
+ 'application/vnd.nokia.radio-preset' => 'rpst',
+ 'application/vnd.nokia.radio-presets' => 'rpss',
+ 'application/vnd.novadigm.edm' => 'edm',
+ 'application/vnd.novadigm.edx' => 'edx',
+ 'application/vnd.novadigm.ext' => 'ext',
+ 'application/vnd.oasis.opendocument.chart' => 'odc',
+ 'application/vnd.oasis.opendocument.chart-template' => 'otc',
+ 'application/vnd.oasis.opendocument.database' => 'odb',
+ 'application/vnd.oasis.opendocument.formula' => 'odf',
+ 'application/vnd.oasis.opendocument.formula-template' => 'odft',
+ 'application/vnd.oasis.opendocument.graphics' => 'odg',
+ 'application/vnd.oasis.opendocument.graphics-template' => 'otg',
+ 'application/vnd.oasis.opendocument.image' => 'odi',
+ 'application/vnd.oasis.opendocument.image-template' => 'oti',
+ 'application/vnd.oasis.opendocument.presentation' => 'odp',
+ 'application/vnd.oasis.opendocument.presentation-template' => 'otp',
+ 'application/vnd.oasis.opendocument.spreadsheet' => 'ods',
+ 'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots',
+ 'application/vnd.oasis.opendocument.text' => 'odt',
+ 'application/vnd.oasis.opendocument.text-master' => 'odm',
+ 'application/vnd.oasis.opendocument.text-template' => 'ott',
+ 'application/vnd.oasis.opendocument.text-web' => 'oth',
+ 'application/vnd.olpc-sugar' => 'xo',
+ 'application/vnd.oma.dd2+xml' => 'dd2',
+ 'application/vnd.openofficeorg.extension' => 'oxt',
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx',
+ 'application/vnd.openxmlformats-officedocument.presentationml.slide' => 'sldx',
+ 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'ppsx',
+ 'application/vnd.openxmlformats-officedocument.presentationml.template' => 'potx',
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'xltx',
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx',
+ 'application/vnd.osgeo.mapguide.package' => 'mgp',
+ 'application/vnd.osgi.dp' => 'dp',
+ 'application/vnd.osgi.subsystem' => 'esa',
+ 'application/vnd.palm' => 'pdb',
+ 'application/vnd.pawaafile' => 'paw',
+ 'application/vnd.pg.format' => 'str',
+ 'application/vnd.pg.osasli' => 'ei6',
+ 'application/vnd.picsel' => 'efif',
+ 'application/vnd.pmi.widget' => 'wg',
+ 'application/vnd.pocketlearn' => 'plf',
+ 'application/vnd.powerbuilder6' => 'pbd',
+ 'application/vnd.previewsystems.box' => 'box',
+ 'application/vnd.proteus.magazine' => 'mgz',
+ 'application/vnd.publishare-delta-tree' => 'qps',
+ 'application/vnd.pvi.ptid1' => 'ptid',
+ 'application/vnd.quark.quarkxpress' => 'qxd',
+ 'application/vnd.realvnc.bed' => 'bed',
+ 'application/vnd.recordare.musicxml' => 'mxl',
+ 'application/vnd.recordare.musicxml+xml' => 'musicxml',
+ 'application/vnd.rig.cryptonote' => 'cryptonote',
+ 'application/vnd.rim.cod' => 'cod',
+ 'application/vnd.rn-realmedia' => 'rm',
+ 'application/vnd.rn-realmedia-vbr' => 'rmvb',
+ 'application/vnd.route66.link66+xml' => 'link66',
+ 'application/vnd.sailingtracker.track' => 'st',
+ 'application/vnd.seemail' => 'see',
+ 'application/vnd.sema' => 'sema',
+ 'application/vnd.semd' => 'semd',
+ 'application/vnd.semf' => 'semf',
+ 'application/vnd.shana.informed.formdata' => 'ifm',
+ 'application/vnd.shana.informed.formtemplate' => 'itp',
+ 'application/vnd.shana.informed.interchange' => 'iif',
+ 'application/vnd.shana.informed.package' => 'ipk',
+ 'application/vnd.simtech-mindmapper' => 'twd',
+ 'application/vnd.smaf' => 'mmf',
+ 'application/vnd.smart.teacher' => 'teacher',
+ 'application/vnd.solent.sdkm+xml' => 'sdkm',
+ 'application/vnd.spotfire.dxp' => 'dxp',
+ 'application/vnd.spotfire.sfs' => 'sfs',
+ 'application/vnd.stardivision.calc' => 'sdc',
+ 'application/vnd.stardivision.draw' => 'sda',
+ 'application/vnd.stardivision.impress' => 'sdd',
+ 'application/vnd.stardivision.math' => 'smf',
+ 'application/vnd.stardivision.writer' => 'sdw',
+ 'application/vnd.stardivision.writer-global' => 'sgl',
+ 'application/vnd.stepmania.package' => 'smzip',
+ 'application/vnd.stepmania.stepchart' => 'sm',
+ 'application/vnd.sun.xml.calc' => 'sxc',
+ 'application/vnd.sun.xml.calc.template' => 'stc',
+ 'application/vnd.sun.xml.draw' => 'sxd',
+ 'application/vnd.sun.xml.draw.template' => 'std',
+ 'application/vnd.sun.xml.impress' => 'sxi',
+ 'application/vnd.sun.xml.impress.template' => 'sti',
+ 'application/vnd.sun.xml.math' => 'sxm',
+ 'application/vnd.sun.xml.writer' => 'sxw',
+ 'application/vnd.sun.xml.writer.global' => 'sxg',
+ 'application/vnd.sun.xml.writer.template' => 'stw',
+ 'application/vnd.sus-calendar' => 'sus',
+ 'application/vnd.svd' => 'svd',
+ 'application/vnd.symbian.install' => 'sis',
+ 'application/vnd.syncml+xml' => 'xsm',
+ 'application/vnd.syncml.dm+wbxml' => 'bdm',
+ 'application/vnd.syncml.dm+xml' => 'xdm',
+ 'application/vnd.tao.intent-module-archive' => 'tao',
+ 'application/vnd.tcpdump.pcap' => 'pcap',
+ 'application/vnd.tmobile-livetv' => 'tmo',
+ 'application/vnd.trid.tpt' => 'tpt',
+ 'application/vnd.triscape.mxs' => 'mxs',
+ 'application/vnd.trueapp' => 'tra',
+ 'application/vnd.ufdl' => 'ufd',
+ 'application/vnd.uiq.theme' => 'utz',
+ 'application/vnd.umajin' => 'umj',
+ 'application/vnd.unity' => 'unityweb',
+ 'application/vnd.uoml+xml' => 'uoml',
+ 'application/vnd.vcx' => 'vcx',
+ 'application/vnd.visio' => 'vsd',
+ 'application/vnd.visionary' => 'vis',
+ 'application/vnd.vsf' => 'vsf',
+ 'application/vnd.wap.wbxml' => 'wbxml',
+ 'application/vnd.wap.wmlc' => 'wmlc',
+ 'application/vnd.wap.wmlscriptc' => 'wmlsc',
+ 'application/vnd.webturbo' => 'wtb',
+ 'application/vnd.wolfram.player' => 'nbp',
+ 'application/vnd.wordperfect' => 'wpd',
+ 'application/vnd.wqd' => 'wqd',
+ 'application/vnd.wt.stf' => 'stf',
+ 'application/vnd.xara' => 'xar',
+ 'application/vnd.xfdl' => 'xfdl',
+ 'application/vnd.yamaha.hv-dic' => 'hvd',
+ 'application/vnd.yamaha.hv-script' => 'hvs',
+ 'application/vnd.yamaha.hv-voice' => 'hvp',
+ 'application/vnd.yamaha.openscoreformat' => 'osf',
+ 'application/vnd.yamaha.openscoreformat.osfpvg+xml' => 'osfpvg',
+ 'application/vnd.yamaha.smaf-audio' => 'saf',
+ 'application/vnd.yamaha.smaf-phrase' => 'spf',
+ 'application/vnd.yellowriver-custom-menu' => 'cmp',
+ 'application/vnd.zul' => 'zir',
+ 'application/vnd.zzazz.deck+xml' => 'zaz',
+ 'application/voicexml+xml' => 'vxml',
+ 'application/widget' => 'wgt',
+ 'application/winhlp' => 'hlp',
+ 'application/wsdl+xml' => 'wsdl',
+ 'application/wspolicy+xml' => 'wspolicy',
+ 'application/x-7z-compressed' => '7z',
+ 'application/x-abiword' => 'abw',
+ 'application/x-ace-compressed' => 'ace',
+ 'application/x-apple-diskimage' => 'dmg',
+ 'application/x-authorware-bin' => 'aab',
+ 'application/x-authorware-map' => 'aam',
+ 'application/x-authorware-seg' => 'aas',
+ 'application/x-bcpio' => 'bcpio',
+ 'application/x-bittorrent' => 'torrent',
+ 'application/x-blorb' => 'blb',
+ 'application/x-bzip' => 'bz',
+ 'application/x-bzip2' => 'bz2',
+ 'application/x-cbr' => 'cbr',
+ 'application/x-cdlink' => 'vcd',
+ 'application/x-cfs-compressed' => 'cfs',
+ 'application/x-chat' => 'chat',
+ 'application/x-chess-pgn' => 'pgn',
+ 'application/x-conference' => 'nsc',
+ 'application/x-cpio' => 'cpio',
+ 'application/x-csh' => 'csh',
+ 'application/x-debian-package' => 'deb',
+ 'application/x-dgc-compressed' => 'dgc',
+ 'application/x-director' => 'dir',
+ 'application/x-doom' => 'wad',
+ 'application/x-dtbncx+xml' => 'ncx',
+ 'application/x-dtbook+xml' => 'dtb',
+ 'application/x-dtbresource+xml' => 'res',
+ 'application/x-dvi' => 'dvi',
+ 'application/x-envoy' => 'evy',
+ 'application/x-eva' => 'eva',
+ 'application/x-font-bdf' => 'bdf',
+ 'application/x-font-ghostscript' => 'gsf',
+ 'application/x-font-linux-psf' => 'psf',
+ 'application/x-font-otf' => 'otf',
+ 'application/x-font-pcf' => 'pcf',
+ 'application/x-font-snf' => 'snf',
+ 'application/x-font-ttf' => 'ttf',
+ 'application/x-font-type1' => 'pfa',
+ 'application/x-font-woff' => 'woff',
+ 'application/x-freearc' => 'arc',
+ 'application/x-futuresplash' => 'spl',
+ 'application/x-gca-compressed' => 'gca',
+ 'application/x-glulx' => 'ulx',
+ 'application/x-gnumeric' => 'gnumeric',
+ 'application/x-gramps-xml' => 'gramps',
+ 'application/x-gtar' => 'gtar',
+ 'application/x-hdf' => 'hdf',
+ 'application/x-install-instructions' => 'install',
+ 'application/x-iso9660-image' => 'iso',
+ 'application/x-java-jnlp-file' => 'jnlp',
+ 'application/x-latex' => 'latex',
+ 'application/x-lzh-compressed' => 'lzh',
+ 'application/x-mie' => 'mie',
+ 'application/x-mobipocket-ebook' => 'prc',
+ 'application/x-ms-application' => 'application',
+ 'application/x-ms-shortcut' => 'lnk',
+ 'application/x-ms-wmd' => 'wmd',
+ 'application/x-ms-wmz' => 'wmz',
+ 'application/x-ms-xbap' => 'xbap',
+ 'application/x-msaccess' => 'mdb',
+ 'application/x-msbinder' => 'obd',
+ 'application/x-mscardfile' => 'crd',
+ 'application/x-msclip' => 'clp',
+ 'application/x-msdownload' => 'exe',
+ 'application/x-msmediaview' => 'mvb',
+ 'application/x-msmetafile' => 'wmf',
+ 'application/x-msmoney' => 'mny',
+ 'application/x-mspublisher' => 'pub',
+ 'application/x-msschedule' => 'scd',
+ 'application/x-msterminal' => 'trm',
+ 'application/x-mswrite' => 'wri',
+ 'application/x-netcdf' => 'nc',
+ 'application/x-nzb' => 'nzb',
+ 'application/x-pkcs12' => 'p12',
+ 'application/x-pkcs7-certificates' => 'p7b',
+ 'application/x-pkcs7-certreqresp' => 'p7r',
+ 'application/x-rar-compressed' => 'rar',
+ 'application/x-rar' => 'rar',
+ 'application/x-research-info-systems' => 'ris',
+ 'application/x-sh' => 'sh',
+ 'application/x-shar' => 'shar',
+ 'application/x-shockwave-flash' => 'swf',
+ 'application/x-silverlight-app' => 'xap',
+ 'application/x-sql' => 'sql',
+ 'application/x-stuffit' => 'sit',
+ 'application/x-stuffitx' => 'sitx',
+ 'application/x-subrip' => 'srt',
+ 'application/x-sv4cpio' => 'sv4cpio',
+ 'application/x-sv4crc' => 'sv4crc',
+ 'application/x-t3vm-image' => 't3',
+ 'application/x-tads' => 'gam',
+ 'application/x-tar' => 'tar',
+ 'application/x-tcl' => 'tcl',
+ 'application/x-tex' => 'tex',
+ 'application/x-tex-tfm' => 'tfm',
+ 'application/x-texinfo' => 'texinfo',
+ 'application/x-tgif' => 'obj',
+ 'application/x-ustar' => 'ustar',
+ 'application/x-wais-source' => 'src',
+ 'application/x-x509-ca-cert' => 'der',
+ 'application/x-xfig' => 'fig',
+ 'application/x-xliff+xml' => 'xlf',
+ 'application/x-xpinstall' => 'xpi',
+ 'application/x-xz' => 'xz',
+ 'application/x-zip-compressed' => 'zip',
+ 'application/x-zmachine' => 'z1',
+ 'application/xaml+xml' => 'xaml',
+ 'application/xcap-diff+xml' => 'xdf',
+ 'application/xenc+xml' => 'xenc',
+ 'application/xhtml+xml' => 'xhtml',
+ 'application/xml' => 'xml',
+ 'application/xml-dtd' => 'dtd',
+ 'application/xop+xml' => 'xop',
+ 'application/xproc+xml' => 'xpl',
+ 'application/xslt+xml' => 'xslt',
+ 'application/xspf+xml' => 'xspf',
+ 'application/xv+xml' => 'mxml',
+ 'application/yang' => 'yang',
+ 'application/yin+xml' => 'yin',
+ 'application/zip' => 'zip',
+ 'audio/adpcm' => 'adp',
+ 'audio/basic' => 'au',
+ 'audio/midi' => 'mid',
+ 'audio/mp4' => 'mp4a',
+ 'audio/mpeg' => 'mpga',
+ 'audio/ogg' => 'oga',
+ 'audio/s3m' => 's3m',
+ 'audio/silk' => 'sil',
+ 'audio/vnd.dece.audio' => 'uva',
+ 'audio/vnd.digital-winds' => 'eol',
+ 'audio/vnd.dra' => 'dra',
+ 'audio/vnd.dts' => 'dts',
+ 'audio/vnd.dts.hd' => 'dtshd',
+ 'audio/vnd.lucent.voice' => 'lvp',
+ 'audio/vnd.ms-playready.media.pya' => 'pya',
+ 'audio/vnd.nuera.ecelp4800' => 'ecelp4800',
+ 'audio/vnd.nuera.ecelp7470' => 'ecelp7470',
+ 'audio/vnd.nuera.ecelp9600' => 'ecelp9600',
+ 'audio/vnd.rip' => 'rip',
+ 'audio/webm' => 'weba',
+ 'audio/x-aac' => 'aac',
+ 'audio/x-aiff' => 'aif',
+ 'audio/x-caf' => 'caf',
+ 'audio/x-flac' => 'flac',
+ 'audio/x-matroska' => 'mka',
+ 'audio/x-mpegurl' => 'm3u',
+ 'audio/x-ms-wax' => 'wax',
+ 'audio/x-ms-wma' => 'wma',
+ 'audio/x-pn-realaudio' => 'ram',
+ 'audio/x-pn-realaudio-plugin' => 'rmp',
+ 'audio/x-wav' => 'wav',
+ 'audio/xm' => 'xm',
+ 'chemical/x-cdx' => 'cdx',
+ 'chemical/x-cif' => 'cif',
+ 'chemical/x-cmdf' => 'cmdf',
+ 'chemical/x-cml' => 'cml',
+ 'chemical/x-csml' => 'csml',
+ 'chemical/x-xyz' => 'xyz',
+ 'image/bmp' => 'bmp',
+ 'image/x-ms-bmp' => 'bmp',
+ 'image/cgm' => 'cgm',
+ 'image/g3fax' => 'g3',
+ 'image/gif' => 'gif',
+ 'image/ief' => 'ief',
+ 'image/jpeg' => 'jpeg',
+ 'image/pjpeg' => 'jpeg',
+ 'image/ktx' => 'ktx',
+ 'image/png' => 'png',
+ 'image/prs.btif' => 'btif',
+ 'image/sgi' => 'sgi',
+ 'image/svg+xml' => 'svg',
+ 'image/tiff' => 'tiff',
+ 'image/vnd.adobe.photoshop' => 'psd',
+ 'image/vnd.dece.graphic' => 'uvi',
+ 'image/vnd.dvb.subtitle' => 'sub',
+ 'image/vnd.djvu' => 'djvu',
+ 'image/vnd.dwg' => 'dwg',
+ 'image/vnd.dxf' => 'dxf',
+ 'image/vnd.fastbidsheet' => 'fbs',
+ 'image/vnd.fpx' => 'fpx',
+ 'image/vnd.fst' => 'fst',
+ 'image/vnd.fujixerox.edmics-mmr' => 'mmr',
+ 'image/vnd.fujixerox.edmics-rlc' => 'rlc',
+ 'image/vnd.ms-modi' => 'mdi',
+ 'image/vnd.ms-photo' => 'wdp',
+ 'image/vnd.net-fpx' => 'npx',
+ 'image/vnd.wap.wbmp' => 'wbmp',
+ 'image/vnd.xiff' => 'xif',
+ 'image/webp' => 'webp',
+ 'image/x-3ds' => '3ds',
+ 'image/x-cmu-raster' => 'ras',
+ 'image/x-cmx' => 'cmx',
+ 'image/x-freehand' => 'fh',
+ 'image/x-icon' => 'ico',
+ 'image/x-mrsid-image' => 'sid',
+ 'image/x-pcx' => 'pcx',
+ 'image/x-pict' => 'pic',
+ 'image/x-portable-anymap' => 'pnm',
+ 'image/x-portable-bitmap' => 'pbm',
+ 'image/x-portable-graymap' => 'pgm',
+ 'image/x-portable-pixmap' => 'ppm',
+ 'image/x-rgb' => 'rgb',
+ 'image/x-tga' => 'tga',
+ 'image/x-xbitmap' => 'xbm',
+ 'image/x-xpixmap' => 'xpm',
+ 'image/x-xwindowdump' => 'xwd',
+ 'message/rfc822' => 'eml',
+ 'model/iges' => 'igs',
+ 'model/mesh' => 'msh',
+ 'model/vnd.collada+xml' => 'dae',
+ 'model/vnd.dwf' => 'dwf',
+ 'model/vnd.gdl' => 'gdl',
+ 'model/vnd.gtw' => 'gtw',
+ 'model/vnd.mts' => 'mts',
+ 'model/vnd.vtu' => 'vtu',
+ 'model/vrml' => 'wrl',
+ 'model/x3d+binary' => 'x3db',
+ 'model/x3d+vrml' => 'x3dv',
+ 'model/x3d+xml' => 'x3d',
+ 'text/cache-manifest' => 'appcache',
+ 'text/calendar' => 'ics',
+ 'text/css' => 'css',
+ 'text/csv' => 'csv',
+ 'text/html' => 'html',
+ 'text/n3' => 'n3',
+ 'text/plain' => 'txt',
+ 'text/prs.lines.tag' => 'dsc',
+ 'text/richtext' => 'rtx',
+ 'text/rtf' => 'rtf',
+ 'text/sgml' => 'sgml',
+ 'text/tab-separated-values' => 'tsv',
+ 'text/troff' => 't',
+ 'text/turtle' => 'ttl',
+ 'text/uri-list' => 'uri',
+ 'text/vcard' => 'vcard',
+ 'text/vnd.curl' => 'curl',
+ 'text/vnd.curl.dcurl' => 'dcurl',
+ 'text/vnd.curl.scurl' => 'scurl',
+ 'text/vnd.curl.mcurl' => 'mcurl',
+ 'text/vnd.dvb.subtitle' => 'sub',
+ 'text/vnd.fly' => 'fly',
+ 'text/vnd.fmi.flexstor' => 'flx',
+ 'text/vnd.graphviz' => 'gv',
+ 'text/vnd.in3d.3dml' => '3dml',
+ 'text/vnd.in3d.spot' => 'spot',
+ 'text/vnd.sun.j2me.app-descriptor' => 'jad',
+ 'text/vnd.wap.wml' => 'wml',
+ 'text/vnd.wap.wmlscript' => 'wmls',
+ 'text/vtt' => 'vtt',
+ 'text/x-asm' => 's',
+ 'text/x-c' => 'c',
+ 'text/x-fortran' => 'f',
+ 'text/x-pascal' => 'p',
+ 'text/x-java-source' => 'java',
+ 'text/x-opml' => 'opml',
+ 'text/x-nfo' => 'nfo',
+ 'text/x-setext' => 'etx',
+ 'text/x-sfv' => 'sfv',
+ 'text/x-uuencode' => 'uu',
+ 'text/x-vcalendar' => 'vcs',
+ 'text/x-vcard' => 'vcf',
+ 'video/3gpp' => '3gp',
+ 'video/3gpp2' => '3g2',
+ 'video/h261' => 'h261',
+ 'video/h263' => 'h263',
+ 'video/h264' => 'h264',
+ 'video/jpeg' => 'jpgv',
+ 'video/jpm' => 'jpm',
+ 'video/mj2' => 'mj2',
+ 'video/mp4' => 'mp4',
+ 'video/mpeg' => 'mpeg',
+ 'video/ogg' => 'ogv',
+ 'video/quicktime' => 'qt',
+ 'video/vnd.dece.hd' => 'uvh',
+ 'video/vnd.dece.mobile' => 'uvm',
+ 'video/vnd.dece.pd' => 'uvp',
+ 'video/vnd.dece.sd' => 'uvs',
+ 'video/vnd.dece.video' => 'uvv',
+ 'video/vnd.dvb.file' => 'dvb',
+ 'video/vnd.fvt' => 'fvt',
+ 'video/vnd.mpegurl' => 'mxu',
+ 'video/vnd.ms-playready.media.pyv' => 'pyv',
+ 'video/vnd.uvvu.mp4' => 'uvu',
+ 'video/vnd.vivo' => 'viv',
+ 'video/webm' => 'webm',
+ 'video/x-f4v' => 'f4v',
+ 'video/x-fli' => 'fli',
+ 'video/x-flv' => 'flv',
+ 'video/x-m4v' => 'm4v',
+ 'video/x-matroska' => 'mkv',
+ 'video/x-mng' => 'mng',
+ 'video/x-ms-asf' => 'asf',
+ 'video/x-ms-vob' => 'vob',
+ 'video/x-ms-wm' => 'wm',
+ 'video/x-ms-wmv' => 'wmv',
+ 'video/x-ms-wmx' => 'wmx',
+ 'video/x-ms-wvx' => 'wvx',
+ 'video/x-msvideo' => 'avi',
+ 'video/x-sgi-movie' => 'movie',
+ 'video/x-smv' => 'smv',
+ 'x-conference/x-cooltalk' => 'ice',
+ );
+
+ /**
+ * {@inheritdoc}
+ */
+ public function guess($mimeType)
+ {
+ return isset($this->defaultExtensions[$mimeType]) ? $this->defaultExtensions[$mimeType] : null;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php
new file mode 100644
index 0000000..e3ef45e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php
@@ -0,0 +1,142 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\MimeType;
+
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
+
+/**
+ * A singleton mime type guesser.
+ *
+ * By default, all mime type guessers provided by the framework are installed
+ * (if available on the current OS/PHP setup).
+ *
+ * You can register custom guessers by calling the register() method on the
+ * singleton instance. Custom guessers are always called before any default ones.
+ *
+ * $guesser = MimeTypeGuesser::getInstance();
+ * $guesser->register(new MyCustomMimeTypeGuesser());
+ *
+ * If you want to change the order of the default guessers, just re-register your
+ * preferred one as a custom one. The last registered guesser is preferred over
+ * previously registered ones.
+ *
+ * Re-registering a built-in guesser also allows you to configure it:
+ *
+ * $guesser = MimeTypeGuesser::getInstance();
+ * $guesser->register(new FileinfoMimeTypeGuesser('/path/to/magic/file'));
+ *
+ * @author Bernhard Schussek
+ */
+class MimeTypeGuesser implements MimeTypeGuesserInterface
+{
+ /**
+ * The singleton instance.
+ *
+ * @var MimeTypeGuesser
+ */
+ private static $instance = null;
+
+ /**
+ * All registered MimeTypeGuesserInterface instances.
+ *
+ * @var array
+ */
+ protected $guessers = array();
+
+ /**
+ * Returns the singleton instance.
+ *
+ * @return self
+ */
+ public static function getInstance()
+ {
+ if (null === self::$instance) {
+ self::$instance = new self();
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Resets the singleton instance.
+ */
+ public static function reset()
+ {
+ self::$instance = null;
+ }
+
+ /**
+ * Registers all natively provided mime type guessers.
+ */
+ private function __construct()
+ {
+ if (FileBinaryMimeTypeGuesser::isSupported()) {
+ $this->register(new FileBinaryMimeTypeGuesser());
+ }
+
+ if (FileinfoMimeTypeGuesser::isSupported()) {
+ $this->register(new FileinfoMimeTypeGuesser());
+ }
+ }
+
+ /**
+ * Registers a new mime type guesser.
+ *
+ * When guessing, this guesser is preferred over previously registered ones.
+ */
+ public function register(MimeTypeGuesserInterface $guesser)
+ {
+ array_unshift($this->guessers, $guesser);
+ }
+
+ /**
+ * Tries to guess the mime type of the given file.
+ *
+ * The file is passed to each registered mime type guesser in reverse order
+ * of their registration (last registered is queried first). Once a guesser
+ * returns a value that is not NULL, this method terminates and returns the
+ * value.
+ *
+ * @param string $path The path to the file
+ *
+ * @return string The mime type or NULL, if none could be guessed
+ *
+ * @throws \LogicException
+ * @throws FileNotFoundException
+ * @throws AccessDeniedException
+ */
+ public function guess($path)
+ {
+ if (!is_file($path)) {
+ throw new FileNotFoundException($path);
+ }
+
+ if (!is_readable($path)) {
+ throw new AccessDeniedException($path);
+ }
+
+ if (!$this->guessers) {
+ $msg = 'Unable to guess the mime type as no guessers are available';
+ if (!FileinfoMimeTypeGuesser::isSupported()) {
+ $msg .= ' (Did you enable the php_fileinfo extension?)';
+ }
+ throw new \LogicException($msg);
+ }
+
+ foreach ($this->guessers as $guesser) {
+ if (null !== $mimeType = $guesser->guess($path)) {
+ return $mimeType;
+ }
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php
new file mode 100644
index 0000000..f8c3ad2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php
@@ -0,0 +1,35 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\MimeType;
+
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
+
+/**
+ * Guesses the mime type of a file.
+ *
+ * @author Bernhard Schussek
+ */
+interface MimeTypeGuesserInterface
+{
+ /**
+ * Guesses the mime type of the file with the given path.
+ *
+ * @param string $path The path to the file
+ *
+ * @return string The mime type or NULL, if none could be guessed
+ *
+ * @throws FileNotFoundException If the file does not exist
+ * @throws AccessDeniedException If the file could not be read
+ */
+ public function guess($path);
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/Stream.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/Stream.php
new file mode 100644
index 0000000..69ae74c
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/Stream.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File;
+
+/**
+ * A PHP stream of unknown size.
+ *
+ * @author Nicolas Grekas
+ */
+class Stream extends File
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function getSize()
+ {
+ return false;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/UploadedFile.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/UploadedFile.php
new file mode 100644
index 0000000..082d8d5
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/File/UploadedFile.php
@@ -0,0 +1,266 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File;
+
+use Symfony\Component\HttpFoundation\File\Exception\FileException;
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser;
+
+/**
+ * A file uploaded through a form.
+ *
+ * @author Bernhard Schussek
+ * @author Florian Eckerstorfer
+ * @author Fabien Potencier
+ */
+class UploadedFile extends File
+{
+ private $test = false;
+ private $originalName;
+ private $mimeType;
+ private $size;
+ private $error;
+
+ /**
+ * Accepts the information of the uploaded file as provided by the PHP global $_FILES.
+ *
+ * The file object is only created when the uploaded file is valid (i.e. when the
+ * isValid() method returns true). Otherwise the only methods that could be called
+ * on an UploadedFile instance are:
+ *
+ * * getClientOriginalName,
+ * * getClientMimeType,
+ * * isValid,
+ * * getError.
+ *
+ * Calling any other method on an non-valid instance will cause an unpredictable result.
+ *
+ * @param string $path The full temporary path to the file
+ * @param string $originalName The original file name of the uploaded file
+ * @param string|null $mimeType The type of the file as provided by PHP; null defaults to application/octet-stream
+ * @param int|null $size The file size provided by the uploader
+ * @param int|null $error The error constant of the upload (one of PHP's UPLOAD_ERR_XXX constants); null defaults to UPLOAD_ERR_OK
+ * @param bool $test Whether the test mode is active
+ * Local files are used in test mode hence the code should not enforce HTTP uploads
+ *
+ * @throws FileException If file_uploads is disabled
+ * @throws FileNotFoundException If the file does not exist
+ */
+ public function __construct($path, $originalName, $mimeType = null, $size = null, $error = null, $test = false)
+ {
+ $this->originalName = $this->getName($originalName);
+ $this->mimeType = $mimeType ?: 'application/octet-stream';
+ $this->size = $size;
+ $this->error = $error ?: UPLOAD_ERR_OK;
+ $this->test = (bool) $test;
+
+ parent::__construct($path, UPLOAD_ERR_OK === $this->error);
+ }
+
+ /**
+ * Returns the original file name.
+ *
+ * It is extracted from the request from which the file has been uploaded.
+ * Then it should not be considered as a safe value.
+ *
+ * @return string|null The original name
+ */
+ public function getClientOriginalName()
+ {
+ return $this->originalName;
+ }
+
+ /**
+ * Returns the original file extension.
+ *
+ * It is extracted from the original file name that was uploaded.
+ * Then it should not be considered as a safe value.
+ *
+ * @return string The extension
+ */
+ public function getClientOriginalExtension()
+ {
+ return pathinfo($this->originalName, PATHINFO_EXTENSION);
+ }
+
+ /**
+ * Returns the file mime type.
+ *
+ * The client mime type is extracted from the request from which the file
+ * was uploaded, so it should not be considered as a safe value.
+ *
+ * For a trusted mime type, use getMimeType() instead (which guesses the mime
+ * type based on the file content).
+ *
+ * @return string|null The mime type
+ *
+ * @see getMimeType()
+ */
+ public function getClientMimeType()
+ {
+ return $this->mimeType;
+ }
+
+ /**
+ * Returns the extension based on the client mime type.
+ *
+ * If the mime type is unknown, returns null.
+ *
+ * This method uses the mime type as guessed by getClientMimeType()
+ * to guess the file extension. As such, the extension returned
+ * by this method cannot be trusted.
+ *
+ * For a trusted extension, use guessExtension() instead (which guesses
+ * the extension based on the guessed mime type for the file).
+ *
+ * @return string|null The guessed extension or null if it cannot be guessed
+ *
+ * @see guessExtension()
+ * @see getClientMimeType()
+ */
+ public function guessClientExtension()
+ {
+ $type = $this->getClientMimeType();
+ $guesser = ExtensionGuesser::getInstance();
+
+ return $guesser->guess($type);
+ }
+
+ /**
+ * Returns the file size.
+ *
+ * It is extracted from the request from which the file has been uploaded.
+ * Then it should not be considered as a safe value.
+ *
+ * @return int|null The file size
+ */
+ public function getClientSize()
+ {
+ return $this->size;
+ }
+
+ /**
+ * Returns the upload error.
+ *
+ * If the upload was successful, the constant UPLOAD_ERR_OK is returned.
+ * Otherwise one of the other UPLOAD_ERR_XXX constants is returned.
+ *
+ * @return int The upload error
+ */
+ public function getError()
+ {
+ return $this->error;
+ }
+
+ /**
+ * Returns whether the file was uploaded successfully.
+ *
+ * @return bool True if the file has been uploaded with HTTP and no error occurred
+ */
+ public function isValid()
+ {
+ $isOk = UPLOAD_ERR_OK === $this->error;
+
+ return $this->test ? $isOk : $isOk && is_uploaded_file($this->getPathname());
+ }
+
+ /**
+ * Moves the file to a new location.
+ *
+ * @param string $directory The destination folder
+ * @param string $name The new file name
+ *
+ * @return File A File object representing the new file
+ *
+ * @throws FileException if, for any reason, the file could not have been moved
+ */
+ public function move($directory, $name = null)
+ {
+ if ($this->isValid()) {
+ if ($this->test) {
+ return parent::move($directory, $name);
+ }
+
+ $target = $this->getTargetFile($directory, $name);
+
+ if (!@move_uploaded_file($this->getPathname(), $target)) {
+ $error = error_get_last();
+ throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error['message'])));
+ }
+
+ @chmod($target, 0666 & ~umask());
+
+ return $target;
+ }
+
+ throw new FileException($this->getErrorMessage());
+ }
+
+ /**
+ * Returns the maximum size of an uploaded file as configured in php.ini.
+ *
+ * @return int The maximum size of an uploaded file in bytes
+ */
+ public static function getMaxFilesize()
+ {
+ $iniMax = strtolower(ini_get('upload_max_filesize'));
+
+ if ('' === $iniMax) {
+ return PHP_INT_MAX;
+ }
+
+ $max = ltrim($iniMax, '+');
+ if (0 === strpos($max, '0x')) {
+ $max = intval($max, 16);
+ } elseif (0 === strpos($max, '0')) {
+ $max = intval($max, 8);
+ } else {
+ $max = (int) $max;
+ }
+
+ switch (substr($iniMax, -1)) {
+ case 't': $max *= 1024;
+ // no break
+ case 'g': $max *= 1024;
+ // no break
+ case 'm': $max *= 1024;
+ // no break
+ case 'k': $max *= 1024;
+ }
+
+ return $max;
+ }
+
+ /**
+ * Returns an informative upload error message.
+ *
+ * @return string The error message regarding the specified error code
+ */
+ public function getErrorMessage()
+ {
+ static $errors = array(
+ UPLOAD_ERR_INI_SIZE => 'The file "%s" exceeds your upload_max_filesize ini directive (limit is %d KiB).',
+ UPLOAD_ERR_FORM_SIZE => 'The file "%s" exceeds the upload limit defined in your form.',
+ UPLOAD_ERR_PARTIAL => 'The file "%s" was only partially uploaded.',
+ UPLOAD_ERR_NO_FILE => 'No file was uploaded.',
+ UPLOAD_ERR_CANT_WRITE => 'The file "%s" could not be written on disk.',
+ UPLOAD_ERR_NO_TMP_DIR => 'File could not be uploaded: missing temporary directory.',
+ UPLOAD_ERR_EXTENSION => 'File upload was stopped by a PHP extension.',
+ );
+
+ $errorCode = $this->error;
+ $maxFilesize = UPLOAD_ERR_INI_SIZE === $errorCode ? self::getMaxFilesize() / 1024 : 0;
+ $message = isset($errors[$errorCode]) ? $errors[$errorCode] : 'The file "%s" was not uploaded due to an unknown error.';
+
+ return sprintf($message, $this->getClientOriginalName(), $maxFilesize);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/FileBag.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/FileBag.php
new file mode 100644
index 0000000..5edd0e6
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/FileBag.php
@@ -0,0 +1,144 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+use Symfony\Component\HttpFoundation\File\UploadedFile;
+
+/**
+ * FileBag is a container for uploaded files.
+ *
+ * @author Fabien Potencier
+ * @author Bulat Shakirzyanov
+ */
+class FileBag extends ParameterBag
+{
+ private static $fileKeys = array('error', 'name', 'size', 'tmp_name', 'type');
+
+ /**
+ * @param array $parameters An array of HTTP files
+ */
+ public function __construct(array $parameters = array())
+ {
+ $this->replace($parameters);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function replace(array $files = array())
+ {
+ $this->parameters = array();
+ $this->add($files);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set($key, $value)
+ {
+ if (!is_array($value) && !$value instanceof UploadedFile) {
+ throw new \InvalidArgumentException('An uploaded file must be an array or an instance of UploadedFile.');
+ }
+
+ parent::set($key, $this->convertFileInformation($value));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add(array $files = array())
+ {
+ foreach ($files as $key => $file) {
+ $this->set($key, $file);
+ }
+ }
+
+ /**
+ * Converts uploaded files to UploadedFile instances.
+ *
+ * @param array|UploadedFile $file A (multi-dimensional) array of uploaded file information
+ *
+ * @return UploadedFile[]|UploadedFile|null A (multi-dimensional) array of UploadedFile instances
+ */
+ protected function convertFileInformation($file)
+ {
+ if ($file instanceof UploadedFile) {
+ return $file;
+ }
+
+ $file = $this->fixPhpFilesArray($file);
+ if (is_array($file)) {
+ $keys = array_keys($file);
+ sort($keys);
+
+ if ($keys == self::$fileKeys) {
+ if (UPLOAD_ERR_NO_FILE == $file['error']) {
+ $file = null;
+ } else {
+ $file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['size'], $file['error']);
+ }
+ } else {
+ $file = array_map(array($this, 'convertFileInformation'), $file);
+ if (array_keys($keys) === $keys) {
+ $file = array_filter($file);
+ }
+ }
+ }
+
+ return $file;
+ }
+
+ /**
+ * Fixes a malformed PHP $_FILES array.
+ *
+ * PHP has a bug that the format of the $_FILES array differs, depending on
+ * whether the uploaded file fields had normal field names or array-like
+ * field names ("normal" vs. "parent[child]").
+ *
+ * This method fixes the array to look like the "normal" $_FILES array.
+ *
+ * It's safe to pass an already converted array, in which case this method
+ * just returns the original array unmodified.
+ *
+ * @return array
+ */
+ protected function fixPhpFilesArray($data)
+ {
+ if (!is_array($data)) {
+ return $data;
+ }
+
+ $keys = array_keys($data);
+ sort($keys);
+
+ if (self::$fileKeys != $keys || !isset($data['name']) || !is_array($data['name'])) {
+ return $data;
+ }
+
+ $files = $data;
+ foreach (self::$fileKeys as $k) {
+ unset($files[$k]);
+ }
+
+ foreach ($data['name'] as $key => $name) {
+ $files[$key] = $this->fixPhpFilesArray(array(
+ 'error' => $data['error'][$key],
+ 'name' => $name,
+ 'type' => $data['type'][$key],
+ 'tmp_name' => $data['tmp_name'][$key],
+ 'size' => $data['size'][$key],
+ ));
+ }
+
+ return $files;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/HeaderBag.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/HeaderBag.php
new file mode 100644
index 0000000..7aaa52a
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/HeaderBag.php
@@ -0,0 +1,331 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * HeaderBag is a container for HTTP headers.
+ *
+ * @author Fabien Potencier
+ */
+class HeaderBag implements \IteratorAggregate, \Countable
+{
+ protected $headers = array();
+ protected $cacheControl = array();
+
+ /**
+ * @param array $headers An array of HTTP headers
+ */
+ public function __construct(array $headers = array())
+ {
+ foreach ($headers as $key => $values) {
+ $this->set($key, $values);
+ }
+ }
+
+ /**
+ * Returns the headers as a string.
+ *
+ * @return string The headers
+ */
+ public function __toString()
+ {
+ if (!$headers = $this->all()) {
+ return '';
+ }
+
+ ksort($headers);
+ $max = max(array_map('strlen', array_keys($headers))) + 1;
+ $content = '';
+ foreach ($headers as $name => $values) {
+ $name = implode('-', array_map('ucfirst', explode('-', $name)));
+ foreach ($values as $value) {
+ $content .= sprintf("%-{$max}s %s\r\n", $name.':', $value);
+ }
+ }
+
+ return $content;
+ }
+
+ /**
+ * Returns the headers.
+ *
+ * @return array An array of headers
+ */
+ public function all()
+ {
+ return $this->headers;
+ }
+
+ /**
+ * Returns the parameter keys.
+ *
+ * @return array An array of parameter keys
+ */
+ public function keys()
+ {
+ return array_keys($this->all());
+ }
+
+ /**
+ * Replaces the current HTTP headers by a new set.
+ *
+ * @param array $headers An array of HTTP headers
+ */
+ public function replace(array $headers = array())
+ {
+ $this->headers = array();
+ $this->add($headers);
+ }
+
+ /**
+ * Adds new headers the current HTTP headers set.
+ *
+ * @param array $headers An array of HTTP headers
+ */
+ public function add(array $headers)
+ {
+ foreach ($headers as $key => $values) {
+ $this->set($key, $values);
+ }
+ }
+
+ /**
+ * Returns a header value by name.
+ *
+ * @param string $key The header name
+ * @param string|string[] $default The default value
+ * @param bool $first Whether to return the first value or all header values
+ *
+ * @return string|string[] The first header value or default value if $first is true, an array of values otherwise
+ */
+ public function get($key, $default = null, $first = true)
+ {
+ $key = str_replace('_', '-', strtolower($key));
+ $headers = $this->all();
+
+ if (!array_key_exists($key, $headers)) {
+ if (null === $default) {
+ return $first ? null : array();
+ }
+
+ return $first ? $default : array($default);
+ }
+
+ if ($first) {
+ return \count($headers[$key]) ? $headers[$key][0] : $default;
+ }
+
+ return $headers[$key];
+ }
+
+ /**
+ * Sets a header by name.
+ *
+ * @param string $key The key
+ * @param string|string[] $values The value or an array of values
+ * @param bool $replace Whether to replace the actual value or not (true by default)
+ */
+ public function set($key, $values, $replace = true)
+ {
+ $key = str_replace('_', '-', strtolower($key));
+
+ if (\is_array($values)) {
+ $values = array_values($values);
+
+ if (true === $replace || !isset($this->headers[$key])) {
+ $this->headers[$key] = $values;
+ } else {
+ $this->headers[$key] = array_merge($this->headers[$key], $values);
+ }
+ } else {
+ if (true === $replace || !isset($this->headers[$key])) {
+ $this->headers[$key] = array($values);
+ } else {
+ $this->headers[$key][] = $values;
+ }
+ }
+
+ if ('cache-control' === $key) {
+ $this->cacheControl = $this->parseCacheControl(implode(', ', $this->headers[$key]));
+ }
+ }
+
+ /**
+ * Returns true if the HTTP header is defined.
+ *
+ * @param string $key The HTTP header
+ *
+ * @return bool true if the parameter exists, false otherwise
+ */
+ public function has($key)
+ {
+ return array_key_exists(str_replace('_', '-', strtolower($key)), $this->all());
+ }
+
+ /**
+ * Returns true if the given HTTP header contains the given value.
+ *
+ * @param string $key The HTTP header name
+ * @param string $value The HTTP value
+ *
+ * @return bool true if the value is contained in the header, false otherwise
+ */
+ public function contains($key, $value)
+ {
+ return in_array($value, $this->get($key, null, false));
+ }
+
+ /**
+ * Removes a header.
+ *
+ * @param string $key The HTTP header name
+ */
+ public function remove($key)
+ {
+ $key = str_replace('_', '-', strtolower($key));
+
+ unset($this->headers[$key]);
+
+ if ('cache-control' === $key) {
+ $this->cacheControl = array();
+ }
+ }
+
+ /**
+ * Returns the HTTP header value converted to a date.
+ *
+ * @param string $key The parameter key
+ * @param \DateTime $default The default value
+ *
+ * @return null|\DateTime The parsed DateTime or the default value if the header does not exist
+ *
+ * @throws \RuntimeException When the HTTP header is not parseable
+ */
+ public function getDate($key, \DateTime $default = null)
+ {
+ if (null === $value = $this->get($key)) {
+ return $default;
+ }
+
+ if (false === $date = \DateTime::createFromFormat(DATE_RFC2822, $value)) {
+ throw new \RuntimeException(sprintf('The %s HTTP header is not parseable (%s).', $key, $value));
+ }
+
+ return $date;
+ }
+
+ /**
+ * Adds a custom Cache-Control directive.
+ *
+ * @param string $key The Cache-Control directive name
+ * @param mixed $value The Cache-Control directive value
+ */
+ public function addCacheControlDirective($key, $value = true)
+ {
+ $this->cacheControl[$key] = $value;
+
+ $this->set('Cache-Control', $this->getCacheControlHeader());
+ }
+
+ /**
+ * Returns true if the Cache-Control directive is defined.
+ *
+ * @param string $key The Cache-Control directive
+ *
+ * @return bool true if the directive exists, false otherwise
+ */
+ public function hasCacheControlDirective($key)
+ {
+ return array_key_exists($key, $this->cacheControl);
+ }
+
+ /**
+ * Returns a Cache-Control directive value by name.
+ *
+ * @param string $key The directive name
+ *
+ * @return mixed|null The directive value if defined, null otherwise
+ */
+ public function getCacheControlDirective($key)
+ {
+ return array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null;
+ }
+
+ /**
+ * Removes a Cache-Control directive.
+ *
+ * @param string $key The Cache-Control directive
+ */
+ public function removeCacheControlDirective($key)
+ {
+ unset($this->cacheControl[$key]);
+
+ $this->set('Cache-Control', $this->getCacheControlHeader());
+ }
+
+ /**
+ * Returns an iterator for headers.
+ *
+ * @return \ArrayIterator An \ArrayIterator instance
+ */
+ public function getIterator()
+ {
+ return new \ArrayIterator($this->headers);
+ }
+
+ /**
+ * Returns the number of headers.
+ *
+ * @return int The number of headers
+ */
+ public function count()
+ {
+ return count($this->headers);
+ }
+
+ protected function getCacheControlHeader()
+ {
+ $parts = array();
+ ksort($this->cacheControl);
+ foreach ($this->cacheControl as $key => $value) {
+ if (true === $value) {
+ $parts[] = $key;
+ } else {
+ if (preg_match('#[^a-zA-Z0-9._-]#', $value)) {
+ $value = '"'.$value.'"';
+ }
+
+ $parts[] = "$key=$value";
+ }
+ }
+
+ return implode(', ', $parts);
+ }
+
+ /**
+ * Parses a Cache-Control HTTP header.
+ *
+ * @param string $header The value of the Cache-Control HTTP header
+ *
+ * @return array An array representing the attribute values
+ */
+ protected function parseCacheControl($header)
+ {
+ $cacheControl = array();
+ preg_match_all('#([a-zA-Z][a-zA-Z_-]*)\s*(?:=(?:"([^"]*)"|([^ \t",;]*)))?#', $header, $matches, PREG_SET_ORDER);
+ foreach ($matches as $match) {
+ $cacheControl[strtolower($match[1])] = isset($match[3]) ? $match[3] : (isset($match[2]) ? $match[2] : true);
+ }
+
+ return $cacheControl;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/IpUtils.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/IpUtils.php
new file mode 100644
index 0000000..86d135b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/IpUtils.php
@@ -0,0 +1,156 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * Http utility functions.
+ *
+ * @author Fabien Potencier
+ */
+class IpUtils
+{
+ private static $checkedIps = array();
+
+ /**
+ * This class should not be instantiated.
+ */
+ private function __construct()
+ {
+ }
+
+ /**
+ * Checks if an IPv4 or IPv6 address is contained in the list of given IPs or subnets.
+ *
+ * @param string $requestIp IP to check
+ * @param string|array $ips List of IPs or subnets (can be a string if only a single one)
+ *
+ * @return bool Whether the IP is valid
+ */
+ public static function checkIp($requestIp, $ips)
+ {
+ if (!is_array($ips)) {
+ $ips = array($ips);
+ }
+
+ $method = substr_count($requestIp, ':') > 1 ? 'checkIp6' : 'checkIp4';
+
+ foreach ($ips as $ip) {
+ if (self::$method($requestIp, $ip)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Compares two IPv4 addresses.
+ * In case a subnet is given, it checks if it contains the request IP.
+ *
+ * @param string $requestIp IPv4 address to check
+ * @param string $ip IPv4 address or subnet in CIDR notation
+ *
+ * @return bool Whether the request IP matches the IP, or whether the request IP is within the CIDR subnet
+ */
+ public static function checkIp4($requestIp, $ip)
+ {
+ $cacheKey = $requestIp.'-'.$ip;
+ if (isset(self::$checkedIps[$cacheKey])) {
+ return self::$checkedIps[$cacheKey];
+ }
+
+ if (!filter_var($requestIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
+ return self::$checkedIps[$cacheKey] = false;
+ }
+
+ if (false !== strpos($ip, '/')) {
+ list($address, $netmask) = explode('/', $ip, 2);
+
+ if ('0' === $netmask) {
+ return self::$checkedIps[$cacheKey] = filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
+ }
+
+ if ($netmask < 0 || $netmask > 32) {
+ return self::$checkedIps[$cacheKey] = false;
+ }
+ } else {
+ $address = $ip;
+ $netmask = 32;
+ }
+
+ if (false === ip2long($address)) {
+ return self::$checkedIps[$cacheKey] = false;
+ }
+
+ return self::$checkedIps[$cacheKey] = 0 === substr_compare(sprintf('%032b', ip2long($requestIp)), sprintf('%032b', ip2long($address)), 0, $netmask);
+ }
+
+ /**
+ * Compares two IPv6 addresses.
+ * In case a subnet is given, it checks if it contains the request IP.
+ *
+ * @author David Soria Parra
+ *
+ * @see https://github.com/dsp/v6tools
+ *
+ * @param string $requestIp IPv6 address to check
+ * @param string $ip IPv6 address or subnet in CIDR notation
+ *
+ * @return bool Whether the IP is valid
+ *
+ * @throws \RuntimeException When IPV6 support is not enabled
+ */
+ public static function checkIp6($requestIp, $ip)
+ {
+ $cacheKey = $requestIp.'-'.$ip;
+ if (isset(self::$checkedIps[$cacheKey])) {
+ return self::$checkedIps[$cacheKey];
+ }
+
+ if (!((extension_loaded('sockets') && defined('AF_INET6')) || @inet_pton('::1'))) {
+ throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".');
+ }
+
+ if (false !== strpos($ip, '/')) {
+ list($address, $netmask) = explode('/', $ip, 2);
+
+ if ('0' === $netmask) {
+ return (bool) unpack('n*', @inet_pton($address));
+ }
+
+ if ($netmask < 1 || $netmask > 128) {
+ return self::$checkedIps[$cacheKey] = false;
+ }
+ } else {
+ $address = $ip;
+ $netmask = 128;
+ }
+
+ $bytesAddr = unpack('n*', @inet_pton($address));
+ $bytesTest = unpack('n*', @inet_pton($requestIp));
+
+ if (!$bytesAddr || !$bytesTest) {
+ return self::$checkedIps[$cacheKey] = false;
+ }
+
+ for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; ++$i) {
+ $left = $netmask - 16 * ($i - 1);
+ $left = ($left <= 16) ? $left : 16;
+ $mask = ~(0xffff >> $left) & 0xffff;
+ if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) {
+ return self::$checkedIps[$cacheKey] = false;
+ }
+ }
+
+ return self::$checkedIps[$cacheKey] = true;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/JsonResponse.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/JsonResponse.php
new file mode 100644
index 0000000..137ac33
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/JsonResponse.php
@@ -0,0 +1,220 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * Response represents an HTTP response in JSON format.
+ *
+ * Note that this class does not force the returned JSON content to be an
+ * object. It is however recommended that you do return an object as it
+ * protects yourself against XSSI and JSON-JavaScript Hijacking.
+ *
+ * @see https://www.owasp.org/index.php/OWASP_AJAX_Security_Guidelines#Always_return_JSON_with_an_Object_on_the_outside
+ *
+ * @author Igor Wiedler
+ */
+class JsonResponse extends Response
+{
+ protected $data;
+ protected $callback;
+
+ // Encode <, >, ', &, and " characters in the JSON, making it also safe to be embedded into HTML.
+ // 15 === JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT
+ const DEFAULT_ENCODING_OPTIONS = 15;
+
+ protected $encodingOptions = self::DEFAULT_ENCODING_OPTIONS;
+
+ /**
+ * @param mixed $data The response data
+ * @param int $status The response status code
+ * @param array $headers An array of response headers
+ * @param bool $json If the data is already a JSON string
+ */
+ public function __construct($data = null, $status = 200, $headers = array(), $json = false)
+ {
+ parent::__construct('', $status, $headers);
+
+ if (null === $data) {
+ $data = new \ArrayObject();
+ }
+
+ $json ? $this->setJson($data) : $this->setData($data);
+ }
+
+ /**
+ * Factory method for chainability.
+ *
+ * Example:
+ *
+ * return JsonResponse::create($data, 200)
+ * ->setSharedMaxAge(300);
+ *
+ * @param mixed $data The json response data
+ * @param int $status The response status code
+ * @param array $headers An array of response headers
+ *
+ * @return static
+ */
+ public static function create($data = null, $status = 200, $headers = array())
+ {
+ return new static($data, $status, $headers);
+ }
+
+ /**
+ * Make easier the creation of JsonResponse from raw json.
+ */
+ public static function fromJsonString($data = null, $status = 200, $headers = array())
+ {
+ return new static($data, $status, $headers, true);
+ }
+
+ /**
+ * Sets the JSONP callback.
+ *
+ * @param string|null $callback The JSONP callback or null to use none
+ *
+ * @return $this
+ *
+ * @throws \InvalidArgumentException When the callback name is not valid
+ */
+ public function setCallback($callback = null)
+ {
+ if (null !== $callback) {
+ // partially taken from http://www.geekality.net/2011/08/03/valid-javascript-identifier/
+ // partially taken from https://github.com/willdurand/JsonpCallbackValidator
+ // JsonpCallbackValidator is released under the MIT License. See https://github.com/willdurand/JsonpCallbackValidator/blob/v1.1.0/LICENSE for details.
+ // (c) William Durand
+ $pattern = '/^[$_\p{L}][$_\p{L}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\x{200C}\x{200D}]*(?:\[(?:"(?:\\\.|[^"\\\])*"|\'(?:\\\.|[^\'\\\])*\'|\d+)\])*?$/u';
+ $reserved = array(
+ 'break', 'do', 'instanceof', 'typeof', 'case', 'else', 'new', 'var', 'catch', 'finally', 'return', 'void', 'continue', 'for', 'switch', 'while',
+ 'debugger', 'function', 'this', 'with', 'default', 'if', 'throw', 'delete', 'in', 'try', 'class', 'enum', 'extends', 'super', 'const', 'export',
+ 'import', 'implements', 'let', 'private', 'public', 'yield', 'interface', 'package', 'protected', 'static', 'null', 'true', 'false',
+ );
+ $parts = explode('.', $callback);
+ foreach ($parts as $part) {
+ if (!preg_match($pattern, $part) || in_array($part, $reserved, true)) {
+ throw new \InvalidArgumentException('The callback name is not valid.');
+ }
+ }
+ }
+
+ $this->callback = $callback;
+
+ return $this->update();
+ }
+
+ /**
+ * Sets a raw string containing a JSON document to be sent.
+ *
+ * @param string $json
+ *
+ * @return $this
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setJson($json)
+ {
+ $this->data = $json;
+
+ return $this->update();
+ }
+
+ /**
+ * Sets the data to be sent as JSON.
+ *
+ * @param mixed $data
+ *
+ * @return $this
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setData($data = array())
+ {
+ if (defined('HHVM_VERSION')) {
+ // HHVM does not trigger any warnings and let exceptions
+ // thrown from a JsonSerializable object pass through.
+ // If only PHP did the same...
+ $data = json_encode($data, $this->encodingOptions);
+ } else {
+ if (!interface_exists('JsonSerializable', false)) {
+ set_error_handler(function () { return false; });
+ try {
+ $data = @json_encode($data, $this->encodingOptions);
+ } finally {
+ restore_error_handler();
+ }
+ } else {
+ try {
+ $data = json_encode($data, $this->encodingOptions);
+ } catch (\Exception $e) {
+ if ('Exception' === get_class($e) && 0 === strpos($e->getMessage(), 'Failed calling ')) {
+ throw $e->getPrevious() ?: $e;
+ }
+ throw $e;
+ }
+ }
+ }
+
+ if (JSON_ERROR_NONE !== json_last_error()) {
+ throw new \InvalidArgumentException(json_last_error_msg());
+ }
+
+ return $this->setJson($data);
+ }
+
+ /**
+ * Returns options used while encoding data to JSON.
+ *
+ * @return int
+ */
+ public function getEncodingOptions()
+ {
+ return $this->encodingOptions;
+ }
+
+ /**
+ * Sets options used while encoding data to JSON.
+ *
+ * @param int $encodingOptions
+ *
+ * @return $this
+ */
+ public function setEncodingOptions($encodingOptions)
+ {
+ $this->encodingOptions = (int) $encodingOptions;
+
+ return $this->setData(json_decode($this->data));
+ }
+
+ /**
+ * Updates the content and headers according to the JSON data and callback.
+ *
+ * @return $this
+ */
+ protected function update()
+ {
+ if (null !== $this->callback) {
+ // Not using application/javascript for compatibility reasons with older browsers.
+ $this->headers->set('Content-Type', 'text/javascript');
+
+ return $this->setContent(sprintf('/**/%s(%s);', $this->callback, $this->data));
+ }
+
+ // Only set the header when there is none or when it equals 'text/javascript' (from a previous update with callback)
+ // in order to not overwrite a custom definition.
+ if (!$this->headers->has('Content-Type') || 'text/javascript' === $this->headers->get('Content-Type')) {
+ $this->headers->set('Content-Type', 'application/json');
+ }
+
+ return $this->setContent($this->data);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/LICENSE b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/LICENSE
new file mode 100644
index 0000000..21d7fb9
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2004-2018 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/ParameterBag.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/ParameterBag.php
new file mode 100644
index 0000000..257ef8b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/ParameterBag.php
@@ -0,0 +1,234 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * ParameterBag is a container for key/value pairs.
+ *
+ * @author Fabien Potencier
+ */
+class ParameterBag implements \IteratorAggregate, \Countable
+{
+ /**
+ * Parameter storage.
+ */
+ protected $parameters;
+
+ /**
+ * @param array $parameters An array of parameters
+ */
+ public function __construct(array $parameters = array())
+ {
+ $this->parameters = $parameters;
+ }
+
+ /**
+ * Returns the parameters.
+ *
+ * @return array An array of parameters
+ */
+ public function all()
+ {
+ return $this->parameters;
+ }
+
+ /**
+ * Returns the parameter keys.
+ *
+ * @return array An array of parameter keys
+ */
+ public function keys()
+ {
+ return array_keys($this->parameters);
+ }
+
+ /**
+ * Replaces the current parameters by a new set.
+ *
+ * @param array $parameters An array of parameters
+ */
+ public function replace(array $parameters = array())
+ {
+ $this->parameters = $parameters;
+ }
+
+ /**
+ * Adds parameters.
+ *
+ * @param array $parameters An array of parameters
+ */
+ public function add(array $parameters = array())
+ {
+ $this->parameters = array_replace($this->parameters, $parameters);
+ }
+
+ /**
+ * Returns a parameter by name.
+ *
+ * @param string $key The key
+ * @param mixed $default The default value if the parameter key does not exist
+ *
+ * @return mixed
+ */
+ public function get($key, $default = null)
+ {
+ return array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default;
+ }
+
+ /**
+ * Sets a parameter by name.
+ *
+ * @param string $key The key
+ * @param mixed $value The value
+ */
+ public function set($key, $value)
+ {
+ $this->parameters[$key] = $value;
+ }
+
+ /**
+ * Returns true if the parameter is defined.
+ *
+ * @param string $key The key
+ *
+ * @return bool true if the parameter exists, false otherwise
+ */
+ public function has($key)
+ {
+ return array_key_exists($key, $this->parameters);
+ }
+
+ /**
+ * Removes a parameter.
+ *
+ * @param string $key The key
+ */
+ public function remove($key)
+ {
+ unset($this->parameters[$key]);
+ }
+
+ /**
+ * Returns the alphabetic characters of the parameter value.
+ *
+ * @param string $key The parameter key
+ * @param string $default The default value if the parameter key does not exist
+ *
+ * @return string The filtered value
+ */
+ public function getAlpha($key, $default = '')
+ {
+ return preg_replace('/[^[:alpha:]]/', '', $this->get($key, $default));
+ }
+
+ /**
+ * Returns the alphabetic characters and digits of the parameter value.
+ *
+ * @param string $key The parameter key
+ * @param string $default The default value if the parameter key does not exist
+ *
+ * @return string The filtered value
+ */
+ public function getAlnum($key, $default = '')
+ {
+ return preg_replace('/[^[:alnum:]]/', '', $this->get($key, $default));
+ }
+
+ /**
+ * Returns the digits of the parameter value.
+ *
+ * @param string $key The parameter key
+ * @param string $default The default value if the parameter key does not exist
+ *
+ * @return string The filtered value
+ */
+ public function getDigits($key, $default = '')
+ {
+ // we need to remove - and + because they're allowed in the filter
+ return str_replace(array('-', '+'), '', $this->filter($key, $default, FILTER_SANITIZE_NUMBER_INT));
+ }
+
+ /**
+ * Returns the parameter value converted to integer.
+ *
+ * @param string $key The parameter key
+ * @param int $default The default value if the parameter key does not exist
+ *
+ * @return int The filtered value
+ */
+ public function getInt($key, $default = 0)
+ {
+ return (int) $this->get($key, $default);
+ }
+
+ /**
+ * Returns the parameter value converted to boolean.
+ *
+ * @param string $key The parameter key
+ * @param mixed $default The default value if the parameter key does not exist
+ *
+ * @return bool The filtered value
+ */
+ public function getBoolean($key, $default = false)
+ {
+ return $this->filter($key, $default, FILTER_VALIDATE_BOOLEAN);
+ }
+
+ /**
+ * Filter key.
+ *
+ * @param string $key Key
+ * @param mixed $default Default = null
+ * @param int $filter FILTER_* constant
+ * @param mixed $options Filter options
+ *
+ * @see http://php.net/manual/en/function.filter-var.php
+ *
+ * @return mixed
+ */
+ public function filter($key, $default = null, $filter = FILTER_DEFAULT, $options = array())
+ {
+ $value = $this->get($key, $default);
+
+ // Always turn $options into an array - this allows filter_var option shortcuts.
+ if (!is_array($options) && $options) {
+ $options = array('flags' => $options);
+ }
+
+ // Add a convenience check for arrays.
+ if (is_array($value) && !isset($options['flags'])) {
+ $options['flags'] = FILTER_REQUIRE_ARRAY;
+ }
+
+ return filter_var($value, $filter, $options);
+ }
+
+ /**
+ * Returns an iterator for parameters.
+ *
+ * @return \ArrayIterator An \ArrayIterator instance
+ */
+ public function getIterator()
+ {
+ return new \ArrayIterator($this->parameters);
+ }
+
+ /**
+ * Returns the number of parameters.
+ *
+ * @return int The number of parameters
+ */
+ public function count()
+ {
+ return count($this->parameters);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/README.md b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/README.md
new file mode 100644
index 0000000..8907f0b
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/README.md
@@ -0,0 +1,14 @@
+HttpFoundation Component
+========================
+
+The HttpFoundation component defines an object-oriented layer for the HTTP
+specification.
+
+Resources
+---------
+
+ * [Documentation](https://symfony.com/doc/current/components/http_foundation/index.html)
+ * [Contributing](https://symfony.com/doc/current/contributing/index.html)
+ * [Report issues](https://github.com/symfony/symfony/issues) and
+ [send Pull Requests](https://github.com/symfony/symfony/pulls)
+ in the [main Symfony repository](https://github.com/symfony/symfony)
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/RedirectResponse.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/RedirectResponse.php
new file mode 100644
index 0000000..01681dc
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/RedirectResponse.php
@@ -0,0 +1,109 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * RedirectResponse represents an HTTP response doing a redirect.
+ *
+ * @author Fabien Potencier
+ */
+class RedirectResponse extends Response
+{
+ protected $targetUrl;
+
+ /**
+ * Creates a redirect response so that it conforms to the rules defined for a redirect status code.
+ *
+ * @param string $url The URL to redirect to. The URL should be a full URL, with schema etc.,
+ * but practically every browser redirects on paths only as well
+ * @param int $status The status code (302 by default)
+ * @param array $headers The headers (Location is always set to the given URL)
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @see http://tools.ietf.org/html/rfc2616#section-10.3
+ */
+ public function __construct($url, $status = 302, $headers = array())
+ {
+ parent::__construct('', $status, $headers);
+
+ $this->setTargetUrl($url);
+
+ if (!$this->isRedirect()) {
+ throw new \InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $status));
+ }
+
+ if (301 == $status && !array_key_exists('cache-control', $headers)) {
+ $this->headers->remove('cache-control');
+ }
+ }
+
+ /**
+ * Factory method for chainability.
+ *
+ * @param string $url The url to redirect to
+ * @param int $status The response status code
+ * @param array $headers An array of response headers
+ *
+ * @return static
+ */
+ public static function create($url = '', $status = 302, $headers = array())
+ {
+ return new static($url, $status, $headers);
+ }
+
+ /**
+ * Returns the target URL.
+ *
+ * @return string target URL
+ */
+ public function getTargetUrl()
+ {
+ return $this->targetUrl;
+ }
+
+ /**
+ * Sets the redirect target of this response.
+ *
+ * @param string $url The URL to redirect to
+ *
+ * @return $this
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setTargetUrl($url)
+ {
+ if (empty($url)) {
+ throw new \InvalidArgumentException('Cannot redirect to an empty URL.');
+ }
+
+ $this->targetUrl = $url;
+
+ $this->setContent(
+ sprintf('
+
+
+
+
+
+ Redirecting to %1$s
+
+
+ Redirecting to %1$s .
+
+', htmlspecialchars($url, ENT_QUOTES, 'UTF-8')));
+
+ $this->headers->set('Location', $url);
+
+ return $this;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Request.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Request.php
new file mode 100644
index 0000000..164fb4e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Request.php
@@ -0,0 +1,2154 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException;
+use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
+use Symfony\Component\HttpFoundation\Session\SessionInterface;
+
+/**
+ * Request represents an HTTP request.
+ *
+ * The methods dealing with URL accept / return a raw path (% encoded):
+ * * getBasePath
+ * * getBaseUrl
+ * * getPathInfo
+ * * getRequestUri
+ * * getUri
+ * * getUriForPath
+ *
+ * @author Fabien Potencier
+ */
+class Request
+{
+ const HEADER_FORWARDED = 0b00001; // When using RFC 7239
+ const HEADER_X_FORWARDED_FOR = 0b00010;
+ const HEADER_X_FORWARDED_HOST = 0b00100;
+ const HEADER_X_FORWARDED_PROTO = 0b01000;
+ const HEADER_X_FORWARDED_PORT = 0b10000;
+ const HEADER_X_FORWARDED_ALL = 0b11110; // All "X-Forwarded-*" headers
+ const HEADER_X_FORWARDED_AWS_ELB = 0b11010; // AWS ELB doesn't send X-Forwarded-Host
+
+ /** @deprecated since version 3.3, to be removed in 4.0 */
+ const HEADER_CLIENT_IP = self::HEADER_X_FORWARDED_FOR;
+ /** @deprecated since version 3.3, to be removed in 4.0 */
+ const HEADER_CLIENT_HOST = self::HEADER_X_FORWARDED_HOST;
+ /** @deprecated since version 3.3, to be removed in 4.0 */
+ const HEADER_CLIENT_PROTO = self::HEADER_X_FORWARDED_PROTO;
+ /** @deprecated since version 3.3, to be removed in 4.0 */
+ const HEADER_CLIENT_PORT = self::HEADER_X_FORWARDED_PORT;
+
+ const METHOD_HEAD = 'HEAD';
+ const METHOD_GET = 'GET';
+ const METHOD_POST = 'POST';
+ const METHOD_PUT = 'PUT';
+ const METHOD_PATCH = 'PATCH';
+ const METHOD_DELETE = 'DELETE';
+ const METHOD_PURGE = 'PURGE';
+ const METHOD_OPTIONS = 'OPTIONS';
+ const METHOD_TRACE = 'TRACE';
+ const METHOD_CONNECT = 'CONNECT';
+
+ /**
+ * @var string[]
+ */
+ protected static $trustedProxies = array();
+
+ /**
+ * @var string[]
+ */
+ protected static $trustedHostPatterns = array();
+
+ /**
+ * @var string[]
+ */
+ protected static $trustedHosts = array();
+
+ /**
+ * Names for headers that can be trusted when
+ * using trusted proxies.
+ *
+ * The FORWARDED header is the standard as of rfc7239.
+ *
+ * The other headers are non-standard, but widely used
+ * by popular reverse proxies (like Apache mod_proxy or Amazon EC2).
+ *
+ * @deprecated since version 3.3, to be removed in 4.0
+ */
+ protected static $trustedHeaders = array(
+ self::HEADER_FORWARDED => 'FORWARDED',
+ self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
+ self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
+ self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
+ self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
+ );
+
+ protected static $httpMethodParameterOverride = false;
+
+ /**
+ * Custom parameters.
+ *
+ * @var \Symfony\Component\HttpFoundation\ParameterBag
+ */
+ public $attributes;
+
+ /**
+ * Request body parameters ($_POST).
+ *
+ * @var \Symfony\Component\HttpFoundation\ParameterBag
+ */
+ public $request;
+
+ /**
+ * Query string parameters ($_GET).
+ *
+ * @var \Symfony\Component\HttpFoundation\ParameterBag
+ */
+ public $query;
+
+ /**
+ * Server and execution environment parameters ($_SERVER).
+ *
+ * @var \Symfony\Component\HttpFoundation\ServerBag
+ */
+ public $server;
+
+ /**
+ * Uploaded files ($_FILES).
+ *
+ * @var \Symfony\Component\HttpFoundation\FileBag
+ */
+ public $files;
+
+ /**
+ * Cookies ($_COOKIE).
+ *
+ * @var \Symfony\Component\HttpFoundation\ParameterBag
+ */
+ public $cookies;
+
+ /**
+ * Headers (taken from the $_SERVER).
+ *
+ * @var \Symfony\Component\HttpFoundation\HeaderBag
+ */
+ public $headers;
+
+ /**
+ * @var string|resource|false|null
+ */
+ protected $content;
+
+ /**
+ * @var array
+ */
+ protected $languages;
+
+ /**
+ * @var array
+ */
+ protected $charsets;
+
+ /**
+ * @var array
+ */
+ protected $encodings;
+
+ /**
+ * @var array
+ */
+ protected $acceptableContentTypes;
+
+ /**
+ * @var string
+ */
+ protected $pathInfo;
+
+ /**
+ * @var string
+ */
+ protected $requestUri;
+
+ /**
+ * @var string
+ */
+ protected $baseUrl;
+
+ /**
+ * @var string
+ */
+ protected $basePath;
+
+ /**
+ * @var string
+ */
+ protected $method;
+
+ /**
+ * @var string
+ */
+ protected $format;
+
+ /**
+ * @var \Symfony\Component\HttpFoundation\Session\SessionInterface
+ */
+ protected $session;
+
+ /**
+ * @var string
+ */
+ protected $locale;
+
+ /**
+ * @var string
+ */
+ protected $defaultLocale = 'en';
+
+ /**
+ * @var array
+ */
+ protected static $formats;
+
+ protected static $requestFactory;
+
+ private $isHostValid = true;
+ private $isForwardedValid = true;
+
+ private static $trustedHeaderSet = -1;
+
+ /** @deprecated since version 3.3, to be removed in 4.0 */
+ private static $trustedHeaderNames = array(
+ self::HEADER_FORWARDED => 'FORWARDED',
+ self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
+ self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
+ self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
+ self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
+ );
+
+ private static $forwardedParams = array(
+ self::HEADER_X_FORWARDED_FOR => 'for',
+ self::HEADER_X_FORWARDED_HOST => 'host',
+ self::HEADER_X_FORWARDED_PROTO => 'proto',
+ self::HEADER_X_FORWARDED_PORT => 'host',
+ );
+
+ /**
+ * @param array $query The GET parameters
+ * @param array $request The POST parameters
+ * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
+ * @param array $cookies The COOKIE parameters
+ * @param array $files The FILES parameters
+ * @param array $server The SERVER parameters
+ * @param string|resource|null $content The raw body data
+ */
+ public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
+ {
+ $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content);
+ }
+
+ /**
+ * Sets the parameters for this request.
+ *
+ * This method also re-initializes all properties.
+ *
+ * @param array $query The GET parameters
+ * @param array $request The POST parameters
+ * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
+ * @param array $cookies The COOKIE parameters
+ * @param array $files The FILES parameters
+ * @param array $server The SERVER parameters
+ * @param string|resource|null $content The raw body data
+ */
+ public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
+ {
+ $this->request = new ParameterBag($request);
+ $this->query = new ParameterBag($query);
+ $this->attributes = new ParameterBag($attributes);
+ $this->cookies = new ParameterBag($cookies);
+ $this->files = new FileBag($files);
+ $this->server = new ServerBag($server);
+ $this->headers = new HeaderBag($this->server->getHeaders());
+
+ $this->content = $content;
+ $this->languages = null;
+ $this->charsets = null;
+ $this->encodings = null;
+ $this->acceptableContentTypes = null;
+ $this->pathInfo = null;
+ $this->requestUri = null;
+ $this->baseUrl = null;
+ $this->basePath = null;
+ $this->method = null;
+ $this->format = null;
+ }
+
+ /**
+ * Creates a new request with values from PHP's super globals.
+ *
+ * @return static
+ */
+ public static function createFromGlobals()
+ {
+ // With the php's bug #66606, the php's built-in web server
+ // stores the Content-Type and Content-Length header values in
+ // HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH fields.
+ $server = $_SERVER;
+ if ('cli-server' === PHP_SAPI) {
+ if (array_key_exists('HTTP_CONTENT_LENGTH', $_SERVER)) {
+ $server['CONTENT_LENGTH'] = $_SERVER['HTTP_CONTENT_LENGTH'];
+ }
+ if (array_key_exists('HTTP_CONTENT_TYPE', $_SERVER)) {
+ $server['CONTENT_TYPE'] = $_SERVER['HTTP_CONTENT_TYPE'];
+ }
+ }
+
+ $request = self::createRequestFromFactory($_GET, $_POST, array(), $_COOKIE, $_FILES, $server);
+
+ if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
+ && in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE', 'PATCH'))
+ ) {
+ parse_str($request->getContent(), $data);
+ $request->request = new ParameterBag($data);
+ }
+
+ return $request;
+ }
+
+ /**
+ * Creates a Request based on a given URI and configuration.
+ *
+ * The information contained in the URI always take precedence
+ * over the other information (server and parameters).
+ *
+ * @param string $uri The URI
+ * @param string $method The HTTP method
+ * @param array $parameters The query (GET) or request (POST) parameters
+ * @param array $cookies The request cookies ($_COOKIE)
+ * @param array $files The request files ($_FILES)
+ * @param array $server The server parameters ($_SERVER)
+ * @param string|resource|null $content The raw body data
+ *
+ * @return static
+ */
+ public static function create($uri, $method = 'GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null)
+ {
+ $server = array_replace(array(
+ 'SERVER_NAME' => 'localhost',
+ 'SERVER_PORT' => 80,
+ 'HTTP_HOST' => 'localhost',
+ 'HTTP_USER_AGENT' => 'Symfony/3.X',
+ 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
+ 'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5',
+ 'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
+ 'REMOTE_ADDR' => '127.0.0.1',
+ 'SCRIPT_NAME' => '',
+ 'SCRIPT_FILENAME' => '',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ 'REQUEST_TIME' => time(),
+ ), $server);
+
+ $server['PATH_INFO'] = '';
+ $server['REQUEST_METHOD'] = strtoupper($method);
+
+ $components = parse_url($uri);
+ if (isset($components['host'])) {
+ $server['SERVER_NAME'] = $components['host'];
+ $server['HTTP_HOST'] = $components['host'];
+ }
+
+ if (isset($components['scheme'])) {
+ if ('https' === $components['scheme']) {
+ $server['HTTPS'] = 'on';
+ $server['SERVER_PORT'] = 443;
+ } else {
+ unset($server['HTTPS']);
+ $server['SERVER_PORT'] = 80;
+ }
+ }
+
+ if (isset($components['port'])) {
+ $server['SERVER_PORT'] = $components['port'];
+ $server['HTTP_HOST'] = $server['HTTP_HOST'].':'.$components['port'];
+ }
+
+ if (isset($components['user'])) {
+ $server['PHP_AUTH_USER'] = $components['user'];
+ }
+
+ if (isset($components['pass'])) {
+ $server['PHP_AUTH_PW'] = $components['pass'];
+ }
+
+ if (!isset($components['path'])) {
+ $components['path'] = '/';
+ }
+
+ switch (strtoupper($method)) {
+ case 'POST':
+ case 'PUT':
+ case 'DELETE':
+ if (!isset($server['CONTENT_TYPE'])) {
+ $server['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
+ }
+ // no break
+ case 'PATCH':
+ $request = $parameters;
+ $query = array();
+ break;
+ default:
+ $request = array();
+ $query = $parameters;
+ break;
+ }
+
+ $queryString = '';
+ if (isset($components['query'])) {
+ parse_str(html_entity_decode($components['query']), $qs);
+
+ if ($query) {
+ $query = array_replace($qs, $query);
+ $queryString = http_build_query($query, '', '&');
+ } else {
+ $query = $qs;
+ $queryString = $components['query'];
+ }
+ } elseif ($query) {
+ $queryString = http_build_query($query, '', '&');
+ }
+
+ $server['REQUEST_URI'] = $components['path'].('' !== $queryString ? '?'.$queryString : '');
+ $server['QUERY_STRING'] = $queryString;
+
+ return self::createRequestFromFactory($query, $request, array(), $cookies, $files, $server, $content);
+ }
+
+ /**
+ * Sets a callable able to create a Request instance.
+ *
+ * This is mainly useful when you need to override the Request class
+ * to keep BC with an existing system. It should not be used for any
+ * other purpose.
+ *
+ * @param callable|null $callable A PHP callable
+ */
+ public static function setFactory($callable)
+ {
+ self::$requestFactory = $callable;
+ }
+
+ /**
+ * Clones a request and overrides some of its parameters.
+ *
+ * @param array $query The GET parameters
+ * @param array $request The POST parameters
+ * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
+ * @param array $cookies The COOKIE parameters
+ * @param array $files The FILES parameters
+ * @param array $server The SERVER parameters
+ *
+ * @return static
+ */
+ public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null)
+ {
+ $dup = clone $this;
+ if (null !== $query) {
+ $dup->query = new ParameterBag($query);
+ }
+ if (null !== $request) {
+ $dup->request = new ParameterBag($request);
+ }
+ if (null !== $attributes) {
+ $dup->attributes = new ParameterBag($attributes);
+ }
+ if (null !== $cookies) {
+ $dup->cookies = new ParameterBag($cookies);
+ }
+ if (null !== $files) {
+ $dup->files = new FileBag($files);
+ }
+ if (null !== $server) {
+ $dup->server = new ServerBag($server);
+ $dup->headers = new HeaderBag($dup->server->getHeaders());
+ }
+ $dup->languages = null;
+ $dup->charsets = null;
+ $dup->encodings = null;
+ $dup->acceptableContentTypes = null;
+ $dup->pathInfo = null;
+ $dup->requestUri = null;
+ $dup->baseUrl = null;
+ $dup->basePath = null;
+ $dup->method = null;
+ $dup->format = null;
+
+ if (!$dup->get('_format') && $this->get('_format')) {
+ $dup->attributes->set('_format', $this->get('_format'));
+ }
+
+ if (!$dup->getRequestFormat(null)) {
+ $dup->setRequestFormat($this->getRequestFormat(null));
+ }
+
+ return $dup;
+ }
+
+ /**
+ * Clones the current request.
+ *
+ * Note that the session is not cloned as duplicated requests
+ * are most of the time sub-requests of the main one.
+ */
+ public function __clone()
+ {
+ $this->query = clone $this->query;
+ $this->request = clone $this->request;
+ $this->attributes = clone $this->attributes;
+ $this->cookies = clone $this->cookies;
+ $this->files = clone $this->files;
+ $this->server = clone $this->server;
+ $this->headers = clone $this->headers;
+ }
+
+ /**
+ * Returns the request as a string.
+ *
+ * @return string The request
+ */
+ public function __toString()
+ {
+ try {
+ $content = $this->getContent();
+ } catch (\LogicException $e) {
+ return trigger_error($e, E_USER_ERROR);
+ }
+
+ $cookieHeader = '';
+ $cookies = array();
+
+ foreach ($this->cookies as $k => $v) {
+ $cookies[] = $k.'='.$v;
+ }
+
+ if (!empty($cookies)) {
+ $cookieHeader = 'Cookie: '.implode('; ', $cookies)."\r\n";
+ }
+
+ return
+ sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n".
+ $this->headers.
+ $cookieHeader."\r\n".
+ $content;
+ }
+
+ /**
+ * Overrides the PHP global variables according to this request instance.
+ *
+ * It overrides $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE.
+ * $_FILES is never overridden, see rfc1867
+ */
+ public function overrideGlobals()
+ {
+ $this->server->set('QUERY_STRING', static::normalizeQueryString(http_build_query($this->query->all(), '', '&')));
+
+ $_GET = $this->query->all();
+ $_POST = $this->request->all();
+ $_SERVER = $this->server->all();
+ $_COOKIE = $this->cookies->all();
+
+ foreach ($this->headers->all() as $key => $value) {
+ $key = strtoupper(str_replace('-', '_', $key));
+ if (in_array($key, array('CONTENT_TYPE', 'CONTENT_LENGTH'))) {
+ $_SERVER[$key] = implode(', ', $value);
+ } else {
+ $_SERVER['HTTP_'.$key] = implode(', ', $value);
+ }
+ }
+
+ $request = array('g' => $_GET, 'p' => $_POST, 'c' => $_COOKIE);
+
+ $requestOrder = ini_get('request_order') ?: ini_get('variables_order');
+ $requestOrder = preg_replace('#[^cgp]#', '', strtolower($requestOrder)) ?: 'gp';
+
+ $_REQUEST = array();
+ foreach (str_split($requestOrder) as $order) {
+ $_REQUEST = array_merge($_REQUEST, $request[$order]);
+ }
+ }
+
+ /**
+ * Sets a list of trusted proxies.
+ *
+ * You should only list the reverse proxies that you manage directly.
+ *
+ * @param array $proxies A list of trusted proxies
+ * @param int $trustedHeaderSet A bit field of Request::HEADER_*, to set which headers to trust from your proxies
+ *
+ * @throws \InvalidArgumentException When $trustedHeaderSet is invalid
+ */
+ public static function setTrustedProxies(array $proxies/*, int $trustedHeaderSet*/)
+ {
+ self::$trustedProxies = $proxies;
+
+ if (2 > func_num_args()) {
+ @trigger_error(sprintf('The %s() method expects a bit field of Request::HEADER_* as second argument since Symfony 3.3. Defining it will be required in 4.0. ', __METHOD__), E_USER_DEPRECATED);
+
+ return;
+ }
+ $trustedHeaderSet = (int) func_get_arg(1);
+
+ foreach (self::$trustedHeaderNames as $header => $name) {
+ self::$trustedHeaders[$header] = $header & $trustedHeaderSet ? $name : null;
+ }
+ self::$trustedHeaderSet = $trustedHeaderSet;
+ }
+
+ /**
+ * Gets the list of trusted proxies.
+ *
+ * @return array An array of trusted proxies
+ */
+ public static function getTrustedProxies()
+ {
+ return self::$trustedProxies;
+ }
+
+ /**
+ * Gets the set of trusted headers from trusted proxies.
+ *
+ * @return int A bit field of Request::HEADER_* that defines which headers are trusted from your proxies
+ */
+ public static function getTrustedHeaderSet()
+ {
+ return self::$trustedHeaderSet;
+ }
+
+ /**
+ * Sets a list of trusted host patterns.
+ *
+ * You should only list the hosts you manage using regexs.
+ *
+ * @param array $hostPatterns A list of trusted host patterns
+ */
+ public static function setTrustedHosts(array $hostPatterns)
+ {
+ self::$trustedHostPatterns = array_map(function ($hostPattern) {
+ return sprintf('#%s#i', $hostPattern);
+ }, $hostPatterns);
+ // we need to reset trusted hosts on trusted host patterns change
+ self::$trustedHosts = array();
+ }
+
+ /**
+ * Gets the list of trusted host patterns.
+ *
+ * @return array An array of trusted host patterns
+ */
+ public static function getTrustedHosts()
+ {
+ return self::$trustedHostPatterns;
+ }
+
+ /**
+ * Sets the name for trusted headers.
+ *
+ * The following header keys are supported:
+ *
+ * * Request::HEADER_CLIENT_IP: defaults to X-Forwarded-For (see getClientIp())
+ * * Request::HEADER_CLIENT_HOST: defaults to X-Forwarded-Host (see getHost())
+ * * Request::HEADER_CLIENT_PORT: defaults to X-Forwarded-Port (see getPort())
+ * * Request::HEADER_CLIENT_PROTO: defaults to X-Forwarded-Proto (see getScheme() and isSecure())
+ * * Request::HEADER_FORWARDED: defaults to Forwarded (see RFC 7239)
+ *
+ * Setting an empty value allows to disable the trusted header for the given key.
+ *
+ * @param string $key The header key
+ * @param string $value The header name
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @deprecated since version 3.3, to be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead.
+ */
+ public static function setTrustedHeaderName($key, $value)
+ {
+ @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead.', __METHOD__), E_USER_DEPRECATED);
+
+ if ('forwarded' === $key) {
+ $key = self::HEADER_FORWARDED;
+ } elseif ('client_ip' === $key) {
+ $key = self::HEADER_CLIENT_IP;
+ } elseif ('client_host' === $key) {
+ $key = self::HEADER_CLIENT_HOST;
+ } elseif ('client_proto' === $key) {
+ $key = self::HEADER_CLIENT_PROTO;
+ } elseif ('client_port' === $key) {
+ $key = self::HEADER_CLIENT_PORT;
+ } elseif (!array_key_exists($key, self::$trustedHeaders)) {
+ throw new \InvalidArgumentException(sprintf('Unable to set the trusted header name for key "%s".', $key));
+ }
+
+ self::$trustedHeaders[$key] = $value;
+
+ if (null !== $value) {
+ self::$trustedHeaderNames[$key] = $value;
+ self::$trustedHeaderSet |= $key;
+ } else {
+ self::$trustedHeaderSet &= ~$key;
+ }
+ }
+
+ /**
+ * Gets the trusted proxy header name.
+ *
+ * @param string $key The header key
+ *
+ * @return string The header name
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @deprecated since version 3.3, to be removed in 4.0. Use the Request::getTrustedHeaderSet() method instead.
+ */
+ public static function getTrustedHeaderName($key)
+ {
+ if (2 > func_num_args() || func_get_arg(1)) {
+ @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the Request::getTrustedHeaderSet() method instead.', __METHOD__), E_USER_DEPRECATED);
+ }
+
+ if (!array_key_exists($key, self::$trustedHeaders)) {
+ throw new \InvalidArgumentException(sprintf('Unable to get the trusted header name for key "%s".', $key));
+ }
+
+ return self::$trustedHeaders[$key];
+ }
+
+ /**
+ * Normalizes a query string.
+ *
+ * It builds a normalized query string, where keys/value pairs are alphabetized,
+ * have consistent escaping and unneeded delimiters are removed.
+ *
+ * @param string $qs Query string
+ *
+ * @return string A normalized query string for the Request
+ */
+ public static function normalizeQueryString($qs)
+ {
+ if ('' == $qs) {
+ return '';
+ }
+
+ $parts = array();
+ $order = array();
+
+ foreach (explode('&', $qs) as $param) {
+ if ('' === $param || '=' === $param[0]) {
+ // Ignore useless delimiters, e.g. "x=y&".
+ // Also ignore pairs with empty key, even if there was a value, e.g. "=value", as such nameless values cannot be retrieved anyway.
+ // PHP also does not include them when building _GET.
+ continue;
+ }
+
+ $keyValuePair = explode('=', $param, 2);
+
+ // GET parameters, that are submitted from a HTML form, encode spaces as "+" by default (as defined in enctype application/x-www-form-urlencoded).
+ // PHP also converts "+" to spaces when filling the global _GET or when using the function parse_str. This is why we use urldecode and then normalize to
+ // RFC 3986 with rawurlencode.
+ $parts[] = isset($keyValuePair[1]) ?
+ rawurlencode(urldecode($keyValuePair[0])).'='.rawurlencode(urldecode($keyValuePair[1])) :
+ rawurlencode(urldecode($keyValuePair[0]));
+ $order[] = urldecode($keyValuePair[0]);
+ }
+
+ array_multisort($order, SORT_ASC, $parts);
+
+ return implode('&', $parts);
+ }
+
+ /**
+ * Enables support for the _method request parameter to determine the intended HTTP method.
+ *
+ * Be warned that enabling this feature might lead to CSRF issues in your code.
+ * Check that you are using CSRF tokens when required.
+ * If the HTTP method parameter override is enabled, an html-form with method "POST" can be altered
+ * and used to send a "PUT" or "DELETE" request via the _method request parameter.
+ * If these methods are not protected against CSRF, this presents a possible vulnerability.
+ *
+ * The HTTP method can only be overridden when the real HTTP method is POST.
+ */
+ public static function enableHttpMethodParameterOverride()
+ {
+ self::$httpMethodParameterOverride = true;
+ }
+
+ /**
+ * Checks whether support for the _method request parameter is enabled.
+ *
+ * @return bool True when the _method request parameter is enabled, false otherwise
+ */
+ public static function getHttpMethodParameterOverride()
+ {
+ return self::$httpMethodParameterOverride;
+ }
+
+ /**
+ * Gets a "parameter" value from any bag.
+ *
+ * This method is mainly useful for libraries that want to provide some flexibility. If you don't need the
+ * flexibility in controllers, it is better to explicitly get request parameters from the appropriate
+ * public property instead (attributes, query, request).
+ *
+ * Order of precedence: PATH (routing placeholders or custom attributes), GET, BODY
+ *
+ * @param string $key The key
+ * @param mixed $default The default value if the parameter key does not exist
+ *
+ * @return mixed
+ */
+ public function get($key, $default = null)
+ {
+ if ($this !== $result = $this->attributes->get($key, $this)) {
+ return $result;
+ }
+
+ if ($this !== $result = $this->query->get($key, $this)) {
+ return $result;
+ }
+
+ if ($this !== $result = $this->request->get($key, $this)) {
+ return $result;
+ }
+
+ return $default;
+ }
+
+ /**
+ * Gets the Session.
+ *
+ * @return SessionInterface|null The session
+ */
+ public function getSession()
+ {
+ return $this->session;
+ }
+
+ /**
+ * Whether the request contains a Session which was started in one of the
+ * previous requests.
+ *
+ * @return bool
+ */
+ public function hasPreviousSession()
+ {
+ // the check for $this->session avoids malicious users trying to fake a session cookie with proper name
+ return $this->hasSession() && $this->cookies->has($this->session->getName());
+ }
+
+ /**
+ * Whether the request contains a Session object.
+ *
+ * This method does not give any information about the state of the session object,
+ * like whether the session is started or not. It is just a way to check if this Request
+ * is associated with a Session instance.
+ *
+ * @return bool true when the Request contains a Session object, false otherwise
+ */
+ public function hasSession()
+ {
+ return null !== $this->session;
+ }
+
+ /**
+ * Sets the Session.
+ *
+ * @param SessionInterface $session The Session
+ */
+ public function setSession(SessionInterface $session)
+ {
+ $this->session = $session;
+ }
+
+ /**
+ * Returns the client IP addresses.
+ *
+ * In the returned array the most trusted IP address is first, and the
+ * least trusted one last. The "real" client IP address is the last one,
+ * but this is also the least trusted one. Trusted proxies are stripped.
+ *
+ * Use this method carefully; you should use getClientIp() instead.
+ *
+ * @return array The client IP addresses
+ *
+ * @see getClientIp()
+ */
+ public function getClientIps()
+ {
+ $ip = $this->server->get('REMOTE_ADDR');
+
+ if (!$this->isFromTrustedProxy()) {
+ return array($ip);
+ }
+
+ return $this->getTrustedValues(self::HEADER_CLIENT_IP, $ip) ?: array($ip);
+ }
+
+ /**
+ * Returns the client IP address.
+ *
+ * This method can read the client IP address from the "X-Forwarded-For" header
+ * when trusted proxies were set via "setTrustedProxies()". The "X-Forwarded-For"
+ * header value is a comma+space separated list of IP addresses, the left-most
+ * being the original client, and each successive proxy that passed the request
+ * adding the IP address where it received the request from.
+ *
+ * If your reverse proxy uses a different header name than "X-Forwarded-For",
+ * ("Client-Ip" for instance), configure it via the $trustedHeaderSet
+ * argument of the Request::setTrustedProxies() method instead.
+ *
+ * @return string|null The client IP address
+ *
+ * @see getClientIps()
+ * @see http://en.wikipedia.org/wiki/X-Forwarded-For
+ */
+ public function getClientIp()
+ {
+ $ipAddresses = $this->getClientIps();
+
+ return $ipAddresses[0];
+ }
+
+ /**
+ * Returns current script name.
+ *
+ * @return string
+ */
+ public function getScriptName()
+ {
+ return $this->server->get('SCRIPT_NAME', $this->server->get('ORIG_SCRIPT_NAME', ''));
+ }
+
+ /**
+ * Returns the path being requested relative to the executed script.
+ *
+ * The path info always starts with a /.
+ *
+ * Suppose this request is instantiated from /mysite on localhost:
+ *
+ * * http://localhost/mysite returns an empty string
+ * * http://localhost/mysite/about returns '/about'
+ * * http://localhost/mysite/enco%20ded returns '/enco%20ded'
+ * * http://localhost/mysite/about?var=1 returns '/about'
+ *
+ * @return string The raw path (i.e. not urldecoded)
+ */
+ public function getPathInfo()
+ {
+ if (null === $this->pathInfo) {
+ $this->pathInfo = $this->preparePathInfo();
+ }
+
+ return $this->pathInfo;
+ }
+
+ /**
+ * Returns the root path from which this request is executed.
+ *
+ * Suppose that an index.php file instantiates this request object:
+ *
+ * * http://localhost/index.php returns an empty string
+ * * http://localhost/index.php/page returns an empty string
+ * * http://localhost/web/index.php returns '/web'
+ * * http://localhost/we%20b/index.php returns '/we%20b'
+ *
+ * @return string The raw path (i.e. not urldecoded)
+ */
+ public function getBasePath()
+ {
+ if (null === $this->basePath) {
+ $this->basePath = $this->prepareBasePath();
+ }
+
+ return $this->basePath;
+ }
+
+ /**
+ * Returns the root URL from which this request is executed.
+ *
+ * The base URL never ends with a /.
+ *
+ * This is similar to getBasePath(), except that it also includes the
+ * script filename (e.g. index.php) if one exists.
+ *
+ * @return string The raw URL (i.e. not urldecoded)
+ */
+ public function getBaseUrl()
+ {
+ if (null === $this->baseUrl) {
+ $this->baseUrl = $this->prepareBaseUrl();
+ }
+
+ return $this->baseUrl;
+ }
+
+ /**
+ * Gets the request's scheme.
+ *
+ * @return string
+ */
+ public function getScheme()
+ {
+ return $this->isSecure() ? 'https' : 'http';
+ }
+
+ /**
+ * Returns the port on which the request is made.
+ *
+ * This method can read the client port from the "X-Forwarded-Port" header
+ * when trusted proxies were set via "setTrustedProxies()".
+ *
+ * The "X-Forwarded-Port" header must contain the client port.
+ *
+ * If your reverse proxy uses a different header name than "X-Forwarded-Port",
+ * configure it via via the $trustedHeaderSet argument of the
+ * Request::setTrustedProxies() method instead.
+ *
+ * @return int|string can be a string if fetched from the server bag
+ */
+ public function getPort()
+ {
+ if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_CLIENT_PORT)) {
+ $host = $host[0];
+ } elseif ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_CLIENT_HOST)) {
+ $host = $host[0];
+ } elseif (!$host = $this->headers->get('HOST')) {
+ return $this->server->get('SERVER_PORT');
+ }
+
+ if ('[' === $host[0]) {
+ $pos = strpos($host, ':', strrpos($host, ']'));
+ } else {
+ $pos = strrpos($host, ':');
+ }
+
+ if (false !== $pos) {
+ return (int) substr($host, $pos + 1);
+ }
+
+ return 'https' === $this->getScheme() ? 443 : 80;
+ }
+
+ /**
+ * Returns the user.
+ *
+ * @return string|null
+ */
+ public function getUser()
+ {
+ return $this->headers->get('PHP_AUTH_USER');
+ }
+
+ /**
+ * Returns the password.
+ *
+ * @return string|null
+ */
+ public function getPassword()
+ {
+ return $this->headers->get('PHP_AUTH_PW');
+ }
+
+ /**
+ * Gets the user info.
+ *
+ * @return string A user name and, optionally, scheme-specific information about how to gain authorization to access the server
+ */
+ public function getUserInfo()
+ {
+ $userinfo = $this->getUser();
+
+ $pass = $this->getPassword();
+ if ('' != $pass) {
+ $userinfo .= ":$pass";
+ }
+
+ return $userinfo;
+ }
+
+ /**
+ * Returns the HTTP host being requested.
+ *
+ * The port name will be appended to the host if it's non-standard.
+ *
+ * @return string
+ */
+ public function getHttpHost()
+ {
+ $scheme = $this->getScheme();
+ $port = $this->getPort();
+
+ if (('http' == $scheme && 80 == $port) || ('https' == $scheme && 443 == $port)) {
+ return $this->getHost();
+ }
+
+ return $this->getHost().':'.$port;
+ }
+
+ /**
+ * Returns the requested URI (path and query string).
+ *
+ * @return string The raw URI (i.e. not URI decoded)
+ */
+ public function getRequestUri()
+ {
+ if (null === $this->requestUri) {
+ $this->requestUri = $this->prepareRequestUri();
+ }
+
+ return $this->requestUri;
+ }
+
+ /**
+ * Gets the scheme and HTTP host.
+ *
+ * If the URL was called with basic authentication, the user
+ * and the password are not added to the generated string.
+ *
+ * @return string The scheme and HTTP host
+ */
+ public function getSchemeAndHttpHost()
+ {
+ return $this->getScheme().'://'.$this->getHttpHost();
+ }
+
+ /**
+ * Generates a normalized URI (URL) for the Request.
+ *
+ * @return string A normalized URI (URL) for the Request
+ *
+ * @see getQueryString()
+ */
+ public function getUri()
+ {
+ if (null !== $qs = $this->getQueryString()) {
+ $qs = '?'.$qs;
+ }
+
+ return $this->getSchemeAndHttpHost().$this->getBaseUrl().$this->getPathInfo().$qs;
+ }
+
+ /**
+ * Generates a normalized URI for the given path.
+ *
+ * @param string $path A path to use instead of the current one
+ *
+ * @return string The normalized URI for the path
+ */
+ public function getUriForPath($path)
+ {
+ return $this->getSchemeAndHttpHost().$this->getBaseUrl().$path;
+ }
+
+ /**
+ * Returns the path as relative reference from the current Request path.
+ *
+ * Only the URIs path component (no schema, host etc.) is relevant and must be given.
+ * Both paths must be absolute and not contain relative parts.
+ * Relative URLs from one resource to another are useful when generating self-contained downloadable document archives.
+ * Furthermore, they can be used to reduce the link size in documents.
+ *
+ * Example target paths, given a base path of "/a/b/c/d":
+ * - "/a/b/c/d" -> ""
+ * - "/a/b/c/" -> "./"
+ * - "/a/b/" -> "../"
+ * - "/a/b/c/other" -> "other"
+ * - "/a/x/y" -> "../../x/y"
+ *
+ * @param string $path The target path
+ *
+ * @return string The relative target path
+ */
+ public function getRelativeUriForPath($path)
+ {
+ // be sure that we are dealing with an absolute path
+ if (!isset($path[0]) || '/' !== $path[0]) {
+ return $path;
+ }
+
+ if ($path === $basePath = $this->getPathInfo()) {
+ return '';
+ }
+
+ $sourceDirs = explode('/', isset($basePath[0]) && '/' === $basePath[0] ? substr($basePath, 1) : $basePath);
+ $targetDirs = explode('/', isset($path[0]) && '/' === $path[0] ? substr($path, 1) : $path);
+ array_pop($sourceDirs);
+ $targetFile = array_pop($targetDirs);
+
+ foreach ($sourceDirs as $i => $dir) {
+ if (isset($targetDirs[$i]) && $dir === $targetDirs[$i]) {
+ unset($sourceDirs[$i], $targetDirs[$i]);
+ } else {
+ break;
+ }
+ }
+
+ $targetDirs[] = $targetFile;
+ $path = str_repeat('../', count($sourceDirs)).implode('/', $targetDirs);
+
+ // A reference to the same base directory or an empty subdirectory must be prefixed with "./".
+ // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
+ // as the first segment of a relative-path reference, as it would be mistaken for a scheme name
+ // (see http://tools.ietf.org/html/rfc3986#section-4.2).
+ return !isset($path[0]) || '/' === $path[0]
+ || false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos)
+ ? "./$path" : $path;
+ }
+
+ /**
+ * Generates the normalized query string for the Request.
+ *
+ * It builds a normalized query string, where keys/value pairs are alphabetized
+ * and have consistent escaping.
+ *
+ * @return string|null A normalized query string for the Request
+ */
+ public function getQueryString()
+ {
+ $qs = static::normalizeQueryString($this->server->get('QUERY_STRING'));
+
+ return '' === $qs ? null : $qs;
+ }
+
+ /**
+ * Checks whether the request is secure or not.
+ *
+ * This method can read the client protocol from the "X-Forwarded-Proto" header
+ * when trusted proxies were set via "setTrustedProxies()".
+ *
+ * The "X-Forwarded-Proto" header must contain the protocol: "https" or "http".
+ *
+ * If your reverse proxy uses a different header name than "X-Forwarded-Proto"
+ * ("SSL_HTTPS" for instance), configure it via the $trustedHeaderSet
+ * argument of the Request::setTrustedProxies() method instead.
+ *
+ * @return bool
+ */
+ public function isSecure()
+ {
+ if ($this->isFromTrustedProxy() && $proto = $this->getTrustedValues(self::HEADER_CLIENT_PROTO)) {
+ return in_array(strtolower($proto[0]), array('https', 'on', 'ssl', '1'), true);
+ }
+
+ $https = $this->server->get('HTTPS');
+
+ return !empty($https) && 'off' !== strtolower($https);
+ }
+
+ /**
+ * Returns the host name.
+ *
+ * This method can read the client host name from the "X-Forwarded-Host" header
+ * when trusted proxies were set via "setTrustedProxies()".
+ *
+ * The "X-Forwarded-Host" header must contain the client host name.
+ *
+ * If your reverse proxy uses a different header name than "X-Forwarded-Host",
+ * configure it via the $trustedHeaderSet argument of the
+ * Request::setTrustedProxies() method instead.
+ *
+ * @return string
+ *
+ * @throws SuspiciousOperationException when the host name is invalid or not trusted
+ */
+ public function getHost()
+ {
+ if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_CLIENT_HOST)) {
+ $host = $host[0];
+ } elseif (!$host = $this->headers->get('HOST')) {
+ if (!$host = $this->server->get('SERVER_NAME')) {
+ $host = $this->server->get('SERVER_ADDR', '');
+ }
+ }
+
+ // trim and remove port number from host
+ // host is lowercase as per RFC 952/2181
+ $host = strtolower(preg_replace('/:\d+$/', '', trim($host)));
+
+ // as the host can come from the user (HTTP_HOST and depending on the configuration, SERVER_NAME too can come from the user)
+ // check that it does not contain forbidden characters (see RFC 952 and RFC 2181)
+ // use preg_replace() instead of preg_match() to prevent DoS attacks with long host names
+ if ($host && '' !== preg_replace('/(?:^\[)?[a-zA-Z0-9-:\]_]+\.?/', '', $host)) {
+ if (!$this->isHostValid) {
+ return '';
+ }
+ $this->isHostValid = false;
+
+ throw new SuspiciousOperationException(sprintf('Invalid Host "%s".', $host));
+ }
+
+ if (count(self::$trustedHostPatterns) > 0) {
+ // to avoid host header injection attacks, you should provide a list of trusted host patterns
+
+ if (in_array($host, self::$trustedHosts)) {
+ return $host;
+ }
+
+ foreach (self::$trustedHostPatterns as $pattern) {
+ if (preg_match($pattern, $host)) {
+ self::$trustedHosts[] = $host;
+
+ return $host;
+ }
+ }
+
+ if (!$this->isHostValid) {
+ return '';
+ }
+ $this->isHostValid = false;
+
+ throw new SuspiciousOperationException(sprintf('Untrusted Host "%s".', $host));
+ }
+
+ return $host;
+ }
+
+ /**
+ * Sets the request method.
+ *
+ * @param string $method
+ */
+ public function setMethod($method)
+ {
+ $this->method = null;
+ $this->server->set('REQUEST_METHOD', $method);
+ }
+
+ /**
+ * Gets the request "intended" method.
+ *
+ * If the X-HTTP-Method-Override header is set, and if the method is a POST,
+ * then it is used to determine the "real" intended HTTP method.
+ *
+ * The _method request parameter can also be used to determine the HTTP method,
+ * but only if enableHttpMethodParameterOverride() has been called.
+ *
+ * The method is always an uppercased string.
+ *
+ * @return string The request method
+ *
+ * @see getRealMethod()
+ */
+ public function getMethod()
+ {
+ if (null === $this->method) {
+ $this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
+
+ if ('POST' === $this->method) {
+ if ($method = $this->headers->get('X-HTTP-METHOD-OVERRIDE')) {
+ $this->method = strtoupper($method);
+ } elseif (self::$httpMethodParameterOverride) {
+ $this->method = strtoupper($this->request->get('_method', $this->query->get('_method', 'POST')));
+ }
+ }
+ }
+
+ return $this->method;
+ }
+
+ /**
+ * Gets the "real" request method.
+ *
+ * @return string The request method
+ *
+ * @see getMethod()
+ */
+ public function getRealMethod()
+ {
+ return strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
+ }
+
+ /**
+ * Gets the mime type associated with the format.
+ *
+ * @param string $format The format
+ *
+ * @return string The associated mime type (null if not found)
+ */
+ public function getMimeType($format)
+ {
+ if (null === static::$formats) {
+ static::initializeFormats();
+ }
+
+ return isset(static::$formats[$format]) ? static::$formats[$format][0] : null;
+ }
+
+ /**
+ * Gets the mime types associated with the format.
+ *
+ * @param string $format The format
+ *
+ * @return array The associated mime types
+ */
+ public static function getMimeTypes($format)
+ {
+ if (null === static::$formats) {
+ static::initializeFormats();
+ }
+
+ return isset(static::$formats[$format]) ? static::$formats[$format] : array();
+ }
+
+ /**
+ * Gets the format associated with the mime type.
+ *
+ * @param string $mimeType The associated mime type
+ *
+ * @return string|null The format (null if not found)
+ */
+ public function getFormat($mimeType)
+ {
+ $canonicalMimeType = null;
+ if (false !== $pos = strpos($mimeType, ';')) {
+ $canonicalMimeType = substr($mimeType, 0, $pos);
+ }
+
+ if (null === static::$formats) {
+ static::initializeFormats();
+ }
+
+ foreach (static::$formats as $format => $mimeTypes) {
+ if (in_array($mimeType, (array) $mimeTypes)) {
+ return $format;
+ }
+ if (null !== $canonicalMimeType && in_array($canonicalMimeType, (array) $mimeTypes)) {
+ return $format;
+ }
+ }
+ }
+
+ /**
+ * Associates a format with mime types.
+ *
+ * @param string $format The format
+ * @param string|array $mimeTypes The associated mime types (the preferred one must be the first as it will be used as the content type)
+ */
+ public function setFormat($format, $mimeTypes)
+ {
+ if (null === static::$formats) {
+ static::initializeFormats();
+ }
+
+ static::$formats[$format] = is_array($mimeTypes) ? $mimeTypes : array($mimeTypes);
+ }
+
+ /**
+ * Gets the request format.
+ *
+ * Here is the process to determine the format:
+ *
+ * * format defined by the user (with setRequestFormat())
+ * * _format request attribute
+ * * $default
+ *
+ * @param string $default The default format
+ *
+ * @return string The request format
+ */
+ public function getRequestFormat($default = 'html')
+ {
+ if (null === $this->format) {
+ $this->format = $this->attributes->get('_format');
+ }
+
+ return null === $this->format ? $default : $this->format;
+ }
+
+ /**
+ * Sets the request format.
+ *
+ * @param string $format The request format
+ */
+ public function setRequestFormat($format)
+ {
+ $this->format = $format;
+ }
+
+ /**
+ * Gets the format associated with the request.
+ *
+ * @return string|null The format (null if no content type is present)
+ */
+ public function getContentType()
+ {
+ return $this->getFormat($this->headers->get('CONTENT_TYPE'));
+ }
+
+ /**
+ * Sets the default locale.
+ *
+ * @param string $locale
+ */
+ public function setDefaultLocale($locale)
+ {
+ $this->defaultLocale = $locale;
+
+ if (null === $this->locale) {
+ $this->setPhpDefaultLocale($locale);
+ }
+ }
+
+ /**
+ * Get the default locale.
+ *
+ * @return string
+ */
+ public function getDefaultLocale()
+ {
+ return $this->defaultLocale;
+ }
+
+ /**
+ * Sets the locale.
+ *
+ * @param string $locale
+ */
+ public function setLocale($locale)
+ {
+ $this->setPhpDefaultLocale($this->locale = $locale);
+ }
+
+ /**
+ * Get the locale.
+ *
+ * @return string
+ */
+ public function getLocale()
+ {
+ return null === $this->locale ? $this->defaultLocale : $this->locale;
+ }
+
+ /**
+ * Checks if the request method is of specified type.
+ *
+ * @param string $method Uppercase request method (GET, POST etc)
+ *
+ * @return bool
+ */
+ public function isMethod($method)
+ {
+ return $this->getMethod() === strtoupper($method);
+ }
+
+ /**
+ * Checks whether or not the method is safe.
+ *
+ * @see https://tools.ietf.org/html/rfc7231#section-4.2.1
+ *
+ * @param bool $andCacheable Adds the additional condition that the method should be cacheable. True by default.
+ *
+ * @return bool
+ */
+ public function isMethodSafe(/* $andCacheable = true */)
+ {
+ if (!func_num_args() || func_get_arg(0)) {
+ // This deprecation should be turned into a BadMethodCallException in 4.0 (without adding the argument in the signature)
+ // then setting $andCacheable to false should be deprecated in 4.1
+ @trigger_error('Checking only for cacheable HTTP methods with Symfony\Component\HttpFoundation\Request::isMethodSafe() is deprecated since Symfony 3.2 and will throw an exception in 4.0. Disable checking only for cacheable methods by calling the method with `false` as first argument or use the Request::isMethodCacheable() instead.', E_USER_DEPRECATED);
+
+ return in_array($this->getMethod(), array('GET', 'HEAD'));
+ }
+
+ return in_array($this->getMethod(), array('GET', 'HEAD', 'OPTIONS', 'TRACE'));
+ }
+
+ /**
+ * Checks whether or not the method is idempotent.
+ *
+ * @return bool
+ */
+ public function isMethodIdempotent()
+ {
+ return in_array($this->getMethod(), array('HEAD', 'GET', 'PUT', 'DELETE', 'TRACE', 'OPTIONS', 'PURGE'));
+ }
+
+ /**
+ * Checks whether the method is cacheable or not.
+ *
+ * @see https://tools.ietf.org/html/rfc7231#section-4.2.3
+ *
+ * @return bool
+ */
+ public function isMethodCacheable()
+ {
+ return in_array($this->getMethod(), array('GET', 'HEAD'));
+ }
+
+ /**
+ * Returns the protocol version.
+ *
+ * If the application is behind a proxy, the protocol version used in the
+ * requests between the client and the proxy and between the proxy and the
+ * server might be different. This returns the former (from the "Via" header)
+ * if the proxy is trusted (see "setTrustedProxies()"), otherwise it returns
+ * the latter (from the "SERVER_PROTOCOL" server parameter).
+ *
+ * @return string
+ */
+ public function getProtocolVersion()
+ {
+ if ($this->isFromTrustedProxy()) {
+ preg_match('~^(HTTP/)?([1-9]\.[0-9]) ~', $this->headers->get('Via'), $matches);
+
+ if ($matches) {
+ return 'HTTP/'.$matches[2];
+ }
+ }
+
+ return $this->server->get('SERVER_PROTOCOL');
+ }
+
+ /**
+ * Returns the request body content.
+ *
+ * @param bool $asResource If true, a resource will be returned
+ *
+ * @return string|resource The request body content or a resource to read the body stream
+ *
+ * @throws \LogicException
+ */
+ public function getContent($asResource = false)
+ {
+ $currentContentIsResource = is_resource($this->content);
+ if (\PHP_VERSION_ID < 50600 && false === $this->content) {
+ throw new \LogicException('getContent() can only be called once when using the resource return type and PHP below 5.6.');
+ }
+
+ if (true === $asResource) {
+ if ($currentContentIsResource) {
+ rewind($this->content);
+
+ return $this->content;
+ }
+
+ // Content passed in parameter (test)
+ if (is_string($this->content)) {
+ $resource = fopen('php://temp', 'r+');
+ fwrite($resource, $this->content);
+ rewind($resource);
+
+ return $resource;
+ }
+
+ $this->content = false;
+
+ return fopen('php://input', 'rb');
+ }
+
+ if ($currentContentIsResource) {
+ rewind($this->content);
+
+ return stream_get_contents($this->content);
+ }
+
+ if (null === $this->content || false === $this->content) {
+ $this->content = file_get_contents('php://input');
+ }
+
+ return $this->content;
+ }
+
+ /**
+ * Gets the Etags.
+ *
+ * @return array The entity tags
+ */
+ public function getETags()
+ {
+ return preg_split('/\s*,\s*/', $this->headers->get('if_none_match'), null, PREG_SPLIT_NO_EMPTY);
+ }
+
+ /**
+ * @return bool
+ */
+ public function isNoCache()
+ {
+ return $this->headers->hasCacheControlDirective('no-cache') || 'no-cache' == $this->headers->get('Pragma');
+ }
+
+ /**
+ * Returns the preferred language.
+ *
+ * @param array $locales An array of ordered available locales
+ *
+ * @return string|null The preferred locale
+ */
+ public function getPreferredLanguage(array $locales = null)
+ {
+ $preferredLanguages = $this->getLanguages();
+
+ if (empty($locales)) {
+ return isset($preferredLanguages[0]) ? $preferredLanguages[0] : null;
+ }
+
+ if (!$preferredLanguages) {
+ return $locales[0];
+ }
+
+ $extendedPreferredLanguages = array();
+ foreach ($preferredLanguages as $language) {
+ $extendedPreferredLanguages[] = $language;
+ if (false !== $position = strpos($language, '_')) {
+ $superLanguage = substr($language, 0, $position);
+ if (!in_array($superLanguage, $preferredLanguages)) {
+ $extendedPreferredLanguages[] = $superLanguage;
+ }
+ }
+ }
+
+ $preferredLanguages = array_values(array_intersect($extendedPreferredLanguages, $locales));
+
+ return isset($preferredLanguages[0]) ? $preferredLanguages[0] : $locales[0];
+ }
+
+ /**
+ * Gets a list of languages acceptable by the client browser.
+ *
+ * @return array Languages ordered in the user browser preferences
+ */
+ public function getLanguages()
+ {
+ if (null !== $this->languages) {
+ return $this->languages;
+ }
+
+ $languages = AcceptHeader::fromString($this->headers->get('Accept-Language'))->all();
+ $this->languages = array();
+ foreach ($languages as $lang => $acceptHeaderItem) {
+ if (false !== strpos($lang, '-')) {
+ $codes = explode('-', $lang);
+ if ('i' === $codes[0]) {
+ // Language not listed in ISO 639 that are not variants
+ // of any listed language, which can be registered with the
+ // i-prefix, such as i-cherokee
+ if (count($codes) > 1) {
+ $lang = $codes[1];
+ }
+ } else {
+ for ($i = 0, $max = count($codes); $i < $max; ++$i) {
+ if (0 === $i) {
+ $lang = strtolower($codes[0]);
+ } else {
+ $lang .= '_'.strtoupper($codes[$i]);
+ }
+ }
+ }
+ }
+
+ $this->languages[] = $lang;
+ }
+
+ return $this->languages;
+ }
+
+ /**
+ * Gets a list of charsets acceptable by the client browser.
+ *
+ * @return array List of charsets in preferable order
+ */
+ public function getCharsets()
+ {
+ if (null !== $this->charsets) {
+ return $this->charsets;
+ }
+
+ return $this->charsets = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Charset'))->all());
+ }
+
+ /**
+ * Gets a list of encodings acceptable by the client browser.
+ *
+ * @return array List of encodings in preferable order
+ */
+ public function getEncodings()
+ {
+ if (null !== $this->encodings) {
+ return $this->encodings;
+ }
+
+ return $this->encodings = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Encoding'))->all());
+ }
+
+ /**
+ * Gets a list of content types acceptable by the client browser.
+ *
+ * @return array List of content types in preferable order
+ */
+ public function getAcceptableContentTypes()
+ {
+ if (null !== $this->acceptableContentTypes) {
+ return $this->acceptableContentTypes;
+ }
+
+ return $this->acceptableContentTypes = array_keys(AcceptHeader::fromString($this->headers->get('Accept'))->all());
+ }
+
+ /**
+ * Returns true if the request is a XMLHttpRequest.
+ *
+ * It works if your JavaScript library sets an X-Requested-With HTTP header.
+ * It is known to work with common JavaScript frameworks:
+ *
+ * @see http://en.wikipedia.org/wiki/List_of_Ajax_frameworks#JavaScript
+ *
+ * @return bool true if the request is an XMLHttpRequest, false otherwise
+ */
+ public function isXmlHttpRequest()
+ {
+ return 'XMLHttpRequest' == $this->headers->get('X-Requested-With');
+ }
+
+ /*
+ * The following methods are derived from code of the Zend Framework (1.10dev - 2010-01-24)
+ *
+ * Code subject to the new BSD license (http://framework.zend.com/license/new-bsd).
+ *
+ * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ */
+
+ protected function prepareRequestUri()
+ {
+ $requestUri = '';
+
+ if ($this->headers->has('X_ORIGINAL_URL')) {
+ // IIS with Microsoft Rewrite Module
+ $requestUri = $this->headers->get('X_ORIGINAL_URL');
+ $this->headers->remove('X_ORIGINAL_URL');
+ $this->server->remove('HTTP_X_ORIGINAL_URL');
+ $this->server->remove('UNENCODED_URL');
+ $this->server->remove('IIS_WasUrlRewritten');
+ } elseif ($this->headers->has('X_REWRITE_URL')) {
+ // IIS with ISAPI_Rewrite
+ $requestUri = $this->headers->get('X_REWRITE_URL');
+ $this->headers->remove('X_REWRITE_URL');
+ } elseif ('1' == $this->server->get('IIS_WasUrlRewritten') && '' != $this->server->get('UNENCODED_URL')) {
+ // IIS7 with URL Rewrite: make sure we get the unencoded URL (double slash problem)
+ $requestUri = $this->server->get('UNENCODED_URL');
+ $this->server->remove('UNENCODED_URL');
+ $this->server->remove('IIS_WasUrlRewritten');
+ } elseif ($this->server->has('REQUEST_URI')) {
+ $requestUri = $this->server->get('REQUEST_URI');
+ // HTTP proxy reqs setup request URI with scheme and host [and port] + the URL path, only use URL path
+ $schemeAndHttpHost = $this->getSchemeAndHttpHost();
+ if (0 === strpos($requestUri, $schemeAndHttpHost)) {
+ $requestUri = substr($requestUri, strlen($schemeAndHttpHost));
+ }
+ } elseif ($this->server->has('ORIG_PATH_INFO')) {
+ // IIS 5.0, PHP as CGI
+ $requestUri = $this->server->get('ORIG_PATH_INFO');
+ if ('' != $this->server->get('QUERY_STRING')) {
+ $requestUri .= '?'.$this->server->get('QUERY_STRING');
+ }
+ $this->server->remove('ORIG_PATH_INFO');
+ }
+
+ // normalize the request URI to ease creating sub-requests from this request
+ $this->server->set('REQUEST_URI', $requestUri);
+
+ return $requestUri;
+ }
+
+ /**
+ * Prepares the base URL.
+ *
+ * @return string
+ */
+ protected function prepareBaseUrl()
+ {
+ $filename = basename($this->server->get('SCRIPT_FILENAME'));
+
+ if (basename($this->server->get('SCRIPT_NAME')) === $filename) {
+ $baseUrl = $this->server->get('SCRIPT_NAME');
+ } elseif (basename($this->server->get('PHP_SELF')) === $filename) {
+ $baseUrl = $this->server->get('PHP_SELF');
+ } elseif (basename($this->server->get('ORIG_SCRIPT_NAME')) === $filename) {
+ $baseUrl = $this->server->get('ORIG_SCRIPT_NAME'); // 1and1 shared hosting compatibility
+ } else {
+ // Backtrack up the script_filename to find the portion matching
+ // php_self
+ $path = $this->server->get('PHP_SELF', '');
+ $file = $this->server->get('SCRIPT_FILENAME', '');
+ $segs = explode('/', trim($file, '/'));
+ $segs = array_reverse($segs);
+ $index = 0;
+ $last = count($segs);
+ $baseUrl = '';
+ do {
+ $seg = $segs[$index];
+ $baseUrl = '/'.$seg.$baseUrl;
+ ++$index;
+ } while ($last > $index && (false !== $pos = strpos($path, $baseUrl)) && 0 != $pos);
+ }
+
+ // Does the baseUrl have anything in common with the request_uri?
+ $requestUri = $this->getRequestUri();
+ if ('' !== $requestUri && '/' !== $requestUri[0]) {
+ $requestUri = '/'.$requestUri;
+ }
+
+ if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) {
+ // full $baseUrl matches
+ return $prefix;
+ }
+
+ if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, rtrim(dirname($baseUrl), '/'.DIRECTORY_SEPARATOR).'/')) {
+ // directory portion of $baseUrl matches
+ return rtrim($prefix, '/'.DIRECTORY_SEPARATOR);
+ }
+
+ $truncatedRequestUri = $requestUri;
+ if (false !== $pos = strpos($requestUri, '?')) {
+ $truncatedRequestUri = substr($requestUri, 0, $pos);
+ }
+
+ $basename = basename($baseUrl);
+ if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri), $basename)) {
+ // no match whatsoever; set it blank
+ return '';
+ }
+
+ // If using mod_rewrite or ISAPI_Rewrite strip the script filename
+ // out of baseUrl. $pos !== 0 makes sure it is not matching a value
+ // from PATH_INFO or QUERY_STRING
+ if (strlen($requestUri) >= strlen($baseUrl) && (false !== $pos = strpos($requestUri, $baseUrl)) && 0 !== $pos) {
+ $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl));
+ }
+
+ return rtrim($baseUrl, '/'.DIRECTORY_SEPARATOR);
+ }
+
+ /**
+ * Prepares the base path.
+ *
+ * @return string base path
+ */
+ protected function prepareBasePath()
+ {
+ $baseUrl = $this->getBaseUrl();
+ if (empty($baseUrl)) {
+ return '';
+ }
+
+ $filename = basename($this->server->get('SCRIPT_FILENAME'));
+ if (basename($baseUrl) === $filename) {
+ $basePath = dirname($baseUrl);
+ } else {
+ $basePath = $baseUrl;
+ }
+
+ if ('\\' === DIRECTORY_SEPARATOR) {
+ $basePath = str_replace('\\', '/', $basePath);
+ }
+
+ return rtrim($basePath, '/');
+ }
+
+ /**
+ * Prepares the path info.
+ *
+ * @return string path info
+ */
+ protected function preparePathInfo()
+ {
+ if (null === ($requestUri = $this->getRequestUri())) {
+ return '/';
+ }
+
+ // Remove the query string from REQUEST_URI
+ if (false !== $pos = strpos($requestUri, '?')) {
+ $requestUri = substr($requestUri, 0, $pos);
+ }
+ if ('' !== $requestUri && '/' !== $requestUri[0]) {
+ $requestUri = '/'.$requestUri;
+ }
+
+ if (null === ($baseUrl = $this->getBaseUrl())) {
+ return $requestUri;
+ }
+
+ $pathInfo = substr($requestUri, strlen($baseUrl));
+ if (false === $pathInfo || '' === $pathInfo) {
+ // If substr() returns false then PATH_INFO is set to an empty string
+ return '/';
+ }
+
+ return (string) $pathInfo;
+ }
+
+ /**
+ * Initializes HTTP request formats.
+ */
+ protected static function initializeFormats()
+ {
+ static::$formats = array(
+ 'html' => array('text/html', 'application/xhtml+xml'),
+ 'txt' => array('text/plain'),
+ 'js' => array('application/javascript', 'application/x-javascript', 'text/javascript'),
+ 'css' => array('text/css'),
+ 'json' => array('application/json', 'application/x-json'),
+ 'jsonld' => array('application/ld+json'),
+ 'xml' => array('text/xml', 'application/xml', 'application/x-xml'),
+ 'rdf' => array('application/rdf+xml'),
+ 'atom' => array('application/atom+xml'),
+ 'rss' => array('application/rss+xml'),
+ 'form' => array('application/x-www-form-urlencoded'),
+ );
+ }
+
+ /**
+ * Sets the default PHP locale.
+ *
+ * @param string $locale
+ */
+ private function setPhpDefaultLocale($locale)
+ {
+ // if either the class Locale doesn't exist, or an exception is thrown when
+ // setting the default locale, the intl module is not installed, and
+ // the call can be ignored:
+ try {
+ if (class_exists('Locale', false)) {
+ \Locale::setDefault($locale);
+ }
+ } catch (\Exception $e) {
+ }
+ }
+
+ /*
+ * Returns the prefix as encoded in the string when the string starts with
+ * the given prefix, false otherwise.
+ *
+ * @param string $string The urlencoded string
+ * @param string $prefix The prefix not encoded
+ *
+ * @return string|false The prefix as it is encoded in $string, or false
+ */
+ private function getUrlencodedPrefix($string, $prefix)
+ {
+ if (0 !== strpos(rawurldecode($string), $prefix)) {
+ return false;
+ }
+
+ $len = strlen($prefix);
+
+ if (preg_match(sprintf('#^(%%[[:xdigit:]]{2}|.){%d}#', $len), $string, $match)) {
+ return $match[0];
+ }
+
+ return false;
+ }
+
+ private static function createRequestFromFactory(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
+ {
+ if (self::$requestFactory) {
+ $request = call_user_func(self::$requestFactory, $query, $request, $attributes, $cookies, $files, $server, $content);
+
+ if (!$request instanceof self) {
+ throw new \LogicException('The Request factory must return an instance of Symfony\Component\HttpFoundation\Request.');
+ }
+
+ return $request;
+ }
+
+ return new static($query, $request, $attributes, $cookies, $files, $server, $content);
+ }
+
+ /**
+ * Indicates whether this request originated from a trusted proxy.
+ *
+ * This can be useful to determine whether or not to trust the
+ * contents of a proxy-specific header.
+ *
+ * @return bool true if the request came from a trusted proxy, false otherwise
+ */
+ public function isFromTrustedProxy()
+ {
+ return self::$trustedProxies && IpUtils::checkIp($this->server->get('REMOTE_ADDR'), self::$trustedProxies);
+ }
+
+ private function getTrustedValues($type, $ip = null)
+ {
+ $clientValues = array();
+ $forwardedValues = array();
+
+ if (self::$trustedHeaders[$type] && $this->headers->has(self::$trustedHeaders[$type])) {
+ foreach (explode(',', $this->headers->get(self::$trustedHeaders[$type])) as $v) {
+ $clientValues[] = (self::HEADER_CLIENT_PORT === $type ? '0.0.0.0:' : '').trim($v);
+ }
+ }
+
+ if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) {
+ $forwardedValues = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]);
+ $forwardedValues = preg_match_all(sprintf('{(?:%s)=(?:"?\[?)([a-zA-Z0-9\.:_\-/]*+)}', self::$forwardedParams[$type]), $forwardedValues, $matches) ? $matches[1] : array();
+ }
+
+ if (null !== $ip) {
+ $clientValues = $this->normalizeAndFilterClientIps($clientValues, $ip);
+ $forwardedValues = $this->normalizeAndFilterClientIps($forwardedValues, $ip);
+ }
+
+ if ($forwardedValues === $clientValues || !$clientValues) {
+ return $forwardedValues;
+ }
+
+ if (!$forwardedValues) {
+ return $clientValues;
+ }
+
+ if (!$this->isForwardedValid) {
+ return null !== $ip ? array('0.0.0.0', $ip) : array();
+ }
+ $this->isForwardedValid = false;
+
+ throw new ConflictingHeadersException(sprintf('The request has both a trusted "%s" header and a trusted "%s" header, conflicting with each other. You should either configure your proxy to remove one of them, or configure your project to distrust the offending one.', self::$trustedHeaders[self::HEADER_FORWARDED], self::$trustedHeaders[$type]));
+ }
+
+ private function normalizeAndFilterClientIps(array $clientIps, $ip)
+ {
+ if (!$clientIps) {
+ return array();
+ }
+ $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
+ $firstTrustedIp = null;
+
+ foreach ($clientIps as $key => $clientIp) {
+ // Remove port (unfortunately, it does happen)
+ if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) {
+ $clientIps[$key] = $clientIp = $match[1];
+ }
+
+ if (!filter_var($clientIp, FILTER_VALIDATE_IP)) {
+ unset($clientIps[$key]);
+
+ continue;
+ }
+
+ if (IpUtils::checkIp($clientIp, self::$trustedProxies)) {
+ unset($clientIps[$key]);
+
+ // Fallback to this when the client IP falls into the range of trusted proxies
+ if (null === $firstTrustedIp) {
+ $firstTrustedIp = $clientIp;
+ }
+ }
+ }
+
+ // Now the IP chain contains only untrusted proxies and the client IP
+ return $clientIps ? array_reverse($clientIps) : array($firstTrustedIp);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/RequestMatcher.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/RequestMatcher.php
new file mode 100644
index 0000000..076d077
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/RequestMatcher.php
@@ -0,0 +1,178 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * RequestMatcher compares a pre-defined set of checks against a Request instance.
+ *
+ * @author Fabien Potencier
+ */
+class RequestMatcher implements RequestMatcherInterface
+{
+ /**
+ * @var string|null
+ */
+ private $path;
+
+ /**
+ * @var string|null
+ */
+ private $host;
+
+ /**
+ * @var string[]
+ */
+ private $methods = array();
+
+ /**
+ * @var string[]
+ */
+ private $ips = array();
+
+ /**
+ * @var array
+ */
+ private $attributes = array();
+
+ /**
+ * @var string[]
+ */
+ private $schemes = array();
+
+ /**
+ * @param string|null $path
+ * @param string|null $host
+ * @param string|string[]|null $methods
+ * @param string|string[]|null $ips
+ * @param array $attributes
+ * @param string|string[]|null $schemes
+ */
+ public function __construct($path = null, $host = null, $methods = null, $ips = null, array $attributes = array(), $schemes = null)
+ {
+ $this->matchPath($path);
+ $this->matchHost($host);
+ $this->matchMethod($methods);
+ $this->matchIps($ips);
+ $this->matchScheme($schemes);
+
+ foreach ($attributes as $k => $v) {
+ $this->matchAttribute($k, $v);
+ }
+ }
+
+ /**
+ * Adds a check for the HTTP scheme.
+ *
+ * @param string|string[]|null $scheme An HTTP scheme or an array of HTTP schemes
+ */
+ public function matchScheme($scheme)
+ {
+ $this->schemes = null !== $scheme ? array_map('strtolower', (array) $scheme) : array();
+ }
+
+ /**
+ * Adds a check for the URL host name.
+ *
+ * @param string|null $regexp A Regexp
+ */
+ public function matchHost($regexp)
+ {
+ $this->host = $regexp;
+ }
+
+ /**
+ * Adds a check for the URL path info.
+ *
+ * @param string|null $regexp A Regexp
+ */
+ public function matchPath($regexp)
+ {
+ $this->path = $regexp;
+ }
+
+ /**
+ * Adds a check for the client IP.
+ *
+ * @param string $ip A specific IP address or a range specified using IP/netmask like 192.168.1.0/24
+ */
+ public function matchIp($ip)
+ {
+ $this->matchIps($ip);
+ }
+
+ /**
+ * Adds a check for the client IP.
+ *
+ * @param string|string[]|null $ips A specific IP address or a range specified using IP/netmask like 192.168.1.0/24
+ */
+ public function matchIps($ips)
+ {
+ $this->ips = null !== $ips ? (array) $ips : array();
+ }
+
+ /**
+ * Adds a check for the HTTP method.
+ *
+ * @param string|string[]|null $method An HTTP method or an array of HTTP methods
+ */
+ public function matchMethod($method)
+ {
+ $this->methods = null !== $method ? array_map('strtoupper', (array) $method) : array();
+ }
+
+ /**
+ * Adds a check for request attribute.
+ *
+ * @param string $key The request attribute name
+ * @param string $regexp A Regexp
+ */
+ public function matchAttribute($key, $regexp)
+ {
+ $this->attributes[$key] = $regexp;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function matches(Request $request)
+ {
+ if ($this->schemes && !in_array($request->getScheme(), $this->schemes, true)) {
+ return false;
+ }
+
+ if ($this->methods && !in_array($request->getMethod(), $this->methods, true)) {
+ return false;
+ }
+
+ foreach ($this->attributes as $key => $pattern) {
+ if (!preg_match('{'.$pattern.'}', $request->attributes->get($key))) {
+ return false;
+ }
+ }
+
+ if (null !== $this->path && !preg_match('{'.$this->path.'}', rawurldecode($request->getPathInfo()))) {
+ return false;
+ }
+
+ if (null !== $this->host && !preg_match('{'.$this->host.'}i', $request->getHost())) {
+ return false;
+ }
+
+ if (IpUtils::checkIp($request->getClientIp(), $this->ips)) {
+ return true;
+ }
+
+ // Note to future implementors: add additional checks above the
+ // foreach above or else your check might not be run!
+ return 0 === count($this->ips);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/RequestMatcherInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/RequestMatcherInterface.php
new file mode 100644
index 0000000..c26db3e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/RequestMatcherInterface.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * RequestMatcherInterface is an interface for strategies to match a Request.
+ *
+ * @author Fabien Potencier
+ */
+interface RequestMatcherInterface
+{
+ /**
+ * Decides whether the rule(s) implemented by the strategy matches the supplied request.
+ *
+ * @return bool true if the request matches, false otherwise
+ */
+ public function matches(Request $request);
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/RequestStack.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/RequestStack.php
new file mode 100644
index 0000000..3d9cfd0
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/RequestStack.php
@@ -0,0 +1,103 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * Request stack that controls the lifecycle of requests.
+ *
+ * @author Benjamin Eberlei
+ */
+class RequestStack
+{
+ /**
+ * @var Request[]
+ */
+ private $requests = array();
+
+ /**
+ * Pushes a Request on the stack.
+ *
+ * This method should generally not be called directly as the stack
+ * management should be taken care of by the application itself.
+ */
+ public function push(Request $request)
+ {
+ $this->requests[] = $request;
+ }
+
+ /**
+ * Pops the current request from the stack.
+ *
+ * This operation lets the current request go out of scope.
+ *
+ * This method should generally not be called directly as the stack
+ * management should be taken care of by the application itself.
+ *
+ * @return Request|null
+ */
+ public function pop()
+ {
+ if (!$this->requests) {
+ return;
+ }
+
+ return array_pop($this->requests);
+ }
+
+ /**
+ * @return Request|null
+ */
+ public function getCurrentRequest()
+ {
+ return end($this->requests) ?: null;
+ }
+
+ /**
+ * Gets the master Request.
+ *
+ * Be warned that making your code aware of the master request
+ * might make it un-compatible with other features of your framework
+ * like ESI support.
+ *
+ * @return Request|null
+ */
+ public function getMasterRequest()
+ {
+ if (!$this->requests) {
+ return;
+ }
+
+ return $this->requests[0];
+ }
+
+ /**
+ * Returns the parent request of the current.
+ *
+ * Be warned that making your code aware of the parent request
+ * might make it un-compatible with other features of your framework
+ * like ESI support.
+ *
+ * If current Request is the master request, it returns null.
+ *
+ * @return Request|null
+ */
+ public function getParentRequest()
+ {
+ $pos = count($this->requests) - 2;
+
+ if (!isset($this->requests[$pos])) {
+ return;
+ }
+
+ return $this->requests[$pos];
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Response.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Response.php
new file mode 100644
index 0000000..6f8a623
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Response.php
@@ -0,0 +1,1298 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * Response represents an HTTP response.
+ *
+ * @author Fabien Potencier
+ */
+class Response
+{
+ const HTTP_CONTINUE = 100;
+ const HTTP_SWITCHING_PROTOCOLS = 101;
+ const HTTP_PROCESSING = 102; // RFC2518
+ const HTTP_OK = 200;
+ const HTTP_CREATED = 201;
+ const HTTP_ACCEPTED = 202;
+ const HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
+ const HTTP_NO_CONTENT = 204;
+ const HTTP_RESET_CONTENT = 205;
+ const HTTP_PARTIAL_CONTENT = 206;
+ const HTTP_MULTI_STATUS = 207; // RFC4918
+ const HTTP_ALREADY_REPORTED = 208; // RFC5842
+ const HTTP_IM_USED = 226; // RFC3229
+ const HTTP_MULTIPLE_CHOICES = 300;
+ const HTTP_MOVED_PERMANENTLY = 301;
+ const HTTP_FOUND = 302;
+ const HTTP_SEE_OTHER = 303;
+ const HTTP_NOT_MODIFIED = 304;
+ const HTTP_USE_PROXY = 305;
+ const HTTP_RESERVED = 306;
+ const HTTP_TEMPORARY_REDIRECT = 307;
+ const HTTP_PERMANENTLY_REDIRECT = 308; // RFC7238
+ const HTTP_BAD_REQUEST = 400;
+ const HTTP_UNAUTHORIZED = 401;
+ const HTTP_PAYMENT_REQUIRED = 402;
+ const HTTP_FORBIDDEN = 403;
+ const HTTP_NOT_FOUND = 404;
+ const HTTP_METHOD_NOT_ALLOWED = 405;
+ const HTTP_NOT_ACCEPTABLE = 406;
+ const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
+ const HTTP_REQUEST_TIMEOUT = 408;
+ const HTTP_CONFLICT = 409;
+ const HTTP_GONE = 410;
+ const HTTP_LENGTH_REQUIRED = 411;
+ const HTTP_PRECONDITION_FAILED = 412;
+ const HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
+ const HTTP_REQUEST_URI_TOO_LONG = 414;
+ const HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
+ const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
+ const HTTP_EXPECTATION_FAILED = 417;
+ const HTTP_I_AM_A_TEAPOT = 418; // RFC2324
+ const HTTP_MISDIRECTED_REQUEST = 421; // RFC7540
+ const HTTP_UNPROCESSABLE_ENTITY = 422; // RFC4918
+ const HTTP_LOCKED = 423; // RFC4918
+ const HTTP_FAILED_DEPENDENCY = 424; // RFC4918
+ const HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL = 425; // RFC2817
+ const HTTP_UPGRADE_REQUIRED = 426; // RFC2817
+ const HTTP_PRECONDITION_REQUIRED = 428; // RFC6585
+ const HTTP_TOO_MANY_REQUESTS = 429; // RFC6585
+ const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; // RFC6585
+ const HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451;
+ const HTTP_INTERNAL_SERVER_ERROR = 500;
+ const HTTP_NOT_IMPLEMENTED = 501;
+ const HTTP_BAD_GATEWAY = 502;
+ const HTTP_SERVICE_UNAVAILABLE = 503;
+ const HTTP_GATEWAY_TIMEOUT = 504;
+ const HTTP_VERSION_NOT_SUPPORTED = 505;
+ const HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL = 506; // RFC2295
+ const HTTP_INSUFFICIENT_STORAGE = 507; // RFC4918
+ const HTTP_LOOP_DETECTED = 508; // RFC5842
+ const HTTP_NOT_EXTENDED = 510; // RFC2774
+ const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; // RFC6585
+
+ /**
+ * @var \Symfony\Component\HttpFoundation\ResponseHeaderBag
+ */
+ public $headers;
+
+ /**
+ * @var string
+ */
+ protected $content;
+
+ /**
+ * @var string
+ */
+ protected $version;
+
+ /**
+ * @var int
+ */
+ protected $statusCode;
+
+ /**
+ * @var string
+ */
+ protected $statusText;
+
+ /**
+ * @var string
+ */
+ protected $charset;
+
+ /**
+ * Status codes translation table.
+ *
+ * The list of codes is complete according to the
+ * {@link http://www.iana.org/assignments/http-status-codes/ Hypertext Transfer Protocol (HTTP) Status Code Registry}
+ * (last updated 2016-03-01).
+ *
+ * Unless otherwise noted, the status code is defined in RFC2616.
+ *
+ * @var array
+ */
+ public static $statusTexts = array(
+ 100 => 'Continue',
+ 101 => 'Switching Protocols',
+ 102 => 'Processing', // RFC2518
+ 103 => 'Early Hints',
+ 200 => 'OK',
+ 201 => 'Created',
+ 202 => 'Accepted',
+ 203 => 'Non-Authoritative Information',
+ 204 => 'No Content',
+ 205 => 'Reset Content',
+ 206 => 'Partial Content',
+ 207 => 'Multi-Status', // RFC4918
+ 208 => 'Already Reported', // RFC5842
+ 226 => 'IM Used', // RFC3229
+ 300 => 'Multiple Choices',
+ 301 => 'Moved Permanently',
+ 302 => 'Found',
+ 303 => 'See Other',
+ 304 => 'Not Modified',
+ 305 => 'Use Proxy',
+ 307 => 'Temporary Redirect',
+ 308 => 'Permanent Redirect', // RFC7238
+ 400 => 'Bad Request',
+ 401 => 'Unauthorized',
+ 402 => 'Payment Required',
+ 403 => 'Forbidden',
+ 404 => 'Not Found',
+ 405 => 'Method Not Allowed',
+ 406 => 'Not Acceptable',
+ 407 => 'Proxy Authentication Required',
+ 408 => 'Request Timeout',
+ 409 => 'Conflict',
+ 410 => 'Gone',
+ 411 => 'Length Required',
+ 412 => 'Precondition Failed',
+ 413 => 'Payload Too Large',
+ 414 => 'URI Too Long',
+ 415 => 'Unsupported Media Type',
+ 416 => 'Range Not Satisfiable',
+ 417 => 'Expectation Failed',
+ 418 => 'I\'m a teapot', // RFC2324
+ 421 => 'Misdirected Request', // RFC7540
+ 422 => 'Unprocessable Entity', // RFC4918
+ 423 => 'Locked', // RFC4918
+ 424 => 'Failed Dependency', // RFC4918
+ 425 => 'Reserved for WebDAV advanced collections expired proposal', // RFC2817
+ 426 => 'Upgrade Required', // RFC2817
+ 428 => 'Precondition Required', // RFC6585
+ 429 => 'Too Many Requests', // RFC6585
+ 431 => 'Request Header Fields Too Large', // RFC6585
+ 451 => 'Unavailable For Legal Reasons', // RFC7725
+ 500 => 'Internal Server Error',
+ 501 => 'Not Implemented',
+ 502 => 'Bad Gateway',
+ 503 => 'Service Unavailable',
+ 504 => 'Gateway Timeout',
+ 505 => 'HTTP Version Not Supported',
+ 506 => 'Variant Also Negotiates', // RFC2295
+ 507 => 'Insufficient Storage', // RFC4918
+ 508 => 'Loop Detected', // RFC5842
+ 510 => 'Not Extended', // RFC2774
+ 511 => 'Network Authentication Required', // RFC6585
+ );
+
+ /**
+ * @param mixed $content The response content, see setContent()
+ * @param int $status The response status code
+ * @param array $headers An array of response headers
+ *
+ * @throws \InvalidArgumentException When the HTTP status code is not valid
+ */
+ public function __construct($content = '', $status = 200, $headers = array())
+ {
+ $this->headers = new ResponseHeaderBag($headers);
+ $this->setContent($content);
+ $this->setStatusCode($status);
+ $this->setProtocolVersion('1.0');
+ }
+
+ /**
+ * Factory method for chainability.
+ *
+ * Example:
+ *
+ * return Response::create($body, 200)
+ * ->setSharedMaxAge(300);
+ *
+ * @param mixed $content The response content, see setContent()
+ * @param int $status The response status code
+ * @param array $headers An array of response headers
+ *
+ * @return static
+ */
+ public static function create($content = '', $status = 200, $headers = array())
+ {
+ return new static($content, $status, $headers);
+ }
+
+ /**
+ * Returns the Response as an HTTP string.
+ *
+ * The string representation of the Response is the same as the
+ * one that will be sent to the client only if the prepare() method
+ * has been called before.
+ *
+ * @return string The Response as an HTTP string
+ *
+ * @see prepare()
+ */
+ public function __toString()
+ {
+ return
+ sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)."\r\n".
+ $this->headers."\r\n".
+ $this->getContent();
+ }
+
+ /**
+ * Clones the current Response instance.
+ */
+ public function __clone()
+ {
+ $this->headers = clone $this->headers;
+ }
+
+ /**
+ * Prepares the Response before it is sent to the client.
+ *
+ * This method tweaks the Response to ensure that it is
+ * compliant with RFC 2616. Most of the changes are based on
+ * the Request that is "associated" with this Response.
+ *
+ * @return $this
+ */
+ public function prepare(Request $request)
+ {
+ $headers = $this->headers;
+
+ if ($this->isInformational() || $this->isEmpty()) {
+ $this->setContent(null);
+ $headers->remove('Content-Type');
+ $headers->remove('Content-Length');
+ } else {
+ // Content-type based on the Request
+ if (!$headers->has('Content-Type')) {
+ $format = $request->getRequestFormat();
+ if (null !== $format && $mimeType = $request->getMimeType($format)) {
+ $headers->set('Content-Type', $mimeType);
+ }
+ }
+
+ // Fix Content-Type
+ $charset = $this->charset ?: 'UTF-8';
+ if (!$headers->has('Content-Type')) {
+ $headers->set('Content-Type', 'text/html; charset='.$charset);
+ } elseif (0 === stripos($headers->get('Content-Type'), 'text/') && false === stripos($headers->get('Content-Type'), 'charset')) {
+ // add the charset
+ $headers->set('Content-Type', $headers->get('Content-Type').'; charset='.$charset);
+ }
+
+ // Fix Content-Length
+ if ($headers->has('Transfer-Encoding')) {
+ $headers->remove('Content-Length');
+ }
+
+ if ($request->isMethod('HEAD')) {
+ // cf. RFC2616 14.13
+ $length = $headers->get('Content-Length');
+ $this->setContent(null);
+ if ($length) {
+ $headers->set('Content-Length', $length);
+ }
+ }
+ }
+
+ // Fix protocol
+ if ('HTTP/1.0' != $request->server->get('SERVER_PROTOCOL')) {
+ $this->setProtocolVersion('1.1');
+ }
+
+ // Check if we need to send extra expire info headers
+ if ('1.0' == $this->getProtocolVersion() && false !== strpos($this->headers->get('Cache-Control'), 'no-cache')) {
+ $this->headers->set('pragma', 'no-cache');
+ $this->headers->set('expires', -1);
+ }
+
+ $this->ensureIEOverSSLCompatibility($request);
+
+ return $this;
+ }
+
+ /**
+ * Sends HTTP headers.
+ *
+ * @return $this
+ */
+ public function sendHeaders()
+ {
+ // headers have already been sent by the developer
+ if (headers_sent()) {
+ return $this;
+ }
+
+ // headers
+ foreach ($this->headers->allPreserveCaseWithoutCookies() as $name => $values) {
+ foreach ($values as $value) {
+ header($name.': '.$value, false, $this->statusCode);
+ }
+ }
+
+ // status
+ header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode);
+
+ // cookies
+ foreach ($this->headers->getCookies() as $cookie) {
+ if ($cookie->isRaw()) {
+ setrawcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly());
+ } else {
+ setcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly());
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Sends content for the current web response.
+ *
+ * @return $this
+ */
+ public function sendContent()
+ {
+ echo $this->content;
+
+ return $this;
+ }
+
+ /**
+ * Sends HTTP headers and content.
+ *
+ * @return $this
+ */
+ public function send()
+ {
+ $this->sendHeaders();
+ $this->sendContent();
+
+ if (function_exists('fastcgi_finish_request')) {
+ fastcgi_finish_request();
+ } elseif (!\in_array(PHP_SAPI, array('cli', 'phpdbg'), true)) {
+ static::closeOutputBuffers(0, true);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Sets the response content.
+ *
+ * Valid types are strings, numbers, null, and objects that implement a __toString() method.
+ *
+ * @param mixed $content Content that can be cast to string
+ *
+ * @return $this
+ *
+ * @throws \UnexpectedValueException
+ */
+ public function setContent($content)
+ {
+ if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable(array($content, '__toString'))) {
+ throw new \UnexpectedValueException(sprintf('The Response content must be a string or object implementing __toString(), "%s" given.', gettype($content)));
+ }
+
+ $this->content = (string) $content;
+
+ return $this;
+ }
+
+ /**
+ * Gets the current response content.
+ *
+ * @return string Content
+ */
+ public function getContent()
+ {
+ return $this->content;
+ }
+
+ /**
+ * Sets the HTTP protocol version (1.0 or 1.1).
+ *
+ * @param string $version The HTTP protocol version
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setProtocolVersion($version)
+ {
+ $this->version = $version;
+
+ return $this;
+ }
+
+ /**
+ * Gets the HTTP protocol version.
+ *
+ * @return string The HTTP protocol version
+ *
+ * @final since version 3.2
+ */
+ public function getProtocolVersion()
+ {
+ return $this->version;
+ }
+
+ /**
+ * Sets the response status code.
+ *
+ * If the status text is null it will be automatically populated for the known
+ * status codes and left empty otherwise.
+ *
+ * @param int $code HTTP status code
+ * @param mixed $text HTTP status text
+ *
+ * @return $this
+ *
+ * @throws \InvalidArgumentException When the HTTP status code is not valid
+ *
+ * @final since version 3.2
+ */
+ public function setStatusCode($code, $text = null)
+ {
+ $this->statusCode = $code = (int) $code;
+ if ($this->isInvalid()) {
+ throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code));
+ }
+
+ if (null === $text) {
+ $this->statusText = isset(self::$statusTexts[$code]) ? self::$statusTexts[$code] : 'unknown status';
+
+ return $this;
+ }
+
+ if (false === $text) {
+ $this->statusText = '';
+
+ return $this;
+ }
+
+ $this->statusText = $text;
+
+ return $this;
+ }
+
+ /**
+ * Retrieves the status code for the current web response.
+ *
+ * @return int Status code
+ *
+ * @final since version 3.2
+ */
+ public function getStatusCode()
+ {
+ return $this->statusCode;
+ }
+
+ /**
+ * Sets the response charset.
+ *
+ * @param string $charset Character set
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setCharset($charset)
+ {
+ $this->charset = $charset;
+
+ return $this;
+ }
+
+ /**
+ * Retrieves the response charset.
+ *
+ * @return string Character set
+ *
+ * @final since version 3.2
+ */
+ public function getCharset()
+ {
+ return $this->charset;
+ }
+
+ /**
+ * Returns true if the response is worth caching under any circumstance.
+ *
+ * Responses marked "private" with an explicit Cache-Control directive are
+ * considered uncacheable.
+ *
+ * Responses with neither a freshness lifetime (Expires, max-age) nor cache
+ * validator (Last-Modified, ETag) are considered uncacheable.
+ *
+ * @return bool true if the response is worth caching, false otherwise
+ *
+ * @final since version 3.3
+ */
+ public function isCacheable()
+ {
+ if (!in_array($this->statusCode, array(200, 203, 300, 301, 302, 404, 410))) {
+ return false;
+ }
+
+ if ($this->headers->hasCacheControlDirective('no-store') || $this->headers->getCacheControlDirective('private')) {
+ return false;
+ }
+
+ return $this->isValidateable() || $this->isFresh();
+ }
+
+ /**
+ * Returns true if the response is "fresh".
+ *
+ * Fresh responses may be served from cache without any interaction with the
+ * origin. A response is considered fresh when it includes a Cache-Control/max-age
+ * indicator or Expires header and the calculated age is less than the freshness lifetime.
+ *
+ * @return bool true if the response is fresh, false otherwise
+ *
+ * @final since version 3.3
+ */
+ public function isFresh()
+ {
+ return $this->getTtl() > 0;
+ }
+
+ /**
+ * Returns true if the response includes headers that can be used to validate
+ * the response with the origin server using a conditional GET request.
+ *
+ * @return bool true if the response is validateable, false otherwise
+ *
+ * @final since version 3.3
+ */
+ public function isValidateable()
+ {
+ return $this->headers->has('Last-Modified') || $this->headers->has('ETag');
+ }
+
+ /**
+ * Marks the response as "private".
+ *
+ * It makes the response ineligible for serving other clients.
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setPrivate()
+ {
+ $this->headers->removeCacheControlDirective('public');
+ $this->headers->addCacheControlDirective('private');
+
+ return $this;
+ }
+
+ /**
+ * Marks the response as "public".
+ *
+ * It makes the response eligible for serving other clients.
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setPublic()
+ {
+ $this->headers->addCacheControlDirective('public');
+ $this->headers->removeCacheControlDirective('private');
+
+ return $this;
+ }
+
+ /**
+ * Marks the response as "immutable".
+ *
+ * @param bool $immutable enables or disables the immutable directive
+ *
+ * @return $this
+ *
+ * @final
+ */
+ public function setImmutable($immutable = true)
+ {
+ if ($immutable) {
+ $this->headers->addCacheControlDirective('immutable');
+ } else {
+ $this->headers->removeCacheControlDirective('immutable');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns true if the response is marked as "immutable".
+ *
+ * @return bool returns true if the response is marked as "immutable"; otherwise false
+ *
+ * @final
+ */
+ public function isImmutable()
+ {
+ return $this->headers->hasCacheControlDirective('immutable');
+ }
+
+ /**
+ * Returns true if the response must be revalidated by caches.
+ *
+ * This method indicates that the response must not be served stale by a
+ * cache in any circumstance without first revalidating with the origin.
+ * When present, the TTL of the response should not be overridden to be
+ * greater than the value provided by the origin.
+ *
+ * @return bool true if the response must be revalidated by a cache, false otherwise
+ *
+ * @final since version 3.3
+ */
+ public function mustRevalidate()
+ {
+ return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->hasCacheControlDirective('proxy-revalidate');
+ }
+
+ /**
+ * Returns the Date header as a DateTime instance.
+ *
+ * @return \DateTime A \DateTime instance
+ *
+ * @throws \RuntimeException When the header is not parseable
+ *
+ * @final since version 3.2
+ */
+ public function getDate()
+ {
+ return $this->headers->getDate('Date');
+ }
+
+ /**
+ * Sets the Date header.
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setDate(\DateTime $date)
+ {
+ $date->setTimezone(new \DateTimeZone('UTC'));
+ $this->headers->set('Date', $date->format('D, d M Y H:i:s').' GMT');
+
+ return $this;
+ }
+
+ /**
+ * Returns the age of the response.
+ *
+ * @return int The age of the response in seconds
+ *
+ * @final since version 3.2
+ */
+ public function getAge()
+ {
+ if (null !== $age = $this->headers->get('Age')) {
+ return (int) $age;
+ }
+
+ return max(time() - $this->getDate()->format('U'), 0);
+ }
+
+ /**
+ * Marks the response stale by setting the Age header to be equal to the maximum age of the response.
+ *
+ * @return $this
+ */
+ public function expire()
+ {
+ if ($this->isFresh()) {
+ $this->headers->set('Age', $this->getMaxAge());
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the value of the Expires header as a DateTime instance.
+ *
+ * @return \DateTime|null A DateTime instance or null if the header does not exist
+ *
+ * @final since version 3.2
+ */
+ public function getExpires()
+ {
+ try {
+ return $this->headers->getDate('Expires');
+ } catch (\RuntimeException $e) {
+ // according to RFC 2616 invalid date formats (e.g. "0" and "-1") must be treated as in the past
+ return \DateTime::createFromFormat(DATE_RFC2822, 'Sat, 01 Jan 00 00:00:00 +0000');
+ }
+ }
+
+ /**
+ * Sets the Expires HTTP header with a DateTime instance.
+ *
+ * Passing null as value will remove the header.
+ *
+ * @param \DateTime|null $date A \DateTime instance or null to remove the header
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setExpires(\DateTime $date = null)
+ {
+ if (null === $date) {
+ $this->headers->remove('Expires');
+ } else {
+ $date = clone $date;
+ $date->setTimezone(new \DateTimeZone('UTC'));
+ $this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the number of seconds after the time specified in the response's Date
+ * header when the response should no longer be considered fresh.
+ *
+ * First, it checks for a s-maxage directive, then a max-age directive, and then it falls
+ * back on an expires header. It returns null when no maximum age can be established.
+ *
+ * @return int|null Number of seconds
+ *
+ * @final since version 3.2
+ */
+ public function getMaxAge()
+ {
+ if ($this->headers->hasCacheControlDirective('s-maxage')) {
+ return (int) $this->headers->getCacheControlDirective('s-maxage');
+ }
+
+ if ($this->headers->hasCacheControlDirective('max-age')) {
+ return (int) $this->headers->getCacheControlDirective('max-age');
+ }
+
+ if (null !== $this->getExpires()) {
+ return $this->getExpires()->format('U') - $this->getDate()->format('U');
+ }
+ }
+
+ /**
+ * Sets the number of seconds after which the response should no longer be considered fresh.
+ *
+ * This methods sets the Cache-Control max-age directive.
+ *
+ * @param int $value Number of seconds
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setMaxAge($value)
+ {
+ $this->headers->addCacheControlDirective('max-age', $value);
+
+ return $this;
+ }
+
+ /**
+ * Sets the number of seconds after which the response should no longer be considered fresh by shared caches.
+ *
+ * This methods sets the Cache-Control s-maxage directive.
+ *
+ * @param int $value Number of seconds
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setSharedMaxAge($value)
+ {
+ $this->setPublic();
+ $this->headers->addCacheControlDirective('s-maxage', $value);
+
+ return $this;
+ }
+
+ /**
+ * Returns the response's time-to-live in seconds.
+ *
+ * It returns null when no freshness information is present in the response.
+ *
+ * When the responses TTL is <= 0, the response may not be served from cache without first
+ * revalidating with the origin.
+ *
+ * @return int|null The TTL in seconds
+ *
+ * @final since version 3.2
+ */
+ public function getTtl()
+ {
+ if (null !== $maxAge = $this->getMaxAge()) {
+ return $maxAge - $this->getAge();
+ }
+ }
+
+ /**
+ * Sets the response's time-to-live for shared caches.
+ *
+ * This method adjusts the Cache-Control/s-maxage directive.
+ *
+ * @param int $seconds Number of seconds
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setTtl($seconds)
+ {
+ $this->setSharedMaxAge($this->getAge() + $seconds);
+
+ return $this;
+ }
+
+ /**
+ * Sets the response's time-to-live for private/client caches.
+ *
+ * This method adjusts the Cache-Control/max-age directive.
+ *
+ * @param int $seconds Number of seconds
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setClientTtl($seconds)
+ {
+ $this->setMaxAge($this->getAge() + $seconds);
+
+ return $this;
+ }
+
+ /**
+ * Returns the Last-Modified HTTP header as a DateTime instance.
+ *
+ * @return \DateTime|null A DateTime instance or null if the header does not exist
+ *
+ * @throws \RuntimeException When the HTTP header is not parseable
+ *
+ * @final since version 3.2
+ */
+ public function getLastModified()
+ {
+ return $this->headers->getDate('Last-Modified');
+ }
+
+ /**
+ * Sets the Last-Modified HTTP header with a DateTime instance.
+ *
+ * Passing null as value will remove the header.
+ *
+ * @param \DateTime|null $date A \DateTime instance or null to remove the header
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setLastModified(\DateTime $date = null)
+ {
+ if (null === $date) {
+ $this->headers->remove('Last-Modified');
+ } else {
+ $date = clone $date;
+ $date->setTimezone(new \DateTimeZone('UTC'));
+ $this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the literal value of the ETag HTTP header.
+ *
+ * @return string|null The ETag HTTP header or null if it does not exist
+ *
+ * @final since version 3.2
+ */
+ public function getEtag()
+ {
+ return $this->headers->get('ETag');
+ }
+
+ /**
+ * Sets the ETag value.
+ *
+ * @param string|null $etag The ETag unique identifier or null to remove the header
+ * @param bool $weak Whether you want a weak ETag or not
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setEtag($etag = null, $weak = false)
+ {
+ if (null === $etag) {
+ $this->headers->remove('Etag');
+ } else {
+ if (0 !== strpos($etag, '"')) {
+ $etag = '"'.$etag.'"';
+ }
+
+ $this->headers->set('ETag', (true === $weak ? 'W/' : '').$etag);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Sets the response's cache headers (validation and/or expiration).
+ *
+ * Available options are: etag, last_modified, max_age, s_maxage, private, public and immutable.
+ *
+ * @param array $options An array of cache options
+ *
+ * @return $this
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @final since version 3.3
+ */
+ public function setCache(array $options)
+ {
+ if ($diff = array_diff(array_keys($options), array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public', 'immutable'))) {
+ throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', array_values($diff))));
+ }
+
+ if (isset($options['etag'])) {
+ $this->setEtag($options['etag']);
+ }
+
+ if (isset($options['last_modified'])) {
+ $this->setLastModified($options['last_modified']);
+ }
+
+ if (isset($options['max_age'])) {
+ $this->setMaxAge($options['max_age']);
+ }
+
+ if (isset($options['s_maxage'])) {
+ $this->setSharedMaxAge($options['s_maxage']);
+ }
+
+ if (isset($options['public'])) {
+ if ($options['public']) {
+ $this->setPublic();
+ } else {
+ $this->setPrivate();
+ }
+ }
+
+ if (isset($options['private'])) {
+ if ($options['private']) {
+ $this->setPrivate();
+ } else {
+ $this->setPublic();
+ }
+ }
+
+ if (isset($options['immutable'])) {
+ $this->setImmutable((bool) $options['immutable']);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Modifies the response so that it conforms to the rules defined for a 304 status code.
+ *
+ * This sets the status, removes the body, and discards any headers
+ * that MUST NOT be included in 304 responses.
+ *
+ * @return $this
+ *
+ * @see http://tools.ietf.org/html/rfc2616#section-10.3.5
+ *
+ * @final since version 3.3
+ */
+ public function setNotModified()
+ {
+ $this->setStatusCode(304);
+ $this->setContent(null);
+
+ // remove headers that MUST NOT be included with 304 Not Modified responses
+ foreach (array('Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified') as $header) {
+ $this->headers->remove($header);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns true if the response includes a Vary header.
+ *
+ * @return bool true if the response includes a Vary header, false otherwise
+ *
+ * @final since version 3.2
+ */
+ public function hasVary()
+ {
+ return null !== $this->headers->get('Vary');
+ }
+
+ /**
+ * Returns an array of header names given in the Vary header.
+ *
+ * @return array An array of Vary names
+ *
+ * @final since version 3.2
+ */
+ public function getVary()
+ {
+ if (!$vary = $this->headers->get('Vary', null, false)) {
+ return array();
+ }
+
+ $ret = array();
+ foreach ($vary as $item) {
+ $ret = array_merge($ret, preg_split('/[\s,]+/', $item));
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Sets the Vary header.
+ *
+ * @param string|array $headers
+ * @param bool $replace Whether to replace the actual value or not (true by default)
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setVary($headers, $replace = true)
+ {
+ $this->headers->set('Vary', $headers, $replace);
+
+ return $this;
+ }
+
+ /**
+ * Determines if the Response validators (ETag, Last-Modified) match
+ * a conditional value specified in the Request.
+ *
+ * If the Response is not modified, it sets the status code to 304 and
+ * removes the actual content by calling the setNotModified() method.
+ *
+ * @return bool true if the Response validators match the Request, false otherwise
+ *
+ * @final since version 3.3
+ */
+ public function isNotModified(Request $request)
+ {
+ if (!$request->isMethodCacheable()) {
+ return false;
+ }
+
+ $notModified = false;
+ $lastModified = $this->headers->get('Last-Modified');
+ $modifiedSince = $request->headers->get('If-Modified-Since');
+
+ if ($etags = $request->getETags()) {
+ $notModified = in_array($this->getEtag(), $etags) || in_array('*', $etags);
+ }
+
+ if ($modifiedSince && $lastModified) {
+ $notModified = strtotime($modifiedSince) >= strtotime($lastModified) && (!$etags || $notModified);
+ }
+
+ if ($notModified) {
+ $this->setNotModified();
+ }
+
+ return $notModified;
+ }
+
+ /**
+ * Is response invalid?
+ *
+ * @return bool
+ *
+ * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+ *
+ * @final since version 3.2
+ */
+ public function isInvalid()
+ {
+ return $this->statusCode < 100 || $this->statusCode >= 600;
+ }
+
+ /**
+ * Is response informative?
+ *
+ * @return bool
+ *
+ * @final since version 3.3
+ */
+ public function isInformational()
+ {
+ return $this->statusCode >= 100 && $this->statusCode < 200;
+ }
+
+ /**
+ * Is response successful?
+ *
+ * @return bool
+ *
+ * @final since version 3.2
+ */
+ public function isSuccessful()
+ {
+ return $this->statusCode >= 200 && $this->statusCode < 300;
+ }
+
+ /**
+ * Is the response a redirect?
+ *
+ * @return bool
+ *
+ * @final since version 3.2
+ */
+ public function isRedirection()
+ {
+ return $this->statusCode >= 300 && $this->statusCode < 400;
+ }
+
+ /**
+ * Is there a client error?
+ *
+ * @return bool
+ *
+ * @final since version 3.2
+ */
+ public function isClientError()
+ {
+ return $this->statusCode >= 400 && $this->statusCode < 500;
+ }
+
+ /**
+ * Was there a server side error?
+ *
+ * @return bool
+ *
+ * @final since version 3.3
+ */
+ public function isServerError()
+ {
+ return $this->statusCode >= 500 && $this->statusCode < 600;
+ }
+
+ /**
+ * Is the response OK?
+ *
+ * @return bool
+ *
+ * @final since version 3.2
+ */
+ public function isOk()
+ {
+ return 200 === $this->statusCode;
+ }
+
+ /**
+ * Is the response forbidden?
+ *
+ * @return bool
+ *
+ * @final since version 3.2
+ */
+ public function isForbidden()
+ {
+ return 403 === $this->statusCode;
+ }
+
+ /**
+ * Is the response a not found error?
+ *
+ * @return bool
+ *
+ * @final since version 3.2
+ */
+ public function isNotFound()
+ {
+ return 404 === $this->statusCode;
+ }
+
+ /**
+ * Is the response a redirect of some form?
+ *
+ * @param string $location
+ *
+ * @return bool
+ *
+ * @final since version 3.2
+ */
+ public function isRedirect($location = null)
+ {
+ return in_array($this->statusCode, array(201, 301, 302, 303, 307, 308)) && (null === $location ?: $location == $this->headers->get('Location'));
+ }
+
+ /**
+ * Is the response empty?
+ *
+ * @return bool
+ *
+ * @final since version 3.2
+ */
+ public function isEmpty()
+ {
+ return in_array($this->statusCode, array(204, 304));
+ }
+
+ /**
+ * Cleans or flushes output buffers up to target level.
+ *
+ * Resulting level can be greater than target level if a non-removable buffer has been encountered.
+ *
+ * @param int $targetLevel The target output buffering level
+ * @param bool $flush Whether to flush or clean the buffers
+ *
+ * @final since version 3.3
+ */
+ public static function closeOutputBuffers($targetLevel, $flush)
+ {
+ $status = ob_get_status(true);
+ $level = count($status);
+ // PHP_OUTPUT_HANDLER_* are not defined on HHVM 3.3
+ $flags = defined('PHP_OUTPUT_HANDLER_REMOVABLE') ? PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE) : -1;
+
+ while ($level-- > $targetLevel && ($s = $status[$level]) && (!isset($s['del']) ? !isset($s['flags']) || ($s['flags'] & $flags) === $flags : $s['del'])) {
+ if ($flush) {
+ ob_end_flush();
+ } else {
+ ob_end_clean();
+ }
+ }
+ }
+
+ /**
+ * Checks if we need to remove Cache-Control for SSL encrypted downloads when using IE < 9.
+ *
+ * @see http://support.microsoft.com/kb/323308
+ *
+ * @final since version 3.3
+ */
+ protected function ensureIEOverSSLCompatibility(Request $request)
+ {
+ if (false !== stripos($this->headers->get('Content-Disposition'), 'attachment') && 1 == preg_match('/MSIE (.*?);/i', $request->server->get('HTTP_USER_AGENT'), $match) && true === $request->isSecure()) {
+ if ((int) preg_replace('/(MSIE )(.*?);/', '$2', $match[0]) < 9) {
+ $this->headers->remove('Cache-Control');
+ }
+ }
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/ResponseHeaderBag.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/ResponseHeaderBag.php
new file mode 100644
index 0000000..11a8593
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/ResponseHeaderBag.php
@@ -0,0 +1,340 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * ResponseHeaderBag is a container for Response HTTP headers.
+ *
+ * @author Fabien Potencier
+ */
+class ResponseHeaderBag extends HeaderBag
+{
+ const COOKIES_FLAT = 'flat';
+ const COOKIES_ARRAY = 'array';
+
+ const DISPOSITION_ATTACHMENT = 'attachment';
+ const DISPOSITION_INLINE = 'inline';
+
+ protected $computedCacheControl = array();
+ protected $cookies = array();
+ protected $headerNames = array();
+
+ public function __construct(array $headers = array())
+ {
+ parent::__construct($headers);
+
+ if (!isset($this->headers['cache-control'])) {
+ $this->set('Cache-Control', '');
+ }
+
+ /* RFC2616 - 14.18 says all Responses need to have a Date */
+ if (!isset($this->headers['date'])) {
+ $this->initDate();
+ }
+ }
+
+ /**
+ * Returns the headers, with original capitalizations.
+ *
+ * @return array An array of headers
+ */
+ public function allPreserveCase()
+ {
+ $headers = array();
+ foreach ($this->all() as $name => $value) {
+ $headers[isset($this->headerNames[$name]) ? $this->headerNames[$name] : $name] = $value;
+ }
+
+ return $headers;
+ }
+
+ public function allPreserveCaseWithoutCookies()
+ {
+ $headers = $this->allPreserveCase();
+ if (isset($this->headerNames['set-cookie'])) {
+ unset($headers[$this->headerNames['set-cookie']]);
+ }
+
+ return $headers;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function replace(array $headers = array())
+ {
+ $this->headerNames = array();
+
+ parent::replace($headers);
+
+ if (!isset($this->headers['cache-control'])) {
+ $this->set('Cache-Control', '');
+ }
+
+ if (!isset($this->headers['date'])) {
+ $this->initDate();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all()
+ {
+ $headers = parent::all();
+ foreach ($this->getCookies() as $cookie) {
+ $headers['set-cookie'][] = (string) $cookie;
+ }
+
+ return $headers;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set($key, $values, $replace = true)
+ {
+ $uniqueKey = str_replace('_', '-', strtolower($key));
+
+ if ('set-cookie' === $uniqueKey) {
+ if ($replace) {
+ $this->cookies = array();
+ }
+ foreach ((array) $values as $cookie) {
+ $this->setCookie(Cookie::fromString($cookie));
+ }
+ $this->headerNames[$uniqueKey] = $key;
+
+ return;
+ }
+
+ $this->headerNames[$uniqueKey] = $key;
+
+ parent::set($key, $values, $replace);
+
+ // ensure the cache-control header has sensible defaults
+ if (\in_array($uniqueKey, array('cache-control', 'etag', 'last-modified', 'expires'), true)) {
+ $computed = $this->computeCacheControlValue();
+ $this->headers['cache-control'] = array($computed);
+ $this->headerNames['cache-control'] = 'Cache-Control';
+ $this->computedCacheControl = $this->parseCacheControl($computed);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function remove($key)
+ {
+ $uniqueKey = str_replace('_', '-', strtolower($key));
+ unset($this->headerNames[$uniqueKey]);
+
+ if ('set-cookie' === $uniqueKey) {
+ $this->cookies = array();
+
+ return;
+ }
+
+ parent::remove($key);
+
+ if ('cache-control' === $uniqueKey) {
+ $this->computedCacheControl = array();
+ }
+
+ if ('date' === $uniqueKey) {
+ $this->initDate();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hasCacheControlDirective($key)
+ {
+ return array_key_exists($key, $this->computedCacheControl);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCacheControlDirective($key)
+ {
+ return array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null;
+ }
+
+ public function setCookie(Cookie $cookie)
+ {
+ $this->cookies[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie;
+ $this->headerNames['set-cookie'] = 'Set-Cookie';
+ }
+
+ /**
+ * Removes a cookie from the array, but does not unset it in the browser.
+ *
+ * @param string $name
+ * @param string $path
+ * @param string $domain
+ */
+ public function removeCookie($name, $path = '/', $domain = null)
+ {
+ if (null === $path) {
+ $path = '/';
+ }
+
+ unset($this->cookies[$domain][$path][$name]);
+
+ if (empty($this->cookies[$domain][$path])) {
+ unset($this->cookies[$domain][$path]);
+
+ if (empty($this->cookies[$domain])) {
+ unset($this->cookies[$domain]);
+ }
+ }
+
+ if (empty($this->cookies)) {
+ unset($this->headerNames['set-cookie']);
+ }
+ }
+
+ /**
+ * Returns an array with all cookies.
+ *
+ * @param string $format
+ *
+ * @return array
+ *
+ * @throws \InvalidArgumentException When the $format is invalid
+ */
+ public function getCookies($format = self::COOKIES_FLAT)
+ {
+ if (!in_array($format, array(self::COOKIES_FLAT, self::COOKIES_ARRAY))) {
+ throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', array(self::COOKIES_FLAT, self::COOKIES_ARRAY))));
+ }
+
+ if (self::COOKIES_ARRAY === $format) {
+ return $this->cookies;
+ }
+
+ $flattenedCookies = array();
+ foreach ($this->cookies as $path) {
+ foreach ($path as $cookies) {
+ foreach ($cookies as $cookie) {
+ $flattenedCookies[] = $cookie;
+ }
+ }
+ }
+
+ return $flattenedCookies;
+ }
+
+ /**
+ * Clears a cookie in the browser.
+ *
+ * @param string $name
+ * @param string $path
+ * @param string $domain
+ * @param bool $secure
+ * @param bool $httpOnly
+ */
+ public function clearCookie($name, $path = '/', $domain = null, $secure = false, $httpOnly = true)
+ {
+ $this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly));
+ }
+
+ /**
+ * Generates a HTTP Content-Disposition field-value.
+ *
+ * @param string $disposition One of "inline" or "attachment"
+ * @param string $filename A unicode string
+ * @param string $filenameFallback A string containing only ASCII characters that
+ * is semantically equivalent to $filename. If the filename is already ASCII,
+ * it can be omitted, or just copied from $filename
+ *
+ * @return string A string suitable for use as a Content-Disposition field-value
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @see RFC 6266
+ */
+ public function makeDisposition($disposition, $filename, $filenameFallback = '')
+ {
+ if (!in_array($disposition, array(self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE))) {
+ throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE));
+ }
+
+ if ('' == $filenameFallback) {
+ $filenameFallback = $filename;
+ }
+
+ // filenameFallback is not ASCII.
+ if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) {
+ throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.');
+ }
+
+ // percent characters aren't safe in fallback.
+ if (false !== strpos($filenameFallback, '%')) {
+ throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.');
+ }
+
+ // path separators aren't allowed in either.
+ if (false !== strpos($filename, '/') || false !== strpos($filename, '\\') || false !== strpos($filenameFallback, '/') || false !== strpos($filenameFallback, '\\')) {
+ throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.');
+ }
+
+ $output = sprintf('%s; filename="%s"', $disposition, str_replace('"', '\\"', $filenameFallback));
+
+ if ($filename !== $filenameFallback) {
+ $output .= sprintf("; filename*=utf-8''%s", rawurlencode($filename));
+ }
+
+ return $output;
+ }
+
+ /**
+ * Returns the calculated value of the cache-control header.
+ *
+ * This considers several other headers and calculates or modifies the
+ * cache-control header to a sensible, conservative value.
+ *
+ * @return string
+ */
+ protected function computeCacheControlValue()
+ {
+ if (!$this->cacheControl && !$this->has('ETag') && !$this->has('Last-Modified') && !$this->has('Expires')) {
+ return 'no-cache, private';
+ }
+
+ if (!$this->cacheControl) {
+ // conservative by default
+ return 'private, must-revalidate';
+ }
+
+ $header = $this->getCacheControlHeader();
+ if (isset($this->cacheControl['public']) || isset($this->cacheControl['private'])) {
+ return $header;
+ }
+
+ // public if s-maxage is defined, private otherwise
+ if (!isset($this->cacheControl['s-maxage'])) {
+ return $header.', private';
+ }
+
+ return $header;
+ }
+
+ private function initDate()
+ {
+ $now = \DateTime::createFromFormat('U', time());
+ $now->setTimezone(new \DateTimeZone('UTC'));
+ $this->set('Date', $now->format('D, d M Y H:i:s').' GMT');
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/ServerBag.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/ServerBag.php
new file mode 100644
index 0000000..19d2022
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/ServerBag.php
@@ -0,0 +1,102 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * ServerBag is a container for HTTP headers from the $_SERVER variable.
+ *
+ * @author Fabien Potencier
+ * @author Bulat Shakirzyanov
+ * @author Robert Kiss
+ */
+class ServerBag extends ParameterBag
+{
+ /**
+ * Gets the HTTP headers.
+ *
+ * @return array
+ */
+ public function getHeaders()
+ {
+ $headers = array();
+ $contentHeaders = array('CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true);
+ foreach ($this->parameters as $key => $value) {
+ if (0 === strpos($key, 'HTTP_')) {
+ $headers[substr($key, 5)] = $value;
+ }
+ // CONTENT_* are not prefixed with HTTP_
+ elseif (isset($contentHeaders[$key])) {
+ $headers[$key] = $value;
+ }
+ }
+
+ if (isset($this->parameters['PHP_AUTH_USER'])) {
+ $headers['PHP_AUTH_USER'] = $this->parameters['PHP_AUTH_USER'];
+ $headers['PHP_AUTH_PW'] = isset($this->parameters['PHP_AUTH_PW']) ? $this->parameters['PHP_AUTH_PW'] : '';
+ } else {
+ /*
+ * php-cgi under Apache does not pass HTTP Basic user/pass to PHP by default
+ * For this workaround to work, add these lines to your .htaccess file:
+ * RewriteCond %{HTTP:Authorization} ^(.+)$
+ * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+ *
+ * A sample .htaccess file:
+ * RewriteEngine On
+ * RewriteCond %{HTTP:Authorization} ^(.+)$
+ * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+ * RewriteCond %{REQUEST_FILENAME} !-f
+ * RewriteRule ^(.*)$ app.php [QSA,L]
+ */
+
+ $authorizationHeader = null;
+ if (isset($this->parameters['HTTP_AUTHORIZATION'])) {
+ $authorizationHeader = $this->parameters['HTTP_AUTHORIZATION'];
+ } elseif (isset($this->parameters['REDIRECT_HTTP_AUTHORIZATION'])) {
+ $authorizationHeader = $this->parameters['REDIRECT_HTTP_AUTHORIZATION'];
+ }
+
+ if (null !== $authorizationHeader) {
+ if (0 === stripos($authorizationHeader, 'basic ')) {
+ // Decode AUTHORIZATION header into PHP_AUTH_USER and PHP_AUTH_PW when authorization header is basic
+ $exploded = explode(':', base64_decode(substr($authorizationHeader, 6)), 2);
+ if (2 == count($exploded)) {
+ list($headers['PHP_AUTH_USER'], $headers['PHP_AUTH_PW']) = $exploded;
+ }
+ } elseif (empty($this->parameters['PHP_AUTH_DIGEST']) && (0 === stripos($authorizationHeader, 'digest '))) {
+ // In some circumstances PHP_AUTH_DIGEST needs to be set
+ $headers['PHP_AUTH_DIGEST'] = $authorizationHeader;
+ $this->parameters['PHP_AUTH_DIGEST'] = $authorizationHeader;
+ } elseif (0 === stripos($authorizationHeader, 'bearer ')) {
+ /*
+ * XXX: Since there is no PHP_AUTH_BEARER in PHP predefined variables,
+ * I'll just set $headers['AUTHORIZATION'] here.
+ * http://php.net/manual/en/reserved.variables.server.php
+ */
+ $headers['AUTHORIZATION'] = $authorizationHeader;
+ }
+ }
+ }
+
+ if (isset($headers['AUTHORIZATION'])) {
+ return $headers;
+ }
+
+ // PHP_AUTH_USER/PHP_AUTH_PW
+ if (isset($headers['PHP_AUTH_USER'])) {
+ $headers['AUTHORIZATION'] = 'Basic '.base64_encode($headers['PHP_AUTH_USER'].':'.$headers['PHP_AUTH_PW']);
+ } elseif (isset($headers['PHP_AUTH_DIGEST'])) {
+ $headers['AUTHORIZATION'] = $headers['PHP_AUTH_DIGEST'];
+ }
+
+ return $headers;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php
new file mode 100644
index 0000000..ea1fda2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php
@@ -0,0 +1,148 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Attribute;
+
+/**
+ * This class relates to session attribute storage.
+ */
+class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Countable
+{
+ private $name = 'attributes';
+ private $storageKey;
+
+ protected $attributes = array();
+
+ /**
+ * @param string $storageKey The key used to store attributes in the session
+ */
+ public function __construct($storageKey = '_sf2_attributes')
+ {
+ $this->storageKey = $storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function initialize(array &$attributes)
+ {
+ $this->attributes = &$attributes;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStorageKey()
+ {
+ return $this->storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has($name)
+ {
+ return array_key_exists($name, $this->attributes);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get($name, $default = null)
+ {
+ return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set($name, $value)
+ {
+ $this->attributes[$name] = $value;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function replace(array $attributes)
+ {
+ $this->attributes = array();
+ foreach ($attributes as $key => $value) {
+ $this->set($key, $value);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function remove($name)
+ {
+ $retval = null;
+ if (array_key_exists($name, $this->attributes)) {
+ $retval = $this->attributes[$name];
+ unset($this->attributes[$name]);
+ }
+
+ return $retval;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ $return = $this->attributes;
+ $this->attributes = array();
+
+ return $return;
+ }
+
+ /**
+ * Returns an iterator for attributes.
+ *
+ * @return \ArrayIterator An \ArrayIterator instance
+ */
+ public function getIterator()
+ {
+ return new \ArrayIterator($this->attributes);
+ }
+
+ /**
+ * Returns the number of attributes.
+ *
+ * @return int The number of attributes
+ */
+ public function count()
+ {
+ return count($this->attributes);
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php
new file mode 100644
index 0000000..0d8d179
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php
@@ -0,0 +1,72 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Attribute;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * Attributes store.
+ *
+ * @author Drak
+ */
+interface AttributeBagInterface extends SessionBagInterface
+{
+ /**
+ * Checks if an attribute is defined.
+ *
+ * @param string $name The attribute name
+ *
+ * @return bool true if the attribute is defined, false otherwise
+ */
+ public function has($name);
+
+ /**
+ * Returns an attribute.
+ *
+ * @param string $name The attribute name
+ * @param mixed $default The default value if not found
+ *
+ * @return mixed
+ */
+ public function get($name, $default = null);
+
+ /**
+ * Sets an attribute.
+ *
+ * @param string $name
+ * @param mixed $value
+ */
+ public function set($name, $value);
+
+ /**
+ * Returns attributes.
+ *
+ * @return array Attributes
+ */
+ public function all();
+
+ /**
+ * Sets attributes.
+ *
+ * @param array $attributes Attributes
+ */
+ public function replace(array $attributes);
+
+ /**
+ * Removes an attribute.
+ *
+ * @param string $name
+ *
+ * @return mixed The removed value or null when it does not exist
+ */
+ public function remove($name);
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php
new file mode 100644
index 0000000..abbf37e
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php
@@ -0,0 +1,153 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Attribute;
+
+/**
+ * This class provides structured storage of session attributes using
+ * a name spacing character in the key.
+ *
+ * @author Drak
+ */
+class NamespacedAttributeBag extends AttributeBag
+{
+ private $namespaceCharacter;
+
+ /**
+ * @param string $storageKey Session storage key
+ * @param string $namespaceCharacter Namespace character to use in keys
+ */
+ public function __construct($storageKey = '_sf2_attributes', $namespaceCharacter = '/')
+ {
+ $this->namespaceCharacter = $namespaceCharacter;
+ parent::__construct($storageKey);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has($name)
+ {
+ // reference mismatch: if fixed, re-introduced in array_key_exists; keep as it is
+ $attributes = $this->resolveAttributePath($name);
+ $name = $this->resolveKey($name);
+
+ if (null === $attributes) {
+ return false;
+ }
+
+ return array_key_exists($name, $attributes);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get($name, $default = null)
+ {
+ // reference mismatch: if fixed, re-introduced in array_key_exists; keep as it is
+ $attributes = $this->resolveAttributePath($name);
+ $name = $this->resolveKey($name);
+
+ if (null === $attributes) {
+ return $default;
+ }
+
+ return array_key_exists($name, $attributes) ? $attributes[$name] : $default;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set($name, $value)
+ {
+ $attributes = &$this->resolveAttributePath($name, true);
+ $name = $this->resolveKey($name);
+ $attributes[$name] = $value;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function remove($name)
+ {
+ $retval = null;
+ $attributes = &$this->resolveAttributePath($name);
+ $name = $this->resolveKey($name);
+ if (null !== $attributes && array_key_exists($name, $attributes)) {
+ $retval = $attributes[$name];
+ unset($attributes[$name]);
+ }
+
+ return $retval;
+ }
+
+ /**
+ * Resolves a path in attributes property and returns it as a reference.
+ *
+ * This method allows structured namespacing of session attributes.
+ *
+ * @param string $name Key name
+ * @param bool $writeContext Write context, default false
+ *
+ * @return array
+ */
+ protected function &resolveAttributePath($name, $writeContext = false)
+ {
+ $array = &$this->attributes;
+ $name = (0 === strpos($name, $this->namespaceCharacter)) ? substr($name, 1) : $name;
+
+ // Check if there is anything to do, else return
+ if (!$name) {
+ return $array;
+ }
+
+ $parts = explode($this->namespaceCharacter, $name);
+ if (count($parts) < 2) {
+ if (!$writeContext) {
+ return $array;
+ }
+
+ $array[$parts[0]] = array();
+
+ return $array;
+ }
+
+ unset($parts[count($parts) - 1]);
+
+ foreach ($parts as $part) {
+ if (null !== $array && !array_key_exists($part, $array)) {
+ $array[$part] = $writeContext ? array() : null;
+ }
+
+ $array = &$array[$part];
+ }
+
+ return $array;
+ }
+
+ /**
+ * Resolves the key from the name.
+ *
+ * This is the last part in a dot separated string.
+ *
+ * @param string $name
+ *
+ * @return string
+ */
+ protected function resolveKey($name)
+ {
+ if (false !== $pos = strrpos($name, $this->namespaceCharacter)) {
+ $name = substr($name, $pos + 1);
+ }
+
+ return $name;
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php
new file mode 100644
index 0000000..77521c2
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php
@@ -0,0 +1,161 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Flash;
+
+/**
+ * AutoExpireFlashBag flash message container.
+ *
+ * @author Drak
+ */
+class AutoExpireFlashBag implements FlashBagInterface
+{
+ private $name = 'flashes';
+ private $flashes = array('display' => array(), 'new' => array());
+ private $storageKey;
+
+ /**
+ * @param string $storageKey The key used to store flashes in the session
+ */
+ public function __construct($storageKey = '_symfony_flashes')
+ {
+ $this->storageKey = $storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function initialize(array &$flashes)
+ {
+ $this->flashes = &$flashes;
+
+ // The logic: messages from the last request will be stored in new, so we move them to previous
+ // This request we will show what is in 'display'. What is placed into 'new' this time round will
+ // be moved to display next time round.
+ $this->flashes['display'] = array_key_exists('new', $this->flashes) ? $this->flashes['new'] : array();
+ $this->flashes['new'] = array();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add($type, $message)
+ {
+ $this->flashes['new'][$type][] = $message;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function peek($type, array $default = array())
+ {
+ return $this->has($type) ? $this->flashes['display'][$type] : $default;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function peekAll()
+ {
+ return array_key_exists('display', $this->flashes) ? (array) $this->flashes['display'] : array();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get($type, array $default = array())
+ {
+ $return = $default;
+
+ if (!$this->has($type)) {
+ return $return;
+ }
+
+ if (isset($this->flashes['display'][$type])) {
+ $return = $this->flashes['display'][$type];
+ unset($this->flashes['display'][$type]);
+ }
+
+ return $return;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all()
+ {
+ $return = $this->flashes['display'];
+ $this->flashes['display'] = array();
+
+ return $return;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setAll(array $messages)
+ {
+ $this->flashes['new'] = $messages;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set($type, $messages)
+ {
+ $this->flashes['new'][$type] = (array) $messages;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has($type)
+ {
+ return array_key_exists($type, $this->flashes['display']) && $this->flashes['display'][$type];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function keys()
+ {
+ return array_keys($this->flashes['display']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStorageKey()
+ {
+ return $this->storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ return $this->all();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Session/Flash/FlashBag.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Session/Flash/FlashBag.php
new file mode 100644
index 0000000..12fb740
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Session/Flash/FlashBag.php
@@ -0,0 +1,152 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Flash;
+
+/**
+ * FlashBag flash message container.
+ *
+ * @author Drak
+ */
+class FlashBag implements FlashBagInterface
+{
+ private $name = 'flashes';
+ private $flashes = array();
+ private $storageKey;
+
+ /**
+ * @param string $storageKey The key used to store flashes in the session
+ */
+ public function __construct($storageKey = '_symfony_flashes')
+ {
+ $this->storageKey = $storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function initialize(array &$flashes)
+ {
+ $this->flashes = &$flashes;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add($type, $message)
+ {
+ $this->flashes[$type][] = $message;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function peek($type, array $default = array())
+ {
+ return $this->has($type) ? $this->flashes[$type] : $default;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function peekAll()
+ {
+ return $this->flashes;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get($type, array $default = array())
+ {
+ if (!$this->has($type)) {
+ return $default;
+ }
+
+ $return = $this->flashes[$type];
+
+ unset($this->flashes[$type]);
+
+ return $return;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all()
+ {
+ $return = $this->peekAll();
+ $this->flashes = array();
+
+ return $return;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set($type, $messages)
+ {
+ $this->flashes[$type] = (array) $messages;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setAll(array $messages)
+ {
+ $this->flashes = $messages;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has($type)
+ {
+ return array_key_exists($type, $this->flashes) && $this->flashes[$type];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function keys()
+ {
+ return array_keys($this->flashes);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStorageKey()
+ {
+ return $this->storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ return $this->all();
+ }
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.php
new file mode 100644
index 0000000..80e97f1
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.php
@@ -0,0 +1,93 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Flash;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * FlashBagInterface.
+ *
+ * @author Drak
+ */
+interface FlashBagInterface extends SessionBagInterface
+{
+ /**
+ * Adds a flash message for type.
+ *
+ * @param string $type
+ * @param string $message
+ */
+ public function add($type, $message);
+
+ /**
+ * Registers a message for a given type.
+ *
+ * @param string $type
+ * @param string|array $message
+ */
+ public function set($type, $message);
+
+ /**
+ * Gets flash messages for a given type.
+ *
+ * @param string $type Message category type
+ * @param array $default Default value if $type does not exist
+ *
+ * @return array
+ */
+ public function peek($type, array $default = array());
+
+ /**
+ * Gets all flash messages.
+ *
+ * @return array
+ */
+ public function peekAll();
+
+ /**
+ * Gets and clears flash from the stack.
+ *
+ * @param string $type
+ * @param array $default Default value if $type does not exist
+ *
+ * @return array
+ */
+ public function get($type, array $default = array());
+
+ /**
+ * Gets and clears flashes from the stack.
+ *
+ * @return array
+ */
+ public function all();
+
+ /**
+ * Sets all flash messages.
+ */
+ public function setAll(array $messages);
+
+ /**
+ * Has flash messages for a given type?
+ *
+ * @param string $type
+ *
+ * @return bool
+ */
+ public function has($type);
+
+ /**
+ * Returns a list of all defined types.
+ *
+ * @return array
+ */
+ public function keys();
+}
diff --git a/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Session/Session.php b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Session/Session.php
new file mode 100644
index 0000000..a46cffb
--- /dev/null
+++ b/main/app/sprinkles/core/assets/SiteAssets/php/vendor/symfony/http-foundation/Session/Session.php
@@ -0,0 +1,273 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session;
+
+use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
+use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
+
+/**
+ * @author Fabien Potencier
+ * @author Drak