transit 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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);