<commit_before>// @flow
 /* flowlint
  *   untyped-import:off
  */

import * as React from 'react';
import { useEffect, useState } from 'react';
import get from 'lodash/get';
import zip from 'lodash/zip';

import queryString from 'query-string';

import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import type { AppState } from 'store';
import type { PerfDataState } from 'store/perfdata/reducer';
import type { InitialProps } from 'components/utils';
import { t } from 'i18next';
import Spinner from '@salesforce/design-system-react/components/spinner';
import DataTable from '@salesforce/design-system-react/components/data-table';
import DataTableColumn from '@salesforce/design-system-react/components/data-table/column';
import DataTableCell from '@salesforce/design-system-react/components/data-table/cell';

import FieldPicker from './fieldPicker';
import FilterPicker from './filterPicker';
import PerfTableOptionsUI from './perfTableOptionsUI';

import { perfRESTFetch, perfREST_UI_Fetch } from 'store/perfdata/actions';

import { selectPerfState, selectPerf_UI_State } from 'store/perfdata/selectors';

const DEFAULT_COLUMNS = ["Method Name", "Duration"];

export const ShowRenderTime = () => null
//
{(new Date()).toString()}

export class QueryParamHelpers{
  default_params: { [string]: mixed };

  constructor(default_params:{[string]: mixed}) {
    this.default_params = default_params;
  }

   get = (name?:string) => {
    let parts = { ...this.default_params, ...queryString.parse(window.location.search)};

    if(name){
      return parts[name];
    }else{
      return parts;
    }
  }

  set = (newQueryParts: {[string] : string | string[] | null | typeof undefined}) => {
    let qs = queryString.stringify({...this.get(), ...newQueryParts});
    window.history.pushState(null, "", window.location.pathname+"?"+qs);
  };
}

/**
 * Add iDs to table values for consumption by the SLDS DataTable
 * @param {*} rows hashes from database
 */
const addIds = (rows : {}[]) => {
  return rows.map((row, index)=>{return {...row, id: index.toString()}})
}

const PerfDataTableSpinner = ({status}) => {
  if(status === "PERF_DATA_LOADING"){
    return <Spinner
      size="small"
      variant="base"
      assistiveText={{ label: 'Small spinner' }}
    />
  }
  return null;
}

let default_query_params = {page_size: 10, include_fields : ["repo", "duration_average"]};
export const queryparams = new QueryParamHelpers(default_query_params);

type ReduxProps = {doPerfRESTFetch: Function,
                  doPerfREST_UI_Fetch: Function,
                  perfdatastate: PerfDataState,
                  perfdataUIstate:{}};
type SelfProps = {default_columns: string[]};

export const UnwrappedPerfTable = ({doPerfRESTFetch, doPerfREST_UI_Fetch,
                          perfdatastate, perfdataUIstate,
                          match, location, history, default_columns }:
                              ReduxProps & InitialProps & SelfProps) => {
    default_columns = default_columns || DEFAULT_COLUMNS;
  if ((perfdataUIstate && perfdataUIstate.status === "UI_DATA_ERROR") ||
    (perfdatastate && perfdatastate.status === "PERF_DATA_ERROR")){
      return <AuthError/>
    }

    let queryParts = queryparams.get;
    let changeUrl = queryparams.set;

    useEffect(() => {
      doPerfRESTFetch(null, queryParts());
      doPerfREST_UI_Fetch();
      let pathParts = window.location.pathname.split("/");

      /* Special case for getting repo name from URL
        * path into query params with other filters
        */
      changeUrl({repo: pathParts[pathParts.length-2]})
    }, []);

    var items;
    if(perfdatastate && perfdatastate.perfdata){
      items = addIds(perfdatastate.perfdata.results);
    }else{
      items = [];
    }

    // its okay to pass null or undefined because query-string has reasonable
    // and useful interpretations of both of them.
    const fetchServerData = (params?: {[string]: string | string[] | null | typeof undefined}) => {
      changeUrl({...queryParts(), ...params});
      doPerfRESTFetch(null, queryParts());
    }

    /*
     * Extract the page from the server-generated URL
     *
     * TODO: this code may be obsolete as of March 27
     *
     */
    const goPageFromUrl = (url: string) => {
      var qs = url.split("?", 2)[1];
      var qParts = queryString.parse(qs);
      var page = qParts["page"];
      if(Array.isArray(page) ){
        fetchServerData({page: page[1]});
      }else if(typeof page === 'string' ){
        fetchServerData({page});
      }else{
        fetchServerData({page: undefined});
      }
    };

    var page = parseInt(queryParts()["page"]||"1") - 1;
    var custom_page_size = queryParts("page_size");
    var page_size = custom_page_size ? parseInt(custom_page_size) :
                        get(perfdatastate, "perfdata.results.length") || -1;
    var previousPage:number|null = get(perfdatastate, "perfdata.previous");
    var nextPage:number|null = get(perfdatastate, "perfdata.next")

    /* https://appexchange.salesforce.com/listingDetail?listingId=a0N3A00000E9TBZUA3 */
    const PerfDataTableFooter = () => {
      return (

        { items.length>0 && perfdatastate && perfdatastate.perfdata &&
          <React.Fragment>

                          Showing {(page * page_size).toString()} to {' '}
                                {Math.min((page + 1) * page_size, 1).toString()} {' '}
                          of  {get(perfdatastate, "perfdata.count").toString()} records

                      <button onClick={()=>doPerfRESTFetch(previousPage)}
                        className="slds-button slds-button--brand"
                        disabled={!previousPage}>Previous</button>
                      <button onClick={()=>doPerfRESTFetch(nextPage)}
                        className="slds-button slds-button--brand"
                        disabled={!nextPage}>Next</button>

          </React.Fragment>
        }

    )}

    const columns = () => {
      let columns;
      if(items.length>0){
        let columnNames = Object.keys(items[0]).filter((item)=>item!="id");
        return zip(columnNames, columnNames);
      }else{
        return zip(default_columns, default_columns)
      }
    }

    const PerfDataColumns = () => {
      return (
          columns().map(([name, label])=>
          {

            let isSorted:bool = false, sortDirection:string|null = null;

            if(queryParts()["o"]===name){
              isSorted = true;
              sortDirection = "asc";
            }else if(queryParts()["o"]==="-"+name){
              isSorted = true;
              sortDirection = "desc";
            }

            return  <DataTableColumn
                          key={name}
                          label={label}
                          property={name}
                          sortable
                          isSorted={isSorted}
                          sortDirection={sortDirection}
                        ></DataTableColumn>
          }
      ))
    }

    const doSort = (sortColumn, ...rest) => {
      var sortProperty = sortColumn.property;
      const sortDirection = sortColumn.sortDirection;

      if (sortDirection === 'desc') {
        sortProperty = "-" + sortProperty
      }
      fetchServerData({o: sortProperty, page: '1'});
    };

    return

      <ShowRenderTime/>
      <PerfTableOptionsUI
          fetchServerData={fetchServerData}
          key="thePerfAccordian"/>

            <ShowRenderTime/>
            <PerfDataTableSpinner status={get(perfdatastate, "status")}/>
            <DataTable items={items}
                fixedLayout={true}
                onSort={doSort}
              id="perfDataTable">
            {PerfDataColumns()}
            </DataTable>
            <PerfDataTableFooter/>

  };

const AuthError = () => {
  return

Please ensure you are on the VPN and logged in to view this data.

}

const select = (appState: AppState) => {
  return {
    perfdatastate: selectPerfState(appState),
    perfdataUIstate: selectPerf_UI_State(appState),
  }};

const actions = {
  doPerfRESTFetch: (url, queryparts) => perfRESTFetch(url || "/api/testmethod_perf?", queryparts),
  doPerfREST_UI_Fetch: perfREST_UI_Fetch,
};

const WrappedPerfTable: React.ComponentType<{}> = withRouter(connect(select, actions)(
  UnwrappedPerfTable,
));

export default WrappedPerfTable;<commit_msg>Clean up auth design.<commit_after>// @flow
 /* flowlint
  *   untyped-import:off
  */

import * as React from 'react';
import { useEffect, useState } from 'react';
import get from 'lodash/get';
import zip from 'lodash/zip';

import queryString from 'query-string';

import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import type { AppState } from 'store';
import type { PerfDataState } from 'store/perfdata/reducer';
import type { InitialProps } from 'components/utils';
import { t } from 'i18next';
import Spinner from '@salesforce/design-system-react/components/spinner';
import DataTable from '@salesforce/design-system-react/components/data-table';
import DataTableColumn from '@salesforce/design-system-react/components/data-table/column';
import DataTableCell from '@salesforce/design-system-react/components/data-table/cell';
import BrandBand from '@salesforce/design-system-react/components/brand-band';

import FieldPicker from './fieldPicker';
import FilterPicker from './filterPicker';
import PerfTableOptionsUI from './perfTableOptionsUI';

import { perfRESTFetch, perfREST_UI_Fetch } from 'store/perfdata/actions';

import { selectPerfState, selectPerf_UI_State } from 'store/perfdata/selectors';

const DEFAULT_COLUMNS = ["Method Name", "Duration"];

export const ShowRenderTime = () => null
//
{(new Date()).toString()}

export class QueryParamHelpers{
  default_params: { [string]: mixed };

  constructor(default_params:{[string]: mixed}) {
    this.default_params = default_params;
  }

   get = (name?:string) => {
    let parts = { ...this.default_params, ...queryString.parse(window.location.search)};

    if(name){
      return parts[name];
    }else{
      return parts;
    }
  }

  set = (newQueryParts: {[string] : string | string[] | null | typeof undefined}) => {
    let qs = queryString.stringify({...this.get(), ...newQueryParts});
    window.history.pushState(null, "", window.location.pathname+"?"+qs);
  };
}

/**
 * Add iDs to table values for consumption by the SLDS DataTable
 * @param {*} rows hashes from database
 */
const addIds = (rows : {}[]) => {
  return rows.map((row, index)=>{return {...row, id: index.toString()}})
}

const PerfDataTableSpinner = ({status}) => {
  if(status === "PERF_DATA_LOADING"){
    return <Spinner
      size="small"
      variant="base"
      assistiveText={{ label: 'Small spinner' }}
    />
  }
  return null;
}

let default_query_params = {page_size: 10, include_fields : ["repo", "duration_average"]};
export const queryparams = new QueryParamHelpers(default_query_params);

type ReduxProps = {doPerfRESTFetch: Function,
                  doPerfREST_UI_Fetch: Function,
                  perfdatastate: PerfDataState,
                  perfdataUIstate:{}};
type SelfProps = {default_columns: string[]};

