<commit_before>import React, {Component} from 'react';
import ReactSlider, {createSliderWithTooltip} from 'rc-slider';
import {assoc, omit, pickBy} from 'ramda';
import computeSliderStyle from '../utils/computeSliderStyle';

import 'rc-slider/assets/index.css';

import {propTypes, defaultProps} from '../components/Slider.react';

/**
 * A slider component with a single handle.
 */
export default class Slider extends Component {
    constructor(props) {
        super(props);
        this.propsToState = this.propsToState.bind(this);
        this.DashSlider = props.tooltip
            ? createSliderWithTooltip(ReactSlider)
            : ReactSlider;
        this._computeStyle = computeSliderStyle();
        this.state = {
            value: props.value,
        };
    }

    propsToState(newProps) {
        if (newProps.value !== this.props.value) {
            this.setState({value: newProps.value});
        }
    }

    UNSAFE_componentWillReceiveProps(newProps) {
        if (newProps.tooltip !== this.props.tooltip) {
            this.DashSlider = newProps.tooltip
                ? createSliderWithTooltip(ReactSlider)
                : ReactSlider;
        }
        this.propsToState(newProps);
    }

    UNSAFE_componentWillMount() {
        this.propsToState(this.props);
    }

    render() {
        const {
            className,
            id,
            loading_state,
            setProps,
            tooltip,
            updatemode,
            vertical,
            verticalHeight,
        } = this.props;
        const value = this.state.value;

        let tipProps;
        if (tooltip && tooltip.always_visible) {
            /**
             * clone `tooltip` but with renamed key `always_visible` -> `visible`
             * the rc-tooltip API uses `visible`, but `always_visible` is more semantic
             * assigns the new (renamed) key to the old key and deletes the old key
             */
            tipProps = assoc('visible', tooltip.always_visible, tooltip);
            delete tipProps.always_visible;
        } else {
            tipProps = tooltip;
        }

        const truncatedMarks = this.props.marks
            ? pickBy(
                  (k, mark) => mark >= this.props.min && mark <= this.props.max,
                  this.props.marks
              )
            : this.props.marks;

        return (
            <div
                id={id}
                data-dash-is-loading={
                    (loading_state && loading_state.is_loading) || undefined
                }
                className={className}
                style={this._computeStyle(vertical, verticalHeight, tooltip)}
            >
                <this.DashSlider
                    onChange={value => {
                        if (updatemode === 'drag') {
                            setProps({value});
                        } else {
                            this.setState({value});
                        }
                    }}
                    onAfterChange={value => {
                        if (updatemode === 'mouseup') {
                            setProps({value});
                        }
                    }}
                    tipProps={{
                        ...tipProps,
                        getTooltipContainer: node => node
                    }}
                    style={{position:'relative'}}
                    value={value}
                    marks={truncatedMarks}
                    {...omit(
                        [
                            'className',
                            'setProps',
                            'updatemode',
                            'value',
                            'marks',
                            'verticalHeight',
                        ],
                        this.props
                    )}
                />
            </div>
        );
    }
}

Slider.propTypes = propTypes;
Slider.defaultProps = defaultProps;
<commit_msg>format<commit_after>import React, {Component} from 'react';
import ReactSlider, {createSliderWithTooltip} from 'rc-slider';
import {assoc, omit, pickBy} from 'ramda';
import computeSliderStyle from '../utils/computeSliderStyle';

import 'rc-slider/assets/index.css';

import {propTypes, defaultProps} from '../components/Slider.react';

/**
 * A slider component with a single handle.
 */
export default class Slider extends Component {
    constructor(props) {
        super(props);
        this.propsToState = this.propsToState.bind(this);
        this.DashSlider = props.tooltip
            ? createSliderWithTooltip(ReactSlider)
            : ReactSlider;
        this._computeStyle = computeSliderStyle();
        this.state = {
            value: props.value,
        };
    }

    propsToState(newProps) {
        if (newProps.value !== this.props.value) {
            this.setState({value: newProps.value});
        }
    }

    UNSAFE_componentWillReceiveProps(newProps) {
        if (newProps.tooltip !== this.props.tooltip) {
            this.DashSlider = newProps.tooltip
                ? createSliderWithTooltip(ReactSlider)
                : ReactSlider;
        }
        this.propsToState(newProps);
    }

    UNSAFE_componentWillMount() {
        this.propsToState(this.props);
    }

    render() {
        const {
            className,
            id,
            loading_state,
            setProps,
            tooltip,
            updatemode,
            vertical,
            verticalHeight,
        } = this.props;
        const value = this.state.value;

        let tipProps;
        if (tooltip && tooltip.always_visible) {
            /**
             * clone `tooltip` but with renamed key `always_visible` -> `visible`
             * the rc-tooltip API uses `visible`, but `always_visible` is more semantic
             * assigns the new (renamed) key to the old key and deletes the old key
             */
            tipProps = assoc('visible', tooltip.always_visible, tooltip);
            delete tipProps.always_visible;
        } else {
            tipProps = tooltip;
        }

        const truncatedMarks = this.props.marks
            ? pickBy(
                  (k, mark) => mark >= this.props.min && mark <= this.props.max,
                  this.props.marks
              )
            : this.props.marks;

        return (
            <div
                id={id}
                data-dash-is-loading={
                    (loading_state && loading_state.is_loading) || undefined
                }
                className={className}
                style={this._computeStyle(vertical, verticalHeight, tooltip)}
            >
                <this.DashSlider
                    onChange={value => {
                        if (updatemode === 'drag') {
                            setProps({value});
                        } else {
                            this.setState({value});
                        }
                    }}
                    onAfterChange={value => {
                        if (updatemode === 'mouseup') {
                            setProps({value});
                        }
                    }}
                    tipProps={{
                        ...tipProps,
                        getTooltipContainer: node => node,
                    }}
                    style={{position: 'relative'}}
                    value={value}
                    marks={truncatedMarks}
                    {...omit(
                        [
                            'className',
                            'setProps',
                            'updatemode',
                            'value',
                            'marks',
                            'verticalHeight',
                        ],
                        this.props
                    )}
                />
            </div>
        );
    }
}

Slider.propTypes = propTypes;
Slider.defaultProps = defaultProps;
<|endoftext|>


<commit_before>/* @flow */
'use strict';

import React from 'react';
import {View, Platform} from 'react-native';
import NativeBaseComponent from '../Base/NativeBaseComponent';
import computeProps from '../../Utils/computeProps';

export default class Header extends NativeBaseComponent {

    getInitialStyle() {
        return {
            navbar: {
                backgroundColor: this.getTheme().toolbarDefaultBg,
                justifyContent: (!Array.isArray(this.props.children)) ? 'center' : 'space-between',
                flexDirection: 'row',
                alignItems: 'center',
                padding: 15,
                paddingTop: (Platform.OS === 'ios' ) ? 25 : 15,
                shadowColor: '#000',
                shadowOffset: {width: 0, height: 2},
                shadowOpacity: 0.1,
                shadowRadius: 1.5,
                height: this.getTheme().toolbarHeight
            },
            title : {
                color: '#fff',
                fontSize: 20,
                fontWeight: "500",
                alignSelf: 'center'
            }
        }
    }

    prepareRootProps() {

        var defaultProps = {
            style: this.getInitialStyle().navbar
        };

        return computeProps(this.props, defaultProps);

    }

    render() {

        return(
            <View {...this.prepareRootProps()}>
                { !Array.isArray(this.props.children) &&
                <View >
                    {this.props.children}
                </View>}

                { Array.isArray(this.props.children) &&
                <View style={{flex: 1,alignItems: 'center', justifyContent: 'flex-start', flexDirection: 'row', marginLeft: -14}}>
                    {this.props.children[0]}
                </View>}

                { Array.isArray(this.props.children) &&
                <View style={{flex: 3, alignSelf: 'center'}}>
                    {this.props.children[1]}
                </View>}

                { Array.isArray(this.props.children) &&
                <View style={{flex:1,alignItems: 'center', justifyContent: 'flex-end', flexDirection: 'row', marginRight: -14}}>
                    {this.props.children[2]}
                </View>}
            </View>
        );
    }
}
<commit_msg>header search bar<commit_after>/* @flow */
'use strict';

import React from 'react';
import {View, Platform} from 'react-native';
import NativeBaseComponent from '../Base/NativeBaseComponent';
import computeProps from '../../Utils/computeProps';

export default class Header extends NativeBaseComponent {

    getInitialStyle() {
        return {
            navbar: {
                backgroundColor: this.getTheme().toolbarDefaultBg,
                justifyContent: (!Array.isArray(this.props.children)) ? 'center' : 'space-between',
                flexDirection: 'row',
                alignItems: 'center',
                padding: 15,
                paddingTop: (Platform.OS === 'ios' ) ? 25 : 15,
                shadowColor: '#000',
                shadowOffset: {width: 0, height: 2},
                shadowOpacity: 0.1,
                shadowRadius: 1.5,
                height: this.getTheme().toolbarHeight
            }
        }
    }

