: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
FlowFlow.checkScrollbar();
FlowFlow.$html.addClass( 'popup_visible' );
$popup.on( 'click', this.popupClick );
var notice = '<div class="ff-notice">Access token is required for %network% feeds, please add on <a href="#ff-auth-tab" class="ff-pseudo-link">AUTH tab</a></div>';
if ( ! $popup.find('.ff-notice').length ) {
if ( ! $( '#facebook_access_token' ).val() ) {
$view.addClass( 'auth-required' ).find( 'h1' ).after( notice.replace( '%network%', 'Facebook' ) )
if ( ! $( '#oauth_access_token' ).val() ) {
$view.addClass( 'auth-required' ).find( 'h1' ).after( notice.replace( '%network%', 'Twitter' ) )
if ( ! $( '#google_api_key' ).val() ) {
$view.addClass( 'auth-required' ).find( 'h1' ).after( notice.replace( '%network%', 'Youtube' ) )
if ( ! $( '#facebook_access_token' ).val() ) {
$view.addClass( 'auth-required' ).find( '> h1' ).after( '<div class="ff-notice">If you plan to use official Instagram API by Facebook, access token is required, please add on <a href="#ff-auth-tab" class="ff-pseudo-link">AUTH tab</a></div>' )
// store all input values to revert them on popup closing without save
var initialSettings = {};
$view.find( 'input, select' ).each( function ( i, el ) {
if ( $el.is(':radio') ) {
if ( $el.is(':checked') ) {
initialSettings[ this.name ] = $el.val();
} else if ( $el.is(':checkbox')) {
if ( $el.is(':checked') ) {
initialSettings[ this.name ] = 'yep';
initialSettings[ this.name ] = 'nope';
initialSettings[ this.name ] = $.trim( $el.val() );
$view.data( 'initialSettings', initialSettings );
popupClick: function (e) {
if ($t.is('.popup' )) $('.active .popup .popupclose').click();
if ($t.is('.ff-pseudo-link' )) {
$('.active .popup .popupclose').click();
$('#ff-auth-tab').click();
filterFeed: function (e) {
var uid = $t.closest('[data-uid]').attr('data-uid');
var $popup = this.$popup;
$popup.find('.feed-view-visible').removeClass('feed-view-visible');
var feed = this.model.get('feeds') ? this.model.get('feeds')[uid] : null;
var network = feed && feed.type;
var $view = $popup.find('.feed-view[data-uid=' + uid + ']');
var $filterView = $popup.find('.feed-view[data-filter-uid=' + uid + ']');
if ( ! $view.length && ! $filterView.length ) {
$view = $( _.template(templates[network + 'View'])({
$filterView = $( _.template(templates['filterView'])({
this.$el.find( '#feed-views' ).append( $view );
this.$el.find( '#filter-views' ).append( $filterView );
$popup.find('.feed-view[data-filter-uid=' + uid + ']').addClass('feed-view-visible');
$popup.addClass('add-feed-step-2');
$popup.find('.feed-popup-controls').hide();
$popup.find('.feed-popup-controls.edit').show();
FlowFlow.checkScrollbar();
FlowFlow.$html.addClass('popup_visible');
$popup.on('click', this.popupClick);
resetFeedCache: function (e) {
var uid = $t.closest('[data-uid]').attr('data-uid');
var changed = this.model.get('feeds_changed');
var feeds = this.model.get('feeds');
changed[ uid ] = _.clone( feeds[ uid ] );
changed[ uid ][ 'state' ] = 'reset_cache';
checkStreams: function (e) {
var streams = streamRowModels.models;
var id = $t.closest('[data-uid]').attr('data-uid');
for ( var i = 0, len = streams.length; i < len; i++ ) {
streamFeeds = streams[i].get( 'feeds' );
found = _.find( streamFeeds, function (feed) {
str += streams[i].get( 'name' ) + ' #' + streams[i].id
if ( ! str ) str = 'not yet connected to any streams';
FlowFlow.popup( 'Feed is connected to:<br>' + str , 'neutral', 'alert');
toggleDropDown: function (e) {
var $cont = $t.closest('td');
var isOpened = $cont.data('popup') === 'opened';
$('td.open').removeClass('open').data('popup', '');
$cont.removeClass('open');
//FlowFlow.$body.off('click', this.popupMoreClick);
$cont.data('popup', 'opened');
//FlowFlow.$body.on('click', self.popupMoreClick);
popupMoreClick: function (e) {
if (!$t.closest('.feed-dropdown-menu').length) {
$('td.open').removeClass('open').data('popup', '');
FlowFlow.$body.off('click', this.popupMoreClick);
popupLeave: function () {
$('td.open').removeClass('open').data('popup', '');
deleteFeed: function (e) {
var promise = FlowFlow.popup('Do you want to permanently delete this feed?');
var $t = $( e.currentTarget );
promise.then(function success(){
var uid = $t.closest('[data-uid]').attr('data-uid');
var modelFeeds = self.model.get('feeds');
var changed = self.model.get('feeds_changed');
changed[ uid ] = _.clone( modelFeeds[ uid ] );
changed[ uid ][ 'state' ] = 'deleted';
if ( changed[ uid ][ 'boosted' ] === 'yep' ) {
FlowFlow.availableBoosts++;
FlowFlow.$boostSmartElement.html( FlowFlow.availableBoosts + ' boost' + ( FlowFlow.availableBoosts != 1 ? 's' : '' ) );
if (self.paginator._items.length - 1 < self.paginator.options.perPage * self.paginator._currentPageNum - (self.paginator.options.perPage - 1) ) {
self.currentPage = self.paginator._currentPageNum - 1 || 1;
self.currentPage = self.paginator._currentPageNum;
closeFeedPopup: function (e) {
FlowFlow.$html.removeClass('popup_visible');
FlowFlow.resetScrollbar();
var $popup = this.$popup;
var feeds = this.model.get('feeds');
var changed = this.model.get('feeds_changed');
var $view = $popup.find('.feed-view-visible');
$popup.off('click', this.popupClick);
var $fresh = $popup.find('.feed-view-dynamic');
if ( $fresh.length && this.feedChanged ) {
if (this.model.get('feeds').length) this.model.get('feeds').pop();
$popup.removeClass('add-feed-step-1 add-feed-step-2');
this.feedChanged = false;
// restore settings and revert model
var initialSettings = $view.data( 'initialSettings' ) || {};
$view.find( 'input, select' ).each( function ( i, el ) {
if ( $el.is( ':radio') ) {
if ( initialSettings[ this.name ] == $el.val() ) {
$el.prop( 'checked', true ).change();
} else if ( $el.is(':checkbox') ) {
if ( initialSettings[ this.name ] == 'yep' ) {
$el.prop( 'checked', true ).change();
$el.prop( 'checked', false ).change();
$el.val( initialSettings[ this.name ] ).change();
$view.data( 'initialSettings', '' );
// delete from changed obj
delete changed[ $view.data('uid') ];
showErrorIfPresent: function (e) {
var $error = $(e.currentTarget).find('.cache-status-error'), errorStr = '';
if (!$error.length) return;
var errorData = $error.data('err');
if (errorData && errorData[0]) {
errorData = errorData[0];
var messages = errorData['message'];
if ($.isArray(messages)) {
for (var i = 0, len = messages.length; i < len;i++) {
if (i > 0) errorStr += '<br>';
errorStr += messages[i]['msg'];
} else if (typeof messages === 'object') {
errorStr += messages['msg'];
errorStr += JSON.stringify(messages)
errorStr += 'Unknown error. Please ask for support <a href="https://social-streams.com/contact/">here</a>'
} else if (typeof messages === 'string') {
} else if (errorData['msg']) {
errorStr += errorData['msg'];
} else if ($.isArray(errorData)) {
errorStr += errorData[0].msg;
errorStr += JSON.stringify(errorData[0])
errorStr += 'Unknown error. Please <a href="https://social-streams.com/contact/">submit ticket</a> and send access'
errorStr += JSON.stringify(errorData)
errorStr += 'Unknown error. Please <a href="https://social-streams.com/contact/">submit ticket</a> and send access'
var offset = $error.offset();
FlowFlow.$errorPopup.addClass('visible').css({top: offset.top - 20, left: offset.left + 30});
if (errorData.type === 'facebook' && errorStr.indexOf('Application request limit') + 1) {
errorStr += '. Check <a href="http://docs.social-streams.com/article/133-facebook-app-request-limit-reached" target="_blank">more info</a>'
else if (errorStr.toLowerCase().indexOf('bad request') + 1) {
errorStr += '<br><br>Check <a href="https://docs.social-streams.com/article/55-400-bad-request" target="_blank"> info</a>'
FlowFlow.$errorPopup.html('<h4>App received next error message for this feed:</h4><p>' + errorStr + '</p>')
goBackToFeedChoice: function (e) {
var $popup = this.$popup;
var feeds = this.model.get('feeds');
var $view = $popup.find('.feed-view-dynamic')
var uid = $view.data('uid')
$popup.removeClass('add-feed-step-2').addClass('add-feed-step-1');
$popup.find('.feed-view-dynamic').remove();
this.feedChanged = false;
createFeedView: function (e) {
var $t = $( e.currentTarget );
var $popup = this.$popup;
var network = $t.data('network') || 'instagram'; // if not defined then it's grace
var fid = FlowFlow.getRandomId();
var compiled = _.template(templates[network + 'View'])({uid: fid});
var filterViewCompiled = _.template(templates['filterView'])({uid: fid});
var $view = $( compiled );
var $filterView = $( filterViewCompiled );
if ( vars.m == 'l' && network.indexOf( 'gram' ) == -1 && network.indexOf( 'book' ) == -1 && network.indexOf( 'erest' ) == -1 && network.indexOf( 'twi' ) == -1 ) return;
if ( plugin == 'insta_flow' ) {
$popup.find('.feed-view-visible').removeClass('feed-view-visible');
$view.addClass('feed-view-visible').add($filterView).addClass('feed-view-dynamic').data('fid', fid);
$popup.removeClass('add-feed-step-1').addClass('add-feed-step-2');
$popup.find('.feed-popup-controls').hide();
$popup.find('.networks-content .feed-popup-controls.add').show();
$popup.find('#feed-views').prepend($view);
$popup.find('#filter-views').prepend($filterView);
// focus on content field
$view.find( '[name=' + fid + '-content]' ).focus();
var notice = '<div class="ff-notice">Access token is required for %network% feeds, please add on <a href="#ff-auth-tab" class="ff-pseudo-link">AUTH tab</a></div>';
if ( ! $popup.find('.ff-notice').length ) {
if ( ! $( '#facebook_access_token' ).val() ) {
$view.addClass( 'auth-required' ).find( 'h1' ).after( notice.replace( '%network%', 'Facebook' ) )
if ( ! $( '#oauth_access_token' ).val() ) {
$view.addClass( 'auth-required' ).find( 'h1' ).after( notice.replace( '%network%', 'Twitter' ) )
if ( ! $( '#google_api_key' ).val() ) {
$view.addClass( 'auth-required' ).find( 'h1' ).after( notice.replace( '%network%', 'Youtube' ) )
if ( ! $( '#facebook_access_token' ).val() ) {
$view.addClass( 'auth-required' ).find( 'h1' ).after( '<div class="ff-notice">If you plan to use official Instagram API by Facebook, access token is required, please add on <a href="#ff-auth-tab" class="ff-pseudo-link">AUTH tab</a></div>' )
this.addFeedInModel($view, fid);
if ($view.attr('data-feed-type') === 'wordpress') {
$view.find('input:radio').first().change();
updateModel: function ( event, triggeredFromList ) {
var $t = $( event.currentTarget );
var name = $t.attr('name');
var $view = $t.closest('.feed-view');
var id = $view.data('uid') || $view.data('filter-uid');
var isFresh = $view.is('.feed-view-dynamic');
var modelFeeds = this.model.get('feeds');
var changed = this.model.get('feeds_changed');
var streams, feeds, found;
var cancelUpdatingModel = false;
var cancelBoostChange = false;
currProp = name.replace(id + '-', '');
if ( currProp == 'boosted' && ! triggeredFromList ) {
boostInputChecked = $t.is(':checked');
if ( ( boostInputChecked && modelFeeds[ id ][ 'boosted' ] == 'yep' ) || ( ! boostInputChecked && modelFeeds[ id ][ 'boosted' ] == 'nope' ) ) {
// do nothing, it means it was changed programmatically from yep to yep, or nope to nope
// check if boosts available, checked AND (not number OR less than 1)
if ( boostInputChecked && ( ! Number.isInteger( FlowFlow.availableBoosts ) || parseInt( FlowFlow.availableBoosts ) < 1 ) ) {
FlowFlow.popup( 'No boosts available, please remove from other feeds to free up boosts or upgrade your plan', 'neutral', 'alert' );
// revert if not available
$t.prop( 'checked', false );
cancelBoostChange = true;
if ( boostInputChecked ) { // set to enable boost on save
// boosts available UI decreased
if ( ! cancelBoostChange ) FlowFlow.availableBoosts--;
} else { // set to disable boost on save
streams = streamRowModels.models;
// alert if try to disable boost for feed in cloud stream
for ( var i = 0, len = streams.length; i < len; i++ ) {
feeds = streams[ i ].get( 'feeds' );
found = _.find( feeds, function ( feed ) {
if ( found ) { // this feed in
var alert = FlowFlow.popup( 'You are about to remove boost from feed that is connected to cloud stream ID #' + streams[ i ].get( 'id' ) + ( streams[ i ].get( 'name' ) ? ' "' + streams[ i ].get( 'name' ) + '"' : '' ) + '. Cloud streams can\'t contain self-hosted feeds. Please disconnect feed from stream first.', 'neutral', 'alert' );
$t.prop( 'checked', true );
cancelUpdatingModel = true;