<commit_before>/*
 * ES6/jQuery accessible accordion system, using ARIA
 * Website: http://a11y.nicolas-hoffmann.net/accordion/
 * License MIT: https://github.com/nico3333fr/jquery-accessible-hide-show-aria/blob/master/LICENSE
 */
;(doc => {

  'use strict';

  const ACCORDION_JS = 'js-accordion';
  const ACCORDION_JS_HEADER = 'js-accordion__header';
  const ACCORDION_JS_PANEL = 'js-accordion__panel';

  const ACCORDION_DATA_PREFIX_CLASS = 'data-accordion-prefix-classes';
  const ACCORDION_DATA_OPENED = 'data-accordion-opened';
  const ACCORDION_DATA_MULTISELECTABLE = 'data-accordion-multiselectable';

  const ACCORDION_PREFIX_IDS = 'accordion';
  const ACCORDION_BUTTON_ID = '_tab';
  const ACCORDION_PANEL_ID = '_panel';

  const ACCORDION_STYLE = 'accordion';
  const ACCORDION_TITLE_STYLE = 'accordion__title';
  const ACCORDION_HEADER_STYLE = 'accordion__header';
  const ACCORDION_PANEL_STYLE = 'accordion__panel';

  const ACCORDION_ROLE_TABLIST = 'tablist';
  const ACCORDION_ROLE_TAB = 'tab';
  const ACCORDION_ROLE_TABPANEL = 'tabpanel';

  const ATTR_ROLE = 'role';
  const ATTR_MULTISELECTABLE = 'aria-multiselectable';
  const ATTR_EXPANDED = 'aria-expanded';
  const ATTR_LABELLEDBY = 'aria-labelledby';
  const ATTR_HIDDEN = 'aria-hidden';
  const ATTR_CONTROLS = 'aria-controls';
  const ATTR_SELECTED = 'aria-selected';

  const IS_OPENED_CLASS = 'is-opened';

  const findById = id => doc.getElementById(id);

  const addClass = (el, className) => {
        if (el.classList) {
          el.classList.add(className); // IE 10+
        }
        else {
             el.className += ' ' + className; // IE 8+
             }
        }

  const removeClass = (el, className) => {
        if (el.classList) {
          el.classList.remove(className); // IE 10+
        }
        else {
             el.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' '); // IE 8+
             }
        }

  const hasClass = (el, className) => {
        if (el.classList) {
          return el.classList.contains(className); // IE 10+
        }
        else {
             return new RegExp('(^| )' + className + '( |$)', 'gi').test(el.className); // IE 8+ ?
             }
        }

  const setAttributes = (node, attrs) => {
        Object
          .keys(attrs)
          .forEach((attribute) => {
            node.setAttribute(attribute, attrs[attribute]);
          });
        };
/*  const triggerEvent = (el, event_type) => {
        if (el.fireEvent) {
          el.fireEvent('on' + event_type);
        }
        else {
              let evObj = document.createEvent('Events');
              evObj.initEvent(event_type, true, false);
              el.dispatchEvent(evObj);
             }
        }*/
  const unSelectHeaders = (elts) => {
        elts
           .forEach((header_node, index) => {
             setAttributes(header_node, {
               [ATTR_SELECTED] : 'false' ,
               'tabindex' : '-1'
             });
           });
        }

  const selectHeader = (el) => {
        el.setAttribute( ATTR_SELECTED , true );
        el.removeAttribute ( 'tabindex' );
        }

  const selectHeaderInList = (elts, param) => {
        let indice_trouve;
        elts
           .forEach((header_node, index) => {

                if ( header_node.getAttribute(ATTR_SELECTED) === 'true' ) {
                   indice_trouve = index;
                   }

           });
        if ( param === 'next' ){
            selectHeader ( elts[indice_trouve+1] );
            setTimeout(function(){ elts[indice_trouve+1].focus(); }, 0);
            }
        if ( param === 'prev' ){
            selectHeader ( elts[indice_trouve-1] );
            setTimeout(function(){ elts[indice_trouve-1].focus(); }, 0);
            }

        }

  /* gets an element el, search if it is child of parent class, returns id of the parent */
  let searchParent = ( el, parentClass ) => {
        let found = false;
        let parentElement = el.parentNode;
        while ( parentElement && found === false ) {
            if ( hasClass( parentElement, parentClass ) === true ) {
                found = true;
                }
                else {
                     parentElement = parentElement.parentNode;
                     }
            }
        if ( found === true ) {
           return parentElement.getAttribute('id');
           }
           else { return ''; }
  }

  // Find all accordions
  const $listAccordions = () => [].slice.call(doc.querySelectorAll('.' + ACCORDION_JS));

  const onLoad = () => {

  $listAccordions()
    .forEach((accordion_node, index) => {

      let iLisible = index + 1;
      let prefixClassName = accordion_node.hasAttribute(ACCORDION_DATA_PREFIX_CLASS) === true ? accordion_node.getAttribute(ACCORDION_DATA_PREFIX_CLASS) + '-' : '' ;
      let accordionClassName = accordion_node.hasAttribute(ACCORDION_DATA_PREFIX_CLASS) === true ? accordion_node.getAttribute(ACCORDION_DATA_PREFIX_CLASS) : '' ;
      let multiSelectableAttribute = accordion_node.hasAttribute(ACCORDION_DATA_MULTISELECTABLE) === true ? accordion_node.getAttribute(ACCORDION_DATA_MULTISELECTABLE) : '' ;
      /*let toExpand = node.nextElementSibling;
      let expandmoreText = node.innerHTML;
      let expandButton = doc.createElement("BUTTON");*/

      // clear element before adding button to it
      //node.innerHTML = '';

      // Init attributes accordion
      if (accordion_node.getAttribute(ACCORDION_DATA_MULTISELECTABLE) == 'none'){
          accordion_node.setAttribute( ATTR_MULTISELECTABLE , 'false' );
         }
         else {
               accordion_node.setAttribute( ATTR_MULTISELECTABLE , 'true' );
              }
      accordion_node.setAttribute( ATTR_ROLE , ACCORDION_ROLE_TABLIST );
      addClass( accordion_node, prefixClassName + ACCORDION_STYLE);

      let $listAccordionsHeader = [].slice.call(accordion_node.querySelectorAll('.' + ACCORDION_JS_HEADER));
      $listAccordionsHeader
        .forEach((header_node, index_header) => {

            let indexHeaderLisible = index_header+1;
            let accordionPanel = header_node.nextElementSibling;
            let accordionHeaderText = header_node.innerHTML;
            let accordionButton = document.createElement("BUTTON");
            let accordionOpenedAttribute = header_node.hasAttribute(ACCORDION_DATA_OPENED) === true ? header_node.getAttribute(ACCORDION_DATA_OPENED) : '' ;

            // set button with attributes
            accordionButton.innerHTML = accordionHeaderText;
            addClass( accordionButton, ACCORDION_JS_HEADER );
            addClass( accordionButton, prefixClassName + ACCORDION_HEADER_STYLE );
            setAttributes(accordionButton, {
              [ATTR_ROLE] : ACCORDION_ROLE_TAB ,
              'id' : ACCORDION_PREFIX_IDS + iLisible + ACCORDION_BUTTON_ID + indexHeaderLisible ,
              [ATTR_CONTROLS] : ACCORDION_PREFIX_IDS + iLisible + ACCORDION_PANEL_ID + indexHeaderLisible ,
              [ATTR_SELECTED] : 'false' ,
              'tabindex' : '-1'
             });

            // place button
            accordionButton = accordion_node.insertBefore(accordionButton, header_node);

            // move title into panel
            accordionPanel.insertBefore(header_node, accordionPanel.firstChild);
            // set title with attributes
            addClass( header_node, prefixClassName + ACCORDION_TITLE_STYLE );
            removeClass ( header_node, ACCORDION_JS_HEADER );

            // set attributes to panels
            addClass( accordionPanel, prefixClassName + ACCORDION_PANEL_STYLE);
            setAttributes(accordionPanel, {
              [ATTR_ROLE] : ACCORDION_ROLE_TABPANEL ,
              [ATTR_LABELLEDBY] : ACCORDION_PREFIX_IDS + iLisible + ACCORDION_BUTTON_ID + indexHeaderLisible ,
              'id' : ACCORDION_PREFIX_IDS + iLisible + ACCORDION_PANEL_ID + indexHeaderLisible
             });

            if (accordionOpenedAttribute === 'true'){
               accordionButton.setAttribute( ATTR_EXPANDED , 'true' );
               header_node.removeAttribute( ACCORDION_DATA_OPENED );
               accordionPanel.setAttribute( ATTR_HIDDEN , 'false' );
               }
               else {
                     accordionButton.setAttribute( ATTR_EXPANDED , 'false' );
                     accordionPanel.setAttribute( ATTR_HIDDEN , 'true' );
                    }

            // init first one focusable
            if ( indexHeaderLisible === 1 ) {
                accordionButton.removeAttribute( 'tabindex' );
                }

        });

    });

  // click on
  ['click', 'keydown', 'focus']
    .forEach(eventName => {
       let isCtrl = false;

       doc.body
          .addEventListener(eventName, e => {

             // focus on button
             if ( hasClass( e.target, ACCORDION_JS_HEADER) === true && eventName === 'focus' ) {
                 let buttonTag = e.target;
                 let accordionContainer = buttonTag.parentNode;
                 let $accordionAllHeaders = [].slice.call(accordionContainer.querySelectorAll('.' + ACCORDION_JS_HEADER));

                 unSelectHeaders($accordionAllHeaders);

                 selectHeader(buttonTag);

                }

             // click on button
             if ( hasClass( e.target, ACCORDION_JS_HEADER) === true && eventName === 'click' ) {
                let buttonTag = e.target;
                let accordionContainer = buttonTag.parentNode;
                let $accordionAllHeaders = [].slice.call(accordionContainer.querySelectorAll('.' + ACCORDION_JS_HEADER));
                let accordionMultiSelectable = accordionContainer.getAttribute(ATTR_MULTISELECTABLE);
                let destination = findById(buttonTag.getAttribute( ATTR_CONTROLS ));
                let stateButton = buttonTag.getAttribute(ATTR_EXPANDED);

                // if closed
                if ( stateButton === 'false' ){
                    buttonTag.setAttribute( ATTR_EXPANDED , true );
                    destination.removeAttribute( ATTR_HIDDEN );
                   }
                   else {
                        buttonTag.setAttribute( ATTR_EXPANDED , false );
                        destination.setAttribute( ATTR_HIDDEN , true );
                        }

                if ( accordionMultiSelectable === 'false' ) {
                   $accordionAllHeaders
                      .forEach((header_node, index) => {

                         let destinationPanel = findById(header_node.getAttribute( ATTR_CONTROLS ));

                         if ( header_node !== buttonTag ){
                            header_node.setAttribute( ATTR_SELECTED , false );
                            header_node.setAttribute( ATTR_EXPANDED , false );
                            destinationPanel.setAttribute( ATTR_HIDDEN , true );
                            }
                            else {
                                 header_node.setAttribute( ATTR_SELECTED , true );
                                 }
                      });

                   }

                setTimeout(function(){ buttonTag.focus(); }, 0);
                e.preventDefault();

                }

                // keyboard management for headers
                if ( hasClass( e.target, ACCORDION_JS_HEADER) === true && eventName === 'keydown' ) {
                    let buttonTag = e.target;
                    let accordionContainer = buttonTag.parentNode;
                    let $accordionAllHeaders = [].slice.call(accordionContainer.querySelectorAll('.' + ACCORDION_JS_HEADER));

                    // strike home on a tab => 1st tab
                    if ( e.keyCode === 36 ) {
                       unSelectHeaders($accordionAllHeaders);
                       selectHeader($accordionAllHeaders[0]);
                       setTimeout(function(){ $accordionAllHeaders[0].focus(); }, 0);
                       e.preventDefault();
                       }
                    // strike end on the tab => last tab
                    else if ( e.keyCode == 35 ) {
                            unSelectHeaders($accordionAllHeaders);
                            selectHeader($accordionAllHeaders[ $accordionAllHeaders.length-1 ]);
                            setTimeout(function(){ $accordionAllHeaders[ $accordionAllHeaders.length-1 ].focus(); }, 0);
                            e.preventDefault();
                           }
                    // strike up or left on the tab => previous tab
                    else if ( ( e.keyCode == 37 || e.keyCode == 38 ) && !e.ctrlKey ) {

                            // if first selected = select last
                            //if ( $accordionAllHeaders[ $accordionAllHeaders.length-1 ].getAttribute( ATTR_SELECTED ) === 'true' ) {
                            if ( $accordionAllHeaders[ 0 ].getAttribute( ATTR_SELECTED ) === 'true' ) {
                               unSelectHeaders($accordionAllHeaders);
                               selectHeader($accordionAllHeaders[ $accordionAllHeaders.length-1 ]);
                               setTimeout(function(){ $accordionAllHeaders[ $accordionAllHeaders.length-1 ].focus(); }, 0);
                               e.preventDefault();
                               }
                            else {
                                  selectHeaderInList( $accordionAllHeaders, 'prev' );
                                  e.preventDefault();
                                 }

                            }
                    // strike down or right in the tab => next tab
                    else if ( ( e.keyCode == 40 || e.keyCode == 39 ) && !e.ctrlKey ) {

                            // if last selected = select first
                            if ( $accordionAllHeaders[ $accordionAllHeaders.length-1 ].getAttribute( ATTR_SELECTED ) === 'true' ) {
                               unSelectHeaders($accordionAllHeaders);
                               selectHeader($accordionAllHeaders[0]);
                               setTimeout(function(){ $accordionAllHeaders[0].focus(); }, 0);
                               e.preventDefault();
                               }
                            else {
                                  selectHeaderInList( $accordionAllHeaders, 'next' );
                                  e.preventDefault();
                                 }

                            }
                    }

                // keyboard management for panels
                let id_panel = searchParent ( e.target, ACCORDION_JS_PANEL );
                if ( id_panel !== '' && eventName === 'keydown' ) {
                    //console.log('yes in pabnelk' + e.keyCode);
                    let panelTag = findById( id_panel );
                    let accordionContainer = panelTag.parentNode;
                    let $accordionAllHeaders = [].slice.call(accordionContainer.querySelectorAll('.' + ACCORDION_JS_HEADER));
                    let buttonTag = findById(panelTag.getAttribute( ATTR_LABELLEDBY ));

                    // strike up + ctrl => go to header
                    if ( e.keyCode == 38 && e.ctrlKey ) {
                        unSelectHeaders($accordionAllHeaders);
                        selectHeader(buttonTag);
                        setTimeout(function(){ buttonTag.focus(); }, 0);
                        e.preventDefault();
                        }
                    // strike pageup + ctrl => go to prev header
                    if ( e.keyCode == 33 && e.ctrlKey ) {
                        // go to header
                        unSelectHeaders($accordionAllHeaders);
                        selectHeader(buttonTag);
                        buttonTag.focus();
                        e.preventDefault();
                        // then previous
                        if ( $accordionAllHeaders[ 0 ].getAttribute( ATTR_SELECTED ) === 'true' ) {
                            unSelectHeaders($accordionAllHeaders);
                            selectHeader($accordionAllHeaders[ $accordionAllHeaders.length-1 ]);
                            setTimeout(function(){ $accordionAllHeaders[ $accordionAllHeaders.length-1 ].focus(); }, 0);
                           }
                           else {
                                selectHeaderInList( $accordionAllHeaders, 'prev' );
                                }

                        }
                    // strike pagedown + ctrl => go to next header
                    if ( e.keyCode == 34 && e.ctrlKey ) {
                        // go to header
                        unSelectHeaders($accordionAllHeaders);
                        selectHeader(buttonTag);
                        buttonTag.focus();
                        e.preventDefault();
                        // then next
                        if ( $accordionAllHeaders[ $accordionAllHeaders.length-1 ].getAttribute( ATTR_SELECTED ) === 'true' ) {
                            unSelectHeaders($accordionAllHeaders);
                            selectHeader($accordionAllHeaders[0]);
                            setTimeout(function(){ $accordionAllHeaders[0].focus(); }, 0);
                           }
                           else {
                                selectHeaderInList( $accordionAllHeaders, 'next' );
                                }

                        }

                }

          }, true);
    });
    document.removeEventListener('DOMContentLoaded', onLoad);
  }

  document.addEventListener('DOMContentLoaded', onLoad);

})(document);
<commit_msg>Update van11y-accessible-accordion-aria.es6.js<commit_after>/*
 * ES2015 accessible accordion system, using ARIA
 * Website: https://van11y.net/accessible-accordion/
 * License MIT: https://github.com/nico3333fr/van11y-accessible-accordion-aria/blob/master/LICENSE
 */
