kopia lustrzana https://github.com/c9/core
2173 wiersze
72 KiB
JavaScript
2173 wiersze
72 KiB
JavaScript
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
|
|
* <a:application xmlns:a="http://ajax.org/2005/aml">
|
|
* <!-- startcontent -->
|
|
* <a:window
|
|
* visible = "true"
|
|
* width = "400"
|
|
* height = "150"
|
|
* title = "Simple Tab" >
|
|
* <a:tab anchors="10 10 10 10">
|
|
* <a:page caption="General">
|
|
* <a:checkbox>Example</a:checkbox>
|
|
* <a:button>Example</a:button>
|
|
* </a:page>
|
|
* <a:page caption="Advanced">
|
|
* <a:checkbox>Test checkbox</a:checkbox>
|
|
* <a:checkbox>Test checkbox</a:checkbox>
|
|
* <a:checkbox>Test checkbox</a:checkbox>
|
|
* </a:page>
|
|
* <a:page caption="Ajax.org">
|
|
* <a:checkbox>This ok?</a:checkbox>
|
|
* <a:checkbox>This better?</a:checkbox>
|
|
* </a:page>
|
|
* </a:tab>
|
|
* </a:window>
|
|
* <!-- endcontent -->
|
|
* </a:application>
|
|
* ```
|
|
*
|
|
* @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
|
|
* <a:dropdown id="ddMenu" value="0">
|
|
* <a:item value="0">Home</a:item>
|
|
* <a:item value="1">General</a:item>
|
|
* <a:item value="2">Advanced</a:item>
|
|
* </a:dropdown>
|
|
*
|
|
* <a:pages activepagenr="{ddMenu.value}">
|
|
* <a:page>
|
|
* <h1>Home Page</h1>
|
|
* </a:page>
|
|
* <a:page>
|
|
* <h1>General Page</h1>
|
|
* </a:page>
|
|
* <a:page>
|
|
* <h1>Advanced Page</h1>
|
|
* </a:page>
|
|
* </a:pages>
|
|
* ```
|
|
*/
|
|
this.$propHandlers["activepagenr"] =
|
|
|
|
/**
|
|
* @attribute {String} activepage Sets or gets the name of the active page.
|
|
*
|
|
* #### Example
|
|
*
|
|
* ```xml
|
|
* <a:tab activepage="general" width="250" height="100">
|
|
* <a:page id="home" caption="Home">
|
|
* ...
|
|
* </a:page>
|
|
* <a:page id="advanced" caption="Advanced">
|
|
* ...
|
|
* </a:page>
|
|
* <a:page id="general" caption="General">
|
|
* ...
|
|
* </a:page>
|
|
* </a:tab>
|
|
* ```
|
|
*/
|
|
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 (!(node.relPage || node).$ext) return onfinish();
|
|
|
|
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
|
|
* <a:application xmlns:a="http://ajax.org/2005/aml">
|
|
* <!-- startcontent -->
|
|
* <a:tab id="tab" width="300" height="100">
|
|
* <a:page caption="General">
|
|
* <a:checkbox>Example</a:checkbox>
|
|
* <a:button>Example</a:button>
|
|
* </a:page>
|
|
* <a:page caption="Advanced">
|
|
* <a:checkbox>Test checkbox</a:checkbox>
|
|
* <a:checkbox>Test checkbox</a:checkbox>
|
|
* <a:checkbox>Test checkbox</a:checkbox>
|
|
* </a:page>
|
|
* <a:page caption="Ajax.org">
|
|
* <a:checkbox>This ok?</a:checkbox>
|
|
* <a:checkbox>This better?</a:checkbox>
|
|
* </a:page>
|
|
* </a:tab>
|
|
* <!-- endcontent -->
|
|
* </a:application>
|
|
* ```
|
|
*
|
|
* @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);
|
|
|
|
|
|
|
|
};
|
|
}); |