<commit_before>// -------------------------------------------------------------------------- \\
// File: NotificationView.js                                                  \\
// Module: PanelViews                                                         \\
// Requires: Core, Foundation, DOM, View                                      \\
// Author: Neil Jenkins                                                       \\
// License: © 2010–2013 Opera Software ASA. All rights reserved.              \\
// -------------------------------------------------------------------------- \\

"use strict";

( function ( NS, undefined ) {

var hiddenLayout = {
    top: 0
};

var NotificationView = NS.Class({

    Extends: NS.View,

    Mixin: NS.AnimatableView,

    animateLayerDuration: 200,

    className: function () {
        return 'NotificationView' +
            ( this.get( 'userMayClose' ) ? ' closable' : '' );
    }.property( 'userMayClose' ),

    destroyOnClose: true,

    isShowing: false,
    userMayClose: true,
    precedence: 0,
    timeout: 0,

    hiddenLayout: hiddenLayout,
    visibleLayout: function () {
        return {
            top: this.get( 'pxHeight' )
        };
    }.property(),

    text: '',
    html: '',

    _timer: null,

    show: function ( notificationsContainer ) {
        if ( this.get( 'isShowing' ) ) {
            this.hide( true );
        }
        notificationsContainer.insertView( this );
        this._show();
        var timeout = this.get( 'timeout' );
        if ( timeout ) {
            this._timer =
                NS.RunLoop.invokeAfterDelay( this.hide, timeout, this );
        }
        return this.set( 'isShowing', true );
    },

    _show: function () {
        if ( this.get( 'isShowing' ) ) {
            this.set( 'layout',  this.get( 'visibleLayout' ) );
        }
    }.later(),

    hide: function ( doNotAnimate ) {
        if ( this.get( 'isShowing' ) ) {
            // If we don't have a layerAnimation object yet, we can't have
            // animated in, so just detach immediately. Otherwise, stop the
            // current layer animation, then animate out.
            var layerAnimation = NS.meta( this ).cache.layerAnimation,
                hiddenLayout = this.get( 'hiddenLayout' );
            if ( !doNotAnimate && layerAnimation &&
                    this.get( 'layout' ) !== hiddenLayout ) {
                layerAnimation.stop();
                this.set( 'layout', hiddenLayout );
            } else {
                this.detach();
            }
            if ( this._timer ) {
                NS.RunLoop.cancel( this._timer );
                this._timer = null;
            }
            this.set( 'isShowing', false );
        }
        return this;
    },

    didAnimate: function () {
        this.increment( 'animating', -1 );
        if ( !this.get( 'animating' ) && !this.get( 'isShowing' ) ) {
            this.detach();
        }
    },

    detach: function () {
        this.get( 'parentView' )
            .removeView( this )
            .notificationDidHide( this );

        if ( this.get( 'destroyOnClose' ) ) {
            this.destroy();
        }
        return this;
    },

    layout: hiddenLayout,

    draw: function ( layer ) {
        this.drawNotification( layer );
        if ( this.get( 'userMayClose' ) ) {
            var el = NS.Element.create;
            layer.appendChild(
                this._close = el( 'a.close', [
                    el( 'i.close-image' ),
                    NS.loc( 'Close' )
                ])
            );
        }
    },

    drawNotification: function ( layer ) {
        var text = this.get( 'text' ),
            html = this.get( 'html' );
        if ( text || html ) {
            layer.appendChild(
                this._textNode = NS.Element.create( 'span', {
                    text: text || undefined,
                    html: text ? undefined : html
                })
            );
        }
    },

    notificationNeedsRedraw: function ( self, property, oldValue ) {
       return this.propertyNeedsRedraw( self, property, oldValue );
    }.observes( 'text', 'html' ),

    redrawText: function () {
        this.redrawHTML();
    },
    redrawHTML: function () {
        var text = this.get( 'text' ),
            html = this.get( 'html' );
        this._textNode.set( html ? 'html' : 'text', html || text );
    },

    hideOnClick: function ( event ) {
        if ( this.get( 'userMayClose' ) &&
                NS.Element.contains( this._close, event.target ) ) {
            event.preventDefault();
            this.hide();
        }
    }.on( 'click' )
});

var NotificationContainerView = NS.Class({

    Extends: NS.View,

    className: 'NotificationContainerView',

    showing: null,

    init: function ( mixin ) {
        this._waiting = [];
        NotificationContainerView.parent.init.call( this, mixin );
    },

    positioning: 'absolute',

    willShow: function ( notification ) {
        var showing = this.get( 'showing' );
        if ( showing ) {
            if ( notification !== showing &&
                    notification.get( 'precedence' ) >=
                    showing.get( 'precedence' ) ) {
                this._waiting.push( notification );
                this.hide( showing );
            }
            return false;
        }
        return true;
    },

    show: function ( notification ) {
        if ( this.willShow( notification ) ) {
            this.set( 'showing', notification );
            notification.show( this );
        }
    },

    hide: function ( notification ) {
        var showing = this.get( 'showing' );
        if ( showing && ( !notification || notification === showing ) ) {
            showing.hide();
        }
    },

    notificationDidHide: function ( notification ) {
        this.set( 'showing', null );
        var nextNotification = this._waiting.pop();
        if ( nextNotification ) {
            this.show( nextNotification );
        }
    }
});

NS.NotificationContainerView = NotificationContainerView;
NS.NotificationView = NotificationView;

}( this.O ) );
<commit_msg>Change default position of NotificationView.<commit_after>// -------------------------------------------------------------------------- \\
// File: NotificationView.js                                                  \\
// Module: PanelViews                                                         \\
// Requires: Core, Foundation, DOM, View                                      \\
// Author: Neil Jenkins                                                       \\
// License: © 2010–2013 Opera Software ASA. All rights reserved.              \\
// -------------------------------------------------------------------------- \\

"use strict";

( function ( NS, undefined ) {

var hiddenLayout = {
    top: 0
};

var NotificationView = NS.Class({

    Extends: NS.View,

    Mixin: NS.AnimatableView,

    animateLayerDuration: 200,

    className: function () {
        return 'NotificationView' +
            ( this.get( 'userMayClose' ) ? ' closable' : '' );
    }.property( 'userMayClose' ),

    destroyOnClose: true,

    isShowing: false,
    userMayClose: true,
    precedence: 0,
    timeout: 0,

    hiddenLayout: hiddenLayout,
    visibleLayout: function () {
        return {
            top: -this.get( 'pxHeight' ) - 10
        };
    }.property(),

    text: '',
    html: '',

    _timer: null,

    show: function ( notificationsContainer ) {
        if ( this.get( 'isShowing' ) ) {
            this.hide( true );
        }
        notificationsContainer.insertView( this );
        this._show();
        var timeout = this.get( 'timeout' );
        if ( timeout ) {
            this._timer =
                NS.RunLoop.invokeAfterDelay( this.hide, timeout, this );
        }
        return this.set( 'isShowing', true );
    },

    _show: function () {
        if ( this.get( 'isShowing' ) ) {
            this.set( 'layout',  this.get( 'visibleLayout' ) );
        }
    }.later(),

    hide: function ( doNotAnimate ) {
        if ( this.get( 'isShowing' ) ) {
            // If we don't have a layerAnimation object yet, we can't have
            // animated in, so just detach immediately. Otherwise, stop the
            // current layer animation, then animate out.
            var layerAnimation = NS.meta( this ).cache.layerAnimation,
                hiddenLayout = this.get( 'hiddenLayout' );
            if ( !doNotAnimate && layerAnimation &&
                    this.get( 'layout' ) !== hiddenLayout ) {
                layerAnimation.stop();
                this.set( 'layout', hiddenLayout );
            } else {
                this.detach();
            }
            if ( this._timer ) {
                NS.RunLoop.cancel( this._timer );
                this._timer = null;
            }
            this.set( 'isShowing', false );
        }
        return this;
    },

    didAnimate: function () {
        this.increment( 'animating', -1 );
        if ( !this.get( 'animating' ) && !this.get( 'isShowing' ) ) {
            this.detach();
        }
    },

    detach: function () {
        this.get( 'parentView' )
            .removeView( this )
            .notificationDidHide( this );

        if ( this.get( 'destroyOnClose' ) ) {
            this.destroy();
        }
        return this;
    },

    layout: hiddenLayout,

    draw: function ( layer ) {
        this.drawNotification( layer );
        if ( this.get( 'userMayClose' ) ) {
            var el = NS.Element.create;
            layer.appendChild(
                this._close = el( 'a.close', [
                    el( 'i.icon.icon-close' ),
                    NS.loc( 'Close' )
                ])
            );
        }
    },

    drawNotification: function ( layer ) {
        var text = this.get( 'text' ),
            html = this.get( 'html' );
        if ( text || html ) {
            layer.appendChild(
                this._textNode = NS.Element.create( 'span', {
                    text: text || undefined,
                    html: text ? undefined : html
                })
            );
        }
    },

    notificationNeedsRedraw: function ( self, property, oldValue ) {
       return this.propertyNeedsRedraw( self, property, oldValue );
    }.observes( 'text', 'html' ),

    redrawText: function () {
        this.redrawHTML();
    },
    redrawHTML: function () {
        var text = this.get( 'text' ),
            html = this.get( 'html' );
        this._textNode.set( html ? 'html' : 'text', html || text );
    },

    hideOnClick: function ( event ) {
        if ( this.get( 'userMayClose' ) &&
                NS.Element.contains( this._close, event.target ) ) {
            event.preventDefault();
            this.hide();
        }
    }.on( 'click' )
});

var NotificationContainerView = NS.Class({

    Extends: NS.View,

    className: 'NotificationContainerView',

    showing: null,

    init: function ( mixin ) {
        this._waiting = [];
        NotificationContainerView.parent.init.call( this, mixin );
    },

    positioning: 'absolute',

    willShow: function ( notification ) {
        var showing = this.get( 'showing' );
        if ( showing ) {
            if ( notification !== showing &&
                    notification.get( 'precedence' ) >=
                    showing.get( 'precedence' ) ) {
                this._waiting.push( notification );
                this.hide( showing );
            }
            return false;
        }
        return true;
    },

    show: function ( notification ) {
        if ( this.willShow( notification ) ) {
            this.set( 'showing', notification );
            notification.show( this );
        }
    },

    hide: function ( notification ) {
        var showing = this.get( 'showing' );
        if ( showing && ( !notification || notification === showing ) ) {
            showing.hide();
        }
    },

    notificationDidHide: function ( notification ) {
        this.set( 'showing', null );
        var nextNotification = this._waiting.pop();
        if ( nextNotification ) {
            this.show( nextNotification );
        }
    }
});

NS.NotificationContainerView = NotificationContainerView;
NS.NotificationView = NotificationView;

}( this.O ) );
<|endoftext|>


