<commit_before>/**
 * UX Rocket
 * jQuery based accordion
 * @author Bilal Cinarli
 * @dependency jQueryUI Autocomplete
 * @dependency Handlebars
 * @dependency UX Rocket Clear
 */

;
(function($) {
    var ux, // local shorthand

        defaults = {
            type             : "list",
            item             : 10,
            service          : null,
            minLength        : 2,
            formdata         : null,
            categoryTextLimit: 25,
            queryType        : 'GET',
            hidden           : null,
            template         : null,
            highlight        : true,
            arrowSelection   : false,
            arrowNavigation  : true,

            // callbacks
            onReady          : false,
            onSelect         : false,
            onClear          : false,
            onButtonClick    : false
        },

        templates = {
            list    : {
                wrap: '

',
                item: '' +
                    '
' +
                    '   ' +
                    '       {{{name}}}' +
                    '       {{#if title}}' +
                    '
{{substr title 50}}' +
                    '       {{/if}}' +
                    '   ' +
                    '
'
            },
            image   : {
                wrap: '

',
                item: '' +
                    '
' +
                    '   ' +
                    '        ' +
                    '       {{{name}}}' +
                    '       {{#if title}}' +
                    '
{{substr title 50}}' +
                    '       {{/if}}' +
                    '   ' +
                    '
'
            },
            category: {
                wrap: '

',
                head: '' +
                    '
' +
                    '   {{substr category 25}}' +
                    '
',
                item: '' +
                    '
' +
                    '   ' +
                    '       {{{name}}}' +
                    '       {{#if title}}' +
                    '
{{substr title 50}}' +
                    '       {{/if}}' +
                    '   ' +
                    '
'
            },
            tree    : {
                wrap: '

',
                head: '' +
                    '
' +
                    '   {{substr category 25}}' +
                    '
',
                item: '' +
                    '
' +
                    '   ' +
                    '        ' +
                    '       {{{name}}}' +
                    '       {{#if title}}' +
                    '
{{substr title 50}}' +
                    '       {{/if}}' +
                    '   ' +
                    '
'
            }
        },
		events = {
			click: 'click.uxAutocomplete'
		},
		ns = {
			rocket: 'uxRocket',
			data  : 'uxAutocomplete',
			ready : 'uxitd-autocomplete-ready',
			rocketWrap: 'uxitd-plugin-wrap',
			wrap  : 'uxitd-autocomplete-wrap',
			icon : 'icon-search'
		};

    var AutoComplete = function(el, options, selector) {
        var opts = $.extend({}, defaults, options, $(el).data(), {'selector': selector}),
        // cache
            $el = $(el);

        $el.data(ns.data, opts);

        // set element layout
        setLayout($el);

        // call Ready Callback
        callback(opts.onReady);

        // bind actions
        bindActions($el, opts);

        // set template actions
        setTemplate($el, opts);
    };

    var setLayout = function($el) {
        var columns,
            _opts = $el.data(ns.data);

        columns = ' ' + $el.context.className.replace(ns.ready, '');

        if(_opts.selector.charAt(0) == '.') {
            columns = columns.replace(' ' + _opts.selector.substr(1), '');
            columns = columns.replace(_opts.selector.substr(1) + ' ', '');
        }

        if($el.parent().is('.' + ns.rocketWrap)) {
            $el.parent().addClass(ns.wrap + columns + ' group');
        }
        else {
            $el.wrap('');
        }

        $el.after('');
    };

    var setSource = function($el, opts) {
        var fdata = null,
            url_pattern = /^(http|https|\/|\.\/|\.\.\/)/,
            source = opts.service,
            sourceFunction;

        if(!url_pattern.test(source)) {
            source = eval(source);

            sourceFunction = function(request, response) {
                // check if element is active or not : fix for IE
                if(!$el.is(':focus')){
                    return;
                }

                var matcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i");

                response($.map(source.itemList, function(item) {
                    if(matcher.test(item.name)) {
                        return setItem(item);
                    }
                }));
            };
        }
        else {
            sourceFunction = function(request, response) {
                // check if element is active or not : fix for IE
                if(!$el.is(':focus')){
                    return;
                }

                if(opts.formdata != null) {
                    fdata = $(opts.formdata).serialize();
                }

                $.ajax({
                    url     : source,
                    dataType: 'json',
                    type    : opts.queryType,
                    data    : {
                        type    : opts.type,
                        term    : request.term,
                        formdata: fdata
                    },
                    success : function(data) {
                        response($.map(data.itemList.slice(0, opts.item), function(item) {
                            return setItem(item);
                        }));
                    }
                });
            };
        }

        return sourceFunction;
    };

    var bindActions = function($el, opts) {

        if(typeof opts.props !== 'undefined' && opts.props != '') {
            var props = opts.props.split(':');

            $el.next('.' + ns.icon).addClass('uxitd-autocomplete-icon-clickable').on(events.click, function(e) {
                e.preventDefault();
                eval(props[1]);
            });
        }

        if(opts.onButtonClick !== false) {
            $el.next('.' + ns.icon).on(events.click, function() {
                callback(opts.onButtonClick);
            });
        }

        $el.autocomplete({
            open     : function(event, ui) {
                $(".uxitd-autocomplete-category").removeClass('ui-menu-item');

                $(this).autocomplete("widget").css({
                    "width": ($(this).outerWidth() + "px")
                });
            },
            minLength: opts.minLength,
            source   : setSource($el, opts),
            focus    : function(event, ui) {
                if(!opts.arrowNavigation) {
                    event.preventDefault();
                }
            },
            select   : function(event, ui) {
                if(opts.hidden != null) {
                    $(opts.hidden).val(ui.item.id);
                }

                if(opts.arrowSelection === true && event.keyCode == 13 && typeof ui.item.url !== 'undefined') {
					// check if target is blank or not
                    var _target = $("#" + event.currentTarget.id).find(".ui-state-focus a").attr("target");

                    if(_target == '_blank'){
                        window.open(ui.item.url);
                    }
                    else {
                        window.location = ui.item.url;
                    }
                }

                callback(opts.onSelect);
            }
        }).clear({
            clearAlso: opts.hidden,
            onClear  : opts.onClear
        });
    };

    var setTemplate = function($el, opts) {
        var autocomplete = $el.data("uiAutocomplete") ? $el.data("uiAutocomplete") : $el.data("ui-autocomplete"),
            template = {};

        if(opts.template === null) {
            switch(opts.type) {
                default:
                case 'list':
                    template = templates.list;
                    break;
                case 'image':
                    template = templates.image;
                    break;
                case 'category':
                    template = templates.category;
                    break;
                case 'tree':
                    template = templates.tree;
                    break;
            }
        }

        else {
            template = opts.template;
        }

        autocomplete._renderMenu = function(ul, items) {
            var _this = this,
                _list = [],
                currentCategory = "";

            $.each(items, function(index, item) {
                var temp;

                if((opts.type == 'category' || opts.type == 'tree') && typeof item.category !== 'undefined' && item.category != currentCategory) {

                    var categoryHead = Handlebars.compile(template.head)(item);

                    _list.push(categoryHead);

                    currentCategory = item.category;
                }

                temp = _this._renderItemData(ul, item);

                _list.push(temp);
            });

            ul.append(_list);

            if(currentCategory != "") {
                ul.addClass('uxitd-category');
            }
        };

        autocomplete._create = function() {
            var _this = this;

            _this._super();
            _this.widget().menu("option", "items", "> :not(.ui-autocomplete-category)");
        };

        autocomplete._renderItemData = function(ul, item) {
            var rendered,
                listItem = Handlebars.compile(template.item)(item);

            rendered = listItem;

            if(opts.highlight === true) {
                rendered = listItem.replace(
                    new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + $.ui.autocomplete.escapeRegex(this.term) + ")(?![^<>]*>)(?![^&;]+;)", "gi"),
                    '$1');
            }

            return $(rendered)
                .addClass('ui-menu-item')
                .data("ui-autocomplete-item", item)
                .attr('role', 'presentation');
        };
    };

    // fallback for 0.x.x versions
    var setItem = function(item) {
        if(typeof item.value === 'undefined') {
            item.value = item.name;
        }

        /**
         * Fallback for old data json format
         * @deprecated visiblename
         */
        if(typeof item.visiblename !== 'undefined') {
            item.value = item.visiblename;
        }

        return item;
    };

    // global callback
    var callback = function(fn) {
        // if callback string is function call it directly
        if(typeof fn === 'function') {
            call(fn);
        }

        // if callback defined via data-attribute, call it via new Function
        else {
            if(fn !== false) {
                var func = new Function('return ' + fn);
                func();
            }
        }
    };

    // jquery bindings
    ux = $.fn.uxitdautocomplete = $.uxautocomplete = function(options) {
        var selector = this.selector;

        return this.each(function() {
            var $el = $(this),
                autocomplete;

            if($el.hasClass(ns.ready) || $el.hasClass(ns.rocketWrap)) {
                return;
            }

            $el.addClass(ns.ready);
            autocomplete = new AutoComplete(this, options, selector);
        });
    };

    // version
    ux.version = '1.4.6';

    // settings
    ux.settings = defaults;
})(jQuery);<commit_msg>uxRocket registry added<commit_after>/**
 * UX Rocket
 * jQuery based accordion
 * @author Bilal Cinarli
 * @dependency jQueryUI Autocomplete
 * @dependency Handlebars
 * @dependency UX Rocket Clear
 */

