<commit_before>// This file containts lxchat bussines logic
//Self-Invoking Anonymous Function Notation
// !function(){}(); // easy to read, the result is unimportant.
// (function(){})(); // like above but more parens.
// (function(){}()); // Douglas Crockford's style when you need function results.
// Further reading: http://javascript.crockford.com/code.html then search for invoked immediately

// function definitions section
window.lxchatSetData = function(userMessage) {
    // Save button temporary disabled
    $("#lxchatSave").attr("disabled", true);
    if (userMessage === '') {
        toastr["warning"]("¿Quieres compartir alguna novedad? Escribe un mensaje", "Chat - Mensaje vacío", {
            "positionClass": "toast-bottom-center"
        });
    } else {
        // currentUser Name
        var currentUser = $(".user_label:eq(0)").text().trim();
        /*Today's date splitted*/
        var d = new Date();
        var dd = d.getDate();
        var dm = d.getMonth() + 1;
        var dy = d.getFullYear();
        var dh = d.getHours();
        var di = d.getMinutes();
        var ampm = (dh > 12) ? "pm" : "am";
        dh = (dh > 12) ? "0" + (dh - 12) : dh;
        di = (di < 10) ? "0" + di : di;
        var newMessage = currentUser + ":\n" + userMessage + "\n" + dd + "/" + dm + "/" + dy + " " + dh + ":" + di + " " + ampm + "\n\n";

        var currentForm = document.forms['DetailView'];
        if (!currentForm) {
            currentForm = document.forms['EditView'];
        }
        var record_id = currentForm.record.value;
        var module_name = currentForm.module.value;

        var data = "method=" + "lxChat" + "&chat_c=" + newMessage + "&record_id=" + record_id + "&module=" + module_name + "&array_position=" + lxchat_array_position + "&save=" + 1;
        $.ajax({
            // beforeSend is a pre-request callback function that can be used to modify the jqXHR.
            beforeSend: function(jqXHR, settings) {
                console.groupCollapsed("LxChat Logic '%s' '%s' '%s' '%s'", module_name, 'lx-chat.js', 'lxchatSetData()', 'ajax beforeSend');
                console.log("*** start ***");
                console.log("beforeSend callback:", settings.url);
                console.groupEnd();
            },
            url: 'lxajax.php',
            type: 'POST',
            data: data,
            // success is a function to be called if the request succeeds.
            success: function(data, status, jqXHR) {
                console.groupCollapsed("LxChat logic '%s' '%s' '%s' '%s'", module_name, 'lx-chat.js', 'lxchatSetData()', 'ajax success');
                console.log("success callback:", status);
                console.log("data:", data);
                $("#lxchatnewmsg").val('');
                $("textarea#" + lxchatfield).val(data);
                $('#lxchatcontent').val(data).trigger('autosize');
                var h1 = $('#lxchatcontent')[0].scrollHeight,
                    h2 = $('#lxchatcontent').height();
                $('#lxchatcontent').scrollTop(h1 - h2);
                console.groupEnd();
            },
            // error is a function to be called if the request fails.
            error: function(jqXHR, status, error) {
                console.groupCollapsed("LxChat logic '%s' '%s' '%s' '%s'", module_name, 'lx-chat.js', 'lxchatSetData()', 'ajax error');
                console.log("error callback:", status);
                console.log("Function lxchatSetData error:", error);
                console.groupEnd();
            }, // end error
            // complete is a function to be called when the request finishes (after success and error callbacks are executed).
            // complete: function(jqXHR, status) {
            // },
            datatype: "text"
        });
    }
    $("#lxchatSave").removeAttr("disabled");
}

