zen 0.4 → 0.4.1

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 (92) hide show
  1. data/.aspell.en.pws +31 -0
  2. data/.gems +1 -1
  3. data/.mailmap +1 -0
  4. data/.travis.yml +13 -7
  5. data/.yardopts +1 -1
  6. data/Rakefile +2 -2
  7. data/guide/changelog.md +23 -5
  8. data/guide/css/common.css +48 -0
  9. data/guide/javascript.md +1 -0
  10. data/guide/javascript/zen_form.md +46 -0
  11. data/guide/zen_compared.md +104 -0
  12. data/lib/zen.rb +21 -12
  13. data/lib/zen/helper/breadcrumb.rb +1 -1
  14. data/lib/zen/helper/message.rb +4 -5
  15. data/lib/zen/language.rb +75 -10
  16. data/lib/zen/markup.rb +20 -8
  17. data/lib/zen/model/init.rb +14 -9
  18. data/lib/zen/model/plugin/events.rb +1 -1
  19. data/lib/zen/package.rb +71 -14
  20. data/lib/zen/package/categories/lib/categories/controller/categories.rb +39 -12
  21. data/lib/zen/package/categories/lib/categories/controller/category_groups.rb +26 -5
  22. data/lib/zen/package/categories/lib/categories/helper/category.rb +37 -6
  23. data/lib/zen/package/categories/lib/categories/helper/category_frontend.rb +2 -2
  24. data/lib/zen/package/categories/lib/categories/view/admin/categories/form.xhtml +26 -29
  25. data/lib/zen/package/comments/lib/comments.rb +9 -1
  26. data/lib/zen/package/comments/lib/comments/anti_spam.rb +1 -1
  27. data/lib/zen/package/comments/lib/comments/controller/comments.rb +59 -14
  28. data/lib/zen/package/comments/lib/comments/controller/comments_form.rb +49 -11
  29. data/lib/zen/package/comments/lib/comments/helper/comment.rb +14 -2
  30. data/lib/zen/package/comments/lib/comments/view/admin/comments/form.xhtml +3 -3
  31. data/lib/zen/package/comments/migrations/1308774099_comment_status.rb +1 -1
  32. data/lib/zen/package/custom_fields/lib/custom_fields/blue_form_parameters.rb +12 -0
  33. data/lib/zen/package/custom_fields/lib/custom_fields/controller/custom_field_groups.rb +24 -6
  34. data/lib/zen/package/custom_fields/lib/custom_fields/controller/custom_field_types.rb +68 -20
  35. data/lib/zen/package/custom_fields/lib/custom_fields/controller/custom_fields.rb +106 -26
  36. data/lib/zen/package/custom_fields/lib/custom_fields/helper/custom_field.rb +50 -11
  37. data/lib/zen/package/custom_fields/lib/custom_fields/view/admin/custom-fields/form.xhtml +2 -2
  38. data/lib/zen/package/dashboard/lib/dashboard/controller/dashboard.rb +1 -1
  39. data/lib/zen/package/menu.rb +6 -1
  40. data/lib/zen/package/menus/lib/menus/controller/menu_items.rb +44 -9
  41. data/lib/zen/package/menus/lib/menus/controller/menus.rb +53 -13
  42. data/lib/zen/package/menus/lib/menus/helper/menu.rb +30 -4
  43. data/lib/zen/package/menus/lib/menus/model/menu.rb +4 -2
  44. data/lib/zen/package/sections/lib/sections/controller/section_entries.rb +48 -9
  45. data/lib/zen/package/sections/lib/sections/controller/sections.rb +77 -21
  46. data/lib/zen/package/sections/lib/sections/helper/section.rb +32 -4
  47. data/lib/zen/package/sections/lib/sections/model/section_entry.rb +1 -1
  48. data/lib/zen/package/sections/lib/sections/view/admin/section-entries/form.xhtml +5 -4
  49. data/lib/zen/package/sections/lib/sections/view/admin/sections/form.xhtml +3 -3
  50. data/lib/zen/package/sections/migrations/1308813320_section_entry_statuses.rb +1 -1
  51. data/lib/zen/package/settings/lib/settings/blue_form_parameters.rb +2 -2
  52. data/lib/zen/package/settings/lib/settings/controller/settings.rb +60 -15
  53. data/lib/zen/package/settings/lib/settings/view/admin/settings/index.xhtml +1 -1
  54. data/lib/zen/package/users/lib/users/controller/user_groups.rb +42 -7
  55. data/lib/zen/package/users/lib/users/controller/users.rb +78 -16
  56. data/lib/zen/package/users/lib/users/helper/users.rb +29 -4
  57. data/lib/zen/package/users/lib/users/view/admin/user-groups/form.xhtml +2 -2
  58. data/lib/zen/package/users/lib/users/view/admin/users/form.xhtml +2 -2
  59. data/lib/zen/public/admin/zen/css/general.css +5 -0
  60. data/lib/zen/public/admin/zen/css/messages.css +1 -0
  61. data/lib/zen/public/admin/zen/css/tables.css +33 -0
  62. data/lib/zen/public/admin/zen/css/tabs.css +8 -0
  63. data/lib/zen/public/admin/zen/js/index.js +21 -2
  64. data/lib/zen/public/admin/zen/js/lib/events.js +45 -0
  65. data/lib/zen/public/admin/zen/js/lib/form.js +229 -0
  66. data/lib/zen/public/admin/zen/js/lib/hash.js +0 -27
  67. data/lib/zen/task/spelling.rake +97 -0
  68. data/lib/zen/task/test.rake +21 -0
  69. data/lib/zen/theme.rb +80 -24
  70. data/lib/zen/validation.rb +1 -1
  71. data/lib/zen/version.rb +1 -1
  72. data/proto/app/config/config.rb.erb +9 -4
  73. data/proto/app/config/middlewares.rb +1 -2
  74. data/spec/README.md +56 -0
  75. data/spec/zen/controller/admin_controller.rb +0 -1
  76. data/spec/zen/controller/preview.rb +0 -1
  77. data/spec/zen/package.rb +32 -0
  78. data/spec/zen/package/categories/controller/categories.rb +7 -0
  79. data/spec/zen/package/categories/controller/category_groups.rb +7 -0
  80. data/spec/zen/package/comments/controller/comments.rb +7 -0
  81. data/spec/zen/package/custom_fields/controller/custom_field_groups.rb +7 -0
  82. data/spec/zen/package/custom_fields/controller/custom_field_types.rb +7 -0
  83. data/spec/zen/package/custom_fields/controller/custom_fields.rb +7 -0
  84. data/spec/zen/package/menus/controller/menu_items.rb +7 -0
  85. data/spec/zen/package/menus/controller/menus.rb +7 -0
  86. data/spec/zen/package/menus/helper/menu_frontend.rb +1 -1
  87. data/spec/zen/package/sections/controller/section_entries.rb +39 -0
  88. data/spec/zen/package/sections/controller/sections.rb +14 -0
  89. data/spec/zen/package/users/controller/user_groups.rb +7 -0
  90. data/spec/zen/package/users/controller/users.rb +7 -0
  91. data/zen.gemspec +6 -5
  92. metadata +142 -40
