To start with, very nice write-ups! Not easy to be clear and understandable with complex topics and you’re nailing it.
GaryDotzlaw:
Honestly, I think Servoy needs to support objects and the prototype better. There is really no good reason it can not see the functions; every other editor can.
I think you missed the relatively recent Servoy addition of the @constructor jsdoc tag. Basically fixes all of your workarounds with the prototype.
From a Servoy Magazine article that we just haven’t gotten around to writing up (so I’ll just post it here and let you explain in english!), some code. The constructor function “Resolution” is used to build up a a popup menu of items with each item passing along associated properties to the popup menu’s action method. Which just so happens to be the same method so eventually at the end of the method, you have webutils stuff happening with parameters based on the selected menu item’s instance of the prototype. Code credits to Troy:
/**
* @param {JSEvent|Number} [event] the event that triggered the action/item index
* @param {Number} [menuParentIdx]
* @param {Boolean} [menuSelected]
* @param {String} [menuParentTxt]
* @param {String} [menuTxt]
* @param {String} [name] Name of item selected
* @param {Number} [width]
* @param {Number} [height]
* @param {Boolean} [getDims] Return dimensions for selected option
*
* @return {{width: Number, height:Number}|undefined}
*
* @properties={typeid:24,uuid:"2EFD8325-BF6F-40CC-8188-C445DAB04ECC"}
*/
function ACTION_resize(event,menuParentIdx,menuSelected,menuParentTxt,menuTxt,name,width,height,getDims) {
/**
* @constructor
* @param {String} name Display value of resolution
* @param {Number} big Largest side of resolution
* @param {Number} little Smallest side of resolution
*/
function Resolution(name, big, little) {
/**
* Display value of resolution
* @type {String}
*/
this.name = name
/**
* Largest side of resolution
* @type {Number}
*/
this.big = big
/**
* Smallest side of resolution
* @type {Number}
*/
this.little = little
/**
* Display "Name (horizontal x portrait)"
* @return {String}
*/
this.getName = function() {
return name + ' (' + this.getDimHoriz() + ' x ' + this.getDimVert() +')'
}
/**
* Returns horizontal dimension
* @return {Number}
*/
this.getDimHoriz = function() {
return _resizeOrient ? little : big
}
/**
* Returns vertical dimension
* @return {Number}
*/
this.getDimVert = function() {
return !_resizeOrient ? little : big
}
}
if (application.getApplicationType() != APPLICATION_TYPES.WEB_CLIENT) {
plugins.dialogs.showErrorDialog(
'Error',
'Resize works only in the web'
)
}
else {
var map = {
phone: [
new Resolution('Normal',480,320),
new Resolution('iPhone 5+',568,320),
new Resolution('nHD',640,360)
],
tablet: [
new Resolution('iPad',1024,768),
new Resolution('7-inch',1024,600),
new Resolution('Large',640,480),
new Resolution('Ginormous',960,720)
],
desktop: [
new Resolution('Most common',1366,768), //as of 2013 Oct
new Resolution('Square',1280,1024),
new Resolution('20-incher',1680,1050),
new Resolution('24-incher',1920,1200)
],
tv: [
new Resolution('720p HD',1280,720),
new Resolution('1080p Full HD',1920,1080),
new Resolution('4K Ultra HD',3840,2160)
]
}
map.phone.icon = 'media:///ssstandard_mobile_small.png'
map.tablet.icon = 'media:///ssstandard_tablet_small.png'
map.desktop.icon = 'media:///ssstandard_desktop_small.png'
map.tv.icon = 'media:///ssstandard_landscape_small.png'
/**
* @param {String} type
*/
function mapLoop(type) {
var displayName = type == 'tv' ? 'TV' : utils.stringInitCap(type)
//divider with name and icon
if (map[type].icon) {
item = menu.addMenuItem(displayName)
if (solutionModel.getMedia(utils.stringReplace(map[type].icon,'media:///',''))) {
item.setIcon(map[type].icon)
}
item.enabled = false
}
for (var i = 0; i < map[type].length; i++) {
/** @type {Resolution} */
var res = map[type][i]
if (res instanceof Resolution) {
if (res.name == _resizeSelected) {
item = menu.addCheckBox(res.getName())
item.selected = true
}
else {
item = menu.addMenuItem(res.getName())
}
item.setMethod(ACTION_resize)
item.methodArguments = [res.name,res.getDimHoriz(),res.getDimVert()]
}
}
}
//when right clicked, give a moment to grab focus elsewhere
if (event instanceof JSEvent) {
var menu = plugins.window.createPopupMenu()
var item
//fluiditize
item = menu.addCheckBox('Fit to window')
item.setIcon("media:///ssstandard_move_small.png")
item.setMethod(ACTION_resize)
item.methodArguments = ['Fluid']
if (_resizeSelected == 'Fluid') {
item.selected = true
}
// menu.addSeparator()
//orientate
item = menu.addMenuItem('Flip orientation')
item.setIcon("media:///ssstandard_rotate_small.png")
item.setMethod(ACTION_resize)
item.methodArguments = ['Orient']
menu.addSeparator()
mapLoop('phone')
mapLoop('tablet')
mapLoop('desktop')
mapLoop('tv')
menu.show(elements.lbl_resize)
TOGGLE_block_popup(true)
}
else {
TOGGLE_block_popup(false)
var id = plugins.WebClientUtils.getElementMarkupId(forms.WEB_0F_page__live__web__view.elements.lbl_page)
//grab default name if nothing passed in
if (!name) {
name = _resizeSelected
outer:
for (var i in map) {
for (var j = 0; j < map[i].length; j++) {
if (map[i][j].name == name) {
width = map[i][j].getDimHoriz()
height = map[i][j].getDimVert()
break outer
}
}
}
}
//return out selected dimensions
if (getDims) {
if (name != 'Fluid') {
return {
width: width,
height: height
}
}
}
else {
//orientation
if (name == 'Orient') {
//konami code (toggle orientation 3 times in a row)
ACTION_resize.konami = typeof ACTION_resize.konami == 'number' ? ACTION_resize.konami + 1 : 1
//flip orientation
_resizeOrient = _resizeOrient ? 0 : 1
//toggle orientation for selected resolution
if (_resizeSelected != 'Fluid') {
ACTION_resize()
}
return
}
//fluid
else if (name == 'Fluid') {
plugins.WebClientUtils.executeClientSideJS(
'$("#' + id + '_cms").animate({width:"100%",height:"100%"}).removeClass("drunk");'
)
}
//specific dimension
else {
//activate konami code
var konami = ''
if (event) {
ACTION_resize.konami = null
}
if (ACTION_resize.konami == 3) {
konami = '.addClass("drunk")'
}
plugins.WebClientUtils.executeClientSideJS(
'$("#' + id + '_cms").animate({width:"' + width + 'px",height:"' + height + 'px"})' + konami + ';'
)
}
//save down what resolution we're on
_resizeSelected = name
}
}
}
}
Code completion on the constructor function:
[attachment=1]constructor-code-completion.png[/attachment]
End result in real world app:
[attachment=0]constructor-in-action.png[/attachment]