fuelux-rails 2.4.2 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,3 @@
1
1
  module FueluxRails
2
- VERSION = "2.4.2"
2
+ VERSION = "2.5.0"
3
3
  end
@@ -17,7 +17,7 @@ module Fuelux
17
17
  else
18
18
  puts <<-EOM
19
19
  Warning:
20
- app/assets/stylesheets/bootstrap_overrides.css.less does not exist
20
+ app/assets/stylesheets/bootstrap_and_overrides.css.less does not exist
21
21
  Run "rails generate bootstrap:install" and then rerun "rails generate fuelux:install"
22
22
  EOM
23
23
  end
@@ -15,12 +15,12 @@ namespace :fuelux_rails do
15
15
  SCRIPTS_REPO = "https://raw.github.com/ExactTarget/fuelux/%s/src/%s.js"
16
16
  SCRIPTS_PATH = "./vendor/assets/javascripts/fuelux/%s.js"
17
17
 
18
- SCRIPTS = %w( util checkbox combobox datagrid intelligent-dropdown pillbox
19
- radio search select spinner tree wizard).freeze
18
+ SCRIPTS = %w( util checkbox combobox datagrid datepicker intelligent-dropdown pillbox preloader
19
+ radio scheduler search select spinner tree wizard).freeze
20
20
 
21
21
  STYLESHEETS_REPO = "https://raw.github.com/ExactTarget/fuelux/%s/src/less/%s.less"
22
22
  STYLESHEETS_PATH = "./vendor/toolkit/fuelux/%s.less"
23
- EXTRA_VARIABLES = "// Tree\n// --------------------------------------------------\n@treeBackgroundHover: #DFEEF5;\n@treeBackgroundSelect: #B9DFF1;\n"
23
+ EXTRA_VARIABLES = "// Tree\n// --------------------------------------------------\n@treeBackgroundHover: #DFEEF5;\n@treeBackgroundSelect: #B9DFF1;\n// Backup Variables\n// --------------------------------------------------\n@white: #FFFFFF;\n@grayLight: #999999;\n@grayLighter: #EEEEEE;\n"
24
24
 
25
25
  def update_javascript(file, tag = 'master')
26
26
  repo = SCRIPTS_REPO % [tag, file]
@@ -72,7 +72,7 @@ namespace :fuelux_rails do
72
72
  doc << original_file.read
73
73
  end
74
74
  lines = IO.readlines(path).map do |line|
75
- line.gsub /url\(\.{2}\/img\/([^\)]+)\)/, "image-url('fuelux/\\1')"
75
+ line.gsub(/url\(\.{2}\/img\/([^\)]+)\)/, "image-url('fuelux/\\1')").gsub /url\(\"?\.{2}\/fonts\/([^\"\)]+)\"?\)/, "font-url('fuelux/\\1')"
76
76
  end
77
77
  File.open(path, 'w') {|doc| doc.puts lines }
78
78
  %(@import "fuelux/#{file}.less";\n)
@@ -0,0 +1,19 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3
+ <svg xmlns="http://www.w3.org/2000/svg">
4
+ <metadata>This SVG font generated by Fontastic.me</metadata>
5
+ <defs>
6
+ <font id="fuelux-preloader" horiz-adv-x="512">
7
+ <font-face font-family="fuelux-preloader" font-weight="500" font-stretch="normal" units-per-em="512" panose-1="2 0 6 9 0 0 0 0 0 0" ascent="480" descent="-32" bbox="3 -29 509 477" underline-thickness="50" underline-position="-100" unicode-range="U+0030-0038"/>
8
+ <missing-glyph/>
9
+ <glyph glyph-name="zero" unicode="0" d="M151 359l-59 59c41 34 92 56 149 59v-83c-34 -3 -65 -15 -90 -35zM3 245c5 56 29 107 64 147l58 -58c-21 -25 -34 -56 -38 -89h-84zM62 60c-34 41 -56 92 -59 149h83c3 -34 15 -65 35 -90zM235 55v-84c-56 5 -107 29 -147 64l58 58c25 -21 56 -34 89 -38zM361 89l59 -59 c-41 -34 -92 -56 -149 -59v83c34 3 65 15 90 35zM509 203c-5 -56 -29 -107 -64 -147l-58 58c21 25 34 56 38 89h84zM277 393v84c56 -5 107 -29 147 -64l-58 -58c-25 21 -56 34 -89 38zM450 388c34 -41 56 -92 59 -149h-83c-3 34 -15 65 -35 90z"/>
10
+ <glyph glyph-name="one" unicode="1" d="M277 393v84c56 -5 107 -29 147 -64l-58 -58c-25 21 -56 34 -89 38z"/>
11
+ <glyph glyph-name="two" unicode="2" d="M450 388c34 -41 56 -92 59 -149h-83c-3 34 -15 65 -35 90z"/>
12
+ <glyph glyph-name="three" unicode="3" d="M509 203c-5 -56 -29 -107 -64 -147l-58 58c21 25 34 56 38 89h84z"/>
13
+ <glyph glyph-name="four" unicode="4" d="M361 89l59 -59c-41 -34 -92 -56 -149 -59v83c34 3 65 15 90 35z"/>
14
+ <glyph glyph-name="five" unicode="5" d="M235 55v-84c-56 5 -107 29 -147 64l58 58c25 -21 56 -34 89 -38z"/>
15
+ <glyph glyph-name="six" unicode="6" d="M62 60c-34 41 -56 92 -59 149h83c3 -34 15 -65 35 -90z"/>
16
+ <glyph glyph-name="seven" unicode="7" d="M3 245c5 56 29 107 64 147l58 -58c-21 -25 -34 -56 -38 -89h-84z"/>
17
+ <glyph glyph-name="eight" unicode="8" d="M151 359l-59 59c41 34 92 56 149 59v-83c-34 -3 -65 -15 -90 -35z"/>
18
+ </font>
19
+ </defs></svg>
@@ -2,9 +2,11 @@
2
2
  //= require fuelux/checkbox
3
3
  //= require fuelux/combobox
4
4
  //= require fuelux/datagrid
5
+ //= require fuelux/datepicker
5
6
  //= require fuelux/intelligent-dropdown
6
7
  //= require fuelux/pillbox
7
8
  //= require fuelux/radio
9
+ //= require fuelux/scheduler
8
10
  //= require fuelux/search
9
11
  //= require fuelux/select
10
12
  //= require fuelux/spinner
@@ -72,17 +72,17 @@
72
72
  },
