Source

DuGR_api.jsxinc

// The API which extends DuAEF & DuESF


// ====== api_version.jsx======

/**
 * <h3>Duduf Groups</h3>
 * <p><i>Group After Effects layers by tags, and run batch methods on them.</i><br />
 * <code>DuGR</code> is the high-level interface to the layer groups,<br />
 * and it relies on {@link DuAETag}, a lower-level interface which adds tags on layers,<br />
 * and stores data in these tags.</p>
 * <p>A group is created using {@link DuGR.addSelectedLayersToGroup DuGR.addSelectedLayersToGroup()} or {@link DuAETag.addGroup DuAETag.addGroup()}.<br />
 * Groups can be renamed with {@link DuGR.renameGroup DuGR.renameGroup()},<br />
 * They can be removed with {@link DuGR.removeGroup DuGR.removeGroup()}.<br />
 * You can run a method on all layers belonging to a set of groups with {@link DuGR.do DuGR.do()}.</p>
 * <p>{@link DuGR.Group} contains predefined groups to sort layers by type or attribute and use {@link DuGR.do DuGR.do()} on them.</p>
 * <P>You can use {@link DuGR.isolate DuGR.isolate()} to isolate layers in the timeline, the comp panel, or both,<br />
 * use {@link DuGR.exitIsolation DuGR.exitIsolation()} to get out of isolation mode.</p>
 * <p>DuGR requires <i>DuAEF</i>, the <i>Duduf After Effects Framework</i>. Two builds of the <i>DuGR API</i> are available:<br />
 * <ul><li><code>DuGR_api.jsxinc</code> does not include <i>DuAEF</i>, and can be used to compine multiple <i>Duduf APIs</i> with a single copy of <i>DuAEF</i>.<br />
 * Be careful to grab the right version of <i>DuAEF</i> in this case.</li>
 * <li><code>DuAEF_DuGR_api.jsxinc</code> includes all dependencies, with <i>DuAEF</i>, and is easier to include in your scripts.</li></ul></p>
 * @example
 * // Encapsulate everything to avoid global variables!
 * // The parameter is either undefined (stand alone script) or the panel containing the ui (ScriptUI)
 * (function(thisObj)
 * {
 *      // If you only need DuGR, just include DuAEF_DuGR_api
 *      #include "DuAEF_DuGR_api.jsxinc";
 *      
 *      // Running the init() method of DuAEF is required to setup everything properly.
 *      DuAEF.init( "YourScriptName", "1.0.0", "YourCompanyName" );
 *      
 *      // These info can be used by the framework to improve UX, but they're optional
 *      DuESF.chatURL = 'http://chat.rxlab.info'; // A link to a live-chat server like Discord or Slack...
 *      DuESF.bugReportURL = 'https://github.com/RxLaboratory/DuAEF_Dugr/issues/new/choose'; // A link to a bug report form
 *      DuESF.featureRequestURL = 'https://github.com/RxLaboratory/DuAEF_Dugr/issues/new/choose'; // A link to a feature request form
 *      DuESF.aboutURL = 'http://rxlaboratory.org/tools/dugr'; // A link to the webpage about your script
 *      DuESF.docURL = 'http://dugr.rxlab.guide'; // A link to the documentation of the script
 *      DuESF.scriptAbout = 'Duduf Groups: group After Effects layers!'; // A short string describing your script
 *      DuESF.companyURL = 'https://rxlaboratory.org'; // A link to your company's website
 *      DuESF.rxVersionURL = 'http://version.rxlab.io' // A link to an RxVersion server to check for updates
 *      
 *      // Build your UI here, declare your methods, etc.
 * 
 *      // This will be our main panel
 *      var ui = DuScriptUI.scriptPanel( thisObj, true, true, new File($.fileName) );
 *      ui.addCommonSettings(); // Automatically adds the language settings, location of the settings file, etc
 *
 *      DuScriptUI.staticText( ui.settingsGroup, "Hello world of settings!" ); // Adds a static text to the settings panel
 *      DuScriptUI.staticText( ui.mainGroup, "Hello worlds!" ); // Adds a static text to the main panel
 *      
 *      // When you're ready to display everything
 *      DuScriptUI.showUI(ui);
 *
 *      // Note that if you don't have a UI or if you don't use DuScriptUI to show it,
 *      // you HAVE TO run this method before running any other function:
 *      // DuAEF.enterRunTime();
 *  
 * })(this);
 * @example
 * // Encapsulate everything to avoid global variables!
 * // The parameter is either undefined (stand alone script) or the panel containing the ui (ScriptUI)
 * (function(thisObj)
 * {
 *      // If you need to combine DuGR and other APIs like DuIO or DuIK
 *      // Include DuAEF first, and then stand-alone APIs
 *      #include "DuAEF.jsxinc";
 *      #include "DuGR_api.jsxinc";
 *      #include "DuIK_api.jsxinc";
 *      #include "DuIO_api.jsxinc";
 *      
 *      // Running the init() method of DuAEF is required to setup everything properly.
 *      DuAEF.init( "YourScriptName", "1.0.0", "YourCompanyName" );
 *      
 *      // These info can be used by the framework to improve UX, but they're optional
 *      DuESF.chatURL = 'http://chat.rxlab.info'; // A link to a live-chat server like Discord or Slack...
 *      DuESF.bugReportURL = 'https://github.com/RxLaboratory/DuAEF_Dugr/issues/new/choose'; // A link to a bug report form
 *      DuESF.featureRequestURL = 'https://github.com/RxLaboratory/DuAEF_Dugr/issues/new/choose'; // A link to a feature request form
 *      DuESF.aboutURL = 'http://rxlaboratory.org/tools/dugr'; // A link to the webpage about your script
 *      DuESF.docURL = 'http://dugr.rxlab.guide'; // A link to the documentation of the script
 *      DuESF.scriptAbout = 'Duduf Groups: group After Effects layers!'; // A short string describing your script
 *      DuESF.companyURL = 'https://rxlaboratory.org'; // A link to your company's website
 *      DuESF.rxVersionURL = 'http://version.rxlab.io' // A link to an RxVersion server to check for updates
 *      
 *      // Build your UI here, declare your methods, etc.
 * 
 *      // This will be our main panel
 *      var ui = DuScriptUI.scriptPanel( thisObj, true, true, new File($.fileName) );
 *      ui.addCommonSettings(); // Automatically adds the language settings, location of the settings file, etc
 *
 *      DuScriptUI.staticText( ui.settingsGroup, "Hello world of settings!" ); // Adds a static text to the settings panel
 *      DuScriptUI.staticText( ui.mainGroup, "Hello worlds!" ); // Adds a static text to the main panel
 *      
 *      // When you're ready to display everything
 *      DuScriptUI.showUI(ui);
 *
 *      // Note that if you don't have a UI or if you don't use DuScriptUI to show it,
 *      // you HAVE TO run this method before running any other function:
 *      // DuAEF.enterRunTime();
 *  
 * })(this);
 * @namespace
 * @author Nicolas Dufresne and contributors
 * @copyright 2017 - 2023 Nicolas Dufresne, RxLaboratory
 * @version {dugrVersion}
 * @requires DuAEF>=1.0.0
 * @see {@link DuAETag} for low-level group management
 * @category DuGR
 * @license GPL-3.0 <br />
 * DuGR is free software: you can redistribute it and/or modify<br />
 * it under the terms of the GNU General Public License as published by<br />
 * the Free Software Foundation, either version 3 of the License, or<br />
 * (at your option) any later version.<br />
 *<br />
 * DuGR is distributed in the hope that it will be useful,<br />
 * but WITHOUT ANY WARRANTY; without even the implied warranty of<br />
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br />
 * GNU General Public License for more details.<br />
 *<br />
 * You should have received a copy of the GNU General Public License<br />
 * along with DuGR. If not, see {@link http://www.gnu.org/licenses/}.
 */
