Dhtmlx Scheduller(以下简称ds)代码其实分成了很多内容,主要包括了四个对象
Dthmlx
dhtmlxAjax
dhmltxGridObject
Scheduler
以及很多其他方法包括jquery兼容性处理,_CreatBox()为首的一些列创建窗口的方法,button(),callback(),getofferSetSum(),geturlSymbol()等一系列特性方法。
我们需要关注其中的scheduler对象。
最简单的范例
JS代码
scheduler.config.xml_date="%Y-%m-%d %H:%i"; scheduler.init('scheduler_here',new Date(2010,0,10),"week"); scheduler.load("./data/events.xml");
Html代码
<div id="scheduler_here" class="dhx_cal_container" style='width:100%; height:100%;'> <div class="dhx_cal_navline"> <div class="dhx_cal_prev_button"> </div> <div class="dhx_cal_next_button"> </div> <div class="dhx_cal_today_button"></div> <div class="dhx_cal_date"></div> <div class="dhx_cal_tab" name="day_tab" style="right:204px;"></div> <div class="dhx_cal_tab" name="week_tab" style="right:140px;"></div> <div class="dhx_cal_tab" name="month_tab" style="right:76px;"></div> </div> <div class="dhx_cal_header"> </div> <div class="dhx_cal_data"> </div> </div>
从scheduler.init()开始
所有代码都是从scheduler.init开始的,从这个入口开机我们可以看到scheduler主要调用了如下几个方法
本来看dthmlx源码只有一个目的:我要为月视图上的任务条创建一个垂直工具条。
现在好,小半天下来,除了想看的没看明白,不想看的全看明白了。既然看了这么多了,还是直接总结一下备用。学习本来就有两种方式,一种是循序渐进稳扎稳大,一种是直奔主题,根据目的来驱动。
init方法最终会调用setCurrentView,setCurrentView又回去调用updateView最终生成了所有页面event元素, now我们知道了,核心方法是updateView。
接下来根据单个功能点来看代码吧,要不然功能太庞大了
对于单击evnent元素(周视图的event块元素和月视图event bar元素),执行流程又是如下的:
event和event menu元素的生成顺序
其实可以看出来week视图和month视图生成元素的顺序基本上是一致的,只是到了renderdate方法的时候走了一下分支:
- 如果是月视图,则调用reder_event_bar方法;
- 如果是周视图,则调用render_event方法。
月视图通过render_event方法生成了块状事件区域,然后继续调用_render_v_bar生成了垂直工具条。
但是对于月视图而言只是生成了任务bar,没有生成工具栏,原因就是缺少了_render_v_bar方法。但是想要为其生成工具栏的话,_render_v_bar方法不能够直接在此处调用,需要做一些定制化的改造
Week视图生成event区域/event_menu代码
scheduler.render_event = function(ev) { var menu = scheduler.xy.menu_width; var menu_offset = (this.config.use_select_menu_space) ? 0 : menu; if (ev._sday < 0) return; //can occur in case of recurring event during time shift var parent = scheduler.locate_holder(ev._sday); if (!parent) return; //attempt to render non-visible event var sm = ev.start_date.getHours() * 60 + ev.start_date.getMinutes(); var em = (ev.end_date.getHours() * 60 + ev.end_date.getMinutes()) || (scheduler.config.last_hour * 60); var ev_count = ev._count || 1; var ev_sorder = ev._sorder || 0; var top = (Math.round((sm * 60 * 1000 - this.config.first_hour * 60 * 60 * 1000) * this.config.hour_size_px / (60 * 60 * 1000))) % (this.config.hour_size_px * 24); //42px/hour var height = Math.max(scheduler.xy.min_event_height, (em - sm) * this.config.hour_size_px / 60); //42px/hour var width = Math.floor((parent.clientWidth - menu_offset) / ev_count); var left = ev_sorder * width + 1; if (!ev._inner) width = width * (ev_count - ev_sorder); if (this.config.cascade_event_display) { var limit = this.config.cascade_event_count; var margin = this.config.cascade_event_margin; left = ev_sorder % limit * margin; var right = (ev._inner) ? (ev_count - ev_sorder - 1) % limit * margin / 2 : 0; width = Math.floor(parent.clientWidth - menu_offset - left - right); } //生成了event区域 var d = this._render_v_bar(ev.id, menu_offset + left, top, width, height, ev._text_style, scheduler.templates.event_header(ev.start_date, ev.end_date, ev), scheduler.templates.event_text(ev.start_date, ev.end_date, ev)); this._rendered.push(d); parent.appendChild(d); left = left + parseInt(parent.style.left, 10) + menu_offset; if (this._edit_id == ev.id) { d.style.zIndex = 1; //fix overlapping issue width = Math.max(width - 4, scheduler.xy.editor_width); d = document.createElement("DIV"); d.setAttribute("event_id", ev.id); this.set_xy(d, width, height - 20, left, top + 14); d.className = "dhx_cal_editor"; var d2 = document.createElement("DIV"); this.set_xy(d2, width - 6, height - 26); d2.style.cssText += ";margin:2px 2px 2px 2px;overflow:hidden;"; //生成了编辑区域 d.appendChild(d2); this._els["dhx_cal_data"][0].appendChild(d); this._rendered.push(d); d2.innerHTML = "<textarea class='dhx_cal_editor'>" + ev.text + "</textarea>"; if (this._quirks7) d2.firstChild.style.height = height - 12 + "px"; //IEFIX this._editor = d2.firstChild; this._editor.onkeydown = function(e) { if ((e || event).shiftKey) return true; var code = (e || event).keyCode; if (code == scheduler.keys.edit_save) scheduler.editStop(true); if (code == scheduler.keys.edit_cancel) scheduler.editStop(false); }; this._editor.onselectstart = function (e) { return (e || event).cancelBubble = true; }; scheduler._focus(d2.firstChild, true); //IE and opera can add x-scroll during focusing this._els["dhx_cal_data"][0].scrollLeft = 0; } if (this.xy.menu_width !== 0 && this._select_id == ev.id) { if (this.config.cascade_event_display && this._drag_mode) d.style.zIndex = 1; //fix overlapping issue for cascade view in case of dnd of selected event var icons = this.config["icons_" + ((this._edit_id == ev.id) ? "edit" : "select")]; var icons_str = ""; var bg_color = (ev.color ? ("background-color: " + ev.color + ";") : ""); var color = (ev.textColor ? ("color: " + ev.textColor + ";") : ""); //生成按钮 for (var i = 0; i < icons.length; i++) icons_str += "<div class='dhx_menu_icon " + icons[i] + "' style='" + bg_color + "" + color + "' title='" + this.locale.labels[icons[i]] + "'></div>"; //瓦萨,这个不知道生成了什么? var obj = this._render_v_bar(ev.id, left - menu + 1, top, menu, icons.length * 20 + 26 - 2, "", "<div style='" + bg_color + "" + color + "' class='dhx_menu_head'></div>", icons_str, true); obj.style.left = left - menu + 1; this._els["dhx_cal_data"][0].appendChild(obj); this._rendered.push(obj); } };
生成元素的时候会调用如下方法在外部保存一层元素。其实本质上就是生成了一个悬浮元素
scheduler._render_v_bar = function (id, x, y, w, h, style, contentA, contentB, bottom) { var d = document.createElement("DIV"); var ev = this.getEvent(id); var cs = (bottom) ? "dhx_cal_event dhx_cal_select_menu" : "dhx_cal_event"; var cse = scheduler.templates.event_class(ev.start_date, ev.end_date, ev); if (cse) cs = cs + " " + cse; var bg_color = (ev.color ? ("background:" + ev.color + ";") : ""); var color = (ev.textColor ? ("color:" + ev.textColor + ";") : ""); var html = '<div event_id="' + id + '" class="' + cs + '" style="position:absolute; top:' + y + 'px; left:' + x + 'px; width:' + (w - 4) + 'px; height:' + h + 'px;' + (style || "") + '"></div>'; d.innerHTML = html; var container = d.cloneNode(true).firstChild; if (!bottom && scheduler.renderEvent(container, ev)) { return container; } else { container = d.firstChild; var inner_html = '<div class="dhx_event_move dhx_header" style=" width:' + (w - 6) + 'px;' + bg_color + '" > </div>'; inner_html += '<div class="dhx_event_move dhx_title" style="' + bg_color + '' + color + '">' + contentA + '</div>'; inner_html += '<div class="dhx_body" style=" width:' + (w - (this._quirks ? 4 : 14)) + 'px; height:' + (h - (this._quirks ? 20 : 30) + 1) + 'px;' + bg_color + '' + color + '">' + contentB + '</div>'; // +2 css specific, moved from render_event var footer_class = "dhx_event_resize dhx_footer"; if (bottom) footer_class = "dhx_resize_denied " + footer_class; inner_html += '<div class="' + footer_class + '" style=" width:' + (w - 8) + 'px;' + (bottom ? ' margin-top:-1px;' : '') + '' + bg_color + '' + color + '" ></div>'; container.innerHTML = inner_html; } return container; };
month视图生成event区域代码
scheduler.render_event_bar = function (ev) { var parent = this._rendered_location; var pos = this._get_event_bar_pos(ev); var y = pos.y; var x = pos.x; var x2 = pos.x2; //events in ignored dates if (!x2) return; var d = document.createElement("DIV"); var cs = "dhx_cal_event_clear"; if (!ev._timed) { cs = "dhx_cal_event_line"; if (ev.hasOwnProperty("_first_chunk") && ev._first_chunk) cs += " dhx_cal_event_line_start"; if (ev.hasOwnProperty("_last_chunk") && ev._last_chunk) cs += " dhx_cal_event_line_end"; } var cse = scheduler.templates.event_class(ev.start_date, ev.end_date, ev); if (cse) cs = cs + " " + cse; var bg_color = (ev.color ? ("background:" + ev.color + ";") : ""); var color = (ev.textColor ? ("color:" + ev.textColor + ";") : ""); var html = '<div event_id="' + ev.id + '" class="' + cs + '" style="position:absolute; top:' + y + 'px; left:' + x + 'px; width:' + (x2 - x - 15) + 'px;' + color + '' + bg_color + '' + (ev._text_style || "") + '">'; ev = scheduler.getEvent(ev.id); // ev at this point could be a part of a larged event if (ev._timed) html += scheduler.templates.event_bar_date(ev.start_date, ev.end_date, ev); html += scheduler.templates.event_bar_text(ev.start_date, ev.end_date, ev) + '</div>'; html += '</div>'; d.innerHTML = html; this._rendered.push(d.firstChild); parent.appendChild(d.firstChild); //其实增加了如下的代码就可以生成元素了 debugger; if (this.xy.menu_width !== 0 && this._select_id == ev.id) { if (this.config.cascade_event_display && this._drag_mode) d.style.zIndex = 1; //fix overlapping issue for cascade view in case of dnd of selected event var icons = this.config["icons_" + ((this._edit_id == ev.id) ? "edit" : "select")]; var icons_str = ""; var bg_color = (ev.color ? ("background-color: " + ev.color + ";") : ""); var color = (ev.textColor ? ("color: " + ev.textColor + ";") : ""); for (var i = 0; i < icons.length; i++) icons_str += "<div class='dhx_menu_icon " + icons[i] + "' style='" + bg_color + "" + color + "' title='" + this.locale.labels[icons[i]] + "'></div>"; var obj = this._render_v_bar(ev.id, 100, 100,300, 100, "", "<div style='" + bg_color + "" + color + "' class='dhx_menu_head'></div>", icons_str, true); obj.style.left = 200 + 1; this._els["dhx_cal_data"][0].appendChild(obj); this._rendered.push(obj); } };
至此,我们就实现了生成工具条的目标。
Dhtmlx还是一个非常强大的控件,里面可以复用的内容非常多,包括图标,弹出元素样式。后续继续跟踪形成一个系列来写,这不是结果