usefull_filter 0.0.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA512:
3
+ data.tar.gz: 472b4bc87a817818a0b0154c20d3cac9ef84202c23b4a17f0d531fe64eb8079f2d6cb26cfa6155adcfbbc6a76a8e718566363a2f72135d5d0def4381e08957f2
4
+ metadata.gz: 9c7a67d87fd54c5ad1e7fa6acd9e4247dc0d69679f2222afa1878a0375c206efc9371969c835e597e90e21493d657df9bb59acff8c765820c99214054c4ce2b8
5
+ SHA1:
6
+ data.tar.gz: 032e5f180a6dadee2775a07bbf8549b05d59373d
7
+ metadata.gz: 60224ac3910252312735d255fd5dcc683cd90d17
@@ -1,4 +1,8 @@
1
+ 1.0.0 (24 Oct 2013)
2
+ - Stable Release
3
+
4
+ 0.0.6 (6 Sept 2013)
5
+ - Fixed localization of associations according to usefull_table convention
1
6
  0.0.1 (11 July 2012)
2
- - Starting projeect
3
- - test
4
- -test2
7
+ - Deploy
8
+
@@ -1,3 +1,8 @@
1
1
  = UsefullFilter
2
2
 
3
- This project rocks and uses MIT-LICENSE.
3
+ A very beta version, published only for testing...
4
+
5
+ use it if you want but at your own risk!
6
+
7
+ This project rocks and uses MIT-LICENSE.
8
+
@@ -4,6 +4,7 @@
4
4
  #[documents_controller]
5
5
  # Vedi TableHelper
6
6
  #
7
+ #
7
8
  #[index.html.erb]
8
9
  # <%=filter_for_new @search, :url => magazzino_documents_path do |f| %>
9
10
  # <%= f.text_field :data, :size => 10, :filters => ["equals", "less_than_or_equal_to", "greater_than_or_equal_to"] %>
@@ -12,7 +13,7 @@
12
13
  # <%= f.text_field :BollaXENrDoC, filters_except => ["equals", "does_not_equal"] %>
13
14
  # <%= f.text_field :DoCSit %>
14
15
  # <%= f.submit %>
15
- # <% end %>
16
+ # <% end %> f
16
17
  module UsefullFilterHelper
17
18
  #Contatta l'url passato ripassando i filtri attivi in @search
18
19
  def filter_button_tag(name, url, method = :get)
@@ -34,8 +35,28 @@ module UsefullFilterHelper
34
35
  filter_for(obj, *args, &proc)
35
36
  end
36
37
 
38
+ # Parsa il params per accomodare il tipo di dato dei valori
39
+ def parse_params(param)
40
+ param.keys.each do |k|
41
+ param[k]=param[k].split(' ') if '_in' == /_in$/.match(k).to_s
42
+ end unless param.blank?
43
+ #mylog("parse_params: #{param.inspect}")
44
+ param
45
+ end
46
+
47
+ # Parsa il params per accomodare il tipo di dato dei valori
48
+ def parse_params_rev(param)
49
+ param.keys.each do |k|
50
+ param[k]=param[k].join(' ') if '_in' == /_in$/.match(k).to_s
51
+ end
52
+ mylog("parse_params_rev: #{param.inspect}")
53
+ param
54
+ end
55
+
56
+
57
+
37
58
  #Cerca una serie di filtri utilizzando MetaSearch di cui fa il wrapper
38
- #obj è una istanza metashearch
59
+ #obj è una istanza metasearch
39
60
  def filter_for(obj, *args, &proc)
40
61
  unless obj.blank?
41
62
  options = args.extract_options!
@@ -52,18 +73,25 @@ module UsefullFilterHelper
52
73
  filter_title = I18n.t("filter_title", :scope => "meta_search.buttons")
53
74
 
54
75
  #Estraggo le options che mi interessano, perchè una volta passate al builder
55
- #per qulache arcano motivo vengono alterate....
76
+ #per qualche arcano motivo vengono alterate....
56
77
  classe = options[:html_1].delete(:class)
57
78
  url = options[:url]
58
- #UserSession.log("FilterHelper#filter_for_new: options=#{options.inspect}")
79
+ # mylog("FilterHelper#filter_for_new: args=#{args.inspect}")
80
+ # mylog("FilterHelper#filter_for_new: options=#{options.inspect}")
81
+ # mylog("FilterHelper#filter_for_new: obj=#{obj.inspect}")
59
82
  content_tag(:div,
83
+ javascript_include_tag("usefull_filter_date_select") +
84
+ javascript_include_tag("usefull_helpers_toggle_area")+
85
+ stylesheet_link_tag("usefull_filter") +
86
+ stylesheet_link_tag("usefull_filter_date_select") +
60
87
  content_tag(:h3, filter_title) +
88
+ toggle_area("all_filters") +
61
89
  content_tag(:ul,
62
90
  form_for(obj, *(args << options), &proc) +
63
91
  form_tag(url_for(url), :method => :get) do
64
92
  obj.search_attributes.each_key {|k| concat hidden_field_tag("search[#{k}]") }
65
- concat submit_tag(remove_filter_label, :class => "usefull_filter_push-2")
66
- end ),
93
+ concat submit_tag(remove_filter_label, :class => "remove_filter_btn")
94
+ end, :class=> "all_filters", :style => "display: none;" ),
67
95
  :class => classe)
68
96
  end
69
97
  end
@@ -4,9 +4,9 @@ module UsefullFilter
4
4
  desc "Install generator for UsefullFilter gem"
5
5
  source_root File.expand_path("../templates", __FILE__)
6
6
 
7
- #def copy_config
8
- # directory "config"
9
- #end
7
+ def copy_config
8
+ directory "."
9
+ end
10
10
 
11
11
  #def copy_public
12
12
  # directory "public"
