├── www └── .gitkeep ├── app ├── config.js ├── components │ ├── index.js │ ├── ui │ │ ├── loading │ │ │ ├── spinner.scss │ │ │ ├── loading.html │ │ │ └── index.js │ │ ├── subheader_pane │ │ │ ├── subheader_pane.html │ │ │ ├── subheader_pane_content.html │ │ │ ├── index.js │ │ │ ├── show_subheader_pane_directive.js │ │ │ ├── subheader_pane.scss │ │ │ ├── subheader_pane_content_directive.js │ │ │ └── subheader_pane_directive.js │ │ ├── fake_tabs │ │ │ ├── fake_tabs.html │ │ │ ├── fake_tab.html │ │ │ └── index.js │ │ ├── shrinkable_image_header │ │ │ ├── shrinkable_image_header.html │ │ │ ├── index.js │ │ │ └── directive.js │ │ ├── static_map │ │ │ ├── static_map.html │ │ │ ├── static_map.scss │ │ │ └── index.js │ │ └── view_switching │ │ │ └── index.js │ ├── views │ │ ├── google_images │ │ │ ├── google_images.jade │ │ │ ├── controllers │ │ │ │ └── google_images_controller.js │ │ │ └── index.js │ │ ├── index.js │ │ └── tabs.jade │ ├── utils │ │ ├── default_route │ │ │ └── index.js │ │ └── cached_image_loader │ │ │ └── index.js │ ├── data │ │ ├── google_images │ │ │ └── index.js │ │ └── store │ │ │ └── index.js │ └── bootstrap.js ├── templates.js ├── env │ ├── development.js │ └── production.js ├── fonts │ ├── -DackuIFgo7Hfy3rR14C34X0hVgzZQUfRDuZrPvH3D8.woff2 │ ├── Be0CkOtwwI2n86HMhtablYX0hVgzZQUfRDuZrPvH3D8.woff2 │ ├── UgYUhLCkRUocJ8OZnvelZ4X0hVgzZQUfRDuZrPvH3D8.woff2 │ ├── fND5XPYKrF2tQDwwfWZJI4joYw3YTyktCCer_ilOlhE.woff2 │ ├── fND5XPYKrF2tQDwwfWZJI_y1_HTwRwgtl1cPga3Fy3Y.woff2 │ ├── fND5XPYKrF2tQDwwfWZJIw7aC6SjiAOpAWOKfJDfVRY.woff2 │ ├── fND5XPYKrF2tQDwwfWZJIxampu5_7CjHW5spxoeN3Vs.woff2 │ ├── fND5XPYKrF2tQDwwfWZJIxdwxCXfZpKo5kWAx_74bHs.woff2 │ ├── qLBu5CQmSMt1H43OiWJ77ZBw1xU1rKptJj_0jans920.woff2 │ ├── r_tUZNl0G8xCoOmp_JkSCg7aC6SjiAOpAWOKfJDfVRY.woff2 │ ├── r_tUZNl0G8xCoOmp_JkSChampu5_7CjHW5spxoeN3Vs.woff2 │ ├── r_tUZNl0G8xCoOmp_JkSChdwxCXfZpKo5kWAx_74bHs.woff2 │ ├── r_tUZNl0G8xCoOmp_JkSCojoYw3YTyktCCer_ilOlhE.woff2 │ ├── r_tUZNl0G8xCoOmp_JkSCvy1_HTwRwgtl1cPga3Fy3Y.woff2 │ ├── wz1gqe57Mbg11v-OrLlVjoX0hVgzZQUfRDuZrPvH3D8.woff2 │ └── qLBu5CQmSMt1H43OiWJ77ZBw1xU1rKptJj_0jans920 (1).woff2 ├── _variables.scss ├── index.scss ├── index.html ├── index.js ├── jqlite_animate_comment_patch.js └── fonts.scss ├── ionic.project ├── dev-server ├── .gitignore ├── .editorconfig ├── bower.json ├── config.xml ├── README.md ├── package.json ├── hooks ├── after_prepare │ └── 010_add_platform_class.js └── README.md ├── gulpfile.js └── webpack.config.js /www/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | }; 5 | -------------------------------------------------------------------------------- /app/components/index.js: -------------------------------------------------------------------------------- 1 | var App = angular.module('app', [ 2 | require('./utils/default_route') 3 | ]); -------------------------------------------------------------------------------- /app/templates.js: -------------------------------------------------------------------------------- 1 | var TemplatesModule = angular.module('templates', []); 2 | module.exports = TemplatesModule.name; 3 | -------------------------------------------------------------------------------- /ionic.project: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ionic-webpack", 3 | "app_id": "", 4 | "gulpStartupTasks": [ 5 | "watch" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /app/components/ui/loading/spinner.scss: -------------------------------------------------------------------------------- 1 | .spinner.spinner-large { 2 | svg { 3 | width: 48px; 4 | height: 48px; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /app/components/ui/subheader_pane/subheader_pane.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/components/ui/subheader_pane/subheader_pane_content.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | -------------------------------------------------------------------------------- /dev-server: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | webpack-dev-server --content-base www/ -devtool eval-source-map --port 5000 --hot --inline --colors --history-api-fallback 3 | -------------------------------------------------------------------------------- /app/components/ui/fake_tabs/fake_tabs.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | 7 | -------------------------------------------------------------------------------- /app/env/development.js: -------------------------------------------------------------------------------- 1 | var EnvironmentModule = angular.module('environment', []) 2 | .config(function ($logProvider){ 3 | }); 4 | 5 | module.exports = EnvironmentModule.name; 6 | -------------------------------------------------------------------------------- /app/env/production.js: -------------------------------------------------------------------------------- 1 | var EnvironmentModule = angular.module('environment', []) 2 | .config(function ($logProvider){ 3 | }); 4 | 5 | module.exports = EnvironmentModule.name; 6 | -------------------------------------------------------------------------------- /app/fonts/-DackuIFgo7Hfy3rR14C34X0hVgzZQUfRDuZrPvH3D8.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMar4enko/ionic-webpack-starter/HEAD/app/fonts/-DackuIFgo7Hfy3rR14C34X0hVgzZQUfRDuZrPvH3D8.woff2 -------------------------------------------------------------------------------- /app/fonts/Be0CkOtwwI2n86HMhtablYX0hVgzZQUfRDuZrPvH3D8.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMar4enko/ionic-webpack-starter/HEAD/app/fonts/Be0CkOtwwI2n86HMhtablYX0hVgzZQUfRDuZrPvH3D8.woff2 -------------------------------------------------------------------------------- /app/fonts/UgYUhLCkRUocJ8OZnvelZ4X0hVgzZQUfRDuZrPvH3D8.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMar4enko/ionic-webpack-starter/HEAD/app/fonts/UgYUhLCkRUocJ8OZnvelZ4X0hVgzZQUfRDuZrPvH3D8.woff2 -------------------------------------------------------------------------------- /app/fonts/fND5XPYKrF2tQDwwfWZJI4joYw3YTyktCCer_ilOlhE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMar4enko/ionic-webpack-starter/HEAD/app/fonts/fND5XPYKrF2tQDwwfWZJI4joYw3YTyktCCer_ilOlhE.woff2 -------------------------------------------------------------------------------- /app/fonts/fND5XPYKrF2tQDwwfWZJI_y1_HTwRwgtl1cPga3Fy3Y.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMar4enko/ionic-webpack-starter/HEAD/app/fonts/fND5XPYKrF2tQDwwfWZJI_y1_HTwRwgtl1cPga3Fy3Y.woff2 -------------------------------------------------------------------------------- /app/fonts/fND5XPYKrF2tQDwwfWZJIw7aC6SjiAOpAWOKfJDfVRY.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMar4enko/ionic-webpack-starter/HEAD/app/fonts/fND5XPYKrF2tQDwwfWZJIw7aC6SjiAOpAWOKfJDfVRY.woff2 -------------------------------------------------------------------------------- /app/fonts/fND5XPYKrF2tQDwwfWZJIxampu5_7CjHW5spxoeN3Vs.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMar4enko/ionic-webpack-starter/HEAD/app/fonts/fND5XPYKrF2tQDwwfWZJIxampu5_7CjHW5spxoeN3Vs.woff2 -------------------------------------------------------------------------------- /app/fonts/fND5XPYKrF2tQDwwfWZJIxdwxCXfZpKo5kWAx_74bHs.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMar4enko/ionic-webpack-starter/HEAD/app/fonts/fND5XPYKrF2tQDwwfWZJIxdwxCXfZpKo5kWAx_74bHs.woff2 -------------------------------------------------------------------------------- /app/fonts/qLBu5CQmSMt1H43OiWJ77ZBw1xU1rKptJj_0jans920.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMar4enko/ionic-webpack-starter/HEAD/app/fonts/qLBu5CQmSMt1H43OiWJ77ZBw1xU1rKptJj_0jans920.woff2 -------------------------------------------------------------------------------- /app/fonts/r_tUZNl0G8xCoOmp_JkSCg7aC6SjiAOpAWOKfJDfVRY.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMar4enko/ionic-webpack-starter/HEAD/app/fonts/r_tUZNl0G8xCoOmp_JkSCg7aC6SjiAOpAWOKfJDfVRY.woff2 -------------------------------------------------------------------------------- /app/fonts/r_tUZNl0G8xCoOmp_JkSChampu5_7CjHW5spxoeN3Vs.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMar4enko/ionic-webpack-starter/HEAD/app/fonts/r_tUZNl0G8xCoOmp_JkSChampu5_7CjHW5spxoeN3Vs.woff2 -------------------------------------------------------------------------------- /app/fonts/r_tUZNl0G8xCoOmp_JkSChdwxCXfZpKo5kWAx_74bHs.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMar4enko/ionic-webpack-starter/HEAD/app/fonts/r_tUZNl0G8xCoOmp_JkSChdwxCXfZpKo5kWAx_74bHs.woff2 -------------------------------------------------------------------------------- /app/fonts/r_tUZNl0G8xCoOmp_JkSCojoYw3YTyktCCer_ilOlhE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMar4enko/ionic-webpack-starter/HEAD/app/fonts/r_tUZNl0G8xCoOmp_JkSCojoYw3YTyktCCer_ilOlhE.woff2 -------------------------------------------------------------------------------- /app/fonts/r_tUZNl0G8xCoOmp_JkSCvy1_HTwRwgtl1cPga3Fy3Y.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMar4enko/ionic-webpack-starter/HEAD/app/fonts/r_tUZNl0G8xCoOmp_JkSCvy1_HTwRwgtl1cPga3Fy3Y.woff2 -------------------------------------------------------------------------------- /app/fonts/wz1gqe57Mbg11v-OrLlVjoX0hVgzZQUfRDuZrPvH3D8.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMar4enko/ionic-webpack-starter/HEAD/app/fonts/wz1gqe57Mbg11v-OrLlVjoX0hVgzZQUfRDuZrPvH3D8.woff2 -------------------------------------------------------------------------------- /app/components/ui/loading/loading.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | 6 | -------------------------------------------------------------------------------- /app/fonts/qLBu5CQmSMt1H43OiWJ77ZBw1xU1rKptJj_0jans920 (1).woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AMar4enko/ionic-webpack-starter/HEAD/app/fonts/qLBu5CQmSMt1H43OiWJ77ZBw1xU1rKptJj_0jans920 (1).woff2 -------------------------------------------------------------------------------- /app/components/ui/shrinkable_image_header/shrinkable_image_header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/_variables.scss: -------------------------------------------------------------------------------- 1 | $font-family-sans-serif: 'Comfortaa', 'Helvetica Neue', 'Roboto', sans-serif !default; 2 | $font-family-light-sans-serif: 'Comfortaa', 'HelveticaNeue-Light', 'Roboto-Light', sans-serif-light !default; -------------------------------------------------------------------------------- /app/index.scss: -------------------------------------------------------------------------------- 1 | @import "./variables"; 2 | @import "./fonts"; 3 | 4 | // The path for our ionicons font files, relative to the built CSS in www/css 5 | $ionicons-font-path: "../bower_components/ionic/fonts" !default; 6 | @import "ionic/scss/ionic"; 7 | -------------------------------------------------------------------------------- /app/components/ui/static_map/static_map.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 |
7 | -------------------------------------------------------------------------------- /app/components/ui/fake_tabs/fake_tab.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Specifies intentionally untracked files to ignore when using Git 2 | # http://git-scm.com/docs/gitignore 3 | 4 | bower_components/ 5 | node_modules/ 6 | platforms/ 7 | plugins/ 8 | .tmp/ 9 | .sass-cache/ 10 | .DS_Store 11 | /.idea 12 | /engine 13 | *.log 14 | -------------------------------------------------------------------------------- /app/components/ui/shrinkable_image_header/index.js: -------------------------------------------------------------------------------- 1 | var ShrinkableImageHeader = angular.module('app.ui.shrinkableImageHeader', []); 2 | 3 | module.exports = ShrinkableImageHeader.name; 4 | 5 | ShrinkableImageHeader.directive('shrinkableImageHeader', require('./directive')); 6 | 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /app/components/views/google_images/google_images.jade: -------------------------------------------------------------------------------- 1 | ion-view(title="Google Images test") 2 | ion-content 3 | ion-list 4 | ion-item(collection-repeat="image in googleImages.items" class="item-avatar") 5 | img(ng-src="{{image.tbUrl}}") 6 | h2(ng-bind="image.titleNoFormatting") 7 | p(ng-bind="image.contentNoFormatting") 8 | -------------------------------------------------------------------------------- /app/components/views/google_images/controllers/google_images_controller.js: -------------------------------------------------------------------------------- 1 | module.exports = GoogleImagesController; 2 | 3 | function GoogleImagesController (GoogleImages){ 4 | var vm = this; 5 | GoogleImages.findAll({q: 'habrahabr', start: 0, rsz: 8}).then(function (items){ 6 | vm.items = items; 7 | }); 8 | } 9 | 10 | GoogleImagesController.resolve = { 11 | 12 | }; -------------------------------------------------------------------------------- /app/components/utils/default_route/index.js: -------------------------------------------------------------------------------- 1 | var DefaultRouteModule = angular.module('app.utils.defaultRoute', ['ionic', 'ui.router']); 2 | 3 | module.exports = DefaultRouteModule.name; 4 | 5 | DefaultRouteModule.config(function ($urlRouterProvider){ 6 | $urlRouterProvider.otherwise(defaultUrl); 7 | 8 | function defaultUrl ($inject){ 9 | return $location.replace().url('/signin'); 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /app/components/ui/static_map/static_map.scss: -------------------------------------------------------------------------------- 1 | @import '../../../../bower_components/bourbon/app/assets/stylesheets/bourbon'; 2 | 3 | .static-map { 4 | .img-wrap { 5 | opacity: 0; 6 | } 7 | &.loading { 8 | .img-wrap { 9 | opacity: 0; 10 | @include transition(all 0.3s ease-in-out); 11 | } 12 | } 13 | 14 | &.loaded { 15 | .img-wrap { 16 | opacity: 1; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/components/ui/subheader_pane/index.js: -------------------------------------------------------------------------------- 1 | var SubheaderSlider = angular.module('app.ui.subheaderSlider', []); 2 | 3 | module.exports = SubheaderSlider.name; 4 | 5 | require('./subheader_pane.scss'); 6 | 7 | SubheaderSlider.directive('subheaderPane', require('./subheader_pane_directive')); 8 | SubheaderSlider.directive('showSubheaderPane', require('./show_subheader_pane_directive')); 9 | SubheaderSlider.directive('subheaderPaneContent', require('./subheader_pane_content_directive')); 10 | -------------------------------------------------------------------------------- /app/components/ui/subheader_pane/show_subheader_pane_directive.js: -------------------------------------------------------------------------------- 1 | module.exports = ShowSubheaderPaneDirectiveProvider; 2 | 3 | function ShowSubheaderPaneDirectiveProvider (){ 4 | return { 5 | restrict: 'A', 6 | require: '^subheaderPane', 7 | link: function ($scope, iElem, iAttrs, subheaderPane){ 8 | iElem.bind('click', function (event){ 9 | $scope.$apply(function (){ 10 | subheaderPane.showHide(iAttrs.showSubheaderPane && $scope.$eval(iAttrs.showSubheaderPane)); 11 | }); 12 | }); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/components/data/google_images/index.js: -------------------------------------------------------------------------------- 1 | var GoogleImagesData = angular.module('app.data.googleImages', [ 2 | require('../store') 3 | ]); 4 | 5 | module.exports = GoogleImagesData.name; 6 | 7 | var imageResource; 8 | 9 | GoogleImagesData.run(function (DataStore){ 10 | imageResource = DataStore.defineResource({ 11 | name: 'GoogleImage', 12 | basePath: 'http://ajax.googleapis.com/ajax/services/search', 13 | endpoint: '/images?v=1.0', 14 | idAttribute: 'imageId' 15 | }); 16 | }); 17 | 18 | GoogleImagesData.factory('GoogleImages', function (){ 19 | return imageResource; 20 | }); -------------------------------------------------------------------------------- /app/components/views/index.js: -------------------------------------------------------------------------------- 1 | var Views = angular.module('app.views', [ 2 | require('./google_images'), 3 | require('../ui/fake_tabs') 4 | ]); 5 | var slideMenuTemplate = require('./tabs.jade'); 6 | 7 | module.exports = Views.name; 8 | 9 | Views.config(function ($stateProvider){ 10 | $stateProvider.state( 11 | 'app', 12 | { 13 | url: '', 14 | abstract: true 15 | } 16 | ).state( 17 | 'app.tabs', 18 | { 19 | url: '/tabs', 20 | abstract: true, 21 | views: { 22 | '@': { 23 | templateUrl: slideMenuTemplate 24 | } 25 | } 26 | } 27 | ); 28 | }); 29 | -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {%= o.htmlWebpackPlugin.options.title || o.htmlWebpackPlugin.options.pkg.name || 'Webpack App'%} 8 | 9 | 10 | 11 | 12 | {% for (var chunk in o.htmlWebpackPlugin.assets) { %}{% } %} 13 | 14 | 15 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ionic-webpack", 3 | "version": "0.0.1", 4 | "dependencies": { 5 | "ionic": "git@github.com:driftyco/ionic-bower.git#1.0.0-rc.1-nightly-1153", 6 | "angular": "1.3.15", 7 | "angular-animate": "~1.3.15", 8 | "angular-sanitize": "~1.3.15", 9 | "angular-ui-router": "0.2.13", 10 | "angular-cache": "~3.2.5", 11 | "auth0-angular": "~4.0.1", 12 | "ngstorage": "~0.3.0", 13 | "lodash": "~3.5.0", 14 | "selfish": "~0.3.3", 15 | "bourbon": "~4.2.1", 16 | "imgcache.js": "~0.7.6" 17 | }, 18 | "resolutions": { 19 | "angular-animate": "~1.3.15", 20 | "angular-sanitize": "~1.3.15", 21 | "angular": "1.3.13", 22 | "lodash": "~3.5.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/components/data/store/index.js: -------------------------------------------------------------------------------- 1 | var DataStore = angular.module('app.data.store', ['js-data']); 2 | 3 | module.exports = DataStore.name; 4 | 5 | DataStore 6 | .provider('DataStore', function (DSProvider){ 7 | return DSProvider; 8 | }) 9 | .config(function (DataStoreProvider, DSHttpAdapterProvider){ 10 | DSHttpAdapterProvider.defaults = { 11 | deserialize: function (config, response){ 12 | if(config.class == 'GoogleImage'){ 13 | return response.data.responseData.results; 14 | } 15 | return data; 16 | } 17 | }; 18 | DataStoreProvider.defaults.basePath = 'some.endpoint/'; 19 | }) 20 | .run(function (DataStore, DSHttpAdapter){ 21 | DataStore.registerAdapter('http', DSHttpAdapter, { default: true }); 22 | }); 23 | -------------------------------------------------------------------------------- /app/components/ui/subheader_pane/subheader_pane.scss: -------------------------------------------------------------------------------- 1 | @import './variables'; 2 | 3 | .subheader-pane { 4 | z-index: 11 !important; 5 | &.has-tabs-top.has-subheader { 6 | top: 137px; 7 | } 8 | &.has-subheader { 9 | top: 88px; 10 | } 11 | } 12 | 13 | .pane-backdrop { 14 | &:after{ 15 | content: " "; 16 | display: block; 17 | position: absolute; 18 | left: 0; top: 0; 19 | right: 0; bottom: 0; 20 | visibility: visible; 21 | opacity: 0; 22 | background-color: black; 23 | z-index: 3; 24 | transition: opacity 0.6s ease-out; 25 | -webkit-transition: opacity 0.6s ease-out; 26 | pointer-events: none; 27 | } 28 | &.pane-backdrop-visible { 29 | &:after { 30 | //visibility: visible; 31 | opacity: 0.8; 32 | } 33 | } 34 | } 35 | 36 | 37 | -------------------------------------------------------------------------------- /app/components/views/google_images/index.js: -------------------------------------------------------------------------------- 1 | var GoogleImages = angular.module('app.views.googleImages', [ 2 | require('../../data/google_images') 3 | ]); 4 | 5 | var GoogleImagesController = require('./controllers/google_images_controller'); 6 | var template = require('./google_images.jade'); 7 | 8 | module.exports = GoogleImages.name; 9 | 10 | GoogleImages.controller('GoogleImagesController', GoogleImagesController); 11 | 12 | GoogleImages.config(function ($stateProvider){ 13 | $stateProvider.state( 14 | 'app.tabs.googleImages', 15 | { 16 | url: '/google-images', 17 | resolve: GoogleImagesController.resolve, 18 | views: { 19 | '@app.tabs': { 20 | templateUrl: template, 21 | controller: 'GoogleImagesController as googleImages' 22 | } 23 | } 24 | } 25 | ) 26 | }); -------------------------------------------------------------------------------- /app/components/views/tabs.jade: -------------------------------------------------------------------------------- 1 | ion-side-menus 2 | ion-side-menu-content 3 | ion-nav-bar.bar.bar-royal(align-title='center') 4 | ion-nav-back-button 5 | ion-nav-buttons(side='left') 6 | button.button.button-icon.button-clear.ion-navicon(menu-toggle='left') 7 | 8 | ion-nav-title App 9 | ion-pane 10 | ion-fake-tabs(background='royal', color='light') 11 | ion-fake-tab(sref='app.tabs.googleImages', icon='ion-android-image', root='true') 12 | ion-fake-tab(sref='app.tabs.test1', icon='ion-android-happy', root='true') 13 | ion-fake-tab(sref='app.tabs.test2', icon='ion-android-bulb', root='true') 14 | ion-nav-view(animation='slide-left-right') 15 | 16 | ion-side-menu.dark-bg.dark-border(side='left', expose-aside-when='large') 17 | ion-header-bar.bar-dark 18 | h1.title Side menu 19 | ion-content.has-header 20 | ion-list 21 | ion-item Menu item 22 | -------------------------------------------------------------------------------- /app/components/ui/loading/index.js: -------------------------------------------------------------------------------- 1 | require('./spinner.scss'); 2 | 3 | var Loading = angular.module('app.ui.loading', []); 4 | var loadingTemplateUrl = require('./loading.html'); 5 | 6 | module.exports = Loading.name; 7 | 8 | Loading.factory('LoadingPopup', LoadingPopupFactory); 9 | 10 | Loading.run(function ($rootScope, $ionicLoading){ 11 | $rootScope.$on('$stateChangeError', $ionicLoading.hide.bind($ionicLoading)); 12 | }); 13 | 14 | /** 15 | * @ngInject 16 | * @constructor 17 | * @param $ionicLoading 18 | * @param $rootScope 19 | * @param $sce 20 | */ 21 | function LoadingPopupFactory ($ionicLoading, $rootScope, $sce){ 22 | var loadingScope = $rootScope.$new(true); 23 | 24 | return function (loadingMessage){ 25 | loadingScope.$message = $sce.trustAsHtml(loadingMessage); 26 | $ionicLoading.show({ 27 | templateUrl: loadingTemplateUrl, 28 | hideOnStateChange: true, 29 | scope: loadingScope 30 | }); 31 | 32 | return function (){ 33 | $ionicLoading.hide(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/components/ui/view_switching/index.js: -------------------------------------------------------------------------------- 1 | var ViewSwitching = angular.module('app.ui.view-switching', [ 2 | require('../loading') 3 | ]); 4 | var _ = require('lodash'); 5 | 6 | module.exports = ViewSwitching.name; 7 | 8 | ViewSwitching.service('ViewSwitching', ViewSwitchingService); 9 | 10 | function ViewSwitchingService ($state, $ionicViewSwitcher, $ionicHistory, LoadingPopup){ 11 | this.goTo = goToState; 12 | 13 | function goToState (newState, params, direction, options){ 14 | direction = direction || 'forward'; 15 | options = options || {}; 16 | 17 | 18 | if($state.current.name == newState){ 19 | if(!params || _.isEmpty(params)){ 20 | return; 21 | } 22 | } 23 | 24 | if(options.root){ 25 | $ionicHistory.nextViewOptions({ 26 | historyRoot: true 27 | }); 28 | } 29 | 30 | if(direction){ 31 | $ionicViewSwitcher.nextDirection(direction); 32 | } 33 | 34 | LoadingPopup(options.popupText || 'Loading'); 35 | 36 | $state.go(newState, params); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var 4 | angular = require('angular'), 5 | templates = require('./templates'), 6 | bootstrap = require('./components/bootstrap'); 7 | 8 | var appModule = module.exports = angular 9 | .module('ionic-webpack', [ 10 | bootstrap.name, 11 | require('angular-router-exception-handler'), 12 | require('./env/'+NODE_ENV), 13 | require('./components/views'), 14 | templates 15 | ]) 16 | .constant('version', require('../package.json').version) 17 | .constant('config', require('./config')) 18 | .config(function ($compileProvider) { 19 | $compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|local|data|filesystem|file):/); 20 | $compileProvider.aHrefSanitizationWhitelist( 21 | /^\s*(https?|ftp|mailto|file|tel):/); 22 | }); 23 | 24 | // Add the styles to the page 25 | require('./index.scss'); 26 | 27 | // Patching angular + angular-animate error with transcluding comment element 28 | require('./jqlite_animate_comment_patch')(angular.element); 29 | 30 | bootstrap.ionicBootstrap(appModule, global, { 31 | fonts: ['Comfortaa'] 32 | }); 33 | 34 | -------------------------------------------------------------------------------- /app/components/utils/cached_image_loader/index.js: -------------------------------------------------------------------------------- 1 | var ImgCache = require('imgcache.js'); 2 | var CachedImageLoader = angular.module('app.utils.cachedImageLoader', []); 3 | 4 | module.exports = CachedImageLoader.name; 5 | 6 | CachedImageLoader.factory('CachedImageLoader', function($q){ 7 | var initDefer = $q.defer(); 8 | ImgCache.options.debug = true; 9 | ImgCache.options.chromeQuota = 50*1024*1024; 10 | ImgCache.init(initDefer.resolve, initDefer.reject); 11 | return function(src){ 12 | return initDefer.promise.then(function(){ 13 | var defer = $q.defer(); 14 | ImgCache.isCached(src, function(_src, cached){ 15 | if(cached){ 16 | ImgCache.getCachedFileURL(src, function(originalUrl, cachedFileUrl){ 17 | defer.resolve(cachedFileUrl); 18 | }, defer.reject); 19 | return; 20 | } 21 | ImgCache.cacheFile(src, function(){ 22 | ImgCache.getCachedFileURL(src, function(originalUrl, cachedFileUrl){ 23 | defer.resolve(cachedFileUrl); 24 | }, defer.reject); 25 | }, defer.reject, defer.notify); 26 | }); 27 | return defer.promise; 28 | }); 29 | }; 30 | }); 31 | -------------------------------------------------------------------------------- /config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ` 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | ionic-webpack 52 | 53 | An Ionic Framework and Cordova project. 54 | 55 | 56 | Ionic Framework Team 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Ionic Webpack starter app 2 | 3 | What's included: 4 | 5 | * __jade__ as templating language (or use html, if don't care) 6 | * __sass__ for style management (with ready-to-go Ionic styles override) 7 | * __js-data__ for data management (let's give it a try instead of Restangular, which is great too) 8 | * __imgcache.js__ and wrapper service to cache loaded images (Google Chrome or Cordova only) 9 | * __environment management__ (check env app/env dir) 10 | * __font preloading__ 11 | 12 | Plugins you will need to run app on device: 13 | 14 | * com.ionic.keyboard 15 | * org.apache.cordova.console 16 | * org.apache.cordova.device 17 | * org.apache.cordova.file 18 | * org.apache.cordova.file-transfer 19 | * org.apache.cordova.inappbrowser 20 | 21 | To get started run 22 | ```bash 23 | $ npm install 24 | $ ./dev-server 25 | ``` 26 | 27 | Will start development server with live reload on localhost:5000 28 | 29 | To build app run 30 | ```bash 31 | gulp webpack:build 32 | ``` 33 | 34 | App will be built into www folder, then you can package ionic app. 35 | 36 | ### Environment management 37 | You can choose environment to build using --env key 38 | ```bash 39 | gulp webpack:build --env=production 40 | ``` 41 | 42 | Environment other than __development__ will build minified project using ngAnnotate. 43 | Fonts preloading also enables in __non-development__ environments. 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ionic-webpack", 3 | "version": "0.0.1", 4 | "description": "Ionic Webpack starter app", 5 | "scripts": { 6 | "install": "./node_modules/.bin/bower install" 7 | }, 8 | "devDependencies": { 9 | "bower": "^1.3.12", 10 | "css-loader": "^0.9.0", 11 | "del": "^0.1.3", 12 | "exports-loader": "^0.6.2", 13 | "expose-loader": "^0.6.0", 14 | "extract-text-webpack-plugin": "^0.3.8", 15 | "file-loader": "^0.7.2", 16 | "gulp": "^3.8.8", 17 | "gulp-util": "^3.0.1", 18 | "html-loader": "^0.2.3", 19 | "html-webpack-plugin": "^1.1.0", 20 | "image-webpack-loader": "^1.2.0", 21 | "imagemin": "^3.1.0", 22 | "imports-loader": "^0.6.3", 23 | "jade": "^1.9.2", 24 | "jade-html-loader": "0.0.2", 25 | "json-loader": "^0.5.1", 26 | "loader-utils": "^0.2.6", 27 | "lodash": "^2.4.1", 28 | "ng-annotate-webpack-plugin": "^0.1.2", 29 | "ngtemplate-loader": "^1.3.0", 30 | "open": "0.0.5", 31 | "sass-loader": "^0.5.0", 32 | "style-loader": "^0.8.3", 33 | "url-loader": "^0.5.5", 34 | "webfontloader": "^1.5.14", 35 | "webpack": "^1.4.5", 36 | "webpack-dev-server": "^1.6.5", 37 | "yargs": "^3.4.5" 38 | }, 39 | "dependencies": { 40 | "angular-router-exception-handler": "^1.0.1", 41 | "components-webfontloader": "^1.5.2", 42 | "js-data": "^1.5.13", 43 | "js-data-angular": "^2.2.3" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/jqlite_animate_comment_patch.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | module.exports = patchJQLite; 3 | 4 | function patchJQLite (ngElement){ 5 | function after (element, newElement) { 6 | var index = element, parent = element.parentNode; 7 | if(!parent) return; 8 | newElement = new ngElement(newElement); 9 | 10 | for (var i = 0, ii = newElement.length; i < ii; i++) { 11 | var node = newElement[i]; 12 | parent.insertBefore(node, index.nextSibling); 13 | index = node; 14 | } 15 | } 16 | 17 | function chain(fn, name) { 18 | /** 19 | * chaining functions 20 | */ 21 | ngElement.prototype[name] = function(arg1, arg2, arg3) { 22 | var value; 23 | 24 | for (var i = 0, ii = this.length; i < ii; i++) { 25 | if (_.isUndefined(value)) { 26 | value = fn(this[i], arg1, arg2, arg3); 27 | if (!_.isUndefined(value)) { 28 | // any function which returns a value needs to be wrapped 29 | value = ngElement(value); 30 | } 31 | } else { 32 | jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3)); 33 | } 34 | } 35 | return !_.isUndefined(value) ? value : this; 36 | }; 37 | } 38 | 39 | chain(after, 'after'); 40 | } 41 | 42 | // AngularJS core code 43 | function jqLiteAddNodes(root, elements) { 44 | // THIS CODE IS VERY HOT. Don't make changes without benchmarking. 45 | 46 | if (elements) { 47 | 48 | // if a Node (the most common case) 49 | if (elements.nodeType) { 50 | root[root.length++] = elements; 51 | } else { 52 | var length = elements.length; 53 | 54 | // if an Array or NodeList and not a Window 55 | if (typeof length === 'number' && elements.window !== elements) { 56 | if (length) { 57 | for (var i = 0; i < length; i++) { 58 | root[root.length++] = elements[i]; 59 | } 60 | } 61 | } else { 62 | root[root.length++] = elements; 63 | } 64 | } 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /app/components/ui/shrinkable_image_header/directive.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var directiveTemplateUrl = require('./shrinkable_image_header.html'); 3 | 4 | module.exports = ShrinkableImageHeaderProvider; 5 | 6 | /** 7 | * @ngInject 8 | * @constructor 9 | */ 10 | function ShrinkableImageHeaderProvider ($parse){ 11 | return { 12 | restrict: 'E', 13 | transclude: true, 14 | templateUrl: directiveTemplateUrl, 15 | scope: { 16 | imageUrl: '=imageUrl' 17 | }, 18 | replace: true, 19 | link: function ($scope, $element, $attr){ 20 | var minHeaderHeight = $attr.minHeight ? $scope.$eval($attr.minHeight) : 0; 21 | var content = angular.element($element.parent()[0].querySelectorAll('ion-content')); 22 | 23 | content.bind('scroll', manageScroll($element,minHeaderHeight)) 24 | } 25 | }; 26 | 27 | function shrink(header, threshold, minThreshold, amt, dir) { 28 | ionic.requestAnimationFrame(function() { 29 | // Threshold is equal to bar-height 30 | 31 | amt = Math.max(minThreshold, threshold - amt); 32 | amt = Math.min(threshold * 1.3, amt); 33 | amt = amt < 0 ? 0 : amt; 34 | // Re-position the header 35 | header.style[ionic.CSS.TRANSFORM] = 'translate3d(0,-' + (threshold-amt) + 'px, 0)'; 36 | }); 37 | } 38 | 39 | function manageScroll ($headerImageElement, minThreshold){ 40 | var 41 | elem = $headerImageElement[0], 42 | threshold = elem.getBoundingClientRect().height, prev = 0, prevDir = 0, dir = 1, prevShrinkAmt = 0, starty = 0; 43 | return function (e){ 44 | var delta; 45 | // Scroll delta 46 | delta = e.detail.scrollTop - prev; 47 | prev = e.detail.scrollTop; 48 | 49 | dir = delta >= 0 ? 1 : -1; 50 | if(dir !== prevDir) starty = e.detail.scrollTop; 51 | 52 | if(dir === 1){ 53 | // Calculate expansion amount 54 | //shrinkAmt = prevShrinkAmt - Math.min(threshold, (starty - e.detail.scrollTop)); 55 | shrink(elem, threshold, minThreshold, e.detail.scrollTop, dir); 56 | }else{ 57 | shrink(elem, threshold, minThreshold, e.detail.scrollTop, dir); 58 | } 59 | 60 | }; 61 | 62 | } 63 | } -------------------------------------------------------------------------------- /app/components/ui/subheader_pane/subheader_pane_content_directive.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var templateUrl = require('./subheader_pane_content.html'); 3 | module.exports = SubheaderPaneContentDirectiveProvider; 4 | 5 | 6 | function SubheaderPaneContentDirectiveProvider (){ 7 | return { 8 | restrict: 'E', 9 | templateUrl: templateUrl, 10 | transclude: true, 11 | require: ['^subheaderPane','subheaderPaneContent'], 12 | controller: SubheaderPaneContentController, 13 | link: preLinkFn 14 | }; 15 | 16 | function preLinkFn ($scope, iElem, iAttrs, controllers, transcludeFn) { 17 | var subheaderPane = controllers[0]; 18 | var paneContent = controllers[1]; 19 | subheaderPane.setContent(paneContent); 20 | transcludeFn(function (clone){ 21 | var ionContent = angular.element(iElem[0].querySelector('ion-content')); 22 | _.each(iElem[0].attributes, function (value) { 23 | if(value.name == 'class'){ 24 | ionContent.addClass(value.value); 25 | }else{ 26 | ionContent.attr(value.name, value.value); 27 | } 28 | iElem.removeAttr(value.name); 29 | }); 30 | }); 31 | } 32 | 33 | function postLinkFn ($scope, iElem, iAttrs, controllers, transcludeFn) { 34 | 35 | } 36 | } 37 | 38 | 39 | function SubheaderPaneContentController ($element){ 40 | this.show = _.partial(showHide, true); 41 | 42 | this.hide = _.partial(showHide, false); 43 | 44 | this.showHide = showHide; 45 | 46 | function showHide (show, animate){ 47 | var ionContent = $element[0].querySelector('ion-content'); 48 | var scroll = angular.element(ionContent).children()[0]; 49 | var height; 50 | if(animate) { 51 | ionContent.style[ionic.CSS.TRANSITION] = 'all 0.5s ease-in'; 52 | }else{ 53 | ionContent.style[ionic.CSS.TRANSITION] = ''; 54 | } 55 | 56 | ionContent.style['z-index'] = '10'; 57 | 58 | if(show){ 59 | ionContent.style[ionic.CSS.TRANSFORM] = 'translate3d(0,0,0)'; 60 | }else{ 61 | height = ionContent.getBoundingClientRect().height; 62 | ionContent.style[ionic.CSS.TRANSFORM] = 'translate3d(0,-' + height + 'px, 0)'; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/components/bootstrap.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Module dependencies 5 | */ 6 | var angular = require('angular'); 7 | // set the public path 8 | var scripts = global.document.getElementsByTagName('script'); 9 | var src = scripts[scripts.length - 1].getAttribute('src'); 10 | global.__webpack_public_path__ = src.substr(0, src.lastIndexOf('/') + 1); 11 | 12 | // Add Angular/Ionic dependencies 13 | require('angular-animate'); 14 | require('angular-sanitize'); 15 | require('angular-ui-router'); 16 | require('ionic/js/ionic'); 17 | require('ionic/js/ionic-angular'); 18 | require('components-webfontloader'); 19 | 20 | var libsModule = module.exports = angular.module('common.libs', [ 21 | 'ionic' 22 | ]); 23 | 24 | libsModule.ionicBootstrap = function (module, window, options) { 25 | if (!window || !window.document) { 26 | throw new Error('window and document objects required.'); 27 | } 28 | 29 | options = options || {}; 30 | 31 | function onDeviceReady () { 32 | // bootstrap angular app 33 | loadFonts(function (){ 34 | angular.element(window.document).ready(function () { 35 | angular.bootstrap(window.document, [ 36 | module.name 37 | ]); 38 | }); 39 | }); 40 | // remove document deviceready listener 41 | window.document.removeEventListener('deviceready', onDeviceReady, false); 42 | } 43 | 44 | function onWindowLoad () { 45 | if (!(!window.cordova && !window.PhoneGap && !window.phonegap)) { 46 | // when on device add document deviceready listener 47 | window.document.addEventListener('deviceready', onDeviceReady, false); 48 | 49 | } else { 50 | // when on browser trigger onDeviceReady 51 | onDeviceReady(); 52 | } 53 | 54 | // remove window load listener 55 | window.removeEventListener('load', onWindowLoad, false); 56 | } 57 | 58 | function loadFonts (cb){ 59 | if((NODE_ENV == 'development') || !options.fonts) return cb(); 60 | window.WebFont.load({ 61 | custom: { 62 | families: options.fonts, 63 | testStrings: options.fontsTestStrings 64 | }, 65 | active: cb 66 | }); 67 | } 68 | 69 | // add window load listener 70 | window.addEventListener('load', onWindowLoad, false); 71 | }; 72 | -------------------------------------------------------------------------------- /app/components/ui/static_map/index.js: -------------------------------------------------------------------------------- 1 | STATIC_API_ENDPOINT = 'http://maps.googleapis.com/maps/api/staticmap'; 2 | var _ = require('lodash'); 3 | var StaticMap = angular.module('app.ui.staticMap', [ 4 | require('../../utils/cached_image_loader') 5 | ]); 6 | var templateUrl = require('./static_map.html'); 7 | 8 | module.exports = StaticMap.name; 9 | 10 | require('./static_map.scss'); 11 | 12 | StaticMap.directive('staticGoogleMap', StaticGoogleMapDirectiveProvider); 13 | 14 | function StaticGoogleMapDirectiveProvider ($timeout, CachedImageLoader){ 15 | return { 16 | restrict: 'E', 17 | scope: { 18 | markers: '&markers', 19 | center: '¢er', 20 | zoom: '&zoom' 21 | }, 22 | templateUrl: templateUrl, 23 | replace: true, 24 | transclude: true, 25 | link: linkFn 26 | }; 27 | 28 | function linkFn ($scope, iElem, iAttrs){ 29 | 30 | var timerId = $timeout(setMapLoading, 100); 31 | var clientRect = iElem[0].getBoundingClientRect(); 32 | var markers = $scope.markers(); 33 | var center = $scope.center(); 34 | var zoom = $scope.zoom(); 35 | 36 | if(!(markers || center)){ 37 | throw 'Error initializing static Google Map - you need to provide either markers or center'; 38 | } 39 | 40 | CachedImageLoader(STATIC_API_ENDPOINT + '?' + buildParams()) 41 | .then(function (imageSrc){ 42 | $timeout.cancel(timerId); 43 | $scope.mapImageUrl = imageSrc; 44 | $timeout(function(){ 45 | $scope.loaded = true; 46 | },100); 47 | }); 48 | 49 | function setMapLoading (){ 50 | $scope.loading = true; 51 | } 52 | 53 | function buildParams (){ 54 | var paramStr = 'size='+clientRect.width.toFixed(0)+'x'+clientRect.height.toFixed(0); 55 | if(markers){ 56 | paramStr += '&markers=' + _.map(markers, function (marker) { 57 | return _.map(marker, function (v, k){ 58 | if(k == 'location'){ 59 | return locationParam(v); 60 | } 61 | return k+':'+v; 62 | }).join('|'); 63 | }).join('&markers='); 64 | }else{ 65 | paramStr += '¢er='+locationParam(center); 66 | } 67 | 68 | if(zoom) paramStr += '&zoom='+zoom; 69 | 70 | return paramStr; 71 | } 72 | 73 | function locationParam (location){ 74 | if(_.isArray(location)){ 75 | return '' + location[0] + ',' + location[1]; 76 | } 77 | return location.lat + ',' + location.lng; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/components/ui/subheader_pane/subheader_pane_directive.js: -------------------------------------------------------------------------------- 1 | var templateUrl = require('./subheader_pane.html'); 2 | 3 | module.exports = SubheaderPaneDirectiveProvider; 4 | 5 | function SubheaderPaneDirectiveProvider (){ 6 | return { 7 | restrict: 'E', 8 | templateUrl: templateUrl, 9 | replace: true, 10 | transclude: true, 11 | controller: SubheaderPaneController, 12 | priority: 999999, 13 | compile: compile 14 | }; 15 | 16 | function compile (){ 17 | return { 18 | post: linkFn 19 | } 20 | } 21 | 22 | function linkFn ($scope, iElem, iAttrs, ctrl, transcludeFn){ 23 | var sliderPane = angular.element(iElem[0].querySelector('subheader-pane-content')); 24 | sliderPane.detach(); 25 | iElem.parent().prepend(sliderPane); 26 | $scope.$$postDigest(function (){ 27 | ctrl.init(); 28 | }); 29 | } 30 | } 31 | 32 | function SubheaderPaneController ($scope, $element, $attrs){ 33 | var content, elem = $element[0]; 34 | this.show = _.partial(showHide, true); 35 | 36 | this.hide = _.partial(showHide, false); 37 | 38 | this.showHide = showHide; 39 | 40 | function showHide (show, animate){ 41 | var height; 42 | 43 | animate = _.isUndefined(animate) ? true : animate; 44 | 45 | // 46 | //_.each($element.children(), function (children){ 47 | // if(animate) { 48 | // children.style[ionic.CSS.TRANSITION] = 'all 0.5s ease-in'; 49 | // }else{ 50 | // children.style[ionic.CSS.TRANSITION] = ''; 51 | // } 52 | //}); 53 | // 54 | //if(animate) { 55 | // elem.style[ionic.CSS.TRANSITION] = 'all 0.5s ease-in'; 56 | //}else{ 57 | // elem.style[ionic.CSS.TRANSITION] = ''; 58 | //} 59 | // 60 | //if(show){ 61 | // elem.style[ionic.CSS.TRANSFORM] = 'translate3d(0,0,0)'; 62 | // _.each($element.children(), function (children){ 63 | // children.style['opacity'] = '1'; 64 | // }); 65 | //}else{ 66 | // _.each($element.children(), function (children){ 67 | // children.style['opacity'] = '0'; 68 | // }); 69 | // height = elem.getBoundingClientRect().height; 70 | // elem.style[ionic.CSS.TRANSFORM] = 'translate3d(0,-' + height + 'px, 0)'; 71 | //} 72 | 73 | content.showHide(show, animate); 74 | if($attrs.onPaneToggle) $scope.$eval($attrs.onPaneToggle, {$visible: show}); 75 | } 76 | 77 | this.init = function (){ 78 | content && content.hide(); 79 | }; 80 | 81 | this.setContent = function (_content){ 82 | content = _content; 83 | }; 84 | } 85 | 86 | 87 | -------------------------------------------------------------------------------- /hooks/after_prepare/010_add_platform_class.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // Add Platform Class 4 | // v1.0 5 | // Automatically adds the platform class to the body tag 6 | // after the `prepare` command. By placing the platform CSS classes 7 | // directly in the HTML built for the platform, it speeds up 8 | // rendering the correct layout/style for the specific platform 9 | // instead of waiting for the JS to figure out the correct classes. 10 | 11 | var fs = require('fs'); 12 | var path = require('path'); 13 | 14 | var rootdir = process.argv[2]; 15 | 16 | function addPlatformBodyTag(indexPath, platform) { 17 | // add the platform class to the body tag 18 | try { 19 | var platformClass = 'platform-' + platform; 20 | var cordovaClass = 'platform-cordova platform-webview'; 21 | 22 | var html = fs.readFileSync(indexPath, 'utf8'); 23 | 24 | var bodyTag = findBodyTag(html); 25 | if(!bodyTag) return; // no opening body tag, something's wrong 26 | 27 | if(bodyTag.indexOf(platformClass) > -1) return; // already added 28 | 29 | var newBodyTag = bodyTag; 30 | 31 | var classAttr = findClassAttr(bodyTag); 32 | if(classAttr) { 33 | // body tag has existing class attribute, add the classname 34 | var endingQuote = classAttr.substring(classAttr.length-1); 35 | var newClassAttr = classAttr.substring(0, classAttr.length-1); 36 | newClassAttr += ' ' + platformClass + ' ' + cordovaClass + endingQuote; 37 | newBodyTag = bodyTag.replace(classAttr, newClassAttr); 38 | 39 | } else { 40 | // add class attribute to the body tag 41 | newBodyTag = bodyTag.replace('>', ' class="' + platformClass + ' ' + cordovaClass + '">'); 42 | } 43 | 44 | html = html.replace(bodyTag, newBodyTag); 45 | 46 | fs.writeFileSync(indexPath, html, 'utf8'); 47 | 48 | process.stdout.write('add to body class: ' + platformClass + '\n'); 49 | } catch(e) { 50 | process.stdout.write(e); 51 | } 52 | } 53 | 54 | function findBodyTag(html) { 55 | // get the body tag 56 | try{ 57 | return html.match(/])(.*?)>/gi)[0]; 58 | }catch(e){} 59 | } 60 | 61 | function findClassAttr(bodyTag) { 62 | // get the body tag's class attribute 63 | try{ 64 | return bodyTag.match(/ class=["|'](.*?)["|']/gi)[0]; 65 | }catch(e){} 66 | } 67 | 68 | if (rootdir) { 69 | 70 | // go through each of the platform directories that have been prepared 71 | var platforms = (process.env.CORDOVA_PLATFORMS ? process.env.CORDOVA_PLATFORMS.split(',') : []); 72 | 73 | for(var x=0; x 21 | # Cordova Hooks 22 | 23 | This directory may contain scripts used to customize cordova commands. This 24 | directory used to exist at `.cordova/hooks`, but has now been moved to the 25 | project root. Any scripts you add to these directories will be executed before 26 | and after the commands corresponding to the directory name. Useful for 27 | integrating your own build systems or integrating with version control systems. 28 | 29 | __Remember__: Make your scripts executable. 30 | 31 | ## Hook Directories 32 | The following subdirectories will be used for hooks: 33 | 34 | after_build/ 35 | after_compile/ 36 | after_docs/ 37 | after_emulate/ 38 | after_platform_add/ 39 | after_platform_rm/ 40 | after_platform_ls/ 41 | after_plugin_add/ 42 | after_plugin_ls/ 43 | after_plugin_rm/ 44 | after_plugin_search/ 45 | after_prepare/ 46 | after_run/ 47 | after_serve/ 48 | before_build/ 49 | before_compile/ 50 | before_docs/ 51 | before_emulate/ 52 | before_platform_add/ 53 | before_platform_rm/ 54 | before_platform_ls/ 55 | before_plugin_add/ 56 | before_plugin_ls/ 57 | before_plugin_rm/ 58 | before_plugin_search/ 59 | before_prepare/ 60 | before_run/ 61 | before_serve/ 62 | pre_package/ <-- Windows 8 and Windows Phone only. 63 | 64 | ## Script Interface 65 | 66 | All scripts are run from the project's root directory and have the root directory passes as the first argument. All other options are passed to the script using environment variables: 67 | 68 | * CORDOVA_VERSION - The version of the Cordova-CLI. 69 | * CORDOVA_PLATFORMS - Comma separated list of platforms that the command applies to (e.g.: android, ios). 70 | * CORDOVA_PLUGINS - Comma separated list of plugin IDs that the command applies to (e.g.: org.apache.cordova.file, org.apache.cordova.file-transfer) 71 | * CORDOVA_HOOK - Path to the hook that is being executed. 72 | * CORDOVA_CMDLINE - The exact command-line arguments passed to cordova (e.g.: cordova run ios --emulate) 73 | 74 | If a script returns a non-zero exit code, then the parent cordova command will be aborted. 75 | 76 | 77 | ## Writing hooks 78 | 79 | We highly recommend writting your hooks using Node.js so that they are 80 | cross-platform. Some good examples are shown here: 81 | 82 | [http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/](http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/) 83 | 84 | -------------------------------------------------------------------------------- /app/components/ui/fake_tabs/index.js: -------------------------------------------------------------------------------- 1 | var FakeTabs = angular.module('app.ui.fakeTabs', []); 2 | var tabsTemplateUrl = require('./fake_tabs.html'); 3 | var tabTemplateUrl = require('./fake_tab.html'); 4 | 5 | module.exports = FakeTabs.name; 6 | 7 | FakeTabs.directive('ionFakeTabs', FakeTabsProvider); 8 | FakeTabs.directive('ionFakeTab', FakeTabProvider); 9 | /** 10 | * @ngInject 11 | * @constructor 12 | */ 13 | function FakeTabsProvider (){ 14 | return { 15 | restrict: 'E', 16 | controller: FakeTabsController, 17 | templateUrl: tabsTemplateUrl, 18 | require: 'ionFakeTabs', 19 | transclude: true, 20 | replace: true, 21 | compile: function (tElem, tAttrs){ 22 | return { 23 | pre: function ($scope, iElem, iAttrs, fakeTabsCtrl){ 24 | $scope.tabs = fakeTabsCtrl; 25 | if(ionic.Platform.isAndroid()){ 26 | iElem.children().addClass('tabs-top tabs-striped'); 27 | } 28 | }, 29 | post: function ($scope, iElem, iAttrs, fakeTabsCtrl){ 30 | var navView = angular.element(iElem[0].querySelectorAll('ion-nav-view')); 31 | iElem.append(navView.detach()); 32 | } 33 | } 34 | } 35 | }; 36 | } 37 | 38 | /** 39 | * @ngInject 40 | * @constructor 41 | */ 42 | function FakeTabProvider ($state, $rootScope, $ionicHistory, $ionicViewSwitcher){ 43 | return { 44 | restrict: 'E', 45 | transclude: true, 46 | replace: true, 47 | templateUrl: tabTemplateUrl, 48 | scope: true, 49 | compile: function (tElem, tAttrs){ 50 | return { 51 | post: function ($scope, iElem, iAttrs){ 52 | var cancelListener = $rootScope.$on('$stateChangeSuccess', checkActiveState); 53 | 54 | $scope.active = false; 55 | $scope.hasIcon = !!iAttrs.icon; 56 | $scope.iconClasses = $scope.hasIcon ? iAttrs.icon : null; 57 | $scope.generateHref = generateHref; 58 | $scope.prepareViewSwitch = prepareViewSwitch; 59 | 60 | $scope.$on('$destroy', cancelListener); 61 | 62 | checkActiveState(); 63 | 64 | function checkActiveState (){ 65 | $scope.active = $state.current.name == iAttrs.sref; 66 | } 67 | 68 | function generateHref (){ 69 | return $state.href(iAttrs.sref, iAttrs.srefParams ? $scope.$eval(iAttrs.srefParams) : null); 70 | } 71 | 72 | function prepareViewSwitch (){ 73 | if(iAttrs.root){ 74 | $ionicHistory.nextViewOptions({ 75 | historyRoot: true 76 | }); 77 | } 78 | $ionicViewSwitcher.nextDirection(iAttrs.direction || 'forward'); 79 | } 80 | } 81 | }; 82 | } 83 | }; 84 | } 85 | 86 | /** 87 | * @ngInject 88 | * @param $scope 89 | * @param $attrs 90 | * @constructor 91 | */ 92 | function FakeTabsController ($scope, $attrs){ 93 | var platformClasses = []; 94 | var colorClasses = []; 95 | var contentClasses = []; 96 | 97 | if($attrs.color) colorClasses.push('tabs-color-'+$attrs.color); 98 | if($attrs.background) colorClasses.push('tabs-background-'+$attrs.background); 99 | 100 | if(ionic.Platform.isAndroid()){ 101 | $scope.$hasTabsTop = true; 102 | }else{ 103 | $scope.$hasTabs = true; 104 | } 105 | 106 | this.classes = [].concat(platformClasses, colorClasses); 107 | 108 | this.getViewClasses = function (){ 109 | return contentClasses; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require("gulp"); 2 | var gutil = require('gulp-util'); 3 | var webpack = require("webpack"); 4 | var ngAnnotateWebpackPlugin = require("ng-annotate-webpack-plugin"); 5 | var WebpackDevServer = require("webpack-dev-server"); 6 | var webpackConfig = require("./webpack.config.js"); 7 | var argv = require('yargs').argv; 8 | 9 | argv.env = argv.env || 'development'; 10 | 11 | var src = './src', 12 | dest = './dist', 13 | webpackOptions = { 14 | debug: true, 15 | devtool: '#source-map', 16 | watchDelay: 200 17 | }; 18 | 19 | // The development server (the recommended option for development) 20 | gulp.task("default", ["webpack-dev-server"]); 21 | 22 | // Build and watch cycle (another option for development) 23 | // Advantage: No server required, can run app from filesystem 24 | // Disadvantage: Requests are not blocked until bundle is available, 25 | // can serve an old app on refresh 26 | gulp.task("build-dev", ["webpack:build-dev"], function() { 27 | gulp.watch(["src/**/*"], ["webpack:build-dev"]); 28 | }); 29 | 30 | // Production build 31 | gulp.task("build", ["webpack:build"]); 32 | 33 | gulp.task("webpack:build", function(callback) { 34 | // modify some webpack config options 35 | var myConfig = Object.create(webpackConfig); 36 | 37 | if(argv.env != 'development'){ 38 | myConfig.plugins = myConfig.plugins.concat( 39 | new ngAnnotateWebpackPlugin({ 40 | add: true 41 | }), 42 | new webpack.optimize.DedupePlugin(), 43 | new webpack.optimize.UglifyJsPlugin({ 44 | output: {comments: false} 45 | }) 46 | ); 47 | } 48 | 49 | // run webpack 50 | webpack(myConfig, function(err, stats) { 51 | if(err) throw new gutil.PluginError("webpack:build", err); 52 | gutil.log("[webpack:build]", stats.toString({ 53 | colors: true 54 | })); 55 | callback(); 56 | }); 57 | }); 58 | 59 | // modify some webpack config options 60 | var myDevConfig = Object.create(webpackConfig); 61 | myDevConfig.devtool = "sourcemap"; 62 | myDevConfig.debug = true; 63 | 64 | // create a single instance of the compiler to allow caching 65 | var devCompiler = webpack(myDevConfig); 66 | 67 | gulp.task("webpack:build-dev", function(callback) { 68 | // run webpack 69 | devCompiler.run(function(err, stats) { 70 | if(err) throw new gutil.PluginError("webpack:build-dev", err); 71 | gutil.log("[webpack:build-dev]", stats.toString({ 72 | colors: true 73 | })); 74 | callback(); 75 | }); 76 | }); 77 | 78 | gulp.task("webpack-dev-server", function(callback) { 79 | // modify some webpack config options 80 | var myConfig = Object.create(webpackConfig); 81 | myConfig.devtool = "eval"; 82 | myConfig.debug = true; 83 | 84 | // Start a webpack-dev-server 85 | new WebpackDevServer(webpack(myConfig), { 86 | publicPath: "/www", 87 | inline: true, 88 | hot: true, 89 | stats: { 90 | colors: true 91 | } 92 | }).listen(5000, "localhost", function(err) { 93 | if(err) throw new gutil.PluginError("webpack-dev-server", err); 94 | gutil.log("[webpack-dev-server]", "http://localhost:5000/webpack-dev-server/index.html"); 95 | }); 96 | }); 97 | 98 | gulp.task("webpack-test-server", function(callback) { 99 | // modify some webpack config options 100 | var myConfig = Object.create(webpackConfig); 101 | myConfig.debug = false; 102 | 103 | // Start a webpack-dev-server 104 | new WebpackDevServer(webpack(myConfig), { 105 | publicPath: "/", 106 | inline: true, 107 | stats: { 108 | colors: true 109 | } 110 | }).listen(5000, "localhost", function(err) { 111 | if(err) throw new gutil.PluginError("webpack-dev-server", err); 112 | gutil.log("[webpack-dev-server]", "http://localhost:5000/webpack-dev-server/index.html"); 113 | }); 114 | }); 115 | 116 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var _ = require("lodash"); 2 | var path = require("path"); 3 | var webpack = require("webpack"); 4 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 5 | var ExtractTextPlugin = require("extract-text-webpack-plugin"); 6 | var argv = require('yargs').argv; 7 | var cssLoaders, plugins; 8 | var appRoot = path.resolve(__dirname, "./app"); 9 | 10 | argv.env = argv.env || 'development'; 11 | 12 | 13 | if(argv.env != 'development'){ 14 | cssLoaders = [ 15 | { test: /\.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader?root="+appRoot) }, 16 | { test: /\.less$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader?root="+appRoot+"!less-loader") }, 17 | { test: /\.scss$/, loader: ExtractTextPlugin.extract("style-loader","css-loader?root="+appRoot+"!sass") } 18 | ]; 19 | plugins = [ 20 | new ExtractTextPlugin("[name].css") 21 | ]; 22 | 23 | 24 | }else{ 25 | cssLoaders = [ 26 | { test: /\.css$/, loader: "style-loader!css-loader?root="+appRoot }, 27 | { test: /\.less$/, loader: "style-loader!css-loader?root="+appRoot+"!less-loader" }, 28 | { test: /\.scss$/, loader: "style-loader!css-loader?root="+appRoot+"!sass" } 29 | ]; 30 | plugins = []; 31 | } 32 | 33 | module.exports = { 34 | cache: true, 35 | debug: true, 36 | devTool: 'eval', 37 | entry: { 38 | vendor: ['angular', 'angular-ui-router', 'lodash', 'angular-animate', 'angular-cache', 'angular-sanitize', 'js-data', 'js-data-angular'], 39 | app: ['./app/index'] 40 | }, 41 | output: { 42 | path: path.join(__dirname, "www"), 43 | filename: "[name].bundle.js", 44 | chunkFilename: "[id].bundle.js" 45 | }, 46 | module: { 47 | loaders: cssLoaders.concat([ 48 | { test: /\.(jpe?g|png|gif)$/i, loader: 'image?bypassOnDebug&optimizationLevel=7&interlaced=false' }, 49 | { 50 | test : /\.json$/, 51 | loader : 'json' 52 | }, 53 | { test: /\.html$/, 54 | loader: "ngtemplate?relativeTo=" + (path.resolve(__dirname, './app')) + "/&module=templates!html?root=app"}, 55 | { test: /\.jade$/, 56 | loader: "ngtemplate?relativeTo=" + (path.resolve(__dirname, './app')) + "/&module=templates!html!jade-html"}, 57 | { test: /\.woff($|\?)/, loader: 'url-loader?prefix=font/&limit=5000&mimetype=application/font-woff' }, 58 | { test: /\.woff2($|\?)/, loader: 'url-loader?prefix=font/&limit=5000&mimetype=application/font-woff' }, 59 | { test: /\.ttf($|\?)/, loader: "file-loader?prefix=font/" }, 60 | { test: /\.eot($|\?)/, loader: "file-loader?prefix=font/" }, 61 | { test: /\.svg($|\?)/, loader: "file-loader?prefix=font/" }, 62 | { 63 | test : /[\/]angular\.js$/, 64 | loader : 'exports?angular' 65 | }, { 66 | test : /[\/]ionic\.js$/, 67 | loader : 'exports?ionic' 68 | } 69 | ]), 70 | noParse: [ 71 | /bower_components/ 72 | ] 73 | }, 74 | resolve: { 75 | root: [ 76 | path.join(__dirname, 'app'), 77 | path.join(__dirname, 'bower_components'), 78 | path.join(__dirname, 'node_modules') 79 | ], 80 | moduleDirectories: [ 81 | 'bower_components', 82 | 'node_modules' 83 | ], 84 | alias: { 85 | app: [path.join(__dirname, 'app')] 86 | } 87 | }, 88 | externals: { 89 | 'js-data-schema': 'undefined' 90 | }, 91 | plugins: plugins.concat([ 92 | new webpack.ProvidePlugin({ 93 | _: "lodash" 94 | }), 95 | new webpack.ResolverPlugin( 96 | new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin( 97 | 'bower.json', ['main']) 98 | ), 99 | new webpack.DefinePlugin({ 100 | NODE_ENV: JSON.stringify(argv.env || 'development') 101 | }), 102 | new webpack.optimize.CommonsChunkPlugin({ 103 | name: 'vendor', 104 | minChunks: Infinity 105 | }), 106 | new HtmlWebpackPlugin({ 107 | pkg : require('./package.json'), 108 | template : 'app/index.html', 109 | env : argv.env || 'development' 110 | }) 111 | ]) 112 | }; 113 | -------------------------------------------------------------------------------- /app/fonts.scss: -------------------------------------------------------------------------------- 1 | /* cyrillic-ext */ 2 | @font-face { 3 | font-family: 'Comfortaa'; 4 | font-style: normal; 5 | font-weight: 300; 6 | src: local('Comfortaa Light'), local('Comfortaa-Light'), url(./fonts/r_tUZNl0G8xCoOmp_JkSCg7aC6SjiAOpAWOKfJDfVRY.woff2) format('woff2'); 7 | unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F; 8 | } 9 | /* cyrillic */ 10 | @font-face { 11 | font-family: 'Comfortaa'; 12 | font-style: normal; 13 | font-weight: 300; 14 | src: local('Comfortaa Light'), local('Comfortaa-Light'), url(./fonts/r_tUZNl0G8xCoOmp_JkSChdwxCXfZpKo5kWAx_74bHs.woff2) format('woff2'); 15 | unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; 16 | } 17 | /* greek */ 18 | @font-face { 19 | font-family: 'Comfortaa'; 20 | font-style: normal; 21 | font-weight: 300; 22 | src: local('Comfortaa Light'), local('Comfortaa-Light'), url(./fonts/r_tUZNl0G8xCoOmp_JkSCvy1_HTwRwgtl1cPga3Fy3Y.woff2) format('woff2'); 23 | unicode-range: U+0370-03FF; 24 | } 25 | /* latin-ext */ 26 | @font-face { 27 | font-family: 'Comfortaa'; 28 | font-style: normal; 29 | font-weight: 300; 30 | src: local('Comfortaa Light'), local('Comfortaa-Light'), url(./fonts/r_tUZNl0G8xCoOmp_JkSCojoYw3YTyktCCer_ilOlhE.woff2) format('woff2'); 31 | unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; 32 | } 33 | /* latin */ 34 | @font-face { 35 | font-family: 'Comfortaa'; 36 | font-style: normal; 37 | font-weight: 300; 38 | src: local('Comfortaa Light'), local('Comfortaa-Light'), url(./fonts/r_tUZNl0G8xCoOmp_JkSChampu5_7CjHW5spxoeN3Vs.woff2) format('woff2'); 39 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; 40 | } 41 | /* cyrillic-ext */ 42 | @font-face { 43 | font-family: 'Comfortaa'; 44 | font-style: normal; 45 | font-weight: 400; 46 | src: local('Comfortaa'), local('Comfortaa-Regular'), url(./fonts/Be0CkOtwwI2n86HMhtablYX0hVgzZQUfRDuZrPvH3D8.woff2) format('woff2'); 47 | unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F; 48 | } 49 | /* cyrillic */ 50 | @font-face { 51 | font-family: 'Comfortaa'; 52 | font-style: normal; 53 | font-weight: 400; 54 | src: local('Comfortaa'), local('Comfortaa-Regular'), url(./fonts/-DackuIFgo7Hfy3rR14C34X0hVgzZQUfRDuZrPvH3D8.woff2) format('woff2'); 55 | unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; 56 | } 57 | /* greek */ 58 | @font-face { 59 | font-family: 'Comfortaa'; 60 | font-style: normal; 61 | font-weight: 400; 62 | src: local('Comfortaa'), local('Comfortaa-Regular'), url(./fonts/UgYUhLCkRUocJ8OZnvelZ4X0hVgzZQUfRDuZrPvH3D8.woff2) format('woff2'); 63 | unicode-range: U+0370-03FF; 64 | } 65 | /* latin-ext */ 66 | @font-face { 67 | font-family: 'Comfortaa'; 68 | font-style: normal; 69 | font-weight: 400; 70 | src: local('Comfortaa'), local('Comfortaa-Regular'), url(./fonts/wz1gqe57Mbg11v-OrLlVjoX0hVgzZQUfRDuZrPvH3D8.woff2) format('woff2'); 71 | unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; 72 | } 73 | /* latin */ 74 | @font-face { 75 | font-family: 'Comfortaa'; 76 | font-style: normal; 77 | font-weight: 400; 78 | src: local('Comfortaa'), local('Comfortaa-Regular'), url(./fonts/qLBu5CQmSMt1H43OiWJ77ZBw1xU1rKptJj_0jans920.woff2) format('woff2'); 79 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; 80 | } 81 | /* cyrillic-ext */ 82 | @font-face { 83 | font-family: 'Comfortaa'; 84 | font-style: normal; 85 | font-weight: 700; 86 | src: local('Comfortaa Bold'), local('Comfortaa-Bold'), url(./fonts/fND5XPYKrF2tQDwwfWZJIw7aC6SjiAOpAWOKfJDfVRY.woff2) format('woff2'); 87 | unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F; 88 | } 89 | /* cyrillic */ 90 | @font-face { 91 | font-family: 'Comfortaa'; 92 | font-style: normal; 93 | font-weight: 700; 94 | src: local('Comfortaa Bold'), local('Comfortaa-Bold'), url(./fonts/fND5XPYKrF2tQDwwfWZJIxdwxCXfZpKo5kWAx_74bHs.woff2) format('woff2'); 95 | unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; 96 | } 97 | /* greek */ 98 | @font-face { 99 | font-family: 'Comfortaa'; 100 | font-style: normal; 101 | font-weight: 700; 102 | src: local('Comfortaa Bold'), local('Comfortaa-Bold'), url(./fonts/fND5XPYKrF2tQDwwfWZJI_y1_HTwRwgtl1cPga3Fy3Y.woff2) format('woff2'); 103 | unicode-range: U+0370-03FF; 104 | } 105 | /* latin-ext */ 106 | @font-face { 107 | font-family: 'Comfortaa'; 108 | font-style: normal; 109 | font-weight: 700; 110 | src: local('Comfortaa Bold'), local('Comfortaa-Bold'), url(./fonts/fND5XPYKrF2tQDwwfWZJI4joYw3YTyktCCer_ilOlhE.woff2) format('woff2'); 111 | unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; 112 | } 113 | /* latin */ 114 | @font-face { 115 | font-family: 'Comfortaa'; 116 | font-style: normal; 117 | font-weight: 700; 118 | src: local('Comfortaa Bold'), local('Comfortaa-Bold'), url(./fonts/fND5XPYKrF2tQDwwfWZJIxampu5_7CjHW5spxoeN3Vs.woff2) format('woff2'); 119 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; 120 | } --------------------------------------------------------------------------------