Commits

Matthew Schinckel committed a65edd3

Added knockout-powered datepicker.

  • Participants
  • Parent commits 59a7023

Comments (0)

Files changed (3)

File jquery/static/jquery.jqui/css/_datepicker.sass

   background-color: white
   font-family: Helvetica
   font-size: 10px
-  width: 275px
   header, .header
     +rounded-border-top(8px)
     background: url(../images/bg-header.gif) repeat-x
     height: 21px
     position: relative
     font-size: 12px
-    h1
+    h1, span
       text-align: center
       font-weight: bold
       display: inline-block
       select
         position: relative
         margin-top: -6px
+        font: -webkit-small-control
     button
       position: absolute
       z-index: 100
     width: 100%
     display: block
     padding: 4px
+    margin: 0
     th
       padding: 4px
     td

File jquery/static/jquery.jqui/js/datepicker.js

+/*
+  
+  Knockout-savvy datepicker.
+  
+ */
+
+ko.bindingHandlers.datepicker = {
+  init: function(element, valueAccessor, allBindingAccessor, viewModel) {
+    var $element = $(element);
+    var value = valueAccessor(), allBindings = allBindingAccessor();
+    
+    var dp = DatePicker(value, element, $('#datepicker-container .datepicker').clone(), viewModel);
+    
+    // Setup the datepicker widget.
+    $element.focus(function() {
+      // Show the datepicker widget.
+      dp.show();
+    });
+    $element.blur(function() {
+      // Hide the datepicker.
+      //$datepicker.detach();
+    });
+
+  },
+  update: function(element, valueAccessor, allBindingAccessor, viewModel) {
+    var $element = $(element);
+    var value = valueAccessor(), allBindings = allBindingAccessor();
+  }
+};
+
+var DatePicker = function DatePicker(source, element, widget, viewModel) {
+  var MONTHS = [
+    "January", "February", "March", "April", "May", "June", "July",
+    "August", "September", "October", "November", "December"
+  ];
+  // By having the weekdays listed twice, we can start on any weekday, and do a slice
+  // to get all of the names. WEEKDAYS.slice(x, x+7)
+  var WEEKDAYS = [
+    "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
+    "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"
+  ];
+
+  var dp = {
+    strings: {
+      today: "Today",
+      done: "Done"
+    },
+    monthChoices: ko.observableArray(_.map(MONTHS, function(m, i){ return {name:m, value:i};})),
+    yearChoices: ko.observableArray(_.range(2008, 2015)),
+    visibleYear: ko.observable(),
+    visibleMonth: ko.observable(),
+    showWeekNumbers: ko.observable(false),
+    selectedDate: source,
+    today: new Date().strftime(Date.ISO_DATE)
+  };
+  
+  var selectedDate = source;
+  var weekStartDay = ko.observable(1);
+  var $element = $(element);
+  var $widget = $(widget);
+  
+  if (selectedDate) {
+    var date = new Date(Date.parse(selectedDate()));
+    dp.visibleYear(date.getFullYear());
+    dp.visibleMonth(date.getMonth());
+  }
+  
+  
+  
+  
+  /* Dependant Observables */  
+  dp.weekdays = ko.dependentObservable(function weekdays() {
+    return _(WEEKDAYS).chain().slice(weekStartDay(), weekStartDay()+7).map(function(d) {
+      return d.slice(0,2);
+    }).value();
+  });
+  
+  dp.visibleWeeks = ko.dependentObservable(function visibleWeeks() {
+    // Return the weeks of the currently visible month.
+    var visibleWeeks = [];
+    var month = dp.visibleMonth();
+    var year = dp.visibleYear();
+    
+    // Go to the first day of the visible month.
+    var startOfWeek = new Date(year, month, 1);
+    
+    // Go back to the first day of the week for this day.
+    startOfWeek.addDays(weekStartDay() - startOfWeek.getDay());
+    
+    // While we still have any days in this month in our list
+    _(_.range(5)).each(function(i){
+      var days = _.map(_.range(7), function(j) {
+        var day = new Date(startOfWeek).addDays(j + i*7 );
+        return {day: day.getDate(), iso: day.strftime(Date.ISO_DATE), date: day};
+      });
+      var weekNumber = days[0].date.getWeek();
+      visibleWeeks.push({days: days, weekNumber: weekNumber});
+      
+      return;
+      
+      if (_.any(days, function(day){ return day.getMonth() === month; })) {
+        visibleWeeks.push({days: days, weekNumber: weekNumber});
+      }
+    });
+    
+    return visibleWeeks;
+    
+  });
+  
+  /* Event Handlers */
+  
+  
+  dp.prevMonth = function prevMonth(evt) {
+    var month = Number(dp.visibleMonth());
+    var year = Number(dp.visibleYear());
+    if (month){
+      dp.visibleMonth(month - 1);
+    } else {
+      dp.visibleMonth(11);
+      dp.visibleYear(year-1);
+    }
+  };
+  
+  dp.nextMonth = function nextMonth(evt) {
+    var month = Number(dp.visibleMonth());
+    var year = Number(dp.visibleYear());
+    if (month < 11){
+      dp.visibleMonth(month + 1);
+    } else {
+      dp.visibleMonth(0);
+      dp.visibleYear(year+1);
+    }
+  
+  };
+  
+  dp.show = function show() {
+    $widget.insertAfter($element);
+  };
+  
+  dp.hide = function hide() {
+    $widget.detach();
+  };
+  
+  dp.closePicker = function closePicker(evt) {
+    var date = _.map(selectedDate().split('-'), Number);
+    dp.visibleYear(date[0]);
+    dp.visibleMonth(date[1]-1);
+    dp.hide();
+  };
+  
+  dp.selectToday = function selectToday(evt) {
+    selectedDate(new Date().strftime(Date.ISO_DATE));
+    dp.closePicker(evt);
+  };
+  
+  dp.selectDate = function selectDate(evt) {
+    selectedDate($(evt.target).attr('data-date'));
+    dp.closePicker(evt);
+  };
+  
+  ko.applyBindings(dp, $widget[0]);
+  
+  return dp;
+};

