define(function(require, module, exports) { return function(apf) { var $setTimeout = setTimeout; var $setInterval = setInterval; /** * A page in a pageable element (_i.e._ a page in {@link apf.tab}). * * #### Example * * ```xml, demo * * * * * * Example * Example * * * Test checkbox * Test checkbox * Test checkbox * * * This ok? * This better? * * * * * * ``` * * @class apf.page * @define page * @container * @inherits apf.Presentation * @allowchild {elements}, {anyaml} * * * @author Ruben Daniels (ruben AT ajax DOT org) * @version %I%, %G% * @since 0.8 */ apf.page = function(struct, tagName) { this.$init(tagName || "page", apf.NODE_VISIBLE, struct); }; (function(){ this.canHaveChildren = true; this.$focussable = false; this.closebtn = false; this.autofocus = true; /** * Sets the caption of the button of this element. * @param {String} caption The text displayed on the button of this element. */ this.setCaption = function(caption) { this.setProperty("caption", caption, false, true); }; /** * Sets the icon of the button of this element. * @param {String} icon The icon displayed on the button of this element. */ this.setIcon = function(icon) { this.setProperty("icon", icon, false, true); }; // *** Delayed Render Support *** // //Hack this.addEventListener("beforerender", function(){ this.parentNode.dispatchEvent("beforerender", { page: this }); }); this.addEventListener("afterrender", function(){ this.parentNode.dispatchEvent("afterrender", { page: this }); }); // *** Properties *** // this.$booleanProperties["visible"] = true; this.$booleanProperties["fake"] = true; this.$booleanProperties["closebtn"] = true; this.$booleanProperties["autofocus"] = true; this.$supportedProperties.push("fake", "caption", "icon", "tooltip", "type", "buttons", "closebtn", "trans-in", "trans-out", "autofocus"); /** * @attribute {Boolean} closebtn Sets or gets whether this page's button shows a close button inside it. */ this.$propHandlers["closebtn"] = function(value) { //if (!this.$amlLoaded || !this.parentNode.$hasButtons) // return; var _self = this; if (value) { var btncontainer = this.parentNode.$getLayoutNode("button", "container", this.$button); this.parentNode.$getNewContext("btnclose"); var elBtnClose = this.parentNode.$getLayoutNode("btnclose"); if (elBtnClose) { // if (elBtnClose.nodeType == 1) { apf.setStyleClass(this.$button, "btnclose"); elBtnClose.addEventListener("mousedown", function(e) { apf.cancelBubble(e, apf.lookup(_self.$uniqueId)); }, false); elBtnClose.addEventListener("click", function(e) { var page = apf.lookup(_self.$uniqueId); page.parentNode.remove(page, e); }, false); btncontainer.appendChild(elBtnClose); } } }; /** * @attribute {String} caption Sets or gets the text displayed on the button of this element. */ this.$propHandlers["tooltip"] = function(value) { if (!this.parentNode) return; var node = this.parentNode .$getLayoutNode("button", "caption", this.$button); (node.nodeType == 1 ? node : node.parentNode).setAttribute("title", value || ""); } /** * @attribute {String} caption Sets or gets the text displayed on the button of this element. */ this.$propHandlers["caption"] = function(value) { if (!this.parentNode) return; var node = this.parentNode .$getLayoutNode("button", "caption", this.$button); if (node.nodeType == 1) node.innerHTML = value; else node.nodeValue = value; }; this.$propHandlers["icon"] = function(value) { if (!this.parentNode) return; var node = this.parentNode .$getLayoutNode("button", "icon", this.$button); if (node && node.nodeType == 1) apf.skins.setIcon(node, value, this.parentNode.iconPath); }; this.$propHandlers["visible"] = function(value) { if (!this.parentNode) return; if (value) { if (this.$fake) { this.parentNode.set(this.$fake); this.visible = false; return; } this.$ext.style.display = ""; if (this.parentNode.$hasButtons) this.$button.style.display = "block"; if (!this.parentNode.$activepage) this.parentNode.set(this); } else { if (this.$active) { this.$deactivate(); // Try to find a next page, if any. var nextPage = this.parentNode.activepagenr + 1; var pages = this.parentNode.getPages() var len = pages.length while (nextPage < len && !pages[nextPage].visible) nextPage++; if (nextPage == len) { // Try to find a previous page, if any. nextPage = this.parentNode.activepagenr - 1; while (nextPage >= 0 && len && !pages[nextPage].visible) nextPage--; } if (nextPage >= 0) this.parentNode.set(nextPage); else { this.parentNode.activepage = this.parentNode.activepagenr = this.parentNode.$activepage = null; } } this.$ext.style.display = "none"; if (this.parentNode.$hasButtons) this.$button.style.display = "none"; } }; /** * @attribute {Boolean} fake Sets or gets whether this page actually contains elements or * only provides a button in the pageable parent element. */ this.$propHandlers["fake"] = function(value) { if (this.$ext) { apf.destroyHtmlNode(this.$ext); this.$int = this.$ext = null; } }; this.$propHandlers["type"] = function(value) { this.setProperty("fake", true); if (this.relPage && this.$active) this.relPage.$deactivate(); this.relPage = this.parentNode.getPage(value); if (this.$active) this.$activate(); }; // *** DOM Hooks *** // this.addEventListener("DOMNodeRemoved", function(e) { if (e && e.currentTarget != this) return; if (this.$button) { if (this.$position & 1) this.parentNode.$setStyleClass(this.$button, "", ["firstbtn", "firstcurbtn"]); if (this.$position & 2) this.parentNode.$setStyleClass(this.$button, "", ["lastbtn"]); } if (!e.$doOnlyAdmin) { if (this.$button) this.$button.parentNode.removeChild(this.$button); if (this.parentNode && this.parentNode.$activepage == this) { if (this.$button) this.parentNode.$setStyleClass(this.$button, "", ["curbtn"]); this.parentNode.$setStyleClass(this.$ext, "", ["curpage"]); } } }); this.addEventListener("DOMNodeRemovedFromDocument", function(e) { if (this.fake && this.parentNode && this.parentNode.$activepage == this) this.$deactivate(); }); this.addEventListener("DOMNodeInserted", function(e) { if (e && e.currentTarget != this || !this.$amlLoaded) //|| !e.$oldParent return; if (!e.$isMoveWithinParent && this.skinName != this.parentNode.skinName) { // this.$destroy(); //clean up button var skin = this.parentNode.skinName.split(":"); this.$forceSkinChange(skin[1], skin[0]); } else if (this.$button && (!e.$oldParent || e.$oldParent.$hasButtons) && this.parentNode.$buttons) this.parentNode.$buttons.insertBefore(this.$button, e.$beforeNode && e.$beforeNode.$button || null); if (this.type) this.relPage = this.parentNode.getPage && this.parentNode.getPage(this.type); }, true); // *** Private state functions *** // this.$position = 0; this.$first = function(remove) { if (remove) { this.$isFirst = false; this.$position -= 1; this.parentNode.$setStyleClass(this.$button, "", ["firstbtn", "firstcurbtn"]); } else { this.$isFirst = true; this.$position = this.$position | 1; this.parentNode.$setStyleClass(this.$button, "firstbtn" + (this.parentNode.$activepage == this ? " firstcurbtn" : "")); } }; this.$last = function(remove) { if (remove) { this.$isLast = false; this.$position -= 2; this.parentNode.$setStyleClass(this.$button, "", ["lastbtn"]); } else { this.$isLast = true; this.$position = this.$position | 2; this.parentNode.$setStyleClass(this.$button, "lastbtn"); } }; this.$deactivate = function(fakeOther) { this.$active = false; if (!this.parentNode) return; if (this.parentNode.$hasButtons) { if (this.$position > 0) this.parentNode.$setStyleClass(this.$button, "", ["firstcurbtn"]); this.parentNode.$setStyleClass(this.$button, "", ["curbtn"]); } if ((!this.fake || this.relPage) && !fakeOther) { this.parentNode.$setStyleClass(this.fake ? this.relPage.$ext : this.$ext, "", ["curpage"]); if (this.fake) { if (!this.relPage.visible) this.relPage.$ext.style.display = "none"; this.relPage.dispatchEvent("prop.visible", {value:false}); } this.dispatchEvent("prop.visible", {value:false}); } }; this.$deactivateButton = function() { if (this.parentNode && this.parentNode.$hasButtons) { if (this.$position > 0) this.parentNode.$setStyleClass(this.$button, "", ["firstcurbtn"]); this.parentNode.$setStyleClass(this.$button, "", ["curbtn"]); } }; this.$activate = function(){ //if (this.disabled) //return false; this.$active = true; if (!this.$drawn) { var f; this.addEventListener("DOMNodeInsertedIntoDocument", f = function(e) { this.removeEventListener("DOMNodeInsertedIntoDocument", f); if (!this.$active) return; this.$activate(); }); return; } if (this.parentNode.$hasButtons) { if (this.$isFirst) this.parentNode.$setStyleClass(this.$button, "firstcurbtn"); this.parentNode.$setStyleClass(this.$button, "curbtn"); } if (!this.fake || this.relPage) { if (this.fake) { if (this.relPage) { this.relPage.$ext.style.display = ""; this.parentNode.$setStyleClass(this.relPage.$ext, "curpage"); this.relPage.$fake = this; if (this.relPage.$render) this.relPage.$render(); this.relPage.dispatchEvent("prop.visible", {value:true}); } } else { this.parentNode.$setStyleClass(this.$ext, "curpage"); } if (apf.layout && this.relPage) apf.layout.forceResize(this.fake ? this.relPage.$int : this.$int); } if (this.$render) this.$render(); if (!this.fake) { this.dispatchEvent("prop.visible", {value:true}); } }; this.$activateButton = function() { if (this.$active) return; if (!this.$drawn) { var f; this.addEventListener("DOMNodeInsertedIntoDocument", f = function(e) { this.removeEventListener("DOMNodeInsertedIntoDocument", f); this.$activateButton(); }); return; } if (this.parentNode && this.parentNode.$hasButtons) { if (this.$isFirst) this.parentNode.$setStyleClass(this.$button, "firstcurbtn"); this.parentNode.$setStyleClass(this.$button, "curbtn"); } if (this.$render) this.$render(); }; this.addEventListener("$skinchange", function(){ if (this.caption) this.$propHandlers["caption"].call(this, this.caption); if (this.icon) this.$propHandlers["icon"].call(this, this.icon); }); this.$enable = function(){ if (this.$button) this.$setStyleClass(this.$button, null, ["btnDisabled"]);//@todo this.$baseCSSname + }; this.$disable = function(){ if (this.$button) this.$setStyleClass(this.$button, "btnDisabled");//@todo this.$baseCSSname + }; function $btnSet(oHtml) { this.parentNode.set(this); if (this.autofocus) this.canHaveChildren = 2; this.$setStyleClass(oHtml, "down", null, true); } this.$btnDown = function(oHtml, htmlEvent) { if (this.disabled) return; if (htmlEvent.button == 2) { return; } if (htmlEvent.button == 1) { apf.stopEvent(htmlEvent); return; } if (this.parentNode.dispatchEvent("tabselectclick", { page: this, htmlEvent: htmlEvent }) === false) return; this.$btnPressed = true; //if (!this.parentNode.$order) $btnSet.call(this, oHtml); } this.$btnUp = function(oHtml, htmlEvent) { this.parentNode.$setStyleClass(oHtml, "", ["down"], true); if (this.disabled) return; if (htmlEvent.button == 1) { apf.stopEvent(htmlEvent); var page = apf.lookup(this.$uniqueId); page.parentNode.remove(page, htmlEvent); return; } if (false && this.parentNode.$order && this.$btnPressed) { this.$dragging = false; $btnSet.call(this, oHtml); } this.$btnPressed = false; this.parentNode.dispatchEvent("tabselectmouseup"); } this.$btnOut = function(oHtml) { this.parentNode.$setStyleClass(oHtml, "", ["over"], true); this.canHaveChildren = true; this.$dragging = false; this.$btnPressed = false; } // *** Init *** // this.$canLeechSkin = true; this.addEventListener("prop.class", function(e) { apf.setStyleClass(this.$button, e.value, this.$lastClassValueBtn ? [this.$lastClassValueBtn] : null); this.$lastClassValueBtn = e.value; }); this.$draw = function(isSkinSwitch) { this.skinName = this.parentNode.skinName; var sType = this.getAttribute("type") if (sType) { this.fake = true; this.relPage = this.parentNode.getPage(sType) || null; } if (this.parentNode.$hasButtons) { //this.parentNode.$removeEditable(); //@todo multilingual support is broken when using dom this.parentNode.$getNewContext("button"); var elBtn = this.parentNode.$getLayoutNode("button"); elBtn.setAttribute(this.parentNode.$getOption("main", "select") || "onmousedown", 'apf.lookup(' + this.$uniqueId + ').$btnDown(this, event);'); elBtn.setAttribute("onmouseup", 'apf.lookup(' + this.$uniqueId + ').$btnUp(this, event)'); elBtn.setAttribute("onmouseover", 'var o = apf.lookup(' + this.$uniqueId + ').parentNode;if(apf.lookup(' + this.$uniqueId + ') != o.$activepage' + (this.parentNode.overactivetab ? " || true" : "") + ') o.$setStyleClass(this, "over", null, true);'); elBtn.setAttribute("onmouseout", 'var o = apf.lookup(' + this.$uniqueId + ');o&&o.$btnOut(this, event);'); //var cssClass = this.getAttribute("class"); //if (cssClass) { // apf.setStyleClass(elBtn, cssClass); // this.$lastClassValueBtn = cssClass; //} this.$button = apf.insertHtmlNode(elBtn, this.parentNode.$buttons); var closebtn = this.closebtn = this.getAttribute("closebtn"); if ((apf.isTrue(closebtn) || ((this.parentNode.buttons || "").indexOf("close") > -1 && !apf.isFalse(closebtn)))) this.$propHandlers["closebtn"].call(this, true); // if (this.parentNode.$scale) { // var w = apf.getHtmlInnerWidth(this.parentNode.$buttons); // var l = this.parentNode.getPages().length; // this.$button.style.width = Math.round(Math.min(w/l, this.parentNode.$maxBtnWidth)) + "px"; // } if (!isSkinSwitch && this.nextSibling && this.nextSibling.$button) this.$button.parentNode.insertBefore(this.$button, this.nextSibling.$button); this.$button.host = this; } if (this.fake) return; if (this.$ext) this.$ext.parentNode.removeChild(this.$ext); //@todo mem leaks? this.$ext = this.parentNode.$getExternal("page", this.parentNode.oPages, null, this); this.$ext.host = this; this.$int = this.parentNode .$getLayoutNode("page", "container", this.$ext); //if (this.$int) //this.$int.setAttribute("id", this.$int.getAttribute("id")); //@todo this doesnt support hidden nodes. if (this.visible) { if (this.$isLast) this.$last(); if (this.$isFirst) this.$first(); } var _self = this; this.parentNode && this.parentNode.getPages().forEach(function(page) { if (page && page.type == _self.id) { page.relPage = _self; if (page.$active) { _self.$fake = page; page.$activate(); } _self.$button.style.display = "none"; _self.visible = false; } }) }; this.$destroy = function(){ if (this.$button) { if (this.parentNode && !this.parentNode.$amlDestroyed && this.$button.parentNode) this.$button.parentNode.removeChild(this.$button); this.$button.host = null; this.$button = null; } }; }).call(apf.page.prototype = new apf.Presentation()); apf.aml.setElement("page", apf.page); /** * Baseclass of a paged element. * * @class apf.BaseTab * @baseclass * @allowchild page * @author Ruben Daniels (ruben AT ajax DOT org) * @version %I%, %G% * @since 0.8 * @inherits apf.Presentation * */ /** * @event beforeswitch Fires before this element switches to another page. * @cancelable Prevents the page to become active. * @param {Object} e The standard event object. It contains the following properties: * - previous ([[String]] or [[Number]]): The name or number of the current page. * - previousId ([[Number]]): The number of the current page. * - previousPage ([[apf.page]]): The current page. * - next ([[String]] or [[Number]]): The name or number of the page the will become active. * - nextId ([[Number]]): The number of the page the will become active. * - nextPage ([[apf.page]]): The page the will become active. */ /** * @event afterswitch Fires after this element has switched to another page. * @param {Object} e The standard event object. It contains the following properties: * - previous ([[String]] or [[Number]]): The name or number of the previous page. * - previousId ([[Number]]): The number of the previous page. * - previousPage ([[apf.page]]): The previous page. * - next ([[String]] or [[Number]]): The name or number of the current page. * - nextId ([[Number]]): The number of the the current page. * - nextPage ([[apf.page]]): The the current page. */ apf.BaseTab = function(){ this.$init(true); }; (function() { this.isPaged = true; this.$focussable = apf.KEYBOARD; this.length = 0; this.isLoading = {}; this.inited = this.ready = false; this.$scroll = true; /** * Sets the current page of this element. * @param {String | Number} page The name or number of the page which is made active. * @param {Function} callback The function called after setting the page. Especially handy when using the `src` attribute. */ this.set = function(page, callback, noEvent) { if (noEvent || this.src && !this.$findPage(page, {})) { return this.$propHandlers["activepage"].call( this, page, null, null, callback, noEvent); } if (this.activepage == (page.name || page)) return callback && callback(this.getPage(page)); this.$lastCallback = callback; this.setProperty("activepage", page); }; // *** Properties and Attributes *** // this.$supportedProperties.push("activepage", "activepagenr", "length", "src", "loading", "trans-in", "trans-out"); /** * @property {Number} [SCROLL_LEFT=1] The constant representing the "scroll left" button * @readonly */ /** * @property {Number} [SCROLL_RIGHT=2] The constant representing the "scroll right" button * @readonly */ /** * @property {Number} [SCROLL_BOTH=4] The constant representing the "scroll left" and "scroll right" buttons * @readonly */ /** * @attribute {Number} activepagenr Sets or gets the child number of the active page. * * #### Example * * This example uses property binding to maintain consistency between a * dropdown which is used as a menu, and a pages element * * ```xml * * Home * General * Advanced * * * * *

Home Page

*
* *

General Page

*
* *

Advanced Page

*
*
* ``` */ this.$propHandlers["activepagenr"] = /** * @attribute {String} activepage Sets or gets the name of the active page. * * #### Example * * ```xml * * * ... * * * ... * * * ... * * * ``` */ this.$propHandlers["activepage"] = function(next, prop, force, callback, noEvent) { if (!this.inited || apf.isNot(next) || next == -1) return; if (!callback) { callback = this.$lastCallback; delete this.$lastCallback; } var page, info = {}; page = this.$findPage(next, info); if (!page) { if (this.src) { if (this.isLoading[next]) return; if (this.$findPage("loading", {})) this.$propHandlers["activepage"].call(this, "loading"); this.setProperty("loading", true); this.isLoading[next] = true; page = this.ownerDocument.createElementNS(apf.ns.apf, "page"); page.setAttribute("id", next); this.appendChild(page); var _self = this; page.insertMarkup(this.src, { page: next, //@todo apf3.0 change callback arguments in xinclude callback: function(options) { delete _self.isLoading[next]; if (!options.xmlNode) { var oError = new Error(apf.formatErrorString(0, null, "Loading new page", "Could not load new page: " + _self.src)); _self.setProperty("loading", false); if (this.dispatchEvent("error", apf.extend({ error: oError, bubbles: true }, options)) === false) return true; throw oError; } else { //for success _self.setProperty("activepage", next); //Needs to be after set if (callback) callback(options.amlNode); _self.setProperty("loading", false); } } }); return; } return false; } if (page.parentNode != this) { return false; } if (!page.visible || page.disabled) { return false; } //If page is given as first argument, let's use its position if (next.tagName) { next = info.position; this.activepage = page.name || next;//page.type || } //Call the onbeforeswitch event; if (!noEvent) { var oEvent = { previous: this.activepage, previousId: this.activepagenr, previousPage: this.$activepage, next: next, nextId: info.position, nextPage: page }; if (this.dispatchEvent("beforeswitch", oEvent) === false) { //Loader support if (this.hideLoader) this.hideLoader(); return false; } } //Maintain an activepagenr property (not reentrant) this.activepagenr = info.position; this.setProperty("activepagenr", info.position); //Deactivate the current page, if any, and activate the new one if (this.$activepage) this.$activepage.$deactivate(); page.$activate(); this.$activepage = page; //this.scrollIntoView(page); //Loader support if (this.hideLoader) { if (page.$rendered !== false) { this.hideLoader(); } else { //Delayed rendering support page.addEventListener("afterrender", function(){ this.parentNode.hideLoader(); }); } } if (!noEvent) { if (page.$rendered !== false) this.dispatchEvent("afterswitch", oEvent); else { //Delayed rendering support page.addEventListener("afterrender", function(){ this.parentNode.dispatchEvent("afterswitch", oEvent); }); } } if (typeof callback == "function") callback(page); return true; }; /** * @attribute {String} buttons Sets or gets the modifier for tab page buttons, seperated by a `|` character * * Possible values include: * - `close`: The button has a close button inside it * - `scale`: The buttons are scaled to make room for more buttons * - `scroll`: When the buttons take too much space, scroll buttons are displayed */ this.$propHandlers["buttons"] = function(value) { //this.buttons = value; this.$scale = value.indexOf("scale") > -1; this.$scroll = !this.$scale; this.$order = value.indexOf("order") > -1; //@todo skin change //@todo buttons on the side if (this.$scale) { this.$maxBtnWidth = parseInt(this.$getOption("button", "maxwidth")) || 150; this.$minBtnWidth = parseInt(this.$getOption("button", "minwidth")) || 10; this.$setStyleClass(this.$buttons, "scale"); this.addEventListener("resize", scalersz); // this.minwidth = this.$minBtnWidth * this.getPages().length + 10; // this.$ext.style.minWidth = Math.max(0, this.minwidth - apf.getWidthDiff(this.$ext)) + "px"; } else { this.$setStyleClass(this.$buttons, "", ["scale"]); this.removeEventListener("resize", scalersz); } }; function visCheck(){ scalersz.call(this) } this.anims = "add|remove|sync"; //Add an element function animAddTab(tab, callback) { var t = tab.$button; var animateWidth = (t.offsetWidth - apf.getWidthDiff(t)) < parseInt(apf.getStyle(t, "maxWidth")); if (animateWidth) { var p = tab.parentNode.getPages()[0] == tab ? null : tab.previousSibling.$button; var tb = p ? (p.offsetWidth - apf.getWidthDiff(p)) : parseInt(apf.getStyle(t, "maxWidth")); t.style.maxWidth = "0px"; } t.style.marginTop = (t.offsetHeight + 2) + "px"; function animateToTop(){ t.style.marginTop = "0px"; setTimeout(function(){ t.style[apf.CSSPREFIX + "TransitionProperty"] = ""; t.style[apf.CSSPREFIX + "TransitionDuration"] = ""; t.style[apf.CSSPREFIX + "TimingFunction"] = ""; t.style.marginTop = ""; if (animateWidth) t.style.maxWidth = ""; callback(tab); }, 150); } setTimeout(function(){ t.style[apf.CSSPREFIX + "TransitionProperty"] = "margin-top, max-width"; t.style[apf.CSSPREFIX + "TransitionDuration"] = "100ms, 50ms"; t.style[apf.CSSPREFIX + "TimingFunction"] = "cubic-bezier(.10, .10, .25, .90), cubic-bezier(.10, .10, .25, .90)"; if (animateWidth) { t.style.maxWidth = tb + "px"; setTimeout(animateToTop, 50); } else animateToTop(); }); } //Remove an element function animRemoveTab(tab, isLast, isContracted, callback, isOnly) { var t = tab.$button; if (!t) return; var tb = t.offsetHeight; var diff = t.offsetWidth; t.style[apf.CSSPREFIX + "TransitionProperty"] = "margin-top, max-width, padding"; t.style[apf.CSSPREFIX + "TransitionDuration"] = (isOnly ? ".2" : ".15") + "s, .1s, .1s"; t.style[apf.CSSPREFIX + "TimingFunction"] = "linear, ease-out, ease-out"; t.style.marginTop = (tb + 2) + "px"; var p = t.parentNode; if (apf.isGecko) p = p.parentNode; p.style[apf.CSSPREFIX + "TransitionProperty"] = "padding-right"; p.style[apf.CSSPREFIX + "TransitionDuration"] = ".2s"; p.style[apf.CSSPREFIX + "TimingFunction"] = "ease-out"; // if (!isLast && isContracted) { // t.style.minWidth = "20px" // t.style.maxWidth = "0px"; // t.style.padding = 0; // end(); // } // else { setTimeout(function(){ if (isLast) p.style.paddingRight = ""; else { var cur = parseInt(apf.getStyle(p, "paddingRight")); p.style.paddingRight = (cur + diff - (apf.tabRightDelta || 15)) + "px"; } t.className += " destroyed"; end(); }, isOnly ? 150 : 100); // } function end(){ setTimeout(function(){ p.style[apf.CSSPREFIX + "TransitionProperty"] = ""; p.style[apf.CSSPREFIX + "TransitionDuration"] = ""; p.style[apf.CSSPREFIX + "TimingFunction"] = ""; t.style[apf.CSSPREFIX + "TransitionProperty"] = ""; t.style[apf.CSSPREFIX + "TransitionDuration"] = ""; t.style[apf.CSSPREFIX + "TimingFunction"] = ""; t.style.display = "none"; callback(tab); }, 150); } } this.$scaleinit = function(node, type, callback, force) { var _self = this; var pg = this.getPages(); var l = pg.length; if (!l) return; // this.minwidth = this.$minBtnWidth * l + 10; //@todo padding + margin of button container // this.$ext.style.minWidth = Math.max(0, this.minwidth - apf.getWidthDiff(this.$ext)) + "px"; if (force && !this.$ext.offsetWidth && !this.$ext.offsetHeight || this.anims.indexOf(type) == -1 || this.skipAnimOnce) { scalersz.call(this); this.skipAnimOnce = false; if (type == "add") node.dispatchEvent("afteropen"); else if (type == "remove") { if (node.dispatchEvent("afterclose") !== false) callback(); } return; } if (!apf.window.vManager.check(this, "tabscale", visCheck)) return; if (!type) return scalersz.call(this); function btnMoHandler(e) { var pos = apf.getAbsolutePosition(this); if (e.clientX <= pos[0] || e.clientY <= pos[1] || e.clientX >= pos[0] + this.offsetWidth || e.clientY >= pos[1] + this.offsetHeight) { apf.removeListener(_self.$buttons, "mouseout", btnMoHandler); delete _self.$waitForMouseOut; _self.$scaleinit(null, "sync"); // } // else if (_self.$waitForMouseOut) // _self.$waitForMouseOut = 2; } } if (type == "add") { animAddTab(node, function(){ node.dispatchEvent("afteropen"); }); } else if (type == "sync") { scalersz.call(this); } else if (type == "remove") { var onfinish = function(){ if (node.dispatchEvent("afterclose") === false) return; callback(); if (!isLast && isContracted) { var pages = _self.getPages(); for (var i = 0, l = pages.length; i < l; i++) { var page = pages[i]; page.$button.style.minWidth = ""; page.$button.style.maxWidth = ""; } } if (_self.$waitForMouseOut == 2) { apf.removeListener(_self.$buttons, "mouseout", btnMoHandler); delete _self.$waitForMouseOut; } else if (isLast) delete _self.$waitForMouseOut; }; var pages = this.getPages(); var lNode = pages[pages.length - 1]; while (lNode && (!lNode.$button || lNode.$button.style.top)) { lNode = lNode.previousSibling; } if (!lNode || !node.$button) return; var isLast = lNode == node; var isContracted = (node.$button.offsetWidth - apf.getWidthDiff(node.$button) != parseInt(apf.getStyle(node.$button, "maxWidth"))); if (!isLast && isContracted) { for (var i = 0, l = pages.length; i < l; i++) { var page = pages[i]; page.$button.style.minWidth = page.$button.style.maxWidth = (page.$button.offsetWidth - (apf.isGecko ? 0 : apf.getWidthDiff(page.$button))) + "px"; } } var isCur = this.$activepage == node; //Set activetab if the current one is lost if (_self.nextTabInLine) { _self.set(_self.nextTabInLine); delete _self.nextTabInLine; } else if (_self.$activepage == node) { var ln = node.nextSibling; while (ln && (!ln.$first || !ln.visible)) ln = ln.nextSibling; var rn = node.previousSibling; while (rn && (!rn.$last || !rn.visible)) rn = rn.previousSibling; if (ln || rn) _self.set(ln || rn); } if (isCur) { apf.setStyleClass(node.$button, "curbtn"); if (node.$button) node.$button.style.zIndex = 0; } if (pages.length == 1) (node.relPage || node).$ext.style.display = "none"; animRemoveTab(node, isLast, isContracted, onfinish, pages.length == 1); this.$waitForMouseOut = true; if (!isLast) apf.addListener(_self.$buttons, "mouseout", btnMoHandler); } } /* * Update the size of the tab container */ function scalersz(e, excl) { if (!this.length && !this.getPages().length || this.$waitForMouseOut) return; var p = apf.isGecko ? this.$buttons.parentNode : this.$buttons; p.style[apf.CSSPREFIX + "TransitionProperty"] = "padding-right"; p.style[apf.CSSPREFIX + "TransitionDuration"] = this.length < 4 ? ".4s" : ".2s"; p.style[apf.CSSPREFIX + "TimingFunction"] = "ease-out"; if (apf.isGecko) { p.style.paddingRight = apf.getWidthDiff(this.$buttons) + "px"; } else { p.style.paddingRight = ""; } setTimeout(function(){ p.style[apf.CSSPREFIX + "TransitionProperty"] = ""; p.style[apf.CSSPREFIX + "TransitionDuration"] = ""; p.style[apf.CSSPREFIX + "TimingFunction"] = ""; }, 250); } // *** Public methods *** // /** * Retrieves an array of all the page elements of this element. * @returns {Array} An array of all the {apf.page} elements */ this.getPages = function(){ var r = [], nodes = this.childNodes; if (!nodes) return r; for (var i = 0, l = nodes.length; i < l; i++) { if ("page|case".indexOf(nodes[i].localName) > -1 && nodes[i].visible !== false) r.push(nodes[i]); } return r; }; /** * Retrieves a page element by its name or child number. * @param {String | Number} nameOrId The name or child number of the page element to retrieve. * @return {apf.page} The found page element. */ this.getPage = function(nameOrId) { if (apf.isNot(nameOrId)) return this.$activepage; else return this.$findPage(nameOrId); }; /** * Adds a new page element * @param {String} [caption] The text displayed on the button of the page * @param {String} [name] The name of the page which is can be referenced by * @param {String} [type] The type of the page * @param {apf.page} [insertBefore] The page to insert ahead of; `null` means to put it at the end * @param {Function} [callback] A callback to call and pass the new page to * @return {apf.page} The created page element. */ this.add = function(caption, name, type, before, callback) { var page = this.ownerDocument.createElementNS(apf.ns.aml, "page"); if (name) page.setAttribute("id", name); if (type) page.setAttribute("type", type); if (caption) page.setAttribute("caption", caption); if (callback) callback(page); this.insertBefore(page, before); //this.scrollIntoView(page); return page; }; /** * Removes a page element from this element. This function destroys ALL children * of this page. To simple remove the page from the DOM tree, use the * [[apf.AmlNode.removeNode]] method. * * @param {Mixed} nameOrId The name or child number of the page element to remove * @return {apf.page} The removed page element */ this.remove = function(nameOrId, force, noAnimation) { var page = typeof nameOrId == "object" ? nameOrId : this.$findPage(nameOrId); if (!page) return false; var e = {page: page}; if (typeof force == "object") { e.htmlEvent = force; force = false; } if (!force && this.dispatchEvent("close", e) === false) return; if (this.$scale && !noAnimation) { this.$scaleinit(page, "remove", function(){ //page.removeNode(); page.destroy(true, true); }, true); } else { //page.removeNode(); if (page.dispatchEvent("afterclose") !== false) page.destroy(true, true); //@todo this is wrong, we can also use removeChild //this.setScrollerState(); } return page; }; /* var SCROLLANIM = { scrollOn: false, steps: 15, interval: 10, size: 0, left: 0, control: { stop: false }, stopHandle: function() { bAnimating = false; } }, SCROLL_OFF = 0x0001, SCROLL_HOVER = 0x0002, SCROLL_DOWN = 0x0004, SCROLL_DIS = 0x0008, SCROLL_L_STATE = SCROLL_OFF, SCROLL_R_STATE = SCROLL_OFF, SCROLL_LEFT = 0x0001, SCROLL_RIGHT = 0x0002, SCROLL_BOTH = 0x0004, bAnimating = false, scrollTimer = null, keepScrolling = false, globalDir = SCROLL_LEFT; */ function getButtonsWidth() { var cId = "cache_" + this.$buttons.childNodes.length; if (SCROLLANIM[cId]) return SCROLLANIM[cId]; var iWidth = 0; for (var i = 0, l = this.$buttons.childNodes.length; i < l; i++) { if (typeof this.$buttons.childNodes[i].offsetWidth != "undefined") iWidth += this.$buttons.childNodes[i].offsetWidth; } return SCROLLANIM[cId] = iWidth; } function setButtonState(dir, state) { var bBoth = dir & SCROLL_BOTH; if (bBoth) dir = SCROLL_LEFT; var oBtn = this[dir & SCROLL_LEFT ? "oLeftScroll" : "oRightScroll"]; if (!(state & SCROLL_DIS)) { if (dir & SCROLL_LEFT) SCROLL_L_STATE = state; else SCROLL_R_STATE = state; } if (state & SCROLL_OFF) apf.setStyleClass(oBtn, "", ["disabled", "hover", "down"]); else if (state & SCROLL_HOVER) apf.setStyleClass(oBtn, "hover", ["disabled", "down"]); else if (state & SCROLL_DOWN) apf.setStyleClass(oBtn, "down", ["disabled", "hover"]); else if (state & SCROLL_DIS) apf.setStyleClass(oBtn, "disabled", ["hover", "down"]); if (bBoth) setButtonState(SCROLL_RIGHT, state); } /** * Sets the state scroller buttons: `enabled`, `disabled` or completely `hidden`, * depending on the state of the tab buttons * * @param {Boolean} [bOn] Indicates whether to turn the scroll buttons on or off * @param {Number} [iBtns] Specifies the buttons to set the state of. Can be [[apf.BaseTab.SCROLL_LEFT]], [[apf.BaseTab.SCROLL_RIGHT]] or [[apf.BaseTab.SCROLL_BOTH]] * */ this.setScrollerState = function(bOn, iBtns) { if (!this.ready || !this.$hasButtons || !this.oScroller) return; if (typeof bOn == "undefined") { var scrollerWidth = this.oScroller.offsetWidth || parseInt(apf.getStyle(this.oScroller, "width").replace(/(px|em|%)/, "")); bOn = ((getButtonsWidth.call(this) + scrollerWidth) > this.$ext.offsetWidth); iBtns = SCROLL_BOTH; } if (iBtns & SCROLL_BOTH && bOn !== SCROLLANIM.scrollOn) { // in case of HIDING the scroller: check if the anim stuff has reverted SCROLLANIM.scrollOn = bOn; if (!bOn) { this.$buttons.style.left = SCROLLANIM.left + "px"; this.oScroller.style.display = "none"; } //else // TODO: scroll active tab into view if it becomes hidden beneath scroller node(s) } else { this.oScroller.style.display = ""; } this.oScroller.style.display = (iBtns & SCROLL_BOTH && !bOn) ? "none" : ""; if (typeof iBtns == "undefined") iBtns = SCROLL_BOTH; if (!bOn) { if ((iBtns & SCROLL_LEFT) || (iBtns & SCROLL_BOTH)) setButtonState.call(this, SCROLL_LEFT, SCROLL_DIS); if ((iBtns & SCROLL_RIGHT) || (iBtns & SCROLL_BOTH)) setButtonState.call(this, SCROLL_RIGHT, SCROLL_DIS); } }; /** * Corrects the state of the scroller buttons when the state of external * components change, like on a resize event of a window. * */ this.correctScrollState = function() { if (!this.ready || !this.$hasButtons || !this.oScroller) return; this.setScrollerState(); }; /** * Retrieves the utmost left or right boundaries of the tab buttons strip that * can be scrolled to. The tabs cannot scroll any further than these boundaries * * @param {Number} dir Determines which boundary side to look at, either [[apf.BaseTab.SCROLL_LEFT]] or [[apf.BaseTab.SCROLL_RIGHT]] * @param {Boolean} [useCache] Used only when tabs are draggable. Not implemented. * @returns {Number} */ function getAnimationBoundary(dir, useCache) { if (SCROLLANIM.size <= 0) { SCROLLANIM.left = this.$buttons.offsetLeft; SCROLLANIM.size = Math.round(this.firstChild.$button.offsetWidth); } if (dir & SCROLL_LEFT) { return SCROLLANIM.left; } else if (dir & SCROLL_RIGHT) { // TODO: support Drag n Drop of tabs... //if (typeof useCache == "undefined") useCache = false; //if (!tabcontrol.drag) tabcontrol.drag = {}; //if (useCache && tabcontrol.drag.boundCache) // return tabcontrol.drag.boundCache; var oNode = this.$buttons.childNodes[this.$buttons.childNodes.length - 1]; return this.$ext.offsetWidth - (oNode.offsetLeft + oNode.offsetWidth + (this.oScroller.offsetWidth + 4));// used to be tabcontrol.drag.boundCache; } } /** * @event scroll Executed when the user presses one of the two scroll buttons * (left or right). * * If the tab-buttons strip can be scrolled, the * respective behavior is called. * * @param {Event} e An event object, usually a mousedown event from a scroller-button * @param {Number} dir The direction of the scroll, either [[apf.BaseTab.SCROLL_LEFT]] or [[apf.BaseTab.SCROLL_RIGHT]] * */ this.scroll = function(e, dir) { if (!this.ready || !this.$hasButtons || !this.oScroller) return; if (!e) e = window.event; if (typeof e["type"] == "unknown") //scope expired (prolly GC'ed) e = {type: "click"}; if (bAnimating && e.type != "dblclick") return; var bAnimating = true; if (typeof dir == "undefined") dir = SCROLL_LEFT; //apf.tween.clearQueue(this.$buttons, true); var iCurrentLeft = this.$buttons.offsetLeft, size = e["delta"] ? Math.round(e.delta * 36) : SCROLLANIM.size, //get maximum left offset for either direction iBoundary = getAnimationBoundary.call(this, dir), _self = this; if (dir & SCROLL_LEFT) { setButtonState(SCROLL_LEFT, SCROLL_DOWN); setButtonState(SCROLL_RIGHT, SCROLL_OFF); if (iCurrentLeft === iBoundary) { this.setScrollerState(false, SCROLL_LEFT); return apf.tween.single(this.$buttons, { steps: SCROLLANIM.steps, interval: 20, from: iCurrentLeft, to: iCurrentLeft + 12, type: "left", anim: apf.tween.EASEOUT, onstop: SCROLLANIM.stopHandle, onfinish: function(oNode) { apf.tween.single(oNode, { steps: SCROLLANIM.steps, interval: SCROLLANIM.interval, from: iCurrentLeft + 12, to: iCurrentLeft, type: "left", anim: apf.tween.EASEIN, onstop: SCROLLANIM.stopHandle, onfinish: function() { bAnimating = false; if (e.name == "mousescroll") setButtonState(SCROLL_LEFT, SCROLL_OFF); } }); } }); } //one scroll animation scrolls by a SCROLLANIM.size px. var iTargetLeft = iCurrentLeft + (e.type == "dblclick" ? size * 3 : size); if (iTargetLeft > iBoundary) iTargetLeft = iBoundary; if (iTargetLeft === iBoundary) this.setScrollerState(false, SCROLL_LEFT); this.setScrollerState(true, SCROLL_RIGHT); //start animated scroll to the left apf.tween.single(this.$buttons, { steps: SCROLLANIM.steps, interval: SCROLLANIM.interval, control: SCROLLANIM.control, from: iCurrentLeft, to: iTargetLeft, type: "left", anim: apf.tween.NORMAL, onstop: SCROLLANIM.stopHandle, onfinish: function() { bAnimating = false; if (e.name == "mousescroll") setButtonState(SCROLL_LEFT, SCROLL_OFF); if (keepScrolling) _self.scroll(e, globalDir); } }); } else if (dir & SCROLL_RIGHT) { this.setScrollerState(true); setButtonState(SCROLL_RIGHT, SCROLL_DOWN); setButtonState(SCROLL_LEFT, SCROLL_OFF); if (iCurrentLeft === iBoundary) { this.setScrollerState(false, SCROLL_RIGHT); return apf.tween.single(this.$buttons, { steps: SCROLLANIM.steps, interval: 20, from: iCurrentLeft, to: iCurrentLeft - 24, type: "left", anim: apf.tween.EASEOUT, onstop: SCROLLANIM.stopHandle, onfinish: function(oNode, options) { apf.tween.single(oNode, { steps: SCROLLANIM.steps, interval: SCROLLANIM.interval, from: iCurrentLeft - 24, to: iCurrentLeft, type: "left", anim: apf.tween.EASEIN, onstop: SCROLLANIM.stopHandle, onfinish: function() { bAnimating = false; if (e.name == "mousescroll") setButtonState(SCROLL_RIGHT, SCROLL_OFF); } }); } }); } //one scroll animation scrolls by a SCROLLANIM.size px. var iTargetLeft = iCurrentLeft - (e.type == "dblclick" ? size * 3 : size); //make sure we don't scroll more to the right than the //maximum left: if (iTargetLeft < iBoundary) iTargetLeft = iBoundary; //start animated scroll to the right apf.tween.single(this.$buttons, { steps: SCROLLANIM.steps, interval: SCROLLANIM.interval, control: SCROLLANIM.control, from: iCurrentLeft, to: iTargetLeft, type: "left", anim: apf.tween.NORMAL, onstop: SCROLLANIM.stopHandle, onfinish: function() { bAnimating = false; if (e.name == "mousescroll") setButtonState(SCROLL_RIGHT, SCROLL_OFF); if (keepScrolling) _self.scroll(e, globalDir); } }); } }; /** * If a tab page is outside of the user's view, this function scrolls that * tabpage into view smoothly. * * @param {apf.page} oPage The page to scroll into view * */ this.scrollIntoView = function(oPage) { bAnimating = false; if (!this.ready || !this.$hasButtons || !this.oScroller || !oPage.$drawn) return; bAnimating = true; if (this.$buttons.offsetWidth < this.$ext.offsetWidth) return this.setScrollerState(false); var iTabLeft = oPage.$button.offsetLeft, iTabWidth = oPage.$button.offsetWidth, iCurrentLeft = this.$buttons.offsetLeft; if (SCROLLANIM.size <= 0) { SCROLLANIM.left = this.$buttons.offsetLeft; var p = this.firstChild; while (!p.$button) p = p.nextSibling; SCROLLANIM.size = Math.round(p.$button.offsetWidth); } this.$buttons.style.left = iCurrentLeft; var iRealWidth = this.$ext.offsetWidth, iScrollCorr = this.oScroller.offsetWidth + 4, iTargetLeft = null, dir; if ((iTabLeft + iTabWidth) > ((iRealWidth - iScrollCorr) - iCurrentLeft)) { //scroll to the right iTargetLeft = (-(iTabLeft - SCROLLANIM.left) + (iRealWidth - iTabWidth - iScrollCorr)); dir = SCROLL_RIGHT; } else if ((iCurrentLeft + iTabLeft) < SCROLLANIM.left) { //sroll to the left iTargetLeft = SCROLLANIM.left - iTabLeft; dir = SCROLL_LEFT; } if (iTargetLeft !== null) { this.setScrollerState(true); setButtonState(SCROLL_RIGHT, dir & SCROLL_RIGHT ? SCROLL_DOWN : SCROLL_OFF); setButtonState(SCROLL_LEFT, dir & SCROLL_LEFT ? SCROLL_DOWN : SCROLL_OFF); apf.tween.clearQueue(this.$buttons, true); apf.tween.single(this.$buttons, { steps: SCROLLANIM.steps, interval: SCROLLANIM.interval, from: iCurrentLeft, to: iTargetLeft, type: "left", anim: apf.tween.NORMAL, onstop: SCROLLANIM.stopHandle, onfinish: function() { bAnimating = false; setButtonState(SCROLL_RIGHT, SCROLL_OFF); setButtonState(SCROLL_LEFT, SCROLL_OFF); } }); } else bAnimating = false; }; // *** DOM Hooks *** // this.addEventListener("DOMNodeRemoved", function(e) { var amlNode = e.currentTarget; if (e.$doOnlyAdmin || e.relatedNode != this || amlNode.localName != "page") return; if ((this.activepage || this.activepage == 0) && this.activepage != -1) { if (!this.getPage(this.nextTabInLine)) this.nextTabInLine = null; if (this.nextTabInLine) this.set(this.nextTabInLine); if (!this.nextTabInLine && this.$activepage == amlNode) { var ln = amlNode.nextSibling; while (ln && (!ln.$first || !ln.visible)) ln = ln.nextSibling; var rn = amlNode.previousSibling; while (rn && (!rn.$last || !rn.visible)) rn = rn.previousSibling; if (this.firstChild == amlNode && ln) ln && ln.$first(); if (this.lastChild == amlNode && rn) rn && rn.$last(); if (ln || rn) this.set(ln || rn); else { amlNode.$deactivate(); //this.setScrollerState(); this.$activepage = this.activepage = this.activepagenr = null; this.setProperty("activepage", null); } } else { //if (this.$scroll) //this.setScrollerState(); if (this.$scale) this.$scaleinit(); } delete this.nextTabInLine; } this.setProperty("length", this.getPages().length - 1); }); this.addEventListener("DOMNodeInserted",function(e) { var amlNode = e.currentTarget; if (amlNode.localName != "page" || e.relatedNode != this || amlNode.nodeType != 1) return; var pages = this.getPages(); if (!e.$beforeNode) { var lastChild, pg = pages; if (lastChild = pg[pg.length - 2]) lastChild.$last(true); amlNode.$last(); } var p2, p = pages[0]; //@todo $beforeNode doesnt have to be a page if (amlNode == p) { if (p2 = this.getPage(1)) p2.$first(true); amlNode.$first(); } if (this.$activepage) { var info = {}; this.$findPage(this.$activepage, info); if (this.activepagenr != info.position) { if (parseInt(this.activepage) == this.activepage) { this.activepage = info.position; this.setProperty("activepage", info.position); } this.activepagenr = info.position; this.setProperty("activepagenr", info.position); } } else if (!this.activepage && !this.$activepage && !amlNode.render || amlNode.$rendered) { this.set(amlNode); } if (this.$scale && amlNode.visible && !e.$isMoveWithinParent) this.$scaleinit(amlNode, "add"); else { amlNode.dispatchEvent("afteropen"); } this.setProperty("length", this.getPages().length); }); // *** Private state handling functions *** // this.$findPage = function(nameOrId, info) { var node, nodes = this.childNodes; if (!nodes) return; if (nameOrId.localName) { for (var t = 0, i = 0, l = nodes.length; i < l; i++) { node = nodes[i]; if ("page|case".indexOf(node.localName) > -1 && (++t) && node == nameOrId) { if (info) info.position = t - 1; return node; } } } else { for (var t = 0, i = 0, l = nodes.length; i < l; i++) { node = nodes[i]; if ("page|case".indexOf(node.localName) > -1 && (t++ == nameOrId || node.name == nameOrId)) { if (info) info.position = t - 1; return node; } } } return null; }; this.$enable = function(){ var nodes = this.childNodes; for (var i = 0, l = nodes.length; i < l; i++) { if (nodes[i].enable) nodes[i].enable(); } }; this.$disable = function(){ var nodes = this.childNodes; for (var i = 0, l = nodes.length; i < l; i++) { if (nodes[i].disable) nodes[i].disable(); } }; // *** Keyboard support *** // // *** Init *** // this.$loadChildren = function(callback) { var page = false, _self = this, i, j, l, node, nodes; this.inited = true; if (this.$hasButtons) { this.$buttons = this.$getLayoutNode("main", "buttons", this.$ext); this.$buttons.setAttribute("id", this.$uniqueId + "_buttons"); if (false && apf.isGecko && !this.$gotContainer) { var div = this.$ext.appendChild(document.createElement("div")); div.style.backgroundImage = apf.getStyle(this.$buttons, "backgroundImage"); div.style.backgroundColor = apf.getStyle(this.$buttons, "backgroundColor"); div.style.position = "absolute"; div.style.left = 0; div.style.top = 0; div.style.right = 0; div.style.overflow = "hidden"; div.style.height = this.$buttons.offsetHeight + "px"; div.appendChild(this.$buttons); this.$buttons.style.width = "100%"; div.style.paddingRight = apf.getWidthDiff(this.$buttons) + "px"; this.$gotContainer = true; } } this.oPages = this.$getLayoutNode("main", "pages", this.$ext); // add scroller node(s) /*this.oScroller = this.$getLayoutNode("main", "scroller", this.oPages); if (this.oScroller) { function startTimer(e, dir) { clearTimeout(scrollTimer); globalDir = dir; scrollTimer = $setTimeout(function() { keepScrolling = true; _self.scroll(e, dir); }, 500); } function stopTimer() { clearTimeout(scrollTimer); keepScrolling = false; } this.oScroller.onmouseout = function(e) { SCROLLANIM.control.stop = true; setButtonState(SCROLL_BOTH, SCROLL_OFF); }; /*apf.addEventListener("mousescroll", function(e) { var found = (e.target == _self.$buttons); while (!found && e.target != document.body) { e.target = e.target.offsetParent; found = (e.target == _self.$buttons); } if (!found) return; var dir = e.delta > 0 ? SCROLL_LEFT : SCROLL_RIGHT; e.delta = Math.abs(e.delta); _self.scroll(e, dir); });* / this.oLeftScroll = apf.getNode(this.oScroller, [0]); this.oRightScroll = apf.getNode(this.oScroller, [1]); ["oLeftScroll", "oRightScroll"].forEach(function(sBtn) { var dir = sBtn == "oLeftScroll" ? SCROLL_LEFT : SCROLL_RIGHT, revDir = sBtn == "oLeftScroll" ? SCROLL_RIGHT : SCROLL_LEFT; _self[sBtn].ondbclick = _self[sBtn].onmousedown = function(e) { SCROLLANIM.control.stop = false; var state = dir & SCROLL_LEFT ? SCROLL_L_STATE : SCROLL_R_STATE; if (this.className.indexOf("disabled") != -1 || state & SCROLL_DOWN) return; e = e || event; _self.scroll(e, dir); startTimer(e, dir); if (!apf.isSafariOld) this.onmouseout(); }; _self[sBtn].onmouseover = function() { SCROLLANIM.control.stop = false; var state = dir & SCROLL_LEFT ? SCROLL_L_STATE : SCROLL_R_STATE; if (this.className.indexOf("disabled") != -1 || state & SCROLL_DOWN) return; setButtonState(dir, SCROLL_HOVER); setButtonState(revDir, SCROLL_OFF); globalDir = dir; }; _self[sBtn].onmouseout = function() { var state = dir & SCROLL_LEFT ? SCROLL_L_STATE : SCROLL_R_STATE; if (this.className.indexOf("disabled") != -1 || state & SCROLL_DOWN) return; setButtonState(dir, SCROLL_OFF); }; _self[sBtn].onmouseup = function() { if (this.className.indexOf("disabled") == -1) { setButtonState(dir, SCROLL_OFF); } stopTimer(); SCROLLANIM.control.stop = true; }; }); } apf.layout.setRules(this.$ext, this.$uniqueId + "_tabscroller", "var o = apf.all[" + this.$uniqueId + "]; o && o.correctScrollState()"); apf.layout.queue(this.$ext);*/ //Skin changing support if (this.$int) { //apf.AmlParser.replaceNode(this.oPages, oPages); this.$int = this.oPages; page = true; //@todo apf3.0 skin change? nodes = this.childNodes; for (i = 0; i < nodes.length; i++) { node = nodes[i]; if (node.nodeType != 1) continue; node.$draw(true); if (node.$skinchange) node.$skinchange(); node.$loadAml(); } } else { this.$int = this.oPages; //Build children nodes = this.getPages(); if (nodes.length) { nodes[0].$first(); (node = nodes[nodes.length - 1]).$last(); } } //Set active page if (node) { this.activepage = (typeof this.activepage != "undefined" ? this.activepage : this.activepagenr) || 0; page = this.getPage(this.activepage); if (!page.render || page.$rendered) this.$propHandlers.activepage.call(this, this.activepage); } else { this.isPages = false; } this.setProperty("length", this.getPages().length); this.ready = true; /*window.setTimeout(function() { _self.setScrollerState(); }, 0);*/ if (!this.activepage && this.getAttribute("src")) { this.src = this.getAttribute("src"); this.$propHandlers["activepage"].call(this); } }; this.$destroy = function(bSkinChange) { if (bSkinChange || !this.oScroller) return; /*apf.layout.removeRule(this.$ext, this.$uniqueId + "_tabscroller"); [this.oLeftScroll, this.oRightScroll].forEach(function(oBtn) { oBtn.onmousedown = oBtn.ondblclick = oBtn.onmouseover = oBtn.onmouseout = oBtn.onmouseup = null; });*/ }; }).call(apf.BaseTab.prototype = new apf.Presentation()); /** * An element displaying a page and several buttons allowing a * user to switch between the pages. Each page can contain * arbitrary AML. Each page can render its content during * startup of the application, or when the page is activated. * * #### Example * * ```xml, demo * * * * * Example * Example * * * Test checkbox * Test checkbox * Test checkbox * * * This ok? * This better? * * * * * ``` * * @class apf.tab * @define tab * @container * @allowchild page * * * @author Ruben Daniels (ruben AT ajax DOT org) * @version %I%, %G% * @since 0.1 * * @inherits apf.BaseTab */ apf["switch"] = function(struct, tagName) { this.$hasButtons = false; this.$init(tagName || "switch", apf.NODE_VISIBLE, struct); }; apf.pages = function(struct, tagName) { this.$hasButtons = false; this.$init(tagName || "pages", apf.NODE_VISIBLE, struct); this.$focussable = false; }; apf.tab = function(struct, tagName) { this.$hasButtons = true; this.$init(tagName || "tab", apf.NODE_VISIBLE, struct); }; (function(){ this.$focussable = apf.KEYBOARD; // This object can get the focus from the keyboard // *** Init *** // this.$draw = function(bSkinChange) { //Build Main Skin this.$ext = this.$getExternal(); this.$loadChildren(); }; }).call(apf.tab.prototype = new apf.BaseTab()); apf["switch"].prototype = apf.pages.prototype = apf.tab.prototype; apf.aml.setElement("switch", apf["switch"]); apf.aml.setElement("pages", apf.pages); apf.aml.setElement("tab", apf.tab); }; });