<commit_before>module.exports = {

//---------------------------------------------------------------------
// Action Name
//
// This is the name of the action displayed in the editor.
//---------------------------------------------------------------------

name: "Mod Information",

//---------------------------------------------------------------------
// Action Section
//
// This is the section the action will fall into.
//---------------------------------------------------------------------

section: "#Mod Information",

//---------------------------------------------------------------------
// Action Subtitle
//
// This function generates the subtitle displayed next to the name.
//---------------------------------------------------------------------

subtitle: function(data) {
	return `Does nothing - Click "Edit" for more information`;
},

//---------------------------------------------------------------------
	 // DBM Mods Manager Variables (Optional but nice to have!)
	 //
	 // These are variables that DBM Mods Manager uses to show information
	 // about the mods for people to see in the list.
	 //---------------------------------------------------------------------

	 // Who made the mod (If not set, defaults to "DBM Mods")
	 author: "Lasse",

	 // The version of the mod (Defaults to 1.0.0)
	 version: "1.8.4",

	 // A short description to show on the mod line for this mod (Must be on a single line)
	 short_description: "Information about the Mod Collection.",

	 // If it depends on any other mods by name, ex: WrexMODS if the mod uses something from WrexMods

	 //---------------------------------------------------------------------

//---------------------------------------------------------------------
// Action Storage Function
//
// Stores the relevant variable info for the editor.
//---------------------------------------------------------------------

//variableStorage: function(data, varType) {},

//---------------------------------------------------------------------
// Action Fields
//
// These are the fields for the action. These fields are customized
// by creating elements with corresponding IDs in the HTML. These
// are also the names of the fields stored in the action's JSON data.
//---------------------------------------------------------------------

fields: [],

//---------------------------------------------------------------------
// Command HTML
//
// This function returns a string containing the HTML used for
// editting actions.
//
// The "isEvent" parameter will be true if this action is being used
// for an event. Due to their nature, events lack certain information,
// so edit the HTML to reflect this.
//
// The "data" parameter stores constants for select elements to use.
// Each is an array: index 0 for commands, index 1 for events.
// The names are: sendTargets, members, roles, channels,
//                messages, servers, variables
//---------------------------------------------------------------------

html: function(isEvent, data) {
	return `

		Welcome!

		Thank you for using the DBM Mod Collection!

		If you want to tell us something, join the Discord Guild below.

		And if something doesn't work feel free to create an issue on GitHub

		or open #support and describe your problem.

		Discord:

		Join the Discord Guild to stay updated and be able to suggest things.

		Join now

		Your version:

		1.8.5 - beta

		Changelog:

		Click here to see the latest updates:

		Open Changelog

		GitHub:

		Visit us on GitHub! The whole mod collection is on GitHub

		and everyone is invited to join us developing new mods!

		Copy and paste the link to view the site in your browser.

		https://github.com/Discord-Bot-Maker-Mods/DBM-Mods/

`
},

//---------------------------------------------------------------------
// Action Editor Init Code
//
// When the HTML is first applied to the action editor, this code
// is also run. This helps add modifications or setup reactionary
// functions for the DOM elements.
//---------------------------------------------------------------------

init: function() {},

//---------------------------------------------------------------------
// Action Bot Function
//
// This is the function for the action within the Bot's Action class.
// Keep in mind event calls won't have access to the "msg" parameter,
// so be sure to provide checks for variable existance.
//---------------------------------------------------------------------

action: function(cache) {
	this.callNextAction(cache);
},

//---------------------------------------------------------------------
// Action Bot Mod
//
// Upon initialization of the bot, this code is run. Using the bot's
// DBM namespace, one can add/modify existing functions if necessary.
// In order to reduce conflictions between mods, be sure to alias
// functions you wish to overwrite.
//---------------------------------------------------------------------

mod: function(DBM) {
}

}; // End of module
<commit_msg>[No DL - Info Commit] Added auto-generating table of mods to the Mod Information according to the installed mods. Dont forget_MOD.js at the end of your mods, it relies on it! More built in features soon, including auto-updating built right into it. Updated version in Mod Information to reflect the beta<commit_after>module.exports = {

//---------------------------------------------------------------------
// Action Name
//
// This is the name of the action displayed in the editor.
//---------------------------------------------------------------------

name: "Mod Information",

//---------------------------------------------------------------------
// Action Section
//
// This is the section the action will fall into.
//---------------------------------------------------------------------

section: "#Mod Information",

//---------------------------------------------------------------------
// Action Subtitle
//
// This function generates the subtitle displayed next to the name.
//---------------------------------------------------------------------

subtitle: function(data) {
	return `Does nothing - Click "Edit" for more information`;
},

//---------------------------------------------------------------------
// DBM Mods Manager Variables (Optional but nice to have!)
//
// These are variables that DBM Mods Manager uses to show information
// about the mods for people to see in the list.
//---------------------------------------------------------------------

// Who made the mod (If not set, defaults to "DBM Mods")
author: "Lasse",

// The version of the mod (Defaults to 1.0.0)
version: "1.8.5 - beta",

// A short description to show on the mod line for this mod.
short_description: "Information about the Mod Collection.",

// If it depends on any other mods by name, ex: WrexMODS if the mod uses something from WrexMods

//---------------------------------------------------------------------

//---------------------------------------------------------------------
// Action Storage Function
//
// Stores the relevant variable info for the editor.
//---------------------------------------------------------------------

variableStorage: function(data, varType) {},

//---------------------------------------------------------------------
// Action Fields
//
// These are the fields for the action. These fields are customized
// by creating elements with corresponding IDs in the HTML. These
// are also the names of the fields stored in the action's JSON data.
//---------------------------------------------------------------------

fields: ["mods"],

//---------------------------------------------------------------------
// Command HTML
//
// This function returns a string containing the HTML used for
// editting actions.
//
// The "isEvent" parameter will be true if this action is being used
// for an event. Due to their nature, events lack certain information,
// so edit the HTML to reflect this.
//
// The "data" parameter stores constants for select elements to use.
// Each is an array: index 0 for commands, index 1 for events.
// The names are: sendTargets, members, roles, channels,
//                messages, servers, variables
//---------------------------------------------------------------------

html: function(isEvent, data) {
	return `

		Welcome!

		Thank you for using the DBM Mod Collection!

		If you want to tell us something, join the Discord Guild below.

		And if something doesn't work feel free to create an issue on GitHub

		or open #support and describe your problem.

		Discord:

		Join the Discord Guild to stay updated and be able to suggest things.

		Join now

		Your version:

		${this.version}

		Changelog:

		Click here to see the latest updates:

		Open Changelog

		GitHub:

		Visit us on GitHub! The whole mod collection is on GitHub

		and everyone is invited to join us developing new mods!

		Copy and paste the link to view the site in your browser.

		https://github.com/Discord-Bot-Maker-Mods/DBM-Mods/

		Current List of Mods

					Name
					Section
					Author

`
},

//---------------------------------------------------------------------
// Action Editor Init Code
//
// When the HTML is first applied to the action editor, this code
// is also run. This helps add modifications or setup reactionary
// functions for the DOM elements.
//---------------------------------------------------------------------

init: function() {
	const {glob, document} = this;

	var path = require("path")

	try {

		var mods = document.getElementById("mods");

		require("fs").readdirSync(__dirname).forEach(function(file) {
			if(file.match(/MOD.js/i)) {
				var action = require(path.join(__dirname, file));
				if(action.name && action.action !== null) {

					const tr = document.createElement('tr')
					tr.setAttribute('class', 'table-dark')

					const name = document.createElement('td')
					const headerText = document.createElement("b")
					headerText.innerHTML = action.name
					name.appendChild(headerText)

					name.setAttribute('scope', 'row')
					tr.appendChild(name)

					const section = document.createElement('td')
					section.appendChild(document.createTextNode(action.section))
					tr.appendChild(section)

					const author = document.createElement('td')
					author.appendChild(document.createTextNode(action.author ? action.author : "DBM"))
					tr.appendChild(author)
					mods.appendChild(tr);
				}
			}
		});
	} catch (error) {
		// write any init errors to errors.txt in dbm's main directory
		require("fs").appendFile("errors.txt", error.stack ? error.stack : error + "\r\n");
	}
},

//---------------------------------------------------------------------
// Action Bot Function
//
// This is the function for the action within the Bot's Action class.
// Keep in mind event calls won't have access to the "msg" parameter,
// so be sure to provide checks for variable existance.
//---------------------------------------------------------------------

action: function(cache) {
	this.callNextAction(cache);
},

//---------------------------------------------------------------------
// Action Bot Mod
//
// Upon initialization of the bot, this code is run. Using the bot's
// DBM namespace, one can add/modify existing functions if necessary.
// In order to reduce conflictions between mods, be sure to alias
// functions you wish to overwrite.
//---------------------------------------------------------------------

mod: function(DBM) {
}

}; // End of module
<|endoftext|>


<commit_before>var locastyle = locastyle || {};

locastyle.popover = (function() {
  'use strict';

  var config = {
    module       : '[data-ls-module="popover"]',
    idPopover    : '#ls-popover-',
    popoverClass : '.ls-popover',
    trigger      : 'click',
    events: {
      clickAnywhere: 'click.clickanywhere',
      opened: 'popover:opened',
      closed: 'popover:closed',
      destroyed: 'popover:destroyed'
    }
  }

  function init() {
    clickAnywhereClose();
    buildPopover();
    bindPopover();
    startOpened();
  }

  // When click or hover elements, show the popovers
  function bindPopover() {
    $(config.module).each(function(index, popoverTrigger) {
      var trigger = $(popoverTrigger).attr('data-trigger') === 'hover' ? 'mouseover' : config.trigger;

      $(popoverTrigger).on(trigger, function(event) {
        event.preventDefault();
        event.stopPropagation();

        var popoverTarget = $(popoverTrigger).data('target');

        if ($(popoverTarget).hasClass('ls-active')) {
          hide(popoverTarget);
        } else {
          show(popoverTarget);
          clickAnywhereClose(popoverTarget)
        }

        if (trigger === 'mouseover') {
          $(popoverTrigger).on('mouseout', function() {
            hide(popoverTarget);
          });
        }

      });
    });

  }

  function clickAnywhereClose(target) {
    $(document).on(config.events.clickAnywhere, function(event){
      if(!$(event.target).parents('.ls-popover').length){
        hide(target);
      }
    });
  }

  // When open page, start popover automatically
  function startOpened() {
    $(config.module+'[data-ls-popover="open"]').each(function() {
      show($(this).data('target'));
    });
  }

  // If popover was not created, we build the HTML using a template
  function buildPopover() {
    $(config.module).each(function(index, popoverTrigger) {

      // Add attr data-target to popover triggers
      $(popoverTrigger).attr('data-target', config.idPopover+index);

      if (!$(config.idPopover+index).length) {
        var data = {
          index        : index,
          title        : $(popoverTrigger).data('title'),
          content      : $(popoverTrigger).data('content'),
          placement    : $(popoverTrigger).data('placement'),
          customClasses: $(popoverTrigger).data('custom-class')
        }

        $('body').append(locastyle.templates.popover(data));

        // Define position of popovers based on his triggers
        setPosition(popoverTrigger);
      }
    });
  }

  // Define position of popovers
  function setPosition(popoverTrigger) {
    var data = {
        target    : $(popoverTrigger).data('target'),
        top       : $(popoverTrigger).offset().top,
        left      : $(popoverTrigger).offset().left,
        width     : $(popoverTrigger).outerWidth(),
        height    : $(popoverTrigger).outerHeight(),
        placement : $(popoverTrigger).data('placement')
    }

    // Define the position of popovers and your elements triggers
    switch (data.placement) {
      case 'right':
        $(data.target).css({
          top : data.top  += (data.height/2 -2),
          left: data.left += (data.width + 12)
        });
        break;
      case 'bottom':
        $(data.target).css({
          top : data.top  += (data.height + 12),
          left: data.left += (data.width/2 + 4)
        });
        break;
      case 'left':
        $(data.target).css({
          top : data.top  += (data.height/2 -2 ),
          left: data.left -= 12
        });
        break;
      default:
      case 'top':
        $(data.target).css({
          top : data.top  -=  12,
          left: data.left += (data.width/2 + 4)
        });

    }

  }

  // Show called popover
  function show(target) {
    $(target).addClass('ls-active');
    $(target).off(config.events.closed).trigger(config.events.opened);
  }

  // Hide all or visible popovers
  function hide(target) {
    $(target || '.ls-popover.ls-active').removeClass('ls-active');
    $(target).trigger(config.events.closed).off(config.events.opened)

    if(!$('.ls-popover.ls-active').length) {
      $(document).off(config.events.clickAnywhere)
    }

  }

  // Destroy all created popovers
  function destroy() {
    $(config.popoverClass).remove();

    // Unbind all events.
    $.each(config.events, function(index, event) {
      $(document).unbind(event);
    });

    $(document).trigger(config.events.destroyed);
  }

  return {
    init   : init,
    show   : show,
    hide   : hide,
    destroy: destroy
  };

}());
<commit_msg>Setting position if the breakpoint-updated is fired.<commit_after>var locastyle = locastyle || {};

