JavaXT
|
|
Button Classif(!javaxt) var javaxt={}; if(!javaxt.dhtml) javaxt.dhtml={}; //****************************************************************************** //** Button Class //****************************************************************************** /** * Custom button control. The button has 3 parts: icon, label, and an arrow. * ******************************************************************************/ javaxt.dhtml.Button = function(parent, config) { this.className = "javaxt.dhtml.Button"; var me = this; var defaultConfig = { /** Text label for the button. */ label: null, /** If true, the button will initially appear in a selected state and the * isSelected() method will return true. Default is false. */ selected: false, /** If true, the button will initially appear disabled and the * isDisabled() method will return true. Default is false. */ disabled: false, /** If true, the button will be rendered as a toggle button. Default is * false, unless the button has a menu. */ toggle: false, /** If true, will create a drop-down menu for the button. Components such * as buttons or custom DOM elements can be added directly to the menu. * See getMenuPanel() for more information. */ menu: false, /** Sets the position of the menu panel. Options are "bottom" or "right". */ menuAlign: "bottom", /** Used to set the "display" style attribute for the outer DOM element. * This is not commonly ued. Default is "inline-block". */ display: "inline-block", /** Used to set the width of the button. This property is optional. */ width: null, /** Used to set the height of the button. This property is optional. */ height: null, /** Used to set the icon position relative to the button label. Options * are "left" or "right". Note that the icon style is set in the style * config. The icon style should not be used to control whether the * icon appears to the left or right of the label. Use this config * instead. */ iconAlign: "left", /** Style for individual elements within the component. Note that you can * provide CSS class names instead of individual style definitions. */ style:{ button: { border: "1px solid #cccccc", borderRadius: "3px", background: "#F6F6F6", cursor: "pointer", padding: "3px 7px", margin: "0px", color: "#2b2b2b" }, label: { fontFamily: "helvetica,arial,verdana,sans-serif", fontSize: "14px", whiteSpace: "nowrap" }, icon: { }, arrow: { }, select: { background: "#007FFF", border: "1px solid #003EFF", color: "#FFFFFF" }, hover: { background: "#ededed" }, menu: { border: "1px solid #cccccc", background: "#F6F6F6", cursor: "pointer", padding: "3px 3px", zIndex: "1" } }, /** Sound to play when the button is clicked */ sound: null }; var mainDiv; var mask, menu; var icon, label, arrow; //************************************************************************** //** Constructor //************************************************************************** var init = function(){ if (typeof parent === "string"){ parent = document.getElementById(parent); } if (!parent) return; //Clone the config so we don't modify the original config object var clone = {}; merge(clone, config); //Merge clone with default config merge(clone, defaultConfig); config = clone; //Get icon alignment var iconAlignment = config.iconAlign; if (!iconAlignment) iconAlignment = config.style.iconAlign; //legacy if (iconAlignment!=="right") iconAlignment = "left"; //Get menu alignment var menuAlignment = config.menuAlign; if (!menuAlignment) menuAlignment = config.style.menuAlignment; //legacy if (menuAlignment!=="right") menuAlignment = "bottom"; //Update arrow style as needed if (config.menu===true){ var arrowDefined = false; for (var key in config.style.arrow){ if (config.style.arrow.hasOwnProperty(key)){ arrowDefined = true; break; } } if (!arrowDefined){ if (menuAlignment==="bottom"){ config.style.arrow = { width: 0, height: 0, borderLeft: "5px solid transparent", borderRight: "5px solid transparent", borderTop: "5px solid #575757", marginLeft: "10px" }; config.style.arrowSelect = { borderTop: "5px solid #FFFFFF" }; } } } //Create outer div used to hold the button, mask, and menu var outerDiv = createElement('div', parent); outerDiv.setAttribute("desc", me.className); outerDiv.style.display = config.display; if (config.width){ if (typeof config.width === "string"){ outerDiv.style.width = config.width; } else{ outerDiv.style.width = config.width + "px"; } } if (config.height){ if (typeof config.height === "string"){ outerDiv.style.height = config.height; } else{ outerDiv.style.height = config.height + "px"; } } outerDiv.style.position = "relative"; if (config.hidden===true){ //legacy config... outerDiv.style.visibility = 'hidden'; outerDiv.style.display = 'none'; } me.el = outerDiv; //The button is implemented using a simple HTML table with 3 columns. The //left and right columns are for icons and the center column is for the //button label. The width of the center column is set to 100% and the //width of the left and right columns are defined by the "icon" and //"arrow" styles. Unfortunately, some browsers seem to have issues //rendering the table correctly inside of a div when the outerDiv's //display style set to "inline-block". For example, Mobile Safari will //completely ignore the "inline-block" style and stretch the button to //100% of the available width. In Chrome, if the left or right columns has //a width, the "inline-block" style is ignored and the button is stretched //to 100% of the available width. As a workaround, it looks like we can //wrap the button div in another div with the display style set to "table". var tableDiv = createElement('div', outerDiv); if (outerDiv.style.display==="inline-block"){ tableDiv.style.display = "table"; if (config.width) tableDiv.style.width = outerDiv.style.width; } tableDiv.style.height = "100%"; //Create main div used to represent the button mainDiv = createElement('div', tableDiv, config.style.button); mainDiv.setAttribute("desc", "button"); addEventHandlers(mainDiv); var table = createTable(mainDiv); table.style.fontFamily = "inherit"; table.style.textAlign = "inherit"; table.style.color = "inherit"; var tr = table.addRow(); var td; //Add icon (or label) td = tr.addColumn(); if (iconAlignment==="left"){ icon = createElement("div", td, config.style.icon); } else{ arrow = createElement("div", td, config.style.arrow); } //Add label td = tr.addColumn({width: "100%"}); label = createElement("div", td); setStyle(label, "label"); if (config.label) label.innerHTML = config.label; //Add arrow (or icon) td = tr.addColumn(); if (iconAlignment==="left"){ arrow = createElement("div", td, config.style.arrow); } else{ icon = createElement("div", td, config.style.icon); } //Create menu panel as needed if (config.menu===true){ config.toggle = true; menu = createElement('div', outerDiv, config.style.menu); menu.setAttribute("desc", "menu"); menu.style.position = "absolute"; menu.style.visibility = "hidden"; var hideMenu = function(e){ if (!mainDiv.contains(e.target)){ menu.style.visibility = "hidden"; me.deselect(); } }; //Hide menu if the client clicks outside of the menu window.addEventListener('click', hideMenu); //Create logic to process touch events var touchStartTime, touchEndTime; var x1, x2, y1, y2; window.addEventListener('touchstart', function(e){ x1 = e.changedTouches[0].pageX; y1 = e.changedTouches[0].pageY; touchStartTime = new Date().getTime(); touchEndTime = null; }); window.addEventListener('touchend', function(e){ touchEndTime= new Date().getTime(); x2 = e.changedTouches[0].pageX; y2 = e.changedTouches[0].pageY; var distance = Math.sqrt( (x2-=x1)*x2 + (y2-=y1)*y2 ); if (distance<0) distance = -distance; var duration = touchEndTime - touchStartTime; if ((duration <= 500 && distance <= 10) || //Quick tap (duration > 500 && distance <= 10)) { //Long press hideMenu(e); } }); } //Set button state if (config.disabled===true) me.disable(); if (config.selected===true) me.select(); //Add public show/hide methods addShowHide(me); }; //************************************************************************** //** addEventHandlers //************************************************************************** var addEventHandlers = function(div){ //Disable text selection div.unselectable="on"; div.onselectstart=function(){return false;}; //Create onclick function var onclick = function(){ if (config.sound!=null) config.sound.play(); if (config.toggle===true){ if (menu){ if (isTouch){ me.toggle(); } else{ //Do nothing - button is toggled on mouse down... } } else{ me.toggle(); } } else{ setStyle(mainDiv,"button"); setStyle(icon,"icon"); setStyle(arrow,"arrow"); } me.onClick(); }; //Create logic to process touch events var touchStartTime; var touchEndTime; var x1, x2, y1, y2; var isTouch = false; div.ontouchstart = function(e) { isTouch = true; e.preventDefault(); x1 = e.changedTouches[0].pageX; y1 = e.changedTouches[0].pageY; touchStartTime = new Date().getTime(); touchEndTime = null; if (div.selected!==true){ addStyle(div, "hover"); addStyle(icon, "iconHover"); addStyle(arrow, "arrowHover"); } }; div.ontouchend = function(e) { touchEndTime= new Date().getTime(); x2 = e.changedTouches[0].pageX; y2 = e.changedTouches[0].pageY; var distance = Math.sqrt( (x2-=x1)*x2 + (y2-=y1)*y2 ); if (distance<0) distance = -distance; var duration = touchEndTime - touchStartTime; if ((duration <= 500 && distance <= 10) || //Quick tap (duration > 500 && distance <= 10)) { //Long press onclick(); } else{ setStyle(div, "button"); setStyle(icon, "icon"); setStyle(arrow, "arrow"); } }; //Logic to process mouse events if (!isTouch){ div.onmousedown=function(){ addStyle(div, "select"); addStyle(icon, "iconSelect"); addStyle(arrow, "arrowSelect"); if (menu){ me.toggle(); //TODO: Add mouseup events to buttons in the menu } //return false; }; div.onclick = function(){ onclick(); }; div.onmouseover = function(){ if (div.selected!==true){ addStyle(div, "hover"); addStyle(icon, "iconHover"); addStyle(arrow, "arrowHover"); } }; div.onmouseout = function(){ if (div.selected!==true){ setStyle(div, "button"); setStyle(icon, "icon"); setStyle(arrow, "arrow"); } }; } }; //************************************************************************** //** onClick //************************************************************************** /** Called whenever the button is clicked. */ this.onClick = function(){}; //************************************************************************** //** getText //************************************************************************** /** Returns the button label. */ this.getText = function(){ return label.innerHTML; }; //************************************************************************** //** enable //************************************************************************** /** Used to enable the button. */ this.enable = function(){ var outerDiv = me.el; outerDiv.style.opacity = ""; if (mask) mask.style.visibility = "hidden"; }; //************************************************************************** //** disable //************************************************************************** /** Used to disable the button. */ this.disable = function(){ var outerDiv = me.el; outerDiv.style.opacity = "0.5"; if (mask){ mask.style.visibility = "visible"; } else{ mask = createElement('div',{ position: "absolute", zIndex: "1", width: "100%", height: "100%" }); mask.setAttribute("desc", "mask"); outerDiv.insertBefore(mask, outerDiv.firstChild); } }; //************************************************************************** //** isEnabled //************************************************************************** /** Returns true if the button is enabled (i.e. not disabled). */ this.isEnabled = function(){ return !me.isDisabled(); }; //************************************************************************** //** isDisabled //************************************************************************** /** Returns true if the button is disabled. */ this.isDisabled = function(){ if (mask){ if (mask.style.visibility !== "hidden") return true; } return false; }; //************************************************************************** //** select //************************************************************************** this.select = function(){ if (mainDiv.selected===true) return; mainDiv.selected = true; setStyle(mainDiv,"button"); setStyle(icon,"icon"); setStyle(arrow,"arrow"); addStyle(mainDiv,"select"); addStyle(icon,"iconSelect"); addStyle(arrow,"arrowSelect"); }; //************************************************************************** //** deselect //************************************************************************** this.deselect = function(){ if (mainDiv.selected===true){ mainDiv.selected = false; setStyle(mainDiv,"button"); setStyle(icon,"icon"); setStyle(arrow,"arrow"); } }; //************************************************************************** //** isSelected //************************************************************************** /** Returns true if the button is selected (e.g. depressed) */ this.isSelected = function(){ return (mainDiv.selected===true); }; //************************************************************************** //** toggle //************************************************************************** /** Used to toggle the button's selection state. */ this.toggle = function(){ if (config.toggle===true){ if (mainDiv.selected===true){ me.deselect(); if (menu) menu.style.visibility = "hidden"; } else{ me.select(); if (menu) menu.style.visibility = "visible"; } } }; //************************************************************************** //** getMenuPanel //************************************************************************** /** Returns the DOM element associated with the menu panel. Typically, this * is used to render menu options (i.e. buttons). */ this.getMenuPanel = function(){ return menu; }; //************************************************************************** //** Utils //************************************************************************** var merge = javaxt.dhtml.utils.merge; var createTable = javaxt.dhtml.utils.createTable; var createElement = javaxt.dhtml.utils.createElement; var addShowHide = javaxt.dhtml.utils.addShowHide; var setStyle = function(el, style){ javaxt.dhtml.utils.setStyle(el, config.style[style]); }; var addStyle = function(el, style){ javaxt.dhtml.utils.addStyle(el, config.style[style]); }; init(); }; |