「カレンダー」のカスタムバインディング:ko

  1. <!DOCTYPE html>
  2. <html lang="ja">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>「カレンダー」のカスタムバインディング:ko</title>
  6. <script type="text/javascript" src="jquery-1.5.1.min.js"></script>
  7. <script type="text/javascript" src="knockout-2.1.0beta.js"></script>
  8. <script type="text/javascript">
  9.   // (週単位で)日にち
  10.   function makeTbodyOfCalendar(DayArray, today, nameClass)
  11.   {
  12.     calendar = "";
  13.     for (i=0; i<DayArray.length; i++)
  14.     {
  15.       if (i == 0)
  16.       {
  17.         calendar += '<tr>';
  18.       }
  19.       else if (i%7 == 0)
  20.       {
  21.         calendar += '</tr>' + "\n" + '<tr>';
  22.       }
  23.       
  24.       // 「今日」の場合はclassを追加。
  25.       todayClass = "";
  26.       if (today != "" && today == DayArray[i])
  27.       {
  28.         todayClass = ' class="' + nameClass + '"';
  29.       }
  30.       calendar += '<td' + todayClass + '>' + DayArray[i] + '</td>';
  31.     }
  32.     calendar += '</tr>';
  33.     return calendar;
  34.   }
  35.   // 年月のチェック
  36.   function checkDate(date)
  37.   {
  38.     need = date.split("/");
  39.     if (need.length == 2)
  40.     {
  41.       if (1900<parseFloat(need[0]) && 1<=parseFloat(need[1]) && parseFloat(need[1])<=12)
  42.       {
  43.         return true;
  44.       }
  45.     }
  46.     return false;
  47.   }
  48.   
  49.   /**
  50.    * 年月日に指定された数値を足して返す
  51.    * ymdDate:日付データ
  52.    * 追加する項目:年月日のどれか(ymd)
  53.    * 追加する数値
  54.    */
  55.   function addDate(ymdDate, target, add)
  56.   {
  57.     addNum = parseFloat(add);
  58.     
  59.     yea = ymdDate.getFullYear();
  60.     mon = ymdDate.getMonth();
  61.     day = ymdDate.getDate();
  62.     
  63.     if(target == "y")
  64.     {
  65.       yea += addNum;
  66.     }
  67.     else if(target == "m")
  68.     {
  69.       mon += addNum;
  70.     }
  71.     else if(target == "d")
  72.     {
  73.       day += addNum;
  74.     }
  75.     return new Date(yea, mon, day);
  76.   }
  77.   /**
  78.    * カレンダー用に日曜から1日、月末から土曜までを何かで埋め、配列にして返す
  79.    * year    表示するカレンダーの年
  80.    * month  表示するカレンダーの月
  81.    * Fill    日曜から1日まで、月末から土曜まで埋める文字
  82.    */
  83.   function convertDayOfCalendar(year, month, Fill)
  84.   {
  85.     // 月初と月末
  86.     var startMonth = new Date(year + "/" + month + "/1");
  87.     var endMonth   = addDate(new Date(year, month, 1), "d", -1);
  88.     
  89.     // カレンダー用に日にちを配列化:
  90.     DayArray = new Array();
  91.     
  92.     // 日曜~1日の空白を埋める。
  93.     for (i=0; i<startMonth.getDay(); i++)
  94.     {
  95.       DayArray.push(Fill);
  96.     }
  97.     
  98.     // 月初め(1日)~月末までを埋める
  99.     for (i=0; i<endMonth.getDate(); i++)
  100.     {
  101.       DayArray.push(i+1);
  102.     }
  103.     
  104.     // 月末~土曜の空白を埋める
  105.     if (endMonth.getDay() != 6)
  106.     {
  107.       startWeek = parseFloat(endMonth.getDay()) + 1;
  108.       for (i=startWeek; i<7; i++)
  109.       {
  110.         DayArray.push(Fill);
  111.       }
  112.     }
  113.     return DayArray;
  114.   }
  115.   // カレンダーバインディング
  116.   // サブプロパティ:
  117.   // koCalendarFill  (指定なし:"")       // 空白箇所を埋める文字
  118.   // koCalendarToday (指定なし:"today")  // 今日のclass名
  119.   ko.bindingHandlers.koCalendar = {
  120.     update: function(element, valueAccessor, allBindingsAccessor)
  121.     {
  122.       // 現状の値と、サブプロパティ一覧の取得
  123.       var value = valueAccessor(), allBindings = allBindingsAccessor();
  124.       var valueUnwrapped = ko.utils.unwrapObservable(value);
  125.       
  126.       // 各サブプロパティの取得
  127.       var koCalendarFill   = allBindings.koCalendarFill   || "";        // 空白箇所を埋める文字
  128.       var koCalendarToday  = allBindings.koCalendarToday  || "today";   // 今日のclass名
  129.       
  130.       var calendar = "";
  131.       
  132.       // 今日
  133.       var now = new Date();
  134.       var nowY = now.getFullYear();  // 現在の年
  135.       var nowM = now.getMonth() + 1; // 現在の月
  136.       var nowD = now.getDate();      // 現在の日
  137.       
  138.       // 年月が正常かチェック
  139.       if (checkDate(valueUnwrapped) == true)
  140.       {
  141.         need = valueUnwrapped.split("/");
  142.         
  143.         y = parseFloat(need[0]);
  144.         m = parseFloat(need[1]);
  145.       }
  146.       else
  147.       {
  148.         // 年月が異常なら今月にする
  149.         y = nowY;
  150.         m = nowD;
  151.       }
  152.       
  153.       // 今月かチェック
  154.       today = "";
  155.       if (("" + nowY + nowM) == ("" + y + m))
  156.       {
  157.         today = nowD;
  158.       }
  159.       
  160.       // カレンダー用に日を配列化(日曜から1日までを何かで埋める)
  161.       DayArray = convertDayOfCalendar(y, m, koCalendarFill);
  162.       
  163.       // カレンダー用にタグを生成
  164.       var htmltagWeek = makeTbodyOfCalendar(DayArray, today, koCalendarToday);
  165.       
  166.       calendar = htmltagWeek;
  167.       
  168.       // 処理
  169.       if (calendar != "")
  170.       {
  171.         $(element).html(calendar);
  172.       }
  173.       else
  174.       {
  175.         $(element).html("");
  176.       }
  177.     }
  178.   };
  179.   $(document).ready(function () {
  180.     ko.applyBindings(new ViewModel());
  181.   });
  182.   function ViewModel() {
  183.     var self = this;
  184.     self.valYearMonth = ko.observable("2012/06");
  185.     
  186.     // 前月
  187.     self.prefCal = function()
  188.     {
  189.       pref = self.valYearMonth().split("/");
  190.       if (parseFloat(pref[1]) == 1)
  191.       {
  192.         prefY = parseFloat(pref[0]) - 1
  193.         prefM = "12";
  194.       }
  195.       else
  196.       {
  197.         prefY = parseFloat(pref[0]);
  198.         prefM = parseFloat(pref[1]) - 1;
  199.       }
  200.       if (prefM<10)
  201.       {
  202.         prefM = "0" + prefM;
  203.       }
  204.       self.valYearMonth(prefY + "/" + prefM);
  205.     };
  206.     
  207.     // 翌月
  208.     self.nextCal = function()
  209.     {
  210.       next = self.valYearMonth().split("/");
  211.       if (parseFloat(next[1]) == 12)
  212.       {
  213.         nextY = "" + (parseFloat(next[0]) + 1);
  214.         nextM = "1";
  215.       }
  216.       else
  217.       {
  218.         nextY = parseFloat(next[0]);
  219.         nextM = parseFloat(next[1]) + 1;
  220.       }
  221.       if (nextM<10)
  222.       {
  223.         nextM = "0" + nextM;
  224.       }
  225.       self.valYearMonth(nextY + "/" + nextM);
  226.     };
  227.     
  228.     // 今月
  229.     self.nowCal = function()
  230.     {
  231.       var now = new Date();
  232.       var nowY = now.getFullYear();  // 現在の年
  233.       var nowM = now.getMonth() + 1; // 現在の月
  234.       if (nowM<10)
  235.       {
  236.         nowM = "0" + nowM;
  237.       }
  238.       self.valYearMonth(nowY + "/" + nowM);
  239.     };
  240.     
  241.   }
  242. </script>
  243. <style type="text/css">
  244. th, td
  245. {
  246.   border:1px solid #ccc;
  247.   padding:3px;
  248.   text-align:center;
  249. }
  250. .koCalendarToday
  251. {
  252.   font-weight:bold;
  253. }
  254. .moveCal
  255. {
  256.   color:#0000ff;
  257.   cursor:pointer;
  258. }
  259. .moveCal:hover, .nowCal:hover
  260. {
  261.   color:#ff0000;
  262.   cursor:pointer;
  263. }
  264. .nowCal
  265. {
  266.   position:absolute;
  267.   bottom:0;
  268.   left:3px;
  269.   color:#0000ff;
  270.   cursor:pointer;
  271.   font-size:10pt;
  272. }
  273. </style>
  274. </head>
  275. <body>
  276. <h1>「カレンダー」のカスタムバインディング:ko</h1>
  277. <table>
  278. <caption>
  279. <div style="position:relative;top:0;left:0;">
  280. <span class="moveCal" data-bind="click: prefCal">&laquo;</span>
  281. <span data-bind="text: valYearMonth"></span>
  282. <span class="moveCal" data-bind="click: nextCal">&raquo;</span>
  283. <span class="nowCal" data-bind="click: nowCal">[今月]</span>
  284. </div>
  285. </caption>
  286. <thead>
  287. <th>日</th>
  288. <th>月</th>
  289. <th>火</th>
  290. <th>水</th>
  291. <th>木</th>
  292. <th>金</th>
  293. <th>土</th>
  294. </thead>
  295. <tbody data-bind="koCalendar: valYearMonth, koCalendarToday: 'koCalendarToday'"></tbody>
  296. </table>
  297. <div style="font-size:10pt;text-align:right;margin-top:0.5em;">
  298. <a href="//tips.recatnap.info/" target="_top">PCスキルの小技・忘却防止メモ</a> -
  299. <a href="//tips.recatnap.info/wiki/" target="_top">PCスキルの小技・忘却防止メモのまとめ(wiki)</a>
  300. </div>
  301. <div style="font-size:10pt;text-align:center;margin-top:0.5em;padding:0.5em;border-top:1px solid #ccc;">
  302. Copyright &copy; 2009 by PCスキルの小技・忘却防止メモ. All rights reserved.
  303. </div>
  304. </body>
  305. </html>