<commit_before>/*
 * This file is part of yoob.js version 0.7-PRE
 * Available from https://github.com/catseye/yoob.js/
 * This file is in the public domain.  See http://unlicense.org/ for details.
 */
if (window.yoob === undefined) yoob = {};

/*
 * Functions for creating elements.
 *
 * I dunno -- maybe just setting innerHTML would be better.
 */

yoob.makeCanvas = function(container, width, height) {
    var canvas = document.createElement('canvas');
    if (width) {
        canvas.width = width;
    }
    if (height) {
        canvas.height = height;
    }
    container.appendChild(canvas);
    return canvas;
};

yoob.makeButton = function(container, labelText, fun) {
    var button = document.createElement('button');
    button.innerHTML = labelText;
    container.appendChild(button);
    if (fun) {
        button.onclick = fun;
    }
    return button;
};

yoob.checkBoxNumber = 0;
yoob.makeCheckbox = function(container, checked, labelText, fun) {
    var checkbox = document.createElement('input');
    checkbox.type = "checkbox";
    checkbox.id = 'cfzzzb_' + yoob.checkBoxNumber;
    checkbox.checked = checked;
    var label = document.createElement('label');
    label.htmlFor = 'cfzzzb_' + yoob.checkBoxNumber;
    yoob.checkBoxNumber += 1;
    label.appendChild(document.createTextNode(labelText));

    container.appendChild(checkbox);
    container.appendChild(label);

    if (fun) {
        checkbox.onchange = function(e) {
            fun(checkbox.checked);
        }
    }
    return checkbox;
};

yoob.makeTextInput = function(container, size, value) {
    var input = document.createElement('input');
    input.size = "" + (size || 12);
    input.value = value || "";
    container.appendChild(input);
    return input;
};

yoob.makeSlider = function(container, min, max, value, fun) {
    var slider = document.createElement('input');
    slider.type = "range";
    slider.min = min;
    slider.max = max;
    slider.value = value || 0;
    if (fun) {
        slider.onchange = function(e) {
            fun(0 + slider.value);
        }
    }
    container.appendChild(slider);
    return slider;
};

yoob.makeParagraph = function(container, innerHTML) {
    var p = document.createElement('p');
    p.innerHTML = innerHTML || '';
    container.appendChild(p);
    return p;
};

yoob.makeSpan = function(container, innerHTML) {
    var span = document.createElement('span');
    span.innerHTML = innerHTML || '';
    container.appendChild(span);
    return span;
};

yoob.makeDiv = function(container, innerHTML) {
    var div = document.createElement('div');
    div.innerHTML = innerHTML || '';
    container.appendChild(div);
    return div;
};

yoob.makeTextArea = function(container, cols, rows, initial) {
    var textarea = document.createElement('textarea');
    textarea.rows = "" + rows;
    textarea.cols = "" + cols;
    if (initial) {
        container.value = initial;
    }
    container.appendChild(textarea);
    return textarea;
};

yoob.makeLineBreak = function(container) {
    var br = document.createElement('br');
    container.appendChild(br);
    return br;
};

yoob.makeSelect = function(container, labelText, optionsArray) {
    var label = document.createElement('label');
    label.innerHTML = labelText;
    container.appendChild(label);

    var select = document.createElement("select");

    for (var i = 0; i < optionsArray.length; i++) {
        var op = document.createElement("option");
        op.value = optionsArray[i][0];
        op.text = optionsArray[i][1];
        if (optionsArray[i].length > 2) {
            op.selected = optionsArray[i][2];
        } else {
            op.selected = false;
        }
        select.options.add(op);
    }

    container.appendChild(select);
    return select;
};
<commit_msg>Add yoob.SliderPlusTextInput object.<commit_after>/*
 * This file is part of yoob.js version 0.7-PRE
 * Available from https://github.com/catseye/yoob.js/
 * This file is in the public domain.  See http://unlicense.org/ for details.
 */
if (window.yoob === undefined) yoob = {};

/*
 * Functions for creating elements.
 *
 * I dunno -- maybe just setting innerHTML would be better.
 */

yoob.makeCanvas = function(container, width, height) {
    var canvas = document.createElement('canvas');
    if (width) {
        canvas.width = width;
    }
    if (height) {
        canvas.height = height;
    }
    container.appendChild(canvas);
    return canvas;
};

yoob.makeButton = function(container, labelText, fun) {
    var button = document.createElement('button');
    button.innerHTML = labelText;
    container.appendChild(button);
    if (fun) {
        button.onclick = fun;
    }
    return button;
};

yoob.checkBoxNumber = 0;
yoob.makeCheckbox = function(container, checked, labelText, fun) {
    var checkbox = document.createElement('input');
    checkbox.type = "checkbox";
    checkbox.id = 'cfzzzb_' + yoob.checkBoxNumber;
    checkbox.checked = checked;
    var label = document.createElement('label');
    label.htmlFor = 'cfzzzb_' + yoob.checkBoxNumber;
    yoob.checkBoxNumber += 1;
    label.appendChild(document.createTextNode(labelText));

    container.appendChild(checkbox);
    container.appendChild(label);

    if (fun) {
        checkbox.onchange = function(e) {
            fun(checkbox.checked);
        };
    }
    return checkbox;
};

yoob.makeTextInput = function(container, size, value) {
    var input = document.createElement('input');
    input.size = "" + (size || 12);
    input.value = value || "";
    container.appendChild(input);
    return input;
};

yoob.makeSlider = function(container, min, max, value, fun) {
    var slider = document.createElement('input');
    slider.type = "range";
    slider.min = min;
    slider.max = max;
    slider.value = value || 0;
    if (fun) {
        slider.onchange = function(e) {
            fun(parseInt(slider.value, 10));
        };
    }
    container.appendChild(slider);
    return slider;
};

yoob.makeParagraph = function(container, innerHTML) {
    var p = document.createElement('p');
    p.innerHTML = innerHTML || '';
    container.appendChild(p);
    return p;
};

yoob.makeSpan = function(container, innerHTML) {
    var span = document.createElement('span');
    span.innerHTML = innerHTML || '';
    container.appendChild(span);
    return span;
};

yoob.makeDiv = function(container, innerHTML) {
    var div = document.createElement('div');
    div.innerHTML = innerHTML || '';
    container.appendChild(div);
    return div;
};

yoob.makeTextArea = function(container, cols, rows, initial) {
    var textarea = document.createElement('textarea');
    textarea.rows = "" + rows;
    textarea.cols = "" + cols;
    if (initial) {
        container.value = initial;
    }
    container.appendChild(textarea);
    return textarea;
};

yoob.makeLineBreak = function(container) {
    var br = document.createElement('br');
    container.appendChild(br);
    return br;
};

yoob.makeSelect = function(container, labelText, optionsArray) {
    var label = document.createElement('label');
    label.innerHTML = labelText;
    container.appendChild(label);

    var select = document.createElement("select");

    for (var i = 0; i < optionsArray.length; i++) {
        var op = document.createElement("option");
        op.value = optionsArray[i][0];
        op.text = optionsArray[i][1];
        if (optionsArray[i].length > 2) {
            op.selected = optionsArray[i][2];
        } else {
            op.selected = false;
        }
        select.options.add(op);
    }

    container.appendChild(select);
    return select;
};

SliderPlusTextInput = function() {
    this.init = function(cfg) {
        this.slider = cfg.slider;
        this.textInput = cfg.textInput;
        this.callback = cfg.callback;
        return this;
    };

    this.set = function(value) {
        this.slider.value = "" + value;
        this.textInput.value = "" + value;
        this.callback(value);
    };
};

yoob.makeSliderPlusTextInput = function(container, label, min_, max_, value, callback) {
    yoob.makeSpan(container, label);
    var slider = yoob.makeSlider(container, min_, max_, value);
    var s = "" + value;
    var textInput = yoob.makeTextInput(container, s.length + 1, s);
    slider.onchange = function(e) {
        textInput.value = slider.value;
        fun(parseInt(slider.value, 10));
    };
    textInput.onchange = function(e) {
        var v = parseInt(textInput.value, 10);
        if (v !== NaN) {
            slider.value = "" + v;
            fun(v);
        }
    };
    return new SliderPlusTextInput().init({
        'slider': slider,
        'textInput': textInput,
        'callback': callback
    });
};
<|endoftext|>


<commit_before>Generator = function(editorUi, fileName, language) {
    this.editorUi = editorUi;
    this.fileName = fileName;
    this.language = language;
    this.classAndInterfaceLists = {}; // key = mxCells id
}

Generator.prototype.generateCode = function() {
    var editor = this.editorUi.editor;
    var fileName = this.fileName;
    var language = this.language;
    var xmlString = mxUtils.getXml(editor.getGraphXml());
    var xml = this.xmlStringToXml(xmlString);

    this.parseXmlToclassAndInterfaceList(xml);
    console.log(this.classAndInterfaceLists);
    var code = '';
    if (language == 'java')
    {
        code = this.generateCodeJava();
    }

    console.log(code);
    // If code is generated, prompt download file
    if (code) {
        this.saveCode(fileName, code);
        editorUi.hideDialog();
    }

}

Generator.prototype.generateCodeJava = function() {
    var list = this.classAndInterfaceLists;
    var str = '';
    for (var key in list) {
        if (list.hasOwnProperty(key)) {
            str += this.generateStringFromClassElement(list[key]);
            str += '\n\n';
        }
    }
    return str;
}