;(doc => {

  'use strict';

  const ACCORDION_JS = 'js-accordion';
  const ACCORDION_JS_HEADER = 'js-accordion__header';
  const ACCORDION_JS_PANEL = 'js-accordion__panel';

  const ACCORDION_DATA_PREFIX_CLASS = 'data-accordion-prefix-classes';
  const ACCORDION_DATA_OPENED = 'data-accordion-opened';
  const ACCORDION_DATA_MULTISELECTABLE = 'data-accordion-multiselectable';

  const ACCORDION_PREFIX_IDS = 'accordion';
  const ACCORDION_BUTTON_ID = '_tab';
  const ACCORDION_PANEL_ID = '_panel';

  const ACCORDION_STYLE = 'accordion';
  const ACCORDION_TITLE_STYLE = 'accordion__title';
  const ACCORDION_HEADER_STYLE = 'accordion__header';
  const ACCORDION_PANEL_STYLE = 'accordion__panel';

  const ACCORDION_ROLE_TABLIST = 'tablist';
  const ACCORDION_ROLE_TAB = 'tab';
  const ACCORDION_ROLE_TABPANEL = 'tabpanel';

  const ATTR_ROLE = 'role';
  const ATTR_MULTISELECTABLE = 'aria-multiselectable';
  const ATTR_EXPANDED = 'aria-expanded';
  const ATTR_LABELLEDBY = 'aria-labelledby';
  const ATTR_HIDDEN = 'aria-hidden';
  const ATTR_CONTROLS = 'aria-controls';
  const ATTR_SELECTED = 'aria-selected';

  const IS_OPENED_CLASS = 'is-opened';

  const findById = id => doc.getElementById(id);

  const addClass = (el, className) => {
        if (el.classList) {
          el.classList.add(className); // IE 10+
        }
        else {
             el.className += ' ' + className; // IE 8+
             }
        }

  const removeClass = (el, className) => {
        if (el.classList) {
          el.classList.remove(className); // IE 10+
        }
        else {
             el.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' '); // IE 8+
             }
        }

  const hasClass = (el, className) => {
        if (el.classList) {
          return el.classList.contains(className); // IE 10+
        }
        else {
             return new RegExp('(^| )' + className + '( |$)', 'gi').test(el.className); // IE 8+ ?
             }
        }

  const setAttributes = (node, attrs) => {
        Object
          .keys(attrs)
          .forEach((attribute) => {
            node.setAttribute(attribute, attrs[attribute]);
          });
        };
/*  const triggerEvent = (el, event_type) => {
        if (el.fireEvent) {
          el.fireEvent('on' + event_type);
        }
        else {
              let evObj = document.createEvent('Events');
              evObj.initEvent(event_type, true, false);
              el.dispatchEvent(evObj);
             }
        }*/
  const unSelectHeaders = (elts) => {
        elts
           .forEach((header_node, index) => {
             setAttributes(header_node, {
               [ATTR_SELECTED] : 'false' ,
               'tabindex' : '-1'
             });
           });
        }

  const selectHeader = (el) => {
        el.setAttribute( ATTR_SELECTED , true );
        el.removeAttribute ( 'tabindex' );
        }

  const selectHeaderInList = (elts, param) => {
        let indice_trouve;
        elts
           .forEach((header_node, index) => {

                if ( header_node.getAttribute(ATTR_SELECTED) === 'true' ) {
                   indice_trouve = index;
                   }

           });
        if ( param === 'next' ){
            selectHeader ( elts[indice_trouve+1] );
            setTimeout(function(){ elts[indice_trouve+1].focus(); }, 0);
            }
        if ( param === 'prev' ){
            selectHeader ( elts[indice_trouve-1] );
            setTimeout(function(){ elts[indice_trouve-1].focus(); }, 0);
            }

        }

  /* gets an element el, search if it is child of parent class, returns id of the parent */
  let searchParent = ( el, parentClass ) => {
        let found = false;
        let parentElement = el.parentNode;
        while ( parentElement && found === false ) {
            if ( hasClass( parentElement, parentClass ) === true ) {
                found = true;
                }
                else {
                     parentElement = parentElement.parentNode;
                     }
            }
        if ( found === true ) {
           return parentElement.getAttribute('id');
           }
           else { return ''; }
  }

  // Find all accordions
  const $listAccordions = () => [].slice.call(doc.querySelectorAll('.' + ACCORDION_JS));

  const onLoad = () => {

  $listAccordions()
    .forEach((accordion_node, index) => {

      let iLisible = index + 1;
      let prefixClassName = accordion_node.hasAttribute(ACCORDION_DATA_PREFIX_CLASS) === true ? accordion_node.getAttribute(ACCORDION_DATA_PREFIX_CLASS) + '-' : '' ;
      let accordionClassName = accordion_node.hasAttribute(ACCORDION_DATA_PREFIX_CLASS) === true ? accordion_node.getAttribute(ACCORDION_DATA_PREFIX_CLASS) : '' ;
      let multiSelectableAttribute = accordion_node.hasAttribute(ACCORDION_DATA_MULTISELECTABLE) === true ? accordion_node.getAttribute(ACCORDION_DATA_MULTISELECTABLE) : '' ;
      /*let toExpand = node.nextElementSibling;
      let expandmoreText = node.innerHTML;
      let expandButton = doc.createElement("BUTTON");*/

      // clear element before adding button to it
      //node.innerHTML = '';

      // Init attributes accordion
      if (accordion_node.getAttribute(ACCORDION_DATA_MULTISELECTABLE) == 'none'){
          accordion_node.setAttribute( ATTR_MULTISELECTABLE , 'false' );
         }
         else {
               accordion_node.setAttribute( ATTR_MULTISELECTABLE , 'true' );
              }
      accordion_node.setAttribute( ATTR_ROLE , ACCORDION_ROLE_TABLIST );
      addClass( accordion_node, prefixClassName + ACCORDION_STYLE);

      let $listAccordionsHeader = [].slice.call(accordion_node.querySelectorAll('.' + ACCORDION_JS_HEADER));
      $listAccordionsHeader
        .forEach((header_node, index_header) => {

            let indexHeaderLisible = index_header+1;
            let accordionPanel = header_node.nextElementSibling;
            let accordionHeaderText = header_node.innerHTML;
            let accordionButton = document.createElement("BUTTON");
            let accordionOpenedAttribute = header_node.hasAttribute(ACCORDION_DATA_OPENED) === true ? header_node.getAttribute(ACCORDION_DATA_OPENED) : '' ;

            // set button with attributes
            accordionButton.innerHTML = accordionHeaderText;
            addClass( accordionButton, ACCORDION_JS_HEADER );
            addClass( accordionButton, prefixClassName + ACCORDION_HEADER_STYLE );
            setAttributes(accordionButton, {
              [ATTR_ROLE] : ACCORDION_ROLE_TAB ,
              'id' : ACCORDION_PREFIX_IDS + iLisible + ACCORDION_BUTTON_ID + indexHeaderLisible ,
              [ATTR_CONTROLS] : ACCORDION_PREFIX_IDS + iLisible + ACCORDION_PANEL_ID + indexHeaderLisible ,
              [ATTR_SELECTED] : 'false' ,
              'tabindex' : '-1'
             });

            // place button
            accordionButton = accordion_node.insertBefore(accordionButton, header_node);

            // move title into panel
            accordionPanel.insertBefore(header_node, accordionPanel.firstChild);
            // set title with attributes
            addClass( header_node, prefixClassName + ACCORDION_TITLE_STYLE );
            removeClass ( header_node, ACCORDION_JS_HEADER );

            // set attributes to panels
            addClass( accordionPanel, prefixClassName + ACCORDION_PANEL_STYLE);
            setAttributes(accordionPanel, {
              [ATTR_ROLE] : ACCORDION_ROLE_TABPANEL ,
              [ATTR_LABELLEDBY] : ACCORDION_PREFIX_IDS + iLisible + ACCORDION_BUTTON_ID + indexHeaderLisible ,
              'id' : ACCORDION_PREFIX_IDS + iLisible + ACCORDION_PANEL_ID + indexHeaderLisible
             });

            if (accordionOpenedAttribute === 'true'){
               accordionButton.setAttribute( ATTR_EXPANDED , 'true' );
               header_node.removeAttribute( ACCORDION_DATA_OPENED );
               accordionPanel.setAttribute( ATTR_HIDDEN , 'false' );
               }
               else {
                     accordionButton.setAttribute( ATTR_EXPANDED , 'false' );
                     accordionPanel.setAttribute( ATTR_HIDDEN , 'true' );
                    }

            // init first one focusable
            if ( indexHeaderLisible === 1 ) {
                accordionButton.removeAttribute( 'tabindex' );
                }

        });

    });

  // click on
  ['click', 'keydown', 'focus']
    .forEach(eventName => {
       let isCtrl = false;

       doc.body
          .addEventListener(eventName, e => {

             // focus on button
             if ( hasClass( e.target, ACCORDION_JS_HEADER) === true && eventName === 'focus' ) {
                 let buttonTag = e.target;
                 let accordionContainer = buttonTag.parentNode;
                 let $accordionAllHeaders = [].slice.call(accordionContainer.querySelectorAll('.' + ACCORDION_JS_HEADER));

                 unSelectHeaders($accordionAllHeaders);

                 selectHeader(buttonTag);

                }

             // click on button
             if ( hasClass( e.target, ACCORDION_JS_HEADER) === true && eventName === 'click' ) {
                let buttonTag = e.target;
                let accordionContainer = buttonTag.parentNode;
                let $accordionAllHeaders = [].slice.call(accordionContainer.querySelectorAll('.' + ACCORDION_JS_HEADER));
                let accordionMultiSelectable = accordionContainer.getAttribute(ATTR_MULTISELECTABLE);
                let destination = findById(buttonTag.getAttribute( ATTR_CONTROLS ));
                let stateButton = buttonTag.getAttribute(ATTR_EXPANDED);

                // if closed
                if ( stateButton === 'false' ){
                    buttonTag.setAttribute( ATTR_EXPANDED , true );
                    destination.removeAttribute( ATTR_HIDDEN );
                   }
                   else {
                        buttonTag.setAttribute( ATTR_EXPANDED , false );
                        destination.setAttribute( ATTR_HIDDEN , true );
                        }

                if ( accordionMultiSelectable === 'false' ) {
                   $accordionAllHeaders
                      .forEach((header_node, index) => {

                         let destinationPanel = findById(header_node.getAttribute( ATTR_CONTROLS ));

                         if ( header_node !== buttonTag ){
                            header_node.setAttribute( ATTR_SELECTED , false );
                            header_node.setAttribute( ATTR_EXPANDED , false );
                            destinationPanel.setAttribute( ATTR_HIDDEN , true );
                            }
                            else {
                                 header_node.setAttribute( ATTR_SELECTED , true );
                                 }
                      });

                   }

                setTimeout(function(){ buttonTag.focus(); }, 0);
                e.preventDefault();

                }

                // keyboard management for headers
                if ( hasClass( e.target, ACCORDION_JS_HEADER) === true && eventName === 'keydown' ) {
                    let buttonTag = e.target;
                    let accordionContainer = buttonTag.parentNode;
                    let $accordionAllHeaders = [].slice.call(accordionContainer.querySelectorAll('.' + ACCORDION_JS_HEADER));

                    // strike home on a tab => 1st tab
                    if ( e.keyCode === 36 ) {
                       unSelectHeaders($accordionAllHeaders);
                       selectHeader($accordionAllHeaders[0]);
                       setTimeout(function(){ $accordionAllHeaders[0].focus(); }, 0);
                       e.preventDefault();
                       }
                    // strike end on the tab => last tab
                    else if ( e.keyCode == 35 ) {
                            unSelectHeaders($accordionAllHeaders);
                            selectHeader($accordionAllHeaders[ $accordionAllHeaders.length-1 ]);
                            setTimeout(function(){ $accordionAllHeaders[ $accordionAllHeaders.length-1 ].focus(); }, 0);
                            e.preventDefault();
                           }
                    // strike up or left on the tab => previous tab
                    else if ( ( e.keyCode == 37 || e.keyCode == 38 ) && !e.ctrlKey ) {

                            // if first selected = select last
                            //if ( $accordionAllHeaders[ $accordionAllHeaders.length-1 ].getAttribute( ATTR_SELECTED ) === 'true' ) {
                            if ( $accordionAllHeaders[ 0 ].getAttribute( ATTR_SELECTED ) === 'true' ) {
                               unSelectHeaders($accordionAllHeaders);
                               selectHeader($accordionAllHeaders[ $accordionAllHeaders.length-1 ]);
                               setTimeout(function(){ $accordionAllHeaders[ $accordionAllHeaders.length-1 ].focus(); }, 0);
                               e.preventDefault();
                               }
                            else {
                                  selectHeaderInList( $accordionAllHeaders, 'prev' );
                                  e.preventDefault();
                                 }

                            }
                    // strike down or right in the tab => next tab
                    else if ( ( e.keyCode == 40 || e.keyCode == 39 ) && !e.ctrlKey ) {

                            // if last selected = select first
                            if ( $accordionAllHeaders[ $accordionAllHeaders.length-1 ].getAttribute( ATTR_SELECTED ) === 'true' ) {
                               unSelectHeaders($accordionAllHeaders);
                               selectHeader($accordionAllHeaders[0]);
                               setTimeout(function(){ $accordionAllHeaders[0].focus(); }, 0);
                               e.preventDefault();
                               }
                            else {
                                  selectHeaderInList( $accordionAllHeaders, 'next' );
                                  e.preventDefault();
                                 }

                            }
                    }

                // keyboard management for panels
                let id_panel = searchParent ( e.target, ACCORDION_JS_PANEL );
                if ( id_panel !== '' && eventName === 'keydown' ) {
                    //console.log('yes in pabnelk' + e.keyCode);
                    let panelTag = findById( id_panel );
                    let accordionContainer = panelTag.parentNode;
                    let $accordionAllHeaders = [].slice.call(accordionContainer.querySelectorAll('.' + ACCORDION_JS_HEADER));
                    let buttonTag = findById(panelTag.getAttribute( ATTR_LABELLEDBY ));

                    // strike up + ctrl => go to header
                    if ( e.keyCode == 38 && e.ctrlKey ) {
                        unSelectHeaders($accordionAllHeaders);
                        selectHeader(buttonTag);
                        setTimeout(function(){ buttonTag.focus(); }, 0);
                        e.preventDefault();
                        }
                    // strike pageup + ctrl => go to prev header
                    if ( e.keyCode == 33 && e.ctrlKey ) {
                        // go to header
                        unSelectHeaders($accordionAllHeaders);
                        selectHeader(buttonTag);
                        buttonTag.focus();
                        e.preventDefault();
                        // then previous
                        if ( $accordionAllHeaders[ 0 ].getAttribute( ATTR_SELECTED ) === 'true' ) {
                            unSelectHeaders($accordionAllHeaders);
                            selectHeader($accordionAllHeaders[ $accordionAllHeaders.length-1 ]);
                            setTimeout(function(){ $accordionAllHeaders[ $accordionAllHeaders.length-1 ].focus(); }, 0);
                           }
                           else {
                                selectHeaderInList( $accordionAllHeaders, 'prev' );
                                }

                        }
                    // strike pagedown + ctrl => go to next header
                    if ( e.keyCode == 34 && e.ctrlKey ) {
                        // go to header
                        unSelectHeaders($accordionAllHeaders);
                        selectHeader(buttonTag);
                        buttonTag.focus();
                        e.preventDefault();
                        // then next
                        if ( $accordionAllHeaders[ $accordionAllHeaders.length-1 ].getAttribute( ATTR_SELECTED ) === 'true' ) {
                            unSelectHeaders($accordionAllHeaders);
                            selectHeader($accordionAllHeaders[0]);
                            setTimeout(function(){ $accordionAllHeaders[0].focus(); }, 0);
                           }
                           else {
                                selectHeaderInList( $accordionAllHeaders, 'next' );
                                }

                        }

                }

          }, true);
    });
    document.removeEventListener('DOMContentLoaded', onLoad);
  }

  document.addEventListener('DOMContentLoaded', onLoad);

})(document);
<|endoftext|>