;
(function($) {
    var ux, // local shorthand

        defaults = {
            type             : "list",
            item             : 10,
            service          : null,
            minLength        : 2,
            formdata         : null,
            categoryTextLimit: 25,
            queryType        : 'GET',
            hidden           : null,
            template         : null,
            highlight        : true,
            arrowSelection   : false,
            arrowNavigation  : true,

            // callbacks
            onReady          : false,
            onSelect         : false,
            onClear          : false,
            onButtonClick    : false
        },

        templates = {
            list    : {
                wrap: '

',
                item: '' +
                    '
' +
                    '   ' +
                    '       {{{name}}}' +
                    '       {{#if title}}' +
                    '
{{substr title 50}}' +
                    '       {{/if}}' +
                    '   ' +
                    '
'
            },
            image   : {
                wrap: '

',
                item: '' +
                    '
' +
                    '   ' +
                    '        ' +
                    '       {{{name}}}' +
                    '       {{#if title}}' +
                    '
{{substr title 50}}' +
                    '       {{/if}}' +
                    '   ' +
                    '
'
            },
            category: {
                wrap: '

',
                head: '' +
                    '
' +
                    '   {{substr category 25}}' +
                    '
',
                item: '' +
                    '
' +
                    '   ' +
                    '       {{{name}}}' +
                    '       {{#if title}}' +
                    '
{{substr title 50}}' +
                    '       {{/if}}' +
                    '   ' +
                    '
'
            },
            tree    : {
                wrap: '

',
                head: '' +
                    '
' +
                    '   {{substr category 25}}' +
                    '
',
                item: '' +
                    '
' +
                    '   ' +
                    '        ' +
                    '       {{{name}}}' +
                    '       {{#if title}}' +
                    '
{{substr title 50}}' +
                    '       {{/if}}' +
                    '   ' +
                    '
'
            }
        },
		events = {
			click: 'click.uxAutocomplete'
		},
		ns = {
			rocket	  : 'uxRocket',
			data  	  : 'uxAutocomplete',
			ready 	  : 'uxitd-autocomplete-ready',
			rocketWrap: 'uxitd-plugin-wrap',
			wrap 	  : 'uxitd-autocomplete-wrap',
			icon 	  : 'icon-search'
		};

    var AutoComplete = function(el, options, selector) {
        var opts = $.extend({}, defaults, options, $(el).data(), {'selector': selector}),
        // cache
            $el = $(el);

        $el.data(ns.data, opts);

        // set element layout
        setLayout($el);

        // call Ready Callback
        callback(opts.onReady);

        // bind actions
        bindActions($el, opts);

        // set template actions
        setTemplate($el, opts);
    };

    var setLayout = function($el) {
        var columns,
            _opts = $el.data(ns.data);

        columns = ' ' + $el.context.className.replace(ns.ready, '');

        if(_opts.selector.charAt(0) == '.') {
            columns = columns.replace(' ' + _opts.selector.substr(1), '');
            columns = columns.replace(_opts.selector.substr(1) + ' ', '');
        }

        if($el.parent().is('.' + ns.rocketWrap)) {
            $el.parent().addClass(ns.wrap + columns + ' group');
        }
        else {
            $el.wrap('');
        }

        $el.after('');
    };

    var setSource = function($el, opts) {
        var fdata = null,
            url_pattern = /^(http|https|\/|\.\/|\.\.\/)/,
            source = opts.service,
            sourceFunction;

        if(!url_pattern.test(source)) {
            source = eval(source);

            sourceFunction = function(request, response) {
                // check if element is active or not : fix for IE
                if(!$el.is(':focus')){
                    return;
                }

                var matcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i");

                response($.map(source.itemList, function(item) {
                    if(matcher.test(item.name)) {
                        return setItem(item);
                    }
                }));
            };
        }
        else {
            sourceFunction = function(request, response) {
                // check if element is active or not : fix for IE
                if(!$el.is(':focus')){
                    return;
                }

                if(opts.formdata != null) {
                    fdata = $(opts.formdata).serialize();
                }

                $.ajax({
                    url     : source,
                    dataType: 'json',
                    type    : opts.queryType,
                    data    : {
                        type    : opts.type,
                        term    : request.term,
                        formdata: fdata
                    },
                    success : function(data) {
                        response($.map(data.itemList.slice(0, opts.item), function(item) {
                            return setItem(item);
                        }));
                    }
                });
            };
        }

        return sourceFunction;
    };

    var bindActions = function($el, opts) {
        if(typeof opts.props !== 'undefined' && opts.props != '') {
            var props = opts.props.split(':');

            $el.next('.' + ns.icon).addClass('uxitd-autocomplete-icon-clickable').on(events.click, function(e) {
                e.preventDefault();
                eval(props[1]);
            });
        }

        if(opts.onButtonClick !== false) {
            $el.next('.' + ns.icon).on(events.click, function() {
                callback(opts.onButtonClick);
            });
        }

        $el.autocomplete({
            open     : function(event, ui) {
                $(".uxitd-autocomplete-category").removeClass('ui-menu-item');

                $(this).autocomplete("widget").css({
                    "width": ($(this).outerWidth() + "px")
                });
            },
            minLength: opts.minLength,
            source   : setSource($el, opts),
            focus    : function(event, ui) {
                if(!opts.arrowNavigation) {
                    event.preventDefault();
                }
            },
            select   : function(event, ui) {
                if(opts.hidden != null) {
                    $(opts.hidden).val(ui.item.id);
                }

                if(opts.arrowSelection === true && event.keyCode == 13 && typeof ui.item.url !== 'undefined') {
					// check if target is blank or not
                    var _target = $("#" + event.currentTarget.id).find(".ui-state-focus a").attr("target");

                    if(_target == '_blank'){
                        window.open(ui.item.url);
                    }
                    else {
                        window.location = ui.item.url;
                    }
                }

                callback(opts.onSelect);
            }
        }).clear({
            clearAlso: opts.hidden,
            onClear  : opts.onClear
        });
    };

    var setTemplate = function($el, opts) {
        var autocomplete = $el.data("uiAutocomplete") ? $el.data("uiAutocomplete") : $el.data("ui-autocomplete"),
            template = {};

        if(opts.template === null) {
            switch(opts.type) {
                default:
                case 'list':
                    template = templates.list;
                    break;
                case 'image':
                    template = templates.image;
                    break;
                case 'category':
                    template = templates.category;
                    break;
                case 'tree':
                    template = templates.tree;
                    break;
            }
        }

        else {
            template = opts.template;
        }

        autocomplete._renderMenu = function(ul, items) {
            var _this = this,
                _list = [],
                currentCategory = "";

            $.each(items, function(index, item) {
                var temp;

                if((opts.type == 'category' || opts.type == 'tree') && typeof item.category !== 'undefined' && item.category != currentCategory) {

                    var categoryHead = Handlebars.compile(template.head)(item);

                    _list.push(categoryHead);

                    currentCategory = item.category;
                }

                temp = _this._renderItemData(ul, item);

                _list.push(temp);
            });

            ul.append(_list);

            if(currentCategory != "") {
                ul.addClass('uxitd-category');
            }
        };

        autocomplete._create = function() {
            var _this = this;

            _this._super();
            _this.widget().menu("option", "items", "> :not(.ui-autocomplete-category)");
        };

        autocomplete._renderItemData = function(ul, item) {
            var rendered,
                listItem = Handlebars.compile(template.item)(item);

            rendered = listItem;

            if(opts.highlight === true) {
                rendered = listItem.replace(
                    new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + $.ui.autocomplete.escapeRegex(this.term) + ")(?![^<>]*>)(?![^&;]+;)", "gi"),
                    '$1');
            }

            return $(rendered)
                .addClass('ui-menu-item')
                .data("ui-autocomplete-item", item)
                .attr('role', 'presentation');
        };
    };

    // fallback for 0.x.x versions
    var setItem = function(item) {
        if(typeof item.value === 'undefined') {
            item.value = item.name;
        }

        /**
         * Fallback for old data json format
         * @deprecated visiblename
         */
        if(typeof item.visiblename !== 'undefined') {
            item.value = item.visiblename;
        }

        return item;
    };

    // global callback
    var callback = function(fn) {
        // if callback string is function call it directly
        if(typeof fn === 'function') {
            call(fn);
        }

        // if callback defined via data-attribute, call it via new Function
        else {
            if(fn !== false) {
                var func = new Function('return ' + fn);
                func();
            }
        }
    };

    // jquery bindings
    ux = $.fn.uxitdautocomplete = $.uxautocomplete = function(options) {
        var selector = this.selector;

        return this.each(function() {
            var $el = $(this),
				uxrocket = $el.data('uxRocket') || {},
                autocomplete;

            if($el.hasClass(ns.ready) || $el.hasClass(ns.rocketWrap)) {
                return;
            }

            $el.addClass(ns.ready);

            uxrocket[ns.data] = {'hasWrapper': true, 'wrapper': ns.wrap, 'ready': ns.ready, 'selector': selector, 'options': options};

            $el.data(ns.rocket, uxrocket);

            autocomplete = new AutoComplete(this, options, selector);
        });
    };

    // version
    ux.version = '1.4.6';

    // settings
    ux.settings = defaults;
})(jQuery);<|endoftext|>


<commit_before>import React from 'react';

import {CompositeDisposable, Disposable} from 'atom';
import cx from 'classnames';
import {autobind} from 'core-decorators';

import HunkView from './hunk-view';
import Commands, {Command} from './commands';
import FilePatchSelection from './file-patch-selection';
import EventWatcher from '../event-watcher';

export default class FilePatchView extends React.Component {
  static propTypes = {
    commandRegistry: React.PropTypes.object.isRequired,
    filePath: React.PropTypes.string.isRequired,
    hunks: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
    stagingStatus: React.PropTypes.oneOf(['unstaged', 'staged']).isRequired,
    isPartiallyStaged: React.PropTypes.bool.isRequired,
    hasUndoHistory: React.PropTypes.bool.isRequired,
    attemptLineStageOperation: React.PropTypes.func.isRequired,
    attemptHunkStageOperation: React.PropTypes.func.isRequired,
    discardLines: React.PropTypes.func.isRequired,
    undoLastDiscard: React.PropTypes.func.isRequired,
    openCurrentFile: React.PropTypes.func.isRequired,
    didSurfaceFile: React.PropTypes.func.isRequired,
    didDiveIntoCorrespondingFilePatch: React.PropTypes.func.isRequired,
    eventWatcher: React.PropTypes.instanceOf(EventWatcher),
  }