Generator.prototype.generateStringFromClassElement = function(element) {
    var attributesStr = this.generateStringAttribute(element);
    var methodsStr = this.generateStringMethod(element);

    var str = '';
    if (element.type === 'class') {
        var className = element.className;

        str +=
`\
class ${className} {
${attributesStr}
${methodsStr}
}\
`;
    }
    else if (element.type === 'interface') {
        var interfaceName = element.interfaceName;
        str += `interface ${interfaceName} {\n${attributesStr}\n${methodsStr}\n}\n`;
    }
    return str;
}

Generator.prototype.generateStringAttribute = function(element) {
    var attributesStr = '';

    for (var i = 0; i < element.privateAttributes.length; i++) {
        var attribute = element.privateAttributes[i];
        attributesStr += `  private ${attribute.type} ${attribute.attributeName}\n`;
    }
    for (var i = 0; i < element.publicAttributes.length; i++) {
        var attribute = element.publicAttributes[i];
        attributesStr += `  public ${attribute.type} ${attribute.attributeName}\n`;
    }
    for (var i = 0; i < element.protectedAttributes.length; i++) {
        var attribute = element.protectedAttributes[i];
        attributesStr += `  protected ${attribute.type} ${attribute.attributeName}\n`;
    }

    return attributesStr;
}

Generator.prototype.generateStringMethod = function(element) {
    var methodsStr = '';

// ================== TEMPLATE FOR PRIVATE METHODS =============================
    for (var i = 0; i < element.privateMethods.length; i++) {
        var method = element.privateMethods[i];
        methodsStr +=
`\
    private ${method.returnType} ${method.methodName} {
        /* insert code here */
    }

`
    }
// ================== END TEMPLATE FOR PRIVATE METHODS =============================

// ================== TEMPLATE FOR PUBLIC METHODS =============================
    for (var i = 0; i < element.publicMethods.length; i++) {
        var method = element.publicMethods[i];
        methodsStr +=
`\
    public ${method.returnType} ${method.methodName} {
        /* insert code here */
    }

`
    }
// ================== END TEMPLATE FOR PUBLIC METHODS =============================

// ================== TEMPLATE FOR PROTECTED METHODS =============================
    for (var i = 0; i < element.protectedMethods.length; i++) {
        var method = element.protectedMethods[i];
        methodsStr +=
`\
    protected ${method.returnType} ${method.methodName} {
        /* insert code here */
    }

`
    }
// ================== END TEMPLATE FOR PROTECTD METHODS =============================
    return methodsStr;
}

Generator.prototype.parseXmlToclassAndInterfaceList = function(xml) {
    var mxCells = xml.getElementsByTagName("mxCell");

    // Remove the first 2 because they are the canvas elements
    for (var id = 2; id < mxCells.length; id++) {
        var mxCell = mxCells[id];
        var style = mxCell.getAttribute('style');
        var componentName = this.getComponentNameFromStyle(style);
        if (componentName) {
            this.doAction(mxCell, componentName);
        }
    }
}

Generator.prototype.doAction = function(mxCell, componentName) {
    if (componentName === 'class') {
        this.generateAndPushClassElement(mxCell);
    } else if (componentName === 'interface') {
        this.generateAndPushInterfaceElement(mxCell);
    }
    else if (componentName === 'attribute') {
        var value = mxCell.getAttribute('value');
        var parent = mxCell.getAttribute('parent');
        var parts = value.split(' ');
        var accessibility = parts[0];
        var attribute = {
            attributeName: parts[1].slice(0, -1),
            type: parts[2],
        };
        if (accessibility === "+") {
            this.classAndInterfaceLists[parent].publicAttributes.push(attribute);
        } else if (accessibility === "-") {
            this.classAndInterfaceLists[parent].privateAttributes.push(attribute);
        } else if (accessibility === "#") {
            this.classAndInterfaceLists[parent].protectedAttributes.push(attribute);
        }
    } else if (componentName === 'method') {
        var value = mxCell.getAttribute('value');
        var parent = mxCell.getAttribute('parent');
        var parts = value.split(' ');
        var accessibility = parts[0];
        var method = {
            methodName: parts[1].slice(0, -1),
            returnType: parts[2],
        };
        if (accessibility === "+") {
            this.classAndInterfaceLists[parent].publicMethods.push(method);
        } else if (accessibility === "-") {
            this.classAndInterfaceLists[parent].privateMethods.push(method);
        } else if (accessibility === "#") {
            this.classAndInterfaceLists[parent].protectedMethods.push(method);
        }
    }
}

Generator.prototype.generateAndPushClassElement = function(mxCell) {
        var element = {
            type: 'class',
            className: mxCell.getAttribute('value'),
            privateAttributes: [],
            publicAttributes: [],
            protectedAttributes: [],
            privateMethods: [],
            publicMethods: [],
            protectedMethods: [],
            implements: [],
            extends: [],
        };
        var id = mxCell.getAttribute('id');
        this.classAndInterfaceLists[id] = element;
}

Generator.prototype.generateAndPushInterfaceElement = function(mxCell) {
        var element = {
            type: 'interface',
            interfaceName: mxCell.getAttribute('value').split(' ')[1],
            privateAttributes: [],
            publicAttributes: [],
            protectedAttributes: [],
            privateMethods: [],
            publicMethods: [],
            protectedMethods: [],
            implements: [],
            extends: [],
        };
        var id = mxCell.getAttribute('id');
        this.classAndInterfaceLists[id] = element;
}

Generator.prototype.getComponentNameFromStyle = function(style) {
    var elements = style.split(';');
    var index = -1;
    for (var i = 0; i < elements.length && index === -1; i++) {
        if (elements[i].indexOf('componentName') >= 0) {
            index = i;
        }
    }
    var componentName = null;
    if (index >= 0) {
        componentName = elements[index].split('=')[1];
    }
    return componentName;
}

Generator.prototype.saveCode = function(fileName, code) {
    var data = new Blob([code], {type: 'text/plain'});

    // If we are replacing a previously generated file we need to
    // manually revoke the object URL to avoid memory leaks.
    if (code !== null) {
      window.URL.revokeObjectURL(code);
    }

    code = window.URL.createObjectURL(data);

    var link = document.createElement('a');
    if (typeof link.download === 'string') {
        document.body.appendChild(link); // Firefox requires the link to be in the body
        link.download = fileName;
        link.href = code;
        link.click();
        document.body.removeChild(link); // remove the link when done
    } else {
        location.replace(uri);
    }
}

Generator.prototype.xmlStringToXml = function(xmlString) {
    // get the function to parse xmlStr to xml
    var parseXml;

    if (window.DOMParser) {
        parseXml = function(xmlStr) {
            return ( new window.DOMParser() ).parseFromString(xmlStr, "text/xml");
        };
    } else if (typeof window.ActiveXObject != "undefined" && new window.ActiveXObject("Microsoft.XMLDOM")) {
        parseXml = function(xmlStr) {
            var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM");
            xmlDoc.async = "false";
            xmlDoc.loadXML(xmlStr);
            return xmlDoc;
        };
    } else {
        parseXml = function() { return null; }
    }

    return parseXml(xmlString);
}<commit_msg>Refactor template method<commit_after>Generator = function(editorUi, fileName, language) {
    this.editorUi = editorUi;
    this.fileName = fileName;
    this.language = language;
    this.classAndInterfaceLists = {}; // key = mxCells id
}

Generator.prototype.generateCode = function() {
    var editor = this.editorUi.editor;
    var fileName = this.fileName;
    var language = this.language;
    var xmlString = mxUtils.getXml(editor.getGraphXml());
    var xml = this.xmlStringToXml(xmlString);

    this.parseXmlToclassAndInterfaceList(xml);
    console.log(this.classAndInterfaceLists);
    var code = '';
    if (language == 'java')
    {
        code = this.generateCodeJava();
    }

    console.log(code);
    // If code is generated, prompt download file
    if (code) {
        this.saveCode(fileName, code);
        editorUi.hideDialog();
    }

}

Generator.prototype.generateCodeJava = function() {
    var list = this.classAndInterfaceLists;
    var str = '';
    for (var key in list) {
        if (list.hasOwnProperty(key)) {
            str += this.generateStringFromClassElement(list[key]);
            str += '\n\n';
        }
    }
    return str;
}

Generator.prototype.generateStringFromClassElement = function(element) {
    var attributesStr = this.generateStringAttribute(element);
    var methodsStr = this.generateStringMethod(element);

    var str = '';
    if (element.type === 'class') {
        var className = element.className;

        str +=
`\
class ${className} {
${attributesStr}
${methodsStr}
}\
`;
    }
    else if (element.type === 'interface') {
        var interfaceName = element.interfaceName;
        str += `interface ${interfaceName} {\n${attributesStr}\n${methodsStr}\n}\n`;
    }
    return str;
}

/** ============== Generate Class Attributes String ================================ */
Generator.prototype.generateStringAttributeTemplate = function(scopeName, attribute) {
    return `    ${scopeName} ${attribute.type} ${attribute.attributeName}\n`
}

Generator.prototype.generateStringAttribute = function(element) {
    var attributesStr = '';

    for (var i = 0; i < element.privateAttributes.length; i++) {
        var attribute = element.privateAttributes[i];
        attributesStr += this.generateStringAttributeTemplate('private', attribute);
    }
    for (var i = 0; i < element.publicAttributes.length; i++) {
        var attribute = element.publicAttributes[i];
        attributesStr += this.generateStringAttributeTemplate('public', attribute);
    }
    for (var i = 0; i < element.protectedAttributes.length; i++) {
        var attribute = element.protectedAttributes[i];
        attributesStr += this.generateStringAttributeTemplate('protected', attribute);
    }

    return attributesStr;
}
/** ============== END Generate Class Attributes String ================================ */