export const UnwrappedPerfTable = ({doPerfRESTFetch, doPerfREST_UI_Fetch,
                          perfdatastate, perfdataUIstate,
                          match, location, history, default_columns }:
                              ReduxProps & InitialProps & SelfProps) => {
    default_columns = default_columns || DEFAULT_COLUMNS;
  if ((perfdataUIstate && perfdataUIstate.status === "UI_DATA_ERROR") ||
    (perfdatastate && perfdatastate.status === "PERF_DATA_ERROR")){
    return <AuthError
        message= "Top Secret! Please ensure you are on the VPN and logged in to MetaCI."/>
    }

    let queryParts = queryparams.get;
    let changeUrl = queryparams.set;

    useEffect(() => {
      doPerfRESTFetch(null, queryParts());
      doPerfREST_UI_Fetch();
      let pathParts = window.location.pathname.split("/");

      /* Special case for getting repo name from URL
        * path into query params with other filters
        */
      changeUrl({repo: pathParts[pathParts.length-2]})
    }, []);

    var items;
    if(perfdatastate && perfdatastate.perfdata){
      items = addIds(perfdatastate.perfdata.results);
    }else{
      items = [];
    }

    // its okay to pass null or undefined because query-string has reasonable
    // and useful interpretations of both of them.
    const fetchServerData = (params?: {[string]: string | string[] | null | typeof undefined}) => {
      changeUrl({...queryParts(), ...params});
      doPerfRESTFetch(null, queryParts());
    }

    /*
     * Extract the page from the server-generated URL
     *
     * TODO: this code may be obsolete as of March 27
     *
     */
    const goPageFromUrl = (url: string) => {
      var qs = url.split("?", 2)[1];
      var qParts = queryString.parse(qs);
      var page = qParts["page"];
      if(Array.isArray(page) ){
        fetchServerData({page: page[1]});
      }else if(typeof page === 'string' ){
        fetchServerData({page});
      }else{
        fetchServerData({page: undefined});
      }
    };

    var page = parseInt(queryParts()["page"]||"1") - 1;
    var custom_page_size = queryParts("page_size");
    var page_size = custom_page_size ? parseInt(custom_page_size) :
                        get(perfdatastate, "perfdata.results.length") || -1;
    var previousPage:number|null = get(perfdatastate, "perfdata.previous");
    var nextPage:number|null = get(perfdatastate, "perfdata.next")

    /* https://appexchange.salesforce.com/listingDetail?listingId=a0N3A00000E9TBZUA3 */
    const PerfDataTableFooter = () => {
      return (

        { items.length>0 && perfdatastate && perfdatastate.perfdata &&
          <React.Fragment>

                          Showing {(page * page_size).toString()} to {' '}
                                {Math.min((page + 1) * page_size, 1).toString()} {' '}
                          of  {get(perfdatastate, "perfdata.count").toString()} records

                      <button onClick={()=>doPerfRESTFetch(previousPage)}
                        className="slds-button slds-button--brand"
                        disabled={!previousPage}>Previous</button>
                      <button onClick={()=>doPerfRESTFetch(nextPage)}
                        className="slds-button slds-button--brand"
                        disabled={!nextPage}>Next</button>

          </React.Fragment>
        }

    )}

    const columns = () => {
      let columns;
      if(items.length>0){
        let columnNames = Object.keys(items[0]).filter((item)=>item!="id");
        return zip(columnNames, columnNames);
      }else{
        return zip(default_columns, default_columns)
      }
    }

    const PerfDataColumns = () => {
      return (
          columns().map(([name, label])=>
          {

            let isSorted:bool = false, sortDirection:string|null = null;

            if(queryParts()["o"]===name){
              isSorted = true;
              sortDirection = "asc";
            }else if(queryParts()["o"]==="-"+name){
              isSorted = true;
              sortDirection = "desc";
            }

            return  <DataTableColumn
                          key={name}
                          label={label}
                          property={name}
                          sortable
                          isSorted={isSorted}
                          sortDirection={sortDirection}
                        ></DataTableColumn>
          }
      ))
    }

    const doSort = (sortColumn, ...rest) => {
      var sortProperty = sortColumn.property;
      const sortDirection = sortColumn.sortDirection;

      if (sortDirection === 'desc') {
        sortProperty = "-" + sortProperty
      }
      fetchServerData({o: sortProperty, page: '1'});
    };

    return

      <ShowRenderTime/>
      <PerfTableOptionsUI
          fetchServerData={fetchServerData}
          key="thePerfAccordian"/>

            <ShowRenderTime/>
            <PerfDataTableSpinner status={get(perfdatastate, "status")}/>
            <DataTable items={items}
                fixedLayout={true}
                onSort={doSort}
              id="perfDataTable">
            {PerfDataColumns()}
            </DataTable>
            <PerfDataTableFooter/>

  };

const AuthError = ({message}:{message:string}) => {
  return <BrandBand
      id="brand-band-lightning-blue"
      className="slds-p-around_small"
      theme="lightning-blue"
      style={{textAlign:"center"}}
    >

{message}

    </BrandBand>
}

const select = (appState: AppState) => {
  return {
    perfdatastate: selectPerfState(appState),
    perfdataUIstate: selectPerf_UI_State(appState),
  }};

const actions = {
  doPerfRESTFetch: (url, queryparts) => perfRESTFetch(url || "/api/testmethod_perf?", queryparts),
  doPerfREST_UI_Fetch: perfREST_UI_Fetch,
};

const WrappedPerfTable: React.ComponentType<{}> = withRouter(connect(select, actions)(
  UnwrappedPerfTable,
));

export default WrappedPerfTable;<|endoftext|>


<commit_before>/* global require */
module.exports = function (grunt) {
  require('jit-grunt')(grunt);

  // Scripts to be concatenated and minified (in load order)
  // Use un-minified scripts if possible for more meaningful stack traces in development
  var scripts = [
    'app/bower_components/webmaker-analytics/analytics.js',
    'app/bower_components/eventEmitter/EventEmitter.js',
    'app/bower_components/webmaker-auth-client/dist/webmaker-auth-client.min.js',
    'app/bower_components/momentjs/min/moment-with-langs.js',
    'app/bower_components/jquery/dist/jquery.js',
    'app/bower_components/selectize/dist/js/standalone/selectize.js',
    'app/bower_components/makeapi-client/src/make-api.js',
    'app/bower_components/angular/angular.js',
    'app/bower_components/angular-paginate-anything/src/paginate-anything.js',
    'app/bower_components/angular-sanitize/angular-sanitize.js',
    'app/bower_components/angular-resource/angular-resource.js',
    'app/bower_components/angular-route/angular-route.js',
    'app/bower_components/angular-animate/angular-animate.js',
    'app/bower_components/angular-bootstrap/ui-bootstrap.js',
    'app/bower_components/angular-bootstrap/ui-bootstrap-tpls.js',
    'app/bower_components/makeapi-angular/dist/makeapi-angular.templates.js',
    'app/bower_components/makeapi-angular/dist/makeapi-angular.js',

    // Be sure to use proper array style dependency injection for these Angular scripts
    // Improper dep injection will result in broken code post-compression
    'app/js/app.js',
    'app/js/services.js',
    'app/js/controllers.js',
    'app/js/filters.js',
    'app/js/directives.js',
    'app/js/i18n.js'
  ];

  // Generate HTML to include individual scripts for local development
  var scriptIncludes = '';

  scripts.forEach(function (script) {
    script = script.replace('app/', '');
    scriptIncludes += '<script src="/' + script + '"></script>';
  });

  grunt.initConfig({
    less: {
      development: {
        files: {
          'app/compiled/app.ltr.css': 'app/less/app.less'
        },
        options: {
          sourceMap: true,
          sourceMapBasepath: 'app',
          sourceMapRootpath: '/'
        }
      },
      production: {
        files: {
          'app/compiled/app.ltr.css': 'app/less/app.less'
        }
      }
    },
    watch: {
      less: {
        files: ['app/less/**/*.less', 'bower.json'],
        tasks: ['less:development'],
        options: {
          livereload: true,
          spawn: false
        }
      },
      assets: {
        files: ['app/img/**/*', 'app/js/**/*.js', 'app/views/**/*.html'],
        options: {
          livereload: true,
          spawn: false
        }
      },
      server: {
        files: ['server/**/*', 'package.json'],
        tasks: ['shell:runServer']
      }
    },
    cssjanus: {
      'app/compiled/app.rtl.uncss.css': 'app/compiled/app.ltr.uncss.css',
      options: {
        swapLtrRtlInUrl: true,
        swapLeftRightInUrl: false,
        generateExactDuplicates: false
      }
    },
    shell: {
      runServer: {
        options: {
          async: true
        },
        command: 'node server/server.js'
      },
      runTests: {
        command: './test/test.sh'
      }
    },
    jsonlint: {
      json: {
        src: [
          'bower.json',
          'package.json',
          'locale/**/*.json'
        ]
      }
    },
    jshint: {
      all: ['Gruntfile.js', 'app/js/**/*.js'],
      options: {
        ignores: ['app/js/lib/**/*.js'],
        jshintrc: '.jshintrc'
      }
    },
    jsbeautifier: {
      modify: {
        src: ['Gruntfile.js', 'app/js/**/*.js', '!app/js/lib/**/*.js'],
        options: {
          config: '.jsbeautifyrc'
        }
      },
      validate: {
        src: ['Gruntfile.js', 'app/js/**/*.js', '!app/js/lib/**/*.js'],
        options: {
          mode: 'VERIFY_ONLY',
          config: '.jsbeautifyrc'
        }
      }
    },
    angular_i18n_finder: {
      files: ['app/index.html', 'app/views/*.html', 'app/views/**/*.html'],
      options: {
        pathToJSON: ['locale/en_US/*.json'],
        ignoreKeys: []
      }
    },
    cssmin: {
      options: {
        keepSpecialComments: 0
      },
      ltr: {
        src: 'app/compiled/app.ltr.uncss.css',
        dest: 'app/compiled/app.ltr.uncss.min.css'
      },
      rtl: {
        src: 'app/compiled/app.rtl.uncss.css',
        dest: 'app/compiled/app.rtl.uncss.min.css'
      }
    },
    uncss: {
      production: {
        options: {
          ignore: ['.pagination > .active > a', /\.multicolor-header\.color-[0-9]/, /we-rsvp.*/, /\.dropdown-menu/, /\.navbar/],
          stylesheets: ['compiled/app.ltr.css'],
          urls: ['http://localhost:1981/', 'http://localhost:1981/#/events'] // Deprecated
        },
        files: {
          'app/compiled/app.ltr.uncss.css': ['app/index.html', 'app/views/**/*.html']
        }
      }
    },
    'string-replace': {
      production: {
        files: {
          'app/index.html': 'app/index.template'
        },
        options: {
          replacements: [{
            pattern: '%_EXTENSIONS_%',
            replacement: '.uncss.min'
          }, {
            pattern: '%_LIVE_RELOAD_%',
            replacement: ''
          }]
        }
      },
      development: {
        files: {
          'app/index.html': 'app/index.template'
        },
        options: {
          replacements: [{
            pattern: '%_EXTENSIONS_%',
            replacement: ''
          }, {
            pattern: '<script src="/compiled/app.min.js"></script>',
            replacement: scriptIncludes
          }, {
            pattern: '%_LIVE_RELOAD_%',
            replacement: '<script src="//localhost:35729/livereload.js"></script>'
          }]
        }
      }
    },
    uglify: {
      options: {
        sourceMap: true
      },
      production: {
        files: {
          'app/compiled/app.min.js': scripts
        }
      }
    }
  });

  grunt.loadNpmTasks('grunt-shell-spawn');

  // Run in "dev mode"
  grunt.registerTask('default', [
    'string-replace:development',
    'shell:runServer',
    'less:development',
    'watch'
  ]);

  // Build project for deployment to production
  grunt.registerTask('build', [
    'string-replace:production',
    'shell:runServer',
    'less:production',
    'uncss',
    'cssjanus',
    'cssmin',
    'uglify',
    'shell:runServer:kill'
  ]);

  // Clean code before a commit
  grunt.registerTask('clean', [
    'jsbeautifier:modify',
    'jsonlint',
    'jshint',
    'angular_i18n_finder'
  ]);

  // Validate code before commit (read only)
  grunt.registerTask('validate', [
    'jsbeautifier:validate',
    'jsonlint',
    'jshint'
  ]);

  // Testing
  grunt.registerTask('test', [
    'shell:runTests'
  ]);

};
<commit_msg>fixing uncss build: adding missing css rules<commit_after>/* global require */
module.exports = function (grunt) {
  require('jit-grunt')(grunt);

  // Scripts to be concatenated and minified (in load order)
  // Use un-minified scripts if possible for more meaningful stack traces in development
  var scripts = [
    'app/bower_components/webmaker-analytics/analytics.js',
    'app/bower_components/eventEmitter/EventEmitter.js',
    'app/bower_components/webmaker-auth-client/dist/webmaker-auth-client.min.js',
    'app/bower_components/momentjs/min/moment-with-langs.js',
    'app/bower_components/jquery/dist/jquery.js',
    'app/bower_components/selectize/dist/js/standalone/selectize.js',
    'app/bower_components/makeapi-client/src/make-api.js',
    'app/bower_components/angular/angular.js',
    'app/bower_components/angular-paginate-anything/src/paginate-anything.js',
    'app/bower_components/angular-sanitize/angular-sanitize.js',
    'app/bower_components/angular-resource/angular-resource.js',
    'app/bower_components/angular-route/angular-route.js',
    'app/bower_components/angular-animate/angular-animate.js',
    'app/bower_components/angular-bootstrap/ui-bootstrap.js',
    'app/bower_components/angular-bootstrap/ui-bootstrap-tpls.js',
    'app/bower_components/makeapi-angular/dist/makeapi-angular.templates.js',
    'app/bower_components/makeapi-angular/dist/makeapi-angular.js',

    // Be sure to use proper array style dependency injection for these Angular scripts
    // Improper dep injection will result in broken code post-compression
    'app/js/app.js',
    'app/js/services.js',
    'app/js/controllers.js',
    'app/js/filters.js',
    'app/js/directives.js',
    'app/js/i18n.js'
  ];

  // Generate HTML to include individual scripts for local development
  var scriptIncludes = '';

  scripts.forEach(function (script) {
    script = script.replace('app/', '');
    scriptIncludes += '<script src="/' + script + '"></script>';
  });

  grunt.initConfig({
    less: {
      development: {
        files: {
          'app/compiled/app.ltr.css': 'app/less/app.less'
        },
        options: {
          sourceMap: true,
          sourceMapBasepath: 'app',
          sourceMapRootpath: '/'
        }
      },
      production: {
        files: {
          'app/compiled/app.ltr.css': 'app/less/app.less'
        }
      }
    },
    watch: {
      less: {
        files: ['app/less/**/*.less', 'bower.json'],
        tasks: ['less:development'],
        options: {
          livereload: true,
          spawn: false
        }
      },
      assets: {
        files: ['app/img/**/*', 'app/js/**/*.js', 'app/views/**/*.html'],
        options: {
          livereload: true,
          spawn: false
        }
      },
      server: {
        files: ['server/**/*', 'package.json'],
        tasks: ['shell:runServer']
      }
    },
    cssjanus: {
      'app/compiled/app.rtl.uncss.css': 'app/compiled/app.ltr.uncss.css',
      options: {
        swapLtrRtlInUrl: true,
        swapLeftRightInUrl: false,
        generateExactDuplicates: false
      }
    },
    shell: {
      runServer: {
        options: {
          async: true
        },
        command: 'node server/server.js'
      },
      runTests: {
        command: './test/test.sh'
      }
    },
    jsonlint: {
      json: {
        src: [
          'bower.json',
          'package.json',
          'locale/**/*.json'
        ]
      }
    },
    jshint: {
      all: ['Gruntfile.js', 'app/js/**/*.js'],
      options: {
        ignores: ['app/js/lib/**/*.js'],
        jshintrc: '.jshintrc'
      }
    },
    jsbeautifier: {
      modify: {
        src: ['Gruntfile.js', 'app/js/**/*.js', '!app/js/lib/**/*.js'],
        options: {
          config: '.jsbeautifyrc'
        }
      },
      validate: {
        src: ['Gruntfile.js', 'app/js/**/*.js', '!app/js/lib/**/*.js'],
        options: {
          mode: 'VERIFY_ONLY',
          config: '.jsbeautifyrc'
        }
      }
    },
    angular_i18n_finder: {
      files: ['app/index.html', 'app/views/*.html', 'app/views/**/*.html'],
      options: {
        pathToJSON: ['locale/en_US/*.json'],
        ignoreKeys: []
      }
    },
    cssmin: {
      options: {
        keepSpecialComments: 0
      },
      ltr: {
        src: 'app/compiled/app.ltr.uncss.css',
        dest: 'app/compiled/app.ltr.uncss.min.css'
      },
      rtl: {
        src: 'app/compiled/app.rtl.uncss.css',
        dest: 'app/compiled/app.rtl.uncss.min.css'
      }
    },
    uncss: {
      production: {
        options: {
          ignore: [
            '.pagination > .active > a',
            /\.multicolor-header\.color-[0-9]/,
            /we-rsvp.*/,
            /\.dropdown-menu/,
            /\.navbar/,
            /(\[ng|\[data-ng|\[x-ng|\.ng|\.x).*/, // Angular bizness
            /\.listing-home.*/
          ],
          stylesheets: ['compiled/app.ltr.css']
        },
        files: {
          'app/compiled/app.ltr.uncss.css': ['app/index.html', 'app/views/**/*.html']
        }
      }
    },
    'string-replace': {
      production: {
        files: {
          'app/index.html': 'app/index.template'
        },
        options: {
          replacements: [{
            pattern: '%_EXTENSIONS_%',
            replacement: '.uncss.min'
          }, {
            pattern: '%_LIVE_RELOAD_%',
            replacement: ''
          }]
        }
      },
      development: {
        files: {
          'app/index.html': 'app/index.template'
        },
        options: {
          replacements: [{
            pattern: '%_EXTENSIONS_%',
            replacement: ''
          }, {
            pattern: '<script src="/compiled/app.min.js"></script>',
            replacement: scriptIncludes
          }, {
            pattern: '%_LIVE_RELOAD_%',
            replacement: '<script src="//localhost:35729/livereload.js"></script>'
          }]
        }
      }
    },
    uglify: {
      options: {
        sourceMap: true
      },
      production: {
        files: {
          'app/compiled/app.min.js': scripts
        }
      }
    }
  });

  grunt.loadNpmTasks('grunt-shell-spawn');

  // Run in "dev mode"
  grunt.registerTask('default', [
    'string-replace:development',
    'shell:runServer',
    'less:development',
    'watch'
  ]);

  // Build project for deployment to production
  grunt.registerTask('build', [
    'string-replace:production',
    'shell:runServer',
    'less:production',
    'uncss',
    'cssjanus',
    'cssmin',
    'uglify',
    'shell:runServer:kill'
  ]);

  // Clean code before a commit
  grunt.registerTask('clean', [
    'jsbeautifier:modify',
    'jsonlint',
    'jshint',
    'angular_i18n_finder'
  ]);

  // Validate code before commit (read only)
  grunt.registerTask('validate', [
    'jsbeautifier:validate',
    'jsonlint',
    'jshint'
  ]);

  // Testing
  grunt.registerTask('test', [
    'shell:runTests'
  ]);

};
<|endoftext|>


<commit_before>module.exports = function (grunt) {
  // * Read command-line switches
  // - Read in --browsers CLI option; split it on commas into an array if it's a string, otherwise ignore it
  var browsers = typeof grunt.option('browsers') == 'string' ? grunt.option('browsers').split(',') : undefined;

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    library: grunt.file.readJSON('bower.json'),
    concat: {
      options: {
        separator: ''
      },
      library: {
        src: [
          'src/**/*.js'
        ],
        dest: 'dest/<%= library.name %>.js'
      }
    },
    uglify: {
      options: {
        banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
      },
      jid: {
        files: {
          'dest/<%= library.name %>.min.js': ['<%= concat.library.dest %>']
        }
      }
    },
    jshint: {
      beforeConcat: {
        src: ['gruntfile.js', '<%= library.name %>/**/*.js']
      },
      afterConcat: {
        src: [
          '<%= concat.library.dest %>'
        ]
      },
      options: {
        // options here to override JSHint defaults
        globals: {
          jQuery: true,
          console: true,
          module: true,
          document: true,
          angular: true
        },
        globalstrict: false
      }
    },
    testFiles: {
      karmaUnit: 'karma.conf.js'
    },
    karma: {
      unit: {
        options: {
          configFile: '<%= testFiles.karmaUnit %>',
          autoWatch: false,
          singleRun: true,
          browsers: browsers || ['Chrome']
        }
      }
    },
    watch: {
      options: {
        livereload: true
      },
      files: [
        'Gruntfile.js',
        'src/**/*'
      ],
      tasks: ['build']
    },
    connect: {
      server: {
        options: {
          hostname: '*'
        }
      }
    }
  });

  // Load grunt-karma task plugin
  grunt.loadNpmTasks('grunt-karma');

  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-connect');

  grunt.registerTask('test', ['jshint', 'karma:unit']);
  grunt.registerTask('build', ['jshint:beforeConcat', 'concat', 'jshint:afterConcat', 'uglify']);
  grunt.registerTask('default', ['build', 'connect:server', 'watch']);

};
<commit_msg>refactor(gruntfile): revert task names<commit_after>module.exports = function (grunt) {
  // * Read command-line switches
  // - Read in --browsers CLI option; split it on commas into an array if it's a string, otherwise ignore it
  var browsers = typeof grunt.option('browsers') == 'string' ? grunt.option('browsers').split(',') : undefined;

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    library: grunt.file.readJSON('bower.json'),
    concat: {
      options: {
        separator: ''
      },
      library: {
        src: [
          'src/**/*.js'
        ],
        dest: 'dest/<%= library.name %>.js'
      }
    },
    uglify: {
      options: {
        banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
      },
      jid: {
        files: {
          'dest/<%= library.name %>.min.js': ['<%= concat.library.dest %>']
        }
      }
    },
    jshint: {
      beforeConcat: {
        src: ['gruntfile.js', '<%= library.name %>/**/*.js']
      },
      afterConcat: {
        src: [
          '<%= concat.library.dest %>'
        ]
      },
      options: {
        // options here to override JSHint defaults
        globals: {
          jQuery: true,
          console: true,
          module: true,
          document: true,
          angular: true
        },
        globalstrict: false
      }
    },
    testFiles: {
      karmaUnit: 'karma.conf.js'
    },
    karma: {
      unit: {
        options: {
          configFile: '<%= testFiles.karmaUnit %>',
          autoWatch: false,
          singleRun: true,
          browsers: browsers || ['Chrome']
        }
      }
    },
    watch: {
      options: {
        livereload: true
      },
      files: [
        'Gruntfile.js',
        'src/**/*'
      ],
      tasks: ['default']
    },
    connect: {
      server: {
        options: {
          hostname: '*'
        }
      }
    }
  });

  // Load grunt-karma task plugin
  grunt.loadNpmTasks('grunt-karma');

  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-connect');

  grunt.registerTask('test', ['jshint', 'karma:unit']);
  grunt.registerTask('default', ['jshint:beforeConcat', 'concat', 'jshint:afterConcat', 'uglify']);
  grunt.registerTask('livereload', ['default', 'connect:server', 'watch']);

};
<|endoftext|>


<commit_before>'use strict';

describe("Date Utilities", function() {

    describe("Function 'roundToFollowingHalfHour'", function() {

        it("Before half hours", function() {
            var date = new Date();
            date.setMinutes(0);
            var hoursBefore = date.getHours();

            for(var i = 0; i < 30; i++) {
                var currentDate = new Date(date);
                currentDate.setMinutes(i);
                DateUtilities.roundToFollowingHalfHour(currentDate);
                expect(currentDate.getHours()).toBe(hoursBefore);
            }
        });

        it("Exactly half hours", function() {
            var date = new Date();
            date.setMinutes(30);
            var hoursBefore = date.getHours();

            for(var i = 0; i < 24; i++) {
                var currentDate = new Date(date);
                currentDate.setHours(currentDate.getHours() + i);
                DateUtilities.roundToFollowingHalfHour(currentDate);
                expect(currentDate.getHours()).toBe((hoursBefore + i) % 24);
            }
        });

        it("After half hours", function() {
            var date = new Date();
            date.setMinutes(31);
            var hoursBefore = date.getHours();

            for(var i = 0; i < 30; i++) {
                var currentDate = new Date(date);
                currentDate.setMinutes(date.getMinutes() + i);
                DateUtilities.roundToFollowingHalfHour(currentDate);
                expect(currentDate.getHours()).toBe(hoursBefore + 1);
            }
        });

    });
});
<commit_msg>Fix bug in DateUtilities test suite.<commit_after>'use strict';

describe("Date Utilities", function() {

    describe("Function 'roundToFollowingHalfHour'", function() {

        it("Before half hours", function() {
            var date = new Date();
            date.setMinutes(0);
            var hoursBefore = date.getHours();

            for(var i = 0; i < 30; i++) {
                var currentDate = new Date(date);
                currentDate.setMinutes(i);
                DateUtilities.roundToFollowingHalfHour(currentDate);
                expect(currentDate.getHours()).toBe(hoursBefore);
            }
        });

        it("Exactly half hours", function() {
            var date = new Date();
            date.setMinutes(30);
            var hoursBefore = date.getHours();

            for(var i = 0; i < 24; i++) {
                var currentDate = new Date(date);
                currentDate.setHours(currentDate.getHours() + i);
                DateUtilities.roundToFollowingHalfHour(currentDate);
                expect(currentDate.getHours()).toBe((hoursBefore + i) % 24);
            }
        });

        it("After half hours", function() {
            var date = new Date();
            date.setMinutes(31);
            var hoursBefore = date.getHours();

            for(var i = 0; i < 30; i++) {
                var currentDate = new Date(date);
                currentDate.setMinutes(date.getMinutes() + i);
                DateUtilities.roundToFollowingHalfHour(currentDate);
                expect(currentDate.getHours()).toBe((hoursBefore + 1) % 24);
            }
        });

    });
});
<|endoftext|>


<commit_before>import {css, endsWith, height, isNumeric, isString, offset, query, toFloat} from '../util/index';

export default function (UIkit) {

    UIkit.component('height-viewport', {

        props: {
            expand: Boolean,
            offsetTop: Boolean,
            offsetBottom: Boolean,
            minHeight: Number
        },

        defaults: {
            expand: false,
            offsetTop: false,
            offsetBottom: false,
            minHeight: 0
        },

        update: {

            write() {

                css(this.$el, 'boxSizing', 'border-box');

                const viewport = height(window);
                let minHeight, offsetTop = 0;

                if (this.expand) {

                    css(this.$el, {height: '', minHeight: ''});

                    const diff = viewport - height(document);

                    if (diff > 0) {
                        minHeight = offsetHeight(this.$el) + diff;
                    }

                } else {

                    const {top} = offset(this.$el);

                    if (top < viewport / 2 && this.offsetTop) {
                        offsetTop += top;
                    }

                    if (this.offsetBottom === true) {

                        offsetTop += offsetHeight(this.$el.nextElementSibling);

                    } else if (isNumeric(this.offsetBottom)) {

                        offsetTop += (viewport / 100) * this.offsetBottom;

                    } else if (this.offsetBottom && endsWith(this.offsetBottom, 'px')) {

                        offsetTop += toFloat(this.offsetBottom);

                    } else if (isString(this.offsetBottom)) {

                        offsetTop += offsetHeight(query(this.offsetBottom, this.$el));

                    }

                    // on mobile devices (iOS and Android) window.innerHeight !== 100vh
                    minHeight = offsetTop ? `calc(100vh - ${offsetTop}px)` : '100vh';

                }

                if (!minHeight) {
                    return;
                }

                css(this.$el, {height: '', minHeight});

                const elHeight = this.$el.offsetHeight;
                if (this.minHeight && this.minHeight > elHeight) {
                    css(this.$el, 'minHeight', this.minHeight);
                }

                // IE 11 fix (min-height on a flex container won't apply to its flex items)
                if (viewport - offsetTop >= elHeight) {
                    css(this.$el, 'height', minHeight);
                }

            },

            events: ['load', 'resize']

        }

    });

    function offsetHeight(el) {
        return el && el.offsetHeight || 0;
    }

}
<commit_msg>Fix uk-height-viewport="expand: true"; Resolves #3228<commit_after>import {css, endsWith, height, isNumeric, isString, offset, query, toFloat} from '../util/index';

export default function (UIkit) {

    UIkit.component('height-viewport', {

        props: {
            expand: Boolean,
            offsetTop: Boolean,
            offsetBottom: Boolean,
            minHeight: Number
        },

        defaults: {
            expand: false,
            offsetTop: false,
            offsetBottom: false,
            minHeight: 0
        },

        update: {

            write() {

                css(this.$el, 'boxSizing', 'border-box');

                const viewport = height(window);
                let minHeight, offsetTop = 0;

                if (this.expand) {

                    css(this.$el, {height: '', minHeight: ''});

                    const diff = viewport - height(document.documentElement);

                    if (diff > 0) {
                        minHeight = offsetHeight(this.$el) + diff;
                    }

                } else {

                    const {top} = offset(this.$el);

                    if (top < viewport / 2 && this.offsetTop) {
                        offsetTop += top;
                    }

                    if (this.offsetBottom === true) {

                        offsetTop += offsetHeight(this.$el.nextElementSibling);

                    } else if (isNumeric(this.offsetBottom)) {

                        offsetTop += (viewport / 100) * this.offsetBottom;

                    } else if (this.offsetBottom && endsWith(this.offsetBottom, 'px')) {

                        offsetTop += toFloat(this.offsetBottom);

                    } else if (isString(this.offsetBottom)) {

                        offsetTop += offsetHeight(query(this.offsetBottom, this.$el));

                    }

                    // on mobile devices (iOS and Android) window.innerHeight !== 100vh
                    minHeight = offsetTop ? `calc(100vh - ${offsetTop}px)` : '100vh';

                }

                if (!minHeight) {
                    return;
                }

                css(this.$el, {height: '', minHeight});

                const elHeight = this.$el.offsetHeight;
                if (this.minHeight && this.minHeight > elHeight) {
                    css(this.$el, 'minHeight', this.minHeight);
                }

                // IE 11 fix (min-height on a flex container won't apply to its flex items)
                if (viewport - offsetTop >= elHeight) {
                    css(this.$el, 'height', minHeight);
                }

            },

            events: ['load', 'resize']

        }

    });

    function offsetHeight(el) {
        return el && el.offsetHeight || 0;
    }

}
<|endoftext|>


<commit_before>/*
Name:       nsapi.js - Unofficial NodeJS module for Nederlandse Spoorwegen API
Author:     Franklin van de Meent
Source:     https://github.com/fvdm/nodejs-ns-api
Feedback:   https://github.com/fvdm/nodejs-ns-api/issues
API Docs:   http://www.ns.nl/api/api
License:    Unlicense / Public Domain

This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <http://unlicense.org>
*/

var http = require('http'),
    util = require('util'),
    querystring = require('querystring'),
    xml2json = require('xml2json'),
    app = {username: '', password: ''}

// ! Vertrektijden
app.vertrektijden = function( station, callback ) {
	app.talk( 'avt', {station: station}, function( err, data ) {
		if( !err ) {
			if( !data.ActueleVertrekTijden || !data.ActueleVertrekTijden.VertrekkendeTrein ) {
				callback( new Error('unexpected response') )
			} else {
				data = data.ActueleVertrekTijden.VertrekkendeTrein
				if( data.RitNummer !== undefined ) {
					data = [data]
					callback( null, data )
				} else {
					for( var t in data ) {
						data[t].VertrekSpoor.spoor = data[t].VertrekSpoor.$t
						delete data[t].VertrekSpoor.$t
					}
					callback( null, data )
				}
			}
		} else {
			callback( err, data )
		}
	})
}

// ! Prijzen
app.prijzen = function( props, callback ) {
	app.talk( 'prijzen-v2', props, callback )
}

// ! Reisadviezen
app.reisadvies = function( props, callback ) {
	app.talk( 'treinplanner', props, function( err, data ) {
		if( !err ) {
			if( !data.reismogelijkheden.reismogelijkheid ) {
				callback( new Error('unexpected response') )
			} else {
				callback( null, data.reismogelijkheden.reismogelijkheid )
			}
		} else {
			callback( err, data )
		}
	})
}

// ! Stationslijst
app.stations = function( treeKey, callback ) {
	if( typeof treeKey === 'function' ) {
		var callback = treeKey
		var treeKey = 'code'
	}

	app.talk( 'stations-v2', function( err, data ) {
		if( !err ) {
			if( !data.stations.station ) {
				callback( new Error('unexpected response') )
			} else {
				var tree = {}
				for( var s in data.stations.station ) {
					var station = data.stations.station[s]

					if( treeKey === 'code' ) {
						tree[ station.code ] = station
					} else if( typeof station[ treeKey ] !== 'string' ) {
						callback( new Error('key not found in station') )
						return
					} else {
						if( !tree[ station[ treeKey ] ] ) {
							tree[ station[ treeKey ] ] = {}
						}
						tree[ station[ treeKey ] ][ station.code ] = station
					}
				}

				callback( null, tree )
			}
		} else {
			callback( err, data )
		}
	})
}

// ! Storingen
app.storingen = function( params, callback ) {
	app.talk( 'storingen', params, function( err, data ) {
		if( !err ) {
			if( !data.storingen.ongepland || !data.storingen.gepland ) {
				callback( new Error('unexpected response') )
			} else {
				if( data.storingen.ongepland.storing !== undefined ) {
					if( data.storingen.ongepland.storing.id !== undefined ) {
						data.storingen.ongepland = [data.storingen.ongepland.storing]
					} else if( data.storingen.ongepland.storing.length >= 1 ) {
						var storingen = []
						for( var s in data.storingen.ongepland.storing ) {
							storingen.push( data.storingen.ongepland.storing[s] )
						}
						data.storingen.ongepland = storingen
					}
				} else {
					data.storingen.ongepland = []
				}

				if( data.storingen.gepland.storing !== undefined ) {
					if( data.storingen.gepland.storing.id !== undefined ) {
						data.storingen.gepland = [data.storingen.gepland.storing]
					} else if( data.storingen.gepland.storing.length >= 1 ) {
						var storingen = []
						for( var s in data.storingen.gepland.storing ) {
							storingen.push( data.storingen.gepland.storing[s] )
						}
						data.storingen.gepland = storingen
					}
				} else {
					data.storingen.gepland = []
				}

				callback( null, data.storingen )
			}
		} else {
			callback( err, data )
		}
	})
}

// ! Communicate
app.talk = function( path, props, callback ) {
	if( typeof props === 'function' ) {
		var callback = props
		var props = {}
	}

	var options = {
		host:	'webservices.ns.nl',
		port:	80,
		path:	'/ns-api-'+ path +'?'+ querystring.stringify( props ),
		method:	'GET',
		auth:	app.username +':'+ app.password
	}

	var req = http.request( options )

	req.on( 'response', function( response ) {
		var data = ''
		response.on( 'data', function( ch ) { data += ch })
		response.on( 'close', function() { callback( new Error('disconnected') ) })
		response.on( 'end', function() {
			data = data.toString('utf8').trim()
			if( data.match('<faultstring>') ) {
				data.replace( /<faultstring>([0-9]+):([^<]+)<\/faultstring>/, function( s, code, error ) {
					var err = new Error('API error')
					err.details = {
						code:		code,
						message:	error
					}
					callback( err )
				})
			} else if( data.match('<?xml') ) {
				data = xml2json.toJson( data, {
					object: true,
					coerce: true,
					trim: true,
					sanitize: true
				})
				callback( null, data )
			} else {
				var err = new Error('invalid response')
				err.details = {
					request: options,
					response: {
						headers: response.headers,
						data: data
					}
				}
				callback( err )
			}
		})
	})

	req.on( 'error', function( error ) {
		var err = new Error('request failed')
		err.details = error
		callback( err )
	})

	req.end()
}

// ready
module.exports = app
<commit_msg>Converted app.reisadvies to new xml2json module #1<commit_after>/*
Name:       nsapi.js - Unofficial NodeJS module for Nederlandse Spoorwegen API
Author:     Franklin van de Meent
Source:     https://github.com/fvdm/nodejs-ns-api
Feedback:   https://github.com/fvdm/nodejs-ns-api/issues
API Docs:   http://www.ns.nl/api/api
License:    Unlicense / Public Domain

This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <http://unlicense.org>
*/

var http = require('http'),
    util = require('util'),
    querystring = require('querystring'),
    xml2json = require('xml2json'),
    app = {username: '', password: ''}

// ! Vertrektijden
app.vertrektijden = function( station, callback ) {
	app.talk( 'avt', {station: station}, function( err, data ) {
		if( !err ) {
			if( !data.ActueleVertrekTijden || !data.ActueleVertrekTijden.VertrekkendeTrein ) {
				callback( new Error('unexpected response') )
			} else {
				data = data.ActueleVertrekTijden.VertrekkendeTrein
				if( data.RitNummer !== undefined ) {
					data = [data]
					callback( null, data )
				} else {
					for( var t in data ) {
						data[t].VertrekSpoor.spoor = data[t].VertrekSpoor.$t
						delete data[t].VertrekSpoor.$t
					}
					callback( null, data )
				}
			}
		} else {
			callback( err, data )
		}
	})
}

// ! Prijzen
app.prijzen = function( props, callback ) {
	app.talk( 'prijzen-v2', props, callback )
}

// ! Reisadviezen
app.reisadvies = function( props, callback ) {
	app.talk( 'treinplanner', props, function( err, data ) {
		if( !err ) {
			if( !data.ReisMogelijkheden || !data.ReisMogelijkheden.ReisMogelijkheid ) {
				callback( new Error('unexpected response') )
			} else {
				data = data.ReisMogelijkheden.ReisMogelijkheid

				if( !util.isArray( data ) ) {
					data = [data]
				}

				if( data.length >= 1 ) {
					for( var r in data ) {
						var reis = data[r]
						if( !util.isArray( reis.ReisDeel ) ) {
							reis.ReisDeel = [reis.ReisDeel]
						}

						for( var d in reis.ReisDeel ) {
							var deel = reis.ReisDeel[d]
							for( var s in deel.ReisStop ) {
								var stop = deel.ReisStop[s]
								stop.SpoorWijziging = stop.Spoor.wijziging
								stop.Spoor = stop.Spoor.$t
								delete stop.Spoor.$t
								deel.ReisStop[s] = stop
							}
							reis.ReisDeel[d] = deel
						}

						data[r] = reis
					}
				}

				callback( null, data )
			}
		} else {
			callback( err, data )
		}
	})
}

// ! Stationslijst
app.stations = function( treeKey, callback ) {
	if( typeof treeKey === 'function' ) {
		var callback = treeKey
		var treeKey = 'code'
	}

	app.talk( 'stations-v2', function( err, data ) {
		if( !err ) {
			if( !data.stations.station ) {
				callback( new Error('unexpected response') )
			} else {
				var tree = {}
				for( var s in data.stations.station ) {
					var station = data.stations.station[s]

					if( treeKey === 'code' ) {
						tree[ station.code ] = station
					} else if( typeof station[ treeKey ] !== 'string' ) {
						callback( new Error('key not found in station') )
						return
					} else {
						if( !tree[ station[ treeKey ] ] ) {
							tree[ station[ treeKey ] ] = {}
						}
						tree[ station[ treeKey ] ][ station.code ] = station
					}
				}

				callback( null, tree )
			}
		} else {
			callback( err, data )
		}
	})
}

// ! Storingen
app.storingen = function( params, callback ) {
	app.talk( 'storingen', params, function( err, data ) {
		if( !err ) {
			if( !data.storingen.ongepland || !data.storingen.gepland ) {
				callback( new Error('unexpected response') )
			} else {
				if( data.storingen.ongepland.storing !== undefined ) {
					if( data.storingen.ongepland.storing.id !== undefined ) {
						data.storingen.ongepland = [data.storingen.ongepland.storing]
					} else if( data.storingen.ongepland.storing.length >= 1 ) {
						var storingen = []
						for( var s in data.storingen.ongepland.storing ) {
							storingen.push( data.storingen.ongepland.storing[s] )
						}
						data.storingen.ongepland = storingen
					}
				} else {
					data.storingen.ongepland = []
				}

				if( data.storingen.gepland.storing !== undefined ) {
					if( data.storingen.gepland.storing.id !== undefined ) {
						data.storingen.gepland = [data.storingen.gepland.storing]
					} else if( data.storingen.gepland.storing.length >= 1 ) {
						var storingen = []
						for( var s in data.storingen.gepland.storing ) {
							storingen.push( data.storingen.gepland.storing[s] )
						}
						data.storingen.gepland = storingen
					}
				} else {
					data.storingen.gepland = []
				}

				callback( null, data.storingen )
			}
		} else {
			callback( err, data )
		}
	})
}

// ! Communicate
app.talk = function( path, props, callback ) {
	if( typeof props === 'function' ) {
		var callback = props
		var props = {}
	}

	var options = {
		host:	'webservices.ns.nl',
		port:	80,
		path:	'/ns-api-'+ path +'?'+ querystring.stringify( props ),
		method:	'GET',
		auth:	app.username +':'+ app.password
	}

	var req = http.request( options )

	req.on( 'response', function( response ) {
		var data = ''
		response.on( 'data', function( ch ) { data += ch })
		response.on( 'close', function() { callback( new Error('disconnected') ) })
		response.on( 'end', function() {
			data = data.toString('utf8').trim()
			if( data.match('<faultstring>') ) {
				data.replace( /<faultstring>([0-9]+):([^<]+)<\/faultstring>/, function( s, code, error ) {
					var err = new Error('API error')
					err.details = {
						code:		code,
						message:	error
					}
					callback( err )
				})
			} else if( data.match('<?xml') ) {
				data = xml2json.toJson( data, {
					object: true,
					coerce: true,
					trim: true,
					sanitize: true
				})
				callback( null, data )
			} else {
				var err = new Error('invalid response')
				err.details = {
					request: options,
					response: {
						headers: response.headers,
						data: data
					}
				}
				callback( err )
			}
		})
	})

	req.on( 'error', function( error ) {
		var err = new Error('request failed')
		err.details = error
		callback( err )
	})

	req.end()
}

// ready
module.exports = app
<|endoftext|>


<commit_before>/*
 * raygun
 * https://github.com/MindscapeHQ/raygun4node
 *
 * Copyright (c) 2015 MindscapeHQ
 * Licensed under the MIT license.
 */

'use strict';

var stackTrace = require('stack-trace');
var os = require('os');
var humanString = require('object-to-human-string');
var packageDetails = require('../package.json');

function filterKeys(obj, filters) {
  if (!obj || !filters || typeof obj !== 'object') {
    return obj;
  }
  Object.keys(obj).forEach(function (i) {
    if (filters.indexOf(i) > -1) {
      delete obj[i];
    } else {
      obj[i] = filterKeys(obj[i], filters);
    }
  });
  return obj;
}

var RaygunMessageBuilder = function (options) {
  options = options || {};
  var _filters;

  if (Array.isArray(options.filters)) {
    _filters = options.filters;
  }

  var message = {
    occurredOn: new Date(),
    details: {
      client: {
        name: 'raygun-node',
        version: packageDetails.version
      }
    }
  };

  this.build = function () {
    return message;
  };

  this.setErrorDetails = function (error) {
    var errorType = Object.prototype.toString.call(error);

    if (errorType === '[object Object]' && options.useHumanStringForObject) {
      error = humanString(error);
      message.details.groupingKey = error.replace(/\W+/g, "").substring(0, 64);
    }

    if (typeof error === "string") {
      message.details.error = {
        message: error
      };

      return this;
    }

    var stack = [];
    var trace = stackTrace.parse(error);
    trace.forEach(function (callSite) {
      stack.push({
        lineNumber: callSite.getLineNumber(),
        className: callSite.getTypeName() || 'unknown',
        fileName: callSite.getFileName(),
        methodName: callSite.getFunctionName() || '[anonymous]'
      });
    });

    message.details.error = {
      stackTrace: stack,
      message: error.message || "NoMessage",
      className: error.name
    };

    return this;
  };

  this.setEnvironmentDetails = function () {
    var environment = {
      osVersion: os.type() + ' ' + os.platform() + ' ' + os.release(),
      architecture: os.arch(),
      totalPhysicalMemory: os.totalmem(),
      availablePhysicalMemory: os.freemem(),
      utcOffset: new Date().getTimezoneOffset() / -60.0
    };

    // cpus seems to return undefined on some systems
    var cpus = os.cpus();

    if (cpus && cpus.length && cpus.length > 0) {
      environment.processorCount = cpus.length;
      environment.cpu = cpus[0].model;
    }

    message.details.environment = environment;

    return this;
  };

  this.setMachineName = function (machineName) {
    message.details.machineName = machineName || os.hostname();
    return this;
  };

  this.setUserCustomData = function (customData) {
    message.details.userCustomData = customData;
    return this;
  };

  this.setTags = function (tags) {
    if (Array.isArray(tags)) {
      message.details.tags = tags;
    }
    return this;
  };

  this.setRequestDetails = function (request) {
    if (request) {
      message.details.request = {
        hostName: request.hostname || request.host,
        url: request.path,
        httpMethod: request.method,
        ipAddress: request.ip,
        queryString: filterKeys(request.query, _filters),
        headers: filterKeys(request.headers, _filters),
        form: filterKeys(request.body, _filters)
      };
    }
    return this;
  };

  var extractUserProperties = function(userData) {
    var data = {};
    if(userData.identifier) {
      data.identifier = userData.identifier;
    }
    if(userData.email) {
      data.email = userData.email;
    }
    if(userData.fullName) {
      data.fullName = userData.fullName;
    }
    if(userData.firstName) {
      data.firstName = userData.firstName;
    }
    if(userData.uuid) {
      data.uuid = userData.uuid;
    }
    return data;
  };

  this.setUser = function (user) {
    var userData = user;
    if (user instanceof Function) {
      userData = user();
    }

    if (userData instanceof Object) {
        message.details.user = extractUserProperties(userData);
    } else {
        message.details.user = { 'identifier': userData };
    }

    return this;
  };

  this.setVersion = function (version) {
    message.details.version = version;
    return this;
  };
};

exports = module.exports = RaygunMessageBuilder;
<commit_msg>treat custom errors as Error's<commit_after>/*
 * raygun
 * https://github.com/MindscapeHQ/raygun4node
 *
 * Copyright (c) 2015 MindscapeHQ
 * Licensed under the MIT license.
 */

'use strict';

var stackTrace = require('stack-trace');
var os = require('os');
var humanString = require('object-to-human-string');
var packageDetails = require('../package.json');

function filterKeys(obj, filters) {
  if (!obj || !filters || typeof obj !== 'object') {
    return obj;
  }
  Object.keys(obj).forEach(function (i) {
    if (filters.indexOf(i) > -1) {
      delete obj[i];
    } else {
      obj[i] = filterKeys(obj[i], filters);
    }
  });
  return obj;
}

var RaygunMessageBuilder = function (options) {
  options = options || {};
  var _filters;

  if (Array.isArray(options.filters)) {
    _filters = options.filters;
  }

  var message = {
    occurredOn: new Date(),
    details: {
      client: {
        name: 'raygun-node',
        version: packageDetails.version
      }
    }
  };

  this.build = function () {
    return message;
  };

  this.setErrorDetails = function (error) {
    var errorType = Object.prototype.toString.call(error);

    if (!(errorType instanceof Error) && options.useHumanStringForObject) {
      error = humanString(error);
      message.details.groupingKey = error.replace(/\W+/g, "").substring(0, 64);
    }

    if (typeof error === "string") {
      message.details.error = {
        message: error
      };

      return this;
    }

    var stack = [];
    var trace = stackTrace.parse(error);
    trace.forEach(function (callSite) {
      stack.push({
        lineNumber: callSite.getLineNumber(),
        className: callSite.getTypeName() || 'unknown',
        fileName: callSite.getFileName(),
        methodName: callSite.getFunctionName() || '[anonymous]'
      });
    });

    message.details.error = {
      stackTrace: stack,
      message: error.message || "NoMessage",
      className: error.name
    };

    return this;
  };

  this.setEnvironmentDetails = function () {
    var environment = {
      osVersion: os.type() + ' ' + os.platform() + ' ' + os.release(),
      architecture: os.arch(),
      totalPhysicalMemory: os.totalmem(),
      availablePhysicalMemory: os.freemem(),
      utcOffset: new Date().getTimezoneOffset() / -60.0
    };

    // cpus seems to return undefined on some systems
    var cpus = os.cpus();

    if (cpus && cpus.length && cpus.length > 0) {
      environment.processorCount = cpus.length;
      environment.cpu = cpus[0].model;
    }

    message.details.environment = environment;

    return this;
  };

  this.setMachineName = function (machineName) {
    message.details.machineName = machineName || os.hostname();
    return this;
  };

  this.setUserCustomData = function (customData) {
    message.details.userCustomData = customData;
    return this;
  };

  this.setTags = function (tags) {
    if (Array.isArray(tags)) {
      message.details.tags = tags;
    }
    return this;
  };

  this.setRequestDetails = function (request) {
    if (request) {
      message.details.request = {
        hostName: request.hostname || request.host,
        url: request.path,
        httpMethod: request.method,
        ipAddress: request.ip,
        queryString: filterKeys(request.query, _filters),
        headers: filterKeys(request.headers, _filters),
        form: filterKeys(request.body, _filters)
      };
    }
    return this;
  };

  var extractUserProperties = function(userData) {
    var data = {};
    if(userData.identifier) {
      data.identifier = userData.identifier;
    }
    if(userData.email) {
      data.email = userData.email;
    }
    if(userData.fullName) {
      data.fullName = userData.fullName;
    }
    if(userData.firstName) {
      data.firstName = userData.firstName;
    }
    if(userData.uuid) {
      data.uuid = userData.uuid;
    }
    return data;
  };

  this.setUser = function (user) {
    var userData = user;
    if (user instanceof Function) {
      userData = user();
    }

    if (userData instanceof Object) {
        message.details.user = extractUserProperties(userData);
    } else {
        message.details.user = { 'identifier': userData };
    }

    return this;
  };

  this.setVersion = function (version) {
    message.details.version = version;
    return this;
  };
};

exports = module.exports = RaygunMessageBuilder;
<|endoftext|>


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

module.exports = function(grunt) {
    var packageJSON = require('./package.json');
    var versionJSONFile = 'app/config/version.json';

    grunt.initConfig({
        exec: {
            bower_install: {
                command: "bower install",
                stdout: false
            }
        },

        clean: {
            lib: ["app/lib"],
            docs: ["docs"],
            war: ["target"],
            tests: ["reports"],
            less: ["app/css/app.css"],
            build: [".tmp", "build"]
        },

        "git-describe": {
            options: {
                failOnError: false
            },
            neonGTDVersion: {
                tags: true,
                all: true,
                template: '{%=tag%}-{%=since%}-{%=object%}'
            }
        },

        // Injects the bower components into the <!-- bower --> blocks in our index.html file.
        wiredep: {
            app: {
                src: "app/index.html",
                directory: "app/lib",
                ignorePath: "app/",
                // The dependencies for these libraries are added to index.html manually.
                exclude: [
                    // Exclude ag-grid.js because it must be included after angular.js and wiredep may not order them correctly.
                    /ag-grid\.js/,
                    // Exclude angular-gantt because it defines incorrect files in the main of its bower.json file (should be dist/ not assets/).
                    /angular-gantt/,
                    // Exclude javascript-detect-element-resize because it defines its "pure js" lib in the main of its bower.json file and we use its "jquery plugin" lib.
                    /javascript-detect-element-resize/,
                    // Exclude angular-ui-tree.min.js because the non-minified js file is also included.
                    /angular-ui-tree\.min\.js/,
                    // Exclude html5shiv and respond because we only include them for old versions of IE.
                    /html5shiv/,
                    /respond/,
                    // Exclude the remaining libraries because they do not have bower.json files.
                    /threedubmedia/,
                    /mergesort/,
                    /opencpu/,
                    /openlayers/,
                    /heatmapjs/,
                    /user-ale/,
                    /rainbowvis.js/
                ]
            }
        },

        injector: {
            js: {
                options: {
                    transform: function(filePath) {
                        filePath = filePath.replace("/app/", "");
                        return '<script src="' + filePath + '"></script>';
                    }
                },
                files: {
                    "app/index.html": [
                        "app/js/namespaces.js",
                        "app/js/neon-extensions/*.js",
                        "app/js/charts/*.js",
                        "app/js/coremap/*.js",
                        "app/js/mediators/*.js",
                        "app/js/tables/*.js",
                        "app/js/time/*.js",
                        "app/js/app.js",
                        "app/js/*Service.js",
                        "app/js/*Controller.js",
                        "app/js/dataset-wizard/*.js",
                        "app/js/directives/*.js"
                    ]
                }
            },

            less: {
                options: {
                    starttag: "/* injector:less */",
                    endtag: "/* endinjector */",
                    transform: function(filePath) {
                        filePath = filePath.replace("/app/css/", "");
                        return '@import "' + filePath + '";';
                    }
                },
                files: {
                    "app/css/app.less": [
                        "app/css/*.less",
                        "app/css/directives/*.less",
                        "!app/css/app.less",
                        "!app/css/theme*.less",
                        "!app/css/variables*.less",
                        "!app/css/widgetStyle.less"
                    ]
                }
            }
        },

        less: {
            options: {
                dumpLineNumbers: 'comments',
                paths: [
                    'app/components'
                ]
            },
            themeDarkGreen: {
                files: {
                    'app/css/dark-green.css': 'app/css/theme-green-on-dark.less'
                }
            },
            themeLightGreen: {
                files: {
                    'app/css/light-green.css': 'app/css/theme-green-on-light.less'
                }
            },
            themeDarkPurple: {
                files: {
                    'app/css/dark-purple.css': 'app/css/theme-purple-on-dark.less'
                }
            }
        },

        // Packages all the images into a spritesheet and creates a CSS file for using the sprites.
        sprite: {
            app: {
                src: ["app/img/*.png", "app/img/visualizations/*.png", "app/img/visualizations/gray/*.png", "!app/img/neon-dashboard-spritesheet.png"],
                dest: "app/img/neon-dashboard-spritesheet.png",
                destCss: "app/css/neon-dashboard-sprites.css"
            }
        },

        // Prepares concatenation and minification of CSS and JS dependencies in our index.html file.
        // Automatically configures files for the concat, uglify, and cssmin tasks using the <!-- build --> blocks in our index.html.
        useminPrepare: {
            html: "app/index.html",
            options: {
                dest: "build"
            }
        },

        // Copies concatenated, minified files into the build directory and inject dependencies into our index.html file.
        usemin: {
            html: ["build/{,*/}*.html"],
            options: {
                assetsDirs: ["build"]
            }
        },

        // Packages all the HTML partials into a JS file.
        ngtemplates: {
            options: {
                module: "neonDemo",
                // Add the template JS file to the list of JS files that the usemin task configured to concatenate.
                usemin: "build/js/neon-dashboard-app.min.js"
                // TODO Use the htmlmin option when the html-minifier task fully supports angular (currently the task just hangs when run).
            },
            app: {
                cwd: "app/",
                src: ["partials/**/*.html"],
                dest: "build/js/neon-dashboard-templates.js"
            }
        },

        // Files for uglify are automatically configured by the useminPrepare task.
        uglify: {
            options: {
                mangle: false
            }
        },

        // Copy dependencies for third-party libraries (using their relative paths) and other Neon Dashboard files/folders for the production version.
        copy: {
            bootstrap: {
                expand: true,
                cwd: "app/lib/bootstrap/dist/",
                src: ["fonts/**"],
                dest: "build/"
            },
            jquery_ui_lightness: {
                expand: true,
                cwd: "app/lib/jquery-ui/themes/ui-lightness",
                src: ["images/**"],
                dest: "build/css/"
            },
            jquery_ui_smoothness: {
                expand: true,
                cwd: "app/lib/jquery-ui/themes/smoothness",
                src: ["images/**"],
                dest: "build/css/"
            },
            openlayers: {
                expand: true,
                cwd: "app/",
                src: ["lib/openlayers/OpenLayers.js", "lib/openlayers/img/*", "lib/openlayers/theme/default/style.css", "lib/openlayers/theme/default/img/*"],
                dest: "build/"
            },
            userale: {
                expand: true,
                cwd: "app/",
                src: ["lib/user-ale/helper-libs/javascript/userale-worker.js"],
                dest: "build/"
            },
            app: {
                expand: true,
                cwd: "app/",
                src: ["index.html", "config/**", "css/light-*.css", "css/dark-*.css", "fonts/**", "help/**", "img/Neon_16x16.png", "img/neon-dashboard-spritesheet.png"],
                dest: "build/"
            }
        },

        jshint: {
            options: {
                jshintrc: '.jshintrc',
                force: true
            },
            console: [
                'Gruntfile.js',
                'app/js/*.js',
                'app/js/**/*.js',
                'test/**/*.js',
                '!app/js/vendor/**/*.js'
            ],
            xml: {
                options: {
                    reporter: "jslint",
                    reporterOutput: "reports/jslint.xml"
                },
                files: {
                    src: [
                        'Gruntfile.js',
                        'app/js/*.js',
                        'app/js/**/*.js',
                        'test/**/*.js',
                        '!app/js/vendor/**/*.js'
                    ]
                }
            }
        },

        jscs: {
            options: {
                config: ".jscsrc",
                excludeFiles: ["app/js/vendor/**/*.js"],
                force: true
            },
            console: {
                options: {
                    reporter: 'console'
                },
                files: {
                    src: ['Gruntfile.js', 'app/js/**/*.js', 'test/**/*.js']
                }
            },
            xml: {
                options: {
                    reporterOutput: 'reports/jscs.xml',
                    reporter: 'checkstyle'
                },
                files: {
                    src: ['Gruntfile.js', 'app/js/**/*.js', 'test/**/*.js']
                }
            }
        },

        karma: {
            unit: {
                options: {
                    configFile: 'karma.conf.js'
                }
            }
        },

        /*
         * Build a WAR (web archive) without Maven or the JVM installed.
         */
        war: {
            development: {
                options: {
                    war_dist_folder: "target",
                    war_verbose: true,
                    war_name: 'neon-gtd-dev-' + packageJSON.version,
                    webxml_welcome: 'index.html',
                    webxml_display_name: packageJSON.shortDescription,
                    webxml_mime_mapping: [{
                        extension: 'woff',
                        mime_type: 'application/font-woff'
                    }]
                },
                files: [{
                    expand: true,
                    cwd: ".",
                    src: ["app/**", "!**/*.less"],
                    dest: ""
                }]
            },
            production: {
                options: {
                    war_dist_folder: "target",
                    war_verbose: true,
                    war_name: 'neon-gtd-' + packageJSON.version,
                    webxml_welcome: 'index.html',
                    webxml_display_name: packageJSON.shortDescription,
                    webxml_mime_mapping: [{
                        extension: 'woff',
                        mime_type: 'application/font-woff'
                    }]
                },
                files: [{
                    expand: true,
                    cwd: "build/",
                    src: ["**"],
                    dest: "app/"
                }]
            }
        },

        yuidoc: {
            compile: {
                name: packageJSON.shortDescription,
                description: packageJSON.description,
                version: packageJSON.version,
                url: packageJSON.repository.url,
                logo: '../app/img/Neon_60x34.png',
                options: {
                    paths: 'app/js',
                    exclude: "vendor",
                    outdir: 'docs'
                }
            }
        },

        watch: {
            less: {
                files: ['app/css/**/*.less'],
                tasks: ['compile-less'],
                options: {
                    spawn: false
                }
            }
        }
    });

    grunt.loadNpmTasks('grunt-angular-templates');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-cssmin');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-yuidoc');
    grunt.loadNpmTasks('grunt-exec');
    grunt.loadNpmTasks('grunt-git-describe');
    grunt.loadNpmTasks('grunt-injector');
    grunt.loadNpmTasks('grunt-jscs');
    grunt.loadNpmTasks('grunt-karma');
    grunt.loadNpmTasks('grunt-spritesmith');
    grunt.loadNpmTasks('grunt-usemin');
    grunt.loadNpmTasks('grunt-war');
    grunt.loadNpmTasks('grunt-wiredep');

    grunt.registerTask("bower_install", ["clean:lib", "exec:bower_install", "wiredep"]);
    grunt.registerTask('compile-less', ['clean:less', 'injector:less', 'less:themeLightGreen', 'less:themeDarkGreen', 'less:themeDarkPurple', 'sprite']);
    grunt.registerTask('saveRevision', function() {
        grunt.event.once('git-describe', function(rev) {
            var date = new Date(Date.now()).toISOString();
            date = date.replace(/-/g, '.').replace(/T.*/, '');

            var versionObj = {
                name: packageJSON.name,
                version: packageJSON.version + '-' + rev[2] + '-g' + rev[3] + '-' + date
            };
            grunt.file.write(versionJSONFile, JSON.stringify(versionObj));
        });
        grunt.task.run('git-describe');
    });
    grunt.registerTask('test', ['jshint:console', 'jscs:console', 'karma']);

    var defaultTasks = [
        'saveRevision',
        'wiredep',
        'injector',
        'less:themeLightGreen',
        'less:themeDarkGreen',
        'less:themeDarkPurple',
        'sprite',
        'useminPrepare',
        'ngtemplates',
        'concat',
        'uglify',
        'cssmin',
        'copy',
        'jshint:xml',
        'jscs:xml',
        'yuidoc',
        'usemin',
        'war'
    ];

    grunt.registerTask('no-bower', ['clean:docs', 'clean:war', 'clean:tests', 'clean:build'].concat(defaultTasks));
    grunt.registerTask('default', ['clean', 'exec:bower_install'].concat(defaultTasks));
};
<commit_msg>Fixed build to run on Windows<commit_after>'use strict';
//
// Copyright 2016 Next Century Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

module.exports = function(grunt) {
    var packageJSON = require('./package.json');
    var versionJSONFile = 'app/config/version.json';

    grunt.initConfig({
        exec: {
            bower_install: {
                command: "bower install",
                stdout: false
            }
        },

        clean: {
            lib: ["app/lib"],
            docs: ["docs"],
            war: ["target"],
            tests: ["reports"],
            less: ["app/css/app.css"],
            build: [".tmp", "build"]
        },

        "git-describe": {
            options: {
                failOnError: false
            },
            neonGTDVersion: {
                tags: true,
                all: true,
                template: '{%=tag%}-{%=since%}-{%=object%}'
            }
        },

        // Injects the bower components into the <!-- bower --> blocks in our index.html file.
        wiredep: {
            app: {
                src: "app/index.html",
                directory: "app/lib",
                ignorePath: "app/",
                // The dependencies for these libraries are added to index.html manually.
                exclude: [
                    // Exclude ag-grid.js because it must be included after angular.js and wiredep may not order them correctly.
                    /ag-grid\.js/,
                    // Exclude angular-gantt because it defines incorrect files in the main of its bower.json file (should be dist/ not assets/).
                    /angular-gantt/,
                    // Exclude javascript-detect-element-resize because it defines its "pure js" lib in the main of its bower.json file and we use its "jquery plugin" lib.
                    /javascript-detect-element-resize/,
                    // Exclude angular-ui-tree.min.js because the non-minified js file is also included.
                    /angular-ui-tree\.min\.js/,
                    // Exclude html5shiv and respond because we only include them for old versions of IE.
                    /html5shiv/,
                    /respond/,
                    // Exclude the remaining libraries because they do not have bower.json files.
                    /threedubmedia/,
                    /mergesort/,
                    /opencpu/,
                    /openlayers/,
                    /heatmapjs/,
                    /user-ale/,
                    /rainbowvis.js/
                ]
            }
        },

        injector: {
            js: {
                options: {
                    starttag: "/* injector:js */",
                    endtag: "/* endinjector */",
                    transform: function(filePath) {
                        filePath = filePath.replace("/app/", "");
                        return '<script src="' + filePath + '"></script>';
                    }
                },
                files: {
                    "app/index.html": [
                        "app/js/namespaces.js",
                        "app/js/neon-extensions/*.js",
                        "app/js/charts/*.js",
                        "app/js/coremap/*.js",
                        "app/js/mediators/*.js",
                        "app/js/tables/*.js",
                        "app/js/time/*.js",
                        "app/js/app.js",
                        "app/js/*Service.js",
                        "app/js/*Controller.js",
                        "app/js/dataset-wizard/*.js",
                        "app/js/directives/*.js"
                    ]
                }
            },

            less: {
                options: {
                    starttag: "/* injector:less */",
                    endtag: "/* endinjector */",
                    transform: function(filePath) {
                        filePath = filePath.replace("/app/css/", "");
                        return '@import "' + filePath + '";';
                    }
                },
                files: {
                    "app/css/app.less": [
                        "app/css/*.less",
                        "app/css/directives/*.less",
                        "!app/css/app.less",
                        "!app/css/theme*.less",
                        "!app/css/variables*.less",
                        "!app/css/widgetStyle.less"
                    ]
                }
            }
        },

        less: {
            options: {
                dumpLineNumbers: 'comments',
                paths: [
                    'app/components'
                ]
            },
            themeDarkGreen: {
                files: {
                    'app/css/dark-green.css': 'app/css/theme-green-on-dark.less'
                }
            },
            themeLightGreen: {
                files: {
                    'app/css/light-green.css': 'app/css/theme-green-on-light.less'
                }
            },
            themeDarkPurple: {
                files: {
                    'app/css/dark-purple.css': 'app/css/theme-purple-on-dark.less'
                }
            }
        },

        // Packages all the images into a spritesheet and creates a CSS file for using the sprites.
        sprite: {
            app: {
                src: ["app/img/*.png", "app/img/visualizations/*.png", "app/img/visualizations/gray/*.png", "!app/img/neon-dashboard-spritesheet.png"],
                dest: "app/img/neon-dashboard-spritesheet.png",
                destCss: "app/css/neon-dashboard-sprites.css"
            }
        },

        // Prepares concatenation and minification of CSS and JS dependencies in our index.html file.
        // Automatically configures files for the concat, uglify, and cssmin tasks using the <!-- build --> blocks in our index.html.
        useminPrepare: {
            html: "app/index.html",
            options: {
                dest: "build"
            }
        },

        // Copies concatenated, minified files into the build directory and inject dependencies into our index.html file.
        usemin: {
            html: ["build/{,*/}*.html"],
            options: {
                assetsDirs: ["build"]
            }
        },

        // Packages all the HTML partials into a JS file.
        ngtemplates: {
            options: {
                module: "neonDemo",
                // Add the template JS file to the list of JS files that the usemin task configured to concatenate.
                usemin: "build/js/neon-dashboard-app.min.js"
                // TODO Use the htmlmin option when the html-minifier task fully supports angular (currently the task just hangs when run).
            },
            app: {
                cwd: "app/",
                src: ["partials/**/*.html"],
                dest: "build/js/neon-dashboard-templates.js"
            }
        },

        // Files for uglify are automatically configured by the useminPrepare task.
        uglify: {
            options: {
                mangle: false
            }
        },

        // Copy dependencies for third-party libraries (using their relative paths) and other Neon Dashboard files/folders for the production version.
        copy: {
            bootstrap: {
                expand: true,
                cwd: "app/lib/bootstrap/dist/",
                src: ["fonts/**"],
                dest: "build/"
            },
            jquery_ui_lightness: {
                expand: true,
                cwd: "app/lib/jquery-ui/themes/ui-lightness",
                src: ["images/**"],
                dest: "build/css/"
            },
            jquery_ui_smoothness: {
                expand: true,
                cwd: "app/lib/jquery-ui/themes/smoothness",
                src: ["images/**"],
                dest: "build/css/"
            },
            openlayers: {
                expand: true,
                cwd: "app/",
                src: ["lib/openlayers/OpenLayers.js", "lib/openlayers/img/*", "lib/openlayers/theme/default/style.css", "lib/openlayers/theme/default/img/*"],
                dest: "build/"
            },
            userale: {
                expand: true,
                cwd: "app/",
                src: ["lib/user-ale/helper-libs/javascript/userale-worker.js"],
                dest: "build/"
            },
            app: {
                expand: true,
                cwd: "app/",
                src: ["index.html", "config/**", "css/light-*.css", "css/dark-*.css", "fonts/**", "help/**", "img/Neon_16x16.png", "img/neon-dashboard-spritesheet.png"],
                dest: "build/"
            }
        },

        jshint: {
            options: {
                jshintrc: '.jshintrc',
                force: true
            },
            console: [
                'Gruntfile.js',
                'app/js/*.js',
                'app/js/**/*.js',
                'test/**/*.js',
                '!app/js/vendor/**/*.js'
            ],
            xml: {
                options: {
                    reporter: "jslint",
                    reporterOutput: "reports/jslint.xml"
                },
                files: {
                    src: [
                        'Gruntfile.js',
                        'app/js/*.js',
                        'app/js/**/*.js',
                        'test/**/*.js',
                        '!app/js/vendor/**/*.js'
                    ]
                }
            }
        },

        jscs: {
            options: {
                config: ".jscsrc",
                excludeFiles: ["app/js/vendor/**/*.js"],
                force: true
            },
            console: {
                options: {
                    reporter: 'console'
                },
                files: {
                    src: ['Gruntfile.js', 'app/js/**/*.js', 'test/**/*.js']
                }
            },
            xml: {
                options: {
                    reporterOutput: 'reports/jscs.xml',
                    reporter: 'checkstyle'
                },
                files: {
                    src: ['Gruntfile.js', 'app/js/**/*.js', 'test/**/*.js']
                }
            }
        },

        karma: {
            unit: {
                options: {
                    configFile: 'karma.conf.js'
                }
            }
        },

        /*
         * Build a WAR (web archive) without Maven or the JVM installed.
         */
        war: {
            development: {
                options: {
                    war_dist_folder: "target",
                    war_verbose: true,
                    war_name: 'neon-gtd-dev-' + packageJSON.version,
                    webxml_welcome: 'index.html',
                    webxml_display_name: packageJSON.shortDescription,
                    webxml_mime_mapping: [{
                        extension: 'woff',
                        mime_type: 'application/font-woff'
                    }]
                },
                files: [{
                    expand: true,
                    cwd: ".",
                    src: ["app/**", "!**/*.less"],
                    dest: ""
                }]
            },
            production: {
                options: {
                    war_dist_folder: "target",
                    war_verbose: true,
                    war_name: 'neon-gtd-' + packageJSON.version,
                    webxml_welcome: 'index.html',
                    webxml_display_name: packageJSON.shortDescription,
                    webxml_mime_mapping: [{
                        extension: 'woff',
                        mime_type: 'application/font-woff'
                    }]
                },
                files: [{
                    expand: true,
                    cwd: "build/",
                    src: ["**"],
                    dest: "app/"
                }]
            }
        },

        yuidoc: {
            compile: {
                name: packageJSON.shortDescription,
                description: packageJSON.description,
                version: packageJSON.version,
                url: packageJSON.repository.url,
                logo: '../app/img/Neon_60x34.png',
                options: {
                    paths: 'app/js',
                    exclude: "vendor",
                    outdir: 'docs'
                }
            }
        },

        watch: {
            less: {
                files: ['app/css/**/*.less'],
                tasks: ['compile-less'],
                options: {
                    spawn: false
                }
            }
        }
    });

    grunt.loadNpmTasks('grunt-angular-templates');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-cssmin');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-yuidoc');
    grunt.loadNpmTasks('grunt-exec');
    grunt.loadNpmTasks('grunt-git-describe');
    grunt.loadNpmTasks('grunt-injector');
    grunt.loadNpmTasks('grunt-jscs');
    grunt.loadNpmTasks('grunt-karma');
    grunt.loadNpmTasks('grunt-spritesmith');
    grunt.loadNpmTasks('grunt-usemin');
    grunt.loadNpmTasks('grunt-war');
    grunt.loadNpmTasks('grunt-wiredep');

    grunt.registerTask("bower_install", ["clean:lib", "exec:bower_install", "wiredep"]);
    grunt.registerTask('compile-less', ['clean:less', 'injector:less', 'less:themeLightGreen', 'less:themeDarkGreen', 'less:themeDarkPurple', 'sprite']);
    grunt.registerTask('saveRevision', function() {
        grunt.event.once('git-describe', function(rev) {
            var date = new Date(Date.now()).toISOString();
            date = date.replace(/-/g, '.').replace(/T.*/, '');

            var versionObj = {
                name: packageJSON.name,
                version: packageJSON.version + '-' + rev[2] + '-g' + rev[3] + '-' + date
            };
            grunt.file.write(versionJSONFile, JSON.stringify(versionObj));
        });
        grunt.task.run('git-describe');
    });
    grunt.registerTask('test', ['jshint:console', 'jscs:console', 'karma']);

    var defaultTasks = [
        'saveRevision',
        'wiredep',
        'injector',
        'less:themeLightGreen',
        'less:themeDarkGreen',
        'less:themeDarkPurple',
        'sprite',
        'useminPrepare',
        'ngtemplates',
        'concat',
        'uglify',
        'cssmin',
        'copy',
        'jshint:xml',
        'jscs:xml',
        'yuidoc',
        'usemin',
        'war'
    ];

    grunt.registerTask('no-bower', ['clean:docs', 'clean:war', 'clean:tests', 'clean:build'].concat(defaultTasks));
    grunt.registerTask('default', ['clean', 'exec:bower_install'].concat(defaultTasks));
};
<|endoftext|>