locastyle.popover = (function() {
  'use strict';

  var config = {
    module       : '[data-ls-module="popover"]',
    idPopover    : '#ls-popover-',
    popoverClass : '.ls-popover',
    trigger      : 'click',
    events: {
      clickAnywhere: 'click.clickanywhere',
      opened: 'popover:opened',
      closed: 'popover:closed',
      destroyed: 'popover:destroyed'
    }
  }

  function init() {
    clickAnywhereClose();
    buildPopover();
    bindPopover();
    startOpened();
  }

  // When click or hover elements, show the popovers
  function bindPopover() {
    $(config.module).each(function(index, popoverTrigger) {
      var trigger = $(popoverTrigger).attr('data-trigger') === 'hover' ? 'mouseover' : config.trigger;

      $(popoverTrigger).on(trigger, function(event) {
        event.preventDefault();
        event.stopPropagation();

        var popoverTarget = $(popoverTrigger).data('target');

        if ($(popoverTarget).hasClass('ls-active')) {
          hide(popoverTarget);
        } else {
          show(popoverTarget);
          clickAnywhereClose(popoverTarget)
        }

        if (trigger === 'mouseover') {
          $(popoverTrigger).on('mouseout', function() {
            hide(popoverTarget);
          });
        }

      });
    });

  }

  function clickAnywhereClose(target) {
    $(document).on(config.events.clickAnywhere, function(event){
      if(!$(event.target).parents('.ls-popover').length){
        hide(target);
      }
    });
  }

  // When open page, start popover automatically
  function startOpened() {
    $(config.module+'[data-ls-popover="open"]').each(function() {
      show($(this).data('target'));
    });
  }

  // If popover was not created, we build the HTML using a template
  function buildPopover() {
    $(config.module).each(function(index, popoverTrigger) {

      // Add attr data-target to popover triggers
      $(popoverTrigger).attr('data-target', config.idPopover+index);

      if (!$(config.idPopover+index).length) {
        var data = {
          index        : index,
          title        : $(popoverTrigger).data('title'),
          content      : $(popoverTrigger).data('content'),
          placement    : $(popoverTrigger).data('placement'),
          customClasses: $(popoverTrigger).data('custom-class')
        }

        $('body').append(locastyle.templates.popover(data));

        // Define position of popovers based on his triggers
        setPosition(popoverTrigger);
      }

      $(window).on('breakpoint-updated', function(){
        setPosition(popoverTrigger);
      });

    });
  }

  // Define position of popovers
  function setPosition(popoverTrigger) {
    var data = {
        target    : $(popoverTrigger).data('target'),
        top       : $(popoverTrigger).offset().top,
        left      : $(popoverTrigger).offset().left,
        width     : $(popoverTrigger).outerWidth(),
        height    : $(popoverTrigger).outerHeight(),
        placement : $(popoverTrigger).data('placement')
    }

    // Define the position of popovers and your elements triggers
    switch (data.placement) {
      case 'right':
        $(data.target).css({
          top : data.top  += (data.height/2 -2),
          left: data.left += (data.width + 12)
        });
        break;
      case 'bottom':
        $(data.target).css({
          top : data.top  += (data.height + 12),
          left: data.left += (data.width/2 + 4)
        });
        break;
      case 'left':
        $(data.target).css({
          top : data.top  += (data.height/2 -2 ),
          left: data.left -= 12
        });
        break;
      default:
      case 'top':
        $(data.target).css({
          top : data.top  -=  12,
          left: data.left += (data.width/2 + 4)
        });

    }

  }

  // Show called popover
  function show(target) {
    $(target).addClass('ls-active');
    $(target).off(config.events.closed).trigger(config.events.opened);
  }

  // Hide all or visible popovers
  function hide(target) {
    $(target || '.ls-popover.ls-active').removeClass('ls-active');
    $(target).trigger(config.events.closed).off(config.events.opened)

    if(!$('.ls-popover.ls-active').length) {
      $(document).off(config.events.clickAnywhere)
    }

  }

  // Destroy all created popovers
  function destroy() {
    $(config.popoverClass).remove();

    // Unbind all events.
    $.each(config.events, function(index, event) {
      $(document).unbind(event);
    });

    $(document).trigger(config.events.destroyed);
  }

  return {
    init   : init,
    show   : show,
    hide   : hide,
    destroy: destroy
  };

}());
<|endoftext|>


<commit_before>import {DOM_CHANGED_EVENT} from '../classes/fef-dom-observer';

export const COMPONENT_LOADED = 'fef.component.image.slider.loaded';

// Self loading on document.ready and updates if dom change event fired
$(window).on(DOM_CHANGED_EVENT, (e) => {
    $('.image-slider').each((index, element) => {
        new FefImageSlider($(element));
    });
});

export class FefImageSlider {

    constructor($element) {

        let currentPosition = 50;

        this.bindMotionEvents($element);
        this.bindWindowResizeEvents($element);

        this.bindMoveLeftClick(currentPosition, $element);
        this.bindMoveRightClick(currentPosition, $element);

        $(window).trigger(COMPONENT_LOADED, { 'component': this, 'element': $element });
        $element.addClass('ready');
    }

    /**
     * @param $element
     */
    bindMotionEvents($element) {

        $element.on('mousemove touchstart touchmove', (event) => {
            let compBoxLeft = $element.offset().left;

            if (event.type === 'touchstart' || event.type === 'touchmove') {
                event = event.originalEvent.touches[0];
            }

            this.moveSlider(event, compBoxLeft, $element);
        });
    }

    /**
     * @param $element
     */
    bindWindowResizeEvents($element) {
        $(window).resize( (event) => {
            let compBoxLeft = $element.offset().left;
            this.moveSlider(event, compBoxLeft, $element);
        });
    }

    /**
     * @param currentPosition
     * @param $element
     */
    bindMoveLeftClick(currentPosition, $element) {
        $element.parent().find('.image-slider__move-left').on('click', (event) => {
            event.preventDefault();
            let newPosition = currentPosition > 0 ? currentPosition - 10 : currentPosition;
            currentPosition = newPosition;
            this.moveSlider(event, '', $element, newPosition);
        });
    }

    /**
     * @param currentPosition
     * @param $element
     */
    bindMoveRightClick(currentPosition, $element) {
        $element.parent().find('.image-slider__move-right').on('click', (event) => {
            event.preventDefault();
            let newPosition = currentPosition < 100 ? currentPosition + 10 : currentPosition;
            currentPosition = newPosition;
            moveSlider(event, '', $element, newPosition);
        });
    }

    /**
     * @param event
     * @param compBoxLeft
     * @param $element
     * @param newPosition
     */
    moveSlider(event, compBoxLeft, $element, newPosition) {
        let position = typeof newPosition == 'undefined' ? ((event.pageX - compBoxLeft) / $element.outerWidth()) * 100 : newPosition;
        let $wrapper = $element.find('.image-slider__cover-wrapper'),
            $coverImage = $element.find('.image-slider__image--cover'),
            $sliderElementLeft = $element.find('.image-slider__slider--left'),
            $sliderElementRight = $element.find('.image-slider__slider--right');

        if (position >= 0 && position <= 100) {
            $wrapper.css('width', position + '%');
            $coverImage.width(((100 / position) * 100) + '%');
            $sliderElementLeft.css({'left': position + '%'});
            $sliderElementRight.css({'left': position + '%'});
        }
    }

}
<commit_msg>it's like this and like that and like this 🎵<commit_after>import {DOM_CHANGED_EVENT} from '../classes/fef-dom-observer';

export const COMPONENT_LOADED = 'fef.component.image.slider.loaded';

// Self loading on document.ready and updates if dom change event fired
$(window).on(DOM_CHANGED_EVENT, (e) => {
    $('.image-slider').each((index, element) => {
        new FefImageSlider($(element));
    });
});

export class FefImageSlider {

    constructor($element) {

        let currentPosition = 50;

        this.bindMotionEvents($element);
        this.bindWindowResizeEvents($element);

        this.bindMoveLeftClick(currentPosition, $element);
        this.bindMoveRightClick(currentPosition, $element);

        $(window).trigger(COMPONENT_LOADED, { 'component': this, 'element': $element });
        $element.addClass('ready');
    }

    /**
     * @param $element
     */
    bindMotionEvents($element) {

        $element.on('mousemove touchstart touchmove', (event) => {
            let compBoxLeft = $element.offset().left;

            if (event.type === 'touchstart' || event.type === 'touchmove') {
                event = event.originalEvent.touches[0];
            }

            this.moveSlider(event, compBoxLeft, $element);
        });
    }

    /**
     * @param $element
     */
    bindWindowResizeEvents($element) {
        $(window).resize( (event) => {
            let compBoxLeft = $element.offset().left;
            this.moveSlider(event, compBoxLeft, $element);
        });
    }

    /**
     * @param currentPosition
     * @param $element
     */
    bindMoveLeftClick(currentPosition, $element) {
        $element.parent().find('.image-slider__move-left').on('click', (event) => {
            event.preventDefault();
            let newPosition = currentPosition > 0 ? currentPosition - 10 : currentPosition;
            currentPosition = newPosition;
            this.moveSlider(event, '', $element, newPosition);
        });
    }

    /**
     * @param currentPosition
     * @param $element
     */
    bindMoveRightClick(currentPosition, $element) {
        $element.parent().find('.image-slider__move-right').on('click', (event) => {
            event.preventDefault();
            let newPosition = currentPosition < 100 ? currentPosition + 10 : currentPosition;
            currentPosition = newPosition;
            this.moveSlider(event, '', $element, newPosition);
        });
    }

