<commit_before>define([
    'jquery',
    'underscore',
    'XMLWriter',
    'vellum/util'
], function (
    $,
    _,
    XMLWriter,
    util
) {
    var createXForm = function (form) {
        var xmlWriter = new XMLWriter( 'UTF-8', '1.0' );
        form.vellum.beforeSerialize();

        xmlWriter.writeStartDocument();
        //Generate header boilerplate up to instance level
        xmlWriter.writeStartElement('h:html');
        write_html_tag_boilerplate(xmlWriter);
        xmlWriter.writeStartElement('h:head');
        xmlWriter.writeStartElement('h:title');
        xmlWriter.writeString(form.formName);
        xmlWriter.writeEndElement();       //CLOSE TITLE

        xmlWriter.writeStartElement('model');
        xmlWriter.writeStartElement('instance');
        _writeInstanceAttributes(xmlWriter, form.instanceMetadata[0]);

        createDataBlock(form, xmlWriter);
        xmlWriter.writeEndElement(); //CLOSE MAIN INSTANCE

        // other instances
        for (var i = 1; i < form.instanceMetadata.length; i++) {
            _writeInstance(xmlWriter, form.instanceMetadata[i], true);
        }

        createBindList(form, xmlWriter);

        _.each(form.getSetValues(), function (setValue) {
            xmlWriter.writeStartElement('setvalue');
            xmlWriter.writeAttributeString('event', setValue.event);
            xmlWriter.writeAttributeString('ref', setValue.ref);
            xmlWriter.writeAttributeString('value', setValue.value);
            xmlWriter.writeEndElement();
        });

        form.vellum.contributeToModelXML(xmlWriter);

        xmlWriter.writeEndElement(); //CLOSE MODEL

        form.vellum.contributeToHeadXML(xmlWriter, form);

        xmlWriter.writeEndElement(); //CLOSE HEAD

        xmlWriter.writeStartElement('h:body');
        createControlBlock(form, xmlWriter);
        xmlWriter.writeEndElement(); //CLOSE BODY
        xmlWriter.writeEndElement(); //CLOSE HTML
        xmlWriter.writeEndDocument(); //CLOSE DOCUMENT

        var ret = xmlWriter.flush();

        return ret;
    };

    var createModelHeader = function (form, xmlWriter) {
        var uuid, uiVersion, version, formName, jrm;
        //assume we're currently pointed at the opening date block tag
        //e.g. <model><instance><data> <--- we're at <data> now.

        jrm = form.formJRM;
        if(!jrm) {
            jrm = "http://dev.commcarehq.org/jr/xforms";
        }

        uuid = form.formUuid; //gets set at parse time/by UI
        if(!uuid) {
            uuid = "http://openrosa.org/formdesigner/" + util.generate_xmlns_uuid();
        }

        xmlWriter.writeAttributeString("xmlns:jrm",jrm);
        xmlWriter.writeAttributeString("xmlns", uuid);
        xmlWriter.writeAttributeString("uiVersion", form.formUIVersion || "1");
        xmlWriter.writeAttributeString("version", form.formVersion || "1");
        xmlWriter.writeAttributeString("name", form.formName || "New Form");
    };

    function write_html_tag_boilerplate (xmlWriter) {
        xmlWriter.writeAttributeString( "xmlns:h", "http://www.w3.org/1999/xhtml" );
        xmlWriter.writeAttributeString( "xmlns:orx", "http://openrosa.org/jr/xforms" );
        xmlWriter.writeAttributeString( "xmlns", "http://www.w3.org/2002/xforms" );
        xmlWriter.writeAttributeString( "xmlns:xsd", "http://www.w3.org/2001/XMLSchema" );
        xmlWriter.writeAttributeString( "xmlns:jr", "http://openrosa.org/javarosa" );
        xmlWriter.writeAttributeString( "xmlns:vellum", "http://commcarehq.org/xforms/vellum" );
    }

    var _writeInstanceAttributes = function (writer, instanceMetadata) {
        for (var attrId in instanceMetadata.attributes) {
            if (instanceMetadata.attributes.hasOwnProperty(attrId)) {
                writer.writeAttributeString(attrId, instanceMetadata.attributes[attrId]);
            }
        }
    };

    var _writeInstance = function (writer, instanceMetadata, manualChildren) {
        writer.writeStartElement('instance');
        _writeInstanceAttributes(writer, instanceMetadata);
        if (manualChildren && instanceMetadata.children) {
            // seriously, this is what you have to do
            // HT: http://stackoverflow.com/questions/652763/jquery-object-to-string
            writer.writeXML($('<div>').append(instanceMetadata.children).clone().html());
        }
        writer.writeEndElement();
    };

    var createDataBlock = function (form, xmlWriter) {
        function mapFunc (node) {
            var mug = node.getValue();

            xmlWriter.writeStartElement(node.getID());

            if (node.isRootNode) {
                createModelHeader(form, xmlWriter);
            } else {
                var rawDataAttributes = mug.p.rawDataAttributes;
                // Write any custom attributes first
                for (var k in rawDataAttributes) {
                    if (rawDataAttributes.hasOwnProperty(k)) {
                        xmlWriter.writeAttributeString(k, rawDataAttributes[k]);
                    }
                }

                var dataValue = mug.p.dataValue,
                    keyAttr = mug.p.keyAttr,
                    xmlnsAttr = mug.p.xmlnsAttr;

                if (dataValue){
                    xmlWriter.writeString(dataValue);
                }
                if (keyAttr){
                    xmlWriter.writeAttributeString("key", keyAttr);
                }
                if (xmlnsAttr){
                    xmlWriter.writeAttributeString("xmlns", xmlnsAttr);
                }
                if (mug.__className === "Repeat"){
                    xmlWriter.writeAttributeString("jr:template","");
                }
            }
        }

        function afterFunc (node) {
            xmlWriter.writeEndElement();
        }

        form.dataTree.treeMap(mapFunc, afterFunc);
    };

    var createBindList = function (form, xmlWriter) {
        var bList = form.getBindList(),
            mug, i, attrs, j;

        function populateVariables (mug){
            var constraintMsgItextID = mug.p.constraintMsgItextID,
                constraintMsg;
            if (constraintMsgItextID && !constraintMsgItextID.isEmpty()) {
                constraintMsg = "jr:itext('" + constraintMsgItextID.id + "')";
            } else {
                constraintMsg = mug.p.constraintMsgAttr;
            }

            return {
                nodeset: form.getAbsolutePath(mug),
                type: mug.p.dataType,
                constraint: mug.p.constraintAttr,
                "jr:constraintMsg": constraintMsg,
                relevant: mug.p.relevantAttr,
                required: util.createXPathBoolFromJS(mug.p.requiredAttr),
                calculate: mug.p.calculateAttr,
                "jr:preload": mug.p.preload,
                "jr:preloadParams": mug.p.preloadParams
            };
        }

        for (i in bList) {
            if(bList.hasOwnProperty(i)){
                mug = bList[i];
                attrs = populateVariables(mug);
                if(attrs.nodeset){
                    xmlWriter.writeStartElement('bind');
                    for (j in attrs) {
                        if (attrs.hasOwnProperty(j) && attrs[j]) {
                            xmlWriter.writeAttributeString(j, attrs[j]);
                        }
                    }
                    _(mug.p.rawBindAttributes).each(function (v, k) {
                        if (!attrs.hasOwnProperty(k)) {
                            xmlWriter.writeAttributeString(k, v);
                        }
                    });
                    xmlWriter.writeEndElement();
                }
            }
        }
    };

    var createControlBlock = function (form, xmlWriter) {
        function mapFunc(node) {
            if(node.isRootNode) { //skip
                return;
            }

            var mug = node.getValue();

            if (mug.__className === "ReadOnly") {
                xmlWriter.writeXML($('<div>').append(mug.p.rawControlXML).clone().html());
                return;
            }
            var label, hasItext, isItextOptional;

            function createOpenControlTag(tagName, elLabel) {
                tagName = tagName.toLowerCase();
                var isGroupOrRepeat = (tagName === 'group' || tagName === 'repeat'),
                    isODKMedia = (tagName === 'upload');

                /**
                 * Creates the label tag inside of a control Element in the xform
                 */
                function createLabel() {
                    if (elLabel.ref || elLabel.defText) {
                        xmlWriter.writeStartElement('label');
                        if (elLabel.ref) {
                            xmlWriter.writeAttributeString('ref',elLabel.ref);
                        }
                        if (elLabel.defText) {
                            xmlWriter.writeString(elLabel.defText);
                        }
                        xmlWriter.writeEndElement(); //close Label tag;
                    }
                }

                // Special logic block to make sure the label ends up in the right place
                if (isGroupOrRepeat) {
                    xmlWriter.writeStartElement('group');
                    createLabel();
                    if (tagName === 'repeat') {
                        xmlWriter.writeStartElement('repeat');
                    }
                } else {
                    xmlWriter.writeStartElement(tagName);
                }
                if (tagName !== 'group' && tagName !== 'repeat' &&
                    tagName !== 'itemset')
                {
                    createLabel();
                }

                var defaultValue = mug.p.defaultValue;
                if (tagName === 'item' && defaultValue) {
                    //do a value tag for an item Mug
                    xmlWriter.writeStartElement('value');
                    xmlWriter.writeString(defaultValue);
                    xmlWriter.writeEndElement();
                }

                if (tagName === 'itemset') {
                    var data = mug.p.itemsetData;
                    xmlWriter.writeAttributeString(
                        'nodeset', data.getAttr('nodeset', ''));
                    xmlWriter.writeStartElement('label');
                    xmlWriter.writeAttributeString(
                        'ref', data.getAttr('labelRef', ''));
                    xmlWriter.writeEndElement();
                    xmlWriter.writeStartElement('value');
                    xmlWriter.writeAttributeString(
                        'ref', data.getAttr('valueRef', ''));
                    xmlWriter.writeEndElement();
                }

                // Write any custom attributes first
                var rawControlAttributes = mug.p.rawControlAttributes;
                for (var k in rawControlAttributes) {
                    if (rawControlAttributes.hasOwnProperty(k)) {
                        xmlWriter.writeAttributeString(k, rawControlAttributes[k]);
                    }
                }

                // Set the nodeset/ref attribute correctly
                if (tagName !== 'item' && tagName !== 'itemset') {
                    var attr, absPath;
                    if (tagName === 'repeat') {
                        attr = 'nodeset';
                    } else {
                        attr = 'ref';
                    }
                    absPath = form.getAbsolutePath(mug);
                    xmlWriter.writeAttributeString(attr, absPath);
                }

                // Set other relevant attributes

                if (tagName === 'repeat') {
                    var r_count = mug.p.repeat_count,
                        r_noaddrem = mug.p.no_add_remove;

                    //make r_noaddrem an XPath bool
                    r_noaddrem = util.createXPathBoolFromJS(r_noaddrem);

                    if (r_count) {
                        xmlWriter.writeAttributeString("jr:count",r_count);
                    }

                    if (r_noaddrem) {
                        xmlWriter.writeAttributeString("jr:noAddRemove", r_noaddrem);
                    }
                } else if (isODKMedia) {
                    var mediaType = mug.p.mediaType;
                    if (mediaType) {
                        xmlWriter.writeAttributeString("mediatype", mediaType);
                    }
                }

                var appearanceAttr = mug.getAppearanceAttribute();
                if (appearanceAttr) {
                    xmlWriter.writeAttributeString("appearance", appearanceAttr);
                }

                // Do hint label
                if( tagName !== 'item' && tagName !== 'repeat'){
                    var hintLabel = mug.p.hintLabel,
                        hintItextID = mug.p.hintItextID;
                    if(hintLabel || (hintItextID && hintItextID.id)) {
                        xmlWriter.writeStartElement('hint');
                        if(hintLabel){
                            xmlWriter.writeString(hintLabel);
                        }
                        if(hintItextID.id){
                            var ref = "jr:itext('" + hintItextID.id + "')";
                            xmlWriter.writeAttributeString('ref',ref);
                        }
                        xmlWriter.writeEndElement();
                    }
                }
            }

            //create the label object (for createOpenControlTag())
            if (mug.p.label) {
                label = {};
                label.defText = mug.p.label;
            }
            var labelItextID = mug.p.labelItextID;
            if (labelItextID) {
                if (!label) {
                    label = {};
                }

                label.ref = "jr:itext('" + labelItextID.id + "')";
                // iID is optional so by extension Itext is optional.
                isItextOptional = mug.spec.labelItextID.presence === 'optional';
                if (labelItextID.isEmpty() && isItextOptional) {
                    label.ref = '';
                }
            }

            createOpenControlTag(mug.p.tagName, label);
        }

        function afterFunc(node) {
            if (node.isRootNode) {
                return;
            }
            var mug = node.getValue();
            if (mug.__className === "ReadOnly") {
                return;
            }

            //finish off
            xmlWriter.writeEndElement(); //close control tag.
            if(mug.p.tagName === 'repeat'){
                xmlWriter.writeEndElement(); //special case where we have to close the repeat as well as the group tag.
            }
        }

        form.controlTree.treeMap(mapFunc, afterFunc);
    };

    return {
        createXForm: createXForm
    };
});
<commit_msg>Prevent integer and boolean values from killing XML writer<commit_after>define([
    'jquery',
    'underscore',
    'XMLWriter',
    'vellum/util'
], function (
    $,
    _,
    XMLWriter,
    util
) {
    var createXForm = function (form) {
        var xmlWriter = new XMLWriter( 'UTF-8', '1.0' );
        form.vellum.beforeSerialize();

        xmlWriter.writeStartDocument();
        //Generate header boilerplate up to instance level
        xmlWriter.writeStartElement('h:html');
        write_html_tag_boilerplate(xmlWriter);
        xmlWriter.writeStartElement('h:head');
        xmlWriter.writeStartElement('h:title');
        xmlWriter.writeString(form.formName);
        xmlWriter.writeEndElement();       //CLOSE TITLE

        xmlWriter.writeStartElement('model');
        xmlWriter.writeStartElement('instance');
        _writeInstanceAttributes(xmlWriter, form.instanceMetadata[0]);

        createDataBlock(form, xmlWriter);
        xmlWriter.writeEndElement(); //CLOSE MAIN INSTANCE

        // other instances
        for (var i = 1; i < form.instanceMetadata.length; i++) {
            _writeInstance(xmlWriter, form.instanceMetadata[i], true);
        }

        createBindList(form, xmlWriter);

        _.each(form.getSetValues(), function (setValue) {
            xmlWriter.writeStartElement('setvalue');
            xmlWriter.writeAttributeString('event', setValue.event);
            xmlWriter.writeAttributeString('ref', setValue.ref);
            xmlWriter.writeAttributeString('value', setValue.value);
            xmlWriter.writeEndElement();
        });

        form.vellum.contributeToModelXML(xmlWriter);

        xmlWriter.writeEndElement(); //CLOSE MODEL

        form.vellum.contributeToHeadXML(xmlWriter, form);

        xmlWriter.writeEndElement(); //CLOSE HEAD

        xmlWriter.writeStartElement('h:body');
        createControlBlock(form, xmlWriter);
        xmlWriter.writeEndElement(); //CLOSE BODY
        xmlWriter.writeEndElement(); //CLOSE HTML
        xmlWriter.writeEndDocument(); //CLOSE DOCUMENT

        var ret = xmlWriter.flush();

        return ret;
    };

    var createModelHeader = function (form, xmlWriter) {
        var uuid, uiVersion, version, formName, jrm;
        //assume we're currently pointed at the opening date block tag
        //e.g. <model><instance><data> <--- we're at <data> now.

        jrm = form.formJRM;
        if(!jrm) {
            jrm = "http://dev.commcarehq.org/jr/xforms";
        }

        uuid = form.formUuid; //gets set at parse time/by UI
        if(!uuid) {
            uuid = "http://openrosa.org/formdesigner/" + util.generate_xmlns_uuid();
        }

        xmlWriter.writeAttributeString("xmlns:jrm",jrm);
        xmlWriter.writeAttributeString("xmlns", uuid);
        xmlWriter.writeAttributeString("uiVersion", form.formUIVersion || "1");
        xmlWriter.writeAttributeString("version", form.formVersion || "1");
        xmlWriter.writeAttributeString("name", form.formName || "New Form");
    };

    function write_html_tag_boilerplate (xmlWriter) {
        xmlWriter.writeAttributeString( "xmlns:h", "http://www.w3.org/1999/xhtml" );
        xmlWriter.writeAttributeString( "xmlns:orx", "http://openrosa.org/jr/xforms" );
        xmlWriter.writeAttributeString( "xmlns", "http://www.w3.org/2002/xforms" );
        xmlWriter.writeAttributeString( "xmlns:xsd", "http://www.w3.org/2001/XMLSchema" );
        xmlWriter.writeAttributeString( "xmlns:jr", "http://openrosa.org/javarosa" );
        xmlWriter.writeAttributeString( "xmlns:vellum", "http://commcarehq.org/xforms/vellum" );
    }

    var _writeInstanceAttributes = function (writer, instanceMetadata) {
        for (var attrId in instanceMetadata.attributes) {
            if (instanceMetadata.attributes.hasOwnProperty(attrId)) {
                writer.writeAttributeString(attrId, instanceMetadata.attributes[attrId]);
            }
        }
    };

    var _writeInstance = function (writer, instanceMetadata, manualChildren) {
        writer.writeStartElement('instance');
        _writeInstanceAttributes(writer, instanceMetadata);
        if (manualChildren && instanceMetadata.children) {
            // seriously, this is what you have to do
            // HT: http://stackoverflow.com/questions/652763/jquery-object-to-string
            writer.writeXML($('<div>').append(instanceMetadata.children).clone().html());
        }
        writer.writeEndElement();
    };

    var createDataBlock = function (form, xmlWriter) {
        function mapFunc (node) {
            var mug = node.getValue();

            xmlWriter.writeStartElement(node.getID());

            if (node.isRootNode) {
                createModelHeader(form, xmlWriter);
            } else {
                var rawDataAttributes = mug.p.rawDataAttributes;
                // Write any custom attributes first
                for (var k in rawDataAttributes) {
                    if (rawDataAttributes.hasOwnProperty(k)) {
                        xmlWriter.writeAttributeString(k, rawDataAttributes[k]);
                    }
                }

                var dataValue = mug.p.dataValue,
                    keyAttr = mug.p.keyAttr,
                    xmlnsAttr = mug.p.xmlnsAttr;

                if (dataValue){
                    xmlWriter.writeString(dataValue);
                }
                if (keyAttr){
                    xmlWriter.writeAttributeString("key", keyAttr);
                }
                if (xmlnsAttr){
                    xmlWriter.writeAttributeString("xmlns", xmlnsAttr);
                }
                if (mug.__className === "Repeat"){
                    xmlWriter.writeAttributeString("jr:template","");
                }
            }
        }

        function afterFunc (node) {
            xmlWriter.writeEndElement();
        }

        form.dataTree.treeMap(mapFunc, afterFunc);
    };

    var createBindList = function (form, xmlWriter) {
        var bList = form.getBindList(),
            mug, i, attrs, j;

        function populateVariables (mug){
            var constraintMsgItextID = mug.p.constraintMsgItextID,
                constraintMsg;
            if (constraintMsgItextID && !constraintMsgItextID.isEmpty()) {
                constraintMsg = "jr:itext('" + constraintMsgItextID.id + "')";
            } else {
                constraintMsg = mug.p.constraintMsgAttr;
            }

            return {
                nodeset: form.getAbsolutePath(mug),
                type: mug.p.dataType,
                constraint: mug.p.constraintAttr,
                "jr:constraintMsg": constraintMsg,
                relevant: mug.p.relevantAttr,
                required: util.createXPathBoolFromJS(mug.p.requiredAttr),
                calculate: mug.p.calculateAttr,
                "jr:preload": mug.p.preload,
                "jr:preloadParams": mug.p.preloadParams
            };
        }

        for (i in bList) {
            if(bList.hasOwnProperty(i)){
                mug = bList[i];
                attrs = populateVariables(mug);
                if(attrs.nodeset){
                    xmlWriter.writeStartElement('bind');
                    for (j in attrs) {
                        if (attrs.hasOwnProperty(j) && attrs[j]) {
                            xmlWriter.writeAttributeString(j, attrs[j]);
                        }
                    }
                    _(mug.p.rawBindAttributes).each(function (v, k) {
                        if (!attrs.hasOwnProperty(k)) {
                            xmlWriter.writeAttributeString(k, v);
                        }
                    });
                    xmlWriter.writeEndElement();
                }
            }
        }
    };

    var createControlBlock = function (form, xmlWriter) {
        function mapFunc(node) {
            if(node.isRootNode) { //skip
                return;
            }

            var mug = node.getValue();

            if (mug.__className === "ReadOnly") {
                xmlWriter.writeXML($('<div>').append(mug.p.rawControlXML).clone().html());
                return;
            }
            var label, hasItext, isItextOptional;

            function createOpenControlTag(tagName, elLabel) {
                tagName = tagName.toLowerCase();
                var isGroupOrRepeat = (tagName === 'group' || tagName === 'repeat'),
                    isODKMedia = (tagName === 'upload');

                /**
                 * Creates the label tag inside of a control Element in the xform
                 */
                function createLabel() {
                    if (elLabel.ref || elLabel.defText) {
                        xmlWriter.writeStartElement('label');
                        if (elLabel.ref) {
                            xmlWriter.writeAttributeString('ref',elLabel.ref);
                        }
                        if (elLabel.defText) {
                            xmlWriter.writeString(elLabel.defText);
                        }
                        xmlWriter.writeEndElement(); //close Label tag;
                    }
                }

                // Special logic block to make sure the label ends up in the right place
                if (isGroupOrRepeat) {
                    xmlWriter.writeStartElement('group');
                    createLabel();
                    if (tagName === 'repeat') {
                        xmlWriter.writeStartElement('repeat');
                    }
                } else {
                    xmlWriter.writeStartElement(tagName);
                }
                if (tagName !== 'group' && tagName !== 'repeat' &&
                    tagName !== 'itemset')
                {
                    createLabel();
                }

                var defaultValue = mug.p.defaultValue;
                if (tagName === 'item' && defaultValue) {
                    //do a value tag for an item Mug
                    xmlWriter.writeStartElement('value');
                    xmlWriter.writeString(defaultValue);
                    xmlWriter.writeEndElement();
                }

                if (tagName === 'itemset') {
                    var data = mug.p.itemsetData;
                    xmlWriter.writeAttributeString(
                        'nodeset', data.getAttr('nodeset', ''));
                    xmlWriter.writeStartElement('label');
                    xmlWriter.writeAttributeString(
                        'ref', data.getAttr('labelRef', ''));
                    xmlWriter.writeEndElement();
                    xmlWriter.writeStartElement('value');
                    xmlWriter.writeAttributeString(
                        'ref', data.getAttr('valueRef', ''));
                    xmlWriter.writeEndElement();
                }

                // Write any custom attributes first
                var rawControlAttributes = mug.p.rawControlAttributes;
                for (var k in rawControlAttributes) {
                    if (rawControlAttributes.hasOwnProperty(k)) {
                        xmlWriter.writeAttributeString(k, rawControlAttributes[k]);
                    }
                }

                // Set the nodeset/ref attribute correctly
                if (tagName !== 'item' && tagName !== 'itemset') {
                    var attr, absPath;
                    if (tagName === 'repeat') {
                        attr = 'nodeset';
                    } else {
                        attr = 'ref';
                    }
                    absPath = form.getAbsolutePath(mug);
                    xmlWriter.writeAttributeString(attr, absPath);
                }

                // Set other relevant attributes

                if (tagName === 'repeat') {
                    var r_count = mug.p.repeat_count,
                        r_noaddrem = mug.p.no_add_remove;

                    //make r_noaddrem an XPath bool
                    r_noaddrem = util.createXPathBoolFromJS(r_noaddrem);

                    if (r_count) {
                        xmlWriter.writeAttributeString("jr:count", String(r_count));
                    }

                    if (r_noaddrem) {
                        xmlWriter.writeAttributeString("jr:noAddRemove", String(r_noaddrem));
                    }
                } else if (isODKMedia) {
                    var mediaType = mug.p.mediaType;
                    if (mediaType) {
                        xmlWriter.writeAttributeString("mediatype", mediaType);
                    }
                }

                var appearanceAttr = mug.getAppearanceAttribute();
                if (appearanceAttr) {
                    xmlWriter.writeAttributeString("appearance", appearanceAttr);
                }

                // Do hint label
                if( tagName !== 'item' && tagName !== 'repeat'){
                    var hintLabel = mug.p.hintLabel,
                        hintItextID = mug.p.hintItextID;
                    if(hintLabel || (hintItextID && hintItextID.id)) {
                        xmlWriter.writeStartElement('hint');
                        if(hintLabel){
                            xmlWriter.writeString(hintLabel);
                        }
                        if(hintItextID.id){
                            var ref = "jr:itext('" + hintItextID.id + "')";
                            xmlWriter.writeAttributeString('ref',ref);
                        }
                        xmlWriter.writeEndElement();
                    }
                }
            }

            //create the label object (for createOpenControlTag())
            if (mug.p.label) {
                label = {};
                label.defText = mug.p.label;
            }
            var labelItextID = mug.p.labelItextID;
            if (labelItextID) {
                if (!label) {
                    label = {};
                }

                label.ref = "jr:itext('" + labelItextID.id + "')";
                // iID is optional so by extension Itext is optional.
                isItextOptional = mug.spec.labelItextID.presence === 'optional';
                if (labelItextID.isEmpty() && isItextOptional) {
                    label.ref = '';
                }
            }

            createOpenControlTag(mug.p.tagName, label);
        }

        function afterFunc(node) {
            if (node.isRootNode) {
                return;
            }
            var mug = node.getValue();
            if (mug.__className === "ReadOnly") {
                return;
            }

            //finish off
            xmlWriter.writeEndElement(); //close control tag.
            if(mug.p.tagName === 'repeat'){
                xmlWriter.writeEndElement(); //special case where we have to close the repeat as well as the group tag.
            }
        }

        form.controlTree.treeMap(mapFunc, afterFunc);
    };

    return {
        createXForm: createXForm
    };
});
<|endoftext|>