    prepareRootProps() {

        var defaultProps = {
            style: this.getInitialStyle().navbar
        };

        return computeProps(this.props, defaultProps);

    }
    renderChildren() {
        if(!Array.isArray(this.props.children)) {
            return this.props.children;
        }

        else if (Array.isArray(this.props.children)) {
            var newChildren = [];
            var childrenArray = React.Children.toArray(this.props.children);

            if (this.props.searchBar) {
                newChildren.push(<View style={{flex: 4,alignItems: 'center', justifyContent: 'flex-start', flexDirection: 'row'}}>
                                    {childrenArray[0]}
                                </View>)
                newChildren.push(<View style={{flex:1,alignItems: 'center', justifyContent: 'flex-end', flexDirection: 'row', marginRight: -14}}>
                                    {childrenArray[1]}
                                </View>)
            }
            else {
                newChildren.push(<View style={{flex: 1,alignItems: 'center', justifyContent: 'flex-start', flexDirection: 'row', marginLeft: -14}}>
                                    {this.props.children[0]}
                                </View>)
                newChildren.push(<View style={{flex: 3, alignSelf: 'center'}}>
                                    {this.props.children[1]}
                                </View>)
                newChildren.push(<View style={{flex:1,alignItems: 'center', justifyContent: 'flex-end', flexDirection: 'row', marginRight: -14}}>
                                    {this.props.children[2]}
                                </View>)
            }
          return newChildren;
        }
    }

    render() {
        return(
            <View {...this.prepareRootProps()} >
                {this.renderChildren()}
            </View>
        );
    }
}
<|endoftext|>


<commit_before>import { parseBoundingBox, parseStyle } from './geoServerXmlStyleReducer'

const RESTRICTED = false
const WORKSPACE = __WORKSPACE__
const ENDPOINT = __API__

/**
 * Parses XML response from GeoServer, creating a layers array
 * @param response response from GeoServer API (as XML)
 */
const geoServerXmlReducer = (response) => {
    let layers = []

    // adds iterators to XML nodes, so we can run forEach on them
    NodeList.prototype.forEach = Array.prototype.forEach
    HTMLCollection.prototype.forEach = Array.prototype.forEach

    // get root layers node, and parse layer data for each layer
    const parser = new DOMParser()
    const xmlDoc = parser.parseFromString(response.data, 'text/xml')
    xmlDoc.firstElementChild.childNodes.forEach((rootChildrenNode) => {
        if (rootChildrenNode.nodeName === 'Capability') {
            rootChildrenNode.childNodes.forEach( (capabilityChildrenNode) => {
                if (capabilityChildrenNode.nodeName === 'Layer') {
                    capabilityChildrenNode.childNodes.forEach((rootLayerChildrenNode) => {
                        if (rootLayerChildrenNode.nodeName === 'Layer') {
                            layers = parseLayerNode(rootLayerChildrenNode, layers)
                        }
                    })
                }
            })
        }
    })

    return layers
}

/**
* Parses XML node for a single layer, populating GeoAPI.layers array.
* @param xmlNode XML node with a GeoServer layer
* @param layers layers array we're building
*/
const parseLayerNode = (xmlNode, layers) => {
    if (isValidLayer(xmlNode)) {
        const charts = []
        const caops = []
        let menu = ''
        let menu2 = ''
        let name
        let title
        let abstract
        let table

        // gets name, title, abstract, and keywords for caops and menu
        xmlNode.childNodes.map(layerChildrenNode => {
            const nodeName = []
            nodeName['Nodes'] = () => null
            nodeName['Name'] = () => { name = layerChildrenNode.textContent }
            nodeName['Title'] = () => { title = layerChildrenNode.textContent }
            nodeName['Abstract'] = () => { abstract = layerChildrenNode.textContent }
            nodeName['KeywordList'] = () => {
                layerChildrenNode.childNodes.map(keywordNode => {
                    if (keywordNode.nodeName === 'Keyword') {
                        const keywordsArray = keywordNode.textContent.split(':')

                        if (keywordsArray[0] === 'cao') {
                            caops.push(keywordsArray[1])
                        }
                        if (keywordsArray[0] === 'tabela') {
                            table = JSON.parse(keywordsArray[1])
                        }
                        if (keywordsArray[0] === 'menu') {
                            menu = keywordsArray[1]
                        }
                        if (keywordsArray[0] === 'menu2') {
                            const menu2Array = [
                                keywordsArray[1]
                            ]
                            if (keywordsArray.length > 2) {
                                // copy all submenus
                                for (let i = 2, l = keywordsArray.length; i < l; i++) {
                                    menu2Array.push(keywordsArray[i])
                                }
                            }
                            menu2 = menu2Array
                        }
                        if (keywordsArray[0] === 'grafico') {
                            const chartData = keywordsArray[1].split('|')
                            const chartColumns = chartData[3].split(',')
                            for (let c = 0, lc = chartColumns.length; c < lc; c++) {
                                chartColumns[c] = chartColumns[c].split('/')
                            }
                            const chartObject = {
                                type: chartData[0],
                                title: chartData[1],
                                entity: chartData[2],
                                columns: chartColumns
                            }
                            charts.push(chartObject)
                        }
                    }
                })
            }

            nodeName[layerChildrenNode.nodeName]()
        })

        // create layer object
        var layer = {
            name,
            title,
            caops,
            menu,
            menu2,
            table,
            charts,
            id:         `${WORKSPACE}_${name}`,
            workspace:   WORKSPACE,
            display:     true,
            restricted:  RESTRICTED,
            layerName:  `${WORKSPACE}:${name}`,
            description: abstract,
            bbox:        parseBoundingBox(xmlNode),
            key:         layers.length,
        }

        // get layer styles
        layer.styles = parseStyle(xmlNode, layer)

        // add to layers array if they have a valid menu key (i.e. is published)
        if (menu) {
            layers.push(layer)
        }
    }

    return layers
}

/**
* Check if the given XML node represents a valid layer.
* The layers we use must have a KeywordList node, with at least one element inside.
* @param xmlNode XML node with a GeoServer layer
* @returns {Boolean}
*/
const isValidLayer = (xmlNode) => {
    let ret = false

    xmlNode.childNodes.forEach((layerChildrenNode) => {
        if (layerChildrenNode.nodeName === 'KeywordList' && layerChildrenNode.children.length > 0) {
            if (ret === false) {
                ret = true
            }
        }
    })

    return ret
}

export default geoServerXmlReducer
<commit_msg>[fix] Correção de chamada do método map para forEach do objeto Array do javascript.<commit_after>import { parseBoundingBox, parseStyle } from './geoServerXmlStyleReducer'

const RESTRICTED = false
const WORKSPACE = __WORKSPACE__
const ENDPOINT = __API__

/**
 * Parses XML response from GeoServer, creating a layers array
 * @param response response from GeoServer API (as XML)
 */
const geoServerXmlReducer = (response) => {
    let layers = []

    // adds iterators to XML nodes, so we can run forEach on them
    NodeList.prototype.forEach = Array.prototype.forEach
    HTMLCollection.prototype.forEach = Array.prototype.forEach

    // get root layers node, and parse layer data for each layer
    const parser = new DOMParser()
    const xmlDoc = parser.parseFromString(response.data, 'text/xml')
    xmlDoc.firstElementChild.childNodes.forEach((rootChildrenNode) => {
        if (rootChildrenNode.nodeName === 'Capability') {
            rootChildrenNode.childNodes.forEach( (capabilityChildrenNode) => {
                if (capabilityChildrenNode.nodeName === 'Layer') {
                    capabilityChildrenNode.childNodes.forEach((rootLayerChildrenNode) => {
                        if (rootLayerChildrenNode.nodeName === 'Layer') {
                            layers = parseLayerNode(rootLayerChildrenNode, layers)
                        }
                    })
                }
            })
        }
    })

    return layers
}