var DuGR = {}
DuGR.version = "{dugrVersion}";
/**
 * Predefined and automatic groups
 * @enum {string}
 */
DuGR.Group = {
    /**
     * All layers in the composition
     */
    ALL: 'DuGR.all',
    /**
     * Selected layers
     */
    SELECTED: 'DuGR.selected',
    /**
     * Layers included in a custom group
     */
    GROUPED: 'DuGR.grouped',
    /**
     * Precompositions
     */
    COMP: 'DuGR.type.comp',
    /**
     * Null objects
     */
    NULL: 'DuGR.type.null',
    /**
     * Solids
     */
    SOLID: 'DuGR.type.solid',
    /**
     * Shape layers
     */
    SHAPE: 'DuGR.type.shape',
    /**
     * Text layers
     */
    TEXT: 'DuGR.type.text',
    /**
     * Adjustement layers
     */
    ADJUSTMENT: 'DuGR.type.adjustment',
    /**
     * 3D Light layers
     */
    LIGHT: 'DuGR.type.light',
    /**
     * Camera layers
     */
    CAMERA: 'DuGR.type.camera',
    /**
     * 3D Model layers
     */
    THREED_MODEL: 'DuGR.type.threeDModel',
    /**
     * Visible layers
     */
    VISIBLE: 'DuGR.attr.visible',
    /**
     * Audio layers
     */
    SOUND: 'DuGR.attr.sound',
    /**
     * Solo layers
     */
    SOLO: 'DuGR.attr.solo',
    /**
     * Locked layers
     */
    LOCKED: 'DuGR.attr.locked',
    /**
     * Shy layers
     */
    SHY: 'DuGR.attr.shy',
    /**
     * Effects enabled
     */
    EFFECTS: 'DuGR.attr.effects',
    /**
     * Motion Blur enabled
     */
    MB: 'DuGR.attr.mb',
    /**
     * 3D layers
     */
    THREE_D: 'DuGR.attr.three_d',
    /**
     * Guide layers
     */
    GUIDE: 'DuGR.attr.guide',
    /**
     * The layer does not have any parent
     */
    ORPHAN: 'DuGR.hierarchy.orphan',
    /**
     * The layer has at least one child
     */
    HAS_CHILD: 'DuGR.hierarchy.hasChildren',
    /**
     * This is a special group: all layers in there will always be ignored by all methods.
     */
    IGNORED: '::Ignored::',
    /**
     * Layer styles are enabled.
     */
    LAYER_STYLES: 'DuGR.layerStyles',
    /**
     * Layer style: Drop shadow is enabled.
     */
    STYLE_DROP_SHADOW: 'DuGR.style.dropShadow',
    /**
     * Layer style: Inner shadow is enabled.
     */
    STYLE_INNER_SHADOW: 'DuGR.style.innerShadow',
    /**
     * Layer style: Outer glow is enabled..
     */
    STYLE_OUTER_GLOW: 'DuGR.style.outerGlow',
    /**
     * Layer style: Inner glow is enabled.
     */
    STYLE_INNER_GLOW: 'DuGR.style.innerGlow',
    /**
     * Layer style: Bevel is enabled.
     */
    STYLE_BEVEL: 'DuGR.style.bevel',
    /**
     * Layer style: Satin is enabled.
     */
    STYLE_SATIN: 'DuGR.style.satin',
    /**
     * Layer style: Color overlay is enabled.
     */
    STYLE_COLOR: 'DuGR.style.color',
    /**
     * Layer style: Gradient Overlay is enabled.
     */
    STYLE_GRADIENT: 'DuGR.style.gradient',
    /**
     * Layer style: Stroke is enabled.
     */
    STYLE_STROKE: 'DuGR.style.stroke',
    /**
     * Animation: at current time.
     */
    AT_TIME: 'DuGR.animation.atTime',
    /**
     * Animation: in the current time range.
     */
    IN_TIME_RANGE: 'DuGR.animation.inTimeRange',
    /**
     * Animation: has keyframes.
     */
    HAS_KEYFRAMES: 'DuGR.animation.hasKeyframes',
    /**
     * Animation: has expressions.
     */
    HAS_EXPRESSIONS: 'DuGR.animation.hasExpressions',
    /**
     * Matte: has at least one mask.
     */
    HAS_MASK: 'DuGR.matte.hasMask',
    /**
     * Matte: has track matte.
     */
    HAS_MATTE: 'DuGR.matte.hasMatte',
    /**
     * Matte: is used as a track matte.
     */
    IS_MATTE: 'DuGR.matte.isMatte',
    /**
     * Matte: Preserve transparency is enabled.
     */
    PRESERVE_TRANSPARENCY: 'DuGR.matte.preserveTransparency',
    /**
     * Matte: The blending mode is normal.
     */
    NORMAL_BLENDING: 'DuGR.matte.normalBlending',
    /**
     * Matte: The blending mode is not normal.
     */
    OTHER_BLENDING: 'DuGR.matte.otherBlending',
    /**
     * Attribute: has effects.
     */
    OTHER_BLENDING: 'DuGR.attr.hasEffects',
}