  static defaultProps = {
    eventWatcher: new EventWatcher(),
  }

  constructor(props, context) {
    super(props, context);

    this.mouseSelectionInProgress = false;

    window.addEventListener('mouseup', this.mouseup);
    this.disposables = new CompositeDisposable();
    this.disposables.add(new Disposable(() => window.removeEventListener('mouseup', this.mouseup)));

    this.state = {
      selection: new FilePatchSelection(this.props.hunks),
    };
  }

  componentWillReceiveProps(nextProps) {
    const hunksChanged = this.props.hunks.length !== nextProps.hunks.length ||
      this.props.hunks.some((hunk, index) => hunk !== nextProps.hunks[index]);

    if (hunksChanged) {
      this.setState(prevState => {
        return {
          selection: prevState.selection.updateHunks(nextProps.hunks),
        };
      }, () => {
        nextProps.eventWatcher.resolvePatchChangedPromise();
      });
    }
  }

  render() {
    const selectedHunks = this.state.selection.getSelectedHunks();
    const selectedLines = this.state.selection.getSelectedLines();
    const headHunk = this.state.selection.getHeadHunk();
    const headLine = this.state.selection.getHeadLine();
    const hunkSelectionMode = this.state.selection.getMode() === 'hunk';

    const unstaged = this.props.stagingStatus === 'unstaged';
    const stageButtonLabelPrefix = unstaged ? 'Stage' : 'Unstage';

    return (

 { this.element = e; }}>

        <Commands registry={this.props.commandRegistry} target=".github-FilePatchView">
          <Command command="github:toggle-patch-selection-mode" callback={this.togglePatchSelectionMode} />
          <Command command="core:confirm" callback={this.didConfirm} />
          <Command command="core:move-up" callback={this.selectPrevious} />
          <Command command="core:move-down" callback={this.selectNext} />
          <Command command="core:move-right" callback={this.didMoveRight} />
          <Command command="core:move-to-top" callback={this.selectFirst} />
          <Command command="core:move-to-bottom" callback={this.selectLast} />
          <Command command="core:select-up" callback={this.selectToPrevious} />
          <Command command="core:select-down" callback={this.selectToNext} />
          <Command command="core:select-to-top" callback={this.selectToFirst} />
          <Command command="core:select-to-bottom" callback={this.selectToLast} />
          <Command command="core:select-all" callback={this.selectAll} />
          <Command command="github:select-next-hunk" callback={this.selectNextHunk} />
          <Command command="github:select-previous-hunk" callback={this.selectPreviousHunk} />
          <Command command="github:open-file" callback={this.openFile} />
          <Command
            command="github:view-corresponding-diff"
            callback={() => this.props.isPartiallyStaged && this.props.didDiveIntoCorrespondingFilePatch()}
          />
          <Command command="github:discard-selected-lines" callback={this.discardSelection} />
          <Command
            command="core:undo"
            callback={() => this.props.hasUndoHistory && this.props.undoLastDiscard()}
          />
        </Commands>

        <Commands registry={this.props.commandRegistry} target="atom-workspace">
          <Command
            command="github:undo-last-discard-in-diff-view"
            callback={() => this.props.hasUndoHistory && this.props.undoLastDiscard()}
          />
        </Commands>

            {unstaged ? 'Unstaged Changes for ' : 'Staged Changes for '}
            {this.props.filePath}

          {this.renderButtonGroup()}

          {this.props.hunks.map(hunk => {
            const isSelected = selectedHunks.has(hunk);
            let stageButtonSuffix = (hunkSelectionMode || !isSelected) ? ' Hunk' : ' Selection';
            if (selectedHunks.size > 1 && selectedHunks.has(hunk)) {
              stageButtonSuffix += 's';
            }
            const stageButtonLabel = stageButtonLabelPrefix + stageButtonSuffix;

            return (
              <HunkView
                key={hunk.getHeader()}
                hunk={hunk}
                isSelected={selectedHunks.has(hunk)}
                hunkSelectionMode={hunkSelectionMode}
                stageButtonLabel={stageButtonLabel}
                selectedLines={selectedLines}
                headLine={headLine}
                headHunk={headHunk}
                mousedownOnHeader={e => this.mousedownOnHeader(e, hunk)}
                mousedownOnLine={this.mousedownOnLine}
                mousemoveOnLine={this.mousemoveOnLine}
                contextMenuOnItem={this.contextMenuOnItem}
                didClickStageButton={() => this.didClickStageButtonForHunk(hunk)}
              />
            );
          })}

    );
  }

  @autobind
  renderButtonGroup() {
    const unstaged = this.props.stagingStatus === 'unstaged';

    return (

        {this.props.hasUndoHistory && unstaged ? (
          <button
            className="btn icon icon-history"
            onClick={this.props.undoLastDiscard}>
            Undo Discard
          </button>
        ) : null}
        {this.props.isPartiallyStaged ? (
          <button
            className={cx('btn', 'icon', {'icon-tasklist': unstaged, 'icon-list-unordered': !unstaged})}
            onClick={this.props.didDiveIntoCorrespondingFilePatch}
          />
        ) : null}
        <button
          className="btn icon icon-code"
          onClick={this.openFile}
        />
        <button
          className={cx('btn', 'icon', {'icon-move-down': unstaged, 'icon-move-up': !unstaged})}
          onClick={this.stageOrUnstageAll}>
          {unstaged ? 'Stage All' : 'Unstage All'}
        </button>

    );
  }

  componentWillUnmount() {
    this.disposables.dispose();
  }

  @autobind
  contextMenuOnItem(event, hunk, line) {
    const resend = () => {
      event.stopPropagation();

      const newEvent = new MouseEvent(event.type, event);
      requestAnimationFrame(() => {
        event.target.parentNode.dispatchEvent(newEvent);
      });
    };

    const mode = this.state.selection.getMode();
    if (mode === 'hunk' && !this.state.selection.getSelectedHunks().has(hunk)) {
      this.setState(prevState => {
        return {selection: prevState.selection.selectHunk(hunk, event.shiftKey)};
      }, resend);
    } else if (mode === 'line' && !this.state.selection.getSelectedLines().has(line)) {
      this.setState(prevState => {
        return {selection: prevState.selection.selectLine(line, event.shiftKey)};
      }, resend);
    }
  }

  mousedownOnHeader(event, hunk) {
    if (event.button !== 0) { return; }
    const windows = process.platform === 'win32';
    if (event.ctrlKey && !windows) { return; } // simply open context menu

    this.mouseSelectionInProgress = true;
    event.persist && event.persist();

    this.setState(prevState => {
      let selection = prevState.selection;
      if (event.metaKey || (event.ctrlKey && windows)) {
        if (selection.getMode() === 'hunk') {
          selection = selection.addOrSubtractHunkSelection(hunk);
        } else {
          // TODO: optimize
          selection = hunk.getLines().reduce(
            (current, line) => current.addOrSubtractLineSelection(line).coalesce(),
            selection,
          );
        }
      } else if (event.shiftKey) {
        if (selection.getMode() === 'hunk') {
          selection = selection.selectHunk(hunk, true);
        } else {
          const hunkLines = hunk.getLines();
          const tailIndex = selection.getLineSelectionTailIndex();
          const selectedHunkAfterTail = tailIndex < hunkLines[0].diffLineNumber;
          if (selectedHunkAfterTail) {
            selection = selection.selectLine(hunkLines[hunkLines.length - 1], true);
          } else {
            selection = selection.selectLine(hunkLines[0], true);
          }
        }
      } else {
        selection = selection.selectHunk(hunk, false);
      }

      return {selection};
    });
  }

  @autobind
  mousedownOnLine(event, hunk, line) {
    if (event.button !== 0) { return; }
    const windows = process.platform === 'win32';
    if (event.ctrlKey && !windows) { return; } // simply open context menu

    this.mouseSelectionInProgress = true;
    event.persist && event.persist();

    this.setState(prevState => {
      let selection = prevState.selection;

      if (event.metaKey || (event.ctrlKey && windows)) {
        if (selection.getMode() === 'hunk') {
          selection = selection.addOrSubtractHunkSelection(hunk);
        } else {
          selection = selection.addOrSubtractLineSelection(line);
        }
      } else if (event.shiftKey) {
        if (selection.getMode() === 'hunk') {
          selection = selection.selectHunk(hunk, true);
        } else {
          selection = selection.selectLine(line, true);
        }
      } else if (event.detail === 1) {
        selection = selection.selectLine(line, false);
      } else if (event.detail === 2) {
        selection = selection.selectHunk(hunk, false);
      }

      return {selection};
    });
  }

  @autobind
  mousemoveOnLine(event, hunk, line) {
    if (!this.mouseSelectionInProgress) { return; }

    this.setState(prevState => {
      let selection = null;
      if (prevState.selection.getMode() === 'hunk') {
        selection = prevState.selection.selectHunk(hunk, true);
      } else {
        selection = prevState.selection.selectLine(line, true);
      }
      return {selection};
    });
  }

  @autobind
  mouseup() {
    this.mouseSelectionInProgress = false;
    this.setState(prevState => {
      return {selection: prevState.selection.coalesce()};
    });
  }

  @autobind
  togglePatchSelectionMode() {
    this.setState(prevState => ({selection: prevState.selection.toggleMode()}));
  }

  getPatchSelectionMode() {
    return this.state.selection.getMode();
  }

  getSelectedHunks() {
    return this.state.selection.getSelectedHunks();
  }

  getSelectedLines() {
    return this.state.selection.getSelectedLines();
  }

  @autobind
  selectNext() {
    this.setState(prevState => ({selection: prevState.selection.selectNext()}));
  }

  @autobind
  selectNextHunk() {
    this.setState(prevState => ({selection: prevState.selection.jumpToNextHunk()}));
  }

  @autobind
  selectToNext() {
    this.setState(prevState => {
      return {selection: prevState.selection.selectNext(true).coalesce()};
    });
  }

  @autobind
  selectPrevious() {
    this.setState(prevState => ({selection: prevState.selection.selectPrevious()}));
  }

  @autobind
  selectPreviousHunk() {
    this.setState(prevState => ({selection: prevState.selection.jumpToPreviousHunk()}));
  }

  @autobind
  selectToPrevious() {
    this.setState(prevState => {
      return {selection: prevState.selection.selectPrevious(true).coalesce()};
    });
  }

  @autobind
  selectFirst() {
    this.setState(prevState => ({selection: prevState.selection.selectFirst()}));
  }

  @autobind
  selectToFirst() {
    this.setState(prevState => ({selection: prevState.selection.selectFirst(true)}));
  }

  @autobind
  selectLast() {
    this.setState(prevState => ({selection: prevState.selection.selectLast()}));
  }

  @autobind
  selectToLast() {
    this.setState(prevState => ({selection: prevState.selection.selectLast(true)}));
  }

  @autobind
  selectAll() {
    this.setState(prevState => ({selection: prevState.selection.selectAll()}));
  }

  getNextHunkUpdatePromise() {
    return this.state.selection.getNextUpdatePromise();
  }

  didClickStageButtonForHunk(hunk) {
    if (this.state.selection.getSelectedHunks().has(hunk)) {
      this.props.attemptLineStageOperation(this.state.selection.getSelectedLines());
    } else {
      this.setState(prevState => ({selection: prevState.selection.selectHunk(hunk)}), () => {
        this.props.attemptHunkStageOperation(hunk);
      });
    }
  }

  @autobind
  didConfirm() {
    return this.didClickStageButtonForHunk([...this.state.selection.getSelectedHunks()][0]);
  }

  @autobind
  didMoveRight() {
    if (this.props.didSurfaceFile) {
      this.props.didSurfaceFile();
    }
  }

  focus() {
    this.element.focus();
  }

  @autobind
  openFile() {
    let lineNumber = 0;
    const firstSelectedLine = Array.from(this.state.selection.getSelectedLines())[0];
    if (firstSelectedLine && firstSelectedLine.newLineNumber > -1) {
      lineNumber = firstSelectedLine.newLineNumber;
    } else {
      const firstSelectedHunk = Array.from(this.state.selection.getSelectedHunks())[0];
      lineNumber = firstSelectedHunk ? firstSelectedHunk.getNewStartRow() : 0;
    }
    return this.props.openCurrentFile({lineNumber});
  }

  @autobind
  async stageOrUnstageAll() {
    await this.selectAll();
    this.didConfirm();
  }

  @autobind
  discardSelection() {
    const selectedLines = this.state.selection.getSelectedLines();
    return selectedLines.size ? this.props.discardLines(selectedLines) : null;
  }
}
<commit_msg>Event subscriptions in componentDidMount<commit_after>import React from 'react';