<commit_before>(function(factory, global) {
  if (typeof define === 'function' && define.amd) {
    define(function() {
      return factory(global, navigator)
    })
  } else if (typeof exports === 'object' && typeof module === 'object') {
    module.exports = factory(global, navigator)
  } else {
    // mock the navigator object when under test since `navigator.onLine` is read only
    global.RobustWebSocket = factory(global, typeof Mocha !== 'undefined' ? Mocha : navigator)
  }
})(function(global, navigator) {

  var RobustWebSocket = function(url, protocols, userOptions) {
    var realWs = { close: function() {} },
        connectTimeout,
        self = this,
        attempts = 0,
        reconnects = -1,
        reconnectWhenOnlineAgain = false,
        explicitlyClosed = false,
        pendingReconnect,
        opts = Object.assign({},
          RobustWebSocket.defaultOptions,
          typeof userOptions === 'function' ? { shouldReconnect: userOptions } : userOptions
        )

    if (typeof opts.timeout !== 'number') {
      throw new Error('timeout must be the number of milliseconds to timeout a connection attempt')
    }

    if (typeof opts.shouldReconnect !== 'function') {
      throw new Error('shouldReconnect must be a function that returns the number of milliseconds to wait for a reconnect attempt, or null or undefined to not reconnect.')
    }

    ['bufferedAmount', 'url', 'readyState', 'protocol', 'extensions'].forEach(function(readOnlyProp) {
      Object.defineProperty(self, readOnlyProp, {
        get: function() { return realWs[readOnlyProp] }
      })
    })

    function clearPendingReconnectIfNeeded() {
      if (pendingReconnect) {
        pendingReconnect = null
        clearTimeout(pendingReconnect)
      }
    }

    var ononline = function(event) {
      if (reconnectWhenOnlineAgain) {
        clearPendingReconnectIfNeeded()
        reconnect(event)
      }
    },
    onoffline = function() {
      reconnectWhenOnlineAgain = true
      realWs.close(1000)
    },
    connectivityEventsAttached = false

    function detachConnectivityEvents() {
      if (connectivityEventsAttached) {
        global.removeEventListener('online', ononline)
        global.removeEventListener('offline', onoffline)
        connectivityEventsAttached = false
      }
    }

    function attachConnectivityEvents() {
      if (!connectivityEventsAttached) {
        global.addEventListener('online', ononline)
        global.addEventListener('offline', onoffline)
        connectivityEventsAttached = true
      }
    }

    self.send = function() {
      return realWs.send.apply(realWs, arguments)
    }

    self.close = function(code, reason) {
      if (typeof code !== 'number') {
        reason = code
        code = 1000
      }

      clearPendingReconnectIfNeeded()
      reconnectWhenOnlineAgain = false
      explicitlyClosed = true
      detachConnectivityEvents()

      return realWs.close(code, reason)
    }

    self.open = function() {
      if (realWs.readyState !== WebSocket.OPEN && realWs.readyState !== WebSocket.CONNECTING) {
        clearPendingReconnectIfNeeded()
        reconnectWhenOnlineAgain = false
        explicitlyClosed = false

        newWebSocket()
      }
    }

    function reconnect(event) {
      if (event.code === 1000 || explicitlyClosed) {
        attempts = 0
        return
      }
      if (navigator.onLine === false) {
        reconnectWhenOnlineAgain = true
        return
      }

      var delay = opts.shouldReconnect(event, self)
      if (typeof delay === 'number') {
        pendingReconnect = setTimeout(newWebSocket, delay)
      }
    }

    Object.defineProperty(self, 'listeners', {
      value: {
        open: [function(event) {
          if (connectTimeout) {
            clearTimeout(connectTimeout)
            connectTimeout = null
          }
          event.reconnects = ++reconnects
          event.attempts = attempts
          attempts = 0
          reconnectWhenOnlineAgain = false
        }],
        close: [reconnect]
      }
    })

    Object.defineProperty(self, 'attempts', {
      get: function() { return attempts },
      enumerable: true
    })

    Object.defineProperty(self, 'reconnects', {
      get: function() { return reconnects },
      enumerable: true
    })

    function newWebSocket() {
      pendingReconnect = null
      realWs = new WebSocket(url, protocols)
      realWs.binaryType = self.binaryType

      attempts++
      self.dispatchEvent(Object.assign(new CustomEvent('connecting'), {
        attempts: attempts,
        reconnects: reconnects
      }))

      connectTimeout = setTimeout(function() {
        connectTimeout = null
        detachConnectivityEvents()
        self.dispatchEvent(Object.assign(new CustomEvent('timeout'), {
          attempts: attempts,
          reconnects: reconnects
        }))
      }, opts.timeout)

      ;['open', 'close', 'message', 'error'].forEach(function(stdEvent) {
        realWs.addEventListener(stdEvent, function(event) {
          self.dispatchEvent(event)

          var cb = self['on' + stdEvent]
          if (typeof cb === 'function') {
            return cb.apply(self, arguments)
          }
        })
      })

      attachConnectivityEvents()
    }

    if (opts.automaticOpen) {
      newWebSocket()
    }
  }

  RobustWebSocket.defaultOptions = {
    // the time to wait before a successful connection
    // before the attempt is considered to have timed out
    timeout: 4000,
    // Given a CloseEvent or OnlineEvent and the RobustWebSocket state,
    // should a reconnect be attempted? Return the number of milliseconds to wait
    // to reconnect (or null or undefined to not), rather than true or false
    shouldReconnect: function(event, ws) {
      if (event.code === 1008 || event.code === 1011) return
      return [0, 3000, 10000][ws.attempts]
    },

    // Create and connect the WebSocket when the instance is instantiated.
    // Defaults to true to match standard WebSocket behavior
    automaticOpen: true
  }

  RobustWebSocket.prototype.binaryType = 'blob'

  // Taken from MDN https://developer.mozilla.org/en-US/docs/Web/API/EventTarget
  RobustWebSocket.prototype.addEventListener = function(type, callback) {
    if (!(type in this.listeners)) {
      this.listeners[type] = []
    }
    this.listeners[type].push(callback)
  }

  RobustWebSocket.prototype.removeEventListener = function(type, callback) {
    if (!(type in this.listeners)) {
      return
    }
    var stack = this.listeners[type]
    for (var i = 0, l = stack.length; i < l; i++) {
      if (stack[i] === callback) {
        stack.splice(i, 1)
        return
      }
    }
  }

  RobustWebSocket.prototype.dispatchEvent = function(event) {
    if (!(event.type in this.listeners)) {
      return
    }
    event.currentTarget = this
    var stack = this.listeners[event.type]
    for (var i = 0, l = stack.length; i < l; i++) {
      stack[i].call(this, event)
    }
  }

  return RobustWebSocket
}, typeof window != 'undefined' ? window : (typeof global != 'undefined' ? global : this));
<commit_msg>Do not set currentTarget to the RobustWebSocket instance since it's read only. Fixes #10.<commit_after>(function(factory, global) {
  if (typeof define === 'function' && define.amd) {
    define(function() {
      return factory(global, navigator)
    })
  } else if (typeof exports === 'object' && typeof module === 'object') {
    module.exports = factory(global, navigator)
  } else {
    // mock the navigator object when under test since `navigator.onLine` is read only
    global.RobustWebSocket = factory(global, typeof Mocha !== 'undefined' ? Mocha : navigator)
  }
})(function(global, navigator) {

  var RobustWebSocket = function(url, protocols, userOptions) {
    var realWs = { close: function() {} },
        connectTimeout,
        self = this,
        attempts = 0,
        reconnects = -1,
        reconnectWhenOnlineAgain = false,
        explicitlyClosed = false,
        pendingReconnect,
        opts = Object.assign({},
          RobustWebSocket.defaultOptions,
          typeof userOptions === 'function' ? { shouldReconnect: userOptions } : userOptions
        )

    if (typeof opts.timeout !== 'number') {
      throw new Error('timeout must be the number of milliseconds to timeout a connection attempt')
    }

    if (typeof opts.shouldReconnect !== 'function') {
      throw new Error('shouldReconnect must be a function that returns the number of milliseconds to wait for a reconnect attempt, or null or undefined to not reconnect.')
    }

    ['bufferedAmount', 'url', 'readyState', 'protocol', 'extensions'].forEach(function(readOnlyProp) {
      Object.defineProperty(self, readOnlyProp, {
        get: function() { return realWs[readOnlyProp] }
      })
    })

    function clearPendingReconnectIfNeeded() {
      if (pendingReconnect) {
        pendingReconnect = null
        clearTimeout(pendingReconnect)
      }
    }

    var ononline = function(event) {
      if (reconnectWhenOnlineAgain) {
        clearPendingReconnectIfNeeded()
        reconnect(event)
      }
    },
    onoffline = function() {
      reconnectWhenOnlineAgain = true
      realWs.close(1000)
    },
    connectivityEventsAttached = false

    function detachConnectivityEvents() {
      if (connectivityEventsAttached) {
        global.removeEventListener('online', ononline)
        global.removeEventListener('offline', onoffline)
        connectivityEventsAttached = false
      }
    }

    function attachConnectivityEvents() {
      if (!connectivityEventsAttached) {
        global.addEventListener('online', ononline)
        global.addEventListener('offline', onoffline)
        connectivityEventsAttached = true
      }
    }

    self.send = function() {
      return realWs.send.apply(realWs, arguments)
    }

    self.close = function(code, reason) {
      if (typeof code !== 'number') {
        reason = code
        code = 1000
      }

      clearPendingReconnectIfNeeded()
      reconnectWhenOnlineAgain = false
      explicitlyClosed = true
      detachConnectivityEvents()

      return realWs.close(code, reason)
    }

    self.open = function() {
      if (realWs.readyState !== WebSocket.OPEN && realWs.readyState !== WebSocket.CONNECTING) {
        clearPendingReconnectIfNeeded()
        reconnectWhenOnlineAgain = false
        explicitlyClosed = false

        newWebSocket()
      }
    }

    function reconnect(event) {
      if (event.code === 1000 || explicitlyClosed) {
        attempts = 0
        return
      }
      if (navigator.onLine === false) {
        reconnectWhenOnlineAgain = true
        return
      }

      var delay = opts.shouldReconnect(event, self)
      if (typeof delay === 'number') {
        pendingReconnect = setTimeout(newWebSocket, delay)
      }
    }

    Object.defineProperty(self, 'listeners', {
      value: {
        open: [function(event) {
          if (connectTimeout) {
            clearTimeout(connectTimeout)
            connectTimeout = null
          }
          event.reconnects = ++reconnects
          event.attempts = attempts
          attempts = 0
          reconnectWhenOnlineAgain = false
        }],
        close: [reconnect]
      }
    })

    Object.defineProperty(self, 'attempts', {
      get: function() { return attempts },
      enumerable: true
    })

    Object.defineProperty(self, 'reconnects', {
      get: function() { return reconnects },
      enumerable: true
    })

    function newWebSocket() {
      pendingReconnect = null
      realWs = new WebSocket(url, protocols)
      realWs.binaryType = self.binaryType

      attempts++
      self.dispatchEvent(Object.assign(new CustomEvent('connecting'), {
        attempts: attempts,
        reconnects: reconnects
      }))

      connectTimeout = setTimeout(function() {
        connectTimeout = null
        detachConnectivityEvents()
        self.dispatchEvent(Object.assign(new CustomEvent('timeout'), {
          attempts: attempts,
          reconnects: reconnects
        }))
      }, opts.timeout)

      ;['open', 'close', 'message', 'error'].forEach(function(stdEvent) {
        realWs.addEventListener(stdEvent, function(event) {
          self.dispatchEvent(event)

          var cb = self['on' + stdEvent]
          if (typeof cb === 'function') {
            return cb.apply(self, arguments)
          }
        })
      })

      attachConnectivityEvents()
    }

    if (opts.automaticOpen) {
      newWebSocket()
    }
  }

  RobustWebSocket.defaultOptions = {
    // the time to wait before a successful connection
    // before the attempt is considered to have timed out
    timeout: 4000,
    // Given a CloseEvent or OnlineEvent and the RobustWebSocket state,
    // should a reconnect be attempted? Return the number of milliseconds to wait
    // to reconnect (or null or undefined to not), rather than true or false
    shouldReconnect: function(event, ws) {
      if (event.code === 1008 || event.code === 1011) return
      return [0, 3000, 10000][ws.attempts]
    },

    // Create and connect the WebSocket when the instance is instantiated.
    // Defaults to true to match standard WebSocket behavior
    automaticOpen: true
  }

  RobustWebSocket.prototype.binaryType = 'blob'

  // Taken from MDN https://developer.mozilla.org/en-US/docs/Web/API/EventTarget
  RobustWebSocket.prototype.addEventListener = function(type, callback) {
    if (!(type in this.listeners)) {
      this.listeners[type] = []
    }
    this.listeners[type].push(callback)
  }

  RobustWebSocket.prototype.removeEventListener = function(type, callback) {
    if (!(type in this.listeners)) {
      return
    }
    var stack = this.listeners[type]
    for (var i = 0, l = stack.length; i < l; i++) {
      if (stack[i] === callback) {
        stack.splice(i, 1)
        return
      }
    }
  }

  RobustWebSocket.prototype.dispatchEvent = function(event) {
    if (!(event.type in this.listeners)) {
      return
    }
    var stack = this.listeners[event.type]
    for (var i = 0, l = stack.length; i < l; i++) {
      stack[i].call(this, event)
    }
  }

  return RobustWebSocket
}, typeof window != 'undefined' ? window : (typeof global != 'undefined' ? global : this));
<|endoftext|>


