backtastic 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 (93) hide show
  1. data/.gitignore +10 -0
  2. data/Gemfile +4 -0
  3. data/README.md +72 -0
  4. data/Rakefile +1 -0
  5. data/backtastic.gemspec +28 -0
  6. data/example/.gitignore +15 -0
  7. data/example/Gemfile +44 -0
  8. data/example/README.rdoc +261 -0
  9. data/example/Rakefile +7 -0
  10. data/example/app/assets/images/rails.png +0 -0
  11. data/example/app/assets/javascripts/application.js.coffee +22 -0
  12. data/example/app/assets/javascripts/backbone/example.js.coffee +15 -0
  13. data/example/app/assets/javascripts/backbone/models/.gitkeep +0 -0
  14. data/example/app/assets/javascripts/backbone/models/occupation.coffee +7 -0
  15. data/example/app/assets/javascripts/backbone/models/people.coffee +7 -0
  16. data/example/app/assets/javascripts/backbone/models/thing.coffee +1 -0
  17. data/example/app/assets/javascripts/backbone/routers/.gitkeep +0 -0
  18. data/example/app/assets/javascripts/backbone/routers/people_router.coffee +27 -0
  19. data/example/app/assets/javascripts/backbone/templates/.gitkeep +0 -0
  20. data/example/app/assets/javascripts/backbone/views/.gitkeep +0 -0
  21. data/example/app/assets/javascripts/backbone/views/edit_person_view.coffee +21 -0
  22. data/example/app/assets/javascripts/backbone/views/example_form_view.coffee +2 -0
  23. data/example/app/assets/javascripts/backbone/views/people_view.coffee +6 -0
  24. data/example/app/assets/javascripts/bootstrap.js.coffee +4 -0
  25. data/example/app/assets/stylesheets/application.css +13 -0
  26. data/example/app/assets/stylesheets/bootstrap_and_overrides.css.less +30 -0
  27. data/example/app/assets/templates/edit_person_view_template.jst.hamlc +13 -0
  28. data/example/app/assets/templates/example_form_view_template.jst.hamlc +2 -0
  29. data/example/app/assets/templates/people_view_template.jst.hamlc +6 -0
  30. data/example/app/controllers/application_controller.rb +3 -0
  31. data/example/app/controllers/occupations_controller.rb +3 -0
  32. data/example/app/controllers/people_controller.rb +3 -0
  33. data/example/app/helpers/application_helper.rb +2 -0
  34. data/example/app/mailers/.gitkeep +0 -0
  35. data/example/app/models/.gitkeep +0 -0
  36. data/example/app/models/occupation.rb +2 -0
  37. data/example/app/models/person.rb +5 -0
  38. data/example/app/views/layouts/application.html.erb +75 -0
  39. data/example/app/views/people/index.html.erb +7 -0
  40. data/example/config.ru +4 -0
  41. data/example/config/application.rb +59 -0
  42. data/example/config/boot.rb +6 -0
  43. data/example/config/database.yml +25 -0
  44. data/example/config/environment.rb +5 -0
  45. data/example/config/environments/development.rb +37 -0
  46. data/example/config/environments/production.rb +67 -0
  47. data/example/config/environments/test.rb +37 -0
  48. data/example/config/initializers/backtrace_silencers.rb +7 -0
  49. data/example/config/initializers/inflections.rb +15 -0
  50. data/example/config/initializers/mime_types.rb +5 -0
  51. data/example/config/initializers/secret_token.rb +7 -0
  52. data/example/config/initializers/session_store.rb +8 -0
  53. data/example/config/initializers/wrap_parameters.rb +14 -0
  54. data/example/config/locales/en.yml +5 -0
  55. data/example/config/routes.rb +62 -0
  56. data/example/db/migrate/20120429172946_create_people.rb +12 -0
  57. data/example/db/migrate/20120526164853_create_occupations.rb +9 -0
  58. data/example/db/schema.rb +31 -0
  59. data/example/db/seeds.rb +7 -0
  60. data/example/doc/README_FOR_APP +2 -0
  61. data/example/lib/assets/.gitkeep +0 -0
  62. data/example/lib/tasks/.gitkeep +0 -0
  63. data/example/log/.gitkeep +0 -0
  64. data/example/public/404.html +26 -0
  65. data/example/public/422.html +26 -0
  66. data/example/public/500.html +25 -0
  67. data/example/public/favicon.ico +0 -0
  68. data/example/public/index.html +241 -0
  69. data/example/public/robots.txt +5 -0
  70. data/example/script/rails +6 -0
  71. data/example/spec/javascripts/spec.js.coffee +7 -0
  72. data/example/spec/javascripts/views/edit_person_view_spec.coffee +39 -0
  73. data/example/spec/javascripts/views/example_form_view_spec.coffee +12 -0
  74. data/example/spec/javascripts/views/select_field_view_spec.coffee +28 -0
  75. data/example/spec/javascripts/views/text_field_view_spec.coffee +17 -0
  76. data/example/vendor/assets/javascripts/.gitkeep +0 -0
  77. data/example/vendor/assets/stylesheets/.gitkeep +0 -0
  78. data/example/vendor/plugins/.gitkeep +0 -0
  79. data/lib/assets/javascripts/backtastic.coffee +13 -0
  80. data/lib/assets/javascripts/templates/select_field_template.jst.hamlc +4 -0
  81. data/lib/assets/javascripts/templates/text_field_template.jst.hamlc +2 -0
  82. data/lib/assets/javascripts/views/backtastic_view.coffee +12 -0
  83. data/lib/assets/javascripts/views/date_field_view.coffee +12 -0
  84. data/lib/assets/javascripts/views/form_field_view.coffee +23 -0
  85. data/lib/assets/javascripts/views/form_view.coffee +38 -0
  86. data/lib/assets/javascripts/views/select_field_view.coffee +8 -0
  87. data/lib/assets/javascripts/views/text_field_view.coffee +4 -0
  88. data/lib/backtastic.rb +9 -0
  89. data/lib/backtastic/version.rb +3 -0
  90. data/vendor/assets/javascripts/bootstrap-datepicker.js +401 -0
  91. data/vendor/assets/javascripts/jquery.serializeObject.js.coffee +19 -0
  92. data/vendor/assets/stylesheets/datepicker.less +122 -0
  93. metadata +182 -0