/** ============== Generate Class Method String ================================ */
Generator.prototype.generateMethodTemplate = function(methodScopeName, method) {
    return (
`\
    ${methodScopeName} ${method.returnType} ${method.methodName} {
        /* insert code here */
    }

`
    );
}

Generator.prototype.generateStringMethod = function(element) {
    var methodsStr = '';

    for (var i = 0; i < element.privateMethods.length; i++) {
        var method = element.privateMethods[i];
        methodsStr += this.generateMethodTemplate('private', method);
    }

    for (var i = 0; i < element.publicMethods.length; i++) {
        var method = element.publicMethods[i];
        methodsStr += this.generateMethodTemplate('public', method);
    }

    for (var i = 0; i < element.protectedMethods.length; i++) {
        var method = element.protectedMethods[i];
        methodsStr += this.generateMethodTemplate('protected', method);
    }

    return methodsStr;
}
/** ============== END Generate Class Method String ================================ */

Generator.prototype.parseXmlToclassAndInterfaceList = function(xml) {
    var mxCells = xml.getElementsByTagName("mxCell");

    // Remove the first 2 because they are the canvas elements
    for (var id = 2; id < mxCells.length; id++) {
        var mxCell = mxCells[id];
        var style = mxCell.getAttribute('style');
        var componentName = this.getComponentNameFromStyle(style);
        if (componentName) {
            this.doAction(mxCell, componentName);
        }
    }
}

Generator.prototype.doAction = function(mxCell, componentName) {
    if (componentName === 'class') {
        this.generateAndPushClassElement(mxCell);
    } else if (componentName === 'interface') {
        this.generateAndPushInterfaceElement(mxCell);
    }
    else if (componentName === 'attribute') {
        var value = mxCell.getAttribute('value');
        var parent = mxCell.getAttribute('parent');
        var parts = value.split(' ');
        var accessibility = parts[0];
        var attribute = {
            attributeName: parts[1].slice(0, -1),
            type: parts[2],
        };
        if (accessibility === "+") {
            this.classAndInterfaceLists[parent].publicAttributes.push(attribute);
        } else if (accessibility === "-") {
            this.classAndInterfaceLists[parent].privateAttributes.push(attribute);
        } else if (accessibility === "#") {
            this.classAndInterfaceLists[parent].protectedAttributes.push(attribute);
        }
    } else if (componentName === 'method') {
        var value = mxCell.getAttribute('value');
        var parent = mxCell.getAttribute('parent');
        var parts = value.split(' ');
        var accessibility = parts[0];
        var method = {
            methodName: parts[1].slice(0, -1),
            returnType: parts[2],
        };
        if (accessibility === "+") {
            this.classAndInterfaceLists[parent].publicMethods.push(method);
        } else if (accessibility === "-") {
            this.classAndInterfaceLists[parent].privateMethods.push(method);
        } else if (accessibility === "#") {
            this.classAndInterfaceLists[parent].protectedMethods.push(method);
        }
    }
}

Generator.prototype.generateAndPushClassElement = function(mxCell) {
        var element = {
            type: 'class',
            className: mxCell.getAttribute('value'),
            privateAttributes: [],
            publicAttributes: [],
            protectedAttributes: [],
            privateMethods: [],
            publicMethods: [],
            protectedMethods: [],
            implements: [],
            extends: [],
        };
        var id = mxCell.getAttribute('id');
        this.classAndInterfaceLists[id] = element;
}

Generator.prototype.generateAndPushInterfaceElement = function(mxCell) {
        var element = {
            type: 'interface',
            interfaceName: mxCell.getAttribute('value').split(' ')[1],
            privateAttributes: [],
            publicAttributes: [],
            protectedAttributes: [],
            privateMethods: [],
            publicMethods: [],
            protectedMethods: [],
            implements: [],
            extends: [],
        };
        var id = mxCell.getAttribute('id');
        this.classAndInterfaceLists[id] = element;
}

Generator.prototype.getComponentNameFromStyle = function(style) {
    var elements = style.split(';');
    var index = -1;
    for (var i = 0; i < elements.length && index === -1; i++) {
        if (elements[i].indexOf('componentName') >= 0) {
            index = i;
        }
    }
    var componentName = null;
    if (index >= 0) {
        componentName = elements[index].split('=')[1];
    }
    return componentName;
}

Generator.prototype.saveCode = function(fileName, code) {
    var data = new Blob([code], {type: 'text/plain'});

    // If we are replacing a previously generated file we need to
    // manually revoke the object URL to avoid memory leaks.
    if (code !== null) {
      window.URL.revokeObjectURL(code);
    }

    code = window.URL.createObjectURL(data);

    var link = document.createElement('a');
    if (typeof link.download === 'string') {
        document.body.appendChild(link); // Firefox requires the link to be in the body
        link.download = fileName;
        link.href = code;
        link.click();
        document.body.removeChild(link); // remove the link when done
    } else {
        location.replace(uri);
    }
}

Generator.prototype.xmlStringToXml = function(xmlString) {
    // get the function to parse xmlStr to xml
    var parseXml;

    if (window.DOMParser) {
        parseXml = function(xmlStr) {
            return ( new window.DOMParser() ).parseFromString(xmlStr, "text/xml");
        };
    } else if (typeof window.ActiveXObject != "undefined" && new window.ActiveXObject("Microsoft.XMLDOM")) {
        parseXml = function(xmlStr) {
            var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM");
            xmlDoc.async = "false";
            xmlDoc.loadXML(xmlStr);
            return xmlDoc;
        };
    } else {
        parseXml = function() { return null; }
    }

    return parseXml(xmlString);
}<|endoftext|>


<commit_before>var DataExplorer = (function () {
    var deferred = new $.Deferred(),
        options = {
            'User': {},
            'Site': {}
        };

    function init(settings) {
        if (typeof settings === 'object') {
            for (var setting in settings) {
                if (settings.hasOwnProperty(setting)) {
                    if (typeof settings[setting] === 'object') {
                        if (typeof options[setting] === 'object') {
                            $.extend(options[setting], settings[setting]);
                        }
                    } else {
                        if (setting.indexOf('.') !== -1) {
                            var option = setting.split('.');

                            if (typeof options[option[0]] === 'object') {
                                options[option[0]][option[1]] = settings[setting];
                            }
                        } else {
                            options[setting] = settings[setting];
                        }
                    }
                }
            }
        }

        $(document).ready(function () {
            deferred.resolve();
        });
    }

    function ready(callback) {
        deferred.done(callback);
    }

    function template(target, attribute, replacements) {
        if (typeof target === 'string') {
            target = $(target);
        }

        if (!target.length) {
            return;
        }

        target.each(function () {
            var key = 'tmpl-' + attribute,
                self = $(this),
                template = self.data(key);

            if (!template) {
                if (attribute === 'text') {
                    template = this.firstChild.nodeValue;
                } else {
                    template = this[attribute];
                }

                if (!template) {
                    return;
                }

                self.data(key, template);
            }

            if (attribute === 'text') {
                this.firstChild.nodeValue = template.format(replacements);
            } else {
                this[attribute] = template.format(replacements);
            }
        });
    }

    function DeferredRequest(settings) {
        if (!(this instanceof DeferredRequest)) {
            return new DeferredRequest(settings);
        }

        var options = $.extend({
                'delay': 950,
                'type': 'post'
            }, settings),
            pending = null,
            dispatched = false,
            deferred = null;

        $(document).bind('unload', function () {
            request(true);
        });

        this.request = function rerequest(data) {
            if (pending) {
                clearTimeout(pending);
            }

            if (!dispatched) {
                options.data = data;
                pending = setTimeout(request, options.delay);
            } else {
                deferred = function () {
                    rerequest(data);
                };
            }
        }

        function request(synchronous) {
            dispatched = true;
            synchronous = !!synchronous;

            $.ajax({
                'async': synchronous,
                'data': options.data,
                'type': options.type,
                'url': options.url,
                'success': response
            });
        }

        function response(response) {
            if (options.callback) {
                options.callback(response);
            }

            dispatched = false;

            if (deferred) {
                deferred();
            }
        }
    }

    return {
        'init': init,
        'ready': ready,
        'options': options,
        'template': template,
        'DeferredRequest': DeferredRequest
    };
})();

$.fn.tabs = function () {
    return this.delegate("a:not(.youarehere)", "click", function () {
        $(this.hash).show();
        $(this).addClass("youarehere")
            .trigger('show')
            .siblings()
            .removeClass("youarehere")
            .each(function () {
                $(this.hash).hide();
            });
    }).delegate("a", "click", function () {
        return false;
    });
};

if (!String.prototype.trim) {
    String.prototype.trim = function () {
        return this.replace(/^\s+|\s+$/g, '');
    };
}

String.prototype.format = function (replacements) {
    if (!replacements) {
        return this;
    }

    var result = this, replacement;

    for (replacement in replacements) {
        if (replacements.hasOwnProperty(replacement)) {
            var regex = new RegExp(":" + replacement + ';?', 'g');

            result = result.replace(regex, function (match, optional) {
                return replacements[replacement] || "";
            });
        }
    }

    return result;
}

String.prototype.from = function (char, inclusive) {
    char = this.indexOf(char);

    if (typeof inclusive === 'undefined') {
        inclusive = true;
    }

    if (char < 1) {
        return this;
    }

    return this.substring(char - (inclusive ? 0 : 1));
}

