26 |
--------------------------------------------------------------------------------
/test/server.js:
--------------------------------------------------------------------------------
1 | /* global __dirname: false, module: true */
2 |
3 | var glob = require('glob'),
4 | path = require('path'),
5 | url = require('url'),
6 | fs = require('fs'),
7 | cheerio = require('cheerio'),
8 | layoutHtml = fs.readFileSync(path.resolve(__dirname, 'layout.html')),
9 | testsDoms = {},
10 | testFiles = glob.sync(path.resolve(__dirname, '**/*.test.html'));
11 |
12 | for (var i = 0; i < testFiles.length; i++) {
13 | var abs = testFiles[i];
14 | var key = path.relative(__dirname, abs).replace(/\.test\.html$/, '');
15 | testsDoms['/' + key] = cheerio.load(fs.readFileSync(abs), {decodeEntities: false});
16 | }
17 |
18 | module.exports = function(req, res, next) {
19 | var parsedUrl = url.parse(req.url, true);
20 |
21 | var test = testsDoms[parsedUrl.pathname];
22 | if (!test) { return next(); }
23 |
24 | var layout = cheerio.load(layoutHtml, {decodeEntities: false});
25 |
26 | layout('head').append(test('head').contents().clone());
27 | layout('body').append(test('body').contents().clone());
28 |
29 | res.write(layout.html());
30 | res.end();
31 | };
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 mcasimir (https://github.com/mcasimir)
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/src/less/lib/sections.less:
--------------------------------------------------------------------------------
1 | .section {
2 | .container-fluid;
3 | background: @body-bg;
4 | padding-top: floor(@line-height-computed / 2);
5 | padding-bottom: floor(@line-height-computed / 2);
6 | }
7 |
8 | .section-wide {
9 | padding-left: 0;
10 | padding-right: 0;
11 | }
12 |
13 | .section.section-condensed {
14 | padding-top: 0; padding-bottom: 0;
15 | }
16 |
17 | .section.section-break {
18 | margin-bottom: @line-height-computed;
19 | .box-shadow(~"0 -1px rgba(0, 0, 0, 0.08), 0 1px rgba(0, 0, 0, 0.07), 0 1px 1px rgba(0, 0, 0, 0.05)");
20 | }
21 |
22 | .section-default {
23 | background-color: @label-default-bg;
24 | color: @label-color;
25 | }
26 |
27 | .section-primary {
28 | background-color: @label-primary-bg;
29 | color: @label-color;
30 | }
31 |
32 | .section-success {
33 | background-color: @label-success-bg;
34 | color: @label-color;
35 | }
36 |
37 | .section-info {
38 | background-color: @label-info-bg;
39 | color: @label-color;
40 | }
41 |
42 | .section-warning {
43 | background-color: @label-warning-bg;
44 | color: @label-color;
45 | }
46 |
47 | .section-danger {
48 | background-color: @label-danger-bg;
49 | color: @label-color;
50 | }
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mobile-angular-ui",
3 | "homepage": "https://github.com/mcasimir/mobile-angular-ui",
4 | "authors": [
5 | "mcasimir "
6 | ],
7 | "version": "1.2.0",
8 | "description": "Mobile UI for Bootstrap 3 and Angular JS",
9 | "main": [
10 | "dist/css/mobile-angular-ui-hover.css",
11 | "dist/css/mobile-angular-ui-base.css",
12 | "dist/css/mobile-angular-ui-desktop.css",
13 | "dist/js/mobile-angular-ui.js",
14 | "dist/js/mobile-angular-ui.gestures.js",
15 | "dist/fonts/fontawesome-webfont.eot",
16 | "dist/fonts/fontawesome-webfont.svg",
17 | "dist/fonts/fontawesome-webfont.ttf",
18 | "dist/fonts/fontawesome-webfont.woff"
19 | ],
20 | "keywords": [
21 | "mobile-angular-ui",
22 | "angularjs",
23 | "boostrap3",
24 | "angular-mobile",
25 | "mobile-ui",
26 | "boostrap3-mobile"
27 | ],
28 | "license": "MIT",
29 | "ignore": [
30 | "**/.*",
31 | "node_modules",
32 | "bower_components",
33 | "test",
34 | "ROADMAP.tasks"
35 | ],
36 | "devDependencies": {
37 | "font-awesome": "4",
38 | "bootstrap": "3",
39 | "fastclick": "1",
40 | "overthrow": "0.7"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/demo/forms.html:
--------------------------------------------------------------------------------
1 |
2 | Forms
3 |
4 |
5 |
6 |
7 |
8 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/src/js/mobile-angular-ui.core.js:
--------------------------------------------------------------------------------
1 | /**
2 |
3 | @module mobile-angular-ui.core
4 |
5 | @description
6 |
7 | It has all the core functionalities of Mobile Angular UI. It aims to act as a common base
8 | for an UI framework providing services and directives to create components and implement
9 | UI interactions with angular.
10 |
11 |
12 | NOTE
13 |
14 |
It has no dependency on Bootstrap.
15 |
It is not related to mobile apps only.
16 |
It is not requiring CSS support.
17 |
You can use it on any Angular Application and with any CSS framework.
42 |
--------------------------------------------------------------------------------
/src/js/mobile-angular-ui.gestures.js:
--------------------------------------------------------------------------------
1 | /**
2 | @module mobile-angular-ui.gestures
3 | @memberof!
4 | @position 100
5 | @description
6 |
7 | It has directives and services to support `touch`, `swipe` and `drag` gestures.
8 |
9 | It does not need any `.css` to work.
10 |
11 |
12 |
13 | This module will not work with `ngTouch` cause it is intended, among offering more features, to be a drop-in replacement for it.
14 |
15 |
16 | Be aware that `ngTouch` is still not playing well with `fastclick.js` and its usage with `mobile-angular-ui` is currently discouraged anyway.
17 |
10 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
11 |
12 |
13 | Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
14 |
15 |
16 | Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
17 |
18 |
19 | Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
20 |
21 |
22 | Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis.
23 |
24 |
25 | At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/ROADMAP.tasks:
--------------------------------------------------------------------------------
1 | /*=================================================
2 | = MOBILE ANGULAR UI ROADMAP =
3 | =================================================*/
4 |
5 | This file will contain an extremely detailed tasks list
6 | to follow development of new releases.
7 |
8 | Unscheduled:
9 | ❑ Look forward to use typescript/closure compiler or similar to perform maximum compression/optimization and structure sources better
10 | ❑ Support basic material design principles and visual feedbacks
11 |
12 |
13 | Unscheduled 1.2:
14 | ❑ Support ms devices through -ms- touch property
15 | For IE11+, you can use touch-action: manipulation; to disable double-tap-to-zoom on certain elements (like links and buttons). For IE10 use -ms-touch-action: manipulation
16 | ❑ Define multiple states in `ui-state`
17 | ❑ Make shared states exportable to QP
18 | ❑ Fix migration for `ui-switch`
19 | ❑ Fix $transform issues decomposing previous rotations
20 | ❑ Check for: https://github.com/scottjehl/device-bugs/issues/2 !!!
21 | ❑ Fix or invalidate old ui-switch issues:
22 | ❑ https://github.com/mcasimir/mobile-angular-ui/issues/92
23 | ❑ https://github.com/mcasimir/mobile-angular-ui/issues/87
24 | ❑ https://github.com/mcasimir/mobile-angular-ui/issues/86
25 | ❑ https://github.com/mcasimir/mobile-angular-ui/issues/85
26 |
27 | --- ✄ -----------------------
28 |
29 | 1.2.0.beta.11 (gestures and gulp):
30 |
31 | ✔ switch should work with $drag if present @done (15-01-26 19:47)
32 |
33 | ✔ $swipe @done (15-01-26 13:45)
34 | ✔ Allow for nested $swipe @done (15-01-18 23:03)
35 | ✔ Fix compatibility between $swipe and FastClick @done (15-01-19 00:09)
36 | ✔ move $touch's touch.direction to touch.angle and use touch.direction for 'LEFT', 'RIGHT', 'UP' and 'DOWN' @done (15-01-04 07:34)
37 |
38 | ✔ NOBOUNCE @done (15-01-26 13:03)
39 | ✔ Use nobounce tricks to avoid IOS bouncing after scroll or dragging @done (15-01-19 22:32)
40 | ✔ Move nobounce to directive @done (15-01-26 13:03)
41 |
42 | ✔ $touch @done (15-01-26 13:45)
43 | ✔ multiple $touch bindings should not interfere each other @done (15-01-19 08:43)
44 | ✔ detect multitouch and handle or prevent them @done (15-01-26 13:45)
45 |
46 | ✔ Add tests for $touch and $drag, adapt those for $swipe and $transform @done (15-01-23 22:01)
47 | ✔ Make it simpler to deal with ng-repeat and multiple shared states @done (15-01-23 22:01)
48 | ✔ check for SVG compatibility (remove HTMLElement references and use something more generic) @done (14-12-30 17:04)
49 | ✔ $touch should accept bounduaries either as rect {top, bottom, left, right} or element @done (14-12-30 17:02)
50 | ✔ Add { area: rectOrElement } to options @done (14-12-30 17:02)
51 | ✔ it should treat element and window the same way (or should we use element.ownerDocument instead?) @done (14-12-30 17:02)
52 | ✔ remove option for allowing outer movement (should be the default) @done (14-12-30 17:02)
53 | ✔ $drag.TRANSLATE_INSIDE(elem) should work @done (14-12-30 07:42)
54 | ✔ fix carousel in demo @done (14-12-30 07:42)
55 | ✔ Better detect swipes using direction and velocity @done (14-12-31 00:45)
56 | ✔ Switch to gulp @done (15-01-02 00:39)
57 | ✔ add gestures to bower.json @done (15-01-02 00:39)
58 | ✔ remove tests and any other unnecessary files from bower.json @done (15-01-02 00:44)
59 | ✔ release should take care of npm too @done (15-01-02 00:44)
60 | ✔ simplify testing @done (15-01-04 07:31)
61 | ✔ a test shoudl be an .html file only @done (15-01-04 07:32)
62 | ✔ inspect angular simpler @done (15-01-04 07:33)
63 | ✔ selectively filter tests by id or name @done (15-01-04 07:33)
64 | ✔ automatically ensure console is free from errors @done (15-01-04 09:09)
--------------------------------------------------------------------------------
/test/manual/fastclick.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
17 |
18 |
19 |
20 |
21 |
22 |
68 |
69 |
70 |
Layer A responds to click events normally, which on iOS will introduce a 300ms delay.
71 |
Layer B is enhanced with FastClick, and will fire the click handler with no delay.
72 |
The layers will behave normally on platforms that don't support touch events.
79 |
80 |
81 |
82 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/src/js/core/touchmoveDefaults.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Provides directives and service to prevent touchmove default behaviour
3 | * for touch devices (ie. bounce on overscroll in IOS).
4 | *
5 | * #### Usage
6 | *
7 | * Use `ui-prevent-touchmove-defaults` directive on root element of your app:
8 | *
9 | * ``` html
10 | *
11 | *
12 | *
13 | * ```
14 | *
15 | * Doing so `touchmove.preventDefault` logic for inner elements is inverted,
16 | * so any `touchmove` default behaviour is automatically prevented.
17 | *
18 | * If you wish to allow the default behaviour, for example to allow
19 | * inner elements to scroll, you have to explicitly mark an event to allow
20 | * touchmove default.
21 | *
22 | * Mobile Angular UI already handles this for `scrollable` elements, so you don't have
23 | * to do anything in order to support scroll.
24 | *
25 | * If you wish to allow touchmove defaults for certain element under certain conditions
26 | * you can use the `allowTouchmoveDefault` service.
27 | *
28 | * ie.
29 | *
30 | * ``` js
31 | * // always allow touchmove default for an element
32 | * allowTouchmoveDefault(myelem);
33 | * ```
34 | *
35 | * ``` js
36 | * // allow touchmove default for an element only under certain conditions
37 | * allowTouchmoveDefault(myelem, function(touchmove){
38 | * return touchmove.pageY > 100;
39 | * });
40 | * ```
41 | *
42 | * @module mobile-angular-ui.core.touchmoveDefaults
43 | */
44 | (function () {
45 | 'use strict';
46 | var module = angular.module('mobile-angular-ui.core.touchmoveDefaults', []);
47 |
48 | module.directive('uiPreventTouchmoveDefaults', function() {
49 | var preventTouchmoveDefaultsCb = function(e) {
50 | if (e.allowTouchmoveDefault !== true) {
51 | e.preventDefault();
52 | }
53 | };
54 |
55 | return {
56 | compile: function(element) {
57 | if ('ontouchmove' in document) {
58 | element.on('touchmove', preventTouchmoveDefaultsCb);
59 | }
60 | }
61 | };
62 | });
63 |
64 | /**
65 | * Bind a listener to an element to allow `touchmove` default behaviour
66 | * when `touchmove` happens inside the bound element.
67 | *
68 | * You can also provide a function to decide when to allow and
69 | * when to prevent it.
70 | *
71 | * ``` js
72 | * // always allow touchmove default
73 | * allowTouchmoveDefault(myelem);
74 | *
75 | * // allow touchmove default only under certain conditions
76 | * allowTouchmoveDefault(myelem, function(touchmove){
77 | * return touchmove.pageY > 100;
78 | * });
79 | * ```
80 | *
81 | * @param {Element|$element} element The element to bind.
82 | * @param {function} condition A `function(touchmove)⟶boolean` to decide
83 | * whether to allow default behavior or not.
84 | *
85 | * @service allowTouchmoveDefault
86 | * @as function
87 | * @returns function Function to unbind the listener
88 | */
89 |
90 | module.factory('allowTouchmoveDefault', function(){
91 | var fnTrue = function() { return true; };
92 |
93 | if ('ontouchmove' in document) {
94 | return function($element, condition) {
95 | condition = condition || fnTrue;
96 |
97 | var allowTouchmoveDefaultCallback = function(e) {
98 | if (condition(e)) { e.allowTouchmoveDefault = true; }
99 | };
100 |
101 | $element = angular.element($element);
102 | $element.on('touchmove', allowTouchmoveDefaultCallback);
103 |
104 | $element.on('$destroy', function() {
105 | $element.off('touchmove', allowTouchmoveDefaultCallback);
106 | $element = null;
107 | });
108 |
109 | return function() {
110 | if ($element) {
111 | $element.off('touchmove', allowTouchmoveDefaultCallback);
112 | }
113 | };
114 | };
115 | } else {
116 | return angular.noop;
117 | }
118 | });
119 |
120 | }());
--------------------------------------------------------------------------------
/src/js/components/navbars.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @module mobile-angular-ui.components.navbars
3 | * @description
4 | *
5 | * Bootstrap default navbars are awesome for responsive websites, but are not the
6 | * best with a small screen. Also fixed positioning is yet not an option to create
7 | * navbars standing in top or bottom of the screen.
8 | *
9 | * Mobile Angular Ui offers an alternative to bootstrap navbars that is more
10 | * suitable for mobile.
11 | *
12 | * It uses scrollable areas to avoid scroll issues. In the following figure you can
13 | * see the difference between fixed navbars and navbars with absolute positioning.
14 | *
15 | *
16 | *
17 | *
18 | *
19 | * Here is the basic markup to achieve this.
20 | *
21 | * ``` html
22 | *
23 | *
24 | *
25 | *
26 | *
27 | *
28 | *
29 | *
30 | *
31 | *
32 | *
33 | *
34 | *
35 | * ```
36 | *
37 | * As you can notice the base class is `.navbar-app` while the positioning is
38 | * obtained adding either `.navbar-absolute-top` or `.navbar-absolute-bottom`
39 | * class.
40 | *
41 | * ### Mobile Navbar Layout
42 | *
43 | * Top navbar in mobile design most of the times follows a clear pattern: a
44 | * centered title surrounded by one or two action buttons, the _back_ or the
45 | * _menu_ buttons are two common examples.
46 | *
47 | * Twitter Bootstrap ships with a different arrangement of components for navbars
48 | * since they are supposed to host an horizontal navigation menu.
49 | *
50 | * `.navbar-app` is specifically designed to support this different type of
51 | * `.interaction and arrangement.
52 | *
53 | * Consider the following example:
54 | *
55 | * ``` html
56 | *
57 | *
58 | *
59 | * Navbar Brand
60 | *
61 | *
62 | *
63 | *
64 | * Left Action
65 | *
66 | *
67 | *
68 | *
69 | *
70 | * Right Action
71 | *
72 | *
73 | *
74 | *
75 | * ```
76 | *
77 | * `.navbar-brand-center` is a specialization of BS3's `.navbar-brand`. It will
78 | * render the title centered and below the two button groups. Note that `.navbar-
79 | * brand-center` will position the title with absolute positioning ensuring that
80 | * it will never cover the buttons, which would cause interaction problems.
81 | *
82 | */
83 |
84 | (function() {
85 | 'use strict';
86 |
87 | var module = angular.module('mobile-angular-ui.components.navbars', []);
88 |
89 | /**
90 | * @directive navbarAbsoluteTop
91 | * @restrict C
92 | * @description
93 | *
94 | * Setup absolute positioned top navbar.
95 | *
96 | * ``` html
97 | *
114 | * ```
115 | */
116 | angular.forEach(['top', 'bottom'], function(side) {
117 | var directiveName = 'navbarAbsolute' + side.charAt(0).toUpperCase() + side.slice(1);
118 | module.directive(directiveName, [
119 | '$rootElement',
120 | function($rootElement) {
121 | return {
122 | restrict: 'C',
123 | link: function(scope) {
124 | $rootElement.addClass('has-navbar-' + side);
125 | scope.$on('$destroy', function(){
126 | $rootElement.removeClass('has-navbar-' + side);
127 | });
128 | }
129 | };
130 | }
131 | ]);
132 | });
133 |
134 | })();
--------------------------------------------------------------------------------
/src/js/components/modals.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This module will provide directives to create modals and overlays components.
3 | *
4 | * @module mobile-angular-ui.components.modals
5 | */
6 | (function () {
7 | 'use strict';
8 | angular.module('mobile-angular-ui.components.modals', [])
9 |
10 | /**
11 | * A directive to create modals and overlays components.
12 | *
13 | * Modals are basically the same of Bootstrap 3 but you have to use uiState
14 | * with `ngIf/uiIf` or `ngHide/uiHide` to `activate/dismiss` it.
15 | *
16 | * By default both modals and overlay are made always showing up by
17 | * css rule `.modal {display:block}`, so you can use it with
18 | * `ngAnimate` and other angular directives in a simpler way.
19 | *
20 | * Overlay are a style of modal that looks more native in mobile devices providing a blurred
21 | * overlay as background.
22 | *
23 | * You can create an overlay adding `.modal-overlay` class to a modal.
24 | *
25 | * ### Note
26 | *
27 | * For modals and overlays to cover the entire page you have to attach them
28 | * as child of `body` element. To achieve this from a view is a common use for
29 | * `contentFor/yieldTo` directives contained from
30 | * [capture module](/docs/module:mobile-angular-ui/module:core/module:capture):
31 | *
32 | * ``` html
33 | *
34 | *
35 | *
36 | *
37 | *
38 | *
39 | *
40 | * ```
41 | *
42 | * Then you can wrap your modals and overlays in `contentFor`:
43 | *
44 | * ``` html
45 | *
",link:function(e,a,l){a.on("click tap",function(){(null===l.disabled||void 0===l.disabled)&&(e.model=!e.model,e.$apply(),null!==e.changeExpr&&void 0!==e.changeExpr&&e.$parent.$eval(e.changeExpr))}),a.addClass("switch-transition-enabled")}}})}(),function(){"use strict";var e=angular.module("mobile-angular-ui.migrate.toggle",["mobile-angular-ui.core.sharedState"]);e.directive("toggle",["SharedState",function(e){return{restrict:"A",link:function(a,l,n){var t=(n.exclusionGroup,n.toggle||"toggle"),r=void 0!==n.bubble&&"false"!==n.bubble,i=n.activeClass,o=n.inactiveClass,s=n.parentActiveClass,u=n.parentInactiveClass,c=l.parent(),d=n.target;if(!d&&n.href&&(d=n.href.slice(1)),!d)throw new Error('Toggle directive requires "target" attribute to be set. If you are using toggleByClass yet be aware that is not supported by migration version of toggle.\nPlease switch to ui-* directives instead.');var g=function(e){e?(s&&c.addClass(s),i&&l.addClass(i),u&&c.removeClass(u),o&&l.removeClass(o)):(s&&c.removeClass(s),i&&l.removeClass(i),u&&c.addClass(u),o&&l.addClass(o))};a.$on("mobile-angular-ui.state.changed."+d,function(e,a){g(a)}),g(e.get("id")),l.on("click tap",function(l){return a.$$phase||a.$apply(function(){"on"===t?e.turnOn(d):"off"===t?e.turnOff(d):e.toggle(d)}),r?!0:(l.preventDefault(),!1)})}}}]),e.directive("toggleable",["SharedState","$rootScope",function(e,a){return{restrict:"A",link:function(l,n,t){var r=t.exclusionGroup,i="active"===t.default,o=t.activeClass,s=t.inactiveClass,u=t.parentActiveClass,c=t.parentInactiveClass,d=n.parent(),g=t.toggleable||t.id;l.$on("mobile-angular-ui.state.changed."+g,function(e,l){l?(u&&d.addClass(u),o&&n.addClass(o),c&&d.removeClass(c),s&&n.removeClass(s)):(u&&d.removeClass(u),o&&n.removeClass(o),c&&d.addClass(c),s&&n.addClass(s)),a.$emit("mobile-angular-ui.toggle.toggled",g,l,r)}),e.initialize(l,g,{defaultValue:i,exclusionGroup:r})}}}]),e.run(["$rootScope","SharedState",function(e,a){e.toggle=function(e,l){"on"===l?a.turnOn(e):"off"===l?a.turnOff(e):a.toggle(e)}}])}(),function(){"use strict";angular.module("mobile-angular-ui.migrate",["mobile-angular-ui.migrate.toggle","mobile-angular-ui.migrate.forms","mobile-angular-ui.migrate.panels","mobile-angular-ui.migrate.disabled","mobile-angular-ui.migrate.overlay","mobile-angular-ui.migrate.carousel","mobile-angular-ui.migrate.namespaceAliases","mobile-angular-ui.migrate.switch"])}();
2 | //# sourceMappingURL=mobile-angular-ui.migrate.min.js.map
--------------------------------------------------------------------------------
/src/js/core/capture.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @module mobile-angular-ui.core.capture
3 | * @description
4 | *
5 | * The `capture` module exposes directives to let you extract markup which can be used in other parts of a template using `uiContentFor` and `uiYieldTo` directives.
6 | *
7 | * It provides a way to move or clone a block of markup to other parts of the document.
8 | *
9 | * This method is particularly useful to setup parts of the layout within an angular view. Since blocks of html are transplanted within their original `$scope` is easy to create layout interactions depending on the context. Some tipical task you can accomplish with these directives are: _setup the navbar title depending on the view_ or _place a submit button for a form inside a navbar_.
10 | *
11 | * ## Usage
12 | *
13 | * Declare it as a dependency to your app unless you have already included some of its super-modules.
14 | *
15 | * ```
16 | * angular.module('myApp', ['mobile-angular-ui']);
17 | * ```
18 | *
19 | * Or
20 | *
21 | * ```
22 | * angular.module('myApp', ['mobile-angular-ui']);
23 | * ```
24 | *
25 | * Or
26 | *
27 | * ```
28 | * angular.module('myApp', ['mobile-angular-ui.core.capture']);
29 | * ```
30 | *
31 | * Use `ui-yield-to` as a placeholder.
32 | *
33 | * ``` html
34 | *
35 | *
36 | *
37 | *
38 | * Default Title
39 | *
40 | *
41 | *
42 | *
43 | *
44 | *
45 | * ```
46 | *
47 | * Use `ui-content-for` inside any view to populate the `ui-yield-to` content.
48 | *
49 | * ``` html
50 | *
51 | *
52 | *
53 | * My View Title
54 | *
55 | * ```
56 | *
57 | * Since the original scope is preserved you can use directives inside `ui-content-for` blocks to interact with the current scope. In the following example we will add a navbar button to submit a form inside a nested view.
58 | *
59 | *
60 | * ``` html
61 | *
62 | *
63 | *
64 | *
65 | *
66 | *
67 | *
68 | *
69 | *
70 | *
71 | * ```
72 | *
73 | * ``` html
74 | *
75 | *
76 | *
89 | * ```
90 | *
91 | * ``` javascript
92 | * app.controller('newCustomerController', function($scope, Store){
93 | * $scope.customer = {};
94 | * $scope.createCustomer = function(){
95 | * Store.create($scope.customer);
96 | * // ...
97 | * }
98 | * });
99 | * ```
100 | *
101 | * If you wish you can also duplicate markup instead of move it. Just add `duplicate` parameter to `uiContentFor` directive to specify this behaviour.
102 | *
103 | * ``` html
104 | *
105 | *
108 | *
109 | * ```
110 | */
111 | (function () {
112 | 'use strict';
113 |
114 | angular.module('mobile-angular-ui.core.capture', [])
115 |
116 | .run([
117 | 'Capture',
118 | '$rootScope',
119 | function(Capture, $rootScope) {
120 | $rootScope.$on('$routeChangeStart', function() {
121 | Capture.resetAll();
122 | });
123 | }
124 | ])
125 |
126 | .factory('Capture', [
127 | '$compile',
128 | function($compile) {
129 | var yielders = {};
130 |
131 | return {
132 | resetAll: function() {
133 | for (var name in yielders) {
134 | if (yielders.hasOwnProperty(name)) {
135 | this.resetYielder(name);
136 | }
137 | }
138 | },
139 |
140 | resetYielder: function(name) {
141 | var b = yielders[name];
142 | this.setContentFor(name, b.defaultContent, b.defaultScope);
143 | },
144 |
145 | putYielder: function(name, element, defaultScope, defaultContent) {
146 | var yielder = {};
147 | yielder.name = name;
148 | yielder.element = element;
149 | yielder.defaultContent = defaultContent || '';
150 | yielder.defaultScope = defaultScope;
151 | yielders[name] = yielder;
152 | },
153 |
154 | getYielder: function(name) {
155 | return yielders[name];
156 | },
157 |
158 | removeYielder: function(name) {
159 | delete yielders[name];
160 | },
161 |
162 | setContentFor: function(name, content, scope) {
163 | var b = yielders[name];
164 | if (!b) {
165 | return;
166 | }
167 | b.element.html(content);
168 | $compile(b.element.contents())(scope);
169 | }
170 |
171 | };
172 | }
173 | ])
174 |
175 | /**
176 | * @directive uiContentFor
177 | * @restrict A
178 | * @description
179 | *
180 | * `ui-content-for` makes inner contents to replace the corresponding
181 | * `ui-yield-to` placeholder contents.
182 | *
183 | * `uiContentFor` is intended to be used inside a view in order to populate outer placeholders.
184 | * Any content you send to placeholders via `ui-content-for` is
185 | * reverted to placeholder defaults after view changes (ie. on `$routeChangeStart`).
186 | *
187 | * @param {string} uiContentFor The id of the placeholder to be replaced
188 | * @param {boolean} uiDuplicate If present duplicates the content instead of moving it (default to `false`)
189 | *
190 | */
191 | .directive('uiContentFor', [
192 | 'Capture',
193 | function(Capture) {
194 | return {
195 | compile: function(tElem, tAttrs) {
196 | var rawContent = tElem.html();
197 | if(tAttrs.uiDuplicate === null || tAttrs.uiDuplicate === undefined) {
198 | // no need to compile anything!
199 | tElem.html('');
200 | tElem.remove();
201 | }
202 | return function(scope, elem, attrs) {
203 | Capture.setContentFor(attrs.uiContentFor, rawContent, scope);
204 | };
205 | }
206 | };
207 | }
208 | ])
209 |
210 | /**
211 | * @directive uiYieldTo
212 | * @restrict A
213 | * @description
214 | *
215 | * `ui-yield-to` defines a placeholder which contents will be further replaced by `ui-content-for` directive.
216 | *
217 | * Inner html is considered to be a default. Default is restored any time `$routeChangeStart` happens.
218 | *
219 | * @param {string} uiYieldTo The unique id of this placeholder.
220 | *
221 | */
222 | .directive('uiYieldTo', [
223 | '$compile', 'Capture', function($compile, Capture) {
224 | return {
225 | link: function(scope, element, attr) {
226 | Capture.putYielder(attr.uiYieldTo, element, scope, element.html());
227 |
228 | element.on('$destroy', function(){
229 | Capture.removeYielder(attr.uiYieldTo);
230 | });
231 |
232 | scope.$on('$destroy', function(){
233 | Capture.removeYielder(attr.uiYieldTo);
234 | });
235 | }
236 | };
237 | }
238 | ]);
239 |
240 | }());
--------------------------------------------------------------------------------
/dist/css/mobile-angular-ui-hover.min.css:
--------------------------------------------------------------------------------
1 | a:hover{outline:0;color:#0055b3;text-decoration:underline}a.text-primary:hover{color:#0062cc}a.text-success:hover{color:#2b542c}a.text-info:hover{color:#245269}a.text-warning:hover{color:#66512c}a.text-danger:hover{color:#843534}a.bg-primary:hover{background-color:#0062cc}a.bg-success:hover{background-color:#c1e2b3}a.bg-info:hover{background-color:#afd9ee}a.bg-warning:hover{background-color:#f7ecb5}a.bg-danger:hover{background-color:#e4b9b9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.btn:hover{color:#333;text-decoration:none}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-primary:hover{color:#fff;background-color:#0062cc;border-color:#0051a8}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover{background-color:#007aff;border-color:#006ee6}.btn-success:hover{color:#fff;background-color:#2ac845;border-color:#24aa3b}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover{background-color:#4cd964;border-color:#37d552}.btn-info:hover{color:#fff;background-color:#218ebd;border-color:#1b779e}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover{background-color:#34aadc;border-color:#249ed2}.btn-warning:hover{color:#fff;background-color:#cca300;border-color:#a88700}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover{background-color:#fc0;border-color:#e6b800}.btn-danger:hover{color:#fff;background-color:#fc0d00;border-color:#d80b00}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover{background-color:#ff3b30;border-color:#ff2317}.btn-link:hover{border-color:transparent;color:#0055b3;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.dropdown-menu>li>a:hover{text-decoration:none;color:#262626;background-color:#f5f5f5}.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;outline:0;background-color:#007aff}.dropdown-menu>.disabled>a:hover{color:#777;text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);cursor:not-allowed}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.input-group-btn>.btn:hover{z-index:2}.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a:hover{color:#777;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a:hover{background-color:#eee;border-color:#007aff}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a:hover{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}.nav-pills>li.active>a:hover{color:#fff;background-color:#007aff}.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}.navbar-brand:hover{text-decoration:none}@media (max-width:767px){.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}.navbar-default .navbar-brand:hover{color:#0062cc;background-color:transparent}.navbar-default .navbar-nav>li>a:hover{color:#006ee6;background-color:transparent}.navbar-default .navbar-nav>.active>a:hover{color:#1a87ff;background-color:#e6e6e6}.navbar-default .navbar-nav>.disabled>a:hover{color:#66afff;background-color:transparent}.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-nav>.open>a:hover{background-color:#e6e6e6;color:#1a87ff}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#006ee6;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#1a87ff;background-color:#e6e6e6}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#66afff;background-color:transparent}}.navbar-default .navbar-link:hover,.navbar-default .btn-link:hover{color:#006ee6}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover{color:#66afff}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-nav>.open>a:hover{background-color:#080808;color:#fff}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link:hover,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .disabled>a:hover{color:#777;background-color:#fff;cursor:not-allowed}a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label-default[href]:hover{background-color:#5e5e5e}.label-primary[href]:hover{background-color:#0062cc}.label-success[href]:hover{background-color:#2ac845}.label-info[href]:hover{background-color:#218ebd}.label-warning[href]:hover{background-color:#cca300}.label-danger[href]:hover{background-color:#fc0d00}a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}a.thumbnail:hover{border-color:#007aff}a.list-group-item:hover{text-decoration:none;color:#555;background-color:#f5f5f5}.list-group-item.disabled:hover{background-color:#eee;color:#777;cursor:not-allowed}.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active:hover{z-index:2;color:#fff;background-color:#007aff;border-color:#007aff}.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>.small{color:inherit}.list-group-item.active:hover .list-group-item-text{color:#cce4ff}a.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}a.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}a.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}a.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.close:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}.carousel-control:hover{outline:0;color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.btn.active:hover{-webkit-box-shadow:none;box-shadow:none}.btn-group>.btn-default.active:hover{color:#fff;background-color:#007aff;border-color:#006ee6}.navbar-app .btn-navbar:hover,.navbar-app .btn:hover{color:#007aff}
--------------------------------------------------------------------------------
/src/js/components/scrollable.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @module mobile-angular-ui.components.scrollable
3 | * @description
4 | *
5 | * One thing you'll always have to deal with approaching mobile web app development is scroll and `position:fixed` bugs.
6 | *
7 | * Due to the lack of support in some devices fixed positioned elements may bounce or disappear during scroll. Also mobile interaction often leverages horizontal scroll eg. in carousels or sliders.
8 | *
9 | * We use `overflow:auto` to create scrollable areas and solve any problems related to scroll.
10 | *
11 | * Since `overflow:auto` is not always available in touch devices we use [Overthrow](http://filamentgroup.github.io/Overthrow/) to polyfill that.
12 | *
13 | * Markup for any scrollable areas is as simple as:
14 | *
15 | * ``` html
16 | *
17 | *
...
18 | *
19 | * ```
20 | *
21 | * This piece of code will trigger a directive that properly setup a new `Overthrow` instance for the `.scrollable` node.
22 | *
23 | * #### Headers and footers
24 | *
25 | * `.scrollable-header/.scrollable-footer` can be used to add fixed header/footer to a scrollable area without having to deal with css height and positioning to avoid breaking scroll.
26 | *
27 | * ``` html
28 | *
29 | *
30 | *
31 | *
32 | *
33 | * ```
34 | *
35 | * #### scrollTo
36 | *
37 | * `.scrollable-content` controller exposes a `scrollTo` function: `scrollTo(offsetOrElement, margin)`
38 | *
39 | * You have to require it in your directives to use it or obtain through `element().controller`:
40 | *
41 | * ``` js
42 | * var elem = element(document.getElementById('myScrollableContent'));
43 | * var scrollableContentController = elem.controller('scrollableContent');
44 | *
45 | * // - Scroll to top of containedElement
46 | * scrollableContentController.scrollTo(containedElement);
47 | *
48 | * // - Scroll to top of containedElement with a margin of 10px;
49 | * scrollableContentController.scrollTo(containedElement, 10);
50 | *
51 | * // - Scroll top by 200px;
52 | * scrollableContentController.scrollTo(200);
53 | * ```
54 | *
55 | * #### `ui-scroll-bottom/ui-scroll-top`
56 | *
57 | * You can use `ui-scroll-bottom/ui-scroll-top` directives handle that events and implement features like _infinite scroll_.
58 | *
59 | * ``` html
60 | *