has_accounts_engine 1.1.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. data/app/assets/images/16x16/add.png +0 -0
  2. data/app/assets/images/16x16/alert.png +0 -0
  3. data/app/assets/images/16x16/combolink.png +0 -0
  4. data/app/assets/images/16x16/copy.png +0 -0
  5. data/app/assets/images/16x16/delete.png +0 -0
  6. data/app/assets/images/16x16/edit.png +0 -0
  7. data/app/assets/images/16x16/filter.png +0 -0
  8. data/app/assets/images/16x16/index.png +0 -0
  9. data/app/assets/images/16x16/notice.png +0 -0
  10. data/app/assets/images/16x16/pdf.png +0 -0
  11. data/app/assets/images/16x16/preferences.png +0 -0
  12. data/app/assets/images/16x16/print.png +0 -0
  13. data/app/assets/images/16x16/show.png +0 -0
  14. data/app/assets/images/22x22/arrow-right.png +0 -0
  15. data/app/assets/images/22x22/edit.png +0 -0
  16. data/app/assets/images/22x22/insert_booking.png +0 -0
  17. data/app/assets/images/22x22/remove.png +0 -0
  18. data/app/assets/images/32x32/edit.png +0 -0
  19. data/app/assets/images/32x32/remove.png +0 -0
  20. data/app/assets/images/48x48/accounting.png +0 -0
  21. data/app/assets/images/48x48/basic_claims_data.png +0 -0
  22. data/app/assets/images/48x48/contacts.png +0 -0
  23. data/app/assets/images/48x48/invoicing.png +0 -0
  24. data/app/assets/images/48x48/nav_users.png +0 -0
  25. data/app/assets/images/48x48/preferences.png +0 -0
  26. data/app/assets/images/48x48/projects.png +0 -0
  27. data/app/assets/images/48x48/salaries.png +0 -0
  28. data/app/assets/images/48x48/settings.png +0 -0
  29. data/app/assets/images/48x48/stocks.png +0 -0
  30. data/app/assets/images/48x48/store.png +0 -0
  31. data/app/assets/images/button_bg.png +0 -0
  32. data/app/assets/images/crystal-icons/16x16/filter.png +0 -0
  33. data/app/assets/images/down.png +0 -0
  34. data/app/assets/images/favicon.ico +0 -0
  35. data/app/assets/images/flavour-extended/48x48/invoice.png +0 -0
  36. data/app/assets/images/free-business-desktop-icons/48x48/business.png +0 -0
  37. data/app/assets/images/gnome-web-icons/48x48/Gnome-X-Office-Address-Book-48.png +0 -0
  38. data/app/assets/images/jquery-ui/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  39. data/app/assets/images/jquery-ui/ui-bg_flat_55_fbec88_40x100.png +0 -0
  40. data/app/assets/images/jquery-ui/ui-bg_glass_75_759fcf_1x400.png +0 -0
  41. data/app/assets/images/jquery-ui/ui-bg_glass_85_aeb9cf_1x400.png +0 -0
  42. data/app/assets/images/jquery-ui/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  43. data/app/assets/images/jquery-ui/ui-bg_gloss-wave_55_aeb9cf_500x100.png +0 -0
  44. data/app/assets/images/jquery-ui/ui-bg_inset-hard_100_759fcf_1x100.png +0 -0
  45. data/app/assets/images/jquery-ui/ui-bg_inset-hard_100_f6f6f6_1x100.png +0 -0
  46. data/app/assets/images/jquery-ui/ui-icons_217bc0_256x240.png +0 -0
  47. data/app/assets/images/jquery-ui/ui-icons_2e83ff_256x240.png +0 -0
  48. data/app/assets/images/jquery-ui/ui-icons_469bdd_256x240.png +0 -0
  49. data/app/assets/images/jquery-ui/ui-icons_6da8d5_256x240.png +0 -0
  50. data/app/assets/images/jquery-ui/ui-icons_cd0a0a_256x240.png +0 -0
  51. data/app/assets/images/jquery-ui/ui-icons_d8e7f3_256x240.png +0 -0
  52. data/app/assets/images/jquery-ui/ui-icons_f9bd01_256x240.png +0 -0
  53. data/app/assets/images/letter-logo.alpha.png +0 -0
  54. data/app/assets/images/letter-logo.big.png +0 -0
  55. data/app/assets/images/letter-logo.orig.png +0 -0
  56. data/app/assets/images/letter-logo.png +0 -0
  57. data/app/assets/images/loading.gif +0 -0
  58. data/app/assets/images/logo.png +0 -0
  59. data/app/assets/images/oxygen-icons/16x16/application-pdf.png +0 -0
  60. data/app/assets/images/oxygen-icons/16x16/document-preview.png +0 -0
  61. data/app/assets/images/oxygen-icons/16x16/document-print.png +0 -0
  62. data/app/assets/images/oxygen-icons/16x16/edit-copy.png +0 -0
  63. data/app/assets/images/oxygen-icons/16x16/go-next.png +0 -0
  64. data/app/assets/images/oxygen-icons/16x16/mail-mark-notjunk.png +0 -0
  65. data/app/assets/images/oxygen-icons/16x16/status_unknown.png +0 -0
  66. data/app/assets/images/oxygen-icons/48x48/preferences-system-time.png +0 -0
  67. data/app/assets/images/oxygen-icons/48x48/system-users.png +0 -0
  68. data/app/assets/images/oxygen-icons/48x48/view-bank-account-savings.png +0 -0
  69. data/app/assets/images/oxygen-icons/48x48/view-loan-asset.png +0 -0
  70. data/app/assets/images/rails.png +0 -0
  71. data/app/assets/images/up.png +0 -0
  72. data/app/assets/javascripts/has_accounts_engine/accounting-jquery.js +77 -0
  73. data/app/assets/javascripts/has_accounts_engine/accounting.js +411 -0
  74. data/app/assets/javascripts/has_accounts_engine/application.js +248 -0
  75. data/app/assets/javascripts/has_accounts_engine/bootstrap.datepicker.js +455 -0
  76. data/app/assets/javascripts/has_accounts_engine/cyt.js +117 -0
  77. data/app/assets/stylesheets/has_accounts_engine/.gitkeep +0 -0
  78. data/app/assets/stylesheets/has_accounts_engine/app_bootstrap.css.scss +5 -0
  79. data/app/assets/stylesheets/has_accounts_engine/application.css +14 -0
  80. data/app/assets/stylesheets/has_accounts_engine/bootstrap.datepicker.css +7 -0
  81. data/app/assets/stylesheets/has_accounts_engine/bootstrap.scss +1 -0
  82. data/app/assets/stylesheets/has_accounts_engine/partials/_base.sass +39 -0
  83. data/app/assets/stylesheets/has_accounts_engine/partials/_jquery.sass +1 -0
  84. data/app/assets/stylesheets/has_accounts_engine/partials/content/_accounting.sass +16 -0
  85. data/app/assets/stylesheets/has_accounts_engine/partials/content/_app.sass +46 -0
  86. data/app/assets/stylesheets/has_accounts_engine/partials/content/_combobox.sass +6 -0
  87. data/app/assets/stylesheets/has_accounts_engine/partials/content/_contextual.sass +10 -0
  88. data/app/assets/stylesheets/has_accounts_engine/partials/content/_fixed_sidebar.sass +11 -0
  89. data/app/assets/stylesheets/has_accounts_engine/partials/content/_form_view.sass +24 -0
  90. data/app/assets/stylesheets/has_accounts_engine/partials/content/_icons.sass +29 -0
  91. data/app/assets/stylesheets/has_accounts_engine/partials/content/_invoice.sass +10 -0
  92. data/app/assets/stylesheets/has_accounts_engine/partials/content/_overview.sass +44 -0
  93. data/app/assets/stylesheets/has_accounts_engine/partials/content/_select_form_labels.sass +28 -0
  94. data/app/assets/stylesheets/has_accounts_engine/partials/jquery/_jquery_ui.sass +1461 -0
  95. data/app/assets/stylesheets/has_accounts_engine/partials/print/_app.sass +2 -0
  96. data/app/assets/stylesheets/has_accounts_engine/partials/print/_layout.sass +66 -0
  97. data/app/assets/stylesheets/has_accounts_engine/print.scss +5 -0
  98. data/app/assets/stylesheets/has_accounts_engine/screen.scss +144 -0
  99. data/app/controllers/accounts_controller.rb +1 -1
  100. data/app/controllers/booking_templates_controller.rb +1 -1
  101. data/app/controllers/bookings_controller.rb +1 -1
  102. data/app/helpers/bootstrap_helper.rb +147 -0
  103. data/app/helpers/has_accounts_helper.rb +9 -0
  104. data/app/inputs/combobox_input.rb +22 -0
  105. data/app/inputs/date_field_input.rb +11 -0
  106. data/app/views/accounts/_booking_item.html.haml +2 -2
  107. data/app/views/accounts/_booking_list_header.html.haml +1 -1
  108. data/app/views/accounts/_booking_list_turnover.html.haml +1 -1
  109. data/app/views/accounts/_edit_booking.html.haml +1 -1
  110. data/app/views/accounts/_edit_bookings.html.haml +1 -1
  111. data/app/views/accounts/_show.html.haml +3 -2
  112. data/app/views/accounts/_show_bookings.html.haml +2 -2
  113. data/app/views/accounts/show.html.haml +1 -1
  114. data/app/views/booking_templates/_booking_template.html.haml +1 -1
  115. data/app/views/booking_templates/_form.html.haml +2 -2
  116. data/app/views/booking_templates/_list.html.haml +1 -1
  117. data/app/views/bookings/_booking.html.haml +1 -1
  118. data/app/views/bookings/_form.html.haml +6 -5
  119. data/app/views/bookings/_list.html.haml +1 -1
  120. data/app/views/bookings/_sidebar.html.haml +0 -5
  121. data/app/views/bookings/_simple_form.html.haml +2 -2
  122. data/app/views/has_accounts/_form.html.haml +5 -0
  123. data/app/views/has_accounts/_list.html.haml +15 -0
  124. data/app/views/has_accounts/_search_form.html.haml +6 -0
  125. data/app/views/has_accounts/_show.html.haml +2 -0
  126. data/app/views/has_accounts/_show_attachments.html.haml +9 -0
  127. data/app/views/has_accounts/_sidebar.html.haml +8 -0
  128. data/app/views/has_accounts/create.js.erb +3 -0
  129. data/app/views/has_accounts/destroy.js.erb +2 -0
  130. data/app/views/has_accounts/edit.html.haml +5 -0
  131. data/app/views/has_accounts/edit.js.erb +3 -0
  132. data/app/views/has_accounts/index.html.haml +6 -0
  133. data/app/views/has_accounts/new.html.haml +5 -0
  134. data/app/views/has_accounts/new.js.erb +3 -0
  135. data/app/views/has_accounts/show.html.haml +5 -0
  136. data/app/views/has_accounts/show_modal.js.erb +7 -0
  137. data/app/views/has_accounts/update.js.erb +3 -0
  138. data/app/views/layouts/has_accounts_engine.html.haml +48 -0
  139. data/config/locales/de.yml +14 -37
  140. data/config/locales/en.yml +26 -23
  141. data/lib/has_accounts_engine/railtie.rb +4 -0
  142. data/lib/has_accounts_engine/version.rb +1 -1
  143. metadata +169 -8
  144. data/app/controllers/bank_accounts_controller.rb +0 -3
  145. data/app/controllers/banks_controller.rb +0 -3
  146. data/app/views/bank_accounts/_bank_account.html.haml +0 -1
  147. data/app/views/bank_accounts/_form.html.haml +0 -10
  148. data/app/views/banks/_bank.html.haml +0 -3
  149. data/app/views/banks/_form.html.haml +0 -12
