function mergeTemplates(oldTemplate, newTemplate) {
    // we don't care about speed here, just make sure we have a totally
    // distinct object from the previous template
    oldTemplate = Lib.extendDeep({}, oldTemplate);

    // sort keys so we always get annotationdefaults before annotations etc
    // so arrayTemplater will work right
    var oldKeys = Object.keys(oldTemplate).sort();
    var i, j;

    function mergeOne(oldVal, newVal, key) {
        if(isPlainObject(newVal) && isPlainObject(oldVal)) {
            mergeTemplates(oldVal, newVal);
        } else if(Array.isArray(newVal) && Array.isArray(oldVal)) {
            // Note: omitted `inclusionAttr` from arrayTemplater here,
            // it's irrelevant as we only want the resulting `_template`.
            var templater = Template.arrayTemplater({_template: oldTemplate}, key);
            for(j = 0; j < newVal.length; j++) {
                var item = newVal[j];
                var oldItem = templater.newItem(item)._template;
                if(oldItem) mergeTemplates(oldItem, item);
            }
            var defaultItems = templater.defaultItems();
            for(j = 0; j < defaultItems.length; j++) newVal.push(defaultItems[j]._template);

            // templateitemname only applies to receiving plots
            for(j = 0; j < newVal.length; j++) delete newVal[j].templateitemname;
        }
    }

    for(i = 0; i < oldKeys.length; i++) {
        var key = oldKeys[i];
        var oldVal = oldTemplate[key];
        if(key in newTemplate) {
            mergeOne(oldVal, newTemplate[key], key);
        } else newTemplate[key] = oldVal;

        // if this is a base key from the old template (eg xaxis), look for
        // extended keys (eg xaxis2) in the new template to merge into
        if(getBaseKey(key) === key) {
            for(var key2 in newTemplate) {
                var baseKey2 = getBaseKey(key2);
                if(key2 !== baseKey2 && baseKey2 === key && !(key2 in oldTemplate)) {
                    mergeOne(oldVal, newTemplate[key2], key);
                }
            }
        }
    }
}
Plotly.makeTemplate: create a template off an existing figure to reuse
style attributes on other figures.

Note: separated from the rest of templates because otherwise we get circular
references due to PlotSchema.

@param {object|DOM element|string} figure: The figure to base the template on
    should contain a trace array `figure.data`
    and a layout object `figure.layout`
@returns {object} template: the extracted template - can then be used as
    `layout.template` in another figure.