diff --git a/.eslintrc.json b/.eslintrc.json index 1f13912..32330f8 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -4,7 +4,7 @@ "browser": true }, "globals": { - "sclivetickerAjax": "readonly", + "scliveticker": "readonly", "wp": "readonly" }, "extends": [ diff --git a/includes/class-scliveticker.php b/includes/class-scliveticker.php index 109e4e7..7ef9e11 100644 --- a/includes/class-scliveticker.php +++ b/includes/class-scliveticker.php @@ -205,16 +205,6 @@ class SCLiveticker { } else { $show_feed = 1 === self::$options['show_feed']; } - - $output = '
'; + $output = '
'; // Show RSS feed link, if configured. if ( $show_feed ) { @@ -262,7 +264,7 @@ class SCLiveticker { if ( self::$shortcode_present || self::$widget_present || self::block_present() ) { wp_enqueue_script( 'scliveticker-js', - SCLIVETICKER_BASE . 'scripts/liveticker.min.js', + SCLIVETICKER_BASE . 'scripts/liveticker.js', array(), self::VERSION, true @@ -271,10 +273,11 @@ class SCLiveticker { // Add endpoint to script. wp_localize_script( 'scliveticker-js', - 'sclivetickerAjax', + 'scliveticker', array( 'ajax_url' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'scliveticker_update-ticks' ), + 'api' => rest_url(), 'poll_interval' => self::$options['poll_interval'] * 1000, ) ); @@ -435,9 +438,9 @@ class SCLiveticker { */ private static function tick_html( $time, $title, $content, $is_widget = false ) { return '
  • ' - . '

    ' . esc_html( $time ) . '' - . '' . esc_html( $title ) . '

    ' - . '

    ' . $content . '

  • '; + . '' . esc_html( $time ) . '' + . '' . esc_html( $title ) . '' + . '
    ' . $content . '
    '; } /** diff --git a/scripts/liveticker.js b/scripts/liveticker.js index 9f2eebe..629873b 100644 --- a/scripts/liveticker.js +++ b/scripts/liveticker.js @@ -4,11 +4,9 @@ * @class */ ( function() { - var ajaxURL = sclivetickerAjax.ajax_url; - var nonce = sclivetickerAjax.nonce; - var pollInterval = sclivetickerAjax.poll_interval; + var apiURL; + var pollInterval; var ticker; - var widgets; /** * Initialize iveticker JS component. @@ -19,67 +17,42 @@ var updateNow = false; // Opt out if AJAX pobject not present. - if ( 'undefined' === typeof sclivetickerAjax ) { + if ( 'undefined' === typeof scliveticker ) { return; } - // Extract AJAX settings. - ajaxURL = sclivetickerAjax.ajax_url; - nonce = sclivetickerAjax.nonce; - pollInterval = sclivetickerAjax.poll_interval; + // Extract settings. + apiURL = scliveticker.api + 'wp/v2/scliveticker_tick'; + pollInterval = scliveticker.poll_interval; // Get ticker elements. ticker = [].map.call( document.querySelectorAll( 'div.wp-block-scliveticker-ticker.sclt-ajax' ), function( elem ) { - var list = elem.querySelector( 'ul' ); - var last = Number( elem.getAttribute( 'data-sclt-last' ) ); - - if ( ! list ) { - list = document.createElement( 'ul' ); - elem.appendChild( list ); - } - - if ( 0 === last ) { + elem = parseElement( elem, false ); + if ( '0' === elem.lastPoll ) { updateNow = true; } - - return { - s: elem.getAttribute( 'data-sclt-ticker' ), - l: elem.getAttribute( 'data-sclt-limit' ), - t: last, - e: list, - }; + return elem; } ); // Get widget elements. - widgets = [].map.call( - document.querySelectorAll( 'div.wp-widget-scliveticker-ticker.sclt-ajax' ), - function( elem ) { - var list = elem.querySelector( 'ul' ); - var last = Number( elem.getAttribute( 'data-sclt-last' ) ); - - if ( ! list ) { - list = document.createElement( 'ul' ); - elem.appendChild( list ); + ticker.concat( + [].map.call( + document.querySelectorAll( 'div.wp-widget-scliveticker-ticker.sclt-ajax' ), + function( elem ) { + elem = parseElement( elem, true ); + if ( 0 === elem.lastPoll ) { + updateNow = true; + } + return elem; } - - if ( 0 === last ) { - updateNow = true; - } - - return { - w: elem.getAttribute( 'data-sclt-ticker' ), - l: elem.getAttribute( 'data-sclt-limit' ), - t: last, - e: list, - }; - } + ) ); // Trigger update, if necessary. - if ( ( 0 < ticker.length || widgets.length ) && 0 < pollInterval ) { + if ( ( 0 < ticker.length ) && 0 < pollInterval ) { if ( updateNow ) { update(); } else { @@ -89,97 +62,108 @@ }; /** - * Update liveticker on current page via AJAX call. + * Parse an HTML element containing a liveticker. + * + * @param {HTMLElement} elem The element. + * @param {boolean} widget Is the element a widget? + * @return {{ticker: string, lastPoll: number, ticks: any, limit: string, isWidget: *}} Ticker descriptor object. + */ + var parseElement = function( elem, widget ) { + var list = elem.querySelector( 'ul' ); + var last = elem.getAttribute( 'data-sclt-last' ); + + if ( ! list ) { + list = document.createElement( 'ul' ); + elem.appendChild( list ); + } + + return { + ticker: elem.getAttribute( 'data-sclt-ticker' ), + limit: elem.getAttribute( 'data-sclt-limit' ), + lastPoll: last, + ticks: list, + isWidget: widget, + }; + }; + + /** + * Update liveticker on current page via REST API call. * * @return {void} */ var update = function() { - // Extract ticker-slug, limit and timestamp of last poll. - var updateReq = 'action=sclt_update-ticks&_ajax_nonce=' + nonce; - var i, j; - var xhr = new XMLHttpRequest(); - - for ( i = 0; i < ticker.length; i++ ) { - updateReq = updateReq + - '&update[' + i + '][s]=' + ticker[ i ].s + - '&update[' + i + '][l]=' + ticker[ i ].l + - '&update[' + i + '][t]=' + ticker[ i ].t; - } - for ( j = 0; j < widgets.length; j++ ) { - updateReq = updateReq + - '&update[' + ( i + j ) + '][w]=' + widgets[ j ].w + - '&update[' + ( i + j ) + '][l]=' + widgets[ j ].l + - '&update[' + ( i + j ) + '][t]=' + widgets[ j ].t; - } - - // Issue AJAX request. - xhr.open( 'POST', ajaxURL, true ); - xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded;' ); - xhr.onreadystatechange = function() { - var updateResp; - if ( XMLHttpRequest.DONE === this.readyState && 200 === this.status ) { - try { - updateResp = JSON.parse( this.responseText ); - if ( updateResp ) { - updateResp.forEach( - function( u ) { - ticker.forEach( - function( t ) { - if ( t.s === u.s ) { - t.t = u.t; // Update last poll timestamp. - updateHTML( t, u ); // Update HTML markup. - } - } - ); - widgets.forEach( - function( t ) { - if ( t.w === u.w ) { - t.t = u.t; - updateHTML( t, u ); - } + // Iterate over available tickers. + ticker.forEach( + function( t ) { + var xhr = new XMLHttpRequest(); + var query = '?ticker=' + encodeURI( t.ticker ) + + '&limit=' + encodeURI( t.limit ) + + '&last=' + encodeURI( t.lastPoll ); + xhr.open( 'GET', apiURL + query, true ); + xhr.addEventListener( + 'load', + function() { + var updateResp; + try { + updateResp = JSON.parse( this.responseText ); + if ( updateResp ) { + updateResp.reverse(); + updateResp.forEach( + function( u ) { + addTick( t, u ); } ); } - ); + setTimeout( update, pollInterval ); // Re-trigger update. + } catch ( e ) { + // eslint-disable-next-line no-console + console.warn( 'Liveticker AJAX update failed, stopping automatic updates.' ); + } } - setTimeout( update, pollInterval ); // Re-trigger update. - } catch ( e ) { - // eslint-disable-next-line no-console - console.warn( 'Liveticker AJAX update failed, stopping automatic updates.' ); - } + ); + xhr.send(); } - }; - xhr.send( updateReq ); + ); }; /** * Do actual update of HTML code. * - * @param {Object} t Ticker or Widget reference. - * @param {number} t.l Limit of entries to display. - * @param {HTMLElement} t.e HTML element of the ticker/widget. - * @param {Object} u Update entity. - * @param {string} u.h HTML code to append. - * @param {number} u.t Timetsamp of last update. + * @param {Object} t Ticker or Widget reference. + * @param {Object} u Update entity. * @return {void} */ - var updateHTML = function( t, u ) { + var addTick = function( t, u ) { // Parse new DOM-part. - var n = document.createElement( 'ul' ); - n.innerHTML = u.h; + var li = document.createElement( 'li' ); + var time = document.createElement( 'span' ); + var title = document.createElement( 'span' ); + var content = document.createElement( 'div' ); + var cls = t.isWidget ? 'sclt-widget' : 'sclt-tick'; - // Prepend new ticks to container. - while ( n.hasChildNodes() ) { - t.e.prepend( n.lastChild ); - } + li.classList.add( cls ); + time.classList.add( cls + '-time' ); + time.innerText = u.modified_rendered; + title.classList.add( cls + '-title' ); + title.innerText = u.title.rendered; + content.classList.add( cls + '-content' ); + content.innerHTML = u.content.rendered; + li.appendChild( time ); + li.appendChild( title ); + li.appendChild( content ); - t.e.parentNode.setAttribute( 'data-sclt-last', u.t ); + // Prepend new tick to container. + t.ticks.prepend( li ); + + // Update last poll time. + t.lastPoll = u.date_gmt; + t.ticks.parentNode.setAttribute( 'data-sclt-last', u.date_gmt ); // Remove tail, if limit is set. - if ( 0 < t.l ) { - [].slice.call( t.e.getElementsByTagName( 'li' ), t.l ).forEach( - function( li ) { - li.remove(); + if ( 0 < t.limit ) { + [].slice.call( t.ticks.getElementsByTagName( 'li' ), t.limit ).forEach( + function( l ) { + l.remove(); } ); }