Creating and updating link to instagram image or video I've been using the InstagramPhotoLink extension for Opera for some time but instagram broke it recently. I thought I could try to fix it... and I did. 

The original extension is rather limited and it supports only a link to the first image and no videos so while fixing it I completely rewrote it in order to extend it.

It works by looking for an image or video element with a specific class and extracts the src attribute from it and puts it in a new a-element inside the media-container.

Switching images/videos is supported by the MutationObserver that updates the links when either a div or an attribute changes. 

I came across a problem where the MutatorObserver reacts too quickly and before the src attribute of the new image is set. I solved it by adding a small delay by adding a call to setTimeout to wait until everything is updated.

function createMediaLink() {

    let mo = new MutationObserver(mutations =&gt; {
        mutations.forEach(mutation =&gt; {
            if (mutation.type == "attributes") {
                //console.log(mutation.attributeName);
                addOrUpdateVideoLink();
            }
            if (mutation.type == "childList") {
                //console.log(mutation.childList);
                addOrUpdateImageLink();
            }
        });
    });

    return (
        addOrUpdateImageLink(mo) ||
        addOrUpdateVideoLink(mo)
    );
}

function addOrUpdateImageLink(mo) {

    let image = document.getElementsByClassName('FFVAD');

    if (image.length == 0) {
        return false;
    }

    image = image[0];

    if (mo) {
        // the nearest div to the image that stays there when switching images
        var div = document.getElementsByClassName("rQDP3");
        mo.observe(div[0], {
            childList: true
        });
    }

    setTimeout(() =&gt; {
        addOrUpdateMediaLink(image.src);
    }, 50);

    return true;
}

function addOrUpdateVideoLink(mo) {

    let video = document.getElementsByClassName('tWeCl');

    if (video.length == 0) {
        return false;
    }

    video = video[0];

    if (mo) {
        mo.observe(video, {
            attributes: true
        });
    }

    setTimeout(() =&gt; {
        addOrUpdateMediaLink(video.src);
    }, 50);

    return true;
}

function addOrUpdateMediaLink(src) {
    console.log(`src: ${src}`);
    let a = document.getElementsByClassName('_open_');
    if (a.length == 0) {
        a = document.createElement("a");
        a.className = "_open_";
        a.innerHTML = 'Open in new tab';
        a.target = "_blank";
        // media container
        document.getElementsByClassName("_97aPb")[0].appendChild(a);
    } else {
        a = a[0];
    }
    a.href = src;
}

createMediaLink();


I don't write javascript too often so this is the best I can do but I'm pretty sure this can still be improved. What do you think?
addOrUpdateImageLink() and addOrUpdateVideoLink() are essentially the same, therefore it would be better to create just one function that would accept different parameters dependent on whether our media is an image or video;
In createMediaLink() you invoke addOrUpdate………Link() functions before you declare them. It is not wrong, but it worsens readability and code flow;
Setting 50 ms delay is not the best way to achieve what you are trying to do. Since I don't know how the DOM workings look like, I left it unchanged though;
Use strict equality operator (===) wherever possible — it performs no type conversion;
Once you pick which quotes you use ('' or ""), stick to it. Generally, single quotes option is more popular and standard.


Rewrite

const addOrUpdateMediaLink = src =&gt; {
  if (!src) { return; }
  console.log(`src: ${src}`);

  let a = document.querySelector('._open_');

  if (!a) {
    a = document.createElement('a');
    [a.className, a.textContent, a.target] = ['_open_', 'Open in new tab', '_blank'];

    // media container
    document.querySelector('._97aPb').appendChild(a);
  }

  a.href = src;
};

const addOrUpdate = (selector, observeSelector, attr, mo) =&gt; {
  let media = document.querySelector(selector);

  if (!media) { return false; }

  if (mo) {
    const obj = {};
    obj[attr] = true;
    mo.observe(document.querySelector(observeSelector), obj);
  }

  setTimeout(() =&gt; addOrUpdateMediaLink(media.src), 50);

  return true;
};

const createMediaLink = () =&gt; {
  const selectors = {
    attributes: ['.tWeCl', '.tWeCl'],
    childList:  ['.FFVAD', '.rQDP3']
  };

  const mo = new MutationObserver(mutations =&gt; mutations.forEach(mutation =&gt; addOrUpdate(selectors[mutation.type][0])));

  return addOrUpdate(...selectors.childList, 'childList', mo) || addOrUpdate(...selectors.attributes, 'attributes', mo);
};

createMediaLink();