String.prototype.asVariable = function () {
    var chunks = this.split(/\s+/),
        result = chunks[0].toLowerCase();

    if (chunks.length == 0) {
        return result;
    }

    for (var i = 1; i < chunks.length; ++i) {
        result = result
            + chunks[i].substring(0, 1).toUpperCase()
            + chunks[i].substring(1).toLowerCase();
    }

    return result;
}<commit_msg>Fixed issue where formatter wasn't handling a 0 value correctly<commit_after>var DataExplorer = (function () {
    var deferred = new $.Deferred(),
        options = {
            'User': {},
            'Site': {}
        };

    function init(settings) {
        if (typeof settings === 'object') {
            for (var setting in settings) {
                if (settings.hasOwnProperty(setting)) {
                    if (typeof settings[setting] === 'object') {
                        if (typeof options[setting] === 'object') {
                            $.extend(options[setting], settings[setting]);
                        }
                    } else {
                        if (setting.indexOf('.') !== -1) {
                            var option = setting.split('.');

                            if (typeof options[option[0]] === 'object') {
                                options[option[0]][option[1]] = settings[setting];
                            }
                        } else {
                            options[setting] = settings[setting];
                        }
                    }
                }
            }
        }

        $(document).ready(function () {
            deferred.resolve();
        });
    }

    function ready(callback) {
        deferred.done(callback);
    }

    function template(target, attribute, replacements) {
        if (typeof target === 'string') {
            target = $(target);
        }

        if (!target.length) {
            return;
        }

        target.each(function () {
            var key = 'tmpl-' + attribute,
                self = $(this),
                template = self.data(key);

            if (!template) {
                if (attribute === 'text') {
                    template = this.firstChild.nodeValue;
                } else {
                    template = this[attribute];
                }

                if (!template) {
                    return;
                }

                self.data(key, template);
            }

            if (attribute === 'text') {
                this.firstChild.nodeValue = template.format(replacements);
            } else {
                this[attribute] = template.format(replacements);
            }
        });
    }

    function DeferredRequest(settings) {
        if (!(this instanceof DeferredRequest)) {
            return new DeferredRequest(settings);
        }

        var options = $.extend({
                'delay': 950,
                'type': 'post'
            }, settings),
            pending = null,
            dispatched = false,
            deferred = null;

        $(document).bind('unload', function () {
            request(true);
        });

        this.request = function rerequest(data) {
            if (pending) {
                clearTimeout(pending);
            }

            if (!dispatched) {
                options.data = data;
                pending = setTimeout(request, options.delay);
            } else {
                deferred = function () {
                    rerequest(data);
                };
            }
        }

        function request(synchronous) {
            dispatched = true;
            synchronous = !!synchronous;

            $.ajax({
                'async': synchronous,
                'data': options.data,
                'type': options.type,
                'url': options.url,
                'success': response
            });
        }

        function response(response) {
            if (options.callback) {
                options.callback(response);
            }

            dispatched = false;

            if (deferred) {
                deferred();
            }
        }
    }

    return {
        'init': init,
        'ready': ready,
        'options': options,
        'template': template,
        'DeferredRequest': DeferredRequest
    };
})();

$.fn.tabs = function () {
    return this.delegate("a:not(.youarehere)", "click", function () {
        $(this.hash).show();
        $(this).addClass("youarehere")
            .trigger('show')
            .siblings()
            .removeClass("youarehere")
            .each(function () {
                $(this.hash).hide();
            });
    }).delegate("a", "click", function () {
        return false;
    });
};

if (!String.prototype.trim) {
    String.prototype.trim = function () {
        return this.replace(/^\s+|\s+$/g, '');
    };
}

String.prototype.format = function (replacements) {
    if (!replacements) {
        return this;
    }

    var result = this, replacement;

    for (replacement in replacements) {
        if (replacements.hasOwnProperty(replacement)) {
            var regex = new RegExp(":" + replacement + ';?', 'g');

            result = result.replace(regex, function (match, optional) {
                var sub = replacements[replacement];

                if (!sub && sub !== 0) {
                    sub = "";
                }

                return sub;
            });
        }
    }

    return result;
}

String.prototype.from = function (char, inclusive) {
    char = this.indexOf(char);

    if (typeof inclusive === 'undefined') {
        inclusive = true;
    }

    if (char < 1) {
        return this;
    }

    return this.substring(char - (inclusive ? 0 : 1));
}

String.prototype.asVariable = function () {
    var chunks = this.split(/\s+/),
        result = chunks[0].toLowerCase();

    if (chunks.length == 0) {
        return result;
    }

    for (var i = 1; i < chunks.length; ++i) {
        result = result
            + chunks[i].substring(0, 1).toUpperCase()
            + chunks[i].substring(1).toLowerCase();
    }

    return result;
}<|endoftext|>


<commit_before>myApp.controller('InventoryController', function($scope, $window, $location, $routeParams, inventoryService, breadCrumbService) {
    $scope.inventories = inventoryService.loadInventories();
    $scope.inventory = inventoryService.getInventory($routeParams.id);
    $scope.headerIcon = $scope.inventory ? "arrow_back" : "";
    $scope.breadCrumbs = breadCrumbService;

    $scope.$on('$routeChangeSuccess', function (e, curr, prev) {
        var breadCrumb = curr.locals.breadCrumb;
        breadCrumb.path = $location.path();
        breadCrumbService.update(breadCrumb);
    });

    $scope.goBack = function() {
        $window.history.go(-1);
    };

    $scope.newInventory = function() {
        var inventory = inventoryService.newInventory();
        $location.path(`/new/${inventory.id}`);
    };

    $scope.editInventory = function(inv) {
        $location.path(`/edit/${inv.id}`);
    };

    $scope.addInventory = function() {
        var id = $routeParams.id;
        $scope.inventories[id] = $scope.inventory;
        inventoryService.storeInventories();
        $location.path(`/view/${id}`);
    };

    $scope.newItem = function() {
        var id = $scope.inventory.id;
        var item = inventoryService.newItem();
        $location.path(`/edit/${id}/item/new/${item.id}`);
    };

    $scope.addItem = function() {
        var { id, itemId } = $routeParams;
        var inventory = $scope.inventories[id];

        inventory.items[itemId] = $scope.item;
        inventoryService.storeInventories();
        $location.path(`/view/${id}`);
    };

    $scope.removeItem = function() {
    };
});
<commit_msg>Fix leading slash in breadcrumbs links<commit_after>myApp.controller('InventoryController', function($scope, $window, $location, $routeParams, inventoryService, breadCrumbService) {
    $scope.inventories = inventoryService.loadInventories();
    $scope.inventory = inventoryService.getInventory($routeParams.id);
    $scope.headerIcon = $scope.inventory ? "arrow_back" : "";
    $scope.breadCrumbs = breadCrumbService;

    $scope.$on('$routeChangeSuccess', function (e, curr, prev) {
        var breadCrumb = curr.locals.breadCrumb;
        breadCrumb.path = $location.path().substring(1);
        breadCrumbService.update(breadCrumb);
    });

    $scope.goBack = function() {
        $window.history.go(-1);
    };

    $scope.newInventory = function() {
        var inventory = inventoryService.newInventory();
        $location.path(`/new/${inventory.id}`);
    };

    $scope.editInventory = function(inv) {
        $location.path(`/edit/${inv.id}`);
    };

    $scope.addInventory = function() {
        var id = $routeParams.id;
        $scope.inventories[id] = $scope.inventory;
        inventoryService.storeInventories();
        $location.path(`/view/${id}`);
    };

    $scope.newItem = function() {
        var id = $scope.inventory.id;
        var item = inventoryService.newItem();
        $location.path(`/edit/${id}/item/new/${item.id}`);
    };

    $scope.addItem = function() {
        var { id, itemId } = $routeParams;
        var inventory = $scope.inventories[id];

        inventory.items[itemId] = $scope.item;
        inventoryService.storeInventories();
        $location.path(`/view/${id}`);
    };

    $scope.removeItem = function() {
    };
});
<|endoftext|>