<commit_before>// This is a manifest file that'll be compiled into application.js.
//
// Any JavaScript file within this directory can be referenced here using a relative path.
//
// You're free to add application-wide JavaScript to this file, but it's generally better
// to create separate JavaScript files as needed.
//
//= require jquery
//= require bootstrap
//= require libs/bootstrap-tagsinput
//= require libs/summernote
//= require libs/summernote-ko-KR
//= require libs/summernote-ext-video
//= require libs/summernote-ext-fontstyle
//= require libs/summernote-ext-codeblock
//= require libs/spin
//= require libs/jquery.spin
//= require libs/placeholder_polyfill.jquery
//= require libs/mustache
//= require libs/jquery.timeago
//= require libs/jquery.timeago-ko-KR
//= require apps/utils
//= require_self

if (typeof jQuery !== 'undefined') {
	(function($) {
        $('.timeago').timeago();

        $('[data-toggle="tooltip"]').tooltip();

        $.fn.spin.presets = {
            tiny: { lines: 8, length: 1, width: 2, radius: 4, color:'#56595c' },
            small: { lines: 11, length: 0, width: 3, radius: 10, shadow: true, hwaccel: true, color:'#56595c' },
            large: { lines: 10, length: 8, width: 4, radius: 8, color:'#56595c' }
        };

        $.ajaxSetup({
            statusCode: {
                401: function() {
                    location.href = contextPath+'/login/auth?redirectUrl='+encodedURL;
                }
            }
        });

        $('.sidebar-header').click(function() {
            if($('.sidebar').is('.open')) {
                $('.sidebar').removeClass('open');
                $('.sidebar-category-nav').removeClass('open');
            } else {
                $('.sidebar').addClass('open');
                $('.sidebar-category-nav').addClass('open');
            }
        });

        $('html, body').click(function(e) {
            if(!$(e.target).is('.sidebar-header  *, .nav-main *, .sidebar-category-nav *, #search-google-form *')) {
                $('.sidebar').removeClass('open');
                $('.sidebar-category-nav').removeClass('open');
            }

            if(!$(e.target).is('#search-google, #search-google *, #search-google-popover *')) {
                $('#search-google').popover('hide');
            }

            if(!$(e.target).is('#user-func, #user-func  *, #user-func-popover *')) {
                $('#user-func').popover('hide');
            }

            if(!$(e.target).is('#user-notification, #user-notification *, #notification-popover *')) {
                $('#user-notification').popover('hide');
            }
        });

        var onImagaUpload = function(files, editor, $editable) {

            var $form = $('.note-image-dialog .note-modal-form');

            $('').appendTo('body');

            $.imageUploaded = function(image) {
                editor.insertImage($editable, image);
            };

            $form.attr({
                enctype: 'multipart/form-data',
                target: 'imageUploadHandlerFrame',
                action: contextPath+'/file/image',
                method: 'post'
            });

            $form[0].submit();
        };

        $.extend($.summernote.options, {
            lang: 'ko-KR',
            height: 300,                  // set editable area's height
            tabsize: 2,                   // size of tab
            placeholder: '내용을 입력해 주세요.', // set editable area's placeholder text
            prettifyHtml: false,
            disableLinkTarget: true,
            // toolbar
            toolbar: [
                ['style', ['style']],
                ['font', ['bold', 'italic', 'underline', 'strikethrough', 'clear']],
                ['color', ['color']],
                ['para', ['ul', 'ol', 'table']],
                ['insert', ['codeblock', 'link', 'picture', 'video', 'hr']],
                ['view', ['fullscreen', 'codeview', 'help']]
            ],
            onImageUpload : onImagaUpload
        });

	})(jQuery);
}<commit_msg>remove summernote-ext-fontstyle<commit_after>// This is a manifest file that'll be compiled into application.js.
//
// Any JavaScript file within this directory can be referenced here using a relative path.
//
// You're free to add application-wide JavaScript to this file, but it's generally better
// to create separate JavaScript files as needed.
//
//= require jquery
//= require bootstrap
//= require libs/bootstrap-tagsinput
//= require libs/summernote
//= require libs/summernote-ko-KR
//= require libs/summernote-ext-video
//= require libs/summernote-ext-codeblock
//= require libs/spin
//= require libs/jquery.spin
//= require libs/placeholder_polyfill.jquery
//= require libs/mustache
//= require libs/jquery.timeago
//= require libs/jquery.timeago-ko-KR
//= require apps/utils
//= require_self