<commit_before>var http = require("http");
var sys = require("sys");
var url = require("url");
var WebSocketClient = require("ws");

var port = 9092;
var defaultHost = "localhost";
var defaultPort = 9000;
var clients = {byID:{},byName:{}};
var DEFAULT_TIMEOUT = 5*60; // 5 minutes
var DEBUG = true;
var clientID = 0;
var wsClient;

/*

Node.js server that accepts pure HTTP requests and pipes the requests through websockets.
The server keeps track of different clients and maintains a websocket connection with spacebrew to pass along future messages.

HTTP request:
currently only tested as a GET request

CAN TEST WITH:
http://localhost:9092/?config={%22config%22:{%22name%22:%22test%22,%22publish%22:{%22messages%22:[{%22name%22:%22output%22,%22type%22:%22string%22},{%22name%22:%22out%22,%22type%22:%22string%22}]},%22subscribe%22:{%22messages%22:[{%22name%22:%22input%22,%22type%22:%22string%22,%22bufferSize%22:3}]}}}
http://localhost:9092/?clientID=0&poll=true
http://localhost:9092/?clientID=0&publish=[{%22message%22:{%22clientName%22:%22test%22,%22name%22:%22output%22,%22type%22:%22string%22,%22value%22:%22hello!%22}}]
 */

