zettapi_client
Version:
Client side CRUD operations in angular to use with zettapi_server rest api to get started quickly in any CMS project
1,862 lines (1,619 loc) • 171 kB
JavaScript
var app = angular.module('zapi', [
'ngSanitize',
'ngAnimate',
'ui.bootstrap',
'blockUI',
'inform',
'chart.js',
'btford.socket-io',
'ngCsv',
'ngTable',
'selector',
'angularUtils.directives.dirPagination',
'angularMoment'
]);
app.constant('zapiPath', 'node_modules/zettapi_client/');
app.controller('entityCtrl', function ($entity, entityMap, ErrorSvc, $page, mySocket, $window, $routeParams, $controller, $location, $scope, $uibModal, blockUI, inform, $route, NgTableParams) {
//set scope variables
$scope.table = { params: null };
$scope.console = null;
$scope.entities = entityMap;
$scope.entityName = $routeParams.entity;
$scope.lookup = {};
$scope.items = [];
$scope.item = {};
$scope.newFile = {};
//set scope functions
$scope.add = function (item) {
blockUI.start("A enviar dados...");
$entity.add($routeParams.entity, item)
.then(function (response) {
if (item._id) {
inform.add($scope.entity.title + ' - registo alterado com sucesso!', { ttl: 2000, type: 'info' });
}
else {
inform.add($scope.entity.title + ' - novo registo criado com sucesso!', { ttl: 2000, type: 'info' });
}
//$location.path($scope.entityName + "/list");
})
.catch(function (response) {
inform.add(response.data, { ttl: 2000, type: 'danger' });
})
.finally(function () {
blockUI.stop();
});
};
$scope.remove = function (item) {
blockUI.start("Averiguando eligibilidade de remoção...");
$entity.remove($routeParams.entity, item)
.then(function (response) {
inform.add($scope.entity.title + ' - registo apagado com sucesso!', { ttl: 2000, type: 'info' });
})
.catch(function (response) {
inform.add(response.error ? response.error : response.data, { ttl: 2000, type: 'danger' });
})
.finally(function () {
blockUI.stop();
});
};
$scope.openEdit = function (item) {
if (!$scope.entity.modal) {
if (item) {
$location.path($routeParams.entity + '/edit/' + item._id);
}
else {
$location.path($routeParams.entity + '/edit');
}
return;
}
setEdit(item);
var modalInstance = $uibModal.open({
animation: true,
templateUrl: 'client/entity/entity.modal.html',
controller: 'entityModalCtrl',
size: 'lg',
backdrop: 'static',
scope: $scope
});
modalInstance.result.then(function (newItem) {
$scope.add(newItem);
}, function () {
inform.add('Alteração ao registo cancelada', { ttl: 2000, type: 'info' });
$scope.item = {};
});
};
$scope.validate = function () {
if (typeof $scope.getError === 'function') {
var response = $scope.getError($scope.item, $scope.items);
if (response.disabled) {
$scope.console = response.tooltip;
return true;
}
}
$scope.console = null;
return false;
};
$scope.unflattenArray = function (items) {
var data = [];
items.forEach(function (item) {
data.push(unflatten(item));
});
return data;
};
$scope.flattenArray = function (items) {
var table = [], keys = [], key, data = [];
items.forEach(function (item) {
var row = flatten(item);
for (key in row) {
if (keys.indexOf(key) !== -1) {
continue;
}
keys.push(key);
}
table.push(row);
});
table.forEach(function (row) {
var datum = {};
for (var i = 0; i < keys.length; i++) {
key = keys[i];
if (typeof row[key] === 'undefined') {
row[key] = "";
}
datum[key] = row[key];
}
data.push(datum);
});
return data;
};
function flatten(data) {
var result = {};
function recurse(cur, prop) {
if (Object(cur) !== cur) {
result[prop] = cur;
}
else if (Array.isArray(cur)) {
for (var i = 0, l = cur.length; i < l; i++)
recurse(cur[i], prop + "[" + i + "]");
if (l === 0)
result[prop] = [];
}
else {
var isEmpty = true;
for (var p in cur) {
isEmpty = false;
recurse(cur[p], prop ? prop + "." + p : p);
}
if (isEmpty && prop)
result[prop] = {};
}
}
recurse(data, "");
return result;
}
function unflatten(data) {
if (Object(data) !== data || Array.isArray(data)) {
return data;
}
var regex = /\.?([^.\[\]]+)|\[(\d+)\]/g, resultholder = {};
for (var p in data) {
var cur = resultholder, prop = "", m;
while ((m = regex.exec(p))) {
cur = cur[prop] || (cur[prop] = (m[2] ? [] : {}));
prop = m[2] || m[1];
}
cur[prop] = data[p];
}
return resultholder[""] || resultholder;
}
//set private methods
function get(entity, id, callback) {
blockUI.start("A obter dados...");
getLookups($scope.lookup, function (err, lookups) {
if (err) {
inform.add("Não foi possível obter dados.", { ttl: 2000, type: 'danger' });
blockUI.stop();
callback(err);
return;
}
$scope.lookup = lookups;
//if user is creating a new record
if (!$routeParams.id && $routeParams.action === 'edit') {
blockUI.stop();
callback();
return;
}
$entity.get(entity, id)
.then(function (response) {
if (id) {
$scope.item = response.data;
}
else {
$scope.items = response.data;
updateTable();
}
})
.catch(function (response) {
inform.add(response.error ? response.error : response.data, { ttl: 2000, type: 'danger' });
})
.finally(function () {
blockUI.stop();
callback();
});
});
}
function getLookups(lookup, callback) {
if (lookup) {
if (lookup.length) {
callback(null, lookup);
return;
}
}
$entity.getLookups($routeParams.entity, function (err, data) {
if (err) {
callback(err);
return;
}
callback(null, data);
});
}
function setEdit(item) {
if (item) {
$scope.item = angular.copy(item);
}
else {
$scope.item = typeof $scope.blank === 'function' ? $scope.blank() : {};
}
}
function updateTable() {
$scope.table.params = new NgTableParams({
page: 1,
count: $scope.items.length,
sorting: { nome: "asc" }
}, {
total: 1,
counts: [],
dataset: $scope.items
});
}
function loadFinished() {
//inject child controller
$controller($routeParams.entity + 'Ctrl', { $scope: $scope });
var websocket;
if (typeof entityMap[$routeParams.entity].websocket === 'undefined') {
websocket = $routeParams.entity;
}
else {
websocket = eval(entityMap[$routeParams.entity].websocket) + "." + $routeParams.entity;
}
mySocket.on(websocket + '.remove', onThisEntityRemove);
mySocket.on(websocket + '.new', onThisEntityAdd);
mySocket.on(websocket + '.edit', onThisEntityEdit);
}
function onThisEntityAdd(data) {
switch ($routeParams.action) {
case 'list':
$scope.items.unshift(data);
updateTable();
break;
}
//todo update lookup
}
function onThisEntityEdit(data) {
switch ($routeParams.action) {
case 'list':
for (var i = 0; i < $scope.items.length; i++) {
if ($scope.items[i]._id === data._id) {
$scope.items[i] = data;
updateTable();
break;
}
}
break;
case 'view':
case 'edit':
if ($scope.item._id === data._id) {
$scope.item = data;
}
break;
}
//todo update lookup
}
function onThisEntityRemove(data) {
switch ($routeParams.action) {
case 'list':
for (var i = 0; i < $scope.items.length; i++) {
if ($scope.items[i]._id === data) {
$scope.items.splice(i, 1);
updateTable();
break;
}
}
break;
case 'view':
case 'edit':
if ($scope.item._id === data._id) {
$location.path('/' + $routeParams.entity + '/list');
}
break;
}
//todo update lookup
}
//initialization
function initialize(callback) {
$page.setTitle('Onde quer ir?');
$scope.entity = entityMap[$routeParams.entity];
if (!$scope.entity) {
return;
}
switch ($routeParams.action) {
case 'edit':
if (!$routeParams.id) {
$page.setTitle($scope.entity.title + ' - Novo Registo');
get($routeParams.entity, null, callback);
}
else {
$page.setTitle($scope.entity.title + ' - Alterar Registo');
get($routeParams.entity, $routeParams.id, callback);
}
break;
case 'view':
if (!$routeParams.id) {
return;
}
$page.setTitle($scope.entity.title + ' - Visualizar');
get($routeParams.entity, $routeParams.id, callback);
break;
case 'list':
if ($routeParams.id) {
return;
}
$page.setTitle($scope.entity.title);
get($routeParams.entity, null, callback);
break;
default:
return;
}
}
initialize(loadFinished);
});
app.controller('entityModalCtrl', function($uibModalInstance, $scope) {
$scope.ok = function() {
$scope.$close($scope.item);
};
$scope.cancel = function() {
$scope.$dismiss('cancel');
};
});
app.controller('activityCtrl', function($scope) {
});
app.controller('alertCtrl', function() {
});
app.controller('countryCtrl', function($scope) {
});
app.controller('errorCtrl', function($scope) {
});
app.controller('holidayCtrl', function(ErrorSvc, $scope, $holiday) {
$scope.getError = function(holiday, holidays) {
$scope.nextOccurrence = null;
if (!holiday) {
return ErrorSvc.validationArgs(true);
}
if (!holiday.name) {
return ErrorSvc.validationArgs(true, "Introduza um nome do feriado");
}
if (holiday.name.trim() === "") {
return ErrorSvc.validationArgs(true, "Introduza um nome do feriado");
}
for (var i = 0; i < holidays.length; i++) {
if (holidays[i].name == holiday.name && holiday._id != holidays[i]._id) {
return ErrorSvc.validationArgs(true, "Feriado já existente");
}
}
if (!holiday.date && !holiday.formula) {
return ErrorSvc.validationArgs(true, "Impossivel calcular próxima ocorrência para este feriado");
}
if (holiday.date) {
var pattern = /^(?:(?:[12][0-9]|0[1-9])\/(02)|(?:30|[12][0-9]|0[1-9])\/(?:(?:0[469]|11))|(?:3[01]|[12][0-9]|0[1-9])\/(?:(?:0[13578]|1[02])))$/;
if (!pattern.test(holiday.date)) {
return ErrorSvc.validationArgs(true, "A data deve ser no formato dd/mm");
}
}
else if (holiday.formula) {
try {
var currentYear = new Date().getFullYear();
eval('my_function = ' + holiday.formula);
var d = my_function(currentYear);
if (Object.prototype.toString.call(d) === "[object Date]" ) {
//it is a date
if (isNaN(d.getTime())) {
return ErrorSvc.validationArgs(true, "Data inválida");
}
}
else {
return ErrorSvc.validationArgs(true, "Data inválida");
}
}
catch(err) {
return ErrorSvc.validationArgs(true, "A formula que introduziu contém um erro: " + err);
}
}
return ErrorSvc.validationArgs(false);
};
$scope.calculateNextOccurrence = $holiday.calculateNextOccurrence;
});
app.controller('messageCtrl', function($scope) {
});
app.controller('roleCtrl', function (ErrorSvc, $scope, $role) {
$scope.permissions = {};
$role.getPermissions().then(function (response) {
$scope.permissions = response.data;
});
$scope.togglePermissions = function (value) {
if (!$scope.item.permissions) {
$scope.item.permissions = {};
}
for (var entity in $scope.permissions) {
if (!$scope.item.permissions[entity]) {
$scope.item.permissions[entity] = {};
}
for (var action in $scope.permissions[entity]) {
if (action === 'label') {
continue;
}
$scope.item.permissions[entity][action] = value;
}
}
};
$scope.getError = function (role, roles) {
if (!role) {
return ErrorSvc.validationArgs(true);
}
if (!role.name) {
return ErrorSvc.validationArgs(true, "Introduza um nome para o perfil de utilizador");
}
if (role.name.trim() === "") {
return ErrorSvc.validationArgs(true, "Introduza um nome para o perfil de utilizador");
}
for (var i = 0; i < roles.length; i++) {
if (roles[i].name == role.name && roles[i]._id != role._id) {
return ErrorSvc.validationArgs(true, "Perfil de utilizador já existente");
}
}
return ErrorSvc.validationArgs(false);
};
$scope.blank = function () {
var permissions = {};
for (var entity in $scope.permissions) {
permissions[entity] = {};
for (var action in $scope.permissions[entity]) {
if (action === 'label') {
continue;
}
permissions[entity][action] = true;
}
}
return {
name: "",
approvalLevel: 0,
admin: false,
permissions: permissions
};
};
});
//manage tasks Controller
app.controller('taskCtrl', function ($scope, $date, ErrorSvc, $task, mySocket, blockUI) {
$scope.taskTypes = [];
$scope.getError = function (task, tasks) {
if (!task) {
return ErrorSvc.validationArgs(true);
}
if (!task.name) {
return ErrorSvc.validationArgs(true, "Indique um nome para a tarefa");
}
if (task.name.trim() === "") {
return ErrorSvc.validationArgs(true, "Indique um nome para a tarefa");
}
if (!task.type) {
return ErrorSvc.validationArgs(true, "Escolha um tipo de tarefa");
}
if (!task.criteria) {
return ErrorSvc.validationArgs(true, "Complete o critério de periodicidade");
}
if (!task.criteria.month) {
return ErrorSvc.validationArgs(true, "Complete o critério de periodicidade");
}
if (!task.criteria.monthday) {
return ErrorSvc.validationArgs(true, "Complete o critério de periodicidade");
}
if (!task.criteria.weekday) {
return ErrorSvc.validationArgs(true, "Complete o critério de periodicidade");
}
if (!task.criteria.hour) {
return ErrorSvc.validationArgs(true, "Complete o critério de periodicidade");
}
if (!task.criteria.minute) {
return ErrorSvc.validationArgs(true, "Complete o critério de periodicidade");
}
return ErrorSvc.validationArgs(false);
};
$scope.formatDatetime = $date.formatDatetime;
$scope.calculateTimespan = $date.calculateTimespan;
$scope.toggleState = $task.toggleState;
$scope.run = function (task) {
$task.run(task)
.then(function (response) {
blockUI.start('A executar...');
})
.catch(function (response) {
swal('Ocorreu um erro ao iniciar a tarefa manualmente.', response.data, 'error');
});
};
mySocket.on($scope.login._id, function(data) {
swal('Tarefa executada com sucesso.', null, 'success');
blockUI.stop();
});
$task.getTypes(function (types) {
$scope.taskTypes = types;
});
});
app.filter("dateFilter", function(moment) {
return function(items, from, to) {
if (!from || !to) {
return items;
}
from = moment(from);
to = moment(to);
var result = [];
for (var i = 0; i < items.length; i++) {
var date = moment(items[i].data_pagamento);
if (date >= from && date <= to) {
result.push(items[i]);
}
}
return result;
};
});
app.filter('orderObjectBy', function () {
return function (items, field, reverse) {
var filtered = [];
angular.forEach(items, function (item) {
filtered.push(item);
});
filtered.sort(function (a, b) {
return (a[field] > b[field] ? 1 : -1);
});
if (reverse) filtered.reverse();
return filtered;
};
});
app.directive('zlActivation', function (zapiPath) {
return {
restrict: 'E',
scope: true,
replace: false,
templateUrl: zapiPath + '/directives/activation/activation.html',
controller: function ($scope, $auth, $location, $routeParams) {
$scope.user = null;
$auth.activateAccount($routeParams.code, function (err, user) {
if (err) {
return $location.path('/');
}
$scope.user = user;
});
$scope.changePassword = function (newPassword1, newPassword2) {
$auth.changePassword($scope.user, newPassword1, newPassword2, function (err) {
if (!err) {
return $location.path('/');
}
});
};
}
};
});
app.directive('zlAddress', function (zapiPath) {
return {
restrict: 'E',
scope: {
item: '=',
countries: '='
},
replace: false,
templateUrl: zapiPath + '/directives/address/address.html',
controller: function ($scope, $address, inform) {
$scope.getAddressPT = function(zipcode) {
$address.getAddressPT(zipcode, function (err, data) {
if (err) {
$scope.item.address = "";
$scope.item.city = "";
$scope.item.country = "";
$scope.item.district = "";
$scope.item.county = "";
$scope.item.locality = "";
return inform.add("Não foi possível completar o arruamento", { ttl: 2000, type: "warning" });
}
$scope.item.address = data.address;
$scope.item.city = data.city;
$scope.item.country = data.country;
$scope.item.district = data.district;
$scope.item.county = data.county;
$scope.item.locality = data.locality;
$address.getCoordinates(zipcode, function (err, coords) {
if (!err) {
$scope.item.coords = coords;
}
});
});
};
$scope.validateZipcode = $address.validateZipcode;
}
};
});
app.directive('console', function(zapiPath) {
return {
restrict: 'E',
scope: true,
replace: false,
templateUrl: zapiPath + '/directives/console/console.html'
};
});
app.directive('zlContainer', function(zapiPath) {
return {
restrict: 'E',
replace: false,
templateUrl: zapiPath + '/directives/container/container.html',
scope: {
entity: '@',
item: '=',
lookup: '=',
label: '@',
var: '@',
key: '@',
isVisible: '&?',
isMovable: '@',
noInsert: '@',
removable: '@',
approvalMaxLevel: '@'
},
controller: function($scope, $rootScope, $controller, $timeout) {
if ($scope.key) {
$scope.newContainerItem = {};
}
else {
$scope.newContainerItem = "";
}
$scope.append = function(newContainerItem) {
append(newContainerItem, function(err) {
if (err) {
return swal("Atenção", err, "warning");
}
//clear new container item fields
if ($scope.key) {
$scope.newContainerItem = {};
}
else {
$scope.newContainerItem = "";
}
});
};
$scope.remove = function(containerItem) {
var index = $scope.item[$scope.var].indexOf(containerItem);
if (index != -1) {
$scope.item[$scope.var].splice(index, 1);
}
};
$scope.validate = function(newContainerItem) {
if (!newContainerItem) {
return true;
}
if ($scope.key) {
if (Object.keys(newContainerItem).length === 0 && JSON.stringify(newContainerItem) === JSON.stringify({})) {
return true;
}
}
if (typeof $scope.getError === 'function') {
var response = $scope.getError(newContainerItem, $scope.item[$scope.var]);
if (response.disabled) {
$scope.console = response.tooltip;
return true;
}
}
$scope.console = null;
return false;
};
$scope.pushBack = function(index) {
var _containerItem = $scope.item[$scope.var].splice(index, 1);
$scope.item[$scope.var].splice(index - 1, 0, _containerItem[0]);
};
$scope.pushForward = function(index) {
var _containerItem = $scope.item[$scope.var].splice(index, 1);
$scope.item[$scope.var].splice(index + 1, 0, _containerItem[0]);
};
//$timeout is only ready after directive has fully loaded
//this way when the controller loads, $scope will be fully populated
$timeout(function() {
//inject child controller
$controller($scope.var + 'Ctrl', {$scope: $scope});
});
function append(newContainerItem, callback) {
var i, property;
if (!$scope.item[$scope.var]) {
$scope.item[$scope.var] = [];
}
//check if containerItem is not empty
if ($scope.key) {
var key = {
property: null,
key: null
};
if (newContainerItem[$scope.key]) {
key.key = newContainerItem[$scope.key];
}
else {
for (property in newContainerItem) {
if (newContainerItem[property][$scope.key]) {
key.property = property;
key.key = newContainerItem[property][$scope.key];
break;
}
}
}
if (key.key) {
//check if containerItem already exists in container
for (i = 0; i < $scope.item[$scope.var].length; i++) {
var obj = key.property ? $scope.item[$scope.var][i][key.property] : $scope.item[$scope.var][i];
if (obj[$scope.key] === key.key) {
return callback('Registo duplicado em ' + $scope.label);
}
}
}
}
else {
for (i = 0; i < $scope.item[$scope.var].length; i++) {
if ($scope.item[$scope.var][i] === newContainerItem) {
return callback('Registo duplicado em ' + $scope.label);
}
}
}
//append item to container
$scope.item[$scope.var].unshift(newContainerItem);
//approvable containers only
if ($scope.approvalMaxLevel) {
//assign approval
if ($scope.approvalMaxLevel) {
newContainerItem.approval = {
level: $rootScope.login.role.approvalLevel,
maxLevel: $scope.approvalMaxLevel
};
}
}
callback();
}
}
};
});
app.directive("contenteditable", function() {
return {
require: "ngModel",
link: function(scope, element, attrs, ngModel) {
function read() {
ngModel.$setViewValue(element.html());
}
ngModel.$render = function() {
element.html(ngModel.$viewValue || "");
};
element.bind("blur keyup change", function() {
scope.$apply(read);
});
}
};
});
app.directive('zlCountry', function(zapiPath) {
return {
restrict: 'E',
scope: {
item: '=',
'var': '@',
label: '@',
lookup: '='
},
replace: false,
templateUrl: zapiPath + '/directives/country/country.html'
};
});
app.directive('zlFile', function(zapiPath) {
return {
restrict: 'A',
scope: {
item: '=',
var: '=',
docid: '='
},
replace: false,
link: function (scope, element, attrs) {
if (scope.docid) {
if (scope.docid._id) {
scope.docid = scope.docid._id;
}
}
},
templateUrl: zapiPath + '/directives/document/file.html'
};
});
app.directive('fileModel', function ($parse) {
return {
restrict: 'A',
replace: false,
link: function(scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function() {
scope.$apply(function() {
modelSetter(scope, element[0].files[0]);
});
});
}
};
});
app.directive('zlDynamicField', function (zapiPath) {
return {
restrict: 'E',
scope: {
field: '=',
value: '='
},
replace: false,
template: '<div ng-include src="contentUrl" include-replace></div>',
link: function (scope, element, attrs) {
var type = scope.field.type || 'Unknown';
if (scope.field.type === 'ObjectId' && typeof scope.field.ref === 'undefined') {
type = "String";
}
scope.contentUrl = zapiPath + "/directives/dynamicField/" + type + ".html";
}
};
});
app.directive('entityEdit', function(zapiPath) {
return {
restrict: 'E',
scope: true,
replace: false,
link: function(scope, element, attrs) {
var scaffoldUrl = scope.entity.custom ? 'client/entity/' : (zapiPath + '/entity/');
scope.contentUrl = scaffoldUrl + scope.entityName + '/' + scope.entityName + '.edit.html';
},
template: '<div ng-include="contentUrl"></div>'
};
});
app.directive('entityList', function(zapiPath) {
return {
restrict: 'E',
scope: true,
replace: false,
link: function(scope, element, attrs) {
var scaffoldUrl = scope.entity.custom ? 'client/entity/' : (zapiPath + '/entity/');
scope.contentUrl = scaffoldUrl + scope.entityName + '/' + scope.entityName + '.list.html';
},
template: '<div ng-include src="contentUrl" include-replace></div>'
};
});
app.directive('entityView', function(zapiPath) {
return {
restrict: 'E',
scope: true,
replace: false,
link: function(scope, element, attrs) {
var scaffoldUrl = scope.entity.custom ? 'client/entity/' : (zapiPath + 'entity/');
scope.contentUrl = scaffoldUrl + scope.entityName + '/' + scope.entityName + '.view.html';
},
template: '<div ng-include="contentUrl"></div>'
};
});
app.directive('zlGraph', function (zapiPath) {
return {
restrict: 'E',
scope: {
item: '='
},
replace: false,
templateUrl: zapiPath + '/directives/graph/graph.html',
controller: function ($scope, blockUI, $graph) {
var chartTypes = [
['bar', 'doughnut', 'pie', 'horizontalBar'],
['line', 'radar'],
['bubble', 'polar-area']
];
$scope.getNext = function () {
var index = chartTypes[$scope.item.dimension].indexOf($scope.item.type) + 1;
return chartTypes[$scope.item.dimension][index === chartTypes[$scope.item.dimension].length ? 0 : index];
};
$scope.toggle = function () {
$scope.item.type = $scope.getNext();
};
$scope.chart = {};
var graphBlockUI = blockUI.instances.get('graphBlockUI');
graphBlockUI.start("A criar gráfico...");
$graph.get($scope.item.namespace, $scope.item.collection, $scope.item.query, $scope.item.obj, $scope.item.seriesKey, $scope.item.dataKey, $scope.item.labelsKey, function (chart) {
$scope.chart = chart;
console.log(chart);
graphBlockUI.stop();
});
}
};
});
app.directive('zlLogin', function (zapiPath) {
return {
restrict: 'E',
scope: true,
replace: false,
templateUrl: zapiPath + '/directives/login/login.html',
controller: function (blockUI, $auth, $location, $scope, $uibModalStack) {
$scope.login = function () {
if ($scope.validateLogin()) {
return;
}
$auth.login($scope.username, $scope.password, function (err) {
if (err) {
reset();
}
else {
$location.path('/profile');
$uibModalStack.dismissAll();
}
});
};
$scope.validateLogin = function () {
if (!$scope.username) {
return true;
}
if (!$scope.password) {
return true;
}
return false;
};
function reset() {
$scope.username = "";
$scope.password = "";
}
}
};
});
app.directive('zlNewsletter', function (zapiPath, blockUI, inform, $entity) {
return {
restrict: 'E',
scope: true,
replace: false,
templateUrl: zapiPath + '/directives/newsletter/newsletter.html',
controller: function ($scope, $newsletter) {
$scope.saveEmail = function (item) {
$newsletter.subscribe(item, function () {
$scope.newsItem = {};
});
};
}
};
});
app.directive('asDate', function () {
return {
require: '^ngModel',
restrict: 'A',
link: function (scope, element, attrs, ctrl) {
ctrl.$formatters.splice(0, ctrl.$formatters.length);
ctrl.$parsers.splice(0, ctrl.$parsers.length);
ctrl.$formatters.push(function (modelValue) {
if (!modelValue) {
return;
}
return new Date(modelValue).toISOString().slice(0,10);
});
ctrl.$parsers.push(function (modelValue) {
return modelValue;
});
}
};
});
app.directive('dlKeyCode', dlKeyCode);
function dlKeyCode() {
return {
restrict: 'A',
link: function($scope, $element, $attrs) {
$element.bind("keypress", function(event) {
var keyCode = event.which || event.keyCode;
if (keyCode == $attrs.code) {
$scope.$apply(function() {
$scope.$eval($attrs.dlKeyCode, {$event: event});
});
}
});
}
};
}
app.directive('asFloat', function () {
return {
require: '^ngModel',
restrict: 'A',
link: function (scope, element, attrs, ctrl) {
ctrl.$formatters.splice(0, ctrl.$formatters.length);
ctrl.$parsers.splice(0, ctrl.$parsers.length);
ctrl.$formatters.push(function (modelValue) {
if (!modelValue) {
return;
}
return parseFloat(modelValue);
});
ctrl.$parsers.push(function (modelValue) {
return modelValue;
});
}
};
});
app.directive('asInteger', function () {
return {
require: '^ngModel',
restrict: 'A',
link: function (scope, element, attrs, ctrl) {
ctrl.$formatters.splice(0, ctrl.$formatters.length);
ctrl.$parsers.splice(0, ctrl.$parsers.length);
ctrl.$formatters.push(function (modelValue) {
if (!modelValue) {
return;
}
return parseInt(modelValue);
});
ctrl.$parsers.push(function (modelValue) {
return modelValue;
});
}
};
});
app.directive('lettersOnly', function () {
return {
require: 'ngModel',
link: function (scope, element, attr, ngModelCtrl) {
function onlyLetters(text) {
if (text) {
var transformedInput = text.replace(/[^A-Za-z]/g, '');
if (transformedInput !== text) {
ngModelCtrl.$setViewValue(transformedInput);
ngModelCtrl.$render();
}
return transformedInput;
}
return undefined;
}
if (attr.lettersOnly === 'true') {
ngModelCtrl.$parsers.push(onlyLetters);
}
}
};
});
app.directive('numbersOnly', function () {
return {
require: 'ngModel',
link: function (scope, element, attr, ngModelCtrl) {
function onlyNumbers(text) {
if (text) {
var transformedInput = text.replace(/[^0-9]/g, '');
if (transformedInput !== text) {
ngModelCtrl.$setViewValue(transformedInput);
ngModelCtrl.$render();
}
return transformedInput;
}
return undefined;
}
if (attr.numbersOnly === 'true') {
ngModelCtrl.$parsers.push(onlyNumbers);
}
}
};
});
app.directive('zlQueryBuilder', function (zapiPath) {
return {
restrict: 'E',
scope: false,
replace: false,
template: zapiPath + '/directives/queryBuilder/queryBuilder.html',
controller: function($scope) {
$scope.choices = [
{
key: '$exists',
caption: 'existe',
template: 'Boolean.html'
},
{
key: ''
}
];
}
};
});
/*
<option ng-value="'exists'">existe</option>
<option ng-value="'startsWith'">começa com</option>
<option ng-value="'endsWith'">acaba em</option>
<option ng-value="'contains'">contém</option>
<option ng-value="'equals'">igual a</option>
<option ng-value="'notEquals'">diferente de</option>
<option ng-value="'in'">algum de</option>
<option ng-value="'nin'">nenhum de</option>
<option ng-value="'gt'">gt</option>
<option ng-value="'gte'">gte</option>
<option ng-value="'lt'">lt</option>
<option ng-value="'lte'">lte</option>
<option ng-value="'inclusiveBetween'">entre (inclusive)</option>
<option ng-value="'exclusiveBetween'">entre (exclusive)</option>
*/
app.directive('zlReset', function (zapiPath) {
return {
restrict: 'E',
scope: true,
replace: false,
templateUrl: zapiPath + '/directives/reset/reset.html',
controller: function ($scope, $auth, $location, blockUI) {
$scope.reset = function (item) {
if ($scope.validateReset(item)) {
return;
}
$auth.resetPassword(item.email, item.username, function (err) {
if (!err) {
return $location.path("/");
}
});
};
$scope.validateReset = function (item) {
if (!item) {
return true;
}
if (!item.email) {
return true;
}
if (!item.username) {
return true;
}
return false;
};
}
};
});
app.directive('zlSignup', function (zapiPath) {
return {
restrict: 'E',
scope: true,
replace: false,
templateUrl: zapiPath + '/directives/signup/signup.html',
controller: function ($scope, blockUI, $entity, $uibModalStack) {
$scope.signup = function (item) {
if ($scope.validateSignup(item)) {
return;
}
blockUI.start("A reservar conta...");
$entity.add('user', item)
.then(function (response) {
swal("Está quase!", "Consulte o seu email para ativar a conta de utilizador", "success");
$uibModalStack.dismissAll();
})
.catch(function (response) {
swal("Erro", response.data, "error");
})
.finally(function () {
blockUI.stop();
});
};
$scope.validateSignup = function (item) {
if (!item) {
return true;
}
if (!item.username) {
return true;
}
var validUsername = new RegExp('^[a-zA-Z0-9_]*$');
if (!validUsername.test(item.username)) {
return true;
}
if (!item.email) {
return true;
}
return false;
};
}
};
});
app.service('$address', function($http, blockUI) {
this.getAddressPT = function(zipcode, callback) {
if (this.validateZipcode(zipcode)) {
return callback("Código postal inválido");
}
blockUI.start('A procurar arruamento...');
$http.get("./api/address/pt/" + zipcode)
.then(function(response) {
callback(null, response.data);
})
.catch(function(response) {
callback(response);
})
.finally(function() {
blockUI.stop();
});
};
this.validateZipcode = function(zipcode) {
if (!zipcode) {
return true;
}
if (typeof zipcode !== 'string') {
zipcode = zipcode + '';
}
var cps = zipcode.split('-');
if (cps.length < 1 || cps.length > 2) {
return true;
}
if (isNaN(cps[0])) {
return true;
}
if (cps.length > 1) {
if (isNaN(cps[1])) {
return true;
}
}
return false;
};
this.getCoordinates = function(zipcode, callback) {
try {
var geocoder = new google.maps.Geocoder();
geocoder.geocode({ 'address': zipcode }, function (results, status) {
if (status !== google.maps.GeocoderStatus.OK) {
callback(status);
}
callback(null, {
lat: results[0].geometry.location.lat(),
lng: results[0].geometry.location.lng()
});
});
}
catch (error) {
callback(error);
}
};
});
app.factory('$auth', function ($http, $crypto, blockUI) {
var service = {
currentUser: null,
login: function (username, password, callback) {
blockUI.start("A autenticar credenciais...");
return $http.post('/api/session/login/', { username: username, password: $crypto.md5(password) })
.then(function (response) {
service.currentUser = response.data.user;
callback();
})
.catch(function (response) {
swal("Login Falhou", response.data, "error");
callback(true);
})
.finally(function () {
blockUI.stop();
});
},
logout: function (callback) {
$http.get('/api/session/logout')
.then(function (response) {
service.currentUser = null;
callback(null, response);
})
.catch(function (response) {
callback(response);
});
},
requestCurrentUser: function (callback) {
if (service.isAuthenticated()) {
callback(null, service.currentUser);
}
else {
$http.get('/api/session/currentuser')
.then(function (response) {
service.currentUser = response.data.user;
callback(null, service.currentUser);
})
.catch(function (response) {
service.currentUser = null;
callback(response);
});
}
},
isAuthenticated: function () {
return !!service.currentUser;
},
activateAccount: function (rawCode, callback) {
if (!rawCode) {
return callback(true);
}
var codes = rawCode.split('&');
if (codes.length !== 2) {
return callback(true);
}
blockUI.start("A verificar código de ativação...");
$http.post('/api/user/activate/', { email: codes[0], code: codes[1] })
.then(function (response) {
callback(null, response.data);
})
.catch(function (response) {
swal({
title: "Conta já ativa!",
text: "Consulte o email que recebeu com instruções de acesso",
type: "info",
confirmButtonText: "OK"
});
callback(true);
})
.finally(function () {
blockUI.stop();
});
},
resetPassword: function (email, username, callback) {
blockUI.start("A comunicar pedido de recuperação...");
return $http.post('/api/user/resetpassword/', { email: email, username: username })
.then(function (response) {
swal("Recuperação de Conta", "A sua conta foi recuperada! Consulte a sua conta de email.", "success");
callback();
})
.catch(function (response) {
swal("Erro", response.data, "error");
callback(true);
})
.finally(function () {
blockUI.stop();
});
},
changePassword: function (user, newPassword1, newPassword2, callback) {
if (!newPassword1) {
return;
}
if (newPassword1 !== newPassword2) {
return swal("Atenção!", "As palavras chave têm de ser iguais", "error");
}
user.newPassword = $crypto.md5(newPassword1);
blockUI.start("A atualizar dados...");
$http.post('/api/user/changepassword/', user)
.then(function (response) {
swal({
title: "Parabéns!",
text: "Palavra chave alterada com sucesso!",
type: "success",
confirmButtonText: "Entrar"
});
callback();
})
.catch(function (response) {
swal("Erro", "Ocorreu um erro ao criar a sua password, por favor tente novamente mais tarde", "error");
callback(true);
})
.finally(function () {
blockUI.stop();
});
}
};
return service;
});
app.service('$crypto', function() {
function md5cycle(x, k) {
var a = x[0], b = x[1], c = x[2], d = x[3];
a = ff(a, b, c, d, k[0], 7, -680876936);
d = ff(d, a, b, c, k[1], 12, -389564586);
c = ff(c, d, a, b, k[2], 17, 606105819);
b = ff(b, c, d, a, k[3], 22, -1044525330);
a = ff(a, b, c, d, k[4], 7, -176418897);
d = ff(d, a, b, c, k[5], 12, 1200080426);
c = ff(c, d, a, b, k[6], 17, -1473231341);
b = ff(b, c, d, a, k[7], 22, -45705983);
a = ff(a, b, c, d, k[8], 7, 1770035416);
d = ff(d, a, b, c, k[9], 12, -1958414417);
c = ff(c, d, a, b, k[10], 17, -42063);
b = ff(b, c, d, a, k[11], 22, -1990404162);
a = ff(a, b, c, d, k[12], 7, 1804603682);
d = ff(d, a, b, c, k[13], 12, -40341101);
c = ff(c, d, a, b, k[14], 17, -1502002290);
b = ff(b, c, d, a, k[15], 22, 1236535329);
a = gg(a, b, c, d, k[1], 5, -165796510);
d = gg(d, a, b, c, k[6], 9, -1069501632);
c = gg(c, d, a, b, k[11], 14, 643717713);
b = gg(b, c, d, a, k[0], 20, -373897302);
a = gg(a, b, c, d, k[5], 5, -701558691);
d = gg(d, a, b, c, k[10], 9, 38016083);
c = gg(c, d, a, b, k[15], 14, -660478335);
b = gg(b, c, d, a, k[4], 20, -405537848);
a = gg(a, b, c, d, k[9], 5, 568446438);
d = gg(d, a, b, c, k[14], 9, -1019803690);
c = gg(c, d, a, b, k[3], 14, -187363961);
b = gg(b, c, d, a, k[8], 20, 1163531501);
a = gg(a, b, c, d, k[13], 5, -1444681467);
d = gg(d, a, b, c, k[2], 9, -51403784);
c = gg(c, d, a, b, k[7], 14, 1735328473);
b = gg(b, c, d, a, k[12], 20, -1926607734);
a = hh(a, b, c, d, k[5], 4, -378558);
d = hh(d, a, b, c, k[8], 11, -2022574463);
c = hh(c, d, a, b, k[11], 16, 1839030562);
b = hh(b, c, d, a, k[14], 23, -35309556);
a = hh(a, b, c, d, k[1], 4, -1530992060);
d = hh(d, a, b, c, k[4], 11, 1272893353);
c = hh(c, d, a, b, k[7], 16, -155497632);
b = hh(b, c, d, a, k[10], 23, -1094730640);
a = hh(a, b, c, d, k[13], 4, 681279174);
d = hh(d, a, b, c, k[0], 11, -358537222);
c = hh(c, d, a, b, k[3], 16, -722521979);
b = hh(b, c, d, a, k[6], 23, 76029189);
a = hh(a, b, c, d, k[9], 4, -640364487);
d = hh(d, a, b, c, k[12], 11, -421815835);
c = hh(c, d, a, b, k[15], 16, 530742520);
b = hh(b, c, d, a, k[2], 23, -995338651);
a = ii(a, b, c, d, k[0], 6, -198630844);
d = ii(d, a, b, c, k[7], 10, 1126891415);
c = ii(c, d, a, b, k[14], 15, -1416354905);
b = ii(b, c, d, a, k[5], 21, -57434055);
a = ii(a, b, c, d, k[12], 6, 1700485571);
d = ii(d, a, b, c, k[3], 10, -1894986606);
c = ii(c, d, a, b, k[10], 15, -1051523);
b = ii(b, c, d, a, k[1], 21, -2054922799);
a = ii(a, b, c, d, k[8], 6, 1873313359);
d = ii(d, a, b, c, k[15], 10, -30611744);
c = ii(c, d, a, b, k[6], 15, -1560198380);
b = ii(b, c, d, a, k[13], 21, 1309151649);
a = ii(a, b, c, d, k[4], 6, -145523070);
d = ii(d, a, b, c, k[11], 10, -1120210379);
c = ii(c, d, a, b, k[2], 15, 718787259);
b = ii(b, c, d, a, k[9], 21, -343485551);
x[0] = add32(a, x[0]);
x[1] = add32(b, x[1]);
x[2] = add32(c, x[2]);
x[3] = add32(d, x[3]);
}
function cmn(q, a, b, x, s, t) {
a = add32(add32(a, q), add32(x, t));
return add32((a << s) | (a >>> (32 - s)), b);
}
function ff(a, b, c, d, x, s, t) {
return cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function gg(a, b, c, d, x, s, t) {
return cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function hh(a, b, c, d, x, s, t) {
return cmn(b ^ c ^ d, a, b, x, s, t);
}
function ii(a, b, c, d, x, s, t) {
return cmn(c ^ (b | (~d)), a, b, x, s, t);
}
function md51(s) {
txt = '';
var n = s.length,
state = [1732584193, -271733879, -1732584194, 271733878], i;
for (i=64; i<=s.length; i+=64) {
md5cycle(state, md5blk(s.substring(i-64, i)));
}
s = s.substring(i-64);
var tail = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0];
for (i=0; i<s.length; i++)
tail[i>>2] |= s.charCodeAt(i) << ((i%4) << 3);
tail[i>>2] |= 0x80 << ((i%4) << 3);
if (i > 55) {
md5cycle(state, tail);
for (i=0; i<16; i++) tail[i] = 0;
}
tail[14] = n*8;
md5cycle(state, tail);
return state;
}
/* there needs to be support for Unicode here,
* unless we pretend that we can redefine the MD-5
* algorithm for multi-byte characters (perhaps
* by adding every four 16-bit characters and
* shortening the sum to 32 bits). Otherwise
* I suggest performing MD-5 as if every character
* was two bytes--e.g., 0040 0025 = @%--but then
* how will an ordinary MD-5 sum be matched?
* There is no way to standardize text to something
* like UTF-8 before transformation; speed cost is
* utterly prohibitive. The JavaScript standard
* itself needs to look at this: it should start
* providing access to strings as preformed UTF-8
* 8-bit unsigned value arrays.
*/
function md5blk(s) { /* I figured global was faster. */
var md5blks = [], i; /* Andy King said do it this way. */
for (i=0; i<64; i+=4) {
md5blks[i>>2] = s.charCodeAt(i) + (s.charCodeAt(i+1) << 8) + (s.charCodeAt(i+2) << 16) + (s.charCodeAt(i+3) << 24);
}
return md5blks;
}
var hex_chr = '0123456789abcdef'.split('');
function rhex(n) {
var s='', j=0;
for(; j<4; j++)
s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];
return s;
}
function hex(x) {
for (var i=0; i<x.length; i++)
x[i] = rhex(x[i]);
return x.join('');
}
this.md5 = function(s) {
return hex(md51(s));
};
/* this function is much faster,
so if possible we use it. Some IEs
are the only ones I know of that
need the idiotic second function,
generated by an if clause. */
var add32 = function(a, b) {
return (a + b) & 0xFFFFFFFF;
};
if (this.md5('hello') != '5d41402abc4b2a76b9719d911017c592') {
add32 = function(x, y) {
var lsw = (x & 0xFFFF) + (y & 0xFFFF),
msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
};
}
});
app.factory('$date', function(moment) {
var factory = {};
factory.collisionDetection = function(start, end, events) {
start = factory.extract(start);
end = end ? factory.extract(end) : start;
if (end < start) {
return null;
}
for (var i = 0; i < events.length; i++) {
/*
if (events[i].approval) {
if (events[i].approval.cancelled) {
continue;
}
if (events[i].approval.level < events[i].approval.maxLevel) {
continue;
}
}
*/
var eventStart = factory.extract(events[i].dateStart);
var eventEnd = eventStart;
if (events[i].dateEnd) {
eventEnd = factory.extract(events[i].dateEnd);
}
// $ |__$__|
if (eventEnd > start && eventEnd <= end) {
return true;
}
// |__$__| $
if (eventStart >= start && eventStart < end) {
return true;
}
// $ |____| $
if (eventStart <= start && eventEnd >= end) {
return true;
}
// |___$___$____|
if (eventStart >= start && eventEnd <= end) {
return true;
}
}
return false;
};
factory.extract = function(datetime) {
try {
return moment(datetime).format('YYYY-MM-DD');
}
catch (err) {
return factory.extract(new Date(datetime));
}
};
factory.extractMinutes = function(time) {
try {
return factory.pad(time.getHours()) + ':' + factory.pad(time.getMinutes());
}
catch (err) {
time = new Date(time);
return factory.pad(time.getHours()) + ':' + factory.pad(time.getMinutes());
}
};
factory.formatDatetime = function(datetime) {
if (!datetime) {
return '-';
}
var d = new Date(datetime);
return d.getFullYear() + '-' + factory.pad(d.getMonth() + 1) + '-' + factory.pad(d.getDate()) + ' às ' +
factory.pad(d.getHours()) + ':' + factory.pad(d.getMinutes()) + ':' + factory.pad(d.getSeconds());
};
factory.calculateTimespan = function(startTimestamp, endTimestamp) {
var end;
if (endTimestamp) {
end = moment(endTimestamp);
}
else {
end = moment();
}
var start = new Date(startTimestamp);
var duration = moment.duration(end.diff(start));
var mins = duration.asMinutes().toFixed(2);
return mins;
};
factory.totalMinutes = function(timeString) {
var HourMinutes = timeString.split(':');
if (HourMinutes.length == 1) {
HourMinutes.push(0);
}
var hours = parseInt(HourMinutes[0]);
var minutes = parseInt(HourMinutes[1]);
return hours * 60 + minutes;
};
factory.pad = function(number, count) {
var res = number + "";
count = count || 2;
while (res.length < count) {
res = "0" + res;
}
return res;
};
factory.getTimestamp = function() {
var d = new Date();
var timestamp = factory.pad(d.getHours()) + ":" + factory.pad(d.getMinutes());
return timestamp;
};
factory.getWeekDay = function(wee