: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
//console.profile("Processing page");
//console.time("Page loading");
'transition': 'transitionend',
'WebkitTransition': 'webkitTransitionEnd',
'MozTransition': 'transitionend',
'OTransition': 'otransitionend'
elem = document.createElement('div');
for(var t in transitions){
if(typeof elem.style[t] !== 'undefined'){
window.transitionEnd = transitions[t];
if (!window.transitionEnd) window.transitionEnd = false;
Number.isInteger = Number.isInteger || function(value) {
return typeof value === 'number' &&
Math.floor(value) === value;
jQuery.fn.textWidth = function(_text, _font){//get width of text with font. usage: $("div").textWidth();
var fakeEl = jQuery('<span>').hide().appendTo(document.body).text(_text || this.val() || this.text()).css('font', _font || this.css('font')),
jQuery.fn.autoresize = function(options){//resizes elements based on content size. usage: $('input').autoresize({padding:10,minWidth:0,maxWidth:100});
options = jQuery.extend({padding:10,minWidth:0,maxWidth:10000}, options||{});
$t.on('input', function() {
$t.css('width', Math.min(options.maxWidth,Math.max(options.minWidth,$t.textWidth() + options.padding)));
var FlowFlowApp = ( function($) {
// streams model, view and collection declaring
var StreamModelsCollection;
// rows model, view and collection declaring
var StreamRowModelsCollection;
var feedsModel, feedsView;
var templates = window.ff_templates;
var sessionStorage = window.sessionStorage || {getItem: function(){return false}, setItem: function(){}};
var transitionEnd = window.transitionEnd;
var alphabet = 'abcdefghijklmnopqrstuvwxyz';
var ua = navigator.userAgent.toLowerCase();
var isWebkit = /safari|chrome/.test(ua);
var isMobile = /android|blackBerry|iphone|ipad|ipod|opera mini|iemobile/i.test(ua);
var isIE = /msie|trident.*rv\:11\./.test(ua);
var isFF = /firefox/.test( ua );
// making life of hackers harder when minified
var vars = window [ window[ 'l' + alphabet[0] + '_plugin']['slug_' + 'down'] + '_' + 'v' + alphabet[0] + 'r' + 's' ];
var plugin = window[ 'l' + alphabet[0] + '_plugin']['slug_' + 'down'];
var la_plugin_slug_down = plugin;
if ( plugin === 'insta_flow' ) {
document.documentElement.style.setProperty('--main-ui-color', "#c13584");
document.documentElement.style.setProperty('--bg-ui-color', "#fffef5");
document.documentElement.style.setProperty('--link-ui-color', "#bb0069");
document.documentElement.style.setProperty('--main-ui-color-hover', "#e3027f");
savedView : sessionStorage.getItem( 'ff_stream' ) || 'list',
$streamsContainer : null,
$errorPopup : $('<div id="error-popup"></div>'),
activeTabIndex: parseInt( sessionStorage.getItem('as_active_tab') || 0 ),
renderBoostsUI: function ( model ) {
if ( plugin == 'insta_flow' ) return;
'action': la_plugin_slug_down + '_get_boosts',
this.$cloudStatusEl = $( '#ff-cloud-status' );
// add dynamic boosts block
if ( ! this.$boostWidget ) {
this.$boostWidget = $( '<div id="ff-boost-widget" class="ff-boosts--not-available ff-hide"><a class="" href="#addons-tab" id="ff-boost-link"><i class="flaticon-rocket" style="display: inline-block;"></i> Boost</a><span class="ff-boost-info">In use: <span id="ff-boost-in-use">-</span> Expire on: <span id="ff-boost-exp">-</span></span><a href="#addons-tab" class="ff-pseudo-link">Get Boosts!</a></div>' );
this.$sources.find( '#feeds-list-section' ).append( this.$boostWidget );
this.$boostSmartElement = this.$boostWidget.find( '#ff-boost-link' );
this.$boostSmartElement.on( 'mouseenter', function () {
if ( self.drake ) self.$sources.addClass( 'ff-droparea--active' );
}).on( 'mouseleave', function () {
if ( self.drake && ! self.drake.dragging ) self.$sources.removeClass( 'ff-droparea--active' );
if ( !! window.boostsActivated !== true ) { // convert to boolean
data[ 'not_active' ] = true;
var boostsRequest = $.post( vars.ajaxurl, data ).done( function( res ) {
if ( res.error == 'not_allowed' ) {
var months = [ 'Jan', 'Feb', 'March', 'April', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec' ];
subscription = JSON.parse( res );
alert( 'We are sorry but something went wrong with boost activation. Please contact via social-streams.com/contact to sort this out.')
self.subscription = subscription;
if ( subscription.state === 'active' || subscription.state === 'trialing' ) {
self.$boostWidget.removeClass( 'ff-boosts--not-available' );
inUse = ( _.filter( model.get( 'feeds' ), function( feed ){ return feed.boosted == 'yep' }) ).length;
nextPaymentDate = new Date( subscription.next_payment.date );
self.availableBoosts = subscription.availableBoosts;
if ( self.availableBoosts > 0 ) {
self.$boostSmartElement.html( self.availableBoosts + ' boost' + ( self.availableBoosts != 1 ? 's' : '' )).addClass( 'ff-item__draggable' );
self.$boostWidget.find( '#ff-boost-in-use' ).html( inUse );
self.$boostWidget.find( '#ff-boost-exp' ).html( nextPaymentDate.getDate() + ' ' + months[ nextPaymentDate.getMonth() ] + ' ' + nextPaymentDate.getFullYear() );
if ( subscription.plan === $( 'boosts_custom_plan' ).val() ) {
$( '.boosts_custom .boost_custom__plan_txt' ).html( 'Plan ID ' + subscription.subscription_id + ' activated.')
$( '.boosts_custom .boost_custom__plan_actions' ).html( '<a href="/wp-admin/admin-ajax.php?action=flow_flow_cancel_subscription" class="ff-pseudo-link">Cancel subscription</a>')
// init drag n drop in any case after re-render
if ( self.drake ) self.drake.destroy();
self.drake = dragula( [ document.getElementById( 'ff-boost-widget' ) ], {
isContainer: function (el) {
return el.classList.contains( 'feed-row' );
self.drake.on( 'drag', function ( item, _source ) {
console.log( 'drag start' )
if ( item.id === 'ff-boost-link' && self.availableBoosts > 0 ) {
var available = self.availableBoosts;
// if ( ! $('#sources-tab').is( '.active' ) ) $('#sources-tab').click();
self.$sources.addClass( 'ff-droparea--active' );
item.innerHTML = '1 Boost';
setTimeout( function () {
item.innerHTML = ( available - 1 > 0 ? available - 1 : '<i class="flaticon-rocket" style="display: inline-block;"></i>' ) + ' boost' + ( available - 1 > 1 ? 's' : '' );
if ( available - 1 < 1 ) {
self.$boostSmartElement.removeClass( 'ff-item__draggable' );
self.$boostSmartElement.addClass( 'ff-item__draggable' );
} else if ( _source.classList.contains( 'feed-row' ) && item.classList.contains( 'td-info' ) && item.querySelector( '.ff-item__draggable' ) ) { // remove boost from feed case
self.$boostWidget.addClass( 'ff-droparea-widget--active' );
} else { // if target not boost icon then cancel
self.drake.on( 'over', function ( item, _lastDropTarget, _source ) {
console.log( 'drag over' );
if ( item.id === 'ff-boost-link' ) {
$( _lastDropTarget ).addClass( 'ff-droparea' );
$( _lastDropTarget ).addClass( 'ff-droparea-widget' );
self.drake.on( 'out', function ( item, _lastDropTarget, _source ) {
console.log( 'drag out' );
if ( item.id === 'ff-boost-link' ) {
$( _lastDropTarget ).removeClass( 'ff-droparea' );
$( _lastDropTarget ).removeClass( 'ff-droparea-widget' );
self.$sources.find( '.hilite-boost' ).off( 'mouseenter mouseleave' ).on( 'mouseenter', function () {
self.$boostWidget.addClass( 'ff-droparea-widget--active' );
} ).on( 'mouseleave', function () {
if ( ! self.drake.dragging ) self.$boostWidget.removeClass( 'ff-droparea-widget--active' );
self.drake.on( 'drop', function ( item, _lastDropTarget, _source, _dropTargetChild ) {
console.log( 'drag drop' )
self.$sources.removeClass( 'ff-droparea--active' );
self.$boostWidget.removeClass( 'ff-droparea-widget--active' );
// _lastDropTarget null if not container
if ( ! _lastDropTarget || _lastDropTarget.classList.contains( 'feed-boosted' ) ) {
self.showNotification( 'Feed is already boosted.<i class="flaticon-error"></i>' )
if ( _lastDropTarget.getAttribute( 'data-network' ) === 'wordpress' ) {
self.showNotification( 'Boosting WP feeds is not currently possible.<i class="flaticon-error"></i>' )
if ( _lastDropTarget.classList.contains( 'feed-row' ) && self.availableBoosts > 0 ) {
var $t = $( _lastDropTarget );
var uid = $t.data('uid');
var feeds = model.get('feeds');
var type = $t.data('network');
var $view = model.view.$popup.find('[data-uid="' + uid + '"]');
$view = $( _.template(templates[ type + 'View'])({
model.view.$el.find( '#feed-views' ).append( $view );
model.view.setInputsValue( uid );
var $channeling = $view.find('input[name="' + uid + '-boosted"]');
var checked = $channeling.is(':checked');
if ( checked ) return; // already boosted
var streams = streamRowModels.models;
// check if feed in self-hosted stream
for ( var i = 0, len = streams.length; i < len; i++ ) {
_feeds = streams[ i ].get( 'feeds' );
found = _.find( _feeds, function ( feed ) {
if ( found && streams[ i ].get( 'cloud' ) == 'nope' ) { // feed is connected to self-hosted stream
var alert = FlowFlow.popup( 'You\'re trying to boost feed that is connected to self-hosted stream #' + streams[ i ].get( 'id' ) + ( streams[ i ].get( 'name' ) ? ' "' + streams[ i ].get( 'name' ) + '"' : '' ) + '. It\'s not possible to have boosted feeds in self-hosted stream. Please disconnect feed from stream first and create cloud stream for it. Or go to settings of this stream and activate CLOUD switcher on SOURCE tab', 'neutral', 'alert' );
alert.then( function () {
feeds[ uid ]['boosted'] = 'yep';
$channeling.prop('checked', true).change();
// self.availableBoosts--;
model.view.saveViaAjax();
} if ( _source.classList.contains( 'feed-row' ) && item.classList.contains( 'td-info' ) && _lastDropTarget.id == 'ff-boost-widget' ) { // remove boost from feed case
var streams = streamRowModels.models, found;
var uid = $( _source ).data('uid');
for ( var i = 0, len = streams.length; i < len; i++ ) {
feeds = streams[ i ].get( 'feeds' );
found = _.find( feeds, function ( feed ) {
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' );
alert.then( function () {
if ( sync ) applyChange();
// increment boosts in UI
self.$boostSmartElement.html( self.availableBoosts + 1 + ' boost' + ( self.availableBoosts + 1 != 1 ? 's' : '' ) );
var uid = $t.data('uid');
var type = $t.data('network');
var feeds = model.get('feeds');
var $view = model.view.$popup.find('[data-uid="' + uid + '"]');
$view = $( _.template(templates[ type + 'View'])({
model.view.$el.find( '#feed-views' ).append( $view );
model.view.setInputsValue( uid );
var $channeling = $view.find('input[name="' + uid + '-boosted"]');
var checked = $channeling.is(':checked');
feeds[ uid ]['boosted'] = 'nope';
$channeling.prop( 'checked', false ).trigger( 'change', { triggeredFromList: true } );
// todo cancel change event
model.view.saveViaAjax();
self.drake.on( 'cancel', function ( item, _lastDropTarget, _source ) {
console.log( 'drag cancel' );
self.$sources.removeClass( 'ff-droparea--active' );
self.$boostWidget.removeClass( 'ff-droparea-widget--active' );
if ( item.id === 'ff-boost-link' && self.availableBoosts > 0 ) {
var initialHtml = item.innerHTML;
self.$boostSmartElement.html( initialHtml );
} else if (subscription.state === 'deleted') {
window.location.replace( vars.ajaxurl + '?action=flow_flow_cancel_subscription');
self.availableBoosts = 'not_active';
self.$cloudStatusEl.html( 'Your server can connect to cloud OK <span class="cache-status-ok"></span>' );
self.$boostWidget.removeClass( 'ff-hide' );
self.$cloudStatusEl.html( 'It seems your server can\'t connect to cloud <span class="cache-status-error"></span>' );
renderBoostPricingTable: function ( plans, boosts ) {
// sort plans by order prop
plans = _.sortBy( plans, 'order' );
// replacing placeholders
for ( var i = 0, len = plans.length; i < len; i++ ) {
html += ff_templates.pricing_table_item;
// adding custom plan card if it's not active plan and not found in plans
// todo detect better if not custom is active
if ( ! boosts.plan_id || plans.length < 5 ) {
html += ff_templates.pricing_table_item;
$( '#boosts .pricing-table' ).html( html )
$( '.pricing-table__item' ).filter( '[data-plan]' ).each( function ( index, el ) {
$t.attr( 'data-id', id );
$t.attr( 'data-plan', plan.name.toLowerCase() );
$t.find( 'h2' ).html( plan.name )
$t.find( '.pricing-table__item_price ').html( plan.recurring_price.USD );
for ( var i = 0, len = plan.options.length; i < len; i++ ) {
feature = plan.options[ i ];
featuresHtml += '<li>' + feature + '</li>';
$t.find( '.pricing-table__content ul' ).html( featuresHtml );
if ( id == boosts.plan_id && ( boosts.state === 'active' || boosts.state === 'trialing' ) ) { // active plan
$t.addClass( 'pricing-table__active' );