droom 0.0.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 (207) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +23 -0
  3. data/Rakefile +27 -0
  4. data/app/assets/images/droom/arrows.png +0 -0
  5. data/app/assets/images/droom/big_icons.png +0 -0
  6. data/app/assets/images/droom/blueblob.png +0 -0
  7. data/app/assets/images/droom/close.png +0 -0
  8. data/app/assets/images/droom/cross.png +0 -0
  9. data/app/assets/images/droom/download.png +0 -0
  10. data/app/assets/images/droom/greyblob.png +0 -0
  11. data/app/assets/images/droom/help/goodreader.jpg +0 -0
  12. data/app/assets/images/droom/ical.png +0 -0
  13. data/app/assets/images/droom/icons.png +0 -0
  14. data/app/assets/images/droom/medium_icons.png +0 -0
  15. data/app/assets/images/droom/medium_object_icons.png +0 -0
  16. data/app/assets/images/droom/minimonth.png +0 -0
  17. data/app/assets/images/droom/minisymbols.png +0 -0
  18. data/app/assets/images/droom/object_icons.png +0 -0
  19. data/app/assets/images/droom/pinkblob.png +0 -0
  20. data/app/assets/images/droom/place_busy.png +0 -0
  21. data/app/assets/images/droom/place_quiet.png +0 -0
  22. data/app/assets/images/droom/plus_bullet.png +0 -0
  23. data/app/assets/images/droom/search.png +0 -0
  24. data/app/assets/images/droom/setup.png +0 -0
  25. data/app/assets/images/droom/small_go.png +0 -0
  26. data/app/assets/images/droom/smallbullet.png +0 -0
  27. data/app/assets/images/droom/smallspinner.gif +0 -0
  28. data/app/assets/images/droom/spinner.gif +0 -0
  29. data/app/assets/images/droom/spr_toolbar_icons_r2.png +0 -0
  30. data/app/assets/images/droom/tablesort.png +0 -0
  31. data/app/assets/images/droom/tick.png +0 -0
  32. data/app/assets/images/droom/tinysymbols.png +0 -0
  33. data/app/assets/images/droom/twister.png +0 -0
  34. data/app/assets/images/droom/vcard.png +0 -0
  35. data/app/assets/images/droom/venue_bullet.png +0 -0
  36. data/app/assets/javascripts/droom.js.coffee +88 -0
  37. data/app/assets/javascripts/droom/calendar.js.coffee +121 -0
  38. data/app/assets/javascripts/droom/drag_sort.js.coffee +22 -0
  39. data/app/assets/javascripts/droom/forms.js.coffee +746 -0
  40. data/app/assets/javascripts/droom/lib/extensions.js.coffee +60 -0
  41. data/app/assets/javascripts/droom/lib/jquery.animate-colors.js +109 -0
  42. data/app/assets/javascripts/droom/lib/jquery.cookie.js +71 -0
  43. data/app/assets/javascripts/droom/lib/jquery.sortable.js +97 -0
  44. data/app/assets/javascripts/droom/lib/kalendae.js +1692 -0
  45. data/app/assets/javascripts/droom/lib/modernizr.js +4 -0
  46. data/app/assets/javascripts/droom/lib/parser_rules/advanced.js +553 -0
  47. data/app/assets/javascripts/droom/lib/parser_rules/simple.js +32 -0
  48. data/app/assets/javascripts/droom/lib/wysihtml5.js +9550 -0
  49. data/app/assets/javascripts/droom/map.js.coffee +123 -0
  50. data/app/assets/javascripts/droom/sort.js.coffee +126 -0
  51. data/app/assets/javascripts/droom/suggester.js.coffee +230 -0
  52. data/app/assets/stylesheets/droom.css.sass +1151 -0
  53. data/app/assets/stylesheets/lib/_kalendae.css.sass +142 -0
  54. data/app/assets/stylesheets/lib/_toolbar.css.sass +192 -0
  55. data/app/controllers/droom/dashboard_controller.rb +27 -0
  56. data/app/controllers/droom/document_attachments_controller.rb +19 -0
  57. data/app/controllers/droom/documents_controller.rb +116 -0
  58. data/app/controllers/droom/engine_controller.rb +43 -0
  59. data/app/controllers/droom/events_controller.rb +120 -0
  60. data/app/controllers/droom/group_invitations_controller.rb +47 -0
  61. data/app/controllers/droom/groups_controller.rb +67 -0
  62. data/app/controllers/droom/invitations_controller.rb +43 -0
  63. data/app/controllers/droom/memberships_controller.rb +47 -0
  64. data/app/controllers/droom/pages_controller.rb +61 -0
  65. data/app/controllers/droom/people_controller.rb +92 -0
  66. data/app/controllers/droom/suggestions_controller.rb +58 -0
  67. data/app/controllers/droom/venues_controller.rb +39 -0
  68. data/app/helpers/droom/droom_helper.rb +74 -0
  69. data/app/models/droom/agenda_category.rb +8 -0
  70. data/app/models/droom/category.rb +27 -0
  71. data/app/models/droom/document.rb +110 -0
  72. data/app/models/droom/document_attachment.rb +71 -0
  73. data/app/models/droom/document_link.rb +31 -0
  74. data/app/models/droom/event.rb +409 -0
  75. data/app/models/droom/event_set.rb +6 -0
  76. data/app/models/droom/group.rb +66 -0
  77. data/app/models/droom/group_invitation.rb +23 -0
  78. data/app/models/droom/invitation.rb +30 -0
  79. data/app/models/droom/membership.rb +27 -0
  80. data/app/models/droom/page.rb +26 -0
  81. data/app/models/droom/person.rb +302 -0
  82. data/app/models/droom/personal_document.rb +98 -0
  83. data/app/models/droom/recurrence_rule.rb +82 -0
  84. data/app/models/droom/venue.rb +125 -0
  85. data/app/views/droom/dashboard/_marginalia.html.haml +3 -0
  86. data/app/views/droom/dashboard/_my_future_events.html.haml +9 -0
  87. data/app/views/droom/dashboard/_my_group_documents.html.haml +6 -0
  88. data/app/views/droom/dashboard/_my_past_events.haml +6 -0
  89. data/app/views/droom/dashboard/index.html.haml +5 -0
  90. data/app/views/droom/documents/_created.html.haml +2 -0
  91. data/app/views/droom/documents/_document.html.haml +14 -0
  92. data/app/views/droom/documents/_document_line.html.haml +2 -0
  93. data/app/views/droom/documents/_documents_list.html.haml +31 -0
  94. data/app/views/droom/documents/_documents_table.html.haml +13 -0
  95. data/app/views/droom/documents/_event_document_form.html.haml +15 -0
  96. data/app/views/droom/documents/_form.html.haml +22 -0
  97. data/app/views/droom/documents/_listing.html.haml +8 -0
  98. data/app/views/droom/documents/_suggested.html.haml +9 -0
  99. data/app/views/droom/documents/_table_document.html.haml +31 -0
  100. data/app/views/droom/documents/edit.html.haml +1 -0
  101. data/app/views/droom/documents/index.html.haml +29 -0
  102. data/app/views/droom/documents/new.html.haml +1 -0
  103. data/app/views/droom/errors/bang.html.haml +12 -0
  104. data/app/views/droom/errors/not_allowed.html.haml +12 -0
  105. data/app/views/droom/errors/not_found.html.haml +12 -0
  106. data/app/views/droom/events/_attachment.html.haml +1 -0
  107. data/app/views/droom/events/_attachment_list.html.haml +4 -0
  108. data/app/views/droom/events/_calendar.html.haml +54 -0
  109. data/app/views/droom/events/_created.html.haml +2 -0
  110. data/app/views/droom/events/_event.html.haml +77 -0
  111. data/app/views/droom/events/_event_line.html.haml +13 -0
  112. data/app/views/droom/events/_events.html.haml +2 -0
  113. data/app/views/droom/events/_form.html.haml +35 -0
  114. data/app/views/droom/events/_invitations.html.haml +20 -0
  115. data/app/views/droom/events/_other_page_parts.html.haml +0 -0
  116. data/app/views/droom/events/_popup_event.html.haml +6 -0
  117. data/app/views/droom/events/_suggested.html.haml +12 -0
  118. data/app/views/droom/events/_views.html.haml +7 -0
  119. data/app/views/droom/events/edit.html.haml +1 -0
  120. data/app/views/droom/events/index.html.haml +12 -0
  121. data/app/views/droom/events/index.rss.builder +20 -0
  122. data/app/views/droom/events/new.html.haml +1 -0
  123. data/app/views/droom/events/show.html.haml +4 -0
  124. data/app/views/droom/group_invitations/_attending_groups.html.haml +8 -0
  125. data/app/views/droom/group_invitations/_created.html.haml +3 -0
  126. data/app/views/droom/group_invitations/_form.html.haml +4 -0
  127. data/app/views/droom/group_invitations/new.html.haml +1 -0
  128. data/app/views/droom/groups/_created.html.haml +3 -0
  129. data/app/views/droom/groups/_form.html.haml +12 -0
  130. data/app/views/droom/groups/_group.html.haml +11 -0
  131. data/app/views/droom/groups/_groups.html.haml +3 -0
  132. data/app/views/droom/groups/edit.html.haml +1 -0
  133. data/app/views/droom/groups/index.html.haml +7 -0
  134. data/app/views/droom/groups/show.html.haml +13 -0
  135. data/app/views/droom/invitations/_attending_people.html.haml +8 -0
  136. data/app/views/droom/invitations/_created.html.haml +3 -0
  137. data/app/views/droom/invitations/_form.html.haml +5 -0
  138. data/app/views/droom/invitations/new.html.haml +1 -0
  139. data/app/views/droom/memberships/_button.html.haml +11 -0
  140. data/app/views/droom/memberships/_created.html.haml +4 -0
  141. data/app/views/droom/memberships/_form.html.haml +7 -0
  142. data/app/views/droom/memberships/_member.html.haml +33 -0
  143. data/app/views/droom/memberships/_memberships.html.haml +4 -0
  144. data/app/views/droom/pages/_contents.html.haml +10 -0
  145. data/app/views/droom/pages/_form.html.haml +36 -0
  146. data/app/views/droom/pages/_full_page.html.haml +17 -0
  147. data/app/views/droom/pages/_page.html.haml +5 -0
  148. data/app/views/droom/pages/_pages.html.haml +2 -0
  149. data/app/views/droom/pages/admin.html.haml +24 -0
  150. data/app/views/droom/pages/edit.html.haml +1 -0
  151. data/app/views/droom/pages/index.html.haml +10 -0
  152. data/app/views/droom/pages/new.html.haml +4 -0
  153. data/app/views/droom/pages/show.html.haml +5 -0
  154. data/app/views/droom/people/_created.html.haml +6 -0
  155. data/app/views/droom/people/_form.html.haml +40 -0
  156. data/app/views/droom/people/_people.html.haml +29 -0
  157. data/app/views/droom/people/_person.html.haml +34 -0
  158. data/app/views/droom/people/_suggested.html.haml +9 -0
  159. data/app/views/droom/people/edit.html.haml +1 -0
  160. data/app/views/droom/people/index.html.haml +11 -0
  161. data/app/views/droom/people/new.html.haml +1 -0
  162. data/app/views/droom/people/show.html.haml +1 -0
  163. data/app/views/droom/shared/_calendar_and_search.html.haml +2 -0
  164. data/app/views/droom/shared/_calendar_holder.haml +3 -0
  165. data/app/views/droom/shared/_controls.html.haml +8 -0
  166. data/app/views/droom/shared/_navigation.html.haml +8 -0
  167. data/app/views/droom/shared/_search_form.html.haml +4 -0
  168. data/app/views/droom/shared/_suggestions.html.haml +11 -0
  169. data/app/views/droom/shared/_toolbar.html.haml +17 -0
  170. data/app/views/droom/venues/_suggested.html.haml +6 -0
  171. data/app/views/droom/venues/index.html.haml +6 -0
  172. data/app/views/droom/venues/show.html.haml +21 -0
  173. data/app/views/layouts/droom/application.html.haml +36 -0
  174. data/config/initializers/dav.rb +2 -0
  175. data/config/initializers/paperclip.rb +26 -0
  176. data/config/initializers/snail.rb +2 -0
  177. data/config/locales/en.yml +191 -0
  178. data/config/routes.rb +51 -0
  179. data/db/migrate/20120910075016_create_droom_data.rb +134 -0
  180. data/db/migrate/20120917095804_agenda_sections.rb +13 -0
  181. data/db/migrate/20120918121352_add_postal_address_to_people.rb +10 -0
  182. data/db/migrate/20121009075049_give_groups_descriptions.rb +5 -0
  183. data/db/migrate/20121009105244_more_names.rb +5 -0
  184. data/db/migrate/20121009145944_event_agenda_sections.rb +13 -0
  185. data/db/migrate/20121011091230_create_group_invitations.rb +10 -0
  186. data/db/migrate/20121012144720_give_people_positions.rb +5 -0
  187. data/db/migrate/20121012154558_help_pages.rb +13 -0
  188. data/db/migrate/20121012163201_category_slugs.rb +5 -0
  189. data/db/migrate/20121101160102_document_links.rb +14 -0
  190. data/db/migrate/20121101181247_people_visibility.rb +7 -0
  191. data/db/migrate/20121102094738_shy_people.rb +6 -0
  192. data/db/migrate/20121102095856_visibility_defaults.rb +8 -0
  193. data/lib/droom.rb +73 -0
  194. data/lib/droom/dav_resource.rb +36 -0
  195. data/lib/droom/engine.rb +8 -0
  196. data/lib/droom/helpers.rb +25 -0
  197. data/lib/droom/monkeys.rb +15 -0
  198. data/lib/droom/renderers.rb +13 -0
  199. data/lib/droom/validators.rb +5 -0
  200. data/lib/droom/version.rb +3 -0
  201. data/lib/generators/droom/dashboard/dashboard_generator.rb +16 -0
  202. data/lib/generators/droom/install/USAGE +9 -0
  203. data/lib/generators/droom/install/install_generator.rb +15 -0
  204. data/lib/generators/droom/install/templates/droom_initializer.rb +6 -0
  205. data/lib/generators/droom/views/views_generator.rb +9 -0
  206. data/lib/tasks/droom_tasks.rake +4 -0
  207. metadata +635 -0
