transit 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (186) hide show
  1. data/Gemfile +19 -11
  2. data/app/assets/images/transit/icon24x24.png +0 -0
  3. data/app/assets/images/transit/icon24x24_files.png +0 -0
  4. data/app/assets/images/transit/jplayer.swf +0 -0
  5. data/app/assets/images/transit/uploadify.swf +0 -0
  6. data/app/assets/images/transit/video_player.swf +0 -0
  7. data/app/assets/javascripts/jqtools/expose.js +224 -0
  8. data/app/assets/javascripts/jqtools/flashembed.js +300 -0
  9. data/app/assets/javascripts/jqtools/overlay.js +294 -0
  10. data/app/assets/javascripts/jqtools/scrollable.js +3 -0
  11. data/app/assets/javascripts/jqtools/scrollable/autoscroll.js +81 -0
  12. data/app/assets/javascripts/jqtools/scrollable/base.js +335 -0
  13. data/app/assets/javascripts/jqtools/scrollable/navigator.js +139 -0
  14. data/app/assets/javascripts/jqtools/validator.js +590 -0
  15. data/app/assets/javascripts/jqueryui/accordion.js +611 -0
  16. data/app/assets/javascripts/jqueryui/autocomplete.js +612 -0
  17. data/app/assets/javascripts/jqueryui/button.js +388 -0
  18. data/app/assets/javascripts/jqueryui/core.js +312 -0
  19. data/app/assets/javascripts/jqueryui/datepicker.js +1791 -0
  20. data/app/assets/javascripts/jqueryui/dialog.js +878 -0
  21. data/app/assets/javascripts/jqueryui/draggable.js +815 -0
  22. data/app/assets/javascripts/jqueryui/droppable.js +285 -0
  23. data/app/assets/javascripts/jqueryui/effects/blind.js +49 -0
  24. data/app/assets/javascripts/jqueryui/effects/bounce.js +78 -0
  25. data/app/assets/javascripts/jqueryui/effects/clip.js +54 -0
  26. data/app/assets/javascripts/jqueryui/effects/core.js +746 -0
  27. data/app/assets/javascripts/jqueryui/effects/drop.js +50 -0
  28. data/app/assets/javascripts/jqueryui/effects/explode.js +79 -0
  29. data/app/assets/javascripts/jqueryui/effects/fade.js +32 -0
  30. data/app/assets/javascripts/jqueryui/effects/fold.js +56 -0
  31. data/app/assets/javascripts/jqueryui/effects/highlight.js +50 -0
  32. data/app/assets/javascripts/jqueryui/effects/pulsate.js +51 -0
  33. data/app/assets/javascripts/jqueryui/effects/scale.js +178 -0
  34. data/app/assets/javascripts/jqueryui/effects/shake.js +57 -0
  35. data/app/assets/javascripts/jqueryui/effects/slide.js +50 -0
  36. data/app/assets/javascripts/jqueryui/effects/transfer.js +45 -0
  37. data/app/assets/javascripts/jqueryui/mouse.js +160 -0
  38. data/app/assets/javascripts/jqueryui/position.js +252 -0
  39. data/app/assets/javascripts/jqueryui/progressbar.js +109 -0
  40. data/app/assets/javascripts/jqueryui/resizable.js +814 -0
  41. data/app/assets/javascripts/jqueryui/selectable.js +266 -0
  42. data/app/assets/javascripts/jqueryui/slider.js +666 -0
  43. data/app/assets/javascripts/jqueryui/sortable.js +1077 -0
  44. data/app/assets/javascripts/jqueryui/tabs.js +758 -0
  45. data/app/assets/javascripts/jqueryui/widget.js +262 -0
  46. data/app/assets/javascripts/libs/backbone.js +1152 -0
  47. data/app/assets/javascripts/libs/cookie.js +89 -0
  48. data/app/assets/javascripts/libs/fileinput.js +130 -0
  49. data/app/assets/javascripts/libs/jplayer.js +1768 -0
  50. data/app/assets/javascripts/libs/proper.js +541 -0
  51. data/app/assets/javascripts/libs/sanitize.js +282 -0
  52. data/app/assets/javascripts/libs/selecttolist.js +75 -0
  53. data/app/assets/javascripts/libs/underscore.js +807 -0
  54. data/app/assets/javascripts/libs/uploadify.js +677 -0
  55. data/app/assets/javascripts/libs/wymeditor.js +9538 -0
  56. data/app/assets/javascripts/transit.js +4 -0
  57. data/app/assets/javascripts/transit/admin.js +22 -0
  58. data/app/assets/javascripts/transit/admin/contexts.js +52 -0
  59. data/app/assets/javascripts/transit/admin/fields.js +36 -0
  60. data/app/assets/javascripts/transit/admin/upload.js +109 -0
  61. data/app/assets/javascripts/transit/config.js.erb +101 -0
  62. data/app/assets/javascripts/transit/contexts/audio.js +39 -0
  63. data/app/assets/javascripts/transit/contexts/video.js +79 -0
  64. data/app/assets/javascripts/transit/core.js +171 -0
  65. data/app/assets/javascripts/transit/frontend.js +3 -0
  66. data/app/assets/javascripts/transit/lib/base64.js +120 -0
  67. data/app/assets/javascripts/transit/lib/editor.js +177 -0
  68. data/app/assets/javascripts/transit/views/audio_player.jst +22 -0
  69. data/app/assets/javascripts/transit/views/editor_toolbar.jst +12 -0
  70. data/app/assets/javascripts/transit/views/file_upload.jst +5 -0
  71. data/app/assets/javascripts/transit/views/video_player.jst +20 -0
  72. data/app/assets/javascripts/transit/views/wym_box.jst +4 -0
  73. data/app/assets/javascripts/transit/views/wym_iframe.jst +3 -0
  74. data/app/assets/stylesheets/transit.css.scss.erb +42 -0
  75. data/app/assets/stylesheets/transit/forms.css.scss +66 -0
  76. data/app/assets/stylesheets/transit/media/audio.css.scss +65 -0
  77. data/app/assets/stylesheets/transit/media/video.css.scss +30 -0
  78. data/app/assets/stylesheets/transit/panel.css.scss +100 -0
  79. data/app/assets/stylesheets/transit/ui.css.scss +507 -0
  80. data/app/controllers/pages_controller.rb +3 -0
  81. data/app/controllers/posts_controller.rb +3 -0
  82. data/app/controllers/transit/assets_controller.rb +38 -0
  83. data/app/controllers/transit/contexts_controller.rb +12 -9
  84. data/app/controllers/transit/pages_controller.rb +26 -0
  85. data/app/controllers/transit/posts_controller.rb +31 -0
  86. data/app/controllers/transit/topics_controller.rb +5 -0
  87. data/app/controllers/transit_controller.rb +16 -0
  88. data/app/helpers/transit/admin_helper.rb +43 -0
  89. data/app/helpers/transit/form_helper.rb +17 -0
  90. data/app/helpers/transit/package_helper.rb +41 -0
  91. data/app/helpers/transit/pagination_helper.rb +58 -0
  92. data/app/helpers/transit_helper.rb +42 -0
  93. data/app/models/comment.rb +37 -0
  94. data/app/models/contexts/audio.rb +12 -0
  95. data/app/models/{text.rb → contexts/text.rb} +0 -0
  96. data/app/models/contexts/video.rb +24 -0
  97. data/app/models/topic.rb +19 -0
  98. data/app/models/transit/asset.rb +68 -0
  99. data/{lib → app/models}/transit/context.rb +29 -0
  100. data/app/views/contexts/_text.html.erb +1 -1
  101. data/app/views/posts/index.rss.builder +18 -0
  102. data/app/views/transit/assets/_file.html.erb +4 -0
  103. data/app/views/transit/assets/_image.html.erb +13 -0
  104. data/app/views/transit/assets/create.js.erb +8 -0
  105. data/app/views/transit/assets/destroy.js.erb +3 -0
  106. data/app/views/transit/assets/manage.html.erb +20 -0
  107. data/app/views/transit/contexts/_audio.html.erb +18 -0
  108. data/app/views/transit/contexts/_text.html.erb +6 -0
  109. data/app/views/transit/contexts/_video.html.erb +13 -0
  110. data/app/views/transit/contexts/destroy.js.erb +1 -0
  111. data/app/views/transit/contexts/index.html.erb +5 -0
  112. data/app/views/transit/contexts/new.js.erb +7 -0
  113. data/app/views/transit/contexts/show.html.erb +8 -0
  114. data/app/views/transit/index.html.erb +26 -0
  115. data/app/views/transit/index.js.erb +1 -0
  116. data/app/views/transit/interface/post_panel.html.erb +96 -0
  117. data/app/views/transit/pages/_table.html.erb +7 -0
  118. data/app/views/transit/pages/edit.html.erb +17 -0
  119. data/app/views/transit/pages/index.html.erb +17 -0
  120. data/app/views/transit/pages/update.js.erb +1 -0
  121. data/app/views/transit/posts/_form.html.erb +49 -0
  122. data/app/views/transit/posts/edit.html.erb +14 -0
  123. data/app/views/transit/posts/new.html.erb +21 -0
  124. data/app/views/transit/table.html.erb +13 -0
  125. data/app/views/transit/table.js.erb +8 -0
  126. data/app/views/transit/topics/manage.html.erb +28 -0
  127. data/config/locales/en.yml +22 -0
  128. data/config/routes.rb +3 -3
  129. data/lib/transit.rb +51 -17
  130. data/lib/transit/admin.rb +85 -0
  131. data/lib/transit/builders/form_builder.rb +319 -0
  132. data/lib/transit/builders/jst_builder.rb +38 -0
  133. data/lib/transit/builders/package_builder.rb +45 -0
  134. data/lib/transit/config.rb +20 -0
  135. data/lib/transit/controller/generator.rb +42 -0
  136. data/lib/transit/controller/responder.rb +34 -0
  137. data/lib/transit/core_ext.rb +18 -0
  138. data/lib/transit/errors/resource_not_found.rb +6 -0
  139. data/lib/transit/model/assets.rb +14 -0
  140. data/lib/transit/model/attachments.rb +55 -0
  141. data/lib/transit/model/auto_increment.rb +22 -0
  142. data/lib/transit/model/base.rb +56 -0
  143. data/lib/transit/model/comments.rb +19 -0
  144. data/lib/transit/model/hooks.rb +38 -0
  145. data/lib/transit/model/owners.rb +14 -0
  146. data/lib/transit/model/paginator.rb +92 -0
  147. data/lib/transit/model/topics.rb +14 -0
  148. data/lib/transit/package/page.rb +20 -12
  149. data/lib/transit/package/post.rb +87 -33
  150. data/lib/transit/package/post/validations.rb +14 -0
  151. data/lib/transit/rails/engine.rb +29 -13
  152. data/lib/transit/rails/railtie.rb +31 -0
  153. data/lib/transit/rails/routing.rb +11 -6
  154. data/lib/transit/services.rb +13 -0
  155. data/lib/transit/services/base.rb +14 -0
  156. data/lib/transit/services/facebook.rb +13 -0
  157. data/lib/transit/services/ted.rb +10 -0
  158. data/lib/transit/services/twitter.rb +13 -0
  159. data/lib/transit/services/vimeo.rb +10 -0
  160. data/lib/transit/services/you_tube.rb +12 -0
  161. data/lib/transit/version.rb +1 -1
  162. metadata +178 -31
  163. data/app/assets/stylesheets/includes/_compat.scss +0 -24
  164. data/app/assets/stylesheets/includes/_defaults.scss +0 -99
  165. data/app/assets/stylesheets/includes/_global.scss +0 -16
  166. data/app/assets/stylesheets/includes/_imports.scss +0 -27
  167. data/app/assets/stylesheets/includes/_mixins.scss +0 -38
  168. data/app/assets/stylesheets/includes/_setup.scss +0 -85
  169. data/app/assets/stylesheets/layout.css.scss +0 -29
  170. data/app/assets/stylesheets/transit.css +0 -3
  171. data/app/controllers/application_controller.rb +0 -5
  172. data/app/controllers/transit/index_controller.rb +0 -7
  173. data/app/controllers/transit/packages_controller.rb +0 -64
  174. data/app/controllers/transit/transit_controller.rb +0 -4
  175. data/app/helpers/routing_helpers.rb +0 -7
  176. data/app/models/audio.rb +0 -4
  177. data/app/models/package_asset.rb +0 -11
  178. data/app/models/video.rb +0 -8
  179. data/app/views/contexts/_audio.html.erb +0 -1
  180. data/app/views/contexts/_video.html.erb +0 -1
  181. data/app/views/layouts/transit.html.erb +0 -31
  182. data/app/views/transit/index/index.html.erb +0 -0
  183. data/lib/transit/helpers/controller_helpers.rb +0 -40
  184. data/lib/transit/helpers/model_helpers.rb +0 -26
  185. data/lib/transit/package.rb +0 -25
  186. data/lib/transit/package/base.rb +0 -49
@@ -0,0 +1,294 @@
1
+ //= require './expose'
2
+
3
+ /**
4
+ * @license
5
+ * jQuery Tools @VERSION Overlay - Overlay base. Extend it.
6
+ *
7
+ * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
8
+ *
9
+ * http://flowplayer.org/tools/overlay/
10
+ *
11
+ * Since: March 2008
12
+ * Date: @DATE
13
+ */
14
+ (function($) {
15
+
16
+ // static constructs
17
+ $.tools = $.tools || {version: '@VERSION'};
18
+
19
+ $.tools.overlay = {
20
+
21
+ addEffect: function(name, loadFn, closeFn) {
22
+ effects[name] = [loadFn, closeFn];
23
+ },
24
+
25
+ conf: {
26
+ close: null,
27
+ closeOnClick: true,
28
+ closeOnEsc: true,
29
+ closeSpeed: 'fast',
30
+ effect: 'default',
31
+
32
+ // since 1.2. fixed positioning not supported by IE6
33
+ fixed: !$.browser.msie || $.browser.version > 6,
34
+
35
+ left: 'center',
36
+ load: false, // 1.2
37
+ mask: null,
38
+ oneInstance: true,
39
+ speed: 'normal',
40
+ target: null, // target element to be overlayed. by default taken from [rel]
41
+ top: '10%'
42
+ }
43
+ };
44
+
45
+
46
+ var instances = [], effects = {};
47
+
48
+ // the default effect. nice and easy!
49
+ $.tools.overlay.addEffect('default',
50
+
51
+ /*
52
+ onLoad/onClose functions must be called otherwise none of the
53
+ user supplied callback methods won't be called
54
+ */
55
+ function(pos, onLoad) {
56
+
57
+ var conf = this.getConf(),
58
+ w = $(window);
59
+
60
+ if (!conf.fixed) {
61
+ pos.top += w.scrollTop();
62
+ pos.left += w.scrollLeft();
63
+ }
64
+
65
+ pos.position = conf.fixed ? 'fixed' : 'absolute';
66
+ this.getOverlay().css(pos).fadeIn(conf.speed, onLoad);
67
+
68
+ }, function(onClose) {
69
+ this.getOverlay().fadeOut(this.getConf().closeSpeed, onClose);
70
+ }
71
+ );
72
+
73
+
74
+ function Overlay(trigger, conf) {
75
+
76
+ // private variables
77
+ var self = this,
78
+ fire = trigger.add(self),
79
+ w = $(window),
80
+ closers,
81
+ overlay,
82
+ opened,
83
+ maskConf = $.tools.expose && (conf.mask || conf.expose),
84
+ uid = Math.random().toString().slice(10);
85
+
86
+
87
+ // mask configuration
88
+ if (maskConf) {
89
+ if (typeof maskConf == 'string') { maskConf = {color: maskConf}; }
90
+ maskConf.closeOnClick = maskConf.closeOnEsc = false;
91
+ }
92
+
93
+ // get overlay and triggerr
94
+ var jq = conf.target || trigger.attr("rel");
95
+ overlay = jq ? $(jq) : null || trigger;
96
+
97
+ // overlay not found. cannot continue
98
+ if (!overlay.length) { throw "Could not find Overlay: " + jq; }
99
+
100
+ // trigger's click event
101
+ if (trigger && trigger.index(overlay) == -1) {
102
+ trigger.click(function(e) {
103
+ self.load(e);
104
+ return e.preventDefault();
105
+ });
106
+ }
107
+
108
+ // API methods
109
+ $.extend(self, {
110
+
111
+ load: function(e) {
112
+
113
+ // can be opened only once
114
+ if (self.isOpened()) { return self; }
115
+
116
+ // find the effect
117
+ var eff = effects[conf.effect];
118
+ if (!eff) { throw "Overlay: cannot find effect : \"" + conf.effect + "\""; }
119
+
120
+ // close other instances?
121
+ if (conf.oneInstance) {
122
+ $.each(instances, function() {
123
+ this.close(e);
124
+ });
125
+ }
126
+
127
+ // onBeforeLoad
128
+ e = e || $.Event();
129
+ e.type = "onBeforeLoad";
130
+ fire.trigger(e);
131
+ if (e.isDefaultPrevented()) { return self; }
132
+
133
+ // opened
134
+ opened = true;
135
+
136
+ // possible mask effect
137
+ if (maskConf) { $(overlay).expose(maskConf); }
138
+
139
+ // position & dimensions
140
+ var top = conf.top,
141
+ left = conf.left,
142
+ oWidth = overlay.outerWidth({margin:true}),
143
+ oHeight = overlay.outerHeight({margin:true});
144
+
145
+ if (typeof top == 'string') {
146
+ top = top == 'center' ? Math.max((w.height() - oHeight) / 2, 0) :
147
+ parseInt(top, 10) / 100 * w.height();
148
+ }
149
+
150
+ if (left == 'center') { left = Math.max((w.width() - oWidth) / 2, 0); }
151
+
152
+
153
+ // load effect
154
+ eff[0].call(self, {top: top, left: left}, function() {
155
+ if (opened) {
156
+ e.type = "onLoad";
157
+ fire.trigger(e);
158
+ }
159
+ });
160
+
161
+ // mask.click closes overlay
162
+ if (maskConf && conf.closeOnClick) {
163
+ $.mask.getMask().one("click", self.close);
164
+ }
165
+
166
+ // when window is clicked outside overlay, we close
167
+ if (conf.closeOnClick) {
168
+ $(document).bind("click." + uid, function(e) {
169
+ if (!$(e.target).parents(overlay).length) {
170
+ self.close(e);
171
+ }
172
+ });
173
+ }
174
+
175
+ // keyboard::escape
176
+ if (conf.closeOnEsc) {
177
+
178
+ // one callback is enough if multiple instances are loaded simultaneously
179
+ $(document).bind("keydown." + uid, function(e) {
180
+ if (e.keyCode == 27) {
181
+ self.close(e);
182
+ }
183
+ });
184
+ }
185
+
186
+
187
+ return self;
188
+ },
189
+
190
+ close: function(e) {
191
+
192
+ if (!self.isOpened()) { return self; }
193
+
194
+ e = e || $.Event();
195
+ e.type = "onBeforeClose";
196
+ fire.trigger(e);
197
+ if (e.isDefaultPrevented()) { return; }
198
+
199
+ opened = false;
200
+
201
+ // close effect
202
+ effects[conf.effect][1].call(self, function() {
203
+ e.type = "onClose";
204
+ fire.trigger(e);
205
+ });
206
+
207
+ // unbind the keyboard / clicking actions
208
+ $(document).unbind("click." + uid).unbind("keydown." + uid);
209
+
210
+ if (maskConf) {
211
+ $.mask.close();
212
+ }
213
+
214
+ return self;
215
+ },
216
+
217
+ getOverlay: function() {
218
+ return overlay;
219
+ },
220
+
221
+ getTrigger: function() {
222
+ return trigger;
223
+ },
224
+
225
+ getClosers: function() {
226
+ return closers;
227
+ },
228
+
229
+ isOpened: function() {
230
+ return opened;
231
+ },
232
+
233
+ // manipulate start, finish and speeds
234
+ getConf: function() {
235
+ return conf;
236
+ }
237
+
238
+ });
239
+
240
+ // callbacks
241
+ $.each("onBeforeLoad,onStart,onLoad,onBeforeClose,onClose".split(","), function(i, name) {
242
+
243
+ // configuration
244
+ if ($.isFunction(conf[name])) {
245
+ $(self).bind(name, conf[name]);
246
+ }
247
+
248
+ // API
249
+ self[name] = function(fn) {
250
+ if (fn) { $(self).bind(name, fn); }
251
+ return self;
252
+ };
253
+ });
254
+
255
+ // close button
256
+ closers = overlay.find(conf.close || ".close");
257
+
258
+ if (!closers.length && !conf.close) {
259
+ closers = $('<a class="close"></a>');
260
+ overlay.prepend(closers);
261
+ }
262
+
263
+ closers.click(function(e) {
264
+ self.close(e);
265
+ });
266
+
267
+ // autoload
268
+ if (conf.load) { self.load(); }
269
+
270
+ }
271
+
272
+ // jQuery plugin initialization
273
+ $.fn.overlay = function(conf) {
274
+
275
+ // already constructed --> return API
276
+ var el = this.data("overlay");
277
+ if (el) { return el; }
278
+
279
+ if ($.isFunction(conf)) {
280
+ conf = {onBeforeLoad: conf};
281
+ }
282
+
283
+ conf = $.extend(true, {}, $.tools.overlay.conf, conf);
284
+
285
+ this.each(function() {
286
+ el = new Overlay($(this), conf);
287
+ instances.push(el);
288
+ $(this).data("overlay", el);
289
+ });
290
+
291
+ return conf.api ? el: this;
292
+ };
293
+
294
+ })(jQuery);
@@ -0,0 +1,3 @@
1
+ //= require './scrollable/base'
2
+ //= require './scrollable/navigator'
3
+ //= require './scrollable/autoscroll'
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @license
3
+ * jQuery Tools @VERSION / Scrollable Autoscroll
4
+ *
5
+ * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
6
+ *
7
+ * http://flowplayer.org/tools/scrollable/autoscroll.html
8
+ *
9
+ * Since: September 2009
10
+ * Date: @DATE
11
+ */
12
+ (function($) {
13
+
14
+ var t = $.tools.scrollable;
15
+
16
+ t.autoscroll = {
17
+
18
+ conf: {
19
+ autoplay: true,
20
+ interval: 3000,
21
+ autopause: true
22
+ }
23
+ };
24
+
25
+ // jQuery plugin implementation
26
+ $.fn.autoscroll = function(conf) {
27
+
28
+ if (typeof conf == 'number') {
29
+ conf = {interval: conf};
30
+ }
31
+
32
+ var opts = $.extend({}, t.autoscroll.conf, conf), ret;
33
+
34
+ this.each(function() {
35
+
36
+ var api = $(this).data("scrollable");
37
+ if (api) { ret = api; }
38
+
39
+ // interval stuff
40
+ var timer, stopped = true;
41
+
42
+ api.play = function() {
43
+
44
+ // do not start additional timer if already exists
45
+ if (timer) { return; }
46
+
47
+ stopped = false;
48
+
49
+ // construct new timer
50
+ timer = setInterval(function() {
51
+ api.next();
52
+ }, opts.interval);
53
+
54
+ };
55
+
56
+ api.pause = function() {
57
+ timer = clearInterval(timer);
58
+ };
59
+
60
+ // when stopped - mouseover won't restart
61
+ api.stop = function() {
62
+ api.pause();
63
+ stopped = true;
64
+ };
65
+
66
+ /* when mouse enters, autoscroll stops */
67
+ if (opts.autopause) {
68
+ api.getRoot().add(api.getNaviButtons()).hover(api.pause, api.play);
69
+ }
70
+
71
+ if (opts.autoplay) {
72
+ api.play();
73
+ }
74
+
75
+ });
76
+
77
+ return opts.api ? ret : this;
78
+
79
+ };
80
+
81
+ })(jQuery);
@@ -0,0 +1,335 @@
1
+ /**
2
+ * @license
3
+ * jQuery Tools @VERSION Scrollable - New wave UI design
4
+ *
5
+ * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
6
+ *
7
+ * http://flowplayer.org/tools/scrollable.html
8
+ *
9
+ * Since: March 2008
10
+ * Date: @DATE
11
+ */
12
+ (function($) {
13
+
14
+ // static constructs
15
+ $.tools = $.tools || {version: '@VERSION'};
16
+
17
+ $.tools.scrollable = {
18
+
19
+ conf: {
20
+ activeClass: 'active',
21
+ circular: false,
22
+ clonedClass: 'cloned',
23
+ disabledClass: 'disabled',
24
+ easing: 'swing',
25
+ initialIndex: 0,
26
+ item: null,
27
+ items: '.items',
28
+ keyboard: true,
29
+ mousewheel: false,
30
+ next: '.next',
31
+ prev: '.prev',
32
+ speed: 400,
33
+ vertical: false,
34
+ touch: true,
35
+ wheelSpeed: 0
36
+ }
37
+ };
38
+
39
+ // get hidden element's width or height even though it's hidden
40
+ function dim(el, key) {
41
+ var v = parseInt(el.css(key), 10);
42
+ if (v) { return v; }
43
+ var s = el[0].currentStyle;
44
+ return s && s.width && parseInt(s.width, 10);
45
+ }
46
+
47
+ function find(root, query) {
48
+ var el = $(query);
49
+ return el.length < 2 ? el : root.parent().find(query);
50
+ }
51
+
52
+ var current;
53
+
54
+ // constructor
55
+ function Scrollable(root, conf) {
56
+
57
+ // current instance
58
+ var self = this,
59
+ fire = root.add(self),
60
+ itemWrap = root.children(),
61
+ index = 0,
62
+ vertical = conf.vertical;
63
+
64
+ if (!current) { current = self; }
65
+ if (itemWrap.length > 1) { itemWrap = $(conf.items, root); }
66
+
67
+ // methods
68
+ $.extend(self, {
69
+
70
+ getConf: function() {
71
+ return conf;
72
+ },
73
+
74
+ getIndex: function() {
75
+ return index;
76
+ },
77
+
78
+ getSize: function() {
79
+ return self.getItems().size();
80
+ },
81
+
82
+ getNaviButtons: function() {
83
+ return prev.add(next);
84
+ },
85
+
86
+ getRoot: function() {
87
+ return root;
88
+ },
89
+
90
+ getItemWrap: function() {
91
+ return itemWrap;
92
+ },
93
+
94
+ getItems: function() {
95
+ return itemWrap.children(conf.item).not("." + conf.clonedClass);
96
+ },
97
+
98
+ move: function(offset, time) {
99
+ return self.seekTo(index + offset, time);
100
+ },
101
+
102
+ next: function(time) {
103
+ return self.move(1, time);
104
+ },
105
+
106
+ prev: function(time) {
107
+ return self.move(-1, time);
108
+ },
109
+
110
+ begin: function(time) {
111
+ return self.seekTo(0, time);
112
+ },
113
+
114
+ end: function(time) {
115
+ return self.seekTo(self.getSize() -1, time);
116
+ },
117
+
118
+ focus: function() {
119
+ current = self;
120
+ return self;
121
+ },
122
+
123
+ addItem: function(item) {
124
+ item = $(item);
125
+
126
+ if (!conf.circular) {
127
+ itemWrap.append(item);
128
+ } else {
129
+ itemWrap.children("." + conf.clonedClass + ":last").before(item);
130
+ itemWrap.children("." + conf.clonedClass + ":first").replaceWith(item.clone().addClass(conf.clonedClass));
131
+ }
132
+
133
+ fire.trigger("onAddItem", [item]);
134
+ return self;
135
+ },
136
+
137
+
138
+ /* all seeking functions depend on this */
139
+ seekTo: function(i, time, fn) {
140
+
141
+ // ensure numeric index
142
+ if (!i.jquery) { i *= 1; }
143
+
144
+ // avoid seeking from end clone to the beginning
145
+ if (conf.circular && i === 0 && index == -1 && time !== 0) { return self; }
146
+
147
+ // check that index is sane
148
+ if (!conf.circular && i < 0 || i > self.getSize() || i < -1) { return self; }
149
+
150
+ var item = i;
151
+
152
+ if (i.jquery) {
153
+ i = self.getItems().index(i);
154
+
155
+ } else {
156
+ item = self.getItems().eq(i);
157
+ }
158
+
159
+ // onBeforeSeek
160
+ var e = $.Event("onBeforeSeek");
161
+ if (!fn) {
162
+ fire.trigger(e, [i, time]);
163
+ if (e.isDefaultPrevented() || !item.length) { return self; }
164
+ }
165
+
166
+ var props = vertical ? {top: -item.position().top} : {left: -item.position().left};
167
+
168
+ index = i;
169
+ current = self;
170
+ if (time === undefined) { time = conf.speed; }
171
+
172
+ itemWrap.animate(props, time, conf.easing, fn || function() {
173
+ fire.trigger("onSeek", [i]);
174
+ });
175
+
176
+ return self;
177
+ }
178
+
179
+ });
180
+
181
+ // callbacks
182
+ $.each(['onBeforeSeek', 'onSeek', 'onAddItem'], function(i, name) {
183
+
184
+ // configuration
185
+ if ($.isFunction(conf[name])) {
186
+ $(self).bind(name, conf[name]);
187
+ }
188
+
189
+ self[name] = function(fn) {
190
+ if (fn) { $(self).bind(name, fn); }
191
+ return self;
192
+ };
193
+ });
194
+
195
+ // circular loop
196
+ if (conf.circular) {
197
+
198
+ var cloned1 = self.getItems().slice(-1).clone().prependTo(itemWrap),
199
+ cloned2 = self.getItems().eq(1).clone().appendTo(itemWrap);
200
+
201
+ cloned1.add(cloned2).addClass(conf.clonedClass);
202
+
203
+ self.onBeforeSeek(function(e, i, time) {
204
+
205
+
206
+ if (e.isDefaultPrevented()) { return; }
207
+
208
+ /*
209
+ 1. animate to the clone without event triggering
210
+ 2. seek to correct position with 0 speed
211
+ */
212
+ if (i == -1) {
213
+ self.seekTo(cloned1, time, function() {
214
+ self.end(0);
215
+ });
216
+ return e.preventDefault();
217
+
218
+ } else if (i == self.getSize()) {
219
+ self.seekTo(cloned2, time, function() {
220
+ self.begin(0);
221
+ });
222
+ }
223
+
224
+ });
225
+
226
+ // seek over the cloned item
227
+ self.seekTo(0, 0, function() {});
228
+ }
229
+
230
+ // next/prev buttons
231
+ var prev = find(root, conf.prev).click(function() { self.prev(); }),
232
+ next = find(root, conf.next).click(function() { self.next(); });
233
+
234
+ if (!conf.circular && self.getSize() > 1) {
235
+
236
+ self.onBeforeSeek(function(e, i) {
237
+ setTimeout(function() {
238
+ if (!e.isDefaultPrevented()) {
239
+ prev.toggleClass(conf.disabledClass, i <= 0);
240
+ next.toggleClass(conf.disabledClass, i >= self.getSize() -1);
241
+ }
242
+ }, 1);
243
+ });
244
+
245
+ if (!conf.initialIndex) {
246
+ prev.addClass(conf.disabledClass);
247
+ }
248
+ }
249
+
250
+ // mousewheel support
251
+ if (conf.mousewheel && $.fn.mousewheel) {
252
+ root.mousewheel(function(e, delta) {
253
+ if (conf.mousewheel) {
254
+ self.move(delta < 0 ? 1 : -1, conf.wheelSpeed || 50);
255
+ return false;
256
+ }
257
+ });
258
+ }
259
+
260
+ // touch event
261
+ if (conf.touch) {
262
+ var touch = {};
263
+
264
+ itemWrap[0].ontouchstart = function(e) {
265
+ var t = e.touches[0];
266
+ touch.x = t.clientX;
267
+ touch.y = t.clientY;
268
+ };
269
+
270
+ itemWrap[0].ontouchmove = function(e) {
271
+
272
+ // only deal with one finger
273
+ if (e.touches.length == 1 && !itemWrap.is(":animated")) {
274
+ var t = e.touches[0],
275
+ deltaX = touch.x - t.clientX,
276
+ deltaY = touch.y - t.clientY;
277
+
278
+ self[vertical && deltaY > 0 || !vertical && deltaX > 0 ? 'next' : 'prev']();
279
+ e.preventDefault();
280
+ }
281
+ };
282
+ }
283
+
284
+ if (conf.keyboard) {
285
+
286
+ $(document).bind("keydown.scrollable", function(evt) {
287
+
288
+ // skip certain conditions
289
+ if (!conf.keyboard || evt.altKey || evt.ctrlKey || $(evt.target).is(":input")) { return; }
290
+
291
+ // does this instance have focus?
292
+ if (conf.keyboard != 'static' && current != self) { return; }
293
+
294
+ var key = evt.keyCode;
295
+
296
+ if (vertical && (key == 38 || key == 40)) {
297
+ self.move(key == 38 ? -1 : 1);
298
+ return evt.preventDefault();
299
+ }
300
+
301
+ if (!vertical && (key == 37 || key == 39)) {
302
+ self.move(key == 37 ? -1 : 1);
303
+ return evt.preventDefault();
304
+ }
305
+
306
+ });
307
+ }
308
+
309
+ // initial index
310
+ if (conf.initialIndex) {
311
+ self.seekTo(conf.initialIndex, 0, function() {});
312
+ }
313
+ }
314
+
315
+
316
+ // jQuery plugin implementation
317
+ $.fn.scrollable = function(conf) {
318
+
319
+ // already constructed --> return API
320
+ var el = this.data("scrollable");
321
+ if (el) { return el; }
322
+
323
+ conf = $.extend({}, $.tools.scrollable.conf, conf);
324
+
325
+ this.each(function() {
326
+ el = new Scrollable($(this), conf);
327
+ $(this).data("scrollable", el);
328
+ });
329
+
330
+ return conf.api ? el: this;
331
+
332
+ };
333
+
334
+
335
+ })(jQuery);