Application Interface

The main module of every Pink application must inherit its prototype from the Pink.App module. The app's modules should require this module in their dependencies to access the framework's public interface.

Attributes

  • modalModule : object
    Exposes a generic modal viewmodel for databinding, usually used in the application main layout. Must be bound to a DOM element for the app's showModalWindow method to work.
    <div data-bind="module: app.modalModule"></div>
  • alertModule : object
    Exposes a confirm modal viewmodel for databinding, usually used in the application main layout. Must be bound to a DOM element for the app's showConfirm method to work.
    <div data-bind="module: app.alertModule"></div>
  • infoModule : object
    Exposes an informative modal viewmodel for databinding, usually used in the application main layout. Must be bound to a DOM element for the app's showInfoBox method to work.
    <div data-bind="module: app.infoModule"></div>
  • rootRoute : string
    The default route to show when the app is loaded, or the url fragment is empty.
  • undefinedRoute : string
    The route to show when there's an non existent route on the url fragment.
  • mainModule: object
    Exposes the active route's module (taken from the url fragment) for databinding, usually used in the application main layout. Must be bound to a DOM element.
    <section data-bind="module: mainModule"/>
  • definedRoutes: object
    Exposes the application's defined routing parameters for databinding, usually used in the application main menu.
     <ul id="mainMenuDropDown" class="menu vertical white">
         <!-- ko foreach:  definedRoutes.visibleRoutes -->
             <li data-bind="css: { active: isActive }">
                 <a data-bind="attr: { href: '#'+hash }, html: caption"></a>
             </li>
         <!-- /ko -->
     </ul>
                                        

Methods

  • showInfoToast(message) : void
    Shows a temporary notification panel in the UI for information purposes.
    The app's DOM must have an element with an id toastPanel, usually in the main layout.
    app.showInfoToast('Hello there!');
  • showErrorToast(message) : void
    Shows a temporary notification panel in the UI for application error purposes.
    The app's DOM must have an element with an id toastPanel, usually in the main layout.
    app.showErrorToast('Oops, an error occurred :(');
  • showSuccessToast(message) : void
    Shows a temporary notification panel in the UI to notify the user that some action completed with success.
    The app's DOM must have an element with an id toastPanel, usually in the main layout.
    app.showSuccessToast('Document saved');
  • showModalWindow(title, moduleName, params, modalStyle) : void
    Shows a modal window dialog with a custom module in the contents area.
    • title - string containing the modal's title
    • moduleName - string containing a valid Pink UI module name (eg. 'App.Tasks.EditDialog')
    • params - object with the parameters to pass to the module
      Goto the ModalWindow section for more detais on the modal's contents module interface
    • modalStyle - object with modal window style parameters
      {width : string, height : string, cancelVisible : bool, confirmCaption : string}
    See the modalModule attribute
    app.showModalWindow('Edit item', 'App.Item.Edit', {item: item}, {width: '600px', height: '400px'});
  • showMiniModalWindow(title, moduleName, params, modalStyle) : void
    Shows a mini modal window with a custom module in the content area.
    See showModalWindow for details and example usage
  • showSmallModalWindow(title, moduleName, params, modalStyle) : void
    Shows a small modal window with a custom module in the content area.
    See showModalWindow for details and example usage
  • showLargeModalWindow(title, moduleName, params, modalStyle) : void
    Shows a large modal window with a custom module in the contents area.
    See showModalWindow for details and example usage
  • showConfirm(title, message, confirmCallback, cancelCallback) : void
    Shows a modal window for the user to respond to a confirm action.
    • title - string containing the modal's title
    • message - string with the message for the user
    • confirmCallback - function to be called if the user confirms the action
    • cancelCallback - function to be called if the user cancels the action
    app.showConfirm('Please confirm...', 'Remove the item?', confirmHandler, cancelHandler);
  • showInfoBox(title, message) : void
    Shows a modal window with a simple message.
    • title - string containing the modal's title
    • message - string with the message for the user
    app.showInfoBox('User info', 'Here is some extra info');
  • showStandby() : void
    Shows a standby panel that disables user input.
    There must exist a DOM element with the id standbyLightBox for this method to work
  • hideStandby() : void
    Hides the standby panel.
    There must exist a DOM element with the id standbyLightBox for this method to work
  • listVisibleRoutes() : array
    This method should be overriden by the application main module, to return the routes to be rendered in the main application menu.
     Module.prototype.listVisibleRoutes = function() {
         return [
           {isActive: ko.observable(true), caption: 'To-do', hash: 'todo', module: 'App.Tasks.Home', arguments: {filter: 'todo'}},
           {isActive: ko.observable(false), caption: 'Incomplete', hash: 'incomplete', module: 'App.Tasks.Home', arguments: {filter: 'incomplete'}},
           {isActive: ko.observable(false), caption: 'Completed', hash: 'completed', module: 'App.Tasks.Home', arguments: {filter: 'complete'}}
         ];
     };
  • listInvisibleRoutes() : array
    This method should be overriden by the application main module, to return the routes not visible in the application's main menu.
     Module.prototype.listInvisibleRoutes = function() {
         return [
           {hash: 'users\\?search=:search', module: 'App.Example.ListUsers', parentModule: 'App.Example.ListUsers'}
         ];
     };
    If the invisible route's parentModule attribute is defined, it's used to set the parent module's route active flag to true.
  • addVisibleRoute(route) : void
    Allows a plugin to add a new visible route. The plugin should call this method on its initialization code
    app.addVisibleRoute({isActive: ko.observable(false), caption: 'Export', hash: 'export', module: 'App.Tasks.Plugins.Export'})
  • addInvisibleRoute(route) : void
    Allows a plugin to add a new invisible route. The plugin should call this method on its initialization code
    app.addInvisibleRoute({caption: 'Export item', hash: 'export\\?item=:item', module: 'App.Tasks.Plugins.ExportItem'})
  • addCustomSignals() : void
    Should be overriden in the application's main module, to define custom client side events for application UI logic.
     Module.prototype.addCustomSignals = function() {
         this.signals.taskAdded = new Signal();
         this.signals.taskUpdated = new Signal();
     };
                                            
  • navigateTo(path, options) : void
    Instructs the router to navigate to a new route inside the app.
    • path - string with url fragement
    • options - object {silent: bool} allows to change the url without the routing logic
    app.navigateTo('#home');
  • navigateToStart() : void
    Instructs the router to navigate to the route defined in the rootRoute attribute.
    app.navigateToStart();
  • listPluginModules() : array
    Should be overriden by the apps main module to return an array with the names of the modules to be loaded on the app's bootstrap.
  • run() : void
    Must be called on the main page to start the application bootstrap process.
     <script type="text/javascript">
         Ink.requireModules(['App.Tasks'], function(app) {
             app.run();
         });        
     </script> 
  • ready() : void
    Override this method to add your own custom initialization logic. If you override this method, you must call start() when ready.
     Module.prototype.ready = function() {
         var self=this;
         
         Ink.requireModules(['App.Images.View'], function() {
             self.start();
         });
     }; 

