/*
* This file is part of Toolkit.
*
* Toolkit is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Toolkit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
/**
* The <code>useraction</code> event is emitted when a widget gets modified by user interaction.
* The event is emitted for the option <code>show</code>.
*
* @event TK.Pager#useraction
*
* @param {string} name - The name of the option which was changed due to the users action
* @param {mixed} value - The new value of the option
*/
"use strict";
(function(w, TK){
TK.Pager = TK.class({
/**
* TK.Pager, also known as Notebook in other UI toolkits, provides
* multiple containers for displaying contents which are switchable
* via a {@link TK.ButtonArray}.
*
* @class TK.Pager
*
* @param {Object} [options={ }] - An object containing initial options.
*
* @property {String} [options.position="top"] - The position of the ButtonArray. Can either be `top`, `right`, `left` or `bottom`.
* @property {Array<Object>} [options.pages=[]] -
* An array of objects with the following members:
* @property {String|Object} [options.pages.label=""] - A string
* used as the buttons label or an object containing options for
* a {@link TK.Button}.
* @property {TK.Container|DOMNode|String} [options.pages.content]
* - The content of the page. Can be
* either an instance of {@link TK.Container} (or derivate),
* a DOMNode or a string which gets wrapped in a new {@link TK.Container}.
* @property {Integer} [options.show=-1] - The page to show.
*
* @extends TK.Container
*
* @example
* var pager = new TK.Pager({
* pages: [
* {
* label: "Empty Page 1",
* content: document.createElement("span")
* },
* {
* label: { label:"Foobar", class:"foobar" },
* content: "<h1>Foobar</h1><p>Lorem ipsum dolor sit amet</p>"
* }
* ]
* });
*/
_class: "Pager",
Extends: TK.Container,
_options: Object.assign(Object.create(TK.Container.prototype._options), {
position: "string",
direction: "string",
pages: "array",
show: "int",
resized: "boolean",
}),
options: {
position: "top",
direction: "forward",
pages: [],
show: -1,
resized: false,
},
static_events: {
set_show: function(value) {
var page = this.current();
if (page) {
page.set("active", true);
this.show_child(page);
/**
* The page to show has changed.
*
* @param {TK.Container} page - The {@link TK.Container} instance of the newly selected page.
* @param {number} id - The ID of the page.
*
* @event TK.Pager#changed
*/
this.fire_event("changed", page, value);
}
},
set_pages: function(value) {
for (var i = 0; i < this.pages.length; i++)
this.pages[i].destroy();
this.pages = [];
this.add_pages(value);
},
set_position: function(value) {
var badir;
if (value === "top" || value === "bottom") {
badir = "horizontal";
} else {
badir = "vertical";
}
this.buttonarray.set("direction", badir);
},
},
initialize: function (options) {
this.pages = [];
TK.Container.prototype.initialize.call(this, options);
/**
* The main DIV element. Has the class <code>toolkit-pager</code>.
*
* @member TK.Pager#element
*/
/**
* @member {HTMLDivElement} TK.Pager#_buttonarray_wrapper - An internal container for layout purposes containing the #TK.ButtonArray.
* Has classes <code>toolkit-buttonarray-wrapper</code> and <code>toolkit-wrapper</code>.
*/
/**
* @member {HTMLDivElement} TK.Pager#_container_wrapper - An internal container for layout purposes containing the _clip element.
* Has classes <code>toolkit-wrapper</code> and <code>toolkit-container-wrapper</code>.
*/
/**
* @member {HTMLDivElement} TK.Pager#_clip - The clipping area containing the pages.
* Has class <code>toolkit-clip</code>.
*/
TK.add_class(this.element, "toolkit-pager");
/**
* The {@link TK.ButtonArray} instance acting as the menu.
*
* @member TK.Pager#buttonarray
*/
this.buttonarray = new TK.ButtonArray({
container: this.element,
});
this.buttonarray.add_event("userset", function(key, value) {
this.parent.userset(key, value);
return false;
});
var b_o = this.buttonarray._options;
var _o = this._options;
for (var k in b_o) {
if (!b_o.hasOwnProperty(k)) continue;
var o = "buttonarray." + k;
_o[o] = b_o[k];
this.add_event("set_" + o, (function (t,k) {
return function (v) {
t.buttonarray.set(k, v);
}
})(this, k));
if (typeof this.options[o] !== "undefined")
this.set(o, this.options[o]);
}
/**
* @member {HTMLDivElement} TK.Pager#_clip - The clipping of the pages.
* Has class <code>toolkit-clip</code>.
*/
this._clip = TK.element("div", "toolkit-clip");
this.element.appendChild(this._clip);
this.add_child(this.buttonarray);
this.add_pages(this.options.pages);
this.set("position", this.options.position);
this.set("show", this.options.show);
},
redraw: function () {
TK.Container.prototype.redraw.call(this);
var O = this.options;
var I = this.invalid;
var E = this.element;
if (I.overlap)
TK[(O.overlap ? "add_" : "remove_") + "class"](E, "toolkit-overlap");
if (I.direction) {
I.direction = false;
TK.remove_class(E, "toolkit-forward", "toolkit-backward");
TK.add_class(E, "toolkit-" + O.direction);
}
if (I.position) {
I.position = false;
TK.remove_class(E, "toolkit-top", "toolkit-right", "toolkit-bottom",
"toolkit-left", "toolkit-vertical", "toolkit-horizontal");
switch (O.position) {
case "top":
TK.add_class(E, "toolkit-top", "toolkit-vertical");
break;
case "bottom":
TK.add_class(E, "toolkit-bottom", "toolkit-vertical");
break;
case "left":
TK.add_class(E, "toolkit-left", "toolkit-horizontal");
break;
case "right":
TK.add_class(E, "toolkit-right", "toolkit-horizontal");
break;
default:
TK.warn("Unsupported position", O.position);
}
I.layout = true;
}
if (I.show) {
I.show = false;
for (var i = 0; i < this.pages.length; i ++) {
var page = this.pages[i];
if (i === O.show)
page.add_class("toolkit-active");
else
page.remove_class("toolkit-active");
}
}
},
/**
* Adds an array of pages.
*
* @method TK.Pager#add_pages
*
* @param {Array<Object>} options - An Array of objects with members
* `label` and `content`. `label` is a string with the {@link TK.Button}s label or
* an object containing options for the {@link TK.Button} instance.
* `content` is either a {@link TK.Container} (or derivate) widget,
* a DOMNode (needs option `options` to be set) or a string which
* gets wrapped in a new {@link TK.Container} with options from
* argument `options`.
*
* @example
* var p = new TK.Pager();
* p.add_pages([
* {
* label: "Page 1",
* content: "<h1>Page1</h1>",
* }
* ]);
*
*/
add_pages: function (options) {
for (var i = 0; i < options.length; i++)
this.add_page(options[i].label, options[i].content);
},
/**
* Adds a {@link TK.Container} to the pager and a corresponding {@link TK.Button}
* to the pagers {@link TK.ButtonArray}.
*
* @method TK.Pager#add_page
*
* @param {string|Object} button - A string with the {@link TK.Button}s label or
* an object containing options for the {@link TK.Button} instance.
* @param {TK.Widget|Class|string} content - The content of the page.
* Either a {@link TK.Container} (or derivate) widget,
* a DOMNode (needs option `options` to be set) or a string which
* gets wrapped in a new {@link TK.Container} with options from
* argument `options`.
* @param {Object} [options={ }] - An object containing options for
* the {@link TK.Container} to be added as page if `content` is
* either a string or a DOMNode.
* @param {integer|undefined} position - The position to add the new
* page to. If undefined, the page is added at the end.
* @emits TK.Pager#added
*/
add_page: function (button, content, position, options) {
var p;
if (typeof button === "string")
button = {label: button};
this.buttonarray.add_button(button, position);
if (typeof content === "string" || TK.is_dom_node(content)) {
if (!options) options = {};
options.content = content;
p = new TK.Container(options);
} else if (typeof content === "function") {
// assume here content is a subclass of Container
p = new content(options);
} else {
p = content;
}
p.add_class("toolkit-page");
p.set("container", this._clip);
var len = this.pages.length;
if (position >= 0 && position < len - 1) {
this.pages.splice(position, 0, p);
this._clip.insertBefore(p.element, this._clip.childNodes[position]);
} else {
position = len;
this.pages.push(p);
this._clip.appendChild(p.element);
}
/**
* A page was added to the TK.Pager.
*
* @event TK.Pager#added
*
* @param {TK.Container} page - The {@link TK.Container} which was added as a page.
*/
this.fire_event("added", p);
this.add_child(p);
// TODO: not always necessary
if (this.current() === p) {
this.options.show = position;
this.buttonarray.set("show", position);
p.set("active", true);
p.set("display_state", "show");
} else {
/* do not use animation */
p.force_hide();
this.hide_child(p);
}
this.invalid.layout = true;
this.trigger_draw();
return p;
},
/**
* Removes a page from the TK.Pager.
*
* @method TK.Pager#remove_page
*
* @param {integer|TK.Container} page - The container to remove. Either a
* position or the {@link TK.Container} widget generated by <code>add_page</code>.
*
* @emits TK.Pager#removed
*/
remove_page: function (page) {
if (typeof page === "object")
page = this.pages.indexOf(page);
if (page < 0 || page >= this.pages.length)
return;
this.buttonarray.remove_button(page);
if (page < this.options.show)
this.set("show", this.options.show-1);
else if (page === this.options.show)
this.set("show", this.options.show);
var p = this.pages[page];
this.pages.splice(page, 1);
p.destroy();
this.remove_child(p);
this.invalid.layout = true;
this.trigger_draw();
/**
* A page was removed from the Pager
*
* @event TK.Pager#removed
*
* @param {TK.Container} page - The {@link TK.Container} which was removed.
*/
this.fire_event("removed", p);
},
current: function() {
/**
* Returns the currently displayed page or null.
*
* @method TK.Pager#current
*/
var n = this.options.show;
if (n >= 0 && n < this.pages.length) {
return this.pages[n];
}
return null;
},
/**
* Opens the first page of the pager. Returns <code>true</code> if a
* first page exists, <code>false</code> otherwise.
*
* @method TK.Pager#first
*/
first: function() {
if (this.pages.length) {
this.set("show", 0);
return true;
}
return false;
},
/**
* Opens the last page of the pager. Returns <code>true</code> if a
* last page exists, <code>false</code> otherwise.
*
* @method TK.Pager#last
*/
last: function() {
if (this.pages.length) {
this.set("show", this.pages.length-1);
return true;
}
return false;
},
/**
* Opens the next page of the pager. Returns <code>true</code> if a
* next page exists, <code>false</code> otherwise.
*
* @method TK.Pager#next
*/
next: function() {
var c = this.options.show;
return this.set("show", c+1) !== c;
},
/**
* Opens the previous page of the pager. Returns <code>true</code> if a
* previous page exists, <code>false</code> otherwise.
*
* @method TK.Pager#prev
*/
prev: function() {
var c = this.options.show;
return this.set("show", c-1) !== c;
},
set: function (key, value) {
var page;
if (key === "show") {
if (value < 0) value = 0;
else if (value >= this.pages.length) value = this.pages.length - 1;
if (value === this.options.show) return value;
if (value > this.options.show) {
this.set("direction", "forward");
} else {
this.set("direction", "backward");
}
page = this.current();
if (page) {
this.hide_child(page);
page.set("active", false);
}
this.buttonarray.set("show", value);
}
return TK.Container.prototype.set.call(this, key, value);
},
get: function (key) {
if (key === "pages") return this.pages;
return TK.Container.prototype.get.call(this, key);
}
});
})(this, this.TK);