diff --git a/Config/Schema/empty b/Config/Schema/empty
new file mode 100644
index 0000000..e69de29
diff --git a/Config/Schema/schema.xml b/Config/Schema/schema.xml
new file mode 100644
index 0000000..d78fd3c
--- /dev/null
+++ b/Config/Schema/schema.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+ REFERENCES cm_co_dashboard_widgets(id)
+
+
+
+
+
+
+
+
+
+ co_dashboard_widget_id
+
+
+
+
\ No newline at end of file
diff --git a/Controller/CoGrouperLitesController.php b/Controller/CoGrouperLitesController.php
new file mode 100644
index 0000000..57b143a
--- /dev/null
+++ b/Controller/CoGrouperLitesController.php
@@ -0,0 +1,84 @@
+set('title_for_layout', _txt('pl.grouperlite.config.edit.title'));
+
+ parent::beforeRender();
+ }
+
+ public function display($id) {
+ $cfg = $this->CoGrouperLite->getConfig();
+
+ $this->set('pl_grouperlite_index_url', Router::url([
+ 'plugin' => "grouper_lite",
+ 'controller' => 'GrouperGroups',
+ 'action' => 'index',
+ 'co' => $this->cur_co['Co']['id'],
+ 'glid' => $id
+ ]));
+ $this->set('title_for_layout', _txt('pl.grouperlite.config.display.title'));
+
+ $this->set('coid', $this->cur_co['Co']['id']);
+ $this->set('glid', $id);
+
+ // Pass the config so we know which div to overwrite
+ $this->set('vv_config', $cfg);
+ }
+
+
+ /**
+ * Authorization for this Controller, called by Auth component
+ * - precondition: Session.Auth holds data used for authz decisions
+ * - postcondition: $permissions set with calculated permissions
+ *
+ * @since COmanage Registry v3.2.0
+ * @return Array Permissions
+ */
+
+ function isAuthorized() {
+ $roles = $this->Role->calculateCMRoles();
+
+ // Determine what operations this user can perform
+
+ // Construct the permission set for this user, which will also be passed to the view.
+ // Ask the parent to calculate the display permission, based on the configuration.
+ // Note that the display permission is set at the Dashboard, not Dashboard Widget level.
+ $p = $this->calculateParentPermissions($roles);
+
+ $p['add'] = ($roles['cmadmin'] || $roles['coadmin']);
+
+ // Delete an existing Grouper Widget
+ $p['delete'] = ($roles['cmadmin'] || $roles['coadmin']);
+
+ // Edit an existing Grouper Widget
+ $p['edit'] = ($roles['cmadmin'] || $roles['coadmin']);
+
+ // View an existing Grouper Widget
+ //Not sure the difference between view and index, so will open both to comembers.
+ $p['view'] = ($roles['cmadmin'] || $roles['coadmin'] || $roles['comember']);
+ $p['index'] = ($roles['cmadmin'] || $roles['coadmin'] || $roles['comember']);
+
+ $this->set('permissions', $p);
+ return ($p[$this->action]);
+ }
+}
\ No newline at end of file
diff --git a/Controller/Component/empty b/Controller/Component/empty
new file mode 100644
index 0000000..e69de29
diff --git a/Controller/GrouperGroupsController.php b/Controller/GrouperGroupsController.php
new file mode 100644
index 0000000..69fc296
--- /dev/null
+++ b/Controller/GrouperGroupsController.php
@@ -0,0 +1,591 @@
+ 20,
+ 'maxlimit' => 100,
+ 'page' => 1
+ );
+
+
+ /**
+ * Overrides parent beforeFilter to verify that Session contains the correct API settings.
+ *
+ * @return CakeResponse|void|null
+ *
+ */
+ public function beforeFilter() {
+ parent::beforeFilter();
+
+ //Need to find which plugin instance choosing, if more than one from cm_co_grouper_lites
+ // table being used in COmanage.
+ $grouperConnData = $this->Session->read('Plugin.Grouper.Api');
+ if ($this->Session->check('Plugin.Grouper.Api.id') && count($grouperConnData) == 5) {
+ if (isset($this->passedArgs['glid'])) {
+ if ($this->Session->read('Plugin.Grouper.Api.id') !== $this->passedArgs['glid']) {
+ $this->setConnection();
+ }
+ }
+ } else {
+ if (!isset($this->passedArgs['glid'])) {
+ //If user links directly to GrouperLite url, will be redirected to main index to choose an
+ //appropriate Dashboard Widget.
+ return $this->redirect('/');
+ }
+ $this->setConnection();
+ }
+
+ //Need to verify if user is part of
+ }
+
+ /**
+ * Adding Grouper Conn info to SESSION for use in Lib/GrouperApiAccess.php
+ */
+ private function setConnection() {
+ $this->Session->write('Plugin.Grouper.Api.id', $this->passedArgs['glid']);
+
+ //Now get the setup Dasboard instance from db for connection info.
+ $getConnInfo = new CoGrouperLite();
+ $connectionInfo = $getConnInfo->findById($this->passedArgs['glid']);
+ $this->Session->write('Plugin.Grouper.Api.url', $connectionInfo['CoGrouperLite']['connUrl']);
+ $this->Session->write('Plugin.Grouper.Api.version', $connectionInfo['CoGrouperLite']['connVer']);
+ $this->Session->write('Plugin.Grouper.Api.user', $connectionInfo['CoGrouperLite']['connUser']);
+ $this->Session->write('Plugin.Grouper.Api.pass', $connectionInfo['CoGrouperLite']['connPass']);
+ }
+
+ /**
+ * No true Index page, so sent to default page of Optin
+ *
+ * @return CakeResponse Redirect to Optin page
+ */
+ public function index() {
+ return $this->redirect(
+ array('controller' => 'GrouperGroups', 'action' => 'groupoptin')
+ );
+ }
+
+ /**
+ * Display of Grouper Group Information, such as Group Properties, Members and Attributes
+ *
+ */
+ public function groupInfo() {
+ $name = urldecode($this->request->query['groupname']);
+
+ $this->set('isuserowner', $this->GrouperGroup->isUserOwner($this->userId));
+
+ $this->set('title', _txt('pl.grouperlite.title.groupinfo'));
+
+ try {
+ $details = $this->GrouperGroup->groupDescriptions($name);
+ $this->set('groupergroupsdetail', $details[0]);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': ' . var_export($e->getMessage(), true));
+
+ $this->set('groupergroupsdetail', array());
+ $this->Flash->set(_txt('pl.grouperlite.message.flash.info-group-failed'), array('key' => 'error'));
+ }
+
+ try {
+ $groupMembers = $this->membersInGroup();
+ $this->set('groupergroupssubscribers', $groupMembers);
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': ' . var_export($e->getMessage(), true));
+
+ $this->set('groupergroupssubscribers', array());
+ $this->Flash->set(_txt('pl.grouperlite.message.flash.group-detail-members-failed'), array('key' => 'error'));
+ }
+
+ $this->set('grouperbaseurl', $this->Session->read('Plugin.Grouper.Api.url'));
+ }
+
+ /**
+ * Show all members of group in Grouper Group detail page
+ * Called from method GroupInfo
+ *
+ */
+ public function membersInGroup() {
+ $groupName = urldecode($this->request->query['groupname']);
+
+ //Set initial
+ $scope = [
+ 'groupName' => $groupName
+ ];
+
+ $details = [];
+
+ try {
+ $details = $this->GrouperGroup->membersInGroup($scope);
+ // $this->set('membersingroup', $details);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': ' . var_export($e->getMessage(), true));
+
+ // $this->set('membersingroup', array());
+ $this->Flash->set(_txt('pl.grouperlite.message.flash.group-detail-members-failed'), array('key' => 'error'));
+ }
+
+ return $details;
+ }
+
+
+ /**
+ * Listing of all Grouper Groups owned/admin by User Or search those Grouper Groups
+ */
+ public function groupOwner() {
+ $this->set('title', _txt('pl.grouperlite.title.groupowner'));
+
+ //Set initial settings for pagination
+ $scope = [
+ 'userId' => $this->userId,
+ 'page' => ($this->passedArgs['page'] ? $this->passedArgs['page']: $this->paginate['page']),
+ 'limit' => ($this->passedArgs['limit'] ? $this->passedArgs['limit']: $this->paginate['limit']),
+ ];
+
+ if (isset($this->request->data['search'])) {
+ $searchCriteria = urldecode($this->request->data['search']);
+ $this->set('searchcriteria', $searchCriteria);
+
+ try {
+ //Add settings for search Owned Groups
+ $scope['method'] = 'getSearchedGroups';
+ $scope['searchcriteria'] = $searchCriteria;
+ $scope['searchpage'] = 'ownerGroups';
+
+ $data = $this->Paginator->paginate('GrouperGroup', $scope);
+ $this->set('groupergroupsowner', $data);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ' Search: ' . var_export($e->getMessage(), true));
+
+ $this->set('groupergroupsowner', array());
+ $this->Flash->set(_txt('pl.grouperlite.message.flash.owner-group-failed'), array('key' => 'error'));
+ return;
+ }
+ } else {
+ try {
+ $scope['method'] = 'ownerGroups';
+
+ $data = $this->Paginator->paginate('GrouperGroup', $scope);
+ $this->set('groupergroupsowner', $data);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': ' . var_export($e->getMessage(), true));
+
+ $this->set('groupergroupsowner', array());
+ $this->Flash->set(_txt('pl.grouperlite.message.flash.owner-group-failed'), array('key' => 'error'));
+ return;
+ }
+
+ }
+ $this->set('isuserowner', $this->GrouperGroup->isUserOwner($this->userId));
+ $this->set('isTemplateUser', $this->GrouperGroup->isTemplateUser($this->userId));
+ }
+
+ /**
+ * Returns all Groups that the User is a member of in Grouper
+ * This includes self-joined Optin Groups, as well as required Groups User cannot leave
+ *
+ */
+ public function groupMember() {
+ $this->set('title', _txt('pl.grouperlite.title.groupmember'));
+
+ //Set initial settings for pagination
+ $scope = [
+ 'userId' => $this->userId,
+ 'page' => ($this->passedArgs['page'] ? $this->passedArgs['page']: $this->paginate['page']),
+ 'limit' => ($this->passedArgs['limit'] ? $this->passedArgs['limit']: $this->paginate['limit']),
+ ];
+
+ if (isset($this->request->data['search'])) {
+ $searchCriteria = urldecode($this->request->data['search']);
+ $this->set('searchcriteria', $searchCriteria);
+
+ try {
+ //Add settings for search Member Groups
+ $scope['method'] = 'getSearchedGroups';
+ $scope['searchcriteria'] = $searchCriteria;
+ $scope['searchpage'] = 'filteredMemberOfGroups';
+
+ $data = $this->Paginator->paginate('GrouperGroup', $scope);
+ $this->set('groupergroupmemberships', $data);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ' Search: ' . var_export($e->getMessage(), true));
+
+ $this->Flash->set("Your Search Group cannot be found, please try again later.", array('key' => 'error'));
+ $this->set('groupergroupmemberships', array());
+ return;
+ }
+ } else {
+ try {
+ //Add setting for Group Membership
+ $scope['method'] = 'filteredMemberOfGroups';
+
+ $data = $this->Paginator->paginate('GrouperGroup', $scope);
+ $this->set('groupergroupmemberships', $data);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': ' . var_export($e->getMessage(), true));
+
+ $this->Flash->set("Your Member Group cannot be found, please try again later.", array('key' => 'error'));
+ $this->set('groupergroupmemberships', array());
+ return;
+ }
+ }
+ $this->set('isuserowner', $this->GrouperGroup->isUserOwner($this->userId));
+ $this->set('isTemplateUser', $this->GrouperGroup->isTemplateUser($this->userId));
+ }
+
+ /**
+ * Display all Groups a User can Join
+ */
+ public function groupOptin() {
+ $this->set('title', _txt('pl.grouperlite.title.groupoptin'));
+
+ //Set initial settings for pagination
+ $scope = [
+ 'userId' => $this->userId,
+ 'page' => (isset($this->passedArgs['page']) ? $this->passedArgs['page']: $this->paginate['page']),
+ 'limit' => (isset($this->passedArgs['limit']) ? $this->passedArgs['limit']: $this->paginate['limit']),
+ ];
+
+ if (isset($this->request->data['search'])) {
+ $searchCriteria = urldecode($this->request->data['search']);
+ $this->set('searchcriteria', $searchCriteria);
+
+ try {
+ //Add settings for search Optin's
+ $scope['method'] = 'getSearchedGroups';
+ $scope['searchcriteria'] = $searchCriteria;
+ $scope['searchpage'] = 'optinGroups';
+
+ $data = $this->Paginator->paginate('GrouperGroup', $scope);
+ $this->set('groupergroupoptin', $data);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . 'Search: ' . var_export($e->getMessage(), true));
+
+ $this->Flash->set("Your Optin Group Search cannot be found, please try again later.", array('key' => 'error'));
+ $this->set('groupergroupoptin', array());
+ return;
+ }
+ } else {
+ try {
+ //Add settings for optinGroups
+ $scope['method'] = 'optinGroups';
+
+ $data = $this->Paginator->paginate('GrouperGroup', $scope);
+ $this->set('groupergroupoptin', $data);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': ' . var_export($e->getMessage(), true));
+ $this->Flash->set("An error occurred with the Optin Groups, please try again later.", array('key' => 'error'));
+ $this->set('groupergroupoptin', array());
+ return;
+ }
+ }
+ $this->set('isuserowner', $this->GrouperGroup->isUserOwner($this->userId));
+ $this->set('isTemplateUser', $this->GrouperGroup->isTemplateUser($this->userId));
+ }
+
+ /**
+ * Create a new Grouper Group via Grouper Template
+ * Note: This is tightly coupled code to requirements, so view is hardcoded to reflect current reqs. Will need
+ * to update when reqs change or are updated!!!
+ *
+ * Editing via a template will not be supported in this version of the plugin - Bill Kaufman
+ *
+ */
+ public function groupCreateTemplate() {
+ if ($this->request->is('post')){
+ if(!$this->GrouperGroup->createGroupWithTemplate($this->userId, $this->request->data)){
+ $this->Flash->set("Error in creating group!", array('key' => 'error'));
+ return $this->redirect(array('action' => 'groupoptin'));
+ } else {
+ $this->Flash->set("Success in creating group!", array('key' => 'success'));
+ return $this->redirect(array('action' => 'groupoptin'));
+ }
+ }
+ $this->set('title', _txt('pl.grouperlite.title.templatecreate'));
+ }
+
+ /**
+ * Delete a Grouper Group via Grouper Template
+ *
+ */
+ public function groupDeleteTemplate() {
+ if(!$this->GrouperGroup->deleteGroupWithTemplate($this->userId, $this->request->data)){
+ $this->Flash->set("Error in deleting group!", array('key' => 'error'));
+ return $this->redirect(array('action' => 'groupoptin'));
+ }
+
+ $this->set('title', _txt('pl.grouperlite.title.templatecreate'));
+ }
+
+
+ public function groupCreate() {
+ if ($this->request->is('post')){
+ if(!$this->GrouperGroup->createUpdateGroup($this->userId, $this->request->data)){
+ $this->Flash->set("Error in creating group!", array('key' => 'error'));
+ return $this->redirect(array('action' => 'groupOwner'));
+ } else {
+ $this->Flash->set("Your Group has been created!", array('key' => 'success'));
+ return $this->redirect(array('action' => 'groupOwner'));
+ }
+ }
+
+ $this->set('title', _txt('pl.grouperlite.title.groupcreate'));
+ $this->set('grouperstems', $this->GrouperGroup->getOwnedStems($this->userId));
+ }
+
+ //TODO - Finish this call
+ public function groupDelete() {
+ if(!$this->GrouperGroup->deleteGroup($this->userId, $this->request->data)){
+ $this->Flash->set("Error in deleting group!", array('key' => 'error'));
+ return $this->redirect(array('action' => 'groupOwner'));
+ } else {
+ $this->Flash->set("Your Group has been deleted!", array('key' => 'success'));
+ return $this->redirect(array('action' => 'groupOwner'));
+ }
+
+ $this->set('grouperstems', $this->GrouperGroup->getOwnedStems($this->userId));
+ }
+
+
+ /**
+ * Process to join a group displayed on the "Optin" page
+ *
+ * @return CakeResponse Redirect back to "Optin" page
+ */
+ public function joinGroup() {
+ if ($this->request->is('post')) {
+ $name = $this->request->data['GroupName'];
+
+ try {
+ if ($this->GrouperGroup->joinGroup($this->userId, $name)) {
+ $this->Flash->set(_txt('pl.grouperlite.message.flash.join-group-success'), array('key' => 'success'));
+ } else {
+ $this->Flash->set(_txt('pl.grouperlite.message.flash.join-group-failed'), array('key' => 'error'));
+ }
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': ' . var_export($e->getMessage(), true));
+ $this->Flash->set(_txt('pl.grouperlite.message.flash.join-group-error'), array('key' => 'error'));
+ }
+
+ } else {
+ $this->Flash->set(_txt('pl.grouperlite.message.flash.join-group-error'));
+ }
+
+ return $this->redirect(array('action' => 'groupoptin'));
+ }
+
+ /**
+ * Process to leave a group displayed on the "Member Of" page
+ *
+ * @return CakeResponse Redirect back to "Member Of" page
+ */
+ public function leaveGroup() {
+ if ($this->request->is('post')) {
+ $name = $this->request->data['GroupName'];
+
+ try {
+ if ($this->GrouperGroup->leaveGroup($this->userId, $name)) {
+ $this->Flash->set(_txt('pl.grouperlite.message.flash.leave-group-success'), array('key' => 'success'));
+ } else {
+ $this->Flash->set(_txt('pl.grouperlite.message.flash.leave-group-failed'), array('key' => 'error'));
+ }
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': ' . var_export($e->getMessage(), true));
+ $this->Flash->set(_txt('pl.grouperlite.message.flash.leave-group-error'), array('key' => 'error'));
+ }
+
+ } else {
+ $this->Flash->set(_txt('pl.grouperlite.message.flash.leave-group-error'), array('key' => 'error'));
+ }
+
+ return $this->redirect(array('action' => 'groupmember'));
+ }
+
+ /**
+ * NOTE: All permissions will be done on the Grouper side. All Authenticated users will be able to
+ * use this plugin for self-admin of groups.
+ *
+ * Authorization for this Controller, called by Auth component
+ * - precondition: Session.Auth holds data used for authz decisions
+ * - postcondition: $permissions set with calculated permissions
+ *
+ * @return Array Permissions
+ * @since COmanage Registry v3.2.0
+ */
+ function isAuthorized() {
+ $roles = $this->Role->calculateCMRoles();
+
+ //Need to pull in UserID for access to Grouper
+ if ($this->Session->check('Auth.User.username')) {
+ $this->userId = $this->Session->read('Auth.User.username');
+ }
+
+ // Determine what operations this user can perform
+
+ // Construct the permission set for this user, which will also be passed to the view.
+ //Note: Leaving in current format, in case need to restrict certain pages, can just remove true and add params.
+ $p = array();
+
+ $p['index'] = true;
+ $p['groupinfo'] = true;
+ $p['membersInGroup'] = true;
+ $p['groupowner'] = true;
+ $p['groupmember'] = true;
+ $p['groupoptin'] = true;
+ $p['emaillistsoptin'] = true;
+ $p['emaillistsmember'] = true;
+ $p['emaillistsmanage'] = true;
+ $p['emaillistsinfo'] = true;
+ $p['groupcreate'] = true;
+ $p['groupdelete'] = true;
+ $p['joingroup'] = true;
+ $p['leavegroup'] = true;
+ $p['groupcreatetemplate'] = true;
+
+ $this->set('permissions', $p);
+
+ return ($p[$this->action]);
+ }
+
+ public function emaillistsOptin() {
+ $this->set('title', _txt('pl.grouperlite.title.emaillists-join'));
+
+ //Set initial settings for pagination
+ $scope = [
+ 'userId' => $this->userId,
+ 'page' => (isset($this->passedArgs['page']) ? $this->passedArgs['page']: $this->paginate['page']),
+ 'limit' => (isset($this->passedArgs['limit']) ? $this->passedArgs['limit']: $this->paginate['limit']),
+ ];
+
+ try {
+ //Add settings for optinEmailLists
+ $scope['method'] = 'optinEmailGroups';
+
+ $data = $this->Paginator->paginate('GrouperGroup', $scope);
+ $this->set('emailgroups', $data);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': ' . var_export($e->getMessage(), true));
+ $this->Flash->set("An error occurred with the Optin Groups, please try again later.", array('key' => 'error'));
+ $this->set('emailgroups', array());
+ return;
+ }
+
+ $this->set('isuserowner', $this->GrouperGroup->isUserOwner($this->userId));
+ $this->set('isTemplateUser', $this->GrouperGroup->isTemplateUser($this->userId));
+ }
+
+ public function emaillistsMember()
+ {
+ $this->set('title', _txt('pl.grouperlite.title.emaillists-member'));
+
+ //Set initial settings for pagination
+ $scope = [
+ 'userId' => $this->userId,
+ 'page' => ($this->passedArgs['page'] ? $this->passedArgs['page']: $this->paginate['page']),
+ 'limit' => ($this->passedArgs['limit'] ? $this->passedArgs['limit']: $this->paginate['limit']),
+ ];
+
+ try {
+ //Add setting for Group Membership
+ $scope['method'] = 'filteredMemberOfEmails';
+
+ $data = $this->Paginator->paginate('GrouperGroup', $scope);
+ $this->set('emailgroups', $data);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': ' . var_export($e->getMessage(), true));
+
+ $this->Flash->set("Your Member Group cannot be found, please try again later.", array('key' => 'error'));
+ $this->set('emailgroups', array());
+ return;
+ }
+
+ $this->set('isuserowner', $this->GrouperGroup->isUserOwner($this->userId));
+ $this->set('isTemplateUser', $this->GrouperGroup->isTemplateUser($this->userId));
+ }
+
+
+ public function emaillistsManage() {
+ $this->set('title', _txt('pl.grouperlite.title.emaillists-manage'));
+ // mock data
+ $this->set('group', array(
+ 'member' => true,
+ 'name' => 'Email List 1',
+ 'domain' => 'internet2',
+ 'description' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
+ 'enabled' => 'T'
+ ));
+
+ $this->set('isuserowner', $this->GrouperGroup->isUserOwner($this->userId));
+ $this->set('isTemplateUser', $this->GrouperGroup->isTemplateUser($this->userId));
+ }
+
+ public function emaillistInfo() {
+ $this->set('title', _txt('pl.grouperlite.title.emaillistsinfo'));
+ // mock data
+ $this->set('groupergroupsdetail', array(
+ 'member' => true,
+ 'domain' => 'internet2',
+ 'uuid' => 'abc123xyz789',
+ 'displayExtension' => 'email-list-1',
+ 'extension' => 'email-list-1',
+ 'displayName' => 'Email List 1',
+ 'typeOfGroup' => 'list',
+ 'name' => 'Email List 1',
+ 'description' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
+ 'enabled' => 'T',
+ 'attributes' => array()
+ ));
+ $this->set('grouperbaseurl', $this->Session->read('Plugin.Grouper.Api.url'));
+
+ }
+}
diff --git a/Controller/GrouperLiteAppController.php b/Controller/GrouperLiteAppController.php
new file mode 100644
index 0000000..15ac4ac
--- /dev/null
+++ b/Controller/GrouperLiteAppController.php
@@ -0,0 +1,7 @@
+http = new GrouperHTTPWrapper();
+
+ if (!CakeSession::check('Plugin.Grouper.Api.id')) {
+ CakeLog::write('error', __METHOD__ . ': No Widget record in Session');
+ throw new GrouperLiteException("No GrouperLite instance captured");
+ }
+
+ $connUrl = CakeSession::read('Plugin.Grouper.Api.url');
+ $connVer = CakeSession::read('Plugin.Grouper.Api.version');
+
+ $this->config['fullUrl'] = $connUrl . $this->_urlServlet . $connVer;
+
+ $this->http->setUser(CakeSession::read('Plugin.Grouper.Api.user'));
+ $this->http->setPassword(CakeSession::read('Plugin.Grouper.Api.pass'));
+ }
+
+ /**
+ * NOT BEING USED
+ * Get User information from Grouper Web Service
+ *
+ * @param array $queryData Array of conditions for querying
+ * @return array
+ * @throws Exception
+ */
+ public function getGrouperUser(array $queryData) {
+
+ $userId = $queryData['userId'];
+ $this->http->setHeader(array('Content-Type' => 'application/json', 'Accept' => 'application/json'));
+
+ // Create attributes want returned from call to Grouper WS
+ $formArray = array(
+ 'WsRestGetSubjectsRequest' => array(
+ 'subjectAttributeNames' => array(
+ 'edupersonprincipalname',
+ 'uid',
+ 'mail',
+ 'cn',
+ 'givenname'
+ ),
+ 'wsSubjectLookups' => array(
+ array('subjectIdentifier' => $userId)
+ )
+ )
+ );
+ $formData = json_encode($formArray);
+ $connectionUrl = $this->config['fullUrl'] . '/subjects';
+
+ try {
+ $results = $this->http->sendRequest('GET', $connectionUrl, $formData);
+
+ // Parse out relevant records to send front end
+ if (isset($results['WsGetSubjectsResults']['wsSubjects'][0]['id']) && $results['WsGetSubjectsResults']['wsSubjects'][0]['id'] != NULL) {
+ return $results['WsGetSubjectsResults']['wsSubjects'][0]['id'];
+ }
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+
+ return array();
+ }
+
+ /**
+ * Get Groups that User is a member of from Grouper.
+ *
+ * @param array $queryData Array of conditions for querying
+ * @return array Membership records that User is a member of in Grouper
+ * @throws GrouperLiteException
+ */
+ public function getGrouperMemberOfGroups(array $queryData) {
+
+ //Build request logic
+ $userId = $queryData['userId'];
+ $connectionUrl = "{$this->config['fullUrl']}/subjects/{$userId}/groups";
+
+ try {
+ $results = $this->http->sendRequest('GET', $connectionUrl);
+
+ // Parse out relevant records to send front end
+ if (isset($results['WsGetGroupsLiteResult']['wsGroups']) && $results['WsGetGroupsLiteResult']['wsGroups'] != NULL) {
+ return $results['WsGetGroupsLiteResult']['wsGroups'];
+ }
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+
+ return array();
+ }
+
+ /**
+ * For Optin groups, calls Grouper WS to join or leave a group
+ *
+ * @param array $queryData Array of conditions for querying
+ * @return bool True if join or leave successful, False if not
+ * @throws GrouperLiteException
+ */
+ public function grouperGroupLeaveOrJoin(array $queryData) {
+
+ $groupName = $queryData['groupName'];
+ $userId = $queryData['userId'];
+ $groupLeaveOrJoin = $queryData['LeaveJoin'];
+ $groupName = urlencode($groupName);
+
+ if ($groupLeaveOrJoin == "Leave") {
+ $memberRequest = "WsRestDeleteMemberRequest";
+ $resultResponse = 'WsDeleteMemberResults';
+ $resultGroup = 'wsGroup';
+ } elseif ($groupLeaveOrJoin == "Join") {
+ $memberRequest = "WsRestAddMemberRequest";
+ $resultResponse = 'WsAddMemberResults';
+ $resultGroup = 'wsGroupAssigned';
+ } else {
+ CakeLog::write('error',__METHOD__ . ": Option of $groupLeaveOrJoin is not supported");
+ throw new GrouperLiteException("Receved option of $groupLeaveOrJoin which is not supported");
+ }
+
+ //Build request logic
+ $groupCommand = array(
+ $memberRequest => array(
+ "actAsSubjectLookup" => array(
+ "subjectId" => $userId
+ ),
+ "replaceAllExisting" => "F",
+ "subjectLookups" => array(
+ array("subjectId" => $userId)
+ )
+ )
+ );
+
+ $this->http->setHeader(array('Content-Type' => 'application/json', 'Accept' => 'application/json'));
+ $connectionUrl = "{$this->config['fullUrl']}/groups/{$groupName}/members";
+
+ try {
+ $results = $this->http->sendRequest('PUT', $connectionUrl, json_encode($groupCommand));
+
+ if (isset($results[$resultResponse][$resultGroup]) && $results[$resultResponse][$resultGroup] != NULL) {
+ $groupAssigned = $results[$resultResponse][$resultGroup]['name'];
+ if ($groupAssigned == urldecode($groupName)) {
+ return true;
+ }
+ }
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+
+ return false;
+ }
+
+ /**
+ * Gets all available Optin groups in Grouper
+ * Checks to see which Optin groups the user is already a member of
+ * Returns Optin groups that can be joined and ones that the user is already joined
+ *
+ * @param array $queryData Array of conditions for querying
+ * @return array Optin groups from Grouper
+ * @throws GrouperLiteException
+ *
+ */
+ public function getOptinGroups(array $queryData) {
+ $queryData['groupType'] = 'Optins';
+
+ try {
+ $results = $this->useMembershipUrl($queryData);
+
+ if (isset($results['WsGetMembershipsResults']['wsGroups']) && $results['WsGetMembershipsResults']['wsGroups'] != NULL) {
+ return $results['WsGetMembershipsResults']['wsGroups'];
+ }
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+ return array();
+ }
+
+ /**
+ * Gets all groups in Grouper where user is an admin/owner
+ *
+ * @param array $queryData Array of conditions for querying
+ * @return array Array of groups from Grouper
+ * @throws GrouperLiteException
+ *
+ */
+ public function getOwnedGroups(array $queryData) {
+ $queryData['groupType'] = 'Owner';
+
+ try {
+ $results = $this->useMembershipUrl($queryData);
+
+ if (isset($results['WsGetMembershipsResults']['wsGroups']) && $results['WsGetMembershipsResults']['wsGroups'] != NULL) {
+ return $results['WsGetMembershipsResults']['wsGroups'];
+ }
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+ return array();
+ }
+
+
+ /**
+ * Get members associated to a specific Grouper Group
+ *
+ * @param array $queryData Array of conditions for querying
+ * @return array Listing of Members belonging to Grouper Group
+ * @throws GrouperLiteException
+ */
+ public function getMembersInGroup(array $queryData) {
+
+ //Build request logic
+ $usersToShow = array(
+ "WsRestGetMembersRequest" => array(
+ "wsGroupLookups" => array(
+ array("groupName" => $queryData['groupName'])
+ ),
+ "subjectAttributeNames" => array(
+ "name"
+ )
+ )
+ );
+
+ $this->http->setHeader(array('Content-Type' => 'application/json', 'Accept' => 'application/json'));
+ $connectionUrl = "{$this->config['fullUrl']}/groups";
+
+ try {
+ $results = $this->http->sendRequest('POST', $connectionUrl, json_encode($usersToShow));
+
+ // Parse out relevant records to send front end
+ if (isset($results['WsGetMembersResults']['results'][0]['wsSubjects']) && $results['WsGetMembersResults']['results'][0]['wsSubjects'] != NULL) {
+ return $results['WsGetMembersResults']['results'][0]['wsSubjects'];
+ }
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+
+ return array();
+ }
+
+ /**
+ * Gets all Stems/Folders where User is admin/owner
+ *
+ * @param array $queryData Array of conditions for querying
+ * @return array Array of Stems/Folders from Grouper
+ * @throws GrouperLiteException
+ */
+ public function getOwnedStems(array $queryData) {
+ $queryData['groupType'] = 'StemOwner';
+
+ try {
+ $results = $this->useMembershipUrl($queryData);
+
+ if (isset($results['WsGetMembershipsResults']['wsStems']) && $results['WsGetMembershipsResults']['wsStems'] != NULL) {
+ return $results['WsGetMembershipsResults']['wsStems'];
+ }
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+ return array();
+ }
+
+ /**
+ * Used for requests made to Membership endpoint in Grouper WS
+ *
+ * @see getOptinGroups()
+ * @see getOwnedGroups()
+ * @see getOwnedStems()
+ *
+ * @param array $queryData Array of conditions for querying
+ * @return array Group records associated to calling method
+ * @throws GrouperLiteException
+ */
+ private function useMembershipUrl(array $queryData) {
+ $groupType = $queryData['groupType'];
+ $userId = $queryData['userId'];
+
+ if ($groupType == 'Optins') {
+ $fieldName = "optins";
+ $subjectId = "GrouperAll";
+ } elseif ($groupType == 'Owner') {
+ $fieldName = "admin";
+ $subjectId = $userId;
+ } elseif ($groupType == 'StemOwner') {
+ $fieldName = "stemAdmin";
+ $subjectId = $userId;
+ } else {
+ CakeLog::write('error', __METHOD__ . ": Option of $groupType is not supported");
+ throw new GrouperLiteException("Option of $groupType is not supported");
+ }
+
+ //Build request logic
+ $groupsToShow = array(
+ "WsRestGetMembershipsRequest" => array(
+ "fieldName" => $fieldName,
+ "wsSubjectLookups" => array(
+ array("subjectId" => $subjectId)
+ )
+ )
+ );
+ $this->http->setHeader(array('Content-Type' => 'application/json', 'Accept' => 'application/json'));
+ $connectionUrl = "{$this->config['fullUrl']}/memberships";
+
+ try {
+ return $this->http->sendRequest('POST', $connectionUrl, json_encode($groupsToShow));
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+ }
+
+ /**
+ * Returns a Distinct Grouper Group with its associated values
+ *
+ * @param array $queryData Array of conditions for querying
+ * @return array Array of attributes for a Group
+ * @throws GrouperLiteException
+ */
+ public function getGrouperGroupDescription(array $queryData) {
+ $groupName = $queryData['groupName'];
+
+ //Build request logic
+ $groupName = urlencode($groupName);
+ $connectionUrl = "{$this->config['fullUrl']}/groups/{$groupName}/memberships";
+
+ try {
+ $results = $this->http->sendRequest('GET', $connectionUrl);
+
+ if (isset($results['WsGetMembershipsResults']['wsGroups']) && $results['WsGetMembershipsResults']['wsGroups'] != NULL) {
+ return $results['WsGetMembershipsResults']['wsGroups'];
+ }
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+ return array();
+ }
+
+ /**
+ * Method used to CREATE a new Group in Grouper via the Template method.
+ *
+ * @param array $queryData Array of conditions and data adding new Grouper Group
+ * @return bool True if added successfully
+ * @throws GrouperLiteException
+ *
+ */
+ public function createGroupWithTemplate(array $queryData) {
+ //Currently only supporting create group, need to test if update a group will work!
+
+ $data = $queryData['data'];
+ $userId = $queryData['userId'];
+
+ //Build request logic
+ $inputFields = array();
+ foreach($data as $key => $value) {
+ $inputFields[] = array('name' => $key, 'value' => $value);
+ }
+
+ $groupToSave = array(
+ "WsRestGshTemplateExecRequest" => array(
+ "gshTemplateActAsSubjectLookup" => array(
+ "subjectSourceId" => "ldap",
+ "subjectId" => $userId
+ ),
+ "ownerStemLookup" => array(
+ "stemName" => "ref:incommon-collab"
+ ),
+ "ownerType" => "stem",
+ "configId" => "createNewWorkingGroup",
+ "inputs" => $inputFields
+ )
+ );
+
+ $this->http->setHeader(array('Content-Type' => 'application/json', 'Accept' => 'application/json'));
+ $connectionUrl = "{$this->config['fullUrl']}/gshTemplateExec";
+
+ try {
+ $results = $this->http->sendRequest('POST', $connectionUrl, json_encode($groupToSave));
+
+ if (isset($results['WsGshTemplateExecResult']['resultMetadata']['resultCode'])) {
+ if (stripos($results['WsGshTemplateExecResult']['resultMetadata']['resultCode'], "SUCCESS", 0) !== false) {
+ return true;
+ }
+ }
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+ return false;
+ }
+
+ /**
+ * Method used to DELETE a Group in Grouper via the Template method.
+ *
+ * @param array $queryData Array of conditions and data adding new Grouper Group
+ * @return bool True if deleted successfully
+ * @throws GrouperLiteException
+ *
+ */
+ public function deleteGroupWithTemplate(array $queryData) {
+
+ $workingGroupExt = $queryData['workingGroupExt'];
+ $userId = $queryData['userId'];
+
+ $groupToDelete = array(
+ "WsRestGshTemplateExecRequest" => array(
+ "gshTemplateActAsSubjectLookup" => array(
+ "subjectSourceId" => "ldap",
+ "subjectId" => $userId
+ ),
+ "ownerStemLookup" => array(
+ "stemName" => "ref:incommon-collab"
+ ),
+ "ownerType" => "stem",
+ "configId" => "createWorkingGroup",
+ "inputs" => array(
+ array(
+ "name" => "gsh_input_workingGroupExtension",
+ "value" => $workingGroupExt
+ )
+ )
+ )
+ );
+
+ $this->http->setHeader(array('Content-Type' => 'application/json', 'Accept' => 'application/json'));
+ $connectionUrl = "{$this->config['fullUrl']}/gshTemplateExec";
+
+ try {
+ $results = $this->http->sendRequest('POST', $connectionUrl, json_encode($groupToSave));
+
+ if (isset($results['WsGshTemplateExecResult']['resultMetadata']['resultCode'])) {
+ if (stripos($results['WsGshTemplateExecResult']['resultMetadata']['resultCode'], "SUCCESS", 0) !== false) {
+ return true;
+ }
+ }
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+ return false;
+ }
+
+ /**
+ * For creating/updating Ad-Hoc groups not using the Grouper Template format
+ *
+ * Create or Update a Group where User is Admin/Owner
+ *
+ * @param array $queryData Array of conditions and data adding new Grouper Group
+ * @return bool True if added or updated successful
+ * @throws GrouperLiteException
+ */
+ public function createUpdateGroup(array $queryData) {
+ //TODO Currently only supporting create group, need to test if update a group will work!
+
+ $groupName = htmlentities($queryData['name']);
+ $stemName = htmlentities($queryData['stem']);
+ $userId = $queryData['userId'];
+ $groupDescription = htmlentities($queryData['description']);
+ $privileges = $queryData['privileges'];
+
+ //Group name may be in "friendly" format, so need to do some small conversions before saving.
+ $newGroupName = ucfirst(strtolower($groupName));
+ $newGroupName = str_replace(' ', '', $newGroupName);
+
+ $newGroupToSave = "$stemName:$newGroupName";
+
+ //Build request logic
+ $groupToSave = array(
+ "WsRestGroupSaveRequest" => array(
+ "actAsSubjectLookup" => array(
+ "subjectId" => $userId
+ ),
+ "wsGroupToSaves" => array(
+ array(
+ "wsGroup" => array(
+ "description" => $groupDescription,
+ "name" => $newGroupToSave,
+ "displayExtension" => $groupName
+ ),
+ "wsGroupLookup" => array(
+ "groupName" => $groupDescription
+ )
+ )
+ )
+ )
+ );
+ $this->http->setHeader(array('Content-Type' => 'application/json', 'Accept' => 'application/json'));
+ $connectionUrl = "{$this->config['fullUrl']}/groups";
+
+ try {
+ $results = $this->http->sendRequest('POST', $connectionUrl, json_encode($groupToSave));
+
+ if (isset($results['WsGroupSaveResults']['results']['resultMetadata']['resultCode'])) {
+ if (stripos($results['WsGroupSaveResults']['results']['resultMetadata']['resultCode'], "Success", 0) !== false) {
+ return true;
+ }
+ }
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+ return false;
+ }
+
+ /**
+ * Listing of attributes in Grouper for a given Group Name
+ *
+ * @param array $queryData Array of conditions for querying
+ * @return array Record of Grouper attributes for given GroupName
+ * @throws GrouperLiteException
+ */
+ public function getGroupAttributes(array $queryData) {
+ // Need to pass in the full stem path, so thing like sandbox:app:sympa are good!
+ $groupName = $queryData['groupName'];
+
+ //Build request logic
+ $stemToFind = array(
+ "WsRestGetAttributeAssignmentsRequest" => array(
+ "attributeAssignType" => "group",
+ "includeAssignmentsOnAssignments" => "T",
+ "wsOwnerGroupLookups" => array(
+ array(
+ "groupName" => $groupName,
+ )
+ )
+ )
+ );
+ $this->http->setHeader(array('Content-Type' => 'application/json', 'Accept' => 'application/json'));
+ $connectionUrl = "{$this->config['fullUrl']}/attributeAssignments";
+
+ try {
+ $results = $this->http->sendRequest('POST', $connectionUrl, json_encode($stemToFind));
+
+ if (isset($results['WsGetAttributeAssignmentsResults']['wsAttributeAssigns']) && $results['WsGetAttributeAssignmentsResults']['wsAttributeAssigns'] != NULL) {
+ return $results['WsGetAttributeAssignmentsResults']['wsAttributeAssigns'];
+ }
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+ return array();
+ }
+
+}
diff --git a/Lib/GrouperHTTPWrapper.php b/Lib/GrouperHTTPWrapper.php
new file mode 100644
index 0000000..625d96c
--- /dev/null
+++ b/Lib/GrouperHTTPWrapper.php
@@ -0,0 +1,151 @@
+config['ssl_verify_peer'] = false;
+ }
+
+ /**
+ * Setter for SSL Verify Peer
+ *
+ * @param boolean $val
+ */
+ public function setSSLVerify(bool $val) {
+ $this->config['ssl_verify_peer'] = $val;
+ }
+
+ /**
+ * Setter - User account for accessing Grouper Web Services
+ *
+ * @param string $user
+ */
+ public function setUser(string $user) {
+
+ $this->_user = $user;
+ }
+
+ /**
+ * Setter - Password for accessing Grouper Web Services
+ *
+ * @param string $password
+ */
+ public function setPassword(string $password) {
+
+ $this->_password = $password;
+ }
+
+ /**
+ * Setter - Header settings needed to call Grouper Web Services
+ *
+ * @param array $headerSetting
+ */
+ public function setHeader(array $headerSetting) {
+ $this->_request['header'] = $headerSetting;
+ }
+
+ /**
+ * Send request to Grouper Web Services for Grouper Groups Information
+ *
+ * @param string $method HTTP RESTful method (example: GET, POST, PUT, etc.)
+ * @param string $uri Grouper RESTful endpoint with request string, if applicable
+ * @param string $body JSON formatted request, if applicable
+ * @return array array of records | array with error message
+ * @throws GrouperLiteException If issue with Grouper WS connection
+ */
+ public function sendRequest(string $method, string $uri, string $body = ''): array {
+
+ $this->_request['method'] = $method;
+ $this->_request['uri'] = $this->_parseUri($uri);
+ $this->_request['uri']['user'] = $this->_user;
+ $this->_request['uri']['pass'] = $this->_password;
+ $this->_request['body'] = $body;
+
+ try {
+ $results = $this->request($this->_request);
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred: ' . var_export($e->getMessage(), true));
+ throw new GrouperLiteException('An error occurred talking to Grouper WS');
+ }
+ return $this->_verifyResults($results);
+ }
+
+ /**
+ * Verify data from Grouper Web Service came back successfully, if not log error and send to front end.
+ *
+ * @param HttpSocketResponse $apiResults Results from Grouper WS
+ * @return array array of records | array with error message
+ * @throws GrouperLiteException If issue with Grouper WS data returned
+ */
+ private function _verifyResults(HttpSocketResponse $apiResults): array {
+
+ $resBody = array();
+ if ($apiResults->isOk()) {
+ $resBody = json_decode($apiResults->body(), true);
+
+ $mainKey = key($resBody);
+ $apiSuccess = $resBody[$mainKey]['resultMetadata']['resultCode'];
+
+ if ($apiSuccess != 'SUCCESS') {
+ CakeLog::write('error', __METHOD__ . ': Result Code was ' . var_export($apiSuccess, true));
+ CakeLog::write('error', __METHOD__ . ': Error of ' . var_export($apiResults->body(), true));
+ throw new GrouperLiteException('Result from Grouper WS was' . var_export($apiSuccess, true));
+ }
+ } else {
+ CakeLog::write('error', __METHOD__ . ': Verify error of ' . var_export($apiResults->body(), true));
+ throw new GrouperLiteException('An error occurred while talking to Grouper WS');
+ }
+ return $resBody;
+ }
+}
diff --git a/Lib/GrouperLiteException.php b/Lib/GrouperLiteException.php
new file mode 100644
index 0000000..6e4621b
--- /dev/null
+++ b/Lib/GrouperLiteException.php
@@ -0,0 +1,51 @@
+code}]: {$this->message}\n";
+ }
+}
\ No newline at end of file
diff --git a/Lib/empty b/Lib/empty
new file mode 100644
index 0000000..e69de29
diff --git a/Lib/lang.php b/Lib/lang.php
new file mode 100644
index 0000000..80f2baf
--- /dev/null
+++ b/Lib/lang.php
@@ -0,0 +1,131 @@
+ 'Grouper Configuration Settings',
+ 'pl.grouperlite.config.edit.title' => 'Edit Grouper Configuration Settings',
+ 'pl.grouperlite.config.grouper-url' => 'Grouper URL',
+ 'pl.grouperlite.config.grouper-version' => 'Grouper Version',
+ 'pl.grouperlite.config.grouper-un' => 'Username',
+ 'pl.grouperlite.config.grouper-pw' => 'Password',
+
+ 'pl.grouperlite.crumb.root' => 'Grouper',
+ 'pl.grouperlite.nav.groups-can-join' => 'Groups I can join',
+ 'pl.grouperlite.nav.groups-presided' => 'Groups I manage',
+ 'pl.grouperlite.nav.memberships' => 'My memberships',
+ 'pl.grouperlite.nav.emaillists-join' => 'Email lists I can join',
+ 'pl.grouperlite.nav.emaillists-member' => 'My email lists',
+ 'pl.grouperlite.nav.emaillists-manage' => 'Email lists I manage',
+ 'pl.grouperlite.nav.create-group' => 'Create group',
+ 'pl.grouperlite.nav.create-email' => 'Create email list',
+ 'pl.grouperlite.nav.emaillists' => 'Email Lists',
+ 'pl.grouperlite.nav.groups' => 'Groups',
+
+ 'pl.grouperlite.title.root' => 'Grouper Collaborations:',
+ 'pl.grouperlite.title.groupinfo' => 'Group configuration and attributes',
+ 'pl.grouperlite.title.groupowner' => 'Groups I manage',
+ 'pl.grouperlite.title.groupmember' => 'My Memberships',
+ 'pl.grouperlite.title.groupoptin' => 'Groups I can join',
+ 'pl.grouperlite.title.emaillists-join' => 'Email lists I can join',
+ 'pl.grouperlite.title.emaillists-member' => 'My email lists',
+ 'pl.grouperlite.title.emaillists-manage' => 'Email lists I manage',
+ 'pl.grouperlite.title.emaillistsinfo' => 'Email list configuration and attributes',
+ 'pl.grouperlite.title.groupcreate' => 'Create Group',
+ 'pl.grouperlite.title.templatecreate' => 'Create a Templated Group',
+
+ 'pl.grouperlite.message.flash.join-group-success' => 'You have been added to the group!',
+ 'pl.grouperlite.message.flash.join-group-failed' => 'You are unable to join the group!',
+ 'pl.grouperlite.message.flash.join-group-error' => 'An error occurred in joining the group, please try again later.',
+ 'pl.grouperlite.message.flash.info-group-failed' => 'Error in viewing group info, please try again later.',
+
+ 'pl.grouperlite.message.flash.leave-group-success' => 'You have been deleted from the group!',
+ 'pl.grouperlite.message.flash.leave-group-failed' => 'You are unable to delete the group!',
+ 'pl.grouperlite.message.flash.leave-group-error' => 'An error occurred in leaving the group, please try again later.',
+ 'pl.grouperlite.message.flash.owner-group-failed' => 'Error occurred in viewing groups you manage, please try again later.',
+ 'pl.grouperlite.message.flash.member-group-failed' => 'Error occurred in viewing groups you are a member, please try again later.',
+ 'pl.grouperlite.message.flash.optin-group-failed' => 'Error occurred in viewing Optin groups, please try again later.',
+
+ 'pl.grouperlite.message.flash.group-detail-members-failed' => 'Error in viewing the members of this group, please try again later.',
+
+ 'pl.grouperlite.table.name' => 'Name',
+ 'pl.grouperlite.table.description' => 'Description',
+ 'pl.grouperlite.table.status' => 'Status',
+ 'pl.grouperlite.table.action' => 'Action',
+ 'pl.grouperlite.table.open' => 'Access',
+ 'pl.grouperlite.table.groups' => 'Groups',
+ 'pl.grouperlite.table.attribute-name' => 'Attribute name',
+ 'pl.grouperlite.table.attribute-description' => 'Attribute description',
+
+ 'pl.grouperlite.table.display-name' => 'Display Name:',
+ 'pl.grouperlite.table.grouper-name' => 'Grouper Name:',
+ 'pl.grouperlite.table.display-extension' => 'Display Extension:',
+ 'pl.grouperlite.table.extension' => 'Extension:',
+ 'pl.grouperlite.table.enabled' => 'Enabled:',
+ 'pl.grouperlite.table.type-of-group' => 'Type of Group:',
+ 'pl.grouperlite.table.uuid' => 'UUID:',
+
+ 'pl.grouperlite.table.status-open' => 'Open',
+ 'pl.grouperlite.table.status-moderated' => 'Moderated',
+ 'pl.grouperlite.table.user-type-members' => 'Subscribers',
+ 'pl.grouperlite.table.user-type-moderators' => 'Moderators',
+ 'pl.grouperlite.table.user-type-owners' => 'Owners',
+
+ 'pl.grouperlite.group.info.attributes-zero-state' => 'No attributes defined.',
+ 'pl.grouperlite.group.info.attributes-heading' => 'Attributes',
+ 'pl.grouperlite.group.info.properties-heading' => 'Properties',
+ 'pl.grouperlite.group.info.page-heading' => 'Group:',
+ 'pl.grouperlite.group.info.members-heading' => 'Members',
+
+ 'pl.grouperlite.value.descr.zerostate' => 'No Description',
+ 'pl.grouperlite.value.enabled' => 'Enabled',
+ 'pl.grouperlite.value.disabled' => 'Disabled',
+
+ 'pl.grouperlite.action.join' => 'Join',
+ 'pl.grouperlite.action.leave' => 'Leave',
+ 'pl.grouperlite.action.edit-group' => 'Edit',
+ 'pl.grouperlite.action.disable-group' => 'Disable',
+ 'pl.grouperlite.action.subscribe' => 'Subscribe',
+ 'pl.grouperlite.action.unsubscribe' => 'Unsubscribe',
+ 'pl.grouperlite.action.search' => 'Search',
+ 'pl.grouperlite.action.remove' => 'Remove',
+ 'pl.grouperlite.action.view' => 'View',
+ 'pl.grouperlite.action.edit' => 'Edit',
+ 'pl.grouperlite.action.view-in-grouper' => 'View in Grouper',
+ 'pl.grouperlite.action.view-members' => 'View members',
+ 'pl.grouperlite.action.close' => 'Close',
+
+ 'pl.grouperlite.form.group.template.label' => 'Select a template',
+ 'pl.grouperlite.form.group.template.empty' => '(choose one)',
+ 'pl.grouperlite.form.group.name.label' => 'Display name',
+ 'pl.grouperlite.form.group.name.help' => 'Name is the label that identifies this group, and might change.',
+ 'pl.grouperlite.form.group.stem.label' => 'Create in this folder:',
+ 'pl.grouperlite.form.group.stem.help' => 'Enter a folder name.',
+ 'pl.grouperlite.form.group.description.label' => 'Description:',
+ 'pl.grouperlite.form.group.description.help' => 'Description contains notes about the group, which could include: what the group represents, why it was created, etc.',
+ 'pl.grouperlite.form.group.privs.label' => 'Privileges',
+ 'pl.grouperlite.form.group.privs.label.READ' => 'READ',
+ 'pl.grouperlite.form.group.privs.label.VIEW' => 'VIEW',
+ 'pl.grouperlite.form.group.privs.label.OPTIN' => 'OPTIN',
+ 'pl.grouperlite.form.group.privs.label.OPTOUT' => 'OPTOUT',
+ 'pl.grouperlite.form.group.privs.label.ATTRIBUTE_READ' => 'ATTRIBUTE READ',
+ 'pl.grouperlite.form.group.subs.label' => 'Subscribers:',
+ 'pl.grouperlite.form.group.subs.help' => 'Select subscribers to add to the group.',
+ 'pl.grouperlite.form.group.subs.placeholder' => 'Search subscribers',
+ 'pl.grouperlite.form.group.action.save' => 'Save Group',
+
+ 'pl.grouperlite.form.template.work-group-extension.label' => 'Working Group Extension',
+ 'pl.grouperlite.form.template.work-group-disp-extension.label' => 'Working Group Display Extension',
+ 'pl.grouperlite.form.template.work-group-description.label' => 'Working Group Description',
+ 'pl.grouperlite.form.template.enable-email-list.label' => 'Enable email list?',
+ 'pl.grouperlite.form.template.domain.label' => 'Domain:',
+ 'pl.grouperlite.form.template.value.internet2' => 'Internet2',
+ 'pl.grouperlite.form.template.value.incommon' => 'Incommon',
+ 'pl.grouperlite.form.template.is-optin.label' => 'Is optin?',
+ 'pl.grouperlite.form.template.add-wiki.label' => 'Add wiki?',
+ 'pl.grouperlite.form.template.add-project.label' => 'Add project?',
+ 'pl.grouperlite.form.template.value.positive' => 'Yes',
+ 'pl.grouperlite.form.template.value.negative' => 'No',
+
+ 'pl.grouperlite.search.tags.text' => 'Search',
+
+ 'pl.grouperlite.pagination.counter' => 'Viewing {:start}-{:end} of {:count}'
+);
\ No newline at end of file
diff --git a/Locale/eng/LC_MESSAGES/empty b/Locale/eng/LC_MESSAGES/empty
new file mode 100644
index 0000000..e69de29
diff --git a/Model/Behavior/empty b/Model/Behavior/empty
new file mode 100644
index 0000000..e69de29
diff --git a/Model/CoGrouperLite.php b/Model/CoGrouperLite.php
new file mode 100644
index 0000000..a0798e3
--- /dev/null
+++ b/Model/CoGrouperLite.php
@@ -0,0 +1,75 @@
+ array(
+ "parent" => "CoDashboardWidget",
+ "fk" => "co_dashboard_widget_id"
+ )
+ );*/
+
+ // Validation rules for table elements
+ //Tried adding rule to each field to make alphaNumeric, but keeps throwing errors. will research.
+ public $validate = array(
+ 'co_dashboard_widget_id' => array(
+ 'rule' => 'numeric',
+ 'required' => true
+ ),
+ 'connUrl' => array(
+ 'required' => true
+ ),
+ 'connVer' => array(
+ 'required' => true
+ ),
+ 'connUser' => array(
+ 'required' => true
+ ),
+ 'connPass' => array(
+ 'required' => true
+ ),
+ );
+
+}
\ No newline at end of file
diff --git a/Model/CoManagePerson.php b/Model/CoManagePerson.php
new file mode 100644
index 0000000..8d999c9
--- /dev/null
+++ b/Model/CoManagePerson.php
@@ -0,0 +1,11 @@
+grouperAPI == null) {
+ $this->grouperAPI = new GrouperApiAccess();
+ }
+ }
+
+ /**
+ * Pull attributes for a Group in Grouper
+ *
+ * @param string $groupName Name of Group
+ * @return array Attributes in Grouper for this Group
+ * @throws GrouperLiteException
+ */
+ public function getGroupAttributes(string $groupName) {
+ $this->initApi();
+
+ try {
+ $args = array();
+ $args['groupName'] = $groupName;
+
+ return $this->grouperAPI->getGroupAttributes($args);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', 'GrouperLite Attribute Model - getGroupAttributes: An error occurred');
+ throw $e;
+ }
+ }
+
+}
diff --git a/Model/GrouperGroup.php b/Model/GrouperGroup.php
new file mode 100644
index 0000000..89bf151
--- /dev/null
+++ b/Model/GrouperGroup.php
@@ -0,0 +1,665 @@
+initApi();
+
+ try {
+ $args = array();
+ $args['userId'] = $userId;
+
+ $ownGroups = $this->grouperAPI->getOwnedGroups($args);
+
+ if (count($ownGroups) > 0) {
+ CakeSession::write('Plugin.Grouper.isUserOwner', 'T');
+ return 'T';
+ }
+ CakeSession::write('Plugin.Grouper.isUserOwner', 'F');
+ return 'F';
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+ }
+
+
+ /**
+ * Used to instantiate API class
+ *
+ * Tried to use the constructor for GrouperGroup to set this but it was triggering the GrouperAPIAccess class
+ * to instantiate before the Controller was able populate the connection settings from the DB into the Session,
+ * so error was being thrown.
+ * Now will call this before each function call to verify set.
+ */
+ private function initApi() {
+ if ($this->grouperAPI == null) {
+ $this->grouperAPI = new GrouperApiAccess();
+ }
+ }
+
+ /**
+ * Return all Groups that a User belongs to in Grouper.
+ * Will also add Optin Groups and flag them as joined so can display Optout option in UI.
+ *
+ * @param array $conditions Listing of conditions for display of records, including UserId
+ * @return array Records of Groups from Grouper that the User belongs to
+ * @throws GrouperLiteException
+ *
+ */
+ public function filteredMemberOfGroups(array $conditions) {
+ $this->initApi();
+
+ try {
+ $memberOfGroups = $this->memberOfGroups($conditions);
+
+ // Determine which groups can be left by user, if want.
+ $optInGroups = $this->grouperAPI->getOptinGroups($conditions);
+
+ foreach ($memberOfGroups as &$memberOfGroup) {
+ foreach ($optInGroups as $key => $value) {
+ if ($value['name'] == $memberOfGroup['name']) {
+ //Match!
+ $memberOfGroup['optedin'] = true;
+ //Remove Optin group since already found and now less loops
+ unset($optInGroups[$key]);
+ break;
+ }
+ }
+ }
+
+ return $this->getFriendlyName($memberOfGroups);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+ }
+
+ public function filteredMemberOfEmails(array $conditions) {
+ $memberOfEmails = $this->filteredMemberOfGroups($conditions);
+
+ // Strip out all Groups that are not in Sympa Stem/Directory
+ foreach($memberOfEmails as $key => $value){
+ if(strpos($value['name'], 'sympa') === false) {
+ unset($memberOfEmails[$key]);
+ }
+ }
+
+ return $memberOfEmails;
+ }
+
+ /**
+ * Internal process used by other functions to fetch Groups the User is a member of
+ *
+ * @param array $conditions Listing of conditions for display of records, including UserId
+ * @return array Records of Groups from Grouper that the User belongs to
+ * @throws GrouperLiteException
+ *
+ */
+ private function memberOfGroups(array $conditions) {
+
+ try {
+ return $this->grouperAPI->getGrouperMemberOfGroups($conditions);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+ }
+
+ /**
+ * Determine if result set contains friendly name, if so add as a new attribute in result set
+ *
+ * @param array $groups Current result set of Groups
+ * @return array Same result set with added param for friendly name
+ *
+ */
+ private function getFriendlyName(array $groups) {
+
+ //NOT fetching attributes now, just using name display name as friendly name, if different from name field
+ foreach ($groups as &$group) {
+ //$group['friendlyName'] = $group['displayName'];
+ $nameSections = explode(':', $group['name']);
+ $friendlySections = explode(':', $group['displayName']);
+ $numberNameSections = count($nameSections);
+ for ($x = 0; $x < $numberNameSections; $x++) {
+ if ($nameSections[$x] == $friendlySections[$x]) {
+ unset($friendlySections[$x]);
+ }
+ }
+ $group['friendlyName'] = implode(':', $friendlySections);
+ if (strlen($group['friendlyName']) == 0) {
+ $group['friendlyName'] = $group['name'];
+ }
+ }
+
+ return $groups;
+
+ /* Old Process, keeping in case changes back!
+ foreach ($groups as &$group) {
+ $group['friendlyName'] = $group['displayName'];
+ $attributes = $this->grouperAPI->getGroupAttributes(array('conditions' => array('groupName' => $group['displayName'])));
+ foreach ($attributes as $attribute) {
+ if ($attribute['attributeDefNameName'] == $this->friendly) {
+ if (isset($attribute['wsAttributeAssignValues']['valueSystem'])) {
+ $group['friendlyName'] = $attribute['wsAttributeAssignValues']['valueSystem'];
+ }
+ break;
+ }
+ }
+ }*/
+ }
+
+ /**
+ * Gets the Grouper Groups params and values as well as its associated attributes.
+ *
+ * @param string $groupName Name of Group, do not confuse with DisplayName field!
+ * @return array An set of attributes associated to a specific Grouper Group
+ * @throws GrouperLiteException
+ */
+ public function groupDescriptions(string $groupName) {
+ $this->initApi();
+
+ try {
+ $args = array();
+ $args['groupName'] = $groupName;
+
+ $groupDescription = $this->grouperAPI->getGrouperGroupDescription($args);
+ $groupInfo = $this->getFriendlyName($groupDescription);
+
+ //make call to get attributes for group
+ $grouperAtt = new GrouperAttribute();
+ $groupAttributes = $grouperAtt->getGroupAttributes($groupName);
+
+ $groupInfo[0]["attributes"] = $groupAttributes;
+ return $groupInfo;
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+
+ }
+
+ /**
+ * Process for User to Leave a Group
+ *
+ * @param string $userId Id of User
+ * @param string $groupName Name of Group Leaving, do not confuse with DisplayName field!
+ * @return bool True|False
+ * @throws GrouperLiteException
+ *
+ */
+ public function leaveGroup(string $userId, string $groupName) {
+ $this->initApi();
+
+ try {
+ $args = array();
+ $args['LeaveJoin'] = 'Leave';
+ $args['userId'] = $userId;
+ $args['groupName'] = $groupName;
+
+ return $this->grouperAPI->grouperGroupLeaveOrJoin($args);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+ }
+
+ /**
+ * Process for User to Join a Group
+ *
+ * @param string $userId Id of User
+ * @param string $groupName Name of Group Joining, do not confuse with DisplayName field!
+ * @return bool True|False
+ * @throws GrouperLiteException
+ *
+ */
+ public function joinGroup(string $userId, string $groupName) {
+ $this->initApi();
+
+ try {
+ $args = array();
+ $args['LeaveJoin'] = 'Join';
+ $args['userId'] = $userId;
+ $args['groupName'] = $groupName;
+
+ return $this->grouperAPI->grouperGroupLeaveOrJoin($args);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+ }
+
+ /**
+ * Return all Grouper Groups that the User has a role of owner/admin
+ *
+ * @param array $conditions Listing of conditions for display of records, including UserId
+ * @return array
+ * @throws GrouperLiteException
+ *
+ */
+ public function ownerGroups(array $conditions) {
+ $this->initApi();
+
+ try {
+ $ownGroups = $this->grouperAPI->getOwnedGroups($conditions);
+
+ return $this->getFriendlyName($ownGroups);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+ }
+
+ /**
+ * Get members associated to a specific Grouper Group
+ *
+ * @param array $conditions Listing of conditions for display of records
+ * @return array Listing of members in requested Grouper Group
+ * @throws GrouperLiteException Captured in Controller
+ *
+ */
+ public function membersInGroup(array $conditions) {
+ $this->initApi();
+
+ try {
+ return $this->grouperAPI->getMembersInGroup($conditions);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+ }
+
+ /**
+ * Return all Groups the User can JOIN
+ * Get all Groups with Optin attribute set and display ones User can join.
+ * Will Match up with Groups User is already a member of to determine which Optin groups to not display
+ *
+ * @param array $conditions Listing of conditions for display of records, including UserId
+ * @return array Listing of Optin groups available in Grouper
+ * @throws GrouperLiteException Captured in Controller
+ */
+ public function optinGroups(array $conditions) {
+ $this->initApi();
+
+ try {
+ $joinOrLeave = $this->grouperAPI->getOptinGroups($conditions);
+ $userGroups = $this->memberOfGroups($conditions);
+
+ //See if Optin group match any of the groups user already belongs to.
+ foreach ($joinOrLeave as $key => $value) {
+ foreach ($userGroups as $userGroup) {
+ if ($value['name'] == $userGroup['name']) {
+ //Match!!
+ //Removing from array since already a member of.
+ unset($joinOrLeave[$key]);
+ break;
+ }
+ }
+ }
+ //$finalSet = $this->paginateRecords($joinOrLeave, $conditions);
+
+ return $this->getFriendlyName($joinOrLeave);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+ }
+
+ public function optinEmailGroups(array $conditions) {
+ $allGroups = $this->optinGroups($conditions);
+
+ // Strip out all Groups that are not in Sympa Stem/Directory
+ foreach($allGroups as $key => $value){
+ if(strpos($value['name'], 'sympa') === false) {
+ unset($allGroups[$key]);
+ }
+ }
+
+ return $allGroups;
+ }
+
+ /**
+ * Determine if User can use the Grouper Template to create a suite of Groups including Email lists.
+ *
+ * @param string $userId Id of User
+ * @return string T for True and F for False
+ * @throws GrouperLiteException
+ */
+ public function isTemplateUser(string $userId) {
+ if (CakeSession::check('Plugin.Grouper.isTemplateUser')) {
+ return CakeSession::read('Plugin.Grouper.isTemplateUser');
+ }
+
+ $this->initApi();
+
+ try {
+ $args = array();
+ $args['userId'] = $userId;
+
+ $memberOfGroups = $this->grouperAPI->getGrouperMemberOfGroups($args);
+
+ //now cycle through and see if part of correct group to be able to use template
+ $member = 'F';
+ foreach ($memberOfGroups as $memberOfGroup) {
+ if ($memberOfGroup['name'] == $this->templateCreationGroup) {
+ $member = 'T';
+ break;
+ }
+ }
+
+ if ($member == 'T') {
+ CakeSession::write('Plugin.Grouper.isTemplateUser', 'T');
+ return 'T';
+ }
+ CakeSession::write('Plugin.Grouper.isTemplateUser', 'F');
+ return 'F';
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+
+ }
+
+
+ /**
+ * Create a new Grouper Group using the Template methodology in Grouper
+ *
+ * @param string $userId Id of User
+ * @param array $groupData Data needed to create new Grouper Group via Template
+ * @return bool True if successfully created record
+ * @throws GrouperLiteException
+ *
+ */
+ public function createGroupWithTemplate(string $userId, array $groupData) {
+ $this->initApi();
+
+ try {
+ //Need to massage incoming data to meet Grouper Template requirements
+ $fields = array(
+ 'gsh_input_isSympa',
+ 'gsh_input_isSympaModerated',
+ 'gsh_input_isOptin',
+ 'gsh_input_isConfluence',
+ 'gsh_input_isJira'
+ );
+ // Template does not except true/false, so convert to string and send that way
+ foreach($fields as $field) {
+ ($groupData[$field] == '0') ? $groupData[$field] = 'false' : $groupData[$field] = 'true';
+ }
+
+ $args = array();
+ $args['userId'] = $userId;
+ $args['data'] = $groupData;
+
+ // Reset Session variable that shows if User is an owner or not
+ $this->resetUserOwner();
+
+ return $this->grouperAPI->createGroupWithTemplate($args);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+
+ }
+
+ /**
+ * Delete a Grouper Group using the Template methodology in Grouper
+ *
+ * @param string $userId Id of User
+ * @param array $groupData Data needed to delete a Grouper Group via Template
+ * @return bool True if successfully deleted record
+ * @throws GrouperLiteException
+ *
+ */
+ public function deleteGroupWithTemplate(string $userId, array $groupData) {
+ $this->initApi();
+
+ try {
+ $args = array();
+ $args['userId'] = $userId;
+ $args['workingGroupExt'] = $groupData['workingGroupExt'];
+
+ // Reset Session variable that shows if User is an owner or not
+ $this->resetUserOwner();
+
+ return $this->grouperAPI->deleteGroupWithTemplate($args);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+
+ }
+
+ /**
+ * Creates/Updates a new Grouper Group not Template related
+ *
+ * @param string $userId Id of user
+ * @param array $data Data from form to Save in Grouper as a Group
+ * @return bool True if saved | False if already created
+ * @throws GrouperLiteException
+ *
+ */
+ public function createUpdateGroup(string $userId, array $data) {
+ $this->initApi();
+
+ try {
+ $args = $data;
+ $args['userId'] = $userId;
+
+ // Reset Session variable that shows if User is an owner or not
+ $this->resetUserOwner();
+
+ return $this->grouperAPI->createUpdateGroup($args);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+ }
+
+ /**
+ * Private function used to reset the IsUserOwner Session variable,
+ * that maintains the status of a user being an owner/admin of a group
+ *
+ * @see GrouperGroup::isUserOwner()
+ */
+ private function resetUserOwner() {
+ if (CakeSession::check('Plugin.Grouper.isUserOwner')) {
+ CakeSession::delete('Plugin.Grouper.isUserOwner');
+ }
+ }
+
+ /**
+ * Get Grouper Stems where User has authority to create a Grouper Group
+ *
+ * @param string $userId Id of User
+ * @return array Stem records
+ * @throws GrouperLiteException
+ */
+ public function getOwnedStems(string $userId) {
+ $this->initApi();
+
+ try {
+ $args = array();
+ $args['userId'] = $userId;
+
+ return $this->grouperAPI->getOwnedStems($args);
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+ }
+
+ /**
+ * Search for Groups/Lists related to Search term.
+ * Will import all records the user can see and then do search in this code rather than call Grouper WS Search
+ * functionality. This is due to the fact that the grouperName is autogenerated and this app needs to search
+ * attributes which the Grouper WS does not do.
+ *
+ * @param array $conditions Listing of conditions for display of records, including UserId
+ * @return array Records that meet search criteria
+ * @throws Exception Captured in Controller
+ */
+ public function getSearchedGroups(array $conditions) {
+ $this->initApi();
+
+ try {
+ //Breakout page where search was called and forward to appropriate method for processing
+ $page = $conditions['searchpage'];
+ $pageResults = $this->$page($conditions);
+
+ $returnResults = array();
+ $searchCriteria = $conditions['searchcriteria'];
+
+ foreach ($pageResults as $result) {
+ $match = preg_grep("/$searchCriteria/i", $result);
+ if (!empty($match)) {
+ $returnResults[] = $result;
+ }
+ }
+
+ return $returnResults;
+
+ } catch (Exception $e) {
+ CakeLog::write('error', __METHOD__ . ': An error occurred');
+ throw $e;
+ }
+ }
+
+ /**
+ * Method needed to support Pagination
+ * Adjusted to fit the data source being the Grouper API vs DB calls
+ *
+ * @param array $conditions Listing of conditions for display of records with pagination
+ * @return array Records requested by user with pagination support
+ *
+ */
+ public function paginate($conditions) {
+
+ //Pull out the method that should be run.
+ $method = $conditions['method'];
+
+ $resultSet = $this->$method($conditions);
+
+ return $this->paginateRecords($resultSet, $conditions);
+ }
+
+ /**
+ * Parses returned records to display only requested records for pagination.
+ *
+ * @param array $recSet All Grouper Records associated to User's request
+ * @param array $conditions Listing of conditions for display of records with pagination
+ * @return array Records requested by user with pagination support
+ *
+ */
+ private function paginateRecords(array $recSet, array $conditions) {
+
+ //set for pagination record count
+ $this->totalRecords = count($recSet);
+
+ //Return all, if requested
+ if (strtolower($conditions['limit']) == 'all') {
+ return $recSet;
+ }
+
+ //Now slice recordset to return correct set of records.
+ $page = $conditions['page'] - 1;
+ $limit = $conditions['limit'];
+ if ($page == 0) {
+ $start = 0;
+ } else {
+ $start = $page * $limit;
+ }
+ $end = $start + $limit;
+
+ return array_slice($recSet, $start, $end);
+ }
+
+ /**
+ * Needed method to support Cake's pagination in the views
+ *
+ * @param null $conditions Listing of conditions for display of records with pagination
+ * @param int $recursive
+ * @param array $extra
+ * @return int Total number of Grouper Records returned by Grouper API call
+ *
+ */
+ public function paginateCount($conditions = null, $recursive = 0,
+ $extra = array()) {
+ return $this->totalRecords;
+ }
+}
diff --git a/Model/GrouperLite.php b/Model/GrouperLite.php
new file mode 100644
index 0000000..dec2df3
--- /dev/null
+++ b/Model/GrouperLite.php
@@ -0,0 +1,58 @@
+ array(
+ 'Grouper groups' => array(
+ 'controller' => "groupergroups",
+ 'action' => "groupoptin"
+ )
+ )*/
+ );
+ }
+
+
+}
\ No newline at end of file
diff --git a/Model/GrouperLiteAppModel.php b/Model/GrouperLiteAppModel.php
new file mode 100644
index 0000000..18cbb6d
--- /dev/null
+++ b/Model/GrouperLiteAppModel.php
@@ -0,0 +1,32 @@
+element('GrouperLite.base-styles');
+
+?>
+
+
+
+
+
+
+
+
+ Html->image('GrouperLite.grouper-logo.png', array('class' => 'logo-fluid')); ?>
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/View/CoGrouperLites/edit.ctp b/View/CoGrouperLites/edit.ctp
new file mode 100644
index 0000000..414142a
--- /dev/null
+++ b/View/CoGrouperLites/edit.ctp
@@ -0,0 +1,71 @@
+name;
+ $req = Inflector::singularize($model);
+
+ // Add page title & page buttons
+ $params = array();
+ $params['title'] = $title_for_layout;
+
+ // For Authenticators during enrollment
+ if(!empty($vv_co_enrollment_authenticator)
+ && ($vv_co_enrollment_authenticator['CoEnrollmentAuthenticator']['required'] == RequiredEnum::Optional)
+ && !empty($this->request->params['named']['onFinish'])) {
+ $params['topLinks'][] = $this->Html->link(_txt('op.skip'),
+ urldecode($this->request->params['named']['onFinish']),
+ array('class' => 'forwardbutton'));
+ }
+
+ if(!empty($this->plugin)) {
+ if(file_exists(APP . "Plugin/" . $this->plugin . "/View/" . $model . "/buttons.inc")) {
+ include(APP . "Plugin/" . $this->plugin . "/View/" . $model . "/buttons.inc");
+ } elseif(file_exists(LOCAL . "Plugin/" . $this->plugin . "/View/" . $model . "/buttons.inc")) {
+ include(LOCAL . "Plugin/" . $this->plugin . "/View/" . $model . "/buttons.inc");
+ }
+ } else {
+ if(file_exists(APP . "View/" . $model . "/buttons.inc")) {
+ include(APP . "View/" . $model . "/buttons.inc");
+ }
+ }
+ print $this->element("pageTitleAndButtons", $params);
+
+ $submit_label = _txt('op.save');
+ print $this->Form->create($req, array('inputDefaults' => array('label' => false, 'div' => false)));
+ if(!empty($this->plugin)) {
+ if(file_exists(APP . "Plugin/" . $this->plugin . "/View/" . $model . "/fields.inc")) {
+ include(APP . "Plugin/" . $this->plugin . "/View/" . $model . "/fields.inc");
+ } elseif(file_exists(LOCAL . "Plugin/" . $this->plugin . "/View/" . $model . "/fields.inc")) {
+ include(LOCAL . "Plugin/" . $this->plugin . "/View/" . $model . "/fields.inc");
+ }
+ } else {
+ include(APP . "View/" . $model . "/fields.inc");
+ }
+ print $this->Form->end();
+?>
diff --git a/View/CoGrouperLites/fields.inc b/View/CoGrouperLites/fields.inc
new file mode 100644
index 0000000..3811a43
--- /dev/null
+++ b/View/CoGrouperLites/fields.inc
@@ -0,0 +1,209 @@
+
+action === "add" && $permissions['add']);
+$editable = ($this->action === "edit" && $permissions['edit']);
+
+$e = (!$addable && !$editable) ? false : true;
+
+// We shouldn't get here if we don't have at least read permission, but check just in case
+
+if (!$e && !$permissions['view']) {
+ return false;
+}
+
+// Add breadcrumbs
+print $this->element("coCrumb");
+
+$args = array();
+$args['plugin'] = null;
+$args['controller'] = 'co_dashboards';
+$args['action'] = 'index';
+$args['co'] = $cur_co['Co']['id'];
+$this->Html->addCrumb(_txt('ct.co_dashboards.pl'), $args);
+
+$args = array();
+$args['plugin'] = null;
+$args['controller'] = 'co_dashboards';
+$args['action'] = 'edit';
+$args[] = $co_grouper_lites[0]['CoDashboardWidget']['co_dashboard_id'];
+$this->Html->addCrumb(_txt('ct.co_dashboards.1'), $args);
+
+$args = array();
+$args['plugin'] = null;
+$args['controller'] = 'co_dashboard_widgets';
+$args['action'] = 'index';
+$args['codashboard'] = $co_grouper_lites[0]['CoDashboardWidget']['co_dashboard_id'];
+$this->Html->addCrumb(_txt('ct.co_dashboard_widgets.1'), $args);
+
+$crumbTxt = _txt('op.' . $this->action . '-a', array(filter_var($co_grouper_lites[0]['CoDashboardWidget']['description'], FILTER_SANITIZE_SPECIAL_CHARS)));
+$this->Html->addCrumb($crumbTxt);
+
+print $this->Form->hidden('co_dashboard_widget_id', array('default' => $vv_dwid)) . "\n";
+
+?>
+
+
+
\ No newline at end of file
diff --git a/View/Elements/Components/groupattributes.ctp b/View/Elements/Components/groupattributes.ctp
new file mode 100644
index 0000000..e23f097
--- /dev/null
+++ b/View/Elements/Components/groupattributes.ctp
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/View/Elements/Components/groupproperties.ctp b/View/Elements/Components/groupproperties.ctp
new file mode 100644
index 0000000..c21099a
--- /dev/null
+++ b/View/Elements/Components/groupproperties.ctp
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/View/Elements/Components/navigation-emaillists.ctp b/View/Elements/Components/navigation-emaillists.ctp
new file mode 100644
index 0000000..7597315
--- /dev/null
+++ b/View/Elements/Components/navigation-emaillists.ctp
@@ -0,0 +1,68 @@
+
+
+
+ Html->link(
+ _txt('pl.grouperlite.nav.emaillists-join'),
+ array(
+ 'controller' => 'GrouperGroups',
+ 'action' => 'emaillistsoptin'
+ ),
+ array('class' => array('nav-link', $active == 'emaillistsoptin' ? 'active' : ''))
+ )
+ ?>
+
+
+ Html->link(
+ _txt('pl.grouperlite.nav.emaillists-member'),
+ array(
+ 'controller' => 'GrouperGroups',
+ 'action' => 'emaillistsmember'
+ ),
+ array('class' => array('nav-link', $active == 'emaillistsmember' ? 'active' : ''))
+ )
+ ?>
+
+
+ Html->link(
+ _txt('pl.grouperlite.nav.emaillists-manage'),
+ array(
+ 'controller' => 'GrouperGroups',
+ 'action' => 'emaillistsmanage'
+ ),
+ array('class' => array('nav-link', $active == 'emaillistsmanage' ? 'active' : ''))
+ )
+ ?>
+
+
+
+
+ Html->url(
+ array(
+ 'controller' => 'groupergroups',
+ 'action' => 'groupcreatetemplateform'
+ )
+ );
+ ?>
+
+
+
+
+
+
+ Html->url(
+ array(
+ 'controller' => 'groupergroups',
+ 'action' => 'groupoptin'
+ )
+ );
+ ?>
+
+
+
+
+
\ No newline at end of file
diff --git a/View/Elements/Components/navigation-groups.ctp b/View/Elements/Components/navigation-groups.ctp
new file mode 100644
index 0000000..636e6c4
--- /dev/null
+++ b/View/Elements/Components/navigation-groups.ctp
@@ -0,0 +1,68 @@
+
+
+
+ Html->link(
+ _txt('pl.grouperlite.nav.groups-can-join'),
+ array(
+ 'controller' => 'groupergroups',
+ 'action' => 'groupoptin'
+ ),
+ array('class' => array('nav-link', $active == 'groupoptin' ? 'active' : ''))
+ )
+ ?>
+
+
+ Html->link(
+ _txt('pl.grouperlite.nav.memberships'),
+ array(
+ 'controller' => 'groupergroups',
+ 'action' => 'groupmember'
+ ),
+ array('class' => array('nav-link', $active == 'groupmember' ? 'active' : ''))
+ )
+ ?>
+
+
+ Html->link(
+ _txt('pl.grouperlite.nav.groups-presided'),
+ array(
+ 'controller' => 'groupergroups',
+ 'action' => 'groupowner'
+ ),
+ array('class' => array('nav-link', $active == 'groupowner' ? 'active' : ''))
+ )
+ ?>
+
+
+
+
+ Html->url(
+ array(
+ 'controller' => 'groupergroups',
+ 'action' => 'groupcreateform'
+ )
+ );
+ ?>
+
+
+
+
+
+
+ Html->url(
+ array(
+ 'controller' => 'groupergroups',
+ 'action' => 'emaillistsoptin'
+ )
+ );
+ ?>
+
+
+
+
+
\ No newline at end of file
diff --git a/View/Elements/Components/optAction.ctp b/View/Elements/Components/optAction.ctp
new file mode 100644
index 0000000..2928dca
--- /dev/null
+++ b/View/Elements/Components/optAction.ctp
@@ -0,0 +1,10 @@
+Form->create(false, array(
+ 'url' => array('controller' => 'groupergroups', 'action' => $action),
+ 'class' => 'd-flex justify-content-center'
+)); ?>
+Form->hidden('GroupName', array('default' => $group)); ?>
+
+
+
+
+Form->end(); ?>
\ No newline at end of file
diff --git a/View/Elements/Components/search.ctp b/View/Elements/Components/search.ctp
new file mode 100644
index 0000000..ca42a44
--- /dev/null
+++ b/View/Elements/Components/search.ctp
@@ -0,0 +1,49 @@
+
+Form->create(false, array(
+ 'url' => array('controller' => 'groupergroups', 'action' => $active),
+ 'class' => 'search mb-4'
+)); ?>
+
+Form->end(); ?>
\ No newline at end of file
diff --git a/View/Elements/Components/subscriberList.ctp b/View/Elements/Components/subscriberList.ctp
new file mode 100644
index 0000000..57e98e3
--- /dev/null
+++ b/View/Elements/Components/subscriberList.ctp
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/View/Elements/base-styles.ctp b/View/Elements/base-styles.ctp
new file mode 100644
index 0000000..00ead17
--- /dev/null
+++ b/View/Elements/base-styles.ctp
@@ -0,0 +1,57 @@
+
\ No newline at end of file
diff --git a/View/Elements/empty b/View/Elements/empty
new file mode 100644
index 0000000..e69de29
diff --git a/View/Elements/pagination.ctp b/View/Elements/pagination.ctp
new file mode 100644
index 0000000..1f0d819
--- /dev/null
+++ b/View/Elements/pagination.ctp
@@ -0,0 +1,141 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/View/GrouperGroups/base.ctp b/View/GrouperGroups/base.ctp
new file mode 100644
index 0000000..6b393b3
--- /dev/null
+++ b/View/GrouperGroups/base.ctp
@@ -0,0 +1,19 @@
+element('GrouperLite.base-styles');
+print $this->Html->css('GrouperLite.co-grouper-plugin') . "\n ";
+?>
+
+Html->script('GrouperLite.typeahead.bundle.js') . "\n ";
+
+$this->Html->addCrumb(_txt('pl.grouperlite.crumb.root'), array('controller' => 'groupergroups', 'action' => 'groupoptin'), array('prepend' => true));
+?>
+
+
+
+ Html->image('GrouperLite.Grouper.jpg', array('class' => 'img-fluid mr-2', 'style' => 'height: 50px')); ?>
+
+
+
+ fetch('content'); ?>
+
\ No newline at end of file
diff --git a/View/GrouperGroups/emaillistinfo.ctp b/View/GrouperGroups/emaillistinfo.ctp
new file mode 100644
index 0000000..daa4379
--- /dev/null
+++ b/View/GrouperGroups/emaillistinfo.ctp
@@ -0,0 +1,70 @@
+extend('/GrouperGroups/base'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ element('Components/groupproperties', array(
+ 'group' => $groupergroupsdetail
+ )); ?>
+
+
+
+
+ element('Components/groupattributes', array(
+ 'attributes' => $groupergroupsdetail['attributes'],
+ 'baseUrl' => $attrUrlBase
+ )); ?>
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/View/GrouperGroups/emaillistsmanage.ctp b/View/GrouperGroups/emaillistsmanage.ctp
new file mode 100644
index 0000000..3c3c41c
--- /dev/null
+++ b/View/GrouperGroups/emaillistsmanage.ctp
@@ -0,0 +1,48 @@
+extend('/GrouperGroups/base'); ?>
+Html->addCrumb('Email lists I manage'); ?>
+element('Components/navigation-emaillists', array('active' => 'emaillistsmanage', 'create' => true)); ?>
+element('Components/search', array('active' => 'emaillistsmanaged')); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Html->link(
+ isset($group['name']) ? $group['domain'] . ':' . $group['name'] : "--",
+ array(
+ 'controller' => 'groupergroups',
+ 'action' => 'emaillistinfo',
+ '?' => array('groupname' => urlencode($group['name']))
+ )
+ ) ?>
+
+
+ (10) |
+ (2) |
+ (1)
+
+
+
+
+
+
+
+ */ ?>
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/View/GrouperGroups/emaillistsmember.ctp b/View/GrouperGroups/emaillistsmember.ctp
new file mode 100644
index 0000000..ed1264a
--- /dev/null
+++ b/View/GrouperGroups/emaillistsmember.ctp
@@ -0,0 +1,38 @@
+extend('/GrouperGroups/base'); ?>
+Html->addCrumb('Email lists I can join'); ?>
+element('Components/navigation-emaillists', array('active' => 'emaillistsmember')); ?>
+element('Components/search', array('active' => 'emaillistsmember')); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Html->link(
+ isset($group['name']) ? $group['domain'].':'.$group['name'] : "No Name",
+ array(
+ 'controller' => 'groupergroups',
+ 'action' => 'emaillistinfo',
+ '?' => array('groupname' => urlencode($group['name']))
+ )
+ ) ?>
+
+
+ element('GrouperLite.Components/optAction', array(
+ 'member' => $group['optedin'],
+ 'action' => 'leavegroup',
+ 'group' => $group['name']
+ )) : ''; ?>
+
+
+
+
+
+
diff --git a/View/GrouperGroups/emaillistsoptin.ctp b/View/GrouperGroups/emaillistsoptin.ctp
new file mode 100644
index 0000000..a980633
--- /dev/null
+++ b/View/GrouperGroups/emaillistsoptin.ctp
@@ -0,0 +1,37 @@
+extend('/GrouperGroups/base'); ?>
+Html->addCrumb('Email lists I can join'); ?>
+element('Components/navigation-emaillists', array('active' => 'emaillistsoptin')); ?>
+element('Components/search', array('active' => 'emaillistsoptin')); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Html->link(
+ isset($group['name']) ? $group['domain'] . ':' . $group['name'] : "No Name",
+ array(
+ 'controller' => 'groupergroups',
+ 'action' => 'emaillistinfo',
+ '?' => array('groupname' => urlencode($group['name']))
+ )
+ ) ?>
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/View/GrouperGroups/groupcreate.ctp b/View/GrouperGroups/groupcreate.ctp
new file mode 100644
index 0000000..c3bf238
--- /dev/null
+++ b/View/GrouperGroups/groupcreate.ctp
@@ -0,0 +1,35 @@
+extend('/GrouperGroups/base');
+$this->Html->addCrumb('Create a Group');
+
+$model = $this->name;
+?>
+
+
+
+
+
+
Stems coming in are: Id=stem
+
+
Value= Display=
+
+
+ Form->create(false, array(
+ 'url' => array('controller' => 'groupergroups', 'action' => 'groupcreate')
+ )); ?>
+ plugin)) {
+ if (file_exists(APP . "Plugin/" . $this->plugin . "/View/" . $model . "/groupfields.inc")) {
+ include(APP . "Plugin/" . $this->plugin . "/View/" . $model . "/groupfields.inc");
+ } elseif (file_exists(LOCAL . "Plugin/" . $this->plugin . "/View/" . $model . "/groupfields.inc")) {
+ include(LOCAL . "Plugin/" . $this->plugin . "/View/" . $model . "/groupfields.inc");
+ }
+ } else {
+ include(APP . "View/" . $model . "/groupfields.inc");
+ }
+ ?>
+ Form->end(); ?>
+
+
+
+
\ No newline at end of file
diff --git a/View/GrouperGroups/groupcreatetemplate.ctp b/View/GrouperGroups/groupcreatetemplate.ctp
new file mode 100644
index 0000000..c7db590
--- /dev/null
+++ b/View/GrouperGroups/groupcreatetemplate.ctp
@@ -0,0 +1,30 @@
+extend('/GrouperGroups/base');
+$this->Html->addCrumb('Create a Templated Group');
+
+$model = $this->name;
+?>
+
+
+
+
+
+ Form->create(false, array(
+ 'url' => array('controller' => 'groupergroups', 'action' => 'groupcreatetemplate')
+ )); ?>
+ plugin)) {
+ if (file_exists(APP . "Plugin/" . $this->plugin . "/View/" . $model . "/templatefields.inc")) {
+ include(APP . "Plugin/" . $this->plugin . "/View/" . $model . "/templatefields.inc");
+ } elseif (file_exists(LOCAL . "Plugin/" . $this->plugin . "/View/" . $model . "/templatefields.inc")) {
+ include(LOCAL . "Plugin/" . $this->plugin . "/View/" . $model . "/templatefields.inc");
+ }
+ } else {
+ include(APP . "View/" . $model . "/templatefields.inc");
+ }
+ ?>
+ Form->end(); ?>
+
+
+
+
diff --git a/View/GrouperGroups/groupfields.inc b/View/GrouperGroups/groupfields.inc
new file mode 100644
index 0000000..ceb7111
--- /dev/null
+++ b/View/GrouperGroups/groupfields.inc
@@ -0,0 +1,210 @@
+
+
+
+
+
+
+
+
+ Form->label(false, _txt('pl.grouperlite.form.group.stem.label'), array(
+ 'for' => 'folder',
+ 'class' => "col-sm-3 col-form-label"
+ )); ?>
+
+ Form->input('grouptemplate', array(
+ 'label' => false,
+ 'class' => 'form-control',
+ 'id' => 'folder',
+ 'default' => 'sandbox:org:unicon:',
+ 'readonly' => true
+ )); ?>
+
+
+
+
+
+ */ ?>
+
+
+
+ echo $this->Form->label(false, _txt('pl.grouperlite.form.group.subs.label'), array(
+ 'for' => 'subscribers',
+ 'class' => "col-sm-3 col-form-label"
+ )); ?>
+
+ Form->input('grouptemplate', array(
+ 'label' => false,
+ 'class' => 'typeahead form-control',
+ 'id' => 'name',
+ 'placeholder' => _txt('pl.grouperlite.form.group.subs.placeholder')
+ )); ?>
+
+
+
+
+
+ */ ?>
+
+
\ No newline at end of file
diff --git a/View/GrouperGroups/groupinfo.ctp b/View/GrouperGroups/groupinfo.ctp
new file mode 100644
index 0000000..c668513
--- /dev/null
+++ b/View/GrouperGroups/groupinfo.ctp
@@ -0,0 +1,71 @@
+extend('/GrouperGroups/base'); ?>
+Html->addCrumb('Group configuration'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ element('Components/groupproperties', array(
+ 'group' => $groupergroupsdetail
+ )); ?>
+
+
+
+
+ element('Components/groupattributes', array(
+ 'attributes' => $groupergroupsdetail['attributes'],
+ 'baseUrl' => $attrUrlBase
+ )); ?>
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/View/GrouperGroups/groupmember.ctp b/View/GrouperGroups/groupmember.ctp
new file mode 100644
index 0000000..561fa98
--- /dev/null
+++ b/View/GrouperGroups/groupmember.ctp
@@ -0,0 +1,55 @@
+extend('/GrouperGroups/base'); ?>
+Html->addCrumb(_txt('pl.grouperlite.nav.memberships')); ?>
+element('GrouperLite.Components/navigation-groups', array('active' => 'groupmember')); ?>
+element('GrouperLite.Components/search', array('active' => 'groupmember')); ?>
+
+ element("pagination", array(
+ 'goto' => false,
+ 'limit' => false,
+ 'numbers' => false,
+ 'counter' => true,
+ 'class' => 'counter'
+ )); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ element('GrouperLite.Components/optAction', array(
+ 'member' => $group['optedin'],
+ 'action' => 'leavegroup',
+ 'group' => $group['name']
+ )) : ''; ?>
+
+
+
+
+
+
+
+ element("pagination", array(
+ 'goto' => false,
+ 'limit' => true,
+ 'numbers' => true,
+ 'counter' => false
+ )); ?>
+
+
+
+
+
\ No newline at end of file
diff --git a/View/GrouperGroups/groupoptin.ctp b/View/GrouperGroups/groupoptin.ctp
new file mode 100644
index 0000000..b972042
--- /dev/null
+++ b/View/GrouperGroups/groupoptin.ctp
@@ -0,0 +1,55 @@
+extend('/GrouperGroups/base'); ?>
+Html->addCrumb(_txt('pl.grouperlite.nav.groups-can-join')); ?>
+element('GrouperLite.Components/navigation-groups', array('active' => 'groupoptin')); ?>
+element('GrouperLite.Components/search', array('active' => 'groupoptin')); ?>
+
+ element("pagination", array(
+ 'goto' => false,
+ 'limit' => false,
+ 'numbers' => false,
+ 'counter' => true,
+ 'class' => 'counter'
+ )); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ element('GrouperLite.Components/optAction', array(
+ 'member' => $group['member'],
+ 'action' => 'joingroup',
+ 'group' => $group['name']
+ )); ?>
+
+
+
+
+
+
+
+ element("pagination", array(
+ 'goto' => false,
+ 'limit' => true,
+ 'numbers' => true,
+ 'counter' => false
+ )); ?>
+
+
+
+
+
\ No newline at end of file
diff --git a/View/GrouperGroups/groupowner.ctp b/View/GrouperGroups/groupowner.ctp
new file mode 100644
index 0000000..593b2b3
--- /dev/null
+++ b/View/GrouperGroups/groupowner.ctp
@@ -0,0 +1,62 @@
+extend('/GrouperGroups/base'); ?>
+Html->addCrumb(_txt('pl.grouperlite.nav.groups-presided')); ?>
+element('Components/navigation-groups', array('active' => 'groupowner')); ?>
+element('Components/search', array('active' => 'groupowner')); ?>
+
+ element("pagination", array(
+ 'goto' => false,
+ 'limit' => false,
+ 'numbers' => false,
+ 'counter' => true,
+ 'class' => 'counter'
+ )); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ element("pagination", array(
+ 'goto' => false,
+ 'limit' => true,
+ 'numbers' => true,
+ 'counter' => false
+ )); ?>
+
+
+
+
+
\ No newline at end of file
diff --git a/View/GrouperGroups/joingroup.ctp b/View/GrouperGroups/joingroup.ctp
new file mode 100644
index 0000000..e4d9672
--- /dev/null
+++ b/View/GrouperGroups/joingroup.ctp
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/View/GrouperGroups/leavegroup.ctp b/View/GrouperGroups/leavegroup.ctp
new file mode 100644
index 0000000..2a3df51
--- /dev/null
+++ b/View/GrouperGroups/leavegroup.ctp
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/View/GrouperGroups/templatefields.inc b/View/GrouperGroups/templatefields.inc
new file mode 100644
index 0000000..47b7d85
--- /dev/null
+++ b/View/GrouperGroups/templatefields.inc
@@ -0,0 +1,249 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/View/GrouperGroups/users.json b/View/GrouperGroups/users.json
new file mode 100644
index 0000000..6e08fee
--- /dev/null
+++ b/View/GrouperGroups/users.json
@@ -0,0 +1,344 @@
+[
+ {
+ "name": "Galena Munoz",
+ "email": "ipsum.cursus.vestibulum@urnaconvalliserat.net",
+ "cou": "Active, Member",
+ "org": "Eu Corporation"
+ },
+ {
+ "name": "Doris Santos",
+ "email": "vitae@non.org",
+ "cou": "Active, Filesource, Member",
+ "org": "Aliquam LLC"
+ },
+ {
+ "name": "Alma English",
+ "email": "pede.Cum.sociis@telluseu.net",
+ "cou": "Member, Active, Writer, Filesource",
+ "org": "Consectetuer Corp."
+ },
+ {
+ "name": "Halee Finley",
+ "email": "in.consectetuer.ipsum@parturientmontes.co.uk",
+ "cou": "Member",
+ "org": "Felis Ltd"
+ },
+ {
+ "name": "Austin Hardy",
+ "email": "iaculis@Donecegestas.co.uk",
+ "cou": "Writer",
+ "org": "Nonummy LLP"
+ },
+ {
+ "name": "Victor Powell",
+ "email": "nisi@eget.ca",
+ "cou": "Member, Active",
+ "org": "Praesent Interdum Ligula PC"
+ },
+ {
+ "name": "Baxter Estrada",
+ "email": "in.consequat.enim@vestibulum.co.uk",
+ "cou": "",
+ "org": "In Molestie Tortor Corporation"
+ },
+ {
+ "name": "Carlos Rose",
+ "email": "elit.elit@non.co.uk",
+ "cou": "Member, Writer, Filesource, Active",
+ "org": "Risus Nulla Eget LLP"
+ },
+ {
+ "name": "Ishmael Patton",
+ "email": "leo.Morbi@mollisnoncursus.co.uk",
+ "cou": "Writer, Filesource",
+ "org": "A Enim Suspendisse Consulting"
+ },
+ {
+ "name": "Ryder Tanner",
+ "email": "varius@rutrum.org",
+ "cou": "Writer, Filesource",
+ "org": "Ornare Elit Elit Consulting"
+ },
+ {
+ "name": "Dane Rollins",
+ "email": "Curabitur.consequat.lectus@Naminterdum.ca",
+ "cou": "Member",
+ "org": "Ut Pellentesque Consulting"
+ },
+ {
+ "name": "Brian Kaufman",
+ "email": "aliquet.vel@orciluctuset.edu",
+ "cou": "Filesource, Active",
+ "org": "Justo Eu Arcu Inc."
+ },
+ {
+ "name": "Lester Price",
+ "email": "Integer@idlibero.edu",
+ "cou": "Writer, Member, Active, Filesource",
+ "org": "Dapibus Rutrum Justo Incorporated"
+ },
+ {
+ "name": "Helen Cardenas",
+ "email": "Curabitur.egestas.nunc@quisdiam.org",
+ "cou": "Member, Active, Filesource",
+ "org": "Faucibus Morbi Vehicula Associates"
+ },
+ {
+ "name": "Hop Holloway",
+ "email": "non.arcu.Vivamus@nibhvulputate.org",
+ "cou": "Filesource, Active, Member",
+ "org": "Nunc Sed Pede Ltd"
+ },
+ {
+ "name": "Madeson Hendrix",
+ "email": "viverra@purus.com",
+ "cou": "",
+ "org": "Neque Venenatis Consulting"
+ },
+ {
+ "name": "Amity Navarro",
+ "email": "tincidunt.Donec@inconsequat.com",
+ "cou": "Writer",
+ "org": "Pretium Aliquet Consulting"
+ },
+ {
+ "name": "Cameron Booth",
+ "email": "nulla.magna@Quisqueporttitor.net",
+ "cou": "",
+ "org": "Fusce Associates"
+ },
+ {
+ "name": "Sybil Burgess",
+ "email": "Fusce.feugiat@sem.org",
+ "cou": "Writer, Member, Filesource, Active",
+ "org": "Curabitur Corporation"
+ },
+ {
+ "name": "Otto Cantrell",
+ "email": "Ut@nonarcu.org",
+ "cou": "",
+ "org": "Cursus PC"
+ },
+ {
+ "name": "Mannix Obrien",
+ "email": "eu.neque@egestasDuisac.ca",
+ "cou": "",
+ "org": "Amet Faucibus Corp."
+ },
+ {
+ "name": "Marcia Gill",
+ "email": "molestie.in.tempus@posuere.edu",
+ "cou": "",
+ "org": "Et Corp."
+ },
+ {
+ "name": "Tanek Figueroa",
+ "email": "diam@arcuSed.org",
+ "cou": "Active",
+ "org": "Bibendum Donec Ltd"
+ },
+ {
+ "name": "Ira Lynn",
+ "email": "aliquet@consequatpurusMaecenas.edu",
+ "cou": "Filesource",
+ "org": "Massa Suspendisse Eleifend Corporation"
+ },
+ {
+ "name": "Lesley Pittman",
+ "email": "iaculis.odio@Phasellus.edu",
+ "cou": "Member, Writer",
+ "org": "Urna Et Arcu PC"
+ },
+ {
+ "name": "Len Ryan",
+ "email": "ut.mi.Duis@sitamet.org",
+ "cou": "Writer, Filesource, Active",
+ "org": "Vestibulum Accumsan PC"
+ },
+ {
+ "name": "Sarah Morton",
+ "email": "dis.parturient@rhoncusProin.org",
+ "cou": "Active",
+ "org": "Amet Consectetuer PC"
+ },
+ {
+ "name": "Marny Hewitt",
+ "email": "dapibus@orciquis.ca",
+ "cou": "Active, Writer",
+ "org": "Ultricies Consulting"
+ },
+ {
+ "name": "Abel Lyons",
+ "email": "Proin.eget.odio@enim.org",
+ "cou": "Writer",
+ "org": "Amet Lorem Corporation"
+ },
+ {
+ "name": "Slade Schneider",
+ "email": "ante.dictum.cursus@turpisAliquamadipiscing.co.uk",
+ "cou": "",
+ "org": "Lacinia Company"
+ },
+ {
+ "name": "Ashton Arnold",
+ "email": "velit.justo@velitin.edu",
+ "cou": "",
+ "org": "Diam Ltd"
+ },
+ {
+ "name": "Phelan Goff",
+ "email": "ullamcorper.viverra.Maecenas@Donecporttitortellus.ca",
+ "cou": "",
+ "org": "Faucibus Id Libero Institute"
+ },
+ {
+ "name": "Graham Underwood",
+ "email": "Nulla.dignissim.Maecenas@Donecsollicitudin.ca",
+ "cou": "Filesource, Member",
+ "org": "Duis Gravida Praesent Ltd"
+ },
+ {
+ "name": "Alisa Scott",
+ "email": "ipsum.primis.in@erat.co.uk",
+ "cou": "Writer, Active",
+ "org": "Vivamus Non Lorem Inc."
+ },
+ {
+ "name": "Asher Mccray",
+ "email": "semper.auctor@nibhAliquamornare.edu",
+ "cou": "Writer, Member",
+ "org": "Orci Corporation"
+ },
+ {
+ "name": "Hanna Salas",
+ "email": "feugiat@purusactellus.net",
+ "cou": "",
+ "org": "Lectus Convallis Industries"
+ },
+ {
+ "name": "Jeremy Riley",
+ "email": "Ut.sagittis@necante.ca",
+ "cou": "Writer",
+ "org": "Luctus Curabitur LLP"
+ },
+ {
+ "name": "Violet Trevino",
+ "email": "vestibulum@nec.edu",
+ "cou": "",
+ "org": "Ligula Aenean Gravida Consulting"
+ },
+ {
+ "name": "Carissa Myers",
+ "email": "non@rhoncusDonec.co.uk",
+ "cou": "Member, Filesource",
+ "org": "Tempor Corporation"
+ },
+ {
+ "name": "Kadeem Osborn",
+ "email": "nulla.In@aliquameros.ca",
+ "cou": "Filesource, Active, Member, Writer",
+ "org": "Tempus Risus Donec PC"
+ },
+ {
+ "name": "Kiayada England",
+ "email": "ultrices.posuere@lectus.net",
+ "cou": "",
+ "org": "Orci Institute"
+ },
+ {
+ "name": "Connor Gardner",
+ "email": "diam.eu@tortorIntegeraliquam.org",
+ "cou": "Member, Active, Writer",
+ "org": "Vulputate Nisi Sem LLP"
+ },
+ {
+ "name": "Alfonso Casey",
+ "email": "montes.nascetur.ridiculus@risus.net",
+ "cou": "Member",
+ "org": "Sed Eu Nibh Consulting"
+ },
+ {
+ "name": "Avye Raymond",
+ "email": "mauris.sapien@tempusrisusDonec.co.uk",
+ "cou": "Active, Writer, Filesource, Member",
+ "org": "Amet Ante Incorporated"
+ },
+ {
+ "name": "Quamar Cross",
+ "email": "elit.pretium@Sedid.org",
+ "cou": "",
+ "org": "Dapibus PC"
+ },
+ {
+ "name": "Justine Kemp",
+ "email": "diam@ornare.org",
+ "cou": "Writer, Filesource",
+ "org": "Libero At Auctor Associates"
+ },
+ {
+ "name": "Blair Rush",
+ "email": "at.velit.Cras@pulvinar.ca",
+ "cou": "",
+ "org": "Nulla Tempor Associates"
+ },
+ {
+ "name": "Jael Travis",
+ "email": "ut.dolor.dapibus@vestibulumneceuismod.ca",
+ "cou": "",
+ "org": "Facilisis Lorem LLC"
+ },
+ {
+ "name": "Donovan Patel",
+ "email": "bibendum.ullamcorper@Sedeu.co.uk",
+ "cou": "Member, Filesource, Writer, Active",
+ "org": "Ipsum Sodales Incorporated"
+ },
+ {
+ "name": "Sylvester Brady",
+ "email": "amet@consequatauctornunc.net",
+ "cou": "",
+ "org": "Semper Nam Limited"
+ },
+ {
+ "name": "Yuri Eaton",
+ "email": "Nullam.lobortis.quam@Etiamligulatortor.com",
+ "cou": "",
+ "org": "Eu Elit Nulla LLP"
+ },
+ {
+ "name": "Bree Harmon",
+ "email": "libero.Integer@maurisaliquam.ca",
+ "cou": "Filesource, Active, Member, Writer",
+ "org": "Dui In Sodales PC"
+ },
+ {
+ "name": "Tanek Tucker",
+ "email": "arcu@Craslorem.org",
+ "cou": "Active, Member, Filesource",
+ "org": "Nisl Arcu Company"
+ },
+ {
+ "name": "Jermaine Stevens",
+ "email": "luctus@Donec.org",
+ "cou": "Active, Writer",
+ "org": "Pellentesque Sed Dictum Corp."
+ },
+ {
+ "name": "Ira Robinson",
+ "email": "malesuada.vel@lobortisClass.ca",
+ "cou": "Active, Member",
+ "org": "Ac Sem Ut Consulting"
+ },
+ {
+ "name": "Jocelyn Fulton",
+ "email": "tristique.senectus.et@faucibusorci.co.uk",
+ "cou": "Active, Member, Filesource, Writer",
+ "org": "Ante Ipsum Institute"
+ },
+ {
+ "name": "Tatyana Kelly",
+ "email": "a.facilisis.non@tortorat.ca",
+ "cou": "",
+ "org": "Donec Feugiat Limited"
+ }
+]
\ No newline at end of file
diff --git a/View/Helper/empty b/View/Helper/empty
new file mode 100644
index 0000000..e69de29
diff --git a/View/Layouts/Emails/html/default.ctp b/View/Layouts/Emails/html/default.ctp
new file mode 100644
index 0000000..747994a
--- /dev/null
+++ b/View/Layouts/Emails/html/default.ctp
@@ -0,0 +1,27 @@
+
+
+
+
+ fetch('title'); ?>
+
+
+ fetch('content'); ?>
+
+ This email was sent using the CakePHP Framework
+
+
\ No newline at end of file
diff --git a/View/Layouts/Emails/text/default.ctp b/View/Layouts/Emails/text/default.ctp
new file mode 100644
index 0000000..82a1e19
--- /dev/null
+++ b/View/Layouts/Emails/text/default.ctp
@@ -0,0 +1,19 @@
+
+fetch('content'); ?>
+
+This email was sent using the CakePHP Framework, https://cakephp.org.
diff --git a/View/Layouts/ajax.ctp b/View/Layouts/ajax.ctp
new file mode 100644
index 0000000..7e14112
--- /dev/null
+++ b/View/Layouts/ajax.ctp
@@ -0,0 +1,17 @@
+
+fetch('content'); ?>
diff --git a/View/Layouts/empty b/View/Layouts/empty
new file mode 100644
index 0000000..e69de29
diff --git a/View/Layouts/error.ctp b/View/Layouts/error.ctp
new file mode 100644
index 0000000..9eea68b
--- /dev/null
+++ b/View/Layouts/error.ctp
@@ -0,0 +1,59 @@
+
+
+
+
+ Html->charset(); ?>
+
+ :
+ fetch('title'); ?>
+
+ Html->meta('icon');
+
+ echo $this->Html->css('cake.generic');
+
+ echo $this->fetch('GrouperLite.meta');
+ echo $this->fetch('GrouperLite.css');
+ echo $this->fetch('GrouperLite.script');
+ ?>
+
+
+
+
+
+
+ Session->flash(); ?>
+
+ fetch('content'); ?>
+
+
+
+ element('sql_dump'); ?>
+
+
diff --git a/View/Layouts/flash.ctp b/View/Layouts/flash.ctp
new file mode 100644
index 0000000..226d3c1
--- /dev/null
+++ b/View/Layouts/flash.ctp
@@ -0,0 +1,39 @@
+
+
+
+
+Html->charset(); ?>
+
+
+', $pause, $url);
+endif;
+?>
+
+
+
+
+ Html->link($message, $url); ?>
+
+
+
diff --git a/View/Layouts/js/default.ctp b/View/Layouts/js/default.ctp
new file mode 100644
index 0000000..f694538
--- /dev/null
+++ b/View/Layouts/js/default.ctp
@@ -0,0 +1,2 @@
+fetch('script'); ?>
+
diff --git a/View/Layouts/rss/default.ctp b/View/Layouts/rss/default.ctp
new file mode 100644
index 0000000..60a5365
--- /dev/null
+++ b/View/Layouts/rss/default.ctp
@@ -0,0 +1,13 @@
+fetch('title');
+endif;
+
+echo $this->Rss->document(
+ $this->Rss->channel(
+ array(), $channel, $this->fetch('content')
+ )
+);
diff --git a/View/Layouts/xml/default.ctp b/View/Layouts/xml/default.ctp
new file mode 100644
index 0000000..fbd5ee0
--- /dev/null
+++ b/View/Layouts/xml/default.ctp
@@ -0,0 +1 @@
+fetch('content'); ?>
diff --git a/webroot/css/co-grouper-base.css b/webroot/css/co-grouper-base.css
new file mode 100644
index 0000000..8080b7f
--- /dev/null
+++ b/webroot/css/co-grouper-base.css
@@ -0,0 +1,28 @@
+:root {
+ --red: #CC3333;
+ --blue: #1D7AB4;
+ --green: #22AA22;
+ --teal: #1c6070;
+ --danger: var(--red);
+ --success: var(--green);
+ --primary: var(--teal);
+}
+
+.grouper .btn.btn-primary {
+ background-color: var(--primary);
+ border-color: var(--primary);
+ box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12);
+}
+
+.grouper .btn.btn-success.btn-grouper {
+ background-color: var(--success);
+ border-color: var(--success);
+ box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12);
+}
+
+.grouper .btn.btn-danger.btn-grouper {
+ background-color: var(--danger);
+ border-color: var(--danger);
+ color: white;
+ box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12);
+}
\ No newline at end of file
diff --git a/webroot/css/co-grouper-plugin.css b/webroot/css/co-grouper-plugin.css
new file mode 100644
index 0000000..108088f
--- /dev/null
+++ b/webroot/css/co-grouper-plugin.css
@@ -0,0 +1,275 @@
+.badge.badge-primary {
+ background-color: var(--primary);
+ border-color: var(--primary);
+}
+
+.btn.btn-icon {
+ color: white;
+ padding: 0;
+ margin: 0;
+}
+
+.btn-sm {
+ padding: 0.6rem 1rem;
+ font-size: 1rem;
+ line-height: 1;
+ border-radius: .2rem;
+}
+
+a {
+ color: var(--primary);
+}
+
+#grouper-plugin .btn.btn-primary:hover {
+ background-color: black;
+ border-color: black;
+}
+
+#grouper-plugin .input-group .btn {
+ border-radius: 0;
+}
+
+#grouper-plugin .nav.nav-tabs .nav-item {
+ margin-right: 2px;
+}
+
+#grouper-plugin .nav.nav-tabs .nav-item a.nav-link {
+ padding: 0.75rem 2rem;
+ transition: background-color .2s cubic-bezier(.4, 0, .2, 1), color .2s cubic-bezier(.4, 0, .2, 1);
+ border-radius: 0;
+ ;
+}
+
+#grouper-plugin .nav.nav-tabs .nav-item a.nav-link:not(.active) {
+ color: white;
+ background: var(--primary);
+}
+
+#grouper-plugin .nav.nav-tabs .nav-item a.nav-link:not(.active):hover {
+ border-color: black;
+ background: black;
+}
+
+#grouper-plugin .nav.nav-tabs .nav-item a.nav-link:not(.active):focus {
+ border-color: transparent;
+}
+
+#grouper-plugin .table {}
+
+#grouper-plugin .table thead {}
+
+#grouper-plugin .table thead th {
+ background-color: var(--primary);
+ color: white;
+}
+
+#grouper-plugin .table thead th.group {}
+
+#grouper-plugin .table thead th.group.name {
+ min-width: 200px;
+}
+
+#grouper-plugin .table thead th.group.path {
+ min-width: 200px;
+}
+
+#grouper-plugin .table thead th.group.description {
+ width: auto;
+}
+
+#grouper-plugin .table thead th.group.action {
+ width: 20%;
+ max-width: 200px;
+}
+
+#grouper-plugin .table thead th.attr {
+ vertical-align: middle;
+}
+
+#grouper-plugin .table thead th.attr.action {
+ width: 100px;
+}
+
+#grouper-plugin .table thead th.group.open, #grouper-plugin .table thead th.group.status {
+ width: 80px;
+}
+
+#grouper-plugin .table tfoot tr th {
+ padding: 0;
+}
+
+#grouper-plugin .table thead th, #grouper-plugin .table tbody td {
+ padding: 0.75em;
+ text-align: left;
+ border-top: none;
+ border-right: 2px solid #fff;
+ border-bottom: 2px solid #fff;
+ font-weight: normal;
+ vertical-align: middle;
+}
+
+#grouper-plugin .form-control, #grouper-plugin .custom-select {
+ border-radius: 0;
+}
+
+#grouper-plugin .input-group.input-group-search input.form-control {
+ font-size: 1.25rem;
+ padding: 1.3rem 1rem;
+}
+
+#grouper-plugin .input-group.input-group-search .input-group-append .btn.btn-outline-secondary {
+ font-size: 1.25rem;
+ border-color: #ced4da;
+ color: inherit;
+}
+
+#grouper-plugin .input-group.input-group-search .input-group-append .btn.btn-outline-secondary:hover {
+ background-color: black;
+ color: white;
+}
+
+#grouper-plugin .btn.btn-link.btn-text {
+ padding: 0;
+ margin: 0;
+}
+
+#grouper-plugin .paging {
+ background: var(--primary);
+ color: white;
+}
+
+.logo-fluid {
+ max-width: 194px;
+ height: auto;
+}
+
+.badge {
+ font-size: 100%;
+}
+
+.twitter-typeahead {
+ width: 100%;
+}
+
+.tt-menu {
+ width: 422px;
+ margin: 12px 0;
+ padding: 8px 0;
+ background-color: #fff;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ -webkit-border-radius: 8px;
+ -moz-border-radius: 8px;
+ border-radius: 8px;
+ -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
+ -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
+ box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
+}
+
+.tt-suggestion {
+ padding: 3px 20px;
+ font-size: 18px;
+ line-height: 24px;
+}
+
+.tt-cursor {
+ background-color: #EEE;
+}
+
+.input-group>.input.text {
+ position: relative;
+ flex: 1 1 auto;
+ width: 1%;
+ min-width: 0;
+ margin-bottom: 0;
+}
+
+a.list-group-item-action:hover {
+ text-decoration: underline;
+}
+
+a.list-group-item-action:hover .fa {
+ text-decoration: none;
+}
+
+.modal.modal-primary .modal-header {
+ background: var(--primary);
+ color: white;
+}
+
+.modal.modal-primary .modal-title, .modal.modal-primary .modal-header, .modal.modal-primary .close {
+ color: white;
+}
+
+.modal.modal-primary .close {
+ opacity: 1;
+}
+
+.grouper #pagination label {
+ margin-bottom: 0;
+}
+
+.grouper .pagination {
+ margin: 0;
+ background-color: var(--primary);
+ color: white;
+ padding: 2px;
+}
+
+.grouper .counter {
+ margin: 0.5rem 0;
+ font-size: 0.9rem;
+}
+
+.grouper .pagination a {
+ color: white;
+}
+
+.grouper .pagination .pagination-element:not(.pagination-numbers):not(.pagination-limit) {
+ padding: 0.5rem;
+ margin-right: 0.5rem;
+}
+
+.grouper .pagination .pagination-element.pagination-limit {
+ padding: 0 1rem;
+}
+
+.grouper .pagination .pagination-element.pagination-numbers {
+ margin-right: 1rem;
+}
+
+.grouper .pagination .pagination-element.pagination-numbers-link:not(:first-child) {
+ border-left: 1px solid rgba(255, 255, 255, 0.5);
+}
+
+.grouper .pagination .pagination-element.pagination-numbers .pagination-numbers-list>span {
+ border-right: 1px solid rgba(255, 255, 255, 0.5);
+ padding: 0.5rem 1rem;
+}
+
+.grouper .pagination .pagination-element.pagination-numbers .pagination-numbers-link:not(:empty) {
+ border-right: 1px solid rgba(255, 255, 255, 0.5);
+ padding: 0.5rem 1rem;
+}
+
+.grouper .pagination .pagination-element.pagination-numbers .pagination-numbers-list .current.pagination-numbers-item {
+ background: white;
+ color: var(--primary);
+}
+
+.grouper .pagination .pagination-element.pagination-form * {
+ margin: 0 0.5rem 0 0;
+}
+
+.grouper .pagination .btn {
+ padding: 0.2rem 1rem;
+ filter: brightness(120%);
+}
+
+.grouper .pagination label {
+ margin: 0;
+}
+
+.grouper .pagination .muted {
+ color: rgba(255, 255, 255, 0.8);
+}
\ No newline at end of file
diff --git a/webroot/css/empty b/webroot/css/empty
new file mode 100644
index 0000000..e69de29
diff --git a/webroot/css/fonts/Font-Awesome-4.6.3/css/font-awesome.css b/webroot/css/fonts/Font-Awesome-4.6.3/css/font-awesome.css
new file mode 100644
index 0000000..9fb64ce
--- /dev/null
+++ b/webroot/css/fonts/Font-Awesome-4.6.3/css/font-awesome.css
@@ -0,0 +1,2203 @@
+/*!
+ * Font Awesome 4.6.3 by @davegandy - http://fontawesome.io - @fontawesome
+ * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ */
+/* FONT PATH
+ * -------------------------- */
+@font-face {
+ font-family: 'FontAwesome';
+ src: url('../fonts/fontawesome-webfont.eot?v=4.6.3');
+ src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.6.3') format('embedded-opentype'),
+ url('../fonts/fontawesome-webfont.woff2?v=4.6.3') format('woff2'),
+ url('../fonts/fontawesome-webfont.woff?v=4.6.3') format('woff'),
+ url('../fonts/fontawesome-webfont.ttf?v=4.6.3') format('truetype'),
+ url('../fonts/fontawesome-webfont.svg?v=4.6.3#fontawesomeregular') format('svg');
+ font-weight: normal;
+ font-style: normal;
+}
+.fa {
+ display: inline-block;
+ font: normal normal normal 14px/1 FontAwesome;
+ font-size: inherit;
+ text-rendering: auto;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+/* makes the font 33% larger relative to the icon container */
+.fa-lg {
+ font-size: 1.33333333em;
+ line-height: 0.75em;
+ vertical-align: -15%;
+}
+.fa-2x {
+ font-size: 2em;
+}
+.fa-3x {
+ font-size: 3em;
+}
+.fa-4x {
+ font-size: 4em;
+}
+.fa-5x {
+ font-size: 5em;
+}
+.fa-fw {
+ width: 1.28571429em;
+ text-align: center;
+}
+.fa-ul {
+ padding-left: 0;
+ margin-left: 2.14285714em;
+ list-style-type: none;
+}
+.fa-ul > li {
+ position: relative;
+}
+.fa-li {
+ position: absolute;
+ left: -2.14285714em;
+ width: 2.14285714em;
+ top: 0.14285714em;
+ text-align: center;
+}
+.fa-li.fa-lg {
+ left: -1.85714286em;
+}
+.fa-border {
+ padding: .2em .25em .15em;
+ border: solid 0.08em #eeeeee;
+ border-radius: .1em;
+}
+.fa-pull-left {
+ float: left;
+}
+.fa-pull-right {
+ float: right;
+}
+.fa.fa-pull-left {
+ margin-right: .3em;
+}
+.fa.fa-pull-right {
+ margin-left: .3em;
+}
+/* Deprecated as of 4.4.0 */
+.pull-right {
+ float: right;
+}
+.pull-left {
+ float: left;
+}
+.fa.pull-left {
+ margin-right: .3em;
+}
+.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(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(359deg);
+ transform: rotate(359deg);
+ }
+}
+@keyframes fa-spin {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(359deg);
+ transform: rotate(359deg);
+ }
+}
+.fa-rotate-90 {
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
+ -webkit-transform: rotate(90deg);
+ -ms-transform: rotate(90deg);
+ transform: rotate(90deg);
+}
+.fa-rotate-180 {
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
+ -webkit-transform: rotate(180deg);
+ -ms-transform: rotate(180deg);
+ transform: rotate(180deg);
+}
+.fa-rotate-270 {
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
+ -webkit-transform: rotate(270deg);
+ -ms-transform: rotate(270deg);
+ transform: rotate(270deg);
+}
+.fa-flip-horizontal {
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
+ -webkit-transform: scale(-1, 1);
+ -ms-transform: scale(-1, 1);
+ transform: scale(-1, 1);
+}
+.fa-flip-vertical {
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
+ -webkit-transform: scale(1, -1);
+ -ms-transform: scale(1, -1);
+ transform: scale(1, -1);
+}
+:root .fa-rotate-90,
+:root .fa-rotate-180,
+:root .fa-rotate-270,
+:root .fa-flip-horizontal,
+:root .fa-flip-vertical {
+ filter: none;
+}
+.fa-stack {
+ position: relative;
+ display: inline-block;
+ width: 2em;
+ height: 2em;
+ line-height: 2em;
+ vertical-align: middle;
+}
+.fa-stack-1x,
+.fa-stack-2x {
+ position: absolute;
+ left: 0;
+ width: 100%;
+ text-align: center;
+}
+.fa-stack-1x {
+ line-height: inherit;
+}
+.fa-stack-2x {
+ font-size: 2em;
+}
+.fa-inverse {
+ color: #ffffff;
+}
+/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen
+ readers do not read off random characters that represent icons */
+.fa-glass:before {
+ content: "\f000";
+}
+.fa-music:before {
+ content: "\f001";
+}
+.fa-search:before {
+ content: "\f002";
+}
+.fa-envelope-o:before {
+ content: "\f003";
+}
+.fa-heart:before {
+ content: "\f004";
+}
+.fa-star:before {
+ content: "\f005";
+}
+.fa-star-o:before {
+ content: "\f006";
+}
+.fa-user:before {
+ content: "\f007";
+}
+.fa-film:before {
+ content: "\f008";
+}
+.fa-th-large:before {
+ content: "\f009";
+}
+.fa-th:before {
+ content: "\f00a";
+}
+.fa-th-list:before {
+ content: "\f00b";
+}
+.fa-check:before {
+ content: "\f00c";
+}
+.fa-remove:before,
+.fa-close:before,
+.fa-times:before {
+ content: "\f00d";
+}
+.fa-search-plus:before {
+ content: "\f00e";
+}
+.fa-search-minus:before {
+ content: "\f010";
+}
+.fa-power-off:before {
+ content: "\f011";
+}
+.fa-signal:before {
+ content: "\f012";
+}
+.fa-gear:before,
+.fa-cog:before {
+ content: "\f013";
+}
+.fa-trash-o:before {
+ content: "\f014";
+}
+.fa-home:before {
+ content: "\f015";
+}
+.fa-file-o:before {
+ content: "\f016";
+}
+.fa-clock-o:before {
+ content: "\f017";
+}
+.fa-road:before {
+ content: "\f018";
+}
+.fa-download:before {
+ content: "\f019";
+}
+.fa-arrow-circle-o-down:before {
+ content: "\f01a";
+}
+.fa-arrow-circle-o-up:before {
+ content: "\f01b";
+}
+.fa-inbox:before {
+ content: "\f01c";
+}
+.fa-play-circle-o:before {
+ content: "\f01d";
+}
+.fa-rotate-right:before,
+.fa-repeat:before {
+ content: "\f01e";
+}
+.fa-refresh:before {
+ content: "\f021";
+}
+.fa-list-alt:before {
+ content: "\f022";
+}
+.fa-lock:before {
+ content: "\f023";
+}
+.fa-flag:before {
+ content: "\f024";
+}
+.fa-headphones:before {
+ content: "\f025";
+}
+.fa-volume-off:before {
+ content: "\f026";
+}
+.fa-volume-down:before {
+ content: "\f027";
+}
+.fa-volume-up:before {
+ content: "\f028";
+}
+.fa-qrcode:before {
+ content: "\f029";
+}
+.fa-barcode:before {
+ content: "\f02a";
+}
+.fa-tag:before {
+ content: "\f02b";
+}
+.fa-tags:before {
+ content: "\f02c";
+}
+.fa-book:before {
+ content: "\f02d";
+}
+.fa-bookmark:before {
+ content: "\f02e";
+}
+.fa-print:before {
+ content: "\f02f";
+}
+.fa-camera:before {
+ content: "\f030";
+}
+.fa-font:before {
+ content: "\f031";
+}
+.fa-bold:before {
+ content: "\f032";
+}
+.fa-italic:before {
+ content: "\f033";
+}
+.fa-text-height:before {
+ content: "\f034";
+}
+.fa-text-width:before {
+ content: "\f035";
+}
+.fa-align-left:before {
+ content: "\f036";
+}
+.fa-align-center:before {
+ content: "\f037";
+}
+.fa-align-right:before {
+ content: "\f038";
+}
+.fa-align-justify:before {
+ content: "\f039";
+}
+.fa-list:before {
+ content: "\f03a";
+}
+.fa-dedent:before,
+.fa-outdent:before {
+ content: "\f03b";
+}
+.fa-indent:before {
+ content: "\f03c";
+}
+.fa-video-camera:before {
+ content: "\f03d";
+}
+.fa-photo:before,
+.fa-image:before,
+.fa-picture-o:before {
+ content: "\f03e";
+}
+.fa-pencil:before {
+ content: "\f040";
+}
+.fa-map-marker:before {
+ content: "\f041";
+}
+.fa-adjust:before {
+ content: "\f042";
+}
+.fa-tint:before {
+ content: "\f043";
+}
+.fa-edit:before,
+.fa-pencil-square-o:before {
+ content: "\f044";
+}
+.fa-share-square-o:before {
+ content: "\f045";
+}
+.fa-check-square-o:before {
+ content: "\f046";
+}
+.fa-arrows:before {
+ content: "\f047";
+}
+.fa-step-backward:before {
+ content: "\f048";
+}
+.fa-fast-backward:before {
+ content: "\f049";
+}
+.fa-backward:before {
+ content: "\f04a";
+}
+.fa-play:before {
+ content: "\f04b";
+}
+.fa-pause:before {
+ content: "\f04c";
+}
+.fa-stop:before {
+ content: "\f04d";
+}
+.fa-forward:before {
+ content: "\f04e";
+}
+.fa-fast-forward:before {
+ content: "\f050";
+}
+.fa-step-forward:before {
+ content: "\f051";
+}
+.fa-eject:before {
+ content: "\f052";
+}
+.fa-chevron-left:before {
+ content: "\f053";
+}
+.fa-chevron-right:before {
+ content: "\f054";
+}
+.fa-plus-circle:before {
+ content: "\f055";
+}
+.fa-minus-circle:before {
+ content: "\f056";
+}
+.fa-times-circle:before {
+ content: "\f057";
+}
+.fa-check-circle:before {
+ content: "\f058";
+}
+.fa-question-circle:before {
+ content: "\f059";
+}
+.fa-info-circle:before {
+ content: "\f05a";
+}
+.fa-crosshairs:before {
+ content: "\f05b";
+}
+.fa-times-circle-o:before {
+ content: "\f05c";
+}
+.fa-check-circle-o:before {
+ content: "\f05d";
+}
+.fa-ban:before {
+ content: "\f05e";
+}
+.fa-arrow-left:before {
+ content: "\f060";
+}
+.fa-arrow-right:before {
+ content: "\f061";
+}
+.fa-arrow-up:before {
+ content: "\f062";
+}
+.fa-arrow-down:before {
+ content: "\f063";
+}
+.fa-mail-forward:before,
+.fa-share:before {
+ content: "\f064";
+}
+.fa-expand:before {
+ content: "\f065";
+}
+.fa-compress:before {
+ content: "\f066";
+}
+.fa-plus:before {
+ content: "\f067";
+}
+.fa-minus:before {
+ content: "\f068";
+}
+.fa-asterisk:before {
+ content: "\f069";
+}
+.fa-exclamation-circle:before {
+ content: "\f06a";
+}
+.fa-gift:before {
+ content: "\f06b";
+}
+.fa-leaf:before {
+ content: "\f06c";
+}
+.fa-fire:before {
+ content: "\f06d";
+}
+.fa-eye:before {
+ content: "\f06e";
+}
+.fa-eye-slash:before {
+ content: "\f070";
+}
+.fa-warning:before,
+.fa-exclamation-triangle:before {
+ content: "\f071";
+}
+.fa-plane:before {
+ content: "\f072";
+}
+.fa-calendar:before {
+ content: "\f073";
+}
+.fa-random:before {
+ content: "\f074";
+}
+.fa-comment:before {
+ content: "\f075";
+}
+.fa-magnet:before {
+ content: "\f076";
+}
+.fa-chevron-up:before {
+ content: "\f077";
+}
+.fa-chevron-down:before {
+ content: "\f078";
+}
+.fa-retweet:before {
+ content: "\f079";
+}
+.fa-shopping-cart:before {
+ content: "\f07a";
+}
+.fa-folder:before {
+ content: "\f07b";
+}
+.fa-folder-open:before {
+ content: "\f07c";
+}
+.fa-arrows-v:before {
+ content: "\f07d";
+}
+.fa-arrows-h:before {
+ content: "\f07e";
+}
+.fa-bar-chart-o:before,
+.fa-bar-chart:before {
+ content: "\f080";
+}
+.fa-twitter-square:before {
+ content: "\f081";
+}
+.fa-facebook-square:before {
+ content: "\f082";
+}
+.fa-camera-retro:before {
+ content: "\f083";
+}
+.fa-key:before {
+ content: "\f084";
+}
+.fa-gears:before,
+.fa-cogs:before {
+ content: "\f085";
+}
+.fa-comments:before {
+ content: "\f086";
+}
+.fa-thumbs-o-up:before {
+ content: "\f087";
+}
+.fa-thumbs-o-down:before {
+ content: "\f088";
+}
+.fa-star-half:before {
+ content: "\f089";
+}
+.fa-heart-o:before {
+ content: "\f08a";
+}
+.fa-sign-out:before {
+ content: "\f08b";
+}
+.fa-linkedin-square:before {
+ content: "\f08c";
+}
+.fa-thumb-tack:before {
+ content: "\f08d";
+}
+.fa-external-link:before {
+ content: "\f08e";
+}
+.fa-sign-in:before {
+ content: "\f090";
+}
+.fa-trophy:before {
+ content: "\f091";
+}
+.fa-github-square:before {
+ content: "\f092";
+}
+.fa-upload:before {
+ content: "\f093";
+}
+.fa-lemon-o:before {
+ content: "\f094";
+}
+.fa-phone:before {
+ content: "\f095";
+}
+.fa-square-o:before {
+ content: "\f096";
+}
+.fa-bookmark-o:before {
+ content: "\f097";
+}
+.fa-phone-square:before {
+ content: "\f098";
+}
+.fa-twitter:before {
+ content: "\f099";
+}
+.fa-facebook-f:before,
+.fa-facebook:before {
+ content: "\f09a";
+}
+.fa-github:before {
+ content: "\f09b";
+}
+.fa-unlock:before {
+ content: "\f09c";
+}
+.fa-credit-card:before {
+ content: "\f09d";
+}
+.fa-feed:before,
+.fa-rss:before {
+ content: "\f09e";
+}
+.fa-hdd-o:before {
+ content: "\f0a0";
+}
+.fa-bullhorn:before {
+ content: "\f0a1";
+}
+.fa-bell:before {
+ content: "\f0f3";
+}
+.fa-certificate:before {
+ content: "\f0a3";
+}
+.fa-hand-o-right:before {
+ content: "\f0a4";
+}
+.fa-hand-o-left:before {
+ content: "\f0a5";
+}
+.fa-hand-o-up:before {
+ content: "\f0a6";
+}
+.fa-hand-o-down:before {
+ content: "\f0a7";
+}
+.fa-arrow-circle-left:before {
+ content: "\f0a8";
+}
+.fa-arrow-circle-right:before {
+ content: "\f0a9";
+}
+.fa-arrow-circle-up:before {
+ content: "\f0aa";
+}
+.fa-arrow-circle-down:before {
+ content: "\f0ab";
+}
+.fa-globe:before {
+ content: "\f0ac";
+}
+.fa-wrench:before {
+ content: "\f0ad";
+}
+.fa-tasks:before {
+ content: "\f0ae";
+}
+.fa-filter:before {
+ content: "\f0b0";
+}
+.fa-briefcase:before {
+ content: "\f0b1";
+}
+.fa-arrows-alt:before {
+ content: "\f0b2";
+}
+.fa-group:before,
+.fa-users:before {
+ content: "\f0c0";
+}
+.fa-chain:before,
+.fa-link:before {
+ content: "\f0c1";
+}
+.fa-cloud:before {
+ content: "\f0c2";
+}
+.fa-flask:before {
+ content: "\f0c3";
+}
+.fa-cut:before,
+.fa-scissors:before {
+ content: "\f0c4";
+}
+.fa-copy:before,
+.fa-files-o:before {
+ content: "\f0c5";
+}
+.fa-paperclip:before {
+ content: "\f0c6";
+}
+.fa-save:before,
+.fa-floppy-o:before {
+ content: "\f0c7";
+}
+.fa-square:before {
+ content: "\f0c8";
+}
+.fa-navicon:before,
+.fa-reorder:before,
+.fa-bars:before {
+ content: "\f0c9";
+}
+.fa-list-ul:before {
+ content: "\f0ca";
+}
+.fa-list-ol:before {
+ content: "\f0cb";
+}
+.fa-strikethrough:before {
+ content: "\f0cc";
+}
+.fa-underline:before {
+ content: "\f0cd";
+}
+.fa-table:before {
+ content: "\f0ce";
+}
+.fa-magic:before {
+ content: "\f0d0";
+}
+.fa-truck:before {
+ content: "\f0d1";
+}
+.fa-pinterest:before {
+ content: "\f0d2";
+}
+.fa-pinterest-square:before {
+ content: "\f0d3";
+}
+.fa-google-plus-square:before {
+ content: "\f0d4";
+}
+.fa-google-plus:before {
+ content: "\f0d5";
+}
+.fa-money:before {
+ content: "\f0d6";
+}
+.fa-caret-down:before {
+ content: "\f0d7";
+}
+.fa-caret-up:before {
+ content: "\f0d8";
+}
+.fa-caret-left:before {
+ content: "\f0d9";
+}
+.fa-caret-right:before {
+ content: "\f0da";
+}
+.fa-columns:before {
+ content: "\f0db";
+}
+.fa-unsorted:before,
+.fa-sort:before {
+ content: "\f0dc";
+}
+.fa-sort-down:before,
+.fa-sort-desc:before {
+ content: "\f0dd";
+}
+.fa-sort-up:before,
+.fa-sort-asc:before {
+ content: "\f0de";
+}
+.fa-envelope:before {
+ content: "\f0e0";
+}
+.fa-linkedin:before {
+ content: "\f0e1";
+}
+.fa-rotate-left:before,
+.fa-undo:before {
+ content: "\f0e2";
+}
+.fa-legal:before,
+.fa-gavel:before {
+ content: "\f0e3";
+}
+.fa-dashboard:before,
+.fa-tachometer:before {
+ content: "\f0e4";
+}
+.fa-comment-o:before {
+ content: "\f0e5";
+}
+.fa-comments-o:before {
+ content: "\f0e6";
+}
+.fa-flash:before,
+.fa-bolt:before {
+ content: "\f0e7";
+}
+.fa-sitemap:before {
+ content: "\f0e8";
+}
+.fa-umbrella:before {
+ content: "\f0e9";
+}
+.fa-paste:before,
+.fa-clipboard:before {
+ content: "\f0ea";
+}
+.fa-lightbulb-o:before {
+ content: "\f0eb";
+}
+.fa-exchange:before {
+ content: "\f0ec";
+}
+.fa-cloud-download:before {
+ content: "\f0ed";
+}
+.fa-cloud-upload:before {
+ content: "\f0ee";
+}
+.fa-user-md:before {
+ content: "\f0f0";
+}
+.fa-stethoscope:before {
+ content: "\f0f1";
+}
+.fa-suitcase:before {
+ content: "\f0f2";
+}
+.fa-bell-o:before {
+ content: "\f0a2";
+}
+.fa-coffee:before {
+ content: "\f0f4";
+}
+.fa-cutlery:before {
+ content: "\f0f5";
+}
+.fa-file-text-o:before {
+ content: "\f0f6";
+}
+.fa-building-o:before {
+ content: "\f0f7";
+}
+.fa-hospital-o:before {
+ content: "\f0f8";
+}
+.fa-ambulance:before {
+ content: "\f0f9";
+}
+.fa-medkit:before {
+ content: "\f0fa";
+}
+.fa-fighter-jet:before {
+ content: "\f0fb";
+}
+.fa-beer:before {
+ content: "\f0fc";
+}
+.fa-h-square:before {
+ content: "\f0fd";
+}
+.fa-plus-square:before {
+ content: "\f0fe";
+}
+.fa-angle-double-left:before {
+ content: "\f100";
+}
+.fa-angle-double-right:before {
+ content: "\f101";
+}
+.fa-angle-double-up:before {
+ content: "\f102";
+}
+.fa-angle-double-down:before {
+ content: "\f103";
+}
+.fa-angle-left:before {
+ content: "\f104";
+}
+.fa-angle-right:before {
+ content: "\f105";
+}
+.fa-angle-up:before {
+ content: "\f106";
+}
+.fa-angle-down:before {
+ content: "\f107";
+}
+.fa-desktop:before {
+ content: "\f108";
+}
+.fa-laptop:before {
+ content: "\f109";
+}
+.fa-tablet:before {
+ content: "\f10a";
+}
+.fa-mobile-phone:before,
+.fa-mobile:before {
+ content: "\f10b";
+}
+.fa-circle-o:before {
+ content: "\f10c";
+}
+.fa-quote-left:before {
+ content: "\f10d";
+}
+.fa-quote-right:before {
+ content: "\f10e";
+}
+.fa-spinner:before {
+ content: "\f110";
+}
+.fa-circle:before {
+ content: "\f111";
+}
+.fa-mail-reply:before,
+.fa-reply:before {
+ content: "\f112";
+}
+.fa-github-alt:before {
+ content: "\f113";
+}
+.fa-folder-o:before {
+ content: "\f114";
+}
+.fa-folder-open-o:before {
+ content: "\f115";
+}
+.fa-smile-o:before {
+ content: "\f118";
+}
+.fa-frown-o:before {
+ content: "\f119";
+}
+.fa-meh-o:before {
+ content: "\f11a";
+}
+.fa-gamepad:before {
+ content: "\f11b";
+}
+.fa-keyboard-o:before {
+ content: "\f11c";
+}
+.fa-flag-o:before {
+ content: "\f11d";
+}
+.fa-flag-checkered:before {
+ content: "\f11e";
+}
+.fa-terminal:before {
+ content: "\f120";
+}
+.fa-code:before {
+ content: "\f121";
+}
+.fa-mail-reply-all:before,
+.fa-reply-all:before {
+ content: "\f122";
+}
+.fa-star-half-empty:before,
+.fa-star-half-full:before,
+.fa-star-half-o:before {
+ content: "\f123";
+}
+.fa-location-arrow:before {
+ content: "\f124";
+}
+.fa-crop:before {
+ content: "\f125";
+}
+.fa-code-fork:before {
+ content: "\f126";
+}
+.fa-unlink:before,
+.fa-chain-broken:before {
+ content: "\f127";
+}
+.fa-question:before {
+ content: "\f128";
+}
+.fa-info:before {
+ content: "\f129";
+}
+.fa-exclamation:before {
+ content: "\f12a";
+}
+.fa-superscript:before {
+ content: "\f12b";
+}
+.fa-subscript:before {
+ content: "\f12c";
+}
+.fa-eraser:before {
+ content: "\f12d";
+}
+.fa-puzzle-piece:before {
+ content: "\f12e";
+}
+.fa-microphone:before {
+ content: "\f130";
+}
+.fa-microphone-slash:before {
+ content: "\f131";
+}
+.fa-shield:before {
+ content: "\f132";
+}
+.fa-calendar-o:before {
+ content: "\f133";
+}
+.fa-fire-extinguisher:before {
+ content: "\f134";
+}
+.fa-rocket:before {
+ content: "\f135";
+}
+.fa-maxcdn:before {
+ content: "\f136";
+}
+.fa-chevron-circle-left:before {
+ content: "\f137";
+}
+.fa-chevron-circle-right:before {
+ content: "\f138";
+}
+.fa-chevron-circle-up:before {
+ content: "\f139";
+}
+.fa-chevron-circle-down:before {
+ content: "\f13a";
+}
+.fa-html5:before {
+ content: "\f13b";
+}
+.fa-css3:before {
+ content: "\f13c";
+}
+.fa-anchor:before {
+ content: "\f13d";
+}
+.fa-unlock-alt:before {
+ content: "\f13e";
+}
+.fa-bullseye:before {
+ content: "\f140";
+}
+.fa-ellipsis-h:before {
+ content: "\f141";
+}
+.fa-ellipsis-v:before {
+ content: "\f142";
+}
+.fa-rss-square:before {
+ content: "\f143";
+}
+.fa-play-circle:before {
+ content: "\f144";
+}
+.fa-ticket:before {
+ content: "\f145";
+}
+.fa-minus-square:before {
+ content: "\f146";
+}
+.fa-minus-square-o:before {
+ content: "\f147";
+}
+.fa-level-up:before {
+ content: "\f148";
+}
+.fa-level-down:before {
+ content: "\f149";
+}
+.fa-check-square:before {
+ content: "\f14a";
+}
+.fa-pencil-square:before {
+ content: "\f14b";
+}
+.fa-external-link-square:before {
+ content: "\f14c";
+}
+.fa-share-square:before {
+ content: "\f14d";
+}
+.fa-compass:before {
+ content: "\f14e";
+}
+.fa-toggle-down:before,
+.fa-caret-square-o-down:before {
+ content: "\f150";
+}
+.fa-toggle-up:before,
+.fa-caret-square-o-up:before {
+ content: "\f151";
+}
+.fa-toggle-right:before,
+.fa-caret-square-o-right:before {
+ content: "\f152";
+}
+.fa-euro:before,
+.fa-eur:before {
+ content: "\f153";
+}
+.fa-gbp:before {
+ content: "\f154";
+}
+.fa-dollar:before,
+.fa-usd:before {
+ content: "\f155";
+}
+.fa-rupee:before,
+.fa-inr:before {
+ content: "\f156";
+}
+.fa-cny:before,
+.fa-rmb:before,
+.fa-yen:before,
+.fa-jpy:before {
+ content: "\f157";
+}
+.fa-ruble:before,
+.fa-rouble:before,
+.fa-rub:before {
+ content: "\f158";
+}
+.fa-won:before,
+.fa-krw:before {
+ content: "\f159";
+}
+.fa-bitcoin:before,
+.fa-btc:before {
+ content: "\f15a";
+}
+.fa-file:before {
+ content: "\f15b";
+}
+.fa-file-text:before {
+ content: "\f15c";
+}
+.fa-sort-alpha-asc:before {
+ content: "\f15d";
+}
+.fa-sort-alpha-desc:before {
+ content: "\f15e";
+}
+.fa-sort-amount-asc:before {
+ content: "\f160";
+}
+.fa-sort-amount-desc:before {
+ content: "\f161";
+}
+.fa-sort-numeric-asc:before {
+ content: "\f162";
+}
+.fa-sort-numeric-desc:before {
+ content: "\f163";
+}
+.fa-thumbs-up:before {
+ content: "\f164";
+}
+.fa-thumbs-down:before {
+ content: "\f165";
+}
+.fa-youtube-square:before {
+ content: "\f166";
+}
+.fa-youtube:before {
+ content: "\f167";
+}
+.fa-xing:before {
+ content: "\f168";
+}
+.fa-xing-square:before {
+ content: "\f169";
+}
+.fa-youtube-play:before {
+ content: "\f16a";
+}
+.fa-dropbox:before {
+ content: "\f16b";
+}
+.fa-stack-overflow:before {
+ content: "\f16c";
+}
+.fa-instagram:before {
+ content: "\f16d";
+}
+.fa-flickr:before {
+ content: "\f16e";
+}
+.fa-adn:before {
+ content: "\f170";
+}
+.fa-bitbucket:before {
+ content: "\f171";
+}
+.fa-bitbucket-square:before {
+ content: "\f172";
+}
+.fa-tumblr:before {
+ content: "\f173";
+}
+.fa-tumblr-square:before {
+ content: "\f174";
+}
+.fa-long-arrow-down:before {
+ content: "\f175";
+}
+.fa-long-arrow-up:before {
+ content: "\f176";
+}
+.fa-long-arrow-left:before {
+ content: "\f177";
+}
+.fa-long-arrow-right:before {
+ content: "\f178";
+}
+.fa-apple:before {
+ content: "\f179";
+}
+.fa-windows:before {
+ content: "\f17a";
+}
+.fa-android:before {
+ content: "\f17b";
+}
+.fa-linux:before {
+ content: "\f17c";
+}
+.fa-dribbble:before {
+ content: "\f17d";
+}
+.fa-skype:before {
+ content: "\f17e";
+}
+.fa-foursquare:before {
+ content: "\f180";
+}
+.fa-trello:before {
+ content: "\f181";
+}
+.fa-female:before {
+ content: "\f182";
+}
+.fa-male:before {
+ content: "\f183";
+}
+.fa-gittip:before,
+.fa-gratipay:before {
+ content: "\f184";
+}
+.fa-sun-o:before {
+ content: "\f185";
+}
+.fa-moon-o:before {
+ content: "\f186";
+}
+.fa-archive:before {
+ content: "\f187";
+}
+.fa-bug:before {
+ content: "\f188";
+}
+.fa-vk:before {
+ content: "\f189";
+}
+.fa-weibo:before {
+ content: "\f18a";
+}
+.fa-renren:before {
+ content: "\f18b";
+}
+.fa-pagelines:before {
+ content: "\f18c";
+}
+.fa-stack-exchange:before {
+ content: "\f18d";
+}
+.fa-arrow-circle-o-right:before {
+ content: "\f18e";
+}
+.fa-arrow-circle-o-left:before {
+ content: "\f190";
+}
+.fa-toggle-left:before,
+.fa-caret-square-o-left:before {
+ content: "\f191";
+}
+.fa-dot-circle-o:before {
+ content: "\f192";
+}
+.fa-wheelchair:before {
+ content: "\f193";
+}
+.fa-vimeo-square:before {
+ content: "\f194";
+}
+.fa-turkish-lira:before,
+.fa-try:before {
+ content: "\f195";
+}
+.fa-plus-square-o:before {
+ content: "\f196";
+}
+.fa-space-shuttle:before {
+ content: "\f197";
+}
+.fa-slack:before {
+ content: "\f198";
+}
+.fa-envelope-square:before {
+ content: "\f199";
+}
+.fa-wordpress:before {
+ content: "\f19a";
+}
+.fa-openid:before {
+ content: "\f19b";
+}
+.fa-institution:before,
+.fa-bank:before,
+.fa-university:before {
+ content: "\f19c";
+}
+.fa-mortar-board:before,
+.fa-graduation-cap:before {
+ content: "\f19d";
+}
+.fa-yahoo:before {
+ content: "\f19e";
+}
+.fa-google:before {
+ content: "\f1a0";
+}
+.fa-reddit:before {
+ content: "\f1a1";
+}
+.fa-reddit-square:before {
+ content: "\f1a2";
+}
+.fa-stumbleupon-circle:before {
+ content: "\f1a3";
+}
+.fa-stumbleupon:before {
+ content: "\f1a4";
+}
+.fa-delicious:before {
+ content: "\f1a5";
+}
+.fa-digg:before {
+ content: "\f1a6";
+}
+.fa-pied-piper-pp:before {
+ content: "\f1a7";
+}
+.fa-pied-piper-alt:before {
+ content: "\f1a8";
+}
+.fa-drupal:before {
+ content: "\f1a9";
+}
+.fa-joomla:before {
+ content: "\f1aa";
+}
+.fa-language:before {
+ content: "\f1ab";
+}
+.fa-fax:before {
+ content: "\f1ac";
+}
+.fa-building:before {
+ content: "\f1ad";
+}
+.fa-child:before {
+ content: "\f1ae";
+}
+.fa-paw:before {
+ content: "\f1b0";
+}
+.fa-spoon:before {
+ content: "\f1b1";
+}
+.fa-cube:before {
+ content: "\f1b2";
+}
+.fa-cubes:before {
+ content: "\f1b3";
+}
+.fa-behance:before {
+ content: "\f1b4";
+}
+.fa-behance-square:before {
+ content: "\f1b5";
+}
+.fa-steam:before {
+ content: "\f1b6";
+}
+.fa-steam-square:before {
+ content: "\f1b7";
+}
+.fa-recycle:before {
+ content: "\f1b8";
+}
+.fa-automobile:before,
+.fa-car:before {
+ content: "\f1b9";
+}
+.fa-cab:before,
+.fa-taxi:before {
+ content: "\f1ba";
+}
+.fa-tree:before {
+ content: "\f1bb";
+}
+.fa-spotify:before {
+ content: "\f1bc";
+}
+.fa-deviantart:before {
+ content: "\f1bd";
+}
+.fa-soundcloud:before {
+ content: "\f1be";
+}
+.fa-database:before {
+ content: "\f1c0";
+}
+.fa-file-pdf-o:before {
+ content: "\f1c1";
+}
+.fa-file-word-o:before {
+ content: "\f1c2";
+}
+.fa-file-excel-o:before {
+ content: "\f1c3";
+}
+.fa-file-powerpoint-o:before {
+ content: "\f1c4";
+}
+.fa-file-photo-o:before,
+.fa-file-picture-o:before,
+.fa-file-image-o:before {
+ content: "\f1c5";
+}
+.fa-file-zip-o:before,
+.fa-file-archive-o:before {
+ content: "\f1c6";
+}
+.fa-file-sound-o:before,
+.fa-file-audio-o:before {
+ content: "\f1c7";
+}
+.fa-file-movie-o:before,
+.fa-file-video-o:before {
+ content: "\f1c8";
+}
+.fa-file-code-o:before {
+ content: "\f1c9";
+}
+.fa-vine:before {
+ content: "\f1ca";
+}
+.fa-codepen:before {
+ content: "\f1cb";
+}
+.fa-jsfiddle:before {
+ content: "\f1cc";
+}
+.fa-life-bouy:before,
+.fa-life-buoy:before,
+.fa-life-saver:before,
+.fa-support:before,
+.fa-life-ring:before {
+ content: "\f1cd";
+}
+.fa-circle-o-notch:before {
+ content: "\f1ce";
+}
+.fa-ra:before,
+.fa-resistance:before,
+.fa-rebel:before {
+ content: "\f1d0";
+}
+.fa-ge:before,
+.fa-empire:before {
+ content: "\f1d1";
+}
+.fa-git-square:before {
+ content: "\f1d2";
+}
+.fa-git:before {
+ content: "\f1d3";
+}
+.fa-y-combinator-square:before,
+.fa-yc-square:before,
+.fa-hacker-news:before {
+ content: "\f1d4";
+}
+.fa-tencent-weibo:before {
+ content: "\f1d5";
+}
+.fa-qq:before {
+ content: "\f1d6";
+}
+.fa-wechat:before,
+.fa-weixin:before {
+ content: "\f1d7";
+}
+.fa-send:before,
+.fa-paper-plane:before {
+ content: "\f1d8";
+}
+.fa-send-o:before,
+.fa-paper-plane-o:before {
+ content: "\f1d9";
+}
+.fa-history:before {
+ content: "\f1da";
+}
+.fa-circle-thin:before {
+ content: "\f1db";
+}
+.fa-header:before {
+ content: "\f1dc";
+}
+.fa-paragraph:before {
+ content: "\f1dd";
+}
+.fa-sliders:before {
+ content: "\f1de";
+}
+.fa-share-alt:before {
+ content: "\f1e0";
+}
+.fa-share-alt-square:before {
+ content: "\f1e1";
+}
+.fa-bomb:before {
+ content: "\f1e2";
+}
+.fa-soccer-ball-o:before,
+.fa-futbol-o:before {
+ content: "\f1e3";
+}
+.fa-tty:before {
+ content: "\f1e4";
+}
+.fa-binoculars:before {
+ content: "\f1e5";
+}
+.fa-plug:before {
+ content: "\f1e6";
+}
+.fa-slideshare:before {
+ content: "\f1e7";
+}
+.fa-twitch:before {
+ content: "\f1e8";
+}
+.fa-yelp:before {
+ content: "\f1e9";
+}
+.fa-newspaper-o:before {
+ content: "\f1ea";
+}
+.fa-wifi:before {
+ content: "\f1eb";
+}
+.fa-calculator:before {
+ content: "\f1ec";
+}
+.fa-paypal:before {
+ content: "\f1ed";
+}
+.fa-google-wallet:before {
+ content: "\f1ee";
+}
+.fa-cc-visa:before {
+ content: "\f1f0";
+}
+.fa-cc-mastercard:before {
+ content: "\f1f1";
+}
+.fa-cc-discover:before {
+ content: "\f1f2";
+}
+.fa-cc-amex:before {
+ content: "\f1f3";
+}
+.fa-cc-paypal:before {
+ content: "\f1f4";
+}
+.fa-cc-stripe:before {
+ content: "\f1f5";
+}
+.fa-bell-slash:before {
+ content: "\f1f6";
+}
+.fa-bell-slash-o:before {
+ content: "\f1f7";
+}
+.fa-trash:before {
+ content: "\f1f8";
+}
+.fa-copyright:before {
+ content: "\f1f9";
+}
+.fa-at:before {
+ content: "\f1fa";
+}
+.fa-eyedropper:before {
+ content: "\f1fb";
+}
+.fa-paint-brush:before {
+ content: "\f1fc";
+}
+.fa-birthday-cake:before {
+ content: "\f1fd";
+}
+.fa-area-chart:before {
+ content: "\f1fe";
+}
+.fa-pie-chart:before {
+ content: "\f200";
+}
+.fa-line-chart:before {
+ content: "\f201";
+}
+.fa-lastfm:before {
+ content: "\f202";
+}
+.fa-lastfm-square:before {
+ content: "\f203";
+}
+.fa-toggle-off:before {
+ content: "\f204";
+}
+.fa-toggle-on:before {
+ content: "\f205";
+}
+.fa-bicycle:before {
+ content: "\f206";
+}
+.fa-bus:before {
+ content: "\f207";
+}
+.fa-ioxhost:before {
+ content: "\f208";
+}
+.fa-angellist:before {
+ content: "\f209";
+}
+.fa-cc:before {
+ content: "\f20a";
+}
+.fa-shekel:before,
+.fa-sheqel:before,
+.fa-ils:before {
+ content: "\f20b";
+}
+.fa-meanpath:before {
+ content: "\f20c";
+}
+.fa-buysellads:before {
+ content: "\f20d";
+}
+.fa-connectdevelop:before {
+ content: "\f20e";
+}
+.fa-dashcube:before {
+ content: "\f210";
+}
+.fa-forumbee:before {
+ content: "\f211";
+}
+.fa-leanpub:before {
+ content: "\f212";
+}
+.fa-sellsy:before {
+ content: "\f213";
+}
+.fa-shirtsinbulk:before {
+ content: "\f214";
+}
+.fa-simplybuilt:before {
+ content: "\f215";
+}
+.fa-skyatlas:before {
+ content: "\f216";
+}
+.fa-cart-plus:before {
+ content: "\f217";
+}
+.fa-cart-arrow-down:before {
+ content: "\f218";
+}
+.fa-diamond:before {
+ content: "\f219";
+}
+.fa-ship:before {
+ content: "\f21a";
+}
+.fa-user-secret:before {
+ content: "\f21b";
+}
+.fa-motorcycle:before {
+ content: "\f21c";
+}
+.fa-street-view:before {
+ content: "\f21d";
+}
+.fa-heartbeat:before {
+ content: "\f21e";
+}
+.fa-venus:before {
+ content: "\f221";
+}
+.fa-mars:before {
+ content: "\f222";
+}
+.fa-mercury:before {
+ content: "\f223";
+}
+.fa-intersex:before,
+.fa-transgender:before {
+ content: "\f224";
+}
+.fa-transgender-alt:before {
+ content: "\f225";
+}
+.fa-venus-double:before {
+ content: "\f226";
+}
+.fa-mars-double:before {
+ content: "\f227";
+}
+.fa-venus-mars:before {
+ content: "\f228";
+}
+.fa-mars-stroke:before {
+ content: "\f229";
+}
+.fa-mars-stroke-v:before {
+ content: "\f22a";
+}
+.fa-mars-stroke-h:before {
+ content: "\f22b";
+}
+.fa-neuter:before {
+ content: "\f22c";
+}
+.fa-genderless:before {
+ content: "\f22d";
+}
+.fa-facebook-official:before {
+ content: "\f230";
+}
+.fa-pinterest-p:before {
+ content: "\f231";
+}
+.fa-whatsapp:before {
+ content: "\f232";
+}
+.fa-server:before {
+ content: "\f233";
+}
+.fa-user-plus:before {
+ content: "\f234";
+}
+.fa-user-times:before {
+ content: "\f235";
+}
+.fa-hotel:before,
+.fa-bed:before {
+ content: "\f236";
+}
+.fa-viacoin:before {
+ content: "\f237";
+}
+.fa-train:before {
+ content: "\f238";
+}
+.fa-subway:before {
+ content: "\f239";
+}
+.fa-medium:before {
+ content: "\f23a";
+}
+.fa-yc:before,
+.fa-y-combinator:before {
+ content: "\f23b";
+}
+.fa-optin-monster:before {
+ content: "\f23c";
+}
+.fa-opencart:before {
+ content: "\f23d";
+}
+.fa-expeditedssl:before {
+ content: "\f23e";
+}
+.fa-battery-4:before,
+.fa-battery-full:before {
+ content: "\f240";
+}
+.fa-battery-3:before,
+.fa-battery-three-quarters:before {
+ content: "\f241";
+}
+.fa-battery-2:before,
+.fa-battery-half:before {
+ content: "\f242";
+}
+.fa-battery-1:before,
+.fa-battery-quarter:before {
+ content: "\f243";
+}
+.fa-battery-0:before,
+.fa-battery-empty:before {
+ content: "\f244";
+}
+.fa-mouse-pointer:before {
+ content: "\f245";
+}
+.fa-i-cursor:before {
+ content: "\f246";
+}
+.fa-object-group:before {
+ content: "\f247";
+}
+.fa-object-ungroup:before {
+ content: "\f248";
+}
+.fa-sticky-note:before {
+ content: "\f249";
+}
+.fa-sticky-note-o:before {
+ content: "\f24a";
+}
+.fa-cc-jcb:before {
+ content: "\f24b";
+}
+.fa-cc-diners-club:before {
+ content: "\f24c";
+}
+.fa-clone:before {
+ content: "\f24d";
+}
+.fa-balance-scale:before {
+ content: "\f24e";
+}
+.fa-hourglass-o:before {
+ content: "\f250";
+}
+.fa-hourglass-1:before,
+.fa-hourglass-start:before {
+ content: "\f251";
+}
+.fa-hourglass-2:before,
+.fa-hourglass-half:before {
+ content: "\f252";
+}
+.fa-hourglass-3:before,
+.fa-hourglass-end:before {
+ content: "\f253";
+}
+.fa-hourglass:before {
+ content: "\f254";
+}
+.fa-hand-grab-o:before,
+.fa-hand-rock-o:before {
+ content: "\f255";
+}
+.fa-hand-stop-o:before,
+.fa-hand-paper-o:before {
+ content: "\f256";
+}
+.fa-hand-scissors-o:before {
+ content: "\f257";
+}
+.fa-hand-lizard-o:before {
+ content: "\f258";
+}
+.fa-hand-spock-o:before {
+ content: "\f259";
+}
+.fa-hand-pointer-o:before {
+ content: "\f25a";
+}
+.fa-hand-peace-o:before {
+ content: "\f25b";
+}
+.fa-trademark:before {
+ content: "\f25c";
+}
+.fa-registered:before {
+ content: "\f25d";
+}
+.fa-creative-commons:before {
+ content: "\f25e";
+}
+.fa-gg:before {
+ content: "\f260";
+}
+.fa-gg-circle:before {
+ content: "\f261";
+}
+.fa-tripadvisor:before {
+ content: "\f262";
+}
+.fa-odnoklassniki:before {
+ content: "\f263";
+}
+.fa-odnoklassniki-square:before {
+ content: "\f264";
+}
+.fa-get-pocket:before {
+ content: "\f265";
+}
+.fa-wikipedia-w:before {
+ content: "\f266";
+}
+.fa-safari:before {
+ content: "\f267";
+}
+.fa-chrome:before {
+ content: "\f268";
+}
+.fa-firefox:before {
+ content: "\f269";
+}
+.fa-opera:before {
+ content: "\f26a";
+}
+.fa-internet-explorer:before {
+ content: "\f26b";
+}
+.fa-tv:before,
+.fa-television:before {
+ content: "\f26c";
+}
+.fa-contao:before {
+ content: "\f26d";
+}
+.fa-500px:before {
+ content: "\f26e";
+}
+.fa-amazon:before {
+ content: "\f270";
+}
+.fa-calendar-plus-o:before {
+ content: "\f271";
+}
+.fa-calendar-minus-o:before {
+ content: "\f272";
+}
+.fa-calendar-times-o:before {
+ content: "\f273";
+}
+.fa-calendar-check-o:before {
+ content: "\f274";
+}
+.fa-industry:before {
+ content: "\f275";
+}
+.fa-map-pin:before {
+ content: "\f276";
+}
+.fa-map-signs:before {
+ content: "\f277";
+}
+.fa-map-o:before {
+ content: "\f278";
+}
+.fa-map:before {
+ content: "\f279";
+}
+.fa-commenting:before {
+ content: "\f27a";
+}
+.fa-commenting-o:before {
+ content: "\f27b";
+}
+.fa-houzz:before {
+ content: "\f27c";
+}
+.fa-vimeo:before {
+ content: "\f27d";
+}
+.fa-black-tie:before {
+ content: "\f27e";
+}
+.fa-fonticons:before {
+ content: "\f280";
+}
+.fa-reddit-alien:before {
+ content: "\f281";
+}
+.fa-edge:before {
+ content: "\f282";
+}
+.fa-credit-card-alt:before {
+ content: "\f283";
+}
+.fa-codiepie:before {
+ content: "\f284";
+}
+.fa-modx:before {
+ content: "\f285";
+}
+.fa-fort-awesome:before {
+ content: "\f286";
+}
+.fa-usb:before {
+ content: "\f287";
+}
+.fa-product-hunt:before {
+ content: "\f288";
+}
+.fa-mixcloud:before {
+ content: "\f289";
+}
+.fa-scribd:before {
+ content: "\f28a";
+}
+.fa-pause-circle:before {
+ content: "\f28b";
+}
+.fa-pause-circle-o:before {
+ content: "\f28c";
+}
+.fa-stop-circle:before {
+ content: "\f28d";
+}
+.fa-stop-circle-o:before {
+ content: "\f28e";
+}
+.fa-shopping-bag:before {
+ content: "\f290";
+}
+.fa-shopping-basket:before {
+ content: "\f291";
+}
+.fa-hashtag:before {
+ content: "\f292";
+}
+.fa-bluetooth:before {
+ content: "\f293";
+}
+.fa-bluetooth-b:before {
+ content: "\f294";
+}
+.fa-percent:before {
+ content: "\f295";
+}
+.fa-gitlab:before {
+ content: "\f296";
+}
+.fa-wpbeginner:before {
+ content: "\f297";
+}
+.fa-wpforms:before {
+ content: "\f298";
+}
+.fa-envira:before {
+ content: "\f299";
+}
+.fa-universal-access:before {
+ content: "\f29a";
+}
+.fa-wheelchair-alt:before {
+ content: "\f29b";
+}
+.fa-question-circle-o:before {
+ content: "\f29c";
+}
+.fa-blind:before {
+ content: "\f29d";
+}
+.fa-audio-description:before {
+ content: "\f29e";
+}
+.fa-volume-control-phone:before {
+ content: "\f2a0";
+}
+.fa-braille:before {
+ content: "\f2a1";
+}
+.fa-assistive-listening-systems:before {
+ content: "\f2a2";
+}
+.fa-asl-interpreting:before,
+.fa-american-sign-language-interpreting:before {
+ content: "\f2a3";
+}
+.fa-deafness:before,
+.fa-hard-of-hearing:before,
+.fa-deaf:before {
+ content: "\f2a4";
+}
+.fa-glide:before {
+ content: "\f2a5";
+}
+.fa-glide-g:before {
+ content: "\f2a6";
+}
+.fa-signing:before,
+.fa-sign-language:before {
+ content: "\f2a7";
+}
+.fa-low-vision:before {
+ content: "\f2a8";
+}
+.fa-viadeo:before {
+ content: "\f2a9";
+}
+.fa-viadeo-square:before {
+ content: "\f2aa";
+}
+.fa-snapchat:before {
+ content: "\f2ab";
+}
+.fa-snapchat-ghost:before {
+ content: "\f2ac";
+}
+.fa-snapchat-square:before {
+ content: "\f2ad";
+}
+.fa-pied-piper:before {
+ content: "\f2ae";
+}
+.fa-first-order:before {
+ content: "\f2b0";
+}
+.fa-yoast:before {
+ content: "\f2b1";
+}
+.fa-themeisle:before {
+ content: "\f2b2";
+}
+.fa-google-plus-circle:before,
+.fa-google-plus-official:before {
+ content: "\f2b3";
+}
+.fa-fa:before,
+.fa-font-awesome:before {
+ content: "\f2b4";
+}
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+}
+.sr-only-focusable:active,
+.sr-only-focusable:focus {
+ position: static;
+ width: auto;
+ height: auto;
+ margin: 0;
+ overflow: visible;
+ clip: auto;
+}
diff --git a/webroot/css/fonts/Font-Awesome-4.6.3/css/font-awesome.css.map b/webroot/css/fonts/Font-Awesome-4.6.3/css/font-awesome.css.map
new file mode 100644
index 0000000..60763a8
--- /dev/null
+++ b/webroot/css/fonts/Font-Awesome-4.6.3/css/font-awesome.css.map
@@ -0,0 +1,7 @@
+{
+"version": 3,
+"mappings": ";;;;;;;AAGA,UAUC;EATC,WAAW,EAAE,aAAa;EAC1B,GAAG,EAAE,+CAAgE;EACrE,GAAG,EAAE,ySAAmG;EAKxG,WAAW,EAAE,MAAM;EACnB,UAAU,EAAE,MAAM;ACTpB,GAAmB;EACjB,OAAO,EAAE,YAAY;EACrB,IAAI,EAAE,uCAAwD;EAC9D,SAAS,EAAE,OAAO;EAClB,cAAc,EAAE,IAAI;EACpB,sBAAsB,EAAE,WAAW;EACnC,uBAAuB,EAAE,SAAS;EAClC,SAAS,EAAE,eAAe;;;ACN5B,MAAsB;EACpB,SAAS,EAAE,SAAS;EACpB,WAAW,EAAE,MAAS;EACtB,cAAc,EAAE,IAAI;;AAEtB,MAAsB;EAAE,SAAS,EAAE,GAAG;;AACtC,MAAsB;EAAE,SAAS,EAAE,GAAG;;AACtC,MAAsB;EAAE,SAAS,EAAE,GAAG;;AACtC,MAAsB;EAAE,SAAS,EAAE,GAAG;;ACVtC,MAAsB;EACpB,KAAK,EAAE,SAAW;EAClB,UAAU,EAAE,MAAM;;ACDpB,MAAsB;EACpB,YAAY,EAAE,CAAC;EACf,WAAW,ECKU,SAAS;EDJ9B,eAAe,EAAE,IAAI;EACrB,WAAK;IAAE,QAAQ,EAAE,QAAQ;;AAE3B,MAAsB;EACpB,QAAQ,EAAE,QAAQ;EAClB,IAAI,EAAE,UAAa;EACnB,KAAK,ECFgB,SAAS;EDG9B,GAAG,EAAE,SAAU;EACf,UAAU,EAAE,MAAM;EAClB,YAAuB;IACrB,IAAI,EAAE,UAA0B;;AEbpC,UAA0B;EACxB,OAAO,EAAE,gBAAgB;EACzB,MAAM,EAAE,iBAA4B;EACpC,aAAa,EAAE,IAAI;;AAGrB,WAAY;EAAE,KAAK,EAAE,KAAK;;AAC1B,UAAW;EAAE,KAAK,EAAE,IAAI;;AAGtB,aAAY;EAAE,YAAY,EAAE,IAAI;AAChC,cAAa;EAAE,WAAW,EAAE,IAAI;;ACXlC,QAAwB;EACtB,iBAAiB,EAAE,0BAA0B;EACrC,SAAS,EAAE,0BAA0B;;AAG/C,SAAyB;EACvB,iBAAiB,EAAE,4BAA4B;EACvC,SAAS,EAAE,4BAA4B;;AAGjD,0BASC;EARC,EAAG;IACD,iBAAiB,EAAE,YAAY;IACvB,SAAS,EAAE,YAAY;EAEjC,IAAK;IACH,iBAAiB,EAAE,cAAc;IACzB,SAAS,EAAE,cAAc;AAIrC,kBASC;EARC,EAAG;IACD,iBAAiB,EAAE,YAAY;IACvB,SAAS,EAAE,YAAY;EAEjC,IAAK;IACH,iBAAiB,EAAE,cAAc;IACzB,SAAS,EAAE,cAAc;AC5BrC,aAA8B;ECY5B,MAAM,EAAE,wDAAmE;EAC3E,iBAAiB,EAAE,aAAgB;EAC/B,aAAa,EAAE,aAAgB;EAC3B,SAAS,EAAE,aAAgB;;ADdrC,cAA8B;ECW5B,MAAM,EAAE,wDAAmE;EAC3E,iBAAiB,EAAE,cAAgB;EAC/B,aAAa,EAAE,cAAgB;EAC3B,SAAS,EAAE,cAAgB;;ADbrC,cAA8B;ECU5B,MAAM,EAAE,wDAAmE;EAC3E,iBAAiB,EAAE,cAAgB;EAC/B,aAAa,EAAE,cAAgB;EAC3B,SAAS,EAAE,cAAgB;;ADXrC,mBAAmC;ECejC,MAAM,EAAE,wDAAmE;EAC3E,iBAAiB,EAAE,YAAoB;EACnC,aAAa,EAAE,YAAoB;EAC/B,SAAS,EAAE,YAAoB;;ADjBzC,iBAAmC;ECcjC,MAAM,EAAE,wDAAmE;EAC3E,iBAAiB,EAAE,YAAoB;EACnC,aAAa,EAAE,YAAoB;EAC/B,SAAS,EAAE,YAAoB;;ADZzC;;;;uBAIuC;EACrC,MAAM,EAAE,IAAI;;AEfd,SAAyB;EACvB,QAAQ,EAAE,QAAQ;EAClB,OAAO,EAAE,YAAY;EACrB,KAAK,EAAE,GAAG;EACV,MAAM,EAAE,GAAG;EACX,WAAW,EAAE,GAAG;EAChB,cAAc,EAAE,MAAM;;AAExB,0BAAyD;EACvD,QAAQ,EAAE,QAAQ;EAClB,IAAI,EAAE,CAAC;EACP,KAAK,EAAE,IAAI;EACX,UAAU,EAAE,MAAM;;AAEpB,YAA4B;EAAE,WAAW,EAAE,OAAO;;AAClD,YAA4B;EAAE,SAAS,EAAE,GAAG;;AAC5C,WAA2B;EAAE,KAAK,ELVZ,IAAI;;;;AMN1B,gBAAgC;EAAE,OAAO,ENoQ1B,GAAO;;AMnQtB,gBAAgC;EAAE,OAAO,EN0W1B,GAAO;;AMzWtB,iBAAiC;EAAE,OAAO,ENmb1B,GAAO;;AMlbvB,qBAAqC;EAAE,OAAO,ENmL1B,GAAO;;AMlL3B,gBAAgC;EAAE,OAAO,ENkR1B,GAAO;;AMjRtB,eAA+B;EAAE,OAAO,ENke1B,GAAO;;AMjerB,iBAAiC;EAAE,OAAO,ENse1B,GAAO;;AMrevB,eAA+B;EAAE,OAAO,EN+iB1B,GAAO;;AM9iBrB,eAA+B;EAAE,OAAO,ENyN1B,GAAO;;AMxNrB,mBAAmC;EAAE,OAAO,ENggB1B,GAAO;;AM/fzB,aAA6B;EAAE,OAAO,EN8f1B,GAAO;;AM7fnB,kBAAkC;EAAE,OAAO,EN+f1B,GAAO;;AM9fxB,gBAAgC;EAAE,OAAO,ENoG1B,GAAO;;AMnGtB;;gBAEgC;EAAE,OAAO,ENkgB1B,GAAO;;AMjgBtB,sBAAsC;EAAE,OAAO,ENua1B,GAAO;;AMta5B,uBAAuC;EAAE,OAAO,ENqa1B,GAAO;;AMpa7B,oBAAoC;EAAE,OAAO,EN+X1B,GAAO;;AM9X1B,iBAAiC;EAAE,OAAO,ENsb1B,GAAO;;AMrbvB;cAC8B;EAAE,OAAO,ENwH1B,GAAO;;AMvHpB,kBAAkC;EAAE,OAAO,ENygB1B,GAAO;;AMxgBxB,eAA+B;EAAE,OAAO,ENmQ1B,GAAO;;AMlQrB,iBAAiC;EAAE,OAAO,EN6L1B,GAAO;;AM5LvB,kBAAkC;EAAE,OAAO,EN0G1B,GAAO;;AMzGxB,eAA+B;EAAE,OAAO,EN+Y1B,GAAO;;AM9YrB,mBAAmC;EAAE,OAAO,ENiJ1B,GAAO;;AMhJzB,8BAA8C;EAAE,OAAO,ENI1B,GAAO;;AMHpC,4BAA4C;EAAE,OAAO,ENM1B,GAAO;;AMLlC,gBAAgC;EAAE,OAAO,ENkQ1B,GAAO;;AMjQtB,wBAAwC;EAAE,OAAO,EN4W1B,GAAO;;AM3W9B;iBACiC;EAAE,OAAO,ENmY1B,GAAO;;AMlYvB,kBAAkC;EAAE,OAAO,EN8X1B,GAAO;;AM7XxB,mBAAmC;EAAE,OAAO,ENiS1B,GAAO;;AMhSzB,eAA+B;EAAE,OAAO,ENoS1B,GAAO;;AMnSrB,eAA+B;EAAE,OAAO,ENgM1B,GAAO;;AM/LrB,qBAAqC;EAAE,OAAO,EN+O1B,GAAO;;AM9O3B,qBAAqC;EAAE,OAAO,EN8hB1B,GAAO;;AM7hB3B,sBAAsC;EAAE,OAAO,EN4hB1B,GAAO;;AM3hB5B,oBAAoC;EAAE,OAAO,EN6hB1B,GAAO;;AM5hB1B,iBAAiC;EAAE,OAAO,EN2W1B,GAAO;;AM1WvB,kBAAkC;EAAE,OAAO,ENW1B,GAAO;;AMVxB,cAA8B;EAAE,OAAO,ENod1B,GAAO;;AMndpB,eAA+B;EAAE,OAAO,ENod1B,GAAO;;AMndrB,eAA+B;EAAE,OAAO,EN2B1B,GAAO;;AM1BrB,mBAAmC;EAAE,OAAO,EN2B1B,GAAO;;AM1BzB,gBAAgC;EAAE,OAAO,ENkW1B,GAAO;;AMjWtB,iBAAiC;EAAE,OAAO,ENwC1B,GAAO;;AMvCvB,eAA+B;EAAE,OAAO,EN8L1B,GAAO;;AM7LrB,eAA+B;EAAE,OAAO,ENmB1B,GAAO;;AMlBrB,iBAAiC;EAAE,OAAO,ENoP1B,GAAO;;AMnPvB,sBAAsC;EAAE,OAAO,ENid1B,GAAO;;AMhd5B,qBAAqC;EAAE,OAAO,ENid1B,GAAO;;AMhd3B,qBAAqC;EAAE,OAAO,EN1C1B,GAAO;;AM2C3B,uBAAuC;EAAE,OAAO,EN7C1B,GAAO;;AM8C7B,sBAAsC;EAAE,OAAO,EN3C1B,GAAO;;AM4C5B,wBAAwC;EAAE,OAAO,EN9C1B,GAAO;;AM+C9B,eAA+B;EAAE,OAAO,ENwQ1B,GAAO;;AMvQrB;kBACkC;EAAE,OAAO,ENmT1B,GAAO;;AMlTxB,iBAAiC;EAAE,OAAO,ENmO1B,GAAO;;AMlOvB,uBAAuC;EAAE,OAAO,ENigB1B,GAAO;;AMhgB7B;;oBAEoC;EAAE,OAAO,EN+T1B,GAAO;;AM9T1B,iBAAiC;EAAE,OAAO,ENwT1B,GAAO;;AMvTvB,qBAAqC;EAAE,OAAO,EN+Q1B,GAAO;;AM9Q3B,iBAAiC;EAAE,OAAO,EN5D1B,GAAO;;AM6DvB,eAA+B;EAAE,OAAO,EN8c1B,GAAO;;AM7crB;0BAC0C;EAAE,OAAO,ENqT1B,GAAO;;AMpThC,yBAAyC;EAAE,OAAO,ENuX1B,GAAO;;AMtX/B,yBAAyC;EAAE,OAAO,EN0C1B,GAAO;;AMzC/B,iBAAiC;EAAE,OAAO,ENjC1B,GAAO;;AMkCvB,wBAAwC;EAAE,OAAO,ENma1B,GAAO;;AMla9B,wBAAwC;EAAE,OAAO,EN4H1B,GAAO;;AM3H9B,mBAAmC;EAAE,OAAO,EN7B1B,GAAO;;AM8BzB,eAA+B;EAAE,OAAO,EN0T1B,GAAO;;AMzTrB,gBAAgC;EAAE,OAAO,ENwS1B,GAAO;;AMvStB,eAA+B;EAAE,OAAO,ENia1B,GAAO;;AMharB,kBAAkC;EAAE,OAAO,ENgK1B,GAAO;;AM/JxB,uBAAuC;EAAE,OAAO,ENuH1B,GAAO;;AMtH7B,uBAAuC;EAAE,OAAO,EN4Z1B,GAAO;;AM3Z7B,gBAAgC;EAAE,OAAO,EN4F1B,GAAO;;AM3FtB,uBAAuC;EAAE,OAAO,ENoC1B,GAAO;;AMnC7B,wBAAwC;EAAE,OAAO,ENoC1B,GAAO;;AMnC9B,sBAAsC;EAAE,OAAO,ENsT1B,GAAO;;AMrT5B,uBAAuC;EAAE,OAAO,ENyQ1B,GAAO;;AMxQ7B,uBAAuC;EAAE,OAAO,ENwb1B,GAAO;;AMvb7B,uBAAuC;EAAE,OAAO,ENsB1B,GAAO;;AMrB7B,0BAA0C;EAAE,OAAO,EN2T1B,GAAO;;AM1ThC,sBAAsC;EAAE,OAAO,ENsM1B,GAAO;;AMrM5B,qBAAqC;EAAE,OAAO,EN6D1B,GAAO;;AM5D3B,yBAAyC;EAAE,OAAO,ENob1B,GAAO;;AMnb/B,yBAAyC;EAAE,OAAO,ENkB1B,GAAO;;AMjB/B,cAA8B;EAAE,OAAO,EN/C1B,GAAO;;AMgDpB,qBAAqC;EAAE,OAAO,EN3D1B,GAAO;;AM4D3B,sBAAsC;EAAE,OAAO,EN3D1B,GAAO;;AM4D5B,mBAAmC;EAAE,OAAO,EN3D1B,GAAO;;AM4DzB,qBAAqC;EAAE,OAAO,EN/D1B,GAAO;;AMgE3B;gBACgC;EAAE,OAAO,ENqV1B,GAAO;;AMpVtB,iBAAiC;EAAE,OAAO,ENuF1B,GAAO;;AMtFvB,mBAAmC;EAAE,OAAO,EN4C1B,GAAO;;AM3CzB,eAA+B;EAAE,OAAO,ENmS1B,GAAO;;AMlSrB,gBAAgC;EAAE,OAAO,ENsP1B,GAAO;;AMrPtB,mBAAmC;EAAE,OAAO,EN9D1B,GAAO;;AM+DzB,6BAA6C;EAAE,OAAO,ENgF1B,GAAO;;AM/EnC,eAA+B;EAAE,OAAO,EN+I1B,GAAO;;AM9IrB,eAA+B;EAAE,OAAO,ENoM1B,GAAO;;AMnMrB,eAA+B;EAAE,OAAO,ENmH1B,GAAO;;AMlHrB,cAA8B;EAAE,OAAO,ENiF1B,GAAO;;AMhFpB,oBAAoC;EAAE,OAAO,ENiF1B,GAAO;;AMhF1B;+BAC+C;EAAE,OAAO,EN0E1B,GAAO;;AMzErC,gBAAgC;EAAE,OAAO,ENmR1B,GAAO;;AMlRtB,mBAAmC;EAAE,OAAO,EN/B1B,GAAO;;AMgCzB,iBAAiC;EAAE,OAAO,ENoS1B,GAAO;;AMnSvB,kBAAkC;EAAE,OAAO,ENwB1B,GAAO;;AMvBxB,iBAAiC;EAAE,OAAO,ENqN1B,GAAO;;AMpNvB,qBAAqC;EAAE,OAAO,ENE1B,GAAO;;AMD3B,uBAAuC;EAAE,OAAO,ENF1B,GAAO;;AMG7B,kBAAkC;EAAE,OAAO,EN2S1B,GAAO;;AM1SxB,wBAAwC;EAAE,OAAO,ENyU1B,GAAO;;AMxU9B,iBAAiC;EAAE,OAAO,EN8G1B,GAAO;;AM7GvB,sBAAsC;EAAE,OAAO,EN+G1B,GAAO;;AM9G5B,mBAAmC;EAAE,OAAO,ENnF1B,GAAO;;AMoFzB,mBAAmC;EAAE,OAAO,ENrF1B,GAAO;;AMsFzB;oBACoC;EAAE,OAAO,EN/E1B,GAAO;;AMgF1B,yBAAyC;EAAE,OAAO,ENua1B,GAAO;;AMta/B,0BAA0C;EAAE,OAAO,ENmE1B,GAAO;;AMlEhC,uBAAuC;EAAE,OAAO,EN5C1B,GAAO;;AM6C7B,cAA8B;EAAE,OAAO,ENqK1B,GAAO;;AMpKpB;eAC+B;EAAE,OAAO,ENK1B,GAAO;;AMJrB,mBAAmC;EAAE,OAAO,ENQ1B,GAAO;;AMPzB,sBAAsC;EAAE,OAAO,ENmY1B,GAAO;;AMlY5B,wBAAwC;EAAE,OAAO,ENiY1B,GAAO;;AMhY9B,oBAAoC;EAAE,OAAO,EN2V1B,GAAO;;AM1V1B,kBAAkC;EAAE,OAAO,ENyI1B,GAAO;;AMxIxB,mBAAmC;EAAE,OAAO,ENyT1B,GAAO;;AMxTzB,0BAA0C;EAAE,OAAO,ENiL1B,GAAO;;AMhLhC,qBAAqC;EAAE,OAAO,EN0X1B,GAAO;;AMzX3B,wBAAwC;EAAE,OAAO,EN8C1B,GAAO;;AM7C9B,kBAAkC;EAAE,OAAO,ENoT1B,GAAO;;AMnTxB,iBAAiC;EAAE,OAAO,EN8Y1B,GAAO;;AM7YvB,wBAAwC;EAAE,OAAO,EN6G1B,GAAO;;AM5G9B,iBAAiC;EAAE,OAAO,EN8Z1B,GAAO;;AM7ZvB,kBAAkC;EAAE,OAAO,EN+J1B,GAAO;;AM9JxB,gBAAgC;EAAE,OAAO,ENsO1B,GAAO;;AMrOtB,mBAAmC;EAAE,OAAO,EN2U1B,GAAO;;AM1UzB,qBAAqC;EAAE,OAAO,EN/E1B,GAAO;;AMgF3B,uBAAuC;EAAE,OAAO,ENoO1B,GAAO;;AMnO7B,kBAAkC;EAAE,OAAO,EN8Y1B,GAAO;;AM7YxB;mBACmC;EAAE,OAAO,ENuC1B,GAAO;;AMtCzB,iBAAiC;EAAE,OAAO,ENiG1B,GAAO;;AMhGvB,iBAAiC;EAAE,OAAO,ENiZ1B,GAAO;;AMhZvB,sBAAsC;EAAE,OAAO,ENR1B,GAAO;;AMS5B,cAA8B;EAAE,OAAO,EN4Q1B,GAAO;;AM3QpB,gBAAgC;EAAE,OAAO,ENgH1B,GAAO;;AM/GtB,mBAAmC;EAAE,OAAO,ENnF1B,GAAO;;AMoFzB,eAA+B;EAAE,OAAO,ENzG1B,GAAO;;AM0GrB,sBAAsC;EAAE,OAAO,ENzD1B,GAAO;;AM0D5B,uBAAuC;EAAE,OAAO,EN0G1B,GAAO;;AMzG7B,sBAAsC;EAAE,OAAO,ENwG1B,GAAO;;AMvG5B,oBAAoC;EAAE,OAAO,ENyG1B,GAAO;;AMxG1B,sBAAsC;EAAE,OAAO,ENqG1B,GAAO;;AMpG5B,4BAA4C;EAAE,OAAO,EN5I1B,GAAO;;AM6IlC,6BAA6C;EAAE,OAAO,ENxI1B,GAAO;;AMyInC,0BAA0C;EAAE,OAAO,ENxI1B,GAAO;;AMyIhC,4BAA4C;EAAE,OAAO,ENhJ1B,GAAO;;AMiJlC,gBAAgC;EAAE,OAAO,ENsF1B,GAAO;;AMrFtB,iBAAiC;EAAE,OAAO,ENia1B,GAAO;;AMhavB,gBAAgC;EAAE,OAAO,ENiV1B,GAAO;;AMhVtB,iBAAiC;EAAE,OAAO,ENgD1B,GAAO;;AM/CvB,oBAAoC;EAAE,OAAO,ENvG1B,GAAO;;AMwG1B,qBAAqC;EAAE,OAAO,ENzI1B,GAAO;;AM0I3B;gBACgC;EAAE,OAAO,ENqY1B,GAAO;;AMpYtB;eAC+B;EAAE,OAAO,ENuI1B,GAAO;;AMtIrB,gBAAgC;EAAE,OAAO,ENpD1B,GAAO;;AMqDtB,gBAAgC;EAAE,OAAO,EN+C1B,GAAO;;AM9CtB;mBACmC;EAAE,OAAO,ENwP1B,GAAO;;AMvPzB;kBACkC;EAAE,OAAO,ENkC1B,GAAO;;AMjCxB,oBAAoC;EAAE,OAAO,ENsL1B,GAAO;;AMrL1B;mBACmC;EAAE,OAAO,EN0C1B,GAAO;;AMzCzB,iBAAiC;EAAE,OAAO,ENiS1B,GAAO;;AMhSvB;;eAE+B;EAAE,OAAO,EN9I1B,GAAO;;AM+IrB,kBAAkC;EAAE,OAAO,ENgI1B,GAAO;;AM/HxB,kBAAkC;EAAE,OAAO,EN8H1B,GAAO;;AM7HxB,wBAAwC;EAAE,OAAO,EN4S1B,GAAO;;AM3S9B,oBAAoC;EAAE,OAAO,ENoW1B,GAAO;;AMnW1B,gBAAgC;EAAE,OAAO,ENmT1B,GAAO;;AMlTtB,gBAAgC;EAAE,OAAO,ENkI1B,GAAO;;AMjItB,gBAAgC;EAAE,OAAO,ENuV1B,GAAO;;AMtVtB,oBAAoC;EAAE,OAAO,ENwL1B,GAAO;;AMvL1B,2BAA2C;EAAE,OAAO,ENyL1B,GAAO;;AMxLjC,6BAA6C;EAAE,OAAO,ENyD1B,GAAO;;AMxDnC,sBAAsC;EAAE,OAAO,ENuD1B,GAAO;;AMtD5B,gBAAgC;EAAE,OAAO,ENsJ1B,GAAO;;AMrJtB,qBAAqC;EAAE,OAAO,ENtH1B,GAAO;;AMuH3B,mBAAmC;EAAE,OAAO,ENhH1B,GAAO;;AMiHzB,qBAAqC;EAAE,OAAO,ENvH1B,GAAO;;AMwH3B,sBAAsC;EAAE,OAAO,ENvH1B,GAAO;;AMwH5B,kBAAkC;EAAE,OAAO,ENvE1B,GAAO;;AMwExB;eAC+B;EAAE,OAAO,EN2P1B,GAAO;;AM1PrB;oBACoC;EAAE,OAAO,EN+P1B,GAAO;;AM9P1B;mBACmC;EAAE,OAAO,EN4P1B,GAAO;;AM3PzB,mBAAmC;EAAE,OAAO,ENxC1B,GAAO;;AMyCzB,mBAAmC;EAAE,OAAO,ENkG1B,GAAO;;AMjGzB;eAC+B;EAAE,OAAO,EN8U1B,GAAO;;AM7UrB;gBACgC;EAAE,OAAO,ENqB1B,GAAO;;AMpBtB;qBACqC;EAAE,OAAO,EN2R1B,GAAO;;AM1R3B,oBAAoC;EAAE,OAAO,ENpF1B,GAAO;;AMqF1B,qBAAqC;EAAE,OAAO,ENnF1B,GAAO;;AMoF3B;eAC+B;EAAE,OAAO,ENjK1B,GAAO;;AMkKrB,kBAAkC;EAAE,OAAO,ENkO1B,GAAO;;AMjOxB,mBAAmC;EAAE,OAAO,ENkU1B,GAAO;;AMjUzB;oBACoC;EAAE,OAAO,EN1G1B,GAAO;;AM2G1B,sBAAsC;EAAE,OAAO,ENgF1B,GAAO;;AM/E5B,mBAAmC;EAAE,OAAO,ENnD1B,GAAO;;AMoDzB,yBAAyC;EAAE,OAAO,ENzG1B,GAAO;;AM0G/B,uBAAuC;EAAE,OAAO,ENzG1B,GAAO;;AM0G7B,kBAAkC;EAAE,OAAO,ENsU1B,GAAO;;AMrUxB,sBAAsC;EAAE,OAAO,EN+P1B,GAAO;;AM9P5B,mBAAmC;EAAE,OAAO,ENsQ1B,GAAO;;AMrQzB,iBAAiC;EAAE,OAAO,ENvL1B,GAAO;;AMwLvB,iBAAiC;EAAE,OAAO,ENzG1B,GAAO;;AM0GvB,kBAAkC;EAAE,OAAO,ENtF1B,GAAO;;AMuFxB,sBAAsC;EAAE,OAAO,EN3B1B,GAAO;;AM4B5B,qBAAqC;EAAE,OAAO,ENxK1B,GAAO;;AMyK3B,qBAAqC;EAAE,OAAO,ENkC1B,GAAO;;AMjC3B,oBAAoC;EAAE,OAAO,EN3O1B,GAAO;;AM4O1B,iBAAiC;EAAE,OAAO,ENiG1B,GAAO;;AMhGvB,sBAAsC;EAAE,OAAO,EN/C1B,GAAO;;AMgD5B,eAA+B;EAAE,OAAO,ENpM1B,GAAO;;AMqMrB,mBAAmC;EAAE,OAAO,ENe1B,GAAO;;AMdzB,sBAAsC;EAAE,OAAO,ENgJ1B,GAAO;;AM/I5B,4BAA4C;EAAE,OAAO,EN5O1B,GAAO;;AM6OlC,6BAA6C;EAAE,OAAO,EN5O1B,GAAO;;AM6OnC,0BAA0C;EAAE,OAAO,EN5O1B,GAAO;;AM6OhC,4BAA4C;EAAE,OAAO,ENhP1B,GAAO;;AMiPlC,qBAAqC;EAAE,OAAO,EN5O1B,GAAO;;AM6O3B,sBAAsC;EAAE,OAAO,EN5O1B,GAAO;;AM6O5B,mBAAmC;EAAE,OAAO,EN5O1B,GAAO;;AM6OzB,qBAAqC;EAAE,OAAO,ENhP1B,GAAO;;AMiP3B,kBAAkC;EAAE,OAAO,ENlG1B,GAAO;;AMmGxB,iBAAiC;EAAE,OAAO,ENuC1B,GAAO;;AMtCvB,iBAAiC;EAAE,OAAO,ENoP1B,GAAO;;AMnPvB;iBACiC;EAAE,OAAO,ENyF1B,GAAO;;AMxFvB,mBAAmC;EAAE,OAAO,EN9I1B,GAAO;;AM+IzB,qBAAqC;EAAE,OAAO,EN0I1B,GAAO;;AMzI3B,sBAAsC;EAAE,OAAO,EN0I1B,GAAO;;AMzI5B,kBAAkC;EAAE,OAAO,ENgN1B,GAAO;;AM/MxB,iBAAiC;EAAE,OAAO,ENnJ1B,GAAO;;AMoJvB;gBACgC;EAAE,OAAO,ENkJ1B,GAAO;;AMjJtB,qBAAqC;EAAE,OAAO,ENnB1B,GAAO;;AMoB3B,mBAAmC;EAAE,OAAO,ENxC1B,GAAO;;AMyCzB,wBAAwC;EAAE,OAAO,ENvC1B,GAAO;;AMwC9B,kBAAkC;EAAE,OAAO,EN0L1B,GAAO;;AMzLxB,kBAAkC;EAAE,OAAO,ENpC1B,GAAO;;AMqCxB,gBAAgC;EAAE,OAAO,ENoE1B,GAAO;;AMnEtB,kBAAkC;EAAE,OAAO,ENpC1B,GAAO;;AMqCxB,qBAAqC;EAAE,OAAO,ENkB1B,GAAO;;AMjB3B,iBAAiC;EAAE,OAAO,ENrD1B,GAAO;;AMsDvB,yBAAyC;EAAE,OAAO,ENvD1B,GAAO;;AMwD/B,mBAAmC;EAAE,OAAO,ENuO1B,GAAO;;AMtOzB,eAA+B;EAAE,OAAO,ENtJ1B,GAAO;;AMuJrB;oBACoC;EAAE,OAAO,ENqI1B,GAAO;;AMpI1B;;sBAEsC;EAAE,OAAO,ENuM1B,GAAO;;AMtM5B,yBAAyC;EAAE,OAAO,ENkC1B,GAAO;;AMjC/B,eAA+B;EAAE,OAAO,EN5I1B,GAAO;;AM6IrB,oBAAoC;EAAE,OAAO,EN7J1B,GAAO;;AM8J1B;uBACuC;EAAE,OAAO,EN1L1B,GAAO;;AM2L7B,mBAAmC;EAAE,OAAO,EN4G1B,GAAO;;AM3GzB,eAA+B;EAAE,OAAO,ENT1B,GAAO;;AMUrB,sBAAsC;EAAE,OAAO,ENhH1B,GAAO;;AMiH5B,sBAAsC;EAAE,OAAO,EN8M1B,GAAO;;AM7M5B,oBAAoC;EAAE,OAAO,ENyM1B,GAAO;;AMxM1B,iBAAiC;EAAE,OAAO,ENvH1B,GAAO;;AMwHvB,uBAAuC;EAAE,OAAO,ENmG1B,GAAO;;AMlG7B,qBAAqC;EAAE,OAAO,EN8C1B,GAAO;;AM7C3B,2BAA2C;EAAE,OAAO,EN8C1B,GAAO;;AM7CjC,iBAAiC;EAAE,OAAO,ENgJ1B,GAAO;;AM/IvB,qBAAqC;EAAE,OAAO,EN5N1B,GAAO;;AM6N3B,4BAA4C;EAAE,OAAO,ENjF1B,GAAO;;AMkFlC,iBAAiC;EAAE,OAAO,ENoH1B,GAAO;;AMnHvB,iBAAiC;EAAE,OAAO,ENkC1B,GAAO;;AMjCvB,8BAA8C;EAAE,OAAO,ENlM1B,GAAO;;AMmMpC,+BAA+C;EAAE,OAAO,ENlM1B,GAAO;;AMmMrC,4BAA4C;EAAE,OAAO,ENlM1B,GAAO;;AMmMlC,8BAA8C;EAAE,OAAO,ENtM1B,GAAO;;AMuMpC,gBAAgC;EAAE,OAAO,EN/B1B,GAAO;;AMgCtB,eAA+B;EAAE,OAAO,ENjK1B,GAAO;;AMkKrB,iBAAiC;EAAE,OAAO,EN9S1B,GAAO;;AM+SvB,qBAAqC;EAAE,OAAO,ENmP1B,GAAO;;AMlP3B,mBAAmC;EAAE,OAAO,EN9O1B,GAAO;;AM+OzB,qBAAqC;EAAE,OAAO,EN/I1B,GAAO;;AMgJ3B,qBAAqC;EAAE,OAAO,EN/I1B,GAAO;;AMgJ3B,qBAAqC;EAAE,OAAO,EN4G1B,GAAO;;AM3G3B,sBAAsC;EAAE,OAAO,ENsE1B,GAAO;;AMrE5B,iBAAiC;EAAE,OAAO,EN2M1B,GAAO;;AM1MvB,uBAAuC;EAAE,OAAO,EN6B1B,GAAO;;AM5B7B,yBAAyC;EAAE,OAAO,EN6B1B,GAAO;;AM5B/B,mBAAmC;EAAE,OAAO,ENhB1B,GAAO;;AMiBzB,qBAAqC;EAAE,OAAO,ENlB1B,GAAO;;AMmB3B,uBAAuC;EAAE,OAAO,ENvN1B,GAAO;;AMwN7B,wBAAwC;EAAE,OAAO,ENiD1B,GAAO;;AMhD9B,+BAA+C;EAAE,OAAO,EN3I1B,GAAO;;AM4IrC,uBAAuC;EAAE,OAAO,ENkH1B,GAAO;;AMjH7B,kBAAkC;EAAE,OAAO,EN1L1B,GAAO;;AM2LxB;8BAC8C;EAAE,OAAO,ENjP1B,GAAO;;AMkPpC;4BAC4C;EAAE,OAAO,ENhP1B,GAAO;;AMiPlC;+BAC+C;EAAE,OAAO,ENnP1B,GAAO;;AMoPrC;cAC8B;EAAE,OAAO,EN7J1B,GAAO;;AM8JpB,cAA8B;EAAE,OAAO,EN/F1B,GAAO;;AMgGpB;cAC8B;EAAE,OAAO,EN4N1B,GAAO;;AM3NpB;cAC8B;EAAE,OAAO,ENvD1B,GAAO;;AMwDpB;;;cAG8B;EAAE,OAAO,ENrD1B,GAAO;;AMsDpB;;cAE8B;EAAE,OAAO,EN8E1B,GAAO;;AM7EpB;cAC8B;EAAE,OAAO,ENtD1B,GAAO;;AMuDpB;cAC8B;EAAE,OAAO,ENzR1B,GAAO;;AM0RpB,eAA+B;EAAE,OAAO,ENzJ1B,GAAO;;AM0JrB,oBAAoC;EAAE,OAAO,EN7I1B,GAAO;;AM8I1B,yBAAyC;EAAE,OAAO,EN2G1B,GAAO;;AM1G/B,0BAA0C;EAAE,OAAO,EN2G1B,GAAO;;AM1GhC,0BAA0C;EAAE,OAAO,EN2G1B,GAAO;;AM1GhC,2BAA2C;EAAE,OAAO,EN2G1B,GAAO;;AM1GjC,2BAA2C;EAAE,OAAO,EN8G1B,GAAO;;AM7GjC,4BAA4C;EAAE,OAAO,EN8G1B,GAAO;;AM7GlC,oBAAoC;EAAE,OAAO,ENgK1B,GAAO;;AM/J1B,sBAAsC;EAAE,OAAO,EN4J1B,GAAO;;AM3J5B,yBAAyC;EAAE,OAAO,ENwO1B,GAAO;;AMvO/B,kBAAkC;EAAE,OAAO,ENqO1B,GAAO;;AMpOxB,eAA+B;EAAE,OAAO,EN+N1B,GAAO;;AM9NrB,sBAAsC;EAAE,OAAO,EN+N1B,GAAO;;AM9N5B,uBAAuC;EAAE,OAAO,ENmO1B,GAAO;;AMlO7B,kBAAkC;EAAE,OAAO,ENxM1B,GAAO;;AMyMxB,yBAAyC;EAAE,OAAO,EN+G1B,GAAO;;AM9G/B,oBAAoC;EAAE,OAAO,ENnF1B,GAAO;;AMoF1B,iBAAiC;EAAE,OAAO,EN/I1B,GAAO;;AMgJvB,cAA8B;EAAE,OAAO,ENhX1B,GAAO;;AMiXpB,oBAAoC;EAAE,OAAO,ENxT1B,GAAO;;AMyT1B,2BAA2C;EAAE,OAAO,ENxT1B,GAAO;;AMyTjC,iBAAiC;EAAE,OAAO,ENyK1B,GAAO;;AMxKvB,wBAAwC;EAAE,OAAO,ENyK1B,GAAO;;AMxK9B,0BAA0C;EAAE,OAAO,ENtD1B,GAAO;;AMuDhC,wBAAwC;EAAE,OAAO,ENpD1B,GAAO;;AMqD9B,0BAA0C;EAAE,OAAO,ENvD1B,GAAO;;AMwDhC,2BAA2C;EAAE,OAAO,ENvD1B,GAAO;;AMwDjC,gBAAgC;EAAE,OAAO,ENxW1B,GAAO;;AMyWtB,kBAAkC;EAAE,OAAO,EN0M1B,GAAO;;AMzMxB,kBAAkC;EAAE,OAAO,ENpX1B,GAAO;;AMqXxB,gBAAgC;EAAE,OAAO,ENpE1B,GAAO;;AMqEtB,mBAAmC;EAAE,OAAO,EN1N1B,GAAO;;AM2NzB,gBAAgC;EAAE,OAAO,ENqE1B,GAAO;;AMpEtB,qBAAqC;EAAE,OAAO,ENtJ1B,GAAO;;AMuJ3B,iBAAiC;EAAE,OAAO,ENuJ1B,GAAO;;AMtJvB,iBAAiC;EAAE,OAAO,EN/L1B,GAAO;;AMgMvB,eAA+B;EAAE,OAAO,EN1D1B,GAAO;;AM2DrB;mBACmC;EAAE,OAAO,ENnI1B,GAAO;;AMoIzB,gBAAgC;EAAE,OAAO,EN2G1B,GAAO;;AM1GtB,iBAAiC;EAAE,OAAO,ENxC1B,GAAO;;AMyCvB,kBAAkC;EAAE,OAAO,ENrX1B,GAAO;;AMsXxB,cAA8B;EAAE,OAAO,ENpU1B,GAAO;;AMqUpB,aAA6B;EAAE,OAAO,ENgL1B,GAAO;;AM/KnB,gBAAgC;EAAE,OAAO,ENqL1B,GAAO;;AMpLtB,iBAAiC;EAAE,OAAO,ENa1B,GAAO;;AMZvB,oBAAoC;EAAE,OAAO,ENrC1B,GAAO;;AMsC1B,yBAAyC;EAAE,OAAO,EN8E1B,GAAO;;AM7E/B,+BAA+C;EAAE,OAAO,ENtX1B,GAAO;;AMuXrC,8BAA8C;EAAE,OAAO,ENxX1B,GAAO;;AMyXpC;8BAC8C;EAAE,OAAO,EN3T1B,GAAO;;AM4TpC,uBAAuC;EAAE,OAAO,ENjP1B,GAAO;;AMkP7B,qBAAqC;EAAE,OAAO,EN+K1B,GAAO;;AM9K3B,uBAAuC;EAAE,OAAO,ENmK1B,GAAO;;AMlK7B;cAC8B;EAAE,OAAO,ENoI1B,GAAO;;AMnIpB,wBAAwC;EAAE,OAAO,ENjB1B,GAAO;;AMkB9B,wBAAwC;EAAE,OAAO,EN6D1B,GAAO;;AM5D9B,gBAAgC;EAAE,OAAO,EN2C1B,GAAO;;AM1CtB,0BAA0C;EAAE,OAAO,EN7O1B,GAAO;;AM8OhC,oBAAoC;EAAE,OAAO,EN2K1B,GAAO;;AM1K1B,iBAAiC;EAAE,OAAO,ENvD1B,GAAO;;AMwDvB;;qBAEqC;EAAE,OAAO,ENsI1B,GAAO;;AMrI3B;yBACyC;EAAE,OAAO,ENjK1B,GAAO;;AMkK/B,gBAAgC;EAAE,OAAO,ENwK1B,GAAO;;AMvKtB,iBAAiC;EAAE,OAAO,ENvK1B,GAAO;;AMwKvB,iBAAiC;EAAE,OAAO,ENhB1B,GAAO;;AMiBvB,wBAAwC;EAAE,OAAO,ENhB1B,GAAO;;AMiB9B,6BAA6C;EAAE,OAAO,ENsE1B,GAAO;;AMrEnC,sBAAsC;EAAE,OAAO,ENoE1B,GAAO;;AMnE5B,oBAAoC;EAAE,OAAO,EN7Q1B,GAAO;;AM8Q1B,eAA+B;EAAE,OAAO,EN1Q1B,GAAO;;AM2QrB,qBAAqC;EAAE,OAAO,ENjD1B,GAAO;;AMkD3B,yBAAyC;EAAE,OAAO,ENjD1B,GAAO;;AMkD/B,iBAAiC;EAAE,OAAO,ENvQ1B,GAAO;;AMwQvB,iBAAiC;EAAE,OAAO,EN9I1B,GAAO;;AM+IvB,mBAAmC;EAAE,OAAO,ENzI1B,GAAO;;AM0IzB,cAA8B;EAAE,OAAO,EN9O1B,GAAO;;AM+OpB,mBAAmC;EAAE,OAAO,EN3W1B,GAAO;;AM4WzB,gBAAgC;EAAE,OAAO,EN9T1B,GAAO;;AM+TtB,cAA8B;EAAE,OAAO,ENnE1B,GAAO;;AMoEpB,gBAAgC;EAAE,OAAO,ENoC1B,GAAO;;AMnCtB,eAA+B;EAAE,OAAO,ENjS1B,GAAO;;AMkSrB,gBAAgC;EAAE,OAAO,ENjS1B,GAAO;;AMkStB,kBAAkC;EAAE,OAAO,ENtY1B,GAAO;;AMuYxB,yBAAyC;EAAE,OAAO,ENtY1B,GAAO;;AMuY/B,gBAAgC;EAAE,OAAO,EN2C1B,GAAO;;AM1CtB,uBAAuC;EAAE,OAAO,EN2C1B,GAAO;;AM1C7B,kBAAkC;EAAE,OAAO,ENvC1B,GAAO;;AMwCxB;cAC8B;EAAE,OAAO,EN3W1B,GAAO;;AM4WpB;eAC+B;EAAE,OAAO,EN2D1B,GAAO;;AM1DrB,eAA+B;EAAE,OAAO,ENuF1B,GAAO;;AMtFrB,kBAAkC;EAAE,OAAO,ENwB1B,GAAO;;AMvBxB,qBAAqC;EAAE,OAAO,ENpS1B,GAAO;;AMqS3B,qBAAqC;EAAE,OAAO,ENkB1B,GAAO;;AMjB3B,mBAAmC;EAAE,OAAO,EN1S1B,GAAO;;AM2SzB,qBAAqC;EAAE,OAAO,ENxP1B,GAAO;;AMyP3B,sBAAsC;EAAE,OAAO,ENjP1B,GAAO;;AMkP5B,uBAAuC;EAAE,OAAO,EN9P1B,GAAO;;AM+P7B,4BAA4C;EAAE,OAAO,ENxP1B,GAAO;;AMyPlC;;uBAEuC;EAAE,OAAO,ENjQ1B,GAAO;;AMkQ7B;yBACyC;EAAE,OAAO,ENvQ1B,GAAO;;AMwQ/B;uBACuC;EAAE,OAAO,ENxQ1B,GAAO;;AMyQ7B;uBACuC;EAAE,OAAO,EN7P1B,GAAO;;AM8P7B,sBAAsC;EAAE,OAAO,EN1Q1B,GAAO;;AM2Q5B,eAA+B;EAAE,OAAO,ENsG1B,GAAO;;AMrGrB,kBAAkC;EAAE,OAAO,ENlV1B,GAAO;;AMmVxB,mBAAmC;EAAE,OAAO,ENnL1B,GAAO;;AMoLzB;;;;oBAIoC;EAAE,OAAO,ENxK1B,GAAO;;AMyK1B,yBAAyC;EAAE,OAAO,ENpW1B,GAAO;;AMqW/B;gBACgC;EAAE,OAAO,EN1E1B,GAAO;;AM2EtB;iBACiC;EAAE,OAAO,ENpT1B,GAAO;;AMqTvB,qBAAqC;EAAE,OAAO,EN1O1B,GAAO;;AM2O3B,cAA8B;EAAE,OAAO,EN5O1B,GAAO;;AM6OpB,sBAAsC;EAAE,OAAO,EN7N1B,GAAO;;AM8N5B,wBAAwC;EAAE,OAAO,ENwB1B,GAAO;;AMvB9B,aAA6B;EAAE,OAAO,ENzF1B,GAAO;;AM0FnB;iBACiC;EAAE,OAAO,EN2F1B,GAAO;;AM1FvB;sBACsC;EAAE,OAAO,EN9H1B,GAAO;;AM+H5B;wBACwC;EAAE,OAAO,EN/H1B,GAAO;;AMgI9B,kBAAkC;EAAE,OAAO,EN3N1B,GAAO;;AM4NxB;sBACsC;EAAE,OAAO,ENrX1B,GAAO;;AMsX5B,iBAAiC;EAAE,OAAO,ENnO1B,GAAO;;AMoOvB,oBAAoC;EAAE,OAAO,ENlI1B,GAAO;;AMmI1B,kBAAkC;EAAE,OAAO,EN1C1B,GAAO;;AM2CxB,oBAAoC;EAAE,OAAO,EN7D1B,GAAO;;AM8D1B,2BAA2C;EAAE,OAAO,EN7D1B,GAAO;;AM8DjC,eAA+B;EAAE,OAAO,ENpb1B,GAAO;;AMqbrB;mBACmC;EAAE,OAAO,ENzQ1B,GAAO;;AM0QzB,cAA8B;EAAE,OAAO,ENsC1B,GAAO;;AMrCpB,qBAAqC;EAAE,OAAO,EN/b1B,GAAO;;AMgc3B,eAA+B;EAAE,OAAO,ENrH1B,GAAO;;AMsHrB,qBAAqC;EAAE,OAAO,ENlD1B,GAAO;;AMmD3B,iBAAiC;EAAE,OAAO,ENsC1B,GAAO;;AMrCvB,eAA+B;EAAE,OAAO,ENiF1B,GAAO;;AMhFrB,sBAAsC;EAAE,OAAO,ENvJ1B,GAAO;;AMwJ5B,eAA+B;EAAE,OAAO,ENuE1B,GAAO;;AMtErB,qBAAqC;EAAE,OAAO,ENjb1B,GAAO;;AMkb3B,iBAAiC;EAAE,OAAO,EN9I1B,GAAO;;AM+IvB,wBAAwC;EAAE,OAAO,ENhQ1B,GAAO;;AMiQ9B,kBAAkC;EAAE,OAAO,EN9Z1B,GAAO;;AM+ZxB,wBAAwC;EAAE,OAAO,ENla1B,GAAO;;AMma9B,sBAAsC;EAAE,OAAO,ENpa1B,GAAO;;AMqa5B,kBAAkC;EAAE,OAAO,ENta1B,GAAO;;AMuaxB,oBAAoC;EAAE,OAAO,ENpa1B,GAAO;;AMqa1B,oBAAoC;EAAE,OAAO,ENpa1B,GAAO;;AMqa1B,qBAAqC;EAAE,OAAO,ENld1B,GAAO;;AMmd3B,uBAAuC;EAAE,OAAO,ENld1B,GAAO;;AMmd7B,gBAAgC;EAAE,OAAO,ENY1B,GAAO;;AMXtB,oBAAoC;EAAE,OAAO,EN3X1B,GAAO;;AM4X1B,aAA6B;EAAE,OAAO,ENre1B,GAAO;;AMsenB,qBAAqC;EAAE,OAAO,ENjV1B,GAAO;;AMkV3B,sBAAsC;EAAE,OAAO,ENpK1B,GAAO;;AMqK5B,wBAAwC;EAAE,OAAO,ENrd1B,GAAO;;AMsd9B,qBAAqC;EAAE,OAAO,EN3f1B,GAAO;;AM4f3B,oBAAoC;EAAE,OAAO,ENvJ1B,GAAO;;AMwJ1B,qBAAqC;EAAE,OAAO,EN5N1B,GAAO;;AM6N3B,iBAAiC;EAAE,OAAO,EN1O1B,GAAO;;AM2OvB,wBAAwC;EAAE,OAAO,EN1O1B,GAAO;;AM2O9B,qBAAqC;EAAE,OAAO,ENN1B,GAAO;;AMO3B,oBAAoC;EAAE,OAAO,ENN1B,GAAO;;AMO1B,kBAAkC;EAAE,OAAO,EN/d1B,GAAO;;AMgexB,cAA8B;EAAE,OAAO,EN7c1B,GAAO;;AM8cpB,kBAAkC;EAAE,OAAO,EN1P1B,GAAO;;AM2PxB,oBAAoC;EAAE,OAAO,ENhhB1B,GAAO;;AMihB1B,aAA6B;EAAE,OAAO,EN7b1B,GAAO;;AM8bnB;;cAE8B;EAAE,OAAO,ENxQ1B,GAAO;;AMyQpB,mBAAmC;EAAE,OAAO,EN7M1B,GAAO;;AM8MzB,qBAAqC;EAAE,OAAO,ENpd1B,GAAO;;AMqd3B,yBAAyC;EAAE,OAAO,ENnZ1B,GAAO;;AMoZ/B,mBAAmC;EAAE,OAAO,ENxY1B,GAAO;;AMyYzB,mBAAmC;EAAE,OAAO,EN1T1B,GAAO;;AM2TzB,kBAAkC;EAAE,OAAO,ENxP1B,GAAO;;AMyPxB,iBAAiC;EAAE,OAAO,ENrH1B,GAAO;;AMsHvB,uBAAuC;EAAE,OAAO,ENzG1B,GAAO;;AM0G7B,sBAAsC;EAAE,OAAO,ENrG1B,GAAO;;AMsG5B,mBAAmC;EAAE,OAAO,ENpG1B,GAAO;;AMqGzB,oBAAoC;EAAE,OAAO,EN5c1B,GAAO;;AM6c1B,0BAA0C;EAAE,OAAO,EN9c1B,GAAO;;AM+chC,kBAAkC;EAAE,OAAO,EN3Y1B,GAAO;;AM4YxB,eAA+B;EAAE,OAAO,ENhH1B,GAAO;;AMiHrB,sBAAsC;EAAE,OAAO,ENI1B,GAAO;;AMH5B,qBAAqC;EAAE,OAAO,EN5M1B,GAAO;;AM6M3B,sBAAsC;EAAE,OAAO,ENpE1B,GAAO;;AMqE5B,oBAAoC;EAAE,OAAO,ENhS1B,GAAO;;AMiS1B,gBAAgC;EAAE,OAAO,ENG1B,GAAO;;AMFtB,eAA+B;EAAE,OAAO,ENtO1B,GAAO;;AMuOrB,kBAAkC;EAAE,OAAO,EN7N1B,GAAO;;AM8NxB,sBAAsC;EAAE,OAAO,ENhC1B,GAAO;;AMiC5B,0BAA0C;EAAE,OAAO,ENhC1B,GAAO;;AMiChC,uBAAuC;EAAE,OAAO,END1B,GAAO;;AME7B,sBAAsC;EAAE,OAAO,EN1O1B,GAAO;;AM2O5B,qBAAqC;EAAE,OAAO,ENF1B,GAAO;;AMG3B,sBAAsC;EAAE,OAAO,EN3O1B,GAAO;;AM4O5B,wBAAwC;EAAE,OAAO,EN1O1B,GAAO;;AM2O9B,wBAAwC;EAAE,OAAO,EN5O1B,GAAO;;AM6O9B,iBAAiC;EAAE,OAAO,ENvN1B,GAAO;;AMwNvB,4BAA4C;EAAE,OAAO,EN9X1B,GAAO;;AM+XlC,sBAAsC;EAAE,OAAO,ENhM1B,GAAO;;AMiM5B,mBAAmC;EAAE,OAAO,ENI1B,GAAO;;AMHzB,iBAAiC;EAAE,OAAO,EN7I1B,GAAO;;AM8IvB,oBAAoC;EAAE,OAAO,ENjB1B,GAAO;;AMkB1B,qBAAqC;EAAE,OAAO,ENhB1B,GAAO;;AMiB3B;cAC8B;EAAE,OAAO,ENphB1B,GAAO;;AMqhBpB,kBAAkC;EAAE,OAAO,ENd1B,GAAO;;AMexB,gBAAgC;EAAE,OAAO,ENnD1B,GAAO;;AMoDtB,iBAAiC;EAAE,OAAO,ENvF1B,GAAO;;AMwFvB,iBAAiC;EAAE,OAAO,ENrP1B,GAAO",
+"sources": ["../scss/_path.scss","../scss/_core.scss","../scss/_larger.scss","../scss/_fixed-width.scss","../scss/_list.scss","../scss/_variables.scss","../scss/_bordered-pulled.scss","../scss/_animated.scss","../scss/_rotated-flipped.scss","../scss/_mixins.scss","../scss/_stacked.scss","../scss/_icons.scss"],
+"names": [],
+"file": "font-awesome.css"
+}
diff --git a/webroot/css/fonts/Font-Awesome-4.6.3/css/font-awesome.min.css b/webroot/css/fonts/Font-Awesome-4.6.3/css/font-awesome.min.css
new file mode 100644
index 0000000..9b27f8e
--- /dev/null
+++ b/webroot/css/fonts/Font-Awesome-4.6.3/css/font-awesome.min.css
@@ -0,0 +1,4 @@
+/*!
+ * Font Awesome 4.6.3 by @davegandy - http://fontawesome.io - @fontawesome
+ * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.6.3');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.6.3') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.6.3') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.6.3') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.6.3') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.6.3#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.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(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}
diff --git a/webroot/css/fonts/Font-Awesome-4.6.3/fonts/FontAwesome.otf b/webroot/css/fonts/Font-Awesome-4.6.3/fonts/FontAwesome.otf
new file mode 100644
index 0000000..f9da2c2
Binary files /dev/null and b/webroot/css/fonts/Font-Awesome-4.6.3/fonts/FontAwesome.otf differ
diff --git a/webroot/css/fonts/Font-Awesome-4.6.3/fonts/fontawesome-webfont.eot b/webroot/css/fonts/Font-Awesome-4.6.3/fonts/fontawesome-webfont.eot
new file mode 100644
index 0000000..c7b00d2
Binary files /dev/null and b/webroot/css/fonts/Font-Awesome-4.6.3/fonts/fontawesome-webfont.eot differ
diff --git a/webroot/css/fonts/Font-Awesome-4.6.3/fonts/fontawesome-webfont.svg b/webroot/css/fonts/Font-Awesome-4.6.3/fonts/fontawesome-webfont.svg
new file mode 100644
index 0000000..8b66187
--- /dev/null
+++ b/webroot/css/fonts/Font-Awesome-4.6.3/fonts/fontawesome-webfont.svg
@@ -0,0 +1,685 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/webroot/css/fonts/Font-Awesome-4.6.3/fonts/fontawesome-webfont.ttf b/webroot/css/fonts/Font-Awesome-4.6.3/fonts/fontawesome-webfont.ttf
new file mode 100644
index 0000000..f221e50
Binary files /dev/null and b/webroot/css/fonts/Font-Awesome-4.6.3/fonts/fontawesome-webfont.ttf differ
diff --git a/webroot/css/fonts/Font-Awesome-4.6.3/fonts/fontawesome-webfont.woff b/webroot/css/fonts/Font-Awesome-4.6.3/fonts/fontawesome-webfont.woff
new file mode 100644
index 0000000..6e7483c
Binary files /dev/null and b/webroot/css/fonts/Font-Awesome-4.6.3/fonts/fontawesome-webfont.woff differ
diff --git a/webroot/css/fonts/Font-Awesome-4.6.3/fonts/fontawesome-webfont.woff2 b/webroot/css/fonts/Font-Awesome-4.6.3/fonts/fontawesome-webfont.woff2
new file mode 100644
index 0000000..a27cd15
Binary files /dev/null and b/webroot/css/fonts/Font-Awesome-4.6.3/fonts/fontawesome-webfont.woff2 differ
diff --git a/webroot/css/fonts/material-icons/MaterialIcons-Regular.eot b/webroot/css/fonts/material-icons/MaterialIcons-Regular.eot
new file mode 100644
index 0000000..70508eb
Binary files /dev/null and b/webroot/css/fonts/material-icons/MaterialIcons-Regular.eot differ
diff --git a/webroot/css/fonts/material-icons/MaterialIcons-Regular.ijmap b/webroot/css/fonts/material-icons/MaterialIcons-Regular.ijmap
new file mode 100644
index 0000000..d9f1d25
--- /dev/null
+++ b/webroot/css/fonts/material-icons/MaterialIcons-Regular.ijmap
@@ -0,0 +1 @@
+{"icons":{"e84d":{"name":"3d Rotation"},"eb3b":{"name":"Ac Unit"},"e190":{"name":"Access Alarm"},"e191":{"name":"Access Alarms"},"e192":{"name":"Access Time"},"e84e":{"name":"Accessibility"},"e914":{"name":"Accessible"},"e84f":{"name":"Account Balance"},"e850":{"name":"Account Balance Wallet"},"e851":{"name":"Account Box"},"e853":{"name":"Account Circle"},"e60e":{"name":"Adb"},"e145":{"name":"Add"},"e439":{"name":"Add A Photo"},"e193":{"name":"Add Alarm"},"e003":{"name":"Add Alert"},"e146":{"name":"Add Box"},"e147":{"name":"Add Circle"},"e148":{"name":"Add Circle Outline"},"e567":{"name":"Add Location"},"e854":{"name":"Add Shopping Cart"},"e39d":{"name":"Add To Photos"},"e05c":{"name":"Add To Queue"},"e39e":{"name":"Adjust"},"e630":{"name":"Airline Seat Flat"},"e631":{"name":"Airline Seat Flat Angled"},"e632":{"name":"Airline Seat Individual Suite"},"e633":{"name":"Airline Seat Legroom Extra"},"e634":{"name":"Airline Seat Legroom Normal"},"e635":{"name":"Airline Seat Legroom Reduced"},"e636":{"name":"Airline Seat Recline Extra"},"e637":{"name":"Airline Seat Recline Normal"},"e195":{"name":"Airplanemode Active"},"e194":{"name":"Airplanemode Inactive"},"e055":{"name":"Airplay"},"eb3c":{"name":"Airport Shuttle"},"e855":{"name":"Alarm"},"e856":{"name":"Alarm Add"},"e857":{"name":"Alarm Off"},"e858":{"name":"Alarm On"},"e019":{"name":"Album"},"eb3d":{"name":"All Inclusive"},"e90b":{"name":"All Out"},"e859":{"name":"Android"},"e85a":{"name":"Announcement"},"e5c3":{"name":"Apps"},"e149":{"name":"Archive"},"e5c4":{"name":"Arrow Back"},"e5db":{"name":"Arrow Downward"},"e5c5":{"name":"Arrow Drop Down"},"e5c6":{"name":"Arrow Drop Down Circle"},"e5c7":{"name":"Arrow Drop Up"},"e5c8":{"name":"Arrow Forward"},"e5d8":{"name":"Arrow Upward"},"e060":{"name":"Art Track"},"e85b":{"name":"Aspect Ratio"},"e85c":{"name":"Assessment"},"e85d":{"name":"Assignment"},"e85e":{"name":"Assignment Ind"},"e85f":{"name":"Assignment Late"},"e860":{"name":"Assignment Return"},"e861":{"name":"Assignment Returned"},"e862":{"name":"Assignment Turned In"},"e39f":{"name":"Assistant"},"e3a0":{"name":"Assistant Photo"},"e226":{"name":"Attach File"},"e227":{"name":"Attach Money"},"e2bc":{"name":"Attachment"},"e3a1":{"name":"Audiotrack"},"e863":{"name":"Autorenew"},"e01b":{"name":"Av Timer"},"e14a":{"name":"Backspace"},"e864":{"name":"Backup"},"e19c":{"name":"Battery Alert"},"e1a3":{"name":"Battery Charging Full"},"e1a4":{"name":"Battery Full"},"e1a5":{"name":"Battery Std"},"e1a6":{"name":"Battery Unknown"},"eb3e":{"name":"Beach Access"},"e52d":{"name":"Beenhere"},"e14b":{"name":"Block"},"e1a7":{"name":"Bluetooth"},"e60f":{"name":"Bluetooth Audio"},"e1a8":{"name":"Bluetooth Connected"},"e1a9":{"name":"Bluetooth Disabled"},"e1aa":{"name":"Bluetooth Searching"},"e3a2":{"name":"Blur Circular"},"e3a3":{"name":"Blur Linear"},"e3a4":{"name":"Blur Off"},"e3a5":{"name":"Blur On"},"e865":{"name":"Book"},"e866":{"name":"Bookmark"},"e867":{"name":"Bookmark Border"},"e228":{"name":"Border All"},"e229":{"name":"Border Bottom"},"e22a":{"name":"Border Clear"},"e22b":{"name":"Border Color"},"e22c":{"name":"Border Horizontal"},"e22d":{"name":"Border Inner"},"e22e":{"name":"Border Left"},"e22f":{"name":"Border Outer"},"e230":{"name":"Border Right"},"e231":{"name":"Border Style"},"e232":{"name":"Border Top"},"e233":{"name":"Border Vertical"},"e06b":{"name":"Branding Watermark"},"e3a6":{"name":"Brightness 1"},"e3a7":{"name":"Brightness 2"},"e3a8":{"name":"Brightness 3"},"e3a9":{"name":"Brightness 4"},"e3aa":{"name":"Brightness 5"},"e3ab":{"name":"Brightness 6"},"e3ac":{"name":"Brightness 7"},"e1ab":{"name":"Brightness Auto"},"e1ac":{"name":"Brightness High"},"e1ad":{"name":"Brightness Low"},"e1ae":{"name":"Brightness Medium"},"e3ad":{"name":"Broken Image"},"e3ae":{"name":"Brush"},"e6dd":{"name":"Bubble Chart"},"e868":{"name":"Bug Report"},"e869":{"name":"Build"},"e43c":{"name":"Burst Mode"},"e0af":{"name":"Business"},"eb3f":{"name":"Business Center"},"e86a":{"name":"Cached"},"e7e9":{"name":"Cake"},"e0b0":{"name":"Call"},"e0b1":{"name":"Call End"},"e0b2":{"name":"Call Made"},"e0b3":{"name":"Call Merge"},"e0b4":{"name":"Call Missed"},"e0e4":{"name":"Call Missed Outgoing"},"e0b5":{"name":"Call Received"},"e0b6":{"name":"Call Split"},"e06c":{"name":"Call To Action"},"e3af":{"name":"Camera"},"e3b0":{"name":"Camera Alt"},"e8fc":{"name":"Camera Enhance"},"e3b1":{"name":"Camera Front"},"e3b2":{"name":"Camera Rear"},"e3b3":{"name":"Camera Roll"},"e5c9":{"name":"Cancel"},"e8f6":{"name":"Card Giftcard"},"e8f7":{"name":"Card Membership"},"e8f8":{"name":"Card Travel"},"eb40":{"name":"Casino"},"e307":{"name":"Cast"},"e308":{"name":"Cast Connected"},"e3b4":{"name":"Center Focus Strong"},"e3b5":{"name":"Center Focus Weak"},"e86b":{"name":"Change History"},"e0b7":{"name":"Chat"},"e0ca":{"name":"Chat Bubble"},"e0cb":{"name":"Chat Bubble Outline"},"e5ca":{"name":"Check"},"e834":{"name":"Check Box"},"e835":{"name":"Check Box Outline Blank"},"e86c":{"name":"Check Circle"},"e5cb":{"name":"Chevron Left"},"e5cc":{"name":"Chevron Right"},"eb41":{"name":"Child Care"},"eb42":{"name":"Child Friendly"},"e86d":{"name":"Chrome Reader Mode"},"e86e":{"name":"Class"},"e14c":{"name":"Clear"},"e0b8":{"name":"Clear All"},"e5cd":{"name":"Close"},"e01c":{"name":"Closed Caption"},"e2bd":{"name":"Cloud"},"e2be":{"name":"Cloud Circle"},"e2bf":{"name":"Cloud Done"},"e2c0":{"name":"Cloud Download"},"e2c1":{"name":"Cloud Off"},"e2c2":{"name":"Cloud Queue"},"e2c3":{"name":"Cloud Upload"},"e86f":{"name":"Code"},"e3b6":{"name":"Collections"},"e431":{"name":"Collections Bookmark"},"e3b7":{"name":"Color Lens"},"e3b8":{"name":"Colorize"},"e0b9":{"name":"Comment"},"e3b9":{"name":"Compare"},"e915":{"name":"Compare Arrows"},"e30a":{"name":"Computer"},"e638":{"name":"Confirmation Number"},"e0d0":{"name":"Contact Mail"},"e0cf":{"name":"Contact Phone"},"e0ba":{"name":"Contacts"},"e14d":{"name":"Content Copy"},"e14e":{"name":"Content Cut"},"e14f":{"name":"Content Paste"},"e3ba":{"name":"Control Point"},"e3bb":{"name":"Control Point Duplicate"},"e90c":{"name":"Copyright"},"e150":{"name":"Create"},"e2cc":{"name":"Create New Folder"},"e870":{"name":"Credit Card"},"e3be":{"name":"Crop"},"e3bc":{"name":"Crop 16 9"},"e3bd":{"name":"Crop 3 2"},"e3bf":{"name":"Crop 5 4"},"e3c0":{"name":"Crop 7 5"},"e3c1":{"name":"Crop Din"},"e3c2":{"name":"Crop Free"},"e3c3":{"name":"Crop Landscape"},"e3c4":{"name":"Crop Original"},"e3c5":{"name":"Crop Portrait"},"e437":{"name":"Crop Rotate"},"e3c6":{"name":"Crop Square"},"e871":{"name":"Dashboard"},"e1af":{"name":"Data Usage"},"e916":{"name":"Date Range"},"e3c7":{"name":"Dehaze"},"e872":{"name":"Delete"},"e92b":{"name":"Delete Forever"},"e16c":{"name":"Delete Sweep"},"e873":{"name":"Description"},"e30b":{"name":"Desktop Mac"},"e30c":{"name":"Desktop Windows"},"e3c8":{"name":"Details"},"e30d":{"name":"Developer Board"},"e1b0":{"name":"Developer Mode"},"e335":{"name":"Device Hub"},"e1b1":{"name":"Devices"},"e337":{"name":"Devices Other"},"e0bb":{"name":"Dialer Sip"},"e0bc":{"name":"Dialpad"},"e52e":{"name":"Directions"},"e52f":{"name":"Directions Bike"},"e532":{"name":"Directions Boat"},"e530":{"name":"Directions Bus"},"e531":{"name":"Directions Car"},"e534":{"name":"Directions Railway"},"e566":{"name":"Directions Run"},"e533":{"name":"Directions Subway"},"e535":{"name":"Directions Transit"},"e536":{"name":"Directions Walk"},"e610":{"name":"Disc Full"},"e875":{"name":"Dns"},"e612":{"name":"Do Not Disturb"},"e611":{"name":"Do Not Disturb Alt"},"e643":{"name":"Do Not Disturb Off"},"e644":{"name":"Do Not Disturb On"},"e30e":{"name":"Dock"},"e7ee":{"name":"Domain"},"e876":{"name":"Done"},"e877":{"name":"Done All"},"e917":{"name":"Donut Large"},"e918":{"name":"Donut Small"},"e151":{"name":"Drafts"},"e25d":{"name":"Drag Handle"},"e613":{"name":"Drive Eta"},"e1b2":{"name":"Dvr"},"e3c9":{"name":"Edit"},"e568":{"name":"Edit Location"},"e8fb":{"name":"Eject"},"e0be":{"name":"Email"},"e63f":{"name":"Enhanced Encryption"},"e01d":{"name":"Equalizer"},"e000":{"name":"Error"},"e001":{"name":"Error Outline"},"e926":{"name":"Euro Symbol"},"e56d":{"name":"Ev Station"},"e878":{"name":"Event"},"e614":{"name":"Event Available"},"e615":{"name":"Event Busy"},"e616":{"name":"Event Note"},"e903":{"name":"Event Seat"},"e879":{"name":"Exit To App"},"e5ce":{"name":"Expand Less"},"e5cf":{"name":"Expand More"},"e01e":{"name":"Explicit"},"e87a":{"name":"Explore"},"e3ca":{"name":"Exposure"},"e3cb":{"name":"Exposure Neg 1"},"e3cc":{"name":"Exposure Neg 2"},"e3cd":{"name":"Exposure Plus 1"},"e3ce":{"name":"Exposure Plus 2"},"e3cf":{"name":"Exposure Zero"},"e87b":{"name":"Extension"},"e87c":{"name":"Face"},"e01f":{"name":"Fast Forward"},"e020":{"name":"Fast Rewind"},"e87d":{"name":"Favorite"},"e87e":{"name":"Favorite Border"},"e06d":{"name":"Featured Play List"},"e06e":{"name":"Featured Video"},"e87f":{"name":"Feedback"},"e05d":{"name":"Fiber Dvr"},"e061":{"name":"Fiber Manual Record"},"e05e":{"name":"Fiber New"},"e06a":{"name":"Fiber Pin"},"e062":{"name":"Fiber Smart Record"},"e2c4":{"name":"File Download"},"e2c6":{"name":"File Upload"},"e3d3":{"name":"Filter"},"e3d0":{"name":"Filter 1"},"e3d1":{"name":"Filter 2"},"e3d2":{"name":"Filter 3"},"e3d4":{"name":"Filter 4"},"e3d5":{"name":"Filter 5"},"e3d6":{"name":"Filter 6"},"e3d7":{"name":"Filter 7"},"e3d8":{"name":"Filter 8"},"e3d9":{"name":"Filter 9"},"e3da":{"name":"Filter 9 Plus"},"e3db":{"name":"Filter B And W"},"e3dc":{"name":"Filter Center Focus"},"e3dd":{"name":"Filter Drama"},"e3de":{"name":"Filter Frames"},"e3df":{"name":"Filter Hdr"},"e152":{"name":"Filter List"},"e3e0":{"name":"Filter None"},"e3e2":{"name":"Filter Tilt Shift"},"e3e3":{"name":"Filter Vintage"},"e880":{"name":"Find In Page"},"e881":{"name":"Find Replace"},"e90d":{"name":"Fingerprint"},"e5dc":{"name":"First Page"},"eb43":{"name":"Fitness Center"},"e153":{"name":"Flag"},"e3e4":{"name":"Flare"},"e3e5":{"name":"Flash Auto"},"e3e6":{"name":"Flash Off"},"e3e7":{"name":"Flash On"},"e539":{"name":"Flight"},"e904":{"name":"Flight Land"},"e905":{"name":"Flight Takeoff"},"e3e8":{"name":"Flip"},"e882":{"name":"Flip To Back"},"e883":{"name":"Flip To Front"},"e2c7":{"name":"Folder"},"e2c8":{"name":"Folder Open"},"e2c9":{"name":"Folder Shared"},"e617":{"name":"Folder Special"},"e167":{"name":"Font Download"},"e234":{"name":"Format Align Center"},"e235":{"name":"Format Align Justify"},"e236":{"name":"Format Align Left"},"e237":{"name":"Format Align Right"},"e238":{"name":"Format Bold"},"e239":{"name":"Format Clear"},"e23a":{"name":"Format Color Fill"},"e23b":{"name":"Format Color Reset"},"e23c":{"name":"Format Color Text"},"e23d":{"name":"Format Indent Decrease"},"e23e":{"name":"Format Indent Increase"},"e23f":{"name":"Format Italic"},"e240":{"name":"Format Line Spacing"},"e241":{"name":"Format List Bulleted"},"e242":{"name":"Format List Numbered"},"e243":{"name":"Format Paint"},"e244":{"name":"Format Quote"},"e25e":{"name":"Format Shapes"},"e245":{"name":"Format Size"},"e246":{"name":"Format Strikethrough"},"e247":{"name":"Format Textdirection L To R"},"e248":{"name":"Format Textdirection R To L"},"e249":{"name":"Format Underlined"},"e0bf":{"name":"Forum"},"e154":{"name":"Forward"},"e056":{"name":"Forward 10"},"e057":{"name":"Forward 30"},"e058":{"name":"Forward 5"},"eb44":{"name":"Free Breakfast"},"e5d0":{"name":"Fullscreen"},"e5d1":{"name":"Fullscreen Exit"},"e24a":{"name":"Functions"},"e927":{"name":"G Translate"},"e30f":{"name":"Gamepad"},"e021":{"name":"Games"},"e90e":{"name":"Gavel"},"e155":{"name":"Gesture"},"e884":{"name":"Get App"},"e908":{"name":"Gif"},"eb45":{"name":"Golf Course"},"e1b3":{"name":"Gps Fixed"},"e1b4":{"name":"Gps Not Fixed"},"e1b5":{"name":"Gps Off"},"e885":{"name":"Grade"},"e3e9":{"name":"Gradient"},"e3ea":{"name":"Grain"},"e1b8":{"name":"Graphic Eq"},"e3eb":{"name":"Grid Off"},"e3ec":{"name":"Grid On"},"e7ef":{"name":"Group"},"e7f0":{"name":"Group Add"},"e886":{"name":"Group Work"},"e052":{"name":"Hd"},"e3ed":{"name":"Hdr Off"},"e3ee":{"name":"Hdr On"},"e3f1":{"name":"Hdr Strong"},"e3f2":{"name":"Hdr Weak"},"e310":{"name":"Headset"},"e311":{"name":"Headset Mic"},"e3f3":{"name":"Healing"},"e023":{"name":"Hearing"},"e887":{"name":"Help"},"e8fd":{"name":"Help Outline"},"e024":{"name":"High Quality"},"e25f":{"name":"Highlight"},"e888":{"name":"Highlight Off"},"e889":{"name":"History"},"e88a":{"name":"Home"},"eb46":{"name":"Hot Tub"},"e53a":{"name":"Hotel"},"e88b":{"name":"Hourglass Empty"},"e88c":{"name":"Hourglass Full"},"e902":{"name":"Http"},"e88d":{"name":"Https"},"e3f4":{"name":"Image"},"e3f5":{"name":"Image Aspect Ratio"},"e0e0":{"name":"Import Contacts"},"e0c3":{"name":"Import Export"},"e912":{"name":"Important Devices"},"e156":{"name":"Inbox"},"e909":{"name":"Indeterminate Check Box"},"e88e":{"name":"Info"},"e88f":{"name":"Info Outline"},"e890":{"name":"Input"},"e24b":{"name":"Insert Chart"},"e24c":{"name":"Insert Comment"},"e24d":{"name":"Insert Drive File"},"e24e":{"name":"Insert Emoticon"},"e24f":{"name":"Insert Invitation"},"e250":{"name":"Insert Link"},"e251":{"name":"Insert Photo"},"e891":{"name":"Invert Colors"},"e0c4":{"name":"Invert Colors Off"},"e3f6":{"name":"Iso"},"e312":{"name":"Keyboard"},"e313":{"name":"Keyboard Arrow Down"},"e314":{"name":"Keyboard Arrow Left"},"e315":{"name":"Keyboard Arrow Right"},"e316":{"name":"Keyboard Arrow Up"},"e317":{"name":"Keyboard Backspace"},"e318":{"name":"Keyboard Capslock"},"e31a":{"name":"Keyboard Hide"},"e31b":{"name":"Keyboard Return"},"e31c":{"name":"Keyboard Tab"},"e31d":{"name":"Keyboard Voice"},"eb47":{"name":"Kitchen"},"e892":{"name":"Label"},"e893":{"name":"Label Outline"},"e3f7":{"name":"Landscape"},"e894":{"name":"Language"},"e31e":{"name":"Laptop"},"e31f":{"name":"Laptop Chromebook"},"e320":{"name":"Laptop Mac"},"e321":{"name":"Laptop Windows"},"e5dd":{"name":"Last Page"},"e895":{"name":"Launch"},"e53b":{"name":"Layers"},"e53c":{"name":"Layers Clear"},"e3f8":{"name":"Leak Add"},"e3f9":{"name":"Leak Remove"},"e3fa":{"name":"Lens"},"e02e":{"name":"Library Add"},"e02f":{"name":"Library Books"},"e030":{"name":"Library Music"},"e90f":{"name":"Lightbulb Outline"},"e919":{"name":"Line Style"},"e91a":{"name":"Line Weight"},"e260":{"name":"Linear Scale"},"e157":{"name":"Link"},"e438":{"name":"Linked Camera"},"e896":{"name":"List"},"e0c6":{"name":"Live Help"},"e639":{"name":"Live Tv"},"e53f":{"name":"Local Activity"},"e53d":{"name":"Local Airport"},"e53e":{"name":"Local Atm"},"e540":{"name":"Local Bar"},"e541":{"name":"Local Cafe"},"e542":{"name":"Local Car Wash"},"e543":{"name":"Local Convenience Store"},"e556":{"name":"Local Dining"},"e544":{"name":"Local Drink"},"e545":{"name":"Local Florist"},"e546":{"name":"Local Gas Station"},"e547":{"name":"Local Grocery Store"},"e548":{"name":"Local Hospital"},"e549":{"name":"Local Hotel"},"e54a":{"name":"Local Laundry Service"},"e54b":{"name":"Local Library"},"e54c":{"name":"Local Mall"},"e54d":{"name":"Local Movies"},"e54e":{"name":"Local Offer"},"e54f":{"name":"Local Parking"},"e550":{"name":"Local Pharmacy"},"e551":{"name":"Local Phone"},"e552":{"name":"Local Pizza"},"e553":{"name":"Local Play"},"e554":{"name":"Local Post Office"},"e555":{"name":"Local Printshop"},"e557":{"name":"Local See"},"e558":{"name":"Local Shipping"},"e559":{"name":"Local Taxi"},"e7f1":{"name":"Location City"},"e1b6":{"name":"Location Disabled"},"e0c7":{"name":"Location Off"},"e0c8":{"name":"Location On"},"e1b7":{"name":"Location Searching"},"e897":{"name":"Lock"},"e898":{"name":"Lock Open"},"e899":{"name":"Lock Outline"},"e3fc":{"name":"Looks"},"e3fb":{"name":"Looks 3"},"e3fd":{"name":"Looks 4"},"e3fe":{"name":"Looks 5"},"e3ff":{"name":"Looks 6"},"e400":{"name":"Looks One"},"e401":{"name":"Looks Two"},"e028":{"name":"Loop"},"e402":{"name":"Loupe"},"e16d":{"name":"Low Priority"},"e89a":{"name":"Loyalty"},"e158":{"name":"Mail"},"e0e1":{"name":"Mail Outline"},"e55b":{"name":"Map"},"e159":{"name":"Markunread"},"e89b":{"name":"Markunread Mailbox"},"e322":{"name":"Memory"},"e5d2":{"name":"Menu"},"e252":{"name":"Merge Type"},"e0c9":{"name":"Message"},"e029":{"name":"Mic"},"e02a":{"name":"Mic None"},"e02b":{"name":"Mic Off"},"e618":{"name":"Mms"},"e253":{"name":"Mode Comment"},"e254":{"name":"Mode Edit"},"e263":{"name":"Monetization On"},"e25c":{"name":"Money Off"},"e403":{"name":"Monochrome Photos"},"e7f2":{"name":"Mood"},"e7f3":{"name":"Mood Bad"},"e619":{"name":"More"},"e5d3":{"name":"More Horiz"},"e5d4":{"name":"More Vert"},"e91b":{"name":"Motorcycle"},"e323":{"name":"Mouse"},"e168":{"name":"Move To Inbox"},"e02c":{"name":"Movie"},"e404":{"name":"Movie Creation"},"e43a":{"name":"Movie Filter"},"e6df":{"name":"Multiline Chart"},"e405":{"name":"Music Note"},"e063":{"name":"Music Video"},"e55c":{"name":"My Location"},"e406":{"name":"Nature"},"e407":{"name":"Nature People"},"e408":{"name":"Navigate Before"},"e409":{"name":"Navigate Next"},"e55d":{"name":"Navigation"},"e569":{"name":"Near Me"},"e1b9":{"name":"Network Cell"},"e640":{"name":"Network Check"},"e61a":{"name":"Network Locked"},"e1ba":{"name":"Network Wifi"},"e031":{"name":"New Releases"},"e16a":{"name":"Next Week"},"e1bb":{"name":"Nfc"},"e641":{"name":"No Encryption"},"e0cc":{"name":"No Sim"},"e033":{"name":"Not Interested"},"e06f":{"name":"Note"},"e89c":{"name":"Note Add"},"e7f4":{"name":"Notifications"},"e7f7":{"name":"Notifications Active"},"e7f5":{"name":"Notifications None"},"e7f6":{"name":"Notifications Off"},"e7f8":{"name":"Notifications Paused"},"e90a":{"name":"Offline Pin"},"e63a":{"name":"Ondemand Video"},"e91c":{"name":"Opacity"},"e89d":{"name":"Open In Browser"},"e89e":{"name":"Open In New"},"e89f":{"name":"Open With"},"e7f9":{"name":"Pages"},"e8a0":{"name":"Pageview"},"e40a":{"name":"Palette"},"e925":{"name":"Pan Tool"},"e40b":{"name":"Panorama"},"e40c":{"name":"Panorama Fish Eye"},"e40d":{"name":"Panorama Horizontal"},"e40e":{"name":"Panorama Vertical"},"e40f":{"name":"Panorama Wide Angle"},"e7fa":{"name":"Party Mode"},"e034":{"name":"Pause"},"e035":{"name":"Pause Circle Filled"},"e036":{"name":"Pause Circle Outline"},"e8a1":{"name":"Payment"},"e7fb":{"name":"People"},"e7fc":{"name":"People Outline"},"e8a2":{"name":"Perm Camera Mic"},"e8a3":{"name":"Perm Contact Calendar"},"e8a4":{"name":"Perm Data Setting"},"e8a5":{"name":"Perm Device Information"},"e8a6":{"name":"Perm Identity"},"e8a7":{"name":"Perm Media"},"e8a8":{"name":"Perm Phone Msg"},"e8a9":{"name":"Perm Scan Wifi"},"e7fd":{"name":"Person"},"e7fe":{"name":"Person Add"},"e7ff":{"name":"Person Outline"},"e55a":{"name":"Person Pin"},"e56a":{"name":"Person Pin Circle"},"e63b":{"name":"Personal Video"},"e91d":{"name":"Pets"},"e0cd":{"name":"Phone"},"e324":{"name":"Phone Android"},"e61b":{"name":"Phone Bluetooth Speaker"},"e61c":{"name":"Phone Forwarded"},"e61d":{"name":"Phone In Talk"},"e325":{"name":"Phone Iphone"},"e61e":{"name":"Phone Locked"},"e61f":{"name":"Phone Missed"},"e620":{"name":"Phone Paused"},"e326":{"name":"Phonelink"},"e0db":{"name":"Phonelink Erase"},"e0dc":{"name":"Phonelink Lock"},"e327":{"name":"Phonelink Off"},"e0dd":{"name":"Phonelink Ring"},"e0de":{"name":"Phonelink Setup"},"e410":{"name":"Photo"},"e411":{"name":"Photo Album"},"e412":{"name":"Photo Camera"},"e43b":{"name":"Photo Filter"},"e413":{"name":"Photo Library"},"e432":{"name":"Photo Size Select Actual"},"e433":{"name":"Photo Size Select Large"},"e434":{"name":"Photo Size Select Small"},"e415":{"name":"Picture As Pdf"},"e8aa":{"name":"Picture In Picture"},"e911":{"name":"Picture In Picture Alt"},"e6c4":{"name":"Pie Chart"},"e6c5":{"name":"Pie Chart Outlined"},"e55e":{"name":"Pin Drop"},"e55f":{"name":"Place"},"e037":{"name":"Play Arrow"},"e038":{"name":"Play Circle Filled"},"e039":{"name":"Play Circle Outline"},"e906":{"name":"Play For Work"},"e03b":{"name":"Playlist Add"},"e065":{"name":"Playlist Add Check"},"e05f":{"name":"Playlist Play"},"e800":{"name":"Plus One"},"e801":{"name":"Poll"},"e8ab":{"name":"Polymer"},"eb48":{"name":"Pool"},"e0ce":{"name":"Portable Wifi Off"},"e416":{"name":"Portrait"},"e63c":{"name":"Power"},"e336":{"name":"Power Input"},"e8ac":{"name":"Power Settings New"},"e91e":{"name":"Pregnant Woman"},"e0df":{"name":"Present To All"},"e8ad":{"name":"Print"},"e645":{"name":"Priority High"},"e80b":{"name":"Public"},"e255":{"name":"Publish"},"e8ae":{"name":"Query Builder"},"e8af":{"name":"Question Answer"},"e03c":{"name":"Queue"},"e03d":{"name":"Queue Music"},"e066":{"name":"Queue Play Next"},"e03e":{"name":"Radio"},"e837":{"name":"Radio Button Checked"},"e836":{"name":"Radio Button Unchecked"},"e560":{"name":"Rate Review"},"e8b0":{"name":"Receipt"},"e03f":{"name":"Recent Actors"},"e91f":{"name":"Record Voice Over"},"e8b1":{"name":"Redeem"},"e15a":{"name":"Redo"},"e5d5":{"name":"Refresh"},"e15b":{"name":"Remove"},"e15c":{"name":"Remove Circle"},"e15d":{"name":"Remove Circle Outline"},"e067":{"name":"Remove From Queue"},"e417":{"name":"Remove Red Eye"},"e928":{"name":"Remove Shopping Cart"},"e8fe":{"name":"Reorder"},"e040":{"name":"Repeat"},"e041":{"name":"Repeat One"},"e042":{"name":"Replay"},"e059":{"name":"Replay 10"},"e05a":{"name":"Replay 30"},"e05b":{"name":"Replay 5"},"e15e":{"name":"Reply"},"e15f":{"name":"Reply All"},"e160":{"name":"Report"},"e8b2":{"name":"Report Problem"},"e56c":{"name":"Restaurant"},"e561":{"name":"Restaurant Menu"},"e8b3":{"name":"Restore"},"e929":{"name":"Restore Page"},"e0d1":{"name":"Ring Volume"},"e8b4":{"name":"Room"},"eb49":{"name":"Room Service"},"e418":{"name":"Rotate 90 Degrees Ccw"},"e419":{"name":"Rotate Left"},"e41a":{"name":"Rotate Right"},"e920":{"name":"Rounded Corner"},"e328":{"name":"Router"},"e921":{"name":"Rowing"},"e0e5":{"name":"Rss Feed"},"e642":{"name":"Rv Hookup"},"e562":{"name":"Satellite"},"e161":{"name":"Save"},"e329":{"name":"Scanner"},"e8b5":{"name":"Schedule"},"e80c":{"name":"School"},"e1be":{"name":"Screen Lock Landscape"},"e1bf":{"name":"Screen Lock Portrait"},"e1c0":{"name":"Screen Lock Rotation"},"e1c1":{"name":"Screen Rotation"},"e0e2":{"name":"Screen Share"},"e623":{"name":"Sd Card"},"e1c2":{"name":"Sd Storage"},"e8b6":{"name":"Search"},"e32a":{"name":"Security"},"e162":{"name":"Select All"},"e163":{"name":"Send"},"e811":{"name":"Sentiment Dissatisfied"},"e812":{"name":"Sentiment Neutral"},"e813":{"name":"Sentiment Satisfied"},"e814":{"name":"Sentiment Very Dissatisfied"},"e815":{"name":"Sentiment Very Satisfied"},"e8b8":{"name":"Settings"},"e8b9":{"name":"Settings Applications"},"e8ba":{"name":"Settings Backup Restore"},"e8bb":{"name":"Settings Bluetooth"},"e8bd":{"name":"Settings Brightness"},"e8bc":{"name":"Settings Cell"},"e8be":{"name":"Settings Ethernet"},"e8bf":{"name":"Settings Input Antenna"},"e8c0":{"name":"Settings Input Component"},"e8c1":{"name":"Settings Input Composite"},"e8c2":{"name":"Settings Input Hdmi"},"e8c3":{"name":"Settings Input Svideo"},"e8c4":{"name":"Settings Overscan"},"e8c5":{"name":"Settings Phone"},"e8c6":{"name":"Settings Power"},"e8c7":{"name":"Settings Remote"},"e1c3":{"name":"Settings System Daydream"},"e8c8":{"name":"Settings Voice"},"e80d":{"name":"Share"},"e8c9":{"name":"Shop"},"e8ca":{"name":"Shop Two"},"e8cb":{"name":"Shopping Basket"},"e8cc":{"name":"Shopping Cart"},"e261":{"name":"Short Text"},"e6e1":{"name":"Show Chart"},"e043":{"name":"Shuffle"},"e1c8":{"name":"Signal Cellular 4 Bar"},"e1cd":{"name":"Signal Cellular Connected No Internet 4 Bar"},"e1ce":{"name":"Signal Cellular No Sim"},"e1cf":{"name":"Signal Cellular Null"},"e1d0":{"name":"Signal Cellular Off"},"e1d8":{"name":"Signal Wifi 4 Bar"},"e1d9":{"name":"Signal Wifi 4 Bar Lock"},"e1da":{"name":"Signal Wifi Off"},"e32b":{"name":"Sim Card"},"e624":{"name":"Sim Card Alert"},"e044":{"name":"Skip Next"},"e045":{"name":"Skip Previous"},"e41b":{"name":"Slideshow"},"e068":{"name":"Slow Motion Video"},"e32c":{"name":"Smartphone"},"eb4a":{"name":"Smoke Free"},"eb4b":{"name":"Smoking Rooms"},"e625":{"name":"Sms"},"e626":{"name":"Sms Failed"},"e046":{"name":"Snooze"},"e164":{"name":"Sort"},"e053":{"name":"Sort By Alpha"},"eb4c":{"name":"Spa"},"e256":{"name":"Space Bar"},"e32d":{"name":"Speaker"},"e32e":{"name":"Speaker Group"},"e8cd":{"name":"Speaker Notes"},"e92a":{"name":"Speaker Notes Off"},"e0d2":{"name":"Speaker Phone"},"e8ce":{"name":"Spellcheck"},"e838":{"name":"Star"},"e83a":{"name":"Star Border"},"e839":{"name":"Star Half"},"e8d0":{"name":"Stars"},"e0d3":{"name":"Stay Current Landscape"},"e0d4":{"name":"Stay Current Portrait"},"e0d5":{"name":"Stay Primary Landscape"},"e0d6":{"name":"Stay Primary Portrait"},"e047":{"name":"Stop"},"e0e3":{"name":"Stop Screen Share"},"e1db":{"name":"Storage"},"e8d1":{"name":"Store"},"e563":{"name":"Store Mall Directory"},"e41c":{"name":"Straighten"},"e56e":{"name":"Streetview"},"e257":{"name":"Strikethrough S"},"e41d":{"name":"Style"},"e5d9":{"name":"Subdirectory Arrow Left"},"e5da":{"name":"Subdirectory Arrow Right"},"e8d2":{"name":"Subject"},"e064":{"name":"Subscriptions"},"e048":{"name":"Subtitles"},"e56f":{"name":"Subway"},"e8d3":{"name":"Supervisor Account"},"e049":{"name":"Surround Sound"},"e0d7":{"name":"Swap Calls"},"e8d4":{"name":"Swap Horiz"},"e8d5":{"name":"Swap Vert"},"e8d6":{"name":"Swap Vertical Circle"},"e41e":{"name":"Switch Camera"},"e41f":{"name":"Switch Video"},"e627":{"name":"Sync"},"e628":{"name":"Sync Disabled"},"e629":{"name":"Sync Problem"},"e62a":{"name":"System Update"},"e8d7":{"name":"System Update Alt"},"e8d8":{"name":"Tab"},"e8d9":{"name":"Tab Unselected"},"e32f":{"name":"Tablet"},"e330":{"name":"Tablet Android"},"e331":{"name":"Tablet Mac"},"e420":{"name":"Tag Faces"},"e62b":{"name":"Tap And Play"},"e564":{"name":"Terrain"},"e262":{"name":"Text Fields"},"e165":{"name":"Text Format"},"e0d8":{"name":"Textsms"},"e421":{"name":"Texture"},"e8da":{"name":"Theaters"},"e8db":{"name":"Thumb Down"},"e8dc":{"name":"Thumb Up"},"e8dd":{"name":"Thumbs Up Down"},"e62c":{"name":"Time To Leave"},"e422":{"name":"Timelapse"},"e922":{"name":"Timeline"},"e425":{"name":"Timer"},"e423":{"name":"Timer 10"},"e424":{"name":"Timer 3"},"e426":{"name":"Timer Off"},"e264":{"name":"Title"},"e8de":{"name":"Toc"},"e8df":{"name":"Today"},"e8e0":{"name":"Toll"},"e427":{"name":"Tonality"},"e913":{"name":"Touch App"},"e332":{"name":"Toys"},"e8e1":{"name":"Track Changes"},"e565":{"name":"Traffic"},"e570":{"name":"Train"},"e571":{"name":"Tram"},"e572":{"name":"Transfer Within A Station"},"e428":{"name":"Transform"},"e8e2":{"name":"Translate"},"e8e3":{"name":"Trending Down"},"e8e4":{"name":"Trending Flat"},"e8e5":{"name":"Trending Up"},"e429":{"name":"Tune"},"e8e6":{"name":"Turned In"},"e8e7":{"name":"Turned In Not"},"e333":{"name":"Tv"},"e169":{"name":"Unarchive"},"e166":{"name":"Undo"},"e5d6":{"name":"Unfold Less"},"e5d7":{"name":"Unfold More"},"e923":{"name":"Update"},"e1e0":{"name":"Usb"},"e8e8":{"name":"Verified User"},"e258":{"name":"Vertical Align Bottom"},"e259":{"name":"Vertical Align Center"},"e25a":{"name":"Vertical Align Top"},"e62d":{"name":"Vibration"},"e070":{"name":"Video Call"},"e071":{"name":"Video Label"},"e04a":{"name":"Video Library"},"e04b":{"name":"Videocam"},"e04c":{"name":"Videocam Off"},"e338":{"name":"Videogame Asset"},"e8e9":{"name":"View Agenda"},"e8ea":{"name":"View Array"},"e8eb":{"name":"View Carousel"},"e8ec":{"name":"View Column"},"e42a":{"name":"View Comfy"},"e42b":{"name":"View Compact"},"e8ed":{"name":"View Day"},"e8ee":{"name":"View Headline"},"e8ef":{"name":"View List"},"e8f0":{"name":"View Module"},"e8f1":{"name":"View Quilt"},"e8f2":{"name":"View Stream"},"e8f3":{"name":"View Week"},"e435":{"name":"Vignette"},"e8f4":{"name":"Visibility"},"e8f5":{"name":"Visibility Off"},"e62e":{"name":"Voice Chat"},"e0d9":{"name":"Voicemail"},"e04d":{"name":"Volume Down"},"e04e":{"name":"Volume Mute"},"e04f":{"name":"Volume Off"},"e050":{"name":"Volume Up"},"e0da":{"name":"Vpn Key"},"e62f":{"name":"Vpn Lock"},"e1bc":{"name":"Wallpaper"},"e002":{"name":"Warning"},"e334":{"name":"Watch"},"e924":{"name":"Watch Later"},"e42c":{"name":"Wb Auto"},"e42d":{"name":"Wb Cloudy"},"e42e":{"name":"Wb Incandescent"},"e436":{"name":"Wb Iridescent"},"e430":{"name":"Wb Sunny"},"e63d":{"name":"Wc"},"e051":{"name":"Web"},"e069":{"name":"Web Asset"},"e16b":{"name":"Weekend"},"e80e":{"name":"Whatshot"},"e1bd":{"name":"Widgets"},"e63e":{"name":"Wifi"},"e1e1":{"name":"Wifi Lock"},"e1e2":{"name":"Wifi Tethering"},"e8f9":{"name":"Work"},"e25b":{"name":"Wrap Text"},"e8fa":{"name":"Youtube Searched For"},"e8ff":{"name":"Zoom In"},"e900":{"name":"Zoom Out"},"e56b":{"name":"Zoom Out Map"}}}
\ No newline at end of file
diff --git a/webroot/css/fonts/material-icons/MaterialIcons-Regular.svg b/webroot/css/fonts/material-icons/MaterialIcons-Regular.svg
new file mode 100644
index 0000000..a449327
--- /dev/null
+++ b/webroot/css/fonts/material-icons/MaterialIcons-Regular.svg
@@ -0,0 +1,2373 @@
+
+
+
+
+
+Created by FontForge 20151118 at Mon Feb 8 11:58:02 2016
+ By shyndman
+Copyright 2015 Google, Inc. All Rights Reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/webroot/css/fonts/material-icons/MaterialIcons-Regular.ttf b/webroot/css/fonts/material-icons/MaterialIcons-Regular.ttf
new file mode 100644
index 0000000..7015564
Binary files /dev/null and b/webroot/css/fonts/material-icons/MaterialIcons-Regular.ttf differ
diff --git a/webroot/css/fonts/material-icons/MaterialIcons-Regular.woff b/webroot/css/fonts/material-icons/MaterialIcons-Regular.woff
new file mode 100644
index 0000000..b648a3e
Binary files /dev/null and b/webroot/css/fonts/material-icons/MaterialIcons-Regular.woff differ
diff --git a/webroot/css/fonts/material-icons/MaterialIcons-Regular.woff2 b/webroot/css/fonts/material-icons/MaterialIcons-Regular.woff2
new file mode 100644
index 0000000..dcffe91
Binary files /dev/null and b/webroot/css/fonts/material-icons/MaterialIcons-Regular.woff2 differ
diff --git a/webroot/css/fonts/material-icons/material-icons.css b/webroot/css/fonts/material-icons/material-icons.css
new file mode 100644
index 0000000..2270c09
--- /dev/null
+++ b/webroot/css/fonts/material-icons/material-icons.css
@@ -0,0 +1,36 @@
+@font-face {
+ font-family: 'Material Icons';
+ font-style: normal;
+ font-weight: 400;
+ src: url(MaterialIcons-Regular.eot); /* For IE6-8 */
+ src: local('Material Icons'),
+ local('MaterialIcons-Regular'),
+ url(MaterialIcons-Regular.woff2) format('woff2'),
+ url(MaterialIcons-Regular.woff) format('woff'),
+ url(MaterialIcons-Regular.ttf) format('truetype');
+}
+
+.material-icons {
+ font-family: 'Material Icons';
+ font-weight: normal;
+ font-style: normal;
+ font-size: 24px; /* Preferred icon size */
+ display: inline-block;
+ line-height: 1;
+ text-transform: none;
+ letter-spacing: normal;
+ word-wrap: normal;
+ white-space: nowrap;
+ direction: ltr;
+
+ /* Support for all WebKit browsers. */
+ -webkit-font-smoothing: antialiased;
+ /* Support for Safari and Chrome. */
+ text-rendering: optimizeLegibility;
+
+ /* Support for Firefox. */
+ -moz-osx-font-smoothing: grayscale;
+
+ /* Support for IE. */
+ font-feature-settings: 'liga';
+}
diff --git a/webroot/css/fonts/notosans_bold/NotoSans-Bold-webfont.eot b/webroot/css/fonts/notosans_bold/NotoSans-Bold-webfont.eot
new file mode 100644
index 0000000..9bf761c
Binary files /dev/null and b/webroot/css/fonts/notosans_bold/NotoSans-Bold-webfont.eot differ
diff --git a/webroot/css/fonts/notosans_bold/NotoSans-Bold-webfont.svg b/webroot/css/fonts/notosans_bold/NotoSans-Bold-webfont.svg
new file mode 100644
index 0000000..ed809fc
--- /dev/null
+++ b/webroot/css/fonts/notosans_bold/NotoSans-Bold-webfont.svg
@@ -0,0 +1,21104 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/webroot/css/fonts/notosans_bold/NotoSans-Bold-webfont.ttf b/webroot/css/fonts/notosans_bold/NotoSans-Bold-webfont.ttf
new file mode 100644
index 0000000..dc4bddc
Binary files /dev/null and b/webroot/css/fonts/notosans_bold/NotoSans-Bold-webfont.ttf differ
diff --git a/webroot/css/fonts/notosans_bold/NotoSans-Bold-webfont.woff b/webroot/css/fonts/notosans_bold/NotoSans-Bold-webfont.woff
new file mode 100644
index 0000000..35f1df7
Binary files /dev/null and b/webroot/css/fonts/notosans_bold/NotoSans-Bold-webfont.woff differ
diff --git a/webroot/css/fonts/notosans_bold/stylesheet.css b/webroot/css/fonts/notosans_bold/stylesheet.css
new file mode 100644
index 0000000..397e899
--- /dev/null
+++ b/webroot/css/fonts/notosans_bold/stylesheet.css
@@ -0,0 +1,12 @@
+@font-face {
+ font-family: 'Noto Sans Bold';
+ src: url('NotoSans-Bold-webfont.eot');
+ src: url('NotoSans-Bold-webfont.eot?#iefix') format('embedded-opentype'),
+ url('NotoSans-Bold-webfont.woff') format('woff'),
+ url('NotoSans-Bold-webfont.ttf') format('truetype'),
+ url('NotoSans-Bold-webfont.svg#noto_sansbold') format('svg');
+ font-weight: normal;
+ font-style: normal;
+
+}
+
diff --git a/webroot/css/fonts/notosans_bolditalic/NotoSans-BoldItalic-webfont.eot b/webroot/css/fonts/notosans_bolditalic/NotoSans-BoldItalic-webfont.eot
new file mode 100644
index 0000000..8ee1bef
Binary files /dev/null and b/webroot/css/fonts/notosans_bolditalic/NotoSans-BoldItalic-webfont.eot differ
diff --git a/webroot/css/fonts/notosans_bolditalic/NotoSans-BoldItalic-webfont.svg b/webroot/css/fonts/notosans_bolditalic/NotoSans-BoldItalic-webfont.svg
new file mode 100644
index 0000000..a8bb319
--- /dev/null
+++ b/webroot/css/fonts/notosans_bolditalic/NotoSans-BoldItalic-webfont.svg
@@ -0,0 +1,20979 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/webroot/css/fonts/notosans_bolditalic/NotoSans-BoldItalic-webfont.ttf b/webroot/css/fonts/notosans_bolditalic/NotoSans-BoldItalic-webfont.ttf
new file mode 100644
index 0000000..82f7457
Binary files /dev/null and b/webroot/css/fonts/notosans_bolditalic/NotoSans-BoldItalic-webfont.ttf differ
diff --git a/webroot/css/fonts/notosans_bolditalic/NotoSans-BoldItalic-webfont.woff b/webroot/css/fonts/notosans_bolditalic/NotoSans-BoldItalic-webfont.woff
new file mode 100644
index 0000000..b1901aa
Binary files /dev/null and b/webroot/css/fonts/notosans_bolditalic/NotoSans-BoldItalic-webfont.woff differ
diff --git a/webroot/css/fonts/notosans_bolditalic/stylesheet.css b/webroot/css/fonts/notosans_bolditalic/stylesheet.css
new file mode 100644
index 0000000..697b615
--- /dev/null
+++ b/webroot/css/fonts/notosans_bolditalic/stylesheet.css
@@ -0,0 +1,12 @@
+@font-face {
+ font-family: 'noto_sansbold_italic';
+ src: url('NotoSans-BoldItalic-webfont.eot');
+ src: url('NotoSans-BoldItalic-webfont.eot?#iefix') format('embedded-opentype'),
+ url('NotoSans-BoldItalic-webfont.woff') format('woff'),
+ url('NotoSans-BoldItalic-webfont.ttf') format('truetype'),
+ url('NotoSans-BoldItalic-webfont.svg#noto_sansbold_italic') format('svg');
+ font-weight: normal;
+ font-style: normal;
+
+}
+
diff --git a/webroot/css/fonts/notosans_italic/NotoSans-Italic-webfont.eot b/webroot/css/fonts/notosans_italic/NotoSans-Italic-webfont.eot
new file mode 100644
index 0000000..2a1acb8
Binary files /dev/null and b/webroot/css/fonts/notosans_italic/NotoSans-Italic-webfont.eot differ
diff --git a/webroot/css/fonts/notosans_italic/NotoSans-Italic-webfont.svg b/webroot/css/fonts/notosans_italic/NotoSans-Italic-webfont.svg
new file mode 100644
index 0000000..888bbd2
--- /dev/null
+++ b/webroot/css/fonts/notosans_italic/NotoSans-Italic-webfont.svg
@@ -0,0 +1,15272 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/webroot/css/fonts/notosans_italic/NotoSans-Italic-webfont.ttf b/webroot/css/fonts/notosans_italic/NotoSans-Italic-webfont.ttf
new file mode 100644
index 0000000..418ac29
Binary files /dev/null and b/webroot/css/fonts/notosans_italic/NotoSans-Italic-webfont.ttf differ
diff --git a/webroot/css/fonts/notosans_italic/NotoSans-Italic-webfont.woff b/webroot/css/fonts/notosans_italic/NotoSans-Italic-webfont.woff
new file mode 100644
index 0000000..24b6e3b
Binary files /dev/null and b/webroot/css/fonts/notosans_italic/NotoSans-Italic-webfont.woff differ
diff --git a/webroot/css/fonts/notosans_italic/stylesheet.css b/webroot/css/fonts/notosans_italic/stylesheet.css
new file mode 100644
index 0000000..b8781d8
--- /dev/null
+++ b/webroot/css/fonts/notosans_italic/stylesheet.css
@@ -0,0 +1,12 @@
+@font-face {
+ font-family: 'noto_sansitalic';
+ src: url('NotoSans-Italic-webfont.eot');
+ src: url('NotoSans-Italic-webfont.eot?#iefix') format('embedded-opentype'),
+ url('NotoSans-Italic-webfont.woff') format('woff'),
+ url('NotoSans-Italic-webfont.ttf') format('truetype'),
+ url('NotoSans-Italic-webfont.svg#noto_sansitalic') format('svg');
+ font-weight: normal;
+ font-style: normal;
+
+}
+
diff --git a/webroot/css/fonts/notosans_regular/NotoSans-Regular-webfont.eot b/webroot/css/fonts/notosans_regular/NotoSans-Regular-webfont.eot
new file mode 100644
index 0000000..7f2da14
Binary files /dev/null and b/webroot/css/fonts/notosans_regular/NotoSans-Regular-webfont.eot differ
diff --git a/webroot/css/fonts/notosans_regular/NotoSans-Regular-webfont.svg b/webroot/css/fonts/notosans_regular/NotoSans-Regular-webfont.svg
new file mode 100644
index 0000000..1e194aa
--- /dev/null
+++ b/webroot/css/fonts/notosans_regular/NotoSans-Regular-webfont.svg
@@ -0,0 +1,21104 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/webroot/css/fonts/notosans_regular/NotoSans-Regular-webfont.ttf b/webroot/css/fonts/notosans_regular/NotoSans-Regular-webfont.ttf
new file mode 100644
index 0000000..d8fe193
Binary files /dev/null and b/webroot/css/fonts/notosans_regular/NotoSans-Regular-webfont.ttf differ
diff --git a/webroot/css/fonts/notosans_regular/NotoSans-Regular-webfont.woff b/webroot/css/fonts/notosans_regular/NotoSans-Regular-webfont.woff
new file mode 100644
index 0000000..c2d3187
Binary files /dev/null and b/webroot/css/fonts/notosans_regular/NotoSans-Regular-webfont.woff differ
diff --git a/webroot/css/fonts/notosans_regular/stylesheet.css b/webroot/css/fonts/notosans_regular/stylesheet.css
new file mode 100644
index 0000000..e242f87
--- /dev/null
+++ b/webroot/css/fonts/notosans_regular/stylesheet.css
@@ -0,0 +1,11 @@
+@font-face {
+ font-family: 'Noto Sans';
+ src: url('NotoSans-Regular-webfont.eot');
+ src: url('NotoSans-Regular-webfont.eot?#iefix') format('embedded-opentype'),
+ url('NotoSans-Regular-webfont.woff') format('woff'),
+ url('NotoSans-Regular-webfont.ttf') format('truetype'),
+ url('NotoSans-Regular-webfont.svg#noto_sansregular') format('svg');
+ font-weight: normal;
+ font-style: normal;
+}
+
diff --git a/webroot/files/users.json b/webroot/files/users.json
new file mode 100644
index 0000000..25bea0e
--- /dev/null
+++ b/webroot/files/users.json
@@ -0,0 +1,602 @@
+[
+ {
+ "name": "Galena Munoz",
+ "email": "ipsum.cursus.vestibulum@urnaconvalliserat.net",
+ "cou": "Active, Member",
+ "org": "Eu Corporation"
+ },
+ {
+ "name": "Doris Santos",
+ "email": "vitae@non.org",
+ "cou": "Active, Filesource, Member",
+ "org": "Aliquam LLC"
+ },
+ {
+ "name": "Alma English",
+ "email": "pede.Cum.sociis@telluseu.net",
+ "cou": "Member, Active, Writer, Filesource",
+ "org": "Consectetuer Corp."
+ },
+ {
+ "name": "Halee Finley",
+ "email": "in.consectetuer.ipsum@parturientmontes.co.uk",
+ "cou": "Member",
+ "org": "Felis Ltd"
+ },
+ {
+ "name": "Austin Hardy",
+ "email": "iaculis@Donecegestas.co.uk",
+ "cou": "Writer",
+ "org": "Nonummy LLP"
+ },
+ {
+ "name": "Victor Powell",
+ "email": "nisi@eget.ca",
+ "cou": "Member, Active",
+ "org": "Praesent Interdum Ligula PC"
+ },
+ {
+ "name": "Baxter Estrada",
+ "email": "in.consequat.enim@vestibulum.co.uk",
+ "cou": "",
+ "org": "In Molestie Tortor Corporation"
+ },
+ {
+ "name": "Carlos Rose",
+ "email": "elit.elit@non.co.uk",
+ "cou": "Member, Writer, Filesource, Active",
+ "org": "Risus Nulla Eget LLP"
+ },
+ {
+ "name": "Ishmael Patton",
+ "email": "leo.Morbi@mollisnoncursus.co.uk",
+ "cou": "Writer, Filesource",
+ "org": "A Enim Suspendisse Consulting"
+ },
+ {
+ "name": "Ryder Tanner",
+ "email": "varius@rutrum.org",
+ "cou": "Writer, Filesource",
+ "org": "Ornare Elit Elit Consulting"
+ },
+ {
+ "name": "Dane Rollins",
+ "email": "Curabitur.consequat.lectus@Naminterdum.ca",
+ "cou": "Member",
+ "org": "Ut Pellentesque Consulting"
+ },
+ {
+ "name": "Brian Kaufman",
+ "email": "aliquet.vel@orciluctuset.edu",
+ "cou": "Filesource, Active",
+ "org": "Justo Eu Arcu Inc."
+ },
+ {
+ "name": "Lester Price",
+ "email": "Integer@idlibero.edu",
+ "cou": "Writer, Member, Active, Filesource",
+ "org": "Dapibus Rutrum Justo Incorporated"
+ },
+ {
+ "name": "Helen Cardenas",
+ "email": "Curabitur.egestas.nunc@quisdiam.org",
+ "cou": "Member, Active, Filesource",
+ "org": "Faucibus Morbi Vehicula Associates"
+ },
+ {
+ "name": "Hop Holloway",
+ "email": "non.arcu.Vivamus@nibhvulputate.org",
+ "cou": "Filesource, Active, Member",
+ "org": "Nunc Sed Pede Ltd"
+ },
+ {
+ "name": "Madeson Hendrix",
+ "email": "viverra@purus.com",
+ "cou": "",
+ "org": "Neque Venenatis Consulting"
+ },
+ {
+ "name": "Amity Navarro",
+ "email": "tincidunt.Donec@inconsequat.com",
+ "cou": "Writer",
+ "org": "Pretium Aliquet Consulting"
+ },
+ {
+ "name": "Cameron Booth",
+ "email": "nulla.magna@Quisqueporttitor.net",
+ "cou": "",
+ "org": "Fusce Associates"
+ },
+ {
+ "name": "Sybil Burgess",
+ "email": "Fusce.feugiat@sem.org",
+ "cou": "Writer, Member, Filesource, Active",
+ "org": "Curabitur Corporation"
+ },
+ {
+ "name": "Otto Cantrell",
+ "email": "Ut@nonarcu.org",
+ "cou": "",
+ "org": "Cursus PC"
+ },
+ {
+ "name": "Mannix Obrien",
+ "email": "eu.neque@egestasDuisac.ca",
+ "cou": "",
+ "org": "Amet Faucibus Corp."
+ },
+ {
+ "name": "Marcia Gill",
+ "email": "molestie.in.tempus@posuere.edu",
+ "cou": "",
+ "org": "Et Corp."
+ },
+ {
+ "name": "Tanek Figueroa",
+ "email": "diam@arcuSed.org",
+ "cou": "Active",
+ "org": "Bibendum Donec Ltd"
+ },
+ {
+ "name": "Ira Lynn",
+ "email": "aliquet@consequatpurusMaecenas.edu",
+ "cou": "Filesource",
+ "org": "Massa Suspendisse Eleifend Corporation"
+ },
+ {
+ "name": "Lesley Pittman",
+ "email": "iaculis.odio@Phasellus.edu",
+ "cou": "Member, Writer",
+ "org": "Urna Et Arcu PC"
+ },
+ {
+ "name": "Len Ryan",
+ "email": "ut.mi.Duis@sitamet.org",
+ "cou": "Writer, Filesource, Active",
+ "org": "Vestibulum Accumsan PC"
+ },
+ {
+ "name": "Sarah Morton",
+ "email": "dis.parturient@rhoncusProin.org",
+ "cou": "Active",
+ "org": "Amet Consectetuer PC"
+ },
+ {
+ "name": "Marny Hewitt",
+ "email": "dapibus@orciquis.ca",
+ "cou": "Active, Writer",
+ "org": "Ultricies Consulting"
+ },
+ {
+ "name": "Abel Lyons",
+ "email": "Proin.eget.odio@enim.org",
+ "cou": "Writer",
+ "org": "Amet Lorem Corporation"
+ },
+ {
+ "name": "Slade Schneider",
+ "email": "ante.dictum.cursus@turpisAliquamadipiscing.co.uk",
+ "cou": "",
+ "org": "Lacinia Company"
+ },
+ {
+ "name": "Ashton Arnold",
+ "email": "velit.justo@velitin.edu",
+ "cou": "",
+ "org": "Diam Ltd"
+ },
+ {
+ "name": "Phelan Goff",
+ "email": "ullamcorper.viverra.Maecenas@Donecporttitortellus.ca",
+ "cou": "",
+ "org": "Faucibus Id Libero Institute"
+ },
+ {
+ "name": "Graham Underwood",
+ "email": "Nulla.dignissim.Maecenas@Donecsollicitudin.ca",
+ "cou": "Filesource, Member",
+ "org": "Duis Gravida Praesent Ltd"
+ },
+ {
+ "name": "Alisa Scott",
+ "email": "ipsum.primis.in@erat.co.uk",
+ "cou": "Writer, Active",
+ "org": "Vivamus Non Lorem Inc."
+ },
+ {
+ "name": "Asher Mccray",
+ "email": "semper.auctor@nibhAliquamornare.edu",
+ "cou": "Writer, Member",
+ "org": "Orci Corporation"
+ },
+ {
+ "name": "Hanna Salas",
+ "email": "feugiat@purusactellus.net",
+ "cou": "",
+ "org": "Lectus Convallis Industries"
+ },
+ {
+ "name": "Jeremy Riley",
+ "email": "Ut.sagittis@necante.ca",
+ "cou": "Writer",
+ "org": "Luctus Curabitur LLP"
+ },
+ {
+ "name": "Violet Trevino",
+ "email": "vestibulum@nec.edu",
+ "cou": "",
+ "org": "Ligula Aenean Gravida Consulting"
+ },
+ {
+ "name": "Carissa Myers",
+ "email": "non@rhoncusDonec.co.uk",
+ "cou": "Member, Filesource",
+ "org": "Tempor Corporation"
+ },
+ {
+ "name": "Kadeem Osborn",
+ "email": "nulla.In@aliquameros.ca",
+ "cou": "Filesource, Active, Member, Writer",
+ "org": "Tempus Risus Donec PC"
+ },
+ {
+ "name": "Kiayada England",
+ "email": "ultrices.posuere@lectus.net",
+ "cou": "",
+ "org": "Orci Institute"
+ },
+ {
+ "name": "Connor Gardner",
+ "email": "diam.eu@tortorIntegeraliquam.org",
+ "cou": "Member, Active, Writer",
+ "org": "Vulputate Nisi Sem LLP"
+ },
+ {
+ "name": "Alfonso Casey",
+ "email": "montes.nascetur.ridiculus@risus.net",
+ "cou": "Member",
+ "org": "Sed Eu Nibh Consulting"
+ },
+ {
+ "name": "Avye Raymond",
+ "email": "mauris.sapien@tempusrisusDonec.co.uk",
+ "cou": "Active, Writer, Filesource, Member",
+ "org": "Amet Ante Incorporated"
+ },
+ {
+ "name": "Quamar Cross",
+ "email": "elit.pretium@Sedid.org",
+ "cou": "",
+ "org": "Dapibus PC"
+ },
+ {
+ "name": "Justine Kemp",
+ "email": "diam@ornare.org",
+ "cou": "Writer, Filesource",
+ "org": "Libero At Auctor Associates"
+ },
+ {
+ "name": "Blair Rush",
+ "email": "at.velit.Cras@pulvinar.ca",
+ "cou": "",
+ "org": "Nulla Tempor Associates"
+ },
+ {
+ "name": "Jael Travis",
+ "email": "ut.dolor.dapibus@vestibulumneceuismod.ca",
+ "cou": "",
+ "org": "Facilisis Lorem LLC"
+ },
+ {
+ "name": "Donovan Patel",
+ "email": "bibendum.ullamcorper@Sedeu.co.uk",
+ "cou": "Member, Filesource, Writer, Active",
+ "org": "Ipsum Sodales Incorporated"
+ },
+ {
+ "name": "Sylvester Brady",
+ "email": "amet@consequatauctornunc.net",
+ "cou": "",
+ "org": "Semper Nam Limited"
+ },
+ {
+ "name": "Yuri Eaton",
+ "email": "Nullam.lobortis.quam@Etiamligulatortor.com",
+ "cou": "",
+ "org": "Eu Elit Nulla LLP"
+ },
+ {
+ "name": "Bree Harmon",
+ "email": "libero.Integer@maurisaliquam.ca",
+ "cou": "Filesource, Active, Member, Writer",
+ "org": "Dui In Sodales PC"
+ },
+ {
+ "name": "Tanek Tucker",
+ "email": "arcu@Craslorem.org",
+ "cou": "Active, Member, Filesource",
+ "org": "Nisl Arcu Company"
+ },
+ {
+ "name": "Jermaine Stevens",
+ "email": "luctus@Donec.org",
+ "cou": "Active, Writer",
+ "org": "Pellentesque Sed Dictum Corp."
+ },
+ {
+ "name": "Ira Robinson",
+ "email": "malesuada.vel@lobortisClass.ca",
+ "cou": "Active, Member",
+ "org": "Ac Sem Ut Consulting"
+ },
+ {
+ "name": "Jocelyn Fulton",
+ "email": "tristique.senectus.et@faucibusorci.co.uk",
+ "cou": "Active, Member, Filesource, Writer",
+ "org": "Ante Ipsum Institute"
+ },
+ {
+ "name": "Tatyana Kelly",
+ "email": "a.facilisis.non@tortorat.ca",
+ "cou": "",
+ "org": "Donec Feugiat Limited"
+ },
+ {
+ "name": "Bevis Sanchez",
+ "email": "Vivamus.rhoncus@leoin.com",
+ "cou": "Member",
+ "org": "Condimentum Donec Limited"
+ },
+ {
+ "name": "Louis Weaver",
+ "email": "vitae.posuere.at@semper.ca",
+ "cou": "Filesource, Writer, Member",
+ "org": "Interdum Ligula Eu Corp."
+ },
+ {
+ "name": "Jordan Caldwell",
+ "email": "volutpat@sagittisNullam.co.uk",
+ "cou": "Filesource",
+ "org": "Adipiscing PC"
+ },
+ {
+ "name": "Eleanor Gentry",
+ "email": "vehicula.Pellentesque.tincidunt@lacusNulla.ca",
+ "cou": "Filesource, Member, Active",
+ "org": "Elit Pellentesque A Industries"
+ },
+ {
+ "name": "Quincy Morgan",
+ "email": "dictum.eleifend.nunc@velquam.ca",
+ "cou": "",
+ "org": "Nec Associates"
+ },
+ {
+ "name": "Thomas Giles",
+ "email": "libero@montesnascetur.edu",
+ "cou": "Writer, Filesource",
+ "org": "Erat Semper Corp."
+ },
+ {
+ "name": "Alec Shelton",
+ "email": "mi.eleifend@purusMaecenaslibero.ca",
+ "cou": "Active, Member, Writer, Filesource",
+ "org": "Tincidunt Tempus Risus PC"
+ },
+ {
+ "name": "Rooney Boyle",
+ "email": "Mauris.non.dui@posuerecubiliaCurae.org",
+ "cou": "",
+ "org": "Tellus Incorporated"
+ },
+ {
+ "name": "Amela Mcknight",
+ "email": "odio@tinciduntorci.edu",
+ "cou": "Filesource",
+ "org": "Sed Sem Consulting"
+ },
+ {
+ "name": "Maia Gates",
+ "email": "vel.arcu@et.org",
+ "cou": "Filesource",
+ "org": "Ornare In Faucibus Corporation"
+ },
+ {
+ "name": "David Sexton",
+ "email": "Vivamus.non.lorem@euaccumsan.co.uk",
+ "cou": "Filesource, Writer",
+ "org": "Suspendisse Sed Company"
+ },
+ {
+ "name": "Rama Christensen",
+ "email": "pede.blandit.congue@lectuspede.ca",
+ "cou": "Writer",
+ "org": "Primis In Faucibus Institute"
+ },
+ {
+ "name": "Athena Barnes",
+ "email": "lobortis.mauris@Nulladignissim.com",
+ "cou": "Writer, Member, Active, Filesource",
+ "org": "In Molestie Industries"
+ },
+ {
+ "name": "Aimee Snow",
+ "email": "Nunc.pulvinar.arcu@sollicitudin.ca",
+ "cou": "Active, Filesource, Writer",
+ "org": "Egestas Blandit LLP"
+ },
+ {
+ "name": "Emma Thornton",
+ "email": "mattis.velit.justo@et.net",
+ "cou": "Writer, Member, Filesource",
+ "org": "Id Erat Foundation"
+ },
+ {
+ "name": "Anjolie Craft",
+ "email": "at.arcu.Vestibulum@mifringillami.net",
+ "cou": "",
+ "org": "Nibh Dolor Nonummy LLP"
+ },
+ {
+ "name": "Hamish Dickerson",
+ "email": "vitae.odio.sagittis@convallisdolorQuisque.co.uk",
+ "cou": "",
+ "org": "Vehicula Aliquet Libero LLC"
+ },
+ {
+ "name": "Matthew Lindsey",
+ "email": "orci.Ut@parturientmontes.edu",
+ "cou": "Member, Active, Filesource, Writer",
+ "org": "Et Nunc Associates"
+ },
+ {
+ "name": "Wesley Mckay",
+ "email": "non.bibendum@molestie.net",
+ "cou": "Filesource, Member, Active",
+ "org": "Tempus Non Lacinia Consulting"
+ },
+ {
+ "name": "Hilary Mcmahon",
+ "email": "ante.Nunc@magnaLoremipsum.com",
+ "cou": "Writer",
+ "org": "Vulputate Eu Associates"
+ },
+ {
+ "name": "Asher Finley",
+ "email": "et@orci.edu",
+ "cou": "Writer",
+ "org": "Eget Incorporated"
+ },
+ {
+ "name": "Chantale Whitney",
+ "email": "neque@lectusrutrum.edu",
+ "cou": "Filesource, Writer, Member, Active",
+ "org": "Enim LLP"
+ },
+ {
+ "name": "Levi Floyd",
+ "email": "ipsum.porta.elit@aliquetPhasellusfermentum.co.uk",
+ "cou": "Active, Filesource, Member",
+ "org": "A Arcu Sed Institute"
+ },
+ {
+ "name": "Jenna Lane",
+ "email": "Nulla.facilisi@acrisusMorbi.com",
+ "cou": "Writer",
+ "org": "Nisl Limited"
+ },
+ {
+ "name": "Molly Ratliff",
+ "email": "Cras@Sed.com",
+ "cou": "Writer",
+ "org": "Nulla Integer Vulputate Institute"
+ },
+ {
+ "name": "Rina Cole",
+ "email": "pede.sagittis@nibhDonecest.edu",
+ "cou": "Member, Filesource, Active",
+ "org": "Sit Corp."
+ },
+ {
+ "name": "Melanie Buckner",
+ "email": "tortor@Donecporttitortellus.co.uk",
+ "cou": "",
+ "org": "Velit Associates"
+ },
+ {
+ "name": "Basil Holland",
+ "email": "Nam.ligula@euodio.ca",
+ "cou": "",
+ "org": "Diam Vel Arcu Inc."
+ },
+ {
+ "name": "Kevin Graves",
+ "email": "eleifend.nunc.risus@arcuCurabiturut.com",
+ "cou": "Member, Active, Writer",
+ "org": "Risus Duis Limited"
+ },
+ {
+ "name": "Lillith Sears",
+ "email": "blandit.at.nisi@ametmetus.ca",
+ "cou": "Member, Filesource, Writer, Active",
+ "org": "Vitae Inc."
+ },
+ {
+ "name": "Nehru Roth",
+ "email": "ultrices.iaculis@mifringilla.com",
+ "cou": "Active",
+ "org": "Malesuada LLC"
+ },
+ {
+ "name": "Alden Hodges",
+ "email": "faucibus.orci.luctus@accumsan.ca",
+ "cou": "Filesource",
+ "org": "Orci Luctus Et Consulting"
+ },
+ {
+ "name": "Devin Waller",
+ "email": "aliquam.adipiscing@porttitor.com",
+ "cou": "",
+ "org": "Consectetuer Limited"
+ },
+ {
+ "name": "Lacota Dennis",
+ "email": "eget@imperdietnecleo.com",
+ "cou": "",
+ "org": "Nec Ante Blandit Corporation"
+ },
+ {
+ "name": "Yvette Ball",
+ "email": "augue.Sed@velitPellentesque.ca",
+ "cou": "Filesource, Active, Member",
+ "org": "Eu Tempor Erat Incorporated"
+ },
+ {
+ "name": "Nayda Guerra",
+ "email": "mauris.Morbi.non@necorci.net",
+ "cou": "Writer, Filesource, Active, Member",
+ "org": "Nisl Elementum Consulting"
+ },
+ {
+ "name": "Wang Sexton",
+ "email": "a@eulacusQuisque.ca",
+ "cou": "Filesource, Writer",
+ "org": "Fames Institute"
+ },
+ {
+ "name": "Ian Hughes",
+ "email": "ac.libero@dignissimlacus.co.uk",
+ "cou": "Member",
+ "org": "Turpis Aliquam Corp."
+ },
+ {
+ "name": "Galena Marshall",
+ "email": "ac.libero.nec@luctus.ca",
+ "cou": "",
+ "org": "Cursus LLP"
+ },
+ {
+ "name": "Piper Rowland",
+ "email": "eu.turpis@Maurisnulla.ca",
+ "cou": "Active, Member, Writer",
+ "org": "Nec Luctus Felis Ltd"
+ },
+ {
+ "name": "Madison Alvarez",
+ "email": "odio@necanteblandit.org",
+ "cou": "Filesource, Writer, Member, Active",
+ "org": "Rhoncus Associates"
+ },
+ {
+ "name": "Vielka Hunter",
+ "email": "Maecenas@feugiat.net",
+ "cou": "Filesource",
+ "org": "Cum PC"
+ },
+ {
+ "name": "Michelle Franco",
+ "email": "Suspendisse.sed.dolor@penatibusetmagnis.net",
+ "cou": "Member, Writer, Active",
+ "org": "Vestibulum Lorem Corp."
+ }
+]
\ No newline at end of file
diff --git a/webroot/img/COmanage-Logo-LG-onBlue.png b/webroot/img/COmanage-Logo-LG-onBlue.png
new file mode 100755
index 0000000..2610ed2
Binary files /dev/null and b/webroot/img/COmanage-Logo-LG-onBlue.png differ
diff --git a/webroot/img/COmanage-Logo-LG-onWhite.png b/webroot/img/COmanage-Logo-LG-onWhite.png
new file mode 100755
index 0000000..8517e42
Binary files /dev/null and b/webroot/img/COmanage-Logo-LG-onWhite.png differ
diff --git a/webroot/img/Grouper.jpg b/webroot/img/Grouper.jpg
new file mode 100644
index 0000000..b7fd2bc
Binary files /dev/null and b/webroot/img/Grouper.jpg differ
diff --git a/webroot/img/arrows-333333.png b/webroot/img/arrows-333333.png
new file mode 100644
index 0000000..480d445
Binary files /dev/null and b/webroot/img/arrows-333333.png differ
diff --git a/webroot/img/cake-favicon.ico b/webroot/img/cake-favicon.ico
new file mode 100644
index 0000000..b36e81f
Binary files /dev/null and b/webroot/img/cake-favicon.ico differ
diff --git a/webroot/img/cake.icon.png b/webroot/img/cake.icon.png
new file mode 100644
index 0000000..394fa42
Binary files /dev/null and b/webroot/img/cake.icon.png differ
diff --git a/webroot/img/cake.power.gif b/webroot/img/cake.power.gif
new file mode 100644
index 0000000..8f8d570
Binary files /dev/null and b/webroot/img/cake.power.gif differ
diff --git a/webroot/img/comanage-favicon.ico b/webroot/img/comanage-favicon.ico
new file mode 100644
index 0000000..3434f24
Binary files /dev/null and b/webroot/img/comanage-favicon.ico differ
diff --git a/webroot/img/comanage-favicon.png b/webroot/img/comanage-favicon.png
new file mode 100644
index 0000000..c8b9489
Binary files /dev/null and b/webroot/img/comanage-favicon.png differ
diff --git a/webroot/img/comanage-gear.png b/webroot/img/comanage-gear.png
new file mode 100644
index 0000000..f9995a9
Binary files /dev/null and b/webroot/img/comanage-gear.png differ
diff --git a/webroot/img/comanage-logo.jpg b/webroot/img/comanage-logo.jpg
new file mode 100644
index 0000000..37c529b
Binary files /dev/null and b/webroot/img/comanage-logo.jpg differ
diff --git a/webroot/img/comanage-logo.png b/webroot/img/comanage-logo.png
new file mode 100644
index 0000000..6d6ee98
Binary files /dev/null and b/webroot/img/comanage-logo.png differ
diff --git a/webroot/img/empty b/webroot/img/empty
new file mode 100644
index 0000000..e69de29
diff --git a/webroot/img/grouper-logo.png b/webroot/img/grouper-logo.png
new file mode 100644
index 0000000..bb90c47
Binary files /dev/null and b/webroot/img/grouper-logo.png differ
diff --git a/webroot/img/header_bg.jpg b/webroot/img/header_bg.jpg
new file mode 100644
index 0000000..70ff1f4
Binary files /dev/null and b/webroot/img/header_bg.jpg differ
diff --git a/webroot/js/bootstrap.bundle.js b/webroot/js/bootstrap.bundle.js
new file mode 100644
index 0000000..5fda309
--- /dev/null
+++ b/webroot/js/bootstrap.bundle.js
@@ -0,0 +1,7031 @@
+/*!
+ * Bootstrap v4.5.3 (https://getbootstrap.com/)
+ * Copyright 2011-2020 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('jquery')) :
+ typeof define === 'function' && define.amd ? define(['exports', 'jquery'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.bootstrap = {}, global.jQuery));
+}(this, (function (exports, $) { 'use strict';
+
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
+
+ var $__default = /*#__PURE__*/_interopDefaultLegacy($);
+
+ function _defineProperties(target, props) {
+ for (var i = 0; i < props.length; i++) {
+ var descriptor = props[i];
+ descriptor.enumerable = descriptor.enumerable || false;
+ descriptor.configurable = true;
+ if ("value" in descriptor) descriptor.writable = true;
+ Object.defineProperty(target, descriptor.key, descriptor);
+ }
+ }
+
+ function _createClass(Constructor, protoProps, staticProps) {
+ if (protoProps) _defineProperties(Constructor.prototype, protoProps);
+ if (staticProps) _defineProperties(Constructor, staticProps);
+ return Constructor;
+ }
+
+ function _extends() {
+ _extends = Object.assign || function (target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i];
+
+ for (var key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+ }
+
+ return target;
+ };
+
+ return _extends.apply(this, arguments);
+ }
+
+ function _inheritsLoose(subClass, superClass) {
+ subClass.prototype = Object.create(superClass.prototype);
+ subClass.prototype.constructor = subClass;
+ subClass.__proto__ = superClass;
+ }
+
+ /**
+ * --------------------------------------------------------------------------
+ * Bootstrap (v4.5.3): util.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+ /**
+ * ------------------------------------------------------------------------
+ * Private TransitionEnd Helpers
+ * ------------------------------------------------------------------------
+ */
+
+ var TRANSITION_END = 'transitionend';
+ var MAX_UID = 1000000;
+ var MILLISECONDS_MULTIPLIER = 1000; // Shoutout AngusCroll (https://goo.gl/pxwQGp)
+
+ function toType(obj) {
+ if (obj === null || typeof obj === 'undefined') {
+ return "" + obj;
+ }
+
+ return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase();
+ }
+
+ function getSpecialTransitionEndEvent() {
+ return {
+ bindType: TRANSITION_END,
+ delegateType: TRANSITION_END,
+ handle: function handle(event) {
+ if ($__default['default'](event.target).is(this)) {
+ return event.handleObj.handler.apply(this, arguments); // eslint-disable-line prefer-rest-params
+ }
+
+ return undefined;
+ }
+ };
+ }
+
+ function transitionEndEmulator(duration) {
+ var _this = this;
+
+ var called = false;
+ $__default['default'](this).one(Util.TRANSITION_END, function () {
+ called = true;
+ });
+ setTimeout(function () {
+ if (!called) {
+ Util.triggerTransitionEnd(_this);
+ }
+ }, duration);
+ return this;
+ }
+
+ function setTransitionEndSupport() {
+ $__default['default'].fn.emulateTransitionEnd = transitionEndEmulator;
+ $__default['default'].event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent();
+ }
+ /**
+ * --------------------------------------------------------------------------
+ * Public Util Api
+ * --------------------------------------------------------------------------
+ */
+
+
+ var Util = {
+ TRANSITION_END: 'bsTransitionEnd',
+ getUID: function getUID(prefix) {
+ do {
+ prefix += ~~(Math.random() * MAX_UID); // "~~" acts like a faster Math.floor() here
+ } while (document.getElementById(prefix));
+
+ return prefix;
+ },
+ getSelectorFromElement: function getSelectorFromElement(element) {
+ var selector = element.getAttribute('data-target');
+
+ if (!selector || selector === '#') {
+ var hrefAttr = element.getAttribute('href');
+ selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : '';
+ }
+
+ try {
+ return document.querySelector(selector) ? selector : null;
+ } catch (_) {
+ return null;
+ }
+ },
+ getTransitionDurationFromElement: function getTransitionDurationFromElement(element) {
+ if (!element) {
+ return 0;
+ } // Get transition-duration of the element
+
+
+ var transitionDuration = $__default['default'](element).css('transition-duration');
+ var transitionDelay = $__default['default'](element).css('transition-delay');
+ var floatTransitionDuration = parseFloat(transitionDuration);
+ var floatTransitionDelay = parseFloat(transitionDelay); // Return 0 if element or transition duration is not found
+
+ if (!floatTransitionDuration && !floatTransitionDelay) {
+ return 0;
+ } // If multiple durations are defined, take the first
+
+
+ transitionDuration = transitionDuration.split(',')[0];
+ transitionDelay = transitionDelay.split(',')[0];
+ return (parseFloat(transitionDuration) + parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;
+ },
+ reflow: function reflow(element) {
+ return element.offsetHeight;
+ },
+ triggerTransitionEnd: function triggerTransitionEnd(element) {
+ $__default['default'](element).trigger(TRANSITION_END);
+ },
+ supportsTransitionEnd: function supportsTransitionEnd() {
+ return Boolean(TRANSITION_END);
+ },
+ isElement: function isElement(obj) {
+ return (obj[0] || obj).nodeType;
+ },
+ typeCheckConfig: function typeCheckConfig(componentName, config, configTypes) {
+ for (var property in configTypes) {
+ if (Object.prototype.hasOwnProperty.call(configTypes, property)) {
+ var expectedTypes = configTypes[property];
+ var value = config[property];
+ var valueType = value && Util.isElement(value) ? 'element' : toType(value);
+
+ if (!new RegExp(expectedTypes).test(valueType)) {
+ throw new Error(componentName.toUpperCase() + ": " + ("Option \"" + property + "\" provided type \"" + valueType + "\" ") + ("but expected type \"" + expectedTypes + "\"."));
+ }
+ }
+ }
+ },
+ findShadowRoot: function findShadowRoot(element) {
+ if (!document.documentElement.attachShadow) {
+ return null;
+ } // Can find the shadow root otherwise it'll return the document
+
+
+ if (typeof element.getRootNode === 'function') {
+ var root = element.getRootNode();
+ return root instanceof ShadowRoot ? root : null;
+ }
+
+ if (element instanceof ShadowRoot) {
+ return element;
+ } // when we don't find a shadow root
+
+
+ if (!element.parentNode) {
+ return null;
+ }
+
+ return Util.findShadowRoot(element.parentNode);
+ },
+ jQueryDetection: function jQueryDetection() {
+ if (typeof $__default['default'] === 'undefined') {
+ throw new TypeError('Bootstrap\'s JavaScript requires jQuery. jQuery must be included before Bootstrap\'s JavaScript.');
+ }
+
+ var version = $__default['default'].fn.jquery.split(' ')[0].split('.');
+ var minMajor = 1;
+ var ltMajor = 2;
+ var minMinor = 9;
+ var minPatch = 1;
+ var maxMajor = 4;
+
+ if (version[0] < ltMajor && version[1] < minMinor || version[0] === minMajor && version[1] === minMinor && version[2] < minPatch || version[0] >= maxMajor) {
+ throw new Error('Bootstrap\'s JavaScript requires at least jQuery v1.9.1 but less than v4.0.0');
+ }
+ }
+ };
+ Util.jQueryDetection();
+ setTransitionEndSupport();
+
+ /**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+ var NAME = 'alert';
+ var VERSION = '4.5.3';
+ var DATA_KEY = 'bs.alert';
+ var EVENT_KEY = "." + DATA_KEY;
+ var DATA_API_KEY = '.data-api';
+ var JQUERY_NO_CONFLICT = $__default['default'].fn[NAME];
+ var SELECTOR_DISMISS = '[data-dismiss="alert"]';
+ var EVENT_CLOSE = "close" + EVENT_KEY;
+ var EVENT_CLOSED = "closed" + EVENT_KEY;
+ var EVENT_CLICK_DATA_API = "click" + EVENT_KEY + DATA_API_KEY;
+ var CLASS_NAME_ALERT = 'alert';
+ var CLASS_NAME_FADE = 'fade';
+ var CLASS_NAME_SHOW = 'show';
+ /**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+ var Alert = /*#__PURE__*/function () {
+ function Alert(element) {
+ this._element = element;
+ } // Getters
+
+
+ var _proto = Alert.prototype;
+
+ // Public
+ _proto.close = function close(element) {
+ var rootElement = this._element;
+
+ if (element) {
+ rootElement = this._getRootElement(element);
+ }
+
+ var customEvent = this._triggerCloseEvent(rootElement);
+
+ if (customEvent.isDefaultPrevented()) {
+ return;
+ }
+
+ this._removeElement(rootElement);
+ };
+
+ _proto.dispose = function dispose() {
+ $__default['default'].removeData(this._element, DATA_KEY);
+ this._element = null;
+ } // Private
+ ;
+
+ _proto._getRootElement = function _getRootElement(element) {
+ var selector = Util.getSelectorFromElement(element);
+ var parent = false;
+
+ if (selector) {
+ parent = document.querySelector(selector);
+ }
+
+ if (!parent) {
+ parent = $__default['default'](element).closest("." + CLASS_NAME_ALERT)[0];
+ }
+
+ return parent;
+ };
+
+ _proto._triggerCloseEvent = function _triggerCloseEvent(element) {
+ var closeEvent = $__default['default'].Event(EVENT_CLOSE);
+ $__default['default'](element).trigger(closeEvent);
+ return closeEvent;
+ };
+
+ _proto._removeElement = function _removeElement(element) {
+ var _this = this;
+
+ $__default['default'](element).removeClass(CLASS_NAME_SHOW);
+
+ if (!$__default['default'](element).hasClass(CLASS_NAME_FADE)) {
+ this._destroyElement(element);
+
+ return;
+ }
+
+ var transitionDuration = Util.getTransitionDurationFromElement(element);
+ $__default['default'](element).one(Util.TRANSITION_END, function (event) {
+ return _this._destroyElement(element, event);
+ }).emulateTransitionEnd(transitionDuration);
+ };
+
+ _proto._destroyElement = function _destroyElement(element) {
+ $__default['default'](element).detach().trigger(EVENT_CLOSED).remove();
+ } // Static
+ ;
+
+ Alert._jQueryInterface = function _jQueryInterface(config) {
+ return this.each(function () {
+ var $element = $__default['default'](this);
+ var data = $element.data(DATA_KEY);
+
+ if (!data) {
+ data = new Alert(this);
+ $element.data(DATA_KEY, data);
+ }
+
+ if (config === 'close') {
+ data[config](this);
+ }
+ });
+ };
+
+ Alert._handleDismiss = function _handleDismiss(alertInstance) {
+ return function (event) {
+ if (event) {
+ event.preventDefault();
+ }
+
+ alertInstance.close(this);
+ };
+ };
+
+ _createClass(Alert, null, [{
+ key: "VERSION",
+ get: function get() {
+ return VERSION;
+ }
+ }]);
+
+ return Alert;
+ }();
+ /**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+
+ $__default['default'](document).on(EVENT_CLICK_DATA_API, SELECTOR_DISMISS, Alert._handleDismiss(new Alert()));
+ /**
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ */
+
+ $__default['default'].fn[NAME] = Alert._jQueryInterface;
+ $__default['default'].fn[NAME].Constructor = Alert;
+
+ $__default['default'].fn[NAME].noConflict = function () {
+ $__default['default'].fn[NAME] = JQUERY_NO_CONFLICT;
+ return Alert._jQueryInterface;
+ };
+
+ /**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+ var NAME$1 = 'button';
+ var VERSION$1 = '4.5.3';
+ var DATA_KEY$1 = 'bs.button';
+ var EVENT_KEY$1 = "." + DATA_KEY$1;
+ var DATA_API_KEY$1 = '.data-api';
+ var JQUERY_NO_CONFLICT$1 = $__default['default'].fn[NAME$1];
+ var CLASS_NAME_ACTIVE = 'active';
+ var CLASS_NAME_BUTTON = 'btn';
+ var CLASS_NAME_FOCUS = 'focus';
+ var SELECTOR_DATA_TOGGLE_CARROT = '[data-toggle^="button"]';
+ var SELECTOR_DATA_TOGGLES = '[data-toggle="buttons"]';
+ var SELECTOR_DATA_TOGGLE = '[data-toggle="button"]';
+ var SELECTOR_DATA_TOGGLES_BUTTONS = '[data-toggle="buttons"] .btn';
+ var SELECTOR_INPUT = 'input:not([type="hidden"])';
+ var SELECTOR_ACTIVE = '.active';
+ var SELECTOR_BUTTON = '.btn';
+ var EVENT_CLICK_DATA_API$1 = "click" + EVENT_KEY$1 + DATA_API_KEY$1;
+ var EVENT_FOCUS_BLUR_DATA_API = "focus" + EVENT_KEY$1 + DATA_API_KEY$1 + " " + ("blur" + EVENT_KEY$1 + DATA_API_KEY$1);
+ var EVENT_LOAD_DATA_API = "load" + EVENT_KEY$1 + DATA_API_KEY$1;
+ /**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+ var Button = /*#__PURE__*/function () {
+ function Button(element) {
+ this._element = element;
+ this.shouldAvoidTriggerChange = false;
+ } // Getters
+
+
+ var _proto = Button.prototype;
+
+ // Public
+ _proto.toggle = function toggle() {
+ var triggerChangeEvent = true;
+ var addAriaPressed = true;
+ var rootElement = $__default['default'](this._element).closest(SELECTOR_DATA_TOGGLES)[0];
+
+ if (rootElement) {
+ var input = this._element.querySelector(SELECTOR_INPUT);
+
+ if (input) {
+ if (input.type === 'radio') {
+ if (input.checked && this._element.classList.contains(CLASS_NAME_ACTIVE)) {
+ triggerChangeEvent = false;
+ } else {
+ var activeElement = rootElement.querySelector(SELECTOR_ACTIVE);
+
+ if (activeElement) {
+ $__default['default'](activeElement).removeClass(CLASS_NAME_ACTIVE);
+ }
+ }
+ }
+
+ if (triggerChangeEvent) {
+ // if it's not a radio button or checkbox don't add a pointless/invalid checked property to the input
+ if (input.type === 'checkbox' || input.type === 'radio') {
+ input.checked = !this._element.classList.contains(CLASS_NAME_ACTIVE);
+ }
+
+ if (!this.shouldAvoidTriggerChange) {
+ $__default['default'](input).trigger('change');
+ }
+ }
+
+ input.focus();
+ addAriaPressed = false;
+ }
+ }
+
+ if (!(this._element.hasAttribute('disabled') || this._element.classList.contains('disabled'))) {
+ if (addAriaPressed) {
+ this._element.setAttribute('aria-pressed', !this._element.classList.contains(CLASS_NAME_ACTIVE));
+ }
+
+ if (triggerChangeEvent) {
+ $__default['default'](this._element).toggleClass(CLASS_NAME_ACTIVE);
+ }
+ }
+ };
+
+ _proto.dispose = function dispose() {
+ $__default['default'].removeData(this._element, DATA_KEY$1);
+ this._element = null;
+ } // Static
+ ;
+
+ Button._jQueryInterface = function _jQueryInterface(config, avoidTriggerChange) {
+ return this.each(function () {
+ var $element = $__default['default'](this);
+ var data = $element.data(DATA_KEY$1);
+
+ if (!data) {
+ data = new Button(this);
+ $element.data(DATA_KEY$1, data);
+ }
+
+ data.shouldAvoidTriggerChange = avoidTriggerChange;
+
+ if (config === 'toggle') {
+ data[config]();
+ }
+ });
+ };
+
+ _createClass(Button, null, [{
+ key: "VERSION",
+ get: function get() {
+ return VERSION$1;
+ }
+ }]);
+
+ return Button;
+ }();
+ /**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+
+ $__default['default'](document).on(EVENT_CLICK_DATA_API$1, SELECTOR_DATA_TOGGLE_CARROT, function (event) {
+ var button = event.target;
+ var initialButton = button;
+
+ if (!$__default['default'](button).hasClass(CLASS_NAME_BUTTON)) {
+ button = $__default['default'](button).closest(SELECTOR_BUTTON)[0];
+ }
+
+ if (!button || button.hasAttribute('disabled') || button.classList.contains('disabled')) {
+ event.preventDefault(); // work around Firefox bug #1540995
+ } else {
+ var inputBtn = button.querySelector(SELECTOR_INPUT);
+
+ if (inputBtn && (inputBtn.hasAttribute('disabled') || inputBtn.classList.contains('disabled'))) {
+ event.preventDefault(); // work around Firefox bug #1540995
+
+ return;
+ }
+
+ if (initialButton.tagName === 'INPUT' || button.tagName !== 'LABEL') {
+ Button._jQueryInterface.call($__default['default'](button), 'toggle', initialButton.tagName === 'INPUT');
+ }
+ }
+ }).on(EVENT_FOCUS_BLUR_DATA_API, SELECTOR_DATA_TOGGLE_CARROT, function (event) {
+ var button = $__default['default'](event.target).closest(SELECTOR_BUTTON)[0];
+ $__default['default'](button).toggleClass(CLASS_NAME_FOCUS, /^focus(in)?$/.test(event.type));
+ });
+ $__default['default'](window).on(EVENT_LOAD_DATA_API, function () {
+ // ensure correct active class is set to match the controls' actual values/states
+ // find all checkboxes/readio buttons inside data-toggle groups
+ var buttons = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLES_BUTTONS));
+
+ for (var i = 0, len = buttons.length; i < len; i++) {
+ var button = buttons[i];
+ var input = button.querySelector(SELECTOR_INPUT);
+
+ if (input.checked || input.hasAttribute('checked')) {
+ button.classList.add(CLASS_NAME_ACTIVE);
+ } else {
+ button.classList.remove(CLASS_NAME_ACTIVE);
+ }
+ } // find all button toggles
+
+
+ buttons = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE));
+
+ for (var _i = 0, _len = buttons.length; _i < _len; _i++) {
+ var _button = buttons[_i];
+
+ if (_button.getAttribute('aria-pressed') === 'true') {
+ _button.classList.add(CLASS_NAME_ACTIVE);
+ } else {
+ _button.classList.remove(CLASS_NAME_ACTIVE);
+ }
+ }
+ });
+ /**
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ */
+
+ $__default['default'].fn[NAME$1] = Button._jQueryInterface;
+ $__default['default'].fn[NAME$1].Constructor = Button;
+
+ $__default['default'].fn[NAME$1].noConflict = function () {
+ $__default['default'].fn[NAME$1] = JQUERY_NO_CONFLICT$1;
+ return Button._jQueryInterface;
+ };
+
+ /**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+ var NAME$2 = 'carousel';
+ var VERSION$2 = '4.5.3';
+ var DATA_KEY$2 = 'bs.carousel';
+ var EVENT_KEY$2 = "." + DATA_KEY$2;
+ var DATA_API_KEY$2 = '.data-api';
+ var JQUERY_NO_CONFLICT$2 = $__default['default'].fn[NAME$2];
+ var ARROW_LEFT_KEYCODE = 37; // KeyboardEvent.which value for left arrow key
+
+ var ARROW_RIGHT_KEYCODE = 39; // KeyboardEvent.which value for right arrow key
+
+ var TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch
+
+ var SWIPE_THRESHOLD = 40;
+ var Default = {
+ interval: 5000,
+ keyboard: true,
+ slide: false,
+ pause: 'hover',
+ wrap: true,
+ touch: true
+ };
+ var DefaultType = {
+ interval: '(number|boolean)',
+ keyboard: 'boolean',
+ slide: '(boolean|string)',
+ pause: '(string|boolean)',
+ wrap: 'boolean',
+ touch: 'boolean'
+ };
+ var DIRECTION_NEXT = 'next';
+ var DIRECTION_PREV = 'prev';
+ var DIRECTION_LEFT = 'left';
+ var DIRECTION_RIGHT = 'right';
+ var EVENT_SLIDE = "slide" + EVENT_KEY$2;
+ var EVENT_SLID = "slid" + EVENT_KEY$2;
+ var EVENT_KEYDOWN = "keydown" + EVENT_KEY$2;
+ var EVENT_MOUSEENTER = "mouseenter" + EVENT_KEY$2;
+ var EVENT_MOUSELEAVE = "mouseleave" + EVENT_KEY$2;
+ var EVENT_TOUCHSTART = "touchstart" + EVENT_KEY$2;
+ var EVENT_TOUCHMOVE = "touchmove" + EVENT_KEY$2;
+ var EVENT_TOUCHEND = "touchend" + EVENT_KEY$2;
+ var EVENT_POINTERDOWN = "pointerdown" + EVENT_KEY$2;
+ var EVENT_POINTERUP = "pointerup" + EVENT_KEY$2;
+ var EVENT_DRAG_START = "dragstart" + EVENT_KEY$2;
+ var EVENT_LOAD_DATA_API$1 = "load" + EVENT_KEY$2 + DATA_API_KEY$2;
+ var EVENT_CLICK_DATA_API$2 = "click" + EVENT_KEY$2 + DATA_API_KEY$2;
+ var CLASS_NAME_CAROUSEL = 'carousel';
+ var CLASS_NAME_ACTIVE$1 = 'active';
+ var CLASS_NAME_SLIDE = 'slide';
+ var CLASS_NAME_RIGHT = 'carousel-item-right';
+ var CLASS_NAME_LEFT = 'carousel-item-left';
+ var CLASS_NAME_NEXT = 'carousel-item-next';
+ var CLASS_NAME_PREV = 'carousel-item-prev';
+ var CLASS_NAME_POINTER_EVENT = 'pointer-event';
+ var SELECTOR_ACTIVE$1 = '.active';
+ var SELECTOR_ACTIVE_ITEM = '.active.carousel-item';
+ var SELECTOR_ITEM = '.carousel-item';
+ var SELECTOR_ITEM_IMG = '.carousel-item img';
+ var SELECTOR_NEXT_PREV = '.carousel-item-next, .carousel-item-prev';
+ var SELECTOR_INDICATORS = '.carousel-indicators';
+ var SELECTOR_DATA_SLIDE = '[data-slide], [data-slide-to]';
+ var SELECTOR_DATA_RIDE = '[data-ride="carousel"]';
+ var PointerType = {
+ TOUCH: 'touch',
+ PEN: 'pen'
+ };
+ /**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+ var Carousel = /*#__PURE__*/function () {
+ function Carousel(element, config) {
+ this._items = null;
+ this._interval = null;
+ this._activeElement = null;
+ this._isPaused = false;
+ this._isSliding = false;
+ this.touchTimeout = null;
+ this.touchStartX = 0;
+ this.touchDeltaX = 0;
+ this._config = this._getConfig(config);
+ this._element = element;
+ this._indicatorsElement = this._element.querySelector(SELECTOR_INDICATORS);
+ this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0;
+ this._pointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent);
+
+ this._addEventListeners();
+ } // Getters
+
+
+ var _proto = Carousel.prototype;
+
+ // Public
+ _proto.next = function next() {
+ if (!this._isSliding) {
+ this._slide(DIRECTION_NEXT);
+ }
+ };
+
+ _proto.nextWhenVisible = function nextWhenVisible() {
+ var $element = $__default['default'](this._element); // Don't call next when the page isn't visible
+ // or the carousel or its parent isn't visible
+
+ if (!document.hidden && $element.is(':visible') && $element.css('visibility') !== 'hidden') {
+ this.next();
+ }
+ };
+
+ _proto.prev = function prev() {
+ if (!this._isSliding) {
+ this._slide(DIRECTION_PREV);
+ }
+ };
+
+ _proto.pause = function pause(event) {
+ if (!event) {
+ this._isPaused = true;
+ }
+
+ if (this._element.querySelector(SELECTOR_NEXT_PREV)) {
+ Util.triggerTransitionEnd(this._element);
+ this.cycle(true);
+ }
+
+ clearInterval(this._interval);
+ this._interval = null;
+ };
+
+ _proto.cycle = function cycle(event) {
+ if (!event) {
+ this._isPaused = false;
+ }
+
+ if (this._interval) {
+ clearInterval(this._interval);
+ this._interval = null;
+ }
+
+ if (this._config.interval && !this._isPaused) {
+ this._interval = setInterval((document.visibilityState ? this.nextWhenVisible : this.next).bind(this), this._config.interval);
+ }
+ };
+
+ _proto.to = function to(index) {
+ var _this = this;
+
+ this._activeElement = this._element.querySelector(SELECTOR_ACTIVE_ITEM);
+
+ var activeIndex = this._getItemIndex(this._activeElement);
+
+ if (index > this._items.length - 1 || index < 0) {
+ return;
+ }
+
+ if (this._isSliding) {
+ $__default['default'](this._element).one(EVENT_SLID, function () {
+ return _this.to(index);
+ });
+ return;
+ }
+
+ if (activeIndex === index) {
+ this.pause();
+ this.cycle();
+ return;
+ }
+
+ var direction = index > activeIndex ? DIRECTION_NEXT : DIRECTION_PREV;
+
+ this._slide(direction, this._items[index]);
+ };
+
+ _proto.dispose = function dispose() {
+ $__default['default'](this._element).off(EVENT_KEY$2);
+ $__default['default'].removeData(this._element, DATA_KEY$2);
+ this._items = null;
+ this._config = null;
+ this._element = null;
+ this._interval = null;
+ this._isPaused = null;
+ this._isSliding = null;
+ this._activeElement = null;
+ this._indicatorsElement = null;
+ } // Private
+ ;
+
+ _proto._getConfig = function _getConfig(config) {
+ config = _extends({}, Default, config);
+ Util.typeCheckConfig(NAME$2, config, DefaultType);
+ return config;
+ };
+
+ _proto._handleSwipe = function _handleSwipe() {
+ var absDeltax = Math.abs(this.touchDeltaX);
+
+ if (absDeltax <= SWIPE_THRESHOLD) {
+ return;
+ }
+
+ var direction = absDeltax / this.touchDeltaX;
+ this.touchDeltaX = 0; // swipe left
+
+ if (direction > 0) {
+ this.prev();
+ } // swipe right
+
+
+ if (direction < 0) {
+ this.next();
+ }
+ };
+
+ _proto._addEventListeners = function _addEventListeners() {
+ var _this2 = this;
+
+ if (this._config.keyboard) {
+ $__default['default'](this._element).on(EVENT_KEYDOWN, function (event) {
+ return _this2._keydown(event);
+ });
+ }
+
+ if (this._config.pause === 'hover') {
+ $__default['default'](this._element).on(EVENT_MOUSEENTER, function (event) {
+ return _this2.pause(event);
+ }).on(EVENT_MOUSELEAVE, function (event) {
+ return _this2.cycle(event);
+ });
+ }
+
+ if (this._config.touch) {
+ this._addTouchEventListeners();
+ }
+ };
+
+ _proto._addTouchEventListeners = function _addTouchEventListeners() {
+ var _this3 = this;
+
+ if (!this._touchSupported) {
+ return;
+ }
+
+ var start = function start(event) {
+ if (_this3._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) {
+ _this3.touchStartX = event.originalEvent.clientX;
+ } else if (!_this3._pointerEvent) {
+ _this3.touchStartX = event.originalEvent.touches[0].clientX;
+ }
+ };
+
+ var move = function move(event) {
+ // ensure swiping with one touch and not pinching
+ if (event.originalEvent.touches && event.originalEvent.touches.length > 1) {
+ _this3.touchDeltaX = 0;
+ } else {
+ _this3.touchDeltaX = event.originalEvent.touches[0].clientX - _this3.touchStartX;
+ }
+ };
+
+ var end = function end(event) {
+ if (_this3._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) {
+ _this3.touchDeltaX = event.originalEvent.clientX - _this3.touchStartX;
+ }
+
+ _this3._handleSwipe();
+
+ if (_this3._config.pause === 'hover') {
+ // If it's a touch-enabled device, mouseenter/leave are fired as
+ // part of the mouse compatibility events on first tap - the carousel
+ // would stop cycling until user tapped out of it;
+ // here, we listen for touchend, explicitly pause the carousel
+ // (as if it's the second time we tap on it, mouseenter compat event
+ // is NOT fired) and after a timeout (to allow for mouse compatibility
+ // events to fire) we explicitly restart cycling
+ _this3.pause();
+
+ if (_this3.touchTimeout) {
+ clearTimeout(_this3.touchTimeout);
+ }
+
+ _this3.touchTimeout = setTimeout(function (event) {
+ return _this3.cycle(event);
+ }, TOUCHEVENT_COMPAT_WAIT + _this3._config.interval);
+ }
+ };
+
+ $__default['default'](this._element.querySelectorAll(SELECTOR_ITEM_IMG)).on(EVENT_DRAG_START, function (e) {
+ return e.preventDefault();
+ });
+
+ if (this._pointerEvent) {
+ $__default['default'](this._element).on(EVENT_POINTERDOWN, function (event) {
+ return start(event);
+ });
+ $__default['default'](this._element).on(EVENT_POINTERUP, function (event) {
+ return end(event);
+ });
+
+ this._element.classList.add(CLASS_NAME_POINTER_EVENT);
+ } else {
+ $__default['default'](this._element).on(EVENT_TOUCHSTART, function (event) {
+ return start(event);
+ });
+ $__default['default'](this._element).on(EVENT_TOUCHMOVE, function (event) {
+ return move(event);
+ });
+ $__default['default'](this._element).on(EVENT_TOUCHEND, function (event) {
+ return end(event);
+ });
+ }
+ };
+
+ _proto._keydown = function _keydown(event) {
+ if (/input|textarea/i.test(event.target.tagName)) {
+ return;
+ }
+
+ switch (event.which) {
+ case ARROW_LEFT_KEYCODE:
+ event.preventDefault();
+ this.prev();
+ break;
+
+ case ARROW_RIGHT_KEYCODE:
+ event.preventDefault();
+ this.next();
+ break;
+ }
+ };
+
+ _proto._getItemIndex = function _getItemIndex(element) {
+ this._items = element && element.parentNode ? [].slice.call(element.parentNode.querySelectorAll(SELECTOR_ITEM)) : [];
+ return this._items.indexOf(element);
+ };
+
+ _proto._getItemByDirection = function _getItemByDirection(direction, activeElement) {
+ var isNextDirection = direction === DIRECTION_NEXT;
+ var isPrevDirection = direction === DIRECTION_PREV;
+
+ var activeIndex = this._getItemIndex(activeElement);
+
+ var lastItemIndex = this._items.length - 1;
+ var isGoingToWrap = isPrevDirection && activeIndex === 0 || isNextDirection && activeIndex === lastItemIndex;
+
+ if (isGoingToWrap && !this._config.wrap) {
+ return activeElement;
+ }
+
+ var delta = direction === DIRECTION_PREV ? -1 : 1;
+ var itemIndex = (activeIndex + delta) % this._items.length;
+ return itemIndex === -1 ? this._items[this._items.length - 1] : this._items[itemIndex];
+ };
+
+ _proto._triggerSlideEvent = function _triggerSlideEvent(relatedTarget, eventDirectionName) {
+ var targetIndex = this._getItemIndex(relatedTarget);
+
+ var fromIndex = this._getItemIndex(this._element.querySelector(SELECTOR_ACTIVE_ITEM));
+
+ var slideEvent = $__default['default'].Event(EVENT_SLIDE, {
+ relatedTarget: relatedTarget,
+ direction: eventDirectionName,
+ from: fromIndex,
+ to: targetIndex
+ });
+ $__default['default'](this._element).trigger(slideEvent);
+ return slideEvent;
+ };
+
+ _proto._setActiveIndicatorElement = function _setActiveIndicatorElement(element) {
+ if (this._indicatorsElement) {
+ var indicators = [].slice.call(this._indicatorsElement.querySelectorAll(SELECTOR_ACTIVE$1));
+ $__default['default'](indicators).removeClass(CLASS_NAME_ACTIVE$1);
+
+ var nextIndicator = this._indicatorsElement.children[this._getItemIndex(element)];
+
+ if (nextIndicator) {
+ $__default['default'](nextIndicator).addClass(CLASS_NAME_ACTIVE$1);
+ }
+ }
+ };
+
+ _proto._slide = function _slide(direction, element) {
+ var _this4 = this;
+
+ var activeElement = this._element.querySelector(SELECTOR_ACTIVE_ITEM);
+
+ var activeElementIndex = this._getItemIndex(activeElement);
+
+ var nextElement = element || activeElement && this._getItemByDirection(direction, activeElement);
+
+ var nextElementIndex = this._getItemIndex(nextElement);
+
+ var isCycling = Boolean(this._interval);
+ var directionalClassName;
+ var orderClassName;
+ var eventDirectionName;
+
+ if (direction === DIRECTION_NEXT) {
+ directionalClassName = CLASS_NAME_LEFT;
+ orderClassName = CLASS_NAME_NEXT;
+ eventDirectionName = DIRECTION_LEFT;
+ } else {
+ directionalClassName = CLASS_NAME_RIGHT;
+ orderClassName = CLASS_NAME_PREV;
+ eventDirectionName = DIRECTION_RIGHT;
+ }
+
+ if (nextElement && $__default['default'](nextElement).hasClass(CLASS_NAME_ACTIVE$1)) {
+ this._isSliding = false;
+ return;
+ }
+
+ var slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName);
+
+ if (slideEvent.isDefaultPrevented()) {
+ return;
+ }
+
+ if (!activeElement || !nextElement) {
+ // Some weirdness is happening, so we bail
+ return;
+ }
+
+ this._isSliding = true;
+
+ if (isCycling) {
+ this.pause();
+ }
+
+ this._setActiveIndicatorElement(nextElement);
+
+ var slidEvent = $__default['default'].Event(EVENT_SLID, {
+ relatedTarget: nextElement,
+ direction: eventDirectionName,
+ from: activeElementIndex,
+ to: nextElementIndex
+ });
+
+ if ($__default['default'](this._element).hasClass(CLASS_NAME_SLIDE)) {
+ $__default['default'](nextElement).addClass(orderClassName);
+ Util.reflow(nextElement);
+ $__default['default'](activeElement).addClass(directionalClassName);
+ $__default['default'](nextElement).addClass(directionalClassName);
+ var nextElementInterval = parseInt(nextElement.getAttribute('data-interval'), 10);
+
+ if (nextElementInterval) {
+ this._config.defaultInterval = this._config.defaultInterval || this._config.interval;
+ this._config.interval = nextElementInterval;
+ } else {
+ this._config.interval = this._config.defaultInterval || this._config.interval;
+ }
+
+ var transitionDuration = Util.getTransitionDurationFromElement(activeElement);
+ $__default['default'](activeElement).one(Util.TRANSITION_END, function () {
+ $__default['default'](nextElement).removeClass(directionalClassName + " " + orderClassName).addClass(CLASS_NAME_ACTIVE$1);
+ $__default['default'](activeElement).removeClass(CLASS_NAME_ACTIVE$1 + " " + orderClassName + " " + directionalClassName);
+ _this4._isSliding = false;
+ setTimeout(function () {
+ return $__default['default'](_this4._element).trigger(slidEvent);
+ }, 0);
+ }).emulateTransitionEnd(transitionDuration);
+ } else {
+ $__default['default'](activeElement).removeClass(CLASS_NAME_ACTIVE$1);
+ $__default['default'](nextElement).addClass(CLASS_NAME_ACTIVE$1);
+ this._isSliding = false;
+ $__default['default'](this._element).trigger(slidEvent);
+ }
+
+ if (isCycling) {
+ this.cycle();
+ }
+ } // Static
+ ;
+
+ Carousel._jQueryInterface = function _jQueryInterface(config) {
+ return this.each(function () {
+ var data = $__default['default'](this).data(DATA_KEY$2);
+
+ var _config = _extends({}, Default, $__default['default'](this).data());
+
+ if (typeof config === 'object') {
+ _config = _extends({}, _config, config);
+ }
+
+ var action = typeof config === 'string' ? config : _config.slide;
+
+ if (!data) {
+ data = new Carousel(this, _config);
+ $__default['default'](this).data(DATA_KEY$2, data);
+ }
+
+ if (typeof config === 'number') {
+ data.to(config);
+ } else if (typeof action === 'string') {
+ if (typeof data[action] === 'undefined') {
+ throw new TypeError("No method named \"" + action + "\"");
+ }
+
+ data[action]();
+ } else if (_config.interval && _config.ride) {
+ data.pause();
+ data.cycle();
+ }
+ });
+ };
+
+ Carousel._dataApiClickHandler = function _dataApiClickHandler(event) {
+ var selector = Util.getSelectorFromElement(this);
+
+ if (!selector) {
+ return;
+ }
+
+ var target = $__default['default'](selector)[0];
+
+ if (!target || !$__default['default'](target).hasClass(CLASS_NAME_CAROUSEL)) {
+ return;
+ }
+
+ var config = _extends({}, $__default['default'](target).data(), $__default['default'](this).data());
+
+ var slideIndex = this.getAttribute('data-slide-to');
+
+ if (slideIndex) {
+ config.interval = false;
+ }
+
+ Carousel._jQueryInterface.call($__default['default'](target), config);
+
+ if (slideIndex) {
+ $__default['default'](target).data(DATA_KEY$2).to(slideIndex);
+ }
+
+ event.preventDefault();
+ };
+
+ _createClass(Carousel, null, [{
+ key: "VERSION",
+ get: function get() {
+ return VERSION$2;
+ }
+ }, {
+ key: "Default",
+ get: function get() {
+ return Default;
+ }
+ }]);
+
+ return Carousel;
+ }();
+ /**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+
+ $__default['default'](document).on(EVENT_CLICK_DATA_API$2, SELECTOR_DATA_SLIDE, Carousel._dataApiClickHandler);
+ $__default['default'](window).on(EVENT_LOAD_DATA_API$1, function () {
+ var carousels = [].slice.call(document.querySelectorAll(SELECTOR_DATA_RIDE));
+
+ for (var i = 0, len = carousels.length; i < len; i++) {
+ var $carousel = $__default['default'](carousels[i]);
+
+ Carousel._jQueryInterface.call($carousel, $carousel.data());
+ }
+ });
+ /**
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ */
+
+ $__default['default'].fn[NAME$2] = Carousel._jQueryInterface;
+ $__default['default'].fn[NAME$2].Constructor = Carousel;
+
+ $__default['default'].fn[NAME$2].noConflict = function () {
+ $__default['default'].fn[NAME$2] = JQUERY_NO_CONFLICT$2;
+ return Carousel._jQueryInterface;
+ };
+
+ /**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+ var NAME$3 = 'collapse';
+ var VERSION$3 = '4.5.3';
+ var DATA_KEY$3 = 'bs.collapse';
+ var EVENT_KEY$3 = "." + DATA_KEY$3;
+ var DATA_API_KEY$3 = '.data-api';
+ var JQUERY_NO_CONFLICT$3 = $__default['default'].fn[NAME$3];
+ var Default$1 = {
+ toggle: true,
+ parent: ''
+ };
+ var DefaultType$1 = {
+ toggle: 'boolean',
+ parent: '(string|element)'
+ };
+ var EVENT_SHOW = "show" + EVENT_KEY$3;
+ var EVENT_SHOWN = "shown" + EVENT_KEY$3;
+ var EVENT_HIDE = "hide" + EVENT_KEY$3;
+ var EVENT_HIDDEN = "hidden" + EVENT_KEY$3;
+ var EVENT_CLICK_DATA_API$3 = "click" + EVENT_KEY$3 + DATA_API_KEY$3;
+ var CLASS_NAME_SHOW$1 = 'show';
+ var CLASS_NAME_COLLAPSE = 'collapse';
+ var CLASS_NAME_COLLAPSING = 'collapsing';
+ var CLASS_NAME_COLLAPSED = 'collapsed';
+ var DIMENSION_WIDTH = 'width';
+ var DIMENSION_HEIGHT = 'height';
+ var SELECTOR_ACTIVES = '.show, .collapsing';
+ var SELECTOR_DATA_TOGGLE$1 = '[data-toggle="collapse"]';
+ /**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+ var Collapse = /*#__PURE__*/function () {
+ function Collapse(element, config) {
+ this._isTransitioning = false;
+ this._element = element;
+ this._config = this._getConfig(config);
+ this._triggerArray = [].slice.call(document.querySelectorAll("[data-toggle=\"collapse\"][href=\"#" + element.id + "\"]," + ("[data-toggle=\"collapse\"][data-target=\"#" + element.id + "\"]")));
+ var toggleList = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE$1));
+
+ for (var i = 0, len = toggleList.length; i < len; i++) {
+ var elem = toggleList[i];
+ var selector = Util.getSelectorFromElement(elem);
+ var filterElement = [].slice.call(document.querySelectorAll(selector)).filter(function (foundElem) {
+ return foundElem === element;
+ });
+
+ if (selector !== null && filterElement.length > 0) {
+ this._selector = selector;
+
+ this._triggerArray.push(elem);
+ }
+ }
+
+ this._parent = this._config.parent ? this._getParent() : null;
+
+ if (!this._config.parent) {
+ this._addAriaAndCollapsedClass(this._element, this._triggerArray);
+ }
+
+ if (this._config.toggle) {
+ this.toggle();
+ }
+ } // Getters
+
+
+ var _proto = Collapse.prototype;
+
+ // Public
+ _proto.toggle = function toggle() {
+ if ($__default['default'](this._element).hasClass(CLASS_NAME_SHOW$1)) {
+ this.hide();
+ } else {
+ this.show();
+ }
+ };
+
+ _proto.show = function show() {
+ var _this = this;
+
+ if (this._isTransitioning || $__default['default'](this._element).hasClass(CLASS_NAME_SHOW$1)) {
+ return;
+ }
+
+ var actives;
+ var activesData;
+
+ if (this._parent) {
+ actives = [].slice.call(this._parent.querySelectorAll(SELECTOR_ACTIVES)).filter(function (elem) {
+ if (typeof _this._config.parent === 'string') {
+ return elem.getAttribute('data-parent') === _this._config.parent;
+ }
+
+ return elem.classList.contains(CLASS_NAME_COLLAPSE);
+ });
+
+ if (actives.length === 0) {
+ actives = null;
+ }
+ }
+
+ if (actives) {
+ activesData = $__default['default'](actives).not(this._selector).data(DATA_KEY$3);
+
+ if (activesData && activesData._isTransitioning) {
+ return;
+ }
+ }
+
+ var startEvent = $__default['default'].Event(EVENT_SHOW);
+ $__default['default'](this._element).trigger(startEvent);
+
+ if (startEvent.isDefaultPrevented()) {
+ return;
+ }
+
+ if (actives) {
+ Collapse._jQueryInterface.call($__default['default'](actives).not(this._selector), 'hide');
+
+ if (!activesData) {
+ $__default['default'](actives).data(DATA_KEY$3, null);
+ }
+ }
+
+ var dimension = this._getDimension();
+
+ $__default['default'](this._element).removeClass(CLASS_NAME_COLLAPSE).addClass(CLASS_NAME_COLLAPSING);
+ this._element.style[dimension] = 0;
+
+ if (this._triggerArray.length) {
+ $__default['default'](this._triggerArray).removeClass(CLASS_NAME_COLLAPSED).attr('aria-expanded', true);
+ }
+
+ this.setTransitioning(true);
+
+ var complete = function complete() {
+ $__default['default'](_this._element).removeClass(CLASS_NAME_COLLAPSING).addClass(CLASS_NAME_COLLAPSE + " " + CLASS_NAME_SHOW$1);
+ _this._element.style[dimension] = '';
+
+ _this.setTransitioning(false);
+
+ $__default['default'](_this._element).trigger(EVENT_SHOWN);
+ };
+
+ var capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1);
+ var scrollSize = "scroll" + capitalizedDimension;
+ var transitionDuration = Util.getTransitionDurationFromElement(this._element);
+ $__default['default'](this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
+ this._element.style[dimension] = this._element[scrollSize] + "px";
+ };
+
+ _proto.hide = function hide() {
+ var _this2 = this;
+
+ if (this._isTransitioning || !$__default['default'](this._element).hasClass(CLASS_NAME_SHOW$1)) {
+ return;
+ }
+
+ var startEvent = $__default['default'].Event(EVENT_HIDE);
+ $__default['default'](this._element).trigger(startEvent);
+
+ if (startEvent.isDefaultPrevented()) {
+ return;
+ }
+
+ var dimension = this._getDimension();
+
+ this._element.style[dimension] = this._element.getBoundingClientRect()[dimension] + "px";
+ Util.reflow(this._element);
+ $__default['default'](this._element).addClass(CLASS_NAME_COLLAPSING).removeClass(CLASS_NAME_COLLAPSE + " " + CLASS_NAME_SHOW$1);
+ var triggerArrayLength = this._triggerArray.length;
+
+ if (triggerArrayLength > 0) {
+ for (var i = 0; i < triggerArrayLength; i++) {
+ var trigger = this._triggerArray[i];
+ var selector = Util.getSelectorFromElement(trigger);
+
+ if (selector !== null) {
+ var $elem = $__default['default']([].slice.call(document.querySelectorAll(selector)));
+
+ if (!$elem.hasClass(CLASS_NAME_SHOW$1)) {
+ $__default['default'](trigger).addClass(CLASS_NAME_COLLAPSED).attr('aria-expanded', false);
+ }
+ }
+ }
+ }
+
+ this.setTransitioning(true);
+
+ var complete = function complete() {
+ _this2.setTransitioning(false);
+
+ $__default['default'](_this2._element).removeClass(CLASS_NAME_COLLAPSING).addClass(CLASS_NAME_COLLAPSE).trigger(EVENT_HIDDEN);
+ };
+
+ this._element.style[dimension] = '';
+ var transitionDuration = Util.getTransitionDurationFromElement(this._element);
+ $__default['default'](this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
+ };
+
+ _proto.setTransitioning = function setTransitioning(isTransitioning) {
+ this._isTransitioning = isTransitioning;
+ };
+
+ _proto.dispose = function dispose() {
+ $__default['default'].removeData(this._element, DATA_KEY$3);
+ this._config = null;
+ this._parent = null;
+ this._element = null;
+ this._triggerArray = null;
+ this._isTransitioning = null;
+ } // Private
+ ;
+
+ _proto._getConfig = function _getConfig(config) {
+ config = _extends({}, Default$1, config);
+ config.toggle = Boolean(config.toggle); // Coerce string values
+
+ Util.typeCheckConfig(NAME$3, config, DefaultType$1);
+ return config;
+ };
+
+ _proto._getDimension = function _getDimension() {
+ var hasWidth = $__default['default'](this._element).hasClass(DIMENSION_WIDTH);
+ return hasWidth ? DIMENSION_WIDTH : DIMENSION_HEIGHT;
+ };
+
+ _proto._getParent = function _getParent() {
+ var _this3 = this;
+
+ var parent;
+
+ if (Util.isElement(this._config.parent)) {
+ parent = this._config.parent; // It's a jQuery object
+
+ if (typeof this._config.parent.jquery !== 'undefined') {
+ parent = this._config.parent[0];
+ }
+ } else {
+ parent = document.querySelector(this._config.parent);
+ }
+
+ var selector = "[data-toggle=\"collapse\"][data-parent=\"" + this._config.parent + "\"]";
+ var children = [].slice.call(parent.querySelectorAll(selector));
+ $__default['default'](children).each(function (i, element) {
+ _this3._addAriaAndCollapsedClass(Collapse._getTargetFromElement(element), [element]);
+ });
+ return parent;
+ };
+
+ _proto._addAriaAndCollapsedClass = function _addAriaAndCollapsedClass(element, triggerArray) {
+ var isOpen = $__default['default'](element).hasClass(CLASS_NAME_SHOW$1);
+
+ if (triggerArray.length) {
+ $__default['default'](triggerArray).toggleClass(CLASS_NAME_COLLAPSED, !isOpen).attr('aria-expanded', isOpen);
+ }
+ } // Static
+ ;
+
+ Collapse._getTargetFromElement = function _getTargetFromElement(element) {
+ var selector = Util.getSelectorFromElement(element);
+ return selector ? document.querySelector(selector) : null;
+ };
+
+ Collapse._jQueryInterface = function _jQueryInterface(config) {
+ return this.each(function () {
+ var $element = $__default['default'](this);
+ var data = $element.data(DATA_KEY$3);
+
+ var _config = _extends({}, Default$1, $element.data(), typeof config === 'object' && config ? config : {});
+
+ if (!data && _config.toggle && typeof config === 'string' && /show|hide/.test(config)) {
+ _config.toggle = false;
+ }
+
+ if (!data) {
+ data = new Collapse(this, _config);
+ $element.data(DATA_KEY$3, data);
+ }
+
+ if (typeof config === 'string') {
+ if (typeof data[config] === 'undefined') {
+ throw new TypeError("No method named \"" + config + "\"");
+ }
+
+ data[config]();
+ }
+ });
+ };
+
+ _createClass(Collapse, null, [{
+ key: "VERSION",
+ get: function get() {
+ return VERSION$3;
+ }
+ }, {
+ key: "Default",
+ get: function get() {
+ return Default$1;
+ }
+ }]);
+
+ return Collapse;
+ }();
+ /**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+
+ $__default['default'](document).on(EVENT_CLICK_DATA_API$3, SELECTOR_DATA_TOGGLE$1, function (event) {
+ // preventDefault only for elements (which change the URL) not inside the collapsible element
+ if (event.currentTarget.tagName === 'A') {
+ event.preventDefault();
+ }
+
+ var $trigger = $__default['default'](this);
+ var selector = Util.getSelectorFromElement(this);
+ var selectors = [].slice.call(document.querySelectorAll(selector));
+ $__default['default'](selectors).each(function () {
+ var $target = $__default['default'](this);
+ var data = $target.data(DATA_KEY$3);
+ var config = data ? 'toggle' : $trigger.data();
+
+ Collapse._jQueryInterface.call($target, config);
+ });
+ });
+ /**
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ */
+
+ $__default['default'].fn[NAME$3] = Collapse._jQueryInterface;
+ $__default['default'].fn[NAME$3].Constructor = Collapse;
+
+ $__default['default'].fn[NAME$3].noConflict = function () {
+ $__default['default'].fn[NAME$3] = JQUERY_NO_CONFLICT$3;
+ return Collapse._jQueryInterface;
+ };
+
+ /**!
+ * @fileOverview Kickass library to create and place poppers near their reference elements.
+ * @version 1.16.1
+ * @license
+ * Copyright (c) 2016 Federico Zivolo and contributors
+ *
+ * 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.
+ */
+ var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined' && typeof navigator !== 'undefined';
+
+ var timeoutDuration = function () {
+ var longerTimeoutBrowsers = ['Edge', 'Trident', 'Firefox'];
+ for (var i = 0; i < longerTimeoutBrowsers.length; i += 1) {
+ if (isBrowser && navigator.userAgent.indexOf(longerTimeoutBrowsers[i]) >= 0) {
+ return 1;
+ }
+ }
+ return 0;
+ }();
+
+ function microtaskDebounce(fn) {
+ var called = false;
+ return function () {
+ if (called) {
+ return;
+ }
+ called = true;
+ window.Promise.resolve().then(function () {
+ called = false;
+ fn();
+ });
+ };
+ }
+
+ function taskDebounce(fn) {
+ var scheduled = false;
+ return function () {
+ if (!scheduled) {
+ scheduled = true;
+ setTimeout(function () {
+ scheduled = false;
+ fn();
+ }, timeoutDuration);
+ }
+ };
+ }
+
+ var supportsMicroTasks = isBrowser && window.Promise;
+
+ /**
+ * Create a debounced version of a method, that's asynchronously deferred
+ * but called in the minimum time possible.
+ *
+ * @method
+ * @memberof Popper.Utils
+ * @argument {Function} fn
+ * @returns {Function}
+ */
+ var debounce = supportsMicroTasks ? microtaskDebounce : taskDebounce;
+
+ /**
+ * Check if the given variable is a function
+ * @method
+ * @memberof Popper.Utils
+ * @argument {Any} functionToCheck - variable to check
+ * @returns {Boolean} answer to: is a function?
+ */
+ function isFunction(functionToCheck) {
+ var getType = {};
+ return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
+ }
+
+ /**
+ * Get CSS computed property of the given element
+ * @method
+ * @memberof Popper.Utils
+ * @argument {Eement} element
+ * @argument {String} property
+ */
+ function getStyleComputedProperty(element, property) {
+ if (element.nodeType !== 1) {
+ return [];
+ }
+ // NOTE: 1 DOM access here
+ var window = element.ownerDocument.defaultView;
+ var css = window.getComputedStyle(element, null);
+ return property ? css[property] : css;
+ }
+
+ /**
+ * Returns the parentNode or the host of the element
+ * @method
+ * @memberof Popper.Utils
+ * @argument {Element} element
+ * @returns {Element} parent
+ */
+ function getParentNode(element) {
+ if (element.nodeName === 'HTML') {
+ return element;
+ }
+ return element.parentNode || element.host;
+ }
+
+ /**
+ * Returns the scrolling parent of the given element
+ * @method
+ * @memberof Popper.Utils
+ * @argument {Element} element
+ * @returns {Element} scroll parent
+ */
+ function getScrollParent(element) {
+ // Return body, `getScroll` will take care to get the correct `scrollTop` from it
+ if (!element) {
+ return document.body;
+ }
+
+ switch (element.nodeName) {
+ case 'HTML':
+ case 'BODY':
+ return element.ownerDocument.body;
+ case '#document':
+ return element.body;
+ }
+
+ // Firefox want us to check `-x` and `-y` variations as well
+
+ var _getStyleComputedProp = getStyleComputedProperty(element),
+ overflow = _getStyleComputedProp.overflow,
+ overflowX = _getStyleComputedProp.overflowX,
+ overflowY = _getStyleComputedProp.overflowY;
+
+ if (/(auto|scroll|overlay)/.test(overflow + overflowY + overflowX)) {
+ return element;
+ }
+
+ return getScrollParent(getParentNode(element));
+ }
+
+ /**
+ * Returns the reference node of the reference object, or the reference object itself.
+ * @method
+ * @memberof Popper.Utils
+ * @param {Element|Object} reference - the reference element (the popper will be relative to this)
+ * @returns {Element} parent
+ */
+ function getReferenceNode(reference) {
+ return reference && reference.referenceNode ? reference.referenceNode : reference;
+ }
+
+ var isIE11 = isBrowser && !!(window.MSInputMethodContext && document.documentMode);
+ var isIE10 = isBrowser && /MSIE 10/.test(navigator.userAgent);
+
+ /**
+ * Determines if the browser is Internet Explorer
+ * @method
+ * @memberof Popper.Utils
+ * @param {Number} version to check
+ * @returns {Boolean} isIE
+ */
+ function isIE(version) {
+ if (version === 11) {
+ return isIE11;
+ }
+ if (version === 10) {
+ return isIE10;
+ }
+ return isIE11 || isIE10;
+ }
+
+ /**
+ * Returns the offset parent of the given element
+ * @method
+ * @memberof Popper.Utils
+ * @argument {Element} element
+ * @returns {Element} offset parent
+ */
+ function getOffsetParent(element) {
+ if (!element) {
+ return document.documentElement;
+ }
+
+ var noOffsetParent = isIE(10) ? document.body : null;
+
+ // NOTE: 1 DOM access here
+ var offsetParent = element.offsetParent || null;
+ // Skip hidden elements which don't have an offsetParent
+ while (offsetParent === noOffsetParent && element.nextElementSibling) {
+ offsetParent = (element = element.nextElementSibling).offsetParent;
+ }
+
+ var nodeName = offsetParent && offsetParent.nodeName;
+
+ if (!nodeName || nodeName === 'BODY' || nodeName === 'HTML') {
+ return element ? element.ownerDocument.documentElement : document.documentElement;
+ }
+
+ // .offsetParent will return the closest TH, TD or TABLE in case
+ // no offsetParent is present, I hate this job...
+ if (['TH', 'TD', 'TABLE'].indexOf(offsetParent.nodeName) !== -1 && getStyleComputedProperty(offsetParent, 'position') === 'static') {
+ return getOffsetParent(offsetParent);
+ }
+
+ return offsetParent;
+ }
+
+ function isOffsetContainer(element) {
+ var nodeName = element.nodeName;
+
+ if (nodeName === 'BODY') {
+ return false;
+ }
+ return nodeName === 'HTML' || getOffsetParent(element.firstElementChild) === element;
+ }
+
+ /**
+ * Finds the root node (document, shadowDOM root) of the given element
+ * @method
+ * @memberof Popper.Utils
+ * @argument {Element} node
+ * @returns {Element} root node
+ */
+ function getRoot(node) {
+ if (node.parentNode !== null) {
+ return getRoot(node.parentNode);
+ }
+
+ return node;
+ }
+
+ /**
+ * Finds the offset parent common to the two provided nodes
+ * @method
+ * @memberof Popper.Utils
+ * @argument {Element} element1
+ * @argument {Element} element2
+ * @returns {Element} common offset parent
+ */
+ function findCommonOffsetParent(element1, element2) {
+ // This check is needed to avoid errors in case one of the elements isn't defined for any reason
+ if (!element1 || !element1.nodeType || !element2 || !element2.nodeType) {
+ return document.documentElement;
+ }
+
+ // Here we make sure to give as "start" the element that comes first in the DOM
+ var order = element1.compareDocumentPosition(element2) & Node.DOCUMENT_POSITION_FOLLOWING;
+ var start = order ? element1 : element2;
+ var end = order ? element2 : element1;
+
+ // Get common ancestor container
+ var range = document.createRange();
+ range.setStart(start, 0);
+ range.setEnd(end, 0);
+ var commonAncestorContainer = range.commonAncestorContainer;
+
+ // Both nodes are inside #document
+
+ if (element1 !== commonAncestorContainer && element2 !== commonAncestorContainer || start.contains(end)) {
+ if (isOffsetContainer(commonAncestorContainer)) {
+ return commonAncestorContainer;
+ }
+
+ return getOffsetParent(commonAncestorContainer);
+ }
+
+ // one of the nodes is inside shadowDOM, find which one
+ var element1root = getRoot(element1);
+ if (element1root.host) {
+ return findCommonOffsetParent(element1root.host, element2);
+ } else {
+ return findCommonOffsetParent(element1, getRoot(element2).host);
+ }
+ }
+
+ /**
+ * Gets the scroll value of the given element in the given side (top and left)
+ * @method
+ * @memberof Popper.Utils
+ * @argument {Element} element
+ * @argument {String} side `top` or `left`
+ * @returns {number} amount of scrolled pixels
+ */
+ function getScroll(element) {
+ var side = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'top';
+
+ var upperSide = side === 'top' ? 'scrollTop' : 'scrollLeft';
+ var nodeName = element.nodeName;
+
+ if (nodeName === 'BODY' || nodeName === 'HTML') {
+ var html = element.ownerDocument.documentElement;
+ var scrollingElement = element.ownerDocument.scrollingElement || html;
+ return scrollingElement[upperSide];
+ }
+
+ return element[upperSide];
+ }
+
+ /*
+ * Sum or subtract the element scroll values (left and top) from a given rect object
+ * @method
+ * @memberof Popper.Utils
+ * @param {Object} rect - Rect object you want to change
+ * @param {HTMLElement} element - The element from the function reads the scroll values
+ * @param {Boolean} subtract - set to true if you want to subtract the scroll values
+ * @return {Object} rect - The modifier rect object
+ */
+ function includeScroll(rect, element) {
+ var subtract = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
+
+ var scrollTop = getScroll(element, 'top');
+ var scrollLeft = getScroll(element, 'left');
+ var modifier = subtract ? -1 : 1;
+ rect.top += scrollTop * modifier;
+ rect.bottom += scrollTop * modifier;
+ rect.left += scrollLeft * modifier;
+ rect.right += scrollLeft * modifier;
+ return rect;
+ }
+
+ /*
+ * Helper to detect borders of a given element
+ * @method
+ * @memberof Popper.Utils
+ * @param {CSSStyleDeclaration} styles
+ * Result of `getStyleComputedProperty` on the given element
+ * @param {String} axis - `x` or `y`
+ * @return {number} borders - The borders size of the given axis
+ */
+
+ function getBordersSize(styles, axis) {
+ var sideA = axis === 'x' ? 'Left' : 'Top';
+ var sideB = sideA === 'Left' ? 'Right' : 'Bottom';
+
+ return parseFloat(styles['border' + sideA + 'Width']) + parseFloat(styles['border' + sideB + 'Width']);
+ }
+
+ function getSize(axis, body, html, computedStyle) {
+ return Math.max(body['offset' + axis], body['scroll' + axis], html['client' + axis], html['offset' + axis], html['scroll' + axis], isIE(10) ? parseInt(html['offset' + axis]) + parseInt(computedStyle['margin' + (axis === 'Height' ? 'Top' : 'Left')]) + parseInt(computedStyle['margin' + (axis === 'Height' ? 'Bottom' : 'Right')]) : 0);
+ }
+
+ function getWindowSizes(document) {
+ var body = document.body;
+ var html = document.documentElement;
+ var computedStyle = isIE(10) && getComputedStyle(html);
+
+ return {
+ height: getSize('Height', body, html, computedStyle),
+ width: getSize('Width', body, html, computedStyle)
+ };
+ }
+
+ var classCallCheck = function (instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError("Cannot call a class as a function");
+ }
+ };
+
+ var createClass = function () {
+ function defineProperties(target, props) {
+ for (var i = 0; i < props.length; i++) {
+ var descriptor = props[i];
+ descriptor.enumerable = descriptor.enumerable || false;
+ descriptor.configurable = true;
+ if ("value" in descriptor) descriptor.writable = true;
+ Object.defineProperty(target, descriptor.key, descriptor);
+ }
+ }
+
+ return function (Constructor, protoProps, staticProps) {
+ if (protoProps) defineProperties(Constructor.prototype, protoProps);
+ if (staticProps) defineProperties(Constructor, staticProps);
+ return Constructor;
+ };
+ }();
+
+
+
+
+
+ var defineProperty = function (obj, key, value) {
+ if (key in obj) {
+ Object.defineProperty(obj, key, {
+ value: value,
+ enumerable: true,
+ configurable: true,
+ writable: true
+ });
+ } else {
+ obj[key] = value;
+ }
+
+ return obj;
+ };
+
+ var _extends$1 = Object.assign || function (target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i];
+
+ for (var key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+ }
+
+ return target;
+ };
+
+ /**
+ * Given element offsets, generate an output similar to getBoundingClientRect
+ * @method
+ * @memberof Popper.Utils
+ * @argument {Object} offsets
+ * @returns {Object} ClientRect like output
+ */
+ function getClientRect(offsets) {
+ return _extends$1({}, offsets, {
+ right: offsets.left + offsets.width,
+ bottom: offsets.top + offsets.height
+ });
+ }
+
+ /**
+ * Get bounding client rect of given element
+ * @method
+ * @memberof Popper.Utils
+ * @param {HTMLElement} element
+ * @return {Object} client rect
+ */
+ function getBoundingClientRect(element) {
+ var rect = {};
+
+ // IE10 10 FIX: Please, don't ask, the element isn't
+ // considered in DOM in some circumstances...
+ // This isn't reproducible in IE10 compatibility mode of IE11
+ try {
+ if (isIE(10)) {
+ rect = element.getBoundingClientRect();
+ var scrollTop = getScroll(element, 'top');
+ var scrollLeft = getScroll(element, 'left');
+ rect.top += scrollTop;
+ rect.left += scrollLeft;
+ rect.bottom += scrollTop;
+ rect.right += scrollLeft;
+ } else {
+ rect = element.getBoundingClientRect();
+ }
+ } catch (e) {}
+
+ var result = {
+ left: rect.left,
+ top: rect.top,
+ width: rect.right - rect.left,
+ height: rect.bottom - rect.top
+ };
+
+ // subtract scrollbar size from sizes
+ var sizes = element.nodeName === 'HTML' ? getWindowSizes(element.ownerDocument) : {};
+ var width = sizes.width || element.clientWidth || result.width;
+ var height = sizes.height || element.clientHeight || result.height;
+
+ var horizScrollbar = element.offsetWidth - width;
+ var vertScrollbar = element.offsetHeight - height;
+
+ // if an hypothetical scrollbar is detected, we must be sure it's not a `border`
+ // we make this check conditional for performance reasons
+ if (horizScrollbar || vertScrollbar) {
+ var styles = getStyleComputedProperty(element);
+ horizScrollbar -= getBordersSize(styles, 'x');
+ vertScrollbar -= getBordersSize(styles, 'y');
+
+ result.width -= horizScrollbar;
+ result.height -= vertScrollbar;
+ }
+
+ return getClientRect(result);
+ }
+
+ function getOffsetRectRelativeToArbitraryNode(children, parent) {
+ var fixedPosition = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
+
+ var isIE10 = isIE(10);
+ var isHTML = parent.nodeName === 'HTML';
+ var childrenRect = getBoundingClientRect(children);
+ var parentRect = getBoundingClientRect(parent);
+ var scrollParent = getScrollParent(children);
+
+ var styles = getStyleComputedProperty(parent);
+ var borderTopWidth = parseFloat(styles.borderTopWidth);
+ var borderLeftWidth = parseFloat(styles.borderLeftWidth);
+
+ // In cases where the parent is fixed, we must ignore negative scroll in offset calc
+ if (fixedPosition && isHTML) {
+ parentRect.top = Math.max(parentRect.top, 0);
+ parentRect.left = Math.max(parentRect.left, 0);
+ }
+ var offsets = getClientRect({
+ top: childrenRect.top - parentRect.top - borderTopWidth,
+ left: childrenRect.left - parentRect.left - borderLeftWidth,
+ width: childrenRect.width,
+ height: childrenRect.height
+ });
+ offsets.marginTop = 0;
+ offsets.marginLeft = 0;
+
+ // Subtract margins of documentElement in case it's being used as parent
+ // we do this only on HTML because it's the only element that behaves
+ // differently when margins are applied to it. The margins are included in
+ // the box of the documentElement, in the other cases not.
+ if (!isIE10 && isHTML) {
+ var marginTop = parseFloat(styles.marginTop);
+ var marginLeft = parseFloat(styles.marginLeft);
+
+ offsets.top -= borderTopWidth - marginTop;
+ offsets.bottom -= borderTopWidth - marginTop;
+ offsets.left -= borderLeftWidth - marginLeft;
+ offsets.right -= borderLeftWidth - marginLeft;
+
+ // Attach marginTop and marginLeft because in some circumstances we may need them
+ offsets.marginTop = marginTop;
+ offsets.marginLeft = marginLeft;
+ }
+
+ if (isIE10 && !fixedPosition ? parent.contains(scrollParent) : parent === scrollParent && scrollParent.nodeName !== 'BODY') {
+ offsets = includeScroll(offsets, parent);
+ }
+
+ return offsets;
+ }
+
+ function getViewportOffsetRectRelativeToArtbitraryNode(element) {
+ var excludeScroll = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
+
+ var html = element.ownerDocument.documentElement;
+ var relativeOffset = getOffsetRectRelativeToArbitraryNode(element, html);
+ var width = Math.max(html.clientWidth, window.innerWidth || 0);
+ var height = Math.max(html.clientHeight, window.innerHeight || 0);
+
+ var scrollTop = !excludeScroll ? getScroll(html) : 0;
+ var scrollLeft = !excludeScroll ? getScroll(html, 'left') : 0;
+
+ var offset = {
+ top: scrollTop - relativeOffset.top + relativeOffset.marginTop,
+ left: scrollLeft - relativeOffset.left + relativeOffset.marginLeft,
+ width: width,
+ height: height
+ };
+
+ return getClientRect(offset);
+ }
+
+ /**
+ * Check if the given element is fixed or is inside a fixed parent
+ * @method
+ * @memberof Popper.Utils
+ * @argument {Element} element
+ * @argument {Element} customContainer
+ * @returns {Boolean} answer to "isFixed?"
+ */
+ function isFixed(element) {
+ var nodeName = element.nodeName;
+ if (nodeName === 'BODY' || nodeName === 'HTML') {
+ return false;
+ }
+ if (getStyleComputedProperty(element, 'position') === 'fixed') {
+ return true;
+ }
+ var parentNode = getParentNode(element);
+ if (!parentNode) {
+ return false;
+ }
+ return isFixed(parentNode);
+ }
+
+ /**
+ * Finds the first parent of an element that has a transformed property defined
+ * @method
+ * @memberof Popper.Utils
+ * @argument {Element} element
+ * @returns {Element} first transformed parent or documentElement
+ */
+
+ function getFixedPositionOffsetParent(element) {
+ // This check is needed to avoid errors in case one of the elements isn't defined for any reason
+ if (!element || !element.parentElement || isIE()) {
+ return document.documentElement;
+ }
+ var el = element.parentElement;
+ while (el && getStyleComputedProperty(el, 'transform') === 'none') {
+ el = el.parentElement;
+ }
+ return el || document.documentElement;
+ }
+
+ /**
+ * Computed the boundaries limits and return them
+ * @method
+ * @memberof Popper.Utils
+ * @param {HTMLElement} popper
+ * @param {HTMLElement} reference
+ * @param {number} padding
+ * @param {HTMLElement} boundariesElement - Element used to define the boundaries
+ * @param {Boolean} fixedPosition - Is in fixed position mode
+ * @returns {Object} Coordinates of the boundaries
+ */
+ function getBoundaries(popper, reference, padding, boundariesElement) {
+ var fixedPosition = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
+
+ // NOTE: 1 DOM access here
+
+ var boundaries = { top: 0, left: 0 };
+ var offsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, getReferenceNode(reference));
+
+ // Handle viewport case
+ if (boundariesElement === 'viewport') {
+ boundaries = getViewportOffsetRectRelativeToArtbitraryNode(offsetParent, fixedPosition);
+ } else {
+ // Handle other cases based on DOM element used as boundaries
+ var boundariesNode = void 0;
+ if (boundariesElement === 'scrollParent') {
+ boundariesNode = getScrollParent(getParentNode(reference));
+ if (boundariesNode.nodeName === 'BODY') {
+ boundariesNode = popper.ownerDocument.documentElement;
+ }
+ } else if (boundariesElement === 'window') {
+ boundariesNode = popper.ownerDocument.documentElement;
+ } else {
+ boundariesNode = boundariesElement;
+ }
+
+ var offsets = getOffsetRectRelativeToArbitraryNode(boundariesNode, offsetParent, fixedPosition);
+
+ // In case of HTML, we need a different computation
+ if (boundariesNode.nodeName === 'HTML' && !isFixed(offsetParent)) {
+ var _getWindowSizes = getWindowSizes(popper.ownerDocument),
+ height = _getWindowSizes.height,
+ width = _getWindowSizes.width;
+
+ boundaries.top += offsets.top - offsets.marginTop;
+ boundaries.bottom = height + offsets.top;
+ boundaries.left += offsets.left - offsets.marginLeft;
+ boundaries.right = width + offsets.left;
+ } else {
+ // for all the other DOM elements, this one is good
+ boundaries = offsets;
+ }
+ }
+
+ // Add paddings
+ padding = padding || 0;
+ var isPaddingNumber = typeof padding === 'number';
+ boundaries.left += isPaddingNumber ? padding : padding.left || 0;
+ boundaries.top += isPaddingNumber ? padding : padding.top || 0;
+ boundaries.right -= isPaddingNumber ? padding : padding.right || 0;
+ boundaries.bottom -= isPaddingNumber ? padding : padding.bottom || 0;
+
+ return boundaries;
+ }
+
+ function getArea(_ref) {
+ var width = _ref.width,
+ height = _ref.height;
+
+ return width * height;
+ }
+
+ /**
+ * Utility used to transform the `auto` placement to the placement with more
+ * available space.
+ * @method
+ * @memberof Popper.Utils
+ * @argument {Object} data - The data object generated by update method
+ * @argument {Object} options - Modifiers configuration and options
+ * @returns {Object} The data object, properly modified
+ */
+ function computeAutoPlacement(placement, refRect, popper, reference, boundariesElement) {
+ var padding = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 0;
+
+ if (placement.indexOf('auto') === -1) {
+ return placement;
+ }
+
+ var boundaries = getBoundaries(popper, reference, padding, boundariesElement);
+
+ var rects = {
+ top: {
+ width: boundaries.width,
+ height: refRect.top - boundaries.top
+ },
+ right: {
+ width: boundaries.right - refRect.right,
+ height: boundaries.height
+ },
+ bottom: {
+ width: boundaries.width,
+ height: boundaries.bottom - refRect.bottom
+ },
+ left: {
+ width: refRect.left - boundaries.left,
+ height: boundaries.height
+ }
+ };
+
+ var sortedAreas = Object.keys(rects).map(function (key) {
+ return _extends$1({
+ key: key
+ }, rects[key], {
+ area: getArea(rects[key])
+ });
+ }).sort(function (a, b) {
+ return b.area - a.area;
+ });
+
+ var filteredAreas = sortedAreas.filter(function (_ref2) {
+ var width = _ref2.width,
+ height = _ref2.height;
+ return width >= popper.clientWidth && height >= popper.clientHeight;
+ });
+
+ var computedPlacement = filteredAreas.length > 0 ? filteredAreas[0].key : sortedAreas[0].key;
+
+ var variation = placement.split('-')[1];
+
+ return computedPlacement + (variation ? '-' + variation : '');
+ }
+
+ /**
+ * Get offsets to the reference element
+ * @method
+ * @memberof Popper.Utils
+ * @param {Object} state
+ * @param {Element} popper - the popper element
+ * @param {Element} reference - the reference element (the popper will be relative to this)
+ * @param {Element} fixedPosition - is in fixed position mode
+ * @returns {Object} An object containing the offsets which will be applied to the popper
+ */
+ function getReferenceOffsets(state, popper, reference) {
+ var fixedPosition = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
+
+ var commonOffsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, getReferenceNode(reference));
+ return getOffsetRectRelativeToArbitraryNode(reference, commonOffsetParent, fixedPosition);
+ }
+
+ /**
+ * Get the outer sizes of the given element (offset size + margins)
+ * @method
+ * @memberof Popper.Utils
+ * @argument {Element} element
+ * @returns {Object} object containing width and height properties
+ */
+ function getOuterSizes(element) {
+ var window = element.ownerDocument.defaultView;
+ var styles = window.getComputedStyle(element);
+ var x = parseFloat(styles.marginTop || 0) + parseFloat(styles.marginBottom || 0);
+ var y = parseFloat(styles.marginLeft || 0) + parseFloat(styles.marginRight || 0);
+ var result = {
+ width: element.offsetWidth + y,
+ height: element.offsetHeight + x
+ };
+ return result;
+ }
+
+ /**
+ * Get the opposite placement of the given one
+ * @method
+ * @memberof Popper.Utils
+ * @argument {String} placement
+ * @returns {String} flipped placement
+ */
+ function getOppositePlacement(placement) {
+ var hash = { left: 'right', right: 'left', bottom: 'top', top: 'bottom' };
+ return placement.replace(/left|right|bottom|top/g, function (matched) {
+ return hash[matched];
+ });
+ }
+
+ /**
+ * Get offsets to the popper
+ * @method
+ * @memberof Popper.Utils
+ * @param {Object} position - CSS position the Popper will get applied
+ * @param {HTMLElement} popper - the popper element
+ * @param {Object} referenceOffsets - the reference offsets (the popper will be relative to this)
+ * @param {String} placement - one of the valid placement options
+ * @returns {Object} popperOffsets - An object containing the offsets which will be applied to the popper
+ */
+ function getPopperOffsets(popper, referenceOffsets, placement) {
+ placement = placement.split('-')[0];
+
+ // Get popper node sizes
+ var popperRect = getOuterSizes(popper);
+
+ // Add position, width and height to our offsets object
+ var popperOffsets = {
+ width: popperRect.width,
+ height: popperRect.height
+ };
+
+ // depending by the popper placement we have to compute its offsets slightly differently
+ var isHoriz = ['right', 'left'].indexOf(placement) !== -1;
+ var mainSide = isHoriz ? 'top' : 'left';
+ var secondarySide = isHoriz ? 'left' : 'top';
+ var measurement = isHoriz ? 'height' : 'width';
+ var secondaryMeasurement = !isHoriz ? 'height' : 'width';
+
+ popperOffsets[mainSide] = referenceOffsets[mainSide] + referenceOffsets[measurement] / 2 - popperRect[measurement] / 2;
+ if (placement === secondarySide) {
+ popperOffsets[secondarySide] = referenceOffsets[secondarySide] - popperRect[secondaryMeasurement];
+ } else {
+ popperOffsets[secondarySide] = referenceOffsets[getOppositePlacement(secondarySide)];
+ }
+
+ return popperOffsets;
+ }
+
+ /**
+ * Mimics the `find` method of Array
+ * @method
+ * @memberof Popper.Utils
+ * @argument {Array} arr
+ * @argument prop
+ * @argument value
+ * @returns index or -1
+ */
+ function find(arr, check) {
+ // use native find if supported
+ if (Array.prototype.find) {
+ return arr.find(check);
+ }
+
+ // use `filter` to obtain the same behavior of `find`
+ return arr.filter(check)[0];
+ }
+
+ /**
+ * Return the index of the matching object
+ * @method
+ * @memberof Popper.Utils
+ * @argument {Array} arr
+ * @argument prop
+ * @argument value
+ * @returns index or -1
+ */
+ function findIndex(arr, prop, value) {
+ // use native findIndex if supported
+ if (Array.prototype.findIndex) {
+ return arr.findIndex(function (cur) {
+ return cur[prop] === value;
+ });
+ }
+
+ // use `find` + `indexOf` if `findIndex` isn't supported
+ var match = find(arr, function (obj) {
+ return obj[prop] === value;
+ });
+ return arr.indexOf(match);
+ }
+
+ /**
+ * Loop trough the list of modifiers and run them in order,
+ * each of them will then edit the data object.
+ * @method
+ * @memberof Popper.Utils
+ * @param {dataObject} data
+ * @param {Array} modifiers
+ * @param {String} ends - Optional modifier name used as stopper
+ * @returns {dataObject}
+ */
+ function runModifiers(modifiers, data, ends) {
+ var modifiersToRun = ends === undefined ? modifiers : modifiers.slice(0, findIndex(modifiers, 'name', ends));
+
+ modifiersToRun.forEach(function (modifier) {
+ if (modifier['function']) {
+ // eslint-disable-line dot-notation
+ console.warn('`modifier.function` is deprecated, use `modifier.fn`!');
+ }
+ var fn = modifier['function'] || modifier.fn; // eslint-disable-line dot-notation
+ if (modifier.enabled && isFunction(fn)) {
+ // Add properties to offsets to make them a complete clientRect object
+ // we do this before each modifier to make sure the previous one doesn't
+ // mess with these values
+ data.offsets.popper = getClientRect(data.offsets.popper);
+ data.offsets.reference = getClientRect(data.offsets.reference);
+
+ data = fn(data, modifier);
+ }
+ });
+
+ return data;
+ }
+
+ /**
+ * Updates the position of the popper, computing the new offsets and applying
+ * the new style.
+ * Prefer `scheduleUpdate` over `update` because of performance reasons.
+ * @method
+ * @memberof Popper
+ */
+ function update() {
+ // if popper is destroyed, don't perform any further update
+ if (this.state.isDestroyed) {
+ return;
+ }
+
+ var data = {
+ instance: this,
+ styles: {},
+ arrowStyles: {},
+ attributes: {},
+ flipped: false,
+ offsets: {}
+ };
+
+ // compute reference element offsets
+ data.offsets.reference = getReferenceOffsets(this.state, this.popper, this.reference, this.options.positionFixed);
+
+ // compute auto placement, store placement inside the data object,
+ // modifiers will be able to edit `placement` if needed
+ // and refer to originalPlacement to know the original value
+ data.placement = computeAutoPlacement(this.options.placement, data.offsets.reference, this.popper, this.reference, this.options.modifiers.flip.boundariesElement, this.options.modifiers.flip.padding);
+
+ // store the computed placement inside `originalPlacement`
+ data.originalPlacement = data.placement;
+
+ data.positionFixed = this.options.positionFixed;
+
+ // compute the popper offsets
+ data.offsets.popper = getPopperOffsets(this.popper, data.offsets.reference, data.placement);
+
+ data.offsets.popper.position = this.options.positionFixed ? 'fixed' : 'absolute';
+
+ // run the modifiers
+ data = runModifiers(this.modifiers, data);
+
+ // the first `update` will call `onCreate` callback
+ // the other ones will call `onUpdate` callback
+ if (!this.state.isCreated) {
+ this.state.isCreated = true;
+ this.options.onCreate(data);
+ } else {
+ this.options.onUpdate(data);
+ }
+ }
+
+ /**
+ * Helper used to know if the given modifier is enabled.
+ * @method
+ * @memberof Popper.Utils
+ * @returns {Boolean}
+ */
+ function isModifierEnabled(modifiers, modifierName) {
+ return modifiers.some(function (_ref) {
+ var name = _ref.name,
+ enabled = _ref.enabled;
+ return enabled && name === modifierName;
+ });
+ }
+
+ /**
+ * Get the prefixed supported property name
+ * @method
+ * @memberof Popper.Utils
+ * @argument {String} property (camelCase)
+ * @returns {String} prefixed property (camelCase or PascalCase, depending on the vendor prefix)
+ */
+ function getSupportedPropertyName(property) {
+ var prefixes = [false, 'ms', 'Webkit', 'Moz', 'O'];
+ var upperProp = property.charAt(0).toUpperCase() + property.slice(1);
+
+ for (var i = 0; i < prefixes.length; i++) {
+ var prefix = prefixes[i];
+ var toCheck = prefix ? '' + prefix + upperProp : property;
+ if (typeof document.body.style[toCheck] !== 'undefined') {
+ return toCheck;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Destroys the popper.
+ * @method
+ * @memberof Popper
+ */
+ function destroy() {
+ this.state.isDestroyed = true;
+
+ // touch DOM only if `applyStyle` modifier is enabled
+ if (isModifierEnabled(this.modifiers, 'applyStyle')) {
+ this.popper.removeAttribute('x-placement');
+ this.popper.style.position = '';
+ this.popper.style.top = '';
+ this.popper.style.left = '';
+ this.popper.style.right = '';
+ this.popper.style.bottom = '';
+ this.popper.style.willChange = '';
+ this.popper.style[getSupportedPropertyName('transform')] = '';
+ }
+
+ this.disableEventListeners();
+
+ // remove the popper if user explicitly asked for the deletion on destroy
+ // do not use `remove` because IE11 doesn't support it
+ if (this.options.removeOnDestroy) {
+ this.popper.parentNode.removeChild(this.popper);
+ }
+ return this;
+ }
+
+ /**
+ * Get the window associated with the element
+ * @argument {Element} element
+ * @returns {Window}
+ */
+ function getWindow(element) {
+ var ownerDocument = element.ownerDocument;
+ return ownerDocument ? ownerDocument.defaultView : window;
+ }
+
+ function attachToScrollParents(scrollParent, event, callback, scrollParents) {
+ var isBody = scrollParent.nodeName === 'BODY';
+ var target = isBody ? scrollParent.ownerDocument.defaultView : scrollParent;
+ target.addEventListener(event, callback, { passive: true });
+
+ if (!isBody) {
+ attachToScrollParents(getScrollParent(target.parentNode), event, callback, scrollParents);
+ }
+ scrollParents.push(target);
+ }
+
+ /**
+ * Setup needed event listeners used to update the popper position
+ * @method
+ * @memberof Popper.Utils
+ * @private
+ */
+ function setupEventListeners(reference, options, state, updateBound) {
+ // Resize event listener on window
+ state.updateBound = updateBound;
+ getWindow(reference).addEventListener('resize', state.updateBound, { passive: true });
+
+ // Scroll event listener on scroll parents
+ var scrollElement = getScrollParent(reference);
+ attachToScrollParents(scrollElement, 'scroll', state.updateBound, state.scrollParents);
+ state.scrollElement = scrollElement;
+ state.eventsEnabled = true;
+
+ return state;
+ }
+
+ /**
+ * It will add resize/scroll events and start recalculating
+ * position of the popper element when they are triggered.
+ * @method
+ * @memberof Popper
+ */
+ function enableEventListeners() {
+ if (!this.state.eventsEnabled) {
+ this.state = setupEventListeners(this.reference, this.options, this.state, this.scheduleUpdate);
+ }
+ }
+
+ /**
+ * Remove event listeners used to update the popper position
+ * @method
+ * @memberof Popper.Utils
+ * @private
+ */
+ function removeEventListeners(reference, state) {
+ // Remove resize event listener on window
+ getWindow(reference).removeEventListener('resize', state.updateBound);
+
+ // Remove scroll event listener on scroll parents
+ state.scrollParents.forEach(function (target) {
+ target.removeEventListener('scroll', state.updateBound);
+ });
+
+ // Reset state
+ state.updateBound = null;
+ state.scrollParents = [];
+ state.scrollElement = null;
+ state.eventsEnabled = false;
+ return state;
+ }
+
+ /**
+ * It will remove resize/scroll events and won't recalculate popper position
+ * when they are triggered. It also won't trigger `onUpdate` callback anymore,
+ * unless you call `update` method manually.
+ * @method
+ * @memberof Popper
+ */
+ function disableEventListeners() {
+ if (this.state.eventsEnabled) {
+ cancelAnimationFrame(this.scheduleUpdate);
+ this.state = removeEventListeners(this.reference, this.state);
+ }
+ }
+
+ /**
+ * Tells if a given input is a number
+ * @method
+ * @memberof Popper.Utils
+ * @param {*} input to check
+ * @return {Boolean}
+ */
+ function isNumeric(n) {
+ return n !== '' && !isNaN(parseFloat(n)) && isFinite(n);
+ }
+
+ /**
+ * Set the style to the given popper
+ * @method
+ * @memberof Popper.Utils
+ * @argument {Element} element - Element to apply the style to
+ * @argument {Object} styles
+ * Object with a list of properties and values which will be applied to the element
+ */
+ function setStyles(element, styles) {
+ Object.keys(styles).forEach(function (prop) {
+ var unit = '';
+ // add unit if the value is numeric and is one of the following
+ if (['width', 'height', 'top', 'right', 'bottom', 'left'].indexOf(prop) !== -1 && isNumeric(styles[prop])) {
+ unit = 'px';
+ }
+ element.style[prop] = styles[prop] + unit;
+ });
+ }
+
+ /**
+ * Set the attributes to the given popper
+ * @method
+ * @memberof Popper.Utils
+ * @argument {Element} element - Element to apply the attributes to
+ * @argument {Object} styles
+ * Object with a list of properties and values which will be applied to the element
+ */
+ function setAttributes(element, attributes) {
+ Object.keys(attributes).forEach(function (prop) {
+ var value = attributes[prop];
+ if (value !== false) {
+ element.setAttribute(prop, attributes[prop]);
+ } else {
+ element.removeAttribute(prop);
+ }
+ });
+ }
+
+ /**
+ * @function
+ * @memberof Modifiers
+ * @argument {Object} data - The data object generated by `update` method
+ * @argument {Object} data.styles - List of style properties - values to apply to popper element
+ * @argument {Object} data.attributes - List of attribute properties - values to apply to popper element
+ * @argument {Object} options - Modifiers configuration and options
+ * @returns {Object} The same data object
+ */
+ function applyStyle(data) {
+ // any property present in `data.styles` will be applied to the popper,
+ // in this way we can make the 3rd party modifiers add custom styles to it
+ // Be aware, modifiers could override the properties defined in the previous
+ // lines of this modifier!
+ setStyles(data.instance.popper, data.styles);
+
+ // any property present in `data.attributes` will be applied to the popper,
+ // they will be set as HTML attributes of the element
+ setAttributes(data.instance.popper, data.attributes);
+
+ // if arrowElement is defined and arrowStyles has some properties
+ if (data.arrowElement && Object.keys(data.arrowStyles).length) {
+ setStyles(data.arrowElement, data.arrowStyles);
+ }
+
+ return data;
+ }
+
+ /**
+ * Set the x-placement attribute before everything else because it could be used
+ * to add margins to the popper margins needs to be calculated to get the
+ * correct popper offsets.
+ * @method
+ * @memberof Popper.modifiers
+ * @param {HTMLElement} reference - The reference element used to position the popper
+ * @param {HTMLElement} popper - The HTML element used as popper
+ * @param {Object} options - Popper.js options
+ */
+ function applyStyleOnLoad(reference, popper, options, modifierOptions, state) {
+ // compute reference element offsets
+ var referenceOffsets = getReferenceOffsets(state, popper, reference, options.positionFixed);
+
+ // compute auto placement, store placement inside the data object,
+ // modifiers will be able to edit `placement` if needed
+ // and refer to originalPlacement to know the original value
+ var placement = computeAutoPlacement(options.placement, referenceOffsets, popper, reference, options.modifiers.flip.boundariesElement, options.modifiers.flip.padding);
+
+ popper.setAttribute('x-placement', placement);
+
+ // Apply `position` to popper before anything else because
+ // without the position applied we can't guarantee correct computations
+ setStyles(popper, { position: options.positionFixed ? 'fixed' : 'absolute' });
+
+ return options;
+ }
+
+ /**
+ * @function
+ * @memberof Popper.Utils
+ * @argument {Object} data - The data object generated by `update` method
+ * @argument {Boolean} shouldRound - If the offsets should be rounded at all
+ * @returns {Object} The popper's position offsets rounded
+ *
+ * The tale of pixel-perfect positioning. It's still not 100% perfect, but as
+ * good as it can be within reason.
+ * Discussion here: https://github.com/FezVrasta/popper.js/pull/715
+ *
+ * Low DPI screens cause a popper to be blurry if not using full pixels (Safari
+ * as well on High DPI screens).
+ *
+ * Firefox prefers no rounding for positioning and does not have blurriness on
+ * high DPI screens.
+ *
+ * Only horizontal placement and left/right values need to be considered.
+ */
+ function getRoundedOffsets(data, shouldRound) {
+ var _data$offsets = data.offsets,
+ popper = _data$offsets.popper,
+ reference = _data$offsets.reference;
+ var round = Math.round,
+ floor = Math.floor;
+
+ var noRound = function noRound(v) {
+ return v;
+ };
+
+ var referenceWidth = round(reference.width);
+ var popperWidth = round(popper.width);
+
+ var isVertical = ['left', 'right'].indexOf(data.placement) !== -1;
+ var isVariation = data.placement.indexOf('-') !== -1;
+ var sameWidthParity = referenceWidth % 2 === popperWidth % 2;
+ var bothOddWidth = referenceWidth % 2 === 1 && popperWidth % 2 === 1;
+
+ var horizontalToInteger = !shouldRound ? noRound : isVertical || isVariation || sameWidthParity ? round : floor;
+ var verticalToInteger = !shouldRound ? noRound : round;
+
+ return {
+ left: horizontalToInteger(bothOddWidth && !isVariation && shouldRound ? popper.left - 1 : popper.left),
+ top: verticalToInteger(popper.top),
+ bottom: verticalToInteger(popper.bottom),
+ right: horizontalToInteger(popper.right)
+ };
+ }
+
+ var isFirefox = isBrowser && /Firefox/i.test(navigator.userAgent);
+
+ /**
+ * @function
+ * @memberof Modifiers
+ * @argument {Object} data - The data object generated by `update` method
+ * @argument {Object} options - Modifiers configuration and options
+ * @returns {Object} The data object, properly modified
+ */
+ function computeStyle(data, options) {
+ var x = options.x,
+ y = options.y;
+ var popper = data.offsets.popper;
+
+ // Remove this legacy support in Popper.js v2
+
+ var legacyGpuAccelerationOption = find(data.instance.modifiers, function (modifier) {
+ return modifier.name === 'applyStyle';
+ }).gpuAcceleration;
+ if (legacyGpuAccelerationOption !== undefined) {
+ console.warn('WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!');
+ }
+ var gpuAcceleration = legacyGpuAccelerationOption !== undefined ? legacyGpuAccelerationOption : options.gpuAcceleration;
+
+ var offsetParent = getOffsetParent(data.instance.popper);
+ var offsetParentRect = getBoundingClientRect(offsetParent);
+
+ // Styles
+ var styles = {
+ position: popper.position
+ };
+
+ var offsets = getRoundedOffsets(data, window.devicePixelRatio < 2 || !isFirefox);
+
+ var sideA = x === 'bottom' ? 'top' : 'bottom';
+ var sideB = y === 'right' ? 'left' : 'right';
+
+ // if gpuAcceleration is set to `true` and transform is supported,
+ // we use `translate3d` to apply the position to the popper we
+ // automatically use the supported prefixed version if needed
+ var prefixedProperty = getSupportedPropertyName('transform');
+
+ // now, let's make a step back and look at this code closely (wtf?)
+ // If the content of the popper grows once it's been positioned, it
+ // may happen that the popper gets misplaced because of the new content
+ // overflowing its reference element
+ // To avoid this problem, we provide two options (x and y), which allow
+ // the consumer to define the offset origin.
+ // If we position a popper on top of a reference element, we can set
+ // `x` to `top` to make the popper grow towards its top instead of
+ // its bottom.
+ var left = void 0,
+ top = void 0;
+ if (sideA === 'bottom') {
+ // when offsetParent is the positioning is relative to the bottom of the screen (excluding the scrollbar)
+ // and not the bottom of the html element
+ if (offsetParent.nodeName === 'HTML') {
+ top = -offsetParent.clientHeight + offsets.bottom;
+ } else {
+ top = -offsetParentRect.height + offsets.bottom;
+ }
+ } else {
+ top = offsets.top;
+ }
+ if (sideB === 'right') {
+ if (offsetParent.nodeName === 'HTML') {
+ left = -offsetParent.clientWidth + offsets.right;
+ } else {
+ left = -offsetParentRect.width + offsets.right;
+ }
+ } else {
+ left = offsets.left;
+ }
+ if (gpuAcceleration && prefixedProperty) {
+ styles[prefixedProperty] = 'translate3d(' + left + 'px, ' + top + 'px, 0)';
+ styles[sideA] = 0;
+ styles[sideB] = 0;
+ styles.willChange = 'transform';
+ } else {
+ // othwerise, we use the standard `top`, `left`, `bottom` and `right` properties
+ var invertTop = sideA === 'bottom' ? -1 : 1;
+ var invertLeft = sideB === 'right' ? -1 : 1;
+ styles[sideA] = top * invertTop;
+ styles[sideB] = left * invertLeft;
+ styles.willChange = sideA + ', ' + sideB;
+ }
+
+ // Attributes
+ var attributes = {
+ 'x-placement': data.placement
+ };
+
+ // Update `data` attributes, styles and arrowStyles
+ data.attributes = _extends$1({}, attributes, data.attributes);
+ data.styles = _extends$1({}, styles, data.styles);
+ data.arrowStyles = _extends$1({}, data.offsets.arrow, data.arrowStyles);
+
+ return data;
+ }
+
+ /**
+ * Helper used to know if the given modifier depends from another one.
+ * It checks if the needed modifier is listed and enabled.
+ * @method
+ * @memberof Popper.Utils
+ * @param {Array} modifiers - list of modifiers
+ * @param {String} requestingName - name of requesting modifier
+ * @param {String} requestedName - name of requested modifier
+ * @returns {Boolean}
+ */
+ function isModifierRequired(modifiers, requestingName, requestedName) {
+ var requesting = find(modifiers, function (_ref) {
+ var name = _ref.name;
+ return name === requestingName;
+ });
+
+ var isRequired = !!requesting && modifiers.some(function (modifier) {
+ return modifier.name === requestedName && modifier.enabled && modifier.order < requesting.order;
+ });
+
+ if (!isRequired) {
+ var _requesting = '`' + requestingName + '`';
+ var requested = '`' + requestedName + '`';
+ console.warn(requested + ' modifier is required by ' + _requesting + ' modifier in order to work, be sure to include it before ' + _requesting + '!');
+ }
+ return isRequired;
+ }
+
+ /**
+ * @function
+ * @memberof Modifiers
+ * @argument {Object} data - The data object generated by update method
+ * @argument {Object} options - Modifiers configuration and options
+ * @returns {Object} The data object, properly modified
+ */
+ function arrow(data, options) {
+ var _data$offsets$arrow;
+
+ // arrow depends on keepTogether in order to work
+ if (!isModifierRequired(data.instance.modifiers, 'arrow', 'keepTogether')) {
+ return data;
+ }
+
+ var arrowElement = options.element;
+
+ // if arrowElement is a string, suppose it's a CSS selector
+ if (typeof arrowElement === 'string') {
+ arrowElement = data.instance.popper.querySelector(arrowElement);
+
+ // if arrowElement is not found, don't run the modifier
+ if (!arrowElement) {
+ return data;
+ }
+ } else {
+ // if the arrowElement isn't a query selector we must check that the
+ // provided DOM node is child of its popper node
+ if (!data.instance.popper.contains(arrowElement)) {
+ console.warn('WARNING: `arrow.element` must be child of its popper element!');
+ return data;
+ }
+ }
+
+ var placement = data.placement.split('-')[0];
+ var _data$offsets = data.offsets,
+ popper = _data$offsets.popper,
+ reference = _data$offsets.reference;
+
+ var isVertical = ['left', 'right'].indexOf(placement) !== -1;
+
+ var len = isVertical ? 'height' : 'width';
+ var sideCapitalized = isVertical ? 'Top' : 'Left';
+ var side = sideCapitalized.toLowerCase();
+ var altSide = isVertical ? 'left' : 'top';
+ var opSide = isVertical ? 'bottom' : 'right';
+ var arrowElementSize = getOuterSizes(arrowElement)[len];
+
+ //
+ // extends keepTogether behavior making sure the popper and its
+ // reference have enough pixels in conjunction
+ //
+
+ // top/left side
+ if (reference[opSide] - arrowElementSize < popper[side]) {
+ data.offsets.popper[side] -= popper[side] - (reference[opSide] - arrowElementSize);
+ }
+ // bottom/right side
+ if (reference[side] + arrowElementSize > popper[opSide]) {
+ data.offsets.popper[side] += reference[side] + arrowElementSize - popper[opSide];
+ }
+ data.offsets.popper = getClientRect(data.offsets.popper);
+
+ // compute center of the popper
+ var center = reference[side] + reference[len] / 2 - arrowElementSize / 2;
+
+ // Compute the sideValue using the updated popper offsets
+ // take popper margin in account because we don't have this info available
+ var css = getStyleComputedProperty(data.instance.popper);
+ var popperMarginSide = parseFloat(css['margin' + sideCapitalized]);
+ var popperBorderSide = parseFloat(css['border' + sideCapitalized + 'Width']);
+ var sideValue = center - data.offsets.popper[side] - popperMarginSide - popperBorderSide;
+
+ // prevent arrowElement from being placed not contiguously to its popper
+ sideValue = Math.max(Math.min(popper[len] - arrowElementSize, sideValue), 0);
+
+ data.arrowElement = arrowElement;
+ data.offsets.arrow = (_data$offsets$arrow = {}, defineProperty(_data$offsets$arrow, side, Math.round(sideValue)), defineProperty(_data$offsets$arrow, altSide, ''), _data$offsets$arrow);
+
+ return data;
+ }
+
+ /**
+ * Get the opposite placement variation of the given one
+ * @method
+ * @memberof Popper.Utils
+ * @argument {String} placement variation
+ * @returns {String} flipped placement variation
+ */
+ function getOppositeVariation(variation) {
+ if (variation === 'end') {
+ return 'start';
+ } else if (variation === 'start') {
+ return 'end';
+ }
+ return variation;
+ }
+
+ /**
+ * List of accepted placements to use as values of the `placement` option.
+ * Valid placements are:
+ * - `auto`
+ * - `top`
+ * - `right`
+ * - `bottom`
+ * - `left`
+ *
+ * Each placement can have a variation from this list:
+ * - `-start`
+ * - `-end`
+ *
+ * Variations are interpreted easily if you think of them as the left to right
+ * written languages. Horizontally (`top` and `bottom`), `start` is left and `end`
+ * is right.
+ * Vertically (`left` and `right`), `start` is top and `end` is bottom.
+ *
+ * Some valid examples are:
+ * - `top-end` (on top of reference, right aligned)
+ * - `right-start` (on right of reference, top aligned)
+ * - `bottom` (on bottom, centered)
+ * - `auto-end` (on the side with more space available, alignment depends by placement)
+ *
+ * @static
+ * @type {Array}
+ * @enum {String}
+ * @readonly
+ * @method placements
+ * @memberof Popper
+ */
+ var placements = ['auto-start', 'auto', 'auto-end', 'top-start', 'top', 'top-end', 'right-start', 'right', 'right-end', 'bottom-end', 'bottom', 'bottom-start', 'left-end', 'left', 'left-start'];
+
+ // Get rid of `auto` `auto-start` and `auto-end`
+ var validPlacements = placements.slice(3);
+
+ /**
+ * Given an initial placement, returns all the subsequent placements
+ * clockwise (or counter-clockwise).
+ *
+ * @method
+ * @memberof Popper.Utils
+ * @argument {String} placement - A valid placement (it accepts variations)
+ * @argument {Boolean} counter - Set to true to walk the placements counterclockwise
+ * @returns {Array} placements including their variations
+ */
+ function clockwise(placement) {
+ var counter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
+
+ var index = validPlacements.indexOf(placement);
+ var arr = validPlacements.slice(index + 1).concat(validPlacements.slice(0, index));
+ return counter ? arr.reverse() : arr;
+ }
+
+ var BEHAVIORS = {
+ FLIP: 'flip',
+ CLOCKWISE: 'clockwise',
+ COUNTERCLOCKWISE: 'counterclockwise'
+ };
+
+ /**
+ * @function
+ * @memberof Modifiers
+ * @argument {Object} data - The data object generated by update method
+ * @argument {Object} options - Modifiers configuration and options
+ * @returns {Object} The data object, properly modified
+ */
+ function flip(data, options) {
+ // if `inner` modifier is enabled, we can't use the `flip` modifier
+ if (isModifierEnabled(data.instance.modifiers, 'inner')) {
+ return data;
+ }
+
+ if (data.flipped && data.placement === data.originalPlacement) {
+ // seems like flip is trying to loop, probably there's not enough space on any of the flippable sides
+ return data;
+ }
+
+ var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, options.boundariesElement, data.positionFixed);
+
+ var placement = data.placement.split('-')[0];
+ var placementOpposite = getOppositePlacement(placement);
+ var variation = data.placement.split('-')[1] || '';
+
+ var flipOrder = [];
+
+ switch (options.behavior) {
+ case BEHAVIORS.FLIP:
+ flipOrder = [placement, placementOpposite];
+ break;
+ case BEHAVIORS.CLOCKWISE:
+ flipOrder = clockwise(placement);
+ break;
+ case BEHAVIORS.COUNTERCLOCKWISE:
+ flipOrder = clockwise(placement, true);
+ break;
+ default:
+ flipOrder = options.behavior;
+ }
+
+ flipOrder.forEach(function (step, index) {
+ if (placement !== step || flipOrder.length === index + 1) {
+ return data;
+ }
+
+ placement = data.placement.split('-')[0];
+ placementOpposite = getOppositePlacement(placement);
+
+ var popperOffsets = data.offsets.popper;
+ var refOffsets = data.offsets.reference;
+
+ // using floor because the reference offsets may contain decimals we are not going to consider here
+ var floor = Math.floor;
+ var overlapsRef = placement === 'left' && floor(popperOffsets.right) > floor(refOffsets.left) || placement === 'right' && floor(popperOffsets.left) < floor(refOffsets.right) || placement === 'top' && floor(popperOffsets.bottom) > floor(refOffsets.top) || placement === 'bottom' && floor(popperOffsets.top) < floor(refOffsets.bottom);
+
+ var overflowsLeft = floor(popperOffsets.left) < floor(boundaries.left);
+ var overflowsRight = floor(popperOffsets.right) > floor(boundaries.right);
+ var overflowsTop = floor(popperOffsets.top) < floor(boundaries.top);
+ var overflowsBottom = floor(popperOffsets.bottom) > floor(boundaries.bottom);
+
+ var overflowsBoundaries = placement === 'left' && overflowsLeft || placement === 'right' && overflowsRight || placement === 'top' && overflowsTop || placement === 'bottom' && overflowsBottom;
+
+ // flip the variation if required
+ var isVertical = ['top', 'bottom'].indexOf(placement) !== -1;
+
+ // flips variation if reference element overflows boundaries
+ var flippedVariationByRef = !!options.flipVariations && (isVertical && variation === 'start' && overflowsLeft || isVertical && variation === 'end' && overflowsRight || !isVertical && variation === 'start' && overflowsTop || !isVertical && variation === 'end' && overflowsBottom);
+
+ // flips variation if popper content overflows boundaries
+ var flippedVariationByContent = !!options.flipVariationsByContent && (isVertical && variation === 'start' && overflowsRight || isVertical && variation === 'end' && overflowsLeft || !isVertical && variation === 'start' && overflowsBottom || !isVertical && variation === 'end' && overflowsTop);
+
+ var flippedVariation = flippedVariationByRef || flippedVariationByContent;
+
+ if (overlapsRef || overflowsBoundaries || flippedVariation) {
+ // this boolean to detect any flip loop
+ data.flipped = true;
+
+ if (overlapsRef || overflowsBoundaries) {
+ placement = flipOrder[index + 1];
+ }
+
+ if (flippedVariation) {
+ variation = getOppositeVariation(variation);
+ }
+
+ data.placement = placement + (variation ? '-' + variation : '');
+
+ // this object contains `position`, we want to preserve it along with
+ // any additional property we may add in the future
+ data.offsets.popper = _extends$1({}, data.offsets.popper, getPopperOffsets(data.instance.popper, data.offsets.reference, data.placement));
+
+ data = runModifiers(data.instance.modifiers, data, 'flip');
+ }
+ });
+ return data;
+ }
+
+ /**
+ * @function
+ * @memberof Modifiers
+ * @argument {Object} data - The data object generated by update method
+ * @argument {Object} options - Modifiers configuration and options
+ * @returns {Object} The data object, properly modified
+ */
+ function keepTogether(data) {
+ var _data$offsets = data.offsets,
+ popper = _data$offsets.popper,
+ reference = _data$offsets.reference;
+
+ var placement = data.placement.split('-')[0];
+ var floor = Math.floor;
+ var isVertical = ['top', 'bottom'].indexOf(placement) !== -1;
+ var side = isVertical ? 'right' : 'bottom';
+ var opSide = isVertical ? 'left' : 'top';
+ var measurement = isVertical ? 'width' : 'height';
+
+ if (popper[side] < floor(reference[opSide])) {
+ data.offsets.popper[opSide] = floor(reference[opSide]) - popper[measurement];
+ }
+ if (popper[opSide] > floor(reference[side])) {
+ data.offsets.popper[opSide] = floor(reference[side]);
+ }
+
+ return data;
+ }
+
+ /**
+ * Converts a string containing value + unit into a px value number
+ * @function
+ * @memberof {modifiers~offset}
+ * @private
+ * @argument {String} str - Value + unit string
+ * @argument {String} measurement - `height` or `width`
+ * @argument {Object} popperOffsets
+ * @argument {Object} referenceOffsets
+ * @returns {Number|String}
+ * Value in pixels, or original string if no values were extracted
+ */
+ function toValue(str, measurement, popperOffsets, referenceOffsets) {
+ // separate value from unit
+ var split = str.match(/((?:\-|\+)?\d*\.?\d*)(.*)/);
+ var value = +split[1];
+ var unit = split[2];
+
+ // If it's not a number it's an operator, I guess
+ if (!value) {
+ return str;
+ }
+
+ if (unit.indexOf('%') === 0) {
+ var element = void 0;
+ switch (unit) {
+ case '%p':
+ element = popperOffsets;
+ break;
+ case '%':
+ case '%r':
+ default:
+ element = referenceOffsets;
+ }
+
+ var rect = getClientRect(element);
+ return rect[measurement] / 100 * value;
+ } else if (unit === 'vh' || unit === 'vw') {
+ // if is a vh or vw, we calculate the size based on the viewport
+ var size = void 0;
+ if (unit === 'vh') {
+ size = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
+ } else {
+ size = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
+ }
+ return size / 100 * value;
+ } else {
+ // if is an explicit pixel unit, we get rid of the unit and keep the value
+ // if is an implicit unit, it's px, and we return just the value
+ return value;
+ }
+ }
+
+ /**
+ * Parse an `offset` string to extrapolate `x` and `y` numeric offsets.
+ * @function
+ * @memberof {modifiers~offset}
+ * @private
+ * @argument {String} offset
+ * @argument {Object} popperOffsets
+ * @argument {Object} referenceOffsets
+ * @argument {String} basePlacement
+ * @returns {Array} a two cells array with x and y offsets in numbers
+ */
+ function parseOffset(offset, popperOffsets, referenceOffsets, basePlacement) {
+ var offsets = [0, 0];
+
+ // Use height if placement is left or right and index is 0 otherwise use width
+ // in this way the first offset will use an axis and the second one
+ // will use the other one
+ var useHeight = ['right', 'left'].indexOf(basePlacement) !== -1;
+
+ // Split the offset string to obtain a list of values and operands
+ // The regex addresses values with the plus or minus sign in front (+10, -20, etc)
+ var fragments = offset.split(/(\+|\-)/).map(function (frag) {
+ return frag.trim();
+ });
+
+ // Detect if the offset string contains a pair of values or a single one
+ // they could be separated by comma or space
+ var divider = fragments.indexOf(find(fragments, function (frag) {
+ return frag.search(/,|\s/) !== -1;
+ }));
+
+ if (fragments[divider] && fragments[divider].indexOf(',') === -1) {
+ console.warn('Offsets separated by white space(s) are deprecated, use a comma (,) instead.');
+ }
+
+ // If divider is found, we divide the list of values and operands to divide
+ // them by ofset X and Y.
+ var splitRegex = /\s*,\s*|\s+/;
+ var ops = divider !== -1 ? [fragments.slice(0, divider).concat([fragments[divider].split(splitRegex)[0]]), [fragments[divider].split(splitRegex)[1]].concat(fragments.slice(divider + 1))] : [fragments];
+
+ // Convert the values with units to absolute pixels to allow our computations
+ ops = ops.map(function (op, index) {
+ // Most of the units rely on the orientation of the popper
+ var measurement = (index === 1 ? !useHeight : useHeight) ? 'height' : 'width';
+ var mergeWithPrevious = false;
+ return op
+ // This aggregates any `+` or `-` sign that aren't considered operators
+ // e.g.: 10 + +5 => [10, +, +5]
+ .reduce(function (a, b) {
+ if (a[a.length - 1] === '' && ['+', '-'].indexOf(b) !== -1) {
+ a[a.length - 1] = b;
+ mergeWithPrevious = true;
+ return a;
+ } else if (mergeWithPrevious) {
+ a[a.length - 1] += b;
+ mergeWithPrevious = false;
+ return a;
+ } else {
+ return a.concat(b);
+ }
+ }, [])
+ // Here we convert the string values into number values (in px)
+ .map(function (str) {
+ return toValue(str, measurement, popperOffsets, referenceOffsets);
+ });
+ });
+
+ // Loop trough the offsets arrays and execute the operations
+ ops.forEach(function (op, index) {
+ op.forEach(function (frag, index2) {
+ if (isNumeric(frag)) {
+ offsets[index] += frag * (op[index2 - 1] === '-' ? -1 : 1);
+ }
+ });
+ });
+ return offsets;
+ }
+
+ /**
+ * @function
+ * @memberof Modifiers
+ * @argument {Object} data - The data object generated by update method
+ * @argument {Object} options - Modifiers configuration and options
+ * @argument {Number|String} options.offset=0
+ * The offset value as described in the modifier description
+ * @returns {Object} The data object, properly modified
+ */
+ function offset(data, _ref) {
+ var offset = _ref.offset;
+ var placement = data.placement,
+ _data$offsets = data.offsets,
+ popper = _data$offsets.popper,
+ reference = _data$offsets.reference;
+
+ var basePlacement = placement.split('-')[0];
+
+ var offsets = void 0;
+ if (isNumeric(+offset)) {
+ offsets = [+offset, 0];
+ } else {
+ offsets = parseOffset(offset, popper, reference, basePlacement);
+ }
+
+ if (basePlacement === 'left') {
+ popper.top += offsets[0];
+ popper.left -= offsets[1];
+ } else if (basePlacement === 'right') {
+ popper.top += offsets[0];
+ popper.left += offsets[1];
+ } else if (basePlacement === 'top') {
+ popper.left += offsets[0];
+ popper.top -= offsets[1];
+ } else if (basePlacement === 'bottom') {
+ popper.left += offsets[0];
+ popper.top += offsets[1];
+ }
+
+ data.popper = popper;
+ return data;
+ }
+
+ /**
+ * @function
+ * @memberof Modifiers
+ * @argument {Object} data - The data object generated by `update` method
+ * @argument {Object} options - Modifiers configuration and options
+ * @returns {Object} The data object, properly modified
+ */
+ function preventOverflow(data, options) {
+ var boundariesElement = options.boundariesElement || getOffsetParent(data.instance.popper);
+
+ // If offsetParent is the reference element, we really want to
+ // go one step up and use the next offsetParent as reference to
+ // avoid to make this modifier completely useless and look like broken
+ if (data.instance.reference === boundariesElement) {
+ boundariesElement = getOffsetParent(boundariesElement);
+ }
+
+ // NOTE: DOM access here
+ // resets the popper's position so that the document size can be calculated excluding
+ // the size of the popper element itself
+ var transformProp = getSupportedPropertyName('transform');
+ var popperStyles = data.instance.popper.style; // assignment to help minification
+ var top = popperStyles.top,
+ left = popperStyles.left,
+ transform = popperStyles[transformProp];
+
+ popperStyles.top = '';
+ popperStyles.left = '';
+ popperStyles[transformProp] = '';
+
+ var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, boundariesElement, data.positionFixed);
+
+ // NOTE: DOM access here
+ // restores the original style properties after the offsets have been computed
+ popperStyles.top = top;
+ popperStyles.left = left;
+ popperStyles[transformProp] = transform;
+
+ options.boundaries = boundaries;
+
+ var order = options.priority;
+ var popper = data.offsets.popper;
+
+ var check = {
+ primary: function primary(placement) {
+ var value = popper[placement];
+ if (popper[placement] < boundaries[placement] && !options.escapeWithReference) {
+ value = Math.max(popper[placement], boundaries[placement]);
+ }
+ return defineProperty({}, placement, value);
+ },
+ secondary: function secondary(placement) {
+ var mainSide = placement === 'right' ? 'left' : 'top';
+ var value = popper[mainSide];
+ if (popper[placement] > boundaries[placement] && !options.escapeWithReference) {
+ value = Math.min(popper[mainSide], boundaries[placement] - (placement === 'right' ? popper.width : popper.height));
+ }
+ return defineProperty({}, mainSide, value);
+ }
+ };
+
+ order.forEach(function (placement) {
+ var side = ['left', 'top'].indexOf(placement) !== -1 ? 'primary' : 'secondary';
+ popper = _extends$1({}, popper, check[side](placement));
+ });
+
+ data.offsets.popper = popper;
+
+ return data;
+ }
+
+ /**
+ * @function
+ * @memberof Modifiers
+ * @argument {Object} data - The data object generated by `update` method
+ * @argument {Object} options - Modifiers configuration and options
+ * @returns {Object} The data object, properly modified
+ */
+ function shift(data) {
+ var placement = data.placement;
+ var basePlacement = placement.split('-')[0];
+ var shiftvariation = placement.split('-')[1];
+
+ // if shift shiftvariation is specified, run the modifier
+ if (shiftvariation) {
+ var _data$offsets = data.offsets,
+ reference = _data$offsets.reference,
+ popper = _data$offsets.popper;
+
+ var isVertical = ['bottom', 'top'].indexOf(basePlacement) !== -1;
+ var side = isVertical ? 'left' : 'top';
+ var measurement = isVertical ? 'width' : 'height';
+
+ var shiftOffsets = {
+ start: defineProperty({}, side, reference[side]),
+ end: defineProperty({}, side, reference[side] + reference[measurement] - popper[measurement])
+ };
+
+ data.offsets.popper = _extends$1({}, popper, shiftOffsets[shiftvariation]);
+ }
+
+ return data;
+ }
+
+ /**
+ * @function
+ * @memberof Modifiers
+ * @argument {Object} data - The data object generated by update method
+ * @argument {Object} options - Modifiers configuration and options
+ * @returns {Object} The data object, properly modified
+ */
+ function hide(data) {
+ if (!isModifierRequired(data.instance.modifiers, 'hide', 'preventOverflow')) {
+ return data;
+ }
+
+ var refRect = data.offsets.reference;
+ var bound = find(data.instance.modifiers, function (modifier) {
+ return modifier.name === 'preventOverflow';
+ }).boundaries;
+
+ if (refRect.bottom < bound.top || refRect.left > bound.right || refRect.top > bound.bottom || refRect.right < bound.left) {
+ // Avoid unnecessary DOM access if visibility hasn't changed
+ if (data.hide === true) {
+ return data;
+ }
+
+ data.hide = true;
+ data.attributes['x-out-of-boundaries'] = '';
+ } else {
+ // Avoid unnecessary DOM access if visibility hasn't changed
+ if (data.hide === false) {
+ return data;
+ }
+
+ data.hide = false;
+ data.attributes['x-out-of-boundaries'] = false;
+ }
+
+ return data;
+ }
+
+ /**
+ * @function
+ * @memberof Modifiers
+ * @argument {Object} data - The data object generated by `update` method
+ * @argument {Object} options - Modifiers configuration and options
+ * @returns {Object} The data object, properly modified
+ */
+ function inner(data) {
+ var placement = data.placement;
+ var basePlacement = placement.split('-')[0];
+ var _data$offsets = data.offsets,
+ popper = _data$offsets.popper,
+ reference = _data$offsets.reference;
+
+ var isHoriz = ['left', 'right'].indexOf(basePlacement) !== -1;
+
+ var subtractLength = ['top', 'left'].indexOf(basePlacement) === -1;
+
+ popper[isHoriz ? 'left' : 'top'] = reference[basePlacement] - (subtractLength ? popper[isHoriz ? 'width' : 'height'] : 0);
+
+ data.placement = getOppositePlacement(placement);
+ data.offsets.popper = getClientRect(popper);
+
+ return data;
+ }
+
+ /**
+ * Modifier function, each modifier can have a function of this type assigned
+ * to its `fn` property.
+ * These functions will be called on each update, this means that you must
+ * make sure they are performant enough to avoid performance bottlenecks.
+ *
+ * @function ModifierFn
+ * @argument {dataObject} data - The data object generated by `update` method
+ * @argument {Object} options - Modifiers configuration and options
+ * @returns {dataObject} The data object, properly modified
+ */
+
+ /**
+ * Modifiers are plugins used to alter the behavior of your poppers.
+ * Popper.js uses a set of 9 modifiers to provide all the basic functionalities
+ * needed by the library.
+ *
+ * Usually you don't want to override the `order`, `fn` and `onLoad` props.
+ * All the other properties are configurations that could be tweaked.
+ * @namespace modifiers
+ */
+ var modifiers = {
+ /**
+ * Modifier used to shift the popper on the start or end of its reference
+ * element.
+ * It will read the variation of the `placement` property.
+ * It can be one either `-end` or `-start`.
+ * @memberof modifiers
+ * @inner
+ */
+ shift: {
+ /** @prop {number} order=100 - Index used to define the order of execution */
+ order: 100,
+ /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
+ enabled: true,
+ /** @prop {ModifierFn} */
+ fn: shift
+ },
+
+ /**
+ * The `offset` modifier can shift your popper on both its axis.
+ *
+ * It accepts the following units:
+ * - `px` or unit-less, interpreted as pixels
+ * - `%` or `%r`, percentage relative to the length of the reference element
+ * - `%p`, percentage relative to the length of the popper element
+ * - `vw`, CSS viewport width unit
+ * - `vh`, CSS viewport height unit
+ *
+ * For length is intended the main axis relative to the placement of the popper.
+ * This means that if the placement is `top` or `bottom`, the length will be the
+ * `width`. In case of `left` or `right`, it will be the `height`.
+ *
+ * You can provide a single value (as `Number` or `String`), or a pair of values
+ * as `String` divided by a comma or one (or more) white spaces.
+ * The latter is a deprecated method because it leads to confusion and will be
+ * removed in v2.
+ * Additionally, it accepts additions and subtractions between different units.
+ * Note that multiplications and divisions aren't supported.
+ *
+ * Valid examples are:
+ * ```
+ * 10
+ * '10%'
+ * '10, 10'
+ * '10%, 10'
+ * '10 + 10%'
+ * '10 - 5vh + 3%'
+ * '-10px + 5vh, 5px - 6%'
+ * ```
+ * > **NB**: If you desire to apply offsets to your poppers in a way that may make them overlap
+ * > with their reference element, unfortunately, you will have to disable the `flip` modifier.
+ * > You can read more on this at this [issue](https://github.com/FezVrasta/popper.js/issues/373).
+ *
+ * @memberof modifiers
+ * @inner
+ */
+ offset: {
+ /** @prop {number} order=200 - Index used to define the order of execution */
+ order: 200,
+ /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
+ enabled: true,
+ /** @prop {ModifierFn} */
+ fn: offset,
+ /** @prop {Number|String} offset=0
+ * The offset value as described in the modifier description
+ */
+ offset: 0
+ },
+
+ /**
+ * Modifier used to prevent the popper from being positioned outside the boundary.
+ *
+ * A scenario exists where the reference itself is not within the boundaries.
+ * We can say it has "escaped the boundaries" — or just "escaped".
+ * In this case we need to decide whether the popper should either:
+ *
+ * - detach from the reference and remain "trapped" in the boundaries, or
+ * - if it should ignore the boundary and "escape with its reference"
+ *
+ * When `escapeWithReference` is set to`true` and reference is completely
+ * outside its boundaries, the popper will overflow (or completely leave)
+ * the boundaries in order to remain attached to the edge of the reference.
+ *
+ * @memberof modifiers
+ * @inner
+ */
+ preventOverflow: {
+ /** @prop {number} order=300 - Index used to define the order of execution */
+ order: 300,
+ /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
+ enabled: true,
+ /** @prop {ModifierFn} */
+ fn: preventOverflow,
+ /**
+ * @prop {Array} [priority=['left','right','top','bottom']]
+ * Popper will try to prevent overflow following these priorities by default,
+ * then, it could overflow on the left and on top of the `boundariesElement`
+ */
+ priority: ['left', 'right', 'top', 'bottom'],
+ /**
+ * @prop {number} padding=5
+ * Amount of pixel used to define a minimum distance between the boundaries
+ * and the popper. This makes sure the popper always has a little padding
+ * between the edges of its container
+ */
+ padding: 5,
+ /**
+ * @prop {String|HTMLElement} boundariesElement='scrollParent'
+ * Boundaries used by the modifier. Can be `scrollParent`, `window`,
+ * `viewport` or any DOM element.
+ */
+ boundariesElement: 'scrollParent'
+ },
+
+ /**
+ * Modifier used to make sure the reference and its popper stay near each other
+ * without leaving any gap between the two. Especially useful when the arrow is
+ * enabled and you want to ensure that it points to its reference element.
+ * It cares only about the first axis. You can still have poppers with margin
+ * between the popper and its reference element.
+ * @memberof modifiers
+ * @inner
+ */
+ keepTogether: {
+ /** @prop {number} order=400 - Index used to define the order of execution */
+ order: 400,
+ /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
+ enabled: true,
+ /** @prop {ModifierFn} */
+ fn: keepTogether
+ },
+
+ /**
+ * This modifier is used to move the `arrowElement` of the popper to make
+ * sure it is positioned between the reference element and its popper element.
+ * It will read the outer size of the `arrowElement` node to detect how many
+ * pixels of conjunction are needed.
+ *
+ * It has no effect if no `arrowElement` is provided.
+ * @memberof modifiers
+ * @inner
+ */
+ arrow: {
+ /** @prop {number} order=500 - Index used to define the order of execution */
+ order: 500,
+ /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
+ enabled: true,
+ /** @prop {ModifierFn} */
+ fn: arrow,
+ /** @prop {String|HTMLElement} element='[x-arrow]' - Selector or node used as arrow */
+ element: '[x-arrow]'
+ },
+
+ /**
+ * Modifier used to flip the popper's placement when it starts to overlap its
+ * reference element.
+ *
+ * Requires the `preventOverflow` modifier before it in order to work.
+ *
+ * **NOTE:** this modifier will interrupt the current update cycle and will
+ * restart it if it detects the need to flip the placement.
+ * @memberof modifiers
+ * @inner
+ */
+ flip: {
+ /** @prop {number} order=600 - Index used to define the order of execution */
+ order: 600,
+ /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
+ enabled: true,
+ /** @prop {ModifierFn} */
+ fn: flip,
+ /**
+ * @prop {String|Array} behavior='flip'
+ * The behavior used to change the popper's placement. It can be one of
+ * `flip`, `clockwise`, `counterclockwise` or an array with a list of valid
+ * placements (with optional variations)
+ */
+ behavior: 'flip',
+ /**
+ * @prop {number} padding=5
+ * The popper will flip if it hits the edges of the `boundariesElement`
+ */
+ padding: 5,
+ /**
+ * @prop {String|HTMLElement} boundariesElement='viewport'
+ * The element which will define the boundaries of the popper position.
+ * The popper will never be placed outside of the defined boundaries
+ * (except if `keepTogether` is enabled)
+ */
+ boundariesElement: 'viewport',
+ /**
+ * @prop {Boolean} flipVariations=false
+ * The popper will switch placement variation between `-start` and `-end` when
+ * the reference element overlaps its boundaries.
+ *
+ * The original placement should have a set variation.
+ */
+ flipVariations: false,
+ /**
+ * @prop {Boolean} flipVariationsByContent=false
+ * The popper will switch placement variation between `-start` and `-end` when
+ * the popper element overlaps its reference boundaries.
+ *
+ * The original placement should have a set variation.
+ */
+ flipVariationsByContent: false
+ },
+
+ /**
+ * Modifier used to make the popper flow toward the inner of the reference element.
+ * By default, when this modifier is disabled, the popper will be placed outside
+ * the reference element.
+ * @memberof modifiers
+ * @inner
+ */
+ inner: {
+ /** @prop {number} order=700 - Index used to define the order of execution */
+ order: 700,
+ /** @prop {Boolean} enabled=false - Whether the modifier is enabled or not */
+ enabled: false,
+ /** @prop {ModifierFn} */
+ fn: inner
+ },
+
+ /**
+ * Modifier used to hide the popper when its reference element is outside of the
+ * popper boundaries. It will set a `x-out-of-boundaries` attribute which can
+ * be used to hide with a CSS selector the popper when its reference is
+ * out of boundaries.
+ *
+ * Requires the `preventOverflow` modifier before it in order to work.
+ * @memberof modifiers
+ * @inner
+ */
+ hide: {
+ /** @prop {number} order=800 - Index used to define the order of execution */
+ order: 800,
+ /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
+ enabled: true,
+ /** @prop {ModifierFn} */
+ fn: hide
+ },
+
+ /**
+ * Computes the style that will be applied to the popper element to gets
+ * properly positioned.
+ *
+ * Note that this modifier will not touch the DOM, it just prepares the styles
+ * so that `applyStyle` modifier can apply it. This separation is useful
+ * in case you need to replace `applyStyle` with a custom implementation.
+ *
+ * This modifier has `850` as `order` value to maintain backward compatibility
+ * with previous versions of Popper.js. Expect the modifiers ordering method
+ * to change in future major versions of the library.
+ *
+ * @memberof modifiers
+ * @inner
+ */
+ computeStyle: {
+ /** @prop {number} order=850 - Index used to define the order of execution */
+ order: 850,
+ /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
+ enabled: true,
+ /** @prop {ModifierFn} */
+ fn: computeStyle,
+ /**
+ * @prop {Boolean} gpuAcceleration=true
+ * If true, it uses the CSS 3D transformation to position the popper.
+ * Otherwise, it will use the `top` and `left` properties
+ */
+ gpuAcceleration: true,
+ /**
+ * @prop {string} [x='bottom']
+ * Where to anchor the X axis (`bottom` or `top`). AKA X offset origin.
+ * Change this if your popper should grow in a direction different from `bottom`
+ */
+ x: 'bottom',
+ /**
+ * @prop {string} [x='left']
+ * Where to anchor the Y axis (`left` or `right`). AKA Y offset origin.
+ * Change this if your popper should grow in a direction different from `right`
+ */
+ y: 'right'
+ },
+
+ /**
+ * Applies the computed styles to the popper element.
+ *
+ * All the DOM manipulations are limited to this modifier. This is useful in case
+ * you want to integrate Popper.js inside a framework or view library and you
+ * want to delegate all the DOM manipulations to it.
+ *
+ * Note that if you disable this modifier, you must make sure the popper element
+ * has its position set to `absolute` before Popper.js can do its work!
+ *
+ * Just disable this modifier and define your own to achieve the desired effect.
+ *
+ * @memberof modifiers
+ * @inner
+ */
+ applyStyle: {
+ /** @prop {number} order=900 - Index used to define the order of execution */
+ order: 900,
+ /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
+ enabled: true,
+ /** @prop {ModifierFn} */
+ fn: applyStyle,
+ /** @prop {Function} */
+ onLoad: applyStyleOnLoad,
+ /**
+ * @deprecated since version 1.10.0, the property moved to `computeStyle` modifier
+ * @prop {Boolean} gpuAcceleration=true
+ * If true, it uses the CSS 3D transformation to position the popper.
+ * Otherwise, it will use the `top` and `left` properties
+ */
+ gpuAcceleration: undefined
+ }
+ };
+
+ /**
+ * The `dataObject` is an object containing all the information used by Popper.js.
+ * This object is passed to modifiers and to the `onCreate` and `onUpdate` callbacks.
+ * @name dataObject
+ * @property {Object} data.instance The Popper.js instance
+ * @property {String} data.placement Placement applied to popper
+ * @property {String} data.originalPlacement Placement originally defined on init
+ * @property {Boolean} data.flipped True if popper has been flipped by flip modifier
+ * @property {Boolean} data.hide True if the reference element is out of boundaries, useful to know when to hide the popper
+ * @property {HTMLElement} data.arrowElement Node used as arrow by arrow modifier
+ * @property {Object} data.styles Any CSS property defined here will be applied to the popper. It expects the JavaScript nomenclature (eg. `marginBottom`)
+ * @property {Object} data.arrowStyles Any CSS property defined here will be applied to the popper arrow. It expects the JavaScript nomenclature (eg. `marginBottom`)
+ * @property {Object} data.boundaries Offsets of the popper boundaries
+ * @property {Object} data.offsets The measurements of popper, reference and arrow elements
+ * @property {Object} data.offsets.popper `top`, `left`, `width`, `height` values
+ * @property {Object} data.offsets.reference `top`, `left`, `width`, `height` values
+ * @property {Object} data.offsets.arrow] `top` and `left` offsets, only one of them will be different from 0
+ */
+
+ /**
+ * Default options provided to Popper.js constructor.
+ * These can be overridden using the `options` argument of Popper.js.
+ * To override an option, simply pass an object with the same
+ * structure of the `options` object, as the 3rd argument. For example:
+ * ```
+ * new Popper(ref, pop, {
+ * modifiers: {
+ * preventOverflow: { enabled: false }
+ * }
+ * })
+ * ```
+ * @type {Object}
+ * @static
+ * @memberof Popper
+ */
+ var Defaults = {
+ /**
+ * Popper's placement.
+ * @prop {Popper.placements} placement='bottom'
+ */
+ placement: 'bottom',
+
+ /**
+ * Set this to true if you want popper to position it self in 'fixed' mode
+ * @prop {Boolean} positionFixed=false
+ */
+ positionFixed: false,
+
+ /**
+ * Whether events (resize, scroll) are initially enabled.
+ * @prop {Boolean} eventsEnabled=true
+ */
+ eventsEnabled: true,
+
+ /**
+ * Set to true if you want to automatically remove the popper when
+ * you call the `destroy` method.
+ * @prop {Boolean} removeOnDestroy=false
+ */
+ removeOnDestroy: false,
+
+ /**
+ * Callback called when the popper is created.
+ * By default, it is set to no-op.
+ * Access Popper.js instance with `data.instance`.
+ * @prop {onCreate}
+ */
+ onCreate: function onCreate() {},
+
+ /**
+ * Callback called when the popper is updated. This callback is not called
+ * on the initialization/creation of the popper, but only on subsequent
+ * updates.
+ * By default, it is set to no-op.
+ * Access Popper.js instance with `data.instance`.
+ * @prop {onUpdate}
+ */
+ onUpdate: function onUpdate() {},
+
+ /**
+ * List of modifiers used to modify the offsets before they are applied to the popper.
+ * They provide most of the functionalities of Popper.js.
+ * @prop {modifiers}
+ */
+ modifiers: modifiers
+ };
+
+ /**
+ * @callback onCreate
+ * @param {dataObject} data
+ */
+
+ /**
+ * @callback onUpdate
+ * @param {dataObject} data
+ */
+
+ // Utils
+ // Methods
+ var Popper = function () {
+ /**
+ * Creates a new Popper.js instance.
+ * @class Popper
+ * @param {Element|referenceObject} reference - The reference element used to position the popper
+ * @param {Element} popper - The HTML / XML element used as the popper
+ * @param {Object} options - Your custom options to override the ones defined in [Defaults](#defaults)
+ * @return {Object} instance - The generated Popper.js instance
+ */
+ function Popper(reference, popper) {
+ var _this = this;
+
+ var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
+ classCallCheck(this, Popper);
+
+ this.scheduleUpdate = function () {
+ return requestAnimationFrame(_this.update);
+ };
+
+ // make update() debounced, so that it only runs at most once-per-tick
+ this.update = debounce(this.update.bind(this));
+
+ // with {} we create a new object with the options inside it
+ this.options = _extends$1({}, Popper.Defaults, options);
+
+ // init state
+ this.state = {
+ isDestroyed: false,
+ isCreated: false,
+ scrollParents: []
+ };
+
+ // get reference and popper elements (allow jQuery wrappers)
+ this.reference = reference && reference.jquery ? reference[0] : reference;
+ this.popper = popper && popper.jquery ? popper[0] : popper;
+
+ // Deep merge modifiers options
+ this.options.modifiers = {};
+ Object.keys(_extends$1({}, Popper.Defaults.modifiers, options.modifiers)).forEach(function (name) {
+ _this.options.modifiers[name] = _extends$1({}, Popper.Defaults.modifiers[name] || {}, options.modifiers ? options.modifiers[name] : {});
+ });
+
+ // Refactoring modifiers' list (Object => Array)
+ this.modifiers = Object.keys(this.options.modifiers).map(function (name) {
+ return _extends$1({
+ name: name
+ }, _this.options.modifiers[name]);
+ })
+ // sort the modifiers by order
+ .sort(function (a, b) {
+ return a.order - b.order;
+ });
+
+ // modifiers have the ability to execute arbitrary code when Popper.js get inited
+ // such code is executed in the same order of its modifier
+ // they could add new properties to their options configuration
+ // BE AWARE: don't add options to `options.modifiers.name` but to `modifierOptions`!
+ this.modifiers.forEach(function (modifierOptions) {
+ if (modifierOptions.enabled && isFunction(modifierOptions.onLoad)) {
+ modifierOptions.onLoad(_this.reference, _this.popper, _this.options, modifierOptions, _this.state);
+ }
+ });
+
+ // fire the first update to position the popper in the right place
+ this.update();
+
+ var eventsEnabled = this.options.eventsEnabled;
+ if (eventsEnabled) {
+ // setup event listeners, they will take care of update the position in specific situations
+ this.enableEventListeners();
+ }
+
+ this.state.eventsEnabled = eventsEnabled;
+ }
+
+ // We can't use class properties because they don't get listed in the
+ // class prototype and break stuff like Sinon stubs
+
+
+ createClass(Popper, [{
+ key: 'update',
+ value: function update$$1() {
+ return update.call(this);
+ }
+ }, {
+ key: 'destroy',
+ value: function destroy$$1() {
+ return destroy.call(this);
+ }
+ }, {
+ key: 'enableEventListeners',
+ value: function enableEventListeners$$1() {
+ return enableEventListeners.call(this);
+ }
+ }, {
+ key: 'disableEventListeners',
+ value: function disableEventListeners$$1() {
+ return disableEventListeners.call(this);
+ }
+
+ /**
+ * Schedules an update. It will run on the next UI update available.
+ * @method scheduleUpdate
+ * @memberof Popper
+ */
+
+
+ /**
+ * Collection of utilities useful when writing custom modifiers.
+ * Starting from version 1.7, this method is available only if you
+ * include `popper-utils.js` before `popper.js`.
+ *
+ * **DEPRECATION**: This way to access PopperUtils is deprecated
+ * and will be removed in v2! Use the PopperUtils module directly instead.
+ * Due to the high instability of the methods contained in Utils, we can't
+ * guarantee them to follow semver. Use them at your own risk!
+ * @static
+ * @private
+ * @type {Object}
+ * @deprecated since version 1.8
+ * @member Utils
+ * @memberof Popper
+ */
+
+ }]);
+ return Popper;
+ }();
+
+ /**
+ * The `referenceObject` is an object that provides an interface compatible with Popper.js
+ * and lets you use it as replacement of a real DOM node.
+ * You can use this method to position a popper relatively to a set of coordinates
+ * in case you don't have a DOM node to use as reference.
+ *
+ * ```
+ * new Popper(referenceObject, popperNode);
+ * ```
+ *
+ * NB: This feature isn't supported in Internet Explorer 10.
+ * @name referenceObject
+ * @property {Function} data.getBoundingClientRect
+ * A function that returns a set of coordinates compatible with the native `getBoundingClientRect` method.
+ * @property {number} data.clientWidth
+ * An ES6 getter that will return the width of the virtual reference element.
+ * @property {number} data.clientHeight
+ * An ES6 getter that will return the height of the virtual reference element.
+ */
+
+
+ Popper.Utils = (typeof window !== 'undefined' ? window : global).PopperUtils;
+ Popper.placements = placements;
+ Popper.Defaults = Defaults;
+
+ /**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+ var NAME$4 = 'dropdown';
+ var VERSION$4 = '4.5.3';
+ var DATA_KEY$4 = 'bs.dropdown';
+ var EVENT_KEY$4 = "." + DATA_KEY$4;
+ var DATA_API_KEY$4 = '.data-api';
+ var JQUERY_NO_CONFLICT$4 = $__default['default'].fn[NAME$4];
+ var ESCAPE_KEYCODE = 27; // KeyboardEvent.which value for Escape (Esc) key
+
+ var SPACE_KEYCODE = 32; // KeyboardEvent.which value for space key
+
+ var TAB_KEYCODE = 9; // KeyboardEvent.which value for tab key
+
+ var ARROW_UP_KEYCODE = 38; // KeyboardEvent.which value for up arrow key
+
+ var ARROW_DOWN_KEYCODE = 40; // KeyboardEvent.which value for down arrow key
+
+ var RIGHT_MOUSE_BUTTON_WHICH = 3; // MouseEvent.which value for the right button (assuming a right-handed mouse)
+
+ var REGEXP_KEYDOWN = new RegExp(ARROW_UP_KEYCODE + "|" + ARROW_DOWN_KEYCODE + "|" + ESCAPE_KEYCODE);
+ var EVENT_HIDE$1 = "hide" + EVENT_KEY$4;
+ var EVENT_HIDDEN$1 = "hidden" + EVENT_KEY$4;
+ var EVENT_SHOW$1 = "show" + EVENT_KEY$4;
+ var EVENT_SHOWN$1 = "shown" + EVENT_KEY$4;
+ var EVENT_CLICK = "click" + EVENT_KEY$4;
+ var EVENT_CLICK_DATA_API$4 = "click" + EVENT_KEY$4 + DATA_API_KEY$4;
+ var EVENT_KEYDOWN_DATA_API = "keydown" + EVENT_KEY$4 + DATA_API_KEY$4;
+ var EVENT_KEYUP_DATA_API = "keyup" + EVENT_KEY$4 + DATA_API_KEY$4;
+ var CLASS_NAME_DISABLED = 'disabled';
+ var CLASS_NAME_SHOW$2 = 'show';
+ var CLASS_NAME_DROPUP = 'dropup';
+ var CLASS_NAME_DROPRIGHT = 'dropright';
+ var CLASS_NAME_DROPLEFT = 'dropleft';
+ var CLASS_NAME_MENURIGHT = 'dropdown-menu-right';
+ var CLASS_NAME_POSITION_STATIC = 'position-static';
+ var SELECTOR_DATA_TOGGLE$2 = '[data-toggle="dropdown"]';
+ var SELECTOR_FORM_CHILD = '.dropdown form';
+ var SELECTOR_MENU = '.dropdown-menu';
+ var SELECTOR_NAVBAR_NAV = '.navbar-nav';
+ var SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)';
+ var PLACEMENT_TOP = 'top-start';
+ var PLACEMENT_TOPEND = 'top-end';
+ var PLACEMENT_BOTTOM = 'bottom-start';
+ var PLACEMENT_BOTTOMEND = 'bottom-end';
+ var PLACEMENT_RIGHT = 'right-start';
+ var PLACEMENT_LEFT = 'left-start';
+ var Default$2 = {
+ offset: 0,
+ flip: true,
+ boundary: 'scrollParent',
+ reference: 'toggle',
+ display: 'dynamic',
+ popperConfig: null
+ };
+ var DefaultType$2 = {
+ offset: '(number|string|function)',
+ flip: 'boolean',
+ boundary: '(string|element)',
+ reference: '(string|element)',
+ display: 'string',
+ popperConfig: '(null|object)'
+ };
+ /**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+ var Dropdown = /*#__PURE__*/function () {
+ function Dropdown(element, config) {
+ this._element = element;
+ this._popper = null;
+ this._config = this._getConfig(config);
+ this._menu = this._getMenuElement();
+ this._inNavbar = this._detectNavbar();
+
+ this._addEventListeners();
+ } // Getters
+
+
+ var _proto = Dropdown.prototype;
+
+ // Public
+ _proto.toggle = function toggle() {
+ if (this._element.disabled || $__default['default'](this._element).hasClass(CLASS_NAME_DISABLED)) {
+ return;
+ }
+
+ var isActive = $__default['default'](this._menu).hasClass(CLASS_NAME_SHOW$2);
+
+ Dropdown._clearMenus();
+
+ if (isActive) {
+ return;
+ }
+
+ this.show(true);
+ };
+
+ _proto.show = function show(usePopper) {
+ if (usePopper === void 0) {
+ usePopper = false;
+ }
+
+ if (this._element.disabled || $__default['default'](this._element).hasClass(CLASS_NAME_DISABLED) || $__default['default'](this._menu).hasClass(CLASS_NAME_SHOW$2)) {
+ return;
+ }
+
+ var relatedTarget = {
+ relatedTarget: this._element
+ };
+ var showEvent = $__default['default'].Event(EVENT_SHOW$1, relatedTarget);
+
+ var parent = Dropdown._getParentFromElement(this._element);
+
+ $__default['default'](parent).trigger(showEvent);
+
+ if (showEvent.isDefaultPrevented()) {
+ return;
+ } // Disable totally Popper.js for Dropdown in Navbar
+
+
+ if (!this._inNavbar && usePopper) {
+ /**
+ * Check for Popper dependency
+ * Popper - https://popper.js.org
+ */
+ if (typeof Popper === 'undefined') {
+ throw new TypeError('Bootstrap\'s dropdowns require Popper.js (https://popper.js.org/)');
+ }
+
+ var referenceElement = this._element;
+
+ if (this._config.reference === 'parent') {
+ referenceElement = parent;
+ } else if (Util.isElement(this._config.reference)) {
+ referenceElement = this._config.reference; // Check if it's jQuery element
+
+ if (typeof this._config.reference.jquery !== 'undefined') {
+ referenceElement = this._config.reference[0];
+ }
+ } // If boundary is not `scrollParent`, then set position to `static`
+ // to allow the menu to "escape" the scroll parent's boundaries
+ // https://github.com/twbs/bootstrap/issues/24251
+
+
+ if (this._config.boundary !== 'scrollParent') {
+ $__default['default'](parent).addClass(CLASS_NAME_POSITION_STATIC);
+ }
+
+ this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig());
+ } // If this is a touch-enabled device we add extra
+ // empty mouseover listeners to the body's immediate children;
+ // only needed because of broken event delegation on iOS
+ // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
+
+
+ if ('ontouchstart' in document.documentElement && $__default['default'](parent).closest(SELECTOR_NAVBAR_NAV).length === 0) {
+ $__default['default'](document.body).children().on('mouseover', null, $__default['default'].noop);
+ }
+
+ this._element.focus();
+
+ this._element.setAttribute('aria-expanded', true);
+
+ $__default['default'](this._menu).toggleClass(CLASS_NAME_SHOW$2);
+ $__default['default'](parent).toggleClass(CLASS_NAME_SHOW$2).trigger($__default['default'].Event(EVENT_SHOWN$1, relatedTarget));
+ };
+
+ _proto.hide = function hide() {
+ if (this._element.disabled || $__default['default'](this._element).hasClass(CLASS_NAME_DISABLED) || !$__default['default'](this._menu).hasClass(CLASS_NAME_SHOW$2)) {
+ return;
+ }
+
+ var relatedTarget = {
+ relatedTarget: this._element
+ };
+ var hideEvent = $__default['default'].Event(EVENT_HIDE$1, relatedTarget);
+
+ var parent = Dropdown._getParentFromElement(this._element);
+
+ $__default['default'](parent).trigger(hideEvent);
+
+ if (hideEvent.isDefaultPrevented()) {
+ return;
+ }
+
+ if (this._popper) {
+ this._popper.destroy();
+ }
+
+ $__default['default'](this._menu).toggleClass(CLASS_NAME_SHOW$2);
+ $__default['default'](parent).toggleClass(CLASS_NAME_SHOW$2).trigger($__default['default'].Event(EVENT_HIDDEN$1, relatedTarget));
+ };
+
+ _proto.dispose = function dispose() {
+ $__default['default'].removeData(this._element, DATA_KEY$4);
+ $__default['default'](this._element).off(EVENT_KEY$4);
+ this._element = null;
+ this._menu = null;
+
+ if (this._popper !== null) {
+ this._popper.destroy();
+
+ this._popper = null;
+ }
+ };
+
+ _proto.update = function update() {
+ this._inNavbar = this._detectNavbar();
+
+ if (this._popper !== null) {
+ this._popper.scheduleUpdate();
+ }
+ } // Private
+ ;
+
+ _proto._addEventListeners = function _addEventListeners() {
+ var _this = this;
+
+ $__default['default'](this._element).on(EVENT_CLICK, function (event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ _this.toggle();
+ });
+ };
+
+ _proto._getConfig = function _getConfig(config) {
+ config = _extends({}, this.constructor.Default, $__default['default'](this._element).data(), config);
+ Util.typeCheckConfig(NAME$4, config, this.constructor.DefaultType);
+ return config;
+ };
+
+ _proto._getMenuElement = function _getMenuElement() {
+ if (!this._menu) {
+ var parent = Dropdown._getParentFromElement(this._element);
+
+ if (parent) {
+ this._menu = parent.querySelector(SELECTOR_MENU);
+ }
+ }
+
+ return this._menu;
+ };
+
+ _proto._getPlacement = function _getPlacement() {
+ var $parentDropdown = $__default['default'](this._element.parentNode);
+ var placement = PLACEMENT_BOTTOM; // Handle dropup
+
+ if ($parentDropdown.hasClass(CLASS_NAME_DROPUP)) {
+ placement = $__default['default'](this._menu).hasClass(CLASS_NAME_MENURIGHT) ? PLACEMENT_TOPEND : PLACEMENT_TOP;
+ } else if ($parentDropdown.hasClass(CLASS_NAME_DROPRIGHT)) {
+ placement = PLACEMENT_RIGHT;
+ } else if ($parentDropdown.hasClass(CLASS_NAME_DROPLEFT)) {
+ placement = PLACEMENT_LEFT;
+ } else if ($__default['default'](this._menu).hasClass(CLASS_NAME_MENURIGHT)) {
+ placement = PLACEMENT_BOTTOMEND;
+ }
+
+ return placement;
+ };
+
+ _proto._detectNavbar = function _detectNavbar() {
+ return $__default['default'](this._element).closest('.navbar').length > 0;
+ };
+
+ _proto._getOffset = function _getOffset() {
+ var _this2 = this;
+
+ var offset = {};
+
+ if (typeof this._config.offset === 'function') {
+ offset.fn = function (data) {
+ data.offsets = _extends({}, data.offsets, _this2._config.offset(data.offsets, _this2._element) || {});
+ return data;
+ };
+ } else {
+ offset.offset = this._config.offset;
+ }
+
+ return offset;
+ };
+
+ _proto._getPopperConfig = function _getPopperConfig() {
+ var popperConfig = {
+ placement: this._getPlacement(),
+ modifiers: {
+ offset: this._getOffset(),
+ flip: {
+ enabled: this._config.flip
+ },
+ preventOverflow: {
+ boundariesElement: this._config.boundary
+ }
+ }
+ }; // Disable Popper.js if we have a static display
+
+ if (this._config.display === 'static') {
+ popperConfig.modifiers.applyStyle = {
+ enabled: false
+ };
+ }
+
+ return _extends({}, popperConfig, this._config.popperConfig);
+ } // Static
+ ;
+
+ Dropdown._jQueryInterface = function _jQueryInterface(config) {
+ return this.each(function () {
+ var data = $__default['default'](this).data(DATA_KEY$4);
+
+ var _config = typeof config === 'object' ? config : null;
+
+ if (!data) {
+ data = new Dropdown(this, _config);
+ $__default['default'](this).data(DATA_KEY$4, data);
+ }
+
+ if (typeof config === 'string') {
+ if (typeof data[config] === 'undefined') {
+ throw new TypeError("No method named \"" + config + "\"");
+ }
+
+ data[config]();
+ }
+ });
+ };
+
+ Dropdown._clearMenus = function _clearMenus(event) {
+ if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH || event.type === 'keyup' && event.which !== TAB_KEYCODE)) {
+ return;
+ }
+
+ var toggles = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE$2));
+
+ for (var i = 0, len = toggles.length; i < len; i++) {
+ var parent = Dropdown._getParentFromElement(toggles[i]);
+
+ var context = $__default['default'](toggles[i]).data(DATA_KEY$4);
+ var relatedTarget = {
+ relatedTarget: toggles[i]
+ };
+
+ if (event && event.type === 'click') {
+ relatedTarget.clickEvent = event;
+ }
+
+ if (!context) {
+ continue;
+ }
+
+ var dropdownMenu = context._menu;
+
+ if (!$__default['default'](parent).hasClass(CLASS_NAME_SHOW$2)) {
+ continue;
+ }
+
+ if (event && (event.type === 'click' && /input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) && $__default['default'].contains(parent, event.target)) {
+ continue;
+ }
+
+ var hideEvent = $__default['default'].Event(EVENT_HIDE$1, relatedTarget);
+ $__default['default'](parent).trigger(hideEvent);
+
+ if (hideEvent.isDefaultPrevented()) {
+ continue;
+ } // If this is a touch-enabled device we remove the extra
+ // empty mouseover listeners we added for iOS support
+
+
+ if ('ontouchstart' in document.documentElement) {
+ $__default['default'](document.body).children().off('mouseover', null, $__default['default'].noop);
+ }
+
+ toggles[i].setAttribute('aria-expanded', 'false');
+
+ if (context._popper) {
+ context._popper.destroy();
+ }
+
+ $__default['default'](dropdownMenu).removeClass(CLASS_NAME_SHOW$2);
+ $__default['default'](parent).removeClass(CLASS_NAME_SHOW$2).trigger($__default['default'].Event(EVENT_HIDDEN$1, relatedTarget));
+ }
+ };
+
+ Dropdown._getParentFromElement = function _getParentFromElement(element) {
+ var parent;
+ var selector = Util.getSelectorFromElement(element);
+
+ if (selector) {
+ parent = document.querySelector(selector);
+ }
+
+ return parent || element.parentNode;
+ } // eslint-disable-next-line complexity
+ ;
+
+ Dropdown._dataApiKeydownHandler = function _dataApiKeydownHandler(event) {
+ // If not input/textarea:
+ // - And not a key in REGEXP_KEYDOWN => not a dropdown command
+ // If input/textarea:
+ // - If space key => not a dropdown command
+ // - If key is other than escape
+ // - If key is not up or down => not a dropdown command
+ // - If trigger inside the menu => not a dropdown command
+ if (/input|textarea/i.test(event.target.tagName) ? event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE && (event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE || $__default['default'](event.target).closest(SELECTOR_MENU).length) : !REGEXP_KEYDOWN.test(event.which)) {
+ return;
+ }
+
+ if (this.disabled || $__default['default'](this).hasClass(CLASS_NAME_DISABLED)) {
+ return;
+ }
+
+ var parent = Dropdown._getParentFromElement(this);
+
+ var isActive = $__default['default'](parent).hasClass(CLASS_NAME_SHOW$2);
+
+ if (!isActive && event.which === ESCAPE_KEYCODE) {
+ return;
+ }
+
+ event.preventDefault();
+ event.stopPropagation();
+
+ if (!isActive || event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE) {
+ if (event.which === ESCAPE_KEYCODE) {
+ $__default['default'](parent.querySelector(SELECTOR_DATA_TOGGLE$2)).trigger('focus');
+ }
+
+ $__default['default'](this).trigger('click');
+ return;
+ }
+
+ var items = [].slice.call(parent.querySelectorAll(SELECTOR_VISIBLE_ITEMS)).filter(function (item) {
+ return $__default['default'](item).is(':visible');
+ });
+
+ if (items.length === 0) {
+ return;
+ }
+
+ var index = items.indexOf(event.target);
+
+ if (event.which === ARROW_UP_KEYCODE && index > 0) {
+ // Up
+ index--;
+ }
+
+ if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) {
+ // Down
+ index++;
+ }
+
+ if (index < 0) {
+ index = 0;
+ }
+
+ items[index].focus();
+ };
+
+ _createClass(Dropdown, null, [{
+ key: "VERSION",
+ get: function get() {
+ return VERSION$4;
+ }
+ }, {
+ key: "Default",
+ get: function get() {
+ return Default$2;
+ }
+ }, {
+ key: "DefaultType",
+ get: function get() {
+ return DefaultType$2;
+ }
+ }]);
+
+ return Dropdown;
+ }();
+ /**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+
+ $__default['default'](document).on(EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE$2, Dropdown._dataApiKeydownHandler).on(EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown._dataApiKeydownHandler).on(EVENT_CLICK_DATA_API$4 + " " + EVENT_KEYUP_DATA_API, Dropdown._clearMenus).on(EVENT_CLICK_DATA_API$4, SELECTOR_DATA_TOGGLE$2, function (event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ Dropdown._jQueryInterface.call($__default['default'](this), 'toggle');
+ }).on(EVENT_CLICK_DATA_API$4, SELECTOR_FORM_CHILD, function (e) {
+ e.stopPropagation();
+ });
+ /**
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ */
+
+ $__default['default'].fn[NAME$4] = Dropdown._jQueryInterface;
+ $__default['default'].fn[NAME$4].Constructor = Dropdown;
+
+ $__default['default'].fn[NAME$4].noConflict = function () {
+ $__default['default'].fn[NAME$4] = JQUERY_NO_CONFLICT$4;
+ return Dropdown._jQueryInterface;
+ };
+
+ /**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+ var NAME$5 = 'modal';
+ var VERSION$5 = '4.5.3';
+ var DATA_KEY$5 = 'bs.modal';
+ var EVENT_KEY$5 = "." + DATA_KEY$5;
+ var DATA_API_KEY$5 = '.data-api';
+ var JQUERY_NO_CONFLICT$5 = $__default['default'].fn[NAME$5];
+ var ESCAPE_KEYCODE$1 = 27; // KeyboardEvent.which value for Escape (Esc) key
+
+ var Default$3 = {
+ backdrop: true,
+ keyboard: true,
+ focus: true,
+ show: true
+ };
+ var DefaultType$3 = {
+ backdrop: '(boolean|string)',
+ keyboard: 'boolean',
+ focus: 'boolean',
+ show: 'boolean'
+ };
+ var EVENT_HIDE$2 = "hide" + EVENT_KEY$5;
+ var EVENT_HIDE_PREVENTED = "hidePrevented" + EVENT_KEY$5;
+ var EVENT_HIDDEN$2 = "hidden" + EVENT_KEY$5;
+ var EVENT_SHOW$2 = "show" + EVENT_KEY$5;
+ var EVENT_SHOWN$2 = "shown" + EVENT_KEY$5;
+ var EVENT_FOCUSIN = "focusin" + EVENT_KEY$5;
+ var EVENT_RESIZE = "resize" + EVENT_KEY$5;
+ var EVENT_CLICK_DISMISS = "click.dismiss" + EVENT_KEY$5;
+ var EVENT_KEYDOWN_DISMISS = "keydown.dismiss" + EVENT_KEY$5;
+ var EVENT_MOUSEUP_DISMISS = "mouseup.dismiss" + EVENT_KEY$5;
+ var EVENT_MOUSEDOWN_DISMISS = "mousedown.dismiss" + EVENT_KEY$5;
+ var EVENT_CLICK_DATA_API$5 = "click" + EVENT_KEY$5 + DATA_API_KEY$5;
+ var CLASS_NAME_SCROLLABLE = 'modal-dialog-scrollable';
+ var CLASS_NAME_SCROLLBAR_MEASURER = 'modal-scrollbar-measure';
+ var CLASS_NAME_BACKDROP = 'modal-backdrop';
+ var CLASS_NAME_OPEN = 'modal-open';
+ var CLASS_NAME_FADE$1 = 'fade';
+ var CLASS_NAME_SHOW$3 = 'show';
+ var CLASS_NAME_STATIC = 'modal-static';
+ var SELECTOR_DIALOG = '.modal-dialog';
+ var SELECTOR_MODAL_BODY = '.modal-body';
+ var SELECTOR_DATA_TOGGLE$3 = '[data-toggle="modal"]';
+ var SELECTOR_DATA_DISMISS = '[data-dismiss="modal"]';
+ var SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top';
+ var SELECTOR_STICKY_CONTENT = '.sticky-top';
+ /**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+ var Modal = /*#__PURE__*/function () {
+ function Modal(element, config) {
+ this._config = this._getConfig(config);
+ this._element = element;
+ this._dialog = element.querySelector(SELECTOR_DIALOG);
+ this._backdrop = null;
+ this._isShown = false;
+ this._isBodyOverflowing = false;
+ this._ignoreBackdropClick = false;
+ this._isTransitioning = false;
+ this._scrollbarWidth = 0;
+ } // Getters
+
+
+ var _proto = Modal.prototype;
+
+ // Public
+ _proto.toggle = function toggle(relatedTarget) {
+ return this._isShown ? this.hide() : this.show(relatedTarget);
+ };
+
+ _proto.show = function show(relatedTarget) {
+ var _this = this;
+
+ if (this._isShown || this._isTransitioning) {
+ return;
+ }
+
+ if ($__default['default'](this._element).hasClass(CLASS_NAME_FADE$1)) {
+ this._isTransitioning = true;
+ }
+
+ var showEvent = $__default['default'].Event(EVENT_SHOW$2, {
+ relatedTarget: relatedTarget
+ });
+ $__default['default'](this._element).trigger(showEvent);
+
+ if (this._isShown || showEvent.isDefaultPrevented()) {
+ return;
+ }
+
+ this._isShown = true;
+
+ this._checkScrollbar();
+
+ this._setScrollbar();
+
+ this._adjustDialog();
+
+ this._setEscapeEvent();
+
+ this._setResizeEvent();
+
+ $__default['default'](this._element).on(EVENT_CLICK_DISMISS, SELECTOR_DATA_DISMISS, function (event) {
+ return _this.hide(event);
+ });
+ $__default['default'](this._dialog).on(EVENT_MOUSEDOWN_DISMISS, function () {
+ $__default['default'](_this._element).one(EVENT_MOUSEUP_DISMISS, function (event) {
+ if ($__default['default'](event.target).is(_this._element)) {
+ _this._ignoreBackdropClick = true;
+ }
+ });
+ });
+
+ this._showBackdrop(function () {
+ return _this._showElement(relatedTarget);
+ });
+ };
+
+ _proto.hide = function hide(event) {
+ var _this2 = this;
+
+ if (event) {
+ event.preventDefault();
+ }
+
+ if (!this._isShown || this._isTransitioning) {
+ return;
+ }
+
+ var hideEvent = $__default['default'].Event(EVENT_HIDE$2);
+ $__default['default'](this._element).trigger(hideEvent);
+
+ if (!this._isShown || hideEvent.isDefaultPrevented()) {
+ return;
+ }
+
+ this._isShown = false;
+ var transition = $__default['default'](this._element).hasClass(CLASS_NAME_FADE$1);
+
+ if (transition) {
+ this._isTransitioning = true;
+ }
+
+ this._setEscapeEvent();
+
+ this._setResizeEvent();
+
+ $__default['default'](document).off(EVENT_FOCUSIN);
+ $__default['default'](this._element).removeClass(CLASS_NAME_SHOW$3);
+ $__default['default'](this._element).off(EVENT_CLICK_DISMISS);
+ $__default['default'](this._dialog).off(EVENT_MOUSEDOWN_DISMISS);
+
+ if (transition) {
+ var transitionDuration = Util.getTransitionDurationFromElement(this._element);
+ $__default['default'](this._element).one(Util.TRANSITION_END, function (event) {
+ return _this2._hideModal(event);
+ }).emulateTransitionEnd(transitionDuration);
+ } else {
+ this._hideModal();
+ }
+ };
+
+ _proto.dispose = function dispose() {
+ [window, this._element, this._dialog].forEach(function (htmlElement) {
+ return $__default['default'](htmlElement).off(EVENT_KEY$5);
+ });
+ /**
+ * `document` has 2 events `EVENT_FOCUSIN` and `EVENT_CLICK_DATA_API`
+ * Do not move `document` in `htmlElements` array
+ * It will remove `EVENT_CLICK_DATA_API` event that should remain
+ */
+
+ $__default['default'](document).off(EVENT_FOCUSIN);
+ $__default['default'].removeData(this._element, DATA_KEY$5);
+ this._config = null;
+ this._element = null;
+ this._dialog = null;
+ this._backdrop = null;
+ this._isShown = null;
+ this._isBodyOverflowing = null;
+ this._ignoreBackdropClick = null;
+ this._isTransitioning = null;
+ this._scrollbarWidth = null;
+ };
+
+ _proto.handleUpdate = function handleUpdate() {
+ this._adjustDialog();
+ } // Private
+ ;
+
+ _proto._getConfig = function _getConfig(config) {
+ config = _extends({}, Default$3, config);
+ Util.typeCheckConfig(NAME$5, config, DefaultType$3);
+ return config;
+ };
+
+ _proto._triggerBackdropTransition = function _triggerBackdropTransition() {
+ var _this3 = this;
+
+ if (this._config.backdrop === 'static') {
+ var hideEventPrevented = $__default['default'].Event(EVENT_HIDE_PREVENTED);
+ $__default['default'](this._element).trigger(hideEventPrevented);
+
+ if (hideEventPrevented.isDefaultPrevented()) {
+ return;
+ }
+
+ var isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;
+
+ if (!isModalOverflowing) {
+ this._element.style.overflowY = 'hidden';
+ }
+
+ this._element.classList.add(CLASS_NAME_STATIC);
+
+ var modalTransitionDuration = Util.getTransitionDurationFromElement(this._dialog);
+ $__default['default'](this._element).off(Util.TRANSITION_END);
+ $__default['default'](this._element).one(Util.TRANSITION_END, function () {
+ _this3._element.classList.remove(CLASS_NAME_STATIC);
+
+ if (!isModalOverflowing) {
+ $__default['default'](_this3._element).one(Util.TRANSITION_END, function () {
+ _this3._element.style.overflowY = '';
+ }).emulateTransitionEnd(_this3._element, modalTransitionDuration);
+ }
+ }).emulateTransitionEnd(modalTransitionDuration);
+
+ this._element.focus();
+ } else {
+ this.hide();
+ }
+ };
+
+ _proto._showElement = function _showElement(relatedTarget) {
+ var _this4 = this;
+
+ var transition = $__default['default'](this._element).hasClass(CLASS_NAME_FADE$1);
+ var modalBody = this._dialog ? this._dialog.querySelector(SELECTOR_MODAL_BODY) : null;
+
+ if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {
+ // Don't move modal's DOM position
+ document.body.appendChild(this._element);
+ }
+
+ this._element.style.display = 'block';
+
+ this._element.removeAttribute('aria-hidden');
+
+ this._element.setAttribute('aria-modal', true);
+
+ this._element.setAttribute('role', 'dialog');
+
+ if ($__default['default'](this._dialog).hasClass(CLASS_NAME_SCROLLABLE) && modalBody) {
+ modalBody.scrollTop = 0;
+ } else {
+ this._element.scrollTop = 0;
+ }
+
+ if (transition) {
+ Util.reflow(this._element);
+ }
+
+ $__default['default'](this._element).addClass(CLASS_NAME_SHOW$3);
+
+ if (this._config.focus) {
+ this._enforceFocus();
+ }
+
+ var shownEvent = $__default['default'].Event(EVENT_SHOWN$2, {
+ relatedTarget: relatedTarget
+ });
+
+ var transitionComplete = function transitionComplete() {
+ if (_this4._config.focus) {
+ _this4._element.focus();
+ }
+
+ _this4._isTransitioning = false;
+ $__default['default'](_this4._element).trigger(shownEvent);
+ };
+
+ if (transition) {
+ var transitionDuration = Util.getTransitionDurationFromElement(this._dialog);
+ $__default['default'](this._dialog).one(Util.TRANSITION_END, transitionComplete).emulateTransitionEnd(transitionDuration);
+ } else {
+ transitionComplete();
+ }
+ };
+
+ _proto._enforceFocus = function _enforceFocus() {
+ var _this5 = this;
+
+ $__default['default'](document).off(EVENT_FOCUSIN) // Guard against infinite focus loop
+ .on(EVENT_FOCUSIN, function (event) {
+ if (document !== event.target && _this5._element !== event.target && $__default['default'](_this5._element).has(event.target).length === 0) {
+ _this5._element.focus();
+ }
+ });
+ };
+
+ _proto._setEscapeEvent = function _setEscapeEvent() {
+ var _this6 = this;
+
+ if (this._isShown) {
+ $__default['default'](this._element).on(EVENT_KEYDOWN_DISMISS, function (event) {
+ if (_this6._config.keyboard && event.which === ESCAPE_KEYCODE$1) {
+ event.preventDefault();
+
+ _this6.hide();
+ } else if (!_this6._config.keyboard && event.which === ESCAPE_KEYCODE$1) {
+ _this6._triggerBackdropTransition();
+ }
+ });
+ } else if (!this._isShown) {
+ $__default['default'](this._element).off(EVENT_KEYDOWN_DISMISS);
+ }
+ };
+
+ _proto._setResizeEvent = function _setResizeEvent() {
+ var _this7 = this;
+
+ if (this._isShown) {
+ $__default['default'](window).on(EVENT_RESIZE, function (event) {
+ return _this7.handleUpdate(event);
+ });
+ } else {
+ $__default['default'](window).off(EVENT_RESIZE);
+ }
+ };
+
+ _proto._hideModal = function _hideModal() {
+ var _this8 = this;
+
+ this._element.style.display = 'none';
+
+ this._element.setAttribute('aria-hidden', true);
+
+ this._element.removeAttribute('aria-modal');
+
+ this._element.removeAttribute('role');
+
+ this._isTransitioning = false;
+
+ this._showBackdrop(function () {
+ $__default['default'](document.body).removeClass(CLASS_NAME_OPEN);
+
+ _this8._resetAdjustments();
+
+ _this8._resetScrollbar();
+
+ $__default['default'](_this8._element).trigger(EVENT_HIDDEN$2);
+ });
+ };
+
+ _proto._removeBackdrop = function _removeBackdrop() {
+ if (this._backdrop) {
+ $__default['default'](this._backdrop).remove();
+ this._backdrop = null;
+ }
+ };
+
+ _proto._showBackdrop = function _showBackdrop(callback) {
+ var _this9 = this;
+
+ var animate = $__default['default'](this._element).hasClass(CLASS_NAME_FADE$1) ? CLASS_NAME_FADE$1 : '';
+
+ if (this._isShown && this._config.backdrop) {
+ this._backdrop = document.createElement('div');
+ this._backdrop.className = CLASS_NAME_BACKDROP;
+
+ if (animate) {
+ this._backdrop.classList.add(animate);
+ }
+
+ $__default['default'](this._backdrop).appendTo(document.body);
+ $__default['default'](this._element).on(EVENT_CLICK_DISMISS, function (event) {
+ if (_this9._ignoreBackdropClick) {
+ _this9._ignoreBackdropClick = false;
+ return;
+ }
+
+ if (event.target !== event.currentTarget) {
+ return;
+ }
+
+ _this9._triggerBackdropTransition();
+ });
+
+ if (animate) {
+ Util.reflow(this._backdrop);
+ }
+
+ $__default['default'](this._backdrop).addClass(CLASS_NAME_SHOW$3);
+
+ if (!callback) {
+ return;
+ }
+
+ if (!animate) {
+ callback();
+ return;
+ }
+
+ var backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop);
+ $__default['default'](this._backdrop).one(Util.TRANSITION_END, callback).emulateTransitionEnd(backdropTransitionDuration);
+ } else if (!this._isShown && this._backdrop) {
+ $__default['default'](this._backdrop).removeClass(CLASS_NAME_SHOW$3);
+
+ var callbackRemove = function callbackRemove() {
+ _this9._removeBackdrop();
+
+ if (callback) {
+ callback();
+ }
+ };
+
+ if ($__default['default'](this._element).hasClass(CLASS_NAME_FADE$1)) {
+ var _backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop);
+
+ $__default['default'](this._backdrop).one(Util.TRANSITION_END, callbackRemove).emulateTransitionEnd(_backdropTransitionDuration);
+ } else {
+ callbackRemove();
+ }
+ } else if (callback) {
+ callback();
+ }
+ } // ----------------------------------------------------------------------
+ // the following methods are used to handle overflowing modals
+ // todo (fat): these should probably be refactored out of modal.js
+ // ----------------------------------------------------------------------
+ ;
+
+ _proto._adjustDialog = function _adjustDialog() {
+ var isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;
+
+ if (!this._isBodyOverflowing && isModalOverflowing) {
+ this._element.style.paddingLeft = this._scrollbarWidth + "px";
+ }
+
+ if (this._isBodyOverflowing && !isModalOverflowing) {
+ this._element.style.paddingRight = this._scrollbarWidth + "px";
+ }
+ };
+
+ _proto._resetAdjustments = function _resetAdjustments() {
+ this._element.style.paddingLeft = '';
+ this._element.style.paddingRight = '';
+ };
+
+ _proto._checkScrollbar = function _checkScrollbar() {
+ var rect = document.body.getBoundingClientRect();
+ this._isBodyOverflowing = Math.round(rect.left + rect.right) < window.innerWidth;
+ this._scrollbarWidth = this._getScrollbarWidth();
+ };
+
+ _proto._setScrollbar = function _setScrollbar() {
+ var _this10 = this;
+
+ if (this._isBodyOverflowing) {
+ // Note: DOMNode.style.paddingRight returns the actual value or '' if not set
+ // while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set
+ var fixedContent = [].slice.call(document.querySelectorAll(SELECTOR_FIXED_CONTENT));
+ var stickyContent = [].slice.call(document.querySelectorAll(SELECTOR_STICKY_CONTENT)); // Adjust fixed content padding
+
+ $__default['default'](fixedContent).each(function (index, element) {
+ var actualPadding = element.style.paddingRight;
+ var calculatedPadding = $__default['default'](element).css('padding-right');
+ $__default['default'](element).data('padding-right', actualPadding).css('padding-right', parseFloat(calculatedPadding) + _this10._scrollbarWidth + "px");
+ }); // Adjust sticky content margin
+
+ $__default['default'](stickyContent).each(function (index, element) {
+ var actualMargin = element.style.marginRight;
+ var calculatedMargin = $__default['default'](element).css('margin-right');
+ $__default['default'](element).data('margin-right', actualMargin).css('margin-right', parseFloat(calculatedMargin) - _this10._scrollbarWidth + "px");
+ }); // Adjust body padding
+
+ var actualPadding = document.body.style.paddingRight;
+ var calculatedPadding = $__default['default'](document.body).css('padding-right');
+ $__default['default'](document.body).data('padding-right', actualPadding).css('padding-right', parseFloat(calculatedPadding) + this._scrollbarWidth + "px");
+ }
+
+ $__default['default'](document.body).addClass(CLASS_NAME_OPEN);
+ };
+
+ _proto._resetScrollbar = function _resetScrollbar() {
+ // Restore fixed content padding
+ var fixedContent = [].slice.call(document.querySelectorAll(SELECTOR_FIXED_CONTENT));
+ $__default['default'](fixedContent).each(function (index, element) {
+ var padding = $__default['default'](element).data('padding-right');
+ $__default['default'](element).removeData('padding-right');
+ element.style.paddingRight = padding ? padding : '';
+ }); // Restore sticky content
+
+ var elements = [].slice.call(document.querySelectorAll("" + SELECTOR_STICKY_CONTENT));
+ $__default['default'](elements).each(function (index, element) {
+ var margin = $__default['default'](element).data('margin-right');
+
+ if (typeof margin !== 'undefined') {
+ $__default['default'](element).css('margin-right', margin).removeData('margin-right');
+ }
+ }); // Restore body padding
+
+ var padding = $__default['default'](document.body).data('padding-right');
+ $__default['default'](document.body).removeData('padding-right');
+ document.body.style.paddingRight = padding ? padding : '';
+ };
+
+ _proto._getScrollbarWidth = function _getScrollbarWidth() {
+ // thx d.walsh
+ var scrollDiv = document.createElement('div');
+ scrollDiv.className = CLASS_NAME_SCROLLBAR_MEASURER;
+ document.body.appendChild(scrollDiv);
+ var scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth;
+ document.body.removeChild(scrollDiv);
+ return scrollbarWidth;
+ } // Static
+ ;
+
+ Modal._jQueryInterface = function _jQueryInterface(config, relatedTarget) {
+ return this.each(function () {
+ var data = $__default['default'](this).data(DATA_KEY$5);
+
+ var _config = _extends({}, Default$3, $__default['default'](this).data(), typeof config === 'object' && config ? config : {});
+
+ if (!data) {
+ data = new Modal(this, _config);
+ $__default['default'](this).data(DATA_KEY$5, data);
+ }
+
+ if (typeof config === 'string') {
+ if (typeof data[config] === 'undefined') {
+ throw new TypeError("No method named \"" + config + "\"");
+ }
+
+ data[config](relatedTarget);
+ } else if (_config.show) {
+ data.show(relatedTarget);
+ }
+ });
+ };
+
+ _createClass(Modal, null, [{
+ key: "VERSION",
+ get: function get() {
+ return VERSION$5;
+ }
+ }, {
+ key: "Default",
+ get: function get() {
+ return Default$3;
+ }
+ }]);
+
+ return Modal;
+ }();
+ /**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+
+ $__default['default'](document).on(EVENT_CLICK_DATA_API$5, SELECTOR_DATA_TOGGLE$3, function (event) {
+ var _this11 = this;
+
+ var target;
+ var selector = Util.getSelectorFromElement(this);
+
+ if (selector) {
+ target = document.querySelector(selector);
+ }
+
+ var config = $__default['default'](target).data(DATA_KEY$5) ? 'toggle' : _extends({}, $__default['default'](target).data(), $__default['default'](this).data());
+
+ if (this.tagName === 'A' || this.tagName === 'AREA') {
+ event.preventDefault();
+ }
+
+ var $target = $__default['default'](target).one(EVENT_SHOW$2, function (showEvent) {
+ if (showEvent.isDefaultPrevented()) {
+ // Only register focus restorer if modal will actually get shown
+ return;
+ }
+
+ $target.one(EVENT_HIDDEN$2, function () {
+ if ($__default['default'](_this11).is(':visible')) {
+ _this11.focus();
+ }
+ });
+ });
+
+ Modal._jQueryInterface.call($__default['default'](target), config, this);
+ });
+ /**
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ */
+
+ $__default['default'].fn[NAME$5] = Modal._jQueryInterface;
+ $__default['default'].fn[NAME$5].Constructor = Modal;
+
+ $__default['default'].fn[NAME$5].noConflict = function () {
+ $__default['default'].fn[NAME$5] = JQUERY_NO_CONFLICT$5;
+ return Modal._jQueryInterface;
+ };
+
+ /**
+ * --------------------------------------------------------------------------
+ * Bootstrap (v4.5.3): tools/sanitizer.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+ var uriAttrs = ['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href'];
+ var ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i;
+ var DefaultWhitelist = {
+ // Global attributes allowed on any supplied element below.
+ '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
+ a: ['target', 'href', 'title', 'rel'],
+ area: [],
+ b: [],
+ br: [],
+ col: [],
+ code: [],
+ div: [],
+ em: [],
+ hr: [],
+ h1: [],
+ h2: [],
+ h3: [],
+ h4: [],
+ h5: [],
+ h6: [],
+ i: [],
+ img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],
+ li: [],
+ ol: [],
+ p: [],
+ pre: [],
+ s: [],
+ small: [],
+ span: [],
+ sub: [],
+ sup: [],
+ strong: [],
+ u: [],
+ ul: []
+ };
+ /**
+ * A pattern that recognizes a commonly useful subset of URLs that are safe.
+ *
+ * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts
+ */
+
+ var SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^#&/:?]*(?:[#/?]|$))/gi;
+ /**
+ * A pattern that matches safe data URLs. Only matches image, video and audio types.
+ *
+ * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts
+ */
+
+ var DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i;
+
+ function allowedAttribute(attr, allowedAttributeList) {
+ var attrName = attr.nodeName.toLowerCase();
+
+ if (allowedAttributeList.indexOf(attrName) !== -1) {
+ if (uriAttrs.indexOf(attrName) !== -1) {
+ return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN));
+ }
+
+ return true;
+ }
+
+ var regExp = allowedAttributeList.filter(function (attrRegex) {
+ return attrRegex instanceof RegExp;
+ }); // Check if a regular expression validates the attribute.
+
+ for (var i = 0, len = regExp.length; i < len; i++) {
+ if (attrName.match(regExp[i])) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) {
+ if (unsafeHtml.length === 0) {
+ return unsafeHtml;
+ }
+
+ if (sanitizeFn && typeof sanitizeFn === 'function') {
+ return sanitizeFn(unsafeHtml);
+ }
+
+ var domParser = new window.DOMParser();
+ var createdDocument = domParser.parseFromString(unsafeHtml, 'text/html');
+ var whitelistKeys = Object.keys(whiteList);
+ var elements = [].slice.call(createdDocument.body.querySelectorAll('*'));
+
+ var _loop = function _loop(i, len) {
+ var el = elements[i];
+ var elName = el.nodeName.toLowerCase();
+
+ if (whitelistKeys.indexOf(el.nodeName.toLowerCase()) === -1) {
+ el.parentNode.removeChild(el);
+ return "continue";
+ }
+
+ var attributeList = [].slice.call(el.attributes);
+ var whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || []);
+ attributeList.forEach(function (attr) {
+ if (!allowedAttribute(attr, whitelistedAttributes)) {
+ el.removeAttribute(attr.nodeName);
+ }
+ });
+ };
+
+ for (var i = 0, len = elements.length; i < len; i++) {
+ var _ret = _loop(i);
+
+ if (_ret === "continue") continue;
+ }
+
+ return createdDocument.body.innerHTML;
+ }
+
+ /**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+ var NAME$6 = 'tooltip';
+ var VERSION$6 = '4.5.3';
+ var DATA_KEY$6 = 'bs.tooltip';
+ var EVENT_KEY$6 = "." + DATA_KEY$6;
+ var JQUERY_NO_CONFLICT$6 = $__default['default'].fn[NAME$6];
+ var CLASS_PREFIX = 'bs-tooltip';
+ var BSCLS_PREFIX_REGEX = new RegExp("(^|\\s)" + CLASS_PREFIX + "\\S+", 'g');
+ var DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn'];
+ var DefaultType$4 = {
+ animation: 'boolean',
+ template: 'string',
+ title: '(string|element|function)',
+ trigger: 'string',
+ delay: '(number|object)',
+ html: 'boolean',
+ selector: '(string|boolean)',
+ placement: '(string|function)',
+ offset: '(number|string|function)',
+ container: '(string|element|boolean)',
+ fallbackPlacement: '(string|array)',
+ boundary: '(string|element)',
+ sanitize: 'boolean',
+ sanitizeFn: '(null|function)',
+ whiteList: 'object',
+ popperConfig: '(null|object)'
+ };
+ var AttachmentMap = {
+ AUTO: 'auto',
+ TOP: 'top',
+ RIGHT: 'right',
+ BOTTOM: 'bottom',
+ LEFT: 'left'
+ };
+ var Default$4 = {
+ animation: true,
+ template: '',
+ trigger: 'hover focus',
+ title: '',
+ delay: 0,
+ html: false,
+ selector: false,
+ placement: 'top',
+ offset: 0,
+ container: false,
+ fallbackPlacement: 'flip',
+ boundary: 'scrollParent',
+ sanitize: true,
+ sanitizeFn: null,
+ whiteList: DefaultWhitelist,
+ popperConfig: null
+ };
+ var HOVER_STATE_SHOW = 'show';
+ var HOVER_STATE_OUT = 'out';
+ var Event = {
+ HIDE: "hide" + EVENT_KEY$6,
+ HIDDEN: "hidden" + EVENT_KEY$6,
+ SHOW: "show" + EVENT_KEY$6,
+ SHOWN: "shown" + EVENT_KEY$6,
+ INSERTED: "inserted" + EVENT_KEY$6,
+ CLICK: "click" + EVENT_KEY$6,
+ FOCUSIN: "focusin" + EVENT_KEY$6,
+ FOCUSOUT: "focusout" + EVENT_KEY$6,
+ MOUSEENTER: "mouseenter" + EVENT_KEY$6,
+ MOUSELEAVE: "mouseleave" + EVENT_KEY$6
+ };
+ var CLASS_NAME_FADE$2 = 'fade';
+ var CLASS_NAME_SHOW$4 = 'show';
+ var SELECTOR_TOOLTIP_INNER = '.tooltip-inner';
+ var SELECTOR_ARROW = '.arrow';
+ var TRIGGER_HOVER = 'hover';
+ var TRIGGER_FOCUS = 'focus';
+ var TRIGGER_CLICK = 'click';
+ var TRIGGER_MANUAL = 'manual';
+ /**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+ var Tooltip = /*#__PURE__*/function () {
+ function Tooltip(element, config) {
+ if (typeof Popper === 'undefined') {
+ throw new TypeError('Bootstrap\'s tooltips require Popper.js (https://popper.js.org/)');
+ } // private
+
+
+ this._isEnabled = true;
+ this._timeout = 0;
+ this._hoverState = '';
+ this._activeTrigger = {};
+ this._popper = null; // Protected
+
+ this.element = element;
+ this.config = this._getConfig(config);
+ this.tip = null;
+
+ this._setListeners();
+ } // Getters
+
+
+ var _proto = Tooltip.prototype;
+
+ // Public
+ _proto.enable = function enable() {
+ this._isEnabled = true;
+ };
+
+ _proto.disable = function disable() {
+ this._isEnabled = false;
+ };
+
+ _proto.toggleEnabled = function toggleEnabled() {
+ this._isEnabled = !this._isEnabled;
+ };
+
+ _proto.toggle = function toggle(event) {
+ if (!this._isEnabled) {
+ return;
+ }
+
+ if (event) {
+ var dataKey = this.constructor.DATA_KEY;
+ var context = $__default['default'](event.currentTarget).data(dataKey);
+
+ if (!context) {
+ context = new this.constructor(event.currentTarget, this._getDelegateConfig());
+ $__default['default'](event.currentTarget).data(dataKey, context);
+ }
+
+ context._activeTrigger.click = !context._activeTrigger.click;
+
+ if (context._isWithActiveTrigger()) {
+ context._enter(null, context);
+ } else {
+ context._leave(null, context);
+ }
+ } else {
+ if ($__default['default'](this.getTipElement()).hasClass(CLASS_NAME_SHOW$4)) {
+ this._leave(null, this);
+
+ return;
+ }
+
+ this._enter(null, this);
+ }
+ };
+
+ _proto.dispose = function dispose() {
+ clearTimeout(this._timeout);
+ $__default['default'].removeData(this.element, this.constructor.DATA_KEY);
+ $__default['default'](this.element).off(this.constructor.EVENT_KEY);
+ $__default['default'](this.element).closest('.modal').off('hide.bs.modal', this._hideModalHandler);
+
+ if (this.tip) {
+ $__default['default'](this.tip).remove();
+ }
+
+ this._isEnabled = null;
+ this._timeout = null;
+ this._hoverState = null;
+ this._activeTrigger = null;
+
+ if (this._popper) {
+ this._popper.destroy();
+ }
+
+ this._popper = null;
+ this.element = null;
+ this.config = null;
+ this.tip = null;
+ };
+
+ _proto.show = function show() {
+ var _this = this;
+
+ if ($__default['default'](this.element).css('display') === 'none') {
+ throw new Error('Please use show on visible elements');
+ }
+
+ var showEvent = $__default['default'].Event(this.constructor.Event.SHOW);
+
+ if (this.isWithContent() && this._isEnabled) {
+ $__default['default'](this.element).trigger(showEvent);
+ var shadowRoot = Util.findShadowRoot(this.element);
+ var isInTheDom = $__default['default'].contains(shadowRoot !== null ? shadowRoot : this.element.ownerDocument.documentElement, this.element);
+
+ if (showEvent.isDefaultPrevented() || !isInTheDom) {
+ return;
+ }
+
+ var tip = this.getTipElement();
+ var tipId = Util.getUID(this.constructor.NAME);
+ tip.setAttribute('id', tipId);
+ this.element.setAttribute('aria-describedby', tipId);
+ this.setContent();
+
+ if (this.config.animation) {
+ $__default['default'](tip).addClass(CLASS_NAME_FADE$2);
+ }
+
+ var placement = typeof this.config.placement === 'function' ? this.config.placement.call(this, tip, this.element) : this.config.placement;
+
+ var attachment = this._getAttachment(placement);
+
+ this.addAttachmentClass(attachment);
+
+ var container = this._getContainer();
+
+ $__default['default'](tip).data(this.constructor.DATA_KEY, this);
+
+ if (!$__default['default'].contains(this.element.ownerDocument.documentElement, this.tip)) {
+ $__default['default'](tip).appendTo(container);
+ }
+
+ $__default['default'](this.element).trigger(this.constructor.Event.INSERTED);
+ this._popper = new Popper(this.element, tip, this._getPopperConfig(attachment));
+ $__default['default'](tip).addClass(CLASS_NAME_SHOW$4); // If this is a touch-enabled device we add extra
+ // empty mouseover listeners to the body's immediate children;
+ // only needed because of broken event delegation on iOS
+ // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
+
+ if ('ontouchstart' in document.documentElement) {
+ $__default['default'](document.body).children().on('mouseover', null, $__default['default'].noop);
+ }
+
+ var complete = function complete() {
+ if (_this.config.animation) {
+ _this._fixTransition();
+ }
+
+ var prevHoverState = _this._hoverState;
+ _this._hoverState = null;
+ $__default['default'](_this.element).trigger(_this.constructor.Event.SHOWN);
+
+ if (prevHoverState === HOVER_STATE_OUT) {
+ _this._leave(null, _this);
+ }
+ };
+
+ if ($__default['default'](this.tip).hasClass(CLASS_NAME_FADE$2)) {
+ var transitionDuration = Util.getTransitionDurationFromElement(this.tip);
+ $__default['default'](this.tip).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
+ } else {
+ complete();
+ }
+ }
+ };
+
+ _proto.hide = function hide(callback) {
+ var _this2 = this;
+
+ var tip = this.getTipElement();
+ var hideEvent = $__default['default'].Event(this.constructor.Event.HIDE);
+
+ var complete = function complete() {
+ if (_this2._hoverState !== HOVER_STATE_SHOW && tip.parentNode) {
+ tip.parentNode.removeChild(tip);
+ }
+
+ _this2._cleanTipClass();
+
+ _this2.element.removeAttribute('aria-describedby');
+
+ $__default['default'](_this2.element).trigger(_this2.constructor.Event.HIDDEN);
+
+ if (_this2._popper !== null) {
+ _this2._popper.destroy();
+ }
+
+ if (callback) {
+ callback();
+ }
+ };
+
+ $__default['default'](this.element).trigger(hideEvent);
+
+ if (hideEvent.isDefaultPrevented()) {
+ return;
+ }
+
+ $__default['default'](tip).removeClass(CLASS_NAME_SHOW$4); // If this is a touch-enabled device we remove the extra
+ // empty mouseover listeners we added for iOS support
+
+ if ('ontouchstart' in document.documentElement) {
+ $__default['default'](document.body).children().off('mouseover', null, $__default['default'].noop);
+ }
+
+ this._activeTrigger[TRIGGER_CLICK] = false;
+ this._activeTrigger[TRIGGER_FOCUS] = false;
+ this._activeTrigger[TRIGGER_HOVER] = false;
+
+ if ($__default['default'](this.tip).hasClass(CLASS_NAME_FADE$2)) {
+ var transitionDuration = Util.getTransitionDurationFromElement(tip);
+ $__default['default'](tip).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
+ } else {
+ complete();
+ }
+
+ this._hoverState = '';
+ };
+
+ _proto.update = function update() {
+ if (this._popper !== null) {
+ this._popper.scheduleUpdate();
+ }
+ } // Protected
+ ;
+
+ _proto.isWithContent = function isWithContent() {
+ return Boolean(this.getTitle());
+ };
+
+ _proto.addAttachmentClass = function addAttachmentClass(attachment) {
+ $__default['default'](this.getTipElement()).addClass(CLASS_PREFIX + "-" + attachment);
+ };
+
+ _proto.getTipElement = function getTipElement() {
+ this.tip = this.tip || $__default['default'](this.config.template)[0];
+ return this.tip;
+ };
+
+ _proto.setContent = function setContent() {
+ var tip = this.getTipElement();
+ this.setElementContent($__default['default'](tip.querySelectorAll(SELECTOR_TOOLTIP_INNER)), this.getTitle());
+ $__default['default'](tip).removeClass(CLASS_NAME_FADE$2 + " " + CLASS_NAME_SHOW$4);
+ };
+
+ _proto.setElementContent = function setElementContent($element, content) {
+ if (typeof content === 'object' && (content.nodeType || content.jquery)) {
+ // Content is a DOM node or a jQuery
+ if (this.config.html) {
+ if (!$__default['default'](content).parent().is($element)) {
+ $element.empty().append(content);
+ }
+ } else {
+ $element.text($__default['default'](content).text());
+ }
+
+ return;
+ }
+
+ if (this.config.html) {
+ if (this.config.sanitize) {
+ content = sanitizeHtml(content, this.config.whiteList, this.config.sanitizeFn);
+ }
+
+ $element.html(content);
+ } else {
+ $element.text(content);
+ }
+ };
+
+ _proto.getTitle = function getTitle() {
+ var title = this.element.getAttribute('data-original-title');
+
+ if (!title) {
+ title = typeof this.config.title === 'function' ? this.config.title.call(this.element) : this.config.title;
+ }
+
+ return title;
+ } // Private
+ ;
+
+ _proto._getPopperConfig = function _getPopperConfig(attachment) {
+ var _this3 = this;
+
+ var defaultBsConfig = {
+ placement: attachment,
+ modifiers: {
+ offset: this._getOffset(),
+ flip: {
+ behavior: this.config.fallbackPlacement
+ },
+ arrow: {
+ element: SELECTOR_ARROW
+ },
+ preventOverflow: {
+ boundariesElement: this.config.boundary
+ }
+ },
+ onCreate: function onCreate(data) {
+ if (data.originalPlacement !== data.placement) {
+ _this3._handlePopperPlacementChange(data);
+ }
+ },
+ onUpdate: function onUpdate(data) {
+ return _this3._handlePopperPlacementChange(data);
+ }
+ };
+ return _extends({}, defaultBsConfig, this.config.popperConfig);
+ };
+
+ _proto._getOffset = function _getOffset() {
+ var _this4 = this;
+
+ var offset = {};
+
+ if (typeof this.config.offset === 'function') {
+ offset.fn = function (data) {
+ data.offsets = _extends({}, data.offsets, _this4.config.offset(data.offsets, _this4.element) || {});
+ return data;
+ };
+ } else {
+ offset.offset = this.config.offset;
+ }
+
+ return offset;
+ };
+
+ _proto._getContainer = function _getContainer() {
+ if (this.config.container === false) {
+ return document.body;
+ }
+
+ if (Util.isElement(this.config.container)) {
+ return $__default['default'](this.config.container);
+ }
+
+ return $__default['default'](document).find(this.config.container);
+ };
+
+ _proto._getAttachment = function _getAttachment(placement) {
+ return AttachmentMap[placement.toUpperCase()];
+ };
+
+ _proto._setListeners = function _setListeners() {
+ var _this5 = this;
+
+ var triggers = this.config.trigger.split(' ');
+ triggers.forEach(function (trigger) {
+ if (trigger === 'click') {
+ $__default['default'](_this5.element).on(_this5.constructor.Event.CLICK, _this5.config.selector, function (event) {
+ return _this5.toggle(event);
+ });
+ } else if (trigger !== TRIGGER_MANUAL) {
+ var eventIn = trigger === TRIGGER_HOVER ? _this5.constructor.Event.MOUSEENTER : _this5.constructor.Event.FOCUSIN;
+ var eventOut = trigger === TRIGGER_HOVER ? _this5.constructor.Event.MOUSELEAVE : _this5.constructor.Event.FOCUSOUT;
+ $__default['default'](_this5.element).on(eventIn, _this5.config.selector, function (event) {
+ return _this5._enter(event);
+ }).on(eventOut, _this5.config.selector, function (event) {
+ return _this5._leave(event);
+ });
+ }
+ });
+
+ this._hideModalHandler = function () {
+ if (_this5.element) {
+ _this5.hide();
+ }
+ };
+
+ $__default['default'](this.element).closest('.modal').on('hide.bs.modal', this._hideModalHandler);
+
+ if (this.config.selector) {
+ this.config = _extends({}, this.config, {
+ trigger: 'manual',
+ selector: ''
+ });
+ } else {
+ this._fixTitle();
+ }
+ };
+
+ _proto._fixTitle = function _fixTitle() {
+ var titleType = typeof this.element.getAttribute('data-original-title');
+
+ if (this.element.getAttribute('title') || titleType !== 'string') {
+ this.element.setAttribute('data-original-title', this.element.getAttribute('title') || '');
+ this.element.setAttribute('title', '');
+ }
+ };
+
+ _proto._enter = function _enter(event, context) {
+ var dataKey = this.constructor.DATA_KEY;
+ context = context || $__default['default'](event.currentTarget).data(dataKey);
+
+ if (!context) {
+ context = new this.constructor(event.currentTarget, this._getDelegateConfig());
+ $__default['default'](event.currentTarget).data(dataKey, context);
+ }
+
+ if (event) {
+ context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true;
+ }
+
+ if ($__default['default'](context.getTipElement()).hasClass(CLASS_NAME_SHOW$4) || context._hoverState === HOVER_STATE_SHOW) {
+ context._hoverState = HOVER_STATE_SHOW;
+ return;
+ }
+
+ clearTimeout(context._timeout);
+ context._hoverState = HOVER_STATE_SHOW;
+
+ if (!context.config.delay || !context.config.delay.show) {
+ context.show();
+ return;
+ }
+
+ context._timeout = setTimeout(function () {
+ if (context._hoverState === HOVER_STATE_SHOW) {
+ context.show();
+ }
+ }, context.config.delay.show);
+ };
+
+ _proto._leave = function _leave(event, context) {
+ var dataKey = this.constructor.DATA_KEY;
+ context = context || $__default['default'](event.currentTarget).data(dataKey);
+
+ if (!context) {
+ context = new this.constructor(event.currentTarget, this._getDelegateConfig());
+ $__default['default'](event.currentTarget).data(dataKey, context);
+ }
+
+ if (event) {
+ context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = false;
+ }
+
+ if (context._isWithActiveTrigger()) {
+ return;
+ }
+
+ clearTimeout(context._timeout);
+ context._hoverState = HOVER_STATE_OUT;
+
+ if (!context.config.delay || !context.config.delay.hide) {
+ context.hide();
+ return;
+ }
+
+ context._timeout = setTimeout(function () {
+ if (context._hoverState === HOVER_STATE_OUT) {
+ context.hide();
+ }
+ }, context.config.delay.hide);
+ };
+
+ _proto._isWithActiveTrigger = function _isWithActiveTrigger() {
+ for (var trigger in this._activeTrigger) {
+ if (this._activeTrigger[trigger]) {
+ return true;
+ }
+ }
+
+ return false;
+ };
+
+ _proto._getConfig = function _getConfig(config) {
+ var dataAttributes = $__default['default'](this.element).data();
+ Object.keys(dataAttributes).forEach(function (dataAttr) {
+ if (DISALLOWED_ATTRIBUTES.indexOf(dataAttr) !== -1) {
+ delete dataAttributes[dataAttr];
+ }
+ });
+ config = _extends({}, this.constructor.Default, dataAttributes, typeof config === 'object' && config ? config : {});
+
+ if (typeof config.delay === 'number') {
+ config.delay = {
+ show: config.delay,
+ hide: config.delay
+ };
+ }
+
+ if (typeof config.title === 'number') {
+ config.title = config.title.toString();
+ }
+
+ if (typeof config.content === 'number') {
+ config.content = config.content.toString();
+ }
+
+ Util.typeCheckConfig(NAME$6, config, this.constructor.DefaultType);
+
+ if (config.sanitize) {
+ config.template = sanitizeHtml(config.template, config.whiteList, config.sanitizeFn);
+ }
+
+ return config;
+ };
+
+ _proto._getDelegateConfig = function _getDelegateConfig() {
+ var config = {};
+
+ if (this.config) {
+ for (var key in this.config) {
+ if (this.constructor.Default[key] !== this.config[key]) {
+ config[key] = this.config[key];
+ }
+ }
+ }
+
+ return config;
+ };
+
+ _proto._cleanTipClass = function _cleanTipClass() {
+ var $tip = $__default['default'](this.getTipElement());
+ var tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX);
+
+ if (tabClass !== null && tabClass.length) {
+ $tip.removeClass(tabClass.join(''));
+ }
+ };
+
+ _proto._handlePopperPlacementChange = function _handlePopperPlacementChange(popperData) {
+ this.tip = popperData.instance.popper;
+
+ this._cleanTipClass();
+
+ this.addAttachmentClass(this._getAttachment(popperData.placement));
+ };
+
+ _proto._fixTransition = function _fixTransition() {
+ var tip = this.getTipElement();
+ var initConfigAnimation = this.config.animation;
+
+ if (tip.getAttribute('x-placement') !== null) {
+ return;
+ }
+
+ $__default['default'](tip).removeClass(CLASS_NAME_FADE$2);
+ this.config.animation = false;
+ this.hide();
+ this.show();
+ this.config.animation = initConfigAnimation;
+ } // Static
+ ;
+
+ Tooltip._jQueryInterface = function _jQueryInterface(config) {
+ return this.each(function () {
+ var $element = $__default['default'](this);
+ var data = $element.data(DATA_KEY$6);
+
+ var _config = typeof config === 'object' && config;
+
+ if (!data && /dispose|hide/.test(config)) {
+ return;
+ }
+
+ if (!data) {
+ data = new Tooltip(this, _config);
+ $element.data(DATA_KEY$6, data);
+ }
+
+ if (typeof config === 'string') {
+ if (typeof data[config] === 'undefined') {
+ throw new TypeError("No method named \"" + config + "\"");
+ }
+
+ data[config]();
+ }
+ });
+ };
+
+ _createClass(Tooltip, null, [{
+ key: "VERSION",
+ get: function get() {
+ return VERSION$6;
+ }
+ }, {
+ key: "Default",
+ get: function get() {
+ return Default$4;
+ }
+ }, {
+ key: "NAME",
+ get: function get() {
+ return NAME$6;
+ }
+ }, {
+ key: "DATA_KEY",
+ get: function get() {
+ return DATA_KEY$6;
+ }
+ }, {
+ key: "Event",
+ get: function get() {
+ return Event;
+ }
+ }, {
+ key: "EVENT_KEY",
+ get: function get() {
+ return EVENT_KEY$6;
+ }
+ }, {
+ key: "DefaultType",
+ get: function get() {
+ return DefaultType$4;
+ }
+ }]);
+
+ return Tooltip;
+ }();
+ /**
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ */
+
+
+ $__default['default'].fn[NAME$6] = Tooltip._jQueryInterface;
+ $__default['default'].fn[NAME$6].Constructor = Tooltip;
+
+ $__default['default'].fn[NAME$6].noConflict = function () {
+ $__default['default'].fn[NAME$6] = JQUERY_NO_CONFLICT$6;
+ return Tooltip._jQueryInterface;
+ };
+
+ /**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+ var NAME$7 = 'popover';
+ var VERSION$7 = '4.5.3';
+ var DATA_KEY$7 = 'bs.popover';
+ var EVENT_KEY$7 = "." + DATA_KEY$7;
+ var JQUERY_NO_CONFLICT$7 = $__default['default'].fn[NAME$7];
+ var CLASS_PREFIX$1 = 'bs-popover';
+ var BSCLS_PREFIX_REGEX$1 = new RegExp("(^|\\s)" + CLASS_PREFIX$1 + "\\S+", 'g');
+
+ var Default$5 = _extends({}, Tooltip.Default, {
+ placement: 'right',
+ trigger: 'click',
+ content: '',
+ template: ''
+ });
+
+ var DefaultType$5 = _extends({}, Tooltip.DefaultType, {
+ content: '(string|element|function)'
+ });
+
+ var CLASS_NAME_FADE$3 = 'fade';
+ var CLASS_NAME_SHOW$5 = 'show';
+ var SELECTOR_TITLE = '.popover-header';
+ var SELECTOR_CONTENT = '.popover-body';
+ var Event$1 = {
+ HIDE: "hide" + EVENT_KEY$7,
+ HIDDEN: "hidden" + EVENT_KEY$7,
+ SHOW: "show" + EVENT_KEY$7,
+ SHOWN: "shown" + EVENT_KEY$7,
+ INSERTED: "inserted" + EVENT_KEY$7,
+ CLICK: "click" + EVENT_KEY$7,
+ FOCUSIN: "focusin" + EVENT_KEY$7,
+ FOCUSOUT: "focusout" + EVENT_KEY$7,
+ MOUSEENTER: "mouseenter" + EVENT_KEY$7,
+ MOUSELEAVE: "mouseleave" + EVENT_KEY$7
+ };
+ /**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+ var Popover = /*#__PURE__*/function (_Tooltip) {
+ _inheritsLoose(Popover, _Tooltip);
+
+ function Popover() {
+ return _Tooltip.apply(this, arguments) || this;
+ }
+
+ var _proto = Popover.prototype;
+
+ // Overrides
+ _proto.isWithContent = function isWithContent() {
+ return this.getTitle() || this._getContent();
+ };
+
+ _proto.addAttachmentClass = function addAttachmentClass(attachment) {
+ $__default['default'](this.getTipElement()).addClass(CLASS_PREFIX$1 + "-" + attachment);
+ };
+
+ _proto.getTipElement = function getTipElement() {
+ this.tip = this.tip || $__default['default'](this.config.template)[0];
+ return this.tip;
+ };
+
+ _proto.setContent = function setContent() {
+ var $tip = $__default['default'](this.getTipElement()); // We use append for html objects to maintain js events
+
+ this.setElementContent($tip.find(SELECTOR_TITLE), this.getTitle());
+
+ var content = this._getContent();
+
+ if (typeof content === 'function') {
+ content = content.call(this.element);
+ }
+
+ this.setElementContent($tip.find(SELECTOR_CONTENT), content);
+ $tip.removeClass(CLASS_NAME_FADE$3 + " " + CLASS_NAME_SHOW$5);
+ } // Private
+ ;
+
+ _proto._getContent = function _getContent() {
+ return this.element.getAttribute('data-content') || this.config.content;
+ };
+
+ _proto._cleanTipClass = function _cleanTipClass() {
+ var $tip = $__default['default'](this.getTipElement());
+ var tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX$1);
+
+ if (tabClass !== null && tabClass.length > 0) {
+ $tip.removeClass(tabClass.join(''));
+ }
+ } // Static
+ ;
+
+ Popover._jQueryInterface = function _jQueryInterface(config) {
+ return this.each(function () {
+ var data = $__default['default'](this).data(DATA_KEY$7);
+
+ var _config = typeof config === 'object' ? config : null;
+
+ if (!data && /dispose|hide/.test(config)) {
+ return;
+ }
+
+ if (!data) {
+ data = new Popover(this, _config);
+ $__default['default'](this).data(DATA_KEY$7, data);
+ }
+
+ if (typeof config === 'string') {
+ if (typeof data[config] === 'undefined') {
+ throw new TypeError("No method named \"" + config + "\"");
+ }
+
+ data[config]();
+ }
+ });
+ };
+
+ _createClass(Popover, null, [{
+ key: "VERSION",
+ // Getters
+ get: function get() {
+ return VERSION$7;
+ }
+ }, {
+ key: "Default",
+ get: function get() {
+ return Default$5;
+ }
+ }, {
+ key: "NAME",
+ get: function get() {
+ return NAME$7;
+ }
+ }, {
+ key: "DATA_KEY",
+ get: function get() {
+ return DATA_KEY$7;
+ }
+ }, {
+ key: "Event",
+ get: function get() {
+ return Event$1;
+ }
+ }, {
+ key: "EVENT_KEY",
+ get: function get() {
+ return EVENT_KEY$7;
+ }
+ }, {
+ key: "DefaultType",
+ get: function get() {
+ return DefaultType$5;
+ }
+ }]);
+
+ return Popover;
+ }(Tooltip);
+ /**
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ */
+
+
+ $__default['default'].fn[NAME$7] = Popover._jQueryInterface;
+ $__default['default'].fn[NAME$7].Constructor = Popover;
+
+ $__default['default'].fn[NAME$7].noConflict = function () {
+ $__default['default'].fn[NAME$7] = JQUERY_NO_CONFLICT$7;
+ return Popover._jQueryInterface;
+ };
+
+ /**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+ var NAME$8 = 'scrollspy';
+ var VERSION$8 = '4.5.3';
+ var DATA_KEY$8 = 'bs.scrollspy';
+ var EVENT_KEY$8 = "." + DATA_KEY$8;
+ var DATA_API_KEY$6 = '.data-api';
+ var JQUERY_NO_CONFLICT$8 = $__default['default'].fn[NAME$8];
+ var Default$6 = {
+ offset: 10,
+ method: 'auto',
+ target: ''
+ };
+ var DefaultType$6 = {
+ offset: 'number',
+ method: 'string',
+ target: '(string|element)'
+ };
+ var EVENT_ACTIVATE = "activate" + EVENT_KEY$8;
+ var EVENT_SCROLL = "scroll" + EVENT_KEY$8;
+ var EVENT_LOAD_DATA_API$2 = "load" + EVENT_KEY$8 + DATA_API_KEY$6;
+ var CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item';
+ var CLASS_NAME_ACTIVE$2 = 'active';
+ var SELECTOR_DATA_SPY = '[data-spy="scroll"]';
+ var SELECTOR_NAV_LIST_GROUP = '.nav, .list-group';
+ var SELECTOR_NAV_LINKS = '.nav-link';
+ var SELECTOR_NAV_ITEMS = '.nav-item';
+ var SELECTOR_LIST_ITEMS = '.list-group-item';
+ var SELECTOR_DROPDOWN = '.dropdown';
+ var SELECTOR_DROPDOWN_ITEMS = '.dropdown-item';
+ var SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle';
+ var METHOD_OFFSET = 'offset';
+ var METHOD_POSITION = 'position';
+ /**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+ var ScrollSpy = /*#__PURE__*/function () {
+ function ScrollSpy(element, config) {
+ var _this = this;
+
+ this._element = element;
+ this._scrollElement = element.tagName === 'BODY' ? window : element;
+ this._config = this._getConfig(config);
+ this._selector = this._config.target + " " + SELECTOR_NAV_LINKS + "," + (this._config.target + " " + SELECTOR_LIST_ITEMS + ",") + (this._config.target + " " + SELECTOR_DROPDOWN_ITEMS);
+ this._offsets = [];
+ this._targets = [];
+ this._activeTarget = null;
+ this._scrollHeight = 0;
+ $__default['default'](this._scrollElement).on(EVENT_SCROLL, function (event) {
+ return _this._process(event);
+ });
+ this.refresh();
+
+ this._process();
+ } // Getters
+
+
+ var _proto = ScrollSpy.prototype;
+
+ // Public
+ _proto.refresh = function refresh() {
+ var _this2 = this;
+
+ var autoMethod = this._scrollElement === this._scrollElement.window ? METHOD_OFFSET : METHOD_POSITION;
+ var offsetMethod = this._config.method === 'auto' ? autoMethod : this._config.method;
+ var offsetBase = offsetMethod === METHOD_POSITION ? this._getScrollTop() : 0;
+ this._offsets = [];
+ this._targets = [];
+ this._scrollHeight = this._getScrollHeight();
+ var targets = [].slice.call(document.querySelectorAll(this._selector));
+ targets.map(function (element) {
+ var target;
+ var targetSelector = Util.getSelectorFromElement(element);
+
+ if (targetSelector) {
+ target = document.querySelector(targetSelector);
+ }
+
+ if (target) {
+ var targetBCR = target.getBoundingClientRect();
+
+ if (targetBCR.width || targetBCR.height) {
+ // TODO (fat): remove sketch reliance on jQuery position/offset
+ return [$__default['default'](target)[offsetMethod]().top + offsetBase, targetSelector];
+ }
+ }
+
+ return null;
+ }).filter(function (item) {
+ return item;
+ }).sort(function (a, b) {
+ return a[0] - b[0];
+ }).forEach(function (item) {
+ _this2._offsets.push(item[0]);
+
+ _this2._targets.push(item[1]);
+ });
+ };
+
+ _proto.dispose = function dispose() {
+ $__default['default'].removeData(this._element, DATA_KEY$8);
+ $__default['default'](this._scrollElement).off(EVENT_KEY$8);
+ this._element = null;
+ this._scrollElement = null;
+ this._config = null;
+ this._selector = null;
+ this._offsets = null;
+ this._targets = null;
+ this._activeTarget = null;
+ this._scrollHeight = null;
+ } // Private
+ ;
+
+ _proto._getConfig = function _getConfig(config) {
+ config = _extends({}, Default$6, typeof config === 'object' && config ? config : {});
+
+ if (typeof config.target !== 'string' && Util.isElement(config.target)) {
+ var id = $__default['default'](config.target).attr('id');
+
+ if (!id) {
+ id = Util.getUID(NAME$8);
+ $__default['default'](config.target).attr('id', id);
+ }
+
+ config.target = "#" + id;
+ }
+
+ Util.typeCheckConfig(NAME$8, config, DefaultType$6);
+ return config;
+ };
+
+ _proto._getScrollTop = function _getScrollTop() {
+ return this._scrollElement === window ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop;
+ };
+
+ _proto._getScrollHeight = function _getScrollHeight() {
+ return this._scrollElement.scrollHeight || Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
+ };
+
+ _proto._getOffsetHeight = function _getOffsetHeight() {
+ return this._scrollElement === window ? window.innerHeight : this._scrollElement.getBoundingClientRect().height;
+ };
+
+ _proto._process = function _process() {
+ var scrollTop = this._getScrollTop() + this._config.offset;
+
+ var scrollHeight = this._getScrollHeight();
+
+ var maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight();
+
+ if (this._scrollHeight !== scrollHeight) {
+ this.refresh();
+ }
+
+ if (scrollTop >= maxScroll) {
+ var target = this._targets[this._targets.length - 1];
+
+ if (this._activeTarget !== target) {
+ this._activate(target);
+ }
+
+ return;
+ }
+
+ if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {
+ this._activeTarget = null;
+
+ this._clear();
+
+ return;
+ }
+
+ for (var i = this._offsets.length; i--;) {
+ var isActiveTarget = this._activeTarget !== this._targets[i] && scrollTop >= this._offsets[i] && (typeof this._offsets[i + 1] === 'undefined' || scrollTop < this._offsets[i + 1]);
+
+ if (isActiveTarget) {
+ this._activate(this._targets[i]);
+ }
+ }
+ };
+
+ _proto._activate = function _activate(target) {
+ this._activeTarget = target;
+
+ this._clear();
+
+ var queries = this._selector.split(',').map(function (selector) {
+ return selector + "[data-target=\"" + target + "\"]," + selector + "[href=\"" + target + "\"]";
+ });
+
+ var $link = $__default['default']([].slice.call(document.querySelectorAll(queries.join(','))));
+
+ if ($link.hasClass(CLASS_NAME_DROPDOWN_ITEM)) {
+ $link.closest(SELECTOR_DROPDOWN).find(SELECTOR_DROPDOWN_TOGGLE).addClass(CLASS_NAME_ACTIVE$2);
+ $link.addClass(CLASS_NAME_ACTIVE$2);
+ } else {
+ // Set triggered link as active
+ $link.addClass(CLASS_NAME_ACTIVE$2); // Set triggered links parents as active
+ // With both and markup a parent is the previous sibling of any nav ancestor
+
+ $link.parents(SELECTOR_NAV_LIST_GROUP).prev(SELECTOR_NAV_LINKS + ", " + SELECTOR_LIST_ITEMS).addClass(CLASS_NAME_ACTIVE$2); // Handle special case when .nav-link is inside .nav-item
+
+ $link.parents(SELECTOR_NAV_LIST_GROUP).prev(SELECTOR_NAV_ITEMS).children(SELECTOR_NAV_LINKS).addClass(CLASS_NAME_ACTIVE$2);
+ }
+
+ $__default['default'](this._scrollElement).trigger(EVENT_ACTIVATE, {
+ relatedTarget: target
+ });
+ };
+
+ _proto._clear = function _clear() {
+ [].slice.call(document.querySelectorAll(this._selector)).filter(function (node) {
+ return node.classList.contains(CLASS_NAME_ACTIVE$2);
+ }).forEach(function (node) {
+ return node.classList.remove(CLASS_NAME_ACTIVE$2);
+ });
+ } // Static
+ ;
+
+ ScrollSpy._jQueryInterface = function _jQueryInterface(config) {
+ return this.each(function () {
+ var data = $__default['default'](this).data(DATA_KEY$8);
+
+ var _config = typeof config === 'object' && config;
+
+ if (!data) {
+ data = new ScrollSpy(this, _config);
+ $__default['default'](this).data(DATA_KEY$8, data);
+ }
+
+ if (typeof config === 'string') {
+ if (typeof data[config] === 'undefined') {
+ throw new TypeError("No method named \"" + config + "\"");
+ }
+
+ data[config]();
+ }
+ });
+ };
+
+ _createClass(ScrollSpy, null, [{
+ key: "VERSION",
+ get: function get() {
+ return VERSION$8;
+ }
+ }, {
+ key: "Default",
+ get: function get() {
+ return Default$6;
+ }
+ }]);
+
+ return ScrollSpy;
+ }();
+ /**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+
+ $__default['default'](window).on(EVENT_LOAD_DATA_API$2, function () {
+ var scrollSpys = [].slice.call(document.querySelectorAll(SELECTOR_DATA_SPY));
+ var scrollSpysLength = scrollSpys.length;
+
+ for (var i = scrollSpysLength; i--;) {
+ var $spy = $__default['default'](scrollSpys[i]);
+
+ ScrollSpy._jQueryInterface.call($spy, $spy.data());
+ }
+ });
+ /**
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ */
+
+ $__default['default'].fn[NAME$8] = ScrollSpy._jQueryInterface;
+ $__default['default'].fn[NAME$8].Constructor = ScrollSpy;
+
+ $__default['default'].fn[NAME$8].noConflict = function () {
+ $__default['default'].fn[NAME$8] = JQUERY_NO_CONFLICT$8;
+ return ScrollSpy._jQueryInterface;
+ };
+
+ /**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+ var NAME$9 = 'tab';
+ var VERSION$9 = '4.5.3';
+ var DATA_KEY$9 = 'bs.tab';
+ var EVENT_KEY$9 = "." + DATA_KEY$9;
+ var DATA_API_KEY$7 = '.data-api';
+ var JQUERY_NO_CONFLICT$9 = $__default['default'].fn[NAME$9];
+ var EVENT_HIDE$3 = "hide" + EVENT_KEY$9;
+ var EVENT_HIDDEN$3 = "hidden" + EVENT_KEY$9;
+ var EVENT_SHOW$3 = "show" + EVENT_KEY$9;
+ var EVENT_SHOWN$3 = "shown" + EVENT_KEY$9;
+ var EVENT_CLICK_DATA_API$6 = "click" + EVENT_KEY$9 + DATA_API_KEY$7;
+ var CLASS_NAME_DROPDOWN_MENU = 'dropdown-menu';
+ var CLASS_NAME_ACTIVE$3 = 'active';
+ var CLASS_NAME_DISABLED$1 = 'disabled';
+ var CLASS_NAME_FADE$4 = 'fade';
+ var CLASS_NAME_SHOW$6 = 'show';
+ var SELECTOR_DROPDOWN$1 = '.dropdown';
+ var SELECTOR_NAV_LIST_GROUP$1 = '.nav, .list-group';
+ var SELECTOR_ACTIVE$2 = '.active';
+ var SELECTOR_ACTIVE_UL = '> li > .active';
+ var SELECTOR_DATA_TOGGLE$4 = '[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]';
+ var SELECTOR_DROPDOWN_TOGGLE$1 = '.dropdown-toggle';
+ var SELECTOR_DROPDOWN_ACTIVE_CHILD = '> .dropdown-menu .active';
+ /**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+ var Tab = /*#__PURE__*/function () {
+ function Tab(element) {
+ this._element = element;
+ } // Getters
+
+
+ var _proto = Tab.prototype;
+
+ // Public
+ _proto.show = function show() {
+ var _this = this;
+
+ if (this._element.parentNode && this._element.parentNode.nodeType === Node.ELEMENT_NODE && $__default['default'](this._element).hasClass(CLASS_NAME_ACTIVE$3) || $__default['default'](this._element).hasClass(CLASS_NAME_DISABLED$1)) {
+ return;
+ }
+
+ var target;
+ var previous;
+ var listElement = $__default['default'](this._element).closest(SELECTOR_NAV_LIST_GROUP$1)[0];
+ var selector = Util.getSelectorFromElement(this._element);
+
+ if (listElement) {
+ var itemSelector = listElement.nodeName === 'UL' || listElement.nodeName === 'OL' ? SELECTOR_ACTIVE_UL : SELECTOR_ACTIVE$2;
+ previous = $__default['default'].makeArray($__default['default'](listElement).find(itemSelector));
+ previous = previous[previous.length - 1];
+ }
+
+ var hideEvent = $__default['default'].Event(EVENT_HIDE$3, {
+ relatedTarget: this._element
+ });
+ var showEvent = $__default['default'].Event(EVENT_SHOW$3, {
+ relatedTarget: previous
+ });
+
+ if (previous) {
+ $__default['default'](previous).trigger(hideEvent);
+ }
+
+ $__default['default'](this._element).trigger(showEvent);
+
+ if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) {
+ return;
+ }
+
+ if (selector) {
+ target = document.querySelector(selector);
+ }
+
+ this._activate(this._element, listElement);
+
+ var complete = function complete() {
+ var hiddenEvent = $__default['default'].Event(EVENT_HIDDEN$3, {
+ relatedTarget: _this._element
+ });
+ var shownEvent = $__default['default'].Event(EVENT_SHOWN$3, {
+ relatedTarget: previous
+ });
+ $__default['default'](previous).trigger(hiddenEvent);
+ $__default['default'](_this._element).trigger(shownEvent);
+ };
+
+ if (target) {
+ this._activate(target, target.parentNode, complete);
+ } else {
+ complete();
+ }
+ };
+
+ _proto.dispose = function dispose() {
+ $__default['default'].removeData(this._element, DATA_KEY$9);
+ this._element = null;
+ } // Private
+ ;
+
+ _proto._activate = function _activate(element, container, callback) {
+ var _this2 = this;
+
+ var activeElements = container && (container.nodeName === 'UL' || container.nodeName === 'OL') ? $__default['default'](container).find(SELECTOR_ACTIVE_UL) : $__default['default'](container).children(SELECTOR_ACTIVE$2);
+ var active = activeElements[0];
+ var isTransitioning = callback && active && $__default['default'](active).hasClass(CLASS_NAME_FADE$4);
+
+ var complete = function complete() {
+ return _this2._transitionComplete(element, active, callback);
+ };
+
+ if (active && isTransitioning) {
+ var transitionDuration = Util.getTransitionDurationFromElement(active);
+ $__default['default'](active).removeClass(CLASS_NAME_SHOW$6).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
+ } else {
+ complete();
+ }
+ };
+
+ _proto._transitionComplete = function _transitionComplete(element, active, callback) {
+ if (active) {
+ $__default['default'](active).removeClass(CLASS_NAME_ACTIVE$3);
+ var dropdownChild = $__default['default'](active.parentNode).find(SELECTOR_DROPDOWN_ACTIVE_CHILD)[0];
+
+ if (dropdownChild) {
+ $__default['default'](dropdownChild).removeClass(CLASS_NAME_ACTIVE$3);
+ }
+
+ if (active.getAttribute('role') === 'tab') {
+ active.setAttribute('aria-selected', false);
+ }
+ }
+
+ $__default['default'](element).addClass(CLASS_NAME_ACTIVE$3);
+
+ if (element.getAttribute('role') === 'tab') {
+ element.setAttribute('aria-selected', true);
+ }
+
+ Util.reflow(element);
+
+ if (element.classList.contains(CLASS_NAME_FADE$4)) {
+ element.classList.add(CLASS_NAME_SHOW$6);
+ }
+
+ if (element.parentNode && $__default['default'](element.parentNode).hasClass(CLASS_NAME_DROPDOWN_MENU)) {
+ var dropdownElement = $__default['default'](element).closest(SELECTOR_DROPDOWN$1)[0];
+
+ if (dropdownElement) {
+ var dropdownToggleList = [].slice.call(dropdownElement.querySelectorAll(SELECTOR_DROPDOWN_TOGGLE$1));
+ $__default['default'](dropdownToggleList).addClass(CLASS_NAME_ACTIVE$3);
+ }
+
+ element.setAttribute('aria-expanded', true);
+ }
+
+ if (callback) {
+ callback();
+ }
+ } // Static
+ ;
+
+ Tab._jQueryInterface = function _jQueryInterface(config) {
+ return this.each(function () {
+ var $this = $__default['default'](this);
+ var data = $this.data(DATA_KEY$9);
+
+ if (!data) {
+ data = new Tab(this);
+ $this.data(DATA_KEY$9, data);
+ }
+
+ if (typeof config === 'string') {
+ if (typeof data[config] === 'undefined') {
+ throw new TypeError("No method named \"" + config + "\"");
+ }
+
+ data[config]();
+ }
+ });
+ };
+
+ _createClass(Tab, null, [{
+ key: "VERSION",
+ get: function get() {
+ return VERSION$9;
+ }
+ }]);
+
+ return Tab;
+ }();
+ /**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+
+ $__default['default'](document).on(EVENT_CLICK_DATA_API$6, SELECTOR_DATA_TOGGLE$4, function (event) {
+ event.preventDefault();
+
+ Tab._jQueryInterface.call($__default['default'](this), 'show');
+ });
+ /**
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ */
+
+ $__default['default'].fn[NAME$9] = Tab._jQueryInterface;
+ $__default['default'].fn[NAME$9].Constructor = Tab;
+
+ $__default['default'].fn[NAME$9].noConflict = function () {
+ $__default['default'].fn[NAME$9] = JQUERY_NO_CONFLICT$9;
+ return Tab._jQueryInterface;
+ };
+
+ /**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+ var NAME$a = 'toast';
+ var VERSION$a = '4.5.3';
+ var DATA_KEY$a = 'bs.toast';
+ var EVENT_KEY$a = "." + DATA_KEY$a;
+ var JQUERY_NO_CONFLICT$a = $__default['default'].fn[NAME$a];
+ var EVENT_CLICK_DISMISS$1 = "click.dismiss" + EVENT_KEY$a;
+ var EVENT_HIDE$4 = "hide" + EVENT_KEY$a;
+ var EVENT_HIDDEN$4 = "hidden" + EVENT_KEY$a;
+ var EVENT_SHOW$4 = "show" + EVENT_KEY$a;
+ var EVENT_SHOWN$4 = "shown" + EVENT_KEY$a;
+ var CLASS_NAME_FADE$5 = 'fade';
+ var CLASS_NAME_HIDE = 'hide';
+ var CLASS_NAME_SHOW$7 = 'show';
+ var CLASS_NAME_SHOWING = 'showing';
+ var DefaultType$7 = {
+ animation: 'boolean',
+ autohide: 'boolean',
+ delay: 'number'
+ };
+ var Default$7 = {
+ animation: true,
+ autohide: true,
+ delay: 500
+ };
+ var SELECTOR_DATA_DISMISS$1 = '[data-dismiss="toast"]';
+ /**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+ var Toast = /*#__PURE__*/function () {
+ function Toast(element, config) {
+ this._element = element;
+ this._config = this._getConfig(config);
+ this._timeout = null;
+
+ this._setListeners();
+ } // Getters
+
+
+ var _proto = Toast.prototype;
+
+ // Public
+ _proto.show = function show() {
+ var _this = this;
+
+ var showEvent = $__default['default'].Event(EVENT_SHOW$4);
+ $__default['default'](this._element).trigger(showEvent);
+
+ if (showEvent.isDefaultPrevented()) {
+ return;
+ }
+
+ this._clearTimeout();
+
+ if (this._config.animation) {
+ this._element.classList.add(CLASS_NAME_FADE$5);
+ }
+
+ var complete = function complete() {
+ _this._element.classList.remove(CLASS_NAME_SHOWING);
+
+ _this._element.classList.add(CLASS_NAME_SHOW$7);
+
+ $__default['default'](_this._element).trigger(EVENT_SHOWN$4);
+
+ if (_this._config.autohide) {
+ _this._timeout = setTimeout(function () {
+ _this.hide();
+ }, _this._config.delay);
+ }
+ };
+
+ this._element.classList.remove(CLASS_NAME_HIDE);
+
+ Util.reflow(this._element);
+
+ this._element.classList.add(CLASS_NAME_SHOWING);
+
+ if (this._config.animation) {
+ var transitionDuration = Util.getTransitionDurationFromElement(this._element);
+ $__default['default'](this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
+ } else {
+ complete();
+ }
+ };
+
+ _proto.hide = function hide() {
+ if (!this._element.classList.contains(CLASS_NAME_SHOW$7)) {
+ return;
+ }
+
+ var hideEvent = $__default['default'].Event(EVENT_HIDE$4);
+ $__default['default'](this._element).trigger(hideEvent);
+
+ if (hideEvent.isDefaultPrevented()) {
+ return;
+ }
+
+ this._close();
+ };
+
+ _proto.dispose = function dispose() {
+ this._clearTimeout();
+
+ if (this._element.classList.contains(CLASS_NAME_SHOW$7)) {
+ this._element.classList.remove(CLASS_NAME_SHOW$7);
+ }
+
+ $__default['default'](this._element).off(EVENT_CLICK_DISMISS$1);
+ $__default['default'].removeData(this._element, DATA_KEY$a);
+ this._element = null;
+ this._config = null;
+ } // Private
+ ;
+
+ _proto._getConfig = function _getConfig(config) {
+ config = _extends({}, Default$7, $__default['default'](this._element).data(), typeof config === 'object' && config ? config : {});
+ Util.typeCheckConfig(NAME$a, config, this.constructor.DefaultType);
+ return config;
+ };
+
+ _proto._setListeners = function _setListeners() {
+ var _this2 = this;
+
+ $__default['default'](this._element).on(EVENT_CLICK_DISMISS$1, SELECTOR_DATA_DISMISS$1, function () {
+ return _this2.hide();
+ });
+ };
+
+ _proto._close = function _close() {
+ var _this3 = this;
+
+ var complete = function complete() {
+ _this3._element.classList.add(CLASS_NAME_HIDE);
+
+ $__default['default'](_this3._element).trigger(EVENT_HIDDEN$4);
+ };
+
+ this._element.classList.remove(CLASS_NAME_SHOW$7);
+
+ if (this._config.animation) {
+ var transitionDuration = Util.getTransitionDurationFromElement(this._element);
+ $__default['default'](this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
+ } else {
+ complete();
+ }
+ };
+
+ _proto._clearTimeout = function _clearTimeout() {
+ clearTimeout(this._timeout);
+ this._timeout = null;
+ } // Static
+ ;
+
+ Toast._jQueryInterface = function _jQueryInterface(config) {
+ return this.each(function () {
+ var $element = $__default['default'](this);
+ var data = $element.data(DATA_KEY$a);
+
+ var _config = typeof config === 'object' && config;
+
+ if (!data) {
+ data = new Toast(this, _config);
+ $element.data(DATA_KEY$a, data);
+ }
+
+ if (typeof config === 'string') {
+ if (typeof data[config] === 'undefined') {
+ throw new TypeError("No method named \"" + config + "\"");
+ }
+
+ data[config](this);
+ }
+ });
+ };
+
+ _createClass(Toast, null, [{
+ key: "VERSION",
+ get: function get() {
+ return VERSION$a;
+ }
+ }, {
+ key: "DefaultType",
+ get: function get() {
+ return DefaultType$7;
+ }
+ }, {
+ key: "Default",
+ get: function get() {
+ return Default$7;
+ }
+ }]);
+
+ return Toast;
+ }();
+ /**
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ */
+
+
+ $__default['default'].fn[NAME$a] = Toast._jQueryInterface;
+ $__default['default'].fn[NAME$a].Constructor = Toast;
+
+ $__default['default'].fn[NAME$a].noConflict = function () {
+ $__default['default'].fn[NAME$a] = JQUERY_NO_CONFLICT$a;
+ return Toast._jQueryInterface;
+ };
+
+ exports.Alert = Alert;
+ exports.Button = Button;
+ exports.Carousel = Carousel;
+ exports.Collapse = Collapse;
+ exports.Dropdown = Dropdown;
+ exports.Modal = Modal;
+ exports.Popover = Popover;
+ exports.Scrollspy = ScrollSpy;
+ exports.Tab = Tab;
+ exports.Toast = Toast;
+ exports.Tooltip = Tooltip;
+ exports.Util = Util;
+
+ Object.defineProperty(exports, '__esModule', { value: true });
+
+})));
+//# sourceMappingURL=bootstrap.bundle.js.map
diff --git a/webroot/js/empty b/webroot/js/empty
new file mode 100644
index 0000000..e69de29
diff --git a/webroot/js/jquery-3.5.1.min.js b/webroot/js/jquery-3.5.1.min.js
new file mode 100644
index 0000000..b061403
--- /dev/null
+++ b/webroot/js/jquery-3.5.1.min.js
@@ -0,0 +1,2 @@
+/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */
+!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),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("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML=" ",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML=" ";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML=" ",y.option=!!ce.lastChild;var ge={thead:[1,""],col:[2,""],tr:[2,""],td:[3,""],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0= this.maxSize) {
+ this.list.remove(tailItem);
+ delete this.hash[tailItem.key];
+ this.size--;
+ }
+ if (node = this.hash[key]) {
+ node.val = val;
+ this.list.moveToFront(node);
+ } else {
+ node = new Node(key, val);
+ this.list.add(node);
+ this.hash[key] = node;
+ this.size++;
+ }
+ },
+ get: function get(key) {
+ var node = this.hash[key];
+ if (node) {
+ this.list.moveToFront(node);
+ return node.val;
+ }
+ },
+ reset: function reset() {
+ this.size = 0;
+ this.hash = {};
+ this.list = new List();
+ }
+ });
+ function List() {
+ this.head = this.tail = null;
+ }
+ _.mixin(List.prototype, {
+ add: function add(node) {
+ if (this.head) {
+ node.next = this.head;
+ this.head.prev = node;
+ }
+ this.head = node;
+ this.tail = this.tail || node;
+ },
+ remove: function remove(node) {
+ node.prev ? node.prev.next = node.next : this.head = node.next;
+ node.next ? node.next.prev = node.prev : this.tail = node.prev;
+ },
+ moveToFront: function(node) {
+ this.remove(node);
+ this.add(node);
+ }
+ });
+ function Node(key, val) {
+ this.key = key;
+ this.val = val;
+ this.prev = this.next = null;
+ }
+ return LruCache;
+ }();
+ var PersistentStorage = function() {
+ "use strict";
+ var LOCAL_STORAGE;
+ try {
+ LOCAL_STORAGE = window.localStorage;
+ LOCAL_STORAGE.setItem("~~~", "!");
+ LOCAL_STORAGE.removeItem("~~~");
+ } catch (err) {
+ LOCAL_STORAGE = null;
+ }
+ function PersistentStorage(namespace, override) {
+ this.prefix = [ "__", namespace, "__" ].join("");
+ this.ttlKey = "__ttl__";
+ this.keyMatcher = new RegExp("^" + _.escapeRegExChars(this.prefix));
+ this.ls = override || LOCAL_STORAGE;
+ !this.ls && this._noop();
+ }
+ _.mixin(PersistentStorage.prototype, {
+ _prefix: function(key) {
+ return this.prefix + key;
+ },
+ _ttlKey: function(key) {
+ return this._prefix(key) + this.ttlKey;
+ },
+ _noop: function() {
+ this.get = this.set = this.remove = this.clear = this.isExpired = _.noop;
+ },
+ _safeSet: function(key, val) {
+ try {
+ this.ls.setItem(key, val);
+ } catch (err) {
+ if (err.name === "QuotaExceededError") {
+ this.clear();
+ this._noop();
+ }
+ }
+ },
+ get: function(key) {
+ if (this.isExpired(key)) {
+ this.remove(key);
+ }
+ return decode(this.ls.getItem(this._prefix(key)));
+ },
+ set: function(key, val, ttl) {
+ if (_.isNumber(ttl)) {
+ this._safeSet(this._ttlKey(key), encode(now() + ttl));
+ } else {
+ this.ls.removeItem(this._ttlKey(key));
+ }
+ return this._safeSet(this._prefix(key), encode(val));
+ },
+ remove: function(key) {
+ this.ls.removeItem(this._ttlKey(key));
+ this.ls.removeItem(this._prefix(key));
+ return this;
+ },
+ clear: function() {
+ var i, keys = gatherMatchingKeys(this.keyMatcher);
+ for (i = keys.length; i--; ) {
+ this.remove(keys[i]);
+ }
+ return this;
+ },
+ isExpired: function(key) {
+ var ttl = decode(this.ls.getItem(this._ttlKey(key)));
+ return _.isNumber(ttl) && now() > ttl ? true : false;
+ }
+ });
+ return PersistentStorage;
+ function now() {
+ return new Date().getTime();
+ }
+ function encode(val) {
+ return JSON.stringify(_.isUndefined(val) ? null : val);
+ }
+ function decode(val) {
+ return $.parseJSON(val);
+ }
+ function gatherMatchingKeys(keyMatcher) {
+ var i, key, keys = [], len = LOCAL_STORAGE.length;
+ for (i = 0; i < len; i++) {
+ if ((key = LOCAL_STORAGE.key(i)).match(keyMatcher)) {
+ keys.push(key.replace(keyMatcher, ""));
+ }
+ }
+ return keys;
+ }
+ }();
+ var Transport = function() {
+ "use strict";
+ var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, sharedCache = new LruCache(10);
+ function Transport(o) {
+ o = o || {};
+ this.cancelled = false;
+ this.lastReq = null;
+ this._send = o.transport;
+ this._get = o.limiter ? o.limiter(this._get) : this._get;
+ this._cache = o.cache === false ? new LruCache(0) : sharedCache;
+ }
+ Transport.setMaxPendingRequests = function setMaxPendingRequests(num) {
+ maxPendingRequests = num;
+ };
+ Transport.resetCache = function resetCache() {
+ sharedCache.reset();
+ };
+ _.mixin(Transport.prototype, {
+ _fingerprint: function fingerprint(o) {
+ o = o || {};
+ return o.url + o.type + $.param(o.data || {});
+ },
+ _get: function(o, cb) {
+ var that = this, fingerprint, jqXhr;
+ fingerprint = this._fingerprint(o);
+ if (this.cancelled || fingerprint !== this.lastReq) {
+ return;
+ }
+ if (jqXhr = pendingRequests[fingerprint]) {
+ jqXhr.done(done).fail(fail);
+ } else if (pendingRequestsCount < maxPendingRequests) {
+ pendingRequestsCount++;
+ pendingRequests[fingerprint] = this._send(o).done(done).fail(fail).always(always);
+ } else {
+ this.onDeckRequestArgs = [].slice.call(arguments, 0);
+ }
+ function done(resp) {
+ cb(null, resp);
+ that._cache.set(fingerprint, resp);
+ }
+ function fail() {
+ cb(true);
+ }
+ function always() {
+ pendingRequestsCount--;
+ delete pendingRequests[fingerprint];
+ if (that.onDeckRequestArgs) {
+ that._get.apply(that, that.onDeckRequestArgs);
+ that.onDeckRequestArgs = null;
+ }
+ }
+ },
+ get: function(o, cb) {
+ var resp, fingerprint;
+ cb = cb || $.noop;
+ o = _.isString(o) ? {
+ url: o
+ } : o || {};
+ fingerprint = this._fingerprint(o);
+ this.cancelled = false;
+ this.lastReq = fingerprint;
+ if (resp = this._cache.get(fingerprint)) {
+ cb(null, resp);
+ } else {
+ this._get(o, cb);
+ }
+ },
+ cancel: function() {
+ this.cancelled = true;
+ }
+ });
+ return Transport;
+ }();
+ var SearchIndex = window.SearchIndex = function() {
+ "use strict";
+ var CHILDREN = "c", IDS = "i";
+ function SearchIndex(o) {
+ o = o || {};
+ if (!o.datumTokenizer || !o.queryTokenizer) {
+ $.error("datumTokenizer and queryTokenizer are both required");
+ }
+ this.identify = o.identify || _.stringify;
+ this.datumTokenizer = o.datumTokenizer;
+ this.queryTokenizer = o.queryTokenizer;
+ this.reset();
+ }
+ _.mixin(SearchIndex.prototype, {
+ bootstrap: function bootstrap(o) {
+ this.datums = o.datums;
+ this.trie = o.trie;
+ },
+ add: function(data) {
+ var that = this;
+ data = _.isArray(data) ? data : [ data ];
+ _.each(data, function(datum) {
+ var id, tokens;
+ that.datums[id = that.identify(datum)] = datum;
+ tokens = normalizeTokens(that.datumTokenizer(datum));
+ _.each(tokens, function(token) {
+ var node, chars, ch;
+ node = that.trie;
+ chars = token.split("");
+ while (ch = chars.shift()) {
+ node = node[CHILDREN][ch] || (node[CHILDREN][ch] = newNode());
+ node[IDS].push(id);
+ }
+ });
+ });
+ },
+ get: function get(ids) {
+ var that = this;
+ return _.map(ids, function(id) {
+ return that.datums[id];
+ });
+ },
+ search: function search(query) {
+ var that = this, tokens, matches;
+ tokens = normalizeTokens(this.queryTokenizer(query));
+ _.each(tokens, function(token) {
+ var node, chars, ch, ids;
+ if (matches && matches.length === 0) {
+ return false;
+ }
+ node = that.trie;
+ chars = token.split("");
+ while (node && (ch = chars.shift())) {
+ node = node[CHILDREN][ch];
+ }
+ if (node && chars.length === 0) {
+ ids = node[IDS].slice(0);
+ matches = matches ? getIntersection(matches, ids) : ids;
+ } else {
+ matches = [];
+ return false;
+ }
+ });
+ return matches ? _.map(unique(matches), function(id) {
+ return that.datums[id];
+ }) : [];
+ },
+ all: function all() {
+ var values = [];
+ for (var key in this.datums) {
+ values.push(this.datums[key]);
+ }
+ return values;
+ },
+ reset: function reset() {
+ this.datums = {};
+ this.trie = newNode();
+ },
+ serialize: function serialize() {
+ return {
+ datums: this.datums,
+ trie: this.trie
+ };
+ }
+ });
+ return SearchIndex;
+ function normalizeTokens(tokens) {
+ tokens = _.filter(tokens, function(token) {
+ return !!token;
+ });
+ tokens = _.map(tokens, function(token) {
+ return token.toLowerCase();
+ });
+ return tokens;
+ }
+ function newNode() {
+ var node = {};
+ node[IDS] = [];
+ node[CHILDREN] = {};
+ return node;
+ }
+ function unique(array) {
+ var seen = {}, uniques = [];
+ for (var i = 0, len = array.length; i < len; i++) {
+ if (!seen[array[i]]) {
+ seen[array[i]] = true;
+ uniques.push(array[i]);
+ }
+ }
+ return uniques;
+ }
+ function getIntersection(arrayA, arrayB) {
+ var ai = 0, bi = 0, intersection = [];
+ arrayA = arrayA.sort();
+ arrayB = arrayB.sort();
+ var lenArrayA = arrayA.length, lenArrayB = arrayB.length;
+ while (ai < lenArrayA && bi < lenArrayB) {
+ if (arrayA[ai] < arrayB[bi]) {
+ ai++;
+ } else if (arrayA[ai] > arrayB[bi]) {
+ bi++;
+ } else {
+ intersection.push(arrayA[ai]);
+ ai++;
+ bi++;
+ }
+ }
+ return intersection;
+ }
+ }();
+ var Prefetch = function() {
+ "use strict";
+ var keys;
+ keys = {
+ data: "data",
+ protocol: "protocol",
+ thumbprint: "thumbprint"
+ };
+ function Prefetch(o) {
+ this.url = o.url;
+ this.ttl = o.ttl;
+ this.cache = o.cache;
+ this.prepare = o.prepare;
+ this.transform = o.transform;
+ this.transport = o.transport;
+ this.thumbprint = o.thumbprint;
+ this.storage = new PersistentStorage(o.cacheKey);
+ }
+ _.mixin(Prefetch.prototype, {
+ _settings: function settings() {
+ return {
+ url: this.url,
+ type: "GET",
+ dataType: "json"
+ };
+ },
+ store: function store(data) {
+ if (!this.cache) {
+ return;
+ }
+ this.storage.set(keys.data, data, this.ttl);
+ this.storage.set(keys.protocol, location.protocol, this.ttl);
+ this.storage.set(keys.thumbprint, this.thumbprint, this.ttl);
+ },
+ fromCache: function fromCache() {
+ var stored = {}, isExpired;
+ if (!this.cache) {
+ return null;
+ }
+ stored.data = this.storage.get(keys.data);
+ stored.protocol = this.storage.get(keys.protocol);
+ stored.thumbprint = this.storage.get(keys.thumbprint);
+ isExpired = stored.thumbprint !== this.thumbprint || stored.protocol !== location.protocol;
+ return stored.data && !isExpired ? stored.data : null;
+ },
+ fromNetwork: function(cb) {
+ var that = this, settings;
+ if (!cb) {
+ return;
+ }
+ settings = this.prepare(this._settings());
+ this.transport(settings).fail(onError).done(onResponse);
+ function onError() {
+ cb(true);
+ }
+ function onResponse(resp) {
+ cb(null, that.transform(resp));
+ }
+ },
+ clear: function clear() {
+ this.storage.clear();
+ return this;
+ }
+ });
+ return Prefetch;
+ }();
+ var Remote = function() {
+ "use strict";
+ function Remote(o) {
+ this.url = o.url;
+ this.prepare = o.prepare;
+ this.transform = o.transform;
+ this.transport = new Transport({
+ cache: o.cache,
+ limiter: o.limiter,
+ transport: o.transport
+ });
+ }
+ _.mixin(Remote.prototype, {
+ _settings: function settings() {
+ return {
+ url: this.url,
+ type: "GET",
+ dataType: "json"
+ };
+ },
+ get: function get(query, cb) {
+ var that = this, settings;
+ if (!cb) {
+ return;
+ }
+ query = query || "";
+ settings = this.prepare(query, this._settings());
+ return this.transport.get(settings, onResponse);
+ function onResponse(err, resp) {
+ err ? cb([]) : cb(that.transform(resp));
+ }
+ },
+ cancelLastRequest: function cancelLastRequest() {
+ this.transport.cancel();
+ }
+ });
+ return Remote;
+ }();
+ var oParser = function() {
+ "use strict";
+ return function parse(o) {
+ var defaults, sorter;
+ defaults = {
+ initialize: true,
+ identify: _.stringify,
+ datumTokenizer: null,
+ queryTokenizer: null,
+ sufficient: 5,
+ sorter: null,
+ local: [],
+ prefetch: null,
+ remote: null
+ };
+ o = _.mixin(defaults, o || {});
+ !o.datumTokenizer && $.error("datumTokenizer is required");
+ !o.queryTokenizer && $.error("queryTokenizer is required");
+ sorter = o.sorter;
+ o.sorter = sorter ? function(x) {
+ return x.sort(sorter);
+ } : _.identity;
+ o.local = _.isFunction(o.local) ? o.local() : o.local;
+ o.prefetch = parsePrefetch(o.prefetch);
+ o.remote = parseRemote(o.remote);
+ return o;
+ };
+ function parsePrefetch(o) {
+ var defaults;
+ if (!o) {
+ return null;
+ }
+ defaults = {
+ url: null,
+ ttl: 24 * 60 * 60 * 1e3,
+ cache: true,
+ cacheKey: null,
+ thumbprint: "",
+ prepare: _.identity,
+ transform: _.identity,
+ transport: null
+ };
+ o = _.isString(o) ? {
+ url: o
+ } : o;
+ o = _.mixin(defaults, o);
+ !o.url && $.error("prefetch requires url to be set");
+ o.transform = o.filter || o.transform;
+ o.cacheKey = o.cacheKey || o.url;
+ o.thumbprint = VERSION + o.thumbprint;
+ o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;
+ return o;
+ }
+ function parseRemote(o) {
+ var defaults;
+ if (!o) {
+ return;
+ }
+ defaults = {
+ url: null,
+ cache: true,
+ prepare: null,
+ replace: null,
+ wildcard: null,
+ limiter: null,
+ rateLimitBy: "debounce",
+ rateLimitWait: 300,
+ transform: _.identity,
+ transport: null
+ };
+ o = _.isString(o) ? {
+ url: o
+ } : o;
+ o = _.mixin(defaults, o);
+ !o.url && $.error("remote requires url to be set");
+ o.transform = o.filter || o.transform;
+ o.prepare = toRemotePrepare(o);
+ o.limiter = toLimiter(o);
+ o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;
+ delete o.replace;
+ delete o.wildcard;
+ delete o.rateLimitBy;
+ delete o.rateLimitWait;
+ return o;
+ }
+ function toRemotePrepare(o) {
+ var prepare, replace, wildcard;
+ prepare = o.prepare;
+ replace = o.replace;
+ wildcard = o.wildcard;
+ if (prepare) {
+ return prepare;
+ }
+ if (replace) {
+ prepare = prepareByReplace;
+ } else if (o.wildcard) {
+ prepare = prepareByWildcard;
+ } else {
+ prepare = idenityPrepare;
+ }
+ return prepare;
+ function prepareByReplace(query, settings) {
+ settings.url = replace(settings.url, query);
+ return settings;
+ }
+ function prepareByWildcard(query, settings) {
+ settings.url = settings.url.replace(wildcard, encodeURIComponent(query));
+ return settings;
+ }
+ function idenityPrepare(query, settings) {
+ return settings;
+ }
+ }
+ function toLimiter(o) {
+ var limiter, method, wait;
+ limiter = o.limiter;
+ method = o.rateLimitBy;
+ wait = o.rateLimitWait;
+ if (!limiter) {
+ limiter = /^throttle$/i.test(method) ? throttle(wait) : debounce(wait);
+ }
+ return limiter;
+ function debounce(wait) {
+ return function debounce(fn) {
+ return _.debounce(fn, wait);
+ };
+ }
+ function throttle(wait) {
+ return function throttle(fn) {
+ return _.throttle(fn, wait);
+ };
+ }
+ }
+ function callbackToDeferred(fn) {
+ return function wrapper(o) {
+ var deferred = $.Deferred();
+ fn(o, onSuccess, onError);
+ return deferred;
+ function onSuccess(resp) {
+ _.defer(function() {
+ deferred.resolve(resp);
+ });
+ }
+ function onError(err) {
+ _.defer(function() {
+ deferred.reject(err);
+ });
+ }
+ };
+ }
+ }();
+ var Bloodhound = function() {
+ "use strict";
+ var old;
+ old = window && window.Bloodhound;
+ function Bloodhound(o) {
+ o = oParser(o);
+ this.sorter = o.sorter;
+ this.identify = o.identify;
+ this.sufficient = o.sufficient;
+ this.local = o.local;
+ this.remote = o.remote ? new Remote(o.remote) : null;
+ this.prefetch = o.prefetch ? new Prefetch(o.prefetch) : null;
+ this.index = new SearchIndex({
+ identify: this.identify,
+ datumTokenizer: o.datumTokenizer,
+ queryTokenizer: o.queryTokenizer
+ });
+ o.initialize !== false && this.initialize();
+ }
+ Bloodhound.noConflict = function noConflict() {
+ window && (window.Bloodhound = old);
+ return Bloodhound;
+ };
+ Bloodhound.tokenizers = tokenizers;
+ _.mixin(Bloodhound.prototype, {
+ __ttAdapter: function ttAdapter() {
+ var that = this;
+ return this.remote ? withAsync : withoutAsync;
+ function withAsync(query, sync, async) {
+ return that.search(query, sync, async);
+ }
+ function withoutAsync(query, sync) {
+ return that.search(query, sync);
+ }
+ },
+ _loadPrefetch: function loadPrefetch() {
+ var that = this, deferred, serialized;
+ deferred = $.Deferred();
+ if (!this.prefetch) {
+ deferred.resolve();
+ } else if (serialized = this.prefetch.fromCache()) {
+ this.index.bootstrap(serialized);
+ deferred.resolve();
+ } else {
+ this.prefetch.fromNetwork(done);
+ }
+ return deferred.promise();
+ function done(err, data) {
+ if (err) {
+ return deferred.reject();
+ }
+ that.add(data);
+ that.prefetch.store(that.index.serialize());
+ deferred.resolve();
+ }
+ },
+ _initialize: function initialize() {
+ var that = this, deferred;
+ this.clear();
+ (this.initPromise = this._loadPrefetch()).done(addLocalToIndex);
+ return this.initPromise;
+ function addLocalToIndex() {
+ that.add(that.local);
+ }
+ },
+ initialize: function initialize(force) {
+ return !this.initPromise || force ? this._initialize() : this.initPromise;
+ },
+ add: function add(data) {
+ this.index.add(data);
+ return this;
+ },
+ get: function get(ids) {
+ ids = _.isArray(ids) ? ids : [].slice.call(arguments);
+ return this.index.get(ids);
+ },
+ search: function search(query, sync, async) {
+ var that = this, local;
+ local = this.sorter(this.index.search(query));
+ sync(this.remote ? local.slice() : local);
+ if (this.remote && local.length < this.sufficient) {
+ this.remote.get(query, processRemote);
+ } else if (this.remote) {
+ this.remote.cancelLastRequest();
+ }
+ return this;
+ function processRemote(remote) {
+ var nonDuplicates = [];
+ _.each(remote, function(r) {
+ !_.some(local, function(l) {
+ return that.identify(r) === that.identify(l);
+ }) && nonDuplicates.push(r);
+ });
+ async && async(nonDuplicates);
+ }
+ },
+ all: function all() {
+ return this.index.all();
+ },
+ clear: function clear() {
+ this.index.reset();
+ return this;
+ },
+ clearPrefetchCache: function clearPrefetchCache() {
+ this.prefetch && this.prefetch.clear();
+ return this;
+ },
+ clearRemoteCache: function clearRemoteCache() {
+ Transport.resetCache();
+ return this;
+ },
+ ttAdapter: function ttAdapter() {
+ return this.__ttAdapter();
+ }
+ });
+ return Bloodhound;
+ }();
+ return Bloodhound;
+});
+
+(function(root, factory) {
+ if (typeof define === "function" && define.amd) {
+ define("typeahead.js", [ "jquery" ], function(a0) {
+ return factory(a0);
+ });
+ } else if (typeof exports === "object") {
+ module.exports = factory(require("jquery"));
+ } else {
+ factory(jQuery);
+ }
+})(this, function($) {
+ var _ = function() {
+ "use strict";
+ return {
+ isMsie: function() {
+ return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false;
+ },
+ isBlankString: function(str) {
+ return !str || /^\s*$/.test(str);
+ },
+ escapeRegExChars: function(str) {
+ return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
+ },
+ isString: function(obj) {
+ return typeof obj === "string";
+ },
+ isNumber: function(obj) {
+ return typeof obj === "number";
+ },
+ isArray: $.isArray,
+ isFunction: $.isFunction,
+ isObject: $.isPlainObject,
+ isUndefined: function(obj) {
+ return typeof obj === "undefined";
+ },
+ isElement: function(obj) {
+ return !!(obj && obj.nodeType === 1);
+ },
+ isJQuery: function(obj) {
+ return obj instanceof $;
+ },
+ toStr: function toStr(s) {
+ return _.isUndefined(s) || s === null ? "" : s + "";
+ },
+ bind: $.proxy,
+ each: function(collection, cb) {
+ $.each(collection, reverseArgs);
+ function reverseArgs(index, value) {
+ return cb(value, index);
+ }
+ },
+ map: $.map,
+ filter: $.grep,
+ every: function(obj, test) {
+ var result = true;
+ if (!obj) {
+ return result;
+ }
+ $.each(obj, function(key, val) {
+ if (!(result = test.call(null, val, key, obj))) {
+ return false;
+ }
+ });
+ return !!result;
+ },
+ some: function(obj, test) {
+ var result = false;
+ if (!obj) {
+ return result;
+ }
+ $.each(obj, function(key, val) {
+ if (result = test.call(null, val, key, obj)) {
+ return false;
+ }
+ });
+ return !!result;
+ },
+ mixin: $.extend,
+ identity: function(x) {
+ return x;
+ },
+ clone: function(obj) {
+ return $.extend(true, {}, obj);
+ },
+ getIdGenerator: function() {
+ var counter = 0;
+ return function() {
+ return counter++;
+ };
+ },
+ templatify: function templatify(obj) {
+ return $.isFunction(obj) ? obj : template;
+ function template() {
+ return String(obj);
+ }
+ },
+ defer: function(fn) {
+ setTimeout(fn, 0);
+ },
+ debounce: function(func, wait, immediate) {
+ var timeout, result;
+ return function() {
+ var context = this, args = arguments, later, callNow;
+ later = function() {
+ timeout = null;
+ if (!immediate) {
+ result = func.apply(context, args);
+ }
+ };
+ callNow = immediate && !timeout;
+ clearTimeout(timeout);
+ timeout = setTimeout(later, wait);
+ if (callNow) {
+ result = func.apply(context, args);
+ }
+ return result;
+ };
+ },
+ throttle: function(func, wait) {
+ var context, args, timeout, result, previous, later;
+ previous = 0;
+ later = function() {
+ previous = new Date();
+ timeout = null;
+ result = func.apply(context, args);
+ };
+ return function() {
+ var now = new Date(), remaining = wait - (now - previous);
+ context = this;
+ args = arguments;
+ if (remaining <= 0) {
+ clearTimeout(timeout);
+ timeout = null;
+ previous = now;
+ result = func.apply(context, args);
+ } else if (!timeout) {
+ timeout = setTimeout(later, remaining);
+ }
+ return result;
+ };
+ },
+ stringify: function(val) {
+ return _.isString(val) ? val : JSON.stringify(val);
+ },
+ noop: function() {}
+ };
+ }();
+ var WWW = function() {
+ "use strict";
+ var defaultClassNames = {
+ wrapper: "twitter-typeahead",
+ input: "tt-input",
+ hint: "tt-hint",
+ menu: "tt-menu",
+ dataset: "tt-dataset",
+ suggestion: "tt-suggestion",
+ selectable: "tt-selectable",
+ empty: "tt-empty",
+ open: "tt-open",
+ cursor: "tt-cursor",
+ highlight: "tt-highlight"
+ };
+ return build;
+ function build(o) {
+ var www, classes;
+ classes = _.mixin({}, defaultClassNames, o);
+ www = {
+ css: buildCss(),
+ classes: classes,
+ html: buildHtml(classes),
+ selectors: buildSelectors(classes)
+ };
+ return {
+ css: www.css,
+ html: www.html,
+ classes: www.classes,
+ selectors: www.selectors,
+ mixin: function(o) {
+ _.mixin(o, www);
+ }
+ };
+ }
+ function buildHtml(c) {
+ return {
+ wrapper: ' ',
+ menu: ''
+ };
+ }
+ function buildSelectors(classes) {
+ var selectors = {};
+ _.each(classes, function(v, k) {
+ selectors[k] = "." + v;
+ });
+ return selectors;
+ }
+ function buildCss() {
+ var css = {
+ wrapper: {
+ position: "relative",
+ display: "inline-block"
+ },
+ hint: {
+ position: "absolute",
+ top: "0",
+ left: "0",
+ borderColor: "transparent",
+ boxShadow: "none",
+ opacity: "1"
+ },
+ input: {
+ position: "relative",
+ verticalAlign: "top",
+ backgroundColor: "transparent"
+ },
+ inputWithNoHint: {
+ position: "relative",
+ verticalAlign: "top"
+ },
+ menu: {
+ position: "absolute",
+ top: "100%",
+ left: "0",
+ zIndex: "100",
+ display: "none"
+ },
+ ltr: {
+ left: "0",
+ right: "auto"
+ },
+ rtl: {
+ left: "auto",
+ right: " 0"
+ }
+ };
+ if (_.isMsie()) {
+ _.mixin(css.input, {
+ backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)"
+ });
+ }
+ return css;
+ }
+ }();
+ var EventBus = function() {
+ "use strict";
+ var namespace, deprecationMap;
+ namespace = "typeahead:";
+ deprecationMap = {
+ render: "rendered",
+ cursorchange: "cursorchanged",
+ select: "selected",
+ autocomplete: "autocompleted"
+ };
+ function EventBus(o) {
+ if (!o || !o.el) {
+ $.error("EventBus initialized without el");
+ }
+ this.$el = $(o.el);
+ }
+ _.mixin(EventBus.prototype, {
+ _trigger: function(type, args) {
+ var $e;
+ $e = $.Event(namespace + type);
+ (args = args || []).unshift($e);
+ this.$el.trigger.apply(this.$el, args);
+ return $e;
+ },
+ before: function(type) {
+ var args, $e;
+ args = [].slice.call(arguments, 1);
+ $e = this._trigger("before" + type, args);
+ return $e.isDefaultPrevented();
+ },
+ trigger: function(type) {
+ var deprecatedType;
+ this._trigger(type, [].slice.call(arguments, 1));
+ if (deprecatedType = deprecationMap[type]) {
+ this._trigger(deprecatedType, [].slice.call(arguments, 1));
+ }
+ }
+ });
+ return EventBus;
+ }();
+ var EventEmitter = function() {
+ "use strict";
+ var splitter = /\s+/, nextTick = getNextTick();
+ return {
+ onSync: onSync,
+ onAsync: onAsync,
+ off: off,
+ trigger: trigger
+ };
+ function on(method, types, cb, context) {
+ var type;
+ if (!cb) {
+ return this;
+ }
+ types = types.split(splitter);
+ cb = context ? bindContext(cb, context) : cb;
+ this._callbacks = this._callbacks || {};
+ while (type = types.shift()) {
+ this._callbacks[type] = this._callbacks[type] || {
+ sync: [],
+ async: []
+ };
+ this._callbacks[type][method].push(cb);
+ }
+ return this;
+ }
+ function onAsync(types, cb, context) {
+ return on.call(this, "async", types, cb, context);
+ }
+ function onSync(types, cb, context) {
+ return on.call(this, "sync", types, cb, context);
+ }
+ function off(types) {
+ var type;
+ if (!this._callbacks) {
+ return this;
+ }
+ types = types.split(splitter);
+ while (type = types.shift()) {
+ delete this._callbacks[type];
+ }
+ return this;
+ }
+ function trigger(types) {
+ var type, callbacks, args, syncFlush, asyncFlush;
+ if (!this._callbacks) {
+ return this;
+ }
+ types = types.split(splitter);
+ args = [].slice.call(arguments, 1);
+ while ((type = types.shift()) && (callbacks = this._callbacks[type])) {
+ syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args));
+ asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args));
+ syncFlush() && nextTick(asyncFlush);
+ }
+ return this;
+ }
+ function getFlush(callbacks, context, args) {
+ return flush;
+ function flush() {
+ var cancelled;
+ for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) {
+ cancelled = callbacks[i].apply(context, args) === false;
+ }
+ return !cancelled;
+ }
+ }
+ function getNextTick() {
+ var nextTickFn;
+ if (window.setImmediate) {
+ nextTickFn = function nextTickSetImmediate(fn) {
+ setImmediate(function() {
+ fn();
+ });
+ };
+ } else {
+ nextTickFn = function nextTickSetTimeout(fn) {
+ setTimeout(function() {
+ fn();
+ }, 0);
+ };
+ }
+ return nextTickFn;
+ }
+ function bindContext(fn, context) {
+ return fn.bind ? fn.bind(context) : function() {
+ fn.apply(context, [].slice.call(arguments, 0));
+ };
+ }
+ }();
+ var highlight = function(doc) {
+ "use strict";
+ var defaults = {
+ node: null,
+ pattern: null,
+ tagName: "strong",
+ className: null,
+ wordsOnly: false,
+ caseSensitive: false
+ };
+ return function hightlight(o) {
+ var regex;
+ o = _.mixin({}, defaults, o);
+ if (!o.node || !o.pattern) {
+ return;
+ }
+ o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ];
+ regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly);
+ traverse(o.node, hightlightTextNode);
+ function hightlightTextNode(textNode) {
+ var match, patternNode, wrapperNode;
+ if (match = regex.exec(textNode.data)) {
+ wrapperNode = doc.createElement(o.tagName);
+ o.className && (wrapperNode.className = o.className);
+ patternNode = textNode.splitText(match.index);
+ patternNode.splitText(match[0].length);
+ wrapperNode.appendChild(patternNode.cloneNode(true));
+ textNode.parentNode.replaceChild(wrapperNode, patternNode);
+ }
+ return !!match;
+ }
+ function traverse(el, hightlightTextNode) {
+ var childNode, TEXT_NODE_TYPE = 3;
+ for (var i = 0; i < el.childNodes.length; i++) {
+ childNode = el.childNodes[i];
+ if (childNode.nodeType === TEXT_NODE_TYPE) {
+ i += hightlightTextNode(childNode) ? 1 : 0;
+ } else {
+ traverse(childNode, hightlightTextNode);
+ }
+ }
+ }
+ };
+ function getRegex(patterns, caseSensitive, wordsOnly) {
+ var escapedPatterns = [], regexStr;
+ for (var i = 0, len = patterns.length; i < len; i++) {
+ escapedPatterns.push(_.escapeRegExChars(patterns[i]));
+ }
+ regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")";
+ return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i");
+ }
+ }(window.document);
+ var Input = function() {
+ "use strict";
+ var specialKeyCodeMap;
+ specialKeyCodeMap = {
+ 9: "tab",
+ 27: "esc",
+ 37: "left",
+ 39: "right",
+ 13: "enter",
+ 38: "up",
+ 40: "down"
+ };
+ function Input(o, www) {
+ o = o || {};
+ if (!o.input) {
+ $.error("input is missing");
+ }
+ www.mixin(this);
+ this.$hint = $(o.hint);
+ this.$input = $(o.input);
+ this.query = this.$input.val();
+ this.queryWhenFocused = this.hasFocus() ? this.query : null;
+ this.$overflowHelper = buildOverflowHelper(this.$input);
+ this._checkLanguageDirection();
+ if (this.$hint.length === 0) {
+ this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop;
+ }
+ }
+ Input.normalizeQuery = function(str) {
+ return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " ");
+ };
+ _.mixin(Input.prototype, EventEmitter, {
+ _onBlur: function onBlur() {
+ this.resetInputValue();
+ this.trigger("blurred");
+ },
+ _onFocus: function onFocus() {
+ this.queryWhenFocused = this.query;
+ this.trigger("focused");
+ },
+ _onKeydown: function onKeydown($e) {
+ var keyName = specialKeyCodeMap[$e.which || $e.keyCode];
+ this._managePreventDefault(keyName, $e);
+ if (keyName && this._shouldTrigger(keyName, $e)) {
+ this.trigger(keyName + "Keyed", $e);
+ }
+ },
+ _onInput: function onInput() {
+ this._setQuery(this.getInputValue());
+ this.clearHintIfInvalid();
+ this._checkLanguageDirection();
+ },
+ _managePreventDefault: function managePreventDefault(keyName, $e) {
+ var preventDefault;
+ switch (keyName) {
+ case "up":
+ case "down":
+ preventDefault = !withModifier($e);
+ break;
+
+ default:
+ preventDefault = false;
+ }
+ preventDefault && $e.preventDefault();
+ },
+ _shouldTrigger: function shouldTrigger(keyName, $e) {
+ var trigger;
+ switch (keyName) {
+ case "tab":
+ trigger = !withModifier($e);
+ break;
+
+ default:
+ trigger = true;
+ }
+ return trigger;
+ },
+ _checkLanguageDirection: function checkLanguageDirection() {
+ var dir = (this.$input.css("direction") || "ltr").toLowerCase();
+ if (this.dir !== dir) {
+ this.dir = dir;
+ this.$hint.attr("dir", dir);
+ this.trigger("langDirChanged", dir);
+ }
+ },
+ _setQuery: function setQuery(val, silent) {
+ var areEquivalent, hasDifferentWhitespace;
+ areEquivalent = areQueriesEquivalent(val, this.query);
+ hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false;
+ this.query = val;
+ if (!silent && !areEquivalent) {
+ this.trigger("queryChanged", this.query);
+ } else if (!silent && hasDifferentWhitespace) {
+ this.trigger("whitespaceChanged", this.query);
+ }
+ },
+ bind: function() {
+ var that = this, onBlur, onFocus, onKeydown, onInput;
+ onBlur = _.bind(this._onBlur, this);
+ onFocus = _.bind(this._onFocus, this);
+ onKeydown = _.bind(this._onKeydown, this);
+ onInput = _.bind(this._onInput, this);
+ this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown);
+ if (!_.isMsie() || _.isMsie() > 9) {
+ this.$input.on("input.tt", onInput);
+ } else {
+ this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) {
+ if (specialKeyCodeMap[$e.which || $e.keyCode]) {
+ return;
+ }
+ _.defer(_.bind(that._onInput, that, $e));
+ });
+ }
+ return this;
+ },
+ focus: function focus() {
+ this.$input.focus();
+ },
+ blur: function blur() {
+ this.$input.blur();
+ },
+ getLangDir: function getLangDir() {
+ return this.dir;
+ },
+ getQuery: function getQuery() {
+ return this.query || "";
+ },
+ setQuery: function setQuery(val, silent) {
+ this.setInputValue(val);
+ this._setQuery(val, silent);
+ },
+ hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() {
+ return this.query !== this.queryWhenFocused;
+ },
+ getInputValue: function getInputValue() {
+ return this.$input.val();
+ },
+ setInputValue: function setInputValue(value) {
+ this.$input.val(value);
+ this.clearHintIfInvalid();
+ this._checkLanguageDirection();
+ },
+ resetInputValue: function resetInputValue() {
+ this.setInputValue(this.query);
+ },
+ getHint: function getHint() {
+ return this.$hint.val();
+ },
+ setHint: function setHint(value) {
+ this.$hint.val(value);
+ },
+ clearHint: function clearHint() {
+ this.setHint("");
+ },
+ clearHintIfInvalid: function clearHintIfInvalid() {
+ var val, hint, valIsPrefixOfHint, isValid;
+ val = this.getInputValue();
+ hint = this.getHint();
+ valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0;
+ isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow();
+ !isValid && this.clearHint();
+ },
+ hasFocus: function hasFocus() {
+ return this.$input.is(":focus");
+ },
+ hasOverflow: function hasOverflow() {
+ var constraint = this.$input.width() - 2;
+ this.$overflowHelper.text(this.getInputValue());
+ return this.$overflowHelper.width() >= constraint;
+ },
+ isCursorAtEnd: function() {
+ var valueLength, selectionStart, range;
+ valueLength = this.$input.val().length;
+ selectionStart = this.$input[0].selectionStart;
+ if (_.isNumber(selectionStart)) {
+ return selectionStart === valueLength;
+ } else if (document.selection) {
+ range = document.selection.createRange();
+ range.moveStart("character", -valueLength);
+ return valueLength === range.text.length;
+ }
+ return true;
+ },
+ destroy: function destroy() {
+ this.$hint.off(".tt");
+ this.$input.off(".tt");
+ this.$overflowHelper.remove();
+ this.$hint = this.$input = this.$overflowHelper = $("");
+ }
+ });
+ return Input;
+ function buildOverflowHelper($input) {
+ return $('
').css({
+ position: "absolute",
+ visibility: "hidden",
+ whiteSpace: "pre",
+ fontFamily: $input.css("font-family"),
+ fontSize: $input.css("font-size"),
+ fontStyle: $input.css("font-style"),
+ fontVariant: $input.css("font-variant"),
+ fontWeight: $input.css("font-weight"),
+ wordSpacing: $input.css("word-spacing"),
+ letterSpacing: $input.css("letter-spacing"),
+ textIndent: $input.css("text-indent"),
+ textRendering: $input.css("text-rendering"),
+ textTransform: $input.css("text-transform")
+ }).insertAfter($input);
+ }
+ function areQueriesEquivalent(a, b) {
+ return Input.normalizeQuery(a) === Input.normalizeQuery(b);
+ }
+ function withModifier($e) {
+ return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey;
+ }
+ }();
+ var Dataset = function() {
+ "use strict";
+ var keys, nameGenerator;
+ keys = {
+ val: "tt-selectable-display",
+ obj: "tt-selectable-object"
+ };
+ nameGenerator = _.getIdGenerator();
+ function Dataset(o, www) {
+ o = o || {};
+ o.templates = o.templates || {};
+ o.templates.notFound = o.templates.notFound || o.templates.empty;
+ if (!o.source) {
+ $.error("missing source");
+ }
+ if (!o.node) {
+ $.error("missing node");
+ }
+ if (o.name && !isValidName(o.name)) {
+ $.error("invalid dataset name: " + o.name);
+ }
+ www.mixin(this);
+ this.highlight = !!o.highlight;
+ this.name = o.name || nameGenerator();
+ this.limit = o.limit || 5;
+ this.displayFn = getDisplayFn(o.display || o.displayKey);
+ this.templates = getTemplates(o.templates, this.displayFn);
+ this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source;
+ this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async;
+ this._resetLastSuggestion();
+ this.$el = $(o.node).addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name);
+ }
+ Dataset.extractData = function extractData(el) {
+ var $el = $(el);
+ if ($el.data(keys.obj)) {
+ return {
+ val: $el.data(keys.val) || "",
+ obj: $el.data(keys.obj) || null
+ };
+ }
+ return null;
+ };
+ _.mixin(Dataset.prototype, EventEmitter, {
+ _overwrite: function overwrite(query, suggestions) {
+ suggestions = suggestions || [];
+ if (suggestions.length) {
+ this._renderSuggestions(query, suggestions);
+ } else if (this.async && this.templates.pending) {
+ this._renderPending(query);
+ } else if (!this.async && this.templates.notFound) {
+ this._renderNotFound(query);
+ } else {
+ this._empty();
+ }
+ this.trigger("rendered", this.name, suggestions, false);
+ },
+ _append: function append(query, suggestions) {
+ suggestions = suggestions || [];
+ if (suggestions.length && this.$lastSuggestion.length) {
+ this._appendSuggestions(query, suggestions);
+ } else if (suggestions.length) {
+ this._renderSuggestions(query, suggestions);
+ } else if (!this.$lastSuggestion.length && this.templates.notFound) {
+ this._renderNotFound(query);
+ }
+ this.trigger("rendered", this.name, suggestions, true);
+ },
+ _renderSuggestions: function renderSuggestions(query, suggestions) {
+ var $fragment;
+ $fragment = this._getSuggestionsFragment(query, suggestions);
+ this.$lastSuggestion = $fragment.children().last();
+ this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions));
+ },
+ _appendSuggestions: function appendSuggestions(query, suggestions) {
+ var $fragment, $lastSuggestion;
+ $fragment = this._getSuggestionsFragment(query, suggestions);
+ $lastSuggestion = $fragment.children().last();
+ this.$lastSuggestion.after($fragment);
+ this.$lastSuggestion = $lastSuggestion;
+ },
+ _renderPending: function renderPending(query) {
+ var template = this.templates.pending;
+ this._resetLastSuggestion();
+ template && this.$el.html(template({
+ query: query,
+ dataset: this.name
+ }));
+ },
+ _renderNotFound: function renderNotFound(query) {
+ var template = this.templates.notFound;
+ this._resetLastSuggestion();
+ template && this.$el.html(template({
+ query: query,
+ dataset: this.name
+ }));
+ },
+ _empty: function empty() {
+ this.$el.empty();
+ this._resetLastSuggestion();
+ },
+ _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) {
+ var that = this, fragment;
+ fragment = document.createDocumentFragment();
+ _.each(suggestions, function getSuggestionNode(suggestion) {
+ var $el, context;
+ context = that._injectQuery(query, suggestion);
+ $el = $(that.templates.suggestion(context)).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable);
+ fragment.appendChild($el[0]);
+ });
+ this.highlight && highlight({
+ className: this.classes.highlight,
+ node: fragment,
+ pattern: query
+ });
+ return $(fragment);
+ },
+ _getFooter: function getFooter(query, suggestions) {
+ return this.templates.footer ? this.templates.footer({
+ query: query,
+ suggestions: suggestions,
+ dataset: this.name
+ }) : null;
+ },
+ _getHeader: function getHeader(query, suggestions) {
+ return this.templates.header ? this.templates.header({
+ query: query,
+ suggestions: suggestions,
+ dataset: this.name
+ }) : null;
+ },
+ _resetLastSuggestion: function resetLastSuggestion() {
+ this.$lastSuggestion = $();
+ },
+ _injectQuery: function injectQuery(query, obj) {
+ return _.isObject(obj) ? _.mixin({
+ _query: query
+ }, obj) : obj;
+ },
+ update: function update(query) {
+ var that = this, canceled = false, syncCalled = false, rendered = 0;
+ this.cancel();
+ this.cancel = function cancel() {
+ canceled = true;
+ that.cancel = $.noop;
+ that.async && that.trigger("asyncCanceled", query);
+ };
+ this.source(query, sync, async);
+ !syncCalled && sync([]);
+ function sync(suggestions) {
+ if (syncCalled) {
+ return;
+ }
+ syncCalled = true;
+ suggestions = (suggestions || []).slice(0, that.limit);
+ rendered = suggestions.length;
+ that._overwrite(query, suggestions);
+ if (rendered < that.limit && that.async) {
+ that.trigger("asyncRequested", query);
+ }
+ }
+ function async(suggestions) {
+ suggestions = suggestions || [];
+ if (!canceled && rendered < that.limit) {
+ that.cancel = $.noop;
+ rendered += suggestions.length;
+ that._append(query, suggestions.slice(0, that.limit - rendered));
+ that.async && that.trigger("asyncReceived", query);
+ }
+ }
+ },
+ cancel: $.noop,
+ clear: function clear() {
+ this._empty();
+ this.cancel();
+ this.trigger("cleared");
+ },
+ isEmpty: function isEmpty() {
+ return this.$el.is(":empty");
+ },
+ destroy: function destroy() {
+ this.$el = $("
");
+ }
+ });
+ return Dataset;
+ function getDisplayFn(display) {
+ display = display || _.stringify;
+ return _.isFunction(display) ? display : displayFn;
+ function displayFn(obj) {
+ return obj[display];
+ }
+ }
+ function getTemplates(templates, displayFn) {
+ return {
+ notFound: templates.notFound && _.templatify(templates.notFound),
+ pending: templates.pending && _.templatify(templates.pending),
+ header: templates.header && _.templatify(templates.header),
+ footer: templates.footer && _.templatify(templates.footer),
+ suggestion: templates.suggestion || suggestionTemplate
+ };
+ function suggestionTemplate(context) {
+ return $("
").text(displayFn(context));
+ }
+ }
+ function isValidName(str) {
+ return /^[_a-zA-Z0-9-]+$/.test(str);
+ }
+ }();
+ var Menu = function() {
+ "use strict";
+ function Menu(o, www) {
+ var that = this;
+ o = o || {};
+ if (!o.node) {
+ $.error("node is required");
+ }
+ www.mixin(this);
+ this.$node = $(o.node);
+ this.query = null;
+ this.datasets = _.map(o.datasets, initializeDataset);
+ function initializeDataset(oDataset) {
+ var node = that.$node.find(oDataset.node).first();
+ oDataset.node = node.length ? node : $("
").appendTo(that.$node);
+ return new Dataset(oDataset, www);
+ }
+ }
+ _.mixin(Menu.prototype, EventEmitter, {
+ _onSelectableClick: function onSelectableClick($e) {
+ this.trigger("selectableClicked", $($e.currentTarget));
+ },
+ _onRendered: function onRendered(type, dataset, suggestions, async) {
+ this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty());
+ this.trigger("datasetRendered", dataset, suggestions, async);
+ },
+ _onCleared: function onCleared() {
+ this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty());
+ this.trigger("datasetCleared");
+ },
+ _propagate: function propagate() {
+ this.trigger.apply(this, arguments);
+ },
+ _allDatasetsEmpty: function allDatasetsEmpty() {
+ return _.every(this.datasets, isDatasetEmpty);
+ function isDatasetEmpty(dataset) {
+ return dataset.isEmpty();
+ }
+ },
+ _getSelectables: function getSelectables() {
+ return this.$node.find(this.selectors.selectable);
+ },
+ _removeCursor: function _removeCursor() {
+ var $selectable = this.getActiveSelectable();
+ $selectable && $selectable.removeClass(this.classes.cursor);
+ },
+ _ensureVisible: function ensureVisible($el) {
+ var elTop, elBottom, nodeScrollTop, nodeHeight;
+ elTop = $el.position().top;
+ elBottom = elTop + $el.outerHeight(true);
+ nodeScrollTop = this.$node.scrollTop();
+ nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10);
+ if (elTop < 0) {
+ this.$node.scrollTop(nodeScrollTop + elTop);
+ } else if (nodeHeight < elBottom) {
+ this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight));
+ }
+ },
+ bind: function() {
+ var that = this, onSelectableClick;
+ onSelectableClick = _.bind(this._onSelectableClick, this);
+ this.$node.on("click.tt", this.selectors.selectable, onSelectableClick);
+ _.each(this.datasets, function(dataset) {
+ dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that);
+ });
+ return this;
+ },
+ isOpen: function isOpen() {
+ return this.$node.hasClass(this.classes.open);
+ },
+ open: function open() {
+ this.$node.addClass(this.classes.open);
+ },
+ close: function close() {
+ this.$node.removeClass(this.classes.open);
+ this._removeCursor();
+ },
+ setLanguageDirection: function setLanguageDirection(dir) {
+ this.$node.attr("dir", dir);
+ },
+ selectableRelativeToCursor: function selectableRelativeToCursor(delta) {
+ var $selectables, $oldCursor, oldIndex, newIndex;
+ $oldCursor = this.getActiveSelectable();
+ $selectables = this._getSelectables();
+ oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1;
+ newIndex = oldIndex + delta;
+ newIndex = (newIndex + 1) % ($selectables.length + 1) - 1;
+ newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex;
+ return newIndex === -1 ? null : $selectables.eq(newIndex);
+ },
+ setCursor: function setCursor($selectable) {
+ this._removeCursor();
+ if ($selectable = $selectable && $selectable.first()) {
+ $selectable.addClass(this.classes.cursor);
+ this._ensureVisible($selectable);
+ }
+ },
+ getSelectableData: function getSelectableData($el) {
+ return $el && $el.length ? Dataset.extractData($el) : null;
+ },
+ getActiveSelectable: function getActiveSelectable() {
+ var $selectable = this._getSelectables().filter(this.selectors.cursor).first();
+ return $selectable.length ? $selectable : null;
+ },
+ getTopSelectable: function getTopSelectable() {
+ var $selectable = this._getSelectables().first();
+ return $selectable.length ? $selectable : null;
+ },
+ update: function update(query) {
+ var isValidUpdate = query !== this.query;
+ if (isValidUpdate) {
+ this.query = query;
+ _.each(this.datasets, updateDataset);
+ }
+ return isValidUpdate;
+ function updateDataset(dataset) {
+ dataset.update(query);
+ }
+ },
+ empty: function empty() {
+ _.each(this.datasets, clearDataset);
+ this.query = null;
+ this.$node.addClass(this.classes.empty);
+ function clearDataset(dataset) {
+ dataset.clear();
+ }
+ },
+ destroy: function destroy() {
+ this.$node.off(".tt");
+ this.$node = $("
");
+ _.each(this.datasets, destroyDataset);
+ function destroyDataset(dataset) {
+ dataset.destroy();
+ }
+ }
+ });
+ return Menu;
+ }();
+ var DefaultMenu = function() {
+ "use strict";
+ var s = Menu.prototype;
+ function DefaultMenu() {
+ Menu.apply(this, [].slice.call(arguments, 0));
+ }
+ _.mixin(DefaultMenu.prototype, Menu.prototype, {
+ open: function open() {
+ !this._allDatasetsEmpty() && this._show();
+ return s.open.apply(this, [].slice.call(arguments, 0));
+ },
+ close: function close() {
+ this._hide();
+ return s.close.apply(this, [].slice.call(arguments, 0));
+ },
+ _onRendered: function onRendered() {
+ if (this._allDatasetsEmpty()) {
+ this._hide();
+ } else {
+ this.isOpen() && this._show();
+ }
+ return s._onRendered.apply(this, [].slice.call(arguments, 0));
+ },
+ _onCleared: function onCleared() {
+ if (this._allDatasetsEmpty()) {
+ this._hide();
+ } else {
+ this.isOpen() && this._show();
+ }
+ return s._onCleared.apply(this, [].slice.call(arguments, 0));
+ },
+ setLanguageDirection: function setLanguageDirection(dir) {
+ this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl);
+ return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0));
+ },
+ _hide: function hide() {
+ this.$node.hide();
+ },
+ _show: function show() {
+ this.$node.css("display", "block");
+ }
+ });
+ return DefaultMenu;
+ }();
+ var Typeahead = function() {
+ "use strict";
+ function Typeahead(o, www) {
+ var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged;
+ o = o || {};
+ if (!o.input) {
+ $.error("missing input");
+ }
+ if (!o.menu) {
+ $.error("missing menu");
+ }
+ if (!o.eventBus) {
+ $.error("missing event bus");
+ }
+ www.mixin(this);
+ this.eventBus = o.eventBus;
+ this.minLength = _.isNumber(o.minLength) ? o.minLength : 1;
+ this.input = o.input;
+ this.menu = o.menu;
+ this.enabled = true;
+ this.active = false;
+ this.input.hasFocus() && this.activate();
+ this.dir = this.input.getLangDir();
+ this._hacks();
+ this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this);
+ onFocused = c(this, "activate", "open", "_onFocused");
+ onBlurred = c(this, "deactivate", "_onBlurred");
+ onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed");
+ onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed");
+ onEscKeyed = c(this, "isActive", "_onEscKeyed");
+ onUpKeyed = c(this, "isActive", "open", "_onUpKeyed");
+ onDownKeyed = c(this, "isActive", "open", "_onDownKeyed");
+ onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed");
+ onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed");
+ onQueryChanged = c(this, "_openIfActive", "_onQueryChanged");
+ onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged");
+ this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this);
+ }
+ _.mixin(Typeahead.prototype, {
+ _hacks: function hacks() {
+ var $input, $menu;
+ $input = this.input.$input || $("
");
+ $menu = this.menu.$node || $("
");
+ $input.on("blur.tt", function($e) {
+ var active, isActive, hasActive;
+ active = document.activeElement;
+ isActive = $menu.is(active);
+ hasActive = $menu.has(active).length > 0;
+ if (_.isMsie() && (isActive || hasActive)) {
+ $e.preventDefault();
+ $e.stopImmediatePropagation();
+ _.defer(function() {
+ $input.focus();
+ });
+ }
+ });
+ $menu.on("mousedown.tt", function($e) {
+ $e.preventDefault();
+ });
+ },
+ _onSelectableClicked: function onSelectableClicked(type, $el) {
+ this.select($el);
+ },
+ _onDatasetCleared: function onDatasetCleared() {
+ this._updateHint();
+ },
+ _onDatasetRendered: function onDatasetRendered(type, dataset, suggestions, async) {
+ this._updateHint();
+ this.eventBus.trigger("render", suggestions, async, dataset);
+ },
+ _onAsyncRequested: function onAsyncRequested(type, dataset, query) {
+ this.eventBus.trigger("asyncrequest", query, dataset);
+ },
+ _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) {
+ this.eventBus.trigger("asynccancel", query, dataset);
+ },
+ _onAsyncReceived: function onAsyncReceived(type, dataset, query) {
+ this.eventBus.trigger("asyncreceive", query, dataset);
+ },
+ _onFocused: function onFocused() {
+ this._minLengthMet() && this.menu.update(this.input.getQuery());
+ },
+ _onBlurred: function onBlurred() {
+ if (this.input.hasQueryChangedSinceLastFocus()) {
+ this.eventBus.trigger("change", this.input.getQuery());
+ }
+ },
+ _onEnterKeyed: function onEnterKeyed(type, $e) {
+ var $selectable;
+ if ($selectable = this.menu.getActiveSelectable()) {
+ this.select($selectable) && $e.preventDefault();
+ }
+ },
+ _onTabKeyed: function onTabKeyed(type, $e) {
+ var $selectable;
+ if ($selectable = this.menu.getActiveSelectable()) {
+ this.select($selectable) && $e.preventDefault();
+ } else if ($selectable = this.menu.getTopSelectable()) {
+ this.autocomplete($selectable) && $e.preventDefault();
+ }
+ },
+ _onEscKeyed: function onEscKeyed() {
+ this.close();
+ },
+ _onUpKeyed: function onUpKeyed() {
+ this.moveCursor(-1);
+ },
+ _onDownKeyed: function onDownKeyed() {
+ this.moveCursor(+1);
+ },
+ _onLeftKeyed: function onLeftKeyed() {
+ if (this.dir === "rtl" && this.input.isCursorAtEnd()) {
+ this.autocomplete(this.menu.getTopSelectable());
+ }
+ },
+ _onRightKeyed: function onRightKeyed() {
+ if (this.dir === "ltr" && this.input.isCursorAtEnd()) {
+ this.autocomplete(this.menu.getTopSelectable());
+ }
+ },
+ _onQueryChanged: function onQueryChanged(e, query) {
+ this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty();
+ },
+ _onWhitespaceChanged: function onWhitespaceChanged() {
+ this._updateHint();
+ },
+ _onLangDirChanged: function onLangDirChanged(e, dir) {
+ if (this.dir !== dir) {
+ this.dir = dir;
+ this.menu.setLanguageDirection(dir);
+ }
+ },
+ _openIfActive: function openIfActive() {
+ this.isActive() && this.open();
+ },
+ _minLengthMet: function minLengthMet(query) {
+ query = _.isString(query) ? query : this.input.getQuery() || "";
+ return query.length >= this.minLength;
+ },
+ _updateHint: function updateHint() {
+ var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match;
+ $selectable = this.menu.getTopSelectable();
+ data = this.menu.getSelectableData($selectable);
+ val = this.input.getInputValue();
+ if (data && !_.isBlankString(val) && !this.input.hasOverflow()) {
+ query = Input.normalizeQuery(val);
+ escapedQuery = _.escapeRegExChars(query);
+ frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i");
+ match = frontMatchRegEx.exec(data.val);
+ match && this.input.setHint(val + match[1]);
+ } else {
+ this.input.clearHint();
+ }
+ },
+ isEnabled: function isEnabled() {
+ return this.enabled;
+ },
+ enable: function enable() {
+ this.enabled = true;
+ },
+ disable: function disable() {
+ this.enabled = false;
+ },
+ isActive: function isActive() {
+ return this.active;
+ },
+ activate: function activate() {
+ if (this.isActive()) {
+ return true;
+ } else if (!this.isEnabled() || this.eventBus.before("active")) {
+ return false;
+ } else {
+ this.active = true;
+ this.eventBus.trigger("active");
+ return true;
+ }
+ },
+ deactivate: function deactivate() {
+ if (!this.isActive()) {
+ return true;
+ } else if (this.eventBus.before("idle")) {
+ return false;
+ } else {
+ this.active = false;
+ this.close();
+ this.eventBus.trigger("idle");
+ return true;
+ }
+ },
+ isOpen: function isOpen() {
+ return this.menu.isOpen();
+ },
+ open: function open() {
+ if (!this.isOpen() && !this.eventBus.before("open")) {
+ this.menu.open();
+ this._updateHint();
+ this.eventBus.trigger("open");
+ }
+ return this.isOpen();
+ },
+ close: function close() {
+ if (this.isOpen() && !this.eventBus.before("close")) {
+ this.menu.close();
+ this.input.clearHint();
+ this.input.resetInputValue();
+ this.eventBus.trigger("close");
+ }
+ return !this.isOpen();
+ },
+ setVal: function setVal(val) {
+ this.input.setQuery(_.toStr(val));
+ },
+ getVal: function getVal() {
+ return this.input.getQuery();
+ },
+ select: function select($selectable) {
+ var data = this.menu.getSelectableData($selectable);
+ if (data && !this.eventBus.before("select", data.obj)) {
+ this.input.setQuery(data.val, true);
+ this.eventBus.trigger("select", data.obj);
+ this.close();
+ return true;
+ }
+ return false;
+ },
+ autocomplete: function autocomplete($selectable) {
+ var query, data, isValid;
+ query = this.input.getQuery();
+ data = this.menu.getSelectableData($selectable);
+ isValid = data && query !== data.val;
+ if (isValid && !this.eventBus.before("autocomplete", data.obj)) {
+ this.input.setQuery(data.val);
+ this.eventBus.trigger("autocomplete", data.obj);
+ return true;
+ }
+ return false;
+ },
+ moveCursor: function moveCursor(delta) {
+ var query, $candidate, data, payload, cancelMove;
+ query = this.input.getQuery();
+ $candidate = this.menu.selectableRelativeToCursor(delta);
+ data = this.menu.getSelectableData($candidate);
+ payload = data ? data.obj : null;
+ cancelMove = this._minLengthMet() && this.menu.update(query);
+ if (!cancelMove && !this.eventBus.before("cursorchange", payload)) {
+ this.menu.setCursor($candidate);
+ if (data) {
+ this.input.setInputValue(data.val);
+ } else {
+ this.input.resetInputValue();
+ this._updateHint();
+ }
+ this.eventBus.trigger("cursorchange", payload);
+ return true;
+ }
+ return false;
+ },
+ destroy: function destroy() {
+ this.input.destroy();
+ this.menu.destroy();
+ }
+ });
+ return Typeahead;
+ function c(ctx) {
+ var methods = [].slice.call(arguments, 1);
+ return function() {
+ var args = [].slice.call(arguments);
+ _.each(methods, function(method) {
+ return ctx[method].apply(ctx, args);
+ });
+ };
+ }
+ }();
+ (function() {
+ "use strict";
+ var old, keys, methods;
+ old = $.fn.typeahead;
+ keys = {
+ www: "tt-www",
+ attrs: "tt-attrs",
+ typeahead: "tt-typeahead"
+ };
+ methods = {
+ initialize: function initialize(o, datasets) {
+ var www;
+ datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1);
+ o = o || {};
+ www = WWW(o.classNames);
+ return this.each(attach);
+ function attach() {
+ var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, typeahead, MenuConstructor;
+ _.each(datasets, function(d) {
+ d.highlight = !!o.highlight;
+ });
+ $input = $(this);
+ $wrapper = $(www.html.wrapper);
+ $hint = $elOrNull(o.hint);
+ $menu = $elOrNull(o.menu);
+ defaultHint = o.hint !== false && !$hint;
+ defaultMenu = o.menu !== false && !$menu;
+ defaultHint && ($hint = buildHintFromInput($input, www));
+ defaultMenu && ($menu = $(www.html.menu).css(www.css.menu));
+ $hint && $hint.val("");
+ $input = prepInput($input, www);
+ if (defaultHint || defaultMenu) {
+ $wrapper.css(www.css.wrapper);
+ $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint);
+ $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null);
+ }
+ MenuConstructor = defaultMenu ? DefaultMenu : Menu;
+ eventBus = new EventBus({
+ el: $input
+ });
+ input = new Input({
+ hint: $hint,
+ input: $input
+ }, www);
+ menu = new MenuConstructor({
+ node: $menu,
+ datasets: datasets
+ }, www);
+ typeahead = new Typeahead({
+ input: input,
+ menu: menu,
+ eventBus: eventBus,
+ minLength: o.minLength
+ }, www);
+ $input.data(keys.www, www);
+ $input.data(keys.typeahead, typeahead);
+ }
+ },
+ isEnabled: function isEnabled() {
+ var enabled;
+ ttEach(this.first(), function(t) {
+ enabled = t.isEnabled();
+ });
+ return enabled;
+ },
+ enable: function enable() {
+ ttEach(this, function(t) {
+ t.enable();
+ });
+ return this;
+ },
+ disable: function disable() {
+ ttEach(this, function(t) {
+ t.disable();
+ });
+ return this;
+ },
+ isActive: function isActive() {
+ var active;
+ ttEach(this.first(), function(t) {
+ active = t.isActive();
+ });
+ return active;
+ },
+ activate: function activate() {
+ ttEach(this, function(t) {
+ t.activate();
+ });
+ return this;
+ },
+ deactivate: function deactivate() {
+ ttEach(this, function(t) {
+ t.deactivate();
+ });
+ return this;
+ },
+ isOpen: function isOpen() {
+ var open;
+ ttEach(this.first(), function(t) {
+ open = t.isOpen();
+ });
+ return open;
+ },
+ open: function open() {
+ ttEach(this, function(t) {
+ t.open();
+ });
+ return this;
+ },
+ close: function close() {
+ ttEach(this, function(t) {
+ t.close();
+ });
+ return this;
+ },
+ select: function select(el) {
+ var success = false, $el = $(el);
+ ttEach(this.first(), function(t) {
+ success = t.select($el);
+ });
+ return success;
+ },
+ autocomplete: function autocomplete(el) {
+ var success = false, $el = $(el);
+ ttEach(this.first(), function(t) {
+ success = t.autocomplete($el);
+ });
+ return success;
+ },
+ moveCursor: function moveCursoe(delta) {
+ var success = false;
+ ttEach(this.first(), function(t) {
+ success = t.moveCursor(delta);
+ });
+ return success;
+ },
+ val: function val(newVal) {
+ var query;
+ if (!arguments.length) {
+ ttEach(this.first(), function(t) {
+ query = t.getVal();
+ });
+ return query;
+ } else {
+ ttEach(this, function(t) {
+ t.setVal(newVal);
+ });
+ return this;
+ }
+ },
+ destroy: function destroy() {
+ ttEach(this, function(typeahead, $input) {
+ revert($input);
+ typeahead.destroy();
+ });
+ return this;
+ }
+ };
+ $.fn.typeahead = function(method) {
+ if (methods[method]) {
+ return methods[method].apply(this, [].slice.call(arguments, 1));
+ } else {
+ return methods.initialize.apply(this, arguments);
+ }
+ };
+ $.fn.typeahead.noConflict = function noConflict() {
+ $.fn.typeahead = old;
+ return this;
+ };
+ function ttEach($els, fn) {
+ $els.each(function() {
+ var $input = $(this), typeahead;
+ (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input);
+ });
+ }
+ function buildHintFromInput($input, www) {
+ return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop("readonly", true).removeAttr("id name placeholder required").attr({
+ autocomplete: "off",
+ spellcheck: "false",
+ tabindex: -1
+ });
+ }
+ function prepInput($input, www) {
+ $input.data(keys.attrs, {
+ dir: $input.attr("dir"),
+ autocomplete: $input.attr("autocomplete"),
+ spellcheck: $input.attr("spellcheck"),
+ style: $input.attr("style")
+ });
+ $input.addClass(www.classes.input).attr({
+ autocomplete: "off",
+ spellcheck: false
+ });
+ try {
+ !$input.attr("dir") && $input.attr("dir", "auto");
+ } catch (e) {}
+ return $input;
+ }
+ function getBackgroundStyles($el) {
+ return {
+ backgroundAttachment: $el.css("background-attachment"),
+ backgroundClip: $el.css("background-clip"),
+ backgroundColor: $el.css("background-color"),
+ backgroundImage: $el.css("background-image"),
+ backgroundOrigin: $el.css("background-origin"),
+ backgroundPosition: $el.css("background-position"),
+ backgroundRepeat: $el.css("background-repeat"),
+ backgroundSize: $el.css("background-size")
+ };
+ }
+ function revert($input) {
+ var www, $wrapper;
+ www = $input.data(keys.www);
+ $wrapper = $input.parent().filter(www.selectors.wrapper);
+ _.each($input.data(keys.attrs), function(val, key) {
+ _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val);
+ });
+ $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input);
+ if ($wrapper.length) {
+ $input.detach().insertAfter($wrapper);
+ $wrapper.remove();
+ }
+ }
+ function $elOrNull(obj) {
+ var isValid, $el;
+ isValid = _.isJQuery(obj) || _.isElement(obj);
+ $el = isValid ? $(obj).first() : [];
+ return $el.length ? $el : null;
+ }
+ })();
+});
\ No newline at end of file