Routing

Pink provides an hash based routing module (Routie), that is used to manage the view change logic. The routing callback infers what is the active module from the window url hash fragment by looking at the application's defined visible and invisible routes.

All routes have a hash attribute that can be parameterized (eg. for invisible routes), a module attribute with the name of the Pink UI module to load, and an optional arguments attribute that can contain extra options to pass to the module.

The visible routes, should be used in the application layout module, to build a menu, by binding to the app's definedRoutes.visibleRoutes attribute. They can be defined in the app's listVisibleRoutes method, or by calling the addVisibleRoute method.

The visible route has two aditional attributes named 'isActive' and 'caption, that indicate what should be the selected menu option, and the menu item's caption respectively.

The invisible routes, defined in the app's listInvisibleRoutes, or by calling the addInvisibleRoute method, allow the definition of parameterized routes eg:

 {hash: 'users\\?search=:search', module: 'App.Example.ListUsers', parentModule: 'App.Example.ListUsers'}
                                
In this example,for the route to match, the defined hash must have a search term, that is passed to the module's options.

In addition, the invisible route can provide an optional parentModule attribute, that tells the framework what should be the selected menu option relating to this route.

The routing callback is also responsible for updating the app's mainModule attribute with the module's name and parameters, notifing the "module" binding handler to reload.

The module parameters are passed as an object to the module's constructor or to module's "activate" method, in case of a class or static module respectively.

Signals

Although not required, we suggest you adopt a decoupled modular arquitecture for your Pink projects through the use of a client side application event bus.