@@ -47,8 +47,27 @@ window.addEvent('domready', function()
47
47
  });
48
48
  });
49
49
 
50
- $$('form[data-autosave-url]').each(function(el)
50
+ $$('form:not(#search_form)').each(function(form)
51
51
  {
52
- new Zen.Autosave(el, el.get('data-autosave-url'));
52
+ new Zen.Form(form);
53
+ });
54
+
55
+ $$('.message').addEvent('click', function()
56
+ {
57
+ var _this = this;
58
+ var fx = new Fx.Tween(
59
+ this,
60
+ {property: 'margin-top', duration: 'short'}
61
+ );
62
+
63
+ fx.start(0, -this.getHeight()).chain(function()
64
+ {
65
+ _this.destroy();
66
+
67
+ if ( $$('.message').length <= 0 )
68
+ {
69
+ $('message_container').destroy();
70
+ }
71
+ });
53
72
  });
54
73
  });
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Adds support for the onhashchange event when using ``window.addEvent()``.
5
+ *
6
+ * Basic usage is as following:
7
+ *
8
+ * window.addEvent('hashchange', function(e, hash)
9
+ * {
10
+ * console.log(hash);
11
+ * });
12
+ *
13
+ * Do note that this implementation does not offer any backwards compatibility
14
+ * with browsers that don't natively support ``window.onhashchange``.
15
+ *
16
+ * @since 21-12-2011
17
+ */
18
+ Element.Events.hashchange = {
19
+ onAdd: function()
20
+ {
21
+ var _this = this;
22
+
23
+ this.onhashchange = function()
24
+ {
25
+ this.fireEvent('hashchange');
26
+ };
27
+ }
28
+ };
29
+
30
+ /**
31
+ * Adds support for the "invalid" event that is raised for invalid form fields.
32
+ *
33
+ * @since 10-03-2012
34
+ */
35
+ Element.Events.invalid = {
36
+ onAdd: function()
37
+ {
38
+ var _this = this;
39
+
40
+ this.oninvalid = function()
41
+ {
42
+ _this.fireEvent('invalid');
43
+ };
44
+ }
45
+ };
@@ -0,0 +1,229 @@
1
+ "use strict";
2
+
3
+ namespace('Zen');
4
+
5
+ /**
6
+ * Zen.Form is a class used for adding various dynamic features to HTML forms
7
+ * such as automatically saving the data and handling validation errors using
8
+ * the HTML5 constraints API.
9
+ *
10
+ * For the features related to the HTML5 constraints API to work properly users
11
+ * should be using a modern browser. If this API is not implemented it will be
12
+ * silently ignored. Automatically saving data is supported regardless of the
13
+ * existence of this API and is handled by Zen.Autosave.
14
+ *
15
+ * ## Usage
16
+ *
17
+ * In order to use this class you'll have to create a new instance of it and
18
+ * pass an instance of Element to it (an instance for a ``<form>`` element):
19
+ *
20
+ * new Zen.Form($('my_form'));
21
+ *
22
+ * By passing and object to the second parameter you can customize various parts
23
+ * of this class. The following options are available:
24
+ *
25
+ * * autosave_attribute: name of the attribute that contains the URL to send
26
+ * requests to when automatically saving form data. This option is set to
27
+ * "data-autosave-url" by default.
28
+ * * tabs_selector: a CSS selector used to determine the tabs for the form, set
29
+ * to "div.tabs ul" by default.
30
+ * * error_class: the primary class to use for error indicators in a tab, set to
31
+ * "tab_error" by default.
32
+ *
33
+ * An example of setting one of these options is the following:
34
+ *
35
+ * new Zen.Form($('my_form'), {error_class: 'custom_tab_error'});
36
+ *
37
+ * Keep in mind that out of the box there's no need to manually use this class,
38
+ * Zen already does this for you.
39
+ *
40
+ * ## Tabs and Errors
41
+ *
42
+ * This class makes it easier for users to nice form errors. This is achieved by
43
+ * showing a small error icon in a tab of which the corresponding tab field
44
+ * contains a number of invalid form elements. For this to work properly you
45
+ * should add the class "tab_field" to each tab field:
46
+ *
47
+ * <div id="general" class="tab_field">
48
+ * <!-- Tab field's content -->
49
+ * </div>
50
+ *
51
+ * @since 12-03-2012
52
+ */
53
+ Zen.Form = new Class(
54
+ {
55
+ Implements: Options,
56
+
57
+ /**
58
+ * The form element for this class.
59
+ *
60
+ * @since 12-03-2012
61
+ */
62
+ element: null,
63
+
64
+ /**
65
+ * Object containing the default and custom defined options merged together.
66
+ *
67
+ * @since 12-03-2012
68
+ */
69
+ options:
70
+ {
71
+ // String containing the name of the attribute that contains the URL to
72
+ // use for automatically saving forms.
73
+ autosave_attribute: 'data-autosave-url',
74
+
75
+ // CSS selector to use for matching the list container of a set of
76
+ // tabs.
77
+ tabs_selector: 'div.tabs ul',
78
+
79
+ // The class to apply to the error indicators.
80
+ error_class: 'tab_error'
81
+ },
82
+
83
+ /**
84
+ * Creates and prepares a new instance of the class.
85
+ *
86
+ * @since 12-03-2012
87
+ * @param {Element} element The form element to use for the instance of this
88
+ * class.
89
+ * @param {Object} options An object containing custom options to use for
90
+ * a particular instance of this class.
91
+ */
92
+ initialize: function(element, options)
93
+ {
94
+ if ( typeOf(element) !== 'element' )
95
+ {
96
+ throw new TypeError(
97
+ 'Expected an element as the first parameter but got '
98
+ + typeOf(element)
99
+ + ' instead'
100
+ );
101
+ }
102
+
103
+ this.setOptions(options);
104
+
105
+ this.element = element;
106
+
107
+ var _this = this;
108
+ var autosave = this.element.get(this.options.autosave_attribute);
109
+
110
+ if ( autosave )
111
+ {
112
+ new Zen.Autosave(this.element, autosave);
113
+ }
114
+
115
+ // Don't bother with the errors and such if the browser vendor is too
116
+ // lazy to properly implement the HTML5 specification.
117
+ if ( !this.element.checkValidity ) return;
118
+
119
+ // Don't bother with the validation bit if the form has no tab fields
120
+ // either.
121
+ if ( this.element.getElements('.tab_field').length <= 0 ) return;
122
+
123
+ this.element.getElements('*').each(function(el)
124
+ {
125
+ var type = el.get('type');
126
+ var event_name = 'blur';
127
+
128
+ // Ignore hidden fields
129
+ if ( type === 'hidden' ) return;
130
+
131
+ // Checkboxes and radio buttons don't call the "blur" event,
132
+ // they instead use "change".
133
+ if ( type === 'checkbox' || type === 'radio' )
134
+ {
135
+ event_name = 'change';
136
+ }
137
+
138
+ el.addEvent('invalid', function()
139
+ {
140
+ _this.invalidElement(this);
141
+ });
142
+
143
+ el.addEvent(event_name, function()
144
+ {
145
+ _this.blurElement(this);
146
+ });
147
+ });
148
+ },
149
+
150
+ /**
151
+ * Handles the "invalid" event of individual form elements. In case an
152
+ * element is invalid a small icon indicating that there is invalid data
153
+ * will be displayed in the tab the field belongs to.
154
+ *
155
+ * @since 12-03-2012
156
+ * @param {Element} element The invalid form element.
157
+ */
158
+ invalidElement: function(element)
159
+ {
160
+ var tab = this.getTab(element);
161
+
162
+ if ( !tab ) return;
163
+
164
+ var li = tab.getParent('li');
165
+
166
+ if ( li.getElements('.' + this.options.error_class).length <= 0 )
167
+ {
168
+ var error = new Element('span',
169
+ {
170
+ 'class': this.options.error_class + ' icon error'
171
+ });
172
+
173
+ error.inject(tab, 'before');
174
+ }
175
+ },
176
+
177
+ /**
178
+ * Handles the "blur" event of an individual form element. This event is
179
+ * used to check if all the errors in a tab field are gone and if so will
180
+ * remove the error indicator.
181
+ *
182
+ * @since 12-03-2012
183
+ * @param {Element} element The form element that lost focus.
184
+ */
185
+ blurElement: function(element)
186
+ {
187
+ var field = element.getParent('.tab_field');
188
+
189
+ if ( !field ) return;
190
+
191
+ if ( field.getElements(':invalid').length <= 0 )
192
+ {
193
+ var tab = this.getTab(element);
194
+
195
+ if ( !tab ) return;
196
+
197
+ // Get the error indicator and remove it if needed.
198
+ var error = tab.getParent('li')
199
+ .getElement('.' + this.options.error_class);
200
+
201
+ if ( error )
202
+ {
203
+ error.destroy();
204
+ }
205
+ }
206
+ },
207
+
208
+ /**
209
+ * Given a form element (or any other kind of element in a form) this method
210
+ * will try to retrieve the tab field the element belongs to.
211
+ *
212
+ * @since 12-03-2012
213
+ * @param {Element} element The element for which to retrieve the tab
214
+ * field.
215
+ * @return {Element|null}
216
+ */
217
+ getTab: function(element)
218
+ {
219
+ var field = element.getParent('.tab_field');
220
+ var tabs = $$(this.options.tabs_selector);
221
+
222
+ if ( tabs.length <= 0 || !field )
223
+ {
224
+ return null;
225
+ }
226
+
227
+ return tabs[0].getElement('a[href="#' + field.get('id') + '"]');
228
+ },
229
+ });
@@ -2,33 +2,6 @@
2
2
 
