/* * Copyright (C) 2005 - 2011 Jaspersoft Corporation. All rights reserved. * http://www.jaspersoft.com. * * Unless you have purchased a commercial license agreement from Jaspersoft, * the following license terms apply: * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ /** * ActionModel Object Originally written by Angus Croll. * Refactored for VFR project by Papanii Okai * @author Angus Croll * @author Papanii Okai (a.k.a The Code Pimp) */ var localContext = window; var actionModel = {}; //suffix to be appended to dynamically created context names actionModel.CONTEXT_SUFFIX = "Level"; actionModel.lastItemWasSeparator = false; actionModel.noItemsYet = true; actionModel.data = null; actionModel.openedSubMenus = []; //action constants actionModel.SIMPLE_ACTION = "simpleAction"; actionModel.SELECT_ACTION = "selectAction"; actionModel.INPUT_ACTION = "inputAction"; actionModel.OPTION_ACTION = "optionAction"; actionModel.SEPARATOR = "separator"; actionModel.SINGLE_SELECT_CONSTRAINT = "singleSelect"; actionModel.HIGH_Z_INDEX = 99999; actionModel.MENUOFFSET = 15; //reserved variables actionModel.RES_LABEL = "$label"; actionModel.RES_ID = "$id"; actionModel.RES_SELECTED = "$selected"; actionModel.EVENT = "$event"; actionModel.reservedVarMap = {}; if(typeof(window['selObjects']) != "undefined" ) { actionModel.reservedVarMap[actionModel.RES_SELECTED] = selObjects; } actionModel.tempDiv = null; actionModel.parentMenu = ""; actionModel.menuDom = null; actionModel.menuListDom = null; actionModel.PARENT_MENU_CONTAINER = "menuList"; actionModel.PARENT_MENU_STYLE = "menu vertical context hidden"; actionModel.SIMPLE_ACTION_DOM_ID = "menuList_simpleAction"; actionModel.SEPARATOR_DOM_ID = "menuList_separator"; actionModel.FLYOUT_PARENT_DOM_ID = "menuList_flyout"; actionModel.EXTRA_INPUT_DOM_ID = "menuList_extraInput"; actionModel.LIST_ITEM_DOM_ID = "menuList_listItem"; actionModel.DISPLAY_STYLE = "inline"; //patterns actionModel.DROP_DOWN_MENU = ".dropDown"; //////////////////////////////////////////////////////////////////// // Menu building //////////////////////////////////////////////////////////////////// /** * Used to present the context menu to the user. * @param menuContext * @param event the event tied to the menu. * @param className class name a caller may wish to use instead of the default. * @param coordinates used for menu positioning. * @actionModelScriptTag * @updateContextActionModel - optional method which can update context action model */ actionModel.showDynamicMenu = function(menuContext, event, className, coordinates, actionModelScriptTag, updateContextActionModel){ if(menuContext == null){ if(isProVersion()){ var selectionObj = designerBase.getSelectedObject(); if (selectionObj && selectionObj.menuLevel) { menuContext = selectionObj.menuLevel; }else { return; //no context - nothing to show } } else { return; } } actionModel.resetMenu(); actionModel.updateCSSClass(className); actionModel.initActionModelData(actionModelScriptTag); actionModel.assembleMenuFromActionModel(menuContext, event, actionModel.menuListDom, updateContextActionModel); actionModel.setMenuPosition(actionModel.menuDom, event, coordinates); actionModel.setMenuEventHandlers(actionModel.menuDom); if(isIE()) { jQuery('#'+actionModel.menuDom.id).prepend('
'); } actionModel.makeMenuVisible(actionModel.menuDom); isIE7() && setWidthStyleByReflection(actionModel.menuDom, '.content'); actionModel.adjustMenuPosition(actionModel.menuDom); actionModel.focusMenu(); jQuery('body').on('mouseout', actionModel.closeHandler); }; actionModel.closeHandler = function (event) { var target = jQuery(event.relatedTarget); if (((target.hasClass("mutton") || target.parents(".mutton").length) && event.relatedTarget) || (target.attr("id") === "menu" || target.parents("#menu").length)) { event.stopPropagation(); } else { actionModel.hideMenu(); } } /** * Used to create a simple temp div */ actionModel.createTempDiv = function(){ actionModel.tempDiv = document.createElement("div"); }; /** * Check if part of menu object/DOM. * @param obj Object we are investigating. */ actionModel.isTopAncestorTheMenu = function(obj){ var ancestors = $(obj).ancestors(); var bool = false; ancestors.each(function(parent){ if(parent.readAttribute("id") == "menu"){ bool = true; } }); return bool; }; /** * Hide any menus created by the action model. * This means removing all child nodes and setting it back to default state. */ actionModel.resetMenu = function(){ var dom = $("menu"); dom.parentId = undefined; dom.setAttribute("tabIndex",-1); dom.childElements().each(function(child){Element.remove(child);}); //add common template dom.innerHTML = $('commonMenu').cloneNode(true).innerHTML; var menuList = dom.down("ul"); var menuListId = menuList.readAttribute("id"); menuList.writeAttribute("id", actionModel.updateTemplateId(menuListId)); //remove any generated styles. dom.className = actionModel.PARENT_MENU_STYLE; dom.writeAttribute("style", ""); actionModel.menuDom = dom; actionModel.menuListDom = menuList; }; /** * Used to update a template's id */ actionModel.updateTemplateId = function(templateId){ var re = new RegExp("_template"); return templateId.replace(re, ""); }; /** * Method used to update the menu's css className * @param className */ actionModel.updateCSSClass = function(className){ if(isNotNullORUndefined(className)){ $(actionModel.menuDom).className = className + " hidden"; } }; /** * Determine position of menu based on mouse position.. * @param event */ actionModel.setMenuPosition = function(menuObj, event, coordinates){ var ev = event ? event : window.event; var location = Event.pointer(ev); if (isSupportsTouch() && ev.changedTouches) { location = { x: ev.changedTouches[0].pageX + 20, y: ev.changedTouches[0].pageY } } var leftOffset; var topOffset; //if coordinates are provided, override. if(isNotNullORUndefined(coordinates)){ if(isNotNullORUndefined(coordinates.menuLeft)){ leftOffset = coordinates.menuLeft; }else{ leftOffset = location.x; } if(isNotNullORUndefined(coordinates.menuTop)){ topOffset = coordinates.menuTop; }else{ topOffset = location.y; } }else{ leftOffset = location.x; topOffset = location.y; } //set top left to the mouse click menuObj.setStyle({ 'left': leftOffset + 'px', 'top' : topOffset +'px' }); }; actionModel.adjustMenuPosition = function(menu, left, top, width, height){ if(!$(menu).hasClassName("dropDown") || $(menu).hasClassName("fitable")){ fitObjectIntoScreen(menu, null, top, null, height); } }; /** * construct the menu dynamically based on the client side action model * @param menuContext * @param event * @updateContextActionModel - optional method which can update context action model */ actionModel.assembleMenuFromActionModel = function(menuContext, event, contentParent, updateContextActionModel) { actionModel.noItemsYet = true; var contextActionModel = actionModel.data[menuContext]; //In this place we can update action model for specified context on the fly. //This is necessary to have ability to construct fully dynamic menus which items can be calculated only //right before showing menu updateContextActionModel && (contextActionModel = updateContextActionModel(menuContext, contextActionModel)); contextActionModel.each(function(thisAction){ actionModel.appendToMenu(thisAction, event, contentParent) }); actionModel.removeTrailingSeparator(); }; /** * Initialize Action Model data with the JSON evaluated from the given element or directly from object. * * @param scriptTag Script holder identifier or simple object representing data. */ actionModel.initActionModelData = function(scriptTag) { if(scriptTag === "navigationActionModel"){ actionModel.data = primaryNavModule.JSON; }else { // TODO: replace Prototype evalJSON method with JQuery.parseJSON (problem - 1.4.4 causes exception) actionModel.data = _.isString(scriptTag) ? jQuery('#' + scriptTag).html().evalJSON().evalJSON() : scriptTag; } }; /** * Helper method used in the construction of the menu * @param thisAction * @param event */ actionModel.appendToMenu = function(thisAction, event, contentParent) { if (!actionModel.passesClientTest(thisAction)) { return; //does not satisfy client-side condition to appear in this menu } var mouseUpFunction = getMenuMouseupFunction( getAsFunction(thisAction.action), thisAction.actionArgs, thisAction.text, thisAction.id, event); actionModel.appendDesiredRowType(thisAction, event, mouseUpFunction, contentParent); }; /** * Append desired row type to menu * @param thisAction * @param mouseUpFunction * @param contentParent * @param event */ actionModel.appendDesiredRowType = function(thisAction, event, mouseUpFunction, contentParent){ //add new
  • element if (thisAction.type == actionModel.SIMPLE_ACTION) { actionModel.addSimpleActionRow(thisAction, mouseUpFunction, contentParent); } //add new
  • separator if (thisAction.type == actionModel.SEPARATOR && !actionModel.lastItemWasSeparator) { actionModel.addSeparatorRows(thisAction, contentParent); } //add new
  • flyout or new window? if (thisAction.type == actionModel.SELECT_ACTION) { actionModel.addSelector(thisAction, contentParent, event); } //add new
  • option if (thisAction.type == actionModel.OPTION_ACTION) { actionModel.addOption(thisAction, mouseUpFunction, contentParent); } }; /** * Add a simple action row to the menu * @param thisAction * @param mouseUpFunction * @param contentParent */ actionModel.addSimpleActionRow = function(thisAction, mouseUpFunction, contentParent) { var newMenuRow = null; if(thisAction.className && (thisAction.className == "requiresInput")){ newMenuRow = $(actionModel.EXTRA_INPUT_DOM_ID).cloneNode(true); }else{ newMenuRow = $(actionModel.SIMPLE_ACTION_DOM_ID).cloneNode(true); } if (isSupportsTouch()) { newMenuRow.ontouchend = mouseUpFunction; } else { newMenuRow.onmouseup = mouseUpFunction; } newMenuRow.onmouseup_saved = mouseUpFunction; var textPlacement = $(newMenuRow).down(".button"); var newText = document.createTextNode(thisAction.text); newText.nodeValue = replaceNbsps(newText.nodeValue); textPlacement.appendChild(newText); //set new id to make it unique. var menuId = newMenuRow.readAttribute("id") + "." + thisAction.action; newMenuRow.writeAttribute("id", menuId); actionModel.insertMenuRow(contentParent, newMenuRow, false, thisAction.isDisabled, thisAction.id, null); }; /** * Add a separator row to the menu * @param thisAction * @param contentParent */ actionModel.addSeparatorRows = function(thisAction, contentParent){ var cssOverride = null; if (actionModel.noItemsYet) { return; //don't need separator at very top; } var separator = $(actionModel.SEPARATOR_DOM_ID).cloneNode(true); var separatorId = separator.readAttribute("id") + getRandomId(); separator.writeAttribute("id", separatorId); if(thisAction.className){ cssOverride = thisAction.className; } actionModel.insertMenuRow(contentParent, separator, true, thisAction.isDisabled, null, cssOverride); }; /** * Used to add a option * @param thisAction * @param mouseUpFunction * @param contentParent */ actionModel.addOption = function(thisAction, mouseUpFunction, contentParent){ if (!actionModel.passesClientTest(thisAction)) { return; //does not satisfy client-side condition to appear in this menu } var cssOverride = null; var newOptionRow = $(actionModel.LIST_ITEM_DOM_ID).cloneNode(true); thisAction.button = (thisAction.button && String(thisAction.button).toLowerCase()=="true") ? true : false; if (isSupportsTouch()) { newOptionRow.ontouchend = mouseUpFunction; } else { newOptionRow.onmouseup = mouseUpFunction; } newOptionRow.onmouseup_saved = mouseUpFunction; var textPlacement = $(newOptionRow).down(".button"); var newText = document.createTextNode(thisAction.text); newText.nodeValue = replaceNbsps(newText.nodeValue); textPlacement.appendChild(newText); var newOptionRowId = newOptionRow.readAttribute("id") + getRandomId() + "_" + thisAction.action; newOptionRow.writeAttribute("id", newOptionRowId);//update dom id if(thisAction.className){ cssOverride = thisAction.className; } if (!thisAction.button && evaluateTest(thisAction.isSelectedTest, thisAction.isSelectedTestArgs, thisAction.text, thisAction.id, false, false)) { textPlacement.addClassName("down"); } actionModel.insertMenuRow(contentParent, newOptionRow, false, thisAction.isDisabled, thisAction.id, cssOverride); }; /** * Creates selector row and all sub menus related to that row * @param thisAction * @param contentParent * @param event */ actionModel.addSelector = function(thisAction, contentParent, event) { //since in the action model we specify the type of menu using the class nomenclature, we can check what //type of selector we want by simply checking the class in the action object var parentSelector; var className = thisAction.className; if(className == "flyout"){ //do not render flyout list item if no submenu items exist if (thisAction && (thisAction.children.length > 0)) { parentSelector = $(actionModel.FLYOUT_PARENT_DOM_ID).cloneNode(true); var textPlacement = $(parentSelector).down(".button"); var newText = document.createTextNode(thisAction.text); newText.nodeValue = replaceNbsps(newText.nodeValue); textPlacement.appendChild(newText); var parentSelectorId = parentSelector.readAttribute("id") + getRandomId(); parentSelector.writeAttribute("id", parentSelectorId); actionModel.insertMenuRow(contentParent, parentSelector, false, thisAction.isDisabled, thisAction.id, null); //now create submenu var submenu = actionModel.menuDom.cloneNode(false); submenu.writeAttribute("id", parentSelector.readAttribute("id") + "_subMenu"); //add common template submenu.innerHTML = $('commonMenu').cloneNode(true).innerHTML; var submenuList = submenu.down("ul"); var submenuListId = submenuList.readAttribute("id"); submenuList.writeAttribute("id", actionModel.updateTemplateId(submenuListId)); //get containers width. parentSelector.appendChild(submenu); var subMenuContainer = submenu.select('ul')[0]; var subMenuContainerId = subMenuContainer.writeAttribute("id", subMenuContainer.readAttribute("id") + "_subMenu"); actionModel.buildSubMenu(thisAction, subMenuContainerId, event); } } else { thisAction.children.each(function(childAction){ var mouseUpFunction = getMenuMouseupFunction( getAsFunction(childAction.action), childAction.actionArgs, childAction.text, childAction.id, event); actionModel.appendDesiredRowType(childAction, event, mouseUpFunction, contentParent); }); } }; /** * This method gets the left value for a submenu. It calculates if by getting the with of the parents content and adds * the parents left value to it since all menu objects are absolute. * @param selectorObj */ actionModel.getSubMenuLeft = function(selectorObj){ //
    var width = $(selectorObj).up(2).clientWidth; //width of parent window var leftOffSet = Math.abs(parseInt($(selectorObj).up(1).offsetLeft)); //offset return (width + leftOffSet); }; actionModel.getSubMenuTop = function(selectorObj){ return Math.abs(parseInt($(selectorObj).offsetTop)); //offset }; /** * Used to position the submenu with respect to its parent. * @param parent */ actionModel.showChildSubmenu = function(parent){ var pid = parent.parentNode.parentNode.parentNode.id; /* * Hides submenus previously open */ var i; var j; var f = false; for(i=0;idiv.content>ul').children('li.node').each(function(){ actionModel.hideChildSubmenu(jQuery(this).get(0)); }) } if(f) { jQuery('#'+actionModel.openedSubMenus[i]+'>div.content>ul').children('li.node').each(function(){ actionModel.hideChildSubmenu(jQuery(this).get(0)); }) } } actionModel.openedSubMenus.splice(j,actionModel.openedSubMenus.length-j); var submenuTop = null; var submenuHeight = null; var submenuWidth = null; var bottomPadding = 20; //hack to prevent bug20863 var submenu = parent.childElements()[1]; //applying style for flyout submenu.... submenu.setStyle({display: actionModel.DISPLAY_STYLE}); submenu.setStyle({position:'absolute'}); var leftValue = actionModel.getSubMenuLeft(parent); var topValue = 0; // var topValue = actionModel.getSubMenuTop(parent) + "px"; //set top left to the mouse click submenu.setStyle({ 'left': leftValue + "px", 'top' : topValue + "px" }); if(isIE()) { jQuery(submenu).prepend('
    '); } actionModel.makeMenuVisible(submenu); isIE7() && setWidthStyleByReflection(submenu, '.content'); submenuTop = $(submenu).cumulativeOffset()[1]; submenuHeight = $(submenu).getHeight(); //hack to prevent bug20863 (Due to Tim's markup change) var windowHeight = getWindowHeight(); var submenuBottom = submenuTop + submenuHeight; if(windowHeight < submenuBottom){ topValue = (windowHeight - submenuBottom - bottomPadding); submenu.setStyle({ 'top' : topValue + "px" }); } var submenuLeftOffset = submenu.cumulativeOffset()[0]; submenuWidth = submenu.getWidth(); if(getWindowWidth() < (submenuLeftOffset + submenuWidth)){ submenu.setStyle({ 'left' : "-" + submenuWidth + "px" }); } actionModel.openedSubMenus.push(pid); }; /** * Used to hide submenu * @param parent */ actionModel.hideChildSubmenu = function(parent){ var submenu = parent.childElements()[1]; submenu.setStyle({display: "none"}); submenu.setStyle({position:'absolute'}); submenu.setStyle({left: 0}); actionModel.makeMenuInVisible($(submenu)); }; /** * Used to change the css display value to make it visible. * @param menu object we changing the display style for. */ actionModel.makeMenuVisible = function(menu){ //test to see if we have any items to show var list = menu.down("ul"); if(list.childElements().length > 0){ menu.setStyle({zIndex: actionModel.HIGH_Z_INDEX}); $(menu).removeClassName("hidden"); } }; /** * Used to change the css display value to make it invisible * @param menu object we changing the display style for. */ actionModel.makeMenuInVisible = function(menu){ menu.addClassName("hidden"); }; /** * Used to build a the logical structure for a flyout menu * @param thisAction * @param parentId * @param event */ actionModel.buildSubMenu = function(thisAction, parentId, event){ thisAction.children.each(function(childAction){ actionModel.appendToMenu(childAction, event, $(parentId)); }); }; actionModel.isMenuShowing = function(){ return !($("menu").getStyle("display") == "none" || $('menu').hasClassName("hidden")); }; /** * Helper method to update row object * @param domObject * @param actionText */ actionModel.updateRowDom = function(domObject, actionText){ if(domObject){ domObject.innerHTML = actionText; var domObjectId = domObject.readAttribute("id") + getRandomId(); domObject.writeAttribute("id", domObjectId); } return domObject; }; /** * Used to launch new menu (E.g. Calculated fields.) */ actionModel.launchNewMenu = function(){ actionModel.hideMenu(); alert("action not implemented..."); }; /** * Helper to insert row in to menu * @param container * @param newMenuRow * @param isSeparator * @param isDisabled * @param userId */ actionModel.insertMenuRow = function(container, newMenuRow, isSeparator, isDisabled, userId, className) { //check to see if item has a id, if not set one.. if(newMenuRow.readAttribute("id") == null){ if (userId) { newMenuRow.setAttribute('id',userId); } else { newMenuRow.identify(); //sets unique id } } if (isDisabled) { actionModel.disableMenuOption(newMenuRow); } if(className){ newMenuRow.addClassName(className); } container.appendChild(newMenuRow); actionModel.lastItemWasSeparator = isSeparator; actionModel.noItemsYet = false; }; actionModel.disableMenuOption = function(menuOption){ buttonManager.disable(menuOption); _.isEmpty(menuOption.childNodes) && buttonManager.disable(menuOption.childNodes[0]); }; /** * Checking success of test. * @param action */ actionModel.passesClientTest = function(action) { var clientTestFunction; var result = true; //default to true because if there are no tests, we passed by default! //passes explicit test? if (action.clientTest) { clientTestFunction = action.clientTest; var testForNegative = false; if (clientTestFunction.startsWith("!")) { //negative test clientTestFunction = action.clientTest.sub("!",""); testForNegative = true; } result = evaluateTest(clientTestFunction, action.clientTestArgs, action.text, action.id, testForNegative, null); } //number of selections ok? if ((typeof(window['selObjects']) != "undefined" )) { if (result && action.selectionConstraint) { result = action.selectionConstraint == actionModel.SINGLE_SELECT_CONSTRAINT ? selObjects.length == 1 : selObjects.length > 1; } } return result; }; /** * If last row is a separator, remove it */ actionModel.removeTrailingSeparator = function(){ var lastRow = actionModel.menuListDom.lastChild; if ($(lastRow) && !actionModel.noItemsYet) { var lastRowId = lastRow.identify(); var reg = new RegExp("\w*separator\w*"); if (reg.test(lastRowId)) { lastRow.remove(); } } }; /** * Only applies to certain menus (e.g. naviagtion menu has a parent mutton) */ actionModel.getMenuParent = function() { return $(this.menuDom.parentId); }; actionModel.getFirstMenuButton = function() { return this.menuDom.down(layoutModule.BUTTON_PATTERN, 0); }; actionModel.focusMenu = function() { //will restore old focus when menu is closed actionModel.lastFocused = document.activeElement; !this.menuDom.hasClassName(layoutModule.HIDDEN_CLASS) && this.menuDom.focus(); if (!this.menuDom.match(actionModel.DROP_DOWN_MENU) && !isSupportsTouch()) { var childItems = this.menuDom.select("li"); if (childItems[0]) { var firstButton = childItems[0].down(layoutModule.BUTTON_PATTERN); firstButton && buttonManager.over(firstButton); } } }; /** * Used to display fine positioned menu relative to the object * * @param event Initial event * @param object Element that cause element to appear * @param context actionModel context in which we show this menu. Used to find proper metadata * @param cssClass Menu css class * @param aModel action model */ actionModel.showDropDownMenu = function(event, object, context, cssClass, aModel){ object = jQuery(object); var offsets = object.offset(), coordinates = { "menuLeft":offsets.left, "menuTop":offsets.top + object.height() - 1 }; actionModel.showDynamicMenu(context, event, cssClass, coordinates, aModel); }; //////////////////////////////////////////////////////////// // General section for menu events //////////////////////////////////////////////////////////// /** * Register event handlers to menu objects */ actionModel.setMenuEventHandlers = function(menu){ menu.select("li").each(function(object){ /* * Special mouse-enter and mouse-leave events for fly-outs */ if ((!object.hasClassName("disabled"))) { if (object.hasClassName("node")) { if (isSupportsTouch()) { Event.observe(object, 'touchstart', function(event) { if (!event.firstCall) { actionModel.showChildSubmenu(object); } event.firstCall = true; }); /* Event.observe(object, 'touchend', function(event) { var child = object.down(); buttonManager.up(child); buttonManager.out(child); event.stop(); }); */ } else { Event.observe(object, 'mouseenter', function(event) { actionModel.showChildSubmenu(object); }); Event.observe(object, 'mouseleave', function(event) { actionModel.hideChildSubmenu(object); }); } } } }); }; actionModel.initializeOneTimeMenuHandlers = function() { /** * generic mouseenter for menu - to initialize for mouse use */ $('menu').observe('mouseenter', function(event) { var selected = this.select("." + layoutModule.HOVERED_CLASS)[0]; selected && buttonManager.out(selected); }.bind($('menu'))) }; /** * Public method used to hide a menu. */ actionModel.hideMenu = function(){ if (actionModel.isMenuShowing()) { var setNextFocus = function(){ var nextFocus = actionModel.lastFocused; try { nextFocus && nextFocus.focus(); } catch(ex) { //IE gets bothered if you try to focus something that it can't } }; !isSupportsTouch() && setTimeout(setNextFocus, 0); //timeout so that any current key action misses this focus actionModel.makeMenuInVisible($("menu")); jQuery('body').off('mouseout', actionModel.closeHandler); } }; /** * Utility method which helps to create menu element * @param type type of menu element, one of ["simpleAction", "selectAction", "optionAction"] * this is the only mandatory parameter to create manu item. * @param options - object with optional arguments. * Possible object structure is: * { * text: test, * clientTest: clientTest, * clientTestArgs: clientTestArgs, * action: action, * actionArgs: actionArgs, * children: children, * isSelectedTest: isSelectedTest, * isSelectedTestArgs: isSelectedTestArgs, * selectionConstraint: selectionConstraint, * className: className * } */ actionModel.createMenuElement = function(type, options){ if (!type) { throw new Error("Can not construct actionModel object: type can not be empty"); } return Object.extend({type: type}, options); }; //////////////////////////////////////////////////////////// // function utils //////////////////////////////////////////////////////////// function replaceNbsps(str) { var re = new RegExp(" ", "g"); return str.replace(re, " "); } function fireMenuAction(myFunction, when, event) { if(isIPad() && actionModel && actionModel.isMenuShowing()){ actionModel.hideMenu(); } myFunction = getAsFunction(myFunction); if(when) myFunction(); if(actionModel && actionModel.isMenuShowing()){ actionModel.hideMenu(); } } function fireMenuActionWithEvent(args, myFunction, when, event) { var belongsToLocalContext = localContext && localContext[myFunction]; myFunction = getAsFunction(myFunction); if (when) { if (args && isArray(args)) { myFunction.apply(belongsToLocalContext ? localContext : null, args); } else { myFunction.call(belongsToLocalContext ? localContext : null, args); } } if(actionModel && actionModel.isMenuShowing()){ actionModel.hideMenu(); } } function getMenuMouseupFunction(actionFunction, argsToInvokeWith, label, id, event) { if (!actionFunction) { return null; } if (!argsToInvokeWith) { return function(){ fireMenuAction(actionFunction, true, event); } } else { var args = resolveReservedStringsInArray(argsToInvokeWith, label, id, event); return function(){ fireMenuActionWithEvent(args, actionFunction, true, event); } } } function evaluateTest(thisFunctionName, functionArgs, label, id, testForNegative, defaultResult) { var result = (defaultResult!=null) ? defaultResult : true; //if no default assume true; var theFunction = getAsFunction(thisFunctionName); if (theFunction) { result = theFunction.apply(this,resolveReservedStringsInArray(functionArgs, label, id, null)); if (testForNegative) { result = !result; } } return result; } function resolveReservedStringsInArray(oldArray, label, id, ev) { if (!oldArray) { return []; } return oldArray.collect(function(thisString) {return resolveReservedStrings(thisString, label, id, ev)}); } function resolveReservedStrings(thisString, label, id, ev) { var directMapping = actionModel.reservedVarMap[thisString]; if (directMapping) { return directMapping; } if (thisString==actionModel.RES_LABEL) { return label; } if (thisString==actionModel.RES_ID) { return id; } if (thisString==actionModel.EVENT) { return ev; } return thisString; } /** * Get a random number. * May need to move this to common utils */ function getRandomId(){ var rand = Math.round(Math.random(10) * 50); return "_" + rand; }