<commit_before>/*jshint sub:true*/

/**
 * Copyright (c) 2015 Metropolitan Transportation Authority
 *
 * 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.
 *
 * @authors https://github.com/camsys/onebusaway-nyc-atstop/graphs/contributors
 */

angular.module('atstop', ['ionic', 'atstop.controllers', 'atstop.services', 'atstop.directives', 'leaflet-directive', 'ngCordova', 'angular-cache', 'timer', 'angular-svg-round-progress'])

// global timeout variable for HTTP requests
.value('httpTimeout', 5000)

.constant('$ionicLoadingConfig', {
    template: '<ion-spinner></ion-spinner>',
    showBackdrop: false
})

.run(function($ionicPlatform, $ionicPopup, $cordovaNetwork) {
    $ionicPlatform.ready(function() {
        if (window.cordova && window.cordova.plugins.Keyboard) {
            cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);

        }

        if (window.StatusBar) {
            StatusBar.styleDefault();
        }

        //checking if app is in cordova. Otherwise, don't worry about network connections.
        if (window.cordova && $cordovaNetwork.isOffline()) {
            $ionicPopup.alert({
                    title: "Internet Disconnected",
                    content: "The internet is not available on your device."
                })
                .then(function(result) {
                    if (result) {
                        ionic.Platform.exitApp();
                    }
                });
        }
    });
})

// use Angular Cache by default
.run(function($http, CacheFactory) {
    $http.defaults.cache = CacheFactory('dataCache', {
        maxAge: 15 * 60 * 1000, // Items added to this cache expire after 15 minutes
        cacheFlushInterval: 60 * 60 * 1000, // This cache will clear itself every hour
        deleteOnExpire: 'aggressive' // Items will be deleted from this cache when they expire
    });
})

.run(function($rootScope, $ionicHistory, $ionicLoading, $ionicPopup, $cordovaNetwork, $timeout, $ionicTabsDelegate) {

    // if 'loading:show' is broadcasted then show the loading indicator or hide if 'loading:hide' is broadcasted
    $rootScope.$on('loading:show', function() {
        $ionicLoading.show();
    });

    $rootScope.$on('loading:hide', function() {
        $ionicLoading.hide();
    });

    // Do we need this?
    $rootScope.$on('requestRejection', function(obj, data) {
        $ionicLoading.hide();

        if (data.config.url.indexOf("autocomplete") == -1) {
            var popup = $ionicPopup.alert({
                title: "Error",
                content: "Something went wrong. Please check your internet connection."
            });
            $timeout(function() {
                popup.close();
            }, 3000);
        }

    });
})

.config(function($httpProvider, $ionicConfigProvider) {
    $ionicConfigProvider.views.swipeBackEnabled(false);
    $ionicConfigProvider.tabs.position('bottom');

    if (ionic.Platform.isAndroid()) {
        $ionicConfigProvider.views.transition('none');
    }

    $httpProvider.interceptors.push(function($rootScope) {
        return {
            request: function(config) {
                $rootScope.$broadcast('loading:show');
                return config;
            },
            requestError: function(rejection) {
                $rootScope.$broadcast('requestRejection', rejection);
                return rejection;
            },
            response: function(response) {
                $rootScope.$broadcast('loading:hide');
                return response;
            },
            responseError: function(rejection) {
                $rootScope.$broadcast('requestRejection', rejection);
                return rejection;
            }
        };
    });
})

.config(function($stateProvider, $urlRouterProvider) {
    $stateProvider

    // abstract state for the tabs
        .state('tab', {
        url: "/tab",
        abstract: true,
        templateUrl: "templates/tabs.html"
    })

    .state('tab.home', {
        url: '/home',
        cache: false,
        views: {
            'tab-home': {
                templateUrl: 'templates/tab-home.html',
                controller: 'SearchCtrl'
            }
        }
    })

    .state('tab.route', {
        url: '/route/:routeId/:routeName',
        views: {
            'tab-home': {
                templateUrl: 'templates/route.html',
                controller: 'RouteCtrl'
            }
        }
    })

    .state('tab.route-favorites', {
        url: '/route-favorites/:routeId/:routeName',
        views: {
            'tab-favorites': {
                templateUrl: 'templates/route.html',
                controller: 'RouteCtrl'
            }
        }
    })

    .state('tab.geolocation', {
        url: '/geolocation/:latitude/:longitude/:address',
        cache: false,
        views: {
            'tab-home': {
                templateUrl: 'templates/tab-nearby-stops-and-routes.html',
                controller: 'NearbyStopsAndRoutesCtrl'
            }
        }
    })

    .state('tab.atstop', {
        url: '/atstop/:stopId/:stopName',
        cache: false,
        views: {
            'tab-home': {
                templateUrl: 'templates/atstop.html',
                controller: 'AtStopCtrl'
            }
        }
    })

    .state('tab.map', {
        url: '/map/:routeId/:routeName/:stopId',
        cache: false,
        views: {
            'tab-home': {
                templateUrl: 'templates/map.html',
                controller: 'MapCtrl'
            }
        }
    })

    .state('tab.about', {
        url: '/about',
        views: {
            'tab-home': {
                templateUrl: 'templates/about.html',
                controller: 'AboutCtrl'
            }
        }
    })

    .state('tab.favorites', {
        url: '/favorites',
        cache: false,
        views: {
            'tab-favorites': {
                templateUrl: 'templates/tab-favorites.html',
                controller: 'FavoritesCtrl'
            }
        }
    })

    .state('tab.atstop-favorites', {
        url: '/atstop-favorites/:stopId/:stopName',
        cache: false,
        views: {
            'tab-favorites': {
                templateUrl: 'templates/atstop.html',
                controller: 'AtStopCtrl'
            }
        }
    })

    .state('tab.map-favorites', {
        url: '/map-favorites/:routeId/:routeName/:stopId',
        cache: false,
        views: {
            'tab-favorites': {
                templateUrl: 'templates/map.html',
                controller: 'MapCtrl'
            }
        }
    })

    .state('tab.nearby-stops-and-routes', {
        url: '/nearby-stops-and-routes',
        cache: false,
        views: {
            'tab-nearby-stops-and-routes': {
                templateUrl: 'templates/tab-nearby-stops-and-routes.html',
                controller: 'NearbyStopsAndRoutesCtrl'
            }
        }
    })

    .state('tab.atstop-gps', {
        url: '/atstop-gps/:stopId/:stopName',
        cache: false,
        views: {
            'tab-nearby-stops-and-routes': {
                templateUrl: 'templates/atstop.html',
                controller: 'AtStopCtrl'
            }
        }
    })

    .state('tab.map-gps', {
        url: '/map-gps/:routeId/:routeName/:stopId',
        cache: false,
        views: {
            'tab-nearby-stops-and-routes': {
                templateUrl: 'templates/map.html',
                controller: 'MapCtrl'
            }
        }
    });

    // if none of the above states are matched, use this as the fallback
    $urlRouterProvider.otherwise('/tab/home');
});
<commit_msg>App: We should create a default cache storage only if it does not exist already. Otherwise, the ngDescribe fails trying to create the existing dataCache.<commit_after>/*jshint sub:true*/

/**
 * Copyright (c) 2015 Metropolitan Transportation Authority
 *
 * 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.
 *
 * @authors https://github.com/camsys/onebusaway-nyc-atstop/graphs/contributors
 */

angular.module('atstop', ['ionic', 'atstop.controllers', 'atstop.services', 'atstop.directives', 'leaflet-directive', 'ngCordova', 'angular-cache', 'timer', 'angular-svg-round-progress'])

// global timeout variable for HTTP requests
.value('httpTimeout', 5000)

.constant('$ionicLoadingConfig', {
    template: '<ion-spinner></ion-spinner>',
    showBackdrop: false
})

.run(function($ionicPlatform, $ionicPopup, $cordovaNetwork) {
    $ionicPlatform.ready(function() {
        if (window.cordova && window.cordova.plugins.Keyboard) {
            cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);

        }

        if (window.StatusBar) {
            StatusBar.styleDefault();
        }

        //checking if app is in cordova. Otherwise, don't worry about network connections.
        if (window.cordova && $cordovaNetwork.isOffline()) {
            $ionicPopup.alert({
                    title: "Internet Disconnected",
                    content: "The internet is not available on your device."
                })
                .then(function(result) {
                    if (result) {
                        ionic.Platform.exitApp();
                    }
                });
        }
    });
})

// use Angular Cache by default
.run(function($http, CacheFactory) {
  if (!CacheFactory.get('dataCache')) {
    $http.defaults.cache = CacheFactory('dataCache', {
        maxAge: 15 * 60 * 1000, // Items added to this cache expire after 15 minutes
        cacheFlushInterval: 60 * 60 * 1000, // This cache will clear itself every hour
        deleteOnExpire: 'aggressive' // Items will be deleted from this cache when they expire
    });
  }
})

.run(function($rootScope, $ionicHistory, $ionicLoading, $ionicPopup, $cordovaNetwork, $timeout, $ionicTabsDelegate) {

    // if 'loading:show' is broadcasted then show the loading indicator or hide if 'loading:hide' is broadcasted
    $rootScope.$on('loading:show', function() {
        $ionicLoading.show();
    });

    $rootScope.$on('loading:hide', function() {
        $ionicLoading.hide();
    });

    // Do we need this?
    $rootScope.$on('requestRejection', function(obj, data) {
        $ionicLoading.hide();

        if (data.config.url.indexOf("autocomplete") == -1) {
            var popup = $ionicPopup.alert({
                title: "Error",
                content: "Something went wrong. Please check your internet connection."
            });
            $timeout(function() {
                popup.close();
            }, 3000);
        }

    });
})

.config(function($httpProvider, $ionicConfigProvider) {
    $ionicConfigProvider.views.swipeBackEnabled(false);
    $ionicConfigProvider.tabs.position('bottom');

    if (ionic.Platform.isAndroid()) {
        $ionicConfigProvider.views.transition('none');
    }

    $httpProvider.interceptors.push(function($rootScope) {
        return {
            request: function(config) {
                $rootScope.$broadcast('loading:show');
                return config;
            },
            requestError: function(rejection) {
                $rootScope.$broadcast('requestRejection', rejection);
                return rejection;
            },
            response: function(response) {
                $rootScope.$broadcast('loading:hide');
                return response;
            },
            responseError: function(rejection) {
                $rootScope.$broadcast('requestRejection', rejection);
                return rejection;
            }
        };
    });
})

.config(function($stateProvider, $urlRouterProvider) {
    $stateProvider

    // abstract state for the tabs
        .state('tab', {
        url: "/tab",
        abstract: true,
        templateUrl: "templates/tabs.html"
    })

    .state('tab.home', {
        url: '/home',
        cache: false,
        views: {
            'tab-home': {
                templateUrl: 'templates/tab-home.html',
                controller: 'SearchCtrl'
            }
        }
    })

    .state('tab.route', {
        url: '/route/:routeId/:routeName',
        views: {
            'tab-home': {
                templateUrl: 'templates/route.html',
                controller: 'RouteCtrl'
            }
        }
    })

    .state('tab.route-favorites', {
        url: '/route-favorites/:routeId/:routeName',
        views: {
            'tab-favorites': {
                templateUrl: 'templates/route.html',
                controller: 'RouteCtrl'
            }
        }
    })

    .state('tab.geolocation', {
        url: '/geolocation/:latitude/:longitude/:address',
        cache: false,
        views: {
            'tab-home': {
                templateUrl: 'templates/tab-nearby-stops-and-routes.html',
                controller: 'NearbyStopsAndRoutesCtrl'
            }
        }
    })

    .state('tab.atstop', {
        url: '/atstop/:stopId/:stopName',
        cache: false,
        views: {
            'tab-home': {
                templateUrl: 'templates/atstop.html',
                controller: 'AtStopCtrl'
            }
        }
    })

    .state('tab.map', {
        url: '/map/:routeId/:routeName/:stopId',
        cache: false,
        views: {
            'tab-home': {
                templateUrl: 'templates/map.html',
                controller: 'MapCtrl'
            }
        }
    })

    .state('tab.about', {
        url: '/about',
        views: {
            'tab-home': {
                templateUrl: 'templates/about.html',
                controller: 'AboutCtrl'
            }
        }
    })

    .state('tab.favorites', {
        url: '/favorites',
        cache: false,
        views: {
            'tab-favorites': {
                templateUrl: 'templates/tab-favorites.html',
                controller: 'FavoritesCtrl'
            }
        }
    })

    .state('tab.atstop-favorites', {
        url: '/atstop-favorites/:stopId/:stopName',
        cache: false,
        views: {
            'tab-favorites': {
                templateUrl: 'templates/atstop.html',
                controller: 'AtStopCtrl'
            }
        }
    })

    .state('tab.map-favorites', {
        url: '/map-favorites/:routeId/:routeName/:stopId',
        cache: false,
        views: {
            'tab-favorites': {
                templateUrl: 'templates/map.html',
                controller: 'MapCtrl'
            }
        }
    })

    .state('tab.nearby-stops-and-routes', {
        url: '/nearby-stops-and-routes',
        cache: false,
        views: {
            'tab-nearby-stops-and-routes': {
                templateUrl: 'templates/tab-nearby-stops-and-routes.html',
                controller: 'NearbyStopsAndRoutesCtrl'
            }
        }
    })

    .state('tab.atstop-gps', {
        url: '/atstop-gps/:stopId/:stopName',
        cache: false,
        views: {
            'tab-nearby-stops-and-routes': {
                templateUrl: 'templates/atstop.html',
                controller: 'AtStopCtrl'
            }
        }
    })

    .state('tab.map-gps', {
        url: '/map-gps/:routeId/:routeName/:stopId',
        cache: false,
        views: {
            'tab-nearby-stops-and-routes': {
                templateUrl: 'templates/map.html',
                controller: 'MapCtrl'
            }
        }
    });

    // if none of the above states are matched, use this as the fallback
    $urlRouterProvider.otherwise('/tab/home');
});
<|endoftext|>


<commit_before>/*jshint node:true*/
module.exports = {
  useYarn: true,
  scenarios: [
    {
      name: 'default',
      dependencies: { }
    },
    {
      name: 'ember-2.0',
      bower: {
        dependencies: {
          ember: '~2.0.0',
          'ember-cli-shims': '0.0.6',
          'ember-data': '~2.0.0',
        },
      },
      npm: {
        devDependencies: {
          'ember-cli-shims': null,
          'ember-data': '~2.0.0',
          'ember-source': null,
        },
      },
    },
    {
      name: 'ember-lts-2.4',
      bower: {
        dependencies: {
          ember: 'components/ember#lts-2-4',
          'ember-cli-shims': '0.1.0',
        },
        resolutions: {
          ember: 'lts-2-4',
        },
      },
      npm: {
        devDependencies: {
          'ember-cli-shims': null,
          'ember-data': '~2.4.0',
          'ember-source': null,
        },
      },
    },
    {
      name: 'ember-lts-2.8',
      bower: {
        dependencies: {
          ember: 'components/ember#lts-2-8',
        },
        resolutions: {
          ember: 'lts-2-8',
        },
      },
      npm: {
        devDependencies: {
          'ember-data': '~2.8.0',
          'ember-source': null,
        },
      },
    },
    {
      name: 'ember-lts-2.12',
      npm: {
        devDependencies: {
          'ember-data': '~2.12.0',
          'ember-source': '~2.12.0',
        },
      },
    },
    {
      name: 'ember-lts-2.16',
      npm: {
        devDependencies: {
          'ember-data': '~2.16.0',
          'ember-source': '~2.16.0-beta.1',
        },
      },
    },
    {
      name: 'ember-release',
      bower: {
        dependencies: {
          ember: 'components/ember#release',
        },
        resolutions: {
          ember: 'release',
        },
      },
      npm: {
        devDependencies: {
          'ember-data': 'emberjs/data#release',
          'ember-source': null,
        },
      },
    },
    {
      name: 'ember-beta',
      bower: {
        dependencies: {
          ember: 'components/ember#beta',
        },
        resolutions: {
          ember: 'beta',
        },
      },
      npm: {
        devDependencies: {
          'ember-data': 'emberjs/data#beta',
          'ember-source': null,
        },
      },
    },
    {
      name: 'ember-canary',
      bower: {
        dependencies: {
          ember: 'components/ember#canary',
        },
        resolutions: {
          ember: 'canary',
        },
      },
      npm: {
        devDependencies: {
          'ember-data': 'emberjs/data#master',
          'ember-source': null,
        },
      },
    },
  ]
};
<commit_msg>ember-try: Fix `default` scenario<commit_after>/*jshint node:true*/
module.exports = {
  useYarn: true,
  scenarios: [
    {
      name: 'default',
      bower: {
        dependencies: {},
      },
    },
    {
      name: 'ember-2.0',
      bower: {
        dependencies: {
          ember: '~2.0.0',
          'ember-cli-shims': '0.0.6',
          'ember-data': '~2.0.0',
        },
      },
      npm: {
        devDependencies: {
          'ember-cli-shims': null,
          'ember-data': '~2.0.0',
          'ember-source': null,
        },
      },
    },
    {
      name: 'ember-lts-2.4',
      bower: {
        dependencies: {
          ember: 'components/ember#lts-2-4',
          'ember-cli-shims': '0.1.0',
        },
        resolutions: {
          ember: 'lts-2-4',
        },
      },
      npm: {
        devDependencies: {
          'ember-cli-shims': null,
          'ember-data': '~2.4.0',
          'ember-source': null,
        },
      },
    },
    {
      name: 'ember-lts-2.8',
      bower: {
        dependencies: {
          ember: 'components/ember#lts-2-8',
        },
        resolutions: {
          ember: 'lts-2-8',
        },
      },
      npm: {
        devDependencies: {
          'ember-data': '~2.8.0',
          'ember-source': null,
        },
      },
    },
    {
      name: 'ember-lts-2.12',
      npm: {
        devDependencies: {
          'ember-data': '~2.12.0',
          'ember-source': '~2.12.0',
        },
      },
    },
    {
      name: 'ember-lts-2.16',
      npm: {
        devDependencies: {
          'ember-data': '~2.16.0',
          'ember-source': '~2.16.0-beta.1',
        },
      },
    },
    {
      name: 'ember-release',
      bower: {
        dependencies: {
          ember: 'components/ember#release',
        },
        resolutions: {
          ember: 'release',
        },
      },
      npm: {
        devDependencies: {
          'ember-data': 'emberjs/data#release',
          'ember-source': null,
        },
      },
    },
    {
      name: 'ember-beta',
      bower: {
        dependencies: {
          ember: 'components/ember#beta',
        },
        resolutions: {
          ember: 'beta',
        },
      },
      npm: {
        devDependencies: {
          'ember-data': 'emberjs/data#beta',
          'ember-source': null,
        },
      },
    },
    {
      name: 'ember-canary',
      bower: {
        dependencies: {
          ember: 'components/ember#canary',
        },
        resolutions: {
          ember: 'canary',
        },
      },
      npm: {
        devDependencies: {
          'ember-data': 'emberjs/data#master',
          'ember-source': null,
        },
      },
    },
  ]
};
<|endoftext|>


<commit_before>const React = require("react")
const gatsby = jest.requireActual("gatsby")

const gatsbyMock = {
  ...gatsby,
  graphql: jest.fn(),
  Link: jest
    .fn()
    .mockImplementation(
      ({
        activeClassName,
        activeStyle,
        getProps,
        innerRef,
        partiallyActive,
        ref,
        replace,
        to,
        ...rest
      }) =>
        React.createElement("a", {
          ...rest,
          href: to,
        })
    ),
  StaticQuery: jest.fn(),
  useStaticQuery: jest.fn(),
}

gatsbyMock.StaticQuery.mockImplementationOnce(({ render }) =>
  render({
    site: {
      siteMetadata: {
        title: "Educodar - Aulas de programação grátis para jovens",
        titleTemplate: "%s · Educodar",
        siteUrl: "https://educodar.com.br",
        description:
          "Projeto de ensino de programação básica e exposição a T.I. para estudantes de escolas de comunidades. Iniciativa sem fins lucrativos. Cursos e alimentação gratuitos.",
        image: "/images/logo.png",
      },
    },
  })
)

module.exports = gatsbyMock
<commit_msg>test(gatsby): always mock StaticQuery implementation<commit_after>const React = require("react")
const gatsby = jest.requireActual("gatsby")

const gatsbyMock = {
  ...gatsby,
  graphql: jest.fn(),
  Link: jest
    .fn()
    .mockImplementation(
      ({
        activeClassName,
        activeStyle,
        getProps,
        innerRef,
        partiallyActive,
        ref,
        replace,
        to,
        ...rest
      }) =>
        React.createElement("a", {
          ...rest,
          href: to,
        })
    ),
  StaticQuery: jest.fn(),
  useStaticQuery: jest.fn(),
}

gatsbyMock.StaticQuery.mockImplementation(({ render }) =>
  render({
    site: {
      siteMetadata: {
        title: "Educodar - Aulas de programação grátis para jovens",
        titleTemplate: "%s · Educodar",
        siteUrl: "https://educodar.com.br",
        description:
          "Projeto de ensino de programação básica e exposição a T.I. para estudantes de escolas de comunidades. Iniciativa sem fins lucrativos. Cursos e alimentação gratuitos.",
        image: "/images/logo.png",
      },
    },
  })
)

module.exports = gatsbyMock
<|endoftext|>


<commit_before>/* eslint-env node */
module.exports = {
  scenarios: [
    {
      name: 'ember-1.13',
      bower: {
        dependencies: {
          'ember': '~1.13.0'
        },
        resolutions: {
          'ember': '~1.13.0'
        }
      },
      npm: {
        devDependencies: {
          'ember-data': '~2.12.0',
          'ember-hash-helper-polyfill': '0.1.2',
          'ember-source': null
        }
      }
    },
    {
      name: 'ember-lts-2.8',
      bower: {
        dependencies: {
          'ember': 'components/ember#lts-2-8'
        },
        resolutions: {
          'ember': 'lts-2-8'
        }
      },
      npm: {
        devDependencies: {
          'ember-source': null
        }
      }
    },
    {
      name: 'ember-lts-2.12',
      npm: {
        devDependencies: {
          'ember-source': '~2.12.0'
        }
      }
    },
    {
      name: 'ember-release',
      bower: {
        dependencies: {
          'ember': 'components/ember#release'
        },
        resolutions: {
          'ember': 'release'
        }
      },
      npm: {
        devDependencies: {
          'ember-source': null
        }
      }
    },
    {
      name: 'ember-beta',
      bower: {
        dependencies: {
          'ember': 'components/ember#beta'
        },
        resolutions: {
          'ember': 'beta'
        }
      },
      npm: {
        devDependencies: {
          'ember-source': null
        }
      }
    },
    {
      name: 'ember-canary',
      bower: {
        dependencies: {
          'ember': 'components/ember#canary'
        },
        resolutions: {
          'ember': 'canary'
        }
      },
      npm: {
        devDependencies: {
          'ember-source': null
        }
      }
    },
    {
      name: 'ember-default',
      npm: {
        devDependencies: {}
      }
    }
  ]
};
<commit_msg>remove ember-data pin<commit_after>/* eslint-env node */
module.exports = {
  scenarios: [
    {
      name: 'ember-1.13',
      bower: {
        dependencies: {
          'ember': '~1.13.0'
        },
        resolutions: {
          'ember': '~1.13.0'
        }
      },
      npm: {
        devDependencies: {
          'ember-hash-helper-polyfill': '0.1.2',
          'ember-source': null
        }
      }
    },
    {
      name: 'ember-lts-2.8',
      bower: {
        dependencies: {
          'ember': 'components/ember#lts-2-8'
        },
        resolutions: {
          'ember': 'lts-2-8'
        }
      },
      npm: {
        devDependencies: {
          'ember-source': null
        }
      }
    },
    {
      name: 'ember-lts-2.12',
      npm: {
        devDependencies: {
          'ember-source': '~2.12.0'
        }
      }
    },
    {
      name: 'ember-release',
      bower: {
        dependencies: {
          'ember': 'components/ember#release'
        },
        resolutions: {
          'ember': 'release'
        }
      },
      npm: {
        devDependencies: {
          'ember-source': null
        }
      }
    },
    {
      name: 'ember-beta',
      bower: {
        dependencies: {
          'ember': 'components/ember#beta'
        },
        resolutions: {
          'ember': 'beta'
        }
      },
      npm: {
        devDependencies: {
          'ember-source': null
        }
      }
    },
    {
      name: 'ember-canary',
      bower: {
        dependencies: {
          'ember': 'components/ember#canary'
        },
        resolutions: {
          'ember': 'canary'
        }
      },
      npm: {
        devDependencies: {
          'ember-source': null
        }
      }
    },
    {
      name: 'ember-default',
      npm: {
        devDependencies: {}
      }
    }
  ]
};
<|endoftext|>


<commit_before>/**
* @fileOverview Produces line chart
* @todo ADD startIndex and endIndex to constructor with proper comments
* @todo add comments to panzoom
* @todo add comment for pan speed
* @todo test what happens if i have less data than numdisplaypoints
*/

class DataVisualizer
{
    /**
     * @class DataVisualizer
     * @classdesc DataVisualizer is a wrapper class for line-chart in chart.js.
     *
     * @param {string}    id -                                  ID of canvas that chart should be rendered on.
     * @param {Objects[]} visualizerConfig -                    The configuration for y-axes data.
     * @param {string}    visualizerConfig[].buttonId -         The ID of toggle button corresponding to y-axes data.
     * @param {Integer[]} visualizerConfig[].data -             Array with data to be displayed. Refresh chart must be called when data arrays are updated.When array is updated to display, call refresh chart.
     * @param {integer}   visualizerConfig[].datasetIndex -     Index attribute set on buttonId.
     * @param {integer}   visualizerConfig[].color -            Color of data points.
     * @param {string}    visualizerConfig[].name -             Name of data to be used as label.
     * @param {string}    visualizerConfig[].units -            Units used for data (ie meters).
     * @param {string}    visualizerConfig[].fill -             Determines whether this data should be represented as area or line chart.
     * @param {integer}   defaultNumDisplayPoints -             Number of data points to display on the graph by default.
     */
    constructor(id, visualizerConfig, defaultNumDisplayPoints)
    {
        /**
         * Number of total packets added to chart. Only recalculated during chart refresh.
         *
         * @private
         * @name    DataVisualizer#numPackets
         * @type    Integer
         * @default 0
         */
        this.numPackets = 0;

        /**
         * Number of data points to display on line chart.
         *
         * @private
         * @name    DataVisualizer#numDisplayPoints
         * @type    Integer
         */
        this.numDisplayPoints = defaultNumDisplayPoints;

        /**
         * Default number of points to display on zoom reset.
         *
         * @private
         * @name    DataVisualizer#defaultNumDisplayPoints
         * @type    Integer
         */
        this.defaultNumDisplayPoints = defaultNumDisplayPoints;

        /**
         * If data is zoomed or panned, we will stop drawing new incoming datapoints.
         *
         * @private
         * @name    DataVisualizer#zoomed
         * @type    Bool
         * @default false
         */
         this.zoomed = false;

        /**
         * If data is zoomed or panned, we will stop drawing new incoming datapoints.
         *
         * @private
         * @name    DataVisualizer#panned
         * @type    Bool
         * @default false
         */
         this.panned = false;

        /**
         * The higher the number the slower the pan speed. Note it only changes
         * the speed of a flick scroll.
         *
         * @private
         * @name    DataVisualizer#panSpeed
         * @type    Integer
         * @default 10
         */
         this.panSpeed = 10;

        /**
         * Index of first datapoint to be drawn.
         *
         * @private
         * @name    DataVisualizer#startIndex
         * @type    Integer
         */
         this.startIndex = 0;

        /**
         * Index of last datapoint to be drawn.
         *
         * @private
         * @name    DataVisualizer#startIndex
         * @type    Integer
         */
         this.endIndex = 0;

        /**
         * Chart object to be used by chartjs for chart manipulation>
         *
         * @private
         * @name    DataVisualizer#chart
         * @type    Object
         */
        this.chart;

        /**
         * Data will hold all of the data that has been pushed to the graph,
         * whereas the charts datasets will only hold data to be displayed.
         *
         * @private
         * @name DataVisualizer#data
         * @type Array
         */
         this.data = [];

        /**
         * Resets zoom to default number of data points  and pans to the end
         * of the graph.
         *
         * @method  resetZoom
         * @name    DataVisualizer#resetZoom
         */
        this.resetZoom = function()
        {
            this.zoomed = false;
            this.panned = false;
            this.numDisplayPoints = this.defaultNumDisplayPoints;
            this.startIndex = this.numPackets - this.numDisplayPoints;

            this.refreshChart();

        }.bind(this)

        /**
         * Touch and scroll event handler used to pan the graph.
         *
         * @method  pan
         * @name    DataVisualizer#pan
         */
        this.pan = function(e)
        {
            // used for calculating how far we should pan
            var deltaX = 0;
            this.pan.startX = 0;

            this.panned = true;

            // in case of start of touch we shouldn't move at all, just need to grab touch position
            if (e.type == "touchstart")
            {
                this.pan.startX = e.touches[0].clientX;
            }

            // calculate how far to pan based on how far fingers have scrolled since last touchmove
            else if (e.type == "touchmove")
            {
                // only allow 2 finger pan
                if (e.changedTouches.length > 1)
                {
                    deltaX = startX - e.changedTouches[0].clientX;
                    this.pan.startX = e.changedTouches[0].clientX;
                }
            }

            // for wheel and scroll events
            else
            {
                // to fix issue where no scroll happens at all due to rounding of decimal values < |1|
                if (e.deltaX < 0)
                {
                    deltaX = e.deltaX - this.panSpeed;
                }
                else
                {
                    deltaX = e.deltaX + this.panSpeed;
                }
            }

            this.startIndex += (deltaX / this.panSpeed);
            if (this.startIndex < 0) {
                this.startIndex = 0;
            }
            if ((this.startIndex + this.numDisplayPoints) > this.numPackets) {
                this.startIndex = this.numPackets - this.numDisplayPoints;
            }

            // rounding both indexes as they will be used to index into arrays and need to be integers
            this.startIndex = Math.round(this.startIndex);
            this.endIndex = Math.round(this.startIndex + this.numDisplayPoints);

            // redrawing chart
            this.refreshChart();

            // preventing a page scroll
            e.preventDefault();

        }.bind(this);

        // set scroll and touch event listeners on canvas to add pan and zoom capabilities
        document.getElementById(id).addEventListener('wheel', this.pan);
        document.getElementById(id).addEventListener('scroll', this.pan);
        document.getElementById(id).addEventListener('touchstart', this.pan);
        document.getElementById(id).addEventListener('touchmove', this.pan);

        /**
         * Displays or hides the data corresponding to the selected button. Requires that initToggleButtons has been called.
         *
         * @method  toggleData
         * @name    DataVisualizer#toggleData
         *
         * @param {Object} button - Button element corresponding to data to be toggled.
         */
        this.toggleData = function(button)
        {
            var dataSetIndex = button.getAttribute('data-data-set-index');
            var dataSet = this.chart.config.data.datasets[dataSetIndex];
            var yAxis = this.chart.config.options.scales.yAxes[dataSetIndex];

            button.classList.remove("altitude-color");
            console.log(button);

            // toggling data and axis visibility
            dataSet.hidden = !dataSet.hidden;
            yAxis.display = !yAxis.display;

            this.chart.update();

        }.bind(this)

        /**
         * Adds the specified data to each dataset. There should be 1 value for
         * each dataset in the graph.
         *
         * @method  addDataPoint
         * @name    DataVisualizer#addDataPoint
         *
         * @param {Objects[]} data - Array with 1 value for each dataset.
         *                           If a visible dataset, the value should be an integer.
         */
        this.addDataPoint = function(data)
        {
            var i;
            var datasets = this.chart.config.data.datasets;

            for (i = 0; i < datasets.length; i++)
            {
                // pushing incoming data into overall data array
                this.data[i].push(data[i]);
            }

            this.refreshChart();

        }.bind(this)

        /**
         * Adds the specified data to each dataset. There should be 1 value for
         * each dataset in the graph.
         *
         * @method  addDataPoint
         * @name    DataVisualizer#addDataPoint
         *
         * @param {Objects[]} data - Array with 1 value for each dataset.
         *                           If a visible dataset, the value should be an integer.
         */
        this.refreshChart = function()
        {
            var i, j;
            var datasets    = this.chart.config.data.datasets;

            this.numPackets = (this.zoomed == true) ? this.numPackets : this.data[0].length;

            if (!this.zoomed && !this.panned) {
                this.startIndex  = (this.numPackets > this.numDisplayPoints) ? (this.numPackets - this.numDisplayPoints) : 0;
                this.endIndex    = (this.numPackets > this.numDisplayPoints) ? (this.startIndex + this.numDisplayPoints) : this.numPackets;
            }

            // pushing data from overall array to display data array
            for (i = 0; i < datasets.length; i++)
            {
                datasets[i].data.length = 0;

                for (j = this.startIndex; j < this.endIndex; j++) {
                    datasets[i].data.push(this.data[i][j]);
                }
            }

            packetIndex.length = 0;
            for (i = this.startIndex; i < this.endIndex; i++)
            {
                packetIndex.push(i);
            }

            this.chart.update();

        }.bind(this)

        /**
         * Initializes the graph using the parameters passed in through the visualizerConfig objects.
         * Also initializes the data array.
         *
         * @method  initGraph
         * @name    DataVisualizer#initGraph
         *
         * @private
         * @param {Objects[]} visualizerConfig                - The configuration for y-axes data.
         * @param {string}    visualizerConfig[].buttonId     - The ID of toggle button corresponding to y-axes data.
         * @param {integer}   visualizerConfig[].datasetIndex - Index attribute set on buttonId.
         * @param {integer}   visualizerConfig[].color        - Color of data points.
         * @param {string}    visualizerConfig[].name         - Name of data to be used as label.
         * @param {string}    visualizerConfig[].units        - Units used for data (ie meters).
         * @param {string}    visualizerConfig[].fill         - Determines whether this data should be represented as area or line chart.
         */
        this.initGraph = function(visualizerConfig)
        {
            for (var i = 0; i < visualizerConfig.length; i++)
            {
                this.data.push(visualizerConfig[i].data);

                // Configuring the data set options
                var dataConfig = {
                    label: visualizerConfig[i].name,
                    borderColor: visualizerConfig[i].color,
                    data: [],
                    backgroundColor: Samples.utils.transparentize(visualizerConfig[i].color, 0.9),
                    fill: visualizerConfig[i].fill,
                    yAxisID: visualizerConfig[i].buttonId
                };

                // Configuring the axes options
                var axesConfig = {
                    id: visualizerConfig[i].buttonId,
                    display: true,
                    position: (i % 2) ? "left" : "right",
                    scaleLabel: {
                        display: false,
                        labelString: visualizerConfig[i].units
                    },
                    ticks: {
                        callback:
                        (function(units) {
                            return function(label, index, labels) {
                                if (index == 0) {
                                    return units;
                                }
                                return Math.round(label);
                            }
                        })(visualizerConfig[i].units)
                    }
                };

                // push configuration to config array
                dataSetsConfig.push(dataConfig);
                yAxesConfig.push(axesConfig);
            }

            // setting up config variable for chart
            config.options.scales.yaxes = yAxesConfig;
            config.data.datasets = dataSetsConfig;

        }.bind(this)

        /**
         * Initializes toggle buttons to reference correct dataset.
         *
         * @method  initToggleButtons
         * @name    DataVisualizer#initToggleButtons
         *
         * @private
         * @param {Objects[]} visualizerConfig                - The configuration for y-axes data.
         * @param {string}    visualizerConfig[].buttonId     - The ID of toggle button corresponding to y-axes data.
         * @param {integer}   visualizerConfig[].datasetIndex - Index attribute set on buttonId.
         */
        function initToggleButtons(visualizerConfig)
        {
            var button;

            for (var i = 0; i < visualizerConfig.length; i++)
            {
                button = document.getElementById(visualizerConfig[i].buttonId);
                button.setAttribute('data-data-set-index', visualizerConfig[i].datasetIndex);
            }
        }

        // initialize graph configuration
        this.initGraph(visualizerConfig);

        // initializing toggle buttons
        initToggleButtons(visualizerConfig);

        // creating and configuring chart
        var ctx = document.getElementById(id).getContext("2d");
        this.chart = new Chart(ctx, config);
        Chart.defaults.global.defaultFontColor = "#ebebeb";
        Chart.defaults.global.defaultFontFamily = "'Lato','Helvetica Neue','Helvetica','Arial',sans-serif";

        console.log(this.chart)
    }
}

<commit_msg>Fixed pan bug on mobile<commit_after>/**
* @fileOverview Produces line chart
* @todo test what happens if i have less data than numdisplaypoints
*/

class DataVisualizer
{
    /**
     * @class DataVisualizer
     * @classdesc DataVisualizer is a wrapper class for line-chart in chart.js.
     *
     * @param {string}    id -                                  ID of canvas that chart should be rendered on.
     * @param {Objects[]} visualizerConfig -                    The configuration for y-axes data.
     * @param {string}    visualizerConfig[].buttonId -         The ID of toggle button corresponding to y-axes data.
     * @param {Integer[]} visualizerConfig[].data -             Array with data to be displayed. Refresh chart must be called when data arrays are updated.When array is updated to display, call refresh chart.
     * @param {integer}   visualizerConfig[].datasetIndex -     Index attribute set on buttonId.
     * @param {integer}   visualizerConfig[].color -            Color of data points.
     * @param {string}    visualizerConfig[].name -             Name of data to be used as label.
     * @param {string}    visualizerConfig[].units -            Units used for data (ie meters).
     * @param {string}    visualizerConfig[].fill -             Determines whether this data should be represented as area or line chart.
     * @param {integer}   defaultNumDisplayPoints -             Number of data points to display on the graph by default.
     */
    constructor(id, visualizerConfig, defaultNumDisplayPoints)
    {
        /**
         * Number of total packets added to chart. Only recalculated during chart refresh.
         *
         * @private
         * @name    DataVisualizer#numPackets
         * @type    Integer
         * @default 0
         */
        this.numPackets = 0;

        /**
         * Number of data points to display on line chart.
         *
         * @private
         * @name    DataVisualizer#numDisplayPoints
         * @type    Integer
         */
        this.numDisplayPoints = defaultNumDisplayPoints;

        /**
         * Default number of points to display on zoom reset.
         *
         * @private
         * @name    DataVisualizer#defaultNumDisplayPoints
         * @type    Integer
         */
        this.defaultNumDisplayPoints = defaultNumDisplayPoints;

        /**
         * If data is zoomed or panned, we will stop drawing new incoming datapoints.
         *
         * @private
         * @name    DataVisualizer#zoomed
         * @type    Bool
         * @default false
         */
         this.zoomed = false;

        /**
         * If data is zoomed or panned, we will stop drawing new incoming datapoints.
         *
         * @private
         * @name    DataVisualizer#panned
         * @type    Bool
         * @default false
         */
         this.panned = false;

        /**
         * The higher the number the slower the pan speed. Note it only changes
         * the speed of a flick scroll.
         *
         * @private
         * @name    DataVisualizer#panSpeed
         * @type    Integer
         * @default 10
         */
         this.panSpeed = 10;

        /**
         * Index of first datapoint to be drawn.
         *
         * @private
         * @name    DataVisualizer#startIndex
         * @type    Integer
         */
         this.startIndex = 0;

        /**
         * Index of last datapoint to be drawn.
         *
         * @private
         * @name    DataVisualizer#startIndex
         * @type    Integer
         */
         this.endIndex = 0;

        /**
         * Chart object to be used by chartjs for chart manipulation>
         *
         * @private
         * @name    DataVisualizer#chart
         * @type    Object
         */
        this.chart;

        /**
         * Data will hold all of the data that has been pushed to the graph,
         * whereas the charts datasets will only hold data to be displayed.
         *
         * @private
         * @name DataVisualizer#data
         * @type Array
         */
         this.data = [];

        /**
         * Resets zoom to default number of data points  and pans to the end
         * of the graph.
         *
         * @method  resetZoom
         * @name    DataVisualizer#resetZoom
         */
        this.resetZoom = function()
        {
            this.zoomed = false;
            this.panned = false;
            this.numDisplayPoints = this.defaultNumDisplayPoints;
            this.startIndex = this.numPackets - this.numDisplayPoints;

            this.refreshChart();

        }.bind(this)

        /**
         * Touch and scroll event handler used to pan the graph.
         *
         * @method  pan
         * @name    DataVisualizer#pan
         */
        this.pan = function(e)
        {
            // used for calculating how far we should pan
            var deltaX = 0;

            // "static" variable used to hold previous touch position
            this.pan.startX;

            this.panned = true;

            // in case of start of touch we shouldn't move at all, just need to grab touch position
            if (e.type == "touchstart")
            {
                this.pan.startX = e.touches[0].clientX;
                console.log(e.touches[0].clientX);
            }

            // calculate how far to pan based on how far fingers have scrolled since last touchmove
            else if (e.type == "touchmove")
            {
                // only allow 2 finger pan
                if (e.changedTouches.length > 0)
                {
                    console.log(this.pan.startX);
                    deltaX = this.pan.startX - e.changedTouches[0].clientX;
                    this.pan.startX = e.changedTouches[0].clientX;
                    console.log(e.changedTouches[0].clientX);
                }
            }

            // for wheel and scroll events
            else
            {
                // to fix issue where no scroll happens at all due to rounding of decimal values < |1|
                if (e.deltaX < 0)
                {
                    deltaX = e.deltaX - this.panSpeed;
                }
                else
                {
                    deltaX = e.deltaX + this.panSpeed;
                }
            }

            this.startIndex += (deltaX / this.panSpeed);
            if (this.startIndex < 0) {
                this.startIndex = 0;
            }
            if ((this.startIndex + this.numDisplayPoints) > this.numPackets) {
                this.startIndex = this.numPackets - this.numDisplayPoints;
            }

            // rounding both indexes as they will be used to index into arrays and need to be integers
            this.startIndex = Math.round(this.startIndex);
            this.endIndex = Math.round(this.startIndex + this.numDisplayPoints);

            // redrawing chart
            this.refreshChart();

            // preventing a page scroll
            e.preventDefault();

        }.bind(this);

        this.zoom = function(e)
        {
            console.log(e);

        }.bind(this)

        // set scroll and touch event listeners on canvas to add pan and zoom capabilities
        document.getElementById(id).addEventListener('wheel', this.pan);
        document.getElementById(id).addEventListener('scroll', this.pan);
        document.getElementById(id).addEventListener('touchstart', this.pan);
        document.getElementById(id).addEventListener('touchmove', this.pan);

        /**
         * Displays or hides the data corresponding to the selected button. Requires that initToggleButtons has been called.
         *
         * @method  toggleData
         * @name    DataVisualizer#toggleData
         *
         * @param {Object} button - Button element corresponding to data to be toggled.
         */
        this.toggleData = function(button)
        {
            var dataSetIndex = button.getAttribute('data-data-set-index');
            var dataSet = this.chart.config.data.datasets[dataSetIndex];
            var yAxis = this.chart.config.options.scales.yAxes[dataSetIndex];

            button.classList.remove("altitude-color");

            // toggling data and axis visibility
            dataSet.hidden = !dataSet.hidden;
            yAxis.display = !yAxis.display;

            this.chart.update();

        }.bind(this)

        /**
         * Adds the specified data to each dataset. There should be 1 value for
         * each dataset in the graph.
         *
         * @method  addDataPoint
         * @name    DataVisualizer#addDataPoint
         *
         * @param {Objects[]} data - Array with 1 value for each dataset.
         *                           If a visible dataset, the value should be an integer.
         */
        this.addDataPoint = function(data)
        {
            var i;
            var datasets = this.chart.config.data.datasets;

            for (i = 0; i < datasets.length; i++)
            {
                // pushing incoming data into overall data array
                this.data[i].push(data[i]);
            }

            this.refreshChart();

        }.bind(this)

        /**
         * Adds the specified data to each dataset. There should be 1 value for
         * each dataset in the graph.
         *
         * @method  addDataPoint
         * @name    DataVisualizer#addDataPoint
         *
         * @param {Objects[]} data - Array with 1 value for each dataset.
         *                           If a visible dataset, the value should be an integer.
         */
        this.refreshChart = function()
        {
            var i, j;
            var datasets    = this.chart.config.data.datasets;

            this.numPackets = (this.zoomed == true) ? this.numPackets : this.data[0].length;

            if (!this.zoomed && !this.panned) {
                this.startIndex  = (this.numPackets > this.numDisplayPoints) ? (this.numPackets - this.numDisplayPoints) : 0;
                this.endIndex    = (this.numPackets > this.numDisplayPoints) ? (this.startIndex + this.numDisplayPoints) : this.numPackets;
            }

            // pushing data from overall array to display data array
            for (i = 0; i < datasets.length; i++)
            {
                datasets[i].data.length = 0;

                for (j = this.startIndex; j < this.endIndex; j++) {
                    datasets[i].data.push(this.data[i][j]);
                }
            }

            packetIndex.length = 0;
            for (i = this.startIndex; i < this.endIndex; i++)
            {
                packetIndex.push(i);
            }

            this.chart.update();

        }.bind(this)

        /**
         * Initializes the graph using the parameters passed in through the visualizerConfig objects.
         * Also initializes the data array.
         *
         * @method  initGraph
         * @name    DataVisualizer#initGraph
         *
         * @private
         * @param {Objects[]} visualizerConfig                - The configuration for y-axes data.
         * @param {string}    visualizerConfig[].buttonId     - The ID of toggle button corresponding to y-axes data.
         * @param {integer}   visualizerConfig[].datasetIndex - Index attribute set on buttonId.
         * @param {integer}   visualizerConfig[].color        - Color of data points.
         * @param {string}    visualizerConfig[].name         - Name of data to be used as label.
         * @param {string}    visualizerConfig[].units        - Units used for data (ie meters).
         * @param {string}    visualizerConfig[].fill         - Determines whether this data should be represented as area or line chart.
         */
        this.initGraph = function(visualizerConfig)
        {
            for (var i = 0; i < visualizerConfig.length; i++)
            {
                this.data.push(visualizerConfig[i].data);

                // Configuring the data set options
                var dataConfig = {
                    label: visualizerConfig[i].name,
                    borderColor: visualizerConfig[i].color,
                    data: [],
                    backgroundColor: Samples.utils.transparentize(visualizerConfig[i].color, 0.9),
                    fill: visualizerConfig[i].fill,
                    yAxisID: visualizerConfig[i].buttonId
                };

                // Configuring the axes options
                var axesConfig = {
                    id: visualizerConfig[i].buttonId,
                    display: true,
                    position: (i % 2) ? "left" : "right",
                    scaleLabel: {
                        display: false,
                        labelString: visualizerConfig[i].units
                    },
                    ticks: {
                        callback:
                        (function(units) {
                            return function(label, index, labels) {
                                if (index == 0) {
                                    return units;
                                }
                                return Math.round(label);
                            }
                        })(visualizerConfig[i].units)
                    }
                };

                // push configuration to config array
                dataSetsConfig.push(dataConfig);
                yAxesConfig.push(axesConfig);
            }

            // setting up config variable for chart
            config.options.scales.yaxes = yAxesConfig;
            config.data.datasets = dataSetsConfig;

        }.bind(this)

        /**
         * Initializes toggle buttons to reference correct dataset.
         *
         * @method  initToggleButtons
         * @name    DataVisualizer#initToggleButtons
         *
         * @private
         * @param {Objects[]} visualizerConfig                - The configuration for y-axes data.
         * @param {string}    visualizerConfig[].buttonId     - The ID of toggle button corresponding to y-axes data.
         * @param {integer}   visualizerConfig[].datasetIndex - Index attribute set on buttonId.
         */
        function initToggleButtons(visualizerConfig)
        {
            var button;

            for (var i = 0; i < visualizerConfig.length; i++)
            {
                button = document.getElementById(visualizerConfig[i].buttonId);
                button.setAttribute('data-data-set-index', visualizerConfig[i].datasetIndex);
            }
        }

        // initialize graph configuration
        this.initGraph(visualizerConfig);

        // initializing toggle buttons
        initToggleButtons(visualizerConfig);

        // creating and configuring chart
        var ctx = document.getElementById(id).getContext("2d");
        this.chart = new Chart(ctx, config);
        Chart.defaults.global.defaultFontColor = "#ebebeb";
        Chart.defaults.global.defaultFontFamily = "'Lato','Helvetica Neue','Helvetica','Arial',sans-serif";
    }
}

<|endoftext|>


<commit_before>/* @flow weak */

/**
 * OfflineMobile Android Stocktakes Page
 * Sustainable Solutions (NZ) Ltd. 2016
 */

import React from 'react';
import {
  StyleSheet,
  View,
} from 'react-native';

import {
  Cell,
  DataTable,
  Header,
  HeaderCell,
  Row,
} from '../widgets/DataTable';

import Icon from 'react-native-vector-icons/Ionicons';
import { generateUUID } from '../database';
import { ListView } from 'realm/react-native';
import { Button } from '../widgets';
import globalStyles from '../globalStyles';

/**
* Renders the page for displaying Stocktakes.
* @prop   {Realm}               database    App wide database.
* @prop   {func}                navigateTo  callBack for navigation stack.
* @state  {ListView.DataSource} dataSource  DataTable input, used to update rows being rendered.
* @state  {Realm.Results}       Stocktakes  Filtered to have only customer_stocktake.
* @state  {string}              sortBy      Locked to createdDate created date column.
* @state  {boolean}             isAscending Direction sortBy should sort
*                                           (ascending/descending:true/false).
*/
export class StocktakesPage extends React.Component {
  constructor(props) {
    super(props);
    const dataSource = new ListView.DataSource({
      rowHasChanged: (row1, row2) => row1 !== row2,
    });
    this.state = {
      dataSource: dataSource,
      stocktakes: props.database.objects('Stocktake'),
      sortBy: 'createdDate',
      isAscending: false,
    };
    this.componentWillMount = this.componentWillMount.bind(this);
    this.deleteSelection = this.deleteSelection.bind(this);
    this.onColumnSort = this.onColumnSort.bind(this);
    this.renderHeader = this.renderHeader.bind(this);
    this.renderRow = this.renderRow.bind(this);
    this.refreshData = this.refreshData.bind(this);
    this.onNewStockTake = this.onNewStockTake.bind(this);
  }

  componentWillMount() {
    this.refreshData();
  }

  onColumnSort() {
    this.setState({
      isAscending: !this.state.isAscending,
    });
    this.refreshData();
  }

  onNewStockTake() {
    this.props.database.write(() => {
      this.props.database.create('Stocktake', {
        id: generateUUID(),
        name: 'best stocktake',
        createdDate: new Date(),
        status: 'new',
        comment: 'Testing StocktakesPage',
        serialNumber: '1337',
        lines: [],
      });
    });
    this.props.navigateTo('stocktakeManager', 'New StockTake');
  }

  /**
   * Takes an array of stocktakes and deletes them from database
   * @param {array} stocktakes  the array of stocktakes to delete
   */
  deleteSelection(stocktakes) {
    this.props.database.write(() => {
      for (const stocktake in stocktakes) {
        if (stocktakes.hasOwnProperty(stocktake)) {
          this.props.database.delete('Stocktake', stocktake);
        }
      }
    });
    this.refreshData();
  }

  /**
   * Updates data within dataSource in state according to the state of sortBy and
   * isAscending. This sort by on this page is always 'createdDate'.
   */
  refreshData() {
    const { stocktakes, sortBy, dataSource, isAscending } = this.state;
    const data = stocktakes.sorted(sortBy, !isAscending); // 2nd arg: reverse sort order if true
    this.setState({ dataSource: dataSource.cloneWithRows(data) });
  }

  renderHeader() {
    return (
      <Header style={globalStyles.dataTableHeader}>
        <HeaderCell
          style={globalStyles.dataTableHeaderCell}
          textStyle={globalStyles.dataTableText}
          width={COLUMN_WIDTHS[0]}
          text={'DESCRIPTION'}
        />
        <HeaderCell
          style={globalStyles.dataTableHeaderCell}
          textStyle={globalStyles.dataTableText}
          width={COLUMN_WIDTHS[1]}
          onPress={() => this.onColumnSort()}
          isAscending={this.state.isAscending}
          isSelected={this.state.sortBy === 'createdDate'}
          text={'CREATED DATE'}
        />
        <HeaderCell
          style={globalStyles.dataTableHeaderCell}
          textStyle={globalStyles.dataTableText}
          width={COLUMN_WIDTHS[2]}
          text={'STATUS'}
        />
        <HeaderCell
          style={globalStyles.dataTableHeaderCell}
          textStyle={globalStyles.dataTableText}
          width={COLUMN_WIDTHS[3]}
          text={'DELETE'}
        />
      </Header>
    );
  }

  renderRow(stocktake) {
    return (
      <Row
        style={globalStyles.dataTableRow}
        onPress={() => this.props.navigateTo('stocktakes', `StockTake ${stocktake.serialNumber}`)}
      >
        <Cell
          style={globalStyles.dataTableCell}
          textStyle={globalStyles.dataTableText}
          width={COLUMN_WIDTHS[0]}
        >
          {stocktake.comment}
        </Cell>
        <Cell
          style={globalStyles.dataTableCell}
          textStyle={globalStyles.dataTableText}
          width={COLUMN_WIDTHS[1]}
        >
          {stocktake.createdDate.toISOString()}
        </Cell>
        <Cell
          style={globalStyles.dataTableCell}
          textStyle={globalStyles.dataTableText}
          width={COLUMN_WIDTHS[2]}
        >
          {stocktake.status}
        </Cell>
        <Cell
          style={globalStyles.dataTableCell}
          textStyle={globalStyles.dataTableText}
          width={COLUMN_WIDTHS[3]}
        >
          <Icon name="md-remove-circle" size={15} color="grey" />
        </Cell>
      </Row>
    );
  }

  render() {
    return (
      <View style={globalStyles.pageContentContainer}>
        <View style={globalStyles.container}>
          <View style={globalStyles.horizontalContainer}>
            <Button
              style={globalStyles.button}
              textStyle={globalStyles.buttonText}
              text="New StockTake"
              onPress={this.onNewStockTake}
            />
          </View>
          <DataTable
            style={globalStyles.dataTable}
            listViewStyle={localStyles.listView}
            dataSource={this.state.dataSource}
            renderRow={this.renderRow}
            renderHeader={this.renderHeader}
          />
        </View>
      </View>
    );
  }
}

StocktakesPage.propTypes = {
  database: React.PropTypes.object,
  navigateTo: React.PropTypes.func.isRequired,
};
const COLUMN_WIDTHS = [6, 2, 2, 1];
const localStyles = StyleSheet.create({
  listView: {
    flex: 1,
  },
  rightMostCell: {
    borderRightWidth: 0,
  },
  dataTable: {
    flex: 1,
  },
});
<commit_msg>onDelete function created<commit_after>/* @flow weak */

/**
 * OfflineMobile Android Stocktakes Page
 * Sustainable Solutions (NZ) Ltd. 2016
 */

import React from 'react';
import {
  StyleSheet,
  View,
} from 'react-native';

import {
  Cell,
  DataTable,
  Header,
  HeaderCell,
  Row,
} from '../widgets/DataTable';

import Icon from 'react-native-vector-icons/Ionicons';
import { generateUUID } from '../database';
import { ListView } from 'realm/react-native';
import { Button } from '../widgets';
import globalStyles from '../globalStyles';

/**
* Renders the page for displaying Stocktakes.
* @prop   {Realm}               database    App wide database.
* @prop   {func}                navigateTo  callBack for navigation stack.
* @state  {ListView.DataSource} dataSource  DataTable input, used to update rows being rendered.
* @state  {Realm.Results}       Stocktakes  Filtered to have only customer_stocktake.
* @state  {string}              sortBy      Locked to createdDate created date column.
* @state  {boolean}             isAscending Direction sortBy should sort
*                                           (ascending/descending:true/false).
*/
export class StocktakesPage extends React.Component {
  constructor(props) {
    super(props);
    const dataSource = new ListView.DataSource({
      rowHasChanged: (row1, row2) => row1 !== row2,
    });
    this.state = {
      dataSource: dataSource,
      stocktakes: props.database.objects('Stocktake'),
      sortBy: 'createdDate',
      isAscending: false,
    };
    this.componentWillMount = this.componentWillMount.bind(this);
    this.onDelete = this.onDelete.bind(this);
    this.onColumnSort = this.onColumnSort.bind(this);
    this.renderHeader = this.renderHeader.bind(this);
    this.renderRow = this.renderRow.bind(this);
    this.refreshData = this.refreshData.bind(this);
    this.onNewStockTake = this.onNewStockTake.bind(this);
  }

  componentWillMount() {
    this.refreshData();
  }

  onColumnSort() {
    this.setState({
      isAscending: !this.state.isAscending,
    });
    this.refreshData();
  }

  onNewStockTake() {
    this.props.database.write(() => {
      this.props.database.create('Stocktake', {
        id: generateUUID(),
        name: 'best stocktake',
        createdDate: new Date(),
        status: 'new',
        comment: 'Testing StocktakesPage',
        serialNumber: '1337',
        lines: [],
      });
    });
    this.props.navigateTo('stocktakeManager', 'New StockTake');
  }

  /**
   * Takes an array of stocktakes and deletes them from database
   * @param {array} stocktakes  the array of stocktakes to delete
   */
  onDelete(stocktakes) {
    this.props.database.write(() => {
      for (const stocktake of stocktakes) {
        this.props.database.delete('Stocktake', stocktake);
      }
    });
    this.refreshData();
  }

  /**
   * Updates data within dataSource in state according to the state of sortBy and
   * isAscending. This sort by on this page is always 'createdDate'.
   */
  refreshData() {
    const { stocktakes, sortBy, dataSource, isAscending } = this.state;
    const data = stocktakes.sorted(sortBy, !isAscending); // 2nd arg: reverse sort order if true
    this.setState({ dataSource: dataSource.cloneWithRows(data) });
  }

  renderHeader() {
    return (
      <Header style={globalStyles.dataTableHeader}>
        <HeaderCell
          style={globalStyles.dataTableHeaderCell}
          textStyle={globalStyles.dataTableText}
          width={COLUMN_WIDTHS[0]}
          text={'DESCRIPTION'}
        />
        <HeaderCell
          style={globalStyles.dataTableHeaderCell}
          textStyle={globalStyles.dataTableText}
          width={COLUMN_WIDTHS[1]}
          onPress={() => this.onColumnSort()}
          isAscending={this.state.isAscending}
          isSelected={this.state.sortBy === 'createdDate'}
          text={'CREATED DATE'}
        />
        <HeaderCell
          style={globalStyles.dataTableHeaderCell}
          textStyle={globalStyles.dataTableText}
          width={COLUMN_WIDTHS[2]}
          text={'STATUS'}
        />
        <HeaderCell
          style={globalStyles.dataTableHeaderCell}
          textStyle={globalStyles.dataTableText}
          width={COLUMN_WIDTHS[3]}
          text={'DELETE'}
        />
      </Header>
    );
  }

  renderRow(stocktake) {
    return (
      <Row
        style={globalStyles.dataTableRow}
        onPress={() => this.props.navigateTo('stocktakes', `StockTake ${stocktake.serialNumber}`)}
      >
        <Cell
          style={globalStyles.dataTableCell}
          textStyle={globalStyles.dataTableText}
          width={COLUMN_WIDTHS[0]}
        >
          {stocktake.comment}
        </Cell>
        <Cell
          style={globalStyles.dataTableCell}
          textStyle={globalStyles.dataTableText}
          width={COLUMN_WIDTHS[1]}
        >
          {stocktake.createdDate.toISOString()}
        </Cell>
        <Cell
          style={globalStyles.dataTableCell}
          textStyle={globalStyles.dataTableText}
          width={COLUMN_WIDTHS[2]}
        >
          {stocktake.status}
        </Cell>
        <Cell
          style={globalStyles.dataTableCell}
          textStyle={globalStyles.dataTableText}
          width={COLUMN_WIDTHS[3]}
        >
          <Icon name="md-remove-circle" size={15} color="grey" />
        </Cell>
      </Row>
    );
  }

  render() {
    return (
      <View style={globalStyles.pageContentContainer}>
        <View style={globalStyles.container}>
          <View style={globalStyles.horizontalContainer}>
            <Button
              style={globalStyles.button}
              textStyle={globalStyles.buttonText}
              text="New StockTake"
              onPress={this.onNewStockTake}
            />
          </View>
          <DataTable
            style={globalStyles.dataTable}
            listViewStyle={localStyles.listView}
            dataSource={this.state.dataSource}
            renderRow={this.renderRow}
            renderHeader={this.renderHeader}
          />
        </View>
      </View>
    );
  }
}

StocktakesPage.propTypes = {
  database: React.PropTypes.object,
  navigateTo: React.PropTypes.func.isRequired,
};
const COLUMN_WIDTHS = [6, 2, 2, 1];
const localStyles = StyleSheet.create({
  listView: {
    flex: 1,
  },
  rightMostCell: {
    borderRightWidth: 0,
  },
  dataTable: {
    flex: 1,
  },
});
<|endoftext|>


<commit_before>"use strict";
/**
 * Created by warp on 29/04/2017.
 */
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
var core_1 = require("@angular/core");
var http_1 = require("@angular/http");
var config_1 = require("../config/config");
require("rxjs/Rx");
var RequestService = (function () {
    function RequestService(http, config) {
        this.http = http;
        this.conf = config;
    }
    /*
     Get base url of API
     */
    RequestService.prototype.getBaseUrl = function () {
        return this.conf.getConfiguration('apiBaseUrl');
    };
    /*
     Methodes Joueurs
     */
    // Liste tous les joueurs
    RequestService.prototype.listJoueur = function () {
        var _this = this;
        return this.getBaseUrl().switchMap(function (url) {
            return _this.http.get(url + 'joueur').map(function (res) { return res = res.json(); });
        });
    };
    // Récupére un joueur par ID
    RequestService.prototype.showJoueur = function (id) {
        var _this = this;
        return this.getBaseUrl().switchMap(function (url) {
            return _this.http.get(url + 'joueur/' + id).map(function (res) { return res = res.json(); });
        });
    };
    /*
     Methodes Arbitres
     */
    // Liste tous les arbitres
    RequestService.prototype.listArbitre = function () {
        var _this = this;
        return this.getBaseUrl().switchMap(function (url) {
            return _this.http.get(url + 'arbitre').map(function (res) { return res = res.json(); });
        });
    };
    // Récupére un arbitre par ID
    RequestService.prototype.showArbitre = function (id) {
        var _this = this;
        return this.getBaseUrl().switchMap(function (url) {
            return _this.http.get(url + 'arbitre/' + id).map(function (res) { return res = res.json(); });
        });
    };
    /*
     Methodes Rencontres
     */
    // Liste tous les arbitres
    RequestService.prototype.listRencontres = function () {
        var _this = this;
        return this.getBaseUrl().switchMap(function (url) {
            return _this.http.get(url + 'rencontre').map(function (res) { return res = res.json(); });
        });
    };
    // Récupére un arbitre par ID
    RequestService.prototype.showRencontre = function (id) {
        var _this = this;
        return this.getBaseUrl().switchMap(function (url) {
            return _this.http.get(url + 'rencontres/' + id).map(function (res) { return res = res.json(); });
        });
    };
    /*
     Methodes Tournois
     */
    // Liste tous les tournois
    RequestService.prototype.listTournois = function () {
        var _this = this;
        return this.getBaseUrl().switchMap(function (url) {
            return _this.http.get(url + 'tournoi').map(function (res) { return res = res.json(); });
        });
    };
    // Récupére un tournoi par ID
    RequestService.prototype.showTournoi = function (id) {
        var _this = this;
        return this.getBaseUrl().switchMap(function (url) {
            return _this.http.get(url + 'tournoi/' + id).map(function (res) { return res = res.json(); });
        });
    };
    return RequestService;
}());
RequestService = __decorate([
    core_1.Injectable(),
    __metadata("design:paramtypes", [http_1.Http, config_1.Config])
], RequestService);
exports.RequestService = RequestService;
//# sourceMappingURL=request.service.js.map<commit_msg>Delete request.service.js<commit_after><|endoftext|>


<commit_before>/*jshint node:true*/
module.exports = {
  scenarios: [
    {
      name: 'default',
      bower: {
        devDependencies: {}
      }
    },
    {
      name: 'ember-ember-data-2.6',
      bower: {
        devDependencies: {
          'ember': 'components/ember#2.6'
        },
        resolutions: {
          'ember': '2.6'
        }
      },
      npm: {
        devDependencies: {
          'ember-data': 'emberjs/data#2.6',
          'ember-data-model-fragments': '2.3.3'
        },
        resolutions: {
          'ember-data': '2.6',
          'ember-data-model-fragments': '2.3.3'
        }
      }
    },
    {
      name: 'ember-ember-data-release',
      bower: {
        devDependencies: {
          'ember': 'components/ember#release'
        },
        resolutions: {
          'ember': 'release'
        }
      },
      npm: {
        devDependencies: {
          'ember-data': 'emberjs/data#release'
        },
        resolutions: {
          'ember-data': 'release'
        }
      }
    },
    {
      name: 'ember-ember-data-beta',
      bower: {
        devDependencies: {
          'ember': 'components/ember#beta'
        },
        resolutions: {
          'ember': 'beta'
        }
      },
      npm: {
        devDependencies: {
          'ember-data': 'emberjs/data#beta'
        },
        resolutions: {
          'ember-data': 'beta'
        }
      }
    },
    {
      name: 'ember-ember-data-canary',
      bower: {
        devDependencies: {
          'ember': 'components/ember#canary'
        },
        resolutions: {
          'ember': 'canary'
        }
      },
      npm: {
        devDependencies: {
          'ember-data': 'emberjs/data#canary'
        },
        resolutions: {
          'ember-data': 'canary'
        }
      }
    }
  ]
};
<commit_msg>ember try updates<commit_after>/*jshint node:true*/
module.exports = {
  scenarios: [
    {
      name: 'default',
      bower: {
        devDependencies: {}
      }
    },
    {
      name: 'ember-ember-data-2.6',
      npm: {
        devDependencies: {
          'ember-data': 'emberjs/data#2.6.2',
          'ember-data-model-fragments': '2.3.3'
        },
        resolutions: {
          'ember-data': '2.6.2',
          'ember-data-model-fragments': '2.3.3'
        }
      }
    },
    {
      name: 'ember-ember-data-release',
      bower: {
        devDependencies: {
          'ember': 'components/ember#release'
        },
        resolutions: {
          'ember': 'release'
        }
      },
      npm: {
        devDependencies: {
          'ember-data': 'emberjs/data#release'
        },
        resolutions: {
          'ember-data': 'release'
        }
      }
    },
    {
      name: 'ember-ember-data-beta',
      bower: {
        devDependencies: {
          'ember': 'components/ember#beta'
        },
        resolutions: {
          'ember': 'beta'
        }
      },
      npm: {
        devDependencies: {
          'ember-data': 'emberjs/data#beta'
        },
        resolutions: {
          'ember-data': 'beta'
        }
      }
    },
    {
      name: 'ember-ember-data-canary',
      bower: {
        devDependencies: {
          'ember': 'components/ember#canary'
        },
        resolutions: {
          'ember': 'canary'
        }
      },
      npm: {
        devDependencies: {
          'ember-data': 'emberjs/data#canary'
        },
        resolutions: {
          'ember-data': 'canary'
        }
      }
    }
  ]
};
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import Helmet from 'react-helmet'

import logoEue from '../../components/img/logo_eue.png'
import logoQx from '../../components/img/logo_qooxdoo.png'

import { m } from '../../utils/breakpoints'

const Projekte = ({
  data: { site: { siteMetadata: { title: siteTitle } } }
}) => (

    <Helmet>
      <title>{`Projekte | ${siteTitle}`}</title>
    </Helmet>

Projekte

Hier sehen Sie meine Projektreferenzen:

Projekt

              1&1 Control Center Access

 (Bestandskundenportal)

Firma

1&1 Internet SE

Aufgabengebiete

Frontend & Tooling (JS)

Technologien im Frontend

qooxdoo, React, Angular 1&2

Zeitraum

            <time>2015 - 2016</time>

          Über 8,7 Millionen 1&1-Bestandskunden von DSL und Mobile-Produkten
          können Ihre Vertrags- und Leistungsdetails über das{' '}
          1&1 Control Center (Access) einsehen sowie diverse
          Einstellungen vornehmen (z.B. SIM-Karte bestellen).

          Als Teil des Frontend-Teams, welches diese Web-Applikation entwickelt
          hat, waren meine Aufgaben:

            Fachliche Umsetzung von User-Stories, u.a.:

                1&1 Multi-Card (Anfordern, Aktivieren, Sperren, Entsperren)

DSL-Störungsformular

                Einbindung der Kontaktstrategie in Hilfe-Lasche mit
                Kontaktkanal- und Anliegenauswahl

Technikerterminvereinbarung mit Calendar-Widget

                Verschiedene DSL und Mobile Accordions, um „Einstellungen &
                Services“ zu konfigurieren

            Wartung/Modernisierung der Frontend-Toolchain:

Einbindung von Browserify und Babel

Umstellung des Buildprozesses auf Node.js/npm-packages

Einbau von ESLint als Ersatz für JSCS und JSHint

            Verantwortung der Jobs auf dem CI-Server (Jenkins):

Job, der die App baut und Releases mittels Maven erstellt

                Frontend Code Quality-Job, der Checks durchführt (Linting von JS
                und SCSS-Code sowie JS Unit- und Integrationstests)

                Frontend Security Job, der auf bekannte Schwachstellen in
                npm-Packages, die verwendet werden, hinweist

            Verbesserung des Entwicklerflows:

Migration von SVN zu Git

Einsatz von selbstgeschriebenen Git Hooks

            Sonstiges:

Überarbeitung der REST-Ressourcen mit dem Backend-Team

Erweiterung des Frontend-Review/Release-Prozesses

Dokumentation des CD-Build/Relase/Deployment-Prozesses

Analyse/Konzeption FE- Zielarchitektur mit Migrationsplan

Schreiben von Unit/E2E-Tests (Karma, Jasmine, Protractor)

Leiten von Reviews und Retrospektiven

Releasemanager und Bugmanager in diversen Scrum-Sprints

                Team-Einführung in Kanban (als Alt. für Scrum) sowie Kibana

Projekt

            qooxdoo

            Open-Source Web-Framework

Firma

1&1 Internet AG

Aufgabengebiete

            Core-Entwickler,

            Toolchainverantwortlicher (JS/Python)

Zeitraum

            <time>2012 - 2015</time>

          qooxdoo ist ein Open Source Web Framework zur
          Erstellung von RIAs, nativ-ähnlicher Mobile Apps und leichtgewichtiger
          Web-Anwendungen. Die komplette Entwicklung passiert auf{' '}
          GitHub.

          Als Teil des Core-Teams, welches qooxdoo weiterentwickelt, waren meine
          Aufgaben:

            Core-Entwickler und verantwortlich für die (Python-)Toolchain:

                Weiterentwicklung der Buildprozesse (Parsing, Preprocessing,
                Minifying etc)

                Weiterentwicklung des JS-Framework-Codes auf GitHub, v.a. des
                IO-Bereiches (z.B. XHR/JSONP und REST)

            Konzeption und Entwicklung einer neuen JS-Toolchain (via
            Grunt-Plugins):

Parsing von JS-Code inkl. Scope- und Dependency-Analyse

Minifizierung und Optimierung von JS Code

Preprocessing von Ressourcen (z.B. Bildern)

Verarbeitung qooxdoo-spezifischer Compiler-Hints

Pflege/Wartung der bestehenden Python Toolchain

Einführung von ESLint und custom-Lint-Regeln

Erstellung des Contribution-Catalog (für 3rd Party Libs)

UnitTests mittels nodeunit/Mocha/Chai

Dokumentation mittels Sphinx/JSDoc

                Interaktion mit Open-Source Community (Pull Requests / Support
                via Stack Overflow und Mailingliste)

)

Projekte.propTypes = {
  data: PropTypes.shape({
    site: PropTypes.shape({
      siteMetadata: PropTypes.shape({
        title: PropTypes.string.isRequired
      }).isRequired
    }).isRequired
  }).isRequired
}

export default Projekte

export const pageQuery = graphql`
  query ProjectsQuery {
    site {
      siteMetadata {
        title
      }
    }
  }
`
<commit_msg>add project reference for Audi<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import Helmet from 'react-helmet'

import logoEue from '../../components/img/logo_eue.png'
import logoQx from '../../components/img/logo_qooxdoo.png'

import { m } from '../../utils/breakpoints'

const Projekte = ({
  data: { site: { siteMetadata: { title: siteTitle } } }
}) => (

    <Helmet>
      <title>{`Projekte | ${siteTitle}`}</title>
    </Helmet>

Projekte

Hier sehen Sie meine Projektreferenzen:

        <img
          src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxOC4xLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iRWJlbmVfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCAxNzUuNiA2MiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMTc1LjYgNjIiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPHBhdGggZD0iTTE0NC4yLDBjLTcsMC0xMy41LDIuMy0xOC44LDYuMkMxMjAuMSwyLjMsMTEzLjYsMCwxMDYuNiwwYy03LDAtMTMuNSwyLjMtMTguOCw2LjJDODIuNSwyLjMsNzYuMSwwLDY5LDANCgljLTcsMC0xMy41LDIuMy0xOC44LDYuMkM0NSwyLjMsMzguNSwwLDMxLjQsMEMxNC4xLDAsMCwxMy45LDAsMzFzMTQuMSwzMSwzMS40LDMxYzcsMCwxMy41LTIuMywxOC44LTYuMkM1NS41LDU5LjcsNjIsNjIsNjksNjINCgljNywwLDEzLjUtMi4zLDE4LjgtNi4yQzkzLDU5LjcsOTkuNSw2MiwxMDYuNiw2MmM3LDAsMTMuNS0yLjMsMTguOC02LjJjNS4yLDMuOSwxMS43LDYuMiwxOC44LDYuMmMxNy4zLDAsMzEuNC0xMy45LDMxLjQtMzENCglTMTYxLjUsMCwxNDQuMiwweiBNMTI1LjQsNDdjLTMuOC00LjMtNi4xLTkuOS02LjEtMTZjMC02LjEsMi4zLTExLjcsNi4xLTE2YzMuOCw0LjMsNi4xLDkuOSw2LjEsMTZDMTMxLjUsMzcuMSwxMjkuMiw0Mi43LDEyNS40LDQ3DQoJeiBNODcuOCw0N2MtMy44LTQuMy02LjEtOS45LTYuMS0xNmMwLTYuMSwyLjMtMTEuNyw2LjEtMTZjMy44LDQuMyw2LjEsOS45LDYuMSwxNkM5My45LDM3LjEsOTEuNiw0Mi43LDg3LjgsNDd6IE01MC4yLDQ3DQoJYy0zLjgtNC4zLTYuMS05LjktNi4xLTE2YzAtNi4xLDIuMy0xMS43LDYuMS0xNmMzLjgsNC4zLDYuMSw5LjksNi4xLDE2QzU2LjQsMzcuMSw1NCw0Mi43LDUwLjIsNDd6IE02LjUsMzENCgljMC0xMy41LDExLjItMjQuNSwyNC45LTI0LjVjNS4xLDAsOS45LDEuNSwxMy45LDQuMmMtNC44LDUuNC03LjcsMTIuNS03LjcsMjAuM2MwLDcuOCwyLjksMTQuOSw3LjcsMjAuM2MtNCwyLjYtOC43LDQuMi0xMy45LDQuMg0KCUMxNy43LDU1LjUsNi41LDQ0LjUsNi41LDMxeiBNNTUuMSw1MS4zYzQuOC01LjQsNy43LTEyLjUsNy43LTIwLjNjMC03LjgtMi45LTE0LjktNy43LTIwLjNjNC0yLjYsOC43LTQuMiwxMy45LTQuMg0KCXM5LjksMS41LDEzLjksNC4yYy00LjgsNS40LTcuOCwxMi41LTcuOCwyMC4zYzAsNy44LDIuOSwxNC45LDcuOCwyMC4zYy00LDIuNi04LjcsNC4yLTEzLjksNC4yQzYzLjksNTUuNSw1OS4xLDUzLjksNTUuMSw1MS4zeg0KCSBNOTIuNyw1MS4zYzQuOC01LjQsNy44LTEyLjUsNy44LTIwLjNjMC03LjgtMi45LTE0LjktNy44LTIwLjNjNC0yLjYsOC43LTQuMiwxMy45LTQuMnM5LjksMS41LDEzLjksNC4yDQoJYy00LjgsNS40LTcuOCwxMi41LTcuOCwyMC4zYzAsNy44LDIuOSwxNC45LDcuOCwyMC4zYy00LDIuNi04LjcsNC4yLTEzLjksNC4yUzk2LjcsNTMuOSw5Mi43LDUxLjN6IE0xNDQuMiw1NS41DQoJYy01LjEsMC05LjktMS41LTEzLjktNC4yYzQuOC01LjQsNy44LTEyLjUsNy44LTIwLjNjMC03LjgtMi45LTE0LjktNy44LTIwLjNjNC0yLjYsOC43LTQuMiwxMy45LTQuMmMxMy43LDAsMjQuOSwxMSwyNC45LDI0LjUNCglDMTY5LjEsNDQuNSwxNTcuOSw1NS41LDE0NC4yLDU1LjV6Ii8+DQo8L3N2Zz4NCg=="
          width="100"
          height="55"
          alt="Audi AG"
        />

Projekt

              Audi Gebrauchtwagenbörse - Onlinekauf (Checkout)

Firma

Audi AG

Aufgabengebiete

Frontend (JS)

Technologien im Frontend

            React, React Router, Redux, Redux Form, Redux-Saga, styled
            components, webpack

Zeitraum

            <time>2017 (4 Monate / remote)</time>

          Autorisierte Audi-Händler können über die offizielle
          Gebrauchtwagenbörse von Audi ihre Fahrzeuge einstellen und zum
          Onlinekauf freigeben. Potentielle Interessenten können den kompletten
          Autokauf (Entscheidung für Abholung oder Lieferung, Zulassung und
          Bezahlung/Finanzierung) in einem mehrstufigen Prozess online
          abwickeln.

          Als Teil des Frontend-Teams, welches den Checkout-Prozess als
          Web-Applikation entwickelt hat, waren meine Aufgaben:

            Fachliche Umsetzung von User-Stories, u.a.:

                Implementierung diverser Views gemäß{' '}
                Atomic Design{' '}
                und Responsive web design

Implementierung diverser Atoms, Molecules und Organisms

Implementierung des kompletten Fragecenters

Erstellung von Formularen mit Redux Form

Erstellung von Actions/Reducern mit Redux

Validierung sämtlicher Formulare

            Verbesserung des Entwicklerflows:

Einsatz von ESLint (integriert als Git Hook)

Einsatz von Prettier (integriert als Git Hook)

Dokumentation diverser Atoms, Molecules und Organisms

Projekt

              1&1 Control Center Access

 (Bestandskundenportal)

Firma

1&1 Internet SE

Aufgabengebiete

Frontend & Tooling (JS)

Technologien im Frontend

qooxdoo, React, Angular 1&2

Zeitraum

            <time>2015 - 2016</time>

          Über 8,7 Millionen 1&1-Bestandskunden von DSL und Mobile-Produkten
          können Ihre Vertrags- und Leistungsdetails über das{' '}
          1&1 Control Center (Access) einsehen sowie diverse
          Einstellungen vornehmen (z.B. SIM-Karte bestellen).

          Als Teil des Frontend-Teams, welches diese Web-Applikation entwickelt
          hat, waren meine Aufgaben:

            Fachliche Umsetzung von User-Stories, u.a.:

                1&1 Multi-Card (Anfordern, Aktivieren, Sperren, Entsperren)

DSL-Störungsformular

                Einbindung der Kontaktstrategie in Hilfe-Lasche mit
                Kontaktkanal- und Anliegenauswahl

Technikerterminvereinbarung mit Calendar-Widget

                Verschiedene DSL und Mobile Accordions, um „Einstellungen &
                Services“ zu konfigurieren

            Wartung/Modernisierung der Frontend-Toolchain:

Einbindung von Browserify und Babel

Umstellung des Buildprozesses auf Node.js/npm-packages

Einbau von ESLint als Ersatz für JSCS und JSHint

            Verantwortung der Jobs auf dem CI-Server (Jenkins):

Job, der die App baut und Releases mittels Maven erstellt

                Frontend Code Quality-Job, der Checks durchführt (Linting von JS
                und SCSS-Code sowie JS Unit- und Integrationstests)

                Frontend Security Job, der auf bekannte Schwachstellen in
                npm-Packages, die verwendet werden, hinweist

            Verbesserung des Entwicklerflows:

Migration von SVN zu Git

Einsatz von selbstgeschriebenen Git Hooks

            Sonstiges:

Überarbeitung der REST-Ressourcen mit dem Backend-Team

Erweiterung des Frontend-Review/Release-Prozesses

Dokumentation des CD-Build/Relase/Deployment-Prozesses

Analyse/Konzeption FE- Zielarchitektur mit Migrationsplan

Schreiben von Unit/E2E-Tests (Karma, Jasmine, Protractor)

Leiten von Reviews und Retrospektiven

Releasemanager und Bugmanager in diversen Scrum-Sprints

                Team-Einführung in Kanban (als Alt. für Scrum) sowie Kibana

Projekt

            qooxdoo

            Open-Source Web-Framework

Firma

1&1 Internet AG

Aufgabengebiete

            Core-Entwickler,

            Toolchainverantwortlicher (JS/Python)

Zeitraum

            <time>2012 - 2015</time>

          qooxdoo ist ein Open Source Web Framework zur
          Erstellung von RIAs, nativ-ähnlicher Mobile Apps und leichtgewichtiger
          Web-Anwendungen. Die komplette Entwicklung passiert auf{' '}
          GitHub.

          Als Teil des Core-Teams, welches qooxdoo weiterentwickelt, waren meine
          Aufgaben:

            Core-Entwickler und verantwortlich für die (Python-)Toolchain:

                Weiterentwicklung der Buildprozesse (Parsing, Preprocessing,
                Minifying etc)

                Weiterentwicklung des JS-Framework-Codes auf GitHub, v.a. des
                IO-Bereiches (z.B. XHR/JSONP und REST)

            Konzeption und Entwicklung einer neuen JS-Toolchain (via
            Grunt-Plugins):

Parsing von JS-Code inkl. Scope- und Dependency-Analyse

Minifizierung und Optimierung von JS Code

Preprocessing von Ressourcen (z.B. Bildern)

Verarbeitung qooxdoo-spezifischer Compiler-Hints

Pflege/Wartung der bestehenden Python Toolchain

Einführung von ESLint und custom-Lint-Regeln

Erstellung des Contribution-Catalog (für 3rd Party Libs)

UnitTests mittels nodeunit/Mocha/Chai

Dokumentation mittels Sphinx/JSDoc

                Interaktion mit Open-Source Community (Pull Requests / Support
                via Stack Overflow und Mailingliste)

)

Projekte.propTypes = {
  data: PropTypes.shape({
    site: PropTypes.shape({
      siteMetadata: PropTypes.shape({
        title: PropTypes.string.isRequired
      }).isRequired
    }).isRequired
  }).isRequired
}

export default Projekte

export const pageQuery = graphql`
  query ProjectsQuery {
    site {
      siteMetadata {
        title
      }
    }
  }
`
<|endoftext|>


<commit_before>var $source;
var canvas;
var imageLoader;
var ctx;
var imageFilename;
var img;
var $save;
var $textColor;
var imageHeight;

var handleImage = function(e) {
    var reader = new FileReader();
    reader.onload = function(event){
        imageFilename = event.target.result
        renderCanvas();
    }
    reader.readAsDataURL(e.target.files[0]);
}

var renderCanvas = function() {
    ctx.clearRect(0,0,canvas.width,canvas.height);

    for (var i = 0; i < $textColor.length; i++) {
        if ($textColor.eq(i).is(':checked')) {
            var textColor = $textColor.eq(i).val();
        }
    }

    img = new Image();

    img.onload = function(){
        var imageAspect = img.width / img.height;
        canvas.width = 600;
        canvas.height = 600 / imageAspect;
        ctx.drawImage(img, 0, 0, 600, canvas.height);

        var logo = new Image();

        logo.onload = function(){
            ctx.drawImage(logo, canvas.width - (logo.width + 20), canvas.height - (logo.height + 20));
        }

        logo.src = 'assets/npr-' + textColor + '.png';

        ctx.textBaseline = 'bottom';
        ctx.textAlign = 'left';
        ctx.fillStyle = textColor;
        ctx.font = 'normal 12pt Gotham';

        if (textColor === 'white') {
            ctx.shadowColor = 'black';
            ctx.shadowOffsetX = 5;
            ctx.shadowOffsetY = 5;
            ctx.shadowBlur = 10;
        }

        ctx.fillText($source.val(), 20, canvas.height - 20);
    }

    img.src = imageFilename || 'assets/test.png';
}

var onSaveClick = function() {
    /// create an "off-screen" anchor tag
    var link = document.createElement('a'),
        e;

    /// the key here is to set the download attribute of the a tag
    link.download = 'download.png';

    /// convert canvas content to data-uri for link. When download
    /// attribute is set the content pointed to by link will be
    /// pushed as "download" in HTML5 capable browsers
    link.href = canvas.toDataURL();

    /// create a "fake" click-event to trigger the download
    if (document.createEvent) {

        e = document.createEvent("MouseEvents");
        e.initMouseEvent("click", true, true, window,
                         0, 0, 0, 0, 0, false, false, false,
                         false, 0, null);

        link.dispatchEvent(e);

    } else if (link.fireEvent) {
        link.fireEvent("onclick");
    }
}

$(document).ready(function() {
    $source = $('#source');
    canvas = $('#imageCanvas')[0];
    imageLoader = $('#imageLoader');
    ctx = canvas.getContext('2d');
    $save = $('.save-btn');
    $textColor = $('input[name="textColor"]');

    $source.on('keyup', renderCanvas);
    imageLoader.on('change', handleImage);
    $save.on('click', onSaveClick);
    $textColor.on('change', renderCanvas);

    renderCanvas();
});<commit_msg>handle safari's lack of support for download<commit_after>var $source;
var canvas;
var imageLoader;
var ctx;
var imageFilename;
var img;
var $save;
var $textColor;
var imageHeight;
var fixedWidth = 1000;

var handleImage = function(e) {
    var reader = new FileReader();
    reader.onload = function(event){
        imageFilename = event.target.result
        renderCanvas();
    }
    reader.readAsDataURL(e.target.files[0]);
}

var renderCanvas = function() {
    ctx.clearRect(0,0,canvas.width,canvas.height);

    for (var i = 0; i < $textColor.length; i++) {
        if ($textColor.eq(i).is(':checked')) {
            var textColor = $textColor.eq(i).val();
        }
    }

    img = new Image();

    img.onload = function(){
        var imageAspect = img.width / img.height;
        canvas.width = fixedWidth;
        canvas.height = fixedWidth / imageAspect;
        ctx.drawImage(img, 0, 0, fixedWidth, canvas.height);

        var logo = new Image();

        logo.onload = function(){
            if (textColor === 'white') {
                ctx.globalAlpha = "0.7";
            }
            ctx.drawImage(logo, canvas.width - (logo.width + 20), canvas.height - (logo.height + 20));
        }

        logo.src = 'assets/npr-' + textColor + '.png';

        ctx.textBaseline = 'bottom';
        ctx.textAlign = 'left';
        ctx.fillStyle = textColor;
        ctx.font = 'normal 12pt Gotham';

        if (textColor === 'white') {
            ctx.shadowColor = 'black';
            ctx.shadowOffsetX = 5;
            ctx.shadowOffsetY = 5;
            ctx.shadowBlur = 10;
        }

        ctx.fillText($source.val(), 20, canvas.height - 20);
    }

    img.src = imageFilename || 'assets/test.png';
}

var onSaveClick = function() {
    /// create an "off-screen" anchor tag
    var link = document.createElement('a'),
        e;

    /// the key here is to set the download attribute of the a tag
    link.download = 'download.png';

    /// convert canvas content to data-uri for link. When download
    /// attribute is set the content pointed to by link will be
    /// pushed as "download" in HTML5 capable browsers
    link.href = canvas.toDataURL();
    link.target = "_blank";

    /// create a "fake" click-event to trigger the download
    if (document.createEvent) {

        e = document.createEvent("MouseEvents");
        e.initMouseEvent("click", true, true, window,
                         0, 0, 0, 0, 0, false, false, false,
                         false, 0, null);

        link.dispatchEvent(e);

    } else if (link.fireEvent) {
        link.fireEvent("onclick");
    }
}

$(document).ready(function() {
    $source = $('#source');
    canvas = $('#imageCanvas')[0];
    imageLoader = $('#imageLoader');
    ctx = canvas.getContext('2d');
    $save = $('.save-btn');
    $textColor = $('input[name="textColor"]');

    $source.on('keyup', renderCanvas);
    imageLoader.on('change', handleImage);
    $save.on('click', onSaveClick);
    $textColor.on('change', renderCanvas);

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