├── .gitignore ├── default.css ├── .gitattributes ├── changes.txt ├── screenshot.png ├── release ├── js_calendar_1_0.tar.gz └── js_calendar_1_1.tar.gz ├── Makefile ├── calendar_db_null.js ├── calendar_null.html ├── calendar_cookie.html ├── calendar_cgi_pstore.html ├── cgi_pstore ├── list.rb ├── utils.rb ├── list_icalendar.rb └── modify.rb ├── calendar.html ├── calendar_db_cgi.js ├── http_loader.js ├── calendar.css ├── holiday.js ├── index.html ├── calendar_db_cookie.js └── calendar.js /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | -------------------------------------------------------------------------------- /default.css: -------------------------------------------------------------------------------- 1 | 2 | h1 { 3 | font-size: 10pt; 4 | } 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js encoding=utf-8 2 | *.html encoding=utf-8 3 | -------------------------------------------------------------------------------- /changes.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misohena/js_calendar/master/changes.txt -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misohena/js_calendar/master/screenshot.png -------------------------------------------------------------------------------- /release/js_calendar_1_0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misohena/js_calendar/master/release/js_calendar_1_0.tar.gz -------------------------------------------------------------------------------- /release/js_calendar_1_1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misohena/js_calendar/master/release/js_calendar_1_1.tar.gz -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | RELEASE_NAME = js_calendar_1_1 3 | 4 | .PHONY: release 5 | release: 6 | mkdir -p $(RELEASE_NAME) 7 | cp *.css *.js *.html *.txt *.png -R cgi_pstore $(RELEASE_NAME) 8 | tar cvzf release/$(RELEASE_NAME).tar.gz $(RELEASE_NAME) 9 | rm -R $(RELEASE_NAME) 10 | -------------------------------------------------------------------------------- /calendar_db_null.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // 3 | // example of async interface for calendar DB. 4 | // 5 | // Copyright(c) 2009 AKIYAMA Kouhei. 6 | // 7 | function CalendarData() 8 | { 9 | } 10 | CalendarData.prototype = { 11 | readEventItems: function(firstDate, lastDate, callback) 12 | { 13 | if(callback){ 14 | callback([ 15 | // {date:new Date(2009,11-1,15), value:"予定1"}, 16 | // {date:new Date(2009,11-1,25), value:"11:00 予定2"}, 17 | ]); 18 | } 19 | }, 20 | 21 | changeEventItem: function(date, oldValue, newValue, callback) 22 | { 23 | if(callback){ 24 | callback( 25 | true, // succeeded? 26 | newValue // current value 27 | ); 28 | } 29 | }, 30 | }; 31 | 32 | -------------------------------------------------------------------------------- /calendar_null.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |
5 | 6 | 7 | 8 |
| 分類 | ファイル名 | 概要 | 依存するファイル |
|---|---|---|---|
| 使用例 | calendar_null.html | カレンダーのHTML。データを保存しないバージョン。UIのテスト用。 | default.css, calendar.css, holiday.js, calendar.js, calendar_db_null.js |
| calendar_cgi_pstore.html | カレンダーのHTML。CGIを通してデータを保存するバージョン。サーバー上でのデータの保存はRubyのpstoreを使う。 | default.css, calendar.css, holiday.js, calendar.js, calendar_db_cgi.js, http_loader.js | |
| calendar_cookie.html | カレンダーのHTML。クッキーにデータを保存するバージョン。サーバーは不要。データの共有は出来ない。容量制限あり。 | default.css, calendar.css, holiday.js, calendar.js, calendar_db_cookie.js | |
| default.css | *.htmlから共通で使用するスタイルシート。 | ||
| UI | calendar.js | カレンダーのUIを実現するためのJavaScript。テーブルの生成、セルの入力処理、イベント項目の表示・入力処理を行う。実際のデータの読み書きはcalendar_db_*.jsのいずれかに依頼する。 | holiday.js, calendar_db_*.js |
| calendar.css | calendar.jsが生成するカレンダーテーブルの見た目を決めるスタイルシート。 | ||
| holiday.js | 休日情報を提供するJavaScript。 | ||
| データインタフェース | calendar_db_null.js | データを保存しないダミーのカレンダーデータインタフェース。 | |
| calendar_db_cgi.js | CGIを通してサーバーにデータを保存するカレンダーデータインタフェース。 | http_loader.js, cgi_*/list.rb, cgi_*/modify.rb | |
| calendar_db_cookie.js | クッキーにデータを保存するカレンダーデータインタフェース。calendar.jsが使う。 | ||
| http_loader.js | サーバーとの非同期通信をサポートするJavaScript。calendar_db_cgi.jsが使う。 | ||
| CGI(pstore) | cgi_pstore/list.rb | 読み込み用CGI。calendar_db_cgi.rbから呼び出す。 | cgi_pstore/utils.rb |
| cgi_pstore/modify.rb | 書き込み用CGI。calendar_db_cgi.rbから呼び出す。 | cgi_pstore/utils.rb | |
| cgi_pstore/utils.rb | list.rbとmodify.rbから使う共通コード。 |
月曜始まりにするには、calendar.js内の「firstDayOfWeek: 0」を「firstDayOfWeek: 1」へ変更してください。
63 |休みの曜日を変更するには、calendar.js内の「holidayDaysOfWeek:」の後を変更してください。
64 |見た目(色合いや寸法)を変更するには、calendar.cssを変更してください。
65 | 66 | 67 |本ソフトウェアはMITライセンスに基づき自由に使用することが出来ます。
69 | 70 |Copyright (c) 2009-2010 AKIYAMA Kouhei
71 | 72 |Permission is hereby granted, free of charge, to any person obtaining a copy 73 | of this software and associated documentation files (the "Software"), to deal 74 | in the Software without restriction, including without limitation the rights 75 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 76 | copies of the Software, and to permit persons to whom the Software is 77 | furnished to do so, subject to the following conditions:
78 | 79 |The above copyright notice and this permission notice shall be included in 80 | all copies or substantial portions of the Software.
81 | 82 |THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 83 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 84 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 85 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 86 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 87 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 88 | THE SOFTWARE.
89 | 90 | 91 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /calendar_db_cookie.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // 3 | // クッキーへデータを保存するカレンダーデータベースインタフェースです。 4 | // 5 | // ブラウザがデータを記憶するため、サーバーを用意する必要がありません。 6 | // ただし保存できる容量は3800バイトまでです。その容量を越えた時点でそれ以上書き込めなくなります。 7 | // 7日以上過ぎたイベントは自動的に消去するので、沢山の予定を書き込まなければある程度使い続けることが出来ます。 8 | // ただしクッキーは何かの拍子に誤って消してしまうことがあるので、あまり信用しない方が良いかもしれません。 9 | // 10 | // Copyright(c) 2009 AKIYAMA Kouhei. 11 | // 12 | function CalendarData() 13 | { 14 | } 15 | CalendarData.prototype = { 16 | readEventItems: function(firstDate, lastDate, callback) 17 | { 18 | if(!callback){return;} 19 | 20 | // read all events from the cookie. 21 | var events = this.getEvents(); 22 | 23 | // filter events [firstDate, lastdate) 24 | var firstNum = this.convDateToNum(firstDate); 25 | var lastNum = this.convDateToNum(lastDate); 26 | var eventsInRange = new Array(); 27 | for(var i = 0; i < events.length; ++i){ 28 | var d = this.convDateToNum(events[i].date); 29 | if(d >= firstNum && d < lastNum){ 30 | eventsInRange.push(events[i]); 31 | } 32 | } 33 | 34 | // notify the result. 35 | callback(eventsInRange); 36 | }, 37 | 38 | changeEventItem: function(date, oldValue, newValue, callback) 39 | { 40 | // read all events from the cookie. 41 | var events = this.getEvents(); 42 | 43 | // find the index of the target event(date, oldValue). 44 | var target; 45 | var dateNum = this.convDateToNum(date); 46 | for(target = 0; target < events.length; ++target){ 47 | if(this.convDateToNum(events[target].date) == dateNum && events[target].value == oldValue){ 48 | break; 49 | } 50 | } 51 | 52 | var succeeded = false; 53 | if(! oldValue && newValue){ 54 | // add new event. 55 | events.push({date: new Date(date), value: newValue}); 56 | succeeded = true; 57 | } 58 | else if(oldValue && ! newValue){ 59 | // delete event. 60 | if(target < events.length){ 61 | events.splice(target, 1); 62 | succeeded = true; 63 | } 64 | else{ 65 | //error 66 | } 67 | } 68 | else if(oldValue && newValue){ 69 | // change event value. 70 | if(target < events.length){ 71 | events[target].value = newValue; 72 | succeeded = true; 73 | } 74 | else{ 75 | //error 76 | } 77 | } 78 | 79 | // write all events to the cookie. 80 | if(succeeded){ 81 | succeeded = this.setEvents(events); 82 | } 83 | 84 | // notify the result. 85 | if(callback){ 86 | callback(succeeded, succeeded ? newValue : ""); 87 | } 88 | }, 89 | 90 | convDateToNum: function(date) 91 | { 92 | return date.getFullYear()*10000 + (date.getMonth()+1)*100 + date.getDate(); 93 | }, 94 | 95 | convNumToDate: function(num) 96 | { 97 | return new Date(Math.floor(num / 10000), Math.floor(num/100)%100-1, num%100); 98 | }, 99 | 100 | cookieName: "events", 101 | cookieLengthMax: 3800, 102 | 103 | getEvents: function() 104 | { 105 | var cookies = document.cookie; 106 | //alert(cookies); 107 | var pos = cookies.indexOf(this.cookieName + "="); 108 | if(pos == -1){ 109 | return []; 110 | } 111 | 112 | var first = pos + this.cookieName.length + 1; 113 | var last = cookies.indexOf(";", first); 114 | if(last == -1){ 115 | last = cookies.length; 116 | } 117 | // ex: "events=20091107:birthday&20091107:meeting&20091120:holiday" 118 | var cookieValue = cookies.substring(first, last); 119 | var events = new Array(); 120 | var lines = cookieValue.split('&'); 121 | for(var i = 0; i < lines.length; ++i){ 122 | var datevalue = lines[i].split(':'); 123 | events.push({ 124 | date: this.convNumToDate(parseInt(datevalue[0])), 125 | value: unescape(datevalue[1]) 126 | }); 127 | } 128 | 129 | ///@todo sort 130 | 131 | return events; 132 | }, 133 | 134 | setEvents: function(events) 135 | { 136 | // create a cookie value. and remove old events. 137 | var dateNumLower = this.convDateToNum(new Date((new Date()).getTime() - 7*24*60*60*1000)); 138 | var cookieValue = ""; 139 | for(var i = 0; i < events.length; ++i){ 140 | var dateNum = this.convDateToNum(events[i].date); 141 | if(dateNum < dateNumLower){ 142 | continue; 143 | } 144 | 145 | if(cookieValue != ""){ 146 | cookieValue += "&"; 147 | } 148 | cookieValue += dateNum + ":" + escape(events[i].value) 149 | } 150 | 151 | // validate length of cookie value. 152 | if(cookieValue.length >= this.cookieLengthMax){ 153 | return false; // error 154 | } 155 | 156 | // expiration date of the cookie value. 157 | var expires = new Date(); 158 | expires.setFullYear(expires.getFullYear() + 1); 159 | 160 | // write a cookie. 161 | var cookie = this.cookieName + "=" + cookieValue 162 | + "; expires=" + expires.toGMTString(); 163 | //alert(cookie); 164 | document.cookie = cookie; 165 | 166 | return true; 167 | } 168 | }; 169 | 170 | -------------------------------------------------------------------------------- /calendar.js: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // title SimpleCalendar 3 | // since 2009-11-03 4 | // author AKIYAMA Kouhei 5 | 6 | // require holiday.js (function ktHolidayName) 7 | // require calendar_db_*.js (function/class CalendarData) 8 | 9 | 10 | 11 | var CalendarApp = { 12 | // Settings. 13 | 14 | cssPrefix: "calendar", 15 | weekDayNames: ["日", "月", "火", "水", "木", "金", "土"], 16 | holidayDaysOfWeek: (1<<0) | (1<<6), 17 | firstDayOfWeek: 0, 18 | displayWeeks: 10, 19 | 20 | // Date/Time Utilities 21 | 22 | getHolidayName: function(date) 23 | { 24 | return JapaneseHoliday.getHolidayName(date); 25 | }, 26 | 27 | isHoliday: function(date) 28 | { 29 | return ((CalendarApp.holidayDaysOfWeek >> date.getDay()) & 1) 30 | || !!CalendarApp.getHolidayName(date); 31 | }, 32 | 33 | isPastDate: function(date, now) 34 | { 35 | if(!now){ 36 | now = new Date(); 37 | } 38 | 39 | if(date.getFullYear() < now.getFullYear()) 40 | return true; 41 | if(date.getFullYear() > now.getFullYear()){ 42 | return false; 43 | } 44 | if(date.getMonth() < now.getMonth()){ 45 | return true; 46 | } 47 | if(date.getMonth() > now.getMonth()){ 48 | return false; 49 | } 50 | if(date.getDate() < now.getDate()){ 51 | return true; 52 | } 53 | //if(date.getDate() > now.getDate()){ 54 | // return false; 55 | //} 56 | return false; 57 | }, 58 | 59 | addDate: function(date, delta) 60 | { 61 | date.setTime(date.getTime() + delta * (24*60*60*1000)); 62 | }, 63 | 64 | convertDateToYYYYMMDD: function(date) 65 | { 66 | return date.getFullYear() * 10000 67 | + (date.getMonth()+1) * 100 68 | + date.getDate(); 69 | }, 70 | 71 | // DOM Utilities 72 | 73 | getLastScriptNode: function() 74 | { 75 | var n = document; 76 | while(n && n.nodeName.toLowerCase() != "script") { n = n.lastChild;} 77 | return n; 78 | }, 79 | /* 80 | createDomNode: function(obj) 81 | { 82 | if(!obj){ 83 | return null; 84 | } 85 | 86 | if(obj.elem){ 87 | var e = document.createElement(obj.elem); 88 | 89 | if(obj.children){ 90 | for(var i = 0; i < obj.children.length; ++i){ 91 | var child = CalendarApp.createDomNode(obj.children[i]); 92 | if(child){ 93 | e.appendChild(child); 94 | } 95 | } 96 | } 97 | 98 | if(obj.atrs){ 99 | for(var key in obj.atrs){ 100 | e[key] = obj.atrs[key]; 101 | } 102 | } 103 | return e; 104 | } 105 | else if(obj.textnode){ 106 | var t = document.createTextNode(obj.textnode); 107 | return t; 108 | } 109 | else{ 110 | return null; 111 | } 112 | }, 113 | */ 114 | 115 | 116 | addEventListener: function(elem, evname, func) 117 | { 118 | if(!elem){return;} 119 | if(elem.addEventListener){ //for DOM 120 | elem.addEventListener(evname, func, false); 121 | } 122 | else if(elem.attachEvent){ //for IE 123 | switch(evname){ 124 | case "click": evname = "onclick"; break; 125 | case "blur": evname = "onblur"; break; 126 | default: evname = ""; break; 127 | } 128 | if(evname != ""){ 129 | elem.attachEvent(evname, func); 130 | } 131 | } 132 | }, 133 | 134 | 135 | // Create Elements. 136 | 137 | createWeekRow: function(firstDate, func, rowClassName) 138 | { 139 | var date = new Date(firstDate); 140 | 141 | var row = document.createElement("tr"); 142 | row.className = rowClassName; 143 | for(var d = 0; d < 7; ++d){ 144 | row.appendChild(func(date)); 145 | CalendarApp.addDate(date, 1); 146 | } 147 | return row; 148 | }, 149 | 150 | createWeekDayNamesRow: function(firstDate) 151 | { 152 | return CalendarApp.createWeekRow( 153 | firstDate, 154 | function(date){ 155 | var th = document.createElement("th"); 156 | th.appendChild(document.createTextNode(CalendarApp.weekDayNames[date.getDay()])); 157 | return th; 158 | }, 159 | CalendarApp.cssPrefix + "-weekdaynames-row"); 160 | }, 161 | 162 | getDateClassName: function(date, now, suffix) 163 | { 164 | var classes = []; 165 | 166 | if(new Date(date.getTime() + 7*24*60*60*1000).getMonth() != date.getMonth()){ 167 | classes.push(CalendarApp.cssPrefix + "-last-dayweek-of-month" + suffix); 168 | } 169 | if(new Date(date.getTime() + 1*24*60*60*1000).getMonth() != date.getMonth()){ 170 | classes.push(CalendarApp.cssPrefix + "-last-date-of-month" + suffix); 171 | } 172 | classes.push(CalendarApp.cssPrefix + ( 173 | CalendarApp.isPastDate(date, now) ? "-pastday" : 174 | CalendarApp.isHoliday(date) ? "-holiday" : 175 | "-normalday") + suffix); 176 | return classes.join(" "); 177 | }, 178 | 179 | createDateHeaderCell: function(date, now) 180 | { 181 | var cell = document.createElement("td"); 182 | cell.className = CalendarApp.getDateClassName(date, now, "-header"); 183 | 184 | if(date.getDate() == 1){ 185 | var monthName = document.createElement("span"); 186 | monthName.className = CalendarApp.cssPrefix + "-month-name"; 187 | monthName.appendChild(document.createTextNode("(" + (1 + date.getMonth()) + ")")); 188 | cell.appendChild(monthName); 189 | cell.appendChild(document.createTextNode("/" + date.getDate())); 190 | } 191 | else{ 192 | cell.appendChild(document.createTextNode(date.getDate())); 193 | } 194 | 195 | var holidayName = CalendarApp.getHolidayName(date); 196 | if(holidayName){ 197 | cell.appendChild(document.createTextNode(":" + holidayName)); 198 | } 199 | return cell; 200 | }, 201 | 202 | createDateContentCell: function(date, now, db, cellsDic) 203 | { 204 | var cell = document.createElement("td"); 205 | cell.className = CalendarApp.getDateClassName(date, now, "-content"); 206 | 207 | var ctrl = new CalendarApp.CalendarCellCtrl(cell, date, db); 208 | if(cellsDic){ 209 | cellsDic[CalendarApp.convertDateToYYYYMMDD(date)] = ctrl; 210 | } 211 | 212 | return cell; 213 | }, 214 | 215 | createDateHeaderRow: function(firstDate, now) 216 | { 217 | return CalendarApp.createWeekRow( 218 | firstDate, 219 | function(date){return CalendarApp.createDateHeaderCell(date, now);}, 220 | CalendarApp.cssPrefix + "-date-header-row"); 221 | }, 222 | 223 | createDateContentRow: function(firstDate, now, db, cellsArray) 224 | { 225 | return CalendarApp.createWeekRow( 226 | firstDate, 227 | function(date){return CalendarApp.createDateContentCell(date, now, db, cellsArray);}, 228 | CalendarApp.cssPrefix + "-date-content-row"); 229 | }, 230 | 231 | 232 | readEventItemsToCells: function(db, firstDate, lastDate, cellsDic) 233 | { 234 | db.readEventItems(firstDate, lastDate, function(items){ 235 | if(!items){ 236 | alert("ERROR:failed to read calendar events."); 237 | return; 238 | } 239 | for(var i = 0; i < items.length; ++i){ 240 | var cell = cellsDic[CalendarApp.convertDateToYYYYMMDD(items[i].date)]; 241 | if(cell){ 242 | cell.addEventItem(items[i].value, false); 243 | } 244 | } 245 | }); 246 | }, 247 | 248 | appendWeeks: function(firstDate, weekCount, db, table, cellsDic, now) 249 | { 250 | var date = new Date(firstDate); 251 | for(var week = 0; week < weekCount; ++week){ 252 | table.appendChild(CalendarApp.createDateHeaderRow(date, now)); 253 | table.appendChild(CalendarApp.createDateContentRow(date, now, db, cellsDic)); 254 | CalendarApp.addDate(date, 7); 255 | } 256 | 257 | CalendarApp.readEventItemsToCells(db, firstDate, date, cellsDic); 258 | 259 | return date; 260 | }, 261 | 262 | createCalendarCtrl: function() 263 | { 264 | var db = new CalendarData(); 265 | 266 | var now = new Date(); 267 | var nowPosOnWeek = (7 + now.getDay() - CalendarApp.firstDayOfWeek) % 7; 268 | 269 | var firstDate = new Date( 270 | now.getFullYear(), 271 | now.getMonth(), 272 | now.getDate() - (nowPosOnWeek + 7)); ///@todo ok? 273 | 274 | 275 | var table = document.createElement("table"); 276 | table.className = CalendarApp.cssPrefix + "-table"; 277 | 278 | table.appendChild(CalendarApp.createWeekDayNamesRow(firstDate)); 279 | 280 | var cellsDic = new Object; 281 | var lastDate = CalendarApp.appendWeeks(firstDate, CalendarApp.displayWeeks, db, table, cellsDic, now); 282 | 283 | return new CalendarApp.CalendarCtrl(db, table, cellsDic, firstDate, lastDate, now); 284 | }, 285 | 286 | placeCalendar: function() 287 | { 288 | var parent = CalendarApp.getLastScriptNode().parentNode; 289 | parent.appendChild(CalendarApp.createCalendarCtrl().div); 290 | } 291 | 292 | }; 293 | 294 | 295 | 296 | // 297 | // calendar control. 298 | // 299 | CalendarApp.CalendarCtrl = function(db, table, cellsDic, firstDate, lastDate, now) 300 | { 301 | this.db = db; 302 | this.table = table; 303 | this.cellsDic = cellsDic; 304 | this.firstDate = firstDate; 305 | this.lastDate = lastDate; 306 | this.now = now; 307 | 308 | this.div = document.createElement("div"); 309 | this.div.appendChild(table); 310 | 311 | this.div.appendChild(this.createButtonMore()); 312 | }; 313 | CalendarApp.CalendarCtrl.prototype = { 314 | createButtonMore: function() 315 | { 316 | var self = this; 317 | var more = document.createElement("input"); 318 | more.setAttribute("type", "button"); 319 | more.setAttribute("value", "more.."); 320 | CalendarApp.addEventListener(more, "click", function(){self.onClickMore();}); 321 | return more; 322 | }, 323 | 324 | onClickMore: function() 325 | { 326 | this.lastDate = CalendarApp.appendWeeks( 327 | this.lastDate, 4, this.db, this.table, this.cellsDic, this.now); 328 | } 329 | }; 330 | 331 | 332 | 333 | // 334 | // calendar cell control. 335 | // 336 | CalendarApp.CalendarCellCtrl = function(cell, date, db) 337 | { 338 | var self = this; 339 | this.date = new Date(date); 340 | this.db = db; 341 | this.cell = cell; 342 | CalendarApp.addEventListener(this.cell, "click", function(){ self.onCellClick();}); 343 | this.items = new Array(); 344 | }; 345 | CalendarApp.CalendarCellCtrl.prototype = { 346 | onCellClick: function() 347 | { 348 | for(var i = 0; i < this.items.length; ++i){ 349 | if(this.items[i].isEditing()){ 350 | return; 351 | } 352 | } 353 | 354 | this.addEventItem("", true); 355 | }, 356 | 357 | addEventItem: function(value, editingMode) 358 | { 359 | ///@todo isEmptyな項目を削除する。 360 | ///@todo 個数制限を設ける。 361 | 362 | this.items.push(new CalendarApp.CalendarEventItemCtrl( 363 | this.cell, value, editingMode, this.date, this.db)); 364 | } 365 | }; 366 | 367 | 368 | 369 | 370 | // 371 | // event item control. 372 | // show/edit a event description. 373 | // 374 | CalendarApp.CalendarEventItemCtrl = function(cell, value, editingMode, date, db) 375 | { 376 | this.date = date; 377 | this.db = db; 378 | this.cell = cell; 379 | this.div = null; 380 | this.textarea = null; 381 | this.msg = null; 382 | this.value = value; 383 | 384 | if(editingMode){ 385 | this.textarea = this.createTextArea(value); 386 | this.cell.appendChild(this.textarea); 387 | this.textarea.focus(); 388 | } 389 | else{ 390 | this.div = this.createDiv(value); 391 | this.cell.appendChild(this.div); 392 | } 393 | }; 394 | CalendarApp.CalendarEventItemCtrl.prototype = { 395 | isEmpty: function() { return !this.textarea && !this.div && !this.msg;}, 396 | isEditing: function() { return !!this.textarea; }, 397 | //isProcessing: function() { return !!this.msg; }, 398 | 399 | createDiv: function(value) 400 | { 401 | var self = this; 402 | var div = document.createElement("div"); 403 | div.className = CalendarApp.cssPrefix + "-event-item"; 404 | div.appendChild(document.createTextNode(value)); 405 | CalendarApp.addEventListener(div, "click", function(){self.onDivClick();}); 406 | return div; 407 | }, 408 | 409 | onDivClick: function() 410 | { 411 | if(!this.div){return;} 412 | 413 | this.textarea = this.createTextArea(this.value); 414 | this.cell.insertBefore(this.textarea, this.div); 415 | this.textarea.focus(); 416 | 417 | this.cell.removeChild(this.div); 418 | this.div = null; 419 | }, 420 | 421 | createTextArea: function(value) 422 | { 423 | var self = this; 424 | var textarea = document.createElement("textarea"); 425 | textarea.className = CalendarApp.cssPrefix + "-event-item-input"; 426 | textarea.value = value; 427 | CalendarApp.addEventListener(textarea, "blur", function(){self.onTextAreaBlur();}); 428 | return textarea; 429 | }, 430 | 431 | onTextAreaBlur: function() 432 | { 433 | if(!this.textarea){return;} 434 | 435 | var self = this; 436 | var oldValue = this.value; 437 | var newValue = this.textarea.value; 438 | ///@todo 値の検証が必要。 439 | if(newValue != oldValue){ 440 | this.value = newValue; 441 | 442 | this.msg = this.createMessageDiv("writing..."); 443 | this.cell.insertBefore(this.msg, this.textarea); 444 | this.db.changeEventItem( 445 | this.date, oldValue, newValue, 446 | function(succeeded, currValue){self.onCalendarDataChanged(succeeded, currValue);}); 447 | } 448 | else{ 449 | if(newValue == ""){ 450 | // cancel to create a new item. 451 | } 452 | else{ 453 | this.div = this.createDiv(this.value); 454 | this.cell.insertBefore(this.div, this.textarea); 455 | } 456 | } 457 | this.cell.removeChild(this.textarea); 458 | this.textarea = null; 459 | }, 460 | 461 | createMessageDiv: function(value) 462 | { 463 | var msg = document.createElement("div"); 464 | msg.className = CalendarApp.cssPrefix + "-event-item-msg"; 465 | msg.appendChild(document.createTextNode(value)); 466 | return msg; 467 | }, 468 | 469 | onCalendarDataChanged: function(succeeded, currValue) 470 | { 471 | if(!this.msg){return;} 472 | 473 | if(!succeeded){ 474 | alert("ERROR:failed to modify the event."); 475 | } 476 | 477 | this.value = currValue; 478 | if(this.value == ""){ 479 | // remove this item. 480 | } 481 | else{ 482 | this.div = this.createDiv(this.value); 483 | this.cell.insertBefore(this.div, this.msg); 484 | } 485 | 486 | this.cell.removeChild(this.msg); 487 | this.msg = null; 488 | } 489 | 490 | }; 491 | 492 | 493 | 494 | --------------------------------------------------------------------------------