import {CompositeDisposable, Disposable} from 'atom';
import cx from 'classnames';
import {autobind} from 'core-decorators';

import HunkView from './hunk-view';
import Commands, {Command} from './commands';
import FilePatchSelection from './file-patch-selection';
import EventWatcher from '../event-watcher';

export default class FilePatchView extends React.Component {
  static propTypes = {
    commandRegistry: React.PropTypes.object.isRequired,
    filePath: React.PropTypes.string.isRequired,
    hunks: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
    stagingStatus: React.PropTypes.oneOf(['unstaged', 'staged']).isRequired,
    isPartiallyStaged: React.PropTypes.bool.isRequired,
    hasUndoHistory: React.PropTypes.bool.isRequired,
    attemptLineStageOperation: React.PropTypes.func.isRequired,
    attemptHunkStageOperation: React.PropTypes.func.isRequired,
    discardLines: React.PropTypes.func.isRequired,
    undoLastDiscard: React.PropTypes.func.isRequired,
    openCurrentFile: React.PropTypes.func.isRequired,
    didSurfaceFile: React.PropTypes.func.isRequired,
    didDiveIntoCorrespondingFilePatch: React.PropTypes.func.isRequired,
    eventWatcher: React.PropTypes.instanceOf(EventWatcher),
  }

  static defaultProps = {
    eventWatcher: new EventWatcher(),
  }

  constructor(props, context) {
    super(props, context);

    this.mouseSelectionInProgress = false;
    this.disposables = new CompositeDisposable();

    this.state = {
      selection: new FilePatchSelection(this.props.hunks),
    };
  }

  componentDidMount() {
    window.addEventListener('mouseup', this.mouseup);
    this.disposables.add(new Disposable(() => window.removeEventListener('mouseup', this.mouseup)));
  }

  componentWillReceiveProps(nextProps) {
    const hunksChanged = this.props.hunks.length !== nextProps.hunks.length ||
      this.props.hunks.some((hunk, index) => hunk !== nextProps.hunks[index]);

    if (hunksChanged) {
      this.setState(prevState => {
        return {
          selection: prevState.selection.updateHunks(nextProps.hunks),
        };
      }, () => {
        nextProps.eventWatcher.resolvePatchChangedPromise();
      });
    }
  }

