This commit is contained in:
Max Lynch
2013-08-28 15:29:42 -05:00
parent b62a42aa6a
commit b024e66812
24 changed files with 26018 additions and 1987 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
.DS_Store
*.sw[mnpcod]
example/cordova/iOS/www/js/framework
example/cordova/iOS/www/js/framework
.sass-cache/

View File

@ -6,6 +6,6 @@ release:
@sass -I scss/ scss/framework.scss:dist/framework.css
cordova:
@cp js/framework/*.js example/cordova/iOS/www/js
@cp -R js/ example/cordova/iOS/www/js
@cp dist/framework.css example/cordova/iOS/www/css

View File

@ -221,26 +221,36 @@ a.list-item {
margin-bottom: 0;
line-height: 1.3; }
/* the container of panel content to show */
.panel-content {
display: block;
width: 17em;
[data-panel] {
display: none;
min-height: 100%;
max-height: 100%;
position: absolute;
top: 0;
z-index: 0; }
.panel-active {
display: block;
width: 270px; }
header, main, footer {
z-index: 100;
left: 0;
right: 0;
-webkit-transition: -webkit-transform 200ms ease;
-moz-transition: -moz-transform 200ms ease;
transition: transform 200ms ease;
-webkit-transition: -webkit-transform 300ms ease;
-moz-transition: -moz-transform 300ms ease;
transition: transform 300ms ease;
-webkit-transform: translate3d(0, 0, 0);
-moz-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0); }
.panel-opened > section > header,
.panel-opened > section > main,
.panel-opened > section > footer {
-webkit-transform: translate3d(270px, 0, 0);
-moz-transform: translate3d(270px, 0, 0);
transform: translate3d(270px, 0, 0); }
.ptr-capable {
-webkit-user-drag: element; }
@ -524,7 +534,7 @@ main {
a.list-item {
color: #333333; }
.panel-content {
[data-panel] {
background: #eeeeee;
border-right: 1px solid #bbbbbb; }

View File

@ -7,12 +7,21 @@
<link rel="stylesheet" href="css/framework.css">
<script src="js/angular-1.2.0rc1.min.js"></script>
<script src="js/angular-touch.js"></script>
<script src="js/app.js"></script>
<script src="js/framework/framework-panel.js"></script>
<script src="js/framework/glue/angular-touch.js"></script>
</head>
<body ng-app="Chat">
<section class="panel-page-container">
<section id="this-panel" class="panel-content" ng-class="{'panel-open': isPanelShowing}" ng-controller="MenuCtrl">
</section>
<panel id="this-panel" class="panel-content" ng-class="{'panel-open': isPanelShowing}" ng-controller="MenuCtrl">
<ul class="list">
<li class="list-item">
Max Lynch
</li>
</ul>
</panel>
<section ng-controller="RoomsCtrl">
@ -31,14 +40,6 @@
</section>
</section>
<script src="js/app.js"></script>
<!--
<script src="js/framework-gestures.js"></script>
<script src="js/framework-events.js"></script>
-->
<script src="js/framework-panel.js"></script>
</body>
</html>

View File

@ -0,0 +1,536 @@
/**
* @license AngularJS v1.2.0rc1
* (c) 2010-2012 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {'use strict';
/**
* @ngdoc overview
* @name ngTouch
* @description
* Touch events and other mobile helpers.
* Based on jQuery Mobile touch event handling (jquerymobile.com)
*/
// define ngTouch module
var ngTouch = angular.module('ngTouch', []);
/**
* @ngdoc object
* @name ngTouch.$swipe
*
* @description
* The `$swipe` service is a service that abstracts the messier details of hold-and-drag swipe
* behavior, to make implementing swipe-related directives more convenient.
*
* It is used by the `ngSwipeLeft` and `ngSwipeRight` directives in `ngTouch`, and by
* `ngCarousel` in a separate component.
*
* # Usage
* The `$swipe` service is an object with a single method: `bind`. `bind` takes an element
* which is to be watched for swipes, and an object with four handler functions. See the
* documentation for `bind` below.
*/
ngTouch.factory('$swipe', [function() {
// The total distance in any direction before we make the call on swipe vs. scroll.
var MOVE_BUFFER_RADIUS = 10;
function getCoordinates(event) {
var touches = event.touches && event.touches.length ? event.touches : [event];
var e = (event.changedTouches && event.changedTouches[0]) ||
(event.originalEvent && event.originalEvent.changedTouches &&
event.originalEvent.changedTouches[0]) ||
touches[0].originalEvent || touches[0];
return {
x: e.clientX,
y: e.clientY
};
}
return {
/**
* @ngdoc method
* @name ngTouch.$swipe#bind
* @methodOf ngTouch.$swipe
*
* @description
* The main method of `$swipe`. It takes an element to be watched for swipe motions, and an
* object containing event handlers.
*
* The four events are `start`, `move`, `end`, and `cancel`. `start`, `move`, and `end`
* receive as a parameter a coordinates object of the form `{ x: 150, y: 310 }`.
*
* `start` is called on either `mousedown` or `touchstart`. After this event, `$swipe` is
* watching for `touchmove` or `mousemove` events. These events are ignored until the total
* distance moved in either dimension exceeds a small threshold.
*
* Once this threshold is exceeded, either the horizontal or vertical delta is greater.
* - If the horizontal distance is greater, this is a swipe and `move` and `end` events follow.
* - If the vertical distance is greater, this is a scroll, and we let the browser take over.
* A `cancel` event is sent.
*
* `move` is called on `mousemove` and `touchmove` after the above logic has determined that
* a swipe is in progress.
*
* `end` is called when a swipe is successfully completed with a `touchend` or `mouseup`.
*
* `cancel` is called either on a `touchcancel` from the browser, or when we begin scrolling
* as described above.
*
*/
bind: function(element, eventHandlers) {
// Absolute total movement, used to control swipe vs. scroll.
var totalX, totalY;
// Coordinates of the start position.
var startCoords;
// Last event's position.
var lastPos;
// Whether a swipe is active.
var active = false;
element.on('touchstart mousedown', function(event) {
startCoords = getCoordinates(event);
active = true;
totalX = 0;
totalY = 0;
lastPos = startCoords;
eventHandlers['start'] && eventHandlers['start'](startCoords);
});
element.on('touchcancel', function(event) {
active = false;
eventHandlers['cancel'] && eventHandlers['cancel']();
});
element.on('touchmove mousemove', function(event) {
if (!active) return;
// Android will send a touchcancel if it thinks we're starting to scroll.
// So when the total distance (+ or - or both) exceeds 10px in either direction,
// we either:
// - On totalX > totalY, we send preventDefault() and treat this as a swipe.
// - On totalY > totalX, we let the browser handle it as a scroll.
if (!startCoords) return;
var coords = getCoordinates(event);
totalX += Math.abs(coords.x - lastPos.x);
totalY += Math.abs(coords.y - lastPos.y);
lastPos = coords;
if (totalX < MOVE_BUFFER_RADIUS && totalY < MOVE_BUFFER_RADIUS) {
return;
}
// One of totalX or totalY has exceeded the buffer, so decide on swipe vs. scroll.
if (totalY > totalX) {
// Allow native scrolling to take over.
active = false;
eventHandlers['cancel'] && eventHandlers['cancel']();
return;
} else {
// Prevent the browser from scrolling.
event.preventDefault();
eventHandlers['move'] && eventHandlers['move'](coords);
}
});
element.on('touchend mouseup', function(event) {
if (!active) return;
active = false;
eventHandlers['end'] && eventHandlers['end'](getCoordinates(event));
});
}
};
}]);
/**
* @ngdoc directive
* @name ngTouch.directive:ngClick
*
* @description
* A more powerful replacement for the default ngClick designed to be used on touchscreen
* devices. Most mobile browsers wait about 300ms after a tap-and-release before sending
* the click event. This version handles them immediately, and then prevents the
* following click event from propagating.
*
* This directive can fall back to using an ordinary click event, and so works on desktop
* browsers as well as mobile.
*
* This directive also sets the CSS class `ng-click-active` while the element is being held
* down (by a mouse click or touch) so you can restyle the depressed element if you wish.
*
* @element ANY
* @param {expression} ngClick {@link guide/expression Expression} to evaluate
* upon tap. (Event object is available as `$event`)
*
* @example
<doc:example>
<doc:source>
<button ng-click="count = count + 1" ng-init="count=0">
Increment
</button>
count: {{ count }}
</doc:source>
</doc:example>
*/
ngTouch.config(['$provide', function($provide) {
$provide.decorator('ngClickDirective', ['$delegate', function($delegate) {
// drop the default ngClick directive
$delegate.shift();
return $delegate;
}]);
}]);
ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
function($parse, $timeout, $rootElement) {
var TAP_DURATION = 750; // Shorter than 750ms is a tap, longer is a taphold or drag.
var MOVE_TOLERANCE = 12; // 12px seems to work in most mobile browsers.
var PREVENT_DURATION = 2500; // 2.5 seconds maximum from preventGhostClick call to click
var CLICKBUSTER_THRESHOLD = 25; // 25 pixels in any dimension is the limit for busting clicks.
var ACTIVE_CLASS_NAME = 'ng-click-active';
var lastPreventedTime;
var touchCoordinates;
// TAP EVENTS AND GHOST CLICKS
//
// Why tap events?
// Mobile browsers detect a tap, then wait a moment (usually ~300ms) to see if you're
// double-tapping, and then fire a click event.
//
// This delay sucks and makes mobile apps feel unresponsive.
// So we detect touchstart, touchmove, touchcancel and touchend ourselves and determine when
// the user has tapped on something.
//
// What happens when the browser then generates a click event?
// The browser, of course, also detects the tap and fires a click after a delay. This results in
// tapping/clicking twice. So we do "clickbusting" to prevent it.
//
// How does it work?
// We attach global touchstart and click handlers, that run during the capture (early) phase.
// So the sequence for a tap is:
// - global touchstart: Sets an "allowable region" at the point touched.
// - element's touchstart: Starts a touch
// (- touchmove or touchcancel ends the touch, no click follows)
// - element's touchend: Determines if the tap is valid (didn't move too far away, didn't hold
// too long) and fires the user's tap handler. The touchend also calls preventGhostClick().
// - preventGhostClick() removes the allowable region the global touchstart created.
// - The browser generates a click event.
// - The global click handler catches the click, and checks whether it was in an allowable region.
// - If preventGhostClick was called, the region will have been removed, the click is busted.
// - If the region is still there, the click proceeds normally. Therefore clicks on links and
// other elements without ngTap on them work normally.
//
// This is an ugly, terrible hack!
// Yeah, tell me about it. The alternatives are using the slow click events, or making our users
// deal with the ghost clicks, so I consider this the least of evils. Fortunately Angular
// encapsulates this ugly logic away from the user.
//
// Why not just put click handlers on the element?
// We do that too, just to be sure. The problem is that the tap event might have caused the DOM
// to change, so that the click fires in the same position but something else is there now. So
// the handlers are global and care only about coordinates and not elements.
// Checks if the coordinates are close enough to be within the region.
function hit(x1, y1, x2, y2) {
return Math.abs(x1 - x2) < CLICKBUSTER_THRESHOLD && Math.abs(y1 - y2) < CLICKBUSTER_THRESHOLD;
}
// Checks a list of allowable regions against a click location.
// Returns true if the click should be allowed.
// Splices out the allowable region from the list after it has been used.
function checkAllowableRegions(touchCoordinates, x, y) {
for (var i = 0; i < touchCoordinates.length; i += 2) {
if (hit(touchCoordinates[i], touchCoordinates[i+1], x, y)) {
touchCoordinates.splice(i, i + 2);
return true; // allowable region
}
}
return false; // No allowable region; bust it.
}
// Global click handler that prevents the click if it's in a bustable zone and preventGhostClick
// was called recently.
function onClick(event) {
if (Date.now() - lastPreventedTime > PREVENT_DURATION) {
return; // Too old.
}
var touches = event.touches && event.touches.length ? event.touches : [event];
var x = touches[0].clientX;
var y = touches[0].clientY;
// Work around desktop Webkit quirk where clicking a label will fire two clicks (on the label
// and on the input element). Depending on the exact browser, this second click we don't want
// to bust has either (0,0) or negative coordinates.
if (x < 1 && y < 1) {
return; // offscreen
}
// Look for an allowable region containing this click.
// If we find one, that means it was created by touchstart and not removed by
// preventGhostClick, so we don't bust it.
if (checkAllowableRegions(touchCoordinates, x, y)) {
return;
}
// If we didn't find an allowable region, bust the click.
event.stopPropagation();
event.preventDefault();
// Blur focused form elements
event.target && event.target.blur();
}
// Global touchstart handler that creates an allowable region for a click event.
// This allowable region can be removed by preventGhostClick if we want to bust it.
function onTouchStart(event) {
var touches = event.touches && event.touches.length ? event.touches : [event];
var x = touches[0].clientX;
var y = touches[0].clientY;
touchCoordinates.push(x, y);
$timeout(function() {
// Remove the allowable region.
for (var i = 0; i < touchCoordinates.length; i += 2) {
if (touchCoordinates[i] == x && touchCoordinates[i+1] == y) {
touchCoordinates.splice(i, i + 2);
return;
}
}
}, PREVENT_DURATION, false);
}
// On the first call, attaches some event handlers. Then whenever it gets called, it creates a
// zone around the touchstart where clicks will get busted.
function preventGhostClick(x, y) {
if (!touchCoordinates) {
$rootElement[0].addEventListener('click', onClick, true);
$rootElement[0].addEventListener('touchstart', onTouchStart, true);
touchCoordinates = [];
}
lastPreventedTime = Date.now();
checkAllowableRegions(touchCoordinates, x, y);
}
// Actual linking function.
return function(scope, element, attr) {
var clickHandler = $parse(attr.ngClick),
tapping = false,
tapElement, // Used to blur the element after a tap.
startTime, // Used to check if the tap was held too long.
touchStartX,
touchStartY;
function resetState() {
tapping = false;
element.removeClass(ACTIVE_CLASS_NAME);
}
element.on('touchstart', function(event) {
tapping = true;
tapElement = event.target ? event.target : event.srcElement; // IE uses srcElement.
// Hack for Safari, which can target text nodes instead of containers.
if(tapElement.nodeType == 3) {
tapElement = tapElement.parentNode;
}
element.addClass(ACTIVE_CLASS_NAME);
startTime = Date.now();
var touches = event.touches && event.touches.length ? event.touches : [event];
var e = touches[0].originalEvent || touches[0];
touchStartX = e.clientX;
touchStartY = e.clientY;
});
element.on('touchmove', function(event) {
resetState();
});
element.on('touchcancel', function(event) {
resetState();
});
element.on('touchend', function(event) {
var diff = Date.now() - startTime;
var touches = (event.changedTouches && event.changedTouches.length) ? event.changedTouches :
((event.touches && event.touches.length) ? event.touches : [event]);
var e = touches[0].originalEvent || touches[0];
var x = e.clientX;
var y = e.clientY;
var dist = Math.sqrt( Math.pow(x - touchStartX, 2) + Math.pow(y - touchStartY, 2) );
if (tapping && diff < TAP_DURATION && dist < MOVE_TOLERANCE) {
// Call preventGhostClick so the clickbuster will catch the corresponding click.
preventGhostClick(x, y);
// Blur the focused element (the button, probably) before firing the callback.
// This doesn't work perfectly on Android Chrome, but seems to work elsewhere.
// I couldn't get anything to work reliably on Android Chrome.
if (tapElement) {
tapElement.blur();
}
if (!angular.isDefined(attr.disabled) || attr.disabled === false) {
element.triggerHandler('click', event);
}
}
resetState();
});
// Hack for iOS Safari's benefit. It goes searching for onclick handlers and is liable to click
// something else nearby.
element.onclick = function(event) { };
// Actual click handler.
// There are three different kinds of clicks, only two of which reach this point.
// - On desktop browsers without touch events, their clicks will always come here.
// - On mobile browsers, the simulated "fast" click will call this.
// - But the browser's follow-up slow click will be "busted" before it reaches this handler.
// Therefore it's safe to use this directive on both mobile and desktop.
element.on('click', function(event) {
scope.$apply(function() {
clickHandler(scope, {$event: event});
});
});
element.on('mousedown', function(event) {
element.addClass(ACTIVE_CLASS_NAME);
});
element.on('mousemove mouseup', function(event) {
element.removeClass(ACTIVE_CLASS_NAME);
});
};
}]);
/**
* @ngdoc directive
* @name ngTouch.directive:ngSwipeLeft
*
* @description
* Specify custom behavior when an element is swiped to the left on a touchscreen device.
* A leftward swipe is a quick, right-to-left slide of the finger.
* Though ngSwipeLeft is designed for touch-based devices, it will work with a mouse click and drag too.
*
* @element ANY
* @param {expression} ngSwipeLeft {@link guide/expression Expression} to evaluate
* upon left swipe. (Event object is available as `$event`)
*
* @example
<doc:example>
<doc:source>
<div ng-show="!showActions" ng-swipe-left="showActions = true">
Some list content, like an email in the inbox
</div>
<div ng-show="showActions" ng-swipe-right="showActions = false">
<button ng-click="reply()">Reply</button>
<button ng-click="delete()">Delete</button>
</div>
</doc:source>
</doc:example>
*/
/**
* @ngdoc directive
* @name ngTouch.directive:ngSwipeRight
*
* @description
* Specify custom behavior when an element is swiped to the right on a touchscreen device.
* A rightward swipe is a quick, left-to-right slide of the finger.
* Though ngSwipeRight is designed for touch-based devices, it will work with a mouse click and drag too.
*
* @element ANY
* @param {expression} ngSwipeRight {@link guide/expression Expression} to evaluate
* upon right swipe. (Event object is available as `$event`)
*
* @example
<doc:example>
<doc:source>
<div ng-show="!showActions" ng-swipe-left="showActions = true">
Some list content, like an email in the inbox
</div>
<div ng-show="showActions" ng-swipe-right="showActions = false">
<button ng-click="reply()">Reply</button>
<button ng-click="delete()">Delete</button>
</div>
</doc:source>
</doc:example>
*/
function makeSwipeDirective(directiveName, direction, eventName) {
ngTouch.directive(directiveName, ['$parse', '$swipe', function($parse, $swipe) {
// The maximum vertical delta for a swipe should be less than 75px.
var MAX_VERTICAL_DISTANCE = 75;
// Vertical distance should not be more than a fraction of the horizontal distance.
var MAX_VERTICAL_RATIO = 0.3;
// At least a 30px lateral motion is necessary for a swipe.
var MIN_HORIZONTAL_DISTANCE = 30;
return function(scope, element, attr) {
var swipeHandler = $parse(attr[directiveName]);
var startCoords, valid;
function validSwipe(coords) {
// Check that it's within the coordinates.
// Absolute vertical distance must be within tolerances.
// Horizontal distance, we take the current X - the starting X.
// This is negative for leftward swipes and positive for rightward swipes.
// After multiplying by the direction (-1 for left, +1 for right), legal swipes
// (ie. same direction as the directive wants) will have a positive delta and
// illegal ones a negative delta.
// Therefore this delta must be positive, and larger than the minimum.
if (!startCoords) return false;
var deltaY = Math.abs(coords.y - startCoords.y);
var deltaX = (coords.x - startCoords.x) * direction;
return valid && // Short circuit for already-invalidated swipes.
deltaY < MAX_VERTICAL_DISTANCE &&
deltaX > 0 &&
deltaX > MIN_HORIZONTAL_DISTANCE &&
deltaY / deltaX < MAX_VERTICAL_RATIO;
}
$swipe.bind(element, {
'start': function(coords) {
startCoords = coords;
valid = true;
},
'cancel': function() {
valid = false;
},
'end': function(coords) {
if (validSwipe(coords)) {
scope.$apply(function() {
element.triggerHandler(eventName);
swipeHandler(scope);
});
}
}
});
};
}]);
}
// Left is negative X-coordinate, right is positive.
makeSwipeDirective('ngSwipeLeft', -1, 'swipeleft');
makeSwipeDirective('ngSwipeRight', 1, 'swiperight');
})(window, window.angular);

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,23 @@
var chat = angular.module('Chat', ['ngTouch']);
chat.controller('MenuCtrl', function($scope) {
chat.service('MenuService', function() {
this.openPanel = function() {
console.log('Opening panel');
}
});
chat.controller('RoomsCtrl', function($scope) {
chat.controller('MenuCtrl', function($scope) {
$scope.isPanelShowing = false;
});
chat.controller('RoomsCtrl', function($scope, MenuService) {
$scope.rooms = [
{ name: 'All', key: 'all' },
{ name: 'Marketing', key: 'marketing' }
];
$scope.openPanel = function() {
$scope.isPanelShowing = true;
MenuService.openPanel();
};
$scope.showAlert = function() {

View File

@ -1,29 +0,0 @@
(function(window, document, framework) {
framework.Button = function() {}
// Process an the touchstart event and if this is a button,
// add the .active class so Android will show depressed
// button states.
framework.Button.prototype._onTouchStart = function(event) {
console.log('Touch start!', event);
if(event.target && event.target.classList.contains('button')) {
event.target.classList.add('active');
}
};
// Remove any active state on touch end/cancel/etc.
framework.Button.prototype._onTouchEnd = function(event) {
console.log('Touch end!', event);
if(event.target && event.target.classList.contains('button')) {
event.target.classList.remove('active');
}
// TODO: Process the click? Set flag to not process other click events
};
document.addEventListener('touchstart', framework.Button.prototype._onTouchStart);
document.addEventListener('touchend', framework.Button.prototype._onTouchEnd);
document.addEventListener('touchcancel', framework.Button.prototype._onTouchEnd);
})(this, document, FM = this.FM || {});

View File

@ -1,98 +0,0 @@
/**
* framework-events.js
*
* Author: Max Lynch <max@drifty.com>
*
* Framework events handles various mobile browser events, and
* detects special events like tap/swipe/etc. and emits them
* as custom events that can be used in an app.
*
* Portions lovingly adapted from github.com/maker/ratchet and github.com/alexgibson/tap.js - thanks guys!
*/
(function(window, document, framework) {
framework.EventController = {
// Trigger a new event
trigger: function(eventType, data) {
// TODO: Do we need to use the old-school createEvent stuff?
var event = new CustomEvent(eventType, data);
// Make sure to trigger the event on the given target, or dispatch it from
// the window if we don't have an event target
data.target && data.target.dispatchEvent(event) || window.dispatchEvent(event);
},
// Bind an event
on: function(type, callback, element) {
var e = element || window;
e.addEventListener(type, callback);
},
off: function(type, callback, element) {
element.removeEventListener(type, callback);
},
// Register for a new gesture event on the given element
onGesture: function(type, callback, element) {
var gesture = new framework.Gesture(element);
gesture.on(type, callback);
return gesture;
},
// Unregister a previous gesture event
offGesture: function(gesture, type, callback) {
gesture.off(type, callback);
},
// With a click event, we need to check the target
// and if it's an internal target that doesn't want
// a click, cancel it
handleClick: function(e) {
var target = e.target;
if(framework.Gestures.HAS_TOUCHEVENTS) {
// We don't allow any clicks on mobile
e.preventDefault();
return false;
}
if (
! target
|| e.which > 1
|| e.metaKey
|| e.ctrlKey
//|| isScrolling
|| location.protocol !== target.protocol
|| location.host !== target.host
// Not sure abotu this one
//|| !target.hash && /#/.test(target.href)
|| target.hash && target.href.replace(target.hash, '') === location.href.replace(location.hash, '')
//|| target.getAttribute('data-ignore') == 'push'
) {
// Allow it
return;
}
// We need to cancel this one
e.preventDefault();
},
handlePopState: function(event) {
console.log("EVENT: popstate", event);
},
};
// Map some convenient top-level functions for event handling
framework.on = framework.EventController.on;
framework.off = framework.EventController.off;
framework.trigger = framework.EventController.trigger;
framework.onGesture = framework.EventController.onGesture;
framework.offGesture = framework.EventController.offGesture;
// Set up various listeners
window.addEventListener('click', framework.EventController.handleClick);
//window.addEventListener('popstate', framework.EventController.handlePopState);
})(this, document, FM = this.FM || {});

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +0,0 @@
(function(window, document, framework) {
function initalize() {
// remove the ready listeners
document.removeEventListener( "DOMContentLoaded", initalize, false );
window.removeEventListener( "load", initalize, false );
// trigger that the DOM is ready
framework.trigger("ready");
// trigger that the start page is in view
framework.trigger("pageview");
// trigger that the webapp has been initalized
framework.trigger("initalized");
}
// When the DOM is ready, initalize the webapp
if ( document.readyState === "complete" ) {
// DOM is already ready
setTimeout( initalize );
} else {
// DOM isn't ready yet, add event listeners
document.addEventListener( "DOMContentLoaded", initalize, false );
window.addEventListener( "load", initalize, false );
}
})(this, document, FM = this.FM || {});

View File

@ -1,23 +0,0 @@
(function(window, document, framework) {
framework.List = function() {}
framework.List.prototype._TAB_ITEM_CLASS = 'tab-item';
framework.List.prototype._onTouchStart = function(event) {
console.log('Touch start!', event);
if(event.target && event.target.parentNode.classList.contains(this._TAB_ITEM_CLASS)) {
event.target.classList.add('active');
}
};
framework.List.prototype._onTouchEnd = function(event) {
console.log('Touch end!', event);
if(event.target && event.target.parentNode.classList.contains(this._TAB_ITEM_CLASS)) {
event.target.classList.remove('active');
}
};
document.addEventListener('mousedown', framework.List.prototype._onTouchStart);
document.addEventListener('touchstart', framework.List.prototype._onTouchStart);
document.addEventListener('touchend', framework.List.prototype._onTouchEnd);
})(this, document, FM = this.FM || {});

View File

@ -1,154 +0,0 @@
(function(window, document, location, framework) {
var
x,
el;
// Add listeners to each link in the document
function click(e) {
// an element has been clicked. If its a link its good to go
// if its not a link then jump up its parents until you find
// its wrapping link. If you never find a link do nothing.
if(e.target) {
el = e.target;
if(el.tagName === "A") {
return linkClick(e, el);
}
while(el.parentElement) {
el = el.parentElement;
if(el.tagName === "A") {
return linkClick(e, el);
}
}
}
}
// A link has been clicked
function linkClick(e, el) {
// if they clicked a link while scrolling don't nav to it
if(framework.isScrolling) {
e.preventDefault();
return false;
}
// data-history-go="-1"
// shortcut if they just want to use window.history.go()
if(el.dataset.historyGo) {
window.history.go( parseInt(el.dataset.historyGo, 10) );
e.preventDefault();
return false;
}
// only intercept the nav click if they're going to the same domain or page
if (location.protocol === el.protocol && location.host === el.host) {
// trigger the event that a new page should be shown
framework.trigger("pageinit", el.href);
// decide how to handle this click depending on the href
if(el.getAttribute("href").indexOf("#") === 0) {
// this click is going to another element within this same page
} else {
// this click is going to another page in the same domain
requestData({
url: el.href,
success: successPageLoad,
fail: failedPageLoad
});
}
// stop the browser itself from continuing on with this click
// the above code will take care of the navigation
e.preventDefault();
return false;
}
}
function locationChange(e) {
if(!this._initPopstate) {
this._initPopstate = true;
return;
}
requestData({
url: location.href,
success: successPageLoad,
fail: failedPageLoad
});
}
function requestData(options) {
var xhr = new XMLHttpRequest();
xhr.open('GET', options.url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if(xhr.status === 200) {
options.success(xhr, options);
} else {
options.fail(xhr, options);
}
}
};
xhr.send();
}
function successPageLoad(xhr, options) {
var data = parseXHR(xhr, options);
framework.trigger("pageloaded", {
data: data
});
history.pushState({}, data.title, data.url);
document.title = data.title;
}
function failedPageLoad(xhr, options) {
framework.trigger("pageinitfailed", {
responseText: xhr.responseText,
responseStatus: xhr.status
});
}
function parseXHR(xhr, options) {
var
container,
tmp,
data = {};
data.url = options.url;
if (!xhr.responseText) return data;
container = document.createElement('div');
container.innerHTML = xhr.responseText;
// get the title of the page
tmp = container.querySelector("title");
if(tmp) {
data.title = tmp.innerText;
} else {
data.title = data.url;
}
// get the main content of the page
tmp = container.querySelector("main");
if(tmp) {
data.main = tmp.innerHTML;
} else {
// something is wrong with the data, trigger that the page init failed
framework.trigger("pageinitfailed");
}
return data;
}
// listen to every click
document.addEventListener("click", click, false);
// listen to when the location changes
framework.on("popstate", locationChange);
})(this, document, location, FM = this.FM || {});

View File

@ -1,56 +0,0 @@
(function(window, document, framework) {
var
el,
styleElement,
isPanelOpen;
function onTap(e) {
var el = e.target;
return togglePanel(e, el, el.dataset.togglePanel);
if(e.target) {
if(el.dataset.togglePanel) {
}
while(el.parentElement) {
el = el.parentElement;
if(el.dataset.togglePanel) {
return togglePanel(e, el, el.dataset.togglePanel);
}
}
}
}
function togglePanel(e, el, panelName) {
var styles = "";
if(isPanelOpen) {
// there is a panel already open, so close it
isPanelOpen = false;
} else {
// open the panel
styles = "section .panel-content ~ * { width:" + document.width + "px !important; \
-webkit-transform: translate3d(17em,0,0); \
-moz-transform: translate3d(17em,0,0); \
transform: translate3d(17em,0,0); }";
isPanelOpen = true;
}
setStyles(styles);
}
function setStyles(styles) {
// get the <style> from the head that will be used by the panel
if(!styleElement) {
styleElement = document.createElement("style");
styleElement.id = "panel-styles";
styleElement.innerHTML = styles;
document.head.appendChild(styleElement);
} else {
styleElement.innerHTML = styles;
}
}
//framework.onGesture("tap", onTap, document.getElementById('open-panel'));
})(this, document, FM = this.FM || {});

View File

@ -1,25 +0,0 @@
'use strict';
(function(window, document, framework) {
framework.Tabs = function() {}
framework.Tabs.prototype._TAB_ITEM_CLASS = 'tab-item';
framework.Tabs.prototype._onTouchStart = function(event) {
console.log('Touch start!', event);
if(event.target && event.target.parentNode.classList.contains(this._TAB_ITEM_CLASS)) {
event.target.classList.add('active');
}
};
framework.Tabs.prototype._onTouchEnd = function(event) {
console.log('Touch end!', event);
if(event.target && event.target.parentNode.classList.contains(this._TAB_ITEM_CLASS)) {
event.target.classList.remove('active');
}
};
document.addEventListener('mousedown', framework.Tabs.prototype._onTouchStart);
document.addEventListener('touchstart', framework.Tabs.prototype._onTouchStart);
document.addEventListener('touchend', framework.Tabs.prototype._onTouchEnd);
})(this, document, FM = this.FM || {});

View File

@ -1,68 +0,0 @@
'use strict';
(function(window, document, framework) {
// Loop through each element in the DOM and collect up all
// the templates it has. A template either has data to supply
// to others, or it needs data from another template
function initTemplates() {
var
x,
el,
tmp,
emptyTemplates = [],
container,
templateElements;
// collect up all the templates currently in the DOM
templateElements = document.body.querySelectorAll("[data-template]");
for(x=0; x<templateElements.length; x++) {
el = templateElements[x];
if(el.dataset.template && !el.tSet) {
// this element is either supplying template
// data or it needs to be filled with template data
if(el.innerHTML == "") {
// this element is requesting to have its innerHTML
// built from a template already set
emptyTemplates.push(el);
} else {
// this element contains innerHTML which should be used
// as a template for other elements. Save this template
// data for future use.
// Save only in sessionStorage, which maintains a storage area that's
// available for the duration of the page session. A page session
// lasts for as long as the browser is open and survives over page
// reloads and restores. Opening a page in a new tab or window will
// cause a new session to be initiated.
sessionStorage.setItem("t:" + el.dataset.template, el.outerHTML);
}
// remember that this is set so we don't bother doing all this
// code again for the same element in the future
el.tSet = true;
}
}
// go through each empty template and build it up with existing template data
for(x=0; x<emptyTemplates.length; x++) {
el = emptyTemplates[x];
tmp = sessionStorage.getItem("t:" + el.dataset.template);
if(tmp) {
// we've got template data, plug it into this element's innerHTML
container = document.createElement("div");
container.innerHTML = tmp;
el.parentNode.replaceChild(container.children[0].cloneNode(true), el);
}
}
}
framework.on("ready", initTemplates);
framework.on("pagecreate", initTemplates);
})(this, document, FM = this.FM || {});

View File

@ -1,41 +0,0 @@
(function(window, document, framework) {
function initTransitions(e) {
var data = e.detail.data;
// build a new main element to hold the new html
var newMainElement = document.createElement("main");
newMainElement.innerHTML = data.main;
// get the old main element, which will be the first one
var oldMainElement = document.querySelector("main");
// decide how to do the page transition
if(data.transition === "slide-from-left") {
slideStart(newMainElement, oldMainElement, "left");
} else {
// No animation. Nothing fancy here
noTransition(newMainElement, oldMainElement, data);
}
}
function noTransition(newMainElement, oldMainElement, data) {
// entirely replace the old element, no transition
oldMainElement.parentNode.replaceChild(newMainElement, oldMainElement);
framework.trigger("pagecreate", {
url: data.url,
title: data.title
});
framework.trigger("pageview");
}
function slideStart(newMainElement, oldMainElement, fromDirection) {
// copy what the main element currently looks like into a document fragment
// make all the changes to the document fragment, then replace the
// old main with the two new ones. Both the old and new main will be
// in the DOM, but their CSS classes will do the transitioning for us
}
framework.on("pageloaded", initTransitions);
})(this, document, FM = this.FM || {});

View File

@ -1,22 +0,0 @@
(function(window, document, framework) {
framework.Utils = {
/**
* extend method,
* also used for cloning when dest is an empty object
* @param {Object} dest
* @param {Object} src
* @parm {Boolean} merge do a merge
* @returns {Object} dest
*/
extend: function extend(dest, src, merge) {
for (var key in src) {
if(dest[key] !== undefined && merge) {
continue;
}
dest[key] = src[key];
}
return dest;
},
}
})(this, document, FM = this.FM || {});

View File

@ -0,0 +1,12 @@
/* used to tie angular.js with the framework */
/* nowhere should the framework reference angular.js */
/* nowhere in angular.js should it reference the framework */
var ionic = angular.module('ionic', ['ngTouch']);
ionic.directive('panel', ['$parse', '$timeout', '$rootElement',
function($parse, $timeout, $rootElement) {
return function(scope, element, attrs) {
};
}
]);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,741 @@
/**
* ==================================
* Ratchet v1.0.0
* Licensed under The MIT License
* http://opensource.org/licenses/MIT
* ==================================
*/
/* ----------------------------------
* POPOVER v1.0.0
* Licensed under The MIT License
* http://opensource.org/licenses/MIT
* ---------------------------------- */
!function () {
var popover;
var findPopovers = function (target) {
var i, popovers = document.querySelectorAll('a');
for (; target && target !== document; target = target.parentNode) {
for (i = popovers.length; i--;) { if (popovers[i] === target) return target; }
}
};
var onPopoverHidden = function () {
document.body.removeChild(backdrop);
popover.style.display = 'none';
popover.removeEventListener('webkitTransitionEnd', onPopoverHidden);
}
var backdrop = function () {
var element = document.createElement('div');
element.classList.add('backdrop');
element.addEventListener('touchend', function () {
popover.addEventListener('webkitTransitionEnd', onPopoverHidden);
popover.classList.remove('visible');
});
return element;
}();
var getPopover = function (e) {
var anchor = findPopovers(e.target);
if (!anchor || !anchor.hash) return;
popover = document.querySelector(anchor.hash);
if (!popover || !popover.classList.contains('popover')) return;
return popover;
}
window.addEventListener('touchend', function (e) {
var popover = getPopover(e);
if (!popover) return;
popover.style.display = 'block';
popover.offsetHeight;
popover.classList.add('visible');
popover.parentNode.appendChild(backdrop);
});
window.addEventListener('click', function (e) { if (getPopover(e)) e.preventDefault(); });
}();
/* ----------------------------------
* PUSH v1.0.0
* Licensed under The MIT License
* inspired by chris's jquery.pjax.js
* http://opensource.org/licenses/MIT
* ---------------------------------- */
!function () {
var noop = function () {};
// Pushstate cacheing
// ==================
var isScrolling;
var maxCacheLength = 20;
var cacheMapping = sessionStorage;
var domCache = {};
var transitionMap = {
'slide-in' : 'slide-out',
'slide-out' : 'slide-in',
'fade' : 'fade'
};
var bars = {
bartab : '.bar-tab',
bartitle : '.bar-title',
barfooter : '.bar-footer',
barheadersecondary : '.bar-header-secondary'
}
var cacheReplace = function (data, updates) {
PUSH.id = data.id;
if (updates) data = getCached(data.id);
cacheMapping[data.id] = JSON.stringify(data);
window.history.replaceState(data.id, data.title, data.url);
domCache[data.id] = document.body.cloneNode(true);
};
var cachePush = function () {
var id = PUSH.id;
var cacheForwardStack = JSON.parse(cacheMapping.cacheForwardStack || '[]');
var cacheBackStack = JSON.parse(cacheMapping.cacheBackStack || '[]');
cacheBackStack.push(id);
while (cacheForwardStack.length) delete cacheMapping[cacheForwardStack.shift()];
while (cacheBackStack.length > maxCacheLength) delete cacheMapping[cacheBackStack.shift()];
window.history.pushState(null, '', cacheMapping[PUSH.id].url);
cacheMapping.cacheForwardStack = JSON.stringify(cacheForwardStack);
cacheMapping.cacheBackStack = JSON.stringify(cacheBackStack);
};
var cachePop = function (id, direction) {
var forward = direction == 'forward';
var cacheForwardStack = JSON.parse(cacheMapping.cacheForwardStack || '[]');
var cacheBackStack = JSON.parse(cacheMapping.cacheBackStack || '[]');
var pushStack = forward ? cacheBackStack : cacheForwardStack;
var popStack = forward ? cacheForwardStack : cacheBackStack;
if (PUSH.id) pushStack.push(PUSH.id);
popStack.pop();
cacheMapping.cacheForwardStack = JSON.stringify(cacheForwardStack);
cacheMapping.cacheBackStack = JSON.stringify(cacheBackStack);
};
var getCached = function (id) {
return JSON.parse(cacheMapping[id] || null) || {};
};
var getTarget = function (e) {
var target = findTarget(e.target);
if (
! target
|| e.which > 1
|| e.metaKey
|| e.ctrlKey
|| isScrolling
|| location.protocol !== target.protocol
|| location.host !== target.host
|| !target.hash && /#/.test(target.href)
|| target.hash && target.href.replace(target.hash, '') === location.href.replace(location.hash, '')
|| target.getAttribute('data-ignore') == 'push'
) return;
return target;
};
// Main event handlers (touchend, popstate)
// ==========================================
var touchend = function (e) {
var target = getTarget(e);
if (!target) return;
e.preventDefault();
PUSH({
url : target.href,
hash : target.hash,
timeout : target.getAttribute('data-timeout'),
transition : target.getAttribute('data-transition')
});
};
var popstate = function (e) {
var key;
var barElement;
var activeObj;
var activeDom;
var direction;
var transition;
var transitionFrom;
var transitionFromObj;
var id = e.state;
if (!id || !cacheMapping[id]) return;
direction = PUSH.id < id ? 'forward' : 'back';
cachePop(id, direction);
activeObj = getCached(id);
activeDom = domCache[id];
if (activeObj.title) document.title = activeObj.title;
if (direction == 'back') {
transitionFrom = JSON.parse(direction == 'back' ? cacheMapping.cacheForwardStack : cacheMapping.cacheBackStack);
transitionFromObj = getCached(transitionFrom[transitionFrom.length - 1]);
} else {
transitionFromObj = activeObj;
}
if (direction == 'back' && !transitionFromObj.id) return PUSH.id = id;
transition = direction == 'back' ? transitionMap[transitionFromObj.transition] : transitionFromObj.transition;
if (!activeDom) {
return PUSH({
id : activeObj.id,
url : activeObj.url,
title : activeObj.title,
timeout : activeObj.timeout,
transition : transition,
ignorePush : true
});
}
if (transitionFromObj.transition) {
activeObj = extendWithDom(activeObj, '.content', activeDom.cloneNode(true));
for (key in bars) {
barElement = document.querySelector(bars[key])
if (activeObj[key]) swapContent(activeObj[key], barElement);
else if (barElement) barElement.parentNode.removeChild(barElement);
}
}
swapContent(
(activeObj.contents || activeDom).cloneNode(true),
document.querySelector('.content'),
transition
);
PUSH.id = id;
document.body.offsetHeight; // force reflow to prevent scroll
};
// Core PUSH functionality
// =======================
var PUSH = function (options) {
var key;
var data = {};
var xhr = PUSH.xhr;
options.container = options.container || options.transition ? document.querySelector('.content') : document.body;
for (key in bars) {
options[key] = options[key] || document.querySelector(bars[key]);
}
if (xhr && xhr.readyState < 4) {
xhr.onreadystatechange = noop;
xhr.abort()
}
xhr = new XMLHttpRequest();
xhr.open('GET', options.url, true);
xhr.setRequestHeader('X-PUSH', 'true');
xhr.onreadystatechange = function () {
if (options._timeout) clearTimeout(options._timeout);
if (xhr.readyState == 4) xhr.status == 200 ? success(xhr, options) : failure(options.url);
};
if (!PUSH.id) {
cacheReplace({
id : +new Date,
url : window.location.href,
title : document.title,
timeout : options.timeout,
transition : null
});
}
if (options.timeout) {
options._timeout = setTimeout(function () { xhr.abort('timeout'); }, options.timeout);
}
xhr.send();
if (xhr.readyState && !options.ignorePush) cachePush();
};
// Main XHR handlers
// =================
var success = function (xhr, options) {
var key;
var barElement;
var data = parseXHR(xhr, options);
if (!data.contents) return locationReplace(options.url);
if (data.title) document.title = data.title;
if (options.transition) {
for (key in bars) {
barElement = document.querySelector(bars[key])
if (data[key]) swapContent(data[key], barElement);
else if (barElement) barElement.parentNode.removeChild(barElement);
}
}
swapContent(data.contents, options.container, options.transition, function () {
cacheReplace({
id : options.id || +new Date,
url : data.url,
title : data.title,
timeout : options.timeout,
transition : options.transition
}, options.id);
triggerStateChange();
});
if (!options.ignorePush && window._gaq) _gaq.push(['_trackPageview']) // google analytics
if (!options.hash) return;
};
var failure = function (url) {
throw new Error('Could not get: ' + url)
};
// PUSH helpers
// ============
var swapContent = function (swap, container, transition, complete) {
var enter;
var containerDirection;
var swapDirection;
if (!transition) {
if (container) container.innerHTML = swap.innerHTML;
else if (swap.classList.contains('content')) document.body.appendChild(swap);
else document.body.insertBefore(swap, document.querySelector('.content'));
} else {
enter = /in$/.test(transition);
if (transition == 'fade') {
container.classList.add('in');
container.classList.add('fade');
swap.classList.add('fade');
}
if (/slide/.test(transition)) {
swap.classList.add(enter ? 'right' : 'left');
swap.classList.add('slide');
container.classList.add('slide');
}
container.parentNode.insertBefore(swap, container);
}
if (!transition) complete && complete();
if (transition == 'fade') {
container.offsetWidth; // force reflow
container.classList.remove('in');
container.addEventListener('webkitTransitionEnd', fadeContainerEnd);
function fadeContainerEnd() {
container.removeEventListener('webkitTransitionEnd', fadeContainerEnd);
swap.classList.add('in');
swap.addEventListener('webkitTransitionEnd', fadeSwapEnd);
}
function fadeSwapEnd () {
swap.removeEventListener('webkitTransitionEnd', fadeSwapEnd);
container.parentNode.removeChild(container);
swap.classList.remove('fade');
swap.classList.remove('in');
complete && complete();
}
}
if (/slide/.test(transition)) {
container.offsetWidth; // force reflow
swapDirection = enter ? 'right' : 'left'
containerDirection = enter ? 'left' : 'right'
container.classList.add(containerDirection);
swap.classList.remove(swapDirection);
swap.addEventListener('webkitTransitionEnd', slideEnd);
function slideEnd() {
swap.removeEventListener('webkitTransitionEnd', slideEnd);
swap.classList.remove('slide');
swap.classList.remove(swapDirection);
container.parentNode.removeChild(container);
complete && complete();
}
}
};
var triggerStateChange = function () {
var e = new CustomEvent('push', {
detail: { state: getCached(PUSH.id) },
bubbles: true,
cancelable: true
});
window.dispatchEvent(e);
};
var findTarget = function (target) {
var i, toggles = document.querySelectorAll('a');
for (; target && target !== document; target = target.parentNode) {
for (i = toggles.length; i--;) { if (toggles[i] === target) return target; }
}
};
var locationReplace = function (url) {
window.history.replaceState(null, '', '#');
window.location.replace(url);
};
var parseURL = function (url) {
var a = document.createElement('a'); a.href = url; return a;
};
var extendWithDom = function (obj, fragment, dom) {
var i;
var result = {};
for (i in obj) result[i] = obj[i];
Object.keys(bars).forEach(function (key) {
var el = dom.querySelector(bars[key]);
if (el) el.parentNode.removeChild(el);
result[key] = el;
});
result.contents = dom.querySelector(fragment);
return result;
};
var parseXHR = function (xhr, options) {
var head;
var body;
var data = {};
var responseText = xhr.responseText;
data.url = options.url;
if (!responseText) return data;
if (/<html/i.test(responseText)) {
head = document.createElement('div');
body = document.createElement('div');
head.innerHTML = responseText.match(/<head[^>]*>([\s\S.]*)<\/head>/i)[0]
body.innerHTML = responseText.match(/<body[^>]*>([\s\S.]*)<\/body>/i)[0]
} else {
head = body = document.createElement('div');
head.innerHTML = responseText;
}
data.title = head.querySelector('title');
data.title = data.title && data.title.innerText.trim();
if (options.transition) data = extendWithDom(data, '.content', body);
else data.contents = body;
return data;
};
// Attach PUSH event handlers
// ==========================
window.addEventListener('touchstart', function () { isScrolling = false; });
window.addEventListener('touchmove', function () { isScrolling = true; })
window.addEventListener('touchend', touchend);
window.addEventListener('click', function (e) { if (getTarget(e)) e.preventDefault(); });
window.addEventListener('popstate', popstate);
}();/* ----------------------------------
* TABS v1.0.0
* Licensed under The MIT License
* http://opensource.org/licenses/MIT
* ---------------------------------- */
!function () {
var getTarget = function (target) {
var i, popovers = document.querySelectorAll('.segmented-controller li a');
for (; target && target !== document; target = target.parentNode) {
for (i = popovers.length; i--;) { if (popovers[i] === target) return target; }
}
};
window.addEventListener("touchend", function (e) {
var activeTab;
var activeBody;
var targetBody;
var targetTab;
var className = 'active';
var classSelector = '.' + className;
var targetAnchor = getTarget(e.target);
if (!targetAnchor) return;
targetTab = targetAnchor.parentNode;
activeTab = targetTab.parentNode.querySelector(classSelector);
if (activeTab) activeTab.classList.remove(className);
targetTab.classList.add(className);
if (!targetAnchor.hash) return;
targetBody = document.querySelector(targetAnchor.hash);
if (!targetBody) return;
activeBody = targetBody.parentNode.querySelector(classSelector);
if (activeBody) activeBody.classList.remove(className);
targetBody.classList.add(className)
});
window.addEventListener('click', function (e) { if (getTarget(e.target)) e.preventDefault(); });
}();/* ----------------------------------
* SLIDER v1.0.0
* Licensed under The MIT License
* Adapted from Brad Birdsall's swipe
* http://opensource.org/licenses/MIT
* ---------------------------------- */
!function () {
var pageX;
var pageY;
var slider;
var deltaX;
var deltaY;
var offsetX;
var lastSlide;
var startTime;
var resistance;
var sliderWidth;
var slideNumber;
var isScrolling;
var scrollableArea;
var getSlider = function (target) {
var i, sliders = document.querySelectorAll('.slider ul');
for (; target && target !== document; target = target.parentNode) {
for (i = sliders.length; i--;) { if (sliders[i] === target) return target; }
}
}
var getScroll = function () {
var translate3d = slider.style.webkitTransform.match(/translate3d\(([^,]*)/);
return parseInt(translate3d ? translate3d[1] : 0)
};
var setSlideNumber = function (offset) {
var round = offset ? (deltaX < 0 ? 'ceil' : 'floor') : 'round';
slideNumber = Math[round](getScroll() / ( scrollableArea / slider.children.length) );
slideNumber += offset;
slideNumber = Math.min(slideNumber, 0);
slideNumber = Math.max(-(slider.children.length - 1), slideNumber);
}
var onTouchStart = function (e) {
slider = getSlider(e.target);
if (!slider) return;
var firstItem = slider.querySelector('li');
scrollableArea = firstItem.offsetWidth * slider.children.length;
isScrolling = undefined;
sliderWidth = slider.offsetWidth;
resistance = 1;
lastSlide = -(slider.children.length - 1);
startTime = +new Date;
pageX = e.touches[0].pageX;
pageY = e.touches[0].pageY;
setSlideNumber(0);
slider.style['-webkit-transition-duration'] = 0;
};
var onTouchMove = function (e) {
if (e.touches.length > 1 || !slider) return; // Exit if a pinch || no slider
deltaX = e.touches[0].pageX - pageX;
deltaY = e.touches[0].pageY - pageY;
pageX = e.touches[0].pageX;
pageY = e.touches[0].pageY;
if (typeof isScrolling == 'undefined') {
isScrolling = Math.abs(deltaY) > Math.abs(deltaX);
}
if (isScrolling) return;
offsetX = (deltaX / resistance) + getScroll();
e.preventDefault();
resistance = slideNumber == 0 && deltaX > 0 ? (pageX / sliderWidth) + 1.25 :
slideNumber == lastSlide && deltaX < 0 ? (Math.abs(pageX) / sliderWidth) + 1.25 : 1;
slider.style.webkitTransform = 'translate3d(' + offsetX + 'px,0,0)';
};
var onTouchEnd = function (e) {
if (!slider || isScrolling) return;
setSlideNumber(
(+new Date) - startTime < 1000 && Math.abs(deltaX) > 15 ? (deltaX < 0 ? -1 : 1) : 0
);
offsetX = slideNumber * sliderWidth;
slider.style['-webkit-transition-duration'] = '.2s';
slider.style.webkitTransform = 'translate3d(' + offsetX + 'px,0,0)';
e = new CustomEvent('slide', {
detail: { slideNumber: Math.abs(slideNumber) },
bubbles: true,
cancelable: true
});
slider.parentNode.dispatchEvent(e);
};
window.addEventListener('touchstart', onTouchStart);
window.addEventListener('touchmove', onTouchMove);
window.addEventListener('touchend', onTouchEnd);
}();
/* ----------------------------------
* TOGGLE v1.0.0
* Licensed under The MIT License
* http://opensource.org/licenses/MIT
* ---------------------------------- */
!function () {
var start = {};
var touchMove = false;
var distanceX = false;
var toggle = false;
var findToggle = function (target) {
var i, toggles = document.querySelectorAll('.toggle');
for (; target && target !== document; target = target.parentNode) {
for (i = toggles.length; i--;) { if (toggles[i] === target) return target; }
}
}
window.addEventListener('touchstart', function (e) {
e = e.originalEvent || e;
toggle = findToggle(e.target);
if (!toggle) return;
var handle = toggle.querySelector('.toggle-handle');
var toggleWidth = toggle.offsetWidth;
var handleWidth = handle.offsetWidth;
var offset = toggle.classList.contains('active') ? toggleWidth - handleWidth : 0;
start = { pageX : e.touches[0].pageX - offset, pageY : e.touches[0].pageY };
touchMove = false;
// todo: probably should be moved to the css
toggle.style['-webkit-transition-duration'] = 0;
});
window.addEventListener('touchmove', function (e) {
e = e.originalEvent || e;
if (e.touches.length > 1) return; // Exit if a pinch
if (!toggle) return;
var handle = toggle.querySelector('.toggle-handle');
var current = e.touches[0];
var toggleWidth = toggle.offsetWidth;
var handleWidth = handle.offsetWidth;
var offset = toggleWidth - handleWidth;
touchMove = true;
distanceX = current.pageX - start.pageX;
if (Math.abs(distanceX) < Math.abs(current.pageY - start.pageY)) return;
e.preventDefault();
if (distanceX < 0) return handle.style.webkitTransform = 'translate3d(0,0,0)';
if (distanceX > offset) return handle.style.webkitTransform = 'translate3d(' + offset + 'px,0,0)';
handle.style.webkitTransform = 'translate3d(' + distanceX + 'px,0,0)';
toggle.classList[(distanceX > (toggleWidth/2 - handleWidth/2)) ? 'add' : 'remove']('active');
});
window.addEventListener('touchend', function (e) {
if (!toggle) return;
var handle = toggle.querySelector('.toggle-handle');
var toggleWidth = toggle.offsetWidth;
var handleWidth = handle.offsetWidth;
var offset = toggleWidth - handleWidth;
var slideOn = (!touchMove && !toggle.classList.contains('active')) || (touchMove && (distanceX > (toggleWidth/2 - handleWidth/2)));
if (slideOn) handle.style.webkitTransform = 'translate3d(' + offset + 'px,0,0)';
else handle.style.webkitTransform = 'translate3d(0,0,0)';
toggle.classList[slideOn ? 'add' : 'remove']('active');
e = new CustomEvent('toggle', {
detail: { isActive: slideOn },
bubbles: true,
cancelable: true
});
toggle.dispatchEvent(e);
touchMove = false;
toggle = false;
});
}();

View File

@ -0,0 +1,42 @@
(function(window, document, framework) {
// this file should not be apart of the build
// its just just for testing that the correct
// events are being triggered and at the correct
// times, and so we don't have to hardcode/remove
// console calls throughout the code
framework.on('ready', function(){
console.log('ready');
});
framework.on('initalized', function(){
console.log('initalized');
});
framework.on('pageinit', function(e){
console.log('pageinit:', e.detail);
});
framework.on('pageinitfailed', function(){
console.log('pageinitfailed');
});
framework.on('pageloaded', function(e){
console.log('pageloaded,', e.detail.data.url, ", Title:", e.detail.data.title);
});
framework.on('pagecreate', function(e){
console.log('pagecreate,', e.detail.url);
});
framework.on('pageview', function(){
console.log('pageview');
});
framework.on('pageremove', function(){
console.log('pageremove');
});
})(this, document, FM = this.FM || {});

View File

@ -1,3 +1,12 @@
/* used to tie angular.js with the framework */
/* nowhere should the framework reference angular.js */
/* nowhere in angular.js should it reference the framework */
/* nowhere in angular.js should it reference the framework */
var ionic = angular.module('ionic', ['ngTouch']);
ionic.directive('panel', ['$parse', '$timeout', '$rootElement',
function($parse, $timeout, $rootElement) {
return function(scope, element, attrs) {
};
}
]);