(function ($, Config, undefined) { // The length of a day in seconds var DAY_IN_SECONDS = 24 * 60 * 60; // Update the table only when the tool is shown $(document).on('wpra/tools/on_loaded', function (event, tool) { if (tool === 'crons') { if (Store.isLoaded) { init(); } else { $(document).ready(init); } } $(document).on('wpra/tools/on_switched_to_crons', init); }); /** * Initializes the crons tool. */ function init() { Loading.init(); Pagination.init(); Table.init(); Timeline.init(); Info.init(); Store.init(); } /** * The loading component. */ var Loading = { element: null, wrapper: null, shown: false, progress: null, maxWidth: 100, init: function () { Loading.element = $('.wpra-crons-loading'); Loading.wrapper = $('.wpra-crons-wrap'); Loading.bar = Loading.element.find('.wpra-crons-loading-bar'); Loading.hide().update(); }, update: function () { Loading.element.toggle(Loading.shown); Loading.wrapper.toggle(!Loading.shown); Loading.bar.css({ width: (Loading.progress * Loading.maxWidth) + '%' }); }, setProgress: function (progress) { Loading.progress = progress; return Loading; }, show: function () { Loading.shown = true; return Loading; }, hide: function () { Loading.shown = false; Loading.progress = 0; return Loading; }, }; /** * The data store. */ var Store = { feeds: [], groups: {}, count: 0, isLoaded: false, page: 1, numPages: 1, init: function () { // Show the loading message with an empty progress bar Loading.setProgress(0).show().update(); var currPage = 1; var loadNextPage = function () { // Update the loading Loading.setProgress(currPage / Store.numPages).update(); // If reached the last page, hide the progress bar if (currPage >= Store.numPages) { // Generate the groups Store.groupFeeds(); // Update the components setTimeout(function () { Loading.hide().update(); Pagination.update(); Table.update(); Timeline.update(); Info.update(); }, 500); return; } // Increment the page currPage++; // Fetch the page Store.fetchSources(currPage, loadNextPage); }; // Load the first page Store.fetchSources(1, loadNextPage); }, update: function (delegateUpate) { // Re-group the feeds Store.groupFeeds(); // Update the table and timeline if (delegateUpate !== false) { Table.update(); Timeline.update(); } }, fetchSources: function (page, callback) { page = (page === null || page === undefined) ? Store.page : page; $.ajax({ url: Config.restUrl + 'wpra/v1/sources', method: 'GET', data: { num: Config.perPage, page: page }, beforeSend: function (request) { request.setRequestHeader("X-WP-NONCE", Config.restApiNonce); }, success: function (response) { if (response && response.items) { Store.count = response.count; Store.feeds = Store.feeds.concat(response.items); if (!Store.isLoaded) { Store.numPages = Math.ceil(Store.count / Store.feeds.length); } // Save the original cron information Store.feeds = Store.feeds.map(function (feed) { feed.original = { active: feed.active, update_time: feed.update_time, update_interval: feed.update_interval, }; return feed; }); // Sort the feeds Store.feeds = Store.feeds.sort(function (a, b) { return Time.compare(Feed.getUpdateTime(a), Feed.getUpdateTime(b)); }); Store.isLoaded = true; Store.update(); } if (callback) { callback(); } }, error: function (response) { console.error(response); }, }); }, getIntervalName: function (interval) { return Config.schedules[interval] ? Config.schedules[interval]['display'] : interval; }, groupFeeds: function () { Store.groups = {}; for (var i in Store.feeds) { var feed = Store.feeds[i]; var time = Feed.getUpdateTime(feed), timeStr = Time.format(time); if (!Store.groups[timeStr]) { Store.groups[timeStr] = []; } Store.groups[timeStr].push(feed); // Get the interval time in seconds var interval = Config.schedules[Feed.getUpdateInterval(feed)]['interval']; var intervalObj = Time.fromSeconds(interval); // Add recurrences for feeds that fetch more than once a day if (interval < DAY_IN_SECONDS) { var t = { hours: time.hours, minutes: time.minutes, }; var numRepeats = Math.floor(DAY_IN_SECONDS / interval) - 1; for (var i = 0; i < numRepeats; ++i) { // Add the interval to the temporary time t = Time.add(t, intervalObj); // Convert to seconds, clamp to 24 hours and convert back to an object // This lets us format it into a time string without having 24+ hours var t2 = Time.fromSeconds(Time.toSeconds(t) % DAY_IN_SECONDS); // Get the time string for the clamped time var str = Time.format(t2); // Add the recurrence to the groups if (!Store.groups[str]) { Store.groups[str] = []; } Store.groups[str].push(feed); } } } var collapsed = {}; for (var timeStr in Store.groups) { // Get the time object and string for the previous minute var group = Store.groups[timeStr], time = Time.parse(timeStr), prevTime = Time.add(time, {hours: 0, minutes: -1}), prevTimeStr = Time.format(prevTime); // The key to use - either this group's time string or a time string for 1 minute less var key = Store.groups.hasOwnProperty(prevTimeStr) ? prevTimeStr : timeStr; // Create the array for the key if needed if (!Array.isArray(collapsed[key])) { collapsed[key] = []; } // Add the group to the array for the key collapsed[key] = collapsed[key].concat(group); } Store.groups = Object.keys(collapsed).sort().reduce((acc, key) => (acc[key] = collapsed[key], acc), {}); }, }; /** * Functions related to feed sources and their data. */ var Feed = { getState: function (feed) { return feed.active ? 'active' : 'paused'; }, getUpdateInterval: function (feed) { return feed.update_interval; }, getUpdateTime: function (feed) { return (feed.update_time) ? Time.parse(feed.update_time) : Time.parse(Config.globalTime); }, }; /** * The feed sources table. */ var Table = { element: null, body: null, page: 1, numPerPage: Config.perPage, highlighted: null, init: function () { if (Table.element === null) { Table.element = $('#wpra-crons-tool-table'); Table.body = Table.element.find('tbody'); } }, createRow: function (feed) { var id = feed.id, state = Feed.getState(feed), name = feed.name, interval = Feed.getUpdateInterval(feed), timeStr = Time.format(Feed.getUpdateTime(feed)); var elRow = $('