  render() {
    const selectedHunks = this.state.selection.getSelectedHunks();
    const selectedLines = this.state.selection.getSelectedLines();
    const headHunk = this.state.selection.getHeadHunk();
    const headLine = this.state.selection.getHeadLine();
    const hunkSelectionMode = this.state.selection.getMode() === 'hunk';

    const unstaged = this.props.stagingStatus === 'unstaged';
    const stageButtonLabelPrefix = unstaged ? 'Stage' : 'Unstage';

    return (

 { this.element = e; }}>

        <Commands registry={this.props.commandRegistry} target=".github-FilePatchView">
          <Command command="github:toggle-patch-selection-mode" callback={this.togglePatchSelectionMode} />
          <Command command="core:confirm" callback={this.didConfirm} />
          <Command command="core:move-up" callback={this.selectPrevious} />
          <Command command="core:move-down" callback={this.selectNext} />
          <Command command="core:move-right" callback={this.didMoveRight} />
          <Command command="core:move-to-top" callback={this.selectFirst} />
          <Command command="core:move-to-bottom" callback={this.selectLast} />
          <Command command="core:select-up" callback={this.selectToPrevious} />
          <Command command="core:select-down" callback={this.selectToNext} />
          <Command command="core:select-to-top" callback={this.selectToFirst} />
          <Command command="core:select-to-bottom" callback={this.selectToLast} />
          <Command command="core:select-all" callback={this.selectAll} />
          <Command command="github:select-next-hunk" callback={this.selectNextHunk} />
          <Command command="github:select-previous-hunk" callback={this.selectPreviousHunk} />
          <Command command="github:open-file" callback={this.openFile} />
          <Command
            command="github:view-corresponding-diff"
            callback={() => this.props.isPartiallyStaged && this.props.didDiveIntoCorrespondingFilePatch()}
          />
          <Command command="github:discard-selected-lines" callback={this.discardSelection} />
          <Command
            command="core:undo"
            callback={() => this.props.hasUndoHistory && this.props.undoLastDiscard()}
          />
        </Commands>

        <Commands registry={this.props.commandRegistry} target="atom-workspace">
          <Command
            command="github:undo-last-discard-in-diff-view"
            callback={() => this.props.hasUndoHistory && this.props.undoLastDiscard()}
          />
        </Commands>

            {unstaged ? 'Unstaged Changes for ' : 'Staged Changes for '}
            {this.props.filePath}

          {this.renderButtonGroup()}

          {this.props.hunks.map(hunk => {
            const isSelected = selectedHunks.has(hunk);
            let stageButtonSuffix = (hunkSelectionMode || !isSelected) ? ' Hunk' : ' Selection';
            if (selectedHunks.size > 1 && selectedHunks.has(hunk)) {
              stageButtonSuffix += 's';
            }
            const stageButtonLabel = stageButtonLabelPrefix + stageButtonSuffix;

            return (
              <HunkView
                key={hunk.getHeader()}
                hunk={hunk}
                isSelected={selectedHunks.has(hunk)}
                hunkSelectionMode={hunkSelectionMode}
                stageButtonLabel={stageButtonLabel}
                selectedLines={selectedLines}
                headLine={headLine}
                headHunk={headHunk}
                mousedownOnHeader={e => this.mousedownOnHeader(e, hunk)}
                mousedownOnLine={this.mousedownOnLine}
                mousemoveOnLine={this.mousemoveOnLine}
                contextMenuOnItem={this.contextMenuOnItem}
                didClickStageButton={() => this.didClickStageButtonForHunk(hunk)}
              />
            );
          })}

    );
  }

  @autobind
  renderButtonGroup() {
    const unstaged = this.props.stagingStatus === 'unstaged';

    return (

        {this.props.hasUndoHistory && unstaged ? (
          <button
            className="btn icon icon-history"
            onClick={this.props.undoLastDiscard}>
            Undo Discard
          </button>
        ) : null}
        {this.props.isPartiallyStaged ? (
          <button
            className={cx('btn', 'icon', {'icon-tasklist': unstaged, 'icon-list-unordered': !unstaged})}
            onClick={this.props.didDiveIntoCorrespondingFilePatch}
          />
        ) : null}
        <button
          className="btn icon icon-code"
          onClick={this.openFile}
        />
        <button
          className={cx('btn', 'icon', {'icon-move-down': unstaged, 'icon-move-up': !unstaged})}
          onClick={this.stageOrUnstageAll}>
          {unstaged ? 'Stage All' : 'Unstage All'}
        </button>

    );
  }

  componentWillUnmount() {
    this.disposables.dispose();
  }

  @autobind
  contextMenuOnItem(event, hunk, line) {
    const resend = () => {
      event.stopPropagation();

      const newEvent = new MouseEvent(event.type, event);
      requestAnimationFrame(() => {
        event.target.parentNode.dispatchEvent(newEvent);
      });
    };

    const mode = this.state.selection.getMode();
    if (mode === 'hunk' && !this.state.selection.getSelectedHunks().has(hunk)) {
      this.setState(prevState => {
        return {selection: prevState.selection.selectHunk(hunk, event.shiftKey)};
      }, resend);
    } else if (mode === 'line' && !this.state.selection.getSelectedLines().has(line)) {
      this.setState(prevState => {
        return {selection: prevState.selection.selectLine(line, event.shiftKey)};
      }, resend);
    }
  }

  mousedownOnHeader(event, hunk) {
    if (event.button !== 0) { return; }
    const windows = process.platform === 'win32';
    if (event.ctrlKey && !windows) { return; } // simply open context menu

    this.mouseSelectionInProgress = true;
    event.persist && event.persist();

    this.setState(prevState => {
      let selection = prevState.selection;
      if (event.metaKey || (event.ctrlKey && windows)) {
        if (selection.getMode() === 'hunk') {
          selection = selection.addOrSubtractHunkSelection(hunk);
        } else {
          // TODO: optimize
          selection = hunk.getLines().reduce(
            (current, line) => current.addOrSubtractLineSelection(line).coalesce(),
            selection,
          );
        }
      } else if (event.shiftKey) {
        if (selection.getMode() === 'hunk') {
          selection = selection.selectHunk(hunk, true);
        } else {
          const hunkLines = hunk.getLines();
          const tailIndex = selection.getLineSelectionTailIndex();
          const selectedHunkAfterTail = tailIndex < hunkLines[0].diffLineNumber;
          if (selectedHunkAfterTail) {
            selection = selection.selectLine(hunkLines[hunkLines.length - 1], true);
          } else {
            selection = selection.selectLine(hunkLines[0], true);
          }
        }
      } else {
        selection = selection.selectHunk(hunk, false);
      }

      return {selection};
    });
  }

  @autobind
  mousedownOnLine(event, hunk, line) {
    if (event.button !== 0) { return; }
    const windows = process.platform === 'win32';
    if (event.ctrlKey && !windows) { return; } // simply open context menu

    this.mouseSelectionInProgress = true;
    event.persist && event.persist();

    this.setState(prevState => {
      let selection = prevState.selection;

      if (event.metaKey || (event.ctrlKey && windows)) {
        if (selection.getMode() === 'hunk') {
          selection = selection.addOrSubtractHunkSelection(hunk);
        } else {
          selection = selection.addOrSubtractLineSelection(line);
        }
      } else if (event.shiftKey) {
        if (selection.getMode() === 'hunk') {
          selection = selection.selectHunk(hunk, true);
        } else {
          selection = selection.selectLine(line, true);
        }
      } else if (event.detail === 1) {
        selection = selection.selectLine(line, false);
      } else if (event.detail === 2) {
        selection = selection.selectHunk(hunk, false);
      }

      return {selection};
    });
  }

  @autobind
  mousemoveOnLine(event, hunk, line) {
    if (!this.mouseSelectionInProgress) { return; }

    this.setState(prevState => {
      let selection = null;
      if (prevState.selection.getMode() === 'hunk') {
        selection = prevState.selection.selectHunk(hunk, true);
      } else {
        selection = prevState.selection.selectLine(line, true);
      }
      return {selection};
    });
  }

  @autobind
  mouseup() {
    this.mouseSelectionInProgress = false;
    this.setState(prevState => {
      return {selection: prevState.selection.coalesce()};
    });
  }

  @autobind
  togglePatchSelectionMode() {
    this.setState(prevState => ({selection: prevState.selection.toggleMode()}));
  }

  getPatchSelectionMode() {
    return this.state.selection.getMode();
  }

  getSelectedHunks() {
    return this.state.selection.getSelectedHunks();
  }

  getSelectedLines() {
    return this.state.selection.getSelectedLines();
  }

  @autobind
  selectNext() {
    this.setState(prevState => ({selection: prevState.selection.selectNext()}));
  }

  @autobind
  selectNextHunk() {
    this.setState(prevState => ({selection: prevState.selection.jumpToNextHunk()}));
  }

  @autobind
  selectToNext() {
    this.setState(prevState => {
      return {selection: prevState.selection.selectNext(true).coalesce()};
    });
  }

  @autobind
  selectPrevious() {
    this.setState(prevState => ({selection: prevState.selection.selectPrevious()}));
  }

  @autobind
  selectPreviousHunk() {
    this.setState(prevState => ({selection: prevState.selection.jumpToPreviousHunk()}));
  }

  @autobind
  selectToPrevious() {
    this.setState(prevState => {
      return {selection: prevState.selection.selectPrevious(true).coalesce()};
    });
  }

  @autobind
  selectFirst() {
    this.setState(prevState => ({selection: prevState.selection.selectFirst()}));
  }

  @autobind
  selectToFirst() {
    this.setState(prevState => ({selection: prevState.selection.selectFirst(true)}));
  }

  @autobind
  selectLast() {
    this.setState(prevState => ({selection: prevState.selection.selectLast()}));
  }

  @autobind
  selectToLast() {
    this.setState(prevState => ({selection: prevState.selection.selectLast(true)}));
  }

  @autobind
  selectAll() {
    this.setState(prevState => ({selection: prevState.selection.selectAll()}));
  }

  getNextHunkUpdatePromise() {
    return this.state.selection.getNextUpdatePromise();
  }

  didClickStageButtonForHunk(hunk) {
    if (this.state.selection.getSelectedHunks().has(hunk)) {
      this.props.attemptLineStageOperation(this.state.selection.getSelectedLines());
    } else {
      this.setState(prevState => ({selection: prevState.selection.selectHunk(hunk)}), () => {
        this.props.attemptHunkStageOperation(hunk);
      });
    }
  }

  @autobind
  didConfirm() {
    return this.didClickStageButtonForHunk([...this.state.selection.getSelectedHunks()][0]);
  }

  @autobind
  didMoveRight() {
    if (this.props.didSurfaceFile) {
      this.props.didSurfaceFile();
    }
  }

  focus() {
    this.element.focus();
  }

  @autobind
  openFile() {
    let lineNumber = 0;
    const firstSelectedLine = Array.from(this.state.selection.getSelectedLines())[0];
    if (firstSelectedLine && firstSelectedLine.newLineNumber > -1) {
      lineNumber = firstSelectedLine.newLineNumber;
    } else {
      const firstSelectedHunk = Array.from(this.state.selection.getSelectedHunks())[0];
      lineNumber = firstSelectedHunk ? firstSelectedHunk.getNewStartRow() : 0;
    }
    return this.props.openCurrentFile({lineNumber});
  }

  @autobind
  async stageOrUnstageAll() {
    await this.selectAll();
    this.didConfirm();
  }

  @autobind
  discardSelection() {
    const selectedLines = this.state.selection.getSelectedLines();
    return selectedLines.size ? this.props.discardLines(selectedLines) : null;
  }
}
<|endoftext|>


<commit_before>// Library for making equilateral polygons in HTML SVG format
// Author: Joshua Conn

/**
 *  Updates points attribute on HTML Polygon to render an equilateral triangle
 *  @param polygonElmnt and HTML (dom) polygon element to have its points attribute updated
 *  @param radius half of the width/height of the triangle to render
 *  @param center (point) object with x and y properties
 *  @param rotation in radians
 */
function svgRenderTriangle(polygonElmnt, radius, center={x:0,y:0}, rotate=0) {
    svgRenderNGon(polygonElmnt, radius, center, 3, rotate);
}

/**
 *  Updates points attribute on HTML Polygon to render a square
 *  @param polygonElmnt and HTML (dom) polygon element to have its points attribute updated
 *  @param radius half of the width/height of the square to render
 *  @param center (point) object with x and y properties
 *  @param rotation in radians
 */
function svgRenderSquare(polygonElmnt, radius, center={x:0,y:0}, rotate=0) {
    svgRenderNGon(polygonElmnt, radius, center, 4, rotate);
}

/**
 *  Updates points attribute on HTML Polygon to render a pentagon
 *  @param polygonElmnt and HTML (dom) polygon element to have its points attribute updated
 *  @param radius half of the width/height of the pentagon to render
 *  @param center (point) object with x and y properties
 *  @param rotation in radians
 */
function svgRenderPentagon(polygonElmnt, radius, center={x:0,y:0}, rotate=0) {
    svgRenderNGon(polygonElmnt, radius, center, 5, rotate);
}

/**
 *  Updates points attribute on HTML Polygon to render an equilateral hexagon
 *  @param polygonElmnt and HTML (dom) polygon element to have its points attribute updated
 *  @param radius half of the width/height of the hexagon to render
 *  @param center (point) object with x and y properties
 *  @param rotation in radians
 */
function svgRenderHexagon(polygonElmnt, radius, center={x:0,y:0}, rotate=0) {
    svgRenderNGon(polygonElmnt, radius, center, 6, rotate);
}

/**
 *  Updates points attribute on HTML Polygon to render an equilateral polygon
 *  @param polygonElmnt and HTML (dom) polygon element to have its points attribute upated
 *  @param radius half of the width/height of the polygon to render
 *  @param center an (point) object with x and y properties
 *  @param n 3=triangle (default) 4=square, 5=pentagon, ....
 */