if (typeof jQuery !== 'undefined') {
	(function($) {
        $('.timeago').timeago();

        $('[data-toggle="tooltip"]').tooltip();

        $.fn.spin.presets = {
            tiny: { lines: 8, length: 1, width: 2, radius: 4, color:'#56595c' },
            small: { lines: 11, length: 0, width: 3, radius: 10, shadow: true, hwaccel: true, color:'#56595c' },
            large: { lines: 10, length: 8, width: 4, radius: 8, color:'#56595c' }
        };

        $.ajaxSetup({
            statusCode: {
                401: function() {
                    location.href = contextPath+'/login/auth?redirectUrl='+encodedURL;
                }
            }
        });

        $('.sidebar-header').click(function() {
            if($('.sidebar').is('.open')) {
                $('.sidebar').removeClass('open');
                $('.sidebar-category-nav').removeClass('open');
            } else {
                $('.sidebar').addClass('open');
                $('.sidebar-category-nav').addClass('open');
            }
        });

        $('html, body').click(function(e) {
            if(!$(e.target).is('.sidebar-header  *, .nav-main *, .sidebar-category-nav *, #search-google-form *')) {
                $('.sidebar').removeClass('open');
                $('.sidebar-category-nav').removeClass('open');
            }

            if(!$(e.target).is('#search-google, #search-google *, #search-google-popover *')) {
                $('#search-google').popover('hide');
            }

            if(!$(e.target).is('#user-func, #user-func  *, #user-func-popover *')) {
                $('#user-func').popover('hide');
            }

            if(!$(e.target).is('#user-notification, #user-notification *, #notification-popover *')) {
                $('#user-notification').popover('hide');
            }
        });

        var onImagaUpload = function(files, editor, $editable) {

            var $form = $('.note-image-dialog .note-modal-form');

            $('').appendTo('body');

            $.imageUploaded = function(image) {
                editor.insertImage($editable, image);
            };

            $form.attr({
                enctype: 'multipart/form-data',
                target: 'imageUploadHandlerFrame',
                action: contextPath+'/file/image',
                method: 'post'
            });

            $form[0].submit();
        };

        $.extend($.summernote.options, {
            lang: 'ko-KR',
            height: 300,                  // set editable area's height
            tabsize: 2,                   // size of tab
            placeholder: '내용을 입력해 주세요.', // set editable area's placeholder text
            prettifyHtml: false,
            disableLinkTarget: true,
            // toolbar
            toolbar: [
                ['style', ['style']],
                ['font', ['bold', 'italic', 'underline', 'strikethrough', 'clear']],
                ['color', ['color']],
                ['para', ['ul', 'ol', 'table']],
                ['insert', ['codeblock', 'link', 'picture', 'video', 'hr']],
                ['view', ['fullscreen', 'codeview', 'help']]
            ],
            onImageUpload : onImagaUpload
        });

	})(jQuery);
}<|endoftext|>


