c9-core/plugins/c9.ide.ui/lib/page.js

2173 wiersze
72 KiB
JavaScript
Czysty Zwykły widok Historia

2015-02-10 19:41:24 +00:00
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;
}
2015-11-04 20:51:20 +00:00
if (!(node.relPage || node).$ext) return onfinish();
2015-02-10 19:41:24 +00:00
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);
};
});