@@ -0,0 +1,29 @@
1
+ it:
2
+ meta_search:
3
+ predicates:
4
+ equals: "%{attribute} ="
5
+ does_not_equal: "%{attribute} !="
6
+ contains: "%{attribute} contiene"
7
+ does_not_contain: "%{attribute} non contiene"
8
+ starts_with: "%{attribute} inizia con"
9
+ does_not_start_with: "%{attribute} non inizia con"
10
+ ends_with: "%{attribute} finisce con"
11
+ does_not_end_with: "%{attribute} non finisce con"
12
+ greater_than: "%{attribute} >"
13
+ less_than: "%{attribute} <"
14
+ greater_than_or_equal_to: "%{attribute} >="
15
+ less_than_or_equal_to: "%{attribute} <="
16
+ in: "%{attribute} è uno di"
17
+ not_in: "%{attribute} non è uno di"
18
+ is_true: "%{attribute} è vero"
19
+ is_false: "%{attribute} è falso"
20
+ is_present: "%{attribute} è presente"
21
+ is_blank: "%{attribute} è bianco"
22
+ is_null: "%{attribute} è nullo"
23
+ is_not_null: "%{attribute} non è nullo"
24
+ between: "%{attribute} compreso tra"
25
+ buttons:
26
+ applay_filter: "Filtra"
27
+ remove_filter: "Rimuovi Filtri"
28
+ filter_title: "Filtri"
29
+
@@ -0,0 +1,2420 @@
1
+ /********************************************************************
2
+ * Kalendae, a framework agnostic javascript date picker *
3
+ * Copyright(c) 2013 Jarvis Badgley (chipersoft@gmail.com) *
4
+ * http://github.com/ChiperSoft/Kalendae *
5
+ * Version 0.4.2 *
6
+ ********************************************************************/
7
+
8
+ (function (undefined) {
9
+
10
+ var today, moment;
11
+
12
+ var Kalendae = function (targetElement, options) {
13
+ if (typeof document.addEventListener !== 'function' && !util.isIE8()) return;
14
+
15
+ //if the first argument isn't an element and isn't a string, assume that it is the options object
16
+ var is_element = false;
17
+ try {
18
+ is_element = targetElement instanceof Element;
19
+ }
20
+ catch (err) {
21
+ is_element = !!targetElement && is_element.nodeType === 1;
22
+ }
23
+ if (!(is_element || typeof(targetElement) === 'string')) options = targetElement;
24
+
25
+ var self = this,
26
+ classes = self.classes,
27
+ opts = self.settings = util.merge(self.defaults, {attachTo:targetElement}, options || {}),
28
+ $container = self.container = util.make('div', {'class':classes.container}),
29
+ calendars = self.calendars = [],
30
+ startDay = moment().day(opts.weekStart),
31
+ vsd,
32
+ columnHeaders = [],
33
+ $cal,
34
+ $title,
35
+ $caption,
36
+ $header,
37
+ $days, dayNodes = [],
38
+ $span,
39
+ i = 0,
40
+ j = opts.months;
41
+
42
+ if (util.isIE8()) util.addClassName($container, 'ie8');
43
+
44
+ //generate the column headers (Su, Mo, Tu, etc)
45
+ i = 7;
46
+ while (i--) {
47
+ columnHeaders.push( startDay.format(opts.columnHeaderFormat) );
48
+ startDay.add('days',1);
49
+ }
50
+
51
+ //setup publish/subscribe and apply any subscriptions passed in settings
52
+ MinPubSub(self);
53
+ if (typeof opts.subscribe === 'object') {
54
+ for (i in opts.subscribe) if (opts.subscribe.hasOwnProperty(i)) {
55
+ self.subscribe(i, opts.subscribe[i]);
56
+ }
57
+ }
58
+
59
+ //process default selected dates
60
+ self._sel = [];
61
+ if (!!opts.selected) self.setSelected(opts.selected, false);
62
+
63
+ //set the view month
64
+ if (!!opts.viewStartDate) {
65
+ vsd = moment(opts.viewStartDate, opts.format);
66
+ } else if (self._sel.length > 0) {
67
+ vsd = moment(self._sel[0]);
68
+ } else {
69
+ vsd = moment();
70
+ }
71
+ self.viewStartDate = vsd.date(1);
72
+
73
+ var viewDelta = ({
74
+ 'past' : opts.months-1,
75
+ 'today-past' : opts.months-1,
76
+ 'any' : opts.months>2?Math.floor(opts.months/2):0,
77
+ 'today-future' : 0,
78
+ 'future' : 0
79
+ })[this.settings.direction];
80
+
81
+
82
+ if (viewDelta && moment().month()==moment(self.viewStartDate).month()){
83
+ self.viewStartDate = moment(self.viewStartDate).subtract({M:viewDelta}).date(1);
84
+ }
85
+
86
+
87
+ if (typeof opts.blackout === 'function') {
88
+ self.blackout = opts.blackout;
89
+ } else if (!!opts.blackout) {
90
+ var bdates = parseDates(opts.blackout, opts.parseSplitDelimiter, opts.format);
91
+ self.blackout = function (input) {
92
+ input = moment(input).yearDay();
93
+ if (input < 1 || !self._sel) return false;
94
+ var i = bdates.length;
95
+ while (i--) if (bdates[i].yearDay() === input) return true;
96
+ return false;
97
+ };
98
+ } else {
99
+ self.blackout = function () {return false;};
100
+ }
101
+
102
+
103
+ self.direction = self.directions[opts.direction] ? self.directions[opts.direction] : self.directions['any'];
104
+
105
+
106
+ //for the total months setting, generate N calendar views and add them to the container
107
+ j = Math.max(opts.months,1);
108
+ while (j--) {
109
+ $cal = util.make('div', {'class':classes.calendar}, $container);
110
+
111
+ $cal.setAttribute('data-cal-index', j);
112
+ if (opts.months > 1) {
113
+ if (j == Math.max(opts.months-1,1)) util.addClassName($cal, classes.monthFirst);
114
+ else if (j === 0) util.addClassName($cal, classes.monthLast);
115
+ else util.addClassName($cal, classes.monthMiddle);
116
+ }
117
+
118
+ //title bar
119
+ $title = util.make('div', {'class':classes.title}, $cal);
120
+ if(!opts.useYearNav){
121
+ util.addClassName($title, classes.disableYearNav);
122
+ }
123
+ util.make('a', {'class':classes.previousYear}, $title); //previous button
124
+ util.make('a', {'class':classes.previousMonth}, $title); //previous button
125
+ util.make('a', {'class':classes.nextYear}, $title); //next button
126
+ util.make('a', {'class':classes.nextMonth}, $title); //next button
127
+ $caption = util.make('span', {'class':classes.caption}, $title); //title caption
128
+
129
+ //column headers
130
+ $header = util.make('div', {'class':classes.header}, $cal);
131
+ i = 0;
132
+ do {
133
+ $span = util.make('span', {}, $header);
134
+ $span.innerHTML = columnHeaders[i];
135
+ } while (++i < 7);
136
+
137
+ //individual day cells
138
+ $days = util.make('div', {'class':classes.days}, $cal);
139
+ i = 0;
140
+ dayNodes = [];
141
+ while (i++ < 42) {
142
+ dayNodes.push(util.make('span', {}, $days));
143
+ }
144
+
145
+ //store each calendar view for easy redrawing
146
+ calendars.push({
147
+ caption:$caption,
148
+ days:dayNodes
149
+ });
150
+
151
+ if (j) util.make('div', {'class':classes.monthSeparator}, $container);
152
+ }
153
+
154
+ self.draw();
155
+
156
+ util.addEvent($container, 'mousedown', function (event, target) {
157
+ var clickedDate;
158
+ if (util.hasClassName(target, classes.nextMonth)) {
159
+ //NEXT MONTH BUTTON
160
+ if (!self.disableNext && self.publish('view-changed', self, ['next-month']) !== false) {
161
+ self.viewStartDate.add('months',1);
162
+ self.draw();
163
+ }
164
+ return false;
165
+
166
+ } else if (util.hasClassName(target, classes.previousMonth)) {
167
+ //PREVIOUS MONTH BUTTON
168
+ if (!self.disablePreviousMonth && self.publish('view-changed', self, ['previous-month']) !== false) {
169
+ self.viewStartDate.subtract('months',1);
170
+ self.draw();
171
+ }
172
+ return false;
173
+
174
+ } else if (util.hasClassName(target, classes.nextYear)) {
175
+ //NEXT MONTH BUTTON
176
+ if (!self.disableNext && self.publish('view-changed', self, ['next-year']) !== false) {
177
+ self.viewStartDate.add('years',1);
178
+ self.draw();
179
+ }
180
+ return false;
181
+
182
+ } else if (util.hasClassName(target, classes.previousYear)) {
183
+ //PREVIOUS MONTH BUTTON
184
+ if (!self.disablePreviousMonth && self.publish('view-changed', self, ['previous-year']) !== false) {
185
+ self.viewStartDate.subtract('years',1);
186
+ self.draw();
187
+ }
188
+ return false;
189
+
190
+
191
+
192
+ } else if (util.hasClassName(target.parentNode, classes.days) && util.hasClassName(target, classes.dayActive) && (clickedDate = target.getAttribute('data-date'))) {
193
+ //DAY CLICK
194
+ clickedDate = moment(clickedDate, opts.dayAttributeFormat).hours(12);
195
+ if (self.publish('date-clicked', self, [clickedDate]) !== false) {
196
+
197
+ switch (opts.mode) {
198
+ case 'multiple':
199
+ if (!self.addSelected(clickedDate)) self.removeSelected(clickedDate);
200
+ break;
201
+ case 'range':
202
+ self.addSelected(clickedDate);
203
+ break;
204
+ case 'single':
205
+ /* falls through */
206
+ default:
207
+ self.addSelected(clickedDate);
208
+ break;
209
+ }
210
+
211
+ }
212
+ return false;
213
+
214
+ }
215
+ return false;
216
+ });
217
+
218
+
219
+ if (!!(opts.attachTo = util.$(opts.attachTo))) {
220
+ opts.attachTo.appendChild($container);
221
+ }
222
+
223
+ };
224
+
225
+ Kalendae.prototype = {
226
+ defaults : {
227
+ attachTo :null, /* the element to attach the root container to. can be string or DOMElement */
228
+ months :1, /* total number of months to display side by side */
229
+ weekStart :0, /* day to use for the start of the week. 0 is Sunday */
230
+ direction :'any', /* past, today-past, any, today-future, future */
231
+ directionScrolling :true, /* if a direction other than any is defined, prevent scrolling out of range */
232
+ viewStartDate :null, /* date in the month to display. When multiple months, this is the left most */
233
+ blackout :null, /* array of dates, or function to be passed a date */
234
+ selected :null, /* dates already selected. can be string, date, or array of strings or dates. */
235
+ mode :'single', /* single, multiple, range */
236
+ dayOutOfMonthClickable:false,
237
+ format :null, /* string used for parsing dates. */
238
+ subscribe :null, /* object containing events to subscribe to */
239
+
240
+ columnHeaderFormat :'dd', /* number of characters to show in the column headers */
241
+ titleFormat :'MMMM, YYYY', /* format mask for month titles. See momentjs.com for rules */
242
+ dayNumberFormat :'D', /* format mask for individual days */
243
+ dayAttributeFormat :'YYYY-MM-DD', /* format mask for the data-date attribute set on every span */
244
+ parseSplitDelimiter : /,\s*|\s+-\s+/, /* regex to use for splitting multiple dates from a passed string */
245
+ rangeDelimiter :' - ', /* string to use between dates when outputting in range mode */
246
+ multipleDelimiter :', ', /* string to use between dates when outputting in multiple mode */
247
+ useYearNav :true,
248
+
249
+ dateClassMap :{}
250
+ },
251
+ classes : {
252
+ container :'kalendae',
253
+ calendar :'k-calendar',
254
+ monthFirst :'k-first-month',
255
+ monthMiddle :'k-middle-month',
256
+ monthLast :'k-last-month',
257
+ title :'k-title',
258
+ previousMonth :'k-btn-previous-month',
259
+ nextMonth :'k-btn-next-month',
260
+ previousYear :'k-btn-previous-year',
261
+ nextYear :'k-btn-next-year',
262
+ caption :'k-caption',
263
+ header :'k-header',
264
+ days :'k-days',
265
+ dayOutOfMonth :'k-out-of-month',
266
+ dayInMonth :'k-in-month',
267
+ dayActive :'k-active',
268
+ daySelected :'k-selected',
269
+ dayInRange :'k-range',
270
+ dayToday :'k-today',
271
+ monthSeparator :'k-separator',
272
+ disablePreviousMonth :'k-disable-previous-month-btn',
273
+ disableNextMonth :'k-disable-next-month-btn',
274
+ disablePreviousYear :'k-disable-previous-year-btn',
275
+ disableNextYear :'k-disable-next-year-btn',
276
+ disableYearNav :'k-disable-year-nav'
277
+ },
278
+
279
+ disablePreviousMonth: false,
280
+ disableNextMonth: false,
281
+ disablePreviousYear: false,
282
+ disableNextYear: false,
283
+
284
+ directions: {
285
+ 'past' :function (date) {return moment(date).yearDay() >= today.yearDay();},
286
+ 'today-past' :function (date) {return moment(date).yearDay() > today.yearDay();},
287
+ 'any' :function (date) {return false;},
288
+ 'today-future' :function (date) {return moment(date).yearDay() < today.yearDay();},
289
+ 'future' :function (date) {return moment(date).yearDay() <= today.yearDay();}
290
+ },
291
+
292
+ getSelectedAsDates : function () {
293
+ var out = [];
294
+ var i=0, c = this._sel.length;
295
+ for (;i<c;i++) {
296
+ out.push(this._sel[i].toDate());
297
+ }
298
+ return out;
299
+ },
300
+
301
+ getSelectedAsText : function (format) {
302
+ var out = [];
303
+ var i=0, c = this._sel.length;
304
+ for (;i<c;i++) {
305
+ out.push(this._sel[i].format(format || this.settings.format || 'YYYY-MM-DD'));
306
+ }
307
+ return out;
308
+ },
309
+
310
+ getSelectedRaw : function () {
311
+ var out = [];
312
+ var i=0, c = this._sel.length;
313
+ for (;i<c;i++) {
314
+ out.push(moment(this._sel[i]));
315
+ }
316
+ return out;
317
+ },
318
+
319
+ getSelected : function (format) {
320
+ var sel = this.getSelectedAsText(format);
321
+ switch (this.settings.mode) {
322
+ case 'range':
323
+ sel.splice(2); //shouldn't be more than two, but lets just make sure.
324
+ return sel.join(this.settings.rangeDelimiter);
325
+
326
+ case 'multiple':
327
+ return sel.join(this.settings.multipleDelimiter);
328
+
329
+ case 'single':
330
+ /* falls through */
331
+ default:
332
+ return (sel[0] || null);
333
+ }
334
+ },
335
+
336
+ isSelected : function (input) {
337
+ input = moment(input).yearDay();
338
+ if (input < 1 || !this._sel || this._sel.length < 1) return false;
339
+
340
+ switch (this.settings.mode) {
341
+ case 'range':
342
+ var a = this._sel[0] ? this._sel[0].yearDay() : 0,
343
+ b = this._sel[1] ? this._sel[1].yearDay() : 0;
344
+
345
+ if (a === input || b === input) return 1;
346
+ if (!a || !b) return 0;
347
+
348
+ if ((input > a && input < b) || (a<b && input < a && input > b)) return -1;
349
+ return false;
350
+
351
+ case 'multiple':
352
+ var i = this._sel.length;
353
+ while (i--) {
354
+ if (this._sel[i].yearDay() === input) {
355
+ return true;
356
+ }
357
+ }
358
+ return false;
359
+
360
+
361
+ case 'single':
362
+ /* falls through */
363
+ default:
364
+ return (this._sel[0] && (this._sel[0].yearDay() === input));
365
+ }
366
+
367
+ return false;
368
+ },
369
+
370
+ setSelected : function (input, draw) {
371
+ var i,
372
+ new_dates = parseDates(input, this.settings.parseSplitDelimiter, this.settings.format),
373
+ old_dates = parseDates(this.getSelected(), this.settings.parseSplitDelimiter, this.settings.format);
374
+
375
+ i = old_dates.length;
376
+ while(i--) { this.removeSelected(old_dates[i], false); }
377
+
378
+ i = new_dates.length;
379
+ while(i--) { this.addSelected(new_dates[i], false); }
380
+
381
+ if (draw !== false) this.draw();
382
+ },
383
+
384
+ addSelected : function (date, draw) {
385
+ date = moment(date, this.settings.format).hours(12);
386
+
387
+ if(this.settings.dayOutOfMonthClickable && this.settings.mode !== 'range'){ this.makeSelectedDateVisible(date); }
388
+
389
+ switch (this.settings.mode) {
390
+ case 'multiple':
391
+ if (!this.isSelected(date)) this._sel.push(date);
392
+ else return false;
393
+ break;
394
+ case 'range':
395
+
396
+ if (this._sel.length !== 1) this._sel = [date];
397
+ else {
398
+ if (date.yearDay() > this._sel[0].yearDay()) this._sel[1] = date;
399
+ else this._sel = [date, this._sel[0]];
400
+ }
401
+ break;
402
+ case 'single':
403
+ /* falls through */
404
+ default:
405
+ this._sel = [date];
406
+ break;
407
+ }
408
+ this._sel.sort(function (a,b) {return a.yearDay() - b.yearDay();});
409
+ this.publish('change', this, [date]);
410
+ if (draw !== false) this.draw();
411
+ return true;
412
+ },
413
+
414
+ makeSelectedDateVisible: function (date) {
415
+ outOfViewMonth = moment(date).date('1').diff(this.viewStartDate,'months');
416
+
417
+ if(outOfViewMonth < 0){
418
+ this.viewStartDate.subtract('months',1);
419
+ }
420
+ else if(outOfViewMonth > 0 && outOfViewMonth >= this.settings.months){
421
+ this.viewStartDate.add('months',1);
422
+ }
423
+ },
424
+
425
+ removeSelected : function (date, draw) {
426
+ date = moment(date, this.settings.format).hours(12);
427
+ var i = this._sel.length;
428
+ while (i--) {
429
+ if (this._sel[i].yearDay() === date.yearDay()) {
430
+ this._sel.splice(i,1);
431
+ this.publish('change', this, [date]);
432
+ if (draw !== false) this.draw();
433
+ return true;
434
+ }
435
+ }
436
+ return false;
437
+ },
438
+
439
+ draw : function draw() {
440
+ // return;
441
+ var month = moment(this.viewStartDate).hours(12), //force middle of the day to avoid any weird date shifts
442
+ day,
443
+ classes = this.classes,
444
+ cal,
445
+ $span,
446
+ klass,
447
+ i=0, c,
448
+ j=0, k,
449
+ s,
450
+ dateString,
451
+ opts = this.settings,
452
+ diff;
453
+
454
+ c = this.calendars.length;
455
+
456
+ do {
457
+ day = moment(month).date(1);
458
+ day.day( day.day() < this.settings.weekStart ? this.settings.weekStart-7 : this.settings.weekStart);
459
+ //if the first day of the month is less than our week start, back up a week
460
+
461
+ cal = this.calendars[i];
462
+ cal.caption.innerHTML = month.format(this.settings.titleFormat);
463
+ j = 0;
464
+ do {
465
+ $span = cal.days[j];
466
+
467
+ klass = [];
468
+
469
+ s = this.isSelected(day);
470
+
471
+ if (s) klass.push(({'-1':classes.dayInRange,'1':classes.daySelected, 'true':classes.daySelected})[s]);
472
+
473
+ if (day.month() != month.month()) klass.push(classes.dayOutOfMonth);
474
+ else klass.push(classes.dayInMonth);
475
+
476
+ if (!(this.blackout(day) || this.direction(day) || (day.month() != month.month() && opts.dayOutOfMonthClickable === false)) || s>0) klass.push(classes.dayActive);
477
+
478
+ if (day.yearDay() === today.yearDay()) klass.push(classes.dayToday);
479
+
480
+ dateString = day.format(this.settings.dayAttributeFormat);
481
+ if (opts.dateClassMap[dateString]) klass.push(opts.dateClassMap[dateString]);
482
+
483
+ $span.innerHTML = day.format(opts.dayNumberFormat);
484
+ $span.className = klass.join(' ');
485
+ $span.setAttribute('data-date', dateString);
486
+
487
+
488
+ day.add('days',1);
489
+ } while (++j < 42);
490
+ month.add('months',1);
491
+ } while (++i < c);
492
+
493
+ if (opts.directionScrolling) {
494
+ if (opts.direction==='today-past' || opts.direction==='past') {
495
+ diff = month.add({m:1}).diff(moment(), 'months', true);
496
+ if (diff <= 0) {
497
+ this.disableNextMonth = false;
498
+ util.removeClassName(this.container, classes.disableNextMonth);
499
+ } else {
500
+ this.disableNextMonth = true;
501
+ util.addClassName(this.container, classes.disableNextMonth);
502
+ }
503
+
504
+ } else if (opts.direction==='today-future' || opts.direction==='future') {
505
+ diff = month.subtract({m:1}).diff(moment(), 'months', true);
506
+ if (diff > opts.months) {
507
+ this.disablePreviousMonth = false;
508
+ util.removeClassName(this.container, classes.disablePreviousMonth);
509
+ } else {
510
+ this.disablePreviousMonth = true;
511
+ util.addClassName(this.container, classes.disablePreviousMonth);
512
+ }
513
+
514
+ }
515
+
516
+
517
+ if (opts.direction==='today-past' || opts.direction==='past') {
518
+ diff = month.add({m:12}).diff(moment(), 'months', true);
519
+ if (diff <= -11) {
520
+ this.disableNextYear = false;
521
+ util.removeClassName(this.container, classes.disableNextYear);
522
+ } else {
523
+ this.disableNextYear = true;
524
+ util.addClassName(this.container, classes.disableNextYear);
525
+ }
526
+
527
+ } else if (opts.direction==='today-future' || opts.direction==='future') {
528
+ diff = month.subtract({m:12}).diff(moment(), 'months', true);
529
+ if (diff > (11 + opts.months)) {
530
+ this.disablePreviousYear = false;
531
+ util.removeClassName(this.container, classes.disablePreviousYear);
532
+ } else {
533
+ this.disablePreviousYear = true;
534
+ util.addClassName(this.container, classes.disablePreviousYear);
535
+ }
536
+
537
+ }
538
+
539
+ }
540
+ }
541
+ };
542
+
543
+ var parseDates = function (input, delimiter, format) {
544
+ var output = [];
545
+
546
+ if (typeof input === 'string') {
547
+ input = input.split(delimiter);
548
+ } else if (!util.isArray(input)) {
549
+ input = [input];
550
+ }
551
+
552
+ var c = input.length,
553
+ i = 0;
554
+
555
+ do {
556
+ if (input[i]) output.push( moment(input[i], format).hours(12) );
557
+ } while (++i < c);
558
+
559
+ return output;
560
+ };
561
+
562
+
563
+
564
+ window.Kalendae = Kalendae;
565
+
566
+ var util = Kalendae.util = {
567
+
568
+ isIE8: function() {
569
+ return !!( (/msie 8./i).test(navigator.appVersion) && !(/opera/i).test(navigator.userAgent) && window.ActiveXObject && XDomainRequest && !window.msPerformance );
570
+ },
571
+
572
+ // ELEMENT FUNCTIONS
573
+
574
+ $: function (elem) {
575
+ return (typeof elem == 'string') ? document.getElementById(elem) : elem;
576
+ },
577
+
578
+ $$: function (selector) {
579
+ return document.querySelectorAll(selector);
580
+ },
581
+
582
+ make: function (tagName, attributes, attach) {
583
+ var k, e = document.createElement(tagName);
584
+ if (!!attributes) for (k in attributes) if (attributes.hasOwnProperty(k)) e.setAttribute(k, attributes[k]);
585
+ if (!!attach) attach.appendChild(e);
586
+ return e;
587
+ },
588
+
589
+ // Returns true if the DOM element is visible, false if it's hidden.
590
+ // Checks if display is anything other than none.
591
+ isVisible: function (elem) {
592
+ // shamelessly copied from jQuery
593
+ return elem.offsetWidth > 0 || elem.offsetHeight > 0;
594
+ },
595
+
596
+ getStyle: function (elem, styleProp) {
597
+ var y;
598
+ if (elem.currentStyle) {
599
+ y = elem.currentStyle[styleProp];
600
+ } else if (window.getComputedStyle) {
601
+ y = window.getComputedStyle(elem, null)[styleProp];
602
+ }
603
+ return y;
604
+ },
605
+
606
+ domReady:function (f){/in/.test(document.readyState) ? setTimeout(function() {util.domReady(f);},9) : f()},
607
+
608
+ // Adds a listener callback to a DOM element which is fired on a specified
609
+ // event. Callback is sent the event object and the element that triggered the event
610
+ addEvent: function (elem, eventName, callback) {
611
+ var listener = function (event) {
612
+ event = event || window.event;
613
+ var target = event.target || event.srcElement;
614
+ var block = callback.apply(elem, [event, target]);
615
+ if (block === false) {
616
+ if (!!event.preventDefault) event.preventDefault();
617
+ else {
618
+ event.returnValue = false;
619
+ event.cancelBubble = true;
620
+ }
621
+ }
622
+ return block;
623
+ };
624
+ if (elem.attachEvent) { // IE only. The "on" is mandatory.
625
+ elem.attachEvent("on" + eventName, listener);
626
+ } else { // Other browsers.
627
+ elem.addEventListener(eventName, listener, false);
628
+ }
629
+ return listener;
630
+ },
631
+
632
+ // Removes a listener callback from a DOM element which is fired on a specified
633
+ // event.
634
+ removeEvent: function (elem, event, listener) {
635
+ if (elem.detachEvent) { // IE only. The "on" is mandatory.
636
+ elem.detachEvent("on" + event, listener);
637
+ } else { // Other browsers.
638
+ elem.removeEventListener(event, listener, false);
639
+ }
640
+ },
641
+
642
+ hasClassName: function(elem, className) { //copied and modified from Prototype.js
643
+ if (!(elem = util.$(elem))) return false;
644
+ var eClassName = elem.className;
645
+ return (eClassName.length > 0 && (eClassName == className || new RegExp("(^|\\s)" + className + "(\\s|$)").test(eClassName)));
646
+ },
647
+
648
+ addClassName: function(elem, className) { //copied and modified from Prototype.js
649
+ if (!(elem = util.$(elem))) return;
650
+ if (!util.hasClassName(elem, className)) elem.className += (elem.className ? ' ' : '') + className;
651
+ },
652
+
653
+ removeClassName: function(elem, className) { //copied and modified from Prototype.js
654
+ if (!(elem = util.$(elem))) return;
655
+ elem.className = util.trimString(elem.className.replace(new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' '));
656
+ },
657
+
658
+ isFixed: function (elem) {
659
+ do {
660
+ if (util.getStyle(elem, 'position') === 'fixed') return true;
661
+ } while ((elem = elem.offsetParent));
662
+ return false;
663
+ },
664
+
665
+ scrollContainer: function (elem) {
666
+ do {
667
+ var overflow = util.getStyle(elem, 'overflow');
668
+ if (overflow === 'auto' || overflow === 'scroll') return elem;
669
+ } while ((elem = elem.parentNode) && elem != window.document.body);
670
+ return null;
671
+ },
672
+
673
+ getPosition: function (elem, isInner) {
674
+ var x = elem.offsetLeft,
675
+ y = elem.offsetTop,
676
+ r = {};
677
+
678
+ if (!isInner) {
679
+ while ((elem = elem.offsetParent)) {
680
+ x += elem.offsetLeft;
681
+ y += elem.offsetTop;
682
+ }
683
+ }
684
+
685
+ r[0] = r.left = x;
686
+ r[1] = r.top = y;
687
+ return r;
688
+ },
689
+
690
+ getHeight: function (elem) {
691
+ return elem.offsetHeight || elem.scrollHeight;
692
+ },
693
+
694
+ getWidth: function (elem) {
695
+ return elem.offsetWidth || elem.scrollWidth;
696
+ },
697
+
698
+
699
+ // TEXT FUNCTIONS
700
+
701
+ trimString: function (input) {
702
+ return input.replace(/^\s+/, '').replace(/\s+$/, '');
703
+ },
704
+
705
+
706
+ // OBJECT FUNCTIONS
707
+
708
+ merge: function () {
709
+ /* Combines multiple objects into one.
710
+ * Syntax: util.extend([true], object1, object2, ... objectN)
711
+ * If first argument is true, function will merge recursively.
712
+ */
713
+
714
+ var deep = (arguments[0]===true),
715
+ d = {},
716
+ i = deep?1:0;
717
+
718
+ var _c = function (a, b) {
719
+ if (typeof b !== 'object') return;
720
+ for (var k in b) if (b.hasOwnProperty(k)) {
721
+ //if property is an object or array, merge the contents instead of overwriting, if extend() was called as such
722
+ if (deep && typeof a[k] === 'object' && typeof b[k] === 'object') _update(a[k], b[k]);
723
+ else a[k] = b[k];
724
+ }
725
+ return a;
726
+ };
727
+
728
+ for (; i < arguments.length; i++) {
729
+ _c(d, arguments[i]);
730
+ }
731
+ return d;
732
+ },
733
+
734
+ isArray: function (array) {
735
+ return Object.prototype.toString.call(array) == "[object Array]"
736
+ }
737
+ };
738
+
739
+
740
+ //auto-initializaiton code
741
+ if (typeof document.addEventListener === 'function') Kalendae.util.domReady(function () {
742
+ var els = util.$$('.auto-kal'),
743
+ i = els.length,
744
+ e,
745
+ options,
746
+ optionsRaw;
747
+
748
+ while (i--) {
749
+ e = els[i];
750
+ optionsRaw = e.getAttribute('data-kal');
751
+ options = (optionsRaw == null || optionsRaw == "") ? {} : (new Function('return {' + optionsRaw + '};'))();
752
+
753
+ if (e.tagName === 'INPUT') {
754
+ //if element is an input, bind a popup calendar to the input.
755
+ new Kalendae.Input(e, options);
756
+ } else {
757
+ //otherwise, insert a flat calendar into the element.
758
+ new Kalendae(util.merge(options, {attachTo:e}));
759
+ }
760
+
761
+ }
762
+ });
763
+ Kalendae.Input = function (targetElement, options) {
764
+ if (typeof document.addEventListener !== 'function' && !util.isIE8()) return;
765
+
766
+ var $input = this.input = util.$(targetElement),
767
+ overwriteInput,
768
+ $closeButton,
769
+ changing = false;
770
+
771
+ if (!$input || $input.tagName !== 'INPUT') throw "First argument for Kalendae.Input must be an <input> element or a valid element id.";
772
+
773
+ var self = this,
774
+ classes = self.classes,
775
+ opts = self.settings = util.merge(self.defaults, options);
776
+
777
+ //force attachment to the body
778
+ opts.attachTo = window.document.body;
779
+
780
+ //if no override provided, use the input's contents
781
+ if (!opts.selected) opts.selected = $input.value;
782
+ else overwriteInput = true;
783
+
784
+ //call our parent constructor
785
+ Kalendae.call(self, opts);
786
+
787
+ //create the close button
788
+ if (opts.closeButton) {
789
+ $closeButton = util.make('a', {'class':classes.closeButton}, self.container);
790
+ util.addEvent($closeButton, 'click', function () {
791
+ $input.blur();
792
+ });
793
+ }
794
+
795
+ if (overwriteInput) $input.value = self.getSelected();
796
+
797
+ var $container = self.container,
798
+ noclose = false;
799
+
800
+ $container.style.display = 'none';
801
+ util.addClassName($container, classes.positioned);
802
+
803
+ util.addEvent($container, 'mousedown', function (event, target) {
804
+ noclose = true; //IE8 doesn't obey event blocking when it comes to focusing, so we have to do this shit.
805
+ });
806
+ util.addEvent(window.document, 'mousedown', function (event, target) {
807
+ noclose = false;
808
+ });
809
+
810
+ util.addEvent($input, 'focus', function () {
811
+ changing = true; // prevent setSelected from altering the input contents.
812
+ self.setSelected(this.value);
813
+ changing = false;
814
+ self.show();
815
+ });
816
+
817
+ util.addEvent($input, 'blur', function () {
818
+ if (noclose && util.isIE8()) {
819
+ noclose = false;
820
+ $input.focus();
821
+ }
822
+ else self.hide();
823
+ });
824
+ util.addEvent($input, 'keyup', function (event) {
825
+ changing = true; // prevent setSelected from altering the input contents.
826
+ self.setSelected(this.value);
827
+ changing = false;
828
+ });
829
+
830
+ var $scrollContainer = util.scrollContainer($input);
831
+
832
+ if( $scrollContainer ) {
833
+
834
+ // Hide calendar when $scrollContainer is scrolled
835
+ util.addEvent($scrollContainer, 'scroll', function (event) {
836
+ $input.blur();
837
+ });
838
+ }
839
+
840
+ self.subscribe('change', function () {
841
+ if (changing) {
842
+ // the change event came from an internal modification, don't update the field contents
843
+ return;
844
+ }
845
+ $input.value = self.getSelected();
846
+ });
847
+
848
+ };
849
+
850
+ Kalendae.Input.prototype = util.merge(Kalendae.prototype, {
851
+ defaults : util.merge(Kalendae.prototype.defaults, {
852
+ format: 'MM/DD/YYYY',
853
+ side: 'bottom',
854
+ closeButton: true,
855
+ offsetLeft: 0,
856
+ offsetTop: 0
857
+ }),
858
+ classes : util.merge(Kalendae.prototype.classes, {
859
+ positioned : 'k-floating',
860
+ closeButton: 'k-btn-close'
861
+ }),
862
+
863
+ show : function () {
864
+ var $container = this.container,
865
+ style = $container.style,
866
+ $input = this.input,
867
+ pos = util.getPosition($input),
868
+ $scrollContainer = util.scrollContainer($input),
869
+ scrollTop = $scrollContainer ? $scrollContainer.scrollTop : 0,
870
+ opts = this.settings;
871
+
872
+ style.display = '';
873
+ switch (opts.side) {
874
+ case 'left':
875
+ style.left = (pos.left - util.getWidth($container) + opts.offsetLeft) + 'px';
876
+ style.top = (pos.top + opts.offsetTop - scrollTop) + 'px';
877
+ break;
878
+ case 'right':
879
+ style.left = (pos.left + util.getWidth($input)) + 'px';
880
+ style.top = (pos.top + opts.offsetTop - scrollTop) + 'px';
881
+ break;
882
+ case 'top':
883
+ style.left = (pos.left + opts.offsetLeft) + 'px';
884
+ style.top = (pos.top - util.getHeight($container) + opts.offsetTop - scrollTop) + 'px';
885
+ break;
886
+ case 'bottom':
887
+ /* falls through */
888
+ default:
889
+ style.left = (pos.left + opts.offsetLeft) + 'px';
890
+ style.top = (pos.top + util.getHeight($input) + opts.offsetTop - scrollTop) + 'px';
891
+ break;
892
+ }
893
+
894
+ style.position = util.isFixed($input) ? 'fixed' : 'absolute';
895
+
896
+ this.publish('show', this);
897
+ },
898
+
899
+ hide : function () {
900
+ this.container.style.display = 'none';
901
+ this.publish('hide', this);
902
+ }
903
+
904
+ });
905
+
906
+
907
+ /*!
908
+ * MinPubSub, modified for use on Kalendae
909
+ * Copyright(c) 2011 Daniel Lamb <daniellmb.com>
910
+ * https://github.com/daniellmb/MinPubSub
911
+ * MIT Licensed
912
+ */
913
+
914
+ var MinPubSub = function(d){
915
+
916
+ if (!d) d = this;
917
+
918
+ // the topic/subscription hash
919
+ var cache = d.c_ || {}; //check for "c_" cache for unit testing
920
+
921
+ d.publish = function(/* String */ topic, /* Object */ target, /* Array? */ args){
922
+ // summary:
923
+ // Publish some data on a named topic.
924
+ // topic: String
925
+ // The channel to publish on
926
+ // args: Array?
927
+ // The data to publish. Each array item is converted into an ordered
928
+ // arguments on the subscribed functions.
929
+ //
930
+ // example:
931
+ // Publish stuff on '/some/topic'. Anything subscribed will be called
932
+ // with a function signature like: function(a,b,c){ ... }
933
+ //
934
+ // publish("/some/topic", ["a","b","c"]);
935
+
936
+ var subs = cache[topic],
937
+ len = subs ? subs.length : 0,
938
+ r;
939
+
940
+ //can change loop or reverse array if the order matters
941
+ while(len--){
942
+ r = subs[len].apply(target, args || []);
943
+ if (typeof r === 'boolean') return r;
944
+ }
945
+ };
946
+
947
+ d.subscribe = function(/* String */ topic, /* Function */ callback, /* Boolean */ topPriority){
948
+ // summary:
949
+ // Register a callback on a named topic.
950
+ // topic: String
951
+ // The channel to subscribe to
952
+ // callback: Function
953
+ // The handler event. Anytime something is publish'ed on a
954
+ // subscribed channel, the callback will be called with the
955
+ // published array as ordered arguments.
956
+ //
957
+ // returns: Array
958
+ // A handle which can be used to unsubscribe this particular subscription.
959
+ //
960
+ // example:
961
+ // subscribe("/some/topic", function(a, b, c){ /* handle data */ });
962
+
963
+ if(!cache[topic]){
964
+ cache[topic] = [];
965
+ }
966
+ if (topPriority)
967
+ cache[topic].push(callback);
968
+ else
969
+ cache[topic].unshift(callback);
970
+ return [topic, callback]; // Array
971
+ };
972
+
973
+ d.unsubscribe = function(/* Array */ handle){
974
+ // summary:
975
+ // Disconnect a subscribed function for a topic.
976
+ // handle: Array
977
+ // The return value from a subscribe call.
978
+ // example:
979
+ // var handle = subscribe("/some/topic", function(){});
980
+ // unsubscribe(handle);
981
+
982
+ var subs = cache[handle[0]],
983
+ callback = handle[1],
984
+ len = subs ? subs.length : 0;
985
+
986
+ while(len--){
987
+ if(subs[len] === callback){
988
+ subs.splice(len, 1);
989
+ }
990
+ }
991
+ };
992
+
993
+ };// moment.js
994
+ // version : 2.0.0
995
+ // author : Tim Wood
996
+ // license : MIT
997
+ // momentjs.com
998
+
999
+ (function (undefined) {
1000
+
1001
+ /************************************
1002
+ Constants
1003
+ ************************************/
1004
+
1005
+ var moment,
1006
+ VERSION = "2.0.0",
1007
+ round = Math.round, i,
1008
+ // internal storage for language config files
1009
+ languages = {},
1010
+
1011
+ // check for nodeJS
1012
+ hasModule = (typeof module !== 'undefined' && module.exports),
1013
+
1014
+ // ASP.NET json date format regex
1015
+ aspNetJsonRegex = /^\/?Date\((\-?\d+)/i,
1016
+
1017
+ // format tokens
1018
+ formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYY|YYYY|YY|a|A|hh?|HH?|mm?|ss?|SS?S?|X|zz?|ZZ?|.)/g,
1019
+ localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,
1020
+
1021
+ // parsing tokens
1022
+ parseMultipleFormatChunker = /([0-9a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)/gi,
1023
+
1024
+ // parsing token regexes
1025
+ parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99
1026
+ parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999
1027
+ parseTokenThreeDigits = /\d{3}/, // 000 - 999
1028
+ parseTokenFourDigits = /\d{1,4}/, // 0 - 9999
1029
+ parseTokenSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999
1030
+ parseTokenWord = /[0-9]*[a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF]+\s*?[\u0600-\u06FF]+/i, // any word (or two) characters or numbers including two word month in arabic.
1031
+ parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/i, // +00:00 -00:00 +0000 -0000 or Z
1032
+ parseTokenT = /T/i, // T (ISO seperator)
1033
+ parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123
1034
+
1035
+ // preliminary iso regex
1036
+ // 0000-00-00 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000
1037
+ isoRegex = /^\s*\d{4}-\d\d-\d\d((T| )(\d\d(:\d\d(:\d\d(\.\d\d?\d?)?)?)?)?([\+\-]\d\d:?\d\d)?)?/,
1038
+ isoFormat = 'YYYY-MM-DDTHH:mm:ssZ',
1039
+
1040
+ // iso time formats and regexes
1041
+ isoTimes = [
1042
+ ['HH:mm:ss.S', /(T| )\d\d:\d\d:\d\d\.\d{1,3}/],
1043
+ ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/],
1044
+ ['HH:mm', /(T| )\d\d:\d\d/],
1045
+ ['HH', /(T| )\d\d/]
1046
+ ],
1047
+
1048
+ // timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"]
1049
+ parseTimezoneChunker = /([\+\-]|\d\d)/gi,
1050
+
1051
+ // getter and setter names
1052
+ proxyGettersAndSetters = 'Month|Date|Hours|Minutes|Seconds|Milliseconds'.split('|'),
1053
+ unitMillisecondFactors = {
1054
+ 'Milliseconds' : 1,
1055
+ 'Seconds' : 1e3,
1056
+ 'Minutes' : 6e4,
1057
+ 'Hours' : 36e5,
1058
+ 'Days' : 864e5,
1059
+ 'Months' : 2592e6,
1060
+ 'Years' : 31536e6
1061
+ },
1062
+
1063
+ // format function strings
1064
+ formatFunctions = {},
1065
+
1066
+ // tokens to ordinalize and pad
1067
+ ordinalizeTokens = 'DDD w W M D d'.split(' '),
1068
+ paddedTokens = 'M D H h m s w W'.split(' '),
1069
+
1070
+ formatTokenFunctions = {
1071
+ M : function () {
1072
+ return this.month() + 1;
1073
+ },
1074
+ MMM : function (format) {
1075
+ return this.lang().monthsShort(this, format);
1076
+ },
1077
+ MMMM : function (format) {
1078
+ return this.lang().months(this, format);
1079
+ },
1080
+ D : function () {
1081
+ return this.date();
1082
+ },
1083
+ DDD : function () {
1084
+ return this.dayOfYear();
1085
+ },
1086
+ d : function () {
1087
+ return this.day();
1088
+ },
1089
+ dd : function (format) {
1090
+ return this.lang().weekdaysMin(this, format);
1091
+ },
1092
+ ddd : function (format) {
1093
+ return this.lang().weekdaysShort(this, format);
1094
+ },
1095
+ dddd : function (format) {
1096
+ return this.lang().weekdays(this, format);
1097
+ },
1098
+ w : function () {
1099
+ return this.week();
1100
+ },
1101
+ W : function () {
1102
+ return this.isoWeek();
1103
+ },
1104
+ YY : function () {
1105
+ return leftZeroFill(this.year() % 100, 2);
1106
+ },
1107
+ YYYY : function () {
1108
+ return leftZeroFill(this.year(), 4);
1109
+ },
1110
+ YYYYY : function () {
1111
+ return leftZeroFill(this.year(), 5);
1112
+ },
1113
+ a : function () {
1114
+ return this.lang().meridiem(this.hours(), this.minutes(), true);
1115
+ },
1116
+ A : function () {
1117
+ return this.lang().meridiem(this.hours(), this.minutes(), false);
1118
+ },
1119
+ H : function () {
1120
+ return this.hours();
1121
+ },
1122
+ h : function () {
1123
+ return this.hours() % 12 || 12;
1124
+ },
1125
+ m : function () {
1126
+ return this.minutes();
1127
+ },
1128
+ s : function () {
1129
+ return this.seconds();
1130
+ },
1131
+ S : function () {
1132
+ return ~~(this.milliseconds() / 100);
1133
+ },
1134
+ SS : function () {
1135
+ return leftZeroFill(~~(this.milliseconds() / 10), 2);
1136
+ },
1137
+ SSS : function () {
1138
+ return leftZeroFill(this.milliseconds(), 3);
1139
+ },
1140
+ Z : function () {
1141
+ var a = -this.zone(),
1142
+ b = "+";
1143
+ if (a < 0) {
1144
+ a = -a;
1145
+ b = "-";
1146
+ }
1147
+ return b + leftZeroFill(~~(a / 60), 2) + ":" + leftZeroFill(~~a % 60, 2);
1148
+ },
1149
+ ZZ : function () {
1150
+ var a = -this.zone(),
1151
+ b = "+";
1152
+ if (a < 0) {
1153
+ a = -a;
1154
+ b = "-";
1155
+ }
1156
+ return b + leftZeroFill(~~(10 * a / 6), 4);
1157
+ },
1158
+ X : function () {
1159
+ return this.unix();
1160
+ }
1161
+ };
1162
+
1163
+ function padToken(func, count) {
1164
+ return function (a) {
1165
+ return leftZeroFill(func.call(this, a), count);
1166
+ };
1167
+ }
1168
+ function ordinalizeToken(func) {
1169
+ return function (a) {
1170
+ return this.lang().ordinal(func.call(this, a));
1171
+ };
1172
+ }
1173
+
1174
+ while (ordinalizeTokens.length) {
1175
+ i = ordinalizeTokens.pop();
1176
+ formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i]);
1177
+ }
1178
+ while (paddedTokens.length) {
1179
+ i = paddedTokens.pop();
1180
+ formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2);
1181
+ }
1182
+ formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3);
1183
+
1184
+
1185
+ /************************************
1186
+ Constructors
1187
+ ************************************/
1188
+
1189
+ function Language() {
1190
+
1191
+ }
1192
+
1193
+ // Moment prototype object
1194
+ function Moment(config) {
1195
+ extend(this, config);
1196
+ }
1197
+
1198
+ // Duration Constructor
1199
+ function Duration(duration) {
1200
+ var data = this._data = {},
1201
+ years = duration.years || duration.year || duration.y || 0,
1202
+ months = duration.months || duration.month || duration.M || 0,
1203
+ weeks = duration.weeks || duration.week || duration.w || 0,
1204
+ days = duration.days || duration.day || duration.d || 0,
1205
+ hours = duration.hours || duration.hour || duration.h || 0,
1206
+ minutes = duration.minutes || duration.minute || duration.m || 0,
1207
+ seconds = duration.seconds || duration.second || duration.s || 0,
1208
+ milliseconds = duration.milliseconds || duration.millisecond || duration.ms || 0;
1209
+
1210
+ // representation for dateAddRemove
1211
+ this._milliseconds = milliseconds +
1212
+ seconds * 1e3 + // 1000
1213
+ minutes * 6e4 + // 1000 * 60
1214
+ hours * 36e5; // 1000 * 60 * 60
1215
+ // Because of dateAddRemove treats 24 hours as different from a
1216
+ // day when working around DST, we need to store them separately
1217
+ this._days = days +
1218
+ weeks * 7;
1219
+ // It is impossible translate months into days without knowing
1220
+ // which months you are are talking about, so we have to store
1221
+ // it separately.
1222
+ this._months = months +
1223
+ years * 12;
1224
+
1225
+ // The following code bubbles up values, see the tests for
1226
+ // examples of what that means.
1227
+ data.milliseconds = milliseconds % 1000;
1228
+ seconds += absRound(milliseconds / 1000);
1229
+
1230
+ data.seconds = seconds % 60;
1231
+ minutes += absRound(seconds / 60);
1232
+
1233
+ data.minutes = minutes % 60;
1234
+ hours += absRound(minutes / 60);
1235
+
1236
+ data.hours = hours % 24;
1237
+ days += absRound(hours / 24);
1238
+
1239
+ days += weeks * 7;
1240
+ data.days = days % 30;
1241
+
1242
+ months += absRound(days / 30);
1243
+
1244
+ data.months = months % 12;
1245
+ years += absRound(months / 12);
1246
+
1247
+ data.years = years;
1248
+ }
1249
+
1250
+
1251
+ /************************************
1252
+ Helpers
1253
+ ************************************/
1254
+
1255
+
1256
+ function extend(a, b) {
1257
+ for (var i in b) {
1258
+ if (b.hasOwnProperty(i)) {
1259
+ a[i] = b[i];
1260
+ }
1261
+ }
1262
+ return a;
1263
+ }
1264
+
1265
+ function absRound(number) {
1266
+ if (number < 0) {
1267
+ return Math.ceil(number);
1268
+ } else {
1269
+ return Math.floor(number);
1270
+ }
1271
+ }
1272
+
1273
+ // left zero fill a number
1274
+ // see http://jsperf.com/left-zero-filling for performance comparison
1275
+ function leftZeroFill(number, targetLength) {
1276
+ var output = number + '';
1277
+ while (output.length < targetLength) {
1278
+ output = '0' + output;
1279
+ }
1280
+ return output;
1281
+ }
1282
+
1283
+ // helper function for _.addTime and _.subtractTime
1284
+ function addOrSubtractDurationFromMoment(mom, duration, isAdding) {
1285
+ var ms = duration._milliseconds,
1286
+ d = duration._days,
1287
+ M = duration._months,
1288
+ currentDate;
1289
+
1290
+ if (ms) {
1291
+ mom._d.setTime(+mom + ms * isAdding);
1292
+ }
1293
+ if (d) {
1294
+ mom.date(mom.date() + d * isAdding);
1295
+ }
1296
+ if (M) {
1297
+ currentDate = mom.date();
1298
+ mom.date(1)
1299
+ .month(mom.month() + M * isAdding)
1300
+ .date(Math.min(currentDate, mom.daysInMonth()));
1301
+ }
1302
+ }
1303
+
1304
+ // check if is an array
1305
+ function isArray(input) {
1306
+ return Object.prototype.toString.call(input) === '[object Array]';
1307
+ }
1308
+
1309
+ // compare two arrays, return the number of differences
1310
+ function compareArrays(array1, array2) {
1311
+ var len = Math.min(array1.length, array2.length),
1312
+ lengthDiff = Math.abs(array1.length - array2.length),
1313
+ diffs = 0,
1314
+ i;
1315
+ for (i = 0; i < len; i++) {
1316
+ if (~~array1[i] !== ~~array2[i]) {
1317
+ diffs++;
1318
+ }
1319
+ }
1320
+ return diffs + lengthDiff;
1321
+ }
1322
+
1323
+
1324
+ /************************************
1325
+ Languages
1326
+ ************************************/
1327
+
1328
+
1329
+ Language.prototype = {
1330
+ set : function (config) {
1331
+ var prop, i;
1332
+ for (i in config) {
1333
+ prop = config[i];
1334
+ if (typeof prop === 'function') {
1335
+ this[i] = prop;
1336
+ } else {
1337
+ this['_' + i] = prop;
1338
+ }
1339
+ }
1340
+ },
1341
+
1342
+ _months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
1343
+ months : function (m) {
1344
+ return this._months[m.month()];
1345
+ },
1346
+
1347
+ _monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),
1348
+ monthsShort : function (m) {
1349
+ return this._monthsShort[m.month()];
1350
+ },
1351
+
1352
+ monthsParse : function (monthName) {
1353
+ var i, mom, regex, output;
1354
+
1355
+ if (!this._monthsParse) {
1356
+ this._monthsParse = [];
1357
+ }
1358
+
1359
+ for (i = 0; i < 12; i++) {
1360
+ // make the regex if we don't have it already
1361
+ if (!this._monthsParse[i]) {
1362
+ mom = moment([2000, i]);
1363
+ regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
1364
+ this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
1365
+ }
1366
+ // test the regex
1367
+ if (this._monthsParse[i].test(monthName)) {
1368
+ return i;
1369
+ }
1370
+ }
1371
+ },
1372
+
1373
+ _weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
1374
+ weekdays : function (m) {
1375
+ return this._weekdays[m.day()];
1376
+ },
1377
+
1378
+ _weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),
1379
+ weekdaysShort : function (m) {
1380
+ return this._weekdaysShort[m.day()];
1381
+ },
1382
+
1383
+ _weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"),
1384
+ weekdaysMin : function (m) {
1385
+ return this._weekdaysMin[m.day()];
1386
+ },
1387
+
1388
+ _longDateFormat : {
1389
+ LT : "h:mm A",
1390
+ L : "MM/DD/YYYY",
1391
+ LL : "MMMM D YYYY",
1392
+ LLL : "MMMM D YYYY LT",
1393
+ LLLL : "dddd, MMMM D YYYY LT"
1394
+ },
1395
+ longDateFormat : function (key) {
1396
+ var output = this._longDateFormat[key];
1397
+ if (!output && this._longDateFormat[key.toUpperCase()]) {
1398
+ output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) {
1399
+ return val.slice(1);
1400
+ });
1401
+ this._longDateFormat[key] = output;
1402
+ }
1403
+ return output;
1404
+ },
1405
+
1406
+ meridiem : function (hours, minutes, isLower) {
1407
+ if (hours > 11) {
1408
+ return isLower ? 'pm' : 'PM';
1409
+ } else {
1410
+ return isLower ? 'am' : 'AM';
1411
+ }
1412
+ },
1413
+
1414
+ _calendar : {
1415
+ sameDay : '[Today at] LT',
1416
+ nextDay : '[Tomorrow at] LT',
1417
+ nextWeek : 'dddd [at] LT',
1418
+ lastDay : '[Yesterday at] LT',
1419
+ lastWeek : '[last] dddd [at] LT',
1420
+ sameElse : 'L'
1421
+ },
1422
+ calendar : function (key, mom) {
1423
+ var output = this._calendar[key];
1424
+ return typeof output === 'function' ? output.apply(mom) : output;
1425
+ },
1426
+
1427
+ _relativeTime : {
1428
+ future : "in %s",
1429
+ past : "%s ago",
1430
+ s : "a few seconds",
1431
+ m : "a minute",
1432
+ mm : "%d minutes",
1433
+ h : "an hour",
1434
+ hh : "%d hours",
1435
+ d : "a day",
1436
+ dd : "%d days",
1437
+ M : "a month",
1438
+ MM : "%d months",
1439
+ y : "a year",
1440
+ yy : "%d years"
1441
+ },
1442
+ relativeTime : function (number, withoutSuffix, string, isFuture) {
1443
+ var output = this._relativeTime[string];
1444
+ return (typeof output === 'function') ?
1445
+ output(number, withoutSuffix, string, isFuture) :
1446
+ output.replace(/%d/i, number);
1447
+ },
1448
+ pastFuture : function (diff, output) {
1449
+ var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
1450
+ return typeof format === 'function' ? format(output) : format.replace(/%s/i, output);
1451
+ },
1452
+
1453
+ ordinal : function (number) {
1454
+ return this._ordinal.replace("%d", number);
1455
+ },
1456
+ _ordinal : "%d",
1457
+
1458
+ preparse : function (string) {
1459
+ return string;
1460
+ },
1461
+
1462
+ postformat : function (string) {
1463
+ return string;
1464
+ },
1465
+
1466
+ week : function (mom) {
1467
+ return weekOfYear(mom, this._week.dow, this._week.doy);
1468
+ },
1469
+ _week : {
1470
+ dow : 0, // Sunday is the first day of the week.
1471
+ doy : 6 // The week that contains Jan 1st is the first week of the year.
1472
+ }
1473
+ };
1474
+
1475
+ // Loads a language definition into the `languages` cache. The function
1476
+ // takes a key and optionally values. If not in the browser and no values
1477
+ // are provided, it will load the language file module. As a convenience,
1478
+ // this function also returns the language values.
1479
+ function loadLang(key, values) {
1480
+ values.abbr = key;
1481
+ if (!languages[key]) {
1482
+ languages[key] = new Language();
1483
+ }
1484
+ languages[key].set(values);
1485
+ return languages[key];
1486
+ }
1487
+
1488
+ // Determines which language definition to use and returns it.
1489
+ //
1490
+ // With no parameters, it will return the global language. If you
1491
+ // pass in a language key, such as 'en', it will return the
1492
+ // definition for 'en', so long as 'en' has already been loaded using
1493
+ // moment.lang.
1494
+ function getLangDefinition(key) {
1495
+ if (!key) {
1496
+ return moment.fn._lang;
1497
+ }
1498
+ if (!languages[key] && hasModule) {
1499
+ require('./lang/' + key);
1500
+ }
1501
+ return languages[key];
1502
+ }
1503
+
1504
+
1505
+ /************************************
1506
+ Formatting
1507
+ ************************************/
1508
+
1509
+
1510
+ function removeFormattingTokens(input) {
1511
+ if (input.match(/\[.*\]/)) {
1512
+ return input.replace(/^\[|\]$/g, "");
1513
+ }
1514
+ return input.replace(/\\/g, "");
1515
+ }
1516
+
1517
+ function makeFormatFunction(format) {
1518
+ var array = format.match(formattingTokens), i, length;
1519
+
1520
+ for (i = 0, length = array.length; i < length; i++) {
1521
+ if (formatTokenFunctions[array[i]]) {
1522
+ array[i] = formatTokenFunctions[array[i]];
1523
+ } else {
1524
+ array[i] = removeFormattingTokens(array[i]);
1525
+ }
1526
+ }
1527
+
1528
+ return function (mom) {
1529
+ var output = "";
1530
+ for (i = 0; i < length; i++) {
1531
+ output += typeof array[i].call === 'function' ? array[i].call(mom, format) : array[i];
1532
+ }
1533
+ return output;
1534
+ };
1535
+ }
1536
+
1537
+ // format date using native date object
1538
+ function formatMoment(m, format) {
1539
+ var i = 5;
1540
+
1541
+ function replaceLongDateFormatTokens(input) {
1542
+ return m.lang().longDateFormat(input) || input;
1543
+ }
1544
+
1545
+ while (i-- && localFormattingTokens.test(format)) {
1546
+ format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
1547
+ }
1548
+
1549
+ if (!formatFunctions[format]) {
1550
+ formatFunctions[format] = makeFormatFunction(format);
1551
+ }
1552
+
1553
+ return formatFunctions[format](m);
1554
+ }
1555
+
1556
+
1557
+ /************************************
1558
+ Parsing
1559
+ ************************************/
1560
+
1561
+
1562
+ // get the regex to find the next token
1563
+ function getParseRegexForToken(token) {
1564
+ switch (token) {
1565
+ case 'DDDD':
1566
+ return parseTokenThreeDigits;
1567
+ case 'YYYY':
1568
+ return parseTokenFourDigits;
1569
+ case 'YYYYY':
1570
+ return parseTokenSixDigits;
1571
+ case 'S':
1572
+ case 'SS':
1573
+ case 'SSS':
1574
+ case 'DDD':
1575
+ return parseTokenOneToThreeDigits;
1576
+ case 'MMM':
1577
+ case 'MMMM':
1578
+ case 'dd':
1579
+ case 'ddd':
1580
+ case 'dddd':
1581
+ case 'a':
1582
+ case 'A':
1583
+ return parseTokenWord;
1584
+ case 'X':
1585
+ return parseTokenTimestampMs;
1586
+ case 'Z':
1587
+ case 'ZZ':
1588
+ return parseTokenTimezone;
1589
+ case 'T':
1590
+ return parseTokenT;
1591
+ case 'MM':
1592
+ case 'DD':
1593
+ case 'YY':
1594
+ case 'HH':
1595
+ case 'hh':
1596
+ case 'mm':
1597
+ case 'ss':
1598
+ case 'M':
1599
+ case 'D':
1600
+ case 'd':
1601
+ case 'H':
1602
+ case 'h':
1603
+ case 'm':
1604
+ case 's':
1605
+ return parseTokenOneOrTwoDigits;
1606
+ default :
1607
+ return new RegExp(token.replace('\\', ''));
1608
+ }
1609
+ }
1610
+
1611
+ // function to convert string input to date
1612
+ function addTimeToArrayFromToken(token, input, config) {
1613
+ var a, b,
1614
+ datePartArray = config._a;
1615
+
1616
+ switch (token) {
1617
+ // MONTH
1618
+ case 'M' : // fall through to MM
1619
+ case 'MM' :
1620
+ datePartArray[1] = (input == null) ? 0 : ~~input - 1;
1621
+ break;
1622
+ case 'MMM' : // fall through to MMMM
1623
+ case 'MMMM' :
1624
+ a = getLangDefinition(config._l).monthsParse(input);
1625
+ // if we didn't find a month name, mark the date as invalid.
1626
+ if (a != null) {
1627
+ datePartArray[1] = a;
1628
+ } else {
1629
+ config._isValid = false;
1630
+ }
1631
+ break;
1632
+ // DAY OF MONTH
1633
+ case 'D' : // fall through to DDDD
1634
+ case 'DD' : // fall through to DDDD
1635
+ case 'DDD' : // fall through to DDDD
1636
+ case 'DDDD' :
1637
+ if (input != null) {
1638
+ datePartArray[2] = ~~input;
1639
+ }
1640
+ break;
1641
+ // YEAR
1642
+ case 'YY' :
1643
+ datePartArray[0] = ~~input + (~~input > 68 ? 1900 : 2000);
1644
+ break;
1645
+ case 'YYYY' :
1646
+ case 'YYYYY' :
1647
+ datePartArray[0] = ~~input;
1648
+ break;
1649
+ // AM / PM
1650
+ case 'a' : // fall through to A
1651
+ case 'A' :
1652
+ config._isPm = ((input + '').toLowerCase() === 'pm');
1653
+ break;
1654
+ // 24 HOUR
1655
+ case 'H' : // fall through to hh
1656
+ case 'HH' : // fall through to hh
1657
+ case 'h' : // fall through to hh
1658
+ case 'hh' :
1659
+ datePartArray[3] = ~~input;
1660
+ break;
1661
+ // MINUTE
1662
+ case 'm' : // fall through to mm
1663
+ case 'mm' :
1664
+ datePartArray[4] = ~~input;
1665
+ break;
1666
+ // SECOND
1667
+ case 's' : // fall through to ss
1668
+ case 'ss' :
1669
+ datePartArray[5] = ~~input;
1670
+ break;
1671
+ // MILLISECOND
1672
+ case 'S' :
1673
+ case 'SS' :
1674
+ case 'SSS' :
1675
+ datePartArray[6] = ~~ (('0.' + input) * 1000);
1676
+ break;
1677
+ // UNIX TIMESTAMP WITH MS
1678
+ case 'X':
1679
+ config._d = new Date(parseFloat(input) * 1000);
1680
+ break;
1681
+ // TIMEZONE
1682
+ case 'Z' : // fall through to ZZ
1683
+ case 'ZZ' :
1684
+ config._useUTC = true;
1685
+ a = (input + '').match(parseTimezoneChunker);
1686
+ if (a && a[1]) {
1687
+ config._tzh = ~~a[1];
1688
+ }
1689
+ if (a && a[2]) {
1690
+ config._tzm = ~~a[2];
1691
+ }
1692
+ // reverse offsets
1693
+ if (a && a[0] === '+') {
1694
+ config._tzh = -config._tzh;
1695
+ config._tzm = -config._tzm;
1696
+ }
1697
+ break;
1698
+ }
1699
+
1700
+ // if the input is null, the date is not valid
1701
+ if (input == null) {
1702
+ config._isValid = false;
1703
+ }
1704
+ }
1705
+
1706
+ // convert an array to a date.
1707
+ // the array should mirror the parameters below
1708
+ // note: all values past the year are optional and will default to the lowest possible value.
1709
+ // [year, month, day , hour, minute, second, millisecond]
1710
+ function dateFromArray(config) {
1711
+ var i, date, input = [];
1712
+
1713
+ if (config._d) {
1714
+ return;
1715
+ }
1716
+
1717
+ for (i = 0; i < 7; i++) {
1718
+ config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
1719
+ }
1720
+
1721
+ // add the offsets to the time to be parsed so that we can have a clean array for checking isValid
1722
+ input[3] += config._tzh || 0;
1723
+ input[4] += config._tzm || 0;
1724
+
1725
+ date = new Date(0);
1726
+
1727
+ if (config._useUTC) {
1728
+ date.setUTCFullYear(input[0], input[1], input[2]);
1729
+ date.setUTCHours(input[3], input[4], input[5], input[6]);
1730
+ } else {
1731
+ date.setFullYear(input[0], input[1], input[2]);
1732
+ date.setHours(input[3], input[4], input[5], input[6]);
1733
+ }
1734
+
1735
+ config._d = date;
1736
+ }
1737
+
1738
+ // date from string and format string
1739
+ function makeDateFromStringAndFormat(config) {
1740
+ // This array is used to make a Date, either with `new Date` or `Date.UTC`
1741
+ var tokens = config._f.match(formattingTokens),
1742
+ string = config._i,
1743
+ i, parsedInput;
1744
+
1745
+ config._a = [];
1746
+
1747
+ for (i = 0; i < tokens.length; i++) {
1748
+ parsedInput = (getParseRegexForToken(tokens[i]).exec(string) || [])[0];
1749
+ if (parsedInput) {
1750
+ string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
1751
+ }
1752
+ // don't parse if its not a known token
1753
+ if (formatTokenFunctions[tokens[i]]) {
1754
+ addTimeToArrayFromToken(tokens[i], parsedInput, config);
1755
+ }
1756
+ }
1757
+ // handle am pm
1758
+ if (config._isPm && config._a[3] < 12) {
1759
+ config._a[3] += 12;
1760
+ }
1761
+ // if is 12 am, change hours to 0
1762
+ if (config._isPm === false && config._a[3] === 12) {
1763
+ config._a[3] = 0;
1764
+ }
1765
+ // return
1766
+ dateFromArray(config);
1767
+ }
1768
+
1769
+ // date from string and array of format strings
1770
+ function makeDateFromStringAndArray(config) {
1771
+ var tempConfig,
1772
+ tempMoment,
1773
+ bestMoment,
1774
+
1775
+ scoreToBeat = 99,
1776
+ i,
1777
+ currentDate,
1778
+ currentScore;
1779
+
1780
+ while (config._f.length) {
1781
+ tempConfig = extend({}, config);
1782
+ tempConfig._f = config._f.pop();
1783
+ makeDateFromStringAndFormat(tempConfig);
1784
+ tempMoment = new Moment(tempConfig);
1785
+
1786
+ if (tempMoment.isValid()) {
1787
+ bestMoment = tempMoment;
1788
+ break;
1789
+ }
1790
+
1791
+ currentScore = compareArrays(tempConfig._a, tempMoment.toArray());
1792
+
1793
+ if (currentScore < scoreToBeat) {
1794
+ scoreToBeat = currentScore;
1795
+ bestMoment = tempMoment;
1796
+ }
1797
+ }
1798
+
1799
+ extend(config, bestMoment);
1800
+ }
1801
+
1802
+ // date from iso format
1803
+ function makeDateFromString(config) {
1804
+ var i,
1805
+ string = config._i;
1806
+ if (isoRegex.exec(string)) {
1807
+ config._f = 'YYYY-MM-DDT';
1808
+ for (i = 0; i < 4; i++) {
1809
+ if (isoTimes[i][1].exec(string)) {
1810
+ config._f += isoTimes[i][0];
1811
+ break;
1812
+ }
1813
+ }
1814
+ if (parseTokenTimezone.exec(string)) {
1815
+ config._f += " Z";
1816
+ }
1817
+ makeDateFromStringAndFormat(config);
1818
+ } else {
1819
+ config._d = new Date(string);
1820
+ }
1821
+ }
1822
+
1823
+ function makeDateFromInput(config) {
1824
+ var input = config._i,
1825
+ matched = aspNetJsonRegex.exec(input);
1826
+
1827
+ if (input === undefined) {
1828
+ config._d = new Date();
1829
+ } else if (matched) {
1830
+ config._d = new Date(+matched[1]);
1831
+ } else if (typeof input === 'string') {
1832
+ makeDateFromString(config);
1833
+ } else if (isArray(input)) {
1834
+ config._a = input.slice(0);
1835
+ dateFromArray(config);
1836
+ } else {
1837
+ config._d = input instanceof Date ? new Date(+input) : new Date(input);
1838
+ }
1839
+ }
1840
+
1841
+
1842
+ /************************************
1843
+ Relative Time
1844
+ ************************************/
1845
+
1846
+
1847
+ // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
1848
+ function substituteTimeAgo(string, number, withoutSuffix, isFuture, lang) {
1849
+ return lang.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
1850
+ }
1851
+
1852
+ function relativeTime(milliseconds, withoutSuffix, lang) {
1853
+ var seconds = round(Math.abs(milliseconds) / 1000),
1854
+ minutes = round(seconds / 60),
1855
+ hours = round(minutes / 60),
1856
+ days = round(hours / 24),
1857
+ years = round(days / 365),
1858
+ args = seconds < 45 && ['s', seconds] ||
1859
+ minutes === 1 && ['m'] ||
1860
+ minutes < 45 && ['mm', minutes] ||
1861
+ hours === 1 && ['h'] ||
1862
+ hours < 22 && ['hh', hours] ||
1863
+ days === 1 && ['d'] ||
1864
+ days <= 25 && ['dd', days] ||
1865
+ days <= 45 && ['M'] ||
1866
+ days < 345 && ['MM', round(days / 30)] ||
1867
+ years === 1 && ['y'] || ['yy', years];
1868
+ args[2] = withoutSuffix;
1869
+ args[3] = milliseconds > 0;
1870
+ args[4] = lang;
1871
+ return substituteTimeAgo.apply({}, args);
1872
+ }
1873
+
1874
+
1875
+ /************************************
1876
+ Week of Year
1877
+ ************************************/
1878
+
1879
+
1880
+ // firstDayOfWeek 0 = sun, 6 = sat
1881
+ // the day of the week that starts the week
1882
+ // (usually sunday or monday)
1883
+ // firstDayOfWeekOfYear 0 = sun, 6 = sat
1884
+ // the first week is the week that contains the first
1885
+ // of this day of the week
1886
+ // (eg. ISO weeks use thursday (4))
1887
+ function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) {
1888
+ var end = firstDayOfWeekOfYear - firstDayOfWeek,
1889
+ daysToDayOfWeek = firstDayOfWeekOfYear - mom.day();
1890
+
1891
+
1892
+ if (daysToDayOfWeek > end) {
1893
+ daysToDayOfWeek -= 7;
1894
+ }
1895
+
1896
+ if (daysToDayOfWeek < end - 7) {
1897
+ daysToDayOfWeek += 7;
1898
+ }
1899
+
1900
+ return Math.ceil(moment(mom).add('d', daysToDayOfWeek).dayOfYear() / 7);
1901
+ }
1902
+
1903
+
1904
+ /************************************
1905
+ Top Level Functions
1906
+ ************************************/
1907
+
1908
+ function makeMoment(config) {
1909
+ var input = config._i,
1910
+ format = config._f;
1911
+
1912
+ if (input === null || input === '') {
1913
+ return null;
1914
+ }
1915
+
1916
+ if (typeof input === 'string') {
1917
+ config._i = input = getLangDefinition().preparse(input);
1918
+ }
1919
+
1920
+ if (moment.isMoment(input)) {
1921
+ config = extend({}, input);
1922
+ config._d = new Date(+input._d);
1923
+ } else if (format) {
1924
+ if (isArray(format)) {
1925
+ makeDateFromStringAndArray(config);
1926
+ } else {
1927
+ makeDateFromStringAndFormat(config);
1928
+ }
1929
+ } else {
1930
+ makeDateFromInput(config);
1931
+ }
1932
+
1933
+ return new Moment(config);
1934
+ }
1935
+
1936
+ moment = function (input, format, lang) {
1937
+ return makeMoment({
1938
+ _i : input,
1939
+ _f : format,
1940
+ _l : lang,
1941
+ _isUTC : false
1942
+ });
1943
+ };
1944
+
1945
+ // creating with utc
1946
+ moment.utc = function (input, format, lang) {
1947
+ return makeMoment({
1948
+ _useUTC : true,
1949
+ _isUTC : true,
1950
+ _l : lang,
1951
+ _i : input,
1952
+ _f : format
1953
+ });
1954
+ };
1955
+
1956
+ // creating with unix timestamp (in seconds)
1957
+ moment.unix = function (input) {
1958
+ return moment(input * 1000);
1959
+ };
1960
+
1961
+ // duration
1962
+ moment.duration = function (input, key) {
1963
+ var isDuration = moment.isDuration(input),
1964
+ isNumber = (typeof input === 'number'),
1965
+ duration = (isDuration ? input._data : (isNumber ? {} : input)),
1966
+ ret;
1967
+
1968
+ if (isNumber) {
1969
+ if (key) {
1970
+ duration[key] = input;
1971
+ } else {
1972
+ duration.milliseconds = input;
1973
+ }
1974
+ }
1975
+
1976
+ ret = new Duration(duration);
1977
+
1978
+ if (isDuration && input.hasOwnProperty('_lang')) {
1979
+ ret._lang = input._lang;
1980
+ }
1981
+
1982
+ return ret;
1983
+ };
1984
+
1985
+ // version number
1986
+ moment.version = VERSION;
1987
+
1988
+ // default format
1989
+ moment.defaultFormat = isoFormat;
1990
+
1991
+ // This function will load languages and then set the global language. If
1992
+ // no arguments are passed in, it will simply return the current global
1993
+ // language key.
1994
+ moment.lang = function (key, values) {
1995
+ var i;
1996
+
1997
+ if (!key) {
1998
+ return moment.fn._lang._abbr;
1999
+ }
2000
+ if (values) {
2001
+ loadLang(key, values);
2002
+ } else if (!languages[key]) {
2003
+ getLangDefinition(key);
2004
+ }
2005
+ moment.duration.fn._lang = moment.fn._lang = getLangDefinition(key);
2006
+ };
2007
+
2008
+ // returns language data
2009
+ moment.langData = function (key) {
2010
+ if (key && key._lang && key._lang._abbr) {
2011
+ key = key._lang._abbr;
2012
+ }
2013
+ return getLangDefinition(key);
2014
+ };
2015
+
2016
+ // compare moment object
2017
+ moment.isMoment = function (obj) {
2018
+ return obj instanceof Moment;
2019
+ };
2020
+
2021
+ // for typechecking Duration objects
2022
+ moment.isDuration = function (obj) {
2023
+ return obj instanceof Duration;
2024
+ };
2025
+
2026
+
2027
+ /************************************
2028
+ Moment Prototype
2029
+ ************************************/
2030
+
2031
+
2032
+ moment.fn = Moment.prototype = {
2033
+
2034
+ clone : function () {
2035
+ return moment(this);
2036
+ },
2037
+
2038
+ valueOf : function () {
2039
+ return +this._d;
2040
+ },
2041
+
2042
+ unix : function () {
2043
+ return Math.floor(+this._d / 1000);
2044
+ },
2045
+
2046
+ toString : function () {
2047
+ return this.format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ");
2048
+ },
2049
+
2050
+ toDate : function () {
2051
+ return this._d;
2052
+ },
2053
+
2054
+ toJSON : function () {
2055
+ return moment.utc(this).format('YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
2056
+ },
2057
+
2058
+ toArray : function () {
2059
+ var m = this;
2060
+ return [
2061
+ m.year(),
2062
+ m.month(),
2063
+ m.date(),
2064
+ m.hours(),
2065
+ m.minutes(),
2066
+ m.seconds(),
2067
+ m.milliseconds()
2068
+ ];
2069
+ },
2070
+
2071
+ isValid : function () {
2072
+ if (this._isValid == null) {
2073
+ if (this._a) {
2074
+ this._isValid = !compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray());
2075
+ } else {
2076
+ this._isValid = !isNaN(this._d.getTime());
2077
+ }
2078
+ }
2079
+ return !!this._isValid;
2080
+ },
2081
+
2082
+ utc : function () {
2083
+ this._isUTC = true;
2084
+ return this;
2085
+ },
2086
+
2087
+ local : function () {
2088
+ this._isUTC = false;
2089
+ return this;
2090
+ },
2091
+
2092
+ format : function (inputString) {
2093
+ var output = formatMoment(this, inputString || moment.defaultFormat);
2094
+ return this.lang().postformat(output);
2095
+ },
2096
+
2097
+ add : function (input, val) {
2098
+ var dur;
2099
+ // switch args to support add('s', 1) and add(1, 's')
2100
+ if (typeof input === 'string') {
2101
+ dur = moment.duration(+val, input);
2102
+ } else {
2103
+ dur = moment.duration(input, val);
2104
+ }
2105
+ addOrSubtractDurationFromMoment(this, dur, 1);
2106
+ return this;
2107
+ },
2108
+
2109
+ subtract : function (input, val) {
2110
+ var dur;
2111
+ // switch args to support subtract('s', 1) and subtract(1, 's')
2112
+ if (typeof input === 'string') {
2113
+ dur = moment.duration(+val, input);
2114
+ } else {
2115
+ dur = moment.duration(input, val);
2116
+ }
2117
+ addOrSubtractDurationFromMoment(this, dur, -1);
2118
+ return this;
2119
+ },
2120
+
2121
+ diff : function (input, units, asFloat) {
2122
+ var that = this._isUTC ? moment(input).utc() : moment(input).local(),
2123
+ zoneDiff = (this.zone() - that.zone()) * 6e4,
2124
+ diff, output;
2125
+
2126
+ if (units) {
2127
+ // standardize on singular form
2128
+ units = units.replace(/s$/, '');
2129
+ }
2130
+
2131
+ if (units === 'year' || units === 'month') {
2132
+ diff = (this.daysInMonth() + that.daysInMonth()) * 432e5; // 24 * 60 * 60 * 1000 / 2
2133
+ output = ((this.year() - that.year()) * 12) + (this.month() - that.month());
2134
+ output += ((this - moment(this).startOf('month')) - (that - moment(that).startOf('month'))) / diff;
2135
+ if (units === 'year') {
2136
+ output = output / 12;
2137
+ }
2138
+ } else {
2139
+ diff = (this - that) - zoneDiff;
2140
+ output = units === 'second' ? diff / 1e3 : // 1000
2141
+ units === 'minute' ? diff / 6e4 : // 1000 * 60
2142
+ units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60
2143
+ units === 'day' ? diff / 864e5 : // 1000 * 60 * 60 * 24
2144
+ units === 'week' ? diff / 6048e5 : // 1000 * 60 * 60 * 24 * 7
2145
+ diff;
2146
+ }
2147
+ return asFloat ? output : absRound(output);
2148
+ },
2149
+
2150
+ from : function (time, withoutSuffix) {
2151
+ return moment.duration(this.diff(time)).lang(this.lang()._abbr).humanize(!withoutSuffix);
2152
+ },
2153
+
2154
+ fromNow : function (withoutSuffix) {
2155
+ return this.from(moment(), withoutSuffix);
2156
+ },
2157
+
2158
+ calendar : function () {
2159
+ var diff = this.diff(moment().startOf('day'), 'days', true),
2160
+ format = diff < -6 ? 'sameElse' :
2161
+ diff < -1 ? 'lastWeek' :
2162
+ diff < 0 ? 'lastDay' :
2163
+ diff < 1 ? 'sameDay' :
2164
+ diff < 2 ? 'nextDay' :
2165
+ diff < 7 ? 'nextWeek' : 'sameElse';
2166
+ return this.format(this.lang().calendar(format, this));
2167
+ },
2168
+
2169
+ isLeapYear : function () {
2170
+ var year = this.year();
2171
+ return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
2172
+ },
2173
+
2174
+ isDST : function () {
2175
+ return (this.zone() < moment([this.year()]).zone() ||
2176
+ this.zone() < moment([this.year(), 5]).zone());
2177
+ },
2178
+
2179
+ day : function (input) {
2180
+ var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
2181
+ return input == null ? day :
2182
+ this.add({ d : input - day });
2183
+ },
2184
+
2185
+ startOf: function (units) {
2186
+ units = units.replace(/s$/, '');
2187
+ // the following switch intentionally omits break keywords
2188
+ // to utilize falling through the cases.
2189
+ switch (units) {
2190
+ case 'year':
2191
+ this.month(0);
2192
+ /* falls through */
2193
+ case 'month':
2194
+ this.date(1);
2195
+ /* falls through */
2196
+ case 'week':
2197
+ case 'day':
2198
+ this.hours(0);
2199
+ /* falls through */
2200
+ case 'hour':
2201
+ this.minutes(0);
2202
+ /* falls through */
2203
+ case 'minute':
2204
+ this.seconds(0);
2205
+ /* falls through */
2206
+ case 'second':
2207
+ this.milliseconds(0);
2208
+ /* falls through */
2209
+ }
2210
+
2211
+ // weeks are a special case
2212
+ if (units === 'week') {
2213
+ this.day(0);
2214
+ }
2215
+
2216
+ return this;
2217
+ },
2218
+
2219
+ endOf: function (units) {
2220
+ return this.startOf(units).add(units.replace(/s?$/, 's'), 1).subtract('ms', 1);
2221
+ },
2222
+
2223
+ isAfter: function (input, units) {
2224
+ units = typeof units !== 'undefined' ? units : 'millisecond';
2225
+ return +this.clone().startOf(units) > +moment(input).startOf(units);
2226
+ },
2227
+
2228
+ isBefore: function (input, units) {
2229
+ units = typeof units !== 'undefined' ? units : 'millisecond';
2230
+ return +this.clone().startOf(units) < +moment(input).startOf(units);
2231
+ },
2232
+
2233
+ isSame: function (input, units) {
2234
+ units = typeof units !== 'undefined' ? units : 'millisecond';
2235
+ return +this.clone().startOf(units) === +moment(input).startOf(units);
2236
+ },
2237
+
2238
+ zone : function () {
2239
+ return this._isUTC ? 0 : this._d.getTimezoneOffset();
2240
+ },
2241
+
2242
+ daysInMonth : function () {
2243
+ return moment.utc([this.year(), this.month() + 1, 0]).date();
2244
+ },
2245
+
2246
+ dayOfYear : function (input) {
2247
+ var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1;
2248
+ return input == null ? dayOfYear : this.add("d", (input - dayOfYear));
2249
+ },
2250
+
2251
+ isoWeek : function (input) {
2252
+ var week = weekOfYear(this, 1, 4);
2253
+ return input == null ? week : this.add("d", (input - week) * 7);
2254
+ },
2255
+
2256
+ week : function (input) {
2257
+ var week = this.lang().week(this);
2258
+ return input == null ? week : this.add("d", (input - week) * 7);
2259
+ },
2260
+
2261
+ // If passed a language key, it will set the language for this
2262
+ // instance. Otherwise, it will return the language configuration
2263
+ // variables for this instance.
2264
+ lang : function (key) {
2265
+ if (key === undefined) {
2266
+ return this._lang;
2267
+ } else {
2268
+ this._lang = getLangDefinition(key);
2269
+ return this;
2270
+ }
2271
+ }
2272
+ };
2273
+
2274
+ // helper for adding shortcuts
2275
+ function makeGetterAndSetter(name, key) {
2276
+ moment.fn[name] = moment.fn[name + 's'] = function (input) {
2277
+ var utc = this._isUTC ? 'UTC' : '';
2278
+ if (input != null) {
2279
+ this._d['set' + utc + key](input);
2280
+ return this;
2281
+ } else {
2282
+ return this._d['get' + utc + key]();
2283
+ }
2284
+ };
2285
+ }
2286
+
2287
+ // loop through and add shortcuts (Month, Date, Hours, Minutes, Seconds, Milliseconds)
2288
+ for (i = 0; i < proxyGettersAndSetters.length; i ++) {
2289
+ makeGetterAndSetter(proxyGettersAndSetters[i].toLowerCase().replace(/s$/, ''), proxyGettersAndSetters[i]);
2290
+ }
2291
+
2292
+ // add shortcut for year (uses different syntax than the getter/setter 'year' == 'FullYear')
2293
+ makeGetterAndSetter('year', 'FullYear');
2294
+
2295
+ // add plural methods
2296
+ moment.fn.days = moment.fn.day;
2297
+ moment.fn.weeks = moment.fn.week;
2298
+ moment.fn.isoWeeks = moment.fn.isoWeek;
2299
+
2300
+ /************************************
2301
+ Duration Prototype
2302
+ ************************************/
2303
+
2304
+
2305
+ moment.duration.fn = Duration.prototype = {
2306
+ weeks : function () {
2307
+ return absRound(this.days() / 7);
2308
+ },
2309
+
2310
+ valueOf : function () {
2311
+ return this._milliseconds +
2312
+ this._days * 864e5 +
2313
+ this._months * 2592e6;
2314
+ },
2315
+
2316
+ humanize : function (withSuffix) {
2317
+ var difference = +this,
2318
+ output = relativeTime(difference, !withSuffix, this.lang());
2319
+
2320
+ if (withSuffix) {
2321
+ output = this.lang().pastFuture(difference, output);
2322
+ }
2323
+
2324
+ return this.lang().postformat(output);
2325
+ },
2326
+
2327
+ lang : moment.fn.lang
2328
+ };
2329
+
2330
+ function makeDurationGetter(name) {
2331
+ moment.duration.fn[name] = function () {
2332
+ return this._data[name];
2333
+ };
2334
+ }
2335
+
2336
+ function makeDurationAsGetter(name, factor) {
2337
+ moment.duration.fn['as' + name] = function () {
2338
+ return +this / factor;
2339
+ };
2340
+ }
2341
+
2342
+ for (i in unitMillisecondFactors) {
2343
+ if (unitMillisecondFactors.hasOwnProperty(i)) {
2344
+ makeDurationAsGetter(i, unitMillisecondFactors[i]);
2345
+ makeDurationGetter(i.toLowerCase());
2346
+ }
2347
+ }
2348
+
2349
+ makeDurationAsGetter('Weeks', 6048e5);
2350
+
2351
+
2352
+ /************************************
2353
+ Default Lang
2354
+ ************************************/
2355
+
2356
+
2357
+ // Set default language, other languages will inherit from English.
2358
+ moment.lang('en', {
2359
+ ordinal : function (number) {
2360
+ var b = number % 10,
2361
+ output = (~~ (number % 100 / 10) === 1) ? 'th' :
2362
+ (b === 1) ? 'st' :
2363
+ (b === 2) ? 'nd' :
2364
+ (b === 3) ? 'rd' : 'th';
2365
+ return number + output;
2366
+ }
2367
+ });
2368
+
2369
+
2370
+ /************************************
2371
+ Exposing Moment
2372
+ ************************************/
2373
+
2374
+ this['moment'] = moment;
2375
+
2376
+ }).call(typeof Kalendae === 'undefined' ? window : Kalendae);
2377
+
2378
+ if (!Kalendae.moment) {
2379
+ if (window.moment) {
2380
+ Kalendae.moment = window.moment;
2381
+ } else {
2382
+ throw "Kalendae requires moment.js. You must use kalendae.standalone.js if moment is not available on the page.";
2383
+ }
2384
+ }
2385
+
2386
+ moment = Kalendae.moment;
2387
+
2388
+ //function to reset the date object to 00:00 GMT
2389
+ moment.fn.stripTime = function () {
2390
+ this._d = new Date(Math.floor(this._d.valueOf() / 86400000) * 86400000);
2391
+ return this;
2392
+ };
2393
+
2394
+
2395
+ //function to get the total number of days since the epoch.
2396
+ moment.fn.yearDay = function (input) {
2397
+ var yearday = Math.floor(this._d / 86400000);
2398
+ return (typeof input === 'undefined') ? yearday :
2399
+ this.add({ d : input - yearday });
2400
+ };
2401
+
2402
+ today = Kalendae.moment().stripTime();
2403
+
2404
+ if (typeof jQuery !== 'undefined' && (typeof document.addEventListener === 'function' || util.isIE8())) {
2405
+ jQuery.fn.kalendae = function (options) {
2406
+ this.each(function (i, e) {
2407
+ if (e.tagName === 'INPUT') {
2408
+ //if element is an input, bind a popup calendar to the input.
2409
+ $(e).data('kalendae', new Kalendae.Input(e, options));
2410
+ } else {
2411
+ //otherwise, insert a flat calendar into the element.
2412
+ $(e).data('kalendae', new Kalendae($.extend({}, {attachTo:e}, options)));
2413
+ }
2414
+ });
2415
+ return this;
2416
+ };
2417
+ }
2418
+
2419
+
2420
+ })();