/**
 * Returns the number of seconds since the Unix epoch.
 * @return {Number} The number of seconds since the Unix epoch
 */
var getSecondsEpoch = function() {
    var d = new Date();
    var seconds = d.getTime() / 1000;
    return seconds;
};

/**
 * Sends the provided client's configuration to the SB server.
 * This function will also create some data structures for queuing messages for
 * the client.
 * @param  {json} clientData The data storing all info related to this client
 * @param  {json} output The json object returned in the response
 */
var configureClient = function(clientData, output) {

    if (!clientData.received){
        clientData.received = {};
    }
    var subscribers = clientData.config.config.subscribe.messages;

    //first remove any buffers that don't match current client definition
    for (var type in clientData.received){
        for(var name in clientData.received[type]){
            var found = false;
            for(var index in subscribers){
                if (subscribers[index].type == type && subscribers[index].name == name){
                    found = true;
                    break;
                }
            }
            if (!found){
                delete clientData.received[type][name];
            }
        }
        if (Object.keys(clientData.received[type]).length === 0){
            delete clientData.received[type];
        }
    }

    //the add new buffers to match current client definition
    for (var i = subscribers.length - 1; i >= 0; i--) {
        var sub = subscribers[i];
        //copying ROOT -> type -> name -> DATA multi-level map from the server
        //(see handleMessageMessage in spacebrew.js)
        if (!clientData.received[sub.type]){
            clientData.received[sub.type] = {};
        }
        if (!clientData.received[sub.type][sub.name]){
            //this will be an array to act as a buffer
            //TODO: ensure bufferSize is an integer
            clientData.received[sub.type][sub.name] = {bufferSize:sub.bufferSize || 1, buffer:[]};
            if (sub.bufferSize){
                delete sub.bufferSize;
            }
        }
    }

    //send config to SB server
    var jsonConfig = JSON.stringify(clientData.config);
    if (DEBUG){
        sys.puts("jsonConfig: " + jsonConfig);
    }

    //TODO: check if ws is connected
    try{
        wsClient.send(jsonConfig);
    }
    catch (error){
        output.error = true;
        output.messages.push("Error while sending config: " + error.name + " msg: " + error.message);
    }
};

/**
 * Handle the json data from the Server and forward it to the appropriate function
 * @param  {json} json The message sent from the Server
 * @return {boolean}      True iff the message was a recognized type
 */
var handleMessage = function(json) {
    if (json.message){
        var clientData = clients.byName[json.message.clientName];
        if (clientData){
            var nameMap = clientData.received[json.message.type];
            if (nameMap){
                var bufferObj = nameMap[json.message.name];
                if (bufferObj){
                    bufferObj.buffer.push(json);
                    if (bufferObj.buffer.length > bufferObj.bufferSize){
                        bufferObj.buffer.splice(0,1);
                    }
                }
            }
        }
    } else if (json.admin){
    } else if (json.config){
    } else if (json.route){
    } else if (json.remove){
    } else {
        return false;
    }
    return true;
};

/**
 * Called when we receive a message from the Server.
 * @param  {websocket message} data The websocket message from the Server
 */
var receivedMessage = function(data, flags) {
    // console.log(data);
    if (data){
        var json = JSON.parse(data);
        //TODO: check if json is an array, otherwise use it as solo message
        //when we hit a malformed message, output a warning
        if (!handleMessage(json)){
            for(var i = 0, end = json.length; i < end; i++){
                handleMessage(json[i]);
            }
        }
    }
};

/**
 * Creates the websocket client which is used to communicate with the SB server.
 */
var setupWSClient = function() {
    // create the wsclient
    wsClient = new WebSocketClient("ws://"+defaultHost+":"+defaultPort);
    wsClient.on("open", function(conn){
        console.log("connected");
    });
    wsClient.on("message", receivedMessage);

    wsClient.on("error", function(){
        console.log("ERROR");
        console.log(arguments);
        //attempt re-connect after 5 seconds
        setTimeout(setupWSClient, 5000);
    });

    wsClient.on("close", function(){
        console.log("CLOSE");
        console.log(arguments);
        //attempt re-connect after 5 seconds
        setTimeout(setupWSClient, 5000);
    });
};

setupWSClient();

/**
 * This goes through all registered clients and sees if they have timed out.
 * Any timed out clients will be cleaned up and removed from the SB server.
 */
var checkTimeouts = function() {

    for (var id in clients.byID) {
        var client = clients.byID[id];
        if( getSecondsEpoch() - client.lastUpdate > client.updateTimeout ) {
            //TODO: send 'remove' or 'un-register' command to SB server?
            //  (would need to be implemented server side as well)
            delete clients.byID[id];
            delete clients.byName[client.config.config.name];
            client = undefined;
        }
    }
};

// check timeouts every minute
setInterval(checkTimeouts, 1 * 60 * 1000);

//the server listens for GET requests passing the data via query string
//the following query string values are accepted/expected
//config=<CONFIG_JSON>
//   This is required the first time a client registers. The <CONFIG_JSON>
//  should be the full stringified JSON that you would normally send via
//  websocket to the server. (ie. config={config:{name:.......}})
//   In response to initially registering a client, this server will send back
//  a unique identifier used to identify your client in the future.
//   You can also send this config value again along with the
//  CLIENT_ID (see below) if you need to send a client update.
//   **Setting Buffer size**
//   Each subscriber defaults to a buffer size of 1, if you want a larger
//  buffer, then specify the property 'bufferSize' with an integer value
//  within the individual subscriber's definition.
//  (ie. messages:[{name:mySub,type:string,bufferSize:5}...])
//clientID:<CLIENT_ID>
//   This should be used any time after first registering the client. The
//  <CLIENT_ID> is the unique identifier returned in the GET response that
//  first registers a client with this server.
//poll:<true|false>
//   if true, then the server will provide any stored data for all of the
//  subscribers this client has registered.
//publish:<PUB_JSON>
//   json stipulating values to send out via this client's registered
//  publishers. The format of <PUB_JSON> is an array of publish messages
//  where each publish message is formatted as expected by the Spacebrew server
//  (ie. [<PUB_DATA>\[,<PUB_DATA>....\]] where <PUB_DATA> is
//  {name:<PUB_NAME>,clientName:<CLIENT_NAME>,type:<PUB_TYPE>,
//  value:<PUB_VALUE>:<PUB_VALUE>}
//timeout:<TIMEOUT>
//   The number of seconds since the last interaction from this client before
//  the client is marked as stale. A stale client can be replaced by a new
//  client with a matching name.
//   The default timeout is 5 minutes.

http.createServer(function (req, res) {
    var config, id, publish, poll, timeout, clientData;
    var output = {error:false,messages:[]};
    res.setHeader("Content-Type", "application/json");

    try {
        if(DEBUG) {
            sys.puts("request: " + req.url);
        }

        if(req.method == "GET") {

            var vals = url.parse(req.url, true).query;
            config = vals.config;
            if (config){
                try{
                    config = JSON.parse(config);
                }
                catch (error){
                    output.error = true;
                    output.messages.push("Error parsing config: " + error.name + " msg: " + error.message);
                    config = undefined;
                }
            }
            id = vals.clientID;
            poll = vals.poll;
            if (poll){
                poll = poll.toLowerCase() == "true";
            }
            publish = vals.publish;
            if (publish){
                try{
                    publish = JSON.parse(publish);
                }
                catch (error){
                    output.error = true;
                    output.messages.push("Error parsing publish: " + error.name + " msg: " + error.message);
                    publish = undefined;
                }
            }
            timeout = vals.timeout;

        } else {//"DELETE" "PUT" "POST"...
            /*
            So There is a big discussion here about which HTTP verbs handle which commands.
            The suggestions of REST are that POST is non-idempotent (multiple calls can change server state)
            however all other verbs are idempotent. Since most methods change the state of the server, it seems
            like we should be using POST almost exclusively, but for eand-users, using GET is so easy... and this isn't a "real" HTTP server...
             */
            output.error = true;
            output.messages.push("Unsupported request method of " + req.method);
        }

        //TODO: method
        if (id !== undefined){
            clientData = clients.byID[id];
            clientData.lastUpdate = getSecondsEpoch();
            if (timeout){
                clientData.updateTimeout = timeout;
            }
            if (!clientData){
                output.error = true;
                output.messages.push("ID does not match any registered clients.");
            } else if (config){
                //possibly a client config update
                if (!config.config){
                    output.error = true;
                    output.messages.push("Invalid config format.");
                } else if (config.config.name != clientData.config.config.name){
                    output.error = true;
                    output.messages.push("Supplied client name does not match registered client name.");
                } else {
                    //I believe this is updating both byID and byName
                    clientData.config = config;
                    configureClient(clientData, output);
                }
            }
            if (!output.error){
                if (publish){
                    try{
                        for (var i = 0; i < publish.length; i++) {
                            wsClient.send(JSON.stringify(publish[i]));
                        }
                    }
                    catch(error){
                        output.error = true;
                        output.messages.push("Error parsing published values: " + error.name + " msg: " + error.message);
                    }
                }
                if (poll){
                    //return stored values
                    var messages = [];
                    for(var type in clientData.received){
                        var nameMap = clientData.received[type];
                        for (var name in nameMap){
                            var bufferObj = nameMap[name];
                            if (bufferObj && bufferObj.buffer.length > 0){
                                messages = messages.concat(bufferObj.buffer);
                                bufferObj.buffer = [];
                            }
                        }
                    }
                    output.received = messages;
                }
            }
        } else if (config){
            if (!config.config){
                output.error = true;
                output.messages.push("Config must contain a 'config' object.");
            } else {
                if (!config.config.name){
                    output.error = true;
                    output.messages.push("Config requires a client 'name'.");
                } else {
                    var existingClient = clients.byName[config.config.name];
                    if (existingClient !== undefined){
                        if (getSecondsEpoch() - existingClient.lastUpdate > existingClient.updateTimeout){
                            delete clients.byName[config.config.name];
                            delete clients.byID[existingClient.id];
                            existingClient = undefined;
                        } else {
                            output.error = true;
                            output.messages.push("Client with provided name already registered with this http_link.");
                        }
                    }
                    if (existingClient === undefined){
                        //valid (perhaps) config
                        clientData = {"config":config};
                        clientData.updateTimeout = timeout || DEFAULT_TIMEOUT;
                        configureClient(clientData, output);
                        if (!output.error){
                            clients.byID[clientID] = clientData;
                            clients.byName[config.config.name] = clientData;
                            id = clientID;
                            clientID++;
                        }
                    }
                }
            }
        } else {
            output.error = true;
            output.messages.push("Send client config or reference registered client by ID.");
        }
    }
    catch (error){
        output.error = true;
        output.messages.push("Unexpected error: " + error.name + " msg: " + error.message);
    }
    output.clientID = id;
    res.end(JSON.stringify(output));

}).listen(port);
<commit_msg>only log errors in long-form during DEBUG<commit_after>var http = require("http");
var sys = require("sys");
var url = require("url");
var WebSocketClient = require("ws");