function svgRenderNGon(polygonElmnt, radius, center={x:0,y:0}, n=3, rotate=0) {
	var point = {x:0,y:0}, pointStr="";
  var angleDelta = Math.PI*2/n;
  for(p=1; p<=n; p++) {
    point.x=Math.sin(angleDelta*p+rotate)*radius+center.x;
    point.y=Math.cos(angleDelta*p+rotate)*radius+center.y;
    pointStr+=point.x+","+point.y+" ";
  }
  polygonElmnt.setAttribute("points", pointStr);
}
<commit_msg>update default orientation of svgRenderSquare() - make sides parallel to screen edges<commit_after>// Library for making equilateral polygons in HTML SVG format
// Author: Joshua Conn

/**
 *  Updates points attribute on HTML Polygon to render an equilateral triangle
 *  @param polygonElmnt and HTML (dom) polygon element to have its points attribute updated
 *  @param radius half of the width/height of the triangle to render
 *  @param center (point) object with x and y properties
 *  @param rotation in radians
 */
function svgRenderTriangle(polygonElmnt, radius, center={x:0,y:0}, rotate=0) {
    svgRenderNGon(polygonElmnt, radius, center, 3, rotate);
}

/**
 *  Updates points attribute on HTML Polygon to render a square
 *  @param polygonElmnt and HTML (dom) polygon element to have its points attribute updated
 *  @param radius half of the width/height of the square to render
 *  @param center (point) object with x and y properties
 *  @param rotation in radians
 */
function svgRenderSquare(polygonElmnt, radius, center={x:0,y:0}, rotate=Math.PI/4) {
    svgRenderNGon(polygonElmnt, radius, center, 4, rotate);
}

/**
 *  Updates points attribute on HTML Polygon to render a pentagon
 *  @param polygonElmnt and HTML (dom) polygon element to have its points attribute updated
 *  @param radius half of the width/height of the pentagon to render
 *  @param center (point) object with x and y properties
 *  @param rotation in radians
 */
function svgRenderPentagon(polygonElmnt, radius, center={x:0,y:0}, rotate=0) {
    svgRenderNGon(polygonElmnt, radius, center, 5, rotate);
}

/**
 *  Updates points attribute on HTML Polygon to render an equilateral hexagon
 *  @param polygonElmnt and HTML (dom) polygon element to have its points attribute updated
 *  @param radius half of the width/height of the hexagon to render
 *  @param center (point) object with x and y properties
 *  @param rotation in radians
 */
function svgRenderHexagon(polygonElmnt, radius, center={x:0,y:0}, rotate=0) {
    svgRenderNGon(polygonElmnt, radius, center, 6, rotate);
}

/**
 *  Updates points attribute on HTML Polygon to render an equilateral polygon
 *  @param polygonElmnt and HTML (dom) polygon element to have its points attribute upated
 *  @param radius half of the width/height of the polygon to render
 *  @param center an (point) object with x and y properties
 *  @param n 3=triangle (default) 4=square, 5=pentagon, ....
 */
function svgRenderNGon(polygonElmnt, radius, center={x:0,y:0}, n=3, rotate=0) {
	var point = {x:0,y:0}, pointStr="";
  var angleDelta = Math.PI*2/n;
  for(p=1; p<=n; p++) {
    point.x=Math.sin(angleDelta*p+rotate)*radius+center.x;
    point.y=Math.cos(angleDelta*p+rotate)*radius+center.y;
    pointStr+=point.x+","+point.y+" ";
  }
  polygonElmnt.setAttribute("points", pointStr);
}
<|endoftext|>


<commit_before>
// # [node.js h5bp server-config](https://github.com/h5bp/server-configs)
//  by @xonecas and @niftylettuce

// Requires latest version of `node` and `npm`
// <https://gist.github.com/579814>

// # Getting started:
// * Create directories `$ mkdir -p ~/project/node_modules && cd ~/project`
// * Install dependencies `$ npm install connect colors`

// # Usage:
// `$ PRODUCTION=true node node.js` (prod mode) or `$ node node.js` (dev mode)

var connect  = require('connect')
  , colors = require('colors')
  , cacheAge = 24 * 60 * 60 * 1000
  , prod = process.env.PRODUCTION
  // **NOTE:** You'll need to change these paths:
  , root = prod ? 'path/to/prod/public': 'path/to/dev/public'
  , port = prod ? 80 : 8080;

// # Routes
var routes = function(app) {

  // **TODO:** Insert your routes here (e.g. app.get('/home') app.post('/form'))

  // ## Always keep this route last
  app.get('*', function(req, res, next) {

    var url = req.url
      , ua = req.headers['user-agent'];

    // ## Block access to hidden files and directories that begin with a period
    if (url.match(/(^|\/)\./)) {
      res.end("Not allowed");
    }

    // ## Better website experience for IE users
    //  Force the latest IE version, in cases when it may fall back to IE7 mode
    if(ua && ua.indexOf('MSIE') && /htm?l/.test(ua)) {
      res.setHeader('X-UA-Compatible', 'IE=Edge,chrome=1');
    }

    // ## CORS
    //  <http://github.com/rails/rails/commit/123eb25#commitcomment-118920>
    //  Use ChromeFrame if it's installed, for a better experience with IE folks
    //  Control cross domain using CORS http://enable-cors.org
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader("Access-Control-Allow-Headers", "X-Requested-With");

    // ## Production Environment Compression (requires latest version of node)
    //
    // If you want to enable [gzippo](<https://github.com/tomgallacher/gzippo>):
    //
    //   `var gzippo = require('gzippo');'`
    //
    // Then install gzippo via `$ npm install gzippo` and add to `var routes{}`:
    //
    //   `app.use(gzippo.staticGzip(root, { maxAge: cacheAge}));`
    //
    // Next remove `, connect["static"](root, { maxAge: cacheAge })` from below.

    // **TODO:** Allow concatenation from within specific js and css files
    // https://github.com/paulirish/html5-boilerplate/blob/master/.htaccess/#L111

    // **TODO:** Stop screen flicker in IE on CSS rollovers
    // https://github.com/paulirish/html5-boilerplate/blob/master/.htaccess/#L271

    // **TODO:** Cookie setting from iframes
    // https://github.com/paulirish/html5-boilerplate/blob/master/.htaccess/#L286

    // **TODO:** Suppress or force the "www." at the beginning of URLs
    // https://github.com/paulirish/html5-boilerplate/blob/master/.htaccess/#L315

    // **TODO:** Built-in filename-based cache busting
    // https://github.com/paulirish/html5-boilerplate/blob/master/.htaccess/#L356

    // **TODO:** Prevent SSL cert warnings
    // https://github.com/paulirish/html5-boilerplate/blob/master/.htaccess/#L376

    next();
  });

};

// ## Start the server
var server = connect.createServer(
    // Logger from [Expressling](https://github.com/niftylettuce/expressling)
    connect.logger(''
      + '\\n  ' + ':date'.bold.underline + '\\n\\n' + '  IP: '.cyan.bold
      + ' ' + ':remote-addr'.white + '\\n' + '  Method: '.red.bold
      + ':method'.white + '\\n' + '  URL: '.blue.bold + ':url'.white
      + '\\n' + '  Status: '.yellow.bold + ':status'.white + '\\n'
      + '  User Agent: '.magenta.bold + ':user-agent'.white)
  , connect.router(routes)
  , connect["static"](root, { maxAge: cacheAge })
);
server.listen(port);
console.log('\n  ' + 'NODE UP ON PORT '.rainbow + port);
<commit_msg>updated node.js config. Fixed bug on sending the correct headers to IE, updated the code to remove the www, since shorter url's are sexiergit status<commit_after>// # [node.js h5bp server-config](https://github.com/h5bp/server-configs)
//  by @xonecas and @niftylettuce

// Requires latest version of `node` and `npm`
// <https://gist.github.com/579814>

// # Getting started:
// * Create directories `$ mkdir -p ~/project/node_modules && cd ~/project`

// # Usage:
// `$ PRODUCTION=true node node.js` (prod mode) or `$ node node.js` (dev mode)

var connect  = require('connect'),
   cacheAge = 24 * 60 * 60 * 1000,
   prod = process.env.PRODUCTION,
   // specify a production directory, or serve current directory.
   root = prod ? 'path/to/prod/public': __dirname,
   port = prod ? 80 : 8080;

// # Routes
var routes = function(app) {

   // # Insert your routes here (e.g. app.get('/home') app.post('/form'))

   // ## Always keep this route last
   app.get('*', function(req, res, next) {
      var url = req.url,
         ua = req.headers['user-agent'];

      // ## Block access to hidden files and directories that begin with a period
      if (url.match(/(^|\/)\./)) {
         res.end("Not allowed");
      }

      // ## Better website experience for IE users
      //  Force the latest IE version, in cases when it may fall back to IE7 mode
      if(ua && ua.indexOf('MSIE') && /htm?l$/.test(url)) {
         res.setHeader('X-UA-Compatible', 'IE=Edge,chrome=1');
      }

      //  Control cross domain using CORS http://enable-cors.org
      res.setHeader('Access-Control-Allow-Origin', '*');
      res.setHeader("Access-Control-Allow-Headers", "X-Requested-With");

      // **TODO:** Allow concatenation from within specific js and css files
      // https://github.com/paulirish/html5-boilerplate/blob/master/.htaccess/#L111

      // **TODO:** Stop screen flicker in IE on CSS rollovers
      // https://github.com/paulirish/html5-boilerplate/blob/master/.htaccess/#L271

      // **TODO:** Cookie setting from iframes
      // https://github.com/paulirish/html5-boilerplate/blob/master/.htaccess/#L286

      // **TODO:** Suppress or force the "www." at the beginning of URLs
      // https://github.com/paulirish/html5-boilerplate/blob/master/.htaccess/#L315
      if (/^www\./.test(url)) {
         res.statusCode = 302;
         res.setHeader('Location', url.replace(/^www\./, ''));
         res.end();
      }

      // **TODO:** Built-in filename-based cache busting
      // https://github.com/paulirish/html5-boilerplate/blob/master/.htaccess/#L356

      // **TODO:** Prevent SSL cert warnings
      // https://github.com/paulirish/html5-boilerplate/blob/master/.htaccess/#L376

      next();
   });

};