window.lxchatRender = function(lxchatfield) {
    if (!$("#lxchat").length) {
        var currentUser = $(".user_label:eq(0)").text().trim();
        //Current lxchatfield text
        lxchatfieldtext = $('#' + lxchatfield).text();
        //Current crm field must be hide
        $('#' + lxchatfield).hide();
        //lxchat div added
        $('
CHAT
').insertAfter('#' + lxchatfield);
        $('#lxchat').attr('style', 'position:relative'); //aca no estaba esta línea
        $('#lxchat').attr('style', 'width: 400px; border: 3px solid #6495ED;border-radius:5px;text-align:left;background-color: #BCD2EE;');
        $('#lxchat').append('<textarea id="lxchatcontent" cols="80" rows="6" readonly="readonly" style="width: 100%; overflow: scroll; background-color: #F6FAFD;">' + lxchatfieldtext + '</textarea>');
        $('#lxchatcontent').attr('style', 'width: 100%; height: 200px;background-color: #F6FAFD;');
        //Textarea for new messages added
        $('
<textarea id="lxchatnewmsg" placeholder="¿Quieres compartir alguna novedad, '+currentUser+'?" tabindex="0" title="" cols="80" rows="6" style="width: 400px;height: 90px;background-color: #F6FAFD;"></textarea>').insertAfter('#lxchat');
        //Save new messages button added
        $('
<input id="lxchatSave" type="button" value="enviar mensaje" />').insertAfter('#lxchatnewmsg');
        $(document).on("click", "#lxchatSave", function(event) {
            lxchatSetData($("#lxchatnewmsg").val());
        });
    }
}

window.lxchatFindFieldToRender = function() {
    var currentForm = document.forms['DetailView'];
    if (!currentForm) {
        currentForm = document.forms['EditView'];
    }
    var record_id = currentForm.record.value;
    var module_name = currentForm.module.value;
    var lxajaxdata = "method=" + "lxChatConfigOverrideField";
    $.ajax({
        // beforeSend is a pre-request callback function that can be used to modify the jqXHR.
        beforeSend: function(jqXHR, settings) {
            console.groupCollapsed("LxChat Logic '%s' '%s' '%s' '%s'", module_name, 'lx-chat.js', 'lxchatFindFieldToRender()', 'ajax beforeSend');
            console.log("*** start ***");
            console.log("beforeSend callback:", settings.url);
            console.groupEnd();
        },
        url: 'lxajax.php',
        type: 'GET',
        data: lxajaxdata,
        // success is a function to be called if the request succeeds.
        success: function(data, status, jqXHR) {
            console.groupCollapsed("LxChat logic '%s' '%s' '%s' '%s'", module_name, 'lx-chat.js', 'lxchatFindFieldToRender()', 'ajax success');
            console.log("success callback:", status);
            console.log("data:", data);
            fieldsArray = JSON.parse(data);
            for (var i = 0; i < fieldsArray.length; i++) {
                if ($(document).find("#" + fieldsArray[i]).length) {
                    if (!record_id) {
                        console.log("lxchat doesn't render when record_id isn't present");
                        lxShowCRMfield(fieldsArray[i]);
                        $('

').insertAfter('#' + fieldsArray[i]);
                    } else {
                        lxchatRender(fieldsArray[i]);
                        lxchatfield = fieldsArray[i];
                        lxchat_array_position = i;
                    }
                }
            }
        },
        // error is a function to be called if the request fails.
        error: function(jqXHR, status, error) {
            console.groupCollapsed("LxChat logic '%s' '%s' '%s' '%s'", module_name, 'lx-chat.js', 'lxchatFindFieldToRender()', 'ajax error');
            console.log("error callback:", status);
            console.log("Function lxchatFindFieldToRender error:", error);
            console.groupEnd();
        }, // end error
        // complete is a function to be called when the request finishes (after success and error callbacks are executed).
        complete: function(jqXHR, status) {
            var textarea = document.getElementById('lxchatcontent');
            if (textarea) {
                textarea.scrollTop = textarea.scrollHeight;
            }
        },
        datatype: "text"
    })
    // }
}

// Observers definitions
!function() {
    if ($("#edit_button").length || $("#SAVE").length || $("#SAVE_HEADER").length) {
        // now it ensures that lxchat isn't already present
        if (!$("#lxchat").length) {
            lxchatFindFieldToRender();
        }
    }
    // On other modules
    // create an observer instance
    // https://developer.mozilla.org/en/docs/Web/API/MutationObserver
    var observer = new MutationObserver(function(mutations) {

        // If a specific mutation is need iterate over the array
        // mutations.forEach(function(mutation) {
        // There are 3 types attributes, childList and characterData
        // if (mutation.type == "attributes") {
        // if (mutation.target.nodeName == "DIV" && mutation.target.id == 'tabcontent0') {
        /*when found do your code*/
        // console.log(mutation);
        //}
        //}
        // });

        // If any change will trigger the effect check only if the array exists
        // if (mutations) { /*your code*/ }
        if (mutations) {
            $(document).ready(function() {
                var lxchatfield = '';
                var lxchat_array_position = '';
                // if #edit_button exists is a detailview, if not, then if a #SAVE or #SAVE_HEADER button exists is a editview
                if ($("#edit_button").length || $("#SAVE").length || $("#SAVE_HEADER").length) {
                    // now it ensures that lxchat isn't already present
                    if (!$("#lxchat").length) {
                        lxchatFindFieldToRender();
                    }
                }
            });
            // if needed only once, you can stop observing with observer.disconnect();
            // observer.disconnect();
        }
    });
    // Observer target
    var target = document.querySelector('#content');
    // configuration of the observer:
    // NOTE: At the very least, childList, attributes, or characterData must be set to true. Otherwise, "An invalid or illegal string was specified" error is thrown.
    var config = {
        attributes: true,
        childList: true,
        characterData: true,
        subtree: true
    };
    // pass in the target node, as well as the observer options
    observer.observe(target, config);
    // end observer
}();

// eof
<commit_msg>refactor: placeholder message<commit_after>// This file containts lxchat bussines logic
//Self-Invoking Anonymous Function Notation
// !function(){}(); // easy to read, the result is unimportant.
// (function(){})(); // like above but more parens.
// (function(){}()); // Douglas Crockford's style when you need function results.
// Further reading: http://javascript.crockford.com/code.html then search for invoked immediately

// function definitions section
window.lxchatSetData = function(userMessage) {
    // Save button temporary disabled
    $("#lxchatSave").attr("disabled", true);
    if (userMessage === '') {
        toastr["warning"]("¿Quieres compartir alguna novedad? Escribe un mensaje", "Chat - Mensaje vacío", {
            "positionClass": "toast-bottom-center"
        });
    } else {
        // currentUser Name
        var currentUser = $(".user_label:eq(0)").text().trim();
        /*Today's date splitted*/
        var d = new Date();
        var dd = d.getDate();
        var dm = d.getMonth() + 1;
        var dy = d.getFullYear();
        var dh = d.getHours();
        var di = d.getMinutes();
        var ampm = (dh > 12) ? "pm" : "am";
        dh = (dh > 12) ? "0" + (dh - 12) : dh;
        di = (di < 10) ? "0" + di : di;
        var newMessage = currentUser + ":\n" + userMessage + "\n" + dd + "/" + dm + "/" + dy + " " + dh + ":" + di + " " + ampm + "\n\n";

        var currentForm = document.forms['DetailView'];
        if (!currentForm) {
            currentForm = document.forms['EditView'];
        }
        var record_id = currentForm.record.value;
        var module_name = currentForm.module.value;

        var data = "method=" + "lxChat" + "&chat_c=" + newMessage + "&record_id=" + record_id + "&module=" + module_name + "&array_position=" + lxchat_array_position + "&save=" + 1;
        $.ajax({
            // beforeSend is a pre-request callback function that can be used to modify the jqXHR.
            beforeSend: function(jqXHR, settings) {
                console.groupCollapsed("LxChat Logic '%s' '%s' '%s' '%s'", module_name, 'lx-chat.js', 'lxchatSetData()', 'ajax beforeSend');
                console.log("*** start ***");
                console.log("beforeSend callback:", settings.url);
                console.groupEnd();
            },
            url: 'lxajax.php',
            type: 'POST',
            data: data,
            // success is a function to be called if the request succeeds.
            success: function(data, status, jqXHR) {
                console.groupCollapsed("LxChat logic '%s' '%s' '%s' '%s'", module_name, 'lx-chat.js', 'lxchatSetData()', 'ajax success');
                console.log("success callback:", status);
                console.log("data:", data);
                $("#lxchatnewmsg").val('');
                $("textarea#" + lxchatfield).val(data);
                $('#lxchatcontent').val(data).trigger('autosize');
                var h1 = $('#lxchatcontent')[0].scrollHeight,
                    h2 = $('#lxchatcontent').height();
                $('#lxchatcontent').scrollTop(h1 - h2);
                console.groupEnd();
            },
            // error is a function to be called if the request fails.
            error: function(jqXHR, status, error) {
                console.groupCollapsed("LxChat logic '%s' '%s' '%s' '%s'", module_name, 'lx-chat.js', 'lxchatSetData()', 'ajax error');
                console.log("error callback:", status);
                console.log("Function lxchatSetData error:", error);
                console.groupEnd();
            }, // end error
            // complete is a function to be called when the request finishes (after success and error callbacks are executed).
            // complete: function(jqXHR, status) {
            // },
            datatype: "text"
        });
    }
    $("#lxchatSave").removeAttr("disabled");
}

window.lxchatRender = function(lxchatfield) {
    if (!$("#lxchat").length) {
        var currentUser = $(".user_label:eq(0)").text().trim();
        //Current lxchatfield text
        lxchatfieldtext = $('#' + lxchatfield).text();
        //Current crm field must be hide
        $('#' + lxchatfield).hide();
        //lxchat div added
        $('
CHAT
').insertAfter('#' + lxchatfield);
        $('#lxchat').attr('style', 'position:relative'); //aca no estaba esta línea
        $('#lxchat').attr('style', 'width: 400px; border: 3px solid #6495ED;border-radius:5px;text-align:left;background-color: #BCD2EE;');
        $('#lxchat').append('<textarea id="lxchatcontent" cols="80" rows="6" readonly="readonly" style="width: 100%; overflow: scroll; background-color: #F6FAFD;">' + lxchatfieldtext + '</textarea>');
        $('#lxchatcontent').attr('style', 'width: 100%; height: 200px;background-color: #F6FAFD;');
        //Textarea for new messages added
        $('
<textarea id="lxchatnewmsg" placeholder="¿Quieres compartir alguna novedad, '+currentUser.split(" ")[0]+'?" tabindex="0" title="" cols="80" rows="6" style="width: 400px;height: 90px;background-color: #F6FAFD;"></textarea>').insertAfter('#lxchat');
        //Save new messages button added
        $('
<input id="lxchatSave" type="button" value="enviar mensaje" />').insertAfter('#lxchatnewmsg');
        $(document).on("click", "#lxchatSave", function(event) {
            lxchatSetData($("#lxchatnewmsg").val());
        });
    }
}

window.lxchatFindFieldToRender = function() {
    var currentForm = document.forms['DetailView'];
    if (!currentForm) {
        currentForm = document.forms['EditView'];
    }
    var record_id = currentForm.record.value;
    var module_name = currentForm.module.value;
    var lxajaxdata = "method=" + "lxChatConfigOverrideField";
    $.ajax({
        // beforeSend is a pre-request callback function that can be used to modify the jqXHR.
        beforeSend: function(jqXHR, settings) {
            console.groupCollapsed("LxChat Logic '%s' '%s' '%s' '%s'", module_name, 'lx-chat.js', 'lxchatFindFieldToRender()', 'ajax beforeSend');
            console.log("*** start ***");
            console.log("beforeSend callback:", settings.url);
            console.groupEnd();
        },
        url: 'lxajax.php',
        type: 'GET',
        data: lxajaxdata,
        // success is a function to be called if the request succeeds.
        success: function(data, status, jqXHR) {
            console.groupCollapsed("LxChat logic '%s' '%s' '%s' '%s'", module_name, 'lx-chat.js', 'lxchatFindFieldToRender()', 'ajax success');
            console.log("success callback:", status);
            console.log("data:", data);
            fieldsArray = JSON.parse(data);
            for (var i = 0; i < fieldsArray.length; i++) {
                if ($(document).find("#" + fieldsArray[i]).length) {
                    if (!record_id) {
                        console.log("lxchat doesn't render when record_id isn't present");
                        lxShowCRMfield(fieldsArray[i]);
                        $('

').insertAfter('#' + fieldsArray[i]);
                    } else {
                        lxchatRender(fieldsArray[i]);
                        lxchatfield = fieldsArray[i];
                        lxchat_array_position = i;
                    }
                }
            }
        },
        // error is a function to be called if the request fails.
        error: function(jqXHR, status, error) {
            console.groupCollapsed("LxChat logic '%s' '%s' '%s' '%s'", module_name, 'lx-chat.js', 'lxchatFindFieldToRender()', 'ajax error');
            console.log("error callback:", status);
            console.log("Function lxchatFindFieldToRender error:", error);
            console.groupEnd();
        }, // end error
        // complete is a function to be called when the request finishes (after success and error callbacks are executed).
        complete: function(jqXHR, status) {
            var textarea = document.getElementById('lxchatcontent');
            if (textarea) {
                textarea.scrollTop = textarea.scrollHeight;
            }
        },
        datatype: "text"
    })
    // }
}

// Observers definitions
!function() {
    if ($("#edit_button").length || $("#SAVE").length || $("#SAVE_HEADER").length) {
        // now it ensures that lxchat isn't already present
        if (!$("#lxchat").length) {
            lxchatFindFieldToRender();
        }
    }
    // On other modules
    // create an observer instance
    // https://developer.mozilla.org/en/docs/Web/API/MutationObserver
    var observer = new MutationObserver(function(mutations) {

        // If a specific mutation is need iterate over the array
        // mutations.forEach(function(mutation) {
        // There are 3 types attributes, childList and characterData
        // if (mutation.type == "attributes") {
        // if (mutation.target.nodeName == "DIV" && mutation.target.id == 'tabcontent0') {
        /*when found do your code*/
        // console.log(mutation);
        //}
        //}
        // });

        // If any change will trigger the effect check only if the array exists
        // if (mutations) { /*your code*/ }
        if (mutations) {
            $(document).ready(function() {
                var lxchatfield = '';
                var lxchat_array_position = '';
                // if #edit_button exists is a detailview, if not, then if a #SAVE or #SAVE_HEADER button exists is a editview
                if ($("#edit_button").length || $("#SAVE").length || $("#SAVE_HEADER").length) {
                    // now it ensures that lxchat isn't already present
                    if (!$("#lxchat").length) {
                        lxchatFindFieldToRender();
                    }
                }
            });
            // if needed only once, you can stop observing with observer.disconnect();
            // observer.disconnect();
        }
    });
    // Observer target
    var target = document.querySelector('#content');
    // configuration of the observer:
    // NOTE: At the very least, childList, attributes, or characterData must be set to true. Otherwise, "An invalid or illegal string was specified" error is thrown.
    var config = {
        attributes: true,
        childList: true,
        characterData: true,
        subtree: true
    };
    // pass in the target node, as well as the observer options
    observer.observe(target, config);
    // end observer
}();

// eof
<|endoftext|>


<commit_before>import React, { PropTypes, Component } from 'react';
import { connect } from 'react-redux';
import marked from 'marked';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import FlatButton from 'material-ui/FlatButton';
import { Card, CardActions, CardText } from 'material-ui/Card';
import { RaisedButton } from 'material-ui';
import { yellow200 } from 'material-ui/styles/colors';
// import DeleteIcon from 'material-ui/svg-icons/action/delete';
// import EditIcon from 'material-ui/svg-icons/editor/mode-edit';
import IconMenu from 'material-ui/IconMenu';
import MenuItem from 'material-ui/MenuItem';
import IconButton from 'material-ui/IconButton';
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert';
import CommentEditor from './CommentEditor';
import { muiTheme } from '../styles/styles';
import { Node } from '../produceCommentGraph';

/* eslint-disable */
import { saveReply, deleteReply, editReply, loadDiscussion } from '../actions'; // loadDiscussion
/* eslint-enable */

const COMMENTINDENTAMOUNT = 50;
const COLORARRAY = ['Red', 'Green', 'Blue', 'Yellow', 'Purple'];

class CommentBox extends Component {
  constructor(props) {
    super(props);
    this.state = {
      markdown: '',
    };
    this.ordering = this.props.ordering;
    this.id = props.id;
    this.onToggleReply = this.onToggleReply.bind(this);
    this.onPostReply = this.onPostReply.bind(this);
    this.getLastBeforeEnd = this.getLastBeforeEnd.bind(this);
    this.updateMarkdown = this.updateMarkdown.bind(this);
    this.handleDeleteReply = this.handleDeleteReply.bind(this);
    this.handleEditReply = this.handleEditReply.bind(this);
    this.onPostEdit = this.onPostEdit.bind(this);
  }

  //  style={courses.length > 0 ? 'display: '' : 'display:none'}>

  onToggleReply() {
    this.state = {
      markdown: '',
    };

    let visible = true;
    if (this.props.commentId !== this.props.currentlyOpen) {
      visible = true;
    } else {
      visible = !this.props.isVisible;
    }

    this.props.dispatch({
      type: 'TOGGLE_REPLY',
      parentIdx: this.props.id, // This is the index in the orderings array
      currentlyOpen: (visible ? this.props.commentId : '0'),
      replyText: 'hello',
      isVisible: visible,
      ordering: this.ordering,
      isEditing: false,
    });
  }

  onPostReply() {
    this.props.dispatch({
      type: 'TOGGLE_REPLY',
      parentIdx: this.props.id, // This is the index in the orderings array
      currentlyOpen: '-1',
      replyText: 'hello',
      isVisible: false,
      ordering: this.ordering,
      isEditing: false,
    });

    // const arrayIndex = this.getLastBeforeEnd();   //  this.getLastBeforeEnd(); //  This is where commentBox should be inserted in the array
    //  Splice this node into ordering
    const addedNode = new Node(this.state.markdown);
    addedNode.depth = this.props.node.depth + 1;
    addedNode.parent = this.props.node;

    this.props.dispatch(saveReply(this.state.markdown, addedNode.parent._id, this.props.articleURI));
  }

  onPostEdit() {
    // this.props.dispatch(editReply(this.props.commentId, this.state.markdown));
    // this.props.dispatch({ type: 'EDIT', isEditing: false });

    Promise.resolve(this.props.dispatch(editReply(this.props.commentId, this.state.markdown))).then(() => this.props.dispatch({ type: 'EDIT', isEditing: false }));
    this.props.dispatch(loadDiscussion(this.props.articleURI));
  }

  getLastBeforeEnd() {
    const idx = this.id;
    for (let i = idx + 1; i < this.ordering.length; i += 1) {
      if (this.ordering[i].depth <= this.props.node.depth) {
        return i;
      }
    }
    return this.ordering.length;
  }

  handleDeleteReply() {
    Promise.resolve(this.props.dispatch(deleteReply(this.props.commentId))).then(() => this.props.dispatch(loadDiscussion(this.props.articleURI)));
  }

  handleEditReply() {
    this.setState({ markdown: this.props.node.text });
    this.props.dispatch({ type: 'EDIT', editText: 'Hello', editId: this.props.commentId, isEditing: !this.props.isEditing });
  }

  updateMarkdown(event) {
    this.setState({ markdown: event.target.value });
  }

  render() {
    let textarea = ;
    const madeThisComment = this.props.node.author ? (this.props.userId === this.props.node.author._id) : false;

    /* eslint-disable */

    if (this.props.isVisible && this.props.commentId === this.props.currentlyOpen) {
      textarea = (

          <CommentEditor
            markdown={this.state.markdown}
            onMarkdownChange={this.updateMarkdown}
            style={{
              marginLeft: COMMENTINDENTAMOUNT,
              borderLeft: '3px solid '.concat(COLORARRAY[(this.props.node.depth + 1) % 5]),
            }}
          />

            <RaisedButton label="Post" primary disabled={this.state.markdown === ''} onClick={this.onPostReply} style={{ marginLeft: '20px' }} />
            <div style={{ fontSize: '8pt', opacity: '0.5', textColor: 'grey', }} dangerouslySetInnerHTML={{ __html: marked('\\*\\***bold**\\*\\*  \\__italics_\\_  \\~\\~~~strike~~\\~\\~  \\``code`\\` \\`\\`\\````preformatted```\\`\\`\\` >quote') }} />

      );
    }

    return (

        <MuiThemeProvider muiTheme={muiTheme}>
          <Card>
            <CardText expandable={false}>

              {/* <Avatar  */}
              {/*  src={'https://i.imgur.com/9zgiD0u.jpg'} */}
              {/*    size={35} */}
              {/*  /> */}

              {this.props.authorName}{' '}{this.props.timeSince}
              {madeThisComment ?
                <IconMenu
                  style={{ marginLeft: '96%', marginRight: '25px' }}
                  iconButtonElement={<IconButton><MoreVertIcon /></IconButton>}
                  anchorOrigin={{horizontal: 'right', vertical: 'top'}}
                  targetOrigin={{horizontal: 'right', vertical: 'top'}}
                >
                  <MenuItem primaryText="Edit" onClick={this.handleEditReply}/>
                  <MenuItem primaryText="Delete" onClick={this.handleDeleteReply} />
                </IconMenu> : ''}

                {(this.props.id === 0) ? this.props.articleText : ''}

              {this.props.isEditing && this.props.editId === this.props.commentId ?

                  <CommentEditor
                    markdown={this.state.markdown}
                    onMarkdownChange={this.updateMarkdown}
                    style={{
                      marginLeft: COMMENTINDENTAMOUNT,
                      borderLeft: '3px solid '.concat(COLORARRAY[(this.props.node.depth + 1) % 5]),
                    }}
                  />
                  <RaisedButton label="Post" primary disabled={this.state.markdown === ''} onClick={this.onPostEdit} style={{ marginLeft: '20px' }} />
                  <div
                    style={{ fontSize: '8pt', opacity: '0.5', textColor: 'grey', }}
                    dangerouslySetInnerHTML={{ __html: marked('\\*\\***bold**\\*\\*  \\__italics_\\_  \\~\\~~~strike~~\\~\\~  \\``code`\\` \\`\\`\\````preformatted```\\`\\`\\` >quote') }}
                  >

 :

              }
            </CardText>
            <CardActions>
              <FlatButton label="Reply" onClick={this.onToggleReply} />
            </CardActions>
          </Card>
        </MuiThemeProvider>
        {textarea}

    );
  }
}

/* eslint-disable */

CommentBox.propTypes = {
  articleURI: PropTypes.string.isRequired,
  parentIdx: PropTypes.number.isRequired,
  isVisible: PropTypes.bool.isRequired,
  dispatch: PropTypes.func.isRequired,
  ordering: PropTypes.array.isRequired,
};

CommentBox.defaultProps = {
  isVisible: false,
}

/* eslint-enable */

const mapStateToProps = state => ({
  userId: state.user ? state.user._id : '0',
  editText: state.Discussion.editText,
  isEditing: state.Discussion.isEditing,
  editId : state.Discussion.editId,
  parentIdx: state.Discussion.parentIdx,
  isVisible: state.Discussion.isVisible,
  currentlyOpen: state.Discussion.currentlyOpen,
});

export default connect(
  mapStateToProps,
)(CommentBox);
<commit_msg>changed styling on discussion view<commit_after>import React, { PropTypes, Component } from 'react';
import { connect } from 'react-redux';
import marked from 'marked';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import RaisedButton from 'material-ui/RaisedButton';
import { Card, CardActions, CardText } from 'material-ui/Card';
import { yellow200, grey500 } from 'material-ui/styles/colors';
// import EditIcon from 'material-ui/svg-icons/editor/mode-edit';
// import IconMenu from 'material-ui/IconMenu';
// import MenuItem from 'material-ui/MenuItem';
import IconButton from 'material-ui/IconButton';
import Dialog from 'material-ui/Dialog';
import DeleteIcon from 'material-ui/svg-icons/action/delete';
// import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert';
import { StyleSheet, css } from 'aphrodite';
import CommentEditor from './CommentEditor';
import { muiTheme } from '../styles/styles';
import { Node } from '../produceCommentGraph';

const styles = StyleSheet.create({
  authorLine: {
    display: 'flex',
    justifyContent: 'space-between',
  },
  authorAndTime: {
    display: 'flex',
  },
  authorName: {
    paddingRight: 10,
  },
});

/* eslint-disable */
import { saveReply, deleteReply, editReply, loadDiscussion } from '../actions'; // loadDiscussion
/* eslint-enable */

const COMMENTINDENTAMOUNT = 50;
const COLORARRAY = ['Red', 'Green', 'Blue', 'Yellow', 'Purple'];

class CommentBox extends Component {
  constructor(props) {
    super(props);
    this.state = {
      markdown: '',
      open: false,
    };
    this.ordering = this.props.ordering;
    this.id = props.id;
    this.onToggleReply = this.onToggleReply.bind(this);
    this.onPostReply = this.onPostReply.bind(this);
    this.getLastBeforeEnd = this.getLastBeforeEnd.bind(this);
    this.updateMarkdown = this.updateMarkdown.bind(this);
    this.handleDeleteReply = this.handleDeleteReply.bind(this);
    this.handleEditReply = this.handleEditReply.bind(this);
    this.onPostEdit = this.onPostEdit.bind(this);
  }

  //  style={courses.length > 0 ? 'display: '' : 'display:none'}>

  onToggleReply() {
    this.state = {
      markdown: '',
    };

    let visible = true;
    if (this.props.commentId !== this.props.currentlyOpen) {
      visible = true;
    } else {
      visible = !this.props.isVisible;
    }

    this.props.dispatch({
      type: 'TOGGLE_REPLY',
      parentIdx: this.props.id, // This is the index in the orderings array
      currentlyOpen: (visible ? this.props.commentId : '0'),
      replyText: 'hello',
      isVisible: visible,
      ordering: this.ordering,
      isEditing: false,
    });
  }

  onPostReply() {
    this.props.dispatch({
      type: 'TOGGLE_REPLY',
      parentIdx: this.props.id, // This is the index in the orderings array
      currentlyOpen: '-1',
      replyText: 'hello',
      isVisible: false,
      ordering: this.ordering,
      isEditing: false,
    });

    // const arrayIndex = this.getLastBeforeEnd();   //  this.getLastBeforeEnd(); //  This is where commentBox should be inserted in the array
    //  Splice this node into ordering
    const addedNode = new Node(this.state.markdown);
    addedNode.depth = this.props.node.depth + 1;
    addedNode.parent = this.props.node;

    this.props.dispatch(saveReply(this.state.markdown, addedNode.parent._id, this.props.articleURI));
  }

  onPostEdit() {
    // this.props.dispatch(editReply(this.props.commentId, this.state.markdown));
    // this.props.dispatch({ type: 'EDIT', isEditing: false });

    Promise.resolve(this.props.dispatch(editReply(this.props.commentId, this.state.markdown))).then(() => this.props.dispatch({ type: 'EDIT', isEditing: false }));
    this.props.dispatch(loadDiscussion(this.props.articleURI));
  }

  getLastBeforeEnd() {
    const idx = this.id;
    for (let i = idx + 1; i < this.ordering.length; i += 1) {
      if (this.ordering[i].depth <= this.props.node.depth) {
        return i;
      }
    }
    return this.ordering.length;
  }

  handleOpen = () => {
    this.setState({ open: true });
  };

  handleClose = () => {
    this.setState({ open: false });
  };

  handleDeleteReply() {
    Promise.resolve(this.props.dispatch(deleteReply(this.props.commentId))).then(() => this.props.dispatch(loadDiscussion(this.props.articleURI)));
  }

  handleEditReply() {
    this.setState({ markdown: this.props.node.text });
    this.props.dispatch({ type: 'EDIT', editText: 'Hello', editId: this.props.commentId, isEditing: !this.props.isEditing });
  }

  updateMarkdown(event) {
    this.setState({ markdown: event.target.value });
  }

  render() {
    const actions = [
      <RaisedButton
        label="Cancel"
        onTouchTap={this.handleClose}
        style={{ marginRight: 15 }}
      />,
      <RaisedButton
        label="Delete"
        primary
        keyboardFocused
        onTouchTap={this.handleDeleteReply}
      />,
    ];

    let textarea = ;
    const madeThisComment = this.props.node.author ? (this.props.userId === this.props.node.author._id) : false;

    /* eslint-disable */

    if (this.props.isVisible && this.props.commentId === this.props.currentlyOpen) {
      textarea = (

          <CommentEditor
            markdown={this.state.markdown}
            onMarkdownChange={this.updateMarkdown}
            style={{
              marginLeft: COMMENTINDENTAMOUNT,
              borderLeft: '3px solid '.concat(COLORARRAY[(this.props.node.depth + 1) % 5]),
            }}
          />

            <RaisedButton label="Post" primary disabled={this.state.markdown === ''} onClick={this.onPostReply} style={{ marginLeft: '20px' }} />
            <div style={{ fontSize: '8pt', opacity: '0.5', textColor: 'grey', }} dangerouslySetInnerHTML={{ __html: marked('\\*\\***bold**\\*\\*  \\__italics_\\_  \\~\\~~~strike~~\\~\\~  \\``code`\\` \\`\\`\\````preformatted```\\`\\`\\` >quote') }} />

      );
    }

    return (

        <MuiThemeProvider muiTheme={muiTheme}>
          <Card>
            <CardText expandable={false}>

              {/* <Avatar  */}
              {/*  src={'https://i.imgur.com/9zgiD0u.jpg'} */}
              {/*    size={35} */}
              {/*  /> */}

                    {this.props.authorName}

                  {this.props.timeSince}

                  {madeThisComment ?

                      <IconButton onClick={this.handleOpen}><DeleteIcon hoverColor={grey500} /></IconButton>
                      <Dialog
                        title="Delete this post"
                        actions={actions}
                        modal={false}
                        open={this.state.open}
                        onRequestClose={this.handleClose}
                      >
                        Are you sure you want to delete this post?
                      </Dialog>

                    : '' }

              {/* {madeThisComment ?
                <IconMenu
                  style={{ marginLeft: '96%', marginRight: '25px' }}
                  iconButtonElement={<IconButton><MoreVertIcon /></IconButton>}
                  anchorOrigin={{horizontal: 'right', vertical: 'top'}}
                  targetOrigin={{horizontal: 'right', vertical: 'top'}}
                >
                  <MenuItem primaryText="Edit" onClick={this.handleEditReply}/>
                  <MenuItem primaryText="Delete" onClick={this.handleDeleteReply} />
                </IconMenu> : ''}

 */}

                {(this.props.id === 0) ? this.props.articleText : ''}

              {this.props.isEditing && this.props.editId === this.props.commentId ?

                  <CommentEditor
                    markdown={this.state.markdown}
                    onMarkdownChange={this.updateMarkdown}
                    style={{
                      marginLeft: COMMENTINDENTAMOUNT,
                      borderLeft: '3px solid '.concat(COLORARRAY[(this.props.node.depth + 1) % 5]),
                    }}
                  />
                  <RaisedButton label="Post" primary disabled={this.state.markdown === ''} onClick={this.onPostEdit} style={{ marginLeft: '20px' }} />
                  <div
                    style={{ fontSize: '8pt', opacity: '0.5', textColor: 'grey', }}
                    dangerouslySetInnerHTML={{ __html: marked('\\*\\***bold**\\*\\*  \\__italics_\\_  \\~\\~~~strike~~\\~\\~  \\``code`\\` \\`\\`\\````preformatted```\\`\\`\\` >quote') }}
                  >

 :

              }
            </CardText>
            <CardActions>
              <RaisedButton label="Reply" onClick={this.onToggleReply} />
              {madeThisComment ? <RaisedButton primary label="Edit" onClick={this.handleEditReply} /> : ''}
            </CardActions>
          </Card>
        </MuiThemeProvider>
        {textarea}

    );
  }
}

/* eslint-disable */

CommentBox.propTypes = {
  articleURI: PropTypes.string.isRequired,
  parentIdx: PropTypes.number.isRequired,
  isVisible: PropTypes.bool.isRequired,
  dispatch: PropTypes.func.isRequired,
  ordering: PropTypes.array.isRequired,
};

CommentBox.defaultProps = {
  isVisible: false,
}

/* eslint-enable */

const mapStateToProps = state => ({
  userId: state.user ? state.user._id : '0',
  editText: state.Discussion.editText,
  isEditing: state.Discussion.isEditing,
  editId : state.Discussion.editId,
  parentIdx: state.Discussion.parentIdx,
  isVisible: state.Discussion.isVisible,
  currentlyOpen: state.Discussion.currentlyOpen,
});

export default connect(
  mapStateToProps,
)(CommentBox);
<|endoftext|>


<commit_before>// Features

var Feature = Backbone.Model.extend({});

var FeatureList = Backbone.Collection.extend({});

var FeatureItemView = Backbone.View.extend({

  className: 'feature-item draggable',

  attributes: function() {
    return {
      id: this.model.id,
      'data-name': this.model.get('name'),
      'data-description': this.model.get('description'),
      'data-type': this.model.get('type'),
      'data-targets': '[' + this.model.get('targets') + ']',
    }
  },

  initialize: function() {
    this.model.on({
      'hide': function() { this.$el.hide(); },
      'show': function() { this.$el.show(); },
    }, this);
  },

  render: function() {
    this.$el.text(this.model.get('name'));
    this.$el.draggable({
      helper: 'clone',
      revert: 'invalid',
    });
    return this;
  }

});

var FeatureListView = Backbone.View.extend({

  render: function() {
    this.$el.empty();
    this.collection.forEach(this._addFeatureItem, this);
    return this;
  },

  _addFeatureItem: function(featureItem) {
    var featureItemView = new FeatureItemView({ model: featureItem });
    this.$el.append(featureItemView.render().$el);
  },

  events: {
    'mouseenter .feature-item': '_highlight',
    'mouseleave .feature-item': '_unhighlight',
  },

  _highlight: function(e) {
    $(e.currentTarget).addClass('highlighted');
  },

  _unhighlight: function(e) {
    $(e.currentTarget).removeClass('highlighted');
  },

  filterItems: function(input) {
    this.collection.each(function(i) {
      if (!$.matches(i.get('name').toLowerCase(), input)) {
        i.trigger('hide');
      } else {
        i.trigger('show');
      }
    });
  },

});

// Rules

var Rule = Backbone.Model.extend({

  initialize: function() {
    this.urlRoot = '/rules';
  },

  _update: function(field, attrs, success) {
    this.save(attrs,
              { url: this.url() + '/' + field,
                wait: true,
                success: success,
                error: function(model, xhr, options) {
                  var response = $.parseJSON(xhr.responseText);
                  model.trigger('update-error:' + field, response.message);
                },
              });
  },

  updateName: function(newName) {
    var success = function(model, response, options) {
      model.id = newName;
    };
    this._update('name', { name: newName }, success);
  },

  updateDescription: function(newDescription) {
    var success = function(model, response, options) {};
    this._update('description', { description: newDescription }, success);
  },

});

var RuleView = Backbone.View.extend({

  initialize: function() {
    this.model.on({
      'change:name': function() {
        this._updateURL();
      },
      'change:description': function() {
        this._removeEditControls('description');
        this._renderDescription();
      },
      'update-error:name': function(msg) {
        this._renderAlert('button.name', msg);
      },
      'update-error:description': function(msg) {
        this._renderAlert('button.description', msg);
      },
    }, this);
  },

  render: function() {
    this.$el.empty();
    this.$el.append($.h3('').attr('id', 'rule-name'));
    this.$el.append($.p('').attr('id', 'rule-description'));
    this.$el.append($.div().attr('id', 'rule-lhs'));
    this._renderName();
    this._renderDescription();
    this._renderLHS();
    return this;
  },

  _renderName: function() {
    this.$('#rule-name').text('@' + this.model.get('name'));
  },

  _renderDescription: function() {
    this.$('#rule-description').text(this.model.get('description'));
  },

  _renderLHS: function() {
    var lhsView = new AVMView({ collection: this.model.get('lhs') });
    this.$('#rule-lhs').append(lhsView.render().$el);
    lhsView.trigger('inserted');
  },

  _updateURL: function() {
    window.location.replace(this.model.url() + '/input');
  },

  _renderAlert: function(button, msg) {
    var updateButton = this.$(button);
    updateButton.next('.alert-msg').remove();
    $.alertMsg(msg).insertAfter(updateButton);
  },

  events: {
    'dblclick #rule-name': function(e) {
      this._renderEditControls('name')(e)
    },
    'click button.name': function() {
      this._saveEdits('name')(this)
    },
    'dblclick #rule-description': function(e) {
      this._renderEditControls('description')(e)
    },
    'click button.description': function() {
      this._saveEdits('description')(this)
    },
  },

  _renderEditControls: function(modelField) {
    return function(e) {
      var fieldToEdit = $(e.currentTarget);
      var currentValue = fieldToEdit.text();
      if (modelField === 'name') {
        currentValue = currentValue.substring(1);
      }
      var inputField = $.textInput().addClass(modelField)
        .val(currentValue);
      var okButton = $.okButton().addClass(modelField);
      fieldToEdit.hide();
      inputField.insertAfter(fieldToEdit);
      okButton.insertAfter(inputField);
      inputField.focus();
    };
  },

  _saveEdits: function(modelField) {
    return function(view) {
      var inputField = view.$('input.' + modelField);
      if (!inputField.isEmpty() &&
          inputField.val() !== view.model.get(modelField)) {
        var updateFunction = 'update' + $.cap(modelField);
        view.model[updateFunction](inputField.val());
      } else {
        view._removeEditControls(modelField);
        var renderFunction = '_render' + $.cap(modelField);
        view[renderFunction]();
      }
    };
  },

  _removeEditControls: function(modelField) {
    this.$('.' + modelField).remove();
    this.$('.alert-msg').remove();
    this.$('#rule-' + modelField).show();
  },

});

// AVMs: models

// ...

// AVMs: views

// ...

var AVM = Backbone.Collection.extend({

  initialize: function(models, options) {
    this.ruleName = options.ruleName;
    this.uuid = options.json.uuid;
    this.accept = options.accept || '.feature-item';
    var avm = this;
    _.each(options.json.pairs, (function(pair) {
      avm.add(new Pair(null, { parent: avm,
                               attribute: pair.attribute,
                               value: pair.value }));
    }));
  },

  empty: function() {
    this.each(function(pair) {
      pair.remove();
    });
  },

});

var Pair = Backbone.Model.extend({

  initialize: function(attrs, options) {
    this.parent = options.parent;
    this.set('attribute', new Feature({
      name: options.attribute.name,
      type: options.attribute.type,
      targets: options.attribute.targets
    }));
    if (options.value) {
      this._setValue(options.value);
    }
  },

  _setValue: function(value) {
    if (typeof value === 'string') {
      this.set('value', value);
    } else {
      var accept = '#' + this.get('attribute').get('targets').join(', #');
      this.set('value', new AVM(null, { ruleName: this.parent.ruleName,
                                        accept: accept,
                                        json: value }));
    }
  },

  url: function() {
    return '/rules/' + this.parent.ruleName + '/input';
  },

  create: function() {
    var pair = this;
    var success = function(model, response, options) {
      pair._setValue(model.get('value'));
      pair.parent.add(pair);
    };
    this.save({ uuid: this.parent.uuid },
              { url: this.url(),
                wait: true,
                success: success });
  },

  updateValue: function(newValue) {
    this.save({ uuid: this.parent.uuid,
                newValue: newValue },
              { url: this.url(),
                type: 'PUT',
                wait: true });
  },

  remove: function() {
    this.save({ uuid: this.parent.uuid, },
              { url: this.url(),
                type: 'DELETE',
                wait: true,
                success: function(model, response, options) {
                  model.trigger('remove');
                },
              });
  },

});

var AVMView = Backbone.View.extend({

  className: 'avm',

  attributes: function() {
    return {
      id: this.collection.uuid,
    }
  },

  initialize: function(options) {
    this.parentView = options.parentView;
    if (this.parentView) {
      this.parentView.on({
        'inserted': function() {
          this.trigger('inserted');
        },
      }, this);
    }
    this.on({
      'inserted': function() {
        this.trigger('rendered');
        this._removeBrackets();
        this._renderBrackets();
      },
      'update': function() {
        this._removeBrackets();
        this._renderBrackets();
        this.trigger('re-rendered');
      },
    }, this);
    this.collection.on({
      'add': function(pair) {
        this._renderPair(pair);
        this.trigger('update');
      },
    }, this);
  },

  render: function() {
    this._renderContent();
    this._renderEmptyButton();
    return this;
  },

  _renderBrackets: function() {
    var leftBracket = this._makeBracket('left');
    var rightBracket = this._makeBracket('right');
    var h = this.$el.children('.content').height();
    leftBracket.insertBefore(this.$el.children('.content'));
    rightBracket.insertAfter(this.$el.children('.content'));
    leftBracket.height(h);
    rightBracket.height(h);
  },

  _makeBracket: function(type) {
    return $.div('bracket bracket-' + type);
  },

  _removeBrackets: function() {
    this.$el.children('.bracket').remove();
  },

  _renderPair: function(pair) {
    var placeholder = this.$el.children('.content').children('.placeholder');
    this._makePair(pair).insertBefore(placeholder);
    this.trigger('rendered');
  },

  _renderContent: function() {
    var content = $.div('content');
    this.collection.each(function(pair) {
      content.append(this._makePair(pair));
    }, this);
    content.append(this._makePlaceholder());
    this.$el.append(content);
  },

  _makePair: function(pair) {
    return new PairView({ model: pair, parentView: this }).render().$el;
  },

  _makePlaceholder: function() {
    var view = this;
    return $.placeholder('Drop feature here ...')
      .droppable({
        accept: this.collection.accept,
        drop: function(e, ui) {
          var item = $(ui.helper);
          var parent = view.collection;
          var attribute = {
            name: item.data('name'),
            type: item.data('type'),
            targets: item.dataToArray('targets'),
          };
          new Pair(null, { parent: parent, attribute: attribute }).create();
        },
      });
  },

  _renderEmptyButton: function() {
    this.$el.append($.emptyButton().css('visibility', 'hidden'));
  },

  events: {
    'mouseenter': '_showEmptyButton',
    'mouseleave': '_hideEmptyButton',
    'click .empty-button': '_empty',
  },

  _showEmptyButton: function() {
    this.$('.empty-button').css('visibility', 'visible');
  },

  _hideEmptyButton: function() {
    this.$('.empty-button').css('visibility', 'hidden');
  },

  _empty: function(e) {
    e.stopPropagation();
    this.collection.empty();
  }

});

var PairView = Backbone.View.extend({

  className: 'pair',

  initialize: function(options) {
    this.parentView = options.parentView;
    this.parentView.on({
      'rendered': function() {
        this.trigger('inserted');
      },
    }, this);
    this.model.on({
      'remove': function() {
        this.remove();
        this.parentView.trigger('update');
      }
    }, this);
  },

  render: function() {
    this._renderAttr();
    this._renderVal();
    return this;
  },

  _renderAttr: function() {
    var name = this.model.get('attribute').get('name');
    var attr = $.span('attribute').text(name);
    attr.append($.removeButton(name).css('visibility', 'hidden'));
    this.$el.append(attr);
  },

  _renderVal: function() {
    if (this.model.get('attribute').get('type') === 'complex') {
      this._renderSubstructure();
    } else {
      this._renderValue();
    }
  },

  _renderSubstructure: function() {
    var value = $.div('value');
    var avmView = new AVMView({ collection: this.model.get('value'),
                                parentView: this });
    value.append(avmView.render().$el);
    this.$el.append(value);
    this.listenTo(avmView,
                  're-rendered',
                  function() { this.parentView.trigger('update') });
  },

  _renderValue: function() {
    var value = $.span('value');
    var selectMenu = $.selectMenu();
    var options = this.model.get('attribute').get('targets');
    _.each(options, function(opt) { selectMenu.append($.option(opt)) });
    selectMenu.val(this.model.get('value'));
    value.append(selectMenu);
    this.$el.append(value);
  },

  events: {
    'mouseenter .attribute': '_showRemoveButton',
    'mouseleave .attribute': '_hideRemoveButton',
    'change .value > select': '_changeValue',
    'click .remove-button': '_remove',
  },

  _showRemoveButton: function(e) {
    e.stopPropagation();
    this.$('.remove-button').css('visibility', 'visible');
  },

  _hideRemoveButton: function() {
    this.$('.remove-button').css('visibility', 'hidden');
  },

  _changeValue: function(e) {
    var newValue = $(e.currentTarget).val();
    this.model.updateValue(newValue);
  },

  _remove: function(e) {
    e.stopPropagation();
    this.model.remove();
  }

});

// Application

$(document).ready(function() {

  // "Features" sidebar

  var featureItems = $('.feature-item');

  var featureList = new FeatureList(
    _.map(featureItems, function(i) {
      var item = $(i);
      var name = item.data('name');
      return new Feature({
        id: name,
        name: name,
        type: item.data('type'),
        description: item.data('description'),
        targets: item.dataToArray('targets'),
      });
    }),
    { comparator: 'name' }
  );

  var featureListView = new FeatureListView({
    el: '#feature-list',
    collection: featureList,
  });
  featureListView.render();

  $('#feature-filter').on('keyup', function() {
    var currentInput = $(this).val().toLowerCase();
    featureListView.filterItems(currentInput);
  });

  // Input Builder

  var name = $('#rule-name').text();
  var description = $('#rule-description').text();
  var lhsJSON = $('#rule-lhs').data('json');

  var lhs = new AVM(null, { ruleName: name, json: lhsJSON });

  var rule = new Rule({
    id: name,
    name: name,
    description: description,
    lhs: lhs,
  });

  var ruleView = new RuleView({
    model: rule,
    el: '#interaction-block',
  });
  ruleView.render();

});
<commit_msg>[InputBuilder] Connect InputBuilder to OutputBuilder by adding appropriate link to page header.<commit_after>// Features

var Feature = Backbone.Model.extend({});

var FeatureList = Backbone.Collection.extend({});

var FeatureItemView = Backbone.View.extend({

  className: 'feature-item draggable',

  attributes: function() {
    return {
      id: this.model.id,
      'data-name': this.model.get('name'),
      'data-description': this.model.get('description'),
      'data-type': this.model.get('type'),
      'data-targets': '[' + this.model.get('targets') + ']',
    }
  },

  initialize: function() {
    this.model.on({
      'hide': function() { this.$el.hide(); },
      'show': function() { this.$el.show(); },
    }, this);
  },

  render: function() {
    this.$el.text(this.model.get('name'));
    this.$el.draggable({
      helper: 'clone',
      revert: 'invalid',
    });
    return this;
  }

});

var FeatureListView = Backbone.View.extend({

  render: function() {
    this.$el.empty();
    this.collection.forEach(this._addFeatureItem, this);
    return this;
  },

  _addFeatureItem: function(featureItem) {
    var featureItemView = new FeatureItemView({ model: featureItem });
    this.$el.append(featureItemView.render().$el);
  },

  events: {
    'mouseenter .feature-item': '_highlight',
    'mouseleave .feature-item': '_unhighlight',
  },

  _highlight: function(e) {
    $(e.currentTarget).addClass('highlighted');
  },

  _unhighlight: function(e) {
    $(e.currentTarget).removeClass('highlighted');
  },

  filterItems: function(input) {
    this.collection.each(function(i) {
      if (!$.matches(i.get('name').toLowerCase(), input)) {
        i.trigger('hide');
      } else {
        i.trigger('show');
      }
    });
  },

});

// Rules

var Rule = Backbone.Model.extend({

  initialize: function() {
    this.urlRoot = '/rules';
  },

  _update: function(field, attrs, success) {
    this.save(attrs,
              { url: this.url() + '/' + field,
                wait: true,
                success: success,
                error: function(model, xhr, options) {
                  var response = $.parseJSON(xhr.responseText);
                  model.trigger('update-error:' + field, response.message);
                },
              });
  },

  updateName: function(newName) {
    var success = function(model, response, options) {
      model.id = newName;
    };
    this._update('name', { name: newName }, success);
  },

  updateDescription: function(newDescription) {
    var success = function(model, response, options) {};
    this._update('description', { description: newDescription }, success);
  },

});

var RuleView = Backbone.View.extend({

  initialize: function() {
    this.model.on({
      'change:name': function() {
        this._updateURL();
      },
      'change:description': function() {
        this._removeEditControls('description');
        this._renderDescription();
      },
      'update-error:name': function(msg) {
        this._renderAlert('button.name', msg);
      },
      'update-error:description': function(msg) {
        this._renderAlert('button.description', msg);
      },
    }, this);
  },

  render: function() {
    this.$el.empty();
    this.$el.append($.h3('').attr('id', 'rule-name'));
    this.$el.append($.p('').attr('id', 'rule-description'));
    this.$el.append($.div().attr('id', 'rule-lhs'));
    this._renderName();
    this._renderDescription();
    this._renderLHS();
    return this;
  },

  _renderName: function() {
    this.$('#rule-name').text('@' + this.model.get('name'));
  },

  _renderDescription: function() {
    this.$('#rule-description').text(this.model.get('description'));
  },

  _renderLHS: function() {
    var lhsView = new AVMView({ collection: this.model.get('lhs') });
    this.$('#rule-lhs').append(lhsView.render().$el);
    lhsView.trigger('inserted');
  },

  _updateURL: function() {
    window.location.replace(this.model.url() + '/input');
  },

  _renderAlert: function(button, msg) {
    var updateButton = this.$(button);
    updateButton.next('.alert-msg').remove();
    $.alertMsg(msg).insertAfter(updateButton);
  },

  events: {
    'dblclick #rule-name': function(e) {
      this._renderEditControls('name')(e)
    },
    'click button.name': function() {
      this._saveEdits('name')(this)
    },
    'dblclick #rule-description': function(e) {
      this._renderEditControls('description')(e)
    },
    'click button.description': function() {
      this._saveEdits('description')(this)
    },
  },

  _renderEditControls: function(modelField) {
    return function(e) {
      var fieldToEdit = $(e.currentTarget);
      var currentValue = fieldToEdit.text();
      if (modelField === 'name') {
        currentValue = currentValue.substring(1);
      }
      var inputField = $.textInput().addClass(modelField)
        .val(currentValue);
      var okButton = $.okButton().addClass(modelField);
      fieldToEdit.hide();
      inputField.insertAfter(fieldToEdit);
      okButton.insertAfter(inputField);
      inputField.focus();
    };
  },

  _saveEdits: function(modelField) {
    return function(view) {
      var inputField = view.$('input.' + modelField);
      if (!inputField.isEmpty() &&
          inputField.val() !== view.model.get(modelField)) {
        var updateFunction = 'update' + $.cap(modelField);
        view.model[updateFunction](inputField.val());
      } else {
        view._removeEditControls(modelField);
        var renderFunction = '_render' + $.cap(modelField);
        view[renderFunction]();
      }
    };
  },

  _removeEditControls: function(modelField) {
    this.$('.' + modelField).remove();
    this.$('.alert-msg').remove();
    this.$('#rule-' + modelField).show();
  },

});

// AVMs: models

// ...

// AVMs: views

// ...

var AVM = Backbone.Collection.extend({

  initialize: function(models, options) {
    this.ruleName = options.ruleName;
    this.uuid = options.json.uuid;
    this.accept = options.accept || '.feature-item';
    var avm = this;
    _.each(options.json.pairs, (function(pair) {
      avm.add(new Pair(null, { parent: avm,
                               attribute: pair.attribute,
                               value: pair.value }));
    }));
  },

  empty: function() {
    this.each(function(pair) {
      pair.remove();
    });
  },

});

var Pair = Backbone.Model.extend({

  initialize: function(attrs, options) {
    this.parent = options.parent;
    this.set('attribute', new Feature({
      name: options.attribute.name,
      type: options.attribute.type,
      targets: options.attribute.targets
    }));
    if (options.value) {
      this._setValue(options.value);
    }
  },

  _setValue: function(value) {
    if (typeof value === 'string') {
      this.set('value', value);
    } else {
      var accept = '#' + this.get('attribute').get('targets').join(', #');
      this.set('value', new AVM(null, { ruleName: this.parent.ruleName,
                                        accept: accept,
                                        json: value }));
    }
  },

  url: function() {
    return '/rules/' + this.parent.ruleName + '/input';
  },

  create: function() {
    var pair = this;
    var success = function(model, response, options) {
      pair._setValue(model.get('value'));
      pair.parent.add(pair);
    };
    this.save({ uuid: this.parent.uuid },
              { url: this.url(),
                wait: true,
                success: success });
  },

  updateValue: function(newValue) {
    this.save({ uuid: this.parent.uuid,
                newValue: newValue },
              { url: this.url(),
                type: 'PUT',
                wait: true });
  },

  remove: function() {
    this.save({ uuid: this.parent.uuid, },
              { url: this.url(),
                type: 'DELETE',
                wait: true,
                success: function(model, response, options) {
                  model.trigger('remove');
                },
              });
  },

});

var AVMView = Backbone.View.extend({

  className: 'avm',

  attributes: function() {
    return {
      id: this.collection.uuid,
    }
  },

  initialize: function(options) {
    this.parentView = options.parentView;
    if (this.parentView) {
      this.parentView.on({
        'inserted': function() {
          this.trigger('inserted');
        },
      }, this);
    }
    this.on({
      'inserted': function() {
        this.trigger('rendered');
        this._removeBrackets();
        this._renderBrackets();
      },
      'update': function() {
        this._removeBrackets();
        this._renderBrackets();
        this.trigger('re-rendered');
      },
    }, this);
    this.collection.on({
      'add': function(pair) {
        this._renderPair(pair);
        this.trigger('update');
      },
    }, this);
  },

  render: function() {
    this._renderContent();
    this._renderEmptyButton();
    return this;
  },

  _renderBrackets: function() {
    var leftBracket = this._makeBracket('left');
    var rightBracket = this._makeBracket('right');
    var h = this.$el.children('.content').height();
    leftBracket.insertBefore(this.$el.children('.content'));
    rightBracket.insertAfter(this.$el.children('.content'));
    leftBracket.height(h);
    rightBracket.height(h);
  },

  _makeBracket: function(type) {
    return $.div('bracket bracket-' + type);
  },

  _removeBrackets: function() {
    this.$el.children('.bracket').remove();
  },

  _renderPair: function(pair) {
    var placeholder = this.$el.children('.content').children('.placeholder');
    this._makePair(pair).insertBefore(placeholder);
    this.trigger('rendered');
  },

  _renderContent: function() {
    var content = $.div('content');
    this.collection.each(function(pair) {
      content.append(this._makePair(pair));
    }, this);
    content.append(this._makePlaceholder());
    this.$el.append(content);
  },

  _makePair: function(pair) {
    return new PairView({ model: pair, parentView: this }).render().$el;
  },

  _makePlaceholder: function() {
    var view = this;
    return $.placeholder('Drop feature here ...')
      .droppable({
        accept: this.collection.accept,
        drop: function(e, ui) {
          var item = $(ui.helper);
          var parent = view.collection;
          var attribute = {
            name: item.data('name'),
            type: item.data('type'),
            targets: item.dataToArray('targets'),
          };
          new Pair(null, { parent: parent, attribute: attribute }).create();
        },
      });
  },

  _renderEmptyButton: function() {
    this.$el.append($.emptyButton().css('visibility', 'hidden'));
  },

  events: {
    'mouseenter': '_showEmptyButton',
    'mouseleave': '_hideEmptyButton',
    'click .empty-button': '_empty',
  },

  _showEmptyButton: function() {
    this.$('.empty-button').css('visibility', 'visible');
  },

  _hideEmptyButton: function() {
    this.$('.empty-button').css('visibility', 'hidden');
  },

  _empty: function(e) {
    e.stopPropagation();
    this.collection.empty();
  }

});

var PairView = Backbone.View.extend({

  className: 'pair',

  initialize: function(options) {
    this.parentView = options.parentView;
    this.parentView.on({
      'rendered': function() {
        this.trigger('inserted');
      },
    }, this);
    this.model.on({
      'remove': function() {
        this.remove();
        this.parentView.trigger('update');
      }
    }, this);
  },

  render: function() {
    this._renderAttr();
    this._renderVal();
    return this;
  },

  _renderAttr: function() {
    var name = this.model.get('attribute').get('name');
    var attr = $.span('attribute').text(name);
    attr.append($.removeButton(name).css('visibility', 'hidden'));
    this.$el.append(attr);
  },

  _renderVal: function() {
    if (this.model.get('attribute').get('type') === 'complex') {
      this._renderSubstructure();
    } else {
      this._renderValue();
    }
  },

  _renderSubstructure: function() {
    var value = $.div('value');
    var avmView = new AVMView({ collection: this.model.get('value'),
                                parentView: this });
    value.append(avmView.render().$el);
    this.$el.append(value);
    this.listenTo(avmView,
                  're-rendered',
                  function() { this.parentView.trigger('update') });
  },

  _renderValue: function() {
    var value = $.span('value');
    var selectMenu = $.selectMenu();
    var options = this.model.get('attribute').get('targets');
    _.each(options, function(opt) { selectMenu.append($.option(opt)) });
    selectMenu.val(this.model.get('value'));
    value.append(selectMenu);
    this.$el.append(value);
  },

  events: {
    'mouseenter .attribute': '_showRemoveButton',
    'mouseleave .attribute': '_hideRemoveButton',
    'change .value > select': '_changeValue',
    'click .remove-button': '_remove',
  },

  _showRemoveButton: function(e) {
    e.stopPropagation();
    this.$('.remove-button').css('visibility', 'visible');
  },

  _hideRemoveButton: function() {
    this.$('.remove-button').css('visibility', 'hidden');
  },

  _changeValue: function(e) {
    var newValue = $(e.currentTarget).val();
    this.model.updateValue(newValue);
  },

  _remove: function(e) {
    e.stopPropagation();
    this.model.remove();
  }

});

// Application

$(document).ready(function() {

  // "Features" sidebar

  var featureItems = $('.feature-item');

  var featureList = new FeatureList(
    _.map(featureItems, function(i) {
      var item = $(i);
      var name = item.data('name');
      return new Feature({
        id: name,
        name: name,
        type: item.data('type'),
        description: item.data('description'),
        targets: item.dataToArray('targets'),
      });
    }),
    { comparator: 'name' }
  );

  var featureListView = new FeatureListView({
    el: '#feature-list',
    collection: featureList,
  });
  featureListView.render();

  $('#feature-filter').on('keyup', function() {
    var currentInput = $(this).val().toLowerCase();
    featureListView.filterItems(currentInput);
  });

  // Input Builder

  var name = $('#rule-name').text();
  var description = $('#rule-description').text();
  var lhsJSON = $('#rule-lhs').data('json');

  var lhs = new AVM(null, { ruleName: name, json: lhsJSON });

  var rule = new Rule({
    id: name,
    name: name,
    description: description,
    lhs: lhs,
  });

  var ruleView = new RuleView({
    model: rule,
    el: '#interaction-block',
  });
  ruleView.render();

  // Header

  var navbarButton = $('<li>');
  var navbarLink = $('<a>').attr('href', rule.url() + '/output')
    .text('OutputBuilder');
  navbarButton.append(navbarLink);
  var browseButton = $('.navbar-right').children('li').first();
  navbarButton.insertBefore(browseButton);

});
<|endoftext|>


<commit_before>const _ = require('lodash');
const fountain = require('fountain-generator');

module.exports = fountain.Base.extend({
  prompting() {
    this.fountainPrompting();
  },

  configuring: {
    pkg() {
      const pkg = {
        devDependencies: {
          // https://github.com/babel/babel-eslint/issues/267
          'eslint': '2.2.x',
          'eslint-config-xo-space': '^0.10.0'
        },
        eslintConfig: {
          env: {
            browser: true
          }
        }
      };

      if (this.props.modules === 'webpack') {
        _.merge(pkg, {
          devDependencies: {
            'eslint-loader': '^1.3.0',
            'babel-loader': '^6.2.0'
          }
        });
        if (this.props.framework === 'react') {
          _.merge(pkg, {devDependencies: {'babel-preset-react': '^6.1.18'}});
        }
      } else {
        _.merge(pkg, {devDependencies: {'gulp-eslint': '^2.0.0'}});
      }

      if (this.props.js === 'babel') {
        _.merge(pkg, {
          devDependencies: {
            'babel-eslint': '^5.0.0',
            'eslint-plugin-babel': '^3.1.0'
          },
          eslintConfig: {
            extends: [
              'xo-space/esnext'
            ]
          }
        });
      }

      if (this.props.js === 'js') {
        _.merge(pkg, {
          eslintConfig: {
            extends: [
              'xo-space'
            ]
          }
        });
      }

      if (this.props.framework === 'angular2' && this.props.js !== 'typescript') {
        _.merge(pkg, {
          eslintConfig: {
            rules: {
              'new-cap': [2, {capIsNewExceptions: ['Input', 'Output', 'Component', 'View', 'Injectable', 'RouteConfig', 'Class']}]
            }
          }
        });
      }

      if (this.props.modules === 'inject') {
        if (this.props.framework === 'angular1') {
          _.merge(pkg, {
            eslintConfig: {
              globals: {
                angular: true
              }
            }
          });
        }
        if (this.props.js === 'babel' || this.props.js === 'js' && this.props.framework === 'react') {
          _.merge(pkg, {devDependencies: {'gulp-babel': '^6.1.0'}});
        }
      }

      if (this.props.modules === 'systemjs') {
        _.merge(pkg, {eslintConfig: {globals: {SystemJS: true}}});

        if (this.props.framework === 'angular2') {
          _.merge(pkg, {
            jspm: {
              devDependencies: {
                'babel-plugin-angular2-annotations': 'npm:babel-plugin-angular2-annotations@^5.0.0',
                'babel-plugin-transform-decorators-legacy': 'npm:babel-plugin-transform-decorators-legacy@^1.3.4',
                'babel-plugin-transform-class-properties': 'npm:babel-plugin-transform-class-properties@^6.6.0',
                'babel-plugin-transform-flow-strip-types': 'npm:babel-plugin-transform-flow-strip-types@^6.6.4'
              }
            }
          });
        } else if (this.props.framework === 'react') {
          _.merge(pkg, {jspm: {devDependencies: {'babel-preset-react': 'npm:babel-preset-react@6.5.0'}}});
        }

        if (this.props.js === 'babel' || this.props.js === 'js' && this.props.framework === 'react') {
          _.merge(pkg, {devDependencies: {'babel-core': '^6.2.0'}});
        }

        if (this.props.js === 'babel') {
          _.merge(pkg, {devDependencies: {'babel-preset-es2015': '^6.2.0'}});
        }

        if (this.props.js !== 'typescript') {
          _.merge(pkg, {
            jspm: {
              dependencies: {babel: 'npm:babel-core@^6.6.5'},
              devDependencies: {'plugin-babel': 'npm:systemjs-plugin-babel@^0.0.8'}
            }
          });
        }
      }

      this.mergeJson('package.json', pkg);
    }
  },

  writing: {
    wireing() {
      if (this.props.modules === 'webpack' && this.props.js !== 'typescript') {
        this.replaceInFileWithTemplate(
          'conf/webpack.conf.js',
          'conf/webpack.conf.js',
          / {2}module: \{/
        );
        this.replaceInFileWithTemplate(
          'conf/webpack.conf.js',
          'conf/webpack-test.conf.js',
          / {2}module: \{/
        );
      } else if (this.props.modules === 'systemjs') {
        this.copyTemplate(
          'gulp_tasks/scripts-full.js',
          'gulp_tasks/scripts.js'
        );
      } else if (this.props.modules === 'inject' && this.props.js !== 'typescript') {
        this.replaceInFileWithTemplate(
          'gulp_tasks/scripts-require.js',
          'gulp_tasks/scripts.js',
          /const gulp = require\('gulp'\);/
        );
        this.replaceInFileWithTemplate(
          'gulp_tasks/scripts-stream.js',
          'gulp_tasks/scripts.js',
          / {2}return gulp\.src[^\n]*/
        );
      }
    }
  }
});
<commit_msg>Add caret before version number<commit_after>const _ = require('lodash');
const fountain = require('fountain-generator');

module.exports = fountain.Base.extend({
  prompting() {
    this.fountainPrompting();
  },

  configuring: {
    pkg() {
      const pkg = {
        devDependencies: {
          // https://github.com/babel/babel-eslint/issues/267
          'eslint': '2.2.x',
          'eslint-config-xo-space': '^0.10.0'
        },
        eslintConfig: {
          env: {
            browser: true
          }
        }
      };

      if (this.props.modules === 'webpack') {
        _.merge(pkg, {
          devDependencies: {
            'eslint-loader': '^1.3.0',
            'babel-loader': '^6.2.0'
          }
        });
        if (this.props.framework === 'react') {
          _.merge(pkg, {devDependencies: {'babel-preset-react': '^6.1.18'}});
        }
      } else {
        _.merge(pkg, {devDependencies: {'gulp-eslint': '^2.0.0'}});
      }

      if (this.props.js === 'babel') {
        _.merge(pkg, {
          devDependencies: {
            'babel-eslint': '^5.0.0',
            'eslint-plugin-babel': '^3.1.0'
          },
          eslintConfig: {
            extends: [
              'xo-space/esnext'
            ]
          }
        });
      }

      if (this.props.js === 'js') {
        _.merge(pkg, {
          eslintConfig: {
            extends: [
              'xo-space'
            ]
          }
        });
      }

      if (this.props.framework === 'angular2' && this.props.js !== 'typescript') {
        _.merge(pkg, {
          eslintConfig: {
            rules: {
              'new-cap': [2, {capIsNewExceptions: ['Input', 'Output', 'Component', 'View', 'Injectable', 'RouteConfig', 'Class']}]
            }
          }
        });
      }

      if (this.props.modules === 'inject') {
        if (this.props.framework === 'angular1') {
          _.merge(pkg, {
            eslintConfig: {
              globals: {
                angular: true
              }
            }
          });
        }
        if (this.props.js === 'babel' || this.props.js === 'js' && this.props.framework === 'react') {
          _.merge(pkg, {devDependencies: {'gulp-babel': '^6.1.0'}});
        }
      }

      if (this.props.modules === 'systemjs') {
        _.merge(pkg, {eslintConfig: {globals: {SystemJS: true}}});

        if (this.props.framework === 'angular2') {
          _.merge(pkg, {
            jspm: {
              devDependencies: {
                'babel-plugin-angular2-annotations': 'npm:babel-plugin-angular2-annotations@^5.0.0',
                'babel-plugin-transform-decorators-legacy': 'npm:babel-plugin-transform-decorators-legacy@^1.3.4',
                'babel-plugin-transform-class-properties': 'npm:babel-plugin-transform-class-properties@^6.6.0',
                'babel-plugin-transform-flow-strip-types': 'npm:babel-plugin-transform-flow-strip-types@^6.6.4'
              }
            }
          });
        } else if (this.props.framework === 'react') {
          _.merge(pkg, {jspm: {devDependencies: {'babel-preset-react': 'npm:babel-preset-react@^6.5.0'}}});
        }

        if (this.props.js === 'babel' || this.props.js === 'js' && this.props.framework === 'react') {
          _.merge(pkg, {devDependencies: {'babel-core': '^6.2.0'}});
        }

        if (this.props.js === 'babel') {
          _.merge(pkg, {devDependencies: {'babel-preset-es2015': '^6.2.0'}});
        }

        if (this.props.js !== 'typescript') {
          _.merge(pkg, {
            jspm: {
              dependencies: {babel: 'npm:babel-core@^6.6.5'},
              devDependencies: {'plugin-babel': 'npm:systemjs-plugin-babel@^0.0.8'}
            }
          });
        }
      }

      this.mergeJson('package.json', pkg);
    }
  },

  writing: {
    wireing() {
      if (this.props.modules === 'webpack' && this.props.js !== 'typescript') {
        this.replaceInFileWithTemplate(
          'conf/webpack.conf.js',
          'conf/webpack.conf.js',
          / {2}module: \{/
        );
        this.replaceInFileWithTemplate(
          'conf/webpack.conf.js',
          'conf/webpack-test.conf.js',
          / {2}module: \{/
        );
      } else if (this.props.modules === 'systemjs') {
        this.copyTemplate(
          'gulp_tasks/scripts-full.js',
          'gulp_tasks/scripts.js'
        );
      } else if (this.props.modules === 'inject' && this.props.js !== 'typescript') {
        this.replaceInFileWithTemplate(
          'gulp_tasks/scripts-require.js',
          'gulp_tasks/scripts.js',
          /const gulp = require\('gulp'\);/
        );
        this.replaceInFileWithTemplate(
          'gulp_tasks/scripts-stream.js',
          'gulp_tasks/scripts.js',
          / {2}return gulp\.src[^\n]*/
        );
      }
    }
  }
});
<|endoftext|>


<commit_before>'use strict';

//TODO:
//  -Use real img src
//  -CHECK - Styling on the left side of the results item needs padding fixed.
//  -Handle architecture properties
//  -Share, Directions, & Search
//  -Handle click event on item when isSearchResult === true to select item

var resultItem = {
  create: function (options) {
    this._options = options;
    var rhc = options.isSearchResult ? this._createSearchRHC() : this._createDetailRHC();
    var resultItemHTML = '
' +
      '
' +
        '
' + options.props.title + '
' +
        '
' + options.props.author + '
' +
        '
' + options.props.year + '
' +
      '
' +
      rhc +
    '
';
    options.container.innerHTML += resultItemHTML;
  },

  _createSearchRHC: function () {
    return '
' +
        '' +
      '
';
  },

  _createDetailRHC: function () {
    return '
' +
      '
' +
        '

' +
        '

Learn More

' +
        '

Directions

' +
      '
' +
    '
';
  }
};

module.exports = resultItem;<commit_msg>removed image change<commit_after>'use strict';

//TODO:
//  -Use real img src
//  -CHECK - Styling on the left side of the results item needs padding fixed.
//  -Handle architecture properties
//  -Share, Directions, & Search
//  -Handle click event on item when isSearchResult === true to select item

var resultItem = {
  create: function (options) {
    this._options = options;
    var rhc = options.isSearchResult ? this._createSearchRHC() : this._createDetailRHC();
    var resultItemHTML = '
' +
      '
' +
        '
' + options.props.title + '
' +
        '
' + options.props.author + '
' +
        '
' + options.props.year + '
' +
      '
' +
      rhc +
    '
';
    options.container.innerHTML += resultItemHTML;
  },

  _createSearchRHC: function () {
    return '
' +
        '' +
      '
';
  },

  _createDetailRHC: function () {
    return '
' +
      '
' +
        '

' +
        '

Learn More

' +
        '

Directions

' +
      '
' +
    '
';
  }
};

module.exports = resultItem;<|endoftext|>


<commit_before>// <details> polyfill
// http://caniuse.com/#feat=details

// FF Support for HTML5's <details> and <summary>
// https://bugzilla.mozilla.org/show_bug.cgi?id=591737

// http://www.sitepoint.com/fixing-the-details-element/

(function () {
  'use strict';

  // Add event construct for modern browsers or IE
  // which fires the callback with a pre-converted target reference
  function addEvent(node, type, callback) {
    if (node.addEventListener) {
      node.addEventListener(type, function (e) {
        callback(e, e.target);
      }, false);
    } else if (node.attachEvent) {
      node.attachEvent('on' + type, function (e) {
        callback(e, e.srcElement);
      });
    }
  }

  // Handle cross-modal click events
  function addClickEvent(node, callback) {
    // Prevent space(32) from scrolling the page
    addEvent(node, 'keypress', function (e, target) {
      if (target.nodeName === "SUMMARY") {
        if (e.keyCode === 32) {
          if (e.preventDefault) {
            e.preventDefault();
          } else {
            e.returnValue = false;
          }
        }
      }
    });
    // When the key comes up - check if it is enter(13) or space(32)
    addEvent(node, 'keyup', function (e, target) {
      if (e.keyCode === 13 || e.keyCode === 32) { callback(e, target); }
    });
    addEvent(node, 'mouseup', function (e, target) {
      callback(e, target);
    });
  }

  // Get the nearest ancestor element of a node that matches a given tag name
  function getAncestor(node, match) {
    do {
      if (!node || node.nodeName.toLowerCase() === match) {
        break;
      }
    } while (node = node.parentNode);

    return node;
  }

  // Create a started flag so we can prevent the initialisation
  // function firing from both DOMContentLoaded and window.onload
  var started = false;

  // Initialisation function
  function addDetailsPolyfill(list) {

    // If this has already happened, just return
    // else set the flag so it doesn't happen again
    if (started) {
      return;
    }
    started = true;

    // Get the collection of details elements, but if that's empty
    // then we don't need to bother with the rest of the scripting
    if ((list = document.getElementsByTagName('details')).length === 0) {
      return;
    }

    // else iterate through them to apply their initial state
    var n = list.length, i = 0;
    for (n; i < n; i++) {
      var details = list[i];

      // Detect native implementations
      details.__native = typeof(details.open) == 'boolean';

      // Save shortcuts to the inner summary and content elements
      details.__summary = details.getElementsByTagName('summary').item(0);
      details.__content = details.getElementsByTagName('div').item(0);

      // If the content doesn't have an ID, assign it one now
      // which we'll need for the summary's aria-controls assignment
      if (!details.__content.id) {
        details.__content.id = 'details-content-' + i;
      }

      // Add ARIA role="group" to details
      details.setAttribute('role', 'group');

      // Add role=button to summary
      details.__summary.setAttribute('role', 'button');

      // Add aria-controls
      details.__summary.setAttribute('aria-controls', details.__content.id);

      // Set tabindex so the summary is keyboard accessible
      // details.__summary.setAttribute('tabindex', 0);
      // http://www.saliences.com/browserBugs/tabIndex.html
      details.__summary.tabIndex = 0;

      // Detect initial open/closed state

      // Native support - has 'open' attribute
      if (details.open === true) {
        details.__summary.setAttribute('aria-expanded', 'true');
        details.__content.setAttribute('aria-hidden', 'false');
        details.__content.style.display = 'block';
      }

      // Native support - doesn't have 'open' attribute
      if (details.open === false) {
        details.__summary.setAttribute('aria-expanded', 'false');
        details.__content.setAttribute('aria-hidden', 'true');
        details.__content.style.display = 'none';
      }

      // If this is not a native implementation
      if (!details.__native) {

        // Add an arrow
        var twisty = document.createElement('i');

        // Check for the 'open' attribute
        // If open exists, but isn't supported it won't have a value
        if (details.getAttribute('open') === "") {
          details.__summary.setAttribute('aria-expanded', 'true');
          details.__content.setAttribute('aria-hidden', 'false');
        }

        // If open doesn't exist - it will be null or undefined
        if (details.getAttribute('open') == null || details.getAttribute('open') == "undefined" ) {
          details.__summary.setAttribute('aria-expanded', 'false');
          details.__content.setAttribute('aria-hidden', 'true');
          details.__content.style.display = 'none';
        }

      }

      // Create a circular reference from the summary back to its
      // parent details element, for convenience in the click handler
      details.__summary.__details = details;

      // If this is not a native implementation, create an arrow
      // inside the summary
      if (!details.__native) {

        var twisty = document.createElement('i');

        if (details.getAttribute('open') === "") {
          twisty.className = 'arrow arrow-open';
          twisty.appendChild(document.createTextNode('\u25bc'));
        } else {
          twisty.className = 'arrow arrow-closed';
          twisty.appendChild(document.createTextNode('\u25ba'));
        }

        details.__summary.__twisty = details.__summary.insertBefore(twisty, details.__summary.firstChild);
        details.__summary.__twisty.setAttribute('aria-hidden', 'true');

      }
    }

    // Define a statechange function that updates aria-expanded and style.display
    // Also update the arrow position
    function statechange(summary) {

      var expanded = summary.__details.__summary.getAttribute('aria-expanded') == 'true';
      var hidden = summary.__details.__content.getAttribute('aria-hidden') == 'true';

      summary.__details.__summary.setAttribute('aria-expanded', (expanded ? 'false' : 'true'));
      summary.__details.__content.setAttribute('aria-hidden', (hidden ? 'false' : 'true'));
      summary.__details.__content.style.display = (expanded ? 'none' : 'block');

      if (summary.__twisty) {
        summary.__twisty.firstChild.nodeValue = (expanded ? '\u25ba' : '\u25bc');
        summary.__twisty.setAttribute('class', (expanded ? 'arrow arrow-closed' : 'arrow arrow-open'));
      }

      return true;
    }

    // Bind a click event to handle summary elements
    addClickEvent(document, function(e, summary) {
      if (!(summary = getAncestor(summary, 'summary'))) {
        return true;
      }
      return statechange(summary);
    });
  }

  // Bind two load events for modern and older browsers
  // If the first one fires it will set a flag to block the second one
  // but if it's not supported then the second one will fire
  addEvent(document, 'DOMContentLoaded', addDetailsPolyfill);
  addEvent(window, 'load', addDetailsPolyfill);

})();
<commit_msg>Only use single quotes.<commit_after>// <details> polyfill
// http://caniuse.com/#feat=details

// FF Support for HTML5's <details> and <summary>
// https://bugzilla.mozilla.org/show_bug.cgi?id=591737

// http://www.sitepoint.com/fixing-the-details-element/

(function () {
  'use strict';

  // Add event construct for modern browsers or IE
  // which fires the callback with a pre-converted target reference
  function addEvent(node, type, callback) {
    if (node.addEventListener) {
      node.addEventListener(type, function (e) {
        callback(e, e.target);
      }, false);
    } else if (node.attachEvent) {
      node.attachEvent('on' + type, function (e) {
        callback(e, e.srcElement);
      });
    }
  }

  // Handle cross-modal click events
  function addClickEvent(node, callback) {
    // Prevent space(32) from scrolling the page
    addEvent(node, 'keypress', function (e, target) {
      if (target.nodeName === 'SUMMARY') {
        if (e.keyCode === 32) {
          if (e.preventDefault) {
            e.preventDefault();
          } else {
            e.returnValue = false;
          }
        }
      }
    });
    // When the key comes up - check if it is enter(13) or space(32)
    addEvent(node, 'keyup', function (e, target) {
      if (e.keyCode === 13 || e.keyCode === 32) { callback(e, target); }
    });
    addEvent(node, 'mouseup', function (e, target) {
      callback(e, target);
    });
  }

  // Get the nearest ancestor element of a node that matches a given tag name
  function getAncestor(node, match) {
    do {
      if (!node || node.nodeName.toLowerCase() === match) {
        break;
      }
    } while (node = node.parentNode);

    return node;
  }

  // Create a started flag so we can prevent the initialisation
  // function firing from both DOMContentLoaded and window.onload
  var started = false;

  // Initialisation function
  function addDetailsPolyfill(list) {

    // If this has already happened, just return
    // else set the flag so it doesn't happen again
    if (started) {
      return;
    }
    started = true;

    // Get the collection of details elements, but if that's empty
    // then we don't need to bother with the rest of the scripting
    if ((list = document.getElementsByTagName('details')).length === 0) {
      return;
    }

    // else iterate through them to apply their initial state
    var n = list.length, i = 0;
    for (n; i < n; i++) {
      var details = list[i];

      // Detect native implementations
      details.__native = typeof(details.open) == 'boolean';

      // Save shortcuts to the inner summary and content elements
      details.__summary = details.getElementsByTagName('summary').item(0);
      details.__content = details.getElementsByTagName('div').item(0);

      // If the content doesn't have an ID, assign it one now
      // which we'll need for the summary's aria-controls assignment
      if (!details.__content.id) {
        details.__content.id = 'details-content-' + i;
      }

      // Add ARIA role="group" to details
      details.setAttribute('role', 'group');

      // Add role=button to summary
      details.__summary.setAttribute('role', 'button');

      // Add aria-controls
      details.__summary.setAttribute('aria-controls', details.__content.id);

      // Set tabindex so the summary is keyboard accessible
      // details.__summary.setAttribute('tabindex', 0);
      // http://www.saliences.com/browserBugs/tabIndex.html
      details.__summary.tabIndex = 0;

      // Detect initial open/closed state

      // Native support - has 'open' attribute
      if (details.open === true) {
        details.__summary.setAttribute('aria-expanded', 'true');
        details.__content.setAttribute('aria-hidden', 'false');
        details.__content.style.display = 'block';
      }

      // Native support - doesn't have 'open' attribute
      if (details.open === false) {
        details.__summary.setAttribute('aria-expanded', 'false');
        details.__content.setAttribute('aria-hidden', 'true');
        details.__content.style.display = 'none';
      }

      // If this is not a native implementation
      if (!details.__native) {

        // Add an arrow
        var twisty = document.createElement('i');

        // Check for the 'open' attribute
        // If open exists, but isn't supported it won't have a value
        if (details.getAttribute('open') === "") {
          details.__summary.setAttribute('aria-expanded', 'true');
          details.__content.setAttribute('aria-hidden', 'false');
        }

        // If open doesn't exist - it will be null or undefined
        if (details.getAttribute('open') == null || details.getAttribute('open') == "undefined" ) {
          details.__summary.setAttribute('aria-expanded', 'false');
          details.__content.setAttribute('aria-hidden', 'true');
          details.__content.style.display = 'none';
        }

      }

      // Create a circular reference from the summary back to its
      // parent details element, for convenience in the click handler
      details.__summary.__details = details;

      // If this is not a native implementation, create an arrow
      // inside the summary
      if (!details.__native) {

        var twisty = document.createElement('i');

        if (details.getAttribute('open') === "") {
          twisty.className = 'arrow arrow-open';
          twisty.appendChild(document.createTextNode('\u25bc'));
        } else {
          twisty.className = 'arrow arrow-closed';
          twisty.appendChild(document.createTextNode('\u25ba'));
        }

        details.__summary.__twisty = details.__summary.insertBefore(twisty, details.__summary.firstChild);
        details.__summary.__twisty.setAttribute('aria-hidden', 'true');

      }
    }

    // Define a statechange function that updates aria-expanded and style.display
    // Also update the arrow position
    function statechange(summary) {

      var expanded = summary.__details.__summary.getAttribute('aria-expanded') == 'true';
      var hidden = summary.__details.__content.getAttribute('aria-hidden') == 'true';

      summary.__details.__summary.setAttribute('aria-expanded', (expanded ? 'false' : 'true'));
      summary.__details.__content.setAttribute('aria-hidden', (hidden ? 'false' : 'true'));
      summary.__details.__content.style.display = (expanded ? 'none' : 'block');

      if (summary.__twisty) {
        summary.__twisty.firstChild.nodeValue = (expanded ? '\u25ba' : '\u25bc');
        summary.__twisty.setAttribute('class', (expanded ? 'arrow arrow-closed' : 'arrow arrow-open'));
      }

      return true;
    }

    // Bind a click event to handle summary elements
    addClickEvent(document, function(e, summary) {
      if (!(summary = getAncestor(summary, 'summary'))) {
        return true;
      }
      return statechange(summary);
    });
  }

  // Bind two load events for modern and older browsers
  // If the first one fires it will set a flag to block the second one
  // but if it's not supported then the second one will fire
  addEvent(document, 'DOMContentLoaded', addDetailsPolyfill);
  addEvent(window, 'load', addDetailsPolyfill);

})();
<|endoftext|>