1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | define('directives/spinner',[
|
8 | 'directives/ng-directives',
|
9 | 'jquery'
|
10 | ],function(module) {
|
11 | var Spinner = (function() {
|
12 | var defaults = {
|
13 | selector: '.frame',
|
14 | next: '.next',
|
15 | prev: '.prev',
|
16 | snap: 5,
|
17 | drift: 500
|
18 | };
|
19 |
|
20 |
|
21 | function clamp(value,min,max) {
|
22 | return Math.max(min,Math.min(max,value));
|
23 | }
|
24 |
|
25 | function vdiff(v1,v2) {
|
26 | return v1.map(function(s1,i) {
|
27 | return s1-v2[i];
|
28 | });
|
29 | }
|
30 |
|
31 | function Spinner(el,options) {
|
32 | this.elContainer = $(el);
|
33 | this.setup(options);
|
34 | this.initialize();
|
35 | }
|
36 |
|
37 | function getEvent(e) {
|
38 | if (typeof e.originalEvent.touches !== "undefined" && e.originalEvent.touches.length) {
|
39 | return e.originalEvent.touches[0];
|
40 | }
|
41 | return e;
|
42 | }
|
43 |
|
44 | function handle(fn,scope) {
|
45 | return function(e) {
|
46 | fn.call(scope||this,e,getEvent(e));
|
47 | };
|
48 | }
|
49 |
|
50 | function is_touch_device() {
|
51 | try {
|
52 | document.createEvent("TouchEvent");
|
53 | return true;
|
54 | } catch (e) {
|
55 | return false;
|
56 | }
|
57 | }
|
58 | var isTouch = is_touch_device();
|
59 | var events = {
|
60 | start: isTouch?'touchstart':'mousedown',
|
61 | move: isTouch?'touchmove':'mousemove',
|
62 | end: isTouch?'touchend':'mouseup'
|
63 | };
|
64 |
|
65 | Spinner.prototype.setup = function(options) {
|
66 | this.options = $.extend(defaults,options);
|
67 | this.elFrame = this.elContainer.children(this.options.selector);
|
68 | this.elPrev = this.elContainer.children(this.options.prev);
|
69 | this.elNext = this.elContainer.children(this.options.next);
|
70 | this.min = 0;
|
71 | this.max = this.elFrame.children().length-1;
|
72 | this.step = this.elFrame.children().width();
|
73 | };
|
74 |
|
75 | Spinner.prototype.initialize = function() {
|
76 | this.elPrev.on(events.start,handle(this.prev,this));
|
77 | this.elNext.on(events.start,handle(this.next,this));
|
78 | this.elContainer.on(events.start,handle(this.dragstart,this));
|
79 | $(document).on(events.move,handle(this.drag,this));
|
80 | $(document).on(events.end,handle(this.dragend,this));
|
81 | this.set(this.defaultValue,true);
|
82 | };
|
83 |
|
84 | function transform(el,value) {
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 | el.css({
|
93 | marginLeft: value+'px'
|
94 | });
|
95 | }
|
96 |
|
97 | Spinner.prototype.set = function(value,trigger) {
|
98 | this.max = this.elFrame.children().length-1;
|
99 | if (value > 0) {
|
100 | this.min = 1;
|
101 | }
|
102 | if (value !== undefined) {
|
103 | this.value = clamp(value||0,this.min,this.max);
|
104 | this.offset = -1*this.step*this.value;
|
105 | } else {
|
106 | this.value = undefined;
|
107 | this.offset = 0;
|
108 | }
|
109 | transform(this.elFrame,this.offset);
|
110 | if (trigger) {
|
111 | this.elContainer.trigger('change',[this.value-1,this]);
|
112 | }
|
113 | };
|
114 |
|
115 | Spinner.prototype.repaint = function() {
|
116 | this.set(this.value, false);
|
117 | };
|
118 |
|
119 | Spinner.prototype.prev = function(e) {
|
120 | this.set(this.value-1,true);
|
121 | if (e) {
|
122 | e.stopPropagation();
|
123 | }
|
124 | };
|
125 |
|
126 | Spinner.prototype.next = function(e) {
|
127 | this.set((this.value||0)+1,true);
|
128 | if (e) {
|
129 | e.stopPropagation();
|
130 | }
|
131 | };
|
132 |
|
133 | Spinner.prototype.dragstart = function(e,ev) {
|
134 | this.dragging = true;
|
135 | this.elContainer.addClass('dragging');
|
136 | this.x = [ev.screenX,ev.screenY];
|
137 | this.t = e.timeStamp;
|
138 | };
|
139 | Spinner.prototype.drag = function(e,ev) {
|
140 | if (this.dragging) {
|
141 | this.dx = vdiff([ev.screenX,ev.screenY],this.x);
|
142 | this.dt = (e.timeStamp - this.t)/1000;
|
143 | this.x = [ev.screenX,ev.screenY];
|
144 | this.t = e.timeStamp;
|
145 | this.v = [this.dx[0]/this.dt,this.dx[1]/this.dt];
|
146 | this.offset += this.dx[0];
|
147 | transform(this.elFrame,this.offset);
|
148 | e.stopPropagation();
|
149 | e.preventDefault();
|
150 | }
|
151 | };
|
152 | Spinner.prototype.dragend = function(e,ev) {
|
153 | if (this.dragging) {
|
154 | this.elContainer.removeClass('dragging');
|
155 | if (Math.abs(this.v[0]) < this.options.snap) {
|
156 | this.snap();
|
157 | } else {
|
158 | this.drift();
|
159 | }
|
160 | this.dragging = false;
|
161 | }
|
162 | };
|
163 |
|
164 | Spinner.prototype.snap = function() {
|
165 | var nearest = Math.round(-this.offset / this.step);
|
166 | this.set(nearest,true);
|
167 | };
|
168 | Spinner.prototype.drift = function() {
|
169 | var dist = this.v[0]*this.options.drift/1000;
|
170 | var nearest = Math.round(-(this.offset+dist) / this.step);
|
171 | this.elContainer.addClass('drifting');
|
172 | this.set(nearest,true);
|
173 | window.setTimeout($.proxy(function() {
|
174 | this.elContainer.removeClass('drifting');
|
175 | },this),this.options.drift);
|
176 | };
|
177 |
|
178 | return Spinner;
|
179 | }());
|
180 |
|
181 | $.fn.spinner = function(options) {
|
182 | return this.each(function() {
|
183 | new Spinner($(this),options);
|
184 | });
|
185 | };
|
186 |
|
187 | return module.directive('fllSpinner',[
|
188 | '$parse','$timeout',
|
189 | function($parse,$timeout) {
|
190 | function numbers(min,max) {
|
191 | var i,nrs = [""];
|
192 | for (i=min; i<=max; i++) {
|
193 | nrs.push(i);
|
194 | }
|
195 | return nrs;
|
196 | }
|
197 |
|
198 | return {
|
199 | scope: true,
|
200 | template: [
|
201 | '<span class="spinner">',
|
202 | '<span class="frame">',
|
203 | '<span ng-repeat="n in numbers">{{n}}</span>',
|
204 | '</span>',
|
205 | '<span class="inner"></span>',
|
206 | '<button class="prev"><i class="material-icons">keyboard_arrow_left</i></button>',
|
207 | '<button class="next"><i class="material-icons">keyboard_arrow_right</i></button>',
|
208 | '</span>'
|
209 | ].join(''),
|
210 | link: function($scope,$element,$attrs) {
|
211 | var min,max,s;
|
212 | var model = $parse($attrs.ngModel);
|
213 | var setting = false;
|
214 | $scope.$watch($attrs.min,function(newValue) {
|
215 | min = 1*newValue;
|
216 | repaint();
|
217 | });
|
218 | $scope.$watch($attrs.max,function(newValue) {
|
219 | max = 1*newValue;
|
220 | repaint();
|
221 | });
|
222 | $scope.$parent.$watch($attrs.ngModel,function(newValue,oldValue) {
|
223 | if (s && !setting) {
|
224 | if (newValue === undefined) {
|
225 | s.set(undefined);
|
226 | } else {
|
227 | s.set(newValue-min);
|
228 | }
|
229 | }
|
230 | setting = false;
|
231 | });
|
232 |
|
233 | $timeout(init,false);
|
234 |
|
235 | function init() {
|
236 | var el = $element.children().first();
|
237 | s = new Spinner(el);
|
238 |
|
239 | el.bind('change',function(event, n, element) {
|
240 |
|
241 | model.assign($scope.$parent,String(n+min));
|
242 | setting = true;
|
243 | $scope.$apply();
|
244 | });
|
245 | }
|
246 |
|
247 | function repaint() {
|
248 | $scope.numbers = numbers(min,max);
|
249 | $timeout(function() {
|
250 | s.repaint();
|
251 | },false);
|
252 | }
|
253 |
|
254 | }
|
255 | };
|
256 | }
|
257 | ]);
|
258 | });
|