This repository has been archived on 2018-10-12. You can view files and clone it, but cannot push or open issues or pull requests.
node-task/public/js/ink.stacker.js

253 lines
11 KiB
JavaScript

/**
* Stacking items in columns
* @module Ink.UI.Stacker_1
* @version 1
**/
Ink.createModule('Ink.UI.Stacker', 1, ['Ink.UI.Common_1', 'Ink.Dom.Event_1', 'Ink.Dom.Element_1'], function(Common, InkEvent, InkElement) {
'use strict';
function Stacker() {
Common.BaseUIComponent.apply(this, arguments);
}
Stacker._name = 'Stacker_1';
Stacker._optionDefinition = {
column: ['String', '.stacker-column'],
item: ['String', '.stacker-item'],
// [3.0.0] review this when we have info about our breakpoints from the CSS
customBreakPoints: ['Object', null], // Must be: {xlarge: {max: 9999, min: 1281, cols: 5}, large:{max:1280, min:1001, cols:4} medium:{max:1000, min:801,cols:3}, ...etc..}
largeMax: ['Number', Number.MAX_VALUE],
largeMin: ['Number', 961],
mediumMax: ['Number', 960],
mediumMin: ['Number', 651],
smallMax: ['Number', 650],
smallMin: ['Number', 0],
largeCols: ['Integer', 3],
mediumCols: ['Integer', 2],
smallCols: ['Integer', 1],
isOrdered: ['Boolean', true],
onRunCallback: ['Function', null],
onResizeCallback: ['Function', null],
onAPIReloadCallback: ['Function', null]
};
Stacker.prototype = {
/**
* This module combines several stacks of items together, in smaller screen sizes.
*
* The purpose is to have several stacks of items which may have different heights and as such cannot be used because of `float: left` quirks.
*
* For example, when you have three different columns of information:
*
* [col. A: 1] [col. B: 1] [col. C: 1]
* [col. B: 2] [col. C: 2] [col. C: 2]
*
* and the screen resizes and you need a layout of 2 columns, Stacker reorders the stacks so that you get:
*
* [col. A: 1] [col. B: 1]
* [col. C: 1] [col. A: 2]
* [col. B: 2] [col. C: 2]
*
* Note: If you just want to use a different amount of columns for your items in several viewports, but these items are guaranteed to have a fixed height, don't use this module. Use the `small-*`, `medium-*` and `large-*` classes instead.
*
* @class Ink.UI.Stacker_1
*
* @constructor
* @param {DOMElement|String} [container] Element which contains the stacks (identified by the options.column selector)
* @param {Object} [options] Options object.
* @param {String} [options.column] Selector for the the columns inside the container element. Defaults to '.stacker-column'.
* @param {String} [options.item] Selector for the items in your stack. Defaults to '.stacker-item'.
* @param {Object} [options.customBreakPoints] Options for each breakpoint name. Use this if you have more breakpoints than Ink by default (`large`, `medium`, `small`)
* @param {Object} [options.customBreakpoints.BREAKPOINT_NAME] Custom breakpoints object.
* @param {String} options.customBreakpoints.BREAKPOINT_NAME.max Maximum screen size as seen in your media query
* @param {String} options.customBreakpoints.BREAKPOINT_NAME.min Minimum screen size as seen in your media query
* @param {String} options.customBreakpoints.BREAKPOINT_NAME.cols Column count for this size.
* @param {Number} [options.largeMax] Upper bound of `large` breakpoint
* @param {Number} [options.largeMin] Lower bound of `large` breakpoint. Defaults to 961.
* @param {Number} [options.mediumMax] Upper bound of `medium` breakpoint. Defaults to 960.
* @param {Number} [options.mediumMin] Lower bound of `medium` breakpoint. Defaults to 651.
* @param {Number} [options.smallMax] Upper bound of `small` breakpoint. Defaults to 650.
* @param {Number} [options.smallMin] Lower bound of `small` breakpoint
*
* @param {Integer} [options.largeCols] Number of columns in the `large` viewport. Defaults to 3.
* @param {Integer} [options.mediumCols] Number of columns in the `medium` viewport. Defaults to 2.
* @param {Integer} [options.smallCols] Number of columns in the `small` viewport. Defaults to 1.
*
* @param {Boolean} [options.isOrdered] When false, doesn't reorder stacks when combining them.
* @param {Function} [options.onRunCallback] Called when instantiated.
* @param {Function} [options.onResizeCallback] Called when the window resizes.
* @param {Function} [options.onAPIReloadCallback] Called when the reload function executes.
*
* @sample Ink_UI_Stacker_1.html
**/
_init: function() {
this._aList = [];
this._curLayout = 'large';
// [todo] is this needed?
this._runFirstTime = false;
this._getPageItemsToList();
if(this._canApplyLayoutChange() || !this._runFirstTime) {
this._runFirstTime = true;
this._applyLayoutChange();
if(typeof(this._options.onRunCallback) === 'function') {
this._options.onRunCallback(this._curLayout);
}
}
this._addEvents();
},
/**
* Adds an item to the end of your stacks.
* Call `reloadItems()` when you are done adding items.
* @method addItem
* @param {DOMElement} item Element
**/
addItem: function(item) {
this._aList.push(item);
},
/**
* Updates the layout of your items.
* Call this method after adding items or changing their dimensions. This method is automatically called when the window resizes.
*
* @method reloadItems
**/
reloadItems: function() {
this._applyLayoutChange();
if(typeof(this._options.onAPIReloadCallback) === 'function') {
this._options.onAPIReloadCallback(this._curLayout);
}
},
_addEvents: function() {
InkEvent.observe(window, 'resize', Ink.bindEvent(this._onResize, this));
},
_onResize: function() {
if(this._canApplyLayoutChange()) {
this._removeDomItems();
this._applyLayoutChange();
if(typeof(this._options.onResizeCallback) === 'function') {
this._options.onResizeCallback(this._curLayout);
}
}
},
_setCurLayout: function() {
var viewportWidth = InkElement.viewportWidth();
if(this._options.customBreakpoints && typeof(this._options.customBreakPoints) === 'object') {
for(var prop in this._options.customBreakPoints) {
if(this._options.customBreakPoints.hasOwnProperty(prop)) {
if(viewportWidth >= Number(this._options.customBreakPoints[prop].min) && viewportWidth <= Number(this._options.customBreakPoints[prop].max) && this._curLayout !== prop) {
this._curLayout = prop;
return;
}
}
}
} else {
if(viewportWidth <= Number(this._options.largeMax) && viewportWidth >= Number(this._options.largeMin) && this._curLayout !== 'large') {
this._curLayout = 'large';
} else if(viewportWidth >= Number(this._options.mediumMin) && viewportWidth <= Number(this._options.mediumMax) && this._curLayout !== 'medium') {
this._curLayout = 'medium';
} else if(viewportWidth >= Number(this._options.smallMin) && viewportWidth <= Number(this._options.smallMax) && this._curLayout !== 'small') {
this._curLayout = 'small';
}
}
},
_getColumnsToShow: function() {
if(this._options.customBreakPoints && typeof(this._options.customBreakPoints) === 'object') {
return Number(this._options.customBreakPoints[this._curLayout].cols);
} else {
return Number(this._options[this._curLayout+'Cols']);
}
},
_canApplyLayoutChange: function() {
var curLayout = this._curLayout;
this._setCurLayout();
if(curLayout !== this._curLayout) {
return true;
}
return false;
},
_getPageItemsToList: function() {
this._aColumn = Ink.ss(this._options.column, this._element);
var totalCols = this._aColumn.length;
var index = 0;
if(totalCols > 0) {
for(var i=0; i < this._aColumn.length; i++) {
var aItems = Ink.ss(this._options.item, this._aColumn[i]);
for(var j=0; j < aItems.length; j++) {
if(this._options.isOrdered) {
index = i + (j * totalCols);
}
this._aList[index] = aItems[j];
if(!this._options.isOrdered) {
index++;
}
//aItems[j].style.height = (100 + (Math.random() * 100))+'px';
aItems[j].parentNode.removeChild(aItems[j]);
}
}
if(this._aList.length > 0 && this._options.isOrdered) {
var aNewList = [];
for(var ii=0; ii < this._aList.length; ii++) {
if(typeof(this._aList[ii]) !== 'undefined') {
aNewList.push(this._aList[ii]);
}
}
this._aList = aNewList;
}
}
},
_removeDomItems: function() {
var totalCols = this._aColumn.length;
if(totalCols > 0) {
for(var i=0; i < totalCols; i++) {
var aItems = Ink.ss(this._options.item, this._aColumn[i]);
for(var j=aItems.length - 1; j >= 0; j--) {
aItems[j].parentNode.removeChild(aItems[j]);
}
}
}
},
_applyLayoutChange: function() {
var totalCols = this._getColumnsToShow();
var totalItems = this._aList.length;
var index = 0;
var countCol = 0;
if(totalCols > 0) {
while(countCol < totalCols) {
this._aColumn[countCol].appendChild(this._aList[index]);
index++;
countCol++;
if(index === totalItems) {
return;
}
if(countCol === totalCols) {
countCol = 0;
}
}
}
}
};
Common.createUIComponent(Stacker);
return Stacker;
});