The main beneficts of an event bus are:
- Public interfaces for client side actions (eg. File saved event).
- Each module needs only a limited knowledge about the rest of the aplication, besides the main application interface and available signals, allowing easy implementation exchange. (eg. swap a global search widget with a voice recognition service).
- Easier unit testing because there are less dependencies between the modules, and the events can be readly emitted in tests.

Every Pink application has the following events:

  • viewChanged(route) - Emitted by the framework when the router detects a new route.
  • shellRendered - Emitted when the application main layout is rendered (usually in the Shell module).
  • appReady - Emitted by the framework after the initial modules and plugins have loaded

To register new events your application must assign a new instance of "Pink.Plugin.Signals_1" to app.signals.<signal-name>. This is usally done by overriding the addCustomSignals application's method.

Module.prototype.addCustomSignals = function() {
    this.signals.taskAdded = new Signal();
    this.signals.taskUpdated = new Signal();
};
                        

When you need to emit a registered event (eg. when a task is updated), you should call the signal's dispatch method, passing additional event parameters if needed.

app.signals.taskSaved.dispatch(task);
                        

To subscribe to an event your module needs to call the signal's "add" method with a callback function.

app.signals.taskSaved.add(taskSavedHandler);
                        

To remove a previous subscription call the signal's "remove" method.

app.signals.taskSaved.remove(taskSavedHandler);

For more details about the Signals module go to the js-signals project page.

Custom UI Modules

Pink's UI modules are created by a module binding handler (Knockout extension), this handler dinamically loads and binds an Ink's class or static module file (the view model) named 'lib.js', to a template markup file (the view) named 'tpl.html'.

The new UI module must have a fully qualified package style name (eg. App.Tasks.Shell), and be placed in a directory path with the same structure (eg. App/Tasks/Shell).

Every Pink app should have at least one UI module declared in the html page using Pink's module binding handler, this module is normaly the app's main layout module.

Custom/Native Platform UI support

If you want to support multiple platforms, you can detect the current environment and set the the template file name in the app's constructor function.

 var Module = function() {
     App.call(this, 'home'); 

     /*detect runtime environment*/

     if (environment=='android') {
         ko.bindingHandlers.module.templateName='tpl.android.html';
     }     
 };
                            

Module's lifecycle

The framework calls the following UI module's functions during it's lifecycle:

  • constructor(data:object):void Called on class modules after the module is loaded.
  • initialize(data:object):void Called on static modules after the module is loaded.
  • afterRender(template_elements:array):void Called after Knockout has data bound the module's template.
  • finalize:void Called when the module is about to be destroyed.

Data binding

Binding declaration

<div data-bind="module: str|object"></div>

Binding value

module_name_str | {name: module_name_str, data: module_parameters_object, notifyReady: ready_callback, notifyBeforeDestroy: before_destroy_callback}

  • module_name_str (Observable) Fully qualified module name (eg. 'App.Tasks.Shell').
  • module_parameters_object (Observable) Object with the parameters to pass to the modules constructor or initializer function.
  • ready_callback(boundElement) Function called after Knockout has data bound the module's template.
  • before_destroy_callback(boundElement) Function called when the module is about to be destroyed.

ModalWindow

Pink's modal window module is a data binding wrapper around Ink's 'Ink.UI.Modal' widget, that loads an UI module as it's content.

See Pink's app helper methods showMiniModalWindow, showSmallModalWindow and showLargeModalWindow for a simple standard way to show UI modules in a modal dialog.

Modal contents module interface

The modal's content module constructor or initializer function, receives an object with the follwing attributes:

  • confirmHandler Should be assigned to a modal's content module method responsible for doing the logic required by the user's confirm action. Normally this implies making an async call, calling the "params.confirmCallback()" function when the call succeeds, and calling "hide()".
  • confirmDisabled Observable boolean. Usually assigned to a computed observable responsible for validating the user input, returning true if there's invalid input.
  • params - object with the parameters passed to the module.
    Aditionally to custom parameters you may want to pass, there are the following special parameters:
    • confirmCallback: The modal content's module, should call this function in the 'confirmHandler' method after validation.
    • cancelCallback: Called by the framework's code after the user cancels the dialog.
    • taskButtons: Observable array with buttons to be added to the dialog footer.
      Button object: {caption: string, handler: function, cssClass: string, icon: string}
  • hide Function to be called by the content's module, to hide the modal dialog.

Data binding

Binding declaration

<div data-bind="module: {name: 'Pink.Data.ModalWindow_1', data: modalModel}"></div>

