function relayout(gd, astr, val) {
    gd = Lib.getGraphDiv(gd);
    helpers.clearPromiseQueue(gd);

    var aobj = {};
    if(typeof astr === 'string') {
        aobj[astr] = val;
    } else if(Lib.isPlainObject(astr)) {
        aobj = Lib.extendFlat({}, astr);
    } else {
        Lib.warn('Relayout fail.', astr, val);
        return Promise.reject();
    }

    if(Object.keys(aobj).length) gd.changed = true;

    var specs = _relayout(gd, aobj);
    var flags = specs.flags;

    // clear calcdata if required
    if(flags.calc) gd.calcdata = undefined;

    // fill in redraw sequence

    // even if we don't have anything left in aobj,
    // something may have happened within relayout that we
    // need to wait for
    var seq = [Plots.previousPromises];
    if(flags.layoutReplot) {
        seq.push(subroutines.layoutReplot);
    } else if(Object.keys(aobj).length) {
        axRangeSupplyDefaultsByPass(gd, flags, specs) || Plots.supplyDefaults(gd);

        if(flags.legend) seq.push(subroutines.doLegend);
        if(flags.layoutstyle) seq.push(subroutines.layoutStyles);
        if(flags.axrange) addAxRangeSequence(seq, specs.rangesAltered);
        if(flags.ticks) seq.push(subroutines.doTicksRelayout);
        if(flags.modebar) seq.push(subroutines.doModeBar);
        if(flags.camera) seq.push(subroutines.doCamera);
        if(flags.colorbars) seq.push(subroutines.doColorBars);

        seq.push(emitAfterPlot);
    }

    seq.push(
        Plots.rehover,
        Plots.redrag,
        Plots.reselect
    );

    Queue.add(gd,
        relayout, [gd, specs.undoit],
        relayout, [gd, specs.redoit]
    );

    var plotDone = Lib.syncOrAsync(seq, gd);
    if(!plotDone || !plotDone.then) plotDone = Promise.resolve(gd);

    return plotDone.then(function() {
        gd.emit('plotly_relayout', specs.eventData);
        return gd;
    });
}
relayout: update layout attributes of an existing plot

Can be called two ways:

Signature 1:
@param {String | HTMLDivElement} gd
 the id or dom element of the graph container div
@param {String} astr
 attribute string (like `'xaxis.range[0]'`) to update
@param {*} val
 value to give this attribute

Signature 2:
@param {String | HTMLDivElement} gd
 (as in signature 1)
@param {Object} aobj
 attribute object `{astr1: val1, astr2: val2 ...}`
 allows setting multiple attributes simultaneously