Preparation to bower release
diff --git a/src/services/helpers/ui/comparator.service.js b/src/services/helpers/ui/comparator.service.js
new file mode 100644
index 0000000..24bf9f9
--- /dev/null
+++ b/src/services/helpers/ui/comparator.service.js
@@ -0,0 +1,96 @@
+(function() {
+ 'use strict';
+
+ /**
+ * @ngdoc service
+ * @name xos.uiComponents.Comparator
+ * @description
+ * This factory define a function that replace the native angular.filter comparator.
+ *
+ * It is done to allow the comparation between (0|1) values with booleans.
+ * >Note that this factory return a single function, not an object.
+ *
+ * The tipical usage of this factory is inside an `ng-repeat`
+ * @example
+ * <example module="comparator">
+ * <file name="index.html">
+ * <div ng-controller="sample as vm">
+ * <div class="row">
+ * <div class="col-xs-6">
+ * <label>Filter by name:</label>
+ * <input class="form-control" type="text" ng-model="vm.query.name"/>
+ * </div>
+ * <div class="col-xs-6">
+ * <label>Filter by status:</label>
+ * <select
+ * ng-model="vm.query.status"
+ * ng-options="i for i in [true, false]">
+ * </select>
+ * </div>
+ * </div>
+ * <div ng-repeat="item in vm.data | filter:vm.query:vm.comparator">
+ * <div class="row">
+ * <div class="col-xs-6">{{item.name}}</div>
+ * <div class="col-xs-6">{{item.status}}</div>
+ * </div>
+ * </div>
+ * </div>
+ * </file>
+ * <file name="script.js">
+ * angular.module('comparator', ['xos.uiComponents'])
+ * .controller('sample', function(Comparator){
+ * this.comparator = Comparator;
+ * this.data = [
+ * {name: 'Jhon', status: 1},
+ * {name: 'Jack', status: 0},
+ * {name: 'Mike', status: 1},
+ * {name: 'Scott', status: 0}
+ * ];
+ * });
+ * </file>
+ * </example>
+ **/
+
+ angular
+ .module('xos.uiComponents')
+ .factory('Comparator', comparator);
+
+ function comparator(_) {
+
+ return function(actual, expected){
+
+ if (angular.isUndefined(actual)) {
+ // No substring matching against `undefined`
+ return false;
+ }
+ if ((actual === null) || (expected === null)) {
+ // No substring matching against `null`; only match against `null`
+ return actual === expected;
+ }
+ if (angular.isObject(expected) || (angular.isObject(actual))){
+ return angular.equals(expected, actual);
+ }
+
+ if(_.isBoolean(actual) || _.isBoolean(expected)){
+ if(actual === 0 || actual === 1){
+ actual = !!actual;
+ }
+ return angular.equals(expected, actual);
+ }
+
+ if(!angular.isString(actual) || !angular.isString(expected)){
+ if(angular.isDefined(actual.toString) && angular.isDefined(expected.toString)){
+ actual = actual.toString();
+ expected = expected.toString();
+ }
+ else {
+ return actual === expected;
+ }
+ }
+
+ actual = actual.toLowerCase() + '';
+ expected = expected.toLowerCase() + '';
+ return actual.indexOf(expected) !== -1;
+ };
+ }
+})();
diff --git a/src/services/helpers/ui/form.helpers.js b/src/services/helpers/ui/form.helpers.js
new file mode 100644
index 0000000..582cb8a
--- /dev/null
+++ b/src/services/helpers/ui/form.helpers.js
@@ -0,0 +1,152 @@
+(function () {
+
+ angular.module('xos.uiComponents')
+
+ /**
+ * @ngdoc service
+ * @name xos.uiComponents.XosFormHelpers
+ * @requires xos.uiComponents.LabelFormatter
+ * @requires xos.helpers._
+ **/
+
+ .service('XosFormHelpers', function(_, LabelFormatter){
+
+ /**
+ * @ngdoc method
+ * @name xos.uiComponents.XosFormHelpers#_isEmail
+ * @methodOf xos.uiComponents.XosFormHelpers
+ * @description
+ * Return true if the string is an email address
+ * @param {string} text The string to be evaluated
+ * @returns {boolean} If the string match an email format
+ **/
+
+ this._isEmail = (text) => {
+ const re = /(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))/;
+ return re.test(text);
+ };
+
+ /**
+ * @ngdoc method
+ * @name xos.uiComponents.XosFormHelpers#_getFieldFormat
+ * @methodOf xos.uiComponents.XosFormHelpers
+ * @description
+ * Return the type of the input
+ * @param {mixed} value The data to be evaluated
+ * @returns {string} The type of the input
+ **/
+
+ this._getFieldFormat = (value) => {
+
+ if(angular.isArray(value)){
+ return 'array';
+ }
+
+ // check if is date
+ if (_.isDate(value) || (!Number.isNaN(Date.parse(value)) && new Date(value).getTime() > 631180800000)){
+ return 'date';
+ }
+
+ // check if is boolean
+ // isNaN(false) = false, false is a number (0), true is a number (1)
+ if(typeof value === 'boolean'){
+ return 'boolean';
+ }
+
+ // check if a string is an email
+ if(this._isEmail(value)){
+ return 'email';
+ }
+
+ // if null return string
+ if(angular.isString(value) || value === null){
+ return 'text';
+ }
+
+ return typeof value;
+ };
+
+ /**
+ * @ngdoc method
+ * @name xos.uiComponents.XosFormHelpers#buildFormStructure
+ * @methodOf xos.uiComponents.XosFormHelpers
+ * @description
+ * Return the type of the input
+ * @param {object} modelField An object containing one property for each field of the model
+ * @param {object} customField An object containing one property for each field custom field
+ * @param {object} model The actual model on wich build the form structure (it is used to determine the type of the input)
+ * @returns {object} An object describing the form structure in the form of:
+ * ```
+ * {
+ * 'field-name': {
+ * label: 'Label',
+ * type: 'number', //typeof field
+ * validators: {}, // see xosForm for more details
+ * hint: 'A Custom hint for the field'
+ * }
+ * }
+ * ```
+ **/
+
+ this.buildFormStructure = (modelField, customField, model) => {
+
+ modelField = angular.extend(modelField, customField);
+ customField = customField || {};
+
+ return _.reduce(Object.keys(modelField), (form, f) => {
+
+ form[f] = {
+ label: (customField[f] && customField[f].label) ? `${customField[f].label}:` : LabelFormatter.format(f),
+ type: (customField[f] && customField[f].type) ? customField[f].type : this._getFieldFormat(model[f]),
+ validators: (customField[f] && customField[f].validators) ? customField[f].validators : {},
+ hint: (customField[f] && customField[f].hint)? customField[f].hint : '',
+ };
+
+ if(customField[f] && customField[f].options){
+ form[f].options = customField[f].options;
+ }
+ if(customField[f] && customField[f].properties){
+ form[f].properties = customField[f].properties;
+ }
+ if(form[f].type === 'date'){
+ model[f] = new Date(model[f]);
+ }
+
+ if(form[f].type === 'number'){
+ model[f] = parseInt(model[f], 10);
+ }
+
+ return form;
+ }, {});
+ };
+
+ /**
+ * @ngdoc method
+ * @name xos.uiComponents.XosFormHelpers#parseModelField
+ * @methodOf xos.uiComponents.XosFormHelpers
+ * @description
+ * Helpers for buildFormStructure, convert a list of model properties in an object used to build the form structure, eg:
+ * ```
+ * // input:
+ * ['id', 'name'm 'mail']
+ *
+ * // output
+ * {
+ * id: {},
+ * name: {},
+ * mail: {}
+ * }
+ * ```
+ * @param {array} fields An array of fields representing the model properties
+ * @returns {object} An object containing one property for each field of the model
+ **/
+
+ this.parseModelField = (fields) => {
+ return _.reduce(fields, (form, f) => {
+ form[f] = {};
+ return form;
+ }, {});
+ }
+
+ });
+})();
\ No newline at end of file
diff --git a/src/services/helpers/ui/label_formatter.service.js b/src/services/helpers/ui/label_formatter.service.js
new file mode 100644
index 0000000..f23fea4
--- /dev/null
+++ b/src/services/helpers/ui/label_formatter.service.js
@@ -0,0 +1,90 @@
+(function() {
+ 'use strict';
+
+ /**
+ * @ngdoc service
+ * @name xos.uiComponents.LabelFormatter
+ * @description This factory define a set of helper function to format label started from an object property
+ **/
+
+ angular
+ .module('xos.uiComponents')
+ .factory('LabelFormatter', labelFormatter);
+
+ function labelFormatter() {
+
+ /**
+ * @ngdoc method
+ * @name xos.uiComponents.LabelFormatter#_formatByUnderscore
+ * @methodOf xos.uiComponents.LabelFormatter
+ * @description
+ * Convert a `snake_case` string to readable string.<br/>
+ * Eg: `this_string` will became `this string`
+ * @param {string} string The string to be converted
+ * @returns {string} The converten string
+ **/
+
+ const _formatByUnderscore = string => string.split('_').join(' ').trim();
+
+ /**
+ * @ngdoc method
+ * @name xos.uiComponents.LabelFormatter#_formatByUppercase
+ * @methodOf xos.uiComponents.LabelFormatter
+ * @description
+ * Convert a `camelCase` string to readable string.<br/>
+ * Eg: `thisString` will became `this string`
+ * @param {string} string The string to be converted
+ * @returns {string} The converten string
+ **/
+
+ const _formatByUppercase = string => string.split(/(?=[A-Z])/).map(w => w.toLowerCase()).join(' ');
+
+ /**
+ * @ngdoc method
+ * @name xos.uiComponents.LabelFormatter#_capitalize
+ * @methodOf xos.uiComponents.LabelFormatter
+ * @description
+ * Capitalize the first letter of a string.<br/>
+ * Eg: `this string` will became `This string`
+ * @param {string} string The string to be converted
+ * @returns {string} The converten string
+ **/
+
+ const _capitalize = string => string.slice(0, 1).toUpperCase() + string.slice(1);
+
+ /**
+ * @ngdoc method
+ * @name xos.uiComponents.LabelFormatter#format
+ * @methodOf xos.uiComponents.LabelFormatter
+ * @description
+ * Apply in order:
+ * - _formatByUnderscore
+ * - _formatByUppercase
+ * - _capitalize
+ * - replace multiple space with a single one
+ * - append `:` at the end
+ * <br/>
+ * Eg: `this_string` will became `This string:`<br/>
+ * Eg: `thisString` will became `This string:`
+ * @param {string} string The string to be converted
+ * @returns {string} The converten string
+ **/
+
+ const format = (string) => {
+ string = _formatByUnderscore(string);
+ string = _formatByUppercase(string);
+
+ string = _capitalize(string).replace(/\s\s+/g, ' ') + ':';
+ return string.replace('::', ':');
+ };
+
+ return {
+ // test export
+ _formatByUnderscore: _formatByUnderscore,
+ _formatByUppercase: _formatByUppercase,
+ _capitalize: _capitalize,
+ // export to use
+ format: format
+ };
+ }
+})();
diff --git a/src/services/helpers/user-prefs.service.js b/src/services/helpers/user-prefs.service.js
new file mode 100644
index 0000000..f3f1f19
--- /dev/null
+++ b/src/services/helpers/user-prefs.service.js
@@ -0,0 +1,154 @@
+(function () {
+
+ angular.module('xos.helpers')
+
+ /**
+ * @ngdoc service
+ * @name xos.helpers.XosUserPrefs
+ * @description
+ * This service is used to store the user preferences in cookies, so that they survive to page changes.
+ * The structure of the user preference is:
+ * ```
+ * {
+ * synchronizers: {
+ * notification: {
+ * 'volt': boolean,
+ * 'openstack': boolean,
+ * ...
+ * }
+ * }
+ * userData: {
+ * current_user_site_id: Number,
+ * current_user_site_user_names: Array[1],
+ * ...
+ * }
+ * }
+ * ```
+ **/
+
+ .service('XosUserPrefs', function($cookies, Me, $q){
+
+ let userPrefs = $cookies.get('xosUserPrefs') ? angular.fromJson($cookies.get('xosUserPrefs')) : {};
+
+ /**
+ * @ngdoc method
+ * @name xos.helpers.XosUserPrefs#getAll
+ * @methodOf xos.helpers.XosUserPrefs
+ * @description
+ * Return all the user preferences stored in cookies
+ * @returns {object} The user preferences
+ **/
+ this.getAll = () => {
+ userPrefs = $cookies.get('xosUserPrefs') ? angular.fromJson($cookies.get('xosUserPrefs')) : {};
+ return userPrefs;
+ };
+
+ /**
+ * @ngdoc method
+ * @name xos.helpers.XosUserPrefs#setAll
+ * @methodOf xos.helpers.XosUserPrefs
+ * @description
+ * Override all user preferences
+ * @param {object} prefs The user preferences
+ **/
+ this.setAll = (prefs) => {
+ $cookies.put('xosUserPrefs', angular.toJson(prefs));
+ };
+
+ /**
+ * @ngdoc method
+ * @name xos.helpers.XosUserPrefs#getSynchronizerNotificationStatus
+ * @methodOf xos.helpers.XosUserPrefs
+ * @description
+ * Return the synchronizer notification status, if name is not provided return the status for all synchronizers
+ * @param {string=} prefs The synchronizer name
+ * @returns {object | string} The synchronizer status
+ **/
+ this.getSynchronizerNotificationStatus = (name = false) => {
+ if(name){
+ return this.getAll().synchronizers.notification[name];
+ }
+ return this.getAll().synchronizers.notification;
+ };
+
+
+ /**
+ * @ngdoc method
+ * @name xos.helpers.XosUserPrefs#getUserDetailsCookie
+ * @methodOf xos.helpers.XosUserPrefs
+ * @description
+ * Return all the user details stored in cookies or call the service
+ * @returns {object} The user details
+ **/
+ this.getUserDetailsCookie = () => {
+ const defer = $q.defer();
+ let localPref = this.getAll();
+ if(!localPref.userData){
+ this.setUserDetailsCookie().$promise.then((data)=>{
+ defer.resolve(data);
+ });
+ }
+ else{
+ defer.resolve(localPref.userData);
+ }
+ return {$promise: defer.promise};
+ };
+
+ /**
+ * @ngdoc method
+ * @name xos.helpers.XosUserPrefs#setUserDetailsCookie
+ * @methodOf xos.helpers.XosUserPrefs
+ * @description
+ * Save the user details in the cookie
+ * @param {object} details stored in cookie
+ * @param {objects} returns the user details as a promise
+ **/
+ this.setUserDetailsCookie = (userData = null)=> {
+
+ const defer = $q.defer();
+ let cookies = this.getAll();
+ if (!userData){
+ Me.get().then((user)=> {
+ cookies.userData = user;
+ this.setAll(cookies);
+ defer.resolve(user);
+ })
+ .catch ((e) => {
+ defer.reject(e);
+ });
+ }
+ else {
+ cookies.userData = userData;
+ this.setAll(cookies);
+ defer.resolve(userData);
+ }
+ return {$promise: defer.promise};
+ }
+
+ /**
+ * @ngdoc method
+ * @name xos.helpers.XosUserPrefs#setSynchronizerNotificationStatus
+ * @methodOf xos.helpers.XosUserPrefs
+ * @description
+ * Update the notification status for a single synchronizer
+ * @param {string} name The synchronizer name
+ * @param {boolean} value The notification status (true means that it has been sent)
+ **/
+
+ this.setSynchronizerNotificationStatus = (name = false, value) => {
+ if(!name){
+ throw new Error('[XosUserPrefs] When updating a synchronizer is mandatory to provide a name.')
+ }
+
+ let cookies = this.getAll();
+
+ if(!cookies.synchronizers){
+ cookies.synchronizers = {
+ notification: {}
+ }
+ }
+ cookies.synchronizers.notification[name] = value;
+ this.setAll(cookies);
+ }
+ });
+})();
\ No newline at end of file