angular-ui-rails 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
data/lib/angular-ui-rails.rb
CHANGED
@@ -3,6 +3,12 @@ require "angular-ui-rails/version"
|
|
3
3
|
module AngularUI
|
4
4
|
module Rails
|
5
5
|
class Engine < ::Rails::Engine
|
6
|
+
# Enabling assets precompiling under rails 3.1
|
7
|
+
if Rails.version >= '3.1'
|
8
|
+
initializer :assets do |config|
|
9
|
+
Rails.application.config.assets.precompile += %w( angular-ui-ieshiv.js )
|
10
|
+
end
|
11
|
+
end
|
6
12
|
end
|
7
13
|
end
|
8
|
-
end
|
14
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
/**
|
2
2
|
* AngularUI - The companion suite for AngularJS
|
3
|
-
* @version v0.
|
3
|
+
* @version v0.4.0 - 2013-02-15
|
4
4
|
* @link http://angular-ui.github.com
|
5
5
|
* @license MIT License, http://www.opensource.org/licenses/MIT
|
6
6
|
*/
|
@@ -31,21 +31,7 @@
|
|
31
31
|
window.myCustomTags = window.myCustomTags || []; // externally defined by developer using angular-ui directives
|
32
32
|
tags.push.apply(tags, window.myCustomTags);
|
33
33
|
|
34
|
-
var
|
35
|
-
// Returns the version of Internet Explorer or a -1
|
36
|
-
// (indicating the use of another browser).
|
37
|
-
var rv = -1; // Return value assumes failure.
|
38
|
-
if (navigator.appName == 'Microsoft Internet Explorer') {
|
39
|
-
var ua = navigator.userAgent;
|
40
|
-
var re = new RegExp("MSIE ([0-9]{1,}[\\.0-9]{0,})");
|
41
|
-
if (re.exec(ua) !== null) {
|
42
|
-
rv = parseFloat(RegExp.$1);
|
43
|
-
}
|
44
|
-
}
|
45
|
-
return rv;
|
46
|
-
};
|
47
|
-
|
48
|
-
var toCustomElements = function (str, delim) {
|
34
|
+
var toCustomElements = function (str) {
|
49
35
|
var result = [];
|
50
36
|
var dashed = str.replace(/([A-Z])/g, function ($1) {
|
51
37
|
return " " + $1.toLowerCase();
|
@@ -62,20 +48,12 @@
|
|
62
48
|
return result;
|
63
49
|
};
|
64
50
|
|
65
|
-
var
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
document.createElement(customElement);
|
71
|
-
}
|
51
|
+
for (var i = 0, tlen = tags.length; i < tlen; i++) {
|
52
|
+
var customElements = toCustomElements(tags[i]);
|
53
|
+
for (var j = 0, clen = customElements.length; j < clen; j++) {
|
54
|
+
var customElement = customElements[j];
|
55
|
+
document.createElement(customElement);
|
72
56
|
}
|
73
|
-
};
|
74
|
-
|
75
|
-
var ieVersion = getIE();
|
76
|
-
|
77
|
-
if ((ieVersion > -1 && ieVersion < 9) || debug) {
|
78
|
-
shiv();
|
79
57
|
}
|
80
58
|
|
81
59
|
})(window);
|
@@ -1,6 +1,6 @@
|
|
1
1
|
/**
|
2
2
|
* AngularUI - The companion suite for AngularJS
|
3
|
-
* @version v0.
|
3
|
+
* @version v0.4.0 - 2013-02-15
|
4
4
|
* @link http://angular-ui.github.com
|
5
5
|
* @license MIT License, http://www.opensource.org/licenses/MIT
|
6
6
|
*/
|
@@ -11,106 +11,343 @@ angular.module('ui.filters', ['ui.config']);
|
|
11
11
|
angular.module('ui.directives', ['ui.config']);
|
12
12
|
angular.module('ui', ['ui.filters', 'ui.directives', 'ui.config']);
|
13
13
|
|
14
|
-
|
15
|
-
|
14
|
+
/**
|
15
|
+
* Animates the injection of new DOM elements by simply creating the DOM with a class and then immediately removing it
|
16
|
+
* Animations must be done using CSS3 transitions, but provide excellent flexibility
|
17
|
+
*
|
18
|
+
* @todo Add proper support for animating out
|
19
|
+
* @param [options] {mixed} Can be an object with multiple options, or a string with the animation class
|
20
|
+
* class {string} the CSS class(es) to use. For example, 'ui-hide' might be an excellent alternative class.
|
21
|
+
* @example <li ng-repeat="item in items" ui-animate=" 'ui-hide' ">{{item}}</li>
|
22
|
+
*/
|
23
|
+
angular.module('ui.directives').directive('uiAnimate', ['ui.config', '$timeout', function (uiConfig, $timeout) {
|
24
|
+
var options = {};
|
25
|
+
if (angular.isString(uiConfig.animate)) {
|
26
|
+
options['class'] = uiConfig.animate;
|
27
|
+
} else if (uiConfig.animate) {
|
28
|
+
options = uiConfig.animate;
|
29
|
+
}
|
30
|
+
return {
|
31
|
+
restrict: 'A', // supports using directive as element, attribute and class
|
32
|
+
link: function ($scope, element, attrs) {
|
33
|
+
var opts = {};
|
34
|
+
if (attrs.uiAnimate) {
|
35
|
+
opts = $scope.$eval(attrs.uiAnimate);
|
36
|
+
if (angular.isString(opts)) {
|
37
|
+
opts = {'class': opts};
|
38
|
+
}
|
39
|
+
}
|
40
|
+
opts = angular.extend({'class': 'ui-animate'}, options, opts);
|
16
41
|
|
17
|
-
|
42
|
+
element.addClass(opts['class']);
|
43
|
+
$timeout(function () {
|
44
|
+
element.removeClass(opts['class']);
|
45
|
+
}, 20, false);
|
46
|
+
}
|
47
|
+
};
|
48
|
+
}]);
|
49
|
+
|
50
|
+
|
51
|
+
/*
|
52
|
+
* AngularJs Fullcalendar Wrapper for the JQuery FullCalendar
|
53
|
+
* API @ http://arshaw.com/fullcalendar/
|
54
|
+
*
|
55
|
+
* Angular Calendar Directive that takes in the [eventSources] nested array object as the ng-model and watches (eventSources.length + eventSources[i].length) for changes.
|
56
|
+
* Can also take in multiple event urls as a source object(s) and feed the events per view.
|
57
|
+
* The calendar will watch any eventSource array and update itself when a delta is created
|
58
|
+
* An equalsTracker attrs has been added for use cases that would render the overall length tracker the same even though the events have changed to force updates.
|
59
|
+
*
|
18
60
|
*/
|
19
61
|
|
20
|
-
angular.module('ui.directives').directive('
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
62
|
+
angular.module('ui.directives').directive('uiCalendar',['ui.config', '$parse', function (uiConfig,$parse) {
|
63
|
+
uiConfig.uiCalendar = uiConfig.uiCalendar || {};
|
64
|
+
//returns calendar
|
65
|
+
return {
|
66
|
+
require: 'ngModel',
|
67
|
+
restrict: 'A',
|
68
|
+
link: function(scope, elm, attrs, $timeout) {
|
69
|
+
var sources = scope.$eval(attrs.ngModel);
|
70
|
+
var tracker = 0;
|
71
|
+
/* returns the length of all source arrays plus the length of eventSource itself */
|
72
|
+
var getSources = function () {
|
73
|
+
var equalsTracker = scope.$eval(attrs.equalsTracker);
|
74
|
+
tracker = 0;
|
75
|
+
angular.forEach(sources,function(value,key){
|
76
|
+
if(angular.isArray(value)){
|
77
|
+
tracker += value.length;
|
78
|
+
}
|
79
|
+
});
|
80
|
+
if(angular.isNumber(equalsTracker)){
|
81
|
+
return tracker + sources.length + equalsTracker;
|
82
|
+
}else{
|
83
|
+
return tracker + sources.length;
|
84
|
+
}
|
85
|
+
};
|
86
|
+
/* update the calendar with the correct options */
|
87
|
+
function update() {
|
88
|
+
//calendar object exposed on scope
|
89
|
+
scope.calendar = elm.html('');
|
90
|
+
var view = scope.calendar.fullCalendar('getView');
|
91
|
+
if(view){
|
92
|
+
view = view.name; //setting the default view to be whatever the current view is. This can be overwritten.
|
93
|
+
}
|
94
|
+
/* If the calendar has options added then render them */
|
95
|
+
var expression,
|
96
|
+
options = {
|
97
|
+
defaultView : view,
|
98
|
+
eventSources: sources
|
99
|
+
};
|
100
|
+
if (attrs.uiCalendar) {
|
101
|
+
expression = scope.$eval(attrs.uiCalendar);
|
102
|
+
} else {
|
103
|
+
expression = {};
|
104
|
+
}
|
105
|
+
angular.extend(options, uiConfig.uiCalendar, expression);
|
106
|
+
scope.calendar.fullCalendar(options);
|
56
107
|
}
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
108
|
+
update();
|
109
|
+
/* watches all eventSources */
|
110
|
+
scope.$watch(getSources, function( newVal, oldVal )
|
111
|
+
{
|
112
|
+
update();
|
113
|
+
});
|
114
|
+
}
|
62
115
|
};
|
63
|
-
|
64
|
-
|
65
|
-
|
116
|
+
}]);
|
117
|
+
/*global angular, CodeMirror, Error*/
|
66
118
|
/**
|
67
|
-
*
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
119
|
+
* Binds a CodeMirror widget to a <textarea> element.
|
120
|
+
*/
|
121
|
+
angular.module('ui.directives').directive('uiCodemirror', ['ui.config', '$timeout', function (uiConfig, $timeout) {
|
122
|
+
'use strict';
|
123
|
+
|
124
|
+
var events = ["cursorActivity", "viewportChange", "gutterClick", "focus", "blur", "scroll", "update"];
|
125
|
+
return {
|
126
|
+
restrict:'A',
|
127
|
+
require:'ngModel',
|
128
|
+
link:function (scope, elm, attrs, ngModel) {
|
129
|
+
var options, opts, onChange, deferCodeMirror, codeMirror;
|
130
|
+
|
131
|
+
if (elm[0].type !== 'textarea') {
|
132
|
+
throw new Error('uiCodemirror3 can only be applied to a textarea element');
|
133
|
+
}
|
134
|
+
|
135
|
+
options = uiConfig.codemirror || {};
|
136
|
+
opts = angular.extend({}, options, scope.$eval(attrs.uiCodemirror));
|
137
|
+
|
138
|
+
onChange = function (aEvent) {
|
139
|
+
return function (instance, changeObj) {
|
140
|
+
var newValue = instance.getValue();
|
141
|
+
if (newValue !== ngModel.$viewValue) {
|
142
|
+
ngModel.$setViewValue(newValue);
|
143
|
+
scope.$apply();
|
144
|
+
}
|
145
|
+
if (typeof aEvent === "function")
|
146
|
+
aEvent(instance, changeObj);
|
147
|
+
};
|
148
|
+
};
|
149
|
+
|
150
|
+
deferCodeMirror = function () {
|
151
|
+
codeMirror = CodeMirror.fromTextArea(elm[0], opts);
|
152
|
+
codeMirror.on("change", onChange(opts.onChange));
|
153
|
+
|
154
|
+
for (var i = 0, n = events.length, aEvent; i < n; ++i) {
|
155
|
+
aEvent = opts["on" + events[i].charAt(0).toUpperCase() + events[i].slice(1)];
|
156
|
+
if (aEvent === void 0) continue;
|
157
|
+
if (typeof aEvent !== "function") continue;
|
158
|
+
codeMirror.on(events[i], aEvent);
|
159
|
+
}
|
160
|
+
|
161
|
+
// CodeMirror expects a string, so make sure it gets one.
|
162
|
+
// This does not change the model.
|
163
|
+
ngModel.$formatters.push(function (value) {
|
164
|
+
if (angular.isUndefined(value) || value === null) {
|
165
|
+
return '';
|
166
|
+
}
|
167
|
+
else if (angular.isObject(value) || angular.isArray(value)) {
|
168
|
+
throw new Error('ui-codemirror cannot use an object or an array as a model');
|
169
|
+
}
|
170
|
+
return value;
|
171
|
+
});
|
172
|
+
|
173
|
+
// Override the ngModelController $render method, which is what gets called when the model is updated.
|
174
|
+
// This takes care of the synchronizing the codeMirror element with the underlying model, in the case that it is changed by something else.
|
175
|
+
ngModel.$render = function () {
|
176
|
+
codeMirror.setValue(ngModel.$viewValue);
|
177
|
+
};
|
178
|
+
|
179
|
+
// Watch ui-refresh and refresh the directive
|
180
|
+
if (attrs.uiRefresh) {
|
181
|
+
scope.$watch(attrs.uiRefresh, function(newVal, oldVal){
|
182
|
+
// Skip the initial watch firing
|
183
|
+
if (newVal !== oldVal)
|
184
|
+
$timeout(codeMirror.refresh);
|
185
|
+
});
|
186
|
+
}
|
187
|
+
};
|
188
|
+
|
189
|
+
$timeout(deferCodeMirror);
|
190
|
+
|
191
|
+
}
|
192
|
+
};
|
193
|
+
}]);
|
194
|
+
|
195
|
+
/*
|
196
|
+
Gives the ability to style currency based on its sign.
|
80
197
|
*/
|
81
|
-
angular.module('ui.directives').directive('
|
198
|
+
angular.module('ui.directives').directive('uiCurrency', ['ui.config', 'currencyFilter' , function (uiConfig, currencyFilter) {
|
199
|
+
var options = {
|
200
|
+
pos: 'ui-currency-pos',
|
201
|
+
neg: 'ui-currency-neg',
|
202
|
+
zero: 'ui-currency-zero'
|
203
|
+
};
|
204
|
+
if (uiConfig.currency) {
|
205
|
+
angular.extend(options, uiConfig.currency);
|
206
|
+
}
|
82
207
|
return {
|
83
|
-
restrict: '
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
return function (scope, elm, attrs) {
|
90
|
-
var linkOptions = [], ngChange = 'change';
|
208
|
+
restrict: 'EAC',
|
209
|
+
require: 'ngModel',
|
210
|
+
link: function (scope, element, attrs, controller) {
|
211
|
+
var opts, // instance-specific options
|
212
|
+
renderview,
|
213
|
+
value;
|
91
214
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
215
|
+
opts = angular.extend({}, options, scope.$eval(attrs.uiCurrency));
|
216
|
+
|
217
|
+
renderview = function (viewvalue) {
|
218
|
+
var num;
|
219
|
+
num = viewvalue * 1;
|
220
|
+
element.toggleClass(opts.pos, (num > 0) );
|
221
|
+
element.toggleClass(opts.neg, (num < 0) );
|
222
|
+
element.toggleClass(opts.zero, (num === 0) );
|
223
|
+
if (viewvalue === '') {
|
224
|
+
element.text('');
|
225
|
+
} else {
|
226
|
+
element.text(currencyFilter(num, opts.symbol));
|
99
227
|
}
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
228
|
+
return true;
|
229
|
+
};
|
230
|
+
|
231
|
+
controller.$render = function () {
|
232
|
+
value = controller.$viewValue;
|
233
|
+
element.val(value);
|
234
|
+
renderview(value);
|
235
|
+
};
|
236
|
+
|
237
|
+
}
|
238
|
+
};
|
239
|
+
}]);
|
240
|
+
|
241
|
+
/*global angular */
|
242
|
+
/*
|
243
|
+
jQuery UI Datepicker plugin wrapper
|
244
|
+
|
245
|
+
@note If ≤ IE8 make sure you have a polyfill for Date.toISOString()
|
246
|
+
@param [ui-date] {object} Options to pass to $.fn.datepicker() merged onto ui.config
|
247
|
+
*/
|
248
|
+
|
249
|
+
angular.module('ui.directives')
|
250
|
+
|
251
|
+
.directive('uiDate', ['ui.config', function (uiConfig) {
|
252
|
+
'use strict';
|
253
|
+
var options;
|
254
|
+
options = {};
|
255
|
+
if (angular.isObject(uiConfig.date)) {
|
256
|
+
angular.extend(options, uiConfig.date);
|
257
|
+
}
|
258
|
+
return {
|
259
|
+
require:'?ngModel',
|
260
|
+
link:function (scope, element, attrs, controller) {
|
261
|
+
var getOptions = function () {
|
262
|
+
return angular.extend({}, uiConfig.date, scope.$eval(attrs.uiDate));
|
263
|
+
};
|
264
|
+
var initDateWidget = function () {
|
265
|
+
var opts = getOptions();
|
266
|
+
|
267
|
+
// If we have a controller (i.e. ngModelController) then wire it up
|
268
|
+
if (controller) {
|
269
|
+
var updateModel = function () {
|
270
|
+
scope.$apply(function () {
|
271
|
+
var date = element.datepicker("getDate");
|
272
|
+
element.datepicker("setDate", element.val());
|
273
|
+
controller.$setViewValue(date);
|
274
|
+
element.blur();
|
107
275
|
});
|
276
|
+
};
|
277
|
+
if (opts.onSelect) {
|
278
|
+
// Caller has specified onSelect, so call this as well as updating the model
|
279
|
+
var userHandler = opts.onSelect;
|
280
|
+
opts.onSelect = function (value, picker) {
|
281
|
+
updateModel();
|
282
|
+
scope.$apply(function() {
|
283
|
+
userHandler(value, picker);
|
284
|
+
});
|
285
|
+
};
|
286
|
+
} else {
|
287
|
+
// No onSelect already specified so just update the model
|
288
|
+
opts.onSelect = updateModel;
|
108
289
|
}
|
290
|
+
// In case the user changes the text directly in the input box
|
291
|
+
element.bind('change', updateModel);
|
292
|
+
|
293
|
+
// Update the date picker when the model changes
|
294
|
+
controller.$render = function () {
|
295
|
+
var date = controller.$viewValue;
|
296
|
+
if ( angular.isDefined(date) && date !== null && !angular.isDate(date) ) {
|
297
|
+
throw new Error('ng-Model value must be a Date object - currently it is a ' + typeof date + ' - use ui-date-format to convert it from a string');
|
298
|
+
}
|
299
|
+
element.datepicker("setDate", date);
|
300
|
+
};
|
301
|
+
}
|
302
|
+
// If we don't destroy the old one it doesn't update properly when the config changes
|
303
|
+
element.datepicker('destroy');
|
304
|
+
// Create the new datepicker widget
|
305
|
+
element.datepicker(opts);
|
306
|
+
if ( controller ) {
|
307
|
+
// Force a render to override whatever is in the input text box
|
308
|
+
controller.$render();
|
109
309
|
}
|
110
|
-
elm[attrs.uiJq].apply(elm, linkOptions);
|
111
310
|
};
|
311
|
+
// Watch for changes to the directives options
|
312
|
+
scope.$watch(getOptions, initDateWidget, true);
|
313
|
+
}
|
314
|
+
};
|
315
|
+
}
|
316
|
+
])
|
317
|
+
|
318
|
+
.directive('uiDateFormat', ['ui.config', function(uiConfig) {
|
319
|
+
var directive = {
|
320
|
+
require:'ngModel',
|
321
|
+
link: function(scope, element, attrs, modelCtrl) {
|
322
|
+
var dateFormat = attrs.uiDateFormat || uiConfig.dateFormat;
|
323
|
+
if ( dateFormat ) {
|
324
|
+
// Use the datepicker with the attribute value as the dateFormat string to convert to and from a string
|
325
|
+
modelCtrl.$formatters.push(function(value) {
|
326
|
+
if (angular.isString(value) ) {
|
327
|
+
return $.datepicker.parseDate(dateFormat, value);
|
328
|
+
}
|
329
|
+
});
|
330
|
+
modelCtrl.$parsers.push(function(value){
|
331
|
+
if (value) {
|
332
|
+
return $.datepicker.formatDate(dateFormat, value);
|
333
|
+
}
|
334
|
+
});
|
335
|
+
} else {
|
336
|
+
// Default to ISO formatting
|
337
|
+
modelCtrl.$formatters.push(function(value) {
|
338
|
+
if (angular.isString(value) ) {
|
339
|
+
return new Date(value);
|
340
|
+
}
|
341
|
+
});
|
342
|
+
modelCtrl.$parsers.push(function(value){
|
343
|
+
if (value) {
|
344
|
+
return value.toISOString();
|
345
|
+
}
|
346
|
+
});
|
347
|
+
}
|
112
348
|
}
|
113
349
|
};
|
350
|
+
return directive;
|
114
351
|
}]);
|
115
352
|
|
116
353
|
/**
|
@@ -142,99 +379,219 @@ angular.module('ui.directives').directive('uiEvent', ['$parse',
|
|
142
379
|
}]);
|
143
380
|
|
144
381
|
/*
|
145
|
-
|
382
|
+
* Defines the ui-if tag. This removes/adds an element from the dom depending on a condition
|
383
|
+
* Originally created by @tigbro, for the @jquery-mobile-angular-adapter
|
384
|
+
* https://github.com/tigbro/jquery-mobile-angular-adapter
|
146
385
|
*/
|
147
|
-
angular.module('ui.directives').directive('
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
386
|
+
angular.module('ui.directives').directive('uiIf', [function () {
|
387
|
+
return {
|
388
|
+
transclude: 'element',
|
389
|
+
priority: 1000,
|
390
|
+
terminal: true,
|
391
|
+
restrict: 'A',
|
392
|
+
compile: function (element, attr, transclude) {
|
393
|
+
return function (scope, element, attr) {
|
394
|
+
|
395
|
+
var childElement;
|
396
|
+
var childScope;
|
397
|
+
|
398
|
+
scope.$watch(attr['uiIf'], function (newValue) {
|
399
|
+
if (childElement) {
|
400
|
+
childElement.remove();
|
401
|
+
childElement = undefined;
|
402
|
+
}
|
403
|
+
if (childScope) {
|
404
|
+
childScope.$destroy();
|
405
|
+
childScope = undefined;
|
406
|
+
}
|
160
407
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
return isValid ? value : undefined;
|
408
|
+
if (newValue) {
|
409
|
+
childScope = scope.$new();
|
410
|
+
transclude(childScope, function (clone) {
|
411
|
+
childElement = clone;
|
412
|
+
element.after(clone);
|
413
|
+
});
|
414
|
+
}
|
169
415
|
});
|
416
|
+
};
|
417
|
+
}
|
418
|
+
};
|
419
|
+
}]);
|
420
|
+
/**
|
421
|
+
* General-purpose jQuery wrapper. Simply pass the plugin name as the expression.
|
422
|
+
*
|
423
|
+
* It is possible to specify a default set of parameters for each jQuery plugin.
|
424
|
+
* Under the jq key, namespace each plugin by that which will be passed to ui-jq.
|
425
|
+
* Unfortunately, at this time you can only pre-define the first parameter.
|
426
|
+
* @example { jq : { datepicker : { showOn:'click' } } }
|
427
|
+
*
|
428
|
+
* @param ui-jq {string} The $elm.[pluginName]() to call.
|
429
|
+
* @param [ui-options] {mixed} Expression to be evaluated and passed as options to the function
|
430
|
+
* Multiple parameters can be separated by commas
|
431
|
+
* @param [ui-refresh] {expression} Watch expression and refire plugin on changes
|
432
|
+
*
|
433
|
+
* @example <input ui-jq="datepicker" ui-options="{showOn:'click'},secondParameter,thirdParameter" ui-refresh="iChange">
|
434
|
+
*/
|
435
|
+
angular.module('ui.directives').directive('uiJq', ['ui.config', '$timeout', function uiJqInjectingFunction(uiConfig, $timeout) {
|
170
436
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
});
|
437
|
+
return {
|
438
|
+
restrict: 'A',
|
439
|
+
compile: function uiJqCompilingFunction(tElm, tAttrs) {
|
440
|
+
|
441
|
+
if (!angular.isFunction(tElm[tAttrs.uiJq])) {
|
442
|
+
throw new Error('ui-jq: The "' + tAttrs.uiJq + '" function does not exist');
|
178
443
|
}
|
179
|
-
|
180
|
-
}
|
181
|
-
]);
|
444
|
+
var options = uiConfig.jq && uiConfig.jq[tAttrs.uiJq];
|
182
445
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
446
|
+
return function uiJqLinkingFunction(scope, elm, attrs) {
|
447
|
+
|
448
|
+
var linkOptions = [];
|
449
|
+
|
450
|
+
// If ui-options are passed, merge (or override) them onto global defaults and pass to the jQuery method
|
451
|
+
if (attrs.uiOptions) {
|
452
|
+
linkOptions = scope.$eval('[' + attrs.uiOptions + ']');
|
453
|
+
if (angular.isObject(options) && angular.isObject(linkOptions[0])) {
|
454
|
+
linkOptions[0] = angular.extend({}, options, linkOptions[0]);
|
455
|
+
}
|
456
|
+
} else if (options) {
|
457
|
+
linkOptions = [options];
|
458
|
+
}
|
459
|
+
// If change compatibility is enabled, the form input's "change" event will trigger an "input" event
|
460
|
+
if (attrs.ngModel && elm.is('select,input,textarea')) {
|
461
|
+
elm.on('change', function() {
|
462
|
+
elm.trigger('input');
|
463
|
+
});
|
464
|
+
}
|
465
|
+
|
466
|
+
// Call jQuery method and pass relevant options
|
467
|
+
function callPlugin() {
|
468
|
+
$timeout(function() {
|
469
|
+
elm[attrs.uiJq].apply(elm, linkOptions);
|
470
|
+
}, 0, false);
|
471
|
+
}
|
472
|
+
|
473
|
+
// If ui-refresh is used, re-fire the the method upon every change
|
474
|
+
if (attrs.uiRefresh) {
|
475
|
+
scope.$watch(attrs.uiRefresh, function(newVal) {
|
476
|
+
callPlugin();
|
477
|
+
});
|
478
|
+
}
|
479
|
+
callPlugin();
|
480
|
+
};
|
481
|
+
}
|
482
|
+
};
|
483
|
+
}]);
|
484
|
+
|
485
|
+
angular.module('ui.directives').factory('keypressHelper', ['$parse', function keypress($parse){
|
486
|
+
var keysByCode = {
|
487
|
+
8: 'backspace',
|
488
|
+
9: 'tab',
|
489
|
+
13: 'enter',
|
490
|
+
27: 'esc',
|
491
|
+
32: 'space',
|
492
|
+
33: 'pageup',
|
493
|
+
34: 'pagedown',
|
494
|
+
35: 'end',
|
495
|
+
36: 'home',
|
496
|
+
37: 'left',
|
497
|
+
38: 'up',
|
498
|
+
39: 'right',
|
499
|
+
40: 'down',
|
500
|
+
45: 'insert',
|
501
|
+
46: 'delete'
|
502
|
+
};
|
503
|
+
|
504
|
+
var capitaliseFirstLetter = function (string) {
|
505
|
+
return string.charAt(0).toUpperCase() + string.slice(1);
|
506
|
+
};
|
507
|
+
|
508
|
+
return function(mode, scope, elm, attrs) {
|
509
|
+
var params, combinations = [];
|
510
|
+
params = scope.$eval(attrs['ui'+capitaliseFirstLetter(mode)]);
|
511
|
+
|
512
|
+
// Prepare combinations for simple checking
|
513
|
+
angular.forEach(params, function (v, k) {
|
514
|
+
var combination, expression;
|
515
|
+
expression = $parse(v);
|
516
|
+
|
517
|
+
angular.forEach(k.split(' '), function(variation) {
|
518
|
+
combination = {
|
519
|
+
expression: expression,
|
520
|
+
keys: {}
|
521
|
+
};
|
522
|
+
angular.forEach(variation.split('-'), function (value) {
|
523
|
+
combination.keys[value] = true;
|
201
524
|
});
|
525
|
+
combinations.push(combination);
|
202
526
|
});
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
527
|
+
});
|
528
|
+
|
529
|
+
// Check only matching of pressed keys one of the conditions
|
530
|
+
elm.bind(mode, function (event) {
|
531
|
+
// No need to do that inside the cycle
|
532
|
+
var altPressed = event.metaKey || event.altKey;
|
533
|
+
var ctrlPressed = event.ctrlKey;
|
534
|
+
var shiftPressed = event.shiftKey;
|
535
|
+
var keyCode = event.keyCode;
|
536
|
+
|
537
|
+
// normalize keycodes
|
538
|
+
if (mode === 'keypress' && !shiftPressed && keyCode >= 97 && keyCode <= 122) {
|
539
|
+
keyCode = keyCode - 32;
|
540
|
+
}
|
541
|
+
|
542
|
+
// Iterate over prepared combinations
|
543
|
+
angular.forEach(combinations, function (combination) {
|
544
|
+
|
545
|
+
var mainKeyPressed = (combination.keys[keysByCode[event.keyCode]] || combination.keys[event.keyCode.toString()]) || false;
|
546
|
+
|
547
|
+
var altRequired = combination.keys.alt || false;
|
548
|
+
var ctrlRequired = combination.keys.ctrl || false;
|
549
|
+
var shiftRequired = combination.keys.shift || false;
|
550
|
+
|
551
|
+
if (
|
552
|
+
mainKeyPressed &&
|
553
|
+
( altRequired == altPressed ) &&
|
554
|
+
( ctrlRequired == ctrlPressed ) &&
|
555
|
+
( shiftRequired == shiftPressed )
|
556
|
+
) {
|
557
|
+
// Run the function
|
558
|
+
scope.$apply(function () {
|
559
|
+
combination.expression(scope, { '$event': event });
|
560
|
+
});
|
561
|
+
}
|
207
562
|
});
|
208
|
-
}
|
563
|
+
});
|
209
564
|
};
|
210
565
|
}]);
|
566
|
+
|
211
567
|
/**
|
212
|
-
*
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
resetValue = uiConfig.reset;
|
568
|
+
* Bind one or more handlers to particular keys or their combination
|
569
|
+
* @param hash {mixed} keyBindings Can be an object or string where keybinding expression of keys or keys combinations and AngularJS Exspressions are set. Object syntax: "{ keys1: expression1 [, keys2: expression2 [ , ... ]]}". String syntax: ""expression1 on keys1 [ and expression2 on keys2 [ and ... ]]"". Expression is an AngularJS Expression, and key(s) are dash-separated combinations of keys and modifiers (one or many, if any. Order does not matter). Supported modifiers are 'ctrl', 'shift', 'alt' and key can be used either via its keyCode (13 for Return) or name. Named keys are 'backspace', 'tab', 'enter', 'esc', 'space', 'pageup', 'pagedown', 'end', 'home', 'left', 'up', 'right', 'down', 'insert', 'delete'.
|
570
|
+
* @example <input ui-keypress="{enter:'x = 1', 'ctrl-shift-space':'foo()', 'shift-13':'bar()'}" /> <input ui-keypress="foo = 2 on ctrl-13 and bar('hello') on shift-esc" />
|
571
|
+
**/
|
572
|
+
angular.module('ui.directives').directive('uiKeydown', ['keypressHelper', function(keypressHelper){
|
218
573
|
return {
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
else
|
230
|
-
ctrl.$setViewValue(resetValue);
|
231
|
-
ctrl.$render();
|
232
|
-
});
|
233
|
-
});
|
574
|
+
link: function (scope, elm, attrs) {
|
575
|
+
keypressHelper('keydown', scope, elm, attrs);
|
576
|
+
}
|
577
|
+
};
|
578
|
+
}]);
|
579
|
+
|
580
|
+
angular.module('ui.directives').directive('uiKeypress', ['keypressHelper', function(keypressHelper){
|
581
|
+
return {
|
582
|
+
link: function (scope, elm, attrs) {
|
583
|
+
keypressHelper('keypress', scope, elm, attrs);
|
234
584
|
}
|
235
585
|
};
|
236
586
|
}]);
|
237
587
|
|
588
|
+
angular.module('ui.directives').directive('uiKeyup', ['keypressHelper', function(keypressHelper){
|
589
|
+
return {
|
590
|
+
link: function (scope, elm, attrs) {
|
591
|
+
keypressHelper('keyup', scope, elm, attrs);
|
592
|
+
}
|
593
|
+
};
|
594
|
+
}]);
|
238
595
|
(function () {
|
239
596
|
var app = angular.module('ui.directives');
|
240
597
|
|
@@ -246,7 +603,7 @@ angular.module('ui.directives').directive('uiReset', ['ui.config', function (uiC
|
|
246
603
|
//for the googlemap doesn't interfere with a normal 'click' event
|
247
604
|
var $event = { type: 'map-' + eventName };
|
248
605
|
google.maps.event.addListener(googleObject, eventName, function (evt) {
|
249
|
-
element.
|
606
|
+
element.triggerHandler(angular.extend({}, $event, evt));
|
250
607
|
//We create an $apply if it isn't happening. we need better support for this
|
251
608
|
//We don't want to use timeout because tons of these events fire at once,
|
252
609
|
//and we only need one $apply
|
@@ -360,485 +717,141 @@ angular.module('ui.directives').directive('uiReset', ['ui.config', function (uiC
|
|
360
717
|
'click dblclick');
|
361
718
|
|
362
719
|
})();
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
34: 'pagedown',
|
372
|
-
35: 'end',
|
373
|
-
36: 'home',
|
374
|
-
37: 'left',
|
375
|
-
38: 'up',
|
376
|
-
39: 'right',
|
377
|
-
40: 'down',
|
378
|
-
45: 'insert',
|
379
|
-
46: 'delete'
|
380
|
-
};
|
381
|
-
|
382
|
-
var capitaliseFirstLetter = function (string) {
|
383
|
-
return string.charAt(0).toUpperCase() + string.slice(1);
|
384
|
-
};
|
385
|
-
|
386
|
-
return function(mode, scope, elm, attrs) {
|
387
|
-
var params, combinations = [];
|
388
|
-
params = scope.$eval(attrs['ui'+capitaliseFirstLetter(mode)]);
|
720
|
+
/*
|
721
|
+
Attaches jquery-ui input mask onto input element
|
722
|
+
*/
|
723
|
+
angular.module('ui.directives').directive('uiMask', [
|
724
|
+
function () {
|
725
|
+
return {
|
726
|
+
require:'ngModel',
|
727
|
+
link:function ($scope, element, attrs, controller) {
|
389
728
|
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
729
|
+
/* We override the render method to run the jQuery mask plugin
|
730
|
+
*/
|
731
|
+
controller.$render = function () {
|
732
|
+
var value = controller.$viewValue || '';
|
733
|
+
element.val(value);
|
734
|
+
element.mask($scope.$eval(attrs.uiMask));
|
735
|
+
};
|
394
736
|
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
737
|
+
/* Add a parser that extracts the masked value into the model but only if the mask is valid
|
738
|
+
*/
|
739
|
+
controller.$parsers.push(function (value) {
|
740
|
+
//the second check (or) is only needed due to the fact that element.isMaskValid() will keep returning undefined
|
741
|
+
//until there was at least one key event
|
742
|
+
var isValid = element.isMaskValid() || angular.isUndefined(element.isMaskValid()) && element.val().length>0;
|
743
|
+
controller.$setValidity('mask', isValid);
|
744
|
+
return isValid ? value : undefined;
|
402
745
|
});
|
403
|
-
combinations.push(combination);
|
404
|
-
});
|
405
|
-
});
|
406
|
-
|
407
|
-
// Check only matching of pressed keys one of the conditions
|
408
|
-
elm.bind(mode, function (event) {
|
409
|
-
// No need to do that inside the cycle
|
410
|
-
var altPressed = event.metaKey || event.altKey;
|
411
|
-
var ctrlPressed = event.ctrlKey;
|
412
|
-
var shiftPressed = event.shiftKey;
|
413
|
-
var keyCode = event.keyCode;
|
414
|
-
|
415
|
-
// normalize keycodes
|
416
|
-
if (mode === 'keypress' && !shiftPressed && keyCode >= 97 && keyCode <= 122) {
|
417
|
-
keyCode = keyCode - 32;
|
418
|
-
}
|
419
746
|
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
var altRequired = combination.keys.alt || false;
|
426
|
-
var ctrlRequired = combination.keys.ctrl || false;
|
427
|
-
var shiftRequired = combination.keys.shift || false;
|
428
|
-
|
429
|
-
if (
|
430
|
-
mainKeyPressed &&
|
431
|
-
( altRequired == altPressed ) &&
|
432
|
-
( ctrlRequired == ctrlPressed ) &&
|
433
|
-
( shiftRequired == shiftPressed )
|
434
|
-
) {
|
435
|
-
// Run the function
|
436
|
-
scope.$apply(function () {
|
437
|
-
combination.expression(scope, { '$event': event });
|
747
|
+
/* When keyup, update the view value
|
748
|
+
*/
|
749
|
+
element.bind('keyup', function () {
|
750
|
+
$scope.$apply(function () {
|
751
|
+
controller.$setViewValue(element.mask());
|
438
752
|
});
|
439
|
-
}
|
440
|
-
});
|
441
|
-
});
|
442
|
-
};
|
443
|
-
}]);
|
444
|
-
|
445
|
-
/**
|
446
|
-
* Bind one or more handlers to particular keys or their combination
|
447
|
-
* @param hash {mixed} keyBindings Can be an object or string where keybinding expression of keys or keys combinations and AngularJS Exspressions are set. Object syntax: "{ keys1: expression1 [, keys2: expression2 [ , ... ]]}". String syntax: ""expression1 on keys1 [ and expression2 on keys2 [ and ... ]]"". Expression is an AngularJS Expression, and key(s) are dash-separated combinations of keys and modifiers (one or many, if any. Order does not matter). Supported modifiers are 'ctrl', 'shift', 'alt' and key can be used either via its keyCode (13 for Return) or name. Named keys are 'backspace', 'tab', 'enter', 'esc', 'space', 'pageup', 'pagedown', 'end', 'home', 'left', 'up', 'right', 'down', 'insert', 'delete'.
|
448
|
-
* @example <input ui-keypress="{enter:'x = 1', 'ctrl-shift-space':'foo()', 'shift-13':'bar()'}" /> <input ui-keypress="foo = 2 on ctrl-13 and bar('hello') on shift-esc" />
|
449
|
-
**/
|
450
|
-
angular.module('ui.directives').directive('uiKeydown', ['keypressHelper', function(keypressHelper){
|
451
|
-
return {
|
452
|
-
link: function (scope, elm, attrs) {
|
453
|
-
keypressHelper('keydown', scope, elm, attrs);
|
454
|
-
}
|
455
|
-
};
|
456
|
-
}]);
|
457
|
-
|
458
|
-
angular.module('ui.directives').directive('uiKeypress', ['keypressHelper', function(keypressHelper){
|
459
|
-
return {
|
460
|
-
link: function (scope, elm, attrs) {
|
461
|
-
keypressHelper('keypress', scope, elm, attrs);
|
462
|
-
}
|
463
|
-
};
|
464
|
-
}]);
|
465
|
-
|
466
|
-
angular.module('ui.directives').directive('uiKeyup', ['keypressHelper', function(keypressHelper){
|
467
|
-
return {
|
468
|
-
link: function (scope, elm, attrs) {
|
469
|
-
keypressHelper('keyup', scope, elm, attrs);
|
470
|
-
}
|
471
|
-
};
|
472
|
-
}]);
|
473
|
-
/**
|
474
|
-
* General-purpose validator for ngModel.
|
475
|
-
* angular.js comes with several built-in validation mechanism for input fields (ngRequired, ngPattern etc.) but using
|
476
|
-
* an arbitrary validation function requires creation of a custom formatters and / or parsers.
|
477
|
-
* The ui-validate directive makes it easy to use any function(s) defined in scope as a validator function(s).
|
478
|
-
* A validator function will trigger validation on both model and input changes.
|
479
|
-
*
|
480
|
-
* @example <input ui-validate="myValidatorFunction">
|
481
|
-
* @example <input ui-validate="{foo : validateFoo, bar : validateBar}">
|
482
|
-
*
|
483
|
-
* @param ui-validate {string|object literal} If strings is passed it should be a scope's function to be used as a validator.
|
484
|
-
* If an object literal is passed a key denotes a validation error key while a value should be a validator function.
|
485
|
-
* In both cases validator function should take a value to validate as its argument and should return true/false indicating a validation result.
|
486
|
-
*/
|
487
|
-
angular.module('ui.directives').directive('uiValidate', function () {
|
488
|
-
|
489
|
-
return {
|
490
|
-
restrict: 'A',
|
491
|
-
require: 'ngModel',
|
492
|
-
link: function (scope, elm, attrs, ctrl) {
|
493
|
-
|
494
|
-
var validateFn, validateExpr = attrs.uiValidate;
|
495
|
-
|
496
|
-
validateExpr = scope.$eval(validateExpr);
|
497
|
-
if (!validateExpr) {
|
498
|
-
return;
|
499
|
-
}
|
500
|
-
|
501
|
-
if (angular.isFunction(validateExpr)) {
|
502
|
-
validateExpr = { validator: validateExpr };
|
503
|
-
}
|
504
|
-
|
505
|
-
angular.forEach(validateExpr, function (validatorFn, key) {
|
506
|
-
validateFn = function (valueToValidate) {
|
507
|
-
if (validatorFn(valueToValidate)) {
|
508
|
-
ctrl.$setValidity(key, true);
|
509
|
-
return valueToValidate;
|
510
|
-
} else {
|
511
|
-
ctrl.$setValidity(key, false);
|
512
|
-
return undefined;
|
513
|
-
}
|
514
|
-
};
|
515
|
-
ctrl.$formatters.push(validateFn);
|
516
|
-
ctrl.$parsers.push(validateFn);
|
517
|
-
});
|
518
|
-
}
|
519
|
-
};
|
520
|
-
});
|
521
|
-
/**
|
522
|
-
* Animates the injection of new DOM elements by simply creating the DOM with a class and then immediately removing it
|
523
|
-
* Animations must be done using CSS3 transitions, but provide excellent flexibility
|
524
|
-
*
|
525
|
-
* @todo Add proper support for animating out
|
526
|
-
* @param [options] {mixed} Can be an object with multiple options, or a string with the animation class
|
527
|
-
* class {string} the CSS class(es) to use. For example, 'ui-hide' might be an excellent alternative class.
|
528
|
-
* @example <li ng-repeat="item in items" ui-animate=" 'ui-hide' ">{{item}}</li>
|
529
|
-
*/
|
530
|
-
angular.module('ui.directives').directive('uiAnimate', ['ui.config', '$timeout', function (uiConfig, $timeout) {
|
531
|
-
var options = {};
|
532
|
-
if (angular.isString(uiConfig.animate)) {
|
533
|
-
options['class'] = uiConfig.animate;
|
534
|
-
} else if (uiConfig.animate) {
|
535
|
-
options = uiConfig.animate;
|
536
|
-
}
|
537
|
-
return {
|
538
|
-
restrict: 'A', // supports using directive as element, attribute and class
|
539
|
-
link: function ($scope, element, attrs) {
|
540
|
-
var opts = {};
|
541
|
-
if (attrs.uiAnimate) {
|
542
|
-
opts = $scope.$eval(attrs.uiAnimate);
|
543
|
-
if (angular.isString(opts)) {
|
544
|
-
opts = {'class': opts};
|
545
|
-
}
|
546
|
-
}
|
547
|
-
opts = angular.extend({'class': 'ui-animate'}, options, opts);
|
548
|
-
|
549
|
-
element.addClass(opts['class']);
|
550
|
-
$timeout(function () {
|
551
|
-
element.removeClass(opts['class']);
|
552
|
-
}, 20, false);
|
553
|
-
}
|
554
|
-
};
|
555
|
-
}]);
|
556
|
-
|
557
|
-
|
558
|
-
/**
|
559
|
-
* Enhanced Select2 Dropmenus
|
560
|
-
*
|
561
|
-
* @AJAX Mode - When in this mode, your value will be an object (or array of objects) of the data used by Select2
|
562
|
-
* This change is so that you do not have to do an additional query yourself on top of Select2's own query
|
563
|
-
* @params [options] {object} The configuration options passed to $.fn.select2(). Refer to the documentation
|
564
|
-
*/
|
565
|
-
angular.module('ui.directives').directive('uiSelect2', ['ui.config', '$http', function (uiConfig, $http) {
|
566
|
-
var options = {};
|
567
|
-
if (uiConfig.select2) {
|
568
|
-
angular.extend(options, uiConfig.select2);
|
569
|
-
}
|
570
|
-
return {
|
571
|
-
require: '?ngModel',
|
572
|
-
compile: function (tElm, tAttrs) {
|
573
|
-
var watch,
|
574
|
-
repeatOption,
|
575
|
-
repeatAttr,
|
576
|
-
isSelect = tElm.is('select'),
|
577
|
-
isMultiple = (tAttrs.multiple !== undefined);
|
578
|
-
|
579
|
-
// Enable watching of the options dataset if in use
|
580
|
-
if (tElm.is('select')) {
|
581
|
-
repeatOption = tElm.find('option[ng-repeat], option[data-ng-repeat]');
|
582
|
-
|
583
|
-
if (repeatOption.length) {
|
584
|
-
repeatAttr = repeatOption.attr('ng-repeat') || repeatOption.attr('data-ng-repeat');
|
585
|
-
watch = jQuery.trim(repeatAttr.split('|')[0]).split(' ').pop();
|
586
|
-
}
|
587
|
-
}
|
588
|
-
|
589
|
-
return function (scope, elm, attrs, controller) {
|
590
|
-
// instance-specific options
|
591
|
-
var opts = angular.extend({}, options, scope.$eval(attrs.uiSelect2));
|
592
|
-
|
593
|
-
if (isSelect) {
|
594
|
-
// Use <select multiple> instead
|
595
|
-
delete opts.multiple;
|
596
|
-
delete opts.initSelection;
|
597
|
-
} else if (isMultiple) {
|
598
|
-
opts.multiple = true;
|
599
|
-
}
|
600
|
-
|
601
|
-
if (controller) {
|
602
|
-
// Watch the model for programmatic changes
|
603
|
-
controller.$render = function () {
|
604
|
-
if (isSelect) {
|
605
|
-
elm.select2('val', controller.$modelValue);
|
606
|
-
} else {
|
607
|
-
if (isMultiple && !controller.$modelValue) {
|
608
|
-
elm.select2('data', []);
|
609
|
-
} else {
|
610
|
-
elm.select2('data', controller.$modelValue);
|
611
|
-
}
|
612
|
-
}
|
613
|
-
};
|
614
|
-
|
615
|
-
|
616
|
-
// Watch the options dataset for changes
|
617
|
-
if (watch) {
|
618
|
-
scope.$watch(watch, function (newVal, oldVal, scope) {
|
619
|
-
if (!newVal) return;
|
620
|
-
// Delayed so that the options have time to be rendered
|
621
|
-
setTimeout(function () {
|
622
|
-
elm.select2('val', controller.$viewValue);
|
623
|
-
// Refresh angular to remove the superfluous option
|
624
|
-
elm.trigger('change');
|
625
|
-
});
|
626
|
-
});
|
627
|
-
}
|
628
|
-
|
629
|
-
if (!isSelect) {
|
630
|
-
// Set the view and model value and update the angular template manually for the ajax/multiple select2.
|
631
|
-
elm.bind("change", function () {
|
632
|
-
scope.$apply(function () {
|
633
|
-
controller.$setViewValue(elm.select2('data'));
|
634
|
-
});
|
635
|
-
});
|
636
|
-
|
637
|
-
if (opts.initSelection) {
|
638
|
-
var initSelection = opts.initSelection;
|
639
|
-
opts.initSelection = function (element, callback) {
|
640
|
-
initSelection(element, function (value) {
|
641
|
-
controller.$setViewValue(value);
|
642
|
-
callback(value);
|
643
|
-
});
|
644
|
-
};
|
645
|
-
}
|
646
|
-
}
|
647
|
-
}
|
648
|
-
|
649
|
-
attrs.$observe('disabled', function (value) {
|
650
|
-
elm.select2(value && 'disable' || 'enable');
|
651
|
-
});
|
652
|
-
|
653
|
-
scope.$watch(attrs.ngMultiple, function(newVal) {
|
654
|
-
elm.select2(opts);
|
655
753
|
});
|
656
|
-
|
657
|
-
// Set initial value since Angular doesn't
|
658
|
-
elm.val(scope.$eval(attrs.ngModel));
|
659
|
-
|
660
|
-
// Initialize the plugin late so that the injected DOM does not disrupt the template compiler
|
661
|
-
setTimeout(function () {
|
662
|
-
elm.select2(opts);
|
663
|
-
});
|
664
|
-
};
|
665
|
-
}
|
666
|
-
};
|
667
|
-
}]);
|
668
|
-
|
669
|
-
/*global angular, CodeMirror, Error*/
|
670
|
-
/**
|
671
|
-
* Binds a CodeMirror widget to a <textarea> element.
|
672
|
-
*/
|
673
|
-
angular.module('ui.directives').directive('uiCodemirror', ['ui.config', '$parse', function (uiConfig, $parse) {
|
674
|
-
'use strict';
|
675
|
-
|
676
|
-
uiConfig.codemirror = uiConfig.codemirror || {};
|
677
|
-
return {
|
678
|
-
require: 'ngModel',
|
679
|
-
link: function (scope, elm, attrs, ngModel) {
|
680
|
-
// Only works on textareas
|
681
|
-
if (!elm.is('textarea')) {
|
682
|
-
throw new Error('ui-codemirror can only be applied to a textarea element');
|
683
754
|
}
|
755
|
+
};
|
756
|
+
}
|
757
|
+
]);
|
684
758
|
|
685
|
-
var codemirror;
|
686
|
-
// This is the method that we use to get the value of the ui-codemirror attribute expression.
|
687
|
-
var uiCodemirrorGet = $parse(attrs.uiCodemirror);
|
688
|
-
// This method will be called whenever the code mirror widget content changes
|
689
|
-
var onChangeHandler = function (ed) {
|
690
|
-
// We only update the model if the value has changed - this helps get around a little problem where $render triggers a change despite already being inside a $apply loop.
|
691
|
-
var newValue = ed.getValue();
|
692
|
-
if (newValue !== ngModel.$viewValue) {
|
693
|
-
ngModel.$setViewValue(newValue);
|
694
|
-
scope.$apply();
|
695
|
-
}
|
696
|
-
};
|
697
|
-
// Create and wire up a new code mirror widget (unwiring a previous one if necessary)
|
698
|
-
var updateCodeMirror = function (options) {
|
699
|
-
// Merge together the options from the uiConfig and the attribute itself with the onChange event above.
|
700
|
-
options = angular.extend({}, options, uiConfig.codemirror);
|
701
|
-
|
702
|
-
// We actually want to run both handlers if the user has provided their own onChange handler.
|
703
|
-
var userOnChange = options.onChange;
|
704
|
-
if (userOnChange) {
|
705
|
-
options.onChange = function (ed) {
|
706
|
-
onChangeHandler(ed);
|
707
|
-
userOnChange(ed);
|
708
|
-
};
|
709
|
-
} else {
|
710
|
-
options.onChange = onChangeHandler;
|
711
|
-
}
|
712
|
-
|
713
|
-
// If there is a codemirror widget for this element already then we need to unwire if first
|
714
|
-
if (codemirror) {
|
715
|
-
codemirror.toTextArea();
|
716
|
-
}
|
717
|
-
// Create the new codemirror widget
|
718
|
-
codemirror = CodeMirror.fromTextArea(elm[0], options);
|
719
|
-
};
|
720
|
-
|
721
|
-
// Initialize the code mirror widget
|
722
|
-
updateCodeMirror(uiCodemirrorGet());
|
723
|
-
|
724
|
-
// Now watch to see if the codemirror attribute gets updated
|
725
|
-
scope.$watch(uiCodemirrorGet, updateCodeMirror, true);
|
726
|
-
|
727
|
-
// CodeMirror expects a string, so make sure it gets one.
|
728
|
-
// This does not change the model.
|
729
|
-
ngModel.$formatters.push(function (value) {
|
730
|
-
if (angular.isUndefined(value) || value === null) {
|
731
|
-
return '';
|
732
|
-
}
|
733
|
-
else if (angular.isObject(value) || angular.isArray(value)) {
|
734
|
-
throw new Error('ui-codemirror cannot use an object or an array as a model');
|
735
|
-
}
|
736
|
-
return value;
|
737
|
-
});
|
738
|
-
|
739
|
-
// Override the ngModelController $render method, which is what gets called when the model is updated.
|
740
|
-
// This takes care of the synchronizing the codeMirror element with the underlying model, in the case that it is changed by something else.
|
741
|
-
ngModel.$render = function () {
|
742
|
-
codemirror.setValue(ngModel.$viewValue);
|
743
|
-
};
|
744
|
-
}
|
745
|
-
};
|
746
|
-
}]);
|
747
759
|
/**
|
748
|
-
*
|
760
|
+
* Add a clear button to form inputs to reset their value
|
749
761
|
*/
|
750
|
-
angular.module('ui.directives').directive('
|
751
|
-
|
762
|
+
angular.module('ui.directives').directive('uiReset', ['ui.config', function (uiConfig) {
|
763
|
+
var resetValue = null;
|
764
|
+
if (uiConfig.reset !== undefined)
|
765
|
+
resetValue = uiConfig.reset;
|
752
766
|
return {
|
753
767
|
require: 'ngModel',
|
754
|
-
link: function (scope, elm, attrs,
|
755
|
-
var
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
handle_event_callback: function (e) {
|
768
|
-
if (this.isDirty()) {
|
769
|
-
this.save();
|
770
|
-
ngModel.$setViewValue(elm.val());
|
771
|
-
if (!scope.$$phase)
|
772
|
-
scope.$apply();
|
773
|
-
}
|
774
|
-
return true; // Continue handling
|
775
|
-
},
|
776
|
-
// Update model when calling setContent (such as from the source editor popup)
|
777
|
-
setup: function (ed) {
|
778
|
-
ed.onSetContent.add(function (ed, o) {
|
779
|
-
if (ed.isDirty()) {
|
780
|
-
ed.save();
|
781
|
-
ngModel.$setViewValue(elm.val());
|
782
|
-
if (!scope.$$phase)
|
783
|
-
scope.$apply();
|
784
|
-
}
|
785
|
-
});
|
786
|
-
}
|
787
|
-
};
|
788
|
-
if (attrs.uiTinymce) {
|
789
|
-
expression = scope.$eval(attrs.uiTinymce);
|
790
|
-
} else {
|
791
|
-
expression = {};
|
792
|
-
}
|
793
|
-
angular.extend(options, uiConfig.tinymce, expression);
|
794
|
-
setTimeout(function () {
|
795
|
-
elm.tinymce(options);
|
768
|
+
link: function (scope, elm, attrs, ctrl) {
|
769
|
+
var aElement;
|
770
|
+
aElement = angular.element('<a class="ui-reset" />');
|
771
|
+
elm.wrap('<span class="ui-resetwrap" />').after(aElement);
|
772
|
+
aElement.bind('click', function (e) {
|
773
|
+
e.preventDefault();
|
774
|
+
scope.$apply(function () {
|
775
|
+
if (attrs.uiReset)
|
776
|
+
ctrl.$setViewValue(scope.$eval(attrs.uiReset));
|
777
|
+
else
|
778
|
+
ctrl.$setViewValue(resetValue);
|
779
|
+
ctrl.$render();
|
780
|
+
});
|
796
781
|
});
|
797
782
|
}
|
798
|
-
};
|
799
|
-
}]);
|
800
|
-
|
801
|
-
|
802
|
-
*
|
803
|
-
* Originally created by @tigbro, for the @jquery-mobile-angular-adapter
|
804
|
-
* https://github.com/tigbro/jquery-mobile-angular-adapter
|
783
|
+
};
|
784
|
+
}]);
|
785
|
+
|
786
|
+
/**
|
787
|
+
* Set a $uiRoute boolean to see if the current route matches
|
805
788
|
*/
|
806
|
-
angular.module('ui.directives').directive('
|
789
|
+
angular.module('ui.directives').directive('uiRoute', ['$location', '$parse', function ($location, $parse) {
|
807
790
|
return {
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
791
|
+
restrict: 'AC',
|
792
|
+
compile: function(tElement, tAttrs) {
|
793
|
+
var useProperty;
|
794
|
+
if (tAttrs.uiRoute) {
|
795
|
+
useProperty = 'uiRoute';
|
796
|
+
} else if (tAttrs.ngHref) {
|
797
|
+
useProperty = 'ngHref';
|
798
|
+
} else if (tAttrs.href) {
|
799
|
+
useProperty = 'href';
|
800
|
+
} else {
|
801
|
+
throw new Error('uiRoute missing a route or href property on ' + tElement[0]);
|
802
|
+
}
|
803
|
+
return function ($scope, elm, attrs) {
|
804
|
+
var modelSetter = $parse(attrs.ngModel || attrs.routeModel || '$uiRoute').assign;
|
805
|
+
var watcher = angular.noop;
|
806
|
+
|
807
|
+
// Used by href and ngHref
|
808
|
+
function staticWatcher(newVal) {
|
809
|
+
if ((hash = newVal.indexOf('#')) > -1)
|
810
|
+
newVal = newVal.substr(hash + 1);
|
811
|
+
watcher = function watchHref() {
|
812
|
+
modelSetter($scope, ($location.path().indexOf(newVal) > -1));
|
813
|
+
};
|
814
|
+
watcher();
|
815
|
+
}
|
816
|
+
// Used by uiRoute
|
817
|
+
function regexWatcher(newVal) {
|
818
|
+
if ((hash = newVal.indexOf('#')) > -1)
|
819
|
+
newVal = newVal.substr(hash + 1);
|
820
|
+
watcher = function watchRegex() {
|
821
|
+
var regexp = new RegExp('^' + newVal + '$', ['i']);
|
822
|
+
modelSetter($scope, regexp.test($location.path()));
|
823
|
+
};
|
824
|
+
watcher();
|
825
|
+
}
|
826
|
+
|
827
|
+
switch (useProperty) {
|
828
|
+
case 'uiRoute':
|
829
|
+
// if uiRoute={{}} this will be undefined, otherwise it will have a value and $observe() never gets triggered
|
830
|
+
if (attrs.uiRoute)
|
831
|
+
regexWatcher(attrs.uiRoute);
|
832
|
+
else
|
833
|
+
attrs.$observe('uiRoute', regexWatcher);
|
834
|
+
break;
|
835
|
+
case 'ngHref':
|
836
|
+
// Setup watcher() every time ngHref changes
|
837
|
+
if (attrs.ngHref)
|
838
|
+
staticWatcher(attrs.ngHref);
|
839
|
+
else
|
840
|
+
attrs.$observe('ngHref', staticWatcher);
|
841
|
+
break;
|
842
|
+
case 'href':
|
843
|
+
// Setup watcher()
|
844
|
+
staticWatcher(attrs.href);
|
845
|
+
}
|
846
|
+
|
847
|
+
$scope.$on('$routeChangeSuccess', function(){
|
848
|
+
watcher();
|
837
849
|
});
|
838
|
-
}
|
850
|
+
}
|
839
851
|
}
|
840
852
|
};
|
841
853
|
}]);
|
854
|
+
|
842
855
|
/*global angular, $, document*/
|
843
856
|
/**
|
844
857
|
* Adds a 'ui-scrollfix' class to the element when the page scrolls past it's position.
|
@@ -879,69 +892,131 @@ angular.module('ui.directives').directive('uiScrollfix', ['$window', function ($
|
|
879
892
|
};
|
880
893
|
}]);
|
881
894
|
|
882
|
-
|
883
|
-
*
|
884
|
-
*
|
885
|
-
*
|
886
|
-
*
|
887
|
-
*
|
888
|
-
|
889
|
-
|
895
|
+
/**
|
896
|
+
* Enhanced Select2 Dropmenus
|
897
|
+
*
|
898
|
+
* @AJAX Mode - When in this mode, your value will be an object (or array of objects) of the data used by Select2
|
899
|
+
* This change is so that you do not have to do an additional query yourself on top of Select2's own query
|
900
|
+
* @params [options] {object} The configuration options passed to $.fn.select2(). Refer to the documentation
|
901
|
+
*/
|
902
|
+
angular.module('ui.directives').directive('uiSelect2', ['ui.config', '$timeout', function (uiConfig, $timeout) {
|
903
|
+
var options = {};
|
904
|
+
if (uiConfig.select2) {
|
905
|
+
angular.extend(options, uiConfig.select2);
|
906
|
+
}
|
907
|
+
return {
|
908
|
+
require: '?ngModel',
|
909
|
+
compile: function (tElm, tAttrs) {
|
910
|
+
var watch,
|
911
|
+
repeatOption,
|
912
|
+
repeatAttr,
|
913
|
+
isSelect = tElm.is('select'),
|
914
|
+
isMultiple = (tAttrs.multiple !== undefined);
|
890
915
|
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
916
|
+
// Enable watching of the options dataset if in use
|
917
|
+
if (tElm.is('select')) {
|
918
|
+
repeatOption = tElm.find('option[ng-repeat], option[data-ng-repeat]');
|
919
|
+
|
920
|
+
if (repeatOption.length) {
|
921
|
+
repeatAttr = repeatOption.attr('ng-repeat') || repeatOption.attr('data-ng-repeat');
|
922
|
+
watch = jQuery.trim(repeatAttr.split('|')[0]).split(' ').pop();
|
923
|
+
}
|
924
|
+
}
|
925
|
+
|
926
|
+
return function (scope, elm, attrs, controller) {
|
927
|
+
// instance-specific options
|
928
|
+
var opts = angular.extend({}, options, scope.$eval(attrs.uiSelect2));
|
929
|
+
|
930
|
+
if (isSelect) {
|
931
|
+
// Use <select multiple> instead
|
932
|
+
delete opts.multiple;
|
933
|
+
delete opts.initSelection;
|
934
|
+
} else if (isMultiple) {
|
935
|
+
opts.multiple = true;
|
936
|
+
}
|
937
|
+
|
938
|
+
if (controller) {
|
939
|
+
// Watch the model for programmatic changes
|
940
|
+
controller.$render = function () {
|
941
|
+
if (isSelect) {
|
942
|
+
elm.select2('val', controller.$modelValue);
|
943
|
+
} else {
|
944
|
+
if (isMultiple) {
|
945
|
+
if (!controller.$modelValue) {
|
946
|
+
elm.select2('data', []);
|
947
|
+
} else if (angular.isArray(controller.$modelValue)) {
|
948
|
+
elm.select2('data', controller.$modelValue);
|
949
|
+
} else {
|
950
|
+
elm.select2('val', controller.$modelValue);
|
951
|
+
}
|
952
|
+
} else {
|
953
|
+
if (angular.isObject(controller.$modelValue)) {
|
954
|
+
elm.select2('data', controller.$modelValue);
|
955
|
+
} else {
|
956
|
+
elm.select2('val', controller.$modelValue);
|
957
|
+
}
|
925
958
|
}
|
926
|
-
else{
|
927
|
-
expression = {};
|
928
|
-
}
|
929
|
-
//extend the options to suite the custom directive.
|
930
|
-
angular.extend(options, uiConfig.uiCalendar, expression);
|
931
|
-
//call fullCalendar from an empty html tag, to keep angular happy.
|
932
|
-
elm.html('').fullCalendar(options);
|
933
959
|
}
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
{
|
939
|
-
|
940
|
-
|
941
|
-
|
960
|
+
};
|
961
|
+
|
962
|
+
// Watch the options dataset for changes
|
963
|
+
if (watch) {
|
964
|
+
scope.$watch(watch, function (newVal, oldVal, scope) {
|
965
|
+
if (!newVal) return;
|
966
|
+
// Delayed so that the options have time to be rendered
|
967
|
+
$timeout(function () {
|
968
|
+
elm.select2('val', controller.$viewValue);
|
969
|
+
// Refresh angular to remove the superfluous option
|
970
|
+
elm.trigger('change');
|
971
|
+
});
|
972
|
+
});
|
973
|
+
}
|
974
|
+
|
975
|
+
if (!isSelect) {
|
976
|
+
// Set the view and model value and update the angular template manually for the ajax/multiple select2.
|
977
|
+
elm.bind("change", function () {
|
978
|
+
scope.$apply(function () {
|
979
|
+
controller.$setViewValue(elm.select2('data'));
|
980
|
+
});
|
981
|
+
});
|
982
|
+
|
983
|
+
if (opts.initSelection) {
|
984
|
+
var initSelection = opts.initSelection;
|
985
|
+
opts.initSelection = function (element, callback) {
|
986
|
+
initSelection(element, function (value) {
|
987
|
+
controller.$setViewValue(value);
|
988
|
+
callback(value);
|
989
|
+
});
|
990
|
+
};
|
991
|
+
}
|
992
|
+
}
|
942
993
|
}
|
943
|
-
|
994
|
+
|
995
|
+
attrs.$observe('disabled', function (value) {
|
996
|
+
elm.select2(value && 'disable' || 'enable');
|
997
|
+
});
|
998
|
+
|
999
|
+
if (attrs.ngMultiple) {
|
1000
|
+
scope.$watch(attrs.ngMultiple, function(newVal) {
|
1001
|
+
elm.select2(opts);
|
1002
|
+
});
|
1003
|
+
}
|
1004
|
+
|
1005
|
+
// Set initial value since Angular doesn't
|
1006
|
+
elm.val(scope.$eval(attrs.ngModel));
|
1007
|
+
|
1008
|
+
// Initialize the plugin late so that the injected DOM does not disrupt the template compiler
|
1009
|
+
$timeout(function () {
|
1010
|
+
elm.select2(opts);
|
1011
|
+
// Not sure if I should just check for !isSelect OR if I should check for 'tags' key
|
1012
|
+
if (!opts.initSelection && !isSelect)
|
1013
|
+
controller.$setViewValue(elm.select2('data'));
|
1014
|
+
});
|
1015
|
+
};
|
1016
|
+
}
|
1017
|
+
};
|
944
1018
|
}]);
|
1019
|
+
|
945
1020
|
/**
|
946
1021
|
* uiShow Directive
|
947
1022
|
*
|
@@ -1004,189 +1079,237 @@ angular.module('ui.directives').directive('uiShow', [function () {
|
|
1004
1079
|
}]);
|
1005
1080
|
|
1006
1081
|
/*
|
1007
|
-
|
1008
|
-
*/
|
1009
|
-
angular.module('ui.directives').directive('uiCurrency', ['ui.config', 'currencyFilter' , function (uiConfig, currencyFilter) {
|
1010
|
-
var options = {
|
1011
|
-
pos: 'ui-currency-pos',
|
1012
|
-
neg: 'ui-currency-neg',
|
1013
|
-
zero: 'ui-currency-zero'
|
1014
|
-
};
|
1015
|
-
if (uiConfig.currency) {
|
1016
|
-
angular.extend(options, uiConfig.currency);
|
1017
|
-
}
|
1018
|
-
return {
|
1019
|
-
restrict: 'EAC',
|
1020
|
-
require: 'ngModel',
|
1021
|
-
link: function (scope, element, attrs, controller) {
|
1022
|
-
var opts, // instance-specific options
|
1023
|
-
renderview,
|
1024
|
-
value;
|
1082
|
+
jQuery UI Sortable plugin wrapper
|
1025
1083
|
|
1026
|
-
|
1084
|
+
@param [ui-sortable] {object} Options to pass to $.fn.sortable() merged onto ui.config
|
1085
|
+
*/
|
1086
|
+
angular.module('ui.directives').directive('uiSortable', [
|
1087
|
+
'ui.config', function(uiConfig) {
|
1088
|
+
return {
|
1089
|
+
require: '?ngModel',
|
1090
|
+
link: function(scope, element, attrs, ngModel) {
|
1091
|
+
var onReceive, onRemove, onStart, onUpdate, opts, _receive, _remove, _start, _update;
|
1027
1092
|
|
1028
|
-
|
1029
|
-
var num;
|
1030
|
-
num = viewvalue * 1;
|
1031
|
-
if (num > 0) {
|
1032
|
-
element.addClass(opts.pos);
|
1033
|
-
} else {
|
1034
|
-
element.removeClass(opts.pos);
|
1035
|
-
}
|
1036
|
-
if (num < 0) {
|
1037
|
-
element.addClass(opts.neg);
|
1038
|
-
} else {
|
1039
|
-
element.removeClass(opts.neg);
|
1040
|
-
}
|
1041
|
-
if (num === 0) {
|
1042
|
-
element.addClass(opts.zero);
|
1043
|
-
} else {
|
1044
|
-
element.removeClass(opts.zero);
|
1045
|
-
}
|
1046
|
-
if (viewvalue === '') {
|
1047
|
-
element.text('');
|
1048
|
-
} else {
|
1049
|
-
element.text(currencyFilter(num, opts.symbol));
|
1050
|
-
}
|
1051
|
-
return true;
|
1052
|
-
};
|
1093
|
+
opts = angular.extend({}, uiConfig.sortable, scope.$eval(attrs.uiSortable));
|
1053
1094
|
|
1054
|
-
|
1055
|
-
value = controller.$viewValue;
|
1056
|
-
element.val(value);
|
1057
|
-
renderview(value);
|
1058
|
-
};
|
1095
|
+
if (ngModel) {
|
1059
1096
|
|
1060
|
-
|
1061
|
-
|
1062
|
-
}
|
1097
|
+
ngModel.$render = function() {
|
1098
|
+
element.sortable( "refresh" );
|
1099
|
+
};
|
1063
1100
|
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1101
|
+
onStart = function(e, ui) {
|
1102
|
+
// Save position of dragged item
|
1103
|
+
ui.item.sortable = { index: ui.item.index() };
|
1104
|
+
};
|
1067
1105
|
|
1068
|
-
|
1069
|
-
|
1106
|
+
onUpdate = function(e, ui) {
|
1107
|
+
// For some reason the reference to ngModel in stop() is wrong
|
1108
|
+
ui.item.sortable.resort = ngModel;
|
1109
|
+
};
|
1070
1110
|
|
1071
|
-
|
1111
|
+
onReceive = function(e, ui) {
|
1112
|
+
ui.item.sortable.relocate = true;
|
1113
|
+
// added item to array into correct position and set up flag
|
1114
|
+
ngModel.$modelValue.splice(ui.item.index(), 0, ui.item.sortable.moved);
|
1115
|
+
};
|
1072
1116
|
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
require:'?ngModel',
|
1082
|
-
link:function (scope, element, attrs, controller) {
|
1083
|
-
var getOptions = function () {
|
1084
|
-
return angular.extend({}, uiConfig.date, scope.$eval(attrs.uiDate));
|
1085
|
-
};
|
1086
|
-
var initDateWidget = function () {
|
1087
|
-
var opts = getOptions();
|
1117
|
+
onRemove = function(e, ui) {
|
1118
|
+
// copy data into item
|
1119
|
+
if (ngModel.$modelValue.length === 1) {
|
1120
|
+
ui.item.sortable.moved = ngModel.$modelValue.splice(0, 1)[0];
|
1121
|
+
} else {
|
1122
|
+
ui.item.sortable.moved = ngModel.$modelValue.splice(ui.item.sortable.index, 1)[0];
|
1123
|
+
}
|
1124
|
+
};
|
1088
1125
|
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1126
|
+
onStop = function(e, ui) {
|
1127
|
+
// digest all prepared changes
|
1128
|
+
if (ui.item.sortable.resort && !ui.item.sortable.relocate) {
|
1129
|
+
|
1130
|
+
// Fetch saved and current position of dropped element
|
1131
|
+
var end, start;
|
1132
|
+
start = ui.item.sortable.index;
|
1133
|
+
end = ui.item.index();
|
1134
|
+
if (start < end)
|
1135
|
+
end--;
|
1136
|
+
|
1137
|
+
// Reorder array and apply change to scope
|
1138
|
+
ui.item.sortable.resort.$modelValue.splice(end, 0, ui.item.sortable.resort.$modelValue.splice(start, 1)[0]);
|
1139
|
+
|
1140
|
+
}
|
1141
|
+
if (ui.item.sortable.resort || ui.item.sortable.relocate) {
|
1142
|
+
scope.$apply();
|
1143
|
+
}
|
1144
|
+
};
|
1145
|
+
|
1146
|
+
// If user provided 'start' callback compose it with onStart function
|
1147
|
+
_start = opts.start;
|
1148
|
+
opts.start = function(e, ui) {
|
1149
|
+
onStart(e, ui);
|
1150
|
+
if (typeof _start === "function")
|
1151
|
+
_start(e, ui);
|
1152
|
+
};
|
1153
|
+
|
1154
|
+
// If user provided 'start' callback compose it with onStart function
|
1155
|
+
_stop = opts.stop;
|
1156
|
+
opts.stop = function(e, ui) {
|
1157
|
+
onStop(e, ui);
|
1158
|
+
if (typeof _stop === "function")
|
1159
|
+
_stop(e, ui);
|
1160
|
+
};
|
1161
|
+
|
1162
|
+
// If user provided 'update' callback compose it with onUpdate function
|
1163
|
+
_update = opts.update;
|
1164
|
+
opts.update = function(e, ui) {
|
1165
|
+
onUpdate(e, ui);
|
1166
|
+
if (typeof _update === "function")
|
1167
|
+
_update(e, ui);
|
1097
1168
|
};
|
1098
|
-
if (opts.onSelect) {
|
1099
|
-
// Caller has specified onSelect, so call this as well as updating the model
|
1100
|
-
var userHandler = opts.onSelect;
|
1101
|
-
opts.onSelect = function (value, picker) {
|
1102
|
-
updateModel();
|
1103
|
-
return userHandler(value, picker);
|
1104
|
-
};
|
1105
|
-
} else {
|
1106
|
-
// No onSelect already specified so just update the model
|
1107
|
-
opts.onSelect = updateModel;
|
1108
|
-
}
|
1109
|
-
// In case the user changes the text directly in the input box
|
1110
|
-
element.bind('change', updateModel);
|
1111
1169
|
|
1112
|
-
//
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1170
|
+
// If user provided 'receive' callback compose it with onReceive function
|
1171
|
+
_receive = opts.receive;
|
1172
|
+
opts.receive = function(e, ui) {
|
1173
|
+
onReceive(e, ui);
|
1174
|
+
if (typeof _receive === "function")
|
1175
|
+
_receive(e, ui);
|
1176
|
+
};
|
1177
|
+
|
1178
|
+
// If user provided 'remove' callback compose it with onRemove function
|
1179
|
+
_remove = opts.remove;
|
1180
|
+
opts.remove = function(e, ui) {
|
1181
|
+
onRemove(e, ui);
|
1182
|
+
if (typeof _remove === "function")
|
1183
|
+
_remove(e, ui);
|
1119
1184
|
};
|
1120
1185
|
}
|
1121
|
-
// If we don't destroy the old one it doesn't update properly when the config changes
|
1122
|
-
element.datepicker('destroy');
|
1123
|
-
// Create the new datepicker widget
|
1124
|
-
element.datepicker(opts);
|
1125
|
-
// Force a render to override whatever is in the input text box
|
1126
|
-
controller.$render();
|
1127
|
-
};
|
1128
|
-
// Watch for changes to the directives options
|
1129
|
-
scope.$watch(getOptions, initDateWidget, true);
|
1130
|
-
}
|
1131
|
-
};
|
1132
|
-
}
|
1133
|
-
])
|
1134
1186
|
|
1135
|
-
|
1136
|
-
|
1137
|
-
require:'ngModel',
|
1138
|
-
link: function(scope, element, attrs, modelCtrl) {
|
1139
|
-
if ( attrs.uiDateFormat === '' ) {
|
1140
|
-
// Default to ISO formatting
|
1141
|
-
modelCtrl.$formatters.push(function(value) {
|
1142
|
-
if (angular.isString(value) ) {
|
1143
|
-
return new Date(value);
|
1144
|
-
}
|
1145
|
-
});
|
1146
|
-
modelCtrl.$parsers.push(function(value){
|
1147
|
-
if (value) {
|
1148
|
-
return value.toISOString();
|
1149
|
-
}
|
1150
|
-
});
|
1151
|
-
} else {
|
1152
|
-
var format = attrs.uiDateFormat;
|
1153
|
-
// Use the datepicker with the attribute value as the format string to convert to and from a string
|
1154
|
-
modelCtrl.$formatters.push(function(value) {
|
1155
|
-
if (angular.isString(value) ) {
|
1156
|
-
return $.datepicker.parseDate(format, value);
|
1157
|
-
}
|
1158
|
-
});
|
1159
|
-
modelCtrl.$parsers.push(function(value){
|
1160
|
-
if (value) {
|
1161
|
-
return $.datepicker.formatDate(format, value);
|
1162
|
-
}
|
1163
|
-
});
|
1187
|
+
// Create sortable
|
1188
|
+
element.sortable(opts);
|
1164
1189
|
}
|
1165
|
-
}
|
1166
|
-
}
|
1167
|
-
|
1168
|
-
}]);
|
1190
|
+
};
|
1191
|
+
}
|
1192
|
+
]);
|
1169
1193
|
|
1170
1194
|
/**
|
1171
|
-
*
|
1172
|
-
* @param text {string} haystack to search through
|
1173
|
-
* @param search {string} needle to search for
|
1174
|
-
* @param [caseSensitive] {boolean} optional boolean to use case-sensitive searching
|
1195
|
+
* Binds a TinyMCE widget to <textarea> elements.
|
1175
1196
|
*/
|
1176
|
-
angular.module('ui.
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1197
|
+
angular.module('ui.directives').directive('uiTinymce', ['ui.config', function (uiConfig) {
|
1198
|
+
uiConfig.tinymce = uiConfig.tinymce || {};
|
1199
|
+
return {
|
1200
|
+
require: 'ngModel',
|
1201
|
+
link: function (scope, elm, attrs, ngModel) {
|
1202
|
+
var expression,
|
1203
|
+
options = {
|
1204
|
+
// Update model on button click
|
1205
|
+
onchange_callback: function (inst) {
|
1206
|
+
if (inst.isDirty()) {
|
1207
|
+
inst.save();
|
1208
|
+
ngModel.$setViewValue(elm.val());
|
1209
|
+
if (!scope.$$phase)
|
1210
|
+
scope.$apply();
|
1211
|
+
}
|
1212
|
+
},
|
1213
|
+
// Update model on keypress
|
1214
|
+
handle_event_callback: function (e) {
|
1215
|
+
if (this.isDirty()) {
|
1216
|
+
this.save();
|
1217
|
+
ngModel.$setViewValue(elm.val());
|
1218
|
+
if (!scope.$$phase)
|
1219
|
+
scope.$apply();
|
1220
|
+
}
|
1221
|
+
return true; // Continue handling
|
1222
|
+
},
|
1223
|
+
// Update model when calling setContent (such as from the source editor popup)
|
1224
|
+
setup: function (ed) {
|
1225
|
+
ed.onSetContent.add(function (ed, o) {
|
1226
|
+
if (ed.isDirty()) {
|
1227
|
+
ed.save();
|
1228
|
+
ngModel.$setViewValue(elm.val());
|
1229
|
+
if (!scope.$$phase)
|
1230
|
+
scope.$apply();
|
1231
|
+
}
|
1232
|
+
});
|
1233
|
+
}
|
1234
|
+
};
|
1235
|
+
if (attrs.uiTinymce) {
|
1236
|
+
expression = scope.$eval(attrs.uiTinymce);
|
1183
1237
|
} else {
|
1184
|
-
|
1238
|
+
expression = {};
|
1185
1239
|
}
|
1186
|
-
|
1187
|
-
|
1240
|
+
angular.extend(options, uiConfig.tinymce, expression);
|
1241
|
+
setTimeout(function () {
|
1242
|
+
elm.tinymce(options);
|
1243
|
+
});
|
1188
1244
|
}
|
1189
1245
|
};
|
1246
|
+
}]);
|
1247
|
+
|
1248
|
+
/**
|
1249
|
+
* General-purpose validator for ngModel.
|
1250
|
+
* angular.js comes with several built-in validation mechanism for input fields (ngRequired, ngPattern etc.) but using
|
1251
|
+
* an arbitrary validation function requires creation of a custom formatters and / or parsers.
|
1252
|
+
* The ui-validate directive makes it easy to use any function(s) defined in scope as a validator function(s).
|
1253
|
+
* A validator function will trigger validation on both model and input changes.
|
1254
|
+
*
|
1255
|
+
* @example <input ui-validate=" 'myValidatorFunction($value)' ">
|
1256
|
+
* @example <input ui-validate="{ foo : '$value > anotherModel', bar : 'validateFoo($value)' }">
|
1257
|
+
* @example <input ui-validate="{ foo : '$value > anotherModel' }" ui-validate-watch=" 'anotherModel' ">
|
1258
|
+
* @example <input ui-validate="{ foo : '$value > anotherModel', bar : 'validateFoo($value)' }" ui-validate-watch=" { foo : 'anotherModel' } ">
|
1259
|
+
*
|
1260
|
+
* @param ui-validate {string|object literal} If strings is passed it should be a scope's function to be used as a validator.
|
1261
|
+
* If an object literal is passed a key denotes a validation error key while a value should be a validator function.
|
1262
|
+
* In both cases validator function should take a value to validate as its argument and should return true/false indicating a validation result.
|
1263
|
+
*/
|
1264
|
+
angular.module('ui.directives').directive('uiValidate', function () {
|
1265
|
+
|
1266
|
+
return {
|
1267
|
+
restrict: 'A',
|
1268
|
+
require: 'ngModel',
|
1269
|
+
link: function (scope, elm, attrs, ctrl) {
|
1270
|
+
var validateFn, watch, validators = {},
|
1271
|
+
validateExpr = scope.$eval(attrs.uiValidate);
|
1272
|
+
|
1273
|
+
if (!validateExpr) return;
|
1274
|
+
|
1275
|
+
if (angular.isString(validateExpr)) {
|
1276
|
+
validateExpr = { validator: validateExpr };
|
1277
|
+
}
|
1278
|
+
|
1279
|
+
angular.forEach(validateExpr, function (expression, key) {
|
1280
|
+
validateFn = function (valueToValidate) {
|
1281
|
+
if (scope.$eval(expression, { '$value' : valueToValidate })) {
|
1282
|
+
ctrl.$setValidity(key, true);
|
1283
|
+
return valueToValidate;
|
1284
|
+
} else {
|
1285
|
+
ctrl.$setValidity(key, false);
|
1286
|
+
return undefined;
|
1287
|
+
}
|
1288
|
+
};
|
1289
|
+
validators[key] = validateFn;
|
1290
|
+
ctrl.$formatters.push(validateFn);
|
1291
|
+
ctrl.$parsers.push(validateFn);
|
1292
|
+
});
|
1293
|
+
|
1294
|
+
// Support for ui-validate-watch
|
1295
|
+
if (attrs.uiValidateWatch) {
|
1296
|
+
watch = scope.$eval(attrs.uiValidateWatch);
|
1297
|
+
if (angular.isString(watch)) {
|
1298
|
+
scope.$watch(watch, function(){
|
1299
|
+
angular.forEach(validators, function(validatorFn, key){
|
1300
|
+
validatorFn(ctrl.$modelValue);
|
1301
|
+
});
|
1302
|
+
});
|
1303
|
+
} else {
|
1304
|
+
angular.forEach(watch, function(expression, key){
|
1305
|
+
scope.$watch(expression, function(){
|
1306
|
+
validators[key](ctrl.$modelValue);
|
1307
|
+
});
|
1308
|
+
});
|
1309
|
+
}
|
1310
|
+
}
|
1311
|
+
}
|
1312
|
+
};
|
1190
1313
|
});
|
1191
1314
|
|
1192
1315
|
|
@@ -1225,48 +1348,24 @@ angular.module('ui.filters').filter('format', function(){
|
|
1225
1348
|
});
|
1226
1349
|
|
1227
1350
|
/**
|
1228
|
-
*
|
1229
|
-
* @param
|
1230
|
-
|
1231
|
-
|
1232
|
-
* @return {array}
|
1351
|
+
* Wraps the
|
1352
|
+
* @param text {string} haystack to search through
|
1353
|
+
* @param search {string} needle to search for
|
1354
|
+
* @param [caseSensitive] {boolean} optional boolean to use case-sensitive searching
|
1233
1355
|
*/
|
1234
|
-
angular.module('ui.filters').filter('
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1245
|
-
|
1246
|
-
if (angular.isObject(item) && angular.isString(filterOn)) {
|
1247
|
-
return item[filterOn];
|
1248
|
-
} else {
|
1249
|
-
return item;
|
1250
|
-
}
|
1251
|
-
};
|
1252
|
-
|
1253
|
-
angular.forEach(items, function (item) {
|
1254
|
-
var valueToCheck, isDuplicate = false;
|
1255
|
-
|
1256
|
-
for (var i = 0; i < newItems.length; i++) {
|
1257
|
-
if (angular.equals(extractValueToCompare(newItems[i]), extractValueToCompare(item))) {
|
1258
|
-
isDuplicate = true;
|
1259
|
-
break;
|
1260
|
-
}
|
1261
|
-
}
|
1262
|
-
if (!isDuplicate) {
|
1263
|
-
newItems.push(item);
|
1264
|
-
}
|
1265
|
-
|
1266
|
-
});
|
1267
|
-
items = newItems;
|
1356
|
+
angular.module('ui.filters').filter('highlight', function () {
|
1357
|
+
return function (text, search, caseSensitive) {
|
1358
|
+
if (search || angular.isNumber(search)) {
|
1359
|
+
text = text.toString();
|
1360
|
+
search = search.toString();
|
1361
|
+
if (caseSensitive) {
|
1362
|
+
return text.split(search).join('<span class="ui-match">' + search + '</span>');
|
1363
|
+
} else {
|
1364
|
+
return text.replace(new RegExp(search, 'gi'), '<span class="ui-match">$&</span>');
|
1365
|
+
}
|
1366
|
+
} else {
|
1367
|
+
return text;
|
1268
1368
|
}
|
1269
|
-
return items;
|
1270
1369
|
};
|
1271
1370
|
});
|
1272
1371
|
|
@@ -1314,3 +1413,49 @@ angular.module('ui.filters').filter('inflector', function () {
|
|
1314
1413
|
}
|
1315
1414
|
};
|
1316
1415
|
});
|
1416
|
+
|
1417
|
+
/**
|
1418
|
+
* Filters out all duplicate items from an array by checking the specified key
|
1419
|
+
* @param [key] {string} the name of the attribute of each object to compare for uniqueness
|
1420
|
+
if the key is empty, the entire object will be compared
|
1421
|
+
if the key === false then no filtering will be performed
|
1422
|
+
* @return {array}
|
1423
|
+
*/
|
1424
|
+
angular.module('ui.filters').filter('unique', function () {
|
1425
|
+
|
1426
|
+
return function (items, filterOn) {
|
1427
|
+
|
1428
|
+
if (filterOn === false) {
|
1429
|
+
return items;
|
1430
|
+
}
|
1431
|
+
|
1432
|
+
if ((filterOn || angular.isUndefined(filterOn)) && angular.isArray(items)) {
|
1433
|
+
var hashCheck = {}, newItems = [];
|
1434
|
+
|
1435
|
+
var extractValueToCompare = function (item) {
|
1436
|
+
if (angular.isObject(item) && angular.isString(filterOn)) {
|
1437
|
+
return item[filterOn];
|
1438
|
+
} else {
|
1439
|
+
return item;
|
1440
|
+
}
|
1441
|
+
};
|
1442
|
+
|
1443
|
+
angular.forEach(items, function (item) {
|
1444
|
+
var valueToCheck, isDuplicate = false;
|
1445
|
+
|
1446
|
+
for (var i = 0; i < newItems.length; i++) {
|
1447
|
+
if (angular.equals(extractValueToCompare(newItems[i]), extractValueToCompare(item))) {
|
1448
|
+
isDuplicate = true;
|
1449
|
+
break;
|
1450
|
+
}
|
1451
|
+
}
|
1452
|
+
if (!isDuplicate) {
|
1453
|
+
newItems.push(item);
|
1454
|
+
}
|
1455
|
+
|
1456
|
+
});
|
1457
|
+
items = newItems;
|
1458
|
+
}
|
1459
|
+
return items;
|
1460
|
+
};
|
1461
|
+
});
|