metro4
Version:
The front-end framework for Build responsive, mobile-first projects on the web with the first front-end component library in Metro Style
903 lines (743 loc) • 29.5 kB
JavaScript
/* global Metro, METRO_LOCALE, Datetime, datetime */
(function(Metro, $) {
'use strict';
var Utils = Metro.utils;
var ListDefaultConfig = {
locale: METRO_LOCALE,
listDeferred: 0,
templateBeginToken: "<%",
templateEndToken: "%>",
paginationDistance: 5,
paginationShortMode: true,
thousandSeparator: ",",
decimalSeparator: ",",
itemTag: "li",
defaultTemplateTag: "div",
sortClass: null,
sortDir: "asc",
sortInitial: true,
filterClass: null,
filter: null,
filterString: "",
filters: null,
source: null,
showItemsSteps: false,
showSearch: false,
showListInfo: false,
showPagination: false,
showActivity: true,
muteList: true,
items: -1,
itemsSteps: "all, 10,25,50,100",
itemsAllTitle: "Show all",
listItemsCountTitle: "Show entries:",
listSearchTitle: "Search:",
listInfoTitle: "Showing $1 to $2 of $3 entries",
paginationPrevTitle: "Prev",
paginationNextTitle: "Next",
activityType: "cycle",
activityStyle: "color",
activityTimeout: 100,
searchWrapper: null,
rowsWrapper: null,
infoWrapper: null,
paginationWrapper: null,
clsComponent: "",
clsList: "",
clsListItem: "",
clsListTop: "",
clsItemsCount: "",
clsSearch: "",
clsListBottom: "",
clsListInfo: "",
clsListPagination: "",
clsPagination: "",
clsTemplateTag: "",
onDraw: Metro.noop,
onDrawItem: Metro.noop,
onSortStart: Metro.noop,
onSortStop: Metro.noop,
onSortItemSwitch: Metro.noop,
onSearch: Metro.noop,
onRowsCountChange: Metro.noop,
onDataLoad: Metro.noop,
onDataLoaded: Metro.noop,
onDataLoadError: Metro.noop,
onFilterItemAccepted: Metro.noop,
onFilterItemDeclined: Metro.noop,
onListCreate: Metro.noop
};
Metro.listSetup = function (options) {
ListDefaultConfig = $.extend({}, ListDefaultConfig, options);
};
if (typeof window["metroListSetup"] !== undefined) {
Metro.listSetup(window["metroListSetup"]);
}
Metro.Component('list', {
init: function( options, elem ) {
this._super(elem, options, ListDefaultConfig, {
currentPage: 1,
pagesCount: 1,
filterString: "",
data: null,
activity: null,
busy: false,
filters: [],
wrapperInfo: null,
wrapperSearch: null,
wrapperRows: null,
wrapperPagination: null,
filterIndex: null,
filtersIndexes: [],
itemTemplate: null,
sort: {
dir: "asc",
colIndex: 0
},
header: null,
items: []
});
return this;
},
_create: function(){
var that = this, o = this.options;
if (o.source) {
that._fireEvent("data-load", {
source: o.source
});
fetch(o.source)
.then(Metro.fetch.status)
.then(Metro.fetch.json)
.then(function(data){
that._fireEvent("data-loaded", {
source: o.source,
data: data
});
that._build(data);
})
.catch(function(error){
that._fireEvent("data-load-error", {
source: o.source,
error: error
});
});
} else {
that._build();
}
},
_build: function(data){
if (Utils.isValue(data)) {
this._createItemsFromJSON(data);
} else {
this._createItemsFromHTML()
}
this._createStructure();
this._createEvents();
this._fireEvent("list-create");
},
_createItemsFromHTML: function(){
var that = this, element = this.element, o = this.options;
var clsTemplateTag = (""+o.clsTemplateTag).toArray(",")
this.items = [];
$.each(element.children(o.itemTag), function(){
var tagChildren = $(this).children("*");
if (clsTemplateTag.length) {
if (clsTemplateTag.length === 1) {
tagChildren.addClass(clsTemplateTag[0]);
} else {
tagChildren.each(function (i, child) {
$(child).addClass(clsTemplateTag[i] ? clsTemplateTag[i] : clsTemplateTag[clsTemplateTag.length - 1]);
})
}
}
that.items.push(this);
});
},
_createItemsFromJSON: function(source){
var that = this, o = this.options;
var clsTemplateTag = (""+o.clsTemplateTag).toArray(",")
this.items = [];
if (Utils.isValue(source.template)) {
this.itemTemplate = source.template;
}
if (Utils.isValue(source.header)) {
this.header = source.header;
}
if (Utils.isValue(source.data)) {
$.each(source.data, function(){
var item = '', row = this;
var li = document.createElement(o.itemTag);
var tpl = that.itemTemplate;
var tagChildren;
if (!Utils.isValue(tpl)) {
for (var i in row) {
item += "<"+o.defaultTemplateTag+">"+row[i]+"</"+o.defaultTemplateTag+">";
}
} else {
item = Metro.template(tpl, row, {
beginToken: o.templateBeginToken,
endToken: o.templateEndToken
});
}
li.innerHTML = item;
tagChildren = $(li).children("*");
if (clsTemplateTag.length) {
if (clsTemplateTag.length === 1) {
tagChildren.addClass(clsTemplateTag[0]);
} else {
tagChildren.each(function (i, child) {
$(child).addClass(clsTemplateTag[i] ? clsTemplateTag[i] : clsTemplateTag[clsTemplateTag.length - 1]);
})
}
}
that.items.push(li);
});
}
},
_createTopBlock: function (){
var that = this, element = this.element, o = this.options;
var top_block = $("<div>").addClass("list-top").addClass(o.clsListTop).insertBefore(element);
var search_block, search_input, rows_block, rows_select;
search_block = Utils.isValue(this.wrapperSearch) ? this.wrapperSearch : $("<div>").addClass("list-search-block").addClass(o.clsSearch).appendTo(top_block);
search_input = $("<input>").attr("type", "text").appendTo(search_block);
Metro.makePlugin(search_input, 'input', {
prepend: o.listSearchTitle
})
if (o.showSearch !== true) {
search_block.hide();
}
rows_block = Utils.isValue(this.wrapperRows) ? this.wrapperRows : $("<div>").addClass("list-rows-block").addClass(o.clsItemsCount).appendTo(top_block);
rows_select = $("<select>").appendTo(rows_block);
$.each(o.itemsSteps.toArray(), function () {
var option = $("<option>").attr("value", this === "all" ? -1 : this).text(this === "all" ? o.itemsAllTitle : this).appendTo(rows_select);
if (+this === +o.items) option.attr("selected", "selected");
});
rows_select.select({
filter: false,
prepend: o.listItemsCountTitle,
onChange: function (val) {
if (+val === +o.items) return;
o.items = parseInt(val);
that.currentPage = 1;
that._draw();
that._fireEvent("rows-count-change", {
val: val
});
}
});
if (o.showItemsSteps !== true) {
rows_block.hide();
}
return top_block;
},
_createBottomBlock: function (){
var element = this.element, o = this.options;
var bottom_block = $("<div>").addClass("list-bottom").addClass(o.clsListBottom).insertAfter(element);
var info, pagination;
info = $("<div>").addClass("list-info").addClass(o.clsListInfo).appendTo(bottom_block);
if (o.showListInfo !== true) {
info.hide();
}
pagination = $("<div>").addClass("list-pagination").addClass(o.clsListPagination).appendTo(bottom_block);
if (o.showPagination !== true) {
pagination.hide();
}
return bottom_block;
},
_createStructure: function(){
var that = this, element = this.element, o = this.options;
var list_component;
var w_search = $(o.searchWrapper),
w_info = $(o.infoWrapper),
w_rows = $(o.rowsWrapper),
w_paging = $(o.paginationWrapper);
if (w_search.length > 0) {this.wrapperSearch = w_search;}
if (w_info.length > 0) {this.wrapperInfo = w_info;}
if (w_rows.length > 0) {this.wrapperRows = w_rows;}
if (w_paging.length > 0) {this.wrapperPagination = w_paging;}
if (!element.parent().hasClass("list-component")) {
list_component = $("<div>").addClass("list-component").insertBefore(element);
element.appendTo(list_component);
} else {
list_component = element.parent();
}
list_component.addClass(o.clsComponent);
this.activity = $("<div>").addClass("list-progress").appendTo(list_component);
$("<div>").activity({
type: o.activityType,
style: o.activityStyle
}).appendTo(this.activity);
if (o.showActivity !== true) {
this.activity.css({
visibility: "hidden"
})
}
// element.html("").addClass(o.clsList);
element.addClass(o.clsList);
this._createTopBlock();
this._createBottomBlock();
if (Utils.isValue(o.filterString)) {
this.filterString = o.filterString;
}
var filter_func;
if (Utils.isValue(o.filter)) {
filter_func = Utils.isFunc(o.filter);
if (filter_func === false) {
filter_func = Utils.func(o.filter);
}
that.filterIndex = that.addFilter(filter_func);
}
if (Utils.isValue(o.filters) && typeof o.filters === 'string') {
$.each(o.filters.toArray(), function(){
filter_func = Utils.isFunc(this);
if (filter_func !== false) {
that.filtersIndexes.push(that.addFilter(filter_func));
}
});
}
this.currentPage = 1;
if (o.sortInitial !== false)
this.sorting(o.sortClass, o.sortDir, true);
else
this.draw();
},
_createEvents: function(){
var that = this, element = this.element;
var component = element.parent();
var search = component.find(".list-search-block input");
var customSearch;
search.on(Metro.events.inputchange, function(){
that.filterString = this.value.trim().toLowerCase();
if (that.filterString[that.filterString.length - 1] === ":") {
return ;
}
that.currentPage = 1;
that._draw();
});
if (Utils.isValue(this.wrapperSearch)) {
customSearch = this.wrapperSearch.find("input");
if (customSearch.length > 0) {
customSearch.on(Metro.events.inputchange, function(){
that.filterString = this.value.trim().toLowerCase();
if (that.filterString[that.filterString.length - 1] === ":") {
return ;
}
that.currentPage = 1;
that._draw();
});
}
}
function pageLinkClick(l){
var link = $(l);
var item = link.parent();
if (item.hasClass("active")) {
return ;
}
if (item.hasClass("service")) {
if (link.data("page") === "prev") {
that.currentPage--;
if (that.currentPage === 0) {
that.currentPage = 1;
}
} else {
that.currentPage++;
if (that.currentPage > that.pagesCount) {
that.currentPage = that.pagesCount;
}
}
} else {
that.currentPage = link.data("page");
}
that._draw();
}
component.on(Metro.events.click, ".pagination .page-link", function(){
pageLinkClick(this)
});
if (Utils.isValue(this.wrapperPagination)) {
this.wrapperPagination.on(Metro.events.click, ".pagination .page-link", function(){
pageLinkClick(this)
});
}
},
_info: function(start, stop, length){
var element = this.element, o = this.options;
var component = element.parent();
var info = Utils.isValue(this.wrapperInfo) ? this.wrapperInfo : component.find(".list-info");
var text;
if (info.length === 0) {
return ;
}
if (stop > length) {
stop = length;
}
if (this.items.length === 0) {
start = stop = length = 0;
}
text = o.listInfoTitle;
text = text.replace("$1", start);
text = text.replace("$2", stop);
text = text.replace("$3", length);
info.html(text);
},
_paging: function(length){
var element = this.element, o = this.options;
var component = element.parent();
this.pagesCount = Math.ceil(length / o.items); // Костыль
Metro.pagination({
length: length,
rows: o.items,
current: this.currentPage,
target: Utils.isValue(this.wrapperPagination) ? this.wrapperPagination : component.find(".list-pagination"),
claPagination: o.clsPagination,
prevTitle: o.paginationPrevTitle,
nextTitle: o.paginationNextTitle,
distance: o.paginationShortMode === true ? o.paginationDistance : 0
});
},
_filter: function(){
var that = this,
o = this.options,
items, i, data, inset, c1, result;
if (Utils.isValue(this.filterString) || this.filters.length > 0) {
items = this.items.filter(function(item){
data = "";
if (Utils.isValue(o.filterClass)) {
inset = item.getElementsByClassName(o.filterClass);
if (inset.length > 0) for (i = 0; i < inset.length; i++) {
data += inset[i].textContent;
}
} else {
data = item.textContent;
}
c1 = data.replace(/[\n\r]+|[\s]{2,}/g, ' ').trim().toLowerCase();
result = Utils.isValue(that.filterString) ? c1.indexOf(that.filterString) > -1 : true;
if (result === true && that.filters.length > 0) {
for (i = 0; i < that.filters.length; i++) {
if (Utils.exec(that.filters[i], [item]) !== true) {
result = false;
break;
}
}
}
if (result) {
that._fireEvent("filter-item-accepted", {
item: item
});
} else {
that._fireEvent("filter-item-declined", {
item: item
});
}
return result;
});
that._fireEvent("search", {
search: that.filterString,
items: items
});
} else {
items = this.items;
}
return items;
},
_draw: function(cb){
var element = this.element, o = this.options;
var i;
var start = o.items === -1 ? 0 : o.items * (this.currentPage - 1),
stop = o.items === -1 ? this.items.length - 1 : start + o.items - 1;
var items;
items = this._filter();
element.children(o.itemTag).remove();
for (i = start; i <= stop; i++) {
if (Utils.isValue(items[i])) {
$(items[i]).addClass(o.clsListItem).appendTo(element);
}
this._fireEvent("draw-item", {
item: items[i]
});
}
this._info(start + 1, stop + 1, items.length);
this._paging(items.length);
this.activity.hide();
this._fireEvent("draw");
if (cb !== undefined) {
Utils.exec(cb, [element], element[0])
}
},
_getItemContent: function(item){
var o = this.options, $item = $(item);
var i, inset, data;
var format, formatMask = Utils.isValue($item.data("formatMask")) ? $item.data("formatMask") : null;
if (Utils.isValue(o.sortClass)) {
data = "";
inset = $(item).find("."+o.sortClass);
if (inset.length > 0) for (i = 0; i < inset.length; i++) {
data += inset[i].textContent;
}
format = inset.length > 0 ? inset[0].getAttribute("data-format") : "";
} else {
data = item.textContent;
format = item.getAttribute("data-format");
}
data = (""+data).toLowerCase().replace(/[\n\r]+|[\s]{2,}/g, ' ').trim();
if (Utils.isValue(format)) {
if (['number', 'int', 'integer', 'float', 'money'].indexOf(format) !== -1 && (o.thousandSeparator !== "," || o.decimalSeparator !== "." )) {
data = Utils.parseNumber(data, o.thousandSeparator, o.decimalSeparator);
}
switch (format) {
case "date": data = formatMask ? Datetime.from(data, formatMask, o.locale) : datetime(data); break;
case "number": data = Number(data); break;
case "int":
case "integer": data = parseInt(data); break;
case "float": data = parseFloat(data); break;
case "money": data = Utils.parseMoney(data); break;
case "card": data = Utils.parseCard(data); break;
case "phone": data = Utils.parsePhone(data); break;
}
}
return data;
},
deleteItem: function(value){
var i, deleteIndexes = [], item;
var is_func = Utils.isFunc(value);
for (i = 0; i < this.items.length; i++) {
item = this.items[i];
if (is_func) {
if (Utils.exec(value, [item])) {
deleteIndexes.push(i);
}
} else {
if (item.textContent.includes(value)) {
deleteIndexes.push(i);
}
}
}
this.items = Utils.arrayDeleteByMultipleKeys(this.items, deleteIndexes);
return this;
},
draw: function(){
return this._draw();
},
sorting: function(source, dir, redraw){
var that = this, o = this.options;
if (Utils.isValue(source)) {
o.sortClass = source;
}
if (Utils.isValue(dir) && ["asc", "desc"].indexOf(dir) > -1) {
o.sortDir= dir;
}
this._fireEvent("sort-start", {
items: this.items
});
this.items.sort(function(a, b){
var c1 = that._getItemContent(a);
var c2 = that._getItemContent(b);
var result = 0;
if (c1 < c2) {
result = o.sortDir === "asc" ? -1 : 1;
}
if (c1 > c2) {
result = o.sortDir === "asc" ? 1 : -1;
}
if (result !== 0) {
that._fireEvent("sort-item-switch", {
a: a,
b: b,
result: result
});
}
return result;
});
this._fireEvent("sort-stop", {
items: this.items
})
if (redraw === true) {
this._draw();
}
return this;
},
filter: function(val){
this.filterString = val.trim().toLowerCase();
this.currentPage = 1;
this._draw();
},
setData: function(data){
var that = this, element = this.element, o = this.options;
if (Utils.isValue(data) !== true) {
return ;
}
that._createItemsFromJSON(data);
element.html("");
if (Utils.isValue(o.filterString)) {
that.filterString = o.filterString;
}
var filter_func;
if (Utils.isValue(o.filter)) {
filter_func = Utils.isFunc(o.filter);
if (filter_func === false) {
filter_func = Utils.func(o.filter);
}
that.filterIndex = that.addFilter(filter_func);
}
if (Utils.isValue(o.filters) && typeof o.filters === 'string') {
$.each(o.filters.toArray(), function(){
filter_func = Utils.isFunc(this);
if (filter_func !== false) {
that.filtersIndexes.push(that.addFilter(filter_func));
}
});
}
that.currentPage = 1;
that.sorting(o.sortClass, o.sortDir, true);
},
loadData: function(source){
var that = this, o = this.options;
if (Utils.isValue(source) !== true) {
return ;
}
o.source = source;
this._fireEvent("data-load", {
source: o.source
});
fetch(o.source)
.then(Metro.fetch.status)
.then(Metro.fetch.json)
.then(function(data){
that._fireEvent("data-loaded", {
source: o.source,
data: data
});
that.setData(data)
})
.catch(function(error){
that._fireEvent("data-load-error", {
source: o.source,
error: error
});
});
},
next: function(){
if (this.items.length === 0) return ;
this.currentPage++;
if (this.currentPage > this.pagesCount) {
this.currentPage = this.pagesCount;
return ;
}
this._draw();
},
prev: function(){
if (this.items.length === 0) return ;
this.currentPage--;
if (this.currentPage === 0) {
this.currentPage = 1;
return ;
}
this._draw();
},
first: function(){
if (this.items.length === 0) return ;
this.currentPage = 1;
this._draw();
},
last: function(){
if (this.items.length === 0) return ;
this.currentPage = this.pagesCount;
this._draw();
},
page: function(num){
if (num <= 0) {
num = 1;
}
if (num > this.pagesCount) {
num = this.pagesCount;
}
this.currentPage = num;
this._draw();
},
addFilter: function(f, redraw){
var func = Utils.isFunc(f);
if (func === false) {
return ;
}
this.filters.push(func);
if (redraw === true) {
this.currentPage = 1;
this.draw();
}
return this.filters.length - 1;
},
removeFilter: function(key, redraw){
Utils.arrayDeleteByKey(this.filters, key);
if (redraw === true) {
this.currentPage = 1;
this.draw();
}
return this;
},
removeFilters: function(redraw){
this.filters = [];
if (redraw === true) {
this.currentPage = 1;
this.draw();
}
},
getFilters: function(){
return this.filters;
},
getFilterIndex: function(){
return this.filterIndex;
},
getFiltersIndexes: function(){
return this.filtersIndexes;
},
changeAttribute: function(attributeName){
var that = this, element = this.element, o = this.options;
var changeSortDir = function(){
var dir = element.attr("data-sort-dir");
if (!Utils.isValue(dir)) {
return ;
}
o.sortDir = dir;
that.sorting(o.sortClass, o.sortDir, true);
};
var changeSortClass = function(){
var target = element.attr("data-sort-source");
if (!Utils.isValue(target)) {
return ;
}
o.sortClass = target;
that.sorting(o.sortClass, o.sortDir, true);
};
var changeFilterString = function(){
var filter = element.attr("data-filter-string");
if (!Utils.isValue(filter)) {
return ;
}
o.filterString = filter;
that.filter(o.filterString);
};
switch (attributeName) {
case "data-sort-dir": changeSortDir(); break;
case "data-sort-source": changeSortClass(); break;
case "data-filter-string": changeFilterString(); break;
}
},
destroy: function(){
var element = this.element;
var component = element.parent();
var search = component.find(".list-search-block input");
var customSearch;
search.off(Metro.events.inputchange);
if (Utils.isValue(this.wrapperSearch)) {
customSearch = this.wrapperSearch.find("input");
if (customSearch.length > 0) {
customSearch.off(Metro.events.inputchange);
}
}
component.off(Metro.events.click, ".pagination .page-link");
if (Utils.isValue(this.wrapperPagination)) {
this.wrapperPagination.off(Metro.events.click, ".pagination .page-link");
}
return element;
}
});
}(Metro, m4q));