Binding value

Ink.createModule('App.Example.Shell', '1', ['App.Example'], function(app) {
    var Module = function() {
        this.modalModel = {
            parent: this, 
            title: 'Example modal window', 
            modalWidth: '400px', 
            modalHeight: '200px', 
            contentModule: 'App.Example.QueryDialog', 
            cancelVisible: true, 
            confirmCaption: 'Confirm'
        };
...
                            

{parent: module_host, title: modal_title_str, modalWidth: width_str, modalHeight: height_str, contentModule: module_name_str, cancelVisible: cancel_visible_bool, confirmCaption: confirm_caption_str}

  • module_host Object to host the "modal" interface: {modal: {show: function(params:object):void}}, reponsible to show the modal dialog, and pass parameters to the loaded module.
  • modal_title_str Observable String with the modal dialog's title
  • width_str Observable String with css units for the dialog's width (eg. '20em')
  • height_str Observable String with css units for the dialog's height (eg. '10em')
  • module_name_str Observable String with a Pink's UI module name (eg. 'App.Tasks.View') to load in the contents area
  • cancel_visible_bool Observable Boolean, indicates if the cancel button is visible
  • confirm_caption_str Observable String with the confirm button caption

Grid

The 'Pink.Data.Grid' module provides a Knockout binding handler to render an HTML table, fully customizable through the use of Knockout templates, and also an extendable javascript model for tabular data, supporting client side pagination and custom row sort handling logic (server/client).

Data binding

Binding declaration

/* Example grid binding with a custom cell renderer */

<div data-bind="simpleGrid: tableModel"></div>
<script id="shapeTemplate" type="text/html">
    <img data-bind="attr: {src: $data.shape+'.png'}">
</script>
                            

Binding value

/* Example view model attribute for a table model */

Ink.createModule('App.Shapes.Home', '1', ['Pink.Data.Grid_1'], function(Grid) {
    var Module = function() {
        this.tableModel = new Grid({
            data: [
                {id: 1, shape: 'square'},
                {id: 2, shape: 'circle'},
            ],
            columns: [
                {headerText: 'Id', rowText: 'id'},
                {headerText: 'Shape', rowTemplate: 'shapeTemplate'},
            ]
        });
...
                            

- Grid model options object (passed to constructor):

  • data Observable Array with the table data rows.
  • columns Array of columnDefinition objects

- Column definition object:

  • headerText String with the column header text.
  • headerClass String with a custom column header CSS class name.
  • headerSortOrder Function that returns a columns sort order: 'asc' for ascending ordering, 'desc' for descending ordering and 'sort' for default sort order.
  • headerSortHandler Function that handles the custom sorting logic.
  • visible Observable boolean that indicates if the column if visible.
  • cellClass String with a custom column cell CSS class name.
  • rowHandler Function to be assigned to a cell's click handler
  • href Function that returns an href anchor attribute string for a row.
  • rowText String with the row's data field name or Function that returns a calculated value from the row's data.
  • rowTemplate String with a Knockout template id, for a custom cell renderer.

Paginator

The 'Pink.Data.Paginator' module, provides a Knockout binding handler to render a pagination bar widget, and a javascript model that handles the actual data pagination.

Data binding

Binding declaration

/* Example paginator binding */

<div data-bind="foreach: pageItems">
    <div data-bind="text: $data"/>
</div>
<div data-bind="paginator: paginatedModel"></div>
                            

Binding value

/* Example view model attribute for a paginator */

Ink.createModule('App.Names.List', '1', ['Pink.Data.Paginator_1'], function(Paginator) {
    var Module = function() {
        this.paginatedModel = new Paginator({
            data: ['Alice', 'Sara', 'John', 'Peter', 'Mark', 'Beatriz', 'Roger', 'Paul', 'Trent', 'Daniela', 'Samuel'],
            pageSize: 5,
            pageSizeOptionList: [5, 10, 20]   
        });
        this.pageItems=this.paginatedModel.itemsOnCurrentPage;
...
                            

- Paginator model options object (passed to constructor):

  • data Observable Array with the data rows.
  • pageSize int value with number of rows per page.
  • pageSizeOptionList array with page sizes.

AutoComplete

The 'Pink.Data.AutoComplete' module provides a Knockout binding handler that renders an autocomplete widget bound to an observable array of values.

Data binding

Binding declaration

/* Example autoComplete binding */

<input type="text" data-bind="autoComplete: users, value: selectedUserId, optionsText: 'name', optionsValue: 'id'" />
                            

Binding value

/* 
    Example view model

    Your app must declare the dependency to the 'Pink.Data.AutoComplete' module 
    to register the 'autoComplete' binding handler. 
*/

Ink.createModule('App.Names.List', '1', ['Pink.Data.Binding_1', 'Pink.Data.AutoComplete_1'], function(ko) {
    var Module = function() {
        this.users = ko.observableArray([
                {id: 1, name: 'Alice'}, 
                {id: 2, name: 'Sarah'}, 
                {id: 3, name: 'John'}, 
                {id: 4, name: 'Peter'}, 
                {id: 5, name: 'Mark'}, 
                {id: 6, name: 'Bea'}, 
                {id: 7, name: 'Roger'}, 
                {id: 8, name: 'Paul'}, 
                {id: 9, name: 'Trent'}, 
                {id:10, name: 'Daniela'},
                {id:11, name: 'Sam'}
        ]);
        this.selectedUserId = ko.observable();
...
                            

ToolTip

'Pink.Data.Tooltip' provides a simple Ink Tooltip binding handler.

Data binding

Binding declaration

/* Example tooltip binding */

<img data-bind="tooltip: userTooltip, attr: {src: photo}">
                            

Binding value

/* 
    Example view model

    Your app must declare the dependency to the 'Pink.Data.Tooltip' module 
    to register the 'tooltip' binding handler. 
*/

Ink.createModule('App.Example.Home', '1', ['Pink.Data.Binding_1', 'Pink.Data.Tooltip_1'], function(ko) {
    var Module = function() {
        this.name = ko.observable('John Doe');
        this.photo = ko.observable('john.png');
        this.userTooltip = {text: this.name};
...
                            
Check Ink.UI.Tooltip Ink's docs, for details on the tooltip value object options.

Drag & Drop

'Pink.Data.DragDrop' provides binding handlers to render drop targets and draggable elements. These bindings allow a clean separation of the rendered draggable and the underlying data object, meaning that the view model only needs to handle pure js objects instead of DOM elements.

The drop targets also permit the definition of a supported data flavor, that checks if the transfered data is an instance of a specified constructor function.

The 'draggableContainer' handler builds a container widget for draggables, delegating the draggable rendering to a specified Knockout template.

The 'droppable' handler adds drop target capabilities to an element and can be combined with a 'draggableContainer' to allow full drag&drop.

Data binding

Binding declaration

/* Example trash bin with draggableContainer & droppable bindings */

<div data-bind="draggableContainer: container" />
<div data-bind="droppable: trash" />
<script id="colorTemplate" type="text/html"><img data-bind="attr: {src: $data.name+'.png'}"/>script>
<script id="sizeTemplate" type="text/html"><img data-bind="attr: {width: $data.value}"/>script>
                            

Binding value

/* 
    Example view model

    Your app must declare the dependency to the 'Pink.Data.DragDrop' module 
    to register both 'draggableContainer' and 'droppable' binding handlers. 
*/

Ink.createModule('App.Example.Home', '1', ['Pink.Data.Binding_1', 'Pink.Data.DragDrop_1'], function(ko) {
    var Module = function() {
        var self=this;
        
        function Color(name) {this.name = name;};
        function Size(value) {this.value = value;};
        
        this.dataItems = ko.observableArray([new Color('blue'), new Color('red'), new Color('cyan'), new Size(10), new Size(20)]);
        
        this.trash = {
            hoverClass: 'my-drop-panel', 
            dropHandler: function(data, index) {
                var i;
                
                data = (data.length?data:[data]);
                for (i=0; i < data.length; i++) {
                    self.dataItems.remove(data[i]);
                }
            },
            dataFlavor: Color 
        };
        
        this.container = {
            source: this.dataItems,
            draggableTemplate: function(draggable) {return draggable instanceof Color ? 'colorTemplate' : 'sizeTemplate';}
        };
...
                            

- 'draggableContainer' options:

  • source Observable array with the draggable objects.
  • draggableTemplate String with id of template to render the draggable or function that receives the draggable and returns a corresponding template id.
  • dragOutHandler Function that is called when an object from this container is dropped in a droppable.
  • afterDraggableRender Function that is called after Knockout renders each draggable.

- 'droppable' options:

  • hoverClass String with class name to add to the element when a draggable of the right flavor hovers over it.
  • dropHandler Function to execute when a draggable is dropped in this droppable (receives selectedData as a parameter).
  • dataFlavor Constructor function. If specified, the droppable only accepts drops of objects that are instances of this function.

Kanban

The Kanban UI module provides a simple interface to create customizable lists of tasks generaly used for "getting things done". The tasks, also called cards, are arranged in sections and can be moved from one section to another by drag&drop. The styling of the sections and task's card, is made through the use of Knockout templates, and allows for custom headers, contents and footers, and custom CSS classes.

Additionally to the UI customization, there are javascript view models for sections and cards, that ease the kanban's creation and hide the underlying UI logic.

Data binding

Binding declaration

<!--ko module: {name:'Pink.Data.Kanban_1', data: kanbanModel}-->
<!--/ko-->
                            

Binding value

/* 
    Example view model
*/

Ink.createModule('App.Example.Todo', '1', ['Pink.Data.Binding_1'], function(ko) {
    var Module = function() {
        this.todoTasks = ko.observableArray([
            {title: 'Buy wood', content: 'Oak wood 10"x5"'},
            {title: 'Buy paint', content: 'White (2 gallons)'}
            {title: 'Repair table', content: 'Repair living room table'}
        ]);
        this.completedTasks = ko.observableArray();        
        this.incompleteTasks = ko.observableArray();
        
        this.sections = [
            {title: 'Todo', items: this.todoTasks}, 
            {title: 'Incomplete', items: this.incompleteTasks}, 
            {title: 'Complete', items: this.completedTasks}
        ];
        
        this.kanbanModel = {
            sections: this.sections 
        };
        
...
                            

- Kanban options:

  • sections Observable Array with the sections.
  • showSectionTooltip If falsie hide's the section tooltip
  • previewMoveHandler(targetItems:observableArray,movedData:array,targetIndex:int) Function called before a card is moved. Must return true if the move is to be allowed.
  • cardsMovedHandler(targetItems:observableArray,movedData:array,targetIndex:int) Function called after a card is moved.
  • afterRender(sectionsDomEl:array,kanbanModel:object) Function called after the kanban's sections are rendered (init or update).

- Section options:

  • title String with the section's title.
  • subtitle String with the section's subtitle.
  • headerTemplate String with the id of a Knockout template to render the section header.
  • footerTemplate String with the id of a Knockout template to render the section footer.
  • items Observable Array with the section cards.
  • droppable Optional If false, prevent drops (default: true).
  • css String with class names to add to the section.
  • settingsHandler Optional Callback function for section settings link. If 'undefined' the settings link will be hidden.
  • settingsCaption Optional If defined, should be a string with the settings link title.
  • afterCardRender(cardDomEls:array) Function called after each section's card is rendered.

- Card options:

  • title String with the card's title.
  • contentTemplate Optional String with the id of a Knockout template to render the card's content.
  • content String with the card's message, or Object with the card's details (if 'contentTemplate' is defined).
  • cardClass Optional String with class names to add to the card.
  • editHandler Function with the card's edit link handler, or array of {handler, icon, caption} menu items.

Carousel

The Carousel UI module is a wrapper around Ink's carousel widget.

Data binding

Binding declaration

<div data-pagination="#carouselPagination" data-bind="carousel: carouselModel">
	<ul class="stage column-group" data-bind="foreach: photos">
		<li class="slide all-100">
			<img class="quarter-bottom-space" data-bind="attr: {src: url}" height="300">
			<div class="description">
				<h4 data-bind="text: title"></h4>
			</div>
		</li>						
	</ul>
</div>			    
<nav id="carouselPagination" data-max-size="5" class="ink-navigation" style="margin-left: -1em">
    <ul class="pagination black">
    </ul>
</nav>
                            

Binding value

/* 
    Example view model

    Your app must declare the dependency to the 'Pink.Data.Carousel' module 
    to register the 'carousel' binding handler. 

*/

Ink.createModule('App.Example.Images', '1', ['Pink.Data.Binding_1', 'Pink.Data.Carousel_1'], function(ko) {
    var Module = function() {
    	this.carouselModel = {
    		pagination: '#carouselPagination', 
    		nextLabel: '', 
    		previousLabel: '', 
    		refitHandler: this.carouselRefitHandler
    	};
        
...
                            

- Carousel module options:

  • pagination Id of DOM element containing an Ink's paginator widget
  • nextLabel String with the next image button label
  • previousLabel String with the previous image button label
  • refitHandler Observable Function set by the carousel module to be called when a layout change occurs.

Tabs

'Pink.Data.Tabs' is a wrapper around Ink's Tabs widget.

Data binding

Binding declaration

/* Example tabs binding */
<div class="top" data-bind="tabs: tabsModel">
	<ul class="tabs-nav">
		<li><a class="tabs-tab" href="#gridResults">Grid</a></li>
		<li><a class="tabs-tab" href="#carouselResults">Carousel</a></li>
	</ul>
	<div id="gridResults" class="tabs-content">
		...
	</div>
	<div id="carouselResults" class="tabs-content">
		...
	</div>
</div>
                            

Binding value

/* 
    Example view model

    Your app must declare the dependency to the 'Pink.Data.Tabs' module 
    to register the 'tabs' binding handler. 
*/

Ink.createModule('App.Example.Search', '1', ['Pink.Data.Binding_1', 'Pink.Data.Tabs_1'], function(ko) {
    var Module = function() {
    	this.tabsModel = {
    		onChange: this.tabChangeHandler.bind(this)
    	};
...
                            
Check Ink.UI.Tabs Ink's docs, for details on the tabs value object options.

DatePicker

'Pink.Data.DatePicker' is a wrapper around Ink's DatePicker widget.

Data binding

Binding declaration

/* Example datepicker binding */
<input type="date" data-bind="datePicker: {position: 'bottom'}, value: selectedDate" />
                            

Binding value

/* 
    Example view model

    Your app must declare the dependency to the 'Pink.Data.DatePicker' module 
    to register the 'datePicker' binding handler. 
*/

Ink.createModule('App.Example.Search', '1', ['Pink.Data.Binding_1', 'Pink.Data.DatePicker_1'], function(ko) {
    var Module = function() {
    	this.selectedDate = ko.observable();
...
                            
Check Ink.UI.DatePicker Ink's docs, for details on the datePicker value object options.

Toggle

'Pink.Data.Toggle' is a wrapper around Ink's Toggle widget.

Data binding

Binding declaration

/* Example toggle binding */
<button data-bind="toggle: {target: '#panel'}">Info</button>
<div id="panel" class="hide-all">
    Useful stuff ...
</div>
                            

Binding value

/* 
    Example view model

    Your app must declare the dependency to the 'Pink.Data.Toggle' module 
    to register the 'toggle' binding handler. 
*/

Ink.createModule('App.Example.Search', '1', ['Pink.Data.Binding_1', 'Pink.Data.Toggle_1'], function(ko) {
    var Module = function() {
...
                            
Check Ink.UI.Toggle Ink's docs, for details on the toggle value object options.

Plugins

If you adopt a decoupled modular architecture, then you'll problably want to allow that certain parts of your application can be replaced or extended.

Typically a Pink application defines extension points through the use of:

Pink's bootstrap process includes an application overridable method called listPluginModules, invoked after the initial application structures are defined, but before the routing module is started and before the databinding occurs.

Application bootstrap sequence:

  1. app.constructor - Internal structures initialization
  2. app.run
  3. app.listPluginModules - Application overridable
  4. Require plugin modules
  5. Start routing module
  6. app.ready - Application overridable
  7. app.start - "appReady" signal dispatch, databinding

/* 
    Example application model
*/

Ink.createModule('App.Images', '1', ['Pink.App_1', 'Pink.Plugin.Signals_1', 'Pink.Data.Binding_1'], function(App, Signal, ko) {
    var Module = function() {
        App.call(this, 'search', 'search'); 

        this.appTitle = 'Image search sample';
    };
    
    Module.prototype = new App();
    Module.constructor = Module;

    Module.prototype.listPluginModules = function() {
        return [
            'App.Images.Plugins.Pinterest',
            'App.Images.Plugins.Instagram'
        ];
    };
...
                            

Bundling

Although you can use Pink's original code structure during development, it's usually better to bundle all the required files and templates to reduce the loading time.

For this purpose Pink has a Grunt script in the source root folder, that you can run to build the following bundles in the 'dist' folder:

  • pink-tpl-bundle.js - Include this file to register all Pink HTML templates.
  • pink.min.js - Minified javascript
  • pink.debug.js - Concatenated javascript