@@ -0,0 +1,248 @@
1
+ // This is a manifest file that'll be compiled into including all the files listed below.
2
+ // Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
3
+ // be included in the compiled file accessible from http://example.com/assets/application.js
4
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
5
+ // the compiled file.
6
+ //
7
+ //= require jquery
8
+ //= require jquery_ujs
9
+ //= require twitter/bootstrap
10
+ //= require select2
11
+ //= require has_accounts_engine/accounting
12
+ //= require has_accounts_engine/accounting-jquery
13
+ //= require has_accounts_engine/bootstrap.datepicker
14
+ //= require_tree .
15
+
16
+ // Application specific behaviour
17
+ function addAlternateTableBehaviour() {
18
+ $("table.list tr:odd").addClass("odd");
19
+ }
20
+
21
+ // Dirty Form
22
+ function makeEditForm(form) {
23
+ var buttons = form.find("fieldset.buttons");
24
+ buttons.animate({opacity: 1}, 1000);
25
+ }
26
+
27
+ function addDirtyForm() {
28
+ $(".form-view form").dirty_form()
29
+ .dirty(function(event, data){
30
+ makeEditForm($(this));
31
+ })
32
+
33
+ $(".form-view").focusin(function() {makeEditForm($(this))});
34
+ }
35
+
36
+ function addNestedFormBehaviour() {
37
+ $('body').on('click', '.delete-nested-form-item', function(event) {
38
+ var item = $(this).parents('.nested-form-item');
39
+ // Hide item
40
+ item.hide();
41
+ // Mark as ready to delete
42
+ item.find("input[name$='[_destroy]']").val("1");
43
+ item.addClass('delete');
44
+ // Drop input fields to prevent browser validation problems
45
+ item.find(":input").not("[name$='[_destroy]'], [name$='[id]']").remove();
46
+
47
+ // TODO: should be callbacks
48
+ updatePositions($(this).parents('.nested-form-container'));
49
+ updateLineItems();
50
+
51
+ // Don't follow link
52
+ event.preventDefault();
53
+ });
54
+ }
55
+
56
+ // Currency helpers
57
+ function currencyRound(value) {
58
+ if (isNaN(value)) {
59
+ return 0.0;
60
+ };
61
+
62
+ rounded = Math.round(value * 20) / 20;
63
+
64
+ return rounded.toFixed(2);
65
+ }
66
+
67
+ // Line Item calculation
68
+ function updateLineItemPrice(lineItem) {
69
+ var list = lineItem.parent();
70
+ var reference_code = lineItem.find(":input[name$='[reference_code]']").val();
71
+ var quantity = lineItem.find(":input[name$='[quantity]']").val();
72
+ if (quantity == '%' || quantity == 'saldo_of') {
73
+ var included_items;
74
+ if (reference_code == '') {
75
+ included_items = lineItem.prevAll('.line_item');
76
+ } else {
77
+ // Should match using ~= but acts_as_taggable_adds_colons between tags
78
+ included_items = list.find(":input[name$='[code]'][value='" + reference_code + "']").parents('.line_item, .saldo_line_item');
79
+ if (included_items.length == 0) {
80
+ // Should match using ~= but acts_as_taggable_adds_colons between tags
81
+ included_items = list.find(":input[name$='[include_in_saldo_list]'][value*='" + reference_code + "']").parents('.line_item, .saldo_line_item');
82
+ }
83
+ }
84
+ var price_input = lineItem.find(":input[name$='[price]']");
85
+ price_input.val(calculateTotalAmount(included_items));
86
+ }
87
+ }
88
+
89
+ function updateAllLineItemPrices() {
90
+ $('.line_item, .saldo_line_item').each(function() {
91
+ updateLineItemPrice($(this));
92
+ });
93
+ }
94
+
95
+ function calculateLineItemTotalAmount(lineItem) {
96
+ var times_input = lineItem.find(":input[name$='[times]']");
97
+ var times = accounting.parse(times_input.val());
98
+ if (isNaN(times)) {
99
+ times = 1;
100
+ };
101
+
102
+ var quantity_input = lineItem.find(":input[name$='[quantity]']");
103
+ var price_input = lineItem.find(":input[name$='[price]']");
104
+ var price = accounting.parse(price_input.val());
105
+
106
+ // For 'saldo_of' items, we don't take accounts into account
107
+ if (quantity_input.val() == "saldo_of") {
108
+ return currencyRound(price);
109
+ };
110
+
111
+ var direct_account_id = $('#line_items').data('direct-account-id');
112
+ var direct_account_factor = $('#line_items').data('direct-account-factor');
113
+
114
+ var factor = 0;
115
+ if (lineItem.find(":input[name$='[credit_account_id]']").val() == direct_account_id) {
116
+ factor = 1;
117
+ };
118
+ if (lineItem.find(":input[name$='[debit_account_id]']").val() == direct_account_id) {
119
+ factor = -1;
120
+ };
121
+
122
+ if (quantity_input.val() == '%') {
123
+ times = times / 100;
124
+ };
125
+
126
+ return currencyRound(times * price * factor * direct_account_factor);
127
+ }
128
+
129
+ function updateLineItemTotalAmount(lineItem) {
130
+ var total_amount_input = lineItem.find(".total_amount");
131
+ var total_amount = accounting.formatNumber(calculateLineItemTotalAmount(lineItem));
132
+
133
+ // Update Element
134
+ total_amount_input.text(total_amount);
135
+ }
136
+
137
+ function calculateTotalAmount(lineItems) {
138
+ var total_amount = 0;
139
+ $(lineItems).each(function() {
140
+ total_amount += accounting.parse($(this).find(".total_amount").text());
141
+ });
142
+
143
+ return currencyRound(total_amount);
144
+ }
145
+
146
+ function updateLineItems() {
147
+ if ($('#line_items').length > 0) {
148
+ $('.line_item, .saldo_line_item').each(function() {
149
+ updateLineItemPrice($(this));
150
+ updateLineItemTotalAmount($(this));
151
+ });
152
+ };
153
+ }
154
+
155
+ // Recalculate after every key stroke
156
+ function handleLineItemChange(event) {
157
+ // If character is <return>
158
+ if(event.keyCode == 13) {
159
+ // ...trigger form action
160
+ $(event.currentTarget).submit();
161
+ } else if(event.keyCode == 32) {
162
+ // ...trigger form action
163
+ $(event.currentTarget).submit();
164
+ } else {
165
+ updateLineItems();
166
+ }
167
+ }
168
+
169
+ function addCalculateTotalAmountBehaviour() {
170
+ $("#line_items").find(":input[name$='[times]'], :input[name$='[quantity]'], :input[name$='[price]'], input[name$='[reference_code]']").on('keyup', handleLineItemChange);
171
+ $("#line_items").bind("sortstop", handleLineItemChange);
172
+ }
173
+
174
+ // Sorting
175
+ function updatePositions(collection) {
176
+ var items = collection.find('.nested-form-item').not('.delete');
177
+ items.each(function(index, element) {
178
+ $(this).find("input[id$='_position']").val(index + 1)
179
+ });
180
+ }
181
+
182
+ function initAccounting() {
183
+ // accounting.js
184
+ // Settings object that controls default parameters for library methods:
185
+ accounting.settings = {
186
+ currency: {
187
+ symbol : "", // default currency symbol is '$'
188
+ format: "%v", // controls output: %s = symbol, %v = value/number (can be object: see below)
189
+ decimal : ".", // decimal point separator
190
+ thousand: "'", // thousands separator
191
+ precision : 2 // decimal places
192
+ },
193
+ number: {
194
+ precision : 2, // default precision on numbers is 0
195
+ thousand: "'",
196
+ decimal : "."
197
+ }
198
+ }
199
+ }
200
+
201
+ // Initialize behaviours
202
+ function initializeBehaviours() {
203
+ // Init settings
204
+ initAccounting();
205
+
206
+ // from cyt.js
207
+ addComboboxBehaviour();
208
+ addAutofocusBehaviour();
209
+ addDatePickerBehaviour();
210
+ addLinkifyContainersBehaviour();
211
+ addIconTooltipBehaviour();
212
+ addModalBehaviour();
213
+
214
+ // application
215
+ addAlternateTableBehaviour();
216
+ addNestedFormBehaviour();
217
+
218
+ addCalculateTotalAmountBehaviour();
219
+
220
+ updateLineItems();
221
+
222
+ // twitter bootstrap
223
+ $(function () {
224
+ $(".alert").alert();
225
+ $("*[rel=popover]").popover({
226
+ offset: 10
227
+ });
228
+ $('.small-tooltip').tooltip({
229
+ placement: 'right'
230
+ });
231
+ })
232
+
233
+ // select2
234
+ $('.select2').select2({
235
+ allowClear: true
236
+ });
237
+ $('.select2-tags').each(function(index, element) {
238
+ var tags = $(element).data('tags') || '';
239
+
240
+ $(element).select2({
241
+ tags: tags,
242
+ tokenSeparators: [","]
243
+ })
244
+ })
245
+ }
246
+
247
+ // Loads functions after DOM is ready
248
+ $(document).ready(initializeBehaviours);
@@ -0,0 +1,455 @@
1
+ /* =========================================================
2
+ * bootstrap-datepicker.js
3
+ * http://www.eyecon.ro/bootstrap-datepicker
4
+ * =========================================================
5
+ * Copyright 2012 Stefan Petre
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ * ========================================================= */
19
+
20
+ !function( $ ) {
21
+
22
+ // Picker object
23
+
24
+ var Datepicker = function(element, options){
25
+ this.element = $(element);
26
+ this.format = DPGlobal.parseFormat(options.format||this.element.data('date-format')||'mm/dd/yyyy');
27
+ this.container = options.container||'body'
28
+ this.picker = $(DPGlobal.template)
29
+ .appendTo(this.container)
30
+ .on({
31
+ click: $.proxy(this.click, this),
32
+ mousedown: $.proxy(this.mousedown, this)
33
+ });
34
+ this.isInput = this.element.is('input');
35
+ this.component = this.element.is('.date') ? this.element.find('.add-on') : false;
36
+
37
+ if (this.isInput) {
38
+ this.element.on({
39
+ focus: $.proxy(this.show, this),
40
+ blur: $.proxy(this.hide, this),
41
+ keyup: $.proxy(this.update, this)
42
+ });
43
+ } else {
44
+ if (this.component){
45
+ this.component.on('click', $.proxy(this.show, this));
46
+ } else {
47
+ this.element.on('click', $.proxy(this.show, this));
48
+ }
49
+ }
50
+ this.minViewMode = options.minViewMode||this.element.data('date-minviewmode')||0;
51
+ if (typeof this.minViewMode === 'string') {
52
+ switch (this.minViewMode) {
53
+ case 'months':
54
+ this.minViewMode = 1;
55
+ break;
56
+ case 'years':
57
+ this.minViewMode = 2;
58
+ break;
59
+ default:
60
+ this.minViewMode = 0;
61
+ break;
62
+ }
63
+ }
64
+ this.viewMode = options.viewMode||this.element.data('date-viewmode')||0;
65
+ if (typeof this.viewMode === 'string') {
66
+ switch (this.viewMode) {
67
+ case 'months':
68
+ this.viewMode = 1;
69
+ break;
70
+ case 'years':
71
+ this.viewMode = 2;
72
+ break;
73
+ default:
74
+ this.viewMode = 0;
75
+ break;
76
+ }
77
+ }
78
+ this.startViewMode = this.viewMode;
79
+ this.weekStart = options.weekStart||this.element.data('date-weekstart')||0;
80
+ this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
81
+ this.fillDow();
82
+ this.fillMonths();
83
+ this.update();
84
+ this.showMode();
85
+ };
86
+
87
+ Datepicker.prototype = {
88
+ constructor: Datepicker,
89
+
90
+ show: function(e) {
91
+ this.picker.show();
92
+ this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
93
+ this.place();
94
+ $(window).on('resize', $.proxy(this.place, this));
95
+ if (e ) {
96
+ e.stopPropagation();
97
+ e.preventDefault();
98
+ }
99
+ if (!this.isInput) {
100
+ $(document).on('mousedown', $.proxy(this.hide, this));
101
+ }
102
+ this.element.trigger({
103
+ type: 'show',
104
+ date: this.date
105
+ });
106
+ },
107
+
108
+ hide: function(){
109
+ this.picker.hide();
110
+ $(window).off('resize', this.place);
111
+ this.viewMode = this.startViewMode;
112
+ this.showMode();
113
+ if (!this.isInput) {
114
+ $(document).off('mousedown', this.hide);
115
+ }
116
+ this.set();
117
+ this.element.trigger({
118
+ type: 'hide',
119
+ date: this.date
120
+ });
121
+ },
122
+
123
+ set: function() {
124
+ var formated = DPGlobal.formatDate(this.date, this.format);
125
+ if (!this.isInput) {
126
+ if (this.component){
127
+ this.element.find('input').prop('value', formated);
128
+ }
129
+ this.element.data('date', formated);
130
+ } else {
131
+ this.element.prop('value', formated);
132
+ }
133
+ },
134
+
135
+ setValue: function(newDate) {
136
+ if (typeof newDate === 'string') {
137
+ this.date = DPGlobal.parseDate(newDate, this.format);
138
+ } else {
139
+ this.date = new Date(newDate);
140
+ }
141
+ this.set();
142
+ this.viewDate = new Date(this.date.getFullYear(), this.date.getMonth(), 1, 0, 0, 0, 0);
143
+ this.fill();
144
+ },
145
+
146
+ place: function(){
147
+ var offset = this.component ? this.component.offset() : this.element.offset();
148
+ this.picker.css({
149
+ top: offset.top + this.height,
150
+ left: offset.left
151
+ });
152
+ },
153
+
154
+ update: function(newDate){
155
+ this.date = DPGlobal.parseDate(
156
+ typeof newDate === 'string' ? newDate : (this.isInput ? this.element.prop('value') : this.element.data('date')),
157
+ this.format
158
+ );
159
+ this.viewDate = new Date(this.date.getFullYear(), this.date.getMonth(), 1, 0, 0, 0, 0);
160
+ this.fill();
161
+ },
162
+
163
+ fillDow: function(){
164
+ var dowCnt = this.weekStart;
165
+ var html = '<tr>';
166
+ while (dowCnt < this.weekStart + 7) {
167
+ html += '<th class="dow">'+DPGlobal.dates.daysMin[(dowCnt++)%7]+'</th>';
168
+ }
169
+ html += '</tr>';
170
+ this.picker.find('.datepicker-days thead').append(html);
171
+ },
172
+
173
+ fillMonths: function(){
174
+ var html = '';
175
+ var i = 0
176
+ while (i < 12) {
177
+ html += '<span class="month">'+DPGlobal.dates.monthsShort[i++]+'</span>';
178
+ }
179
+ this.picker.find('.datepicker-months td').append(html);
180
+ },
181
+
182
+ fill: function() {
183
+ var d = new Date(this.viewDate),
184
+ year = d.getFullYear(),
185
+ month = d.getMonth(),
186
+ currentDate = this.date.valueOf();
187
+ this.picker.find('.datepicker-days th:eq(1)')
188
+ .text(DPGlobal.dates.months[month]+' '+year);
189
+ var prevMonth = new Date(year, month-1, 28,0,0,0,0),
190
+ day = DPGlobal.getDaysInMonth(prevMonth.getFullYear(), prevMonth.getMonth());
191
+ prevMonth.setDate(day);
192
+ prevMonth.setDate(day - (prevMonth.getDay() - this.weekStart + 7)%7);
193
+ var nextMonth = new Date(prevMonth);
194
+ nextMonth.setDate(nextMonth.getDate() + 42);
195
+ nextMonth = nextMonth.valueOf();
196
+ html = [];
197
+ var clsName;
198
+ while(prevMonth.valueOf() < nextMonth) {
199
+ if (prevMonth.getDay() === this.weekStart) {
200
+ html.push('<tr>');
201
+ }
202
+ clsName = '';
203
+ if (prevMonth.getMonth() < month) {
204
+ clsName += ' old';
205
+ } else if (prevMonth.getMonth() > month) {
206
+ clsName += ' new';
207
+ }
208
+ if (prevMonth.valueOf() === currentDate) {
209
+ clsName += ' active';
210
+ }
211
+ html.push('<td class="day'+clsName+'">'+prevMonth.getDate() + '</td>');
212
+ if (prevMonth.getDay() === this.weekEnd) {
213
+ html.push('</tr>');
214
+ }
215
+ prevMonth.setDate(prevMonth.getDate()+1);
216
+ }
217
+ this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
218
+ var currentYear = this.date.getFullYear();
219
+
220
+ var months = this.picker.find('.datepicker-months')
221
+ .find('th:eq(1)')
222
+ .text(year)
223
+ .end()
224
+ .find('span').removeClass('active');
225
+ if (currentYear === year) {
226
+ months.eq(this.date.getMonth()).addClass('active');
227
+ }
228
+
229
+ html = '';
230
+ year = parseInt(year/10, 10) * 10;
231
+ var yearCont = this.picker.find('.datepicker-years')
232
+ .find('th:eq(1)')
233
+ .text(year + '-' + (year + 9))
234
+ .end()
235
+ .find('td');
236
+ year -= 1;
237
+ for (var i = -1; i < 11; i++) {
238
+ html += '<span class="year'+(i === -1 || i === 10 ? ' old' : '')+(currentYear === year ? ' active' : '')+'">'+year+'</span>';
239
+ year += 1;
240
+ }
241
+ yearCont.html(html);
242
+ },
243
+
244
+ click: function(e) {
245
+ e.stopPropagation();
246
+ e.preventDefault();
247
+ var target = $(e.target).closest('span, td, th');
248
+ if (target.length === 1) {
249
+ switch(target[0].nodeName.toLowerCase()) {
250
+ case 'th':
251
+ switch(target[0].className) {
252
+ case 'switch':
253
+ this.showMode(1);
254
+ break;
255
+ case 'prev':
256
+ case 'next':
257
+ this.viewDate['set'+DPGlobal.modes[this.viewMode].navFnc].call(
258
+ this.viewDate,
259
+ this.viewDate['get'+DPGlobal.modes[this.viewMode].navFnc].call(this.viewDate) +
260
+ DPGlobal.modes[this.viewMode].navStep * (target[0].className === 'prev' ? -1 : 1)
261
+ );
262
+ this.fill();
263
+ this.set();
264
+ break;
265
+ }
266
+ break;
267
+ case 'span':
268
+ if (target.is('.month')) {
269
+ var month = target.parent().find('span').index(target);
270
+ this.viewDate.setMonth(month);
271
+ } else {
272
+ var year = parseInt(target.text(), 10)||0;
273
+ this.viewDate.setFullYear(year);
274
+ }
275
+ if (this.viewMode !== 0) {
276
+ this.date = new Date(this.viewDate);
277
+ this.element.trigger({
278
+ type: 'changeDate',
279
+ date: this.date,
280
+ viewMode: DPGlobal.modes[this.viewMode].clsName
281
+ });
282
+ }
283
+ this.showMode(-1);
284
+ this.fill();
285
+ this.set();
286
+ break;
287
+ case 'td':
288
+ if (target.is('.day')){
289
+ var day = parseInt(target.text(), 10)||1;
290
+ var month = this.viewDate.getMonth();
291
+ if (target.is('.old')) {
292
+ month -= 1;
293
+ } else if (target.is('.new')) {
294
+ month += 1;
295
+ }
296
+ var year = this.viewDate.getFullYear();
297
+ this.date = new Date(year, month, day,0,0,0,0);
298
+ this.viewDate = new Date(year, month, Math.min(28, day),0,0,0,0);
299
+ this.fill();
300
+ this.set();
301
+ this.element.trigger({
302
+ type: 'changeDate',
303
+ date: this.date,
304
+ viewMode: DPGlobal.modes[this.viewMode].clsName
305
+ });
306
+ }
307
+ break;
308
+ }
309
+ }
310
+ },
311
+
312
+ mousedown: function(e){
313
+ e.stopPropagation();
314
+ e.preventDefault();
315
+ },
316
+
317
+ showMode: function(dir) {
318
+ if (dir) {
319
+ this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
320
+ }
321
+ this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
322
+ }
323
+ };
324
+
325
+ $.fn.datepicker = function ( option, val ) {
326
+ return this.each(function () {
327
+ var $this = $(this),
328
+ data = $this.data('datepicker'),
329
+ options = typeof option === 'object' && option;
330
+ if (!data) {
331
+ $this.data('datepicker', (data = new Datepicker(this, $.extend({}, $.fn.datepicker.defaults,options))));
332
+ }
333
+ if (typeof option === 'string') data[option](val);
334
+ });
335
+ };
336
+
337
+ $.fn.datepicker.defaults = {
338
+ };
339
+ $.fn.datepicker.Constructor = Datepicker;
340
+
341
+ var DPGlobal = {
342
+ modes: [
343
+ {
344
+ clsName: 'days',
345
+ navFnc: 'Month',
346
+ navStep: 1
347
+ },
348
+ {
349
+ clsName: 'months',
350
+ navFnc: 'FullYear',
351
+ navStep: 1
352
+ },
353
+ {
354
+ clsName: 'years',
355
+ navFnc: 'FullYear',
356
+ navStep: 10
357
+ }],
358
+ dates:{
359
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
360
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
361
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
362
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
363
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
364
+ },
365
+ isLeapYear: function (year) {
366
+ return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0))
367
+ },
368
+ getDaysInMonth: function (year, month) {
369
+ return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]
370
+ },
371
+ parseFormat: function(format){
372
+ var separator = format.match(/[.\/\-\s].*?/),
373
+ parts = format.split(/\W+/);
374
+ if (!separator || !parts || parts.length === 0){
375
+ throw new Error("Invalid date format.");
376
+ }
377
+ return {separator: separator, parts: parts};
378
+ },
379
+ parseDate: function(date, format) {
380
+ var parts = date.split(format.separator),
381
+ date = new Date(),
382
+ val;
383
+ date.setHours(0);
384
+ date.setMinutes(0);
385
+ date.setSeconds(0);
386
+ date.setMilliseconds(0);
387
+ if (parts.length === format.parts.length) {
388
+ for (var i=0, cnt = format.parts.length; i < cnt; i++) {
389
+ val = parseInt(parts[i], 10)||1;
390
+ switch(format.parts[i]) {
391
+ case 'dd':
392
+ case 'd':
393
+ date.setDate(val);
394
+ break;
395
+ case 'mm':
396
+ case 'm':
397
+ date.setMonth(val - 1);
398
+ break;
399
+ case 'yy':
400
+ date.setFullYear(2000 + val);
401
+ break;
402
+ case 'yyyy':
403
+ date.setFullYear(val);
404
+ break;
405
+ }
406
+ }
407
+ }
408
+ return date;
409
+ },
410
+ formatDate: function(date, format){
411
+ var val = {
412
+ d: date.getDate(),
413
+ m: date.getMonth() + 1,
414
+ yy: date.getFullYear().toString().substring(2),
415
+ yyyy: date.getFullYear()
416
+ };
417
+ val.dd = (val.d < 10 ? '0' : '') + val.d;
418
+ val.mm = (val.m < 10 ? '0' : '') + val.m;
419
+ var date = [];
420
+ for (var i=0, cnt = format.parts.length; i < cnt; i++) {
421
+ date.push(val[format.parts[i]]);
422
+ }
423
+ return date.join(format.separator);
424
+ },
425
+ headTemplate: '<thead>'+
426
+ '<tr>'+
427
+ '<th class="prev">&lsaquo;</th>'+
428
+ '<th colspan="5" class="switch"></th>'+
429
+ '<th class="next">&rsaquo;</th>'+
430
+ '</tr>'+
431
+ '</thead>',
432
+ contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>'
433
+ };
434
+ DPGlobal.template = '<div class="datepicker dropdown-menu">'+
435
+ '<div class="datepicker-days">'+
436
+ '<table class=" table-condensed">'+
437
+ DPGlobal.headTemplate+
438
+ '<tbody></tbody>'+
439
+ '</table>'+
440
+ '</div>'+
441
+ '<div class="datepicker-months">'+
442
+ '<table class="table-condensed">'+
443
+ DPGlobal.headTemplate+
444
+ DPGlobal.contTemplate+
445
+ '</table>'+
446
+ '</div>'+
447
+ '<div class="datepicker-years">'+
448
+ '<table class="table-condensed">'+
449
+ DPGlobal.headTemplate+
450
+ DPGlobal.contTemplate+
451
+ '</table>'+
452
+ '</div>'+
453
+ '</div>';
454
+
455
+ }( window.jQuery )