diff --git a/.gitignore b/.gitignore
index 5c96d0d..c9d7943 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
+.DS_Store
npm-debug.log
\ No newline at end of file
diff --git a/bower.json b/bower.json
index a3a5853..b115db2 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
{
"name": "ng-tasty",
- "version": "0.3.3",
+ "version": "0.4.0",
"homepage": "https://github.com/Zizzamia/ng-tasty",
"authors": [
"@zizzamia"
diff --git a/ng-tasty-tpls.js b/ng-tasty-tpls.js
index d5522cf..3611323 100644
--- a/ng-tasty-tpls.js
+++ b/ng-tasty-tpls.js
@@ -2,253 +2,11 @@
* ng-tasty
* https://github.com/Zizzamia/ng-tasty
- * Version: 0.3.3 - 2014-11-28
+ * Version: 0.4.0 - 2014-12-08
* License: MIT
*/
-angular.module("ngTasty", ["ngTasty.tpls", "ngTasty.filter","ngTasty.service","ngTasty.table"]);
-angular.module("ngTasty.tpls", ["template/table/head.html","template/table/pagination.html"]);
-/**
- * @ngdoc
- * @name
- *
- */
-angular.module('ngTasty.filter', [
- 'ngTasty.filter.cleanFieldName',
- 'ngTasty.filter.filterInt',
- 'ngTasty.filter.range'
-]);
-
-/**
- * @ngdoc filter
- * @name cleanFieldName
- *
- * @description
- * Calling toString will return the ...
- *
- * @example
- ng-bind="key | cleanFieldName"
- *
- */
-angular.module('ngTasty.filter.cleanFieldName', [])
-.filter('cleanFieldName', function() {
- return function (input) {
- return input.replace(/[^a-zA-Z0-9-]+/g, '-');
- };
-});
-
-/**
- * @ngdoc filter
- * @name filterInt
- * @kind function
- *
- */
-angular.module('ngTasty.filter.filterInt', [])
-.filter('filterInt', function() {
- return function (input) {
- if(/^(\-|\+)?([0-9]+|Infinity)$/.test(input)) {
- return Number(input);
- }
- return NaN;
- };
-});
-
-/**
- * @ngdoc filter
- * @name range
- * @kind function
- *
- * @description
- * Create a list containing arithmetic progressions. The arguments must
- * be plain integers. If the step argument is omitted, it defaults to 1.
- * If the start argument is omitted, it defaults to 0.
- *
- * @example
- ng-repeat="n in [] | range:1:30"
- */
-angular.module('ngTasty.filter.range', [])
-.filter('range', ["$filter", function($filter) {
- return function(input, start, stop, step) {
- start = $filter('filterInt')(start);
- stop = $filter('filterInt')(stop);
- step = $filter('filterInt')(step);
- if (isNaN(start)) {
- start = 0;
- }
- if (isNaN(stop)) {
- stop = start;
- start = 0;
- }
- if (isNaN(step)) {
- step = 1;
- }
- if ((step > 0 && start >= stop) || (step < 0 && start <= stop)){
- return [];
- }
- for (var i = start; step > 0 ? i < stop : i > stop; i += step){
- input.push(i);
- }
- return input;
- };
-}]);
-
-/**
- * @ngdoc
- * @name
- *
- */
-angular.module('ngTasty.service', [
- 'ngTasty.service.tastyUtil',
- 'ngTasty.service.debounce',
- 'ngTasty.service.setProperty',
- 'ngTasty.service.joinObjects',
- 'ngTasty.service.webSocket'
-]);
-
-/**
- * @ngdoc
- * @name
- *
- */
-angular.module('ngTasty.service.tastyUtil', [
- 'ngTasty.service.debounce',
- 'ngTasty.service.setProperty',
- 'ngTasty.service.joinObjects'
-])
-.factory('tastyUtil', ["debounce", "setProperty", "joinObjects", function(debounce, setProperty, joinObjects) {
- return {
- 'debounce': debounce,
- 'setProperty': setProperty,
- 'joinObjects': joinObjects
- };
-}]);
-
-/**
- * @ngdoc
- * @name
- *
- */
-angular.module('ngTasty.service.debounce', [])
-.factory('debounce', ["$timeout", function($timeout) {
- return function(func, wait, immediate) {
- var timeout;
- return function() {
- var context = this, args = arguments;
- $timeout.cancel(timeout);
- timeout = $timeout(function() {
- timeout = null;
- func.apply(context, args);
- }, wait);
- };
- };
-}]);
-
-/**
- * @ngdoc
- * @name
- *
- */
-angular.module('ngTasty.service.setProperty', [])
-.factory('setProperty', function() {
- return function(objOne, objTwo, attrname) {
- if (typeof objTwo[attrname] !== 'undefined' &&
- objTwo[attrname] !== null) {
- objOne[attrname] = objTwo[attrname];
- }
- return objOne;
- };
-});
-
-/**
- * @ngdoc
- * @name
- *
- */
-angular.module('ngTasty.service.joinObjects', [])
-.factory('joinObjects', ["setProperty", function(setProperty) {
- return function(objOne, objTwo, listKeyNotJoin) {
- listKeyNotJoin = listKeyNotJoin || [];
- for (var attrname in objTwo) {
- if (listKeyNotJoin.indexOf(attrname) < 0) {
- setProperty(objOne, objTwo, attrname);
- }
- }
- return objOne;
- };
-}]);
-
-angular.module('ngTasty.service.webSocket', [
- 'ngTasty.service'
-])
-.factory('WebSocket', function() {
-
- return function(url) {
- var blobURL = URL.createObjectURL(new Blob(['(', function() {
- var WSWorker = (function() {
- var _ws;
-
- var initialize = function(url) {
- _ws = new WebSocket(url);
- };
-
- var on = function(event) {
- _ws.onmessage = function(response) {
- var data = JSON.parse(response.data);
- self.postMessage(data);
- };
- };
-
- var send = function(data) {
- _ws.send(data);
- };
-
- return {
- initialize: initialize,
- on: on,
- send: send
- };
-
- })();
-
- self.addEventListener('message', function(e) {
- switch (e.data.cmd) {
- case 'ws_new':
- WSWorker.initialize(e.data.url);
- break;
- case 'ws_on':
- WSWorker.on(e.data.event, e.data.cb);
- break;
- case 'ws_send':
- WSWorker.send(JSON.stringify(e.data.data));
- break;
- default:
- console.log('Unknown command: ' + e.data.cmd);
- }
- });
-
- }.toString(), ')()'], { type: 'application/javascript' }));
-
- var _worker = new Worker(blobURL);
- URL.revokeObjectURL(blobURL);
-
- _worker.postMessage({ cmd: 'ws_new', url: url });
-
- return {
- on: function(event, cb) {
- _worker.postMessage({ cmd: 'ws_on' });
- _worker.addEventListener('message', function(e) {
- if (event === 'all' || e.data.type === event) {
- cb(e.data);
- }
- });
- },
- send: function(data) {
- _worker.postMessage({ cmd: 'ws_send', data: data });
- }
- };
-
- };
-});
-
+angular.module("ngTasty", ["ngTasty.tpls", "ngTasty.component.table"]);
+angular.module("ngTasty.tpls", ["ngTasty.tpls.TableHead","ngTasty.tpls.TablePagination"]);
/**
* @ngdoc directive
* @name tastyTable
@@ -259,10 +17,12 @@ angular.module('ngTasty.service.webSocket', [
*
*/
-angular.module('ngTasty.table', [
+angular.module('ngTasty.component.table', [
'ngTasty.filter.cleanFieldName',
'ngTasty.filter.range',
- 'ngTasty.service.tastyUtil'
+ 'ngTasty.service.tastyUtil',
+ 'ngTasty.tpls.TableHead',
+ 'ngTasty.tpls.TablePagination'
])
.constant('tableConfig', {
init: {
@@ -281,42 +41,20 @@ angular.module('ngTasty.table', [
itemsPerPage: 5,
bindOnce: true
})
-.controller('TableController', ["$scope", "$attrs", "$timeout", "$filter", "$parse", "tableConfig", "tastyUtil", function($scope, $attrs, $timeout, $filter, $parse, tableConfig, tastyUtil) {
+.controller('TableController', ["$scope", "$attrs", "$timeout", "$filter", "tableConfig", "tastyUtil", function($scope, $attrs, $timeout, $filter, tableConfig, tastyUtil) {
'use strict';
- var listScopeToWatch, initTable;
+ var listScopeToWatch, initTable, newScopeName;
this.$scope = $scope;
$scope.init = {};
$scope.query = {};
- listScopeToWatch = ['filters', 'init', 'query', 'resource', 'resourceCallback'];
+ listScopeToWatch = ['bindFilters', 'bindInit', 'bindQuery', 'bindResource', 'bindResourceCallback'];
listScopeToWatch.forEach(function (scopeName) {
- var lastValue, parentGet, compare, parentSet, parentValueWatch;
- if (!$attrs[scopeName]) {
- return;
- }
- parentGet = $parse($attrs[scopeName]);
- if (parentGet.literal) {
- compare = equals;
- } else {
- compare = function(a,b) { return a === b || (a !== a && b !== b); };
+ newScopeName = scopeName.substring(4);
+ newScopeName = newScopeName.charAt(0).toLowerCase() + newScopeName.slice(1);
+ if ($attrs[scopeName]) {
+ tastyUtil.bindTo(scopeName, $scope, $attrs, newScopeName);
}
- parentSet = parentGet.assign;
- lastValue = $scope[scopeName] = parentGet($scope.$parent);
- parentValueWatch = function parentValueWatch(parentValue) {
- if (!compare(parentValue, $scope[scopeName])) {
- // we are out of sync and need to copy
- if (!compare(parentValue, lastValue)) {
- // parent changed and it has precedence
- $scope[scopeName] = parentValue;
- } else {
- // if the parent can be assigned then do so
- parentSet($scope.$parent, parentValue = $scope[scopeName]);
- }
- }
- return lastValue = parentValue;
- };
- parentValueWatch.$stateful = true;
- $scope.$parent.$watch($parse($attrs[scopeName], parentValueWatch), null, parentGet.literal);
});
// Default configs
@@ -350,22 +88,22 @@ angular.module('ngTasty.table', [
* In the future you will have a way to change
* these values by an isolate optional scope variable,
* more info here https://github.com/angular/angular.js/issues/6404 */
- if (!angular.isDefined($attrs.resource) && !angular.isDefined($attrs.resourceCallback)) {
- throw 'AngularJS tastyTable directive: need the resource or resource-callback attribute';
+ if (!angular.isDefined($attrs.bindResource) && !angular.isDefined($attrs.bindResourceCallback)) {
+ throw 'AngularJS tastyTable directive: need the bind-resource or bind-resource-callback attribute';
}
- if (angular.isDefined($attrs.resource)) {
+ if (angular.isDefined($attrs.bindResource)) {
if (!angular.isObject($scope.resource)) {
- throw 'AngularJS tastyTable directive: the resource ('+
- $attrs.resource + ') it\'s not an object';
+ throw 'AngularJS tastyTable directive: the bind-resource ('+
+ $attrs.bindResource + ') it\'s not an object';
} else if (!$scope.resource.header && !$scope.resource.rows) {
- throw 'AngularJS tastyTable directive: the resource ('+
- $attrs.resource + ') has the property header or rows undefined';
+ throw 'AngularJS tastyTable directive: the bind-resource ('+
+ $attrs.bindResource + ') has the property header or rows undefined';
}
}
- if (angular.isDefined($attrs.resourceCallback)) {
+ if (angular.isDefined($attrs.bindResourceCallback)) {
if (!angular.isFunction($scope.resourceCallback)) {
- throw 'AngularJS tastyTable directive: the resource-callback ('+
- $attrs.resourceCallback + ') it\'s not a function';
+ throw 'AngularJS tastyTable directive: the bind-resource-callback ('+
+ $attrs.bindResourceCallback + ') it\'s not a function';
}
$scope.clientSide = false;
}
@@ -388,10 +126,10 @@ angular.module('ngTasty.table', [
$scope.setDirectivesValues = function (resource) {
if (!angular.isObject(resource)) {
- throw 'AngularJS tastyTable directive: the resource '+
+ throw 'AngularJS tastyTable directive: the bind-resource '+
'it\'s not an object';
} else if (!resource.header && !resource.rows) {
- throw 'AngularJS tastyTable directive: the resource '+
+ throw 'AngularJS tastyTable directive: the bind-resource '+
'has the property header or rows undefined';
}
// Assuming if one header uses just one key it's based on the new pattern.
@@ -439,7 +177,7 @@ angular.module('ngTasty.table', [
$scope.rows = $filter('orderBy')($scope.rows, listSortBy, reverse);
}
}
- if ($attrs.filters) {
+ if ($attrs.bindFilters) {
$scope.rows = $filter('filter')($scope.rows, $scope.filters);
}
if ($scope.paginationDirective) {
@@ -468,7 +206,7 @@ angular.module('ngTasty.table', [
urlQuery = tastyUtil.setProperty(urlQuery, params, 'page');
urlQuery = tastyUtil.setProperty(urlQuery, params, 'count');
}
- if ($attrs.filters) {
+ if ($attrs.bindFilters) {
urlQuery = tastyUtil.joinObjects(urlQuery, filters, listKeyNotJoin);
}
return Object.keys(urlQuery).map(function(key) {
@@ -510,7 +248,7 @@ angular.module('ngTasty.table', [
};
// AngularJs $watch callbacks
- if ($attrs.filters) {
+ if ($attrs.bindFilters) {
$scope.$watch('filters', function (newValue, oldValue){
if (newValue !== oldValue) {
if ($scope.clientSide) {
@@ -562,22 +300,35 @@ angular.module('ngTasty.table', [
*
*/
-.directive('tastyThead', ["$filter", function($filter) {
+.directive('tastyThead', ["$filter", "tastyUtil", function($filter, tastyUtil) {
return {
restrict: 'AE',
require: '^tastyTable',
- scope: {
- 'notSortBy': '='
- },
+ scope: {},
templateUrl: 'template/table/head.html',
link: function (scope, element, attrs, tastyTable) {
'use strict';
- var iconUp, iconDown;
+ var iconUp, iconDown, newScopeName, listScopeToWatch;
// Thead it's called
tastyTable.activate('thead');
scope.bindOnce = tastyTable.bindOnce;
scope.columns = [];
+ listScopeToWatch = ['bindNotSortBy'];
+ listScopeToWatch.forEach(function (scopeName) {
+ newScopeName = scopeName.substring(4);
+ newScopeName = newScopeName.charAt(0).toLowerCase() + newScopeName.slice(1);
+ if (attrs[scopeName]) {
+ tastyUtil.bindTo(scopeName, scope, attrs, newScopeName);
+ } else if (attrs[newScopeName]) {
+ if (attrs[newScopeName][0] === '[') {
+ scope[newScopeName] = JSON.parse(attrs[newScopeName]);
+ } else {
+ scope[newScopeName] = attrs[newScopeName];
+ }
+ }
+ });
+
iconUp = 'fa fa-sort-up';
iconDown = 'fa fa-sort-down';
@@ -674,7 +425,7 @@ angular.module('ngTasty.table', [
*
*/
-.directive('tastyPagination', ["$filter", "$templateCache", "$http", "$compile", "$parse", "tableConfig", function($filter, $templateCache, $http, $compile, $parse, tableConfig) {
+.directive('tastyPagination', ["$filter", "$templateCache", "$http", "$compile", "tableConfig", "tastyUtil", function($filter, $templateCache, $http, $compile, tableConfig, tastyUtil) {
return {
restrict: 'AE',
require: '^tastyTable',
@@ -684,42 +435,31 @@ angular.module('ngTasty.table', [
},
link: function (scope, element, attrs, tastyTable) {
'use strict';
- var getPage, setCount, setPaginationRange,
- setPreviousRange, setRemainingRange,
- setPaginationRanges, listScopeToWatch;
-
+ var getPage, setCount, setPaginationRange, setPreviousRange,
+ setRemainingRange, setPaginationRanges, listScopeToWatch, newScopeName;
- listScopeToWatch = ['itemsPerPage', 'listItemsPerPage'];
+ listScopeToWatch = ['bindItemsPerPage', 'bindListItemsPerPage', 'bindTemplateUrl'];
listScopeToWatch.forEach(function (scopeName) {
- var lastValue, parentGet, compare, parentSet, parentValueWatch;
- if (!attrs[scopeName]) {
- return;
- }
- parentGet = $parse(attrs[scopeName]);
- if (parentGet.literal) {
- compare = equals;
- } else {
- compare = function(a,b) { return a === b || (a !== a && b !== b); };
- }
- parentSet = parentGet.assign;
- lastValue = scope[scopeName] = parentGet(scope.$parent);
- parentValueWatch = function parentValueWatch(parentValue) {
- if (!compare(parentValue, scope[scopeName])) {
- // we are out of sync and need to copy
- if (!compare(parentValue, lastValue)) {
- // parent changed and it has precedence
- $scope[scopeName] = parentValue;
- } else {
- // if the parent can be assigned then do so
- parentSet(scope.$parent, parentValue = scope[scopeName]);
- }
+ newScopeName = scopeName.substring(4);
+ newScopeName = newScopeName.charAt(0).toLowerCase() + newScopeName.slice(1);
+ if (attrs[scopeName]) {
+ tastyUtil.bindTo(scopeName, scope, attrs, newScopeName);
+ } else if (attrs[newScopeName]) {
+ if (newScopeName === 'itemsPerPage') {
+ scope[newScopeName] = parseInt(attrs[newScopeName]);
+ } else {
+ scope[newScopeName] = JSON.parse(attrs[newScopeName]);
}
- return lastValue = parentValue;
- };
- parentValueWatch.$stateful = true;
- scope.$parent.$watch($parse(attrs[scopeName], parentValueWatch), null, parentGet.literal);
+ }
});
+ if (scope.templateUrl) {
+ $http.get(scope.templateUrl, { cache: $templateCache })
+ .success(function(templateContent) {
+ element.replaceWith($compile(templateContent)(scope));
+ });
+ }
+
// Default configs
scope.itemsPerPage = scope.itemsPerPage || tableConfig.itemsPerPage;
scope.listItemsPerPage = scope.listItemsPerPage || tableConfig.listItemsPerPage;
@@ -836,7 +576,276 @@ angular.module('ngTasty.table', [
};
}]);
-angular.module('template/table/head.html', []).run(['$templateCache', function($templateCache) {
+/**
+ * @ngdoc filter
+ * @name cleanFieldName
+ *
+ * @description
+ * Calling toString will return the ...
+ *
+ * @example
+ ng-bind="key | cleanFieldName"
+ *
+ */
+angular.module('ngTasty.filter.cleanFieldName', [])
+.filter('cleanFieldName', function() {
+ return function (input) {
+ return input.replace(/[^a-zA-Z0-9-]+/g, '-');
+ };
+});
+
+/**
+ * @ngdoc filter
+ * @name filterInt
+ * @kind function
+ *
+ */
+angular.module('ngTasty.filter.filterInt', [])
+.filter('filterInt', function() {
+ return function (input) {
+ if(/^(\-|\+)?([0-9]+|Infinity)$/.test(input)) {
+ return Number(input);
+ }
+ return NaN;
+ };
+});
+
+/**
+ * @ngdoc filter
+ * @name range
+ * @kind function
+ *
+ * @description
+ * Create a list containing arithmetic progressions. The arguments must
+ * be plain integers. If the step argument is omitted, it defaults to 1.
+ * If the start argument is omitted, it defaults to 0.
+ *
+ * @example
+ ng-repeat="n in [] | range:1:30"
+ */
+angular.module('ngTasty.filter.range', ['ngTasty.filter.filterInt'])
+.filter('range', ["$filter", function($filter) {
+ return function(input, start, stop, step) {
+ start = $filter('filterInt')(start);
+ stop = $filter('filterInt')(stop);
+ step = $filter('filterInt')(step);
+ if (isNaN(start)) {
+ start = 0;
+ }
+ if (isNaN(stop)) {
+ stop = start;
+ start = 0;
+ }
+ if (isNaN(step)) {
+ step = 1;
+ }
+ if ((step > 0 && start >= stop) || (step < 0 && start <= stop)){
+ return [];
+ }
+ for (var i = start; step > 0 ? i < stop : i > stop; i += step){
+ input.push(i);
+ }
+ return input;
+ };
+}]);
+
+/**
+ * @ngdoc
+ * @name
+ *
+ */
+angular.module('ngTasty.service.bindTo', [])
+.factory('bindTo', ["$parse", function($parse) {
+ return function (scopeName, scope, attrs, newScopeName) {
+ var lastValue, parentGet, compare, parentSet,
+ parentValueWatch, isolateScopeName;
+ if (!attrs[scopeName]) {
+ return;
+ }
+ parentGet = $parse(attrs[scopeName]);
+ if (parentGet.literal) {
+ compare = equals;
+ } else {
+ compare = function(a,b) { return a === b || (a !== a && b !== b); };
+ }
+ if (newScopeName) {
+ isolateScopeName = newScopeName;
+ } else {
+ isolateScopeName = scopeName;
+ }
+ parentSet = parentGet.assign;
+ lastValue = scope[isolateScopeName] = parentGet(scope.$parent);
+ parentValueWatch = function parentValueWatch(parentValue) {
+ if (!compare(parentValue, scope[isolateScopeName])) {
+ // we are out of sync and need to copy
+ if (!compare(parentValue, lastValue)) {
+ // parent changed and it has precedence
+ scope[isolateScopeName] = parentValue;
+ } else {
+ // if the parent can be assigned then do so
+ parentSet(scope.$parent, parentValue = scope[isolateScopeName]);
+ }
+ }
+ return lastValue = parentValue;
+ };
+ parentValueWatch.$stateful = true;
+ scope.$parent.$watch($parse(attrs[scopeName], parentValueWatch), null, parentGet.literal);
+ };
+}]);
+/**
+ * @ngdoc
+ * @name
+ *
+ */
+angular.module('ngTasty.service.debounce', [])
+.factory('debounce', ["$timeout", function($timeout) {
+ return function(func, wait, immediate) {
+ var timeout;
+ return function() {
+ var context = this, args = arguments;
+ $timeout.cancel(timeout);
+ timeout = $timeout(function() {
+ timeout = null;
+ func.apply(context, args);
+ }, wait);
+ };
+ };
+}]);
+
+/**
+ * @ngdoc
+ * @name
+ *
+ */
+angular.module('ngTasty.service.joinObjects', ['ngTasty.service.setProperty'])
+.factory('joinObjects', ["setProperty", function(setProperty) {
+ return function(objOne, objTwo, listKeyNotJoin) {
+ listKeyNotJoin = listKeyNotJoin || [];
+ for (var attrname in objTwo) {
+ if (listKeyNotJoin.indexOf(attrname) < 0) {
+ setProperty(objOne, objTwo, attrname);
+ }
+ }
+ return objOne;
+ };
+}]);
+
+/**
+ * @ngdoc
+ * @name
+ *
+ */
+angular.module('ngTasty.service.setProperty', [])
+.factory('setProperty', function() {
+ return function(objOne, objTwo, attrname) {
+ if (typeof objTwo[attrname] !== 'undefined' &&
+ objTwo[attrname] !== null) {
+ objOne[attrname] = objTwo[attrname];
+ }
+ return objOne;
+ };
+});
+
+/**
+ * @ngdoc
+ * @name
+ *
+ */
+angular.module('ngTasty.service.tastyUtil', [
+ 'ngTasty.service.bindTo',
+ 'ngTasty.service.debounce',
+ 'ngTasty.service.setProperty',
+ 'ngTasty.service.joinObjects'
+])
+.factory('tastyUtil', ["debounce", "setProperty", "joinObjects", "bindTo", function(debounce, setProperty, joinObjects, bindTo) {
+ return {
+ 'bindTo': bindTo,
+ 'debounce': debounce,
+ 'setProperty': setProperty,
+ 'joinObjects': joinObjects
+ };
+}]);
+
+angular.module('ngTasty.service.webSocket', [
+ 'ngTasty.service'
+])
+.factory('WebSocket', function() {
+
+ return function(url) {
+ var blobURL = URL.createObjectURL(new Blob(['(', function() {
+ var WSWorker = (function() {
+ var _ws;
+
+ var initialize = function(url) {
+ _ws = new WebSocket(url);
+ };
+
+ var on = function(event) {
+ _ws.onmessage = function(response) {
+ var data = JSON.parse(response.data);
+ self.postMessage(data);
+ };
+ };
+
+ var send = function(data) {
+ _ws.send(data);
+ };
+
+ return {
+ initialize: initialize,
+ on: on,
+ send: send
+ };
+
+ })();
+
+ self.addEventListener('message', function(e) {
+ switch (e.data.cmd) {
+ case 'ws_new':
+ WSWorker.initialize(e.data.url);
+ break;
+ case 'ws_on':
+ WSWorker.on(e.data.event, e.data.cb);
+ break;
+ case 'ws_send':
+ WSWorker.send(JSON.stringify(e.data.data));
+ break;
+ default:
+ console.log('Unknown command: ' + e.data.cmd);
+ }
+ });
+
+ }.toString(), ')()'], { type: 'application/javascript' }));
+
+ var _worker = new Worker(blobURL);
+ URL.revokeObjectURL(blobURL);
+
+ _worker.postMessage({ cmd: 'ws_new', url: url });
+
+ return {
+ on: function(event, cb) {
+ _worker.postMessage({ cmd: 'ws_on' });
+ _worker.addEventListener('message', function(e) {
+ if (event === 'all' || e.data.type === event) {
+ cb(e.data);
+ }
+ });
+ },
+ send: function(data) {
+ _worker.postMessage({ cmd: 'ws_send', data: data });
+ }
+ };
+
+ };
+});
+
+(function(module) {
+try {
+ module = angular.module('ngTasty.tpls.TableHead');
+} catch (e) {
+ module = angular.module('ngTasty.tpls.TableHead', []);
+}
+module.run(['$templateCache', function($templateCache) {
$templateCache.put('template/table/head.html',
'
\n' +
' \n' +
' |
');
}]);
-
-angular.module('template/table/pagination.html', []).run(['$templateCache', function($templateCache) {
+})();
+
+(function(module) {
+try {
+ module = angular.module('ngTasty.tpls.TablePagination');
+} catch (e) {
+ module = angular.module('ngTasty.tpls.TablePagination', []);
+}
+module.run(['$templateCache', function($templateCache) {
$templateCache.put('template/table/pagination.html',
'\n' +
'
\n' +
@@ -882,3 +898,4 @@ angular.module('template/table/pagination.html', []).run(['$templateCache', func
'
\n' +
'
');
}]);
+})();
diff --git a/ng-tasty-tpls.min.js b/ng-tasty-tpls.min.js
index 8438306..f5d4cfa 100644
--- a/ng-tasty-tpls.min.js
+++ b/ng-tasty-tpls.min.js
@@ -2,7 +2,7 @@
* ng-tasty
* https://github.com/Zizzamia/ng-tasty
- * Version: 0.3.3 - 2014-11-28
+ * Version: 0.4.0 - 2014-12-08
* License: MIT
*/
-angular.module("ngTasty",["ngTasty.tpls","ngTasty.filter","ngTasty.service","ngTasty.table"]),angular.module("ngTasty.tpls",["template/table/head.html","template/table/pagination.html"]),angular.module("ngTasty.filter",["ngTasty.filter.cleanFieldName","ngTasty.filter.filterInt","ngTasty.filter.range"]),angular.module("ngTasty.filter.cleanFieldName",[]).filter("cleanFieldName",function(){return function(input){return input.replace(/[^a-zA-Z0-9-]+/g,"-")}}),angular.module("ngTasty.filter.filterInt",[]).filter("filterInt",function(){return function(input){return/^(\-|\+)?([0-9]+|Infinity)$/.test(input)?Number(input):0/0}}),angular.module("ngTasty.filter.range",[]).filter("range",["$filter",function($filter){return function(input,start,stop,step){if(start=$filter("filterInt")(start),stop=$filter("filterInt")(stop),step=$filter("filterInt")(step),isNaN(start)&&(start=0),isNaN(stop)&&(stop=start,start=0),isNaN(step)&&(step=1),step>0&&start>=stop||0>step&&stop>=start)return[];for(var i=start;step>0?stop>i:i>stop;i+=step)input.push(i);return input}}]),angular.module("ngTasty.service",["ngTasty.service.tastyUtil","ngTasty.service.debounce","ngTasty.service.setProperty","ngTasty.service.joinObjects","ngTasty.service.webSocket"]),angular.module("ngTasty.service.tastyUtil",["ngTasty.service.debounce","ngTasty.service.setProperty","ngTasty.service.joinObjects"]).factory("tastyUtil",["debounce","setProperty","joinObjects",function(debounce,setProperty,joinObjects){return{debounce:debounce,setProperty:setProperty,joinObjects:joinObjects}}]),angular.module("ngTasty.service.debounce",[]).factory("debounce",["$timeout",function($timeout){return function(func,wait){var timeout;return function(){var context=this,args=arguments;$timeout.cancel(timeout),timeout=$timeout(function(){timeout=null,func.apply(context,args)},wait)}}}]),angular.module("ngTasty.service.setProperty",[]).factory("setProperty",function(){return function(objOne,objTwo,attrname){return"undefined"!=typeof objTwo[attrname]&&null!==objTwo[attrname]&&(objOne[attrname]=objTwo[attrname]),objOne}}),angular.module("ngTasty.service.joinObjects",[]).factory("joinObjects",["setProperty",function(setProperty){return function(objOne,objTwo,listKeyNotJoin){listKeyNotJoin=listKeyNotJoin||[];for(var attrname in objTwo)listKeyNotJoin.indexOf(attrname)<0&&setProperty(objOne,objTwo,attrname);return objOne}}]),angular.module("ngTasty.service.webSocket",["ngTasty.service"]).factory("WebSocket",function(){return function(url){var blobURL=URL.createObjectURL(new Blob(["(",function(){var WSWorker=function(){var _ws,initialize=function(url){_ws=new WebSocket(url)},on=function(){_ws.onmessage=function(response){var data=JSON.parse(response.data);self.postMessage(data)}},send=function(data){_ws.send(data)};return{initialize:initialize,on:on,send:send}}();self.addEventListener("message",function(e){switch(e.data.cmd){case"ws_new":WSWorker.initialize(e.data.url);break;case"ws_on":WSWorker.on(e.data.event,e.data.cb);break;case"ws_send":WSWorker.send(JSON.stringify(e.data.data));break;default:console.log("Unknown command: "+e.data.cmd)}})}.toString(),")()"],{type:"application/javascript"})),_worker=new Worker(blobURL);return URL.revokeObjectURL(blobURL),_worker.postMessage({cmd:"ws_new",url:url}),{on:function(event,cb){_worker.postMessage({cmd:"ws_on"}),_worker.addEventListener("message",function(e){("all"===event||e.data.type===event)&&cb(e.data)})},send:function(data){_worker.postMessage({cmd:"ws_send",data:data})}}}}),angular.module("ngTasty.table",["ngTasty.filter.cleanFieldName","ngTasty.filter.range","ngTasty.service.tastyUtil"]).constant("tableConfig",{init:{count:5,page:1,sortBy:void 0,sortOrder:void 0},query:{page:"page",count:"count",sortBy:"sort-by",sortOrder:"sort-order"},listItemsPerPage:[5,25,50,100],itemsPerPage:5,bindOnce:!0}).controller("TableController",["$scope","$attrs","$timeout","$filter","$parse","tableConfig","tastyUtil",function($scope,$attrs,$timeout,$filter,$parse,tableConfig,tastyUtil){"use strict";var listScopeToWatch,initTable;if(this.$scope=$scope,$scope.init={},$scope.query={},listScopeToWatch=["filters","init","query","resource","resourceCallback"],listScopeToWatch.forEach(function(scopeName){var lastValue,parentGet,compare,parentSet,parentValueWatch;$attrs[scopeName]&&(parentGet=$parse($attrs[scopeName]),compare=parentGet.literal?equals:function(a,b){return a===b||a!==a&&b!==b},parentSet=parentGet.assign,lastValue=$scope[scopeName]=parentGet($scope.$parent),parentValueWatch=function(parentValue){return compare(parentValue,$scope[scopeName])||(compare(parentValue,lastValue)?parentSet($scope.$parent,parentValue=$scope[scopeName]):$scope[scopeName]=parentValue),lastValue=parentValue},parentValueWatch.$stateful=!0,$scope.$parent.$watch($parse($attrs[scopeName],parentValueWatch),null,parentGet.literal))}),$scope.query.page=$scope.query.page||tableConfig.query.page,$scope.query.count=$scope.query.count||tableConfig.query.count,$scope.query.sortBy=$scope.query.sortBy||tableConfig.query.sortBy,$scope.query.sortOrder=$scope.query.sortOrder||tableConfig.query.sortOrder,$scope.init.count=$scope.init.count||tableConfig.init.count,$scope.init.page=$scope.init.page||tableConfig.init.page,$scope.init.sortBy=$scope.init.sortBy||tableConfig.init.sortBy,$scope.init.sortOrder=$scope.init.sortOrder||tableConfig.init.sortOrder,$scope.clientSide=!0,$scope.url="",$scope.header={columns:[]},$scope.rows=[],$scope.params={},$scope.pagination={count:$scope.init.count,page:$scope.init.page,pages:1,size:0},$scope.theadDirective=!1,$scope.paginationDirective=!1,!angular.isDefined($attrs.resource)&&!angular.isDefined($attrs.resourceCallback))throw"AngularJS tastyTable directive: need the resource or resource-callback attribute";if(angular.isDefined($attrs.resource)){if(!angular.isObject($scope.resource))throw"AngularJS tastyTable directive: the resource ("+$attrs.resource+") it's not an object";if(!$scope.resource.header&&!$scope.resource.rows)throw"AngularJS tastyTable directive: the resource ("+$attrs.resource+") has the property header or rows undefined"}if(angular.isDefined($attrs.resourceCallback)){if(!angular.isFunction($scope.resourceCallback))throw"AngularJS tastyTable directive: the resource-callback ("+$attrs.resourceCallback+") it's not a function";$scope.clientSide=!1}this.activate=function(directiveName){$scope[directiveName+"Directive"]=!0,$scope.params[directiveName]=!0},this.setParams=function(key,value){$scope.params[key]=value,["sortBy","sortOrder"].indexOf(key)>=0&&($scope.header[key]=value)},this.bindOnce=tableConfig.bindOnce,$scope.setDirectivesValues=function(resource){if(!angular.isObject(resource))throw"AngularJS tastyTable directive: the resource it's not an object";if(!resource.header&&!resource.rows)throw"AngularJS tastyTable directive: the resource has the property header or rows undefined";1===Object.keys(resource.header[0]).length&&(resource.header=resource.header.map(function(header){var key=Object.keys(header)[0];return{key:key,name:header[key]}})),$scope.header={columns:resource.header,sortBy:$scope.params.sortBy,sortOrder:$scope.params.sortOrder},$scope.clientSide||($scope.header.sortBy=$scope.header.sortBy||resource.sortBy,$scope.header.sortOrder=$scope.header.sortOrder||resource.sortOrder),$scope.rows=resource.rows,$scope.paginationDirective&&resource.pagination&&($scope.pagination.count=resource.pagination.count,$scope.pagination.page=resource.pagination.page,$scope.pagination.pages=resource.pagination.pages,$scope.pagination.size=resource.pagination.size)},$scope.buildClientResource=function(){var fromRow,toRow,rowToShow,reverse,listSortBy;$scope.theadDirective&&(reverse="asc"===$scope.header.sortOrder?!1:!0,listSortBy=[function(item){return item[$scope.header.sortBy]}],$scope.header.columns[0].key!==$scope.header.sortBy&&listSortBy.push(function(item){return item[$scope.header.columns[0].key]}),$scope.header.sortBy&&($scope.rows=$filter("orderBy")($scope.rows,listSortBy,reverse))),$attrs.filters&&($scope.rows=$filter("filter")($scope.rows,$scope.filters)),$scope.paginationDirective&&($scope.pagination.page=$scope.params.page,$scope.pagination.count=$scope.params.count,$scope.pagination.size=$scope.rows.length,$scope.pagination.pages=Math.ceil($scope.rows.length/$scope.pagination.count),toRow=$scope.pagination.count*$scope.pagination.page,fromRow=toRow-$scope.pagination.count,fromRow>=0&&toRow>=0&&(rowToShow=$scope.rows.slice(fromRow,toRow),$scope.rows=rowToShow))},$scope.buildUrl=function(params,filters){var urlQuery,value,listKeyNotJoin;return urlQuery={},listKeyNotJoin=["sortBy","sortOrder","page","count"],$scope.theadDirective&&(urlQuery=tastyUtil.setProperty(urlQuery,params,"sortBy"),urlQuery=tastyUtil.setProperty(urlQuery,params,"sortOrder")),$scope.paginationDirective&&(urlQuery=tastyUtil.setProperty(urlQuery,params,"page"),urlQuery=tastyUtil.setProperty(urlQuery,params,"count")),$attrs.filters&&(urlQuery=tastyUtil.joinObjects(urlQuery,filters,listKeyNotJoin)),Object.keys(urlQuery).map(function(key){return value=urlQuery[key],$scope.query[key]&&(key=$scope.query[key]),encodeURIComponent(key)+"="+encodeURIComponent(value)}).join("&")},$scope.updateClientSideResource=tastyUtil.debounce(function(){$scope.setDirectivesValues($scope.resource),$scope.buildClientResource()},60),$scope.updateServerSideResource=tastyUtil.debounce(function(){$scope.url=$scope.buildUrl($scope.params,$scope.filters),$scope.resourceCallback($scope.url,$scope.params).then(function(resource){$scope.setDirectivesValues(resource)})},60),initTable=function(){$scope.clientSide?($scope.params.sortBy=$scope.resource.sortBy||$scope.init.sortBy,$scope.params.sortOrder=$scope.resource.sortOrder||$scope.init.sortOrder,$scope.params.page=$scope.init.page,$scope.resource.pagination&&($scope.params.page=$scope.resource.pagination.page||$scope.init.page),$scope.updateClientSideResource()):($scope.params.sortBy=$scope.init.sortBy,$scope.params.sortOrder=$scope.init.sortOrder,$scope.params.page=$scope.init.page,$scope.updateServerSideResource())},$attrs.filters&&$scope.$watch("filters",function(newValue,oldValue){newValue!==oldValue&&($scope.clientSide?$scope.updateClientSideResource():$scope.updateServerSideResource())},!0),$scope.$watchCollection("params",function(newValue,oldValue){newValue!==oldValue&&($scope.clientSide?$scope.updateClientSideResource():$scope.updateServerSideResource())}),$scope.resource&&$scope.$watch("resource",function(newValue,oldValue){newValue!==oldValue&&($scope.params.sortBy=newValue.sortBy,$scope.params.sortOrder=newValue.sortOrder,$scope.updateClientSideResource())},!0),initTable()}]).directive("tastyTable",function(){return{restrict:"A",scope:!0,controller:"TableController"}}).directive("tastyThead",["$filter",function($filter){return{restrict:"AE",require:"^tastyTable",scope:{notSortBy:"="},templateUrl:"template/table/head.html",link:function(scope,element,attrs,tastyTable){"use strict";var iconUp,iconDown;tastyTable.activate("thead"),scope.bindOnce=tastyTable.bindOnce,scope.columns=[],iconUp="fa fa-sort-up",iconDown="fa fa-sort-down",scope.setColumns=function(){var lenHeader,active,sortable,sort,isSorted;scope.columns=[],lenHeader=scope.header.columns.length,scope.header.columns.forEach(function(column){column.style=column.style||{},sortable=!0,active=!1,isSorted="",angular.isArray(scope.notSortBy)&&(sortable=scope.notSortBy.length?scope.notSortBy.indexOf(column.key)<0:!1),(column.key===scope.header.sortBy||"-"+column.key===scope.header.sortBy)&&(active=!0),sort=$filter("cleanFieldName")(column.key),scope.header.sortBy==="-"+sort?isSorted=iconDown:scope.header.sortBy===sort&&(isSorted=iconUp),scope.columns.push({key:column.key,name:column.name,active:active,sortable:sortable,style:column.style,isSorted:isSorted})}),"dsc"===scope.header.sortOrder&&scope.header.sortBy&&"-"!==scope.header.sortBy[0]&&(scope.header.sortBy="-"+scope.header.sortBy)},scope.sortBy=function(column){if(!column.sortable)return!1;var columnName,sortOrder;columnName=$filter("cleanFieldName")(column.key),sortOrder=scope.header.sortBy===columnName?"dsc":"asc",tastyTable.setParams("sortBy",column.key),tastyTable.setParams("sortOrder",sortOrder)},scope.classToShow=function(column){var listClassToShow=[];return column.sortable&&listClassToShow.push("sortable"),column.active&&listClassToShow.push("active"),listClassToShow},tastyTable.$scope.$watchCollection("header",function(newValue,oldValue){newValue&&newValue!==oldValue&&(scope.header=newValue,scope.setColumns())})}}}]).directive("tastyPagination",["$filter","$templateCache","$http","$compile","$parse","tableConfig",function($filter,$templateCache,$http,$compile,$parse,tableConfig){return{restrict:"AE",require:"^tastyTable",scope:{},templateUrl:function(tElement,tAttrs){return tAttrs.templateUrl||"template/table/pagination.html"},link:function(scope,element,attrs,tastyTable){"use strict";var getPage,setCount,setPaginationRange,setPreviousRange,setRemainingRange,setPaginationRanges,listScopeToWatch;listScopeToWatch=["itemsPerPage","listItemsPerPage"],listScopeToWatch.forEach(function(scopeName){var lastValue,parentGet,compare,parentSet,parentValueWatch;attrs[scopeName]&&(parentGet=$parse(attrs[scopeName]),compare=parentGet.literal?equals:function(a,b){return a===b||a!==a&&b!==b},parentSet=parentGet.assign,lastValue=scope[scopeName]=parentGet(scope.$parent),parentValueWatch=function(parentValue){return compare(parentValue,scope[scopeName])||(compare(parentValue,lastValue)?parentSet(scope.$parent,parentValue=scope[scopeName]):$scope[scopeName]=parentValue),lastValue=parentValue},parentValueWatch.$stateful=!0,scope.$parent.$watch($parse(attrs[scopeName],parentValueWatch),null,parentGet.literal))}),scope.itemsPerPage=scope.itemsPerPage||tableConfig.itemsPerPage,scope.listItemsPerPage=scope.listItemsPerPage||tableConfig.listItemsPerPage,tastyTable.activate("pagination"),scope.pagination={},scope.pagMinRange=1,scope.pagMaxRange=1,getPage=function(numPage){tastyTable.setParams("page",numPage)},setCount=function(count){var maxItems,page;maxItems=count*scope.pagination.page,maxItems>scope.pagination.size&&(page=Math.ceil(scope.pagination.size/count),tastyTable.setParams("page",page)),tastyTable.setParams("count",count)},setPaginationRange=function(){var currentPage;currentPage=scope.pagination.page,currentPage>scope.pagination.pages&&(currentPage=scope.pagination.pages),scope.pagMinRange=currentPage-2>0?currentPage-2:1,scope.pagMaxRange=currentPage+2,scope.pagination.page=currentPage,setPaginationRanges()},setPreviousRange=function(){return scope.pagHideMinRange===!0||scope.pagMinRange<1?!1:(scope.pagMaxRange=scope.pagMinRange,scope.pagMinRange=scope.pagMaxRange-scope.itemsPerPage,setPaginationRanges(),void 0)},setRemainingRange=function(){return scope.pagHideMaxRange===!0||scope.pagMaxRange>scope.pagination.pages?!1:(scope.pagMinRange=scope.pagMaxRange,scope.pagMaxRange=scope.pagMinRange+scope.itemsPerPage,scope.pagMaxRange>scope.pagination.pages&&(scope.pagMaxRange=scope.pagination.pages),scope.pagMinRange=scope.pagMaxRange-scope.itemsPerPage,setPaginationRanges(),void 0)},setPaginationRanges=function(){scope.listItemsPerPageShow=[],scope.pagMinRange=scope.pagMinRange>0?scope.pagMinRange:1,scope.pagMaxRange=scope.pagMinRange+5,scope.pagMaxRange>scope.pagination.pages&&(scope.pagMaxRange=scope.pagination.pages+1),scope.pagHideMinRange=scope.pagMinRange<=1,scope.pagHideMaxRange=scope.pagMaxRange>=scope.pagination.pages,scope.classPageMinRange=scope.pagHideMinRange?"disabled":"",scope.classPageMaxRange=scope.pagHideMaxRange?"disabled":"";for(var i=scope.listItemsPerPage.length;i>=0;i--)if(scope.pagination.size>scope.listItemsPerPage[i]){scope.listItemsPerPageShow=scope.listItemsPerPage.slice(0,i+1);break}scope.rangePage=$filter("range")([],scope.pagMinRange,scope.pagMaxRange)},scope.classPaginationCount=function(count){return count==scope.pagination.count?"active":""},scope.classNumPage=function(numPage){return numPage==scope.pagination.page?"active":!1},scope.page={get:getPage,setCount:setCount,previous:setPreviousRange,remaining:setRemainingRange},tastyTable.$scope.$watchCollection("pagination",function(newValue,oldValue){newValue&&newValue!==oldValue&&(scope.pagination=newValue,setPaginationRange())}),scope.page.setCount(scope.itemsPerPage)}}}]),angular.module("template/table/head.html",[]).run(["$templateCache",function($templateCache){$templateCache.put("template/table/head.html",'\n \n \n \n | \n
')}]),angular.module("template/table/pagination.html",[]).run(["$templateCache",function($templateCache){$templateCache.put("template/table/pagination.html",'\n
\n
\n \n
\n
\n
Page \n of ,\n of entries
\n
\n
')}]);
\ No newline at end of file
+angular.module("ngTasty",["ngTasty.tpls","ngTasty.component.table"]),angular.module("ngTasty.tpls",["ngTasty.tpls.TableHead","ngTasty.tpls.TablePagination"]),angular.module("ngTasty.component.table",["ngTasty.filter.cleanFieldName","ngTasty.filter.range","ngTasty.service.tastyUtil","ngTasty.tpls.TableHead","ngTasty.tpls.TablePagination"]).constant("tableConfig",{init:{count:5,page:1,sortBy:void 0,sortOrder:void 0},query:{page:"page",count:"count",sortBy:"sort-by",sortOrder:"sort-order"},listItemsPerPage:[5,25,50,100],itemsPerPage:5,bindOnce:!0}).controller("TableController",["$scope","$attrs","$timeout","$filter","tableConfig","tastyUtil",function($scope,$attrs,$timeout,$filter,tableConfig,tastyUtil){"use strict";var listScopeToWatch,initTable,newScopeName;if(this.$scope=$scope,$scope.init={},$scope.query={},listScopeToWatch=["bindFilters","bindInit","bindQuery","bindResource","bindResourceCallback"],listScopeToWatch.forEach(function(scopeName){newScopeName=scopeName.substring(4),newScopeName=newScopeName.charAt(0).toLowerCase()+newScopeName.slice(1),$attrs[scopeName]&&tastyUtil.bindTo(scopeName,$scope,$attrs,newScopeName)}),$scope.query.page=$scope.query.page||tableConfig.query.page,$scope.query.count=$scope.query.count||tableConfig.query.count,$scope.query.sortBy=$scope.query.sortBy||tableConfig.query.sortBy,$scope.query.sortOrder=$scope.query.sortOrder||tableConfig.query.sortOrder,$scope.init.count=$scope.init.count||tableConfig.init.count,$scope.init.page=$scope.init.page||tableConfig.init.page,$scope.init.sortBy=$scope.init.sortBy||tableConfig.init.sortBy,$scope.init.sortOrder=$scope.init.sortOrder||tableConfig.init.sortOrder,$scope.clientSide=!0,$scope.url="",$scope.header={columns:[]},$scope.rows=[],$scope.params={},$scope.pagination={count:$scope.init.count,page:$scope.init.page,pages:1,size:0},$scope.theadDirective=!1,$scope.paginationDirective=!1,!angular.isDefined($attrs.bindResource)&&!angular.isDefined($attrs.bindResourceCallback))throw"AngularJS tastyTable directive: need the bind-resource or bind-resource-callback attribute";if(angular.isDefined($attrs.bindResource)){if(!angular.isObject($scope.resource))throw"AngularJS tastyTable directive: the bind-resource ("+$attrs.bindResource+") it's not an object";if(!$scope.resource.header&&!$scope.resource.rows)throw"AngularJS tastyTable directive: the bind-resource ("+$attrs.bindResource+") has the property header or rows undefined"}if(angular.isDefined($attrs.bindResourceCallback)){if(!angular.isFunction($scope.resourceCallback))throw"AngularJS tastyTable directive: the bind-resource-callback ("+$attrs.bindResourceCallback+") it's not a function";$scope.clientSide=!1}this.activate=function(directiveName){$scope[directiveName+"Directive"]=!0,$scope.params[directiveName]=!0},this.setParams=function(key,value){$scope.params[key]=value,["sortBy","sortOrder"].indexOf(key)>=0&&($scope.header[key]=value)},this.bindOnce=tableConfig.bindOnce,$scope.setDirectivesValues=function(resource){if(!angular.isObject(resource))throw"AngularJS tastyTable directive: the bind-resource it's not an object";if(!resource.header&&!resource.rows)throw"AngularJS tastyTable directive: the bind-resource has the property header or rows undefined";1===Object.keys(resource.header[0]).length&&(resource.header=resource.header.map(function(header){var key=Object.keys(header)[0];return{key:key,name:header[key]}})),$scope.header={columns:resource.header,sortBy:$scope.params.sortBy,sortOrder:$scope.params.sortOrder},$scope.clientSide||($scope.header.sortBy=$scope.header.sortBy||resource.sortBy,$scope.header.sortOrder=$scope.header.sortOrder||resource.sortOrder),$scope.rows=resource.rows,$scope.paginationDirective&&resource.pagination&&($scope.pagination.count=resource.pagination.count,$scope.pagination.page=resource.pagination.page,$scope.pagination.pages=resource.pagination.pages,$scope.pagination.size=resource.pagination.size)},$scope.buildClientResource=function(){var fromRow,toRow,rowToShow,reverse,listSortBy;$scope.theadDirective&&(reverse="asc"===$scope.header.sortOrder?!1:!0,listSortBy=[function(item){return item[$scope.header.sortBy]}],$scope.header.columns[0].key!==$scope.header.sortBy&&listSortBy.push(function(item){return item[$scope.header.columns[0].key]}),$scope.header.sortBy&&($scope.rows=$filter("orderBy")($scope.rows,listSortBy,reverse))),$attrs.bindFilters&&($scope.rows=$filter("filter")($scope.rows,$scope.filters)),$scope.paginationDirective&&($scope.pagination.page=$scope.params.page,$scope.pagination.count=$scope.params.count,$scope.pagination.size=$scope.rows.length,$scope.pagination.pages=Math.ceil($scope.rows.length/$scope.pagination.count),toRow=$scope.pagination.count*$scope.pagination.page,fromRow=toRow-$scope.pagination.count,fromRow>=0&&toRow>=0&&(rowToShow=$scope.rows.slice(fromRow,toRow),$scope.rows=rowToShow))},$scope.buildUrl=function(params,filters){var urlQuery,value,listKeyNotJoin;return urlQuery={},listKeyNotJoin=["sortBy","sortOrder","page","count"],$scope.theadDirective&&(urlQuery=tastyUtil.setProperty(urlQuery,params,"sortBy"),urlQuery=tastyUtil.setProperty(urlQuery,params,"sortOrder")),$scope.paginationDirective&&(urlQuery=tastyUtil.setProperty(urlQuery,params,"page"),urlQuery=tastyUtil.setProperty(urlQuery,params,"count")),$attrs.bindFilters&&(urlQuery=tastyUtil.joinObjects(urlQuery,filters,listKeyNotJoin)),Object.keys(urlQuery).map(function(key){return value=urlQuery[key],$scope.query[key]&&(key=$scope.query[key]),encodeURIComponent(key)+"="+encodeURIComponent(value)}).join("&")},$scope.updateClientSideResource=tastyUtil.debounce(function(){$scope.setDirectivesValues($scope.resource),$scope.buildClientResource()},60),$scope.updateServerSideResource=tastyUtil.debounce(function(){$scope.url=$scope.buildUrl($scope.params,$scope.filters),$scope.resourceCallback($scope.url,$scope.params).then(function(resource){$scope.setDirectivesValues(resource)})},60),initTable=function(){$scope.clientSide?($scope.params.sortBy=$scope.resource.sortBy||$scope.init.sortBy,$scope.params.sortOrder=$scope.resource.sortOrder||$scope.init.sortOrder,$scope.params.page=$scope.init.page,$scope.resource.pagination&&($scope.params.page=$scope.resource.pagination.page||$scope.init.page),$scope.updateClientSideResource()):($scope.params.sortBy=$scope.init.sortBy,$scope.params.sortOrder=$scope.init.sortOrder,$scope.params.page=$scope.init.page,$scope.updateServerSideResource())},$attrs.bindFilters&&$scope.$watch("filters",function(newValue,oldValue){newValue!==oldValue&&($scope.clientSide?$scope.updateClientSideResource():$scope.updateServerSideResource())},!0),$scope.$watchCollection("params",function(newValue,oldValue){newValue!==oldValue&&($scope.clientSide?$scope.updateClientSideResource():$scope.updateServerSideResource())}),$scope.resource&&$scope.$watch("resource",function(newValue,oldValue){newValue!==oldValue&&($scope.params.sortBy=newValue.sortBy,$scope.params.sortOrder=newValue.sortOrder,$scope.updateClientSideResource())},!0),initTable()}]).directive("tastyTable",function(){return{restrict:"A",scope:!0,controller:"TableController"}}).directive("tastyThead",["$filter","tastyUtil",function($filter,tastyUtil){return{restrict:"AE",require:"^tastyTable",scope:{},templateUrl:"template/table/head.html",link:function(scope,element,attrs,tastyTable){"use strict";var iconUp,iconDown,newScopeName,listScopeToWatch;tastyTable.activate("thead"),scope.bindOnce=tastyTable.bindOnce,scope.columns=[],listScopeToWatch=["bindNotSortBy"],listScopeToWatch.forEach(function(scopeName){newScopeName=scopeName.substring(4),newScopeName=newScopeName.charAt(0).toLowerCase()+newScopeName.slice(1),attrs[scopeName]?tastyUtil.bindTo(scopeName,scope,attrs,newScopeName):attrs[newScopeName]&&(scope[newScopeName]="["===attrs[newScopeName][0]?JSON.parse(attrs[newScopeName]):attrs[newScopeName])}),iconUp="fa fa-sort-up",iconDown="fa fa-sort-down",scope.setColumns=function(){var lenHeader,active,sortable,sort,isSorted;scope.columns=[],lenHeader=scope.header.columns.length,scope.header.columns.forEach(function(column){column.style=column.style||{},sortable=!0,active=!1,isSorted="",angular.isArray(scope.notSortBy)&&(sortable=scope.notSortBy.length?scope.notSortBy.indexOf(column.key)<0:!1),(column.key===scope.header.sortBy||"-"+column.key===scope.header.sortBy)&&(active=!0),sort=$filter("cleanFieldName")(column.key),scope.header.sortBy==="-"+sort?isSorted=iconDown:scope.header.sortBy===sort&&(isSorted=iconUp),scope.columns.push({key:column.key,name:column.name,active:active,sortable:sortable,style:column.style,isSorted:isSorted})}),"dsc"===scope.header.sortOrder&&scope.header.sortBy&&"-"!==scope.header.sortBy[0]&&(scope.header.sortBy="-"+scope.header.sortBy)},scope.sortBy=function(column){if(!column.sortable)return!1;var columnName,sortOrder;columnName=$filter("cleanFieldName")(column.key),sortOrder=scope.header.sortBy===columnName?"dsc":"asc",tastyTable.setParams("sortBy",column.key),tastyTable.setParams("sortOrder",sortOrder)},scope.classToShow=function(column){var listClassToShow=[];return column.sortable&&listClassToShow.push("sortable"),column.active&&listClassToShow.push("active"),listClassToShow},tastyTable.$scope.$watchCollection("header",function(newValue,oldValue){newValue&&newValue!==oldValue&&(scope.header=newValue,scope.setColumns())})}}}]).directive("tastyPagination",["$filter","$templateCache","$http","$compile","tableConfig","tastyUtil",function($filter,$templateCache,$http,$compile,tableConfig,tastyUtil){return{restrict:"AE",require:"^tastyTable",scope:{},templateUrl:function(tElement,tAttrs){return tAttrs.templateUrl||"template/table/pagination.html"},link:function(scope,element,attrs,tastyTable){"use strict";var getPage,setCount,setPaginationRange,setPreviousRange,setRemainingRange,setPaginationRanges,listScopeToWatch,newScopeName;listScopeToWatch=["bindItemsPerPage","bindListItemsPerPage","bindTemplateUrl"],listScopeToWatch.forEach(function(scopeName){newScopeName=scopeName.substring(4),newScopeName=newScopeName.charAt(0).toLowerCase()+newScopeName.slice(1),attrs[scopeName]?tastyUtil.bindTo(scopeName,scope,attrs,newScopeName):attrs[newScopeName]&&(scope[newScopeName]="itemsPerPage"===newScopeName?parseInt(attrs[newScopeName]):JSON.parse(attrs[newScopeName]))}),scope.templateUrl&&$http.get(scope.templateUrl,{cache:$templateCache}).success(function(templateContent){element.replaceWith($compile(templateContent)(scope))}),scope.itemsPerPage=scope.itemsPerPage||tableConfig.itemsPerPage,scope.listItemsPerPage=scope.listItemsPerPage||tableConfig.listItemsPerPage,tastyTable.activate("pagination"),scope.pagination={},scope.pagMinRange=1,scope.pagMaxRange=1,getPage=function(numPage){tastyTable.setParams("page",numPage)},setCount=function(count){var maxItems,page;maxItems=count*scope.pagination.page,maxItems>scope.pagination.size&&(page=Math.ceil(scope.pagination.size/count),tastyTable.setParams("page",page)),tastyTable.setParams("count",count)},setPaginationRange=function(){var currentPage;currentPage=scope.pagination.page,currentPage>scope.pagination.pages&&(currentPage=scope.pagination.pages),scope.pagMinRange=currentPage-2>0?currentPage-2:1,scope.pagMaxRange=currentPage+2,scope.pagination.page=currentPage,setPaginationRanges()},setPreviousRange=function(){return scope.pagHideMinRange===!0||scope.pagMinRange<1?!1:(scope.pagMaxRange=scope.pagMinRange,scope.pagMinRange=scope.pagMaxRange-scope.itemsPerPage,setPaginationRanges(),void 0)},setRemainingRange=function(){return scope.pagHideMaxRange===!0||scope.pagMaxRange>scope.pagination.pages?!1:(scope.pagMinRange=scope.pagMaxRange,scope.pagMaxRange=scope.pagMinRange+scope.itemsPerPage,scope.pagMaxRange>scope.pagination.pages&&(scope.pagMaxRange=scope.pagination.pages),scope.pagMinRange=scope.pagMaxRange-scope.itemsPerPage,setPaginationRanges(),void 0)},setPaginationRanges=function(){scope.listItemsPerPageShow=[],scope.pagMinRange=scope.pagMinRange>0?scope.pagMinRange:1,scope.pagMaxRange=scope.pagMinRange+5,scope.pagMaxRange>scope.pagination.pages&&(scope.pagMaxRange=scope.pagination.pages+1),scope.pagHideMinRange=scope.pagMinRange<=1,scope.pagHideMaxRange=scope.pagMaxRange>=scope.pagination.pages,scope.classPageMinRange=scope.pagHideMinRange?"disabled":"",scope.classPageMaxRange=scope.pagHideMaxRange?"disabled":"";for(var i=scope.listItemsPerPage.length;i>=0;i--)if(scope.pagination.size>scope.listItemsPerPage[i]){scope.listItemsPerPageShow=scope.listItemsPerPage.slice(0,i+1);break}scope.rangePage=$filter("range")([],scope.pagMinRange,scope.pagMaxRange)},scope.classPaginationCount=function(count){return count==scope.pagination.count?"active":""},scope.classNumPage=function(numPage){return numPage==scope.pagination.page?"active":!1},scope.page={get:getPage,setCount:setCount,previous:setPreviousRange,remaining:setRemainingRange},tastyTable.$scope.$watchCollection("pagination",function(newValue,oldValue){newValue&&newValue!==oldValue&&(scope.pagination=newValue,setPaginationRange())}),scope.page.setCount(scope.itemsPerPage)}}}]),angular.module("ngTasty.filter.cleanFieldName",[]).filter("cleanFieldName",function(){return function(input){return input.replace(/[^a-zA-Z0-9-]+/g,"-")}}),angular.module("ngTasty.filter.filterInt",[]).filter("filterInt",function(){return function(input){return/^(\-|\+)?([0-9]+|Infinity)$/.test(input)?Number(input):0/0}}),angular.module("ngTasty.filter.range",["ngTasty.filter.filterInt"]).filter("range",["$filter",function($filter){return function(input,start,stop,step){if(start=$filter("filterInt")(start),stop=$filter("filterInt")(stop),step=$filter("filterInt")(step),isNaN(start)&&(start=0),isNaN(stop)&&(stop=start,start=0),isNaN(step)&&(step=1),step>0&&start>=stop||0>step&&stop>=start)return[];for(var i=start;step>0?stop>i:i>stop;i+=step)input.push(i);return input}}]),angular.module("ngTasty.service.bindTo",[]).factory("bindTo",["$parse",function($parse){return function(scopeName,scope,attrs,newScopeName){var lastValue,parentGet,compare,parentSet,parentValueWatch,isolateScopeName;attrs[scopeName]&&(parentGet=$parse(attrs[scopeName]),compare=parentGet.literal?equals:function(a,b){return a===b||a!==a&&b!==b},isolateScopeName=newScopeName?newScopeName:scopeName,parentSet=parentGet.assign,lastValue=scope[isolateScopeName]=parentGet(scope.$parent),parentValueWatch=function(parentValue){return compare(parentValue,scope[isolateScopeName])||(compare(parentValue,lastValue)?parentSet(scope.$parent,parentValue=scope[isolateScopeName]):scope[isolateScopeName]=parentValue),lastValue=parentValue},parentValueWatch.$stateful=!0,scope.$parent.$watch($parse(attrs[scopeName],parentValueWatch),null,parentGet.literal))}}]),angular.module("ngTasty.service.debounce",[]).factory("debounce",["$timeout",function($timeout){return function(func,wait){var timeout;return function(){var context=this,args=arguments;$timeout.cancel(timeout),timeout=$timeout(function(){timeout=null,func.apply(context,args)},wait)}}}]),angular.module("ngTasty.service.joinObjects",["ngTasty.service.setProperty"]).factory("joinObjects",["setProperty",function(setProperty){return function(objOne,objTwo,listKeyNotJoin){listKeyNotJoin=listKeyNotJoin||[];for(var attrname in objTwo)listKeyNotJoin.indexOf(attrname)<0&&setProperty(objOne,objTwo,attrname);return objOne}}]),angular.module("ngTasty.service.setProperty",[]).factory("setProperty",function(){return function(objOne,objTwo,attrname){return"undefined"!=typeof objTwo[attrname]&&null!==objTwo[attrname]&&(objOne[attrname]=objTwo[attrname]),objOne}}),angular.module("ngTasty.service.tastyUtil",["ngTasty.service.bindTo","ngTasty.service.debounce","ngTasty.service.setProperty","ngTasty.service.joinObjects"]).factory("tastyUtil",["debounce","setProperty","joinObjects","bindTo",function(debounce,setProperty,joinObjects,bindTo){return{bindTo:bindTo,debounce:debounce,setProperty:setProperty,joinObjects:joinObjects}}]),angular.module("ngTasty.service.webSocket",["ngTasty.service"]).factory("WebSocket",function(){return function(url){var blobURL=URL.createObjectURL(new Blob(["(",function(){var WSWorker=function(){var _ws,initialize=function(url){_ws=new WebSocket(url)},on=function(){_ws.onmessage=function(response){var data=JSON.parse(response.data);self.postMessage(data)}},send=function(data){_ws.send(data)};return{initialize:initialize,on:on,send:send}}();self.addEventListener("message",function(e){switch(e.data.cmd){case"ws_new":WSWorker.initialize(e.data.url);break;case"ws_on":WSWorker.on(e.data.event,e.data.cb);break;case"ws_send":WSWorker.send(JSON.stringify(e.data.data));break;default:console.log("Unknown command: "+e.data.cmd)}})}.toString(),")()"],{type:"application/javascript"})),_worker=new Worker(blobURL);return URL.revokeObjectURL(blobURL),_worker.postMessage({cmd:"ws_new",url:url}),{on:function(event,cb){_worker.postMessage({cmd:"ws_on"}),_worker.addEventListener("message",function(e){("all"===event||e.data.type===event)&&cb(e.data)})},send:function(data){_worker.postMessage({cmd:"ws_send",data:data})}}}}),function(module){try{module=angular.module("ngTasty.tpls.TableHead")}catch(e){module=angular.module("ngTasty.tpls.TableHead",[])}module.run(["$templateCache",function($templateCache){$templateCache.put("template/table/head.html",'\n \n \n \n | \n
')}])}(),function(module){try{module=angular.module("ngTasty.tpls.TablePagination")}catch(e){module=angular.module("ngTasty.tpls.TablePagination",[])}module.run(["$templateCache",function($templateCache){$templateCache.put("template/table/pagination.html",'\n
\n
\n \n
\n
\n
Page \n of ,\n of entries
\n
\n
')}])}();
\ No newline at end of file
diff --git a/ng-tasty.js b/ng-tasty.js
index 928a7f6..5f61210 100644
--- a/ng-tasty.js
+++ b/ng-tasty.js
@@ -2,252 +2,10 @@
* ng-tasty
* https://github.com/Zizzamia/ng-tasty
- * Version: 0.3.3 - 2014-11-28
+ * Version: 0.4.0 - 2014-12-08
* License: MIT
*/
-angular.module("ngTasty", ["ngTasty.filter","ngTasty.service","ngTasty.table"]);
-/**
- * @ngdoc
- * @name
- *
- */
-angular.module('ngTasty.filter', [
- 'ngTasty.filter.cleanFieldName',
- 'ngTasty.filter.filterInt',
- 'ngTasty.filter.range'
-]);
-
-/**
- * @ngdoc filter
- * @name cleanFieldName
- *
- * @description
- * Calling toString will return the ...
- *
- * @example
- ng-bind="key | cleanFieldName"
- *
- */
-angular.module('ngTasty.filter.cleanFieldName', [])
-.filter('cleanFieldName', function() {
- return function (input) {
- return input.replace(/[^a-zA-Z0-9-]+/g, '-');
- };
-});
-
-/**
- * @ngdoc filter
- * @name filterInt
- * @kind function
- *
- */
-angular.module('ngTasty.filter.filterInt', [])
-.filter('filterInt', function() {
- return function (input) {
- if(/^(\-|\+)?([0-9]+|Infinity)$/.test(input)) {
- return Number(input);
- }
- return NaN;
- };
-});
-
-/**
- * @ngdoc filter
- * @name range
- * @kind function
- *
- * @description
- * Create a list containing arithmetic progressions. The arguments must
- * be plain integers. If the step argument is omitted, it defaults to 1.
- * If the start argument is omitted, it defaults to 0.
- *
- * @example
- ng-repeat="n in [] | range:1:30"
- */
-angular.module('ngTasty.filter.range', [])
-.filter('range', ["$filter", function($filter) {
- return function(input, start, stop, step) {
- start = $filter('filterInt')(start);
- stop = $filter('filterInt')(stop);
- step = $filter('filterInt')(step);
- if (isNaN(start)) {
- start = 0;
- }
- if (isNaN(stop)) {
- stop = start;
- start = 0;
- }
- if (isNaN(step)) {
- step = 1;
- }
- if ((step > 0 && start >= stop) || (step < 0 && start <= stop)){
- return [];
- }
- for (var i = start; step > 0 ? i < stop : i > stop; i += step){
- input.push(i);
- }
- return input;
- };
-}]);
-
-/**
- * @ngdoc
- * @name
- *
- */
-angular.module('ngTasty.service', [
- 'ngTasty.service.tastyUtil',
- 'ngTasty.service.debounce',
- 'ngTasty.service.setProperty',
- 'ngTasty.service.joinObjects',
- 'ngTasty.service.webSocket'
-]);
-
-/**
- * @ngdoc
- * @name
- *
- */
-angular.module('ngTasty.service.tastyUtil', [
- 'ngTasty.service.debounce',
- 'ngTasty.service.setProperty',
- 'ngTasty.service.joinObjects'
-])
-.factory('tastyUtil', ["debounce", "setProperty", "joinObjects", function(debounce, setProperty, joinObjects) {
- return {
- 'debounce': debounce,
- 'setProperty': setProperty,
- 'joinObjects': joinObjects
- };
-}]);
-
-/**
- * @ngdoc
- * @name
- *
- */
-angular.module('ngTasty.service.debounce', [])
-.factory('debounce', ["$timeout", function($timeout) {
- return function(func, wait, immediate) {
- var timeout;
- return function() {
- var context = this, args = arguments;
- $timeout.cancel(timeout);
- timeout = $timeout(function() {
- timeout = null;
- func.apply(context, args);
- }, wait);
- };
- };
-}]);
-
-/**
- * @ngdoc
- * @name
- *
- */
-angular.module('ngTasty.service.setProperty', [])
-.factory('setProperty', function() {
- return function(objOne, objTwo, attrname) {
- if (typeof objTwo[attrname] !== 'undefined' &&
- objTwo[attrname] !== null) {
- objOne[attrname] = objTwo[attrname];
- }
- return objOne;
- };
-});
-
-/**
- * @ngdoc
- * @name
- *
- */
-angular.module('ngTasty.service.joinObjects', [])
-.factory('joinObjects', ["setProperty", function(setProperty) {
- return function(objOne, objTwo, listKeyNotJoin) {
- listKeyNotJoin = listKeyNotJoin || [];
- for (var attrname in objTwo) {
- if (listKeyNotJoin.indexOf(attrname) < 0) {
- setProperty(objOne, objTwo, attrname);
- }
- }
- return objOne;
- };
-}]);
-
-angular.module('ngTasty.service.webSocket', [
- 'ngTasty.service'
-])
-.factory('WebSocket', function() {
-
- return function(url) {
- var blobURL = URL.createObjectURL(new Blob(['(', function() {
- var WSWorker = (function() {
- var _ws;
-
- var initialize = function(url) {
- _ws = new WebSocket(url);
- };
-
- var on = function(event) {
- _ws.onmessage = function(response) {
- var data = JSON.parse(response.data);
- self.postMessage(data);
- };
- };
-
- var send = function(data) {
- _ws.send(data);
- };
-
- return {
- initialize: initialize,
- on: on,
- send: send
- };
-
- })();
-
- self.addEventListener('message', function(e) {
- switch (e.data.cmd) {
- case 'ws_new':
- WSWorker.initialize(e.data.url);
- break;
- case 'ws_on':
- WSWorker.on(e.data.event, e.data.cb);
- break;
- case 'ws_send':
- WSWorker.send(JSON.stringify(e.data.data));
- break;
- default:
- console.log('Unknown command: ' + e.data.cmd);
- }
- });
-
- }.toString(), ')()'], { type: 'application/javascript' }));
-
- var _worker = new Worker(blobURL);
- URL.revokeObjectURL(blobURL);
-
- _worker.postMessage({ cmd: 'ws_new', url: url });
-
- return {
- on: function(event, cb) {
- _worker.postMessage({ cmd: 'ws_on' });
- _worker.addEventListener('message', function(e) {
- if (event === 'all' || e.data.type === event) {
- cb(e.data);
- }
- });
- },
- send: function(data) {
- _worker.postMessage({ cmd: 'ws_send', data: data });
- }
- };
-
- };
-});
-
+angular.module("ngTasty", ["ngTasty.component.table"]);
/**
* @ngdoc directive
* @name tastyTable
@@ -258,10 +16,12 @@ angular.module('ngTasty.service.webSocket', [
*
*/
-angular.module('ngTasty.table', [
+angular.module('ngTasty.component.table', [
'ngTasty.filter.cleanFieldName',
'ngTasty.filter.range',
- 'ngTasty.service.tastyUtil'
+ 'ngTasty.service.tastyUtil',
+ 'ngTasty.tpls.TableHead',
+ 'ngTasty.tpls.TablePagination'
])
.constant('tableConfig', {
init: {
@@ -280,42 +40,20 @@ angular.module('ngTasty.table', [
itemsPerPage: 5,
bindOnce: true
})
-.controller('TableController', ["$scope", "$attrs", "$timeout", "$filter", "$parse", "tableConfig", "tastyUtil", function($scope, $attrs, $timeout, $filter, $parse, tableConfig, tastyUtil) {
+.controller('TableController', ["$scope", "$attrs", "$timeout", "$filter", "tableConfig", "tastyUtil", function($scope, $attrs, $timeout, $filter, tableConfig, tastyUtil) {
'use strict';
- var listScopeToWatch, initTable;
+ var listScopeToWatch, initTable, newScopeName;
this.$scope = $scope;
$scope.init = {};
$scope.query = {};
- listScopeToWatch = ['filters', 'init', 'query', 'resource', 'resourceCallback'];
+ listScopeToWatch = ['bindFilters', 'bindInit', 'bindQuery', 'bindResource', 'bindResourceCallback'];
listScopeToWatch.forEach(function (scopeName) {
- var lastValue, parentGet, compare, parentSet, parentValueWatch;
- if (!$attrs[scopeName]) {
- return;
- }
- parentGet = $parse($attrs[scopeName]);
- if (parentGet.literal) {
- compare = equals;
- } else {
- compare = function(a,b) { return a === b || (a !== a && b !== b); };
+ newScopeName = scopeName.substring(4);
+ newScopeName = newScopeName.charAt(0).toLowerCase() + newScopeName.slice(1);
+ if ($attrs[scopeName]) {
+ tastyUtil.bindTo(scopeName, $scope, $attrs, newScopeName);
}
- parentSet = parentGet.assign;
- lastValue = $scope[scopeName] = parentGet($scope.$parent);
- parentValueWatch = function parentValueWatch(parentValue) {
- if (!compare(parentValue, $scope[scopeName])) {
- // we are out of sync and need to copy
- if (!compare(parentValue, lastValue)) {
- // parent changed and it has precedence
- $scope[scopeName] = parentValue;
- } else {
- // if the parent can be assigned then do so
- parentSet($scope.$parent, parentValue = $scope[scopeName]);
- }
- }
- return lastValue = parentValue;
- };
- parentValueWatch.$stateful = true;
- $scope.$parent.$watch($parse($attrs[scopeName], parentValueWatch), null, parentGet.literal);
});
// Default configs
@@ -349,22 +87,22 @@ angular.module('ngTasty.table', [
* In the future you will have a way to change
* these values by an isolate optional scope variable,
* more info here https://github.com/angular/angular.js/issues/6404 */
- if (!angular.isDefined($attrs.resource) && !angular.isDefined($attrs.resourceCallback)) {
- throw 'AngularJS tastyTable directive: need the resource or resource-callback attribute';
+ if (!angular.isDefined($attrs.bindResource) && !angular.isDefined($attrs.bindResourceCallback)) {
+ throw 'AngularJS tastyTable directive: need the bind-resource or bind-resource-callback attribute';
}
- if (angular.isDefined($attrs.resource)) {
+ if (angular.isDefined($attrs.bindResource)) {
if (!angular.isObject($scope.resource)) {
- throw 'AngularJS tastyTable directive: the resource ('+
- $attrs.resource + ') it\'s not an object';
+ throw 'AngularJS tastyTable directive: the bind-resource ('+
+ $attrs.bindResource + ') it\'s not an object';
} else if (!$scope.resource.header && !$scope.resource.rows) {
- throw 'AngularJS tastyTable directive: the resource ('+
- $attrs.resource + ') has the property header or rows undefined';
+ throw 'AngularJS tastyTable directive: the bind-resource ('+
+ $attrs.bindResource + ') has the property header or rows undefined';
}
}
- if (angular.isDefined($attrs.resourceCallback)) {
+ if (angular.isDefined($attrs.bindResourceCallback)) {
if (!angular.isFunction($scope.resourceCallback)) {
- throw 'AngularJS tastyTable directive: the resource-callback ('+
- $attrs.resourceCallback + ') it\'s not a function';
+ throw 'AngularJS tastyTable directive: the bind-resource-callback ('+
+ $attrs.bindResourceCallback + ') it\'s not a function';
}
$scope.clientSide = false;
}
@@ -387,10 +125,10 @@ angular.module('ngTasty.table', [
$scope.setDirectivesValues = function (resource) {
if (!angular.isObject(resource)) {
- throw 'AngularJS tastyTable directive: the resource '+
+ throw 'AngularJS tastyTable directive: the bind-resource '+
'it\'s not an object';
} else if (!resource.header && !resource.rows) {
- throw 'AngularJS tastyTable directive: the resource '+
+ throw 'AngularJS tastyTable directive: the bind-resource '+
'has the property header or rows undefined';
}
// Assuming if one header uses just one key it's based on the new pattern.
@@ -438,7 +176,7 @@ angular.module('ngTasty.table', [
$scope.rows = $filter('orderBy')($scope.rows, listSortBy, reverse);
}
}
- if ($attrs.filters) {
+ if ($attrs.bindFilters) {
$scope.rows = $filter('filter')($scope.rows, $scope.filters);
}
if ($scope.paginationDirective) {
@@ -467,7 +205,7 @@ angular.module('ngTasty.table', [
urlQuery = tastyUtil.setProperty(urlQuery, params, 'page');
urlQuery = tastyUtil.setProperty(urlQuery, params, 'count');
}
- if ($attrs.filters) {
+ if ($attrs.bindFilters) {
urlQuery = tastyUtil.joinObjects(urlQuery, filters, listKeyNotJoin);
}
return Object.keys(urlQuery).map(function(key) {
@@ -509,7 +247,7 @@ angular.module('ngTasty.table', [
};
// AngularJs $watch callbacks
- if ($attrs.filters) {
+ if ($attrs.bindFilters) {
$scope.$watch('filters', function (newValue, oldValue){
if (newValue !== oldValue) {
if ($scope.clientSide) {
@@ -561,22 +299,35 @@ angular.module('ngTasty.table', [
*
*/
-.directive('tastyThead', ["$filter", function($filter) {
+.directive('tastyThead', ["$filter", "tastyUtil", function($filter, tastyUtil) {
return {
restrict: 'AE',
require: '^tastyTable',
- scope: {
- 'notSortBy': '='
- },
+ scope: {},
templateUrl: 'template/table/head.html',
link: function (scope, element, attrs, tastyTable) {
'use strict';
- var iconUp, iconDown;
+ var iconUp, iconDown, newScopeName, listScopeToWatch;
// Thead it's called
tastyTable.activate('thead');
scope.bindOnce = tastyTable.bindOnce;
scope.columns = [];
+ listScopeToWatch = ['bindNotSortBy'];
+ listScopeToWatch.forEach(function (scopeName) {
+ newScopeName = scopeName.substring(4);
+ newScopeName = newScopeName.charAt(0).toLowerCase() + newScopeName.slice(1);
+ if (attrs[scopeName]) {
+ tastyUtil.bindTo(scopeName, scope, attrs, newScopeName);
+ } else if (attrs[newScopeName]) {
+ if (attrs[newScopeName][0] === '[') {
+ scope[newScopeName] = JSON.parse(attrs[newScopeName]);
+ } else {
+ scope[newScopeName] = attrs[newScopeName];
+ }
+ }
+ });
+
iconUp = 'fa fa-sort-up';
iconDown = 'fa fa-sort-down';
@@ -673,7 +424,7 @@ angular.module('ngTasty.table', [
*
*/
-.directive('tastyPagination', ["$filter", "$templateCache", "$http", "$compile", "$parse", "tableConfig", function($filter, $templateCache, $http, $compile, $parse, tableConfig) {
+.directive('tastyPagination', ["$filter", "$templateCache", "$http", "$compile", "tableConfig", "tastyUtil", function($filter, $templateCache, $http, $compile, tableConfig, tastyUtil) {
return {
restrict: 'AE',
require: '^tastyTable',
@@ -683,42 +434,31 @@ angular.module('ngTasty.table', [
},
link: function (scope, element, attrs, tastyTable) {
'use strict';
- var getPage, setCount, setPaginationRange,
- setPreviousRange, setRemainingRange,
- setPaginationRanges, listScopeToWatch;
-
+ var getPage, setCount, setPaginationRange, setPreviousRange,
+ setRemainingRange, setPaginationRanges, listScopeToWatch, newScopeName;
- listScopeToWatch = ['itemsPerPage', 'listItemsPerPage'];
+ listScopeToWatch = ['bindItemsPerPage', 'bindListItemsPerPage', 'bindTemplateUrl'];
listScopeToWatch.forEach(function (scopeName) {
- var lastValue, parentGet, compare, parentSet, parentValueWatch;
- if (!attrs[scopeName]) {
- return;
- }
- parentGet = $parse(attrs[scopeName]);
- if (parentGet.literal) {
- compare = equals;
- } else {
- compare = function(a,b) { return a === b || (a !== a && b !== b); };
- }
- parentSet = parentGet.assign;
- lastValue = scope[scopeName] = parentGet(scope.$parent);
- parentValueWatch = function parentValueWatch(parentValue) {
- if (!compare(parentValue, scope[scopeName])) {
- // we are out of sync and need to copy
- if (!compare(parentValue, lastValue)) {
- // parent changed and it has precedence
- $scope[scopeName] = parentValue;
- } else {
- // if the parent can be assigned then do so
- parentSet(scope.$parent, parentValue = scope[scopeName]);
- }
+ newScopeName = scopeName.substring(4);
+ newScopeName = newScopeName.charAt(0).toLowerCase() + newScopeName.slice(1);
+ if (attrs[scopeName]) {
+ tastyUtil.bindTo(scopeName, scope, attrs, newScopeName);
+ } else if (attrs[newScopeName]) {
+ if (newScopeName === 'itemsPerPage') {
+ scope[newScopeName] = parseInt(attrs[newScopeName]);
+ } else {
+ scope[newScopeName] = JSON.parse(attrs[newScopeName]);
}
- return lastValue = parentValue;
- };
- parentValueWatch.$stateful = true;
- scope.$parent.$watch($parse(attrs[scopeName], parentValueWatch), null, parentGet.literal);
+ }
});
+ if (scope.templateUrl) {
+ $http.get(scope.templateUrl, { cache: $templateCache })
+ .success(function(templateContent) {
+ element.replaceWith($compile(templateContent)(scope));
+ });
+ }
+
// Default configs
scope.itemsPerPage = scope.itemsPerPage || tableConfig.itemsPerPage;
scope.listItemsPerPage = scope.listItemsPerPage || tableConfig.listItemsPerPage;
@@ -834,3 +574,266 @@ angular.module('ngTasty.table', [
}
};
}]);
+
+/**
+ * @ngdoc filter
+ * @name cleanFieldName
+ *
+ * @description
+ * Calling toString will return the ...
+ *
+ * @example
+ ng-bind="key | cleanFieldName"
+ *
+ */
+angular.module('ngTasty.filter.cleanFieldName', [])
+.filter('cleanFieldName', function() {
+ return function (input) {
+ return input.replace(/[^a-zA-Z0-9-]+/g, '-');
+ };
+});
+
+/**
+ * @ngdoc filter
+ * @name filterInt
+ * @kind function
+ *
+ */
+angular.module('ngTasty.filter.filterInt', [])
+.filter('filterInt', function() {
+ return function (input) {
+ if(/^(\-|\+)?([0-9]+|Infinity)$/.test(input)) {
+ return Number(input);
+ }
+ return NaN;
+ };
+});
+
+/**
+ * @ngdoc filter
+ * @name range
+ * @kind function
+ *
+ * @description
+ * Create a list containing arithmetic progressions. The arguments must
+ * be plain integers. If the step argument is omitted, it defaults to 1.
+ * If the start argument is omitted, it defaults to 0.
+ *
+ * @example
+ ng-repeat="n in [] | range:1:30"
+ */
+angular.module('ngTasty.filter.range', ['ngTasty.filter.filterInt'])
+.filter('range', ["$filter", function($filter) {
+ return function(input, start, stop, step) {
+ start = $filter('filterInt')(start);
+ stop = $filter('filterInt')(stop);
+ step = $filter('filterInt')(step);
+ if (isNaN(start)) {
+ start = 0;
+ }
+ if (isNaN(stop)) {
+ stop = start;
+ start = 0;
+ }
+ if (isNaN(step)) {
+ step = 1;
+ }
+ if ((step > 0 && start >= stop) || (step < 0 && start <= stop)){
+ return [];
+ }
+ for (var i = start; step > 0 ? i < stop : i > stop; i += step){
+ input.push(i);
+ }
+ return input;
+ };
+}]);
+
+/**
+ * @ngdoc
+ * @name
+ *
+ */
+angular.module('ngTasty.service.bindTo', [])
+.factory('bindTo', ["$parse", function($parse) {
+ return function (scopeName, scope, attrs, newScopeName) {
+ var lastValue, parentGet, compare, parentSet,
+ parentValueWatch, isolateScopeName;
+ if (!attrs[scopeName]) {
+ return;
+ }
+ parentGet = $parse(attrs[scopeName]);
+ if (parentGet.literal) {
+ compare = equals;
+ } else {
+ compare = function(a,b) { return a === b || (a !== a && b !== b); };
+ }
+ if (newScopeName) {
+ isolateScopeName = newScopeName;
+ } else {
+ isolateScopeName = scopeName;
+ }
+ parentSet = parentGet.assign;
+ lastValue = scope[isolateScopeName] = parentGet(scope.$parent);
+ parentValueWatch = function parentValueWatch(parentValue) {
+ if (!compare(parentValue, scope[isolateScopeName])) {
+ // we are out of sync and need to copy
+ if (!compare(parentValue, lastValue)) {
+ // parent changed and it has precedence
+ scope[isolateScopeName] = parentValue;
+ } else {
+ // if the parent can be assigned then do so
+ parentSet(scope.$parent, parentValue = scope[isolateScopeName]);
+ }
+ }
+ return lastValue = parentValue;
+ };
+ parentValueWatch.$stateful = true;
+ scope.$parent.$watch($parse(attrs[scopeName], parentValueWatch), null, parentGet.literal);
+ };
+}]);
+/**
+ * @ngdoc
+ * @name
+ *
+ */
+angular.module('ngTasty.service.debounce', [])
+.factory('debounce', ["$timeout", function($timeout) {
+ return function(func, wait, immediate) {
+ var timeout;
+ return function() {
+ var context = this, args = arguments;
+ $timeout.cancel(timeout);
+ timeout = $timeout(function() {
+ timeout = null;
+ func.apply(context, args);
+ }, wait);
+ };
+ };
+}]);
+
+/**
+ * @ngdoc
+ * @name
+ *
+ */
+angular.module('ngTasty.service.joinObjects', ['ngTasty.service.setProperty'])
+.factory('joinObjects', ["setProperty", function(setProperty) {
+ return function(objOne, objTwo, listKeyNotJoin) {
+ listKeyNotJoin = listKeyNotJoin || [];
+ for (var attrname in objTwo) {
+ if (listKeyNotJoin.indexOf(attrname) < 0) {
+ setProperty(objOne, objTwo, attrname);
+ }
+ }
+ return objOne;
+ };
+}]);
+
+/**
+ * @ngdoc
+ * @name
+ *
+ */
+angular.module('ngTasty.service.setProperty', [])
+.factory('setProperty', function() {
+ return function(objOne, objTwo, attrname) {
+ if (typeof objTwo[attrname] !== 'undefined' &&
+ objTwo[attrname] !== null) {
+ objOne[attrname] = objTwo[attrname];
+ }
+ return objOne;
+ };
+});
+
+/**
+ * @ngdoc
+ * @name
+ *
+ */
+angular.module('ngTasty.service.tastyUtil', [
+ 'ngTasty.service.bindTo',
+ 'ngTasty.service.debounce',
+ 'ngTasty.service.setProperty',
+ 'ngTasty.service.joinObjects'
+])
+.factory('tastyUtil', ["debounce", "setProperty", "joinObjects", "bindTo", function(debounce, setProperty, joinObjects, bindTo) {
+ return {
+ 'bindTo': bindTo,
+ 'debounce': debounce,
+ 'setProperty': setProperty,
+ 'joinObjects': joinObjects
+ };
+}]);
+
+angular.module('ngTasty.service.webSocket', [
+ 'ngTasty.service'
+])
+.factory('WebSocket', function() {
+
+ return function(url) {
+ var blobURL = URL.createObjectURL(new Blob(['(', function() {
+ var WSWorker = (function() {
+ var _ws;
+
+ var initialize = function(url) {
+ _ws = new WebSocket(url);
+ };
+
+ var on = function(event) {
+ _ws.onmessage = function(response) {
+ var data = JSON.parse(response.data);
+ self.postMessage(data);
+ };
+ };
+
+ var send = function(data) {
+ _ws.send(data);
+ };
+
+ return {
+ initialize: initialize,
+ on: on,
+ send: send
+ };
+
+ })();
+
+ self.addEventListener('message', function(e) {
+ switch (e.data.cmd) {
+ case 'ws_new':
+ WSWorker.initialize(e.data.url);
+ break;
+ case 'ws_on':
+ WSWorker.on(e.data.event, e.data.cb);
+ break;
+ case 'ws_send':
+ WSWorker.send(JSON.stringify(e.data.data));
+ break;
+ default:
+ console.log('Unknown command: ' + e.data.cmd);
+ }
+ });
+
+ }.toString(), ')()'], { type: 'application/javascript' }));
+
+ var _worker = new Worker(blobURL);
+ URL.revokeObjectURL(blobURL);
+
+ _worker.postMessage({ cmd: 'ws_new', url: url });
+
+ return {
+ on: function(event, cb) {
+ _worker.postMessage({ cmd: 'ws_on' });
+ _worker.addEventListener('message', function(e) {
+ if (event === 'all' || e.data.type === event) {
+ cb(e.data);
+ }
+ });
+ },
+ send: function(data) {
+ _worker.postMessage({ cmd: 'ws_send', data: data });
+ }
+ };
+
+ };
+});
diff --git a/ng-tasty.min.js b/ng-tasty.min.js
index 59750f3..24d1db8 100644
--- a/ng-tasty.min.js
+++ b/ng-tasty.min.js
@@ -2,7 +2,7 @@
* ng-tasty
* https://github.com/Zizzamia/ng-tasty
- * Version: 0.3.3 - 2014-11-28
+ * Version: 0.4.0 - 2014-12-08
* License: MIT
*/
-angular.module("ngTasty",["ngTasty.filter","ngTasty.service","ngTasty.table"]),angular.module("ngTasty.filter",["ngTasty.filter.cleanFieldName","ngTasty.filter.filterInt","ngTasty.filter.range"]),angular.module("ngTasty.filter.cleanFieldName",[]).filter("cleanFieldName",function(){return function(input){return input.replace(/[^a-zA-Z0-9-]+/g,"-")}}),angular.module("ngTasty.filter.filterInt",[]).filter("filterInt",function(){return function(input){return/^(\-|\+)?([0-9]+|Infinity)$/.test(input)?Number(input):0/0}}),angular.module("ngTasty.filter.range",[]).filter("range",["$filter",function($filter){return function(input,start,stop,step){if(start=$filter("filterInt")(start),stop=$filter("filterInt")(stop),step=$filter("filterInt")(step),isNaN(start)&&(start=0),isNaN(stop)&&(stop=start,start=0),isNaN(step)&&(step=1),step>0&&start>=stop||0>step&&stop>=start)return[];for(var i=start;step>0?stop>i:i>stop;i+=step)input.push(i);return input}}]),angular.module("ngTasty.service",["ngTasty.service.tastyUtil","ngTasty.service.debounce","ngTasty.service.setProperty","ngTasty.service.joinObjects","ngTasty.service.webSocket"]),angular.module("ngTasty.service.tastyUtil",["ngTasty.service.debounce","ngTasty.service.setProperty","ngTasty.service.joinObjects"]).factory("tastyUtil",["debounce","setProperty","joinObjects",function(debounce,setProperty,joinObjects){return{debounce:debounce,setProperty:setProperty,joinObjects:joinObjects}}]),angular.module("ngTasty.service.debounce",[]).factory("debounce",["$timeout",function($timeout){return function(func,wait){var timeout;return function(){var context=this,args=arguments;$timeout.cancel(timeout),timeout=$timeout(function(){timeout=null,func.apply(context,args)},wait)}}}]),angular.module("ngTasty.service.setProperty",[]).factory("setProperty",function(){return function(objOne,objTwo,attrname){return"undefined"!=typeof objTwo[attrname]&&null!==objTwo[attrname]&&(objOne[attrname]=objTwo[attrname]),objOne}}),angular.module("ngTasty.service.joinObjects",[]).factory("joinObjects",["setProperty",function(setProperty){return function(objOne,objTwo,listKeyNotJoin){listKeyNotJoin=listKeyNotJoin||[];for(var attrname in objTwo)listKeyNotJoin.indexOf(attrname)<0&&setProperty(objOne,objTwo,attrname);return objOne}}]),angular.module("ngTasty.service.webSocket",["ngTasty.service"]).factory("WebSocket",function(){return function(url){var blobURL=URL.createObjectURL(new Blob(["(",function(){var WSWorker=function(){var _ws,initialize=function(url){_ws=new WebSocket(url)},on=function(){_ws.onmessage=function(response){var data=JSON.parse(response.data);self.postMessage(data)}},send=function(data){_ws.send(data)};return{initialize:initialize,on:on,send:send}}();self.addEventListener("message",function(e){switch(e.data.cmd){case"ws_new":WSWorker.initialize(e.data.url);break;case"ws_on":WSWorker.on(e.data.event,e.data.cb);break;case"ws_send":WSWorker.send(JSON.stringify(e.data.data));break;default:console.log("Unknown command: "+e.data.cmd)}})}.toString(),")()"],{type:"application/javascript"})),_worker=new Worker(blobURL);return URL.revokeObjectURL(blobURL),_worker.postMessage({cmd:"ws_new",url:url}),{on:function(event,cb){_worker.postMessage({cmd:"ws_on"}),_worker.addEventListener("message",function(e){("all"===event||e.data.type===event)&&cb(e.data)})},send:function(data){_worker.postMessage({cmd:"ws_send",data:data})}}}}),angular.module("ngTasty.table",["ngTasty.filter.cleanFieldName","ngTasty.filter.range","ngTasty.service.tastyUtil"]).constant("tableConfig",{init:{count:5,page:1,sortBy:void 0,sortOrder:void 0},query:{page:"page",count:"count",sortBy:"sort-by",sortOrder:"sort-order"},listItemsPerPage:[5,25,50,100],itemsPerPage:5,bindOnce:!0}).controller("TableController",["$scope","$attrs","$timeout","$filter","$parse","tableConfig","tastyUtil",function($scope,$attrs,$timeout,$filter,$parse,tableConfig,tastyUtil){"use strict";var listScopeToWatch,initTable;if(this.$scope=$scope,$scope.init={},$scope.query={},listScopeToWatch=["filters","init","query","resource","resourceCallback"],listScopeToWatch.forEach(function(scopeName){var lastValue,parentGet,compare,parentSet,parentValueWatch;$attrs[scopeName]&&(parentGet=$parse($attrs[scopeName]),compare=parentGet.literal?equals:function(a,b){return a===b||a!==a&&b!==b},parentSet=parentGet.assign,lastValue=$scope[scopeName]=parentGet($scope.$parent),parentValueWatch=function(parentValue){return compare(parentValue,$scope[scopeName])||(compare(parentValue,lastValue)?parentSet($scope.$parent,parentValue=$scope[scopeName]):$scope[scopeName]=parentValue),lastValue=parentValue},parentValueWatch.$stateful=!0,$scope.$parent.$watch($parse($attrs[scopeName],parentValueWatch),null,parentGet.literal))}),$scope.query.page=$scope.query.page||tableConfig.query.page,$scope.query.count=$scope.query.count||tableConfig.query.count,$scope.query.sortBy=$scope.query.sortBy||tableConfig.query.sortBy,$scope.query.sortOrder=$scope.query.sortOrder||tableConfig.query.sortOrder,$scope.init.count=$scope.init.count||tableConfig.init.count,$scope.init.page=$scope.init.page||tableConfig.init.page,$scope.init.sortBy=$scope.init.sortBy||tableConfig.init.sortBy,$scope.init.sortOrder=$scope.init.sortOrder||tableConfig.init.sortOrder,$scope.clientSide=!0,$scope.url="",$scope.header={columns:[]},$scope.rows=[],$scope.params={},$scope.pagination={count:$scope.init.count,page:$scope.init.page,pages:1,size:0},$scope.theadDirective=!1,$scope.paginationDirective=!1,!angular.isDefined($attrs.resource)&&!angular.isDefined($attrs.resourceCallback))throw"AngularJS tastyTable directive: need the resource or resource-callback attribute";if(angular.isDefined($attrs.resource)){if(!angular.isObject($scope.resource))throw"AngularJS tastyTable directive: the resource ("+$attrs.resource+") it's not an object";if(!$scope.resource.header&&!$scope.resource.rows)throw"AngularJS tastyTable directive: the resource ("+$attrs.resource+") has the property header or rows undefined"}if(angular.isDefined($attrs.resourceCallback)){if(!angular.isFunction($scope.resourceCallback))throw"AngularJS tastyTable directive: the resource-callback ("+$attrs.resourceCallback+") it's not a function";$scope.clientSide=!1}this.activate=function(directiveName){$scope[directiveName+"Directive"]=!0,$scope.params[directiveName]=!0},this.setParams=function(key,value){$scope.params[key]=value,["sortBy","sortOrder"].indexOf(key)>=0&&($scope.header[key]=value)},this.bindOnce=tableConfig.bindOnce,$scope.setDirectivesValues=function(resource){if(!angular.isObject(resource))throw"AngularJS tastyTable directive: the resource it's not an object";if(!resource.header&&!resource.rows)throw"AngularJS tastyTable directive: the resource has the property header or rows undefined";1===Object.keys(resource.header[0]).length&&(resource.header=resource.header.map(function(header){var key=Object.keys(header)[0];return{key:key,name:header[key]}})),$scope.header={columns:resource.header,sortBy:$scope.params.sortBy,sortOrder:$scope.params.sortOrder},$scope.clientSide||($scope.header.sortBy=$scope.header.sortBy||resource.sortBy,$scope.header.sortOrder=$scope.header.sortOrder||resource.sortOrder),$scope.rows=resource.rows,$scope.paginationDirective&&resource.pagination&&($scope.pagination.count=resource.pagination.count,$scope.pagination.page=resource.pagination.page,$scope.pagination.pages=resource.pagination.pages,$scope.pagination.size=resource.pagination.size)},$scope.buildClientResource=function(){var fromRow,toRow,rowToShow,reverse,listSortBy;$scope.theadDirective&&(reverse="asc"===$scope.header.sortOrder?!1:!0,listSortBy=[function(item){return item[$scope.header.sortBy]}],$scope.header.columns[0].key!==$scope.header.sortBy&&listSortBy.push(function(item){return item[$scope.header.columns[0].key]}),$scope.header.sortBy&&($scope.rows=$filter("orderBy")($scope.rows,listSortBy,reverse))),$attrs.filters&&($scope.rows=$filter("filter")($scope.rows,$scope.filters)),$scope.paginationDirective&&($scope.pagination.page=$scope.params.page,$scope.pagination.count=$scope.params.count,$scope.pagination.size=$scope.rows.length,$scope.pagination.pages=Math.ceil($scope.rows.length/$scope.pagination.count),toRow=$scope.pagination.count*$scope.pagination.page,fromRow=toRow-$scope.pagination.count,fromRow>=0&&toRow>=0&&(rowToShow=$scope.rows.slice(fromRow,toRow),$scope.rows=rowToShow))},$scope.buildUrl=function(params,filters){var urlQuery,value,listKeyNotJoin;return urlQuery={},listKeyNotJoin=["sortBy","sortOrder","page","count"],$scope.theadDirective&&(urlQuery=tastyUtil.setProperty(urlQuery,params,"sortBy"),urlQuery=tastyUtil.setProperty(urlQuery,params,"sortOrder")),$scope.paginationDirective&&(urlQuery=tastyUtil.setProperty(urlQuery,params,"page"),urlQuery=tastyUtil.setProperty(urlQuery,params,"count")),$attrs.filters&&(urlQuery=tastyUtil.joinObjects(urlQuery,filters,listKeyNotJoin)),Object.keys(urlQuery).map(function(key){return value=urlQuery[key],$scope.query[key]&&(key=$scope.query[key]),encodeURIComponent(key)+"="+encodeURIComponent(value)}).join("&")},$scope.updateClientSideResource=tastyUtil.debounce(function(){$scope.setDirectivesValues($scope.resource),$scope.buildClientResource()},60),$scope.updateServerSideResource=tastyUtil.debounce(function(){$scope.url=$scope.buildUrl($scope.params,$scope.filters),$scope.resourceCallback($scope.url,$scope.params).then(function(resource){$scope.setDirectivesValues(resource)})},60),initTable=function(){$scope.clientSide?($scope.params.sortBy=$scope.resource.sortBy||$scope.init.sortBy,$scope.params.sortOrder=$scope.resource.sortOrder||$scope.init.sortOrder,$scope.params.page=$scope.init.page,$scope.resource.pagination&&($scope.params.page=$scope.resource.pagination.page||$scope.init.page),$scope.updateClientSideResource()):($scope.params.sortBy=$scope.init.sortBy,$scope.params.sortOrder=$scope.init.sortOrder,$scope.params.page=$scope.init.page,$scope.updateServerSideResource())},$attrs.filters&&$scope.$watch("filters",function(newValue,oldValue){newValue!==oldValue&&($scope.clientSide?$scope.updateClientSideResource():$scope.updateServerSideResource())},!0),$scope.$watchCollection("params",function(newValue,oldValue){newValue!==oldValue&&($scope.clientSide?$scope.updateClientSideResource():$scope.updateServerSideResource())}),$scope.resource&&$scope.$watch("resource",function(newValue,oldValue){newValue!==oldValue&&($scope.params.sortBy=newValue.sortBy,$scope.params.sortOrder=newValue.sortOrder,$scope.updateClientSideResource())},!0),initTable()}]).directive("tastyTable",function(){return{restrict:"A",scope:!0,controller:"TableController"}}).directive("tastyThead",["$filter",function($filter){return{restrict:"AE",require:"^tastyTable",scope:{notSortBy:"="},templateUrl:"template/table/head.html",link:function(scope,element,attrs,tastyTable){"use strict";var iconUp,iconDown;tastyTable.activate("thead"),scope.bindOnce=tastyTable.bindOnce,scope.columns=[],iconUp="fa fa-sort-up",iconDown="fa fa-sort-down",scope.setColumns=function(){var lenHeader,active,sortable,sort,isSorted;scope.columns=[],lenHeader=scope.header.columns.length,scope.header.columns.forEach(function(column){column.style=column.style||{},sortable=!0,active=!1,isSorted="",angular.isArray(scope.notSortBy)&&(sortable=scope.notSortBy.length?scope.notSortBy.indexOf(column.key)<0:!1),(column.key===scope.header.sortBy||"-"+column.key===scope.header.sortBy)&&(active=!0),sort=$filter("cleanFieldName")(column.key),scope.header.sortBy==="-"+sort?isSorted=iconDown:scope.header.sortBy===sort&&(isSorted=iconUp),scope.columns.push({key:column.key,name:column.name,active:active,sortable:sortable,style:column.style,isSorted:isSorted})}),"dsc"===scope.header.sortOrder&&scope.header.sortBy&&"-"!==scope.header.sortBy[0]&&(scope.header.sortBy="-"+scope.header.sortBy)},scope.sortBy=function(column){if(!column.sortable)return!1;var columnName,sortOrder;columnName=$filter("cleanFieldName")(column.key),sortOrder=scope.header.sortBy===columnName?"dsc":"asc",tastyTable.setParams("sortBy",column.key),tastyTable.setParams("sortOrder",sortOrder)},scope.classToShow=function(column){var listClassToShow=[];return column.sortable&&listClassToShow.push("sortable"),column.active&&listClassToShow.push("active"),listClassToShow},tastyTable.$scope.$watchCollection("header",function(newValue,oldValue){newValue&&newValue!==oldValue&&(scope.header=newValue,scope.setColumns())})}}}]).directive("tastyPagination",["$filter","$templateCache","$http","$compile","$parse","tableConfig",function($filter,$templateCache,$http,$compile,$parse,tableConfig){return{restrict:"AE",require:"^tastyTable",scope:{},templateUrl:function(tElement,tAttrs){return tAttrs.templateUrl||"template/table/pagination.html"},link:function(scope,element,attrs,tastyTable){"use strict";var getPage,setCount,setPaginationRange,setPreviousRange,setRemainingRange,setPaginationRanges,listScopeToWatch;listScopeToWatch=["itemsPerPage","listItemsPerPage"],listScopeToWatch.forEach(function(scopeName){var lastValue,parentGet,compare,parentSet,parentValueWatch;attrs[scopeName]&&(parentGet=$parse(attrs[scopeName]),compare=parentGet.literal?equals:function(a,b){return a===b||a!==a&&b!==b},parentSet=parentGet.assign,lastValue=scope[scopeName]=parentGet(scope.$parent),parentValueWatch=function(parentValue){return compare(parentValue,scope[scopeName])||(compare(parentValue,lastValue)?parentSet(scope.$parent,parentValue=scope[scopeName]):$scope[scopeName]=parentValue),lastValue=parentValue},parentValueWatch.$stateful=!0,scope.$parent.$watch($parse(attrs[scopeName],parentValueWatch),null,parentGet.literal))}),scope.itemsPerPage=scope.itemsPerPage||tableConfig.itemsPerPage,scope.listItemsPerPage=scope.listItemsPerPage||tableConfig.listItemsPerPage,tastyTable.activate("pagination"),scope.pagination={},scope.pagMinRange=1,scope.pagMaxRange=1,getPage=function(numPage){tastyTable.setParams("page",numPage)},setCount=function(count){var maxItems,page;maxItems=count*scope.pagination.page,maxItems>scope.pagination.size&&(page=Math.ceil(scope.pagination.size/count),tastyTable.setParams("page",page)),tastyTable.setParams("count",count)},setPaginationRange=function(){var currentPage;currentPage=scope.pagination.page,currentPage>scope.pagination.pages&&(currentPage=scope.pagination.pages),scope.pagMinRange=currentPage-2>0?currentPage-2:1,scope.pagMaxRange=currentPage+2,scope.pagination.page=currentPage,setPaginationRanges()},setPreviousRange=function(){return scope.pagHideMinRange===!0||scope.pagMinRange<1?!1:(scope.pagMaxRange=scope.pagMinRange,scope.pagMinRange=scope.pagMaxRange-scope.itemsPerPage,setPaginationRanges(),void 0)},setRemainingRange=function(){return scope.pagHideMaxRange===!0||scope.pagMaxRange>scope.pagination.pages?!1:(scope.pagMinRange=scope.pagMaxRange,scope.pagMaxRange=scope.pagMinRange+scope.itemsPerPage,scope.pagMaxRange>scope.pagination.pages&&(scope.pagMaxRange=scope.pagination.pages),scope.pagMinRange=scope.pagMaxRange-scope.itemsPerPage,setPaginationRanges(),void 0)},setPaginationRanges=function(){scope.listItemsPerPageShow=[],scope.pagMinRange=scope.pagMinRange>0?scope.pagMinRange:1,scope.pagMaxRange=scope.pagMinRange+5,scope.pagMaxRange>scope.pagination.pages&&(scope.pagMaxRange=scope.pagination.pages+1),scope.pagHideMinRange=scope.pagMinRange<=1,scope.pagHideMaxRange=scope.pagMaxRange>=scope.pagination.pages,scope.classPageMinRange=scope.pagHideMinRange?"disabled":"",scope.classPageMaxRange=scope.pagHideMaxRange?"disabled":"";for(var i=scope.listItemsPerPage.length;i>=0;i--)if(scope.pagination.size>scope.listItemsPerPage[i]){scope.listItemsPerPageShow=scope.listItemsPerPage.slice(0,i+1);break}scope.rangePage=$filter("range")([],scope.pagMinRange,scope.pagMaxRange)},scope.classPaginationCount=function(count){return count==scope.pagination.count?"active":""},scope.classNumPage=function(numPage){return numPage==scope.pagination.page?"active":!1},scope.page={get:getPage,setCount:setCount,previous:setPreviousRange,remaining:setRemainingRange},tastyTable.$scope.$watchCollection("pagination",function(newValue,oldValue){newValue&&newValue!==oldValue&&(scope.pagination=newValue,setPaginationRange())}),scope.page.setCount(scope.itemsPerPage)}}}]);
\ No newline at end of file
+angular.module("ngTasty",["ngTasty.component.table"]),angular.module("ngTasty.component.table",["ngTasty.filter.cleanFieldName","ngTasty.filter.range","ngTasty.service.tastyUtil","ngTasty.tpls.TableHead","ngTasty.tpls.TablePagination"]).constant("tableConfig",{init:{count:5,page:1,sortBy:void 0,sortOrder:void 0},query:{page:"page",count:"count",sortBy:"sort-by",sortOrder:"sort-order"},listItemsPerPage:[5,25,50,100],itemsPerPage:5,bindOnce:!0}).controller("TableController",["$scope","$attrs","$timeout","$filter","tableConfig","tastyUtil",function($scope,$attrs,$timeout,$filter,tableConfig,tastyUtil){"use strict";var listScopeToWatch,initTable,newScopeName;if(this.$scope=$scope,$scope.init={},$scope.query={},listScopeToWatch=["bindFilters","bindInit","bindQuery","bindResource","bindResourceCallback"],listScopeToWatch.forEach(function(scopeName){newScopeName=scopeName.substring(4),newScopeName=newScopeName.charAt(0).toLowerCase()+newScopeName.slice(1),$attrs[scopeName]&&tastyUtil.bindTo(scopeName,$scope,$attrs,newScopeName)}),$scope.query.page=$scope.query.page||tableConfig.query.page,$scope.query.count=$scope.query.count||tableConfig.query.count,$scope.query.sortBy=$scope.query.sortBy||tableConfig.query.sortBy,$scope.query.sortOrder=$scope.query.sortOrder||tableConfig.query.sortOrder,$scope.init.count=$scope.init.count||tableConfig.init.count,$scope.init.page=$scope.init.page||tableConfig.init.page,$scope.init.sortBy=$scope.init.sortBy||tableConfig.init.sortBy,$scope.init.sortOrder=$scope.init.sortOrder||tableConfig.init.sortOrder,$scope.clientSide=!0,$scope.url="",$scope.header={columns:[]},$scope.rows=[],$scope.params={},$scope.pagination={count:$scope.init.count,page:$scope.init.page,pages:1,size:0},$scope.theadDirective=!1,$scope.paginationDirective=!1,!angular.isDefined($attrs.bindResource)&&!angular.isDefined($attrs.bindResourceCallback))throw"AngularJS tastyTable directive: need the bind-resource or bind-resource-callback attribute";if(angular.isDefined($attrs.bindResource)){if(!angular.isObject($scope.resource))throw"AngularJS tastyTable directive: the bind-resource ("+$attrs.bindResource+") it's not an object";if(!$scope.resource.header&&!$scope.resource.rows)throw"AngularJS tastyTable directive: the bind-resource ("+$attrs.bindResource+") has the property header or rows undefined"}if(angular.isDefined($attrs.bindResourceCallback)){if(!angular.isFunction($scope.resourceCallback))throw"AngularJS tastyTable directive: the bind-resource-callback ("+$attrs.bindResourceCallback+") it's not a function";$scope.clientSide=!1}this.activate=function(directiveName){$scope[directiveName+"Directive"]=!0,$scope.params[directiveName]=!0},this.setParams=function(key,value){$scope.params[key]=value,["sortBy","sortOrder"].indexOf(key)>=0&&($scope.header[key]=value)},this.bindOnce=tableConfig.bindOnce,$scope.setDirectivesValues=function(resource){if(!angular.isObject(resource))throw"AngularJS tastyTable directive: the bind-resource it's not an object";if(!resource.header&&!resource.rows)throw"AngularJS tastyTable directive: the bind-resource has the property header or rows undefined";1===Object.keys(resource.header[0]).length&&(resource.header=resource.header.map(function(header){var key=Object.keys(header)[0];return{key:key,name:header[key]}})),$scope.header={columns:resource.header,sortBy:$scope.params.sortBy,sortOrder:$scope.params.sortOrder},$scope.clientSide||($scope.header.sortBy=$scope.header.sortBy||resource.sortBy,$scope.header.sortOrder=$scope.header.sortOrder||resource.sortOrder),$scope.rows=resource.rows,$scope.paginationDirective&&resource.pagination&&($scope.pagination.count=resource.pagination.count,$scope.pagination.page=resource.pagination.page,$scope.pagination.pages=resource.pagination.pages,$scope.pagination.size=resource.pagination.size)},$scope.buildClientResource=function(){var fromRow,toRow,rowToShow,reverse,listSortBy;$scope.theadDirective&&(reverse="asc"===$scope.header.sortOrder?!1:!0,listSortBy=[function(item){return item[$scope.header.sortBy]}],$scope.header.columns[0].key!==$scope.header.sortBy&&listSortBy.push(function(item){return item[$scope.header.columns[0].key]}),$scope.header.sortBy&&($scope.rows=$filter("orderBy")($scope.rows,listSortBy,reverse))),$attrs.bindFilters&&($scope.rows=$filter("filter")($scope.rows,$scope.filters)),$scope.paginationDirective&&($scope.pagination.page=$scope.params.page,$scope.pagination.count=$scope.params.count,$scope.pagination.size=$scope.rows.length,$scope.pagination.pages=Math.ceil($scope.rows.length/$scope.pagination.count),toRow=$scope.pagination.count*$scope.pagination.page,fromRow=toRow-$scope.pagination.count,fromRow>=0&&toRow>=0&&(rowToShow=$scope.rows.slice(fromRow,toRow),$scope.rows=rowToShow))},$scope.buildUrl=function(params,filters){var urlQuery,value,listKeyNotJoin;return urlQuery={},listKeyNotJoin=["sortBy","sortOrder","page","count"],$scope.theadDirective&&(urlQuery=tastyUtil.setProperty(urlQuery,params,"sortBy"),urlQuery=tastyUtil.setProperty(urlQuery,params,"sortOrder")),$scope.paginationDirective&&(urlQuery=tastyUtil.setProperty(urlQuery,params,"page"),urlQuery=tastyUtil.setProperty(urlQuery,params,"count")),$attrs.bindFilters&&(urlQuery=tastyUtil.joinObjects(urlQuery,filters,listKeyNotJoin)),Object.keys(urlQuery).map(function(key){return value=urlQuery[key],$scope.query[key]&&(key=$scope.query[key]),encodeURIComponent(key)+"="+encodeURIComponent(value)}).join("&")},$scope.updateClientSideResource=tastyUtil.debounce(function(){$scope.setDirectivesValues($scope.resource),$scope.buildClientResource()},60),$scope.updateServerSideResource=tastyUtil.debounce(function(){$scope.url=$scope.buildUrl($scope.params,$scope.filters),$scope.resourceCallback($scope.url,$scope.params).then(function(resource){$scope.setDirectivesValues(resource)})},60),initTable=function(){$scope.clientSide?($scope.params.sortBy=$scope.resource.sortBy||$scope.init.sortBy,$scope.params.sortOrder=$scope.resource.sortOrder||$scope.init.sortOrder,$scope.params.page=$scope.init.page,$scope.resource.pagination&&($scope.params.page=$scope.resource.pagination.page||$scope.init.page),$scope.updateClientSideResource()):($scope.params.sortBy=$scope.init.sortBy,$scope.params.sortOrder=$scope.init.sortOrder,$scope.params.page=$scope.init.page,$scope.updateServerSideResource())},$attrs.bindFilters&&$scope.$watch("filters",function(newValue,oldValue){newValue!==oldValue&&($scope.clientSide?$scope.updateClientSideResource():$scope.updateServerSideResource())},!0),$scope.$watchCollection("params",function(newValue,oldValue){newValue!==oldValue&&($scope.clientSide?$scope.updateClientSideResource():$scope.updateServerSideResource())}),$scope.resource&&$scope.$watch("resource",function(newValue,oldValue){newValue!==oldValue&&($scope.params.sortBy=newValue.sortBy,$scope.params.sortOrder=newValue.sortOrder,$scope.updateClientSideResource())},!0),initTable()}]).directive("tastyTable",function(){return{restrict:"A",scope:!0,controller:"TableController"}}).directive("tastyThead",["$filter","tastyUtil",function($filter,tastyUtil){return{restrict:"AE",require:"^tastyTable",scope:{},templateUrl:"template/table/head.html",link:function(scope,element,attrs,tastyTable){"use strict";var iconUp,iconDown,newScopeName,listScopeToWatch;tastyTable.activate("thead"),scope.bindOnce=tastyTable.bindOnce,scope.columns=[],listScopeToWatch=["bindNotSortBy"],listScopeToWatch.forEach(function(scopeName){newScopeName=scopeName.substring(4),newScopeName=newScopeName.charAt(0).toLowerCase()+newScopeName.slice(1),attrs[scopeName]?tastyUtil.bindTo(scopeName,scope,attrs,newScopeName):attrs[newScopeName]&&(scope[newScopeName]="["===attrs[newScopeName][0]?JSON.parse(attrs[newScopeName]):attrs[newScopeName])}),iconUp="fa fa-sort-up",iconDown="fa fa-sort-down",scope.setColumns=function(){var lenHeader,active,sortable,sort,isSorted;scope.columns=[],lenHeader=scope.header.columns.length,scope.header.columns.forEach(function(column){column.style=column.style||{},sortable=!0,active=!1,isSorted="",angular.isArray(scope.notSortBy)&&(sortable=scope.notSortBy.length?scope.notSortBy.indexOf(column.key)<0:!1),(column.key===scope.header.sortBy||"-"+column.key===scope.header.sortBy)&&(active=!0),sort=$filter("cleanFieldName")(column.key),scope.header.sortBy==="-"+sort?isSorted=iconDown:scope.header.sortBy===sort&&(isSorted=iconUp),scope.columns.push({key:column.key,name:column.name,active:active,sortable:sortable,style:column.style,isSorted:isSorted})}),"dsc"===scope.header.sortOrder&&scope.header.sortBy&&"-"!==scope.header.sortBy[0]&&(scope.header.sortBy="-"+scope.header.sortBy)},scope.sortBy=function(column){if(!column.sortable)return!1;var columnName,sortOrder;columnName=$filter("cleanFieldName")(column.key),sortOrder=scope.header.sortBy===columnName?"dsc":"asc",tastyTable.setParams("sortBy",column.key),tastyTable.setParams("sortOrder",sortOrder)},scope.classToShow=function(column){var listClassToShow=[];return column.sortable&&listClassToShow.push("sortable"),column.active&&listClassToShow.push("active"),listClassToShow},tastyTable.$scope.$watchCollection("header",function(newValue,oldValue){newValue&&newValue!==oldValue&&(scope.header=newValue,scope.setColumns())})}}}]).directive("tastyPagination",["$filter","$templateCache","$http","$compile","tableConfig","tastyUtil",function($filter,$templateCache,$http,$compile,tableConfig,tastyUtil){return{restrict:"AE",require:"^tastyTable",scope:{},templateUrl:function(tElement,tAttrs){return tAttrs.templateUrl||"template/table/pagination.html"},link:function(scope,element,attrs,tastyTable){"use strict";var getPage,setCount,setPaginationRange,setPreviousRange,setRemainingRange,setPaginationRanges,listScopeToWatch,newScopeName;listScopeToWatch=["bindItemsPerPage","bindListItemsPerPage","bindTemplateUrl"],listScopeToWatch.forEach(function(scopeName){newScopeName=scopeName.substring(4),newScopeName=newScopeName.charAt(0).toLowerCase()+newScopeName.slice(1),attrs[scopeName]?tastyUtil.bindTo(scopeName,scope,attrs,newScopeName):attrs[newScopeName]&&(scope[newScopeName]="itemsPerPage"===newScopeName?parseInt(attrs[newScopeName]):JSON.parse(attrs[newScopeName]))}),scope.templateUrl&&$http.get(scope.templateUrl,{cache:$templateCache}).success(function(templateContent){element.replaceWith($compile(templateContent)(scope))}),scope.itemsPerPage=scope.itemsPerPage||tableConfig.itemsPerPage,scope.listItemsPerPage=scope.listItemsPerPage||tableConfig.listItemsPerPage,tastyTable.activate("pagination"),scope.pagination={},scope.pagMinRange=1,scope.pagMaxRange=1,getPage=function(numPage){tastyTable.setParams("page",numPage)},setCount=function(count){var maxItems,page;maxItems=count*scope.pagination.page,maxItems>scope.pagination.size&&(page=Math.ceil(scope.pagination.size/count),tastyTable.setParams("page",page)),tastyTable.setParams("count",count)},setPaginationRange=function(){var currentPage;currentPage=scope.pagination.page,currentPage>scope.pagination.pages&&(currentPage=scope.pagination.pages),scope.pagMinRange=currentPage-2>0?currentPage-2:1,scope.pagMaxRange=currentPage+2,scope.pagination.page=currentPage,setPaginationRanges()},setPreviousRange=function(){return scope.pagHideMinRange===!0||scope.pagMinRange<1?!1:(scope.pagMaxRange=scope.pagMinRange,scope.pagMinRange=scope.pagMaxRange-scope.itemsPerPage,setPaginationRanges(),void 0)},setRemainingRange=function(){return scope.pagHideMaxRange===!0||scope.pagMaxRange>scope.pagination.pages?!1:(scope.pagMinRange=scope.pagMaxRange,scope.pagMaxRange=scope.pagMinRange+scope.itemsPerPage,scope.pagMaxRange>scope.pagination.pages&&(scope.pagMaxRange=scope.pagination.pages),scope.pagMinRange=scope.pagMaxRange-scope.itemsPerPage,setPaginationRanges(),void 0)},setPaginationRanges=function(){scope.listItemsPerPageShow=[],scope.pagMinRange=scope.pagMinRange>0?scope.pagMinRange:1,scope.pagMaxRange=scope.pagMinRange+5,scope.pagMaxRange>scope.pagination.pages&&(scope.pagMaxRange=scope.pagination.pages+1),scope.pagHideMinRange=scope.pagMinRange<=1,scope.pagHideMaxRange=scope.pagMaxRange>=scope.pagination.pages,scope.classPageMinRange=scope.pagHideMinRange?"disabled":"",scope.classPageMaxRange=scope.pagHideMaxRange?"disabled":"";for(var i=scope.listItemsPerPage.length;i>=0;i--)if(scope.pagination.size>scope.listItemsPerPage[i]){scope.listItemsPerPageShow=scope.listItemsPerPage.slice(0,i+1);break}scope.rangePage=$filter("range")([],scope.pagMinRange,scope.pagMaxRange)},scope.classPaginationCount=function(count){return count==scope.pagination.count?"active":""},scope.classNumPage=function(numPage){return numPage==scope.pagination.page?"active":!1},scope.page={get:getPage,setCount:setCount,previous:setPreviousRange,remaining:setRemainingRange},tastyTable.$scope.$watchCollection("pagination",function(newValue,oldValue){newValue&&newValue!==oldValue&&(scope.pagination=newValue,setPaginationRange())}),scope.page.setCount(scope.itemsPerPage)}}}]),angular.module("ngTasty.filter.cleanFieldName",[]).filter("cleanFieldName",function(){return function(input){return input.replace(/[^a-zA-Z0-9-]+/g,"-")}}),angular.module("ngTasty.filter.filterInt",[]).filter("filterInt",function(){return function(input){return/^(\-|\+)?([0-9]+|Infinity)$/.test(input)?Number(input):0/0}}),angular.module("ngTasty.filter.range",["ngTasty.filter.filterInt"]).filter("range",["$filter",function($filter){return function(input,start,stop,step){if(start=$filter("filterInt")(start),stop=$filter("filterInt")(stop),step=$filter("filterInt")(step),isNaN(start)&&(start=0),isNaN(stop)&&(stop=start,start=0),isNaN(step)&&(step=1),step>0&&start>=stop||0>step&&stop>=start)return[];for(var i=start;step>0?stop>i:i>stop;i+=step)input.push(i);return input}}]),angular.module("ngTasty.service.bindTo",[]).factory("bindTo",["$parse",function($parse){return function(scopeName,scope,attrs,newScopeName){var lastValue,parentGet,compare,parentSet,parentValueWatch,isolateScopeName;attrs[scopeName]&&(parentGet=$parse(attrs[scopeName]),compare=parentGet.literal?equals:function(a,b){return a===b||a!==a&&b!==b},isolateScopeName=newScopeName?newScopeName:scopeName,parentSet=parentGet.assign,lastValue=scope[isolateScopeName]=parentGet(scope.$parent),parentValueWatch=function(parentValue){return compare(parentValue,scope[isolateScopeName])||(compare(parentValue,lastValue)?parentSet(scope.$parent,parentValue=scope[isolateScopeName]):scope[isolateScopeName]=parentValue),lastValue=parentValue},parentValueWatch.$stateful=!0,scope.$parent.$watch($parse(attrs[scopeName],parentValueWatch),null,parentGet.literal))}}]),angular.module("ngTasty.service.debounce",[]).factory("debounce",["$timeout",function($timeout){return function(func,wait){var timeout;return function(){var context=this,args=arguments;$timeout.cancel(timeout),timeout=$timeout(function(){timeout=null,func.apply(context,args)},wait)}}}]),angular.module("ngTasty.service.joinObjects",["ngTasty.service.setProperty"]).factory("joinObjects",["setProperty",function(setProperty){return function(objOne,objTwo,listKeyNotJoin){listKeyNotJoin=listKeyNotJoin||[];for(var attrname in objTwo)listKeyNotJoin.indexOf(attrname)<0&&setProperty(objOne,objTwo,attrname);return objOne}}]),angular.module("ngTasty.service.setProperty",[]).factory("setProperty",function(){return function(objOne,objTwo,attrname){return"undefined"!=typeof objTwo[attrname]&&null!==objTwo[attrname]&&(objOne[attrname]=objTwo[attrname]),objOne}}),angular.module("ngTasty.service.tastyUtil",["ngTasty.service.bindTo","ngTasty.service.debounce","ngTasty.service.setProperty","ngTasty.service.joinObjects"]).factory("tastyUtil",["debounce","setProperty","joinObjects","bindTo",function(debounce,setProperty,joinObjects,bindTo){return{bindTo:bindTo,debounce:debounce,setProperty:setProperty,joinObjects:joinObjects}}]),angular.module("ngTasty.service.webSocket",["ngTasty.service"]).factory("WebSocket",function(){return function(url){var blobURL=URL.createObjectURL(new Blob(["(",function(){var WSWorker=function(){var _ws,initialize=function(url){_ws=new WebSocket(url)},on=function(){_ws.onmessage=function(response){var data=JSON.parse(response.data);self.postMessage(data)}},send=function(data){_ws.send(data)};return{initialize:initialize,on:on,send:send}}();self.addEventListener("message",function(e){switch(e.data.cmd){case"ws_new":WSWorker.initialize(e.data.url);break;case"ws_on":WSWorker.on(e.data.event,e.data.cb);break;case"ws_send":WSWorker.send(JSON.stringify(e.data.data));break;default:console.log("Unknown command: "+e.data.cmd)}})}.toString(),")()"],{type:"application/javascript"})),_worker=new Worker(blobURL);return URL.revokeObjectURL(blobURL),_worker.postMessage({cmd:"ws_new",url:url}),{on:function(event,cb){_worker.postMessage({cmd:"ws_on"}),_worker.addEventListener("message",function(e){("all"===event||e.data.type===event)&&cb(e.data)})},send:function(data){_worker.postMessage({cmd:"ws_send",data:data})}}}});
\ No newline at end of file
diff --git a/package.json b/package.json
index 89c298f..cc1bd71 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "ng-tasty",
- "version": "0.3.3",
+ "version": "0.4.0",
"description": "A lightweight, flexible, and tasty collection of reusable UI components for AngularJS.",
"main": "index.js",
"repository": {
diff --git a/src/table/table.js b/src/component/table.js
similarity index 81%
rename from src/table/table.js
rename to src/component/table.js
index 06e048b..9bb8cb8 100644
--- a/src/table/table.js
+++ b/src/component/table.js
@@ -8,10 +8,12 @@
*
*/
-angular.module('ngTasty.table', [
+angular.module('ngTasty.component.table', [
'ngTasty.filter.cleanFieldName',
'ngTasty.filter.range',
- 'ngTasty.service.tastyUtil'
+ 'ngTasty.service.tastyUtil',
+ 'ngTasty.tpls.TableHead',
+ 'ngTasty.tpls.TablePagination'
])
.constant('tableConfig', {
init: {
@@ -30,42 +32,20 @@ angular.module('ngTasty.table', [
itemsPerPage: 5,
bindOnce: true
})
-.controller('TableController', function($scope, $attrs, $timeout, $filter, $parse, tableConfig, tastyUtil) {
+.controller('TableController', function($scope, $attrs, $timeout, $filter, tableConfig, tastyUtil) {
'use strict';
- var listScopeToWatch, initTable;
+ var listScopeToWatch, initTable, newScopeName;
this.$scope = $scope;
$scope.init = {};
$scope.query = {};
- listScopeToWatch = ['filters', 'init', 'query', 'resource', 'resourceCallback'];
+ listScopeToWatch = ['bindFilters', 'bindInit', 'bindQuery', 'bindResource', 'bindResourceCallback'];
listScopeToWatch.forEach(function (scopeName) {
- var lastValue, parentGet, compare, parentSet, parentValueWatch;
- if (!$attrs[scopeName]) {
- return;
+ newScopeName = scopeName.substring(4);
+ newScopeName = newScopeName.charAt(0).toLowerCase() + newScopeName.slice(1);
+ if ($attrs[scopeName]) {
+ tastyUtil.bindTo(scopeName, $scope, $attrs, newScopeName);
}
- parentGet = $parse($attrs[scopeName]);
- if (parentGet.literal) {
- compare = equals;
- } else {
- compare = function(a,b) { return a === b || (a !== a && b !== b); };
- }
- parentSet = parentGet.assign;
- lastValue = $scope[scopeName] = parentGet($scope.$parent);
- parentValueWatch = function parentValueWatch(parentValue) {
- if (!compare(parentValue, $scope[scopeName])) {
- // we are out of sync and need to copy
- if (!compare(parentValue, lastValue)) {
- // parent changed and it has precedence
- $scope[scopeName] = parentValue;
- } else {
- // if the parent can be assigned then do so
- parentSet($scope.$parent, parentValue = $scope[scopeName]);
- }
- }
- return lastValue = parentValue;
- };
- parentValueWatch.$stateful = true;
- $scope.$parent.$watch($parse($attrs[scopeName], parentValueWatch), null, parentGet.literal);
});
// Default configs
@@ -99,22 +79,22 @@ angular.module('ngTasty.table', [
* In the future you will have a way to change
* these values by an isolate optional scope variable,
* more info here https://github.com/angular/angular.js/issues/6404 */
- if (!angular.isDefined($attrs.resource) && !angular.isDefined($attrs.resourceCallback)) {
- throw 'AngularJS tastyTable directive: need the resource or resource-callback attribute';
+ if (!angular.isDefined($attrs.bindResource) && !angular.isDefined($attrs.bindResourceCallback)) {
+ throw 'AngularJS tastyTable directive: need the bind-resource or bind-resource-callback attribute';
}
- if (angular.isDefined($attrs.resource)) {
+ if (angular.isDefined($attrs.bindResource)) {
if (!angular.isObject($scope.resource)) {
- throw 'AngularJS tastyTable directive: the resource ('+
- $attrs.resource + ') it\'s not an object';
+ throw 'AngularJS tastyTable directive: the bind-resource ('+
+ $attrs.bindResource + ') it\'s not an object';
} else if (!$scope.resource.header && !$scope.resource.rows) {
- throw 'AngularJS tastyTable directive: the resource ('+
- $attrs.resource + ') has the property header or rows undefined';
+ throw 'AngularJS tastyTable directive: the bind-resource ('+
+ $attrs.bindResource + ') has the property header or rows undefined';
}
}
- if (angular.isDefined($attrs.resourceCallback)) {
+ if (angular.isDefined($attrs.bindResourceCallback)) {
if (!angular.isFunction($scope.resourceCallback)) {
- throw 'AngularJS tastyTable directive: the resource-callback ('+
- $attrs.resourceCallback + ') it\'s not a function';
+ throw 'AngularJS tastyTable directive: the bind-resource-callback ('+
+ $attrs.bindResourceCallback + ') it\'s not a function';
}
$scope.clientSide = false;
}
@@ -137,10 +117,10 @@ angular.module('ngTasty.table', [
$scope.setDirectivesValues = function (resource) {
if (!angular.isObject(resource)) {
- throw 'AngularJS tastyTable directive: the resource '+
+ throw 'AngularJS tastyTable directive: the bind-resource '+
'it\'s not an object';
} else if (!resource.header && !resource.rows) {
- throw 'AngularJS tastyTable directive: the resource '+
+ throw 'AngularJS tastyTable directive: the bind-resource '+
'has the property header or rows undefined';
}
// Assuming if one header uses just one key it's based on the new pattern.
@@ -188,7 +168,7 @@ angular.module('ngTasty.table', [
$scope.rows = $filter('orderBy')($scope.rows, listSortBy, reverse);
}
}
- if ($attrs.filters) {
+ if ($attrs.bindFilters) {
$scope.rows = $filter('filter')($scope.rows, $scope.filters);
}
if ($scope.paginationDirective) {
@@ -217,7 +197,7 @@ angular.module('ngTasty.table', [
urlQuery = tastyUtil.setProperty(urlQuery, params, 'page');
urlQuery = tastyUtil.setProperty(urlQuery, params, 'count');
}
- if ($attrs.filters) {
+ if ($attrs.bindFilters) {
urlQuery = tastyUtil.joinObjects(urlQuery, filters, listKeyNotJoin);
}
return Object.keys(urlQuery).map(function(key) {
@@ -259,7 +239,7 @@ angular.module('ngTasty.table', [
};
// AngularJs $watch callbacks
- if ($attrs.filters) {
+ if ($attrs.bindFilters) {
$scope.$watch('filters', function (newValue, oldValue){
if (newValue !== oldValue) {
if ($scope.clientSide) {
@@ -311,22 +291,35 @@ angular.module('ngTasty.table', [
*
*/
-.directive('tastyThead', function($filter) {
+.directive('tastyThead', function($filter, tastyUtil) {
return {
restrict: 'AE',
require: '^tastyTable',
- scope: {
- 'notSortBy': '='
- },
+ scope: {},
templateUrl: 'template/table/head.html',
link: function (scope, element, attrs, tastyTable) {
'use strict';
- var iconUp, iconDown;
+ var iconUp, iconDown, newScopeName, listScopeToWatch;
// Thead it's called
tastyTable.activate('thead');
scope.bindOnce = tastyTable.bindOnce;
scope.columns = [];
+ listScopeToWatch = ['bindNotSortBy'];
+ listScopeToWatch.forEach(function (scopeName) {
+ newScopeName = scopeName.substring(4);
+ newScopeName = newScopeName.charAt(0).toLowerCase() + newScopeName.slice(1);
+ if (attrs[scopeName]) {
+ tastyUtil.bindTo(scopeName, scope, attrs, newScopeName);
+ } else if (attrs[newScopeName]) {
+ if (attrs[newScopeName][0] === '[') {
+ scope[newScopeName] = JSON.parse(attrs[newScopeName]);
+ } else {
+ scope[newScopeName] = attrs[newScopeName];
+ }
+ }
+ });
+
iconUp = 'fa fa-sort-up';
iconDown = 'fa fa-sort-down';
@@ -423,7 +416,7 @@ angular.module('ngTasty.table', [
*
*/
-.directive('tastyPagination', function($filter, $templateCache, $http, $compile, $parse, tableConfig) {
+.directive('tastyPagination', function($filter, $templateCache, $http, $compile, tableConfig, tastyUtil) {
return {
restrict: 'AE',
require: '^tastyTable',
@@ -433,42 +426,31 @@ angular.module('ngTasty.table', [
},
link: function (scope, element, attrs, tastyTable) {
'use strict';
- var getPage, setCount, setPaginationRange,
- setPreviousRange, setRemainingRange,
- setPaginationRanges, listScopeToWatch;
-
+ var getPage, setCount, setPaginationRange, setPreviousRange,
+ setRemainingRange, setPaginationRanges, listScopeToWatch, newScopeName;
- listScopeToWatch = ['itemsPerPage', 'listItemsPerPage'];
+ listScopeToWatch = ['bindItemsPerPage', 'bindListItemsPerPage', 'bindTemplateUrl'];
listScopeToWatch.forEach(function (scopeName) {
- var lastValue, parentGet, compare, parentSet, parentValueWatch;
- if (!attrs[scopeName]) {
- return;
- }
- parentGet = $parse(attrs[scopeName]);
- if (parentGet.literal) {
- compare = equals;
- } else {
- compare = function(a,b) { return a === b || (a !== a && b !== b); };
- }
- parentSet = parentGet.assign;
- lastValue = scope[scopeName] = parentGet(scope.$parent);
- parentValueWatch = function parentValueWatch(parentValue) {
- if (!compare(parentValue, scope[scopeName])) {
- // we are out of sync and need to copy
- if (!compare(parentValue, lastValue)) {
- // parent changed and it has precedence
- $scope[scopeName] = parentValue;
- } else {
- // if the parent can be assigned then do so
- parentSet(scope.$parent, parentValue = scope[scopeName]);
- }
+ newScopeName = scopeName.substring(4);
+ newScopeName = newScopeName.charAt(0).toLowerCase() + newScopeName.slice(1);
+ if (attrs[scopeName]) {
+ tastyUtil.bindTo(scopeName, scope, attrs, newScopeName);
+ } else if (attrs[newScopeName]) {
+ if (newScopeName === 'itemsPerPage') {
+ scope[newScopeName] = parseInt(attrs[newScopeName]);
+ } else {
+ scope[newScopeName] = JSON.parse(attrs[newScopeName]);
}
- return lastValue = parentValue;
- };
- parentValueWatch.$stateful = true;
- scope.$parent.$watch($parse(attrs[scopeName], parentValueWatch), null, parentGet.literal);
+ }
});
+ if (scope.templateUrl) {
+ $http.get(scope.templateUrl, { cache: $templateCache })
+ .success(function(templateContent) {
+ element.replaceWith($compile(templateContent)(scope));
+ });
+ }
+
// Default configs
scope.itemsPerPage = scope.itemsPerPage || tableConfig.itemsPerPage;
scope.listItemsPerPage = scope.listItemsPerPage || tableConfig.listItemsPerPage;
diff --git a/src/filter/clean-field-name.js b/src/filter/clean-field-name.js
new file mode 100644
index 0000000..e3ffb92
--- /dev/null
+++ b/src/filter/clean-field-name.js
@@ -0,0 +1,17 @@
+/**
+ * @ngdoc filter
+ * @name cleanFieldName
+ *
+ * @description
+ * Calling toString will return the ...
+ *
+ * @example
+ ng-bind="key | cleanFieldName"
+ *
+ */
+angular.module('ngTasty.filter.cleanFieldName', [])
+.filter('cleanFieldName', function() {
+ return function (input) {
+ return input.replace(/[^a-zA-Z0-9-]+/g, '-');
+ };
+});
diff --git a/src/filter/filter-int.js b/src/filter/filter-int.js
new file mode 100644
index 0000000..cf337cc
--- /dev/null
+++ b/src/filter/filter-int.js
@@ -0,0 +1,15 @@
+/**
+ * @ngdoc filter
+ * @name filterInt
+ * @kind function
+ *
+ */
+angular.module('ngTasty.filter.filterInt', [])
+.filter('filterInt', function() {
+ return function (input) {
+ if(/^(\-|\+)?([0-9]+|Infinity)$/.test(input)) {
+ return Number(input);
+ }
+ return NaN;
+ };
+});
diff --git a/src/filter/filter.js b/src/filter/range.js
similarity index 52%
rename from src/filter/filter.js
rename to src/filter/range.js
index 712c83c..0a847ff 100644
--- a/src/filter/filter.js
+++ b/src/filter/range.js
@@ -1,48 +1,3 @@
-/**
- * @ngdoc
- * @name
- *
- */
-angular.module('ngTasty.filter', [
- 'ngTasty.filter.cleanFieldName',
- 'ngTasty.filter.filterInt',
- 'ngTasty.filter.range'
-]);
-
-/**
- * @ngdoc filter
- * @name cleanFieldName
- *
- * @description
- * Calling toString will return the ...
- *
- * @example
- ng-bind="key | cleanFieldName"
- *
- */
-angular.module('ngTasty.filter.cleanFieldName', [])
-.filter('cleanFieldName', function() {
- return function (input) {
- return input.replace(/[^a-zA-Z0-9-]+/g, '-');
- };
-});
-
-/**
- * @ngdoc filter
- * @name filterInt
- * @kind function
- *
- */
-angular.module('ngTasty.filter.filterInt', [])
-.filter('filterInt', function() {
- return function (input) {
- if(/^(\-|\+)?([0-9]+|Infinity)$/.test(input)) {
- return Number(input);
- }
- return NaN;
- };
-});
-
/**
* @ngdoc filter
* @name range
@@ -56,7 +11,7 @@ angular.module('ngTasty.filter.filterInt', [])
* @example
ng-repeat="n in [] | range:1:30"
*/
-angular.module('ngTasty.filter.range', [])
+angular.module('ngTasty.filter.range', ['ngTasty.filter.filterInt'])
.filter('range', function($filter) {
return function(input, start, stop, step) {
start = $filter('filterInt')(start);
diff --git a/src/service/bind-to.js b/src/service/bind-to.js
new file mode 100644
index 0000000..4e26420
--- /dev/null
+++ b/src/service/bind-to.js
@@ -0,0 +1,43 @@
+/**
+ * @ngdoc
+ * @name
+ *
+ */
+angular.module('ngTasty.service.bindTo', [])
+.factory('bindTo', function($parse) {
+ return function (scopeName, scope, attrs, newScopeName) {
+ var lastValue, parentGet, compare, parentSet,
+ parentValueWatch, isolateScopeName;
+ if (!attrs[scopeName]) {
+ return;
+ }
+ parentGet = $parse(attrs[scopeName]);
+ if (parentGet.literal) {
+ compare = equals;
+ } else {
+ compare = function(a,b) { return a === b || (a !== a && b !== b); };
+ }
+ if (newScopeName) {
+ isolateScopeName = newScopeName;
+ } else {
+ isolateScopeName = scopeName;
+ }
+ parentSet = parentGet.assign;
+ lastValue = scope[isolateScopeName] = parentGet(scope.$parent);
+ parentValueWatch = function parentValueWatch(parentValue) {
+ if (!compare(parentValue, scope[isolateScopeName])) {
+ // we are out of sync and need to copy
+ if (!compare(parentValue, lastValue)) {
+ // parent changed and it has precedence
+ scope[isolateScopeName] = parentValue;
+ } else {
+ // if the parent can be assigned then do so
+ parentSet(scope.$parent, parentValue = scope[isolateScopeName]);
+ }
+ }
+ return lastValue = parentValue;
+ };
+ parentValueWatch.$stateful = true;
+ scope.$parent.$watch($parse(attrs[scopeName], parentValueWatch), null, parentGet.literal);
+ };
+});
\ No newline at end of file
diff --git a/src/service/debounce.js b/src/service/debounce.js
new file mode 100644
index 0000000..339a6ea
--- /dev/null
+++ b/src/service/debounce.js
@@ -0,0 +1,19 @@
+/**
+ * @ngdoc
+ * @name
+ *
+ */
+angular.module('ngTasty.service.debounce', [])
+.factory('debounce', function($timeout) {
+ return function(func, wait, immediate) {
+ var timeout;
+ return function() {
+ var context = this, args = arguments;
+ $timeout.cancel(timeout);
+ timeout = $timeout(function() {
+ timeout = null;
+ func.apply(context, args);
+ }, wait);
+ };
+ };
+});
diff --git a/src/service/join-objects.js b/src/service/join-objects.js
new file mode 100644
index 0000000..2b6db5d
--- /dev/null
+++ b/src/service/join-objects.js
@@ -0,0 +1,17 @@
+/**
+ * @ngdoc
+ * @name
+ *
+ */
+angular.module('ngTasty.service.joinObjects', ['ngTasty.service.setProperty'])
+.factory('joinObjects', function(setProperty) {
+ return function(objOne, objTwo, listKeyNotJoin) {
+ listKeyNotJoin = listKeyNotJoin || [];
+ for (var attrname in objTwo) {
+ if (listKeyNotJoin.indexOf(attrname) < 0) {
+ setProperty(objOne, objTwo, attrname);
+ }
+ }
+ return objOne;
+ };
+});
diff --git a/src/service/service.js b/src/service/service.js
deleted file mode 100644
index 90d3ad9..0000000
--- a/src/service/service.js
+++ /dev/null
@@ -1,84 +0,0 @@
-/**
- * @ngdoc
- * @name
- *
- */
-angular.module('ngTasty.service', [
- 'ngTasty.service.tastyUtil',
- 'ngTasty.service.debounce',
- 'ngTasty.service.setProperty',
- 'ngTasty.service.joinObjects',
- 'ngTasty.service.webSocket'
-]);
-
-/**
- * @ngdoc
- * @name
- *
- */
-angular.module('ngTasty.service.tastyUtil', [
- 'ngTasty.service.debounce',
- 'ngTasty.service.setProperty',
- 'ngTasty.service.joinObjects'
-])
-.factory('tastyUtil', function(debounce, setProperty, joinObjects) {
- return {
- 'debounce': debounce,
- 'setProperty': setProperty,
- 'joinObjects': joinObjects
- };
-});
-
-/**
- * @ngdoc
- * @name
- *
- */
-angular.module('ngTasty.service.debounce', [])
-.factory('debounce', function($timeout) {
- return function(func, wait, immediate) {
- var timeout;
- return function() {
- var context = this, args = arguments;
- $timeout.cancel(timeout);
- timeout = $timeout(function() {
- timeout = null;
- func.apply(context, args);
- }, wait);
- };
- };
-});
-
-/**
- * @ngdoc
- * @name
- *
- */
-angular.module('ngTasty.service.setProperty', [])
-.factory('setProperty', function() {
- return function(objOne, objTwo, attrname) {
- if (typeof objTwo[attrname] !== 'undefined' &&
- objTwo[attrname] !== null) {
- objOne[attrname] = objTwo[attrname];
- }
- return objOne;
- };
-});
-
-/**
- * @ngdoc
- * @name
- *
- */
-angular.module('ngTasty.service.joinObjects', [])
-.factory('joinObjects', function(setProperty) {
- return function(objOne, objTwo, listKeyNotJoin) {
- listKeyNotJoin = listKeyNotJoin || [];
- for (var attrname in objTwo) {
- if (listKeyNotJoin.indexOf(attrname) < 0) {
- setProperty(objOne, objTwo, attrname);
- }
- }
- return objOne;
- };
-});
diff --git a/src/service/set-property.js b/src/service/set-property.js
new file mode 100644
index 0000000..69a72ac
--- /dev/null
+++ b/src/service/set-property.js
@@ -0,0 +1,15 @@
+/**
+ * @ngdoc
+ * @name
+ *
+ */
+angular.module('ngTasty.service.setProperty', [])
+.factory('setProperty', function() {
+ return function(objOne, objTwo, attrname) {
+ if (typeof objTwo[attrname] !== 'undefined' &&
+ objTwo[attrname] !== null) {
+ objOne[attrname] = objTwo[attrname];
+ }
+ return objOne;
+ };
+});
diff --git a/src/service/tasty-util.js b/src/service/tasty-util.js
new file mode 100644
index 0000000..a818bd5
--- /dev/null
+++ b/src/service/tasty-util.js
@@ -0,0 +1,19 @@
+/**
+ * @ngdoc
+ * @name
+ *
+ */
+angular.module('ngTasty.service.tastyUtil', [
+ 'ngTasty.service.bindTo',
+ 'ngTasty.service.debounce',
+ 'ngTasty.service.setProperty',
+ 'ngTasty.service.joinObjects'
+])
+.factory('tastyUtil', function(debounce, setProperty, joinObjects, bindTo) {
+ return {
+ 'bindTo': bindTo,
+ 'debounce': debounce,
+ 'setProperty': setProperty,
+ 'joinObjects': joinObjects
+ };
+});