easyui tabs源码阅读(未完待续)
1 /** 2 * jQuery EasyUI 1.2.5 3 * 4 * Licensed under the GPL terms 5 * To use it on other terms please contact us 6 * 7 * Copyright(c) 2009-2011 stworthy [ stworthy@gmail.com ] 8 * 9 */ 10 (function($) { 11 function getMaxScrollWidth(jq) { 12 var header = $(jq).children("div.tabs-header"); 13 var tabsWidth = 0; 14 $("ul.tabs li", header).each(function() { 15 tabsWidth += $(this).outerWidth(true); 16 }); 17 var wrapWidth = header.children("div.tabs-wrap").width(); 18 var padding = parseInt(header.find("ul.tabs").css("padding-left")); 19 return tabsWidth - wrapWidth + padding; 20 }; 21 function setScrollers(jq) { 22 var opts = $.data(jq, "tabs").options; 23 var header = $(jq).children("div.tabs-header"); 24 var tool = header.children("div.tabs-tool"); 25 var leftScroller = header.children("div.tabs-scroller-left"); 26 var rightScroller = header.children("div.tabs-scroller-right"); 27 var wrap = header.children("div.tabs-wrap"); 28 var height = ($.boxModel == true ? (header.outerHeight() - (tool.outerHeight() - tool 29 .height())) 30 : header.outerHeight()); 31 if (opts.plain) { 32 height -= 2; 33 } 34 tool.height(height); 35 var fullWidth = 0; 36 $("ul.tabs li", header).each(function() { 37 fullWidth += $(this).outerWidth(true); 38 }); 39 var realWidth = header.width() - tool.outerWidth(); 40 if (fullWidth > realWidth) { 41 leftScroller.show(); 42 rightScroller.show(); 43 tool.css("right", rightScroller.outerWidth()); 44 wrap.css( { 45 marginLeft : leftScroller.outerWidth(), 46 marginRight : rightScroller.outerWidth() + tool.outerWidth(), 47 left : 0, 48 width : realWidth - leftScroller.outerWidth() - rightScroller.outerWidth() 49 }); 50 } else { 51 leftScroller.hide(); 52 rightScroller.hide(); 53 tool.css("right", 0); 54 wrap.css( { 55 marginLeft : 0, 56 marginRight : tool.outerWidth(), 57 left : 0, 58 width : realWidth 59 }); 60 wrap.scrollLeft(0); 61 } 62 }; 63 function buildButton(jq) { 64 var opts = $.data(jq, "tabs").options; 65 var header = $(jq).children("div.tabs-header"); 66 if (opts.tools) { 67 if (typeof opts.tools == "string") { 68 $(opts.tools).addClass("tabs-tool").appendTo(header); 69 $(opts.tools).show(); 70 } else { 71 header.children("div.tabs-tool").remove(); 72 var tool = $("<div class=\"tabs-tool\"></div>").appendTo(header); 73 for ( var i = 0; i < opts.tools.length; i++) { 74 var button = $("<a href=\"javascript:void(0);\"></a>") 75 .appendTo(tool); 76 button[0].onclick = eval(opts.tools[i].handler || function() { 77 }); 78 button.linkbutton($.extend( {}, opts.tools[i], { 79 plain : true 80 })); 81 } 82 } 83 } else { 84 header.children("div.tabs-tool").remove(); 85 } 86 }; 87 88 /** 89 *设置tabs的尺寸,其中还判断了盒模式,以适用各种浏览器 90 */ 91 function setSize(jq) { 92 var opts = $.data(jq, "tabs").options; 93 var cc = $(jq); 94 if (opts.fit == true) { 95 var p = cc.parent(); 96 opts.width = p.width(); 97 opts.height = p.height(); 98 } 99 cc.width(opts.width).height(opts.height); 100 var header = $(jq).children("div.tabs-header"); 101 if ($.boxModel == true) { 102 header.width(opts.width - (header.outerWidth() - header.width())); 103 } else { 104 header.width(opts.width); 105 } 106 setScrollers(jq); 107 var panels = $(jq).children("div.tabs-panels"); 108 var height = opts.height; 109 if (!isNaN(height)) { 110 if ($.boxModel == true) { 111 var height = panels.outerHeight() - panels.height(); 112 panels.css("height", (height - header.outerHeight() - height) || "auto"); 113 } else { 114 panels.css("height", height - header.outerHeight()); 115 } 116 } else { 117 panels.height("auto"); 118 } 119 var width = opts.width; 120 if (!isNaN(width)) { 121 if ($.boxModel == true) { 122 panels.width(width - (panels.outerWidth() - panels.width())); 123 } else { 124 panels.width(width); 125 } 126 } else { 127 panels.width("auto"); 128 } 129 }; 130 131 function fitContent(jq) { 132 var opts = $.data(jq, "tabs").options; 133 var tab = getSelected(jq); 134 if (tab) { 135 var panels = $(jq).children("div.tabs-panels"); 136 var width = opts.width == "auto" ? "auto" : panels.width(); 137 var height = opts.height == "auto" ? "auto" : panels.height(); 138 tab.panel("resize", { 139 width : width, 140 height : height 141 }); 142 } 143 }; 144 145 /** 146 *此处的jq指class为"easyui-tabs"的div 147 */ 148 function wrapTabs(jq) { 149 var cc = $(jq); 150 cc.addClass("tabs-container"); 151 //将class为"easyui-tabs"的div的子内容用"<div class=\"tabs-panels\"/>"包裹起来; 152 cc.wrapInner("<div class=\"tabs-panels\"/>"); 153 154 //在class为"tabs-header"的div前添加div 155 $("<div class=\"tabs-header\">" 156 + "<div class=\"tabs-scroller-left\"></div>" 157 + "<div class=\"tabs-scroller-right\"></div>" 158 + "<div class=\"tabs-wrap\">" 159 + "<ul class=\"tabs\"></ul>" + "</div>" + "</div>") 160 .prependTo(jq); 161 var tabs = []; 162 var tp = cc.children("div.tabs-panels"); 163 tp.children("div[selected]").attr("toselect", "true"); 164 //根据最初class为"easyui-tabs"的div的子div创建tabs 165 tp.children("div").each(function() { 166 var pp = $(this); 167 tabs.push(pp); 168 createTab(jq, pp); 169 }); 170 cc.children("div.tabs-header").find( 171 ".tabs-scroller-left, .tabs-scroller-right").hover(function() { 172 $(this).addClass("tabs-scroller-over"); 173 }, function() { 174 $(this).removeClass("tabs-scroller-over"); 175 }); 176 cc.bind("_resize", function(e, param) { 177 var opts = $.data(jq, "tabs").options; 178 if (opts.fit == true || param) { 179 setSize(jq); 180 fitContent(jq); 181 } 182 return false; 183 }); 184 return tabs; 185 }; 186 function setProperties(jq) { 187 var opts = $.data(jq, "tabs").options; 188 var header = $(jq).children("div.tabs-header");//获取class为tabs-header的div,此div即为tabs标签部分; 189 var panel = $(jq).children("div.tabs-panels");//获取class为tabs-panels的div,此div即为tabs中要展示的内容; 190 //如果属性plain为true,则设置tabs标签的背景为透明(默认为true); 191 if (opts.plain == true) { 192 header.addClass("tabs-header-plain"); 193 } else { 194 header.removeClass("tabs-header-plain"); 195 } 196 if (opts.border == true) { 197 header.removeClass("tabs-header-noborder"); 198 panel.removeClass("tabs-panels-noborder"); 199 } else { 200 header.addClass("tabs-header-noborder"); 201 panel.addClass("tabs-panels-noborder"); 202 } 203 //给header的子孙元素中class为".tabs-scroller-left"的div绑定一个自定义事件"click.tabs"; 204 $(".tabs-scroller-left", header).unbind(".tabs").bind("click.tabs", 205 function() { 206 var header = $(".tabs-wrap", header); 207 //scrollLeft 获取匹配元素相对滚动条左侧的偏移; 208 var pos = header.scrollLeft() - opts.scrollIncrement; 209 header.animate( { 210 scrollLeft : pos 211 }, opts.scrollDuration); 212 }); 213 $(".tabs-scroller-right", header).unbind(".tabs").bind( 214 "click.tabs", 215 function() { 216 var header = $(".tabs-wrap", header); 217 var pos = Math.min(header.scrollLeft() + opts.scrollIncrement, 218 getMaxScrollWidth(jq)); 219 header.animate( { 220 scrollLeft : pos 221 }, opts.scrollDuration); 222 }); 223 var tabs = $.data(jq, "tabs").tabs; 224 for ( var i = 0, len = tabs.length; i < len; i++) { 225 var tab = tabs[i]; 226 var tab = tab.panel("options").tab; 227 tab.unbind(".tabs").bind("click.tabs", { 228 p : tab 229 }, function(e) { 230 selectTab(jq, getTabIndex(jq, e.data.p)); 231 }).bind("contextmenu.tabs",{ 232 p : tab 233 }, 234 function(e) { 235 opts.onContextMenu.call(jq, e, e.data.p.panel("options").title); 236 }); 237 tab.find("a.tabs-close").unbind(".tabs").bind("click.tabs", { 238 p : tab 239 }, function(e) { 240 closeTab(jq, getTabIndex(jq, e.data.p)); 241 return false; 242 }); 243 } 244 }; 245 function createTab(container, pp, options) { 246 options = options || {}; 247 pp.panel($.extend( {}, options, { 248 border : false, 249 noheader : true, 250 closed : true, 251 doSize : false, 252 iconCls : (options.icon ? options.icon : undefined), 253 onLoad : function() { 254 if (options.onLoad) { 255 options.onLoad.call(this, arguments); 256 } 257 $.data(container, "tabs").options.onLoad.call(container, pp); 258 } 259 })); 260 var opts = pp.panel("options"); 261 var header = $(container).children("div.tabs-header"); 262 var tabs = $("ul.tabs", header); 263 var tab = $("<li></li>").appendTo(tabs); 264 var tabInner = $("<a href=\"javascript:void(0)\" class=\"tabs-inner\"></a>") 265 .appendTo(tab); 266 var tabTitle = $("<span class=\"tabs-title\"></span>").html(opts.title) 267 .appendTo(tabInner); 268 var tabIcon = $("<span class=\"tabs-icon\"></span>").appendTo(tabInner); 269 if (opts.closable) { 270 tabTitle.addClass("tabs-closable"); 271 $("<a href=\"javascript:void(0)\" class=\"tabs-close\"></a>") 272 .appendTo(tab); 273 } 274 if (opts.iconCls) { 275 tabTitle.addClass("tabs-with-icon"); 276 tabIcon.addClass(opts.iconCls); 277 } 278 if (opts.tools) { 279 var tool = $("<span class=\"tabs-p-tool\"></span>").insertAfter(tabInner); 280 if (typeof opts.tools == "string") { 281 $(opts.tools).children().appendTo(tool); 282 } else { 283 for ( var i = 0; i < opts.tools.length; i++) { 284 var t = $("<a href=\"javascript:void(0)\"></a>").appendTo(tool); 285 t.addClass(opts.tools[i].iconCls); 286 if (opts.tools[i].handler) { 287 t.bind("click", eval(opts.tools[i].handler)); 288 } 289 } 290 } 291 var pr = tool.children().length * 12; 292 if (opts.closable) { 293 pr += 8; 294 } else { 295 pr -= 3; 296 tool.css("right", "5px"); 297 } 298 tabTitle.css("padding-right", pr + "px"); 299 } 300 opts.tab = tab; 301 }; 302 function addTab(jq, options) { 303 var opts = $.data(jq, "tabs").options; 304 var tabs = $.data(jq, "tabs").tabs; 305 var pp = $("<div></div>").appendTo($(jq).children("div.tabs-panels")); 306 tabs.push(pp); 307 createTab(jq, pp, options); 308 opts.onAdd.call(jq, options.title); 309 setScrollers(jq); 310 setProperties(jq); 311 selectTab(jq, tabs.length - 1); 312 }; 313 function update(jq, param) { 314 var selectHis = $.data(jq, "tabs").selectHis; 315 var pp = param.tab; 316 var title = pp.panel("options").title; 317 pp.panel($.extend( {}, param.options, { 318 iconCls : (param.options.icon ? param.options.icon : undefined) 319 })); 320 var opts = pp.panel("options"); 321 var tab = opts.tab; 322 tab.find("span.tabs-icon").attr("class", "tabs-icon"); 323 tab.find("a.tabs-close").remove(); 324 tab.find("span.tabs-title").html(opts.title); 325 if (opts.closable) { 326 tab.find("span.tabs-title").addClass("tabs-closable"); 327 $("<a href=\"javascript:void(0)\" class=\"tabs-close\"></a>").appendTo(tab); 328 } else { 329 tab.find("span.tabs-title").removeClass("tabs-closable"); 330 } 331 if (opts.iconCls) { 332 tab.find("span.tabs-title").addClass("tabs-with-icon"); 333 tab.find("span.tabs-icon").addClass(opts.iconCls); 334 } else { 335 tab.find("span.tabs-title").removeClass("tabs-with-icon"); 336 } 337 if (title != opts.title) { 338 for ( var i = 0; i < selectHis.length; i++) { 339 if (selectHis[i] == title) { 340 selectHis[i] = opts.title; 341 } 342 } 343 } 344 setProperties(jq); 345 $.data(jq, "tabs").options.onUpdate.call(jq, opts.title); 346 }; 347 function closeTab(container, title) { 348 var opts = $.data(container, "tabs").options; 349 var tabs = $.data(container, "tabs").tabs; 350 var selectHis = $.data(container, "tabs").selectHis; 351 if (!exists(container, title)) { 352 return; 353 } 354 var tab = getTab(container, title); 355 var title = tab.panel("options").title; 356 if (opts.onBeforeClose.call(container, title) == false) { 357 return; 358 } 359 var tab = getTab(container, title, true); 360 tab.panel("options").tab.remove(); 361 tab.panel("destroy"); 362 opts.onClose.call(container, title); 363 setScrollers(container); 364 for ( var i = 0; i < selectHis.length; i++) { 365 if (selectHis[i] == title) { 366 selectHis.splice(i, 1); 367 i--; 368 } 369 } 370 var lastTab = selectHis.pop(); 371 if (lastTab) { 372 selectTab(container, lastTab); 373 } else { 374 if (tabs.length) { 375 selectTab(container, 0); 376 } 377 } 378 }; 379 function getTab(container, title, close) { 380 var tabs = $.data(container, "tabs").tabs; 381 if (typeof title == "number") { 382 if (title < 0 || title >= tabs.length) { 383 return null; 384 } else { 385 var tab = tabs[title]; 386 if (close) { 387 tabs.splice(title, 1); 388 } 389 return tab; 390 } 391 } 392 for ( var i = 0; i < tabs.length; i++) { 393 var tab = tabs[i]; 394 if (tab.panel("options").title == title) { 395 if (close) { 396 tabs.splice(i, 1); 397 } 398 return tab; 399 } 400 } 401 return null; 402 }; 403 function getTabIndex(jq, tab) { 404 var tabs = $.data(jq, "tabs").tabs; 405 for ( var i = 0; i < tabs.length; i++) { 406 if (tabs[i][0] == $(tab)[0]) { 407 return i; 408 } 409 } 410 return -1; 411 }; 412 function getSelected(jq) { 413 var tabs = $.data(jq, "tabs").tabs; 414 for ( var i = 0; i < tabs.length; i++) { 415 var tab = tabs[i]; 416 if (tab.panel("options").closed == false) { 417 return tab; 418 } 419 } 420 return null; 421 }; 422 function initSelectTab(jq) { 423 var tabs = $.data(jq, "tabs").tabs; 424 for ( var i = 0; i < tabs.length; i++) { 425 if (tabs[i].attr("toselect") == "true") { 426 selectTab(jq, i); 427 return; 428 } 429 } 430 if (tabs.length) { 431 selectTab(jq, 0); 432 } 433 }; 434 function selectTab(jq, title) { 435 var opts = $.data(jq, "tabs").options; 436 var tabs = $.data(jq, "tabs").tabs; 437 var selectHis = $.data(jq, "tabs").selectHis; 438 if (tabs.length == 0) { 439 return; 440 } 441 var tab = getTab(jq, title); 442 if (!tab) { 443 return; 444 } 445 var selected = getSelected(jq); 446 if (selected) { 447 selected.panel("close"); 448 selected.panel("options").tab.removeClass("tabs-selected"); 449 } 450 tab.panel("open"); 451 var title = tab.panel("options").title; 452 selectHis.push(title); 453 var tab = tab.panel("options").tab; 454 tab.addClass("tabs-selected"); 455 var wrap = $(jq).find(">div.tabs-header div.tabs-wrap"); 456 var leftPos = tab.position().left + wrap.scrollLeft(); 457 var left = leftPos - wrap.scrollLeft(); 458 var right = left + tab.outerWidth(); 459 if (left < 0 || right > wrap.innerWidth()) { 460 var pos = Math.min(leftPos - (wrap.width() - tab.width()) / 2, getMaxScrollWidth(jq)); 461 wrap.animate( { 462 scrollLeft : pos 463 }, opts.scrollDuration); 464 } else { 465 var pos = Math.min(wrap.scrollLeft(), getMaxScrollWidth(jq)); 466 wrap.animate( { 467 scrollLeft : pos 468 }, opts.scrollDuration); 469 } 470 fitContent(jq); 471 opts.onSelect.call(jq, title); 472 }; 473 function exists(container, title) { 474 return getTab(container, title) != null; 475 }; 476 477 //构造函数 478 $.fn.tabs = function(options, param) 479 //如果options为string类型,表明是调用tabs的方法; 480 if (typeof options == "string") { 481 return $.fn.tabs.methods[options](this, param); 482 } 483 options = options || {}; 484 return this.each(function() { 485 var state = $.data(this, "tabs"); 486 var opts; 487 if (state) { 488 opts = $.extend(state.options, options); 489 state.options = opts; 490 } else { 491 $.data(this, "tabs", { 492 options : $.extend( {}, $.fn.tabs.defaults, $.fn.tabs 493 .parseOptions(this), options), 494 tabs : wrapTabs(this), 495 selectHis : [] 496 }); 497 } 498 buildButton(this); 499 setProperties(this); 500 setSize(this); 501 initSelectTab(this); 502 }); 503 }; 504 $.fn.tabs.methods = { 505 options : function(jq) { 506 return $.data(jq[0], "tabs").options; 507 }, 508 tabs : function(jq) { 509 return $.data(jq[0], "tabs").tabs; 510 }, 511 resize : function(jq) { 512 return jq.each(function() { 513 setSize(this); 514 fitContent(this); 515 }); 516 }, 517 add : function(jq, options) { 518 return jq.each(function() { 519 addTab(this, options); 520 }); 521 }, 522 close : function(jq, title) { 523 return jq.each(function() { 524 closeTab(this, title); 525 }); 526 }, 527 getTab : function(jq, title) { 528 return getTab(jq[0], title); 529 }, 530 getTabIndex : function(jq, tab) { 531 return getTabIndex(jq[0], tab); 532 }, 533 getSelected : function(jq) { 534 return getSelected(jq[0]); 535 }, 536 select : function(jq, title) { 537 return jq.each(function() { 538 selectTab(this, title); 539 }); 540 }, 541 exists : function(jq, title) { 542 return exists(jq[0], title); 543 }, 544 update : function(jq, param) { 545 return jq.each(function() { 546 update(this, param); 547 }); 548 } 549 }; 550 $.fn.tabs.parseOptions = function(target) { 551 var t = $(target); 552 return { 553 width : (parseInt(target.style.width) || undefined), 554 height : (parseInt(target.style.height) || undefined), 555 fit : (t.attr("fit") ? t.attr("fit") == "true" : undefined), 556 border : (t.attr("border") ? t.attr("border") == "true" : undefined), 557 plain : (t.attr("plain") ? t.attr("plain") == "true" : undefined), 558 tools : t.attr("tools") 559 }; 560 }; 561 $.fn.tabs.defaults = { 562 width : "auto", 563 height : "auto", 564 plain : false, 565 fit : false, 566 border : true, 567 tools : null, 568 scrollIncrement : 100, 569 scrollDuration : 400, 570 onLoad : function(panel) { 571 }, 572 onSelect : function(title) { 573 }, 574 onBeforeClose : function(title) { 575 }, 576 onClose : function(title) { 577 }, 578 onAdd : function(title) { 579 }, 580 onUpdate : function(title) { 581 }, 582 onContextMenu : function(e, title) { 583 } 584 }; 585 })(jQuery);