From fc7a166705872714fcc889c67d8dbb73dca3cbec Mon Sep 17 00:00:00 2001
From: Erik Yuzwa <erik@erikyuzwa.com>
Date: Thu, 4 Feb 2016 13:42:49 -0500
Subject: [PATCH] @erikyuzwa added ability to add child component at specific
 index. closes #2540

---
 .gitignore                  |  2 ++
 CHANGELOG.md                |  1 +
 src/js/component.js         |  9 ++++++---
 src/js/player.js            |  5 +++++
 test/unit/component.test.js | 31 ++++++++++++++++++++++++++++++-
 5 files changed, 44 insertions(+), 4 deletions(-)

diff --git a/.gitignore b/.gitignore
index 4f3d583..b818b14 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,3 +28,5 @@ test/coverage/*
 .sass-cache
 
 dist/*
+
+.idea/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 726431f..04b88b5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ CHANGELOG
 * @hartman updated fullscreen and time controls for more consistent widths ([view](https://github.com/videojs/video.js/pull/2893))
 * @hartman Set a min-width for the progress slider of 4em ([view](https://github.com/videojs/video.js/pull/2902))
 * @misteroneill fixed iphone useragent detection ([view](https://github.com/videojs/video.js/pull/3077))
+* @erikyuzwa added ability to add child component at specific index ([view](https://github.com/videojs/video.js/pull/2540))
 
 --------------------
 
diff --git a/src/js/component.js b/src/js/component.js
index 4b5cc81..d821ae0 100644
--- a/src/js/component.js
+++ b/src/js/component.js
@@ -336,10 +336,11 @@ class Component {
    *
    * @param {String|Component} child The class name or instance of a child to add
    * @param {Object=} options Options, including options to be passed to children of the child.
+   * @param {Number} index into our children array to attempt to add the child
    * @return {Component} The child component (created by this process if a string was used)
    * @method addChild
    */
-  addChild(child, options={}) {
+  addChild(child, options={}, index=this.children_.length) {
     let component;
     let componentName;
 
@@ -388,7 +389,7 @@ class Component {
       component = child;
     }
 
-    this.children_.push(component);
+    this.children_.splice(index, 0, component);
 
     if (typeof component.id === 'function') {
       this.childIndex_[component.id()] = component;
@@ -405,7 +406,9 @@ class Component {
     // Add the UI object's element to the container div (box)
     // Having an element is not required
     if (typeof component.el === 'function' && component.el()) {
-      this.contentEl().appendChild(component.el());
+      let childNodes = this.contentEl().children;
+      let refNode = childNodes[index] || null;
+      this.contentEl().insertBefore(component.el(), refNode);
     }
 
     // Return so it can stored on parent object if desired.
diff --git a/src/js/player.js b/src/js/player.js
index e94a7dd..b15270e 100644
--- a/src/js/player.js
+++ b/src/js/player.js
@@ -300,7 +300,12 @@ class Player extends Component {
     if (tag.parentNode) {
       tag.parentNode.insertBefore(el, tag);
     }
+
+    // insert the tag as the first child of the player element
+    // then manually add it to the children array so that this.addChild
+    // will work properly for other components
     Dom.insertElFirst(tag, el); // Breaks iPhone, fixed in HTML5 setup.
+    this.children_.unshift(tag);
 
     this.el_ = el;
 
diff --git a/test/unit/component.test.js b/test/unit/component.test.js
index 8315128..978ac07 100644
--- a/test/unit/component.test.js
+++ b/test/unit/component.test.js
@@ -55,14 +55,43 @@ test('should add a child component', function(){
   ok(comp.getChildById(child.id()) === child);
 });
 
+test('should add a child component to an index', function(){
+  var comp = new Component(getFakePlayer());
+
+  var child = comp.addChild('component');
+
+  ok(comp.children().length === 1);
+  ok(comp.children()[0] === child);
+
+  var child0 = comp.addChild('component', {}, 0);
+  ok(comp.children().length === 2);
+  ok(comp.children()[0] === child0);
+  ok(comp.children()[1] === child);
+
+  var child1 = comp.addChild('component', {}, '2');
+  ok(comp.children().length === 3);
+  ok(comp.children()[2] === child1);
+
+  var child2 = comp.addChild('component', {}, undefined);
+  ok(comp.children().length === 4);
+  ok(comp.children()[3] === child2);
+
+  var child3 = comp.addChild('component', {}, -1);
+  ok(comp.children().length === 5);
+  ok(comp.children()[3] === child3);
+  ok(comp.children()[4] === child2);
+});
+
 test('addChild should throw if the child does not exist', function() {
   var comp = new Component(getFakePlayer());
 
   throws(function() {
-    comp.addChild('non-existent-child');
+   comp.addChild('non-existent-child');
   }, new Error('Component Non-existent-child does not exist'), 'addChild threw');
+
 });
 
+
 test('should init child components from options', function(){
   var comp = new Component(getFakePlayer(), {
     children: {