File jquery/templates/jqui/datepicker.html

+<div id="datepicker-container" style="display:none">
+<div class="jqui datepicker widget">
+  <header>
+    <button class="prev-month" data-bind="click: prevMonth"></button>
+    <h1>
+    <select data-bind="options: monthChoices, 
+                       optionsText: 'name',
+                       optionsValue: 'value',
+                       value: visibleMonth"></select>
+    <select data-bind="options: yearChoices, value: visibleYear"></select>
+    </h1>
+    <button class="next-month" data-bind="click: nextMonth"></button>
+  </header>
+  
+  <table>
+    <thead>
+      <tr>
+        <!-- ko if: showWeekNumbers -->
+          <th>#</th>
+        <!-- /ko -->
+        <!-- ko foreach: weekdays -->
+          <th data-bind="text: $data"></th>
+        <!-- /ko -->
+      </tr>
+    </thead>
+    <tbody data-bind="foreach: visibleWeeks">
+      <tr>
+        <!-- ko if: $root.showWeekNumbers -->
+          <td data-bind="text: weekNumber"></td>
+        <!-- /ko -->
+        <!-- ko foreach: days -->
+          <td data-bind="text: day, 
+                         click: $root.selectDate, 
+                         attr: {'data-date': iso},
+                         css: {
+                                'prev-month': date.getMonth() < $root.visibleMonth(),
+                                'next-month': date.getMonth() > $root.visibleMonth(),
+                                'selected': iso === $root.selectedDate(),
+                                'today': iso === $root.today
+                              }"></td>
+        <!-- /ko -->
+      </tr>
+    </tbody>
+  </table>
+  
+  <footer>
+    <button data-bind="click: selectToday, text: strings.today"></button>
+    <button data-bind="click: closePicker, text: strings.done"></button>
+  </footer>
+</div>
+</div>