var port = 9092;
var defaultHost = "localhost";
var defaultPort = 9000;
var clients = {byID:{},byName:{}};
var DEFAULT_TIMEOUT = 5*60; // 5 minutes
var DEBUG = true;
var clientID = 0;
var wsClient;

/*

Node.js server that accepts pure HTTP requests and pipes the requests through websockets.
The server keeps track of different clients and maintains a websocket connection with spacebrew to pass along future messages.

HTTP request:
currently only tested as a GET request

CAN TEST WITH:
http://localhost:9092/?config={%22config%22:{%22name%22:%22test%22,%22publish%22:{%22messages%22:[{%22name%22:%22output%22,%22type%22:%22string%22},{%22name%22:%22out%22,%22type%22:%22string%22}]},%22subscribe%22:{%22messages%22:[{%22name%22:%22input%22,%22type%22:%22string%22,%22bufferSize%22:3}]}}}
http://localhost:9092/?clientID=0&poll=true
http://localhost:9092/?clientID=0&publish=[{%22message%22:{%22clientName%22:%22test%22,%22name%22:%22output%22,%22type%22:%22string%22,%22value%22:%22hello!%22}}]
 */

/**
 * Returns the number of seconds since the Unix epoch.
 * @return {Number} The number of seconds since the Unix epoch
 */
var getSecondsEpoch = function() {
    var d = new Date();
    var seconds = d.getTime() / 1000;
    return seconds;
};

/**
 * Sends the provided client's configuration to the SB server.
 * This function will also create some data structures for queuing messages for
 * the client.
 * @param  {json} clientData The data storing all info related to this client
 * @param  {json} output The json object returned in the response
 */
var configureClient = function(clientData, output) {

    if (!clientData.received){
        clientData.received = {};
    }
    var subscribers = clientData.config.config.subscribe.messages;

    //first remove any buffers that don't match current client definition
    for (var type in clientData.received){
        for(var name in clientData.received[type]){
            var found = false;
            for(var index in subscribers){
                if (subscribers[index].type == type && subscribers[index].name == name){
                    found = true;
                    break;
                }
            }
            if (!found){
                delete clientData.received[type][name];
            }
        }
        if (Object.keys(clientData.received[type]).length === 0){
            delete clientData.received[type];
        }
    }

    //the add new buffers to match current client definition
    for (var i = subscribers.length - 1; i >= 0; i--) {
        var sub = subscribers[i];
        //copying ROOT -> type -> name -> DATA multi-level map from the server
        //(see handleMessageMessage in spacebrew.js)
        if (!clientData.received[sub.type]){
            clientData.received[sub.type] = {};
        }
        if (!clientData.received[sub.type][sub.name]){
            //this will be an array to act as a buffer
            //TODO: ensure bufferSize is an integer
            clientData.received[sub.type][sub.name] = {bufferSize:sub.bufferSize || 1, buffer:[]};
            if (sub.bufferSize){
                delete sub.bufferSize;
            }
        }
    }

    //send config to SB server
    var jsonConfig = JSON.stringify(clientData.config);
    if (DEBUG){
        sys.puts("jsonConfig: " + jsonConfig);
    }

    //TODO: check if ws is connected
    try{
        wsClient.send(jsonConfig);
    }
    catch (error){
        output.error = true;
        output.messages.push("Error while sending config: " + error.name + " msg: " + error.message);
    }
};

/**
 * Handle the json data from the Server and forward it to the appropriate function
 * @param  {json} json The message sent from the Server
 * @return {boolean}      True iff the message was a recognized type
 */
var handleMessage = function(json) {
    if (json.message){
        var clientData = clients.byName[json.message.clientName];
        if (clientData){
            var nameMap = clientData.received[json.message.type];
            if (nameMap){
                var bufferObj = nameMap[json.message.name];
                if (bufferObj){
                    bufferObj.buffer.push(json);
                    if (bufferObj.buffer.length > bufferObj.bufferSize){
                        bufferObj.buffer.splice(0,1);
                    }
                }
            }
        }
    } else if (json.admin){
    } else if (json.config){
    } else if (json.route){
    } else if (json.remove){
    } else {
        return false;
    }
    return true;
};

/**
 * Called when we receive a message from the Server.
 * @param  {websocket message} data The websocket message from the Server
 */
var receivedMessage = function(data, flags) {
    if (data){
        var json = JSON.parse(data);
        //TODO: check if json is an array, otherwise use it as solo message
        //when we hit a malformed message, output a warning
        if (!handleMessage(json)){
            for(var i = 0, end = json.length; i < end; i++){
                handleMessage(json[i]);
            }
        }
    }
};

/**
 * Creates the websocket client which is used to communicate with the SB server.
 */
var setupWSClient = function() {
    // create the wsclient
    wsClient = new WebSocketClient("ws://"+defaultHost+":"+defaultPort);
    wsClient.on("open", function(conn){
        //TODO: re-send client configs if this is a re-connect (or generally if there are configs)
        console.log("connected");
    });
    wsClient.on("message", receivedMessage);

    wsClient.on("error", function(){
        console.log("ERROR");
        if (DEBUG){
            console.log(arguments);
        }
        //attempt re-connect after 5 seconds
        setTimeout(setupWSClient, 5000);
    });

    wsClient.on("close", function(){
        console.log("CLOSE");
        if (DEBUG){
            console.log(arguments);
        }
        //attempt re-connect after 5 seconds
        setTimeout(setupWSClient, 5000);
    });
};

setupWSClient();

/**
 * This goes through all registered clients and sees if they have timed out.
 * Any timed out clients will be cleaned up and removed from the SB server.
 */
var checkTimeouts = function() {

    for (var id in clients.byID) {
        var client = clients.byID[id];
        if( getSecondsEpoch() - client.lastUpdate > client.updateTimeout ) {
            //TODO: send 'remove' or 'un-register' command to SB server?
            //  (would need to be implemented server side as well)
            delete clients.byID[id];
            delete clients.byName[client.config.config.name];
            client = undefined;
        }
    }
};

// check timeouts every minute
setInterval(checkTimeouts, 1 * 60 * 1000);

//the server listens for GET requests passing the data via query string
//the following query string values are accepted/expected
//config=<CONFIG_JSON>
//   This is required the first time a client registers. The <CONFIG_JSON>
//  should be the full stringified JSON that you would normally send via
//  websocket to the server. (ie. config={config:{name:.......}})
//   In response to initially registering a client, this server will send back
//  a unique identifier used to identify your client in the future.
//   You can also send this config value again along with the
//  CLIENT_ID (see below) if you need to send a client update.
//   **Setting Buffer size**
//   Each subscriber defaults to a buffer size of 1, if you want a larger
//  buffer, then specify the property 'bufferSize' with an integer value
//  within the individual subscriber's definition.
//  (ie. messages:[{name:mySub,type:string,bufferSize:5}...])
//clientID:<CLIENT_ID>
//   This should be used any time after first registering the client. The
//  <CLIENT_ID> is the unique identifier returned in the GET response that
//  first registers a client with this server.
//poll:<true|false>
//   if true, then the server will provide any stored data for all of the
//  subscribers this client has registered.
//publish:<PUB_JSON>
//   json stipulating values to send out via this client's registered
//  publishers. The format of <PUB_JSON> is an array of publish messages
//  where each publish message is formatted as expected by the Spacebrew server
//  (ie. [<PUB_DATA>\[,<PUB_DATA>....\]] where <PUB_DATA> is
//  {name:<PUB_NAME>,clientName:<CLIENT_NAME>,type:<PUB_TYPE>,
//  value:<PUB_VALUE>:<PUB_VALUE>}
//timeout:<TIMEOUT>
//   The number of seconds since the last interaction from this client before
//  the client is marked as stale. A stale client can be replaced by a new
//  client with a matching name.
//   The default timeout is 5 minutes.