@@ -0,0 +1,60 @@
1
+ # Return the last item in an array.
2
+
3
+ unless Array.prototype.last?
4
+ Array.prototype.last = () ->
5
+ @[@.length - 1]
6
+
7
+ # Return the first item in an array.
8
+
9
+ unless Array.prototype.first?
10
+ Array.prototype.first = () ->
11
+ @[0]
12
+
13
+ # Return the (mathematically) greatest item in an array.
14
+
15
+ unless Array.prototype.max?
16
+ Array.prototype.max = () ->
17
+ Math.max.apply Math, this
18
+
19
+ # Return the (mathematically) least item in an array.
20
+
21
+ unless Array.prototype.min?
22
+ Array.prototype.min = () ->
23
+ Math.min.apply Math, this
24
+
25
+ # Return true if this array contains any items
26
+
27
+ unless Array.prototype.any?
28
+ Array.prototype.any = ()->
29
+ @length > 0
30
+
31
+ # Return true unless this array contains any items
32
+
33
+ unless Array.prototype.empty?
34
+ Array.prototype.empty = ()->
35
+ @length == 0
36
+
37
+ # Return this array as a sentence in the form "x, y and z".
38
+
39
+ unless Array.prototype.toSentence?
40
+ Array.prototype.toSentence = ()->
41
+ @join(", ").replace(/,\s([^,]+)$/, ' and $1')
42
+
43
+ # Truncate a string to the specified number of characters. Add an ellipsis if shortening actually happens.
44
+
45
+ unless String::truncate?
46
+ String::truncate = (length, separator, ellipsis)->
47
+ length ?= 100
48
+ ellipsis ?='...'
49
+ if @length > length
50
+ trimmed = @substr 0, length - ellipsis.length
51
+ trimmed = trimmed.substr(0, trimmed.lastIndexOf(separator)) if separator?
52
+ trimmed + '...'
53
+ else
54
+ @
55
+
56
+ # Remove leading and trailing spaces from a string. Unavailable before JS1.5.
57
+
58
+ unless String::trim?
59
+ String::trim = (length, separator, ellipsis)->
60
+ String(@).replace(/^\s+|\s+$/g, '')
@@ -0,0 +1,109 @@
1
+ /**!
2
+ * @preserve Color animation 20120928
3
+ * http://www.bitstorm.org/jquery/color-animation/
4
+ * Copyright 2011, 2012 Edwin Martin <edwin@bitstorm.org>
5
+ * Released under the MIT and GPL licenses.
6
+ */
7
+
8
+ (function($) {
9
+ /**
10
+ * Check whether the browser supports RGBA color mode.
11
+ *
12
+ * Author Mehdi Kabab <http://pioupioum.fr>
13
+ * @return {boolean} True if the browser support RGBA. False otherwise.
14
+ */
15
+ function isRGBACapable() {
16
+ var $script = $('script:first'),
17
+ color = $script.css('color'),
18
+ result = false;
19
+ if (/^rgba/.test(color)) {
20
+ result = true;
21
+ } else {
22
+ try {
23
+ result = ( color != $script.css('color', 'rgba(0, 0, 0, 0.5)').css('color') );
24
+ $script.css('color', color);
25
+ } catch (e) {
26
+ }
27
+ }
28
+
29
+ return result;
30
+ }
31
+
32
+ $.extend(true, $, {
33
+ support: {
34
+ 'rgba': isRGBACapable()
35
+ }
36
+ });
37
+
38
+ var properties = ['color', 'backgroundColor', 'borderBottomColor', 'borderLeftColor', 'borderRightColor', 'borderTopColor', 'outlineColor'];
39
+ $.each(properties, function(i, property) {
40
+ $.Tween.propHooks[ property ] = {
41
+ get: function(tween) {
42
+ return $(tween.elem).css(property);
43
+ },
44
+ set: function(tween) {
45
+ var style = tween.elem.style;
46
+ var p_begin = parseColor($(tween.elem).css(property));
47
+ var p_end = parseColor(tween.end);
48
+ tween.run = function(progress) {
49
+ style[property] = calculateColor(p_begin, p_end, progress);
50
+ }
51
+ }
52
+ }
53
+ });
54
+
55
+ // borderColor doesn't fit in standard fx.step above.
56
+ $.Tween.propHooks.borderColor = {
57
+ set: function(tween) {
58
+ var style = tween.elem.style;
59
+ var p_begin = [];
60
+ var borders = properties.slice(2, 6); // All four border properties
61
+ $.each(borders, function(i, property) {
62
+ p_begin[property] = parseColor($(tween.elem).css(property));
63
+ });
64
+ var p_end = parseColor(tween.end);
65
+ tween.run = function(progress) {
66
+ $.each(borders, function(i, property) {
67
+ style[property] = calculateColor(p_begin[property], p_end, progress);
68
+ });
69
+ }
70
+ }
71
+ }
72
+
73
+ // Calculate an in-between color. Returns "#aabbcc"-like string.
74
+ function calculateColor(begin, end, pos) {
75
+ var color = 'rgb' + ($.support['rgba'] ? 'a' : '') + '('
76
+ + parseInt((begin[0] + pos * (end[0] - begin[0])), 10) + ','
77
+ + parseInt((begin[1] + pos * (end[1] - begin[1])), 10) + ','
78
+ + parseInt((begin[2] + pos * (end[2] - begin[2])), 10);
79
+ if ($.support['rgba']) {
80
+ color += ',' + (begin && end ? parseFloat(begin[3] + pos * (end[3] - begin[3])) : 1);
81
+ }
82
+ color += ')';
83
+ return color;
84
+ }
85
+
86
+ // Parse an CSS-syntax color. Outputs an array [r, g, b]
87
+ function parseColor(color) {
88
+ var match, triplet;
89
+
90
+ // Match #aabbcc
91
+ if (match = /#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})/.exec(color)) {
92
+ triplet = [parseInt(match[1], 16), parseInt(match[2], 16), parseInt(match[3], 16), 1];
93
+
94
+ // Match #abc
95
+ } else if (match = /#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])/.exec(color)) {
96
+ triplet = [parseInt(match[1], 16) * 17, parseInt(match[2], 16) * 17, parseInt(match[3], 16) * 17, 1];
97
+
98
+ // Match rgb(n, n, n)
99
+ } else if (match = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)) {
100
+ triplet = [parseInt(match[1]), parseInt(match[2]), parseInt(match[3]), 1];
101
+
102
+ } else if (match = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9\.]*)\s*\)/.exec(color)) {
103
+ triplet = [parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3], 10),parseFloat(match[4])];
104
+
105
+ // No browser returns rgb(n%, n%, n%), so little reason to support this format.
106
+ }
107
+ return triplet;
108
+ }
109
+ })(jQuery);
@@ -0,0 +1,71 @@
1
+ /*!
2
+ * jQuery Cookie Plugin v1.3
3
+ * https://github.com/carhartl/jquery-cookie
4
+ *
5
+ * Copyright 2011, Klaus Hartl
6
+ * Dual licensed under the MIT or GPL Version 2 licenses.
7
+ * http://www.opensource.org/licenses/mit-license.php
8
+ * http://www.opensource.org/licenses/GPL-2.0
9
+ */
10
+ (function ($, document, undefined) {
11
+
12
+ var pluses = /\+/g;
13
+
14
+ function raw(s) {
15
+ return s;
16
+ }
17
+
18
+ function decoded(s) {
19
+ return decodeURIComponent(s.replace(pluses, ' '));
20
+ }
21
+
22
+ var config = $.cookie = function (key, value, options) {
23
+
24
+ // write
25
+ if (value !== undefined) {
26
+ options = $.extend({}, config.defaults, options);
27
+
28
+ if (value === null) {
29
+ options.expires = -1;
30
+ }
31
+
32
+ if (typeof options.expires === 'number') {
33
+ var days = options.expires, t = options.expires = new Date();
34
+ t.setDate(t.getDate() + days);
35
+ }
36
+
37
+ value = config.json ? JSON.stringify(value) : String(value);
38
+
39
+ return (document.cookie = [
40
+ encodeURIComponent(key), '=', config.raw ? value : encodeURIComponent(value),
41
+ options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
42
+ options.path ? '; path=' + options.path : '',
43
+ options.domain ? '; domain=' + options.domain : '',
44
+ options.secure ? '; secure' : ''
45
+ ].join(''));
46
+ }
47
+
48
+ // read
49
+ var decode = config.raw ? raw : decoded;
50
+ var cookies = document.cookie.split('; ');
51
+ for (var i = 0, parts; (parts = cookies[i] && cookies[i].split('=')); i++) {
52
+ if (decode(parts.shift()) === key) {
53
+ var cookie = decode(parts.join('='));
54
+ return config.json ? JSON.parse(cookie) : cookie;
55
+ }
56
+ }
57
+
58
+ return null;
59
+ };
60
+
61
+ config.defaults = {};
62
+
63
+ $.removeCookie = function (key, options) {
64
+ if ($.cookie(key) !== null) {
65
+ $.cookie(key, null, options);
66
+ return true;
67
+ }
68
+ return false;
69
+ };
70
+
71
+ })(jQuery, document);
@@ -0,0 +1,97 @@
1
+ /*
2
+ * HTML5 Sortable jQuery Plugin
3
+ * http://farhadi.ir/projects/html5sortable
4
+ *
5
+ * Copyright 2012, Ali Farhadi
6
+ * Released under the MIT license.
7
+ */
8
+ (function($) {
9
+ var dragging, placeholders = $();
10
+ $.fn.sortable = function(options) {
11
+ var method = String(options);
12
+ options = $.extend({
13
+ connectWith: false
14
+ }, options);
15
+ return this.each(function() {
16
+ if (/^enable|disable|destroy$/.test(method)) {
17
+ var items = $(this).children($(this).data('items')).attr('draggable', method == 'enable');
18
+ if (method == 'destroy') {
19
+ items.add(this).removeData('connectWith items')
20
+ .off('dragstart.h5s dragend.h5s selectstart.h5s dragover.h5s dragenter.h5s drop.h5s');
21
+ }
22
+ return;
23
+ }
24
+ var isHandle, index, items = $(this).children(options.items);
25
+ if (/^ul|ol$/i.test(this.tagName)) {
26
+ var tag = 'li'
27
+ } else if (/^tbody$/i.test(this.tagName)) {
28
+ var tag = 'tr'
29
+ var width = this.rows[0].cells.length
30
+ } else {
31
+ var tag = 'div'
32
+ }
33
+ if (tag = 'tr') {
34
+ var placeholder = $('<tr class="sortable-placeholder"><td colspan=' + width + '></td></tr>');
35
+ } else {
36
+ var placeholder = $('<' + tag + ' class="sortable-placeholder">');
37
+ }
38
+ items.find(options.handle).mousedown(function() {
39
+ isHandle = true;
40
+ }).mouseup(function() {
41
+ isHandle = false;
42
+ });
43
+ $(this).data('items', options.items)
44
+ placeholders = placeholders.add(placeholder);
45
+ if (options.connectWith) {
46
+ $(options.connectWith).add(this).data('connectWith', options.connectWith);
47
+ }
48
+ items.attr('draggable', 'true').on('dragstart.h5s', function(e) {
49
+ if (options.handle && !isHandle) {
50
+ return false;
51
+ }
52
+ isHandle = false;
53
+ var dt = e.originalEvent.dataTransfer;
54
+ dt.effectAllowed = 'move';
55
+ dt.setData('Text', 'dummy');
56
+ index = (dragging = $(this)).addClass('sortable-dragging').index();
57
+ }).on('dragend.h5s', function() {
58
+ if (!dragging) {
59
+ return;
60
+ }
61
+ dragging.removeClass('sortable-dragging').show();
62
+ placeholders.detach();
63
+ if (index != dragging.index()) {
64
+ dragging.parent().trigger('sortupdate', {item: dragging});
65
+ }
66
+ dragging = null;
67
+ }).not('a[href], img').on('selectstart.h5s', function() {
68
+ this.dragDrop && this.dragDrop();
69
+ return false;
70
+ }).end().add([this, placeholder]).on('dragover.h5s dragenter.h5s drop.h5s', function(e) {
71
+ if (!items.is(dragging) && options.connectWith !== $(dragging).parent().data('connectWith')) {
72
+ return true;
73
+ }
74
+ if (e.type == 'drop') {
75
+ e.stopPropagation();
76
+ placeholders.filter(':visible').after(dragging);
77
+ dragging.trigger('dragend.h5s');
78
+ return false;
79
+ }
80
+ e.preventDefault();
81
+ e.originalEvent.dataTransfer.dropEffect = 'move';
82
+ if (items.is(this)) {
83
+ if (options.forcePlaceholderSize) {
84
+ placeholder.height(dragging.outerHeight());
85
+ }
86
+ dragging.hide();
87
+ $(this)[placeholder.index() < $(this).index() ? 'after' : 'before'](placeholder);
88
+ placeholders.not(placeholder).detach();
89
+ } else if (!placeholders.is(this) && !$(this).children(options.items).length) {
90
+ placeholders.detach();
91
+ $(this).append(placeholder);
92
+ }
93
+ return false;
94
+ });
95
+ });
96
+ };
97
+ })(jQuery);
@@ -0,0 +1,1692 @@
1
+ /********************************************************************
2
+ * Kalendae, a framework agnostic javascript date picker *
3
+ * Copyright(c) 2012 Jarvis Badgley (chipersoft@gmail.com) *
4
+ * http://github.com/ChiperSoft/Kalendae *
5
+ * Version 0.2.6 *
6
+ ********************************************************************/
7
+
8
+ (function (undefined) {
9
+
10
+ var today;
11
+
12
+ var Kalendae = function (targetElement, options) {
13
+ if (typeof document.addEventListener !== 'function') return;
14
+
15
+ //if the first argument isn't an element and isn't a string, assume that it is the options object
16
+ var is_element = false;
17
+ try {
18
+ is_element = targetElement instanceof Element;
19
+ }
20
+ catch (err) {
21
+ is_element = !!targetElement && is_element.nodeType === 1;
22
+ }
23
+ if (!(is_element || typeof(targetElement) === 'string')) options = targetElement;
24
+
25
+ var self = this,
26
+ classes = self.classes,
27
+ opts = self.settings = util.merge(self.defaults, {attachTo:targetElement}, options || {}),
28
+ $container = self.container = util.make('div', {'class':classes.container}),
29
+ calendars = self.calendars = [],
30
+ startDay = moment().day(opts.weekStart),
31
+ vsd,
32
+ columnHeaders = [],
33
+ $cal,
34
+ $title,
35
+ $caption,
36
+ $header,
37
+ $days, dayNodes = [],
38
+ $span,
39
+ i = 0,
40
+ j = opts.months;
41
+
42
+ if (util.isIE8()) util.addClassName($container, 'ie8');
43
+
44
+ //generate the column headers (Su, Mo, Tu, etc)
45
+ i = 7;
46
+ while (i--) {
47
+ columnHeaders.push( startDay.format('ddd').substr(0,opts.columnHeaderLength) );
48
+ startDay.add('days',1);
49
+ }
50
+
51
+ //setup publish/subscribe and apply any subscriptions passed in settings
52
+ MinPubSub(self);
53
+ if (typeof opts.subscribe === 'object') {
54
+ for (i in opts.subscribe) if (opts.subscribe.hasOwnProperty(i)) {
55
+ self.subscribe(i, opts.subscribe[i]);
56
+ }
57
+ }
58
+
59
+ //process default selected dates
60
+ self._sel = [];
61
+ if (!!opts.selected) self.setSelected(opts.selected, false);
62
+
63
+ //set the view month
64
+ if (!!opts.viewStartDate) {
65
+ vsd = moment(opts.viewStartDate, opts.format);
66
+ } else if (self._sel.length > 0) {
67
+ vsd = moment(self._sel[0]);
68
+ } else {
69
+ vsd = moment();
70
+ }
71
+ self.viewStartDate = vsd.date(1);
72
+
73
+ var viewDelta = ({
74
+ 'past' : opts.months-1,
75
+ 'today-past' : opts.months-1,
76
+ 'any' : opts.months>2?Math.floor(opts.months/2):0,
77
+ 'today-future' : 0,
78
+ 'future' : 0
79
+ })[this.settings.direction];
80
+
81
+
82
+ if (viewDelta && moment().month()==moment(self.viewStartDate).month()){
83
+ self.viewStartDate = moment(self.viewStartDate).subtract({M:viewDelta}).date(1);
84
+ }
85
+
86
+
87
+ if (typeof opts.blackout === 'function') {
88
+ self.blackout = opts.blackout;
89
+ } else if (!!opts.blackout) {
90
+ var bdates = parseDates(opts.blackout, opts.parseSplitDelimiter);
91
+ self.blackout = function (input) {
92
+ input = moment(input).yearDay();
93
+ if (input < 1 || !self._sel || self._sel.length < 1) return false;
94
+ var i = bdates.length;
95
+ while (i--) if (bdates[i].yearDay() === input) return true;
96
+ return false;
97
+ }
98
+ } else {
99
+ self.blackout = function () {return false;}
100
+ }
101
+
102
+
103
+ self.direction = self.directions[opts.direction] ? self.directions[opts.direction] : self.directions['any'];
104
+
105
+
106
+ //for the total months setting, generate N calendar views and add them to the container
107
+ j = Math.max(opts.months,1);
108
+ while (j--) {
109
+ $cal = util.make('div', {'class':classes.calendar}, $container);
110
+
111
+ $cal.setAttribute('data-cal-index', j);
112
+ if (opts.months > 1) {
113
+ if (j == Math.max(opts.months-1,1)) util.addClassName($cal, classes.monthFirst);
114
+ else if (j === 0) util.addClassName($cal, classes.monthLast);
115
+ else util.addClassName($cal, classes.monthMiddle);
116
+ }
117
+
118
+ //title bar
119
+ $title = util.make('div', {'class':classes.title}, $cal);
120
+ util.make('a', {'class':classes.previousYear}, $title); //previous button
121
+ util.make('a', {'class':classes.previousMonth}, $title); //previous button
122
+ util.make('a', {'class':classes.nextYear}, $title); //next button
123
+ util.make('a', {'class':classes.nextMonth}, $title); //next button
124
+ $caption = util.make('span', {'class':classes.caption}, $title); //title caption
125
+
126
+ //column headers
127
+ $header = util.make('div', {'class':classes.header}, $cal);
128
+ i = 0;
129
+ do {
130
+ $span = util.make('span', {}, $header);
131
+ $span.innerHTML = columnHeaders[i];
132
+ } while (++i < 7)
133
+
134
+ //individual day cells
135
+ $days = util.make('div', {'class':classes.days}, $cal);
136
+ i = 0;
137
+ dayNodes = [];
138
+ while (i++ < 42) {
139
+ dayNodes.push(util.make('span', {}, $days));
140
+ }
141
+
142
+ //store each calendar view for easy redrawing
143
+ calendars.push({
144
+ caption:$caption,
145
+ days:dayNodes
146
+ });
147
+
148
+ if (j) util.make('div', {'class':classes.monthSeparator}, $container);
149
+ }
150
+
151
+ self.draw();
152
+
153
+ util.addEvent($container, 'mousedown', function (event, target) {
154
+ var clickedDate;
155
+ if (util.hasClassName(target, classes.nextMonth)) {
156
+ //NEXT MONTH BUTTON
157
+ if (!self.disableNext && self.publish('view-changed', self, ['next-month']) !== false) {
158
+ self.viewStartDate.add('months',1);
159
+ self.draw();
160
+ }
161
+ return false;
162
+
163
+ } else if (util.hasClassName(target, classes.previousMonth)) {
164
+ //PREVIOUS MONTH BUTTON
165
+ if (!self.disablePreviousMonth && self.publish('view-changed', self, ['previous-month']) !== false) {
166
+ self.viewStartDate.subtract('months',1);
167
+ self.draw();
168
+ }
169
+ return false;
170
+
171
+ } else if (util.hasClassName(target, classes.nextYear)) {
172
+ //NEXT MONTH BUTTON
173
+ if (!self.disableNext && self.publish('view-changed', self, ['next-year']) !== false) {
174
+ self.viewStartDate.add('years',1);
175
+ self.draw();
176
+ }
177
+ return false;
178
+
179
+ } else if (util.hasClassName(target, classes.previousYear)) {
180
+ //PREVIOUS MONTH BUTTON
181
+ if (!self.disablePreviousMonth && self.publish('view-changed', self, ['previous-year']) !== false) {
182
+ self.viewStartDate.subtract('years',1);
183
+ self.draw();
184
+ }
185
+ return false;
186
+
187
+
188
+
189
+ } else if (util.hasClassName(target.parentNode, classes.days) && util.hasClassName(target, classes.dayActive) && (clickedDate = target.getAttribute('data-date'))) {
190
+ //DAY CLICK
191
+ clickedDate = moment(clickedDate, opts.dayAttributeFormat).hours(12);
192
+ if (self.publish('date-clicked', self, [clickedDate]) !== false) {
193
+
194
+ switch (opts.mode) {
195
+ case 'multiple':
196
+ if (!self.addSelected(clickedDate)) self.removeSelected(clickedDate);
197
+ break;
198
+ case 'range':
199
+ self.addSelected(clickedDate);
200
+ break;
201
+ case 'single':
202
+ /* falls through */
203
+ default:
204
+ self.addSelected(clickedDate);
205
+ break;
206
+ }
207
+
208
+ }
209
+ return false;
210
+
211
+ }
212
+ return false;
213
+ });
214
+
215
+
216
+ if (!!(opts.attachTo = util.$(opts.attachTo))) {
217
+ opts.attachTo.appendChild($container);
218
+ }
219
+
220
+ };
221
+
222
+ Kalendae.prototype = {
223
+ defaults : {
224
+ attachTo: null, /* the element to attach the root container to. can be string or DOMElement */
225
+ months: 1, /* total number of months to display side by side */
226
+ weekStart: 0, /* day to use for the start of the week. 0 is Sunday */
227
+ direction: 'any', /* past, today-past, any, today-future, future */
228
+ directionScrolling: true, /* if a direction other than any is defined, prevent scrolling out of range */
229
+ viewStartDate: null, /* date in the month to display. When multiple months, this is the left most */
230
+ blackout: null, /* array of dates, or function to be passed a date */
231
+ selected: null, /* dates already selected. can be string, date, or array of strings or dates. */
232
+ mode: 'single', /* single, multiple, range */
233
+ format: null, /* string used for parsing dates. */
234
+ subscribe: null, /* object containing events to subscribe to */
235
+
236
+ columnHeaderLength: 2, /* number of characters to show in the column headers */
237
+ titleFormat: 'MMMM, YYYY', /* format mask for month titles. See momentjs.com for rules */
238
+ dayNumberFormat: 'D', /* format mask for individual days */
239
+ dayAttributeFormat: 'YYYY-MM-DD', /* format mask for the data-date attribute set on every span */
240
+ parseSplitDelimiter: /,\s*|\s+-\s+/, /* regex to use for splitting multiple dates from a passed string */
241
+ rangeDelimiter: ' - ', /* string to use between dates when outputting in range mode */
242
+ multipleDelimiter: ', ', /* string to use between dates when outputting in multiple mode */
243
+
244
+ dateClassMap: {}
245
+ },
246
+ classes : {
247
+ container :'kalendae',
248
+ calendar :'k-calendar',
249
+ monthFirst :'k-first-month',
250
+ monthMiddle :'k-middle-month',
251
+ monthLast :'k-last-month',
252
+ title :'k-title',
253
+ previousMonth :'k-btn-previous-month',
254
+ nextMonth :'k-btn-next-month',
255
+ previousYear :'k-btn-previous-year',
256
+ nextYear :'k-btn-next-year',
257
+ caption :'k-caption',
258
+ header :'k-header',
259
+ days :'k-days',
260
+ dayOutOfMonth :'k-out-of-month',
261
+ dayActive :'k-active',
262
+ daySelected :'k-selected',
263
+ dayInRange :'k-range',
264
+ dayToday :'k-today',
265
+ monthSeparator :'k-separator',
266
+ disablePreviousMonth :'k-disable-previous-month-btn',
267
+ disableNextMonth :'k-disable-next-month-btn',
268
+ disablePreviousYear :'k-disable-previous-year-btn',
269
+ disableNextYear :'k-disable-next-year-btn'
270
+ },
271
+
272
+ disablePreviousMonth: false,
273
+ disableNextMonth: false,
274
+ disablePreviousYear: false,
275
+ disableNextYear: false,
276
+
277
+ directions: {
278
+ 'past' :function (date) {return moment(date).yearDay() >= today.yearDay();},
279
+ 'today-past' :function (date) {return moment(date).yearDay() > today.yearDay();},
280
+ 'any' :function (date) {return false;},
281
+ 'today-future' :function (date) {return moment(date).yearDay() < today.yearDay();},
282
+ 'future' :function (date) {return moment(date).yearDay() <= today.yearDay();}
283
+ },
284
+
285
+ getSelectedAsDates : function () {
286
+ var out = [];
287
+ var i=0, c = this._sel.length;
288
+ for (;i<c;i++) {
289
+ out.push(this._sel[i].toDate());
290
+ }
291
+ return out;
292
+ },
293
+
294
+ getSelectedAsText : function (format) {
295
+ var out = [];
296
+ var i=0, c = this._sel.length;
297
+ for (;i<c;i++) {
298
+ out.push(this._sel[i].format(format || this.settings.format || 'YYYY-MM-DD'))
299
+ }
300
+ return out;
301
+ },
302
+
303
+ getSelectedRaw : function () {
304
+ var out = [];
305
+ var i=0, c = this._sel.length;
306
+ for (;i<c;i++) {
307
+ out.push(moment(this._sel[i]))
308
+ }
309
+ return out;
310
+ },
311
+
312
+ getSelected : function (format) {
313
+ var sel = this.getSelectedAsText(format);
314
+ switch (this.settings.mode) {
315
+ case 'range':
316
+ sel.splice(2); //shouldn't be more than two, but lets just make sure.
317
+ return sel.join(this.settings.rangeDelimiter);
318
+
319
+ case 'multiple':
320
+ return sel.join(this.settings.multipleDelimiter);
321
+
322
+ case 'single':
323
+ /* falls through */
324
+ default:
325
+ return sel[0];
326
+ }
327
+ },
328
+
329
+ isSelected : function (input) {
330
+ input = moment(input).yearDay();
331
+ if (input < 1 || !this._sel || this._sel.length < 1) return false;
332
+
333
+ switch (this.settings.mode) {
334
+ case 'range':
335
+ var a = this._sel[0] ? this._sel[0].yearDay() : 0,
336
+ b = this._sel[1] ? this._sel[1].yearDay() : 0;
337
+
338
+ if (a === input || b === input) return 1;
339
+ if (!a || !b) return 0;
340
+
341
+ if ((input > a && input < b) || (a<b && input < a && input > b)) return -1;
342
+ return false;
343
+
344
+ case 'multiple':
345
+ var i = this._sel.length;
346
+ while (i--) {
347
+ if (this._sel[i].yearDay() === input) {
348
+ return true;
349
+ }
350
+ }
351
+ return false;
352
+
353
+
354
+ case 'single':
355
+ /* falls through */
356
+ default:
357
+ return (this._sel[0] && (this._sel[0].yearDay() === input));
358
+ }
359
+
360
+ return false;
361
+ },
362
+
363
+ setSelected : function (input, draw) {
364
+ this._sel = parseDates(input, this.settings.parseSplitDelimiter, this.settings.format);
365
+ this._sel.sort(function (a,b) {return a.yearDay() - b.yearDay();});
366
+
367
+ if (draw !== false) this.draw();
368
+ },
369
+
370
+ addSelected : function (date, draw) {
371
+ date = moment(date).hours(12);
372
+ switch (this.settings.mode) {
373
+ case 'multiple':
374
+ if (!this.isSelected(date)) this._sel.push(date);
375
+ else return false;
376
+ break;
377
+ case 'range':
378
+
379
+ if (this._sel.length !== 1) this._sel = [date];
380
+ else {
381
+ if (date.yearDay() > this._sel[0].yearDay()) this._sel[1] = date;
382
+ else this._sel = [date, this._sel[0]];
383
+ }
384
+ break;
385
+ case 'single':
386
+ /* falls through */
387
+ default:
388
+ this._sel = [date];
389
+ break;
390
+ }
391
+ this._sel.sort(function (a,b) {return a.yearDay() - b.yearDay();});
392
+ this.publish('change', this);
393
+ if (draw !== false) this.draw();
394
+ return true;
395
+ },
396
+
397
+ removeSelected : function (date, draw) {
398
+ date = moment(date).yearDay();
399
+ var i = this._sel.length;
400
+ while (i--) {
401
+ if (this._sel[i].yearDay() === date) {
402
+ this._sel.splice(i,1);
403
+ this.publish('change', this);
404
+ if (draw !== false) this.draw();
405
+ return true;
406
+ }
407
+ }
408
+ return false;
409
+ },
410
+
411
+ draw : function draw() {
412
+ // return;
413
+ var month = moment(this.viewStartDate).hours(12), //force middle of the day to avoid any weird date shifts
414
+ day,
415
+ classes = this.classes,
416
+ cal,
417
+ $span,
418
+ klass,
419
+ i=0, c,
420
+ j=0, k,
421
+ s,
422
+ dateString,
423
+ opts = this.settings;
424
+
425
+ c = this.calendars.length;
426
+
427
+ do {
428
+ day = moment(month).date(1);
429
+ day.day( day.day() < this.settings.weekStart ? this.settings.weekStart-7 : this.settings.weekStart);
430
+ //if the first day of the month is less than our week start, back up a week
431
+
432
+ cal = this.calendars[i];
433
+ cal.caption.innerHTML = month.format(this.settings.titleFormat);
434
+ j = 0;
435
+ do {
436
+ $span = cal.days[j];
437
+
438
+ klass = [];
439
+
440
+ s = this.isSelected(day);
441
+
442
+ if (s) klass.push(({'-1':classes.dayInRange,'1':classes.daySelected, 'true':classes.daySelected})[s]);
443
+
444
+ if (day.month() != month.month()) klass.push(classes.dayOutOfMonth);
445
+ else if (!(this.blackout(day) || this.direction(day)) || s>0) klass.push(classes.dayActive);
446
+
447
+ if (day.yearDay() === today.yearDay()) klass.push(classes.dayToday);
448
+
449
+ dateString = day.format(this.settings.dayAttributeFormat);
450
+ if (opts.dateClassMap[dateString]) klass.push(opts.dateClassMap[dateString]);
451
+
452
+ $span.innerHTML = day.format(opts.dayNumberFormat);
453
+ $span.className = klass.join(' ');
454
+ $span.setAttribute('data-date', dateString);
455
+
456
+
457
+ day.add('days',1);
458
+ } while (++j < 42);
459
+ month.add('months',1);
460
+ } while (++i < c);
461
+
462
+ if (opts.directionScrolling) {
463
+ var diff = -(moment().diff(month, 'months'));
464
+ if (opts.direction==='today-past' || opts.direction==='past') {
465
+
466
+ if (diff < 0) {
467
+ this.disableNextMonth = false;
468
+ util.removeClassName(this.container, classes.disableNextMonth);
469
+ } else {
470
+ this.disableNextMonth = true;
471
+ util.addClassName(this.container, classes.disableNextMonth);
472
+ }
473
+
474
+ } else if (opts.direction==='today-future' || opts.direction==='future') {
475
+
476
+ if (diff >= opts.months) {
477
+ this.disablePreviousMonth = false;
478
+ util.removeClassName(this.container, classes.disablePreviousMonth);
479
+ } else {
480
+ this.disablePreviousMonth = true;
481
+ util.addClassName(this.container, classes.disablePreviousMonth);
482
+ }
483
+
484
+ }
485
+
486
+
487
+ if (opts.direction==='today-past' || opts.direction==='past') {
488
+ if (month.add({y:1}).diff(moment(), 'months') <= 0) {
489
+ this.disableNextYear = false;
490
+ util.removeClassName(this.container, classes.disableNextYear);
491
+ } else {
492
+ this.disableNextYear = true;
493
+ util.addClassName(this.container, classes.disableNextYear);
494
+ }
495
+
496
+ } else if (opts.direction==='today-future' || opts.direction==='future') {
497
+ if ((month.subtract({y:1}).diff(moment(), 'months') - (opts.months-1)) >= 0) {
498
+ this.disablePreviousYear = false;
499
+ util.removeClassName(this.container, classes.disablePreviousYear);
500
+ } else {
501
+ this.disablePreviousYear = true;
502
+ util.addClassName(this.container, classes.disablePreviousYear);
503
+ }
504
+
505
+ }
506
+
507
+ }
508
+ }
509
+ }
510
+
511
+ var parseDates = function (input, delimiter, format) {
512
+ var output = [];
513
+
514
+ if (typeof input === 'string') {
515
+ input = input.split(delimiter);
516
+ } else if (!util.isArray(input)) {
517
+ input = [input];
518
+ }
519
+
520
+ var c = input.length;
521
+ i = 0;
522
+ do {
523
+ if (input[i]) output.push( moment(input[i], format).hours(12) );
524
+ } while (++i < c);
525
+
526
+ return output;
527
+ }
528
+
529
+
530
+
531
+ window.Kalendae = Kalendae;
532
+
533
+ var util = Kalendae.util = {
534
+
535
+ isIE8: function() {
536
+ return !!( (/msie 8./i).test(navigator.appVersion) && !(/opera/i).test(navigator.userAgent) && window.ActiveXObject && XDomainRequest && !window.msPerformance );
537
+ },
538
+
539
+ // ELEMENT FUNCTIONS
540
+
541
+ $: function (elem) {
542
+ return (typeof elem == 'string') ? document.getElementById(elem) : elem;
543
+ },
544
+
545
+ $$: function (selector) {
546
+ return document.querySelectorAll(selector);
547
+ },
548
+
549
+ make: function (tagName, attributes, attach) {
550
+ var k, e = document.createElement(tagName);
551
+ if (!!attributes) for (k in attributes) if (attributes.hasOwnProperty(k)) e.setAttribute(k, attributes[k]);
552
+ if (!!attach) attach.appendChild(e);
553
+ return e;
554
+ },
555
+
556
+ // Returns true if the DOM element is visible, false if it's hidden.
557
+ // Checks if display is anything other than none.
558
+ isVisible: function (elem) {
559
+ // shamelessly copied from jQuery
560
+ return elem.offsetWidth > 0 || elem.offsetHeight > 0;
561
+ },
562
+
563
+ getStyle: function (elem, styleProp) {
564
+ var y;
565
+ if (elem.currentStyle) {
566
+ y = elem.currentStyle[styleProp];
567
+ } else if (window.getComputedStyle) {
568
+ y = window.getComputedStyle(elem, null)[styleProp];
569
+ }
570
+ return y;
571
+ },
572
+
573
+ domReady:function (f){/in/.test(document.readyState) ? setTimeout(function() {util.domReady(f);},9) : f()},
574
+
575
+ // Adds a listener callback to a DOM element which is fired on a specified
576
+ // event. Callback is sent the event object and the element that triggered the event
577
+ addEvent: function (elem, eventName, callback) {
578
+ var listener = function (event) {
579
+ event = event || window.event;
580
+ var target = event.target || event.srcElement;
581
+ var block = callback.apply(elem, [event, target]);
582
+ if (block === false) {
583
+ if (!!event.preventDefault) event.preventDefault();
584
+ else {
585
+ event.returnValue = false;
586
+ event.cancelBubble = true;
587
+ }
588
+ }
589
+ return block;
590
+ };
591
+ if (elem.attachEvent) { // IE only. The "on" is mandatory.
592
+ elem.attachEvent("on" + eventName, listener);
593
+ } else { // Other browsers.
594
+ elem.addEventListener(eventName, listener, false);
595
+ }
596
+ return listener;
597
+ },
598
+
599
+ // Removes a listener callback from a DOM element which is fired on a specified
600
+ // event.
601
+ removeEvent: function (elem, event, listener) {
602
+ if (elem.detachEvent) { // IE only. The "on" is mandatory.
603
+ elem.detachEvent("on" + event, listener);
604
+ } else { // Other browsers.
605
+ elem.removeEventListener(event, listener, false);
606
+ }
607
+ },
608
+
609
+ hasClassName: function(elem, className) { //copied and modified from Prototype.js
610
+ if (!(elem = util.$(elem))) return false;
611
+ var eClassName = elem.className;
612
+ return (eClassName.length > 0 && (eClassName == className || new RegExp("(^|\\s)" + className + "(\\s|$)").test(eClassName)));
613
+ },
614
+
615
+ addClassName: function(elem, className) { //copied and modified from Prototype.js
616
+ if (!(elem = util.$(elem))) return;
617
+ if (!util.hasClassName(elem, className)) elem.className += (elem.className ? ' ' : '') + className;
618
+ },
619
+
620
+ removeClassName: function(elem, className) { //copied and modified from Prototype.js
621
+ if (!(elem = util.$(elem))) return;
622
+ elem.className = util.trimString(elem.className.replace(new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' '));
623
+ },
624
+
625
+ isFixed: function (elem) {
626
+ do {
627
+ if (util.getStyle(elem, 'position') === 'fixed') return true;
628
+ } while ((elem = elem.offsetParent));
629
+ return false;
630
+ },
631
+
632
+ getPosition: function (elem, isInner) {
633
+ var x = elem.offsetLeft,
634
+ y = elem.offsetTop,
635
+ r = {};
636
+
637
+ if (!isInner) {
638
+ while ((elem = elem.offsetParent)) {
639
+ x += elem.offsetLeft;
640
+ y += elem.offsetTop;
641
+ }
642
+ }
643
+
644
+ r[0] = r.left = x;
645
+ r[1] = r.top = y;
646
+ return r;
647
+ },
648
+
649
+ getHeight: function (elem) {
650
+ return elem.offsetHeight || elem.scrollHeight;
651
+ },
652
+
653
+ getWidth: function (elem) {
654
+ return elem.offsetWidth || elem.scrollWidth;
655
+ },
656
+
657
+
658
+ // TEXT FUNCTIONS
659
+
660
+ trimString: function (input) {
661
+ return input.replace(/^\s+/, '').replace(/\s+$/, '');
662
+ },
663
+
664
+
665
+ // OBJECT FUNCTIONS
666
+
667
+ merge: function () {
668
+ /* Combines multiple objects into one.
669
+ * Syntax: util.extend([true], object1, object2, ... objectN)
670
+ * If first argument is true, function will merge recursively.
671
+ */
672
+
673
+ var deep = (arguments[0]===true),
674
+ d = {},
675
+ i = deep?1:0;
676
+
677
+ var _c = function (a, b) {
678
+ if (typeof b !== 'object') return;
679
+ for (var k in b) if (b.hasOwnProperty(k)) {
680
+ //if property is an object or array, merge the contents instead of overwriting, if extend() was called as such
681
+ if (deep && typeof a[k] === 'object' && typeof b[k] === 'object') _update(a[k], b[k]);
682
+ else a[k] = b[k];
683
+ }
684
+ return a;
685
+ }
686
+
687
+ for (; i < arguments.length; i++) {
688
+ _c(d, arguments[i]);
689
+ }
690
+ return d;
691
+ },
692
+
693
+ isArray: function (array) {
694
+ return !(
695
+ !array ||
696
+ (!array.length || array.length === 0) ||
697
+ typeof array !== 'object' ||
698
+ !array.constructor ||
699
+ array.nodeType ||
700
+ array.item
701
+ );
702
+ }
703
+ };
704
+
705
+
706
+ //auto-initializaiton code
707
+ if (typeof document.addEventListener === 'function') Kalendae.util.domReady(function () {
708
+ var els = util.$$('.auto-kal'),
709
+ i = els.length,
710
+ e,
711
+ options,
712
+ optionsRaw;
713
+
714
+ while (i--) {
715
+ e = els[i];
716
+ optionsRaw = e.getAttribute('data-kal');
717
+ options = (optionsRaw == null || optionsRaw == "") ? {} : (new Function('return {' + optionsRaw + '};'))();
718
+
719
+ if (e.tagName === 'INPUT') {
720
+ //if element is an input, bind a popup calendar to the input.
721
+ new Kalendae.Input(e, options);
722
+ } else {
723
+ //otherwise, insert a flat calendar into the element.
724
+ new Kalendae(util.merge(options, {attachTo:e}));
725
+ }
726
+
727
+ }
728
+ });
729
+
730
+ Kalendae.Input = function (targetElement, options) {
731
+ if (typeof document.addEventListener !== 'function') return;
732
+
733
+ var $input = this.input = util.$(targetElement),
734
+ overwriteInput;
735
+
736
+ if (!$input || $input.tagName !== 'INPUT') throw "First argument for Kalendae.Input must be an <input> element or a valid element id.";
737
+
738
+ var self = this,
739
+ classes = self.classes
740
+ opts = self.settings = util.merge(self.defaults, options);
741
+
742
+ //force attachment to the body
743
+ opts.attachTo = window.document.body;
744
+
745
+ //if no override provided, use the input's contents
746
+ if (!opts.selected) opts.selected = $input.value;
747
+ else overwriteInput = true;
748
+
749
+ //call our parent constructor
750
+ Kalendae.call(self, opts);
751
+
752
+ //create the close button
753
+ if (opts.closeButton) {
754
+ var $closeButton = util.make('a', {'class':classes.closeButton}, self.container)
755
+ util.addEvent($closeButton, 'click', function () {
756
+ $input.blur();
757
+ });
758
+ }
759
+
760
+ if (overwriteInput) $input.value = self.getSelected();
761
+
762
+ var $container = self.container,
763
+ noclose = false;
764
+
765
+ $container.style.display = 'none';
766
+ util.addClassName($container, classes.positioned);
767
+
768
+ util.addEvent($container, 'mousedown', function (event, target) {
769
+ noclose = true; //IE8 doesn't obey event blocking when it comes to focusing, so we have to do this shit.
770
+ });
771
+ util.addEvent(window.document, 'mousedown', function (event, target) {
772
+ noclose = false;
773
+ });
774
+
775
+ util.addEvent($input, 'focus', function () {
776
+ self.setSelected(this.value);
777
+ self.show();
778
+ });
779
+
780
+ util.addEvent($input, 'blur', function () {
781
+ if (noclose) {
782
+ noclose = false;
783
+ $input.focus();
784
+ }
785
+ else self.hide();
786
+ });
787
+ util.addEvent($input, 'keyup', function (event) {
788
+ self.setSelected(this.value);
789
+ });
790
+
791
+ self.subscribe('change', function () {
792
+ $input.value = self.getSelected();
793
+ });
794
+
795
+ };
796
+
797
+ Kalendae.Input.prototype = util.merge(Kalendae.prototype, {
798
+ defaults : util.merge(Kalendae.prototype.defaults, {
799
+ format: 'MM/DD/YYYY',
800
+ side: 'bottom',
801
+ closeButton: true,
802
+ offsetLeft: 0,
803
+ offsetTop: 0
804
+ }),
805
+ classes : util.merge(Kalendae.prototype.classes, {
806
+ positioned : 'k-floating',
807
+ closeButton: 'k-btn-close'
808
+ }),
809
+
810
+ show : function () {
811
+ var $container = this.container,
812
+ style = $container.style,
813
+ $input = this.input,
814
+ pos = util.getPosition($input);
815
+
816
+ style.display = '';
817
+ switch (opts.side) {
818
+ case 'left':
819
+ style.left = (pos.left - util.getWidth($container) + this.settings.offsetLeft) + 'px';
820
+ style.top = (pos.top + this.settings.offsetTop) + 'px';
821
+ break;
822
+ case 'right':
823
+ style.left = (pos.left + util.getWidth($input)) + 'px';
824
+ style.top = (pos.top + this.settings.offsetTop) + 'px';
825
+ break;
826
+ case 'top':
827
+ style.left = (pos.left + this.settings.offsetLeft) + 'px';
828
+ style.top = (pos.top - util.getHeight($container) + this.settings.offsetTop) + 'px';
829
+ break;
830
+ case 'bottom':
831
+ /* falls through */
832
+ default:
833
+ style.left = (pos.left + this.settings.offsetLeft) + 'px';
834
+ style.top = (pos.top + util.getHeight($input) + this.settings.offsetTop) + 'px';
835
+ break;
836
+ }
837
+
838
+ style.position = util.isFixed($input) ? 'fixed' : 'absolute';
839
+
840
+ },
841
+
842
+ hide : function () {
843
+ this.container.style.display = 'none';
844
+ }
845
+
846
+ });
847
+
848
+
849
+ /*!
850
+ * MinPubSub, modified for use on Kalendae
851
+ * Copyright(c) 2011 Daniel Lamb <daniellmb.com>
852
+ * https://github.com/daniellmb/MinPubSub
853
+ * MIT Licensed
854
+ */
855
+
856
+ var MinPubSub = function(d){
857
+
858
+ if (!d) d = this;
859
+
860
+ // the topic/subscription hash
861
+ var cache = d.c_ || {}; //check for "c_" cache for unit testing
862
+
863
+ d.publish = function(/* String */ topic, /* Object */ target, /* Array? */ args){
864
+ // summary:
865
+ // Publish some data on a named topic.
866
+ // topic: String
867
+ // The channel to publish on
868
+ // args: Array?
869
+ // The data to publish. Each array item is converted into an ordered
870
+ // arguments on the subscribed functions.
871
+ //
872
+ // example:
873
+ // Publish stuff on '/some/topic'. Anything subscribed will be called
874
+ // with a function signature like: function(a,b,c){ ... }
875
+ //
876
+ // publish("/some/topic", ["a","b","c"]);
877
+
878
+ var subs = cache[topic],
879
+ len = subs ? subs.length : 0,
880
+ r;
881
+
882
+ //can change loop or reverse array if the order matters
883
+ while(len--){
884
+ r = subs[len].apply(target, args || []);
885
+ if (typeof r === 'boolean') return r;
886
+ }
887
+ };
888
+
889
+ d.subscribe = function(/* String */ topic, /* Function */ callback, /* Boolean */ topPriority){
890
+ // summary:
891
+ // Register a callback on a named topic.
892
+ // topic: String
893
+ // The channel to subscribe to
894
+ // callback: Function
895
+ // The handler event. Anytime something is publish'ed on a
896
+ // subscribed channel, the callback will be called with the
897
+ // published array as ordered arguments.
898
+ //
899
+ // returns: Array
900
+ // A handle which can be used to unsubscribe this particular subscription.
901
+ //
902
+ // example:
903
+ // subscribe("/some/topic", function(a, b, c){ /* handle data */ });
904
+
905
+ if(!cache[topic]){
906
+ cache[topic] = [];
907
+ }
908
+ if (topPriority)
909
+ cache[topic].push(callback);
910
+ else
911
+ cache[topic].unshift(callback);
912
+ return [topic, callback]; // Array
913
+ };
914
+
915
+ d.unsubscribe = function(/* Array */ handle){
916
+ // summary:
917
+ // Disconnect a subscribed function for a topic.
918
+ // handle: Array
919
+ // The return value from a subscribe call.
920
+ // example:
921
+ // var handle = subscribe("/some/topic", function(){});
922
+ // unsubscribe(handle);
923
+
924
+ var subs = cache[handle[0]],
925
+ callback = handle[1],
926
+ len = subs ? subs.length : 0;
927
+
928
+ while(len--){
929
+ if(subs[len] === callback){
930
+ subs.splice(len, 1);
931
+ }
932
+ }
933
+ };
934
+
935
+ };// moment.js
936
+ // Altered slightly for use in Kalendae.js
937
+ // version : 1.5.0
938
+ // author : Tim Wood
939
+ // license : MIT
940
+ // momentjs.com
941
+
942
+ var moment = Kalendae.moment = (function (Date, undefined) {
943
+
944
+ var moment,
945
+ round = Math.round,
946
+ languages = {},
947
+ hasModule = (typeof module !== 'undefined'),
948
+ paramsToParse = 'months|monthsShort|monthsParse|weekdays|weekdaysShort|longDateFormat|calendar|relativeTime|ordinal|meridiem'.split('|'),
949
+ i,
950
+ jsonRegex = /^\/?Date\((\-?\d+)/i,
951
+ charactersToReplace = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|dddd?|do?|w[o|w]?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|zz?|ZZ?|LT|LL?L?L?)/g,
952
+ nonuppercaseLetters = /[^A-Z]/g,
953
+ timezoneRegex = /\([A-Za-z ]+\)|:[0-9]{2} [A-Z]{3} /g,
954
+ tokenCharacters = /(\\)?(MM?M?M?|dd?d?d|DD?D?D?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|ZZ?|T)/g,
955
+ inputCharacters = /(\\)?([0-9]+|([a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+|([\+\-]\d\d:?\d\d))/gi,
956
+ isoRegex = /\d{4}.\d\d.\d\d(T(\d\d(.\d\d(.\d\d)?)?)?([\+\-]\d\d:?\d\d)?)?/,
957
+ isoFormat = 'YYYY-MM-DDTHH:mm:ssZ',
958
+ isoTimes = [
959
+ ['HH:mm:ss', /T\d\d:\d\d:\d\d/],
960
+ ['HH:mm', /T\d\d:\d\d/],
961
+ ['HH', /T\d\d/]
962
+ ],
963
+ timezoneParseRegex = /([\+\-]|\d\d)/gi,
964
+ VERSION = "1.5.0",
965
+ shortcuts = 'Month|Date|Hours|Minutes|Seconds|Milliseconds'.split('|');
966
+
967
+ // Moment prototype object
968
+ function Moment(date, isUTC) {
969
+ this._d = date;
970
+ this._isUTC = !!isUTC;
971
+ }
972
+
973
+ // left zero fill a number
974
+ // see http://jsperf.com/left-zero-filling for performance comparison
975
+ function leftZeroFill(number, targetLength) {
976
+ var output = number + '';
977
+ while (output.length < targetLength) {
978
+ output = '0' + output;
979
+ }
980
+ return output;
981
+ }
982
+
983
+ // helper function for _.addTime and _.subtractTime
984
+ function dateAddRemove(date, _input, adding, val) {
985
+ var isString = (typeof _input === 'string'),
986
+ input = isString ? {} : _input,
987
+ ms, d, M, currentDate;
988
+ if (isString && val) {
989
+ input[_input] = +val;
990
+ }
991
+ ms = (input.ms || input.milliseconds || 0) +
992
+ (input.s || input.seconds || 0) * 1e3 + // 1000
993
+ (input.m || input.minutes || 0) * 6e4 + // 1000 * 60
994
+ (input.h || input.hours || 0) * 36e5; // 1000 * 60 * 60
995
+ d = (input.d || input.days || 0) +
996
+ (input.w || input.weeks || 0) * 7;
997
+ M = (input.M || input.months || 0) +
998
+ (input.y || input.years || 0) * 12;
999
+ if (ms) {
1000
+ date.setTime(+date + ms * adding);
1001
+ }
1002
+ if (d) {
1003
+ date.setDate(date.getDate() + d * adding);
1004
+ }
1005
+ if (M) {
1006
+ currentDate = date.getDate();
1007
+ date.setDate(1);
1008
+ date.setMonth(date.getMonth() + M * adding);
1009
+ date.setDate(Math.min(new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate(), currentDate));
1010
+ }
1011
+ return date;
1012
+ }
1013
+
1014
+ // check if is an array
1015
+ function isArray(input) {
1016
+ return Object.prototype.toString.call(input) === '[object Array]';
1017
+ }
1018
+
1019
+ // convert an array to a date.
1020
+ // the array should mirror the parameters below
1021
+ // note: all values past the year are optional and will default to the lowest possible value.
1022
+ // [year, month, day , hour, minute, second, millisecond]
1023
+ function dateFromArray(input) {
1024
+ return new Date(input[0], input[1] || 0, input[2] || 1, input[3] || 0, input[4] || 0, input[5] || 0, input[6] || 0);
1025
+ }
1026
+
1027
+ // format date using native date object
1028
+ function formatMoment(m, inputString) {
1029
+ var currentMonth = m.month(),
1030
+ currentDate = m.date(),
1031
+ currentYear = m.year(),
1032
+ currentDay = m.day(),
1033
+ currentHours = m.hours(),
1034
+ currentMinutes = m.minutes(),
1035
+ currentSeconds = m.seconds(),
1036
+ currentZone = -m.zone(),
1037
+ ordinal = moment.ordinal,
1038
+ meridiem = moment.meridiem;
1039
+ // check if the character is a format
1040
+ // return formatted string or non string.
1041
+ //
1042
+ // uses switch/case instead of an object of named functions (like http://phpjs.org/functions/date:380)
1043
+ // for minification and performance
1044
+ // see http://jsperf.com/object-of-functions-vs-switch for performance comparison
1045
+ function replaceFunction(input) {
1046
+ // create a couple variables to be used later inside one of the cases.
1047
+ var a, b;
1048
+ switch (input) {
1049
+ // MONTH
1050
+ case 'M' :
1051
+ return currentMonth + 1;
1052
+ case 'Mo' :
1053
+ return (currentMonth + 1) + ordinal(currentMonth + 1);
1054
+ case 'MM' :
1055
+ return leftZeroFill(currentMonth + 1, 2);
1056
+ case 'MMM' :
1057
+ return moment.monthsShort[currentMonth];
1058
+ case 'MMMM' :
1059
+ return moment.months[currentMonth];
1060
+ // DAY OF MONTH
1061
+ case 'D' :
1062
+ return currentDate;
1063
+ case 'Do' :
1064
+ return currentDate + ordinal(currentDate);
1065
+ case 'DD' :
1066
+ return leftZeroFill(currentDate, 2);
1067
+ // DAY OF YEAR
1068
+ case 'DDD' :
1069
+ a = new Date(currentYear, currentMonth, currentDate);
1070
+ b = new Date(currentYear, 0, 1);
1071
+ return ~~ (((a - b) / 864e5) + 1.5);
1072
+ case 'DDDo' :
1073
+ a = replaceFunction('DDD');
1074
+ return a + ordinal(a);
1075
+ case 'DDDD' :
1076
+ return leftZeroFill(replaceFunction('DDD'), 3);
1077
+ // WEEKDAY
1078
+ case 'd' :
1079
+ return currentDay;
1080
+ case 'do' :
1081
+ return currentDay + ordinal(currentDay);
1082
+ case 'ddd' :
1083
+ return moment.weekdaysShort[currentDay];
1084
+ case 'dddd' :
1085
+ return moment.weekdays[currentDay];
1086
+ // WEEK OF YEAR
1087
+ case 'w' :
1088
+ a = new Date(currentYear, currentMonth, currentDate - currentDay + 5);
1089
+ b = new Date(a.getFullYear(), 0, 4);
1090
+ return ~~ ((a - b) / 864e5 / 7 + 1.5);
1091
+ case 'wo' :
1092
+ a = replaceFunction('w');
1093
+ return a + ordinal(a);
1094
+ case 'ww' :
1095
+ return leftZeroFill(replaceFunction('w'), 2);
1096
+ // YEAR
1097
+ case 'YY' :
1098
+ return leftZeroFill(currentYear % 100, 2);
1099
+ case 'YYYY' :
1100
+ return currentYear;
1101
+ // AM / PM
1102
+ case 'a' :
1103
+ return currentHours > 11 ? meridiem.pm : meridiem.am;
1104
+ case 'A' :
1105
+ return currentHours > 11 ? meridiem.PM : meridiem.AM;
1106
+ // 24 HOUR
1107
+ case 'H' :
1108
+ return currentHours;
1109
+ case 'HH' :
1110
+ return leftZeroFill(currentHours, 2);
1111
+ // 12 HOUR
1112
+ case 'h' :
1113
+ return currentHours % 12 || 12;
1114
+ case 'hh' :
1115
+ return leftZeroFill(currentHours % 12 || 12, 2);
1116
+ // MINUTE
1117
+ case 'm' :
1118
+ return currentMinutes;
1119
+ case 'mm' :
1120
+ return leftZeroFill(currentMinutes, 2);
1121
+ // SECOND
1122
+ case 's' :
1123
+ return currentSeconds;
1124
+ case 'ss' :
1125
+ return leftZeroFill(currentSeconds, 2);
1126
+ // TIMEZONE
1127
+ case 'zz' :
1128
+ // depreciating 'zz' fall through to 'z'
1129
+ case 'z' :
1130
+ return (m._d.toString().match(timezoneRegex) || [''])[0].replace(nonuppercaseLetters, '');
1131
+ case 'Z' :
1132
+ return (currentZone < 0 ? '-' : '+') + leftZeroFill(~~(Math.abs(currentZone) / 60), 2) + ':' + leftZeroFill(~~(Math.abs(currentZone) % 60), 2);
1133
+ case 'ZZ' :
1134
+ return (currentZone < 0 ? '-' : '+') + leftZeroFill(~~(10 * Math.abs(currentZone) / 6), 4);
1135
+ // LONG DATES
1136
+ case 'L' :
1137
+ case 'LL' :
1138
+ case 'LLL' :
1139
+ case 'LLLL' :
1140
+ case 'LT' :
1141
+ return formatMoment(m, moment.longDateFormat[input]);
1142
+ // DEFAULT
1143
+ default :
1144
+ return input.replace(/(^\[)|(\\)|\]$/g, "");
1145
+ }
1146
+ }
1147
+ return inputString.replace(charactersToReplace, replaceFunction);
1148
+ }
1149
+
1150
+ // date from string and format string
1151
+ function makeDateFromStringAndFormat(string, format) {
1152
+ var inArray = [0, 0, 1, 0, 0, 0, 0],
1153
+ timezoneHours = 0,
1154
+ timezoneMinutes = 0,
1155
+ isUsingUTC = false,
1156
+ inputParts = string.match(inputCharacters),
1157
+ formatParts = format.match(tokenCharacters),
1158
+ len = Math.min(inputParts.length, formatParts.length),
1159
+ i,
1160
+ isPm;
1161
+
1162
+ // function to convert string input to date
1163
+ function addTime(format, input) {
1164
+ var a;
1165
+ switch (format) {
1166
+ // MONTH
1167
+ case 'M' :
1168
+ // fall through to MM
1169
+ case 'MM' :
1170
+ inArray[1] = ~~input - 1;
1171
+ break;
1172
+ case 'MMM' :
1173
+ // fall through to MMMM
1174
+ case 'MMMM' :
1175
+ for (a = 0; a < 12; a++) {
1176
+ if (moment.monthsParse[a].test(input)) {
1177
+ inArray[1] = a;
1178
+ break;
1179
+ }
1180
+ }
1181
+ break;
1182
+ // DAY OF MONTH
1183
+ case 'D' :
1184
+ // fall through to DDDD
1185
+ case 'DD' :
1186
+ // fall through to DDDD
1187
+ case 'DDD' :
1188
+ // fall through to DDDD
1189
+ case 'DDDD' :
1190
+ inArray[2] = ~~input;
1191
+ break;
1192
+ // YEAR
1193
+ case 'YY' :
1194
+ input = ~~input;
1195
+ inArray[0] = input + (input > 70 ? 1900 : 2000);
1196
+ break;
1197
+ case 'YYYY' :
1198
+ inArray[0] = ~~Math.abs(input);
1199
+ break;
1200
+ // AM / PM
1201
+ case 'a' :
1202
+ // fall through to A
1203
+ case 'A' :
1204
+ isPm = (input.toLowerCase() === 'pm');
1205
+ break;
1206
+ // 24 HOUR
1207
+ case 'H' :
1208
+ // fall through to hh
1209
+ case 'HH' :
1210
+ // fall through to hh
1211
+ case 'h' :
1212
+ // fall through to hh
1213
+ case 'hh' :
1214
+ inArray[3] = ~~input;
1215
+ break;
1216
+ // MINUTE
1217
+ case 'm' :
1218
+ // fall through to mm
1219
+ case 'mm' :
1220
+ inArray[4] = ~~input;
1221
+ break;
1222
+ // SECOND
1223
+ case 's' :
1224
+ // fall through to ss
1225
+ case 'ss' :
1226
+ inArray[5] = ~~input;
1227
+ break;
1228
+ // TIMEZONE
1229
+ case 'Z' :
1230
+ // fall through to ZZ
1231
+ case 'ZZ' :
1232
+ isUsingUTC = true;
1233
+ a = (input || '').match(timezoneParseRegex);
1234
+ if (a && a[1]) {
1235
+ timezoneHours = ~~a[1];
1236
+ }
1237
+ if (a && a[2]) {
1238
+ timezoneMinutes = ~~a[2];
1239
+ }
1240
+ // reverse offsets
1241
+ if (a && a[0] === '+') {
1242
+ timezoneHours = -timezoneHours;
1243
+ timezoneMinutes = -timezoneMinutes;
1244
+ }
1245
+ break;
1246
+ }
1247
+ }
1248
+ for (i = 0; i < len; i++) {
1249
+ addTime(formatParts[i], inputParts[i]);
1250
+ }
1251
+ // handle am pm
1252
+ if (isPm && inArray[3] < 12) {
1253
+ inArray[3] += 12;
1254
+ }
1255
+ // if is 12 am, change hours to 0
1256
+ if (isPm === false && inArray[3] === 12) {
1257
+ inArray[3] = 0;
1258
+ }
1259
+ // handle timezone
1260
+ inArray[3] += timezoneHours;
1261
+ inArray[4] += timezoneMinutes;
1262
+ // return
1263
+ return isUsingUTC ? new Date(Date.UTC.apply({}, inArray)) : dateFromArray(inArray);
1264
+ }
1265
+
1266
+ // compare two arrays, return the number of differences
1267
+ function compareArrays(array1, array2) {
1268
+ var len = Math.min(array1.length, array2.length),
1269
+ lengthDiff = Math.abs(array1.length - array2.length),
1270
+ diffs = 0,
1271
+ i;
1272
+ for (i = 0; i < len; i++) {
1273
+ if (~~array1[i] !== ~~array2[i]) {
1274
+ diffs++;
1275
+ }
1276
+ }
1277
+ return diffs + lengthDiff;
1278
+ }
1279
+
1280
+ // date from string and array of format strings
1281
+ function makeDateFromStringAndArray(string, formats) {
1282
+ var output,
1283
+ inputParts = string.match(inputCharacters),
1284
+ scores = [],
1285
+ scoreToBeat = 99,
1286
+ i,
1287
+ curDate,
1288
+ curScore;
1289
+ for (i = 0; i < formats.length; i++) {
1290
+ curDate = makeDateFromStringAndFormat(string, formats[i]);
1291
+ curScore = compareArrays(inputParts, formatMoment(new Moment(curDate), formats[i]).match(inputCharacters));
1292
+ if (curScore < scoreToBeat) {
1293
+ scoreToBeat = curScore;
1294
+ output = curDate;
1295
+ }
1296
+ }
1297
+ return output;
1298
+ }
1299
+
1300
+ // date from iso format
1301
+ function makeDateFromString(string) {
1302
+ var format = 'YYYY-MM-DDT',
1303
+ i;
1304
+ if (isoRegex.exec(string)) {
1305
+ for (i = 0; i < 3; i++) {
1306
+ if (isoTimes[i][1].exec(string)) {
1307
+ format += isoTimes[i][0];
1308
+ break;
1309
+ }
1310
+ }
1311
+ return makeDateFromStringAndFormat(string, format + 'Z');
1312
+ }
1313
+ return new Date(string);
1314
+ }
1315
+
1316
+ // helper function for _date.from() and _date.fromNow()
1317
+ function substituteTimeAgo(string, number, withoutSuffix) {
1318
+ var rt = moment.relativeTime[string];
1319
+ return (typeof rt === 'function') ?
1320
+ rt(number || 1, !!withoutSuffix, string) :
1321
+ rt.replace(/%d/i, number || 1);
1322
+ }
1323
+
1324
+ function relativeTime(milliseconds, withoutSuffix) {
1325
+ var seconds = round(Math.abs(milliseconds) / 1000),
1326
+ minutes = round(seconds / 60),
1327
+ hours = round(minutes / 60),
1328
+ days = round(hours / 24),
1329
+ years = round(days / 365),
1330
+ args = seconds < 45 && ['s', seconds] ||
1331
+ minutes === 1 && ['m'] ||
1332
+ minutes < 45 && ['mm', minutes] ||
1333
+ hours === 1 && ['h'] ||
1334
+ hours < 22 && ['hh', hours] ||
1335
+ days === 1 && ['d'] ||
1336
+ days <= 25 && ['dd', days] ||
1337
+ days <= 45 && ['M'] ||
1338
+ days < 345 && ['MM', round(days / 30)] ||
1339
+ years === 1 && ['y'] || ['yy', years];
1340
+ args[2] = withoutSuffix;
1341
+ return substituteTimeAgo.apply({}, args);
1342
+ }
1343
+
1344
+ moment = function (input, format) {
1345
+ if (input === null || input === '') {
1346
+ return null;
1347
+ }
1348
+ var date,
1349
+ matched;
1350
+ // parse Moment object
1351
+ if (input && input._d instanceof Date) {
1352
+ date = new Date(+input._d);
1353
+ // parse string and format
1354
+ } else if (format) {
1355
+ if (isArray(format)) {
1356
+ date = makeDateFromStringAndArray(input, format);
1357
+ } else {
1358
+ date = makeDateFromStringAndFormat(input, format);
1359
+ }
1360
+ // evaluate it as a JSON-encoded date
1361
+ } else {
1362
+ matched = jsonRegex.exec(input);
1363
+ date = input === undefined ? new Date() :
1364
+ matched ? new Date(+matched[1]) :
1365
+ input instanceof Date ? input :
1366
+ isArray(input) ? dateFromArray(input) :
1367
+ typeof input === 'string' ? makeDateFromString(input) :
1368
+ new Date(input);
1369
+ }
1370
+ return new Moment(date);
1371
+ };
1372
+
1373
+ // creating with utc
1374
+ moment.utc = function (input, format) {
1375
+ if (isArray(input)) {
1376
+ return new Moment(new Date(Date.UTC.apply({}, input)), true);
1377
+ }
1378
+ return (format && input) ? moment(input + ' 0', format + ' Z').utc() : moment(input).utc();
1379
+ };
1380
+
1381
+ // humanizeDuration
1382
+ moment.humanizeDuration = function (num, type, withSuffix) {
1383
+ var difference = +num,
1384
+ rel = moment.relativeTime,
1385
+ output;
1386
+ switch (type) {
1387
+ case "seconds" :
1388
+ difference *= 1000; // 1000
1389
+ break;
1390
+ case "minutes" :
1391
+ difference *= 60000; // 60 * 1000
1392
+ break;
1393
+ case "hours" :
1394
+ difference *= 3600000; // 60 * 60 * 1000
1395
+ break;
1396
+ case "days" :
1397
+ difference *= 86400000; // 24 * 60 * 60 * 1000
1398
+ break;
1399
+ case "weeks" :
1400
+ difference *= 604800000; // 7 * 24 * 60 * 60 * 1000
1401
+ break;
1402
+ case "months" :
1403
+ difference *= 2592000000; // 30 * 24 * 60 * 60 * 1000
1404
+ break;
1405
+ case "years" :
1406
+ difference *= 31536000000; // 365 * 24 * 60 * 60 * 1000
1407
+ break;
1408
+ default :
1409
+ withSuffix = !!type;
1410
+ break;
1411
+ }
1412
+ output = relativeTime(difference, !withSuffix);
1413
+ return withSuffix ? (difference <= 0 ? rel.past : rel.future).replace(/%s/i, output) : output;
1414
+ };
1415
+
1416
+ // version number
1417
+ moment.version = VERSION;
1418
+
1419
+ // default format
1420
+ moment.defaultFormat = isoFormat;
1421
+
1422
+ // language switching and caching
1423
+ moment.lang = function (key, values) {
1424
+ var i,
1425
+ param,
1426
+ req,
1427
+ parse = [];
1428
+ if (values) {
1429
+ for (i = 0; i < 12; i++) {
1430
+ parse[i] = new RegExp('^' + values.months[i] + '|^' + values.monthsShort[i].replace('.', ''), 'i');
1431
+ }
1432
+ values.monthsParse = values.monthsParse || parse;
1433
+ languages[key] = values;
1434
+ }
1435
+ if (languages[key]) {
1436
+ for (i = 0; i < paramsToParse.length; i++) {
1437
+ param = paramsToParse[i];
1438
+ moment[param] = languages[key][param] || moment[param];
1439
+ }
1440
+ } else {
1441
+ if (hasModule) {
1442
+ req = require('./lang/' + key);
1443
+ moment.lang(key, req);
1444
+ }
1445
+ }
1446
+ };
1447
+
1448
+ // set default language
1449
+ moment.lang('en', {
1450
+ months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
1451
+ monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),
1452
+ weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
1453
+ weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),
1454
+ longDateFormat : {
1455
+ LT : "h:mm A",
1456
+ L : "MM/DD/YYYY",
1457
+ LL : "MMMM D YYYY",
1458
+ LLL : "MMMM D YYYY LT",
1459
+ LLLL : "dddd, MMMM D YYYY LT"
1460
+ },
1461
+ meridiem : {
1462
+ AM : 'AM',
1463
+ am : 'am',
1464
+ PM : 'PM',
1465
+ pm : 'pm'
1466
+ },
1467
+ calendar : {
1468
+ sameDay : '[Today at] LT',
1469
+ nextDay : '[Tomorrow at] LT',
1470
+ nextWeek : 'dddd [at] LT',
1471
+ lastDay : '[Yesterday at] LT',
1472
+ lastWeek : '[last] dddd [at] LT',
1473
+ sameElse : 'L'
1474
+ },
1475
+ relativeTime : {
1476
+ future : "in %s",
1477
+ past : "%s ago",
1478
+ s : "a few seconds",
1479
+ m : "a minute",
1480
+ mm : "%d minutes",
1481
+ h : "an hour",
1482
+ hh : "%d hours",
1483
+ d : "a day",
1484
+ dd : "%d days",
1485
+ M : "a month",
1486
+ MM : "%d months",
1487
+ y : "a year",
1488
+ yy : "%d years"
1489
+ },
1490
+ ordinal : function (number) {
1491
+ var b = number % 10;
1492
+ return (~~ (number % 100 / 10) === 1) ? 'th' :
1493
+ (b === 1) ? 'st' :
1494
+ (b === 2) ? 'nd' :
1495
+ (b === 3) ? 'rd' : 'th';
1496
+ }
1497
+ });
1498
+
1499
+ // compare moment object
1500
+ moment.isMoment = function (obj) {
1501
+ return obj instanceof Moment;
1502
+ };
1503
+
1504
+ // shortcut for prototype
1505
+ moment.fn = Moment.prototype = {
1506
+
1507
+ clone : function () {
1508
+ return moment(this);
1509
+ },
1510
+
1511
+ valueOf : function () {
1512
+ return +this._d;
1513
+ },
1514
+
1515
+ 'native' : function () {
1516
+ return this._d;
1517
+ },
1518
+
1519
+ toString : function () {
1520
+ return this._d.toString();
1521
+ },
1522
+
1523
+ toDate : function () {
1524
+ return this._d;
1525
+ },
1526
+
1527
+ utc : function () {
1528
+ this._isUTC = true;
1529
+ return this;
1530
+ },
1531
+
1532
+ local : function () {
1533
+ this._isUTC = false;
1534
+ return this;
1535
+ },
1536
+
1537
+ format : function (inputString) {
1538
+ return formatMoment(this, inputString ? inputString : moment.defaultFormat);
1539
+ },
1540
+
1541
+ add : function (input, val) {
1542
+ this._d = dateAddRemove(this._d, input, 1, val);
1543
+ return this;
1544
+ },
1545
+
1546
+ subtract : function (input, val) {
1547
+ this._d = dateAddRemove(this._d, input, -1, val);
1548
+ return this;
1549
+ },
1550
+
1551
+ diff : function (input, val, asFloat) {
1552
+ var inputMoment = moment(input),
1553
+ zoneDiff = (this.zone() - inputMoment.zone()) * 6e4,
1554
+ diff = this._d - inputMoment._d - zoneDiff,
1555
+ year = this.year() - inputMoment.year(),
1556
+ month = this.month() - inputMoment.month(),
1557
+ date = this.date() - inputMoment.date(),
1558
+ output;
1559
+ if (val === 'months') {
1560
+ output = year * 12 + month + date / 30;
1561
+ } else if (val === 'years') {
1562
+ output = year + month / 12;
1563
+ } else {
1564
+ output = val === 'seconds' ? diff / 1e3 : // 1000
1565
+ val === 'minutes' ? diff / 6e4 : // 1000 * 60
1566
+ val === 'hours' ? diff / 36e5 : // 1000 * 60 * 60
1567
+ val === 'days' ? diff / 864e5 : // 1000 * 60 * 60 * 24
1568
+ val === 'weeks' ? diff / 6048e5 : // 1000 * 60 * 60 * 24 * 7
1569
+ diff;
1570
+ }
1571
+ return asFloat ? output : round(output);
1572
+ },
1573
+
1574
+ from : function (time, withoutSuffix) {
1575
+ return moment.humanizeDuration(this.diff(time), !withoutSuffix);
1576
+ },
1577
+
1578
+ fromNow : function (withoutSuffix) {
1579
+ return this.from(moment(), withoutSuffix);
1580
+ },
1581
+
1582
+ calendar : function () {
1583
+ var diff = this.diff(moment().sod(), 'days', true),
1584
+ calendar = moment.calendar,
1585
+ allElse = calendar.sameElse,
1586
+ format = diff < -6 ? allElse :
1587
+ diff < -1 ? calendar.lastWeek :
1588
+ diff < 0 ? calendar.lastDay :
1589
+ diff < 1 ? calendar.sameDay :
1590
+ diff < 2 ? calendar.nextDay :
1591
+ diff < 7 ? calendar.nextWeek : allElse;
1592
+ return this.format(typeof format === 'function' ? format.apply(this) : format);
1593
+ },
1594
+
1595
+ isLeapYear : function () {
1596
+ var year = this.year();
1597
+ return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
1598
+ },
1599
+
1600
+ isDST : function () {
1601
+ return (this.zone() < moment([this.year()]).zone() ||
1602
+ this.zone() < moment([this.year(), 5]).zone());
1603
+ },
1604
+
1605
+ day : function (input) {
1606
+ var day = this._d.getDay();
1607
+ return (typeof input === 'undefined') ? day :
1608
+ this.add({ d : input - day });
1609
+ },
1610
+
1611
+ sod: function () {
1612
+ return this.clone()
1613
+ .hours(0)
1614
+ .minutes(0)
1615
+ .seconds(0)
1616
+ .milliseconds(0);
1617
+ },
1618
+
1619
+ eod: function () {
1620
+ // end of day = start of day plus 1 day, minus 1 millisecond
1621
+ return this.sod().add({
1622
+ d : 1,
1623
+ ms : -1
1624
+ });
1625
+ },
1626
+
1627
+ zone : function () {
1628
+ return this._isUTC ? 0 : this._d.getTimezoneOffset();
1629
+ },
1630
+
1631
+ daysInMonth : function () {
1632
+ return this.clone().month(this.month() + 1).date(0).date();
1633
+ }
1634
+ };
1635
+
1636
+ // helper for adding shortcuts
1637
+ function makeShortcut(name, key) {
1638
+ moment.fn[name] = function (input) {
1639
+ var utc = this._isUTC ? 'UTC' : '';
1640
+ if (typeof input !== 'undefined') {
1641
+ this._d['set' + utc + key](input);
1642
+ return this;
1643
+ } else {
1644
+ return this._d['get' + utc + key]();
1645
+ }
1646
+ };
1647
+ }
1648
+
1649
+ // loop through and add shortcuts (Month, Date, Hours, Minutes, Seconds, Milliseconds)
1650
+ for (i = 0; i < shortcuts.length; i ++) {
1651
+ makeShortcut(shortcuts[i].toLowerCase(), shortcuts[i]);
1652
+ }
1653
+
1654
+ // add shortcut for year (uses different syntax than the getter/setter 'year' == 'FullYear')
1655
+ makeShortcut('year', 'FullYear');
1656
+
1657
+ return moment;
1658
+ })(Date);
1659
+
1660
+ //function to reset the date object to 00:00 GMT
1661
+ moment.fn.stripTime = function () {
1662
+ this._d = new Date(Math.floor(this._d.valueOf() / 86400000) * 86400000);
1663
+ return this;
1664
+ }
1665
+
1666
+
1667
+ //function to get the total number of days since the epoch.
1668
+ moment.fn.yearDay = function (input) {
1669
+ var yearday = Math.floor(this._d / 86400000);
1670
+ return (typeof input === 'undefined') ? yearday :
1671
+ this.add({ d : input - yearday });
1672
+ }
1673
+
1674
+ today = moment().stripTime();
1675
+
1676
+ if (typeof jQuery !== 'undefined' && typeof document.addEventListener === 'function') {
1677
+ jQuery.fn.kalendae = function (options) {
1678
+ this.each(function (i, e) {
1679
+ if (e.tagName === 'INPUT') {
1680
+ //if element is an input, bind a popup calendar to the input.
1681
+ $(e).data('kalendae', new Kalendae.Input(e, options));
1682
+ } else {
1683
+ //otherwise, insert a flat calendar into the element.
1684
+ $(e).data('kalendae', new Kalendae($.extend({}, {attachTo:e}, options)));
1685
+ }
1686
+ });
1687
+ return this;
1688
+ }
1689
+ }
1690
+
1691
+
1692
+ })();