chr 0.5.7 → 0.5.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.scss-style.yml +3 -3
  3. data/{LICENSE → LICENSE.md} +0 -0
  4. data/README.md +24 -10
  5. data/app/assets/javascripts/chr/list_tabs.coffee +1 -2
  6. data/app/assets/javascripts/chr/view_tabs.coffee +7 -6
  7. data/app/assets/stylesheets/chr/layout.scss +15 -2
  8. data/app/assets/stylesheets/chr/themes/basic.scss +8 -16
  9. data/chr.gemspec +33 -34
  10. data/lib/chr.rb +9 -12
  11. data/lib/chr/version.rb +1 -1
  12. metadata +8 -51
  13. data/app/assets/javascripts/vendor/slip.js +0 -804
  14. data/bin/chr +0 -13
  15. data/docs/bootstrap.md +0 -23
  16. data/docs/demo.png +0 -0
  17. data/docs/notes.md +0 -3
  18. data/docs/rails.md +0 -233
  19. data/lib/chr/app_builder.rb +0 -378
  20. data/lib/chr/generators/app_generator.rb +0 -211
  21. data/lib/generators/chr/controller_generator.rb +0 -18
  22. data/templates/Gemfile.erb +0 -41
  23. data/templates/Procfile +0 -1
  24. data/templates/README.md.erb +0 -45
  25. data/templates/_analytics.html.erb +0 -1
  26. data/templates/_flashes.html.erb +0 -7
  27. data/templates/_javascript.html.erb +0 -5
  28. data/templates/application.coffee +0 -2
  29. data/templates/application.scss +0 -1
  30. data/templates/application.yml +0 -6
  31. data/templates/application_gitignore +0 -15
  32. data/templates/application_layout.html.erb.erb +0 -17
  33. data/templates/bin_setup.erb +0 -35
  34. data/templates/body_class_helper.rb +0 -15
  35. data/templates/bundler_audit.rake +0 -12
  36. data/templates/carrierwave.rb +0 -21
  37. data/templates/character_admin.coffee.erb +0 -38
  38. data/templates/character_admin.scss +0 -14
  39. data/templates/character_admin_index.html.erb +0 -5
  40. data/templates/character_admin_layout.html.erb.erb +0 -21
  41. data/templates/character_base_controller.rb +0 -14
  42. data/templates/dev.rake +0 -12
  43. data/templates/devise_overrides_passwords_controller.rb +0 -11
  44. data/templates/devise_overrides_passwords_edit.html.erb +0 -31
  45. data/templates/devise_overrides_passwords_new.html.erb +0 -19
  46. data/templates/devise_overrides_sessions_controller.rb +0 -20
  47. data/templates/devise_overrides_sessions_new.html.erb +0 -29
  48. data/templates/errors.rb +0 -34
  49. data/templates/json_encoding.rb +0 -1
  50. data/templates/puma.rb +0 -18
  51. data/templates/routes.rb +0 -86
  52. data/templates/sample.env +0 -6
  53. data/templates/secrets.yml +0 -14
  54. data/templates/smtp.rb +0 -9
  55. data/templates/staging.rb +0 -5