<commit_before>var Resource = require('./resource');
var util = require("util");
var _ = require("lodash");
var crypto = require('crypto');
var pruned = require('./json-pruned');
var fs = require('fs');
var request = require('request-promise');
var Promise = require("bluebird");

function Media(session, params) {
    Resource.apply(this, arguments);
}

util.inherits(Media, Resource);

module.exports = Media;
var Request = require('./request');
var Comment = require('./comment');
var Account = require('./account');
var Location = require('./location');
var Exceptions = require('./exceptions');
var camelKeys = require('camelcase-keys');

Media.prototype.parseParams = function (json) {
    var hash = camelKeys(json);
    var that = this;
    hash.commentCount = hash.commentsDisabled ? 0 : json.comment_count ;
    hash.webLink = "https://www.instagram.com/p/" + json.code + "/";
    hash.carouselMedia = [];
    if(_.isObject(json.location)) {
        var location = json.location;
        location.location = Object.create(json.location);
        location.title = location.name;
        location.subtitle = null;
        this.location = new Location(that.session, location);
    }
    if (json.media_type === 2) {
        hash.video = {
            versions: json.video_versions,
            hasAudio: json.has_audio,
            duration: json.video_duration
        }
    }
    if (json.media_type === 8 && _.isArray(json.carousel_media)) {
        hash.carouselMedia = _.map(json.carousel_media, function (medium) {
           return new Media(that.session, medium);
        });
    }
    if (_.isObject(json.caption))
        hash.caption = json.caption.text;
    hash.takenAt = parseInt(json.taken_at) * 1000;
    if (_.isObject(json.image_versions2)) {
        hash.images = json.image_versions2.candidates;
    } else if (_.isObject(json.carousel_media)) {
        hash.images = json.carousel_media.map(function(media) {
            return media.image_versions2.candidates
        })
    }
    if (_.isArray(json.video_versions))
        hash.videos = json.video_versions;
    this.previewComments = _.map(json.preview_comments, function(comment) {
        return new Comment(that.session, comment);
    });
    // backward compability
    this.comments = this.previewComments;
    this.account = new Account(that.session, json.user);
    return hash;
};

Media.prototype.getParams = function () {
    return _.extend(this._params, {
        account: this.account.params,
        comments: _.map(this.comments, 'params'),
        location: this.location ? this.location.params : {},
        carouselMedia:  _.map(this._params.carouselMedia, 'params')
    });
};

Media.getById = function (session, id) {
    return new Request(session)
        .setMethod('GET')
        .setResource('mediaInfo', {mediaId: id})
        .send()
        .then(function(json) {
            return new Media(session, json.items[0])
        })
};

Media.getByUrl = function(session, url){
    var self = this;
    return request({
        url: 'https://api.instagram.com/oembed/',
        qs:{url:url},
        json:true
    })
        .then(function (response) {
            return self.getById(session, response.media_id)
        })
        .catch(function (reason) {
            if(reason.error === 'No URL Match')throw new Exceptions.NotFoundError('No URL Match');
            else throw reason;
        })
};

Media.likers = function(session, mediaId) {
    return new Request(session)
        .setMethod('GET')
        .setResource('mediaLikes', {mediaId: mediaId})
        .send()
        .then(function(data) {
            return _.map(data.users, function (user) {
                return new Account(session, user);
            });
        });
};

Media.delete = function(session, mediaId) {
    return new Request(session)
        .setMethod('POST')
        .setResource('mediaDeletePhoto', {mediaId: mediaId})
        .setData({
            media_id: mediaId
        })
        .generateUUID()
        .signPayload()
        .send()
        .then(function (json) {
            if(json.did_delete) return;
            throw new Exceptions.RequestError({
                messaage: 'Not posible to delete medium!'
            })
        })
};

Media.edit = function(session, mediaId, caption, userTags) {
    var requestPayload = {
        media_id: mediaId,
        caption_text: caption
    };

    if (userTags) {
        requestPayload.usertags = JSON.stringify(userTags);
    }

    return new Request(session)
        .setMethod('POST')
        .setResource('mediaEdit', {mediaId: mediaId})
        .setData(requestPayload)
        .generateUUID()
        .signPayload()
        .send()
        .then(function (json) {
            if(json.media.caption_is_edited) {
                return new Media(session, json.media);
            }
            throw new Exceptions.RequestError({
                messaage: 'Edit media not successful!'
            });
        });
};

