fuelux-rails 2.4.2 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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);