// ## Start the server
var server = connect.createServer(
   connect.logger('dev'),
   connect.router(routes),
   // if you want gzip, then replace the next line with gzippo
   // https://github.com/tomgallacher/gzippo
   connect["static"](root, { maxAge: cacheAge })
);
server.listen(port);
console.log('NODE UP ON PORT '+ port);
<|endoftext|>


<commit_before>#!/usr/bin/env node

console.log('Nodemon has run.')
<commit_msg>Remove noop.js<commit_after><|endoftext|>


<commit_before>/**
 * @class CM_FormField_Text
 * @extends CM_FormField_Abstract
 */
var CM_FormField_Text = CM_FormField_Abstract.extend({
  _class: 'CM_FormField_Text',

  /** @type Boolean */
  _skipTriggerChange: false,

  /** @type String */
  _valueLast: null,

  events: {
    'blur input, textarea': function() {
      this.trigger('blur');
    },
    'focus input, textarea': function() {
      this.trigger('focus');
    },
    'change input, textarea': function() {
      this.triggerChange();
    }
  },

  ready: function() {
    this._valueLast = this.getInput().val();
  },

  /**
   * @param {String} value
   */
  setValue: function(value) {
    this._skipTriggerChange = true;
    this.getInput().val(value);
    this._skipTriggerChange = false;
  },

  /**
   * @return {Boolean}
   */
  hasFocus: function() {
    return this.getInput().is(':focus');
  },

  triggerChange: function() {
    var valueCurrent = this.getInput().val();
    if (this._valueLast !== valueCurrent) {
      this._valueLast = valueCurrent;
      if (!this._skipTriggerChange) {
        this.trigger('change');
      }
    }
  },

  enableTriggerChangeOnInput: function() {
    // `propertychange` and `keyup` needed for IE9
    this.getInput().on('input propertychange keyup', _.bind(this.triggerChange, this));
  }
});
<commit_msg>Remove skipTriggerChange<commit_after>/**
 * @class CM_FormField_Text
 * @extends CM_FormField_Abstract
 */
var CM_FormField_Text = CM_FormField_Abstract.extend({
  _class: 'CM_FormField_Text',

  /** @type String */
  _valueLast: null,

  events: {
    'blur input, textarea': function() {
      this.trigger('blur');
    },
    'focus input, textarea': function() {
      this.trigger('focus');
    },
    'change input, textarea': function() {
      this.triggerChange();
    }
  },

  ready: function() {
    this._valueLast = this.getInput().val();
  },

  /**
   * @param {String} value
   */
  setValue: function(value) {
    this.getInput().val(value);
  },

  /**
   * @return {Boolean}
   */
  hasFocus: function() {
    return this.getInput().is(':focus');
  },

  triggerChange: function() {
    var valueCurrent = this.getInput().val();
    if (this._valueLast !== valueCurrent) {
      this._valueLast = valueCurrent;
      this.trigger('change');
    }
  },

  enableTriggerChangeOnInput: function() {
    // `propertychange` and `keyup` needed for IE9
    this.getInput().on('input propertychange keyup', _.bind(this.triggerChange, this));
  }
});
<|endoftext|>


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

var crypto = require('crypto');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var WebSocketConnection = require('./WebSocketConnection');

var headerValueSplitRegExp = /,\s*/;
var headerParamSplitRegExp = /;\s*/;
var headerSanitizeRegExp = /[\r\n]/g;
var protocolSeparators = [
	"(", ")", "<", ">", "@",
	",", ";", ":", "\\", "\"",
	"/", "[", "]", "?", "=",
	"{", "}", " ", String.fromCharCode(9)
];

var httpStatusDescriptions = {
    100: "Continue",
    101: "Switching Protocols",
    200: "OK",
    201: "Created",
    203: "Non-Authoritative Information",
    204: "No Content",
    205: "Reset Content",
    206: "Partial Content",
    300: "Multiple Choices",
    301: "Moved Permanently",
    302: "Found",
    303: "See Other",
    304: "Not Modified",
    305: "Use Proxy",
    307: "Temporary Redirect",
    400: "Bad Request",
    401: "Unauthorized",
    402: "Payment Required",
    403: "Forbidden",
    404: "Not Found",
    406: "Not Acceptable",
    407: "Proxy Authorization Required",
    408: "Request Timeout",
    409: "Conflict",
    410: "Gone",
    411: "Length Required",
    412: "Precondition Failed",
    413: "Request Entity Too Long",
    414: "Request-URI Too Long",
    415: "Unsupported Media Type",
    416: "Requested Range Not Satisfiable",
    417: "Expectation Failed",
    426: "Upgrade Required",
    500: "Internal Server Error",
    501: "Not Implemented",
    502: "Bad Gateway",
    503: "Service Unavailable",
    504: "Gateway Timeout",
    505: "HTTP Version Not Supported"
};

function WebSocketRequest(socket, httpRequest, serverConfig) {
    this.socket = socket;
    this.httpRequest = httpRequest;
    this.resource = httpRequest.url;
    this.remoteAddress = socket.remoteAddress;
    this.serverConfig = serverConfig;
};

util.inherits(WebSocketRequest, EventEmitter);

WebSocketRequest.prototype.readHandshake = function() {
    var request = this.httpRequest;
    this.host = request.headers['host'];
    if (!this.host) {
        throw new Error("Client must provide a Host header.");
    }

    this.key = request.headers['sec-websocket-key'];
    if (!this.key) {
        throw new Error("Client must provide a value for Sec-WebSocket-Key.");
    }

    this.websocketVersion = parseInt(request.headers['sec-websocket-version'], 10);

    if (!this.websocketVersion || isNaN(this.websocketVersion)) {
        throw new Error("Client must provide a value for Sec-WebSocket-Version.");
    }

    switch (this.websocketVersion) {
        case 8:
        case 13:
            break;
        default:
            var e = new Error("Unsupported websocket client version: " + this.websocketVersion +
                              "Only versions 8 and 13 are supported.");
            e.httpCode = 426;
            e.headers = {
                "Sec-WebSocket-Version": "13"
            };
            throw e;
    }

    if (this.websocketVersion === 13) {
        this.origin = request.headers['origin'];
    }
    else if (this.websocketVersion === 8) {
        this.origin = request.headers['sec-websocket-origin'];
    }

    // Protocol is optional.
    var protocolString = request.headers['sec-websocket-protocol'];
    if (protocolString) {
        this.requestedProtocols = protocolString.toLocaleLowerCase().split(headerValueSplitRegExp);
    }
    else {
        this.requestedProtocols = [];
    }

    if (request.headers['x-forwarded-for']) {
        this.remoteAddress = request.headers['x-forwarded-for'].split(', ')[0];
    }

    // Extensions are optional.
    var extensionsString = request.headers['sec-websocket-extensions'];
    this.requestedExtensions = this.parseExtensions(extensionsString);
};