/**
* Parses XML node for a single layer, populating GeoAPI.layers array.
* @param xmlNode XML node with a GeoServer layer
* @param layers layers array we're building
*/
const parseLayerNode = (xmlNode, layers) => {
    if (isValidLayer(xmlNode)) {
        const charts = []
        const caops = []
        let menu = ''
        let menu2 = ''
        let name
        let title
        let abstract
        let table

        // gets name, title, abstract, and keywords for caops and menu
        xmlNode.childNodes.forEach(layerChildrenNode => {
            const nodeName = []
            nodeName.Name = () => { name = layerChildrenNode.textContent }
            nodeName.Title = () => { title = layerChildrenNode.textContent }
            nodeName.Abstract = () => { abstract = layerChildrenNode.textContent }
            nodeName.KeywordList = () => {
                layerChildrenNode.childNodes.forEach(keywordNode => {
                    if (keywordNode.nodeName === 'Keyword') {
                        const keywordsArray = keywordNode.textContent.split(':')

                        if (keywordsArray[0] === 'cao') {
                            caops.push(keywordsArray[1])
                        }
                        if (keywordsArray[0] === 'tabela') {
                            table = JSON.parse(keywordsArray[1])
                        }
                        if (keywordsArray[0] === 'menu') {
                            menu = keywordsArray[1]
                        }
                        if (keywordsArray[0] === 'menu2') {
                            const menu2Array = [
                                keywordsArray[1]
                            ]
                            if (keywordsArray.length > 2) {
                                // copy all submenus
                                for (let i = 2, l = keywordsArray.length; i < l; i++) {
                                    menu2Array.push(keywordsArray[i])
                                }
                            }
                            menu2 = menu2Array
                        }
                        if (keywordsArray[0] === 'grafico') {
                            const chartData = keywordsArray[1].split('|')
                            const chartColumns = chartData[3].split(',')
                            for (let c = 0, lc = chartColumns.length; c < lc; c++) {
                                chartColumns[c] = chartColumns[c].split('/')
                            }
                            const chartObject = {
                                type: chartData[0],
                                title: chartData[1],
                                entity: chartData[2],
                                columns: chartColumns
                            }
                            charts.push(chartObject)
                        }
                    }
                })
            }

            if (nodeName[layerChildrenNode.nodeName]) {
                nodeName[layerChildrenNode.nodeName]()
            }
        })

        // create layer object
        var layer = {
            name,
            title,
            caops,
            menu,
            menu2,
            table,
            charts,
            id:         `${WORKSPACE}_${name}`,
            workspace:   WORKSPACE,
            display:     true,
            restricted:  RESTRICTED,
            layerName:  `${WORKSPACE}:${name}`,
            description: abstract,
            bbox:        parseBoundingBox(xmlNode),
            key:         layers.length,
        }

        // get layer styles
        layer.styles = parseStyle(xmlNode, layer)

        // add to layers array if they have a valid menu key (i.e. is published)
        if (menu) {
            layers.push(layer)
        }
    }

    return layers
}

/**
* Check if the given XML node represents a valid layer.
* The layers we use must have a KeywordList node, with at least one element inside.
* @param xmlNode XML node with a GeoServer layer
* @returns {Boolean}
*/
const isValidLayer = (xmlNode) => {
    let ret = false

    xmlNode.childNodes.forEach((layerChildrenNode) => {
        if (layerChildrenNode.nodeName === 'KeywordList' && layerChildrenNode.children.length > 0) {
            if (ret === false) {
                ret = true
            }
        }
    })

    return ret
}

export default geoServerXmlReducer
<|endoftext|>


<commit_before>/*
 * Copyright 2014 Thoughtworks 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.
 */
