//console.profile("Processing page"); //console.time("Page loading"); ;(function (window) { var transitions = { '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]; break; } } if (!window.transitionEnd) window.transitionEnd = false; })(window); //polyfills Number.isInteger = Number.isInteger || function(value) { return typeof value === 'number' && isFinite(value) && Math.floor(value) === value; }; jQuery.fn.textWidth = function(_text, _font){//get width of text with font. usage: $("div").textWidth(); var fakeEl = jQuery('').hide().appendTo(document.body).text(_text || this.val() || this.text()).css('font', _font || this.css('font')), width = fakeEl.width(); fakeEl.remove(); return width; }; 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||{}); var $t = jQuery(this); $t.on('input', function() { $t.css('width', Math.min(options.maxWidth,Math.max(options.minWidth,$t.textWidth() + options.padding))); }).trigger('input'); return this; } var FlowFlowApp = ( function($) { var $ = jQuery; // streams model, view and collection declaring var StreamModel; var StreamModelsCollection; var StreamView; // rows model, view and collection declaring var StreamRowModel; var StreamRowModelsCollection; var StreamRowView; // instances declaring var Streams; var streamRowModels; var streamModels; // Feeds MVC var FeedsModel; var FeedsView; 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; // css variables 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"); } var FlowFlow = { savedView : sessionStorage.getItem( 'ff_stream' ) || 'list', $body: null, $streamsContainer : null, $sources : null, $list : null, $streamsList : null, $errorPopup : $('
'), $html : null, $content : null, $tabList : null, $boostWidget : null, $cloudStatusEl : null, $tabs: null, $overlay : null, $previewStyles : null, $form: null, $popupBanner: null, editor: null, clip: null, activeTabIndex: parseInt( sessionStorage.getItem('as_active_tab') || 0 ), boostPlan: null, availableBoosts: null, renderBoostsUI: function ( model ) { if ( plugin == 'insta_flow' ) return; var self = this; var data = { 'action': la_plugin_slug_down + '_get_boosts', security: vars.nonce }; this.$cloudStatusEl = $( '#ff-cloud-status' ); // add dynamic boosts block if ( ! this.$boostWidget ) { this.$boostWidget = $( '
BoostIn use: -   Expire on: -Get Boosts!
' ); 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' ) { return; } var subscription; var nextPaymentDate; var months = [ 'Jan', 'Feb', 'March', 'April', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec' ]; var inUse; try { subscription = JSON.parse( res ); } catch ( e ) { // alert( 'We are sorry but something went wrong with boost activation. Please contact via social-streams.com/contact to sort this out.') self.$boostWidget.html( '' ); self.$cloudStatusEl.html( 'It seems your server can\'t connect to cloud service, please try later ' ); return; } 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() ); // custom plan 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( 'Cancel subscription') } // init drag n drop in any case after re-render if ( self.drake ) self.drake.destroy(); self.drake = dragula( [ document.getElementById( 'ff-boost-widget' ) ], { revertOnSpill: true, isContainer: function (el) { return el.classList.contains( 'feed-row' ); }, copy: true } ); self.drake.on( 'drag', function ( item, _source ) { console.log( 'drag start' ) if ( item.id === 'ff-boost-link' && self.availableBoosts > 0 ) { var available = self.availableBoosts; // switch to feeds tab // no need // 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 : '' ) + ' boost' + ( available - 1 > 1 ? 's' : '' ); if ( available - 1 < 1 ) { self.$boostSmartElement.removeClass( 'ff-item__draggable' ); } else { self.$boostSmartElement.addClass( 'ff-item__draggable' ); } }, 0) } 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.cancel() } }) self.drake.on( 'over', function ( item, _lastDropTarget, _source ) { console.log( 'drag over' ); if ( item.id === 'ff-boost-link' ) { $( _lastDropTarget ).addClass( 'ff-droparea' ); } else { $( _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' ); } else { $( _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 var sync, found; if ( ! _lastDropTarget || _lastDropTarget.classList.contains( 'feed-boosted' ) ) { self.drake.cancel(); self.showNotification( 'Feed is already boosted.' ) return; } if ( _lastDropTarget.getAttribute( 'data-network' ) === 'wordpress' ) { self.drake.cancel(); self.showNotification( 'Boosting WP feeds is not currently possible.' ) return; } 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 + '"]'); if ( ! $view.length ) { $view = $( _.template(templates[ type + 'View'])({ uid: uid }) ); model.view.$el.find( '#feed-views' ).append( $view ); // set values 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; var _feeds, found; sync = true; // 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 ) { return feed.id == uid }) if ( found && streams[ i ].get( 'cloud' ) == 'nope' ) { // feed is connected to self-hosted stream self.drake.cancel(); 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' ); sync = false; alert.then( function () { }); } } if ( !sync ) return; feeds[ uid ]['boosted'] = 'yep'; $channeling.prop('checked', true).change(); // ajax will update this // 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 sync = true; 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 ) { return feed.id == uid }) if ( found ) { // show first found 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' ); sync = false; alert.then( function () { }); } } if ( sync ) applyChange(); function applyChange() { // increment boosts in UI self.$boostSmartElement.html( self.availableBoosts + 1 + ' boost' + ( self.availableBoosts + 1 != 1 ? 's' : '' ) ); // remove from feed var $t = $( _source ); var uid = $t.data('uid'); var type = $t.data('network'); var feeds = model.get('feeds'); var $view = model.view.$popup.find('[data-uid="' + uid + '"]'); if ( ! $view.length ) { $view = $( _.template(templates[ type + 'View'])({ uid: uid }) ); model.view.$el.find( '#feed-views' ).append( $view ); // set values 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(); } } else { //self.drake.cancel() } }) 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'); } else { self.availableBoosts = 'not_active'; } self.$cloudStatusEl.html( 'Your server can connect to cloud OK ' ); self.$boostWidget.removeClass( 'ff-hide' ); }).error( function () { self.$cloudStatusEl.html( 'It seems your server can\'t connect to cloud ' ); }) return boostsRequest; }, renderBoostPricingTable: function ( plans, boosts ) { var self = this; var isActiveSub = boosts.state === 'active' || boosts.state === 'trialing'; // sort plans by order prop plans = _.sortBy( plans, 'order' ); // replacing placeholders var html = '' 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 ) { var $t = $( this ); var plan, feature, id, extraClass; var featuresHtml = ''; plan = plans[ index ]; if ( plan ) { id = plan.id; $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 ); // features for ( var i = 0, len = plan.options.length; i < len; i++ ) { feature = plan.options[ i ]; extraClass = i == 1 ? " class='boosts-link ff-pseudo-link'" : ''; if ( i > 0 ) { if ( i == 1 ) { extraClass = " class='boosts-link ff-pseudo-link'"; } else { extraClass = " class='ff-pseudo-link boost-feature ff-feature-" + i + "' data-feature='" + i + "'"; } } featuresHtml += '' + feature + ''; } $t.find( '.pricing-table__content ul' ).html( featuresHtml ); if ( id == boosts.plan_id && isActiveSub ) { // active plan $t.addClass( 'pricing-table__active' ); } $t.find( '.extension__cta--secured' ).each( function () { var $a = $( this ); var hr = $a.attr( 'href'); var plan; if ( isActiveSub && boosts.state !== 'trialing' ) { // we have active plan, make all buttons change plan plan = $a.closest( '.pricing-table__item' ).data( 'id' ); $a.html( 'Switch to plan' ).attr( 'href', '/wp-admin/admin-ajax.php?action=flow_flow_upgrade_subscription&plan_id=' + plan ); } else { // otherwise just standard checkout } $a.attr( 'href', hr + '?intent=' + id + ( $a.is( '.green-button') ? '' : '&cancel=1') + '&domain=' + encodeURIComponent( location.href + '&subscription=1') + '&ajax=' + vars.ajaxurl ); }).mousedown( function () { var $a = $( this ); var hr = $a.attr( 'href'); var coupon = $('#boosts_coupon').val(); // todo replace existing coupon if ( coupon && hr.indexOf( 'coupon' ) == -1 ) { $a.attr( 'href', hr + '&coupon=' + coupon) } }) } else { $t.attr( 'data-plan', 'custom' ); $t.find( 'h2' ).html( 'Need more?' ) $t.find( 'h3' ).html( 'From $40' ); $t.find( '.pricing-table__content ul' ).html( '' ); $t.find( '.extension__cta--secured' ).html( 'Enter plan ID' ); $t.find( '.pricing-table__btn span' ).html( 'Request plan' ) } $t.removeClass( 'pricing-table__placeholder' ).find( '.pricing-table__placeholder-content' ).remove(); }) if ( isActiveSub ) { self.$body.find( '[data-plan="standard"]' ).removeClass( 'pricing-table__best' ) } else { self.$body.find( '[data-plan="standard"]' ).addClass( 'pricing-table__best' ) } // show boosts FAQ if ( $( '#addons-tab' ).is( '.active' ) && location.href.indexOf( 'subscription=1' ) != -1 ) { setTimeout( function () { $( '.boosts-link' ).click(); }, 100) } // show boosts activation error for exceeded domains if ( location.href.indexOf( 'subscription=exceeded_domains' ) != -1 ) { this.popup('You exceeded domains limit for your plan. Please contact us here', false, 'alert') } }, makeOverlayTo: function (op, classN) { this.$html.removeClass('popup_visible'); this.resetScrollbar(); if ( op === 'show' ) { this.$overlay.addClass((classN ? classN + ' ' : '') + 'loading') } else { this.$overlay.removeClass(); } }, init: function () { var self = this; this.$html = $('html'); this.$body = $('body'); this.$streamsContainer = $('#streams-cont'); this.$sources = $('#sources-list'); this.$list = this.$streamsContainer.find('#streams-list tbody'); this.$streamsList = $('#streams-list-section'); this.$form = $('#flow_flow_form'); this.$overlay = $('#fade-overlay'); this.$popupBanner = $('#ff-popup-banner'); this.$content = $('.section-contents'); this.$tabList = $('.section-tabs'); this.$tabs = this.$tabList.find('li'); // execute immediately this.$html.addClass('ff-browser-' + (isWebkit ? 'webkit' : isIE ? 'ie' : isFF ? 'ff' : '') + (window.WP_FF_admin ? ' ff-wp' : ' ff-standalone') + (window.isCompact ? ' ff-compact-admin' : '') + ' ff-' + vars.m + ' admin-page-' + window[ 'l' + alphabet[0] + '_plugin_slug_' + 'down' ]); this.setupModelsAndViews(); this.setupTabsAndContainer(); this.attachGlobalEvents(); FlowFlow.popup = this.initPopup(); //this.initClipBoard(); }, createBackup: function (e) { var data = { 'action': 'create_backup', security: vars.nonce }; FlowFlow.makeOverlayTo('show'); $.post( vars.ajaxurl, data).done(function( res ){ if ( res.error == 'not_allowed' ) { var promise = FlowFlow.popup('Nay! You have no permissions to do this, please contact admin.', false, 'alert'); FlowFlow.makeOverlayTo('hide'); return; } location.reload(); }) }, restoreBackup: function (e) { var promise = FlowFlow.popup('Are you sure?'); var self = this; promise.then(function success(){ var data = { action: 'restore_backup', id: $(self).closest('tr').attr('backup-id'), security: vars.nonce } FlowFlow.makeOverlayTo('show'); $.post( vars.ajaxurl, data ).done(function( data ){ if ( data.error == 'not_allowed' ) { var promise = FlowFlow.popup('Nay! You have no permissions to do this, please contact admin.', false, 'alert'); FlowFlow.makeOverlayTo('hide'); return; } sessionStorage.setItem('as_view_mode', 'list'); sessionStorage.setItem('as_active_tab', 0); location.reload(); }) }, function fail () {}) }, deleteBackup: function () { var promise = FlowFlow.popup('Are you sure?'); var self = this; promise.then(function success(){ var data = { action: 'delete_backup', id: $(self).closest('tr').attr('backup-id'), security: vars.nonce } FlowFlow.makeOverlayTo('show'); $.post( vars.ajaxurl, data ).done(function( res ){ if ( res.error == 'not_allowed' ) { var promise = FlowFlow.popup('Nay! You have no permissions to do this, please contact admin.', false, 'alert'); FlowFlow.makeOverlayTo('hide'); return; } location.reload(); }) }, function fail () {}) }, initPopup: function () { // Alert popup var $popup = $('.cd-popup'); //open popup FlowFlow.$form.on('click', '.cd-popup-trigger', function(event){ event.preventDefault(); $popup.addClass('is-visible'); $(document).on('keyup', escClose); }); $popup.find('#cd-button-yes').on('click', function(e){ e.preventDefault(); $popup.data('defer') && $popup.data('defer').resolve(); $popup.removeClass('is-visible'); }) $popup.find('#cd-button-no, .cd-popup-close').on('click', function(e){ e.preventDefault(); $popup.data('defer') && $popup.data('defer').reject(); $popup.removeClass('is-visible'); }) //close popup $popup.on('click', function(event){ if( $(event.target).is('.cd-popup-close') || $(event.target).is('.cd-popup') ) { event.preventDefault(); $(this).removeClass('is-visible'); $(document).off('keyup', escClose); } }); function escClose(event) { if(event.which=='27'){ $popup.data('defer') && $popup.data('defer').reject(); $popup.removeClass('is-visible'); } } function popup ( text, neutral, type, buttons ) { var defer = $.Deferred(); if ( !neutral ) $popup.removeClass( 'is-neutral' ); if ( type !== 'alert' ) { $popup.removeClass( 'is-alert' ); $popup.find('.cd-buttons li:last-child a').html( 'Yes' ); if ( buttons ) { $popup.find('.cd-buttons li:first-child a').html( buttons.left ); $popup.find('.cd-buttons li:last-child a').html( buttons.right ); } } else { $popup.find('.cd-buttons li:last-child a').html('OK') } $popup.data( 'defer', defer ); $popup.find( 'p' ).html( text || 'Are you sure?' ); $popup.addClass( 'is-visible' + ( neutral ? ' is-neutral' : '') + ( type === 'alert' ? ' is-alert' : '' ) ); $(document).on( 'keyup', escClose ); return defer.promise(); } //close popup when clicking the esc keyboard button $( document ).keyup( function( event ){ if( event.which == '27' ){ $popup.removeClass('is-visible'); } }); return popup; }, setupModelsAndViews : function () { var self = this; var savedScrollState = sessionStorage.getItem('as_scroll'); var $htmlAndBody = $('html, body'); for (var i = 0, len = window.streams.length; i < len; i++) { streamRowModels.add(window.streams[i]); } $('#streams-list tbody tr').not('.empty-row').each(function(){ var $t = $(this); var view = new StreamRowView({model: streamRowModels.get($t.attr('data-stream-id')), el: this}); }); if ( this.savedView !== 'list' && streamRowModels.get(this.savedView) ) { this.makeOverlayTo('show'); streamRowModels.get(this.savedView).view.edit().then(function(id){ if (savedScrollState) { $htmlAndBody.scrollTop(savedScrollState); } if ( ! self.$html.is('.boosts_popup_visible, .streams_popup_visible' ) ) self.makeOverlayTo('hide'); setTimeout(function () { if (sessionStorage.getItem('s' + id + '-tab') && streamModels.get(id)) { streamModels.get(id).view.$el.find('.view-tabs [data-tab="' + sessionStorage.getItem('s' + id + '-tab') + '"]').trigger('click') } },0) setTimeout(function(){ self.$streamsContainer.addClass('transition--enabled'); if (savedScrollState) { $htmlAndBody.scrollTop(savedScrollState); } }, 800); }); } else { this.savedView = 'list'; this.switchToView('list'); this.makeOverlayTo('hide'); if (savedScrollState) { $htmlAndBody.scrollTop(savedScrollState); } setTimeout(function(){ self.$streamsContainer.addClass('transition--enabled'); if (savedScrollState) { $htmlAndBody.scrollTop(savedScrollState); } }, 800); } // feeds init moved to async feedsModel = new FeedsModel(); window.feedsModel = feedsModel; feedsView = new FeedsView({model: feedsModel, el: self.$form.find('#sources-list')[0]}); }, tabsCursor: (function () { var $cont; var $tabs; var $sections; var $cursor; var id; var moveCursor; function init ($el, id) { this[id] = {}; var streamTabs = this[id]; streamTabs.$el = $el; streamTabs.id = id; streamTabs.$tabs = streamTabs.$el.find('.view-tabs'); streamTabs.$cursor = streamTabs.$tabs.find('.tab-cursor'); streamTabs.$sections = streamTabs.$el.find('.section[data-tab]'); moveCursor = moveCursor.bind(this); //console.log('activating tabs', this); setupActive.call(this, id); // attachEvents.call(this, $el); streamTabs.$tabs.find('li').click(function () { var val = $(this).data('tab'); var $active = $(this); streamTabs.$tabs.find('.section-active-tab').removeClass('section-active-tab'); $active.addClass('section-active-tab'); streamTabs.$sections.removeClass('active-section').filter('[data-tab="' + val + '"]').addClass('active-section') FlowFlow.setHeight(streamTabs.id); moveCursor($active, streamTabs.id); sessionStorage.setItem('s' + streamTabs.id + '-tab', val); // todo grace-s }) } function setupActive (id) { var $active = this[id].$tabs.find('li:not(".tab-cursor")').first(); this[id].$tabs.find('li:not(".tab-cursor")').first().addClass('section-active-tab'); this[id].$sections.first().addClass('active-section'); FlowFlow.setHeight(id); setTimeout(function(){ moveCursor($active, id); },0) } function moveCursor ($active, id) { var w = $active.outerWidth(); var pos = $active.position(); this[id].$cursor.css({'left' : pos.left + 'px', minWidth: w + 'px'}) } return { initFor: init } })(), attachGlobalEvents : function () { var self = this; var $backupsForm = this.$form.find('#backup-settings'); this.$streamsContainer.find( '.button-add' ).on( 'click', function() { var model, view; FlowFlow.checkScrollbar(); FlowFlow.setScrollbar(); FlowFlow.$html.addClass( 'streams_popup_visible popup_visible' ); if ( plugin == 'insta_flow' ) { createView(); } else { self.$streamsContainer.find( '.streams-popup' ).on( 'click', function ( e ) { var $target = $( e.target ); var type; // close if ( ! $target.closest( '[data-stream-type]' ).length || $target.is('a') ) { FlowFlow.$html.removeClass( 'streams_popup_visible popup_visible' ); FlowFlow.resetScrollbar(); self.$streamsContainer.find( '.streams-popup' ).off( 'click' ); } else { // create streams if ( $target.is( 'a' ) ) return; type = $target.closest( '[data-stream-type]' ).data( 'streamType' ); createView( type ); } }); } function createView( type ) { if ( !self.$streamsContainer.find('#stream-view-new').length ) { var model = new StreamModel( { cloud: ( type === 'cloud' ? 'yep' : 'nope' ) } ); var view = new StreamView({model: model}); streamModels.add(model); view.$el.addClass('stream-view-new'); self.$streamsContainer.append(view.$el); view.saveViaAjax().then(function ( stream ) { if ( stream.error ) { self.$streamsContainer.find('#stream-view-new').remove(); streamModels.remove( model ); self.switchToView('list'); } else { setTimeout(function(){ self.switchToView( stream.id ); setTimeout( function () { view.$el.find('.input-not-obvious input').focus() }, 400) },0) } }); } } }); this.$streamsContainer.find( '.tutorial-link' ).on( 'click', function() { FlowFlow.checkScrollbar(); FlowFlow.setScrollbar(); FlowFlow.$html.addClass( 'popup_visible tutorial_popup_visible' ); self.$streamsContainer.find( '.tutorial-popup' ).on( 'click', function ( e ) { var $target = $( e.target ); // close if ( ! $target.closest( '.popup-content-wrapper' ).length || $target.is( '.popupclose' ) || $target.is('a') ) { FlowFlow.$html.removeClass( 'popup_visible tutorial_popup_visible' ); FlowFlow.resetScrollbar(); self.$streamsContainer.find( '.tutorial-popup' ).off( 'click' ); } else { } } ) } ) this.$form.on( 'click', '.boosts-link, .boost-feature', function(e) { var $t = $(e.target); FlowFlow.checkScrollbar(); FlowFlow.setScrollbar(); FlowFlow.$html.addClass( 'popup_visible boosts_popup_visible' + ($t.data('feature') ? ' popup_feature_' + $t.data('feature') : '') ); self.$form.find( '.boosts-popup' ).on( 'click', function ( e ) { var $target = $( e.target ); // close if ( ! $target.closest( '.popup-content-wrapper' ).length || $target.is( '.popupclose' ) || $target.is('a') ) { FlowFlow.$html.removeClass( 'popup_visible boosts_popup_visible popup_feature_2 popup_feature_3 popup_feature_4' ); FlowFlow.resetScrollbar(); self.$streamsContainer.find( '.boosts-popup' ).off( 'click' ); } else { } } ) }) this.$form.find('#streams-tab').on('click', function () { if (self.$form.is('.stream-view-visible') && self.activeTabIndex === 0) { self.switchToView('list'); } }); self.$tabs.on( 'click' , function() { var index = self.$tabs.index( this ); var $t = $( this ); if ($t.is('#suggestions-tab')) { /* window.open('http://goo.gl/forms/HAJ95k8kAI'); */ self.insertFeedbackForm(); } self.$tabList.add( self.$content ).find( '.active' ).removeClass( 'active' ); $t.add( self.$content.find( '.section-content:eq(' + index + ')' ) ).addClass( 'active' ); if (index !== 0) { self.$form.removeClass('stream-view-visible'); } else { if (self.$form.find('#streams-cont [data-view-mode="streams-update"].view-visible').length) { self.$form.addClass('stream-view-visible'); } } self.activeTabIndex = index; sessionStorage.setItem('as_active_tab', index); return false; }); $backupsForm.on('click', '.create_backup', this.createBackup); $backupsForm.on('click', '.restore_backup', this.restoreBackup); $backupsForm.on('click', '.delete_backup', this.deleteBackup); this.$form.delegate('.admin-button.submit-button', 'click', function (e) { var $t = $(this); var $contentInput; var $cont; var $licenseCont; var invalid, promise; var opts = { doReload: false, doSubscribe: false } // validate activation form if ($t.is('#user-settings-sbmt')) { $licenseCont = $('#envato_license'); if ($licenseCont.is('.plugin-activated')) { promise = self.popup('Are you sure?'); promise.then(function success(){ $licenseCont.find('input').val(''); $licenseCont.find(':checkbox').prop('checked', false); opts.doReload = true; submitForm(opts); }, function(){ // do nothing }); return; } else { // validation if (!self.validateEmail($licenseCont.find('#company_email').val())) { $licenseCont.find('#company_email').addClass('validation-error'); invalid = true; } if (!self.validateCode($licenseCont.find('#purchase_code').val())) { $licenseCont.find('#purchase_code').addClass('validation-error'); invalid = true; } if (invalid) { return; } else { opts.attemptToActivate = true; opts.doReload = true; } } } if ($t.is('#user-settings-sbmt-2')) { $('#news_subscription').prop('checked', true); opts.doReload = true; opts.doSubscribe = true; } submitForm(opts); function submitForm(opts) { $t.addClass('button-in-progress'); self.makeOverlayTo('show'); $t.closest('form').trigger('submit', opts); sessionStorage.setItem('section-submit', $t.attr('id')); } }); this.$form.on('click', 'a[href*="#"]', function (e) { if (this.hash) { FlowFlow.makeOverlayTo('hide'); self.$form.find(this.hash).click(); } return false }) this.$form.on('submit', function(e, opts){ // console.time('submit') e.preventDefault(); var serialized, data; var $inputs = self.$form.find('.section-content').not('#streams-cont, #campaigns-cont, #sources-cont').find(':input'); //Serialize form as array serialized = $inputs.serializeArray(); //trim values for(var i =0, len = serialized.length;i! Extended token is not generated, Facebook feeds might not work