WebSocketRequest.prototype.parseExtensions = function(extensionsString) {
    if (!extensionsString || extensionsString.length === 0) {
        return [];
    }
    extensions = extensionsString.toLocaleLowerCase().split(headerValueSplitRegExp);
    extensions.forEach(function(extension, index, array) {
        var params = extension.split(headerParamSplitRegExp);
        var extensionName = params[0];
        var extensionParams = params.slice(1);
        extensionParams.forEach(function(rawParam, index, array) {
            var arr = rawParam.split('=');
            var obj = {
                name: arr[0],
                value: arr[1]
            };
            array.splice(index, 1, obj);
        });
        var obj = {
            name: extensionName,
            params: extensionParams
        };
        array.splice(index, 1, obj);
    });
    return extensions;
},
WebSocketRequest.prototype.accept = function(acceptedProtocol, allowedOrigin) {
    // TODO: Handle extensions
    var connection = new WebSocketConnection(this.socket, [], acceptedProtocol, false, this.serverConfig);

    connection.websocketVersion = this.websocketVersion;
    connection.remoteAddress = this.remoteAddress;

    // Create key validation hash
    var sha1 = crypto.createHash('sha1');
    sha1.update(this.key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
    var acceptKey = sha1.digest('base64');

    var response = "HTTP/1.1 101 Switching Protocols\r\n" +
                   "Upgrade: websocket\r\n" +
                   "Connection: Upgrade\r\n" +
                   "Sec-WebSocket-Accept: " + acceptKey + "\r\n";
    if (acceptedProtocol) {
        // validate protocol
		for (var i=0; i < acceptedProtocol.length; i++) {
			var charCode = acceptedProtocol.charCodeAt(i);
			var character = acceptedProtocol.charAt(i);
			if (charCode < 0x21 || charCode > 0x7E || protocolSeparators.indexOf(character) !== -1) {
			    this.reject(500);
				throw new Error("Illegal character '" + String.fromCharCode(character) + "' in subprotocol.");
			}
		}
		if (this.requestedProtocols.indexOf(acceptedProtocol) === -1) {
		    this.reject(500);
		    throw new Error("Specified protocol was not requested by the client.");
		}

        acceptedProtocol = acceptedProtocol.replace(headerSanitizeRegExp, '');
        response += "Sec-WebSocket-Protocol: " + acceptedProtocol + "\r\n";
    }
    if (allowedOrigin) {
        allowedOrigin = allowedOrigin.replace(headerSanitizeRegExp, '');
        if (this.websocketVersion === 13) {
            response += "Origin: " + allowedOrigin + "\r\n";
        }
        else if (this.websocketVersion === 8) {
            response += "Sec-WebSocket-Origin: " + allowedOrigin + "\r\n";
        }
    }
    // TODO: handle negotiated extensions
    // if (negotiatedExtensions) {
    //     response += "Sec-WebSocket-Extensions: " + negotiatedExtensions.join(", ") + "\r\n";
    // }
    response += "\r\n";

    this.socket.write(response, 'ascii');

    this.emit('requestAccepted', connection);

    return connection;
};

WebSocketRequest.prototype.reject = function(status, reason, extraHeaders) {
    if (typeof(status) !== 'number') {
        status = 403;
    }
    var response = "HTTP/1.1 " + status + " " + httpStatusDescriptions[status] + "\r\n" +
                   "Connection: close\r\n";
    if (reason) {
        reason = reason.replace(headerSanitizeRegExp, '');
        response += "X-WebSocket-Reject-Reason: " + reason + "\r\n";
    }

    if (extraHeaders) {
        for (var key in extraHeaders) {
            var sanitizedValue = extraHeaders[key].toString().replace(headerSanitizeRegExp, '');
            var sanitizedKey = key.replace(headerSanitizeRegExp, '');
            response += (key + ": " + sanitizedValue + "\r\n");
        }
    }

    response += "\r\n";
    this.socket.end(response, 'ascii');

    this.emit('requestRejected', this);
};

module.exports = WebSocketRequest;<commit_msg>Added optional Set Cookie: header funcitonality to WebSocketRequest.accept method<commit_after>/************************************************************************
 *  Copyright 2010-2011 Worlize Inc.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 ***********************************************************************/

var crypto = require('crypto');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var WebSocketConnection = require('./WebSocketConnection');

var headerValueSplitRegExp = /,\s*/;
var headerParamSplitRegExp = /;\s*/;
var headerSanitizeRegExp = /[\r\n]/g;
var protocolSeparators = [
	"(", ")", "<", ">", "@",
	",", ";", ":", "\\", "\"",
	"/", "[", "]", "?", "=",
	"{", "}", " ", String.fromCharCode(9)
];

var httpStatusDescriptions = {
    100: "Continue",
    101: "Switching Protocols",
    200: "OK",
    201: "Created",
    203: "Non-Authoritative Information",
    204: "No Content",
    205: "Reset Content",
    206: "Partial Content",
    300: "Multiple Choices",
    301: "Moved Permanently",
    302: "Found",
    303: "See Other",
    304: "Not Modified",
    305: "Use Proxy",
    307: "Temporary Redirect",
    400: "Bad Request",
    401: "Unauthorized",
    402: "Payment Required",
    403: "Forbidden",
    404: "Not Found",
    406: "Not Acceptable",
    407: "Proxy Authorization Required",
    408: "Request Timeout",
    409: "Conflict",
    410: "Gone",
    411: "Length Required",
    412: "Precondition Failed",
    413: "Request Entity Too Long",
    414: "Request-URI Too Long",
    415: "Unsupported Media Type",
    416: "Requested Range Not Satisfiable",
    417: "Expectation Failed",
    426: "Upgrade Required",
    500: "Internal Server Error",
    501: "Not Implemented",
    502: "Bad Gateway",
    503: "Service Unavailable",
    504: "Gateway Timeout",
    505: "HTTP Version Not Supported"
};

function WebSocketRequest(socket, httpRequest, serverConfig) {
    this.socket = socket;
    this.httpRequest = httpRequest;
    this.resource = httpRequest.url;
    this.remoteAddress = socket.remoteAddress;
    this.serverConfig = serverConfig;
};

util.inherits(WebSocketRequest, EventEmitter);

WebSocketRequest.prototype.readHandshake = function() {
    var request = this.httpRequest;
    this.host = request.headers['host'];
    if (!this.host) {
        throw new Error("Client must provide a Host header.");
    }

    this.key = request.headers['sec-websocket-key'];
    if (!this.key) {
        throw new Error("Client must provide a value for Sec-WebSocket-Key.");
    }

    this.websocketVersion = parseInt(request.headers['sec-websocket-version'], 10);

    if (!this.websocketVersion || isNaN(this.websocketVersion)) {
        throw new Error("Client must provide a value for Sec-WebSocket-Version.");
    }

    switch (this.websocketVersion) {
        case 8:
        case 13:
            break;
        default:
            var e = new Error("Unsupported websocket client version: " + this.websocketVersion +
                              "Only versions 8 and 13 are supported.");
            e.httpCode = 426;
            e.headers = {
                "Sec-WebSocket-Version": "13"
            };
            throw e;
    }

    if (this.websocketVersion === 13) {
        this.origin = request.headers['origin'];
    }
    else if (this.websocketVersion === 8) {
        this.origin = request.headers['sec-websocket-origin'];
    }

    // Protocol is optional.
    var protocolString = request.headers['sec-websocket-protocol'];
    if (protocolString) {
        this.requestedProtocols = protocolString.toLocaleLowerCase().split(headerValueSplitRegExp);
    }
    else {
        this.requestedProtocols = [];
    }

    if (request.headers['x-forwarded-for']) {
        this.remoteAddress = request.headers['x-forwarded-for'].split(', ')[0];
    }

    // Extensions are optional.
    var extensionsString = request.headers['sec-websocket-extensions'];
    this.requestedExtensions = this.parseExtensions(extensionsString);
};

WebSocketRequest.prototype.parseExtensions = function(extensionsString) {
    if (!extensionsString || extensionsString.length === 0) {
        return [];
    }
    extensions = extensionsString.toLocaleLowerCase().split(headerValueSplitRegExp);
    extensions.forEach(function(extension, index, array) {
        var params = extension.split(headerParamSplitRegExp);
        var extensionName = params[0];
        var extensionParams = params.slice(1);
        extensionParams.forEach(function(rawParam, index, array) {
            var arr = rawParam.split('=');
            var obj = {
                name: arr[0],
                value: arr[1]
            };
            array.splice(index, 1, obj);
        });
        var obj = {
            name: extensionName,
            params: extensionParams
        };
        array.splice(index, 1, obj);
    });
    return extensions;
},
WebSocketRequest.prototype.accept = function(acceptedProtocol, allowedOrigin, cookies) {
    // TODO: Handle extensions
    var connection = new WebSocketConnection(this.socket, [], acceptedProtocol, false, this.serverConfig);

    connection.websocketVersion = this.websocketVersion;
    connection.remoteAddress = this.remoteAddress;

    // Create key validation hash
    var sha1 = crypto.createHash('sha1');
    sha1.update(this.key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
    var acceptKey = sha1.digest('base64');

    var response = "HTTP/1.1 101 Switching Protocols\r\n" +
                   "Upgrade: websocket\r\n" +
                   "Connection: Upgrade\r\n" +
                   "Sec-WebSocket-Accept: " + acceptKey + "\r\n";

    if (acceptedProtocol) {
        // validate protocol
		for (var i=0; i < acceptedProtocol.length; i++) {
			var charCode = acceptedProtocol.charCodeAt(i);
			var character = acceptedProtocol.charAt(i);
			if (charCode < 0x21 || charCode > 0x7E || protocolSeparators.indexOf(character) !== -1) {
			    this.reject(500);
				throw new Error("Illegal character '" + String.fromCharCode(character) + "' in subprotocol.");
			}
		}
		if (this.requestedProtocols.indexOf(acceptedProtocol) === -1) {
		    this.reject(500);
		    throw new Error("Specified protocol was not requested by the client.");
		}

        acceptedProtocol = acceptedProtocol.replace(headerSanitizeRegExp, '');
        response += "Sec-WebSocket-Protocol: " + acceptedProtocol + "\r\n";
    }
    if (allowedOrigin) {
        allowedOrigin = allowedOrigin.replace(headerSanitizeRegExp, '');
        if (this.websocketVersion === 13) {
            response += "Origin: " + allowedOrigin + "\r\n";
        }
        else if (this.websocketVersion === 8) {
            response += "Sec-WebSocket-Origin: " + allowedOrigin + "\r\n";
        }
    }

    if (cookies) {
    	for(var c in cookies){
    		response += "Set-Cookie: " + cookies[c] + "\r\n";
    	}
    }
    // TODO: handle negotiated extensions
    // if (negotiatedExtensions) {
    //     response += "Sec-WebSocket-Extensions: " + negotiatedExtensions.join(", ") + "\r\n";
    // }
    response += "\r\n";

    this.socket.write(response, 'ascii');

    this.emit('requestAccepted', connection);

    return connection;
};

WebSocketRequest.prototype.reject = function(status, reason, extraHeaders) {
    if (typeof(status) !== 'number') {
        status = 403;
    }
    var response = "HTTP/1.1 " + status + " " + httpStatusDescriptions[status] + "\r\n" +
                   "Connection: close\r\n";
    if (reason) {
        reason = reason.replace(headerSanitizeRegExp, '');
        response += "X-WebSocket-Reject-Reason: " + reason + "\r\n";
    }

    if (extraHeaders) {
        for (var key in extraHeaders) {
            var sanitizedValue = extraHeaders[key].toString().replace(headerSanitizeRegExp, '');
            var sanitizedKey = key.replace(headerSanitizeRegExp, '');
            response += (key + ": " + sanitizedValue + "\r\n");
        }
    }

    response += "\r\n";
    this.socket.end(response, 'ascii');

    this.emit('requestRejected', this);
};

module.exports = WebSocketRequest;<|endoftext|>


<commit_before>///* global window */
//
//;(function (win) {
//
//}(window));
<commit_msg>Connecting dots WIP...<commit_after>/* global window */

;(function (win) {
    'use strict';

    win.Okra = {
        inlet: function () {

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