Media.configurePhoto = function (session, uploadId, caption, width, height, userTags) {
    if(_.isEmpty(uploadId))
        throw new Error("Upload argument must be upload valid upload id");
    if(!caption) caption = "";
    if(!width) width = 800;
    if(!height) height = 800;
    if (!userTags) userTags = {};

    const CROP = 1;
    return session.getAccountId()
        .then(function(accountId){
            var payload = pruned({
                source_type: "4",
                caption: caption,
                upload_id: uploadId,
                usertags: JSON.stringify(userTags),
                _uid: accountId.toString(),
                device: session.device.payload,
                edits: {
                    crop_original_size:["$width","$height"],
                    crop_center: ["$zero","$negativeZero"],
                    crop_zoom: "$crop"
                },
                extra: {
                    source_width: width,
                    source_height: height
                }
            });
            payload = payload.replace(/\"\$width\"/gi, width.toFixed(1));
            payload = payload.replace(/\"\$height\"/gi, height.toFixed(1));
            payload = payload.replace(/\"\$zero\"/gi, (0).toFixed(1));
            payload = payload.replace(/\"\$negativeZero\"/gi, "-" + (0).toFixed(1));
            payload = payload.replace(/\"\$crop\"/gi, CROP.toFixed(1));

            return new Request(session)
                .setMethod('POST')
                .setResource('mediaConfigure')
                .setBodyType('form')
                .setData(JSON.parse(payload))
                .generateUUID()
                .signPayload()
                .send()
        })
        .then(function(json) {
            return new Media(session, json.media)
        })
};

Media.configurePhotoStory = function (session, uploadId, width, height) {
    if(_.isEmpty(uploadId))
        throw new Error("Upload argument must be upload valid upload id");
    if(!width) width = 800;
    if(!height) height = 800;
    const CROP = 1;
    return session.getAccountId()
        .then(function(accountId){
            var payload = pruned({
                source_type: "4",
                upload_id: uploadId,
                _uid: accountId.toString(),
                device: session.device.payload,
                edits: {
                    crop_original_size:["$width","$height"],
                    crop_center: ["$zero","$negativeZero"],
                    crop_zoom: "$crop"
                },
                extra: {
                    source_width: width,
                    source_height: height
                }
            });
            payload = payload.replace(/\"\$width\"/gi, width.toFixed(1));
            payload = payload.replace(/\"\$height\"/gi, height.toFixed(1));
            payload = payload.replace(/\"\$zero\"/gi, (0).toFixed(1));
            payload = payload.replace(/\"\$negativeZero\"/gi, "-" + (0).toFixed(1));
            payload = payload.replace(/\"\$crop\"/gi, CROP.toFixed(1));

            return new Request(session)
                .setMethod('POST')
                .setResource('mediaConfigureStory')
                .setBodyType('form')
                .setData(JSON.parse(payload))
                .generateUUID()
                .signPayload()
                .send()
        })
        .then(function(json) {
            return new Media(session, json.media)
        })
};

Media.configureVideo = function (session, uploadId, caption, durationms, delay, {
  audio_muted = false,
  trim_type = 0,
  source_type = 'camera',
  mas_opt_in = 'NOT_PROMPTED',
  disable_comments = false,
  filter_type = 0,
  poster_frame_index = 0,
  geotag_enabled = false,
  camera_position = 'unknown'
} = {}) {
    if(_.isEmpty(uploadId))
        throw new Error("Upload argument must be upload valid upload id");
    if(typeof(durationms)==='undefined')
        throw new Error("Durationms argument must be upload valid video duration");
    var duration = durationms/1000;
    if(!caption) caption = "";
    if(!delay || typeof delay != "number") delay = 6500;
    return Promise.delay(delay)
        .then(function(){
            return session.getAccountId()
        })
        .then(function(accountId){
            var payload = pruned({
              "video_result": "deprecated",
              audio_muted,
              trim_type,
              "client_timestamp": String(new Date().getTime()).substr(0,10),
              "caption": caption,
              "edits": {
                "filter_strength": 1
              },
              "clips": [
                {
                  "length": duration,
                  "cinema": "unsupported",
                  "original_length": duration,
                  source_type,
                  start_time:0,
                  trim_type,
                  "camera_position": "back"
                }
              ],
              "_uid":accountId.toString(),
              source_type,
              mas_opt_in,
              "length": duration,
              disable_comments,
              filter_type,
              poster_frame_index,
              geotag_enabled,
              camera_position,
              "upload_id": uploadId.toString()
            });

            return new Request(session)
                .setMethod('POST')
                .setResource('videoConfigure')
                .setBodyType('form')
                .setData(JSON.parse(payload))
                .generateUUID()
                .signPayload()
                .send()
                .then(function(json) {
                    return new Media(session, json.media)
                })
                .catch(Exceptions.TranscodeTimeoutError,function(error){
                    //Well, we just want to repeat our request. Dunno why this is happening and we should not let our users deal with this crap themselves.
                    return Media.configureVideo(session,uploadId,caption,durationms,delay);
                })
    })
};

Media.configurePhotoAlbum = function (session, uploadId, caption, width, height, userTags) {
    if(_.isEmpty(uploadId))
        throw new Error("Upload argument must be upload valid upload id");
    if(!caption) caption = "";
    if(!width) width = 800;
    if(!height) height = 800;
    if (!userTags) userTags = {};

    const CROP = 1;

    var payload = {
        source_type: "4",
        caption: caption,
        upload_id: uploadId,
        media_folder: 'Instagram',
        device: session.device.payload,
        edits: {
            crop_original_size:[width.toFixed(1),height.toFixed(1)],
            crop_center: [(0).toFixed(1), "-" + (0).toFixed(1)],
            crop_zoom: CROP.toFixed(1)
        },
        extra: {
            source_width: width,
            source_height: height
        }
    };
    return Promise.resolve(payload);
};

Media.configureVideoAlbum = function (session, uploadId, caption, durationms, delay, width, height) {
    if(_.isEmpty(uploadId))
        throw new Error("Upload argument must be upload valid upload id");
    if(typeof(durationms)==='undefined')
        throw new Error("Durationms argument must be upload valid video duration");
    var duration = durationms/1000;
    if(!caption) caption = "";
    if(!delay || typeof delay != "number") delay = 6500;
    return Promise.delay(delay)
        .then(function(){
            var payload = {
                filter_type: "0",
                source_type: "3",
                video_result: "deprecated",
                caption: caption,
                upload_id: uploadId,
                device: session.device.payload,
                length: duration,
                clips: [
                    {
                        length: duration,
                        source_type: "3",
                        camera_position: "back"
                    }
                ],
                audio_muted: false,
                poster_frame_index: 0,
                extra: {
                    source_width: width,
                    source_height: height
                }
            };

            return Promise.resolve(payload);
        })
};

Media.configureAlbum = function (session, medias, caption, disableComments) {
    var albumUploadId = new Date().getTime();

    caption = caption || '';
    disableComments = disableComments || false;

    Promise.mapSeries(medias, function (media) {
        if(media.type === 'photo') {
            return Media.configurePhotoAlbum(session, media.uploadId, caption, media.size[0], media.size[1], media.usertags)
        } else if (media.type === 'video') {
            return Media.configureVideoAlbum(session, media.uploadId, caption, media.durationms, media.delay, media.size[0], media.size[1]);
        } else {
            throw new Error('Invalid media type: ' + media.type);
        }
    })
        .then(function (results) {
            var params = {
                caption: caption,
                client_sidecar_id: albumUploadId,
                children_metadata: results
            };

            if(disableComments) {
                params['disable_comments'] = '1';
            }

            return new Request(session)
                .setMethod('POST')
                .setResource('mediaConfigureSidecar')
                .setBodyType('form')
                .setData(params)
                .generateUUID()
                .signPayload()
                .send()
        })
};<commit_msg>Media.configureAlbum method return Promise<commit_after>var Resource = require('./resource');
var util = require("util");
var _ = require("lodash");
var crypto = require('crypto');
var pruned = require('./json-pruned');
var fs = require('fs');
var request = require('request-promise');
var Promise = require("bluebird");

function Media(session, params) {
    Resource.apply(this, arguments);
}

util.inherits(Media, Resource);

module.exports = Media;
var Request = require('./request');
var Comment = require('./comment');
var Account = require('./account');
var Location = require('./location');
var Exceptions = require('./exceptions');
var camelKeys = require('camelcase-keys');

Media.prototype.parseParams = function (json) {
    var hash = camelKeys(json);
    var that = this;
    hash.commentCount = hash.commentsDisabled ? 0 : json.comment_count ;
    hash.webLink = "https://www.instagram.com/p/" + json.code + "/";
    hash.carouselMedia = [];
    if(_.isObject(json.location)) {
        var location = json.location;
        location.location = Object.create(json.location);
        location.title = location.name;
        location.subtitle = null;
        this.location = new Location(that.session, location);
    }
    if (json.media_type === 2) {
        hash.video = {
            versions: json.video_versions,
            hasAudio: json.has_audio,
            duration: json.video_duration
        }
    }
    if (json.media_type === 8 && _.isArray(json.carousel_media)) {
        hash.carouselMedia = _.map(json.carousel_media, function (medium) {
           return new Media(that.session, medium);
        });
    }
    if (_.isObject(json.caption))
        hash.caption = json.caption.text;
    hash.takenAt = parseInt(json.taken_at) * 1000;
    if (_.isObject(json.image_versions2)) {
        hash.images = json.image_versions2.candidates;
    } else if (_.isObject(json.carousel_media)) {
        hash.images = json.carousel_media.map(function(media) {
            return media.image_versions2.candidates
        })
    }
    if (_.isArray(json.video_versions))
        hash.videos = json.video_versions;
    this.previewComments = _.map(json.preview_comments, function(comment) {
        return new Comment(that.session, comment);
    });
    // backward compability
    this.comments = this.previewComments;
    this.account = new Account(that.session, json.user);
    return hash;
};

Media.prototype.getParams = function () {
    return _.extend(this._params, {
        account: this.account.params,
        comments: _.map(this.comments, 'params'),
        location: this.location ? this.location.params : {},
        carouselMedia:  _.map(this._params.carouselMedia, 'params')
    });
};

Media.getById = function (session, id) {
    return new Request(session)
        .setMethod('GET')
        .setResource('mediaInfo', {mediaId: id})
        .send()
        .then(function(json) {
            return new Media(session, json.items[0])
        })
};

Media.getByUrl = function(session, url){
    var self = this;
    return request({
        url: 'https://api.instagram.com/oembed/',
        qs:{url:url},
        json:true
    })
        .then(function (response) {
            return self.getById(session, response.media_id)
        })
        .catch(function (reason) {
            if(reason.error === 'No URL Match')throw new Exceptions.NotFoundError('No URL Match');
            else throw reason;
        })
};

Media.likers = function(session, mediaId) {
    return new Request(session)
        .setMethod('GET')
        .setResource('mediaLikes', {mediaId: mediaId})
        .send()
        .then(function(data) {
            return _.map(data.users, function (user) {
                return new Account(session, user);
            });
        });
};

Media.delete = function(session, mediaId) {
    return new Request(session)
        .setMethod('POST')
        .setResource('mediaDeletePhoto', {mediaId: mediaId})
        .setData({
            media_id: mediaId
        })
        .generateUUID()
        .signPayload()
        .send()
        .then(function (json) {
            if(json.did_delete) return;
            throw new Exceptions.RequestError({
                messaage: 'Not posible to delete medium!'
            })
        })
};

Media.edit = function(session, mediaId, caption, userTags) {
    var requestPayload = {
        media_id: mediaId,
        caption_text: caption
    };

    if (userTags) {
        requestPayload.usertags = JSON.stringify(userTags);
    }

    return new Request(session)
        .setMethod('POST')
        .setResource('mediaEdit', {mediaId: mediaId})
        .setData(requestPayload)
        .generateUUID()
        .signPayload()
        .send()
        .then(function (json) {
            if(json.media.caption_is_edited) {
                return new Media(session, json.media);
            }
            throw new Exceptions.RequestError({
                messaage: 'Edit media not successful!'
            });
        });
};

Media.configurePhoto = function (session, uploadId, caption, width, height, userTags) {
    if(_.isEmpty(uploadId))
        throw new Error("Upload argument must be upload valid upload id");
    if(!caption) caption = "";
    if(!width) width = 800;
    if(!height) height = 800;
    if (!userTags) userTags = {};

    const CROP = 1;
    return session.getAccountId()
        .then(function(accountId){
            var payload = pruned({
                source_type: "4",
                caption: caption,
                upload_id: uploadId,
                usertags: JSON.stringify(userTags),
                _uid: accountId.toString(),
                device: session.device.payload,
                edits: {
                    crop_original_size:["$width","$height"],
                    crop_center: ["$zero","$negativeZero"],
                    crop_zoom: "$crop"
                },
                extra: {
                    source_width: width,
                    source_height: height
                }
            });
            payload = payload.replace(/\"\$width\"/gi, width.toFixed(1));
            payload = payload.replace(/\"\$height\"/gi, height.toFixed(1));
            payload = payload.replace(/\"\$zero\"/gi, (0).toFixed(1));
            payload = payload.replace(/\"\$negativeZero\"/gi, "-" + (0).toFixed(1));
            payload = payload.replace(/\"\$crop\"/gi, CROP.toFixed(1));

            return new Request(session)
                .setMethod('POST')
                .setResource('mediaConfigure')
                .setBodyType('form')
                .setData(JSON.parse(payload))
                .generateUUID()
                .signPayload()
                .send()
        })
        .then(function(json) {
            return new Media(session, json.media)
        })
};

Media.configurePhotoStory = function (session, uploadId, width, height) {
    if(_.isEmpty(uploadId))
        throw new Error("Upload argument must be upload valid upload id");
    if(!width) width = 800;
    if(!height) height = 800;
    const CROP = 1;
    return session.getAccountId()
        .then(function(accountId){
            var payload = pruned({
                source_type: "4",
                upload_id: uploadId,
                _uid: accountId.toString(),
                device: session.device.payload,
                edits: {
                    crop_original_size:["$width","$height"],
                    crop_center: ["$zero","$negativeZero"],
                    crop_zoom: "$crop"
                },
                extra: {
                    source_width: width,
                    source_height: height
                }
            });
            payload = payload.replace(/\"\$width\"/gi, width.toFixed(1));
            payload = payload.replace(/\"\$height\"/gi, height.toFixed(1));
            payload = payload.replace(/\"\$zero\"/gi, (0).toFixed(1));
            payload = payload.replace(/\"\$negativeZero\"/gi, "-" + (0).toFixed(1));
            payload = payload.replace(/\"\$crop\"/gi, CROP.toFixed(1));

            return new Request(session)
                .setMethod('POST')
                .setResource('mediaConfigureStory')
                .setBodyType('form')
                .setData(JSON.parse(payload))
                .generateUUID()
                .signPayload()
                .send()
        })
        .then(function(json) {
            return new Media(session, json.media)
        })
};

Media.configureVideo = function (session, uploadId, caption, durationms, delay, {
  audio_muted = false,
  trim_type = 0,
  source_type = 'camera',
  mas_opt_in = 'NOT_PROMPTED',
  disable_comments = false,
  filter_type = 0,
  poster_frame_index = 0,
  geotag_enabled = false,
  camera_position = 'unknown'
} = {}) {
    if(_.isEmpty(uploadId))
        throw new Error("Upload argument must be upload valid upload id");
    if(typeof(durationms)==='undefined')
        throw new Error("Durationms argument must be upload valid video duration");
    var duration = durationms/1000;
    if(!caption) caption = "";
    if(!delay || typeof delay != "number") delay = 6500;
    return Promise.delay(delay)
        .then(function(){
            return session.getAccountId()
        })
        .then(function(accountId){
            var payload = pruned({
              "video_result": "deprecated",
              audio_muted,
              trim_type,
              "client_timestamp": String(new Date().getTime()).substr(0,10),
              "caption": caption,
              "edits": {
                "filter_strength": 1
              },
              "clips": [
                {
                  "length": duration,
                  "cinema": "unsupported",
                  "original_length": duration,
                  source_type,
                  start_time:0,
                  trim_type,
                  "camera_position": "back"
                }
              ],
              "_uid":accountId.toString(),
              source_type,
              mas_opt_in,
              "length": duration,
              disable_comments,
              filter_type,
              poster_frame_index,
              geotag_enabled,
              camera_position,
              "upload_id": uploadId.toString()
            });

            return new Request(session)
                .setMethod('POST')
                .setResource('videoConfigure')
                .setBodyType('form')
                .setData(JSON.parse(payload))
                .generateUUID()
                .signPayload()
                .send()
                .then(function(json) {
                    return new Media(session, json.media)
                })
                .catch(Exceptions.TranscodeTimeoutError,function(error){
                    //Well, we just want to repeat our request. Dunno why this is happening and we should not let our users deal with this crap themselves.
                    return Media.configureVideo(session,uploadId,caption,durationms,delay);
                })
    })
};

Media.configurePhotoAlbum = function (session, uploadId, caption, width, height, userTags) {
    if(_.isEmpty(uploadId))
        throw new Error("Upload argument must be upload valid upload id");
    if(!caption) caption = "";
    if(!width) width = 800;
    if(!height) height = 800;
    if (!userTags) userTags = {};

    const CROP = 1;

    var payload = {
        source_type: "4",
        caption: caption,
        upload_id: uploadId,
        media_folder: 'Instagram',
        device: session.device.payload,
        edits: {
            crop_original_size:[width.toFixed(1),height.toFixed(1)],
            crop_center: [(0).toFixed(1), "-" + (0).toFixed(1)],
            crop_zoom: CROP.toFixed(1)
        },
        extra: {
            source_width: width,
            source_height: height
        }
    };
    return Promise.resolve(payload);
};

Media.configureVideoAlbum = function (session, uploadId, caption, durationms, delay, width, height) {
    if(_.isEmpty(uploadId))
        throw new Error("Upload argument must be upload valid upload id");
    if(typeof(durationms)==='undefined')
        throw new Error("Durationms argument must be upload valid video duration");
    var duration = durationms/1000;
    if(!caption) caption = "";
    if(!delay || typeof delay != "number") delay = 6500;
    return Promise.delay(delay)
        .then(function(){
            var payload = {
                filter_type: "0",
                source_type: "3",
                video_result: "deprecated",
                caption: caption,
                upload_id: uploadId,
                device: session.device.payload,
                length: duration,
                clips: [
                    {
                        length: duration,
                        source_type: "3",
                        camera_position: "back"
                    }
                ],
                audio_muted: false,
                poster_frame_index: 0,
                extra: {
                    source_width: width,
                    source_height: height
                }
            };

            return Promise.resolve(payload);
        })
};

Media.configureAlbum = function (session, medias, caption, disableComments) {
    var albumUploadId = new Date().getTime();

    caption = caption || '';
    disableComments = disableComments || false;

    return Promise.mapSeries(medias, function (media) {
        if(media.type === 'photo') {
            return Media.configurePhotoAlbum(session, media.uploadId, caption, media.size[0], media.size[1], media.usertags)
        } else if (media.type === 'video') {
            return Media.configureVideoAlbum(session, media.uploadId, caption, media.durationms, media.delay, media.size[0], media.size[1]);
        } else {
            throw new Error('Invalid media type: ' + media.type);
        }
    })
        .then(function (results) {
            var params = {
                caption: caption,
                client_sidecar_id: albumUploadId,
                children_metadata: results
            };

            if(disableComments) {
                params['disable_comments'] = '1';
            }

            return new Request(session)
                .setMethod('POST')
                .setResource('mediaConfigureSidecar')
                .setBodyType('form')
                .setData(params)
                .generateUUID()
                .signPayload()
                .send()
        })
};
<|endoftext|>


<commit_before>const boom = require('@hapi/boom')
const Netmask = require('netmask').Netmask
const {
  RateLimiterRedis
} = require('rate-limiter-flexible')
const _ = require('underscore')
const underscore = _

const braveHapi = require('./extras-hapi')
const whitelist = require('./hapi-auth-whitelist')

const pluginName = 'rateLimitRedisPlugin'

const graylist = {
  addresses: process.env.IP_GRAYLIST && process.env.IP_GRAYLIST.split(',')
}

if (graylist.addresses) {
  graylist.authorizedAddrs = []
  graylist.authorizedBlocks = []

  graylist.addresses.forEach((entry) => {
    if ((entry.indexOf('/') === -1) && (entry.split('.').length === 4)) return graylist.authorizedAddrs.push(entry)

    graylist.authorizedBlocks.push(new Netmask(entry))
  })
}

module.exports = (runtime) => {
  const redisClient = (runtime.cache && runtime.cache.cache) || runtime.queue.config.client

  /*  access type            requests/minute per IP address
    -------------------    ------------------------------
    anonymous (browser)       60
    administrator (github)  3000
    server (bearer token)  60000
  */
  const rateLimiterAuthed = new RateLimiterRedis({
    redis: redisClient,
    keyPrefix: 'rate-limiter-authed',
    points: 3000, // requests per
    duration: 60 // seconds by IP
  })

  const rateLimiterWhitelisted = new RateLimiterRedis({
    redis: redisClient,
    keyPrefix: 'rate-limiter-whitelist',
    points: 60000, // requests per
    duration: 60 // seconds by IP
  })

  const rateLimiter = new RateLimiterRedis({
    redis: redisClient,
    keyPrefix: 'rate-limiter',
    points: 60, // requests per
    duration: 60 // seconds by IP
  })

  const noRateLimiter = new RateLimiterRedis({
    redis: redisClient,
    keyPrefix: 'no-rate-limiter',
    points: Number.MAX_SAFE_INTEGER, // requests per
    duration: 1 // seconds by IP
  })

  const globalRateLimiter = new RateLimiterRedis({
    redis: redisClient,
    keyPrefix: 'global-limiter',
    points: 1000, // requests per
    duration: 1 // seconds by IP
  })

  const internals = {
    pluginName,
    redisClient,
    rateLimiter,
    rateLimiterAuthed,
    rateLimiterWhitelisted,
    noRateLimiter
  }

  return {
    name: pluginName,
    version: '1.0.0',
    register: function (server) {
      server.ext('onPreAuth', async (request, h) => {
        const address = rateLimitKey(request)
        const rateLimiter = chooseRateLimiter(request)
        try {
          await rateLimiter.consume(address)
          await globalRateLimiter.consume('all')
          return h.continue
        } catch (err) {
          let error
          if (err instanceof Error) {
            // If some Redis error and `insuranceLimiter` is not set
            error = boom.internal('Try later')
          } else {
            // Not enough points to consume
            error = boom.tooManyRequests('Rate limit exceeded')
            error.output.headers['Retry-After'] = Math.round(err.msBeforeNext / 1000) || 1
          }

          return error
        }
      })
    }
  }

  function chooseRateLimiter (request) {
    let authorization, parts, token

    try {
      if (process.env.NODE_ENV !== 'production') {
        return internals.noRateLimiter
      }
      const ipaddr = whitelist.ipaddr(request)
      if (ipaddr === '127.0.0.1') {
        return internals.noRateLimiter
      }

      if ((graylist.authorizedAddrs) &&
          ((graylist.authorizedAddrs.indexOf(ipaddr) !== -1) ||
           (underscore.find(graylist.authorizedBlocks, (block) => { return block.contains(ipaddr) })))) {
        return internals.noRateLimiter
      }

      if (whitelist.authorizedP(ipaddr)) {
        authorization = request.raw.req.headers.authorization
        if (authorization) {
          parts = authorization.split(/\s+/)
          token = (parts[0].toLowerCase() === 'bearer') && parts[1]
        } else {
          token = request.query.access_token
        }

        const tokenlist = process.env.TOKEN_LIST ? process.env.TOKEN_LIST.split(',') : []
        if (typeof token === 'string' && braveHapi.isSimpleTokenValid(tokenlist, token)) {
          return internals.rateLimiterWhitelisted
        }

        return internals.rateLimiterAuthed
      }
    } catch (e) {}

    return internals.rateLimiter
  }

  function rateLimitKey (request) {
    try {
      return whitelist.ipaddr(request) + ':' + runtime.config.server.host
    } catch (e) {
      return 'default'
    }
  }
}
<commit_msg>make rate limit adjustable (#767)<commit_after>const boom = require('@hapi/boom')
const Netmask = require('netmask').Netmask
const {
  RateLimiterRedis
} = require('rate-limiter-flexible')
const _ = require('underscore')
const underscore = _

const braveHapi = require('./extras-hapi')
const whitelist = require('./hapi-auth-whitelist')

const pluginName = 'rateLimitRedisPlugin'

const graylist = {
  addresses: process.env.IP_GRAYLIST && process.env.IP_GRAYLIST.split(',')
}

if (graylist.addresses) {
  graylist.authorizedAddrs = []
  graylist.authorizedBlocks = []

  graylist.addresses.forEach((entry) => {
    if ((entry.indexOf('/') === -1) && (entry.split('.').length === 4)) return graylist.authorizedAddrs.push(entry)

    graylist.authorizedBlocks.push(new Netmask(entry))
  })
}

module.exports = (runtime) => {
  const redisClient = (runtime.cache && runtime.cache.cache) || runtime.queue.config.client

  /*  access type            requests/minute per IP address
    -------------------    ------------------------------
    anonymous (browser)       60
    administrator (github)  3000
    server (bearer token)  60000
  */
  const rateLimiterAuthed = new RateLimiterRedis({
    redis: redisClient,
    keyPrefix: 'rate-limiter-authed',
    points: 3000, // requests per
    duration: 60 // seconds by IP
  })

  const rateLimiterWhitelisted = new RateLimiterRedis({
    redis: redisClient,
    keyPrefix: 'rate-limiter-whitelist',
    points: 60000, // requests per
    duration: 60 // seconds by IP
  })

  const rateLimiter = new RateLimiterRedis({
    redis: redisClient,
    keyPrefix: 'rate-limiter',
    points: 60, // requests per
    duration: 60 // seconds by IP
  })

  const noRateLimiter = new RateLimiterRedis({
    redis: redisClient,
    keyPrefix: 'no-rate-limiter',
    points: Number.MAX_SAFE_INTEGER, // requests per
    duration: 1 // seconds by IP
  })

  const globalRateLimiter = new RateLimiterRedis({
    redis: redisClient,
    keyPrefix: 'global-limiter',
    points: +process.env.GLOBAL_RATE_LIMIT_PER_10S || 10000, // requests per
    duration: 10 // seconds
  })

  const internals = {
    pluginName,
    redisClient,
    rateLimiter,
    rateLimiterAuthed,
    rateLimiterWhitelisted,
    noRateLimiter
  }

  return {
    name: pluginName,
    version: '1.0.0',
    register: function (server) {
      server.ext('onPreAuth', async (request, h) => {
        const address = rateLimitKey(request)
        const rateLimiter = chooseRateLimiter(request)
        try {
          await rateLimiter.consume(address)
          await globalRateLimiter.consume('all')
          return h.continue
        } catch (err) {
          let error
          if (err instanceof Error) {
            // If some Redis error and `insuranceLimiter` is not set
            error = boom.internal('Try later')
          } else {
            // Not enough points to consume
            error = boom.tooManyRequests('Rate limit exceeded')
            error.output.headers['Retry-After'] = Math.round(err.msBeforeNext / 1000) || 1
          }

          return error
        }
      })
    }
  }

  function chooseRateLimiter (request) {
    let authorization, parts, token

    try {
      if (process.env.NODE_ENV !== 'production') {
        return internals.noRateLimiter
      }
      const ipaddr = whitelist.ipaddr(request)
      if (ipaddr === '127.0.0.1') {
        return internals.noRateLimiter
      }

      if ((graylist.authorizedAddrs) &&
          ((graylist.authorizedAddrs.indexOf(ipaddr) !== -1) ||
           (underscore.find(graylist.authorizedBlocks, (block) => { return block.contains(ipaddr) })))) {
        return internals.noRateLimiter
      }

      if (whitelist.authorizedP(ipaddr)) {
        authorization = request.raw.req.headers.authorization
        if (authorization) {
          parts = authorization.split(/\s+/)
          token = (parts[0].toLowerCase() === 'bearer') && parts[1]
        } else {
          token = request.query.access_token
        }

        const tokenlist = process.env.TOKEN_LIST ? process.env.TOKEN_LIST.split(',') : []
        if (typeof token === 'string' && braveHapi.isSimpleTokenValid(tokenlist, token)) {
          return internals.rateLimiterWhitelisted
        }

        return internals.rateLimiterAuthed
      }
    } catch (e) {}

    return internals.rateLimiter
  }

  function rateLimitKey (request) {
    try {
      return whitelist.ipaddr(request) + ':' + runtime.config.server.host
    } catch (e) {
      return 'default'
    }
  }
}
<|endoftext|>


<commit_before>//function asd () {
//    var
        A = new AudioContext,
        C = A.createScriptProcessor(a.width = 1024, 1, 1),
        s = 0, t = 0, C.connect(A.destination),
        C.onaudioprocess = function (l) {
            for (b = l.outputBuffer.getChannelData(x=0),
                     a.height = 370,
                     c.fillStyle = "#214",
                     c.fillRect(0, x, 1024, 370),
                     c.fillStyle = "#46f",
                     c.globalAlpha = .1,
                     c.fillRect(0, x, 1024, x += 37),
                     c.fillRect(0, x, 1024, x += 37),
                     c.fillRect(0, x, 1024, x += 37),
                     c.fillRect(0, x, 1024, x += 37),
                     c.fillRect(0, x, 1024, x += 37),
                     c.fillRect(0, x, 1024, x += 37),
                     c.fillRect(0, x, 1024, x += 37),
                     c.fillRect(0, x, 1024, x += 37),
                     c.fillRect(0, x, 1024, x += 37),
                     c.globalAlpha = 1,
                     c.font = "62px serif",
                     c.fillText("☁", (4*s%1400) - 300, 220),
                     c.fillText("☁", (6*s%1400) - 300, 220),
                     c.font = "110px serif",
                     c.fillText("☁", (7*s%1400) - 300, 220),
                     c.font = "220px serif",
                     c.fillText("☁", (9*s%1400)- 300, 220),
                     c.fillStyle = "#002", x=0;
                 x < 1024;
                 x++,
                     s += 6.25 / A.sampleRate,
                     t ++)
                c.fillRect(x, 330 + Math.min(277 * Math.sin(x / 117) + 4 * Math.sin(x / 11)), 1, 370),
                    k = 55 * Math.pow(1.059463, parseInt("bbnqbbnn99lljjvzeeqqeellggggsnnzbbnnbbnn99lljjvzeeqqeeqqggggllxxlllleeqqaamybbnn99lleeii66iibbnq99lo77jn77jj77jneeqqggssnnnnbbnqeennllllbbnueenn99lpjjjjjjjjjjjjjjeeiillllll77lu11449999bbbb6622222222bbccggjjjjjjjjjjoonniieeeeeeeeeeebeeiillllllllllqqppllggggggggggll"[0 | s % 264], 36)),
                    l = 55 * Math.pow(1.059463, parseInt("    qqqtsqooqqqqqqooomlommmmllllllqqqqqtsqooqqqqqqqsttttvvvvssstsqosttttssssqqqtsqosttttssssqqqtsqooqqqqqqqqqqqqomlommmmllllllllllllllllllllllllllooqqhhhhhhmljmoohhhhhhllhhmmooooovovtvttssqqllllllgghhrrrrrrrrrrrrrrrrxxttttttssttstqtttmmmmmmmmmmmmmmjjggggggeeggegcc"[0 | s % 264], 36)+9),
                    b[x] = (Math.sin(Math.PI * t / A.sampleRate * l * 2) >0 ?.5: -.5)
                        + (Math.sin(Math.PI * t / A.sampleRate * k * 2)   >0 ?.5: -.5),
                c.fillRect(x, 330 + b[x], 1, 370)
            ;

            c.fillStyle = "#B4B",
                c.font = "16px serif",
                c.fillText("THE SECRET OF", 480, 98),
                c.font = "62px serif",
                c.fillText("MENTAL", 420, 150),
                c.fillText("ISLAND", 480, 196)
        }
//}();<commit_msg>900 bytes version with smoother gradient<commit_after>// Compresses to 900 bytes ;)
b.bgColor = "#36f",
A = new AudioContext,
C = A.createScriptProcessor(a.width = 1024, s = 0, 1),
C.connect(A.destination),
C.onaudioprocess = function(b) {
  for (a.height = 480,
  b = b.outputBuffer.getChannelData(d = 0),
  c.fillStyle = "#9cf",
  c.font = "116px serif",
  c.fillText("☁", 7 * s % 1336 - 226, 226),
  c.font = "226px serif",
  c.fillText("☁", 9 * s % 1336 - 226, 226),
  c.font = "66px serif",
  c.fillText("☁", 4 * s % 1336 - 226, 226),
  c.fillText("☁", 6 * s % 1336 - 226, 226),
  c.fillStyle = "#003"; d < 1024; s += 6.25 / A.sampleRate)
    c.fillRect(0, d, 1024, d < 336 ? 1 - d / 336 : 1),
    c.fillRect(d, 336 + (b[d++] = (55 * s * Math.pow(1.059463, parseInt("    qqqtsqooqqqqqqooomlommmmllllllqqqqqtsqooqqqqqqqsttttvvvvssstsqosttttssssqqqtsqosttttssssqqqtsqooqqqqqqqqqqqqomlommmmllllllllllllllllllllllllllooqqhhhhhhmljmoohhhhhhllhhmmooooovovtvttssqqllllllgghhrrrrrrrrrrrrrrrrxxttttttssttstqtttmmmmmmmmmmmmmmjjggggggeeggegccbbnqbbnn99lljjvzeeqqeellggggsnnzbbnnbbnn99lljjvzeeqqeeqqggggllxxlllleeqqaamybbnn99lleeii66iibbnq99lo77jn77jj77jneeqqggssnnnnbbnqeennllllbbnueenn99lpjjjjjjjjjjjjjjeeiillllll77lu11449999bbbb6622222222bbccggjjjjjjjjjjoonniieeeeeeeeeeebeeiillllllllllqqppllggggggggggll"[0 | s % 264 + (d & 1) * 264], 36) + 9) & 1) - .5), 4, 226 * Math.sin(d / 116) + 16 * Math.sin(d / 16));
  c.fillStyle = "#f6f",
  c.fillText("MENTAL", 420, 146),
  c.fillText("ISLAND", 480, 196),
  c.font = "16px serif",
  c.fillText("THE SECRET OF", 480, 96)
}
<|endoftext|>


<commit_before>function lazy(run, time = false) {
    var fireing, args, that;
    var fire = function () {
        if (time > 17) {
            if (fireing === true) {
                fireing = setTimeout(fire, +time);
            } else {
                run.apply(that, args);
                fireing = false;
            }
        } else {
            if (fireing === true) {
                run.apply(that, args);
            }
            fireing = false;
        }
    };
    return function () {
        args = arguments;
        that = this;
        if (fireing) return fireing = true;
        if (time > 0) {
            fireing = setTimeout(fire, +time);
        } else {
            fireing = requestAnimationFrame(fire);
            if (time === false || time === 0) fireing = true;
            else run();
        }
    };
}
module.exports = lazy;<commit_msg>如果函数返回promise，屏蔽两个promise同时执行<commit_after>function lazy(run, time = false) {
    var fireing, args, that;
    var fire = function () {
        if (time > 17) {
            if (fireing === true) {
                fireing = setTimeout(fire, +time);
            }
            else if (isFinite(fireing)) {
                fireing = run.apply(that, args);
            }
            else {
                fireing = false;
            }
        } else {
            if (fireing === true) {
                fireing = run.apply(that, args);
            } else {
                fireing = false;
            }
        }
        if (fireing instanceof Promise) fireing.then(fire, fire);
    };
    return function () {
        args = arguments;
        that = this;
        if (fireing) return fireing = true;
        if (time > 0) {
            fireing = setTimeout(fire, +time);
        } else {
            fireing = requestAnimationFrame(fire);
            if (time === false || time === 0) fireing = true;
            else run();
        }
    };
}
module.exports = lazy;<|endoftext|>