diff --git a/fec/fec/static/js/init.js b/fec/fec/static/js/init.js
index 8784b9cbcc..0b8752fef9 100644
--- a/fec/fec/static/js/init.js
+++ b/fec/fec/static/js/init.js
@@ -59,4 +59,22 @@ $(function() {
}
$p.nextAll().remove();
});
+
+ /**
+ * Check for an in-page element with a classList that contains 'js-launch-fecfile-eligibility'.
+ * If any are found, add the js and css files for it.
+ * fecfile-eligibility.js will activate the launcher button
+ */
+ let eligibilityLauncher = document.querySelector('.js-launch-fecfile-eligibility');
+ if (eligibilityLauncher) {
+ let newScript = document.createElement('script');
+ newScript.src = '/static/js/widgets/fecfile-eligibility.js'; // This could begin with 'https://www.fec.gov/'
+ document.body.append(newScript);
+
+ let eligibilityStyles = document.createElement('link');
+ eligibilityStyles.rel = 'stylesheet';
+ eligibilityStyles.href = '/static/css/widgets/fecfile-eligibility.css'; // This could begin with 'https://www.fec.gov/'
+ document.body.append(eligibilityStyles);
+ }
+ eligibilityLauncher = undefined; // No reason to keep the var
});
diff --git a/fec/fec/static/js/modules/calc-admin-fines.js b/fec/fec/static/js/modules/calc-admin-fines.js
index 761fa3f7f1..81a5239461 100644
--- a/fec/fec/static/js/modules/calc-admin-fines.js
+++ b/fec/fec/static/js/modules/calc-admin-fines.js
@@ -1,7 +1,7 @@
/**
//
*/
-import Vue from 'vue/dist/vue.esm.js';
+import Vue from 'vue-2/dist/vue.esm.js';
import { availableDates, getTotalAdminFine } from './calc-admin-fines-logic.js';
@@ -355,7 +355,7 @@ Vue.component('frames', {
return this[q.showIfVar1] === q.showIfVar1ExpectedValue;
}
},
- frameClass: function(frameIndex, addtionalClasses) {
+ frameClass: function(frameIndex, additionalClasses) {
return [
'frame',
{
@@ -363,7 +363,7 @@ Vue.component('frames', {
current: frameIndex == this.currentFrameNum,
'next off-screen': frameIndex > this.currentFrameNum
},
- addtionalClasses
+ additionalClasses
// {
// viewed: this.frames[navIndex].viewed || navIndex == 0,
// current: navIndex == this.currentFrameNum,
@@ -652,7 +652,7 @@ new Vue({
help: `
For election sensitive reports, a committee is a non-filer if it files after this point or doesn't file at all.
A committee is a late filer if it files the report after the due date, but more than four days before the date of the applicable election.
For non-election sensitive reports, a committee is a non-filer if it files its report later than that or not at all.
- A committee will be considerered a late filer if it files its report within 30 days after the due date.
`
+ A committee will be considered a late filer if it files its report within 30 days after the due date.
`
}
],
viewed: false
@@ -871,7 +871,7 @@ new Vue({
} else if (passedTests === true) {
// If the value is undefined, this is the first time it's being set so let's advance
let autoStep = this[affectedVmodel] == undefined ? true : false;
- // …unless we specifially shouldn't autoadvance
+ // …unless we specifically shouldn't autoadvance
autoStep = frameShouldAutoAdvance;
//
this.setBreadCrumbText(frameNum, qNum, q);
diff --git a/fec/fec/static/js/widgets/fecfile-eligibility.js b/fec/fec/static/js/widgets/fecfile-eligibility.js
new file mode 100644
index 0000000000..4c079b1dce
--- /dev/null
+++ b/fec/fec/static/js/widgets/fecfile-eligibility.js
@@ -0,0 +1,961 @@
+/**
+ * Component to direct filers to FECfile+ or other filing applications.
+ * Expects a button/link in the page ( @see launcherButtonSelector ), activates it,
+ * and may start itself in an open state if the window.location.href sets dialog=open
+ */
+import { createApp } from 'vue/dist/vue.esm-bundler.js';
+
+// Quick-edits
+// (The full data is the last and largest section of this file)
+const goalHref = 'https://fecfile.fec.gov/login';
+
+export default function FecFileEligibility() {
+ this.launcherButtonSelector = '.js-launch-fecfile-eligibility'; // How to find the button to launch this
+ this.appElSelector = 'gov-fec-fecfile-eligibility'; // How to name the main Vue elements, also used in the css
+ this.modalDialog; // The
+ this.app; // The converted into this app
+ this.frames; // The frames of content
+ this.init();
+}
+
+/**
+ * Kick it off. Finds the launcher button, adds the listener, activates it, then opens the
if needed
+ */
+FecFileEligibility.prototype.init = function() {
+ let launcherButton = document.querySelector(this.launcherButtonSelector);
+
+ if (launcherButton) {
+ this.buildModalDialog();
+ launcherButton.classList.remove('is-disabled');
+ launcherButton.addEventListener('click', this.handleOpenDialogClick.bind(this));
+ }
+
+ // Should we start in the open state?
+ let searchParams = new URLSearchParams(window.location.search);
+ if (searchParams && searchParams.get('dialog') === 'open') this.handleOpenDialogClick();
+};
+
+/**
+ * Creates this in document.body and listens for the close button's click event
+ */
+FecFileEligibility.prototype.buildModalDialog = function() {
+ this.modalDialog = document.createElement('dialog');
+ this.modalDialog.setAttribute('id', `modal-${this.appElSelector}`);
+ this.modalDialog.setAttribute('closedby', 'any');
+ this.modalDialog.innerHTML = `
+
+
+
+
Is FECfile+ right for my committee?
+
+
`;
+ document.body.appendChild(this.modalDialog);
+ this.modalDialog.querySelector('.modal__close').addEventListener('click', this.handleCloseDialogClick.bind(this));
+};
+
+/**
+ * Builds the Vue app (if needed), then opens the
with showModal();
+ */
+FecFileEligibility.prototype.handleOpenDialogClick = function() {
+ // If we haven't built the app yet, do that now
+ if (!this.app) this.initMainComponent();
+
+ this.modalDialog.showModal();
+};
+
+/**
+ * Calls .close() on the
+ */
+FecFileEligibility.prototype.handleCloseDialogClick = function() {
+ this.modalDialog.close();
+};
+
+/**
+ * Builds the TopNav, BottomNav, Help, and Frames components and adds them to this.app ( @see initMainComponent() )
+ */
+FecFileEligibility.prototype.buildInnerComponents = function() {
+ /**
+ * The Vue component
+ */
+ this.app.component('TopNav', {
+ props: {
+ currentFrameNum: {
+ type: Number,
+ required: true
+ },
+ frames: {
+ type: Array,
+ required: true
+ }
+ },
+ template: `
+
+
+
+
+ `,
+ methods: {
+ handleClick: function(i) {
+ this.$emit('handle-click', i);
+ },
+ topNavClass: function(navIndex) {
+ return [
+ {
+ viewed: this.frames[navIndex].viewed || navIndex == 0,
+ current: navIndex == this.currentFrameNum,
+ hidden: navIndex == 0 || navIndex == this.frames.length - 1,
+ 'hide-after':
+ navIndex < 1 ||
+ navIndex >= this.frames.length - 2 ||
+ (navIndex == this.currentFrameNum &&
+ !this.frames[navIndex + 1].viewed === true)
+ }
+ // hide-after should be included for nav elements that
+ // - aren't the first (intro frame)
+ // - aren't the last (outro/total frame)
+ // - don't have a value BUT only when the next frame hasn't been viewed
+ ];
+ }
+ }
+ });
+
+ /**
+ * The Vue component
+ */
+ this.app.component('BottomNav', {
+ props: {
+ currentFrameNum: {
+ type: Number,
+ required: true
+ },
+ frames: {
+ type: Array,
+ required: true
+ }
+ },
+ template: `
+
+
+ Back
+ Next
+ Close
+ Create a FECfile+ account and sign in with Login.gov
+ Start over
+ `,
+ methods: {
+ bottomNavClass: function(buttonID) {
+ let isFirstFrame = this.currentFrameNum == 0;
+ let isLastFrame = this.currentFrameNum >= this.frames.length - 1;
+ let isNotLastFrame = !isLastFrame;
+ let currentFrameNeedsAnswer = !this.frames[this.currentFrameNum].viewed;
+
+ return {
+ // style classes:
+ 'button--back': buttonID == 'prev',
+ 'button--alt': buttonID == 'prev' || buttonID == 'restart',
+ 'button--go': buttonID == 'next' || buttonID == 'restart',
+ 'button--cta': buttonID == 'next' || buttonID == 'close' || buttonID == 'go',
+ // behavior classes:
+ 'is-disabled':
+ (isFirstFrame && buttonID == 'prev') ||
+ (currentFrameNeedsAnswer && buttonID == 'next'),
+ hidden:
+ (isLastFrame && (buttonID == 'next' || buttonID == 'close')) ||
+ (isNotLastFrame && (buttonID == 'close' || buttonID == 'restart' || buttonID == 'go')),
+ login: buttonID == 'go'
+ };
+ },
+ handleClick: function(id, e) {
+ this.$emit('handle-click', id, e);
+ }
+ }
+ });
+
+ /**
+ * The Vue component
+ */
+ this.app.component('Help', {
+ props: {
+ frames: {
+ type: Array,
+ required: true
+ },
+ helpClass: {
+ type: String,
+ required: false
+ },
+ helpContent: {
+ type: String,
+ required: false
+ },
+ helpTitle: {
+ type: String,
+ required: false
+ },
+ helpPointerY: {
+ type: Number,
+ required: false
+ }
+ },
+ template: `
+
+
+
+
+
{{ helpTitle }}
+
+
+
+
`,
+ methods: {
+ handleCloseClick: function() {
+ this.$emit('close-click');
+ }
+ }
+ });
+
+ /**
+ * The Vue component
+ */
+ this.app.component('Frames', {
+ props: {
+ currentFrameNum: {
+ type: Number,
+ required: true
+ },
+ frames: {
+ type: Array,
+ required: true
+ },
+ isAuthorizedCommittee: {
+ type: String,
+ required: false
+ },
+ isPacOrParty: {
+ type: String,
+ required: false
+ },
+ isRegistered: {
+ type: String,
+ required: false
+ },
+ hasFiled: {
+ type: String,
+ required: false
+ },
+ plansToAllocate: {
+ type: String,
+ required: false
+ }
+ },
+ template: `
+ `,
+ methods: {
+ /**
+ * @param {Proxy(Object)} q
+ * @returns {boolean}
+ */
+ evalFieldShowRule: function(q) {
+ // If there's no rule, default to showing the field
+ if (!q || !q.showIfVar || !q.showIfVarExpectedValue) return true;
+
+ return this[q.showIfVar] === q.showIfVarExpectedValue;
+ },
+ /**
+ * Returns a string of class names for a given frame based on currentFrameNum
+ * @param {number} frameIndex - Frame #/index
+ * @param {string} additionalClasses - Optional additional class names to include
+ * @returns {string}
+ */
+ frameClass: function(frameIndex, additionalClasses) {
+ return [
+ 'frame',
+ {
+ previous: frameIndex < this.currentFrameNum,
+ current: frameIndex == this.currentFrameNum,
+ 'next off-screen': frameIndex > this.currentFrameNum
+ },
+ additionalClasses
+ // {
+ // viewed: this.frames[navIndex].viewed || navIndex == 0,
+ // current: navIndex == this.currentFrameNum,
+ // hidden: navIndex == 0 || navIndex == this.frames.length - 1,
+ // 'hide-before': navIndex < 2 || navIndex >= this.frames.length - 1
+ // }
+ ];
+ },
+ getVarVal: function(valName) {
+ return this[valName];
+ },
+ /**
+ * Local click event handler that's broadcast upward
+ * @param {string} id - The type of button e.g. 'back', 'go', 'start'
+ * @param {PointerEvent} e
+ */
+ handleButtonClick: function(id, e) {
+ this.$emit('button-click', id, e);
+ },
+ handleQuestionInput: function(
+ frame_index,
+ question_index,
+ question,
+ $event
+ ) {
+ this.$emit(
+ 'question-input',
+ frame_index,
+ question_index,
+ question,
+ $event
+ );
+ },
+ handleHelpClick: function(title, questionHelp, e) {
+ this.$emit('help-i-click', title, questionHelp, e);
+ }
+ }
+ });
+};
+
+/**
+ * Builds the main Vue component, builds the inner components ( @see buildInnerComponents ), then mounts the app
+ */
+FecFileEligibility.prototype.initMainComponent = function() {
+ let appConfig = {
+ data() {
+ return fullData;
+ },
+ template: `
+
+
+
+
+
`,
+ mounted: function() {
+ // Add the transition listeners so frames disappear while out of sight
+ this.startWatchingTransitions();
+
+ // Set initial focus on the first frame's button, but the browser needs to render everything first
+ requestAnimationFrame(() => {
+ document.querySelector('.autofocus').focus();
+ });
+ },
+ computed: {
+ helpClass: function() {
+ return this.showHelp == false ? 'hidden' : 'show';
+ }
+ },
+ methods: {
+ handleButtonClick: function(buttonType, e) {
+ e.preventDefault();
+ if (buttonType == 'start') this.handleTopNavClick(1);
+ else if (buttonType == 'next')
+ this.handleTopNavClick(this.currentFrameNum + 1);
+ else if (buttonType == 'back') this.currentFrameNum--;
+ else if (buttonType == 'go') {
+ window.open(goalHref);
+ }
+ else if (buttonType == 'restart') this.restart();
+ else if (buttonType == 'close') {
+ document.dispatchEvent(new CustomEvent('CLOSE_MODAL'), {
+ detail: {
+ 'data-a11y-dialog-hide': 'gov-fec-calc-af'
+ }
+ });
+ }
+ this.toggleHelp(); // toggling help with no content will hide it
+ },
+ handleTopNavClick: function(navIndex) {
+ // this.frames[navIndex].viewed = true;
+ this.currentFrameNum = navIndex;
+ },
+ handleQuestionInput: function(frameNum, qNum, q, e) {
+ let affectedVmodel = q.vModel;
+ let newValue = q.value ? q.value : e.target.value;
+ let passedTests = false;
+ //
+ // Set the value
+ this[affectedVmodel] = newValue;
+
+ passedTests = q.roadblockContent === '';
+
+ this.frames[this.currentFrameNum].viewed = passedTests;
+ this.setBreadCrumbText(frameNum, qNum, q);
+ },
+ restart: function() {
+ // reset vars
+ this.currentFrameNum = 0;
+ this.isAuthorizedCommittee = undefined;
+ this.isPacOrParty = undefined;
+ this.isRegistered = undefined;
+ this.hasFiled = undefined;
+ this.plansToAllocate = undefined;
+ this.showHelp = false;
+ // Set all the frames to not viewed (for breadcrumbs and Next button)
+ this.frames.forEach(frame => {frame.viewed.false;});
+ // for (let i = 0; i < this.frames.length; i++) {
+ // this.frames[i].viewed = false;
+ // }
+ //
+ // clear breadcrumbs
+ this.setBreadCrumbText('reset');
+ //
+ // TODO - a better way to reset all the form values?
+ let theForm = document.querySelector('form.frames');
+ theForm.reset();
+ },
+ setBreadCrumbText: function(frameNum, qNum, q) {
+ // If we're setting a blank frameNum, we're trying to reset
+ if (frameNum == 'reset') {
+ for (let i = 0; i < this.frames.length; i++) {
+ this.frames[i].navLabel = '';
+ }
+ return;
+ } else if (q == '') {
+ // If we're setting a q of '', we're trying to clear the text for the current nav (like on frame error)
+ this.frames[frameNum].navLabel = ' ';
+ return;
+ }
+
+ let theBreadCrumbText = String(q.breadCrumbText);
+
+ if (frameNum > 0 && q && q.breadCrumbText)
+ this.frames[frameNum].navLabel = theBreadCrumbText;
+ },
+ startWatchingTransitions: function() {
+ const frames = document.querySelectorAll('.frame');
+
+ // Add off-screen to all non-intro frames
+ for (let i = 0; i < frames.length; i++) {
+ //
+ if (!frames[i].classList.contains('intro'))
+ frames[i].classList.add('off-screen');
+
+ frames[i].addEventListener('transitionstart', function(e) {
+ if (e.target.classList.contains('frame'))
+ e.target.classList.remove('off-screen');
+ });
+
+ frames[i].addEventListener('transitionend', function(e) {
+ if (
+ e.target.classList.contains('frame') &&
+ !e.target.classList.contains('current')
+ )
+ e.target.classList.add('off-screen');
+ });
+ }
+ },
+ toggleHelp: function(title, html, e) {
+ if (e) e.preventDefault();
+ if ((this.showHelp && html == this.helpContent) || (!title && !html))
+ this.showHelp = false;
+ else {
+ this.showHelp = true;
+ this.helpContent = html;
+ this.helpTitle = title;
+ // set the help pointer to
+ // (the top of the circled i icon), minus
+ // (the top of the help section), minus
+ // (half of the difference between the heights of the circled i and the help pointer itself)
+ this.helpPointerY =
+ e.target.getBoundingClientRect().top -
+ this.$el.querySelector('#help').getBoundingClientRect().top -
+ 10;
+ }
+ }
+ }
+ };
+
+ this.app = createApp(appConfig);
+ this.buildInnerComponents();
+ this.app.mount(`#${this.appElSelector}`);
+};
+
+/**
+ * The full data for the app. All content changes should be made below
+ */
+const fullData = {
+ currentFrameNum: 0, // int
+ isAuthorizedCommittee: undefined, // bool
+ isPacOrParty: undefined, // string
+ isRegistered: undefined, // bool
+ hasFiled: undefined, // bool
+ plansToAllocate: undefined, // bool
+ helpTitle: '',
+ helpContent: '',
+ helpPointerY: 0,
+ showHelp: false,
+ frames: [
+ {
+ navLabel: '',
+ title: '',
+ class: 'intro',
+ content: [
+ {
+ type: 'p',
+ class: '',
+ content:
+ 'This tool is to help committees and their teams determine whether using FECfile+ is the right \
+ step, or whether other electronic filing tools would be better to comply with reporting requirements \
+ at\u00A0this\u00A0time.' // \u00A0 is a nonbreaking space
+ },
+ {
+ type: 'button',
+ class: 'button button--cta button--go autofocus', // 'autofocus' is for the
+ label: 'Get started',
+ actionId: 'start'
+ }
+ ],
+ viewed: true
+ },
+ {
+ navLabel: '',
+ title: 'Is your committee authorized by a federal\u00A0candidate?',
+ questions: [
+ { type: 'clear' },
+ {
+ label: 'Yes, this committee is authorized',
+ type: 'radio',
+ vModel: 'isAuthorizedCommittee',
+ value: 'true',
+ breadCrumbText: 'Authorized committee',
+ class: 'is_roadblock',
+ helpTitle: 'What is an authorized committee?',
+ help: `An authorized committee is a political committee that has been authorized by a
+ candidate to accept contributions or make expenditures on his or her behalf, or one that accepts
+ contributions or makes expenditures on behalf of a candidate and has not been disavowed by
+ the candidate.
Example: Friends of John McCain Inc `,
+ roadblockContent: `
+
+
Authorized committees are not currently supported by FECfile+ and we recommend using the original
+ FECFile desktop platform, or a third-party service to meet reporting obligations.
+
+
+ `
+ },
+ { type: 'clear' },
+ {
+ label: 'No, this committee is not\u00A0authorized',
+ type: 'radio',
+ vModel: 'isAuthorizedCommittee',
+ value: 'false',
+ breadCrumbText: 'Not authorized',
+ helpTitle: 'What is a committee not authorized by a candidate?',
+ help: `A committee that is not authorized is a political committee that has not been
+ authorized by a candidate to accept contributions or make expenditures on his or her behalf, or one that
+ does not accept contributions or makes expenditures on behalf of a candidate or has been disavowed by
+ the candidate.
Example: American Jobs PAC `,
+ roadblockContent: ''
+ }
+ ],
+ viewed: false
+ },
+ {
+ navLabel: '',
+ title: 'Is your committee a political action committee (PAC) or political party\u00A0committee?',
+ questions: [
+ { type: 'clear' },
+ {
+ label: 'Yes, my committee is a PAC',
+ type: 'radio',
+ vModel: 'isPacOrParty',
+ value: 'PAC',
+ breadCrumbText: 'Political action committee (PAC)',
+ helpTitle: 'What is a political action committee, or PAC?',
+ help: `A popular term for a political committee that is neither a party committee nor an
+ authorized committee of a candidate. PACs directly or indirectly established, administered or financially
+ supported by a corporation or labor organization are called separate segregated funds (SSFs). PACs without
+ such a corporate or labor sponsor are called nonconnected PACs.
Example: Friends of John
+ McCain Inc `,
+ roadblockContent: ''
+ },
+ { type: 'clear' },
+ {
+ label: 'Yes, my committee is a political party\u00A0committee',
+ type: 'radio',
+ vModel: 'isPacOrParty',
+ value: 'party',
+ breadCrumbText: 'Political party committee',
+ helpTitle: 'What is a party committee?',
+ help: `A political committee that represents a political party and is part of the
+ official party structure at the national, state or local level.
`,
+ roadblockContent: ''
+ },
+ { type: 'clear' },
+ {
+ label: 'No, my committee is neither of\u00A0those',
+ type: 'radio',
+ vModel: 'isPacOrParty',
+ value: 'false',
+ breadCrumbText: 'Not a PAC or party committee',
+ class: 'is_roadblock',
+ helpTitle: 'What is an authorized committee?',
+ help: `A political committee that represents a political party and is part of the
+ official party structure at the national, state or local level.
`,
+ roadblockContent: `
+
+
Only PACs and political party committees are currently supported by FECfile+ and we recommend using
+ the original FECFile desktop platform, or a third-party service to meet reporting obligations.
+
+
+ `
+ }
+ ],
+ viewed: false
+ },
+ {
+ navLabel: '',
+ title: 'Has your committee already registered with the FEC by filing a Form 1 Statement of Organization AND \
+ received a committee\u00A0ID?',
+ questions: [
+ { type: 'clear' },
+ {
+ label: 'Yes, my committee is registered and has a committee ID',
+ type: 'radio',
+ vModel: 'isRegistered',
+ value: 'true',
+ breadCrumbText: 'Registered with ID',
+ helpTitle: '',
+ help: '',
+ roadblockContent: ''
+ },
+ { type: 'clear' },
+ {
+ label: 'No, my committee is not registered',
+ type: 'radio',
+ vModel: 'isRegistered',
+ value: 'false',
+ breadCrumbText: 'Hasn’t registered',
+ class: 'is_roadblock',
+ helpTitle: '',
+ help: '',
+ roadblockContent: `
+
+
Only registered PACs and political party committees with a committee ID are currently
+ supported by FECfile+ and we recommend using the original FECFile desktop platform or a
+ third-party service to meet reporting obligations.
+
Register your
+ committee
+
+ `
+ }
+ ],
+ viewed: false
+ },
+ {
+ navLabel: '',
+ title: 'Has your committee ever filed campaign finance reports that contained financial\u00A0activity?',
+ title_example: 'Example: Form 3X for reporting receipts and disbursements',
+ helpTitle: '',
+ help: '',
+ questions: [
+ { type: 'clear' },
+ {
+ label: 'Yes, my committee has filed a financial report',
+ type: 'radio',
+ vModel: 'hasFiled',
+ value: 'true',
+ breadCrumbText: 'Has filed a financial report',
+ class: 'is_roadblock',
+ helpTitle: '',
+ help: '',
+ roadblockContent: `
+
+
At this time, FECfile+ does not include the ability to import transactions or other past
+ activity and it cannot amend reports previously filed using different campaign finance software,
+ so we recommend using the original FECFile desktop platform or a third-party service to meet
+ reporting obligations.
+
+
`
+ },
+ { type: 'clear' },
+ {
+ label: 'No, my committee has not filed a financial report',
+ type: 'radio',
+ vModel: 'hasFiled',
+ value: 'false',
+ breadCrumbText: 'Has not filed financial reports',
+ helpTitle: '',
+ help: '',
+ roadblockContent: ''
+ }
+ ],
+ viewed: false
+ },
+ {
+ navLabel: '',
+ title: 'Does your committee anticipate allocating financial activity between federal and nonfederal accounts and/or anticipate receiving bundled contributions from lobbyists and lobbyist/registrant\u00A0PACs?',
+ title_example: '',
+ helpTitle: 'What does it mean to allocate between federal and non-federal accounts?',
+ help: 'A separate federal account into which funds from either a committee’s federal and nonfederal accounts, \
+ or (for party committees) from its federal and Levin accounts, are deposited solely to pay expenses that must \
+ be allocated. (A party committee must have separate allocation accounts for its federal/nonfederal allocation \
+ and for its federal/Levin allocation.)',
+ questions: [
+ { type: 'clear' },
+ {
+ label: 'Yes, my committee plans to allocate financial activity between federal and nonfederal accounts',
+ type: 'radio',
+ vModel: 'plansToAllocate',
+ value: 'true',
+ breadCrumbText: 'Plans to allocate',
+ class: 'is_roadblock',
+ helpTitle: '',
+ help: '',
+ roadblockContent: `
+
+
At this time, FECfile+ does not include functionality to allow for reporting of allocated \
+ activity , so we recommend using the original FECFile desktop platform or a third-party service \
+ to meet reporting obligations.
+
+
`
+ },
+ { type: 'clear' },
+ {
+ label: 'No, my committee does not plan to allocate financial activity.',
+ type: 'radio',
+ vModel: 'plansToAllocate',
+ value: 'false',
+ breadCrumbText: 'No plans to allocate',
+ helpTitle: '',
+ help: '',
+ roadblockContent: ''
+ }
+ ],
+ viewed: false
+ },
+ {
+ navLabel: '',
+ title: '',
+ class: 'outro',
+ content: [
+ {
+ type: 'p',
+ class: '',
+ html: 'Welcome to FECfile+! '
+ },
+ {
+ type: 'p',
+ class: '',
+ showIfVar: 'isPacOrParty',
+ showIfVarExpectedValue: 'PAC',
+ content:
+ 'As a registered PAC with a committee ID, that has not yet filed any financial reports and has no plans \
+ to allocate activity, your committee is currently supported and can submit reports to meet your reporting \
+ obligations using FECfile+.'
+ },
+ {
+ type: 'p',
+ class: '',
+ showIfVar: 'isPacOrParty',
+ showIfVarExpectedValue: 'party',
+ content:
+ `As a registered political party committee with a committee ID, that has not yet filed any financial \
+ reports and has no plans to allocate activity, your committee is currently supported and can submit \
+ reports to meet your reporting obligations using FECfile+.`
+ },
+ {
+ type: 'p',
+ class: '',
+ html: 'Other caveats to consider: '
+ },
+ {
+ type: 'p',
+ class: '',
+ html:
+ 'No ability to preview reports over 20MB. Reports this size tend to run around 25,000+ \
+ pages and 80,000+ transactions.'
+ },
+ {
+ type: 'p',
+ class: '',
+ html:
+ 'Mobile devices are not recommended at this time. For the best experience, we recommend \
+ using a computer or tablet.'
+ }
+ ],
+ viewed: false
+ }
+ ]
+};
+
+/**
+ * Create an instance of FecFileEligibility()
+ */
+new FecFileEligibility();
diff --git a/fec/fec/static/scss/widgets/fecfile-eligibility.scss b/fec/fec/static/scss/widgets/fecfile-eligibility.scss
new file mode 100644
index 0000000000..ff95182017
--- /dev/null
+++ b/fec/fec/static/scss/widgets/fecfile-eligibility.scss
@@ -0,0 +1,698 @@
+@use '../variables';
+
+$color-base: variables.$base;
+$color-error: variables.$error;
+$color-federal-blue: variables.$federal-blue;
+$color-gray: variables.$gray;
+$color-gray-dark: variables.$gray-dark;
+$color-gray-lightest: variables.$gray-lightest;
+$color-green-light: variables.$green-light;
+$color-gray-medium: variables.$gray-medium;
+$color-inverse: variables.$inverse;
+$font-sans-serif: variables.$sans-serif;
+$font-serif: variables.$serif;
+$font-base-family: variables.$base-font-family;
+$font-rem: 10px; /* To replace ' * $font-rem' sizes, in case the parent page is different */
+$width-med: 40em;
+$width-lg: 860em;
+
+html, body {
+ min-width: 400px !important;
+}
+
+body:has(#modal-gov-fec-fecfile-eligibility[open]) {
+ overflow: hidden; /* prevent scrolling while the is open */
+}
+
+/* Rules specifically for this */
+#modal-gov-fec-fecfile-eligibility {
+ /* Pulled from base.scss etc */
+ border: transparent;
+ color: $color-base;
+ font-family: $font-base-family;
+ font-feature-settings: 'kern', 'liga', 'pnum';
+ font-size: 1.4 * $font-rem;
+ line-height: 1.8 * $font-rem;
+ padding: 0;
+ height: calc(100% - (4 * $font-rem));
+ width: calc(100% - (4 * $font-rem));
+ max-height: 65 * $font-rem;
+ max-width: 80 * $font-rem;
+
+ &::backdrop {
+ background: rgb(from $color-base r g b / 50%);
+ cursor: pointer;
+ }
+
+ h2 {
+ font-family: $font-serif;
+ font-size: 2 * $font-rem; /* then 2.4 * $font-rem */
+ font-weight: bold;
+ line-height: 1.2;
+ width: calc(100% - $font-rem);
+ }
+
+ .modal__content {
+ bottom: unset;
+ height: 100%;
+ left: unset;
+ margin: 0;
+ overflow: hidden;
+ padding: 2 * $font-rem;
+ position: relative;
+ right: unset;
+ top: unset;
+ width: 100%;
+ }
+ .modal__close {
+ position: absolute;
+ right: $font-rem;
+ top: $font-rem;
+ }
+ [data-v-app] {
+ container-name: app;
+ container-type: inline-size;
+ }
+}
+
+/* Vue strips the attributes from its DOM element so we'll use the name */
+#gov-fec-fecfile-eligibility {
+ border-top: 2px solid $color-base;
+ overflow: hidden;
+ position: relative;
+
+ @media screen and (min-width:$width-med) {
+ min-height: calc(100% - (5 * $font-rem));
+ }
+
+ h4 {
+ float: left;
+ font-family: $font-sans-serif;
+ font-size: 1.8 * $font-rem;
+ font-weight: bold;
+ letter-spacing: -0.3px;
+ line-height: 1.2;
+ margin: (0.5 * $font-rem) 0 $font-rem 0;
+ padding-right: 0.5 * $font-rem;
+
+ &:has(+ p) {
+ margin-bottom: 0;
+ }
+
+ &.indented {
+ font-size: 1.4 * $font-rem;
+ max-width: 70%;
+ padding-left: 3.5 * $font-rem;
+ padding-top: 0.5 * $font-rem;
+ }
+ &.subhead {
+ text-transform: uppercase;
+ }
+
+ &.search_example {
+ margin-bottom: 0;
+ }
+
+ button.tooltip__trigger {
+ margin-top: 0.2em;
+ }
+
+ }
+ p {
+ color: inherit;
+ font-size: 1.6 * $font-rem;
+ margin: 0 0 (2.4 * $font-rem) 0;
+ max-width: 80 * $font-rem;
+ width: 100%;
+
+ & + h1 { margin-top: 3 * $font-rem; }
+ & + h2 { margin-top: 3 * $font-rem; }
+ & + h3 { margin-top: 2 * $font-rem; }
+ & + h4 { margin-top: 2 * $font-rem; }
+ }
+
+ .topnav {
+ margin-bottom: $font-rem;
+ min-height: 10 * $font-rem;
+ width: 100%;
+
+ @media screen and (min-width:$width-med) {
+ margin-bottom: 0; /* TODO - is this needed? */
+ min-height: 7.5 * $font-rem;
+ padding-right: calc(33% + (3 * $font-rem));
+ }
+ @media screen and (min-width:$width-lg) {
+ min-height: 7.5 * $font-rem;
+ }
+
+ meter {
+ background: transparent;
+ height: $font-rem;
+ margin-top: $font-rem;
+ width: 100%;
+
+ /* Still need to prefix for these */
+ &::-webkit-meter-bar {
+ background: $color-gray-lightest;
+ border-radius: 0.5 * $font-rem;
+ transition: all 1s;
+ }
+ &::-moz-meter-bar {
+ background: $color-gray-lightest;
+ border-radius: 0.5 * $font-rem;
+ transition: all 1s;
+ }
+ &::-webkit-meter-optimum-value {
+ background: $color-green-light;
+ border-radius: 0.5 * $font-rem;
+ transition: all 1s;
+ }
+ &:-moz-meter-optimum::-moz-meter-bar {
+ background: $color-green-light;
+ border-radius: 0.5 * $font-rem;
+ transition: all 1s;
+ }
+ &.complete {
+ transition: width 0.5s ease-in-out;
+ width: calc(100% - 25px);
+ }
+ }
+ .i-check {
+ background: url('data:image/svg+xml;charset=utf8, %3Csvg%20%20fill%3D%27%234aa564%27%20width%3D%2220%22%20height%3D%2220%22%20viewBox%3D%220%200%2020%2020%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M16.47%206.678l.177.177a1.233%201.233%200%201%200-1.743-1.744l-6.84%206.84h.354L4.854%208.387a1.233%201.233%200%200%200-1.743%201.744l4.259%204.258a1.23%201.23%200%200%200%201.743%200l7.534-7.534-.176-.177z%22%2F%3E%3C%2Fsvg%3E');
+ background-position: center;
+ clip: rect(0 15px 0 0);
+ clip-path: inset(0 15px 0 0);
+ display: block;
+ float: right;
+ height: 15px;
+ margin-top: 0.75 * $font-rem;
+ overflow: hidden;
+ position: absolute;
+ right: 0;
+ top: 0;
+ width: 15px;
+
+ @media screen and (min-width:$width-med) {
+ right: calc(33% + (3 * $font-rem));
+ }
+ }
+ meter.complete + .i-check {
+ background-size: 100%;
+ clip: unset;
+ clip-path: inset(0);
+ transition: clip-path 0.5s;
+ transition-delay: 0.5s;
+ }
+
+ .breadcrumbs {
+ display: block;
+ }
+
+ li {
+ display: inline-block;
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 0.1s;
+
+ &.viewed {
+ color: purple; /* forcing browser colors */
+ cursor: pointer;
+ display: inline-block;
+ opacity: 1;
+ pointer-events: visible;
+ transition:
+ opacity 1s,
+ color 0.5s;
+ }
+ &.current {
+ color: blue; /* forcing browser colors */
+ cursor: default;
+ display: inline-block;
+ opacity: 1;
+ transition:
+ opacity 1s,
+ color 0.5s;
+ }
+ &::after {
+ color: purple; /* forcing browser colors */
+ content: ' › ';
+ display: inline;
+ padding: (0.5 * $font-rem) (0.8 * $font-rem);
+ }
+ &.breadcrumbs__item {
+ display: inline;
+ font-size: 1.4 * $font-rem;
+ line-height: 1.4em;
+ max-width: none;
+ padding: 0;
+ }
+ a.breadcrumbs__link {
+ border: none;
+ border-bottom: 1px dotted $color-inverse;
+ color: $color-gray-dark;
+ font-size: 1.4 * $font-rem;
+ line-height: 1.6 * $font-rem;
+
+ &:hover {
+ border-bottom: 1px dotted $color-federal-blue;
+ }
+ }
+ &.hidden {
+ display: none !important;
+ }
+ &.hide-after::after {
+ content: '';
+ padding: 0;
+ }
+ }
+ } /* end .topnav */
+
+ .bottomnav {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ gap: .5 * $font-rem;
+ margin: 0 auto;
+ max-width: 100%;
+
+ /* for the last (success!) frame, we'll need to account for the larger login button */
+ @container app (width < 600px) {
+ &:has(.login:not(.hidden)) {
+ justify-content: space-between;
+ }
+
+ button.login {
+ order: -1;
+ width: 100%;
+ }
+ }
+
+ button {
+ margin: 0;
+ }
+
+ .break {
+ display: block;
+ height: 1px;
+ margin: -0 auto;
+ width: 100%;
+ }
+ }
+
+ .frames {
+ container-name: frames;
+ container-type: inline-size;
+ display: block;
+ font-family: $font-sans-serif;
+ height: 370px;
+ overflow: hidden;
+ position: relative;
+ width: 100%;
+
+ @container app (width < 375px) {
+ /* For the last frame at the smallest widths, the bottomnav is pushing off the bottom of the screen */
+ &:has(.outro.current) {
+ height: 320px;
+ }
+ }
+ }
+ .frame {
+ display: block;
+ float: left;
+ height: 370px;
+ left: 100%;
+ overflow-y: scroll;
+ pointer-events: none;
+ position: absolute;
+ top: 0;
+ transition: left 0.25s;
+ width: 100% !important;
+
+ @media screen and (min-width:$width-med) {
+ padding: 1em calc(33% + (3 * $font-rem)) 2em 0; /* 33% to give room for the help content */
+ }
+
+ &.previous {
+ left: -100%;
+ transition: left 0.25s;
+ * {
+ pointer-events: none;
+ user-select: none;
+ }
+ }
+ &.next {
+ * {
+ pointer-events: none;
+ user-select: none;
+ }
+ }
+ &.current {
+ display: block; /* (only need to define this because .next.off-screen will jump to transition complete before animation can start) */
+ left: 0;
+ pointer-events: all;
+ transition: left 0.5s;
+ }
+ &.intro {
+ text-align: center;
+
+ @media screen and (min-width:$width-med) {
+ padding-right: 2em;
+ }
+
+ div {
+ margin: 0 auto;
+ max-width: 80%;
+
+ @media screen and (min-width:$width-med) {
+ max-width: 66%;
+ }
+ }
+ .button--cta {
+ margin-top: 1.2 * $font-rem; /* Little extra space above the button */
+ }
+ }
+ &.off-screen * {
+ display: none;
+ }
+ } /* end .frame */
+ label {
+ clear: both;
+ }
+ .buttons-lockup {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ gap: 0.5 * $font-rem;
+
+ & > * {
+ margin: 0;
+ }
+ }
+ button {
+ &.is-disabled {
+ cursor: default;
+ pointer-events: none;
+ }
+ &.button--back.button--alt {
+ background-image: url('data:image/svg+xml;charset=utf8, %3Csvg%20%20fill%3D%27%23212121%27%20width%3D%2212%22%20height%3D%2210%22%20viewBox%3D%220%200%2012%2010%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M.418%203.809L6.652.124a.851.851%200%200%201%201.294.727v7.382a.852.852%200%200%201-1.284.733L.418%205.276a.852.852%200%200%201%200-1.467z%22%2F%3E%3C%2Fsvg%3E');
+ }
+ &.tooltip__trigger {
+ margin: 0.5em 0 (0.5 * $font-rem);
+ position: relative;
+ }
+ &.tooltip__trigger + p {
+ display: inline-block;
+ float: left;
+ width: auto;
+ }
+ &.hidden {
+ display: none !important;
+ }
+ }
+ div.indented {
+ padding-left: 3.5 * $font-rem;
+ }
+
+ /* buttons inside #help */
+ #help + div > button {
+ margin-top: 0.5 * $font-rem; /* don't crop off the top of the focus highlight */
+
+ @media screen and (min-width:$width-med) {
+ margin-bottom: 2 * $font-rem;
+ }
+ }
+
+ .question-type-block {
+ clear: both;
+ display: inline-block;
+ float: left;
+ position: relative;
+ }
+
+ .t-note {
+ font-family: inherit;
+ font-style: italic;
+ }
+
+ [type='radio'] + label {
+ background-color: transparent;
+ border-color: $color-inverse;
+ float: left;
+ margin-right: 0;
+ transition: border-color 0.5s;
+ width: calc(100% - (3 * $font-rem)); /* Save room for the tooltip on the right */
+
+ /* Let the tooltips be staggered when .frames is 400-560 or >= 658.
+ Staggered is preferred but the smaller widths + linebreaks looks messy */
+ @container frames (400px <= width < 560px) or (width >= 658px) {
+ width: unset;
+ }
+ }
+ [type='radio'] + label:hover {
+ background-color: transparent;
+ border-color: #aeb0b5;
+ transition: border-color 0.5s;
+ }
+ [type='radio']:checked + label {
+ background-color: transparent;
+ border: 1px solid #aeb0b5;
+ transition: border-color 0.5s;
+ }
+
+ [type='radio'].is_roadblock {
+ & + label.is_roadblock + div.is_roadblock {
+ clear: both;
+ max-height: 0;
+ opacity: 0;
+ overflow: hidden;
+ transition: max-height 0.5s ease-out, opacity 0.4s ease-out;
+ width: 100%;
+ }
+
+ &:checked + label.is_roadblock + div.is_roadblock {
+ max-height: 500px;
+ opacity: 1;
+ overflow: hidden;
+ transition: max-height 1s ease-out, opacity 0.5s ease-in;
+ }
+ }
+
+ span.clear {
+ clear: both;
+ display: block;
+ height: 0;
+ margin-bottom: $font-rem;
+ width: 100%;
+ }
+
+ input.label-headline {
+ clear: both;
+ float: left;
+ }
+ label {
+ &.indented {
+ float: left;
+
+ // &.search__example {
+ // clear: none;
+ // padding-top: $font-rem;
+ // }
+ }
+ // &.label-headline {
+ // float: left;
+ // font-size: 1.4 * $font-rem;
+ // font-weight: bold;
+ // letter-spacing: -0.3px;
+ // margin-top: 0.2 * $font-rem;
+ // text-transform: uppercase;
+ // }
+ }
+ p {
+ font-size: 1.4 * $font-rem;
+ line-height: 1.25em;
+ margin-bottom: 1.2 * $font-rem;
+ width: 100%;
+
+ &.indented {
+ clear: both;
+ float: left;
+ max-width: 70%;
+ padding-left: 3.5 * $font-rem;
+
+ @media screen and (min-width:$width-med) {
+ max-width: none;
+ }
+ }
+ }
+ // .contact-item {
+ // margin-bottom: 0;
+ // padding-left: 3.5 * $font-rem;
+
+ // &::before {
+ // margin-right: $font-rem;
+ // }
+ // }
+ // .nonbreaking {
+ // display: inline-block;
+ // }
+ // .search__example {
+ // clear: both;
+ // float: left;
+ // margin-top: 0;
+ // }
+ // span.t-note.indented {
+ // margin-left: 3.5 * $font-rem;
+ // }
+
+ #help {
+ background: url('data:image/svg+xml;charset=utf8, %3Csvg%20%20fill%3D%27%23112e51%27%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2220%22%20height%3D%2220%22%20viewBox%3D%220%200%2020%2020%22%3E%3Cpath%20d%3D%22M10%200a10%2010%200%201%200%2010%2010A10%2010%200%200%200%2010%200zm1.2%2016.1a15.69%2015.69%200%200%201-2.4.3q-.9%200-.9-.6a9.32%209.32%200%200%201%20.2-1.4l.8-4.7a3.08%203.08%200%200%200%20.1-.9c0-.3-.4-.4-1.2-.4l.1-.5a14.22%2014.22%200%200%201%202.5-.3.61.61%200%200%201%20.7.6%2011.48%2011.48%200%200%201-.2%201.4l-.8%204.8a2.25%202.25%200%200%200-.1.7c0%20.4.2.5%201.2.5v.5zm-.5-10a1.15%201.15%200%200%201-1.2-1.2%201.35%201.35%200%200%201%201.3-1.4A1.22%201.22%200%200%201%2012%204.7a1.42%201.42%200%200%201-1.3%201.4z%22%2F%3E%3C%2Fsvg%3E');
+ background-color: $color-inverse;
+ background-position: $font-rem $font-rem;
+ background-repeat: no-repeat;
+ background-size: 3 * $font-rem;
+ border-bottom: thin solid $color-federal-blue;
+ border-bottom-left-radius: $font-rem;
+ border-left: thin solid $color-federal-blue;
+ color: $color-federal-blue;
+ height: 100%;
+ left: $font-rem;
+ padding: $font-rem;
+ position: absolute;
+ top: 0;
+ transition: left 0.5s;
+ width: calc(100% - $font-rem);
+ z-index: 100;
+
+ @media screen and (min-width:$width-med) {
+ background: $color-inverse;
+ border: none;
+ border-bottom-left-radius: initial;
+ display: flex;
+ flex-direction: column;
+ left: initial;
+ min-height: calc(100vh - 114px);
+ padding: $font-rem 0 0 calc($font-rem + 20px);
+ right: 0;
+ transition: right 0.5s;
+ width: calc(33% + 20px);
+
+ &::before {
+ border-left: 1px solid $color-federal-blue;
+ content: '';
+ display: block;
+ height: 100%;
+ left: 19px;
+ position: absolute;
+ top: 0;
+ width: 1px;
+ z-index: 0;
+ }
+ }
+
+ p + ul {
+ margin-bottom: 1.2 * $font-rem;
+ margin-top: 0;
+ }
+
+ & > svg {
+ display: none;
+
+ @media screen and (min-width:$width-med) {
+ display: block;
+ }
+ }
+
+ h3 {
+ line-height: 1.4em;
+ padding-left: 4 * $font-rem;
+ padding-right: 30px;
+
+ @media screen and (min-width:$width-med) {
+ flex-grow: 0;
+ padding-left: 0;
+ padding-right: 0;
+ }
+ }
+
+ .help_scroller {
+ flex-grow: 3;
+ overflow-y: scroll;
+ }
+
+ .filters__toggle {
+ padding: 0;
+ position: absolute;
+ right: 0;
+ top: 0;
+
+ &::before {
+ display: none;
+ }
+
+ @media screen and (min-width:$width-med) {
+ display: none;
+ }
+ }
+
+ .help_content {
+ font-family: $font-sans-serif;
+ font-size: 1.4 * $font-rem;
+ line-height: 1.4em;
+ padding-right: $font-rem;
+
+ @media screen and (min-width:$width-med) {
+ max-height: 100%;
+ overflow-y: scroll;
+ }
+
+ p {
+ line-height: 1.4em;
+ }
+ ul {
+ list-style-type: disc;
+ padding-left: 1.5 * $font-rem;
+ }
+ }
+
+ .pointer {
+ left: 0;
+ position: absolute;
+ top: 100px;
+ transition: top 0.33s;
+ }
+
+ @media screen and (min-width:$width-med) {
+ .ps-scrollbar-y-rail {
+ background: $color-gray-medium;
+ border-left: 1px solid $color-gray-dark;
+ left: 0;
+ position: absolute;
+ right: auto;
+ width: 4px;
+ }
+
+ .ps-scrollbar-y {
+ background: $color-gray-dark;
+ left: 0;
+ position: absolute;
+ right: auto;
+ width: 4px;
+ }
+ }
+
+ &.hidden {
+ left: 120%;
+ transition: left 0.5s;
+
+ @media screen and (min-width:$width-med) {
+ left: initial;
+ right: -40%;
+ transition: right 0.5s;
+ }
+
+ .filters__toggle {
+ display: none; /* hide it explicitly to * $font-remove it from the tab order */
+ }
+ }
+ } /* end help */
+}
diff --git a/fec/gulpfile.js b/fec/gulpfile.js
index 8d008edf62..b9d646ae1e 100644
--- a/fec/gulpfile.js
+++ b/fec/gulpfile.js
@@ -62,7 +62,7 @@ gulp.task('purgecss', () => {
'./home/templates/purgecss-homepage/navs.html',
'./home/templates/purgecss-homepage/banners.html',
'./home/templates/purgecss-homepage/hero.html',
- './home/templates/purgecss-homepage/comissioners.html',
+ './home/templates/purgecss-homepage/commissioners.html',
'./home/templates/purgecss-homepage/toggled.html',
'./home/templates/purgecss-homepage/full.html'
]
diff --git a/fec/webpack.config.cjs b/fec/webpack.config.cjs
index 93f4e1c356..f5fecaac51 100644
--- a/fec/webpack.config.cjs
+++ b/fec/webpack.config.cjs
@@ -72,11 +72,6 @@ const mainEntries = {
filename: 'legal-search-ao-[contenthash].js',
dependOn: 'data-init'
},
- // 'legal-app': {
- // import: `${js}/legal/LegalApp.js`,
- // dependOn: 'global'
- // },
- // 'calc-admin-fines-modal': `${js}/modules/calc-admin-fines-modal.js`, // Pulled into init.js
'calc-admin-fines': `${js}/modules/calc-admin-fines.js`,
'fec-timeline': `${js}/modules/fec-timeline.js`,
'widgets/aggregate-totals-box': {
@@ -89,6 +84,10 @@ const mainEntries = {
filename: 'widgets/contributions-by-state-box.js',
dependOn: 'bythenumbers'
},
+ 'widgets/fecfile-eligibility': {
+ import: `${js}/widgets/fecfile-eligibility.js`,
+ filename: 'widgets/fecfile-eligibility.js'
+ },
'widgets/pres-finance-map-box': {
import: `${js}/widgets/pres-finance-map-box.js`,
filename: 'widgets/pres-finance-map-box.js',
@@ -203,7 +202,10 @@ module.exports = [
},
plugins: [
new webpack.DefinePlugin({
- context: {}
+ context: {},
+ __VUE_OPTIONS_API__: true,
+ __VUE_PROD_DEVTOOLS__: mode == 'production' ? false : true,
+ __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: mode == 'production' ? false : true
}),
new webpack.ProvidePlugin({
$: 'jquery',
diff --git a/package-lock.json b/package-lock.json
index b5a511cfd7..d0e374e3b2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -114,7 +114,8 @@
"topojson-client": "^3.1.0",
"underscore": "^1.13.6",
"urijs": "^1.19.11",
- "vue": "^2.7.16",
+ "vue": "^3.5.13",
+ "vue-2": "npm:vue@^2.7.16",
"webpack": "^5.97.1",
"webpack-bundle-analyzer": "^4.10.2",
"webpack-cli": "^6.0.1",
@@ -521,9 +522,9 @@
}
},
"node_modules/@babel/helper-string-parser": {
- "version": "7.24.8",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz",
- "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==",
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
+ "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -531,9 +532,9 @@
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz",
- "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==",
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
+ "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -597,11 +598,14 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.24.8",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.8.tgz",
- "integrity": "sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==",
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz",
+ "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.27.0"
+ },
"bin": {
"parser": "bin/babel-parser.js"
},
@@ -2057,15 +2061,14 @@
}
},
"node_modules/@babel/types": {
- "version": "7.24.9",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.9.tgz",
- "integrity": "sha512-xm8XrMKz0IlUdocVbYJe0Z9xEgidU7msskG8BbhnTPK/HZ2z/7FP7ykqPgrUH+C+r414mNfNWam1f2vqOjqjYQ==",
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
+ "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-string-parser": "^7.24.8",
- "@babel/helper-validator-identifier": "^7.24.7",
- "to-fast-properties": "^2.0.0"
+ "@babel/helper-string-parser": "^7.25.9",
+ "@babel/helper-validator-identifier": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
@@ -3186,47 +3189,115 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/@vue/compiler-core": {
+ "version": "3.5.13",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz",
+ "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.25.3",
+ "@vue/shared": "3.5.13",
+ "entities": "^4.5.0",
+ "estree-walker": "^2.0.2",
+ "source-map-js": "^1.2.0"
+ }
+ },
+ "node_modules/@vue/compiler-dom": {
+ "version": "3.5.13",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz",
+ "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-core": "3.5.13",
+ "@vue/shared": "3.5.13"
+ }
+ },
"node_modules/@vue/compiler-sfc": {
- "version": "2.7.16",
- "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz",
- "integrity": "sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==",
+ "version": "3.5.13",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz",
+ "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@babel/parser": "^7.23.5",
- "postcss": "^8.4.14",
- "source-map": "^0.6.1"
- },
- "optionalDependencies": {
- "prettier": "^1.18.2 || ^2.0.0"
+ "@babel/parser": "^7.25.3",
+ "@vue/compiler-core": "3.5.13",
+ "@vue/compiler-dom": "3.5.13",
+ "@vue/compiler-ssr": "3.5.13",
+ "@vue/shared": "3.5.13",
+ "estree-walker": "^2.0.2",
+ "magic-string": "^0.30.11",
+ "postcss": "^8.4.48",
+ "source-map-js": "^1.2.0"
}
},
- "node_modules/@vue/compiler-sfc/node_modules/prettier": {
- "version": "2.8.8",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
- "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
+ "node_modules/@vue/compiler-ssr": {
+ "version": "3.5.13",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz",
+ "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==",
"dev": true,
"license": "MIT",
- "optional": true,
- "bin": {
- "prettier": "bin-prettier.js"
- },
- "engines": {
- "node": ">=10.13.0"
- },
- "funding": {
- "url": "https://github.com/prettier/prettier?sponsor=1"
+ "dependencies": {
+ "@vue/compiler-dom": "3.5.13",
+ "@vue/shared": "3.5.13"
}
},
- "node_modules/@vue/compiler-sfc/node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "node_modules/@vue/reactivity": {
+ "version": "3.5.13",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz",
+ "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==",
"dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.10.0"
+ "license": "MIT",
+ "dependencies": {
+ "@vue/shared": "3.5.13"
}
},
+ "node_modules/@vue/runtime-core": {
+ "version": "3.5.13",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz",
+ "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vue/reactivity": "3.5.13",
+ "@vue/shared": "3.5.13"
+ }
+ },
+ "node_modules/@vue/runtime-dom": {
+ "version": "3.5.13",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz",
+ "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vue/reactivity": "3.5.13",
+ "@vue/runtime-core": "3.5.13",
+ "@vue/shared": "3.5.13",
+ "csstype": "^3.1.3"
+ }
+ },
+ "node_modules/@vue/server-renderer": {
+ "version": "3.5.13",
+ "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz",
+ "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-ssr": "3.5.13",
+ "@vue/shared": "3.5.13"
+ },
+ "peerDependencies": {
+ "vue": "3.5.13"
+ }
+ },
+ "node_modules/@vue/shared": {
+ "version": "3.5.13",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz",
+ "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@webassemblyjs/ast": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
@@ -10537,6 +10608,13 @@
"node": ">=4.0"
}
},
+ "node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
@@ -16620,6 +16698,16 @@
"es5-ext": "~0.10.2"
}
},
+ "node_modules/magic-string": {
+ "version": "0.30.17",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
+ "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0"
+ }
+ },
"node_modules/make-dir": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
@@ -17765,9 +17853,9 @@
"optional": true
},
"node_modules/nanoid": {
- "version": "3.3.7",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
- "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true,
"funding": [
{
@@ -19479,9 +19567,9 @@
}
},
"node_modules/postcss": {
- "version": "8.4.40",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.40.tgz",
- "integrity": "sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==",
+ "version": "8.5.3",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
+ "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
"dev": true,
"funding": [
{
@@ -19499,9 +19587,9 @@
],
"license": "MIT",
"dependencies": {
- "nanoid": "^3.3.7",
- "picocolors": "^1.0.1",
- "source-map-js": "^1.2.0"
+ "nanoid": "^3.3.8",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
@@ -22069,9 +22157,9 @@
}
},
"node_modules/source-map-js": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
- "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
@@ -23313,16 +23401,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/to-fast-properties": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
- "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/to-object-path": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
@@ -24467,6 +24545,29 @@
}
},
"node_modules/vue": {
+ "version": "3.5.13",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz",
+ "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-dom": "3.5.13",
+ "@vue/compiler-sfc": "3.5.13",
+ "@vue/runtime-dom": "3.5.13",
+ "@vue/server-renderer": "3.5.13",
+ "@vue/shared": "3.5.13"
+ },
+ "peerDependencies": {
+ "typescript": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vue-2": {
+ "name": "vue",
"version": "2.7.16",
"resolved": "https://registry.npmjs.org/vue/-/vue-2.7.16.tgz",
"integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==",
@@ -24478,6 +24579,47 @@
"csstype": "^3.1.0"
}
},
+ "node_modules/vue-2/node_modules/@vue/compiler-sfc": {
+ "version": "2.7.16",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz",
+ "integrity": "sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.23.5",
+ "postcss": "^8.4.14",
+ "source-map": "^0.6.1"
+ },
+ "optionalDependencies": {
+ "prettier": "^1.18.2 || ^2.0.0"
+ }
+ },
+ "node_modules/vue-2/node_modules/prettier": {
+ "version": "2.8.8",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
+ "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "bin": {
+ "prettier": "bin-prettier.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/vue-2/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/walker": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
diff --git a/package.json b/package.json
index b60e46f8d0..180548730d 100644
--- a/package.json
+++ b/package.json
@@ -131,7 +131,8 @@
"topojson-client": "^3.1.0",
"underscore": "^1.13.6",
"urijs": "^1.19.11",
- "vue": "^2.7.16",
+ "vue": "^3.5.13",
+ "vue-2": "npm:vue@^2.7.16",
"webpack": "^5.97.1",
"webpack-bundle-analyzer": "^4.10.2",
"webpack-cli": "^6.0.1",