73
73
 
74
74
  check: function () {
75
- this.$chk.prop('checked', true);
76
- this.setState(this.$chk);
75
+ this.$chk.prop('checked', true);
76
+ this.setState(this.$chk);
77
77
  },
78
78
 
79
79
  uncheck: function () {
80
- this.$chk.prop('checked', false);
81
- this.setState(this.$chk);
80
+ this.$chk.prop('checked', false);
81
+ this.setState(this.$chk);
82
82
  },
83
83
 
84
84
  isChecked: function () {
85
- return this.$chk.is(':checked');
85
+ return this.$chk.is(':checked');
86
86
  }
87
87
  };
88
88
 
@@ -0,0 +1,968 @@
1
+ /*
2
+ * Fuel UX Datepicker
3
+ * https://github.com/ExactTarget/fuelux
4
+ *
5
+ * Copyright (c) 2013 ExactTarget
6
+ * Licensed under the MIT license.
7
+ */
8
+
9
+ !function ($) {
10
+
11
+
12
+ var old = $.fn.datepicker;
13
+ var moment = false;
14
+
15
+ // DATEPICKER CONSTRUCTOR AND PROTOTYPE
16
+
17
+ var Datepicker = function (element, options) {
18
+ this.$element = $(element);
19
+
20
+ this.options = $.extend(true, {}, $.fn.datepicker.defaults, options);
21
+
22
+ this.formatDate = ( Boolean( this.options.createInput ) && Boolean( this.options.createInput.native ) ) ? this.formatNativeDate : this.options.formatDate || this.formatDate;
23
+ this.parseDate = this.options.parseDate || this.parseDate;
24
+ this.blackoutDates = this.options.blackoutDates || this.blackoutDates;
25
+
26
+ // moment set up for parsing input dates
27
+ if( this._checkForMomentJS() ) {
28
+ moment = moment || window.moment; // need to pull in the global moment if they didn't do it via require
29
+ this.moment = true;
30
+ this.momentFormat = this.options.momentConfig.formatCode;
31
+ this.setCulture( this.options.momentConfig.culture );
32
+ }
33
+
34
+ if( this.options.date !== null ) {
35
+ this.date = this.options.date || new Date();
36
+ this.date = this.parseDate( this.date, false );
37
+ this.viewDate = new Date( this.date.valueOf() );
38
+ this.stagedDate = new Date( this.date.valueOf() );
39
+ } else {
40
+ this.date = null;
41
+ this.viewDate = new Date();
42
+ this.stagedDate = new Date();
43
+ }
44
+
45
+ this.inputParsingTarget = null;
46
+
47
+ this.viewDate.setHours( 0,0,0,0 );
48
+ this.stagedDate.setHours( 0,0,0,0 );
49
+
50
+ this.done = false;
51
+
52
+ this.minDate = new Date();
53
+ this.minDate.setDate( this.minDate.getDate() - 1 );
54
+ this.minDate.setHours( 0,0,0,0 );
55
+
56
+ this.maxDate = new Date();
57
+ this.maxDate.setFullYear( this.maxDate.getFullYear() + 10 );
58
+ this.maxDate.setHours( 23,59,59,999 );
59
+
60
+ this.years = this._yearRange( this.viewDate );
61
+
62
+ this.bindingsAdded = false;
63
+
64
+ // OPTIONS
65
+ this.options.dropdownWidth = this.options.dropdownWidth || 170;
66
+ this.options.monthNames = this.options.monthNames || [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ];
67
+ this.options.weekdays = this.options.weekdays || [ "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
68
+
69
+ this.options.showYears = false;
70
+ this.options.showDays = true;
71
+ this.options.showMonths = false;
72
+
73
+ this.options.restrictLastMonth = Boolean( this.options.restrictDateSelection );
74
+ this.options.restrictNextMonth = false;
75
+
76
+ this.months = [
77
+ { abbreviation: this.options.monthNames[0], 'class': '', number: 0 },
78
+ { abbreviation: this.options.monthNames[1], 'class': '', number: 1 },
79
+ { abbreviation: this.options.monthNames[2], 'class': '', number: 2 },
80
+ { abbreviation: this.options.monthNames[3], 'class': '', number: 3 },
81
+ { abbreviation: this.options.monthNames[4], 'class': '', number: 4 },
82
+ { abbreviation: this.options.monthNames[5], 'class': '', number: 5 },
83
+ { abbreviation: this.options.monthNames[6], 'class': '', number: 6 },
84
+ { abbreviation: this.options.monthNames[7], 'class': '', number: 7 },
85
+ { abbreviation: this.options.monthNames[8], 'class': '', number: 8 },
86
+ { abbreviation: this.options.monthNames[9], 'class': '', number: 9 },
87
+ { abbreviation: this.options.monthNames[10], 'class': '', number: 10 },
88
+ { abbreviation: this.options.monthNames[11], 'class': '', number: 11 }
89
+ ];
90
+
91
+ if( Boolean( this.options.createInput ) ) {
92
+ if( typeof this.options.createInput === "boolean" && Boolean( this.options.createInput ) ) {
93
+ this.options.createInput = {};
94
+ }
95
+
96
+ if( typeof this.options.createInput === 'object' && isNaN( this.options.createInput.length ) ) {
97
+ this.options.createInput.inputSize = this.options.createInput.inputSize || 'span3';
98
+ this._renderInput();
99
+ } else {
100
+ throw new Error( 'createInput option needs to be an object or boolean true' );
101
+ }
102
+ } else {
103
+ this._render();
104
+ }
105
+ };
106
+
107
+ Datepicker.prototype = {
108
+
109
+ constructor: Datepicker,
110
+
111
+ // functions that can be called on object
112
+ disable: function() {
113
+ this.$element.find('input, button').attr( 'disabled', true );
114
+ },
115
+
116
+ enable: function() {
117
+ this.$element.find('input, button').attr( 'disabled', false );
118
+ },
119
+
120
+ getFormattedDate: function() {
121
+ return this.formatDate( this.date );
122
+ },
123
+
124
+ getDate: function( options ) {
125
+ if( Boolean( options ) && Boolean( options.unix ) ) {
126
+ return this.date.getTime();
127
+ } else {
128
+ return this.date;
129
+ }
130
+ },
131
+
132
+ setDate: function( date ) {
133
+ this.date = this.parseDate( date, false );
134
+ this.stagedDate = this.date;
135
+ this.viewDate = this.date;
136
+ this._render();
137
+ this.$element.trigger( 'changed', this.date );
138
+ return this.date;
139
+ },
140
+
141
+ getCulture: function() {
142
+ if( Boolean( this.moment ) ) {
143
+ return moment.lang();
144
+ } else {
145
+ throw "moment.js is not available so you cannot use this function";
146
+ }
147
+ },
148
+
149
+ setCulture: function( cultureCode ) {
150
+ if( !Boolean( cultureCode) ) {
151
+ return false;
152
+ }
153
+ if( Boolean( this.moment ) ) {
154
+ moment.lang( cultureCode );
155
+ } else {
156
+ throw "moment.js is not available so you cannot use this function";
157
+ }
158
+ },
159
+
160
+ getFormatCode: function() {
161
+ if( Boolean( this.moment ) ) {
162
+ return this.momentFormat;
163
+ } else {
164
+ throw "moment.js is not available so you cannot use this function";
165
+ }
166
+ },
167
+
168
+ setFormatCode: function( formatCode ) {
169
+ if( !Boolean( formatCode ) ) {
170
+ return false;
171
+ }
172
+ if( Boolean( this.moment ) ) {
173
+ this.momentFormat = formatCode;
174
+ } else {
175
+ throw "moment.js is not available so you cannot use this function";
176
+ }
177
+ },
178
+
179
+ formatDate: function( date ) {
180
+ // if we have moment available use it to format dates. otherwise use default
181
+ if( Boolean( this.moment ) ) {
182
+ return moment( date ).format( this.momentFormat );
183
+ } else {
184
+ // this.pad to is function on extension
185
+ return this.padTwo( date.getMonth() + 1 ) + '-' + this.padTwo( date.getDate() ) + '-' + date.getFullYear();
186
+ }
187
+ },
188
+
189
+ formatNativeDate: function( date ) {
190
+ return date.getFullYear() + '-' + this.padTwo( date.getMonth() + 1 ) + '-' + this.padTwo( date.getDate() );
191
+ },
192
+
193
+ //some code ripped from http://stackoverflow.com/questions/2182246/javascript-dates-in-ie-nan-firefox-chrome-ok
194
+ parseDate: function( date, silent ) {
195
+ // if we have moment, use that to parse the dates
196
+ if( this.moment ) {
197
+ silent = silent || false;
198
+ // if silent is requested (direct user input parsing) return true or false not a date object, otherwise return a date object
199
+ if( silent ) {
200
+ if( moment( date )._d.toString() === "Invalid Date" ) {
201
+ return false;
202
+ } else {
203
+ return true;
204
+ }
205
+ } else {
206
+ return moment( date )._d; //example of using moment for parsing
207
+ }
208
+ } else {
209
+ // if moment isn't present, use previous date parsing strategry
210
+ var dt, isoExp, month, parts;
211
+
212
+ if( Boolean( date) && new Date( date ).toString() !== 'Invalid Date' ) {
213
+ if( typeof( date ) === 'string' ) {
214
+ date = date.split( 'T' )[ 0 ];
215
+ isoExp = /^\s*(\d{4})-(\d\d)-(\d\d)\s*$/;
216
+ dt = new Date( NaN );
217
+ parts = isoExp.exec( date );
218
+
219
+ if( parts ) {
220
+ month = +parts[ 2 ];
221
+ dt.setFullYear( parts[ 1 ], month - 1, parts[ 3 ] );
222
+ if( month !== dt.getMonth() + 1 ) {
223
+ dt.setTime( NaN );
224
+ }
225
+ }
226
+ return dt;
227
+ }
228
+ return new Date( date );
229
+ } else {
230
+ throw new Error( 'could not parse date' );
231
+ }
232
+ }
233
+ },
234
+
235
+ blackoutDates: function( date ) {
236
+ date = date;
237
+ return false;
238
+ },
239
+
240
+ padTwo: function( value ) {
241
+ var s = '0' + value;
242
+ return s.substr( s.length - 2 );
243
+ },
244
+
245
+ _setNullDate: function( showStagedDate ) {
246
+ this.date = null;
247
+ this.viewDate = new Date();
248
+ this.stagedDate = new Date();
249
+ this._insertDateIntoInput( showStagedDate || "" );
250
+ this._renderWithoutInputManipulation();
251
+ },
252
+
253
+ _restrictDateSelectionSetup: function() {
254
+ var scopedLastMonth, scopedNextMonth;
255
+ if( Boolean( this.options ) ) {
256
+ if( !this.options.restrictDateSelection ) {
257
+ scopedLastMonth = false;
258
+ scopedNextMonth = false;
259
+ } else {
260
+ scopedNextMonth = ( this.viewDate.getMonth() < new Date().getMonth() ) ? true : false;
261
+ scopedLastMonth = ( this.viewDate.getMonth() > new Date().getMonth() ) ? false : true;
262
+ }
263
+ }
264
+ this.options.restrictLastMonth = scopedLastMonth;
265
+ this.options.restrictNextMonth = scopedNextMonth;
266
+ },
267
+
268
+ _processDateRestriction: function( date, returnClasses ) {
269
+ var classes = '';
270
+ var restrictBoolean = false;
271
+ returnClasses = returnClasses || false;
272
+
273
+ if( date <= this.minDate || date >= this.maxDate ) {
274
+ if ( Boolean( this.blackoutDates( date ) ) ) {
275
+ classes += ' restrict blackout';
276
+ restrictBoolean = true;
277
+ } else if ( Boolean( this.options ) && Boolean( this.options.restrictDateSelection ) ) {
278
+ classes += ' restrict';
279
+ restrictBoolean = true;
280
+ } else {
281
+ classes += ' past';
282
+ }
283
+ } else if( Boolean( this.blackoutDates( date ) ) ) {
284
+ classes += ' restrict blackout';
285
+ restrictBoolean = true;
286
+ }
287
+ if( Boolean( returnClasses ) ) {
288
+ return classes;
289
+ } else {
290
+ return restrictBoolean;
291
+ }
292
+ },
293
+
294
+ _repeat: function( head, collection, iterator, tail) {
295
+ var value = head;
296
+ for (var i = 0, ii = collection.length; i < ii; i++) {
297
+ value += iterator( collection[i] );
298
+ }
299
+ value += tail;
300
+ return value;
301
+ },
302
+
303
+ _getDaysInMonth: function( month, year ) {
304
+ return 32 - new Date( year, month, 32 ).getDate();
305
+ },
306
+
307
+ _range: function( start, end ) {
308
+ var numbers = [];
309
+ for ( var i = start; i < end; i++ ) {
310
+ numbers[ numbers.length ] = i;
311
+ }
312
+ return numbers;
313
+ },
314
+
315
+ _yearRange: function( date ) {
316
+ var start = ( Math.floor(date.getFullYear() / 10 ) * 10) - 1;
317
+ var end = start + 12;
318
+ var years = this._range(start, end);
319
+ var interval = [];
320
+
321
+ for (var i = 0, ii = years.length; i < ii; i++) {
322
+ var clazz = '';
323
+ if( i === 0 ) {
324
+ clazz = 'previous';
325
+ }
326
+ if( i === years.length - 1 ) {
327
+ clazz = 'next';
328
+ }
329
+ interval[i] = {
330
+ number: years[ i ],
331
+ 'class': clazz
332
+ };
333
+ }
334
+ return interval;
335
+ },
336
+
337
+ _killEvent: function( e ) {
338
+ e.stopPropagation();
339
+ e.preventDefault();
340
+ return false;
341
+ },
342
+
343
+ _applySize: function( elements, size ) {
344
+ for (var i = 0; i < elements.length; i++) {
345
+ $(elements[ i ]).css({
346
+ 'width': size,
347
+ 'height': size,
348
+ 'line-height': size
349
+ });
350
+ }
351
+ },
352
+
353
+ _show: function( show ) {
354
+ return show ? '' : 'display: none;';
355
+ },
356
+
357
+ _hide: function( hide ) {
358
+ return this._show( !hide );
359
+ },
360
+
361
+ _showView: function( view ) {
362
+ if( view === 1 ) {
363
+ this.options.showDays = true;
364
+ this.options.showMonths = false;
365
+ this.options.showYears = false;
366
+ } else if( view === 2 ) {
367
+ this.options.showDays = false;
368
+ this.options.showMonths = true;
369
+ this.options.showYears = false;
370
+ } else if( view === 3 ) {
371
+ this.options.showDays = false;
372
+ this.options.showMonths = false;
373
+ this.options.showYears = true;
374
+ }
375
+ },
376
+
377
+ _updateCalendarData: function() {
378
+ var viewedMonth = this.viewDate.getMonth();
379
+ var viewedYear = this.viewDate.getFullYear();
380
+ var selectedDay = this.stagedDate.getDate();
381
+ var selectedMonth = this.stagedDate.getMonth();
382
+ var selectedYear = this.stagedDate.getFullYear();
383
+ var firstDayOfMonthWeekday = new Date( viewedYear, viewedMonth, 1 ).getDay();
384
+ var lastDayOfMonth = this._getDaysInMonth( viewedMonth, viewedYear );
385
+ var lastDayOfLastMonth = this._getDaysInMonth( viewedMonth - 1, viewedYear );
386
+
387
+ if( firstDayOfMonthWeekday === 0 ) {
388
+ firstDayOfMonthWeekday = 7;
389
+ }
390
+
391
+ var addToEnd = ( 42 - lastDayOfMonth ) - firstDayOfMonthWeekday;
392
+
393
+ this.daysOfLastMonth = this._range( lastDayOfLastMonth - firstDayOfMonthWeekday + 1, lastDayOfLastMonth + 1 );
394
+ this.daysOfNextMonth = this._range( 1, addToEnd + 1 );
395
+
396
+ // blackout functionality for dates of last month on current calendar view
397
+ for( var x = 0, xx = this.daysOfLastMonth.length; x < xx; x++ ) {
398
+ var tmpLastMonthDaysObj = {};
399
+ tmpLastMonthDaysObj.number = this.daysOfLastMonth[ x ];
400
+ tmpLastMonthDaysObj[ 'class' ] = '';
401
+ tmpLastMonthDaysObj[ 'class' ] = this._processDateRestriction( new Date( viewedYear, viewedMonth + 1, this.daysOfLastMonth[ x ], 0, 0, 0, 0 ), true );
402
+ this.daysOfLastMonth[ x ] = tmpLastMonthDaysObj;
403
+ }
404
+
405
+ // blackout functionality for dates of next month on current calendar view
406
+ for( var b = 0, bb = this.daysOfNextMonth.length; b < bb; b++ ) {
407
+ var tmpNextMonthDaysObj = {};
408
+ tmpNextMonthDaysObj.number = this.daysOfNextMonth[ b ];
409
+ tmpNextMonthDaysObj[ 'class' ] = '';
410
+ tmpNextMonthDaysObj[ 'class' ] = this._processDateRestriction( new Date( viewedYear, viewedMonth + 1, this.daysOfNextMonth[ b ], 0, 0, 0, 0 ), true );
411
+ this.daysOfNextMonth[ b ] = tmpNextMonthDaysObj;
412
+ }
413
+
414
+ var now = new Date();
415
+ var currentDay = now.getDate();
416
+ var currentMonth = now.getMonth();
417
+ var currentYear = now.getFullYear();
418
+ var viewingCurrentMonth = viewedMonth === currentMonth;
419
+ var viewingCurrentYear = viewedYear === currentYear;
420
+ var viewingSelectedMonth = viewedMonth === selectedMonth;
421
+ var viewingSelectedYear = viewedYear === selectedYear;
422
+
423
+ var daysOfThisMonth = this._range( 1, lastDayOfMonth + 1 );
424
+ this.daysOfThisMonth = [];
425
+
426
+ for( var i = 0, ii = daysOfThisMonth.length; i < ii; i++) {
427
+
428
+ var weekDay = new Date(viewedYear, viewedMonth, daysOfThisMonth[ i ]).getDay();
429
+ var weekDayClass = 'weekday';
430
+
431
+ if(weekDay === 6 || weekDay === 0) {
432
+ weekDayClass = 'weekend';
433
+ }
434
+ if( weekDay === 1 ) {
435
+ weekDayClass = '';
436
+ }
437
+ weekDayClass += ' weekday' + weekDay;
438
+
439
+ if( daysOfThisMonth[ i ] === selectedDay && viewingSelectedMonth && viewingSelectedYear ) {
440
+ weekDayClass += ' selected';
441
+ } else if( daysOfThisMonth[ i ] === currentDay && viewingCurrentMonth && viewingCurrentYear ) {
442
+ weekDayClass += ' today';
443
+ }
444
+
445
+ var dt = new Date( viewedYear, viewedMonth, daysOfThisMonth[ i ], 0, 0, 0, 0 );
446
+ weekDayClass += this._processDateRestriction( dt, true );
447
+
448
+ this.daysOfThisMonth[ this.daysOfThisMonth.length ] = {
449
+ 'number': daysOfThisMonth[ i ],
450
+ 'class' : weekDayClass
451
+ };
452
+ }
453
+
454
+ var daysInMonth = this._getDaysInMonth( this.minDate.getFullYear(), this.minDate.getMonth() );
455
+ for( var j = 0, jj = this.months.length; j < jj; j++ ) {
456
+
457
+ this.months[ j ][ 'class' ] = '';
458
+ if( viewingCurrentYear && j === currentMonth ) {
459
+ this.months[ j ][ 'class' ] += ' today';
460
+ }
461
+ if( j === selectedMonth && viewingSelectedYear ) {
462
+ this.months[ j ][ 'class' ] += ' selected';
463
+ }
464
+
465
+ var minDt = new Date( viewedYear, j, daysInMonth, 23, 59, 59, 999 );
466
+ var maxDt = new Date( viewedYear, j, 0, 0, 0, 0, 0 );
467
+ if( minDt <= this.minDate || maxDt >= this.maxDate ) {
468
+ if( Boolean( this.options.restrictDateSelection ) ) {
469
+ this.months[ j ][ 'class' ] += ' restrict';
470
+ }
471
+ }
472
+ }
473
+
474
+ this.years = this._yearRange( this.viewDate);
475
+ daysInMonth = this._getDaysInMonth( this.minDate.getFullYear(), 11 );
476
+
477
+ for( var z = 0, zz = this.years.length; z < zz; z++ ) {
478
+ if( this.years[ z ].number === currentYear ) {
479
+ this.years[ z ][ 'class' ] += ' today';
480
+ }
481
+ if( this.years[ z ].number === selectedYear ) {
482
+ this.years[ z ][ 'class' ] += ' selected';
483
+ }
484
+
485
+ var minDt2 = new Date( this.years[ z ].number, 11, daysInMonth, 23, 59, 59, 999);
486
+ var maxDt2 = new Date( this.years[ z ].number, 0, 0, 0, 0, 0, 0);
487
+ if( minDt2 <= this.minDate || maxDt2 >= this.maxDate ) {
488
+ if( Boolean( this.options.restrictDateSelection ) ) {
489
+ this.years[ z ]['class'] += ' restrict';
490
+ }
491
+ }
492
+ }
493
+ },
494
+
495
+ _updateCss: function() {
496
+ while( this.options.dropdownWidth % 7 !== 0 ) {
497
+ this.options.dropdownWidth++;
498
+ }
499
+
500
+ this.$view.css('width', this.options.dropdownWidth + 'px' );
501
+ this.$header.css('width', this.options.dropdownWidth + 'px' );
502
+ this.$labelDiv.css('width', ( this.options.dropdownWidth - 60 ) + 'px' );
503
+ this.$footer.css('width', this.options.dropdownWidth + 'px' );
504
+ var labelSize = ( this.options.dropdownWidth * 0.25 ) - 2;
505
+ var paddingTop = Math.round( ( this.options.dropdownWidth - ( labelSize * 3 ) ) / 2 );
506
+ var paddingBottom = paddingTop;
507
+ while( paddingBottom + paddingTop + ( labelSize * 3 ) < this.options.dropdownWidth ) {
508
+ paddingBottom += 0.1;
509
+ }
510
+ while( paddingBottom + paddingTop + ( labelSize * 3 ) > this.options.dropdownWidth ) {
511
+ paddingBottom -= 0.1;
512
+ }
513
+
514
+ paddingTop = parseInt( paddingTop / 2, 10 );
515
+ paddingBottom = parseInt( paddingBottom / 2, 10 );
516
+
517
+ this.$calendar.css({
518
+ 'float': 'left'
519
+ });
520
+
521
+ this.$monthsView.css({
522
+ 'width': this.options.dropdownWidth + 'px',
523
+ 'padding-top': paddingTop + 'px',
524
+ 'padding-bottom': paddingBottom + 'px'
525
+ });
526
+
527
+ this.$yearsView.css({
528
+ 'width': this.options.dropdownWidth + 'px',
529
+ 'padding-top': paddingTop + 'px',
530
+ 'padding-bottom': paddingBottom + 'px'
531
+ });
532
+
533
+ var cellSize = Math.round( this.options.dropdownWidth / 7.0 ) - 2 + 'px';
534
+ var headerCellSize = Math.round( this.options.dropdownWidth / 7.0 ) + 'px';
535
+ this._applySize( this.$yearsView.children(), labelSize + 'px' );
536
+ this._applySize( this.$monthsView.children(), labelSize + 'px' );
537
+ this._applySize( this.$weekdaysDiv.children(), headerCellSize );
538
+ this._applySize( this.$lastMonthDiv.children(), cellSize );
539
+ this._applySize( this.$thisMonthDiv.children(), cellSize );
540
+ this._applySize( this.$nextMonthDiv.children(), cellSize );
541
+ },
542
+
543
+ _close: function() {
544
+ this.$input.dropdown( 'toggle' );
545
+ },
546
+
547
+ _select: function( e ) {
548
+ this.inputParsingTarget = null;
549
+ if( e.target.className.indexOf( 'restrict' ) > -1 ) {
550
+ return this._killEvent(e);
551
+ } else {
552
+ this._killEvent( e );
553
+ this._close();
554
+ }
555
+
556
+ this.stagedDate = this.viewDate;
557
+ this.stagedDate.setDate( parseInt( e.target.innerHTML, 10 ) );
558
+
559
+ this.setDate( this.stagedDate );
560
+ this._render();
561
+ this.done = true;
562
+ },
563
+
564
+ _pickYear: function( e ) {
565
+ var year = parseInt( $( e.target ).data( 'yearNumber' ), 10 );
566
+ if( e.target.className.indexOf('restrict') > -1 ) {
567
+ return this._killEvent(e);
568
+ }
569
+
570
+ this.viewDate = new Date( year, this.viewDate.getMonth(), 1 );
571
+ this._showView( 2 );
572
+ this._render();
573
+
574
+ return this._killEvent(e);
575
+ },
576
+
577
+ _pickMonth: function( e ) {
578
+ var month = parseInt( $(e.target).data( 'monthNumber' ), 10 );
579
+ if( e.target.className.indexOf( 'restrict' ) > -1 ) {
580
+ return this._killEvent(e);
581
+ }
582
+
583
+ this.viewDate = new Date( this.viewDate.getFullYear(), month, 1 );
584
+ this._showView( 1 );
585
+ this._render();
586
+
587
+ return this._killEvent(e);
588
+ },
589
+
590
+ _previousSet: function( e ) {
591
+ this._previous( e, true );
592
+ },
593
+
594
+ _previous: function( e, set ) {
595
+ if( e.target.className.indexOf( 'restrict' ) > -1 ) {
596
+ return this._killEvent(e);
597
+ }
598
+
599
+ if( this.options.showDays) {
600
+ this.viewDate = new Date( this.viewDate.getFullYear(), this.viewDate.getMonth() - 1, 1 );
601
+ } else if( this.options.showMonths ) {
602
+ this.viewDate = new Date( this.viewDate.getFullYear() - 1, this.viewDate.getMonth(), 1 );
603
+ } else if( this.options.showYears ) {
604
+ this.viewDate = new Date( this.viewDate.getFullYear() - 10, this.viewDate.getMonth(), 1 );
605
+ }
606
+
607
+ if( Boolean( set ) ) {
608
+ this._select( e );
609
+ } else {
610
+ this._render();
611
+ }
612
+ // move this below 'this._render()' if you want it to go to the previous month when you select a day from the current month
613
+ return this._killEvent( e );
614
+ },
615
+
616
+ _nextSet: function( e ) {
617
+ this._next( e, true );
618
+ },
619
+
620
+ _next: function( e, set ) {
621
+ if( e.target.className.indexOf('restrict') > -1 ) {
622
+ return this._killEvent(e);
623
+ }
624
+
625
+ if( this.options.showDays ) {
626
+ this.viewDate = new Date( this.viewDate.getFullYear(), this.viewDate.getMonth() + 1, 1 );
627
+ } else if( this.options.showMonths ) {
628
+ this.viewDate = new Date( this.viewDate.getFullYear() + 1, this.viewDate.getMonth(), 1 );
629
+ } else if( this.options.showYears ) {
630
+ this.viewDate = new Date( this.viewDate.getFullYear() + 10, this.viewDate.getMonth(), 1 );
631
+ }
632
+
633
+ if( Boolean( set ) ) {
634
+ this._select( e );
635
+ } else {
636
+ this._render();
637
+ }
638
+ // move this below 'this._render()' if you want it to go to the next month when you select a day from the current month
639
+ return this._killEvent(e);
640
+ },
641
+
642
+ _today: function( e ) {
643
+ this.viewDate = new Date();
644
+ this._showView( 1 );
645
+ this._render();
646
+ return this._killEvent(e);
647
+ },
648
+
649
+ _emptySpace: function( e ) {
650
+ if( Boolean( this.done ) ) {
651
+ this.done = false;
652
+ }
653
+ return this._killEvent(e);
654
+ },
655
+
656
+ _monthLabel: function() {
657
+ return this.options.monthNames[ this.viewDate.getMonth() ];
658
+ },
659
+
660
+ _yearLabel: function() {
661
+ return this.viewDate.getFullYear();
662
+ },
663
+
664
+ _monthYearLabel: function() {
665
+ var label;
666
+ if( this.options.showDays ) {
667
+ label = this._monthLabel() + ' ' + this._yearLabel();
668
+ } else if( this.options.showMonths ) {
669
+ label = this._yearLabel();
670
+ } else if( this.options.showYears ) {
671
+ label = this.years[ 0 ].number + ' - ' + this.years[ this.years.length - 1 ].number;
672
+ }
673
+ return label;
674
+ },
675
+
676
+ _toggleMonthYearPicker: function( e ) {
677
+ if( this.options.showDays ) {
678
+ this._showView( 2 );
679
+ } else if( this.options.showMonths ) {
680
+ this._showView( 3 );
681
+ } else if( this.options.showYears ) {
682
+ this._showView( 1 );
683
+ }
684
+ this._render();
685
+ return this._killEvent( e );
686
+ },
687
+
688
+ _renderCalendar: function() {
689
+ var self = this;
690
+ self._restrictDateSelectionSetup();
691
+
692
+ return '<div class="calendar">' +
693
+ '<div class="header clearfix">' +
694
+ '<div class="left hover"><div class="leftArrow"></div></div>' +
695
+ '<div class="right hover"><div class="rightArrow"></div></div>' +
696
+ '<div class="center hover">' + self._monthYearLabel() + '</div>' +
697
+ '</div>' +
698
+ '<div class="daysView" style="' + self._show( self.options.showDays ) + '">' +
699
+
700
+ self._repeat( '<div class="weekdays">', self.options.weekdays,
701
+ function( weekday ) {
702
+ return '<div >' + weekday + '</div>';
703
+ }, '</div>' ) +
704
+
705
+ self._repeat( '<div class="lastmonth">', self.daysOfLastMonth,
706
+ function( day ) {
707
+ if( self.options.restrictLastMonth ) {
708
+ day['class'] = day['class'].replace('restrict', '') + " restrict";
709
+ }
710
+ return '<div class="' + day[ 'class' ] + '">' + day.number + '</div>';
711
+ }, '</div>' ) +
712
+
713
+ self._repeat( '<div class="thismonth">', self.daysOfThisMonth,
714
+ function( day ) {
715
+ return '<div class="' + day[ 'class' ] + '">' + day.number + '</div>';
716
+ }, '</div>' ) +
717
+
718
+ self._repeat( '<div class="nextmonth">', self.daysOfNextMonth,
719
+ function( day ) {
720
+ if( self.options.restrictNextMonth ) {
721
+ day['class'] = day['class'].replace('restrict', '') + " restrict";
722
+ }
723
+ return '<div class="' + day[ 'class' ] + '">' + day.number + '</div>';
724
+ }, '</div>' ) +
725
+ '</div>' +
726
+
727
+ self._repeat( '<div class="monthsView" style="' + self._show( self.options.showMonths ) + '">', self.months,
728
+ function( month ) {
729
+ return '<div data-month-number="' + month.number +
730
+ '" class="' + month[ 'class' ] + '">' + month.abbreviation + '</div>';
731
+ }, '</div>' ) +
732
+
733
+ self._repeat( '<div class="yearsView" style="' + self._show( self.options.showYears ) + '">', self.years,
734
+ function( year ) {
735
+ return '<div data-year-number="' + year.number +
736
+ '" class="' + year[ 'class' ] + '">' + year.number + '</div>';
737
+ }, '</div>' ) +
738
+
739
+ '<div class="footer">' +
740
+ '<div class="center hover">Today</div>' +
741
+ '</div>' +
742
+ '</div>';
743
+ },
744
+
745
+ _render: function() {
746
+ this._insertDateIntoInput();
747
+ this._updateCalendarData();
748
+ if ( Boolean( this.bindingsAdded ) ) this._removeBindings();
749
+ this.$element.find( '.dropdown-menu' ).html( this._renderCalendar() );
750
+ this._initializeCalendarElements();
751
+ this._addBindings();
752
+ this._updateCss();
753
+ },
754
+
755
+ _renderWithoutInputManipulation: function() {
756
+ this._updateCalendarData();
757
+ if ( Boolean( this.bindingsAdded ) ) this._removeBindings();
758
+ this.$element.find( '.dropdown-menu' ).html( this._renderCalendar() );
759
+ this._initializeCalendarElements();
760
+ this._addBindings();
761
+ this._updateCss();
762
+ },
763
+
764
+ _renderInput: function() {
765
+ var input = ( Boolean( this.options.createInput.native ) ) ? this._renderInputNative() : this._renderInputHTML();
766
+ this.$element.html( input );
767
+ this._render();
768
+ },
769
+
770
+ _renderInputNative: function() {
771
+ return '<input type="date" value="' + this.formatDate( this.date ) + '"' + this._calculateInputSize( [ 'native' ] ) + '>';
772
+ },
773
+
774
+ _renderInputHTML: function() {
775
+ var inputClass = ( Boolean( this.options.createInput.dropDownBtn ) ) ? 'input-append' : 'input-group';
776
+
777
+ var dropdownHtml = '<div class="' + inputClass + '">' +
778
+ '<div class="dropdown-menu"></div>' +
779
+ '<input type="text" '+ this._calculateInputSize() +' value="'+this.formatDate( this.date ) +'" data-toggle="dropdown">';
780
+
781
+ if( Boolean( this.options.createInput.dropDownBtn ) ) {
782
+ dropdownHtml = dropdownHtml + '<button type="button" class="btn" data-toggle="dropdown"><i class="icon-calendar"></i></button>';
783
+ }
784
+
785
+ dropdownHtml = dropdownHtml + '</div>';
786
+
787
+ return '<div class="datepicker dropdown">' + dropdownHtml + '</div>';
788
+ },
789
+
790
+ _calculateInputSize: function( options ) {
791
+ if( Boolean( parseInt( this.options.createInput.inputSize, 10 ) ) ) {
792
+ return 'style="width:'+ this.options.createInput.inputSize +'px"';
793
+ } else {
794
+ options = ( Boolean( options ) ) ? " " + options.join(' ') : '';
795
+ return 'class="' + this.options.createInput.inputSize + options + '"';
796
+ }
797
+
798
+ },
799
+
800
+ _insertDateIntoInput: function( showStagedDate ) {
801
+ var displayDate;
802
+ if( Boolean( showStagedDate ) ) {
803
+ displayDate = this.formatDate( this.stagedDate );
804
+ } else if( this.date !== null ) {
805
+ displayDate = this.formatDate( this.date );
806
+ } else {
807
+ displayDate = '';
808
+ }
809
+ this.$element.find('input[type="text"]').val( displayDate );
810
+ },
811
+
812
+ _inputDateParsing: function() {
813
+ // the formats we support when using moment.js are either "L" or "l"
814
+ // these can be found here http://momentjs.com/docs/#/customization/long-date-formats/
815
+ var inputValue = this.$input.val();
816
+ var triggerError = true;
817
+ var validLengthMax = 10; // since the length of the longest date format we are going to parse is 10 ("L" format code) we will set this here.
818
+ var validLengthMin = validLengthMax - 2; // since the shortest date format we are going to parse is 8 ("l" format code) we will subtract the difference from the max
819
+
820
+ if( inputValue.length >= validLengthMin && inputValue.length <= validLengthMax ) {
821
+ if( Boolean( this.parseDate( inputValue, true ) ) ) {
822
+ if( !this._processDateRestriction( this.parseDate( inputValue ) ) ) {
823
+ triggerError = false;
824
+ this.setDate( inputValue );
825
+ }
826
+ }
827
+ } else {
828
+ triggerError = false; // don't want to trigger an error because they don't have the correct length
829
+ }
830
+
831
+ if( !!triggerError ) {
832
+ // we will insert the staged date into the input
833
+ this._setNullDate( true );
834
+ this.$element.trigger( 'inputParsingFailed' );
835
+ }
836
+ },
837
+
838
+ _checkForMomentJS: function() {
839
+ // this function get's run on initialization to determin if momentjs is available
840
+ if( $.isFunction( window.moment ) || ( typeof moment !== "undefined" && $.isFunction( moment ) ) ) {
841
+ if( $.isPlainObject( this.options.momentConfig ) ) {
842
+ if( Boolean( this.options.momentConfig.culture ) && Boolean( this.options.momentConfig.formatCode ) ) {
843
+ return true;
844
+ } else {
845
+ return false;
846
+ }
847
+ } else {
848
+ return false;
849
+ }
850
+ } else {
851
+ return false;
852
+ }
853
+ },
854
+
855
+ _initializeCalendarElements: function() {
856
+ this.$input = this.$element.find( 'input[type="text"]' );
857
+ this.$calendar = this.$element.find('div.calendar');
858
+ this.$header = this.$calendar.children().eq(0);
859
+ this.$labelDiv = this.$header.children().eq(2);
860
+ this.$view = this.$calendar.children().eq(1);
861
+ this.$monthsView = this.$calendar.children().eq(2);
862
+ this.$yearsView = this.$calendar.children().eq(3);
863
+ this.$weekdaysDiv = this.$view.children().eq(0);
864
+ this.$lastMonthDiv = this.$view.children().eq(1);
865
+ this.$thisMonthDiv = this.$view.children().eq(2);
866
+ this.$nextMonthDiv = this.$view.children().eq(3);
867
+ this.$footer = this.$calendar.children().eq(4);
868
+ },
869
+
870
+ _addBindings: function() {
871
+ var self = this;
872
+
873
+ // parsing dates on user input is only available when momentjs is used
874
+ if( Boolean( this.moment ) ) {
875
+ this.$calendar.on( 'mouseover', function() {
876
+ self.inputParsingTarget = 'calendar';
877
+ });
878
+ this.$calendar.on( 'mouseout', function() {
879
+ self.inputParsingTarget = null;
880
+ });
881
+
882
+ this.$input.on( 'blur', function() {
883
+ if( self.inputParsingTarget === null ) {
884
+ self._inputDateParsing();
885
+ }
886
+ });
887
+ }
888
+
889
+ this.$calendar.on( 'click', $.proxy( this._emptySpace, this) );
890
+
891
+ this.$header.find( '.left' ).on( 'click', $.proxy( this._previous, this ) );
892
+ this.$header.find( '.right' ).on( 'click', $.proxy( this._next, this ) );
893
+ this.$header.find( '.center' ).on( 'click', $.proxy( this._toggleMonthYearPicker, this ) );
894
+
895
+ this.$lastMonthDiv.find( 'div' ).on( 'click', $.proxy( this._previousSet, this ) );
896
+ this.$thisMonthDiv.find( 'div' ).on( 'click', $.proxy( this._select, this ) );
897
+ this.$nextMonthDiv.find( 'div' ).on( 'click', $.proxy( this._nextSet, this ) );
898
+
899
+ this.$monthsView.find( 'div' ).on( 'click', $.proxy( this._pickMonth, this ) );
900
+ this.$yearsView.find( 'div' ).on( 'click', $.proxy( this._pickYear, this ) );
901
+ this.$footer.find( '.center' ).on( 'click', $.proxy( this._today, this ) );
902
+
903
+ this.bindingsAdded = true;
904
+ },
905
+
906
+ _removeBindings: function() {
907
+ // remove event only if moment is available (meaning it was initialized in the first place)
908
+ if( Boolean( this.moment ) ) {
909
+ this.$calendar.off( 'mouseover' );
910
+ this.$calendar.off( 'mouseout' );
911
+ this.$input.off( 'blur' );
912
+ }
913
+
914
+ this.$calendar.off( 'click' );
915
+
916
+ this.$header.find( '.left' ).off( 'click' );
917
+ this.$header.find( '.right' ).off( 'click' );
918
+ this.$header.find( '.center' ).off( 'click' );
919
+
920
+ this.$lastMonthDiv.find( 'div' ).off( 'click' );
921
+ this.$thisMonthDiv.find( 'div' ).off( 'click' );
922
+ this.$nextMonthDiv.find( 'div' ).off( 'click' );
923
+
924
+ this.$monthsView.find( 'div' ).off( 'click' );
925
+ this.$yearsView.find( 'div' ).off( 'click' );
926
+ this.$footer.find( '.center' ).off( 'click' );
927
+
928
+ this.bindingsAdded = false;
929
+ }
930
+ };
931
+
932
+
933
+ // DATEPICKER PLUGIN DEFINITION
934
+
935
+ $.fn.datepicker = function (option) {
936
+ var args = Array.prototype.slice.call( arguments, 1 );
937
+ var methodReturn;
938
+
939
+ var $set = this.each(function () {
940
+ var $this = $( this );
941
+ var data = $this.data( 'datepicker' );
942
+ var options = typeof option === 'object' && option;
943
+
944
+ if( !data ) $this.data('datepicker', (data = new Datepicker( this, options ) ) );
945
+ if( typeof option === 'string' ) methodReturn = data[ option ].apply( data, args );
946
+ });
947
+
948
+ return ( methodReturn === undefined ) ? $set : methodReturn;
949
+ };
950
+
951
+ $.fn.datepicker.defaults = {
952
+ date: new Date(),
953
+ momentConfig: {
954
+ culture: 'en',
955
+ formatCode: 'L' // more formats can be found here http://momentjs.com/docs/#/customization/long-date-formats/. We only support "L" or "l"
956
+ },
957
+ createInput: false,
958
+ dropdownWidth: 170,
959
+ restrictDateSelection: true
960
+ };
961
+
962
+ $.fn.datepicker.Constructor = Datepicker;
963
+
964
+ $.fn.datepicker.noConflict = function () {
965
+ $.fn.datepicker = old;
966
+ return this;
967
+ };
968
+ }(window.jQuery);