rep.ajax.toolkit 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ .DS_Store
2
+ *~
3
+ pkg
4
+ build
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2009 MIT Hyperstudio
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1 @@
1
+
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |s|
4
+ s.name = "rep.ajax.toolkit"
5
+ s.summary = "Hyperstudio ajax tools"
6
+ s.description = "Hyperstudio ajax tools"
7
+ s.email = "yorkc@mit.edu"
8
+ s.homepage = "http://github.com/repertoire/rep.ajax.toolkit"
9
+ s.authors = ["Christopher York"]
10
+ s.add_dependency('repertoire-assets', '>=0.1.0')
11
+ s.add_dependency('rep.jquery', '>=1.3.2')
12
+ end
13
+ Jeweler::RubyforgeTasks.new do |rubyforge|
14
+ rubyforge.doc_task = "yardoc"
15
+ end
16
+
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler not available. Install it with: sudo gem install jeweler"
20
+ end
21
+
22
+ begin
23
+ require 'repertoire-assets'
24
+ # Repertoire::Assets::Tasks.new(:gem_excludes => ["jquery"])
25
+ rescue LoadError
26
+ puts "Repertoire assets not available. Install it with: sudo gem install repertoire-assets"
27
+ end
data/TODO ADDED
@@ -0,0 +1 @@
1
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.0
File without changes
@@ -0,0 +1,192 @@
1
+ //= require <jquery>
2
+ //= require <jquery/form>
3
+
4
+ //= require "../stylesheets/rep.ajax-validate.css"
5
+ //= provide "../images/rep.ajax-validate/*"
6
+
7
+ /*
8
+ * Repertoire incremental form validation via ajax
9
+ *
10
+ * Requires the JQuery Form plugin (http://www.malsup.com/jquery/form/)
11
+ *
12
+ * Copyright (c) 2009 MIT Hyperstudio
13
+ * Christopher York, 03/2009
14
+ * Revised 06/2009
15
+ */
16
+
17
+ (function($) {
18
+ //
19
+ // Usage:
20
+ //
21
+ // Invoke ajaxValidate on the form to be incrementally validated. On the server side,
22
+ // your validation method should accept the same parameters as the form's actual submit method,
23
+ // and return a JSON hash of errors in the form { field_name: [error1, error2, ...]}; or 'true' if the
24
+ // entire form validates. By default, the plugin only enables the submit button when every
25
+ // field is valid.
26
+ //
27
+ // Basic Example:
28
+ //
29
+ // Only the url for the validation webservice is required.
30
+ //
31
+ // $('#my form').ajaxValidate({ url: '/validate_user' })
32
+ //
33
+ // Example with options:
34
+ //
35
+ // $('#my form').ajaxValidate({
36
+ // url: '/validate_user', /* validation webservice url */
37
+ // disable: false, /* allow submit for invalid forms */
38
+ // feedback: '#validation_feedback', /* put feedback in a special div rather than per field */
39
+ // delegate: { 'user[password_confirmation]': 'user[password]' } /* validate password whenever confirmation changes */
40
+ // initialize: true /* validate all fields on form load */
41
+ // })
42
+ //
43
+ // Also, you can configure the format of the error html by redefining ajaxValidate.format. To use the default styling,
44
+ // link to the rep.ajax-validate.css file and associated assets.
45
+ //
46
+ // Note that input fields are identified by the 'name' attribute rather than id or class, for consistency
47
+ // with form-processing code on the server side. So if your form looks like:
48
+ //
49
+ // <form method="post" action="/users">
50
+ // <div>Email:<input type="text" name="user[email]"/><div class='validate'/></div>
51
+ // <div>Password:<input type="password" name="user[password]"/><div class='validate'/></div>
52
+ // <div>Confirm:<input type="password" name="user[confirm_password]"/><div class='validate'/></div>
53
+ // <div style="clear:both"><input type="submit"/></div>
54
+ // </form>
55
+ //
56
+ // Your validation web service should return JSON keyed to the text fields' name attributes:
57
+ //
58
+ // { 'user[email]': [ 'Email has already been taken by another user', 'Email must be in mit.edu domain' ], ... }
59
+ //
60
+ // One special case to ease usability: When the user hovers over the submit button in preparation to submit, the
61
+ // validator runs if the input is disabled. This handles a common case where the user has filled the final field
62
+ // but not moved cursor focus. Disabling is done via javascript rather than html, since some browsers swallow
63
+ // all events for disabled inputs.
64
+ //
65
+ $.fn.ajaxValidate = function($$options) {
66
+ // plugin defaults + options
67
+ var $settings = $.extend({}, $.fn.ajaxValidate.defaults, $$options);
68
+ return this.each(function() {
69
+ var $form = $(this);
70
+ // element specific options
71
+ var o = $.meta ? $.extend({}, $settings, $form.data()) : $settings;
72
+ // initialize and setup submit button
73
+ initialize($form, o, false);
74
+ // install event handlers to validate single fields when they lose focus
75
+ $form.find(':input').blur(function() {
76
+ // determine if field delegates to another and locate the feedback element
77
+ var field = canonical_field($form, $(this), o);
78
+ // remove existing feedback during server activity
79
+ field.$feedback.empty();
80
+ // core operation: submit form via ajax and update field feedback with formatted errors
81
+ validate_form($form, field.$feedback, o, function(data) {
82
+ disabled = data !== true
83
+ set_disabled($form, data !== true, o);
84
+ set_feedback(field.$feedback, data[field.name], o);
85
+ });
86
+ });
87
+
88
+ // handle disabling form and submit button
89
+ if (o.disable) {
90
+ var $submit = $form.find(o.disable);
91
+
92
+ // only allow submit if button not disabled
93
+ $form.submit(function() {
94
+ var disabled = $submit.hasClass('disabled');
95
+ var $validate = $form.find('.validate');
96
+ if (disabled) {
97
+ $validate.fadeOut(function() { $validate.fadeIn() });
98
+ }
99
+ return !disabled;
100
+ });
101
+
102
+ // validate entire form when user hovers on disabled button
103
+ $submit.mouseover(function() {
104
+ initialize($form, o, true);
105
+ });
106
+ };
107
+ });
108
+ };
109
+
110
+ //
111
+ // format errors. pass in an array of strings, or true/undefined if there are no errors
112
+ //
113
+ $.fn.ajaxValidate.format = function(errors, opts) {
114
+ if (undefined != errors || true == errors) {
115
+ return '<ul class="error">' + $.map(errors, function(e) { return ('<li>' + e + '</li>'); }).join('') + '</ul>';
116
+ } else {
117
+ return '<ul class="pass"></ul>'
118
+ }
119
+ };
120
+
121
+ //
122
+ // option defaults
123
+ //
124
+ $.fn.ajaxValidate.defaults = {
125
+ initialize: false, /* validate all fields' initial values when form loads */
126
+ feedback: '~ .validate', /* jquery selector for error feedback element, relative to the field it monitors */
127
+ disable: ':input[type=submit]', /* Disable submit button until form validates: supply selector or false */
128
+ type: 'POST', /* HTTP verb to use when calling web service */
129
+ delegate: {}, /* delegate validation of one field to another */
130
+ spinner: 'spinner', /* css class to add to feedback during ajax processing */
131
+ };
132
+
133
+ // internal helper functions
134
+
135
+ // pre-flight validation for all fields
136
+ function initialize($form, opts, force) {
137
+ validate_form($form, $form, opts, function(data) {
138
+ set_disabled($form, data != true, opts);
139
+ if (opts.initialize || force) {
140
+ $form.find(':input').each(function() {
141
+ var field = canonical_field($form, $(this), opts);
142
+ set_feedback(field.$feedback, data[field.name], opts);
143
+ });
144
+ }
145
+ });
146
+ }
147
+
148
+ // submit validation info to web service while displaying spinner
149
+ function validate_form($form, $feedback_elem, opts, callback) {
150
+ $feedback_elem.addClass(opts.spinner);
151
+ // submit the form contents to the validation web service
152
+ $form.ajaxSubmit({
153
+ url: opts.url,
154
+ type: opts.type,
155
+ dataType: 'json',
156
+ beforeSend: function(xhr) { xhr.setRequestHeader("Accept", "application/json") }, /* JQuery uses wrong content type header */
157
+ success: function(data) {
158
+ // remove spinner and yield result to callback
159
+ $feedback_elem.removeClass(opts.spinner);
160
+ callback(data);
161
+ }
162
+ });
163
+ }
164
+
165
+ function canonical_field($form, $field, opts) {
166
+ // determine name of field to validate (this is the datamapper model attribute)
167
+ var field_name = $field.attr('name');
168
+ // handle fields whose validation delegates to another
169
+ if (opts.delegate[field_name] != undefined) {
170
+ field_name = opts.delegate[field_name];
171
+ $field = $form.find(':input[name="' + field_name +'"]');
172
+ }
173
+ // determine element to display error feedback
174
+ var $feedback_elem = $field.find(opts.feedback);
175
+ // collect info on field that is being validated
176
+ return { name: field_name, $input: $field, $feedback: $feedback_elem }
177
+ }
178
+
179
+ function set_disabled($form, status, opts) {
180
+ // enable/disable submit button, if option selected
181
+ if (opts.disable) {
182
+ if (status) $form.find(opts.disable).addClass('disabled');
183
+ else $form.find(opts.disable).removeClass('disabled');
184
+ }
185
+ }
186
+
187
+ function set_feedback($elem, errors, opts) {
188
+ // render error markup and update feedback element
189
+ markup = $.fn.ajaxValidate.format(errors, opts);
190
+ $elem.html(markup);
191
+ }
192
+ })(jQuery);
@@ -0,0 +1,37 @@
1
+ //= require <jquery>
2
+
3
+ jQuery.fn.hint = function (blurClass) {
4
+ if (!blurClass) {
5
+ blurClass = 'blur';
6
+ }
7
+
8
+ return this.each(function () {
9
+ // get jQuery version of 'this'
10
+ var $input = jQuery(this),
11
+
12
+ // capture the rest of the variable to allow for reuse
13
+ title = $input.attr('title'),
14
+ $form = jQuery(this.form),
15
+ $win = jQuery(window);
16
+
17
+ function remove() {
18
+ if ($input.val() === title && $input.hasClass(blurClass)) {
19
+ $input.val('').removeClass(blurClass);
20
+ }
21
+ }
22
+
23
+ // only apply logic if the element has the attribute
24
+ if (title) {
25
+ // on blur, set value to title attr if text is blank
26
+ $input.blur(function () {
27
+ if (this.value === '') {
28
+ $input.val(title).addClass(blurClass);
29
+ }
30
+ }).focus(remove).blur(); // now change all inputs to title
31
+
32
+ // clear the pre-defined text when form is submitted
33
+ $form.submit(remove);
34
+ $win.unload(remove); // handles Firefox's autocomplete
35
+ }
36
+ });
37
+ };
@@ -0,0 +1,55 @@
1
+ /*
2
+ * Repertoire abstract event model, for use with widget model
3
+ *
4
+ * Copyright (c) 2009 MIT Hyperstudio
5
+ * Christopher York, 11/2009
6
+ *
7
+ * Requires jquery 1.3.2+
8
+ * Support: Firefox 3+ & Safari 4+. IE emphatically not supported.
9
+ */
10
+
11
+ //= require "global"
12
+
13
+ //= require <jquery>
14
+
15
+ //
16
+ // Mixin that adds functionality for event listening to an arbitrary javascript object.
17
+ //
18
+ // API is the similar to jquery's bind, unbind, and trigger - except that events cannot be
19
+ // namespaced.
20
+ //
21
+ // N.B. This is not a true event dispatch system: there is no event object, just callbacks.
22
+ // Implementation may change.
23
+ //
24
+ repertoire.events = function(self, $proxy) {
25
+
26
+ var handlers = {};
27
+
28
+ // mimic jquery's event bind
29
+ self.bind = function(type, fn) {
30
+ if (!handlers[type])
31
+ handlers[type] = [];
32
+
33
+ handlers[type].push(fn);
34
+ };
35
+
36
+ // mimic jquery's event unbind
37
+ self.unbind = function(type, fn) {
38
+ if (handlers[type]) {
39
+ handlers[type] = jQuery.grep(handlers[type], function(h) {
40
+ return h !== fn;
41
+ });
42
+ }
43
+ };
44
+
45
+ // wrap jquery's event trigger
46
+ self.trigger = function(type, data) {
47
+ if (handlers[type]) {
48
+ jQuery.each(handlers[type], function() {
49
+ this.call(self);
50
+ })
51
+ }
52
+ };
53
+
54
+ return self;
55
+ };
@@ -0,0 +1,52 @@
1
+ /*
2
+ * Repertoire abstract ajax widget
3
+ *
4
+ * Copyright (c) 2009 MIT Hyperstudio
5
+ * Christopher York, 09/2009
6
+ *
7
+ * Requires jquery 1.3.2+
8
+ * Support: Firefox 3+ & Safari 4+. IE emphatically not supported.
9
+ */
10
+
11
+ //= require <jquery>
12
+
13
+ // claim a single global namespace, 'repertoire'
14
+ repertoire = {};
15
+
16
+ // Global defaults inherited by all widgets
17
+ //
18
+ // Options:
19
+ // path_prefix - prefix to add before all generated urls
20
+ //
21
+ repertoire.defaults = {
22
+ path_prefix: ''
23
+ };
24
+
25
+ //
26
+ // Generates a jquery plugin that attaches a widget instance to each matched element
27
+ // and exposes plugin defaults.
28
+ //
29
+ // N.B. This method is currently only in use in the faceting module, and may be deprecated.
30
+ //
31
+ // Usage:
32
+ // $.fn.my_widget = repertoire.plugin(my_widget);
33
+ // $.fn.my_widget.defaults = { /* widget option defaults */ };
34
+ //
35
+ repertoire.plugin = function(self) {
36
+ var fn = function(options) {
37
+ return this.each(function() {
38
+ var settings = $.extend({}, repertoire.defaults, fn.defaults, html_options($(this)), options);
39
+ self($(this), settings).initialize();
40
+ });
41
+ };
42
+ fn.defaults = { };
43
+ return fn;
44
+
45
+ // helper: default widget name and title options from dom
46
+ function html_options($elem) {
47
+ return {
48
+ name: $elem.attr('id'),
49
+ title: $elem.attr('title')
50
+ };
51
+ }
52
+ };
@@ -0,0 +1,165 @@
1
+ /*
2
+ * Repertoire abstract ajax model, for use with widget framework
3
+ *
4
+ * Copyright (c) 2009 MIT Hyperstudio
5
+ * Christopher York, 11/2009
6
+ *
7
+ * Requires jquery 1.3.2+
8
+ * Support: Firefox 3+ & Safari 4+. IE emphatically not supported.
9
+ */
10
+
11
+ //= require "global"
12
+ //= require "widget"
13
+ //= require "events"
14
+
15
+ //= require <jquery>
16
+
17
+ //
18
+ // Abstract model for ajax widgets. This class includes convenience methods for listening to
19
+ // events on models, for jquery ajax calls, and for param encoding in Merb's style.
20
+ //
21
+ // Handles:
22
+ // - change publication and observing
23
+ // - url/query-string construction
24
+ //
25
+ // Usage. The model provides a facade to your ajax webservice, and may hold state for a widget.
26
+ // In some cases (e.g. working with large server-side datasets), it may be appropriate to
27
+ // cache data in the model and fetch it as needed from the webservice.
28
+ //
29
+ // Using the ajax param encoders. These convenience methods help you construct the url and
30
+ // assemble params for an ajax call. It's not required to use them, although they make life
31
+ // easier.
32
+ //
33
+ // self.items = function(callback) {
34
+ // var url = self.default_url(['projects', 'results']);
35
+ // self.fetch(params, url, 'html', callback);
36
+ // }
37
+ //
38
+ repertoire.model = function(options) {
39
+ // this object is an abstract class
40
+ var self = {};
41
+
42
+ // default: no options specified
43
+ options = options || {};
44
+
45
+ // mixin event handler functionality
46
+ repertoire.events(self);
47
+
48
+ //
49
+ // Update the data model given the current state
50
+ //
51
+ // You may either pre-process the state and write your own webservice access methods
52
+ // or use self.fetch for a generic webservice, e.g.
53
+ //
54
+ // self.update = function(state, callback) {
55
+ // var url = self.default_url(['projects', 'results']);
56
+ // self.fetch(state, url, 'html', callback);
57
+ // }
58
+ //
59
+ self.update = function(state, callback) {
60
+ throw "Abstract function: redefine self.update() in your data model."
61
+ };
62
+
63
+ //
64
+ // Utility method to issue an ajax HTTP GET to fetch data from a webservice
65
+ //
66
+ // params: params to send as http query line
67
+ // url: url of webservice to access
68
+ // type: type of data returned (e.g. 'json', 'html')
69
+ // callback: function to call with returned data
70
+ //
71
+ self.fetch = function(params, url, type, callback, $elem, async) {
72
+ if (async == null)
73
+ async = true;
74
+
75
+ var spinnerClass = options.spinner || 'loading';
76
+ if ($elem)
77
+ $elem.addClass(spinnerClass);
78
+ $.ajax({
79
+ async: async,
80
+ url: url,
81
+ data: self.to_query_string(params),
82
+ type: 'GET',
83
+ dataType: type,
84
+ // content negotiation problems may require:
85
+ /* beforeSend: function(xhr) { xhr.setRequestHeader("Accept", "application/json") } */
86
+ success: callback,
87
+ error: function(request, textStatus, errorThrown) {
88
+ if ($elem)
89
+ $elem.html(options.error || 'Could not load');
90
+ },
91
+ complete: function () {
92
+ if ($elem)
93
+ $elem.removeClass(spinnerClass);
94
+ }
95
+ });
96
+ };
97
+
98
+ //
99
+ // Format a webservice url, preferring options.url if possible
100
+ //
101
+ self.default_url = function(default_parts, ext) {
102
+ var path_prefix = options.path_prefix || '';
103
+ var parts = default_parts.slice();
104
+
105
+ parts.unshift(path_prefix);
106
+ url = options.url || parts.join('/')
107
+
108
+ if (ext)
109
+ url += '.' + ext;
110
+
111
+ return url;
112
+ }
113
+
114
+ //
115
+ // Convert a structure of params to a URL query string suitable for use in an HTTP GET request, compliant with Merb's format.
116
+ //
117
+ // An example:
118
+ //
119
+ // Merb::Parse.params_to_query_string(:filter => {:year => [1593, 1597], :genre => ['Tragedy', 'Comedy'] }, :search => 'William')
120
+ // ==> "filter[genre][]=Tragedy&filter[genre][]=Comedy&filter[year][]=1593&filter[year][]=1597&search=William"
121
+ //
122
+ self.to_query_string = function(value, prefix) {
123
+ var vs = [];
124
+ prefix = prefix || '';
125
+ if (value instanceof Array) {
126
+ jQuery.each(value, function(i, v) {
127
+ vs.push(self.to_query_string(v, prefix + '[]'));
128
+ });
129
+ return vs.join('&');
130
+ } else if (typeof(value) == "object") {
131
+ jQuery.each(value, function(k, v) {
132
+ vs.push(self.to_query_string(v, (prefix.length > 0) ? (prefix + '[' + escape(k) + ']') : escape(k)));
133
+ });
134
+ // minor addition to merb: discard empty value lists { e.g. discipline: [] }
135
+ vs = array_filter(vs, function(x) { return x !== ""; });
136
+ return vs.join('&');
137
+ } else {
138
+ return prefix + '=' + escape(value);
139
+ }
140
+ };
141
+
142
+ // Apparently IE doesn't support the filter function? -DD via Brett
143
+ var array_filter = function (thisArray, fun) {
144
+ var len = thisArray.length;
145
+ if (typeof fun != "function")
146
+ throw new TypeError();
147
+
148
+ var res = new Array();
149
+ var thisp = arguments[1];
150
+
151
+ for (var i = 0; i < len; i++) {
152
+ if (i in thisArray) {
153
+ var val = thisArray[i]; // in case fun mutates this
154
+ if (fun.call(thisp, val, i, thisArray))
155
+ res.push(val);
156
+ }
157
+ }
158
+
159
+ return res;
160
+ };
161
+
162
+
163
+ // end of model factory function
164
+ return self;
165
+ }
@@ -0,0 +1,217 @@
1
+ /*
2
+ * Repertoire abstract ajax widget
3
+ *
4
+ * Copyright (c) 2009 MIT Hyperstudio
5
+ * Christopher York, 09/2009
6
+ *
7
+ * Requires jquery 1.3.2+
8
+ * Support: Firefox 3+ & Safari 4+. IE emphatically not supported.
9
+ */
10
+
11
+ //= require <jquery>
12
+
13
+ //= require "global"
14
+
15
+ //
16
+ // Abstract class for ajax widgets
17
+ //
18
+ // Handles:
19
+ // - url/query-string construction
20
+ // - data assembly for sending to webservice
21
+ // - ui event delegation hooks
22
+ // - hooks for injecting custom behaviour
23
+ //
24
+ // Options on all subclassed widgets:
25
+ //
26
+ // url - provide a url to over-ride the widget's default
27
+ // spinner - css class to add to widget during ajax loads
28
+ // error - text to display if ajax load fails
29
+ // injectors - additional jquery markup to inject into widget (see FAQ)
30
+ // handlers - additional jquery event handlers to add to widget (see FAQ)
31
+ // state - additional pre-processing for params sent to webservice (see FAQ)
32
+ //
33
+ // Sub-classes are required to over-ride one method: self.render(). If you wish to
34
+ // use a data model, store a subclass of rep.wigets/model in your widget.
35
+ //
36
+ repertoire.widget = function(selector, options) {
37
+ // this object is an abstract class
38
+ var self = {};
39
+
40
+ // private variables
41
+ var $widget = $(selector);
42
+ options = options || {};
43
+
44
+ // mix in event handling functionality
45
+ repertoire.events(self, $widget);
46
+
47
+ // to run by hand after sub-classes have evaluated
48
+ self.initialize = function() {
49
+ // register any custom handlers
50
+ if (options.handlers !== undefined)
51
+ register_handlers(options.handlers);
52
+
53
+ // load once at beginning
54
+ self.refresh();
55
+ }
56
+
57
+ //
58
+ // Refresh model and render into widget
59
+ //
60
+ // Integrates state and markup injectors
61
+ //
62
+ self.refresh = function() {
63
+ var callback;
64
+
65
+ // pass to custom state processor
66
+ if (options.state !== undefined)
67
+ options.state(self);
68
+
69
+ callback = function() {
70
+ // render the widget
71
+ var markup = self.render.apply(self, arguments);
72
+
73
+ // inject any custom markup
74
+ if (options.injectors !== undefined)
75
+ // TODO. figure out how to send all args to injectors
76
+ process_injectors(markup, options.injectors, arguments[0]);
77
+
78
+ // paint markup into the dom
79
+ if (markup)
80
+ $widget.html(markup);
81
+ };
82
+
83
+ // start rendering
84
+ self.reload(callback);
85
+ };
86
+
87
+ //
88
+ // Render and return markup for this widget.
89
+ //
90
+ // Three forms are possible:
91
+ //
92
+ // (1) Basic... just return a string or jquery object
93
+ //
94
+ // self.render = function() {
95
+ // return 'Hello world!';
96
+ // };
97
+ //
98
+ // (2) If you just want to tweak the superclass' view:
99
+ //
100
+ // var template_fn = self.render; // idiom to access super.render()
101
+ // self.render = function() {
102
+ // var markup = template_fn();
103
+ // return $(markup).find('.title').html('New Title');
104
+ // };
105
+ //
106
+ // (3) If you want to modify the DOM in place, do so
107
+ // and return nothing.
108
+ //
109
+ self.render = function() {
110
+ return $('<div class="rep"/>'); // namespace for all other widget css is 'rep'
111
+ };
112
+
113
+
114
+ //
115
+ // A hook for use when your widget must render the results of an ajax callback. Put
116
+ // the ajax call in self.reload(). Its results will be passed to self.render().
117
+ //
118
+ // self.reload = function(callback) {
119
+ // $.get('http://www.nytimes.com', callback);
120
+ // };
121
+ //
122
+ // self.render = function(daily_news) {
123
+ // $(daily_news).find('title').text(); // widget's view is the title of the new york times
124
+ // }
125
+ //
126
+ // N.B. In real-world cases, the call in self.reload should be to your
127
+ // data model class, which serves as an ajax api facade.
128
+ //
129
+ self.reload = function(callback) {
130
+ callback();
131
+ }
132
+
133
+ //
134
+ // Register a handler for dom events on this widget. Call with an event selector and a standard jquery event
135
+ // handler function, e.g.
136
+ //
137
+ // self.handler('click.toggle_value!.rep .facet .value', function() { ... });
138
+ //
139
+ // Note the syntax used to identify a handler's event, namespace, and the jquery selector: '<event.namespace>!<target>'.
140
+ // Both event and namespace are optional - leave them out to register a click handler with a unique namespace.
141
+ //
142
+ // N.B. This method is intended only for protected use within a widget and its subclasses, since it depends
143
+ // on the view implementation.
144
+ //
145
+ self.handler = function(event_selector, fn) {
146
+ event_selector = parse_event_selector(event_selector);
147
+ var event = event_selector[0],
148
+ selector = event_selector[1]; // why doesn't JS support array decomposition?!?
149
+
150
+ // bind new handler
151
+ $widget.bind(event, function (e) {
152
+ var $el = $(e.target);
153
+ var result = false;
154
+ // walk up dom tree for selector
155
+ while ($el.length > 0) {
156
+ if ($el.is(selector)) {
157
+ result = fn.apply($el[0], [e]);
158
+ if (result === false)
159
+ e.preventDefault();
160
+ return;
161
+ }
162
+ $el = $el.parent();
163
+ }
164
+ });
165
+ }
166
+
167
+ // PRIVATE
168
+
169
+ // register a collection of event handlers
170
+ function register_handlers(handlers) {
171
+ $.each(handlers, function(selector, handler) {
172
+ // register each handler
173
+ self.handler(selector, function() {
174
+ // bind self as an argument for the custom handler
175
+ return handler.apply(this, [self]);
176
+ });
177
+ });
178
+ }
179
+
180
+ // inject custom markup into widget
181
+ function process_injectors($markup, injectors, data) {
182
+ // workaround for jquery find not matching top element
183
+ $wrapped = $("<div/>").append($markup);
184
+
185
+ $.each(injectors, function(selector, injector) {
186
+ var $elems = $wrapped.find(selector);
187
+ if ($elems.length > 0) {
188
+ injector.apply($elems, [ self, data ]);
189
+ }
190
+ });
191
+ }
192
+
193
+ // parse an event name and selector spec
194
+ function parse_event_selector(event_selector) {
195
+ var s = event_selector.split('!');
196
+ var event, selector;
197
+
198
+ if (s.length === 2) {
199
+ event = s[0], selector = s[1];
200
+ } else if (s.length === 1) {
201
+ event = 'click', selector = s[0];
202
+ } else {
203
+ throw "Could not parse event selector: " + event_selector;
204
+ }
205
+
206
+ if (event.indexOf('.')<0) {
207
+ // create a default namespace from selector or random number
208
+ namespace = selector.replace(/[^a-zA-z0-9]/g, '') || new Date().getTime();
209
+ event = event + '.' + namespace;
210
+ }
211
+
212
+ return [event, selector];
213
+ }
214
+
215
+ // end of widget factory function
216
+ return self;
217
+ };
@@ -0,0 +1,13 @@
1
+ /*
2
+ * Repertoire abstract ajax widget
3
+ *
4
+ * Copyright (c) 2009 MIT Hyperstudio
5
+ * Christopher York, 09/2009
6
+ *
7
+ * Requires jquery 1.3.2+
8
+ * Support: Firefox 3+ & Safari 4+. IE emphatically not supported.
9
+ */
10
+
11
+ //= require "rep.widgets/global"
12
+ //= require "rep.widgets/widget"
13
+ //= require "rep.widgets/model"
@@ -0,0 +1,7 @@
1
+ /* default css for the Repertoire ajax form validation widget */
2
+
3
+ .validate { float: left; padding-left: 5px; }
4
+ .validate ul { list-style-type: none; border: 0; margin: 0; padding: 0;}
5
+ .validate .error li { background: url(../images/rep.ajax-validate/red_cross.png) no-repeat left center; color: red; padding-left:15px;}
6
+ .validate .pass { background: url(../images/rep.ajax-validate/green_check.png) no-repeat left center; color: green; min-height:15px; min-width:15px;}
7
+ .validate.spinner { background: url(../images/rep.ajax-validate/spinner_sm.gif) no-repeat left center; min-height:15px; min-width:15px;}
@@ -0,0 +1,63 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rep.ajax.toolkit}
8
+ s.version = "0.3.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Christopher York"]
12
+ s.date = %q{2010-08-01}
13
+ s.description = %q{Hyperstudio ajax tools}
14
+ s.email = %q{yorkc@mit.edu}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README",
18
+ "TODO"
19
+ ]
20
+ s.files = [
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README",
24
+ "Rakefile",
25
+ "TODO",
26
+ "VERSION",
27
+ "lib/rep.ajax.toolkit.rb",
28
+ "public/images/rep.ajax-validate/green_check.png",
29
+ "public/images/rep.ajax-validate/red_cross.png",
30
+ "public/images/rep.ajax-validate/spinner_sm.gif",
31
+ "public/javascripts/rep.ajax-validate.js",
32
+ "public/javascripts/rep.hint.js",
33
+ "public/javascripts/rep.widgets.js",
34
+ "public/javascripts/rep.widgets/events.js",
35
+ "public/javascripts/rep.widgets/global.js",
36
+ "public/javascripts/rep.widgets/model.js",
37
+ "public/javascripts/rep.widgets/widget.js",
38
+ "public/stylesheets/rep.ajax-validate.css",
39
+ "rep.ajax.toolkit.gemspec"
40
+ ]
41
+ s.homepage = %q{http://github.com/repertoire/rep.ajax.toolkit}
42
+ s.rdoc_options = ["--charset=UTF-8"]
43
+ s.require_paths = ["lib"]
44
+ s.rubygems_version = %q{1.3.7}
45
+ s.summary = %q{Hyperstudio ajax tools}
46
+
47
+ if s.respond_to? :specification_version then
48
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
49
+ s.specification_version = 3
50
+
51
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
52
+ s.add_runtime_dependency(%q<repertoire-assets>, [">= 0.1.0"])
53
+ s.add_runtime_dependency(%q<rep.jquery>, [">= 1.3.2"])
54
+ else
55
+ s.add_dependency(%q<repertoire-assets>, [">= 0.1.0"])
56
+ s.add_dependency(%q<rep.jquery>, [">= 1.3.2"])
57
+ end
58
+ else
59
+ s.add_dependency(%q<repertoire-assets>, [">= 0.1.0"])
60
+ s.add_dependency(%q<rep.jquery>, [">= 1.3.2"])
61
+ end
62
+ end
63
+
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rep.ajax.toolkit
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 3
8
+ - 0
9
+ version: 0.3.0
10
+ platform: ruby
11
+ authors:
12
+ - Christopher York
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-08-01 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: repertoire-assets
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ - 1
31
+ - 0
32
+ version: 0.1.0
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: rep.jquery
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 1
45
+ - 3
46
+ - 2
47
+ version: 1.3.2
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ description: Hyperstudio ajax tools
51
+ email: yorkc@mit.edu
52
+ executables: []
53
+
54
+ extensions: []
55
+
56
+ extra_rdoc_files:
57
+ - LICENSE
58
+ - README
59
+ - TODO
60
+ files:
61
+ - .gitignore
62
+ - LICENSE
63
+ - README
64
+ - Rakefile
65
+ - TODO
66
+ - VERSION
67
+ - lib/rep.ajax.toolkit.rb
68
+ - public/images/rep.ajax-validate/green_check.png
69
+ - public/images/rep.ajax-validate/red_cross.png
70
+ - public/images/rep.ajax-validate/spinner_sm.gif
71
+ - public/javascripts/rep.ajax-validate.js
72
+ - public/javascripts/rep.hint.js
73
+ - public/javascripts/rep.widgets.js
74
+ - public/javascripts/rep.widgets/events.js
75
+ - public/javascripts/rep.widgets/global.js
76
+ - public/javascripts/rep.widgets/model.js
77
+ - public/javascripts/rep.widgets/widget.js
78
+ - public/stylesheets/rep.ajax-validate.css
79
+ - rep.ajax.toolkit.gemspec
80
+ has_rdoc: true
81
+ homepage: http://github.com/repertoire/rep.ajax.toolkit
82
+ licenses: []
83
+
84
+ post_install_message:
85
+ rdoc_options:
86
+ - --charset=UTF-8
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ segments:
95
+ - 0
96
+ version: "0"
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ segments:
103
+ - 0
104
+ version: "0"
105
+ requirements: []
106
+
107
+ rubyforge_project:
108
+ rubygems_version: 1.3.7
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: Hyperstudio ajax tools
112
+ test_files: []
113
+