Jump to content

BomAle

Members
  • Posts

    821
  • Joined

  • Last visited

  • Days Won

    2

Reputation Activity

  1. Like
    BomAle reacted to Rikki for a guide, ips.utils.position   
    Description
    Dealing with element positioning - getting current positions, working out new positions, and so on - is a common task in a web application such as IPS4. This utility aims to make that easier by providing some methods to do the calculations for you.
     
    Methods
    object getElemPosition( mixed elem )
    Returns various positioning information for the provided element.
    elem
    DOM element or jQuery object containing the element to work with Returned object:
    absPos left
    Absolute left position right
    Absolute right position (i.e. left + width) top
    Absolute top position bottom
    Absolute bottom position (i.e. top + height) viewportOffset left
    Viewport left offset (i.e. left offset - left scroll postion) top
    Viewport top offset (i.e. top offset - top scroll position) offsetParent
    Reference to the element's offsetParent. That is, the closest element that has a non-static position. fixed
    true if the element has any ancestors which have fixed positioning or have one of the table display values.  
    object positionElem( object options )
    Returns positioning values, allowing the calling method to appropriate position the element with a best-fit on the screen. This method does not apply the positioning values to the element itself.
    options trigger
    The trigger element for this popup; that is, the element that the popup should be 'attached' to target
    The element to be positioned targetContainer
    The element that contains the element to be positioned. Assumed to be body if not provided. center (boolean)
    Specifies whether the method should attempt to position the target centered relative to the trigger. above (boolean)
    Specifies whether the method should attempt to position the target above the trigger. By default, a best fit is attempted. Returned object:
    top
    Ideal top position, in pixels left
    Ideal left position, in pixels fixed
    Whether the element should have fixed positioning applied (because an ancestor does) location horizontal
    Best horizontal location for the element; either center, left or right vertical
    Best vertical location for the element; either bottom or top  
    boolean hasFixedParents( mixed elem )
    Returns true if any of the element's descendants have fixed positioning or have one of the table display classes (e.g. table-row).
    elem
    The element whose ancestors are being checked for fixed positioning. Either a DOM node or a jQuery object.  
    object getElemDims( mixed elem )
    Returns current dimension information for the element.
    elem
    The element whose dimensions are being returned. Either a DOM node or a jQuery object. Returned object:
    width
    Current width in pixels height
    Current height in pixels outerWidth
    Current outer width of the element; that is, the width plus border and padding outerHeight
    Current outer height of the element; that is, the width plus border and padding  
    number nautralWidth( mixed elem )
    Returns the natural width of the provided element (which should be an img element). The natural width is the original width of the image not accounting for any resizing done by CSS or HTML attributes.
    elem
    The element whose natural width is being returned. Either a DOM node or a jQuery object.  
    number nautralHeight( mixed elem )
    Returns the natural height of the provided element (which should be an img element). The natural height is the original height of the image not accounting for any resizing done by CSS or HTML attributes.
    elem
    The element whose natural height is being returned. Either a DOM node or a jQuery object.  
    number lineHeight( mixed elem )
    Attempts to return the computed line-height, in pixels, of the provided element. There are edge cases where this result won't be reliable, such as CSS styling <span> tags inside the element differently to the element itself. Test your use case.
    elem
    The element whose line-height is being returned. Either a DOM node or a jQuery object.
  2. Like
    BomAle reacted to Rikki for a guide, Mixins   
    Mixins are a special type of controller that allow you to augment or change the functionality of an existing controller. This is particularly useful when you need to change something about how a built-in controller works.
     
    Basic structure
    This is the boilerplate for a mixin:
    ;( function($, _, undefined){ "use strict"; ips.controller.mixin('mixinName', 'core.global.core.table', true, function () { // Your code goes here }); }(jQuery, _)); The method signature is:
    ips.controller.mixin( string mixinName, string extendsController, boolean autoLoad, function mixinBody ) mixinName
    A name that will refer to this mixin extendsController
    The full name of the controller that this mixin will extend (e.g. core.global.core.table) autoLoad
    Boolean indicating whether this mixin should always extend the extendsController controller, or only when specified mixinBody
    A function that returns the behaviors that the mixin defines  
    How a mixin works
    A mixin works by allowing you to provide custom code that will execute at key points when the parent controller's method is called. To do that, there are three important methods available to a mixin:
    before()
    Run the specified code before the parent controller's method after()
    Run the specified code after the parent controller's method around()
    The specified code is passed a reference to the parent controller's method, allowing it to be called when desired (and the return value modified In addition to hooking into existing controller methods, your mixin can also provide new methods for its own use and, notably, simply redefine a method present in the parent controller in order to completely replace its functionality.
     
    A note about this
    Within the mixin body, you'll be adding methods to this. The mixin executes in the same context as the parent controller; that is, this in a mixin refers to both the mixin and the controller it extends, allowing you to call methods from the controller, and any create yourself in the mixin. During execution, they are one and the same.
    The same is also true within methods in your mixin since, like in controllers, methods are automatically bound to the controller for you. You may be familiar with this.scope in controller methods (which refers to the DOM element onto which the controller is applied). Since mixin methods are bound in exactly the same way, using this.scope in a mixin method will still give you the element onto which the controller is applied.
     
    before() and after()
    The behavior of these two methods are very similar. They simply allow you to execute your custom code either immediately before, or immediately after the parent controller's code. Your custom code receives the parameters that are passed into the parent method, but not the return value (if any).
    A common usage of either method is to add additional event handlers to a controller. A controller sets up its event handlers in the initialize method, and so a mixin can add more handlers like so:
    this.before('initialize', function () { this.on('click', '#button', this._doSomething); this.on('click', '#button2', this._doSomethingElse); }); In this case, our function will be executed, and then the parent's own initialize method will be executed.
    In this example we don't deal with any parameters but, if the parent method is called with any, they will also be available to your mixin function as normal parameters, in the same order.
     
    around()
    This behaves differently to the previous example, because rather than simply being executed before or after the parent, it actually provides the parent method as a reference to your own function. This allows you to call the parent method at a time of your choosing - perhaps determined based on some logic in your code. Since you have control over calling the parent method, you also have access to its return value, meaning that around can also be used as a way to modify the value that is returned from a controller method.
    As an example, let's assume that the parent controller method returns a JSON object, but we want to augment this with some additional key/values. We can do this using the around call, like so:
    this.around('parentMethod', function (origFn) { var returned = origFn(); return _.extend( returned, { key1: true, key2: false }); }); Notice that the parent method is passed in as a parameter (if the method has any other parameters, these will appear first, and the parent method will be the last parameter). We call this method in order to get its return value for our own use. When we look at the parent method we know that it returns a JSON object, so we extend it with our own values.
    It's worth noting that since you receive both the original parameters, and a reference to the parent method, you have the ability to modify the parameter values before passing them into the parent method. This approach can offer a lot of flexibility.
     
    Custom methods and replacing existing methods
    Finally, you can create new methods in your mixin, or completely replace methods from the parent controller by redefining them. In both cases, this is done like so:
    this.myMethod = function () { //... }; Of course, if you replace an existing method, be sure it plays nicely with any calls to it!
     
    Calling a mixin
    Mixins can be applied to controllers manually when needed (you don't need to do this if you configured your mixing to automatically apply to the controller, though). To do so, specify the mixin name in parenthesis after the controller. For example:
    <div data-controller='core.front.core.someController( mixinName )'> </div> Multiple mixins can be provided if they are comma-delimited.
    Note that mixin files are not loaded on demand in the same way that controller files may be (but they will be compiled into bundles in the same way at build-time). In order for a mixin to be applied, it must be included in the page output. This means that if you're creating a plugin that (for example) has a mixin that applies to a core controller, you are responsible for ensuring the mixin file is included in the page as needed. This may mean creating a theme hook that modifies the output of the includeJS template.
  3. Like
    BomAle reacted to Rikki for a guide, ips.ui.dialog   
    Description
    The dialog widget displays popup windows within the page, typically loading content from a remote source.
     
    Usage
    A dialog is defined simply by including the widget key as an attribute on a trigger element. It is recommended that the trigger element be a link or button, so that if the user has javascript disabled their browser can take them to a full-page version of the dialog contents.
    <a href='...' data-ipsDialog data-ipsDialog-url='...'>Launch dialog</a> Dialogs offer special functionality for forms contained with the dialog, enabling them to be automatically validated and submitted via AJAX when used with the form helper from the IPS4 PHP framework. See the remoteVerify and remoteSubmit options below.
     
    Obtaining a dialog reference
    If you need to control a dialog programmatically, you can do so by first obtaining the reference to the dialog. To do so, call the getObj method, passing in the element on which the dialog was created as the parameter:
    // HTML <div id='elementWithDialog' data-ipsDialog> ... </div> // Javascript var dialog = ips.ui.dialog.getObj( $('#elementWithDialog') ); The returned reference to the dialog instance can be used to call the methods shown below.
     
    Creating a dialog programmatically
    Dialogs can be created programmatically by controllers instead of being created on particular elements. To achieve this, call the create method:
    var dialog = ips.ui.dialog.create( object options ); The options parameter should be an object containing keys for the options below.
    This method returns a reference to the dialog instance on which you can call methods to control the dialog, for example:
    dialog.show(); dialog.hide();  
    Instance methods
    show( boolean initOnly )
    Initializes and shows the dialog. If initOnly is true, the dialog is initialized but not immediately shown.
     
    hide()
    Hides the dialog.
     
    remove( boolean hideFirst )
    Removes the dialog from the DOM. If hideFirst is true, the dialog will call hide() and remove itself after the animation is completed.
     
    setLoading( boolean loading )
    If loading is true, indicates to the user that something is being loaded (i.e. shows a spinner in the dialog). If loading is false, removes the loading state. Note: this method doesn't empty the dialog content first. Call instance.updateContent('') manually if desired.
     
    updateContent( string newContent )
    Updates the contents of the dialog with the provided newContent. New controllers/widgets are automatically initialized after updating.
     
    Options
    url
    (String; optional)
    If provided, the dialog contents will be loaded by calling this URL when the dialog is launched.
     
    content
    (Selector; optional)
    If the dialog content already exists in the DOM, this option should be a selector that will locate the wrapper element for the content.
     
    modal
    (Boolean; optional; default true)
    If true, the page background will fade, preventing the user from interacting with it until the dialog is dismissed.
     
    title
    (String; optional)
    Sets the title shown in the dialog window. If not set, no title bar will display.
     
    size
    (String; optional)
    If provided, will set a special class on the dialog designed to change its size. Currently, narrow, medium, wide and fullscreen are supported as values for this option. If not provided, the default dialog width will be used (defined in CSS).
     
    close
    (Boolean; optional; default true)
    Determines whether the dialog should be built with a close button.
     
    fixed
    (Boolean; optional; default false)
    Determines whether the dialog is fixed. A fixed dialog is anchored to the viewport, not the page. Its height will also be fixed, with the content scrolling inside if necessary. If this is false, the dialog will scroll with the page, and it will expand to fit all the content being shown, regardless of length.
     
    remoteVerify
    (Boolean; optional; default true)
    When the dialog contains a form built using the IPS4 PHP framework form helper, this option instructs the dialog to automatically validate the form values and present any errors to the user.
     
    remoteSubmit
    (Boolean; optional; default false)
    When the dialog contains a form built using the IPS4 PHP framework form helper, this option instructs the dialog to submit the form values via AJAX. If remoteVerify is also true, two separate AJAX requests are fired; first, the form is validated, and if successful the form is submitted with a second request.
    If remoteSubmit is true, after a successful submit the dialog is closed automatically. If the flashMessage option is provided, it will be shown at this point.
    By default, remoteSubmit is false, which means forms submit via a traditional page reload.
     
    callback
    (Function; optional)
    Specifies a callback function that will be executed after the dialog has loaded remote content. Note: this option cannot be provided via the data API, and is therefore only available when interacting with ips.ui.dialog directly.
     
    forceReload
    (Boolean; optional; default false)
    By default, once a dialog has loaded its content, it will not do so again even if the dialog is relaunched. By setting this option to true, the dialog will call the url every time it is opened. This option only takes effect when the url option is used.
     
    Events emitted by ips.ui.dialog
    openDialog
    Triggered when the dialog is shown.
    Event data:
    elemID
    ID of the element that triggered the dialog dialogID
    ID of the dialog element dialog
    Reference to the dialog element contentLoaded
    Boolean indicating whether the dialog content has been loaded (may be false if remote content is used)  
    hideDialog
    Triggered when the dialog is hidden.
    Event data:
    elemID
    ID of the element that triggered the dialog dialogID
    ID of the dialog element dialog
    Reference to the dialog element  
    dialogContentLoaded
    Triggered after remote content has been loaded and inserted into the dialog.
    Event data:
    elemID
    ID of the element that triggered the dialog dialogID
    ID of the dialog element dialog
    Reference to the dialog element contentLoaded
    Boolean indicating whether the dialog content has been loaded (always true here)  
    Events to which ips.ui.dialog responds
    closeDialog
    Instructs the dialog to close.
    Event data:
    dialogID (required)
    The ID of the dialog to close (the event is ignored if the ID does not match the current dialog)
     
  4. Like
    BomAle reacted to Rikki for a guide, UI widgets   
    What's a widget?
    UI widgets are modules that:
    Are instantiated on individual DOM nodes Provide some kind of UI functionality Benefit from being reusable Come with an automatic data API functionality A UI widget is created as a standard module, under the ips.ui namespace.
    Basic widget boilerplate
    ;( function($, _, undefined){ "use strict"; ips.createModule('ips.ui.widgetName', function(){ var respond = function (elem, options, e) { }; // Register this module as a widget to enable the data API ips.ui.registerWidget( 'widgetName', ips.ui.widgetName ); return { respond: respond }; }); }(jQuery, _)); In the example above, a module is created with the module path ips.ui.widgetName. UI widget modules must expose a response method, either the default respond method or by manually specifying a method when the widget is registered (see below). This is the method that is called when the widget is instantiated.
    Registering a UI widget
    A module is registered as a widget by calling the ips.ui.registerWidget method before the module returns its public methods object.
    ips.ui.registerWidget( string widgetKey, object handlerModule [, array acceptedOptions] [, object widgetOptions] [, function callback] ); The method takes the following parameters:
    widgetKey (required)
    The key that is used to reference this widget throughout the software. The key is prefixed with ips when used, to prevent naming collisions. The key forms both the data API attribute key, and the name of the jQuery plugin that can be used to instantiate the widget. Assuming the widget key is set to widget, the data API attribute key and jQuery plugin would be: data-ipsWidget // Data API attribute $('element').ipsWidget(); // jQuery plugin method  
    handlerModule (required)
    A reference to the module that will respond when the widget is instantiated. In practice, this should be a self reference to the module that is being defined.
    acceptedOptions (optional)
    An array of strings defining the options that this widget will accept. When the widget is instantiated, if these options are defined their values will be passed back to the respond method.
    widgetOptions (optional)
    An object containing options for how this widget will be registered or instantiated. The available options are:
    lazyLoad
    By default, widgets are instantiated as soon as they are seen in the DOM. By setting this option to true the widget is not instantiated until the user interacts with the element.
    lazyEvent
    If the lazyLoad option is enabled, this option defines the event that will be watched for. When the event name set here is triggered, the widget will be initialized.
    makejQueryPlugin
    By default, widgets will have an associated jQuery plugin created to allow them to be instantiated programatically. This functionality can be disabled by setting this option to false.
    callback (optional)
    A widget must have a response method, which is called when it is initialized on an element. By default, a method with the name respond is looked for, but a different response function can be set by passing it as this parameter.
    Events
    The javascript framework in IPS4 relies heavily on events. Controllers in particular are completely decoupled and cannot directly communicate with one another. Instead, communication happens by triggering and receiving events.
    It is therefore important that your widget emits events when key actions happen. This allows controllers, other UI widgets, even third-party scripts to respond when something happens within your widget. As an example, the ips.ui.menu widget emits a number of events, including menuOpened, menuClosed and menuItemSelected. Each of these events also provides a number of data items relevant to the event, which can be used by event handlers listening for those events.
    It is this event interaction that facilitates dynamic pages within the IPS Community Suite, so it's important your widget also emits relevant events.
    When you emit an event, it is usually most appropriate to emit it on the element the widget is registered on. Emitting an event uses the standard jQuery syntax:
    $( elem ).trigger( 'myWidgetEvent', { color: 'red', size: 'large' } ); Widget initialization
    A widget is initialized either as soon as it is seen in the DOM, or, if the lazyLoad option is enabled, when the user interacts with the element the widget is created on. In both cases, the initialization process is the same. The internal widget manager will call the response method for the widget (respond by default, or whatever function is passed as the callback option), passing in the following parameters:
    function respond( element elem, array options, event ev ) elem
    A reference to the element on which the widget has been initialized options
    An array of options that have been specified on the element, and which were in the list of expected options when the widget was first registered. Any other options are ignored and won't be passed through. ev
    If the widget is initialized in response to a user interaction because lazyLoad is enabled, this parameter will contain the original event object. It will be undefined if the widget is being initialized on load. Example widget
    Let's take a trivial example, and assume we're creating a widget which hides an element:
    ;( function($, _, undefined){ "use strict"; ips.createModule('ips.ui.hideElem', function(){ var respond = function (elem, options, e) { if( options.animate ){ $( elem ).fadeOut(); } else { $( elem ).hide(); } $( elem ).trigger( 'hiddenElement' ); }; ips.ui.registerWidget( 'hideElem', ips.ui.hideElem, [ 'animate' ], { lazyLoad: true, lazyEvent: 'click' } ); return { respond: respond }; }); }(jQuery, _)); When we register the widget, we set up an animate option in the acceptedOptions parameter. In the widgetOptions parameter, we set lazyLoad to true, and the lazyEvent to click - meaning our widget won't be initialized until the user clicks on the element.
    In our respond method, we simply check if options.animate is true, fading out if so and hiding if not.
    This example widget could be initialized on an element like so:
    <button data-ipsHideElem data-ipsHideElem-animate='true'>Click to hide me</button>  
  5. Like
    BomAle reacted to Rikki for a guide, Controllers   
    Overview
    Controllers are special modules that handle specific functionality on specific pages. They are not necessarily reusable in different contexts, although some may be. A controller is initialized on an individual element, and that element becomes the controller's scope. A controller responds to user events (such as click) and events triggered by UI widgets or sub-controllers, and manipulates its scope element accordingly.
    The scope of a controllers functionality is entirely flexible. A controller might be initialized on a very small fragment of the page and perform just one task, or it might be initialized on a main wrapper and handle functionality that applies to the whole page. A controller can be initialized on an element even if it's a descendent of another element with a controller; this is a common pattern whereby the child controller can emit events that the parent controller can respond to.
    Generally, keep a controller narrowly focused on one aspect of the page. If a controller ends up handing distinct and unrelated functionality, split it into smaller controllers.
    Creating a controller
    A controller is registered in a slightly different way to standard modules; instead, an object literal is passed to the method ips.controller.register:
    ;( function($, _, undefined){ "use strict"; ips.controller.register('type.controllerID', { initialize: function () { // Controller events are initialized here } }); }(jQuery, _)); A controller, at the very least, must define an initialize method, which is called when the controller is initialized on an element. The initialize method should set up events that are handled by this controller only; if other initialization tasks are necessary, it is recommended that you define a setup method within the controller, and call that at the start or end of the initialize method as appropriate.
    Within the controller definition, this refers to the controller instance. That means controller methods can be called internally with this.someMethod().
    A reference to the element that the controller is initialized on (its scope) is available with the scope property:
    // An example which hides the element this.scope.hide(); Method and property names
    For clarity, it is recommended that event handler method names (i.e. the handlers to events you set up in initialize) are not prefixed with an underscore, while other methods and properties of the controller are. This helps create a clear distinction between core functionality of the controller (the event handlers) and supporting methods.
    Handling events
    Event handling is the core purpose of a controller. There are three special methods available for watching and triggering events. Two are named after their jQuery counterparts, but add controller-specific behavior.
    this.on
    Used for watching and responding to events.
    this.on( [element elem,] string eventType [, selector delegate], function callback ); elem
    A reference to the element on which the event will be watched for. If not specified, this.scope is used by default. eventType
    The event being watched for. This can be a default browser event, like click, or events from widgets and models, like menuItemSelected. delegate
    A delegate selector can optionally be specified. See the jQuery documentation for more information. callback
    A callback function, called when this event is observed. The function specified here is automatically bound to this, so internal methods can be passed simply by referencing them like so: this.on( 'click', this.internalMethod );  
    this.trigger
    Used to emit events that originate from this controller (specifically, the scope element).
    this.trigger( [element elem,] string eventType [, object data] ); elem
    A reference to the element on which the event will be triggered. If not specified, this.scope is used by default. eventType
    The event being triggered. data
    An object containing data relevant to the event.  
    this.triggerOn
    Used to emit events directly on sub-controllers from a parent controller.
    this.triggerOn( string controllerName, string eventType [, object data] ); controllerName
    The controller name to find and trigger the event on. The wildcard character (*) is supported at the end of this parameter and the event will be triggered on all matching controllers. Examples: core.front.core.comment core.front.core.* core.* eventType
    The event being triggered. data
    An object containing data relevant to the event.
×
×
  • Create New...