<commit_before>/**
 * @file Thead component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import Th from '../_Th';

import HorizontalAlign from '../_statics/HorizontalAlign';
import SelectMode from '../_statics/SelectMode';
import SelectAllMode from '../_statics/SelectAllMode';
import SortingType from '../_statics/SortingType';

import Util from '../_vendors/Util';
import TableFragment from '../_statics/TableFragment';
import TableCalculation from '../_vendors/TableCalculation';

class Thead extends Component {

    static Align = HorizontalAlign;
    static SelectMode = SelectMode;
    static SelectAllMode = SelectAllMode;
    static SortingType = SortingType;

    constructor(props, ...restArgs) {
        super(props, ...restArgs);
    }

    render() {

        const {
                className, style, columns, baseColIndex,
                sorting, defaultSortingType, sortingAscIconCls, sortingDescIconCls, onSortChange,
                ...restProps
            } = this.props,
            columnsWithSpan = TableCalculation.getColumnsWithSpan(TableFragment.HEAD, columns);

        return (

                    {
                        columnsWithSpan ?
                            columnsWithSpan.map(({column, span}, colIndex) => column ?
                                <Th {...restProps}
                                    key={colIndex}
                                    className={column.headClassName}
                                    style={column.headStyle}
                                    renderer={column.headRenderer}
                                    align={column.headAlign}
                                    colIndex={baseColIndex + colIndex}
                                    span={span}
                                    sorting={sorting}
                                    defaultSortingType={column.defaultSortingType || defaultSortingType}
                                    sortingAscIconCls={sortingAscIconCls}
                                    sortingDescIconCls={sortingDescIconCls}
                                    sortable={column.sortable}
                                    sortingProp={column.sortingProp}
                                    onSortChange={onSortChange}/>
                                :
                                null
                            )
                            :
                            null
                    }

        );

    }
}

Thead.propTypes = {

    className: PropTypes.string,
    style: PropTypes.object,

    /**
     * Children passed into table header.
     */
    columns: PropTypes.arrayOf(PropTypes.shape({

        /**
         * width of column
         */
        width: PropTypes.number,

        /**
         * minimum width of column
         */
        minWidth: PropTypes.number,

        /**
         * The class name of header.
         */
        headClassName: PropTypes.string,

        /**
         * Override the styles of header.
         */
        headStyle: PropTypes.object,

        /**
         * The render content in header.
         * (1) string，example： 'id'
         * (2) callback，example：function (colIndex) {return colIndex;}
         */
        headRenderer: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),

        /**
         * align of table header cell
         */
        headAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * column span of table header
         */
        headSpan: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),

        /**
         * The class name of td.
         */
        bodyClassName: PropTypes.string,

        /**
         * Override the styles of td.
         */
        bodyStyle: PropTypes.object,

        /**
         * The render content in table.
         * (1) data key，example： 'id'
         * (2) data key tamplate，example：'${id} - ${name}'
         * (3) callback，example：function (rowData, rowIndex, colIndex) {return rowData.id;}
         */
        bodyRenderer: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),

        /**
         * align of table body cell
         */
        bodyAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * column span of table body
         */
        bodySpan: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),

        /**
         * The class name of footer.
         */
        footClassName: PropTypes.string,

        /**
         * Override the styles of footer.
         */
        footStyle: PropTypes.object,

        /**
         * The render content in footer.
         * (1) string，example： 'id'
         * (2) callback，example：function (colIndex) {return colIndex;}
         */
        footRenderer: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),

        /**
         * align of table footer cell
         */
        footAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * column span of table foot
         */
        footSpan: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),

        /**
         * If true,this column can be sorted.
         */
        sortable: PropTypes.bool,

        /**
         * Sorting property.
         */
        sortingProp: PropTypes.string,

        defaultSortingType: PropTypes.oneOf(Util.enumerateValue(SortingType))

    })).isRequired,

    data: PropTypes.array,
    baseColIndex: PropTypes.number,

    /**
     * sorting
     */
    sorting: PropTypes.shape({
        prop: PropTypes.string,
        type: PropTypes.oneOf(Util.enumerateValue(SortingType))
    }),
    defaultSortingType: PropTypes.oneOf(Util.enumerateValue(SortingType)),
    sortingAscIconCls: PropTypes.string,
    sortingDescIconCls: PropTypes.string,

    onSortChange: PropTypes.func

};

Thead.defaultProps = {

    baseColIndex: 0,

    defaultSortingType: SortingType.ASC,
    sortingAscIconCls: 'fas fa-sort-up',
    sortingDescIconCls: 'fas fa-sort-down'

};

export default Thead;
<commit_msg>update Thead<commit_after>/**
 * @file Thead component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import Th from '../_Th';

import HorizontalAlign from '../_statics/HorizontalAlign';
import SelectMode from '../_statics/SelectMode';
import SelectAllMode from '../_statics/SelectAllMode';
import SortingType from '../_statics/SortingType';

import Util from '../_vendors/Util';
import TableFragment from '../_statics/TableFragment';
import TableCalculation from '../_vendors/TableCalculation';

class Thead extends Component {

    static Align = HorizontalAlign;
    static SelectMode = SelectMode;
    static SelectAllMode = SelectAllMode;
    static SortingType = SortingType;

    constructor(props, ...restArgs) {
        super(props, ...restArgs);
    }

    render() {

        const {
                className, style, columns, baseColIndex,
                sorting, defaultSortingType, sortingAscIconCls, sortingDescIconCls, onSortChange,
                ...restProps
            } = this.props,
            columnsWithSpan = TableCalculation.getColumnsWithSpan(TableFragment.HEAD, columns);

        return (

                    {
                        columnsWithSpan ?
                            columnsWithSpan.map(({column, span}, colIndex) => column ?
                                <Th {...restProps}
                                    key={colIndex}
                                    className={column.headClassName}
                                    style={column.headStyle}
                                    renderer={column.headRenderer}
                                    align={column.headAlign}
                                    colIndex={baseColIndex + colIndex}
                                    span={span}
                                    sorting={sorting}
                                    defaultSortingType={column.defaultSortingType || defaultSortingType}
                                    sortingAscIconCls={sortingAscIconCls}
                                    sortingDescIconCls={sortingDescIconCls}
                                    sortable={column.sortable}
                                    sortingProp={column.sortingProp}
                                    onSortChange={onSortChange}/>
                                :
                                null
                            )
                            :
                            null
                    }

        );

    }
}

Thead.propTypes = {

    className: PropTypes.string,
    style: PropTypes.object,

    /**
     * Children passed into table header.
     */
    columns: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.shape({

        /**
         * width of column
         */
        width: PropTypes.number,

        /**
         * minimum width of column
         */
        minWidth: PropTypes.number,

        /**
         * The class name of header.
         */
        headClassName: PropTypes.string,

        /**
         * Override the styles of header.
         */
        headStyle: PropTypes.object,

        /**
         * The render content in header.
         * (1) string，example： 'id'
         * (2) callback，example：function (colIndex) {return colIndex;}
         */
        headRenderer: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),

        /**
         * align of table header cell
         */
        headAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * column span of table header
         */
        headSpan: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),

        /**
         * The class name of td.
         */
        bodyClassName: PropTypes.string,

        /**
         * Override the styles of td.
         */
        bodyStyle: PropTypes.object,

        /**
         * The render content in table.
         * (1) data key，example： 'id'
         * (2) data key tamplate，example：'${id} - ${name}'
         * (3) callback，example：function (rowData, rowIndex, colIndex) {return rowData.id;}
         */
        bodyRenderer: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),

        /**
         * align of table body cell
         */
        bodyAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * column span of table body
         */
        bodySpan: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),

        /**
         * The class name of footer.
         */
        footClassName: PropTypes.string,

        /**
         * Override the styles of footer.
         */
        footStyle: PropTypes.object,

        /**
         * The render content in footer.
         * (1) string，example： 'id'
         * (2) callback，example：function (colIndex) {return colIndex;}
         */
        footRenderer: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),

        /**
         * align of table footer cell
         */
        footAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * column span of table foot
         */
        footSpan: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),

        /**
         * If true,this column can be sorted.
         */
        sortable: PropTypes.bool,

        /**
         * Sorting property.
         */
        sortingProp: PropTypes.string,

        defaultSortingType: PropTypes.oneOf(Util.enumerateValue(SortingType))

    }))).isRequired,

    data: PropTypes.array,
    baseColIndex: PropTypes.number,

    /**
     * sorting
     */
    sorting: PropTypes.shape({
        prop: PropTypes.string,
        type: PropTypes.oneOf(Util.enumerateValue(SortingType))
    }),
    defaultSortingType: PropTypes.oneOf(Util.enumerateValue(SortingType)),
    sortingAscIconCls: PropTypes.string,
    sortingDescIconCls: PropTypes.string,

    onSortChange: PropTypes.func

};

Thead.defaultProps = {

    baseColIndex: 0,

    defaultSortingType: SortingType.ASC,
    sortingAscIconCls: 'fas fa-sort-up',
    sortingDescIconCls: 'fas fa-sort-down'

};

export default Thead;
<|endoftext|>


<commit_before>// ==========================================================================
// Project:   MySystem
// Copyright: ©2010 My Company, Inc.
// ==========================================================================
/*globals MySystem */

// This is the function that will start your app running.  The default
// implementation will load any fixtures you have created then instantiate
// your controllers and awake the elements on your page.
//
// As you develop your application you will probably want to override this.
// See comments for some pointers on what to do next.
//
MySystem.main = function main() {

  // Step 1: Instantiate Your Views
  // The default code here will make the mainPane for your application visible
  // on screen.  If you app gets any level of complexity, you will probably
  // create multiple pages and panes.
  MySystem.getPath('mainPage.mainPane').append() ;

  // Set the content property on the primary controller
  var nodes = MySystem.store.find(MySystem.Node);
  MySystem.nodesController.set('content', nodes);
	var story = MySystem.store.find(MySystem.Story, 1);
	MySystem.storyController.set('content', story);

} ;

function main() { MySystem.main(); }
<commit_msg>Limits nodes in starting page to first three in fixtures list, which lets me put more in the fixtures without overpopulating the opening screen.<commit_after>// ==========================================================================
// Project:   MySystem
// Copyright: ©2010 My Company, Inc.
// ==========================================================================
/*globals MySystem */

// This is the function that will start your app running.  The default
// implementation will load any fixtures you have created then instantiate
// your controllers and awake the elements on your page.
//
// As you develop your application you will probably want to override this.
// See comments for some pointers on what to do next.
//
MySystem.main = function main() {

  // Step 1: Instantiate Your Views
  // The default code here will make the mainPane for your application visible
  // on screen.  If you app gets any level of complexity, you will probably
  // create multiple pages and panes.
  MySystem.getPath('mainPage.mainPane').append() ;

  // Set the content property on the primary controller
	var nodeQuery = SC.Query.create({
		conditions: '(guid = "1") OR (guid = "2") OR (guid = "3")',
		recordType: MySystem.Node }); // Get the first three fixtures for display
  var nodes = MySystem.store.find(nodeQuery);
  MySystem.nodesController.set('content', nodes);
	var story = MySystem.store.find(MySystem.Story, 1);
	MySystem.storyController.set('content', story);

} ;

function main() { MySystem.main(); }
<|endoftext|>


<commit_before>/*eslint no-unused-vars:0, no-console:0 */
import CSSTransitionGroup from 'react-transition-group/CSSTransitionGroup'
import Footer from '@twreporter/react-components/lib/footer'
import Helmet from 'react-helmet'
import IndexPageComposite from '@twreporter/react-components/lib/index-page'
import LoadingSpinner from '../components/Spinner'
import PropTypes from 'prop-types'
import React from 'react'
import categoryString from '../constants/category-strings'
import categoryURI from '../conf/category-uri'
import styled, { keyframes } from 'styled-components'
import twreporterRedux from '@twreporter/redux'
import sideBarFactory from '../components/side-bar/side-bar-factory'
import { SITE_NAME, SITE_META } from '../constants/index'
import { connect } from 'react-redux'
// lodash
import get from 'lodash/get'
import keys from 'lodash/keys'
import set from 'lodash/set'

const { CategorySection, EditorPicks, InforgraphicSection,
  LatestSection, LatestTopicSection, NewsLetterSection, PhotographySection,
  ReporterIntro,  ReviewsSection, TopicsSection } = IndexPageComposite.components
const { fetchIndexPageContent, fetchCategoriesPostsOnIndexPage } =  twreporterRedux.actions
const { denormalizePosts, denormalizeTopics } = twreporterRedux.utils
const fieldNames = twreporterRedux.reduxStateFields

const _ = {
  get,
  keys,
  set
}

const StyledCSSTransitionGroup = styled(CSSTransitionGroup)`
  .spinner-leave {
    opacity: 1;
  }

  .spinner-leave.spinner-leave-active {
    opacity: 0;
    transition: opacity 400ms linear 1600ms;
  }
`

const LoadingCover = styled.div`
  position: fixed;
  height: 100vh;
  width: 100%;
  z-index: 1999;
  background-color: white;
  div {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
  }
`

const anchors = [
  {
    id: 'latest',
    label: ''
  }, {
    id: 'editorPick',
    label: '編輯精選'
  }, {
    id: 'latestTopic',
    label: '最新專題'
  }, {
    id: 'review',
    label: '評論'
  }, {
    id: 'news-letter',
    label: ''
  }, {
    id: 'categories',
    label: '議題'
  }, {
    id: 'topic',
    label: '專題'
  }, {
    id: 'photography',
    label: '攝影'
  }, {
    id: 'infographic',
    label: '多媒體'
  }, {
    id: 'aboutus',
    label: '關於我們'
  }
]

const moduleBackgounds = {
  latest: '#f2f2f2',
  editorPick: 'white',
  latestTopic: '#f2f2f2',
  review: 'white',
  category: '#e2e2e2',
  topic: '#f2f2f2',
  photography: '#08192d',
  infographic: '#f2f2f2',
  donation: 'white'
}

const Container = styled.div`
  width: 100%;
  margin: 0 auto;
  background-color: white;
  overflow: hidden;
`

const Background = styled.div`
  background-color: ${props => (props.backgroundColor ? props.backgroundColor : '')};
`

const webSiteJSONLD = {
  '@context' : 'http://schema.org',
  '@type' : 'WebSite',
  'name' : '報導者 The Reporter',
  'url' : 'https://www.twreporter.org/',
  'potentialAction' : {
    '@type' : 'SearchAction',
    'target' : 'https://www.twreporter.org/search?q={search_term}',
    'query-input' : 'required name=search_term'
  }
}

const siteNavigationJSONLD = {
  '@context': 'https://schema.org',
  '@graph':
    [
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/',
        'name':'首頁'
      },
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/topics',
        'name':'最新專題'
      },
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/categories/human_rights_and_society',
        'name':'人權．社會'
      },
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/categories/environment_and_education',
        'name':'環境．教育'
      },
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/categories/politics_and_economy',
        'name':'政經．產業'
      },
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/categories/living_and_medical_care',
        'name':'生活．醫療'
      },
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/categories/culture_and_art',
        'name':'文化．藝術'
      },
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/categories/international',
        'name':'國際．兩岸'
      },
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/categories/infographic',
        'name':'多媒體'
      },
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/photography',
        'name':'影像'
      },
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/categories/reviews',
        'name':'評論'
      }
    ]
}

class Homepage extends React.PureComponent {
  static async fetchData({ store }) {
    await fetchIndexPageContent()(store.dispatch, store.getState)
    const error = _.get(store.getState(), [ fieldNames.indexPage, 'error' ])
    if (error !== null) {
      return Promise.reject(error)
    }
  }

  constructor(props) {
    super(props)
    this.sidebar = null
  }

  componentWillMount() {
    this.props.fetchIndexPageContent()
  }

  async componentDidMount() {
    await this.props.fetchCategoriesPostsOnIndexPage()

    // EX: if the url path is /?section=categories
    // after this component mounted and rendered,
    // the browser will smoothly scroll to categories section
    const sectionQuery = _.get(this.props, 'location.query.section', '')
    if (this.sidebar && sectionQuery) {
      this.sidebar.handleClickAnchor(sectionQuery)
    }
  }

  componentWillUnmount() {
    this.sidebar = null
  }

  render() {
    const { isSpinnerDisplayed } = this.props
    const latestTopicData = this.props[fieldNames.sections.latestTopicSection]
    const latestTopicJSX = latestTopicData ? (
      <LatestTopicSection
        data={latestTopicData}
      />
    ) : null

    const SideBar = sideBarFactory.getIndexPageSideBar()

    return (
      <Container>
        <StyledCSSTransitionGroup
          transitionName="spinner"
          transitionEnter={false}
          transitionLeaveTimeout={2000}
        >
          {!isSpinnerDisplayed ? null : (
            <LoadingCover key="loader">
              <LoadingSpinner alt="首頁載入中" />
            </LoadingCover>
          )}
        </StyledCSSTransitionGroup>
        <Helmet
          title={SITE_NAME.FULL}
          link={[
            { rel: 'canonical', href: SITE_META.URL }
          ]}
          meta={[
            { name: 'description', content: SITE_META.DESC },
            { name: 'twitter:title', content: SITE_NAME.FULL },
            { name: 'twitter:image', content: SITE_META.OG_IMAGE },
            { name: 'twitter:description', content: SITE_META.DESC },
            { property: 'og:title', content: SITE_NAME.FULL },
            { property: 'og:description', content: SITE_META.DESC },
            { property: 'og:image', content: SITE_META.OG_IMAGE },
            { property: 'og:type', content: 'website' },
            { property: 'og:url', content: SITE_META.URL }
          ]}
        />
        <SideBar
          ref={(node) => this.sidebar = node}
          anchors={anchors}
        >
          <LatestSection
            data={this.props[fieldNames.sections.latestSection]}
          />
          <EditorPicks data={this.props[fieldNames.sections.editorPicksSection]} />
          {latestTopicJSX}
          <ReviewsSection
            data={this.props[fieldNames.sections.reviewsSection]}
            moreURI={`categories/${categoryURI.reviews}`}
          />
          <NewsLetterSection />
          <Background
            backgroundColor={moduleBackgounds.category}
          >
            <CategorySection
              data={this.props.categories}
            />
          </Background>
          <Background
            backgroundColor={moduleBackgounds.topic}
          >
            <TopicsSection
              data={this.props[fieldNames.sections.topicsSection]}
            />
          </Background>
          <Background
            backgroundColor={moduleBackgounds.photography}
          >
            <PhotographySection
              data={this.props[fieldNames.sections.photosSection]}
              moreURI="photography"
            />
          </Background>
          <Background
            backgroundColor={moduleBackgounds.infographic}
          >
            <InforgraphicSection
              data={this.props[fieldNames.sections.infographicsSection]}
              moreURI={`categories/${categoryURI.infographic}`}
            />
          </Background>
          <ReporterIntro />
        </SideBar>
        <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(webSiteJSONLD) }} />
        <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(siteNavigationJSONLD) }} />
        <Footer />
      </Container>
    )
  }
}

function buildCategorySectionData(state) {
  const buildData = (post) => {
    return {
      id: _.get(post, 'id', ''),
      slug: _.get(post, 'slug', ''),
      title: _.get(post, 'title', ''),
      hero_image: _.get(post, 'hero_image'),
      style: _.get(post, 'style', '')
    }
  }

  const catFields = _.keys(fieldNames.categories)
  const postEntities = _.get(state, [ fieldNames.entities, fieldNames.postsInEntities ], {})
  const selected = []
  const data = []

  catFields.forEach((field) => {
    let post
    const slugs = _.get(state, [ fieldNames.indexPage, fieldNames.categories[field] ], [])
    if (Array.isArray(slugs)) {
      for (let i = 0; i < slugs.length; i+=1) {
        const slug = slugs[i]
        if (selected.indexOf(slug) === -1) {
          post = buildData(_.get(denormalizePosts(slug, postEntities), 0))
          post.id = field + '-' + post.id
          post.listName = categoryString[field]
          post.moreURI = `categories/${categoryURI[field]}`
          data.push(post)
          break
        }
      }
      if (typeof post !== 'object' && slugs.length > 0) {
        post = buildData(_.get(denormalizePosts(slugs[0], postEntities), 0))
        post.id = field + '-' + post.id
        post.listName = categoryString[field]
        post.moreURI = `categories/${categoryURI[field]}`
        data.push(post)
      }
    }
  })
  return data
}

function mapStateToProps(state) {
  const entities = _.get(state, fieldNames.entities, {})
  const indexPageState = _.get(state, fieldNames.indexPage, {})
  // get post entities
  const postEntities = _.get(entities, fieldNames.postsInEntities, {})

  // get topic entities
  const topicEntities = _.get(entities, fieldNames.topicsInEntities, {})

  // restore the posts
  const sections = fieldNames.sections
  const latest = denormalizePosts(_.get(indexPageState, sections.latestSection, []), postEntities)
  const editorPicks = denormalizePosts(_.get(indexPageState, sections.editorPicksSection, []), postEntities)
  const reviews = denormalizePosts(_.get(indexPageState, sections.reviewsSection, []), postEntities)
  const photoPosts = denormalizePosts(_.get(indexPageState, sections.photosSection, []), postEntities)
  const infoPosts = denormalizePosts(_.get(indexPageState, sections.infographicsSection, []), postEntities)

  // restore the topics
  const latestTopic = _.get(denormalizeTopics(_.get(indexPageState, sections.latestTopicSection), topicEntities, postEntities), 0, null)
  const topics = denormalizeTopics(_.get(indexPageState, sections.topicsSection, []), topicEntities, postEntities)

  // check if spinner should be displayed
  const err = _.get(indexPageState, 'error', null)
  const isFetching = _.get(indexPageState, 'isFetching', false)
  const isSpinnerDisplayed = (latest.length <= 0) && isFetching && !err

  // restore

  return {
    [fieldNames.sections.latestSection]: latest,
    [fieldNames.sections.editorPicksSection]: editorPicks,
    [fieldNames.sections.latestTopicSection]: latestTopic,
    [fieldNames.sections.reviewsSection]: reviews,
    [fieldNames.sections.topicsSection]: topics,
    [fieldNames.sections.photosSection]: photoPosts,
    [fieldNames.sections.infographicsSection]: infoPosts,
    categories: buildCategorySectionData(state),
    isSpinnerDisplayed,
    ifAuthenticated: _.get(state, [ 'auth', 'authenticated' ], false)

  }
}

export { Homepage }
export default connect(mapStateToProps, { fetchIndexPageContent, fetchCategoriesPostsOnIndexPage })(Homepage)
<commit_msg>Add donation-box section between latestTopic section and ReviewsSection<commit_after>/*eslint no-unused-vars:0, no-console:0 */
import CSSTransitionGroup from 'react-transition-group/CSSTransitionGroup'
import Footer from '@twreporter/react-components/lib/footer'
import Helmet from 'react-helmet'
import IndexPageComposite from '@twreporter/react-components/lib/index-page'
import LoadingSpinner from '../components/Spinner'
import PropTypes from 'prop-types'
import React from 'react'
import categoryString from '../constants/category-strings'
import categoryURI from '../conf/category-uri'
import styled, { keyframes } from 'styled-components'
import twreporterRedux from '@twreporter/redux'
import sideBarFactory from '../components/side-bar/side-bar-factory'
import { SITE_NAME, SITE_META } from '../constants/index'
import { connect } from 'react-redux'
// lodash
import get from 'lodash/get'
import keys from 'lodash/keys'
import set from 'lodash/set'

const { CategorySection, DonationBoxSection, EditorPicks, InforgraphicSection,
  LatestSection, LatestTopicSection, NewsLetterSection, PhotographySection,
  ReporterIntro,  ReviewsSection, TopicsSection } = IndexPageComposite.components
const { fetchIndexPageContent, fetchCategoriesPostsOnIndexPage } =  twreporterRedux.actions
const { denormalizePosts, denormalizeTopics } = twreporterRedux.utils
const fieldNames = twreporterRedux.reduxStateFields

const _ = {
  get,
  keys,
  set
}

const StyledCSSTransitionGroup = styled(CSSTransitionGroup)`
  .spinner-leave {
    opacity: 1;
  }

  .spinner-leave.spinner-leave-active {
    opacity: 0;
    transition: opacity 400ms linear 1600ms;
  }
`

const LoadingCover = styled.div`
  position: fixed;
  height: 100vh;
  width: 100%;
  z-index: 1999;
  background-color: white;
  div {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
  }
`

const anchors = [
  {
    id: 'latest',
    label: ''
  }, {
    id: 'editorPick',
    label: '編輯精選'
  }, {
    id: 'latestTopic',
    label: '最新專題'
  }, {
    id: 'review',
    label: '評論'
  }, {
    id: 'news-letter',
    label: ''
  }, {
    id: 'categories',
    label: '議題'
  }, {
    id: 'topic',
    label: '專題'
  }, {
    id: 'photography',
    label: '攝影'
  }, {
    id: 'infographic',
    label: '多媒體'
  }, {
    id: 'aboutus',
    label: '關於我們'
  }
]

const moduleBackgounds = {
  latest: '#f2f2f2',
  editorPick: 'white',
  latestTopic: '#f2f2f2',
  review: 'white',
  category: '#e2e2e2',
  topic: '#f2f2f2',
  photography: '#08192d',
  infographic: '#f2f2f2',
  donation: 'white'
}

const Container = styled.div`
  width: 100%;
  margin: 0 auto;
  background-color: white;
  overflow: hidden;
`

const Background = styled.div`
  background-color: ${props => (props.backgroundColor ? props.backgroundColor : '')};
`

const webSiteJSONLD = {
  '@context' : 'http://schema.org',
  '@type' : 'WebSite',
  'name' : '報導者 The Reporter',
  'url' : 'https://www.twreporter.org/',
  'potentialAction' : {
    '@type' : 'SearchAction',
    'target' : 'https://www.twreporter.org/search?q={search_term}',
    'query-input' : 'required name=search_term'
  }
}

const siteNavigationJSONLD = {
  '@context': 'https://schema.org',
  '@graph':
    [
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/',
        'name':'首頁'
      },
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/topics',
        'name':'最新專題'
      },
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/categories/human_rights_and_society',
        'name':'人權．社會'
      },
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/categories/environment_and_education',
        'name':'環境．教育'
      },
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/categories/politics_and_economy',
        'name':'政經．產業'
      },
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/categories/living_and_medical_care',
        'name':'生活．醫療'
      },
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/categories/culture_and_art',
        'name':'文化．藝術'
      },
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/categories/international',
        'name':'國際．兩岸'
      },
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/categories/infographic',
        'name':'多媒體'
      },
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/photography',
        'name':'影像'
      },
      {
        '@type':'SiteNavigationElement',
        'url':'https://www.twreporter.org/categories/reviews',
        'name':'評論'
      }
    ]
}

class Homepage extends React.PureComponent {
  static async fetchData({ store }) {
    await fetchIndexPageContent()(store.dispatch, store.getState)
    const error = _.get(store.getState(), [ fieldNames.indexPage, 'error' ])
    if (error !== null) {
      return Promise.reject(error)
    }
  }

  constructor(props) {
    super(props)
    this.sidebar = null
  }

  componentWillMount() {
    this.props.fetchIndexPageContent()
  }

  async componentDidMount() {
    await this.props.fetchCategoriesPostsOnIndexPage()

    // EX: if the url path is /?section=categories
    // after this component mounted and rendered,
    // the browser will smoothly scroll to categories section
    const sectionQuery = _.get(this.props, 'location.query.section', '')
    if (this.sidebar && sectionQuery) {
      this.sidebar.handleClickAnchor(sectionQuery)
    }
  }

  componentWillUnmount() {
    this.sidebar = null
  }

  render() {
    const { isSpinnerDisplayed } = this.props
    const latestTopicData = this.props[fieldNames.sections.latestTopicSection]
    const latestTopicJSX = latestTopicData ? (
      <LatestTopicSection
        data={latestTopicData}
      />
    ) : null

    const SideBar = sideBarFactory.getIndexPageSideBar()

    return (
      <Container>
        <StyledCSSTransitionGroup
          transitionName="spinner"
          transitionEnter={false}
          transitionLeaveTimeout={2000}
        >
          {!isSpinnerDisplayed ? null : (
            <LoadingCover key="loader">
              <LoadingSpinner alt="首頁載入中" />
            </LoadingCover>
          )}
        </StyledCSSTransitionGroup>
        <Helmet
          title={SITE_NAME.FULL}
          link={[
            { rel: 'canonical', href: SITE_META.URL }
          ]}
          meta={[
            { name: 'description', content: SITE_META.DESC },
            { name: 'twitter:title', content: SITE_NAME.FULL },
            { name: 'twitter:image', content: SITE_META.OG_IMAGE },
            { name: 'twitter:description', content: SITE_META.DESC },
            { property: 'og:title', content: SITE_NAME.FULL },
            { property: 'og:description', content: SITE_META.DESC },
            { property: 'og:image', content: SITE_META.OG_IMAGE },
            { property: 'og:type', content: 'website' },
            { property: 'og:url', content: SITE_META.URL }
          ]}
        />
        <SideBar
          ref={(node) => this.sidebar = node}
          anchors={anchors}
        >
          <LatestSection
            data={this.props[fieldNames.sections.latestSection]}
          />
          <EditorPicks data={this.props[fieldNames.sections.editorPicksSection]} />
          {latestTopicJSX}
          <DonationBoxSection />
          <ReviewsSection
            data={this.props[fieldNames.sections.reviewsSection]}
            moreURI={`categories/${categoryURI.reviews}`}
          />
          <NewsLetterSection />
          <Background
            backgroundColor={moduleBackgounds.category}
          >
            <CategorySection
              data={this.props.categories}
            />
          </Background>
          <Background
            backgroundColor={moduleBackgounds.topic}
          >
            <TopicsSection
              data={this.props[fieldNames.sections.topicsSection]}
            />
          </Background>
          <Background
            backgroundColor={moduleBackgounds.photography}
          >
            <PhotographySection
              data={this.props[fieldNames.sections.photosSection]}
              moreURI="photography"
            />
          </Background>
          <Background
            backgroundColor={moduleBackgounds.infographic}
          >
            <InforgraphicSection
              data={this.props[fieldNames.sections.infographicsSection]}
              moreURI={`categories/${categoryURI.infographic}`}
            />
          </Background>
          <ReporterIntro />
        </SideBar>
        <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(webSiteJSONLD) }} />
        <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(siteNavigationJSONLD) }} />
        <Footer />
      </Container>
    )
  }
}

function buildCategorySectionData(state) {
  const buildData = (post) => {
    return {
      id: _.get(post, 'id', ''),
      slug: _.get(post, 'slug', ''),
      title: _.get(post, 'title', ''),
      hero_image: _.get(post, 'hero_image'),
      style: _.get(post, 'style', '')
    }
  }

  const catFields = _.keys(fieldNames.categories)
  const postEntities = _.get(state, [ fieldNames.entities, fieldNames.postsInEntities ], {})
  const selected = []
  const data = []

  catFields.forEach((field) => {
    let post
    const slugs = _.get(state, [ fieldNames.indexPage, fieldNames.categories[field] ], [])
    if (Array.isArray(slugs)) {
      for (let i = 0; i < slugs.length; i+=1) {
        const slug = slugs[i]
        if (selected.indexOf(slug) === -1) {
          post = buildData(_.get(denormalizePosts(slug, postEntities), 0))
          post.id = field + '-' + post.id
          post.listName = categoryString[field]
          post.moreURI = `categories/${categoryURI[field]}`
          data.push(post)
          break
        }
      }
      if (typeof post !== 'object' && slugs.length > 0) {
        post = buildData(_.get(denormalizePosts(slugs[0], postEntities), 0))
        post.id = field + '-' + post.id
        post.listName = categoryString[field]
        post.moreURI = `categories/${categoryURI[field]}`
        data.push(post)
      }
    }
  })
  return data
}