/**
 * The way the warning frame can be displayed
 * @enum {Number}
 * @readonly
 * @ts-ignore */
DuGR.WarningFrameMode = {
    /**
     * No frame at all
     */
    NONE: 0,
    /**
     * Above all layers
     */
    ABOVE: 1,
    /**
     * Below all layers
     */
    BELOW: 2
}

/**
 * The way layers are isolated
 * @enum {Number}
 * @readonly
 * @ts-ignore */
DuGR.IsolationMode = {
    /**
     * No isolation
     */
    NONE: 0,
    /**
     * Both the timeline and the comp panel
     */
    BOTH: 1,
    /**
     * Only in the timeline
     */
    TIMELINE: 2,
    /**
     * Only in the comp panel
     */
    COMP_PANEL: 3
}

/**
 * How to isolate layers in the comp panel
 * @enum {Number}
 * @readonly
 * @ts-ignore */
DuGR.CompIsolationMode = {
    HIDE: 0,
    WIREFRAME: 1
}

/**
 * The name of the warning frame layer
 * @default "DuGR::Isolation"
 * @type {string}
 * @memberof DuScriptUI.String
 */
DuGR.DUGR_FRAME_NAME = "DuGR::Isolation";

/**
 * The key for DuGR isolation mode
 * @default 'dugrIsolationModed'
 * @readonly
 * @memberof DuAETag.Key
 */
DuAETag.Key.DUGR_ISOLATION_MODE = 'dugrIsolationMode';
/**
 * Is the layer isolated or not
 * @default 'dugrIsolated'
 * @readonly
 * @memberof DuAETag.Key
 */
DuAETag.Key.DUGR_HIDDEN = 'dugrIsolated';
/**
 * If the layer was shy before isolation
 * @default 'dugrWasShy'
 * @readonly
 * @memberof DuAETag.Key
 */
DuAETag.Key.DUGR_WAS_SHY = 'dugrWasShy';
/**
 * If the layer was enabled before isolation
 * @default 'dugrWasShy'
 * @readonly
 * @memberof DuAETag.Key
 */
DuAETag.Key.DUGR_WAS_ENABLED = 'dugrWasEnabled'
/**
 * If the layer was locked before isolation
 * @default 'dugrWasLocked'
 * @readonly
 * @memberof DuAETag.Key
 */
DuAETag.Key.DUGR_WAS_LOCKED = 'dugrWasLocked'
/**
 * The layer quality before isolation. The value may be one of 'best', 'draft', 'wireframe'.
 * @default 'dugrPreviousQuality'
 * @readonly
 * @memberof DuAETag.Key
 */
DuAETag.Key.DUGR_PREVIOUS_QUALITY = 'dugrPreviousQuality'
/**
 * If the layer had a tag before isolation.
 * @default 'dugrHadTag'
 * @readonly
 * @memberof DuAETag.Key
 */
DuAETag.Key.DUGR_HAD_TAG = 'dugrHadTag'

/**
 * Checks if a layer is included in the given groups
 * @param {Layer} layer The layer to test
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {MarkerValue} [tag] If you have the tag as returned by {@link DuAETag.get} or {@link DuAETag.set}, providing it here improves performance.
 * @param {Boolean} [and=false] If false, the layer must be contained in at least one of the groups, otherwise in all the groups.
 * @returns {Boolean} true if the layer belongs to at least one of the given groups
 */
