<commit_before>// Set the parameters.
var width   = 300,
    height  = 300,
    padding = 50;

// Get our data.
var strings = [
[1,1.5], [2,2.5], [3,3.5], [4,4.5], [5,5.5], [6,6] ];

// Create scale functions.
var xScale = d3.scale.linear()
    .domain([1, d3.max(strings, function(d) { return d[0]; })])
    .range([padding, width - padding * 2]);
var yScale = d3.scale.linear()
    .domain([1, d3.max(strings, function(d) { return d[1]; })])
    .range([height - padding, padding]);

// Define the X axis.
var xAxis = d3.svg.axis()
    .scale(xScale)
    .orient("bottom")
    .ticks(6);

// Define the Y axis.
var yAxis = d3.svg.axis()
    .scale(yScale)
    .orient("left")
    .ticks(6);

// Additional axes
function make_x_axis() {
    return d3.svg.axis()
        .scale(xScale)
         .orient("bottom")
         .ticks(6)
}
function make_y_axis() {
    return d3.svg.axis()
        .scale(yScale)
        .orient("left")
        .ticks(6)
}

// Create SVG element.
var svg = d3.select("body")
    .append("svg")
    .attr("width", width)
    .attr("height", height);

// Create circles.
svg.selectAll("circle")
    .data(strings)
  .enter()
    .append("circle")
    .attr("cx", function(d) { return xScale(d[0]); })
    .attr("cy", function(d) { return yScale(d[1]); })
    .attr("r", 10);

/* Create labels.
svg.selectAll("text")
    .data(strings)
  .enter()
    .append("text")
    .text(function(d) { return d[0] + "," + d[1]; })
    .attr("x", function(d) { return xScale(d[0]); })
    .attr("y", function(d) { return yScale(d[1]); })
    .attr("font-family", "sans-serif")
    .attr("font-size", "11px")
    .attr("fill", "red");
*/

// Add the X axis to the canvas.
svg.append("g")
    .attr("class", "axis")
    .attr("transform", "translate(0," + (height - padding) + ")")
    .call(xAxis);

// Create Y axis.
svg.append("g")
    .attr("class", "axis")
    .attr("transform", "translate(" + padding + ",0)")
    .call(yAxis);

// Add the tick axes to the canvas.
svg.append("g")
    .attr("class", "grid")
    .attr("transform", "translate(0," + (height - padding) + ")")
    .call(make_x_axis()
        .tickSize(-height, 0, 0)
        .tickFormat("")
    );
svg.append("g")
    .attr("class", "grid")
    .call(make_y_axis()
        .tickSize(-width, 0, 0)
        .tickFormat("")
    );

<commit_msg>Constrained ticks to grid<commit_after>// Set the parameters.
var width   = 300,
    height  = 300,
    padding = 50;

// Get our data.
var strings = [
    [1, 1.5],
    [2, 2.5],
    [3, 3.5],
    [4, 4.5],
    [5, 5.5],
    [6, 6.5]
];

// Create scale functions.
var xScale = d3.scale.linear()
    .domain([1, d3.max(strings, function(d) { return d[0]; })])
    .range([padding, width - padding * 2]);
var yScale = d3.scale.linear()
    .domain([1, d3.max(strings, function(d) { return d[1]; })])
    .range([height - padding, padding]);

// Define the X axis.
var xAxis = d3.svg.axis()
    .scale(xScale)
    .orient("bottom")
    .ticks(6);

// Define the Y axis.
var yAxis = d3.svg.axis()
    .scale(yScale)
    .orient("left")
    .ticks(6);

// Create SVG element.
var svg = d3.select("body")
    .append("svg")
    .attr("width", width)
    .attr("height", height);

// Additional axes
function make_x_axis() {
    return d3.svg.axis()
        .scale(xScale)
         .orient("bottom")
         .ticks(6)
}
function make_y_axis() {
    return d3.svg.axis()
        .scale(yScale)
        .orient("left")
        .ticks(6)
}

// Add the edge axes to the canvas.
svg.append("g")
    .attr("class", "axis")
    .attr("transform", "translate(0," + (height - padding) + ")")
    .call(xAxis);
svg.append("g")
    .attr("class", "axis")
    .attr("transform", "translate(" + padding + ",0)")
    .call(yAxis);

// Add the tick axes to the canvas.
svg.append("g")
    .attr("class", "grid")
    .attr("transform", "translate(0," + (height - padding) + ")")
    .call(make_x_axis()
        .tickSize(2 * padding - height, 0, 0)
        .tickFormat("")
    );
svg.append("g")
    .attr("class", "grid")
    .attr("transform", "translate(" + padding + ",0)")
    .call(make_y_axis()
        .tickSize(3 * padding - width, 0, 0)
        .tickFormat("")
    );

// Create circles.
svg.selectAll("circle")
    .data(strings)
  .enter()
    .append("circle")
    .attr("cx", function(d) { return xScale(d[0]); })
    .attr("cy", function(d) { return yScale(d[1]); })
    .attr("r", 10);

/* Create labels.
svg.selectAll("text")
    .data(strings)
  .enter()
    .append("text")
    .text(function(d) { return d[0] + "," + d[1]; })
    .attr("x", function(d) { return xScale(d[0]); })
    .attr("y", function(d) { return yScale(d[1]); })
    .attr("font-family", "sans-serif")
    .attr("font-size", "11px")
    .attr("fill", "red");
*/

<|endoftext|>


<commit_before>/**
 * Created by jlueders on 4/13/15.
 */
angular.module('NodeWebBase')
    .controller('runTabController', function ($scope, $rootScope) {
        $scope.dataSets= ["--select--"];
        $scope.polygonFiles = ["--select--"];
        $scope.polyFile = "polyfile.json";
        $scope.polyFileSelected = function(item){
            $scope.polyFile = item;
        };

        $scope.populatePolygonSelect = function() {
            if(!$rootScope.isAppConfigured())
                return;
            $.ajax({
                url: $rootScope.baseUrl + "app/controlBox/popScoreList",
                data : {
                    filePath: $rootScope.savePath,
                    subDir:$scope.fileSubDir
                },
                dataType: "json",
                success: function (response) {
                    $scope.$apply(function() {
                        $scope.polygonFiles = response.lFiles;
                    });
                },
                error: $rootScope.showError
            });
        };

        $scope.getDataSets = function(){
            if(!$rootScope.isAppConfigured())
                return;
            $.ajax({
                url:  $rootScope.baseUrl + "app/controlBox/getDataSets",
                dataType: "json",
                success: function (response) {
                    $scope.$apply(function(){
                        $scope.dataSets= response;
                    });

                },
                error: $rootScope.showError
            });
        };
        $scope.saveList = function(){

            $rootScope.$emit("getShapesText",
            {
                "scope":this,
                "callback":function(resultsText){
                    if(!$rootScope.isAppConfigured())
                        return;
                    var pName = $("#pFileName").val();
                    $.ajax({
                        url:  $rootScope.baseUrl + "app/controlBox/writePoly",
                        data: {
                            filePath: $rootScope.savePath,
                            filePolygon: pName,
                            fileString: resultsText
                        },
                        dataType: "text",
                        success: function (response) {
                            $("#resultsText").text(pName + " written");
                        },
                        error: $rootScope.showError
                    });
                }
            });
        };

        $scope.applyScores = function() {
            if(!$rootScope.isAppConfigured())
                return;

            var pName = $("#pFileName").val();
            var sName = $("#sFileName").val();
            var dSet = $("#dataSetSelect").val();
            var bML  = $("#useML").is(":checked");
            var bBay = $("#useBayes").is(":checked");
            //change source based on Checkbox value
            var fThresh=$("#sTopN").val();
            var bPer = $("#bPercent").is(":checked");
            if(bPer==true){
                fThresh=$("#sTopPercent").val();
            }
            var nFeat = $("#nFeat").val();
            var sSWords = $("#cStopW").val();
            $.ajax({
                url:  $rootScope.baseUrl + "app/controlBox/applyScores",
                data: {
                    filePath: $rootScope.savePath,
                    filePolygon: pName,
                    fileAppOut: sName,
                    fScoreThresh: fThresh,
                    dataSet: dSet,
                    useML: bML,
                    useBayes: bBay,
                    nFeatures: nFeat,
                    custStopWord: sSWords
                },
                dataType: "text",
                success: function (response) {
                    $("#resultsText").text("Job Launched");
                },
                error: $rootScope.showError
            });
        };
        $scope.modReturn = function() {
            var bChecked = $("#bPercent").is(":checked");
            var f1 = $("#rankReturnInput");
            var f2 = $("#percentReturnInput");
            if(bChecked == true){
                f1.addClass("invis");
                f2.removeClass("invis");
            } else {
                f1.removeClass("invis");
                f2.addClass("invis");
            }
        };

        $scope.toggleAdv = function() {
            var bChecked = $("#bAdvanced").is(":checked");
            var r1 = $("#hr1");
            var r2 = $("#hr2");
            var r3 = $("#hr3");
            var r4 = $("#hr4");
            var r5 = $("#hr5");
            if( bChecked == true) {
                r1.removeClass("invis");
                r2.removeClass("invis");
                //r3.removeClass("invis");
                r4.removeClass("invis");
                r5.removeClass("invis");
            } else {
                r1.addClass("invis");
                r2.addClass("invis");
                //r3.addClass("invis");
                r4.addClass("invis");
                r5.addClass("invis");
            }
        };

        //go ahead and get the data sets from the server
        $scope.getDataSets();
    });
<commit_msg>auto populate polygon files<commit_after>/**
 * Created by jlueders on 4/13/15.
 */