function mapStateToProps(state) {
  const entities = _.get(state, fieldNames.entities, {})
  const indexPageState = _.get(state, fieldNames.indexPage, {})
  // get post entities
  const postEntities = _.get(entities, fieldNames.postsInEntities, {})

  // get topic entities
  const topicEntities = _.get(entities, fieldNames.topicsInEntities, {})

  // restore the posts
  const sections = fieldNames.sections
  const latest = denormalizePosts(_.get(indexPageState, sections.latestSection, []), postEntities)
  const editorPicks = denormalizePosts(_.get(indexPageState, sections.editorPicksSection, []), postEntities)
  const reviews = denormalizePosts(_.get(indexPageState, sections.reviewsSection, []), postEntities)
  const photoPosts = denormalizePosts(_.get(indexPageState, sections.photosSection, []), postEntities)
  const infoPosts = denormalizePosts(_.get(indexPageState, sections.infographicsSection, []), postEntities)

  // restore the topics
  const latestTopic = _.get(denormalizeTopics(_.get(indexPageState, sections.latestTopicSection), topicEntities, postEntities), 0, null)
  const topics = denormalizeTopics(_.get(indexPageState, sections.topicsSection, []), topicEntities, postEntities)

  // check if spinner should be displayed
  const err = _.get(indexPageState, 'error', null)
  const isFetching = _.get(indexPageState, 'isFetching', false)
  const isSpinnerDisplayed = (latest.length <= 0) && isFetching && !err

  // restore

  return {
    [fieldNames.sections.latestSection]: latest,
    [fieldNames.sections.editorPicksSection]: editorPicks,
    [fieldNames.sections.latestTopicSection]: latestTopic,
    [fieldNames.sections.reviewsSection]: reviews,
    [fieldNames.sections.topicsSection]: topics,
    [fieldNames.sections.photosSection]: photoPosts,
    [fieldNames.sections.infographicsSection]: infoPosts,
    categories: buildCategorySectionData(state),
    isSpinnerDisplayed,
    ifAuthenticated: _.get(state, [ 'auth', 'authenticated' ], false)

  }
}

export { Homepage }
export default connect(mapStateToProps, { fetchIndexPageContent, fetchCategoriesPostsOnIndexPage })(Homepage)
<|endoftext|>


<commit_before>import React, { Component } from "react";
import Header from "../components/header/Header";
import Greeting from "./greeting/Greeting";
import Skills from "./skills/Skills";
import Projects from "./projects/Projects";
import StartupProject from "./StartupProjects/StartupProject";
import Achievement from "./achievement/Achievement";
import Blogs from "./blogs/Blogs";
import Contact from "./contact/Contact";
import Footer from "../components/footer/Footer";
import Talks from "./talks/Talks";
import Podcast from "./podcast/Podcast";
import Top from "./topbutton/Top"

export default class Main extends Component {
  render() {
    return (

        <Greeting />
        <Skills />
        <Projects />
        <StartupProject />
        <Achievement />
        <Blogs />
        <Talks />
        <Podcast />
        <Contact />

        <Top />

    );
  }
}
<commit_msg>Some test<commit_after>import React, { Component } from "react";
import Header from "../components/header/Header";
import Greeting from "./greeting/Greeting";
import Skills from "./skills/Skills";
import Projects from "./projects/Projects";
import StartupProject from "./StartupProjects/StartupProject";
import Achievement from "./achievement/Achievement";
import Blogs from "./blogs/Blogs";
import Contact from "./contact/Contact";
import Footer from "../components/footer/Footer";
import Talks from "./talks/Talks";
import Podcast from "./podcast/Podcast";

export default class Main extends Component {
  render() {
    return (

        <Greeting />
        <Skills />
        <Projects />
        <StartupProject />
        <Achievement />
        <Blogs />
        <Talks />
        <Podcast />
        <Contact />

    );
  }
}
<|endoftext|>


<commit_before>var fs = require("fs");
var path = require("path");
var process = require("process");

var srcPath = '/data/photos/';
var dstPath = '/data-crypted/photos/'
var code = process.env.PASSWORD;
var crypt = function (error, files) {
    if (error) return;
    queue.call(function (file) {
        return new Promise(function (ok, oh) {
            fs.readFile(path.join(srcPath, file), function (error, chunk) {
                if (error) {
                    oh(error);
                    return;
                }
                var chunk = encode62.encode(chunk, code);
                fs.writeFile(path.join(dstPath, file), chunk, function (error) {
                    if (error) {
                        oh(error);
                        return;
                    }
                    ok();
                });
            });
        })
    }, files);
};
if (!code) {
    throw new Error("请设置密码");
}
fs.readdir(srcPath, crypt);
<commit_msg>支持大文件<commit_after>var fs = require("fs");
var path = require("path");
var process = require("process");

var srcPath = '/data-source/';
var dstPath = '/data/xiaohua/'
var code = process.env.PASSWORD;
var crypt = function (error, files) {
    if (error) return;
    queue.call(function (file) {
        return new Promise(function (ok, oh) {
            var r = fs.createReadStream(path.join(srcPath, file));
            var w = fs.createWriteStream(path.join(dstPath, file));
            var i = 0;
            r.once("error", oh);
            r.on("data", function (chunk) {
                var sign = code.slice(i % code.length, code.length) + code.slice(0, i % code.length);
                var chunk = encode62.encode(chunk, sign);
                i += chunk.length;
                if (i > 1024 * 1024 * 1024) console.info(file, (i / 1024 / 1024 / 1024).toFixed(2), 'GiB');
                if (i > 1024 * 1024) console.info(file, (i / 1024 / 1024).toFixed(2), 'MiB');
                else if (i > 1024) console.info(file, (i / 1024).toFixed(2), 'KiB');
                else console.info(file, i, 'Byte');
                w.write(chunk);
            });
            r.on('end', function () {
                w.end();
                ok();
            });
        })
    }, files);
};
if (!code) {
    throw new Error("请设置密码");
}
crypt(null, [
    'photos/05.jpg'
]);
// fs.readdir(srcPath, crypt);
<|endoftext|>


<commit_before>Helpers = initHelpers(window, document);

function initHelpers(w, d, undefined) {
  "use strict";
  var useCustomCss = false;
  var extended = extensions();

  var helperObj = {
    report: reportHTML,
    useJQ: loadJQ,
    useCSS: setCustomCss,
    log2Screen: log2Screen,
    initSO: SOInit,
    printDirect: printDirect,
    logClear: screenClear,
    Partial: Partial,
    cloneObj: cloneObj
  };

  function setCustomCss(yn) {
    useCustomCss = yn;
    if (useCustomCss) {
     loadCSS();
    } else {
     unloadCSS();
    }
  }

  function loadJQ(callback) {
      if (w.jQuery) {
        return callback && callback instanceof Function ? callback() : true;
      }

      var jqel  = createElementWithProps('script', {src: 'http://code.jquery.com/jquery-2.1.1.min.js', id: 'jqloaded'});

      d.querySelector('head').appendChild(jqel);

      if (callback && callback instanceof Function)
          jqel.addEventListener('load', callback);

      return void(0);
  }

  function loadCSS() {
    if (d.querySelector('#HelperCSS')) {
      return true;
    }
    var css = createElementWithProps(
                    'link',
                    { href: 'https://rawgit.com/KooiInc/Helpers/master/Helpers.css',
                      type: 'text/css',
                      rel: 'stylesheet',
                      id: 'HelperCSS' }
              );
    return d.querySelector('head').appendChild (css);
  }

  function unloadCSS() {
   var css = document.querySelector('#HelperCSS');
   void (css && document.querySelector('head').removeChild(css));
  }

   // a few usefull augments/polyfills
  function extensions() {
        if (extended) {
          return true;
        }
        // remove double values from an array
        function noDoubles(arr) {
          arr = arr || this;
          return arr.filter(function(val) {
           return !this[val] ? ((this[val] = true), true) : false;
          }, {});
        }

        function stringformat() {
                var args = args2Array(arguments);
                return this.replace(/(\{\d+\})/g, function(a){
                    return args[+(a.substr(1,a.length-2))||0];
                });
        };

        // determine value frequencies in an array
        function frequencies(arr) {
          var mapped = {sum: 0};
          (arr || this).map(function (a){
              if (!(a in this)) { this[a] = 1; }
              else { this[a] += 1; }
              this.sum += 1;
              return a; }, mapped
          );
          return mapped;
        }

        String.Format = function(){
            var args = args2Array(arguments);
            return stringformat.apply(args[0],args.slice(1));
        };

        String.prototype.format = function () {
          return stringformat.apply(this,arguments);
        }

        String.prototype.repeat = function(n){
            var s = this, r = '';
            while(n--) {
                r += s;
            }
            return r;
        };

        Number.prototype.toRange = Number.prototype.toRange || function (fn, startvalue) {
          startvalue = startvalue || 0;
          fn = fn instanceof Function ? fn : function (a,i) { return i+startvalue; };
          return String(new Array(this.valueOf())).split(',').map( fn );
        };

        Number.prototype.pretty = Number.prototype.pretty || function (usa, noprecision) {
            return sep1000(this, usa, noprecision);
        };

        // run functions sequentially
        Function.prototype.andThen = function () {
         var args = args2Array(arguments)
            ,next = args.slice(1);
         this();
         return args[0] && next.length
                 ? args[0].andThen.apply(args[0], next)
                 : args.length == 1  ? args[0]()
                 : true;
        };

        Function.prototype.partial = Function.prototype.partial || function () {
             var stored_args = [].slice.call(arguments)
                ,fn = this;
            return function () {
                return fn.apply(null, stored_args.concat([].slice.call(arguments)));
             };
        };

        Function.prototype.partialx = function(){
            var fn = this, args = Array.prototype.slice.call(arguments);
            return function(){
              var arg = 0;
              for ( var i = 0; i < args.length && arg < arguments.length; i++ )
                if ( args[i] === undefined || args[i] === null)
                  args[i] = arguments[arg++];
              return fn.apply(this, args);
            };
        };

        // is character @ [atpos] upperCase?
        boolean:String.prototype.charIsUpper = function (atpos){
          var chr = this.charAt(atpos);
          return /[A-Z]|[\u0080-\u024F]/.test(chr) && chr === chr.toUpperCase();
        };

        String.prototype.firstUp = function () {
          return this.slice(0,1).toUpperCase()+this.slice(1).toLowerCase();
        }

        String.prototype.isValidEmail = function() {
          // should be sufficient
          return  /^[\w._-]{1,}[+]?[\w._-]{0,}@[\w.-]+\.[a-zA-Z]{2,6}$/.test(this);
        }

        Array.prototype.toRE = function (){
          try { return RegExp.apply(null, this); }
          catch(e) { return /.*/; }
        }

        Array.toRE = function(arr) {
          return ([].toRE.call(arr));
        }

        String.prototype.reCleanup = function(encodeHTML){
          var str = encodeHTML ? this.replace(/[\u0080-\u024F]/g, function(a) {return '&#'+a.charCodeAt(0)+';';}) : this;
          return str.replace(/[?*|.+$\/]|\\/g, function(c) {return c==='\\' ? '' : '\\\\'+c;});
        }

        String.concat = function(joinchar){
          joinchar = joinchar || '';
          return args2Array(arguments).join(joinchar);
        }

        // MDN Array filter polyfill
        if (!Array.prototype.filter) {
          Array.prototype.filter = function(fun)
          {
            if (this === void 0 || this === null)
              throw new TypeError();

            var t = Object(this);
            var len = t.length >>> 0;
            if (typeof fun != "function")
              throw new TypeError();

            var res = [];
            var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
            for (var i = 0; i < len; i++) {
              if (i in t)  {
                var val = t[i];
                if (fun.call(thisArg, val, i, t))
                  res.push(val);
              }
            }

            return res;
          };
        }

        // MDN Array map polyfill
        if (!Array.prototype.map) {
          Array.prototype.map = function(fun)
          {
            if (this === void 0 || this === null)
              throw new TypeError();

            var t = Object(this);
            var len = t.length >>> 0;
            if (typeof fun !== "function")
              throw new TypeError();

            var res = new Array(len);
            var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
            for (var i = 0; i < len; i++) {
              if (i in t)
                res[i] = fun.call(thisArg, t[i], i, t);
            }

            return res;
        };
       }

       Array.prototype.uniquify = noDoubles;

       Array.prototype.frequencies = Array.prototype.frequencies || frequencies;

       Array.prototype.each = Array.prototype.each || function (fn,rewrite) {
          for (var i = 0; i < this.length; i++) {
           if (rewrite){
             this[i] = fn(this[i]);
           } else {
            fn(this[i]);
           }
          }
          return this;
       };

       // statics
       Object.print = function (obj, space) {
            space = space || '  ';
            return '
'+JSON.stringify(obj, null, space)+'
';
       }

       Object.format = function (obj, space) {
            space = space || '  ';
            return '
'+JSON.stringify(obj, null, space)+'
';
       }

       Object.isOneOf = isOfType;

       extended = true;
       return extended;
  }

  // SO specials
  function SOInit() {
    extensions();
    setCustomCss(true);
    var solink = d.querySelector('[data-linkid]');
    if (!solink.querySelector('.linkhover')) {
      solink.appendChild(createElementWithProps('div', { className: 'linkhover', 'data-dyn': 'true' } ));
    }
    loadJQ( jqcallback );

    function jqcallback() {
       $(document).on('mouseover', '.solink',  setSOLink);
       $(document).on('click', '[data-link]', clicklink);
    };
  }

  function setSOLink(e) {
      if ($(this).attr('data-link')) { return true; }

      $.ajax(
          {
              url: 'http://www.nicon.nl/node/stackx/questionx',
              data: {qid: $('.solink').first().attr('data-linkid')},
              method: 'post',
              success: SOcb
          }
      );

      function SOcb(data) {
          var resp = data.items && data.items[0] || data
          ,linkelement = $('.solink').first()
          ,linktip = linkelement.find('.linkhover').first()
          ;
          linktip.html(
              'Click logo to view the related question:

' + resp.title + '

' +
              'Asked by
'+ resp.owner.display_name + '
; '+
              'rep ' + resp.owner.reputation +'; question views: '+resp.view_count
          );
          linkelement.attr('data-link', resp.link);
      }

      return true;
  }

  // firefox needs a link added to the DOM, Chrome, IE don't
  function clicklink(e) {
      e.stopPropagation(); //just this link
      var linkurl = this.getAttribute('data-link')
         ,xlink = document.querySelector('a[href="'+linkurl+'"]') ||
                  function() {
                      var _link = createElementWithProps(
                                    'a', { href: linkurl, target:'_blank', style: { display: 'none' } }
                                  );
                      document.body.appendChild(_link);
                      return _link; }();
      xlink.click();
  }

  // Reporting
  // 1. simple
  function reportHTML() {
    var report = document.querySelector('#result') ||
        function() {
          var r = createElementWithProps( 'div', {id: 'result'} );
          document.querySelector('body').appendChild(r);
          return r;
        }();
    var entry = createElementWithProps('p');
    entry.innerHTML = args2Array(arguments).join('');
    report.appendChild(entry);

    if (useCustomCss) {
      var to = function () { entry.className = 'fadeIn'; };
      var dummy = setTimeout(to, 100);
    }
  }

  // 2. with parameter object usage
  function log2Screen() {
      var result = document.querySelector('#result') ||
                   function () {
                       var r = createElementWithProps('div', {id: 'result'});
                       document.body.appendChild(r);
                       return r; }()
         ,args = args2Array(arguments)
         ,lastarg = args.slice(-1)[0]
         ,optkeys = /clear|clrscr|direct|opts|useopts|continuous/i
         ,opts = Object.isOneOf(lastarg, Object) && Object.keys(lastarg).filter(function(v){return optkeys.test(v);}).length
                 ? lastarg.opts instanceof Object ? lastarg.opts : lastarg
                 : {empty: 1};
      void(!opts.empty && (args = args.slice(0,-1)));

      if (opts.clrscr) {
          return result.innerHTML = '';
      }

      if (opts.clear) {
          result.innerHTML = '';
      }

      if (opts.continuous) {
          return result.innerHTML += args.join('').replace(/\n/g,'
');
      }

      var p = createElementWithProps('p');
      p.innerHTML = args.join('').replace(/\n/g,'
');
      result.appendChild(p);
      return opts.direct ? (p.className = 'fadeIn')
                         : setTimeout(function () { p.className = 'fadeIn'; }, +opts.timed*1000 || 0);
  }

  function printDirect() {
    return log2Screen.apply( null, args2Array(arguments).concat({direct:true}) );
  }

  function screenClear() {
      var res = d.querySelector('#result');
      return  res && (res.innerHTML = '') || true;
  }

  // utilities
  function createElementWithProps(elType, props) {
    var el = d.createElement(elType);
    if (props && isOfType(props, Object)) {
      for (var l in props) {
        if (!props.hasOwnProperty(l)) continue;
        if (/style/i.test(l)) {
          for (var ll in props[l]) {
            if (!props[l].hasOwnProperty(ll)) continue;
            el.style[ll] = props[l][ll];
          }
        } else {
          el[l] = props[l];
        }
      }
    }
    return el;
  }

  function args2Array(args, sliceAt){
    var arr = [];
    while (arr.length < args.length) { arr.push(args[arr.length]); }
    return sliceAt ? arr.slice(sliceAt) : arr;
  }

  // see: http://codereview.stackexchange.com/questions/23317/istypeobj-gettypeobj-v0/23329#23329
  function isOfType(obj) {
    if (!obj) { return false; }
    var test = arguments.length ? args2Array(arguments).slice(1) : null
       ,self = obj.constructor;
    return test ? !!(test.filter(function(a){return a === self}).length)
           : (self.constructor.name ||
              (String(self).match ( /^function\s*([^\s(]+)/im)
                || [0,'ANONYMOUS_CONSTRUCTOR']) [1] );
  };

  function Partial (func) {
    this.initial = args2Array(arguments, 1);
    this.funcp = function () {
                 return func.partialx.apply(func, this.initial);
                };
    if (!Partial.prototype.x) {
      Partial.prototype.x = function () {
        return this.funcp().apply(null, args2Array(arguments));
      };
    }
  };

  function cloneObj(obj) {
    // clone the whole enchillada, recursive
    function clone(o, curr) {
        for (var l in o){
            if (!o.hasOwnProperty(l)) { continue; }
            if (o[l] instanceof Object) {
                curr[l] = cloneObj(o[l]);
            } else {
                curr[l] = o[l];
            }
        }
        return curr;
    }

    return obj instanceof Array
             ? obj.slice().map( function (v) { return cloneObj(v); } )
             : obj instanceof Date
               ? new Date(obj)
               : obj instanceof Object
                 ? clone(obj, {})
                 : obj;
  }

  function sep1000(somenum, usa, noprecision) {
    var dec = (''+somenum).split(/[.,]/)
       ,lendec = dec[1] ? dec[1].length : 0
       ,precision = lendec && !noprecision ? decPrecise(somenum,lendec) : dec[1]
       ,sep = usa ? ',' : '.'
       ,decsep = usa ? '.' : ',';

    // from http://stackoverflow.com/questions/10473994/javascript-adding-decimal-numbers-issue/10474209
    function decPrecise(d,l){
      return String(d.toFixed(12)).split(/[.,]/)[1].substr(0,l);
    }

    function xsep(num,sep) {
      var n = String(num).split('')
         ,i = -3;
      while (n.length + i > 0) {
          n.splice(i, 0, sep);
          i -= 4;
      }
      return n.join('');
    }
    return xsep(dec[0],sep) + (dec[1] ? decsep+precision :'');
  }

  return helperObj
}
<commit_msg>And more<commit_after>Helpers = initHelpers(window, document);

function initHelpers(w, d, undefined) {
  "use strict";
  var useCustomCss = false;
  var extended = extensions();

  var helperObj = {
    report: reportHTML,
    useJQ: loadJQ,
    useCSS: setCustomCss,
    log2Screen: log2Screen,
    initSO: SOInit,
    printDirect: printDirect,
    logClear: screenClear,
    Partial: Partial,
    cloneObj: cloneObj
  };

  function setCustomCss(yn) {
    useCustomCss = yn;
    if (useCustomCss) {
     loadCSS();
    } else {
     unloadCSS();
    }
  }

  function loadJQ(callback) {
      if (w.jQuery) {
        return callback && callback instanceof Function ? callback() : true;
      }

      var jqel  = createElementWithProps('script', {src: 'http://code.jquery.com/jquery-2.1.1.min.js', id: 'jqloaded'});

      d.querySelector('head').appendChild(jqel);

      if (callback && callback instanceof Function)
          jqel.addEventListener('load', callback);

      return void(0);
  }

  function loadCSS() {
    if (d.querySelector('#HelperCSS')) {
      return true;
    }
    var css = createElementWithProps(
                    'link',
                    { href: 'https://rawgit.com/KooiInc/Helpers/master/Helpers.css',
                      type: 'text/css',
                      rel: 'stylesheet',
                      id: 'HelperCSS' }
              );
    return d.querySelector('head').appendChild (css);
  }

  function unloadCSS() {
   var css = document.querySelector('#HelperCSS');
   void (css && document.querySelector('head').removeChild(css));
  }

   // a few usefull augments/polyfills
  function extensions() {
        if (extended) {
          return true;
        }
        // remove double values from an array
        function noDoubles(arr) {
          arr = arr || this;
          return arr.filter(function(val) {
           return !this[val] ? ((this[val] = true), true) : false;
          }, {});
        }

        function stringformat() {
                var args = args2Array(arguments);
                return this.replace(/(\{\d+\})/g, function(a){
                    return args[+(a.substr(1,a.length-2))||0];
                });
        };

        // determine value frequencies in an array
        function frequencies(arr) {
          var mapped = {sum: 0};
          (arr || this).map(function (a){
              if (!(a in this)) { this[a] = 1; }
              else { this[a] += 1; }
              this.sum += 1;
              return a; }, mapped
          );
          return mapped;
        }

        String.Format = function(){
            var args = args2Array(arguments);
            return stringformat.apply(args[0],args.slice(1));
        };

        String.prototype.format = function () {
          return stringformat.apply(this,arguments);
        }

        String.prototype.repeat = function(n){
            var s = this, r = '';
            while(n--) {
                r += s;
            }
            return r;
        };

        Number.prototype.toRange = Number.prototype.toRange || function (fn, startvalue) {
          startvalue = startvalue || 0;
          fn = fn instanceof Function ? fn : function (a,i) { return i+startvalue; };
          return String(new Array(this.valueOf())).split(',').map( fn );
        };

        Number.prototype.pretty = Number.prototype.pretty || function (usa, noprecision) {
            return sep1000(this, usa, noprecision);
        };

        // run functions sequentially
        Function.prototype.andThen = function () {
         var args = args2Array(arguments)
            ,next = args.slice(1);
         this();
         return args[0] && next.length
                 ? args[0].andThen.apply(args[0], next)
                 : args.length == 1  ? args[0]()
                 : true;
        };

        Function.prototype.partial = Function.prototype.partial || function () {
             var stored_args = [].slice.call(arguments)
                ,fn = this;
            return function () {
                return fn.apply(null, stored_args.concat([].slice.call(arguments)));
             };
        };

        Function.prototype.partialx = function(){
            var fn = this, args = Array.prototype.slice.call(arguments);
            return function(){
              var arg = 0;
              for ( var i = 0; i < args.length && arg < arguments.length; i++ )
                if ( args[i] === undefined || args[i] === null)
                  args[i] = arguments[arg++];
              return fn.apply(this, args);
            };
        };

        // is character @ [atpos] upperCase?
        boolean:String.prototype.charIsUpper = function (atpos){
          var chr = this.charAt(atpos);
          return /[A-Z]|[\u0080-\u024F]/.test(chr) && chr === chr.toUpperCase();
        };

        String.prototype.firstUp = function () {
          return this.slice(0,1).toUpperCase()+this.slice(1).toLowerCase();
        }

        String.prototype.isValidEmail = function() {
          // should be sufficient
          return  /^[\w._-]{1,}[+]?[\w._-]{0,}@[\w.-]+\.[a-zA-Z]{2,6}$/.test(this);
        }

        Array.prototype.toRE = function (){
          try { return RegExp.apply(null, this); }
          catch(e) { return /.*/; }
        }

        Array.toRE = function(arr) {
          return ([].toRE.call(arr));
        }

        Array.prototype.toCheckboxValues = Array.prototype.toCheckboxValues || function (checked) {
          checked = checked || [];
          var cbxs = [];
          this.map(function (a, i) {
                     cbxs.push({check: checked.indexOf(i)>-1 ? 1 : 0, val: String(a)});
                    }, cbxs);
          return cbxs;
        };

        String.prototype.reCleanup = function(encodeHTML){
          var str = encodeHTML ? this.replace(/[\u0080-\u024F]/g, function(a) {return '&#'+a.charCodeAt(0)+';';}) : this;
          return str.replace(/[?*|.+$\/]|\\/g, function(c) {return c==='\\' ? '' : '\\\\'+c;});
        }

        String.concat = function(joinchar){
          joinchar = joinchar || '';
          return args2Array(arguments).join(joinchar);
        }

        // MDN Array filter polyfill
        if (!Array.prototype.filter) {
          Array.prototype.filter = function(fun)
          {
            if (this === void 0 || this === null)
              throw new TypeError();

            var t = Object(this);
            var len = t.length >>> 0;
            if (typeof fun != "function")
              throw new TypeError();

            var res = [];
            var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
            for (var i = 0; i < len; i++) {
              if (i in t)  {
                var val = t[i];
                if (fun.call(thisArg, val, i, t))
                  res.push(val);
              }
            }

            return res;
          };
        }

        // MDN Array map polyfill
        if (!Array.prototype.map) {
          Array.prototype.map = function(fun)
          {
            if (this === void 0 || this === null)
              throw new TypeError();

            var t = Object(this);
            var len = t.length >>> 0;
            if (typeof fun !== "function")
              throw new TypeError();

            var res = new Array(len);
            var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
            for (var i = 0; i < len; i++) {
              if (i in t)
                res[i] = fun.call(thisArg, t[i], i, t);
            }

            return res;
        };
       }

       Array.prototype.uniquify = noDoubles;

       Array.prototype.frequencies = Array.prototype.frequencies || frequencies;

       Array.prototype.each = Array.prototype.each || function (fn,rewrite) {
          for (var i = 0; i < this.length; i++) {
           if (rewrite){
             this[i] = fn(this[i]);
           } else {
            fn(this[i]);
           }
          }
          return this;
       };

       // statics
       Object.print = function (obj, space) {
            space = space || '  ';
            return '
'+JSON.stringify(obj, null, space)+'
';
       }

       Object.format = function (obj, space) {
            space = space || '  ';
            return '
'+JSON.stringify(obj, null, space)+'
';
       }

       Object.isOneOf = isOfType;

       extended = true;
       return extended;
  }

  // SO specials
  function SOInit() {
    extensions();
    setCustomCss(true);
    var solink = d.querySelector('[data-linkid]');
    if (!solink.querySelector('.linkhover')) {
      solink.appendChild(createElementWithProps('div', { className: 'linkhover', 'data-dyn': 'true' } ));
    }
    loadJQ( jqcallback );

    function jqcallback() {
       $(document).on('mouseover', '.solink',  setSOLink);
       $(document).on('click', '[data-link]', clicklink);
    };
  }

  function setSOLink(e) {
      if ($(this).attr('data-link')) { return true; }

      $.ajax(
          {
              url: 'http://www.nicon.nl/node/stackx/questionx',
              data: {qid: $('.solink').first().attr('data-linkid')},
              method: 'post',
              success: SOcb
          }
      );

      function SOcb(data) {
          var resp = data.items && data.items[0] || data
          ,linkelement = $('.solink').first()
          ,linktip = linkelement.find('.linkhover').first()
          ;
          linktip.html(
              'Click logo to view the related question:

' + resp.title + '

' +
              'Asked by
'+ resp.owner.display_name + '
; '+
              'rep ' + resp.owner.reputation +'; question views: '+resp.view_count
          );
          linkelement.attr('data-link', resp.link);
      }

      return true;
  }

  // firefox needs a link added to the DOM, Chrome, IE don't
  function clicklink(e) {
      e.stopPropagation(); //just this link
      var linkurl = this.getAttribute('data-link')
         ,xlink = document.querySelector('a[href="'+linkurl+'"]') ||
                  function() {
                      var _link = createElementWithProps(
                                    'a', { href: linkurl, target:'_blank', style: { display: 'none' } }
                                  );
                      document.body.appendChild(_link);
                      return _link; }();
      xlink.click();
  }

  // Reporting
  // 1. simple
  function reportHTML() {
    var report = document.querySelector('#result') ||
        function() {
          var r = createElementWithProps( 'div', {id: 'result'} );
          document.querySelector('body').appendChild(r);
          return r;
        }();
    var entry = createElementWithProps('p');
    entry.innerHTML = args2Array(arguments).join('');
    report.appendChild(entry);

    if (useCustomCss) {
      var to = function () { entry.className = 'fadeIn'; };
      var dummy = setTimeout(to, 100);
    }
  }

  // 2. with parameter object usage
  function log2Screen() {
      var result = document.querySelector('#result') ||
                   function () {
                       var r = createElementWithProps('div', {id: 'result'});
                       document.body.appendChild(r);
                       return r; }()
         ,args = args2Array(arguments)
         ,lastarg = args.slice(-1)[0]
         ,optkeys = /clear|clrscr|direct|opts|useopts|continuous/i
         ,opts = Object.isOneOf(lastarg, Object) && Object.keys(lastarg).filter(function(v){return optkeys.test(v);}).length
                 ? lastarg.opts instanceof Object ? lastarg.opts : lastarg
                 : {empty: 1};
      void(!opts.empty && (args = args.slice(0,-1)));

      if (opts.clrscr) {
          return result.innerHTML = '';
      }

      if (opts.clear) {
          result.innerHTML = '';
      }

      if (opts.continuous) {
          return result.innerHTML += args.join('').replace(/\n/g,'
');
      }

      var p = createElementWithProps('p');
      p.innerHTML = args.join('').replace(/\n/g,'
');
      result.appendChild(p);
      return opts.direct ? (p.className = 'fadeIn')
                         : setTimeout(function () { p.className = 'fadeIn'; }, +opts.timed*1000 || 0);
  }

  function printDirect() {
    return log2Screen.apply( null, args2Array(arguments).concat({direct:true}) );
  }

  function screenClear() {
      var res = d.querySelector('#result');
      return  res && (res.innerHTML = '') || true;
  }

  // utilities
  function createElementWithProps(elType, props) {
    var el = d.createElement(elType);
    if (props && isOfType(props, Object)) {
      for (var l in props) {
        if (!props.hasOwnProperty(l)) continue;
        if (/style/i.test(l)) {
          for (var ll in props[l]) {
            if (!props[l].hasOwnProperty(ll)) continue;
            el.style[ll] = props[l][ll];
          }
        } else {
          el[l] = props[l];
        }
      }
    }
    return el;
  }

  function args2Array(args, sliceAt){
    var arr = [];
    while (arr.length < args.length) { arr.push(args[arr.length]); }
    return sliceAt ? arr.slice(sliceAt) : arr;
  }

  // see: http://codereview.stackexchange.com/questions/23317/istypeobj-gettypeobj-v0/23329#23329
  function isOfType(obj) {
    if (!obj) { return false; }
    var test = arguments.length ? args2Array(arguments).slice(1) : null
       ,self = obj.constructor;
    return test ? !!(test.filter(function(a){return a === self}).length)
           : (self.constructor.name ||
              (String(self).match ( /^function\s*([^\s(]+)/im)
                || [0,'ANONYMOUS_CONSTRUCTOR']) [1] );
  };

  function Partial (func) {
    this.initial = args2Array(arguments, 1);
    this.funcp = function () {
                 return func.partialx.apply(func, this.initial);
                };
    if (!Partial.prototype.x) {
      Partial.prototype.x = function () {
        return this.funcp().apply(null, args2Array(arguments));
      };
    }
  };

  function cloneObj(obj) {
    // clone the whole enchillada, recursive
    function clone(o, curr) {
        for (var l in o){
            if (!o.hasOwnProperty(l)) { continue; }
            if (o[l] instanceof Object) {
                curr[l] = cloneObj(o[l]);
            } else {
                curr[l] = o[l];
            }
        }
        return curr;
    }

    return obj instanceof Array
             ? obj.slice().map( function (v) { return cloneObj(v); } )
             : obj instanceof Date
               ? new Date(obj)
               : obj instanceof Object
                 ? clone(obj, {})
                 : obj;
  }

  function sep1000(somenum, usa, noprecision) {
    var dec = (''+somenum).split(/[.,]/)
       ,lendec = dec[1] ? dec[1].length : 0
       ,precision = lendec && !noprecision ? decPrecise(somenum,lendec) : dec[1]
       ,sep = usa ? ',' : '.'
       ,decsep = usa ? '.' : ',';

    // from http://stackoverflow.com/questions/10473994/javascript-adding-decimal-numbers-issue/10474209
    function decPrecise(d,l){
      return String(d.toFixed(12)).split(/[.,]/)[1].substr(0,l);
    }

    function xsep(num,sep) {
      var n = String(num).split('')
         ,i = -3;
      while (n.length + i > 0) {
          n.splice(i, 0, sep);
          i -= 4;
      }
      return n.join('');
    }
    return xsep(dec[0],sep) + (dec[1] ? decsep+precision :'');
  }

  return helperObj
}
<|endoftext|>


<commit_before>// use global count instead of jquery global event because it does't support cross domain request
window.__apiCount__ = 0
window.__apiCounter = {
  add () {
    window.__apiCount__++
  },
  remove () {
    window.__apiCount__--
  },
  get: function () {
    return window.__apiCount__
  }
}

// function getMessage (xhr) {
//   let message = ''
//   try {
//     let response = JSON.parse(xhr.responseText)
//     message = response.message || response.msg
//   } catch (e) {
//     message = xhr.responseText
//   }
//   if (xhr.status === 0 && xhr.statusText === 'timeout') message = '请求超时'
//   if (xhr.status === 200 && !message) message = xhr.statusText
//   return message
// }
// function handleError (errTextMap = {}, xhr, toast) {
//   let message = getMessage(xhr)
//   if (message) console.warn('Ajax: ' + message)
//   if (xhr.status !== 200 && toast) toast({message: errTextMap[xhr.status] || message || '请求失败', duration: 3500})
// }
// function handleSuccess (xhr, toast) {
//   let message = getMessage(xhr)
//   if (xhr.status === 200) {
//     if (toast) toast({ message: '请求成功' })
//   } else {
//     if (toast) {
//       toast({
//         message: '请求异常',
//         iconClass: 'ion-android-cancel'
//       })
//     }
//     if (message) console.warn('Ajax: ' + message)
//   }
// }

if (!window.__apiConfig__) window.__apiConfig__ = {}
if (!window.__apiRoot__) window.__apiRoot__ = null

function getUrl (modelName, modelConfig = {}, registerConfig = {}, config = {}) {
  let windowApiConfig = window.__apiConfig__[modelName]
  if (!windowApiConfig) windowApiConfig = {}
  let url = windowApiConfig.url ||
    config.url ||
    modelConfig.url ||
    registerConfig.url || ''
  let apiRoot = window.__apiRoot__ ||
    config.__apiRoot ||
    modelConfig.__apiRoot ||
    registerConfig.__apiRoot || ''
  let ignoreGlobalApiRoot = windowApiConfig.ignoreGlobalApiRoot ||
    config.__ignoreGlobalApiRoot ||
    modelConfig.__ignoreGlobalApiRoot ||
    registerConfig.__ignoreGlobalApiRoot
  return ignoreGlobalApiRoot ? url : (apiRoot + url)
}

let apiRegister = function (modelsArray = [], registerConfig = {}, jquery) {
  let $ = jquery
  // merge all models
  let models = {}
  modelsArray.forEach((m, i) => {
    for (let key in m) {
      let model = m[key]
        // test if there are api useing same name
      if (key in models) throw new Error(`model: api should not have same name "${key}".`)
      models[key] = model
    }
  })
  // globle config
  // $.ajaxSetup(Object.assign({}, models.__globlal || {}, {
  //   // reset data and event
  //   data: null,
  //   beforeSend: null,
  //   success: null,
  //   error: null,
  //   complete: null
  // }))
  let globalEvents = { // events set in __global and register should be add in this array
    beforeSend: [],
    success: [],
    error: [],
    complete: []
  }
  function fireGlobalEvents (funcArray, argsArray) {
    for (let i = 0; i < funcArray.length; i++) {
      if (typeof funcArray[i] === 'function') funcArray[i](...argsArray)
    }
  }
  function fireLocalEvent (localEvent, argsArray) {
    if (typeof localEvent === 'function') localEvent(...argsArray)
  }
  let startAndStopEvents = {
    ajaxStart: [],
    ajaxStop: []
  }
  function fireStartAndStopEvents (eventType, argsArray) {
    for (let i = 0; i < startAndStopEvents[eventType].length; i++) {
      if (typeof startAndStopEvents[eventType][i] === 'function') startAndStopEvents[eventType][i](...argsArray)
    }
  }
  let apis = {}
  for (let key in models) {
    if (!/^__/g.test(key)) {
      apis[key] = function (config = {}, triggerAjaxStartAndStopEvent = true, triggerGlobalEvents = true) {
        // merge event
        Object.keys(globalEvents).map(eventName => {
          if (registerConfig[eventName]) globalEvents[eventName].push(registerConfig[eventName])
          if (models.__globlal[eventName]) globalEvents[eventName].push(models.__globlal[eventName])
        })
        Object.keys(startAndStopEvents).map(eventName => {
          if (registerConfig[eventName]) startAndStopEvents[eventName].push(registerConfig[eventName])
          if (models.__globlal[eventName]) startAndStopEvents[eventName].push(models.__globlal[eventName])
        })

        let localEvents = {
          beforeSend: null,
          success: null,
          error: null,
          complete: null
        }
        Object.keys(localEvents).map(eventName => {
          localEvents[eventName] = config[eventName] || models[key][eventName]
        })
        // merge data
        let allData = Object.assign(
          {},
          registerConfig.data || {},
          models.__globlal.data || {},
          models[key].data || {},
          config.data || {})
        // merge config
        let allConfig = Object.assign(
          {},
          registerConfig || {},
          models[key] || {},
          config,
          {
            url: getUrl(key, models[key], registerConfig, config),
            data: allData,
            beforeSend (xhr) {
              if (triggerAjaxStartAndStopEvent) {
                window.__apiCounter.add()
                fireStartAndStopEvents('ajaxStart', [xhr])
              }
              if (triggerGlobalEvents) fireGlobalEvents(globalEvents.beforeSend, [xhr])
              fireLocalEvent(localEvents.beforeSend, [xhr])
            },
            success (data, statusText, xhr) {
              if (triggerGlobalEvents) fireGlobalEvents(globalEvents.success, [data, statusText, xhr])
              fireLocalEvent(localEvents.success, [data, statusText, xhr])
            },
            error (xhr, statusText, error) {
              if (triggerGlobalEvents) fireGlobalEvents(globalEvents.error, [xhr, statusText, error])
              fireLocalEvent(localEvents.error, [xhr, statusText, error])
            },
            complete (xhr, statusText) {
              if (triggerAjaxStartAndStopEvent) {
                window.__apiCounter.remove()
                fireStartAndStopEvents('ajaxStop', [xhr, statusText])
              }
              if (triggerGlobalEvents) fireGlobalEvents(globalEvents.complete, [xhr, statusText])
              fireLocalEvent(localEvents.complete, [xhr, statusText])
            }
          }
        )
        // format data when post application json
        if (allConfig.contentType && allConfig.contentType.toLowerCase().trim() === 'application/json') {
          allConfig.data = JSON.stringify(allData)
        }
        // if got window api config use the type in it
        if (window.__apiConfig__ && window.__apiConfig__[key] && window.__apiConfig__[key].type) {
          allConfig.type = window.__apiConfig__[key].type
        }
        return $.ajax(allConfig)
      }
    }
  }
  return apis
}

export default apiRegister
<commit_msg>change error throwing to console warning when having same api name in models<commit_after>// use global count instead of jquery global event because it does't support cross domain request
window.__apiCount__ = 0
window.__apiCounter = {
  add () {
    window.__apiCount__++
  },
  remove () {
    window.__apiCount__--
  },
  get: function () {
    return window.__apiCount__
  }
}

// function getMessage (xhr) {
//   let message = ''
//   try {
//     let response = JSON.parse(xhr.responseText)
//     message = response.message || response.msg
//   } catch (e) {
//     message = xhr.responseText
//   }
//   if (xhr.status === 0 && xhr.statusText === 'timeout') message = '请求超时'
//   if (xhr.status === 200 && !message) message = xhr.statusText
//   return message
// }
// function handleError (errTextMap = {}, xhr, toast) {
//   let message = getMessage(xhr)
//   if (message) console.warn('Ajax: ' + message)
//   if (xhr.status !== 200 && toast) toast({message: errTextMap[xhr.status] || message || '请求失败', duration: 3500})
// }
// function handleSuccess (xhr, toast) {
//   let message = getMessage(xhr)
//   if (xhr.status === 200) {
//     if (toast) toast({ message: '请求成功' })
//   } else {
//     if (toast) {
//       toast({
//         message: '请求异常',
//         iconClass: 'ion-android-cancel'
//       })
//     }
//     if (message) console.warn('Ajax: ' + message)
//   }
// }

if (!window.__apiConfig__) window.__apiConfig__ = {}
if (!window.__apiRoot__) window.__apiRoot__ = null

function getUrl (modelName, modelConfig = {}, registerConfig = {}, config = {}) {
  let windowApiConfig = window.__apiConfig__[modelName]
  if (!windowApiConfig) windowApiConfig = {}
  let url = windowApiConfig.url ||
    config.url ||
    modelConfig.url ||
    registerConfig.url || ''
  let apiRoot = window.__apiRoot__ ||
    config.__apiRoot ||
    modelConfig.__apiRoot ||
    registerConfig.__apiRoot || ''
  let ignoreGlobalApiRoot = windowApiConfig.ignoreGlobalApiRoot ||
    config.__ignoreGlobalApiRoot ||
    modelConfig.__ignoreGlobalApiRoot ||
    registerConfig.__ignoreGlobalApiRoot
  return ignoreGlobalApiRoot ? url : (apiRoot + url)
}

let apiRegister = function (modelsArray = [], registerConfig = {}, jquery) {
  let $ = jquery
  // merge all models
  let models = {}
  modelsArray.forEach((m, i) => {
    for (let key in m) {
      let model = m[key]
        // test if there are api useing same name
      // if (key in models) throw new Error(`model: api should not have same name "${key}".`)
      if (key in models) console.warn(`ajaxManager: api in models should not have same name "${key}".`)
      models[key] = model
    }
  })
  // globle config
  // $.ajaxSetup(Object.assign({}, models.__globlal || {}, {
  //   // reset data and event
  //   data: null,
  //   beforeSend: null,
  //   success: null,
  //   error: null,
  //   complete: null
  // }))
  let globalEvents = { // events set in __global and register should be add in this array
    beforeSend: [],
    success: [],
    error: [],
    complete: []
  }
  function fireGlobalEvents (funcArray, argsArray) {
    for (let i = 0; i < funcArray.length; i++) {
      if (typeof funcArray[i] === 'function') funcArray[i](...argsArray)
    }
  }
  function fireLocalEvent (localEvent, argsArray) {
    if (typeof localEvent === 'function') localEvent(...argsArray)
  }
  let startAndStopEvents = {
    ajaxStart: [],
    ajaxStop: []
  }
  function fireStartAndStopEvents (eventType, argsArray) {
    for (let i = 0; i < startAndStopEvents[eventType].length; i++) {
      if (typeof startAndStopEvents[eventType][i] === 'function') startAndStopEvents[eventType][i](...argsArray)
    }
  }
  let apis = {}
  for (let key in models) {
    if (!/^__/g.test(key)) {
      apis[key] = function (config = {}, triggerAjaxStartAndStopEvent = true, triggerGlobalEvents = true) {
        // merge event
        Object.keys(globalEvents).map(eventName => {
          if (registerConfig[eventName]) globalEvents[eventName].push(registerConfig[eventName])
          if (models.__globlal[eventName]) globalEvents[eventName].push(models.__globlal[eventName])
        })
        Object.keys(startAndStopEvents).map(eventName => {
          if (registerConfig[eventName]) startAndStopEvents[eventName].push(registerConfig[eventName])
          if (models.__globlal[eventName]) startAndStopEvents[eventName].push(models.__globlal[eventName])
        })

        let localEvents = {
          beforeSend: null,
          success: null,
          error: null,
          complete: null
        }
        Object.keys(localEvents).map(eventName => {
          localEvents[eventName] = config[eventName] || models[key][eventName]
        })
        // merge data
        let allData = Object.assign(
          {},
          registerConfig.data || {},
          models.__globlal.data || {},
          models[key].data || {},
          config.data || {})
        // merge config
        let allConfig = Object.assign(
          {},
          registerConfig || {},
          models[key] || {},
          config,
          {
            url: getUrl(key, models[key], registerConfig, config),
            data: allData,
            beforeSend (xhr) {
              if (triggerAjaxStartAndStopEvent) {
                window.__apiCounter.add()
                fireStartAndStopEvents('ajaxStart', [xhr])
              }
              if (triggerGlobalEvents) fireGlobalEvents(globalEvents.beforeSend, [xhr])
              fireLocalEvent(localEvents.beforeSend, [xhr])
            },
            success (data, statusText, xhr) {
              if (triggerGlobalEvents) fireGlobalEvents(globalEvents.success, [data, statusText, xhr])
              fireLocalEvent(localEvents.success, [data, statusText, xhr])
            },
            error (xhr, statusText, error) {
              if (triggerGlobalEvents) fireGlobalEvents(globalEvents.error, [xhr, statusText, error])
              fireLocalEvent(localEvents.error, [xhr, statusText, error])
            },
            complete (xhr, statusText) {
              if (triggerAjaxStartAndStopEvent) {
                window.__apiCounter.remove()
                fireStartAndStopEvents('ajaxStop', [xhr, statusText])
              }
              if (triggerGlobalEvents) fireGlobalEvents(globalEvents.complete, [xhr, statusText])
              fireLocalEvent(localEvents.complete, [xhr, statusText])
            }
          }
        )
        // format data when post application json
        if (allConfig.contentType && allConfig.contentType.toLowerCase().trim() === 'application/json') {
          allConfig.data = JSON.stringify(allData)
        }
        // if got window api config use the type in it
        if (window.__apiConfig__ && window.__apiConfig__[key] && window.__apiConfig__[key].type) {
          allConfig.type = window.__apiConfig__[key].type
        }
        return $.ajax(allConfig)
      }
    }
  }
  return apis
}

export default apiRegister
<|endoftext|>


<commit_before>'use strict';
angular.module('ngOboe', [])
        .service('Oboe', function(OboeStream) {
            // the passed parameters object need to have a Url and a pattern.
            // the url needs to return a json stream. see the oboe documentation
            // the patterns contains a path whoch selects json objects by the oboe factory
            return function(params) {
                // initialize the data
                var data = [];
                // add the data page by page using a stream
                OboeStream.search(params, function(page) {
                    // a page of records is received.
                    // add each record to the data
                    _.each(page, function(record) {
                        data.push(record);
                    });
                });
                return data;
            };
        })
        .factory('OboeStream', ['$q', function($q) {
                return {
                    // the search function calls the oboe module to get the JSON data in a stream
                    search: function(params, callback) {
                        // the defer will be resolved immediately
                        var defer = $q.defer();
                        var promise = defer.promise;
                        // counter for the received records
                        var counter = 0;
                        // I use an arbitrary page size.
                        var pagesize = 100;
                        // initialize the page of records
                        var page = [];
                        // call the oboe function to start the stream
                        oboe(params)
                                // once the stream starts we can resolve the defer.
                                .start(function() {
                                    defer.resolve();
                                })
                                // for every node containing the specified filter
                                .node(params.pattern, function(node) {
                                    //  we push the node to the page
                                    page.push(node);
                                    counter++;
                                    // if the pagesize is reached return the page using the promise
                                    if (counter % pagesize === 0) {
                                        promise.then(callback(page));
                                        // initialize the page
                                        page = [];
                                    }
                                })
                                .done(function() {
                                    // when the stream is done make surethe last page of nodes is returned
                                    promise.then(callback(page));
                                });
                        return promise;
                    }
                };
            }]);<commit_msg>changed path for main library<commit_after>'use strict';
angular.module('ngOboe', [])
        .service('Oboe', function(OboeStream) {
            // the passed parameters object need to have a Url and a pattern.
            // the url needs to return a json stream. see the oboe documentation
            // the patterns contains a path whoch selects json objects by the oboe factory
            return function(params) {
                // initialize the data
                var data = [];
                // add the data page by page using a stream
                OboeStream.search(params, function(page) {
                    // a page of records is received.
                    // add each record to the data
                    _.each(page, function(record) {
                        data.push(record);
                    });
                });
                return data;
            };
        })
        .factory('OboeStream', ['$q', function($q) {
                return {
                    // the search function calls the oboe module to get the JSON data in a stream
                    search: function(params, callback) {
                        // the defer will be resolved immediately
                        var defer = $q.defer();
                        var promise = defer.promise;
                        // counter for the received records
                        var counter = 0;
                        // I use an arbitrary page size.
                        var pagesize = 100;
                        // initialize the page of records
                        var page = [];
                        // call the oboe function to start the stream
                        oboe(params)
                                // once the stream starts we can resolve the defer.
                                .start(function() {
                                    defer.resolve();
                                })
                                // for every node containing the specified filter
                                .node(params.pattern, function(node) {
                                    //  we push the node to the page
                                    page.push(node);
                                    counter++;
                                    // if the pagesize is reached return the page using the promise
                                    if (counter % pagesize === 0) {
                                        promise.then(callback(page));
                                        // initialize the page
                                        page = [];
                                    }
                                })
                                .done(function() {
                                    // when the stream is done make sure the last page of nodes is returned
                                    promise.then(callback(page));
                                });
                        return promise;
                    }
                };
            }]);<|endoftext|>


<commit_before>(function(){
    angular.module('angularytics', []).provider('Angularytics', function() {

        var eventHandlersNames = ['Google'];
        this.setEventHandlers = function(handlers) {
            if (angular.isString(handlers)) {
                handlers = [handlers];
            }
            eventHandlersNames = [];
            angular.forEach(handlers, function(handler) {
                eventHandlersNames.push(capitalizeHandler(handler))
            });
        }

        var capitalizeHandler = function(handler) {
            return handler.charAt(0).toUpperCase() + handler.substring(1);
        }

        this.$get = function($injector, $rootScope, $location) {

            // Helper methods
            var eventHandlers = [];

            angular.forEach(eventHandlersNames, function(handler) {
                eventHandlers.push($injector.get('Angularytics' + handler + 'Handler'));
            });

            var forEachHandlerDo = function(action) {
                angular.forEach(eventHandlers, function(handler) {
                    action(handler);
                });
            }

            // Event listeing
            $rootScope.$on('$locationChangeSuccess', function() {
                forEachHandlerDo(function(handler) {
                    var url = $location.path();
                    if (url) {
                        handler.trackPageView(url);
                    }
                })
            });

            var service = {};
            // Just dummy function so that it's instantiated on app creation
            service.init = function() {

            }

            service.trackEvent = function(category, action, opt_label, opt_value, opt_noninteraction) {
                forEachHandlerDo(function(handler) {
                    if (category && action) {
                        handler.trackEvent(category, action, opt_label, opt_value, opt_noninteraction);
                    }
                });
            }

            return service;

        };

    });
})();
<commit_msg>Add AngularyticsProvider.setPageChangeEvent()<commit_after>(function(){
    angular.module('angularytics', []).provider('Angularytics', function() {

        var eventHandlersNames = ['Google'];
        this.setEventHandlers = function(handlers) {
            if (angular.isString(handlers)) {
                handlers = [handlers];
            }
            eventHandlersNames = [];
            angular.forEach(handlers, function(handler) {
                eventHandlersNames.push(capitalizeHandler(handler))
            });
        }

        var capitalizeHandler = function(handler) {
            return handler.charAt(0).toUpperCase() + handler.substring(1);
        }

        var pageChangeEvent = '$locationChangeSuccess';
        this.setPageChangeEvent = function(newPageChangeEvent) {
          pageChangeEvent = newPageChangeEvent;
        }

        this.$get = function($injector, $rootScope, $location) {

            // Helper methods
            var eventHandlers = [];

            angular.forEach(eventHandlersNames, function(handler) {
                eventHandlers.push($injector.get('Angularytics' + handler + 'Handler'));
            });

            var forEachHandlerDo = function(action) {
                angular.forEach(eventHandlers, function(handler) {
                    action(handler);
                });
            }

            // Event listeing
            $rootScope.$on(pageChangeEvent, function() {
                forEachHandlerDo(function(handler) {
                    var url = $location.path();
                    if (url) {
                        handler.trackPageView(url);
                    }
                })
            });

            var service = {};
            // Just dummy function so that it's instantiated on app creation
            service.init = function() {

            }

            service.trackEvent = function(category, action, opt_label, opt_value, opt_noninteraction) {
                forEachHandlerDo(function(handler) {
                    if (category && action) {
                        handler.trackEvent(category, action, opt_label, opt_value, opt_noninteraction);
                    }
                });
            }

            return service;

        };

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