DuGR.inGroups = function ( layer, groups, tag, and )
{
    and = def(and, false);

    // The Dugr layer is not in any group
    if (layer.name == i18n._(DuGR.DUGR_FRAME_NAME)) return false;

    var ok = false;
    for( var i = 0, n = groups.length; i < n; i++)
    {
        ok = false;

        var layerStyles = layer.property("ADBE Layer Styles");
        var comp = layer.containingComp;
        var workAreaEnd = comp.workAreaStart + comp.workAreaDuration;

        var group = groups[i];

        if (group == DuGR.Group.ALL) ok = true;
        else if (group == DuGR.Group.SELECTED ) ok = layer.selected;
        else if (group == DuGR.Group.GROUPED ) ok = DuAETag.getGroups( layer ).length > 0;
        else if (group == DuGR.Group.COMP ) ok = DuAELayer.isComp( layer );
        else if (group == DuGR.Group.NULL ) ok = DuAELayer.isNull( layer );
        else if (group == DuGR.Group.SOLID ) ok = DuAELayer.isSolid( layer );
        else if (group == DuGR.Group.SHAPE ) ok = layer instanceof ShapeLayer;
        else if (group == DuGR.Group.TEXT ) ok = layer instanceof TextLayer;
        else if (group == DuGR.Group.ADJUSTMENT ) ok = DuAELayer.isAdjustment( layer );
        else if (group == DuGR.Group.LIGHT ) ok =  layer instanceof LightLayer;
        else if (group == DuGR.Group.CAMERA ) ok = layer instanceof CameraLayer;
        else if (group == DuGR.Group.THREED_MODEL ) ok = layer instanceof ThreeDModelLayer;
        else if (group == DuGR.Group.VISIBLE ) ok = layer.enabled;
        else if (group == DuGR.Group.SOUND ) ok = layer.hasAudio;
        else if (group == DuGR.Group.SOLO ) ok = layer.solo;
        else if (group == DuGR.Group.LOCKED ) ok = layer.locked;
        else if (group == DuGR.Group.SHY ) ok = layer.shy;
        else if (group == DuGR.Group.EFFECTS) ok = layer.effectsActive && layer.property("ADBE Effect Parade").numProperties > 0; 
        else if (group == DuGR.Group.MB ) ok = layer.motionBlur;
        else if (group == DuGR.Group.THREE_D ) ok = DuAELayer.isThreeD( layer );
        else if (group == DuGR.Group.GUIDE ) ok = layer.guideLayer;
        else if (group == DuGR.Group.ORPHAN ) ok = !layer.parent;
        else if (group == DuGR.Group.HAS_CHILD ) ok = DuAELayer.hasChild(layer);

        else if (group == DuGR.Group.LAYER_STYLES ) ok = layerStyles.enabled;
        else if (group == DuGR.Group.STYLE_DROP_SHADOW) ok = layerStyles.property("dropShadow/enabled").enabled;
        else if (group == DuGR.Group.STYLE_INNER_SHADOW ) ok = layerStyles.property("innerShadow/enabled").enabled;
        else if (group == DuGR.Group.STYLE_OUTER_GLOW ) ok = layerStyles.property("outerGlow/enabled").enabled;
        else if (group == DuGR.Group.STYLE_INNER_GLOW ) ok = layerStyles.property("innerGlow/enabled").enabled;
        else if (group == DuGR.Group.STYLE_BEVEL ) ok = layerStyles.property("bevelEmboss/enabled").enabled;
        else if (group == DuGR.Group.STYLE_SATIN ) ok = layerStyles.property("chromeFX/enabled").enabled;
        else if (group == DuGR.Group.STYLE_COLOR ) ok = layerStyles.property("solidFill/enabled").enabled;
        else if (group == DuGR.Group.STYLE_GRADIENT ) ok = layerStyles.property("gradientFill/enabled").enabled;
        else if (group == DuGR.Group.STYLE_STROKE ) ok = layerStyles.property("frameFX/enabled").enabled;

        else if (group == DuGR.Group.AT_TIME ) ok = layer.inPoint <= comp.time && layer.outPoint >= comp.time;       
        else if (group == DuGR.Group.IN_TIME_RANGE ) ok = layer.inPoint < workAreaEnd && layer.outPoint > comp.workAreaStart;
        else if (group == DuGR.Group.HAS_KEYFRAMES ) ok = DuAELayer.hasKeys(layer);
        else if (group == DuGR.Group.HAS_EXPRESSIONS ) ok = DuAELayer.hasExpressions(layer);
        
        else if (group == DuGR.Group.HAS_MASK ) ok = DuAELayer.hasMask(layer);
        else if (group == DuGR.Group.HAS_MATTE ) ok = layer.hasTrackMatte;
        else if (group == DuGR.Group.IS_MATTE ) ok = layer.isTrackMatte;
        else if (group == DuGR.Group.PRESERVE_TRANSPARENCY ) ok = layer.preserveTransparency;
        else if (group == DuGR.Group.NORMAL_BLENDING ) ok = layer.blendingMode == BlendingMode.NORMAL;
        else if (group == DuGR.Group.OTHER_BLENDING ) ok = layer.blendingMode != BlendingMode.NORMAL;

        else
        {
            // Get the custom groups
            var layerGroups = DuAETag.getGroups( layer );
            if (layerGroups.length == 0) ok = false;
            layerGroups = new DuList( layerGroups );
            if (layerGroups.indexOf(group) >= 0) ok = true;
        }
        
        if (ok && !and) return true;
        if (!ok && and) return false;
    }

    return ok;
}

/**
 * Runs a function for each layer in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {DuGR~doCallBack} callback The function to run on each layer.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {DuGR~doNotCallBack} [notCallback] The function to run on each layer IF it is NOT in the groups.
 * @param {Boolean} [and=false] If false, runs the function on the layers contained in at least one of the groups, otherwise in all the groups.
 */
DuGR.do = function( groups, callback, invert, comp, notCallback, and )
{
    notCallback = def(notCallback, function(){} );
    comp = def(comp, DuAEProject.getActiveComp());
    invert = def(invert, false);

    if (!comp) return;
    if (comp.layers.length == 0) return;

    var returnValue;

    new DuList(comp.layers).do( function( layer ){
        if (DuGR.inGroups(layer, [DuGR.Group.IGNORED], undefined, and)) return;
        var ok = DuGR.inGroups(layer, groups, undefined, and);
        if (invert) ok = !ok;
        if (ok) returnValue = callback( layer, returnValue );
        else notCallback( layer );
    } );
}