angular.module('NodeWebBase')
    .controller('runTabController', function ($scope, $rootScope) {
        $scope.dataSets= ["--select--"];
        $scope.polygonFiles = ["--select--"];
        $scope.polyFile = "polyfile.json";
        $scope.polyFileSelected = function(item){
            $scope.polyFile = item;
        };

        $scope.populatePolygonSelect = function() {
            if(!$rootScope.isAppConfigured())
                return;
            $.ajax({
                url: $rootScope.baseUrl + "app/controlBox/popScoreList",
                data : {
                    filePath: $rootScope.savePath,
                    subDir:$scope.fileSubDir
                },
                dataType: "json",
                success: function (response) {
                    $scope.$apply(function() {
                        $scope.polygonFiles = response.lFiles;
                    });
                },
                error: $rootScope.showError
            });
        };

        $scope.getDataSets = function(){
            if(!$rootScope.isAppConfigured())
                return;
            $.ajax({
                url:  $rootScope.baseUrl + "app/controlBox/getDataSets",
                dataType: "json",
                success: function (response) {
                    $scope.$apply(function(){
                        $scope.dataSets= response;
                    });

                },
                error: $rootScope.showError
            });
        };
        $scope.saveList = function(){

            $rootScope.$emit("getShapesText",
            {
                "scope":this,
                "callback":function(resultsText){
                    if(!$rootScope.isAppConfigured())
                        return;
                    var pName = $("#pFileName").val();
                    $.ajax({
                        url:  $rootScope.baseUrl + "app/controlBox/writePoly",
                        data: {
                            filePath: $rootScope.savePath,
                            filePolygon: pName,
                            fileString: resultsText
                        },
                        dataType: "text",
                        success: function (response) {
                            $("#resultsText").text(pName + " written");
                        },
                        error: $rootScope.showError
                    });
                }
            });
        };

        $scope.applyScores = function() {
            if(!$rootScope.isAppConfigured())
                return;

            var pName = $("#pFileName").val();
            var sName = $("#sFileName").val();
            var dSet = $("#dataSetSelect").val();
            var bML  = $("#useML").is(":checked");
            var bBay = $("#useBayes").is(":checked");
            //change source based on Checkbox value
            var fThresh=$("#sTopN").val();
            var bPer = $("#bPercent").is(":checked");
            if(bPer==true){
                fThresh=$("#sTopPercent").val();
            }
            var nFeat = $("#nFeat").val();
            var sSWords = $("#cStopW").val();
            $.ajax({
                url:  $rootScope.baseUrl + "app/controlBox/applyScores",
                data: {
                    filePath: $rootScope.savePath,
                    filePolygon: pName,
                    fileAppOut: sName,
                    fScoreThresh: fThresh,
                    dataSet: dSet,
                    useML: bML,
                    useBayes: bBay,
                    nFeatures: nFeat,
                    custStopWord: sSWords
                },
                dataType: "text",
                success: function (response) {
                    $("#resultsText").text("Job Launched");
                },
                error: $rootScope.showError
            });
        };
        $scope.modReturn = function() {
            var bChecked = $("#bPercent").is(":checked");
            var f1 = $("#rankReturnInput");
            var f2 = $("#percentReturnInput");
            if(bChecked == true){
                f1.addClass("invis");
                f2.removeClass("invis");
            } else {
                f1.removeClass("invis");
                f2.addClass("invis");
            }
        };

        $scope.toggleAdv = function() {
            var bChecked = $("#bAdvanced").is(":checked");
            var r1 = $("#hr1");
            var r2 = $("#hr2");
            var r3 = $("#hr3");
            var r4 = $("#hr4");
            var r5 = $("#hr5");
            if( bChecked == true) {
                r1.removeClass("invis");
                r2.removeClass("invis");
                //r3.removeClass("invis");
                r4.removeClass("invis");
                r5.removeClass("invis");
            } else {
                r1.addClass("invis");
                r2.addClass("invis");
                //r3.addClass("invis");
                r4.addClass("invis");
                r5.addClass("invis");
            }
        };

        //go ahead and get the data sets from the server
        $scope.getDataSets();
        $scope.populatePolygonSelect();
    });
<|endoftext|>


<commit_before>/*
 * Copyright 2020 Google LLC
 *
 * 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
 *
 *     https://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.
 */
/**
 * External dependencies
 */
import { getRelativeDisplayDate } from '@web-stories-wp/date';
import { __, sprintf } from '@web-stories-wp/i18n';
import { useFeatures } from 'flagged';
import PropTypes from 'prop-types';
import { useMemo } from 'react';
import { css } from 'styled-components';
/**
 * Internal dependencies
 */
import { StoryMenu } from '../../../../../components';
import { generateStoryMenu } from '../../../../../components/popoverMenu/story-menu-generator';
import { STORY_STATUS } from '../../../../../constants';
import {
  PageSizePropType,
  RenameStoryPropType,
  StoryMenuPropType,
  StoryPropType,
} from '../../../../../types';
import { titleFormatted } from '../../../../../utils';
import { useConfig } from '../../../../config';
import {
  CardWrapper,
  Container,
  CustomCardGridItem,
  Gradient,
  Poster,
  Scrim,
} from './components';
import StoryDisplayContent from './storyDisplayContent';

const StoryGridItem = ({
  handleFocus,
  isActive,
  itemRefs = {},
  pageSize,
  renameStory,
  story,
  storyMenu,
}) => {
  const { enablePostLocking } = useFeatures();
  const { userId } = useConfig();
  const tabIndex = isActive ? 0 : -1;
  const titleRenameProps = renameStory
    ? {
        editMode: renameStory?.id === story?.id,
        onEditComplete: (newTitle) =>
          renameStory?.handleOnRenameStory(story, newTitle),
        onEditCancel: renameStory?.handleCancelRename,
      }
    : {};

  const isLocked = useMemo(
    () => enablePostLocking && story?.locked && userId !== story?.lockUser.id,
    [enablePostLocking, story, userId]
  );

  const generatedMenuItems = useMemo(
    () =>
      generateStoryMenu({
        menuItemActions: storyMenu.menuItemActions,
        menuItems: storyMenu.menuItems,
        story,
        isLocked,
      }),
    [storyMenu, story, isLocked]
  );

  const storyDate = getRelativeDisplayDate(
    story?.status === STORY_STATUS.DRAFT
      ? story?.modified_gmt
      : story?.created_gmt
  );

  const formattedTitle = titleFormatted(story.title);

  const memoizedStoryMenu = useMemo(
    () => (
      <StoryMenu
        menuLabel={
          isLocked
            ? sprintf(
                /* translators: 1: story title. 2: user currently editing the story. */
                __('Context menu for %1$s (locked by %2$s)', 'web-stories'),
                formattedTitle,
                story?.lockUser.name
              )
            : sprintf(
                /* translators: %s: story title.*/
                __('Context menu for %s', 'web-stories'),
                formattedTitle
              )
        }
        itemActive={isActive}
        tabIndex={tabIndex}
        onMoreButtonSelected={storyMenu.handleMenuToggle}
        contextMenuId={storyMenu.contextMenuId}
        storyId={story.id}
        isInverted
        menuItems={generatedMenuItems}
        menuStyleOverrides={css`
          /* force menu position to bottom corner */
          margin: 0 0 0 auto;
        `}
      />
    ),
    [
      isLocked,
      formattedTitle,
      isActive,
      tabIndex,
      storyMenu,
      story.id,
      story?.lockUser,
      generatedMenuItems,
    ]
  );

  return (
    <CustomCardGridItem
      data-testid={`story-grid-item-${story.id}`}
      onFocus={handleFocus}
      $posterHeight={pageSize.posterHeight}
      ref={(el) => {
        itemRefs.current[story.id] = el;
      }}
      title={sprintf(
        /* translators: %s: story title.*/
        __('Details about %s', 'web-stories'),
        formattedTitle
      )}
    >
      <Container>
        <CardWrapper>
          <Poster
            {...(story.featuredMediaUrl
              ? {
                  alt: sprintf(
                    /* translators: %s: Story title. */
                    __('%s Poster image', 'web-stories'),
                    formattedTitle
                  ),
                  as: 'img',
                  src: story.featuredMediaUrl,
                }
              : null)}
          />
          <Gradient />
          <Scrim>
            <StoryDisplayContent
              author={story.author}
              contextMenu={memoizedStoryMenu}
              displayDate={storyDate}
              formattedTitle={formattedTitle}
              id={story.id}
              isLocked={isLocked}
              lockUser={story?.lockUser}
              status={story?.status}
              tabIndex={tabIndex}
              title={story.title}
              titleLink={story.editStoryLink}
              {...titleRenameProps}
            />
          </Scrim>
        </CardWrapper>
      </Container>
    </CustomCardGridItem>
  );
};

StoryGridItem.propTypes = {
  handleFocus: PropTypes.func,
  isActive: PropTypes.bool,
  itemRefs: PropTypes.shape({
    current: PropTypes.oneOfType([
      PropTypes.func,
      PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
    ]),
  }),
  pageSize: PageSizePropType,
  renameStory: RenameStoryPropType,
  storyMenu: StoryMenuPropType,
  story: StoryPropType,
};