'); $fb_token.removeClass('fb-empty'); } else if (response.settings.flow_flow_fb_auth_options.facebook_access_token == response.fb_extended_token){ } else { if (response.settings && response.settings.flow_flow_fb_auth_options && response.settings.flow_flow_fb_auth_options.facebook_access_token == '') { $fb_token.addClass('fb-empty'); } else { if (response.fb_extended_token && !$fb_token.find('textarea').length) { $fb_token.find('.desc').remove(); $fb_token.append('

Generated long-life token, it should be different from that you entered above then FB auth is OK

'); } $fb_token.removeClass('fb-empty'); } } if (!opts.doReload) self.makeOverlayTo('hide'); $submitted = $('#' + sessionStorage.getItem('section-submit')); $submitted.addClass('updated-button').html('  Updated'); $submitted.removeClass('button-in-progress'); setTimeout( function () { $submitted.html('Save changes').removeClass('updated-button'); }, 2500); } if (opts.doReload) location.reload(); }, 'json' ).fail( function( d ){ console.log( d.responseText ); console.log( d ); alert('Error occurred. ' + d.responseText); self.makeOverlayTo('hide'); }); return false }); this.$form.delegate('input', 'keydown', function (e){ var $t = $(this); if ($t.is('.validation-error')) { $t.removeClass('validation-error'); } if (e.which == 13) { //console.log('enter') } }); this.$form.find('#facebook_use_own_app').change(function(){ var $t = $(this); var $p = $t.closest('dl'); var checked = this.checked; //$p.find('dd, dt').not('.ff-toggler').find('input')[ checked ? 'removeClass' : 'addClass' ]('disabled') $p[ checked ? 'addClass' : 'removeClass' ]('ff-own-app'); $('#facebook-auth')[this.checked ? 'hide' : 'show'](); }).change(); this.$form.find('.extension__cta--disabled').click(function(e){ e.preventDefault(); }); $( window ).on('beforeunload', function (e) { sessionStorage.setItem('as_scroll', $('body').scrollTop() || $('html').scrollTop()); }); this.$errorPopup.on('mouseleave', function(e){ self.$errorPopup.removeClass('visible') }) this.$form.find('#boosts .extension__cta.grey-button').on( 'click', function ( e ) { e.preventDefault(); var hr = this.href; var promise = FlowFlow.popup( 'You are about to cancel subscription. All active boosted feeds and cloud streams will become self-hosted. Are you sure?'); promise.then(function yes(){ window.location.replace( hr ) }, function no ( reason ) { }) }); this.$popupBanner.on( 'click', function () { self.$popupBanner.removeClass( 'banner-visible' ); } ) this.$form.on( 'click', '.show-debug', function ( e ) { e.preventDefault(); $('#debug-info').toggle(); }) // show popup tutorial if ( window.FF_Cookie ) { if ( window.FF_Cookie.get( 'ff_first_time' ) ) { $( '.tutorial-first-time' ).hide(); } else { $( '.tutorial-link' ).click(); window.FF_Cookie.set( 'ff_first_time', '1', { expires: 356 }); } } // coupon $('.coupon-apply').on( 'click', function () { $( '#general-settings-sbmt' ).click(); }) $('.coupon-clear').on( 'click', function () { $( '#boosts_coupon' ).val( '' ); $( '#general-settings-sbmt' ).click(); }) // boost custom plan $( '#boosts').on( 'click', '[data-plan="custom"] .extension__cta--secured', function ( e ) { e.preventDefault(); var prompt = self.popup( '   ', 'neutral', false, { right: 'Go To Payment', left: 'cancel'} ) prompt.then( function yes ( value ) { var id = $( '#boosts_custom_plan_id' ).val(); var coupon = $('#boosts_coupon').val(); window.location.replace( 'https://social-streams.com/boosts/?intent=' + id + '&domain=' + encodeURIComponent( location.href + '&subscription=1') + ( coupon ? '&coupon=' + coupon : '' ) + '&ajax=' + vars.ajaxurl ); }, function cancel ( reason ) { }) }) // boost manual activation $( '#boosts').on( 'click', '#boosts_manual', function ( e ) { e.preventDefault(); var prompt = self.popup( '
   
   
', 'neutral', false, { right: 'Activate', left: 'cancel'} ) prompt.then( function yes ( value ) { var email = $( '#boosts_manual_email' ).val(); var id = $('#boosts_manual_id').val(); if ( email && id ) { window.location.replace( vars.ajaxurl + "?action=flow_flow_payment_success&email=" + email + "&checkout_id=" + id ); } else { self.popup( 'Please enter both email and ID', false, 'alert' ); } }, function cancel ( reason ) { }) }) this.initFacebookAuth(); this.initFoursquareAuth(); this.initInstagramAuth(); }, showNotification: function ( html ) { var timeout = this.$popupBanner.data( 'timeout' ); this.$popupBanner.find( 'div' ).html( html ).end().addClass( 'banner-visible' ); if ( timeout ) { // current clearTimeout( timeout ); } // new timeout = setTimeout( function () { FlowFlow.$popupBanner.removeClass( 'banner-visible' ); }, 8000 ); this.$popupBanner.data( 'timeout', timeout ); }, backUrl: vars.backUrl, initFacebookAuth: function () { //https://www.facebook.com/dialog/oauth var f = "https://flow.looks-awesome.com/service/auth/facebook-instagram.php?" + $.param({ back: this.backUrl }); $("#facebook-auth").click(function(){ var $t = $(this); if ($(this).html() === 'Log Out') { $('#facebook_access_token').val(''); $('#fb-auth-settings-sbmt').click(); $("#facebook-auth").html('Connect'); return } document.location.href = f; }); $("#fb-refresh-token").click(function(){ document.location.href = f; }); if ($('#facebook_access_token').val() !== '') { $("#facebook-auth").html('Log Out') } }, initFoursquareAuth: function () { var j = "https://foursquare.com/oauth2/authenticate?" + $.param({ client_id: "22XC2KJFR2SFU4BNF4PP1HMTV3JUBSEEVTQZCSCXXIERVKA3", redirect_uri: "http://flow.looks-awesome.com/service/auth/foursquare.php?back=" + this.backUrl, response_type: "code" }); $("#foursquare-auth").click(function(){ var $t = $(this); if ($(this).html() === 'Log Out') { $('#foursquare_access_token').val(''); $('#fq-auth-settings-sbmt').click(); $("#foursquare-auth").html('Authorize'); return } document.location.href = j; }); if ($('#foursquare_access_token').val() !== '') { $("#foursquare-auth").html('Log Out') } if ($('#foursquare_client_id').val() === '') { var $par = $('#foursquare_client_id').parent(); $par.add($par.prev('dt').first()).hide(); } if ($('#foursquare_client_secret').val() === '') { var $par = $('#foursquare_client_secret').parent(); $par.add($par.prev('dt').first()).hide(); } }, initInstagramAuth: function () { //http://stackoverflow.com/questions/7131909/facebook-callback-appends-to-return-url/7297873#7297873 if (window.location.hash && window.location.hash == '#_=_') { window.location.hash = ''; } var h = "https://api.instagram.com/oauth/authorize/?" + $.param({ client_id: "94072d7b728f47b68bc7fc86767b3ebe", redirect_uri: "http://social-streams.com/services/auth/instagram.php?back=" + this.backUrl, response_type: "code", scope: "basic public_content" }); $("#inst-auth").click(function(){ var $t = $(this); if ($(this).html() === 'Log Out') { $('#instagram_access_token').val(''); $('#inst-auth-settings-sbmt').click(); $("#inst-auth").html('Authorize'); return } document.location.href = h; }); if ($('#instagram_access_token').val() !== '') { $("#inst-auth").html('Log Out'); } }, checkScrollbar : function () { this.bodyIsOverflowing = document.body.scrollHeight > document.body.clientHeight this.scrollbarWidth = this.measureScrollbar(); }, setScrollbar : function () { var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10) if (this.bodyIsOverflowing) { this.$body.css('padding-right', bodyPad + this.scrollbarWidth); this.$popupBanner.css('margin-right', bodyPad + this.scrollbarWidth); } }, resetScrollbar : function () { this.$body.css('padding-right', ''); this.$popupBanner.css('margin-right', ''); }, measureScrollbar : function () { // thx walsh var scrollDiv = document.createElement('div') scrollDiv.className = 'ff-modal-scrollbar-measure' this.$body.append(scrollDiv) var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth this.$body[0].removeChild(scrollDiv); return scrollbarWidth; }, switchToView: function (view) { var self = this; this.$streamsContainer.find('.view-visible').removeClass('view-visible'); this.setHeight(view); // setTimeout(function(){ if (view === 'list') { self.$streamsContainer.find('#streams-' + view).addClass('view-visible'); self.$form.removeClass('stream-view-visible'); } else { self.$streamsContainer.find('#stream-view-' + view).addClass('view-visible'); self.$form.addClass('stream-view-visible'); } // },0) console.log('switch to view', view) sessionStorage.setItem('ff_stream', view); }, setHeight : function (id) { var self = this; var heights = []; var maxH; // if (id && !isNaN(parseInt(id))) { self.$streamsContainer.find('#stream-view-' + id + ', #streams-list').each(function(){ heights.push($(this).outerHeight()); }); } else { self.$streamsContainer.find('.section-stream[data-view-mode="streams-update"], #streams-list').each(function(){ heights.push($(this).outerHeight()); }); } maxH = Math.max.apply(Math, heights); self.$streamsContainer.css('minHeight', maxH); }, setupTabsAndContainer: function () { var self = this; var $activeTab; $activeTab = $('.section-tabs li:eq(' + this.activeTabIndex +')'); $activeTab.add('.section-content:eq(' + this.activeTabIndex + ')').addClass('active'); if ($activeTab.is('#suggestions-tab')) this.insertFeedbackForm(); // moderation setTimeout(function () { if (!$('[name="mod-roles"]:checked').length) { $('#mod-role-administrator').prop('checked', true); } },0) if ( this.activeTabIndex !== 0 ) { this.makeOverlayTo('hide'); } $('body').append(this.$errorPopup) .append('
'); // add pricing table html // assume it's 5 $( '#boosts .pricing-table' ).html( ff_templates.pricing_table_item + ff_templates.pricing_table_item + ff_templates.pricing_table_item + ff_templates.pricing_table_item + ff_templates.pricing_table_item ) this.$html.addClass('page-loaded'); $('.wrapper').css('opacity', 1); }, insertFeedbackForm: function insertFeedbackForm() { if (!insertFeedbackForm.inserted) { $('#feedback').append(''); insertFeedbackForm.inserted = true; } }, randomString: function (length, chars) { var result = ''; for (var i = length; i > 0; --i) result += chars[Math.round(Math.random() * (chars.length - 1))]; return result; }, getRandomId: function () { return this.randomString(1, alphabet) + this.randomString(1, alphabet) + new Date().getTime().toString().substr(8); }, addCSSRule: function (sheet, selector, rules) { //Backward searching of the selector matching cssRules if (sheet && sheet.cssRules) { var index=sheet.cssRules.length-1; for(var i=index; i>0; i--){ var current_style = sheet.cssRules[i]; if(current_style.selectorText === selector){ //Append the new rules to the current content of the cssRule; rules=current_style.style.cssText + rules; sheet.deleteRule(i); index=i; } } if(sheet.insertRule){ sheet.insertRule(selector + "{" + rules + "}", index); } else{ sheet.addRule(selector, rules, index); } return sheet.cssRules[index].cssText; } }, validateEmail: function (val) { return /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,20}$/.test(val); }, validateCode: function (val) { return /^[a-z0-9]+\-[a-z0-9]+\-[a-z0-9]+\-[a-z0-9]+\-[a-z0-9]+$/.test(val); } } StreamModel = Backbone.Model.extend({ defaults: function () { return { "name": "", "cloud": "nope", "mod": "nope", "order": "smartCompare", "posts": "30", "days": "", "page-posts": "15", "cache": "yep", "cache_lifetime": "10", "gallery": "yep", "gallery-type": "classic", "private": "nope", "hide-on-desktop": "nope", "hide-on-mobile": "nope", "max-res": "nope", "show-only-media-posts": "nope", "titles": "nope", "hidemeta": "nope", "hidetext": "nope", "heading": "", "headingcolor": "rgb(59, 61, 64)", "subheading": "", "subheadingcolor": "rgb(114, 112, 114)", "hhalign": "center", "bgcolor": "rgb(240, 240, 240)", "filter": "yep", "filtercolor": "rgb(205, 205, 205)", "mobileslider": "nope", "viewportin": "yep", "width": "260", "margin": "20", "layout": "masonry", "theme": "classic", "gc-style": "style-1", "upic-pos": "timestamp", "upic-style": "round", "bradius": "15", "icon-style": "label1", "icon-col": "colored", "cardcolor": "rgb(255, 255, 255)", "namecolor": "rgb(59, 61, 64)", "textcolor": "rgb(131, 141, 143)", "linkscolor": "rgb(94, 159, 202)", "restcolor": "rgb(131, 141, 143)", "shadow": "rgba(0, 0, 0, 0.05)", "bcolor": "rgba(240, 237, 231, 0.4)", "talign": "left", "icons-style": "outline", "cards-num": "3", "scrolltop": "yep", "c-desktop": "5", "c-laptop": "4", "c-tablet-l": "3", "c-tablet-p": "2", "c-smart-l": "2", "c-smart-p": "1", "s-desktop": "15", "s-laptop": "15", "s-tablet-l": "10", "s-tablet-p": "10", "s-smart-l": "15", "s-smart-p": "15", "m-c-desktop": "5", "m-c-laptop": "4", "m-c-tablet-l": "3", "m-c-tablet-p": "2", "m-c-smart-l": "2", "m-c-smart-p": "1", "m-s-desktop": "15", "m-s-laptop": "15", "m-s-tablet-l": "10", "m-s-tablet-p": "10", "m-s-smart-l": "15", "m-s-smart-p": "15", "j-h-desktop": "260", "j-h-laptop": "240", "j-h-tablet-l": "220", "j-h-tablet-p": "200", "j-h-smart-l": "180", "j-h-smart-p": "160", "j-s-desktop": "0", "j-s-laptop": "0", "j-s-tablet-l": "0", "j-s-tablet-p": "0", "j-s-smart-l": "0", "j-s-smart-p": "0", "c-r-desktop": "2", "c-r-laptop": "2", "c-r-tablet-l": "2", "c-r-tablet-p": "2", "c-r-smart-l": "1", "c-r-smart-p": "1", "c-c-desktop": "5", "c-c-laptop": "4", "c-c-tablet-l": "3", "c-c-tablet-p": "3", "c-c-smart-l": "3", "c-c-smart-p": "3", "c-s-desktop": "0", "c-s-laptop": "0", "c-s-tablet-l": "0", "c-s-tablet-p": "0", "c-s-smart-l": "0", "c-s-smart-p": "0", "c-autoplay": "", "c-arrows-always": "yep", "c-arrows-mob": "nope", "c-dots": "yep", "c-dots-mob": "nope", "wallwidth": "", "wallvm": "20", "wallhm": "0", "wallcomments": "yep", "g-ratio-w": "1", "g-ratio-h": "2", "g-ratio-img": "1/2", "g-overlay": "nope", "m-overlay": "nope", "css": "", "feeds": [], "template": ['image', 'header', 'text', 'meta'], "tv": "nope", "tv-int": "5", "tv-logo": "", "tv-bg": "", "big": "nope" } }, initialize: function() { console.log('initialize Stream Model', this); // this.set('feeds', []); }, save: function(isNew){ var self = this; var feedsData; var $params = { emulateJSON: true, data: { action: isNew ? la_plugin_slug_down + '_create_stream' : la_plugin_slug_down + '_save_stream_settings', stream: this.toJSON(), security: vars.nonce }, }; // legacy feeds to JSON if (typeof $params.data.stream.feeds !== 'string') { $params.data.stream.feeds = JSON.stringify($params.data.stream.feeds); } if ($params.data.stream.errors) delete $params.data.stream.errors; return Backbone.sync( 'create', this, $params ).done( function( serverModel ){ if ( serverModel && serverModel.error ) { var promise = FlowFlow.popup( serverModel.error == 'not_allowed' ? 'Nay! You have no permissions to do this, please contact admin.' : 'Nay! Something went wrong, please contact support', false, 'alert'); FlowFlow.makeOverlayTo('hide'); return; } if ( serverModel && serverModel['id'] ) { self.set( 'id', serverModel['id'] ) } /*for (var prop in serverModel) { if (prop === 'feeds' && typeof serverModel[prop] !== 'object') serverModel[prop] = JSON.parse(serverModel[prop]) self.set(prop, serverModel[prop]) }*/ }); // always 'create' because we can't use CRUD request names, only POST }, fetch: function(){ var $params = { emulateJSON: true, data: { 'action': la_plugin_slug_down + '_get_stream_settings', 'stream-id': this.get('id'), 'security': vars.nonce } }; return Backbone.sync( 'read', this, $params ).done(function ( res ) { if ( res.error ) { var promise = FlowFlow.popup( res.error == 'not_allowed' ? 'Nay! You have no permissions to do this, please contact admin.' : 'Nay! Something went wrong, please contact support', false, 'alert'); setTimeout(function(){FlowFlow.switchToView('list')}, 1000); return; } }) }, destroy: function() { var self = this; var $params = { emulateJSON: true, type: 'POST', data: { 'action': la_plugin_slug_down + '_delete_stream', 'stream-id': this.get('id'), 'security': vars.nonce } }; return Backbone.sync( 'delete', this, $params ).done(function( stream ){ if ( stream && stream.error ) { var promise = FlowFlow.popup( stream.error == 'not_allowed' ? 'Nay! You have no permissions to do this, please contact admin.' : 'Nay! Something went wrong, please contact support', false, 'alert'); FlowFlow.makeOverlayTo('hide'); return; } self.collection.remove( self ); }) }, urlRoot: vars.ajaxurl, url: function () { return this.urlRoot; } }); StreamRowModel = Backbone.Model.extend({ defaults: function () { return { 'name' : '', 'status' : 'ok', 'cloud' : 'nope', 'layout' : 'masonry', 'feeds' : [] } }, initialize: function() { console.log('initialize Stream Row Model', this); }, destroy: function() { var self = this; var $params = { emulateJSON: true, type: 'POST', data: { 'action': la_plugin_slug_down + '_delete_stream', 'stream-id': this.get('id'), 'security': vars.nonce } }; return Backbone.sync( 'delete', this, $params ).done(function( stream ){ if ( stream && stream.error ) { var promise = FlowFlow.popup( stream.error == 'not_allowed' ? 'Nay! You have no permissions to do this, please contact admin.' : 'Nay! Something went wrong, please contact support', false, 'alert'); FlowFlow.makeOverlayTo('hide'); return; } self.collection.remove( self ); }) }, clone: function() { var self = this; var $params = { emulateJSON: true, type: 'POST', data: { 'action': la_plugin_slug_down + '_clone_stream', 'stream': this.toJSON(), 'security': vars.nonce } }; return Backbone.sync( 'create', this, $params ).done( function( stream ){ if ( stream && stream.error ) { var promise = FlowFlow.popup( stream.error == 'not_allowed' ? 'Nay! You have no permissions to do this, please contact admin.' : 'Nay! Something went wrong, please contact support', false, 'alert'); FlowFlow.makeOverlayTo('hide'); return; } streamRowModels.add( stream ); }) }, urlRoot: vars.ajaxurl, url: function () { return this.urlRoot; } }); StreamModelsCollection = Backbone.Collection.extend({ model: StreamModel }); StreamRowModelsCollection = Backbone.Collection.extend({ model: StreamRowModel }); streamModels = new StreamModelsCollection(); streamRowModels = new StreamRowModelsCollection(); StreamRowView = Backbone.View.extend({ model: StreamRowModel, tagName: "tr", template: _.template( templates.streamRow ), className: "stream-row", ajaxPages: null, events: { "click .flaticon-tool_edit, .td-name": "edit", "click .flaticon-tool_delete": "destroy", "click .flaticon-tool_clone": "clone", "mouseenter .hint-block": "getShortcodePages", "mouseleave .hint-block": "cancelGetShortcodePages", "click span.shortcode": "selectShortcode" }, initialize: function() { this.model.on('change', function(){ console.log('render row model on change', arguments) this.render() }, this); this.model.view = this; // we can work with models collection now this.hideFeeds(); }, rendered: false, render: function( changed ) { var changed, status; var feeds = this.model.get('feeds'); var boosted = 0; var type; // default; var cloud = this.model.get('cloud'); if ( cloud == 'yep' ) { type = ' Cloud'; } else { type = 'Self-Hosted'; // default; } if (!this.rendered) { console.log('render row view', this.model); status = this.model.get('status'); this.$el.html(this.template({ id: this.model.get('id') || 'new', name: stripslashes(this.model.get('name')) || 'Unnamed', status: parseInt( status ) || status === 'ok' ? 'ok' : 'error', type: type, feeds: this.getFeedsStr( feeds ) })); this.$el.attr('data-stream-id', this.model.get('id') || 'new'); this.rendered = true; } else if (this.model.changed && !_.isEmpty(this.model.changed)) { console.log('changing row view', this.model); changed = this.model.changed; if (changed.hasOwnProperty('id')) { this.$el.find('.shortcode').html('[ff id="' + changed.id + '"]') } if (changed.hasOwnProperty('feeds')) { this.$el.find('.td-feed').html(this.getFeedsStr( feeds )); } if (changed.hasOwnProperty('layout')) { this.$el.find('.td-type').html( type ); } if (changed.hasOwnProperty('status')) { this.$el.find('[class*=cache-status]').removeClass().addClass('cache-status-' + changed.status); } if (changed.hasOwnProperty('name')) { this.$el.find('.td-name').html(changed.name || 'Unnamed'); } if (changed.hasOwnProperty('cloud')) { this.$el.find('.td-type').html( type ); } } this.hideFeeds(); }, hideFeeds: function(){ var _this = this; setTimeout(function () { var $cell = _this.$('.td-feed') var cellWidth = $cell.get(0).offsetWidth - 100 // reserve space for "+ N more" badge var $feeds = _this.$('i', $cell) var feedsWidth = 0 var hiddenCount = 0 if($feeds.length === 0) return $.each($feeds, function(i, feed){ feedsWidth += 26 $(feed).show() if(cellWidth < feedsWidth){ $(feed).hide() hiddenCount++ } }) $cell.find('.link-more').remove(); if(cellWidth < feedsWidth){ $cell.append('+ ' + hiddenCount + ' more') } }, 4) }, getFeedsStr: function (feeds) { var result = ''; if (typeof feeds === 'string') { feeds = JSON.parse(feeds); } if (!feeds || !feeds.length) return 'No Feeds'; for (var i = 0, len = feeds.length; i < len; i++) { result += '' } return result || 'No Feeds'; }, edit: function(e, viewStays) { console.log('row edit', this); var defer = $.Deferred(); var self = this, model, request; var id = this.model.get('id'); if (!id) { alert('Something went wrong, please try to reload page') } if (!FlowFlow.$streamsContainer.find('#stream-view-' + id).length) { this.$el.addClass('stream-loading'); model = new StreamModel({id: id}); request = model.fetch(); request.done( function (response, status, xhr) { var view, attribute, value; if (response.feeds && typeof response.feeds === 'string') { response.feeds = JSON.parse(response.feeds); } for (attribute in response) { value = response[attribute]; model.set(attribute, typeof value === 'string' ? stripslashes(value) : value) } console.log('new StreamView') view = new StreamView({model: model}); streamModels.add(model); FlowFlow.$streamsContainer.append(view.$el); self.$el.removeClass('stream-loading'); defer.resolve(id); setTimeout(function () { if (!viewStays) FlowFlow.switchToView(id); },100) } ).fail (function () { alert('Something went wrong, please try to reload page') self.$el.removeClass('stream-loading'); defer.reject(); }) } else { if (!viewStays) FlowFlow.switchToView(id); defer.resolve(id); } return defer.promise() }, destroy: function() { var promise = FlowFlow.popup('Just checking for misclick. Delete stream?'); var self = this; promise.then(function(){ var id = self.model.get('id'); var request = self.model.destroy(); FlowFlow.makeOverlayTo('show'); request.done(function( stream ){ if ( stream && stream.error ) return; self.remove(); if (streamRowModels.length === 0) { FlowFlow.$list.append(templates.streamRowEmpty); } }).always(function(){ FlowFlow.makeOverlayTo('hide'); }).fail(function(){ alert('Something went wrong, please try to reload page'); }) },function(){}) }, clone: function() { var self = this; var request = self.model.clone(); FlowFlow.makeOverlayTo('show'); request.done(function(stream){ var model = streamRowModels.get(stream.id) var view = new StreamRowView({model: model}); FlowFlow.$list.append(view.$el); view.render(); }).always(function(){ FlowFlow.makeOverlayTo('hide'); }).fail(function(){ alert('Something went wrong, please try to reload page'); }) }, getShortcodePages: function() { var id = this.model.get('id'); var data = { action: la_plugin_slug_down + '_get_shortcode_pages', stream: id, security: vars.nonce } var $hint = this.$el.find( '.shortcode-pages' ); $hint.html( '...' ); this.ajaxPages = $.post( vars.ajaxurl, data ).done(function( res ){ console.log( res ); var pages = ''; var data = JSON.parse( res ); var page; if ( !data.length ) { $hint.html( 'No pages found' ); return } for ( var i = 0, len = data.length; i < len; i++ ) { page = data[ i ]; pages += '' + page.post_title + '
'; } $hint.html( pages ); }) .fail(function() { $hint.html( 'Something went wrong, please report error.' ); }) }, cancelGetShortcodePages: function () { if ( this.ajaxPages ) this.ajaxPages.abort(); this.$el.find( '.shortcode-pages' ).html(''); }, selectShortcode: function(e){ var el = e.target; var doc = window.document, sel, range; if (window.getSelection && doc.createRange) { sel = window.getSelection(); range = doc.createRange(); range.selectNodeContents(el); sel.removeAllRanges(); sel.addRange(range); } else if (doc.body.createTextRange) { range = doc.body.createTextRange(); range.moveToElementText(el); range.select(); } } }); StreamView = Backbone.View.extend({ tagname: "div", template: _.template(templates.stream), className: "section-stream", streams: [], rowModel: null, rowView: null, currentId: 'new', $preview: null, events: { "click .admin-button.submit-button": "saveViaAjax", "change input, textarea": "updateModel", "input [type=range]": "updateModel", "colorpicker-change input": "updateModel", "change select:not(.stream-streams__select select)": "updateModel", "click .disabled-button": "disableAction", // "click .stream-streams__item": "showPreview", "click .stream-feeds__item": "detachFeed", "click .stream-feeds__block": "displayFeedsSelect", "click .stream-feeds__btn": "connectFeed", "change [id^=stream-layout]": "changeDesignMode", "change .input-not-obvious input": "saveName", "keyup .input-not-obvious input": "saveName", "change .design-step-2 select[id*=align]" : "previewChangeAlign", "change .design-step-2 select[id*=icons-style]" : "previewChangeIconsLook", "change .design-step-2 select[id*=upic-pos]" : "previewChangeUpic", "change .design-step-2 select[id*=upic-style]" : "previewChangeCorners", "change .design-step-2 select[id*=icon-style]" : "previewChangeIcon", "change .design-step-2 select[id*=icon-col]" : "previewChangeIconCol", "keyup .design-step-2 input[id*=bradius]" : "previewChangeBradius", "keyup .design-step-2 [id*=width]" : "previewChangeWidth", "change .layout-compact select[id*=compact-style]" : "previewChangeCompact", //"change .style-choice select[id*=gc-style]" : "previewChangeStyle", "change .theme-choice input" : "previewChangeTheme" }, initialize: function() { //this.listenTo(this.model, "change", this.render); var self = this; var rowModel, rowView; var rendered = this.rendered; this.model.view = this; this.render(); this.model.listenTo(this, 'changeModel', function (data){ // console.log('changeModel event', data); self.model.set(data.name, data.val); }) if (this.model.isNew()) { } else { this.rowModel = streamRowModels.get(this.model.get('id')); console.log('binding models..') this.bindModels(); } this.$preview = this.$el.find('.preview .ff-stream-wrapper'); self.on('preview-update', function () { var $item = self.$preview.find('.ff-item') if ($item.find('.ff-item-cont').children().first().is('.ff-item-meta')) { $item.addClass('ff-meta-first') } else { $item.removeClass('ff-meta-first') } }); }, bindModels: function () { var self = this; this.model.listenTo(feedsModel, 'change', function(changedModel){ var streamFeeds = this.get('feeds'); var allFeeds = feedsModel.get('feeds'); var changedFeeds = changedModel.get('feeds_changed'); var triggerRender = false, indexToDelete = -1; _.each(streamFeeds, function (feed, index) { var changed = changedFeeds[feed.id]; if (changed) { if (changed['state'] === 'changed') { streamFeeds[index] = allFeeds[feed.id]; triggerRender = true; } else if (changed['state'] === 'deleted') { indexToDelete = index; triggerRender = true; } } }); if (indexToDelete > -1) streamFeeds.splice(indexToDelete, 1); if (triggerRender) { this.view.renderConnectedFeeds(); } console.log('stream listening to feedsModel'); }, this); this.rowModel.listenTo(this.model, 'stream-saved', function (model) { var attrs = self.model.attributes; for (var prop in attrs) { if (self.rowModel['attributes'][prop] !== undefined) { if (typeof attrs[prop] === 'object') { self.rowModel.set(prop, _.clone(attrs[prop])); } else { self.rowModel.set(prop, attrs[prop]); } } } }) }, render: function() { var id = this.model.get('id'); var self = this; console.log('render stream view'); if ( !this.rendered || !this.currentId ) { this.$el.attr('data-view-mode', 'streams-update').attr('id', 'stream-view-' + (id || 'new')); this.$el.html(this.template({ id: id || 'new', plugin_url: window.plugin_url, header: id && id != 'new' ? 'Stream #' + id : 'Creating...', version: window.plugin_ver, TV: templates.tv ? _.template(templates.tv)({id:id}) : '', TVtab: templates.tvTab || '' })) setTimeout(function () { self.$el.find(".input-not-obvious input").autoresize({padding:1,minWidth:56,maxWidth:400}); }) FlowFlow.tabsCursor.initFor(this.$el, id); setTimeout(function () { self.$preview = self.$el.find('.preview .ff-stream-wrapper'); self.configDesign(); self.applySavedTemplate(); self.trigger('preview-update'); },0) this.setupCloudToggle(); } this.setInputsValue(); this.renderConnectedFeeds(); this.currentId = id; this.rendered = true; $(document).trigger('stream_view_built', this.$el); }, saveName: function (e) { var val = e.target.value; var type = e.type; var oldval if (/*e.type === 'change' ||*/ e.type === 'keyup' && e.keyCode == 13) { this.saveViaAjax(); } }, saving: false, configDesign: function () { console.log('config design and cpickers'); var self = this; this.$el.find('input[type="range"]').on('mouseup', function() { this.blur(); }).on('change input', function () { var $t = $(this); var name = this.name.indexOf('-r-') + 1 ? 'row' : 'column'; var $v = $t.data('el') ? $t.data('el') : $t.next('.range-value'); if (!$v) { $v = $t.parent().find('.range-value'); $t.data('el', $v) } $v.html(this.value + ' ' + name + (this.value > 1 ? 's' : '')); $t = null; }).change()/*.rangeslider()*/; this.$el.find('input[data-color-format]').ColorPickerSliders( this.colorPickersConfig ); // set initial values by triggering changes events this.$el.find('[id^=stream-layout]:checked, select[id*=upic-pos], select[id*=upic-style], select[id*=icon-style],select[id*=icon-col], select[id*=icons-style], .design-step-2 select[id*=align]').change(); this.$el.find('.design-step-2 input[id*=bradius]').keyup(); // make preview sortable this.$el.find('.ff-item-cont').sortableCustom({ handle: '.ff-item__draggable', animation: 200, mimicBg: true, draggable: '.ff-item__draggable', onUpdate: function () { var template = []; var $preview = self.$el.find('.ff-item-cont'); $preview.children().each(function () { var role = $(this).data('template'); if (role) template.push(role); }) self.model.set('template', template); $preview.find('.ff-label-wrapper').insertAfter($preview.find('.ff-item-meta')); self.trigger('preview-update'); } }) }, applySavedTemplate: function () { var template = this.model.get('template'); var i, len; var $cont = this.$el.find('.ff-item-cont'); var detached = { 'header': $cont.find('[data-template="header"]').detach(), 'text': $cont.find('[data-template="text"]').detach(), 'image': $cont.find('[data-template="image"]').detach(), 'meta': $cont.find('[data-template="meta"]').detach(), } for ( i = 0, len = template.length; i < len; i++ ) { $cont.append( detached[template[i]] ); } $cont.find('.ff-label-wrapper').insertAfter( detached.meta ); $cont.find('> .ff-item-bar').appendTo($cont); }, setupCloudToggle: function () { var self = this; var cloud = this.model.get( 'cloud' ); var id = this.model.get( 'id' ); this.$el.find( '.section[data-tab="source"]' ).append( '
./. feeds
boosted
' ); // this.$el.find( '#stream-' + id + '-boosted' ).prop( 'checked', cloud == 'yep' ); this.$el.find( '[for=stream-' + id + '-boosted]' ).on( 'click', function ( e ) { e.preventDefault(); var $t = $( this ); var $inp = $t.find( 'input' ); var currentStreamFeeds = self.model.get('feeds'); if ( ! $inp.is( ":checked" ) ) { // intent to boost all feeds if ( FlowFlow.availableBoosts !== null ) { if ( FlowFlow.availableBoosts == 'not_active' ) { FlowFlow.popup( 'No available boosts to access cloud service, please go to Extra tab for more info', 'neutral', false, { right: 'Learn more', left: 'cancel'} ) .then( function yes (value) { $( '#addons-tab' ).click(); }, function cancel (reason) { }) } else if ( FlowFlow.availableBoosts < currentStreamFeeds.length ) { FlowFlow.popup( 'Not enough available boosts, please free up boosts from other feeds or upgrade plan on Extra tab', 'neutral', false, { right: 'Go to extra', left: 'cancel'} ) .then( function yes (value) { $( '#addons-tab' ).click(); }, function cancel (reason) { }) } else { // then check if feeds in other streams // cancel if found var streams = streamRowModels.models; var found; var cancelCloudChange; var streamFeeds; for ( var i = 0, len = streams.length; i < len; i++ ) { if ( streams[ i ].cloud == 'yep' ) continue; if ( streams[ i ].id == id ) continue; // current stream streamFeeds = streams[ i ].get( 'feeds' ); found = _.find( streamFeeds, function ( streamFeed ) { return _.find( currentStreamFeeds, function ( currentStreamFeed ) { return currentStreamFeed.id == streamFeed.id; } ) }) if ( found ) { // this feed in other stream // show first found var alert = FlowFlow.popup( 'One of this stream feeds ' + (found.content ? '("' + found.content + '")' : '' ) + ' is also connected to other stream (Stream #' + streams[ i ].get( 'id' ) + ( streams[ i ].get( 'name' ) ? ' "' + streams[ i ].get( 'name' ) + '"' : '' ) + '). Feed can\'t be in cloud and self-hosted stream simultaneously. Please disconnect feed from other stream first.', 'neutral', false, { right: 'Learn more', left: 'close'} ) .then( function yes (value) { $( '#addons-tab' ).click(); }, function cancel (reason) { }); cancelCloudChange = true; break; } } // сheck if WP feeds var wpFeed = _.find( currentStreamFeeds, function ( feed ) { return feed.type == 'wordpress'; }) if ( wpFeed ) { // show first found var alert = FlowFlow.popup( 'One of this stream feeds has Wordpress source. WordPress feeds can\'t be in cloud currently. Please disconnect feed from stream first.', 'neutral', false, { right: 'Learn more', left: 'close'} ) .then( function yes (value) { $( '#addons-tab' ).click(); }, function cancel (reason) { }); cancelCloudChange = true; } if ( ! cancelCloudChange ) { var alert = FlowFlow.popup( 'You are about to enable cloud service for this stream, aka boosting all connected feeds. This process will be run in background, please wait for confirmation notification. Time to wait depends on number of connected feeds. Please don\'t reload browser', 'neutral', false, { right: 'ENABLE CLOUD', left: 'cancel'} ) .then( function yes (value) { // todo https://www.webniraj.com/2018/10/08/making-ajax-calls-sequentially-using-jquery/ var delay = 0; var $loaded = self.$el.find( '.ff-feeds-counter__loaded' ); self.$el.addClass( 'toggling-cloud' ); self.$el.find( '.ff-feeds-counter__total' ).html( currentStreamFeeds.length ); var requests = []; $loaded.html( 0 ).data( 'current', 0); _.each( currentStreamFeeds, function ( feed ) { // initiate boost for feeds var $view = feedsView.$popup.find('[data-uid="' + feed.id + '"]'); if ( ! $view.length ) { $view = $( _.template(templates[ feed.type + 'View'])({ uid: feed.id }) ); feedsView.$el.find( '#feed-views' ).append( $view ); // set values feedsView.setInputsValue( feed.id ); } var $channeling = $view.find('input[name="' + feed.id + '-boosted"]'); var allFeeds = feedsModel.get( 'feeds' ); var request = $.Deferred(); requests.push( request ); setTimeout( function () { allFeeds[ feed.id ]['boosted'] = 'yep'; //allFeeds[ feed.id ]['enabled'] = 'yep'; $channeling.prop('checked', true ).change(); // trigger change feedsView.saveViaAjax(); var current = $loaded.data( 'current'); $loaded.html( current + 1 ).data( 'current', current + 1 ); request.resolve( feed ); }, delay) delay += 5000; }) if ( currentStreamFeeds == 0 ) { } $.when.apply( $, requests ).then( function () { var args = Array.prototype.slice.call( arguments ); console.log( args, requests ); self.model.set( 'cloud', 'yep' ); $inp.prop( 'checked', true ); self.saveViaAjax().done( function () { self.$el.find( '.dots-loading' ).removeClass( 'dots-loading' ) setTimeout( function () { self.$el.removeClass( 'toggling-cloud' ); }, 3000) }); } ); }, function cancel (reason) { //$t.prop( 'checked', false ); }); } } } else { FlowFlow.showNotification( 'Connection with cloud wasn\'t established, please try later or contact us.' ); $inp.prop( "checked", false ); } } else { // make all feeds regular var alert = FlowFlow.popup( 'You are about to disable cloud service for this stream, aka removing boosts from all connected feeds. To not overload your server with updating of possibly big amount of feeds they will be disabled, please enable LIVE for each feed individually on FEEDS tab.', 'neutral', false, { right: 'DISABLE CLOUD', left: 'cancel'} ) .then( function yes (value) { var delay = 0; // save cloud prop in stream or do this later? self.model.set( 'cloud', 'nope' ); $inp.prop( 'checked', false ); self.saveViaAjax(); _.each( currentStreamFeeds, function ( feed ) { // initiate boost for feeds var $view = feedsView.$popup.find('[data-uid="' + feed.id + '"]'); if ( ! $view.length ) { $view = $( _.template(templates[ feed.type + 'View'])({ uid: feed.id }) ); feedsView.$el.find( '#feed-views' ).append( $view ); // set values feedsView.setInputsValue( feed.id ); } var $channeling = $view.find('input[name="' + feed.id + '-boosted"]'); var allFeeds = feedsModel.get( 'feeds' ); setTimeout( function () { allFeeds[ feed.id ]['boosted'] = 'nope'; $channeling.prop('checked', false ).change(); // trigger change feedsView.saveViaAjax(); }, delay) delay += 500; }) }, function cancel (reason) { // $t.prop( 'checked', true ); }); } }) }, renderConnectedFeeds: function () { var feeds = this.model.get('feeds'); var $cont = this.$el.find('.stream-feeds__list'); var feed, name, fullName; var items = ''; if (!feeds) return; for (var i = 0, len = feeds.length; i < len; i++) { feed = feeds[i]; name = feed.content; fullName = name; if (!name && feed.type === "wordpress") { name = feed['category-name'] || feed['wordpress-type']; } if (feed.type === "rss" ) { if (feed['channel-name']) name = feed['channel-name']; } else if (feed.type === "twitter" && feed['timeline-type'] === 'list_timeline') { name += ' - ' + feed['list-name']; } if (name.length > 13) name = name.substr(0, 13) + '...'; items += '' + stripslashes( name ) + ''; } $cont.html('').append(items).closest('.stream-feeds').removeClass('stream-feeds--connecting'); }, connectFeed: function (e) { var self = this; var $t = $(e.target).closest('.stream-feeds__btn'); var streamFeeds = this.model.get('feeds'); var allFeeds = feedsModel.get('feeds'); var feed; var val; var boostedFeeds = _.filter( streamFeeds , function( feed ){ return feed.boosted == 'yep' }); var promise; if ($t.is('.stream-feeds__close')) { $t.closest('.stream-feeds').removeClass('stream-feeds--connecting') return; } val = this.$el.find('.stream-feeds select :selected').val(); feed = allFeeds[ val ]; if ( !feed ) return; // if cloud stream and regular feed warn user // disabled for now if ( false && feed.boosted == 'nope' && boostedFeeds.length == streamFeeds.length ) { console.log('add regular feed'); promise = FlowFlow.popup( 'You are about to add regular feed to Cloud stream, this will make it Self-Hosted. Are you sure?', 'neutral' ); promise.then(function(){ saveFeeds(); },function(){ $t.closest('.stream-feeds').removeClass('stream-feeds--connecting'); }); } else { saveFeeds(); } function saveFeeds() { // double check it doesn't exist already if ( !_.find( streamFeeds, function(e){ return e.id === val }) ) { streamFeeds.push( feed ); } FlowFlow.makeOverlayTo( 'show' ); var request = self.model.save(); request.done(function(serverModel){ self.model.trigger('stream-saved'); self.renderConnectedFeeds(); }).always(function(){ FlowFlow.makeOverlayTo('hide'); }); } }, displayFeedsSelect: function () { var self = this; var connectedFeeds = this.model.get( 'feeds' ); var availableFeeds = _.clone( feedsModel.get( 'feeds' ) ); var isEmpty = _.isEmpty( availableFeeds ), isEmptyAfterFilter; var connectedFeedsIDs = {}; var isCloudStream = this.model.get( 'cloud' ) == 'yep'; var i, len, feed; var $select = this.$el.find( '.stream-feeds select' ); var options = ''; var name; // create IDs map _.each( connectedFeeds, function ( el ) { connectedFeedsIDs[ el.id ] = true; } ); // filter connected and boosted / regular type for appropriate stream type availableFeeds = _.filter( availableFeeds, function ( feed ) { var isFeedBoosted = feed.boosted == 'yep'; var includeThisFeed = true; if ( isCloudStream && ! isFeedBoosted ) { includeThisFeed = false; } else if ( ! isCloudStream && isFeedBoosted ) { includeThisFeed = false; } return !connectedFeedsIDs[ feed.id ] && includeThisFeed; }) isEmptyAfterFilter = _.isEmpty( availableFeeds ); if ( isEmpty || isEmptyAfterFilter ) { // var msg = isEmpty ? 'It seems there are no available feeds for this type of stream. Go to Feeds tab?' : 'You connected all feeds already. Go to Feeds tab?'; var msg ='It seems there are no available feeds for this type of stream. Go to Feeds tab?'; var promise = FlowFlow.popup( msg, 'neutral' ); promise.then(function(){ FlowFlow.$form.find('#sources-tab').click() },function(){}); return } for (i = 0, len = availableFeeds.length; i < len; i++) { feed = availableFeeds[i]; name = feed.content; if (!name && feed.type == "wordpress") { name = capitaliseFirstLetter(feed['category-name'] || 'Posts'); } options += ''; } $select.html('').append(options).closest('.stream-feeds').addClass('stream-feeds--connecting'); $select.chosen("destroy"); $select.chosen(); }, detachFeed: function (e) { var promise = FlowFlow.popup('Disconnect feed from stream?', 'neutral'); var self = this; var $t = $(e.target).closest('span'); var id = $t.data('id'); e.stopPropagation(); promise.then( function success () { self.model.set('feeds', _.filter(self.model.get('feeds'), function(el){return el.id != id})); FlowFlow.makeOverlayTo('show'); var request = self.model.save(); request.done(function(serverModel){ self.model.trigger('stream-saved'); $t.remove(); }).always(function(){ FlowFlow.makeOverlayTo('hide'); }); }, function fail () { } ) }, disableAction: function (e) { e.stopImmediatePropagation() }, setInputsValue: function () { // console.log('set inputs value'); var $input; var id = this.model.get('id'); var attrs = this.model.attributes; var val; var optVal; var selector; var name; for ( name in attrs ) { //if (/s.+?\-f/.test(name)) continue; selector = '[name="stream-' + id + '-' + name + '"]'; $input = this.$el.find( selector ); val = typeof attrs[name] === 'object' ? JSON.stringify( attrs[name] ) : attrs[name]; if ($input.length > 1) { // assume checkbox group optVal = attrs[name]; if (typeof optVal === 'object') { $input.each(function(){ var $t = $( this ); if (!this.disabled) $t.prop('checked', optVal[this.value]); }); optVal = null; } else { $input.each(function(){ var $t = $( this ); if (!this.disabled) $t.prop('checked', $t.val() == optVal); }); } } else if ($input.is(':radio') || $input.is(':checkbox')) { $input.each(function(){ var $t = $( this ); if ( !this.disabled ) $t.prop( 'checked', attrs[name] === 'yep' ); }); } else { $input.val(val ? stripslashes(val.toString()) : ''); } } }, changeDesignMode: function (e) { var val = e.currentTarget.value; var self = this; var $p = $(e.currentTarget).closest('.section'); $p.removeClass(function(index, cls) { return cls.match(/\w+-layout-chosen/)[0]; }).addClass(val + '-layout-chosen').find('.section-settings').removeClass('settings-section__active').end() .find('.settings-' + val).addClass('settings-section__active'); setTimeout(function () { FlowFlow.setHeight(self.model.get('id')); },0); }, previewChangeAlign: function (e) { var val = e.target.value; var $preview = $(e.target).closest('dl').find('.preview .ff-stream-wrapper'); $preview.css('text-align', val); }, previewChangeUpic: function (e) { var val = e.target.value; this.$preview.removeClass('ff-upic-timestamp ff-upic-centered ff-upic-centered-big ff-upic-off').addClass('ff-upic-' + val); }, previewChangeCorners: function (e) { var val = e.target.value; this.$preview.removeClass('ff-upic-round ff-upic-square').addClass('ff-upic-' + val); if ( val == 'square' ) { this.$el.find( '.upic-style-toggle' ).hide(); } else { this.$el.find( '.upic-style-toggle' ).show(); } }, previewChangeIcon: function (e) { var val = e.target.value; this.$preview.removeClass('ff-sc-label1 ff-sc-label2 ff-sc-stamp1 ff-sc-off').addClass('ff-sc-' + val); }, previewChangeIconCol: function (e) { var val = e.target.value; this.$preview.removeClass('ff-sc-colored ff-sc-light ff-sc-dark').addClass('ff-sc-' + val); }, previewChangeBradius: function (e) { var val = e.target.value; this.$preview.find('.picture-item__inner, .ff-img-holder img').css( 'borderRadius', val + 'px' ); }, previewChangeIconsLook: function (e) { var val = e.target.value; this.$preview.removeClass('ff-fill-icon ff-outline-icon').addClass('ff-' + val + '-icon'); }, previewChangeStyle: function (e) { var val = e.target.value; var $preview = $(e.target).closest('dl').find('.preview .ff-stream-wrapper'); var cls = $preview.attr( 'class' ); if (/flat/.test(cls)) { this.revert($preview); this.reformat($preview, val); } $preview.removeClass(function() { return cls.match(/ff-style-[1-9]/)[0]; }).addClass('ff-' + val); }, previewChangeTheme: function (e) { var val = e.target.value; var $cont = $(e.target).closest('.design-step-2'); $cont.find('.style-choice').hide(); $cont.find('.' + val + '-style').show(); }, previewChangeWidth: function (e) { var val = e.target.value; var $preview = $(e.target).closest('.design-step-2').find('.classic-style .preview, .flat-style .preview'); val = parseInt(val); if (isNaN(val)) return; $preview.find('.ff-item').css('width', val + 'px') }, reformat: function ($stream, style) { $stream.find('.ff-item').each(function(i,el){ var $el = $(el); var $img = $el.find('.ff-img-holder'); var $meta; if (/[12]/.test(style)) { $meta = $el.find('.ff-item-meta'); $el.find('.ff-item-cont').prepend($meta); if (!$img.length) { if (style === 'style-1') { $meta.append($meta.find('.ff-userpic')); } $el.addClass('ff-no-image') } else { $el.addClass('ff-image') } } else if (style === 'style-3') { $el.prepend($el.find('.ff-icon')); } $el.addClass('ff-' + (!$img.length ? 'no-' : '') +'image'); $el.prepend($img); }) }, revert: function ($stream) { $stream.find('.ff-item').each(function(i,el){ //console.log('revert',el) var $el = $(el); var $cont = $el.find('.ff-item-cont'); $cont.append($cont.find('h4')); $cont.append($cont.find('.ff-img-holder')); $cont.append($cont.find('p')); $cont.append($cont.find('.ff-item-meta')); $el.find('.ff-userpic').append($el.find('.ff-icon')) }) }, colorPickersConfig : { previewontriggerelement: true, previewformat: 'rgba', flat: false, color: 'rgb(255, 88, 115)', customswatches: 'card_bg', swatches: [ '#c0392b', 'a3503c', '925873', '927758', '589272', '588c92', '2bb1c0', '2b8ac0', 'e96701', 'c02b74', '000000', '4C4C4C', 'CCCCCC', 'F0F0F0', 'FFFFFF' ], order: { hsl: 1, opacity: 2, preview: 3 }, onchange: function(container, color) { var $preview = container.data('preview'); var sel = container.data('prop').replace(/-\d+/, ''); var $targ = $preview.find('[data-preview*="' + sel + '"]'); var $inp = container.data('input'); var prop = $inp.attr('data-prop'); var pre = ''; $inp.trigger('colorpicker-change'); if (!$targ.length) return; if (prop === 'box-shadow') pre = '0 1px 4px 0 '; $targ.each(function(){ var $t = $(this); //console.log(this, $t.attr('data-overrideProp') || prop) var col = color.tiny.toRgbString(); $t.css($t.attr('data-overrideProp') || prop, pre + col) }); } }, goBack: function() { FlowFlow.switchToView('list'); }, updateModel: function(event) { var $t = $(event.target); var val = $t.val(); var name = $t.attr('name'); var $group; if (!name ) { return; } if ($t.is(':radio')) { val = $t.is(':checked') ? ($t.attr('value') || 'yep') : 'nope' } if ($t.is(':checkbox')) { $group = this.$el.find('[name="' + name + '"]'); if ($group.length > 1) { // group val = {}; $group.each(function () { var cbVal = this.value; if (this.checked) val[cbVal] = 'yep'; }); } else { val = $t.is(':checked') ? 'yep' : 'nope'; } } this.trigger('changeModel', {name: name.replace('stream-' + (this.model.get('id') || 'new') + '-', ''), val: val }) }, saveViaAjax: function ( e ) { if (this.saving) return; else this.saving = true; console.log('save stream'); if ( e ) e.stopImmediatePropagation(); var self = this; var wasEmptyList = streamRowModels.length === 0; var $t = $(e ? e.target : ''); var isNew = this.model.isNew(); // validation in popup if ($t.is('[id^=networks-sbmt]')) { if (!this.validateFeedCfg($t)) return false; } FlowFlow.makeOverlayTo('show'); $t.addClass('button-in-progress'); var promise = this.model.save(isNew); promise.done(function(serverModel){ if (serverModel.error) return; FlowFlow.makeOverlayTo('hide'); self.render(); if (isNew) { self.rowModel = new StreamRowModel( { cloud : serverModel.cloud }); self.rowView = new StreamRowView({model: self.rowModel}); streamRowModels.add(self.rowModel); FlowFlow.$list.append(self.rowView.$el); self.bindModels(); } else { self.$el.removeClass('stream-view-new'); } self.rowModel.set('id', serverModel.id); self.model.trigger('stream-saved'); if (wasEmptyList) { FlowFlow.$list.find('.empty-row').remove(); } sessionStorage.setItem('ff_stream', serverModel.id); $t.addClass('updated-button').html('  Updated'); $t.removeClass('button-in-progress'); setTimeout( function () { $t.html('Save changes').removeClass('updated-button'); }, 2500); }).fail(function(){ alert('Something went wrong. Please try to reload page. If this repeats please contact support at https://social-streams.com/contact/') }).always(function () { self.saving = false; }); return promise; }, showPreview: function (e) { var $t = $(e.target); var id = $t.data('id'); FlowFlow.makeOverlayTo('show'); $.get( vars.ajaxurl, { 'action' : 'flow_flow_show_preview', 'stream-id' : id }).success(function(response){ var $popup = $('.content-popup'); var $body = $('body'); FlowFlow.makeOverlayTo('hide'); $body.css('overflow', 'hidden'); $popup.off(transitionEnd).addClass('is-visible').find('.content-popup__content').html(response); if (FlowFlow.$previewStyles) { FlowFlow.$previewStyles.appendTo('head'); } $popup.on('click', function(event){ if( $(event.target).is('.content-popup__close') || $(event.target).is('.content-popup') ) { event.preventDefault(); var self = this; $(this).removeClass('is-visible'); $popup.off('click'); $popup.on(transitionEnd, function(){ $popup.find('.content-popup__content').html('').off(transitionEnd); $body.find('.ff-slideshow').remove(); if (!FlowFlow.$previewStyles) { FlowFlow.$previewStyles = $('#ff_style, #ff_ad_style'); } FlowFlow.$previewStyles.detach(); }) $body.css('overflow', ''); } }); }).fail(function(){ FlowFlow.makeOverlayTo('hide'); alert('Something went wrong. Please try again after page refresh') }) }, }); // Feeds MVC FeedsModel = Backbone.Model.extend({ defaults: function () { return { "feeds": {}, "feeds_changed": {} } }, initialize: function() { console.log('initialize Feeds Model', this); }, save: function( isNew ) { var self = this; var $params = { emulateJSON: true, data: { action: la_plugin_slug_down + '_save_sources_settings', model: this.toJSON(), security: vars.nonce } }; // filter and send only changed var feed; var feeds = $params.data.model.feeds; var feedsChanged = $params.data.model.feeds_changed; var feedsToSend = {}; var created, changed = {}, id; // created can be only single; for (feed in feedsChanged) { id = feedsChanged[feed]['id']; feedsToSend[id] = feeds[id]; if (feedsChanged[feed]['state'] === 'created') { created = feedsChanged[feed]; } } $params.data.model.feeds = feedsToSend; var newObj = {}; // move last to first if ( created ) { var array = $.map( _.clone( feeds ), function(value, index) { return [value]; }); if ( array.length > 1 ) { // move last to first array.unshift(array.pop()); for (var i = 0; i < array.length; ++i) { newObj[array[i]['id']] = array[i]; } self.set('feeds', newObj); } } /**/ return Backbone.sync( 'create', this, $params ).done(function( serverModel ){ if ( serverModel && serverModel.error ) { var promise = FlowFlow.popup( serverModel.error == 'not_allowed' ? 'Nay! You have no permissions to do this, please contact admin.' : 'Nay! Something went wrong, please contact support', false, 'alert'); FlowFlow.makeOverlayTo('hide'); return; } if (self.isNew() && serverModel && serverModel['id']) { self.set('id', serverModel['id']); } // todo in next updates update stream status when error resolved in feed var id; if (serverModel && serverModel['feeds']) { for (var feed in serverModel['feeds']) { id = serverModel['feeds'][feed]['id']; feeds[id] = serverModel['feeds'][feed]; } } }); }, urlRoot: vars.ajaxurl, url: function () { return this.urlRoot; } }); var feedsViewEvents = { "click .submit-button": "saveViaAjax", "keyup input, textarea": "catchEnter", "keyup #feeds-search": "filterFeedsByName", "click .button-add": "addFeedStepOne", "click .flaticon-tool_more": "toggleDropDown", "mouseleave .controls": "popupLeave", "click [data-action='filter']": "filterFeed", "click [data-action='cache']": "resetFeedCache", "click [data-action='check']": "checkStreams", "click .flaticon-tool_delete": "deleteFeed", "click .tr-error": "hideError", "click .button-go-back": "goBackToFeedChoice", "click .networks-list > li": "createFeedView", "click .popup .button-cancel-action, .popupclose": "closeFeedPopup", "click .ff-toggle-display": "toggleErrorFeeds", "click .ff-search-display": "toggleFilterInput", "keyup [data-action='add-filter']": "addFilter", "click [data-action='delete-filter']": "deleteFilter", "change .feed-view input": "updateModel", "change .feed-view select": "updateModel", "change td .switcher": "toggleFeed", "mouseenter .td-status": "showErrorIfPresent", "mouseleave .td-status": "hideError", } if ( plugin == 'insta_flow' ) { feedsViewEvents[ "click .flaticon-tool_edit, .td-feed, .td-info .highlight"] = "editFeed"; feedsViewEvents[ "click .button-add" ] = "createFeedView"; } else { feedsViewEvents[ "click .flaticon-tool_edit, .td-feed"] = "editFeed" } FeedsView = Backbone.View.extend({ renderedFirstTime: false, paginator: null, updateCycle: null, currentPage: parseInt( sessionStorage.getItem('ff_feeds_page') || 1) , $popup: null, $tab: null, feedChanged: false, showErrorFeedsOnly: false, errorsPresent: false, events: feedsViewEvents, initialize: function() { var self = this; this.model.view = this; this.$tab = $('#sources-tab'); this.$popup = this.$el.find('.popup'); this.render(); this.renderedFirstTime = true; // todo broadcast to boost element this.model.listenTo(this, 'changeFeedInModel', function (data){ console.log('changeFeedInModel event', data); var feeds = self.model.get('feeds'); var feed = feeds[data.id]; if (feed) { feed[data.name] = data.val; } }) }, render: function() { var self = this; var views = ''; var filterViews = ''; var feeds = this.model.get('feeds'); var savedPage; var changed = this.model.get( 'feeds_changed' ), prop, state; console.log( 'RENDER VIEWS', JSON.stringify( changed ) ); if ( ! this.renderedFirstTime ) { var data = { action: la_plugin_slug_down + '_sources', security: vars.nonce } var sourcesRequest = $.post( vars.ajaxurl, data ).done( function( res ) { var feeds; try { feeds = JSON.parse( res ); } catch ( e ) { console.log( 'Error parsing feeds JSON' ); return; } if ( _.isEmpty( feeds ) ) feeds = {}; // set feeds self.model.set( 'feeds', feeds ); _renderUI( feeds ); self.renderedFirstTime = true; if ( plugin == 'insta_flow' ) { $( document ).trigger( 'feeds-loaded', feeds ) return; } // if nothing changed it's first time or hard re-render console.log( 'renderBoostsUI first time') var boostsRequest = FlowFlow.renderBoostsUI( self.model ); $( document ).trigger( 'feeds-loaded', feeds ) $.when( boostsRequest ).then( function onSuccess ( boostsData ) { var boosts; try { boosts = JSON.parse( boostsData ); } catch ( e ) { console.log( 'boosts data parsing error', e ); return; } var plansRequest = $.get( 'https://api.flowflowapp.com/api/v1/flow-flow/ff?action=plans' + ( FlowFlow.subscription && FlowFlow.subscription.plan_id ? '&active_plan=' + FlowFlow.subscription.plan_id : '' ) ); $.when( plansRequest ).then( function onSuccess ( plansData ) { var plans = plansData; FlowFlow.renderBoostPricingTable( plans, boosts ); }, function onError () { console.log( 'plans UI error', arguments ); }) }, function onError () { console.log( 'boosts UI error', arguments ); }) }) // listen to nav events $( document ).on( 'list-nav', function ( e, data ) { console.log( data ); if ( data && data.page && data.page != self.currentPage ) { self.currentPage = data.page; self.renderFeedsList( changed, self.showErrorFeedsOnly ); self.savePage( data.page ); } }) } else { // render UI _renderUI( feeds, changed ); } function _renderUI( feeds, changed ) { if ( ! changed ) { // render all /* _.each( feeds, function ( feed ) { if (!feed.type) { return; } views += _.template(templates[feed.type + 'View'])({ uid: feed.id }); filterViews += _.template(templates['filterView'])({ uid: feed.id }); }); self.$el.find( '#feed-views' ).html( views ); self.$el.find( '#filter-views' ).html( filterViews ); */ } else { _.each( changed, function ( feed ) { // if created or changed, views already exist // if deleted if ( feed.state == 'deleted' ) { self.$el.find( '.feed-view[data-uid="' + feed.id + '"], .feed-view[data-filter-uid="' + feed.id + '"]' ).remove(); } }); } // set pages var index = 0; _.each( feeds, function ( feed, id, feedsHash ) { var page = Math.floor(index / 8); feed.page = page + 1; index++; }); self.renderFeedsList( changed, self.showErrorFeedsOnly ); self.setInputsValue(); // self.addFeedErrors(); if ( self.errorsPresent ) { self.$tab.addClass('errors-present'); } else { self.$tab.removeClass('errors-present'); } self.paginator = self.initPaginator( self.currentPage ); console.log('current page', self.currentPage ) if ( changed ) { if ( _.find( changed , function ( item ) { return item.state === 'created' } ) ) { self.currentPage = 1; } } if ( self.currentPage ) { self.paginator.paginate( typeof self.currentPage === 'number' ? self.currentPage : 1 ); // self.currentPage = false; } else { savedPage = parseInt( sessionStorage.getItem('ff_feeds_page') || 1) ; if ( savedPage > 1 ) { self.paginator.paginate( savedPage ); self.currentPage = savedPage; sessionStorage.setItem( 'ff_feeds_page', -1 ); // one time } } } }, startUpdateCycle: function ( id ) { var self = this; var updateCycle = setTimeout( function () { requestPosts( id ); }, 5000 ); function requestPosts ( id ) { var data = { action: la_plugin_slug_down + '_sources', security: vars.nonce, id: id } $.post( vars.ajaxurl, data ).done( function( feed ) { var serverFeed; // current client feeds var feeds = self.model.get( 'feeds' ); try { serverFeed = JSON.parse( feed ); } catch ( e ) { console.log( 'Error parsing serverFeed JSON' ); return; } // update feeds model individually feeds[ serverFeed.id ] = serverFeed; var changed = self.model.get( 'feeds_changed' ); var renderQueue = false; var state = changed[ id ] && changed[ id ][ 'state' ]; console.log( serverFeed ) console.log( JSON.stringify( changed ) ) if ( serverFeed [ 'status' ] == 1 || serverFeed [ 'status' ] == 0 ) { // feed resolved // delete specific feed from changed hash because its status resolved delete changed[ id ]; renderQueue = true; clearTimeout( updateCycle ); updateCycle = null; } else { // recursion recursion updateCycle = setTimeout( function () { requestPosts( id ); }, 10000 ); } if ( renderQueue ) { self.render(); console.log( 'renderBoostsUI changed ') FlowFlow.renderBoostsUI( self.model ); if ( serverFeed [ 'status' ] != 1 ) { console.log( 'feed status requestPosts', serverFeed [ 'status' ] ) } FlowFlow.showNotification( ( serverFeed [ 'status' ] == 1 ? 'Yay' : 'Something went wrong' ) + '! Feed "' + ( feeds[ id ].type === 'wordpress' ? ( feeds[ id ]['category-name'] || feeds[ id ]['wordpress-type'] ) : feeds[ id ][ 'content' ] ).toUpperCase() + '" ' + ( serverFeed [ 'status' ] == 1 ? 'was successfully' : 'was' ) + ' ' + ( state == 'deleted' ? 'deleted' : ( state == 'created' ? 'created' : 'updated' ) ) + ( serverFeed [ 'status' ] == 1 ? '' : ' with errors' ) + '' ); } } ) } }, renderFeedsList: function ( changed, errorsOnly, searchTerm ) { var feedsListStr = ''; var feeds = this.model.get('feeds'); var self = this; console.log('renderFeedsList changed', JSON.stringify( changed )); if ( ! _.isEmpty( feeds ) ) { _.each( feeds, function ( feed ) { var uid, enabled, status, lastUpdate; var $feed, $error, info, prop, ikey, ival, interval; var settings = {}; var type, icon; // if it's in changed hash we are waiting for status resolve var isChanged = changed && changed[ feed.id ]; // console.log( 'RENDER FEED', isChanged, feed ) if ( errorsOnly ) { if ( _.isArray( feed.errors ) && _.isEmpty( feed.errors) ) { return; } else { // pass error feed so it renders when only error feeds are requested } } else if ( searchTerm && searchTerm.length > 2 ) { // filter out all feeds that doesn't match search term if ( feed.content.indexOf( searchTerm ) == -1 ) { return; } } else if ( feed.page && feed.page != self.currentPage ) { // filter out all feeds not belonging to current page return } uid = feed.id; info = ''; if ( feed['boosted'] === 'yep' ) settings['boosted'] = feed['boosted']; if ( feed['type'] === 'rss' ) { settings['content'] = feed['channel-name'] || feed['content']; } else if ( feed['content'] ) { settings['content'] = feed['content']; } else { settings['content'] = feed['category-name'] || feed['wordpress-type']; } if ( feed['timeline-type'] ) settings['timeline-type'] = feed['timeline-type']; if ( feed['mod'] === 'yep' ) settings['mod'] = feed['mod']; settings['id'] = 'ID: ' + feed['id']; type = settings['timeline-type']; switch(type){ case 'user_timeline': icon = ''; break; case 'likes': case 'liked': icon = ''; break; case 'tag': icon = ''; break; case 'location': case 'coordinates': icon = ''; break; } // todo refactor this crap for ( prop in settings ) { ikey = capitaliseFirstLetter( prop.replace(' timeline', '').replace('_', ' ').replace('-', ' ').replace('timeline ', '') ); ival = stripslashes( settings[prop] ); if ( prop !== 'content' ) ival = capitaliseFirstLetter ( ival ); if ( prop === 'mod' ) ival = 'moderated'; if ( !ival ) continue; ival = ival.replace('_timeline', '').replace('http://', '').replace('https://', ''); if (ival.length > 20) { ival = ival.substring(0, 20) + '...'; } if ( ikey === 'Boosted' && ival === 'Yep' ) { info += ''; } else { if ( ikey.toLowerCase() == 'type' && plugin == "insta_flow" ) continue; info = info + '' + ( plugin == "insta_flow" && ikey.toLowerCase() === 'content' ? icon : '' ) + ival + ''; } } // if (feed.cache_lifetime == 5) { interval = 'Every 5 min'; } else if (feed.cache_lifetime == 30) { interval = 'Every 30 min'; } else if (feed.cache_lifetime == 60) { interval = 'Every hour'; } else if (feed.cache_lifetime == 120) { interval = 'Every 2 hours'; } else if (feed.cache_lifetime == 360) { interval = 'Every 6 hours'; } else if (feed.cache_lifetime == 1440) { interval = 'Once a day'; } else if (feed.cache_lifetime == 10080) { interval = 'Once a week'; } status = isChanged ? '' : ( (feed.status == 1) ? '' : '' ); lastUpdate = feed.last_update && feed.last_update !== 'N/A' ? (feed.last_update + ' (' + interval + ')') : 'N/A'; feedsListStr = feedsListStr + '' + '
  • Filter feed
  • Rebuild cache
  • Connections
' + ( plugin == 'insta_flow' ? '' : '' + /*capitaliseFirstLetter(feed.type) +*/ '' ) + '' + status + '
' + '' + info + '' + '' + lastUpdate + '' + '' + ''; if ( isChanged ) { console.log( feed ) } }); } else { feedsListStr = 'Please add at least one feed'; } this.$el.find('table.feeds-list tbody').html( feedsListStr ); // now render errors here this.addFeedErrors(); }, renderFilters: function( uid ){ var $excludeList = $('.filter-labels[data-type="exclude"]'); var $includeList = $('.filter-labels[data-type="include"]'); var exclude = this.model.get('feeds')[uid]['filter-by-words']; var include = this.model.get('feeds')[uid]['include']; if (exclude == undefined) exclude = ''; if (include == undefined) include = ''; var excludeArr = exclude == '' ? [] : exclude.split(','); var includeArr = include == '' ? [] : include.split(','); $excludeList.html(''); $includeList.html(''); excludeArr.forEach(function (item, i) { var $label = '
  • ' + item + '' + '
  • '; $excludeList.append($label); }) includeArr.forEach(function (item, i) { var $label = '
  • ' + item + '' + '
  • '; $includeList.append($label); }) }, addFilter: function (e) { if (e.which != 13) return; var $field = $(e.target); var id = $field.data('id'); var type = $field.data('type'); var content = $field.val(); var $list = $('[data-filter-uid="' + id + '"] .filter-labels[data-type="' + type + '"]'); var $holder = $('[data-filter-uid="' + id + '"] [data-type="filter-' + type + '-holder"]'); var filters = $holder.val() == "" ? [] : $holder.val().split(','); var $label = '
  • ' + content + '' + '
  • '; if(filters.indexOf(content) == -1){ filters.push(content); $list.append($label); $holder.val(filters.join(',')); if(type == 'exclude'){ this.model.attributes.feeds[id]['filter-by-words'] = filters.join(','); }else{ this.model.attributes.feeds[id]['include'] = filters.join(','); } $holder.trigger('change'); } $field.val(''); }, deleteFilter: function(e){ var $el = $(e.target); var id = $el.data('id') || $el.closest('.feed-view').data('filter-uid'); var type = $el.data('type'); var content = $(e.target).data('content'); var $label = $(e.target).closest('li'); var $holder = $('[data-filter-uid="' + id + '"] [data-type="filter-' + type + '-holder"]'); var filters = $holder.val() == "" ? [] : $holder.val().split(','); filters.forEach(function (item, i) { if(item == content.replace(/\\/g, '')) filters.splice(i, 1); }) $holder.val(filters.join(',')); if(type == 'exclude'){ this.model.attributes.feeds[id]['filter-by-words'] = filters.join(','); }else{ this.model.attributes.feeds[id]['include'] = filters.join(','); } console.log(this.model.attributes); $holder.trigger('change'); $label.remove(); }, toggleFeed: function (e) { var $t = $(e.target); var $row = $t.closest('[data-uid]'); var uid = $row.data('uid'); var type = $row.data( 'network' ); var $view = this.$popup.find('[data-uid="' + uid + '"]'), $filterView; if ( ! $view.length ) { $view = $( _.template(templates[ type + 'View'])({ uid: uid }) ); $filterView = $( _.template(templates['filterView'])({ uid: uid })); this.$el.find( '#feed-views' ).append( $view ); this.$el.find( '#filter-views' ).append( $filterView ); } // set values this.setInputsValue( uid ); var $channeling = $view.children('input:hidden'); //feeds[uid]['enabled'] = e.target.checked ? 'yep' : 'nope'; $channeling.val(e.target.checked ? 'yep' : 'nope').change(); this.saveViaAjax(); }, savePage: function ( page ) { sessionStorage.setItem('ff_feeds_page', page ); }, addFeedErrors: function () { var feeds = this.model.get('feeds'); var self = this; self.errorsPresent = false; _.each( feeds, function (feed) { var errors = feed.errors; var id = feed.id; var $feed, $error; // render only current page // CHANGED: render all because of filters // if ( feed.page && feed.page != self.currentPage ) return if ( errors ) { if (typeof errors !== 'object') { try { errors = JSON.parse(errors); } catch (e) { console.log( e.message ) } } if (!errors.length) return; $feed = self.$el.find('tr[data-uid="' + id + '"]'); $error = $(''); $error.data('err', errors); $feed.find('.td-status').html('').append($error).parent().addClass('tr-error'); self.errorsPresent = true; } }); }, setInputsValue: function ( feedId ) { var feeds = this.model.get('feeds'); var self = this; if (typeof feeds !== 'object') feeds = JSON.parse(feeds); _.each(feeds, function (feed) { var uid, name, $input, val; uid = feed.id; if ( feedId && feedId != uid ) return; for ( name in feed ) { if ( name === 'id' || name === 'type' ) continue; $input = self.$el.find('[name="' + uid + '-' + name + '"]'); if ($input.is(':radio') || $input.is(':checkbox')) { $input.each(function(){ var $t = $( this ); if ($t.val() == feed[name]) $t.prop('checked', true); }); } else { val = feed[name]; $input.val(val ? stripslashes(val.toString()) : ''); } } }); }, initPaginator: function () { var feeds = this.model.get('feeds'); var $list = this.$el.find('#feeds-list'); if ( $list.jPages()) $list.jPages('destroy'); // todo destroy? var feedsArr = Object.keys( feeds ); if (feedsArr.length > 8) { this.$el.addClass('jp-visible'); } else { this.$el.removeClass('jp-visible'); } return this.$el.find("div.holder").jPages({ startPage: this.currentPage, items: feedsArr, containerID : "feeds-list", previous : "←", next : "→", perPage : 8, delay : 0, animation : 'yes' }).data('jPages'); }, hideError: function (e) { var $rel = $(e.relatedTarget); if ($rel.is('#error-popup')) return; FlowFlow.$errorPopup.removeClass('visible'); }, addFeedStepOne: function(e){ this.$popup.removeClass('add-feed-step-2').addClass('add-feed-step-1'); FlowFlow.checkScrollbar(); FlowFlow.setScrollbar(); FlowFlow.$html.addClass('popup_visible'); this.$popup.on('click', this.popupClick); }, editFeed: function (e) { var $t = $(e.target); var uid = $t.closest('[data-uid]').data('uid'); var $popup = this.$popup; 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[feed.type + 'View'])({ uid: uid }) ); $filterView = $( _.template(templates['filterView'])({ uid: uid })); this.$el.find( '#feed-views' ).append( $view ); this.$el.find( '#filter-views' ).append( $filterView ); } // set values this.setInputsValue( uid ); // popup scroll $popup.find('.feed-view-visible').removeClass('feed-view-visible'); $popup.find('.feed-view[data-uid=' + uid + ']').addClass('feed-view-visible'); $popup.addClass('add-feed-step-2'); $popup.find('.feed-popup-controls').hide(); if (feed && feed.enabled === 'nope') { $popup.find('.feed-popup-controls.enable').show(); } else { $popup.find('.feed-popup-controls.edit').show(); } FlowFlow.checkScrollbar(); FlowFlow.setScrollbar(); FlowFlow.$html.addClass( 'popup_visible' ); $popup.on( 'click', this.popupClick ); // add notice about auth var notice = '
    Access token is required for %network% feeds, please add on AUTH tab
    '; if ( ! $popup.find('.ff-notice').length ) { switch ( network ) { case 'facebook': { if ( ! $( '#facebook_access_token' ).val() ) { $view.addClass( 'auth-required' ).find( 'h1' ).after( notice.replace( '%network%', 'Facebook' ) ) } break; } case 'twitter': { if ( ! $( '#oauth_access_token' ).val() ) { $view.addClass( 'auth-required' ).find( 'h1' ).after( notice.replace( '%network%', 'Twitter' ) ) } break; } case 'youtube': { if ( ! $( '#google_api_key' ).val() ) { $view.addClass( 'auth-required' ).find( 'h1' ).after( notice.replace( '%network%', 'Youtube' ) ) } break; } case 'instagram': { if ( ! $( '#facebook_access_token' ).val() ) { $view.addClass( 'auth-required' ).find( '> h1' ).after( '
    If you plan to use official Instagram API by Facebook, access token is required, please add on AUTH tab
    ' ) } break; } } } // store all input values to revert them on popup closing without save var initialSettings = {}; $view.find( 'input, select' ).each( function ( i, el ) { var $el = $( this ); 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'; } else { initialSettings[ this.name ] = 'nope'; } } else { initialSettings[ this.name ] = $.trim( $el.val() ); } }) $view.data( 'initialSettings', initialSettings ); }, popupClick: function (e) { var $t = $(e.target) 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 $t = $(e.target); 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'])({ uid: uid }) ); $filterView = $( _.template(templates['filterView'])({ uid: uid })); 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.setScrollbar(); FlowFlow.$html.addClass('popup_visible'); $popup.on('click', this.popupClick); this.renderFilters(uid) }, resetFeedCache: function (e) { var $t = $(e.target); 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'; this.saveViaAjax(); }, checkStreams: function (e) { var $t = $(e.target); var str = ''; var streams = streamRowModels.models; var streamFeeds, found; 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) { return feed.id == id }) if ( found ) { if ( str ) str += '
    ' str += streams[i].get( 'name' ) + ' #' + streams[i].id // reset found = ''; } } if ( ! str ) str = 'not yet connected to any streams'; FlowFlow.popup( 'Feed is connected to:
    ' + str , 'neutral', 'alert'); }, toggleDropDown: function (e) { var self = this; var $t = $(e.target); var $cont = $t.closest('td'); var isOpened = $cont.data('popup') === 'opened'; $('td.open').removeClass('open').data('popup', ''); if ( isOpened ) { $cont.removeClass('open'); $cont.data('popup', ''); //FlowFlow.$body.off('click', this.popupMoreClick); } else { setTimeout(function () { $cont.addClass('open'); $cont.data('popup', 'opened'); //FlowFlow.$body.on('click', self.popupMoreClick); }, 0) } }, popupMoreClick: function (e) { var $t = $(e.target) 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 ); var self = this; 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; } else { self.currentPage = self.paginator._currentPageNum; } delete modelFeeds[uid]; self.saveViaAjax(); }, function fail () {}) }, closeFeedPopup: function (e) { FlowFlow.$html.removeClass('popup_visible'); FlowFlow.resetScrollbar(); var $popup = this.$popup; var id; 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 ) { id = $fresh.data('uid'); delete changed[ id ]; delete feeds[ id ]; $fresh.remove(); if (this.model.get('feeds').length) this.model.get('feeds').pop(); } setTimeout(function () { $popup.removeClass('add-feed-step-1 add-feed-step-2'); }, 400); this.feedChanged = false; // restore settings and revert model var initialSettings = $view.data( 'initialSettings' ) || {}; $view.find( 'input, select' ).each( function ( i, el ) { var $el = $( this ); 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(); } else { $el.prop( 'checked', false ).change(); } } else { $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]; } else { return; } var messages = errorData['message']; if (messages) { if ($.isArray(messages)) { for (var i = 0, len = messages.length; i < len;i++) { if (i > 0) errorStr += '
    '; errorStr += messages[i]['msg']; } } else if (typeof messages === 'object') { if (messages['msg']) { errorStr += messages['msg']; } else { try { errorStr += JSON.stringify(messages) } catch (e) { errorStr += 'Unknown error. Please ask for support here' } } } else if (typeof messages === 'string') { errorStr += messages } } else if (errorData['msg']) { errorStr += errorData['msg']; } else if ($.isArray(errorData)) { if (errorData[0].msg) { errorStr += errorData[0].msg; } else { try { errorStr += JSON.stringify(errorData[0]) } catch (e) { errorStr += 'Unknown error. Please submit ticket and send access' } } } else { try { errorStr += JSON.stringify(errorData) } catch (e) { errorStr += 'Unknown error. Please submit ticket 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 more info' } else if (errorStr.toLowerCase().indexOf('bad request') + 1) { errorStr += '

    Check info' } if ( errorStr) { } FlowFlow.$errorPopup.html('

    App received next error message for this feed:

    ' + errorStr + '

    ') }, goBackToFeedChoice: function (e) { var $t = $(e.target); 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(); if (this.feedChanged) { delete feeds[uid]; 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; // Prepare popup window if ( plugin == 'insta_flow' ) { this.addFeedStepOne(e); } $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(); // add notice about auth var notice = '
    Access token is required for %network% feeds, please add on AUTH tab
    '; if ( ! $popup.find('.ff-notice').length ) { switch ( network ) { case 'facebook': { if ( ! $( '#facebook_access_token' ).val() ) { $view.addClass( 'auth-required' ).find( 'h1' ).after( notice.replace( '%network%', 'Facebook' ) ) } break; } case 'twitter': { if ( ! $( '#oauth_access_token' ).val() ) { $view.addClass( 'auth-required' ).find( 'h1' ).after( notice.replace( '%network%', 'Twitter' ) ) } break; } case 'youtube': { if ( ! $( '#google_api_key' ).val() ) { $view.addClass( 'auth-required' ).find( 'h1' ).after( notice.replace( '%network%', 'Youtube' ) ) } break; } case 'instagram': { if ( ! $( '#facebook_access_token' ).val() ) { $view.addClass( 'auth-required' ).find( 'h1' ).after( '
    If you plan to use official Instagram API by Facebook, access token is required, please add on AUTH tab
    ' ) } break; } } } this.feedChanged = true; 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 val = $t.val(); var name = $t.attr('name'); var $group; 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 self = this; if ( !name ) { return; } var streams, feeds, found; var currProp; var cancelUpdatingModel = false; var cancelBoostChange = false; currProp = name.replace(id + '-', ''); var boostInputChecked; 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 } else { // 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 ) { return feed.id == id }) if ( found ) { // this feed in // show first found 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; cancelBoostChange = true; break; } } // 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'; if ($t.is(':radio')) { val = $t.is(':checked') ? ($t.attr('value') || 'yep') : 'nope' } if ($t.is(':checkbox')) { $group = self.$el.find('[name="' + name + '"]'); if ($group.length > 1) { // group val = {}; $group.each(function () { var cbVal = this.value; if (this.checked) val[cbVal] = 'yep'; }); } else { val = $t.is(':checked') ? 'yep' : 'nope' } } self.trigger('changeFeedInModel', {name: name.replace(id + '-', ''), val: val, id: id }) } }, addFeedInModel: function ( $view, id, errors ) { var obj = {}; var modelFeeds = this.model.get('feeds'), freshFeeds; $view.find(':input').each(function (i, el) { var $t = $(this); var name = $t.attr('name'); name = name.replace(/\w\w\d+?\-/, ''); if ($t.is(':radio')) { if ($t.is(':checked')) { obj[name] = $t.val(); } } else if ($t.is(':checkbox')) { if ($t.is(':checked')) { obj[name] = 'yep'; } else { obj[name] = 'nope'; } } else { obj[name] = $.trim($t.val()); } }); obj['id'] = id; 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; if (modelFeeds) { modelFeeds[id] = obj; } else { freshFeeds = {}; freshFeeds[id] = obj; 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 }, 300); return false; } return true; }, 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'); } } else { // $list.addClass('show-only-errors'); if ( ! $list.has( '.feed-row:visible' ).length ) { // no errors $list.append( 'Yay! No error feeds.' ); } $t.html('Show all feeds'); this.showErrorFeedsOnly = true; this.renderFeedsList( changed, this.showErrorFeedsOnly ); this.$el.removeClass('jp-visible'); } return false; }, toggleFilterInput: function ( e ) { var $hidden = $('.search-container:hidden'); if ( $hidden.length ) { $hidden.show().find( 'input' ).focus() } else { $('.search-container').hide() } }, filterFeedsByName: function ( e ) { var feeds = this.model.get('feeds'); var changed = this.model.get('feeds_changed'); var feedsArr; var $t = $(e.target); var val = $t.val(); if ( val && val.length > 2 ) { // feedsArr = Object.keys( feeds ); this.renderFeedsList( changed, this.showErrorFeedsOnly, val ); this.$el.removeClass('jp-visible'); } else { 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 self = this; var $t = $( e ? e.target : '' ); var model = this.model; var isNew = model.isNew(); var uid; var feeds = model.get( 'feeds' ); var changed = model.get( 'feeds_changed' ); var $enabled; var id, state; // validation in popup if ( $t.is( '[id^=feed-sbmt]' ) ) { if ( !this.validateFeedCfg( $t ) ) return; } // store page if ( !dontSavePage ) this.currentPage = this.paginator._currentPageNum; uid = this.$el.find('.feed-view-visible').data( 'uid' ); // catching and broadcasting event from toggler in list // what's this???? 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 self.render(); saveFeedsRequest.done( function ( response ) { var feeds = model.get( 'feeds' ); // because model save rewrites feeds property var changed = model.get( 'feeds_changed' ); var id; for ( id in 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.currentPage = 1; 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 delete changed[ id ]; } } else { // success 1 or error 0 // delete specific feed from changed hash because its status resolved delete changed[ id ]; // 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 "' + ( feeds[ id ].type === 'wordpress' ? ( feeds[ id ]['category-name'] || feeds[ id ]['wordpress-type'] ).toUpperCase() : feeds[ id ][ 'content' ] ).toUpperCase() + '" ' + ( response.feeds[ id ] [ 'status' ] == 1 ? 'was successfully' : 'was' ) + ' ' + ( state == 'deleted' ? 'deleted' : ( state == 'created' ? 'created' : 'updated' ) ) + ( response.feeds[ id ] [ 'status' ] == 1 ? '' : ' with errors' ) + '' ); self.render(); FlowFlow.renderBoostsUI( self.model ); } } else { // deleted feed if ( changed [ id ]) { FlowFlow.showNotification( 'Feed "' + ( changed[ id ].type === 'wordpress' ? ( changed[ id ]['category-name'] || changed[ id ]['wordpress-type'] ).toUpperCase() : ( changed[ id ][ 'content' ] || '' )).toUpperCase() + '"' + ' was successfully deleted' ); } // delete specific feed from changed hash because feed itself was deleted delete feeds[ id ]; delete changed[ id ]; self.render(); } } }) .fail( function ( res ) { FlowFlow.showNotification( ( res && res.responseJSON && res.responseJSON.error ? res.responseJSON.error : 'Something went wrong. Please try again or ask support.' ) ); self.render(); } ) // notification banner for ( id in changed ) { state = changed[ id ][ 'state' ]; if ( state !== 'deleted' ) { FlowFlow.showNotification( 'Getting data for "' + ( feeds[ id ].type === 'wordpress' ? ( feeds[ id ]['category-name'] || feeds[ id ]['wordpress-type'] ) : feeds[ id ][ 'content' ] ).toUpperCase() + '" in background, you\'ll be notified when resolved' ); } } return saveFeedsRequest; } }); // 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({ initialize: function() { if (storage[this.id]) { this.set('collapsed', storage[this.id]['collapsed']); } else { 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)) } }) }, defaults : function () { return { 'collapsed' : {} } } }); var SectionsView = Backbone.View.extend({ initialize: function() { this.model.view = this; this.$sections = this.$el.find('> .section'); this.render(); }, render: function(){ var self = this; // add class if collapsed self.$sections.each(function(){ var $t = $(this); var index = self.$sections.index(this); $t.append(''); if (self.model.get('collapsed')[index]) $t.addClass('section--collapsed'); }) }, events: { "click .section__toggle": "toggle" }, toggle: function (e) { console.log('Voi la!'); 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')); if (isCollapsed) { $section.removeClass('section--collapsed'); collapsed[index] = 0; } else { $section.addClass('section--collapsed'); collapsed[index] = 1; } this.model.set('collapsed', collapsed); $(document).trigger('section-toggle', this.model.get('id')); }, $sections: null }); var globalDefaults = { }; function init (opts) { var settings = $.extend(globalDefaults, opts); var model = new SectionsModel(settings); var view = new SectionsView({model: model, el: settings.$element}); return view; } return { init: init }; })( jQuery ) // global shortcuts window.sectionExpandCollapse = sectionExpandCollapse; return { 'init' : function () { var self = this; var controller = FlowFlow.init.apply( FlowFlow, arguments); self.init = function(){return self} return self; }, 'FlowFlow' : FlowFlow, 'Model' : { 'StreamRow' : { 'collection' : streamRowModels, 'Class' : StreamRowModel }, 'Stream' : { 'collection' : streamModels, 'Class' : StreamModel }, 'Feeds' : { 'collection' : feedsModel, 'Class' : FeedsModel } }, 'View' : { 'StreamRow' : { 'Class' : StreamRowView }, 'Stream' : { 'Class' : StreamView }, 'Feeds' : { 'view' : feedsView } }, sectionExpandCollapse : sectionExpandCollapse, popup: FlowFlow.popup } })(jQuery) 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) { if ( !str ) return; str = str.replace(/\\'/g, '\''); str = str.replace(/\\"/g, '"'); str = str.replace(/\\0/g, '\0'); str = str.replace(/\\\\/g, '\\'); return str; } function stripSlashesObjProps (obj) { for (var prop in obj) { if (typeof obj[prop] === 'string') obj[prop] = stripslashes(obj[prop]); } return obj } function getUrlVars() { var vars = {}; var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) { vars[key] = value; }); return vars; }