http.createServer(function (req, res) {
    var config, id, publish, poll, timeout, clientData;
    var output = {error:false,messages:[]};
    res.setHeader("Content-Type", "application/json");

    try {
        if(DEBUG) {
            sys.puts("request: " + req.url);
        }

        if(req.method == "GET") {

            var vals = url.parse(req.url, true).query;
            config = vals.config;
            if (config){
                try{
                    config = JSON.parse(config);
                }
                catch (error){
                    output.error = true;
                    output.messages.push("Error parsing config: " + error.name + " msg: " + error.message);
                    config = undefined;
                }
            }
            id = vals.clientID;
            poll = vals.poll;
            if (poll){
                poll = poll.toLowerCase() == "true";
            }
            publish = vals.publish;
            if (publish){
                try{
                    publish = JSON.parse(publish);
                }
                catch (error){
                    output.error = true;
                    output.messages.push("Error parsing publish: " + error.name + " msg: " + error.message);
                    publish = undefined;
                }
            }
            timeout = vals.timeout;

        } else {//"DELETE" "PUT" "POST"...
            /*
            So There is a big discussion here about which HTTP verbs handle which commands.
            The suggestions of REST are that POST is non-idempotent (multiple calls can change server state)
            however all other verbs are idempotent. Since most methods change the state of the server, it seems
            like we should be using POST almost exclusively, but for eand-users, using GET is so easy... and this isn't a "real" HTTP server...
             */
            output.error = true;
            output.messages.push("Unsupported request method of " + req.method);
        }

        //TODO: method
        if (id !== undefined){
            clientData = clients.byID[id];
            clientData.lastUpdate = getSecondsEpoch();
            if (timeout){
                clientData.updateTimeout = timeout;
            }
            if (!clientData){
                output.error = true;
                output.messages.push("ID does not match any registered clients.");
            } else if (config){
                //possibly a client config update
                if (!config.config){
                    output.error = true;
                    output.messages.push("Invalid config format.");
                } else if (config.config.name != clientData.config.config.name){
                    output.error = true;
                    output.messages.push("Supplied client name does not match registered client name.");
                } else {
                    //I believe this is updating both byID and byName
                    clientData.config = config;
                    configureClient(clientData, output);
                }
            }
            if (!output.error){
                if (publish){
                    try{
                        for (var i = 0; i < publish.length; i++) {
                            wsClient.send(JSON.stringify(publish[i]));
                        }
                    }
                    catch(error){
                        output.error = true;
                        output.messages.push("Error parsing published values: " + error.name + " msg: " + error.message);
                    }
                }
                if (poll){
                    //return stored values
                    var messages = [];
                    for(var type in clientData.received){
                        var nameMap = clientData.received[type];
                        for (var name in nameMap){
                            var bufferObj = nameMap[name];
                            if (bufferObj && bufferObj.buffer.length > 0){
                                messages = messages.concat(bufferObj.buffer);
                                bufferObj.buffer = [];
                            }
                        }
                    }
                    output.received = messages;
                }
            }
        } else if (config){
            if (!config.config){
                output.error = true;
                output.messages.push("Config must contain a 'config' object.");
            } else {
                if (!config.config.name){
                    output.error = true;
                    output.messages.push("Config requires a client 'name'.");
                } else {
                    var existingClient = clients.byName[config.config.name];
                    if (existingClient !== undefined){
                        if (getSecondsEpoch() - existingClient.lastUpdate > existingClient.updateTimeout){
                            delete clients.byName[config.config.name];
                            delete clients.byID[existingClient.id];
                            existingClient = undefined;
                        } else {
                            output.error = true;
                            output.messages.push("Client with provided name already registered with this http_link.");
                        }
                    }
                    if (existingClient === undefined){
                        //valid (perhaps) config
                        clientData = {"config":config};
                        clientData.updateTimeout = timeout || DEFAULT_TIMEOUT;
                        configureClient(clientData, output);
                        if (!output.error){
                            clients.byID[clientID] = clientData;
                            clients.byName[config.config.name] = clientData;
                            id = clientID;
                            clientID++;
                        }
                    }
                }
            }
        } else {
            output.error = true;
            output.messages.push("Send client config or reference registered client by ID.");
        }
    }
    catch (error){
        output.error = true;
        output.messages.push("Unexpected error: " + error.name + " msg: " + error.message);
    }
    output.clientID = id;
    res.end(JSON.stringify(output));

}).listen(port);
<|endoftext|>


<commit_before>"use strict";

// external dependencies
require("jquery");
global.jQuery = require("jquery");
global.$ = global.jQuery;

// angular itself
var angular = require("angular");

// angular dependencies
require("@iamadamjowett/angular-logger-max/logger.service");
require("angular-animate");
require("angular-breadcrumb");
require("angular-clipboard");
require("angular-file-saver");
require("angular-marked");
require("angular-messages");
require("angular-sanitize");
require("angular-slugify");
require("angular-translate");
require("angular-translate-loader-static-files");
require("angular-typewriter");
require("angular-ui-bootstrap");
require("angular-ui-notification");
require("angular-ui-router");
require("ng-tags-input");
require("satellizer");

// the angular module
angular.module("elektra.rest.angular", [
  "angular-clipboard",
  "angular-logger-max",
  require("angular-typewriter"),
  "hc.marked",
  "ncy-angular-breadcrumb",
  "ngAnimate",
  "ngFileSaver",
  "ngMessages",
  "ngSanitize",
  "ngTagsInput",
  "pascalprecht.translate",
  "satellizer",
  "slugifier",
  "ui.bootstrap",
  "ui.router",
  "ui-notification"
]);

// application includes
require("./config");
require("./controllers");
require("./directives");
require("./filter");
require("./run");
require("./services");
<commit_msg>website: fix uirouter scoped package name<commit_after>"use strict";

// external dependencies
require("jquery");
global.jQuery = require("jquery");
global.$ = global.jQuery;

// angular itself
var angular = require("angular");

// angular dependencies
require("@iamadamjowett/angular-logger-max/logger.service");
require("angular-animate");
require("angular-breadcrumb");
require("angular-clipboard");
require("angular-file-saver");
require("angular-marked");
require("angular-messages");
require("angular-sanitize");
require("angular-slugify");
require("angular-translate");
require("angular-translate-loader-static-files");
require("angular-typewriter");
require("angular-ui-bootstrap");
require("angular-ui-notification");
require("@uirouter/angularjs");
require("ng-tags-input");
require("satellizer");

// the angular module
angular.module("elektra.rest.angular", [
  "angular-clipboard",
  "angular-logger-max",
  require("angular-typewriter"),
  "hc.marked",
  "ncy-angular-breadcrumb",
  "ngAnimate",
  "ngFileSaver",
  "ngMessages",
  "ngSanitize",
  "ngTagsInput",
  "pascalprecht.translate",
  "satellizer",
  "slugifier",
  "ui.bootstrap",
  "ui.router",
  "ui-notification"
]);

// application includes
require("./config");
require("./controllers");
require("./directives");
require("./filter");
require("./run");
require("./services");
<|endoftext|>


<commit_before>'use strict';

var provide = function provide(options) {

  if (!options) {
    throw new Error("Missing options");
  }

  var processStr = JSON.stringify({ env: options });

  return {
    intro: function intro() {
      return "var process = " + processStr + ";";
    }
  };
};

module.exports = provide;<commit_msg>commit build<commit_after>'use strict';

var provide = function provide(options) {

  if (!options) {
    throw new Error("Missing options");
  }

  var optionsStr = JSON.stringify(options);

  return {
    intro: function intro() {
      return "\n        var rollupEnv = " + optionsStr + ";\n        var process = process || {};\n        process.env = process.env || {};\n        Object.keys(rollupEnv).forEach(function (prop) {\n          process.env[prop] = rollupEnv[prop];\n        });\n      ";
    }
  };
};

module.exports = provide;<|endoftext|>


<commit_before>import { computed } from 'mobx'
import { createMuiTheme } from '@material-ui/core/styles'
import merge from 'lodash.merge'

const overrides = {
  MuiIconButton: {
    root: {
      height: '2.5rem',
      width: '2.5rem'
    }
  },
  MuiSwitch: {
    root: {
      width: '3.5rem'
    }
  },
  MuiSnackbarContent: {
    root: {
      fontSize: '1.5rem',
      padding: '0 2rem',
      justifyContent: 'center',
      textTransform: 'capitalize',
      backgroundColor: 'rgba(0, 0, 0, 0.618)'
    }
  },
  MuiTooltip: {
    tooltip: {
      display: 'inline-flex'
    }
  }
}

const app = {
  backgroundColor: 'white',
  test: {
    danger: 'white'
  }
}

const theme = { overrides, app }

const darkTheme = {
  overrides,
  app: merge({}, app, {
    backgroundColor: '#202123'
  })
}

export default class ThemeStore {
  constructor (store) {
    this.store = store
  }

  @computed
  get theme () {
    return this.store.userStore.darkTheme ? darkTheme : theme
  }

  @computed
  get muiTheme () {
    return createMuiTheme(this.theme)
  }
}
<commit_msg>Tune merge level<commit_after>import { computed } from 'mobx'
import { createMuiTheme } from '@material-ui/core/styles'
import merge from 'lodash.merge'

const overrides = {
  MuiIconButton: {
    root: {
      height: '2.5rem',
      width: '2.5rem'
    }
  },
  MuiSwitch: {
    root: {
      width: '3.5rem'
    }
  },
  MuiSnackbarContent: {
    root: {
      fontSize: '1.5rem',
      padding: '0 2rem',
      justifyContent: 'center',
      textTransform: 'capitalize',
      backgroundColor: 'rgba(0, 0, 0, 0.618)'
    }
  },
  MuiTooltip: {
    tooltip: {
      display: 'inline-flex'
    }
  }
}

const app = {
  backgroundColor: 'white',
  test: {
    danger: 'white'
  }
}

const theme = { overrides, app }

const darkTheme = merge({}, theme, {
  app: {
    backgroundColor: '#202123'
  }
})

export default class ThemeStore {
  constructor (store) {
    this.store = store
  }

  @computed
  get theme () {
    return this.store.userStore.darkTheme ? darkTheme : theme
  }

  @computed
  get muiTheme () {
    return createMuiTheme(this.theme)
  }
}
<|endoftext|>


<commit_before>module.exports = function(grunt) {

    grunt.initConfig({

        sass: {
            dist: {
                options: {
                    style: 'expanded',
                    compass: false
                },
                files: {
                    'dev/css/style.css': 'dev/sass/style.scss'
                }
            }
        },

        autoprefixer: {
            options: {
                browsers: ['last 10 version']
            },
            multiple_files: {
                expand: true,
                flatten: true,
                src: 'dev/css/*.css',
                dest: 'static/css/'
            }
        },

        cssmin: {
            combine: {
                files: {
                    'static/css/style.min.css': ['static/css/style.css']
                }
            }
        },

        concat: {
            dist: {
                src: [
                    'dev/js/libs/jquery.js',
                    'dev/js/libs/bootstrap.js',
                    'dev/js/scripts.js'
                ],
                dest: 'static/js/production.js',
            }
        },

        uglify: {
            build: {
                src: 'static/js/production.js',
                dest: 'static/js/production.min.js',
            }
        },

        imagemin: {
            dynamic: {
                files: [{
                    expand: true,
                    cwd: 'dev/images/',
                    src: ['**/*.{png,jpg,gif}'],
                    dest: 'static/images/'
                }]
            }
        },

        watch: {
            options: {
                livereload: true,
            },
            scripts: {
                files: ['dev/js/*.js'],
                tasks: ['concat'],
                options: {
                    spawn: false,
                }
            },
            css: {
                files: ['dev/sass/*.scss', 'dev/sass/*/*.scss'],
                tasks: ['sass', 'autoprefixer'],
                options: {
                    spawn: false,
                    livereload: false,
                }
            },
            autoprefixer: {
                files: 'dev/css/**',
                tasks: ['autoprefixer']
            },
            images: {
                files: ['dev/images/*.{png,jpg,gif}'],
                tasks: ['imagemin'],
            }
        },

        connect: {
            server: {
                options: {
                    post: 8000,
                    base: './production/'
                }
            }
        }

    });

    require('load-grunt-tasks')(grunt);

    grunt.registerTask('build', ['concat', 'uglify', 'imagemin', 'sass', 'autoprefixer', 'cssmin']);
    grunt.registerTask('run', ['concat', 'imagemin', 'sass', 'autoprefixer', 'watch']);
    grunt.registerTask('serve', ['connect',]);
    grunt.registerTask('default', ['run'])

};
<commit_msg>change task in gruntfile<commit_after>module.exports = function(grunt) {

    grunt.initConfig({

        sass: {
            dist: {
                options: {
                    style: 'expanded',
                    compass: false
                },
                files: {
                    'dev/css/style.css': 'dev/sass/style.scss'
                }
            }
        },

        autoprefixer: {
            options: {
                browsers: ['last 10 version']
            },
            multiple_files: {
                expand: true,
                flatten: true,
                src: 'dev/css/*.css',
                dest: 'static/css/'
            }
        },

        cssmin: {
            combine: {
                files: {
                    'static/css/style.min.css': ['static/css/style.css']
                }
            }
        },

        concat: {
            dist: {
                src: [
                    'dev/js/libs/jquery.js',
                    'dev/js/libs/bootstrap.js',
                    'dev/js/scripts.js'
                ],
                dest: 'static/js/production.js',
            }
        },

        uglify: {
            build: {
                src: 'static/js/production.js',
                dest: 'static/js/production.min.js',
            }
        },

        imagemin: {
            dynamic: {
                files: [{
                    expand: true,
                    cwd: 'dev/images/',
                    src: ['**/*.{png,jpg,gif}'],
                    dest: 'static/images/'
                }]
            }
        },

        watch: {
            options: {
                livereload: true,
            },
            scripts: {
                files: ['dev/js/*.js'],
                tasks: ['concat'],
                options: {
                    spawn: false,
                }
            },
            css: {
                files: ['dev/sass/*.scss', 'dev/sass/*/*.scss'],
                tasks: ['sass', 'autoprefixer'],
                options: {
                    spawn: false,
                    livereload: false,
                }
            },
            autoprefixer: {
                files: 'dev/css/**',
                tasks: ['autoprefixer']
            },
            images: {
                files: ['dev/images/*.{png,jpg,gif}'],
                tasks: ['imagemin'],
            }
        },

        connect: {
            server: {
                options: {
                    post: 8000,
                    base: './'
                }
            }
        }

    });

    require('load-grunt-tasks')(grunt);

    grunt.registerTask('build', ['concat', 'uglify', 'imagemin', 'sass', 'autoprefixer', 'cssmin']);
    grunt.registerTask('run', ['connect', 'concat', 'imagemin', 'sass', 'autoprefixer', 'watch']);
    grunt.registerTask('serve', ['connect',]);
    grunt.registerTask('default', ['run'])

};
<|endoftext|>


