/*
* 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
*/
"use strict";
(function(w, TK){
/* Abstract toggle logic */
function reset_delay_to () {
window.clearTimeout(this.__delayed_to);
this.__delayed_to = -1;
this.remove_class("toolkit-delayed");
}
function toggle(O) {
if (this.userset("state", !O.state) === false) return;
this.fire_event("toggled", O.state);
}
function press_start() {
var O = this.options;
this.__press_start_time = Date.now();
if (O.delay && this.__delayed_to < 0) {
this.__delayed_to = window.setTimeout((function (t) {
return function () { press_start.call(t); }
})(this), O.delay);
this.add_class("toolkit-delayed");
return;
}
this.remove_class("toolkit-delayed");
if (O.delay && this.__delayed_to >= 0) {
toggle.call(this, O);
}
if (O.press) toggle.call(this, O);
}
function press_end() {
var O = this.options;
if (O.delay && this.__delayed_to >= 0) {
reset_delay_to.call(this);
return;
}
var t = Date.now() - this.__press_start_time;
if ((O.toggle && (!O.press || t > O.press)) || (!O.toggle && O.press)) {
toggle.call(this, O);
}
}
function press_cancel() {
var O = this.options;
if (O.delay && this.__delayed_to >= 0) {
reset_delay_to.call(this);
return;
}
/* this is definitely not a click, its a cancel by leaving the
* button with mouse or finger while pressing */
if (O.press) toggle.call(this, O);
}
/* MOUSE handling */
function mouseup(e) {
this.remove_event("mouseup", mouseup);
this.remove_event("mouseleave", mouseleave);
press_end.call(this);
}
function mouseleave(e) {
this.remove_event("mouseup", mouseup);
this.remove_event("mouseleave", mouseleave);
press_cancel.call(this);
}
function mousedown(e) {
/* only left click please */
if (e.button) return true;
press_start.call(this);
this.add_event("mouseup", mouseup);
this.add_event("mouseleave", mouseleave);
}
/* TOUCH handling */
function is_current_touch(ev) {
var id = this.__touch_id;
var i;
for (i = 0; i < ev.changedTouches.length; i++) {
if (ev.changedTouches[i].identifier === id) {
return true;
}
}
return false;
}
function touchend(e) {
if (!is_current_touch.call(this, e)) return;
this.__touch_id = false;
e.preventDefault();
press_end.call(this);
this.remove_event("touchend", touchend);
this.remove_event("touchcancel", touchleave);
this.remove_event("touchleave", touchleave);
}
function touchleave(e) {
if (!is_current_touch.call(this, e)) return;
this.__touch_id = false;
e.preventDefault();
press_cancel.call(this);
this.remove_event("touchend", touchend);
this.remove_event("touchcancel", touchleave);
this.remove_event("touchleave", touchleave);
}
function touchstart(e) {
if (this.__touch_id !== false) return;
this.__touch_id = e.targetTouches[0].identifier;
press_start.call(this);
this.add_event("touchend", touchend);
this.add_event("touchcancel", touchleave);
this.add_event("touchleave", touchleave);
e.preventDefault();
e.stopPropagation();
return false;
}
function contextmenu(e) {
e.preventDefault();
e.stopPropagation();
return false;
}
var class_regex = /[^A-Za-z0-9_\-]/;
function is_class_name (str) {
return !class_regex.test(str);
}
TK.Toggle = TK.class({
/**
* A toggle button. The toggle button can either be pressed (which means that it will
* switch its state as long as it is pressed) or toggled permanently. Its behavior is
* controlled by the two options <code>press</code> and <code>toggle</code>.
*
* @class TK.Toggle
*
* @extends TK.Button
*
* @param {Object} [options={ }] - An object containing initial options.
*
* @property {Boolean} [options.toggle=true] - If true, the button is toggled on click.
* @property {Integer} [options.press=0] - Controls press behavior. If <code>options.toggle</code>
* is <code>false</code> and this option is <code>0</code>, the toggle button will toggle until
* released. If <code>options.toggle</code> is true and this option is a positive integer, it is
* interpreted as a milliseconds timeout. When pressing a button longer than this timeout, it will
* be toggled until released, otherwise it will be toggled permanently.
* @property {Integer} [options.delay=0] - Delay all actions for n milliseconds. While actions are
* delayed, the widget has class <code>toolkit-delayed</code>. Use to force users to press the button
* for a certain amount of time before it actually gets toggled.
* @property {String|Boolean} [options.icon_active=false] - An optional icon which is only displayed
* when the button toggle state is <code>true</code>. Please note that this option only works if `icon` is also set.
* @property {String|Boolean} [options.label_active=false] - An optional label which is only displayed
* when the button toggle state is <code>true</code>. Please note that this option only works if `label` is also set.
*/
_class: "Toggle",
Extends: TK.Button,
_options: Object.assign(Object.create(TK.Button.prototype._options), {
label_active: "string",
icon_active: "string",
press: "int",
delay: "int",
toggle: "boolean",
}),
options: {
label_active: false,
icon_active: false,
icon_inactive: false,
press: 0,
delay: 0,
toggle: true,
},
static_events: {
mousedown: mousedown,
touchstart: touchstart,
contextmenu: contextmenu,
},
initialize: function (options) {
TK.Button.prototype.initialize.call(this, options);
/**
* @member {HTMLDivElement} TK.Toggle#element - The main DIV container.
* Has class <code>toolkit-toggle</code>.
*/
TK.add_class(this.element, "toolkit-toggle");
this.__press_start_time = 0;
this.__touch_id = false;
this.__delayed_to = -1;
},
redraw: function () {
var O = this.options;
var I = this.invalid;
if (I.state) {
var tmp = (O.state && O.label_active) || O.label;
if (tmp)
this.label.set("label", tmp || "");
tmp = (O.state && O.icon_active) || O.icon;
if (tmp)
this.icon.set("icon", tmp || "");
}
TK.Button.prototype.redraw.call(this);
},
/**
* Toggle the button state.
*
* @method TK.Toggle#toggle
*
* @emits TK.Toggle#toggled
*/
toggle: function () {
toggle.call(this, this.options);
/**
* Is fired when the button was toggled.
*
* @event TK.Toggle#toggled
*
* @param {boolean} state - The state of the {@link TK.Toggle}.
*/
},
});
})(this, this.TK);