<commit_before>angular
  .module("lift-ng", [])
  .service("plumbing", [ "$q", function($q){
    var defers = {};

    var create = function(id) {
      var q = $q.defer();
      defers[id] = q;
      return q;
    };

    var resolve = function(data, q) {
      if(data.msg) {
        q.reject(data.msg)
      } else {
        if (typeof data.data !== "undefined") {
          inject(data.data);
          q.resolve(data.data);
        }
        else {
          q.resolve();
        }
      }
      return q;
    };

    // Called by the LiftNgFutureActor when a Future is fulfilled
    var fulfill = function(data, id) {
      var theId = id || data.id;
      var q = defers[theId];
      if(typeof q !== "undefined" && q !== null) { // We found our awaiting defer/promise
        resolve(data, q);
        delete defers[theId];
      } else { // We arrived before the model which embeds us!
        resolve(data, create(theId));
      }
    };

    // Called to inject promises wherever our serializer encountered a Future
    var inject = function(model) {
      for(var k in model) {
        if(model[k] == null) {
          // Don't do anything, skip
        }
        // It is a future which we need to inject
        else if(model[k]["net.liftmodules.ng.Angular.future"]) {
          var id   = model[k]["net.liftmodules.ng.Angular.future"];
          var data = model[k].data;
          var msg  = model[k].msg;
          var q = defers[id];
          if(typeof q !== "undefined" && q !== null) { // The future resolved before we arrived here
            model[k] = q.promise;
            delete defers[id];
          } else if(data) { // The future had already RESOLVED at serialization time
            var d = $q.defer();
            d.resolve(data);
            model[k] = d.promise;
          } else if(msg) { // The future had already FAILED at serialization time
            var d = $q.defer();
            d.reject(msg);
            model[k] = d.promise;
          } else { // Promise/Future pending
            model[k] = create(id).promise;
          }
        }
        // Not a future, so check children
        else if(typeof model[k] === "object") {
          inject(model[k]);
        }
      }
    };

    return {
      createDefer: create,
      resolve: resolve,
      fulfill: fulfill,
      inject: inject
    }
  }])
  .service("liftProxy", ["$rootScope", "$q", "plumbing", function ($rootScope, $q, plumbing) {
    window.net_liftmodules_ng.init();

    var onServerCommError = function(count, which, req) {
      $rootScope.$emit("net_liftmodules_ng.serverCommError", count, which, req);
    };

    var onServerCommErrorClear = function(which) {
      $rootScope.$emit("net_liftmodules_ng.serverCommErrorClear", which);
    };

    window.net_liftmodules_ng.onServerCommErrorClearCallbacks.push(onServerCommErrorClear);

    var onCometError = function(count) {
      $rootScope.$apply(function(){
        onServerCommError(count, "comet");
      });
    };

    window.net_liftmodules_ng.onServerCommErrorCallbacks.push(onCometError);

    var onErrorFor = function(req) { return function(count) {
      onServerCommError(count, "ajax", req);
    }};

    var toData = function (requestData) {
      return requestData.name + "=" + encodeURIComponent(JSON.stringify({data: requestData.data}))
    };
    var toEnhancedReq = function (requestData) { return {
      data: toData(requestData),
      when: (new Date()).getTime(),
      onError: onErrorFor(requestData)
    }};

    var requestFor;
    if(window.net_liftmodules_ng.enhancedAjax) requestFor = toEnhancedReq;
    else requestFor = toData;

    var svc = {
      request: function (requestData) {
        var req = requestFor(requestData);
        var defer = $q.defer();

        var onSuccess = function(response) { $rootScope.$apply(function(){ // Must work under the watchful eye of angular
          // If there is no future ID, then we have our data and we're done.
          if(!response.futureId) {
            plumbing.resolve(response, defer);
          }

          // Otherwise, we need to plumb out a new promise because we'll get the value later.
          else {
            plumbing.createDefer(response.futureId).promise.then(
              function(data)  { defer.resolve(data)  },
              function(error) { defer.reject(error)  },
              function(notify){ defer.notify(notify) }
            )
          }
        })};

        var onFailure = function() { $rootScope.$apply(function() {
          defer.reject("net.liftmodules.ng.Angular.ajaxErrorRetryExceeded");
        })};

        window.net_liftmodules_ng.ajax(req, onSuccess, onFailure, "json");

        return defer.promise;
      }
    };

    return svc;
  }
]);

(function(){
  var net_liftmodules_ng = window.net_liftmodules_ng || {};
  // Careful to only init once, since it's possible to have multiple angular apps in one page
  net_liftmodules_ng.isInitialized = false;
  // Using an array of callbacks to handle case of multiple angular apps
  net_liftmodules_ng.onServerCommErrorCallbacks = [];
  net_liftmodules_ng.onServerCommErrorClearCallbacks = [];
  net_liftmodules_ng.init = function() { if(net_liftmodules_ng.enhancedAjax) { // Remove this condition once we can support Lift 3.x
    if(!net_liftmodules_ng.isInitialized) {
      var serverCommErrorCount = 0;
      var clearServerCommError = function (which) {
        if (serverCommErrorCount != 0) {
          serverCommErrorCount = 0;
          for (var i = 0; i < net_liftmodules_ng.onServerCommErrorClearCallbacks.length; i++)
            net_liftmodules_ng.onServerCommErrorClearCallbacks[i](which);
        }
      };

      if (liftAjax) {
        // We've passed {data, when} to the ajax lift machinery, so we need to pull the data part back out.
        var onlyData = function (req) {
          // This check prevents us from screwing up any non-lift-ng ajax calls someone could possibly be making.
          if (typeof req === "object") return req.data;
          else return req;
        };

        var ajaxFailureWrapper = function (req, onFailure) {
          return function () {
            // Note that we _could_ count all failures. However, in the unlikely event that someone is doing ajax
            // outside of lift-ng, failures can also be regular ol' Exceptions in business code which are not any
            // indication of our ability to communicate with the server via lift-ng.
            if (typeof req === "object" && typeof req.onError === "function")
              req.onError(++serverCommErrorCount);
            onFailure.apply(this, arguments); // We know lift always passes a failure cb function
          }
        };

        var ajaxSuccessWrapper = function (onSuccess) {
          return function () {
            // Although we are not counting non-lift-ng failures, we will clear any time we successfully communicate with
            // the server for any reason.
            clearServerCommError("ajax");
            onSuccess.apply(this, arguments); // We know lift always passes a success cb function
          }
        };

        // Wrap the json call with our hooks in place
        var origAjax = window.liftAjax.lift_actualJSONCall;
        window.liftAjax.lift_actualJSONCall = function (req, onSuccess, onFailure) {
          return origAjax(onlyData(req), ajaxSuccessWrapper(onSuccess), ajaxFailureWrapper(req, onFailure));
        };

        // Override the sort function if we should retry ajax in order.
        if (net_liftmodules_ng.retryAjaxInOrder) {
          window.liftAjax.lift_ajaxQueueSort = function () {
            window.liftAjax.lift_ajaxQueue.sort(function (a, b) {
              // If both items are one of our doctored-up requests, grab our 'when' which is the original request time.
              if (typeof a.theData === "object" && a.theData.when && typeof b.theData === "object" && b.theData.when)
                return a.theData.when - b.theData.when;
              else // Not our stuff, so let's not screw around with the original order logic.
                return a.when - b.when;
            });
          };
        }
      }

      if (window.liftComet) {
        var origCometOnFailure = window.liftComet.lift_handlerFailureFunc;
        window.liftComet.lift_handlerFailureFunc = function () {
          serverCommErrorCount++;
          for (var i = 0; i < net_liftmodules_ng.onServerCommErrorCallbacks.length; i++)
            net_liftmodules_ng.onServerCommErrorCallbacks[i](serverCommErrorCount);
          origCometOnFailure.apply(this, arguments);
        };

        var origCometOnSuccess = window.liftComet.lift_handlerSuccessFunc;
        window.liftComet.lift_handlerSuccessFunc = function () {
          clearServerCommError("comet");
          origCometOnSuccess.apply(this, arguments);
        };

      }

      net_liftmodules_ng.isInitialized = true;
    }
  }};

  window.net_liftmodules_ng = net_liftmodules_ng;
}).call(this);
<commit_msg>Fixing one of our checks that bombs in Lift 3<commit_after>angular
  .module("lift-ng", [])
  .service("plumbing", [ "$q", function($q){
    var defers = {};

    var create = function(id) {
      var q = $q.defer();
      defers[id] = q;
      return q;
    };

    var resolve = function(data, q) {
      if(data.msg) {
        q.reject(data.msg)
      } else {
        if (typeof data.data !== "undefined") {
          inject(data.data);
          q.resolve(data.data);
        }
        else {
          q.resolve();
        }
      }
      return q;
    };

    // Called by the LiftNgFutureActor when a Future is fulfilled
    var fulfill = function(data, id) {
      var theId = id || data.id;
      var q = defers[theId];
      if(typeof q !== "undefined" && q !== null) { // We found our awaiting defer/promise
        resolve(data, q);
        delete defers[theId];
      } else { // We arrived before the model which embeds us!
        resolve(data, create(theId));
      }
    };

    // Called to inject promises wherever our serializer encountered a Future
    var inject = function(model) {
      for(var k in model) {
        if(model[k] == null) {
          // Don't do anything, skip
        }
        // It is a future which we need to inject
        else if(model[k]["net.liftmodules.ng.Angular.future"]) {
          var id   = model[k]["net.liftmodules.ng.Angular.future"];
          var data = model[k].data;
          var msg  = model[k].msg;
          var q = defers[id];
          if(typeof q !== "undefined" && q !== null) { // The future resolved before we arrived here
            model[k] = q.promise;
            delete defers[id];
          } else if(data) { // The future had already RESOLVED at serialization time
            var d = $q.defer();
            d.resolve(data);
            model[k] = d.promise;
          } else if(msg) { // The future had already FAILED at serialization time
            var d = $q.defer();
            d.reject(msg);
            model[k] = d.promise;
          } else { // Promise/Future pending
            model[k] = create(id).promise;
          }
        }
        // Not a future, so check children
        else if(typeof model[k] === "object") {
          inject(model[k]);
        }
      }
    };

    return {
      createDefer: create,
      resolve: resolve,
      fulfill: fulfill,
      inject: inject
    }
  }])
  .service("liftProxy", ["$rootScope", "$q", "plumbing", function ($rootScope, $q, plumbing) {
    window.net_liftmodules_ng.init();

    var onServerCommError = function(count, which, req) {
      $rootScope.$emit("net_liftmodules_ng.serverCommError", count, which, req);
    };

    var onServerCommErrorClear = function(which) {
      $rootScope.$emit("net_liftmodules_ng.serverCommErrorClear", which);
    };

    window.net_liftmodules_ng.onServerCommErrorClearCallbacks.push(onServerCommErrorClear);

    var onCometError = function(count) {
      $rootScope.$apply(function(){
        onServerCommError(count, "comet");
      });
    };

    window.net_liftmodules_ng.onServerCommErrorCallbacks.push(onCometError);

    var onErrorFor = function(req) { return function(count) {
      onServerCommError(count, "ajax", req);
    }};

    var toData = function (requestData) {
      return requestData.name + "=" + encodeURIComponent(JSON.stringify({data: requestData.data}))
    };
    var toEnhancedReq = function (requestData) { return {
      data: toData(requestData),
      when: (new Date()).getTime(),
      onError: onErrorFor(requestData)
    }};

    var requestFor;
    if(window.net_liftmodules_ng.enhancedAjax) requestFor = toEnhancedReq;
    else requestFor = toData;

    var svc = {
      request: function (requestData) {
        var req = requestFor(requestData);
        var defer = $q.defer();

        var onSuccess = function(response) { $rootScope.$apply(function(){ // Must work under the watchful eye of angular
          // If there is no future ID, then we have our data and we're done.
          if(!response.futureId) {
            plumbing.resolve(response, defer);
          }

          // Otherwise, we need to plumb out a new promise because we'll get the value later.
          else {
            plumbing.createDefer(response.futureId).promise.then(
              function(data)  { defer.resolve(data)  },
              function(error) { defer.reject(error)  },
              function(notify){ defer.notify(notify) }
            )
          }
        })};

        var onFailure = function() { $rootScope.$apply(function() {
          defer.reject("net.liftmodules.ng.Angular.ajaxErrorRetryExceeded");
        })};

        window.net_liftmodules_ng.ajax(req, onSuccess, onFailure, "json");

        return defer.promise;
      }
    };

    return svc;
  }
]);

