Skip to content

Instantly share code, notes, and snippets.

@reneras
Last active August 29, 2015 14:22
Show Gist options
  • Save reneras/8c3e279d4bc6bac0eee5 to your computer and use it in GitHub Desktop.
Save reneras/8c3e279d4bc6bac0eee5 to your computer and use it in GitHub Desktop.
Code
angular.module('dvb.services').service('statics', function($http) {
var statics = null;
var service = {
/*
Load static data async during Angular run phase and cache it, since we're dealing with static data.
*/
load: function(){
var promise = $http.get('/dvb-service/_statics', {cache: true});
promise.then(function (response) {
statics = response.data;
return arguments[0];
});
return promise;
},
/*
* Get path in JSON object in a parser combinator way.
* examples:
* - `vehicle.types.PEV` returns `Personenvoertuig`
* - `vehicle.brands.AM` returns `Aston Martin`
*
* Or as an Angular filter: {{ model.brand | statics: 'vehicle.brands' }}
*
*/
getPath: function (path) {
var obj = angular.copy(statics);
if(!angular.isArray(path)) path = path.split('.');
for (var i = 0; i < path.length; i++)
obj = obj[path[i]];
return obj;
},
/*
In some cases we need the object flatten out. for example for `typeahead`.
before: obj.key -> children(key,value)
after: obj -> children(key,value)
*/
getPathAndFlatten: function(path, value) {
var obj = service.getPath(path);
var newObj = {};
_.map( obj, function(v){
_.each(v, function(v,k){
newObj[k] = v;
})
});
obj = newObj;
return (value) ? obj[value] : obj;
}
};
return service;
});
/* Openinghours `sticker` component.
* Schema: http://schema.org/OpeningHoursSpecification
*
* Examples of input: `Mo,Tu,We,Th,Fr 09:00-17:00` or [`Mo 09:00-12:00', `Mo 13:30-17:00', 'Tu 09:00-17:00']
* Result: https://www.dropbox.com/s/i69fx2hlympnftr/openinghours.jpg?dl=0
*
*/
angular.module('dvb.components')
.controller('dvbOpeningHoursController', function ($scope) {
var days = ['Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrijdag', 'Zaterdag', 'Zondag'];
var daysShort = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'];
$scope.days = days;
this.convertToSelections = function(openingHours) {
var selectionsArray = [],
daysArray = convertToArray(openingHours);
angular.forEach(daysArray, function(ranges){
var day = [];
angular.forEach(ranges, function(range){
day.push(selectionFormat(range))
});
selectionsArray.push(day);
});
return selectionsArray;
};
var convertToArray = this.convertToArray = function(openingHours, sort) {
var daysObject = {
Mo: [],
Tu: [],
We: [],
Th: [],
Fr: [],
Sa: [],
Su: []
};
// push ranges in corresponding day
angular.forEach(angular.copy(openingHours), function(value){
value = value.split(' ');
var days = value[0].split(',');
var range = value[1];
angular.forEach(days, function(v){
daysObject[v].push(range);
})
});
if(sort){
angular.forEach(daysObject, function(v, day){
daysObject[day] = _.sortBy(daysObject[day], function(range){
var firstRange = range.split('-')[0];
// sort on: total minutes
return ( firstRange.split(':')[0] * 60 ) + firstRange.split(':')[1];
})
})
}
return _.toArray(daysObject);
};
var timeFormat = this.timeFormat = function(selection) {
function pad(num) {
var s = "0" + num;
return s.substr(s.length - 2);
}
var timeString = pad(Math.floor(selection)) + ':' + pad(Math.floor((selection - Math.floor(selection)) * 60));
if(timeString=='24:00') timeString = '23:59';
return timeString;
};
var selectionFormat = this.selectionFormat = function(range) {
var selection = [];
angular.forEach(range.split('-'), function(time){
var hours = Math.floor(time.split(':')[0]);
var minutes = parseFloat(time.split(':')[1]);
minutes = (minutes==0) ? 0 : minutes / 60;
selection.push(hours + minutes)
});
return selection;
};
var openingHoursFromSelections = this.openingHoursFromSelections = function(selection) {
if(!selection) selection = [];
var times = selection.map(function (ranges) {
return ranges.map(function (range) {
return timeFormat(range[0]) + '-' + timeFormat(range[1]);
});
});
var daysByTime = {};
var groupsByTime = [];
daysShort.forEach(function (day, i) {
times[i].forEach(function (time) {
if (time == '') {
return;
}
if (!daysByTime[time]) {
daysByTime[time] = {};
groupsByTime.push(time)
}
daysByTime[time][day] = true;
});
});
return groupsByTime.map(function (time) {
var days = daysShort.filter(function (day) {
return !!(daysByTime[time] && daysByTime[time][day]);
});
return days.join(',') + ' ' + time;
})
};
this.updateOpeningHours = function () {
$scope.openingHours = openingHoursFromSelections($scope.selection);
};
});
angular.module('dvb.components')
.directive('dvbRanges', function ($document) {
return {
templateUrl: 'js/components/openinghours/dvb.ranges.html',
scope: {
rangeValues: '=dvbRanges',
options: '=dvbRangesOptions'
},
controller: 'dvbOpeningHoursController',
link: function (scope, barElement, attrs, ctrl) {
var min,
max,
scale,
stepSize,
ranges,rangeValues,
currentRange = null,
handleOffset = 0;
rangeValues = scope.rangeValues;
scope.isDragging = false;
scope.$watch('options', function () {
min = scope.options.min || 0;
max = scope.options.max || 24;
scale = scope.options.scale || 1;
stepSize = 100 / (max - min);
refreshRanges();
}, true);
scope.$watch('rangeValues', refreshRanges, true);
function rangeFromValue(value) {
return {
left: (value[0] - min) * stepSize,
right: (value[1] - min) * stepSize,
value: value,
removeBtnVisible: false
}
}
function refreshRanges() {
if(!scope.isDragging) rangeValues = scope.rangeValues;
ranges = scope.ranges = rangeValues.map(rangeFromValue);
}
scope.startDrag = function ($event, range) {
scope.isDragging = true;
currentRange = range;
$event.preventDefault();
$event.stopPropagation();
};
scope.removeRange = function (range) {
var rangeIndex = ranges.indexOf(range);
ranges.splice(rangeIndex, 1);
rangeValues.splice(rangeIndex, 1);
};
scope.createRange = function ($event) {
scope.newRangeVisible = false;
var barRect = barElement[0].getBoundingClientRect();
var position = scope.newRangeLeft * 100 / barRect.width / stepSize;
var rangeValue = [min + Math.floor(position / scale) * scale, min + Math.ceil(position / scale) * scale];
var range = rangeFromValue(rangeValue);
var indexToInsert = rangeValues.length;
if (rangeValues.length > 0) {
if (rangeValues[0][0] >= rangeValue[1]) {
indexToInsert = 0;
} else {
for (var i = 1, l = rangeValues.length; i < l; i++) {
if (rangeValues[i][0] >= rangeValue[1] && rangeValues[i - 1][1] <= rangeValue[0]) {
indexToInsert = i;
break;
}
}
}
}
var hasPrev = indexToInsert > 0;
var hasNext = indexToInsert < rangeValues.length;
var connectedWithPrev = hasPrev && rangeValues[indexToInsert - 1][1] == rangeValue[0];
var connectedWithNext = hasNext && rangeValues[indexToInsert][0] == rangeValue[1];
if (connectedWithNext && connectedWithPrev) {
mergeWithNext(indexToInsert - 1);
} else {
var rangeToDrag;
if (connectedWithPrev) {
rangeValues[indexToInsert - 1][1] = rangeValue[1];
ranges[indexToInsert - 1].right = range.right;
rangeToDrag = ranges[indexToInsert - 1];
}
if (connectedWithNext) {
rangeValues[indexToInsert][0] = rangeValue[0];
ranges[indexToInsert].left = range.left;
rangeToDrag = ranges[indexToInsert];
}
if (!connectedWithNext && !connectedWithPrev) {
rangeValues.splice(indexToInsert, 0, rangeValue);
ranges.splice(indexToInsert, 0, range);
rangeToDrag = range;
}
scope.isDragging = true;
currentRange = rangeToDrag;
handleOffset = 0;
}
$event.preventDefault();
$event.stopPropagation();
};
function globalMouseUp() {
if (scope.isDragging) {
scope.$apply(function () {
//rangeValues = scope.rangeValues;
scope.isDragging = false;
})
}
}
$document.on('mouseup', globalMouseUp);
scope.newRangeVisible = false;
scope.newRangeLeft = 0;
var overRange = null;
function mergeWithNext(i) {
rangeValues[i][1] = rangeValues[i + 1][1];
ranges[i].right = (rangeValues[i][1] - min) * stepSize;
scope.isDragging = false;
rangeValues.splice(i + 1, 1);
ranges.splice(i + 1, 1);
}
function globalMouseMove(event) {
var barRect = barElement[0].getBoundingClientRect();
var positionPx;
if (scope.isDragging) {
positionPx = event.clientX - barRect.left - handleOffset;
positionPx = Math.min(barRect.width, Math.max(0, positionPx));
var position = positionPx * 100 / barRect.width;
var left = currentRange.left;
var right = currentRange.right;
if (position >= (currentRange.right + currentRange.left) / 2) {
right = position;
} else {
left = position;
}
var leftValue = min + Math.round(left / stepSize / scale) * scale;
var rightVale = min + Math.round(right / stepSize / scale) * scale;
var currentRangeIndex = rangeValues.indexOf(currentRange.value);
if (currentRangeIndex + 1 < rangeValues.length) {
rightVale = Math.min(rightVale, rangeValues[currentRangeIndex + 1][0]);
if (rightVale == rangeValues[currentRangeIndex + 1][0]) {
mergeWithNext(currentRangeIndex);
return;
}
}
if (currentRangeIndex > 0) {
leftValue = Math.max(leftValue, rangeValues[currentRangeIndex - 1][1]);
if (leftValue == rangeValues[currentRangeIndex - 1][1]) {
mergeWithNext(currentRangeIndex - 1);
return;
}
}
if (currentRange.value[0] != leftValue || currentRange.value[1] != rightVale) {
scope.$apply(function () {
currentRange.value[0] = leftValue;
currentRange.value[1] = rightVale;
currentRange.right = (currentRange.value[1] - min) * stepSize;
currentRange.left = (currentRange.value[0] - min) * stepSize;
});
}
} else {
positionPx = event.clientX - barRect.left;
positionPx = Math.min(barRect.width, Math.max(0, positionPx));
var x = event.clientX, y = event.clientY;
if (barRect.top <= y && barRect.bottom >= y && barRect.left <= x && barRect.right >= x) {
var target = angular.element(event.target);
var isOverRangesBar = target.hasClass('dvb-js-ranges-bar');
var isOverCreateRange = target.hasClass('dvb-js-create-range-btn');
if (isOverRangesBar || isOverCreateRange) {
scope.$apply(function () {
scope.newRangeLeft = positionPx;
scope.newRangeVisible = true;
var position = min + positionPx * 100 / barRect.width / stepSize;
var eps = 10 * 100 / barRect.width / stepSize;
var rangeIndex = -1;
rangeValues.forEach(function (value, index) {
if (position >= value[0] - eps && position <= value[1] + eps) {
rangeIndex = index;
}
});
if (rangeIndex != -1) {
scope.newRangeVisible = false;
}
});
} else {
if (scope.newRangeVisible) {
scope.$apply(function () {
scope.newRangeVisible = false;
});
}
}
var isOverRangeBar = target.hasClass('dvb-js-range-bar');
var isOverRemoveRange = target.hasClass('dvb-js-remove-range-btn');
if (isOverRangeBar || isOverRemoveRange) {
var targetScope = target.scope();
var rangeRect;
if (isOverRemoveRange) {
rangeRect = event.target.parentNode.getBoundingClientRect();
} else {
rangeRect = event.target.getBoundingClientRect();
}
var epsRemoveBtn = 6;
var isRemoveBtnRequired = rangeRect.left + epsRemoveBtn <= x && rangeRect.right - epsRemoveBtn >= x;
scope.$apply(function () {
if (isRemoveBtnRequired) {
if (overRange) {
overRange.removeBtnVisible = false;
}
overRange = targetScope.range;
overRange.removeBtnVisible = true;
overRange.removeBtnLeft = x - rangeRect.left - 10;
} else {
if (overRange) {
overRange.removeBtnVisible = false;
overRange = null;
}
}
});
} else {
if (overRange) {
scope.$apply(function () {
overRange.removeBtnVisible = false;
overRange = null;
});
}
}
} else {
if (scope.newRangeVisible) {
scope.$apply(function () {
scope.newRangeVisible = false;
});
}
if (overRange) {
scope.$apply(function () {
overRange.removeBtnVisible = false;
overRange = null;
});
}
}
}
}
$document.on('mousemove', globalMouseMove);
scope.$on('$destroy', function () {
$document.off('mouseup', globalMouseUp);
$document.off('mousemove', globalMouseMove);
});
scope.timeFormat = ctrl.timeFormat;
}
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment