<ul id="tabs" class="ui-helper-clearfix"> <li><a href="#tab1">Tab 1</a></li> <li><a href="#tab2">Tab 2</a></li> <li><a href="#tab3">Tab 3</a></li> </ul> <div id="panels"> <form id="tab1" class="tab"> <label>Name:</label> <input name="name"/> <label>Age:</label> <select name="age"><option>10</option><option>20</option></select> </form> <form id="tab2" class="tab"> <label>Yard:</label> <select name="yard"><option>1</option><option>2</option></select> <label>Team:</label> <input name="team"/> </form> <form id="tab3" class="tab"> <label>Price:</label> <input name="price"/> <label>Comment:</label> <textarea name="comment"></textarea> </form> </div> <div id="modal"> <div id="background"></div> <div id="box"><p>Do you wish to save?</p> <a href="javascript://" id="yes">Yes</a> <a href="javascript://" id="no">No</a> <a href="javascript://" id="cancel">Cancel</a> </div> </div>
can.Control("Tabs", { init : function (el) { $(el).children("li:first").addClass('active'); var tab = this.tab; this.element.children("li:gt(0)").each(function () { tab($(this)).hide() }) }, tab : function (li) { return $(li.find("a").attr("href").match(/#.*/)[0]) }, "li click" : function (el, ev) { ev.preventDefault(); var active = this.element.find('.active') old = this.tab(active), cur = this.tab(el); old.triggerAsync('hide', function () { active.removeClass('active') old.slideUp(function () { el.addClass('active') cur.slideDown() }); }) } }) // create a widget listens for change and marks as dirty can.Control("Dirtybit", { init : function () { this.element.data('formData', this.element.serialize()) }, "change" : function (el) { this.check() }, keyup : function (el) { this.check() }, click : function (el) { this.check() }, check : function () { var el = this.element; if (el.serialize() == el.data('formData')) { el.removeClass('dirty') } else { el.addClass('dirty') } }, " set" : function () { this.element.data('formData', this.element.serialize()) .removeClass('dirty'); } }) // a modal width can.Control("Modal", { init : function () { this.element.show(); }, "a click" : function (a) { this.element.hide(); this.options[a.attr('id')](); this.destroy(); } }) // create a saver widget can.Control("Saver", { " hide" : function (el, ev) { if (el.hasClass('dirty')) { ev.pause() new Modal('#modal', { yes : function () { var save = $('<span>Saving</span>').appendTo(el); $.post("/update", el.serialize(), function () { save.remove(); el.trigger('set'); ev.resume(); }) }, no : function () { ev.resume(); }, cancel : function () { ev.preventDefault(); ev.resume(); } }) } } }); new Tabs('#tabs'); $('#panels .tab').each(function() { new Dirtybit(this); new Saver(this); }); // a fake post method $.post = function (url, data, success) { setTimeout(success, 500) };
body { font-family: verdana } .tabs { padding: 0px; margin: 0px; } .tabs li { float: left; padding: 10px; background-color: #F6F6F6; list-style: none; margin-left: 10px; } .tabs li a { color: #1C94C4; font-weight: bold; text-decoration: none; } .tabs li.active a { color: #F6A828; cursor: default; } .tab { border: solid 2px #F6A828; width: 360px; height: 100px; padding: 20px; } .dirty { border: solid 2px red; } /* clearfix from jQueryUI */ .ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } .ui-helper-clearfix { display: inline-block; } /* required comment for clearfix to work in Opera \*/ * html .ui-helper-clearfix { height: 1%; } .ui-helper-clearfix { display: block; } /* end clearfix */ #modal { position: absolute; top: 0px; left: 0px; height: 100%; width: 100%; display: none; } #background { background-color: gray; top: 0px; left: 0px; height: 100%; width: 100%; opacity: 0.5; } #box { position: absolute; margin: -55px 0px 0px -105px; top: 50%; left: 50%; border: solid 1px gray; background-color: white; opacity: 1; width: 200px; height: 100px; padding: 10px; } span { color: Red; } label { display: block; }