(function(){
  var net_liftmodules_ng = window.net_liftmodules_ng || {};
  // Careful to only init once, since it's possible to have multiple angular apps in one page
  net_liftmodules_ng.isInitialized = false;
  // Using an array of callbacks to handle case of multiple angular apps
  net_liftmodules_ng.onServerCommErrorCallbacks = [];
  net_liftmodules_ng.onServerCommErrorClearCallbacks = [];
  net_liftmodules_ng.init = function() { if(net_liftmodules_ng.enhancedAjax) { // Remove this condition once we can support Lift 3.x
    if(!net_liftmodules_ng.isInitialized) {
      var serverCommErrorCount = 0;
      var clearServerCommError = function (which) {
        if (serverCommErrorCount != 0) {
          serverCommErrorCount = 0;
          for (var i = 0; i < net_liftmodules_ng.onServerCommErrorClearCallbacks.length; i++)
            net_liftmodules_ng.onServerCommErrorClearCallbacks[i](which);
        }
      };

      if (window.liftAjax) {
        // We've passed {data, when} to the ajax lift machinery, so we need to pull the data part back out.
        var onlyData = function (req) {
          // This check prevents us from screwing up any non-lift-ng ajax calls someone could possibly be making.
          if (typeof req === "object") return req.data;
          else return req;
        };

        var ajaxFailureWrapper = function (req, onFailure) {
          return function () {
            // Note that we _could_ count all failures. However, in the unlikely event that someone is doing ajax
            // outside of lift-ng, failures can also be regular ol' Exceptions in business code which are not any
            // indication of our ability to communicate with the server via lift-ng.
            if (typeof req === "object" && typeof req.onError === "function")
              req.onError(++serverCommErrorCount);
            onFailure.apply(this, arguments); // We know lift always passes a failure cb function
          }
        };

        var ajaxSuccessWrapper = function (onSuccess) {
          return function () {
            // Although we are not counting non-lift-ng failures, we will clear any time we successfully communicate with
            // the server for any reason.
            clearServerCommError("ajax");
            onSuccess.apply(this, arguments); // We know lift always passes a success cb function
          }
        };

        // Wrap the json call with our hooks in place
        var origAjax = window.liftAjax.lift_actualJSONCall;
        window.liftAjax.lift_actualJSONCall = function (req, onSuccess, onFailure) {
          return origAjax(onlyData(req), ajaxSuccessWrapper(onSuccess), ajaxFailureWrapper(req, onFailure));
        };

        // Override the sort function if we should retry ajax in order.
        if (net_liftmodules_ng.retryAjaxInOrder) {
          window.liftAjax.lift_ajaxQueueSort = function () {
            window.liftAjax.lift_ajaxQueue.sort(function (a, b) {
              // If both items are one of our doctored-up requests, grab our 'when' which is the original request time.
              if (typeof a.theData === "object" && a.theData.when && typeof b.theData === "object" && b.theData.when)
                return a.theData.when - b.theData.when;
              else // Not our stuff, so let's not screw around with the original order logic.
                return a.when - b.when;
            });
          };
        }
      }

      if (window.liftComet) {
        var origCometOnFailure = window.liftComet.lift_handlerFailureFunc;
        window.liftComet.lift_handlerFailureFunc = function () {
          serverCommErrorCount++;
          for (var i = 0; i < net_liftmodules_ng.onServerCommErrorCallbacks.length; i++)
            net_liftmodules_ng.onServerCommErrorCallbacks[i](serverCommErrorCount);
          origCometOnFailure.apply(this, arguments);
        };

        var origCometOnSuccess = window.liftComet.lift_handlerSuccessFunc;
        window.liftComet.lift_handlerSuccessFunc = function () {
          clearServerCommError("comet");
          origCometOnSuccess.apply(this, arguments);
        };

      }

      net_liftmodules_ng.isInitialized = true;
    }
  }};

  window.net_liftmodules_ng = net_liftmodules_ng;
}).call(this);
<|endoftext|>


<commit_before>/* jshint esversion: 6 */
/* jshint node: true */

'use strict';
var request = require('request-promise');
var accessTokenInfo = require('./common').accessTokenInfo;
var crypto = require('crypto');

class Onboarding {

    onboard(authInfo) {
        console.log("Onboarding Hue Hub");

        // this comes from the onboardFlow property
        // as part of the schema and manifest.xml
        var options = {
            url: "https://api.meethue.com/oauth2/token?" + 'code=' + authInfo[1] + '&grant_type=authorization_code',
            method: "POST",
            headers: {
                'cache-control': 'no-cache'
            },
            followAllRedirects: true,
        };

        return request(options)
            .then(function () {
                //Do Nothing
            })
            .catch(function (err) {
                if (err.statusCode == '401') {
                    //extract nonce code from header
                    var digestHeader = err.response.headers['www-authenticate'];
                    var nonce = digestHeader.substr(digestHeader.indexOf('nonce=\"') + 7, 32);

                    //Compute digest header response
                    var disgestHeaderContent = 'username=\"' + authInfo[0].client_id + '\", ';
                    disgestHeaderContent += 'realm=\"oauth2_client@api.meethue.com\", ';
                    disgestHeaderContent += 'nonce=\"' + nonce + '\", ';
                    disgestHeaderContent += 'uri=\"/oauth2/token\", ';
                    var HASH1 = crypto.createHash('md5').update(authInfo[0].client_id + ':oauth2_client@api.meethue.com:' + authInfo[0].client_secret).digest('hex');
                    var HASH2 = crypto.createHash('md5').update('POST:/oauth2/token').digest('hex');
                    var authHeaderResponse = crypto.createHash('md5').update(HASH1 + ':' + nonce + ':' + HASH2).digest('hex');
                    disgestHeaderContent += 'response=\"' + authHeaderResponse + '\"';

                    options.headers = {
                        'Accept': 'application/json',
                        'Authorization': 'Digest ' + disgestHeaderContent,
                    };

                    return request(options).then(function (body) {
                        var tokenInfo = JSON.parse(body); // This includes refresh token, scope etc..
                        var getOptions = {
                            url: 'https://api.meethue.com/v2/bridges',
                            method: "GET",
                            headers: {
                                'Authorization': 'Bearer ' + tokenInfo.access_token
                            },
                        };

                        console.log('Getting bridge ID associated to the account...');

                        return request(getOptions)
                            .then(function (body) {

                                //Get bridgeId
                                var bridgeIds = JSON.parse(body);
                                if (bridgeIds.length > 0) {

                                    console.log('Success.');

                                    var putOptions = {
                                        url: 'https://api.meethue.com/v2/bridges/' + bridgeIds[0].id + '/0/config',
                                        method: "PUT",
                                        headers: {
                                            'Authorization': 'Bearer ' + tokenInfo.access_token,
                                            'Content-Type': 'application/json'
                                        },
                                        json: { 'linkbutton': true }
                                    };

                                    console.log('Getting whitelist ID from bridge ' + bridgeIds[0].id + '...');

                                    return request(putOptions)
                                        .then(function (body) {
                                            //pressed link button
                                            if (typeof body[0] !== 'undefined' && typeof body[0].success !== 'undefined') {
                                                var postOptions = {
                                                    url: 'https://api.meethue.com/v2/bridges/' + bridgeIds[0].id + '/',
                                                    method: "POST",
                                                    headers: {
                                                        'Authorization': 'Bearer ' + tokenInfo.access_token,
                                                        'Content-Type': 'application/json'
                                                    },
                                                    json: { 'devicetype': authInfo[2] + ' on ' + authInfo[3] }
                                                };

                                                return request(postOptions)
                                                    .then(function (body) {
                                                        //Get whitelist ID
                                                        if (typeof body[0] !== 'undefined' && typeof body[0].success !== 'undefined') {

                                                            console.log('Success.');

                                                            return new accessTokenInfo(
                                                                tokenInfo.access_token,
                                                                tokenInfo.refresh_token,
                                                                tokenInfo.token_type,
                                                                bridgeIds[0].id,
                                                                body[0].success.username
                                                            );
                                                        } else {
                                                            var errMsg = {};
                                                            if(typeof body.error !== 'undefined') {
                                                                errMsg.statusCode = body.error.type;
                                                                errMsg.response = {
                                                                    statusMessage:  body.error.description
                                                                };
                                                            } else {
                                                                errMsg.statusCode = 500;
                                                                errMsg.response = {
                                                                    statusMessage: 'Internal Error - Failed to get whitelist ID.'
                                                                };
                                                            }
                                                            throw errMsg;
                                                        }
                                                    })
                                                    .catch(function (err) {
                                                        console.log("Request failed to: " + postOptions.method + " - " + postOptions.url);
                                                        console.log("Error            : " + err.statusCode + " - " + err.response.statusMessage);
                                                        throw err;
                                                    });
                                            } else {
                                                var errMsg = {};
                                                if(typeof body.error !== 'undefined'){
                                                    errMsg.statusCode = body.error.type;
                                                    errMsg.response = {
                                                        statusMessage:  body.error.description
                                                    };
                                                } else {
                                                    errMsg.statusCode = 500;
                                                    errMsg.response = {
                                                        statusMessage: 'Internal Error - Failed to set link button press.'
                                                    };
                                                }
                                                throw errMsg;
                                            }
                                        })
                                        .catch(function (err) {
                                            console.log("Request failed to: " + putOptions.method + " - " + putOptions.url);
                                            console.log("Error            : " + err.statusCode + " - " + err.response.statusMessage);
                                            throw err;
                                        });

                                } else {
                                    var errMsg =  {
                                        statusCode : "500",
                                        response : {
                                            statusMessage: 'Internal Error - No bridge is associated to the account. No bridge credential was set.'
                                        }
                                    };

                                    throw errMsg;
                                }
                            })
                            .catch(function (err) {
                                console.log("Request failed to: " + getOptions.method + " - " + getOptions.url);
                                console.log("Error            : " + err.statusCode + " - " + err.response.statusMessage);
                                throw err;
                            });
                    })
                        .catch(function (err) {
                            console.log("Request failed to: " + options.method + " - " + options.url);
                            console.log("Error            : " + err.statusCode + " - " + err.response.statusMessage);
                            throw err;
                        });
                } else {
                    console.log("Request failed to: " + options.method + " - " + options.url);
                    console.log("Error            : " + err.statusCode + " - " + err.response.statusMessage);
                    throw err;
                }
            });
    }
}

