rolodex 2.0.2 → 2.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/LICENSE.txt +1 -1
- data/bower.json +1 -2
- data/dist/rolodex.js +1747 -0
- data/dist/rolodex.min.js +1 -0
- data/gulpfile.js +57 -0
- data/lib/rolodex/version.rb +1 -1
- data/package.json +25 -14
- data/vendor/assets/javascripts/rolodex_angular/src/dropdown.coffee +5 -1
- data/vendor/assets/javascripts/rolodex_angular/src/position.coffee +0 -1
- data/vendor/assets/javascripts/rolodex_angular/template/accordion/accordion-group.ngt +12 -0
- data/vendor/assets/javascripts/rolodex_angular/template/accordion/accordion.ngt +1 -0
- data/vendor/assets/javascripts/rolodex_angular/template/alert/alert.ngt +4 -0
- data/vendor/assets/javascripts/rolodex_angular/template/datepicker/datepicker.ngt +5 -0
- data/vendor/assets/javascripts/rolodex_angular/template/datepicker/day.ngt +27 -0
- data/vendor/assets/javascripts/rolodex_angular/template/datepicker/month.ngt +30 -0
- data/vendor/assets/javascripts/rolodex_angular/template/datepicker/popup.ngt +3 -0
- data/vendor/assets/javascripts/rolodex_angular/template/datepicker/year.ngt +30 -0
- data/vendor/assets/javascripts/rolodex_angular/template/modal/window.ngt +3 -0
- data/vendor/assets/stylesheets/rolodex/components/_forms.sass +1 -0
- data/vendor/assets/stylesheets/rolodex/settings/mixins/_svg.sass +3 -1
- data/vendor/assets/stylesheets/rolodex/settings/mixins/_typography.sass +2 -4
- data/vendor/assets/stylesheets/rolodex/settings/variables/_misc.scss +4 -0
- data/vendor/assets/stylesheets/rolodex/settings/variables/_typography.scss +1 -1
- metadata +16 -25
- data/spec/javascripts/rolodex_angular/modal.spec.coffee +0 -13
- data/spec/spec_helper.rb +0 -0
- data/spec/test_lib/angular-mocks.js +0 -2173
- data/spec/test_lib/angular.js +0 -21883
- data/spec/test_lib/jquery-2.1.1.js +0 -9190
- data/spec/test_lib/lodash.js +0 -6785
- data/vendor/assets/javascripts/rolodex_angular/template/accordion/accordion-group.ngt.haml +0 -7
- data/vendor/assets/javascripts/rolodex_angular/template/accordion/accordion.ngt.haml +0 -1
- data/vendor/assets/javascripts/rolodex_angular/template/alert/alert.ngt.haml +0 -3
- data/vendor/assets/javascripts/rolodex_angular/template/datepicker/datepicker.ngt.haml +0 -4
- data/vendor/assets/javascripts/rolodex_angular/template/datepicker/day.ngt.haml +0 -20
- data/vendor/assets/javascripts/rolodex_angular/template/datepicker/month.ngt.haml +0 -17
- data/vendor/assets/javascripts/rolodex_angular/template/datepicker/popup.ngt.haml +0 -2
- data/vendor/assets/javascripts/rolodex_angular/template/datepicker/year.ngt.haml +0 -17
- data/vendor/assets/javascripts/rolodex_angular/template/modal/window.ngt.haml +0 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc5ab80dd8fbb098238df00bc298e58c237bf532
|
4
|
+
data.tar.gz: 128fd7f70a65e3c122b5c206ab552d419a8e07af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9221629f20c4e102a719524ee166968b699888a718a992347d487be61e5decb005cb8c87393b9f13f09e95c322699f943d7d9f9f4552e180306e87e45b0bfe13
|
7
|
+
data.tar.gz: 19fc9ec4421f37a6541968767c91fe69b000ea1c762c40d76d4540a9a218292de05c62fe23d182b9938fe716dc547a5ea3f82cb946471114fe481832613649b2
|
data/.gitignore
CHANGED
data/LICENSE.txt
CHANGED
data/bower.json
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
{
|
2
2
|
"name": "rolodex",
|
3
|
-
"version": "2.0.2",
|
4
3
|
"homepage": "http://bellycard.com/",
|
5
4
|
"repository": {
|
6
5
|
"type": "git",
|
7
6
|
"url": "https://github.com/bellycard/rolodex"
|
8
7
|
},
|
9
|
-
"main": "
|
8
|
+
"main": "dist/rolodex.min.js",
|
10
9
|
"ignore": [
|
11
10
|
"**/.*",
|
12
11
|
"node_modules",
|
data/dist/rolodex.js
ADDED
@@ -0,0 +1,1747 @@
|
|
1
|
+
(function() {
|
2
|
+
angular.module('rolodex', ['templates', 'rolodex.accordion', 'rolodex.alert', 'rolodex.buttons', 'rolodex.datepicker', 'rolodex.dropdown', 'rolodex.modal', 'rolodex.transition']);
|
3
|
+
|
4
|
+
}).call(this);
|
5
|
+
|
6
|
+
angular.module("rolodex").run(["$templateCache", function($templateCache) {$templateCache.put("rolodex_angular/template/accordion/accordion-group","<div class=\"panel panel-default\"><div class=\"panel-heading\"><h4 class=\"panel-title\"><a accordion-transclude=\"heading\" class=\"accordion-toggle\" ng-click=\"toggleOpen()\"><span ng-class=\"{\'text-muted\': isDisabled}\">{{heading}}</span></a></h4></div><div class=\"panel-collapse\" collapse=\"!isOpen\"><div class=\"panel-body\" ng-transclude=\"\"></div></div></div>");
|
7
|
+
$templateCache.put("rolodex_angular/template/accordion/accordion","<div class=\"panel-group\" ng-transclude=\"\"></div>");
|
8
|
+
$templateCache.put("rolodex_angular/template/alert/alert","<div ng-class=\"[\'alert-banner-\' + (type || \'warning\'), closeable ? \'alert-dismissable\' : null]\" role=\"alert\"><button aria-hidden=\"true\" class=\"i-close\" ng-click=\"close()\" ng-show=\"closeable\" type=\"button\">×</button><div ng-transclude=\"\"></div></div>");
|
9
|
+
$templateCache.put("rolodex_angular/template/datepicker/datepicker","<div ng-keydown=\"keydown($event)\" ng-switch=\"datepickerMode\" role=\"application\"><daypicker ng-switch-when=\"day\" tabindex=\"0\"></daypicker><monthpicker ng-switch-when=\"month\" tabindex=\"0\"></monthpicker><yearpicker ng-switch-when=\"year\" tabindex=\"0\"></yearpicker></div>");
|
10
|
+
$templateCache.put("rolodex_angular/template/datepicker/day","<table aria-activedescendant=\"{{ activeDateId }}\" aria-labelledby=\"{{uniqueId }}-title\" role=\"grid\"><thead><tr><th class=\"prev available\" ng-click=\"move(-1)\" tabindex=\"-1\"><div class=\"i-caret\"></div></th><th class=\"month\" colspan=\"{{ 5 + showWeeks }}\">{{ title }}</th><th class=\"next available\" ng-click=\"move(1)\" tabindex=\"-1\"><div class=\"i-caret\"></div></th></tr><tr><th class=\"text-center\" ng-repeat=\"label in labels track by $index\"><small aria-label=\"{{ label.full}}\">{{ label.abbr }}</small></th></tr></thead><tbody><tr ng-repeat=\"row in rows track by $index\"><td aria-disabled=\"{{ !!dt.disabled }}\" id=\"{{ dt.uid }}\" ng-class=\"{active: dt.selected, available: !dt.disabled, off: dt.disabled}\" ng-click=\"select(dt.date, dt.disabled)\" ng-disabled=\"dt.disabled\" ng-repeat=\"dt in row track by dt.date\" role=\"gridcell\" tabindex=\"-1\"><span ng-class=\"{\'text-muted\': dt.secondary}\">{{ dt.label }}</span></td></tr></tbody></table>");
|
11
|
+
$templateCache.put("rolodex_angular/template/datepicker/month","<table aria-activedescendant=\"{{ activeDateId }}\" aria-labelledby=\"{{ uniqueId }}-title\" role=\"grid\"><thead><tr><th><button class=\"btn btn-boring pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\" type=\"button\"><i class=\"i-caret\"></i></button></th><th><button aria-atomic=\"true\" aria-live=\"assertive\" class=\"btn btn-boring\" id=\"{{ uniqueId }}-title\" ng-click=\"toggleMode()\" role=\"heading\" style=\"width:100%;\" tabindex=\"-1\" type=\"button\"><strong>{{ title }}</strong></button></th><th><button class=\"btn btn-boring pull-right\" ng-click=\"move(1)\" tabindex=\"-1\" type=\"button\"><i class=\"i-caret\"></i></button></th></tr></thead><tbody><tr ng-repeat=\"row in rows track by $index\"><td aria-disabled=\"{{ !!dt.disabled }}\" class=\"text-center\" id=\"{{ dt.uid }}\" ng-repeat=\"dt in row track by dt.date\" role=\"gridcell\"><button class=\"btn btn-boring\" ng-class=\"{\'btn-info\': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" style=\"width:100%;\" tabindex=\"-1\" type=\"button\"><span ng-class=\"{\'text-info\': dt.current}\">{{ dt.label }}</span></button></td></tr></tbody></table>");
|
12
|
+
$templateCache.put("rolodex_angular/template/datepicker/popup","<div class=\"date-picker-solo daterangepicker dropdown-menu group\" data-dropdown-content=\"\" ng-keydown=\"keydown($event)\"><div ng-transclude=\"\"></div></div>");
|
13
|
+
$templateCache.put("rolodex_angular/template/datepicker/year","<table aria-activedescendant=\"{{ activeDateId }}\" aria-labelledby=\"{{ uniqueId }}-title\" role=\"grid\"><thead><tr><th><button class=\"btn btn-boring pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\" type=\"button\"><i class=\"i-caret\"></i></button></th><th colspan=\"3\"><button aria-atomic=\"true\" aria-live=\"assertive\" class=\"btn btn-boring\" id=\"{{ uniqueId }}-title\" ng-click=\"toggleMode()\" role=\"heading\" style=\"width:100%;\" tabindex=\"-1\" type=\"button\"><strong>{{ title }}</strong></button></th><th><button class=\"btn btn-boring pull-right\" ng-click=\"move(1)\" tabindex=\"-1\" type=\"button\"><i class=\"i-caret\"></i></button></th></tr></thead><tbody><tr ng-repeat=\"row in rows track by $index\"><td aria-disabled=\"{{ !!dt.disabled }}\" class=\"text-center\" id=\"{{ dt.uid }}\" ng-repeat=\"dt in row track by dt.date\" role=\"gridcell\"><button class=\"btn btn-boring\" ng-class=\"{\'btn-info\': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" style=\"width:100%;\" tabindex=\"-1\" type=\"button\"><span ng-class=\"{\'text-info\': dt.current}\">{{ dt.label }}</span></button></td></tr></tbody></table>");
|
14
|
+
$templateCache.put("rolodex_angular/template/modal/window","<div class=\"modal\" ng-class=\"{\'modal-fade\': animate}\" ng-click=\"close($event)\" role=\"dialog\" tabindex=\"-1\"><div class=\"modal-content\" modal-transclude=\"\"></div></div>");}]);
|
15
|
+
(function() { angular.module('templates', []); }).call(this);
|
16
|
+
(function() {
|
17
|
+
angular.module("rolodex.accordion", ["rolodex.collapse"]).constant("accordionConfig", {
|
18
|
+
closeOthers: true
|
19
|
+
}).controller("AccordionController", [
|
20
|
+
"$scope", "$attrs", "accordionConfig", function($scope, $attrs, accordionConfig) {
|
21
|
+
this.groups = [];
|
22
|
+
this.closeOthers = function(openGroup) {
|
23
|
+
var closeOthers;
|
24
|
+
closeOthers = (angular.isDefined($attrs.closeOthers) ? $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers);
|
25
|
+
if (closeOthers) {
|
26
|
+
return angular.forEach(this.groups, function(group) {
|
27
|
+
if (group !== openGroup) {
|
28
|
+
return group.isOpen = false;
|
29
|
+
}
|
30
|
+
});
|
31
|
+
}
|
32
|
+
};
|
33
|
+
this.addGroup = function(groupScope) {
|
34
|
+
var that;
|
35
|
+
that = this;
|
36
|
+
this.groups.push(groupScope);
|
37
|
+
return groupScope.$on("$destroy", function(event) {
|
38
|
+
return that.removeGroup(groupScope);
|
39
|
+
});
|
40
|
+
};
|
41
|
+
this.removeGroup = function(group) {
|
42
|
+
var index;
|
43
|
+
index = this.groups.indexOf(group);
|
44
|
+
if (index !== -1) {
|
45
|
+
return this.groups.splice(index, 1);
|
46
|
+
}
|
47
|
+
};
|
48
|
+
return this;
|
49
|
+
}
|
50
|
+
]).directive("accordion", function() {
|
51
|
+
return {
|
52
|
+
restrict: "EA",
|
53
|
+
controller: "AccordionController",
|
54
|
+
transclude: true,
|
55
|
+
replace: false,
|
56
|
+
templateUrl: 'rolodex_angular/template/accordion/accordion'
|
57
|
+
};
|
58
|
+
}).directive("accordionGroup", function() {
|
59
|
+
return {
|
60
|
+
require: "^accordion",
|
61
|
+
restrict: "EA",
|
62
|
+
transclude: true,
|
63
|
+
replace: true,
|
64
|
+
templateUrl: 'rolodex_angular/template/accordion/accordion-group',
|
65
|
+
scope: {
|
66
|
+
heading: "@",
|
67
|
+
isOpen: "=?",
|
68
|
+
isDisabled: "=?"
|
69
|
+
},
|
70
|
+
controller: function() {
|
71
|
+
return this.setHeading = function(element) {
|
72
|
+
return this.heading = element;
|
73
|
+
};
|
74
|
+
},
|
75
|
+
link: function(scope, element, attrs, accordionCtrl) {
|
76
|
+
accordionCtrl.addGroup(scope);
|
77
|
+
scope.$watch("isOpen", function(value) {
|
78
|
+
if (value) {
|
79
|
+
accordionCtrl.closeOthers(scope);
|
80
|
+
}
|
81
|
+
});
|
82
|
+
return scope.toggleOpen = function() {
|
83
|
+
if (!scope.isDisabled) {
|
84
|
+
return scope.isOpen = !scope.isOpen;
|
85
|
+
}
|
86
|
+
};
|
87
|
+
}
|
88
|
+
};
|
89
|
+
}).directive("accordionHeading", function() {
|
90
|
+
return {
|
91
|
+
restrict: "EA",
|
92
|
+
transclude: true,
|
93
|
+
template: "",
|
94
|
+
replace: true,
|
95
|
+
require: "^accordionGroup",
|
96
|
+
link: function(scope, element, attr, accordionGroupCtrl, transclude) {
|
97
|
+
return accordionGroupCtrl.setHeading(transclude(scope, (function() {})));
|
98
|
+
}
|
99
|
+
};
|
100
|
+
}).directive("accordionTransclude", function() {
|
101
|
+
return {
|
102
|
+
require: "^accordionGroup",
|
103
|
+
link: function(scope, element, attr, controller) {
|
104
|
+
return scope.$watch((function() {
|
105
|
+
return controller[attr.accordionTransclude];
|
106
|
+
}), function(heading) {
|
107
|
+
if (heading) {
|
108
|
+
element.html("");
|
109
|
+
return element.append(heading);
|
110
|
+
}
|
111
|
+
});
|
112
|
+
}
|
113
|
+
};
|
114
|
+
});
|
115
|
+
|
116
|
+
}).call(this);
|
117
|
+
|
118
|
+
(function() {
|
119
|
+
angular.module('rolodex.alert', []).controller('AlertController', [
|
120
|
+
'$scope', '$attrs', function($scope, $attrs) {
|
121
|
+
return $scope.closeable = 'close' in $attrs;
|
122
|
+
}
|
123
|
+
]).directive('alert', function() {
|
124
|
+
return {
|
125
|
+
restrict: 'EA',
|
126
|
+
controller: 'AlertController',
|
127
|
+
templateUrl: 'rolodex_angular/template/alert/alert',
|
128
|
+
transclude: true,
|
129
|
+
replace: true,
|
130
|
+
scope: {
|
131
|
+
type: '@',
|
132
|
+
close: '&'
|
133
|
+
}
|
134
|
+
};
|
135
|
+
});
|
136
|
+
|
137
|
+
}).call(this);
|
138
|
+
|
139
|
+
(function() {
|
140
|
+
angular.module("rolodex.buttons", []).constant("buttonConfig", {
|
141
|
+
activeClass: "active",
|
142
|
+
toggleEvent: "click"
|
143
|
+
}).controller("ButtonsController", [
|
144
|
+
"buttonConfig", function(buttonConfig) {
|
145
|
+
this.activeClass = buttonConfig.activeClass || "active";
|
146
|
+
this.toggleEvent = buttonConfig.toggleEvent || "click";
|
147
|
+
return this;
|
148
|
+
}
|
149
|
+
]).directive("btnRadio", function() {
|
150
|
+
return {
|
151
|
+
require: ["btnRadio", "ngModel"],
|
152
|
+
controller: "ButtonsController",
|
153
|
+
link: function(scope, element, attrs, ctrls) {
|
154
|
+
var buttonsCtrl, ngModelCtrl;
|
155
|
+
buttonsCtrl = ctrls[0];
|
156
|
+
ngModelCtrl = ctrls[1];
|
157
|
+
ngModelCtrl.$render = function() {
|
158
|
+
return element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.btnRadio)));
|
159
|
+
};
|
160
|
+
return element.bind(buttonsCtrl.toggleEvent, function() {
|
161
|
+
var isActive;
|
162
|
+
isActive = element.hasClass(buttonsCtrl.activeClass);
|
163
|
+
if (!isActive || angular.isDefined(attrs.uncheckable)) {
|
164
|
+
return scope.$apply(function() {
|
165
|
+
ngModelCtrl.$setViewValue((isActive ? null : scope.$eval(attrs.btnRadio)));
|
166
|
+
return ngModelCtrl.$render();
|
167
|
+
});
|
168
|
+
}
|
169
|
+
});
|
170
|
+
}
|
171
|
+
};
|
172
|
+
}).directive("btnCheckbox", function() {
|
173
|
+
return {
|
174
|
+
require: ["btnCheckbox", "ngModel"],
|
175
|
+
controller: "ButtonsController",
|
176
|
+
link: function(scope, element, attrs, ctrls) {
|
177
|
+
var buttonsCtrl, getCheckboxValue, getFalseValue, getTrueValue, ngModelCtrl;
|
178
|
+
getTrueValue = function() {
|
179
|
+
return getCheckboxValue(attrs.btnCheckboxTrue, true);
|
180
|
+
};
|
181
|
+
getFalseValue = function() {
|
182
|
+
return getCheckboxValue(attrs.btnCheckboxFalse, false);
|
183
|
+
};
|
184
|
+
getCheckboxValue = function(attributeValue, defaultValue) {
|
185
|
+
var val;
|
186
|
+
val = scope.$eval(attributeValue);
|
187
|
+
if (angular.isDefined(val)) {
|
188
|
+
return val;
|
189
|
+
} else {
|
190
|
+
return defaultValue;
|
191
|
+
}
|
192
|
+
};
|
193
|
+
buttonsCtrl = ctrls[0];
|
194
|
+
ngModelCtrl = ctrls[1];
|
195
|
+
ngModelCtrl.$render = function() {
|
196
|
+
return element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));
|
197
|
+
};
|
198
|
+
return element.bind(buttonsCtrl.toggleEvent, function() {
|
199
|
+
return scope.$apply(function() {
|
200
|
+
ngModelCtrl.$setViewValue((element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue()));
|
201
|
+
return ngModelCtrl.$render();
|
202
|
+
});
|
203
|
+
});
|
204
|
+
}
|
205
|
+
};
|
206
|
+
});
|
207
|
+
|
208
|
+
}).call(this);
|
209
|
+
|
210
|
+
(function() {
|
211
|
+
angular.module("rolodex.collapse", ["rolodex.transition"]).directive("collapse", [
|
212
|
+
"$transition", function($transition) {
|
213
|
+
return {
|
214
|
+
link: function(scope, element, attrs) {
|
215
|
+
var collapse, collapseDone, currentTransition, doTransition, expand, expandDone, initialAnimSkip;
|
216
|
+
doTransition = function(change) {
|
217
|
+
var currentTransition, newTransition, newTransitionDone;
|
218
|
+
newTransitionDone = function() {
|
219
|
+
var currentTransition;
|
220
|
+
if (currentTransition === newTransition) {
|
221
|
+
return currentTransition = undefined;
|
222
|
+
}
|
223
|
+
};
|
224
|
+
newTransition = $transition(element, change);
|
225
|
+
if (currentTransition) {
|
226
|
+
currentTransition.cancel();
|
227
|
+
}
|
228
|
+
currentTransition = newTransition;
|
229
|
+
newTransition.then(newTransitionDone, newTransitionDone);
|
230
|
+
return newTransition;
|
231
|
+
};
|
232
|
+
expand = function() {
|
233
|
+
var initialAnimSkip;
|
234
|
+
if (initialAnimSkip) {
|
235
|
+
initialAnimSkip = false;
|
236
|
+
expandDone();
|
237
|
+
} else {
|
238
|
+
element.removeClass("collapse").addClass("collapsing");
|
239
|
+
doTransition({
|
240
|
+
height: element[0].scrollHeight + "px"
|
241
|
+
}).then(expandDone);
|
242
|
+
}
|
243
|
+
};
|
244
|
+
expandDone = function() {
|
245
|
+
element.removeClass("collapsing");
|
246
|
+
element.addClass("collapse in");
|
247
|
+
element.css({
|
248
|
+
height: "auto"
|
249
|
+
});
|
250
|
+
};
|
251
|
+
collapse = function() {
|
252
|
+
var initialAnimSkip, x;
|
253
|
+
if (initialAnimSkip) {
|
254
|
+
initialAnimSkip = false;
|
255
|
+
collapseDone();
|
256
|
+
element.css({
|
257
|
+
height: 0
|
258
|
+
});
|
259
|
+
} else {
|
260
|
+
element.css({
|
261
|
+
height: element[0].scrollHeight + "px"
|
262
|
+
});
|
263
|
+
x = element[0].offsetWidth;
|
264
|
+
element.removeClass("collapse in").addClass("collapsing");
|
265
|
+
doTransition({
|
266
|
+
height: 0
|
267
|
+
}).then(collapseDone);
|
268
|
+
}
|
269
|
+
};
|
270
|
+
collapseDone = function() {
|
271
|
+
element.removeClass("collapsing");
|
272
|
+
element.addClass("collapse");
|
273
|
+
};
|
274
|
+
initialAnimSkip = true;
|
275
|
+
currentTransition = void 0;
|
276
|
+
scope.$watch(attrs.collapse, function(shouldCollapse) {
|
277
|
+
if (shouldCollapse) {
|
278
|
+
collapse();
|
279
|
+
} else {
|
280
|
+
expand();
|
281
|
+
}
|
282
|
+
});
|
283
|
+
}
|
284
|
+
};
|
285
|
+
}
|
286
|
+
]);
|
287
|
+
|
288
|
+
}).call(this);
|
289
|
+
|
290
|
+
(function() {
|
291
|
+
angular.module('rolodex.dateparser', []).service("dateParser", [
|
292
|
+
"$locale", "orderByFilter", function($locale, orderByFilter) {
|
293
|
+
var createParser, formatCodeToRegex, isValid;
|
294
|
+
createParser = function(format) {
|
295
|
+
var map, regex;
|
296
|
+
map = [];
|
297
|
+
regex = format.split("");
|
298
|
+
angular.forEach(formatCodeToRegex, function(data, code) {
|
299
|
+
var i, index, n;
|
300
|
+
index = format.indexOf(code);
|
301
|
+
if (index > -1) {
|
302
|
+
format = format.split("");
|
303
|
+
regex[index] = "(" + data.regex + ")";
|
304
|
+
format[index] = "$";
|
305
|
+
i = index + 1;
|
306
|
+
n = index + code.length;
|
307
|
+
while (i < n) {
|
308
|
+
regex[i] = "";
|
309
|
+
format[i] = "$";
|
310
|
+
i++;
|
311
|
+
}
|
312
|
+
format = format.join("");
|
313
|
+
map.push({
|
314
|
+
index: index,
|
315
|
+
apply: data.apply
|
316
|
+
});
|
317
|
+
}
|
318
|
+
});
|
319
|
+
return {
|
320
|
+
regex: new RegExp("^" + regex.join("") + "$"),
|
321
|
+
map: orderByFilter(map, "index")
|
322
|
+
};
|
323
|
+
};
|
324
|
+
isValid = function(year, month, date) {
|
325
|
+
if (month === 1 && date > 28) {
|
326
|
+
return date === 29 && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0);
|
327
|
+
}
|
328
|
+
if (month === 3 || month === 5 || month === 8 || month === 10) {
|
329
|
+
return date < 31;
|
330
|
+
}
|
331
|
+
return true;
|
332
|
+
};
|
333
|
+
this.parsers = {};
|
334
|
+
formatCodeToRegex = {
|
335
|
+
yyyy: {
|
336
|
+
regex: "\\d{4}",
|
337
|
+
apply: function(value) {
|
338
|
+
this.year = +value;
|
339
|
+
}
|
340
|
+
},
|
341
|
+
yy: {
|
342
|
+
regex: "\\d{2}",
|
343
|
+
apply: function(value) {
|
344
|
+
this.year = +value + 2000;
|
345
|
+
}
|
346
|
+
},
|
347
|
+
y: {
|
348
|
+
regex: "\\d{1,4}",
|
349
|
+
apply: function(value) {
|
350
|
+
this.year = +value;
|
351
|
+
}
|
352
|
+
},
|
353
|
+
MMMM: {
|
354
|
+
regex: $locale.DATETIME_FORMATS.MONTH.join("|"),
|
355
|
+
apply: function(value) {
|
356
|
+
this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value);
|
357
|
+
}
|
358
|
+
},
|
359
|
+
MMM: {
|
360
|
+
regex: $locale.DATETIME_FORMATS.SHORTMONTH.join("|"),
|
361
|
+
apply: function(value) {
|
362
|
+
this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value);
|
363
|
+
}
|
364
|
+
},
|
365
|
+
MM: {
|
366
|
+
regex: "0[1-9]|1[0-2]",
|
367
|
+
apply: function(value) {
|
368
|
+
this.month = value - 1;
|
369
|
+
}
|
370
|
+
},
|
371
|
+
M: {
|
372
|
+
regex: "[1-9]|1[0-2]",
|
373
|
+
apply: function(value) {
|
374
|
+
this.month = value - 1;
|
375
|
+
}
|
376
|
+
},
|
377
|
+
dd: {
|
378
|
+
regex: "[0-2][0-9]{1}|3[0-1]{1}",
|
379
|
+
apply: function(value) {
|
380
|
+
this.date = +value;
|
381
|
+
}
|
382
|
+
},
|
383
|
+
d: {
|
384
|
+
regex: "[1-2]?[0-9]{1}|3[0-1]{1}",
|
385
|
+
apply: function(value) {
|
386
|
+
return this.date = +value;
|
387
|
+
}
|
388
|
+
},
|
389
|
+
EEEE: {
|
390
|
+
regex: $locale.DATETIME_FORMATS.DAY.join("|")
|
391
|
+
},
|
392
|
+
EEE: {
|
393
|
+
regex: $locale.DATETIME_FORMATS.SHORTDAY.join("|")
|
394
|
+
}
|
395
|
+
};
|
396
|
+
return this.parse = function(input, format) {
|
397
|
+
var dt, fields, i, map, mapper, n, parser, regex, results;
|
398
|
+
if (!angular.isString(input) || !format) {
|
399
|
+
return input;
|
400
|
+
}
|
401
|
+
format = $locale.DATETIME_FORMATS[format] || format;
|
402
|
+
if (!this.parsers[format]) {
|
403
|
+
this.parsers[format] = createParser(format);
|
404
|
+
}
|
405
|
+
parser = this.parsers[format];
|
406
|
+
regex = parser.regex;
|
407
|
+
map = parser.map;
|
408
|
+
results = input.match(regex);
|
409
|
+
if (results && results.length) {
|
410
|
+
fields = {
|
411
|
+
year: 1900,
|
412
|
+
month: 0,
|
413
|
+
date: 1,
|
414
|
+
hours: 0
|
415
|
+
};
|
416
|
+
dt = void 0;
|
417
|
+
i = 1;
|
418
|
+
n = results.length;
|
419
|
+
while (i < n) {
|
420
|
+
mapper = map[i - 1];
|
421
|
+
if (mapper.apply) {
|
422
|
+
mapper.apply.call(fields, results[i]);
|
423
|
+
}
|
424
|
+
i++;
|
425
|
+
}
|
426
|
+
if (isValid(fields.year, fields.month, fields.date)) {
|
427
|
+
dt = new Date(fields.year, fields.month, fields.date, fields.hours);
|
428
|
+
}
|
429
|
+
return dt;
|
430
|
+
}
|
431
|
+
};
|
432
|
+
}
|
433
|
+
]);
|
434
|
+
|
435
|
+
}).call(this);
|
436
|
+
|
437
|
+
(function() {
|
438
|
+
angular.module('rolodex.datepicker', ['rolodex.dateparser', 'rolodex.dropdown', 'rolodex.position']).constant('datepickerConfig', {
|
439
|
+
formatDay: 'd',
|
440
|
+
formatMonth: 'MMMM',
|
441
|
+
formatYear: 'yyyy',
|
442
|
+
formatDayHeader: 'EEE',
|
443
|
+
formatDayTitle: 'MMMM yyyy',
|
444
|
+
formatMonthTitle: 'yyyy',
|
445
|
+
datepickerMode: 'day',
|
446
|
+
minMode: 'day',
|
447
|
+
maxMode: 'year',
|
448
|
+
showWeeks: true,
|
449
|
+
startingDay: 0,
|
450
|
+
yearRange: 20,
|
451
|
+
minDate: null,
|
452
|
+
maxDate: null
|
453
|
+
}).controller('DatepickerController', [
|
454
|
+
'$scope', '$attrs', '$parse', '$interpolate', '$timeout', '$log', 'dateFilter', 'datepickerConfig', function($scope, $attrs, $parse, $interpolate, $timeout, $log, dateFilter, datepickerConfig) {
|
455
|
+
var focusElement, ngModelCtrl, self;
|
456
|
+
self = this;
|
457
|
+
ngModelCtrl = {
|
458
|
+
$setViewValue: angular.noop
|
459
|
+
};
|
460
|
+
this.modes = ['day', 'month', 'year'];
|
461
|
+
angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle', 'minMode', 'maxMode', 'showWeeks', 'startingDay', 'yearRange'], function(key, index) {
|
462
|
+
self[key] = (angular.isDefined($attrs[key]) ? (index < 8 ? $interpolate($attrs[key])($scope.$parent) : $scope.$parent.$eval($attrs[key])) : datepickerConfig[key]);
|
463
|
+
});
|
464
|
+
angular.forEach(['minDate', 'maxDate'], function(key) {
|
465
|
+
if ($attrs[key]) {
|
466
|
+
return $scope.$parent.$watch($parse($attrs[key]), function(value) {
|
467
|
+
self[key] = (value ? new Date(value) : null);
|
468
|
+
return self.refreshView();
|
469
|
+
});
|
470
|
+
} else {
|
471
|
+
return self[key] = (datepickerConfig[key] ? new Date(datepickerConfig[key]) : null);
|
472
|
+
}
|
473
|
+
});
|
474
|
+
$scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode;
|
475
|
+
$scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
|
476
|
+
this.activeDate = (angular.isDefined($attrs.initDate) ? $scope.$parent.$eval($attrs.initDate) : new Date());
|
477
|
+
this.init = function(ngModelCtrl_) {
|
478
|
+
ngModelCtrl = ngModelCtrl_;
|
479
|
+
return ngModelCtrl.$render = function() {
|
480
|
+
return self.render();
|
481
|
+
};
|
482
|
+
};
|
483
|
+
this.render = function() {
|
484
|
+
var date, isValid;
|
485
|
+
if (ngModelCtrl.$modelValue) {
|
486
|
+
date = new Date(ngModelCtrl.$modelValue);
|
487
|
+
isValid = !isNaN(date);
|
488
|
+
if (isValid) {
|
489
|
+
this.activeDate = date;
|
490
|
+
} else {
|
491
|
+
$log.error('Datepicker directive: \'ng-model\' value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
|
492
|
+
}
|
493
|
+
ngModelCtrl.$setValidity('date', isValid);
|
494
|
+
}
|
495
|
+
return this.refreshView();
|
496
|
+
};
|
497
|
+
this.refreshView = function() {
|
498
|
+
var date;
|
499
|
+
if (this.element) {
|
500
|
+
this._refreshView();
|
501
|
+
date = (ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : null);
|
502
|
+
return ngModelCtrl.$setValidity('date-disabled', !date || (this.element && !this.isDisabled(date)));
|
503
|
+
}
|
504
|
+
};
|
505
|
+
this.createDateObject = function(date, format) {
|
506
|
+
var model;
|
507
|
+
model = (ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : null);
|
508
|
+
return {
|
509
|
+
date: date,
|
510
|
+
label: dateFilter(date, format),
|
511
|
+
selected: model && this.compare(date, model) === 0,
|
512
|
+
disabled: this.isDisabled(date),
|
513
|
+
current: this.compare(date, new Date()) === 0
|
514
|
+
};
|
515
|
+
};
|
516
|
+
this.isDisabled = function(date) {
|
517
|
+
return (this.minDate && this.compare(date, this.minDate) < 0) || (this.maxDate && this.compare(date, this.maxDate) > 0) || ($attrs.dateDisabled && $scope.dateDisabled({
|
518
|
+
date: date,
|
519
|
+
mode: $scope.datepickerMode
|
520
|
+
}));
|
521
|
+
};
|
522
|
+
this.split = function(arr, size) {
|
523
|
+
var arrays;
|
524
|
+
arrays = [];
|
525
|
+
while (arr.length > 0) {
|
526
|
+
arrays.push(arr.splice(0, size));
|
527
|
+
}
|
528
|
+
return arrays;
|
529
|
+
};
|
530
|
+
$scope.select = (function(_this) {
|
531
|
+
return function(date) {
|
532
|
+
var dt;
|
533
|
+
if (_this.isDisabled(date)) {
|
534
|
+
return;
|
535
|
+
}
|
536
|
+
if ($scope.datepickerMode === self.minMode) {
|
537
|
+
dt = (ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : new Date(0, 0, 0, 0, 0, 0, 0));
|
538
|
+
dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
|
539
|
+
ngModelCtrl.$setViewValue(dt);
|
540
|
+
return ngModelCtrl.$render();
|
541
|
+
} else {
|
542
|
+
self.activeDate = date;
|
543
|
+
return $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) - 1];
|
544
|
+
}
|
545
|
+
};
|
546
|
+
})(this);
|
547
|
+
$scope.move = function(direction) {
|
548
|
+
var month, year;
|
549
|
+
year = self.activeDate.getFullYear() + direction * (self.step.years || 0);
|
550
|
+
month = self.activeDate.getMonth() + direction * (self.step.months || 0);
|
551
|
+
self.activeDate.setFullYear(year, month, 1);
|
552
|
+
return self.refreshView();
|
553
|
+
};
|
554
|
+
$scope.toggleMode = function(direction) {
|
555
|
+
direction = direction || 1;
|
556
|
+
if (($scope.datepickerMode === self.maxMode && direction === 1) || ($scope.datepickerMode === self.minMode && direction === -1)) {
|
557
|
+
return;
|
558
|
+
}
|
559
|
+
return $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) + direction];
|
560
|
+
};
|
561
|
+
$scope.keys = {
|
562
|
+
13: 'enter',
|
563
|
+
32: 'space',
|
564
|
+
33: 'pageup',
|
565
|
+
34: 'pagedown',
|
566
|
+
35: 'end',
|
567
|
+
36: 'home',
|
568
|
+
37: 'left',
|
569
|
+
38: 'up',
|
570
|
+
39: 'right',
|
571
|
+
40: 'down'
|
572
|
+
};
|
573
|
+
focusElement = function() {
|
574
|
+
return $timeout((function() {
|
575
|
+
return self.element[0].focus();
|
576
|
+
}), 0, false);
|
577
|
+
};
|
578
|
+
$scope.$on('datepicker.focus', focusElement);
|
579
|
+
$scope.keydown = function(evt) {
|
580
|
+
var key;
|
581
|
+
key = $scope.keys[evt.which];
|
582
|
+
if (!key || evt.shiftKey || evt.altKey) {
|
583
|
+
return;
|
584
|
+
}
|
585
|
+
evt.preventDefault();
|
586
|
+
evt.stopPropagation();
|
587
|
+
if (key === 'enter' || key === 'space') {
|
588
|
+
if (self.isDisabled(self.activeDate)) {
|
589
|
+
return;
|
590
|
+
}
|
591
|
+
$scope.select(self.activeDate);
|
592
|
+
return focusElement();
|
593
|
+
} else if (evt.ctrlKey && (key === 'up' || key === 'down')) {
|
594
|
+
$scope.toggleMode((key === 'up' ? 1 : -1));
|
595
|
+
return focusElement();
|
596
|
+
} else {
|
597
|
+
self.handleKeyDown(key, evt);
|
598
|
+
return self.refreshView();
|
599
|
+
}
|
600
|
+
};
|
601
|
+
return this;
|
602
|
+
}
|
603
|
+
]).directive('datepicker', function() {
|
604
|
+
return {
|
605
|
+
restrict: 'EA',
|
606
|
+
replace: true,
|
607
|
+
templateUrl: 'rolodex_angular/template/datepicker/datepicker',
|
608
|
+
scope: {
|
609
|
+
datepickerMode: '=?',
|
610
|
+
dateDisabled: '&'
|
611
|
+
},
|
612
|
+
require: ['datepicker', '?^ngModel'],
|
613
|
+
controller: 'DatepickerController',
|
614
|
+
link: function(scope, element, attrs, ctrls) {
|
615
|
+
var datepickerCtrl, ngModelCtrl;
|
616
|
+
datepickerCtrl = ctrls[0];
|
617
|
+
ngModelCtrl = ctrls[1];
|
618
|
+
if (ngModelCtrl) {
|
619
|
+
return datepickerCtrl.init(ngModelCtrl);
|
620
|
+
}
|
621
|
+
}
|
622
|
+
};
|
623
|
+
}).directive('daypicker', [
|
624
|
+
'dateFilter', function(dateFilter) {
|
625
|
+
return {
|
626
|
+
restrict: 'EA',
|
627
|
+
replace: true,
|
628
|
+
templateUrl: 'rolodex_angular/template/datepicker/day',
|
629
|
+
require: '^datepicker',
|
630
|
+
link: function(scope, element, attrs, ctrl) {
|
631
|
+
var DAYS_IN_MONTH, getDates, getDaysInMonth, getISO8601WeekNumber;
|
632
|
+
getDaysInMonth = function(year, month) {
|
633
|
+
if ((month === 1) && (year % 4 === 0) && ((year % 100 !== 0) || (year % 400 === 0))) {
|
634
|
+
return 29;
|
635
|
+
} else {
|
636
|
+
return DAYS_IN_MONTH[month];
|
637
|
+
}
|
638
|
+
};
|
639
|
+
getDates = function(startDate, n) {
|
640
|
+
var current, dates, i;
|
641
|
+
dates = new Array(n);
|
642
|
+
current = new Date(startDate);
|
643
|
+
i = 0;
|
644
|
+
current.setHours(12);
|
645
|
+
while (i < n) {
|
646
|
+
dates[i++] = new Date(current);
|
647
|
+
current.setDate(current.getDate() + 1);
|
648
|
+
}
|
649
|
+
return dates;
|
650
|
+
};
|
651
|
+
getISO8601WeekNumber = function(date) {
|
652
|
+
var checkDate, time;
|
653
|
+
checkDate = new Date(date);
|
654
|
+
checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
|
655
|
+
time = checkDate.getTime();
|
656
|
+
checkDate.setMonth(0);
|
657
|
+
checkDate.setDate(1);
|
658
|
+
return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
|
659
|
+
};
|
660
|
+
scope.showWeeks = ctrl.showWeeks;
|
661
|
+
ctrl.step = {
|
662
|
+
months: 1
|
663
|
+
};
|
664
|
+
ctrl.element = element;
|
665
|
+
DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
666
|
+
ctrl._refreshView = function() {
|
667
|
+
var days, difference, firstDate, firstDayOfMonth, i, j, month, numDisplayedFromPreviousMonth, numWeeks, results, weekNumber, year;
|
668
|
+
year = ctrl.activeDate.getFullYear();
|
669
|
+
month = ctrl.activeDate.getMonth();
|
670
|
+
firstDayOfMonth = new Date(year, month, 1);
|
671
|
+
difference = ctrl.startingDay - firstDayOfMonth.getDay();
|
672
|
+
numDisplayedFromPreviousMonth = (difference > 0 ? 7 - difference : -difference);
|
673
|
+
firstDate = new Date(firstDayOfMonth);
|
674
|
+
if (numDisplayedFromPreviousMonth > 0) {
|
675
|
+
firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
|
676
|
+
}
|
677
|
+
days = getDates(firstDate, 42);
|
678
|
+
i = 0;
|
679
|
+
while (i < 42) {
|
680
|
+
days[i] = angular.extend(ctrl.createDateObject(days[i], ctrl.formatDay), {
|
681
|
+
secondary: days[i].getMonth() !== month,
|
682
|
+
uid: scope.uniqueId + '-' + i
|
683
|
+
});
|
684
|
+
i++;
|
685
|
+
}
|
686
|
+
scope.labels = new Array(7);
|
687
|
+
j = 0;
|
688
|
+
while (j < 7) {
|
689
|
+
scope.labels[j] = {
|
690
|
+
abbr: dateFilter(days[j].date, ctrl.formatDayHeader).substr(0, 2),
|
691
|
+
full: dateFilter(days[j].date, 'EEEE')
|
692
|
+
};
|
693
|
+
j++;
|
694
|
+
}
|
695
|
+
scope.title = dateFilter(ctrl.activeDate, ctrl.formatDayTitle);
|
696
|
+
scope.rows = ctrl.split(days, 7);
|
697
|
+
if (scope.showWeeks) {
|
698
|
+
scope.weekNumbers = [];
|
699
|
+
weekNumber = getISO8601WeekNumber(scope.rows[0][0].date);
|
700
|
+
numWeeks = scope.rows.length;
|
701
|
+
results = [];
|
702
|
+
while (scope.weekNumbers.push(weekNumber++) < numWeeks) {
|
703
|
+
continue;
|
704
|
+
}
|
705
|
+
return results;
|
706
|
+
}
|
707
|
+
};
|
708
|
+
ctrl.compare = function(date1, date2) {
|
709
|
+
return new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate());
|
710
|
+
};
|
711
|
+
ctrl.handleKeyDown = function(key, evt) {
|
712
|
+
var date, month;
|
713
|
+
date = ctrl.activeDate.getDate();
|
714
|
+
if (key === 'left') {
|
715
|
+
date = date - 1;
|
716
|
+
} else if (key === 'up') {
|
717
|
+
date = date - 7;
|
718
|
+
} else if (key === 'right') {
|
719
|
+
date = date + 1;
|
720
|
+
} else if (key === 'down') {
|
721
|
+
date = date + 7;
|
722
|
+
} else if (key === 'pageup' || key === 'pagedown') {
|
723
|
+
month = ctrl.activeDate.getMonth() + (key === 'pageup' ? -1 : 1);
|
724
|
+
ctrl.activeDate.setMonth(month, 1);
|
725
|
+
date = Math.min(getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth()), date);
|
726
|
+
} else if (key === 'home') {
|
727
|
+
date = 1;
|
728
|
+
} else {
|
729
|
+
if (key === 'end') {
|
730
|
+
date = getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth());
|
731
|
+
}
|
732
|
+
}
|
733
|
+
return ctrl.activeDate.setDate(date);
|
734
|
+
};
|
735
|
+
return ctrl.refreshView();
|
736
|
+
}
|
737
|
+
};
|
738
|
+
}
|
739
|
+
]).directive('monthpicker', [
|
740
|
+
'dateFilter', function(dateFilter) {
|
741
|
+
return {
|
742
|
+
restrict: 'EA',
|
743
|
+
replace: true,
|
744
|
+
templateUrl: 'rolodex_angular/template/datepicker/month',
|
745
|
+
require: '^datepicker',
|
746
|
+
link: function(scope, element, attrs, ctrl) {
|
747
|
+
ctrl.step = {
|
748
|
+
years: 1
|
749
|
+
};
|
750
|
+
ctrl.element = element;
|
751
|
+
ctrl._refreshView = function() {
|
752
|
+
var i, months, year;
|
753
|
+
months = new Array(12);
|
754
|
+
year = ctrl.activeDate.getFullYear();
|
755
|
+
i = 0;
|
756
|
+
while (i < 12) {
|
757
|
+
months[i] = angular.extend(ctrl.createDateObject(new Date(year, i, 1), ctrl.formatMonth), {
|
758
|
+
uid: scope.uniqueId + '-' + i
|
759
|
+
});
|
760
|
+
i++;
|
761
|
+
}
|
762
|
+
scope.title = dateFilter(ctrl.activeDate, ctrl.formatMonthTitle);
|
763
|
+
return scope.rows = ctrl.split(months, 3);
|
764
|
+
};
|
765
|
+
ctrl.compare = function(date1, date2) {
|
766
|
+
return new Date(date1.getFullYear(), date1.getMonth()) - new Date(date2.getFullYear(), date2.getMonth());
|
767
|
+
};
|
768
|
+
ctrl.handleKeyDown = function(key, evt) {
|
769
|
+
var date, year;
|
770
|
+
date = ctrl.activeDate.getMonth();
|
771
|
+
if (key === 'left') {
|
772
|
+
date = date - 1;
|
773
|
+
} else if (key === 'up') {
|
774
|
+
date = date - 3;
|
775
|
+
} else if (key === 'right') {
|
776
|
+
date = date + 1;
|
777
|
+
} else if (key === 'down') {
|
778
|
+
date = date + 3;
|
779
|
+
} else if (key === 'pageup' || key === 'pagedown') {
|
780
|
+
year = ctrl.activeDate.getFullYear() + (key === 'pageup' ? -1 : 1);
|
781
|
+
ctrl.activeDate.setFullYear(year);
|
782
|
+
} else if (key === 'home') {
|
783
|
+
date = 0;
|
784
|
+
} else {
|
785
|
+
if (key === 'end') {
|
786
|
+
date = 11;
|
787
|
+
}
|
788
|
+
}
|
789
|
+
return ctrl.activeDate.setMonth(date);
|
790
|
+
};
|
791
|
+
return ctrl.refreshView();
|
792
|
+
}
|
793
|
+
};
|
794
|
+
}
|
795
|
+
]).directive('yearpicker', [
|
796
|
+
'dateFilter', function(dateFilter) {
|
797
|
+
return {
|
798
|
+
restrict: 'EA',
|
799
|
+
replace: true,
|
800
|
+
templateUrl: 'rolodex_angular/template/datepicker/year',
|
801
|
+
require: '^datepicker',
|
802
|
+
link: function(scope, element, attrs, ctrl) {
|
803
|
+
var getStartingYear, range;
|
804
|
+
getStartingYear = function(year) {
|
805
|
+
return parseInt((year - 1) / range, 10) * range + 1;
|
806
|
+
};
|
807
|
+
range = ctrl.yearRange;
|
808
|
+
ctrl.step = {
|
809
|
+
years: range
|
810
|
+
};
|
811
|
+
ctrl.element = element;
|
812
|
+
ctrl._refreshView = function() {
|
813
|
+
var i, start, years;
|
814
|
+
years = new Array(range);
|
815
|
+
i = 0;
|
816
|
+
start = getStartingYear(ctrl.activeDate.getFullYear());
|
817
|
+
while (i < range) {
|
818
|
+
years[i] = angular.extend(ctrl.createDateObject(new Date(start + i, 0, 1), ctrl.formatYear), {
|
819
|
+
uid: scope.uniqueId + '-' + i
|
820
|
+
});
|
821
|
+
i++;
|
822
|
+
}
|
823
|
+
scope.title = [years[0].label, years[range - 1].label].join(' - ');
|
824
|
+
return scope.rows = ctrl.split(years, 5);
|
825
|
+
};
|
826
|
+
ctrl.compare = function(date1, date2) {
|
827
|
+
return date1.getFullYear() - date2.getFullYear();
|
828
|
+
};
|
829
|
+
ctrl.handleKeyDown = function(key, evt) {
|
830
|
+
var date;
|
831
|
+
date = ctrl.activeDate.getFullYear();
|
832
|
+
if (key === 'left') {
|
833
|
+
date = date - 1;
|
834
|
+
} else if (key === 'up') {
|
835
|
+
date = date - 5;
|
836
|
+
} else if (key === 'right') {
|
837
|
+
date = date + 1;
|
838
|
+
} else if (key === 'down') {
|
839
|
+
date = date + 5;
|
840
|
+
} else if (key === 'pageup' || key === 'pagedown') {
|
841
|
+
date += (key === 'pageup' ? -1 : 1) * ctrl.step.years;
|
842
|
+
} else if (key === 'home') {
|
843
|
+
date = getStartingYear(ctrl.activeDate.getFullYear());
|
844
|
+
} else {
|
845
|
+
if (key === 'end') {
|
846
|
+
date = getStartingYear(ctrl.activeDate.getFullYear()) + range - 1;
|
847
|
+
}
|
848
|
+
}
|
849
|
+
return ctrl.activeDate.setFullYear(date);
|
850
|
+
};
|
851
|
+
return ctrl.refreshView();
|
852
|
+
}
|
853
|
+
};
|
854
|
+
}
|
855
|
+
]).constant('datepickerPopupConfig', {
|
856
|
+
datepickerPopup: 'yyyy-MM-dd',
|
857
|
+
currentText: 'Today',
|
858
|
+
clearText: 'Clear',
|
859
|
+
closeText: 'Done',
|
860
|
+
closeOnDateSelection: true,
|
861
|
+
appendToBody: false,
|
862
|
+
showButtonBar: true
|
863
|
+
}).directive('datepickerPopup', [
|
864
|
+
'$compile', '$parse', '$document', 'dateFilter', 'dateParser', 'datepickerPopupConfig', 'dropdownService', function($compile, $parse, $document, dateFilter, dateParser, datepickerPopupConfig, dropdownService) {
|
865
|
+
return {
|
866
|
+
restrict: 'EA',
|
867
|
+
require: 'ngModel',
|
868
|
+
scope: {
|
869
|
+
isOpen: '=?',
|
870
|
+
currentText: '@',
|
871
|
+
clearText: '@',
|
872
|
+
closeText: '@',
|
873
|
+
dateDisabled: '&'
|
874
|
+
},
|
875
|
+
link: function(scope, element, attrs, ngModel) {
|
876
|
+
var $popup, appendToBody, cameltoDash, closeOnDateSelection, dateFormat, datepickerEl, parseDate, popupEl;
|
877
|
+
cameltoDash = function(string) {
|
878
|
+
return string.replace(/([A-Z])/g, function($1) {
|
879
|
+
return '-' + $1.toLowerCase();
|
880
|
+
});
|
881
|
+
};
|
882
|
+
parseDate = function(viewValue) {
|
883
|
+
var date;
|
884
|
+
if (!viewValue) {
|
885
|
+
ngModel.$setValidity('date', true);
|
886
|
+
return null;
|
887
|
+
} else if (angular.isDate(viewValue) && !isNaN(viewValue)) {
|
888
|
+
ngModel.$setValidity('date', true);
|
889
|
+
return viewValue;
|
890
|
+
} else if (angular.isString(viewValue)) {
|
891
|
+
date = dateParser.parse(viewValue, dateFormat) || new Date(viewValue);
|
892
|
+
if (isNaN(date)) {
|
893
|
+
ngModel.$setValidity('date', false);
|
894
|
+
return undefined;
|
895
|
+
} else {
|
896
|
+
ngModel.$setValidity('date', true);
|
897
|
+
return date;
|
898
|
+
}
|
899
|
+
} else {
|
900
|
+
ngModel.$setValidity('date', false);
|
901
|
+
return undefined;
|
902
|
+
}
|
903
|
+
};
|
904
|
+
dateFormat = void 0;
|
905
|
+
closeOnDateSelection = (angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection);
|
906
|
+
appendToBody = (angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody);
|
907
|
+
scope.showButtonBar = (angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar);
|
908
|
+
scope.getText = function(key) {
|
909
|
+
return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text'];
|
910
|
+
};
|
911
|
+
attrs.$observe('datepickerPopup', function(value) {
|
912
|
+
dateFormat = value || datepickerPopupConfig.datepickerPopup;
|
913
|
+
return ngModel.$render();
|
914
|
+
});
|
915
|
+
popupEl = angular.element('<div datepicker-popup-wrap><div datepicker></div></div>');
|
916
|
+
popupEl.attr({
|
917
|
+
'ng-model': 'date',
|
918
|
+
'ng-change': 'dateSelection()'
|
919
|
+
});
|
920
|
+
datepickerEl = angular.element(popupEl.children()[0]);
|
921
|
+
if (attrs.datepickerOptions) {
|
922
|
+
angular.forEach(scope.$parent.$eval(attrs.datepickerOptions), function(value, option) {
|
923
|
+
return datepickerEl.attr(cameltoDash(option), value);
|
924
|
+
});
|
925
|
+
}
|
926
|
+
scope.watchData = {};
|
927
|
+
angular.forEach(['minDate', 'maxDate', 'datepickerMode'], function(key) {
|
928
|
+
var getAttribute, setAttribute;
|
929
|
+
if (attrs[key]) {
|
930
|
+
getAttribute = $parse(attrs[key]);
|
931
|
+
scope.$parent.$watch(getAttribute, function(value) {
|
932
|
+
return scope.watchData[key] = value;
|
933
|
+
});
|
934
|
+
datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
|
935
|
+
if (key === 'datepickerMode') {
|
936
|
+
setAttribute = getAttribute.assign;
|
937
|
+
return scope.$watch('watchData.' + key, function(value, oldvalue) {
|
938
|
+
if (value !== oldvalue) {
|
939
|
+
return setAttribute(scope.$parent, value);
|
940
|
+
}
|
941
|
+
});
|
942
|
+
}
|
943
|
+
}
|
944
|
+
});
|
945
|
+
if (attrs.dateDisabled) {
|
946
|
+
datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })');
|
947
|
+
}
|
948
|
+
ngModel.$parsers.unshift(parseDate);
|
949
|
+
scope.dateSelection = function(dt) {
|
950
|
+
if (angular.isDefined(dt)) {
|
951
|
+
scope.date = dt;
|
952
|
+
}
|
953
|
+
ngModel.$setViewValue(scope.date);
|
954
|
+
ngModel.$render();
|
955
|
+
if (closeOnDateSelection) {
|
956
|
+
scope.isOpen = false;
|
957
|
+
scope.forceClose = true;
|
958
|
+
dropdownService.close(scope);
|
959
|
+
return element[0].focus();
|
960
|
+
}
|
961
|
+
};
|
962
|
+
element.bind('input change keyup', function() {
|
963
|
+
return scope.$apply(function() {
|
964
|
+
return scope.date = ngModel.$modelValue;
|
965
|
+
});
|
966
|
+
});
|
967
|
+
ngModel.$render = function() {
|
968
|
+
var date;
|
969
|
+
date = (ngModel.$viewValue ? dateFilter(ngModel.$viewValue, dateFormat) : '');
|
970
|
+
element.val(date);
|
971
|
+
return scope.date = parseDate(ngModel.$modelValue);
|
972
|
+
};
|
973
|
+
scope.select = function(date) {
|
974
|
+
var today;
|
975
|
+
if (date === 'today') {
|
976
|
+
today = new Date();
|
977
|
+
if (angular.isDate(ngModel.$modelValue)) {
|
978
|
+
date = new Date(ngModel.$modelValue);
|
979
|
+
date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());
|
980
|
+
} else {
|
981
|
+
date = new Date(today.setHours(0, 0, 0, 0));
|
982
|
+
}
|
983
|
+
}
|
984
|
+
return scope.dateSelection(date);
|
985
|
+
};
|
986
|
+
$popup = $compile(popupEl)(scope);
|
987
|
+
if (appendToBody) {
|
988
|
+
$document.find('body').append($popup);
|
989
|
+
} else {
|
990
|
+
element.after($popup);
|
991
|
+
}
|
992
|
+
return scope.$on('$destroy', function() {
|
993
|
+
return $popup.remove();
|
994
|
+
});
|
995
|
+
}
|
996
|
+
};
|
997
|
+
}
|
998
|
+
]).directive('datepickerPopupWrap', function() {
|
999
|
+
return {
|
1000
|
+
restrict: 'EA',
|
1001
|
+
replace: true,
|
1002
|
+
transclude: true,
|
1003
|
+
templateUrl: 'rolodex_angular/template/datepicker/popup',
|
1004
|
+
link: function(scope, element, attrs) {
|
1005
|
+
return element.bind('click', function(event) {
|
1006
|
+
event.preventDefault();
|
1007
|
+
return event.stopPropagation();
|
1008
|
+
});
|
1009
|
+
}
|
1010
|
+
};
|
1011
|
+
});
|
1012
|
+
|
1013
|
+
}).call(this);
|
1014
|
+
|
1015
|
+
(function() {
|
1016
|
+
angular.module('rolodex.dropdown', []).constant('dropdownConfig', {
|
1017
|
+
dropDownOpen: 'data-dropdown-open'
|
1018
|
+
}).service('dropdownService', [
|
1019
|
+
'$document', '$timeout', function($document, $timeout) {
|
1020
|
+
var closeDropdown, escapeKeyBind, openScope;
|
1021
|
+
openScope = null;
|
1022
|
+
this.open = function(dropdownScope) {
|
1023
|
+
if (!openScope) {
|
1024
|
+
$document.bind('click', closeDropdown);
|
1025
|
+
$document.bind('keydown', escapeKeyBind);
|
1026
|
+
}
|
1027
|
+
if (openScope && openScope !== dropdownScope) {
|
1028
|
+
openScope.isOpen = false;
|
1029
|
+
}
|
1030
|
+
return openScope = dropdownScope;
|
1031
|
+
};
|
1032
|
+
this.close = function(dropdownScope) {
|
1033
|
+
if (openScope === dropdownScope || dropdownScope.forceClose) {
|
1034
|
+
if (dropdownScope.forceClose) {
|
1035
|
+
if (openScope) {
|
1036
|
+
openScope.isOpen = false;
|
1037
|
+
}
|
1038
|
+
$timeout(function() {
|
1039
|
+
return dropdownScope.isOpen = false;
|
1040
|
+
});
|
1041
|
+
}
|
1042
|
+
openScope = null;
|
1043
|
+
$document.unbind('click', closeDropdown);
|
1044
|
+
return $document.unbind('keydown', escapeKeyBind);
|
1045
|
+
}
|
1046
|
+
};
|
1047
|
+
closeDropdown = function(evt) {
|
1048
|
+
var toggleElement;
|
1049
|
+
toggleElement = openScope.getToggleElement();
|
1050
|
+
if (evt && (toggleElement != null ? toggleElement[0].contains(evt.target) : void 0) || (evt != null ? evt.target.nodeName.toLowerCase() : void 0) === 'input') {
|
1051
|
+
return;
|
1052
|
+
}
|
1053
|
+
return openScope.$apply(function() {
|
1054
|
+
return openScope.isOpen = false;
|
1055
|
+
});
|
1056
|
+
};
|
1057
|
+
escapeKeyBind = function(evt) {
|
1058
|
+
if (evt.which === 27) {
|
1059
|
+
openScope.focusToggleElement();
|
1060
|
+
return closeDropdown();
|
1061
|
+
}
|
1062
|
+
};
|
1063
|
+
return this;
|
1064
|
+
}
|
1065
|
+
]).controller('DropdownController', [
|
1066
|
+
'$scope', '$attrs', '$parse', 'dropdownConfig', 'dropdownService', '$animate', function($scope, $attrs, $parse, dropdownConfig, dropdownService, $animate) {
|
1067
|
+
var dropDownOpen, getIsOpen, scope, self, setIsOpen, toggleInvoker;
|
1068
|
+
self = this;
|
1069
|
+
scope = $scope.$new();
|
1070
|
+
dropDownOpen = dropdownConfig.dropDownOpen;
|
1071
|
+
getIsOpen = void 0;
|
1072
|
+
setIsOpen = angular.noop;
|
1073
|
+
toggleInvoker = ($attrs.onToggle ? $parse($attrs.onToggle) : angular.noop);
|
1074
|
+
this.init = function(element) {
|
1075
|
+
self.$element = element;
|
1076
|
+
if ($attrs.isOpen) {
|
1077
|
+
getIsOpen = $parse($attrs.isOpen);
|
1078
|
+
setIsOpen = getIsOpen.assign;
|
1079
|
+
return $scope.$watch(getIsOpen, function(value) {
|
1080
|
+
return scope.isOpen = !!value;
|
1081
|
+
});
|
1082
|
+
}
|
1083
|
+
};
|
1084
|
+
this.toggle = function(open) {
|
1085
|
+
return scope.isOpen = (arguments.length ? !!open : !scope.isOpen);
|
1086
|
+
};
|
1087
|
+
this.isOpen = function() {
|
1088
|
+
return scope.isOpen;
|
1089
|
+
};
|
1090
|
+
scope.getToggleElement = function() {
|
1091
|
+
return self.toggleElement;
|
1092
|
+
};
|
1093
|
+
scope.focusToggleElement = function() {
|
1094
|
+
if (self.toggleElement) {
|
1095
|
+
self.toggleElement[0].focus();
|
1096
|
+
}
|
1097
|
+
};
|
1098
|
+
scope.$watch('isOpen', function(isOpen, wasOpen) {
|
1099
|
+
var anchors;
|
1100
|
+
anchors = self.$element.find('a');
|
1101
|
+
if (isOpen) {
|
1102
|
+
self.$element.attr(dropDownOpen, '');
|
1103
|
+
scope.focusToggleElement();
|
1104
|
+
dropdownService.open(scope);
|
1105
|
+
if (anchors.length) {
|
1106
|
+
scope.forceClose = true;
|
1107
|
+
_.each(anchors, function(anchor) {
|
1108
|
+
return angular.element(anchor).on('click', function(evt) {
|
1109
|
+
dropdownService.close(scope);
|
1110
|
+
return scope.$digest();
|
1111
|
+
});
|
1112
|
+
});
|
1113
|
+
}
|
1114
|
+
} else {
|
1115
|
+
self.$element.removeAttr(dropDownOpen);
|
1116
|
+
dropdownService.close(scope);
|
1117
|
+
}
|
1118
|
+
setIsOpen($scope, isOpen);
|
1119
|
+
if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
|
1120
|
+
return toggleInvoker($scope, {
|
1121
|
+
open: !!isOpen
|
1122
|
+
});
|
1123
|
+
}
|
1124
|
+
});
|
1125
|
+
$scope.$on('$locationChangeSuccess', function() {
|
1126
|
+
return scope.isOpen = false;
|
1127
|
+
});
|
1128
|
+
$scope.$on('$destroy', function() {
|
1129
|
+
return scope.$destroy();
|
1130
|
+
});
|
1131
|
+
return this;
|
1132
|
+
}
|
1133
|
+
]).directive('dropdown', function() {
|
1134
|
+
return {
|
1135
|
+
controller: 'DropdownController',
|
1136
|
+
link: function(scope, element, attrs, dropdownCtrl) {
|
1137
|
+
return dropdownCtrl.init(element);
|
1138
|
+
}
|
1139
|
+
};
|
1140
|
+
}).directive('dropdownToggle', function() {
|
1141
|
+
return {
|
1142
|
+
require: '?^dropdown',
|
1143
|
+
link: function(scope, element, attrs, dropdownCtrl) {
|
1144
|
+
var toggleDropdown;
|
1145
|
+
if (!dropdownCtrl) {
|
1146
|
+
return;
|
1147
|
+
}
|
1148
|
+
dropdownCtrl.toggleElement = element;
|
1149
|
+
toggleDropdown = function(event) {
|
1150
|
+
event.preventDefault();
|
1151
|
+
if (!element.hasClass('disabled') && !attrs.disabled) {
|
1152
|
+
return scope.$apply(function() {
|
1153
|
+
return dropdownCtrl.toggle();
|
1154
|
+
});
|
1155
|
+
}
|
1156
|
+
};
|
1157
|
+
element.bind('click', toggleDropdown);
|
1158
|
+
element.attr({
|
1159
|
+
'aria-haspopup': true,
|
1160
|
+
'aria-expanded': false
|
1161
|
+
});
|
1162
|
+
scope.$watch(dropdownCtrl.isOpen, function(isOpen) {
|
1163
|
+
return element.attr('aria-expanded', !!isOpen);
|
1164
|
+
});
|
1165
|
+
return scope.$on('$destroy', function() {
|
1166
|
+
return element.unbind('click', toggleDropdown);
|
1167
|
+
});
|
1168
|
+
}
|
1169
|
+
};
|
1170
|
+
});
|
1171
|
+
|
1172
|
+
}).call(this);
|
1173
|
+
|
1174
|
+
(function() {
|
1175
|
+
angular.module("rolodex.modal", ["rolodex.transition", "templates"]).factory("$$stackedMap", function() {
|
1176
|
+
return {
|
1177
|
+
createNew: function() {
|
1178
|
+
var stack;
|
1179
|
+
stack = [];
|
1180
|
+
return {
|
1181
|
+
add: function(key, value) {
|
1182
|
+
return stack.push({
|
1183
|
+
key: key,
|
1184
|
+
value: value
|
1185
|
+
});
|
1186
|
+
},
|
1187
|
+
get: function(key) {
|
1188
|
+
var i;
|
1189
|
+
i = 0;
|
1190
|
+
while (i < stack.length) {
|
1191
|
+
if (key === stack[i].key) {
|
1192
|
+
return stack[i];
|
1193
|
+
}
|
1194
|
+
i++;
|
1195
|
+
}
|
1196
|
+
},
|
1197
|
+
keys: function() {
|
1198
|
+
var i, keys;
|
1199
|
+
keys = [];
|
1200
|
+
i = 0;
|
1201
|
+
while (i < stack.length) {
|
1202
|
+
keys.push(stack[i].key);
|
1203
|
+
i++;
|
1204
|
+
}
|
1205
|
+
return keys;
|
1206
|
+
},
|
1207
|
+
top: function() {
|
1208
|
+
return stack[stack.length - 1];
|
1209
|
+
},
|
1210
|
+
remove: function(key) {
|
1211
|
+
var i, idx;
|
1212
|
+
idx = -1;
|
1213
|
+
i = 0;
|
1214
|
+
while (i < stack.length) {
|
1215
|
+
if (key === stack[i].key) {
|
1216
|
+
idx = i;
|
1217
|
+
break;
|
1218
|
+
}
|
1219
|
+
i++;
|
1220
|
+
}
|
1221
|
+
return stack.splice(idx, 1)[0];
|
1222
|
+
},
|
1223
|
+
removeTop: function() {
|
1224
|
+
return stack.splice(stack.length - 1, 1)[0];
|
1225
|
+
},
|
1226
|
+
length: function() {
|
1227
|
+
return stack.length;
|
1228
|
+
}
|
1229
|
+
};
|
1230
|
+
}
|
1231
|
+
};
|
1232
|
+
}).directive("modalWindow", [
|
1233
|
+
"$modalStack", "$timeout", function($modalStack, $timeout) {
|
1234
|
+
return {
|
1235
|
+
restrict: "EA",
|
1236
|
+
scope: {
|
1237
|
+
index: "@",
|
1238
|
+
animate: "="
|
1239
|
+
},
|
1240
|
+
replace: true,
|
1241
|
+
transclude: true,
|
1242
|
+
templateUrl: function(tElement, tAttrs) {
|
1243
|
+
return tAttrs.templateUrl || "rolodex_angular/template/modal/window";
|
1244
|
+
},
|
1245
|
+
link: function(scope, element, attrs) {
|
1246
|
+
element.addClass(attrs.windowClass || "");
|
1247
|
+
scope.size = attrs.size;
|
1248
|
+
$timeout(function() {
|
1249
|
+
scope.animate = true;
|
1250
|
+
if (!element[0].querySelectorAll("[autofocus]").length) {
|
1251
|
+
element[0].focus();
|
1252
|
+
}
|
1253
|
+
});
|
1254
|
+
return scope.close = function(evt) {
|
1255
|
+
var modal;
|
1256
|
+
modal = $modalStack.getTop();
|
1257
|
+
if (modal && modal.value.backdrop && modal.value.backdrop !== "static" && (evt.target === evt.currentTarget)) {
|
1258
|
+
evt.preventDefault();
|
1259
|
+
evt.stopPropagation();
|
1260
|
+
return $modalStack.dismiss(modal.key, "backdrop click");
|
1261
|
+
}
|
1262
|
+
};
|
1263
|
+
}
|
1264
|
+
};
|
1265
|
+
}
|
1266
|
+
]).directive("modalTransclude", function() {
|
1267
|
+
return {
|
1268
|
+
link: function($scope, $element, $attrs, controller, $transclude) {
|
1269
|
+
return $transclude($scope.$parent, function(clone) {
|
1270
|
+
$element.empty();
|
1271
|
+
return $element.append(clone);
|
1272
|
+
});
|
1273
|
+
}
|
1274
|
+
};
|
1275
|
+
}).factory("$modalStack", [
|
1276
|
+
"$transition", "$timeout", "$document", "$compile", "$rootScope", "$$stackedMap", function($transition, $timeout, $document, $compile, $rootScope, $$stackedMap) {
|
1277
|
+
var $modalStack, OPENED_MODAL_CLASS, backdropDomEl, backdropIndex, backdropScope, checkRemoveBackdrop, openedWindows, removeAfterAnimate, removeModalWindow;
|
1278
|
+
backdropIndex = function() {
|
1279
|
+
var i, opened, topBackdropIndex;
|
1280
|
+
topBackdropIndex = -1;
|
1281
|
+
opened = openedWindows.keys();
|
1282
|
+
i = 0;
|
1283
|
+
while (i < opened.length) {
|
1284
|
+
if (openedWindows.get(opened[i]).value.backdrop) {
|
1285
|
+
topBackdropIndex = i;
|
1286
|
+
}
|
1287
|
+
i++;
|
1288
|
+
}
|
1289
|
+
return topBackdropIndex;
|
1290
|
+
};
|
1291
|
+
removeModalWindow = function(modalInstance) {
|
1292
|
+
var body, modalWindow;
|
1293
|
+
body = $document.find("body").eq(0);
|
1294
|
+
modalWindow = openedWindows.get(modalInstance).value;
|
1295
|
+
openedWindows.remove(modalInstance);
|
1296
|
+
removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, 300, function() {
|
1297
|
+
modalWindow.modalScope.$destroy();
|
1298
|
+
body.toggleClass(OPENED_MODAL_CLASS, openedWindows.length() > 0);
|
1299
|
+
checkRemoveBackdrop();
|
1300
|
+
});
|
1301
|
+
};
|
1302
|
+
checkRemoveBackdrop = function() {
|
1303
|
+
var backdropDomEl, backdropScope, backdropScopeRef;
|
1304
|
+
if (backdropDomEl && backdropIndex() === -1) {
|
1305
|
+
backdropScopeRef = backdropScope;
|
1306
|
+
removeAfterAnimate(backdropDomEl, backdropScope, 150, function() {
|
1307
|
+
backdropScopeRef.$destroy();
|
1308
|
+
backdropScopeRef = null;
|
1309
|
+
});
|
1310
|
+
backdropDomEl = undefined;
|
1311
|
+
backdropScope = undefined;
|
1312
|
+
}
|
1313
|
+
};
|
1314
|
+
removeAfterAnimate = function(domEl, scope, emulateTime, done) {
|
1315
|
+
var afterAnimating, timeout, transitionEndEventName;
|
1316
|
+
afterAnimating = function() {
|
1317
|
+
if (afterAnimating.done) {
|
1318
|
+
return;
|
1319
|
+
}
|
1320
|
+
afterAnimating.done = true;
|
1321
|
+
domEl.remove();
|
1322
|
+
if (done) {
|
1323
|
+
done();
|
1324
|
+
}
|
1325
|
+
};
|
1326
|
+
scope.animate = false;
|
1327
|
+
transitionEndEventName = $transition.transitionEndEventName;
|
1328
|
+
if (transitionEndEventName) {
|
1329
|
+
timeout = $timeout(afterAnimating, emulateTime);
|
1330
|
+
domEl.bind(transitionEndEventName, function() {
|
1331
|
+
$timeout.cancel(timeout);
|
1332
|
+
afterAnimating();
|
1333
|
+
scope.$apply();
|
1334
|
+
});
|
1335
|
+
} else {
|
1336
|
+
$timeout(afterAnimating);
|
1337
|
+
}
|
1338
|
+
};
|
1339
|
+
OPENED_MODAL_CLASS = "modal-open";
|
1340
|
+
backdropDomEl = void 0;
|
1341
|
+
backdropScope = void 0;
|
1342
|
+
openedWindows = $$stackedMap.createNew();
|
1343
|
+
$modalStack = {};
|
1344
|
+
$rootScope.$watch(backdropIndex, function(newBackdropIndex) {
|
1345
|
+
if (backdropScope) {
|
1346
|
+
backdropScope.index = newBackdropIndex;
|
1347
|
+
}
|
1348
|
+
});
|
1349
|
+
$document.bind("keydown", function(evt) {
|
1350
|
+
var modal;
|
1351
|
+
modal = void 0;
|
1352
|
+
if (evt.which === 27) {
|
1353
|
+
modal = openedWindows.top();
|
1354
|
+
if (modal && modal.value.keyboard) {
|
1355
|
+
evt.preventDefault();
|
1356
|
+
$rootScope.$apply(function() {
|
1357
|
+
$modalStack.dismiss(modal.key, "escape key press");
|
1358
|
+
});
|
1359
|
+
}
|
1360
|
+
}
|
1361
|
+
});
|
1362
|
+
$modalStack.open = function(modalInstance, modal) {
|
1363
|
+
var angularBackgroundDomEl, angularDomEl, body, currBackdropIndex, modalDomEl;
|
1364
|
+
openedWindows.add(modalInstance, {
|
1365
|
+
deferred: modal.deferred,
|
1366
|
+
modalScope: modal.scope,
|
1367
|
+
backdrop: modal.backdrop,
|
1368
|
+
keyboard: modal.keyboard
|
1369
|
+
});
|
1370
|
+
body = $document.find("body").eq(0);
|
1371
|
+
currBackdropIndex = backdropIndex();
|
1372
|
+
if (currBackdropIndex >= 0 && !backdropDomEl) {
|
1373
|
+
backdropScope = $rootScope.$new(true);
|
1374
|
+
backdropScope.index = currBackdropIndex;
|
1375
|
+
angularBackgroundDomEl = angular.element("<div modal-backdrop></div>");
|
1376
|
+
angularBackgroundDomEl.attr("backdrop-class", modal.backdropClass);
|
1377
|
+
backdropDomEl = $compile(angularBackgroundDomEl)(backdropScope);
|
1378
|
+
body.append(backdropDomEl);
|
1379
|
+
}
|
1380
|
+
angularDomEl = angular.element("<div modal-window></div>");
|
1381
|
+
angularDomEl.attr({
|
1382
|
+
"template-url": modal.windowTemplateUrl,
|
1383
|
+
"window-class": modal.windowClass,
|
1384
|
+
size: modal.size,
|
1385
|
+
index: openedWindows.length() - 1,
|
1386
|
+
animate: "animate"
|
1387
|
+
}).html(modal.content);
|
1388
|
+
modalDomEl = $compile(angularDomEl)(modal.scope);
|
1389
|
+
openedWindows.top().value.modalDomEl = modalDomEl;
|
1390
|
+
body.append(modalDomEl);
|
1391
|
+
body.addClass(OPENED_MODAL_CLASS);
|
1392
|
+
};
|
1393
|
+
$modalStack.close = function(modalInstance, result) {
|
1394
|
+
var modalWindow;
|
1395
|
+
modalWindow = openedWindows.get(modalInstance);
|
1396
|
+
if (modalWindow) {
|
1397
|
+
modalWindow.value.deferred.resolve(result);
|
1398
|
+
removeModalWindow(modalInstance);
|
1399
|
+
}
|
1400
|
+
};
|
1401
|
+
$modalStack.dismiss = function(modalInstance, reason) {
|
1402
|
+
var modalWindow;
|
1403
|
+
modalWindow = openedWindows.get(modalInstance);
|
1404
|
+
if (modalWindow) {
|
1405
|
+
modalWindow.value.deferred.reject(reason);
|
1406
|
+
removeModalWindow(modalInstance);
|
1407
|
+
}
|
1408
|
+
};
|
1409
|
+
$modalStack.dismissAll = function(reason) {
|
1410
|
+
var topModal;
|
1411
|
+
topModal = this.getTop();
|
1412
|
+
while (topModal) {
|
1413
|
+
this.dismiss(topModal.key, reason);
|
1414
|
+
topModal = this.getTop();
|
1415
|
+
}
|
1416
|
+
};
|
1417
|
+
$modalStack.getTop = function() {
|
1418
|
+
return openedWindows.top();
|
1419
|
+
};
|
1420
|
+
return $modalStack;
|
1421
|
+
}
|
1422
|
+
]).provider("$modal", function() {
|
1423
|
+
var $modalProvider;
|
1424
|
+
$modalProvider = {
|
1425
|
+
options: {
|
1426
|
+
backdrop: true,
|
1427
|
+
keyboard: true
|
1428
|
+
},
|
1429
|
+
$get: [
|
1430
|
+
"$injector", "$rootScope", "$q", "$http", "$templateCache", "$controller", "$modalStack", function($injector, $rootScope, $q, $http, $templateCache, $controller, $modalStack) {
|
1431
|
+
var $modal, getResolvePromises, getTemplatePromise;
|
1432
|
+
getTemplatePromise = function(options) {
|
1433
|
+
if (options.template) {
|
1434
|
+
return $q.when(options.template);
|
1435
|
+
} else {
|
1436
|
+
return $http.get((angular.isFunction(options.templateUrl) ? options.templateUrl() : options.templateUrl), {
|
1437
|
+
cache: $templateCache
|
1438
|
+
}).then(function(result) {
|
1439
|
+
return result.data;
|
1440
|
+
});
|
1441
|
+
}
|
1442
|
+
};
|
1443
|
+
getResolvePromises = function(resolves) {
|
1444
|
+
var promisesArr;
|
1445
|
+
promisesArr = [];
|
1446
|
+
angular.forEach(resolves, function(value) {
|
1447
|
+
if (angular.isFunction(value) || angular.isArray(value)) {
|
1448
|
+
promisesArr.push($q.when($injector.invoke(value)));
|
1449
|
+
}
|
1450
|
+
});
|
1451
|
+
return promisesArr;
|
1452
|
+
};
|
1453
|
+
$modal = {};
|
1454
|
+
$modal.open = function(modalOptions) {
|
1455
|
+
var modalInstance, modalOpenedDeferred, modalResultDeferred, resolveError, resolveSuccess, templateAndResolvePromise;
|
1456
|
+
modalResultDeferred = $q.defer();
|
1457
|
+
modalOpenedDeferred = $q.defer();
|
1458
|
+
modalInstance = {
|
1459
|
+
result: modalResultDeferred.promise,
|
1460
|
+
opened: modalOpenedDeferred.promise,
|
1461
|
+
close: function(result) {
|
1462
|
+
$modalStack.close(modalInstance, result);
|
1463
|
+
},
|
1464
|
+
dismiss: function(reason) {
|
1465
|
+
$modalStack.dismiss(modalInstance, reason);
|
1466
|
+
}
|
1467
|
+
};
|
1468
|
+
modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
|
1469
|
+
modalOptions.resolve = modalOptions.resolve || {};
|
1470
|
+
if (!modalOptions.template && !modalOptions.templateUrl) {
|
1471
|
+
throw new Error("One of template or templateUrl options is required.");
|
1472
|
+
}
|
1473
|
+
templateAndResolvePromise = $q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve)));
|
1474
|
+
templateAndResolvePromise.then((resolveSuccess = function(tplAndVars) {
|
1475
|
+
var ctrlInstance, ctrlLocals, modalScope, resolveIter;
|
1476
|
+
modalScope = (modalOptions.scope || $rootScope).$new();
|
1477
|
+
modalScope.$close = modalInstance.close;
|
1478
|
+
modalScope.$dismiss = modalInstance.dismiss;
|
1479
|
+
ctrlInstance = void 0;
|
1480
|
+
ctrlLocals = {};
|
1481
|
+
resolveIter = 1;
|
1482
|
+
if (modalOptions.controller) {
|
1483
|
+
ctrlLocals.$scope = modalScope;
|
1484
|
+
ctrlLocals.$modalInstance = modalInstance;
|
1485
|
+
angular.forEach(modalOptions.resolve, function(value, key) {
|
1486
|
+
ctrlLocals[key] = tplAndVars[resolveIter++];
|
1487
|
+
});
|
1488
|
+
ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
|
1489
|
+
if (modalOptions.controller) {
|
1490
|
+
modalScope[modalOptions.controllerAs] = ctrlInstance;
|
1491
|
+
}
|
1492
|
+
}
|
1493
|
+
return $modalStack.open(modalInstance, {
|
1494
|
+
scope: modalScope,
|
1495
|
+
deferred: modalResultDeferred,
|
1496
|
+
content: tplAndVars[0],
|
1497
|
+
backdrop: modalOptions.backdrop,
|
1498
|
+
keyboard: modalOptions.keyboard,
|
1499
|
+
backdropClass: modalOptions.backdropClass,
|
1500
|
+
windowClass: modalOptions.windowClass,
|
1501
|
+
windowTemplateUrl: modalOptions.windowTemplateUrl,
|
1502
|
+
size: modalOptions.size
|
1503
|
+
});
|
1504
|
+
}), resolveError = function(reason) {
|
1505
|
+
return modalResultDeferred.reject(reason);
|
1506
|
+
});
|
1507
|
+
templateAndResolvePromise.then((function() {
|
1508
|
+
return modalOpenedDeferred.resolve(true);
|
1509
|
+
}), function() {
|
1510
|
+
return modalOpenedDeferred.reject(false);
|
1511
|
+
});
|
1512
|
+
return modalInstance;
|
1513
|
+
};
|
1514
|
+
return $modal;
|
1515
|
+
}
|
1516
|
+
]
|
1517
|
+
};
|
1518
|
+
return $modalProvider;
|
1519
|
+
});
|
1520
|
+
|
1521
|
+
}).call(this);
|
1522
|
+
|
1523
|
+
|
1524
|
+
/**
|
1525
|
+
A set of utility methods that can be use to retrieve position of DOM elements.
|
1526
|
+
It is meant to be used where we need to absolute-position DOM elements in
|
1527
|
+
relation to other, existing elements (this is the case for tooltips, popovers,
|
1528
|
+
typeahead suggestions etc.).
|
1529
|
+
*/
|
1530
|
+
|
1531
|
+
(function() {
|
1532
|
+
angular.module('rolodex.position', []).factory('$position', [
|
1533
|
+
'$document', '$window', function($document, $window) {
|
1534
|
+
var getStyle, isStaticPositioned, parentOffsetEl;
|
1535
|
+
getStyle = function(el, cssprop) {
|
1536
|
+
if (el.currentStyle) {
|
1537
|
+
return el.currentStyle[cssprop];
|
1538
|
+
} else {
|
1539
|
+
if ($window.getComputedStyle) {
|
1540
|
+
return $window.getComputedStyle(el)[cssprop];
|
1541
|
+
}
|
1542
|
+
}
|
1543
|
+
return el.style[cssprop];
|
1544
|
+
};
|
1545
|
+
|
1546
|
+
/**
|
1547
|
+
Checks if a given element is statically positioned
|
1548
|
+
@param element - raw DOM element
|
1549
|
+
*/
|
1550
|
+
isStaticPositioned = function(element) {
|
1551
|
+
return (getStyle(element, 'position') || 'static') === 'static';
|
1552
|
+
};
|
1553
|
+
|
1554
|
+
/**
|
1555
|
+
returns the closest, non-statically positioned parentOffset of a given element
|
1556
|
+
@param element
|
1557
|
+
*/
|
1558
|
+
parentOffsetEl = function(element) {
|
1559
|
+
var docDomEl, offsetParent;
|
1560
|
+
docDomEl = $document[0];
|
1561
|
+
offsetParent = element.offsetParent || docDomEl;
|
1562
|
+
while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent)) {
|
1563
|
+
offsetParent = offsetParent.offsetParent;
|
1564
|
+
}
|
1565
|
+
return offsetParent || docDomEl;
|
1566
|
+
};
|
1567
|
+
return {
|
1568
|
+
|
1569
|
+
/**
|
1570
|
+
Provides read-only equivalent of jQuery's position function:
|
1571
|
+
http://api.jquery.com/position/
|
1572
|
+
*/
|
1573
|
+
position: function(element) {
|
1574
|
+
var boundingClientRect, elBCR, offsetParentBCR, offsetParentEl;
|
1575
|
+
elBCR = this.offset(element);
|
1576
|
+
offsetParentBCR = {
|
1577
|
+
top: 0,
|
1578
|
+
left: 0
|
1579
|
+
};
|
1580
|
+
offsetParentEl = parentOffsetEl(element[0]);
|
1581
|
+
if (offsetParentEl !== $document[0]) {
|
1582
|
+
offsetParentBCR = this.offset(angular.element(offsetParentEl));
|
1583
|
+
offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
|
1584
|
+
offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
|
1585
|
+
}
|
1586
|
+
boundingClientRect = element[0].getBoundingClientRect();
|
1587
|
+
return {
|
1588
|
+
width: boundingClientRect.width || element.prop('offsetWidth'),
|
1589
|
+
height: boundingClientRect.height || element.prop('offsetHeight'),
|
1590
|
+
top: elBCR.top - offsetParentBCR.top,
|
1591
|
+
left: elBCR.left - offsetParentBCR.left
|
1592
|
+
};
|
1593
|
+
},
|
1594
|
+
|
1595
|
+
/**
|
1596
|
+
Provides read-only equivalent of jQuery's offset function:
|
1597
|
+
http://api.jquery.com/offset/
|
1598
|
+
*/
|
1599
|
+
offset: function(element) {
|
1600
|
+
var boundingClientRect;
|
1601
|
+
boundingClientRect = element[0].getBoundingClientRect();
|
1602
|
+
return {
|
1603
|
+
width: boundingClientRect.width || element.prop('offsetWidth'),
|
1604
|
+
height: boundingClientRect.height || element.prop('offsetHeight'),
|
1605
|
+
top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop),
|
1606
|
+
left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)
|
1607
|
+
};
|
1608
|
+
},
|
1609
|
+
|
1610
|
+
/**
|
1611
|
+
Provides coordinates for the targetEl in relation to hostEl
|
1612
|
+
*/
|
1613
|
+
positionElements: function(hostEl, targetEl, positionStr, appendToBody) {
|
1614
|
+
var hostElPos, pos0, pos1, positionStrParts, shiftHeight, shiftWidth, targetElHeight, targetElPos, targetElWidth;
|
1615
|
+
positionStrParts = positionStr.split('-');
|
1616
|
+
pos0 = positionStrParts[0];
|
1617
|
+
pos1 = positionStrParts[1] || 'center';
|
1618
|
+
hostElPos = void 0;
|
1619
|
+
targetElWidth = void 0;
|
1620
|
+
targetElHeight = void 0;
|
1621
|
+
targetElPos = void 0;
|
1622
|
+
hostElPos = (appendToBody ? this.offset(hostEl) : this.position(hostEl));
|
1623
|
+
targetElWidth = targetEl.prop('offsetWidth');
|
1624
|
+
targetElHeight = targetEl.prop('offsetHeight');
|
1625
|
+
shiftWidth = {
|
1626
|
+
center: function() {
|
1627
|
+
return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
|
1628
|
+
},
|
1629
|
+
left: function() {
|
1630
|
+
return hostElPos.left;
|
1631
|
+
},
|
1632
|
+
right: function() {
|
1633
|
+
return hostElPos.left + hostElPos.width;
|
1634
|
+
}
|
1635
|
+
};
|
1636
|
+
shiftHeight = {
|
1637
|
+
center: function() {
|
1638
|
+
return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
|
1639
|
+
},
|
1640
|
+
top: function() {
|
1641
|
+
return hostElPos.top;
|
1642
|
+
},
|
1643
|
+
bottom: function() {
|
1644
|
+
return hostElPos.top + hostElPos.height;
|
1645
|
+
}
|
1646
|
+
};
|
1647
|
+
switch (pos0) {
|
1648
|
+
case 'right':
|
1649
|
+
targetElPos = {
|
1650
|
+
top: shiftHeight[pos1](),
|
1651
|
+
left: shiftWidth[pos0]()
|
1652
|
+
};
|
1653
|
+
break;
|
1654
|
+
case 'left':
|
1655
|
+
targetElPos = {
|
1656
|
+
top: shiftHeight[pos1](),
|
1657
|
+
left: hostElPos.left - targetElWidth
|
1658
|
+
};
|
1659
|
+
break;
|
1660
|
+
case 'bottom':
|
1661
|
+
targetElPos = {
|
1662
|
+
top: shiftHeight[pos0](),
|
1663
|
+
left: shiftWidth[pos1]()
|
1664
|
+
};
|
1665
|
+
break;
|
1666
|
+
default:
|
1667
|
+
targetElPos = {
|
1668
|
+
top: hostElPos.top - targetElHeight,
|
1669
|
+
left: shiftWidth[pos1]()
|
1670
|
+
};
|
1671
|
+
}
|
1672
|
+
return targetElPos;
|
1673
|
+
}
|
1674
|
+
};
|
1675
|
+
}
|
1676
|
+
]);
|
1677
|
+
|
1678
|
+
}).call(this);
|
1679
|
+
|
1680
|
+
(function() {
|
1681
|
+
angular.module("rolodex.transition", []).factory("$transition", [
|
1682
|
+
"$q", "$timeout", "$rootScope", function($q, $timeout, $rootScope) {
|
1683
|
+
var $transition, animationEndEventNames, findEndEventName, transElement, transitionEndEventNames;
|
1684
|
+
findEndEventName = function(endEventNames) {
|
1685
|
+
var name;
|
1686
|
+
for (name in endEventNames) {
|
1687
|
+
if (transElement.style[name] !== undefined) {
|
1688
|
+
return endEventNames[name];
|
1689
|
+
}
|
1690
|
+
}
|
1691
|
+
};
|
1692
|
+
$transition = function(element, trigger, options) {
|
1693
|
+
var deferred, endEventName, transitionEndHandler;
|
1694
|
+
options = options || {};
|
1695
|
+
deferred = $q.defer();
|
1696
|
+
endEventName = $transition[(options.animation ? "animationEndEventName" : "transitionEndEventName")];
|
1697
|
+
transitionEndHandler = function(event) {
|
1698
|
+
$rootScope.$apply(function() {
|
1699
|
+
element.unbind(endEventName, transitionEndHandler);
|
1700
|
+
deferred.resolve(element);
|
1701
|
+
});
|
1702
|
+
};
|
1703
|
+
if (endEventName) {
|
1704
|
+
element.bind(endEventName, transitionEndHandler);
|
1705
|
+
}
|
1706
|
+
$timeout(function() {
|
1707
|
+
if (angular.isString(trigger)) {
|
1708
|
+
element.addClass(trigger);
|
1709
|
+
} else if (angular.isFunction(trigger)) {
|
1710
|
+
trigger(element);
|
1711
|
+
} else {
|
1712
|
+
if (angular.isObject(trigger)) {
|
1713
|
+
element.css(trigger);
|
1714
|
+
}
|
1715
|
+
}
|
1716
|
+
if (!endEventName) {
|
1717
|
+
deferred.resolve(element);
|
1718
|
+
}
|
1719
|
+
});
|
1720
|
+
deferred.promise.cancel = function() {
|
1721
|
+
if (endEventName) {
|
1722
|
+
element.unbind(endEventName, transitionEndHandler);
|
1723
|
+
}
|
1724
|
+
deferred.reject("Transition cancelled");
|
1725
|
+
};
|
1726
|
+
return deferred.promise;
|
1727
|
+
};
|
1728
|
+
transElement = document.createElement("trans");
|
1729
|
+
transitionEndEventNames = {
|
1730
|
+
WebkitTransition: "webkitTransitionEnd",
|
1731
|
+
MozTransition: "transitionend",
|
1732
|
+
OTransition: "oTransitionEnd",
|
1733
|
+
transition: "transitionend"
|
1734
|
+
};
|
1735
|
+
animationEndEventNames = {
|
1736
|
+
WebkitTransition: "webkitAnimationEnd",
|
1737
|
+
MozTransition: "animationend",
|
1738
|
+
OTransition: "oAnimationEnd",
|
1739
|
+
transition: "animationend"
|
1740
|
+
};
|
1741
|
+
$transition.transitionEndEventName = findEndEventName(transitionEndEventNames);
|
1742
|
+
$transition.animationEndEventName = findEndEventName(animationEndEventNames);
|
1743
|
+
return $transition;
|
1744
|
+
}
|
1745
|
+
]);
|
1746
|
+
|
1747
|
+
}).call(this);
|