IPS Staff
  • Content count

  • Joined

  • Last visited

About Rikki

  • Rank
    data-type='member title'
  • Birthday 08/17/1983

Contact Methods

IPS Marketplace

  • Resources Contributor Total file submissions: 3

Profile Information

  • Gender Male
  • Location Lynchburg, VA :o
  1. ips.ui.infinitescroll

    Description Infinite scrolling is a technique whereby new content is loaded into the page automatically when the user scrolls near the bottom of the page or section. It's a technique commonly associated with sites like Facebook and Twitter. Note: Infinite scrolling can present user interface challenges if not used wisely, including not being able to reach links at the bottom of the page. Before choosing to use infinite scrolling, consider the usability impacts it may have. It may be more appropriate to display a button at the bottom of your content inviting a user to load more content manually. In order to progressively enhance the page, and ensure users with javascript disabled can still access content, infinite scrolling in IPS4 works with the standard pagination markup.   Usage The infinite scroll widget works by having the content inside of a scroll scope element. The element has a fixed height, and hides its overflow so that it scrolls. The content is placed inside a container element and is an arbitrary length, and as the user scrolls and the end of the container approaches the bottom of the scroll scope, the widget will load more content to display. A standard pagination control should exist inside the scroll scope element; it can be hidden from javascript-enabled browsers by placing it inside a noscript tag.   Options scrollScope (Selector; optional; default 'body') A selector that idenfities the element which will be the scroll scope; that is, the element that has a fixed height and which the user will scroll to show more content.   container (Selector; required) A selector identifying the container element inside which your content should exist.   distance (Mixed; optional; default 50) A value that determines how close to the bottom of the scrollScope the container must be to trigger the loading of new content. The value can either be a number (e.g. 50) or a percentage (e.g. 10%) which represents a percentage height of the scrollScope element.   url (String; required) The URL that is called to retrieve more content from the server.   pageParam (String; optional; default 'page') The parameter used to pass the page value when retrieving more content from the server.   loadingTpl (Template key; optional; default 'core.infScroll.loading') The template used to render the 'loading' text when the widget is currently loading new content.   pageBreakTpl (Template key; optional; default 'core.infScroll.pageBreak') The template used to render the breaks between each 'page' of content as it is inserted into the container to aid the user experience.   disableIn (String; optional; default 'phone') Specifies which responsive views, if any, the infinite scroll behavior is disable in. Value should be one or more registered breakpoint keys (phone, tablet or desktop), comma delimited.   Events emitted by ips.ui.infScroll infScrollPageLoaded Triggered when a new page of content has been loaded. Event data: page The number of the most current page (i.e. the one just loaded).
  2. ips.ui.hovercard

    Description The hovercard widget can be used to display a popup with remote content, which is attached to a trigger element.   Example @todo   Usage The hovercard widget is initialized by including the widget key attribute on a link: <a href='...' data-ipsHover>Hover on me</a> The URL that the link points to is also the URL that is called to fetch the hovercard content, by default.   Options target (String; optional) If a URL other than the link's href should be used to load the hovercard content, it can be specified in this option.   content (Mixed; optional) Sets the content of the hovercard. If a selector is provided, the contents of the matching element will be inserted into to hovercard. Alternatively, a string can be supplied which will be inserted into the hovercard. Using this option means remote content won't be loaded by the hovercard.   timeout (Number; optional; default 0.75) The number of seconds between the user hovering on the element and the hovercard beginning to load (and conversely, hiding when the mouse leaves the element).   attach (Selector; optional) The element that the hovercard will be attached to. If not specified, the element that the widget is initialized on will be used.   width (Number; optional; default 300) The width in pixels at which the hovercard will display.   onClick (Boolean; optional; default false) If true, the hovercard will be activated only when the user clicks on the element. By default, the hovercard will show on hover.    
  3. ips.ui.flashMsg

    Description The flashMsg widget can be used to display a 'flash message' to the user - a short notification that appears on the screen for a few seconds, before disappearing. It is commonly used for 'success' messages when an action occurs.   Example @todo   Usage A flash message can be created with the data API, as follows: <span data-ipsFlashMsg data-ipsFlashMsg-text='My message'> The element used with this method is not important; the widget will not interact with the element in any way. However, in most cases, flash messages will be created on the fly in response to user actions, so the more common use will be calling them programatically. This can be done in javascript modules with the following code: ips.ui.flashMsg.show('My message'); Flash messages can be automatically created in two additional ways: Cookies - set a cookie with the key flmsg. The cookie will be deleted after the message displays. URL - pass the parameter flmsg in the page URL, with the value being the message to show.   Options text (String; required) The text to display in the flash message. A sentence or two maximum is recommended.   extraClasses (String; optional; default 'ipsPad') A space-separated string of extra CSS classnames that will be applied to the flash message box.   timeout (Number; optional; default 2) The number of seconds for which this flash will be shown before hiding itself.   Events closeFlashMsg.flashMsg Hides the currently-shown flash message. If any messages are queued, the next will be shown automatically.
  4. 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)  
  5. ips.ui.chart

    Description The Chart widget takes a specially-formatted table of data, and uses Google Charts to render it as an attractive chart. The Chart widget is designed for use soley with the output of the \IPS\Helpers\Chart class and is documented here for completeness only. Consult the documentation for the PHP class for more information.   Options type (String; required) Specifies the type of chart to render, e.g. LineChart   extraOptions (Object; optional) Object of extra options passed through to the Google Charts library.   Events chartInitialized Triggered after the chart has been initialized.
  6. ips.ui.captcha

    Description The Captcha widget is designed to allow captcha to be used dynamically, including situations where it is initialized after the page has loaded (e.g. in popups). The necessary javascript libraries from providers are loaded on the fly. The widget presently supports reCaptcha and KeyCaptcha.   Usage The widget is initialized as follows: <div data-ipsCaptcha data-ipsCaptcha-service='recaptcha' data-ipsCaptcha-key='...'> </div>   Options service (String; required) Specifies the captcha service to initialize. Either recaptcha or keycaptcha.   key (recaptcha only) (String; required) Specifies the reCaptcha API key to use. Not used for KeyCaptcha.   lang (recaptcha only) (String; optional; default 'en-US') Specifies the language to load for reCaptcha. Not used for KeyCaptcha.   theme (recaptcha only) (String; optional; default 'white') Specifies the theme to load for reCaptcha. Not used for KeyCaptcha.   Events The Captcha widget does not emit any events.
  7. ips.ui.autoComplete

    Description The autocomplete widget adds two groups of functionality to a text input. Firstly, it adds support for suggestions based on what the user is typing, and secondly, it adds support for tokenization, allowing the user to enter multiple distinct values into one field. This functionality is useful for entering tags, for example.   Example @todo   Usage The autocomplete widget should be initialized on a text input with the correct widget key attribute: <input type='text' placeholder='Enter text to search' name='field_name' data-ipsAutocomplete data-ipsAutocomplete-dataSource='...'>   Obtaining an autoComplete reference If you need to control an autoComplete programmatically, you can do so by first obtaining a reference to the instance. To do so, call the getObj method, passing in the element on which the dialog was created as the parameter: // HTML <input type='text' id='inputWithAutocomplete' data-ipsAutocomplete> // Javascript var autocomplete = ips.ui.autocomplete.getObj( $('#inputWithAutocomplete') ); You can then call the methods below on the returned reference.   Methods addToken( string value ) Returns boolean; true on success. Adds a token to the autocomplete with the provided value.   getTokens() Returns array containing values. Fetches all of the token values currently present in the autocomplete.   removeToken( element token ) Returns void. Removes the given token element from the autocomplete. Note that this method expects the DOM/jQuery token element, not the value.   removeAll() Returns void. Removes all tokens from the autocomplete.   Options dataSource (Mixed; optional) Specifies the data source that the autocomplete widget should use for suggestions. Possible values: URL - when a URL is supplied, the widget will fetch results from this address with the current value as the parameter q, expecting a JSON object in response Selector - which locates a datalist element on the page, the options of which will provides the results None - initializes the autocomplete widget with no dataSource, meaning suggestions will not be offered to the user   maxItems (Number; optional) Specifies the maximum number of tokens the user can enter into the field. When the maximum is reached, no further tokens can be added.   minLength (Number; optional) Specifies the minimum length a value must be, when freeChoice is true.   maxLength (Number; optional) Specifies the maximum length a value can be, when freeChoice is true.   unique (Boolean; optional; default false) Determines whether tokens entered must be unique. If true, a duplicate will not be permitted and a tooltip will inform the user why. In addition, when a token is used it is automatically removed from the list of suggestions offered for further tokens.   freeChoice (Boolean; optional; default true) If true, the user will be able to enter their own values. If false, they will only be able to select values from the suggestion list. e.g. @todo   forceLower (Boolean; optional; default false) Forces all tokens entered to be lowercase.   resultsElem (Selector; optional) By default, when suggestions are presented they will appear in a floating list attached to the text field. If a selector is provided in this option, the results will instead be inserted into the element it locates.   queryParam (String; optional; default 'q') If a URL is used for dataSource, this parameter provides the parameter name used for the lookup e.g. index.php?q=<term>.   addTokenText (Template key; optional; default 'core.autocomplete.addToken') If freeChoice is false, this option specifies the template that will be used to build the link that the user will click to display the suggestions list.   fieldTemplate (Template key; optional; default 'core.autocomplete.field') The key to the template that will be used to build the widget wrapper (into which the original text field is inserted).   resultsTemplate (Template key; optional; default 'core.autocomplete.resultItem') The key to the template that will be used to build an item within the suggestions list. For example, in a list for selecting colors, the template might show a swatch alongside the name.   tokenTemplate (Template key; optional; default 'core.autocomplete.token') The key to the template that will be used to render an individual token when it is created in the field.   Events Events are emitted on the original text field. autoCompleteReady Triggered when the autocomplete widget is ready for interaction (i.e. the wrapper is built and existing values have been constructed and inserted). Event data: elemID ID of the element the widget was initialized on elem Reference to the above element currentValues Array of current values in the widget (in this case, values that already existed when the widget was initialized)   tokenAdded Triggered when a token is created in the text field, either by selecting a value or hitting the delimiter character (comma by default) Event data: token The value of the token that has been added tokenList Array of all current token values totalTokens Count of the number of tokens added html The HTML representing this item in the results list   tokenDeleted Triggered when a token is deleted Event data: token The value of the token that has been deleted tokenList Array of all current token values totalTokens Count of the number of tokens added
  8. ips.ui.autoCheck

    Description The autoCheck widget makes it simple to select checkboxes of a certain type on a page. This is useful for moderation tools, for example - simply by specifying a filter, topics that match that filter can be selected. Note: The autoCheck widget works in conjunction with the ips.ui.menu widget. It will not work on other kinds of elements. Example @todo Usage The widget itself is initialized on the menu trigger element: <!-- The trigger --> <a href='#elMyMenu_menu' id='elMyMenu' data-ipsMenu data-ipsAutoCheck data-ipsAutoCheck-context='#elContext'> Open Menu </a> Each menu option then receives the special data attribute data-ipsMenuValue (as described in the menu widget documentation) which contains the filter type that the option will check: <li class='ipsMenu_item' data-ipsMenuValue='unapproved'> <a href='...'>Has Unapproved</a> </li> And finally, each checkbox within the context element (see options below) can receive another special data attribute data-state, to indicate which filters it matches: <input type='checkbox' data-state='unread unapproved'> Multiple filter values are simply separated by a space.   Options context (Selector; required) A CSS selector which indicates the wrapper element that contains all of the checkboxes which will be checked for filter matches. For example, if a Data List structure is used, this option might be set to the ID of the root list element.   Events Events are emitted on the menu trigger element. autoChecked Triggered when the user selects an option that has a data-ipsMenuValue attribute. Event data: menu Reference to the menu trigger element currentFilter The filter that has been selected count The number of checkboxes that matched
  9. ips.ui.alert

    Description The alert widget is designed to replace the built-in javascript alert, providing a more pleasant user experience as well as more flexible options and built in callbacks. Several other types of alert dialogs are supported too. Usage Unlike most UI widgets, the alert widget is instantiated programatically, rather than using the data API - typically as a response to a user action in a controller, for example. An alert is created as follows: ips.ui.alert.show( { type: 'alert', icon: 'warning', message: 'Alert message', subText: 'Message subtext', callbacks: { ok: function () { // Do something } } }); Each type of alert shows different buttons depending on the use; a callback can be assigned to each button shown for that type. Alert types Alert Shows a message, with a single button to dismiss the alert. Type: alert Available button callbacks: ok   Confirm Confirm shows a message with OK and Cancel buttons. Type: confirm Available button callbacks: ok, cancel   Prompt Shows a message with OK and Cancel buttons, and prompts the user to input a value. Both callbacks receive the entered value as the first parameter. Type: prompt Available button callbacks: ok, cancel   Verify Verify shows a message with Yes and No buttons. Type: verify Available button callbacks: yes, no   Events No events are emitted by the alert widget; since callbacks are supported, custom events can be triggered inside them to achieve desired functionality.
  10. Introduction

    Initializing widgets UI widgets can now be created on elements without needing to write any code. This is done by specifying special attributes on the element using the HTML5 data attributes. Widgets each define their own behaviors - some wait for user interactions, others might immediately change the display when executed. Usage Each UI widget has a base attribute that identifies it, and causes the element to be passed to the widget module to be handled. All widget attributes have the prefix ips. <!-- If a widget called 'Widget' existed, we could initialize it on an element like this --> <div data-ipsWidget> ... </div> In this example, ipsWidget would be referred to as the widget key. The data- part of the HTML attribute is implied throughout the documentation for individual widgets. Each element can have multiple widgets specified; simply add each necessary widget prefix to the element as above. That said, bear in mind multiple widgets on a single element might cause conflicts if more than one responds to a user interaction. Test to be sure. Note: In many cases, the helpers available in the IPS4 PHP framework will generate all of the required HTML for you, including widget attributes, meaning you won't need to manually specify UI widgets in your HTML. For example, the autocomplete form input type adds the necessary attributes, so you don't need to handle it yourself. Consult the PHP documentation for more information. Passing options Most widgets have a number of options that control their behavior. To pass an option, use this format: <div data-ipsWidget data-ipsWidget-option1='Option value' data-ipsWidget-option2='Another option value' > ... </div> Each option is added as an additional attribute on the element, with the attribute key being data-ipsWidget- followed by the option name. By default, options are passed as strings because they come from the DOM. However, if the option contains only numbers, it will be cast explicitly as a number when passed into the widget. If the option contains the strings true or false, it will be cast as a boolean. In addition, options without a provided value are treated as the boolean true, meaning it is possible to specify true values like so: <div data-ipsWidget data-ipsWidget-showWide> Automatic initialization Widgets are automatically initialized when the data API is used as described. Further to that, when new content is loaded into the page (such as when an AJAX request fires), any widgets within the new content will also be automatically initialized.
  11. 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>  
  12. 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.
  13. Note: This documentation assumes you are running your installation with IN_DEV enabled. Due to the multi-application model that the IPS Social Suite employs, and the way that resources are bundled and served to the browser, there are a number of places in which different types of javascript files are stored. Development & production When developing for IPS4, you create your javascript files in the application or global directories, with a single file holding a single module. In production, however, IPS4 bundles related files together (concatenating and minifying them in the process) to reduce file size and the number of HTTP requests needed to fetch the scripts. You should be aware of how the bundles are built and from where, because this will dictate how you specify javascript files to load in your backend PHP code. Global resources Global resources are those that are loaded on every page load. This includes: Libraries - jQuery, Underscore etc. Application setup - Bootstrap module, loader module, base controller etc. Global modules - UI widgets, utilities, global controllers etc. Front/admin modules - Controllers global to the front-end/admin control panel, needed on every page in that area. All of the above files are located at <root>/dev/js/. Application resources Applications have their own folder for javascript, located at <appName>/dev/js. Within this folder, there should be three subfolders - admin, front and global. These folders hold javascript specific to those areas (or for the whole application, in the case of global). Within each of those folders respectively, there are further subfolders for different types of javascript file - controllers, templates and mixins. Therefore, within an application, the file structure looks like this: /appName /dev /js /front /controllers /profile ips.profile.main.js ips.profile.body.js /templates ips.templates.profile.js /mixins /admin /controllers /templates /mixins /global /controllers /templates /mixins Bundles for an application are built based on folder/filenames, and use the format <location>_<directory>.js. Taking the structure above as our example, if we loaded front_profile.js, it would contain: /js/front/controllers/profile/ips.profile.body.js /js/front/controllers/profile/ips.profile.main.js /js/front/templates/ips.templates.profile.js This bundle would then be included in your PHP code like so: \IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'front_profile.js', 'core' ) ); Third-party libraries An application has an interface directory at <appName>/interface/. This is where third-party libaries used by individual applications should be stored and loaded from. They are not bundled or minified by IPS4, so you should store the pre-minified rather than the uncompressed version. Assuming you had added a file to interface at <appName>/interface/jquery/jquery.rangyinputs.js, it can be included in your PHP code like so: \IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'jquery/jquery.rangyinputs.js', 'appName', 'interface' ) );  
  14. Global scope & closure wrapping Variables must not be leaked into the global scope unless there is an explicit functional need for global access. Within function scope, variables must be declared with the var statement. To help prevent scope leaking, each script file should be entirely wrapped in an anonymous, self-executing closure, passing the jQuery and Underscore objects as parameters: ;( function($, _, undefined){ // Code goes here }(jQuery, _)); This prevents variables from leaking into the global scope, and also ensures jQuery code making use of the dollar variable $ does not conflict with any other code that might be running on the page. Note that a semi-colon appears as the first character. This helps prevent errors caused by incorrect semi-colon insertion in previous code, should this script be concatenated with others. It is a defensive coding practice, and recommended in all files. Strict mode The use of strict mode is required for all default javascript in IPS4. Strict mode helps prevent leaking into the global scope, catches code problems that would otherwise fail silently, and enables javascript engines to perform more optimizations. However, it is important that this is applied using the per-function syntax and not the per-script syntax, otherwise unexpected behavior can occur when script files are concatenated with 3rd-party scripts. Given that all script files should be wrapped in a self-executing closure, the syntax for enabling strict mode looks like this: ;( function($, _, undefined){ "use strict"; // Script here, e.g. module definition etc. }(jQuery, _)); By putting the "use strict"; statement inside the the closure, we ensure all of our code runs in strict mode without affecting any external code by accident. Note that the strict mode statement must be the very first line of the function, as shown. Further information about strict mode and the code requirements it imposes is available at MDN. Documentation & commenting The use of JSDoc is highly encouraged to document code. Refer to the JSDoc documentation for exact syntax. Within scripts, comment blocks /* */ should be reserved for formal documentation and situations where large blocks of existing code need to be commented out temporarily. Line-by-line comments should always use the single-line // comment style. /** * Formal documentation */ // Informal comments to describe code Semi-colons Semi-colons must be used at the end of every statement, even when function/object literals are being assigned. For example: // Simple statements $('#element').doSomething(); var test = 'value'; return 'text'; // Function and object literals var test = function () { ... }; var test2 = { key1: 'value' }; Names All names (variables, methods, properties etc.) should use lowerCamelCase, such as deselectAll, createModule and itemSelector. Logging You should not use console.log for debugging. This does not provide for disabling debugging in production, and can break pages on older browsers if not accounted for. A debugging method is provided via Debug.log that addresses these concerns, and should be used instead. Line length Aim to keep the line length below 120 characters to maintain readability. Longer lines should be split immediately after a comma or operator, with the remainder of the statement indented further to ensure clarity. For example: if( ( options.dataSource && options.dataSource.indexOf('#') === 0 && $( options.dataSource ).length ) || originalTextField.is('[list]') ){ jQuery chaining It is recommended that multiple jQuery methods chained on an element are placed on newlines to improve clarity. Additionally, indentation should be used to reflect the current working level of the chain. For example: $('selector') .find('.classname') .hide() .end() .find('.otherClassname') .css('width', '100%') .show() .end() .append( $('<div/>').attr( { id: 'someID' }); By indenting in this way, we make it clear that the hide() method applies only to the elements matching .classname, before calling end() to return to the original working level - and so on.
  15. In IPS4, most javascript is split into self-contained, named modules. Modules and javascript files have a one-to-one relationship - each javascript file should contain one module only. Modules prevent variables leaking into the global scope (which must be avoided), and helps ensure code stays focused. All modules appear under the ips namespace. Types of Module A number of primary types of module exist within the software: Utility Modules Exist on the ips.utils namespace, and provide assorted functionality that other modules can make use of, including cookie handling and methods for handling responsive design. UI widget modules Exist on the ips.ui namespace, and define interface widgets to add interactivity to pages, and which can be reused using special data attributes. Controllers Controllers are modules that handle specific functionality for one particular page or part of a page. They respond to events from UI widgets and other controllers and update their scope accordingly. Mixins Mixins are used to extend controllers with additional functionality. Other types There are a number of other types of non-modularized file used too: Libraries/interfaces Libraries are 3rd party scripts that provide additional functionality. jQuery and Underscore are two examples. Templates Template files define special Mustache templates, that modules can use when rendering HTML to the browser. Languages Language files define strings which can be translated into other languages.