module.exports = Onboarding;
<commit_msg>Update to reflect the latest testConfig structure.<commit_after>/* jshint esversion: 6 */
/* jshint node: true */

'use strict';
var request = require('request-promise');
var accessTokenInfo = require('./common').accessTokenInfo;
var crypto = require('crypto');

class Onboarding {

    onboard(authInfo) {
        console.log("Onboarding Hue Hub");

        // this comes from the onboardFlow property
        // as part of the schema and manifest.xml
        var options = {
            url: "https://api.meethue.com/oauth2/token?" + 'code=' + authInfo[1] + '&grant_type=authorization_code',
            method: "POST",
            headers: {
                'cache-control': 'no-cache'
            },
            followAllRedirects: true,
        };

        return request(options)
            .then(function () {
                //Do Nothing
            })
            .catch(function (err) {
                if (err.statusCode == '401') {
                    //extract nonce code from header
                    var digestHeader = err.response.headers['www-authenticate'];
                    var nonce = digestHeader.substr(digestHeader.indexOf('nonce=\"') + 7, 32);

                    //Compute digest header response
                    var disgestHeaderContent = 'username=\"' + authInfo[0].client_id + '\", ';
                    disgestHeaderContent += 'realm=\"oauth2_client@api.meethue.com\", ';
                    disgestHeaderContent += 'nonce=\"' + nonce + '\", ';
                    disgestHeaderContent += 'uri=\"/oauth2/token\", ';
                    var HASH1 = crypto.createHash('md5').update(authInfo[0].client_id + ':oauth2_client@api.meethue.com:' + authInfo[0].client_secret).digest('hex');
                    var HASH2 = crypto.createHash('md5').update('POST:/oauth2/token').digest('hex');
                    var authHeaderResponse = crypto.createHash('md5').update(HASH1 + ':' + nonce + ':' + HASH2).digest('hex');
                    disgestHeaderContent += 'response=\"' + authHeaderResponse + '\"';

                    options.headers = {
                        'Accept': 'application/json',
                        'Authorization': 'Digest ' + disgestHeaderContent,
                    };

                    return request(options).then(function (body) {
                        var tokenInfo = JSON.parse(body); // This includes refresh token, scope etc..
                        var getOptions = {
                            url: 'https://api.meethue.com/v2/bridges',
                            method: "GET",
                            headers: {
                                'Authorization': 'Bearer ' + tokenInfo.access_token
                            },
                        };

                        console.log('Getting bridge ID associated to the account...');

                        return request(getOptions)
                            .then(function (body) {

                                //Get bridgeId
                                var bridgeIds = JSON.parse(body);
                                if (bridgeIds.length > 0) {

                                    console.log('Success.');

                                    var putOptions = {
                                        url: 'https://api.meethue.com/v2/bridges/' + bridgeIds[0].id + '/0/config',
                                        method: "PUT",
                                        headers: {
                                            'Authorization': 'Bearer ' + tokenInfo.access_token,
                                            'Content-Type': 'application/json'
                                        },
                                        json: { 'linkbutton': true }
                                    };

                                    console.log('Getting whitelist ID from bridge ' + bridgeIds[0].id + '...');

                                    return request(putOptions)
                                        .then(function (body) {
                                            //pressed link button
                                            if (typeof body[0] !== 'undefined' && typeof body[0].success !== 'undefined') {
                                                var postOptions = {
                                                    url: 'https://api.meethue.com/v2/bridges/' + bridgeIds[0].id + '/',
                                                    method: "POST",
                                                    headers: {
                                                        'Authorization': 'Bearer ' + tokenInfo.access_token,
                                                        'Content-Type': 'application/json'
                                                    },
                                                    json: { 'devicetype': authInfo[0].app_name + ' on ' + authInfo[0].device_id }
                                                };

                                                return request(postOptions)
                                                    .then(function (body) {
                                                        //Get whitelist ID
                                                        if (typeof body[0] !== 'undefined' && typeof body[0].success !== 'undefined') {

                                                            console.log('Success.');

                                                            return new accessTokenInfo(
                                                                tokenInfo.access_token,
                                                                tokenInfo.refresh_token,
                                                                tokenInfo.token_type,
                                                                bridgeIds[0].id,
                                                                body[0].success.username
                                                            );
                                                        } else {
                                                            var errMsg = {};
                                                            if(typeof body.error !== 'undefined') {
                                                                errMsg.statusCode = body.error.type;
                                                                errMsg.response = {
                                                                    statusMessage:  body.error.description
                                                                };
                                                            } else {
                                                                errMsg.statusCode = 500;
                                                                errMsg.response = {
                                                                    statusMessage: 'Internal Error - Failed to get whitelist ID.'
                                                                };
                                                            }
                                                            throw errMsg;
                                                        }
                                                    })
                                                    .catch(function (err) {
                                                        console.log("Request failed to: " + postOptions.method + " - " + postOptions.url);
                                                        console.log("Error            : " + err.statusCode + " - " + err.response.statusMessage);
                                                        throw err;
                                                    });
                                            } else {
                                                var errMsg = {};
                                                if(typeof body.error !== 'undefined'){
                                                    errMsg.statusCode = body.error.type;
                                                    errMsg.response = {
                                                        statusMessage:  body.error.description
                                                    };
                                                } else {
                                                    errMsg.statusCode = 500;
                                                    errMsg.response = {
                                                        statusMessage: 'Internal Error - Failed to set link button press.'
                                                    };
                                                }
                                                throw errMsg;
                                            }
                                        })
                                        .catch(function (err) {
                                            console.log("Request failed to: " + putOptions.method + " - " + putOptions.url);
                                            console.log("Error            : " + err.statusCode + " - " + err.response.statusMessage);
                                            throw err;
                                        });

                                } else {
                                    var errMsg =  {
                                        statusCode : "500",
                                        response : {
                                            statusMessage: 'Internal Error - No bridge is associated to the account. No bridge credential was set.'
                                        }
                                    };

                                    throw errMsg;
                                }
                            })
                            .catch(function (err) {
                                console.log("Request failed to: " + getOptions.method + " - " + getOptions.url);
                                console.log("Error            : " + err.statusCode + " - " + err.response.statusMessage);
                                throw err;
                            });
                    })
                        .catch(function (err) {
                            console.log("Request failed to: " + options.method + " - " + options.url);
                            console.log("Error            : " + err.statusCode + " - " + err.response.statusMessage);
                            throw err;
                        });
                } else {
                    console.log("Request failed to: " + options.method + " - " + options.url);
                    console.log("Error            : " + err.statusCode + " - " + err.response.statusMessage);
                    throw err;
                }
            });
    }
}

module.exports = Onboarding;
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016 Maxwell Dreytser
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

// If you would like IE 8 support, include the polyfill here: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Compatibility
/**
 * Adds a speech bubble above or below the specified element.
 * @param {HTMLElement} element - Element that the
 * @param {string} content - What the bubble will contain (html is ok).
 * @param {string} [additionalCSSClasses] The CSS class to add to the element.
 * @param {HTMLElement} [appendToElement=body] The element to which the SpeechBubble will actually be appended to. By default this is the `body` element.
 * @returns {HTMLElement} The Speech Bubble element.
 */