define([
  'flight/lib/component',
  'component/mixins/with_auth_token_from_hash',
  'component/mixins/repositories_urls',
  'component/templates/popover_template'
  ],
  function (defineComponent, withAuthTokeFromHash, repositoriesURLs, withPopoverTemplate) {
    return defineComponent(githubIssues, withAuthTokeFromHash, repositoriesURLs, withPopoverTemplate);

    function githubIssues() {

      this.defaultAttrs({
          issues : []
      });

      this.createIssue = function (ev, data) {
        var url, repositoryURL;
        repositoryURL = this.getURLFromProject(data.projectName);
        url = this.repoIssuesURL(repositoryURL);

        $.ajax({
          type: 'POST',
          url: url,
          data: JSON.stringify({
            'title': data.issueTitle,
            'body': data.issueBody,
            'labels': ["0 - Backlog"]
          }),
          success: function (response, status, xhr) {
            response.projectName = data.projectName;
            this.trigger("ui:add:issue", {
              "issue": response
            })
          }.bind(this)
        });

      };

      this.addIssue = function (ev, data) {
        this.trigger('data:issues:refreshed', {
          issues: data
        });
      }

      this.filterProjectsByName = function (projects, projectNames) {
        if(! projectNames) {
           return projects;
         }

        var filteredRepos = [];

        _.each(projects, function (project) {
          if ($.inArray(project.projectName, projectNames) > -1) {
            filteredRepos.push(project);
          }
        });

        return filteredRepos;
      };

      this.getIssuesFromProjects = function (projects) {
        var allIssues = [];

        _.each(projects, function(project, index) {
          var issuesArrayJson = project.repo[0] || [];
          _.each(issuesArrayJson, function(issue,index) {
            issue.projectName = project.projectName;
            issue.repoUrl = this.getRepoURLFromIssue(issue.url);
            allIssues.push(issue);
          }.bind(this));
        }.bind(this));
        return allIssues;
      };

      this.getRepoURLFromIssue = function(issueUrl){
        var delimeter = '/';
        if(issueUrl){
          var result = issueUrl.split(delimeter).reverse().slice(2).reverse().join(delimeter) + delimeter;
          return result;
        }
      };

      this.fetchIssues = function (ev, data) {
        $(document).trigger('ui:blockUI');

        data.page = ('page' in data) ? (data.page + 1) : 1;

        var issuesPromises = this.fetchAllIssues(data.page);
        var queries = _(issuesPromises).map(function(v,k) {return v;});
        var names = _(issuesPromises).map(function(v,k) {return k;});
        $.when.apply(this, queries).done(
          function () {
            var issuesResults = arguments;
            var projects = _(names).map(function(name, idx) {
              return {
                'projectName': name,
                'repo': issuesResults[idx]
              }
            });

            var filteredProjects = this.filterProjectsByName(projects, data.projectName),
            issuesFromProjects = this.getIssuesFromProjects(filteredProjects);

            this.trigger('data:issues:refreshed', {
              issues: issuesFromProjects
            });

            this.attr.issues = this.attr.issues.concat(issuesFromProjects);

            if (data.page == 1) {
              this.trigger('data:issues:clearExportCsvLink');
            }

            if (issuesFromProjects.length > 0) {

              this.trigger('ui:needs:issues', data);
            } else {
              this.trigger('ui:needs:priority');
            }
          }.bind(this)
        );
      };

      this.assignMyselfToIssue = function (ev, assignData) {

        var user, issue, url;
        if (assignData != undefined) {
          user = assignData.user;
          issue = assignData.issue;
        }

        if (!issue) {
          return;
        }

        if (!user) {
          this.trigger(document, 'ui:needs:githubUser', assignData);
          return;
        }

        if (!$('#' + issue.id + ' .empty-avatar').is(':visible')) {
          var userLogin = $('#'+ issue.id + ' .assignee-avatar').attr('title');
          if (userLogin == user.login) {
            this.trigger(document, 'ui:unassign:user', assignData);
          }
          else {
            var issue_header = $('#' + issue.id + ' .issue-header');
            if (issue_header.children('.popover').length) {
              issue_header.children('.popover').remove();
            }
            else {
              var that = this,

              popover_confirm = $('

'),
              popover_confirm_yes = $('<button type="button" class="btn btn-default">Unassign</button>'),
              popover_confirm_no = $('<button type="button" class="btn btn-default">Cancel</button>'),
              pop = $(this.popover({
                title: '', body: ''
              })).appendTo(issue_header);

              popover_confirm_yes.bind('click', function(){
                that.trigger(document, 'ui:unassign:user', assignData);
                pop.remove();
              }).appendTo(popover_confirm);
              popover_confirm_no.bind('click', function(){
                pop.remove();
              }).appendTo(popover_confirm);

              pop.children('.popover-header').children('h2').html("Assignee: " + userLogin + "");
              popover_confirm.appendTo(pop.children('.popover-body'));
            }
          }
          return;
        }

        url = issue.url + "?access_token=" + this.getCurrentAuthToken();

        $('#' + issue.id + ' .empty-avatar').toggleClass('loading');
        $('#' + issue.id + ' .empty-avatar-label').hide();

        $.ajax({
          type: 'PATCH',
          url: url,
          data: JSON.stringify({
            assignee: user.login
          }),
          success: function (response, status, xhr) {
            $('#' + issue.id + ' .assigns-myself').toggleClass('assigned');
            $('#' + issue.id + ' .assignee-avatar').attr('src', user.avatar_url);
            $('#' + issue.id + ' .assignee-avatar').attr('title', user.login);
            $('#' + issue.id + ' .assignee-avatar').show();
            $('#' + issue.id + ' .empty-avatar').toggleClass('loading').hide();
            $('#' + issue.id + ' .empty-label').hide();
          },
          error: function() {
            $('#' + issue.id + ' .empty-avatar').toggleClass('loading').hide();
            $('#' + issue.id + ' .empty-avatar-label').show();
          }
        });
      };

      this.unassignMyselfToIssue = function(ev, assignData) {
        var user, issue, url, currentData;
        if (assignData != undefined) {
          user = assignData.user;
          issue = assignData.issue;
        }

        if (!issue) {
          return;
        }

        if (!user) {
          this.trigger(document, 'ui:needs:githubUser', assignData);
          return;
        }

        url = issue.url + "?access_token=" + this.getCurrentAuthToken();

        currentData = {
          src: $('#' + issue.id + ' .assignee-avatar').attr('src'),
          title: $('#' + issue.id + ' .assignee-avatar').attr('title')
        };

        $('#' + issue.id + ' .assignee-avatar').attr('src', '/img/ajax-loader.gif');
        $('#' + issue.id + ' .assignee-avatar').attr('title', 'loading...');

        $.ajax({
          type: 'PATCH',
          url: url,
          data: JSON.stringify({
            assignee: ''
          }),
          success: function (response, status, xhr) {
            $('#' + issue.id + ' .assigns-myself').toggleClass('assigned');
            $('#' + issue.id + ' .assignee-avatar').attr('src', '').hide();
            $('#' + issue.id + ' .assignee-avatar').attr('title', '').hide();
            $('#' + issue.id + ' .empty-avatar').show();
            $('#' + issue.id + ' .empty-avatar-label').show();
            $('#' + issue.id + ' .empty-label').show();
          },
          error: function() {
            $('#' + issue.id + ' .assignee-avatar').attr('src', currentData.src);
            $('#' + issue.id + ' .assignee-avatar').attr('title', currentData.title);
          }
        });
      };

      this.updateDraggable = function(event, ui){

        var label = this.parseLabel(event.target.id);

        var issueMovedParam = {
          label : label ,
          element : this.DOMObjectToIssueMovedParam(ui.item[0]),
          previousElement : this.DOMObjectToIssueMovedParam(ui.item[0].previousElementSibling),
          nextElement : this.DOMObjectToIssueMovedParam(ui.item[0].nextElementSibling)
        };

        this.trigger(document, 'data:issues:issueMoved' , issueMovedParam);
      };

      this.draggable = function (ev, data) {
        $('.backlog, .ready, .development, .quality-assurance, .done').sortable({
          items: '.issue',
          connectWith: '.list-group',
          cancel: '.popover',
          update : this.updateDraggable.bind(this),
          receive: function (event, ui) {
            var label, url , oldLabel, state;

            if (!this.getCurrentAuthToken()) {
              this.trigger(document, 'ui:needs:githubUser');
              return;
            }

            url = this.getIssueUrlFromDraggable(ui);
            label = this.parseLabel(event.target.id);
            oldLabel = this.parseLabel(ui.sender[0].id);
            state = this.getState(event.target.className);

            $('.panel-heading.backlog-header .issues-count').text(' (' + $('.issue-track.backlog .issue').length + ')');
            $('.panel-heading.ready-header .issues-count').text(' (' + $('.issue-track.ready .issue').length + ')');
            $('.panel-heading.development-header .issues-count').text(' (' + $('.issue-track.development .issue').length + ')');
            $('.panel-heading.quality-assurance-header .issues-count').text(' (' + $('.issue-track.quality-assurance .issue').length + ')');

            if (label == "4 - Done") {
              this.triggerRocketAnimation();
              $.ajax({
                type: 'PATCH',
                url: url + this.getAccessTokenParam(),
                data: JSON.stringify({
                  state: "closed"
                })
              });
            }

            $.ajax({
              type: 'POST',
              url: url + "/labels" + this.getAccessTokenParam(),
              data: JSON.stringify([label])
            });

            $.ajax({
              type: 'DELETE',
              url: url + "/labels/" + oldLabel + this.getAccessTokenParam()
            });
          }.bind(this)
        }).disableSelection();
      };

      this.triggerRocketAnimation = function () {
        $(".panel-heading.done img.plain").hide();
        $(".panel-heading.done h3").css('opacity', 0);
        $(".panel-heading.done .issues-count").css('opacity', 0);
        $(".panel-heading.done img.colored").show().animate({
          top: '-650px'
        }, 2000, 'easeInBack', function () {
          $(".panel-heading.done img.colored").hide().css('top', 0);

          $(".panel-heading.done h3").text('Liftoff! We Have a Liftoff!');
          $(".panel-heading.done h3").css('color', '#5dc66c');
          $(".panel-heading.done h3").animate({
            opacity: 1
          }, 2000);

          $(".panel-heading.done .check-done").fadeIn(2000, function () {
            $(".panel-heading.done .check-done").hide();

            $(".panel-heading.done h3").css('opacity', 0);
            $(".panel-heading.done h3").text('Drop here to launch');
            $(".panel-heading.done h3").css('color', '#aaa');

            $(".panel-heading.done img.plain").fadeIn(600);
            $(".panel-heading.done h3").animate({
              opacity: 1
            }, 600);
            $(".panel-heading.done .issues-count").animate({
              opacity: 1
            }, 600);
          });
        });
      };

      this.DOMObjectToIssueMovedParam = function(element) {
        if(element && element.id)
          return { id : element.id, priority : element.dataset.priority  };
        return { id : 0, priority : 0};
      };

      this.parseLabel = function (label) {
        var fullLabel = '';
        label = label.split('-');

        for (var i = 1; i < label.length; i++) {
          var firstLetter = label[i][0];
          fullLabel = fullLabel + firstLetter.toUpperCase() + label[i].substring(1) + ' ';
        }

        fullLabel = label[0] + ' - ' + fullLabel;
        return fullLabel.trim();
      };

      this.getIssueUrlFromDraggable = function (ui) {
        return ui.item[0].childNodes[0].childNodes[1].href.replace('github.com/', 'api.github.com/repos/');
      };

      this.getAccessTokenParam = function () {
        return "?access_token=" + this.getCurrentAuthToken();
      };

      this.getState = function (className) {
        return className.search('done') != -1 ? 'closed' : 'open';
      };

      this.changeNewIssueLink = function (event, projectName) {
        $(".link").attr("href", this.newIssueURL(projectName));
      };

      this.blockUI = function () {
        $.blockUI();
      };

      this.unblockUI = function (e, timeout) {
        setTimeout($.unblockUI, (timeout || 0));
      };

      this.mountExportClick =  function(ev,data){
        this.trigger('data:issues:mountExportCsvLink', {
          issues: this.attr.issues
        });
      };

      this.clearIssues = function(){
        this.attr.issues = [];
      };

      this.after('initialize', function () {
        this.on('ui:needs:issues', this.fetchIssues);
        this.on('ui:add:issue', this.addIssue);
        this.on('ui:create:issue', this.createIssue);
        this.on('ui:assigns:user', this.assignMyselfToIssue);
        this.on('data:githubUser:here', this.assignMyselfToIssue);
        this.on('ui:draggable', this.draggable);
        this.on('ui:issue:createIssuesURL', this.changeNewIssueLink);
        this.on('#export_csv', 'click' , this.mountExportClick);
        this.on('ui:unassign:user', this.unassignMyselfToIssue);
        this.on('ui:blockUI', this.blockUI);
        this.on('ui:unblockUI', this.unblockUI);
        this.on('ui:clear:issue', this.clearIssues)

      });
    }
  }
);
<commit_msg>fixing sidebar issues count when drop issue<commit_after>/*
 * Copyright 2014 Thoughtworks 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.
 */
define([
  'flight/lib/component',
  'component/mixins/with_auth_token_from_hash',
  'component/mixins/repositories_urls',
  'component/templates/popover_template'
  ],
  function (defineComponent, withAuthTokeFromHash, repositoriesURLs, withPopoverTemplate) {
    return defineComponent(githubIssues, withAuthTokeFromHash, repositoriesURLs, withPopoverTemplate);

    function githubIssues() {

      this.defaultAttrs({
          issues : []
      });

      this.createIssue = function (ev, data) {
        var url, repositoryURL;
        repositoryURL = this.getURLFromProject(data.projectName);
        url = this.repoIssuesURL(repositoryURL);

        $.ajax({
          type: 'POST',
          url: url,
          data: JSON.stringify({
            'title': data.issueTitle,
            'body': data.issueBody,
            'labels': ["0 - Backlog"]
          }),
          success: function (response, status, xhr) {
            response.projectName = data.projectName;
            this.trigger("ui:add:issue", {
              "issue": response
            })
          }.bind(this)
        });

      };

      this.addIssue = function (ev, data) {
        this.trigger('data:issues:refreshed', {
          issues: data
        });
      }

      this.filterProjectsByName = function (projects, projectNames) {
        if(! projectNames) {
           return projects;
         }

        var filteredRepos = [];

        _.each(projects, function (project) {
          if ($.inArray(project.projectName, projectNames) > -1) {
            filteredRepos.push(project);
          }
        });

        return filteredRepos;
      };

      this.getIssuesFromProjects = function (projects) {
        var allIssues = [];

        _.each(projects, function(project, index) {
          var issuesArrayJson = project.repo[0] || [];
          _.each(issuesArrayJson, function(issue,index) {
            issue.projectName = project.projectName;
            issue.repoUrl = this.getRepoURLFromIssue(issue.url);
            allIssues.push(issue);
          }.bind(this));
        }.bind(this));
        return allIssues;
      };

      this.getRepoURLFromIssue = function(issueUrl){
        var delimeter = '/';
        if(issueUrl){
          var result = issueUrl.split(delimeter).reverse().slice(2).reverse().join(delimeter) + delimeter;
          return result;
        }
      };

      this.fetchIssues = function (ev, data) {
        $(document).trigger('ui:blockUI');

        data.page = ('page' in data) ? (data.page + 1) : 1;

        var issuesPromises = this.fetchAllIssues(data.page);
        var queries = _(issuesPromises).map(function(v,k) {return v;});
        var names = _(issuesPromises).map(function(v,k) {return k;});
        $.when.apply(this, queries).done(
          function () {
            var issuesResults = arguments;
            var projects = _(names).map(function(name, idx) {
              return {
                'projectName': name,
                'repo': issuesResults[idx]
              }
            });

            var filteredProjects = this.filterProjectsByName(projects, data.projectName),
            issuesFromProjects = this.getIssuesFromProjects(filteredProjects);

            this.trigger('data:issues:refreshed', {
              issues: issuesFromProjects
            });

            this.attr.issues = this.attr.issues.concat(issuesFromProjects);

            if (data.page == 1) {
              this.trigger('data:issues:clearExportCsvLink');
            }

            if (issuesFromProjects.length > 0) {

              this.trigger('ui:needs:issues', data);
            } else {
              this.trigger('ui:needs:priority');
            }
          }.bind(this)
        );
      };

      this.assignMyselfToIssue = function (ev, assignData) {

        var user, issue, url;
        if (assignData != undefined) {
          user = assignData.user;
          issue = assignData.issue;
        }

        if (!issue) {
          return;
        }

        if (!user) {
          this.trigger(document, 'ui:needs:githubUser', assignData);
          return;
        }

        if (!$('#' + issue.id + ' .empty-avatar').is(':visible')) {
          var userLogin = $('#'+ issue.id + ' .assignee-avatar').attr('title');
          if (userLogin == user.login) {
            this.trigger(document, 'ui:unassign:user', assignData);
          }
          else {
            var issue_header = $('#' + issue.id + ' .issue-header');
            if (issue_header.children('.popover').length) {
              issue_header.children('.popover').remove();
            }
            else {
              var that = this,

              popover_confirm = $('

'),
              popover_confirm_yes = $('<button type="button" class="btn btn-default">Unassign</button>'),
              popover_confirm_no = $('<button type="button" class="btn btn-default">Cancel</button>'),
              pop = $(this.popover({
                title: '', body: ''
              })).appendTo(issue_header);

              popover_confirm_yes.bind('click', function(){
                that.trigger(document, 'ui:unassign:user', assignData);
                pop.remove();
              }).appendTo(popover_confirm);
              popover_confirm_no.bind('click', function(){
                pop.remove();
              }).appendTo(popover_confirm);

              pop.children('.popover-header').children('h2').html("Assignee: " + userLogin + "");
              popover_confirm.appendTo(pop.children('.popover-body'));
            }
          }
          return;
        }

        url = issue.url + "?access_token=" + this.getCurrentAuthToken();

        $('#' + issue.id + ' .empty-avatar').toggleClass('loading');
        $('#' + issue.id + ' .empty-avatar-label').hide();

        $.ajax({
          type: 'PATCH',
          url: url,
          data: JSON.stringify({
            assignee: user.login
          }),
          success: function (response, status, xhr) {
            $('#' + issue.id + ' .assigns-myself').toggleClass('assigned');
            $('#' + issue.id + ' .assignee-avatar').attr('src', user.avatar_url);
            $('#' + issue.id + ' .assignee-avatar').attr('title', user.login);
            $('#' + issue.id + ' .assignee-avatar').show();
            $('#' + issue.id + ' .empty-avatar').toggleClass('loading').hide();
            $('#' + issue.id + ' .empty-label').hide();
          },
          error: function() {
            $('#' + issue.id + ' .empty-avatar').toggleClass('loading').hide();
            $('#' + issue.id + ' .empty-avatar-label').show();
          }
        });
      };

      this.unassignMyselfToIssue = function(ev, assignData) {
        var user, issue, url, currentData;
        if (assignData != undefined) {
          user = assignData.user;
          issue = assignData.issue;
        }

        if (!issue) {
          return;
        }

        if (!user) {
          this.trigger(document, 'ui:needs:githubUser', assignData);
          return;
        }

        url = issue.url + "?access_token=" + this.getCurrentAuthToken();

        currentData = {
          src: $('#' + issue.id + ' .assignee-avatar').attr('src'),
          title: $('#' + issue.id + ' .assignee-avatar').attr('title')
        };

        $('#' + issue.id + ' .assignee-avatar').attr('src', '/img/ajax-loader.gif');
        $('#' + issue.id + ' .assignee-avatar').attr('title', 'loading...');

        $.ajax({
          type: 'PATCH',
          url: url,
          data: JSON.stringify({
            assignee: ''
          }),
          success: function (response, status, xhr) {
            $('#' + issue.id + ' .assigns-myself').toggleClass('assigned');
            $('#' + issue.id + ' .assignee-avatar').attr('src', '').hide();
            $('#' + issue.id + ' .assignee-avatar').attr('title', '').hide();
            $('#' + issue.id + ' .empty-avatar').show();
            $('#' + issue.id + ' .empty-avatar-label').show();
            $('#' + issue.id + ' .empty-label').show();
          },
          error: function() {
            $('#' + issue.id + ' .assignee-avatar').attr('src', currentData.src);
            $('#' + issue.id + ' .assignee-avatar').attr('title', currentData.title);
          }
        });
      };

      this.updateDraggable = function(event, ui){

        var label = this.parseLabel(event.target.id);

        var issueMovedParam = {
          label : label ,
          element : this.DOMObjectToIssueMovedParam(ui.item[0]),
          previousElement : this.DOMObjectToIssueMovedParam(ui.item[0].previousElementSibling),
          nextElement : this.DOMObjectToIssueMovedParam(ui.item[0].nextElementSibling)
        };

        this.trigger(document, 'data:issues:issueMoved' , issueMovedParam);
      };

      this.draggable = function (ev, data) {
        $('.backlog, .ready, .development, .quality-assurance, .done').sortable({
          items: '.issue',
          connectWith: '.list-group',
          cancel: '.popover',
          update : this.updateDraggable.bind(this),
          receive: function (event, ui) {
            var label, url , oldLabel, state;

            if (!this.getCurrentAuthToken()) {
              this.trigger(document, 'ui:needs:githubUser');
              return;
            }

            url = this.getIssueUrlFromDraggable(ui);
            label = this.parseLabel(event.target.id);
            oldLabel = this.parseLabel(ui.sender[0].id);
            state = this.getState(event.target.className);

            $('.panel-heading.backlog-header .issues-count').text(' (' + $('.issue-track.backlog .issue').length + ')');
            $('.backlog-vertical-title .issues-count').text(' (' + $('.issue-track.backlog .issue').length + ')');
            $('.panel-heading.ready-header .issues-count').text(' (' + $('.issue-track.ready .issue').length + ')');
            $('.panel-heading.development-header .issues-count').text(' (' + $('.issue-track.development .issue').length + ')');
            $('.panel-heading.quality-assurance-header .issues-count').text(' (' + $('.issue-track.quality-assurance .issue').length + ')');

            if (label == "4 - Done") {
              this.triggerRocketAnimation();
              $.ajax({
                type: 'PATCH',
                url: url + this.getAccessTokenParam(),
                data: JSON.stringify({
                  state: "closed"
                })
              });
            }

            $.ajax({
              type: 'POST',
              url: url + "/labels" + this.getAccessTokenParam(),
              data: JSON.stringify([label])
            });

            $.ajax({
              type: 'DELETE',
              url: url + "/labels/" + oldLabel + this.getAccessTokenParam()
            });
          }.bind(this)
        }).disableSelection();
      };

      this.triggerRocketAnimation = function () {
        $(".panel-heading.done img.plain").hide();
        $(".panel-heading.done h3").css('opacity', 0);
        $(".panel-heading.done .issues-count").css('opacity', 0);
        $(".panel-heading.done img.colored").show().animate({
          top: '-650px'
        }, 2000, 'easeInBack', function () {
          $(".panel-heading.done img.colored").hide().css('top', 0);

          $(".panel-heading.done h3").text('Liftoff! We Have a Liftoff!');
          $(".panel-heading.done h3").css('color', '#5dc66c');
          $(".panel-heading.done h3").animate({
            opacity: 1
          }, 2000);

          $(".panel-heading.done .check-done").fadeIn(2000, function () {
            $(".panel-heading.done .check-done").hide();

            $(".panel-heading.done h3").css('opacity', 0);
            $(".panel-heading.done h3").text('Drop here to launch');
            $(".panel-heading.done h3").css('color', '#aaa');

            $(".panel-heading.done img.plain").fadeIn(600);
            $(".panel-heading.done h3").animate({
              opacity: 1
            }, 600);
            $(".panel-heading.done .issues-count").animate({
              opacity: 1
            }, 600);
          });
        });
      };

      this.DOMObjectToIssueMovedParam = function(element) {
        if(element && element.id)
          return { id : element.id, priority : element.dataset.priority  };
        return { id : 0, priority : 0};
      };

      this.parseLabel = function (label) {
        var fullLabel = '';
        label = label.split('-');

        for (var i = 1; i < label.length; i++) {
          var firstLetter = label[i][0];
          fullLabel = fullLabel + firstLetter.toUpperCase() + label[i].substring(1) + ' ';
        }

        fullLabel = label[0] + ' - ' + fullLabel;
        return fullLabel.trim();
      };

      this.getIssueUrlFromDraggable = function (ui) {
        return ui.item[0].childNodes[0].childNodes[1].href.replace('github.com/', 'api.github.com/repos/');
      };

      this.getAccessTokenParam = function () {
        return "?access_token=" + this.getCurrentAuthToken();
      };

      this.getState = function (className) {
        return className.search('done') != -1 ? 'closed' : 'open';
      };

      this.changeNewIssueLink = function (event, projectName) {
        $(".link").attr("href", this.newIssueURL(projectName));
      };

      this.blockUI = function () {
        $.blockUI();
      };

      this.unblockUI = function (e, timeout) {
        setTimeout($.unblockUI, (timeout || 0));
      };

      this.mountExportClick =  function(ev,data){
        this.trigger('data:issues:mountExportCsvLink', {
          issues: this.attr.issues
        });
      };

      this.clearIssues = function(){
        this.attr.issues = [];
      };

      this.after('initialize', function () {
        this.on('ui:needs:issues', this.fetchIssues);
        this.on('ui:add:issue', this.addIssue);
        this.on('ui:create:issue', this.createIssue);
        this.on('ui:assigns:user', this.assignMyselfToIssue);
        this.on('data:githubUser:here', this.assignMyselfToIssue);
        this.on('ui:draggable', this.draggable);
        this.on('ui:issue:createIssuesURL', this.changeNewIssueLink);
        this.on('#export_csv', 'click' , this.mountExportClick);
        this.on('ui:unassign:user', this.unassignMyselfToIssue);
        this.on('ui:blockUI', this.blockUI);
        this.on('ui:unblockUI', this.unblockUI);
        this.on('ui:clear:issue', this.clearIssues)

      });
    }
  }
);
<|endoftext|>


<commit_before>"use strict";

var Ajaxer = {
  debug: 1,

  // Ajax setup for all requests, this snippet is added to jQuery setup at the end of this file
  ajaxSetup: {
    timeout: 6000, // anything longer is too long
    cache: false, // this little sentence killed a lot of little bugs that was actually one big bug
  },

  // Format of params:
  // params = {
  //   url: 'http://api.lol.com',
  //   data: {sth: 'something', lol: 'laugh out LOUD'}, // Only use if you want a POST request
  //   success: function(data) {
  //     // Do something wth data
  //   },
  //   error: function(jqXHR, text, err) {
  //     // console.log "something, something, error"
  //   },
  // }

  getPlainText: function(params) {
    params.dataType = 'text';
    return this.get(params);
  },

  getJson: function(params) {
    params.dataType = 'json';
    return this.get(params);
  },

  getXml: function(params) {
    params.dataType = 'xml';
    return this.get(params);
  },

  getHtml: function(params) {
    params.dataType = 'html';
    var html = this.get(params);
    html = html.trim(); // jQuery 1.9+ does not consider pages starting with a newline as HTML, first char should be "<"
    return html;
  },

  getCleanHtml: function(params) {
    params.dataType = 'html';
    params.dataFilter = Ajaxer.cleanHtml;
    return this.get(params);
  },

  get: function(params) {
    if (params == undefined) {
      console.log('ERROR: Params is required. Check ajaxer.js to see format of params.');
      return;
    }
    if (params.url == undefined) {
      console.log('ERROR: URL missing from params.');
      return;
    }
    if (params.dataType == undefined) {
      console.log('ERROR: Do not use Ajaxer.get() directly, use getXml, getJson or one of the others instead.');
      return;
    }
    if (params.success == undefined) {
     console.log('ERROR: Params is missing success function. The success function should use the results for something useful.');
      return;
    }
    if (params.error == undefined) {
     console.log('ERROR: Params is missing error function. Error handling must be in place.');
      return;
    }

    var self = this;
    return $.ajax({
      type: (params.data ? 'POST' : 'GET'),
      data: (params.data ? params.data : ''),
      url: params.url,
      dataFilter: params.dataFilter,
      dataType: params.dataType,
      success: params.success,
      error: params.error,
    });
  },

  // IMPORTANT: This function replaces all <img> tags with <pic>
  cleanHtml: function(html, type) {
    var size = html.length;

    // Remove head, links, metas, scripts, iframes, frames, framesets
    html = html.replace(/<head\b[^<]*(?:(?!<\/head>)<[^<]*)*<\/head>/gi, '');
    html = html.replace(/<link\b[^<]*(?:(?!<\/link>)<[^<]*)*<\/link>/gi, '');
    html = html.replace(/<meta\b[^<]*(?:(?!<\/meta>)<[^<]*)*<\/meta>/gi, '');
    html = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
    html = html.replace(/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi, '');
    html = html.replace(/<frame\b[^<]*(?:(?!<\/frame>)<[^<]*)*<\/frame>/gi, '');
    html = html.replace(/<frameset\b[^<]*(?:(?!<\/frameset>)<[^<]*)*<\/frameset>/gi, '');

    // Remove audio, video, object, canvas, applet, embed
    html = html.replace(/<audio\b[^<]*(?:(?!<\/audio>)<[^<]*)*<\/audio>/gi, '');
    html = html.replace(/<video\b[^<]*(?:(?!<\/video>)<[^<]*)*<\/video>/gi, '');
    html = html.replace(/<object\b[^<]*(?:(?!<\/object>)<[^<]*)*<\/object>/gi, '');
    html = html.replace(/<canvas\b[^<]*(?:(?!<\/canvas>)<[^<]*)*<\/canvas>/gi, '');
    html = html.replace(/<applet\b[^<]*(?:(?!<\/applet>)<[^<]*)*<\/applet>/gi, '');
    html = html.replace(/<embed\b[^<]*(?:(?!<\/embed>)<[^<]*)*<\/embed>/gi, '');

    // Remove inline scripts
    html = html.replace(/on\w+\s*?=\s*?("[^"]*"|'[^']*')/gi, '');
    // If any inline scripts didn't use enclosing quotes, turn them into harmless titles
    html = html.replace(/on\w+\s*?=/gi, 'title='); // Note: a bit greedy, but won't cause anything but lulz

    // Rename <img> tags to <pic> tags to prevent jQuery from trying to fetch all images.
    // jQuerys behavior is not too problematic, but has some security concerns, also it
    // will most definitely slow down any slow computer running Notifier, like most
    // Infoscreen computers out there.
    // When parsing for images, we will just look for the <pic> tags.
    html = html.replace(/<[\s]*?img/gi, '<pic');
    html = html.replace(/<[\s]*?\/[\s]*?img/gi, '</pic');

    // jQuery 1.9+ does not consider pages starting with a newline as HTML, first char should be "<"
    html = html.trim();

    // If no more tags were stripped by cleaning one more time.
    if (cleanHtml(html) === html) {
        if (Ajaxer.debug) console.log('Ajaxer cleaned HTML, from', size, 'to', html.length);

        return html;

    // More tags were found, most likely nested html (malicious).
    } else {
        if (Ajaxer.debug) console.log('Ajaxer detected malicious html.');

        return '';
    }
  },
}

// Applying Ajax setup
$.ajaxSetup(Ajaxer.ajaxSetup);
<commit_msg>Set debug to default 0.<commit_after>"use strict";

var Ajaxer = {
  debug: 0,

  // Ajax setup for all requests, this snippet is added to jQuery setup at the end of this file
  ajaxSetup: {
    timeout: 6000, // anything longer is too long
    cache: false, // this little sentence killed a lot of little bugs that was actually one big bug
  },

  // Format of params:
  // params = {
  //   url: 'http://api.lol.com',
  //   data: {sth: 'something', lol: 'laugh out LOUD'}, // Only use if you want a POST request
  //   success: function(data) {
  //     // Do something wth data
  //   },
  //   error: function(jqXHR, text, err) {
  //     // console.log "something, something, error"
  //   },
  // }

  getPlainText: function(params) {
    params.dataType = 'text';
    return this.get(params);
  },

  getJson: function(params) {
    params.dataType = 'json';
    return this.get(params);
  },

  getXml: function(params) {
    params.dataType = 'xml';
    return this.get(params);
  },

  getHtml: function(params) {
    params.dataType = 'html';
    var html = this.get(params);
    html = html.trim(); // jQuery 1.9+ does not consider pages starting with a newline as HTML, first char should be "<"
    return html;
  },

  getCleanHtml: function(params) {
    params.dataType = 'html';
    params.dataFilter = Ajaxer.cleanHtml;
    return this.get(params);
  },

  get: function(params) {
    if (params == undefined) {
      console.log('ERROR: Params is required. Check ajaxer.js to see format of params.');
      return;
    }
    if (params.url == undefined) {
      console.log('ERROR: URL missing from params.');
      return;
    }
    if (params.dataType == undefined) {
      console.log('ERROR: Do not use Ajaxer.get() directly, use getXml, getJson or one of the others instead.');
      return;
    }
    if (params.success == undefined) {
     console.log('ERROR: Params is missing success function. The success function should use the results for something useful.');
      return;
    }
    if (params.error == undefined) {
     console.log('ERROR: Params is missing error function. Error handling must be in place.');
      return;
    }

    var self = this;
    return $.ajax({
      type: (params.data ? 'POST' : 'GET'),
      data: (params.data ? params.data : ''),
      url: params.url,
      dataFilter: params.dataFilter,
      dataType: params.dataType,
      success: params.success,
      error: params.error,
    });
  },

  // IMPORTANT: This function replaces all <img> tags with <pic>
  cleanHtml: function(html, type) {
    var size = html.length;

    // Remove head, links, metas, scripts, iframes, frames, framesets
    html = html.replace(/<head\b[^<]*(?:(?!<\/head>)<[^<]*)*<\/head>/gi, '');
    html = html.replace(/<link\b[^<]*(?:(?!<\/link>)<[^<]*)*<\/link>/gi, '');
    html = html.replace(/<meta\b[^<]*(?:(?!<\/meta>)<[^<]*)*<\/meta>/gi, '');
    html = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
    html = html.replace(/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi, '');
    html = html.replace(/<frame\b[^<]*(?:(?!<\/frame>)<[^<]*)*<\/frame>/gi, '');
    html = html.replace(/<frameset\b[^<]*(?:(?!<\/frameset>)<[^<]*)*<\/frameset>/gi, '');

    // Remove audio, video, object, canvas, applet, embed
    html = html.replace(/<audio\b[^<]*(?:(?!<\/audio>)<[^<]*)*<\/audio>/gi, '');
    html = html.replace(/<video\b[^<]*(?:(?!<\/video>)<[^<]*)*<\/video>/gi, '');
    html = html.replace(/<object\b[^<]*(?:(?!<\/object>)<[^<]*)*<\/object>/gi, '');
    html = html.replace(/<canvas\b[^<]*(?:(?!<\/canvas>)<[^<]*)*<\/canvas>/gi, '');
    html = html.replace(/<applet\b[^<]*(?:(?!<\/applet>)<[^<]*)*<\/applet>/gi, '');
    html = html.replace(/<embed\b[^<]*(?:(?!<\/embed>)<[^<]*)*<\/embed>/gi, '');

    // Remove inline scripts
    html = html.replace(/on\w+\s*?=\s*?("[^"]*"|'[^']*')/gi, '');
    // If any inline scripts didn't use enclosing quotes, turn them into harmless titles
    html = html.replace(/on\w+\s*?=/gi, 'title='); // Note: a bit greedy, but won't cause anything but lulz

    // Rename <img> tags to <pic> tags to prevent jQuery from trying to fetch all images.
    // jQuerys behavior is not too problematic, but has some security concerns, also it
    // will most definitely slow down any slow computer running Notifier, like most
    // Infoscreen computers out there.
    // When parsing for images, we will just look for the <pic> tags.
    html = html.replace(/<[\s]*?img/gi, '<pic');
    html = html.replace(/<[\s]*?\/[\s]*?img/gi, '</pic');

    // jQuery 1.9+ does not consider pages starting with a newline as HTML, first char should be "<"
    html = html.trim();

    // If no more tags were stripped by cleaning one more time.
    if (cleanHtml(html) === html) {
        if (Ajaxer.debug) console.log('Ajaxer cleaned HTML, from', size, 'to', html.length);

        return html;

    // More tags were found, most likely nested html (malicious).
    } else {
        if (Ajaxer.debug) console.log('Ajaxer detected malicious html.');

        return '';
    }
  },
}

// Applying Ajax setup
$.ajaxSetup(Ajaxer.ajaxSetup);
<|endoftext|>


<commit_before>/* @flow */

type RemoveAllSavedTabsAction = {
  type: 'REMOVE_ALL_SAVED_TABS',
};

type RemoveSavedTabIdAction = {
  tabId: number,
  type: 'REMOVE_SAVED_TAB_ID',
};

type SetSavedTabsAction = {
  savedTabs: Array<chrome$Tab>,
  type: 'SET_SAVED_TABS',
};

type SetTotalTabsRemovedAction = {
  totalTabsRemoved: number,
  type: 'SET_TOTAL_TABS_REMOVED_SUCCESS',
};

type SetTotalTabsUnwrangledAction = {
  totalTabsUnwrangled: number,
  type: 'SET_TOTAL_TABS_UNWRANGLED_SUCCESS',
};

type SetTotalTabsWrangledAction = {
  totalTabsWrangled: number,
  type: 'SET_TOTAL_TABS_WRANGLED_SUCCESS',
};

export type Action =
  | RemoveAllSavedTabsAction
  | RemoveSavedTabIdAction
  | SetSavedTabsAction
  | SetTotalTabsRemovedAction
  | SetTotalTabsUnwrangledAction
  | SetTotalTabsWrangledAction;

type State = {
  // Date of installation of Tab Wrangler
  installDate: number,

  // Tabs closed by Tab Wrangler
  savedTabs: Array<chrome$Tab>,

  // Number of tabs closed by any means since install
  totalTabsRemoved: number,

  // Number of tabs unwrangled (re-opened from the corral) since install
  totalTabsUnwrangled: number,

  // Number of tabs wrangled since install
  totalTabsWrangled: number,
};

export function createInitialState() {
  return {
    installDate: Date.now(),
    savedTabs: [],
    totalTabsRemoved: 0,
    totalTabsUnwrangled: 0,
    totalTabsWrangled: 0,
  };
}

const initialState = createInitialState();
export default function localStorage(state: State = initialState, action: Action) {
  switch (action.type) {
    case 'REMOVE_ALL_SAVED_TABS':
      return {
        ...state,
        savedTabs: [],
      };
    case 'REMOVE_SAVED_TAB_ID': {
      // Extract `tabId` to preserve type refinement inside `findIndex` callback.
      const actionTabId = action.tabId;
      const tabIndex = state.savedTabs.findIndex(tab => tab.id === actionTabId);
      if (tabIndex === -1) return state;

      // * Annotate `nextSavedTabs` to appease Flow. It's unclear why this annotation is required
      //   and can't be inferred.
      // * Copy the Array first (using `slice`) to achieve pseudo-immutability.
      const nextSavedTabs: Array<chrome$Tab> = state.savedTabs.slice();
      nextSavedTabs.splice(tabIndex, 1);
      return {
        ...state,
        savedTabs: nextSavedTabs,
      };
    }
    case 'SET_SAVED_TABS':
      return {
        ...state,
        savedTabs: action.savedTabs,
      };
    case 'SET_TOTAL_TABS_REMOVED_SUCCESS':
      return {
        ...state,
        totalTabsRemoved: action.totalTabsRemoved,
      };
    case 'SET_TOTAL_TABS_UNWRANGLED_SUCCESS':
      return {
        ...state,
        totalTabsUnwrangled: action.totalTabsUnwrangled,
      };
    case 'SET_TOTAL_TABS_WRANGLED_SUCCESS':
      return {
        ...state,
        totalTabsWrangled: action.totalTabsWrangled,
      };
    default:
      return state;
  }
}
<commit_msg>Correctly update # of tabs wrangled<commit_after>/* @flow */

type RemoveAllSavedTabsAction = {
  type: 'REMOVE_ALL_SAVED_TABS',
};

type RemoveSavedTabIdAction = {
  tabId: number,
  type: 'REMOVE_SAVED_TAB_ID',
};

type SetSavedTabsAction = {
  savedTabs: Array<chrome$Tab>,
  type: 'SET_SAVED_TABS',
};

type SetTotalTabsRemovedAction = {
  totalTabsRemoved: number,
  type: 'SET_TOTAL_TABS_REMOVED',
};

type SetTotalTabsUnwrangledAction = {
  totalTabsUnwrangled: number,
  type: 'SET_TOTAL_TABS_UNWRANGLED',
};

type SetTotalTabsWrangledAction = {
  totalTabsWrangled: number,
  type: 'SET_TOTAL_TABS_WRANGLED',
};

export type Action =
  | RemoveAllSavedTabsAction
  | RemoveSavedTabIdAction
  | SetSavedTabsAction
  | SetTotalTabsRemovedAction
  | SetTotalTabsUnwrangledAction
  | SetTotalTabsWrangledAction;

type State = {
  // Date of installation of Tab Wrangler
  installDate: number,

  // Tabs closed by Tab Wrangler
  savedTabs: Array<chrome$Tab>,

  // Number of tabs closed by any means since install
  totalTabsRemoved: number,

  // Number of tabs unwrangled (re-opened from the corral) since install
  totalTabsUnwrangled: number,

  // Number of tabs wrangled since install
  totalTabsWrangled: number,
};

export function createInitialState() {
  return {
    installDate: Date.now(),
    savedTabs: [],
    totalTabsRemoved: 0,
    totalTabsUnwrangled: 0,
    totalTabsWrangled: 0,
  };
}

const initialState = createInitialState();
export default function localStorage(state: State = initialState, action: Action) {
  switch (action.type) {
    case 'REMOVE_ALL_SAVED_TABS':
      return {
        ...state,
        savedTabs: [],
      };
    case 'REMOVE_SAVED_TAB_ID': {
      // Extract `tabId` to preserve type refinement inside `findIndex` callback.
      const actionTabId = action.tabId;
      const tabIndex = state.savedTabs.findIndex(tab => tab.id === actionTabId);
      if (tabIndex === -1) return state;

      // * Annotate `nextSavedTabs` to appease Flow. It's unclear why this annotation is required
      //   and can't be inferred.
      // * Copy the Array first (using `slice`) to achieve pseudo-immutability.
      const nextSavedTabs: Array<chrome$Tab> = state.savedTabs.slice();
      nextSavedTabs.splice(tabIndex, 1);
      return {
        ...state,
        savedTabs: nextSavedTabs,
      };
    }
    case 'SET_SAVED_TABS':
      return {
        ...state,
        savedTabs: action.savedTabs,
      };
    case 'SET_TOTAL_TABS_REMOVED':
      return {
        ...state,
        totalTabsRemoved: action.totalTabsRemoved,
      };
    case 'SET_TOTAL_TABS_UNWRANGLED':
      return {
        ...state,
        totalTabsUnwrangled: action.totalTabsUnwrangled,
      };
    case 'SET_TOTAL_TABS_WRANGLED':
      return {
        ...state,
        totalTabsWrangled: action.totalTabsWrangled,
      };
    default:
      return state;
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $, document, window, brackets  */

/**
 * The view that controls the showing and hiding of the sidebar. Dispatches the following events:
 *    hide -- when the sidebar is hidden
 *    show -- when the sidebar is shown
 */

define(function (require, exports, module) {
    "use strict";

    var AppInit             = require("utils/AppInit"),
        ProjectManager      = require("project/ProjectManager"),
        WorkingSetView      = require("project/WorkingSetView"),
        CommandManager      = require("command/CommandManager"),
        Commands            = require("command/Commands"),
        Strings             = require("strings"),
        EditorManager       = require("editor/EditorManager"),
        Global              = require("utils/Global"),
        Resizer             = require("utils/Resizer");

    // These vars are initialized by the htmlReady handler
    // below since they refer to DOM elements
    var $sidebar,
        $sidebarMenuText,
        $openFilesContainer,
        $projectTitle,
        $projectFilesContainer;

    /**
     * @private
     * Update project title when the project root changes
     */
    function _updateProjectTitle() {
        var displayName = ProjectManager.getProjectRoot().name;
        var fullPath = ProjectManager.getProjectRoot().fullPath;

        if (displayName === "" && fullPath === "/") {
            displayName = "/";
        }

        $projectTitle.html(displayName);
        $projectTitle.attr("title", fullPath);
    }

    /**
     * Toggle sidebar visibility.
     */
    function toggleSidebar(width) {
        Resizer.toggle($sidebar);
    }

    // Initialize items dependent on HTML DOM
    AppInit.htmlReady(function () {
        $sidebar                = $("#sidebar");
        $sidebarMenuText        = $("#menu-view-hide-sidebar span");
        $openFilesContainer     = $("#open-files-container");
        $projectTitle           = $("#project-title");
        $projectFilesContainer  = $("#project-files-container");

        // init
        WorkingSetView.create($openFilesContainer);

        $sidebar.on("panelResizeStart", function (evt, width) {
            $sidebar.find(".sidebar-selection-triangle").css("display", "none");
            $sidebar.find(".scroller-shadow").css("display", "none");
        });

        $sidebar.on("panelResizeUpdate", function (evt, width) {
            $sidebar.find(".sidebar-selection").width(width);
        });

        $sidebar.on("panelResizeEnd", function (evt, width) {
            $sidebar.find(".sidebar-selection").width(width);
            $sidebar.find(".sidebar-selection-triangle").css("display", "block").css("left", width);
            $sidebar.find(".scroller-shadow").css("display", "block");
            $projectFilesContainer.triggerHandler("scroll");
            $openFilesContainer.triggerHandler("scroll");
        });

        $sidebar.on("panelCollapsed", function (evt, width) {
            CommandManager.get(Commands.VIEW_HIDE_SIDEBAR).setName(Strings.CMD_SHOW_SIDEBAR);
        });

        $sidebar.on("panelExpanded", function (evt, width) {
            $sidebar.find(".sidebar-selection").width(width);
            $sidebar.find(".scroller-shadow").css("display", "block");
            $sidebar.find(".sidebar-selection-triangle").css("left", width);
            $projectFilesContainer.triggerHandler("scroll");
            $openFilesContainer.triggerHandler("scroll");
            CommandManager.get(Commands.VIEW_HIDE_SIDEBAR).setName(Strings.CMD_HIDE_SIDEBAR);
        });

        // AppInit.htmlReady in utils/Resizer executes before, so it's possible that the sidebar
        // is collapsed before we add the event. Check here initially
        if (!$sidebar.is(":visible")) {
            $sidebar.trigger("panelCollapsed");
        }
    });

    $(ProjectManager).on("projectOpen", _updateProjectTitle);
    CommandManager.register(Strings.CMD_HIDE_SIDEBAR,       Commands.VIEW_HIDE_SIDEBAR,     toggleSidebar);

    // Define public API
    exports.toggleSidebar = toggleSidebar;
});<commit_msg>Trigger scroll after updating title to reposition scroller shadows<commit_after>/*
 * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $, document, window, brackets  */

/**
 * The view that controls the showing and hiding of the sidebar. Dispatches the following events:
 *    hide -- when the sidebar is hidden
 *    show -- when the sidebar is shown
 */

define(function (require, exports, module) {
    "use strict";

    var AppInit             = require("utils/AppInit"),
        ProjectManager      = require("project/ProjectManager"),
        WorkingSetView      = require("project/WorkingSetView"),
        CommandManager      = require("command/CommandManager"),
        Commands            = require("command/Commands"),
        Strings             = require("strings"),
        EditorManager       = require("editor/EditorManager"),
        Global              = require("utils/Global"),
        Resizer             = require("utils/Resizer");

    // These vars are initialized by the htmlReady handler
    // below since they refer to DOM elements
    var $sidebar,
        $sidebarMenuText,
        $openFilesContainer,
        $projectTitle,
        $projectFilesContainer;

    /**
     * @private
     * Update project title when the project root changes
     */
    function _updateProjectTitle() {
        var displayName = ProjectManager.getProjectRoot().name;
        var fullPath = ProjectManager.getProjectRoot().fullPath;

        if (displayName === "" && fullPath === "/") {
            displayName = "/";
        }

        $projectTitle.html(displayName);
        $projectTitle.attr("title", fullPath);

        // Trigger a scroll on the project files container to
        // reposition the scroller shadows and avoid issue #2255
        $projectFilesContainer.trigger("scroll");
    }

    /**
     * Toggle sidebar visibility.
     */
    function toggleSidebar(width) {
        Resizer.toggle($sidebar);
    }

    // Initialize items dependent on HTML DOM
    AppInit.htmlReady(function () {
        $sidebar                = $("#sidebar");
        $sidebarMenuText        = $("#menu-view-hide-sidebar span");
        $openFilesContainer     = $("#open-files-container");
        $projectTitle           = $("#project-title");
        $projectFilesContainer  = $("#project-files-container");

        // init
        WorkingSetView.create($openFilesContainer);

        $sidebar.on("panelResizeStart", function (evt, width) {
            $sidebar.find(".sidebar-selection-triangle").css("display", "none");
            $sidebar.find(".scroller-shadow").css("display", "none");
        });

        $sidebar.on("panelResizeUpdate", function (evt, width) {
            $sidebar.find(".sidebar-selection").width(width);
        });

        $sidebar.on("panelResizeEnd", function (evt, width) {
            $sidebar.find(".sidebar-selection").width(width);
            $sidebar.find(".sidebar-selection-triangle").css("display", "block").css("left", width);
            $sidebar.find(".scroller-shadow").css("display", "block");
            $projectFilesContainer.triggerHandler("scroll");
            $openFilesContainer.triggerHandler("scroll");
        });

        $sidebar.on("panelCollapsed", function (evt, width) {
            CommandManager.get(Commands.VIEW_HIDE_SIDEBAR).setName(Strings.CMD_SHOW_SIDEBAR);
        });

        $sidebar.on("panelExpanded", function (evt, width) {
            $sidebar.find(".sidebar-selection").width(width);
            $sidebar.find(".scroller-shadow").css("display", "block");
            $sidebar.find(".sidebar-selection-triangle").css("left", width);
            $projectFilesContainer.triggerHandler("scroll");
            $openFilesContainer.triggerHandler("scroll");
            CommandManager.get(Commands.VIEW_HIDE_SIDEBAR).setName(Strings.CMD_HIDE_SIDEBAR);
        });

        // AppInit.htmlReady in utils/Resizer executes before, so it's possible that the sidebar
        // is collapsed before we add the event. Check here initially
        if (!$sidebar.is(":visible")) {
            $sidebar.trigger("panelCollapsed");
        }
    });

    $(ProjectManager).on("projectOpen", _updateProjectTitle);
    CommandManager.register(Strings.CMD_HIDE_SIDEBAR,       Commands.VIEW_HIDE_SIDEBAR,     toggleSidebar);

    // Define public API
    exports.toggleSidebar = toggleSidebar;
});<|endoftext|>