    /**
     * @param event
     * @param compBoxLeft
     * @param $element
     * @param newPosition
     */
    moveSlider(event, compBoxLeft, $element, newPosition) {
        let position = typeof newPosition == 'undefined' ? ((event.pageX - compBoxLeft) / $element.outerWidth()) * 100 : newPosition;
        let $wrapper = $element.find('.image-slider__cover-wrapper'),
            $coverImage = $element.find('.image-slider__image--cover'),
            $sliderElementLeft = $element.find('.image-slider__slider--left'),
            $sliderElementRight = $element.find('.image-slider__slider--right');

        if (position >= 0 && position <= 100) {
            $wrapper.css('width', position + '%');
            $coverImage.width(((100 / position) * 100) + '%');
            $sliderElementLeft.css({'left': position + '%'});
            $sliderElementRight.css({'left': position + '%'});
        }
    }

}
<|endoftext|>


<commit_before>document.onkeydown = move;

var puppy = {
  x: 40,
  y: 200,
  alive: true,
  won: false,
  canMove: true,
};

var alien = {
  x: 560,
  y: 80,
  alive: true,
};

var alien2 = {
  x: 560,
  y: 200,
  alive: true,
};

var alien3 = {
  x: 560,
  y: 320,
  alive: true,
};

document.getElementById("up").onclick = function() {move(38);};
document.getElementById("down").onclick = function() {move(40);};
document.getElementById("right").onclick = function() {move(39);};
document.getElementById("left").onclick = function() {move(37);};

var puppyImg = document.getElementById("puppy");
var alienImg = document.getElementById("alien");
var alienImg2 = document.getElementById("alien2");
var alienImg3 = document.getElementById("alien3");

var alienCount = 3;

function move(direction){
  if (puppy.canMove){
    console.log('moving');
    ai_move(alien, alienImg);
    ai_move(alien2, alienImg2);
    ai_move(alien3, alienImg3);
    if (direction.keyCode === 38 || direction === 38){ // move up
      if (puppy.y > 0){
        puppy.y = (puppy.y - 40);
        puppyImg.style.top = (puppy.y + "px");
      }
    }else if (direction.keyCode === 40 || direction === 40){ // move down
      if (puppy.y < 440){
        puppy.y = (puppy.y + 40);
        puppyImg.style.top = (puppy.y + "px");
      }
    }else if (direction.keyCode === 39 || direction == 39 || direction.keyCode === 76){ // move right
      if (puppy.x < 600){
        puppy.x = (puppy.x + 40);
        puppyImg.style.left = (puppy.x + "px");
      }

    }else if (direction.keyCode === 37 || direction == 37 || direction.keyCode === 74){ // move left
      if (puppy.x > 0){
        puppy.x = (puppy.x - 40);
        puppyImg.style.left = (puppy.x + "px");
      }
    }
    status(alien, alienImg);
    status(alien2, alienImg2);
    status(alien3, alienImg3);
    setTimeout(function(){
      puppy.canMove = true;
    }, 200)
    puppy.canMove = false;
  }
}

function ai_move(player, playerImg){
  var random = Math.floor((Math.random() * 4) + 1);
  // move up
  if (random === 1){
    if (player.y > 0){
      player.y = (player.y - 40);
      playerImg.style.top = (player.y + "px");
    }
  }else if (random === 2){
    // move down
    if (player.y < 440){
      player.y = (player.y + 40);
      playerImg.style.top = (player.y + "px");
    }
  }else if (random === 3){
    // move right
    if (player.x < 600){
      player.x = (player.x + 40);
      playerImg.style.left = (player.x + "px");
    }
 }else{
    //move left
    if (player.x > 0){
      player.x = (player.x - 40);
      playerImg.style.left = (player.x + "px");
    }
 }
}
<commit_msg>Add puppy<commit_after>var puppy = {
  x: 40,
  y: 200,
  alive: true,
  won: false,
  canMove: true,
};

var alien = {
  x: 560,
  y: 80,
  alive: true,
};

var alien2 = {
  x: 560,
  y: 200,
  alive: true,
};

var alien3 = {
  x: 560,
  y: 320,
  alive: true,
};

document.getElementById("up").onclick = function() {move(38);};
document.getElementById("down").onclick = function() {move(40);};
document.getElementById("right").onclick = function() {move(39);};
document.getElementById("left").onclick = function() {move(37);};

var puppyImg = document.getElementById("puppy");
var alienImg = document.getElementById("alien");
var alienImg2 = document.getElementById("alien2");
var alienImg3 = document.getElementById("alien3");

var alienCount = 3;

function move(direction){
  if (puppy.canMove){
    console.log('moving');
    ai_move(alien, alienImg);
    ai_move(alien2, alienImg2);
    ai_move(alien3, alienImg3);
    if (direction.keyCode === 38 || direction === 38){ // move up
      if (puppy.y > 0){
        puppy.y = (puppy.y - 40);
        puppyImg.style.top = (puppy.y + "px");
      }
    }else if (direction.keyCode === 40 || direction === 40){ // move down
      if (puppy.y < 440){
        puppy.y = (puppy.y + 40);
        puppyImg.style.top = (puppy.y + "px");
      }
    }else if (direction.keyCode === 39 || direction == 39 || direction.keyCode === 76){ // move right
      if (puppy.x < 600){
        puppy.x = (puppy.x + 40);
        puppyImg.style.left = (puppy.x + "px");
      }

    }else if (direction.keyCode === 37 || direction == 37 || direction.keyCode === 74){ // move left
      if (puppy.x > 0){
        puppy.x = (puppy.x - 40);
        puppyImg.style.left = (puppy.x + "px");
      }
    }
    status(alien, alienImg);
    status(alien2, alienImg2);
    status(alien3, alienImg3);
    setTimeout(function(){
      puppy.canMove = true;
    }, 200)
    puppy.canMove = false;
  }
}

function ai_move(player, playerImg){
  var random = Math.floor((Math.random() * 4) + 1);
  // move up
  if (random === 1){
    if (player.y > 0){
      player.y = (player.y - 40);
      playerImg.style.top = (player.y + "px");
    }
  }else if (random === 2){
    // move down
    if (player.y < 440){
      player.y = (player.y + 40);
      playerImg.style.top = (player.y + "px");
    }
  }else if (random === 3){
    // move right
    if (player.x < 600){
      player.x = (player.x + 40);
      playerImg.style.left = (player.x + "px");
    }
 }else{
    //move left
    if (player.x > 0){
      player.x = (player.x - 40);
      playerImg.style.left = (player.x + "px");
    }
 }
}

document.onkeydown = move;<|endoftext|>


<commit_before>var c4 =
{
  instances: []
};

function graph_get_params (graph)
{
  var graph_selector = graph.graph_selector;
  var inst_selector = graph.instance_selector;
  var selector = {};

  if (graph_selector.host == inst_selector.host)
  {
    selector.host = graph_selector.host;
  }
  else
  {
    selector.graph_host = graph_selector.host;
    selector.inst_host = inst_selector.host;
  }

  if (graph_selector.plugin == inst_selector.plugin)
  {
    selector.plugin = graph_selector.plugin;
  }
  else
  {
    selector.graph_plugin = graph_selector.plugin;
    selector.inst_plugin = inst_selector.plugin;
  }

  if (graph_selector.plugin_instance == inst_selector.plugin_instance)
  {
    selector.plugin_instance = graph_selector.plugin_instance;
  }
  else
  {
    selector.graph_plugin_instance = graph_selector.plugin_instance;
    selector.inst_plugin_instance = inst_selector.plugin_instance;
  }

  if (graph_selector.type == inst_selector.type)
  {
    selector.type = graph_selector.type;
  }
  else
  {
    selector.graph_type = graph_selector.type;
    selector.inst_type = inst_selector.type;
  }

  if (graph_selector.type_instance == inst_selector.type_instance)
  {
    selector.type_instance = graph_selector.type_instance;
  }
  else
  {
    selector.graph_type_instance = graph_selector.type_instance;
    selector.inst_type_instance = inst_selector.type_instance;
  }

  return (selector);
} /* graph_get_params */

function ident_clone (ident)
{
  var ret = {};

  ret.host = ident.host;
  ret.plugin = ident.plugin;
  ret.plugin_instance = ident.plugin_instance;
  ret.type = ident.type;
  ret.type_instance = ident.type_instance;

  return (ret);
} /* ident_clone */

function json_graph_get_def (graph)
{
  if (!graph.def)
  {
    var params = ident_clone (graph.graph_selector);
    params.action = "graph_def_json";

    $.ajax({
      url: "collection.fcgi",
      async: false,
      dataType: 'json',
      data: params,
      success: function (data)
      {
        if (!data)
          return;

        graph.def = data;
      }});
  }

  if (graph.def)
    return (graph.def);
  return;
} /* json_graph_get_def */

function json_graph_update (index)
{
  var graph;
  var def;
  var params;

  graph = c4.instances[index];
  if (!graph)
    return;

  def = json_graph_get_def (graph);
  if (!def)
    return;

  if (!graph.raphael)
  {
    graph.raphael = Raphael ("c4-graph" + index);
  }

  params = graph_get_params (graph);
  params.action = "graph_data_json";
  params.begin = graph.begin;
  params.end = graph.end;

  $.getJSON ("collection.fcgi", params,
      function (data)
      {
        var x_data = [];
        var y_data = [];
        var i;

        if (!data)
          return;

        for (i = 0; i < data.length; i++)
        {
          var ds = data[i];

          var j;
          var x = [];
          var y = [];

          for (j = 0; j < ds.data.length; j++)
          {
            var dp = ds.data[j];
            var t = dp[0];
            var v = dp[1];

            if (v == null)
              continue;

            x.push (t);
            y.push (v);
          }

          x_data.push (x);
          y_data.push (y);
        }

        graph.raphael.clear ();
        if (def.title)
          graph.raphael.g.text (250, 15, def.title);
        if (def.vertical_label)
          graph.raphael.g.text (5, 100, def.vertical_label).rotate (270);
        graph.raphael.g.linechart(50, 25, 500, 150, x_data, y_data, {axis: "0 0 1 1"});
      }); /* getJSON */
} /* json_graph_update */

