Skip to content

Instantly share code, notes, and snippets.

@iconifyit
Last active January 24, 2020 05:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iconifyit/0b88d049bc8af5871bd25290215544c6 to your computer and use it in GitHub Desktop.
Save iconifyit/0b88d049bc8af5871bd25290215544c6 to your computer and use it in GitHub Desktop.
A wrapper class for dialogs created with the Script UI Dialog Builder by Joonas - https://scriptui.joonas.me/
/**
* @author Scott Lewis <scott@atomiclotus.net>
* @copyright 2020 Scott Lewis
* @version 1.0.0
* @url http://github.com/iconifyit
* @url https://atomiclotus.net
*
* ABOUT:
*
* This JS class is a wrapper for dialogs created with the Script UI Dialog Builder by Joonas Pääkkö.
* The wrapper dynamically creates getters and setters as well as provides an easy interface for
* retrieving field values, attaching event handlers to buttons, performing form validation, etc.
* This allows you to simply copy & paste the original dialog into your script/extension and not have
* to modify it at all to work with your code. Additionally, if you need to make changes to the dialog,
* you do not need to change any of your code other than custom handlers and field validators. *
*
* LICENSE:
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the “Software”), to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Usage:
*
* The basic idea is to use the DialogWrapper like a View. You do not need to interact directly with
* the Dialog object itself, you can use the intuitive interfaces created by the wrapper to get, set,
* and validate values, as well as respond to user actions.
*
* var wrapper = new DialogWrapper(
* this,
* new Dialog(),
* ['name', 'email', 'url']
* ['cancel', 'ok'],
* {
* name : 'Scott',
* email : 'scott@atomiclotus.net',
* url : 'https://atomiclotus.net'
* }
* );
*
* Using getters and setters:
*
* NOTE: You do not need to include a class reference. The getters and setters are added
* to the calling scope, typically Window so that calling `getName()` is the same as `window.getName()`
*
* getName() // -> Scott
* getEmail() // -> scott@atomiclotus.net
* getUrl() // -> https://atomiclotus.net
*
* Or update dialog field values:
*
* setName('Scott Lewis');
* setEmail('scott@vectoricons.net');
* setUrl('https://vectoricons.net');
*
* Validating the form:
*
* wrapper.validateForm({
* name : function(name) { return name.tirm() !== '' },
* email : function(email) { // email validation code },
* url : function(url) { // url validation code }
* });
*
* Set the default and cancel buttons:
*
* wrapper.setDefaultElement('Ok');
* wrapper.setCancelElement('cancel');
*
* Set a callback handler:
*
* wrapper.setHandler('ok', 'onclick', function() {
* // Handle the click event.
* });
*
* OR:
*
* wrapper.setHandler('name', 'onchange', function() {
* wrapper.updateFormState();
* });
*
* Get the form's values:
*
* var values = wrapper.getFormValues();
*
* Show & hide the dialog:
*
* wrapper.show();
* wrapper.close();
*
* @see https://scriptui.joonas.me/
*/
(function(global) {
/**
* Get a value from an object or array.
* @param {object|array} subject The object or array to search
* @param {string} key The object property to find
* @param {*} _default The default value to return if property is not found
* @returns {*} The found or default value
*/
function get( subject, key, _default ) {
var value = _default;
if (typeof(subject[key]) !== 'undefined') {
value = subject[key];
}
return value;
}
/**
* Creates a new DialogWrapper class.
* @param global
* @param Dialog
* @param fields
* @param buttons
* @param data
* @constructor
*/
var DialogWrapper = function(global, Dialog, fields, buttons, data) {
var module = this;
try {
/**
* @type {{}}
*/
this.getters = {};
/**
* @type {{}}
*/
this.setters = [];
/**
* @type {Window | Window | IconMetaDialog}
*/
this.dialog = Dialog;
/**
* @type {*|*[]}
*/
this.fields = fields || [];
/**
* @type {*|*[]}
*/
this.buttons = buttons || [];
/**
* @type {*|{}}
*/
this.meta = data || {};
/**
* @type {string}
*/
this.cancelElementName = 'cancel';
/**
* @type {string}
*/
this.defaultElemenName = 'save';
/**
* Initialize the wrapper class.
*/
this._init();
}
catch(e) { throw e; }
}
/**
* Initializes the class.
* @private
*/
DialogWrapper.prototype._init = function() {
var module = this;
/*
* Test global field values.
*/
try {
this.fields.forEach(function(fieldName) {
module.set(fieldName, get(data, fieldName, ''));
});
}
catch(e) { throw e; }
/*
* Create Getters and Setters
*/
for (var f in this.fields) {
var func,
getter = this.makeGetterName(f),
setter = this.makeSetterName(f);
/*
* Adds getter for each field to the calling scope (typically Window)
* Example : getName() // --> returns value of 'name' field of the dialog.
*/
func = function() {
return module.get(f);
}
module.getters[getter] = global[getter] = func;
/*
* Adds setter for each field to the calling scope (typically Window)
* Example : setName(value) // --> sets the 'name' field on the dialog.
*/
func = function(value) {
module.set(f, value);
return module.get(f);
}
module.setters[setter] = global[setter] = func;
}
}
/**
* Show the dialog.
*/
DialogWrapper.prototype.show = function() {
this.dialog.show();
}
/**
* Close the dialog.
*/
DialogWrapper.prototype.close = function() {
this.dialog.close();
}
/**
* Sets the dialog's default button.
* @param buttonName
*/
DialogWrapper.prototype.setDefaultElement = function(buttonName) {
this.defaultElemenName = buttonName;
this.dialog.defaultElement = this.findElement(buttonName);
}
/**
* Sets the dialog's cancel button.
* @param buttonName
*/
DialogWrapper.prototype.setCancelElement = function(buttonName) {
this.cancelElementName = buttonName;
this.dialog.cancelElement = this.findElement(buttonName);
}
/**
* Update the form state based on form validation.
*/
DialogWrapper.prototype.updateFormState = function() {
this.dialog.defaultElement.enabled = this.validateForm();
}
/**
* Make sure required fields have values.
* @returns {boolean}
*
* Usage:
*
* Validations are necessarily custom. The validations variable is an
* object with field names and a function that performs the validation.
* So, for instance, if you have a `name` field that is required, you could
* just pass:
*
* validations = {
* 'name' : function(name) {
* return name.trim() !== '';
* }
* }
*
* DialogWrapper.validateForm(validations)
*/
DialogWrapper.prototype.validateForm = function(validations) {
var module = this,
validator,
value;
for (var fieldName in validations) {
value = module.get(fieldName);
validator = function(){}
if (validations[fieldName] instanceof Function) {
validator = validations[fieldName];
}
if (! validator.call(this, value)) {
return false;
}
}
return true;
}
/**
* Get the form values.
* @returns {{setName: string, folderPath: *, authorName: string, imagesPath: string, fileType: string}}
*/
DialogWrapper.prototype.getFormValues = function() {
var formValues = {};
this.fields.forEach(function(fieldName) {
formValues[fieldName] = this.get(fieldName, null);
})
return formValues;
}
/**
* Get a field value by field name.
* @param key
* @param fallback
* @returns {*}
*/
DialogWrapper.prototype.get = function(key, fallback) {
try {
var field = this.findElement(key);
if (! $is.undefined()) {
if (! $is.undefined(field.text)) {
return field.text;
}
else if (! $is.undefined(field.value)) {
return field.value;
}
}
return fallback;
}
catch(e) { throw e; }
}
/**
* Set a field value by field name.
* @param key
* @param fallback
* @returns {*}
*/
DialogWrapper.prototype.set = function(key, value) {
try {
var field = this.findElement(key);
if (! $is.undefined(field)) {
if (! $is.undefined(field.text)) {
field.text = value;
}
else if (! $is.undefined(field.value)) {
field.value = value;
}
}
return field;
}
catch(e) { throw e; }
}
/**
* Create getter from field names.
* @param fieldName
* @returns {*}
*/
DialogWrapper.prototype.makeGetterName = function(fieldName) {
var f = fieldName;
return fieldName.replace(
/\w\S*/g,
'get' + f.charAt(0).toUpperCase() + f.substr(1)
);
}
/**
* Create setter from field names.
* @param fieldName
* @returns {*}
*/
DialogWrapper.prototype.makeSetterName = function(fieldName) {
var f = fieldName;
return fieldName.replace(
/\w\S*/g,
'set' + f.charAt(0).toUpperCase() + f.substr(1)
);
}
/**
* Wrapper for Dialog.findElement
* @param entityName
* @returns {*}
*/
DialogWrapper.prototype.findElement = function(entityName) {
return this.dialog.findElement(entityName);
}
/**
* Sets event handler on dialog element.
* @param entityName
* @param eventName
* @param handler
*/
DialogWrapper.prototype.setHandler = function(entityName, eventName, handler) {
var entity = this.findElement(entityName);
if (typeof entity !== 'undefined') {
entity[eventName] = handler;
}
}
global.DialogWrapper = DialogWrapper;
})(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment