<commit_before>wysihtml5.polyfills = function(win, doc) {

  // TODO: in future try to replace most inline compability checks with polyfills for code readability

  // IE8 SUPPORT BLOCK
  // You can compile without all this if IE8 is not needed

  // String trim for ie8
  if (!String.prototype.trim) {
    (function() {
      // Make sure we trim BOM and NBSP
      var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
      String.prototype.trim = function() {
        return this.replace(rtrim, '');
      };
    })();
  }

  // addEventListener, removeEventListener
  (function() {
    var s_add = 'addEventListener',
        s_rem = 'removeEventListener';
    if( doc[s_add] ) return;
    win.Element.prototype[ s_add ] = win[ s_add ] = doc[ s_add ] = function( on, fn, self ) {
      return (self = this).attachEvent( 'on' + on, function(e){
        var e = e || win.event;
        e.target = e.target || e.srcElement;
        e.preventDefault  = e.preventDefault  || function(){e.returnValue = false};
        e.stopPropagation = e.stopPropagation || function(){e.cancelBubble = true};
        e.which = e.button ? ( e.button === 2 ? 3 : e.button === 4 ? 2 : e.button ) : e.keyCode;
        fn.call(self, e);
      });
    };
    win.Element.prototype[ s_rem ] = win[ s_rem ] = doc[ s_rem ] = function( on, fn ) {
      return this.detachEvent( 'on' + on, fn );
    };
  })();

  // element.textContent polyfill.
  if (Object.defineProperty && Object.getOwnPropertyDescriptor && Object.getOwnPropertyDescriptor(win.Element.prototype, "textContent") && !Object.getOwnPropertyDescriptor(win.Element.prototype, "textContent").get) {
  	(function() {
  		var innerText = Object.getOwnPropertyDescriptor(win.Element.prototype, "innerText");
  		Object.defineProperty(win.Element.prototype, "textContent",
  			{
  				get: function() {
  					return innerText.get.call(this);
  				},
  				set: function(s) {
  					return innerText.set.call(this, s);
  				}
  			}
  		);
  	})();
  }

  // isArray polyfill for ie8
  if(!Array.isArray) {
    Array.isArray = function(arg) {
      return Object.prototype.toString.call(arg) === '[object Array]';
    };
  }

  // Array indexOf for ie8
  if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function(a,f) {
      for(var c=this.length,r=-1,d=f>>>0; ~(c-d); r=this[--c]===a?c:r);
      return r;
    };
  }

  // Function.prototype.bind()
  // TODO: clean the code from variable 'that' as it can be confusing
  if (!Function.prototype.bind) {
    Function.prototype.bind = function(oThis) {
      if (typeof this !== 'function') {
        // closest thing possible to the ECMAScript 5
        // internal IsCallable function
        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
      }

      var aArgs   = Array.prototype.slice.call(arguments, 1),
          fToBind = this,
          fNOP    = function() {},
          fBound  = function() {
            return fToBind.apply(this instanceof fNOP && oThis
                   ? this
                   : oThis,
                   aArgs.concat(Array.prototype.slice.call(arguments)));
          };

      fNOP.prototype = this.prototype;
      fBound.prototype = new fNOP();

      return fBound;
    };
  }

  // Element.matches Adds ie8 support and unifies nonstandard function names in other browsers
  win.Element && function(ElementPrototype) {
    ElementPrototype.matches = ElementPrototype.matches ||
    ElementPrototype.matchesSelector ||
    ElementPrototype.mozMatchesSelector ||
    ElementPrototype.msMatchesSelector ||
    ElementPrototype.oMatchesSelector ||
    ElementPrototype.webkitMatchesSelector ||
    function (selector) {
      var node = this, nodes = (node.parentNode || node.document).querySelectorAll(selector), i = -1;
      while (nodes[++i] && nodes[i] != node);
      return !!nodes[i];
    };
  }(win.Element.prototype);

  // Element.classList for ie8-9 (toggle all IE)
  // source http://purl.eligrey.com/github/classList.js/blob/master/classList.js

  if ("document" in win) {
    // Full polyfill for browsers with no classList support
    if (!("classList" in doc.createElement("_"))) {
      (function(view) {
        "use strict";
        if (!('Element' in view)) return;

        var
          classListProp = "classList",
          protoProp = "prototype",
          elemCtrProto = view.Element[protoProp],
          objCtr = Object,
          strTrim = String[protoProp].trim || function() {
            return this.replace(/^\s+|\s+$/g, "");
          },
          arrIndexOf = Array[protoProp].indexOf || function(item) {
            var
              i = 0,
              len = this.length;
            for (; i < len; i++) {
              if (i in this && this[i] === item) {
                return i;
              }
            }
            return -1;
          }, // Vendors: please allow content code to instantiate DOMExceptions
          DOMEx = function(type, message) {
            this.name = type;
            this.code = DOMException[type];
            this.message = message;
          },
          checkTokenAndGetIndex = function(classList, token) {
            if (token === "") {
              throw new DOMEx(
                "SYNTAX_ERR", "An invalid or illegal string was specified"
              );
            }
            if (/\s/.test(token)) {
              throw new DOMEx(
                "INVALID_CHARACTER_ERR", "String contains an invalid character"
              );
            }
            return arrIndexOf.call(classList, token);
          },
          ClassList = function(elem) {
            var
              trimmedClasses = strTrim.call(elem.getAttribute("class") || ""),
              classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [],
              i = 0,
              len = classes.length;
            for (; i < len; i++) {
              this.push(classes[i]);
            }
            this._updateClassName = function() {
              elem.setAttribute("class", this.toString());
            };
          },
          classListProto = ClassList[protoProp] = [],
          classListGetter = function() {
            return new ClassList(this);
          };
        // Most DOMException implementations don't allow calling DOMException's toString()
        // on non-DOMExceptions. Error's toString() is sufficient here.
        DOMEx[protoProp] = Error[protoProp];
        classListProto.item = function(i) {
          return this[i] || null;
        };
        classListProto.contains = function(token) {
          token += "";
          return checkTokenAndGetIndex(this, token) !== -1;
        };
        classListProto.add = function() {
          var
            tokens = arguments,
            i = 0,
            l = tokens.length,
            token, updated = false;
          do {
            token = tokens[i] + "";
            if (checkTokenAndGetIndex(this, token) === -1) {
              this.push(token);
              updated = true;
            }
          }
          while (++i < l);

          if (updated) {
            this._updateClassName();
          }
        };
        classListProto.remove = function() {
          var
            tokens = arguments,
            i = 0,
            l = tokens.length,
            token, updated = false,
            index;
          do {
            token = tokens[i] + "";
            index = checkTokenAndGetIndex(this, token);
            while (index !== -1) {
              this.splice(index, 1);
              updated = true;
              index = checkTokenAndGetIndex(this, token);
            }
          }
          while (++i < l);

          if (updated) {
            this._updateClassName();
          }
        };
        classListProto.toggle = function(token, force) {
          token += "";

          var
            result = this.contains(token),
            method = result ?
            force !== true && "remove" :
            force !== false && "add";

          if (method) {
            this[method](token);
          }

          if (force === true || force === false) {
            return force;
          } else {
            return !result;
          }
        };
        classListProto.toString = function() {
          return this.join(" ");
        };

        if (objCtr.defineProperty) {
          var classListPropDesc = {
            get: classListGetter,
            enumerable: true,
            configurable: true
          };
          try {
            objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
          } catch (ex) { // IE 8 doesn't support enumerable:true
            if (ex.number === -0x7FF5EC54) {
              classListPropDesc.enumerable = false;
              objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
            }
          }
        } else if (objCtr[protoProp].__defineGetter__) {
          elemCtrProto.__defineGetter__(classListProp, classListGetter);
        }

      }(win));

    } else if ("DOMTokenList" in win) {
      // There is full or partial native classList support, so just check if we need
      // to normalize the add/remove and toggle APIs.
      // DOMTokenList is expected to exist (removes conflicts with multiple polyfills present on site)

      (function() {
        "use strict";

        var testElement = doc.createElement("_");

        testElement.classList.add("c1", "c2");

        // Polyfill for IE 10/11 and Firefox <26, where classList.add and
        // classList.remove exist but support only one argument at a time.
        if (!testElement.classList.contains("c2")) {
          var createMethod = function(method) {
            var original = win.DOMTokenList.prototype[method];

            win.DOMTokenList.prototype[method] = function(token) {
              var i, len = arguments.length;

              for (i = 0; i < len; i++) {
                token = arguments[i];
                original.call(this, token);
              }
            };
          };
          createMethod('add');
          createMethod('remove');
        }

        testElement.classList.toggle("c3", false);

        // Polyfill for IE 10 and Firefox <24, where classList.toggle does not
        // support the second argument.
        if (testElement.classList.contains("c3")) {
          var _toggle = win.DOMTokenList.prototype.toggle;

          win.DOMTokenList.prototype.toggle = function(token, force) {
            if (1 in arguments && !this.contains(token) === !force) {
              return force;
            } else {
              return _toggle.call(this, token);
            }
          };

        }

        testElement = null;
      }());

    }

  }

  // Safary has a bug of not restoring selection after node.normalize correctly.
  // Detects the misbegaviour and patches it
  var normalizeHasCaretError = function() {
    if ("createRange" in document && "getSelection" in window) {
      var e = document.createElement('div'),
          t1 = document.createTextNode('a'),
          t2 = document.createTextNode('a'),
          t3 = document.createTextNode('a'),
          r = document.createRange(),
          s, ret;

      e.setAttribute('contenteditable', 'true');
      e.appendChild(t1);
      e.appendChild(t2);
      e.appendChild(t3);
      document.body.appendChild(e);
      r.setStart(t2, 1);
      r.setEnd(t2, 1);

      s = window.getSelection();
      s.removeAllRanges();
      s.addRange(r);
      e.normalize();
      s = window.getSelection();

      ret = (e.childNodes.length !== 1 || s.anchorNode !== e.firstChild || s.anchorOffset !== 2);
      e.parentNode.removeChild(e);
      s.removeAllRanges();
      return ret;
    }
  };

  var getTextNodes = function(node){
    var all = [];
    for (node=node.firstChild;node;node=node.nextSibling){
      if (node.nodeType == 3) {
          all.push(node);
      } else {
        all = all.concat(getTextNodes(node));
      }
    }
    return all;
  };

  var normalizeFix = function() {
    var f = Node.prototype.normalize;
    var nf = function() {
      var texts = getTextNodes(this),
          s = this.ownerDocument.defaultView.getSelection(),
          anode = s.anchorNode,
          aoffset = s.anchorOffset,
          aelement = anode && anode.nodeType === 1 && anode.childNodes.length > 0 ? anode.childNodes[aoffset] : undefined,
          fnode = s.focusNode,
          foffset = s.focusOffset,
          felement = fnode && fnode.nodeType === 1 && foffset > 0 ? fnode.childNodes[foffset -1] : undefined,
          r = this.ownerDocument.createRange(),
          prevTxt = texts.shift(),
          curText = prevTxt ? texts.shift() : null;

      if ((anode === fnode && foffset < aoffset) || (anode !== fnode && (anode.compareDocumentPosition(fnode) & Node.DOCUMENT_POSITION_PRECEDING) && !(anode.compareDocumentPosition(fnode) & Node.DOCUMENT_POSITION_CONTAINS))) {
        fnode = [anode, anode = fnode][0];
        foffset = [aoffset, aoffset = foffset][0];
      }

      while(prevTxt && curText) {
        if (curText.previousSibling && curText.previousSibling === prevTxt) {
          if (anode === curText) {
            anode = prevTxt;
            aoffset = prevTxt.nodeValue.length +  aoffset;
          }
          if (fnode === curText) {
            fnode = prevTxt;
            foffset = prevTxt.nodeValue.length +  foffset;
          }
          prevTxt.nodeValue = prevTxt.nodeValue + curText.nodeValue;
          curText.parentNode.removeChild(curText);
          curText = texts.shift();
        } else {
          prevTxt = curText;
          curText = texts.shift();
        }
      }

      if (felement) {
        foffset = Array.prototype.indexOf.call(felement.parentNode.childNodes, felement) + 1;
      }

      if (aelement) {
        aoffset = Array.prototype.indexOf.call(aelement.parentNode.childNodes, aelement);
      }

      if (anode && anode.parentNode && fnode && fnode.parentNode) {
        r.setStart(anode, aoffset);
        r.setEnd(fnode, foffset);
        s.removeAllRanges();
        s.addRange(r);
      }
    };
    Node.prototype.normalize = nf;
  };

  if ("Node" in window && "normalize" in Node.prototype && normalizeHasCaretError()) {
    normalizeFix();
  }
};

wysihtml5.polyfills(window, document);
<commit_msg>Fix IE Edge element.normalise polyfill crashes (caret case)<commit_after>wysihtml5.polyfills = function(win, doc) {

  // TODO: in future try to replace most inline compability checks with polyfills for code readability

  // IE8 SUPPORT BLOCK
  // You can compile without all this if IE8 is not needed

  // String trim for ie8
  if (!String.prototype.trim) {
    (function() {
      // Make sure we trim BOM and NBSP
      var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
      String.prototype.trim = function() {
        return this.replace(rtrim, '');
      };
    })();
  }

  // addEventListener, removeEventListener
  (function() {
    var s_add = 'addEventListener',
        s_rem = 'removeEventListener';
    if( doc[s_add] ) return;
    win.Element.prototype[ s_add ] = win[ s_add ] = doc[ s_add ] = function( on, fn, self ) {
      return (self = this).attachEvent( 'on' + on, function(e){
        var e = e || win.event;
        e.target = e.target || e.srcElement;
        e.preventDefault  = e.preventDefault  || function(){e.returnValue = false};
        e.stopPropagation = e.stopPropagation || function(){e.cancelBubble = true};
        e.which = e.button ? ( e.button === 2 ? 3 : e.button === 4 ? 2 : e.button ) : e.keyCode;
        fn.call(self, e);
      });
    };
    win.Element.prototype[ s_rem ] = win[ s_rem ] = doc[ s_rem ] = function( on, fn ) {
      return this.detachEvent( 'on' + on, fn );
    };
  })();

  // element.textContent polyfill.
  if (Object.defineProperty && Object.getOwnPropertyDescriptor && Object.getOwnPropertyDescriptor(win.Element.prototype, "textContent") && !Object.getOwnPropertyDescriptor(win.Element.prototype, "textContent").get) {
  	(function() {
  		var innerText = Object.getOwnPropertyDescriptor(win.Element.prototype, "innerText");
  		Object.defineProperty(win.Element.prototype, "textContent",
  			{
  				get: function() {
  					return innerText.get.call(this);
  				},
  				set: function(s) {
  					return innerText.set.call(this, s);
  				}
  			}
  		);
  	})();
  }

  // isArray polyfill for ie8
  if(!Array.isArray) {
    Array.isArray = function(arg) {
      return Object.prototype.toString.call(arg) === '[object Array]';
    };
  }

  // Array indexOf for ie8
  if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function(a,f) {
      for(var c=this.length,r=-1,d=f>>>0; ~(c-d); r=this[--c]===a?c:r);
      return r;
    };
  }

  // Function.prototype.bind()
  // TODO: clean the code from variable 'that' as it can be confusing
  if (!Function.prototype.bind) {
    Function.prototype.bind = function(oThis) {
      if (typeof this !== 'function') {
        // closest thing possible to the ECMAScript 5
        // internal IsCallable function
        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
      }

      var aArgs   = Array.prototype.slice.call(arguments, 1),
          fToBind = this,
          fNOP    = function() {},
          fBound  = function() {
            return fToBind.apply(this instanceof fNOP && oThis
                   ? this
                   : oThis,
                   aArgs.concat(Array.prototype.slice.call(arguments)));
          };

      fNOP.prototype = this.prototype;
      fBound.prototype = new fNOP();

      return fBound;
    };
  }

  // Element.matches Adds ie8 support and unifies nonstandard function names in other browsers
  win.Element && function(ElementPrototype) {
    ElementPrototype.matches = ElementPrototype.matches ||
    ElementPrototype.matchesSelector ||
    ElementPrototype.mozMatchesSelector ||
    ElementPrototype.msMatchesSelector ||
    ElementPrototype.oMatchesSelector ||
    ElementPrototype.webkitMatchesSelector ||
    function (selector) {
      var node = this, nodes = (node.parentNode || node.document).querySelectorAll(selector), i = -1;
      while (nodes[++i] && nodes[i] != node);
      return !!nodes[i];
    };
  }(win.Element.prototype);

  // Element.classList for ie8-9 (toggle all IE)
  // source http://purl.eligrey.com/github/classList.js/blob/master/classList.js

  if ("document" in win) {
    // Full polyfill for browsers with no classList support
    if (!("classList" in doc.createElement("_"))) {
      (function(view) {
        "use strict";
        if (!('Element' in view)) return;

        var
          classListProp = "classList",
          protoProp = "prototype",
          elemCtrProto = view.Element[protoProp],
          objCtr = Object,
          strTrim = String[protoProp].trim || function() {
            return this.replace(/^\s+|\s+$/g, "");
          },
          arrIndexOf = Array[protoProp].indexOf || function(item) {
            var
              i = 0,
              len = this.length;
            for (; i < len; i++) {
              if (i in this && this[i] === item) {
                return i;
              }
            }
            return -1;
          }, // Vendors: please allow content code to instantiate DOMExceptions
          DOMEx = function(type, message) {
            this.name = type;
            this.code = DOMException[type];
            this.message = message;
          },
          checkTokenAndGetIndex = function(classList, token) {
            if (token === "") {
              throw new DOMEx(
                "SYNTAX_ERR", "An invalid or illegal string was specified"
              );
            }
            if (/\s/.test(token)) {
              throw new DOMEx(
                "INVALID_CHARACTER_ERR", "String contains an invalid character"
              );
            }
            return arrIndexOf.call(classList, token);
          },
          ClassList = function(elem) {
            var
              trimmedClasses = strTrim.call(elem.getAttribute("class") || ""),
              classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [],
              i = 0,
              len = classes.length;
            for (; i < len; i++) {
              this.push(classes[i]);
            }
            this._updateClassName = function() {
              elem.setAttribute("class", this.toString());
            };
          },
          classListProto = ClassList[protoProp] = [],
          classListGetter = function() {
            return new ClassList(this);
          };
        // Most DOMException implementations don't allow calling DOMException's toString()
        // on non-DOMExceptions. Error's toString() is sufficient here.
        DOMEx[protoProp] = Error[protoProp];
        classListProto.item = function(i) {
          return this[i] || null;
        };
        classListProto.contains = function(token) {
          token += "";
          return checkTokenAndGetIndex(this, token) !== -1;
        };
        classListProto.add = function() {
          var
            tokens = arguments,
            i = 0,
            l = tokens.length,
            token, updated = false;
          do {
            token = tokens[i] + "";
            if (checkTokenAndGetIndex(this, token) === -1) {
              this.push(token);
              updated = true;
            }
          }
          while (++i < l);

          if (updated) {
            this._updateClassName();
          }
        };
        classListProto.remove = function() {
          var
            tokens = arguments,
            i = 0,
            l = tokens.length,
            token, updated = false,
            index;
          do {
            token = tokens[i] + "";
            index = checkTokenAndGetIndex(this, token);
            while (index !== -1) {
              this.splice(index, 1);
              updated = true;
              index = checkTokenAndGetIndex(this, token);
            }
          }
          while (++i < l);

          if (updated) {
            this._updateClassName();
          }
        };
        classListProto.toggle = function(token, force) {
          token += "";

          var
            result = this.contains(token),
            method = result ?
            force !== true && "remove" :
            force !== false && "add";

          if (method) {
            this[method](token);
          }

          if (force === true || force === false) {
            return force;
          } else {
            return !result;
          }
        };
        classListProto.toString = function() {
          return this.join(" ");
        };

        if (objCtr.defineProperty) {
          var classListPropDesc = {
            get: classListGetter,
            enumerable: true,
            configurable: true
          };
          try {
            objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
          } catch (ex) { // IE 8 doesn't support enumerable:true
            if (ex.number === -0x7FF5EC54) {
              classListPropDesc.enumerable = false;
              objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
            }
          }
        } else if (objCtr[protoProp].__defineGetter__) {
          elemCtrProto.__defineGetter__(classListProp, classListGetter);
        }

      }(win));

    } else if ("DOMTokenList" in win) {
      // There is full or partial native classList support, so just check if we need
      // to normalize the add/remove and toggle APIs.
      // DOMTokenList is expected to exist (removes conflicts with multiple polyfills present on site)

      (function() {
        "use strict";

        var testElement = doc.createElement("_");

        testElement.classList.add("c1", "c2");

        // Polyfill for IE 10/11 and Firefox <26, where classList.add and
        // classList.remove exist but support only one argument at a time.
        if (!testElement.classList.contains("c2")) {
          var createMethod = function(method) {
            var original = win.DOMTokenList.prototype[method];

            win.DOMTokenList.prototype[method] = function(token) {
              var i, len = arguments.length;

              for (i = 0; i < len; i++) {
                token = arguments[i];
                original.call(this, token);
              }
            };
          };
          createMethod('add');
          createMethod('remove');
        }

        testElement.classList.toggle("c3", false);

        // Polyfill for IE 10 and Firefox <24, where classList.toggle does not
        // support the second argument.
        if (testElement.classList.contains("c3")) {
          var _toggle = win.DOMTokenList.prototype.toggle;

          win.DOMTokenList.prototype.toggle = function(token, force) {
            if (1 in arguments && !this.contains(token) === !force) {
              return force;
            } else {
              return _toggle.call(this, token);
            }
          };

        }

        testElement = null;
      }());

    }

  }

  // Safary has a bug of not restoring selection after node.normalize correctly.
  // Detects the misbegaviour and patches it
  var normalizeHasCaretError = function() {
    if ("createRange" in document && "getSelection" in window) {
      var e = document.createElement('div'),
          t1 = document.createTextNode('a'),
          t2 = document.createTextNode('a'),
          t3 = document.createTextNode('a'),
          r = document.createRange(),
          s, ret;

      e.setAttribute('contenteditable', 'true');
      e.appendChild(t1);
      e.appendChild(t2);
      e.appendChild(t3);
      document.body.appendChild(e);
      r.setStart(t2, 1);
      r.setEnd(t2, 1);

      s = window.getSelection();
      s.removeAllRanges();
      s.addRange(r);
      e.normalize();
      s = window.getSelection();

      ret = (e.childNodes.length !== 1 || s.anchorNode !== e.firstChild || s.anchorOffset !== 2);
      e.parentNode.removeChild(e);
      s.removeAllRanges();
      return ret;
    }
  };

  var getTextNodes = function(node){
    var all = [];
    for (node=node.firstChild;node;node=node.nextSibling){
      if (node.nodeType == 3) {
          all.push(node);
      } else {
        all = all.concat(getTextNodes(node));
      }
    }
    return all;
  };

  var normalizeFix = function() {
    var f = Node.prototype.normalize;
    var nf = function() {
      var texts = getTextNodes(this),
          s = this.ownerDocument.defaultView.getSelection(),
          anode = s.anchorNode,
          aoffset = s.anchorOffset,
          aelement = anode && anode.nodeType === 1 && anode.childNodes.length > 0 ? anode.childNodes[aoffset] : undefined,
          fnode = s.focusNode,
          foffset = s.focusOffset,
          felement = fnode && fnode.nodeType === 1 && foffset > 0 ? fnode.childNodes[foffset -1] : undefined,
          r = this.ownerDocument.createRange(),
          prevTxt = texts.shift(),
          curText = prevTxt ? texts.shift() : null;

      if (felement && felement.nodeType === 3) {
        fnode = felement;
        foffset = felement.nodeValue.length;
        felement = undefined;
      }

      if (aelement && aelement.nodeType === 3) {
        anode = aelement;
        aoffset = aelement.nodeValue.length;
        aelement = undefined;
      }

      if ((anode === fnode && foffset < aoffset) || (anode !== fnode && (anode.compareDocumentPosition(fnode) & Node.DOCUMENT_POSITION_PRECEDING) && !(anode.compareDocumentPosition(fnode) & Node.DOCUMENT_POSITION_CONTAINS))) {
        fnode = [anode, anode = fnode][0];
        foffset = [aoffset, aoffset = foffset][0];
      }

      while(prevTxt && curText) {
        if (curText.previousSibling && curText.previousSibling === prevTxt) {
          if (anode === curText) {
            anode = prevTxt;
            aoffset = prevTxt.nodeValue.length +  aoffset;
          }
          if (fnode === curText) {
            fnode = prevTxt;
            foffset = prevTxt.nodeValue.length +  foffset;
          }
          prevTxt.nodeValue = prevTxt.nodeValue + curText.nodeValue;
          curText.parentNode.removeChild(curText);
          curText = texts.shift();
        } else {
          prevTxt = curText;
          curText = texts.shift();
        }
      }

      if (felement) {
        foffset = Array.prototype.indexOf.call(felement.parentNode.childNodes, felement) + 1;
      }

      if (aelement) {
        aoffset = Array.prototype.indexOf.call(aelement.parentNode.childNodes, aelement);
      }

      if (anode && anode.parentNode && fnode && fnode.parentNode) {
        r.setStart(anode, aoffset);
        r.setEnd(fnode, foffset);
        s.removeAllRanges();
        s.addRange(r);
      }
    };
    Node.prototype.normalize = nf;
  };

  if ("Node" in window && "normalize" in Node.prototype && normalizeHasCaretError()) {
    normalizeFix();
  }
};

wysihtml5.polyfills(window, document);
<|endoftext|>


<commit_before>define(['jquery', 'webmaker-auth-client/webmaker-auth-client'], function ($, WebmakerAuthClient) {
  'use strict';

  var auth = new WebmakerAuthClient({
    csrfToken: $('meta[name="csrf-token"]').attr('content')
  });

  var loginEl = $('.webmaker-login');
  var logoutEl = $('.webmaker-logout');

  function toggleUserData(userData) {
    var placeHolder = $('#identity');
    var userElement = $('div.user-name');

    if (userData) {
      placeHolder.html('' +
        '' + userData.username + "");
    } else {
      userElement.html('');
    }
  }

  function onLogin(user) {
    $('#webmaker-nav').addClass('loggedin');
    toggleUserData(user);
    loginEl.hide();
    logoutEl.show();
  }

  function onLogout() {
    $('#webmaker-nav').removeClass('loggedin');
    toggleUserData();
    loginEl.show();
    logoutEl.hide();
  }

  auth.on('login', onLogin);
  auth.on('logout', onLogout);

  auth.on('error', function (err) {
    window.console.log(err);
  });

  loginEl.click(auth.login);
  logoutEl.click(auth.logout);

  return auth;
});
<commit_msg>[#945389] Add ga events for login, logout, sign up clicked, and user created.<commit_after>define(['jquery', 'webmaker-auth-client/webmaker-auth-client', 'analytics'], function ($, WebmakerAuthClient, analytics) {
  'use strict';

  var auth = new WebmakerAuthClient({
    csrfToken: $('meta[name="csrf-token"]').attr('content')
  });

  var loginEl = $('.webmaker-login');
  var logoutEl = $('.webmaker-logout');

  function toggleUserData(userData) {
    var placeHolder = $('#identity');
    var userElement = $('div.user-name');

    if (userData) {
      placeHolder.html('' +
        '' + userData.username + "");
    } else {
      userElement.html('');
    }
  }

  function onLogin(user) {
    $('#webmaker-nav').addClass('loggedin');
    toggleUserData(user);
    loginEl.hide();
    logoutEl.show();
  }

  function onLogout() {
    $('#webmaker-nav').removeClass('loggedin');
    toggleUserData();
    loginEl.show();
    logoutEl.hide();
  }

  auth.on('login', onLogin);
  auth.on('logout', onLogout);

  auth.on('error', function (err) {
    window.console.log(err);
  });

  loginEl.click(auth.login);
  loginEl.click(function () {
    analytics.event('Webmaker Login Clicked');
  });
  logoutEl.click(function () {
    analytics.event('Webmaker Logout Clicked');
  });
  auth.on('newuser', function () {
    analytics.event('Webmaker New User Started');
  });
  auth.on('login', function (data, message) {
    if (message === 'user created') {
      analytics.event('Webmaker New User Created', {
        nonInteraction: true
      });
    }
  });
  logoutEl.click(auth.logout);

  return auth;
});
<|endoftext|>


<commit_before>/* global bundle, $, d3plus */

var d3 = require("d3");

var COLOR_TABLE = [
  "#C8D2D9", "#97ABB8", "#FFBEB9", "#FFD1CE"
];

/* This is a poor-man's estimate, given the fact that measuring the
 * actual size of the characters (accounting for kerning) will involve
 * significant DOM mutation. */
var INDIVIDUAL_CHARACTER_SIZE = 10;

/**
 * drawBarForDataPoint
 */
function drawBarForDataPoint(svg, dataPoint, index, length, offsetAmount, dataMax) {
  var computeTextY = function computeTextY() {
    var chartHeight = this.parentNode.parentNode.clientHeight;
    var barSpacing = 10;
    var textHeight = 15;
    return (((chartHeight - (barSpacing * length)) /
            length) / 2) + (textHeight / 2);
  };
  var computeBarWidth = function computeBarWidth() {
    var parentWidth = (this.parentNode.parentNode.clientWidth -
                       offsetAmount);
    var x = d3.scaleLinear().domain([0, dataMax])
                            .range([0, parentWidth]);
    return x(dataPoint.value);
  };
  var g = svg.append("g")
             .attr("transform", function computeBarY() {
               var chartHeight = this.parentNode.clientHeight;
               var barSpacing = 10;
               var barHeight = ((chartHeight - (barSpacing *
                                                length)) /
                                length);

               var y = (index * barSpacing) + (index * barHeight);
               return "translate(0.0, " + y + ")";
             });
  g.append("rect")
   .attr("height", function computeBarHeight() {
     var chartHeight = this.parentNode.parentNode.clientHeight;
     var barSpacing = 10;
     return ((chartHeight - (barSpacing * length)) /
             length);
   })
   .transition()
   .delay(index * 50)
   .duration(750)
   .ease(d3.easeElastic.period(0.4))
   .attr("width", computeBarWidth)
   .attr("rx", "15")
   .attr("ry", "15")
   .attr("x", String(offsetAmount))
   .attr("fill", COLOR_TABLE[index % COLOR_TABLE.length]);
  g.append("text")
   .attr("fill", "#000000")
   .text(dataPoint.name)
   .attr("y", computeTextY)
   .attr("x", 10);
  g.append("text")
   .attr("fill", "#ffffff")
   .text(dataPoint.value)
   .attr("y", computeTextY)
   .transition()
   .delay(index * 50)
   .duration(750)
   .ease(d3.easeElastic.period(0.4))
   .attr("x", function computeNumberX() {
     var barWidth = computeBarWidth.call(this);
     var label = String(dataPoint.value);
     var labelSize = label.length * INDIVIDUAL_CHARACTER_SIZE;
     return (barWidth - labelSize) + offsetAmount;
   });
}

/**
 * drawBarChart
 *
 * Draws a bar chart using data on the given node.
 *
 * @data {array} - The data to use to draw the chart
 * @returns {object} - The completed node
 */
function drawBarChart(data) {
  /* The individual points of data, not including labels */
  var dataPoints = data.map(function onEachDataPoint(w) {
    return w.value;
  });

  var svg = d3.select("div.chart")
              .append("svg")
              .attr("class", "svgchart")
              .attr("width", function computeWidth() {
                return this.parentNode.clientWidth * 0.80;
              })
              .attr("height", function computeHeight() {
                return this.parentNode.clientHeight * 0.80;
              })
              .attr("style", function computeTransform() {
                var x = this.parentNode.clientWidth * 0.10;
                var y = this.parentNode.clientHeight * 0.10;
                return ["transform: translate(", x, "px,", y, "px", ")"].join("");
              });

  /* Estimated sizes of the labels and the bars if no scaling was applied -
   * use this to determine whether an offset should be applied to the chart
   * based on the width of the smallest bar and its corresponding label. If
   * a bar's label size exceeds the size of the bar, we need to make
   * at least enough room on the left hand side for the bar label */
  var offsetAmount = data.reduce(function onEachDataPoint(amount, d) {
    var label = 10 + (INDIVIDUAL_CHARACTER_SIZE * d.name.length);

    if (amount < label) {
      return label;
    }

    return amount;
  }, 0);

  var dataMax = d3.max(dataPoints);
  data.forEach(function forEachDataPoint(dataPoint, index) {
    drawBarForDataPoint(svg, dataPoint, index, data.length, offsetAmount, dataMax);
  });

  return svg;
}

/**
 * drawLineChart
 *
 * Draws a line chart using hte data on the given node.
 *
 * @svg {object} - An svg node to draw the bar chart on
 * @data {array} = The data to use to draw the chart
 * @returns {object} - The completed node
 */
function drawLineChart(data) {
  var dataset = data.map(function onEachDataPoint(d) {
    return {
      year: Number(d.name),
      retractions: d.value,
      name: "Retractions"
    };
  });

  return new d3plus.viz()  // eslint-disable-line new-cap
                   .container(".chart")
                   .data(dataset)
                   .type("line")
                   .id("name")
                   .text("name")
                   .y("retractions")
                   .x("year")
                   .draw();
}

/**
 * updateGraph
 *
 * Update the graph with some new data and remove the old one.
 *
 * @newData: New tuples of data to update the graph with
 */
function updateGraph(newData, name) {
  var drawChartDispatch = {
    countryRetraction: drawBarChart,
    authorRetraction: drawBarChart,
    topicRetraction: drawBarChart,
    retractionsOverTime: drawLineChart
  };

  /* Drop any existing SVG elements */
  Array.prototype.forEach.call(document.getElementsByClassName("chart"),
                               function dropInnerHTMLOf(element) {
                                 /* Drop everything inside this chart */
                                 element.innerHTML = "";  // eslint-disable-line no-param-reassign
                               });

  drawChartDispatch[name](newData);
}

/**
 * postGraphUpdateRequest
 *
 * Post a request to the server to get a new graph.
 *
 * @name The chart name to fetch.
 * @filter A filter string
 # @filterType What to filter on
 */
function postGraphUpdateRequest(name, filterString, filterType) {
  if (!name) {
    $("#incorrectInputAlert").html("You need to specify a chart type to display");
    $("#incorrectInputAlert").attr("style", "");
    return;
  }

  if (filterString && (!filterType || filterType === "none")) {
    $("#incorrectInputAlert").html("You need to specify what to search on");
    $("#incorrectInputAlert").attr("style", "");
    return;
  }

  $("#incorrectInputAlert").attr("style", "visibility: hidden;");

  if (name) {
    $.ajax({
      url: "/get_bar_chart",
      data: {
        name: name,
        filterString: filterString || null,
        filterType: filterType || null
      },
      success: function onXHRSuccess(data) {
        updateGraph(data.data, name);
      }
    });
  }
}

/**
 * listenForUserInteraction
 *
 * For a dropdown menu id and a it's contents' id, listen for
 * click events and then post an update request to fetch a graph.
 */
function listenForUserInteraction(dropdownSelector, menuSelector) {
  $(menuSelector + " li a").click(function onDropdownClick(event) {
    var button = $(dropdownSelector);
    var parentDiv = $(".open");

    button.html(this.text + " ");
    button.attr("selection", $(this).attr("data-selection-id"));
    parentDiv.removeClass("open");
    event.preventDefault();

    postGraphUpdateRequest($("#chartSelectionDropdown").attr("selection"),
                           $("#chartFilterInput").val(),
                           $("#filterSelectionDropdown").attr("selection"));
    return false;
  });
}

document.addEventListener("DOMContentLoaded", function onDOMLoaded() {
  listenForUserInteraction("#chartSelectionDropdown", "#chartSelectionMenu");
  listenForUserInteraction("#filterSelectionDropdown",
                           "#filterSelectionMenu");
  $("#chartFilterInput").keypress(function onKeyPress(event) {
    var keyCode = event.keyCode || event.which;
    if (keyCode === 13) {
      /* User pressed enter, respond accordingly */
      postGraphUpdateRequest($("#chartSelectionDropdown").attr("selection"),
                             $("#chartFilterInput").val(),
                             $("#filterSelectionDropdown").attr("selection"));
    }
  });
});
<commit_msg>retraction: Just rely on external d3<commit_after>/* global bundle, $, d3plus, d3 */

var COLOR_TABLE = [
  "#C8D2D9", "#97ABB8", "#FFBEB9", "#FFD1CE"
];

/* This is a poor-man's estimate, given the fact that measuring the
 * actual size of the characters (accounting for kerning) will involve
 * significant DOM mutation. */
var INDIVIDUAL_CHARACTER_SIZE = 10;

/**
 * drawBarForDataPoint
 */
function drawBarForDataPoint(svg, dataPoint, index, length, offsetAmount, dataMax) {
  var computeTextY = function computeTextY() {
    var chartHeight = this.parentNode.parentNode.clientHeight;
    var barSpacing = 10;
    var textHeight = 15;
    return (((chartHeight - (barSpacing * length)) /
            length) / 2) + (textHeight / 2);
  };
  var computeBarWidth = function computeBarWidth() {
    var parentWidth = (this.parentNode.parentNode.clientWidth -
                       offsetAmount);
    var x = d3.scaleLinear().domain([0, dataMax])
                            .range([0, parentWidth]);
    return x(dataPoint.value);
  };
  var g = svg.append("g")
             .attr("transform", function computeBarY() {
               var chartHeight = this.parentNode.clientHeight;
               var barSpacing = 10;
               var barHeight = ((chartHeight - (barSpacing *
                                                length)) /
                                length);

               var y = (index * barSpacing) + (index * barHeight);
               return "translate(0.0, " + y + ")";
             });
  g.append("rect")
   .attr("height", function computeBarHeight() {
     var chartHeight = this.parentNode.parentNode.clientHeight;
     var barSpacing = 10;
     return ((chartHeight - (barSpacing * length)) /
             length);
   })
   .transition()
   .delay(index * 50)
   .duration(750)
   .ease(d3.easeElastic.period(0.4))
   .attr("width", computeBarWidth)
   .attr("rx", "15")
   .attr("ry", "15")
   .attr("x", String(offsetAmount))
   .attr("fill", COLOR_TABLE[index % COLOR_TABLE.length]);
  g.append("text")
   .attr("fill", "#000000")
   .text(dataPoint.name)
   .attr("y", computeTextY)
   .attr("x", 10);
  g.append("text")
   .attr("fill", "#ffffff")
   .text(dataPoint.value)
   .attr("y", computeTextY)
   .transition()
   .delay(index * 50)
   .duration(750)
   .ease(d3.easeElastic.period(0.4))
   .attr("x", function computeNumberX() {
     var barWidth = computeBarWidth.call(this);
     var label = String(dataPoint.value);
     var labelSize = label.length * INDIVIDUAL_CHARACTER_SIZE;
     return (barWidth - labelSize) + offsetAmount;
   });
}

/**
 * drawBarChart
 *
 * Draws a bar chart using data on the given node.
 *
 * @data {array} - The data to use to draw the chart
 * @returns {object} - The completed node
 */
function drawBarChart(data) {
  /* The individual points of data, not including labels */
  var dataPoints = data.map(function onEachDataPoint(w) {
    return w.value;
  });

  var svg = d3.select("div.chart")
              .append("svg")
              .attr("class", "svgchart")
              .attr("width", function computeWidth() {
                return this.parentNode.clientWidth * 0.80;
              })
              .attr("height", function computeHeight() {
                return this.parentNode.clientHeight * 0.80;
              })
              .attr("style", function computeTransform() {
                var x = this.parentNode.clientWidth * 0.10;
                var y = this.parentNode.clientHeight * 0.10;
                return ["transform: translate(", x, "px,", y, "px", ")"].join("");
              });

  /* Estimated sizes of the labels and the bars if no scaling was applied -
   * use this to determine whether an offset should be applied to the chart
   * based on the width of the smallest bar and its corresponding label. If
   * a bar's label size exceeds the size of the bar, we need to make
   * at least enough room on the left hand side for the bar label */
  var offsetAmount = data.reduce(function onEachDataPoint(amount, d) {
    var label = 10 + (INDIVIDUAL_CHARACTER_SIZE * d.name.length);

    if (amount < label) {
      return label;
    }

    return amount;
  }, 0);

  var dataMax = d3.max(dataPoints);
  data.forEach(function forEachDataPoint(dataPoint, index) {
    drawBarForDataPoint(svg, dataPoint, index, data.length, offsetAmount, dataMax);
  });

  return svg;
}

/**
 * drawLineChart
 *
 * Draws a line chart using hte data on the given node.
 *
 * @svg {object} - An svg node to draw the bar chart on
 * @data {array} = The data to use to draw the chart
 * @returns {object} - The completed node
 */
function drawLineChart(data) {
  var dataset = data.map(function onEachDataPoint(d) {
    return {
      year: Number(d.name),
      retractions: d.value,
      name: "Retractions"
    };
  });

  return new d3plus.viz()  // eslint-disable-line new-cap
                   .container(".chart")
                   .data(dataset)
                   .type("line")
                   .id("name")
                   .text("name")
                   .y("retractions")
                   .x("year")
                   .draw();
}

/**
 * updateGraph
 *
 * Update the graph with some new data and remove the old one.
 *
 * @newData: New tuples of data to update the graph with
 */
function updateGraph(newData, name) {
  var drawChartDispatch = {
    countryRetraction: drawBarChart,
    authorRetraction: drawBarChart,
    topicRetraction: drawBarChart,
    retractionsOverTime: drawLineChart
  };

  /* Drop any existing SVG elements */
  Array.prototype.forEach.call(document.getElementsByClassName("chart"),
                               function dropInnerHTMLOf(element) {
                                 /* Drop everything inside this chart */
                                 element.innerHTML = "";  // eslint-disable-line no-param-reassign
                               });

  drawChartDispatch[name](newData);
}

/**
 * postGraphUpdateRequest
 *
 * Post a request to the server to get a new graph.
 *
 * @name The chart name to fetch.
 * @filter A filter string
 # @filterType What to filter on
 */
function postGraphUpdateRequest(name, filterString, filterType) {
  if (!name) {
    $("#incorrectInputAlert").html("You need to specify a chart type to display");
    $("#incorrectInputAlert").attr("style", "");
    return;
  }

  if (filterString && (!filterType || filterType === "none")) {
    $("#incorrectInputAlert").html("You need to specify what to search on");
    $("#incorrectInputAlert").attr("style", "");
    return;
  }

  $("#incorrectInputAlert").attr("style", "visibility: hidden;");

  if (name) {
    $.ajax({
      url: "/get_bar_chart",
      data: {
        name: name,
        filterString: filterString || null,
        filterType: filterType || null
      },
      success: function onXHRSuccess(data) {
        updateGraph(data.data, name);
      }
    });
  }
}

/**
 * listenForUserInteraction
 *
 * For a dropdown menu id and a it's contents' id, listen for
 * click events and then post an update request to fetch a graph.
 */
function listenForUserInteraction(dropdownSelector, menuSelector) {
  $(menuSelector + " li a").click(function onDropdownClick(event) {
    var button = $(dropdownSelector);
    var parentDiv = $(".open");

    button.html(this.text + " ");
    button.attr("selection", $(this).attr("data-selection-id"));
    parentDiv.removeClass("open");
    event.preventDefault();

    postGraphUpdateRequest($("#chartSelectionDropdown").attr("selection"),
                           $("#chartFilterInput").val(),
                           $("#filterSelectionDropdown").attr("selection"));
    return false;
  });
}

document.addEventListener("DOMContentLoaded", function onDOMLoaded() {
  listenForUserInteraction("#chartSelectionDropdown", "#chartSelectionMenu");
  listenForUserInteraction("#filterSelectionDropdown",
                           "#filterSelectionMenu");
  $("#chartFilterInput").keypress(function onKeyPress(event) {
    var keyCode = event.keyCode || event.which;
    if (keyCode === 13) {
      /* User pressed enter, respond accordingly */
      postGraphUpdateRequest($("#chartSelectionDropdown").attr("selection"),
                             $("#chartFilterInput").val(),
                             $("#filterSelectionDropdown").attr("selection"));
    }
  });
});
<|endoftext|>


<commit_before>/**
 * Unburyme.Loan
 * The loan object that stores individual loan information
 * @constructor
 * @param {number} uid Unique ID for the loan
 * @param {Unburyme.LoanApp} loanApp
 */
Unburyme.Loan = function (uid, loanApp) {

    this.uid = uid;
    this.loanApp = loanApp;
    this.name = 'Loan ' + (this.uid + 1);
    this.balance = 0;
    this.minPayment = 0;
    this.interest = 0;
    this.results = new Unburyme.ResultBar(this.uid, this, this.loanApp);
    this.create();
    this.isInitialized = {
        'loan': 0,
        'name': 0,
        'balance': 0,
        'minPayment': 0,
        'interest': 0
    }
    this.isValid = {
        'loan': 0,
        'name': 0,
        'balance': 0,
        'minPayment': 0,
        'interest': 0
    }
    this.deleted = 0;

};

/**
 * Set up loan in DOM
 */
Unburyme.Loan.prototype.create = function () {

    var html = '
\n';

    html += '
\n';
    html += '

Loan Name

\n'
    html += '
<input id=\'loanname' + this.uid + '\' class=\'name\' />
\n';
    html += '
\n';
    html += '
\n';
    html += '

Current Balance

\n';
    html += '
$<input  id=\'loanbalance' + this.uid + '\' class=\'balance  uninitialized\' />
\n';
    html += '
\n';
    html += '
\n';
    html += '

Min. Payment

\n';
    html += '
$<input id=\'loanminPayment' + this.uid + '\' class=\'minPayment  uninitialized\' />
\n';
    html += '
\n';
    html += '
\n';
    html += '

Interest

\n';
    html += '
<input  id=\'loaninterest' + this.uid + '\' class=\'interest  uninitialized\' />%
\n';
    html += '
\n';
    html += '
'
    html += '<button class=\'destroyLoan btn btn-danger btn-xs\' id=\'delete'+ this.uid + '\'>✖</button>'
    html += '
';
    html += '

';
    html += '
\n';

    $('#loanBarInputContainer').append(html);
    $('#loanbar' + this.uid).css('display', 'none').slideDown(this.loanApp.config.slideSpeed);

};

/**
 * Destroys loan object and DOM elements
 */
Unburyme.Loan.prototype.destroy = function () {

    $('#loanbar' + this.uid).slideUp(this.loanApp.config.slideSpeed, function () {
        $('#loanbar' + this.uid).remove();
    });
    this.results.destroy();
    //Remove all uninitialization tags
    this.cleanField('bar');
    this.cleanField('name');
    this.cleanField('balance');
    this.cleanField('minPayment');
    this.cleanField('interest');

    // Should be removed from array; sanity check
    this.deleted = 1;

    for (var i = 0; i < 5; i++)
        this.isInitialized[i] = 0;

};

Unburyme.Loan.prototype.setName = function (name) {

    this.name = name;

};

Unburyme.Loan.prototype.setBalance = function (balance) {

    this.initializeField('balance');
    this.balance = balance;

};

Unburyme.Loan.prototype.setMinPayment = function (minPayment) {

    this.initializeField('minPayment');
    this.minPayment = minPayment;

};

Unburyme.Loan.prototype.setInterest = function (interest) {

    this.initializeField('interest');
    this.interest = interest;

};

Unburyme.Loan.prototype.getName = function () {

    return this.name;

};

Unburyme.Loan.prototype.getBalance = function () {

    return this.balance;

};

Unburyme.Loan.prototype.getMinPayment = function () {

    return this.minPayment;

};

Unburyme.Loan.prototype.getInterest = function () {

    return this.interest;

};

Unburyme.Loan.prototype.getValue = function (field) {

    var value = '';
    switch (field) {
        case 'name':
            value = this.getName();
            break;
        case 'balance':
            value = this.getBalance();
            break;
        case 'minPayment':
            value = this.getMinPayment();
            break;
        case 'interest':
            value = this.getInterest();
            break;
    }
    return value;

};

Unburyme.Loan.prototype.getUID = function () {

    return this.uid;

};

Unburyme.Loan.prototype.calculate = function () {

    this.results.calculate();

};

Unburyme.Loan.prototype.validate = function (field, value) {

    var validChars = "0123456789., ",
        validTest = true,
        character;
    for (i = 0; i < value.length && validTest == true; i++) {
        character = value.charAt(i);
        if (validChars.indexOf(character) == -1)
            validTest = false;
    }
    if (value == '') {
        validTest = false;
        $('input .' + field).val('');
    }
    if (field == 'name')
        validTest = true;
    if (validTest)
        this.isValid[field] = 1;
    else
        this.isValid[field] = 0;
    if (this.isValid['balance'] && this.isValid['minPayment'] && this.isValid['interest'])
        this.isValid['loan'] = 1;

    // Call validateDisplay to handle visual representation
    this.validateDisplay(field, validTest);
    return validTest;

};

Unburyme.Loan.prototype.validateDisplay = function (field, isFieldValid) {

    if (isFieldValid)
        $('#loan' + field + this.uid).removeClass('invalidField');
    else
        $('#loan' + field + this.uid).addClass('invalidField');
};

Unburyme.Loan.prototype.cleanField = function (field) {

    if (field == 'loan')
        field = 'bar';
    $('#loan' + field + this.uid).removeClass('uninitialized');
    $('#loan' + field + this.uid).removeClass('invalidField');
};

Unburyme.Loan.prototype.initializeField = function (field) {

    this.isInitialized[field] = 1;
    this.cleanField(field);
    if (this.isInitialized['balance'] && this.isInitialized['minPayment'] && this.isInitialized['interest']) {
        this.isInitialized['loan'] = 1;
        this.cleanField('loan');
    }
};

<commit_msg>change input to number<commit_after>/**
 * Unburyme.Loan
 * The loan object that stores individual loan information
 * @constructor
 * @param {number} uid Unique ID for the loan
 * @param {Unburyme.LoanApp} loanApp
 */
Unburyme.Loan = function (uid, loanApp) {

    this.uid = uid;
    this.loanApp = loanApp;
    this.name = 'Loan ' + (this.uid + 1);
    this.balance = 0;
    this.minPayment = 0;
    this.interest = 0;
    this.results = new Unburyme.ResultBar(this.uid, this, this.loanApp);
    this.create();
    this.isInitialized = {
        'loan': 0,
        'name': 0,
        'balance': 0,
        'minPayment': 0,
        'interest': 0
    }
    this.isValid = {
        'loan': 0,
        'name': 0,
        'balance': 0,
        'minPayment': 0,
        'interest': 0
    }
    this.deleted = 0;

};

/**
 * Set up loan in DOM
 */
Unburyme.Loan.prototype.create = function () {

    var html = '
\n';

    html += '
\n';
    html += '

Loan Name

\n'
    html += '
<input id=\'loanname' + this.uid + '\' class=\'name\' />
\n';
    html += '
\n';
    html += '
\n';
    html += '

Current Balance

\n';
    html += '
$<input type=\'number\' id=\'loanbalance' + this.uid + '\' class=\'balance  uninitialized\' />
\n';
    html += '
\n';
    html += '
\n';
    html += '

Min. Payment

\n';
    html += '
$<input type=\'number\' id=\'loanminPayment' + this.uid + '\' class=\'minPayment  uninitialized\' />
\n';
    html += '
\n';
    html += '
\n';
    html += '

Interest

\n';
    html += '
<input type=\'number\'  id=\'loaninterest' + this.uid + '\' class=\'interest  uninitialized\' />%
\n';
    html += '
\n';
    html += '
'
    html += '<button class=\'destroyLoan btn btn-danger btn-xs\' id=\'delete'+ this.uid + '\'>✖</button>'
    html += '
';
    html += '

';
    html += '
\n';

    $('#loanBarInputContainer').append(html);
    $('#loanbar' + this.uid).css('display', 'none').slideDown(this.loanApp.config.slideSpeed);

};

/**
 * Destroys loan object and DOM elements
 */
Unburyme.Loan.prototype.destroy = function () {

    $('#loanbar' + this.uid).slideUp(this.loanApp.config.slideSpeed, function () {
        $('#loanbar' + this.uid).remove();
    });
    this.results.destroy();
    //Remove all uninitialization tags
    this.cleanField('bar');
    this.cleanField('name');
    this.cleanField('balance');
    this.cleanField('minPayment');
    this.cleanField('interest');

    // Should be removed from array; sanity check
    this.deleted = 1;

    for (var i = 0; i < 5; i++)
        this.isInitialized[i] = 0;

};

Unburyme.Loan.prototype.setName = function (name) {

    this.name = name;

};

Unburyme.Loan.prototype.setBalance = function (balance) {

    this.initializeField('balance');
    this.balance = balance;

};

Unburyme.Loan.prototype.setMinPayment = function (minPayment) {

    this.initializeField('minPayment');
    this.minPayment = minPayment;

};

Unburyme.Loan.prototype.setInterest = function (interest) {

    this.initializeField('interest');
    this.interest = interest;

};

Unburyme.Loan.prototype.getName = function () {

    return this.name;

};

Unburyme.Loan.prototype.getBalance = function () {

    return this.balance;

};

Unburyme.Loan.prototype.getMinPayment = function () {

    return this.minPayment;

};

Unburyme.Loan.prototype.getInterest = function () {

    return this.interest;

};

Unburyme.Loan.prototype.getValue = function (field) {

    var value = '';
    switch (field) {
        case 'name':
            value = this.getName();
            break;
        case 'balance':
            value = this.getBalance();
            break;
        case 'minPayment':
            value = this.getMinPayment();
            break;
        case 'interest':
            value = this.getInterest();
            break;
    }
    return value;

};

Unburyme.Loan.prototype.getUID = function () {

    return this.uid;

};

Unburyme.Loan.prototype.calculate = function () {

    this.results.calculate();

};

Unburyme.Loan.prototype.validate = function (field, value) {

    var validChars = "0123456789., ",
        validTest = true,
        character;
    for (i = 0; i < value.length && validTest == true; i++) {
        character = value.charAt(i);
        if (validChars.indexOf(character) == -1)
            validTest = false;
    }
    if (value == '') {
        validTest = false;
        $('input .' + field).val('');
    }
    if (field == 'name')
        validTest = true;
    if (validTest)
        this.isValid[field] = 1;
    else
        this.isValid[field] = 0;
    if (this.isValid['balance'] && this.isValid['minPayment'] && this.isValid['interest'])
        this.isValid['loan'] = 1;

    // Call validateDisplay to handle visual representation
    this.validateDisplay(field, validTest);
    return validTest;

};

Unburyme.Loan.prototype.validateDisplay = function (field, isFieldValid) {

    if (isFieldValid)
        $('#loan' + field + this.uid).removeClass('invalidField');
    else
        $('#loan' + field + this.uid).addClass('invalidField');
};

Unburyme.Loan.prototype.cleanField = function (field) {

    if (field == 'loan')
        field = 'bar';
    $('#loan' + field + this.uid).removeClass('uninitialized');
    $('#loan' + field + this.uid).removeClass('invalidField');
};

Unburyme.Loan.prototype.initializeField = function (field) {

    this.isInitialized[field] = 1;
    this.cleanField(field);
    if (this.isInitialized['balance'] && this.isInitialized['minPayment'] && this.isInitialized['interest']) {
        this.isInitialized['loan'] = 1;
        this.cleanField('loan');
    }
};

<|endoftext|>


<commit_before>'use strict';

var EventEmitter = require('events').EventEmitter;
var objectAssign = require('object-assign');

function User(username, opts) {
  /* global AudioContext */
  EventEmitter.call(this);
  opts = opts || {};
  this._username = username;
  this._self = opts.self;
  this._mediaStream = opts.mediaStream;
  this._audio = opts.audio;
  this._call = opts.call;
  this._volume = 0;
  // this._processVolume();
  this._processFreqs();
}

User.prototype = objectAssign({}, EventEmitter.prototype);

User.prototype._processVolume = function () {
  var audioContext = new AudioContext();
  var analyser = audioContext.createAnalyser();
  var microphone = audioContext.createMediaStreamSource(this._mediaStream);
  this._processor = audioContext.createScriptProcessor(1024, 1, 1);

  analyser.smoothingTimeConstant = 0.5;
  analyser.fftSize = 512;

  microphone.connect(analyser);
  analyser.connect(this._processor);
  this._processor.connect(audioContext.destination);

  // RMS
  var array = new Uint8Array(analyser.frequencyBinCount);
  this._processor.onaudioprocess = function () {
    analyser.getByteFrequencyData(array);
    var values = 0;

    var length = array.length;
    if (length === 0) return;
    for (var i = 0; i < length; i++) {
      values += array[i] * array[i];
    }
    if (values / length <= 1) this._volume = 0;
    else this._volume = 20*Math.log(Math.sqrt(values / length));
    // this._volume = values / length;
    this.emit('volume', this._volume);
  }.bind(this);
};

User.prototype._processFreqs = function () {
  var audioContext = new AudioContext();
  var analyser = audioContext.createAnalyser();
  var microphone = audioContext.createMediaStreamSource(this._mediaStream);
  this._processor = audioContext.createScriptProcessor(1024, 1, 1);

  analyser.fftSize = 512;

  microphone.connect(analyser);
  analyser.connect(this._processor);
  this._processor.connect(audioContext.destination);

  var bufferLength = analyser.fftSize;
  var dataArray = new Uint8Array(bufferLength);
  var points;
  this._processor.onaudioprocess = function () {
    points = [];
    analyser.getByteTimeDomainData(dataArray);

    for (var x = 0; x < bufferLength; x++) {
      points[x] = dataArray[x] / 256.0;
    }

    this.emit('freqs', points);
  }.bind(this);
};

User.prototype.getUsername = function () {
  return this._username;
};

User.prototype.getMediaStream = function () {
  return this._mediaStream;
};

User.prototype.getVolume = function () {
  return this._volume;
};

User.prototype.isSelf = function () {
  return !!this._self;
};

User.prototype.toggleMute = function () {
  this._mediaStream.getAudioTracks()[0].enabled = !this._mediaStream.getAudioTracks()[0].enabled;

  if (this.isMuted()) {
    this._volume = 0;
    this._processor.onaudioprocess = null;
  } else {
    this._processVolume();
  }
};

User.prototype.isMuted = function () {
  return !this._mediaStream.getAudioTracks()[0].enabled;
};

module.exports = User;
<commit_msg>Fixed: resume processing when unmuting<commit_after>'use strict';

var EventEmitter = require('events').EventEmitter;
var objectAssign = require('object-assign');

function User(username, opts) {
  /* global AudioContext */
  EventEmitter.call(this);
  opts = opts || {};
  this._username = username;
  this._self = opts.self;
  this._mediaStream = opts.mediaStream;
  this._audio = opts.audio;
  this._call = opts.call;
  this._volume = 0;
  // this._processVolume();
  this._processFreqs();
}

User.prototype = objectAssign({}, EventEmitter.prototype);

User.prototype._processVolume = function () {
  var audioContext = new AudioContext();
  var analyser = audioContext.createAnalyser();
  var microphone = audioContext.createMediaStreamSource(this._mediaStream);
  this._processor = audioContext.createScriptProcessor(1024, 1, 1);

  analyser.smoothingTimeConstant = 0.5;
  analyser.fftSize = 512;

  microphone.connect(analyser);
  analyser.connect(this._processor);
  this._processor.connect(audioContext.destination);

  // RMS
  var array = new Uint8Array(analyser.frequencyBinCount);
  this._processor.onaudioprocess = function () {
    analyser.getByteFrequencyData(array);
    var values = 0;

    var length = array.length;
    if (length === 0) return;
    for (var i = 0; i < length; i++) {
      values += array[i] * array[i];
    }
    if (values / length <= 1) this._volume = 0;
    else this._volume = 20*Math.log(Math.sqrt(values / length));
    // this._volume = values / length;
    this.emit('volume', this._volume);
  }.bind(this);
};

User.prototype._processFreqs = function () {
  var audioContext = new AudioContext();
  var analyser = audioContext.createAnalyser();
  var microphone = audioContext.createMediaStreamSource(this._mediaStream);
  this._processor = audioContext.createScriptProcessor(1024, 1, 1);

  analyser.fftSize = 512;

  microphone.connect(analyser);
  analyser.connect(this._processor);
  this._processor.connect(audioContext.destination);

  var bufferLength = analyser.fftSize;
  var dataArray = new Uint8Array(bufferLength);
  var points;
  this._processor.onaudioprocess = function () {
    points = [];
    analyser.getByteTimeDomainData(dataArray);

    for (var x = 0; x < bufferLength; x++) {
      points[x] = dataArray[x] / 256.0;
    }

    this.emit('freqs', points);
  }.bind(this);
};

User.prototype.getUsername = function () {
  return this._username;
};

User.prototype.getMediaStream = function () {
  return this._mediaStream;
};

User.prototype.getVolume = function () {
  return this._volume;
};

User.prototype.isSelf = function () {
  return !!this._self;
};

User.prototype.toggleMute = function () {
  this._mediaStream.getAudioTracks()[0].enabled = !this._mediaStream.getAudioTracks()[0].enabled;

  if (this.isMuted()) {
    this._volume = 0;
    this._processor.onaudioprocess = null;
  } else {
    // this._processVolume();
    this._processFreqs();
  }
};

User.prototype.isMuted = function () {
  return !this._mediaStream.getAudioTracks()[0].enabled;
};

module.exports = User;
<|endoftext|>


<commit_before>define(['root', 'mixedice', 'jquery', './resource', './log', 'eventmap'], function(root, mixedice, $, Resource, Log, EventMap) {'use strict';
  /**
   * @module lyria/preloader
   */

  /**
   * Provides a preloader to load assets before they are needed
   *
   * @class Preloader
   */
  var Preloader = (function() {

    /**
     * @constructor
     *
     * @param {Object} assetObject
     */
    var Preloader = function(assetObject) {
      mixedice([this, Preloader.prototype], new EventMap());

      /**
       * @property assets
       * @type {Array}
       */
      if (assetObject != null) {
        this.assets = assetObject;
      } else {
        this.assets = {};
      }

      /**
       * @property maxAssets
       * @type {Number}
       * @default 0
       */
      this.maxAssets = 0;

      /**
       * @property assetsLoaded
       * @type {Number}
       * @default 0
       */
      this.assetsLoaded = 0;

      /**
       * @property percentLoaded
       * @type {Number}
       * @default 0
       */
      this.percentLoaded = 0;

      /**
       * @property steps
       * @type {Array}
       */
      this.steps = [];

      /**
       * @property taskList
       * @type {Array}
       */
      this.taskList = [];
    };

    /**
     * Adds a custom task to the preloader
     *
     * @param {Function} taskFn
     */
    Preloader.prototype.task = function(taskFn) {
      if ( typeof taskFn === 'function') {
        this.taskList.push({
          task: taskFn,
          async: false
        });
      }
    };

    /**
     * Adds a custom asynchronous task to the preloader
     *
     * @param {Function} taskFn
     */
    Preloader.prototype.taskAsync = function(taskFn) {
      if ( typeof taskFn === 'function') {
        this.taskList.push({
          task: taskFn,
          async: true
        });
      }
    };

    /**
     * Starts the preloader and loads all assets asynchronously. Triggers
     * events when necessary.
     *
     * @method start
     */
    Preloader.prototype.start = function() {
      // Check if it's valid
      if (this.assets == null) {
        throw new Error('Assets should not be null. Pass at least an empty object.');
      }

      this.trigger('start');

      var totalSize = 0;

      // Trigger complete event if there is nothing to load
      if (Object.keys(this.assets).length > 0) {
        totalSize = this.assets.totalSize;
      }

      var currentProgress = 0;

      if ((this.steps == null) || (this.steps.length === 0)) {

      }

      var hasLoadingScene = this.sceneDirector != null && this.loadingScene != null;

      if (hasLoadingScene) {
        this.sceneDirector.show(this.loadingScene);
      }

      var self = this;

      var loadCustomTasks = function(done) {
        var maxTasks = self.taskList.length;
        var currentTasks = 0;

        var checkIfComplete = function() {
          if (currentTasks === maxTasks) {
            done();
          }
        };

        var doneFn = function() {
          currentTasks++;
          checkIfComplete();
        };

        if (self.taskList.length === 0) {
          done();
        } else {
          for (var i = 0, j = self.taskList.length; i < j; i++) {
            (function(item) {
              if (item.async) {
                item.task.call(this, doneFn);
              } else {
                item.task();
                doneFn();
              }
            })(self.taskList[i]);
          }
        }

      };

      var loadingProgress = function() {

        var percentLoaded = 100;

        if (currentProgress !== totalSize) {
          percentLoaded = currentProgress / totalSize;
        }

        self.trigger('progress', percentLoaded);

        if (hasLoadingScene) {
          self.sceneDirector.currentScene.trigger('progress', percentLoaded, currentProgress, totalSize);
        }

        if (currentProgress >= totalSize) {
          loadCustomTasks(function() {
            if (hasLoadingScene) {
              self.sceneDirector.currentScene.trigger('complete');
            }

            self.trigger('complete');
          });
        }
      };

      if (Object.keys(this.assets).length > 0) {
        // Go through all assets and preload them
        $.each(this.assets, function(key, value) {

          if (value.files == null || !Array.isArray(value.files) || value.files.length === 0) {
            return true;
          }

          for (var i = 0, j = value.files.length; i < j; i++) {
            (function(iterator) {

              if (iterator.type.indexOf('image') === 0) {
                var img = new root.Image();
                img.onload = function() {
                  currentProgress += iterator.size;

                  loadingProgress();
                };

                img.onerror = function(err) {
                  Log.e('Error while loading ' + iterator.name);
                };

                img.src = iterator.name;
              } else {
                $.ajax({
                  url: iterator.name,
                  dataType: 'text'
                }).always(function() {
                  currentProgress += iterator.size;

                  loadingProgress();
                }).error(function(err) {
                  Log.e('Error while loading ' + iterator.name + ': ' + err);
                });
              }

            })(value.files[i]);
          }
        });
      } else {
        loadingProgress();
      }

    };

    return Preloader;

  })();

  return Preloader;
});
<commit_msg>Audio files are not ajax'ed any more when preloading<commit_after>define(['root', 'mixedice', 'jquery', './resource', './log', 'eventmap'], function(root, mixedice, $, Resource, Log, EventMap) {'use strict';
  /**
   * @module lyria/preloader
   */

  /**
   * Provides a preloader to load assets before they are needed
   *
   * @class Preloader
   */
  var Preloader = (function() {

    /**
     * @constructor
     *
     * @param {Object} assetObject
     */
    var Preloader = function(assetObject) {
      mixedice([this, Preloader.prototype], new EventMap());

      /**
       * @property assets
       * @type {Array}
       */
      if (assetObject != null) {
        this.assets = assetObject;
      } else {
        this.assets = {};
      }

      /**
       * @property maxAssets
       * @type {Number}
       * @default 0
       */
      this.maxAssets = 0;

      /**
       * @property assetsLoaded
       * @type {Number}
       * @default 0
       */
      this.assetsLoaded = 0;

      /**
       * @property percentLoaded
       * @type {Number}
       * @default 0
       */
      this.percentLoaded = 0;

      /**
       * @property steps
       * @type {Array}
       */
      this.steps = [];

      /**
       * @property taskList
       * @type {Array}
       */
      this.taskList = [];
    };

    /**
     * Adds a custom task to the preloader
     *
     * @param {Function} taskFn
     */
    Preloader.prototype.task = function(taskFn) {
      if ( typeof taskFn === 'function') {
        this.taskList.push({
          task: taskFn,
          async: false
        });
      }
    };

    /**
     * Adds a custom asynchronous task to the preloader
     *
     * @param {Function} taskFn
     */
    Preloader.prototype.taskAsync = function(taskFn) {
      if ( typeof taskFn === 'function') {
        this.taskList.push({
          task: taskFn,
          async: true
        });
      }
    };

    /**
     * Starts the preloader and loads all assets asynchronously. Triggers
     * events when necessary.
     *
     * @method start
     */
    Preloader.prototype.start = function() {
      // Check if it's valid
      if (this.assets == null) {
        throw new Error('Assets should not be null. Pass at least an empty object.');
      }

      this.trigger('start');

      var totalSize = 0;

      // Trigger complete event if there is nothing to load
      if (Object.keys(this.assets).length > 0) {
        totalSize = this.assets.totalSize;
      }

      var currentProgress = 0;

      if ((this.steps == null) || (this.steps.length === 0)) {

      }

      var hasLoadingScene = this.sceneDirector != null && this.loadingScene != null;

      if (hasLoadingScene) {
        this.sceneDirector.show(this.loadingScene);
      }

      var self = this;

      var loadCustomTasks = function(done) {
        var maxTasks = self.taskList.length;
        var currentTasks = 0;

        var checkIfComplete = function() {
          if (currentTasks === maxTasks) {
            done();
          }
        };

        var doneFn = function() {
          currentTasks++;
          checkIfComplete();
        };

        if (self.taskList.length === 0) {
          done();
        } else {
          for (var i = 0, j = self.taskList.length; i < j; i++) {
            (function(item) {
              if (item.async) {
                item.task.call(this, doneFn);
              } else {
                item.task();
                doneFn();
              }
            })(self.taskList[i]);
          }
        }

      };

      var loadingProgress = function() {

        var percentLoaded = 100;

        if (currentProgress !== totalSize) {
          percentLoaded = currentProgress / totalSize;
        }

        self.trigger('progress', percentLoaded);

        if (hasLoadingScene) {
          self.sceneDirector.currentScene.trigger('progress', percentLoaded, currentProgress, totalSize);
        }

        if (currentProgress >= totalSize) {
          loadCustomTasks(function() {
            if (hasLoadingScene) {
              self.sceneDirector.currentScene.trigger('complete');
            }

            self.trigger('complete');
          });
        }
      };

      if (Object.keys(this.assets).length > 0) {
        // Go through all assets and preload them
        $.each(this.assets, function(key, value) {

          if (value.files == null || !Array.isArray(value.files) || value.files.length === 0) {
            return true;
          }

          for (var i = 0, j = value.files.length; i < j; i++) {
            (function(iterator) {
              // TODO: Define separate functions for loading process and error handling

              // Handle images here
              if (iterator.type.indexOf('image') === 0) {
                // TODO: Reflect: Does it make sense to put the cached images into an object?
                var img = new root.Image();
                img.onload = function() {
                  currentProgress += iterator.size;

                  loadingProgress();
                };

                img.onerror = function(err) {
                  Log.e('Error while loading ' + iterator.name);
                };

                img.src = iterator.name;
              } else {
                // Handle audio here
                if (iterator.type.indexOf('audio') === 0) {
                  // TODO: Save preloaded files in the AudioManager
                  var audio = new root.Audio();
                  audio.oncanplaythrough = function() {
                    currentProgress += iterator.size;

                    loadingProgress();
                  };

                  audio.onerror = function(err) {
                    Log.e('Error while loading '+ iterator.name);
                  };
                } else {
                  $.ajax({
                    url: iterator.name,
                    dataType: 'text'
                  }).always(function() {
                    currentProgress += iterator.size;

                    loadingProgress();
                  }).error(function(err) {
                    Log.e('Error while loading ' + iterator.name + ': ' + err);
                  });
                }
              }

            })(value.files[i]);
          }
        });
      } else {
        loadingProgress();
      }

    };

    return Preloader;

  })();

  return Preloader;
});
<|endoftext|>


<commit_before><commit_msg>Removed redundant error handler.<commit_after><|endoftext|>


<commit_before>/**
 * @namespace Lyria
 * Lyria namespace decleration
 */
define('lyria/preloader', ['root', 'mixin', 'jquery', 'lyria/resource', 'lyria/log', 'lyria/eventmap'], function(root, mixin, $, Resource, Log, EventMap) {'use strict';

  /**
   *
   */
  var Preloader = (function() {

    var Preloader = function(assetArray) {
      mixin(Preloader.prototype, new EventMap());

      if (assetArray != null) {
        this.assets = assetArray;
      } else {
        this.assets = [];
      }

      this.maxAssets = 0;
      this.assetsLoaded = 0;
      this.percentLoaded = 0;
      this.steps = [];
    };

    Preloader.prototype.start = function() {
      // Check if it's valid
      if (this.assets == null || Object.keys(this.assets).length === 0) {
        return;
      }

      this.trigger('start');

      var totalSize = this.assets.totalSize;
      var currentProgress = 0;

      if ((this.steps == null) || (this.steps.length === 0)) {

      }

      var hasLoadingScene = this.sceneDirector != null && this.loadingScene != null;

      if (hasLoadingScene) {
        this.sceneDirector.show(this.loadingScene);
      }

      var self = this;

      function loadingProgress() {

        var percentLoaded = currentProgress / totalSize;

        self.trigger('progresschange', percentLoaded);

        if (hasLoadingScene) {
          self.sceneDirector.currentScene.trigger('progresschange', percentLoaded);
        }

        if (currentProgress >= totalSize) {
          if (hasLoadingScene) {
            self.sceneDirector.currentScene.trigger('complete');
          }

          self.trigger('complete');
        }
      }

      $.each(this.assets, function(key, value) {

        if (value.files == null || !Array.isArray(value.files) || value.files.length === 0) {
          return false;
        }

        for (var i = 0, j = value.files.length; i < j; i++) {
          (function(iterator) {

            if (iterator.name.indexOf('/' + Resource.path.image + '/') >= 0) {
              var img = new root.Image();
              img.onload = function() {
                currentProgress += iterator.size;

                loadingProgress();
              };

              img.onerror = function(err) {
                Log.e('Error while loading ' + iterator.name);
              };

              img.src = iterator.name;
            } else {
              $.ajax({
                url: iterator.name,
                dataType: 'text'
              }).always(function() {
                currentProgress += iterator.size;

                loadingProgress();
              }).error(function(err) {
                Log.e('Error while loading ' + iterator.name + ': ' + err);
              });
            }

          })(value.files[i]);
        }

      });
    };

    return Preloader;

  })();

  return Preloader;
});<commit_msg>Updated inline documentation in lyria/preloader<commit_after>/**
 * @module Lyria
 */
define('lyria/preloader', ['root', 'mixin', 'jquery', 'lyria/resource', 'lyria/log', 'lyria/eventmap'], function(root, mixin, $, Resource, Log, EventMap) {'use strict';

  /**
   * Provides a preloader to load assets before they are needed
   *
   * @class Preloader
   */
  var Preloader = (function() {

    /**
     * @constructor
     *
     * @param {Object} assetArray
     */
    var Preloader = function(assetArray) {
      mixin(Preloader.prototype, new EventMap());

      /**
       * @property assets
       * @type {Array}
       */
      if (assetArray != null) {
        this.assets = assetArray;
      } else {
        this.assets = [];
      }

      /**
       * @property maxAssets
       * @type {Number}
       * @default 0
       */
      this.maxAssets = 0;

      /**
       * @property assetsLoaded
       * @type {Number}
       * @default 0
       */
      this.assetsLoaded = 0;

      /**
       * @property percentLoaded
       * @type {Number}
       * @default 0
       */
      this.percentLoaded = 0;

      /**
       * @property steps
       * @type {Array}
       */
      this.steps = [];
    };

    /**
     * Starts the preloader and loads all assets asynchronously. Triggers
     * events when necessary.
     *
     * @method start
     */
    Preloader.prototype.start = function() {
      // Check if it's valid
      if (this.assets == null || Object.keys(this.assets).length === 0) {
        return;
      }

      this.trigger('start');

      var totalSize = this.assets.totalSize;
      var currentProgress = 0;

      if ((this.steps == null) || (this.steps.length === 0)) {

      }

      var hasLoadingScene = this.sceneDirector != null && this.loadingScene != null;

      if (hasLoadingScene) {
        this.sceneDirector.show(this.loadingScene);
      }

      var self = this;

      function loadingProgress() {

        var percentLoaded = currentProgress / totalSize;

        self.trigger('progresschange', percentLoaded);

        if (hasLoadingScene) {
          self.sceneDirector.currentScene.trigger('progresschange', percentLoaded);
        }

        if (currentProgress >= totalSize) {
          if (hasLoadingScene) {
            self.sceneDirector.currentScene.trigger('complete');
          }

          self.trigger('complete');
        }
      }

      $.each(this.assets, function(key, value) {

        if (value.files == null || !Array.isArray(value.files) || value.files.length === 0) {
          return false;
        }

        for (var i = 0, j = value.files.length; i < j; i++) {
          (function(iterator) {

            if (iterator.name.indexOf('/' + Resource.path.image + '/') >= 0) {
              var img = new root.Image();
              img.onload = function() {
                currentProgress += iterator.size;

                loadingProgress();
              };

              img.onerror = function(err) {
                Log.e('Error while loading ' + iterator.name);
              };

              img.src = iterator.name;
            } else {
              $.ajax({
                url: iterator.name,
                dataType: 'text'
              }).always(function() {
                currentProgress += iterator.size;

                loadingProgress();
              }).error(function(err) {
                Log.e('Error while loading ' + iterator.name + ': ' + err);
              });
            }

          })(value.files[i]);
        }

      });
    };

    return Preloader;

  })();

  return Preloader;
});<|endoftext|>


<commit_before>'use strict';

import _ from 'underscore';
import menus from './js/menus';
import settings from './js/settings';
import tabmanager from './js/tabmanager';
import updater from './js/updater';

// Declare this global namespace so it can be used from popup.js
// @see startup();
const TW = window.TW = {};

/**
 * @todo: refactor into "get the ones to close" and "close 'em"
 * So it can be tested.
 */
var checkToClose = function(cutOff) {
  var i;
  cutOff = cutOff || new Date().getTime() - settings.get('stayOpen');
  var minTabs = settings.get('minTabs');
  // Tabs which have been locked via the checkbox.
  var lockedIds = settings.get("lockedIds");
  var toCut = tabmanager.getOlderThen(cutOff);
  var tabsToSave = [];

  if (settings.get('paused') === true) {
    return;
  }

  // Update the selected one to make sure it doesn't get closed.
  chrome.tabs.getSelected(null, tabmanager.updateLastAccessed);

  chrome.windows.getAll({populate:true}, function(windows) {
    var tabs = []; // Array of tabs, populated for each window.
    _.each(windows, function(myWindow) {
      tabs = myWindow.tabs;
      // Filter out the pinned tabs
      tabs = _.filter(tabs, function(tab) {return tab.pinned === false;});
      var tabsToCut = _.filter(tabs, function(t) {return toCut.indexOf(t.id) != -1;});
      if ((tabs.length - minTabs) <= 0) {
        // We have less than minTab tabs, abort.
        // Also, let's reset the last accessed time of our current tabs so they
        // don't get closed when we add a new one.
        for (i = 0; i < tabs.length; i++) {
          tabmanager.updateLastAccessed(tabs[i].id);
        }
        return;
      }

      // If cutting will reduce us below 5 tabs, only remove the first N to get to 5.
      tabsToCut = tabsToCut.splice(0, tabs.length - minTabs);

      if (tabsToCut.length === 0) {
        return;
      }

      for (i=0; i < tabsToCut.length; i++) {
        if (lockedIds.indexOf(tabsToCut[i].id) != -1) {
          // Update its time so it gets checked less frequently.
          // Would also be smart to just never add it.
          // @todo: fix that.
          tabmanager.updateLastAccessed(tabsToCut[i].id);
          continue;
        }
        closeTab(tabsToCut[i]);
      }
    });
  });
};

var closeTab = function(tab) {
  if (true === tab.pinned) {
      return;
    }
    if (tabmanager.isWhitelisted(tab.url)) {
      return;
    }

    tabmanager.closedTabs.saveTabs([tab]);
    // Close it in Chrome.
    chrome.tabs.remove(tab.id);
};

var onNewTab = function(tab) {
  // Check if it exists in corral already
  // The 2nd argument is an array of filters, we add one filter
  // which checks for an exact URL match.  If we match throw the old
  // entry away.
  tabmanager.searchTabs(function(tabs) {
    if (tabs.length) {
      _.each(tabs, function(t) {
        tabmanager.closedTabs.removeTab(t.id);
      });
    }
  }, [tabmanager.filters.exactUrl(tab.url)]);

  // Add the new one;
  tabmanager.updateLastAccessed(tab.id);
};

var startup = function() {
  settings.init();
  updater.run();
  tabmanager.closedTabs.init();

  TW.settings = settings;
  TW.updater = updater;
  TW.tabmanager = tabmanager;

  if (settings.get('purgeClosedTabs') !== false) {
    tabmanager.closedTabs.clear();
  }
  settings.set('lockedIds', []);

  // Move this to a function somehwere so we can restart the process.
  chrome.tabs.query({
    windowType: 'normal'
  }, tabmanager.initTabs);
  chrome.tabs.onCreated.addListener(onNewTab);
  chrome.tabs.onUpdated.addListener(tabmanager.updateLastAccessed);
  chrome.tabs.onRemoved.addListener(tabmanager.removeTab);
  chrome.tabs.onActivated.addListener(function(tabInfo) {
    menus.updateContextMenus(tabInfo['tabId']);
    tabmanager.updateLastAccessed(tabInfo['tabId']);
    });
  window.setInterval(checkToClose, settings.get('checkInterval'));
  window.setInterval(tabmanager.updateClosedCount, settings.get('badgeCounterInterval'));

  // Create the "lock tab" context menu:
  menus.createContextMenus();
};

startup();
<commit_msg>Remove unused variable<commit_after>'use strict';

import _ from 'underscore';
import menus from './js/menus';
import settings from './js/settings';
import tabmanager from './js/tabmanager';
import updater from './js/updater';

// Declare this global namespace so it can be used from popup.js
// @see startup();
const TW = window.TW = {};

/**
 * @todo: refactor into "get the ones to close" and "close 'em"
 * So it can be tested.
 */
var checkToClose = function(cutOff) {
  var i;
  cutOff = cutOff || new Date().getTime() - settings.get('stayOpen');
  var minTabs = settings.get('minTabs');

  // Tabs which have been locked via the checkbox.
  var lockedIds = settings.get("lockedIds");
  var toCut = tabmanager.getOlderThen(cutOff);

  if (settings.get('paused') === true) {
    return;
  }

  // Update the selected one to make sure it doesn't get closed.
  chrome.tabs.getSelected(null, tabmanager.updateLastAccessed);

  chrome.windows.getAll({populate:true}, function(windows) {
    var tabs = []; // Array of tabs, populated for each window.
    _.each(windows, function(myWindow) {
      tabs = myWindow.tabs;
      // Filter out the pinned tabs
      tabs = _.filter(tabs, function(tab) {return tab.pinned === false;});
      var tabsToCut = _.filter(tabs, function(t) {return toCut.indexOf(t.id) != -1;});
      if ((tabs.length - minTabs) <= 0) {
        // We have less than minTab tabs, abort.
        // Also, let's reset the last accessed time of our current tabs so they
        // don't get closed when we add a new one.
        for (i = 0; i < tabs.length; i++) {
          tabmanager.updateLastAccessed(tabs[i].id);
        }
        return;
      }

      // If cutting will reduce us below 5 tabs, only remove the first N to get to 5.
      tabsToCut = tabsToCut.splice(0, tabs.length - minTabs);

      if (tabsToCut.length === 0) {
        return;
      }

      for (i=0; i < tabsToCut.length; i++) {
        if (lockedIds.indexOf(tabsToCut[i].id) != -1) {
          // Update its time so it gets checked less frequently.
          // Would also be smart to just never add it.
          // @todo: fix that.
          tabmanager.updateLastAccessed(tabsToCut[i].id);
          continue;
        }
        closeTab(tabsToCut[i]);
      }
    });
  });
};

var closeTab = function(tab) {
  if (true === tab.pinned) {
      return;
    }
    if (tabmanager.isWhitelisted(tab.url)) {
      return;
    }

    tabmanager.closedTabs.saveTabs([tab]);
    // Close it in Chrome.
    chrome.tabs.remove(tab.id);
};

var onNewTab = function(tab) {
  // Check if it exists in corral already
  // The 2nd argument is an array of filters, we add one filter
  // which checks for an exact URL match.  If we match throw the old
  // entry away.
  tabmanager.searchTabs(function(tabs) {
    if (tabs.length) {
      _.each(tabs, function(t) {
        tabmanager.closedTabs.removeTab(t.id);
      });
    }
  }, [tabmanager.filters.exactUrl(tab.url)]);

  // Add the new one;
  tabmanager.updateLastAccessed(tab.id);
};

var startup = function() {
  settings.init();
  updater.run();
  tabmanager.closedTabs.init();

  TW.settings = settings;
  TW.updater = updater;
  TW.tabmanager = tabmanager;

  if (settings.get('purgeClosedTabs') !== false) {
    tabmanager.closedTabs.clear();
  }
  settings.set('lockedIds', []);

  // Move this to a function somehwere so we can restart the process.
  chrome.tabs.query({
    windowType: 'normal'
  }, tabmanager.initTabs);
  chrome.tabs.onCreated.addListener(onNewTab);
  chrome.tabs.onUpdated.addListener(tabmanager.updateLastAccessed);
  chrome.tabs.onRemoved.addListener(tabmanager.removeTab);
  chrome.tabs.onActivated.addListener(function(tabInfo) {
    menus.updateContextMenus(tabInfo['tabId']);
    tabmanager.updateLastAccessed(tabInfo['tabId']);
    });
  window.setInterval(checkToClose, settings.get('checkInterval'));
  window.setInterval(tabmanager.updateClosedCount, settings.get('badgeCounterInterval'));

  // Create the "lock tab" context menu:
  menus.createContextMenus();
};

startup();
<|endoftext|>