window.SpeechBubble = function(element, content, additionalCSSClasses, appendToElement){
	// http://stackoverflow.com/a/34014786/1610754 and http://stackoverflow.com/a/442474/1610754
	function getOffset(elm) {
		// Find the offset of elm from the body or html element
		var _x = 0;
		var _y = 0;
		while( elm && !isNaN( elm.offsetLeft ) && !isNaN( elm.offsetTop ) )
		{
			_x += elm.offsetLeft - elm.scrollLeft + elm.clientLeft;
			_y += elm.offsetTop - elm.scrollTop + elm.clientTop;
			elm = elm.offsetParent;
		}
		return { top: _y, left: _x };
	}

	var SpeechDiv = document.createElement('div');
	if (additionalCSSClasses) {
		SpeechDiv.className = 'speech-bubble-main speech-bubble-top ' + additionalCSSClasses;
	} else {
		SpeechDiv.className = 'speech-bubble-main speech-bubble-top';
	}
	SpeechDiv.innerHTML = content;
	var SpeechID;
	do {
		SpeechID='bubble-' + new Date().getTime();
	} while (document.getElementById(SpeechID));
	SpeechDiv.id = SpeechID;
	SpeechDiv.setAttribute("style", "");
	(appendToElement || document.getElementsByTagName('body')[0]).appendChild(SpeechDiv);

	var SpeechStyle = document.createElement('style');
	document.getElementsByTagName('body')[0].appendChild(SpeechStyle);

	function DoAutoPosition() {
		var elmXY = getOffset(element);

		var speechX;
		var speechY;

		var elmWidth = (element.clientWidth || element.offsetWidth);
		var elmHeight = (element.clientHeight || element.offsetHeight);

		if (elmXY.top + elmHeight + SpeechDiv.clientHeight + 23 > (window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight)) {
			var appliedClasses = SpeechDiv.className.split(' ');
			for ( var i = 0; i < appliedClasses.length; i++ ) {
				if (appliedClasses[i] == 'speech-bubble-top') {
					appliedClasses[i] = 'speech-bubble-bottom';
				}
			}
			SpeechDiv.className = appliedClasses.join(' ');

			speechY = elmXY.top - SpeechDiv.clientHeight - 23;
		} else {
			var appliedClasses = SpeechDiv.className.split(' ');
			for ( var i = 0; i < appliedClasses.length; i++ ) {
				if (appliedClasses[i] == 'speech-bubble-bottom') {
					appliedClasses[i] = 'speech-bubble-top';
				}
			}
			SpeechDiv.className = appliedClasses.join(' ');

			speechY = elmXY.top + elmHeight + 12;
		}

		if (SpeechDiv.clientWidth < element.clientWidth)
			speechX = elmXY.left + (element.clientWidth - SpeechDiv.clientWidth) / 2;
		else
			speechX = elmXY.left;

		SpeechStyle.innerHTML =
			'#' + SpeechDiv.id + ' {' +
			'  left: ' + speechX + 'px;' +
			'  top: ' + speechY + 'px;' +
			'}' +
			'#' + SpeechDiv.id + ':before {';
		if (SpeechDiv.clientWidth < elmWidth)
			SpeechStyle.innerHTML += '  left: ' + ((SpeechDiv.clientWidth - 20) / 2 - 7) + 'px';
		else
			SpeechStyle.innerHTML += '  left: ' + (elmWidth / 2 - 27) + 'px;';
		SpeechStyle.innerHTML +=
			'}' +
			'#' + SpeechDiv.id + ':after {';
		if (SpeechDiv.clientWidth < elmWidth)
			SpeechStyle.innerHTML += '  left: ' + ((SpeechDiv.clientWidth - 20) / 2) + 'px';
		else
			SpeechStyle.innerHTML += '  left: ' + (elmWidth / 2 - 20) + 'px;';
		SpeechStyle.innerHTML += '}';
	}

	DoAutoPosition();

	window.addEventListener('resize', function(){
		DoAutoPosition();
	}, true);

	SpeechDiv.removeBubble = function() {
		SpeechStyle.parentNode.removeChild(SpeechStyle);
		SpeechDiv.parentNode.removeChild(SpeechDiv);

		for ( var i = 0; i < OpenSpeechBubbles.length; i++ ) {
			if (OpenSpeechBubbles[i] == SpeechDiv) {
				OpenSpeechBubbles.splice(i, 1);
			}
		}
	};

	OpenSpeechBubbles.push(SpeechDiv);

	return SpeechDiv;
};

window.OpenSpeechBubbles = [];<commit_msg>If for whatever reason we get jQuery objects instead of actual elements, just take the first element in the object.<commit_after>/*
 * Copyright (c) 2016 Maxwell Dreytser
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

// If you would like IE 8 support, include the polyfill here: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Compatibility
/**
 * Adds a speech bubble above or below the specified element.
 * @param {HTMLElement} targetElement - Element that the
 * @param {string} content - What the bubble will contain (html is ok).
 * @param {string} [additionalCSSClasses] The CSS class to add to the element.
 * @param {HTMLElement} [appendToElement=body] The element to which the SpeechBubble will actually be appended to. By default this is the `body` element.
 * @returns {HTMLElement} The Speech Bubble element.
 */
window.SpeechBubble = function(targetElement, content, additionalCSSClasses, appendToElement){
	// http://stackoverflow.com/a/34014786/1610754 and http://stackoverflow.com/a/442474/1610754
	function getOffset(elm) {
		// Find the offset of elm from the body or html element
		var _x = 0;
		var _y = 0;
		while( elm && !isNaN( elm.offsetLeft ) && !isNaN( elm.offsetTop ) )
		{
			_x += elm.offsetLeft - elm.scrollLeft + elm.clientLeft;
			_y += elm.offsetTop - elm.scrollTop + elm.clientTop;
			elm = elm.offsetParent;
		}
		return { top: _y, left: _x };
	}

	var element;
	if (jQuery && targetElement instanceof jQuery) {
		element = targetElement[0];
	} else {
		element = targetElement;
	}
	var appendElm;
	if (jQuery && appendToElement instanceof jQuery) {
		appendElm = appendToElement[0];
	} else {
		appendElm = appendToElement;
	}

	var SpeechDiv = document.createElement('div');
	if (additionalCSSClasses) {
		SpeechDiv.className = 'speech-bubble-main speech-bubble-top ' + additionalCSSClasses;
	} else {
		SpeechDiv.className = 'speech-bubble-main speech-bubble-top';
	}
	SpeechDiv.innerHTML = content;
	var SpeechID;
	do {
		SpeechID='bubble-' + new Date().getTime();
	} while (document.getElementById(SpeechID));
	SpeechDiv.id = SpeechID;
	SpeechDiv.setAttribute("style", "");
	(appendElm || document.getElementsByTagName('body')[0]).appendChild(SpeechDiv);

	var SpeechStyle = document.createElement('style');
	document.getElementsByTagName('body')[0].appendChild(SpeechStyle);

	function DoAutoPosition() {
		var elmXY = getOffset(element);

		var speechX;
		var speechY;

		var elmWidth = (element.clientWidth || element.offsetWidth);
		var elmHeight = (element.clientHeight || element.offsetHeight);

		if (elmXY.top + elmHeight + SpeechDiv.clientHeight + 23 > (window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight)) {
			var appliedClasses = SpeechDiv.className.split(' ');
			for ( var i = 0; i < appliedClasses.length; i++ ) {
				if (appliedClasses[i] == 'speech-bubble-top') {
					appliedClasses[i] = 'speech-bubble-bottom';
				}
			}
			SpeechDiv.className = appliedClasses.join(' ');

			speechY = elmXY.top - SpeechDiv.clientHeight - 23;
		} else {
			var appliedClasses = SpeechDiv.className.split(' ');
			for ( var i = 0; i < appliedClasses.length; i++ ) {
				if (appliedClasses[i] == 'speech-bubble-bottom') {
					appliedClasses[i] = 'speech-bubble-top';
				}
			}
			SpeechDiv.className = appliedClasses.join(' ');

			speechY = elmXY.top + elmHeight + 12;
		}

		if (SpeechDiv.clientWidth < element.clientWidth)
			speechX = elmXY.left + (element.clientWidth - SpeechDiv.clientWidth) / 2;
		else
			speechX = elmXY.left;

		SpeechStyle.innerHTML =
			'#' + SpeechDiv.id + ' {' +
			'  left: ' + speechX + 'px;' +
			'  top: ' + speechY + 'px;' +
			'}' +
			'#' + SpeechDiv.id + ':before {';
		if (SpeechDiv.clientWidth < elmWidth)
			SpeechStyle.innerHTML += '  left: ' + ((SpeechDiv.clientWidth - 20) / 2 - 7) + 'px';
		else
			SpeechStyle.innerHTML += '  left: ' + (elmWidth / 2 - 27) + 'px;';
		SpeechStyle.innerHTML +=
			'}' +
			'#' + SpeechDiv.id + ':after {';
		if (SpeechDiv.clientWidth < elmWidth)
			SpeechStyle.innerHTML += '  left: ' + ((SpeechDiv.clientWidth - 20) / 2) + 'px';
		else
			SpeechStyle.innerHTML += '  left: ' + (elmWidth / 2 - 20) + 'px;';
		SpeechStyle.innerHTML += '}';
	}

	DoAutoPosition();

	window.addEventListener('resize', function(){
		DoAutoPosition();
	}, true);

	SpeechDiv.removeBubble = function() {
		SpeechStyle.parentNode.removeChild(SpeechStyle);
		SpeechDiv.parentNode.removeChild(SpeechDiv);

		for ( var i = 0; i < OpenSpeechBubbles.length; i++ ) {
			if (OpenSpeechBubbles[i] == SpeechDiv) {
				OpenSpeechBubbles.splice(i, 1);
			}
		}
	};

	OpenSpeechBubbles.push(SpeechDiv);

	return SpeechDiv;
};

window.OpenSpeechBubbles = [];<|endoftext|>