@@ -1,804 +0,0 @@
1
- /*
2
- Slip - swiping and reordering in lists of elements on touch screens, no fuss.
3
-
4
- Fires these events on list elements:
5
-
6
- • slip:swipe
7
- When swipe has been done and user has lifted finger off the screen.
8
- If you execute event.preventDefault() the element will be animated back to original position.
9
- Otherwise it will be animated off the list and set to display:none.
10
-
11
- • slip:beforeswipe
12
- Fired before first swipe movement starts.
13
- If you execute event.preventDefault() then element will not move at all.
14
-
15
- • slip:reorder
16
- Element has been dropped in new location. event.detail contains the location:
17
- • insertBefore: DOM node before which element has been dropped (null is the end of the list). Use with node.insertBefore().
18
- • spliceIndex: Index of element before which current element has been dropped, not counting the element iself.
19
- For use with Array.splice() if the list is reflecting objects in some array.
20
-
21
- • slip:beforereorder
22
- When reordering movement starts.
23
- Element being reordered gets class `slip-reordering`.
24
- If you execute event.preventDefault() then element will not move at all.
25
-
26
- • slip:beforewait
27
- If you execute event.preventDefault() then reordering will begin immediately, blocking ability to scroll the page.
28
-
29
- • slip:tap
30
- When element was tapped without being swiped/reordered.
31
-
32
- • slip:cancelswipe
33
- Fired when the user stops dragging and the element returns to its original position.
34
-
35
-
36
- Usage:
37
-
38
- CSS:
39
- You should set `user-select:none` (and WebKit prefixes, sigh) on list elements,
40
- otherwise unstoppable and glitchy text selection in iOS will get in the way.
41
-
42
- You should set `overflow-x: hidden` on the container or body to prevent horizontal scrollbar
43
- appearing when elements are swiped off the list.
44
-
45
-
46
- var list = document.querySelector('ul#slippylist');
47
- new Slip(list);
48
-
49
- list.addEventListener('slip:beforeswipe', function(e) {
50
- if (shouldNotSwipe(e.target)) e.preventDefault();
51
- });
52
-
53
- list.addEventListener('slip:swipe', function(e) {
54
- // e.target swiped
55
- if (thatWasSwipeToRemove) {
56
- e.target.parentNode.removeChild(e.target);
57
- } else {
58
- e.preventDefault(); // will animate back to original position
59
- }
60
- });
61
-
62
- list.addEventListener('slip:beforereorder', function(e) {
63
- if (shouldNotReorder(e.target)) e.preventDefault();
64
- });
65
-
66
- list.addEventListener('slip:reorder', function(e) {
67
- // e.target reordered.
68
- if (reorderedOK) {
69
- e.target.parentNode.insertBefore(e.target, e.detail.insertBefore);
70
- } else {
71
- e.preventDefault();
72
- }
73
- });
74
-
75
- Requires:
76
- • Touch events
77
- • CSS transforms
78
- • Function.bind()
79
-
80
- Caveats:
81
- • Elements must not change size while reordering or swiping takes place (otherwise it will be visually out of sync)
82
- */
83
- /*! @license
84
- Slip.js 1.2.0
85
-
86
- © 2014 Kornel Lesiński <kornel@geekhood.net>. All rights reserved.
87
-
88
- Redistribution and use in source and binary forms, with or without modification,
89
- are permitted provided that the following conditions are met:
90
-
91
- 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
92
-
93
- 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
94
- the following disclaimer in the documentation and/or other materials provided with the distribution.
95
-
96
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
97
- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
98
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
99
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
100
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
101
- WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
102
- USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
103
- */
104
-
105
- window['Slip'] = (function(){
106
- 'use strict';
107
-
108
- var damnYouChrome = /Chrome\/[34]/.test(navigator.userAgent); // For bugs that can't be programmatically detected :( Intended to catch all versions of Chrome 30-40
109
- var needsBodyHandlerHack = damnYouChrome; // Otherwise I _sometimes_ don't get any touchstart events and only clicks instead.
110
-
111
- /* When dragging elements down in Chrome (tested 34-37) dragged element may appear below stationary elements.
112
- Looks like WebKit bug #61824, but iOS Safari doesn't have that problem. */
113
- var compositorDoesNotOrderLayers = damnYouChrome;
114
-
115
- // -webkit-mess
116
- var testElement = document.createElement('div');
117
-
118
- var transitionPrefix = "webkitTransition" in testElement.style ? "webkitTransition" : "transition";
119
- var transformPrefix = "webkitTransform" in testElement.style ? "webkitTransform" : "transform";
120
- var transformProperty = transformPrefix === "webkitTransform" ? "-webkit-transform" : "transform";
121
- var userSelectPrefix = "webkitUserSelect" in testElement.style ? "webkitUserSelect" : "userSelect";
122
-
123
- testElement.style[transformPrefix] = 'translateZ(0)';
124
- var hwLayerMagic = testElement.style[transformPrefix] ? 'translateZ(0) ' : '';
125
- var hwTopLayerMagic = testElement.style[transformPrefix] ? 'translateZ(1px) ' : '';
126
- testElement = null;
127
-
128
- var globalInstances = 0;
129
- var attachedBodyHandlerHack = false;
130
- var nullHandler = function(){};
131
-
132
- function Slip(container, options) {
133
- if ('string' === typeof container) container = document.querySelector(container);
134
- if (!container || !container.addEventListener) throw new Error("Please specify DOM node to attach to");
135
-
136
- if (!this || this === window) return new Slip(container, options);
137
-
138
- this.options = options;
139
-
140
- // Functions used for as event handlers need usable `this` and must not change to be removable
141
- this.cancel = this.setState.bind(this, this.states.idle);
142
- this.onTouchStart = this.onTouchStart.bind(this);
143
- this.onTouchMove = this.onTouchMove.bind(this);
144
- this.onTouchEnd = this.onTouchEnd.bind(this);
145
- this.onMouseDown = this.onMouseDown.bind(this);
146
- this.onMouseMove = this.onMouseMove.bind(this);
147
- this.onMouseUp = this.onMouseUp.bind(this);
148
- this.onMouseLeave = this.onMouseLeave.bind(this);
149
- this.onSelection = this.onSelection.bind(this);
150
-
151
- this.setState(this.states.idle);
152
- this.attach(container);
153
- }
154
-
155
- function getTransform(node) {
156
- var transform = node.style[transformPrefix];
157
- if (transform) {
158
- return {
159
- value:transform,
160
- original:transform,
161
- };
162
- }
163
-
164
- if (window.getComputedStyle) {
165
- var style = window.getComputedStyle(node).getPropertyValue(transformProperty);
166
- if (style && style !== 'none') return {value:style, original:''};
167
- }
168
- return {value:'', original:''};
169
- }
170
-
171
- function findIndex(target, nodes) {
172
- var originalIndex = 0;
173
- var listCount = 0;
174
-
175
- for (var i=0; i < nodes.length; i++) {
176
- if (nodes[i].nodeType === 1) {
177
- listCount++;
178
- if (nodes[i] === target.node) {
179
- originalIndex = listCount-1;
180
- }
181
- }
182
- }
183
-
184
- return originalIndex;
185
- }
186
-
187
- // All functions in states are going to be executed in context of Slip object
188
- Slip.prototype = {
189
-
190
- container: null,
191
- options: {},
192
- state: null,
193
-
194
- target: null, // the tapped/swiped/reordered node with height and backed up styles
195
-
196
- usingTouch: false, // there's no good way to detect touchscreen preference other than receiving a touch event (really, trust me).
197
- mouseHandlersAttached: false,
198
-
199
- startPosition: null, // x,y,time where first touch began
200
- latestPosition: null, // x,y,time where the finger is currently
201
- previousPosition: null, // x,y,time where the finger was ~100ms ago (for velocity calculation)
202
-
203
- canPreventScrolling: false,
204
-
205
- states: {
206
- idle: function idleStateInit() {
207
- this.target = null;
208
- this.usingTouch = false;
209
- this.removeMouseHandlers();
210
-
211
- return {
212
- allowTextSelection: true,
213
- };
214
- },
215
-
216
- undecided: function undecidedStateInit() {
217
- this.target.height = this.target.node.offsetHeight;
218
- this.target.node.style[transitionPrefix] = '';
219
-
220
- if (!this.dispatch(this.target.originalTarget, 'beforewait')) {
221
- if (this.dispatch(this.target.originalTarget, 'beforereorder')) {
222
- this.setState(this.states.reorder);
223
- }
224
- } else {
225
- var holdTimer = setTimeout(function(){
226
- var move = this.getAbsoluteMovement();
227
- if (this.canPreventScrolling && move.x < 15 && move.y < 25) {
228
- if (this.dispatch(this.target.originalTarget, 'beforereorder')) {
229
- this.setState(this.states.reorder);
230
- }
231
- }
232
- }.bind(this), 300);
233
- }
234
-
235
- return {
236
- leaveState: function() {
237
- clearTimeout(holdTimer);
238
- },
239
-
240
- onMove: function() {
241
- var move = this.getAbsoluteMovement();
242
-
243
- if (move.x > 20 && move.y < Math.max(100, this.target.height)) {
244
- if (this.dispatch(this.target.originalTarget, 'beforeswipe')) {
245
- this.setState(this.states.swipe);
246
- return false;
247
- } else {
248
- this.setState(this.states.idle);
249
- }
250
- }
251
- if (move.y > 20) {
252
- this.setState(this.states.idle);
253
- }
254
-
255
- // Chrome likes sideways scrolling :(
256
- if (move.x > move.y*1.2) return false;
257
- },
258
-
259
- onLeave: function() {
260
- this.setState(this.states.idle);
261
- },
262
-
263
- onEnd: function() {
264
- var allowDefault = this.dispatch(this.target.originalTarget, 'tap');
265
- this.setState(this.states.idle);
266
- return allowDefault;
267
- },
268
- };
269
- },
270
-
271
- swipe: function swipeStateInit() {
272
- var swipeSuccess = false;
273
- var container = this.container;
274
-
275
- var originalIndex = findIndex(this.target, this.container.childNodes);
276
-
277
- container.className += ' slip-swiping-container';
278
- function removeClass() {
279
- container.className = container.className.replace(/(?:^| )slip-swiping-container/,'');
280
- }
281
-
282
- this.target.height = this.target.node.offsetHeight;
283
-
284
- return {
285
- leaveState: function() {
286
- if (swipeSuccess) {
287
- this.animateSwipe(function(target){
288
- target.node.style[transformPrefix] = target.baseTransform.original;
289
- target.node.style[transitionPrefix] = '';
290
- if (this.dispatch(target.node, 'afterswipe')) {
291
- removeClass();
292
- return true;
293
- } else {
294
- this.animateToZero(undefined, target);
295
- }
296
- }.bind(this));
297
- } else {
298
- this.animateToZero(removeClass);
299
- this.dispatch(this.target.node, 'cancelswipe');
300
- }
301
- },
302
-
303
- onMove: function() {
304
- var move = this.getTotalMovement();
305
-
306
- if (Math.abs(move.y) < this.target.height+20) {
307
- this.target.node.style[transformPrefix] = 'translate(' + move.x + 'px,0) ' + hwLayerMagic + this.target.baseTransform.value;
308
- return false;
309
- } else {
310
- this.setState(this.states.idle);
311
- }
312
- },
313
-
314
- onLeave: function() {
315
- this.state.onEnd.call(this);
316
- },
317
-
318
- onEnd: function() {
319
- var dx = this.latestPosition.x - this.previousPosition.x;
320
- var dy = this.latestPosition.y - this.previousPosition.y;
321
- var velocity = Math.sqrt(dx*dx + dy*dy) / (this.latestPosition.time - this.previousPosition.time + 1);
322
-
323
- var move = this.getAbsoluteMovement();
324
- var swiped = velocity > 0.6 && move.time > 110;
325
-
326
- var direction;
327
- if (dx > 0) {
328
- direction = "right";
329
- } else {
330
- direction = "left";
331
- }
332
-
333
- if (swiped) {
334
- if (this.dispatch(this.target.node, 'swipe', {direction: direction, originalIndex: originalIndex})) {
335
- swipeSuccess = true; // can't animate here, leaveState overrides anim
336
- }
337
- }
338
- this.setState(this.states.idle);
339
- return !swiped;
340
- },
341
- };
342
- },
343
-
344
- reorder: function reorderStateInit() {
345
- this.target.height = this.target.node.offsetHeight;
346
-
347
- var nodes = this.container.childNodes;
348
- var originalIndex = findIndex(this.target, nodes);
349
- var mouseOutsideTimer;
350
- var zero = this.target.node.offsetTop + this.target.height/2;
351
- var otherNodes = [];
352
- for(var i=0; i < nodes.length; i++) {
353
- if (nodes[i].nodeType != 1 || nodes[i] === this.target.node) continue;
354
- var t = nodes[i].offsetTop;
355
- nodes[i].style[transitionPrefix] = transformProperty + ' 0.2s ease-in-out';
356
- otherNodes.push({
357
- node: nodes[i],
358
- baseTransform: getTransform(nodes[i]),
359
- pos: t + (t < zero ? nodes[i].offsetHeight : 0) - zero,
360
- });
361
- }
362
-
363
- this.target.node.className += ' slip-reordering';
364
- this.target.node.style.zIndex = '99999';
365
- this.target.node.style[userSelectPrefix] = 'none';
366
- if (compositorDoesNotOrderLayers) {
367
- // Chrome's compositor doesn't sort 2D layers
368
- this.container.style.webkitTransformStyle = 'preserve-3d';
369
- }
370
-
371
- function setPosition() {
372
- /*jshint validthis:true */
373
-
374
- if (mouseOutsideTimer) {
375
- // don't care where the mouse is as long as it moves
376
- clearTimeout(mouseOutsideTimer); mouseOutsideTimer = null;
377
- }
378
-
379
- var move = this.getTotalMovement();
380
- this.target.node.style[transformPrefix] = 'translate(0,' + move.y + 'px) ' + hwTopLayerMagic + this.target.baseTransform.value;
381
-
382
- var height = this.target.height;
383
- otherNodes.forEach(function(o){
384
- var off = 0;
385
- if (o.pos < 0 && move.y < 0 && o.pos > move.y) {
386
- off = height;
387
- }
388
- else if (o.pos > 0 && move.y > 0 && o.pos < move.y) {
389
- off = -height;
390
- }
391
- // FIXME: should change accelerated/non-accelerated state lazily
392
- o.node.style[transformPrefix] = off ? 'translate(0,'+off+'px) ' + hwLayerMagic + o.baseTransform.value : o.baseTransform.original;
393
- });
394
- return false;
395
- }
396
-
397
- setPosition.call(this);
398
-
399
- return {
400
- leaveState: function() {
401
- if (mouseOutsideTimer) clearTimeout(mouseOutsideTimer);
402
-
403
- if (compositorDoesNotOrderLayers) {
404
- this.container.style.webkitTransformStyle = '';
405
- }
406
-
407
- this.target.node.className = this.target.node.className.replace(/(?:^| )slip-reordering/,'');
408
- this.target.node.style[userSelectPrefix] = '';
409
-
410
- this.animateToZero(function(target){
411
- target.node.style.zIndex = '';
412
- });
413
- otherNodes.forEach(function(o){
414
- o.node.style[transformPrefix] = o.baseTransform.original;
415
- o.node.style[transitionPrefix] = ''; // FIXME: animate to new position
416
- });
417
- },
418
-
419
- onMove: setPosition,
420
-
421
- onLeave: function() {
422
- // don't let element get stuck if mouse left the window
423
- // but don't cancel immediately as it'd be annoying near window edges
424
- if (mouseOutsideTimer) clearTimeout(mouseOutsideTimer);
425
- mouseOutsideTimer = setTimeout(function(){
426
- mouseOutsideTimer = null;
427
- this.cancel();
428
- }.bind(this), 700);
429
- },
430
-
431
- onEnd: function() {
432
- var move = this.getTotalMovement();
433
- if (move.y < 0) {
434
- for(var i=0; i < otherNodes.length; i++) {
435
- if (otherNodes[i].pos > move.y) {
436
- this.dispatch(this.target.node, 'reorder', {spliceIndex:i, insertBefore:otherNodes[i].node, originalIndex: originalIndex});
437
- break;
438
- }
439
- }
440
- } else {
441
- for(var i=otherNodes.length-1; i >= 0; i--) {
442
- if (otherNodes[i].pos < move.y) {
443
- this.dispatch(this.target.node, 'reorder', {spliceIndex:i+1, insertBefore:otherNodes[i+1] ? otherNodes[i+1].node : null, originalIndex: originalIndex});
444
- break;
445
- }
446
- }
447
- }
448
- this.setState(this.states.idle);
449
- return false;
450
- },
451
- };
452
- },
453
- },
454
-
455
- attach: function(container) {
456
- globalInstances++;
457
- if (this.container) this.detach();
458
-
459
- // In some cases taps on list elements send *only* click events and no touch events. Spotted only in Chrome 32+
460
- // Having event listener on body seems to solve the issue (although AFAIK may disable smooth scrolling as a side-effect)
461
- if (!attachedBodyHandlerHack && needsBodyHandlerHack) {
462
- attachedBodyHandlerHack = true;
463
- document.body.addEventListener('touchstart', nullHandler, false);
464
- }
465
-
466
- this.container = container;
467
- this.otherNodes = [];
468
-
469
- // selection on iOS interferes with reordering
470
- document.addEventListener("selectionchange", this.onSelection, false);
471
-
472
- // cancel is called e.g. when iOS detects multitasking gesture
473
- this.container.addEventListener('touchcancel', this.cancel, false);
474
- this.container.addEventListener('touchstart', this.onTouchStart, false);
475
- this.container.addEventListener('touchmove', this.onTouchMove, false);
476
- this.container.addEventListener('touchend', this.onTouchEnd, false);
477
- this.container.addEventListener('mousedown', this.onMouseDown, false);
478
- // mousemove and mouseup are attached dynamically
479
- },
480
-
481
- detach: function() {
482
- this.cancel();
483
-
484
- this.container.removeEventListener('mousedown', this.onMouseDown, false);
485
- this.container.removeEventListener('touchend', this.onTouchEnd, false);
486
- this.container.removeEventListener('touchmove', this.onTouchMove, false);
487
- this.container.removeEventListener('touchstart', this.onTouchStart, false);
488
- this.container.removeEventListener('touchcancel', this.cancel, false);
489
-
490
- document.removeEventListener("selectionchange", this.onSelection, false);
491
-
492
- globalInstances--;
493
- if (!globalInstances && attachedBodyHandlerHack) {
494
- attachedBodyHandlerHack = false;
495
- document.body.removeEventListener('touchstart', nullHandler, false);
496
- }
497
- },
498
-
499
- setState: function(newStateCtor){
500
- if (this.state) {
501
- if (this.state.ctor === newStateCtor) return;
502
- if (this.state.leaveState) this.state.leaveState.call(this);
503
- }
504
-
505
- // Must be re-entrant in case ctor changes state
506
- var prevState = this.state;
507
- var nextState = newStateCtor.call(this);
508
- if (this.state === prevState) {
509
- nextState.ctor = newStateCtor;
510
- this.state = nextState;
511
- }
512
- },
513
-
514
- // Here we have an issue with nested lists, so adding an options
515
- // for data container, might require to rewrite it without jquery
516
- findTargetNode: function(target) {
517
- var targetNode = target;
518
-
519
- while(targetNode && targetNode.parentNode !== this.container) {
520
- targetNode = targetNode.parentNode;
521
- }
522
-
523
- var targetContainerClass = $(target).attr('data-container-class');
524
-
525
- if (targetContainerClass) {
526
- if ( ! $(this.container).hasClass(targetContainerClass) ) {
527
- return false;
528
- }
529
- }
530
-
531
- return targetNode;
532
- },
533
-
534
- onSelection: function(e) {
535
- var isRelated = e.target === document || this.findTargetNode(e);
536
- if (!isRelated) return;
537
-
538
- if (e.cancelable || e.defaultPrevented) {
539
- if (!this.state.allowTextSelection) {
540
- e.preventDefault();
541
- }
542
- } else {
543
- // iOS doesn't allow selection to be prevented
544
- this.setState(this.states.idle);
545
- }
546
- },
547
-
548
- addMouseHandlers: function() {
549
- // unlike touch events, mousemove/up is not conveniently fired on the same element,
550
- // but I don't need to listen to unrelated events all the time
551
- if (!this.mouseHandlersAttached) {
552
- this.mouseHandlersAttached = true;
553
- document.documentElement.addEventListener('mouseleave', this.onMouseLeave, false);
554
- window.addEventListener('mousemove', this.onMouseMove, true);
555
- window.addEventListener('mouseup', this.onMouseUp, true);
556
- window.addEventListener('blur', this.cancel, false);
557
- }
558
- },
559
-
560
- removeMouseHandlers: function() {
561
- if (this.mouseHandlersAttached) {
562
- this.mouseHandlersAttached = false;
563
- document.documentElement.removeEventListener('mouseleave', this.onMouseLeave, false);
564
- window.removeEventListener('mousemove', this.onMouseMove, true);
565
- window.removeEventListener('mouseup', this.onMouseUp, true);
566
- window.removeEventListener('blur', this.cancel, false);
567
- }
568
- },
569
-
570
- onMouseLeave: function(e) {
571
- if (this.usingTouch) return;
572
-
573
- if (e.target === document.documentElement || e.relatedTarget === document.documentElement) {
574
- if (this.state.onLeave) {
575
- this.state.onLeave.call(this);
576
- }
577
- }
578
- },
579
-
580
- onMouseDown: function(e) {
581
- if (this.usingTouch || e.button != 0 || !this.setTarget(e)) return;
582
-
583
- this.addMouseHandlers(); // mouseup, etc.
584
-
585
- this.canPreventScrolling = true; // or rather it doesn't apply to mouse
586
-
587
- this.startAtPosition({
588
- x: e.clientX,
589
- y: e.clientY,
590
- time: e.timeStamp,
591
- });
592
- },
593
-
594
- onTouchStart: function(e) {
595
- this.usingTouch = true;
596
- this.canPreventScrolling = true;
597
-
598
- // This implementation cares only about single touch
599
- if (e.touches.length > 1) {
600
- this.setState(this.states.idle);
601
- return;
602
- }
603
-
604
- if (!this.setTarget(e)) return;
605
-
606
- this.startAtPosition({
607
- x: e.touches[0].clientX,
608
- y: e.touches[0].clientY - window.scrollY,
609
- time: e.timeStamp,
610
- });
611
- },
612
-
613
- setTarget: function(e) {
614
- var targetNode = this.findTargetNode(e.target);
615
- if (!targetNode) {
616
- this.setState(this.states.idle);
617
- return false;
618
- }
619
-
620
- //check for a scrollable parent
621
- var scrollContainer = targetNode.parentNode;
622
- while (scrollContainer){
623
- if (scrollContainer.scrollHeight > scrollContainer.clientHeight && window.getComputedStyle(scrollContainer)['overflow-y'] != 'visible') break;
624
- else scrollContainer = scrollContainer.parentNode;
625
- }
626
-
627
- this.target = {
628
- originalTarget: e.target,
629
- node: targetNode,
630
- scrollContainer: scrollContainer,
631
- baseTransform: getTransform(targetNode),
632
- };
633
- return true;
634
- },
635
-
636
- startAtPosition: function(pos) {
637
- this.startPosition = this.previousPosition = this.latestPosition = pos;
638
- this.setState(this.states.undecided);
639
- },
640
-
641
- updatePosition: function(e, pos) {
642
- this.latestPosition = pos;
643
-
644
- var triggerOffset = 40,
645
- offset = 0;
646
-
647
- var scrollable = this.target.scrollContainer || document.body,
648
- containerRect = scrollable.getBoundingClientRect(),
649
- targetRect = this.target.node.getBoundingClientRect(),
650
- bottomOffset = Math.min(containerRect.bottom, window.innerHeight) - targetRect.bottom,
651
- topOffset = targetRect.top - Math.max(containerRect.top, 0);
652
-
653
- if (bottomOffset < triggerOffset){
654
- offset = triggerOffset - bottomOffset;
655
- }
656
- else if (topOffset < triggerOffset){
657
- offset = topOffset - triggerOffset;
658
- }
659
-
660
- var prevScrollTop = scrollable.scrollTop;
661
- scrollable.scrollTop += offset;
662
- if (prevScrollTop != scrollable.scrollTop) this.startPosition.y += prevScrollTop-scrollable.scrollTop;
663
-
664
- if (this.state.onMove) {
665
- if (this.state.onMove.call(this) === false) {
666
- e.preventDefault();
667
- }
668
- }
669
-
670
- // sample latestPosition 100ms for velocity
671
- if (this.latestPosition.time - this.previousPosition.time > 100) {
672
- this.previousPosition = this.latestPosition;
673
- }
674
- },
675
-
676
- onMouseMove: function(e) {
677
- this.updatePosition(e, {
678
- x: e.clientX,
679
- y: e.clientY,
680
- time: e.timeStamp,
681
- });
682
- },
683
-
684
- onTouchMove: function(e) {
685
- this.updatePosition(e, {
686
- x: e.touches[0].clientX,
687
- y: e.touches[0].clientY - window.scrollY,
688
- time: e.timeStamp,
689
- });
690
-
691
- // In Apple's touch model only the first move event after touchstart can prevent scrolling (and event.cancelable is broken)
692
- this.canPreventScrolling = false;
693
- },
694
-
695
- onMouseUp: function(e) {
696
- if (this.usingTouch || e.button !== 0) return;
697
-
698
- if (this.state.onEnd && false === this.state.onEnd.call(this)) {
699
- e.preventDefault();
700
- }
701
- },
702
-
703
- onTouchEnd: function(e) {
704
- if (e.touches.length > 1) {
705
- this.cancel();
706
- } else if (this.state.onEnd && false === this.state.onEnd.call(this)) {
707
- e.preventDefault();
708
- }
709
- },
710
-
711
- getTotalMovement: function() {
712
- return {
713
- x:this.latestPosition.x - this.startPosition.x,
714
- y:this.latestPosition.y - this.startPosition.y,
715
- };
716
- },
717
-
718
- getAbsoluteMovement: function() {
719
- return {
720
- x: Math.abs(this.latestPosition.x - this.startPosition.x),
721
- y: Math.abs(this.latestPosition.y - this.startPosition.y),
722
- time:this.latestPosition.time - this.startPosition.time,
723
- };
724
- },
725
-
726
- dispatch: function(targetNode, eventName, detail) {
727
- var event = document.createEvent('CustomEvent');
728
- if (event && event.initCustomEvent) {
729
- event.initCustomEvent('slip:' + eventName, true, true, detail);
730
- } else {
731
- event = document.createEvent('Event');
732
- event.initEvent('slip:' + eventName, true, true);
733
- event.detail = detail;
734
- }
735
- return targetNode.dispatchEvent(event);
736
- },
737
-
738
- getSiblings: function(target) {
739
- var siblings = [];
740
- var tmp = target.node.nextSibling;
741
- while(tmp) {
742
- if (tmp.nodeType == 1) siblings.push({
743
- node: tmp,
744
- baseTransform: getTransform(tmp),
745
- });
746
- tmp = tmp.nextSibling;
747
- }
748
- return siblings;
749
- },
750
-
751
- animateToZero: function(callback, target) {
752
- // save, because this.target/container could change during animation
753
- target = target || this.target;
754
-
755
- target.node.style[transitionPrefix] = transformProperty + ' 0.1s ease-out';
756
- target.node.style[transformPrefix] = 'translate(0,0) ' + hwLayerMagic + target.baseTransform.value;
757
- setTimeout(function(){
758
- target.node.style[transitionPrefix] = '';
759
- target.node.style[transformPrefix] = target.baseTransform.original;
760
- if (callback) callback.call(this, target);
761
- }.bind(this), 101);
762
- },
763
-
764
- animateSwipe: function(callback) {
765
- var target = this.target;
766
- var siblings = this.getSiblings(target);
767
- var emptySpaceTransform = 'translate(0,' + this.target.height + 'px) ' + hwLayerMagic + ' ';
768
-
769
- // FIXME: animate with real velocity
770
- target.node.style[transitionPrefix] = 'all 0.1s linear';
771
- target.node.style[transformPrefix] = ' translate(' + (this.getTotalMovement().x > 0 ? '' : '-') + '100%,0) ' + hwLayerMagic + target.baseTransform.value;
772
-
773
- setTimeout(function(){
774
- if (callback.call(this, target)) {
775
- siblings.forEach(function(o){
776
- o.node.style[transitionPrefix] = '';
777
- o.node.style[transformPrefix] = emptySpaceTransform + o.baseTransform.value;
778
- });
779
- setTimeout(function(){
780
- siblings.forEach(function(o){
781
- o.node.style[transitionPrefix] = transformProperty + ' 0.1s ease-in-out';
782
- o.node.style[transformPrefix] = 'translate(0,0) ' + hwLayerMagic + o.baseTransform.value;
783
- });
784
- setTimeout(function(){
785
- siblings.forEach(function(o){
786
- o.node.style[transitionPrefix] = '';
787
- o.node.style[transformPrefix] = o.baseTransform.original;
788
- });
789
- },101);
790
- }, 1);
791
- }
792
- }.bind(this), 101);
793
- },
794
- };
795
-
796
- // AMD
797
- if ('function' === typeof define && define.amd) {
798
- define(function(){
799
- return Slip;
800
- });
801
- }
802
- return Slip;
803
- })();
804
-