: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
cancelBoostChange = true;
// because if cancelled, input is set to checked false
if ( ! cancelBoostChange ) FlowFlow.availableBoosts++;
FlowFlow.$boostSmartElement.html( FlowFlow.availableBoosts + ' boost' + ( FlowFlow.availableBoosts != 1 ? 's' : '' ) );
if ( ! cancelUpdatingModel ) applyChange ();
function applyChange () {
changed[ id ] = _.clone( modelFeeds[ id ] );
changed[ id ][ 'state' ] = isFresh ? 'created' : 'changed';
val = $t.is(':checked') ? ($t.attr('value') || 'yep') : 'nope'
if ($t.is(':checkbox')) {
$group = self.$el.find('[name="' + name + '"]');
$group.each(function () {
if (this.checked) val[cbVal] = 'yep';
val = $t.is(':checked') ? 'yep' : 'nope'
self.trigger('changeFeedInModel', {name: name.replace(id + '-', ''), val: val, id: id })
addFeedInModel: function ( $view, id, errors ) {
var modelFeeds = this.model.get('feeds'), freshFeeds;
$view.find(':input').each(function (i, el) {
var name = $t.attr('name');
name = name.replace(/\w\w\d+?\-/, '');
} else if ($t.is(':checkbox')) {
obj[name] = $.trim($t.val());
obj['type'] = $view.data('feed-type');
obj['include'] = $view.parent().next().find('input[name="include"]').val() || '';
obj['filter-by-words'] = $view.parent().next().find('input[name="filter-by-words"]').val() || '';
if (errors) obj['errors'] = errors;
this.model.set('feeds', freshFeeds);
validateFeedCfg: function ($t) {
var $cont = $t.closest('.networks-content').find('.feed-view-visible');
var $contentInput = $cont.find('input[name$=content]');
$cont.find(':input').removeClass('validation-error');
if ($contentInput.length && !$contentInput.val()) {
setTimeout(function(){$contentInput.addClass('validation-error');},0);
$('html, body').animate({
scrollTop: $contentInput.offset().top - 150
toggleErrorFeeds: function (e) {
var $list = this.$el.find('#feeds-list');
var $t = $(e.currentTarget);
var feeds = this.model.get('feeds');
var changed = this.model.get('feeds_changed');
var feedsArr = Object.keys( feeds );
if ( this.showErrorFeedsOnly ) {
$list.find( '.tr-error-empty' ).remove();
// $list.removeClass('show-only-errors');
this.showErrorFeedsOnly = false;
$t.html('Show only error feeds');
this.renderFeedsList( changed, this.showErrorFeedsOnly );
if (feedsArr.length > 8) {
this.$el.addClass('jp-visible');
// $list.addClass('show-only-errors');
if ( ! $list.has( '.feed-row:visible' ).length ) {
$list.append( '<tr class="tr-error tr-error-empty"><td colspan="6">Yay! No error feeds.</td></tr>' );
$t.html('Show all feeds');
this.showErrorFeedsOnly = true;
this.renderFeedsList( changed, this.showErrorFeedsOnly );
this.$el.removeClass('jp-visible');
toggleFilterInput: function ( e ) {
var $hidden = $('.search-container:hidden');
$hidden.show().find( 'input' ).focus()
$('.search-container').hide()
filterFeedsByName: function ( e ) {
var feeds = this.model.get('feeds');
var changed = this.model.get('feeds_changed');
if ( val && val.length > 2 ) {
// feedsArr = Object.keys( feeds );
this.renderFeedsList( changed, this.showErrorFeedsOnly, val );
this.$el.removeClass('jp-visible');
feedsArr = Object.keys( feeds );
this.renderFeedsList( changed, this.showErrorFeedsOnly );
if (feedsArr.length > 8) {
this.$el.addClass('jp-visible');
catchEnter: function( event ) {
var $t = $(event.target);
var $view = $t.closest('.feed-view');
if( event.keyCode == 13 && ! $view.is( '.filter-feed' ) ) {
$( event.target ).closest( '.networks-content' ).find( '.feed-popup-controls:visible .submit-button' ).click();
saveViaAjax: function ( e, dontSavePage ) {
if ( e ) e.stopImmediatePropagation();
var $t = $( e ? e.target : '' );
var isNew = model.isNew();
var feeds = model.get( 'feeds' );
var changed = model.get( 'feeds_changed' );
if ( $t.is( '[id^=feed-sbmt]' ) ) {
if ( !this.validateFeedCfg( $t ) ) return;
if ( !dontSavePage ) this.currentPage = this.paginator._currentPageNum;
uid = this.$el.find('.feed-view-visible').data( 'uid' );
// catching and broadcasting event from toggler in list
if ( e && uid && feeds[uid] && feeds[uid]['enabled'] === 'nope' ) {
$enabled = this.$popup.find('[data-uid="' + uid + '"] > input:hidden');
$enabled.val( 'yep' ).change();
var saveFeedsRequest = this.model.save( isNew );
FlowFlow.makeOverlayTo( 'hide' );
// we don't wait for response to render UI
var feeds = model.get( 'feeds' ); // because model save rewrites feeds property
var changed = model.get( 'feeds_changed' );
state = changed[ id ][ 'state' ];
if ( response.feeds && ! response.feeds[ id ] ) continue;
if ( state && state != 'deleted' ) {
if ( response.feeds[ id ][ 'status' ] == 2 ) {
if ( state === 'created' ) {
self.$el.find( '.feed-view-dynamic' ).removeClass( 'feed-view-dynamic' );
self.startUpdateCycle( id );
else if ( state === 'changed' ) {
self.startUpdateCycle( id );
else if ( state === 'reset_cache' ) {
self.startUpdateCycle( id );
else if ( state === 'deleted' ) { // just delete
// delete specific feed from changes hash
} else { // success 1 or error 0
// delete specific feed from changed hash because its status resolved
// update feed in model for rendering
feeds[ id ] = response.feeds[ id ];
if ( response.feeds[ id ] [ 'status' ] != 1 ) {
console.log( 'feed status saveFeedsRequest', response.feeds[ id ] [ 'status' ] )
FlowFlow.showNotification( ( response.feeds[ id ] [ 'status' ] == 1 ? 'Yay' : 'Something went wrong' ) + '! Feed "<span>' + ( feeds[ id ].type === 'wordpress' ? ( feeds[ id ]['category-name'] || feeds[ id ]['wordpress-type'] ).toUpperCase() : feeds[ id ][ 'content' ] ).toUpperCase() + '</span>" ' + ( response.feeds[ id ] [ 'status' ] == 1 ? 'was successfully' : 'was' ) + ' ' + ( state == 'deleted' ? 'deleted' : ( state == 'created' ? 'created' : 'updated' ) ) + ( response.feeds[ id ] [ 'status' ] == 1 ? '' : ' with errors' ) + '<i class="flaticon-' + ( response.feeds[ id ] [ 'status' ] == 1 ? feeds[ id ][ 'type' ].toLowerCase() : 'error' ) + '"></i>' );
FlowFlow.renderBoostsUI( self.model );
FlowFlow.showNotification( 'Feed "<span>' + ( changed[ id ].type === 'wordpress' ? ( changed[ id ]['category-name'] || changed[ id ]['wordpress-type'] ).toUpperCase() : ( changed[ id ][ 'content' ] || '' )).toUpperCase() + '</span>"' + ' was successfully deleted<i class="flaticon-' + ( changed[ id ] && changed[ id ][ 'type' ] ? changed[ id ][ 'type' ].toLowerCase() + '"' : '' ) + '"></i>' );
// delete specific feed from changed hash because feed itself was deleted
FlowFlow.showNotification( ( res && res.responseJSON && res.responseJSON.error ? res.responseJSON.error : 'Something went wrong. Please try again or ask support.<i class="flaticon-error">' ) );
state = changed[ id ][ 'state' ];
if ( state !== 'deleted' ) {
FlowFlow.showNotification( 'Getting data for "<span>' + ( feeds[ id ].type === 'wordpress' ? ( feeds[ id ]['category-name'] || feeds[ id ]['wordpress-type'] ) : feeds[ id ][ 'content' ] ).toUpperCase() + '</span>" in background, you\'ll be notified when resolved<i class="flaticon-' + feeds[ id ][ 'type' ].toLowerCase() + '"></i>' );
// expand/collapse section module
var sectionExpandCollapse = (function($) {
if (!window.Backbone) return {init: function(){}}
var storage = window.localStorage && JSON.parse(localStorage.getItem('ff_sections')) || {};
var SectionsModel = Backbone.Model.extend({
this.set('collapsed', storage[this.id]['collapsed']);
storage[this.id] = {collapsed: {}}
this.on('change', function(){
if (window.localStorage) {
storage[this.id]['collapsed'] = this.get('collapsed');
window.localStorage.setItem('ff_sections', JSON.stringify(storage))
var SectionsView = Backbone.View.extend({
this.$sections = this.$el.find('> .section');
// add class if collapsed
self.$sections.each(function(){
var index = self.$sections.index(this);
$t.append('<span class="section__toggle flaticon-arrow-down"></span>');
if (self.model.get('collapsed')[index]) $t.addClass('section--collapsed');
"click .section__toggle": "toggle"
var $section = $(e.target).closest('.section');
var isCollapsed = $section.is('.section--collapsed');
var index = this.$sections.index($section);
var collapsed = _.clone(this.model.get('collapsed'));
$section.removeClass('section--collapsed');
$section.addClass('section--collapsed');
this.model.set('collapsed', collapsed);
$(document).trigger('section-toggle', this.model.get('id'));
var settings = $.extend(globalDefaults, opts);
var model = new SectionsModel(settings);
var view = new SectionsView({model: model, el: settings.$element});
window.sectionExpandCollapse = sectionExpandCollapse;
var controller = FlowFlow.init.apply( FlowFlow, arguments);
self.init = function(){return self}
'collection' : streamRowModels,
'collection' : streamModels,
'collection' : feedsModel,
sectionExpandCollapse : sectionExpandCollapse,
jQuery(document).bind('html_ready', function(){
var app = FlowFlowApp.init();
// legacy, compatibility, todo change in add-on
window.confirmPopup = app.FlowFlow.popup;
function capitaliseFirstLetter (string)
return string ? string.charAt(0).toUpperCase() + string.slice(1) : '';
function stripslashes (str) {
str = str.replace(/\\'/g, '\'');
str = str.replace(/\\"/g, '"');
str = str.replace(/\\0/g, '\0');
str = str.replace(/\\\\/g, '\\');
function stripSlashesObjProps (obj) {
if (typeof obj[prop] === 'string') obj[prop] = stripslashes(obj[prop]);
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {