/**
* @namespace
*/
var DSHDLib = (function(){
/**
* Context and settings for the library
*
* @typedef LibraryConfig
* @memberof DSHDLib
* @property {string} domain - The domain of the Design Huddle environment
* @property {string} [access_token] - A backend-generated user access token. Will override guest and visitor settings if set
* @property {string} [client_id] - The client id of your Design Huddle app. Only required for guest and visitor user types
* @property {bool} [guest=false] - Value indicating whether this is a guest user. Will override visitor settings if set
* @property {bool} [visitor=false] - Value indicating whether this is a visitor user
* @property {bool} [admin=false] - Value indicating whether the library is used in the context of an admin user, leveraging the {@link https://api.designhuddle.com/admin|Admin API} and the admin mode of the editor
* @property {string} [guest_id] - Explicitly set a guest user id
* @property {string} [gid_cookie_name] - Override the name of the first-party cookie storing the guest/visitor id
* @property {string} [gid_cookie_domain] - Override the domain of the first-party cookie storing the guest/visitor id. Useful for making it available across subdomains
* @property {number} [gid_cookie_days=365] - Override the default expiration of the first-party cookie storing the guest/visitor id
* @property {bool} [gid_cookie_refresh=false] - Force an existing guest/visitor id cookie expiration to be extended upon the first authenticated request
* @property {string} [gat_cookie_name] - Override the name of the first-party cookie storing the guest/visitor access token
* @property {string} [gat_cookie_domain] - Override the domain of the first-party cookie storing the guest/visitor access token. Useful for making it available across subdomains
* @property {bool} [gat_cookie_refresh=false] - Ignore an existing guest/visitor access token cookie and retrieve a new access token, extending its expiration, upon the first authenticated request
* @property {bool} [new_guest_on_non_guest_access_attempt] - Value indicating whether a new guest user should be created if it's determined that the previously used guest has since been upgraded to a full user and the app is configured to no longer allow guest-based permission to it
* @property {string} [template_customization_object_hash] - A <u>{@link DSHDLib.storeTemplateCustomizationObject|previously acquired}</u> {@link DSHDLib.TemplateCustomizationObjects.TemplateCustomizationObject|TemplateCustomizationObject} hash to automatically apply on all {@link DSHDLib.createProject|createProject} and {@link DSHDLib.getVariableTemplatePreviewURL|getVariableTemplatePreviewURL} requests where an object or hash is not explicitly supplied
* @property {DSHDLib.TemplateCustomizationObjects.TemplateCustomizationObject} [template_customization_object] - A {@link DSHDLib.TemplateCustomizationObjects.TemplateCustomizationObject|TemplateCustomizationObject} to automatically apply on all {@link DSHDLib.createProject|createProject} and {@link DSHDLib.getVariableTemplatePreviewURL|getVariableTemplatePreviewURL} requests where an object or hash is not explicitly supplied
*/
var cfg_default = {};
var domain_public_access_tokens = {};
var tco_cache = {};
var cacheTemplateCustomizationObject = function(cfg, tco, tcoh){
var ctx = cfg.access_token || (cfg.guest ? 'g' : 'v');
tco_cache[ctx] = tco_cache[ctx] || {};
tco_cache[ctx][tcoh] = JSON.stringify(tco);
};
var findCachedTemplateCustomizationObject = function(cfg, tco){
var tcos = JSON.stringify(tco);
var ctx = cfg.access_token || (cfg.guest ? 'g' : 'v');
for (var h in tco_cache[ctx])
if (tcos == tco_cache[ctx][h])
return h;
return null;
};
var assignObject = Object.assign;
if (typeof assignObject != 'function') {
assignObject = function(t){
var o = Object(t);
for (var i = 1; i < arguments.length; i++) {
var s = arguments[i];
if (s !== undefined && s !== null)
for (var k in s)
if (s.hasOwnProperty(k))
o[k] = s[k];
}
return o;
};
}
var getRandomId = function(length){
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var array;
if ('Uint8Array' in self && 'crypto' in self && length <= 65536) {
array = new Uint8Array(length);
self.crypto.getRandomValues(array);
} else {
array = new Array(length);
for (let i = 0; i < length; i++)
array[i] = Math.floor(Math.random() * 62);
}
var result = '';
for (let i = 0; i < length; i++)
result += possible.charAt(array[i] % 62);
return result;
};
var parseDomain = function(url){
var domain = url.substring(url.indexOf('://') + 3);
domain = domain.substring(0, domain.indexOf('/'));
return domain;
};
var parseQS = function(url){
var idx = url.indexOf('?');
if (idx < 0)
return {};
var qs = url.substring(idx + 1);
if (!qs)
return {};
var s = qs.split('&'), params = {};
for (var i = 0; i < s.length; i++) {
var nv = s[i].split('=');
if (nv.length >= 2) {
var name = decodeURIComponent(nv[0]);
if (name.length == 0)
continue;
var value = decodeURIComponent(nv[1]);
if (typeof params[name] == 'undefined')
params[name] = value;
else if (params[name] instanceof Array)
params[name].push(value);
else
params[name] = [params[name], value];
}
}
return params;
};
var getCookieValue = function(name){
var name_s = name + '=';
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ')
c = c.substring(1, c.length);
if (c.indexOf(name_s) == 0)
return c.substring(name_s.length, c.length);
}
return null;
};
var setCookie = function(name, value, expires, domain){
if (expires && !(expires instanceof Date)) {
var days = expires;
expires = new Date();
expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000));
}
document.cookie = name + '=' + value + (domain ? '; domain=' + domain : '') + (expires ? '; expires=' + expires.toGMTString() : '') + '; path=/';
};
var getNewPublicUserID = function(){
return (new Date()).getTime() + '-' + getRandomId(6);
};
var getPublicUserID = function(cfg){
var cn = cfg.gid_cookie_name || ('dshd-gid-' + cfg.client_id);
var exst_gid = getCookieValue(cn);
var gid = cfg.guest_id || exst_gid || getNewPublicUserID();
let cd = cfg.gid_cookie_days === 0 ? 0 : (cfg.gid_cookie_days || 365);
if (cd >= 0 && (!exst_gid || exst_gid != gid || cfg.gid_cookie_refresh || (cfg.guest && (getPublicUserType(cfg) == 'visitor')))) {
setCookie(cn, gid, cd, cfg.gid_cookie_domain);
setCookie(cn + '_ut', cfg.guest ? 'guest' : 'visitor', cd, cfg.gid_cookie_domain);
}
return gid;
};
var getPublicUserType = function(cfg){
return getCookieValue((cfg.gid_cookie_name || ('dshd-gid-' + cfg.client_id)) + '_ut');
};
var hasRecentPublicAccessToken = function(domain){
return domain_public_access_tokens[domain].date_created && domain_public_access_tokens[domain].date_created.getTime() > ((new Date()).getTime() - 120000);
};
var token_retrieval_in_progress = false;
var GetPublicAccessToken = function(cfg, cb){
if (token_retrieval_in_progress) {
setTimeout(function(){
GetPublicAccessToken(cfg, cb);
}, 100);
return;
}
cfg = cfg || {};
var rcfg = assignObject({}, cfg_default, cfg);
if (!rcfg.domain) {
cb(new Error('Missing Domain'));
return;
}
var visitor_need_guest = rcfg.guest && (getPublicUserType(rcfg) == 'visitor');
var token = domain_public_access_tokens[rcfg.domain];
if (!cfg.force_refresh && token && token.expiration_date > (new Date()) && !visitor_need_guest) {
cb(null, token);
return;
}
if (!rcfg.client_id) {
cb(new Error('Missing Client ID'));
return;
}
var cn = rcfg.gat_cookie_name || ('dshd-gat-' + rcfg.client_id);
if (!cfg.force_refresh && !rcfg.gat_cookie_refresh && !visitor_need_guest) {
var exst_gat = getCookieValue(cn);
if (exst_gat) {
var parts = exst_gat.split('_');
if (parts.length > 1) {
var dt = new Date(parts[1]);
if (!isNaN(dt) && !(dt < new Date())) {
token = {
access_token: parts[0],
expiration_date: dt,
expires_in: Math.round((dt.getTime() - (new Date()).getTime()) / 1000)
};
domain_public_access_tokens[rcfg.domain] = token;
cb(null, token);
return;
}
}
}
}
var is_visitor = !rcfg.guest && rcfg.visitor && getPublicUserType(rcfg) != 'guest';
token_retrieval_in_progress = true;
fetch('https://' + rcfg.domain + '/oauth/token/' + (is_visitor ? 'visitor' : 'guest'), {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
credentials: 'omit',
body: 'grant_type=password&client_id=' + rcfg.client_id + '&username=' + encodeURIComponent(getPublicUserID(rcfg))
}).then(function(response){
token_retrieval_in_progress = false;
response.json().then(function(new_token){
if (response.ok) {
var dt = new Date((new Date()).getTime() + (new_token.expires_in * 1000));
domain_public_access_tokens[rcfg.domain] = assignObject({ expiration_date: dt, date_created: new Date() }, new_token);
setCookie(cn, new_token.access_token + '_' + dt.toISOString(), dt, rcfg.gat_cookie_domain);
cb(null, new_token);
} else if (new_token.non_guest) {
if (rcfg.new_guest_on_non_guest_access_attempt && !rcfg.guest_id) {
var new_cfg = assignObject({}, cfg, { guest_id: getNewPublicUserID() });
GetPublicAccessToken(new_cfg, cb);
} else
cb(new Error('Public Access Attempt to Non-Guest'));
} else
cb(new Error('Public Access Token Retrieval Error'), token);
});
}).catch(function(e){
token_retrieval_in_progress = false;
cb(new Error('Public Access Token Retrieval Error: ' + e.name + ' - ' + e.message));
});
};
var GetAccessToken = function(cfg, cb){
var rcfg = assignObject({}, cfg_default, cfg);
if (rcfg.access_token) {
cb(null, rcfg.access_token);
} else if (rcfg.guest || rcfg.visitor) {
GetPublicAccessToken(cfg, function(err, token){
if (err)
cb(err);
else
cb(null, token.access_token);
});
} else
cb(new Error('Missing Access Token'));
};
var makeAPIJSONRequest = function(cfg, action, url, method, params, cb){
var rcfg = assignObject({}, cfg_default, cfg);
var request_info = {
method: method,
mode: 'cors',
credentials: 'omit',
headers: {}
};
if (params) {
request_info.headers['Content-Type'] = 'application/json';
request_info.body = JSON.stringify(params);
}
var makeRequest = function(access_token, attempt){
request_info.headers['Authorization'] = 'Bearer ' + access_token;
fetch(url, request_info).then(function(response){
response.json().then(function(json){
if (response.ok && json.success && json.data) {
cb(null, json.data);
} else if (!rcfg.access_token && (rcfg.guest || rcfg.visitor) && (response.status == 401 || response.status == 403) && !hasRecentPublicAccessToken(rcfg.domain)) {
GetPublicAccessToken(assignObject({}, cfg, { force_refresh: true }), function(err, token){
if (err)
cb(err);
else
makeRequest(token.access_token);
});
} else if (!attempt && (response.status == 502 || response.status == 503 || response.status == 504)) {
setTimeout(function(){
makeRequest(access_token, 1);
}, Math.max(400, Math.round(Math.random() * 1000)));
} else
cb(new Error(action + ' Error'), json);
});
}).catch(function(e){
cb(new Error(action + ' Error: ' + e.name + ' - ' + e.message));
});
};
GetAccessToken(cfg, function(err, access_token){
if (err)
cb(err);
else
makeRequest(access_token);
});
};
var makeAPIJSONGETRequest = function(action, path, params_or_cb, cfg_or_cb, cb){
var params = {};
var cfg = {};
if (params_or_cb instanceof Function) {
cb = params_or_cb;
} else {
params = params_or_cb || {};
if (cfg_or_cb instanceof Function)
cb = cfg_or_cb;
else
cfg = cfg_or_cb;
}
var rcfg = assignObject({}, cfg_default, cfg);
if (!rcfg.domain) {
cb(new Error('Missing Domain'));
return;
}
if (rcfg.visitor && !rcfg.guest) {
cb(new Error('Visitors May Not ' + action));
return;
}
var qs = [];
for (var p in params)
qs.push(p + '=' + encodeURIComponent(Array.isArray(params[p]) ? JSON.stringify(params[p]) : params[p]));
var url = 'https://' + rcfg.domain + (rcfg.admin ? '/partners' : '') + path + (qs.length ? ('?' + qs.join('&')) : '');
makeAPIJSONRequest(cfg, action, url, 'GET', null, cb);
};
/**
* Callback after listing templates
*
* @callback getTemplatesCallback
* @memberof DSHDLib
* @param {Error} [error] - An error returned upon an authorization or other server side issue
* @param {Object} data
* @param {number} data.page
* @param {number} data.limit
* @param {number} data.total
* @param {Object[]} data.items - See {@link https://api.designhuddle.com/user#tag/Templates/paths/~1gallery~1templates/get|here} for the fields returned per template
*/
/**
* List Templates
*
* @function DSHDLib.getTemplates
* @param {!DSHDLib.getTemplatesCallback} callback
* @example
* DSHDLib.getTemplates(function(err, data){
* console.log(data.total);
* });
*/
/**
* List Templates
*
* @function DSHDLib.getTemplates
* @param {Object} [params] - See {@link https://api.designhuddle.com/user#tag/Templates/paths/~1gallery~1templates/get|here} for all possible inputs
* @param {!DSHDLib.getTemplatesCallback} callback
* @example
* DSHDLib.getTemplates({ brand_id: 123, search: "Find Me" }, function(err, data){
* console.log(data.total);
* });
*/
/**
* List Templates
*
* @function DSHDLib.getTemplates
* @param {Object} [params] - See {@link https://api.designhuddle.com/user#tag/Templates/paths/~1gallery~1templates/get|here} for all possible inputs
* @param {DSHDLib.LibraryConfig} [config_override]
* @param {!DSHDLib.getTemplatesCallback} callback
* @example
* DSHDLib.getTemplates({}, { access_token: "abc" }, function(err, data){
* console.log(data.items.length);
* });
*/
var GetTemplates = function(params_or_cb, cfg_or_cb, cb){
makeAPIJSONGETRequest('List Templates', '/api/gallery/templates', params_or_cb, cfg_or_cb, cb);
};
/**
* Callback after listing projects
*
* @callback getProjectsCallback
* @memberof DSHDLib
* @param {Error} [error] - An error returned upon an authorization or other server side issue
* @param {Object} data
* @param {number} data.page
* @param {number} data.limit
* @param {number} data.total
* @param {Object[]} data.items - See {@link https://api.designhuddle.com/user#tag/Projects/paths/~1projects/get|here} for the fields returned per project
*/
/**
* List Projects
*
* @function DSHDLib.getProjects
* @param {!DSHDLib.getProjectsCallback} callback
* @example
* DSHDLib.getProjects(function(err, data){
* console.log(data.total);
* });
*/
/**
* List Projects
*
* @function DSHDLib.getProjects
* @param {Object} [params] - See {@link https://api.designhuddle.com/user#tag/Projects/paths/~1projects/get|here} for all possible inputs
* @param {!DSHDLib.getProjectsCallback} callback
* @example
* DSHDLib.getProjects({ brand_id: 123, search: "Find Me" }, function(err, data){
* console.log(data.total);
* });
*/
/**
* List Projects
*
* @function DSHDLib.getProjects
* @param {Object} [params] - See {@link https://api.designhuddle.com/user#tag/Projects/paths/~1projects/get|here} for all possible inputs
* @param {DSHDLib.LibraryConfig} [config_override]
* @param {!DSHDLib.getProjectsCallback} callback
* @example
* DSHDLib.getProjects({}, { access_token: "abc" }, function(err, data){
* console.log(data.items.length);
* });
*/
var GetProjects = function(params_or_cb, cfg_or_cb, cb){
makeAPIJSONGETRequest('List Projects', '/api/projects', params_or_cb, cfg_or_cb, cb);
};
/**
* Callback after project retrieval
*
* @callback getProjectCallback
* @memberof DSHDLib
* @param {Error} [error] - An error returned upon an authorization or other server side issue
* @param {Object} project - See {@link https://api.designhuddle.com/user#tag/Projects/paths/~1projects~1{project_id}/get|here} for the full list of returned fields
*/
/**
* Retrieve project information
*
* @function DSHDLib.getProject
* @param {!string} project_id - The project to retrieve
* @param {!DSHDLib.getProjectCallback} callback
* @example
* DSHDLib.getProject("abc", function(err, project){
* console.log(project.project_title);
* });
*/
/**
* Retrieve project information
*
* @function DSHDLib.getProject
* @param {!string} project_id - The project to retrieve
* @param {Object} [params] - See {@link https://api.designhuddle.com/user#tag/Projects/paths/~1projects~1{project_id}/get|here} for all possible inputs
* @param {!DSHDLib.getProjectCallback} callback
* @example
* DSHDLib.getProject("abc", { generate_latest_thumbnail: true }, function(err, project){
* console.log(project.thumbnail_url);
* });
*/
/**
* Retrieve project information
*
* @function DSHDLib.getProject
* @param {!string} project_id - The project to retrieve
* @param {Object} [params] - See {@link https://api.designhuddle.com/user#tag/Projects/paths/~1projects~1{project_id}/get|here} for all possible inputs
* @param {DSHDLib.LibraryConfig} [config_override]
* @param {!DSHDLib.getProjectCallback} callback
* @example
* DSHDLib.getProject("abc", { generate_latest_thumbnail: true }, { access_token: "abc" }, function(err, project){
* console.log(project.thumbnail_url);
* });
*/
var GetProject = function(project_id, params_or_cb, cfg_or_cb, cb){
if (!project_id) {
cb(new Error('Missing Project ID'));
return;
}
makeAPIJSONGETRequest('Retrieve Project', '/api/projects/' + project_id, params_or_cb, cfg_or_cb, cb);
};
/**
* Callback after project creation
*
* @callback createProjectCallback
* @memberof DSHDLib
* @param {Error} [error] - An error returned upon an authorization or other server side issue
* @param {Object} data
* @param {string} data.project_id - The unique identifier of the newly created project
*/
/**
* Create a new user-specific project from a template or from scratch
*
* @function DSHDLib.createProject
* @param {!Object} params - See {@link https://api.designhuddle.com/user#tag/Projects/paths/~1projects/post|here} for all possible inputs
* @param {!DSHDLib.createProjectCallback} callback
* @example
* DSHDLib.createProject({
* media_type: "print",
* dimensions: {
* width: 3.5,
* height: 2,
* unit_type: "in"
* }
* }, function(err, data){
* console.log(data.project_id);
* });
* @example
* DSHDLib.createProject({
* template_id: 123,
* customizations: {
* classes: {
* "title": {
* text: "New Title"
* }
* }
* }
* }, function(err, data){
* console.log(data.project_id);
* });
*/
/**
* Create a new user-specific project from a template or from scratch
*
* @function DSHDLib.createProject
* @param {!Object} params - See {@link https://api.designhuddle.com/user#tag/Projects/paths/~1projects/post|here} for all possible inputs
* @param {DSHDLib.LibraryConfig} [config_override]
* @param {!DSHDLib.createProjectCallback} callback
* @example
* DSHDLib.createProject({
* template_code: "abc"
* }, { access_token: "abc" }, function(err, data){
* console.log(data.project_id);
* });
*/
var CreateProject = function(parameters, cfg_or_cb, cb){
var cfg = {};
if (cfg_or_cb instanceof Function)
cb = cfg_or_cb;
else
cfg = cfg_or_cb;
var rcfg = assignObject({}, cfg_default, cfg);
if (!rcfg.domain) {
cb(new Error('Missing Domain'));
return;
}
if (rcfg.visitor && !rcfg.guest) {
cb(new Error('Visitors May Not Create Project'));
return;
}
var params = assignObject({}, parameters);
var tco_converted = false;
if (!params.customizations && !params.customizations_hash && (params.template_id || params.template_code)) {
if (rcfg.template_customization_object_hash)
params.customizations_hash = rcfg.template_customization_object_hash;
else if (rcfg.template_customization_object)
params.customizations = rcfg.template_customization_object;
}
if (params.customizations) {
var hash = findCachedTemplateCustomizationObject(rcfg, params.customizations);
if (hash) {
tco_converted = true;
params.customizations_hash = hash;
delete params.customizations;
}
}
var url = 'https://' + rcfg.domain + (rcfg.admin ? '/partners' : '') + '/api/projects';
makeAPIJSONRequest(cfg, 'Create Project', url, 'POST', params, function(err, data){
if (!err) {
if (params.customizations && data.customizations_hash)
cacheTemplateCustomizationObject(rcfg, params.customizations, data.customizations_hash);
if (tco_converted)
data.customizations_hash = params.customizations_hash;
cb(null, data);
} else
cb(err);
});
};
/**
* Callback for returning the generated TCO Hash
*
* @callback storeTemplateCustomizationObjectCallback
* @memberof DSHDLib
* @param {Error} [error] - An error returned upon an authentication or other server side issue
* @param {Object} data
* @param {string} data.object_hash - The generated TCO Hash uniquely identifying the TCO for subsequent requests
*/
/**
* Store a Template Customization Object (TCO) for later use
*
* @function DSHDLib.storeTemplateCustomizationObject
* @param {!Object} params
* @param {!DSHDLib.TemplateCustomizationObjects.TemplateCustomizationObject} params.object - The TCO
* @param {bool} [params.save_as_user_default] - Specify whether to save this TCO as the user's default data for later use in project creation
* @param {!DSHDLib.storeTemplateCustomizationObjectCallback} callback
* @example
* DSHDLib.storeTemplateCustomizationObject({
* object: {
* classes: {
* "title": {
* text: "New Title"
* }
* }
* }
* }, function(err, data){
* console.log(data.object_hash);
* });
*/
/**
* Store a Template Customization Object (TCO) for later use
*
* @function DSHDLib.storeTemplateCustomizationObject
* @param {!Object} params
* @param {!DSHDLib.TemplateCustomizationObjects.TemplateCustomizationObject} params.object - The TCO
* @param {bool} [params.save_as_user_default] - Specify whether to save this TCO as the user's default data for later use in project creation
* @param {DSHDLib.LibraryConfig} [config_override]
* @param {!DSHDLib.storeTemplateCustomizationObjectCallback} callback
* @example
* DSHDLib.storeTemplateCustomizationObject({
* object: {
* classes: {
* "title": {
* text: "New Title"
* }
* }
* }
* }, { access_token: "abc" }, function(err, data){
* console.log(data.object_hash);
* });
*/
var tcos_in_progress = [];
var StoreTemplateCustomizationObject = function(params, cfg_or_cb, cb){
var cfg = {};
if (cfg_or_cb instanceof Function)
cb = cfg_or_cb;
else
cfg = cfg_or_cb;
var rcfg = assignObject({}, cfg_default, cfg);
if (!rcfg.domain) {
cb(new Error('Missing Domain'));
return;
}
if (!params.object) {
cb(new Error('Missing Template Customization Object'));
return;
}
var exst_hash = findCachedTemplateCustomizationObject(rcfg, params.object);
if (exst_hash) {
cb(null, { object_hash: exst_hash });
return;
}
var tcos = JSON.stringify(params.object);
params.object = JSON.parse(tcos);
if (tcos_in_progress.indexOf(tcos) >= 0) {
setTimeout(function(){
StoreTemplateCustomizationObject(params, cfg, cb);
}, 100);
return;
}
tcos_in_progress.push(tcos);
var icb = function(err, data){
tcos_in_progress.splice(tcos_in_progress.indexOf(tcos), 1);
cb(err, data);
};
var url = 'https://' + rcfg.domain + (rcfg.admin ? '/partners' : '') + '/api/template/customization-objects';
makeAPIJSONRequest(cfg, 'Template Customization Storage', url, 'POST', params, function(err, data){
if (!err) {
cacheTemplateCustomizationObject(rcfg, params.object, data.object_hash);
icb(null, data);
} else
icb(err);
});
};
/**
* @namespace TemplateCustomizationObjects
* @memberof DSHDLib
*/
/**
* An object containing data to populate a template. {@link https://api.designhuddle.com/doc/Template_Customization_Objects.pdf|Read full documentation here}
*
* @typedef {Object} TemplateCustomizationObject
* @memberof DSHDLib.TemplateCustomizationObjects
* @property {Object.<string, DSHDLib.TemplateCustomizationObjects.TemplateCustomizationObjectElementUpdates>} elements - A map of data to populate based on element name matches
* @property {Object.<string, DSHDLib.TemplateCustomizationObjects.TemplateCustomizationObjectElementUpdates>} classes - A map of data to populate based on element class name matches
* @property {Object.<string, DSHDLib.TemplateCustomizationObjects.TemplateCustomizationObjectReplacements>} replacements - A map of replacements to make based on matched properties
*/
/**
* An object containing data to populate a matched element. {@link https://api.designhuddle.com/doc/Template_Customization_Objects.pdf|Read full documentation here}
*
* @typedef {Object} TemplateCustomizationObjectElementUpdates
* @memberof DSHDLib.TemplateCustomizationObjects
* @property {string} [color] - A [color]{@tutorial Colors} to set on matching text, shape and line elements (RGB/CMYK/SPOT)
* @property {string} [text] - Text to replace on matching text elements
* @property {bool} [auto_fit] - A value indicating weither to update font size dynamically to fit the supplied "text" property within any matching textbox's current width/line count
* @property {Object} [font] - Update the font on matching text elements
* @property {string} font.family - The name of the font family to set
* @property {bool} [font.bold=false] - A value indicating weither to use a bold version of the new font (if available)
* @property {bool} [font.italic=false] - A value indicating weither to use an italic version of the new font (if available)
* @property {Object} [stroke] - Update the stroke on matching text, shape and image elements
* @property {string} [stroke.color] - The [color]{@tutorial Colors} to apply (RGB/CMYK/SPOT)
* @property {string} [url] - A public or signed url reference or a data url for a svg, png or jpg file. This will either be placed in a matching zone or replace a placeholder shape element
* @property {Object} [position_relative_to_original] - Override the default position (relative to the matching zone/shape) for images placed via the "url" property
* @property {string} [position_relative_to_original.horizontal_align] - A horizontal position of "left", "center" or "top"
* @property {string} [position_relative_to_original.horizontal_align] - A vertical position of "top", "center" or "bottom"
* @property {Object} [masked_media] - Set or update a masked image in matching shape elements
* @property {string} [masked_media.url] - A public or signed url reference or a data url for a png or jpg file that will be masked in matching shape elements
* @property {Object} [masked_media.color_overlay] - A color overlay to apply to a matching masked image
* @property {string} [masked_media.color_overlay.color] - The [color]{@tutorial Colors} to apply (RGB/CMYK/SPOT)
* @property {number} [masked_media.color_overlay.percentage] - A number between 0 and 1 representing the intensity of the color overlay
* @property {number} [masked_media.saturation] - A number between -1 and 1 representing the saturation to apply to the masked image
* @property {Object} [shadow] - Update an existing shadow on matching text elements
* @property {string} [shadow.color] - The [color]{@tutorial Colors} to apply (RGB/CMYK/SPOT)
* @property {Object} [color_overlay] - A color overlay to apply to any matching image elements
* @property {string} [color_overlay.color] - The [color]{@tutorial Colors} to apply (RGB/CMYK/SPOT)
* @property {number} [color_overlay.percentage] - A number between 0 and 1 representing the intensity of the color overlay
* @property {number} [saturation] - A number between -1 and 1 representing the saturation to apply to the new image set via the "url" property or existing matching image elements
* @tutorial Colors
*/
/**
* An object containing data to replace matched element properties. {@link https://api.designhuddle.com/doc/Template_Customization_Objects.pdf|Read full documentation here}
*
* @typedef {Object} TemplateCustomizationObjectReplacements
* @memberof DSHDLib.TemplateCustomizationObjects
* @property {Object.<string, string>} [color] - Update any instances of the matched color with the new [color]{@tutorial Colors} (RGB/CMYK/SPOT)
* @property {Object.<string, string>} [text] - Update any text elements with the fully matched text with the replacement text
* @property {Object.<string, string>} [text_substring] - Update any text (token) found within text elements with the replacement text
* @tutorial Colors
*/
/**
* The input options to generate a Variable Template Preview URL. {@link https://api.designhuddle.com/doc/Variable_Template_Previews.pdf|Read full documentation here}
*
* @typedef {Object} VariableTemplatePreviewParams
* @memberof DSHDLib
* @property {number} template_id - The auto-generated identifier associated with the template. Required if <i>template_code</i> is not provided
* @property {string} template_code - An optional user-assigned template identifier. Required if <i>template_id</i> is not provided
* @property {number} [page_number=1] - Optionally return a specific page other than the first on multi-page projects
* @property {number} [scene_number=1] - Optionally return a specific scene other than the first on multi-scene video projects
* @property {string} customizations_hash - The TCO hash previously created via LINK to storeTCO. Required if <i>customizations</i> is not provided
* @property {DSHDLib.TemplateCustomizationObjects.TemplateCustomizationObject} customizations - The TCO hash previously created via LINK to storeTCO. Required if <i>customizations_hash</i> is not provided
* @property {number} [width=320] - The width of the desired image. Set automatically based on the aspect ratio of the template if only height is provided
* @property {number} [height] - The height of the desired image. Set automatically based on the aspect ratio of the template if only width is provided
* @property {string} [fit="center"] - The desired cropping of the image if both width and height are provided but do not match the aspect ratio of the template. See all possible options {@link https://api.designhuddle.com/doc/Variable_Template_Previews.pdf|here}
*/
/**
* Callback for functions that return may return an error
*
* @callback errorCallback
* @memberof DSHDLib
* @param {Error} [error] - An error returned upon an authentication or other server side issue
*/
/**
* Callback for returning a dynamic Variable Template Preview image URL
*
* @callback getVariableTemplatePreviewURLCallback
* @memberof DSHDLib
* @param {Error} [error] - An error returned upon an authentication or other server side issue
* @param {string} url - The generated URL
*/
/**
* Retrieve a URL to load a dynamically generated image based on the supplied Template and Template Customization Object (TCO)
*
* @function DSHDLib.getVariableTemplatePreviewURL
* @param {!DSHDLib.VariableTemplatePreviewParams} params
* @param {!DSHDLib.getVariableTemplatePreviewURLCallback} callback
* @example
* DSHDLib.getVariableTemplatePreviewURL({ template_id: 123, customizations_hash: "abc", width: 400 }, function(err, url){
* var img = new Image();
* img.src = url;
* });
*/
/**
* Retrieve a URL to load a dynamically generated image based on the supplied Template and TCO
*
* @function DSHDLib.getVariableTemplatePreviewURL
* @param {!DSHDLib.VariableTemplatePreviewParams} params
* @param {DSHDLib.LibraryConfig} [config_override]
* @param {!DSHDLib.getVariableTemplatePreviewURLCallback} callback
* @example
* DSHDLib.getVariableTemplatePreviewURL({ template_id: 123, customizations_hash: "abc", width: 400 }, { access_token: "abc" }, function(err, url){
* var img = new Image();
* img.src = url;
* });
*/
var GetVariableTemplatePreviewURL = function(params, cfg_or_cb, cb){
var cfg = {};
if (cfg_or_cb instanceof Function)
cb = cfg_or_cb;
else
cfg = cfg_or_cb;
var rcfg = assignObject({}, cfg_default, cfg);
if (!rcfg.domain) {
cb(new Error('Missing Domain'));
return;
}
if (!params.template_id && !params.template_code) {
cb(new Error('Missing Template'));
return;
}
var tco = params.customizations || rcfg.template_customization_object;
var tcoh = params.customizations_hash || rcfg.template_customization_object_hash;
if (tco && !tcoh)
tcoh = findCachedTemplateCustomizationObject(rcfg, tco);
if (!tcoh && !tco) {
cb(new Error('Missing Customizations'));
return;
}
var generateURL = function(access_token){
var url = 'https://' + rcfg.domain + '/previews/vtp?token=' + access_token + '&ch=' + tcoh;
var addParam = function(key, value){
if (value)
url += '&' + key + '=' + encodeURIComponent(value);
};
addParam('p', params.page_number || params.scene_number);
addParam('w', params.width);
addParam('h', params.height);
if (params.fit) {
var options = {
'center':'c','centre':'c','entropy':'en','attention':'a',
'northwest':'nw','north':'n','northeast':'ne','west':'w','east':'e','southwest':'sw','south':'s','southeast':'se',
'top left':'nw','top':'n','top right':'ne','left':'w','right':'e','bottom left':'sw','bottom':'s','bottom right':'se'
};
addParam('fp', options[params.fit]);
}
if (params.template_id)
addParam('t', params.template_id);
else if (params.template_code)
addParam('t', 'code-' + params.template_code);
cb(null, url);
};
GetAccessToken(cfg, function(err, access_token){
if (!err) {
if (!tcoh) {
StoreTemplateCustomizationObject({ object: tco }, cfg, function(ierr, data){
if (!ierr) {
tcoh = data.object_hash;
generateURL(access_token);
} else
cb(ierr);
});
} else
generateURL(access_token);
} else
cb(err);
});
};
/**
* @namespace
* @memberof DSHDLib
*/
var Editors = (function(){
var editors = {};
var editors_private_info = {};
var findEditorByIframe = function(iframe){
for (var e in editors)
if (editors[e].iframe === iframe)
return editors[e];
return null;
};
var findEditorByProject = function(project_id, skip_obj){
for (var e in editors) {
var editor = editors[e];
if (editor !== skip_obj && editor.project_id == project_id)
return editor;
}
return null;
};
var subscriber_defaults = {
'EXIT_PROJECT': function(editor){
editor.redirectOnError({ exit_project: true });
},
'SIGN_OUT': function(editor){
editor.redirectOnError({ sign_out: true });
},
'HANDLE_ERROR': function(editor){
editor.redirectOnError();
},
'RELOAD': function(editor, cfg){
editor.reload(cfg);
},
'PROJECT_CHANGE': function(editor, cfg){
editor.changeProject(cfg.project_id);
}
};
/**
* Callback supplying the editor instance a listener was triggered on
*
* @callback editorCallback
* @memberof DSHDLib.Editors.Editor
* @param {DSHDLib.Editors.Editor} editor - The editor instance triggering the event
*/
var listening = false;
var listen = function(){
var bindEvent = function(element, event_name, event_handler){
if (element.addEventListener) {
element.addEventListener(event_name, event_handler, false);
} else if (element.attachEvent) {
element.attachEvent('on' + event_name, event_handler);
}
};
bindEvent(window, 'message', function(event){
if (event && event.data && typeof event.data === 'string') {
try {
var data = JSON.parse(event.data);
if (data && data.type && data.type.indexOf('DSHD_') == 0 && (data.project_id || (data.ref == 'embederror' && Object.keys(editors).length == 1))) {
var event_name = data.type.substring(5) + (data.request_id ? ('-' + data.request_id) : '');
for (var e in editors) {
var editor = editors[e];
if (data.project_id && editor.project_id != data.project_id)
continue;
var to_send = [].concat(editor.subscribers[event_name] || []);
if (to_send.length) {
for (var s = 0; s < to_send.length; s++) {
try {
to_send[s](editor, data.payload);
} catch {}
}
} else if (subscriber_defaults[event_name] && data.ref != 'embederror')
subscriber_defaults[event_name](editor, data.payload);
}
}
} catch {}
}
});
listening = true;
};
var throwIfExistingProject = function(project_id, not_obj){
if (findEditorByProject(project_id, not_obj))
throw 'Only one Editor can be active per Project ID';
return false;
};
/**
* Class representing an editor iframe on a page - Instantiate via {@link DSHDLib.Editors} factory
*
* @class DSHDLib.Editors.Editor
* @property {HTMLIFrameElement} iframe - The iframe element in the DOM
* @property {string} id - The autogenerated unique identifier of the editor
* @property {bool} initialized - A value indicated whether that editor has fully loaded or re-loaded
* @example
* DSHDLib.Editors.insert("parent_container_id", { project_id: "abc" }, function(err, e){
* var editor = e;
* });
*/
function Editor(iframe_element, cfg_or_url) {
this.iframe = iframe_element;
this.subscribers = {};
this.id = getRandomId(10);
this.initialized = false;
this.onInit = [];
this.onInitDupe = {};
editors_private_info[this.id] = {};
this.handleProjectData(function(e){
if (!e.initialized) {
e.initialized = true;
e.onInitDupe = {};
var to_run = [].concat(e.onInit);
for (var i = 0; i < to_run.length; i++) {
try {
to_run[i](e);
} catch {}
}
}
});
this.refreshConfig(cfg_or_url);
if (!throwIfExistingProject(this.project_id)) {
if (typeof cfg_or_url != 'string' || this.iframe.src != cfg_or_url)
this.setUrl();
editors[this.id] = this;
if (!listening)
listen();
}
}
var allowed_qs_params = ['gallery_id','expand_right_panel','hide_right_panel','zoom','theme_color1','theme_color2','locale','color_defaults_rgb','color_defaults_cmyk','hide_undo_redo','classes_context','classes_list','disable_class_creation','class_modification_element_types','hide_zoom','hide_prev_next','hide_play_scene','hide_play_all','sm','review'];
assignObject(Editor.prototype, {
setUrl: function(url){
if (url) {
var prev_url = editors_private_info[this.id].url;
this.refreshConfig(url);
try {
if (!throwIfExistingProject(this.project_id, this)) {
this.initialized = false;
this.iframe.src = url;
}
} catch (err) {
if (prev_url)
this.refreshConfig(prev_url);
throw err;
}
} else {
var token = editors_private_info[this.id].access_token;
url = 'https://' + this.domain + '/editor?' + (this.admin ? 'partner=1&' : '') + (token ? 'token=' + token + '&' : '') + 'project_id=' + this.project_id + (this.page_number ? '&page=' + this.page_number : '') + (this.safe_mode && !this.sm ? '&sm=1' : '');
for (var p in this) {
if (p.indexOf('product_image_') === 0 || p.indexOf('temporary_background_') === 0 || allowed_qs_params.indexOf(p) >= 0) {
var val = this[p];
if (val) {
if (typeof val == 'boolean')
val = '1';
else if (typeof val == 'object')
val = JSON.stringify(val);
url += '&' + p + '=' + encodeURIComponent(val);
}
}
}
this.initialized = false;
editors_private_info[this.id].url = url;
this.iframe.src = url;
}
},
refreshConfig: function(cfg_or_url){
var cfg;
if (typeof cfg_or_url == 'string') {
editors_private_info[this.id].url = cfg_or_url;
var qs = parseQS(cfg_or_url);
cfg = {
domain: parseDomain(cfg_or_url),
admin: qs.partner == '1',
access_token: qs.token,
project_id: qs.project_id
};
if (qs.page)
cfg.page_number = parseInt(qs.page) || null;
if (qs.sm)
cfg.safe_mode = true;
for (var p in qs)
if (p.indexOf('product_image_') === 0 || allowed_qs_params.indexOf(p) >= 0)
cfg[p] = qs[p];
} else {
editors_private_info[this.id].url = null;
cfg = assignObject({}, cfg_or_url);
}
if (!cfg.domain && !this.domain) {
if (cfg_default.domain)
cfg.domain = cfg_default.domain;
else
throw 'Missing Domain';
}
if (!cfg.access_token && !editors_private_info[this.id].access_token) {
if (cfg_default.access_token) {
cfg.access_token = cfg_default.access_token;
} else {
var rcfg = assignObject({}, cfg_default, this, cfg);
if (rcfg.guest) {
var token = domain_public_access_tokens[rcfg.domain];
if (token && token.expiration_date > (new Date()))
cfg.access_token = token.access_token;
}
}
if (!cfg.access_token && !(cfg.internal || cfg.domain == window.location.host))
throw 'Missing Access Token';
}
if (!cfg.project_id && !this.project_id)
throw 'Missing Project ID';
if (cfg.hasOwnProperty('access_token')) {
editors_private_info[this.id].access_token = cfg.access_token;
delete cfg['access_token'];
}
assignObject(this, cfg);
},
/**
* Load a different project into the editor
*
* @function changeProject
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!string} project_id - The new project to load in the editor
* @example
* editor.changeProject("abc");
*/
changeProject: function(project_id){
if (!throwIfExistingProject(project_id, this)) {
this.project_id = project_id;
this.setUrl();
}
},
/**
* Wait for editor initialization before running other code
*
* @function onInitialized
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!DSHDLib.Editors.Editor.editorCallback} callback - The function to run upon editor initialization
* @example
* editor.onInitialized(function(e){
* console.log('Editor Initialized');
* });
*/
onInitialized: function(callback){
this.onInit.push(callback);
if (this.initialized)
callback(this);
},
/**
* Request a new guest token or update the explicitly-provided access token when approaching expiration or upon notification of authorization failure
*
* @function refreshAccessToken
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {string} [access_token] - A new access token retreived via backend authentication
* @param {DSHDLib.errorCallback} [callback] - The function to run after guest token retrieval
* @example
* editor.refreshAccessToken("abc");
*/
refreshAccessToken: function(access_token, cb){
var me = this;
var refresh = function(token){
if (token != editors_private_info[me.id].access_token) {
editors_private_info[me.id].access_token = token;
me.setUrl();
}
if (cb)
cb();
};
if (!access_token) {
var rcfg = assignObject({}, cfg_default, me);
if (rcfg.guest) {
var cfg = assignObject({}, me, { force_refresh: !hasRecentPublicAccessToken(rcfg.domain) });
GetPublicAccessToken(cfg, function(err, token){
if (!err)
refresh(token.access_token);
else
cb(err);
});
} else {
if (cb)
cb(new Error('Missing Access Token'));
else
throw 'Missing Access Token';
}
} else
refresh(access_token);
},
/**
* Reload the editor. Useful if making external project state changes
*
* @function reload
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {Object} [options]
* @param {number} [options.page_number] - The initial page to open upon project load
* @param {number} [options.scene_number] - The initial scene to open upon project load
* @example
* editor.reload();
* @example
* editor.reload({ page_number: 2 });
* @example
* editor.reload({ scene_number: 3 });
*/
reload: function(cfg){
var pn = cfg && (cfg.page_number || cfg.scene_number);
var page_change = pn && pn != (this.page_number || 1);
var safe_mode = cfg && cfg.safe_mode;
if (page_change || safe_mode) {
if (page_change)
this.page_number = pn;
if (safe_mode)
this.safe_mode = true;
this.setUrl();
} else {
this.initialized = false;
this.iframe.src = editors_private_info[this.id].url;
}
},
/**
* Remove the editor iframe from the DOM and clean up state. This is required before initializing another editor with the same project
*
* @function remove
* @memberof DSHDLib.Editors.Editor
* @instance
* @example
* editor.remove();
*/
remove: function(){
var me = this;
delete editors[me.id];
delete editors_private_info[me.id];
me.send('CLOSE');
setTimeout(function(){
if (me.iframe.parentNode)
me.iframe.parentNode.removeChild(me.iframe);
}, 0);
},
redirectOnError: function(cfg){
var qs = '';
if (cfg) {
if (typeof cfg == 'string')
cfg = parseQS(cfg);
for (var i in cfg)
qs += '&' + i + '=' + (cfg[i] === true ? '1' : encodeURIComponent(cfg[i]));
}
this.iframe.src = 'https://' + this.domain + '/embederror?r=elib' + qs;
},
subscribe: function(event_name, callback){
this.subscribers[event_name] = this.subscribers[event_name] || [];
if (this.subscribers[event_name].indexOf(callback) == -1)
this.subscribers[event_name].push(callback);
},
unsubscribe: function(event_name, callback){
var i = (this.subscribers[event_name] || []).indexOf(callback);
if (i !== -1)
this.subscribers[event_name].splice(i, 1);
},
send: function(event_name, data, unique, force){
var sendDataFn = function(d){
var f = function(e){
e.iframe.contentWindow.postMessage(d, '*');
var i = e.onInit.indexOf(f);
if (i !== -1)
e.onInit.splice(i, 1);
};
return f;
};
var msg = JSON.stringify(assignObject({}, data || {}, { type: 'DSHD_' + event_name }));
if (this.initialized || force) {
sendDataFn(msg)(this);
} else {
var dupe = !unique && this.onInitDupe[msg];
if (!unique && !dupe)
this.onInitDupe[msg] = true;
if (!dupe)
this.onInit.push(sendDataFn(msg));
}
},
/**
* Callback supplying an error or the new scene properties
*
* @callback addSceneCallback
* @memberof DSHDLib.Editors.Editor
* @param {Error} [error] - An error if the scene addition request failed
* @param {DSHDLib.Editors.ProjectData.SceneData} [scene] - The properties of the new scene and any elements it may contain (e.g. upon clone)
*/
/**
* Add a new scene to the current video project
*
* @function addScene
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {Object} [options]
* @param {string} [options.clone_of_scene_id] - Supply the <i>scene_id</i> to copy from an existing scene or layout (retrieved from {@link DSHDLib.Editors.Editor#getProjectData|getProjectData}). If not supplied a blank scene will be created.
* @param {string|number} [options.position="end"] - One of: "beginning", "end", "before_selection", "after_selection", 0-based numeric position
* @param {string} [options.scene_name] - An optional name for the scene. Defaults to the name of the cloned scene if <i>clone_of_scene_id</i> is provided.
* @param {number} [options.duration=5] - The duration of the new scene in seconds. Defaults to the duration of the cloned scene if <i>clone_of_scene_id</i> is provided.
* @param {DSHDLib.Editors.Editor.addSceneCallback} [callback]
* @example
* editor.addScene();
* @example
* editor.addScene({
* clone_of_scene_id: "abc",
* position: "after_selection",
* scene_name: "Altered Name",
* duration: 3
* }, function(err, scene){
* console.log("New Scene ID:", scene.scene_id);
* });
*/
/**
* Callback supplying an error or the new page properties
*
* @callback addPageCallback
* @memberof DSHDLib.Editors.Editor
* @param {Error} [error] - An error if the page addition request failed
* @param {DSHDLib.Editors.ProjectData.PageData} [page] - The properties of the new page and any elements it may contain (e.g. upon clone)
*/
/**
* Add a new page to the current project
*
* @function addPage
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {Object} [options]
* @param {string} [options.clone_of_page_id] - Supply the <i>page_id</i> to copy from an existing page or layout (retrieved from {@link DSHDLib.Editors.Editor#getProjectData|getProjectData}). If not supplied a blank page will be created
* @param {string|number} [options.position="end"] - One of: "beginning", "end", "before_selection", "after_selection", 0-based numeric position
* @param {string} [options.page_name] - An optional name for the page. Defaults to the name of the cloned page if <i>clone_of_page_id</i> is provided.
* @param {DSHDLib.Editors.Editor.addPageCallback} [callback]
* @example
* editor.addPage();
* @example
* editor.addPage({
* clone_of_page_id: "abc",
* position: "beginnning",
* page_name: "Altered Name"
* }, function(err, page){
* console.log("New Page ID:", page.page_id);
* });
*/
addPage: function(cfg, callback){
if (cfg && !cfg.clone_of_page_id)
cfg = assignObject({}, cfg, { clone_of_page_id: cfg.clone_of_scene_id });
if (cfg && !cfg.page_name)
cfg = assignObject({}, cfg, { page_name: cfg.scene_name });
if (callback) {
cfg.request_id = getRandomId(10);
var cb = function(e, data){
e.unsubscribe('PAGE_DATA-' + cfg.request_id, cb);
callback(null, data);
};
this.subscribe('PAGE_DATA-' + cfg.request_id, cb);
}
this.send('ADD_PAGE', cfg, true);
},
/**
* Import scenes from a different project into the current project
*
* @function importScenes
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {Object} options
* @param {string} options.project_id - The project to import scenes from
* @param {string[]} options.scene_ids - The list of <i>scene_id</i>'s to import from the specified project
* @param {string|number} [options.position="after_selection"] - One of: "beginning", "end", "before_selection", "after_selection", 0-based numeric position
* @example
* editor.importScenes({
* project_id: "abc",
* scene_ids: ["def"],
* position: "end"
* });
*/
/**
* Import pages from a different project into the current project
*
* @function importPages
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {Object} options
* @param {string} options.project_id - The project to import pages from
* @param {string[]} options.page_ids - The list of <i>page_id</i>'s to import from the specified project
* @param {string|number} [options.position="after_selection"] - One of: "beginning", "end", "before_selection", "after_selection", 0-based numeric position
* @example
* editor.importPages({
* project_id: "abc",
* page_ids: ["def"],
* position: "end"
* });
*/
importPages: function(cfg){
if (cfg && !cfg.page_ids)
cfg = assignObject({}, cfg, { page_ids: cfg.scene_ids });
this.send('IMPORT_PAGES', cfg, true);
var f = function(e){
postMessage(JSON.stringify({ project_id: e.project_id, type: 'DSHD_PROJECT_DATA_CHANGE' }), location.origin);
e.onInit.splice(e.onInit.indexOf(f), 1);
};
this.onInit.push(f);
},
/**
* Resize all scenes in the current project
*
* @function resizeScenes
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {Object} options
* @param {number} options.width - Supply the new width in px
* @param {number} options.height - Supply the new height in px
* @example
* editor.resizeScenes({
* width: 1920,
* height: 1080
* });
*/
/**
* Resize pages in the current project
*
* @function resizePage
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {Object} options
* @param {number} options.width - Supply the new width in the unit type of the current project
* @param {number} options.height - Supply the new height in the unit type of the current project
* @param {string} [options.page_id] - Supply the <i>page_id</i> of the page or layout to update (retrieved from {@link DSHDLib.Editors.Editor#getProjectData|getProjectData}). If not supplied, all pages and layouts will be updated
* @example
* editor.resizePage({
* width: 50,
* height: 50,
* page_id: "abc"
* });
*/
resizePage: function(cfg){
this.send('RESIZE_PAGE', cfg, true);
},
/**
* Remove scenes/layouts from the current video project
*
* @function removeScenes
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!string[]} scene_ids - The list of scenes/layouts to remove (usually retrieved via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData}).
* @example
* editor.removeScenes(["abc"]);
*/
/**
* Remove pages/layouts from the current project
*
* @function removePages
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!string[]} page_ids - The list of pages/layouts to remove (usually retrieved via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData}).
* @example
* editor.removePages(["abc"]);
*/
removePages: function(pgs){
this.send('REMOVE_PAGES', { page_ids: pgs }, true);
},
/**
* @namespace NewElements
* @memberof DSHDLib.Editors.ProjectData
*/
/**
* Base type for shared properties when creating all new element types
*
* @typedef BaseElement
* @memberof DSHDLib.Editors.ProjectData.NewElements
* @property {?string} [element_name] - An optional element identifier/label. Must be unique across entire project
* @property {string[]} [element_classes] - An optional list of classes associated with the element
* @property {!string} type - One of: "text", "image", "vector", "video", "audio", "zone"
* @property {string} [page_id] - Override the page on a project (retrieved from {@link DSHDLib.Editors.Editor#getProjectData|getProjectData}) that the element is added to. Places on currently open page by default
* @property {string} [scene_id] - Override the scene on a video project (retrieved from {@link DSHDLib.Editors.Editor#getProjectData|getProjectData}) that the element is added to. Places on currently open scene by default
* @property {bool} [selected=true] - A value indicating whether the new element should be selected on the canvas (removing the existing selection) if it is being added to the currently visible page/scene
*/
/**
* Properties specific to new text elements - See {@link DSHDLib.Editors.ProjectData.NewElements.BaseElement|BaseElement} for common properties
*
* @typedef {DSHDLib.Editors.ProjectData.NewElements.BaseElement} TextElement
* @memberof DSHDLib.Editors.ProjectData.NewElements
* @property {string} text - The actual text to display within the textbox
* @property {number} [width] - The width of the textbox in the unit type of the project
* @property {number} [left] - The x coordinate to place the textbox in the unit type of the project. Horizontally centered if not provided
* @property {number} [top] - The y coordinate to place the textbox in the unit type of the project. Vertically centered if not provided
* @property {string} [color] - The fill [color]{@tutorial Colors} to apply to the new text
* @property {string} [default_style] - One of: "header", "subheader", "body"
* @property {number} [font_file_id] - The specific font (including weight/style) to use for the new text. Will override the <i>default_style</i> if supplied
* @property {number} [font_size] - The font size to use for the new text in <i>pt</i> for print projects or <i>px</i> for all other media types. Will override the <i>default_style</i> if supplied
* @property {string} [align] - The horizontal alignment within the textbox. One of: "left", "center", "right", "justify"
* @property {string} [vertical_anchor] - The anchor as line wrapping occurs. One of: "top", "middle", "bottom"
* @property {number} [opacity] - The opacity percentage to set on the new element, between 0 and 1
* @property {Object.<string, string>} [text_token_map] - A map of tokens that may be present in the text and their respective default replacement values
*/
/**
* Asset reference
*
* @typedef {Object} NewElementAsset
* @memberof DSHDLib.Editors.ProjectData.NewElements
* @property {!string} store - Must be "library"
* @property {number} [asset_id] - The internal asset_id of the image/vector/video/audio asset to place. Must be provided without <i>asset_code</i>
* @property {string} [asset_code] - The id of the image/vector/video/audio asset to place in the originating repository. Must be provided without <i>asset_id</i>
*/
/**
* Properties specific to new image elements - See {@link DSHDLib.Editors.ProjectData.NewElements.BaseElement|BaseElement} for common properties
*
* @typedef {DSHDLib.Editors.ProjectData.NewElements.BaseElement} ImageElement
* @memberof DSHDLib.Editors.ProjectData.NewElements
* @property {string} [url] - Signed/public url or the data url of an image (png, jpg, webp, heic, gif or tiff file) to import. Must be provided without <i>asset</i>
* @property {DSHDLib.Editors.ProjectData.NewElements.NewElementAsset} [asset] - Data reference to a previously imported image. Must be provided without <i>url</i>
* @property {number} [width] - The width of the image in the unit type of the project. Will be based off the height (preserving aspect ratio) if not supplied or sized relative to the canvas if neither are supplied
* @property {number} [height] - The height of the image in the unit type of the project. Will be based off the width (preserving aspect ratio) if not supplied or sized relative to the canvas if neither are supplied
* @property {number} [left] - The x coordinate to place the image in the unit type of the project. Horizontally centered if not provided
* @property {number} [top] - The y coordinate to place the image in the unit type of the project. Vertically centered if not provided
* @property {string} [placement="foreground"] - One of: "foreground", "background"
* @property {number} [opacity] - The opacity percentage to set on the new element, between 0 and 1
*/
/**
* Properties specific to new vector elements - See {@link DSHDLib.Editors.ProjectData.NewElements.BaseElement|BaseElement} for common properties
*
* @typedef {DSHDLib.Editors.ProjectData.NewElements.BaseElement} VectorElement
* @memberof DSHDLib.Editors.ProjectData.NewElements
* @property {!string} vector_type - One of: "shape", "svg", "pdf"
* @property {string} [shape_type] - If <i>vector_type</i> is "shape", one of: "rectangle", "ellipse", "triangle"
* @property {string} [url] - If <i>vector_type</i> is "svg" or "pdf", the signed/public url or the data url of an SVG/PDF to import
* @property {DSHDLib.Editors.ProjectData.NewElements.NewElementAsset} [asset] - If <i>vector_type</i> is "svg", a data reference to a previously imported SVG. Must be provided without <i>url</i> in this case
* @property {number} [width] - The width of the element in the unit type of the project. Will be based off the height (preserving aspect ratio) if not supplied or sized relative to the canvas if neither are supplied
* @property {number} [height] - The height of the element in the unit type of the project. Will be based off the width (preserving aspect ratio) if not supplied or sized relative to the canvas if neither are supplied
* @property {number} [left] - The x coordinate to place the element in the unit type of the project. Horizontally centered if not provided
* @property {number} [top] - The y coordinate to place the element in the unit type of the project. Vertically centered if not provided
* @property {bool} [constrain_proportions] - A value indicating whether by default the new element should allow the user to alter its aspect ratio during resize operations
* @property {string} [color] - If <i>vector_type</i> is "shape", the fill [color]{@tutorial Colors} to apply to the new element
* @property {Object.<string, string>} [color_map] - A map of RGB [colors]{@tutorial Colors} in the SVG to swap with alternate colors in [any supported format]{@tutorial Colors}
* @property {number} [opacity] - The opacity percentage to set on the new element, between 0 and 1
*/
/**
* Properties specific to new video elements - See {@link DSHDLib.Editors.ProjectData.NewElements.BaseElement|BaseElement} for common properties
*
* @typedef {DSHDLib.Editors.ProjectData.NewElements.BaseElement} VideoElement
* @memberof DSHDLib.Editors.ProjectData.NewElements
* @property {string} [url] - Signed/public url of a video file to import. Must be provided without <i>asset</i>
* @property {DSHDLib.Editors.ProjectData.NewElements.NewElementAsset} [asset] - Data reference to a previously imported video. Must be provided without <i>url</i>
* @property {number} [width] - The width of the video in pixels. Will be based off the height (preserving aspect ratio) if not supplied or sized relative to the canvas if neither are supplied
* @property {number} [height] - The height of the video in pixels. Will be based off the width (preserving aspect ratio) if not supplied or sized relative to the canvas if neither are supplied
* @property {number} [left] - The x coordinate to place the video in pixels. Horizontally centered if not provided
* @property {number} [top] - The y coordinate to place the video in pixels. Vertically centered if not provided
* @property {number} [trim_start] - The position in seconds to start the new video
* @property {number} [trim_end] - The position in seconds to end the new video
* @property {bool} [expand_scene] - A value indicating whether the scene should be automatically expanded (if necessary) to fully contain the new video
* @property {string} [placement="foreground"] - One of: "foreground", "background"
* @property {number} [opacity] - The opacity percentage to set on the new element, between 0 and 1
*/
/**
* Properties specific to new audio elements - See {@link DSHDLib.Editors.ProjectData.NewElements.BaseElement|BaseElement} for common properties
*
* @typedef {DSHDLib.Editors.ProjectData.NewElements.BaseElement} AudioElement
* @memberof DSHDLib.Editors.ProjectData.NewElements
* @property {string} [url] - Signed/public url of an audio file to import. Must be provided without <i>asset</i>
* @property {DSHDLib.Editors.ProjectData.NewElements.NewElementAsset} [asset] - Data reference to a previously imported audio track. Must be provided without <i>url</i>
* @property {string} [context="scene"] - One of: "scene", "page", "global"
* @property {string} [track_name] - An optional user-viewable/editable name for the audio track
* @property {number} [trim_start] - Optionally override the starting point, in seconds, of the audio track
* @property {number} [trim_end] - Optionally override the ending point, in seconds, of the audio track
*/
/**
* Properties specific to new zone elements - See {@link DSHDLib.Editors.ProjectData.NewElements.BaseElement|BaseElement} for common properties
*
* @typedef {DSHDLib.Editors.ProjectData.NewElements.BaseElement} ZoneElement
* @memberof DSHDLib.Editors.ProjectData.NewElements
* @property {string} [zone_type="fit"] - One of: "fit", "mask"
* @property {bool} [enforce_boundary=true] - If <i>zone_type</i> is "fit", a value indicating whether the zone should restrict movement of its contained element to its boundary
* @property {number} [width] - The width of the element in the unit type of the project. Will be sized relative to the canvas if not supplied
* @property {number} [height] - The height of the element in the unit type of the project. Will be sized relative to the canvas if not supplied
* @property {number} [left] - The x coordinate to place the element in the unit type of the project. Horizontally centered if not provided
* @property {number} [top] - The y coordinate to place the element in the unit type of the project. Vertically centered if not provided
* @property {number} [opacity] - The default opacity percentage to apply to elements added to the new zone element, between 0 and 1. Note that this can be overridden on the contained element itself
*/
/**
* Callback supplying an error or the new element properties
*
* @callback addElementCallback
* @memberof DSHDLib.Editors.Editor
* @param {Error} [error] - An error if the element addition request failed
* @param {DSHDLib.Editors.ProjectData.ElementProperties} [element] - The full properties of the new element
*/
/**
* Add a new element to a page/scene in the loaded project
*
* @function addElement
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!DSHDLib.Editors.ProjectData.NewElements.BaseElement} element - The element properties to add
* @param {DSHDLib.Editors.Editor.addElementCallback} [callback]
* @example
* editor.addElement({
* element_name: "unique_id",
* element_classes: ["header"],
* type: "text",
* default_style: "header",
* color: "#1eefc9",
* text: "Lorem ipsum...",
* top: 100,
* left: 100,
* width: 200
* }, function(err, element){
* console.log("New Element ID:", element.element_id);
* });
* @example
* editor.addElement({
* page_id: "abc",
* element_classes: ["image"],
* type: "image",
* placement: "foreground",
* url: "https://publicorsignedurl.com/file.jpg",
* top: 100,
* selected: false
* }, function(err, element){
* console.log("New Element Width:", element.width);
* });
* @example
* editor.addElement({
* type: "vector",
* vector_type: "svg",
* url: "data:image/svg+xml;base64, ... ",
* color_map: {
* "#1ccfc9": 'spot("Pantone Jade",84,2,33,1)',
* "#eeffee": "cmyk(5,10,15,20)",
* "#012639": "transparent"
* },
* width: 500
* });
* @example
* editor.addElement({
* type: "video",
* placement: "background",
* asset: { store: "library", asset_code: "abc" },
* top: 100,
* trim_end: 8.5,
* expand_scene: true
* });
* @example
* editor.addElement({
* element_classes: ["music"],
* type: "audio",
* context: "global",
* url: "https://publicorsignedurl.com/track.mp3",
* track_name: "BG Track"
* });
* @example
* editor.addElement({
* element_classes: ["logo_placeholder"],
* type: "zone",
* zone_type: "fit",
* enforce_boundary: true,
* width: 500,
* height: 500,
* top: 100,
* left: 100
* }, function(err, element){
* console.log("New Element ID:", element.element_id);
* });
*/
addElement: function(el, callback){
if (el && el.scene_id && !el.page_id)
el = assignObject({}, el, { page_id: el.scene_id });
var r = { element: el };
if (callback) {
r.request_id = getRandomId(10);
var cb = function(e, data){
e.unsubscribe('ELEMENT_DATA-' + r.request_id, cb);
callback(null, data);
};
this.subscribe('ELEMENT_DATA-' + r.request_id, cb);
}
this.send('ADD_ELEMENT', r, true);
},
/**
* @namespace ElementUpdates
* @memberof DSHDLib.Editors.ProjectData
*/
/**
* An object containing data to populate into the loaded project
*
* @typedef {Object} ProjectUpdateObject
* @memberof DSHDLib.Editors.ProjectData.ElementUpdates
* @property {Object.<string, DSHDLib.Editors.ProjectData.ElementUpdates.ElementUpdate>} elements - A map of data to populate based on <i>element_id</i>/<i>element_name</i> matches. <i>element_id</i> can be retrieved via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData} (e.g. finding all elements with a matching class name)
* @property {bool} [debounce=true] - A value indicating whether any contained text changes should be debounced before save. When implementing real-time text updates via a complementary form controls (sending text changes "onkeyup"), leverage debouncing automatically to avoid queued up save operations. For single updates disable debounce to save more quickly
* @property {bool} [skip_user_interaction=false] - For changes that might traditionally prompt the user for more input (e.g. positioning a new image masked in a selected vector element or determining whether to trim a newly added video vs expand the scene to match it), supply <i>true</i> here to skip these user interactions
* @property {bool} [auto_sync_scene_duration=true] - When the user is not prompted on newly added videos to either trim or expand the scene (e.g. by using the <i>skip_user_interaction</i> property), if necessary the scene duration will be extended to fully contain the new video. Supply <i>false</i> here to instead retain the scene duration as is
*/
/**
* An object containing data to update a matched element.
*
* @typedef {Object} ElementUpdate
* @memberof DSHDLib.Editors.ProjectData.ElementUpdates
* @property {string} [element_name] - An optional unique identifier to assign/update on a matched element. Useful for subsequent {@link DSHDLib.Editors.Editor#updateElements|updateElements} requests that can now reference an element by name. Note that <i>element_name</i> must be unique per element across all elements in a project.
* @property {string[]} [element_classes] - An optional list of classes to associate with each matched element
* @property {string[]} [colors] - An array of [colors]{@tutorial Colors} to update on a matched text, shape or line element. Match the array order of <i>colors</i> retrieved via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData} or {@link DSHDLib.Editors.Editor#handleColorSelection|handleColorSelection}
* @property {string} [text] - The replacement text to fully overwrite a matched text element. Note that this will remove any existing character specific style overrides
* @property {Object} [text_insert] - The text and position of new data to insert/replace in a matched text element. Note that this will preserve existing style overrides with the exception of range replacements that span multiple styles. Useful in conjunction with the <i>cursor</i> information returned from {@link DSHDLib.Editors.Editor#getElementData|getElementData} on selected text elements
* @property {!string} text_insert.text - The new text to insert within the existing text string
* @property {number|number[]} text_insert.position - The 0-based position to insert the new text. Can optionally be supplied as an array containing a start and end index which will overwrite the existing text in that range with the newly supplied <i>text</i>
* @property {number} [width] - The width to resize a matched element to in the unit type of the project. Auto calculates height to preserve aspect ratio if explicit <i>height</i> is not provided on image and svg vector types
* @property {number} [height] - The height to resize a matched image/vector element to in the unit type of the project. Auto calculates width to preserve aspect ratio if explicit <i>width</i> is not provided on image and svg vector types
* @property {number} [left] - The x coordinate to move the top of the matched element to in the unit type of the project
* @property {number} [top] - The y coordinate to move the left side of the matched element to in the unit type of the project
* @property {bool} [flip_horizontal] - A value indicating whether to flip/unflip a matched image element horizontally
* @property {bool} [flip_vertical] - A value indicating whether to flip/unflip a matched image element vertically
* @property {bool} [constrain_proportions] - A value indicating whether to lock the aspect ratio of a matched vector/zone element when resizing
* @property {number} [opacity] - The opacity percentage to set on a matched element, between 0 and 1
* @property {string} [url] - A public or signed url reference or a data url for a svg, pdf, png, jpg, webp, heic, gif or tiff file. This will either be placed in a matched zone or replace a placeholder shape element
* @property {string} [url_file_type] - One of: "pdf", "svg". Only necessary for svg/pdf url's if no file extension is present
* @property {Object} [position_relative_to_original] - Override the default position (relative to the matching zone/shape) for images placed via the "url" property
* @property {string} [position_relative_to_original.horizontal_align] - A horizontal position of "left", "center" or "top"
* @property {string} [position_relative_to_original.vertical_align] - A vertical position of "top", "center" or "bottom"
* @property {Object} [masked_media] - Set or update a masked image in a matched shape element
* @property {string} [masked_media.url] - A public or signed url reference or a data url for a png, jpg, webp, heic, gif or tiff file that will be masked in a matched shape element
* @property {DSHDLib.Editors.ProjectData.ElementUpdates.ElementUpdateAsset} [masked_media.asset] - Data reference to a image/video asset to mask in a matched shape element
* @property {Object} [masked_media.color_overlay] - A color overlay to apply to a matched masked image
* @property {string} [masked_media.color_overlay.color] - The [color]{@tutorial Colors} to apply (RGB/CMYK/SPOT)
* @property {number} [masked_media.color_overlay.percentage] - A number between 0 and 1 representing the intensity of the color overlay
* @property {DSHDLib.Editors.ProjectData.ElementUpdates.ElementUpdateAsset} [asset] - Data reference to an asset to place in a matched zone or replace a placeholder shape element
* @property {Object} [color_overlay] - A color overlay to apply to a matched image element
* @property {string} [color_overlay.color] - The [color]{@tutorial Colors} to apply (RGB/CMYK/SPOT)
* @property {number} [color_overlay.percentage] - A number between 0 and 1 representing the intensity of the color overlay
* @property {bool} [remove_background] - If AI-based image background removal is enabled in your account, this will remove the background of a matched image element when <i>true</i> or re-place the background when <i>false</i>
* @property {Object.<string, DSHDLib.Editors.ProjectData.ElementUpdates.LottieLayerUpdate>} [layers] - A collection of layer specific text/color/image data, keyed off the layer name used in the lottie, to update in a matched lottie element
* @property {number} [trim_start] - The position in seconds to start a matched video element
* @property {number} [trim_end] - The position in seconds to end a matched video element
* @property {number} [font_file_id] - The font to set on a matched text element. Will overwrite any existing partial fonts (e.g. bold/italic)
* @property {number} [font_size] - The font size to set on a matched text element in <i>pt</i> for print projects or <i>px</i> for all other media types. Will override Auto Fit if enabled
* @property {string} [align] - The horizontal alignment on text elements within their textbox. One of: "left", "center", "right", "justify"
* @property {string} [vertical_anchor] - The anchor on text elements as line wrapping occurs. One of: "top", "middle", "bottom"
* @property {bool} [rtl] - Used to switch a textbox between left-to-right (ltr) entry mode <i>false</i> and right-to-left (rtl) entry mode <i>true</i>
* @property {Object.<string, string>} [text_token_map] - A map of tokens that may be present in the text and their respective default replacement values
* @tutorial Colors
*/
/**
* Video asset reference to populate a matched placeholder element
*
* @typedef {Object} ElementUpdateAsset
* @memberof DSHDLib.Editors.ProjectData.ElementUpdates
* @property {!string} asset_type - One of: "image", "vector", "video"
* @property {!string} store - Must be "library"
* @property {number} [asset_id] - The internal asset_id of the asset to place. Must be provided without <i>asset_code</i>
* @property {string} [asset_code] - The id of the asset to place from the originating repository. Must be provided without <i>asset_id</i>
*/
/**
* Layer specific data to update in a matched lottie element
*
* @typedef {Object} LottieLayerUpdate
* @memberof DSHDLib.Editors.ProjectData.ElementUpdates
* @property {string} [text] - The replacement text to fully overwrite the existing text in this layer of the matched lottie element
* @property {string} [color] - The RGB [color]{@tutorial Colors} to apply to this layer of the matched lottie element
* @property {string} [url] - A public or signed url reference or a data url for a svg, png, jpg, webp, heic, gif or tiff file. This will replace the image in this layer of the matched lottie element
* @property {DSHDLib.Editors.ProjectData.ElementUpdates.ElementUpdateAsset} [asset] - Data reference to an image/vector asset to replace the image in this layer of the matched lottie element
*/
/**
* Make one or more changes to the current project
*
* @function changeElements
* @memberof DSHDLib.Editors.Editor
* @instance
* @deprecated Renamed to {@link DSHDLib.Editors.Editor#updateElements|updateElements} for naming consistency
* @param {!DSHDLib.Editors.ProjectData.ElementUpdates.ProjectUpdateObject} object - The list of elements to update
*/
changeElements: function(tco){
this.updateElements(tco);
},
/**
* Make one or more updates to elements in the current project
*
* @function updateElements
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!DSHDLib.Editors.ProjectData.ElementUpdates.ProjectUpdateObject} object - The list of elements to update
* @example
* editor.updateElements({
* elements: {
* "abc": {
* text: "My New Text"
* },
* "def": {
* top: 300,
* colors: ["#efefef"]
* }
* }
* });
*/
updateElements: function(tco){
this.send('CHANGE_ELEMENTS', tco, true);
},
/**
* Callback supplying an error or an array of the newly created elements
*
* @callback cloneElementsCallback
* @memberof DSHDLib.Editors.Editor
* @param {Error} [error] - An error if the clone request failed
* @param {DSHDLib.Editors.ProjectData.ElementProperties[]} [elements] - The list of created elements, returned in the same order as the input array
*/
/**
* Clone one or more elements within a page/scene in the loaded project
*
* @function cloneElements
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!string[]} element_ids - The list of elements to clone (usually retrieved via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData}). Note that the copies made of elements inside containers (zones, vectors) will be placed directly on the canvas, outside of any new/existing container
* @param {DSHDLib.Editors.Editor.cloneElementsCallback} [callback]
* @example
* editor.cloneElements(["abc","def"], function(err, elements){
* console.log("New Element IDs:", elements.map(el => el.element_id).join(", "));
* });
*/
cloneElements: function(els, callback){
var r = { elements: els };
if (callback) {
r.request_id = getRandomId(10);
var cb = function(e, data){
e.unsubscribe('ELEMENTS_DATA-' + r.request_id, cb);
callback(null, data);
};
this.subscribe('ELEMENTS_DATA-' + r.request_id, cb);
}
this.send('CLONE_ELEMENTS', r, true);
},
/**
* Remove elements from the current project
*
* @function removeElements
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!string[]} element_ids - The list of elements to remove (usually retrieved via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData}). Note that elements in zones inherit the zone's <i>element_id</i>. Removing an element in a zone will leave the zone unpopulated. Zones themselves can only be removed if unpopulated. So to fully remove a populated zone, call this method twice in a row supplying the same <i>element_id</i>.
* @example
* editor.removeElements(["abc"]);
*/
removeElements: function(els){
this.send('REMOVE_ELEMENTS', { elements: els }, true);
},
/**
* Wrap a zone around an existing element to make it easily replaceable, maintaining its exact size and position
*
* @function convertToZone
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!string} element_id - The <i>image</i>, <i>video</i> or <i>vector</i> type element to convert to a zone (usually retrieved via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData}). Note that the new zone will inherit the element's <i>element_name</i> and <i>element_classes</i> but will be assigned a new <i>element_id</i>.
* @param {Object} [options]
* @param {string} [options.zone_type="fit"] - Either "fit" or "mask" depending on if contained elements should be sized to fully fit within the zone boundary or be masked to fully fill the zone boundary. Note that {@link DSHDLib.Editors.ProjectData.VectorElementProperties|vector elements} can only be converted to "fit" mode zones.
* @example
* editor.convertToZone("abc");
*/
convertToZone: function(element_id, cfg){
this.send('CONVERT_TO_ZONE', assignObject({}, cfg || {}, { element_id: element_id }), true);
},
/**
* Update the background color of one or more pages/scenes. Note that {@link DSHDLib.Editors.Editor#addElement|addElement} with "background" <i>placement</i> can be used to set a background image/video instead
*
* @function updateBackgroundColor
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!Object} options
* @param {!string} options.color - The [color]{@tutorial Colors} to use for the background
* @param {string} [options.page_id] - Supply the <i>page_id</i> of the page or layout to update (retrieved from {@link DSHDLib.Editors.Editor#getProjectData|getProjectData}). If not supplied, all pages and layouts will be updated
* @param {string} [options.scene_id] - Supply the <i>scene_id</i> of the scene to update (retrieved from {@link DSHDLib.Editors.Editor#getProjectData|getProjectData}). If not supplied, all scenes will be updated
* @example
* editor.updateBackgroundColor({ color: "#ffffff" });
*/
updateBackgroundColor: function(cfg){
if (cfg && cfg.scene_id && !cfg.page_id)
cfg = assignObject({}, cfg, { page_id: cfg.scene_id });
this.send('UPDATE_BACKGROUND_COLOR', cfg, true);
},
/**
* Set a die cut border for stickers. Must be using a custom canvas shape on a Print project. Applied as an outer stroke
*
* @function updateCanvasBorder
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!Object} options
* @param {number} [options.width] - The width of the border/stroke in the unit type of the project. Required when creating a new border
* @param {string} [options.color] - The [color]{@tutorial Colors} to use for the border/stroke
* @param {bool} [options.rounded] - A value indicating whether the border/stroke will be rounded around the edges of the canvas shape
* @example
* editor.updateCanvasBorder({ width: 0.25, color: "cmyk(0,0,0,0)" });
*/
updateCanvasBorder: function(cfg){
this.send('UPDATE_CANVAS_STROKE', cfg, true);
},
/**
* Select one or more elements on the currently visible page/scene
*
* @function selectElements
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!string[]} element_ids - The list of elements to select (usually retrieved via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData})
* @param {Object} [options]
* @param {bool} [options.deselect=false] - Supply <i>true</i> to deselect any of the supplied elements that are currently selected
* @example
* editor.selectElements(["abc"]);
* @example
* editor.selectElements(["abc","def"], { deselect: true });
*/
selectElements: function(els, cfg){
this.send('SELECT_ELEMENTS', assignObject({}, cfg || {}, { element_ids: els }), true);
},
/**
* Change the scene currently visible in the editor (for projects with a video media type)
*
* @function changeScene
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!Object} options
* @param {!string} options.scene_id - The scene to change to (ID retrievable via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData} or {@link https://api.designhuddle.com/user#tag/Projects/paths/~1projects~1{project_id}/get|API})
* @example
* editor.changeScene({ scene_id: "abc" });
*/
/**
* Change the page currently visible in the editor (for projects with a digital or print media type)
*
* @function changePage
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!Object} options
* @param {!string} options.page_id - The page to change to (ID retrievable via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData} or {@link https://api.designhuddle.com/user#tag/Projects/paths/~1projects~1{project_id}/get|API})
* @example
* editor.changePage({ page_id: "abc" });
*/
changePage: function(cfg){
if (cfg && !cfg.page_id)
cfg = assignObject({}, cfg, { page_id: cfg.scene_id || cfg.slide_id });
this.send('CHANGE_PAGE', cfg, true);
},
/**
* Change the currently displayed product image background. {@link https://api.designhuddle.com/doc/Product_Image_Backgrounds.pdf|Read full documentation here}
*
* @function changeProductImage
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!Object} options
* @param {!string} options.product_image_url - The url of the image to show. Must match the width and height of the currently displayed image.
* @example
* editor.changeProductImage({ product_image_url: "https://publicorsignedurl.com/image.jpg" });
*/
changeProductImage: function(cfg){
this.send('CHANGE_PRODUCT_IMAGE', cfg, true);
},
/**
* Change the temporary background image or color. Great for simulating paper color/material on print projects. Will be masked inside the canvas (unlike images set via {@link DSHDLib.Editors.Editor#changeProductImage|changeProductImage}), and visible behind the actual (saved) project background, so make that transparent in the template/project to use this feature.
*
* @function changeTemporaryBackground
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!Object} options
* @param {string} [options.color] - The color to display in hex format. Required if <i>image_url</i> is not provided
* @param {string} [options.image_url] - The url of the image to show. Must be added to CSP allowed origin list. Required if <i>color</i> is not provided
* @example
* editor.changeTemporaryBackground({ color: "#FF0000" });
* @example
* editor.changeTemporaryBackground({ image_url: "https://publicorsignedurl.com/image.jpg" });
*/
changeTemporaryBackground: function(cfg){
this.send('CHANGE_TEMPORARY_BACKGROUND', cfg, true);
},
/**
* Change the value used to filter a third party media gallery
*
* @function changeGalleryID
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!string} id - The filter value
* @param {string} [gallery] - The gallery to filter (required if multiple custom galleries connected)
* @example
* editor.changeGalleryID("abc");
*/
changeGalleryID: function(id, gallery){
this.send('CHANGE_GALLERY_ID', { gallery_id: id, gallery: gallery }, true);
},
handleExitEditor: function(callback){
this.subscribe('EXIT_PROJECT', callback);
},
/**
* Receive a notification upon any network connection issues or other unresolvable errors that render the Editor unusable
*
* @function handleError
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!DSHDLib.Editors.Editor.editorCallback} callback
* @example
* editor.handleError(function(e){
* console.log("Error occurred");
* });
*/
handleError: function(callback){
this.subscribe('HANDLE_ERROR', callback);
},
/**
* Receive a notification when the access token being used has expired. You may then request a new access token and update the editor
*
* @function handleUnauthenticated
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!DSHDLib.Editors.Editor.editorCallback} callback
* @example
* editor.handleUnauthenticated(function(e){
* //retrieve new access token
* e.refreshAccessToken("new_token");
* });
*/
handleUnauthenticated: function(callback){
this.subscribe('SIGN_OUT', callback);
},
/**
* Callback supplying the editor instance a reload was triggered on and the reload config
*
* @callback handleReloadCallback
* @memberof DSHDLib.Editors.Editor
* @param {DSHDLib.Editors.Editor} editor - The editor instance triggering the event
* @param {Object} data - Reload configuration data
*/
/**
* Override the default behavior when the editor needs to reload
*
* @function handleReload
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!DSHDLib.Editors.Editor.handleReloadCallback} callback
* @example
* editor.handleReload(function(e, cfg){
* console.log("Editor Reloading");
* e.reload(cfg);
* });
*/
handleReload: function(callback){
this.subscribe('RELOAD', callback);
},
/**
* Callback supplying the editor instance the page change was triggered on and the id of the newly open page
*
* @callback handlePageChangeCallback
* @memberof DSHDLib.Editors.Editor
* @param {DSHDLib.Editors.Editor} editor - The editor instance triggering the event
* @param {Object} data
* @param {string} data.page_id - The id of the newly open page
*/
/**
* Receive a notification when the page is changed in the editor. Useful for displaying page-specific contextual info outside the editor
*
* @function handlePageChange
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!DSHDLib.Editors.Editor.handlePageChangeCallback} callback
* @example
* editor.handlePageChange(function(e, data){
* console.log("New Page ID:", data.page_id);
* });
*/
handlePageChange: function(callback){
this.subscribe('PAGE_CHANGE', callback);
},
/**
* Callback supplying the editor instance the scene change was triggered on and the id of the newly open scene
*
* @callback handleSceneChangeCallback
* @memberof DSHDLib.Editors.Editor
* @param {DSHDLib.Editors.Editor} editor - The editor instance triggering the event
* @param {Object} data
* @param {string} data.scene_id - The id of the newly open scene
*/
/**
* Receive a notification when the scene is changed in the editor. Useful for displaying scene-specific contextual info outside the editor
*
* @function handleSceneChange
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!DSHDLib.Editors.Editor.handleSceneChangeCallback} callback
* @example
* editor.handleSceneChange(function(e, data){
* console.log("New Scene ID:", data.scene_id);
* });
*/
handleSceneChange: function(callback){
var cb = function(e, data){
data.scene_id = data.page_id;
delete data.page_id;
callback(e, data);
};
this.subscribe('PAGE_CHANGE', cb);
},
/**
* Callback supplying the editor instance the project change was triggered on and the id of the new project
*
* @callback handleProjectChangeCallback
* @memberof DSHDLib.Editors.Editor
* @param {DSHDLib.Editors.Editor} editor - The editor instance triggering the event
* @param {Object} data
* @param {string} data.project_id - The unique identifier of the new project
*/
/**
* Override the default behavior when the project context is changed based on an action taken within the editor (e.g. clone to new size/media type)
*
* @function handleProjectChange
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!DSHDLib.Editors.Editor.handleProjectChangeCallback} callback
* @example
* editor.handleProjectChange(function(e, data){
* console.log("New Project ID:", data.project_id);
* e.changeProject(data.project_id);
* });
*/
handleProjectChange: function(callback){
this.subscribe('PROJECT_CHANGE', callback);
},
/**
* @namespace ProjectData
* @memberof DSHDLib.Editors
*/
/**
* Object containing all properties and metadata related to the project and all its contained pages/scenes and ther contained elements
*
* @typedef ProjectData
* @memberof DSHDLib.Editors.ProjectData
* @property {string} project_id - The unique identifer associated with the project
* @property {string} project_title - The current title of the project. Defaults to the template title if not supplied during project creation.
* @property {string} media_type - One of: "digital", "print", "video", "presentation"
* @property {string[]} tags - A list of tags associated with the project
* @property {Object} [metadata] - Any metadata assigned to the project {@link https://api.designhuddle.com/user#update-project|programmatically}
* @property {string} [approval_status] - If previously submitted for approval, one of: "approved", "denied", "awaiting_revisions", "pending_review"
* @property {DSHDLib.Editors.ProjectData.PageData[]} [pages] - A list of all pages the their contained elements for digital, print and presentation projects
* @property {DSHDLib.Editors.ProjectData.SceneData[]} [scenes] - A list of all scenes the their contained elements for video projects
* @property {number} [duration] - The total duration of a video project in seconds
* @property {DSHDLib.Editors.ProjectData.PageData[]|DSHDLib.Editors.ProjectData.SceneData[]} [layouts] - A list of all pages/scenes stored as available layouts
* @property {DSHDLib.Editors.ProjectData.AudioTrack[]} [global_audio_tracks] - A list of all audio tracks not associated with a specific scene on a video project
* @property {Object.<string, DSHDLib.Editors.ProjectData.SceneSlideGroup>} [scene_slide_groups] - A list of groups associated with scene slides on a video or presenation project
* @property {string} [current_page_id] - The currently open page on digital, print and presentation projects
* @property {string} [current_scene_id] - The currently open page on video projects
* @property {bool} unsaved_locking_config - Value indicating whether any modifications to the locking config have been saved for admin context uses
*/
/**
* Object containing all properties of a page in a project and all its contained elements
*
* @typedef PageData
* @memberof DSHDLib.Editors.ProjectData
* @property {string} page_id - The unique identifer automatically assigned to the page upon creation
* @property {Object} dimensions
* @property {number} dimensions.width - The width of the page in the unit type returned
* @property {number} dimensions.height - The height of the page in the unit type returned
* @property {string} dimensions.unit_type_id - One of: "px", "in", "mm"
* @property {string} [group_id] - The scene slide group this page is associated with on a presentation project. Reference this id in <i>scene_slide_groups</i> in {@link DSHDLib.Editors.ProjectData.ProjectData|ProjectData}
* @property {string} [masking_vector_element_id] - A reference to a vector element that is masking the entire canvas
* @property {string} [background_element_id] - A reference to an image/video element acting as the special background layer
* @property {string} [background_color] - The [color]{@tutorial Colors} filling the background layer
* @property {string[]} background_classes - An optional list of user-assigned classes associated with the background layer of this page
* @property {Object} [canvas_border] - The border (outer stroke) applied to a custom shaped canvas (with a <i>masking_vector_element_id</i>). Set via {@link DSHDLib.Editors.Editor.updateCanvasBorder|updateCanvasBorder}
* @property {number} canvas_border.width - The width of the border/stroke in the unit type of the project
* @property {string} canvas_border.color - The [color]{@tutorial Colors} of the border/stroke
* @property {bool} canvas_border.rounded - A value indicating whether the border/stroke is rounded around the edges of the canvas shape
* @property {Object.<string, DSHDLib.Editors.ProjectData.ElementProperties|DSHDLib.Editors.ProjectData.AudioTrack>} elements - The list of elements contained in the page
*/
/**
* Object containing all properties of a scene in a project and all its contained elements
*
* @typedef SceneData
* @memberof DSHDLib.Editors.ProjectData
* @property {string} scene_id - The unique identifer automatically assigned to the scene upon creation
* @property {Object} dimensions
* @property {number} dimensions.width - The width of the scene in pixels
* @property {number} dimensions.height - The height of the scene in pixels
* @property {string} dimensions.unit_type_id - Always "px"
* @property {string} [group_id] - The scene slide group this scene is associated with. Reference this id in <i>scene_slide_groups</i> in {@link DSHDLib.Editors.ProjectData.ProjectData|ProjectData}
* @property {number} duration - The duration of the scene in seconds
* @property {string} [background_element_id] - A reference to an image/video element acting as the special background layer
* @property {string} [background_color] - The [color]{@tutorial Colors} filling the background layer
* @property {string[]} background_classes - An optional list of user-assigned classes associated with the background layer of this scene
* @property {Object.<string, DSHDLib.Editors.ProjectData.ElementProperties|DSHDLib.Editors.ProjectData.AudioTrack>} elements - The list of elements contained in the scene
*/
/**
* Object containing all properties of audio tracks
*
* @typedef AudioTrack
* @memberof DSHDLib.Editors.ProjectData
* @property {string} element_id - The unique identifer automatically assigned to the audio track upon creation
* @property {bool} template_element - Value indicating whether this element was part of a template or added by the user
* @property {string} type - Always "audio"
* @property {string} track_name - An optional user-assigned name
* @property {number} duration - The duration of the track in seconds
* @property {number} delay - The delay before the track starts in seconds, relative to its containing scene/page or the video/presentation as a whole if global
* @property {number} [trim_start] - The start of the trimmed clip relative to the full duration. Only included if the audio has been trimmed
* @property {number} [trim_end] - The end of the trimmed clip relative to the full duration. Only included if the audio has been trimmed
* @property {number} asset_id - The internal asset_id of the file
* @property {string} [asset_code] - The id of the file in the originating repository
* @property {number} [import_progress] - If the imported audio is immediately available for use in the editor, but still requires asynchronous backend import, this shows its import progress percentage - from 0 to 1
*/
/**
* Object containing metadata about scene slide groups
*
* @typedef SceneSlideGroup
* @memberof DSHDLib.Editors.ProjectData
* @property {string} group_name - An optional user-assigned group name
* @property {number} [duration] - The duration of the scene slide group in seconds. Overrides its contained scene duration sum if not evenly split
*/
/**
* Base type for shared properties across all element types
*
* @typedef ElementProperties
* @memberof DSHDLib.Editors.ProjectData
* @property {string} [element_id] - The unique identifer automatically assigned to the element upon creation.
* @property {?string} element_name - An optional user-assigned name
* @property {string[]} element_classes - An optional list of user-assigned classes
* @property {bool} template_element - Value indicating whether this element was part of a template or added by the user
* @property {string} type - One of: "text", "image", "vector", "video", "lottie", "zone"
* @property {number} width - The width of the element in the unit type of the project
* @property {number} height - The height of the element in the unit type of the project
* @property {number} [angle] - The rotation degrees. Excluded if not rotated
* @property {?number} left - The x coordinate of the top of the element in the unit type of the project. Null when rotated, flipped or cropped
* @property {?number} top - The y coordinate of the top of the element in the unit type of the project. Null when rotated, flipped or cropped
* @property {Object} [bounding_box] - Coordinates of a box fully containing the element. Only included if rotated, flipped or cropped
* @property {number} bounding_box.top - The y coordinate of the top of the box in the unit type of the project
* @property {number} bounding_box.right - The x coordinate of the right side of the box in the unit type of the project
* @property {number} bounding_box.bottom - The y coordinate of the bottom of the box in the unit type of the project
* @property {number} bounding_box.left - The x coordinate of the left side of the box in the unit type of the project
* @property {number} opacity - The percentage opacity of the element, between 0 and 1
* @property {bool} [selected] - Value indicating whether this element is currently selected in the editor
*/
/**
* Properties for fonts associated with text elements
*
* @typedef DSHDLib.Editors.ProjectData.FontProperties
* @memberof DSHDLib.Editors.ProjectData
* @property {number} [font_file_id] - The id of the specific font variation. Not present on "project" specific fonts (e.g. partial fonts extracted from imported files)
* @property {string} font_type - One of: "system", "partner", "brand", "user", "pdf", "project"
* @property {string} name - The name of the font family or specific font variation
* @property {string} [weight] - "Bold" if a bold variation in a font family
* @property {number} [style] - "Italic" if an italic variation in a font family
*/
/**
* Properties specific to text elements - See {@link DSHDLib.Editors.ProjectData.ElementProperties|ElementProperties} for common properties
*
* @typedef {DSHDLib.Editors.ProjectData.ElementProperties} TextElementProperties
* @memberof DSHDLib.Editors.ProjectData
* @property {string} text - The actual text typed within the textbox
* @property {string[]} colors - All fill and stroke colors applied to the text
* @property {Object} font_size - The current size of the font
* @property {number} font_size.size - The actual size value
* @property {string} font_size.unit_type_id - One of: "px", "pt"
* @property {DSHDLib.Editors.ProjectData.FontProperties[]} fonts - All fonts applied to the text (e.g. regular, bold, italic)
* @property {number} line_height - A value which, when multiplied by the <i>font_size</i>, indicates the height of each line of text
* @property {number} letter_spacing - A value indicating the spacing between characters, measured in 1/1000 em, which is relative to the font size
* @property {string} align - The alignment of the text within the textbox width
* @property {Object} [curve] - Information about the curve of the text, if curved
* @property {bool} curve.inverse - A value indicating whether the curve is inversed
* @property {number} line_count - The number of lines in the textbox
* @property {number} longest_line_width - The width of the longest line in the unit type of the project
* @property {Object.<string, string>} [text_token_map] - A map of tokens that may be present in the text and their respective default replacement values
* @property {Object} [cursor] - Information about the position of the cursor and selected range of text in a currently selected text element. Useful in creating real-time text manipulation experiences in conjunction with the <i>text_insert</i> property in {@link DSHDLib.Editors.Editor#updateElements|updateElements}
* @property {number} cursor.position - The 0-based index of the cursor location within the text
* @property {number[]} [cursor.selected_range] - The start and end index of the selected text when a selection is active
*/
/**
* Properties specific to image elements - See {@link DSHDLib.Editors.ProjectData.ElementProperties|ElementProperties} for common properties
*
* @typedef {DSHDLib.Editors.ProjectData.ElementProperties} ImageElementProperties
* @memberof DSHDLib.Editors.ProjectData
* @property {string} file_type - The file extension of the source image (e.g. "png", "jpg", "webp", "heic", "tiff")
* @property {number} source_width - The full width of the source image in pixels
* @property {number} source_height - The full height of the source image in pixels
* @property {bool} [asset_opaque] - If the image has been fully analyzed (true for all recently added images), this property will indicate whether or not any pixels with transparency exist
* @property {number} asset_id - The internal asset_id of the file
* @property {string} [asset_code] - The id of the file in the originating repository
* @property {DSHDLib.Editors.ProjectData.ContainerElementZoneProperties} [zone] - Zone information if this element is inside of a zone
* @property {DSHDLib.Editors.ProjectData.ContainerElementVectorProperties} [masking_vector] - Vector information if this element is masked in a vector
*/
/**
* Properties specific to vector elements - See {@link DSHDLib.Editors.ProjectData.ElementProperties|ElementProperties} for common properties
*
* @typedef {DSHDLib.Editors.ProjectData.ElementProperties} VectorElementProperties
* @memberof DSHDLib.Editors.ProjectData
* @property {string} vector_type - One of: "line", "shape", "svg", "pdf"
* @property {string} [shape_type] - If <i>vector_type</i> is "shape", one of: "rectangle", "ellipse", "triangle"
* @property {string[]} colors - All fill and stroke colors applied to the element
* @property {number} path_count - The number of paths in the vector
* @property {number} [asset_id] - The internal asset_id of the file for imported vectors
* @property {string} [asset_code] - The id of the file in the originating repository
* @property {DSHDLib.Editors.ProjectData.ContainerElementZoneProperties} [zone] - Zone information if this element is inside of a zone
*/
/**
* Properties specific to video elements - See {@link DSHDLib.Editors.ProjectData.ElementProperties|ElementProperties} for common properties
*
* @typedef {DSHDLib.Editors.ProjectData.ElementProperties} VideoElementProperties
* @memberof DSHDLib.Editors.ProjectData
* @property {number} duration - The duration of the full video in seconds
* @property {number} source_width - The full width of the source video
* @property {number} source_height - The full height of the source video
* @property {bool} audible - Value indicating whether the video has an audio track
* @property {number} speed - The speed factor of the video
* @property {bool} loop - Value indicating whether the video is set to loop
* @property {number} [trim_start] - The start of the trimmed clip relative to the full duration. Only included if the video has been trimmed
* @property {number} [trim_end] - The end of the trimmed clip relative to the full duration. Only included if the video has been trimmed
* @property {number} asset_id - The internal asset_id of the file
* @property {string} [asset_code] - The id of the file in the originating repository
* @property {number} [import_progress] - If the imported video is immediately available for use in the editor, but still requires asynchronous backend import, this shows its import progress percentage - from 0 to 1
* @property {DSHDLib.Editors.ProjectData.ContainerElementZoneProperties} [zone] - Zone information if this element is inside of a zone
* @property {DSHDLib.Editors.ProjectData.ContainerElementVectorProperties} [masking_vector] - Vector information if this element is masked in a vector
*/
/**
* Properties for text fields in lottie elements
*
* @typedef DSHDLib.Editors.ProjectData.LottieTextProperties
* @memberof DSHDLib.Editors.ProjectData
* @property {string} text - The actual text displayed
* @property {DSHDLib.Editors.ProjectData.FontProperties} font - The font applied to this text
* @property {number} letter_spacing - The letter spacing in pixels
*/
/**
* Properties for embedded images in lottie elements
*
* @typedef DSHDLib.Editors.ProjectData.LottieImageProperties
* @memberof DSHDLib.Editors.ProjectData
* @property {number} asset_id - The internal asset_id of the file
* @property {string} [asset_code] - The id of the file in the originating repository
* @property {number} initial_source_width - The width of the original embedded image in pixels
* @property {number} initial_source_height - The height of the original embedded image in pixels
*/
/**
* Properties for layers in lottie elements
*
* @typedef DSHDLib.Editors.ProjectData.LottieLayerProperties
* @memberof DSHDLib.Editors.ProjectData
* @property {number} [color_index] - The color used in this layer referencing the <i>colors</i> array
* @property {number} [text_index] - The text used in this layer referencing the <i>text</i> array
* @property {number} [image_index] - The image used in this layer referencing the <i>images</i> array
*/
/**
* Properties specific to lottie elements - See {@link DSHDLib.Editors.ProjectData.ElementProperties|ElementProperties} for common properties
*
* @typedef {DSHDLib.Editors.ProjectData.ElementProperties} LottieElementProperties
* @memberof DSHDLib.Editors.ProjectData
* @property {number} duration - The duration of the lottie in seconds
* @property {number} speed - The speed factor of the lottie
* @property {bool} loop - Value indicating whether the lottie is set to loop
* @property {number} source_width - The full width of the source lottie
* @property {number} source_height - The full height of the source lottie
* @property {string[]} colors - All colors used in the lottie
* @property {DSHDLib.Editors.ProjectData.LottieTextProperties[]} text - All text used in the lottie
* @property {DSHDLib.Editors.ProjectData.LottieImageProperties[]} [images] - All images used in the lottie if any are present
* @property {Object.<string, DSHDLib.Editors.ProjectData.LottieLayerProperties>} layers - The list of layers contained in the lottie keyed off the layer name used in the lottie
* @property {number} asset_id - The internal asset_id of the file
* @property {string} [asset_code] - The id of the file in the originating repository
* @property {DSHDLib.Editors.ProjectData.ContainerElementZoneProperties} [zone] - Zone information if this element is inside of a zone
*/
/**
* Properties specific to (unpopulated) zone elements - See {@link DSHDLib.Editors.ProjectData.ElementProperties|ElementProperties} for common properties. Note that when zones are populated they become part of their contained element ({@link DSHDLib.Editors.ProjectData.ImageElementProperties|image}, {@link DSHDLib.Editors.ProjectData.VectorElementProperties|vector}, {@link DSHDLib.Editors.ProjectData.VideoElementProperties|video}) as its "zone" property
*
* @typedef {DSHDLib.Editors.ProjectData.ElementProperties} ZoneElementProperties
* @memberof DSHDLib.Editors.ProjectData
* @property {string} zone_type - Either "fit" or "mask" depending on if contained elements will be sized to fully fit within the zone boundary or be masked to fully fill the zone boundary. Note that {@link DSHDLib.Editors.ProjectData.VectorElementProperties|vector elements} can only be added to "fit" mode zones.
* @property {bool} [enforce_boundary] - If <i>zone_type</i> is "fit", a value indicating whether the zone restricts movement of its contained element to its boundary
*/
/**
* Properties specific to zone elements referenced as a container on vectors/images/videos
*
* @typedef ContainerElementZoneProperties
* @memberof DSHDLib.Editors.ProjectData
* @property {string} zone_type - Either "fit" or "mask" depending on if contained element is sized to fully fit within the zone boundary or masked to fully fill the zone boundary
* @property {bool} [enforce_boundary] - If <i>zone_type</i> is "fit", a value indicating whether the zone restricts movement of its contained element to its boundary
* @property {number} width - The width of the zone in the unit type of the project
* @property {number} height - The height of the zone in the unit type of the project
* @property {number} [angle] - The rotation degrees. Excluded if not rotated
* @property {?number} left - The x coordinate of the top of the zone in the unit type of the project. Null when rotated
* @property {?number} top - The y coordinate of the top of the zone in the unit type of the project. Null when rotated
* @property {Object} [bounding_box] - Coordinates of a box fully containing the zone. Only included if rotated
* @property {number} bounding_box.top - The y coordinate of the top of the box in the unit type of the project
* @property {number} bounding_box.right - The x coordinate of the right side of the box in the unit type of the project
* @property {number} bounding_box.bottom - The y coordinate of the bottom of the box in the unit type of the project
* @property {number} bounding_box.left - The x coordinate of the left side of the box in the unit type of the project
*/
/**
* Properties specific to vector elements referenced as a container on images/videos
*
* @typedef ContainerElementVectorProperties
* @memberof DSHDLib.Editors.ProjectData
* @property {string} vector_type - One of: "shape", "svg"
* @property {string} [shape_type] - If <i>vector_type</i> is "shape", one of: "rectangle", "ellipse", "triangle"
* @property {number} path_count - The number of paths in the vector
* @property {number} width - The width of the vector in the unit type of the project
* @property {number} height - The height of the vector in the unit type of the project
* @property {number} [angle] - The rotation degrees. Excluded if not rotated
* @property {?number} left - The x coordinate of the top of the vector in the unit type of the project. Null when rotated or flipped
* @property {?number} top - The y coordinate of the top of the vector in the unit type of the project. Null when rotated or flipped
* @property {Object} [bounding_box] - Coordinates of a box fully containing the zone. Only included if rotated or flipped
* @property {number} bounding_box.top - The y coordinate of the top of the box in the unit type of the project
* @property {number} bounding_box.right - The x coordinate of the right side of the box in the unit type of the project
* @property {number} bounding_box.bottom - The y coordinate of the bottom of the box in the unit type of the project
* @property {number} bounding_box.left - The x coordinate of the left side of the box in the unit type of the project
* @property {string} [background_color] - The [color]{@tutorial Colors} filling the vector if/when the vector or its contained element is semi-transparent
* @property {number} [asset_id] - The internal asset_id of the file for imported vectors
* @property {string} [asset_code] - The id of the file in the originating repository
*/
/**
* Callback supplying the editor instance the element data request was triggered on and the element properties
*
* @callback handleElementDataCallback
* @memberof DSHDLib.Editors.Editor
* @param {DSHDLib.Editors.Editor} editor - The editor instance triggering the event
* @param {DSHDLib.Editors.ProjectData.ElementProperties} element - The properties of the requested element
*/
/**
* Receive information from every element data request
*
* @function handleElementData
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!DSHDLib.Editors.Editor.handleElementDataCallback} callback
* @example
* editor.handleElementData(function(e, element){
* console.log("Element Type:", element.type);
* });
*/
handleElementData: function(callback){
this.subscribe('ELEMENT_DATA', callback);
},
/**
* Callback supplying the editor instance the scene data request was triggered on and the scene properties
*
* @callback handleSceneDataCallback
* @memberof DSHDLib.Editors.Editor
* @param {DSHDLib.Editors.Editor} editor - The editor instance triggering the event
* @param {DSHDLib.Editors.ProjectData.SceneData} scene - All scene properties and its individual element properties
*/
/**
* Receive information from every scene data request on a video project
*
* @function handleSceneData
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!DSHDLib.Editors.Editor.handleSceneDataCallback} callback
* @example
* editor.handleSceneData(function(e, scene){
* console.log("Width:", scene.dimensions.width);
* });
*/
handleSceneData: function(callback){
this.handlePageData(callback);
},
/**
* Callback supplying the editor instance the page data request was triggered on and the page properties
*
* @callback handlePageDataCallback
* @memberof DSHDLib.Editors.Editor
* @param {DSHDLib.Editors.Editor} editor - The editor instance triggering the event
* @param {DSHDLib.Editors.ProjectData.PageData} page - All page properties and its individual element properties
*/
/**
* Receive information from every page data request
*
* @function handlePageData
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!DSHDLib.Editors.Editor.handlePageDataCallback} callback
* @example
* editor.handlePageData(function(e, page){
* console.log("Width:", page.dimensions.width);
* });
*/
handlePageData: function(callback){
this.subscribe('PAGE_DATA', callback);
},
/**
* Callback supplying the editor instance the element data request was triggered on and the project properties
*
* @callback handleProjectDataCallback
* @memberof DSHDLib.Editors.Editor
* @param {DSHDLib.Editors.Editor} editor - The editor instance triggering the event
* @param {DSHDLib.Editors.ProjectData.ProjectData} project - All project metadata, page/scene properties and individual element properties
*/
/**
* Receive information from every project data request
*
* @function handleProjectData
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!DSHDLib.Editors.Editor.handleProjectDataCallback} callback
* @example
* editor.handleProjectData(function(e, project){
* console.log("Project Title:", project.project_title);
* });
*/
handleProjectData: function(callback){
this.subscribe('PROJECT_DATA', callback);
},
/**
* Properties specific to modified elements referenced in {@link DSHDLib.Editors.Editor.handleProjectDataModificationCallback|handleProjectDataModificationCallback}
*
* @typedef ModifiedElement
* @memberof DSHDLib.Editors.Editor
* @property {!string} element_modification_type - One of: "add", "update", "remove"
*/
/**
* Properties specific to modified pages referenced in {@link DSHDLib.Editors.Editor.handleProjectDataModificationCallback|handleProjectDataModificationCallback}
*
* @typedef ModifiedPage
* @memberof DSHDLib.Editors.Editor
* @property {string} [page_modification_type] - One of: "add", "update", "remove", "reorder"
* @property {Object.<string, DSHDLib.Editors.Editor.ModifiedElement>} [elements] - A map of the elements that were modified on this page. Note that this won't be included with <i>page_modification_type</i> add/remove, so call {@link DSHDLib.Editors.Editor#getProjectData|getProjectData} in those scenarios to retrieve newly added elements (from page clone/import/create from layout) or elements removed as part of a page removal.
*/
/**
* Properties specific to modified scenes referenced in {@link DSHDLib.Editors.Editor.handleProjectDataModificationCallback|handleProjectDataModificationCallback}
*
* @typedef ModifiedScene
* @memberof DSHDLib.Editors.Editor
* @property {string} [scene_modification_type] - One of: "add", "update", "remove", "reorder"
* @property {Object.<string, DSHDLib.Editors.Editor.ModifiedElement>} [elements] - A map of the elements that were modified on this scene. Note that this won't be included with <i>scene_modification_type</i> add/remove, so call {@link DSHDLib.Editors.Editor#getProjectData|getProjectData} in those scenarios to retrieve newly added elements (from scene clone/import/create from layout) or elements removed as part of a scene removal.
*/
/**
* Callback supplying the editor instance the update occurred on and the specifics of the update
*
* @callback handleProjectDataModificationCallback
* @memberof DSHDLib.Editors.Editor
* @param {DSHDLib.Editors.Editor} editor - The editor instance triggering the event
* @param {Object} updates
* @param {Object.<string, DSHDLib.Editors.Editor.ModifiedPage>} [updates.pages] - A map of the pages, on non-video projects, that were modified or had elements on them modified
* @param {Object.<string, DSHDLib.Editors.Editor.ModifiedScene>} [updates.scenes] - A map of the scenes, on video projects, that were modified or had elements on them modified
* @param {Object.<string, DSHDLib.Editors.Editor.ModifiedElement>} [updates.global_audio_tracks] - A map of the global audio track elements, on video projects, that were modified
* @param {bool} [updates.system] - If <i>true</i>, indicates that the update triggering the event was a non-user-initiated conversion/correction alteration, only occurring in specific scenarios
*/
/**
* Receive notification when any change to the project data occurs within the editor. Useful to trigger {@link DSHDLib.Editors.Editor#getProjectData|getProjectData} for validation routines
*
* @function handleProjectDataChange
* @memberof DSHDLib.Editors.Editor
* @instance
* @deprecated Renamed to {@link DSHDLib.Editors.Editor#handleProjectDataModification|handleProjectDataModification} for naming consistency
* @param {!DSHDLib.Editors.Editor.handleProjectDataModificationCallback} callback
* @param {object} [options]
* @param {bool} [options.user_modifications_only=false] - Supply <i>true</i> to only return changes an actual user makes, ignoring other automatic conversion/correction alterations made in specific scenarios
*/
handleProjectDataChange: function(callback, options){
this.handleProjectDataModification(callback, options);
},
/**
* Receive notification when any change to the project data occurs within the editor. Useful to trigger {@link DSHDLib.Editors.Editor#getProjectData|getProjectData} for validation routines
*
* @function handleProjectDataModification
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!DSHDLib.Editors.Editor.handleProjectDataModificationCallback} callback
* @param {object} [options]
* @param {bool} [options.user_modifications_only=false] - Supply <i>true</i> to only return changes an actual user makes, ignoring other automatic conversion/correction alterations made in specific scenarios
* @example
* editor.handleProjectDataModification(function(e, updates){
* console.log("Pages/Scenes Altered:", Object.keys(updates.pages || updates.scenes).length);
* });
*/
handleProjectDataModification: function(callback, options){
var cb = function(e, updates){
if (!(options || {}).user_modifications_only || !updates.system)
callback(e, updates);
};
this.subscribe('PROJECT_DATA_CHANGE', cb);
this.onInitialized(function(e){
e.send('SUBSCRIBE_PROJECT_DATA_CHANGE', { enable: true });
});
},
/**
* Callback supplying the editor instance the render request was triggered on and the rendered image data
*
* @callback handleRenderCallback
* @memberof DSHDLib.Editors.Editor
* @param {DSHDLib.Editors.Editor} editor - The editor instance triggering the event
* @param {Object} data
* @param {number} data.width - The width of the rendered image
* @param {number} data.height - The height of the rendered image
* @param {string} data.url - A data url of the rendered image
*/
/**
* Receive information from every render request. To be used along with {@link DSHDLib.Editors.Editor#requestRender|requestRender}
*
* @function handleRender
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!DSHDLib.Editors.Editor.handleRenderCallback} callback
* @example
* editor.handleRender(function(e, data){
* var width = data.width;
* var height = data.height;
* var data_url = data.url;
* var img = new Image();
* img.src = data_url;
* });
*/
handleRender: function(callback){
this.subscribe('PAGE_RENDER', callback);
},
/**
* Callback supplying the editor instance the element selection was triggered on and information about the selected element(s)
*
* @callback handleSelectionCallback
* @memberof DSHDLib.Editors.Editor
* @param {DSHDLib.Editors.Editor} editor - The editor instance triggering the event
* @param {Object.<string, DSHDLib.Editors.ProjectData.ElementProperties>} elements - A map of the selected elements
* @param {Object} [pointer] - If a simple click event triggered the selection, this value represents the coordinates of the click. Useful to detect the specific element that was clicked when selecting groups
* @param {!number} pointer.x - The x coordinate of the click, relative to the canvas, supplied in the unit type of the project (px, in or mm)
* @param {!number} pointer.y - The y coordinate of the click, relative to the canvas, supplied in the unit type of the project (px, in or mm)
*/
/**
* Receive notification when the user selects one or more elements inside the editor. Useful to show custom menus that update the selected elements via {@link DSHDLib.Editors.Editor#updateElements|updateElements}
*
* @function handleElementSelection
* @memberof DSHDLib.Editors.Editor
* @instance
* @deprecated Replaced with {@link DSHDLib.Editors.Editor#handleSelectionChange|handleSelectionChange} to support firing when all elements are deselected as well
* @param {!DSHDLib.Editors.Editor.handleSelectionCallback} callback
* @example
* editor.handleElementSelection(function(e, elements){
* var element_ids = Object.keys(elements);
* console.log("Selected Element(s): ", element_ids);
* });
*/
handleElementSelection: function(callback){
var cb = function(e, els){
if (Object.keys(els).length) {
var ptr;
for (var el in els) {
ptr = els[el].pointer;
delete els[el].pointer;
}
callback(e, els, ptr);
}
};
this.subscribe('ELEMENT_SELECTION', cb);
this.onInitialized(function(e){
e.send('SUBSCRIBE_ELEMENT_SELECTION', { enable: true });
});
},
/**
* Receive notification when the user selects one or more elements inside the editor or when all elements are deselected. Useful to show custom menus that update the selected elements via {@link DSHDLib.Editors.Editor#updateElements|updateElements}
*
* @function handleSelectionChange
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!DSHDLib.Editors.Editor.handleSelectionCallback} callback
* @example
* editor.handleSelectionChange(function(e, elements){
* var element_ids = Object.keys(elements);
* console.log("Selected Element(s): ", element_ids);
* });
*/
handleSelectionChange: function(callback){
var cb = function(e, els){
var ptr;
for (var el in els) {
ptr = els[el].pointer;
delete els[el].pointer;
}
callback(e, els, ptr);
};
this.subscribe('ELEMENT_SELECTION', cb);
this.onInitialized(function(e){
e.send('SUBSCRIBE_ELEMENT_SELECTION', { enable: true, include_no_selection: true });
});
},
/**
* Callback supplying the editor instance the color swatch click was triggered on, the current details of the color clicked, and information about the selected element(s)
*
* @callback handleColorSelectionCallback
* @memberof DSHDLib.Editors.Editor
* @param {DSHDLib.Editors.Editor} editor - The editor instance triggering the event
* @param {Object} data
* @param {string} data.context - One of: "element", "background", "group"
* @param {Object[]} data.selected_color - Details of the color the user clicked on. One item array unless it's a gradient in which case one item per color stop.
* @param {string} data.selected_color.key - The [string representation]{@tutorial Colors} of the color, matching the color format returned in {@link DSHDLib.Editors.Editor#getProjectData|getProjectData} and expected in {@link DSHDLib.Editors.Editor#updateElements|updateElements}
* @param {Object} data.selected_color.rgb - The RGB representation of the color. Converted for display using your ICC profile when the color is set as CMYK/SPOT.
* @param {number} data.selected_color.rgb.r - The Red value - integer from 0 to 255
* @param {number} data.selected_color.rgb.g - The Green value - integer from 0 to 255
* @param {number} data.selected_color.rgb.b - The Blue value - integer from 0 to 255
* @param {string} data.selected_color.rgb.hex - The hexadecimal representation
* @param {Object} [data.selected_color.cmyk] - Returned if the color is set as CMYK
* @param {number} data.selected_color.cmyk.c - The Cyan value - integer from 0 to 100
* @param {number} data.selected_color.cmyk.m - The Magenta value - integer from 0 to 100
* @param {number} data.selected_color.cmyk.y - The Yellow value - integer from 0 to 100
* @param {number} data.selected_color.cmyk.k - The Black value - integer from 0 to 100
* @param {Object} [data.selected_color.spot] - Returned if the color is set as SPOT
* @param {number} data.selected_color.spot.c - The CMYK fallback Cyan value - integer from 0 to 100
* @param {number} data.selected_color.spot.m - The CMYK fallback Magenta value - integer from 0 to 100
* @param {number} data.selected_color.spot.y - The CMYK fallback Yellow value - integer from 0 to 100
* @param {number} data.selected_color.spot.k - The CMYK fallback Black value - integer from 0 to 100
* @param {string} data.selected_color.spot.name - The SPOT color name (e.g. "PMS 123 C")
* @param {number} [data.selected_color.alpha] - Percentage based opacity of the color - from 0 to 1. Not returned if multiple elements are selected (<i>context</i> is "group") or if a multi-path SVG is selected with this color used across multiple paths with different opacities ("Separate Colors" option in the menu UI disabled)
* @param {number} [data.selected_color.offset] - Percentage based color-stop position - from 0 to 1. Only returned if <i>key</i> is "gradient"
* @param {number} [data.selected_color_index] - Returned if a single element is selected (<i>context</i> is "element"), and points to the specific index this color selection references in the <i>colors</i> array property in the element's data. This index is necessary to distinguish between duplicate colors in the array representing fills vs strokes and when the user first enables "Separate Colors" in the menu UI when selecting a multi-path SVG. Note that by default duplicate fill colors are grouped together and duplicate stroke colors are grouped together in the menu UI and in the element <i>colors</i> array property.
* @param {Object.<string, DSHDLib.Editors.ProjectData.ElementProperties>} [data.element_info] - A map of the selected elements. Not returned when <i>context</i> is "background"
* @tutorial Colors
*/
/**
* Receive notification when the user clicks into a color swatch in one of the editor menus and suppress the built in color menu. Intended to override the default color controls with your own UI (e.g. with specific Pantone color swatch support) and then update the selected element(s) <i>colors</i> property via {@link DSHDLib.Editors.Editor#updateElements|updateElements}
*
* @function handleColorSelection
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!DSHDLib.Editors.Editor.handleColorSelectionCallback} callback
* @example
* editor.handleColorSelection(function(e, data){
* var rgb_to_display = data.selected_color[0].rgb.hex;
* });
*/
handleColorSelection: function(callback){
this.subscribe('COLOR_SELECTION', callback);
this.onInitialized(function(e){
e.send('SUBSCRIBE_COLOR_SELECTION', { enable: true });
});
},
/**
* Callback supplying the editor instance the Upload button click was triggered on and context about the upload request
*
* @callback handleMediaUploadRequestCallback
* @memberof DSHDLib.Editors.Editor
* @param {DSHDLib.Editors.Editor} editor - The editor instance triggering the event
* @param {Object} data
* @param {string} data.context - One of: "element", "background", "root"
* @param {string[]} data.allowed_file_types - The list of file types supported based on the upload request context (e.g. <i>["svg","png","jpg","webp","heic","gif","tiff"]</i>)
* @param {DSHDLib.Editors.ProjectData.ElementProperties} [data.element_info] - The properties of the selected (container) element. Only returned when <i>context</i> is "element"
*/
/**
* Receive notification when the user clicks any Upload button in the editor. Intended to override the default file upload process with your own (e.g. custom file pre-processing) and then reference the file in adding a new element ({@link DSHDLib.Editors.Editor#addElement|addElement}) or updating the selected element ({@link DSHDLib.Editors.Editor#updateElements|updateElements}). Note that this event is usually paired with the setting to disable media drag/drop functionality within the editor.
*
* @function handleMediaUploadRequest
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!DSHDLib.Editors.Editor.handleMediaUploadRequestCallback} callback
* @example
* editor.handleMediaUploadRequest(function(e, data){
* var e = document.createElement("input");
* e.setAttribute("type", "file");
* e.setAttribute("style", "display:none");
* e.setAttribute("accept", "." + data.allowed_file_types.join(", ."));
* document.body.appendChild(e);
* e.click();
* });
*/
handleMediaUploadRequest: function(callback){
this.subscribe('MEDIA_UPLOAD_REQUEST', callback);
this.onInitialized(function(e){
e.send('SUBSCRIBE_MEDIA_UPLOAD_REQUEST', { enable: true });
});
},
handleNavigateTeamManagement: function(callback){
this.subscribe('GOTO_TEAMS', callback);
},
/**
* Receive notification when the user clicks to view all templates after publising a template via {@link DSHDLib.Editors.Editor#showPublishTemplate|showPublishTemplate}
*
* @function handleNavigateTemplateManagement
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!DSHDLib.Editors.Editor.editorCallback} callback
* @example
* editor.handleNavigateTemplateManagement(function(e){
* location.href = '/templates';
* });
*/
handleNavigateTemplateManagement: function(callback){
this.subscribe('GOTO_TEMPLATES', callback);
},
/**
* Callback supplying the editor instance the project title update was triggered on and the new project title
*
* @callback handleProjectTitleUpdateCallback
* @memberof DSHDLib.Editors.Editor
* @param {DSHDLib.Editors.Editor} editor - The editor instance triggering the event
* @param {Object} data
* @param {string} data.project_title - The new project title
*/
/**
* Receive notification after the user changes the project title via {@link DSHDLib.Editors.Editor#showChangeTitle|showChangeTitle}
*
* @function handleProjectTitleChange
* @memberof DSHDLib.Editors.Editor
* @instance
* @deprecated Renamed to {@link DSHDLib.Editors.Editor#handleProjectTitleUpdate|handleProjectTitleUpdate} for naming consistency
* @param {!DSHDLib.Editors.Editor.handleProjectTitleUpdateCallback} callback
*/
handleProjectTitleChange: function(callback){
this.handleProjectTitleUpdate(callback);
},
/**
* Receive notification after the user changes the project title via {@link DSHDLib.Editors.Editor#showUpdateTitle|showUpdateTitle}
*
* @function handleProjectTitleUpdate
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!DSHDLib.Editors.Editor.handleProjectTitleUpdateCallback} callback
* @example
* editor.handleProjectTitleUpdate(function(e, data){
* console.log("New Title:", data.project_title);
* });
*/
handleProjectTitleUpdate: function(callback){
this.subscribe('CHANGE_PROJECT_TITLE', callback);
},
/**
* Callback supplying the editor instance the project approval status change was triggered on and the new approval status
*
* @callback handleApprovalStatusChangeCallback
* @memberof DSHDLib.Editors.Editor
* @param {DSHDLib.Editors.Editor} editor - The editor instance triggering the event
* @param {Object} data
* @param {string} data.approval_status - The new project approval status. Currently only "pending_review" or <i>null</i> to indicate status removal (re-submission required)
* @param {bool} [data.state_updated=false] - A value indicating that the trigger of the status change was a project update
*/
/**
* Receive notification after the project approval status changes via {@link DSHDLib.Editors.Editor#showSubmitForReview|showSubmitForReview}, {@link DSHDLib.Editors.Editor#showSubmitRevision|showSubmitRevision}, {@link DSHDLib.Editors.Editor#cancelPendingReview|cancelPendingReview} or project data changes that require re-submission
*
* @function handleApprovalStatusChange
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!DSHDLib.Editors.Editor.handleApprovalStatusChangeCallback} callback
* @example
* editor.handleApprovalStatusChange(function(e, data){
* console.log("New Status:", data.approval_status);
* });
*/
handleApprovalStatusChange: function(callback){
this.subscribe('CHANGE_APPROVAL_STATUS', callback);
},
/**
* Request the properties of a specific element on any page/scene in the project. To be used along with {@link DSHDLib.Editors.Editor#handleElementData|handleElementData}
*
* @function requestElementData
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!string} element_id - The autogenerated id of the element (usually initially retrieved via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData}). Note that <i>element_name</i> will also work here if enabled in your system
* @example
* editor.requestElementData("abc");
*/
requestElementData: function(element_id){
var cfg = { id: element_id };
this.send('GET_ELEMENT_DATA', cfg);
},
/**
* Callback supplying an error or the requested element
*
* @callback getElementDataCallback
* @memberof DSHDLib.Editors.Editor
* @param {Error} [error] - An error if the element retrieval request failed
* @param {DSHDLib.Editors.ProjectData.ElementProperties} [element] - The properties of the requested element
*/
/**
* Request and receive the properties of a specific element on any page/scene in the project
*
* @function getElementData
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {string} element_id - The autogenerated id of the element (usually initially retrieved via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData}). Note that <i>element_name</i> will also work here if enabled in your system
* @param {!DSHDLib.Editors.Editor.getElementDataCallback} callback
* @example
* editor.getElementData("abc", function(err, element){
* console.log("Element Type:", element.type);
* });
*/
getElementData: function(element_id, callback){
var cb = function(e, data){
if (data.element_id == element_id || data.element_name == element_id) {
e.unsubscribe('ELEMENT_DATA', cb);
callback(null, data);
}
};
this.subscribe('ELEMENT_DATA', cb);
this.requestElementData(element_id);
},
/**
* Request the properties of one scene in a video project and all of its elements. To be used along with {@link DSHDLib.Editors.Editor#handleSceneData|handleSceneData}
*
* @function requestSceneData
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!string} scene_id - The autogenerated id of the scene (usually initially retrieved via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData})
* @example
* editor.requestSceneData("abc");
*/
requestSceneData: function(scene_id){
this.requestPageData(scene_id);
},
/**
* Request the properties of one page in a project and all of its elements. To be used along with {@link DSHDLib.Editors.Editor#handlePageData|handlePageData}
*
* @function requestPageData
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!string} page_id - The autogenerated id of the page (usually initially retrieved via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData})
* @example
* editor.requestPageData("abc");
*/
requestPageData: function(page_id){
var cfg = { page_id: page_id };
this.send('GET_PAGE_DATA', cfg);
},
/**
* Callback supplying an error or the requested scene on a video project
*
* @callback getSceneDataCallback
* @memberof DSHDLib.Editors.Editor
* @param {Error} [error] - An error if the scene retrieval request failed
* @param {DSHDLib.Editors.ProjectData.SceneData} [scene] - The properties of the requested scene and all of its elements
*/
/**
* Request and receive the properties of a specific scene in a video project and all of its elements
*
* @function getSceneData
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {string} scene_id - The autogenerated id of the scene (usually initially retrieved via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData})
* @param {!DSHDLib.Editors.Editor.getSceneDataCallback} callback
* @example
* editor.getSceneData("abc", function(err, scene){
* console.log("Width:", scene.dimensions.width);
* });
*/
getSceneData: function(scene_id, callback){
this.getPageData(scene_id, callback);
},
/**
* Callback supplying an error or the requested page
*
* @callback getPageDataCallback
* @memberof DSHDLib.Editors.Editor
* @param {Error} [error] - An error if the page retrieval request failed
* @param {DSHDLib.Editors.ProjectData.PageData} [page] - The properties of the requested page and all of its elements
*/
/**
* Request and receive the properties of a specific page in the project and all of its elements
*
* @function getPageData
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {string} page_id - The autogenerated id of the page (usually initially retrieved via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData})
* @param {!DSHDLib.Editors.Editor.getPageDataCallback} callback
* @example
* editor.getPageData("abc", function(err, page){
* console.log("Width:", page.dimensions.width);
* });
*/
getPageData: function(page_id, callback){
var cb = function(e, data){
if ((data.page_id || data.scene_id) == page_id) {
e.unsubscribe('PAGE_DATA', cb);
callback(null, data);
}
};
this.subscribe('PAGE_DATA', cb);
this.requestPageData(page_id);
},
/**
* Request the properties of a project and all of its elements. To be used along with {@link DSHDLib.Editors.Editor#handleProjectData|handleProjectData}
*
* @function requestProjectData
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {Object} [options]
* @param {bool} [options.simple=false] - Supply <i>true</i> to only return high level metadata about the project very quickly
* @param {bool} [options.include_layouts=false] - Supply <i>true</i> to include layouts in the returned data
* @example
* editor.requestProjectData();
*/
requestProjectData: function(cfg){
cfg = cfg || {};
if (!cfg.hasOwnProperty('simple'))
cfg = assignObject({}, cfg, { simple: false });
if (this.initialized || !cfg.simple)
this.send('GET_PROJECT_DATA', cfg);
},
/**
* Callback supplying an error or the requested project properties
*
* @callback getProjectDataCallback
* @memberof DSHDLib.Editors.Editor
* @param {Error} [error] - An error if the project retrieval request failed
* @param {DSHDLib.Editors.ProjectData.ProjectData} [project] - All project metadata, page/scene properties and individual element properties
*/
/**
* Request and receive the properties of a project and all of its elements
*
* @function getProjectData
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {Object} [options]
* @param {bool} [options.simple=false] - Supply <i>true</i> to only return high level metadata about the project very quickly
* @param {bool} [options.include_layouts=false] - Supply <i>true</i> to include layouts in the returned data
* @param {bool} [options.return_excluded_elements=false] - Supply <i>true</i> to include elements that are excluded on render
* @param {!DSHDLib.Editors.Editor.getProjectDataCallback} callback
* @example
* editor.getProjectData({}, function(err, project){
* console.log("Project Title:", project.project_title);
* });
*/
getProjectData: function(cfg, callback){
var cb = function(e, data){
var simple = !(data.pages || data.scenes);
var layouts = !!data.layouts;
if ((simple === (cfg.simple || false)) && (layouts === (cfg.include_layouts || false))) {
e.unsubscribe('PROJECT_DATA', cb);
callback(null, data);
}
};
this.subscribe('PROJECT_DATA', cb);
this.requestProjectData(cfg);
},
getColorsUsed: function(cfg, callback){
cfg = cfg || {};
this.getProjectData({ simple: false }, function(err, data){
if (err) {
callback(err);
} else {
var pages = data.pages || data.scenes;
var page_id = cfg.page_id || cfg.scene_id || cfg.slide_id;
var props = ['colors','background_color','stroke_color'];
var colors = [];
var traverse = function(obj){
for (var p in obj) {
if (typeof obj[p] === 'object' && !Array.isArray(obj[p]) && obj[p] !== null) {
traverse(obj[p]);
} else {
if (props.indexOf(p) !== -1) {
var a = Array.isArray(obj[p]) ? obj[p] : [obj[p]];
for (var c = 0; c < a.length; c++)
if (a[c] !== 'transparent' && colors.indexOf(a[c]) === -1)
colors.push(a[c]);
}
}
}
};
for (var p = 0; p < pages.length; p++)
if (!page_id || (page_id == (pages[p].page_id || pages[p].scene_id)))
traverse(pages[p]);
callback(null, colors);
}
});
},
/**
* @namespace RenderCustomizationObjects
* @memberof DSHDLib.Editors.ProjectData
*/
/**
* An object containing data to overwrite the persisted project data in a specific {@link DSHDLib.Editors.Editor#getRender|Render} request
*
* @typedef {Object} RenderCustomizationObject
* @memberof DSHDLib.Editors.ProjectData.RenderCustomizationObjects
* @property {Object.<string, DSHDLib.Editors.ProjectData.RenderCustomizationObjects.RenderCustomizationObjectElementUpdates>} elements - A map of data to populate based on <i>element_id</i>/<i>element_name</i> matches. <i>element_id</i> can be retrieved via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData} (e.g. finding all elements with a matching class name)
*/
/**
* An object containing data to overwrite on a matched element.
*
* @typedef {Object} RenderCustomizationObjectElementUpdates
* @memberof DSHDLib.Editors.ProjectData.RenderCustomizationObjects
* @property {string} [text] - The replacement text to fully overwrite the matched text element. Note that this will remove any existing character specific style overrides
* @property {Object} [text_insert] - The text and position of new data to insert/replace in the matched text element. Note that this will preserve existing style overrides with the exception of range replacements that span multiple styles.
* @property {!string} text_insert.text - The new text to insert within the existing text string
* @property {number|number[]} text_insert.position - The 0-based position to insert the new text. Can optionally be supplied as an array containing a start and end index which will overwrite the existing text in that range with the newly supplied <i>text</i>
* @property {Object.<string, string>} [text_token_map] - A map of tokens that may be present in the text of the matched text element and their replacement values. Note that this will override the <i>text_token_map</i> if set on the text element directly.
* @property {bool} [auto_fit] - A value indicating weither to update font size dynamically to fit the supplied text changes within the matched textbox's current width/line count
*/
/**
* Request a rendered image of a page/scene from the editor. To be used along with {@link DSHDLib.Editors.Editor#handleRender|handleRender}
*
* @function requestRender
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {Object} [options]
* @param {string} [options.page_id] - The page to render (ID retrievable via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData} or {@link https://api.designhuddle.com/user#tag/Projects/paths/~1projects~1{project_id}/get|API}). Uses the first page if not supplied
* @param {string} [options.scene_id] - The scene to render the first frame of(ID retrievable via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData} or {@link https://api.designhuddle.com/user#tag/Projects/paths/~1projects~1{project_id}/get|API}). Uses the first scene if not supplied
* @param {number} [options.max_width=800] - The maximum width of the requested image. Can be used by itself or in conjunction with <i>max_height</i> based on the project's aspect ratio
* @param {number} [options.max_height=800] - The maximum height of the requested image. Can be used by itself or in conjunction with <i>max_width</i> based on the project's aspect ratio
* @param {bool} [options.render_excluded_elements=false] - Value indicating whether or not to include elements in the rendered image that have been tagged with a class that removes them by default upon export
* @param {bool} [options.product_image_background=false] - Value indicating whether or not you want to include the {@link https://api.designhuddle.com/doc/Product_Image_Backgrounds.pdf|product image background} in the rendered image
* @param {bool} [options.temporary_background=false] - Value indicating whether or not you want to include the {@link DSHDLib.Editors.Editor#changeTemporaryBackground|temporary background} in the rendered image
* @param {DSHDLib.Editors.ProjectData.RenderCustomizationObjects.RenderCustomizationObject} [options.customizations] - An object containing data to overwrite the persisted project data in this specific render request
* @example
* var options = {
* max_width: 500,
* max_height: 500
* };
* editor.requestRender(options);
*/
requestRender: function(cfg){
if (cfg && !cfg.page_id)
cfg = assignObject({}, cfg, { page_id: cfg.scene_id || cfg.slide_id });
this.send('GET_PAGE_RENDER', cfg);
},
/**
* Callback supplying an error or the rendered image data
*
* @callback getRenderCallback
* @memberof DSHDLib.Editors.Editor
* @param {Error} [error] - An error if the render request failed
* @param {Object} [data]
* @param {number} data.width - The width of the rendered image
* @param {number} data.height - The height of the rendered image
* @param {string} data.url - A data url of the rendered image
*/
/**
* Request and receive a rendered image of a page/scene from the editor
*
* @function getRender
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {Object} [options]
* @param {string} [options.page_id] - The page to render (ID retrievable via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData} or {@link https://api.designhuddle.com/user#tag/Projects/paths/~1projects~1{project_id}/get|API}). Uses the first page if not supplied
* @param {string} [options.scene_id] - The scene to render the first frame of (ID retrievable via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData} or {@link https://api.designhuddle.com/user#tag/Projects/paths/~1projects~1{project_id}/get|API}). Uses the first scene if not supplied
* @param {number} [options.max_width=800] - The maximum width of the requested image. Can be used by itself or in conjunction with <i>max_height</i> based on the project's aspect ratio
* @param {number} [options.max_height=800] - The maximum height of the requested image. Can be used by itself or in conjunction with <i>max_width</i> based on the project's aspect ratio
* @param {bool} [options.render_excluded_elements=false] - Value indicating whether or not to include elements in the rendered image that have been tagged with a class that removes them by default upon export
* @param {bool} [options.product_image_background=false] - Value indicating whether or not you want to include the {@link https://api.designhuddle.com/doc/Product_Image_Backgrounds.pdf|product image background} in the rendered image
* @param {bool} [options.temporary_background=false] - Value indicating whether or not you want to include the {@link DSHDLib.Editors.Editor#changeTemporaryBackground|temporary background} in the rendered image
* @param {DSHDLib.Editors.ProjectData.RenderCustomizationObjects.RenderCustomizationObject} [options.customizations] - An object containing data to overwrite the persisted project data in this specific render request
* @param {!DSHDLib.Editors.Editor.getRenderCallback} callback
* @example
* var options = {
* max_width: 500,
* max_height: 500,
* customizations: {
* elements: {
* "full_name": {
* text: "John Doe",
* auto_fit: true
* }
* }
* }
* };
* editor.getRender(options, function(err, data){
* if (!err) {
* var width = data.width;
* var height = data.height;
* var data_url = data.url;
* var img = new Image();
* img.src = data_url;
* }
* });
*/
getRender: function(cfg, callback){
cfg = cfg || {};
if (!cfg.page_id)
cfg = assignObject({}, cfg, { page_id: cfg.scene_id || cfg.slide_id });
var cb = function(e, data){
var id = data.page_id || data.scene_id;
if (!cfg.page_id || id == cfg.page_id) {
e.unsubscribe('PAGE_RENDER', cb);
callback(null, data);
}
};
this.subscribe('PAGE_RENDER', cb);
this.requestRender(cfg);
},
/**
* Highlight specific elements on the currently open page/scene in the editor
*
* @function highlightElements
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {!string[]} element_ids - The list of elements to highlight (usually retrieved via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData})
* @param {Object} [options]
* @param {number} [options.duration=1000] - The desired length of the highlight animation in milliseconds
* @param {string} [options.fill_color] - The desired fill color of the highlighted area in RGB hex notation. Uses <i>theme_color2</i> if not supplied
* @param {string} [options.stroke_color="#ffffff"] - The desired stroke color of the highlighted area in RGB hex notation
* @param {number} [options.starting_opacity=0.5] - The starting opacity for the animation of the highlighted area. Must be a number between 0 and 1
* @example
* editor.highlightElements([element_id1, element_id2]);
* @example
* editor.highlightElements([element_id1, element_id2], { duration: 2000, fill_color: "#FF0000" });
*/
highlightElements: function(els, cfg){
this.send('HIGHLIGHT_ELEMENTS', assignObject({}, cfg || {}, { elements: els }));
},
/**
* For video projects, play the currently visible scene on the canvas
*
* @function playScene
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {Object} [options]
* @param {bool} [options.stop=false] - Supply <i>true</i> to indicate that a currently playing scene should be stopped
* @example
* editor.playScene();
*/
playScene: function(cfg){
this.send('PLAY_SCENE', cfg);
},
/**
* For video projects, open the Play All popup and start playing the entire video
*
* @function playAll
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {Object} [options]
* @param {bool} [options.pause] - Supply <i>true</i> to indicate that the currently playing video should be paused or <i>false</i> to unpause
* @example
* editor.playAll();
*/
playAll: function(cfg){
this.send('PLAY_ALL', cfg);
},
/**
* Open the built-in Set Title popup within the editor to allow the user to easily change the project title without building a custom title change workflow via the {@link https://api.designhuddle.com/user#tag/Projects/paths/~1projects~1{project_id}/patch|Update Project API endpoint}
*
* @function showChangeTitle
* @memberof DSHDLib.Editors.Editor
* @instance
* @deprecated Renamed to {@link DSHDLib.Editors.Editor#showUpdateTitle|showUpdateTitle} for naming consistency
*/
showChangeTitle: function(){
this.showUpdateTitle();
},
/**
* Open the built-in Set Title popup within the editor to allow the user to easily update the project title without building a custom title change workflow via the {@link https://api.designhuddle.com/user#tag/Projects/paths/~1projects~1{project_id}/patch|Update Project API endpoint}
*
* @function showUpdateTitle
* @memberof DSHDLib.Editors.Editor
* @instance
* @example
* editor.showUpdateTitle();
*/
showUpdateTitle: function(){
this.send('OPEN_SET_TITLE_POPUP');
},
/**
* Open the built-in Export popup within the editor to allow the user to easily export an image/pdf/video without building a custom export workflow via the {@link https://api.designhuddle.com/user#tag/Export|Export API endpoint}
*
* @function showExport
* @memberof DSHDLib.Editors.Editor
* @instance
* @param {Object} [options]
* @param {string} [options.filename] - The value to populate in the filename textbox
* @param {string} [options.default_social_post_text] - The text to populate for the caption in the built-in social media export workflows
* @param {bool} [options.hide_email=false] - A value indicating whether or not the email option should be available in the export popup
* @example
* editor.showExport();
*/
showExport: function(cfg){
this.send('OPEN_EXPORT_POPUP', cfg);
},
/**
* Open the built-in Publish Template popup within the editor to allow an admin to easily publish a template from the open project without building a custom template publishing workflow via the {@link https://api.designhuddle.com/admin#tag/Templates/paths/~1templates/post|Publish Template API endpoint}
*
* @function showPublishTemplate
* @memberof DSHDLib.Editors.Editor
* @instance
* @example
* editor.showPublishTemplate();
*/
showPublishTemplate: function(){
this.send('OPEN_PUBLISH_TEMPLATE');
},
/**
* Open the built-in Submit For Approval popup within the editor to allow a user to easily submit this project for approval, for projects without an <i>approval_status</i> or those that have been previously "approved" (exposed via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData})
*
* @function showSubmitForReview
* @memberof DSHDLib.Editors.Editor
* @instance
* @example
* editor.showSubmitForReview();
*/
showSubmitForReview: function(){
this.send('OPEN_APPROVAL_FORM');
},
/**
* Open the built-in Submit Revision popup within the editor to allow a user to easily re-submit this project for approval, for projects with an <i>approval_status</i> of "awaiting_revisions" or "denied" (exposed via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData})
*
* @function showSubmitRevision
* @memberof DSHDLib.Editors.Editor
* @instance
* @example
* editor.showSubmitRevision();
*/
showSubmitRevision: function(){
this.send('OPEN_SUBMIT_REVISION');
},
/**
* Cancel the pending review request for a project with an <i>approval_status</i> of "pending_review" (exposed via {@link DSHDLib.Editors.Editor#getProjectData|getProjectData})
*
* @function cancelPendingReview
* @memberof DSHDLib.Editors.Editor
* @instance
* @example
* editor.cancelPendingReview();
*/
cancelPendingReview: function(){
this.send('CANCEL_REVIEW');
},
/**
* Save any unsaved locking configuration changes (exposed via <i>unsaved_locking_config</i> in {@link DSHDLib.Editors.Editor#getProjectData|getProjectData})
*
* @function saveLockingConfig
* @memberof DSHDLib.Editors.Editor
* @instance
* @example
* editor.saveLockingConfig();
*/
saveLockingConfig: function(){
this.send('SAVE_LOCKING_CONFIG');
}
});
Editor.prototype.addScene = Editor.prototype.addPage;
Editor.prototype.importScenes = Editor.prototype.importPages;
Editor.prototype.resizeScenes = Editor.prototype.resizePage;
Editor.prototype.removeScenes = Editor.prototype.removePages;
Editor.prototype.changeScene = Editor.prototype.changeSlide = Editor.prototype.changePage;
Editor.prototype.handleSlideChange = Editor.prototype.handlePageChange;
var lib_obj = {
init: function(iframe_element, cfg){
if (typeof iframe_element === 'string' || iframe_element instanceof String)
iframe_element = document.getElementById(iframe_element);
if (iframe_element instanceof HTMLIFrameElement) {
var e = findEditorByIframe(iframe_element);
if (!e) {
e = new Editor(iframe_element, cfg);
e.send('GET_PROJECT_DATA', { simple: true }, true, true);
} else
e.refreshConfig(cfg);
return e;
} else
throw 'Invalid Iframe Element';
},
/**
* Callback supplying a new editor instance
*
* @callback newEditorCallback
* @memberof DSHDLib.Editors
* @param {Error} [error] - An error if implicit guest token retrieval fails
* @param {DSHDLib.Editors.Editor} editor - A newly created editor instance
*/
build: function(cfg, cb){
var buildEditor = function(){
var iframe = document.createElement('iframe');
iframe.referrerPolicy = 'origin';
return new Editor(iframe, cfg);
};
if (cb) {
GetAccessToken(cfg, function(err){
if (!err) {
try {
cb(null, buildEditor());
} catch (e) {
cb(e);
}
} else
cb(err);
});
} else
return buildEditor();
},
/**
* Context and settings for an editor instance - See {@link DSHDLib.LibraryConfig} for additional library configuration override properties that can be used
*
* @typedef {DSHDLib.LibraryConfig} EditorConfig
* @memberof DSHDLib.Editors
* @property {!string} project_id - The project to load into the editor
* @property {number} [page_number=1] - The page to open in the project upon editor load
* @property {bool} [internal=false] - In special domain/cookie scenarios, allows the editor to open without an access token stored in the library
* @property {string} [gallery_id] - A context-specific filter value for custom media galleries in the editor
* @property {bool} [expand_right_panel=false] - Display the right panel upon editor load even in scenarios where it would be collapsed by default
* @property {bool} [hide_right_panel=false] - Completely hide the right panel and all of its functionality. Commonly used to create custom paging controls alongside the editor
* @property {bool} [hide_undo_redo=false] - Hide the undo/redo controls that show above the canvas
* @property {bool} [hide_zoom=false] - Hide the zoom control that shows above the canvas
* @property {bool} [hide_prev_next=false] - Hide the previous/next page/scene controls that show below the canvas on multi-page/scene projects
* @property {bool} [hide_play_scene=false] - Hide the Play Scene control that shows below the canvas on video projects
* @property {bool} [hide_play_all=false] - Hide the Play All control that shows below the canvas on multi-scene video projects
* @property {number|string} [zoom="fit"] - The initial zoom level applied upon editor load. Allows a number between 0.1 and 3 or "fit" or "fill"
* @property {string} [theme_color1] - Override the default environment theme "dark" color. Supply in hex format with no preceding <i>#</i>
* @property {string} [theme_color2] - Override the default environment theme "light" color. Supply in hex format with no preceding <i>#</i>
* @property {string} [locale] - Override the default user-based locale setting to show the editor in a different language
* @property {string[]} [color_defaults_rgb] - Override the default RGB color palette
* @property {string[]} [color_defaults_cmyk] - Override the default CMYK color palette
* @property {string} [product_image_url] - The URL to an externally hosted image to load as the product image background ({@link https://api.designhuddle.com/doc/Product_Image_Backgrounds.pdf|Doc})
* @property {number} [product_image_width] - Set the width of the product image background relative to the canvas zone ({@link https://api.designhuddle.com/doc/Product_Image_Backgrounds.pdf|Doc})
* @property {number} [product_image_height] - Set the height of the product image background relative to the canvas zone ({@link https://api.designhuddle.com/doc/Product_Image_Backgrounds.pdf|Doc})
* @property {string} [product_image_canvas_zone_dimensions] - Set the canvas zone dimensions relative to the product image background ({@link https://api.designhuddle.com/doc/Product_Image_Backgrounds.pdf|Doc})
* @property {string} [product_image_canvas_zone_offset] - Set the canvas zone position relative to the product image background ({@link https://api.designhuddle.com/doc/Product_Image_Backgrounds.pdf|Doc})
* @property {string} [product_image_canvas_zone_horizontal_align] - If the canvas zone aspect ratio does not match the project aspect ratio, set hoizontal alignment of the canvas within the canvas zone ({@link https://api.designhuddle.com/doc/Product_Image_Backgrounds.pdf|Doc})
* @property {string} [product_image_canvas_zone_vertical_align] - If the canvas zone aspect ratio does not match the project aspect ratio, set vertical alignment of the canvas within the canvas zone ({@link https://api.designhuddle.com/doc/Product_Image_Backgrounds.pdf|Doc})
* @property {number} [product_image_canvas_mobile_zoom_offset] - If the editor loads on mobile, decrease the visible canvas size to display more of the product image background around it
* @property {string} [temporary_background_image_url] - Set a temporary background image on load. See {@link DSHDLib.Editors.Editor#changeTemporaryBackground|changeTemporaryBackground} for more info
* @property {string} [temporary_background_color] - Set a temporary background color on load. Supply in hex format with no preceding <i>#</i>. See {@link DSHDLib.Editors.Editor#changeTemporaryBackground|changeTemporaryBackground} for more info
* @property {string} [classes_context="global"] - For admins, override the Classes list source context. One of: "system_templates", "account"
* @property {string[]} [classes_list] - For admins, override the Classes list with static values. <i>classes_context</i> is ignored when this is set
* @property {bool} [disable_class_creation=false] - Disable the admin from being able to create new Classes
* @property {string[]} [class_modification_element_types] - Disable the admin from being able to set Classes on element types not present in the supplied list. Use an empty array to disable Class selection completely. Otherwise, one or more of: "text", "image", "vector", "video", "lottie", "zone", "background"
*/
/**
* Create and insert a new editor iframe into the DOM
*
* @function DSHDLib.Editors.insert
* @param {!(string|HtmlElement)} parent_element - An id or direct reference for an existing html DOM element to insert the iframe into
* @param {!DSHDLib.Editors.EditorConfig} config - Context and settings for the new editor instance
* @param {!DSHDLib.Editors.newEditorCallback} callback - A function to receive the new editor instance
* @returns {DSHDLib.Editors.Editor} - If an access token is supplied explicitly, this will return the newly created editor instance immediately
* @example
* DSHDLib.Editors.insert("abc", { project_id: "abc" }, function(err, e){
* var editor = e;
* });
* @example
* DSHDLib.Editors.insert("abc", { project_id: "abc", gallery_id: "def", access_token: "ghi" }, function(err, e){
* var editor = e;
* });
*/
insert: function(parent_element, cfg, cb){
var rcfg = assignObject({}, cfg_default, cfg);
if (typeof parent_element === 'string' || parent_element instanceof String)
parent_element = document.getElementById(parent_element);
if (parent_element instanceof Element || parent_element instanceof HTMLDocument) {
var insertIframe = function(e){
e.iframe.frameBorder = 0;
e.iframe.width = '100%';
e.iframe.height = '100%';
parent_element.appendChild(e.iframe);
return e;
};
if (cb || rcfg.guest) {
lib_obj.build(cfg, function(err, e){
if (!err)
insertIframe(e);
if (cb)
cb(err, e);
});
} else
return insertIframe(lib_obj.build(cfg));
} else {
if (cb)
cb(new Error('Unsupported Parent Element Object'));
else if (!rcfg.guest)
throw 'Unsupported Parent Element Object';
}
}
};
return lib_obj;
})();
return {
/**
* Configure or re-configure the library context and settings
*
* @function DSHDLib.configure
* @param {!DSHDLib.LibraryConfig} config
* @example
* DSHDLib.configure({
* domain: "domain.com",
* access_token: "abc"
* });
* @example
* DSHDLib.configure({
* domain: "domain.com",
* client_id: "abc",
* guest: true
* });
*/
configure: function(cfg){
assignObject(cfg_default, cfg);
},
/**
* Callback after retrieving a guest access token
*
* @callback getGuestAccessTokenCallback
* @memberof DSHDLib
* @param {Error} [error] - An error returned upon an authorization or other server side issue
* @param {string} access_token
*/
/**
* Retrieve a guest access token
*
* @function DSHDLib.getGuestAccessToken
* @param {DSHDLib.LibraryConfig} [config_override]
* @param {!DSHDLib.getGuestAccessTokenCallback} callback
* @example
* DSHDLib.getGuestAccessToken({}, function(err, access_token){
* console.log(access_token);
* });
*/
getGuestAccessToken: function(cfg, cb){
cfg = assignObject({}, cfg, { guest: true, access_token: '' });
GetAccessToken(cfg, cb);
},
getVisitorAccessToken: function(cfg, cb){
cfg = assignObject({}, cfg, { visitor: true, access_token: '' });
GetAccessToken(cfg, cb);
},
Editors: Editors,
getTemplates: GetTemplates,
getProjects: GetProjects,
getProject: GetProject,
createProject: CreateProject,
storeTemplateCustomizationObject: StoreTemplateCustomizationObject,
getVariableTemplatePreviewURL: GetVariableTemplatePreviewURL
};
})();
if (!document.currentScript || document.currentScript.src.indexOf('/jssdk/') < 0) {
DSHDEditorLib = {
configure: DSHDLib.configure,
init: DSHDLib.Editors.init,
build: DSHDLib.Editors.build,
insert: DSHDLib.Editors.insert,
getGuestAccessToken: DSHDLib.getGuestAccessToken,
getVisitorAccessToken: DSHDLib.getVisitorAccessToken,
getProjects: DSHDLib.getProjects,
createProject: DSHDLib.createProject,
storeTemplateCustomizationObject: DSHDLib.storeTemplateCustomizationObject,
getVariableTemplatePreviewURL: DSHDLib.getVariableTemplatePreviewURL
};
}