function format_instance(inst)
{
  return ("
" + inst.description
      + "
");
}

function format_instance_list(instances)
{
  var ret = "
";
  var i;

  if (instances.length == 0)
    return ("");

  for (i = 0; i < instances.length; i++)
    ret += format_instance (instances[i]);

  ret += "
";

  return (ret);
}

function format_graph(graph)
{
  return ("
" + graph.title + format_instance_list (graph.instances) + "
");
}

function update_search_suggestions ()
{
  var term = $("#search-input").val ();
  if (term.length < 2)
  {
    $("#search-suggest").hide ();
    return (true);
  }

  $("#search-suggest").show ();
  $.getJSON ("collection.fcgi",
    { "action": "search_json", "q": term},
    function(data)
    {
      var i;
      $("#search-suggest").html ("");
      for (i = 0; i < data.length; i++)
      {
        var graph = data[i];
        $("#search-suggest").append (format_graph (graph));
      }
    }
  );
} /* update_search_suggestions */

function zoom_redraw (jq_obj) /* {{{ */
{
  var url = jq_obj.data ("base_url");

  if ((jq_obj == null) || (url == null))
    return (false);

  if (jq_obj.data ('begin') != null)
    url += ";begin=" + jq_obj.data ('begin');
  if (jq_obj.data ('end') != null)
    url += ";end=" + jq_obj.data ('end');

  jq_obj.attr ("src", url);
  return (true);
} /* }}} function zoom_redraw */

function zoom_reset (graph_id, diff) /* {{{ */
{
  var jq_obj;
  var end;
  var begin;

  jq_obj = $("#" + graph_id);
  if (jq_obj == null)
    return (false);

  end = new Number ((new Date ()).getTime () / 1000);
  begin = new Number (end - diff);

  jq_obj.data ('begin', begin.toFixed (0));
  jq_obj.data ('end', end.toFixed (0));

  return (zoom_redraw (jq_obj));
} /* }}} function zoom_reset */

function zoom_hour (graph_id) /* {{{ */
{
  zoom_reset (graph_id, 3600);
} /* }}} function zoom_hour */

function zoom_day (graph_id) /* {{{ */
{
  zoom_reset (graph_id, 86400);
} /* }}} function zoom_day */

function zoom_week (graph_id) /* {{{ */
{
  zoom_reset (graph_id, 7 * 86400);
} /* }}} function zoom_week */

function zoom_month (graph_id) /* {{{ */
{
  zoom_reset (graph_id, 31 * 86400);
} /* }}} function zoom_month */

function zoom_year (graph_id) /* {{{ */
{
  zoom_reset (graph_id, 366 * 86400);
} /* }}} function zoom_year */

function zoom_relative (graph_id, factor_begin, factor_end) /* {{{ */
{
  var jq_obj;
  var end;
  var begin;
  var diff;

  jq_obj = $("#" + graph_id);
  if (jq_obj == null)
    return (false);

  begin = jq_obj.data ('begin');
  end = jq_obj.data ('end');
  if ((begin == null) || (end == null))
    return (zoom_day (graph_id));

  begin = new Number (begin);
  end = new Number (end);

  diff = end - begin;
  if ((diff <= 300) && (factor_begin > 0.0) && (factor_end < 0.0))
    return (true);

  jq_obj.data ('begin', begin + (diff * factor_begin));
  jq_obj.data ('end', end + (diff * factor_end));

  return (zoom_redraw (jq_obj));
} /* }}} function zoom_relative */

function zoom_reference (graph_id) /* {{{ */
{
  var jq_obj;
  var end;
  var begin;

  jq_obj = $("#" + graph_id);
  if (jq_obj == null)
    return (false);

  begin = jq_obj.data ('begin');
  end = jq_obj.data ('end');
  if ((begin == null) || (end == null))
    return (false);

  $(".graph-img img").each (function ()
  {
    $(this).data ('begin', begin);
    $(this).data ('end', end);
    zoom_redraw ($(this));
  });
} /* }}} function zoom_reference */

function zoom_earlier (graph_id) /* {{{ */
{
  return (zoom_relative (graph_id, -0.2, -0.2));
} /* }}} function zoom_earlier */

function zoom_later (graph_id) /* {{{ */
{
  return (zoom_relative (graph_id, +0.2, +0.2));
} /* }}} function zoom_later */

function zoom_in (graph_id) /* {{{ */
{
  return (zoom_relative (graph_id, +0.2, -0.2));
} /* }}} function zoom_earlier */

function zoom_out (graph_id) /* {{{ */
{
  return (zoom_relative (graph_id, (-1.0 / 3.0), (1.0 / 3.0)));
} /* }}} function zoom_earlier */

$(document).ready(function() {
    /* $("#layout-middle-right").html ("

"); */
    $("#search-form").append ("

");
    $("#search-suggest").hide ();

    $("#search-input").blur (function()
    {
      window.setTimeout (function ()
      {
        $("#search-suggest").hide ();
      }, 500);
    });

    $("#search-input").focus (function()
    {
      var term = $("#search-input").val ();
      if (term.length < 2)
      {
        $("#search-suggest").hide ();
      }
      else
      {
        $("#search-suggest").show ();
      }
    });

    $("#search-input").keyup (function()
    {
      update_search_suggestions ();
    });

    var graph_count = 0;
    $(".graph-img").each (function (index, elem)
    {
      var id = "graph" + graph_count;
      graph_count++;

      $(this).find ("img").each (function (img_index, img_elem)
      {
        var base_url;

        $(this).attr ("id", id);

        base_url = $(this).attr ("src").replace (/;(begin|end)=[^;]*/g, '');
        $(this).data ("base_url", base_url);
      });

      $(this).append ("
"
        + "
H
"
        + "
D
"
        + "
W
"
        + "
M
"
        + "
Y
"
        + "
!
"
        + "
"
        + "
"
        + "
←
"
        + "
−
"
        + "
+
"
        + "
→
"
        + "
"
        );
    });

    var i;
    for (i = 0; i < c4.instances.length; i++)
    {
      json_graph_update (i);
    }
});

/* vim: set sw=2 sts=2 et fdm=marker : */
<commit_msg>share/collection.js: Code cleanup.<commit_after>/**
 * collection4 - collection.js
 * Copyright (C) 2010  Florian octo Forster
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301  USA
 *
 * Authors:
 *   Florian octo Forster <ff at octo.it>
 **/

var c4 =
{
  instances: []
};

function instance_get_params (graph) /* {{{ */
{
  var graph_selector = graph.graph_selector;
  var inst_selector = graph.instance_selector;
  var selector = {};

  if (graph_selector.host == inst_selector.host)
  {
    selector.host = graph_selector.host;
  }
  else
  {
    selector.graph_host = graph_selector.host;
    selector.inst_host = inst_selector.host;
  }

  if (graph_selector.plugin == inst_selector.plugin)
  {
    selector.plugin = graph_selector.plugin;
  }
  else
  {
    selector.graph_plugin = graph_selector.plugin;
    selector.inst_plugin = inst_selector.plugin;
  }

  if (graph_selector.plugin_instance == inst_selector.plugin_instance)
  {
    selector.plugin_instance = graph_selector.plugin_instance;
  }
  else
  {
    selector.graph_plugin_instance = graph_selector.plugin_instance;
    selector.inst_plugin_instance = inst_selector.plugin_instance;
  }

  if (graph_selector.type == inst_selector.type)
  {
    selector.type = graph_selector.type;
  }
  else
  {
    selector.graph_type = graph_selector.type;
    selector.inst_type = inst_selector.type;
  }

  if (graph_selector.type_instance == inst_selector.type_instance)
  {
    selector.type_instance = graph_selector.type_instance;
  }
  else
  {
    selector.graph_type_instance = graph_selector.type_instance;
    selector.inst_type_instance = inst_selector.type_instance;
  }

  return (selector);
} /* }}} instance_get_params */

function ident_clone (ident) /* {{{ */
{
  var ret = {};

  ret.host = ident.host;
  ret.plugin = ident.plugin;
  ret.plugin_instance = ident.plugin_instance;
  ret.type = ident.type;
  ret.type_instance = ident.type_instance;

  return (ret);
} /* }}} ident_clone */

function graph_get_defs (graph)
{
  if (!graph.def)
  {
    var params = ident_clone (graph.graph_selector);
    params.action = "graph_def_json";

    $.ajax({
      url: "collection.fcgi",
      async: false,
      dataType: 'json',
      data: params,
      success: function (data)
      {
        if (!data)
          return;

        graph.def = data;
      }});
  }

  if (graph.def)
    return (graph.def);
  return;
} /* graph_get_defs */

function instance_draw (inst, def, data)
{
  var x_data = [];
  var y_data = [];
  var i;

  if (!inst || !def || !data)
    return;

  for (i = 0; i < data.length; i++)
  {
    var ds = data[i];

    var j;
    var x = [];
    var y = [];

    for (j = 0; j < ds.data.length; j++)
    {
      var dp = ds.data[j];
      var t = dp[0];
      var v = dp[1];

      x.push (t);
      y.push (v);
    }

    x_data.push (x);
    y_data.push (y);
  }

  inst.raphael.clear ();
  if (def.title)
    inst.raphael.g.text (250, 15, def.title);
  if (def.vertical_label)
    inst.raphael.g.text (5, 100, def.vertical_label).rotate (270);
  inst.raphael.g.linechart(50, 25, 500, 150, x_data, y_data, {axis: "0 0 1 1"});
}

function json_graph_update (index)
{
  var inst;
  var def;
  var params;

  inst = c4.instances[index];
  if (!inst)
    return;

  def = graph_get_defs (inst);
  if (!def)
    return;

  if (!inst.raphael)
    inst.raphael = Raphael ("c4-graph" + index);

  params = instance_get_params (inst);
  params.action = "graph_data_json";
  params.begin = inst.begin;
  params.end = inst.end;

  $.getJSON ("collection.fcgi", params,
      function (data)
      {
        instance_draw (inst, def, data);
      }); /* getJSON */
} /* json_graph_update */

function format_instance(inst)
{
  return ("
" + inst.description
      + "
");
}

function format_instance_list(instances)
{
  var ret = "
";
  var i;

  if (instances.length == 0)
    return ("");

  for (i = 0; i < instances.length; i++)
    ret += format_instance (instances[i]);

  ret += "
";

  return (ret);
}

function format_graph(graph)
{
  return ("
" + graph.title + format_instance_list (graph.instances) + "
");
}

function update_search_suggestions ()
{
  var term = $("#search-input").val ();
  if (term.length < 2)
  {
    $("#search-suggest").hide ();
    return (true);
  }

  $("#search-suggest").show ();
  $.getJSON ("collection.fcgi",
    { "action": "search_json", "q": term},
    function(data)
    {
      var i;
      $("#search-suggest").html ("");
      for (i = 0; i < data.length; i++)
      {
        var graph = data[i];
        $("#search-suggest").append (format_graph (graph));
      }
    }
  );
} /* update_search_suggestions */

function zoom_redraw (jq_obj) /* {{{ */
{
  var url = jq_obj.data ("base_url");

  if ((jq_obj == null) || (url == null))
    return (false);

  if (jq_obj.data ('begin') != null)
    url += ";begin=" + jq_obj.data ('begin');
  if (jq_obj.data ('end') != null)
    url += ";end=" + jq_obj.data ('end');

  jq_obj.attr ("src", url);
  return (true);
} /* }}} function zoom_redraw */

function zoom_reset (graph_id, diff) /* {{{ */
{
  var jq_obj;
  var end;
  var begin;

  jq_obj = $("#" + graph_id);
  if (jq_obj == null)
    return (false);

  end = new Number ((new Date ()).getTime () / 1000);
  begin = new Number (end - diff);

  jq_obj.data ('begin', begin.toFixed (0));
  jq_obj.data ('end', end.toFixed (0));

  return (zoom_redraw (jq_obj));
} /* }}} function zoom_reset */

function zoom_hour (graph_id) /* {{{ */
{
  zoom_reset (graph_id, 3600);
} /* }}} function zoom_hour */

function zoom_day (graph_id) /* {{{ */
{
  zoom_reset (graph_id, 86400);
} /* }}} function zoom_day */

function zoom_week (graph_id) /* {{{ */
{
  zoom_reset (graph_id, 7 * 86400);
} /* }}} function zoom_week */

function zoom_month (graph_id) /* {{{ */
{
  zoom_reset (graph_id, 31 * 86400);
} /* }}} function zoom_month */

function zoom_year (graph_id) /* {{{ */
{
  zoom_reset (graph_id, 366 * 86400);
} /* }}} function zoom_year */

function zoom_relative (graph_id, factor_begin, factor_end) /* {{{ */
{
  var jq_obj;
  var end;
  var begin;
  var diff;

  jq_obj = $("#" + graph_id);
  if (jq_obj == null)
    return (false);

  begin = jq_obj.data ('begin');
  end = jq_obj.data ('end');
  if ((begin == null) || (end == null))
    return (zoom_day (graph_id));

  begin = new Number (begin);
  end = new Number (end);

  diff = end - begin;
  if ((diff <= 300) && (factor_begin > 0.0) && (factor_end < 0.0))
    return (true);

  jq_obj.data ('begin', begin + (diff * factor_begin));
  jq_obj.data ('end', end + (diff * factor_end));

  return (zoom_redraw (jq_obj));
} /* }}} function zoom_relative */

function zoom_reference (graph_id) /* {{{ */
{
  var jq_obj;
  var end;
  var begin;

  jq_obj = $("#" + graph_id);
  if (jq_obj == null)
    return (false);

  begin = jq_obj.data ('begin');
  end = jq_obj.data ('end');
  if ((begin == null) || (end == null))
    return (false);

  $(".graph-img img").each (function ()
  {
    $(this).data ('begin', begin);
    $(this).data ('end', end);
    zoom_redraw ($(this));
  });
} /* }}} function zoom_reference */

function zoom_earlier (graph_id) /* {{{ */
{
  return (zoom_relative (graph_id, -0.2, -0.2));
} /* }}} function zoom_earlier */

function zoom_later (graph_id) /* {{{ */
{
  return (zoom_relative (graph_id, +0.2, +0.2));
} /* }}} function zoom_later */

function zoom_in (graph_id) /* {{{ */
{
  return (zoom_relative (graph_id, +0.2, -0.2));
} /* }}} function zoom_earlier */

function zoom_out (graph_id) /* {{{ */
{
  return (zoom_relative (graph_id, (-1.0 / 3.0), (1.0 / 3.0)));
} /* }}} function zoom_earlier */

$(document).ready(function() {
    /* $("#layout-middle-right").html ("

"); */
    $("#search-form").append ("

");
    $("#search-suggest").hide ();

    $("#search-input").blur (function()
    {
      window.setTimeout (function ()
      {
        $("#search-suggest").hide ();
      }, 500);
    });

    $("#search-input").focus (function()
    {
      var term = $("#search-input").val ();
      if (term.length < 2)
      {
        $("#search-suggest").hide ();
      }
      else
      {
        $("#search-suggest").show ();
      }
    });

    $("#search-input").keyup (function()
    {
      update_search_suggestions ();
    });

    var graph_count = 0;
    $(".graph-img").each (function (index, elem)
    {
      var id = "graph" + graph_count;
      graph_count++;

      $(this).find ("img").each (function (img_index, img_elem)
      {
        var base_url;

        $(this).attr ("id", id);

        base_url = $(this).attr ("src").replace (/;(begin|end)=[^;]*/g, '');
        $(this).data ("base_url", base_url);
      });

      $(this).append ("
"
        + "
H
"
        + "
D
"
        + "
W
"
        + "
M
"
        + "
Y
"
        + "
!
"
        + "
"
        + "
"
        + "
←
"
        + "
−
"
        + "
+
"
        + "
→
"
        + "
"
        );
    });

    var i;
    for (i = 0; i < c4.instances.length; i++)
    {
      json_graph_update (i);
    }
});

/* vim: set sw=2 sts=2 et fdm=marker : */
<|endoftext|>


<commit_before>/*global window, document, Ghost, $, _, Backbone, JST */
(function () {
    "use strict";

    Ghost.Views.Debug = Ghost.View.extend({
        events: {
            "click .settings-menu a": "handleMenuClick",
            "click #startupload": "handleUploadClick",
            "click .js-delete": "handleDeleteClick"
        },

        initialize: function () {
            var view = this;

            this.uploadButton = this.$el.find('#startupload');

            // Disable import button and initizalize BlueImp file upload
            this.uploadButton.prop('disabled', 'disabled');
            $('#importfile').fileupload({
                url: Ghost.paths.apiRoot + '/db/',
                limitMultiFileUploads: 1,
                replaceFileInput: false,
                headers: {
                    'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
                },
                dataType: 'json',
                add: function (e, data) {
                    /*jslint unparam:true*/

                    // Bind the upload data to the view, so it is
                    // available to the click handler, and enable the
                    // upload button.
                    view.fileUploadData = data;
                    data.context = view.uploadButton.removeProp('disabled');
                },
                done: function (e, data) {
                    /*jslint unparam:true*/
                    $('#startupload').text('Import');
                    if (!data.result) {
                        throw new Error('No response received from server.');
                    }
                    if (!data.result.message) {
                        throw new Error('Unknown error');
                    }

                    Ghost.notifications.addItem({
                        type: 'success',
                        message: data.result.message,
                        status: 'passive'
                    });
                },
                error: function (response) {
                    $('#startupload').text('Import');
                    var responseJSON = response.responseJSON,
                        message = responseJSON && responseJSON.error ? responseJSON.error : 'unknown';
                    Ghost.notifications.addItem({
                        type: 'error',
                        message: ['A problem was encountered while importing new content to your blog. Error: ', message].join(''),
                        status: 'passive'
                    });
                }

            });

        },

        handleMenuClick: function (ev) {
            ev.preventDefault();

            var $target = $(ev.currentTarget);

            // Hide the current content
            this.$(".settings-content").hide();

            // Show the clicked content
            this.$("#debug-" + $target.attr("class")).show();

            return false;
        },

        handleUploadClick: function (ev) {
            ev.preventDefault();

            if (!this.uploadButton.prop('disabled')) {
                this.fileUploadData.context = this.uploadButton.text('Importing');
                this.fileUploadData.submit();
            }

            // Prevent double post by disabling the button.
            this.uploadButton.prop('disabled', 'disabled');
        },

        handleDeleteClick: function (ev) {
            ev.preventDefault();
            this.addSubview(new Ghost.Views.Modal({
                model: {
                    options: {
                        close: true,
                        confirm: {
                            accept: {
                                func: function () {
                                    $.ajax({
                                        url: Ghost.paths.apiRoot + '/db/',
                                        type: 'DELETE',
                                        headers: {
                                            'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
                                        },
                                        success: function onSuccess(response) {
                                            if (!response) {
                                                throw new Error('No response received from server.');
                                            }
                                            if (!response.message) {
                                                throw new Error(response.detail || 'Unknown error');
                                            }

                                            Ghost.notifications.addItem({
                                                type: 'success',
                                                message: response.message,
                                                status: 'passive'
                                            });

                                        },
                                        error: function onError(response) {
                                            var responseText = JSON.parse(response.responseText),
                                                message = responseText && responseText.error ? responseText.error : 'unknown';
                                            Ghost.notifications.addItem({
                                                type: 'error',
                                                message: ['A problem was encountered while deleting content from your blog. Error: ', message].join(''),
                                                status: 'passive'
                                            });

                                        }
                                    });
                                },
                                text: "Yes"
                            },
                            reject: {
                                func: function () {
                                    return true;
                                },
                                text: "No"
                            }
                        },
                        type: "action",
                        style: ["wide", "centered"],
                        animation: 'fade'
                    },
                    content: {
                        template: 'blank',
                        title: 'Would you really like to delete all content from your blog?',
                        text: '
This is permanent! No backups, no restores, no magic undo button.
 We warned you, ok?
'
                    }
                }
            }));
        }
    });
}());
<commit_msg>Update delete all content button labels<commit_after>/*global window, document, Ghost, $, _, Backbone, JST */
(function () {
    "use strict";

    Ghost.Views.Debug = Ghost.View.extend({
        events: {
            "click .settings-menu a": "handleMenuClick",
            "click #startupload": "handleUploadClick",
            "click .js-delete": "handleDeleteClick"
        },

        initialize: function () {
            var view = this;

            this.uploadButton = this.$el.find('#startupload');

            // Disable import button and initizalize BlueImp file upload
            this.uploadButton.prop('disabled', 'disabled');
            $('#importfile').fileupload({
                url: Ghost.paths.apiRoot + '/db/',
                limitMultiFileUploads: 1,
                replaceFileInput: false,
                headers: {
                    'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
                },
                dataType: 'json',
                add: function (e, data) {
                    /*jslint unparam:true*/

                    // Bind the upload data to the view, so it is
                    // available to the click handler, and enable the
                    // upload button.
                    view.fileUploadData = data;
                    data.context = view.uploadButton.removeProp('disabled');
                },
                done: function (e, data) {
                    /*jslint unparam:true*/
                    $('#startupload').text('Import');
                    if (!data.result) {
                        throw new Error('No response received from server.');
                    }
                    if (!data.result.message) {
                        throw new Error('Unknown error');
                    }

                    Ghost.notifications.addItem({
                        type: 'success',
                        message: data.result.message,
                        status: 'passive'
                    });
                },
                error: function (response) {
                    $('#startupload').text('Import');
                    var responseJSON = response.responseJSON,
                        message = responseJSON && responseJSON.error ? responseJSON.error : 'unknown';
                    Ghost.notifications.addItem({
                        type: 'error',
                        message: ['A problem was encountered while importing new content to your blog. Error: ', message].join(''),
                        status: 'passive'
                    });
                }

            });

        },

        handleMenuClick: function (ev) {
            ev.preventDefault();

            var $target = $(ev.currentTarget);

            // Hide the current content
            this.$(".settings-content").hide();

            // Show the clicked content
            this.$("#debug-" + $target.attr("class")).show();

            return false;
        },

        handleUploadClick: function (ev) {
            ev.preventDefault();

            if (!this.uploadButton.prop('disabled')) {
                this.fileUploadData.context = this.uploadButton.text('Importing');
                this.fileUploadData.submit();
            }

            // Prevent double post by disabling the button.
            this.uploadButton.prop('disabled', 'disabled');
        },

        handleDeleteClick: function (ev) {
            ev.preventDefault();
            this.addSubview(new Ghost.Views.Modal({
                model: {
                    options: {
                        close: true,
                        confirm: {
                            accept: {
                                func: function () {
                                    $.ajax({
                                        url: Ghost.paths.apiRoot + '/db/',
                                        type: 'DELETE',
                                        headers: {
                                            'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
                                        },
                                        success: function onSuccess(response) {
                                            if (!response) {
                                                throw new Error('No response received from server.');
                                            }
                                            if (!response.message) {
                                                throw new Error(response.detail || 'Unknown error');
                                            }

                                            Ghost.notifications.addItem({
                                                type: 'success',
                                                message: response.message,
                                                status: 'passive'
                                            });

                                        },
                                        error: function onError(response) {
                                            var responseText = JSON.parse(response.responseText),
                                                message = responseText && responseText.error ? responseText.error : 'unknown';
                                            Ghost.notifications.addItem({
                                                type: 'error',
                                                message: ['A problem was encountered while deleting content from your blog. Error: ', message].join(''),
                                                status: 'passive'
                                            });

                                        }
                                    });
                                },
                                text: "Delete",
                                buttonClass: "button-delete"
                            },
                            reject: {
                                func: function () {
                                    return true;
                                },
                                text: "Cancel",
                                buttonClass: "button"
                            }
                        },
                        type: "action",
                        style: ["wide", "centered"],
                        animation: 'fade'
                    },
                    content: {
                        template: 'blank',
                        title: 'Would you really like to delete all content from your blog?',
                        text: '
This is permanent! No backups, no restores, no magic undo button.
 We warned you, ok?
'
                    }
                }
            }));
        }
    });
}());
<|endoftext|>


<commit_before>import async from 'async';
import Field from '../Field';
import { listsByKey } from '../../../admin/client/utils/lists';
import React from 'react';
import Select from 'react-select';
import xhr from 'xhr';
import { Button, InputGroup } from 'elemental';

function compareValues (current, next) {
	const currentLength = current ? current.length : 0;
	const nextLength = next ? next.length : 0;
	if (currentLength !== nextLength) return false;
	for (let i = 0; i < currentLength; i++) {
		if (current[i] !== next[i]) return false;
	}
	return true;
}

module.exports = Field.create({

	displayName: 'RelationshipField',
	statics: {
		type: 'Relationship',
	},

	getInitialState () {
		return {
			value: null,
			createIsOpen: false,
		};
	},

	componentDidMount () {
		this._itemsCache = {};
		this.loadValue(this.props.value);
	},

	componentWillReceiveProps (nextProps) {
		if (nextProps.value === this.props.value || nextProps.many && compareValues(this.props.value, nextProps.value)) return;
		this.loadValue(nextProps.value);
	},

	shouldCollapse () {
		if (this.props.many) {
			// many:true relationships have an Array for a value
			return this.props.collapse && !this.props.value.length;
		}
		return this.props.collapse && !this.props.value;
	},

	buildFilters () {
		var filters = {};

		_.forEach(this.props.filters, (value, key) => {
			if (_.isString(value) && value[0] == ':') { // eslint-disable-line eqeqeq
				var fieldName = value.slice(1);

				var val = this.props.values[fieldName];
				if (val) {
					filters[key] = val;
					return;
				}

				// check if filtering by id and item was already saved
				if (fieldName === ':_id' && Keystone.item) {
					filters[key] = Keystone.item.id;
					return;
				}
			} else {
				filters[key] = value;
			}
		}, this);

		var parts = [];

		_.forEach(filters, function (val, key) {
			parts.push('filters[' + key + '][value]=' + encodeURIComponent(val));
		});

		return parts.join('&');
	},

	cacheItem (item) {
		item.href = Keystone.adminPath + '/' + this.props.refList.path + '/' + item.id;
		this._itemsCache[item.id] = item;
	},

	loadValue (values) {
		if (!values) {
			return this.setState({
				loading: false,
				value: null,
			});
		};
		values = Array.isArray(values) ? values : values.split(',');
		const cachedValues = values.map(i => this._itemsCache[i]).filter(i => i);
		if (cachedValues.length === values.length) {
			this.setState({
				loading: false,
				value: this.props.many ? cachedValues : cachedValues[0],
			});
			return;
		}
		this.setState({
			loading: true,
			value: null,
		});
		async.map(values, (value, done) => {
			xhr({
				url: Keystone.adminPath + '/api/' + this.props.refList.path + '/' + value + '?basic',
				responseType: 'json',
			}, (err, resp, data) => {
				if (err || !data) return done(err);
				this.cacheItem(data);
				done(err, data);
			});
		}, (err, expanded) => {
			if (!this.isMounted()) return;
			this.setState({
				loading: false,
				value: this.props.many ? expanded : expanded[0],
			});
		});
	},

	// NOTE: this seems like the wrong way to add options to the Select
	loadOptionsCallback: {},
	loadOptions (input, callback) {
		// NOTE: this seems like the wrong way to add options to the Select
		this.loadOptionsCallback = callback;
		const filters = this.buildFilters();
		xhr({
			url: Keystone.adminPath + '/api/' + this.props.refList.path + '?basic&search=' + input + '&' + filters,
			responseType: 'json',
		}, (err, resp, data) => {
			if (err) {
				console.error('Error loading items:', err);
				return callback(null, []);
			}
			data.results.forEach(this.cacheItem);
			callback(null, {
				options: data.results,
				complete: data.results.length === data.count,
			});
		});
	},

	valueChanged (value) {
		this.props.onChange({
			path: this.props.path,
			value: value,
		});
	},

	openCreate () {
		this.setState({
			createIsOpen: true,
		});
	},

	closeCreate () {
		this.setState({
			createIsOpen: false,
		});
	},

	onCreate (item) {
		this.cacheItem(item);
		if (Array.isArray(this.state.value)) {
			// For many relationships, append the new item to the end
			const values = this.state.value.map((item) => item.id);
			values.push(item.id);
			this.valueChanged(values.join(','));
		} else {
			this.valueChanged(item.id);
		}

		// NOTE: this seems like the wrong way to add options to the Select
		this.loadOptionsCallback(null, {
			complete: true,
			options: Object.keys(this._itemsCache).map((k) => this._itemsCache[k]),
		});
		this.toggleCreate(false);
	},

	renderSelect (noedit) {
		return (
			<Select.Async
				multi={this.props.many}
				disabled={noedit}
				loadOptions={this.loadOptions}
				labelKey="name"
				name={this.props.path}
				onChange={this.valueChanged}
				simpleValue
				value={this.state.value}
				valueKey="id"
			/>
		);
	},

	renderInputGroup () {
		// TODO: find better solution
		//   when importing the CreateForm using: import CreateForm from '../../../admin/client/App/shared/CreateForm';
		//   CreateForm was imported as a blank object. This stack overflow post suggested lazilly requiring it:
		// http://stackoverflow.com/questions/29807664/cyclic-dependency-returns-empty-object-in-react-native
		// TODO: Implement this somewhere higher in the app, it breaks the encapsulation of the RelationshipField component
		const CreateForm = require('../../../admin/client/App/shared/CreateForm');
		return (
			<InputGroup>
				<InputGroup.Section grow>
					{this.renderSelect()}
				</InputGroup.Section>
				<InputGroup.Section>
					<Button onClick={this.openCreate} type="success">+</Button>
				</InputGroup.Section>
				<CreateForm
					list={listsByKey[this.props.refList.key]}
					isOpen={this.state.createIsOpen}
					onCreate={this.onCreate}
					onCancel={this.closeCreate} />
			</InputGroup>
		);
	},

	renderValue () {
		return this.renderSelect(true);
	},

	renderField () {
		if (this.props.createInline) {
			return this.renderInputGroup();
		} else {
			return this.renderSelect();
		}
	},

});
<commit_msg>Add lodash import to RelationshipField, resolve #3241<commit_after>import async from 'async';
import Field from '../Field';
import { listsByKey } from '../../../admin/client/utils/lists';
import React from 'react';
import Select from 'react-select';
import xhr from 'xhr';
import { Button, InputGroup } from 'elemental';
import _ from 'lodash';

function compareValues (current, next) {
	const currentLength = current ? current.length : 0;
	const nextLength = next ? next.length : 0;
	if (currentLength !== nextLength) return false;
	for (let i = 0; i < currentLength; i++) {
		if (current[i] !== next[i]) return false;
	}
	return true;
}

module.exports = Field.create({

	displayName: 'RelationshipField',
	statics: {
		type: 'Relationship',
	},

	getInitialState () {
		return {
			value: null,
			createIsOpen: false,
		};
	},

	componentDidMount () {
		this._itemsCache = {};
		this.loadValue(this.props.value);
	},

	componentWillReceiveProps (nextProps) {
		if (nextProps.value === this.props.value || nextProps.many && compareValues(this.props.value, nextProps.value)) return;
		this.loadValue(nextProps.value);
	},

	shouldCollapse () {
		if (this.props.many) {
			// many:true relationships have an Array for a value
			return this.props.collapse && !this.props.value.length;
		}
		return this.props.collapse && !this.props.value;
	},

	buildFilters () {
		var filters = {};

		_.forEach(this.props.filters, (value, key) => {
			if (_.isString(value) && value[0] == ':') { // eslint-disable-line eqeqeq
				var fieldName = value.slice(1);

				var val = this.props.values[fieldName];
				if (val) {
					filters[key] = val;
					return;
				}

				// check if filtering by id and item was already saved
				if (fieldName === ':_id' && Keystone.item) {
					filters[key] = Keystone.item.id;
					return;
				}
			} else {
				filters[key] = value;
			}
		}, this);

		var parts = [];

		_.forEach(filters, function (val, key) {
			parts.push('filters[' + key + '][value]=' + encodeURIComponent(val));
		});

		return parts.join('&');
	},

	cacheItem (item) {
		item.href = Keystone.adminPath + '/' + this.props.refList.path + '/' + item.id;
		this._itemsCache[item.id] = item;
	},

	loadValue (values) {
		if (!values) {
			return this.setState({
				loading: false,
				value: null,
			});
		};
		values = Array.isArray(values) ? values : values.split(',');
		const cachedValues = values.map(i => this._itemsCache[i]).filter(i => i);
		if (cachedValues.length === values.length) {
			this.setState({
				loading: false,
				value: this.props.many ? cachedValues : cachedValues[0],
			});
			return;
		}
		this.setState({
			loading: true,
			value: null,
		});
		async.map(values, (value, done) => {
			xhr({
				url: Keystone.adminPath + '/api/' + this.props.refList.path + '/' + value + '?basic',
				responseType: 'json',
			}, (err, resp, data) => {
				if (err || !data) return done(err);
				this.cacheItem(data);
				done(err, data);
			});
		}, (err, expanded) => {
			if (!this.isMounted()) return;
			this.setState({
				loading: false,
				value: this.props.many ? expanded : expanded[0],
			});
		});
	},

	// NOTE: this seems like the wrong way to add options to the Select
	loadOptionsCallback: {},
	loadOptions (input, callback) {
		// NOTE: this seems like the wrong way to add options to the Select
		this.loadOptionsCallback = callback;
		const filters = this.buildFilters();
		xhr({
			url: Keystone.adminPath + '/api/' + this.props.refList.path + '?basic&search=' + input + '&' + filters,
			responseType: 'json',
		}, (err, resp, data) => {
			if (err) {
				console.error('Error loading items:', err);
				return callback(null, []);
			}
			data.results.forEach(this.cacheItem);
			callback(null, {
				options: data.results,
				complete: data.results.length === data.count,
			});
		});
	},

	valueChanged (value) {
		this.props.onChange({
			path: this.props.path,
			value: value,
		});
	},

	openCreate () {
		this.setState({
			createIsOpen: true,
		});
	},

	closeCreate () {
		this.setState({
			createIsOpen: false,
		});
	},

	onCreate (item) {
		this.cacheItem(item);
		if (Array.isArray(this.state.value)) {
			// For many relationships, append the new item to the end
			const values = this.state.value.map((item) => item.id);
			values.push(item.id);
			this.valueChanged(values.join(','));
		} else {
			this.valueChanged(item.id);
		}

		// NOTE: this seems like the wrong way to add options to the Select
		this.loadOptionsCallback(null, {
			complete: true,
			options: Object.keys(this._itemsCache).map((k) => this._itemsCache[k]),
		});
		this.toggleCreate(false);
	},

	renderSelect (noedit) {
		return (
			<Select.Async
				multi={this.props.many}
				disabled={noedit}
				loadOptions={this.loadOptions}
				labelKey="name"
				name={this.props.path}
				onChange={this.valueChanged}
				simpleValue
				value={this.state.value}
				valueKey="id"
			/>
		);
	},

	renderInputGroup () {
		// TODO: find better solution
		//   when importing the CreateForm using: import CreateForm from '../../../admin/client/App/shared/CreateForm';
		//   CreateForm was imported as a blank object. This stack overflow post suggested lazilly requiring it:
		// http://stackoverflow.com/questions/29807664/cyclic-dependency-returns-empty-object-in-react-native
		// TODO: Implement this somewhere higher in the app, it breaks the encapsulation of the RelationshipField component
		const CreateForm = require('../../../admin/client/App/shared/CreateForm');
		return (
			<InputGroup>
				<InputGroup.Section grow>
					{this.renderSelect()}
				</InputGroup.Section>
				<InputGroup.Section>
					<Button onClick={this.openCreate} type="success">+</Button>
				</InputGroup.Section>
				<CreateForm
					list={listsByKey[this.props.refList.key]}
					isOpen={this.state.createIsOpen}
					onCreate={this.onCreate}
					onCancel={this.closeCreate} />
			</InputGroup>
		);
	},

	renderValue () {
		return this.renderSelect(true);
	},

	renderField () {
		if (this.props.createInline) {
			return this.renderInputGroup();
		} else {
			return this.renderSelect();
		}
	},

});
<|endoftext|>


<commit_before>import _ from 'underscore';
import Field from '../Field';
import React from 'react';
import Select from 'react-select';
import superagent from 'superagent';
import { Button, FormInput } from 'elemental';

module.exports = Field.create({

	displayName: 'RelationshipField',

	shouldCollapse () {
		// many:true relationships have an Array for a value
		// so need to check length instead
		if (this.props.many) {
			return this.props.collapse && !this.props.value.length;
		}
		return this.props.collapse && !this.props.value;
	},

	getInitialState () {
		return {
			ready: this.props.value ? false : true,
			simpleValue: this.props.value,
			expandedValues: null
		};
	},

	componentDidMount () {
		this.loadValues(this.props.value);
	},

	componentWillReceiveProps (newProps) {
		if (newProps.value !== this.state.simpleValue) {
			this.setState({
				ready: false,
				simpleValue: newProps.value,
				expandedValues: null
			});
			this.loadValues(newProps.value);
		}
	},

	loadValues (input) {
		var expandedValues = [];
		var inputs = _.compact([].concat(input));
		var self = this;

		var finish = function () {
			self.setState({
				ready: true,
				expandedValues: expandedValues
			});
		};

		if (!inputs.length) return finish();

		var callbackCount = 0;
		_.each(inputs, function(input) {
			expandedValues.push({
				value: input
			});
			superagent
				.get('/keystone/api/' + self.props.refList.path + '/' + input + '?simple')
				.set('Accept', 'application/json')
				.end(function (err, res) {
					if (err) throw err;

					var value = res.body;
					_.findWhere(expandedValues, { value: value.id }).label = value.name;

					callbackCount++;
					if (callbackCount === inputs.length) {
						finish();
					}
				});
		});
	},

	buildFilters () {
		var filters = {};

		_.each(this.props.filters, function(value, key) {
			if(_.isString(value) && value[0] == ':') {//eslint-disable-line eqeqeq
				var fieldName = value.slice(1);

				var val = this.props.values[fieldName];
				if (val) {
					filters[key] = val;
					return;
				}

				// check if filtering by id and item was already saved
				if (fieldName === ':_id' && Keystone.item) {
					filters[key] = Keystone.item.id;
					return;
				}
			} else {
				filters[key] = value;
			}
		}, this);

		var parts = [];

		_.each(filters, function (val, key) {
			parts.push('filters[' + key + ']=' + encodeURIComponent(val));
		});

		return parts.join('&');
	},

	buildOptionQuery  (input) {
		return 'context=relationship&q=' + input +
				'&list=' + Keystone.list.path +
				'&field=' + this.props.path +
				'&' + this.buildFilters();
	},

	getOptions (input, callback) {
		superagent
			.get('/keystone/api/' + this.props.refList.path + '/autocomplete?' + this.buildOptionQuery(input))
			.set('Accept', 'application/json')
			.end(function (err, res) {
				if (err) throw err;

				var data = res.body;

				callback(null, {
					options: data.items.map(function (item) {
						return {
							value: item.id,
							label: item.name
						};
					}),
					complete: data.total === data.items.length
				});
			});
	},

	renderLoadingUI () {
		return
loading...
;
	},

	updateValue (simpleValue, expandedValues) {
		this.setState({
			simpleValue: simpleValue,
			expandedValues: expandedValues
		});
		this.props.onChange({
			path: this.props.path,
			value: this.props.many ? _.pluck(expandedValues, 'value') : simpleValue
		});
	},

	renderValue () {
		if (!this.state.ready) {
			return this.renderLoadingUI();
		}
		// Todo: this is only a temporary fix, remodel
		if (this.state.expandedValues && this.state.expandedValues.length) {
			var body = [];

			_.each(this.state.expandedValues, function (item, i) {
				body.push(<FormInput key={i} noedit href={'/keystone/' + this.props.refList.path + '/' + item.value}>{item.label}</FormInput>);
			}, this);

			return body;
		} else {
			return <FormInput noedit>not set)</FormInput>;
		}
	},

	renderField () {
		if (!this.state.ready) {
			return this.renderLoadingUI();
		}
		let button = (!this.props.many && this.props.value) ? (
			<Button key="relational-button" type="link" href={'/keystone/' + this.props.refList.path + '/' + this.props.value} className='keystone-relational-button' title={'Go to "' + this.state.expandedValues[0].label + '"'}>

			</Button>
		) : null;

		return (

				<Select key="relationship-select" multi={this.props.many} onChange={this.updateValue} name={this.props.path} asyncOptions={this.getOptions} value={this.state.expandedValues} />
				{button}

		);
	}

});
<commit_msg>fixed bug where relationships that are deleted break the RelationshipField<commit_after>import _ from 'underscore';
import Field from '../Field';
import React from 'react';
import Select from 'react-select';
import superagent from 'superagent';
import { Button, FormInput } from 'elemental';

module.exports = Field.create({

	displayName: 'RelationshipField',

	shouldCollapse () {
		// many:true relationships have an Array for a value
		// so need to check length instead
		if (this.props.many) {
			return this.props.collapse && !this.props.value.length;
		}
		return this.props.collapse && !this.props.value;
	},

	getInitialState () {
		return {
			ready: this.props.value ? false : true,
			simpleValue: this.props.value,
			expandedValues: null
		};
	},

	componentDidMount () {
		this.loadValues(this.props.value);
	},

	componentWillReceiveProps (newProps) {
		if (newProps.value !== this.state.simpleValue) {
			this.setState({
				ready: false,
				simpleValue: newProps.value,
				expandedValues: null
			});
			this.loadValues(newProps.value);
		}
	},

	loadValues (input) {
		var expandedValues = [];
		var inputs = _.compact([].concat(input));
		var self = this;

		var finish = function () {
			self.setState({
				ready: true,
				expandedValues: expandedValues
			});
		};

		if (!inputs.length) return finish();

		var callbackCount = 0;
		_.each(inputs, function(input) {
			expandedValues.push({
				value: input
			});
			superagent
				.get('/keystone/api/' + self.props.refList.path + '/' + input + '?simple')
				.set('Accept', 'application/json')
				.end(function (err, res) {
					if (err && err.status !== 404) throw err;
					var value = res.body;
					_.findWhere(expandedValues, { value: value.id }).label = value.name;

					callbackCount++;
					if (callbackCount === inputs.length) {
						finish();
					}
				});
		});
	},

	buildFilters () {
		var filters = {};

		_.each(this.props.filters, function(value, key) {
			if(_.isString(value) && value[0] == ':') {//eslint-disable-line eqeqeq
				var fieldName = value.slice(1);

				var val = this.props.values[fieldName];
				if (val) {
					filters[key] = val;
					return;
				}

				// check if filtering by id and item was already saved
				if (fieldName === ':_id' && Keystone.item) {
					filters[key] = Keystone.item.id;
					return;
				}
			} else {
				filters[key] = value;
			}
		}, this);

		var parts = [];

		_.each(filters, function (val, key) {
			parts.push('filters[' + key + ']=' + encodeURIComponent(val));
		});

		return parts.join('&');
	},

	buildOptionQuery  (input) {
		return 'context=relationship&q=' + input +
				'&list=' + Keystone.list.path +
				'&field=' + this.props.path +
				'&' + this.buildFilters();
	},

	getOptions (input, callback) {
		superagent
			.get('/keystone/api/' + this.props.refList.path + '/autocomplete?' + this.buildOptionQuery(input))
			.set('Accept', 'application/json')
			.end(function (err, res) {
				if (err) throw err;

				var data = res.body;

				callback(null, {
					options: data.items.map(function (item) {
						return {
							value: item.id,
							label: item.name
						};
					}),
					complete: data.total === data.items.length
				});
			});
	},

	renderLoadingUI () {
		return
loading...
;
	},

	updateValue (simpleValue, expandedValues) {
		this.setState({
			simpleValue: simpleValue,
			expandedValues: expandedValues
		});
		this.props.onChange({
			path: this.props.path,
			value: this.props.many ? _.pluck(expandedValues, 'value') : simpleValue
		});
	},

	renderValue () {
		if (!this.state.ready) {
			return this.renderLoadingUI();
		}
		// Todo: this is only a temporary fix, remodel
		if (this.state.expandedValues && this.state.expandedValues.length) {
			var body = [];

			_.each(this.state.expandedValues, function (item, i) {
				body.push(<FormInput key={i} noedit href={'/keystone/' + this.props.refList.path + '/' + item.value}>{item.label}</FormInput>);
			}, this);

			return body;
		} else {
			return <FormInput noedit>not set)</FormInput>;
		}
	},

	renderField () {
		if (!this.state.ready) {
			return this.renderLoadingUI();
		}
		let button = (!this.props.many && this.props.value) ? (
			<Button key="relational-button" type="link" href={'/keystone/' + this.props.refList.path + '/' + this.props.value} className='keystone-relational-button' title={'Go to "' + this.state.expandedValues[0].label + '"'}>

			</Button>
		) : null;

		return (

				<Select key="relationship-select" multi={this.props.many} onChange={this.updateValue} name={this.props.path} asyncOptions={this.getOptions} value={this.state.expandedValues} />
				{button}

		);
	}

});
<|endoftext|>