if (document.getElementById('factbase-content')) { Vue.createApp({ /** * Initializes the component before it is mounted. * Retrieves query parameters from the URL and sets them as component properties. * Calls the `search` method to perform a search based on the retrieved parameters. */ beforeMount() { let urlParams = new URLSearchParams(window.location.search); if (urlParams.has('q')) { this.params.q = urlParams.get('q'); } if (urlParams.has('media')) { this.params.media = urlParams.get('media'); } if (urlParams.has('topic')) { this.params.topic = urlParams.get('topic'); } if (urlParams.has('type')) { this.params.type = urlParams.get('type'); } if (urlParams.has('sort')) { this.params.sort = urlParams.get('sort'); } if (urlParams.has('f')) { this.params.f = urlParams.get('f'); } if (urlParams.has('location')) { this.params.location = urlParams.get('location'); } if (urlParams.has('place')) { this.params.place = urlParams.get('place'); } this.search(); }, /** * Updates the component when it is mounted. * * This function attaches an event listener to the modal element referenced by `this.$refs.modal`. * When the modal is about to be closed, it finds all the iframe elements with the id `videoPlayer` * in the document and pauses the Vimeo player associated with each iframe. * * @return {void} This function does not return anything. */ updated() { jQuery(this.$refs.modal).on(jQuery.modal.BEFORE_CLOSE, function (event, modal) { iframes = jQuery(document).find('iframe#videoPlayer'); iframes.each(function (video, item) { var player = new Vimeo.Player(item); player.pause() }); }); }, /** * Returns an object containing the initial data for the component. * * @return {Object} An object with the following properties: */ data() { return { window: window, params: this.getInitialParams(), person: window.person, transcript: window.transcript, transcript_counts: window.transcript_counts, dateRange: { startDate: null, endDate: null, }, modalOpened: null, isVideoPlayerVisible: [], loading: false, results: [], data: [], meta: [], selectedEventTypeIds: [], selectedLoctionIds: [], selectedMediaIds: [], selectedPlaceIds: [], transcriptMapping: { 'all': '', 'remarks': 'remarks', 'speeches': 'speech', 'press-conferences': 'press_conferences', 'vlogs': 'vlog', 'interviews': 'interview', 'op-ed': 'op_ed', 'op-eds': 'op_ed', 'videos': 'video' } } }, computed: { highlightedData() { if (!this.searchQuery) { return this.data.map(item => ({ ...item, highlightedText: item.text })); } const query = this.escapeRegExp(this.searchQuery); const regex = new RegExp(`(${query})`, 'gi'); return this.data.map(item => { const highlightedText = item.text.replace(regex, '$1'); return { ...item, highlightedText }; }); }, numberOfResults() { if(this.meta.records_matched) { return this.meta.records_matched + ' results'; } }, mediaTypes() { const mediaTypeMap = {}; // Step 1: Aggregate media types and count occurrences this.data.forEach(item => { const mediaType = item.media_type; if (!mediaTypeMap[mediaType]) { mediaTypeMap[mediaType] = { id: mediaType, name: mediaType, count: 0 }; } mediaTypeMap[mediaType].count++; }); // Step 2: Convert map to array and sort by name const mediaTypes = Object.values(mediaTypeMap) .sort((a, b) => a.name.localeCompare(b.name)); return mediaTypes; }, selectedMedia() { return this.mediaTypes.filter(media => this.selectedMediaIds.includes(media.id)); }, eventTypes() { const eventTypeMap = {}; // Step 1: Aggregate event types and count occurrences this.data.forEach(item => { const eventType = item.record_type; if (!eventType) { return; } if (!eventTypeMap[eventType]) { eventTypeMap[eventType] = { id: eventType, name: eventType, count: 0 }; } eventTypeMap[eventType].count++; }); // Step 2: Convert map to array and sort by name const eventTypes = Object.values(eventTypeMap) .sort((a, b) => a.name.localeCompare(b.name)); return eventTypes; }, selectedEventTypes() { return this.eventTypes.filter(eventType => this.selectedEventTypeIds.includes(eventType.id)); }, locationTypes() { const locationMap = {}; // Step 1: Aggregate locations and count entries this.data.forEach(item => { const { city, state, state_code, country } = item.location; const id = [city, state, state_code, country].filter(value => value !== null && value !== undefined).join(','); if (!locationMap[id]) { locationMap[id] = { id: id, name: [city, state_code, country].filter(value => value !== null && value !== undefined).join(', '), count: 0 }; } locationMap[id].count++; }); // Step 2: Convert map to array and sort by name const locationTypes = Object.values(locationMap) .sort((a, b) => a.name.localeCompare(b.name)); return locationTypes; }, selectedLoctions() { return this.locationTypes.filter(location => this.selectedLoctionIds.includes(location.id)); }, places() { const places = {}; this.data.forEach(item => { const id = item.place if (!places[id]) { places[id] = { id: id, name: id, count: 0 }; } places[id].count++; }); return Object.values(places).sort((a, b) => a.name.localeCompare(b.name)); }, selectedPlaces() { return this.places.filter(place => this.selectedPlaceIds.includes(place.id)); } }, /** * Initializes the component when it is created. * * @param {type} paramName - description of parameter * @return {type} description of return value */ created() { this.debouncedWatch = window.lodash.debounce((newValue, oldValue) => { this.search(); }, 500); }, /** * Cancels the debounced watch function before the component is unmounted. * * @return {void} */ beforeUnmount() { this.debouncedWatch.cancel(); }, /** * Initializes the component when it is mounted. * */ mounted() { this.getResults(); // const applyCallback = (start, end) => { // this.params.start_date = start.format('YYYY-MM-DD'); // this.params.end_date = end.format('YYYY-MM-DD'); // }; // jQuery(this.$refs.dateInput).daterangepicker({ // startDate: moment('1946-06-14T10:54:00-0400'), // endDate: moment(), // autoApply: true, // autoUpdateInput: true, // ranges: { // 'Today': [moment(), moment()], // 'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')], // 'Last 7 Days': [moment().subtract(6, 'days'), moment()], // 'Last 30 Days': [moment().subtract(29, 'days'), moment()], // 'This Month': [moment().startOf('month'), moment().endOf('month')], // 'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')], // 'Year-To-Date': [moment().startOf('year'), moment()], // 'Last Year': [moment().subtract(1, 'year').startOf('year'), moment().subtract(1, 'year').endOf('year')], // '2010s': [moment('2010-01-01'), moment()], // '2000s': [moment('2000-01-01'), moment('2010-01-01')], // '1990s': [moment('1990-01-01'), moment('2000-01-01')], // '1980s': [moment('1980-01-01'), moment('1990-01-01')], // 'All Time': [moment('1946-06-14T10:54:00-0400'), moment()] // } // }, applyCallback); }, watch: { // 'params.start_date': function (newValue, oldValue) { // this.search(); // }, // 'params.end_date': function (newValue, oldValue) { // this.search(); // }, 'params.type': function (newValue, oldValue) { this.setF(); this.search(); }, 'params.media': function (newValue, oldValue) { this.setF(); this.search(); }, 'params.sort': function (newValue, oldValue) { this.setF(); this.search(); }, selectedMediaIds(newVal, oldVal) { this.setF(); this.params.media = newVal.join(','); this.search(); }, 'selectedEventTypeIds': function (newValue, oldValue) { this.setF(); this.params.type = newValue.join(','); this.search(); }, 'selectedLoctionIds': function (newValue, oldValue) { this.setF(); this.params.location = newValue.join(','); this.search(); }, 'selectedPlaceIds': function (newValue, oldValue) { this.setF(); this.params.place = newValue.join(','); this.search(); }, 'params.q': { handler(newData, old) { this.debouncedWatch(...newData); }, deep: true } }, methods: { getInitialParams() { return { q: '', f: '', media: '', type: '', sort: 'desc', spage: 0, location: '', place: '', topic: '' }; }, sentiment(item) { var score; var label; if(item.document) { score = item.document.sentiment?.score; label = item.document.sentiment?.text; } else { score = item.sentiment.score; label = item.sentiment.text; } if (!score) { return `
${label}
`; } else { switch (true) { case (score >= 0.1): return `
${Math.floor(score * 100) / 100}
`; case (score <= -0.1): return `
${Math.floor(score * 100) / 100}
`; case (score < 0.1 && score > -0.1): return `
${Math.floor(score * 100) / 100}
`; default: return `

${Math.floor(score * 100) / 100}
${label}
${Math.floor(score * 100) / 100}
`; } } }, resetParams() { this.selectedMediaIds = []; this.selectedLoctionIds = []; this.selectedEventTypeIds = []; this.selectedPlaceIds = []; this.params = this.getInitialParams(); }, /** * Sets the value of the 'f' parameter in the 'params' object based on the value of the 'transcript' property. * If the 'transcript' property is truthy, the 'browse' property in the 'params' object is set to 1. * The 'f' parameter is then set to the corresponding value in the 'transcriptMapping' object, or an empty string if no match is found. * The function then generates a query string from the 'params' object and updates the URL with the new query string. * * @return {void} This function does not return a value. */ setF() { // if (this.transcript) { // this.params.browse = 1; // } this.params.f = this.transcriptMapping[this.transcript] || ''; const queryString = Object.entries(this.params) .filter(([key, value]) => value !== null && value !== '') .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`) .join('&'); history.replaceState(null, null, "?" + queryString); }, /** * Opens a modal with the specified record ID. * * @param {string} recordId - The ID of the record to open the modal for. * @return {void} This function does not return a value. */ openModal(recordId) { this.modalOpened = recordId; }, /** * Asynchronously retrieves results from the API based on the current parameters and person, * and updates the data array with the results. If there are no more results, the scroll * event listener is removed. If an error occurs, the results are set to an error message. * * @return {Promise} A promise that resolves when the function completes. */ async getResults() { const scrollHandler = async () => { this.setF(); let bottomOfWindow = (window.innerHeight + Math.round(window.scrollY)) >= document.body.offsetHeight if (bottomOfWindow && !this.loading) { this.loading = true this.params.spage++ let url = 'https://api.factsquared.com/json/factba.se-' + this.person + '-20240623.php?' + this.objectToQueryString(this.params) + '&page='+page; try { const res = await fetch(url, { method: "post", }) this.results = (await res.json()) if(this.results.data.length === 0) { this.loading = false window.removeEventListener('scroll', scrollHandler); return; // No results, so return from the function } this.meta = this.results.meta this.data = this.results.data this.data.forEach(item => { this.data.push({ video_id: item.video_id || (item.video && item.video.video_id), vimeo_start: item.vimeo_start || (item.video && item.video.vimeo_start), ...item }); }); this.loading = false } catch (error) { this.loading = false this.results = 'Error! Could not reach the API. ' + error } finally { this.loading = false } } } window.addEventListener('scroll', scrollHandler); }, /** * Toggles the visibility of the video player for the corresponding index. * * @param {Object} item - The item object. * @param {number} index - The index of the item. * @return {void} This function does not return anything. */ toggleVideo(item, index) { this.isVideoPlayerVisible[index] = !this.isVideoPlayerVisible[index]; this.$forceUpdate(); }, /** * Redirects the user to the specified URL. * * @param {string} url - The URL to redirect to. * @return {void} This function does not return anything. */ redirect(url) { window.location.href = url }, /** * Converts an object into a query string. * * @param {Object} params - The object to convert. * @return {string} The query string representation of the object. */ objectToQueryString(params) { const queryString = Object.entries(params) .filter(([key, value]) => value !== '') .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`) .join('&'); return queryString; }, /** * Asynchronously searches for data using the specified parameters and updates the component state with the results. * * @return {Promise} A Promise that resolves when the search is complete. */ async search() { this.loading = true // this.setF(); this.params.spage = 1 page = this.spage = 1 let url = 'https://api.factsquared.com/json/factba.se-' + this.person + '-20240623.php?' + this.objectToQueryString(this.params) + '&page='+page; try { const res = await fetch(url, { method: "post", }) this.results = (await res.json()) this.meta = this.results.meta this.data = this.results.data this.data.map((item, index) => ({ video_id: item.video_id || item.video.video_id, vimeo_start: item?.video?.vimeo_start, ...item })) this.loading = false } catch (error) { this.loading = false this.results = 'Error! Could not reach the API. ' + error } finally { this.loading = false } } }, template: `
Fact
ba
.
se

Joe Biden

Kamala Harris

Donald Trump

Filters
Event Type
Loading...
N/A
Media
Loading...
N/A
Document
{{ new Date(item.date).toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: '2-digit', year: 'numeric', timeZone: 'UTC' }) }}
No Results
` }).mount('#factbase-content'); } if (document.getElementById('factbase-twitter')) { Vue.createApp({ /** * Initializes the component before it is mounted. * Sets the filter property to the value of the global 'filter' variable. * Calls the 'search' method. */ beforeMount() { this.filter = window.filter; this.search(); }, /** * Returns an object containing the initial data for the component. * * @return {Object} An object with the following properties: * - params: An object with the following properties: * - q: A string representing the search query. * - sort: A string representing the sort order. * - spage: A number representing the current page. * - loading: A boolean value representing the loading state. * - results: An array representing the search results. * - filter: A string representing the filter. * - data: An array representing the component data. * - meta: An array representing the meta data. * - queryString: A string representing the query string. * - filtermap: An object mapping filter names to their corresponding values. */ data() { return { window: window, params: { q: '', sort: 'desc', spage: 0, }, loading: false, results: [], filter: '', data: [], meta: [], queryString: '', filtermap: { 'deleted-tweets': 'deleted', 'flagged-tweets': 'flagged', } } }, /** * Initializes the component when it is created. * * This function sets up a debounced watcher for the `search` method. * The `debouncedWatch` function is debounced using the `window.lodash.debounce` function, * which delays invoking the `search` method until after 500 milliseconds have elapsed * since the last time the debounced function was invoked. * * @return {void} */ created() { this.debouncedWatch = window.lodash.debounce((newValue, oldValue) => { this.search(); }, 500); }, /** * Cancels the debounced watch function before the component is unmounted. * * @return {void} */ beforeUnmount() { this.debouncedWatch.cancel(); }, /** * Initializes the component when it is mounted. * */ mounted() { this.getResults(); }, watch: { 'params.sort': function (newValue, oldValue) { this.search(); }, 'params.q': { handler(newData, old) { this.debouncedWatch(...newData); }, deep: true } }, methods: { /** * Sets the `modalOpened` property to the specified `recordId`. * * @param {string} recordId - The ID of the record to open the modal for. * @return {void} This function does not return a value. */ openModal(recordId) { this.modalOpened = recordId; }, /** * Initializes the scrollHandler function to handle scrolling events. * * @return {void} This function does not return a value. */ async getResults() { const scrollHandler = async () => { let bottomOfWindow = (window.innerHeight + Math.round(window.scrollY)) >= document.body.offsetHeight if (bottomOfWindow && !this.loading) { this.loading = true this.params.spage++ let url = 'https://rcapi.factba.se/json/factba.se-trump-twitter.php?' + this.objectToQueryString(this.params) + '&' + this.filtermap[this.filter] + '=true&page='+page; try { const res = await fetch(url, { method: "post", }) this.results = (await res.json()) if(this.results.data.length === 0) { window.removeEventListener('scroll', scrollHandler); return; // No results, so return from the function } this.results.data.forEach(item => { this.data.push({...item}); }); } catch (error) { this.loading = false this.results = 'Error! Could not reach the API. ' + error } finally { this.loading = false } } } window.addEventListener('scroll', scrollHandler); }, /** * Converts an object of parameters into a query string. * * @param {Object} params - The object containing the parameters. * @return {string} The query string representation of the parameters. */ objectToQueryString(params) { const queryString = Object.entries(params) .filter(([key, value]) => value !== '') .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`) .join('&'); return queryString; }, /** * Converts a date string into a formatted date string. * * @param {string} dateString - The input date string. * @return {string} The formatted date string. */ formatDate(dateString) { const date = new Date(dateString); const options = { year: '2-digit', month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true, timeZoneName: 'short' }; const formattedDate = date.toLocaleString('en-US', options); return formattedDate.replace(',', '').replace(/(?<=\d)(st|nd|rd|th)/, '$& '); }, /** * Asynchronously searches for data using the specified parameters and updates the component state with the results. * * @return {Promise} A Promise that resolves when the search is complete. */ async search() { this.loading = true this.params.spage = 1 let url = 'https://rcapi.factba.se/json/factba.se-trump-twitter.php?' + this.objectToQueryString(this.params) + '&' + this.filtermap[this.filter] + '=true&page='+page;; try { const res = await fetch(url, { method: "post", }) this.results = (await res.json()) this.meta = this.results.meta this.data = this.results.data this.data.map((item, index) => ({...item})); this.loading = false } catch (error) { this.results = 'Error! Could not reach the API. ' + error this.loading = false } finally { this.loading = false } } }, template: `
Fact
ba
.
se

Donald Trump: Twitter

Source: Twitter
{{ formatDate(item.date)}}

{{ item.record_title }}

Deleted
Twitter Flag
No Results
` }).mount('#factbase-twitter'); } jQuery(document).ready(function ($) { $('.toggle-whcd-player').on('click', function () { $('.whcd-player-wrapper').toggleClass('hidden'); const vimeo_player = new Vimeo.Player(transcriptwrapper); vimeo_player.pause(); }); const iframe = document.querySelector('.whcd-player-wrapper > iframe'); if (iframe) { const vimeo_player = new Vimeo.Player(iframe); $('.whcd-player').on('click', function () { var video_seconds = $(this).attr('data-seconds'); vimeo_player.pause(); vimeo_player.setCurrentTime(video_seconds); vimeo_player.play(); }); } $('.toggle-transcript-player').on('click', function () { $('.transcript-player-wrapper').toggleClass('hidden'); const vimeo_player = new Vimeo.Player(transcriptwrapper); vimeo_player.pause(); }); const transcriptwrapper = document.querySelector('.transcript-player-wrapper > iframe'); if (transcriptwrapper) { const vimeo_player = new Vimeo.Player(transcriptwrapper); $('.transcript-play-video').on('click', function () { if ($('.transcript-player-wrapper').hasClass('hidden')) { $('.transcript-player-wrapper').removeClass('hidden'); } var video_seconds = $(this).attr('data-seconds'); vimeo_player.pause(); vimeo_player.setCurrentTime(video_seconds); vimeo_player.play(); }); } if (window.latest_id) { jQuery('#' + window.latest_id).modal({ fadeDuration: 100, }); } window.initTooltip(); }); window.initTooltip = () => { jQuery('.tooltip').tooltipster({ theme: 'tooltipster-borderless', side: 'right', functionInit: function (instance, helper) { var $origin = jQuery(helper.origin), dataOptions = $origin.attr('data-tooltipster'); if (dataOptions) { dataOptions = JSON.parse(dataOptions); jQuery.each(dataOptions, function (name, option) { instance.option(name, option); }); } } }); };