<commit_before>var utilities = require('./utilities');

// argv[2] (required): the name of the active file
// argv[3] (required): the ipv4 address of the server to contact first

var activeFileName = process.argv[2];

var fileContents = "";
var tmpFileContents = fileContents;

var otherServerUri = "http://" + process.argv[3] + ':' +
  utilities.SERVER_HTTP_PORT;

var p = new utilities.p2p.peer(otherServerUri, utilities.SERVER_HTTP_PORT);

// TODO: make proxy solution for network with firewall which blocks all ports

utilities.fs.writeFile(
  activeFileName,
  fileContents,
  function(error) {
    if (error) {
      console.log(error);
    }
    utilities.fs.writeFile(
      activeFileName + utilities.TMP_FILENAME_SUFFIX,
      tmpFileContents,
      function(error) {
        if (error) {
          console.log(error);
        }
        openFileInEmacs(activeFileName);
        p.start(
          // client init function
          function() {
            console.log("speaking on " + utilities.os.hostname() + ':' +
              utilities.SERVER_HTTP_PORT);
          },
          // client socket function
          function(socket) {
            // get user id, filename, and file contents; write to file
            socket.on('connection_info', function(sentFileName) {
              activeFileName = sentFileName;
            });
            socket.on('file_send', function(sentFileContents) {
              // if not self socket
              if ("http://127.0.0.1:" + utilities.SERVER_HTTP_PORT !=
                utilities.p2p.client.getUriOfSocket(socket)) {
                utilities.fs.writeFile(
                  activeFileName,
                  sentFileContents.toString(),
                  function(error) {
                    if (error) {
                      console.log(error);
                    }
                    updateBufferInEmacs(activeFileName);
                  });
              }
            });
            socket.on('file_diff', function(sentFilePatch) {
              if ("http://127.0.0.1:" + utilities.SERVER_HTTP_PORT !=
                utilities.p2p.client.getUriOfSocket(socket)) {
                utilities.fs.readFile(
                  activeFileName,
                  function(error, readFileContents) {
                    utilities.fs.writeFile(
                      activeFileName,
                      utilities.diff_match_patch.patch_apply(
                        sentFilePatch,
                        readFileContents
                      )[0],     // get patched text
                      function(error) {
                        if (error) {
                          console.log(error);
                        }
                        updateBufferInEmacs(activeFileName);
                      });
                  });
              }
            });
          },
          // server init function
          function() {
            console.log("listening on " + utilities.os.hostname() + ':' +
              utilities.SERVER_HTTP_PORT);
            if ("127.0.0.1" == process.argv[3]) { // if initial server
              setInterval(broadcastBuffer, utilities.FILE_SYNC_TIME);
            }
          },
          // server socket function
          function(socket) {
            socket.emit('connection_info', activeFileName);
          });
      });
  });

// requires emacs to be open and server to be on
function openFileInEmacs(filename) {
  var emacsOpenFile = utilities.spawn('emacsclient', ['-n', filename]);
  emacsOpenFile.stdout.on('data', function(data) {
    console.log("emacs stdout: " + data);
  });
  emacsOpenFile.stderr.on('data', function(data) {
    console.log("emacs stderr: " + data);
  });
  emacsOpenFile.on('exit', function(return_code, signal) {
    if (return_code != 0) {
      console.log("error: file could not be opened.");
    } else {
      console.log("FILE OPENED :DDDDDDDDDDDDDDDDDD");
    }
  });
}

function broadcastBuffer() {
  var emacsWriteFile = spawnEmacsCommand(
    "send-buffer-to-file", "\"" + activeFileName + "\"",
    "\"" + utilities.TMP_FILENAME_SUFFIX + "\"");
  emacsWriteFile.stdout.on('data', function(data) {
    console.log("emacs stdout: " + data);
  });
  emacsWriteFile.stderr.on('data', function(data) {
    console.log("emacs stderr: " + data);
  });
  emacsWriteFile.on('exit', function(
    return_code, signal) {
    if (0 != return_code) {
      console.log("error: buffer could not be saved.");
    } else {
      utilities.fs.readFile(
        activeFileName + utilities.TMP_FILENAME_SUFFIX,
        function(error, fileContents) {
          if (error) {
            console.log(error);
          }
          p.emit('file_send', fileContents.toString());
          console.log("file broadcasted")
        });;
    }
  });
}

function broadcastDiff() {
  var emacsWriteFile = spawnEmacsCommand(
    "send-buffer-to-file", "\"" + activeFileName + "\"",
    "\"" + utilities.TMP_FILENAME_SUFFIX + "\"");
  emacsWriteFile.stdout.on('data', function(data) {
    console.log("emacs stdout: " + data)
  });
  emacsWriteFile.stderr.on('data', function(data) {
    console.log("emacs stderr: " + data);
  });
  emacsWriteFile.on('exit', function(return_code, signal) {
    if (0 != return_code) {
      console.log("error: buffer could not be saved.");
    } else {
      utilities.fs.readFile(
        activeFileName + utilities.TMP_FILENAME_SUFFIX,
        function(tmpError, tmpFileContents) {
          if (tmpError) {
            console.log(error);
          }
          utilities.fs.readFile(
            activeFileName,
            function(curError, curFileContents) {
              if (curError) {
                console.log(error);
              }
              utilities.fs.writeFile(
                activeFileName,
                tmpFileContents,
                function(error) {
                  console.log(error);
                  p.emit('file_diff',
                    utilities.diff_match_patch.patch_make(
                      curFileContents, tmpFileContents));
                }
              )
            });
        });
    }
  });
}

function updateBufferInEmacs(filename) {
  var emacsReadFile = spawnEmacsCommand(
    "read-buffer-from-file", "\"" + filename + "\"");
  emacsReadFile.stdout.on('data', function(data) {
    console.log("emacs stdout: " + data);
  });
  emacsReadFile.stderr.on('data', function(data) {
    console.log("emacs stderr: " + data);
  });
  emacsReadFile.on('exit', function(return_code, signal) {
    if (return_code != 0) {
      console.log("error: file could not be read.");
    }
    console.log("file read");
  });
}

// takes variable number of arguments
// returns spawn object
function spawnEmacsCommand() {
  var evalArg = "(";
  for (var i = 0; i < arguments.length; ++i) {
    evalArg += arguments[i] + " ";
  }
  evalArg += ")";
  return utilities.spawn('emacsclient', ['-e', evalArg]);
}
<commit_msg>file transfer<commit_after>var utilities = require('./utilities');

// argv[2] (required): the name of the active file
// argv[3] (required): the ipv4 address of the server to contact first

var activeFileName = process.argv[2];

var fileContents = "";
var tmpFileContents = fileContents;

var otherServerUri = "http://" + process.argv[3] + ':' +
  utilities.SERVER_HTTP_PORT;

var p = new utilities.p2p.peer(otherServerUri, utilities.SERVER_HTTP_PORT);

// TODO: make proxy solution for network with firewall which blocks all ports

utilities.fs.writeFile(
  activeFileName,
  fileContents,
  function(error) {
    if (error) {
      console.log(error);
    }
    utilities.fs.writeFile(
      activeFileName + utilities.TMP_FILENAME_SUFFIX,
      tmpFileContents,
      function(error) {
        if (error) {
          console.log(error);
        }
        openFileInEmacs(activeFileName);
        p.start(
          // client init function
          function() {
            console.log("speaking on " + utilities.os.hostname() + ':' +
              utilities.SERVER_HTTP_PORT);
          },
          // client socket function
          function(socket) {
            // get user id, filename, and file contents; write to file
            socket.on('connection_info', function(sentFileName) {
              activeFileName = sentFileName;
            });
            socket.on('file_send', function(sentFileContents) {
              // if not self socket
              if ("http://127.0.0.1:" + utilities.SERVER_HTTP_PORT !=
                utilities.p2p.client.getUriOfSocket(socket)) {
                utilities.fs.writeFile(
                  activeFileName,
                  sentFileContents.toString(),
                  function(error) {
                    if (error) {
                      console.log(error);
                    }
                    updateBufferInEmacs(activeFileName);
                  });
              }
            });
            socket.on('file_diff', function(sentFilePatch) {
              if ("http://127.0.0.1:" + utilities.SERVER_HTTP_PORT !=
                utilities.p2p.client.getUriOfSocket(socket)) {
                utilities.fs.readFile(
                  activeFileName,
                  function(error, readFileContents) {
                    utilities.fs.writeFile(
                      activeFileName,
                      utilities.diff_match_patch.patch_apply(
                        sentFilePatch,
                        readFileContents
                      )[0], // get patched text
                      function(error) {
                        if (error) {
                          console.log(error);
                        }
                        updateBufferInEmacs(activeFileName);
                      });
                  });
              }
            });
          },
          // server init function
          function() {
            console.log("listening on " + utilities.os.hostname() + ':' +
              utilities.SERVER_HTTP_PORT);
            if ("127.0.0.1" == process.argv[3]) { // if initial server
              setInterval(broadcastBuffer, utilities.FILE_SYNC_TIME);
              setInterval(broadcastDiff, utilities.DIFF_SYNC_TIME);
            }
          },
          // server socket function
          function(socket) {
            socket.emit('connection_info', activeFileName);
          });
      });
  });