/**
 * A function passed to {@link Dugr.do}.
 * @callback doCallBack
 * @memberof DuGR
 * @param {Layer} layer The layer on which the function operates.
 * @param {any} returnValue The return value of the previous iteration. Undefined during the first iteration.
 * @return {any} What the function returns will be automatically passed as a second argument on the next iteration.<br />
 * This is useful if the function has to set a toogle on the first layer, and then re-use it, for example.
 */

/**
 * Toggles the visibility of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 */
DuGR.toggleVisibility = function ( groups, invert, comp )
{
    function f( layer, enabled)
    {
        if (layer.isTrackMatte) return;
        enabled = def( enabled, !layer.enabled );
        layer.enabled = enabled;
        return enabled;
    }

    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the audio of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 */
DuGR.toggleSound = function( groups, invert, comp )
{
    function f( layer, enabled)
    {
        enabled = def( enabled, !layer.audioEnabled );
        layer.audioEnabled = enabled;
        return enabled;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the solo mode of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 */
DuGR.toggleSolo = function( groups, invert, comp )
{
    function f( layer, enabled)
    {
        if (!layer.enabled) return enabled;
        enabled = def( enabled, !layer.solo );
        layer.solo = enabled;
        return enabled;
    }
    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the lock of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 */
DuGR.toggleLocked = function( groups, invert, comp )
{
    function f( layer, enabled)
    {
        enabled = def( enabled, !layer.locked );
        layer.locked = enabled;
        return enabled;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the shy mode of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 */
DuGR.toggleShy = function( groups, invert, comp )
{
    function f( layer, enabled)
    {
        enabled = def( enabled, !layer.shy );
        layer.shy = enabled;
        return enabled;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the collapse transformation/continuous rasterization mode of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {Boolean} [allowLockedChanges=true] If set to false, locked layers won't be changed.
 */
DuGR.toggleCollapseTransformation = function( groups, invert, comp, allowLockedChanges )
{
    function f( layer, enabled)
    {
        if (layer.locked && !allowLockedChanges) return enabled;
        if (!layer.canSetCollapseTransformation) return enabled;

        var locked = layer.locked;
        layer.locked = false;

        enabled = def( enabled, !layer.collapseTransformation );
        layer.collapseTransformation = enabled;

        layer.locked = locked;
        return enabled;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the guide mode of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {Boolean} [allowLockedChanges=true] If set to false, locked layers won't be changed.
 */
DuGR.toggleGuide = function( groups, invert, comp, allowLockedChanges )
{
    function f( layer, enabled)
    {
        if (layer.locked && !allowLockedChanges) return enabled;

        var locked = layer.locked;
        layer.locked = false;

        enabled = def( enabled, !layer.guideLayer );
        layer.guideLayer = enabled;

        layer.locked = locked;
        return enabled;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Changes the render quality of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {Boolean} [allowLockedChanges=true] If set to false, locked layers won't be changed.
 */
DuGR.toggleQuality = function( groups, invert, comp, allowLockedChanges )
{
    function f( layer, quality)
    {
        if (layer.locked && !allowLockedChanges) return quality;

        var locked = layer.locked;
        layer.locked = false;

        if (typeof quality === 'undefined')
        {
            quality = layer.quality;
			if (quality == LayerQuality.BEST) quality = LayerQuality.DRAFT;
			else if (quality == LayerQuality.DRAFT) quality = LayerQuality.WIREFRAME;
			else if (quality == LayerQuality.WIREFRAME) quality = LayerQuality.BEST;
        }
        layer.quality = quality;

        layer.locked = locked;
        return quality;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the the effects of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {Boolean} [allowLockedChanges=true] If set to false, locked layers won't be changed.
 */
DuGR.toggleEffects = function( groups, invert, comp, allowLockedChanges )
{
    function f( layer, enabled)
    {
        if (layer.locked && !allowLockedChanges) return enabled;

        var locked = layer.locked;
        layer.locked = false;

        enabled = def( enabled, !layer.effectsActive );
        layer.effectsActive = enabled;

        layer.locked = locked;
        return enabled;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the effects of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {Boolean} [allowLockedChanges=true] If set to false, locked layers won't be changed.
 */
DuGR.toggleFrameBlending = function( groups, invert, comp, allowLockedChanges )
{
    function f( layer, frameBlending)
    {
        if (layer.locked && !allowLockedChanges) return frameBlending;
        if (!(layer instanceof AVLayer)) return frameBlending;
        if (!layer.source.hasVideo) return;


        var locked = layer.locked;
        layer.locked = false;

        if (typeof frameBlending === 'undefined')
        {
            frameBlending = layer.frameBlendingType;
            if (frameBlending == FrameBlendingType.PIXEL_MOTION) frameBlending = FrameBlendingType.FRAME_MIX;
            else if (frameBlending == FrameBlendingType.FRAME_MIX) frameBlending = FrameBlendingType.NO_FRAME_BLEND;
            else if (frameBlending == FrameBlendingType.NO_FRAME_BLEND) frameBlending = FrameBlendingType.PIXEL_MOTION;
        }

        layer.frameBlendingType = frameBlending;

        layer.locked = locked;
        return frameBlending;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the motion blur of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {Boolean} [allowLockedChanges=true] If set to false, locked layers won't be changed.
 */
DuGR.toggleMotionBlur = function( groups, invert, comp, allowLockedChanges )
{
    function f( layer, enabled)
    {
        if (layer.locked && !allowLockedChanges) return enabled;

        var locked = layer.locked;
        layer.locked = false;

        enabled = def( enabled, !layer.motionBlur );
        layer.motionBlur = enabled;

        layer.locked = locked;
        return enabled;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the adjustment layer mode for all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {Boolean} [allowLockedChanges=true] If set to false, locked layers won't be changed.
 */
DuGR.toggleAdjustment = function( groups, invert, comp, allowLockedChanges )
{
    function f( layer, enabled)
    {
        if (layer.locked && !allowLockedChanges) return enabled;

        var locked = layer.locked;
        layer.locked = false;

        enabled = def( enabled, !layer.adjustmentLayer );
        layer.adjustmentLayer = enabled;

        layer.locked = locked;
        return enabled;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the 3D mode for all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {Boolean} [allowLockedChanges=true] If set to false, locked layers won't be changed.
 */
DuGR.toggleThreeD = function( groups, invert, comp, allowLockedChanges )
{
    function f( layer, enabled)
    {
        if (layer.locked && !allowLockedChanges) return enabled;

        var locked = layer.locked;
        layer.locked = false;

        enabled = def( enabled, !layer.threeDLayer );
        layer.threeDLayer = enabled;

        layer.locked = locked;
        return enabled;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Selects the layers.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {Boolean} [and=false] If false, selects the layers contained in at least one of the groups, otherwise in all the groups.
 */
DuGR.select = function( groups, invert, comp, and )
{
    function f( layer )
    {
        if (layer.locked) return;
        layer.selected = true;
    }

    function nf( layer )
    {
        if (layer.locked) return;
        layer.selected = false;
    }
    
    DuGR.do( groups, f, invert, comp, nf, and);
}

// Low level undocumented method. Creates the warning frame.
DuGR.createDugrLayer = function( comp, frameMode, isolationMode )
{
    comp = def( comp, DuAEProject.getActiveComp() );
    if (!comp) return;

    //add rect
    sq = comp.layers.addShape();
    sq.shy = true;
    sq.guideLayer = true;
    sq.name = i18n._(DuGR.DUGR_FRAME_NAME);

    var tag = DuAETag.set( sq, i18n._(DuGR.DUGR_FRAME_NAME), undefined );
    DuAETag.setValue(sq, DuAETag.Key.DUGR_ISOLATION_MODE, isolationMode, tag);

    if (frameMode != DuGR.WarningFrameMode.ABOVE) sq.moveToEnd();

    if (frameMode == DuGR.WarningFrameMode.NONE)
    {
        sq.locked = true;
        return sq;
    }

    var gr = sq("ADBE Root Vectors Group").addProperty("ADBE Vector Group");
    var groupContent = gr.property("ADBE Vectors Group");
    var shapeProp = groupContent.addProperty("ADBE Vector Shape - Rect");
    shapeProp("ADBE Vector Rect Size").setValue([comp.width,comp.height]);
    var stroke = groupContent.addProperty("ADBE Vector Graphic - Stroke");
    stroke("ADBE Vector Stroke Color").setValue([0.8,0,0]);
    stroke("ADBE Vector Stroke Opacity").setValue(50);
    stroke("ADBE Vector Stroke Width").setValue(comp.width/100);

    sq.locked = true;

    return sq;
}

// Low level undocumented method. Removes the warning frame.
DuGR.removeDugrLayer = function( comp )
{
    var layer = DuGR.getDugrLayer( comp );
    if (!layer) return;
    layer.locked = false;
    layer.remove();
}

// Low level undocumented method. Gets the warning frame.
DuGR.getDugrLayer = function( comp )
{
    comp = def( comp, DuAEProject.getActiveComp() );
    if (!comp) return;
    if (comp.numLayers == 0) return;

    //To be faster, check first and last layer first
    if (comp.layer(1).name == i18n._(DuGR.DUGR_FRAME_NAME)) return comp.layer(1);

    if (comp.layer(comp.numLayers).name == i18n._(DuGR.DUGR_FRAME_NAME))  return comp.layer(comp.numLayers);

    for (var i = 1, n = comp.numLayers; i <= n; i++)
    {
        if (comp.layer(i).name == i18n._(DuGR.DUGR_FRAME_NAME)) return comp.layer(i);
    }

    return null;
}

/**
 * Checks the isolation mode of the comp.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to check.
 * @return {DuGR.IsolationMode} The isolation mode.
 */
DuGR.isolationMode = function( comp )
{
    comp = def(comp, DuAEProject.getActiveComp() );
    if (!comp) return DuGR.IsolationMode.NONE;
    if (comp.numLayers == 0) return DuGR.IsolationMode.NONE;

    var dugrLayer = DuGR.getDugrLayer( comp );
    if (!dugrLayer) return DuGR.IsolationMode.NONE;

    var mode = DuAETag.getValue( dugrLayer, DuAETag.Key.DUGR_ISOLATION_MODE, DuAETag.Type.INT ) ;
    if (mode == null) mode = DuGR.IsolationMode.NONE;
    return mode;
}

/**
 * Activates the isolation mode.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to isolate.
 * @param {DuGR.WarningFrameMode} [frameMode=DuGR.WarningFrameMode.BELOW] How the warning frame must be shown
 * @param {DuGR.IsolationMode} [isolationMode=DuGR.IsolationMode.BOTH] What needs to be hidden.
 * @param {DuGR.CompIsolationMode} [compIsolationMode=DuGR.CompIsolationMode.HIDE] How to hide the layers
 * @param {Boolean} [lockHiddenLayers=true] whether to lock the layers being hidden.
 */
DuGR.isolate = function( groups, invert, comp, frameMode, isolationMode, compIsolationMode, lockHiddenLayers )
{
    comp = def(comp, DuAEProject.getActiveComp());
    if (!comp) return;
    if (comp.numLayers == 0) return;

    frameMode = def(frameMode, DuGR.WarningFrameMode.BELOW);
    isolationMode = def(isolationMode, DuGR.IsolationMode.BOTH);
    compIsolationMode = def(compIsolationMode, DuGR.CompIsolationMode.HIDE);
    lockHiddenLayers = def(lockHiddenLayers, true);
    invert = def(invert, false);

    if (isolationMode == DuGR.IsolationMode.NONE) DuGR.exitIsolation( comp, compositions );

    var timeline = isolationMode == DuGR.IsolationMode.BOTH || isolationMode == DuGR.IsolationMode.TIMELINE;
    var compPanel = isolationMode == DuGR.IsolationMode.BOTH || isolationMode == DuGR.IsolationMode.COMP_PANEL;

    // The previous mode
    var previousMode = DuGR.isolationMode( comp );

    // AE deselects the layers on creation of the new shape... Store the list
    var selectedLayers = DuAEComp.unselectLayers( comp );

    // Create the frame
    var dugrFrameLayer;
    if (previousMode == DuGR.IsolationMode.NONE) dugrFrameLayer = DuGR.createDugrLayer( comp, frameMode, isolationMode );
    else
    {
        dugrFrameLayer = DuGR.getDugrLayer(comp);
        DuAETag.setValue(dugrFrameLayer, DuAETag.Key.DUGR_ISOLATION_MODE, isolationMode);
    }

    // Restore the selection
    DuAEComp.selectLayers(selectedLayers);

    // Keep the shy status of the comp
    if (previousMode == DuGR.IsolationMode.NONE || previousMode == DuGR.IsolationMode.COMP_PANEL)
    {
        DuAETag.setValue( dugrFrameLayer, DuAETag.Key.DUGR_COMP_SHY, comp.hideShyLayers);
    }

    // Activate shy mode
    if (timeline) comp.hideShyLayers = true;

    // Loop layers to hide/shy them or restore their previous state.
    for (var i = 1, n = comp.numLayers; i <= n; i++)
    {
        var layer = comp.layer(i);

        // Get/Set the dugr tag
        var tag = DuAETag.set( layer, undefined, undefined );

        // whether to isolate the layer
        var hide = !DuGR.inGroups( layer, groups, tag );
        if (invert) hide = !hide;

        // whether the layer was already isolated
        var wasHidden = DuAETag.getValue( layer, DuAETag.Key.DUGR_HIDDEN, DuAETag.Type.BOOL, tag );

        // Nothing to do
        if (!wasHidden && !hide) continue;

        if (!wasHidden)
        {
            DuGR.saveStatus( layer, tag );
        }
        
        if (hide)
        {
            DuGR.hideLayer( layer, timeline, compPanel, compIsolationMode, lockHiddenLayers, tag );
        }
        else 
        {
            DuGR.unHideLayer( layer, tag );
        }
    }
}

// low-level undocumented method to save the status of a layer before hiding it.
DuGR.saveStatus = function( layer, tag )
{
    var tagParams = tag.getParameters();

    tagParams[DuAETag.Key.DUGR_HIDDEN] = true;
    tagParams[DuAETag.Key.DUGR_WAS_SHY] = layer.shy;
    tagParams[DuAETag.Key.DUGR_WAS_ENABLED] = layer.enabled;
    tagParams[DuAETag.Key.DUGR_WAS_LOCKED] = layer.locked;
    //params[DuAETag.Key.DUGR_HAD_TAG] = hadTag;

    var quality = 'best';
    if (layer.quality == LayerQuality.DRAFT) quality = 'draft';
    else if (layer.quality == LayerQuality.WIREFRAME) quality = 'wireframe';
    tagParams[DuAETag.Key.DUGR_PREVIOUS_QUALITY] = quality;

    tag.setParameters( tagParams );

    DuAETag.set( layer, undefined, tag );
}

// low-level undocumented method to put a layer to isolation
DuGR.hideLayer = function( layer, timeline, compPanel, compIsolationMode, lockHiddenLayers, tag )
{
    // Ignore the Dugr Layer
    if (layer.name == i18n._(DuGR.DUGR_FRAME_NAME)) return;

    if (DuGR.inGroups(layer, [DuGR.Group.IGNORED])) return;
    // Hide
    if (timeline) layer.shy = true;
    else // Restore shy status
    {
        var wasShy = DuAETag.getValue( layer, DuAETag.Key.DUGR_WAS_SHY, DuAETag.Type.BOOL, tag );
        if (wasShy) layer.shy = true;
        else if (wasShy != null) layer.shy = false;
    }
    if (compPanel)
    {
        if (compIsolationMode == DuGR.CompIsolationMode.WIREFRAME) layer.quality = LayerQuality.WIREFRAME;
        else layer.enabled = false;
    }
    else  // Restore visibility status
    {
        var wasEnabled = DuAETag.getValue( layer, DuAETag.Key.DUGR_WAS_ENABLED, DuAETag.Type.BOOL, tag );
        if (wasEnabled) layer.enabled = true;
        else if (wasEnabled != null)  layer.enabled = false;

        var quality = DuAETag.getValue( layer, DuAETag.Key.DUGR_PREVIOUS_QUALITY, DuAETag.Type.STRING, tag );
        if (quality == 'best') layer.quality = LayerQuality.BEST;
        else if (quality == 'draft') layer.quality = LayerQuality.DRAFT;
        else if (quality == 'wireframe') layer.quality = LayerQuality.WIREFRAME;
    }
    if (lockHiddenLayers) layer.locked = true;

    DuAETag.setValue( layer, DuAETag.Key.DUGR_HIDDEN, true, tag );
}

// low-level undocumented method to remove a layer from isolation
DuGR.unHideLayer = function ( layer, tag )
{
    if (DuGR.inGroups(layer, [DuGR.Group.IGNORED])) return;

    var wasShy = DuAETag.getValue( layer, DuAETag.Key.DUGR_WAS_SHY, DuAETag.Type.BOOL, tag );
    if (wasShy) layer.shy = true;
    else if (wasShy != null) layer.shy = false;

    var wasEnabled = DuAETag.getValue( layer, DuAETag.Key.DUGR_WAS_ENABLED, DuAETag.Type.BOOL, tag );
    if (wasEnabled) layer.enabled = true;
    else if (wasEnabled != null)  layer.enabled = false;

    var wasLocked = DuAETag.getValue( layer, DuAETag.Key.DUGR_WAS_LOCKED, DuAETag.Type.BOOL, tag );
    if (wasLocked) layer.locked = true;
    else if (wasLocked != null) layer.locked = false;

    var quality = DuAETag.getValue( layer, DuAETag.Key.DUGR_PREVIOUS_QUALITY, DuAETag.Type.STRING, tag );
    if (quality == 'best') layer.quality = LayerQuality.BEST;
    else if (quality == 'draft') layer.quality = LayerQuality.DRAFT;
    else if (quality == 'wireframe') layer.quality = LayerQuality.WIREFRAME;

    DuAETag.setValue( layer, DuAETag.Key.DUGR_HIDDEN, false, tag );

    /*var hadTag = DuAETag.getValue( layer, DuAETag.Key.DUGR_HAD_TAG, DuAETag.Type.BOOL, tag );
    if (hadTag) DuAETag.setValue( layer, DuAETag.Key.DUGR_HIDDEN, false );
    else DuAETag.remove( layer );*/
}

/**
 * Exits the isolation mode.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 */
DuGR.exitIsolation = function( comp )
{
    comp = def(comp, DuAEProject.getActiveComp());
    if (!comp) return;
    if (comp.numLayers == 0) return;

    // Reset shy mode
    var dugrFrameLayer = DuGR.getDugrLayer( comp )
    var wasShy = DuAETag.getValue( dugrFrameLayer, DuAETag.Key.DUGR_COMP_SHY, DuAETag.Type.BOOL);
    if (wasShy) comp.hideShyLayers = true;
    else comp.hideShyLayers = false;

    // Remove frame
    DuGR.removeDugrLayer( comp );  

    // Restore layers
    for (var i = 1, n = comp.numLayers; i <= n; i++)
    {
        var layer = comp.layer(i);
        var tag = DuAETag.get( layer );
        // Only hidden layers to improve performance
        var wasHidden = DuAETag.getValue( layer, DuAETag.Key.DUGR_HIDDEN, DuAETag.Type.BOOL, tag );
        if (wasHidden) DuGR.unHideLayer( layer, tag );
    }   
}

/**
 * Adds the selected layers to a (new) group
 * @param {string} group The group name
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 */
DuGR.addSelectedLayersToGroup = function( group, comp )
{
    comp = def(comp, DuAEProject.getActiveComp());
    if (!comp) return;
    if (comp.numLayers == 0) return;

    var layers = comp.selectedLayers;
    for(var i = 0, n = layers.length; i < n; i++)
    {
        var layer = layers[i];
        DuAETag.addGroup( layer, group );
    }
}

/**
 * Gets the list of all groups
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to read.
 * @param {Boolean} [selectedLayers=false] When true, only from selected layers.
 * @returns {DuList} The list of groups
 */
DuGR.listGroups = function ( comp, selectedLayers )
{
    comp = def(comp, DuAEProject.getActiveComp());
    selectedLayers = def(selectedLayers, false);

    if (!comp) return new DuList();
    if (comp.numLayers == 0) return new DuList();
    if (selectedLayers && comp.selectedLayers.length == 0) return new DuList();

    var layers;
    if (selectedLayers) layers = comp.selectedLayers;
    else layers = comp.layers;
    layers = new DuList(layers);

    var groups = new DuList();
    layers.do( function(layer )
    {
        var g = DuAETag.getGroups( layer );
        groups.mergeUnique( g );
    } );
    groups.sort();
    return groups;
}

/**
 * Renames an existing group (in the comp only)
 * @param {string} previousName The current name
 * @param {string} newName The new name
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to read.
 */
DuGR.renameGroup = function( previousName, newName, comp )
{
    comp = def(comp, DuAEProject.getActiveComp());
    if (!comp) return;
    if (comp.numLayers == 0) return;

    for (var i = 1, n = comp.numLayers; i < n; i++)
    {
        DuAETag.renameGroup( comp.layer(i), previousName, newName );
    }
}

/**
 * Removes a group from the composition
 * @param {string} group The group to remove
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {Boolean} [selectedLayers=false] When true, only from selected layers.
 */
DuGR.removeGroup = function( group, comp, selectedLayers )
{
    comp = def(comp, DuAEProject.getActiveComp());
    selectedLayers = def(selectedLayers, false);

    if (!comp) return;
    if (comp.numLayers == 0) return;
    if (selectedLayers && comp.selectedLayers.length == 0) return;

    var layers;
    if (selectedLayers) layers = comp.selectedLayers;
    else layers = comp.layers;
    layers = new DuList(layers);

    layers.do( function(layer )
    {
        DuAETag.removeGroup( layer, group );
    } );
}