3
3
  namespace('Zen');
4
4
 
5
- /**
6
- * Adds support for the onhashchange event when using ``window.addEvent()``.
7
- *
8
- * Basic usage is as following:
9
- *
10
- * window.addEvent('hashchange', function(e, hash)
11
- * {
12
- * console.log(hash);
13
- * });
14
- *
15
- * Do note that this implementation does not offer any backwards compatibility
16
- * with browsers that don't natively support ``window.onhashchange``.
17
- *
18
- * @since 21-12-2011
19
- */
20
- Element.Events.hashchange = {
21
- onAdd: function()
22
- {
23
- var _this = this;
24
-
25
- this.onhashchange = function()
26
- {
27
- this.fireEvent('hashchange');
28
- };
29
- }
30
- };
31
-
32
5
  /**
33
6
  * Zen.Hash is a class that can be used to parse and generate shebang/hash bang
34
7
  * URLs. Parsing is done using ``Zen.Hash#parse`` and generating URLs using
@@ -0,0 +1,97 @@
1
+ # Task that checks the documentation for spelling errors using Raspell/Aspell.
2
+ # This task requires Aspell to be installed along with an English dictionary. On
3
+ # Arch Linux these can be installed as following:
4
+ #
5
+ # $ sudo pacman -S aspell aspell-en
6
+ #
7
+ desc 'Search docs for spelling errors'
8
+ task :spelling do
9
+ require 'raspell'
10
+ require 'ripper'
11
+
12
+ speller = Aspell.new1(
13
+ 'lang' => 'en',
14
+ 'personal' => File.expand_path('../../../../.aspell.en.pws', __FILE__),
15
+ 'ignore-case' => 'true'
16
+ )
17
+
18
+ base_dir = File.expand_path('../../../..', __FILE__)
19
+ files = Dir['lib/zen/**/*.rb']
20
+ exclude_lines = [/^#\s*@/, /^#\s{2,}/, /^#\s*!\[/, /^#\s*\[/]
21
+ exclude_patterns = [/\d+/, /_+/, /^`/]
22
+
23
+ files.each do |file|
24
+ file = File.expand_path(file)
25
+ relative = file.gsub(/^#{base_dir}\//, '')
26
+ errors = []
27
+ content = File.read(file, File.size(file))
28
+
29
+ Ripper.lex(content).each do |group|
30
+ next unless group[1] == :on_comment
31
+
32
+ skip_line = false
33
+
34
+ # Determine whether or not the entire line should be skipped.
35
+ exclude_lines.each do |pattern|
36
+ if group[2] =~ pattern
37
+ skip_line = true
38
+ break
39
+ end
40
+ end
41
+
42
+ next if skip_line
43
+
44
+ # Extract each word out of the line.
45
+ group[2].gsub(/[\w'-]+/).each do |word|
46
+ skip = false
47
+ word = word.gsub(/^'|'$/, '')
48
+
49
+ # Determine if the word should be ignored based on a list of blacklisted
50
+ # patterns.
51
+ exclude_patterns.each do |pattern|
52
+ if word =~ pattern
53
+ skip = true
54
+ break
55
+ end
56
+ end
57
+
58
+ # Exclude words that are actually constants (e.g. FalseClass). This
59
+ # isn't very fast but it prevents the requirement of using a few nasty
60
+ # regular expressions.
61
+ unless skip
62
+ lexed = Ripper.lex(word)
63
+
64
+ if lexed[0] and lexed[0][1] == :on_const
65
+ skip = true
66
+ end
67
+ end
68
+
69
+ next if skip == true
70
+
71
+ unless speller.check(word)
72
+ suggested = speller.suggest(word)[0]
73
+
74
+ if suggested
75
+ errors << {
76
+ :line => group[0][0],
77
+ :column => group[0][1],
78
+ :word => word,
79
+ :suggestion => suggested
80
+ }
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ unless errors.empty?
87
+ puts
88
+ puts relative
89
+ puts
90
+
91
+ errors.each do |error|
92
+ puts " * line ##{error[:line]}, column ##{error[:column]}: " \
93
+ "#{error[:word]}, suggestion: #{error[:suggestion]}"
94
+ end
95
+ end
96
+ end
97
+ end
@@ -31,4 +31,25 @@ namespace :test do
31
31
 
32
32
  sh(command)
33
33
  end
34
+
35
+ # Task that ensures that the various Travis CI tests each use their own
36
+ # database based on the Ruby version.
37
+ desc 'Runs the tests for Travis CI'
38
+ task :travis do
39
+ suffix = '_' + RUBY_VERSION.gsub('.', '_')
40
+
41
+ if ENV['DATABASE']
42
+ if ENV['ADAPTER'] and ENV['ADAPTER'] == 'sqlite'
43
+ split = ENV['DATABASE'].split('.')
44
+
45
+ ENV['DATABASE'] = split[0] + suffix + '.' + split[1]
46
+ else
47
+ ENV['DATABASE'] += suffix
48
+ end
49
+ end
50
+
51
+ Dir.chdir(spec_dir)
52
+
53
+ sh(command)
54
+ end
34
55
  end