// requires emacs to be open and server to be on
function openFileInEmacs(filename) {
  var emacsOpenFile = utilities.spawn('emacsclient', ['-n', filename]);
  emacsOpenFile.stdout.on('data', function(data) {
    console.log("emacs stdout: " + data);
  });
  emacsOpenFile.stderr.on('data', function(data) {
    console.log("emacs stderr: " + data);
  });
  emacsOpenFile.on('exit', function(return_code, signal) {
    if (return_code != 0) {
      console.log("error: file could not be opened.");
    } else {
      console.log("FILE OPENED :DDDDDDDDDDDDDDDDDD");
    }
  });
}

function broadcastBuffer() {
  var emacsWriteFile = spawnEmacsCommand(
    "send-buffer-to-file", "\"" + activeFileName + "\"",
    "\"" + utilities.TMP_FILENAME_SUFFIX + "\"");
  emacsWriteFile.stdout.on('data', function(data) {
    console.log("emacs stdout: " + data);
  });
  emacsWriteFile.stderr.on('data', function(data) {
    console.log("emacs stderr: " + data);
  });
  emacsWriteFile.on('exit', function(
    return_code, signal) {
    if (0 != return_code) {
      console.log("error: buffer could not be saved.");
    } else {
      utilities.fs.readFile(
        activeFileName + utilities.TMP_FILENAME_SUFFIX,
        function(error, fileContents) {
          if (error) {
            console.log(error);
          }
          p.emit('file_send', fileContents.toString());
          console.log("file broadcasted")
        });;
    }
  });
}

function broadcastDiff() {
  var emacsWriteFile = spawnEmacsCommand(
    "send-buffer-to-file", "\"" + activeFileName + "\"",
    "\"" + utilities.TMP_FILENAME_SUFFIX + "\"");
  emacsWriteFile.stdout.on('data', function(data) {
    console.log("emacs stdout: " + data)
  });
  emacsWriteFile.stderr.on('data', function(data) {
    console.log("emacs stderr: " + data);
  });
  emacsWriteFile.on('exit', function(return_code, signal) {
    if (0 != return_code) {
      console.log("error: buffer could not be saved.");
    } else {
      utilities.fs.readFile(
        activeFileName + utilities.TMP_FILENAME_SUFFIX,
        function(tmpError, tmpFileContents) {
          if (tmpError) {
            console.log(error);
          }
          utilities.fs.readFile(
            activeFileName,
            function(curError, curFileContents) {
              if (curError) {
                console.log(error);
              }
              utilities.fs.writeFile(
                activeFileName,
                tmpFileContents,
                function(error) {
                  console.log(error);
                  p.emit('file_diff',
                    utilities.diff_match_patch.patch_make(
                      curFileContents, tmpFileContents));
                }
              )
            });
        });
    }
  });
}

function updateBufferInEmacs(filename) {
  var emacsReadFile = spawnEmacsCommand(
    "read-buffer-from-file", "\"" + filename + "\"");
  emacsReadFile.stdout.on('data', function(data) {
    console.log("emacs stdout: " + data);
  });
  emacsReadFile.stderr.on('data', function(data) {
    console.log("emacs stderr: " + data);
  });
  emacsReadFile.on('exit', function(return_code, signal) {
    if (return_code != 0) {
      console.log("error: file could not be read.");
    }
    console.log("file read");
  });
}

// takes variable number of arguments
// returns spawn object
function spawnEmacsCommand() {
  var evalArg = "(";
  for (var i = 0; i < arguments.length; ++i) {
    evalArg += arguments[i] + " ";
  }
  evalArg += ")";
  return utilities.spawn('emacsclient', ['-e', evalArg]);
}
<|endoftext|>


<commit_before>// Generated by CoffeeScript 1.10.0
var async, checkPermissions, checkReplicationPermissions, db, deleteFiles, errors, helpers, locker, logger;

locker = require('../lib/locker');

db = require('../helpers/db_connect_helper').db_connect();

logger = require('printit')({
  prefix: 'middleware/utils'
});

async = require('async');

errors = require('./errors');

helpers = require('../helpers/utils');

checkReplicationPermissions = helpers.checkReplicationPermissions;

checkPermissions = helpers.checkPermissions;

deleteFiles = helpers.deleteFiles;

module.exports.lockRequest = function(req, res, next) {
  req.lock = req.params.id || req.params.type;
  return locker.runIfUnlock(req.lock, function() {
    locker.addLock(req.lock);
    return next();
  });
};

module.exports.unlockRequest = function(req, res) {
  return locker.removeLock(req.lock);
};

module.exports.getDoc = function(req, res, next) {
  return db.get(req.params.id, function(err, doc) {
    if (err) {
      logger.error(err);
      deleteFiles(req.files);
      return next(err);
    } else if (doc != null) {
      req.doc = doc;
      return next();
    } else {
      deleteFiles(req.files);
      return next(errors.http(404, 'Not found'));
    }
  });
};

module.exports.checkPermissionsFactory = function(permission) {
  return function(req, res, next) {
    return checkPermissions(req, permission, next);
  };
};

module.exports.checkPermissionsByDoc = function(req, res, next) {
  return checkPermissions(req, req.doc.docType, next);
};

module.exports.checkPermissionsByBody = function(req, res, next) {
  return checkPermissions(req, req.body.docType, next);
};

module.exports.checkPermissionsByType = function(req, res, next) {
  return checkPermissions(req, req.params.type, next);
};

module.exports.checkPermissionsPostReplication = function(req, res, next) {
  var err;
  if (req.url.indexOf('/replication/_revs_diff') === 0) {
    return next();
  } else if (req.url === '/replication/_ensure_full_commit') {
    return next();
  } else if (req.url.indexOf('/replication/_changes') === 0) {
    return next();
  } else if (req.url.indexOf('/replication/_bulk_docs') === 0) {
    return async.forEach(req.body.docs, function(doc, cb) {
      var err;
      if (doc._deleted) {
        return db.get(doc._id, function(err, doc) {
          if ((err != null) && err.error === 'not_found') {
            return cb();
          } else if (err) {
            logger.error(err);
            return cb(err);
          } else if (!((doc._id != null) && (doc.docType != null))) {
            err = new Error("Forbidden operation");
            err.status = 403;
            return cb(err);
          } else {
            return checkReplicationPermissions(req, doc, cb);
          }
        });
      } else {
        if (!((doc._id != null) && (doc.docType != null))) {
          err = new Error("Forbidden operation");
          err.status = 403;
          return cb(err);
        } else {
          return checkReplicationPermissions(req, doc, cb);
        }
      }
    }, next);
  } else {
    err = new Error("Forbidden operation");
    err.status = 403;
    return next(err);
  }
};

module.exports.checkPermissionsPutReplication = function(req, res, next) {
  if (req.url.indexOf('/replication/_local') === 0) {
    delete req.body.docType;
    return next();
  } else {
    return next();
  }
};
<commit_msg>built<commit_after>// Generated by CoffeeScript 1.10.0
var async, checkPermissions, checkReplicationPermissions, db, deleteFiles, errors, helpers, locker, logger;

locker = require('../lib/locker');

db = require('../helpers/db_connect_helper').db_connect();

logger = require('printit')({
  prefix: 'middleware/utils'
});

async = require('async');

errors = require('./errors');

helpers = require('../helpers/utils');

checkReplicationPermissions = helpers.checkReplicationPermissions;

checkPermissions = helpers.checkPermissions;

deleteFiles = helpers.deleteFiles;

module.exports.lockRequest = function(req, res, next) {
  req.lock = req.params.id || req.params.type;
  return locker.runIfUnlock(req.lock, function() {
    locker.addLock(req.lock);
    return next();
  });
};

module.exports.unlockRequest = function(req, res) {
  return locker.removeLock(req.lock);
};

module.exports.getDoc = function(req, res, next) {
  return db.get(req.params.id, function(err, doc) {
    if (err) {
      logger.error(err);
      deleteFiles(req.files);
      return next(err);
    } else if (doc != null) {
      req.doc = doc;
      return next();
    } else {
      deleteFiles(req.files);
      return next(errors.http(404, 'Not found'));
    }
  });
};

module.exports.checkPermissionsFactory = function(permission) {
  return function(req, res, next) {
    return checkPermissions(req, permission, next);
  };
};

module.exports.checkPermissionsByDoc = function(req, res, next) {
  return checkPermissions(req, req.doc.docType, next);
};

module.exports.checkPermissionsByBody = function(req, res, next) {
  return checkPermissions(req, req.body.docType, next);
};

module.exports.checkPermissionsByType = function(req, res, next) {
  return checkPermissions(req, req.params.type, next);
};

module.exports.checkPermissionsPostReplication = function(req, res, next) {
  var err;
  if (req.url.indexOf('/replication/_revs_diff') === 0) {
    return next();
  } else if (req.url === '/replication/_ensure_full_commit') {
    return next();
  } else if (req.url.indexOf('/replication/_changes') === 0) {
    return next();
  } else if (req.url.indexOf('/replication/_bulk_docs') === 0) {
    return async.forEach(req.body.docs, function(doc, cb) {
      var err;
      if (doc._deleted) {
        return db.get(doc._id, function(err, doc) {
          if ((err != null) && err.error === 'not_found') {
            return cb();
          } else if (err) {
            logger.error(err);
            return cb(err);
          } else if (doc._id == null) {
            err = new Error("Forbidden operation");
            err.status = 403;
            return cb(err);
          } else {
            return checkReplicationPermissions(req, doc, cb);
          }
        });
      } else {
        if (doc._id == null) {
          err = new Error("Forbidden operation");
          err.status = 403;
          return cb(err);
        } else {
          return checkReplicationPermissions(req, doc, cb);
        }
      }
    }, next);
  } else {
    err = new Error("Forbidden operation");
    err.status = 403;
    return next(err);
  }
};

module.exports.checkPermissionsPutReplication = function(req, res, next) {
  if (req.url.indexOf('/replication/_local') === 0) {
    delete req.body.docType;
    return next();
  } else {
    return next();
  }
};
<|endoftext|>


<commit_before>module.exports = {
    parser: false,
    plugins: {
        'postcss-import': {},
        'postcss-cssnext': {},
        'cssnano': {}
    }
};

<commit_msg>Don't use autoprefixer on cssnano<commit_after>module.exports = {
    parser: false,
    plugins: {
        'postcss-import': {},
        'postcss-cssnext': {},
        'cssnano': {
            autoprefixer: false
        }
    }
};

<|endoftext|>