export default StoryGridItem;
<commit_msg>use an aria label to give context to list item grid posters instead of a title. (#8569)<commit_after>/*
 * Copyright 2020 Google LLC
 *
 * 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
 *
 *     https://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.
 */
/**
 * External dependencies
 */
import { getRelativeDisplayDate } from '@web-stories-wp/date';
import { __, sprintf } from '@web-stories-wp/i18n';
import { useFeatures } from 'flagged';
import PropTypes from 'prop-types';
import { useMemo } from 'react';
import { css } from 'styled-components';
/**
 * Internal dependencies
 */
import { StoryMenu } from '../../../../../components';
import { generateStoryMenu } from '../../../../../components/popoverMenu/story-menu-generator';
import { STORY_STATUS } from '../../../../../constants';
import {
  PageSizePropType,
  RenameStoryPropType,
  StoryMenuPropType,
  StoryPropType,
} from '../../../../../types';
import { titleFormatted } from '../../../../../utils';
import { useConfig } from '../../../../config';
import {
  CardWrapper,
  Container,
  CustomCardGridItem,
  Gradient,
  Poster,
  Scrim,
} from './components';
import StoryDisplayContent from './storyDisplayContent';

const StoryGridItem = ({
  handleFocus,
  isActive,
  itemRefs = {},
  pageSize,
  renameStory,
  story,
  storyMenu,
}) => {
  const { enablePostLocking } = useFeatures();
  const { userId } = useConfig();
  const tabIndex = isActive ? 0 : -1;
  const titleRenameProps = renameStory
    ? {
        editMode: renameStory?.id === story?.id,
        onEditComplete: (newTitle) =>
          renameStory?.handleOnRenameStory(story, newTitle),
        onEditCancel: renameStory?.handleCancelRename,
      }
    : {};

  const isLocked = useMemo(
    () => enablePostLocking && story?.locked && userId !== story?.lockUser.id,
    [enablePostLocking, story, userId]
  );

  const generatedMenuItems = useMemo(
    () =>
      generateStoryMenu({
        menuItemActions: storyMenu.menuItemActions,
        menuItems: storyMenu.menuItems,
        story,
        isLocked,
      }),
    [storyMenu, story, isLocked]
  );

  const storyDate = getRelativeDisplayDate(
    story?.status === STORY_STATUS.DRAFT
      ? story?.modified_gmt
      : story?.created_gmt
  );

  const formattedTitle = titleFormatted(story.title);

  const memoizedStoryMenu = useMemo(
    () => (
      <StoryMenu
        menuLabel={
          isLocked
            ? sprintf(
                /* translators: 1: story title. 2: user currently editing the story. */
                __('Context menu for %1$s (locked by %2$s)', 'web-stories'),
                formattedTitle,
                story?.lockUser.name
              )
            : sprintf(
                /* translators: %s: story title.*/
                __('Context menu for %s', 'web-stories'),
                formattedTitle
              )
        }
        itemActive={isActive}
        tabIndex={tabIndex}
        onMoreButtonSelected={storyMenu.handleMenuToggle}
        contextMenuId={storyMenu.contextMenuId}
        storyId={story.id}
        isInverted
        menuItems={generatedMenuItems}
        menuStyleOverrides={css`
          /* force menu position to bottom corner */
          margin: 0 0 0 auto;
        `}
      />
    ),
    [
      isLocked,
      formattedTitle,
      isActive,
      tabIndex,
      storyMenu,
      story.id,
      story?.lockUser,
      generatedMenuItems,
    ]
  );

  return (
    <CustomCardGridItem
      data-testid={`story-grid-item-${story.id}`}
      onFocus={handleFocus}
      $posterHeight={pageSize.posterHeight}
      ref={(el) => {
        itemRefs.current[story.id] = el;
      }}
      aria-label={sprintf(
        /* translators: %s: story title.*/
        __('Details about %s', 'web-stories'),
        formattedTitle
      )}
    >
      <Container>
        <CardWrapper>
          <Poster
            {...(story.featuredMediaUrl
              ? {
                  alt: sprintf(
                    /* translators: %s: Story title. */
                    __('%s Poster image', 'web-stories'),
                    formattedTitle
                  ),
                  as: 'img',
                  src: story.featuredMediaUrl,
                }
              : null)}
          />
          <Gradient />
          <Scrim>
            <StoryDisplayContent
              author={story.author}
              contextMenu={memoizedStoryMenu}
              displayDate={storyDate}
              formattedTitle={formattedTitle}
              id={story.id}
              isLocked={isLocked}
              lockUser={story?.lockUser}
              status={story?.status}
              tabIndex={tabIndex}
              title={story.title}
              titleLink={story.editStoryLink}
              {...titleRenameProps}
            />
          </Scrim>
        </CardWrapper>
      </Container>
    </CustomCardGridItem>
  );
};

StoryGridItem.propTypes = {
  handleFocus: PropTypes.func,
  isActive: PropTypes.bool,
  itemRefs: PropTypes.shape({
    current: PropTypes.oneOfType([
      PropTypes.func,
      PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
    ]),
  }),
  pageSize: PageSizePropType,
  renameStory: RenameStoryPropType,
  storyMenu: StoryMenuPropType,
  story: StoryPropType,
};

export default StoryGridItem;
<|endoftext|>


<commit_before>// @flow

import Text from './text'
import {emojiIndex} from 'emoji-mart'
import Emoji from './emoji'
import React, {PureComponent} from 'react'
import {List} from 'immutable'
import {globalStyles, globalColors, globalMargins} from '../styles'

import type {Props as EmojiProps} from './emoji'
import type {Props} from './markdown'
import type {PropsOf} from '../constants/types/more'

type TagInfo<C> = {Component: Class<C>, props: PropsOf<C>}

const codeSnippetStyle = {
  ...globalStyles.fontTerminal,
  ...globalStyles.rounded,
  fontSize: 12,
  paddingLeft: globalMargins.xtiny,
  paddingRight: globalMargins.xtiny,
  backgroundColor: globalColors.beige,
  color: globalColors.blue,
}

const codeSnippetBlockStyle = {
  ...codeSnippetStyle,
  display: 'block',
  color: globalColors.black_75,
  backgroundColor: globalColors.beige,
  marginTop: globalMargins.xtiny,
  marginBottom: globalMargins.xtiny,
  paddingTop: globalMargins.xtiny,
  paddingBottom: globalMargins.xtiny,
  paddingLeft: globalMargins.tiny,
  paddingRight: globalMargins.tiny,
  whiteSpace: 'pre',
}

// Order matters, since we want to match the longer ticks first
const openToClosePair = {
  '```': '```',
  '`': '`',
  '*': '*',
  '_': '_',
  '~': '~',
  ':': ':',
}

// We have to escape certain marks when turning them into a regex
const markToRegex = {
  '```': '```',
  '`': '`',
  '*': '\\*',
  '_': '_',
  '~': '~',
  ':': ':',
}

const plainStringTag = {Component: Text, props: {type: 'Body', style: {color: undefined}}}

class EmojiIfExists extends PureComponent<void, EmojiProps, void> {
  render () {
    const emoji = this.props.children && this.props.children.join('')
    const exists = emojiIndex.emojis.hasOwnProperty(emoji)
    return exists ? <Emoji {...this.props} /> : <Text {...plainStringTag.props}>:{emoji}:</Text>
  }
}

const initialOpenToTag = {
  '`': {Component: Text, props: {type: 'Body', style: codeSnippetStyle}},
  '```': {Component: Text, props: {type: 'Body', style: codeSnippetBlockStyle}},
  '*': {Component: Text, props: {type: 'BodySemibold', style: {color: undefined}}},
  '_': {Component: Text, props: {type: 'Body', style: {fontStyle: 'italic', fontWeight: undefined, color: undefined}}},
  '~': {Component: Text, props: {type: 'Body', style: {textDecoration: 'line-through', fontWeight: undefined, color: undefined}}},
  ':': {Component: EmojiIfExists, props: {size: 16}},
}

const openToNextOpenToTag = {
  '`': {},
  '```': {},
  '*': initialOpenToTag,
  '_': initialOpenToTag,
  '~': initialOpenToTag,
  ':': {},
}

type TagMeta = {
  componentInfo: {Component: ReactClass<*>, props: Object},
  textSoFar: string,
  elementsSoFar: List<React$Element<*> | string>,
  openToTag: {[key: string]: TagInfo<Text> | TagInfo<EmojiIfExists>},
  closingTag: ?string,
}

const initalTagMeta: TagMeta = {
  componentInfo: plainStringTag,
  textSoFar: '',
  elementsSoFar: new List(),
  openToTag: initialOpenToTag,
  closingTag: null,
}

type TagStack = List<TagMeta>

const marksRegex = new RegExp(Object.keys(openToClosePair).map(s => '^' + markToRegex[s]).join('|'))
function matchWithMark (text: string): ?{matchingMark: string, restText: string} {
  const m = text.match(marksRegex)
  if (m && m[0]) {
    const matchingMark = m[0]
    return {matchingMark, restText: text.slice(matchingMark.length)}
  }
  return null
}

function hasClosingMark (text: string, openingMark): boolean {
  const closingMark = openToClosePair[openingMark]
  return text.indexOf(closingMark, openingMark.length) !== -1
}

function tagMetaToElement (m: TagMeta, key) {
  const {textSoFar, elementsSoFar, componentInfo: {Component, props}} = m
  return <Component key={key} {...props}>{elementsSoFar.push(textSoFar).toArray()}</Component>
}

function _parse (text: string, tagStack: TagStack, key: number): React$Element<*> {
  // Don't recurse, use a stack
  const parseStack = [{text, tagStack, key}]
  while (parseStack.length) {
    const {text, tagStack, key} = parseStack.pop()

    if (text.length === 0 && tagStack.count() < 1) {
      throw new Error('Messed up parsing markdown text')
    }

    if (text.length === 0 && tagStack.count() === 1) {
      return tagMetaToElement(tagStack.last(), key)
    }

    const topTag = tagStack.last()

    const {openToTag, closingTag} = topTag
    const firstChar = text[0]
    const match = matchWithMark(text)
    const restText = match ? match.restText : text.slice(1)
    const matchingMark: ?string = match && match.matchingMark

    if (text.length === 0 || closingTag && closingTag === matchingMark) {
      const newElement = tagMetaToElement(topTag, key)
      parseStack.push({
        text: restText,
        tagStack: tagStack.pop().update(-1, m => ({...m, elementsSoFar: m.elementsSoFar.push(newElement)})),
        key: key + 1,
      })
    } else if (matchingMark && openToTag[matchingMark] && hasClosingMark(text, matchingMark)) {
      parseStack.push({
        text: restText,
        tagStack: tagStack
          .update(-1, m => ({...m, textSoFar: '', elementsSoFar: m.elementsSoFar.push(m.textSoFar)}))
          .push({
            componentInfo: openToTag[matchingMark],
            closingTag: openToClosePair[matchingMark],
            textSoFar: '',
            elementsSoFar: new List(),
            openToTag: openToNextOpenToTag[matchingMark] || {},
          }),
        key,
      })
    } else {
      if (firstChar === '\\') {
        parseStack.push({
          text: text.slice(2),
          tagStack: tagStack.update(-1, m => ({...m, textSoFar: m.textSoFar + text[1]})),
          key,
        })
      } else {
        parseStack.push({
          text: restText,
          tagStack: tagStack.update(-1, m => ({...m, textSoFar: m.textSoFar + firstChar})),
          key,
        })
      }
    }
  }

  throw new Error('Messed up parsing markdown text')
}

// It's a lot easier to parse emojis if we change :santa::skin-tone-3: to :santa\:\:skin-tone-3:
function preprocessEmojiColors (text: string): string {
  return text.replace(/:([\w-]*)::(skin-tone-\d):/g, ':$1\\:\\:$2:')
}

const initialTagStack = new List([initalTagMeta])

class Markdown extends PureComponent<void, Props, void> {
  render () {
    return <Text type='Body' style={this.props.style}>{_parse(preprocessEmojiColors(this.props.children || ''), initialTagStack, 0)}</Text>
  }
}

export default Markdown
<commit_msg>Quick fix for \ => undefined<commit_after>// @flow

import Text from './text'
import {emojiIndex} from 'emoji-mart'
import Emoji from './emoji'
import React, {PureComponent} from 'react'
import {List} from 'immutable'
import {globalStyles, globalColors, globalMargins} from '../styles'

import type {Props as EmojiProps} from './emoji'
import type {Props} from './markdown'
import type {PropsOf} from '../constants/types/more'

type TagInfo<C> = {Component: Class<C>, props: PropsOf<C>}

const codeSnippetStyle = {
  ...globalStyles.fontTerminal,
  ...globalStyles.rounded,
  fontSize: 12,
  paddingLeft: globalMargins.xtiny,
  paddingRight: globalMargins.xtiny,
  backgroundColor: globalColors.beige,
  color: globalColors.blue,
}

const codeSnippetBlockStyle = {
  ...codeSnippetStyle,
  display: 'block',
  color: globalColors.black_75,
  backgroundColor: globalColors.beige,
  marginTop: globalMargins.xtiny,
  marginBottom: globalMargins.xtiny,
  paddingTop: globalMargins.xtiny,
  paddingBottom: globalMargins.xtiny,
  paddingLeft: globalMargins.tiny,
  paddingRight: globalMargins.tiny,
  whiteSpace: 'pre',
}

// Order matters, since we want to match the longer ticks first
const openToClosePair = {
  '```': '```',
  '`': '`',
  '*': '*',
  '_': '_',
  '~': '~',
  ':': ':',
}

// We have to escape certain marks when turning them into a regex
const markToRegex = {
  '```': '```',
  '`': '`',
  '*': '\\*',
  '_': '_',
  '~': '~',
  ':': ':',
}

const plainStringTag = {Component: Text, props: {type: 'Body', style: {color: undefined}}}

class EmojiIfExists extends PureComponent<void, EmojiProps, void> {
  render () {
    const emoji = this.props.children && this.props.children.join('')
    const exists = emojiIndex.emojis.hasOwnProperty(emoji)
    return exists ? <Emoji {...this.props} /> : <Text {...plainStringTag.props}>:{emoji}:</Text>
  }
}

const initialOpenToTag = {
  '`': {Component: Text, props: {type: 'Body', style: codeSnippetStyle}},
  '```': {Component: Text, props: {type: 'Body', style: codeSnippetBlockStyle}},
  '*': {Component: Text, props: {type: 'BodySemibold', style: {color: undefined}}},
  '_': {Component: Text, props: {type: 'Body', style: {fontStyle: 'italic', fontWeight: undefined, color: undefined}}},
  '~': {Component: Text, props: {type: 'Body', style: {textDecoration: 'line-through', fontWeight: undefined, color: undefined}}},
  ':': {Component: EmojiIfExists, props: {size: 16}},
}

const openToNextOpenToTag = {
  '`': {},
  '```': {},
  '*': initialOpenToTag,
  '_': initialOpenToTag,
  '~': initialOpenToTag,
  ':': {},
}

type TagMeta = {
  componentInfo: {Component: ReactClass<*>, props: Object},
  textSoFar: string,
  elementsSoFar: List<React$Element<*> | string>,
  openToTag: {[key: string]: TagInfo<Text> | TagInfo<EmojiIfExists>},
  closingTag: ?string,
}

const initalTagMeta: TagMeta = {
  componentInfo: plainStringTag,
  textSoFar: '',
  elementsSoFar: new List(),
  openToTag: initialOpenToTag,
  closingTag: null,
}

type TagStack = List<TagMeta>

const marksRegex = new RegExp(Object.keys(openToClosePair).map(s => '^' + markToRegex[s]).join('|'))
function matchWithMark (text: string): ?{matchingMark: string, restText: string} {
  const m = text.match(marksRegex)
  if (m && m[0]) {
    const matchingMark = m[0]
    return {matchingMark, restText: text.slice(matchingMark.length)}
  }
  return null
}

function hasClosingMark (text: string, openingMark): boolean {
  const closingMark = openToClosePair[openingMark]
  return text.indexOf(closingMark, openingMark.length) !== -1
}

function tagMetaToElement (m: TagMeta, key) {
  const {textSoFar, elementsSoFar, componentInfo: {Component, props}} = m
  return <Component key={key} {...props}>{elementsSoFar.push(textSoFar).toArray()}</Component>
}

function _parse (text: string, tagStack: TagStack, key: number): React$Element<*> {
  // Don't recurse, use a stack
  const parseStack = [{text, tagStack, key}]
  while (parseStack.length) {
    const {text, tagStack, key} = parseStack.pop()

    if (text.length === 0 && tagStack.count() < 1) {
      throw new Error('Messed up parsing markdown text')
    }

    if (text.length === 0 && tagStack.count() === 1) {
      return tagMetaToElement(tagStack.last(), key)
    }

    const topTag = tagStack.last()

    const {openToTag, closingTag} = topTag
    const firstChar = text[0]
    const match = matchWithMark(text)
    const restText = match ? match.restText : text.slice(1)
    const matchingMark: ?string = match && match.matchingMark

    if (text.length === 0 || closingTag && closingTag === matchingMark) {
      const newElement = tagMetaToElement(topTag, key)
      parseStack.push({
        text: restText,
        tagStack: tagStack.pop().update(-1, m => ({...m, elementsSoFar: m.elementsSoFar.push(newElement)})),
        key: key + 1,
      })
    } else if (matchingMark && openToTag[matchingMark] && hasClosingMark(text, matchingMark)) {
      parseStack.push({
        text: restText,
        tagStack: tagStack
          .update(-1, m => ({...m, textSoFar: '', elementsSoFar: m.elementsSoFar.push(m.textSoFar)}))
          .push({
            componentInfo: openToTag[matchingMark],
            closingTag: openToClosePair[matchingMark],
            textSoFar: '',
            elementsSoFar: new List(),
            openToTag: openToNextOpenToTag[matchingMark] || {},
          }),
        key,
      })
    } else {
      if (firstChar === '\\' && text.length > 1) {
        parseStack.push({
          text: text.slice(2),
          tagStack: tagStack.update(-1, m => ({...m, textSoFar: m.textSoFar + text[1]})),
          key,
        })
      } else {
        parseStack.push({
          text: restText,
          tagStack: tagStack.update(-1, m => ({...m, textSoFar: m.textSoFar + firstChar})),
          key,
        })
      }
    }
  }

  throw new Error('Messed up parsing markdown text')
}

// It's a lot easier to parse emojis if we change :santa::skin-tone-3: to :santa\:\:skin-tone-3:
function preprocessEmojiColors (text: string): string {
  return text.replace(/:([\w-]*)::(skin-tone-\d):/g, ':$1\\:\\:$2:')
}

const initialTagStack = new List([initalTagMeta])

class Markdown extends PureComponent<void, Props, void> {
  render () {
    return <Text type='Body' style={this.props.style}>{_parse(preprocessEmojiColors(this.props.children || ''), initialTagStack, 0)}</Text>
  }
}

export default Markdown
<|endoftext|>


<commit_before>/*
 * INFORMATION
 * ---------------------------
 * Owner:     jquery.webspirited.com
 * Developer: Matthew Hailwood
 * ---------------------------
 */

/*
 * MODIFIED BY ENRIQUE AND ALDO. Search XXX to see where it was modified
 * Dependencies
 * JQuery $().watermark library is needed to allow input watermarks
 */

(function ($) {
    $.widget("ui.tagit", {

        // default options
        options:{
            //Maps directly to the jQuery-ui Autocomplete option
            tagSource:[],
            //What keys should trigger the completion of a tag
            triggerKeys:['enter', 'space', 'comma', 'tab'],
            //array method for setting initial tags
            initialTags:[],
            //minimum length of tags
            minLength:1,
            maxLength: 20,
            maxTags: 8,
            //should an html select be rendered to allow for normal form submission
            select:false,
            //if false only tags from `tagSource` are able to be entered
            allowNewTags:true,
            //should tag and Tag be treated as identical
            caseSensitive:false,
            //should tags be drag-and-drop sortable?
                //true: entire tag is draggable
                //'handle': a handle is rendered which is draggable
            sortable:false,
            //color to highlight text when a duplicate tag is entered
            highlightOnExistColor:'#0F0',
            //empty search on focus
            emptySearch:true,
			watermarkAllowMessage: "Add tags",
			watermarkDenyMessage: "Tags limit reached",

            //callback function for when tags are changed
            //tagValue: value of tag that was changed
            //action e.g. removed, added, sorted
            tagsChanged:function (tagValue, action, element) {
            }
        },

        _splitAt:/\ |,/g,
        _existingAtIndex:0,
        _pasteMetaKeyPressed:false,
        _keys:{
            backspace:[8],
            enter:[13],
            space:[32],
            comma:[44, 188],
            tab:[9]
        },

        _sortable:{
            sorting:-1
        },

        //initialization function
        _create:function () {
            var self = this;
            this.tagsArray = [];
            this.timer = null;

            //Always reset initial tags
            self.options.initialTags = [];

            //add class "tagit" for theming
            this.element.addClass("tagit");

            //add any initial tags added through html to the array
            this.element.children('li').each(function () {
                var tag = $(this);
                var tagValue = tag.attr('tagValue');
                self.options.initialTags.push({label:tag.text(), value:(tagValue ? tagValue : tag.text())});
            });

            //setup split according to the trigger keys
            self._splitAt = null;
            if ($.inArray('space', self.options.triggerKeys) > 0 && $.inArray('comma', self.options.triggerKeys) > 0)
                self._splitAt = /\ |,/g;
            else if ($.inArray('space', self.options.triggerKeys) > 0)
                self._splitAt = /\ /g;
            else if ($.inArray('comma', self.options.triggerKeys) > 0)
                self._splitAt = /,/g;

            //add the html input
            this.element.html('
<input class="tagit-input" type="text" />
');

            this.input = this.element.find(".tagit-input");

			//Add watermark
			if(typeof $().watermark == "function"){
				this.input.watermark(this.options.watermarkAllowMessage)
			}

            //setup click handler
            $(this.element).click(function (e) {
                if ($(e.target).hasClass('tagit-close')) {
                    // Removes a tag when the little 'x' is clicked.
                    var parent = $(e.target).parent();

                    var tag = self.tagsArray[parent.index()];
                    tag.element.remove();
                    self._popTag(tag);
                }
                else {
                    self.input.focus();
                    if (self.options.emptySearch && $(e.target).hasClass('tagit-input') && self.input.val() == '' && self.input.autocomplete != undefined) {
                        self.input.autocomplete('search');
                    }
                }
            });

            //setup autocomplete handler
            var os = this.options.select;
            this.options.appendTo = this.element;
            this.options.source = this.options.tagSource;
            this.options.select = function (event, ui) {
                self.input.data('autoCompleteTag', true);
                clearTimeout(self.timer);
                if ((ui.item.label === undefined)){
									self._addTag(ui.item.value);
								} else {
									//console.log("Autocomplete called with label and value with "+ ui.item.label + " " + ui.item.value)
									self._addTag(ui.item.label, ui.item.value);
								}

								event.stopPropagation()

                return false;
            }

            this.options.focus = function (event, ui) {
                if (ui.item.label !== undefined && /^key/.test(event.originalEvent.originalEvent.type)) {
                    self.input.val(ui.item.label);
                    self.input.attr('tagValue', ui.item.value);
                    return false;
                }
            };
            this.options.autoFocus = !this.options.allowNewTags;
            this.input.autocomplete(this.options);
            this.options.select = os;

            //setup keydown handler
            this.input.keydown(function (e) {
                var lastLi = self.element.children(".tagit-choice:last");
                if (e.which == self._keys.backspace)
                    return self._backspace(lastLi);

                if (self._isInitKey(e.which) && !(self._isTabKey(e.which) && this.value == '' && !self.input.data('autoCompleteTag'))) {
                    e.preventDefault();
                    self.input.data('autoCompleteTag', false);

                    if (self.options.allowNewTags && $(this).val().length >= self.options.minLength) {
											  //console.log("Keydown event, call addtag with " + $(this).val())
                        self._addTag($(this).val());
                    }
                }

                if (self.options.maxLength !== undefined && self.input.val().length == self.options.maxLength) {
                    e.preventDefault();
                }

                if (lastLi.hasClass('selected'))
                    lastLi.removeClass('selected');

                _pasteMetaKeyPressed = e.metaKey;
                self.lastKey = e.which;
            });

            this.input.keyup(function (e) {

                if (_pasteMetaKeyPressed && (e.which == 91 || e.which == 86))
                    $(this).blur();

                // timeout for the fast copy pasters
                window.setTimeout(function () {
                    _pasteMetaKeyPressed = e.metaKey;
                }, 250);
            });

            //setup blur handler
            this.input.blur(function (e) {
                self.currentLabel = $(this).val();
                self.currentValue = $(this).attr('tagValue');
                if ((self.options.allowNewTags)&&(self.currentLabel)) {
                    //XXX commented by ENRIQUE to add the last tag to the tags
                    //when clicking "update", if you add it 400 milisecs after it is not sent
                    //self.timer = setTimeout(function () {
                        self._addTag(self.currentLabel, self.currentValue);
                        self.currentValue = '';
                        self.currentLabel = '';
                    //}, 400);
                }
                $(this).val('').removeAttr('tagValue');
                return true;
            });

            //define missing trim function for strings
            if (!String.prototype.trim) {
                String.prototype.trim = function () {
                    return this.replace(/^\s+|\s+$/g, '');
                };
            }

            if (this.options.select) {
                this.select = $('<select class="tagit-hiddenSelect" name="' + this.element.attr('name') + '" multiple="multiple"></select>');
                this.element.after(this.select);
            }
            this._initialTags();

            //setup sortable handler
            if (self.options.sortable !== false) {

                var soptions = {
                    items:'.tagit-choice',
                    containment:'parent',
                    opacity: 0.6,
                    tolerance: 'pointer',
                    start:function (event, ui) {
                        self._sortable.tag = $(ui.item);
                        self._sortable.origIndex = self._sortable.tag.index();
                    },
                    update:function (event, ui) {
                        self._sortable.newIndex = self._sortable.tag.index();
                        self._moveTag(self._sortable.origIndex, self._sortable.newIndex);
                        if(self.options.tagsChanged){
                            var tag = self.tagsArray[self._sortable.newIndex];
                            self.options.tagsChanged(tag.value, 'moved', tag.element);
                        }
                    }
                };

                if (self.options.sortable == 'handle') {
                    soptions.handle = 'a.ui-icon';
                    soptions.cursor = 'move';
                }

                self.element.sortable(soptions);
            }

        },

        _popSelect:function (tag) {
            $('option:eq(' + tag.index + ')', this.select).remove();
            this.select.change();
        },

        _addSelect:function (tag) {
            this.select.append('<option selected="selected" value="' + tag.value + '">' + tag.label + '</option>');
            this.select.change();
        },

        _popTag:function (tag) {

            //are we removing the last tag or a specific tag?
            if (tag === undefined){
							tag = this.tagsArray.pop();
						} else {
							this.tagsArray.splice(tag.index, 1);
						}

						//Restart watermark
            if(typeof $().watermark == "function"){
              this.input.watermark(this.options.watermarkAllowMessage)
            }

            //maintain the indexes
            for (var ind in this.tagsArray)
                this.tagsArray[ind].index = ind;

            if (this.options.select)
                this._popSelect(tag);
            if (this.options.tagsChanged)
                this.options.tagsChanged(tag.value || tag.label, 'popped', null);
            return;
        },

        _addTag:function (label, value) {

            if(this.tagsArray.length > (this.options.maxTags-1)){
							$(this.input).blur();
              return false;
            }

            this.input.autocomplete('close').val("");

            //are we trying to add a tag that should be split?
            if (this._splitAt && label.search(this._splitAt) > 0) {
                var result = label.split(this._splitAt);
                for (var i = 0; i < result.length; i++)
                    this._addTag(result[i], value);
                return;
            }

            label = label.replace(/,+$/, "").trim();

            if (label == "")
                return false;

            var tagExists = this._exists(label, value);
            if (tagExists !== false) {
                this._highlightExisting(tagExists);
                return false;
            }

						if(this.tagsArray.length == (this.options.maxTags-1)){
              //Last tag (or try to add more tags than maxTags)
              if(typeof $().watermark == "function"){
                this.input.watermark(this.options.watermarkDenyMessage)
              }
              $(this.input).blur();
            }

            var tag = this.tag(label, value);
            tag.element = $('
'
                + (this.options.sortable == 'handle' ? '' : '')
                + label + '
');
            tag.element.insertBefore(this.input.parent());
            this.tagsArray.push(tag);

            this.input.val("");

            if (this.options.select){
							this._addSelect(tag);
						}

            if (this.options.tagsChanged){
							this.options.tagsChanged(tag.label, 'added', tag.element);
						}

            return true;
        },

		_removeTagWithValue:function (label,value) {
            //var index = $(".tagit-choice[tagvalue='" + value + "']").index()
            var index = this._exists(label, value);
            if(index!=false){
                var tag = this.tagsArray[index];
                tag.element.remove();
                this._popTag(tag);
            }
        },

        _exists:function (label, value) {
            if (this.tagsArray.length == 0)
                return false;

            label = this._lowerIfCaseInsensitive(label);
            value = this._lowerIfCaseInsensitive(value);

            for (var ind in this.tagsArray) {
                if (this._lowerIfCaseInsensitive(this.tagsArray[ind].label) == label) {
                    if (value !== undefined) {
                        if (this._lowerIfCaseInsensitive(this.tagsArray[ind].value) == value)
                            return ind;
                    } else {
                        return ind;
                    }
                }
            }

            return false;
        },

        _highlightExisting:function (index) {
            if (this.options.highlightOnExistColor === undefined)
                return;
            var tag = this.tagsArray[index];
            tag.element.stop();

            var initialColor = tag.element.css('color');
            tag.element.animate({color:this.options.highlightOnExistColor}, 100).animate({'color':initialColor}, 800);
        },

        _isInitKey:function (keyCode) {
            var keyName = "";
            for (var key in this._keys)
                if ($.inArray(keyCode, this._keys[key]) != -1)
                    keyName = key;

            if ($.inArray(keyName, this.options.triggerKeys) != -1)
                return true;
            return false;
        },

        _isTabKey:function (keyCode) {
          var tabKeys = this._keys['tab'];
          return $.inArray(keyCode, tabKeys) > -1;
        },

        _removeTag:function () {
            this._popTag();
            this.element.children(".tagit-choice:last").remove();
        },

        _backspace:function (li) {
            if (this.input.val() == "") {
                // When backspace is pressed, the last tag is deleted.
                if (this.lastKey == this._keys.backspace) {
                    this._popTag();
                    li.remove();
                    this.lastKey = null;
                } else {
                    li.addClass('selected');
                    this.lastKey = this._keys.backspace;
                }
            }
            return true;
        },

        _initialTags:function () {
            var input = this;
            var _temp;
            if (this.options.tagsChanged)
                _temp = this.options.tagsChanged;
            this.options.tagsChanged = null;

            if (this.options.initialTags.length != 0) {
                $(this.options.initialTags).each(function (i, element) {
                    if ((typeof(element) == "object")) {
											input._addTag(element.label, element.value);
										} else {
											input._addTag(element);
										}
                });
            }
            this.options.tagsChanged = _temp;
        },

        _lowerIfCaseInsensitive:function (inp) {

            if (inp === undefined || typeof(inp) != typeof("a"))
                return inp;

            if (this.options.caseSensitive)
                return inp;

            return inp.toLowerCase();

        },

        _moveTag: function (old_index, new_index) {
            this.tagsArray.splice(new_index, 0, this.tagsArray.splice(old_index, 1)[0]);
            for (var ind in this.tagsArray)
                this.tagsArray[ind].index = ind;

            if(this.options.select){
                $('option:eq(' + old_index + ')', this.select).insertBefore($('option:eq(' + new_index + ')', this.select));
            }
        },
        tags:function () {
            return this.tagsArray;
        },

        destroy:function () {
            $.Widget.prototype.destroy.apply(this, arguments); // default destroy
            this.tagsArray = [];
        },

        reset:function () {
            this.element.find(".tagit-choice").remove();
            this.tagsArray = [];
            if (this.options.select) {
                this.select.children().remove();
                this.select.change();
            }
            this._initialTags();
            if (this.options.tagsChanged){
							this.options.tagsChanged(null, 'reset', null);
						}
            if(typeof $().watermark == "function"){
              this.input.watermark(this.options.watermarkAllowMessage)
            }
        },

        fill:function (tags) {

            if (tags !== undefined)
                this.options.initialTags = tags;
            this.reset();
        },

        add:function (label, value) {
            if(typeof(label) == "object")
                return this._addTag({label: label, value: value});
            else
                return this._addTag(label, value);
        },

		remove:function(value){
			this._removeTagWithValue(value,value);
		},

        tag:function (label, value, element) {
            var self = this;
            return {
                label:label,
                value:(value === undefined ? label : value),
                element:element,
                index:self.tagsArray.length
            }
        }

    });
})(jQuery);
<commit_msg>Fix Tagit lines for closing<commit_after>/*
 * INFORMATION
 * ---------------------------
 * Owner:     jquery.webspirited.com
 * Developer: Matthew Hailwood
 * ---------------------------
 */

/*
 * MODIFIED BY ENRIQUE AND ALDO. Search XXX to see where it was modified
 * Dependencies
 * JQuery $().watermark library is needed to allow input watermarks
 */

(function ($) {
    $.widget("ui.tagit", {

        // default options
        options:{
            //Maps directly to the jQuery-ui Autocomplete option
            tagSource:[],
            //What keys should trigger the completion of a tag
            triggerKeys:['enter', 'space', 'comma', 'tab'],
            //array method for setting initial tags
            initialTags:[],
            //minimum length of tags
            minLength:1,
            maxLength: 20,
            maxTags: 8,
            //should an html select be rendered to allow for normal form submission
            select:false,
            //if false only tags from `tagSource` are able to be entered
            allowNewTags:true,
            //should tag and Tag be treated as identical
            caseSensitive:false,
            //should tags be drag-and-drop sortable?
                //true: entire tag is draggable
                //'handle': a handle is rendered which is draggable
            sortable:false,
            //color to highlight text when a duplicate tag is entered
            highlightOnExistColor:'#0F0',
            //empty search on focus
            emptySearch:true,
			watermarkAllowMessage: "Add tags",
			watermarkDenyMessage: "Tags limit reached",

            //callback function for when tags are changed
            //tagValue: value of tag that was changed
            //action e.g. removed, added, sorted
            tagsChanged:function (tagValue, action, element) {
            }
        },

        _splitAt:/\ |,/g,
        _existingAtIndex:0,
        _pasteMetaKeyPressed:false,
        _keys:{
            backspace:[8],
            enter:[13],
            space:[32],
            comma:[44, 188],
            tab:[9]
        },

        _sortable:{
            sorting:-1
        },

        //initialization function
        _create:function () {
            var self = this;
            this.tagsArray = [];
            this.timer = null;

            //Always reset initial tags
            self.options.initialTags = [];

            //add class "tagit" for theming
            this.element.addClass("tagit");

            //add any initial tags added through html to the array
            this.element.children('li').each(function () {
                var tag = $(this);
                var tagValue = tag.attr('tagValue');
                self.options.initialTags.push({label:tag.text(), value:(tagValue ? tagValue : tag.text())});
            });

            //setup split according to the trigger keys
            self._splitAt = null;
            if ($.inArray('space', self.options.triggerKeys) > 0 && $.inArray('comma', self.options.triggerKeys) > 0)
                self._splitAt = /\ |,/g;
            else if ($.inArray('space', self.options.triggerKeys) > 0)
                self._splitAt = /\ /g;
            else if ($.inArray('comma', self.options.triggerKeys) > 0)
                self._splitAt = /,/g;

            //add the html input
            this.element.html('
<input class="tagit-input" type="text" />
');

            this.input = this.element.find(".tagit-input");

			//Add watermark
			if(typeof $().watermark == "function"){
				this.input.watermark(this.options.watermarkAllowMessage)
			}

            //setup click handler
            $(this.element).click(function (e) {
                if ($(e.target).hasClass('tagit-close')) {
                    // Removes a tag when the little 'x' is clicked.
                    var parent = $(e.target).parent();

                    var tag = self.tagsArray[parent.index()];
                    tag.element.remove();
                    self._popTag(tag);
                }
                else {
                    self.input.focus();
                    if (self.options.emptySearch && $(e.target).hasClass('tagit-input') && self.input.val() == '' && self.input.autocomplete != undefined) {
                        self.input.autocomplete('search');
                    }
                }
            });

            //setup autocomplete handler
            var os = this.options.select;
            this.options.appendTo = this.element;
            this.options.source = this.options.tagSource;
            this.options.select = function (event, ui) {
                self.input.data('autoCompleteTag', true);
                clearTimeout(self.timer);
                if ((ui.item.label === undefined)){
									self._addTag(ui.item.value);
								} else {
									//console.log("Autocomplete called with label and value with "+ ui.item.label + " " + ui.item.value)
									self._addTag(ui.item.label, ui.item.value);
								}

								event.stopPropagation()

                return false;
            }

            this.options.focus = function (event, ui) {
                if (ui.item.label !== undefined && /^key/.test(event.originalEvent.originalEvent.type)) {
                    self.input.val(ui.item.label);
                    self.input.attr('tagValue', ui.item.value);
                    return false;
                }
            };
            this.options.autoFocus = !this.options.allowNewTags;
            this.input.autocomplete(this.options);
            this.options.select = os;

            //setup keydown handler
            this.input.keydown(function (e) {
                var lastLi = self.element.children(".tagit-choice:last");
                if (e.which == self._keys.backspace)
                    return self._backspace(lastLi);

                if (self._isInitKey(e.which) && !(self._isTabKey(e.which) && this.value == '' && !self.input.data('autoCompleteTag'))) {
                    e.preventDefault();
                    self.input.data('autoCompleteTag', false);

                    if (self.options.allowNewTags && $(this).val().length >= self.options.minLength) {
											  //console.log("Keydown event, call addtag with " + $(this).val())
                        self._addTag($(this).val());
                    }
                }

                if (self.options.maxLength !== undefined && self.input.val().length == self.options.maxLength) {
                    e.preventDefault();
                }

                if (lastLi.hasClass('selected'))
                    lastLi.removeClass('selected');

                _pasteMetaKeyPressed = e.metaKey;
                self.lastKey = e.which;
            });

            this.input.keyup(function (e) {

                if (_pasteMetaKeyPressed && (e.which == 91 || e.which == 86))
                    $(this).blur();

                // timeout for the fast copy pasters
                window.setTimeout(function () {
                    _pasteMetaKeyPressed = e.metaKey;
                }, 250);
            });

            //setup blur handler
            this.input.blur(function (e) {
                self.currentLabel = $(this).val();
                self.currentValue = $(this).attr('tagValue');
                if ((self.options.allowNewTags)&&(self.currentLabel)) {
                    //XXX commented by ENRIQUE to add the last tag to the tags
                    //when clicking "update", if you add it 400 milisecs after it is not sent
                    //self.timer = setTimeout(function () {
                        self._addTag(self.currentLabel, self.currentValue);
                        self.currentValue = '';
                        self.currentLabel = '';
                    //}, 400);
                }
                $(this).val('').removeAttr('tagValue');
                return true;
            });

            //define missing trim function for strings
            if (!String.prototype.trim) {
                String.prototype.trim = function () {
                    return this.replace(/^\s+|\s+$/g, '');
                };
            }

            if (this.options.select) {
                this.select = $('<select class="tagit-hiddenSelect" name="' + this.element.attr('name') + '" multiple="multiple"></select>');
                this.element.after(this.select);
            }
            this._initialTags();

            //setup sortable handler
            if (self.options.sortable !== false) {

                var soptions = {
                    items:'.tagit-choice',
                    containment:'parent',
                    opacity: 0.6,
                    tolerance: 'pointer',
                    start:function (event, ui) {
                        self._sortable.tag = $(ui.item);
                        self._sortable.origIndex = self._sortable.tag.index();
                    },
                    update:function (event, ui) {
                        self._sortable.newIndex = self._sortable.tag.index();
                        self._moveTag(self._sortable.origIndex, self._sortable.newIndex);
                        if(self.options.tagsChanged){
                            var tag = self.tagsArray[self._sortable.newIndex];
                            self.options.tagsChanged(tag.value, 'moved', tag.element);
                        }
                    }
                };

                if (self.options.sortable == 'handle') {
                    soptions.handle = 'a.ui-icon';
                    soptions.cursor = 'move';
                }

                self.element.sortable(soptions);
            }

        },

        _popSelect:function (tag) {
            $('option:eq(' + tag.index + ')', this.select).remove();
            this.select.change();
        },

        _addSelect:function (tag) {
            this.select.append('<option selected="selected" value="' + tag.value + '">' + tag.label + '</option>');
            this.select.change();
        },

        _popTag:function (tag) {

            //are we removing the last tag or a specific tag?
            if (tag === undefined){
							tag = this.tagsArray.pop();
						} else {
							this.tagsArray.splice(tag.index, 1);
						}

						//Restart watermark
            if(typeof $().watermark == "function"){
              this.input.watermark(this.options.watermarkAllowMessage)
            }

            //maintain the indexes
            for (var ind in this.tagsArray)
                this.tagsArray[ind].index = ind;

            if (this.options.select)
                this._popSelect(tag);
            if (this.options.tagsChanged)
                this.options.tagsChanged(tag.value || tag.label, 'popped', null);
            return;
        },

        _addTag:function (label, value) {

            if(this.tagsArray.length > (this.options.maxTags-1)){
							$(this.input).blur();
              return false;
            }

            this.input.autocomplete('close').val("");

            //are we trying to add a tag that should be split?
            if (this._splitAt && label.search(this._splitAt) > 0) {
                var result = label.split(this._splitAt);
                for (var i = 0; i < result.length; i++)
                    this._addTag(result[i], value);
                return;
            }

            label = label.replace(/,+$/, "").trim();

            if (label == "")
                return false;

            var tagExists = this._exists(label, value);
            if (tagExists !== false) {
                this._highlightExisting(tagExists);
                return false;
            }

						if(this.tagsArray.length == (this.options.maxTags-1)){
              //Last tag (or try to add more tags than maxTags)
              if(typeof $().watermark == "function"){
                this.input.watermark(this.options.watermarkDenyMessage)
              }
              $(this.input).blur();
            }

            var tag = this.tag(label, value);
            tag.element = $('
'
                + (this.options.sortable == 'handle' ? '' : '')
                + label + '
');
            tag.element.insertBefore(this.input.parent());
            this.tagsArray.push(tag);

            this.input.val("");

            if (this.options.select){
							this._addSelect(tag);
						}

            if (this.options.tagsChanged){
							this.options.tagsChanged(tag.label, 'added', tag.element);
						}

            return true;
        },

		_removeTagWithValue:function (label,value) {
            //var index = $(".tagit-choice[tagvalue='" + value + "']").index()
            var index = this._exists(label, value);
            if(index!=false){
                var tag = this.tagsArray[index];
                tag.element.remove();
                this._popTag(tag);
            }
        },

        _exists:function (label, value) {
            if (this.tagsArray.length == 0)
                return false;

            label = this._lowerIfCaseInsensitive(label);
            value = this._lowerIfCaseInsensitive(value);

            for (var ind in this.tagsArray) {
                if (this._lowerIfCaseInsensitive(this.tagsArray[ind].label) == label) {
                    if (value !== undefined) {
                        if (this._lowerIfCaseInsensitive(this.tagsArray[ind].value) == value)
                            return ind;
                    } else {
                        return ind;
                    }
                }
            }

            return false;
        },

        _highlightExisting:function (index) {
            if (this.options.highlightOnExistColor === undefined)
                return;
            var tag = this.tagsArray[index];
            tag.element.stop();

            var initialColor = tag.element.css('color');
            tag.element.animate({color:this.options.highlightOnExistColor}, 100).animate({'color':initialColor}, 800);
        },

        _isInitKey:function (keyCode) {
            var keyName = "";
            for (var key in this._keys)
                if ($.inArray(keyCode, this._keys[key]) != -1)
                    keyName = key;

            if ($.inArray(keyName, this.options.triggerKeys) != -1)
                return true;
            return false;
        },

        _isTabKey:function (keyCode) {
          var tabKeys = this._keys['tab'];
          return $.inArray(keyCode, tabKeys) > -1;
        },

        _removeTag:function () {
            this._popTag();
            this.element.children(".tagit-choice:last").remove();
        },

        _backspace:function (li) {
            if (this.input.val() == "") {
                // When backspace is pressed, the last tag is deleted.
                if (this.lastKey == this._keys.backspace) {
                    this._popTag();
                    li.remove();
                    this.lastKey = null;
                } else {
                    li.addClass('selected');
                    this.lastKey = this._keys.backspace;
                }
            }
            return true;
        },

        _initialTags:function () {
            var input = this;
            var _temp;
            if (this.options.tagsChanged)
                _temp = this.options.tagsChanged;
            this.options.tagsChanged = null;

            if (this.options.initialTags.length != 0) {
                $(this.options.initialTags).each(function (i, element) {
                    if ((typeof(element) == "object")) {
											input._addTag(element.label, element.value);
										} else {
											input._addTag(element);
										}
                });
            }
            this.options.tagsChanged = _temp;
        },

        _lowerIfCaseInsensitive:function (inp) {

            if (inp === undefined || typeof(inp) != typeof("a"))
                return inp;

            if (this.options.caseSensitive)
                return inp;

            return inp.toLowerCase();

        },

        _moveTag: function (old_index, new_index) {
            this.tagsArray.splice(new_index, 0, this.tagsArray.splice(old_index, 1)[0]);
            for (var ind in this.tagsArray)
                this.tagsArray[ind].index = ind;

            if(this.options.select){
                $('option:eq(' + old_index + ')', this.select).insertBefore($('option:eq(' + new_index + ')', this.select));
            }
        },
        tags:function () {
            return this.tagsArray;
        },

        destroy:function () {
            $.Widget.prototype.destroy.apply(this, arguments); // default destroy
            this.tagsArray = [];
        },

        reset:function () {
            this.element.find(".tagit-choice").remove();
            this.tagsArray = [];
            if (this.options.select) {
                this.select.children().remove();
                this.select.change();
            }
            this._initialTags();
            if (this.options.tagsChanged){
							this.options.tagsChanged(null, 'reset', null);
						}
            if(typeof $().watermark == "function"){
              this.input.watermark(this.options.watermarkAllowMessage)
            }
        },

        fill:function (tags) {

            if (tags !== undefined)
                this.options.initialTags = tags;
            this.reset();
        },

        add:function (label, value) {
            if(typeof(label) == "object")
                return this._addTag({label: label, value: value});
            else
                return this._addTag(label, value);
        },

		remove:function(value){
			this._removeTagWithValue(value,value);
		},

        tag:function (label, value, element) {
            var self = this;
            return {
                label:label,
                value:(value === undefined ? label : value),
                element:element,
                index:self.tagsArray.length
            }
        }

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


<commit_before>const _ = require('lodash');
const fs = require('fs');
const path = require('path');
const j = require('@feathersjs/tools').transform;
const Generator = require('../../lib/generator');

const templatePath = path.join(__dirname, 'templates');
const stripSlashes = name => name.replace(/^(\/*)|(\/*)$/g, '');

module.exports = class ServiceGenerator extends Generator {
  prompting() {
    this.checkPackage();

    const { props } = this;
    const prompts = [
      {
        type: 'list',
        name: 'adapter',
        message: 'What kind of service is it?',
        default: 'nedb',
        choices: [
          {
            name: 'A custom service',
            value: 'generic'
          }, {
            name: 'In Memory',
            value: 'memory'
          }, {
            name: 'NeDB',
            value: 'nedb'
          }, {
            name: 'MongoDB',
            value: 'mongodb'
          }, {
            name: 'Mongoose',
            value: 'mongoose'
          }, {
            name: 'Sequelize',
            value: 'sequelize'
          }, {
            name: 'KnexJS',
            value: 'knex'
          }, {
            name: 'RethinkDB',
            value: 'rethinkdb'
          }
        ]
      }, {
        name: 'name',
        message: 'What is the name of the service?',
        validate(input) {
          if(input.trim() === '') {
            return 'Service name can not be empty';
          }

          if(input.trim() === 'authentication') {
            return '`authentication` is a reserved service name.';
          }

          return true;
        },
        when: !props.name
      }, {
        name: 'path',
        message: 'Which path should the service be registered on?',
        when: !props.path,
        default(answers) {
          return `/${_.kebabCase(answers.name || props.name)}`;
        },
        validate(input) {
          if(input.trim() === '') {
            return 'Service path can not be empty';
          }

          return true;
        }
      }, {
        name: 'requiresAuth',
        message: 'Does the service require authentication?',
        type: 'confirm',
        default: false,
        when: !!(this.defaultConfig.authentication && !props.authentication)
      }
    ];

    return this.prompt(prompts).then(answers => {
      const name = answers.name || props.name;

      this.props = Object.assign({
        requiresAuth: false
      }, props, answers, {
        snakeName: _.snakeCase(name),
        kebabName: _.kebabCase(name),
        camelName: _.camelCase(name)
      });
    });
  }

  _transformCode(code) {
    const { camelName, kebabName } = this.props;
    const ast = j(code);
    const mainExpression = ast.find(j.FunctionExpression)
      .closest(j.ExpressionStatement);

    if(mainExpression.length !== 1) {
      throw new Error(`${this.libDirectory}/services/index.js seems to have more than one function declaration and we can not register the new service. Did you modify it?`);
    }

    const serviceRequire = `const ${camelName} = require('./${kebabName}/${kebabName}.service.js');`;
    const serviceCode = `app.configure(${camelName});`;

    // Add require('./service')
    mainExpression.insertBefore(serviceRequire);
    // Add app.configure(service) to service/index.js
    mainExpression.insertLastInFunction(serviceCode);

    return ast.toSource();
  }

  writing() {
    const { adapter, kebabName } = this.props;
    const moduleMappings = {
      generic: `./${kebabName}.class.js`,
      memory: 'feathers-memory',
      nedb: 'feathers-nedb',
      mongodb: 'feathers-mongodb',
      mongoose: 'feathers-mongoose',
      sequelize: 'feathers-sequelize',
      knex: 'feathers-knex',
      rethinkdb: 'feathers-rethinkdb'
    };
    const serviceModule = moduleMappings[adapter];
    const mainFile = this.destinationPath(this.libDirectory, 'services', kebabName, `${kebabName}.service.js`);
    const modelTpl = `${adapter}${this.props.authentication ? '-user' : ''}.js`;
    const hasModel = fs.existsSync(path.join(templatePath, 'model', modelTpl));
    const context = Object.assign({}, this.props, {
      libDirectory: this.libDirectory,
      modelName: hasModel ? `${kebabName}.model` : null,
      path: stripSlashes(this.props.path),
      serviceModule
    });

    // Do not run code transformations if the service file already exists
    if (!this.fs.exists(mainFile)) {
      const servicejs = this.destinationPath(this.libDirectory, 'services', 'index.js');
      const transformed = this._transformCode(
        this.fs.read(servicejs).toString()
      );

      this.conflicter.force = true;
      this.fs.write(servicejs, transformed);
    }

    // Run the `connection` generator for the selected database
    // It will not do anything if the db has been set up already
    if (adapter !== 'generic' && adapter !== 'memory') {
      this.composeWith(require.resolve('../connection'), {
        props: {
          adapter,
          service: this.props.name
        }
      });
    } else if(adapter === 'generic') {
      // Copy the generic service class
      this.fs.copyTpl(
        this.templatePath(this.hasAsync ? 'class-async.js' : 'class.js'),
        this.destinationPath(this.libDirectory, 'services', kebabName, `${kebabName}.class.js`),
        context
      );
    }

    if (context.modelName) {
      // Copy the model
      this.fs.copyTpl(
        this.templatePath('model', modelTpl),
        this.destinationPath(this.libDirectory, 'models', `${context.modelName}.js`),
        context
      );
    }

    this.fs.copyTpl(
      this.templatePath(`hooks${this.props.authentication ? '-user' : ''}.js`),
      this.destinationPath(this.libDirectory, 'services', kebabName, `${kebabName}.hooks.js`),
      context
    );

    if (fs.existsSync(path.join(templatePath, 'types', `${adapter}.js`))) {
      this.fs.copyTpl(
        this.templatePath('types', `${adapter}.js`),
        mainFile,
        context
      );
    } else {
      this.fs.copyTpl(
        this.templatePath('service.js'),
        mainFile,
        context
      );
    }

    this.fs.copyTpl(
      this.templatePath('test.js'),
      this.destinationPath(this.testDirectory, 'services', `${kebabName}.test.js`),
      context
    );

    if (serviceModule.charAt(0) !== '.') {
      this._packagerInstall([ serviceModule ], { save: true });
    }
  }
};
<commit_msg>Do not fail when the services index has been modified (#331)<commit_after>const _ = require('lodash');
const fs = require('fs');
const path = require('path');
const j = require('@feathersjs/tools').transform;
const Generator = require('../../lib/generator');

const templatePath = path.join(__dirname, 'templates');
const stripSlashes = name => name.replace(/^(\/*)|(\/*)$/g, '');

module.exports = class ServiceGenerator extends Generator {
  prompting() {
    this.checkPackage();

    const { props } = this;
    const prompts = [
      {
        type: 'list',
        name: 'adapter',
        message: 'What kind of service is it?',
        default: 'nedb',
        choices: [
          {
            name: 'A custom service',
            value: 'generic'
          }, {
            name: 'In Memory',
            value: 'memory'
          }, {
            name: 'NeDB',
            value: 'nedb'
          }, {
            name: 'MongoDB',
            value: 'mongodb'
          }, {
            name: 'Mongoose',
            value: 'mongoose'
          }, {
            name: 'Sequelize',
            value: 'sequelize'
          }, {
            name: 'KnexJS',
            value: 'knex'
          }, {
            name: 'RethinkDB',
            value: 'rethinkdb'
          }
        ]
      }, {
        name: 'name',
        message: 'What is the name of the service?',
        validate(input) {
          if(input.trim() === '') {
            return 'Service name can not be empty';
          }

          if(input.trim() === 'authentication') {
            return '`authentication` is a reserved service name.';
          }

          return true;
        },
        when: !props.name
      }, {
        name: 'path',
        message: 'Which path should the service be registered on?',
        when: !props.path,
        default(answers) {
          return `/${_.kebabCase(answers.name || props.name)}`;
        },
        validate(input) {
          if(input.trim() === '') {
            return 'Service path can not be empty';
          }

          return true;
        }
      }, {
        name: 'requiresAuth',
        message: 'Does the service require authentication?',
        type: 'confirm',
        default: false,
        when: !!(this.defaultConfig.authentication && !props.authentication)
      }
    ];

    return this.prompt(prompts).then(answers => {
      const name = answers.name || props.name;

      this.props = Object.assign({
        requiresAuth: false
      }, props, answers, {
        snakeName: _.snakeCase(name),
        kebabName: _.kebabCase(name),
        camelName: _.camelCase(name)
      });
    });
  }

  _transformCode(code) {
    const { camelName, kebabName } = this.props;
    const ast = j(code);
    const mainExpression = ast.find(j.FunctionExpression)
      .closest(j.ExpressionStatement);
    const serviceRequire = `const ${camelName} = require('./${kebabName}/${kebabName}.service.js');`;
    const serviceCode = `app.configure(${camelName});`;

    if(mainExpression.length !== 1) {
      this.log
        .writeln()
        .conflict(`${this.libDirectory}/services/index.js seems to have more than one function declaration and we can not register the new service. Did you modify it?`)
        .info('You will need to add the next lines manually to the file')
        .info(serviceRequire)
        .info(serviceCode)
        .writeln();
    } else {
      // Add require('./service')
      mainExpression.insertBefore(serviceRequire);
      // Add app.configure(service) to service/index.js
      mainExpression.insertLastInFunction(serviceCode);
    }

    return ast.toSource();
  }

  writing() {
    const { adapter, kebabName } = this.props;
    const moduleMappings = {
      generic: `./${kebabName}.class.js`,
      memory: 'feathers-memory',
      nedb: 'feathers-nedb',
      mongodb: 'feathers-mongodb',
      mongoose: 'feathers-mongoose',
      sequelize: 'feathers-sequelize',
      knex: 'feathers-knex',
      rethinkdb: 'feathers-rethinkdb'
    };
    const serviceModule = moduleMappings[adapter];
    const mainFile = this.destinationPath(this.libDirectory, 'services', kebabName, `${kebabName}.service.js`);
    const modelTpl = `${adapter}${this.props.authentication ? '-user' : ''}.js`;
    const hasModel = fs.existsSync(path.join(templatePath, 'model', modelTpl));
    const context = Object.assign({}, this.props, {
      libDirectory: this.libDirectory,
      modelName: hasModel ? `${kebabName}.model` : null,
      path: stripSlashes(this.props.path),
      serviceModule
    });

    // Do not run code transformations if the service file already exists
    if (!this.fs.exists(mainFile)) {
      const servicejs = this.destinationPath(this.libDirectory, 'services', 'index.js');
      const transformed = this._transformCode(
        this.fs.read(servicejs).toString()
      );

      this.conflicter.force = true;
      this.fs.write(servicejs, transformed);
    }

    // Run the `connection` generator for the selected database
    // It will not do anything if the db has been set up already
    if (adapter !== 'generic' && adapter !== 'memory') {
      this.composeWith(require.resolve('../connection'), {
        props: {
          adapter,
          service: this.props.name
        }
      });
    } else if(adapter === 'generic') {
      // Copy the generic service class
      this.fs.copyTpl(
        this.templatePath(this.hasAsync ? 'class-async.js' : 'class.js'),
        this.destinationPath(this.libDirectory, 'services', kebabName, `${kebabName}.class.js`),
        context
      );
    }

    if (context.modelName) {
      // Copy the model
      this.fs.copyTpl(
        this.templatePath('model', modelTpl),
        this.destinationPath(this.libDirectory, 'models', `${context.modelName}.js`),
        context
      );
    }

    this.fs.copyTpl(
      this.templatePath(`hooks${this.props.authentication ? '-user' : ''}.js`),
      this.destinationPath(this.libDirectory, 'services', kebabName, `${kebabName}.hooks.js`),
      context
    );

    if (fs.existsSync(path.join(templatePath, 'types', `${adapter}.js`))) {
      this.fs.copyTpl(
        this.templatePath('types', `${adapter}.js`),
        mainFile,
        context
      );
    } else {
      this.fs.copyTpl(
        this.templatePath('service.js'),
        mainFile,
        context
      );
    }

    this.fs.copyTpl(
      this.templatePath('test.js'),
      this.destinationPath(this.testDirectory, 'services', `${kebabName}.test.js`),
      context
    );

    if (serviceModule.charAt(0) !== '.') {
      this._packagerInstall([ serviceModule ], { save: true });
    }
  }
};
<|endoftext|>


<commit_before>import { FETCH_FEATURED_COLLECTIONS_FOR_QUERY, MEDIA_PICKER_TOGGLE_MEDIA_IN_LIST } from '../../../actions/systemActions';
import { createAsyncReducer } from '../../../lib/reduxHelpers';

const featured = createAsyncReducer({
  action: FETCH_FEATURED_COLLECTIONS_FOR_QUERY,
  handleSuccess: (payload, state, meta) => ({
    args: Object.assign({}, meta.args[0], { selected: false }), // for adding/removing from selected list
    list: payload.results.map(c => ({
      ...c,
      name: `${c.tag_set_label}: ${c.label || c.tag}`,
      id: c.tags_id,
      type: 'collection',
      selected: false, // for adding/removing from selected list
    })),
  }),
  [MEDIA_PICKER_TOGGLE_MEDIA_IN_LIST]: (payload, state) => ({
    list: state.list ? state.list.map((c) => {
      if (c.id === payload.id) {
        return ({
          ...c,
          selected: !c.selected,
        });
      }
      return c;
    }) : null,
    timestamp: Date.now().toString(),
  }),
});
export default featured;
<commit_msg>#612 remove prepended tag_set_label for featured as well<commit_after>import { FETCH_FEATURED_COLLECTIONS_FOR_QUERY, MEDIA_PICKER_TOGGLE_MEDIA_IN_LIST } from '../../../actions/systemActions';
import { createAsyncReducer } from '../../../lib/reduxHelpers';

const featured = createAsyncReducer({
  action: FETCH_FEATURED_COLLECTIONS_FOR_QUERY,
  handleSuccess: (payload, state, meta) => ({
    args: Object.assign({}, meta.args[0], { selected: false }), // for adding/removing from selected list
    list: payload.results.map(c => ({
      ...c,
      name: `${c.label || c.tag}`,
      id: c.tags_id,
      type: 'collection',
      selected: false, // for adding/removing from selected list
    })),
  }),
  [MEDIA_PICKER_TOGGLE_MEDIA_IN_LIST]: (payload, state) => ({
    list: state.list ? state.list.map((c) => {
      if (c.id === payload.id) {
        return ({
          ...c,
          selected: !c.selected,
        });
      }
      return c;
    }) : null,
    timestamp: Date.now().toString(),
  }),
});
export default featured;
<|endoftext|>