@@ -0,0 +1,5 @@
1
+ # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
2
+ #
3
+ # To ban all spiders from the entire site uncomment the next two lines:
4
+ # User-Agent: *
5
+ # Disallow: /
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -0,0 +1,7 @@
1
+ #= require application
2
+ #= require jasminerice_reporter
3
+ #= require mock-ajax
4
+ #= require_tree .
5
+
6
+ window.peopleJson = ""
7
+ window.occupationsJson = ""
@@ -0,0 +1,39 @@
1
+ describe "EditPersonView", ->
2
+ beforeEach ->
3
+ setFixtures("<div id='edit_person'></div>")
4
+ @person = new Example.Models.Person
5
+ @occupations = new Example.Collections.OccupationsCollection [
6
+ {id: 1, name: "Doctor"}
7
+ {id: 1, name: "Lawyer"}
8
+ ]
9
+ @editPersonView = new Example.Views.EditPersonView
10
+ el: $("#edit_person")
11
+ model: @person
12
+ occupations: @occupations
13
+ @editPersonView.render()
14
+ it "renders fields", ->
15
+ expect(@editPersonView.$("input[name='first_name']")).toExist()
16
+ expect(@editPersonView.$("select[name='occupation_id']")).toExist()
17
+ describe "saving", ->
18
+ beforeEach ->
19
+ jasmine.Ajax.useMock()
20
+ @editPersonView.$("input[name='first_name']").val("Bob")
21
+ @editPersonView.$("input[name='last_name']").val("Villa")
22
+ @editPersonView.save(new jQuery.Event)
23
+ @request = mostRecentAjaxRequest()
24
+ it "disables the save button", ->
25
+ expect(@editPersonView.$("input[type='submit']").attr("disabled")).toEqual "disabled"
26
+ describe "an error response", ->
27
+ beforeEach ->
28
+ @request.response
29
+ status: 422
30
+ contentType: "application/json"
31
+ responseText: JSON.stringify
32
+ errors:
33
+ first_name: ["cannot be Bob"]
34
+ it "should add the error class", ->
35
+ expect(@editPersonView.$("div.error")).toExist()
36
+ it "should reenable the button", ->
37
+ expect(@editPersonView.$("input[type='submit']").attr("disabled")).toBeFalsy()
38
+ it "should display the error message", ->
39
+ expect(@editPersonView.$el).toHaveText /cannot be Bob/
@@ -0,0 +1,12 @@
1
+ describe "example form view", ->
2
+ beforeEach ->
3
+ setFixtures "<div id='example_form_view'></div>"
4
+ @model = new Example.Models.Thing
5
+ name: "bob"
6
+ @formView = new Example.Views.FormView
7
+ model: @model
8
+ el: $("#example_form_view")
9
+ @formView.render()
10
+ it "renders text fields", ->
11
+ expect(@formView.$("input[name=name]")).toExist()
12
+ expect(@formView.$("input[name=name]").val()).toEqual "bob"
@@ -0,0 +1,28 @@
1
+ class Foo extends Backbone.Model
2
+
3
+ class FooCollection extends Backbone.Collection
4
+ model: Foo
5
+
6
+ describe "select field view", ->
7
+ beforeEach ->
8
+ setFixtures "<div id='select_field_view'></div>"
9
+ @person = new Example.Models.Person
10
+ first_name: "bob"
11
+ occupation_id: "2"
12
+ @occupations = new Example.Collections.OccupationsCollection [{id: 1, name: "Fireman"}, {id: 2, name: "Ragpicker"}]
13
+ @selectFieldView = new Backtastic.Views.SelectFieldView
14
+ model: @person
15
+ field: "occupation_id"
16
+ label: "Occupation"
17
+ el: $("#select_field_view")
18
+ collection: @occupations
19
+ parentView: new Example.Views.FormView
20
+ @selectFieldView.render()
21
+ it "renders a value", ->
22
+ expect(@selectFieldView.$("select[name=occupation_id]").val()).toEqual "2"
23
+ it "renders options", ->
24
+ expect(@selectFieldView.$("option").length).toEqual 2
25
+ expect(@selectFieldView.$("option").first()).toHaveText "Fireman"
26
+ it "puts a label on it", ->
27
+ expect(@selectFieldView.$("label[for=occupation_id]")).toExist()
28
+ expect(@selectFieldView.$("label[for=occupation_id]")).toHaveText /Occupation/
@@ -0,0 +1,17 @@
1
+ describe "text field view", ->
2
+ beforeEach ->
3
+ setFixtures "<div id='text_field_view'></div>"
4
+ @model = new Example.Models.Thing
5
+ name: "bob"
6
+ @textFieldView = new Backtastic.Views.TextFieldView
7
+ model: @model
8
+ field: "name"
9
+ label: "Name"
10
+ el: $("#text_field_view")
11
+ parentView: new Example.Views.FormView
12
+ @textFieldView.render()
13
+ it "renders has a value", ->
14
+ expect(@textFieldView.$("input[name=name]").val()).toEqual "bob"
15
+ it "puts a label on it", ->
16
+ expect(@textFieldView.$("label[for=name]")).toExist()
17
+ expect(@textFieldView.$("label[for=name]")).toHaveText /Name/
File without changes
@@ -0,0 +1,13 @@
1
+ #= require hamlcoffee
2
+ #= require inflection
3
+ #= require jquery.serializeObject
4
+ #= require bootstrap-datepicker
5
+ #= require_self
6
+ #= require_tree .
7
+
8
+ window.Backtastic = {
9
+ Views: {}
10
+ }
11
+
12
+ window.HAML.context = (ctx) -> ctx
13
+ window.HAML.escape = (text) -> text
@@ -0,0 +1,4 @@
1
+ %label{for: @field}= @label
2
+ %select{name: @field}
3
+ - for model in @collection.models
4
+ %option{value: model.id}= model.get("name")
@@ -0,0 +1,2 @@
1
+ %label{for: @field}= @label
2
+ %input{name: @field, value: @model.get(@field)}
@@ -0,0 +1,12 @@
1
+ class Backtastic.View extends Backbone.View
2
+
3
+ attributes: ->
4
+ "data-view-id": @cid
5
+
6
+ toHtml: ->
7
+ @$el.clone().wrap("<p>").parent().html()
8
+
9
+ render: ->
10
+ @trigger("beforeRender")
11
+ @$el.html @template(@)
12
+ @trigger("rendered")
@@ -0,0 +1,12 @@
1
+ #= require ./form_field_view
2
+ class Backtastic.Views.DateFieldView extends Backtastic.Views.FormFieldView
3
+
4
+ template: JST["templates/text_field_template"]
5
+
6
+ constructor: (options) ->
7
+ super
8
+ @format = options.format
9
+
10
+ render: ->
11
+ super
12
+ @$("input").datepicker(format: @format)
@@ -0,0 +1,23 @@
1
+ class Backtastic.Views.FormFieldView extends Backtastic.View
2
+
3
+ constructor: (options)->
4
+ super
5
+ @field = options.field
6
+ @label = options.label
7
+ @parentView = options.parentView
8
+ @parentView.on "rendered", => @afterParentRender()
9
+
10
+ afterParentRender: ->
11
+ @setElement(@parentView.$("[data-view-id=#{@cid}]"))
12
+ @render()
13
+
14
+ render: ->
15
+ super
16
+ @$el.addClass "control-group"
17
+
18
+ displayErrors: (messages) ->
19
+ @$el.addClass "error"
20
+ @$el.append "<span class='help-inline'>#{message}</span>" for message in messages
21
+
22
+ clearErrors: ->
23
+ @$el.removeClass "error"
@@ -0,0 +1,38 @@
1
+ class Backtastic.Views.FormView extends Backtastic.View
2
+
3
+ constructor: ->
4
+ super
5
+ @fieldViews = {}
6
+
7
+ clearErrors: ->
8
+ fieldView.clearErrors() for field, fieldView of @fieldViews
9
+
10
+ displayErrors: (response)->
11
+ @$("input[type='submit']").removeAttr("disabled")
12
+ errors = JSON.parse(response.responseText)
13
+ errors = errors.errors if errors.errors #rails does it this way
14
+ for field, errorMessages of errors
15
+ @fieldViews[field]?.displayErrors(errorMessages)
16
+
17
+ fieldView: (fieldViewClass, options) ->
18
+ fieldView = new fieldViewClass _.extend options,
19
+ parentView: @
20
+ model: @model
21
+ @fieldViews[options.field] = fieldView
22
+ fieldView.toHtml()
23
+
24
+ dateField: (options) ->
25
+ @fieldView(Backtastic.Views.DateFieldView, options)
26
+
27
+ textField: (options) ->
28
+ @fieldView(Backtastic.Views.TextFieldView, options)
29
+
30
+ selectField: (options) ->
31
+ @fieldView(Backtastic.Views.SelectFieldView, options)
32
+
33
+ save: (event)->
34
+ @$("input[type='submit']").attr("disabled", "disabled")
35
+ @clearErrors()
36
+ event.preventDefault()
37
+ @model.on "error", (model, response) => @displayErrors(response)
38
+ @model.save @$("form").serializeObject()
@@ -0,0 +1,8 @@
1
+ class Backtastic.Views.SelectFieldView extends Backtastic.Views.FormFieldView
2
+
3
+ template: JST["templates/select_field_template"]
4
+
5
+ render: ->
6
+ super
7
+ @$("select").val @model.get(@field)
8
+
@@ -0,0 +1,4 @@
1
+ class Backtastic.Views.TextFieldView extends Backtastic.Views.FormFieldView
2
+
3
+ template: JST["templates/text_field_template"]
4
+
@@ -0,0 +1,9 @@
1
+ require "backtastic/version"
2
+ require 'rails-backbone'
3
+ require "inflection-js-rails"
4
+ require "twitter-bootstrap-rails"
5
+
6
+ module Backtastic
7
+ class BacktasticEngine < Rails::Engine
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Backtastic
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,401 @@
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.picker = $(DPGlobal.template)
28
+ .appendTo('body')
29
+ .on({
30
+ click: $.proxy(this.click, this),
31
+ mousedown: $.proxy(this.mousedown, this)
32
+ });
33
+ this.isInput = this.element.is('input');
34
+ this.component = this.element.is('.date') ? this.element.find('.add-on') : false;
35
+
36
+ if (this.isInput) {
37
+ this.element.on({
38
+ focus: $.proxy(this.show, this),
39
+ blur: $.proxy(this.hide, this),
40
+ keyup: $.proxy(this.update, this)
41
+ });
42
+ } else {
43
+ if (this.component){
44
+ this.component.on('click', $.proxy(this.show, this));
45
+ } else {
46
+ this.element.on('click', $.proxy(this.show, this));
47
+ }
48
+ }
49
+
50
+ this.viewMode = 0;
51
+ this.weekStart = options.weekStart||this.element.data('date-weekstart')||0;
52
+ this.weekEnd = this.weekStart == 0 ? 6 : this.weekStart - 1;
53
+ this.fillDow();
54
+ this.fillMonths();
55
+ this.update();
56
+ this.showMode();
57
+ };
58
+
59
+ Datepicker.prototype = {
60
+ constructor: Datepicker,
61
+
62
+ show: function(e) {
63
+ this.picker.show();
64
+ this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
65
+ this.place();
66
+ $(window).on('resize', $.proxy(this.place, this));
67
+ if (e ) {
68
+ e.stopPropagation();
69
+ e.preventDefault();
70
+ }
71
+ if (!this.isInput) {
72
+ $(document).on('mousedown', $.proxy(this.hide, this));
73
+ }
74
+ this.element.trigger({
75
+ type: 'show',
76
+ date: this.date
77
+ });
78
+ },
79
+
80
+ hide: function(){
81
+ this.picker.hide();
82
+ $(window).off('resize', this.place);
83
+ this.viewMode = 0;
84
+ this.showMode();
85
+ if (!this.isInput) {
86
+ $(document).off('mousedown', this.hide);
87
+ }
88
+ this.setValue();
89
+ this.element.trigger({
90
+ type: 'hide',
91
+ date: this.date
92
+ });
93
+ },
94
+
95
+ setValue: function() {
96
+ var formated = DPGlobal.formatDate(this.date, this.format);
97
+ if (!this.isInput) {
98
+ if (this.component){
99
+ this.element.find('input').prop('value', formated);
100
+ }
101
+ this.element.data('date', formated);
102
+ } else {
103
+ this.element.prop('value', formated);
104
+ }
105
+ },
106
+
107
+ place: function(){
108
+ var offset = this.component ? this.component.offset() : this.element.offset();
109
+ this.picker.css({
110
+ top: offset.top + this.height,
111
+ left: offset.left
112
+ });
113
+ },
114
+
115
+ update: function(){
116
+ this.date = DPGlobal.parseDate(
117
+ this.isInput ? this.element.prop('value') : this.element.data('date'),
118
+ this.format
119
+ );
120
+ this.viewDate = new Date(this.date);
121
+ this.fill();
122
+ },
123
+
124
+ fillDow: function(){
125
+ var dowCnt = this.weekStart;
126
+ var html = '<tr>';
127
+ while (dowCnt < this.weekStart + 7) {
128
+ html += '<th class="dow">'+DPGlobal.dates.daysMin[(dowCnt++)%7]+'</th>';
129
+ }
130
+ html += '</tr>';
131
+ this.picker.find('.datepicker-days thead').append(html);
132
+ },
133
+
134
+ fillMonths: function(){
135
+ var html = '';
136
+ var i = 0
137
+ while (i < 12) {
138
+ html += '<span class="month">'+DPGlobal.dates.monthsShort[i++]+'</span>';
139
+ }
140
+ this.picker.find('.datepicker-months td').append(html);
141
+ },
142
+
143
+ fill: function() {
144
+ var d = new Date(this.viewDate),
145
+ year = d.getFullYear(),
146
+ month = d.getMonth(),
147
+ currentDate = this.date.valueOf();
148
+ this.picker.find('.datepicker-days th:eq(1)')
149
+ .text(DPGlobal.dates.months[month]+' '+year);
150
+ var prevMonth = new Date(year, month-1, 28,0,0,0,0),
151
+ day = DPGlobal.getDaysInMonth(prevMonth.getFullYear(), prevMonth.getMonth());
152
+ prevMonth.setDate(day);
153
+ prevMonth.setDate(day - (prevMonth.getDay() - this.weekStart + 7)%7);
154
+ var nextMonth = new Date(prevMonth);
155
+ nextMonth.setDate(nextMonth.getDate() + 42);
156
+ nextMonth = nextMonth.valueOf();
157
+ html = [];
158
+ var clsName;
159
+ while(prevMonth.valueOf() < nextMonth) {
160
+ if (prevMonth.getDay() == this.weekStart) {
161
+ html.push('<tr>');
162
+ }
163
+ clsName = '';
164
+ if (prevMonth.getMonth() < month) {
165
+ clsName += ' old';
166
+ } else if (prevMonth.getMonth() > month) {
167
+ clsName += ' new';
168
+ }
169
+ if (prevMonth.valueOf() == currentDate) {
170
+ clsName += ' active';
171
+ }
172
+ html.push('<td class="day'+clsName+'">'+prevMonth.getDate() + '</td>');
173
+ if (prevMonth.getDay() == this.weekEnd) {
174
+ html.push('</tr>');
175
+ }
176
+ prevMonth.setDate(prevMonth.getDate()+1);
177
+ }
178
+ this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
179
+ var currentYear = this.date.getFullYear();
180
+
181
+ var months = this.picker.find('.datepicker-months')
182
+ .find('th:eq(1)')
183
+ .text(year)
184
+ .end()
185
+ .find('span').removeClass('active');
186
+ if (currentYear == year) {
187
+ months.eq(this.date.getMonth()).addClass('active');
188
+ }
189
+
190
+ html = '';
191
+ year = parseInt(year/10, 10) * 10;
192
+ var yearCont = this.picker.find('.datepicker-years')
193
+ .find('th:eq(1)')
194
+ .text(year + '-' + (year + 9))
195
+ .end()
196
+ .find('td');
197
+ year -= 1;
198
+ for (var i = -1; i < 11; i++) {
199
+ html += '<span class="year'+(i == -1 || i == 10 ? ' old' : '')+(currentYear == year ? ' active' : '')+'">'+year+'</span>';
200
+ year += 1;
201
+ }
202
+ yearCont.html(html);
203
+ },
204
+
205
+ click: function(e) {
206
+ e.stopPropagation();
207
+ e.preventDefault();
208
+ var target = $(e.target).closest('span, td, th');
209
+ if (target.length == 1) {
210
+ switch(target[0].nodeName.toLowerCase()) {
211
+ case 'th':
212
+ switch(target[0].className) {
213
+ case 'switch':
214
+ this.showMode(1);
215
+ break;
216
+ case 'prev':
217
+ case 'next':
218
+ this.viewDate['set'+DPGlobal.modes[this.viewMode].navFnc].call(
219
+ this.viewDate,
220
+ this.viewDate['get'+DPGlobal.modes[this.viewMode].navFnc].call(this.viewDate) +
221
+ DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1)
222
+ );
223
+ this.fill();
224
+ break;
225
+ }
226
+ break;
227
+ case 'span':
228
+ if (target.is('.month')) {
229
+ var month = target.parent().find('span').index(target);
230
+ this.viewDate.setMonth(month);
231
+ } else {
232
+ var year = parseInt(target.text(), 10)||0;
233
+ this.viewDate.setFullYear(year);
234
+ }
235
+ this.showMode(-1);
236
+ this.fill();
237
+ break;
238
+ case 'td':
239
+ if (target.is('.day')){
240
+ var day = parseInt(target.text(), 10)||1;
241
+ var month = this.viewDate.getMonth();
242
+ if (target.is('.old')) {
243
+ month -= 1;
244
+ } else if (target.is('.new')) {
245
+ month += 1;
246
+ }
247
+ var year = this.viewDate.getFullYear();
248
+ this.date = new Date(year, month, day,0,0,0,0);
249
+ this.viewDate = new Date(year, month, day,0,0,0,0);
250
+ this.fill();
251
+ this.setValue();
252
+ this.element.trigger({
253
+ type: 'changeDate',
254
+ date: this.date
255
+ });
256
+ }
257
+ break;
258
+ }
259
+ }
260
+ },
261
+
262
+ mousedown: function(e){
263
+ e.stopPropagation();
264
+ e.preventDefault();
265
+ },
266
+
267
+ showMode: function(dir) {
268
+ if (dir) {
269
+ this.viewMode = Math.max(0, Math.min(2, this.viewMode + dir));
270
+ }
271
+ this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
272
+ }
273
+ };
274
+
275
+ $.fn.datepicker = function ( option ) {
276
+ return this.each(function () {
277
+ var $this = $(this),
278
+ data = $this.data('datepicker'),
279
+ options = typeof option == 'object' && option;
280
+ if (!data) {
281
+ $this.data('datepicker', (data = new Datepicker(this, $.extend({}, $.fn.datepicker.defaults,options))));
282
+ }
283
+ if (typeof option == 'string') data[option]();
284
+ });
285
+ };
286
+
287
+ $.fn.datepicker.defaults = {
288
+ };
289
+ $.fn.datepicker.Constructor = Datepicker;
290
+
291
+ var DPGlobal = {
292
+ modes: [
293
+ {
294
+ clsName: 'days',
295
+ navFnc: 'Month',
296
+ navStep: 1
297
+ },
298
+ {
299
+ clsName: 'months',
300
+ navFnc: 'FullYear',
301
+ navStep: 1
302
+ },
303
+ {
304
+ clsName: 'years',
305
+ navFnc: 'FullYear',
306
+ navStep: 10
307
+ }],
308
+ dates:{
309
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
310
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
311
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
312
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
313
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
314
+ },
315
+ isLeapYear: function (year) {
316
+ return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0))
317
+ },
318
+ getDaysInMonth: function (year, month) {
319
+ return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]
320
+ },
321
+ parseFormat: function(format){
322
+ var separator = format.match(/[.\/-].*?/),
323
+ parts = format.split(/\W+/);
324
+ if (!separator || !parts || parts.length == 0){
325
+ throw new Error("Invalid date format.");
326
+ }
327
+ return {separator: separator, parts: parts};
328
+ },
329
+ parseDate: function(date, format) {
330
+ var parts = date.split(format.separator),
331
+ date = new Date(1970, 1, 1, 0, 0, 0),
332
+ val;
333
+ if (parts.length == format.parts.length) {
334
+ for (var i=0, cnt = format.parts.length; i < cnt; i++) {
335
+ val = parseInt(parts[i], 10)||1;
336
+ switch(format.parts[i]) {
337
+ case 'dd':
338
+ case 'd':
339
+ date.setDate(val);
340
+ break;
341
+ case 'mm':
342
+ case 'm':
343
+ date.setMonth(val - 1);
344
+ break;
345
+ case 'yy':
346
+ date.setFullYear(2000 + val);
347
+ break;
348
+ case 'yyyy':
349
+ date.setFullYear(val);
350
+ break;
351
+ }
352
+ }
353
+ }
354
+ return date;
355
+ },
356
+ formatDate: function(date, format){
357
+ var val = {
358
+ d: date.getDate(),
359
+ m: date.getMonth() + 1,
360
+ yy: date.getFullYear().toString().substring(2),
361
+ yyyy: date.getFullYear()
362
+ };
363
+ val.dd = (val.d < 10 ? '0' : '') + val.d;
364
+ val.mm = (val.m < 10 ? '0' : '') + val.m;
365
+ var date = [];
366
+ for (var i=0, cnt = format.parts.length; i < cnt; i++) {
367
+ date.push(val[format.parts[i]]);
368
+ }
369
+ return date.join(format.separator);
370
+ },
371
+ headTemplate: '<thead>'+
372
+ '<tr>'+
373
+ '<th class="prev"><i class="icon-arrow-left"/></th>'+
374
+ '<th colspan="5" class="switch"></th>'+
375
+ '<th class="next"><i class="icon-arrow-right"/></th>'+
376
+ '</tr>'+
377
+ '</thead>',
378
+ contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>'
379
+ };
380
+ DPGlobal.template = '<div class="datepicker dropdown-menu">'+
381
+ '<div class="datepicker-days">'+
382
+ '<table class=" table-condensed">'+
383
+ DPGlobal.headTemplate+
384
+ '<tbody></tbody>'+
385
+ '</table>'+
386
+ '</div>'+
387
+ '<div class="datepicker-months">'+
388
+ '<table class="table-condensed">'+
389
+ DPGlobal.headTemplate+
390
+ DPGlobal.contTemplate+
391
+ '</table>'+
392
+ '</div>'+
393
+ '<div class="datepicker-years">'+
394
+ '<table class="table-condensed">'+
395
+ DPGlobal.headTemplate+
396
+ DPGlobal.contTemplate+
397
+ '</table>'+
398
+ '</div>'+
399
+ '</div>';
400
+
401
+ }( window.jQuery )