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,139 @@
1
+ /**
2
+ * @license
3
+ * jQuery Tools @VERSION / Scrollable Navigator
4
+ *
5
+ * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
6
+ *
7
+ * http://flowplayer.org/tools/scrollable/navigator.html
8
+ *
9
+ * Since: September 2009
10
+ * Date: @DATE
11
+ */
12
+ (function($) {
13
+
14
+ var t = $.tools.scrollable;
15
+
16
+ t.navigator = {
17
+
18
+ conf: {
19
+ navi: '.navi',
20
+ naviItem: null,
21
+ activeClass: 'active',
22
+ indexed: false,
23
+ idPrefix: null,
24
+
25
+ // 1.2
26
+ history: false
27
+ }
28
+ };
29
+
30
+ function find(root, query) {
31
+ var el = $(query);
32
+ return el.length < 2 ? el : root.parent().find(query);
33
+ }
34
+
35
+ // jQuery plugin implementation
36
+ $.fn.navigator = function(conf) {
37
+
38
+ // configuration
39
+ if (typeof conf == 'string') { conf = {navi: conf}; }
40
+ conf = $.extend({}, t.navigator.conf, conf);
41
+
42
+ var ret;
43
+
44
+ this.each(function() {
45
+
46
+ var api = $(this).data("scrollable"),
47
+ navi = conf.navi.jquery ? conf.navi : find(api.getRoot(), conf.navi),
48
+ buttons = api.getNaviButtons(),
49
+ cls = conf.activeClass,
50
+ history = conf.history && $.fn.history;
51
+
52
+ // @deprecated stuff
53
+ if (api) { ret = api; }
54
+
55
+ api.getNaviButtons = function() {
56
+ return buttons.add(navi);
57
+ };
58
+
59
+
60
+ function doClick(el, i, e) {
61
+ api.seekTo(i);
62
+ if (history) {
63
+ if (location.hash) {
64
+ location.hash = el.attr("href").replace("#", "");
65
+ }
66
+ } else {
67
+ return e.preventDefault();
68
+ }
69
+ }
70
+
71
+ function els() {
72
+ return navi.find(conf.naviItem || '> *');
73
+ }
74
+
75
+ function addItem(i) {
76
+
77
+ var item = $("<" + (conf.naviItem || 'a') + "/>").click(function(e) {
78
+ doClick($(this), i, e);
79
+
80
+ }).attr("href", "#" + i);
81
+
82
+ // index number / id attribute
83
+ if (i === 0) { item.addClass(cls); }
84
+ if (conf.indexed) { item.text(i + 1); }
85
+ if (conf.idPrefix) { item.attr("id", conf.idPrefix + i); }
86
+
87
+ return item.appendTo(navi);
88
+ }
89
+
90
+
91
+ // generate navigator
92
+ if (els().length) {
93
+ els().each(function(i) {
94
+ $(this).click(function(e) {
95
+ doClick($(this), i, e);
96
+ });
97
+ });
98
+
99
+ } else {
100
+ $.each(api.getItems(), function(i) {
101
+ addItem(i);
102
+ });
103
+ }
104
+
105
+ // activate correct entry
106
+ api.onBeforeSeek(function(e, index) {
107
+ setTimeout(function() {
108
+ if (!e.isDefaultPrevented()) {
109
+ var el = els().eq(index);
110
+ if (!e.isDefaultPrevented() && el.length) {
111
+ els().removeClass(cls).eq(index).addClass(cls);
112
+ }
113
+ }
114
+ }, 1);
115
+ });
116
+
117
+ function doHistory(evt, hash) {
118
+ var el = els().eq(hash.replace("#", ""));
119
+ if (!el.length) {
120
+ el = els().filter("[href=" + hash + "]");
121
+ }
122
+ el.click();
123
+ }
124
+
125
+ // new item being added
126
+ api.onAddItem(function(e, item) {
127
+ item = addItem(api.getItems().index(item));
128
+ if (history) { item.history(doHistory); }
129
+ });
130
+
131
+ if (history) { els().history(doHistory); }
132
+
133
+ });
134
+
135
+ return conf.api ? ret : this;
136
+
137
+ };
138
+
139
+ })(jQuery);
@@ -0,0 +1,590 @@
1
+ /**
2
+ * @license
3
+ * jQuery Tools Validator @VERSION - HTML5 is here. Now use it.
4
+ *
5
+ * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
6
+ *
7
+ * http://flowplayer.org/tools/form/validator/
8
+ *
9
+ * Since: Mar 2010
10
+ * Date: @DATE
11
+ */
12
+ /*jslint evil: true */
13
+ (function($) {
14
+
15
+ $.tools = $.tools || {version: '@VERSION'};
16
+
17
+ // globals
18
+ var typeRe = /\[type=([a-z]+)\]/,
19
+ numRe = /^-?[0-9]*(\.[0-9]+)?$/,
20
+ dateInput = $.tools.dateinput,
21
+
22
+ // http://net.tutsplus.com/tutorials/other/8-regular-expressions-you-should-know/
23
+ emailRe = /^([a-z0-9_\.\-\+]+)@([\da-z\.\-]+)\.([a-z\.]{2,6})$/i,
24
+ urlRe = /^(https?:\/\/)?[\da-z\.\-]+\.[a-z\.]{2,6}[#&+_\?\/\w \.\-=]*$/i,
25
+ v;
26
+
27
+ v = $.tools.validator = {
28
+
29
+ conf: {
30
+ grouped: false, // show all error messages at once inside the container
31
+ effect: 'default', // show/hide effect for error message. only 'default' is built-in
32
+ errorClass: 'invalid', // input field class name in case of validation error
33
+
34
+ // when to check for validity?
35
+ inputEvent: null, // change, blur, keyup, null
36
+ errorInputEvent: 'keyup', // change, blur, keyup, null
37
+ formEvent: 'submit', // submit, null
38
+
39
+ lang: 'en', // default language for error messages
40
+ message: '<div/>',
41
+ messageAttr: 'data-message', // name of the attribute for overridden error message
42
+ messageClass: 'error', // error message element's class name
43
+ offset: [0, 0],
44
+ position: 'center right',
45
+ singleError: false, // validate all inputs at once
46
+ speed: 'normal' // message's fade-in speed
47
+ },
48
+
49
+
50
+ /* The Error Messages */
51
+ messages: {
52
+ "*": { en: "Please correct this value" }
53
+ },
54
+
55
+ localize: function(lang, messages) {
56
+ $.each(messages, function(key, msg) {
57
+ v.messages[key] = v.messages[key] || {};
58
+ v.messages[key][lang] = msg;
59
+ });
60
+ },
61
+
62
+ localizeFn: function(key, messages) {
63
+ v.messages[key] = v.messages[key] || {};
64
+ $.extend(v.messages[key], messages);
65
+ },
66
+
67
+ /**
68
+ * Adds a new validator
69
+ */
70
+ fn: function(matcher, msg, fn) {
71
+
72
+ // no message supplied
73
+ if ($.isFunction(msg)) {
74
+ fn = msg;
75
+
76
+ // message(s) on second argument
77
+ } else {
78
+ if (typeof msg == 'string') { msg = {en: msg}; }
79
+ this.messages[matcher.key || matcher] = msg;
80
+ }
81
+
82
+ // check for "[type=xxx]" (not supported by jQuery)
83
+ var test = typeRe.exec(matcher);
84
+ if (test) { matcher = isType(test[1]); }
85
+
86
+ // add validator to the arsenal
87
+ fns.push([matcher, fn]);
88
+ },
89
+
90
+ /* Add new show/hide effect */
91
+ addEffect: function(name, showFn, closeFn) {
92
+ effects[name] = [showFn, closeFn];
93
+ }
94
+
95
+ };
96
+
97
+ /* calculate error message position relative to the input */
98
+ function getPosition(trigger, el, conf) {
99
+
100
+ // get origin top/left position
101
+ var top = trigger.offset().top,
102
+ left = trigger.offset().left,
103
+ pos = conf.position.split(/,?\s+/),
104
+ y = pos[0],
105
+ x = pos[1];
106
+
107
+ top -= el.outerHeight() - conf.offset[0];
108
+ left += trigger.outerWidth() + conf.offset[1];
109
+
110
+
111
+ // iPad position fix
112
+ if (/iPad/i.test(navigator.userAgent)) {
113
+ top -= $(window).scrollTop();
114
+ }
115
+
116
+ // adjust Y
117
+ var height = el.outerHeight() + trigger.outerHeight();
118
+ if (y == 'center') { top += height / 2; }
119
+ if (y == 'bottom') { top += height; }
120
+
121
+ // adjust X
122
+ var width = trigger.outerWidth();
123
+ if (x == 'center') { left -= (width + el.outerWidth()) / 2; }
124
+ if (x == 'left') { left -= width; }
125
+
126
+ return {top: top, left: left};
127
+ }
128
+
129
+
130
+
131
+ // $.is("[type=xxx]") or $.filter("[type=xxx]") not working in jQuery 1.3.2 or 1.4.2
132
+ function isType(type) {
133
+ function fn() {
134
+ return this.getAttribute("type") == type;
135
+ }
136
+ fn.key = "[type=" + type + "]";
137
+ return fn;
138
+ }
139
+
140
+
141
+ var fns = [], effects = {
142
+
143
+ 'default' : [
144
+
145
+ // show errors function
146
+ function(errs) {
147
+
148
+ var conf = this.getConf();
149
+
150
+ // loop errors
151
+ $.each(errs, function(i, err) {
152
+
153
+ // add error class
154
+ var input = err.input;
155
+ input.addClass(conf.errorClass);
156
+
157
+ // get handle to the error container
158
+ var msg = input.data("msg.el");
159
+
160
+ // create it if not present
161
+ if (!msg) {
162
+ msg = $(conf.message).addClass(conf.messageClass).appendTo(document.body);
163
+ input.data("msg.el", msg);
164
+ }
165
+
166
+ // clear the container
167
+ msg.css({visibility: 'hidden'}).find("p").remove();
168
+
169
+ // populate messages
170
+ $.each(err.messages, function(i, m) {
171
+ $("<p/>").html(m).appendTo(msg);
172
+ });
173
+
174
+ // make sure the width is not full body width so it can be positioned correctly
175
+ if (msg.outerWidth() == msg.parent().width()) {
176
+ msg.add(msg.find("p")).css({display: 'inline'});
177
+ }
178
+
179
+ // insert into correct position (relative to the field)
180
+ var pos = getPosition(input, msg, conf);
181
+
182
+ msg.css({ visibility: 'visible', position: 'absolute', top: pos.top, left: pos.left })
183
+ .fadeIn(conf.speed);
184
+ });
185
+
186
+
187
+ // hide errors function
188
+ }, function(inputs) {
189
+
190
+ var conf = this.getConf();
191
+ inputs.removeClass(conf.errorClass).each(function() {
192
+ var msg = $(this).data("msg.el");
193
+ if (msg) { msg.css({visibility: 'hidden'}); }
194
+ });
195
+ }
196
+ ]
197
+ };
198
+
199
+
200
+ /* sperial selectors */
201
+ $.each("email,url,number".split(","), function(i, key) {
202
+ $.expr[':'][key] = function(el) {
203
+ return el.getAttribute("type") === key;
204
+ };
205
+ });
206
+
207
+
208
+ /*
209
+ oninvalid() jQuery plugin.
210
+ Usage: $("input:eq(2)").oninvalid(function() { ... });
211
+ */
212
+ $.fn.oninvalid = function( fn ){
213
+ return this[fn ? "bind" : "trigger"]("OI", fn);
214
+ };
215
+
216
+
217
+ /******* built-in HTML5 standard validators *********/
218
+
219
+ v.fn(":email", "Please enter a valid email address", function(el, v) {
220
+ return !v || emailRe.test(v);
221
+ });
222
+
223
+ v.fn(":url", "Please enter a valid URL", function(el, v) {
224
+ return !v || urlRe.test(v);
225
+ });
226
+
227
+ v.fn(":number", "Please enter a numeric value.", function(el, v) {
228
+ return numRe.test(v);
229
+ });
230
+
231
+ v.fn("[max]", "Please enter a value smaller than $1", function(el, v) {
232
+
233
+ // skip empty values and dateinputs
234
+ if (v === '' || dateInput && el.is(":date")) { return true; }
235
+
236
+ var max = el.attr("max");
237
+ return parseFloat(v) <= parseFloat(max) ? true : [max];
238
+ });
239
+
240
+ v.fn("[min]", "Please enter a value larger than $1", function(el, v) {
241
+
242
+ // skip empty values and dateinputs
243
+ if (v === '' || dateInput && el.is(":date")) { return true; }
244
+
245
+ var min = el.attr("min");
246
+ return parseFloat(v) >= parseFloat(min) ? true : [min];
247
+ });
248
+
249
+ v.fn("[required]", "Please complete this mandatory field.", function(el, v) {
250
+ if (el.is(":checkbox")) { return el.is(":checked"); }
251
+ return !!v;
252
+ });
253
+
254
+ v.fn("[pattern]", function(el) {
255
+ var p = new RegExp("^" + el.attr("pattern") + "$");
256
+ return p.test(el.val());
257
+ });
258
+
259
+
260
+ function Validator(inputs, form, conf) {
261
+
262
+ // private variables
263
+ var self = this,
264
+ fire = form.add(self);
265
+
266
+ // make sure there are input fields available
267
+ inputs = inputs.not(":button, :image, :reset, :submit");
268
+
269
+ // utility function
270
+ function pushMessage(to, matcher, returnValue) {
271
+
272
+ // only one message allowed
273
+ if (!conf.grouped && to.length) { return; }
274
+
275
+ // the error message
276
+ var msg;
277
+
278
+ // substitutions are returned
279
+ if (returnValue === false || $.isArray(returnValue)) {
280
+ msg = v.messages[matcher.key || matcher] || v.messages["*"];
281
+ msg = msg[conf.lang] || v.messages["*"].en;
282
+
283
+ // substitution
284
+ var matches = msg.match(/\$\d/g);
285
+
286
+ if (matches && $.isArray(returnValue)) {
287
+ $.each(matches, function(i) {
288
+ msg = msg.replace(this, returnValue[i]);
289
+ });
290
+ }
291
+
292
+ // error message is returned directly
293
+ } else {
294
+ msg = returnValue[conf.lang] || returnValue;
295
+ }
296
+
297
+ to.push(msg);
298
+ }
299
+
300
+
301
+ // API methods
302
+ $.extend(self, {
303
+
304
+ getConf: function() {
305
+ return conf;
306
+ },
307
+
308
+ getForm: function() {
309
+ return form;
310
+ },
311
+
312
+ getInputs: function() {
313
+ return inputs;
314
+ },
315
+
316
+ reflow: function() {
317
+ inputs.each(function() {
318
+ var input = $(this),
319
+ msg = input.data("msg.el");
320
+
321
+ if (msg) {
322
+ var pos = getPosition(input, msg, conf);
323
+ msg.css({ top: pos.top, left: pos.left });
324
+ }
325
+ });
326
+ return self;
327
+ },
328
+
329
+ /* @param e - for internal use only */
330
+ invalidate: function(errs, e) {
331
+
332
+ // errors are given manually: { fieldName1: 'message1', fieldName2: 'message2' }
333
+ if (!e) {
334
+ var errors = [];
335
+ $.each(errs, function(key, val) {
336
+ var input = inputs.filter("[name='" + key + "']");
337
+ if (input.length) {
338
+
339
+ // trigger HTML5 ininvalid event
340
+ input.trigger("OI", [val]);
341
+
342
+ errors.push({ input: input, messages: [val]});
343
+ }
344
+ });
345
+
346
+ errs = errors;
347
+ e = $.Event();
348
+ }
349
+
350
+ // onFail callback
351
+ e.type = "onFail";
352
+ fire.trigger(e, [errs]);
353
+
354
+ // call the effect
355
+ if (!e.isDefaultPrevented()) {
356
+ effects[conf.effect][0].call(self, errs, e);
357
+ }
358
+
359
+ return self;
360
+ },
361
+
362
+ reset: function(els) {
363
+ els = els || inputs;
364
+ els.removeClass(conf.errorClass).each(function() {
365
+ var msg = $(this).data("msg.el");
366
+ if (msg) {
367
+ msg.remove();
368
+ $(this).data("msg.el", null);
369
+ }
370
+ }).unbind(conf.errorInputEvent || '');
371
+ return self;
372
+ },
373
+
374
+ destroy: function() {
375
+ form.unbind(conf.formEvent + ".V").unbind("reset.V");
376
+ inputs.unbind(conf.inputEvent + ".V").unbind("change.V");
377
+ return self.reset();
378
+ },
379
+
380
+
381
+ //{{{ checkValidity() - flesh and bone of this tool
382
+
383
+ /* @returns boolean */
384
+ checkValidity: function(els, e) {
385
+
386
+ els = els || inputs;
387
+ els = els.not(":disabled");
388
+ if (!els.length) { return true; }
389
+
390
+ e = e || $.Event();
391
+
392
+ // onBeforeValidate
393
+ e.type = "onBeforeValidate";
394
+ fire.trigger(e, [els]);
395
+ if (e.isDefaultPrevented()) { return e.result; }
396
+
397
+ // container for errors
398
+ var errs = [];
399
+
400
+ // loop trough the inputs
401
+ els.not(":radio:not(:checked)").each(function() {
402
+
403
+ // field and it's error message container
404
+ var msgs = [],
405
+ el = $(this).data("messages", msgs),
406
+ event = dateInput && el.is(":date") ? "onHide.v" : conf.errorInputEvent + ".v";
407
+
408
+ // cleanup previous validation event
409
+ el.unbind(event);
410
+
411
+
412
+ // loop all validator functions
413
+ $.each(fns, function() {
414
+ var fn = this, match = fn[0];
415
+
416
+ // match found
417
+ if (el.filter(match).length) {
418
+
419
+ // execute a validator function
420
+ var returnValue = fn[1].call(self, el, el.val());
421
+
422
+
423
+ // validation failed. multiple substitutions can be returned with an array
424
+ if (returnValue !== true) {
425
+
426
+ // onBeforeFail
427
+ e.type = "onBeforeFail";
428
+ fire.trigger(e, [el, match]);
429
+ if (e.isDefaultPrevented()) { return false; }
430
+
431
+ // overridden custom message
432
+ var msg = el.attr(conf.messageAttr);
433
+ if (msg) {
434
+ msgs = [msg];
435
+ return false;
436
+ } else {
437
+ pushMessage(msgs, match, returnValue);
438
+ }
439
+ }
440
+ }
441
+ });
442
+
443
+ if (msgs.length) {
444
+
445
+ errs.push({input: el, messages: msgs});
446
+
447
+ // trigger HTML5 ininvalid event
448
+ el.trigger("OI", [msgs]);
449
+
450
+ // begin validating upon error event type (such as keyup)
451
+ if (conf.errorInputEvent) {
452
+ el.bind(event, function(e) {
453
+ self.checkValidity(el, e);
454
+ });
455
+ }
456
+ }
457
+
458
+ if (conf.singleError && errs.length) { return false; }
459
+
460
+ });
461
+
462
+
463
+ // validation done. now check that we have a proper effect at hand
464
+ var eff = effects[conf.effect];
465
+ if (!eff) { throw "Validator: cannot find effect \"" + conf.effect + "\""; }
466
+
467
+ // errors found
468
+ if (errs.length) {
469
+ self.invalidate(errs, e);
470
+ return false;
471
+
472
+ // no errors
473
+ } else {
474
+
475
+ // call the effect
476
+ eff[1].call(self, els, e);
477
+
478
+ // onSuccess callback
479
+ e.type = "onSuccess";
480
+ fire.trigger(e, [els]);
481
+
482
+ els.unbind(conf.errorInputEvent + ".v");
483
+ }
484
+
485
+ return true;
486
+ }
487
+ //}}}
488
+
489
+ });
490
+
491
+ // callbacks
492
+ $.each("onBeforeValidate,onBeforeFail,onFail,onSuccess".split(","), function(i, name) {
493
+
494
+ // configuration
495
+ if ($.isFunction(conf[name])) {
496
+ $(self).bind(name, conf[name]);
497
+ }
498
+
499
+ // API methods
500
+ self[name] = function(fn) {
501
+ if (fn) { $(self).bind(name, fn); }
502
+ return self;
503
+ };
504
+ });
505
+
506
+
507
+ // form validation
508
+ if (conf.formEvent) {
509
+ form.bind(conf.formEvent + ".V", function(e) {
510
+ if (!self.checkValidity(null, e)) {
511
+ return e.preventDefault();
512
+ }
513
+ });
514
+ }
515
+
516
+ // form reset
517
+ form.bind("reset.V", function() {
518
+ self.reset();
519
+ });
520
+
521
+ // disable browser's default validation mechanism
522
+ if (inputs[0] && inputs[0].validity) {
523
+ inputs.each(function() {
524
+ this.oninvalid = function() {
525
+ return false;
526
+ };
527
+ });
528
+ }
529
+
530
+ // Web Forms 2.0 compatibility
531
+ if (form[0]) {
532
+ form[0].checkValidity = self.checkValidity;
533
+ }
534
+
535
+ // input validation
536
+ if (conf.inputEvent) {
537
+ inputs.bind(conf.inputEvent + ".V", function(e) {
538
+ self.checkValidity($(this), e);
539
+ });
540
+ }
541
+
542
+ // checkboxes, selects and radios are checked separately
543
+ inputs.filter(":checkbox, select").filter("[required]").bind("change.V", function(e) {
544
+ var el = $(this);
545
+ if (this.checked || (el.is("select") && $(this).val())) {
546
+ effects[conf.effect][1].call(self, el, e);
547
+ }
548
+ });
549
+
550
+ var radios = inputs.filter(":radio").change(function(e) {
551
+ self.checkValidity(radios, e);
552
+ });
553
+
554
+ // reposition tooltips when window is resized
555
+ $(window).resize(function() {
556
+ self.reflow();
557
+ });
558
+ }
559
+
560
+
561
+ // jQuery plugin initialization
562
+ $.fn.validator = function(conf) {
563
+
564
+ var instance = this.data("validator");
565
+
566
+ // destroy existing instance
567
+ if (instance) {
568
+ instance.destroy();
569
+ this.removeData("validator");
570
+ }
571
+
572
+ // configuration
573
+ conf = $.extend(true, {}, v.conf, conf);
574
+
575
+ // selector is a form
576
+ if (this.is("form")) {
577
+ return this.each(function() {
578
+ var form = $(this);
579
+ instance = new Validator(form.find(":input"), form, conf);
580
+ form.data("validator", instance);
581
+ });
582
+
583
+ } else {
584
+ instance = new Validator(this, this.eq(0).closest("form"), conf);
585
+ return this.data("validator", instance);
586
+ }
587
+
588
+ };
589
+
590
+ })(jQuery);